| /* |
| * Copyright (c) 2014 Brian Swetland |
| * Copyright (c) 2014 Travis Geiselbrecht |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files |
| * (the "Software"), to deal in the Software without restriction, |
| * including without limitation the rights to use, copy, modify, merge, |
| * publish, distribute, sublicense, and/or sell copies of the Software, |
| * and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| #include <dev/qspi.h> |
| |
| #include <debug.h> |
| #include <assert.h> |
| #include <compiler.h> |
| #include <printf.h> |
| #include <string.h> |
| #include <reg.h> |
| |
| #include <lib/console.h> |
| |
| #include <platform/zynq.h> |
| |
| #define QSPI_CONFIG 0xE000D000 |
| #define CFG_IFMODE (1 << 31) // Inteligent Flash Mode |
| #define CFG_LITTLE_ENDIAN (0 << 26) |
| #define CFG_BIG_ENDIAN (1 << 26) |
| #define CFG_HOLDB_DR (1 << 19) // set to 1 for dual/quad spi mode |
| #define CFG_NO_MODIFY_MASK (1 << 17) // do not modify this bit |
| #define CFG_MANUAL_START (1 << 16) // start transaction |
| #define CFG_MANUAL_START_EN (1 << 15) // enable manual start mode |
| #define CFG_MANUAL_CS_EN (1 << 14) // enable manual CS control |
| #define CFG_MANUAL_CS (1 << 10) // directly drives n_ss_out if MANUAL_CS_EN==1 |
| #define CFG_FIFO_WIDTH_32 (3 << 6) // only valid setting |
| #define CFG_BAUD_MASK (7 << 3) |
| #define CFG_BAUD_DIV_2 (0 << 3) |
| #define CFG_BAUD_DIV_4 (1 << 3) |
| #define CFG_BAUD_DIV_8 (2 << 3) |
| #define CFG_BAUD_DIV_16 (3 << 3) |
| #define CFG_CPHA (1 << 2) // clock phase |
| #define CFG_CPOL (1 << 1) // clock polarity |
| #define CFG_MASTER_MODE (1 << 0) // only valid setting |
| |
| #define QSPI_IRQ_STATUS 0xE000D004 // ro status (write UNDERFLOW/OVERFLOW to clear) |
| #define QSPI_IRQ_ENABLE 0xE000D008 // write 1s to set mask bits |
| #define QSPI_IRQ_DISABLE 0xE000D00C // write 1s to clear mask bits |
| #define QSPI_IRQ_MASK 0xE000D010 // ro mask value (1 = irq enabled) |
| #define TX_UNDERFLOW (1 << 6) |
| #define RX_FIFO_FULL (1 << 5) |
| #define RX_FIFO_NOT_EMPTY (1 << 4) |
| #define TX_FIFO_FULL (1 << 3) |
| #define TX_FIFO_NOT_FULL (1 << 2) |
| #define RX_OVERFLOW (1 << 0) |
| |
| #define QSPI_ENABLE 0xE000D014 // write 1 to enable |
| |
| #define QSPI_DELAY 0xE000D018 |
| #define QSPI_TXD0 0xE000D01C |
| #define QSPI_RXDATA 0xE000D020 |
| #define QSPI_SLAVE_IDLE_COUNT 0xE000D024 |
| #define QSPI_TX_THRESHOLD 0xE000D028 |
| #define QSPI_RX_THRESHOLD 0xE000D02C |
| #define QSPI_GPIO 0xE000D030 |
| #define QSPI_LPBK_DLY_ADJ 0xE000D038 |
| #define QSPI_TXD1 0xE000D080 |
| #define QSPI_TXD2 0xE000D084 |
| #define QSPI_TXD3 0xE000D088 |
| |
| #define QSPI_LINEAR_CONFIG 0xE000D0A0 |
| #define LCFG_ENABLE (1 << 31) // enable linear quad spi mode |
| #define LCFG_TWO_MEM (1 << 30) |
| #define LCFG_SEP_BUS (1 << 29) // 0=shared 1=separate |
| #define LCFG_U_PAGE (1 << 28) |
| #define LCFG_MODE_EN (1 << 25) // send mode bits (required for dual/quad io) |
| #define LCFG_MODE_ON (1 << 24) // only send instruction code for first read |
| #define LCFG_MODE_BITS(n) (((n) & 0xFF) << 16) |
| #define LCFG_DUMMY_BYTES(n) (((n) & 7) << 8) |
| #define LCFG_INST_CODE(n) ((n) & 0xFF) |
| |
| #define QSPI_LINEAR_STATUS 0xE000D0A4 |
| #define QSPI_MODULE_ID 0xE000D0FC |
| |
| int qspi_set_speed(struct qspi_ctxt *qspi, uint32_t khz) |
| { |
| uint32_t n; |
| |
| if (khz >= 100000) { |
| n = CFG_BAUD_DIV_2; |
| khz = 100000; |
| } else if (khz >= 50000) { |
| n = CFG_BAUD_DIV_4; |
| khz = 50000; |
| } else if (khz >= 25000) { |
| n = CFG_BAUD_DIV_8; |
| khz = 25000; |
| } else { |
| return -1; |
| } |
| |
| if (khz == qspi->khz) |
| return 0; |
| |
| qspi->khz = khz; |
| |
| writel(0, QSPI_ENABLE); |
| if (n == CFG_BAUD_DIV_2) { |
| writel(0x20, QSPI_LPBK_DLY_ADJ); |
| } else { |
| writel(0, QSPI_LPBK_DLY_ADJ); |
| } |
| |
| qspi->cfg &= ~CFG_BAUD_MASK; |
| qspi->cfg |= n; |
| |
| writel(qspi->cfg, QSPI_CONFIG); |
| writel(1, QSPI_ENABLE); |
| |
| return 0; |
| } |
| |
| int qspi_init(struct qspi_ctxt *qspi, uint32_t khz) |
| { |
| writel(0, QSPI_ENABLE); |
| writel(0, QSPI_LINEAR_CONFIG); |
| |
| // flush rx fifo |
| while (readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY) |
| readl(QSPI_RXDATA); |
| |
| qspi->cfg = (readl(QSPI_CONFIG) & CFG_NO_MODIFY_MASK) | |
| CFG_IFMODE | |
| CFG_HOLDB_DR | |
| CFG_FIFO_WIDTH_32 | |
| CFG_CPHA | CFG_CPOL | |
| CFG_MASTER_MODE | |
| CFG_BAUD_DIV_2 | |
| CFG_MANUAL_START_EN | CFG_MANUAL_CS_EN | CFG_MANUAL_CS; |
| |
| writel(qspi->cfg, QSPI_CONFIG); |
| qspi->khz = 100000; |
| qspi->linear_mode = false; |
| |
| writel(1, QSPI_ENABLE); |
| |
| // clear sticky irqs |
| writel(TX_UNDERFLOW | RX_OVERFLOW, QSPI_IRQ_STATUS); |
| |
| return 0; |
| } |
| |
| int qspi_enable_linear(struct qspi_ctxt *qspi) |
| { |
| if (qspi->linear_mode) |
| return 0; |
| |
| /* disable the controller */ |
| writel(0, QSPI_ENABLE); |
| writel(0, QSPI_LINEAR_CONFIG); |
| |
| /* put the controller in auto chip select mode and assert chip select */ |
| qspi->cfg &= ~(CFG_MANUAL_START_EN | CFG_MANUAL_CS_EN | CFG_MANUAL_CS); |
| writel(qspi->cfg, QSPI_CONFIG); |
| |
| #if 1 |
| // uses Quad I/O mode |
| // should be 0x82FF02EB according to xilinx manual for spansion flashes |
| writel(LCFG_ENABLE | |
| LCFG_MODE_EN | |
| LCFG_MODE_BITS(0xff) | |
| LCFG_DUMMY_BYTES(2) | |
| LCFG_INST_CODE(0xeb), |
| QSPI_LINEAR_CONFIG); |
| #else |
| // uses Quad Output Read mode |
| // should be 0x8000016B according to xilinx manual for spansion flashes |
| writel(LCFG_ENABLE | |
| LCFG_MODE_BITS(0) | |
| LCFG_DUMMY_BYTES(1) | |
| LCFG_INST_CODE(0x6b), |
| QSPI_LINEAR_CONFIG); |
| #endif |
| |
| /* enable the controller */ |
| writel(1, QSPI_ENABLE); |
| |
| qspi->linear_mode = true; |
| |
| DSB; |
| |
| return 0; |
| } |
| |
| int qspi_disable_linear(struct qspi_ctxt *qspi) |
| { |
| if (!qspi->linear_mode) |
| return 0; |
| |
| /* disable the controller */ |
| writel(0, QSPI_ENABLE); |
| writel(0, QSPI_LINEAR_CONFIG); |
| |
| /* put the controller back into manual chip select mode */ |
| qspi->cfg |= (CFG_MANUAL_START_EN | CFG_MANUAL_CS_EN | CFG_MANUAL_CS); |
| writel(qspi->cfg, QSPI_CONFIG); |
| |
| /* enable the controller */ |
| writel(1, QSPI_ENABLE); |
| |
| qspi->linear_mode = false; |
| |
| DSB; |
| |
| return 0; |
| } |
| |
| void qspi_cs(struct qspi_ctxt *qspi, unsigned int cs) |
| { |
| DEBUG_ASSERT(cs <= 1); |
| |
| if (cs == 0) |
| qspi->cfg &= ~(CFG_MANUAL_CS); |
| else |
| qspi->cfg |= CFG_MANUAL_CS; |
| writel(qspi->cfg, QSPI_CONFIG); |
| } |
| |
| static inline void qspi_xmit(struct qspi_ctxt *qspi) |
| { |
| // start txn |
| writel(qspi->cfg | CFG_MANUAL_START, QSPI_CONFIG); |
| |
| // wait for command to transmit and TX fifo to be empty |
| while ((readl(QSPI_IRQ_STATUS) & TX_FIFO_NOT_FULL) == 0) ; |
| } |
| |
| static inline void qspi_flush_rx(void) |
| { |
| while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ; |
| readl(QSPI_RXDATA); |
| } |
| |
| static const uint32_t TXFIFO[] = { QSPI_TXD1, QSPI_TXD2, QSPI_TXD3, QSPI_TXD0, QSPI_TXD0, QSPI_TXD0 }; |
| |
| void qspi_rd(struct qspi_ctxt *qspi, uint32_t cmd, uint32_t asize, uint32_t *data, uint32_t count) |
| { |
| uint32_t sent = 0; |
| uint32_t rcvd = 0; |
| |
| DEBUG_ASSERT(qspi); |
| DEBUG_ASSERT(asize < 6); |
| |
| qspi_cs(qspi, 0); |
| |
| writel(cmd, TXFIFO[asize]); |
| qspi_xmit(qspi); |
| |
| if (asize == 4) { // dummy byte |
| writel(0, QSPI_TXD1); |
| qspi_xmit(qspi); |
| qspi_flush_rx(); |
| } |
| |
| qspi_flush_rx(); |
| |
| while (rcvd < count) { |
| while (readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY) { |
| *data++ = readl(QSPI_RXDATA); |
| rcvd++; |
| } |
| while ((readl(QSPI_IRQ_STATUS) & TX_FIFO_NOT_FULL) && (sent < count)) { |
| writel(0, QSPI_TXD0); |
| sent++; |
| } |
| qspi_xmit(qspi); |
| } |
| qspi_cs(qspi, 1); |
| } |
| |
| void qspi_wr(struct qspi_ctxt *qspi, uint32_t cmd, uint32_t asize, uint32_t *data, uint32_t count) |
| { |
| uint32_t sent = 0; |
| uint32_t rcvd = 0; |
| |
| DEBUG_ASSERT(qspi); |
| DEBUG_ASSERT(asize < 6); |
| |
| qspi_cs(qspi, 0); |
| |
| writel(cmd, TXFIFO[asize]); |
| qspi_xmit(qspi); |
| |
| if (asize == 4) { // dummy byte |
| writel(0, QSPI_TXD1); |
| qspi_xmit(qspi); |
| qspi_flush_rx(); |
| } |
| |
| qspi_flush_rx(); |
| |
| while (rcvd < count) { |
| while (readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY) { |
| readl(QSPI_RXDATA); // discard |
| rcvd++; |
| } |
| while ((readl(QSPI_IRQ_STATUS) & TX_FIFO_NOT_FULL) && (sent < count)) { |
| writel(*data++, QSPI_TXD0); |
| sent++; |
| } |
| qspi_xmit(qspi); |
| } |
| |
| qspi_cs(qspi, 1); |
| } |
| |
| void qspi_wr1(struct qspi_ctxt *qspi, uint32_t cmd) |
| { |
| DEBUG_ASSERT(qspi); |
| |
| qspi_cs(qspi, 0); |
| writel(cmd, QSPI_TXD1); |
| qspi_xmit(qspi); |
| |
| while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ; |
| |
| readl(QSPI_RXDATA); |
| qspi_cs(qspi, 1); |
| } |
| |
| void qspi_wr2(struct qspi_ctxt *qspi, uint32_t cmd) |
| { |
| DEBUG_ASSERT(qspi); |
| |
| qspi_cs(qspi, 0); |
| writel(cmd, QSPI_TXD2); |
| qspi_xmit(qspi); |
| |
| while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ; |
| |
| readl(QSPI_RXDATA); |
| qspi_cs(qspi, 1); |
| } |
| |
| void qspi_wr3(struct qspi_ctxt *qspi, uint32_t cmd) |
| { |
| DEBUG_ASSERT(qspi); |
| |
| qspi_cs(qspi, 0); |
| writel(cmd, QSPI_TXD3); |
| qspi_xmit(qspi); |
| |
| while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ; |
| |
| readl(QSPI_RXDATA); |
| qspi_cs(qspi, 1); |
| } |
| |
| uint32_t qspi_rd1(struct qspi_ctxt *qspi, uint32_t cmd) |
| { |
| qspi_cs(qspi, 0); |
| writel(cmd, QSPI_TXD2); |
| qspi_xmit(qspi); |
| |
| while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ; |
| |
| qspi_cs(qspi, 1); |
| return readl(QSPI_RXDATA); |
| } |
| |
| // vim: set ts=4 sw=4 noexpandtab: |
| |