#include #include #include #include "common.h" #include "qspi_dma.h" #include "qspi_flash.h" #include "qspi_host.h" #include "qspi_nor.h" #include "bsp.h" #define uart_printf(...) struct qspi_host qspi_host; __align(16) DMA_DESCRIPTOR dma_desc; P_DMA_DESCRIPTOR tx_desc = &dma_desc; void *qspi_memcpy(void *dst, const void *src, int num) { char * psrc = (char *)src; char * pdst = (char *)dst; while (num-- > 0) *pdst++ = *psrc++; return dst; } void _writel(unsigned int data , unsigned int addr ) { *(volatile unsigned int*)(addr)=data; } unsigned int _readl(unsigned int addr) { return *(volatile unsigned int*)(addr); } void udelay(unsigned int cnt) { #if 1 volatile unsigned int i,j; for(i=0;iqpi_enabled) lut_entry[seq++] = QSPI_INSTR_CMD << 10 | QSPI_PAD_4X << 8 | cmd_cfg->opcode; else lut_entry[seq++] = QSPI_INSTR_CMD << 10 | QSPI_PAD_1X << 8 | cmd_cfg->opcode; if (cmd_cfg->addr_bytes) { if (chip->en_addr_4byte && cmd_cfg->addr_bytes == 3) addr_bytes = 4; else addr_bytes = cmd_cfg->addr_bytes; lut_entry[seq++] = QSPI_INSTR_ADDR << 10 | pins[cmd_cfg->addr_pins] << 8 | addr_bytes*8; //if (addr_bytes == 1) // lut_entry[seq - 1] |= 0x4000; } if (cmd_cfg->mode_bits) { if (cmd_cfg->mode_bits == 2) { mode_instr = QSPI_INSTR_MODE2; mode &= 0x3; } else if (cmd_cfg->mode_bits == 4) { mode_instr = QSPI_INSTR_MODE4; mode &= 0xf; } else { mode_instr = QSPI_INSTR_MODE; } lut_entry[seq++] = mode_instr << 10 | pins[cmd_cfg->mode_pins] << 8 | mode; } if (cmd_cfg->dummy_pins) { if (chip->qpi_enabled) dummy_cycles = chip->qpi_dummy; else dummy_cycles = cmd_cfg->dummy_cycles; lut_entry[seq++] = QSPI_INSTR_DUMMY << 10 | pins[cmd_cfg->dummy_pins] << 8 | dummy_cycles; } if (cmd_cfg->data_pins) { if (cmd_cfg->type == CMD_W_RX_DATA) { lut_entry[seq++] = QSPI_INSTR_READ << 10 | pins[cmd_cfg->data_pins] << 8; /* Add JMP_ON_CS for read */ lut_entry[seq++] = QSPI_INSTR_JMP_ON_CS << 10; } else if (cmd_cfg->type == CMD_W_TX_DATA) { lut_entry[seq++] = QSPI_INSTR_WRITE << 10 | pins[cmd_cfg->data_pins] << 8; } else { uart_printf("err: type of cmd %d is wrong in table\r\n", cmd_cfg->opcode); return; } } /* Add stop at the end */ lut_entry[seq++] = QSPI_INSTR_STOP << 10; for (i = 0; i < seq/2; i++) { lut_value = lut_entry[i*2] | (lut_entry[i*2 + 1] << 16); _writel(lut_value, QSPI0_LUT0 + seq_id*0x10 + i*0x4); } if (seq % 2) { lut_value =lut_entry[seq - 1]; _writel(lut_value, QSPI0_LUT0 + seq_id*0x10 + (seq/2)*0x4); } // for (i = 0; i < seq; i++) // uart_printf("seq=%d, lut_entry[%d]=0x%x\n\r", seq, i, lut_entry[i]); return; } static int qspi_update_shared_lut(struct spi_flash_chip *chip, struct spi_flash_cmd *cmd) { struct spi_flash_cmd_cfg *cmd_cfg = cmd->cmd_cfg; uint32_t seq_id = QSPI_LUT_SEQID1; qspi_config_lookup_tbl(chip, cmd_cfg, cmd->mode, seq_id); return seq_id; } /* * Reserved for future optimization * Pre-init some lookup tables for special commands to accelerate */ int qspi_preinit_lookup_tbl(struct spi_flash_chip *chip) { struct spi_flash_cmd_cfg *cmd_cfg = chip->table; int lut_map = 0; for (; cmd_cfg->opcode != 0x0; cmd_cfg++) { if (cmd_cfg->seq_id != 0xFF) { if (lut_map & 1 << cmd_cfg->seq_id) { uart_printf("err: LUT %d already used\r\n", cmd_cfg->seq_id); return -1; } qspi_config_lookup_tbl(chip, cmd_cfg, 0xff,cmd_cfg->seq_id); lut_map |= 1 << cmd_cfg->seq_id; uart_printf("config 0x%x, 0x%x\r\n",cmd_cfg->seq_id, lut_map); } } chip->host->lut_map = lut_map; uart_printf("Fixed LUT bit-map: 0x%x\r\n", lut_map); return 0; } void qspi_enable_ahb_preempt(int on) { uint32_t mcr; mcr = _readl(QSPI0_MCR); if(on) mcr |= QSPI0_MCR_SW_PROG_ERASE; else mcr &= ~QSPI0_MCR_SW_PROG_ERASE; _writel(mcr, QSPI0_MCR); } void qspi_enter_xip_mode(int enable) { uint32_t mcr; mcr = _readl(QSPI0_MCR); if (enable) mcr |= QSPI0_MCR_XIP_EN; else mcr &= ~(QSPI0_MCR_XIP_EN | QSPI0_MCR_SW_PROG_ERASE); _writel(mcr, QSPI0_MCR); } int qspi_enable_xip(struct spi_flash_chip *chip, struct spi_flash_cmd_cfg *cmd_cfg) { struct qspi_host *host = chip->host; qspi_config_lookup_tbl(chip, cmd_cfg, 0xff, QSPI_LUT_SEQID0); qspi_enter_mode(QSPI_DISABLE_MODE); qspi_init_ahb(QSPI_LUT_SEQID0, chip->page_size); qspi_enter_mode(QSPI_NORMAL_MODE); if (host->use_xip) { host->xip_read = 1; uart_printf("XIP Read mode enabled\r\n"); } else { host->xip_read = 0; uart_printf("IPS Read mode enabled\r\n"); } return 0; } static void qspi_reset(void) { uint32_t reg; /* qspi softreset first */ reg = _readl(QSPI0_MCR); reg |= 0x3; _writel(reg, QSPI0_MCR); reg = _readl(QSPI0_MCR); if ((reg & 0x3) != 0x3) uart_printf("reset ignored 0x%x\r\n", reg); udelay(1); //cpu_cycle_delay(10); reg &= ~0x3; _writel(reg, QSPI0_MCR); } static void qspi_clk_enable(void) { // PMUA->QSPI_CLK_RES_CTRL |= 0x1 << 4 | 0x1 << 3;; // PMUA->QSPI_CLK_RES_CTRL |= 0x3; UINT32 val; UINT32 QSPI_CLK_RES_CTRL; QSPI_CLK_RES_CTRL=0xD4282800+0x60; val=_readl(QSPI_CLK_RES_CTRL); val |= ((0x1 << 4) | (0x1 << 3)); _writel(val,QSPI_CLK_RES_CTRL); val |= 0x3; _writel(val,QSPI_CLK_RES_CTRL); } static void qspi_clk_disable(void) { // PMUA->QSPI_CLK_RES_CTRL &= ~(0x1 << 4 | 0x1 << 3); // PMUA->QSPI_CLK_RES_CTRL &= ~0x3; UINT32 val; UINT32 QSPI_CLK_RES_CTRL; QSPI_CLK_RES_CTRL=0xD4282800+0x60; val=_readl(QSPI_CLK_RES_CTRL); val &= ~(0x1 << 4 | 0x1 << 3); _writel(val,QSPI_CLK_RES_CTRL); val &= ~0x3; _writel(val,QSPI_CLK_RES_CTRL); } static void qspi_set_func_clk(struct qspi_host *host, int mhz) { int clk_sel; int freq; UINT32 val; UINT32 QSPI_CLK_RES_CTRL; /* Default qspi clock is divided by 4 in PMU */ freq = mhz << 2; freq *= 1000000; if (freq >= 416000000) clk_sel = QSPI_FUNC_CLK_416MHZ; else if (freq >= 312000000) clk_sel = QSPI_FUNC_CLK_312MHZ; else if (freq >= 208000000) clk_sel = QSPI_FUNC_CLK_208MHZ; else if (freq >= 156000000) clk_sel = QSPI_FUNC_CLK_156MHZ; else if (freq >= 104000000) clk_sel = QSPI_FUNC_CLK_104MHZ; else if (freq >= 78000000) clk_sel = QSPI_FUNC_CLK_78MHZ; else if (freq >= 52000000) clk_sel = QSPI_FUNC_CLK_52MHZ; else clk_sel = QSPI_FUNC_CLK_26MHZ; qspi_clk_disable(); // PMUA->QSPI_CLK_RES_CTRL &= ~(0x7 << 6); // PMUA->QSPI_CLK_RES_CTRL |= clk_sel << 6; QSPI_CLK_RES_CTRL=0xD4282800+0x60; val=_readl(QSPI_CLK_RES_CTRL); val &= ~(0x7 << 6); _writel(val,QSPI_CLK_RES_CTRL); val |= clk_sel << 6; _writel(val,QSPI_CLK_RES_CTRL); qspi_clk_enable(); host->bus_clk = mhz; // uart_printf("Bus clock: %dMHz QSPI_CLK_RES_CTRL: 0x%x\r\n",mhz, PMUA->QSPI_CLK_RES_CTRL); } #define QSPI_CLK_RES_CTRL_REG 0xD4282860 void qspi_clock_change(int mhz) //new implementation on CraneLR { UINT32 clk_sel, val; /* new qspi controller clock is the same as flash device clock*/ switch (mhz) { case 104: clk_sel = 0; break; case 89: clk_sel = 1; break; case 94: clk_sel = 2; break; case 69: clk_sel = 3; break; case 57: clk_sel = 4; break; default: clk_sel = 0; //104 break; } val=_readl(QSPI_CLK_RES_CTRL_REG); val &= ~(0x3F << 6); val |= (0x3<<9)/* clk_div=4*/ | (clk_sel<<6); _writel(val,QSPI_CLK_RES_CTRL_REG); _writel((val|(1<<12)),QSPI_CLK_RES_CTRL_REG); //qspi clock fc while (_readl(QSPI_CLK_RES_CTRL_REG)&(1<<12)); } static void qspi_set_func_clk_new(struct qspi_host *host, int mhz) { UINT32 val; val=_readl(QSPI_CLK_RES_CTRL_REG); val &= ~((0x1<<4)|(0x1<<3)|(0x1<<1)|(0x1<<0)); _writel(val,QSPI_CLK_RES_CTRL_REG); //clock disable val |= ((0x1<<4)|(0x1<<3)|(0x1<<1)|(0x1<<0)); _writel(val,QSPI_CLK_RES_CTRL_REG); //clock enable, thus reset qspi ctrl qspi_clock_change(mhz); host->bus_clk = mhz; } static void qspi_enable_dma(struct qspi_host *host) { uint32_t resr; resr = _readl(QSPI0_RSER); resr |= QSPI0_RSER_TBFDE; _writel(resr, QSPI0_RSER); } static void qspi_disable_dma(struct qspi_host *host) { uint32_t resr; resr = _readl(QSPI0_RSER); resr &= ~QSPI0_RSER_TBFDE; _writel(resr, QSPI0_RSER); } static void qspi_enable_xip_on_int(struct qspi_host *host) { uint32_t resr; resr = _readl(QSPI0_RSER); resr |= QSPI0_RSER_XIP_ON; _writel(resr, QSPI0_RSER); } static void qspi_disable_xip_on_int(struct qspi_host *host) { uint32_t resr; resr = _readl(QSPI0_RSER); resr &= ~QSPI0_RSER_XIP_ON; _writel(resr, QSPI0_RSER); } static void qspi_config_interrupt(struct qspi_host *host) { uint32_t resr; resr = _readl(QSPI0_RSER); resr |= QSPI0_RSER_ILLINIE | QSPI0_RSER_ABSEIE | QSPI0_RSER_AITIE | QSPI0_RSER_AIBSIE | QSPI0_RSER_ABOIE |QSPI0_RSER_IUEIE | QSPI0_RSER_IPIEIE | QSPI0_RSER_IPGEIE | QSPI0_RSER_XIP_ON; _writel(resr, QSPI0_RSER); } static void qspi_enable_interrupt(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; uint32_t resr; resr = _readl(QSPI0_RSER); resr |= QSPI0_RSER_TFIE; if (cmd->n_tx) { resr |= QSPI0_RSER_TBUIE; if (!host->use_dma && host->bytes_left > 0) { resr |= QSPI0_RSER_TBFIE; } } else if (cmd->n_rx) { resr |= QSPI0_RSER_RBOIE | QSPI0_RSER_RBDIE; } _writel(resr, QSPI0_RSER); } static void qspi_disable_interrupt(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; uint32_t resr; resr = _readl(QSPI0_RSER); resr &= ~QSPI0_RSER_TFIE; if (cmd->n_tx) resr &= ~(QSPI0_RSER_TBUIE | QSPI0_RSER_TBFIE); else if (cmd->n_rx) resr &= ~(QSPI0_RSER_RBOIE | QSPI0_RSER_RBDIE); if (host->use_dma) resr &= ~QSPI0_RSER_TBFDE; _writel(0, QSPI0_RSER); } static int qspi_fill_to_txbuff(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; int left_bytes = host->bytes_left; int total_cnt; uint32_t reg; uint32_t data; if (!cmd) { uart_printf("err: receive tx interrupt while no cmd sent\n"); return 1; } total_cnt = (cmd->n_tx + QSPI_TX_BUFF_POP_MIN - 1) / QSPI_TX_BUFF_POP_MIN; total_cnt = total_cnt*QSPI_TX_BUFF_POP_MIN; while (left_bytes > 0) { _writel(QSPI0_FR_TBFF, QSPI0_SR); reg = _readl(QSPI0_SR); if (reg & QSPI0_FR_TBFF) break; if (cmd->n_tx - total_cnt + left_bytes <= 0) { data = 0; } else if (cmd->n_tx - total_cnt + left_bytes < 4) { data = 0; qspi_memcpy(&data, &cmd->tx_buf[total_cnt - left_bytes], left_bytes); } else { data = (*(uint32_t *)&cmd->tx_buf[total_cnt - left_bytes]); } _writel(data, QSPI0_TBDR); left_bytes -= 4; } host->bytes_left = left_bytes; if (left_bytes <= 0) return 0; return 1; } static void qspi_read_from_rxbuff(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; int left_bytes = host->bytes_left; uint32_t sr; uint32_t data; int i; while (left_bytes > 0) { sr = _readl(QSPI0_FR); if (!(sr & QSPI0_FR_RBDF)) break; /* Check RXWE flag for data comming */ for (i = 0; i <= host->wmrk; i++) { data = _readl(QSPI0_RBDR0 + i*4); //uart_printf("i=%d data=0x%x\r\n", i , data); if (left_bytes >= 4) qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes], &data, 4); else qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes], &data, left_bytes); left_bytes -= 4; } /* Set RBDF to trigger RX Buffer POP */ _writel(0x1 << 16, QSPI0_FR); } host->bytes_left = left_bytes; return; } static void qspi_xfer_done(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; int left_bytes = host->bytes_left; uint32_t rdbfl; uint32_t data; uint32_t i; if (host->use_dma) left_bytes = 0; if (cmd->n_rx && left_bytes > 0) { rdbfl = _readl(QSPI0_RBSR); rdbfl = (rdbfl >> 8) & 0x3f; for (i = 0; i <= rdbfl; i++) { data = _readl(QSPI0_RBDR0 + i*4); if (left_bytes >= 4) qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes], &data, 4); else qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes], &data, left_bytes); left_bytes -= 4; } if (left_bytes > 0) { uart_printf("Error: Not read enough data: " "left_bytes=%d, cmd->n_rx=%d\r\n", left_bytes, cmd->n_rx); } } host->bytes_left = left_bytes; host->complete = 1; return; } static void qspi_irq_handler(void *data) { struct qspi_host *host = &qspi_host; struct spi_flash_cmd *cmd = host->cmd; uint32_t fr, resr; (void)data; fr = _readl(QSPI0_FR); _writel(fr & ~QSPI0_FR_RBDF, QSPI0_FR); if (fr & QSPI0_FR_XIP_ON) uart_printf("XIP suspend/resume happen-----fr=0x%x->\r\n", fr); if (!cmd) { uart_printf("Interrupt happen while no cmd sent, fr=0x%x\r\n", fr); return; } resr = _readl(QSPI0_RSER); _writel(0x0, QSPI0_RSER); if (fr & (QSPI0_FR_ILLINE | QSPI0_FR_IUEF | QSPI0_FR_IPAEF | QSPI0_FR_IPIEF | QSPI0_FR_IPGEF | QSPI0_FR_RBOF | QSPI0_FR_TBUF)) { if (fr & QSPI0_FR_ILLINE) uart_printf("Err: Illegal Instruction Error Flag\r\n"); if (fr & QSPI0_FR_IUEF) uart_printf("Err: IP Command Usage Error Flag\r\n"); if (fr & QSPI0_FR_IPAEF) uart_printf("Err: IP Command Trigger during AHB Access Error Flag\r\n"); if (fr & QSPI0_FR_IPIEF) uart_printf("Err: IP Command Trigger could not be executed Error Flag\r\n"); if (fr & QSPI0_FR_IPGEF) uart_printf("Err: IP Command Trigger during AHB Grant Error Flag\r\n"); if (fr & QSPI0_FR_RBOF) uart_printf("Error: RX Buffer Overflow\r\n"); if (fr & QSPI0_FR_TBUF) uart_printf("Error: TX Buffer Underrun Flag\r\n"); host->complete = 1; } if (!host->use_dma && host->bytes_left > 0) { if (cmd->n_tx && (fr & QSPI0_FR_TBFF)) { if (!qspi_fill_to_txbuff(host)) resr &= ~QSPI0_RSER_TBFIE; } if (cmd->n_rx && (fr & QSPI0_FR_RBDF)) qspi_read_from_rxbuff(host); } if (fr & QSPI0_FR_TFF) qspi_xfer_done(host); _writel(resr, QSPI0_RSER); return; } struct qspi_host * qspi_host_init(int cs, int mhz, int use_xip) { struct qspi_host *host = &qspi_host; uint32_t reg; memset(host, 0, sizeof(struct qspi_host)); host->cs_addr[QSPI_CS_A1] = QSPI0_FLASH_A1_BASE; host->cs_addr[QSPI_CS_A2] = QSPI0_FLASH_A2_BASE; host->cs_addr[QSPI_CS_B1] = QSPI0_FLASH_B1_BASE; host->cs_addr[QSPI_CS_B2] = QSPI0_FLASH_B2_BASE; host->use_intr = 0; host->en_tx_dma = 0; host->use_xip = use_xip; qspi_config_mfp(cs); /* Enable qspi clk, and release reset */ if (CHIP_IS_CRANELR || CHIP_IS_CRANELS_A0 || CHIP_IS_CRANELG || CHIP_IS_CRANELRH) qspi_set_func_clk_new(host, mhz); else qspi_set_func_clk(host, mhz); /* qspi softreset first */ qspi_reset(); /* clock settings */ qspi_enter_mode(QSPI_DISABLE_MODE); /* sampled by sfif_clk_b; half cycle delay; */ /* NOT SETTING SMPR[5] FOR QSPI @104M SINCE SOME FLASH DEVICE MAY GET STUCK ON INIT, KEEP PAYING ATTENTION TO THIS WHEN APPLYING CRANE A0 OR OTHER FLASH DEVICES.*/ #if 1 _writel(0x0, QSPI0_SMPR); _writel(0x8, QSPI0_SOCCR); //good for erase/write stability #else if (mhz < 104) _writel(0x0, QSPI0_SMPR); else _writel(0x1 << 5, QSPI0_SMPR); #endif //_writel(0x202, QSPI0_FLSHCR); /* Give the default source address */ qspi_write_sfar(host, host->cs_addr[QSPI_CS_A1]); _writel(0x0, QSPI0_SFACR); //qspi_init_ahb(0); /* config ahb */ /* Set flash memory map */ #if (defined (ENABLE_SLT_FEATURE) && (!defined (ENABLE_SLT_V2_FEATURE))) _writel(QSPI0_FLASH_A1_BASE & 0xfffffc00, QSPI0_SFA1AD); #else _writel(QSPI0_FLASH_A1_TOP & 0xfffffc00, QSPI0_SFA1AD); #endif _writel(QSPI0_FLASH_A2_TOP & 0xfffffc00, QSPI0_SFA2AD); _writel(QSPI0_FLASH_B1_TOP & 0xfffffc00, QSPI0_SFB1AD); _writel(QSPI0_FLASH_B2_TOP & 0xfffffc00, QSPI0_SFB2AD); /* * ISD3FB, ISD2FB, ISD3FA, ISD2FA = 1; ENDIAN = 0x3; END_CFG=0x3 * DELAY_CLK4X_EN = 1 */ reg = _readl(QSPI0_MCR); reg &= ~0x000f000c; reg |= 0x000f000c; _writel(reg, QSPI0_MCR); /* Module enabled */ qspi_enter_mode(QSPI_NORMAL_MODE); /* Read using the IP Bus registers QSPI_RBDR0 to QSPI_RBDR31*/ qspi_write_rbct(host, 0x1 << 8); if (host->use_intr) { uint32_t fr; // ISR_Connect(45, qspi_irq_handler, 0); // INT_Enable(45, 0, 15); fr = _readl(QSPI0_FR); if (fr) _writel(fr, QSPI0_FR); qspi_config_interrupt(host); } /* uart_printf("tx_desc: 0x%x\r\n", tx_desc); uart_printf("use_intr=%d en_tx_dma=%d use_xip=%d\r\n", host->use_intr, host->en_tx_dma, host->use_xip); */ return host; } static void qspi_wait_cmd_done(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; uint32_t fr; do { fr = _readl(QSPI0_FR); if (fr & QSPI0_FR_ILLINE) { uart_printf("Err: Illegal Instruction Error Flag\r\n"); break; } if (fr & QSPI0_FR_IUEF) { uart_printf("Err: IP Command Usage Error Flag\r\n"); break; } if (fr & QSPI0_FR_IPAEF) { uart_printf("Err: IP Command Trigger during AHB Access Error Flag\r\n"); break; } if (fr & QSPI0_FR_IPIEF) { uart_printf("Err: IP Command Trigger could not be executed Error Flag\r\n"); break; } if (fr & QSPI0_FR_IPGEF) { uart_printf("Err: IP Command Trigger during AHB Grant Error Flag\r\n"); break; } if (fr & QSPI0_FR_TFF) { _writel(0x1, QSPI0_FR); break; } } while(1); if (cmd->rx_buf && (fr & QSPI0_FR_RBOF)) uart_printf("Error: RX Buffer Overflow\r\n"); if (cmd->tx_buf && (fr & QSPI0_FR_TBUF)) uart_printf("Error: TX Buffer Underrun Flag\r\n"); } static void qspi_poll_rx_buff(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; int left_bytes = host->bytes_left; uint32_t sr; uint32_t data; int rdbfl, i; do { /* Check RXWE flag for data comming */ sr = _readl(QSPI0_FR); if (!(sr & (0x1 << 16))) continue; for (i = 0; i <= host->wmrk; i++) { data = _readl(QSPI0_RBDR0 + i*4); if (left_bytes >= 4) qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes], &data, 4); else qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes], &data, left_bytes); left_bytes -= 4; } /* Set RBDF to trigger RX Buffer POP */ _writel(0x1 << 16, QSPI0_FR); if (left_bytes < 0) { break; } else if ((left_bytes + 3)/4 < (host->wmrk + 1)) { /* Left bytes < wmrk will not trigger RXWE */ break; } } while(1); /* Wait cmd to be finished */ qspi_wait_cmd_done(host); if (left_bytes > 0) { rdbfl = _readl(QSPI0_RBSR); rdbfl = (rdbfl >> 8) & 0x3f; for (i = 0; i <= rdbfl; i++) { data = _readl(QSPI0_RBDR0 + i*4); if (left_bytes >= 4) qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes], &data, 4); else qspi_memcpy(&cmd->rx_buf[cmd->n_rx - left_bytes], &data, left_bytes); left_bytes -= 4; } } if (left_bytes > 0) { uart_printf("Error: Not read enough data: left_bytes=%d, cmd->n_rx=%d\r\n", left_bytes, cmd->n_rx); } return; } static void qspi_fill_tx_buff(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; int left_bytes = host->bytes_left; int total_cnt; uint32_t reg; uint32_t data; total_cnt = (cmd->n_tx + QSPI_TX_BUFF_POP_MIN - 1) / QSPI_TX_BUFF_POP_MIN; total_cnt = total_cnt*QSPI_TX_BUFF_POP_MIN; while (left_bytes > 0) { reg = _readl(QSPI0_SR); if (reg & (0x1 << 27)) continue; if (cmd->n_tx - total_cnt + left_bytes <= 0) { data = 0; } else if (cmd->n_tx - total_cnt + left_bytes < 4) { data = 0; qspi_memcpy(&data, &cmd->tx_buf[total_cnt - left_bytes], left_bytes); } else { data = (*(uint32_t *)&cmd->tx_buf[total_cnt - left_bytes]); } _writel(data, QSPI0_TBDR); left_bytes -= 4; } host->bytes_left = left_bytes; qspi_wait_cmd_done(host); return; } int qspi_cmd_done_pio(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; /* receive rx data */ if (cmd->n_rx) qspi_poll_rx_buff(host); else if (cmd->n_tx) qspi_fill_tx_buff(host); else qspi_wait_cmd_done(host); return 0; } int qspi_cmd_done_interrupt(struct qspi_host *host) { while (!(host->complete)); qspi_disable_interrupt(host); return 0; } int qspi_start_dma_xfer(struct qspi_host *host) { struct spi_flash_cmd *cmd = host->cmd; DMA_CMDx_T TX_data; TX_data.value = 0; TX_data.bits.IncSrcAddr = 1; TX_data.bits.FlowTrg = 1; TX_data.bits.Width = 3; TX_data.bits.MaxBurstSize = 3; TX_data.bits.Length = host->bytes_left; dma_map_device_to_channel(99, QSPI_DMA_TX_CHANNEL); //TX /* alignment is handled in qspi, should not set this in DMAC */ //set_user_aligment(QSPI_DMA_TX_CHANNEL); #if 0 dma_config_descriptor((uint32_t *)tx_desc, 0, (uint32_t)cmd->tx_buf, QSPI0_TBDR, TX_data.value, 1); dma_load_descriptor((void *)tx_desc, QSPI_DMA_TX_CHANNEL); #else dma_set_mode(DMA_MODE_NONFETCH, QSPI_DMA_TX_CHANNEL); dma_set_reg_nf((uint32_t)cmd->tx_buf, QSPI0_TBDR, &TX_data, QSPI_DMA_TX_CHANNEL); #endif dcache_clean_invalidate_range((uint32_t)cmd->tx_buf, cmd->n_tx); dma_xfer_start(QSPI_DMA_TX_CHANNEL); return 0; } static int __qspi_start_cmd(struct spi_flash_chip *chip, struct spi_flash_cmd *cmd) { struct qspi_host *host = chip->host; struct spi_flash_cmd_cfg *cmd_cfg = cmd->cmd_cfg; uint32_t ipcr, sfar, mcr, sr, fr; uint32_t tmp, data, cpsr; int seq_id = cmd_cfg->seq_id; int left_bytes, tx_cnt, i; int wmrk = 0; host->cmd = cmd; host->bytes_left = 0; host->complete = 0; host->use_dma = 0; // if (seq_id < 0 || !(host->lut_map & 1 << seq_id)) if ((seq_id == 0xff) || (!(host->lut_map & (1 << seq_id)))) { // uart_printf("qspi_update_shared_lut 0x%x 0x%x\r\n", seq_id, (host->lut_map & 1 << seq_id)); seq_id = qspi_update_shared_lut(chip, cmd); } /* Reset the IP sequence pointers */ tmp = _readl(QSPI0_SPTRCLR); tmp |= 0x1 << 8; _writel(tmp, QSPI0_SPTRCLR); if (cmd->n_rx) { /* Clear RX FIFO. Invalidate the RX Buffer */ mcr = _readl(QSPI0_MCR); mcr |= 0x1 << 10; //CLR_RXF _writel(mcr, QSPI0_MCR); /* Set RX Buffer Watermark */ if (cmd->n_rx <= (QSPI_RX_BUFF_MAX*4)) wmrk = (cmd->n_rx + 3)/4 - 1; else wmrk = 0x1; /* Water Mark: 16*4byte */ tmp = _readl(QSPI0_RBCT); tmp &= ~0x1f; tmp |= wmrk; qspi_write_rbct(host, tmp); host->wmrk = wmrk; host->bytes_left = cmd->n_rx; } else if (cmd->n_tx) { /* Clear TX FIFO/Buffer */ mcr = _readl(QSPI0_MCR); mcr |= 0x1 << 11; //CLR_TXF _writel(mcr, QSPI0_MCR); /* Set QSPI_SFACR[CAS] QSPI_SFACR[WA] if needed */ tx_cnt = (cmd->n_tx + QSPI_TX_BUFF_POP_MIN - 1) / QSPI_TX_BUFF_POP_MIN; host->bytes_left = tx_cnt*QSPI_TX_BUFF_POP_MIN; if (host->en_tx_dma && host->bytes_left >= 32) { host->use_dma = 1; wmrk = 0x7; //0x1; // 32bytes watermark tmp = _readl(QSPI0_TBCT); tmp &= ~0x1f; tmp |= wmrk; _writel(tmp, QSPI0_TBCT); host->wmrk = wmrk; qspi_enable_dma(host); qspi_start_dma_xfer(host); #if 0 /* * Before trigger qspi to send data to externl bus,FIFO * need to have some data, or FIFO underflow error may happen. * DMA need some time to write data to TX FIFO, but * poll QSPI register may lead to bus hang(known bug), so we add * a delay here for this requirement. */ udelay(5); //cpu_cycle_delay(20); #else while(1) { uint32_t tbsr; tbsr = _readl(QSPI0_TBSR); tbsr &= QSPI0_TBSR_TRBFL_MASK; tbsr = tbsr >> QSPI0_TBSR_TRBFL_SHIFT; if (tbsr >= min(host->bytes_left/4, QSPI_TX_BUFF_MAX)) break; //uart_printf("====tbsr=%d\r\n", tbsr); } #endif } else { /* * Provide initial data for the program * command into the circular buffer */ host->use_dma = 0; tx_cnt = (tx_cnt*QSPI_TX_BUFF_POP_MIN)/4; if (tx_cnt > QSPI_TX_BUFF_MAX) tx_cnt = QSPI_TX_BUFF_MAX; for (i = 0; i < tx_cnt; i++) { left_bytes = cmd->n_tx - i*4; if (left_bytes <= 0) { data = 0; } else if (left_bytes < 4) { data = 0; qspi_memcpy(&data, &cmd->tx_buf[i*4], left_bytes); } else { data = (*(uint32_t *)&cmd->tx_buf[i*4]); } _writel(data, QSPI0_TBDR); } host->bytes_left -= tx_cnt*4; } } /* Set flash address to be accessed */ sfar = 0; for (i = 0; i < cmd->n_addr; i++) { sfar <<= 8; sfar |= cmd->addr[i]; } sfar += host->cs_addr[chip->cs]; qspi_write_sfar(host, sfar); if (cmd->flag & RST_AHB_DOMAIN) { int size; if (cmd->tx_buf) size = cmd->n_tx; else size = chip->block_size; // dcache_clean_invalidate_range(sfar, size); } /* trigger command */ ipcr = (seq_id & 0xf) << 24; if (cmd->rx_buf) ipcr |= (cmd->n_rx & 0xffff); else if (cmd->tx_buf) ipcr |= (cmd->n_tx & 0xffff); /// cpsr=disableInterrupts(); do { sr = _readl(QSPI0_SR); if (sr & QSPI0_SR_BUSY) continue; fr = _readl(QSPI0_FR); if (fr & QSPI0_FR_XIP_ON) continue; /* Resume Done */ _writel(QSPI0_FR_TFF, QSPI0_FR); /* trigger command */ _writel(ipcr, QSPI0_IPCR); fr = _readl(QSPI0_FR); fr &= QSPI0_FR_IPAEF | QSPI0_FR_IPIEF | QSPI0_FR_IPGEF; if (!fr) break; _writel(fr, QSPI0_FR); } while(1); if (host->use_intr) { qspi_enable_interrupt(host); qspi_cmd_done_interrupt(host); } else { if (host->use_dma) { qspi_wait_cmd_done(host); qspi_disable_dma(host); host->bytes_left = 0; } else { qspi_cmd_done_pio(host); } } //#ifndef CONFIG_XIP // if (cmd->flag & RST_AHB_DOMAIN) { // qspi_reset(); // } //#endif host->cmd = NULL; return 0; } int qspi_start_cmd(struct spi_flash_chip *chip, struct spi_flash_cmd *cmd) { #if 0 struct spi_flash_cmd_cfg *cmd_cfg = cmd->cmd_cfg; int ret; do { ret = __qspi_start_cmd(chip, cmd); if (!ret || (ret != EAGAIN && ret != ETIMEDOUT)) break; uart_printf("cmd%d error %d, retry...\r\n", cmd_cfg->opcode,ret); } while(1); return ret; #else struct spi_flash_cmd_cfg *cmd_cfg = cmd->cmd_cfg; int ret; ret = __qspi_start_cmd(chip, cmd); return ret; #endif }