#include <common.h>
#include <asm/io.h>
-#include <asm/errno.h>
+#include <linux/errno.h>
#include <wait_bit.h>
+#include <spi.h>
#include "cadence_qspi.h"
#define CQSPI_REG_POLL_US (1) /* 1us */
#define CQSPI_INST_TYPE_QUAD (2)
#define CQSPI_STIG_DATA_LEN_MAX (8)
-#define CQSPI_INDIRECTTRIGGER_ADDR_MASK (0xFFFFF)
#define CQSPI_DUMMY_CLKS_PER_BYTE (8)
#define CQSPI_DUMMY_BYTES_MAX (4)
return addr;
}
-static void cadence_qspi_apb_read_fifo_data(void *dest,
- const void *src_ahb_addr, unsigned int bytes)
-{
- unsigned int temp;
- int remaining = bytes;
- unsigned int *dest_ptr = (unsigned int *)dest;
- unsigned int *src_ptr = (unsigned int *)src_ahb_addr;
-
- while (remaining >= sizeof(dest_ptr)) {
- *dest_ptr = readl(src_ptr);
- remaining -= sizeof(src_ptr);
- dest_ptr++;
- }
- if (remaining) {
- /* dangling bytes */
- temp = readl(src_ptr);
- memcpy(dest_ptr, &temp, remaining);
- }
-
- return;
-}
-
-/* Read from SRAM FIFO with polling SRAM fill level. */
-static int qspi_read_sram_fifo_poll(const void *reg_base, void *dest_addr,
- const void *src_addr, unsigned int num_bytes)
-{
- unsigned int remaining = num_bytes;
- unsigned int retry;
- unsigned int sram_level = 0;
- unsigned char *dest = (unsigned char *)dest_addr;
-
- while (remaining > 0) {
- retry = CQSPI_REG_RETRY;
- while (retry--) {
- sram_level = CQSPI_GET_RD_SRAM_LEVEL(reg_base);
- if (sram_level)
- break;
- udelay(1);
- }
-
- if (!retry) {
- printf("QSPI: No receive data after polling for %d times\n",
- CQSPI_REG_RETRY);
- return -1;
- }
-
- sram_level *= CQSPI_FIFO_WIDTH;
- sram_level = sram_level > remaining ? remaining : sram_level;
-
- /* Read data from FIFO. */
- cadence_qspi_apb_read_fifo_data(dest, src_addr, sram_level);
- dest += sram_level;
- remaining -= sram_level;
- udelay(1);
- }
- return 0;
-}
-
void cadence_qspi_apb_controller_enable(void *reg_base)
{
unsigned int reg;
debug("%s: ref_clk %dHz sclk %dHz Div 0x%x\n", __func__,
ref_clk_hz, sclk_hz, div);
- div = (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB;
- reg |= div;
+ /* ensure the baud rate doesn't exceed the max value */
+ if (div > CQSPI_REG_CONFIG_BAUD_MASK)
+ div = CQSPI_REG_CONFIG_BAUD_MASK;
+
+ reg |= (div << CQSPI_REG_CONFIG_BAUD_LSB);
writel(reg, reg_base + CQSPI_REG_CONFIG);
cadence_qspi_apb_controller_enable(reg_base);
/* Opcode + Address (3/4 bytes) + dummy bytes (0-4 bytes) */
int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat,
- unsigned int cmdlen, const u8 *cmdbuf)
+ unsigned int cmdlen, unsigned int rx_width, const u8 *cmdbuf)
{
unsigned int reg;
unsigned int rd_reg;
addr_bytes = cmdlen - 1;
/* Setup the indirect trigger address */
- writel(((u32)plat->ahbbase & CQSPI_INDIRECTTRIGGER_ADDR_MASK),
+ writel((u32)plat->ahbbase,
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
/* Configure the opcode */
rd_reg = cmdbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB;
-#if (CONFIG_SPI_FLASH_QUAD == 1)
- /* Instruction and address at DQ0, data at DQ0-3. */
- rd_reg |= CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
-#endif
+ if (rx_width & SPI_RX_QUAD)
+ /* Instruction and address at DQ0, data at DQ0-3. */
+ rd_reg |= CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
/* Get address */
addr_value = cadence_qspi_apb_cmd2addr(&cmdbuf[1], addr_bytes);
return 0;
}
+static u32 cadence_qspi_get_rd_sram_level(struct cadence_spi_platdata *plat)
+{
+ u32 reg = readl(plat->regbase + CQSPI_REG_SDRAMLEVEL);
+ reg >>= CQSPI_REG_SDRAMLEVEL_RD_LSB;
+ return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK;
+}
+
+static int cadence_qspi_wait_for_data(struct cadence_spi_platdata *plat)
+{
+ unsigned int timeout = 10000;
+ u32 reg;
+
+ while (timeout--) {
+ reg = cadence_qspi_get_rd_sram_level(plat);
+ if (reg)
+ return reg;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat,
- unsigned int rxlen, u8 *rxbuf)
+ unsigned int n_rx, u8 *rxbuf)
{
- unsigned int reg;
+ unsigned int remaining = n_rx;
+ unsigned int bytes_to_read = 0;
+ int ret;
- writel(rxlen, plat->regbase + CQSPI_REG_INDIRECTRDBYTES);
+ writel(n_rx, plat->regbase + CQSPI_REG_INDIRECTRDBYTES);
/* Start the indirect read transfer */
writel(CQSPI_REG_INDIRECTRD_START_MASK,
plat->regbase + CQSPI_REG_INDIRECTRD);
- if (qspi_read_sram_fifo_poll(plat->regbase, (void *)rxbuf,
- (const void *)plat->ahbbase, rxlen))
- goto failrd;
+ while (remaining > 0) {
+ ret = cadence_qspi_wait_for_data(plat);
+ if (ret < 0) {
+ printf("Indirect write timed out (%i)\n", ret);
+ goto failrd;
+ }
- /* Check flash indirect controller */
- reg = readl(plat->regbase + CQSPI_REG_INDIRECTRD);
- if (!(reg & CQSPI_REG_INDIRECTRD_DONE_MASK)) {
- reg = readl(plat->regbase + CQSPI_REG_INDIRECTRD);
- printf("QSPI: indirect completion status error with reg 0x%08x\n",
- reg);
+ bytes_to_read = ret;
+
+ while (bytes_to_read != 0) {
+ bytes_to_read *= CQSPI_FIFO_WIDTH;
+ bytes_to_read = bytes_to_read > remaining ?
+ remaining : bytes_to_read;
+ /* Handle non-4-byte aligned access to avoid data abort. */
+ if (((uintptr_t)rxbuf % 4) || (bytes_to_read % 4))
+ readsb(plat->ahbbase, rxbuf, bytes_to_read);
+ else
+ readsl(plat->ahbbase, rxbuf, bytes_to_read >> 2);
+ rxbuf += bytes_to_read;
+ remaining -= bytes_to_read;
+ bytes_to_read = cadence_qspi_get_rd_sram_level(plat);
+ }
+ }
+
+ /* Check indirect done status */
+ ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTRD,
+ CQSPI_REG_INDIRECTRD_DONE_MASK, 1, 10, 0);
+ if (ret) {
+ printf("Indirect read completion error (%i)\n", ret);
goto failrd;
}
/* Clear indirect completion status */
writel(CQSPI_REG_INDIRECTRD_DONE_MASK,
plat->regbase + CQSPI_REG_INDIRECTRD);
+
return 0;
failrd:
/* Cancel the indirect read */
writel(CQSPI_REG_INDIRECTRD_CANCEL_MASK,
plat->regbase + CQSPI_REG_INDIRECTRD);
- return -1;
+ return ret;
}
/* Opcode + Address (3/4 bytes) */
return -EINVAL;
}
/* Setup the indirect trigger address */
- writel(((u32)plat->ahbbase & CQSPI_INDIRECTTRIGGER_ADDR_MASK),
+ writel((u32)plat->ahbbase,
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
/* Configure the opcode */