From: Brian Hill Date: Fri, 28 Jan 2011 20:40:04 +0000 (-0700) Subject: Xilinx: ARM: Initial QSPI support X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8e8257d2eb04b2c1221fdf429d91a798a5ffc740;p=thirdparty%2Fu-boot.git Xilinx: ARM: Initial QSPI support Initial support of QSPI. Driver ported directly from Linux - primary operational change is the use of polling rather than interrupts. Commands: sf probe 0 0 0 sf erase Where must exactly fall on a sector boundary (N25Q128 = 1k) sf write sf read Common file drivers/mtd/spi/stmicro.c modified to recognized N25Q128 device. Example session: sf probe 0 0 0 sf erase 0x100000 0x140000 tftp 0x8000 vmlinux.bin sf write 0x8000 0x100000 0x140000 sf read 0x800000 0x100000 0x140000 NOTE: Use of SF commands disables linear flash. QSPI not avalable at 0xFC000000 until again set up by boot ROM (post reset). --- diff --git a/board/xilinx/dfe/Makefile b/board/xilinx/dfe/Makefile index 2350ef8596d..60685221bd7 100644 --- a/board/xilinx/dfe/Makefile +++ b/board/xilinx/dfe/Makefile @@ -32,6 +32,8 @@ COBJS := board.o \ xemacpss_g.o xemacpss_control.o xemacpss_sinit.o \ xemacpss_bdring.o xemacpss.o xgmac.o \ xilinx_nandpss.o \ + xilinx_qspipss.o \ + pele_qspi.o \ ../common/xbasic_types.o SOBJS := lowlevel_init.o diff --git a/board/xilinx/dfe/pele_qspi.c b/board/xilinx/dfe/pele_qspi.c new file mode 100644 index 00000000000..cc61babc8e4 --- /dev/null +++ b/board/xilinx/dfe/pele_qspi.c @@ -0,0 +1,162 @@ +/* + * Driver for Pele On-Chip SPI device + * + * Copyright (c) 2010 Xilinx + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include + +#include + +#include "xilinx_qspipss.h" + +struct pele_spi_slave { + struct spi_slave slave; + struct spi_device qspi; +}; + +#define to_pele_spi_slave(s) container_of(s, struct pele_spi_slave, slave) + +__attribute__((weak)) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ +#ifdef DEBUG + printf("spi_cs_is_valid: bus: %d cs: %d\n", + bus, cs); +#endif + return 1; +} + +__attribute__((weak)) +void spi_cs_activate(struct spi_slave *slave) +{ +#ifdef DEBUG + printf("spi_cs_activate: slave 0x%08x\n", (unsigned)slave); +#endif +} + +__attribute__((weak)) +void spi_cs_deactivate(struct spi_slave *slave) +{ +#ifdef DEBUG + printf("spi_cs_deactivate: slave 0x%08x\n", (unsigned)slave); +#endif +} + +void spi_init() +{ +#ifdef DEBUG + printf("spi_init:\n"); +#endif +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct pele_spi_slave *pspi; + +#ifdef DEBUG + printf("spi_setup_slave: bus: %d cs: %d max_hz: %d mode: %d\n", + bus, cs, max_hz, mode); +#endif + + xqspipss_init_hw((void*)XPSS_QSPI_BASEADDR); + + pspi = malloc(sizeof(struct pele_spi_slave)); + if (!pspi) { + return NULL; + } + pspi->slave.bus = bus; + pspi->slave.cs = cs; + pspi->qspi.master.input_clk_hz = 100000000; + pspi->qspi.master.speed_hz = pspi->qspi.master.input_clk_hz / 2; + pspi->qspi.max_speed_hz = pspi->qspi.master.speed_hz; + pspi->qspi.master.dev_busy = 0; + pspi->qspi.master.regs = (void*)XPSS_QSPI_BASEADDR; + pspi->qspi.mode = mode; + pspi->qspi.chip_select = 0; + pspi->qspi.bits_per_word = 32; + xqspipss_setup_transfer(&pspi->qspi, NULL); + + return &pspi->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct pele_spi_slave *pspi; + +#ifdef DEBUG + printf("spi_free_slave: slave: 0x%08x\n", (u32)slave); +#endif + + pspi = to_pele_spi_slave(slave); + free(pspi); +} + +static void spi_portmux(struct spi_slave *slave) +{ +#ifdef DEBUG + printf("spi_portmux: slave: 0x%08x\n", (u32)slave); +#endif +} + +int spi_claim_bus(struct spi_slave *slave) +{ +#ifdef DEBUG + printf("spi_claim_bus: slave: 0x%08x\n", (u32)slave); +#endif + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +#ifdef DEBUG + printf("spi_release_bus: slave: 0x%08x\n", (u32)slave); +#endif +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct pele_spi_slave *pspi; + struct spi_transfer transfer; + +#ifdef DEBUG + printf("spi_xfer: slave: 0x%08x bitlen: %d dout: 0x%08x din: 0x%08x flags: 0x%lx\n", + (u32)slave, bitlen, (u32)dout, (u32)din, flags); +#endif + + pspi = (struct pele_spi_slave *)slave; + transfer.tx_buf = dout; + transfer.rx_buf = din; + transfer.len = bitlen / 8; + + /* Festering sore. + * Assume that the beginning of a transfer with bits to + * transmit must contain a defice command. + */ + if (dout && flags & SPI_XFER_BEGIN) { + pspi->qspi.master.is_inst = 1; + } else { + pspi->qspi.master.is_inst = 0; + } + + if (flags & SPI_XFER_END) { + transfer.cs_change = 1; + } else { + transfer.cs_change = 0; + } + + transfer.delay_usecs = 0; + transfer.bits_per_word = 32; + transfer.speed_hz = pspi->qspi.max_speed_hz; + + xqspipss_transfer(&pspi->qspi, &transfer); + + return 0; +} + diff --git a/board/xilinx/dfe/xilinx_qspipss.c b/board/xilinx/dfe/xilinx_qspipss.c new file mode 100755 index 00000000000..fc897380181 --- /dev/null +++ b/board/xilinx/dfe/xilinx_qspipss.c @@ -0,0 +1,1347 @@ +/* + * + * Xilinx PSS Quad-SPI (QSPI) controller driver (master mode only) + * + * (c) 2011 Xilinx, Inc. + * + * based on Xilinx PSS SPI Driver (xspipss.c) + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifdef LINUX_ONLY_NOT_UBOOT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else + +#include "xbasic_types.h" +#include +#include + +#include +#include +#include + +#include "xilinx_qspipss.h" + +#define DEBUG +#define DEBUG_REG +#undef DEBUG +#undef DEBUG_REG + +#endif + +/****** stubs to make this Linux driver build in this environment **/ + +#define spin_lock_irqsave(__X__, flags) \ + flags = 0; +#define spin_unlock_irqrestore(__X__, __Y__) +#define MODULE_ALIAS(__X__) + +#define dev_dbg(dev, format, arg...) \ + printf(format , ## arg) + +#define dev_err(dev, format, arg...) \ + printf(format , ## arg) + +#define wait_for_completion(__X__) \ + { \ + u32 data; \ + \ + do { \ + data = xqspipss_irq_poll(xqspi);\ + } while (data == 0); \ + } + +#define __devinit +#define __devinitdata +#define __devexit +#define __devexitdata +#define __force + +extern void XIo_Out32(u32 OutAddress, u32 Value); +extern u32 XIo_In32(u32 InAddress); + +#define spi_master_get_devdata(__X__) (&(__X__)) +#define INIT_COMPLETION(__X__) + +/** + * enum irqreturn + * @IRQ_NONE interrupt was not from this device + * @IRQ_HANDLED interrupt was handled by this device + * @IRQ_WAKE_THREAD handler requests to wake the handler thread + */ +enum irqreturn { + IRQ_NONE, + IRQ_HANDLED, + IRQ_WAKE_THREAD, +}; +typedef enum irqreturn irqreturn_t; + +/*******************************************************************/ + +#define HACK_WRITE_NO_DELAY + +/* + * Name of this driver + */ +#define DRIVER_NAME "Xilinx_PSS_QSPI" + +/* + * Register offset definitions + */ +#define XQSPIPSS_CONFIG_OFFSET 0x00 /* Configuration Register, RW */ +#define XQSPIPSS_STATUS_OFFSET 0x04 /* Interrupt Status Register, RO */ +#define XQSPIPSS_IEN_OFFSET 0x08 /* Interrupt Enable Register, WO */ +#define XQSPIPSS_IDIS_OFFSET 0x0C /* Interrupt Disable Reg, WO */ +#define XQSPIPSS_IMASK_OFFSET 0x10 /* Interrupt Enabled Mask Reg,RO */ +#define XQSPIPSS_ENABLE_OFFSET 0x14 /* Enable/Disable Register, RW */ +#define XQSPIPSS_DELAY_OFFSET 0x18 /* Delay Register, RW */ +#define XQSPIPSS_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst, WO */ +#define XQSPIPSS_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst, WO */ +#define XQSPIPSS_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst, WO */ +#define XQSPIPSS_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst, WO */ +#define XQSPIPSS_RXD_OFFSET 0x20 /* Data Receive Register, RO */ +#define XQSPIPSS_SIC_OFFSET 0x24 /* Slave Idle Count Register, RW */ +#define XQSPIPSS_TX_THRESH_OFFSET 0x28 /* TX FIFO Watermark Reg, RW */ +#define XQSPIPSS_RX_THRESH_OFFSET 0x2C /* RX FIFO Watermark Reg, RW */ +#define XQSPIPSS_GPIO_OFFSET 0x30 /* GPIO Register, RW */ +#define XQSPIPSS_LINEAR_CFG_OFFSET 0xA0 /* Linear Adapter Config Ref, RW */ +#define XQSPIPSS_MOD_ID_OFFSET 0xFC /* Module ID Register, RO */ + +/* + * QSPI Configuration Register bit Masks + * + * This register contains various control bits that effect the operation + * of the QSPI controller + */ +#define XQSPIPSS_CONFIG_MANSRT_MASK 0x00010000 /* Manual TX Start */ +#define XQSPIPSS_CONFIG_CPHA_MASK 0x00000004 /* Clock Phase Control */ +#define XQSPIPSS_CONFIG_CPOL_MASK 0x00000002 /* Clock Polarity Control */ +#define XQSPIPSS_CONFIG_SSCTRL_MASK 0x00003C00 /* Slave Select Mask */ + +/* + * QSPI Interrupt Registers bit Masks + * + * All the four interrupt registers (Status/Mask/Enable/Disable) have the same + * bit definitions. + */ +#define XQSPIPSS_IXR_MODF_MASK 0x00000002 /* QSPI Mode Fault */ +#define XQSPIPSS_IXR_TXNFULL_MASK 0x00000004 /* QSPI TX FIFO Overflow */ +#define XQSPIPSS_IXR_TXFULL_MASK 0x00000008 /* QSPI TX FIFO is full */ +#define XQSPIPSS_IXR_RXNEMTY_MASK 0x00000010 /* QSPI RX FIFO Not Empty */ +#define XQSPIPSS_IXR_ALL_MASK (XQSPIPSS_IXR_TXNFULL_MASK | \ + XQSPIPSS_IXR_RXNEMTY_MASK | \ + XQSPIPSS_IXR_MODF_MASK) + +/* + * QSPI Enable Register bit Masks + * + * This register is used to enable or disable the QSPI controller + */ +#define XQSPIPSS_ENABLE_ENABLE_MASK 0x00000001 /* QSPI Enable Bit Mask */ + +/* + * The modebits configurable by the driver to make the SPI support different + * data formats + */ +#define MODEBITS (SPI_CPOL | SPI_CPHA) + +/* + * Definitions for the status of queue + */ +#define XQSPIPSS_QUEUE_STOPPED 0 +#define XQSPIPSS_QUEUE_RUNNING 1 + +/* + * Definitions of the flash commands + */ +/* Flash opcodes in ascending order */ +#define XQSPIPSS_FLASH_OPCODE_WRSR 0x01 /* Write status register */ +#define XQSPIPSS_FLASH_OPCODE_PP 0x02 /* Page program */ +#define XQSPIPSS_FLASH_OPCODE_NORM_READ 0x03 /* Normal read data bytes */ +#define XQSPIPSS_FLASH_OPCODE_WRDS 0x04 /* Write disable */ +#define XQSPIPSS_FLASH_OPCODE_RDSR1 0x05 /* Read status register 1 */ +#define XQSPIPSS_FLASH_OPCODE_WREN 0x06 /* Write enable */ +#define XQSPIPSS_FLASH_OPCODE_FAST_READ 0x0B /* Fast read data bytes */ +#define XQSPIPSS_FLASH_OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define XQSPIPSS_FLASH_OPCODE_RDSR2 0x35 /* Read status register 2 */ +#define XQSPIPSS_FLASH_OPCODE_DUAL_READ 0x3B /* Dual read data bytes */ +#define XQSPIPSS_FLASH_OPCODE_BE_32K 0x52 /* Erase 32KiB block */ +#define XQSPIPSS_FLASH_OPCODE_QUAD_READ 0x6B /* Quad read data bytes */ +#define XQSPIPSS_FLASH_OPCODE_ERASE_SUS 0x75 /* Erase suspend */ +#define XQSPIPSS_FLASH_OPCODE_ERASE_RES 0x7A /* Erase resume */ +#define XQSPIPSS_FLASH_OPCODE_RDID 0x9F /* Read JEDEC ID */ +#define XQSPIPSS_FLASH_OPCODE_BE 0xC7 /* Erase whole flash block */ +#define XQSPIPSS_FLASH_OPCODE_SE 0xD8 /* Sector erase (usually 64KB)*/ + +/* + * Macros for the QSPI controller read/write + */ +#ifdef LINUX_ONLY_NOT_UBOOT +#define xqspipss_read(addr) __raw_readl(addr) +#define xqspipss_write(addr, val) __raw_writel((val), (addr)) +#else +static inline +u32 xqspipss_read(u32 addr) +{ + u32 val; + + val = XIo_In32(addr); +#ifdef DEBUG_REG + printf("xqspipss_read: addr: 0x%08x = 0x%08x\n", + addr, val); +#endif + return val; +} +static inline +void xqspipss_write(u32 addr, u32 val) +{ +#ifdef DEBUG_REG + printf("xqspipss_write: addr: 0x%08x = 0x%08x\n", + addr, val); +#endif + XIo_Out32(addr, val); +} +#endif + +#ifdef LINUX_ONLY_NOT_UBOOT + +/** + * struct xqspipss - Defines qspi driver instance + * @workqueue: Queue of all the transfers + * @work: Information about current transfer + * @queue: Head of the queue + * @queue_state: Queue status + * @regs: Virtual address of the QSPI controller registers + * @input_clk_hz: Input clock frequency of the QSPI controller in Hz + * @irq: IRQ number + * @speed_hz: Current QSPI bus clock speed in Hz + * @trans_queue_lock: Lock used for accessing transfer queue + * @config_reg_lock: Lock used for accessing configuration register + * @txbuf: Pointer to the TX buffer + * @rxbuf: Pointer to the RX buffer + * @bytes_to_transfer: Number of bytes left to transfer + * @bytes_to_receive: Number of bytes left to receive + * @dev_busy: Device busy flag + * @done: Transfer complete status + * @curr_inst: Current executing instruction format + * @inst_response: Responce to the instruction or data + * @is_inst: Flag to indicate the first message in a Transfer request + **/ +struct xqspipss { + struct workqueue_struct *workqueue; + struct work_struct work; + struct list_head queue; + + u8 queue_state; + void __iomem *regs; + u32 input_clk_hz; + u32 irq; + u32 speed_hz; + spinlock_t trans_queue_lock; + spinlock_t config_reg_lock; + const void *txbuf; + void *rxbuf; + int bytes_to_transfer; + int bytes_to_receive; + u8 dev_busy; + struct completion done; + struct xqspipss_inst_format *curr_inst; + u8 inst_response; + bool is_inst; +}; + +#endif + +/** + * struct xqspipss_inst_format - Defines qspi flash instruction format + * @opcode: Operational code of instruction + * @inst_size: Size of the instruction including address bytes + * @offset: Register address where instruction has to be written + **/ +struct xqspipss_inst_format { + u8 opcode; + u8 inst_size; + u8 offset; +}; + +/* + * List of all the QSPI instructions and its format + */ +static struct xqspipss_inst_format __devinitdata flash_inst[] = { + { XQSPIPSS_FLASH_OPCODE_WREN, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_WRDS, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_RDSR1, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_RDSR2, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_WRSR, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_PP, 4, XQSPIPSS_TXD_00_00_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_SE, 4, XQSPIPSS_TXD_00_00_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_BE_32K, 4, XQSPIPSS_TXD_00_00_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_BE_4K, 4, XQSPIPSS_TXD_00_00_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_BE, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_ERASE_SUS, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_ERASE_RES, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_RDID, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_NORM_READ, 4, XQSPIPSS_TXD_00_00_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_FAST_READ, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_DUAL_READ, 1, XQSPIPSS_TXD_00_01_OFFSET }, + { XQSPIPSS_FLASH_OPCODE_QUAD_READ, 1, XQSPIPSS_TXD_00_01_OFFSET }, + /* Add all the instructions supported by the flash device */ +}; + +/** + * xqspipss_init_hw - Initialize the hardware + * @regs_base: Base address of QSPI controller + * + * The default settings of the QSPI controller's configurable parameters on + * reset are + * - Master mode + * - Baud rate divisor is set to 2 + * - Threshold value for TX FIFO not full interrupt is set to 1 + * - Flash memory interface mode enabled + * - Size of the word to be transferred as 8 bit + * This function performs the following actions + * - Disable and clear all the interrupts + * - Enable manual slave select + * - Enable manual start + * - Deselect all the chip select lines + * - Set the size of the word to be transferred as 32 bit + * - Set the little endian mode of TX FIFO and + * - Enable the QSPI controller + **/ +void xqspipss_init_hw(void __iomem *regs_base) +{ + u32 config_reg; + + xqspipss_write(regs_base + XQSPIPSS_ENABLE_OFFSET, + ~XQSPIPSS_ENABLE_ENABLE_MASK); + xqspipss_write(regs_base + XQSPIPSS_IDIS_OFFSET, 0x7F); + + /* Disable linear mode as the boot loader may have used it */ + xqspipss_write(regs_base + XQSPIPSS_LINEAR_CFG_OFFSET, 0); + + /* Clear the RX FIFO */ + while (xqspipss_read(regs_base + XQSPIPSS_STATUS_OFFSET) & + XQSPIPSS_IXR_RXNEMTY_MASK) + xqspipss_read(regs_base + XQSPIPSS_RXD_OFFSET); + + xqspipss_write(regs_base + XQSPIPSS_STATUS_OFFSET , 0x7F); + config_reg = xqspipss_read(regs_base + XQSPIPSS_CONFIG_OFFSET); + config_reg &= 0xFBFFFFFF; /* Set little endian mode of TX FIFO */ + config_reg |= 0x8000FCC1; + xqspipss_write(regs_base + XQSPIPSS_CONFIG_OFFSET, config_reg); + xqspipss_write(regs_base + XQSPIPSS_ENABLE_OFFSET, + XQSPIPSS_ENABLE_ENABLE_MASK); +} + +/** + * xqspipss_copy_read_data - Copy data to RX buffer + * @xqspi: Pointer to the xqspipss structure + * @data: The 32 bit variable where data is stored + * @size: Number of bytes to be copied from data to RX buffer + **/ +static void xqspipss_copy_read_data(struct xqspipss *xqspi, u32 data, u8 size) +{ + u8 byte3; + +#ifdef DEBUG + printf("%s data 0x%04x rxbuf addr: 0x%08x size %d\n", + __FUNCTION__, data, (unsigned)(xqspi->rxbuf), size); +#endif + + if (xqspi->rxbuf) { + switch (size) { + case 1: + *((u8 *)xqspi->rxbuf) = data; + xqspi->rxbuf += 1; + break; + case 2: + *((u16 *)xqspi->rxbuf) = data; + xqspi->rxbuf += 2; + break; + case 3: + *((u16 *)xqspi->rxbuf) = data; + xqspi->rxbuf += 2; + byte3 = (u8)(data >> 16); + *((u8 *)xqspi->rxbuf) = byte3; + xqspi->rxbuf += 1; + break; + case 4: +#ifdef LINUX_ONLY_NOT_UBOOT + (*(u32 *)xqspi->rxbuf) = data; +#else + /* Can not assume word aligned buffer */ + memcpy(xqspi->rxbuf, &data, size); +#endif + xqspi->rxbuf += 4; + break; + default: + /* This will never execute */ + break; + } + } + xqspi->bytes_to_receive -= size; + if (xqspi->bytes_to_receive < 0) { + xqspi->bytes_to_receive = 0; + } +} + +/** + * xqspipss_copy_write_data - Copy data from TX buffer + * @xqspi: Pointer to the xqspipss 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 xqspipss_copy_write_data(struct xqspipss *xqspi, u32 *data, u8 size) +{ + + if (xqspi->txbuf) { + switch (size) { + case 1: + *data = *((u8 *)xqspi->txbuf); + xqspi->txbuf += 1; + *data |= 0xFFFFFF00; + break; + case 2: + *data = *((u16 *)xqspi->txbuf); + xqspi->txbuf += 2; + *data |= 0xFFFF0000; + break; + case 3: + *data = *((u16 *)xqspi->txbuf); + xqspi->txbuf += 2; + *data |= (*((u8 *)xqspi->txbuf) << 16); + xqspi->txbuf += 1; + *data |= 0xFF000000; + break; + case 4: +#ifdef LINUX_ONLY_NOT_UBOOT + *data = *((u32 *)xqspi->txbuf); +#else + /* Can not assume word aligned buffer */ + memcpy(data, xqspi->txbuf, size); +#endif + xqspi->txbuf += 4; + break; + default: + /* This will never execute */ + break; + } + } else + *data = 0; + +#ifdef DEBUG + printf("%s data 0x%08x txbuf addr: 0x%08x size %d\n", + __FUNCTION__, *data, (u32)xqspi->txbuf, size); +#endif + + xqspi->bytes_to_transfer -= size; + if (xqspi->bytes_to_transfer < 0) { + xqspi->bytes_to_transfer = 0; + } +} + +/** + * xqspipss_chipselect - Select or deselect the chip select line + * @qspi: Pointer to the spi_device structure + * @is_on: Select(1) or deselect (0) the chip select line + **/ +static void xqspipss_chipselect(struct spi_device *qspi, int is_on) +{ + struct xqspipss *xqspi = spi_master_get_devdata(qspi->master); + u32 config_reg; + unsigned long flags; + +#ifdef DEBUG + printf("xqspipss_chipselect: is_on: %d\n", is_on); +#endif + + spin_lock_irqsave(&xqspi->config_reg_lock, flags); + + config_reg = xqspipss_read(xqspi->regs + XQSPIPSS_CONFIG_OFFSET); + + if (is_on) { + /* Select the slave */ + config_reg &= ~XQSPIPSS_CONFIG_SSCTRL_MASK; + config_reg |= (((~(0x0001 << qspi->chip_select)) << 10) & + XQSPIPSS_CONFIG_SSCTRL_MASK); + } else + /* Deselect the slave */ + config_reg |= XQSPIPSS_CONFIG_SSCTRL_MASK; + + xqspipss_write(xqspi->regs + XQSPIPSS_CONFIG_OFFSET, config_reg); + + spin_unlock_irqrestore(&xqspi->config_reg_lock, flags); +} + +/** + * xqspipss_setup_transfer - Configure QSPI controller for specified transfer + * @qspi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provides information + * about next transfer setup parameters + * + * Sets the operational mode of QSPI controller for the next QSPI transfer and + * sets the requested clock frequency. + * + * returns: 0 on success and -EINVAL on invalid input parameter + * + * Note: If the requested frequency is not an exact match with what can be + * obtained using the prescalar value, the driver sets the clock frequency which + * is lower than the requested frequency (maximum lower) for the transfer. If + * the requested frequency is higher or lower than that is supported by the QSPI + * controller the driver will set the highest or lowest frequency supported by + * controller. + **/ +int xqspipss_setup_transfer(struct spi_device *qspi, + struct spi_transfer *transfer) +{ + struct xqspipss *xqspi = spi_master_get_devdata(qspi->master); + u8 bits_per_word; + u32 config_reg; + u32 req_hz; + u32 baud_rate_val = 0; + unsigned long flags; + +#ifdef DEBUG + printf("xqspipss_setup_transfer: qspi: 0x%08x transfer: 0x%08x\n", + (u32)qspi, (u32)transfer); +#endif + bits_per_word = (transfer) ? + transfer->bits_per_word : qspi->bits_per_word; + req_hz = (transfer) ? transfer->speed_hz : qspi->max_speed_hz; + + if (qspi->mode & ~MODEBITS) { + dev_err(&qspi->dev, "%s, unsupported mode bits %x\n", + __func__, qspi->mode & ~MODEBITS); + return -EINVAL; + } + + if (bits_per_word != 32) { + bits_per_word = 32; + } + + spin_lock_irqsave(&xqspi->config_reg_lock, flags); + + config_reg = xqspipss_read(xqspi->regs + XQSPIPSS_CONFIG_OFFSET); + + /* Set the QSPI clock phase and clock polarity */ + config_reg &= (~XQSPIPSS_CONFIG_CPHA_MASK) & + (~XQSPIPSS_CONFIG_CPOL_MASK); + if (qspi->mode & SPI_CPHA) + config_reg |= XQSPIPSS_CONFIG_CPHA_MASK; + if (qspi->mode & SPI_CPOL) + config_reg |= XQSPIPSS_CONFIG_CPOL_MASK; + + /* Set the clock frequency */ + if (xqspi->speed_hz != req_hz) { + baud_rate_val = 0; + while ((baud_rate_val < 8) && + (xqspi->input_clk_hz / (2 << baud_rate_val)) > req_hz) { + baud_rate_val++; + } + config_reg &= 0xFFFFFFC7; + config_reg |= (baud_rate_val << 3); + xqspi->speed_hz = req_hz; + } + + xqspipss_write(xqspi->regs + XQSPIPSS_CONFIG_OFFSET, config_reg); + + spin_unlock_irqrestore(&xqspi->config_reg_lock, flags); + +#ifdef DEBUG + dev_dbg(&qspi->dev, "%s, mode %d, %u bits/w, %u clock speed\n", + __func__, qspi->mode & MODEBITS, qspi->bits_per_word, + xqspi->speed_hz); +#endif + + return 0; +} + +/** + * xqspipss_setup - Configure the QSPI controller + * @qspi: Pointer to the spi_device structure + * + * Sets the operational mode of QSPI controller for the next QSPI transfer, baud + * rate and divisor value to setup the requested qspi clock. + * + * returns: 0 on success and error value on failure + **/ +static int xqspipss_setup(struct spi_device *qspi) +{ + + if (qspi->mode & SPI_LSB_FIRST) + return -EINVAL; + + if (!qspi->max_speed_hz) + return -EINVAL; + + if (!qspi->bits_per_word) + qspi->bits_per_word = 32; + + return xqspipss_setup_transfer(qspi, NULL); +} + +/** + * xqspipss_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible + * @xqspi: Pointer to the xqspipss structure + **/ +static void xqspipss_fill_tx_fifo(struct xqspipss *xqspi) +{ + u32 data = 0; + + while ((!(xqspipss_read(xqspi->regs + XQSPIPSS_STATUS_OFFSET) & + XQSPIPSS_IXR_TXFULL_MASK)) && (xqspi->bytes_to_transfer > 0)) { + if (xqspi->bytes_to_transfer < 4) { + xqspipss_copy_write_data(xqspi, &data, + xqspi->bytes_to_transfer); + } else { + xqspipss_copy_write_data(xqspi, &data, 4); + } + + xqspipss_write(xqspi->regs + XQSPIPSS_TXD_00_00_OFFSET, data); + } +} + +/** + * xqspipss_irq - Interrupt service routine of the QSPI controller + * @irq: IRQ number + * @dev_id: Pointer to the xqspi 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: IRQ_HANDLED always + **/ +#ifdef LINUX_ONLY_NOT_UBOOT +static irqreturn_t xqspipss_irq(int irq, void *dev_id) +{ + struct xqspipss *xqspi = dev_id; +#else +static int xqspipss_irq_poll(struct xqspipss *xqspi) +{ + int max_loop; +#endif + u32 intr_status; + +#ifdef DEBUG + printf("xqspipss_irq_poll: xqspi: 0x%08x\n", + (u32)xqspi); +#endif + +#ifdef LINUX_ONLY_NOT_UBOOT + intr_status = xqspipss_read(xqspi->regs + XQSPIPSS_STATUS_OFFSET); +#else + /* u-boot: Poll until any of the interrupt status bits are set */ + max_loop = 0; + do { + intr_status = xqspipss_read(xqspi->regs + XQSPIPSS_STATUS_OFFSET); + max_loop ++; + } while ((intr_status == 0) && (max_loop < 100000)); + if (intr_status == 0) { + printf("xqspipss_irq_poll: timeout\n"); + return 0; + } +#endif + + xqspipss_write(xqspi->regs + XQSPIPSS_STATUS_OFFSET , intr_status); +#ifndef LINUX_ONLY_NOT_UBOOT + /* u-boot: Disable all interrupts */ + xqspipss_write(xqspi->regs + XQSPIPSS_IDIS_OFFSET, + XQSPIPSS_IXR_ALL_MASK); +#endif + + if (intr_status & XQSPIPSS_IXR_MODF_MASK) { + /* Indicate that transfer is completed, the SPI subsystem will + * identify the error as the remaining bytes to be + * transferred is non-zero */ +#ifdef LINUX_ONLY_NOT_UBOOT + complete(&xqspi->done); +#else + /* u-boot: return "operation complete" */ + xqspipss_write(xqspi->regs + XQSPIPSS_IDIS_OFFSET, + XQSPIPSS_IXR_ALL_MASK); + return 1; +#endif + } else if ((intr_status & XQSPIPSS_IXR_TXNFULL_MASK) || + (intr_status & XQSPIPSS_IXR_RXNEMTY_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 */ + u32 config_reg; + + /* Read out the data from the RX FIFO */ + while (xqspipss_read(xqspi->regs + XQSPIPSS_STATUS_OFFSET) & + XQSPIPSS_IXR_RXNEMTY_MASK) { + u32 data; + + data = xqspipss_read(xqspi->regs + XQSPIPSS_RXD_OFFSET); + + if ((xqspi->inst_response) && + (!((xqspi->curr_inst->opcode == + XQSPIPSS_FLASH_OPCODE_RDSR1) || + (xqspi->curr_inst->opcode == + XQSPIPSS_FLASH_OPCODE_RDSR2)))) { + xqspi->inst_response = 0; + xqspipss_copy_read_data(xqspi, data, + xqspi->curr_inst->inst_size); + } else if (xqspi->bytes_to_receive < 4) + xqspipss_copy_read_data(xqspi, data, + xqspi->bytes_to_receive); + else + xqspipss_copy_read_data(xqspi, data, 4); + } + + if (xqspi->bytes_to_transfer) { + /* There is more data to send */ + xqspipss_fill_tx_fifo(xqspi); + + xqspipss_write(xqspi->regs + XQSPIPSS_IEN_OFFSET, + XQSPIPSS_IXR_ALL_MASK); + + spin_lock(&xqspi->config_reg_lock); + config_reg = xqspipss_read(xqspi->regs + + XQSPIPSS_CONFIG_OFFSET); + + config_reg |= XQSPIPSS_CONFIG_MANSRT_MASK; + xqspipss_write(xqspi->regs + XQSPIPSS_CONFIG_OFFSET, + config_reg); + spin_unlock(&xqspi->config_reg_lock); + } else { + /* If transfer and receive is completed then only send + * complete signal */ + if (!xqspi->bytes_to_receive) { +#ifdef LINUX_ONLY_NOT_UBOOT + complete(&xqspi->done); +#else + /* u-boot: return "operation complete" */ + xqspipss_write(xqspi->regs + XQSPIPSS_IDIS_OFFSET, + XQSPIPSS_IXR_ALL_MASK); + return 1; +#endif + } + } + } + +#ifdef LINUX_ONLY_NOT_UBOOT + return IRQ_HANDLED; +#else + /* u-boot: Transfer not complete */ + return 0; +#endif +} + +/** + * xqspipss_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 xqspipss_start_transfer(struct spi_device *qspi, + struct spi_transfer *transfer) +{ + struct xqspipss *xqspi = spi_master_get_devdata(qspi->master); + u32 config_reg; + unsigned long flags; + u32 data = 0; + u8 instruction = 0; + u8 index; +#ifdef HACK_WRITE_NO_DELAY + static bool no_delay = 0; +#endif + +#ifdef DEBUG + printf("xqspipss_start_transfer: qspi: 0x%08x transfer: 0x%08x len: %d\n", + (u32)qspi, (u32)transfer, transfer->len); +#endif + + xqspi->txbuf = transfer->tx_buf; + xqspi->rxbuf = transfer->rx_buf; + xqspi->bytes_to_transfer = transfer->len; + xqspi->bytes_to_receive = transfer->len; + +#ifdef HACK_WRITE_NO_DELAY + if (no_delay) { + /* Indicates Page programm command + address is already in Tx + * FIFO. We need to receive extra 4 bytes for command + address + */ + xqspi->bytes_to_receive += 4; + no_delay = 0; + } +#endif + + if (xqspi->txbuf) + instruction = *(u8 *)xqspi->txbuf; + + if (instruction && xqspi->is_inst) { + for (index = 0 ; index < ARRAY_SIZE(flash_inst); index++) + if (instruction == flash_inst[index].opcode) + break; + + /* Instruction might have already been transmitted. This is a + * 'data only' transfer */ + if (index == ARRAY_SIZE(flash_inst)) + goto xfer_data; + + xqspi->curr_inst = &flash_inst[index]; + xqspi->inst_response = 1; + + /* Get the instruction */ + data = 0; + xqspipss_copy_write_data(xqspi, &data, + xqspi->curr_inst->inst_size); + + /* Write the instruction to LSB of the FIFO. The core is + * designed such that it is not necessary to check whether the + * write FIFO is full before writing. However, write would be + * delayed if the user tries to write when write FIFO is full + */ + xqspipss_write(xqspi->regs + xqspi->curr_inst->offset, data); + +#ifdef HACK_WRITE_NO_DELAY + if (xqspi->curr_inst->opcode == XQSPIPSS_FLASH_OPCODE_PP) { + /* Write instruction + address to the Tx FIFO, but do + * not start transmission yet. Wait for the next + * spi_message with data, and start transmission after + * data is filled into the FIFO + */ + no_delay = 1; + return (transfer->len); + } +#endif + + /* Read status register and Read ID instructions don't require + * to ignore the extra bytes in response of instruction as + * response contains the value */ + if ((instruction == XQSPIPSS_FLASH_OPCODE_RDSR1) || + (instruction == XQSPIPSS_FLASH_OPCODE_RDSR2) || + (instruction == XQSPIPSS_FLASH_OPCODE_RDID)) { + if (xqspi->bytes_to_transfer < 4) + xqspi->bytes_to_transfer = 0; + else + xqspi->bytes_to_transfer -= 3; + } + } + +xfer_data: + INIT_COMPLETION(xqspi->done); + + /* In case of Fast, Dual and Quad reads, transmit the instruction first. + * Address and dummy byte should be transmitted after instruction + * is transmitted */ + if (((xqspi->is_inst == 0) && (xqspi->bytes_to_transfer)) || + ((xqspi->bytes_to_transfer) && + (instruction != XQSPIPSS_FLASH_OPCODE_FAST_READ) && + (instruction != XQSPIPSS_FLASH_OPCODE_DUAL_READ) && + (instruction != XQSPIPSS_FLASH_OPCODE_QUAD_READ))) + xqspipss_fill_tx_fifo(xqspi); + xqspipss_write(xqspi->regs + XQSPIPSS_IEN_OFFSET, + XQSPIPSS_IXR_ALL_MASK); + /* Start the transfer by enabling manual start bit */ + spin_lock_irqsave(&xqspi->config_reg_lock, flags); + config_reg = xqspipss_read(xqspi->regs + + XQSPIPSS_CONFIG_OFFSET) | XQSPIPSS_CONFIG_MANSRT_MASK; + xqspipss_write(xqspi->regs + XQSPIPSS_CONFIG_OFFSET, config_reg); + spin_unlock_irqrestore(&xqspi->config_reg_lock, flags); + + wait_for_completion(&xqspi->done); + + return (transfer->len) - (xqspi->bytes_to_transfer); +} + +#ifdef LINUX_ONLY_NOT_UBOOT +/** + * xqspipss_work_queue - Get the request from queue to perform transfers + * @work: Pointer to the work_struct structure + **/ +static void xqspipss_work_queue(struct work_struct *work) +#else +int +xqspipss_transfer(struct spi_device *qspi, struct spi_transfer *transfer) +#endif +{ +#ifdef LINUX_ONLY_NOT_UBOOT + struct xqspipss *xqspi = container_of(work, struct xqspipss, work); +#else + struct xqspipss *xqspi = spi_master_get_devdata(qspi->master); +#endif + unsigned long flags; + + spin_lock_irqsave(&xqspi->trans_queue_lock, flags); + xqspi->dev_busy = 1; + +#ifdef DEBUG + printf("xqspipss_transfer: \n"); +#endif + +#ifdef LINUX_ONLY_NOT_UBOOT + /* Check if list is empty or queue is stoped */ + if (list_empty(&xqspi->queue) || + xqspi->queue_state == XQSPIPSS_QUEUE_STOPPED) { + xqspi->dev_busy = 0; + spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); + return; + } +#endif + +#ifdef LINUX_ONLY_NOT_UBOOT + /* Keep requesting transfer till list is empty */ + while (!list_empty(&xqspi->queue)) { + struct spi_message *msg; + struct spi_device *qspi; + struct spi_transfer *transfer = NULL; + unsigned cs_change = 1; + int status = 0; + + msg = container_of(xqspi->queue.next, struct spi_message, + queue); + list_del_init(&msg->queue); + spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); + qspi = msg->spi; + xqspi->is_inst = 1; + + list_for_each_entry(transfer, &msg->transfers, transfer_list) { +#else + { + unsigned cs_change = 1; + int status = 0; + + while (1) { +#endif + if (transfer->bits_per_word || transfer->speed_hz) { + status = + xqspipss_setup_transfer(qspi, transfer); + if (status < 0) + break; + } + + /* Select the chip if required */ + if (cs_change) + xqspipss_chipselect(qspi, 1); + + cs_change = transfer->cs_change; + + if (!transfer->tx_buf && !transfer->rx_buf && + transfer->len) { + status = -EINVAL; + break; + } + + /* Request the transfer */ + if (transfer->len) { + status = + xqspipss_start_transfer(qspi, transfer); + xqspi->is_inst = 0; + } + + if (status != transfer->len) { + if (status > 0) + status = -EMSGSIZE; + break; + } +#ifdef LINUX_ONLY_NOT_UBOOT + msg->actual_length += status; +#endif + status = 0; + + if (transfer->delay_usecs) + udelay(transfer->delay_usecs); + + if (cs_change) + /* Deselect the chip */ + xqspipss_chipselect(qspi, 0); + +#ifdef LINUX_ONLY_NOT_UBOOT + if (transfer->transfer_list.next == &msg->transfers) + break; + } + + msg->status = status; + msg->complete(msg->context); +#else + break; + } +#endif + + xqspipss_setup_transfer(qspi, NULL); + +#ifdef LINUX_ONLY_NOT_UBOOT + if (!(status == 0 && cs_change)) + xqspipss_chipselect(qspi, 0); +#endif + + spin_lock_irqsave(&xqspi->trans_queue_lock, flags); + } + + xqspi->dev_busy = 0; + spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); + +#ifndef LINUX_ONLY_NOT_UBOOT + return 0; +#endif +} + +#ifdef LINUX_ONLY_NOT_UBOOT +/** + * xqspipss_transfer - Add a new transfer request at the tail of work queue + * @qspi: Pointer to the spi_device structure + * @message: Pointer to the spi_transfer structure which provides information + * about next transfer parameters + * + * returns: 0 on success, -EINVAL on invalid input parameter and + * -ESHUTDOWN if queue is stopped by module unload function + **/ +static int +xqspipss_transfer(struct spi_device *qspi, struct spi_message *message) +{ + struct xqspipss *xqspi = spi_master_get_devdata(qspi->master); + struct spi_transfer *transfer; + unsigned long flags; + + if (xqspi->queue_state == XQSPIPSS_QUEUE_STOPPED) + return -ESHUTDOWN; + + message->actual_length = 0; + message->status = -EINPROGRESS; + + /* Check each transfer's parameters */ + list_for_each_entry(transfer, &message->transfers, transfer_list) { + u8 bits_per_word = + transfer->bits_per_word ? : qspi->bits_per_word; + + bits_per_word = bits_per_word ? : 32; + if (!transfer->tx_buf && !transfer->rx_buf && transfer->len) + return -EINVAL; + /* QSPI controller supports only 32 bit transfers whereas higher + * layer drivers request 8 bit transfers. Re-visit at a later + * time */ + /* if (bits_per_word != 32) + return -EINVAL; */ + } + + spin_lock_irqsave(&xqspi->trans_queue_lock, flags); + list_add_tail(&message->queue, &xqspi->queue); + if (!xqspi->dev_busy) + queue_work(xqspi->workqueue, &xqspi->work); + spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); + + return 0; +} + +/** + * xqspipss_start_queue - Starts the queue of the QSPI driver + * @xqspi: Pointer to the xqspipss structure + * + * returns: 0 on success and -EBUSY if queue is already running or device is + * busy + **/ +static inline int xqspipss_start_queue(struct xqspipss *xqspi) +{ + unsigned long flags; + + spin_lock_irqsave(&xqspi->trans_queue_lock, flags); + + if (xqspi->queue_state == XQSPIPSS_QUEUE_RUNNING || xqspi->dev_busy) { + spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); + return -EBUSY; + } + + xqspi->queue_state = XQSPIPSS_QUEUE_RUNNING; + spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); + + return 0; +} + +/** + * xqspipss_stop_queue - Stops the queue of the QSPI driver + * @xqspi: Pointer to the xqspipss structure + * + * This function waits till queue is empty and then stops the queue. + * Maximum time out is set to 5 seconds. + * + * returns: 0 on success and -EBUSY if queue is not empty or device is busy + **/ +static inline int xqspipss_stop_queue(struct xqspipss *xqspi) +{ + unsigned long flags; + unsigned limit = 500; + int ret = 0; + + if (xqspi->queue_state != XQSPIPSS_QUEUE_RUNNING) + return ret; + + spin_lock_irqsave(&xqspi->trans_queue_lock, flags); + + while ((!list_empty(&xqspi->queue) || xqspi->dev_busy) && limit--) { + spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); + msleep(10); + spin_lock_irqsave(&xqspi->trans_queue_lock, flags); + } + + if (!list_empty(&xqspi->queue) || xqspi->dev_busy) + ret = -EBUSY; + + if (ret == 0) + xqspi->queue_state = XQSPIPSS_QUEUE_STOPPED; + + spin_unlock_irqrestore(&xqspi->trans_queue_lock, flags); + + return ret; +} + +/** + * xqspipss_destroy_queue - Destroys the queue of the QSPI driver + * @xqspi: Pointer to the xqspipss structure + * + * returns: 0 on success and error value on failure + **/ +static inline int xqspipss_destroy_queue(struct xqspipss *xqspi) +{ + int ret; + + ret = xqspipss_stop_queue(xqspi); + if (ret != 0) + return ret; + + destroy_workqueue(xqspi->workqueue); + + return 0; +} + +/** + * xqspipss_probe - Probe method for the QSPI driver + * @dev: Pointer to the platform_device structure + * + * This function initializes the driver data structures and the hardware. + * + * returns: 0 on success and error value on failure + **/ +static int __devinit xqspipss_probe(struct platform_device *dev) +{ + int ret = 0; + struct spi_master *master; + struct xqspipss *xqspi; + struct resource *r; + struct xspi_platform_data *platform_info; + + master = spi_alloc_master(&dev->dev, sizeof(struct xqspipss)); + if (master == NULL) + return -ENOMEM; + + xqspi = spi_master_get_devdata(master); + platform_set_drvdata(dev, master); + + platform_info = dev->dev.platform_data; + if (platform_info == NULL) { + ret = -ENODEV; + dev_err(&dev->dev, "platform data not available\n"); + goto put_master; + } + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + dev_err(&dev->dev, "platform_get_resource failed\n"); + goto put_master; + } + + if (!request_mem_region(r->start, + r->end - r->start + 1, dev->name)) { + ret = -ENXIO; + dev_err(&dev->dev, "request_mem_region failed\n"); + goto put_master; + } + + xqspi->regs = ioremap(r->start, r->end - r->start + 1); + if (xqspi->regs == NULL) { + ret = -ENOMEM; + dev_err(&dev->dev, "ioremap failed\n"); + goto release_mem; + } + + xqspi->irq = platform_get_irq(dev, 0); + if (xqspi->irq < 0) { + ret = -ENXIO; + dev_err(&dev->dev, "irq resource not found\n"); + goto unmap_io; + } + + ret = request_irq(xqspi->irq, xqspipss_irq, 0, dev->name, xqspi); + if (ret != 0) { + ret = -ENXIO; + dev_err(&dev->dev, "request_irq failed\n"); + goto unmap_io; + } + + /* QSPI controller initializations */ + xqspipss_init_hw(xqspi->regs); + + init_completion(&xqspi->done); + master->bus_num = platform_info->bus_num; + master->num_chipselect = platform_info->num_chipselect; + master->setup = xqspipss_setup; + master->transfer = xqspipss_transfer; + xqspi->input_clk_hz = platform_info->speed_hz; + xqspi->speed_hz = platform_info->speed_hz / 2; + xqspi->dev_busy = 0; + + INIT_LIST_HEAD(&xqspi->queue); + spin_lock_init(&xqspi->trans_queue_lock); + spin_lock_init(&xqspi->config_reg_lock); + + xqspi->queue_state = XQSPIPSS_QUEUE_STOPPED; + xqspi->dev_busy = 0; + + INIT_WORK(&xqspi->work, xqspipss_work_queue); + xqspi->workqueue = + create_singlethread_workqueue(dev_name(&master->dev)); + if (!xqspi->workqueue) { + ret = -ENOMEM; + dev_err(&dev->dev, "problem initializing queue\n"); + goto free_irq; + } + + ret = xqspipss_start_queue(xqspi); + if (ret != 0) { + dev_err(&dev->dev, "problem starting queue\n"); + goto remove_queue; + } + + ret = spi_register_master(master); + if (ret) { + dev_err(&dev->dev, "spi_register_master failed\n"); + goto remove_queue; + } + + dev_info(&dev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", r->start, + (u32 __force)xqspi->regs, xqspi->irq); + + return ret; + +remove_queue: + (void)xqspipss_destroy_queue(xqspi); +free_irq: + free_irq(xqspi->irq, xqspi); +unmap_io: + iounmap(xqspi->regs); +release_mem: + release_mem_region(r->start, r->end - r->start + 1); +put_master: + platform_set_drvdata(dev, NULL); + spi_master_put(master); + return ret; +} + +/** + * xqspipss_remove - Remove method for the QSPI driver + * @dev: Pointer to the platform_device structure + * + * This function is called if a device is physically removed from the system or + * if the driver module is being unloaded. It frees all resources allocated to + * the device. + * + * returns: 0 on success and error value on failure + **/ +static int __devexit xqspipss_remove(struct platform_device *dev) +{ + struct spi_master *master = platform_get_drvdata(dev); + struct xqspipss *xqspi = spi_master_get_devdata(master); + struct resource *r; + int ret = 0; + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&dev->dev, "platform_get_resource failed\n"); + return -ENODEV; + } + + ret = xqspipss_destroy_queue(xqspi); + if (ret != 0) + return ret; + + xqspipss_write(xqspi->regs + XQSPIPSS_ENABLE_OFFSET, + ~XQSPIPSS_ENABLE_ENABLE_MASK); + + free_irq(xqspi->irq, xqspi); + iounmap(xqspi->regs); + release_mem_region(r->start, r->end - r->start + 1); + + spi_unregister_master(master); + spi_master_put(master); + + /* Prevent double remove */ + platform_set_drvdata(dev, NULL); + + dev_dbg(&dev->dev, "remove succeeded\n"); + return 0; +} + +/* Work with hotplug and coldplug */ +MODULE_ALIAS("platform:" DRIVER_NAME); + +/* + * xqspipss_driver - This structure defines the QSPI platform driver + */ +static struct platform_driver xqspipss_driver = { + .probe = xqspipss_probe, + .remove = __devexit_p(xqspipss_remove), + .suspend = NULL, + .resume = NULL, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +/** + * xqspipss_init - QSPI driver module initialization function + * + * returns: 0 on success and error value on failure + **/ +static int __init xqspipss_init(void) +{ + return platform_driver_register(&xqspipss_driver); +} + +module_init(xqspipss_init); + +/** + * xqspipss_exit - QSPI driver module exit function + **/ +static void __exit xqspipss_exit(void) +{ + platform_driver_unregister(&xqspipss_driver); +} + +module_exit(xqspipss_exit); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx PSS QSPI driver"); +MODULE_LICENSE("GPL"); + +#endif diff --git a/board/xilinx/dfe/xilinx_qspipss.h b/board/xilinx/dfe/xilinx_qspipss.h new file mode 100644 index 00000000000..cb077fc9cf6 --- /dev/null +++ b/board/xilinx/dfe/xilinx_qspipss.h @@ -0,0 +1,56 @@ + +#ifndef _XILINX_QSPIPSS_H_ +#define _XILINX_QSPIPSS_H_ + +#ifndef bool +typedef unsigned int bool; +#endif + +struct xqspipss { + u8 queue_state; + void *regs; + u32 input_clk_hz; + u32 irq; + u32 speed_hz; + + const void *txbuf; + void *rxbuf; + int bytes_to_transfer; + int bytes_to_receive; + u8 dev_busy; + int done; + + struct xqspipss_inst_format *curr_inst; + u8 inst_response; + bool is_inst; +}; + +struct spi_device { + struct xqspipss master; + u32 max_speed_hz; + u8 chip_select; + u8 mode; + u8 bits_per_word; +}; + +struct spi_transfer { + const void *tx_buf; + void *rx_buf; + unsigned len; + + unsigned cs_change:1; + u8 bits_per_word; + u16 delay_usecs; + u32 speed_hz; +}; + +/**************************************************************************/ +extern void xqspipss_init_hw(void *regs_base); +extern int xqspipss_setup_transfer(struct spi_device *qspi, + struct spi_transfer *transfer); +extern int xqspipss_transfer(struct spi_device *qspi, + struct spi_transfer *transfer); + +/**************************************************************************/ + +#endif diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c index 31340279cb8..7b83c849a7c 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -55,10 +55,15 @@ #define STM_ID_M25P80 0x14 #define STM_ID_M25P128 0x18 +#define STM_ID_N25Q128 0x20BB1810 + #define STMICRO_SR_WIP (1 << 0) /* Write-in-Progress */ struct stmicro_spi_flash_params { u8 idcode1; + /* XILINX: idcode1 isn't specific enough; + * multiple non-compatible devices match. Store complete idcode */ + u32 idcode; u16 page_size; u16 pages_per_sector; u16 nr_sectors; @@ -127,6 +132,21 @@ static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = { .nr_sectors = 16, .name = "M25P80", }, +/* + * XILINX: + * This table only examines the capacity of the device. + * The M25P128 and N25Q128 have the same capacity (16777216) + * but different number of sectors, pages per sector. + * Match the N25Q128 which is actually present on the board. + */ + { + .idcode1 = STM_ID_M25P128, + .idcode = STM_ID_N25Q128, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 256, + .name = "N25Q128", + }, { .idcode1 = STM_ID_M25P128, .page_size = 256, @@ -340,7 +360,15 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode) for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) { params = &stmicro_spi_flash_table[i]; if (params->idcode1 == idcode[2]) { - break; + if (params->idcode == 0) { + break; + } + /* Check complete device ID, if specified */ + if ((((params->idcode & 0xFF000000) >> 24) == idcode[0]) && + (((params->idcode & 0x00FF0000) >> 16) == idcode[1]) && + (((params->idcode & 0x000000FF) >> 0) == idcode[3])) { + break; + } } } diff --git a/include/configs/xpele.h b/include/configs/xpele.h index 70d9c3c3b78..3c7879fcc49 100644 --- a/include/configs/xpele.h +++ b/include/configs/xpele.h @@ -165,6 +165,21 @@ #define CONFIG_SYS_CBSIZE 256 #define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* phycore */ +/* + * SPI Settings + */ +#define CONFIG_CMD_SPI +#define CONFIG_ENV_SPI_MAX_HZ 30000000 +#define CONFIG_SF_DEFAULT_SPEED 30000000 +#define CONFIG_SPI_FLASH +#define CONFIG_CMD_SF +#ifdef NOTOW_BHILL +#define CONFIG_SPI_FLASH_ATMEL +#define CONFIG_SPI_FLASH_SPANSION +#define CONFIG_SPI_FLASH_WINBOND +#endif +#define CONFIG_SPI_FLASH_STMICRO + /* * NAND Flash settings */