soc/imgtec/pistachio: Add IMGTEC SPI controller driver
The Serial Peripheral Flash Interface (SPFI) block allows communication with various devices over the SPI bus. It uses a configurable transaction interface and it clocks the bus according to the configured command, address, gap (aka dummy) and data lengths. This controller requires the SPI_ATOMIC_SEQUENCING flag set (write and read done in the same transaction) as it cannot directly control CS and will assert/de-assert CS at the beginning/end of a transaction itself. Note that the size of any transfer cannot be greater than 64KB - 1, as this is configured in a 16-bit field. The SOC has 2 SPFI interfaces each of them providing 5 slave select lines. SPFI 0 supports single and dual modes, SPFI 1 supports single, dual and quad modes. For SPFI interface 0: - The block needs the system PLL and the following top level SPI clock registers to be set: - CR_cr_top_spi0clkinternal_CTRL[2:0] with division value - CR_MIPS_CLOCK_GATE[19]: bit cr_top_SPI0CLKOUT_MIPS set - CR_cr_top_SPI0CLKOUT_CTRL[6:0] with division value - The following MFIO configuration parameters are also required: Signal name Pad name MFIO mode spim0_d0_txd MFIO_MIPS_10 0 spim0_d1_rxd MFIO_MIPS_9 0 spim0_mclk MFIO_MIPS_8 0 spim0_cs0 MFIO_MIPS_2 1 spim0_cs1 MFIO_MIPS_1 1 spim0_cs2 MFIO_MIPS_55 1 MFIO_MIPS_28 1 spim0_cs3 MFIO_MIPS_56 1 MFIO_MIPS_29 1 spim0_cs4 MFIO_MIPS_57 1 MFIO_MIMPS_30 1 For SPFI interface 1: - The block needs the system PLL and the following top level SPI clock registers to be set: - CR_cr_top_spi1clkinternal_CTRL[2:0] with division value - CR_MIPS_CLOCK_GATE[20]: bit cr_top_SPI1CLKOUT_MIPS set - CR_cr_top_SPI1CLKOUT_CTRL[6:0] with division value - The following MFIO configuration parameters are also required: Signal name Pad name MFIO mode spim1_d0_txd MFIO_MIPS_5 0 spim1_d1_rxd MFIO_MIPS_4 0 spim1_mclk MFIO_MIPS_3 0 spim1_d2 MFIO_MIPS_6 0 spim1_d3 MFIO_MIPS_7 0 spim1_cs0 MFIO_MIPS_0 0 spim1_cs1 MFIO_MIPS_1 0 MFIO_MIPS_58 1 spim1_cs2 MFIO_MIPS_2 0 MFIO_MIPS_55 2 MFIO_MIPS_31 1 spim1_cs3 MFIO_MIPS_56 2 spim1_cs4 MFIO_MIPS_57 2 BUG=chrome-os-partner:31438, chrome-os-partner:32441 TEST=Tested as bare-metal driver on Pistachio FPGA Change-Id: I3b3e4475976e6fba58cef93b12d997ec5cb26341 Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: 621849942e27f7d6cf2c8ade7f2c4d18d2318b91 Original-Change-Id: Ib257eb6236bd2895281175871b4ab979660f1239 Original-Signed-off-by: Ionela Voinescu <ionela.voinescu@imgtec.com> Original-Reviewed-on: https://chromium-review.googlesource.com/217320 Original-Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: http://review.coreboot.org/9049 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
2d510d01d1
commit
49aad6b387
|
@ -26,6 +26,7 @@ config CPU_IMGTEC_PISTACHIO
|
|||
select HAVE_MONOTONIC_TIMER
|
||||
select HAVE_UART_MEMORY_MAPPED
|
||||
select HAVE_UART_SPECIAL
|
||||
select SPI_ATOMIC_SEQUENCING
|
||||
bool
|
||||
|
||||
if CPU_IMGTEC_PISTACHIO
|
||||
|
|
|
@ -39,6 +39,7 @@ ramstage-y += timestamp.c
|
|||
|
||||
romstage-y += cbmem.c
|
||||
romstage-y += romstage.c
|
||||
romstage-y += monotonic_timer.c
|
||||
romstage-y += timestamp.c
|
||||
|
||||
# Generate the actual coreboot bootblock code
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2014 Imagination Technologies
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of
|
||||
* the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SOC_IMGTEC_DANUBE_CPU_H__
|
||||
#define __SOC_IMGTEC_DANUBE_CPU_H__
|
||||
|
||||
#define IMG_SPIM0_BASE_ADDRESS 0xB8100F00
|
||||
#define IMG_SPIM1_BASE_ADDRESS 0xB8101000
|
||||
|
||||
#endif
|
|
@ -1,9 +1,11 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
* Copyright (C) 2014 Imagination Technologies
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
@ -11,25 +13,526 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <timer.h>
|
||||
#include <spi-generic.h>
|
||||
#include <spi_flash.h>
|
||||
#include "cpu.h"
|
||||
#include "spi.h"
|
||||
|
||||
#if !CONFIG_SPI_ATOMIC_SEQUENCING
|
||||
#error "Unsupported SPI driver API"
|
||||
#endif
|
||||
|
||||
struct img_spi_slave {
|
||||
struct spi_slave slave;
|
||||
/* SPIM instance device parameters */
|
||||
struct spim_device_parameters device_parameters;
|
||||
/* SPIM instance base address */
|
||||
u32 base;
|
||||
/* Boolean property that is TRUE if API has been initialised */
|
||||
int initialised;
|
||||
};
|
||||
|
||||
/* Allocate memory for the maximum number of devices */
|
||||
static struct
|
||||
img_spi_slave img_spi_slaves[SPIM_NUM_BLOCKS*SPIM_NUM_PORTS_PER_BLOCK];
|
||||
|
||||
/*
|
||||
* Wait for the bit at the shift position to be set in reg
|
||||
* If the bit is not set in SPI_TIMEOUT_VALUE_US return with error
|
||||
*/
|
||||
static int wait_status(u32 reg, u32 shift)
|
||||
{
|
||||
struct stopwatch sw;
|
||||
|
||||
stopwatch_init_usecs_expire(&sw, SPI_TIMEOUT_VALUE_US);
|
||||
while (!(read32(reg) & (1 << shift))) {
|
||||
if (stopwatch_expired(&sw))
|
||||
return -SPIM_TIMEOUT;
|
||||
}
|
||||
return SPIM_OK;
|
||||
}
|
||||
|
||||
/* Transmitter function. Fills TX FIFO with data before enabling SPIM */
|
||||
static int transmitdata(struct spi_slave *slave, u8 *buffer, u32 size)
|
||||
{
|
||||
u32 blocksize, base, write_data;
|
||||
int ret;
|
||||
|
||||
base = container_of(slave, struct img_spi_slave, slave)->base;
|
||||
while (size) {
|
||||
/* Wait until FIFO empty */
|
||||
write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_SDE_MASK);
|
||||
ret = wait_status(base + SPFI_INT_STATUS_REG_OFFSET,
|
||||
SPFI_SDE_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Write to FIFO in blocks of 16 words (64 bytes)
|
||||
* Do 32bit writes first.
|
||||
*/
|
||||
blocksize = SPIM_MAX_BLOCK_BYTES;
|
||||
while ((size >= sizeof(u32)) && blocksize) {
|
||||
memcpy(&write_data, buffer, sizeof(u32));
|
||||
write32(base + SPFI_SEND_LONG_REG_OFFSET, write_data);
|
||||
buffer += sizeof(u32);
|
||||
size -= sizeof(u32);
|
||||
blocksize -= sizeof(u32);
|
||||
}
|
||||
while (size && blocksize) {
|
||||
write32(base + SPFI_SEND_BYTE_REG_OFFSET, *buffer);
|
||||
buffer++;
|
||||
size--;
|
||||
blocksize--;
|
||||
}
|
||||
}
|
||||
return SPIM_OK;
|
||||
}
|
||||
|
||||
/* Receiver function */
|
||||
static int receivedata(struct spi_slave *slave, u8 *buffer, u32 size)
|
||||
{
|
||||
u32 read_data, base;
|
||||
int ret;
|
||||
|
||||
base = container_of(slave, struct img_spi_slave, slave)->base;
|
||||
/*
|
||||
* Do 32bit reads first. Clear status GDEX32BIT here so that the first
|
||||
* status reg. read gets the actual bit state
|
||||
*/
|
||||
write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_GDEX32BIT_MASK);
|
||||
while (size >= sizeof(u32)) {
|
||||
ret = wait_status(base + SPFI_INT_STATUS_REG_OFFSET,
|
||||
SPFI_GDEX32BIT_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
read_data = read32(base + SPFI_GET_LONG_REG_OFFSET);
|
||||
memcpy(buffer, &read_data, sizeof(u32));
|
||||
buffer += sizeof(u32);
|
||||
size -= sizeof(u32);
|
||||
/* Clear interrupt status on GDEX32BITL */
|
||||
write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_GDEX32BIT_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the remaining 8bit reads. Clear status GDEX8BIT here so that
|
||||
* the first status reg. read gets the actual bit state
|
||||
*/
|
||||
write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_GDEX8BIT_MASK);
|
||||
while (size) {
|
||||
ret = wait_status(base + SPFI_INT_STATUS_REG_OFFSET,
|
||||
SPFI_GDEX8BIT_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
*buffer = read32(base + SPFI_GET_BYTE_REG_OFFSET);
|
||||
buffer++;
|
||||
size--;
|
||||
/* Clear interrupt status on SPFI_GDEX8BIT */
|
||||
write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_GDEX8BIT_MASK);
|
||||
}
|
||||
return SPIM_OK;
|
||||
}
|
||||
|
||||
/* Sets port parameters in port state register. */
|
||||
static void setparams(struct spi_slave *slave, u32 port,
|
||||
struct spim_device_parameters *params)
|
||||
{
|
||||
u32 spim_parameters, port_state, base;
|
||||
|
||||
spim_parameters = 0;
|
||||
|
||||
base = container_of(slave, struct img_spi_slave, slave)->base;
|
||||
port_state = read32(base + SPFI_PORT_STATE_REG_OFFSET);
|
||||
port_state &= ~((SPIM_PORT0_MASK>>port)|SPFI_PORT_SELECT_MASK);
|
||||
port_state |= params->cs_idle_level<<(SPIM_CS0_IDLE_SHIFT-port);
|
||||
port_state |=
|
||||
params->data_idle_level<<(SPIM_DATA0_IDLE_SHIFT-port);
|
||||
|
||||
/* Clock idle level and phase */
|
||||
switch (params->spi_mode) {
|
||||
case SPIM_MODE_0:
|
||||
break;
|
||||
case SPIM_MODE_1:
|
||||
port_state |= (1 << (SPIM_CLOCK0_PHASE_SHIFT - port));
|
||||
break;
|
||||
case SPIM_MODE_2:
|
||||
port_state |= (1 << (SPIM_CLOCK0_IDLE_SHIFT - port));
|
||||
break;
|
||||
case SPIM_MODE_3:
|
||||
port_state |= (1 << (SPIM_CLOCK0_IDLE_SHIFT - port)) |
|
||||
(1 << (SPIM_CLOCK0_PHASE_SHIFT - port));
|
||||
break;
|
||||
}
|
||||
/* Set port state register */
|
||||
write32(base + SPFI_PORT_STATE_REG_OFFSET, port_state);
|
||||
|
||||
/* Set up values to be written to device parameter register */
|
||||
spim_parameters |= params->bitrate << SPIM_CLK_DIVIDE_SHIFT;
|
||||
spim_parameters |= params->cs_setup << SPIM_CS_SETUP_SHIFT;
|
||||
spim_parameters |= params->cs_hold << SPIM_CS_HOLD_SHIFT;
|
||||
spim_parameters |= params->cs_delay << SPIM_CS_DELAY_SHIFT;
|
||||
|
||||
write32(base + SPFI_PORT_0_PARAM_REG_OFFSET + 4 * port,
|
||||
spim_parameters);
|
||||
}
|
||||
|
||||
/* Sets up transaction register */
|
||||
static u32 transaction_reg_setup(struct spim_buffer *first,
|
||||
struct spim_buffer *second)
|
||||
{
|
||||
u32 reg = 0;
|
||||
|
||||
/* 2nd transfer exists? */
|
||||
if (second) {
|
||||
/*
|
||||
* If second transfer exists, it's a "command followed by data"
|
||||
* type of transfer and first transfer is defined by
|
||||
* CMD_LENGTH, ADDR_LENGTH, DUMMY_LENGTH... fields of
|
||||
* transaction register
|
||||
*/
|
||||
reg = spi_write_reg_field(reg, SPFI_CMD_LENGTH, 1);
|
||||
reg = spi_write_reg_field(reg, SPFI_ADDR_LENGTH,
|
||||
first->size - 1);
|
||||
reg = spi_write_reg_field(reg, SPFI_DUMMY_LENGTH, 0);
|
||||
/* Set data size (size of the second transfer) */
|
||||
reg = spi_write_reg_field(reg, SPFI_TSIZE, second->size);
|
||||
} else {
|
||||
/* Set data size, in this case size of the 1st transfer */
|
||||
reg = spi_write_reg_field(reg, SPFI_TSIZE, first->size);
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* Sets up control register */
|
||||
static u32 control_reg_setup(struct spim_buffer *first,
|
||||
struct spim_buffer *second)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Enable SPFI */
|
||||
reg = SPFI_EN_MASK;
|
||||
reg |= first->inter_byte_delay ? SPIM_BYTE_DELAY_MASK : 0;
|
||||
|
||||
/* Set up the transfer mode */
|
||||
reg = spi_write_reg_field(reg, SPFI_TRNSFR_MODE_DQ, SPIM_CMD_MODE_0);
|
||||
reg = spi_write_reg_field(reg, SPFI_TRNSFR_MODE, SPIM_DMODE_SINGLE);
|
||||
|
||||
if (second) {
|
||||
/* Set TX bit if the 2nd transaction is 'send' */
|
||||
reg = spi_write_reg_field(reg, SPFI_TX_RX,
|
||||
second->isread ? 0 : 1);
|
||||
/*
|
||||
* Set send/get DMA for both transactions
|
||||
* (first is always 'send')
|
||||
*/
|
||||
reg = spi_write_reg_field(reg, SPIM_SEND_DMA, 1);
|
||||
if (second->isread)
|
||||
reg = spi_write_reg_field(reg, SPIM_GET_DMA, 1);
|
||||
|
||||
} else {
|
||||
/* Set TX bit if the 1st transaction is 'send' */
|
||||
reg |= first->isread ? 0 : SPFI_TX_RX_MASK;
|
||||
/* Set send/get DMA */
|
||||
reg |= first->isread ? SPIM_GET_DMA_MASK : SPIM_SEND_DMA_MASK;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* Checks the given buffer information */
|
||||
static int check_buffers(struct spi_slave *slave, struct spim_buffer *first,
|
||||
struct spim_buffer *second){
|
||||
|
||||
if (!(container_of(slave, struct img_spi_slave, slave)->initialised))
|
||||
return -SPIM_API_NOT_INITIALISED;
|
||||
/*
|
||||
* First operation must always be defined
|
||||
* It can be either a read or a write and its size cannot be bigge
|
||||
* than SPIM_MAX_TANSFER_BYTES = 64KB - 1 (0xFFFF)
|
||||
*/
|
||||
if (!first)
|
||||
return -SPIM_INVALID_READ_WRITE;
|
||||
if (first->size > SPIM_MAX_TRANSFER_BYTES)
|
||||
return -SPIM_INVALID_SIZE;
|
||||
if (first->isread > 1)
|
||||
return -SPIM_INVALID_READ_WRITE;
|
||||
/* Check operation parameters for 'second' */
|
||||
if (second) {
|
||||
/*
|
||||
* If the second operation is defined it must be a read
|
||||
* operation and its size must not be bigger than
|
||||
* SPIM_MAX_TANSFER_BYTES = 64KB - 1 (0xFFFF)
|
||||
*/
|
||||
if (second->size > SPIM_MAX_TRANSFER_BYTES)
|
||||
return -SPIM_INVALID_SIZE;
|
||||
if (!second->isread)
|
||||
return -SPIM_INVALID_READ_WRITE;
|
||||
/*
|
||||
* If the second operations is defined, the first operation
|
||||
* must be a write and its size cannot be bigger than
|
||||
* SPIM_MAX_FLASH_COMMAND_BYTES(8): command size (1) +
|
||||
* address size (7).
|
||||
*/
|
||||
if (first->isread)
|
||||
return -SPIM_INVALID_READ_WRITE;
|
||||
if (first->size > SPIM_MAX_FLASH_COMMAND_BYTES)
|
||||
return -SPIM_INVALID_SIZE;
|
||||
|
||||
}
|
||||
return SPIM_OK;
|
||||
}
|
||||
|
||||
/* Checks the set bitrate */
|
||||
static int check_bitrate(u32 rate)
|
||||
{
|
||||
/* Bitrate must be 1, 2, 4, 8, 16, 32, 64, or 128 */
|
||||
switch (rate) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
case 64:
|
||||
case 128:
|
||||
return SPIM_OK;
|
||||
default:
|
||||
return -SPIM_INVALID_BIT_RATE;
|
||||
}
|
||||
return -SPIM_INVALID_BIT_RATE;
|
||||
}
|
||||
|
||||
/* Checks device parameters for errors */
|
||||
static int check_device_params(struct spim_device_parameters *pdev_param)
|
||||
{
|
||||
if (pdev_param->spi_mode < SPIM_MODE_0 ||
|
||||
pdev_param->spi_mode > SPIM_MODE_3)
|
||||
return -SPIM_INVALID_SPI_MODE;
|
||||
if (check_bitrate(pdev_param->bitrate) != SPIM_OK)
|
||||
return -SPIM_INVALID_BIT_RATE;
|
||||
if (pdev_param->cs_idle_level > 1)
|
||||
return -SPIM_INVALID_CS_IDLE_LEVEL;
|
||||
if (pdev_param->data_idle_level > 1)
|
||||
return -SPIM_INVALID_DATA_IDLE_LEVEL;
|
||||
return SPIM_OK;
|
||||
}
|
||||
|
||||
/* Function that carries out read/write operations */
|
||||
static int spim_io(struct spi_slave *slave, struct spim_buffer *first,
|
||||
struct spim_buffer *second)
|
||||
{
|
||||
u32 reg, base;
|
||||
int i, trans_count, ret;
|
||||
struct spim_buffer *transaction[2];
|
||||
|
||||
base = container_of(slave, struct img_spi_slave, slave)->base;
|
||||
|
||||
ret = check_buffers(slave, first, second);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Soft reset peripheral internals, this will terminate any
|
||||
* pending transactions
|
||||
*/
|
||||
write32(base + SPFI_CONTROL_REG_OFFSET, SPIM_SOFT_RESET_MASK);
|
||||
write32(base + SPFI_CONTROL_REG_OFFSET, 0);
|
||||
/* Port state register */
|
||||
reg = read32(base + SPFI_PORT_STATE_REG_OFFSET);
|
||||
reg = spi_write_reg_field(reg, SPFI_PORT_SELECT, slave->cs);
|
||||
write32(base + SPFI_PORT_STATE_REG_OFFSET, reg);
|
||||
/* Set transaction register */
|
||||
reg = transaction_reg_setup(first, second);
|
||||
write32(base + SPFI_TRANSACTION_REG_OFFSET, reg);
|
||||
/* Clear status */
|
||||
write32(base + SPFI_INT_CLEAR_REG_OFFSET, 0xffffffff);
|
||||
/* Set control register */
|
||||
reg = control_reg_setup(first, second);
|
||||
write32(base + SPFI_CONTROL_REG_OFFSET, reg);
|
||||
/* First transaction always exists */
|
||||
transaction[0] = first;
|
||||
trans_count = 1;
|
||||
/* Is there a second transaction? */
|
||||
if (second) {
|
||||
transaction[1] = second;
|
||||
trans_count++;
|
||||
}
|
||||
/* Now write/read FIFO's */
|
||||
for (i = 0; i < trans_count; i++)
|
||||
/* Which transaction to execute, "Send" or "Get"? */
|
||||
if (transaction[i]->isread) {
|
||||
/* Get */
|
||||
ret = receivedata(slave, transaction[i]->buffer,
|
||||
transaction[i]->size);
|
||||
if (ret) {
|
||||
printk(BIOS_ERR,
|
||||
"%s: Error: receive data failed.\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* Send */
|
||||
ret = transmitdata(slave, transaction[i]->buffer,
|
||||
transaction[i]->size);
|
||||
if (ret) {
|
||||
printk(BIOS_ERR,
|
||||
"%s: Error: transmit data failed.\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for end of the transaction */
|
||||
ret = wait_status(base + SPFI_INT_STATUS_REG_OFFSET,
|
||||
SPFI_ALLDONE_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Soft reset peripheral internals, this will terminate any
|
||||
* pending transactions
|
||||
*/
|
||||
write32(base + SPFI_CONTROL_REG_OFFSET, SPIM_SOFT_RESET_MASK);
|
||||
write32(base + SPFI_CONTROL_REG_OFFSET, 0);
|
||||
|
||||
return SPIM_OK;
|
||||
}
|
||||
|
||||
/* Initialization, must be called once on start up */
|
||||
void spi_init(void)
|
||||
{
|
||||
/* Clear everything just in case */
|
||||
memset(img_spi_slaves, 0, sizeof(img_spi_slaves));
|
||||
}
|
||||
|
||||
/* Set up communications parameters for a SPI slave. */
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
|
||||
{
|
||||
return NULL;
|
||||
|
||||
struct img_spi_slave *img_slave = NULL;
|
||||
struct spi_slave *slave;
|
||||
struct spim_device_parameters *device_parameters;
|
||||
u32 base;
|
||||
|
||||
switch (bus) {
|
||||
case 0:
|
||||
base = IMG_SPIM0_BASE_ADDRESS;
|
||||
break;
|
||||
case 1:
|
||||
base = IMG_SPIM1_BASE_ADDRESS;
|
||||
break;
|
||||
default:
|
||||
printk(BIOS_ERR, "%s: Error: unsupported bus.\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
if (cs > SPIM_DEVICE4) {
|
||||
printk(BIOS_ERR, "%s: Error: unsupported chipselect.\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
img_slave = img_spi_slaves + bus * SPIM_NUM_PORTS_PER_BLOCK + cs;
|
||||
slave = &(img_slave->slave);
|
||||
device_parameters = &(img_slave->device_parameters);
|
||||
|
||||
img_slave->base = base;
|
||||
slave->bus = bus;
|
||||
slave->cs = cs;
|
||||
slave->rw = SPI_READ_FLAG | SPI_WRITE_FLAG;
|
||||
device_parameters->bitrate = 64;
|
||||
device_parameters->cs_setup = 0;
|
||||
device_parameters->cs_hold = 0;
|
||||
device_parameters->cs_delay = 0;
|
||||
device_parameters->spi_mode = SPIM_MODE_0;
|
||||
device_parameters->cs_idle_level = 1;
|
||||
device_parameters->data_idle_level = 0;
|
||||
img_slave->initialised = IMG_FALSE;
|
||||
|
||||
return slave;
|
||||
}
|
||||
|
||||
/* Claim the bus and prepare it for communication */
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
return 0;
|
||||
int ret;
|
||||
struct img_spi_slave *img_slave;
|
||||
|
||||
if (!slave) {
|
||||
printk(BIOS_ERR, "%s: Error: slave was not set up.\n",
|
||||
__func__);
|
||||
return -SPIM_API_NOT_INITIALISED;
|
||||
}
|
||||
img_slave = container_of(slave, struct img_spi_slave, slave);
|
||||
if (img_slave->initialised)
|
||||
return SPIM_OK;
|
||||
/* Check device parameters */
|
||||
ret = check_device_params(&(img_slave->device_parameters));
|
||||
if (ret) {
|
||||
printk(BIOS_ERR, "%s: Error: incorrect device parameters.\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
/* Set device parameters */
|
||||
setparams(slave, slave->cs, &(img_slave->device_parameters));
|
||||
/* Soft reset peripheral internals */
|
||||
write32(img_slave->base + SPFI_CONTROL_REG_OFFSET,
|
||||
SPIM_SOFT_RESET_MASK);
|
||||
write32(img_slave->base + SPFI_CONTROL_REG_OFFSET, 0);
|
||||
img_slave->initialised = IMG_TRUE;
|
||||
return SPIM_OK;
|
||||
}
|
||||
|
||||
/* Release the SPI bus */
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
struct img_spi_slave *img_slave;
|
||||
|
||||
if (!slave) {
|
||||
printk(BIOS_ERR, "%s: Error: slave was not set up.\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
img_slave = container_of(slave, struct img_spi_slave, slave);
|
||||
img_slave->initialised = IMG_FALSE;
|
||||
/* Soft reset peripheral internals */
|
||||
write32(img_slave->base + SPFI_CONTROL_REG_OFFSET,
|
||||
SPIM_SOFT_RESET_MASK);
|
||||
write32(img_slave->base + SPFI_CONTROL_REG_OFFSET, 0);
|
||||
}
|
||||
|
||||
int spi_xfer(struct spi_slave *slave, const void *dout,
|
||||
unsigned out_bytes, void *din, unsigned in_bytes)
|
||||
/* SPI transfer */
|
||||
int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bytesout,
|
||||
void *din, unsigned int bytesin)
|
||||
{
|
||||
return 0;
|
||||
struct spim_buffer buff_0;
|
||||
struct spim_buffer buff_1;
|
||||
|
||||
if (!slave) {
|
||||
printk(BIOS_ERR, "%s: Error: slave was not set up.\n",
|
||||
__func__);
|
||||
return -SPIM_API_NOT_INITIALISED;
|
||||
}
|
||||
if (!dout && !din) {
|
||||
printk(BIOS_ERR, "%s: Error: both buffers are NULL.\n",
|
||||
__func__);
|
||||
return -SPIM_INVALID_TRANSFER_DESC;
|
||||
}
|
||||
/* If we only have a read or a write operation
|
||||
* the parameters for it will be put in the first buffer
|
||||
*/
|
||||
buff_0.buffer = (dout) ? (void *)dout : (void *)din;
|
||||
buff_0.size = (dout) ? bytesout : bytesin;
|
||||
buff_0.isread = (dout) ? IMG_FALSE : IMG_TRUE;
|
||||
buff_0.inter_byte_delay = 0;
|
||||
|
||||
if (dout && din) {
|
||||
/* Set up the read buffer to receive our data */
|
||||
buff_1.buffer = din;
|
||||
buff_1.size = bytesin;
|
||||
buff_1.isread = IMG_TRUE;
|
||||
buff_1.inter_byte_delay = 0;
|
||||
}
|
||||
return spim_io(slave, &buff_0, (dout && din) ? &buff_1 : NULL);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2014 Imagination Technologies
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __SOC_IMGTEC_DANUBE_SPI_H__
|
||||
#define __SOC_IMGTEC_DANUBE_SPI_H__
|
||||
|
||||
#include <arch/types.h>
|
||||
#include <arch/io.h>
|
||||
|
||||
#define spi_read_reg_field(regval, field) \
|
||||
( \
|
||||
((field##_MASK) == 0xFFFFFFFF) ? \
|
||||
(regval) : \
|
||||
(((regval) & (field##_MASK)) >> (field##_SHIFT))\
|
||||
)
|
||||
|
||||
#define spi_write_reg_field(regval, field, val) \
|
||||
( \
|
||||
((field##_MASK) == 0xFFFFFFFF) ? \
|
||||
(val) : \
|
||||
(((regval) & ~(field##_MASK)) | \
|
||||
(((val) << (field##_SHIFT)) & (field##_MASK))) \
|
||||
)
|
||||
|
||||
/*
|
||||
* Parameter register
|
||||
* Each of these corresponds to a single port (ie CS line) in the interface
|
||||
* Fields Name Description
|
||||
* ====== ==== ===========
|
||||
* b31:24 CLK_RATE Bit Clock rate = (24.576 * value / 512) MHz
|
||||
* b23:16 CS_SETUP Chip Select setup = (40 * value) ns
|
||||
* b15:8 CS_HOLD Chip Select hold = (40 * value) ns
|
||||
* b7:0 CS_DELAY Chip Select delay = (40 * value) ns
|
||||
*/
|
||||
|
||||
#define SPIM_CLK_DIVIDE_MASK (0xFF000000)
|
||||
#define SPIM_CS_SETUP_MASK (0x00FF0000)
|
||||
#define SPIM_CS_HOLD_MASK (0x0000FF00)
|
||||
#define SPIM_CS_DELAY_MASK (0x000000FF)
|
||||
#define SPIM_CS_PARAM_MASK (SPIM_CS_SETUP_MASK \
|
||||
| SPIM_CS_HOLD_MASK \
|
||||
| SPIM_CS_DELAY_MASK)
|
||||
|
||||
#define SPIM_CLK_DIVIDE_SHIFT (24)
|
||||
#define SPIM_CS_SETUP_SHIFT (16)
|
||||
#define SPIM_CS_HOLD_SHIFT (8)
|
||||
#define SPIM_CS_DELAY_SHIFT (0)
|
||||
#define SPIM_CS_PARAM_SHIFT (0)
|
||||
|
||||
/* Control register */
|
||||
|
||||
#define SPFI_DRIBBLE_COUNT_MASK (0x000e0000)
|
||||
#define SPFI_MEMORY_IF_MASK (0x00008000)
|
||||
#define SPIM_BYTE_DELAY_MASK (0x00004000)
|
||||
#define SPIM_CS_DEASSERT_MASK (0x00002000)
|
||||
#define SPIM_CONTINUE_MASK (0x00001000)
|
||||
#define SPIM_SOFT_RESET_MASK (0x00000800)
|
||||
#define SPIM_SEND_DMA_MASK (0x00000400)
|
||||
#define SPIM_GET_DMA_MASK (0x00000200)
|
||||
#define SPIM_EDGE_TX_RX_MASK (0x00000100)
|
||||
#define SPFI_TRNSFR_MODE_MASK (0x000000e0)
|
||||
#define SPFI_TRNSFR_MODE_DQ_MASK (0x0000001c)
|
||||
#define SPFI_TX_RX_MASK (0x00000002)
|
||||
#define SPFI_EN_MASK (0x00000001)
|
||||
|
||||
#define SPFI_DRIBBLE_COUNT_SHIFT (17)
|
||||
#define SPFI_MEMORY_IF_SHIFT (15)
|
||||
#define SPIM_BYTE_DELAY_SHIFT (14)
|
||||
#define SPIM_CS_DEASSERT_SHIFT (13)
|
||||
#define SPIM_CONTINUE_SHIFT (12)
|
||||
#define SPIM_SOFT_RESET_SHIFT (11)
|
||||
#define SPIM_SEND_DMA_SHIFT (10)
|
||||
#define SPIM_GET_DMA_SHIFT (9)
|
||||
#define SPIM_EDGE_TX_RX_SHIFT (8)
|
||||
#define SPFI_TRNSFR_MODE_SHIFT (5)
|
||||
#define SPFI_TRNSFR_MODE_DQ_SHIFT (2)
|
||||
#define SPFI_TX_RX_SHIFT (1)
|
||||
#define SPFI_EN_SHIFT (0)
|
||||
|
||||
/* Transaction register */
|
||||
|
||||
#define SPFI_TSIZE_MASK (0xffff0000)
|
||||
#define SPFI_CMD_LENGTH_MASK (0x0000e000)
|
||||
#define SPFI_ADDR_LENGTH_MASK (0x00001c00)
|
||||
#define SPFI_DUMMY_LENGTH_MASK (0x000003e0)
|
||||
#define SPFI_PI_LENGTH_MASK (0x0000001c)
|
||||
|
||||
#define SPFI_TSIZE_SHIFT (16)
|
||||
#define SPFI_CMD_LENGTH_SHIFT (13)
|
||||
#define SPFI_ADDR_LENGTH_SHIFT (10)
|
||||
#define SPFI_DUMMY_LENGTH_SHIFT (5)
|
||||
#define SPFI_PI_LENGTH_SHIFT (2)
|
||||
|
||||
/* Port state register */
|
||||
|
||||
#define SPFI_PORT_SELECT_MASK (0x00700000)
|
||||
/* WARNING the following bits are reversed */
|
||||
#define SPFI_CLOCK0_IDLE_MASK (0x000f8000)
|
||||
#define SPFI_CLOCK0_PHASE_MASK (0x00007c00)
|
||||
#define SPFI_CS0_IDLE_MASK (0x000003e0)
|
||||
#define SPFI_DATA0_IDLE_MASK (0x0000001f)
|
||||
|
||||
#define SPIM_CLOCK0_IDLE_MASK (0x000f8000)
|
||||
#define SPIM_CLOCK0_PHASE_MASK (0x00007c00)
|
||||
#define SPIM_CS0_IDLE_MASK (0x000003e0)
|
||||
#define SPIM_DATA0_IDLE_MASK (0x0000001f)
|
||||
|
||||
#define SPIM_PORT0_MASK (0x00084210)
|
||||
|
||||
#define SPFI_PORT_SELECT_SHIFT (20)
|
||||
/* WARNING the following bits are reversed, bit 0 is highest */
|
||||
#define SPFI_CLOCK0_IDLE_SHIFT (19)
|
||||
#define SPFI_CLOCK0_PHASE_SHIFT (14)
|
||||
#define SPFI_CS0_IDLE_SHIFT (9)
|
||||
#define SPFI_DATA0_IDLE_SHIFT (4)
|
||||
|
||||
#define SPIM_CLOCK0_IDLE_SHIFT (19)
|
||||
#define SPIM_CLOCK0_PHASE_SHIFT (14)
|
||||
#define SPIM_CS0_IDLE_SHIFT (9)
|
||||
#define SPIM_DATA0_IDLE_SHIFT (4)
|
||||
|
||||
|
||||
/*
|
||||
* Interrupt registers
|
||||
* SPFI_GDOF_MASK means Rx buffer full, not an overflow, because clock stalls
|
||||
* SPFI_SDUF_MASK means Tx buffer empty, not an underflow, because clock stalls
|
||||
*/
|
||||
#define SPFI_IACCESS_MASK (0x00001000)
|
||||
#define SPFI_GDEX8BIT_MASK (0x00000800)
|
||||
#define SPFI_ALLDONE_MASK (0x00000200)
|
||||
#define SPFI_GDFUL_MASK (0x00000100)
|
||||
#define SPFI_GDHF_MASK (0x00000080)
|
||||
#define SPFI_GDEX32BIT_MASK (0x00000040)
|
||||
#define SPFI_GDTRIG_MASK (0x00000020)
|
||||
#define SPFI_SDFUL_MASK (0x00000008)
|
||||
#define SPFI_SDHF_MASK (0x00000004)
|
||||
#define SPFI_SDE_MASK (0x00000002)
|
||||
#define SPFI_SDTRIG_MASK (0x00000001)
|
||||
|
||||
#define SPFI_IACCESS_SHIFT (12)
|
||||
#define SPFI_GDEX8BIT_SHIFT (11)
|
||||
#define SPFI_ALLDONE_SHIFT (9)
|
||||
#define SPFI_GDFUL_SHIFT (8)
|
||||
#define SPFI_GDHF_SHIFT (7)
|
||||
#define SPFI_GDEX32BIT_SHIFT (6)
|
||||
#define SPFI_GDTRIG_SHIFT (5)
|
||||
#define SPFI_SDFUL_SHIFT (3)
|
||||
#define SPFI_SDHF_SHIFT (2)
|
||||
#define SPFI_SDE_SHIFT (1)
|
||||
#define SPFI_SDTRIG_SHIFT (0)
|
||||
|
||||
|
||||
/* SPFI register block */
|
||||
|
||||
#define SPFI_PORT_0_PARAM_REG_OFFSET (0x00)
|
||||
#define SPFI_PORT_1_PARAM_REG_OFFSET (0x04)
|
||||
#define SPFI_PORT_2_PARAM_REG_OFFSET (0x08)
|
||||
#define SPFI_PORT_3_PARAM_REG_OFFSET (0x0C)
|
||||
#define SPFI_PORT_4_PARAM_REG_OFFSET (0x10)
|
||||
#define SPFI_CONTROL_REG_OFFSET (0x14)
|
||||
#define SPFI_TRANSACTION_REG_OFFSET (0x18)
|
||||
#define SPFI_PORT_STATE_REG_OFFSET (0x1C)
|
||||
|
||||
#define SPFI_SEND_LONG_REG_OFFSET (0x20)
|
||||
#define SPFI_SEND_BYTE_REG_OFFSET (0x24)
|
||||
#define SPFI_GET_LONG_REG_OFFSET (0x28)
|
||||
#define SPFI_GET_BYTE_REG_OFFSET (0x2C)
|
||||
|
||||
#define SPFI_INT_STATUS_REG_OFFSET (0x30)
|
||||
#define SPFI_INT_ENABLE_REG_OFFSET (0x34)
|
||||
#define SPFI_INT_CLEAR_REG_OFFSET (0x38)
|
||||
|
||||
#define SPFI_IMMEDIATE_STATUS_REG_OFFSET (0x3c)
|
||||
|
||||
#define SPFI_FLASH_BASE_ADDRESS_REG_OFFSET (0x48)
|
||||
#define SPFI_FLASH_STATUS_REG_OFFSET (0x4C)
|
||||
|
||||
#define IMG_FALSE 0
|
||||
#define IMG_TRUE 1
|
||||
|
||||
/* Number of SPIM interfaces*/
|
||||
#define SPIM_NUM_BLOCKS 2
|
||||
/* Number of chip select lines supported by the SPI master port. */
|
||||
#define SPIM_NUM_PORTS_PER_BLOCK (SPIM_DUMMY_CS)
|
||||
/* Maximum transfer size (in bytes) for the SPI master port. */
|
||||
#define SPIM_MAX_TRANSFER_BYTES (0xFFFF)
|
||||
/* Maximum size of a flash command: command bytes+address_bytes. */
|
||||
#define SPIM_MAX_FLASH_COMMAND_BYTES (0x8)
|
||||
/* Write operation to fifo done in blocks of 16 words (64 bytes) */
|
||||
#define SPIM_MAX_BLOCK_BYTES (0x40)
|
||||
/* Number of tries until timeout error is returned*/
|
||||
#define SPI_TIMEOUT_VALUE_US 500000
|
||||
|
||||
/* SPIM initialisation function return value.*/
|
||||
enum spim_return {
|
||||
/* Initialisation parameters are valid. */
|
||||
SPIM_OK = 0,
|
||||
/* Mode parameter is invalid. */
|
||||
SPIM_INVALID_SPI_MODE,
|
||||
/* Chip select idle level is invalid. */
|
||||
SPIM_INVALID_CS_IDLE_LEVEL,
|
||||
/* Data idle level is invalid. */
|
||||
SPIM_INVALID_DATA_IDLE_LEVEL,
|
||||
/* Chip select line parameter is invalid. */
|
||||
SPIM_INVALID_CS_LINE,
|
||||
/* Transfer size parameter is invalid. */
|
||||
SPIM_INVALID_SIZE,
|
||||
/* Read/write parameter is invalid. */
|
||||
SPIM_INVALID_READ_WRITE,
|
||||
/* Continue parameter is invalid. */
|
||||
SPIM_INVALID_CONTINUE,
|
||||
/* Invalid block index */
|
||||
SPIM_INVALID_BLOCK_INDEX,
|
||||
/* Extended error values */
|
||||
/* Invalid bit rate */
|
||||
SPIM_INVALID_BIT_RATE,
|
||||
/* Invalid CS hold value */
|
||||
SPIM_INVALID_CS_HOLD_VALUE,
|
||||
/* API function called before API is initialised */
|
||||
SPIM_API_NOT_INITIALISED,
|
||||
/* SPI driver initialisation failed */
|
||||
SPIM_DRIVER_INIT_ERROR,
|
||||
/* Invalid transfer description */
|
||||
SPIM_INVALID_TRANSFER_DESC,
|
||||
/* Timeout */
|
||||
SPIM_TIMEOUT
|
||||
|
||||
};
|
||||
|
||||
/* This type defines the SPI Mode.*/
|
||||
enum spim_mode {
|
||||
/* Mode 0 (clock idle low, data valid on first clock transition). */
|
||||
SPIM_MODE_0 = 0,
|
||||
/* Mode 1 (clock idle low, data valid on second clock transition). */
|
||||
SPIM_MODE_1,
|
||||
/* Mode 2 (clock idle high, data valid on first clock transition). */
|
||||
SPIM_MODE_2,
|
||||
/* Mode 3 (clock idle high, data valid on second clock transition). */
|
||||
SPIM_MODE_3
|
||||
|
||||
};
|
||||
|
||||
/* This type defines the SPIM device numbers (chip select lines). */
|
||||
enum spim_device {
|
||||
/* Device 0 (CS0). */
|
||||
SPIM_DEVICE0 = 0,
|
||||
/* Device 1 (CS1). */
|
||||
SPIM_DEVICE1,
|
||||
/* Device 2 (CS2). */
|
||||
SPIM_DEVICE2,
|
||||
/* Device 3 (CS3). */
|
||||
SPIM_DEVICE3,
|
||||
/* Device 4 (CS4). */
|
||||
SPIM_DEVICE4,
|
||||
/* Dummy chip select. */
|
||||
SPIM_DUMMY_CS
|
||||
|
||||
};
|
||||
|
||||
/* This structure defines communication parameters for a slave device */
|
||||
struct spim_device_parameters {
|
||||
/* Bit rate value.*/
|
||||
unsigned char bitrate;
|
||||
/*
|
||||
* Chip select set up time.
|
||||
* Time taken between chip select going active and activity occurring
|
||||
* on the clock, calculated by dividing the desired set up time in ns
|
||||
* by the Input clock period. (setup time / Input clock freq)
|
||||
*/
|
||||
unsigned char cs_setup;
|
||||
/*
|
||||
* Chip select hold time.
|
||||
* Time after the last clock pulse before chip select goes inactive,
|
||||
* calculated by dividing the desired hold time in ns by the
|
||||
* Input clock period (hold time / Input clock freq).
|
||||
*/
|
||||
unsigned char cs_hold;
|
||||
/*
|
||||
* Chip select delay time (CS minimum inactive time).
|
||||
* Minimum time after chip select goes inactive before chip select
|
||||
* can go active again, calculated by dividing the desired delay time
|
||||
* in ns by the Input clock period (delay time / Input clock freq).
|
||||
*/
|
||||
unsigned char cs_delay;
|
||||
/* SPI Mode. */
|
||||
enum spim_mode spi_mode;
|
||||
/* Chip select idle level (0=low, 1=high, Others=invalid). */
|
||||
unsigned int cs_idle_level;
|
||||
/* Data idle level (0=low, 1=high, Others=invalid). */
|
||||
unsigned int data_idle_level;
|
||||
|
||||
};
|
||||
|
||||
/* Command transfer mode */
|
||||
enum command_mode {
|
||||
/* Command, address, dummy and PI cycles are transferred on sio0 */
|
||||
SPIM_CMD_MODE_0 = 0,
|
||||
/*
|
||||
* Command and Address are transferred on sio0 port only but dummy
|
||||
* cycles and PI is transferred on all the interface ports.
|
||||
*/
|
||||
SPIM_CMD_MODE_1,
|
||||
/*
|
||||
* Command is transferred on sio0 port only but address, dummy
|
||||
* and PI is transferred on all the interface portS
|
||||
*/
|
||||
SPIM_CMD_MODE_2,
|
||||
/*
|
||||
* Command, address, dummy and PI bytes are transferred on all
|
||||
* the interfaces
|
||||
*/
|
||||
SPIM_CMD_MODE_3
|
||||
};
|
||||
|
||||
/* Data transfer mode */
|
||||
enum transfer_mode {
|
||||
/* Transfer data in single mode */
|
||||
SPIM_DMODE_SINGLE = 0,
|
||||
/* Transfer data in dual mode */
|
||||
SPIM_DMODE_DUAL,
|
||||
/* Transfer data in quad mode */
|
||||
SPIM_DMODE_QUAD
|
||||
};
|
||||
|
||||
/* This structure contains parameters that describe an SPIM operation. */
|
||||
struct spim_buffer {
|
||||
/* The buffer to read from or write to. */
|
||||
unsigned char *buffer;
|
||||
|
||||
/* Number of bytes to read/write. Valid range is 0 to 65536 bytes. */
|
||||
unsigned int size;
|
||||
|
||||
/* Read/write select. TRUE for read, FALSE for write, Others-invalid.*/
|
||||
int isread;
|
||||
|
||||
/*
|
||||
* ByteDelay select.
|
||||
* Selects whether or not a delay is inserted between bytes.
|
||||
* 0 - Minimum inter-byte delay
|
||||
* 1 - Inter-byte delay of (cs_hold/master_clk half period)*master_clk.
|
||||
*/
|
||||
int inter_byte_delay;
|
||||
};
|
||||
|
||||
#endif /* __SOC_IMGTEC_DANUBE_SPI_H__ */
|
Loading…
Reference in New Issue