Message ID | 20230531124953.21007-2-quic_mdalam@quicinc.com |
---|---|
State | Superseded |
Headers | show |
Series | [v3,1/5] mtd: rawnand: qcom: Implement exec_op() | expand |
Hello, quic_mdalam@quicinc.com wrote on Wed, 31 May 2023 18:19:50 +0530: > This change will add exec_ops support for RESET , READ_ID, STATUS > command. Imperative form here and after in your commit log please. Add support for... Minor comments below, otherwise looks good. > > Co-developed-by: Sricharan Ramabadhran <quic_srichara@quicinc.com> > Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com> > Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com> > --- > Change in [v3] > > * Updated q_op->flag in qcom_op_cmd_mapping() with OP_PROGRAM_PAGE > instead NAND_CMD_PAGEPROG > > * Change the if condition check for exec_opwrite into single line check. > > * Added error check > > * Removed check for NAND_CMD_RESET, NAND_CMD_READID from pre_command. > > Change in [v2] > > * Missed to post Cover-letter, so posting v2 patch with cover-letter > > Change in [v1] > > * Added initial support for exec_ops. > > drivers/mtd/nand/raw/qcom_nandc.c | 218 +++++++++++++++++++++++++++++- > 1 file changed, 215 insertions(+), 3 deletions(-) > > diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c > index 6a83cd86a12d..659ba923b68a 100644 > --- a/drivers/mtd/nand/raw/qcom_nandc.c > +++ b/drivers/mtd/nand/raw/qcom_nandc.c > @@ -385,6 +385,9 @@ struct nandc_regs { > * @reg_read_pos: marker for data read in reg_read_buf > * > * @cmd1/vld: some fixed controller register values > + * > + * @exec_opwrite: flag to select correct number of code word > + * while reading status > */ > struct qcom_nand_controller { > struct device *dev; > @@ -435,6 +438,7 @@ struct qcom_nand_controller { > int reg_read_pos; > > u32 cmd1, vld; > + bool exec_opwrite; > }; > > /* > @@ -1542,8 +1546,7 @@ static void pre_command(struct qcom_nand_host *host, int command) > > clear_read_regs(nandc); > > - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || > - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) > + if (command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) > clear_bam_transaction(nandc); > } > > @@ -2920,6 +2923,8 @@ static int qcom_op_cmd_mapping(struct qcom_nand_controller *nandc, u8 cmd, > break; > case NAND_CMD_PAGEPROG: > ret = OP_PROGRAM_PAGE; > + q_op->flag = OP_PROGRAM_PAGE; > + nandc->exec_opwrite = true; > break; > default: > break; > @@ -2982,6 +2987,212 @@ static void qcom_parse_instructions(struct nand_chip *chip, > } > } > > +static void qcom_delay_ns(unsigned int ns) > +{ > + if (!ns) > + return; > + > + if (ns < 10000) > + ndelay(ns); > + else > + udelay(DIV_ROUND_UP(ns, 1000)); > +} > + > +static int qcom_wait_rdy_poll(struct nand_chip *chip, unsigned int time_ms) > +{ > + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > + unsigned long start = jiffies + msecs_to_jiffies(time_ms); > + u32 flash; > + > + nandc_read_buffer_sync(nandc, true); > + > + do { > + flash = le32_to_cpu(nandc->reg_read_buf[0]); > + if (flash & FS_READY_BSY_N) > + return 0; > + cpu_relax(); > + } while (time_after(start, jiffies)); > + > + dev_err(nandc->dev, "Timeout waiting for device to be ready:0x%08x\n", flash); > + > + return -ETIMEDOUT; > +} > + > +static int qcom_read_status_exec(struct nand_chip *chip, > + const struct nand_subop *subop) > +{ > + struct qcom_nand_host *host = to_qcom_nand_host(chip); > + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > + struct nand_ecc_ctrl *ecc = &chip->ecc; > + struct qcom_op q_op; > + const struct nand_op_instr *instr = NULL; > + unsigned int op_id = 0; > + unsigned int len = 0; > + int ret = 0, num_cw, i; > + u32 flash_status; > + > + host->status = NAND_STATUS_READY | NAND_STATUS_WP; > + > + qcom_parse_instructions(chip, subop, &q_op); > + > + num_cw = nandc->exec_opwrite ? ecc->steps : 1; > + nandc->exec_opwrite = false; > + > + nandc->buf_count = 0; > + nandc->buf_start = 0; > + host->use_ecc = false; > + > + clear_read_regs(nandc); > + clear_bam_transaction(nandc); > + > + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); > + nandc_set_reg(chip, NAND_EXEC_CMD, 1); > + > + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); > + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); > + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); > + > + ret = submit_descs(nandc); > + if (ret) { > + dev_err(nandc->dev, "failure in sbumitting status descriptor\n"); ^ Please run checkpatch.pl --strict > + free_descs(nandc); > + goto err_out; > + } > + free_descs(nandc); > + > + nandc_read_buffer_sync(nandc, true); > + > + for (i = 0; i < num_cw; i++) { > + flash_status = le32_to_cpu(nandc->reg_read_buf[i]); > + > + if (flash_status & FS_MPU_ERR) > + host->status &= ~NAND_STATUS_WP; > + > + if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && > + (flash_status & FS_DEVICE_STS_ERR))) if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && (flash_status & FS_DEVICE_STS_ERR))) looks more readable? > + host->status |= NAND_STATUS_FAIL; > + } > + > + flash_status = host->status; > + instr = q_op.data_instr; > + op_id = q_op.data_instr_idx; > + len = nand_subop_get_data_len(subop, op_id); > + memcpy(instr->ctx.data.buf.in, &flash_status, len); > + > +err_out: > + return ret; > +} > + > +static int qcom_read_id_type_exec(struct nand_chip *chip, const struct nand_subop *subop) > +{ > + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > + struct qcom_nand_host *host = to_qcom_nand_host(chip); > + struct qcom_op q_op; > + const struct nand_op_instr *instr = NULL; > + unsigned int op_id = 0; > + unsigned int len = 0; > + int ret = 0; > + > + qcom_parse_instructions(chip, subop, &q_op); > + > + nandc->buf_count = 0; > + nandc->buf_start = 0; > + host->use_ecc = false; > + > + clear_read_regs(nandc); > + clear_bam_transaction(nandc); > + > + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); > + nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); > + nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); > + nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT, > + nandc->props->is_bam ? 0 : DM_EN); > + > + nandc_set_reg(chip, NAND_EXEC_CMD, 1); > + > + write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); > + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); > + > + read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); > + > + ret = submit_descs(nandc); > + if (ret) { > + dev_err(nandc->dev, "failure in sbumitting read id descriptor\n"); > + free_descs(nandc); > + goto err_out; > + } > + free_descs(nandc); > + > + instr = q_op.data_instr; > + op_id = q_op.data_instr_idx; > + len = nand_subop_get_data_len(subop, op_id); > + > + nandc_read_buffer_sync(nandc, true); > + memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len); > + > +err_out: > + return ret; > +} > + > +static int qcom_misc_cmd_type_exec(struct nand_chip *chip, const struct nand_subop *subop) > +{ > + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > + struct qcom_nand_host *host = to_qcom_nand_host(chip); > + struct qcom_op q_op; > + int ret = 0; > + > + qcom_parse_instructions(chip, subop, &q_op); > + > + if (q_op.flag == OP_PROGRAM_PAGE) > + goto wait_rdy; > + > + nandc->buf_count = 0; > + nandc->buf_start = 0; > + host->use_ecc = false; > + > + clear_read_regs(nandc); > + clear_bam_transaction(nandc); > + > + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); > + nandc_set_reg(chip, NAND_EXEC_CMD, 1); > + > + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); > + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); > + > + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); > + > + ret = submit_descs(nandc); > + if (ret) { > + dev_err(nandc->dev, "failure in sbumitting misc descriptor\n"); > + free_descs(nandc); > + goto err_out; > + } > + free_descs(nandc); > + > +wait_rdy: > + qcom_delay_ns(q_op.rdy_delay_ns); > + ret = qcom_wait_rdy_poll(chip, q_op.rdy_timeout_ms); > + > +err_out: > + return ret; > +} > + > +static const struct nand_op_parser qcom_op_parser = NAND_OP_PARSER( > + NAND_OP_PARSER_PATTERN( > + qcom_misc_cmd_type_exec, > + NAND_OP_PARSER_PAT_CMD_ELEM(false), > + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), > + NAND_OP_PARSER_PATTERN( > + qcom_read_id_type_exec, > + NAND_OP_PARSER_PAT_CMD_ELEM(false), > + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYCLE), > + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)), > + NAND_OP_PARSER_PATTERN( > + qcom_read_status_exec, > + NAND_OP_PARSER_PAT_CMD_ELEM(false), > + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)), > + ); > + > static int qcom_check_op(struct nand_chip *chip, > const struct nand_operation *op) > { > @@ -3022,7 +3233,8 @@ static int qcom_nand_exec_op(struct nand_chip *chip, > if (check_only) > return qcom_check_op(chip, op); > > - return 0; > + return nand_op_parser_exec_op(chip, &qcom_op_parser, > + op, check_only); > } > > static const struct nand_controller_ops qcom_nandc_ops = { Thanks, Miquèl
On 6/8/2023 7:28 PM, Miquel Raynal wrote: > Hello, > > quic_mdalam@quicinc.com wrote on Wed, 31 May 2023 18:19:50 +0530: > >> This change will add exec_ops support for RESET , READ_ID, STATUS >> command. > > Imperative form here and after in your commit log please. > > Add support for... > > Minor comments below, otherwise looks good. Thanks, I will update commit message in all the patches in next patch V4. > >> >> Co-developed-by: Sricharan Ramabadhran <quic_srichara@quicinc.com> >> Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com> >> Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com> >> --- >> Change in [v3] >> >> * Updated q_op->flag in qcom_op_cmd_mapping() with OP_PROGRAM_PAGE >> instead NAND_CMD_PAGEPROG >> >> * Change the if condition check for exec_opwrite into single line check. >> >> * Added error check >> >> * Removed check for NAND_CMD_RESET, NAND_CMD_READID from pre_command. >> >> Change in [v2] >> >> * Missed to post Cover-letter, so posting v2 patch with cover-letter >> >> Change in [v1] >> >> * Added initial support for exec_ops. >> >> drivers/mtd/nand/raw/qcom_nandc.c | 218 +++++++++++++++++++++++++++++- >> 1 file changed, 215 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c >> index 6a83cd86a12d..659ba923b68a 100644 >> --- a/drivers/mtd/nand/raw/qcom_nandc.c >> +++ b/drivers/mtd/nand/raw/qcom_nandc.c >> @@ -385,6 +385,9 @@ struct nandc_regs { >> * @reg_read_pos: marker for data read in reg_read_buf >> * >> * @cmd1/vld: some fixed controller register values >> + * >> + * @exec_opwrite: flag to select correct number of code word >> + * while reading status >> */ >> struct qcom_nand_controller { >> struct device *dev; >> @@ -435,6 +438,7 @@ struct qcom_nand_controller { >> int reg_read_pos; >> >> u32 cmd1, vld; >> + bool exec_opwrite; >> }; >> >> /* >> @@ -1542,8 +1546,7 @@ static void pre_command(struct qcom_nand_host *host, int command) >> >> clear_read_regs(nandc); >> >> - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || >> - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) >> + if (command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) >> clear_bam_transaction(nandc); >> } >> >> @@ -2920,6 +2923,8 @@ static int qcom_op_cmd_mapping(struct qcom_nand_controller *nandc, u8 cmd, >> break; >> case NAND_CMD_PAGEPROG: >> ret = OP_PROGRAM_PAGE; >> + q_op->flag = OP_PROGRAM_PAGE; >> + nandc->exec_opwrite = true; >> break; >> default: >> break; >> @@ -2982,6 +2987,212 @@ static void qcom_parse_instructions(struct nand_chip *chip, >> } >> } >> >> +static void qcom_delay_ns(unsigned int ns) >> +{ >> + if (!ns) >> + return; >> + >> + if (ns < 10000) >> + ndelay(ns); >> + else >> + udelay(DIV_ROUND_UP(ns, 1000)); >> +} >> + >> +static int qcom_wait_rdy_poll(struct nand_chip *chip, unsigned int time_ms) >> +{ >> + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); >> + unsigned long start = jiffies + msecs_to_jiffies(time_ms); >> + u32 flash; >> + >> + nandc_read_buffer_sync(nandc, true); >> + >> + do { >> + flash = le32_to_cpu(nandc->reg_read_buf[0]); >> + if (flash & FS_READY_BSY_N) >> + return 0; >> + cpu_relax(); >> + } while (time_after(start, jiffies)); >> + >> + dev_err(nandc->dev, "Timeout waiting for device to be ready:0x%08x\n", flash); >> + >> + return -ETIMEDOUT; >> +} >> + >> +static int qcom_read_status_exec(struct nand_chip *chip, >> + const struct nand_subop *subop) >> +{ >> + struct qcom_nand_host *host = to_qcom_nand_host(chip); >> + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); >> + struct nand_ecc_ctrl *ecc = &chip->ecc; >> + struct qcom_op q_op; >> + const struct nand_op_instr *instr = NULL; >> + unsigned int op_id = 0; >> + unsigned int len = 0; >> + int ret = 0, num_cw, i; >> + u32 flash_status; >> + >> + host->status = NAND_STATUS_READY | NAND_STATUS_WP; >> + >> + qcom_parse_instructions(chip, subop, &q_op); >> + >> + num_cw = nandc->exec_opwrite ? ecc->steps : 1; >> + nandc->exec_opwrite = false; >> + >> + nandc->buf_count = 0; >> + nandc->buf_start = 0; >> + host->use_ecc = false; >> + >> + clear_read_regs(nandc); >> + clear_bam_transaction(nandc); >> + >> + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); >> + nandc_set_reg(chip, NAND_EXEC_CMD, 1); >> + >> + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); >> + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); >> + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); >> + >> + ret = submit_descs(nandc); >> + if (ret) { >> + dev_err(nandc->dev, "failure in sbumitting status descriptor\n"); > > ^ > > Please run checkpatch.pl --strict Thanks for pointing this, its spelling mistake. I will fix this in next patch V4. > >> + free_descs(nandc); >> + goto err_out; >> + } >> + free_descs(nandc); >> + >> + nandc_read_buffer_sync(nandc, true); >> + >> + for (i = 0; i < num_cw; i++) { >> + flash_status = le32_to_cpu(nandc->reg_read_buf[i]); >> + >> + if (flash_status & FS_MPU_ERR) >> + host->status &= ~NAND_STATUS_WP; >> + >> + if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && >> + (flash_status & FS_DEVICE_STS_ERR))) > > if (flash_status & FS_OP_ERR || > (i == (num_cw - 1) && (flash_status & FS_DEVICE_STS_ERR))) > > looks more readable? Yes you are right. I will fix this in next patch V4. > >> + host->status |= NAND_STATUS_FAIL; >> + } >> + >> + flash_status = host->status; >> + instr = q_op.data_instr; >> + op_id = q_op.data_instr_idx; >> + len = nand_subop_get_data_len(subop, op_id); >> + memcpy(instr->ctx.data.buf.in, &flash_status, len); >> + >> +err_out: >> + return ret; >> +} >> + >> +static int qcom_read_id_type_exec(struct nand_chip *chip, const struct nand_subop *subop) >> +{ >> + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); >> + struct qcom_nand_host *host = to_qcom_nand_host(chip); >> + struct qcom_op q_op; >> + const struct nand_op_instr *instr = NULL; >> + unsigned int op_id = 0; >> + unsigned int len = 0; >> + int ret = 0; >> + >> + qcom_parse_instructions(chip, subop, &q_op); >> + >> + nandc->buf_count = 0; >> + nandc->buf_start = 0; >> + host->use_ecc = false; >> + >> + clear_read_regs(nandc); >> + clear_bam_transaction(nandc); >> + >> + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); >> + nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); >> + nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); >> + nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT, >> + nandc->props->is_bam ? 0 : DM_EN); >> + >> + nandc_set_reg(chip, NAND_EXEC_CMD, 1); >> + >> + write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); >> + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); >> + >> + read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); >> + >> + ret = submit_descs(nandc); >> + if (ret) { >> + dev_err(nandc->dev, "failure in sbumitting read id descriptor\n"); >> + free_descs(nandc); >> + goto err_out; >> + } >> + free_descs(nandc); >> + >> + instr = q_op.data_instr; >> + op_id = q_op.data_instr_idx; >> + len = nand_subop_get_data_len(subop, op_id); >> + >> + nandc_read_buffer_sync(nandc, true); >> + memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len); >> + >> +err_out: >> + return ret; >> +} >> + >> +static int qcom_misc_cmd_type_exec(struct nand_chip *chip, const struct nand_subop *subop) >> +{ >> + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); >> + struct qcom_nand_host *host = to_qcom_nand_host(chip); >> + struct qcom_op q_op; >> + int ret = 0; >> + >> + qcom_parse_instructions(chip, subop, &q_op); >> + >> + if (q_op.flag == OP_PROGRAM_PAGE) >> + goto wait_rdy; >> + >> + nandc->buf_count = 0; >> + nandc->buf_start = 0; >> + host->use_ecc = false; >> + >> + clear_read_regs(nandc); >> + clear_bam_transaction(nandc); >> + >> + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); >> + nandc_set_reg(chip, NAND_EXEC_CMD, 1); >> + >> + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); >> + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); >> + >> + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); >> + >> + ret = submit_descs(nandc); >> + if (ret) { >> + dev_err(nandc->dev, "failure in sbumitting misc descriptor\n"); >> + free_descs(nandc); >> + goto err_out; >> + } >> + free_descs(nandc); >> + >> +wait_rdy: >> + qcom_delay_ns(q_op.rdy_delay_ns); >> + ret = qcom_wait_rdy_poll(chip, q_op.rdy_timeout_ms); >> + >> +err_out: >> + return ret; >> +} >> + >> +static const struct nand_op_parser qcom_op_parser = NAND_OP_PARSER( >> + NAND_OP_PARSER_PATTERN( >> + qcom_misc_cmd_type_exec, >> + NAND_OP_PARSER_PAT_CMD_ELEM(false), >> + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), >> + NAND_OP_PARSER_PATTERN( >> + qcom_read_id_type_exec, >> + NAND_OP_PARSER_PAT_CMD_ELEM(false), >> + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYCLE), >> + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)), >> + NAND_OP_PARSER_PATTERN( >> + qcom_read_status_exec, >> + NAND_OP_PARSER_PAT_CMD_ELEM(false), >> + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)), >> + ); >> + >> static int qcom_check_op(struct nand_chip *chip, >> const struct nand_operation *op) >> { >> @@ -3022,7 +3233,8 @@ static int qcom_nand_exec_op(struct nand_chip *chip, >> if (check_only) >> return qcom_check_op(chip, op); >> >> - return 0; >> + return nand_op_parser_exec_op(chip, &qcom_op_parser, >> + op, check_only); >> } >> >> static const struct nand_controller_ops qcom_nandc_ops = { > > > Thanks, > Miquèl
diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 6a83cd86a12d..659ba923b68a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -385,6 +385,9 @@ struct nandc_regs { * @reg_read_pos: marker for data read in reg_read_buf * * @cmd1/vld: some fixed controller register values + * + * @exec_opwrite: flag to select correct number of code word + * while reading status */ struct qcom_nand_controller { struct device *dev; @@ -435,6 +438,7 @@ struct qcom_nand_controller { int reg_read_pos; u32 cmd1, vld; + bool exec_opwrite; }; /* @@ -1542,8 +1546,7 @@ static void pre_command(struct qcom_nand_host *host, int command) clear_read_regs(nandc); - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) + if (command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) clear_bam_transaction(nandc); } @@ -2920,6 +2923,8 @@ static int qcom_op_cmd_mapping(struct qcom_nand_controller *nandc, u8 cmd, break; case NAND_CMD_PAGEPROG: ret = OP_PROGRAM_PAGE; + q_op->flag = OP_PROGRAM_PAGE; + nandc->exec_opwrite = true; break; default: break; @@ -2982,6 +2987,212 @@ static void qcom_parse_instructions(struct nand_chip *chip, } } +static void qcom_delay_ns(unsigned int ns) +{ + if (!ns) + return; + + if (ns < 10000) + ndelay(ns); + else + udelay(DIV_ROUND_UP(ns, 1000)); +} + +static int qcom_wait_rdy_poll(struct nand_chip *chip, unsigned int time_ms) +{ + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + unsigned long start = jiffies + msecs_to_jiffies(time_ms); + u32 flash; + + nandc_read_buffer_sync(nandc, true); + + do { + flash = le32_to_cpu(nandc->reg_read_buf[0]); + if (flash & FS_READY_BSY_N) + return 0; + cpu_relax(); + } while (time_after(start, jiffies)); + + dev_err(nandc->dev, "Timeout waiting for device to be ready:0x%08x\n", flash); + + return -ETIMEDOUT; +} + +static int qcom_read_status_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct qcom_op q_op; + const struct nand_op_instr *instr = NULL; + unsigned int op_id = 0; + unsigned int len = 0; + int ret = 0, num_cw, i; + u32 flash_status; + + host->status = NAND_STATUS_READY | NAND_STATUS_WP; + + qcom_parse_instructions(chip, subop, &q_op); + + num_cw = nandc->exec_opwrite ? ecc->steps : 1; + nandc->exec_opwrite = false; + + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); + + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in sbumitting status descriptor\n"); + free_descs(nandc); + goto err_out; + } + free_descs(nandc); + + nandc_read_buffer_sync(nandc, true); + + for (i = 0; i < num_cw; i++) { + flash_status = le32_to_cpu(nandc->reg_read_buf[i]); + + if (flash_status & FS_MPU_ERR) + host->status &= ~NAND_STATUS_WP; + + if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && + (flash_status & FS_DEVICE_STS_ERR))) + host->status |= NAND_STATUS_FAIL; + } + + flash_status = host->status; + instr = q_op.data_instr; + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + memcpy(instr->ctx.data.buf.in, &flash_status, len); + +err_out: + return ret; +} + +static int qcom_read_id_type_exec(struct nand_chip *chip, const struct nand_subop *subop) +{ + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_op q_op; + const struct nand_op_instr *instr = NULL; + unsigned int op_id = 0; + unsigned int len = 0; + int ret = 0; + + qcom_parse_instructions(chip, subop, &q_op); + + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); + nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); + nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); + nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT, + nandc->props->is_bam ? 0 : DM_EN); + + nandc_set_reg(chip, NAND_EXEC_CMD, 1); + + write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in sbumitting read id descriptor\n"); + free_descs(nandc); + goto err_out; + } + free_descs(nandc); + + instr = q_op.data_instr; + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + + nandc_read_buffer_sync(nandc, true); + memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len); + +err_out: + return ret; +} + +static int qcom_misc_cmd_type_exec(struct nand_chip *chip, const struct nand_subop *subop) +{ + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_op q_op; + int ret = 0; + + qcom_parse_instructions(chip, subop, &q_op); + + if (q_op.flag == OP_PROGRAM_PAGE) + goto wait_rdy; + + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); + + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in sbumitting misc descriptor\n"); + free_descs(nandc); + goto err_out; + } + free_descs(nandc); + +wait_rdy: + qcom_delay_ns(q_op.rdy_delay_ns); + ret = qcom_wait_rdy_poll(chip, q_op.rdy_timeout_ms); + +err_out: + return ret; +} + +static const struct nand_op_parser qcom_op_parser = NAND_OP_PARSER( + NAND_OP_PARSER_PATTERN( + qcom_misc_cmd_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + NAND_OP_PARSER_PATTERN( + qcom_read_id_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYCLE), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)), + NAND_OP_PARSER_PATTERN( + qcom_read_status_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)), + ); + static int qcom_check_op(struct nand_chip *chip, const struct nand_operation *op) { @@ -3022,7 +3233,8 @@ static int qcom_nand_exec_op(struct nand_chip *chip, if (check_only) return qcom_check_op(chip, op); - return 0; + return nand_op_parser_exec_op(chip, &qcom_op_parser, + op, check_only); } static const struct nand_controller_ops qcom_nandc_ops = {