/* Opcode + Address (3/4 bytes) */ int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, unsigned int cmdlen, const u8 *cmdbuf) { unsigned int reg; unsigned int addr_bytes = cmdlen > 4 ? 4 : 3; if (cmdlen < 4 || cmdbuf == NULL) { printf("QSPI: iInvalid input argument, len %d cmdbuf 0x%08x\n", cmdlen, (unsigned int)cmdbuf); return -EINVAL; } /* Setup the indirect trigger address */ writel((u32)plat->ahbbase, plat->regbase + CQSPI_REG_INDIRECTTRIGGER); /* Configure the opcode */ reg = cmdbuf[0] << CQSPI_REG_WR_INSTR_OPCODE_LSB; writel(reg, plat->regbase + CQSPI_REG_WR_INSTR); /* Setup write address. */ reg = cadence_qspi_apb_cmd2addr(&cmdbuf[1], addr_bytes); writel(reg, plat->regbase + CQSPI_REG_INDIRECTWRSTARTADDR); reg = readl(plat->regbase + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; reg |= (addr_bytes - 1); writel(reg, plat->regbase + CQSPI_REG_SIZE); return 0; }
static int cadence_qspi_apb_indirect_write_setup(void *reg_base, unsigned int ahb_phy_addr, unsigned txlen, const unsigned char *txbuf) { unsigned int reg; unsigned int addr_bytes = (txlen >= 5) ? 4: 3; pr_debug("%s txlen %d txbuf %p addr_bytes %d\n", __func__, txlen, txbuf, addr_bytes); hex_dump((unsigned int)txbuf, txbuf, txlen); if (txlen < 4 || txbuf == NULL) { pr_err("QSPI: Invalid input argument, txlen %d txbuf 0x%08x\n", txlen, (unsigned int)txbuf); return -EINVAL; } CQSPI_WRITEL((ahb_phy_addr & CQSPI_INDIRECTTRIGGER_ADDR_MASK), reg_base + CQSPI_REG_INDIRECTTRIGGER); /* Set opcode. */ reg = txbuf[0] << CQSPI_REG_WR_INSTR_OPCODE_LSB; CQSPI_WRITEL(reg, reg_base + CQSPI_REG_WR_INSTR); /* Setup write address. */ reg = cadence_qspi_apb_cmd2addr(&txbuf[1], addr_bytes); CQSPI_WRITEL(reg, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR); reg = CQSPI_READL(reg_base + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; reg |= (addr_bytes - 1); CQSPI_WRITEL(reg, reg_base + CQSPI_REG_SIZE); return 0; }
/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */ int cadence_qspi_apb_command_write(void *reg_base, unsigned int cmdlen, const u8 *cmdbuf, unsigned int txlen, const u8 *txbuf) { unsigned int reg = 0; unsigned int addr_value; unsigned int wr_data; unsigned int wr_len; if (!cmdlen || cmdlen > 5 || txlen > 8 || cmdbuf == NULL) { printf("QSPI: Invalid input arguments cmdlen %d txlen %d\n", cmdlen, txlen); return -EINVAL; } reg |= cmdbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; if (cmdlen == 4 || cmdlen == 5) { /* Command with address */ reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); /* Number of bytes to write. */ reg |= ((cmdlen - 2) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; /* Get address */ addr_value = cadence_qspi_apb_cmd2addr(&cmdbuf[1], cmdlen >= 5 ? 4 : 3); writel(addr_value, reg_base + CQSPI_REG_CMDADDRESS); } if (txlen) { /* writing data = yes */ reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB); reg |= ((txlen - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK) << CQSPI_REG_CMDCTRL_WR_BYTES_LSB; wr_len = txlen > 4 ? 4 : txlen; memcpy(&wr_data, txbuf, wr_len); writel(wr_data, reg_base + CQSPI_REG_CMDWRITEDATALOWER); if (txlen > 4) { txbuf += wr_len; wr_len = txlen - wr_len; memcpy(&wr_data, txbuf, wr_len); writel(wr_data, reg_base + CQSPI_REG_CMDWRITEDATAUPPER); } } /* Execute the command */ return cadence_qspi_apb_exec_flash_cmd(reg_base, reg); }
/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */ static int cadence_qspi_apb_command_write(void *reg_base, unsigned txlen, const unsigned char *txbuf) { unsigned int reg; unsigned int addr_value; unsigned int data; pr_debug("%s txlen %d txbuf %p\n", __func__, txlen, txbuf); hex_dump((unsigned int)txbuf, txbuf, txlen); if (!txlen || txlen > 5 || txbuf == NULL) { pr_err("QSPI: Invalid input argument, cmdlen %d txbuf 0x%08x\n", txlen, (unsigned int)txbuf); return -EINVAL; } reg = txbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; if (txlen == 2 || txlen == 3) { /* Command with data only. */ reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB); reg |= ((txlen - 2) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK) << CQSPI_REG_CMDCTRL_WR_BYTES_LSB; memcpy(&data, &txbuf[1], txlen - 1); /* Write the data */ CQSPI_WRITEL(data, reg_base + CQSPI_REG_CMDWRITEDATALOWER); } else if (txlen == 4 || txlen == 5) { /* Command with address */ reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); /* Number of bytes to write. */ reg |= ((txlen - 2) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; /* Get address */ addr_value = cadence_qspi_apb_cmd2addr(&txbuf[1], txlen >=5 ? 4 : 3); CQSPI_WRITEL(addr_value, reg_base + CQSPI_REG_CMDADDRESS); } return cadence_qspi_apb_exec_flash_cmd(reg_base, reg); }
/* 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, unsigned int rx_width, const u8 *cmdbuf) { unsigned int reg; unsigned int rd_reg; unsigned int addr_value; unsigned int dummy_clk; unsigned int dummy_bytes; unsigned int addr_bytes; /* * Identify addr_byte. All NOR flash device drivers are using fast read * which always expecting 1 dummy byte, 1 cmd byte and 3/4 addr byte. * With that, the length is in value of 5 or 6. Only FRAM chip from * ramtron using normal read (which won't need dummy byte). * Unlikely NOR flash using normal read due to performance issue. */ if (cmdlen >= 5) /* to cater fast read where cmd + addr + dummy */ addr_bytes = cmdlen - 2; else /* for normal read (only ramtron as of now) */ addr_bytes = cmdlen - 1; /* Setup the indirect trigger address */ writel((u32)plat->ahbbase, plat->regbase + CQSPI_REG_INDIRECTTRIGGER); /* Configure the opcode */ rd_reg = cmdbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB; 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); writel(addr_value, plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR); /* The remaining lenght is dummy bytes. */ dummy_bytes = cmdlen - addr_bytes - 1; if (dummy_bytes) { if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX) dummy_bytes = CQSPI_DUMMY_BYTES_MAX; rd_reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB); #if defined(CONFIG_SPL_SPI_XIP) && defined(CONFIG_SPL_BUILD) writel(0x0, plat->regbase + CQSPI_REG_MODE_BIT); #else writel(0xFF, plat->regbase + CQSPI_REG_MODE_BIT); #endif /* Convert to clock cycles. */ dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE; /* Need to minus the mode byte (8 clocks). */ dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE; if (dummy_clk) rd_reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK) << CQSPI_REG_RD_INSTR_DUMMY_LSB; } writel(rd_reg, plat->regbase + CQSPI_REG_RD_INSTR); /* set device size */ reg = readl(plat->regbase + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; reg |= (addr_bytes - 1); writel(reg, plat->regbase + CQSPI_REG_SIZE); return 0; }
static int cadence_qspi_apb_indirect_read_setup(void *reg_base, unsigned int ahb_phy_addr, unsigned txlen, const unsigned char *txbuf, unsigned int addr_bytes) { unsigned int reg; unsigned int addr_value; unsigned int dummy_clk; unsigned int dummy_bytes; pr_debug("%s txlen %d txbuf %p addr_bytes %d\n", __func__, txlen, txbuf, addr_bytes); hex_dump((unsigned int)txbuf, txbuf, txlen); if ((addr_bytes == 3 && txlen < 4) || (addr_bytes == 4 && txlen < 5)) { pr_err("QSPI: Invalid txbuf length, length %d\n", txlen); return -EINVAL; } CQSPI_WRITEL((ahb_phy_addr & CQSPI_INDIRECTTRIGGER_ADDR_MASK), reg_base + CQSPI_REG_INDIRECTTRIGGER); reg = txbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB; #ifdef CONFIG_M25PXX_USE_FAST_READ_QUAD_OUTPUT #error WTFO reg |= (CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB); #endif /* CONFIG_M25PXX_USE_FAST_READ_QUAD_OUTPUT */ /* Get address */ addr_value = cadence_qspi_apb_cmd2addr(&txbuf[1], addr_bytes); CQSPI_WRITEL(addr_value, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR); /* The remaining lenght is dummy bytes. */ dummy_bytes = txlen - addr_bytes - 1; /* Setup dummy clock cycles */ if (dummy_bytes) { if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX) dummy_bytes = CQSPI_DUMMY_BYTES_MAX; reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB); /* Set all high to ensure chip doesn't enter XIP */ CQSPI_WRITEL(0xFF, reg_base + CQSPI_REG_MODE_BIT); /* Convert to clock cycles. */ dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE; /* Need to minus the mode byte (8 clocks). */ dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE; if (dummy_clk) reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK) << CQSPI_REG_RD_INSTR_DUMMY_LSB; } CQSPI_WRITEL(reg, reg_base + CQSPI_REG_RD_INSTR); /* Set device size */ reg = CQSPI_READL(reg_base + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; reg |= (addr_bytes - 1); CQSPI_WRITEL(reg, reg_base + CQSPI_REG_SIZE); return 0; }