/* * Copyright (c) 2014 Brian Swetland * Copyright (c) 2014 Travis Geiselbrecht * * Use of this source code is governed by a MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT */ #include #include #include #include #include #include #include #include #include #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); }