diff options
author | Jagan Teki <jteki@openedev.com> | 2015-08-17 18:38:06 +0530 |
---|---|---|
committer | Jagan Teki <jteki@openedev.com> | 2015-10-25 20:17:01 +0530 |
commit | 46d0a9913c2874521ea63e50b6c7593629b7e7d0 (patch) | |
tree | 6ff0c3a25fda4bded2c73dea4cbe00b8f318711d | |
parent | 677f970bc62a661690b3431543d5a5d5e682ba70 (diff) |
spi: Add zynq qspi controller driver
Added zynq qspi controller driver for Xilinx Zynq APSOC,
this driver is driver-model driven with devicetree support.
=> sf probe
SF: Detected S25FL128S_64K with page size 256 Bytes, erase size 64 KiB, total 16 MiB
=> mw.b 0x100 0xCC 0x1000000
=> sf update 0x100 0x0 0x1000000
device 0 whole chip
16777216 bytes written, 0 bytes skipped in 59.842s, speed 289262 B/s
=> sf read 0x3000000 0x0 0x1000000
device 0 whole chip
SF: 16777216 bytes @ 0x0 Read: OK
=> cmp.b 0x3000000 0x100 0x1000000
Total of 16777216 byte(s) were the same
Signed-off-by: Jagan Teki <jteki@openedev.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Acked-by: Siva Durga Prasad Paladugu <sivadur@xilinx.com>
Tested-by: Jagan Teki <jteki@openedev.com>
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/zynq_qspi.c | 622 |
2 files changed, 623 insertions, 0 deletions
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index de241be15a..637fea8544 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.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 0000000000..0ce6127052 --- /dev/null +++ b/drivers/spi/zynq_qspi.c @@ -0,0 +1,622 @@ +/* + * (C) Copyright 2013 Xilinx, Inc. + * (C) Copyright 2015 Jagan Teki <jteki@openedev.com> + * + * Xilinx Zynq Quad-SPI(QSPI) controller driver (master mode only) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <spi.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* zynq qspi register bit masks ZYNQ_QSPI_<REG>_<BIT>_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_SS_MASK (0xF << 10) /* Slave Select */ +#define ZYNQ_QSPI_CR_BAUD_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 */ + +/* zynq 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 */ + +#define ZYNQ_QSPI_TXFIFO_THRESHOLD 1 /* Tx FIFO threshold level*/ +#define ZYNQ_QSPI_RXFIFO_THRESHOLD 32 /* Rx FIFO threshold level */ + +#define ZYNQ_QSPI_CR_BAUD_MAX 8 /* Baud rate divisor max val */ +#define ZYNQ_QSPI_CR_BAUD_SHIFT 3 /* Baud rate divisor shift */ +#define ZYNQ_QSPI_CR_SS_SHIFT 10 /* Slave select shift */ + +#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 drxr; /* 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 */ +}; + +/* zynq qspi platform data */ +struct zynq_qspi_platdata { + struct zynq_qspi_regs *regs; + u32 frequency; /* input frequency */ + u32 speed_hz; +}; + +/* zynq qspi priv */ +struct zynq_qspi_priv { + struct zynq_qspi_regs *regs; + u8 cs; + u8 mode; + u8 fifo_depth; + u32 freq; /* required frequency */ + const void *tx_buf; + void *rx_buf; + unsigned len; + int bytes_to_transfer; + int bytes_to_receive; + unsigned int is_inst; + unsigned cs_change:1; +}; + +static int zynq_qspi_ofdata_to_platdata(struct udevice *bus) +{ + struct zynq_qspi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = bus->of_offset; + + plat->regs = (struct zynq_qspi_regs *)fdtdec_get_addr(blob, + node, "reg"); + + /* FIXME: Use 166MHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 166666666); + plat->speed_hz = plat->frequency / 2; + + debug("%s: regs=%p max-frequency=%d\n", __func__, + plat->regs, plat->frequency); + + return 0; +} + +static void zynq_qspi_init_hw(struct zynq_qspi_priv *priv) +{ + struct zynq_qspi_regs *regs = priv->regs; + u32 confr; + + /* Disable QSPI */ + writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->enr); + + /* Disable Interrupts */ + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->idr); + + /* Clear the TX and RX threshold reg */ + writel(ZYNQ_QSPI_TXFIFO_THRESHOLD, ®s->txftr); + writel(ZYNQ_QSPI_RXFIFO_THRESHOLD, ®s->rxftr); + + /* Clear the RX FIFO */ + while (readl(®s->isr) & ZYNQ_QSPI_IXR_RXNEMPTY_MASK) + readl(®s->drxr); + + /* Clear Interrupts */ + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->isr); + + /* Manual slave select and Auto start */ + confr = readl(®s->cr); + confr &= ~ZYNQ_QSPI_CR_MSA_MASK; + 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; + writel(confr, ®s->cr); + + /* Enable SPI */ + writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->enr); +} + +static int zynq_qspi_probe(struct udevice *bus) +{ + struct zynq_qspi_platdata *plat = dev_get_platdata(bus); + struct zynq_qspi_priv *priv = dev_get_priv(bus); + + priv->regs = plat->regs; + priv->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH; + + /* init the zynq spi hw */ + zynq_qspi_init_hw(priv); + + return 0; +} + +/* + * zynq_qspi_read_data - Copy data to RX buffer + * @zqspi: Pointer to the zynq_qspi structure + * @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_data(struct zynq_qspi_priv *priv, u32 data, u8 size) +{ + u8 byte3; + + debug("%s: data 0x%04x rx_buf addr: 0x%08x size %d\n", __func__ , + data, (unsigned)(priv->rx_buf), size); + + if (priv->rx_buf) { + switch (size) { + case 1: + *((u8 *)priv->rx_buf) = data; + priv->rx_buf += 1; + break; + case 2: + *((u16 *)priv->rx_buf) = data; + priv->rx_buf += 2; + break; + case 3: + *((u16 *)priv->rx_buf) = data; + priv->rx_buf += 2; + byte3 = (u8)(data >> 16); + *((u8 *)priv->rx_buf) = byte3; + priv->rx_buf += 1; + break; + case 4: + /* Can not assume word aligned buffer */ + memcpy(priv->rx_buf, &data, size); + priv->rx_buf += 4; + break; + default: + /* This will never execute */ + break; + } + } + priv->bytes_to_receive -= size; + if (priv->bytes_to_receive < 0) + priv->bytes_to_receive = 0; +} + +/* + * zynq_qspi_write_data - Copy data from TX buffer + * @zqspi: Pointer to the zynq_qspi structure + * @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_data(struct zynq_qspi_priv *priv, + u32 *data, u8 size) +{ + if (priv->tx_buf) { + switch (size) { + case 1: + *data = *((u8 *)priv->tx_buf); + priv->tx_buf += 1; + *data |= 0xFFFFFF00; + break; + case 2: + *data = *((u16 *)priv->tx_buf); + priv->tx_buf += 2; + *data |= 0xFFFF0000; + break; + case 3: + *data = *((u16 *)priv->tx_buf); + priv->tx_buf += 2; + *data |= (*((u8 *)priv->tx_buf) << 16); + priv->tx_buf += 1; + *data |= 0xFF000000; + break; + case 4: + /* Can not assume word aligned buffer */ + memcpy(data, priv->tx_buf, size); + priv->tx_buf += 4; + break; + default: + /* This will never execute */ + break; + } + } else { + *data = 0; + } + + debug("%s: data 0x%08x tx_buf addr: 0x%08x size %d\n", __func__, + *data, (u32)priv->tx_buf, size); + + priv->bytes_to_transfer -= size; + if (priv->bytes_to_transfer < 0) + priv->bytes_to_transfer = 0; +} + +static void zynq_qspi_chipselect(struct zynq_qspi_priv *priv, int is_on) +{ + u32 confr; + struct zynq_qspi_regs *regs = priv->regs; + + confr = readl(®s->cr); + + if (is_on) { + /* Select the slave */ + confr &= ~ZYNQ_QSPI_CR_SS_MASK; + confr |= (~(1 << priv->cs) << ZYNQ_QSPI_CR_SS_SHIFT) & + ZYNQ_QSPI_CR_SS_MASK; + } else + /* Deselect the slave */ + confr |= ZYNQ_QSPI_CR_SS_MASK; + + writel(confr, ®s->cr); +} + +/* + * zynq_qspi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible + * @zqspi: Pointer to the zynq_qspi structure + */ +static void zynq_qspi_fill_tx_fifo(struct zynq_qspi_priv *priv, u32 size) +{ + u32 data = 0; + u32 fifocount = 0; + unsigned len, offset; + struct zynq_qspi_regs *regs = priv->regs; + static const unsigned offsets[4] = { + ZYNQ_QSPI_TXD_00_00_OFFSET, ZYNQ_QSPI_TXD_00_01_OFFSET, + ZYNQ_QSPI_TXD_00_10_OFFSET, ZYNQ_QSPI_TXD_00_11_OFFSET }; + + while ((fifocount < size) && + (priv->bytes_to_transfer > 0)) { + if (priv->bytes_to_transfer >= 4) { + if (priv->tx_buf) { + memcpy(&data, priv->tx_buf, 4); + priv->tx_buf += 4; + } else { + data = 0; + } + writel(data, ®s->txd0r); + priv->bytes_to_transfer -= 4; + fifocount++; + } else { + /* Write TXD1, TXD2, TXD3 only if TxFIFO is empty. */ + if (!(readl(®s->isr) + & ZYNQ_QSPI_IXR_TXOW_MASK) && + !priv->rx_buf) + return; + len = priv->bytes_to_transfer; + zynq_qspi_write_data(priv, &data, len); + offset = (priv->rx_buf) ? offsets[0] : offsets[len]; + writel(data, ®s->cr + (offset / 4)); + } + } +} + +/* + * zynq_qspi_irq_poll - Interrupt service routine of the QSPI controller + * @zqspi: Pointer to the zynq_qspi structure + * + * This function handles TX empty and Mode Fault interrupts only. + * On TX empty interrupt this function reads the received data from RX FIFO and + * fills the TX FIFO if there is any data remaining to be transferred. + * On Mode Fault interrupt this function indicates that transfer is completed, + * the SPI subsystem will identify the error as the remaining bytes to be + * transferred is non-zero. + * + * returns: 0 for poll timeout + * 1 transfer operation complete + */ +static int zynq_qspi_irq_poll(struct zynq_qspi_priv *priv) +{ + struct zynq_qspi_regs *regs = priv->regs; + u32 rxindex = 0; + u32 rxcount; + u32 status, timeout; + + /* Poll until any of the interrupt status bits are set */ + timeout = get_timer(0); + do { + status = readl(®s->isr); + } while ((status == 0) && + (get_timer(timeout) < CONFIG_SYS_ZYNQ_QSPI_WAIT)); + + if (status == 0) { + printf("zynq_qspi_irq_poll: Timeout!\n"); + return -ETIMEDOUT; + } + + writel(status, ®s->isr); + + /* Disable all interrupts */ + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->idr); + if ((status & ZYNQ_QSPI_IXR_TXOW_MASK) || + (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)) { + /* + * This bit is set when Tx FIFO has < THRESHOLD entries. We have + * the THRESHOLD value set to 1, so this bit indicates Tx FIFO + * is empty + */ + rxcount = priv->bytes_to_receive - priv->bytes_to_transfer; + rxcount = (rxcount % 4) ? ((rxcount/4)+1) : (rxcount/4); + while ((rxindex < rxcount) && + (rxindex < ZYNQ_QSPI_RXFIFO_THRESHOLD)) { + /* Read out the data from the RX FIFO */ + u32 data; + data = readl(®s->drxr); + + if (priv->bytes_to_receive >= 4) { + if (priv->rx_buf) { + memcpy(priv->rx_buf, &data, 4); + priv->rx_buf += 4; + } + priv->bytes_to_receive -= 4; + } else { + zynq_qspi_read_data(priv, data, + priv->bytes_to_receive); + } + rxindex++; + } + + if (priv->bytes_to_transfer) { + /* There is more data to send */ + zynq_qspi_fill_tx_fifo(priv, + ZYNQ_QSPI_RXFIFO_THRESHOLD); + + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->ier); + } else { + /* + * If transfer and receive is completed then only send + * complete signal + */ + if (!priv->bytes_to_receive) { + /* return operation complete */ + writel(ZYNQ_QSPI_IXR_ALL_MASK, + ®s->idr); + return 1; + } + } + } + + return 0; +} + +/* + * zynq_qspi_start_transfer - Initiates the QSPI transfer + * @qspi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provide information + * about next transfer parameters + * + * This function fills the TX FIFO, starts the QSPI transfer, and waits for the + * transfer to be completed. + * + * returns: Number of bytes transferred in the last transfer + */ +static int zynq_qspi_start_transfer(struct zynq_qspi_priv *priv) +{ + u32 data = 0; + struct zynq_qspi_regs *regs = priv->regs; + + debug("%s: qspi: 0x%08x transfer: 0x%08x len: %d\n", __func__, + (u32)priv, (u32)priv, priv->len); + + priv->bytes_to_transfer = priv->len; + priv->bytes_to_receive = priv->len; + + if (priv->len < 4) + zynq_qspi_fill_tx_fifo(priv, priv->len); + else + zynq_qspi_fill_tx_fifo(priv, priv->fifo_depth); + + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->ier); + /* Start the transfer by enabling manual start bit */ + + /* wait for completion */ + do { + data = zynq_qspi_irq_poll(priv); + } while (data == 0); + + return (priv->len) - (priv->bytes_to_transfer); +} + +static int zynq_qspi_transfer(struct zynq_qspi_priv *priv) +{ + unsigned cs_change = 1; + int status = 0; + + while (1) { + /* Select the chip if required */ + if (cs_change) + zynq_qspi_chipselect(priv, 1); + + cs_change = priv->cs_change; + + if (!priv->tx_buf && !priv->rx_buf && priv->len) { + status = -1; + break; + } + + /* Request the transfer */ + if (priv->len) { + status = zynq_qspi_start_transfer(priv); + priv->is_inst = 0; + } + + if (status != priv->len) { + if (status > 0) + status = -EMSGSIZE; + debug("zynq_qspi_transfer:%d len:%d\n", + status, priv->len); + break; + } + status = 0; + + if (cs_change) + /* Deselect the chip */ + zynq_qspi_chipselect(priv, 0); + + break; + } + + return 0; +} + +static int zynq_qspi_claim_bus(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct zynq_qspi_priv *priv = dev_get_priv(bus); + struct zynq_qspi_regs *regs = priv->regs; + + writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->enr); + + return 0; +} + +static int zynq_qspi_release_bus(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct zynq_qspi_priv *priv = dev_get_priv(bus); + struct zynq_qspi_regs *regs = priv->regs; + + writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->enr); + + return 0; +} + +static int zynq_qspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct zynq_qspi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + + priv->cs = slave_plat->cs; + priv->tx_buf = dout; + priv->rx_buf = din; + priv->len = bitlen / 8; + + debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n", + bus->seq, slave_plat->cs, bitlen, priv->len, flags); + + /* + * Festering sore. + * Assume that the beginning of a transfer with bits to + * transmit must contain a device command. + */ + if (dout && flags & SPI_XFER_BEGIN) + priv->is_inst = 1; + else + priv->is_inst = 0; + + if (flags & SPI_XFER_END) + priv->cs_change = 1; + else + priv->cs_change = 0; + + zynq_qspi_transfer(priv); + + return 0; +} + +static int zynq_qspi_set_speed(struct udevice *bus, uint speed) +{ + struct zynq_qspi_platdata *plat = bus->platdata; + struct zynq_qspi_priv *priv = dev_get_priv(bus); + struct zynq_qspi_regs *regs = priv->regs; + uint32_t confr; + u8 baud_rate_val = 0; + + if (speed > plat->frequency) + speed = plat->frequency; + + /* Set the clock frequency */ + confr = readl(®s->cr); + if (speed == 0) { + /* Set baudrate x8, if the freq is 0 */ + baud_rate_val = 0x2; + } else if (plat->speed_hz != speed) { + while ((baud_rate_val < ZYNQ_QSPI_CR_BAUD_MAX) && + ((plat->frequency / + (2 << baud_rate_val)) > speed)) + baud_rate_val++; + + plat->speed_hz = speed / (2 << baud_rate_val); + } + confr &= ~ZYNQ_QSPI_CR_BAUD_MASK; + confr |= (baud_rate_val << ZYNQ_QSPI_CR_BAUD_SHIFT); + + writel(confr, ®s->cr); + priv->freq = speed; + + debug("zynq_spi_set_speed: regs=%p, mode=%d\n", priv->regs, priv->freq); + + return 0; +} + +static int zynq_qspi_set_mode(struct udevice *bus, uint mode) +{ + struct zynq_qspi_priv *priv = dev_get_priv(bus); + struct zynq_qspi_regs *regs = priv->regs; + uint32_t confr; + + /* Set the SPI Clock phase and polarities */ + confr = readl(®s->cr); + confr &= ~(ZYNQ_QSPI_CR_CPHA_MASK | ZYNQ_QSPI_CR_CPOL_MASK); + + if (priv->mode & SPI_CPHA) + confr |= ZYNQ_QSPI_CR_CPHA_MASK; + if (priv->mode & SPI_CPOL) + confr |= ZYNQ_QSPI_CR_CPOL_MASK; + + writel(confr, ®s->cr); + priv->mode = mode; + + debug("zynq_spi_set_mode: regs=%p, mode=%d\n", priv->regs, priv->mode); + + return 0; +} + +static const struct dm_spi_ops zynq_qspi_ops = { + .claim_bus = zynq_qspi_claim_bus, + .release_bus = zynq_qspi_release_bus, + .xfer = zynq_qspi_xfer, + .set_speed = zynq_qspi_set_speed, + .set_mode = zynq_qspi_set_mode, +}; + +static const struct udevice_id zynq_qspi_ids[] = { + { .compatible = "xlnx,zynq-qspi-1.0" }, + { } +}; + +U_BOOT_DRIVER(zynq_qspi) = { + .name = "zynq_qspi", + .id = UCLASS_SPI, + .of_match = zynq_qspi_ids, + .ops = &zynq_qspi_ops, + .ofdata_to_platdata = zynq_qspi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct zynq_qspi_platdata), + .priv_auto_alloc_size = sizeof(struct zynq_qspi_priv), + .probe = zynq_qspi_probe, +}; |