On 6/29/2017 12:46 PM, Abhishek Sahu wrote: > 1. Add the function for command descriptor preparation which > will be used only by BAM DMA and it will form the DMA descriptors > containing command elements. > > 2. Add the data descriptor preparation function which will be used > only by BAM DMA for forming the data SGL’s. > > 3. Add clear BAM transaction and call it before every new request > > 4. Check DMA mode for ADM or BAM and call the appropriate > descriptor formation function. > > 5. Enable the BAM in NAND_CTRL. > Should this patch be patch #8 and then add other support ? Regards, Sricharan > Signed-off-by: Abhishek Sahu <absahu@xxxxxxxxxxxxxx> > --- > drivers/mtd/nand/qcom_nandc.c | 190 +++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 180 insertions(+), 10 deletions(-) > > diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c > index 17766af..4c6e594 100644 > --- a/drivers/mtd/nand/qcom_nandc.c > +++ b/drivers/mtd/nand/qcom_nandc.c > @@ -156,6 +156,8 @@ > #define FETCH_ID 0xb > #define RESET_DEVICE 0xd > > +/* NAND_CTRL bits */ > +#define BAM_MODE_EN BIT(0) > /* > * the NAND controller performs reads/writes with ECC in 516 byte chunks. > * the driver calls the chunks 'step' or 'codeword' interchangeably > @@ -190,6 +192,14 @@ > */ > #define NAND_ERASED_CW_SET (0x0008) > > +/* Returns the dma address for reg read buffer */ > +#define REG_BUF_DMA_ADDR(chip, vaddr) \ > + ((chip)->reg_read_buf_phys + \ > + ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf)) > + > +/* Returns the NAND register physical address */ > +#define NAND_REG_PHYS(chip, offset) ((chip)->base_phys + (offset)) > + > #define QPIC_PER_CW_MAX_CMD_ELEMENTS (32) > #define QPIC_PER_CW_MAX_CMD_SGL (32) > #define QPIC_PER_CW_MAX_DATA_SGL (8) > @@ -287,7 +297,8 @@ struct nandc_regs { > * controller > * @dev: parent device > * @base: MMIO base > - * @base_dma: physical base address of controller registers > + * @base_phys: physical base address of controller registers > + * @base_dma: dma base address of controller registers > * @core_clk: controller clock > * @aon_clk: another controller clock > * > @@ -323,6 +334,7 @@ struct qcom_nand_controller { > struct device *dev; > > void __iomem *base; > + phys_addr_t base_phys; > dma_addr_t base_dma; > > struct clk *core_clk; > @@ -467,6 +479,29 @@ static void free_bam_transaction(struct qcom_nand_controller *nandc) > return bam_txn; > } > > +/* Clears the BAM transaction indexes */ > +static void clear_bam_transaction(struct qcom_nand_controller *nandc) > +{ > + struct bam_transaction *bam_txn = nandc->bam_txn; > + > + if (!nandc->dma_bam_enabled) > + return; > + > + bam_txn->bam_ce_pos = 0; > + bam_txn->bam_ce_start = 0; > + bam_txn->cmd_sgl_pos = 0; > + bam_txn->cmd_sgl_start = 0; > + bam_txn->tx_sgl_pos = 0; > + bam_txn->tx_sgl_start = 0; > + bam_txn->rx_sgl_pos = 0; > + bam_txn->rx_sgl_start = 0; > + > + sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * > + QPIC_PER_CW_MAX_CMD_SGL); > + sg_init_table(bam_txn->data_sg, nandc->max_cwperpage * > + QPIC_PER_CW_MAX_DATA_SGL); > +} > + > static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) > { > return container_of(chip, struct qcom_nand_host, chip); > @@ -682,6 +717,102 @@ static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, > return 0; > } > > +/* > + * Prepares the command descriptor for BAM DMA which will be used for NAND > + * register reads and writes. The command descriptor requires the command > + * to be formed in command element type so this function uses the command > + * element from bam transaction ce array and fills the same with required > + * data. A single SGL can contain multiple command elements so > + * NAND_BAM_NEXT_SGL will be used for starting the separate SGL > + * after the current command element. > + */ > +static int prep_dma_desc_command(struct qcom_nand_controller *nandc, bool read, > + int reg_off, const void *vaddr, > + int size, unsigned int flags) > +{ > + int bam_ce_size; > + int i, ret; > + struct bam_cmd_element *bam_ce_buffer; > + struct bam_transaction *bam_txn = nandc->bam_txn; > + > + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; > + > + /* fill the command desc */ > + for (i = 0; i < size; i++) { > + if (read) > + bam_prep_ce(&bam_ce_buffer[i], > + NAND_REG_PHYS(nandc, reg_off + 4 * i), > + BAM_READ_COMMAND, > + REG_BUF_DMA_ADDR(nandc, > + (__le32 *)vaddr + i)); > + else > + bam_prep_ce_le32(&bam_ce_buffer[i], > + NAND_REG_PHYS(nandc, reg_off + 4 * i), > + BAM_WRITE_COMMAND, > + *((__le32 *)vaddr + i)); > + } > + > + bam_txn->bam_ce_pos += size; > + > + /* use the separate sgl after this command */ > + if (flags & NAND_BAM_NEXT_SGL) { > + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; > + bam_ce_size = (bam_txn->bam_ce_pos - > + bam_txn->bam_ce_start) * > + sizeof(struct bam_cmd_element); > + sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], > + bam_ce_buffer, bam_ce_size); > + bam_txn->cmd_sgl_pos++; > + bam_txn->bam_ce_start = bam_txn->bam_ce_pos; > + > + if (flags & NAND_BAM_NWD) { > + ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, > + DMA_PREP_FENCE | > + DMA_PREP_CMD); > + if (ret) > + return ret; > + } > + } > + > + return 0; > +} > + > +/* > + * Prepares the data descriptor for BAM DMA which will be used for NAND > + * data reads and writes. > + */ > +static int prep_dma_desc_data_bam(struct qcom_nand_controller *nandc, bool read, > + int reg_off, const void *vaddr, > + int size, unsigned int flags) > +{ > + int ret; > + struct bam_transaction *bam_txn = nandc->bam_txn; > + > + if (read) { > + sg_set_buf(&bam_txn->data_sg[bam_txn->rx_sgl_pos], > + vaddr, size); > + bam_txn->rx_sgl_pos++; > + } else { > + sg_set_buf(&bam_txn->data_sg[bam_txn->tx_sgl_pos], > + vaddr, size); > + bam_txn->tx_sgl_pos++; > + > + /* > + * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag > + * is not set, form the DMA descriptor > + */ > + if (!(flags & NAND_BAM_NO_EOT)) { > + ret = prepare_bam_async_desc(nandc, nandc->tx_chan, > + DMA_PREP_INTERRUPT); > + if (ret) > + return ret; > + } > + } > + > + return 0; > +} > + > +/* Prepares the dma descriptor for adm dma engine */ > static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read, > int reg_off, const void *vaddr, int size, > bool flow_control) > @@ -764,16 +895,19 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first, > { > bool flow_control = false; > void *vaddr; > - int size; > > if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) > flow_control = true; > > - size = num_regs * sizeof(u32); > vaddr = nandc->reg_read_buf + nandc->reg_read_pos; > nandc->reg_read_pos += num_regs; > > - return prep_dma_desc(nandc, true, first, vaddr, size, flow_control); > + if (nandc->dma_bam_enabled) > + return prep_dma_desc_command(nandc, true, first, vaddr, > + num_regs, flags); > + > + return prep_dma_desc(nandc, true, first, vaddr, num_regs * sizeof(u32), > + flow_control); > } > > /* > @@ -789,7 +923,6 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, > bool flow_control = false; > struct nandc_regs *regs = nandc->regs; > void *vaddr; > - int size; > > vaddr = offset_to_nandc_reg(regs, first); > > @@ -812,9 +945,12 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, > if (first == NAND_DEV_CMD_VLD_RESTORE) > first = NAND_DEV_CMD_VLD; > > - size = num_regs * sizeof(u32); > + if (nandc->dma_bam_enabled) > + return prep_dma_desc_command(nandc, false, first, vaddr, > + num_regs, flags); > > - return prep_dma_desc(nandc, false, first, vaddr, size, flow_control); > + return prep_dma_desc(nandc, false, first, vaddr, num_regs * sizeof(u32), > + flow_control); > } > > /* > @@ -828,6 +964,10 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, > static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, > const u8 *vaddr, int size, unsigned int flags) > { > + if (nandc->dma_bam_enabled) > + return prep_dma_desc_data_bam(nandc, true, reg_off, vaddr, size, > + flags); > + > return prep_dma_desc(nandc, true, reg_off, vaddr, size, false); > } > > @@ -842,6 +982,10 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, > static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, > const u8 *vaddr, int size, unsigned int flags) > { > + if (nandc->dma_bam_enabled) > + return prep_dma_desc_data_bam(nandc, false, reg_off, vaddr, > + size, flags); > + > return prep_dma_desc(nandc, false, reg_off, vaddr, size, false); > } > > @@ -931,6 +1075,8 @@ static int nandc_param(struct qcom_nand_host *host) > struct nand_chip *chip = &host->chip; > struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > > + clear_bam_transaction(nandc); > + > /* > * NAND_CMD_PARAM is called before we know much about the FLASH chip > * in use. we configure the controller to perform a raw read of 512 > @@ -993,6 +1139,8 @@ static int erase_block(struct qcom_nand_host *host, int page_addr) > struct nand_chip *chip = &host->chip; > struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > > + clear_bam_transaction(nandc); > + > nandc_set_reg(nandc, NAND_FLASH_CMD, > BLOCK_ERASE | PAGE_ACC | LAST_PAGE); > nandc_set_reg(nandc, NAND_ADDR0, page_addr); > @@ -1025,10 +1173,13 @@ static int read_id(struct qcom_nand_host *host, int column) > if (column == -1) > return 0; > > + clear_bam_transaction(nandc); > + > nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID); > nandc_set_reg(nandc, NAND_ADDR0, column); > nandc_set_reg(nandc, NAND_ADDR1, 0); > - nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); > + nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, > + nandc->dma_bam_enabled ? 0 : DM_EN); > nandc_set_reg(nandc, NAND_EXEC_CMD, 1); > > write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); > @@ -1045,6 +1196,8 @@ static int reset(struct qcom_nand_host *host) > struct nand_chip *chip = &host->chip; > struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > > + clear_bam_transaction(nandc); > + > nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE); > nandc_set_reg(nandc, NAND_EXEC_CMD, 1); > > @@ -1561,6 +1714,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, > data_buf = buf; > oob_buf = oob_required ? chip->oob_poi : NULL; > > + clear_bam_transaction(nandc); > ret = read_page_ecc(host, data_buf, oob_buf); > if (ret) { > dev_err(nandc->dev, "failure to read page\n"); > @@ -1585,6 +1739,8 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, > oob_buf = chip->oob_poi; > > host->use_ecc = false; > + > + clear_bam_transaction(nandc); > update_rw_regs(host, ecc->steps, true); > > for (i = 0; i < ecc->steps; i++) { > @@ -1641,6 +1797,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, > int ret; > > clear_read_regs(nandc); > + clear_bam_transaction(nandc); > > host->use_ecc = true; > set_address(host, 0, page); > @@ -1664,6 +1821,7 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, > int i, ret; > > clear_read_regs(nandc); > + clear_bam_transaction(nandc); > > data_buf = (u8 *)buf; > oob_buf = chip->oob_poi; > @@ -1729,6 +1887,7 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, > int i, ret; > > clear_read_regs(nandc); > + clear_bam_transaction(nandc); > > data_buf = (u8 *)buf; > oob_buf = chip->oob_poi; > @@ -1803,6 +1962,7 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, > > host->use_ecc = true; > > + clear_bam_transaction(nandc); > ret = copy_last_cw(host, page); > if (ret) > return ret; > @@ -1860,6 +2020,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) > */ > host->use_ecc = false; > > + clear_bam_transaction(nandc); > ret = copy_last_cw(host, page); > if (ret) > goto err; > @@ -1890,6 +2051,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) > int page, ret, status = 0; > > clear_read_regs(nandc); > + clear_bam_transaction(nandc); > > /* > * to mark the BBM as bad, we flash the entire last codeword with 0s. > @@ -2396,11 +2558,18 @@ static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) > /* one time setup of a few nand controller registers */ > static int qcom_nandc_setup(struct qcom_nand_controller *nandc) > { > + u32 nand_ctrl; > + > /* kill onenand */ > nandc_write(nandc, SFLASHC_BURST_CFG, 0); > > - /* enable ADM DMA */ > - nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); > + /* enable ADM or BAM DMA */ > + if (!nandc->dma_bam_enabled) { > + nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); > + } else { > + nand_ctrl = nandc_read(nandc, NAND_CTRL); > + nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); > + } > > /* save the original values of these registers */ > nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1); > @@ -2592,6 +2761,7 @@ static int qcom_nandc_probe(struct platform_device *pdev) > if (IS_ERR(nandc->base)) > return PTR_ERR(nandc->base); > > + nandc->base_phys = res->start; > nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start); > > nandc->core_clk = devm_clk_get(dev, "core"); > -- "QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation --- This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html