From: Jagannadha Sutradharudu Teki Date: Mon, 29 Jul 2013 18:15:16 +0000 (+0530) Subject: spi: Add zynq spi controller driver X-Git-Tag: v2013.10-rc1~39^2~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1465d055f9d7a81edacf30c9d20a1b51dfcbfa8d;p=kernel%2Fu-boot.git spi: Add zynq spi controller driver Zynq spi controller driver supports 2 buses and 3 chipselects on each bus. Signed-off-by: Jagannadha Sutradharudu Teki Acked-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- diff --git a/arch/arm/include/asm/arch-zynq/hardware.h b/arch/arm/include/asm/arch-zynq/hardware.h index 25f0e3d..081624e 100644 --- a/arch/arm/include/asm/arch-zynq/hardware.h +++ b/arch/arm/include/asm/arch-zynq/hardware.h @@ -17,6 +17,8 @@ #define ZYNQ_SDHCI_BASEADDR1 0xE0101000 #define ZYNQ_I2C_BASEADDR0 0xE0004000 #define ZYNQ_I2C_BASEADDR1 0xE0005000 +#define ZYNQ_SPI_BASEADDR0 0xE0006000 +#define ZYNQ_SPI_BASEADDR1 0xE0007000 /* Reflect slcr offsets */ struct slcr_regs { diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 019132e..91d24ce 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ COBJS-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o COBJS-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o COBJS-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o +COBJS-$(CONFIG_ZYNQ_SPI) += zynq_spi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/spi/zynq_spi.c b/drivers/spi/zynq_spi.c new file mode 100644 index 0000000..5da8759 --- /dev/null +++ b/drivers/spi/zynq_spi.c @@ -0,0 +1,280 @@ +/* + * (C) Copyright 2013 Inc. + * + * Xilinx Zynq PS SPI controller driver (master mode only) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* zynq spi register bit masks ZYNQ_SPI___MASK */ +#define ZYNQ_SPI_CR_MSA_MASK (1 << 15) /* Manual start enb */ +#define ZYNQ_SPI_CR_MCS_MASK (1 << 14) /* Manual chip select */ +#define ZYNQ_SPI_CR_CS_MASK (0xF << 10) /* Chip select */ +#define ZYNQ_SPI_CR_BRD_MASK (0x7 << 3) /* Baud rate div */ +#define ZYNQ_SPI_CR_CPHA_MASK (1 << 2) /* Clock phase */ +#define ZYNQ_SPI_CR_CPOL_MASK (1 << 1) /* Clock polarity */ +#define ZYNQ_SPI_CR_MSTREN_MASK (1 << 0) /* Mode select */ +#define ZYNQ_SPI_IXR_RXNEMPTY_MASK (1 << 4) /* RX_FIFO_not_empty */ +#define ZYNQ_SPI_IXR_TXOW_MASK (1 << 2) /* TX_FIFO_not_full */ +#define ZYNQ_SPI_IXR_ALL_MASK 0x7F /* All IXR bits */ +#define ZYNQ_SPI_ENR_SPI_EN_MASK (1 << 0) /* SPI Enable */ + +#define ZYNQ_SPI_FIFO_DEPTH 128 +#ifndef CONFIG_SYS_ZYNQ_SPI_WAIT +#define CONFIG_SYS_ZYNQ_SPI_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */ +#endif + +/* zynq spi register set */ +struct zynq_spi_regs { + u32 cr; /* 0x00 */ + u32 isr; /* 0x04 */ + u32 ier; /* 0x08 */ + u32 idr; /* 0x0C */ + u32 imr; /* 0x10 */ + u32 enr; /* 0x14 */ + u32 dr; /* 0x18 */ + u32 txdr; /* 0x1C */ + u32 rxdr; /* 0x20 */ +}; + +/* zynq spi slave */ +struct zynq_spi_slave { + struct spi_slave slave; + struct zynq_spi_regs *base; + u8 mode; + u8 fifo_depth; + u32 speed_hz; + u32 input_hz; + u32 req_hz; +}; + +static inline struct zynq_spi_slave *to_zynq_spi_slave(struct spi_slave *slave) +{ + return container_of(slave, struct zynq_spi_slave, slave); +} + +static inline struct zynq_spi_regs *get_zynq_spi_base(int dev) +{ + if (dev) + return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR1; + else + return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR0; +} + +static void zynq_spi_init_hw(struct zynq_spi_slave *zslave) +{ + u32 confr; + + /* Disable SPI */ + writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + + /* Disable Interrupts */ + writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->idr); + + /* Clear RX FIFO */ + while (readl(&zslave->base->isr) & + ZYNQ_SPI_IXR_RXNEMPTY_MASK) + readl(&zslave->base->rxdr); + + /* Clear Interrupts */ + writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->isr); + + /* Manual slave select and Auto start */ + confr = ZYNQ_SPI_CR_MCS_MASK | ZYNQ_SPI_CR_CS_MASK | + ZYNQ_SPI_CR_MSTREN_MASK; + confr &= ~ZYNQ_SPI_CR_MSA_MASK; + writel(confr, &zslave->base->cr); + + /* Enable SPI */ + writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + /* 2 bus with 3 chipselect */ + return bus < 2 && cs < 3; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + u32 cr; + + debug("spi_cs_activate: 0x%08x\n", (u32)slave); + + clrbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); + cr = readl(&zslave->base->cr); + /* + * CS cal logic: CS[13:10] + * xxx0 - cs0 + * xx01 - cs1 + * x011 - cs2 + */ + cr |= (~(0x1 << slave->cs) << 10) & ZYNQ_SPI_CR_CS_MASK; + writel(cr, &zslave->base->cr); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + + debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); + + setbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); +} + +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_spi_slave *zslave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + zslave = spi_alloc_slave(struct zynq_spi_slave, bus, cs); + if (!zslave) { + printf("SPI_error: Fail to allocate zynq_spi_slave\n"); + return NULL; + } + + zslave->base = get_zynq_spi_base(bus); + zslave->mode = mode; + zslave->fifo_depth = ZYNQ_SPI_FIFO_DEPTH; + zslave->input_hz = 166666700; + zslave->speed_hz = zslave->input_hz / 2; + zslave->req_hz = max_hz; + + /* init the zynq spi hw */ + zynq_spi_init_hw(zslave); + + return &zslave->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + + debug("spi_free_slave: 0x%08x\n", (u32)slave); + free(zslave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + u32 confr = 0; + u8 baud_rate_val = 0; + + writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + + /* Set the SPI Clock phase and polarities */ + confr = readl(&zslave->base->cr); + confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK); + if (zslave->mode & SPI_CPHA) + confr |= ZYNQ_SPI_CR_CPHA_MASK; + if (zslave->mode & SPI_CPOL) + confr |= ZYNQ_SPI_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_SPI_CR_BRD_MASK; + confr |= (baud_rate_val << 3); + writel(confr, &zslave->base->cr); + + writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + + debug("spi_release_bus: 0x%08x\n", (u32)slave); + writel(~ZYNQ_SPI_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_spi_slave *zslave = to_zynq_spi_slave(slave); + u32 len = bitlen / 8; + u32 tx_len = len, rx_len = len, tx_tvl; + const u8 *tx_buf = dout; + u8 *rx_buf = din, buf; + u32 ts, 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); + + while (rx_len > 0) { + /* Write the data into TX FIFO - tx threshold is fifo_depth */ + tx_tvl = 0; + while ((tx_tvl < zslave->fifo_depth) && tx_len) { + if (tx_buf) + buf = *tx_buf++; + else + buf = 0; + writel(buf, &zslave->base->txdr); + tx_len--; + tx_tvl++; + } + + /* Check TX FIFO completion */ + ts = get_timer(0); + status = readl(&zslave->base->isr); + while (!(status & ZYNQ_SPI_IXR_TXOW_MASK)) { + if (get_timer(ts) > CONFIG_SYS_ZYNQ_SPI_WAIT) { + printf("spi_xfer: Timeout! TX FIFO not full\n"); + return -1; + } + status = readl(&zslave->base->isr); + } + + /* Read the data from RX FIFO */ + status = readl(&zslave->base->isr); + while (status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) { + buf = readl(&zslave->base->rxdr); + if (rx_buf) + *rx_buf++ = buf; + status = readl(&zslave->base->isr); + rx_len--; + } + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +}