From mboxrd@z Thu Jan 1 00:00:00 1970 From: Dinh Nguyen Date: Tue, 05 Nov 2013 22:44:40 -0600 Subject: [U-Boot] [PATCH 10/34] spi: Add zynq qspi controller driver In-Reply-To: <72fa072b-a1a1-400e-9c62-a2966fe2c124@DB9EHSMHS010.ehs.local> References: <1383673615-32473-1-git-send-email-jaganna@xilinx.com> <72fa072b-a1a1-400e-9c62-a2966fe2c124@DB9EHSMHS010.ehs.local> Message-ID: <5279C938.1000607@gmail.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de On 11/5/13 11:46 AM, Jagannadha Sutradharudu Teki wrote: > Zynq qspi controller driver supports single bus > with singe chipselect. > > Zynq qspi can be operated in below connection modes > - single qspi > - dual qspi, with dual stacked > - dual qspi, with dual parallel > > Signed-off-by: Jagannadha Sutradharudu Teki > --- > arch/arm/include/asm/arch-zynq/hardware.h | 1 + > drivers/spi/Makefile | 1 + > drivers/spi/zynq_qspi.c | 447 ++++++++++++++++++++++++++++++ > 3 files changed, 449 insertions(+) > create mode 100644 drivers/spi/zynq_qspi.c > > diff --git a/arch/arm/include/asm/arch-zynq/hardware.h b/arch/arm/include/asm/arch-zynq/hardware.h > index cd69677..05870ae 100644 > --- a/arch/arm/include/asm/arch-zynq/hardware.h > +++ b/arch/arm/include/asm/arch-zynq/hardware.h > @@ -19,6 +19,7 @@ > #define ZYNQ_I2C_BASEADDR1 0xE0005000 > #define ZYNQ_SPI_BASEADDR0 0xE0006000 > #define ZYNQ_SPI_BASEADDR1 0xE0007000 > +#define ZYNQ_QSPI_BASEADDR 0xE000D000 > #define ZYNQ_DDRC_BASEADDR 0xF8006000 > > /* Reflect slcr offsets */ > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index 27902fe..5fafee0 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -37,3 +37,4 @@ obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o > obj-$(CONFIG_TI_QSPI) += ti_qspi.o > obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o > obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o > +obj-$(CONFIG_ZYNQ_QSPI) += zynq_qspi.o > diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c > new file mode 100644 > index 0000000..f38ebca > --- /dev/null > +++ b/drivers/spi/zynq_qspi.c > @@ -0,0 +1,447 @@ > +/* > + * (C) Copyright 2013 Xilinx, Inc. > + * > + * Zynq PS Quad-SPI(QSPI) controller driver (master mode only) > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* zynq spi register bit masks ZYNQ_QSPI___MASK */ > +#define ZYNQ_QSPI_CR_IFMODE_MASK (1 << 31) /* Flash intrface mode*/ > +#define ZYNQ_QSPI_CR_MSA_MASK (1 << 15) /* Manual start enb */ > +#define ZYNQ_QSPI_CR_MCS_MASK (1 << 14) /* Manual chip select */ > +#define ZYNQ_QSPI_CR_PCS_MASK (1 << 10) /* Peri chip select */ > +#define ZYNQ_QSPI_CR_FW_MASK (0x3 << 6) /* FIFO width */ > +#define ZYNQ_QSPI_CR_BRD_MASK (0x7 << 3) /* Baud rate div */ > +#define ZYNQ_QSPI_CR_CPHA_MASK (1 << 2) /* Clock phase */ > +#define ZYNQ_QSPI_CR_CPOL_MASK (1 << 1) /* Clock polarity */ > +#define ZYNQ_QSPI_CR_MSTREN_MASK (1 << 0) /* Mode select */ > +#define ZYNQ_QSPI_IXR_RXNEMPTY_MASK (1 << 4) /* RX_FIFO_not_empty */ > +#define ZYNQ_QSPI_IXR_TXOW_MASK (1 << 2) /* TX_FIFO_not_full */ > +#define ZYNQ_QSPI_IXR_ALL_MASK 0x7F /* All IXR bits */ > +#define ZYNQ_QSPI_ENR_SPI_EN_MASK (1 << 0) /* SPI Enable */ > + > +/* QSPI Transmit Data Register */ > +#define ZYNQ_QSPI_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst */ > +#define ZYNQ_QSPI_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst */ > +#define ZYNQ_QSPI_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst */ > +#define ZYNQ_QSPI_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst */ > + > +/* Definitions of the flash commands - Flash insts in ascending order */ > +#define ZYNQ_QSPI_FLASH_INST_WRSR 0x01 /* Write status register */ > +#define ZYNQ_QSPI_FLASH_INST_PP 0x02 /* Page program */ > +#define ZYNQ_QSPI_FLASH_INST_WRDS 0x04 /* Write disable */ > +#define ZYNQ_QSPI_FLASH_INST_RDSR1 0x05 /* Read status register 1 */ > +#define ZYNQ_QSPI_FLASH_INST_WREN 0x06 /* Write enable */ > +#define ZYNQ_QSPI_FLASH_INST_AFR 0x0B /* Fast read data bytes */ > +#define ZYNQ_QSPI_FLASH_INST_BE_4K 0x20 /* Erase 4KiB block */ > +#define ZYNQ_QSPI_FLASH_INST_RDSR2 0x35 /* Read status register 2 */ > +#define ZYNQ_QSPI_FLASH_INST_BE_32K 0x52 /* Erase 32KiB block */ > +#define ZYNQ_QSPI_FLASH_INST_RDID 0x9F /* Read JEDEC ID */ > +#define ZYNQ_QSPI_FLASH_INST_SE 0xD8 /* Sector erase (usually 64KB)*/ > + > +#define ZYNQ_QSPI_FIFO_DEPTH 63 > +#ifndef CONFIG_SYS_ZYNQ_QSPI_WAIT > +#define CONFIG_SYS_ZYNQ_QSPI_WAIT CONFIG_SYS_HZ/100 /* 10 ms */ > +#endif > + > +/* zynq qspi register set */ > +struct zynq_qspi_regs { > + u32 cr; /* 0x00 */ > + u32 isr; /* 0x04 */ > + u32 ier; /* 0x08 */ > + u32 idr; /* 0x0C */ > + u32 imr; /* 0x10 */ > + u32 enr; /* 0x14 */ > + u32 dr; /* 0x18 */ > + u32 txd0r; /* 0x1C */ > + u32 rxdr; /* 0x20 */ > + u32 sicr; /* 0x24 */ > + u32 txftr; /* 0x28 */ > + u32 rxftr; /* 0x2C */ > + u32 gpior; /* 0x30 */ > + u32 reserved0[19]; > + u32 txd1r; /* 0x80 */ > + u32 txd2r; /* 0x84 */ > + u32 txd3r; /* 0x88 */ > +}; > + > +/* > + * struct zynq_qspi_inst_format - Defines qspi flash instruction format > + * @inst: Instruction code > + * @inst_size: Size of the instruction including address bytes > + * @inst_off: Register address where instruction has to be written > + */ > +struct zynq_qspi_inst_format { > + u8 inst; > + u8 inst_size; > + u8 inst_off; > +}; > + > +/* List of all the QSPI instructions and its format */ > +static struct zynq_qspi_inst_format flash_inst[] = { > + {ZYNQ_QSPI_FLASH_INST_WRSR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_PP, 4, ZYNQ_QSPI_TXD_00_00_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_WRDS, 1, ZYNQ_QSPI_TXD_00_01_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_RDSR1, 1, ZYNQ_QSPI_TXD_00_01_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_WREN, 1, ZYNQ_QSPI_TXD_00_01_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_AFR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_BE_4K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_RDSR2, 1, ZYNQ_QSPI_TXD_00_01_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_BE_32K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_RDID, 1, ZYNQ_QSPI_TXD_00_01_OFFSET}, > + {ZYNQ_QSPI_FLASH_INST_SE, 4, ZYNQ_QSPI_TXD_00_00_OFFSET}, > + /* Add all the instructions supported by the flash device */ > +}; > + > +/* zynq spi slave */ > +struct zynq_qspi_slave { > + struct spi_slave slave; > + struct zynq_qspi_regs *base; > + u8 mode; > + u8 is_inst; > + u8 fifo_depth; > + const void *tx_buf; > + void *rx_buf; > + u32 tx_len; > + u32 rx_len; > + u32 speed_hz; > + u32 input_hz; > + u32 req_hz; > +}; > + > +static inline struct zynq_qspi_slave *to_zynq_qspi_slave( > + struct spi_slave *slave) > +{ > + return container_of(slave, struct zynq_qspi_slave, slave); > +} > + > +static void zynq_qspi_init_hw(struct zynq_qspi_slave *zslave) > +{ > + u32 confr; > + > + /* Disable SPI */ > + writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr); > + > + /* Disable Interrupts */ > + writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->idr); > + > + /* Clear RX FIFO */ > + while (readl(&zslave->base->isr) & > + ZYNQ_QSPI_IXR_RXNEMPTY_MASK) > + readl(&zslave->base->rxdr); > + > + /* Clear Interrupts */ > + writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->isr); > + > + /* Manual slave select and Auto start */ > + confr = ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK | > + ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK | > + ZYNQ_QSPI_CR_MSTREN_MASK; > + confr &= ~ZYNQ_QSPI_CR_MSA_MASK; > + confr &= ~ZYNQ_QSPI_CR_MSA_MASK; Why &= ~ZYNQ_QSPI_CR_MSA_MASK twice? > + writel(confr, &zslave->base->cr); > + > + /* Enable SPI */ > + writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr); > +} > + > +/* > + * zynq_qspi_read - Copy data to RX buffer > + * @zqspi: Pointer to zynq_qspi_slave > + * @data: The 32 bit variable where data is stored > + * @size: Number of bytes to be copied from data to RX buffer > + */ > +static void zynq_qspi_read(struct zynq_qspi_slave *zslave, u32 data, u8 size) > +{ > + if (zslave->rx_buf) { > + data >>= (4 - size) * 8; > + data = le32_to_cpu(data); > + memcpy((u8 *)zslave->rx_buf, &data, size); > + zslave->rx_buf += size; > + } > + > + zslave->rx_len -= size; > +} > + > +/* > + * zynq_qspi_write - Copy data from TX buffer > + * @zslave: Pointer to zynq_qspi_slave > + * @data: Pointer to the 32 bit variable where data is to be copied > + * @size: Number of bytes to be copied from TX buffer to data > + */ > +static void zynq_qspi_write(struct zynq_qspi_slave *zslave, u32 *data, u8 size) > +{ > + if (zslave->tx_buf) { > + switch (size) { > + case 1: > + *data = *((u8 *)zslave->tx_buf); > + zslave->tx_buf += 1; > + *data |= 0xFFFFFF00; > + break; > + case 2: > + *data = *((u16 *)zslave->tx_buf); > + zslave->tx_buf += 2; > + *data |= 0xFFFF0000; > + break; > + case 3: > + *data = *((u16 *)zslave->tx_buf); > + zslave->tx_buf += 2; > + *data |= (*((u8 *)zslave->tx_buf) << 16); > + zslave->tx_buf += 1; > + *data |= 0xFF000000; > + break; > + case 4: > + /* Can not assume word aligned buffer */ > + memcpy(data, zslave->tx_buf, size); > + zslave->tx_buf += 4; > + break; > + default: > + /* This will never execute */ > + break; > + } > + } else { > + *data = 0; > + } > + > + zslave->tx_len -= size; > +} > + > +static int zynq_qspi_check_txfifo(struct zynq_qspi_slave *zslave) > +{ > + u32 ts, status; > + > + ts = get_timer(0); > + status = readl(&zslave->base->isr); > + while (!(status & ZYNQ_QSPI_IXR_TXOW_MASK)) { > + if (get_timer(ts) > CONFIG_SYS_ZYNQ_QSPI_WAIT) { > + printf("spi_xfer: Timeout! TX FIFO not full\n"); > + return -1; > + } > + status = readl(&zslave->base->isr); > + } > + > + return 0; > +} > + > +static int zynq_qspi_process_tx(struct zynq_qspi_slave *zslave) > +{ > + struct zynq_qspi_inst_format *curr_inst; > + u8 inst, index; > + u32 buf; > + > + inst = *(u8 *)zslave->tx_buf; > + /* instuction */ > + if (inst && zslave->is_inst) { > + for (index = 0; index < ARRAY_SIZE(flash_inst); index++) > + if (inst == flash_inst[index].inst) > + break; > + > + if (index == ARRAY_SIZE(flash_inst)) { > + printf("spi_xfer: Unsupported inst %02x\n", inst); > + return -1; > + } > + > + curr_inst = &flash_inst[index]; > + debug("spi_xfer: inst:%02x inst_size:%d inst_off:%02x\n", > + curr_inst->inst, curr_inst->inst_size, > + curr_inst->inst_off); > + > + zynq_qspi_write(zslave, &buf, curr_inst->inst_size); > + writel(buf, &zslave->base->cr + (curr_inst->inst_off / 4)); > + zslave->is_inst = 0; > + } else if (!zslave->is_inst) { /* addr + data */ > + if (zslave->tx_len < 4) { > + /* Check TXOW for txd1, txd2 and txd3 */ > + if (zynq_qspi_check_txfifo(zslave) < 0) > + return -1; > + > + zynq_qspi_write(zslave, &buf, zslave->tx_len); > + writel(buf, > + &zslave->base->txd1r + (zslave->tx_len - 1)); > + } else { > + zynq_qspi_write(zslave, &buf, 4); > + writel(buf, &zslave->base->txd0r); > + } > + } > + > + return 0; > +} > + > +int spi_cs_is_valid(unsigned int bus, unsigned int cs) > +{ > + /* 1 bus with 1 chipselect */ > + return bus < 1 && cs < 1; > +} > + > +void spi_cs_activate(struct spi_slave *slave) > +{ > + struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave); > + > + debug("spi_cs_activate: 0x%08x\n", (u32)slave); > + clrbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK); > + > + zslave->is_inst = 1; > +} > + > +void spi_cs_deactivate(struct spi_slave *slave) > +{ > + struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave); > + > + debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); > + setbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK); > + > + zslave->is_inst = 0; > +} > + > +void spi_init() > +{ > + /* nothing to do */ > +} > + > +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, > + unsigned int max_hz, unsigned int mode) > +{ > + struct zynq_qspi_slave *zslave; > + > + if (!spi_cs_is_valid(bus, cs)) > + return NULL; > + > + zslave = spi_alloc_slave(struct zynq_qspi_slave, bus, cs); > + if (!zslave) { > + printf("SPI_error: Fail to allocate zynq_qspi_slave\n"); > + return NULL; > + } > + > + zslave->base = (struct zynq_qspi_regs *)ZYNQ_QSPI_BASEADDR; > + zslave->mode = mode; > + zslave->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH; > + zslave->input_hz = 200000000; Should this be a CONFIG define? Dinh > + zslave->speed_hz = zslave->input_hz / 2; > + zslave->req_hz = max_hz; > + > + /* init the zynq spi hw */ > + zynq_qspi_init_hw(zslave); > + > + return &zslave->slave; > +} > + > +void spi_free_slave(struct spi_slave *slave) > +{ > + struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave); > + > + debug("spi_free_slave: 0x%08x\n", (u32)slave); > + free(zslave); > +} > + > +int spi_claim_bus(struct spi_slave *slave) > +{ > + struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave); > + u32 confr = 0; > + u8 baud_rate_val = 0; > + > + writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr); > + > + /* Set the SPI Clock phase and polarities */ > + confr = readl(&zslave->base->cr); > + confr &= ~(ZYNQ_QSPI_CR_CPHA_MASK | ZYNQ_QSPI_CR_CPOL_MASK); > + if (zslave->mode & SPI_CPHA) > + confr |= ZYNQ_QSPI_CR_CPHA_MASK; > + if (zslave->mode & SPI_CPOL) > + confr |= ZYNQ_QSPI_CR_CPOL_MASK; > + > + /* Set the clock frequency */ > + if (zslave->req_hz == 0) { > + /* Set baudrate x8, if the req_hz is 0 */ > + baud_rate_val = 0x2; > + } else if (zslave->speed_hz != zslave->req_hz) { > + while ((baud_rate_val < 8) && > + ((zslave->input_hz / > + (2 << baud_rate_val)) > zslave->req_hz)) > + baud_rate_val++; > + zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val); > + } > + confr &= ~ZYNQ_QSPI_CR_BRD_MASK; > + confr |= (baud_rate_val << 3); > + writel(confr, &zslave->base->cr); > + > + writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr); > + > + return 0; > +} > + > +void spi_release_bus(struct spi_slave *slave) > +{ > + struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave); > + > + debug("spi_release_bus: 0x%08x\n", (u32)slave); > + writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr); > +} > + > +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, > + void *din, unsigned long flags) > +{ > + struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave); > + u32 len = bitlen / 8, tx_tvl; > + u32 buf, status; > + > + debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n", > + slave->bus, slave->cs, bitlen, len, flags); > + > + if (bitlen == 0) > + return -1; > + > + if (bitlen % 8) { > + debug("spi_xfer: Non byte aligned SPI transfer\n"); > + return -1; > + } > + > + if (flags & SPI_XFER_BEGIN) > + spi_cs_activate(slave); > + > + zslave->tx_len = len; > + zslave->rx_len = len; > + zslave->tx_buf = dout; > + zslave->rx_buf = din; > + while (zslave->rx_len > 0) { > + /* Write the data into TX FIFO - tx threshold is fifo_depth */ > + tx_tvl = 0; > + while ((tx_tvl < zslave->fifo_depth) && zslave->tx_len) { > + if (zynq_qspi_process_tx(zslave) < 0) { > + flags |= SPI_XFER_END; > + goto out; > + } > + tx_tvl++; > + } > + > + /* Check TX FIFO completion */ > + if (zynq_qspi_check_txfifo(zslave) < 0) { > + flags |= SPI_XFER_END; > + goto out; > + } > + > + /* Read the data from RX FIFO */ > + status = readl(&zslave->base->isr); > + while (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK) { > + buf = readl(&zslave->base->rxdr); > + if (zslave->rx_len < 4) > + zynq_qspi_read(zslave, buf, zslave->rx_len); > + else > + zynq_qspi_read(zslave, buf, 4); > + status = readl(&zslave->base->isr); > + } > + } > + > +out: > + if (flags & SPI_XFER_END) > + spi_cs_deactivate(slave); > + > + return 0; > +}