[v3,2/5] mtd: rawnand: qcom: Add support for reset, readid, status exec_op
Commit Message
This change will add exec_ops support for RESET , READ_ID, STATUS
command.
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(-)
Comments
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
@@ -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 = {