diff mbox series

[v1,1/5] mtd: rawnand: meson: fix NAND access for read/write

Message ID 20230412061700.1492474-2-AVKrasnov@sberdevices.ru
State Changes Requested
Delegated to: Miquel Raynal
Headers show
Series refactoring and fix for Meson NAND | expand

Commit Message

Arseniy Krasnov April 12, 2023, 6:16 a.m. UTC
This fixes read/write functionality. New command sequences were ported
from old vendor's driver. Without this patch driver works unstable. This
change is tested with 'nanddump'/'nandwrite' utilities and mounting
JFFS2 filesystem on AXG family (A113X SoC).

Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
 1 file changed, 101 insertions(+), 15 deletions(-)

Comments

Liang Yang April 12, 2023, 9:37 a.m. UTC | #1
Hi Arseniy,

Thanks for pointing out this problem. also comment inline.

On 2023/4/12 14:16, Arseniy Krasnov wrote:
> [ EXTERNAL EMAIL ]
> 
> This fixes read/write functionality. New command sequences were ported
> from old vendor's driver. Without this patch driver works unstable. This
> change is tested with 'nanddump'/'nandwrite' utilities and mounting
> JFFS2 filesystem on AXG family (A113X SoC).
> 
> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>   drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>   1 file changed, 101 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..256c37c76526 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -26,6 +26,7 @@
>   #define NFC_CMD_IDLE		(0xc << 14)
>   #define NFC_CMD_CLE		(0x5 << 14)
>   #define NFC_CMD_ALE		(0x6 << 14)
> +#define NFC_CMD_DRD		(0x8 << 14)
>   #define NFC_CMD_ADL		((0 << 16) | (3 << 20))
>   #define NFC_CMD_ADH		((1 << 16) | (3 << 20))
>   #define NFC_CMD_AIL		((2 << 16) | (3 << 20))
> @@ -84,6 +85,7 @@
>   
>   #define DMA_BUSY_TIMEOUT	0x100000
>   #define CMD_FIFO_EMPTY_TIMEOUT	1000
> +#define DEVICE_READY_TIMEOUT	1000
>   
>   #define MAX_CE_NUM		2
>   
> @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>   	}
>   }
>   
> +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
> +				     unsigned int timeout_ms)
> +{
> +	u32 cmd_size = 0;
> +	int ret;
> +
> +	/* wait cmd fifo is empty */
> +	ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
> +					 !NFC_CMD_GET_SIZE(cmd_size),
> +					 10, timeout_ms * 1000);
> +	if (ret)
> +		dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
> +
> +	return ret;
> +}
> +
>   static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>   {
> +	meson_nfc_wait_cmd_finish(nfc, 0);
> +
>   	writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>   	       nfc->reg_base + NFC_REG_CMD);
>   }
> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>   	 */
>   	meson_nfc_cmd_idle(nfc, 0);
>   	meson_nfc_cmd_idle(nfc, 0);
> +	meson_nfc_wait_cmd_finish(nfc, 1000);
>   }
>   
> -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
> -				     unsigned int timeout_ms)
> -{
> -	u32 cmd_size = 0;
> -	int ret;
> -
> -	/* wait cmd fifo is empty */
> -	ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
> -					 !NFC_CMD_GET_SIZE(cmd_size),
> -					 10, timeout_ms * 1000);
> -	if (ret)
> -		dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
> -
> -	return ret;
> -}
>   
>   static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>   {
> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>   	return 0;
>   }
>   
> +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
> +{
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +
> +	writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, nfc->timing.twb);
> +	meson_nfc_drain_cmd(nfc);
> +
> +	return readl(nfc->reg_base + NFC_REG_BUF);
> +}
> +
> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
> +{
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	u32 cs = nfc->param.chip_select;
> +	unsigned long cnt = 0;
> +
> +	meson_nfc_drain_cmd(nfc);
> +
> +	writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
> +
> +	/* 10 ms. */
> +	while (cnt < DEVICE_READY_TIMEOUT) {
> +		uint8_t status;
> +
> +		status = meson_nfc_read_byte(nand);
> +
> +		if (status & NAND_STATUS_READY)
> +			break;
> +
> +		usleep_range(10, 11);
> +		cnt++;
> +	}
> +
> +	if (cnt == DEVICE_READY_TIMEOUT) {
> +		dev_err(nfc->dev, "device ready timeout\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
>   static int meson_nfc_write_page_sub(struct nand_chip *nand,
>   				    int page, int raw)
>   {
> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>   	u32 cmd;
>   	int ret;
>   
> +	ret = meson_nfc_wait_dev_ready(nand);
> +	if (ret)
> +		return ret;
> +
>   	meson_nfc_select_chip(nand, nand->cur_cs);
>   
>   	data_len =  mtd->writesize + mtd->oobsize;
> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>   				     NFC_CMD_SCRAMBLER_DISABLE);
>   	}
>   
> +	ret = meson_nfc_wait_dma_finish(nfc);
> +	if (ret)
> +		return ret;
> +
>   	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>   	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>   	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>   
>   	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>   
> +	ret = meson_nfc_wait_dev_ready(nand);
> +	if (ret)
> +		return ret;
> +
>   	return ret;
>   }
>   
> @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>   	} while (!ret);
>   }
>   
> +static inline int meson_nfc_send_read(struct nand_chip *nand)
> +{
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	u32 cs = nfc->param.chip_select;
> +	int ret;
> +
> +	ret = meson_nfc_wait_dev_ready(nand);
> +	if (ret)
> +		return ret;
> +
> +	writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
> +
> +	return 0;
> +}
> +

it already calls meson_nfc_queue_rb() in 
meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in 
meson_nfc_queue_rb()? and we can use the irq method.
also without Ready/Busy pin, the meson_nfc_queue_rb() should change like 
below:
	......
	#define NFC_CMD_RB_INT	((0xb << 10) | BIT(18))

	meson_nfc_cmd_idle(nfc, 0);
	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
	meson_nfc_cmd_idle(nfc, 5);
	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
	writel(cmd, nfc->reg_base + NFC_REG_CMD);

	ret = wait_for_completion_timeout(&nfc->completion,
					  msecs_to_jiffies(timeout_ms));
	if (ret == 0)
		ret = -1;

	writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
	......
	
>   static int meson_nfc_read_page_sub(struct nand_chip *nand,
>   				   int page, int raw)
>   {
> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>   	data_len =  mtd->writesize + mtd->oobsize;
>   	info_len = nand->ecc.steps * PER_INFO_BYTE;
>   
> +	ret = meson_nfc_wait_dev_ready(nand);
> +	if (ret)
> +		return ret;
> +
>   	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>   	if (ret)
>   		return ret;
>   
> +	ret = meson_nfc_send_read(nand);
> +	if (ret)
> +		return ret;
> +
>   	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>   					 data_len, meson_chip->info_buf,
>   					 info_len, DMA_FROM_DEVICE);
> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>   	}
>   
>   	ret = meson_nfc_wait_dma_finish(nfc);
> +	if (ret)
> +		return ret;
> +
>   	meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>   
>   	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
Arseniy Krasnov April 12, 2023, 10:24 a.m. UTC | #2
On 12.04.2023 12:37, Liang Yang wrote:
> Hi Arseniy,
> 
> Thanks for pointing out this problem. also comment inline.
> 
> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>> [ EXTERNAL EMAIL ]
>>
>> This fixes read/write functionality. New command sequences were ported
>> from old vendor's driver. Without this patch driver works unstable. This
>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>> JFFS2 filesystem on AXG family (A113X SoC).
>>
>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>   drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>   1 file changed, 101 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 074e14225c06..256c37c76526 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -26,6 +26,7 @@
>>   #define NFC_CMD_IDLE        (0xc << 14)
>>   #define NFC_CMD_CLE        (0x5 << 14)
>>   #define NFC_CMD_ALE        (0x6 << 14)
>> +#define NFC_CMD_DRD        (0x8 << 14)
>>   #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>   #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>   #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>> @@ -84,6 +85,7 @@
>>     #define DMA_BUSY_TIMEOUT    0x100000
>>   #define CMD_FIFO_EMPTY_TIMEOUT    1000
>> +#define DEVICE_READY_TIMEOUT    1000
>>     #define MAX_CE_NUM        2
>>   @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>       }
>>   }
>>   +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>> +                     unsigned int timeout_ms)
>> +{
>> +    u32 cmd_size = 0;
>> +    int ret;
>> +
>> +    /* wait cmd fifo is empty */
>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>> +                     10, timeout_ms * 1000);
>> +    if (ret)
>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>> +
>> +    return ret;
>> +}
>> +
>>   static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>   {
>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>> +
>>       writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>              nfc->reg_base + NFC_REG_CMD);
>>   }
>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>        */
>>       meson_nfc_cmd_idle(nfc, 0);
>>       meson_nfc_cmd_idle(nfc, 0);
>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>   }
>>   -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>> -                     unsigned int timeout_ms)
>> -{
>> -    u32 cmd_size = 0;
>> -    int ret;
>> -
>> -    /* wait cmd fifo is empty */
>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>> -                     10, timeout_ms * 1000);
>> -    if (ret)
>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>> -
>> -    return ret;
>> -}
>>     static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>   {
>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>       return 0;
>>   }
>>   +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>> +{
>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +
>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>> +    meson_nfc_drain_cmd(nfc);
>> +
>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>> +}
>> +
>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>> +{
>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +    u32 cs = nfc->param.chip_select;
>> +    unsigned long cnt = 0;
>> +
>> +    meson_nfc_drain_cmd(nfc);
>> +
>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>> +
>> +    /* 10 ms. */
>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>> +        uint8_t status;
>> +
>> +        status = meson_nfc_read_byte(nand);
>> +
>> +        if (status & NAND_STATUS_READY)
>> +            break;
>> +
>> +        usleep_range(10, 11);
>> +        cnt++;
>> +    }
>> +
>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>> +        dev_err(nfc->dev, "device ready timeout\n");
>> +        return -ETIMEDOUT;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>   static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>                       int page, int raw)
>>   {
>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>       u32 cmd;
>>       int ret;
>>   +    ret = meson_nfc_wait_dev_ready(nand);
>> +    if (ret)
>> +        return ret;
>> +
>>       meson_nfc_select_chip(nand, nand->cur_cs);
>>         data_len =  mtd->writesize + mtd->oobsize;
>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>                        NFC_CMD_SCRAMBLER_DISABLE);
>>       }
>>   +    ret = meson_nfc_wait_dma_finish(nfc);
>> +    if (ret)
>> +        return ret;
>> +
>>       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>         meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>   +    ret = meson_nfc_wait_dev_ready(nand);
>> +    if (ret)
>> +        return ret;
>> +
>>       return ret;
>>   }
>>   @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>       } while (!ret);
>>   }
>>   +static inline int meson_nfc_send_read(struct nand_chip *nand)
>> +{
>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +    u32 cs = nfc->param.chip_select;
>> +    int ret;
>> +
>> +    ret = meson_nfc_wait_dev_ready(nand);
>> +    if (ret)
>> +        return ret;
>> +
>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>> +
>> +    return 0;
>> +}
>> +
> 
> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>     ......
>     #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
> 
>     meson_nfc_cmd_idle(nfc, 0);
>     cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>     writel(cmd, nfc->reg_base + NFC_REG_CMD);
>     meson_nfc_cmd_idle(nfc, 5);
>     cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>     writel(cmd, nfc->reg_base + NFC_REG_CMD);
> 
>     ret = wait_for_completion_timeout(&nfc->completion,
>                       msecs_to_jiffies(timeout_ms));
>     if (ret == 0)
>         ret = -1;
> 
>     writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>     ......
>

    
Thanks for reply! I'll try this code! One more question about OOB processing in this
driver (as You are author of it):

   OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
   bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
   2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
   32 bytes) become unavailable (in both raw and ECC modes) ?

Thanks, Arseniy

>>   static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>                      int page, int raw)
>>   {
>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>       data_len =  mtd->writesize + mtd->oobsize;
>>       info_len = nand->ecc.steps * PER_INFO_BYTE;
>>   +    ret = meson_nfc_wait_dev_ready(nand);
>> +    if (ret)
>> +        return ret;
>> +
>>       ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>       if (ret)
>>           return ret;
>>   +    ret = meson_nfc_send_read(nand);
>> +    if (ret)
>> +        return ret;
>> +
>>       ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>                        data_len, meson_chip->info_buf,
>>                        info_len, DMA_FROM_DEVICE);
>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>       }
>>         ret = meson_nfc_wait_dma_finish(nfc);
>> +    if (ret)
>> +        return ret;
>> +
>>       meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>         meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>
Arseniy Krasnov April 12, 2023, 12:03 p.m. UTC | #3
On 12.04.2023 13:24, Arseniy Krasnov wrote:
> 
> 
> On 12.04.2023 12:37, Liang Yang wrote:
>> Hi Arseniy,
>>
>> Thanks for pointing out this problem. also comment inline.
>>
>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>> [ EXTERNAL EMAIL ]
>>>
>>> This fixes read/write functionality. New command sequences were ported
>>> from old vendor's driver. Without this patch driver works unstable. This
>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>
>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>> ---
>>>   drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>   1 file changed, 101 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>> index 074e14225c06..256c37c76526 100644
>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>> @@ -26,6 +26,7 @@
>>>   #define NFC_CMD_IDLE        (0xc << 14)
>>>   #define NFC_CMD_CLE        (0x5 << 14)
>>>   #define NFC_CMD_ALE        (0x6 << 14)
>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>   #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>   #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>   #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>> @@ -84,6 +85,7 @@
>>>     #define DMA_BUSY_TIMEOUT    0x100000
>>>   #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>> +#define DEVICE_READY_TIMEOUT    1000
>>>     #define MAX_CE_NUM        2
>>>   @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>       }
>>>   }
>>>   +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>> +                     unsigned int timeout_ms)
>>> +{
>>> +    u32 cmd_size = 0;
>>> +    int ret;
>>> +
>>> +    /* wait cmd fifo is empty */
>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>> +                     10, timeout_ms * 1000);
>>> +    if (ret)
>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>> +
>>> +    return ret;
>>> +}
>>> +
>>>   static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>   {
>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>> +
>>>       writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>              nfc->reg_base + NFC_REG_CMD);
>>>   }
>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>        */
>>>       meson_nfc_cmd_idle(nfc, 0);
>>>       meson_nfc_cmd_idle(nfc, 0);
>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>   }
>>>   -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>> -                     unsigned int timeout_ms)
>>> -{
>>> -    u32 cmd_size = 0;
>>> -    int ret;
>>> -
>>> -    /* wait cmd fifo is empty */
>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>> -                     10, timeout_ms * 1000);
>>> -    if (ret)
>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>> -
>>> -    return ret;
>>> -}
>>>     static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>   {
>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>       return 0;
>>>   }
>>>   +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>> +{
>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>> +
>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>> +    meson_nfc_drain_cmd(nfc);
>>> +
>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>> +}
>>> +
>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>> +{
>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>> +    u32 cs = nfc->param.chip_select;
>>> +    unsigned long cnt = 0;
>>> +
>>> +    meson_nfc_drain_cmd(nfc);
>>> +
>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>> +
>>> +    /* 10 ms. */
>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>> +        uint8_t status;
>>> +
>>> +        status = meson_nfc_read_byte(nand);
>>> +
>>> +        if (status & NAND_STATUS_READY)
>>> +            break;
>>> +
>>> +        usleep_range(10, 11);
>>> +        cnt++;
>>> +    }
>>> +
>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>> +        return -ETIMEDOUT;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>                       int page, int raw)
>>>   {
>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>       u32 cmd;
>>>       int ret;
>>>   +    ret = meson_nfc_wait_dev_ready(nand);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>>       meson_nfc_select_chip(nand, nand->cur_cs);
>>>         data_len =  mtd->writesize + mtd->oobsize;
>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>                        NFC_CMD_SCRAMBLER_DISABLE);
>>>       }
>>>   +    ret = meson_nfc_wait_dma_finish(nfc);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>>       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>         meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>   +    ret = meson_nfc_wait_dev_ready(nand);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>>       return ret;
>>>   }
>>>   @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>       } while (!ret);
>>>   }
>>>   +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>> +{
>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>> +    u32 cs = nfc->param.chip_select;
>>> +    int ret;
>>> +
>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>
>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>     ......
>>     #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))

Sorry, I can see this define as (and it is used in the driver):
#define NFC_CMD_RB_INT          BIT(14) 

in drivers/mtd/nand/raw/meson_nand.c

Which one is correct ?

Thanks, Arseniy

>>
>>     meson_nfc_cmd_idle(nfc, 0);
>>     cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>     writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>     meson_nfc_cmd_idle(nfc, 5);
>>     cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>     writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>
>>     ret = wait_for_completion_timeout(&nfc->completion,
>>                       msecs_to_jiffies(timeout_ms));
>>     if (ret == 0)
>>         ret = -1;
>>
>>     writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>     ......
>>
> 
>     
> Thanks for reply! I'll try this code! One more question about OOB processing in this
> driver (as You are author of it):
> 
>    OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>    bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>    2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>    32 bytes) become unavailable (in both raw and ECC modes) ?
> 
> Thanks, Arseniy
> 
>>>   static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>                      int page, int raw)
>>>   {
>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>       data_len =  mtd->writesize + mtd->oobsize;
>>>       info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>   +    ret = meson_nfc_wait_dev_ready(nand);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>>       ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>       if (ret)
>>>           return ret;
>>>   +    ret = meson_nfc_send_read(nand);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>>       ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>                        data_len, meson_chip->info_buf,
>>>                        info_len, DMA_FROM_DEVICE);
>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>       }
>>>         ret = meson_nfc_wait_dma_finish(nfc);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>>       meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>         meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>
Liang Yang April 12, 2023, 1:30 p.m. UTC | #4
Hi,

On 2023/4/12 20:03, Arseniy Krasnov wrote:
> [ EXTERNAL EMAIL ]
> 
> 
> 
> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>
>>
>> On 12.04.2023 12:37, Liang Yang wrote:
>>> Hi Arseniy,
>>>
>>> Thanks for pointing out this problem. also comment inline.
>>>
>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>> [ EXTERNAL EMAIL ]
>>>>
>>>> This fixes read/write functionality. New command sequences were ported
>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>
>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>    drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>    1 file changed, 101 insertions(+), 15 deletions(-)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 074e14225c06..256c37c76526 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -26,6 +26,7 @@
>>>>    #define NFC_CMD_IDLE        (0xc << 14)
>>>>    #define NFC_CMD_CLE        (0x5 << 14)
>>>>    #define NFC_CMD_ALE        (0x6 << 14)
>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>    #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>    #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>    #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>> @@ -84,6 +85,7 @@
>>>>      #define DMA_BUSY_TIMEOUT    0x100000
>>>>    #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>      #define MAX_CE_NUM        2
>>>>    @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>        }
>>>>    }
>>>>    +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>> +                     unsigned int timeout_ms)
>>>> +{
>>>> +    u32 cmd_size = 0;
>>>> +    int ret;
>>>> +
>>>> +    /* wait cmd fifo is empty */
>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>> +                     10, timeout_ms * 1000);
>>>> +    if (ret)
>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>>    static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>    {
>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>> +
>>>>        writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>               nfc->reg_base + NFC_REG_CMD);
>>>>    }
>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>         */
>>>>        meson_nfc_cmd_idle(nfc, 0);
>>>>        meson_nfc_cmd_idle(nfc, 0);
>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>    }
>>>>    -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>> -                     unsigned int timeout_ms)
>>>> -{
>>>> -    u32 cmd_size = 0;
>>>> -    int ret;
>>>> -
>>>> -    /* wait cmd fifo is empty */
>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>> -                     10, timeout_ms * 1000);
>>>> -    if (ret)
>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>> -
>>>> -    return ret;
>>>> -}
>>>>      static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>    {
>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>        return 0;
>>>>    }
>>>>    +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>> +{
>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>> +
>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>> +    meson_nfc_drain_cmd(nfc);
>>>> +
>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>> +}
>>>> +
>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>> +{
>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>> +    u32 cs = nfc->param.chip_select;
>>>> +    unsigned long cnt = 0;
>>>> +
>>>> +    meson_nfc_drain_cmd(nfc);
>>>> +
>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>> +
>>>> +    /* 10 ms. */
>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>> +        uint8_t status;
>>>> +
>>>> +        status = meson_nfc_read_byte(nand);
>>>> +
>>>> +        if (status & NAND_STATUS_READY)
>>>> +            break;
>>>> +
>>>> +        usleep_range(10, 11);
>>>> +        cnt++;
>>>> +    }
>>>> +
>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>> +        return -ETIMEDOUT;
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>>    static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>                        int page, int raw)
>>>>    {
>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>        u32 cmd;
>>>>        int ret;
>>>>    +    ret = meson_nfc_wait_dev_ready(nand);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>>        meson_nfc_select_chip(nand, nand->cur_cs);
>>>>          data_len =  mtd->writesize + mtd->oobsize;
>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>                         NFC_CMD_SCRAMBLER_DISABLE);
>>>>        }
>>>>    +    ret = meson_nfc_wait_dma_finish(nfc);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>>        cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>        writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>        meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>          meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>    +    ret = meson_nfc_wait_dev_ready(nand);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>>        return ret;
>>>>    }
>>>>    @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>        } while (!ret);
>>>>    }
>>>>    +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>> +{
>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>> +    u32 cs = nfc->param.chip_select;
>>>> +    int ret;
>>>> +
>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>
>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>      ......
>>>      #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
> 
> Sorry, I can see this define as (and it is used in the driver):
> #define NFC_CMD_RB_INT          BIT(14)
> 
> in drivers/mtd/nand/raw/meson_nand.c
> 
> Which one is correct ?

we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).

> 
> Thanks, Arseniy
> 
>>>
>>>      meson_nfc_cmd_idle(nfc, 0);
>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>      meson_nfc_cmd_idle(nfc, 5);
>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>
>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>                        msecs_to_jiffies(timeout_ms));
>>>      if (ret == 0)
>>>          ret = -1;
>>>
>>>      writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>      ......
>>>
>>
>>      
>> Thanks for reply! I'll try this code! One more question about OOB processing in this
>> driver (as You are author of it):
>>
>>     OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>     bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>     2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>     32 bytes) become unavailable (in both raw and ECC modes) ?
>>
>> Thanks, Arseniy
>>
>>>>    static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>                       int page, int raw)
>>>>    {
>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>        data_len =  mtd->writesize + mtd->oobsize;
>>>>        info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>    +    ret = meson_nfc_wait_dev_ready(nand);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>>        ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>        if (ret)
>>>>            return ret;
>>>>    +    ret = meson_nfc_send_read(nand);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>>        ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>                         data_len, meson_chip->info_buf,
>>>>                         info_len, DMA_FROM_DEVICE);
>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>        }
>>>>          ret = meson_nfc_wait_dma_finish(nfc);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>>        meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>          meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>
>
Arseniy Krasnov April 13, 2023, 5:10 a.m. UTC | #5
On 12.04.2023 16:30, Liang Yang wrote:
> Hi,
> 
> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>> [ EXTERNAL EMAIL ]
>>
>>
>>
>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>
>>>
>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>> Hi Arseniy,
>>>>
>>>> Thanks for pointing out this problem. also comment inline.
>>>>
>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>> [ EXTERNAL EMAIL ]
>>>>>
>>>>> This fixes read/write functionality. New command sequences were ported
>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>
>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>> ---
>>>>>    drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>    1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>> index 074e14225c06..256c37c76526 100644
>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>> @@ -26,6 +26,7 @@
>>>>>    #define NFC_CMD_IDLE        (0xc << 14)
>>>>>    #define NFC_CMD_CLE        (0x5 << 14)
>>>>>    #define NFC_CMD_ALE        (0x6 << 14)
>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>    #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>    #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>    #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>> @@ -84,6 +85,7 @@
>>>>>      #define DMA_BUSY_TIMEOUT    0x100000
>>>>>    #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>      #define MAX_CE_NUM        2
>>>>>    @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>        }
>>>>>    }
>>>>>    +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>> +                     unsigned int timeout_ms)
>>>>> +{
>>>>> +    u32 cmd_size = 0;
>>>>> +    int ret;
>>>>> +
>>>>> +    /* wait cmd fifo is empty */
>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>> +                     10, timeout_ms * 1000);
>>>>> +    if (ret)
>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>>    static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>    {
>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>> +
>>>>>        writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>               nfc->reg_base + NFC_REG_CMD);
>>>>>    }
>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>         */
>>>>>        meson_nfc_cmd_idle(nfc, 0);
>>>>>        meson_nfc_cmd_idle(nfc, 0);
>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>    }
>>>>>    -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>> -                     unsigned int timeout_ms)
>>>>> -{
>>>>> -    u32 cmd_size = 0;
>>>>> -    int ret;
>>>>> -
>>>>> -    /* wait cmd fifo is empty */
>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>> -                     10, timeout_ms * 1000);
>>>>> -    if (ret)
>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>> -
>>>>> -    return ret;
>>>>> -}
>>>>>      static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>    {
>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>        return 0;
>>>>>    }
>>>>>    +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>> +{
>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> +
>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>> +
>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>> +{
>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> +    u32 cs = nfc->param.chip_select;
>>>>> +    unsigned long cnt = 0;
>>>>> +
>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>> +
>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> +    /* 10 ms. */
>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>> +        uint8_t status;
>>>>> +
>>>>> +        status = meson_nfc_read_byte(nand);
>>>>> +
>>>>> +        if (status & NAND_STATUS_READY)
>>>>> +            break;
>>>>> +
>>>>> +        usleep_range(10, 11);
>>>>> +        cnt++;
>>>>> +    }
>>>>> +
>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>> +        return -ETIMEDOUT;
>>>>> +    }
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>>    static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>                        int page, int raw)
>>>>>    {
>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>        u32 cmd;
>>>>>        int ret;
>>>>>    +    ret = meson_nfc_wait_dev_ready(nand);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>>        meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>          data_len =  mtd->writesize + mtd->oobsize;
>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>                         NFC_CMD_SCRAMBLER_DISABLE);
>>>>>        }
>>>>>    +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>>        cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>        writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>        meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>          meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>    +    ret = meson_nfc_wait_dev_ready(nand);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>>        return ret;
>>>>>    }
>>>>>    @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>        } while (!ret);
>>>>>    }
>>>>>    +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>> +{
>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> +    u32 cs = nfc->param.chip_select;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>
>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>      ......
>>>>      #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>
>> Sorry, I can see this define as (and it is used in the driver):
>> #define NFC_CMD_RB_INT          BIT(14)
>>
>> in drivers/mtd/nand/raw/meson_nand.c
>>
>> Which one is correct ?
> 
> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
> 

Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
defined incorrectly, so irq waiting does not work?

Thanks, Arseniy

>>
>> Thanks, Arseniy
>>
>>>>
>>>>      meson_nfc_cmd_idle(nfc, 0);
>>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>      meson_nfc_cmd_idle(nfc, 5);
>>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>
>>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>>                        msecs_to_jiffies(timeout_ms));
>>>>      if (ret == 0)
>>>>          ret = -1;
>>>>
>>>>      writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>      ......
>>>>
>>>
>>>      Thanks for reply! I'll try this code! One more question about OOB processing in this
>>> driver (as You are author of it):
>>>
>>>     OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>     bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>     2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>     32 bytes) become unavailable (in both raw and ECC modes) ?
>>>
>>> Thanks, Arseniy
>>>
>>>>>    static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>                       int page, int raw)
>>>>>    {
>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>        data_len =  mtd->writesize + mtd->oobsize;
>>>>>        info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>    +    ret = meson_nfc_wait_dev_ready(nand);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>>        ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>        if (ret)
>>>>>            return ret;
>>>>>    +    ret = meson_nfc_send_read(nand);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>>        ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>                         data_len, meson_chip->info_buf,
>>>>>                         info_len, DMA_FROM_DEVICE);
>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>        }
>>>>>          ret = meson_nfc_wait_dma_finish(nfc);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>>        meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>          meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>
>>
>
Liang Yang April 13, 2023, 5:57 a.m. UTC | #6
Hi Arseniy,

On 2023/4/13 13:10, Arseniy Krasnov wrote:
> [ EXTERNAL EMAIL ]
> 
> 
> 
> On 12.04.2023 16:30, Liang Yang wrote:
>> Hi,
>>
>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>> [ EXTERNAL EMAIL ]
>>>
>>>
>>>
>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>
>>>>
>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>> Hi Arseniy,
>>>>>
>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>
>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>> [ EXTERNAL EMAIL ]
>>>>>>
>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>
>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>> ---
>>>>>>     drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>     1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>> @@ -26,6 +26,7 @@
>>>>>>     #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>     #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>     #define NFC_CMD_ALE        (0x6 << 14)
>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>     #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>     #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>     #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>> @@ -84,6 +85,7 @@
>>>>>>       #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>     #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>       #define MAX_CE_NUM        2
>>>>>>     @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>         }
>>>>>>     }
>>>>>>     +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>> +                     unsigned int timeout_ms)
>>>>>> +{
>>>>>> +    u32 cmd_size = 0;
>>>>>> +    int ret;
>>>>>> +
>>>>>> +    /* wait cmd fifo is empty */
>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>> +                     10, timeout_ms * 1000);
>>>>>> +    if (ret)
>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>> +
>>>>>> +    return ret;
>>>>>> +}
>>>>>> +
>>>>>>     static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>     {
>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>> +
>>>>>>         writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>                nfc->reg_base + NFC_REG_CMD);
>>>>>>     }
>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>          */
>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>     }
>>>>>>     -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>> -                     unsigned int timeout_ms)
>>>>>> -{
>>>>>> -    u32 cmd_size = 0;
>>>>>> -    int ret;
>>>>>> -
>>>>>> -    /* wait cmd fifo is empty */
>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>> -                     10, timeout_ms * 1000);
>>>>>> -    if (ret)
>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>> -
>>>>>> -    return ret;
>>>>>> -}
>>>>>>       static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>     {
>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>         return 0;
>>>>>>     }
>>>>>>     +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>> +{
>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>> +
>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>> +
>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>> +}
>>>>>> +
>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>> +{
>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>> +    unsigned long cnt = 0;
>>>>>> +
>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>> +
>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>> +
>>>>>> +    /* 10 ms. */
>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>> +        uint8_t status;
>>>>>> +
>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>> +
>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>> +            break;
>>>>>> +
>>>>>> +        usleep_range(10, 11);
>>>>>> +        cnt++;
>>>>>> +    }
>>>>>> +
>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>> +        return -ETIMEDOUT;
>>>>>> +    }
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>>     static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>                         int page, int raw)
>>>>>>     {
>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>         u32 cmd;
>>>>>>         int ret;
>>>>>>     +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>>         meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>                          NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>         }
>>>>>>     +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>         meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>           meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>     +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>>         return ret;
>>>>>>     }
>>>>>>     @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>         } while (!ret);
>>>>>>     }
>>>>>>     +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>> +{
>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>> +    int ret;
>>>>>> +
>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>
>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>       ......
>>>>>       #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>
>>> Sorry, I can see this define as (and it is used in the driver):
>>> #define NFC_CMD_RB_INT          BIT(14)
>>>
>>> in drivers/mtd/nand/raw/meson_nand.c
>>>
>>> Which one is correct ?
>>
>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>
> 
> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
> defined incorrectly, so irq waiting does not work?

Previous defined BIT(14) is for having the external Ready/Busy pin of 
the NAND device connected to the controller. the new define is for 
reading status by sending read status(70H) command and read status from 
the data bus(checking the IO6). the both can work on AXG soc.
when the controller RB command is sent, the controller automatically 
checks the level of external Ready/Busy pin or the data bus(IO6) 
periodicity. and generate the irq signal if status is ready.

> 
> Thanks, Arseniy
> 
>>>
>>> Thanks, Arseniy
>>>
>>>>>
>>>>>       meson_nfc_cmd_idle(nfc, 0);
>>>>>       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>       meson_nfc_cmd_idle(nfc, 5);
>>>>>       cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>
>>>>>       ret = wait_for_completion_timeout(&nfc->completion,
>>>>>                         msecs_to_jiffies(timeout_ms));
>>>>>       if (ret == 0)
>>>>>           ret = -1;
>>>>>
>>>>>       writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>       ......
>>>>>
>>>>
>>>>       Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>> driver (as You are author of it):
>>>>
>>>>      OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>      bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>      2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>      32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>
>>>> Thanks, Arseniy
>>>>
>>>>>>     static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>                        int page, int raw)
>>>>>>     {
>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>         data_len =  mtd->writesize + mtd->oobsize;
>>>>>>         info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>     +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>>         ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>         if (ret)
>>>>>>             return ret;
>>>>>>     +    ret = meson_nfc_send_read(nand);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>>         ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>                          data_len, meson_chip->info_buf,
>>>>>>                          info_len, DMA_FROM_DEVICE);
>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>         }
>>>>>>           ret = meson_nfc_wait_dma_finish(nfc);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>>         meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>           meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>
>>>
>>
>
Arseniy Krasnov April 17, 2023, 6:47 a.m. UTC | #7
On 13.04.2023 08:57, Liang Yang wrote:
> Hi Arseniy,
> 
> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>> [ EXTERNAL EMAIL ]
>>
>>
>>
>> On 12.04.2023 16:30, Liang Yang wrote:
>>> Hi,
>>>
>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>> [ EXTERNAL EMAIL ]
>>>>
>>>>
>>>>
>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>
>>>>>
>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>> Hi Arseniy,
>>>>>>
>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>
>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>
>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>
>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>> ---
>>>>>>>     drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>     1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>     #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>     #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>     #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>     #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>     #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>     #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>       #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>     #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>       #define MAX_CE_NUM        2
>>>>>>>     @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>         }
>>>>>>>     }
>>>>>>>     +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>> +                     unsigned int timeout_ms)
>>>>>>> +{
>>>>>>> +    u32 cmd_size = 0;
>>>>>>> +    int ret;
>>>>>>> +
>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>> +    if (ret)
>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>> +
>>>>>>> +    return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>>     static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>     {
>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>> +
>>>>>>>         writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>                nfc->reg_base + NFC_REG_CMD);
>>>>>>>     }
>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>          */
>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>     }
>>>>>>>     -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>> -                     unsigned int timeout_ms)
>>>>>>> -{
>>>>>>> -    u32 cmd_size = 0;
>>>>>>> -    int ret;
>>>>>>> -
>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>> -    if (ret)
>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>> -
>>>>>>> -    return ret;
>>>>>>> -}
>>>>>>>       static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>     {
>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>         return 0;
>>>>>>>     }
>>>>>>>     +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>> +{
>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>> +
>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>> +
>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>> +{
>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>> +    unsigned long cnt = 0;
>>>>>>> +
>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>> +
>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>> +
>>>>>>> +    /* 10 ms. */
>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>> +        uint8_t status;
>>>>>>> +
>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>> +
>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>> +            break;
>>>>>>> +
>>>>>>> +        usleep_range(10, 11);
>>>>>>> +        cnt++;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>> +        return -ETIMEDOUT;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>>     static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>                         int page, int raw)
>>>>>>>     {
>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>         u32 cmd;
>>>>>>>         int ret;
>>>>>>>     +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>>         meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>                          NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>         }
>>>>>>>     +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>         meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>           meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>     +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>>         return ret;
>>>>>>>     }
>>>>>>>     @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>         } while (!ret);
>>>>>>>     }
>>>>>>>     +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>> +{
>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>> +    int ret;
>>>>>>> +
>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>> +
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>
>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>       ......
>>>>>>       #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>
>>>> Sorry, I can see this define as (and it is used in the driver):
>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>
>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>
>>>> Which one is correct ?
>>>
>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>
>>
>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>> defined incorrectly, so irq waiting does not work?
> 
> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
> 
>>
>> Thanks, Arseniy
>>
>>>>
>>>> Thanks, Arseniy
>>>>
>>>>>>
>>>>>>       meson_nfc_cmd_idle(nfc, 0);
>>>>>>       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>       meson_nfc_cmd_idle(nfc, 5);
>>>>>>       cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>
>>>>>>       ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>                         msecs_to_jiffies(timeout_ms));
>>>>>>       if (ret == 0)
>>>>>>           ret = -1;
>>>>>>
>>>>>>       writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>       ......
>>>>>>

Hello Liang!

I've got small questions to clarify Your comment. You suggest two thing IIUC:

1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
NAND_CMD_READ0 for write operation?

2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
because I don't have doc for this NAND controller, so it is very difficult to me to add valid
logic to this driver without any references

May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
handling which is I think more critical?

Thanks, Arseniy

>>>>>
>>>>>       Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>> driver (as You are author of it):
>>>>>
>>>>>      OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>      bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>      2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>      32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>
>>>>> Thanks, Arseniy
>>>>>
>>>>>>>     static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>                        int page, int raw)
>>>>>>>     {
>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>         data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>         info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>     +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>>         ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>         if (ret)
>>>>>>>             return ret;
>>>>>>>     +    ret = meson_nfc_send_read(nand);
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>>         ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>                          data_len, meson_chip->info_buf,
>>>>>>>                          info_len, DMA_FROM_DEVICE);
>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>         }
>>>>>>>           ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>>         meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>           meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>
>>>>
>>>
>>
>
Liang Yang April 17, 2023, 1:54 p.m. UTC | #8
Hi Arseniy,

On 2023/4/17 14:47, Arseniy Krasnov wrote:
> [ EXTERNAL EMAIL ]
> 
> 
> 
> On 13.04.2023 08:57, Liang Yang wrote:
>> Hi Arseniy,
>>
>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>> [ EXTERNAL EMAIL ]
>>>
>>>
>>>
>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>> Hi,
>>>>
>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>> [ EXTERNAL EMAIL ]
>>>>>
>>>>>
>>>>>
>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>
>>>>>>
>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>> Hi Arseniy,
>>>>>>>
>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>
>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>
>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>
>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>> ---
>>>>>>>>      drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>      1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>      #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>      #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>      #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>      #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>      #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>      #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>        #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>      #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>        #define MAX_CE_NUM        2
>>>>>>>>      @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>          }
>>>>>>>>      }
>>>>>>>>      +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>> +{
>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>> +    int ret;
>>>>>>>> +
>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>> +    if (ret)
>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>> +
>>>>>>>> +    return ret;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>      static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>      {
>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>> +
>>>>>>>>          writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>                 nfc->reg_base + NFC_REG_CMD);
>>>>>>>>      }
>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>           */
>>>>>>>>          meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>          meson_nfc_cmd_idle(nfc, 0);
>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>      }
>>>>>>>>      -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>> -{
>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>> -    int ret;
>>>>>>>> -
>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>> -    if (ret)
>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>> -
>>>>>>>> -    return ret;
>>>>>>>> -}
>>>>>>>>        static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>      {
>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>          return 0;
>>>>>>>>      }
>>>>>>>>      +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>> +{
>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>> +
>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>> +
>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>> +{
>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>> +
>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>> +
>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>> +
>>>>>>>> +    /* 10 ms. */
>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>> +        uint8_t status;
>>>>>>>> +
>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>> +
>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>> +            break;
>>>>>>>> +
>>>>>>>> +        usleep_range(10, 11);
>>>>>>>> +        cnt++;
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>      static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>                          int page, int raw)
>>>>>>>>      {
>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>          u32 cmd;
>>>>>>>>          int ret;
>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>> +    if (ret)
>>>>>>>> +        return ret;
>>>>>>>> +
>>>>>>>>          meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>            data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>                           NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>          }
>>>>>>>>      +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>> +    if (ret)
>>>>>>>> +        return ret;
>>>>>>>> +
>>>>>>>>          cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>          writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>          meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>            meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>> +    if (ret)
>>>>>>>> +        return ret;
>>>>>>>> +
>>>>>>>>          return ret;
>>>>>>>>      }
>>>>>>>>      @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>          } while (!ret);
>>>>>>>>      }
>>>>>>>>      +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>> +{
>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>> +    int ret;
>>>>>>>> +
>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>> +    if (ret)
>>>>>>>> +        return ret;
>>>>>>>> +
>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>> +
>>>>>>>> +    return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>
>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>        ......
>>>>>>>        #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>
>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>
>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>
>>>>> Which one is correct ?
>>>>
>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>
>>>
>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>> defined incorrectly, so irq waiting does not work?
>>
>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>
>>>
>>> Thanks, Arseniy
>>>
>>>>>
>>>>> Thanks, Arseniy
>>>>>
>>>>>>>
>>>>>>>        meson_nfc_cmd_idle(nfc, 0);
>>>>>>>        cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>        writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>        meson_nfc_cmd_idle(nfc, 5);
>>>>>>>        cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>        writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>
>>>>>>>        ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>                          msecs_to_jiffies(timeout_ms));
>>>>>>>        if (ret == 0)
>>>>>>>            ret = -1;
>>>>>>>
>>>>>>>        writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>        ......
>>>>>>>
> 
> Hello Liang!
> 
> I've got small questions to clarify Your comment. You suggest two thing IIUC:
> 
> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
> NAND_CMD_READ0 for write operation?

it is ok to me. but does NAND_CMD_READ0 really need to send in the 
controller driver? i don't find the other controller drivers have to 
send it for the old vendor NAND device.

> 
> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
> logic to this driver without any references

Could you please try the following? i have tested it on another SOC (not 
axg).

static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
{
	u32 cmd, cfg;
	int ret = 0;

	meson_nfc_cmd_idle(nfc, nfc->timing.twb);
	meson_nfc_drain_cmd(nfc);
	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);

	cfg = readl(nfc->reg_base + NFC_REG_CFG);
	cfg |= NFC_RB_IRQ_EN;
	writel(cfg, nfc->reg_base + NFC_REG_CFG);

	reinit_completion(&nfc->completion);

	meson_nfc_cmd_idle(nfc, 0);
	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
	meson_nfc_cmd_idle(nfc, 5);
	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
		| nfc->param.chip_select | nfc->timing.tbers_max;
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
	meson_nfc_drain_cmd(nfc);

	ret = wait_for_completion_timeout(&nfc->completion,
					  msecs_to_jiffies(timeout_ms));
	if (ret == 0)
		ret = -1;

	writel(1 << 31, nfc->reg_base + NFC_REG_CMD);

	return ret;
}

also we need to check and return the return value for 
meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and 
meson_nfc_write_page_sub().

> 
> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
> handling which is I think more critical?
> 

sure, it is up to you. it is more important, thanks again.

> Thanks, Arseniy
> 
>>>>>>
>>>>>>        Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>> driver (as You are author of it):
>>>>>>
>>>>>>       OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>       bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>       2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>       32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>
>>>>>> Thanks, Arseniy
>>>>>>
>>>>>>>>      static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>                         int page, int raw)
>>>>>>>>      {
>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>          data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>          info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>> +    if (ret)
>>>>>>>> +        return ret;
>>>>>>>> +
>>>>>>>>          ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>          if (ret)
>>>>>>>>              return ret;
>>>>>>>>      +    ret = meson_nfc_send_read(nand);
>>>>>>>> +    if (ret)
>>>>>>>> +        return ret;
>>>>>>>> +
>>>>>>>>          ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>                           data_len, meson_chip->info_buf,
>>>>>>>>                           info_len, DMA_FROM_DEVICE);
>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>          }
>>>>>>>>            ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>> +    if (ret)
>>>>>>>> +        return ret;
>>>>>>>> +
>>>>>>>>          meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>            meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>
>>>>>
>>>>
>>>
>>
>
Arseniy Krasnov April 17, 2023, 2:10 p.m. UTC | #9
On 17.04.2023 16:54, Liang Yang wrote:
> Hi Arseniy,
> 
> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>> [ EXTERNAL EMAIL ]
>>
>>
>>
>> On 13.04.2023 08:57, Liang Yang wrote:
>>> Hi Arseniy,
>>>
>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>> [ EXTERNAL EMAIL ]
>>>>
>>>>
>>>>
>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>> Hi,
>>>>>
>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>> [ EXTERNAL EMAIL ]
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>> Hi Arseniy,
>>>>>>>>
>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>
>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>
>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>
>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>> ---
>>>>>>>>>      drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>      1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>      #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>      #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>      #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>      #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>      #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>      #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>        #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>      #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>        #define MAX_CE_NUM        2
>>>>>>>>>      @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>          }
>>>>>>>>>      }
>>>>>>>>>      +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>> +{
>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>> +    int ret;
>>>>>>>>> +
>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>> +    if (ret)
>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>> +
>>>>>>>>> +    return ret;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>>      static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>      {
>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>> +
>>>>>>>>>          writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>                 nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>      }
>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>           */
>>>>>>>>>          meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>          meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>      }
>>>>>>>>>      -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>> -{
>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>> -    int ret;
>>>>>>>>> -
>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>> -    if (ret)
>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>> -
>>>>>>>>> -    return ret;
>>>>>>>>> -}
>>>>>>>>>        static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>      {
>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>          return 0;
>>>>>>>>>      }
>>>>>>>>>      +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>> +{
>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>> +
>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>> +
>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>> +{
>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>> +
>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>> +
>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>> +
>>>>>>>>> +    /* 10 ms. */
>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>> +        uint8_t status;
>>>>>>>>> +
>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>> +
>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>> +            break;
>>>>>>>>> +
>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>> +        cnt++;
>>>>>>>>> +    }
>>>>>>>>> +
>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>> +    }
>>>>>>>>> +
>>>>>>>>> +    return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>>      static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>                          int page, int raw)
>>>>>>>>>      {
>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>          u32 cmd;
>>>>>>>>>          int ret;
>>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>> +    if (ret)
>>>>>>>>> +        return ret;
>>>>>>>>> +
>>>>>>>>>          meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>            data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>                           NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>          }
>>>>>>>>>      +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>> +    if (ret)
>>>>>>>>> +        return ret;
>>>>>>>>> +
>>>>>>>>>          cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>          writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>          meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>            meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>> +    if (ret)
>>>>>>>>> +        return ret;
>>>>>>>>> +
>>>>>>>>>          return ret;
>>>>>>>>>      }
>>>>>>>>>      @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>          } while (!ret);
>>>>>>>>>      }
>>>>>>>>>      +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>> +{
>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>> +    int ret;
>>>>>>>>> +
>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>> +    if (ret)
>>>>>>>>> +        return ret;
>>>>>>>>> +
>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>> +
>>>>>>>>> +    return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>
>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>        ......
>>>>>>>>        #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>
>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>
>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>
>>>>>> Which one is correct ?
>>>>>
>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>
>>>>
>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>> defined incorrectly, so irq waiting does not work?
>>>
>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>
>>>>
>>>> Thanks, Arseniy
>>>>
>>>>>>
>>>>>> Thanks, Arseniy
>>>>>>
>>>>>>>>
>>>>>>>>        meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>        cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>        writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>        meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>        cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>        writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>
>>>>>>>>        ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>                          msecs_to_jiffies(timeout_ms));
>>>>>>>>        if (ret == 0)
>>>>>>>>            ret = -1;
>>>>>>>>
>>>>>>>>        writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>        ......
>>>>>>>>
>>
>> Hello Liang!
>>
>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>
>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>> NAND_CMD_READ0 for write operation?
> 
> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.

Hm, I found this command in the old driver. For example without it I get the following error:

# nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
ECC failed: 3975
ECC corrected: 47
Number of bad blocks: 10
Number of bbt blocks: 0
Block size 131072, page size 2048, OOB size 64
Dumping data starting at 0x00000000 and ending at 0x00000800...

But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
there are no ECC errors.

> 
>>
>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>> logic to this driver without any references
> 
> Could you please try the following? i have tested it on another SOC (not axg).
> 
> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> {
>     u32 cmd, cfg;
>     int ret = 0;
> 
>     meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>     meson_nfc_drain_cmd(nfc);
>     meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
> 
>     cfg = readl(nfc->reg_base + NFC_REG_CFG);
>     cfg |= NFC_RB_IRQ_EN;
>     writel(cfg, nfc->reg_base + NFC_REG_CFG);
> 
>     reinit_completion(&nfc->completion);
> 
>     meson_nfc_cmd_idle(nfc, 0);
>     cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>     writel(cmd, nfc->reg_base + NFC_REG_CMD);
>     meson_nfc_cmd_idle(nfc, 5);
>     cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>         | nfc->param.chip_select | nfc->timing.tbers_max;
>     writel(cmd, nfc->reg_base + NFC_REG_CMD);
>     meson_nfc_drain_cmd(nfc);
> 
>     ret = wait_for_completion_timeout(&nfc->completion,
>                       msecs_to_jiffies(timeout_ms));
>     if (ret == 0)
>         ret = -1;
> 
>     writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
> 
>     return ret;
> }

Ok! Thanks, I'll try it!

Thanks, Arseniy

> 
> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
> 
>>
>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>> handling which is I think more critical?
>>
> 
> sure, it is up to you. it is more important, thanks again.
> 
>> Thanks, Arseniy
>>
>>>>>>>
>>>>>>>        Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>> driver (as You are author of it):
>>>>>>>
>>>>>>>       OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>       bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>       2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>       32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>
>>>>>>> Thanks, Arseniy
>>>>>>>
>>>>>>>>>      static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>                         int page, int raw)
>>>>>>>>>      {
>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>          data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>          info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>> +    if (ret)
>>>>>>>>> +        return ret;
>>>>>>>>> +
>>>>>>>>>          ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>          if (ret)
>>>>>>>>>              return ret;
>>>>>>>>>      +    ret = meson_nfc_send_read(nand);
>>>>>>>>> +    if (ret)
>>>>>>>>> +        return ret;
>>>>>>>>> +
>>>>>>>>>          ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>                           data_len, meson_chip->info_buf,
>>>>>>>>>                           info_len, DMA_FROM_DEVICE);
>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>          }
>>>>>>>>>            ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>> +    if (ret)
>>>>>>>>> +        return ret;
>>>>>>>>> +
>>>>>>>>>          meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>            meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
Arseniy Krasnov April 19, 2023, 7:43 p.m. UTC | #10
On 17.04.2023 17:10, Arseniy Krasnov wrote:
> 
> 
> On 17.04.2023 16:54, Liang Yang wrote:
>> Hi Arseniy,
>>
>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>> [ EXTERNAL EMAIL ]
>>>
>>>
>>>
>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>> Hi Arseniy,
>>>>
>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>> [ EXTERNAL EMAIL ]
>>>>>
>>>>>
>>>>>
>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>> Hi,
>>>>>>
>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>> Hi Arseniy,
>>>>>>>>>
>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>
>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>
>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>
>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>> ---
>>>>>>>>>>      drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>      1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>
>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>      #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>      #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>      #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>      #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>      #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>      #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>        #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>      #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>        #define MAX_CE_NUM        2
>>>>>>>>>>      @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>          }
>>>>>>>>>>      }
>>>>>>>>>>      +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>> +{
>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>> +    int ret;
>>>>>>>>>> +
>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>> +    if (ret)
>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>> +
>>>>>>>>>> +    return ret;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>>      static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>      {
>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>> +
>>>>>>>>>>          writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>                 nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>      }
>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>           */
>>>>>>>>>>          meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>          meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>      }
>>>>>>>>>>      -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>> -{
>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>> -    int ret;
>>>>>>>>>> -
>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>> -    if (ret)
>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>> -
>>>>>>>>>> -    return ret;
>>>>>>>>>> -}
>>>>>>>>>>        static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>      {
>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>          return 0;
>>>>>>>>>>      }
>>>>>>>>>>      +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>> +{
>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>> +
>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>> +
>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>> +{
>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>> +
>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>> +
>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>> +
>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>> +        uint8_t status;
>>>>>>>>>> +
>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>> +
>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>> +            break;
>>>>>>>>>> +
>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>> +        cnt++;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>>      static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>      {
>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>          u32 cmd;
>>>>>>>>>>          int ret;
>>>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>> +    if (ret)
>>>>>>>>>> +        return ret;
>>>>>>>>>> +
>>>>>>>>>>          meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>            data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>                           NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>          }
>>>>>>>>>>      +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>> +    if (ret)
>>>>>>>>>> +        return ret;
>>>>>>>>>> +
>>>>>>>>>>          cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>          writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>          meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>            meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>> +    if (ret)
>>>>>>>>>> +        return ret;
>>>>>>>>>> +
>>>>>>>>>>          return ret;
>>>>>>>>>>      }
>>>>>>>>>>      @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>          } while (!ret);
>>>>>>>>>>      }
>>>>>>>>>>      +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>> +{
>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>> +    int ret;
>>>>>>>>>> +
>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>> +    if (ret)
>>>>>>>>>> +        return ret;
>>>>>>>>>> +
>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>> +
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>
>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>        ......
>>>>>>>>>        #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>
>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>
>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>
>>>>>>> Which one is correct ?
>>>>>>
>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>
>>>>>
>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>> defined incorrectly, so irq waiting does not work?
>>>>
>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>
>>>>>
>>>>> Thanks, Arseniy
>>>>>
>>>>>>>
>>>>>>> Thanks, Arseniy
>>>>>>>
>>>>>>>>>
>>>>>>>>>        meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>        cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>        writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>        meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>        cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>        writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>
>>>>>>>>>        ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>                          msecs_to_jiffies(timeout_ms));
>>>>>>>>>        if (ret == 0)
>>>>>>>>>            ret = -1;
>>>>>>>>>
>>>>>>>>>        writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>        ......
>>>>>>>>>
>>>
>>> Hello Liang!
>>>
>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>
>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>> NAND_CMD_READ0 for write operation?
>>
>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
> 
> Hm, I found this command in the old driver. For example without it I get the following error:
> 
> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
> ECC failed: 3975
> ECC corrected: 47
> Number of bad blocks: 10
> Number of bbt blocks: 0
> Block size 131072, page size 2048, OOB size 64
> Dumping data starting at 0x00000000 and ending at 0x00000800...
> 
> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
> there are no ECC errors.
> 
>>
>>>
>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>> logic to this driver without any references
>>
>> Could you please try the following? i have tested it on another SOC (not axg).
>>
>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>> {
>>     u32 cmd, cfg;
>>     int ret = 0;
>>
>>     meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>     meson_nfc_drain_cmd(nfc);
>>     meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>
>>     cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>     cfg |= NFC_RB_IRQ_EN;
>>     writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>
>>     reinit_completion(&nfc->completion);
>>
>>     meson_nfc_cmd_idle(nfc, 0);
>>     cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>     writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>     meson_nfc_cmd_idle(nfc, 5);
>>     cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>         | nfc->param.chip_select | nfc->timing.tbers_max;
>>     writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>     meson_nfc_drain_cmd(nfc);
>>
>>     ret = wait_for_completion_timeout(&nfc->completion,
>>                       msecs_to_jiffies(timeout_ms));
>>     if (ret == 0)
>>         ret = -1;
>>
>>     writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>
>>     return ret;
>> }
> 
> Ok! Thanks, I'll try it!
> 
> Thanks, Arseniy

Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
here is for example output buring bad blocks lookup:

[    2.060835] Scanning device for bad blocks
[    3.350085] Bad eraseblock 20 at 0x000000280000
[    3.536389] Freeing initrd memory: 11808K
[   39.133952] Bad eraseblock 581 at 0x0000048a0000
[   44.837917] Bad eraseblock 671 at 0x0000053e0000
[   45.677964] Bad eraseblock 685 at 0x0000055a0000
[   83.637917] Bad eraseblock 1279 at 0x000009fe0000
[  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left


Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
#define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
of dev ready calls from the old vendor's driver.

Thanks, Arseniy

> 
>>
>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>
>>>
>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>> handling which is I think more critical?
>>>
>>
>> sure, it is up to you. it is more important, thanks again.
>>
>>> Thanks, Arseniy
>>>
>>>>>>>>
>>>>>>>>        Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>> driver (as You are author of it):
>>>>>>>>
>>>>>>>>       OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>       bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>       2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>       32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>
>>>>>>>> Thanks, Arseniy
>>>>>>>>
>>>>>>>>>>      static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>                         int page, int raw)
>>>>>>>>>>      {
>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>          data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>          info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>      +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>> +    if (ret)
>>>>>>>>>> +        return ret;
>>>>>>>>>> +
>>>>>>>>>>          ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>          if (ret)
>>>>>>>>>>              return ret;
>>>>>>>>>>      +    ret = meson_nfc_send_read(nand);
>>>>>>>>>> +    if (ret)
>>>>>>>>>> +        return ret;
>>>>>>>>>> +
>>>>>>>>>>          ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>                           data_len, meson_chip->info_buf,
>>>>>>>>>>                           info_len, DMA_FROM_DEVICE);
>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>          }
>>>>>>>>>>            ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>> +    if (ret)
>>>>>>>>>> +        return ret;
>>>>>>>>>> +
>>>>>>>>>>          meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>            meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
Liang Yang April 20, 2023, 2:22 p.m. UTC | #11
Hi Arseniy,

Sorry, I am busy on other things and will try it on AXG platform in next 
week.

On 2023/4/20 3:43, Arseniy Krasnov wrote:
> [ EXTERNAL EMAIL ]
> 
> On 17.04.2023 17:10, Arseniy Krasnov wrote:
>>
>>
>> On 17.04.2023 16:54, Liang Yang wrote:
>>> Hi Arseniy,
>>>
>>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>>> [ EXTERNAL EMAIL ]
>>>>
>>>>
>>>>
>>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>>> Hi Arseniy,
>>>>>
>>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>>> [ EXTERNAL EMAIL ]
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>>> Hi,
>>>>>>>
>>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>>> Hi Arseniy,
>>>>>>>>>>
>>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>>
>>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>
>>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>>
>>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>>> ---
>>>>>>>>>>>       drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>>       1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>>       #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>>       #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>>       #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>>       #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>>       #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>>       #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>>         #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>>       #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>>         #define MAX_CE_NUM        2
>>>>>>>>>>>       @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>>           }
>>>>>>>>>>>       }
>>>>>>>>>>>       +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>>> +{
>>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>>> +    int ret;
>>>>>>>>>>> +
>>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>>> +
>>>>>>>>>>> +    return ret;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>>       static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>>       {
>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>>> +
>>>>>>>>>>>           writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>>                  nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>       }
>>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>>            */
>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>>       }
>>>>>>>>>>>       -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>>> -{
>>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>>> -    int ret;
>>>>>>>>>>> -
>>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>>> -    if (ret)
>>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>>> -
>>>>>>>>>>> -    return ret;
>>>>>>>>>>> -}
>>>>>>>>>>>         static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>>       {
>>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>>           return 0;
>>>>>>>>>>>       }
>>>>>>>>>>>       +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>> +
>>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>> +
>>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>>> +
>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>> +
>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>> +
>>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>>> +        uint8_t status;
>>>>>>>>>>> +
>>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>>> +
>>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>>> +            break;
>>>>>>>>>>> +
>>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>>> +        cnt++;
>>>>>>>>>>> +    }
>>>>>>>>>>> +
>>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>>> +    }
>>>>>>>>>>> +
>>>>>>>>>>> +    return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>>       static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>                           int page, int raw)
>>>>>>>>>>>       {
>>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>           u32 cmd;
>>>>>>>>>>>           int ret;
>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>>             data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>                            NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>>           }
>>>>>>>>>>>       +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>>           writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>           meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           return ret;
>>>>>>>>>>>       }
>>>>>>>>>>>       @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>>           } while (!ret);
>>>>>>>>>>>       }
>>>>>>>>>>>       +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>> +    int ret;
>>>>>>>>>>> +
>>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>> +
>>>>>>>>>>> +    return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>
>>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>>         ......
>>>>>>>>>>         #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>>
>>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>>
>>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>
>>>>>>>> Which one is correct ?
>>>>>>>
>>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>>
>>>>>>
>>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>>> defined incorrectly, so irq waiting does not work?
>>>>>
>>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>>
>>>>>>
>>>>>> Thanks, Arseniy
>>>>>>
>>>>>>>>
>>>>>>>> Thanks, Arseniy
>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>>         cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>
>>>>>>>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>>                           msecs_to_jiffies(timeout_ms));
>>>>>>>>>>         if (ret == 0)
>>>>>>>>>>             ret = -1;
>>>>>>>>>>
>>>>>>>>>>         writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>         ......
>>>>>>>>>>
>>>>
>>>> Hello Liang!
>>>>
>>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>>
>>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>>> NAND_CMD_READ0 for write operation?
>>>
>>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
>>
>> Hm, I found this command in the old driver. For example without it I get the following error:
>>
>> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
>> ECC failed: 3975
>> ECC corrected: 47
>> Number of bad blocks: 10
>> Number of bbt blocks: 0
>> Block size 131072, page size 2048, OOB size 64
>> Dumping data starting at 0x00000000 and ending at 0x00000800...
>>
>> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
>> there are no ECC errors.
>>
>>>
>>>>
>>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>>> logic to this driver without any references
>>>
>>> Could you please try the following? i have tested it on another SOC (not axg).
>>>
>>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>> {
>>>      u32 cmd, cfg;
>>>      int ret = 0;
>>>
>>>      meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>      meson_nfc_drain_cmd(nfc);
>>>      meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>
>>>      cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>>      cfg |= NFC_RB_IRQ_EN;
>>>      writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>>
>>>      reinit_completion(&nfc->completion);
>>>
>>>      meson_nfc_cmd_idle(nfc, 0);
>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>      meson_nfc_cmd_idle(nfc, 5);
>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>          | nfc->param.chip_select | nfc->timing.tbers_max;
>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>      meson_nfc_drain_cmd(nfc);
>>>
>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>                        msecs_to_jiffies(timeout_ms));
>>>      if (ret == 0)
>>>          ret = -1;
>>>
>>>      writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>>
>>>      return ret;
>>> }
>>
>> Ok! Thanks, I'll try it!
>>
>> Thanks, Arseniy
> 
> Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
> here is for example output buring bad blocks lookup:
> 
> [    2.060835] Scanning device for bad blocks
> [    3.350085] Bad eraseblock 20 at 0x000000280000
> [    3.536389] Freeing initrd memory: 11808K
> [   39.133952] Bad eraseblock 581 at 0x0000048a0000
> [   44.837917] Bad eraseblock 671 at 0x0000053e0000
> [   45.677964] Bad eraseblock 685 at 0x0000055a0000
> [   83.637917] Bad eraseblock 1279 at 0x000009fe0000
> [  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left
> 
> 
> Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
> #define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
> of dev ready calls from the old vendor's driver.
> 
> Thanks, Arseniy
> 
>>
>>>
>>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>>
>>>>
>>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>>> handling which is I think more critical?
>>>>
>>>
>>> sure, it is up to you. it is more important, thanks again.
>>>
>>>> Thanks, Arseniy
>>>>
>>>>>>>>>
>>>>>>>>>         Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>>> driver (as You are author of it):
>>>>>>>>>
>>>>>>>>>        OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>>        bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>>        2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>>        32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>>
>>>>>>>>> Thanks, Arseniy
>>>>>>>>>
>>>>>>>>>>>       static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>>       {
>>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>           info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>>           if (ret)
>>>>>>>>>>>               return ret;
>>>>>>>>>>>       +    ret = meson_nfc_send_read(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>>                            data_len, meson_chip->info_buf,
>>>>>>>>>>>                            info_len, DMA_FROM_DEVICE);
>>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>           }
>>>>>>>>>>>             ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
Arseniy Krasnov April 21, 2023, 5:57 a.m. UTC | #12
On 20.04.2023 17:22, Liang Yang wrote:
> Hi Arseniy,
> 
> Sorry, I am busy on other things and will try it on AXG platform in next week.

Hello Liang!

Sure, no problem

Thanks, Arseniy

> 
> On 2023/4/20 3:43, Arseniy Krasnov wrote:
>> [ EXTERNAL EMAIL ]
>>
>> On 17.04.2023 17:10, Arseniy Krasnov wrote:
>>>
>>>
>>> On 17.04.2023 16:54, Liang Yang wrote:
>>>> Hi Arseniy,
>>>>
>>>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>>>> [ EXTERNAL EMAIL ]
>>>>>
>>>>>
>>>>>
>>>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>>>> Hi Arseniy,
>>>>>>
>>>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>>>> Hi Arseniy,
>>>>>>>>>>>
>>>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>>>
>>>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>>
>>>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>>>
>>>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>>>> ---
>>>>>>>>>>>>       drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>>>       1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>>>
>>>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>>>       #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>>>       #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>>>       #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>>>       #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>>>       #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>>>       #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>>>         #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>>>       #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>>>         #define MAX_CE_NUM        2
>>>>>>>>>>>>       @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>>>           }
>>>>>>>>>>>>       }
>>>>>>>>>>>>       +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return ret;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>>       static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>>>       {
>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>>>> +
>>>>>>>>>>>>           writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>>>                  nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>       }
>>>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>>>            */
>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>>>       }
>>>>>>>>>>>>       -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>>>> -{
>>>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>>>> -    int ret;
>>>>>>>>>>>> -
>>>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>>>> -    if (ret)
>>>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>>>> -
>>>>>>>>>>>> -    return ret;
>>>>>>>>>>>> -}
>>>>>>>>>>>>         static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>>>       {
>>>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>>>           return 0;
>>>>>>>>>>>>       }
>>>>>>>>>>>>       +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>> +        uint8_t status;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>>>> +
>>>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>>>> +            break;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>>>> +        cnt++;
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>>       static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>                           int page, int raw)
>>>>>>>>>>>>       {
>>>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>           u32 cmd;
>>>>>>>>>>>>           int ret;
>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>>>             data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>                            NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>>>           }
>>>>>>>>>>>>       +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>>>           writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>           meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           return ret;
>>>>>>>>>>>>       }
>>>>>>>>>>>>       @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>>>           } while (!ret);
>>>>>>>>>>>>       }
>>>>>>>>>>>>       +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>
>>>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>>>         ......
>>>>>>>>>>>         #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>>>
>>>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>>>
>>>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>
>>>>>>>>> Which one is correct ?
>>>>>>>>
>>>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>>>
>>>>>>>
>>>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>>>> defined incorrectly, so irq waiting does not work?
>>>>>>
>>>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>>>
>>>>>>>
>>>>>>> Thanks, Arseniy
>>>>>>>
>>>>>>>>>
>>>>>>>>> Thanks, Arseniy
>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>>>         cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>
>>>>>>>>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>>>                           msecs_to_jiffies(timeout_ms));
>>>>>>>>>>>         if (ret == 0)
>>>>>>>>>>>             ret = -1;
>>>>>>>>>>>
>>>>>>>>>>>         writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>         ......
>>>>>>>>>>>
>>>>>
>>>>> Hello Liang!
>>>>>
>>>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>>>
>>>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>>>> NAND_CMD_READ0 for write operation?
>>>>
>>>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
>>>
>>> Hm, I found this command in the old driver. For example without it I get the following error:
>>>
>>> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
>>> ECC failed: 3975
>>> ECC corrected: 47
>>> Number of bad blocks: 10
>>> Number of bbt blocks: 0
>>> Block size 131072, page size 2048, OOB size 64
>>> Dumping data starting at 0x00000000 and ending at 0x00000800...
>>>
>>> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
>>> there are no ECC errors.
>>>
>>>>
>>>>>
>>>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>>>> logic to this driver without any references
>>>>
>>>> Could you please try the following? i have tested it on another SOC (not axg).
>>>>
>>>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>> {
>>>>      u32 cmd, cfg;
>>>>      int ret = 0;
>>>>
>>>>      meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>      meson_nfc_drain_cmd(nfc);
>>>>      meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>>
>>>>      cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>>>      cfg |= NFC_RB_IRQ_EN;
>>>>      writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>>>
>>>>      reinit_completion(&nfc->completion);
>>>>
>>>>      meson_nfc_cmd_idle(nfc, 0);
>>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>      meson_nfc_cmd_idle(nfc, 5);
>>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>>          | nfc->param.chip_select | nfc->timing.tbers_max;
>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>      meson_nfc_drain_cmd(nfc);
>>>>
>>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>>                        msecs_to_jiffies(timeout_ms));
>>>>      if (ret == 0)
>>>>          ret = -1;
>>>>
>>>>      writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>>>
>>>>      return ret;
>>>> }
>>>
>>> Ok! Thanks, I'll try it!
>>>
>>> Thanks, Arseniy
>>
>> Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
>> here is for example output buring bad blocks lookup:
>>
>> [    2.060835] Scanning device for bad blocks
>> [    3.350085] Bad eraseblock 20 at 0x000000280000
>> [    3.536389] Freeing initrd memory: 11808K
>> [   39.133952] Bad eraseblock 581 at 0x0000048a0000
>> [   44.837917] Bad eraseblock 671 at 0x0000053e0000
>> [   45.677964] Bad eraseblock 685 at 0x0000055a0000
>> [   83.637917] Bad eraseblock 1279 at 0x000009fe0000
>> [  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left
>>
>>
>> Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
>> #define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
>> of dev ready calls from the old vendor's driver.
>>
>> Thanks, Arseniy
>>
>>>
>>>>
>>>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>>>
>>>>>
>>>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>>>> handling which is I think more critical?
>>>>>
>>>>
>>>> sure, it is up to you. it is more important, thanks again.
>>>>
>>>>> Thanks, Arseniy
>>>>>
>>>>>>>>>>
>>>>>>>>>>         Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>>>> driver (as You are author of it):
>>>>>>>>>>
>>>>>>>>>>        OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>>>        bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>>>        2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>>>        32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>>>
>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>
>>>>>>>>>>>>       static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>>>       {
>>>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>           info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>>>           if (ret)
>>>>>>>>>>>>               return ret;
>>>>>>>>>>>>       +    ret = meson_nfc_send_read(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>>>                            data_len, meson_chip->info_buf,
>>>>>>>>>>>>                            info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>           }
>>>>>>>>>>>>             ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
Arseniy Krasnov April 26, 2023, 7:53 a.m. UTC | #13
On 21.04.2023 08:57, Arseniy Krasnov wrote:
> 
> 
> On 20.04.2023 17:22, Liang Yang wrote:
>> Hi Arseniy,
>>
>> Sorry, I am busy on other things and will try it on AXG platform in next week.
> 
> Hello Liang!
> 
> Sure, no problem
> 
> Thanks, Arseniy

Hello Liang!

I sent v2 patchset anyway, to continue review on it

Thanks, Arseniy

> 
>>
>> On 2023/4/20 3:43, Arseniy Krasnov wrote:
>>> [ EXTERNAL EMAIL ]
>>>
>>> On 17.04.2023 17:10, Arseniy Krasnov wrote:
>>>>
>>>>
>>>> On 17.04.2023 16:54, Liang Yang wrote:
>>>>> Hi Arseniy,
>>>>>
>>>>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>>>>> [ EXTERNAL EMAIL ]
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>>>>> Hi Arseniy,
>>>>>>>
>>>>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>>>>> Hi,
>>>>>>>>>
>>>>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>>>>> Hi Arseniy,
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>>>>
>>>>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>>>
>>>>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>>>>
>>>>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>>>>> ---
>>>>>>>>>>>>>       drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>>>>       1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>>>>
>>>>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>>>>       #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>>>>       #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>>>>       #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>>>>       #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>>>>       #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>>>>       #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>>>>         #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>>>>       #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>>>>         #define MAX_CE_NUM        2
>>>>>>>>>>>>>       @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>>>>           }
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return ret;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>>       static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>>>>       {
>>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>>>>                  nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>       }
>>>>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>>>>            */
>>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>>>>> -{
>>>>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>>>>> -    int ret;
>>>>>>>>>>>>> -
>>>>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>>>>> -    if (ret)
>>>>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>>>>> -
>>>>>>>>>>>>> -    return ret;
>>>>>>>>>>>>> -}
>>>>>>>>>>>>>         static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>>>>       {
>>>>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>>>>           return 0;
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>>> +        uint8_t status;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>>>>> +            break;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>>>>> +        cnt++;
>>>>>>>>>>>>> +    }
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>>>>> +    }
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>>       static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>                           int page, int raw)
>>>>>>>>>>>>>       {
>>>>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>           u32 cmd;
>>>>>>>>>>>>>           int ret;
>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>>>>             data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>                            NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>>>>           }
>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>>>>           writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>           meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           return ret;
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>>>>           } while (!ret);
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>
>>>>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>>>>         ......
>>>>>>>>>>>>         #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>>>>
>>>>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>>>>
>>>>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>
>>>>>>>>>> Which one is correct ?
>>>>>>>>>
>>>>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>>>>
>>>>>>>>
>>>>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>>>>> defined incorrectly, so irq waiting does not work?
>>>>>>>
>>>>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>>>>
>>>>>>>>
>>>>>>>> Thanks, Arseniy
>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>>>>         cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>
>>>>>>>>>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>>>>                           msecs_to_jiffies(timeout_ms));
>>>>>>>>>>>>         if (ret == 0)
>>>>>>>>>>>>             ret = -1;
>>>>>>>>>>>>
>>>>>>>>>>>>         writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>         ......
>>>>>>>>>>>>
>>>>>>
>>>>>> Hello Liang!
>>>>>>
>>>>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>>>>
>>>>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>>>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>>>>> NAND_CMD_READ0 for write operation?
>>>>>
>>>>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
>>>>
>>>> Hm, I found this command in the old driver. For example without it I get the following error:
>>>>
>>>> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
>>>> ECC failed: 3975
>>>> ECC corrected: 47
>>>> Number of bad blocks: 10
>>>> Number of bbt blocks: 0
>>>> Block size 131072, page size 2048, OOB size 64
>>>> Dumping data starting at 0x00000000 and ending at 0x00000800...
>>>>
>>>> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
>>>> there are no ECC errors.
>>>>
>>>>>
>>>>>>
>>>>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>>>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>>>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>>>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>>>>> logic to this driver without any references
>>>>>
>>>>> Could you please try the following? i have tested it on another SOC (not axg).
>>>>>
>>>>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>>> {
>>>>>      u32 cmd, cfg;
>>>>>      int ret = 0;
>>>>>
>>>>>      meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>      meson_nfc_drain_cmd(nfc);
>>>>>      meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>>>
>>>>>      cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>>>>      cfg |= NFC_RB_IRQ_EN;
>>>>>      writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>>>>
>>>>>      reinit_completion(&nfc->completion);
>>>>>
>>>>>      meson_nfc_cmd_idle(nfc, 0);
>>>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>      meson_nfc_cmd_idle(nfc, 5);
>>>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>>>          | nfc->param.chip_select | nfc->timing.tbers_max;
>>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>      meson_nfc_drain_cmd(nfc);
>>>>>
>>>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>>>                        msecs_to_jiffies(timeout_ms));
>>>>>      if (ret == 0)
>>>>>          ret = -1;
>>>>>
>>>>>      writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>>>>
>>>>>      return ret;
>>>>> }
>>>>
>>>> Ok! Thanks, I'll try it!
>>>>
>>>> Thanks, Arseniy
>>>
>>> Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
>>> here is for example output buring bad blocks lookup:
>>>
>>> [    2.060835] Scanning device for bad blocks
>>> [    3.350085] Bad eraseblock 20 at 0x000000280000
>>> [    3.536389] Freeing initrd memory: 11808K
>>> [   39.133952] Bad eraseblock 581 at 0x0000048a0000
>>> [   44.837917] Bad eraseblock 671 at 0x0000053e0000
>>> [   45.677964] Bad eraseblock 685 at 0x0000055a0000
>>> [   83.637917] Bad eraseblock 1279 at 0x000009fe0000
>>> [  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left
>>>
>>>
>>> Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
>>> #define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
>>> of dev ready calls from the old vendor's driver.
>>>
>>> Thanks, Arseniy
>>>
>>>>
>>>>>
>>>>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>>>>
>>>>>>
>>>>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>>>>> handling which is I think more critical?
>>>>>>
>>>>>
>>>>> sure, it is up to you. it is more important, thanks again.
>>>>>
>>>>>> Thanks, Arseniy
>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>         Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>>>>> driver (as You are author of it):
>>>>>>>>>>>
>>>>>>>>>>>        OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>>>>        bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>>>>        2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>>>>        32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>>>>
>>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>>
>>>>>>>>>>>>>       static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>>>>       {
>>>>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>>           info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>>>>           if (ret)
>>>>>>>>>>>>>               return ret;
>>>>>>>>>>>>>       +    ret = meson_nfc_send_read(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>>>>                            data_len, meson_chip->info_buf,
>>>>>>>>>>>>>                            info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>           }
>>>>>>>>>>>>>             ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
Liang Yang April 26, 2023, 12:17 p.m. UTC | #14
Hi Arseniy,

On 2023/4/20 3:43, Arseniy Krasnov wrote:
> [ EXTERNAL EMAIL ]
> 
> On 17.04.2023 17:10, Arseniy Krasnov wrote:
>>
>>
>> On 17.04.2023 16:54, Liang Yang wrote:
>>> Hi Arseniy,
>>>
>>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>>> [ EXTERNAL EMAIL ]
>>>>
>>>>
>>>>
>>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>>> Hi Arseniy,
>>>>>
>>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>>> [ EXTERNAL EMAIL ]
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>>> Hi,
>>>>>>>
>>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>>> Hi Arseniy,
>>>>>>>>>>
>>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>>
>>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>
>>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>>
>>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>>> ---
>>>>>>>>>>>       drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>>       1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>>       #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>>       #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>>       #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>>       #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>>       #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>>       #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>>         #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>>       #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>>         #define MAX_CE_NUM        2
>>>>>>>>>>>       @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>>           }
>>>>>>>>>>>       }
>>>>>>>>>>>       +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>>> +{
>>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>>> +    int ret;
>>>>>>>>>>> +
>>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>>> +
>>>>>>>>>>> +    return ret;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>>       static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>>       {
>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>>> +
>>>>>>>>>>>           writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>>                  nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>       }
>>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>>            */
>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>>       }
>>>>>>>>>>>       -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>>> -{
>>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>>> -    int ret;
>>>>>>>>>>> -
>>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>>> -    if (ret)
>>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>>> -
>>>>>>>>>>> -    return ret;
>>>>>>>>>>> -}
>>>>>>>>>>>         static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>>       {
>>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>>           return 0;
>>>>>>>>>>>       }
>>>>>>>>>>>       +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>> +
>>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>> +
>>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>>> +
>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>> +
>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>> +
>>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>>> +        uint8_t status;
>>>>>>>>>>> +
>>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>>> +
>>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>>> +            break;
>>>>>>>>>>> +
>>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>>> +        cnt++;
>>>>>>>>>>> +    }
>>>>>>>>>>> +
>>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>>> +    }
>>>>>>>>>>> +
>>>>>>>>>>> +    return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>>       static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>                           int page, int raw)
>>>>>>>>>>>       {
>>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>           u32 cmd;
>>>>>>>>>>>           int ret;
>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>>             data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>                            NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>>           }
>>>>>>>>>>>       +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>>           writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>           meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           return ret;
>>>>>>>>>>>       }
>>>>>>>>>>>       @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>>           } while (!ret);
>>>>>>>>>>>       }
>>>>>>>>>>>       +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>> +    int ret;
>>>>>>>>>>> +
>>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>> +
>>>>>>>>>>> +    return 0;
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>
>>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>>         ......
>>>>>>>>>>         #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>>
>>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>>
>>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>
>>>>>>>> Which one is correct ?
>>>>>>>
>>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>>
>>>>>>
>>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>>> defined incorrectly, so irq waiting does not work?
>>>>>
>>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>>
>>>>>>
>>>>>> Thanks, Arseniy
>>>>>>
>>>>>>>>
>>>>>>>> Thanks, Arseniy
>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>>         cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>
>>>>>>>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>>                           msecs_to_jiffies(timeout_ms));
>>>>>>>>>>         if (ret == 0)
>>>>>>>>>>             ret = -1;
>>>>>>>>>>
>>>>>>>>>>         writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>         ......
>>>>>>>>>>
>>>>
>>>> Hello Liang!
>>>>
>>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>>
>>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>>> NAND_CMD_READ0 for write operation?
>>>
>>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
>>
>> Hm, I found this command in the old driver. For example without it I get the following error:
>>
>> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
>> ECC failed: 3975
>> ECC corrected: 47
>> Number of bad blocks: 10
>> Number of bbt blocks: 0
>> Block size 131072, page size 2048, OOB size 64
>> Dumping data starting at 0x00000000 and ending at 0x00000800...
>>
>> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
>> there are no ECC errors.

After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read 
status(NAND_CMD_STATUS = 0x70) commands, it should send NAND_CMD_READ0 
command for exiting the read status mode from the datasheet from NAND 
device. but previous meson_nfc_queue_rb() only checks the Ready/Busy pin 
and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
i think there is something wrong with the Ready/Busy pin(please check 
the hardware whether this Ready/Busy pin is connected with SOC) or the 
source code. i have the board without Ready/Busy pin and prefer to use 
the nfc command called RB_IO6. it sends NAND_CMD_STATUS command and 
checks bit6 of the status register of NAND device from the data bus and 
generate IRQ if ready.

>>
>>>
>>>>
>>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>>> logic to this driver without any references
>>>
>>> Could you please try the following? i have tested it on another SOC (not axg).
>>>
>>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>> {
>>>      u32 cmd, cfg;
>>>      int ret = 0;
>>>
>>>      meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>      meson_nfc_drain_cmd(nfc);
>>>      meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>
>>>      cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>>      cfg |= NFC_RB_IRQ_EN;
>>>      writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>>
>>>      reinit_completion(&nfc->completion);
>>>
>>>      meson_nfc_cmd_idle(nfc, 0);
>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>      meson_nfc_cmd_idle(nfc, 5);
>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>          | nfc->param.chip_select | nfc->timing.tbers_max;
>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>      meson_nfc_drain_cmd(nfc);
>>>
>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>                        msecs_to_jiffies(timeout_ms));
>>>      if (ret == 0)
>>>          ret = -1;
>>>
>>>      writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>>
>>>      return ret;
>>> }
>>
>> Ok! Thanks, I'll try it!
>>
>> Thanks, Arseniy
> 
> Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
> here is for example output buring bad blocks lookup:
> 
> [    2.060835] Scanning device for bad blocks
> [    3.350085] Bad eraseblock 20 at 0x000000280000
> [    3.536389] Freeing initrd memory: 11808K
> [   39.133952] Bad eraseblock 581 at 0x0000048a0000
> [   44.837917] Bad eraseblock 671 at 0x0000053e0000
> [   45.677964] Bad eraseblock 685 at 0x0000055a0000
> [   83.637917] Bad eraseblock 1279 at 0x000009fe0000
> [  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left
> 
> 
> Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
> #define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
> of dev ready calls from the old vendor's driver.
> 
> Thanks, Arseniy

Yes, there is something wrong. i make a patch and it works on AXG 
platfrom. please try it. Thanks.

diff --git a/drivers/mtd/nand/raw/meson_nand.c 
b/drivers/mtd/nand/raw/meson_nand.c
old mode 100644
new mode 100755
index 074e14225c06..529d1a41a4a4
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -37,7 +37,7 @@
  #define NFC_CMD_SCRAMBLER_ENABLE       BIT(19)
  #define NFC_CMD_SCRAMBLER_DISABLE      0
  #define NFC_CMD_SHORTMODE_DISABLE      0
-#define NFC_CMD_RB_INT         BIT(14)
+#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))

  #define NFC_CMD_GET_SIZE(x)    (((x) >> 22) & GENMASK(4, 0))

@@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip 
*nand,
         }
  }

-static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
+static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, 
int cmd_read0)
  {
         u32 cmd, cfg;
         int ret = 0;
@@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc 
*nfc, int timeout_ms)

         reinit_completion(&nfc->completion);

+       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
+       writel(cmd, nfc->reg_base + NFC_REG_CMD);
+       meson_nfc_cmd_idle(nfc, 5);
+
         /* use the max erase time as the maximum clock for waiting R/B */
-       cmd = NFC_CMD_RB | NFC_CMD_RB_INT
-               | nfc->param.chip_select | nfc->timing.tbers_max;
+       cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
         writel(cmd, nfc->reg_base + NFC_REG_CMD);
+       meson_nfc_cmd_idle(nfc, 2);

         ret = wait_for_completion_timeout(&nfc->completion,
                                           msecs_to_jiffies(timeout_ms));
         if (ret == 0)
-               ret = -1;
+               return -1;

-       return ret;
+       if (!cmd_read0)
+               return 0;
+
+       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
+       writel(cmd, nfc->reg_base + NFC_REG_CMD);
+       meson_nfc_drain_cmd(nfc);
+       meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
+
+       return 0;
  }

  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
@@ -623,7 +635,7 @@ static int 
meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
         if (in) {
                 nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | 
NAND_CMD_READSTART;
                 writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
-               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
+               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
         } else {
                 meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
         }
@@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip 
*nand,

         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
         writel(cmd, nfc->reg_base + NFC_REG_CMD);
-       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
+       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);

         meson_nfc_dma_buffer_release(nand, data_len, info_len, 
DMA_TO_DEVICE);

@@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
                         break;

                 case NAND_OP_WAITRDY_INSTR:
-                       meson_nfc_queue_rb(nfc, 
instr->ctx.waitrdy.timeout_ms);
+                       meson_nfc_queue_rb(nfc, 
instr->ctx.waitrdy.timeout_ms, 0);
                         if (instr->delay_ns)
                                 meson_nfc_cmd_idle(nfc, delay_idle);
                         break;
> 
>>
>>>
>>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>>
>>>>
>>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>>> handling which is I think more critical?
>>>>
>>>
>>> sure, it is up to you. it is more important, thanks again.
>>>
>>>> Thanks, Arseniy
>>>>
>>>>>>>>>
>>>>>>>>>         Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>>> driver (as You are author of it):
>>>>>>>>>
>>>>>>>>>        OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>>        bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>>        2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>>        32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>>
>>>>>>>>> Thanks, Arseniy
>>>>>>>>>
>>>>>>>>>>>       static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>>       {
>>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>           info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>>           if (ret)
>>>>>>>>>>>               return ret;
>>>>>>>>>>>       +    ret = meson_nfc_send_read(nand);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>>                            data_len, meson_chip->info_buf,
>>>>>>>>>>>                            info_len, DMA_FROM_DEVICE);
>>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>           }
>>>>>>>>>>>             ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>> +    if (ret)
>>>>>>>>>>> +        return ret;
>>>>>>>>>>> +
>>>>>>>>>>>           meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
Arseniy Krasnov April 26, 2023, 2:47 p.m. UTC | #15
On 26.04.2023 15:17, Liang Yang wrote:
> Hi Arseniy,
> 
> On 2023/4/20 3:43, Arseniy Krasnov wrote:
>> [ EXTERNAL EMAIL ]
>>
>> On 17.04.2023 17:10, Arseniy Krasnov wrote:
>>>
>>>
>>> On 17.04.2023 16:54, Liang Yang wrote:
>>>> Hi Arseniy,
>>>>
>>>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>>>> [ EXTERNAL EMAIL ]
>>>>>
>>>>>
>>>>>
>>>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>>>> Hi Arseniy,
>>>>>>
>>>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>>>> Hi Arseniy,
>>>>>>>>>>>
>>>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>>>
>>>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>>
>>>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>>>
>>>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>>>> ---
>>>>>>>>>>>>       drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>>>       1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>>>
>>>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>>>       #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>>>       #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>>>       #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>>>       #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>>>       #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>>>       #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>>>         #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>>>       #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>>>         #define MAX_CE_NUM        2
>>>>>>>>>>>>       @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>>>           }
>>>>>>>>>>>>       }
>>>>>>>>>>>>       +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return ret;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>>       static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>>>       {
>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>>>> +
>>>>>>>>>>>>           writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>>>                  nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>       }
>>>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>>>            */
>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>>>       }
>>>>>>>>>>>>       -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>>>> -{
>>>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>>>> -    int ret;
>>>>>>>>>>>> -
>>>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>>>> -    if (ret)
>>>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>>>> -
>>>>>>>>>>>> -    return ret;
>>>>>>>>>>>> -}
>>>>>>>>>>>>         static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>>>       {
>>>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>>>           return 0;
>>>>>>>>>>>>       }
>>>>>>>>>>>>       +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>> +        uint8_t status;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>>>> +
>>>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>>>> +            break;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>>>> +        cnt++;
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>>       static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>                           int page, int raw)
>>>>>>>>>>>>       {
>>>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>           u32 cmd;
>>>>>>>>>>>>           int ret;
>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>>>             data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>                            NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>>>           }
>>>>>>>>>>>>       +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>>>           writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>           meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           return ret;
>>>>>>>>>>>>       }
>>>>>>>>>>>>       @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>>>           } while (!ret);
>>>>>>>>>>>>       }
>>>>>>>>>>>>       +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>
>>>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>>>         ......
>>>>>>>>>>>         #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>>>
>>>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>>>
>>>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>
>>>>>>>>> Which one is correct ?
>>>>>>>>
>>>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>>>
>>>>>>>
>>>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>>>> defined incorrectly, so irq waiting does not work?
>>>>>>
>>>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>>>
>>>>>>>
>>>>>>> Thanks, Arseniy
>>>>>>>
>>>>>>>>>
>>>>>>>>> Thanks, Arseniy
>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>>>         cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>
>>>>>>>>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>>>                           msecs_to_jiffies(timeout_ms));
>>>>>>>>>>>         if (ret == 0)
>>>>>>>>>>>             ret = -1;
>>>>>>>>>>>
>>>>>>>>>>>         writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>         ......
>>>>>>>>>>>
>>>>>
>>>>> Hello Liang!
>>>>>
>>>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>>>
>>>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>>>> NAND_CMD_READ0 for write operation?
>>>>
>>>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
>>>
>>> Hm, I found this command in the old driver. For example without it I get the following error:
>>>
>>> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
>>> ECC failed: 3975
>>> ECC corrected: 47
>>> Number of bad blocks: 10
>>> Number of bbt blocks: 0
>>> Block size 131072, page size 2048, OOB size 64
>>> Dumping data starting at 0x00000000 and ending at 0x00000800...
>>>
>>> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
>>> there are no ECC errors.
> 
> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device. but previous meson_nfc_queue_rb() only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the data bus and generate IRQ if ready.
> 
>>>
>>>>
>>>>>
>>>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>>>> logic to this driver without any references
>>>>
>>>> Could you please try the following? i have tested it on another SOC (not axg).
>>>>
>>>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>> {
>>>>      u32 cmd, cfg;
>>>>      int ret = 0;
>>>>
>>>>      meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>      meson_nfc_drain_cmd(nfc);
>>>>      meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>>
>>>>      cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>>>      cfg |= NFC_RB_IRQ_EN;
>>>>      writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>>>
>>>>      reinit_completion(&nfc->completion);
>>>>
>>>>      meson_nfc_cmd_idle(nfc, 0);
>>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>      meson_nfc_cmd_idle(nfc, 5);
>>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>>          | nfc->param.chip_select | nfc->timing.tbers_max;
>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>      meson_nfc_drain_cmd(nfc);
>>>>
>>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>>                        msecs_to_jiffies(timeout_ms));
>>>>      if (ret == 0)
>>>>          ret = -1;
>>>>
>>>>      writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>>>
>>>>      return ret;
>>>> }
>>>
>>> Ok! Thanks, I'll try it!
>>>
>>> Thanks, Arseniy
>>
>> Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
>> here is for example output buring bad blocks lookup:
>>
>> [    2.060835] Scanning device for bad blocks
>> [    3.350085] Bad eraseblock 20 at 0x000000280000
>> [    3.536389] Freeing initrd memory: 11808K
>> [   39.133952] Bad eraseblock 581 at 0x0000048a0000
>> [   44.837917] Bad eraseblock 671 at 0x0000053e0000
>> [   45.677964] Bad eraseblock 685 at 0x0000055a0000
>> [   83.637917] Bad eraseblock 1279 at 0x000009fe0000
>> [  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left
>>
>>
>> Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
>> #define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
>> of dev ready calls from the old vendor's driver.
>>
>> Thanks, Arseniy
> 
> Yes, there is something wrong. i make a patch and it works on AXG platfrom. please try it. Thanks.

Ok, Thank You very much, I'll try it ASAP!

Thanks, Arseniy

> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> old mode 100644
> new mode 100755
> index 074e14225c06..529d1a41a4a4
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -37,7 +37,7 @@
>  #define NFC_CMD_SCRAMBLER_ENABLE       BIT(19)
>  #define NFC_CMD_SCRAMBLER_DISABLE      0
>  #define NFC_CMD_SHORTMODE_DISABLE      0
> -#define NFC_CMD_RB_INT         BIT(14)
> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
> 
>  #define NFC_CMD_GET_SIZE(x)    (((x) >> 22) & GENMASK(4, 0))
> 
> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>         }
>  }
> 
> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>  {
>         u32 cmd, cfg;
>         int ret = 0;
> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> 
>         reinit_completion(&nfc->completion);
> 
> +       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> +       writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +       meson_nfc_cmd_idle(nfc, 5);
> +
>         /* use the max erase time as the maximum clock for waiting R/B */
> -       cmd = NFC_CMD_RB | NFC_CMD_RB_INT
> -               | nfc->param.chip_select | nfc->timing.tbers_max;
> +       cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +       meson_nfc_cmd_idle(nfc, 2);
> 
>         ret = wait_for_completion_timeout(&nfc->completion,
>                                           msecs_to_jiffies(timeout_ms));
>         if (ret == 0)
> -               ret = -1;
> +               return -1;
> 
> -       return ret;
> +       if (!cmd_read0)
> +               return 0;
> +
> +       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
> +       writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +       meson_nfc_drain_cmd(nfc);
> +       meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
> +
> +       return 0;
>  }
> 
>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>         if (in) {
>                 nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>                 writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
> -               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
> +               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>         } else {
>                 meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>         }
> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
> 
>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
> -       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
> +       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
> 
>         meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
> 
> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>                         break;
> 
>                 case NAND_OP_WAITRDY_INSTR:
> -                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
> +                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 0);
>                         if (instr->delay_ns)
>                                 meson_nfc_cmd_idle(nfc, delay_idle);
>                         break;
>>
>>>
>>>>
>>>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>>>
>>>>>
>>>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>>>> handling which is I think more critical?
>>>>>
>>>>
>>>> sure, it is up to you. it is more important, thanks again.
>>>>
>>>>> Thanks, Arseniy
>>>>>
>>>>>>>>>>
>>>>>>>>>>         Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>>>> driver (as You are author of it):
>>>>>>>>>>
>>>>>>>>>>        OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>>>        bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>>>        2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>>>        32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>>>
>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>
>>>>>>>>>>>>       static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>>>       {
>>>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>           info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>>>           if (ret)
>>>>>>>>>>>>               return ret;
>>>>>>>>>>>>       +    ret = meson_nfc_send_read(nand);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>>>                            data_len, meson_chip->info_buf,
>>>>>>>>>>>>                            info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>           }
>>>>>>>>>>>>             ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>> +
>>>>>>>>>>>>           meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
Arseniy Krasnov May 4, 2023, 6:16 a.m. UTC | #16
On 26.04.2023 17:47, Arseniy Krasnov wrote:
> 
> 
> On 26.04.2023 15:17, Liang Yang wrote:
>> Hi Arseniy,
>>
>> On 2023/4/20 3:43, Arseniy Krasnov wrote:
>>> [ EXTERNAL EMAIL ]
>>>
>>> On 17.04.2023 17:10, Arseniy Krasnov wrote:
>>>>
>>>>
>>>> On 17.04.2023 16:54, Liang Yang wrote:
>>>>> Hi Arseniy,
>>>>>
>>>>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>>>>> [ EXTERNAL EMAIL ]
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>>>>> Hi Arseniy,
>>>>>>>
>>>>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>>>>> Hi,
>>>>>>>>>
>>>>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>>>>> Hi Arseniy,
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>>>>
>>>>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>>>
>>>>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>>>>
>>>>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>>>>> ---
>>>>>>>>>>>>>       drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>>>>       1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>>>>
>>>>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>>>>       #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>>>>       #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>>>>       #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>>>>       #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>>>>       #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>>>>       #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>>>>         #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>>>>       #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>>>>         #define MAX_CE_NUM        2
>>>>>>>>>>>>>       @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>>>>           }
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return ret;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>>       static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>>>>       {
>>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>>>>                  nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>       }
>>>>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>>>>            */
>>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>>>>> -{
>>>>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>>>>> -    int ret;
>>>>>>>>>>>>> -
>>>>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>>>>> -    if (ret)
>>>>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>>>>> -
>>>>>>>>>>>>> -    return ret;
>>>>>>>>>>>>> -}
>>>>>>>>>>>>>         static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>>>>       {
>>>>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>>>>           return 0;
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>>> +        uint8_t status;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>>>>> +            break;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>>>>> +        cnt++;
>>>>>>>>>>>>> +    }
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>>>>> +    }
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>>       static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>                           int page, int raw)
>>>>>>>>>>>>>       {
>>>>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>           u32 cmd;
>>>>>>>>>>>>>           int ret;
>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>>>>             data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>                            NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>>>>           }
>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>>>>           writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>           meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           return ret;
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>>>>           } while (!ret);
>>>>>>>>>>>>>       }
>>>>>>>>>>>>>       +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>
>>>>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>>>>         ......
>>>>>>>>>>>>         #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>>>>
>>>>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>>>>
>>>>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>
>>>>>>>>>> Which one is correct ?
>>>>>>>>>
>>>>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>>>>
>>>>>>>>
>>>>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>>>>> defined incorrectly, so irq waiting does not work?
>>>>>>>
>>>>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>>>>
>>>>>>>>
>>>>>>>> Thanks, Arseniy
>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>>>>         cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>
>>>>>>>>>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>>>>                           msecs_to_jiffies(timeout_ms));
>>>>>>>>>>>>         if (ret == 0)
>>>>>>>>>>>>             ret = -1;
>>>>>>>>>>>>
>>>>>>>>>>>>         writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>         ......
>>>>>>>>>>>>
>>>>>>
>>>>>> Hello Liang!
>>>>>>
>>>>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>>>>
>>>>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>>>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>>>>> NAND_CMD_READ0 for write operation?
>>>>>
>>>>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
>>>>
>>>> Hm, I found this command in the old driver. For example without it I get the following error:
>>>>
>>>> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
>>>> ECC failed: 3975
>>>> ECC corrected: 47
>>>> Number of bad blocks: 10
>>>> Number of bbt blocks: 0
>>>> Block size 131072, page size 2048, OOB size 64
>>>> Dumping data starting at 0x00000000 and ending at 0x00000800...
>>>>
>>>> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
>>>> there are no ECC errors.
>>
>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device. but previous meson_nfc_queue_rb() only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the data bus and generate IRQ if ready.
>>
>>>>
>>>>>
>>>>>>
>>>>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>>>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>>>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>>>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>>>>> logic to this driver without any references
>>>>>
>>>>> Could you please try the following? i have tested it on another SOC (not axg).
>>>>>
>>>>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>>> {
>>>>>      u32 cmd, cfg;
>>>>>      int ret = 0;
>>>>>
>>>>>      meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>      meson_nfc_drain_cmd(nfc);
>>>>>      meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>>>
>>>>>      cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>>>>      cfg |= NFC_RB_IRQ_EN;
>>>>>      writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>>>>
>>>>>      reinit_completion(&nfc->completion);
>>>>>
>>>>>      meson_nfc_cmd_idle(nfc, 0);
>>>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>      meson_nfc_cmd_idle(nfc, 5);
>>>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>>>          | nfc->param.chip_select | nfc->timing.tbers_max;
>>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>      meson_nfc_drain_cmd(nfc);
>>>>>
>>>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>>>                        msecs_to_jiffies(timeout_ms));
>>>>>      if (ret == 0)
>>>>>          ret = -1;
>>>>>
>>>>>      writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>>>>
>>>>>      return ret;
>>>>> }
>>>>
>>>> Ok! Thanks, I'll try it!
>>>>
>>>> Thanks, Arseniy
>>>
>>> Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
>>> here is for example output buring bad blocks lookup:
>>>
>>> [    2.060835] Scanning device for bad blocks
>>> [    3.350085] Bad eraseblock 20 at 0x000000280000
>>> [    3.536389] Freeing initrd memory: 11808K
>>> [   39.133952] Bad eraseblock 581 at 0x0000048a0000
>>> [   44.837917] Bad eraseblock 671 at 0x0000053e0000
>>> [   45.677964] Bad eraseblock 685 at 0x0000055a0000
>>> [   83.637917] Bad eraseblock 1279 at 0x000009fe0000
>>> [  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left
>>>
>>>
>>> Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
>>> #define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
>>> of dev ready calls from the old vendor's driver.
>>>
>>> Thanks, Arseniy
>>
>> Yes, there is something wrong. i make a patch and it works on AXG platfrom. please try it. Thanks.
> 
> Ok, Thank You very much, I'll try it ASAP!
> 
> Thanks, Arseniy

Hello @Liang! Sorry for late reply, was a little bit busy. I've tested patch below, seems it
works! Thank You! So I think I can replace this patch from current patchset with this code!

Thanks, Arseniy

> 
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> old mode 100644
>> new mode 100755
>> index 074e14225c06..529d1a41a4a4
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -37,7 +37,7 @@
>>  #define NFC_CMD_SCRAMBLER_ENABLE       BIT(19)
>>  #define NFC_CMD_SCRAMBLER_DISABLE      0
>>  #define NFC_CMD_SHORTMODE_DISABLE      0
>> -#define NFC_CMD_RB_INT         BIT(14)
>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>
>>  #define NFC_CMD_GET_SIZE(x)    (((x) >> 22) & GENMASK(4, 0))
>>
>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>         }
>>  }
>>
>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>  {
>>         u32 cmd, cfg;
>>         int ret = 0;
>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>
>>         reinit_completion(&nfc->completion);
>>
>> +       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>> +       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +       meson_nfc_cmd_idle(nfc, 5);
>> +
>>         /* use the max erase time as the maximum clock for waiting R/B */
>> -       cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>> -               | nfc->param.chip_select | nfc->timing.tbers_max;
>> +       cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +       meson_nfc_cmd_idle(nfc, 2);
>>
>>         ret = wait_for_completion_timeout(&nfc->completion,
>>                                           msecs_to_jiffies(timeout_ms));
>>         if (ret == 0)
>> -               ret = -1;
>> +               return -1;
>>
>> -       return ret;
>> +       if (!cmd_read0)
>> +               return 0;
>> +
>> +       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
>> +       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +       meson_nfc_drain_cmd(nfc);
>> +       meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>> +
>> +       return 0;
>>  }
>>
>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>         if (in) {
>>                 nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>                 writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>> -               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>> +               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>         } else {
>>                 meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>         }
>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>
>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> -       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>> +       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>
>>         meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>
>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>                         break;
>>
>>                 case NAND_OP_WAITRDY_INSTR:
>> -                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>> +                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 0);
>>                         if (instr->delay_ns)
>>                                 meson_nfc_cmd_idle(nfc, delay_idle);
>>                         break;
>>>
>>>>
>>>>>
>>>>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>>>>
>>>>>>
>>>>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>>>>> handling which is I think more critical?
>>>>>>
>>>>>
>>>>> sure, it is up to you. it is more important, thanks again.
>>>>>
>>>>>> Thanks, Arseniy
>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>         Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>>>>> driver (as You are author of it):
>>>>>>>>>>>
>>>>>>>>>>>        OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>>>>        bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>>>>        2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>>>>        32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>>>>
>>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>>
>>>>>>>>>>>>>       static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>>>>       {
>>>>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>>           info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>>>>           if (ret)
>>>>>>>>>>>>>               return ret;
>>>>>>>>>>>>>       +    ret = meson_nfc_send_read(nand);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>>>>                            data_len, meson_chip->info_buf,
>>>>>>>>>>>>>                            info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>           }
>>>>>>>>>>>>>             ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>>           meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
Arseniy Krasnov May 10, 2023, 11:34 a.m. UTC | #17
On 04.05.2023 09:16, Arseniy Krasnov wrote:
> 
> 
> On 26.04.2023 17:47, Arseniy Krasnov wrote:
>>
>>
>> On 26.04.2023 15:17, Liang Yang wrote:
>>> Hi Arseniy,
>>>
>>> On 2023/4/20 3:43, Arseniy Krasnov wrote:
>>>> [ EXTERNAL EMAIL ]
>>>>
>>>> On 17.04.2023 17:10, Arseniy Krasnov wrote:
>>>>>
>>>>>
>>>>> On 17.04.2023 16:54, Liang Yang wrote:
>>>>>> Hi Arseniy,
>>>>>>
>>>>>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>>>>>> Hi Arseniy,
>>>>>>>>
>>>>>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>>>>>> Hi,
>>>>>>>>>>
>>>>>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>>>>>> Hi Arseniy,
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>       drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>>>>>       1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>>>>>       #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>>>>>       #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>>>>>       #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>>>>>       #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>>>>>       #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>>>>>       #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>>>>>         #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>>>>>       #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>>>>>         #define MAX_CE_NUM        2
>>>>>>>>>>>>>>       @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>>>>>           }
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>       +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    return ret;
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>       static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>>>>>       {
>>>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>           writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>>>>>                  nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>>>>>            */
>>>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>       -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>>>>>> -{
>>>>>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>>>>>> -    int ret;
>>>>>>>>>>>>>> -
>>>>>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>>>>>> -    if (ret)
>>>>>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>>>>>> -
>>>>>>>>>>>>>> -    return ret;
>>>>>>>>>>>>>> -}
>>>>>>>>>>>>>>         static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>>>>>       {
>>>>>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>>>>>           return 0;
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>       +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>>>> +        uint8_t status;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>>>>>> +            break;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>>>>>> +        cnt++;
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>       static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>                           int page, int raw)
>>>>>>>>>>>>>>       {
>>>>>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>           u32 cmd;
>>>>>>>>>>>>>>           int ret;
>>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>           meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>>>>>             data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>                            NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>>>>>           }
>>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>           cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>>>>>           writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>           meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>           return ret;
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>       @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>>>>>           } while (!ret);
>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>       +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>
>>>>>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>>>>>         ......
>>>>>>>>>>>>>         #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>>>>>
>>>>>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>>>>>
>>>>>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>
>>>>>>>>>>> Which one is correct ?
>>>>>>>>>>
>>>>>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>>>>>> defined incorrectly, so irq waiting does not work?
>>>>>>>>
>>>>>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>>>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Thanks, Arseniy
>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>>>>>         cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>
>>>>>>>>>>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>>>>>                           msecs_to_jiffies(timeout_ms));
>>>>>>>>>>>>>         if (ret == 0)
>>>>>>>>>>>>>             ret = -1;
>>>>>>>>>>>>>
>>>>>>>>>>>>>         writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>         ......
>>>>>>>>>>>>>
>>>>>>>
>>>>>>> Hello Liang!
>>>>>>>
>>>>>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>>>>>
>>>>>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>>>>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>>>>>> NAND_CMD_READ0 for write operation?
>>>>>>
>>>>>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
>>>>>
>>>>> Hm, I found this command in the old driver. For example without it I get the following error:
>>>>>
>>>>> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
>>>>> ECC failed: 3975
>>>>> ECC corrected: 47
>>>>> Number of bad blocks: 10
>>>>> Number of bbt blocks: 0
>>>>> Block size 131072, page size 2048, OOB size 64
>>>>> Dumping data starting at 0x00000000 and ending at 0x00000800...
>>>>>
>>>>> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
>>>>> there are no ECC errors.
>>>
>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device. but previous meson_nfc_queue_rb() only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the data bus and generate IRQ if ready.
>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>>>>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>>>>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>>>>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>>>>>> logic to this driver without any references
>>>>>>
>>>>>> Could you please try the following? i have tested it on another SOC (not axg).
>>>>>>
>>>>>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>>>> {
>>>>>>      u32 cmd, cfg;
>>>>>>      int ret = 0;
>>>>>>
>>>>>>      meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>      meson_nfc_drain_cmd(nfc);
>>>>>>      meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>>>>
>>>>>>      cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>>>>>      cfg |= NFC_RB_IRQ_EN;
>>>>>>      writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>>>>>
>>>>>>      reinit_completion(&nfc->completion);
>>>>>>
>>>>>>      meson_nfc_cmd_idle(nfc, 0);
>>>>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>      meson_nfc_cmd_idle(nfc, 5);
>>>>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>>>>          | nfc->param.chip_select | nfc->timing.tbers_max;
>>>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>      meson_nfc_drain_cmd(nfc);
>>>>>>
>>>>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>                        msecs_to_jiffies(timeout_ms));
>>>>>>      if (ret == 0)
>>>>>>          ret = -1;
>>>>>>
>>>>>>      writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>>>>>
>>>>>>      return ret;
>>>>>> }
>>>>>
>>>>> Ok! Thanks, I'll try it!
>>>>>
>>>>> Thanks, Arseniy
>>>>
>>>> Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
>>>> here is for example output buring bad blocks lookup:
>>>>
>>>> [    2.060835] Scanning device for bad blocks
>>>> [    3.350085] Bad eraseblock 20 at 0x000000280000
>>>> [    3.536389] Freeing initrd memory: 11808K
>>>> [   39.133952] Bad eraseblock 581 at 0x0000048a0000
>>>> [   44.837917] Bad eraseblock 671 at 0x0000053e0000
>>>> [   45.677964] Bad eraseblock 685 at 0x0000055a0000
>>>> [   83.637917] Bad eraseblock 1279 at 0x000009fe0000
>>>> [  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left
>>>>
>>>>
>>>> Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
>>>> #define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
>>>> of dev ready calls from the old vendor's driver.
>>>>
>>>> Thanks, Arseniy
>>>
>>> Yes, there is something wrong. i make a patch and it works on AXG platfrom. please try it. Thanks.
>>
>> Ok, Thank You very much, I'll try it ASAP!
>>
>> Thanks, Arseniy
> 
> Hello @Liang! Sorry for late reply, was a little bit busy. I've tested patch below, seems it
> works! Thank You! So I think I can replace this patch from current patchset with this code!
> 
> Thanks, Arseniy

Hello @Liang! I've added this change to v3 patchset, it works correctly. I've used "Suggested-By" tag.
https://lore.kernel.org/linux-mtd/20230510110835.26115-2-AVKrasnov@sberdevices.ru/T/#u
Thanks for this fix!

Thanks, Arseniy

> 
>>
>>>
>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>> old mode 100644
>>> new mode 100755
>>> index 074e14225c06..529d1a41a4a4
>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>> @@ -37,7 +37,7 @@
>>>  #define NFC_CMD_SCRAMBLER_ENABLE       BIT(19)
>>>  #define NFC_CMD_SCRAMBLER_DISABLE      0
>>>  #define NFC_CMD_SHORTMODE_DISABLE      0
>>> -#define NFC_CMD_RB_INT         BIT(14)
>>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>>
>>>  #define NFC_CMD_GET_SIZE(x)    (((x) >> 22) & GENMASK(4, 0))
>>>
>>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>         }
>>>  }
>>>
>>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>  {
>>>         u32 cmd, cfg;
>>>         int ret = 0;
>>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>
>>>         reinit_completion(&nfc->completion);
>>>
>>> +       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>> +       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +       meson_nfc_cmd_idle(nfc, 5);
>>> +
>>>         /* use the max erase time as the maximum clock for waiting R/B */
>>> -       cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>> -               | nfc->param.chip_select | nfc->timing.tbers_max;
>>> +       cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +       meson_nfc_cmd_idle(nfc, 2);
>>>
>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>                                           msecs_to_jiffies(timeout_ms));
>>>         if (ret == 0)
>>> -               ret = -1;
>>> +               return -1;
>>>
>>> -       return ret;
>>> +       if (!cmd_read0)
>>> +               return 0;
>>> +
>>> +       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
>>> +       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +       meson_nfc_drain_cmd(nfc);
>>> +       meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>> +
>>> +       return 0;
>>>  }
>>>
>>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>         if (in) {
>>>                 nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>>                 writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>>> -               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>>> +               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>>         } else {
>>>                 meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>>         }
>>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>
>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> -       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>> +       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>>
>>>         meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>
>>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>>                         break;
>>>
>>>                 case NAND_OP_WAITRDY_INSTR:
>>> -                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>>> +                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 0);
>>>                         if (instr->delay_ns)
>>>                                 meson_nfc_cmd_idle(nfc, delay_idle);
>>>                         break;
>>>>
>>>>>
>>>>>>
>>>>>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>>>>>
>>>>>>>
>>>>>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>>>>>> handling which is I think more critical?
>>>>>>>
>>>>>>
>>>>>> sure, it is up to you. it is more important, thanks again.
>>>>>>
>>>>>>> Thanks, Arseniy
>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>         Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>>>>>> driver (as You are author of it):
>>>>>>>>>>>>
>>>>>>>>>>>>        OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>>>>>        bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>>>>>        2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>>>>>        32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>>>
>>>>>>>>>>>>>>       static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>>>>>       {
>>>>>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>>>           info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>           ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>>>>>           if (ret)
>>>>>>>>>>>>>>               return ret;
>>>>>>>>>>>>>>       +    ret = meson_nfc_send_read(nand);
>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>           ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>>>>>                            data_len, meson_chip->info_buf,
>>>>>>>>>>>>>>                            info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>           }
>>>>>>>>>>>>>>             ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>           meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
Arseniy Krasnov May 11, 2023, 10:43 a.m. UTC | #18
On 10.05.2023 14:34, Arseniy Krasnov wrote:
> 
> 
> On 04.05.2023 09:16, Arseniy Krasnov wrote:
>>
>>
>> On 26.04.2023 17:47, Arseniy Krasnov wrote:
>>>
>>>
>>> On 26.04.2023 15:17, Liang Yang wrote:
>>>> Hi Arseniy,
>>>>
>>>> On 2023/4/20 3:43, Arseniy Krasnov wrote:
>>>>> [ EXTERNAL EMAIL ]
>>>>>
>>>>> On 17.04.2023 17:10, Arseniy Krasnov wrote:
>>>>>>
>>>>>>
>>>>>> On 17.04.2023 16:54, Liang Yang wrote:
>>>>>>> Hi Arseniy,
>>>>>>>
>>>>>>> On 2023/4/17 14:47, Arseniy Krasnov wrote:
>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 13.04.2023 08:57, Liang Yang wrote:
>>>>>>>>> Hi Arseniy,
>>>>>>>>>
>>>>>>>>> On 2023/4/13 13:10, Arseniy Krasnov wrote:
>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On 12.04.2023 16:30, Liang Yang wrote:
>>>>>>>>>>> Hi,
>>>>>>>>>>>
>>>>>>>>>>> On 2023/4/12 20:03, Arseniy Krasnov wrote:
>>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> On 12.04.2023 13:24, Arseniy Krasnov wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 12.04.2023 12:37, Liang Yang wrote:
>>>>>>>>>>>>>> Hi Arseniy,
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Thanks for pointing out this problem. also comment inline.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On 2023/4/12 14:16, Arseniy Krasnov wrote:
>>>>>>>>>>>>>>> [ EXTERNAL EMAIL ]
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> This fixes read/write functionality. New command sequences were ported
>>>>>>>>>>>>>>> from old vendor's driver. Without this patch driver works unstable. This
>>>>>>>>>>>>>>> change is tested with 'nanddump'/'nandwrite' utilities and mounting
>>>>>>>>>>>>>>> JFFS2 filesystem on AXG family (A113X SoC).
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>>>>>>>>>>>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>       drivers/mtd/nand/raw/meson_nand.c | 116 ++++++++++++++++++++++++++----
>>>>>>>>>>>>>>>       1 file changed, 101 insertions(+), 15 deletions(-)
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>>>> index 074e14225c06..256c37c76526 100644
>>>>>>>>>>>>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>>>> @@ -26,6 +26,7 @@
>>>>>>>>>>>>>>>       #define NFC_CMD_IDLE        (0xc << 14)
>>>>>>>>>>>>>>>       #define NFC_CMD_CLE        (0x5 << 14)
>>>>>>>>>>>>>>>       #define NFC_CMD_ALE        (0x6 << 14)
>>>>>>>>>>>>>>> +#define NFC_CMD_DRD        (0x8 << 14)
>>>>>>>>>>>>>>>       #define NFC_CMD_ADL        ((0 << 16) | (3 << 20))
>>>>>>>>>>>>>>>       #define NFC_CMD_ADH        ((1 << 16) | (3 << 20))
>>>>>>>>>>>>>>>       #define NFC_CMD_AIL        ((2 << 16) | (3 << 20))
>>>>>>>>>>>>>>> @@ -84,6 +85,7 @@
>>>>>>>>>>>>>>>         #define DMA_BUSY_TIMEOUT    0x100000
>>>>>>>>>>>>>>>       #define CMD_FIFO_EMPTY_TIMEOUT    1000
>>>>>>>>>>>>>>> +#define DEVICE_READY_TIMEOUT    1000
>>>>>>>>>>>>>>>         #define MAX_CE_NUM        2
>>>>>>>>>>>>>>>       @@ -255,8 +257,26 @@ static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>>>>>>>>>>>>>>>           }
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>       +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>>>>> +                     unsigned int timeout_ms)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    u32 cmd_size = 0;
>>>>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    /* wait cmd fifo is empty */
>>>>>>>>>>>>>>> +    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>>>>> +                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>>>>> +                     10, timeout_ms * 1000);
>>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>>> +        dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    return ret;
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>       static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>>>>>>>>>>>>       {
>>>>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 0);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>           writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>>>>>>>>>>>>>>>                  nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>> @@ -308,23 +328,9 @@ static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>>>>>>>>>>>>            */
>>>>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>>>>           meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>>>> +    meson_nfc_wait_cmd_finish(nfc, 1000);
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>       -static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>>>>>>>>>>>>>>> -                     unsigned int timeout_ms)
>>>>>>>>>>>>>>> -{
>>>>>>>>>>>>>>> -    u32 cmd_size = 0;
>>>>>>>>>>>>>>> -    int ret;
>>>>>>>>>>>>>>> -
>>>>>>>>>>>>>>> -    /* wait cmd fifo is empty */
>>>>>>>>>>>>>>> -    ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>>>>>>>>>>>>>>> -                     !NFC_CMD_GET_SIZE(cmd_size),
>>>>>>>>>>>>>>> -                     10, timeout_ms * 1000);
>>>>>>>>>>>>>>> -    if (ret)
>>>>>>>>>>>>>>> -        dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
>>>>>>>>>>>>>>> -
>>>>>>>>>>>>>>> -    return ret;
>>>>>>>>>>>>>>> -}
>>>>>>>>>>>>>>>         static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>>>>>>>>>>>>       {
>>>>>>>>>>>>>>> @@ -631,6 +637,48 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>>>>>>>>>>>>           return 0;
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>       +static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>> +    meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    return readl(nfc->reg_base + NFC_REG_BUF);
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>>>>> +    unsigned long cnt = 0;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    meson_nfc_drain_cmd(nfc);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    /* 10 ms. */
>>>>>>>>>>>>>>> +    while (cnt < DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>>>>> +        uint8_t status;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        status = meson_nfc_read_byte(nand);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        if (status & NAND_STATUS_READY)
>>>>>>>>>>>>>>> +            break;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        usleep_range(10, 11);
>>>>>>>>>>>>>>> +        cnt++;
>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (cnt == DEVICE_READY_TIMEOUT) {
>>>>>>>>>>>>>>> +        dev_err(nfc->dev, "device ready timeout\n");
>>>>>>>>>>>>>>> +        return -ETIMEDOUT;
>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>       static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>>                           int page, int raw)
>>>>>>>>>>>>>>>       {
>>>>>>>>>>>>>>> @@ -643,6 +691,10 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>>           u32 cmd;
>>>>>>>>>>>>>>>           int ret;
>>>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>           meson_nfc_select_chip(nand, nand->cur_cs);
>>>>>>>>>>>>>>>             data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>>>> @@ -667,12 +719,20 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>>                            NFC_CMD_SCRAMBLER_DISABLE);
>>>>>>>>>>>>>>>           }
>>>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>           cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>>>>>>>>>>>>           writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>>           meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>           return ret;
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>       @@ -720,6 +780,21 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>>>>>>>>>>>>           } while (!ret);
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>       +static inline int meson_nfc_send_read(struct nand_chip *nand)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>>>>>>>>>>>> +    u32 cs = nfc->param.chip_select;
>>>>>>>>>>>>>>> +    int ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    return 0;
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> it already calls meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute(). Could you implements this in meson_nfc_queue_rb()? and we can use the irq method.
>>>>>>>>>>>>>> also without Ready/Busy pin, the meson_nfc_queue_rb() should change like below:
>>>>>>>>>>>>>>         ......
>>>>>>>>>>>>>>         #define NFC_CMD_RB_INT    ((0xb << 10) | BIT(18))
>>>>>>>>>>>>
>>>>>>>>>>>> Sorry, I can see this define as (and it is used in the driver):
>>>>>>>>>>>> #define NFC_CMD_RB_INT          BIT(14)
>>>>>>>>>>>>
>>>>>>>>>>>> in drivers/mtd/nand/raw/meson_nand.c
>>>>>>>>>>>>
>>>>>>>>>>>> Which one is correct ?
>>>>>>>>>>>
>>>>>>>>>>> we need to modify the define 'NFC_CMD_RB_INT' as ((0xb << 10) | BIT(18)).
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Ok, NFC_CMD_RB_INT - it is "Ready/Busy_Interrupt" ? You suppose that currently it is
>>>>>>>>>> defined incorrectly, so irq waiting does not work?
>>>>>>>>>
>>>>>>>>> Previous defined BIT(14) is for having the external Ready/Busy pin of the NAND device connected to the controller. the new define is for reading status by sending read status(70H) command and read status from the data bus(checking the IO6). the both can work on AXG soc.
>>>>>>>>> when the controller RB command is sent, the controller automatically checks the level of external Ready/Busy pin or the data bus(IO6) periodicity. and generate the irq signal if status is ready.
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 0);
>>>>>>>>>>>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>         meson_nfc_cmd_idle(nfc, 5);
>>>>>>>>>>>>>>         cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>>>>>>>>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>>>>>>>>                           msecs_to_jiffies(timeout_ms));
>>>>>>>>>>>>>>         if (ret == 0)
>>>>>>>>>>>>>>             ret = -1;
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>         writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
>>>>>>>>>>>>>>         ......
>>>>>>>>>>>>>>
>>>>>>>>
>>>>>>>> Hello Liang!
>>>>>>>>
>>>>>>>> I've got small questions to clarify Your comment. You suggest two thing IIUC:
>>>>>>>>
>>>>>>>> 1) Send NAND_CMD_READ0 from 'meson_nfc_queue_rb()'. This means that extra argument is needed to
>>>>>>>> 'meson_nfc_queue_rb()' which shows that read operation is going to be performed. We can't send
>>>>>>>> NAND_CMD_READ0 for write operation?
>>>>>>>
>>>>>>> it is ok to me. but does NAND_CMD_READ0 really need to send in the controller driver? i don't find the other controller drivers have to send it for the old vendor NAND device.
>>>>>>
>>>>>> Hm, I found this command in the old driver. For example without it I get the following error:
>>>>>>
>>>>>> # nanddump -c -s 0 -l 2048 /dev/mtd0 --oob
>>>>>> ECC failed: 3975
>>>>>> ECC corrected: 47
>>>>>> Number of bad blocks: 10
>>>>>> Number of bbt blocks: 0
>>>>>> Block size 131072, page size 2048, OOB size 64
>>>>>> Dumping data starting at 0x00000000 and ending at 0x00000800...
>>>>>>
>>>>>> But data is not corrupted and seems ok. With this extra NAND_CMD_READ0 everything is ok - data is still valid and
>>>>>> there are no ECC errors.
>>>>
>>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device. but previous meson_nfc_queue_rb() only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the data bus and generate IRQ if ready.
>>>>
>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> 2) About code and define above, I tried to replace current body of 'meson_nfc_queue_rb()', but it
>>>>>>>> didn't work. May be I did it wrong, because what to do with 'meson_nfc_wait_dev_ready()' and it's
>>>>>>>> call sites? It must be removed? Could You please explain Your idea in more details? I'm asking You
>>>>>>>> because I don't have doc for this NAND controller, so it is very difficult to me to add valid
>>>>>>>> logic to this driver without any references
>>>>>>>
>>>>>>> Could you please try the following? i have tested it on another SOC (not axg).
>>>>>>>
>>>>>>> static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>>>>> {
>>>>>>>      u32 cmd, cfg;
>>>>>>>      int ret = 0;
>>>>>>>
>>>>>>>      meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>>>>>>>      meson_nfc_drain_cmd(nfc);
>>>>>>>      meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>>>>>
>>>>>>>      cfg = readl(nfc->reg_base + NFC_REG_CFG);
>>>>>>>      cfg |= NFC_RB_IRQ_EN;
>>>>>>>      writel(cfg, nfc->reg_base + NFC_REG_CFG);
>>>>>>>
>>>>>>>      reinit_completion(&nfc->completion);
>>>>>>>
>>>>>>>      meson_nfc_cmd_idle(nfc, 0);
>>>>>>>      cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>      meson_nfc_cmd_idle(nfc, 5);
>>>>>>>      cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>>>>>          | nfc->param.chip_select | nfc->timing.tbers_max;
>>>>>>>      writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>>>>      meson_nfc_drain_cmd(nfc);
>>>>>>>
>>>>>>>      ret = wait_for_completion_timeout(&nfc->completion,
>>>>>>>                        msecs_to_jiffies(timeout_ms));
>>>>>>>      if (ret == 0)
>>>>>>>          ret = -1;
>>>>>>>
>>>>>>>      writel(1 << 31, nfc->reg_base + NFC_REG_CMD);
>>>>>>>
>>>>>>>      return ret;
>>>>>>> }
>>>>>>
>>>>>> Ok! Thanks, I'll try it!
>>>>>>
>>>>>> Thanks, Arseniy
>>>>>
>>>>> Hello @Liang, I tried this code, seems with this implementation NAND driver works very slow,
>>>>> here is for example output buring bad blocks lookup:
>>>>>
>>>>> [    2.060835] Scanning device for bad blocks
>>>>> [    3.350085] Bad eraseblock 20 at 0x000000280000
>>>>> [    3.536389] Freeing initrd memory: 11808K
>>>>> [   39.133952] Bad eraseblock 581 at 0x0000048a0000
>>>>> [   44.837917] Bad eraseblock 671 at 0x0000053e0000
>>>>> [   45.677964] Bad eraseblock 685 at 0x0000055a0000
>>>>> [   83.637917] Bad eraseblock 1279 at 0x000009fe0000
>>>>> [  132.833318] modprobe (56) used greatest stack depth: 12672 bytes left
>>>>>
>>>>>
>>>>> Take a look at timeouts. I used Your variant of 'meson_nfc_queue_rb()' from above,
>>>>> #define NFC_CMD_RB_INT is ((0xb << 10) | BIT(18)). I tested it with my ports of
>>>>> of dev ready calls from the old vendor's driver.
>>>>>
>>>>> Thanks, Arseniy
>>>>
>>>> Yes, there is something wrong. i make a patch and it works on AXG platfrom. please try it. Thanks.
>>>
>>> Ok, Thank You very much, I'll try it ASAP!
>>>
>>> Thanks, Arseniy
>>
>> Hello @Liang! Sorry for late reply, was a little bit busy. I've tested patch below, seems it
>> works! Thank You! So I think I can replace this patch from current patchset with this code!
>>
>> Thanks, Arseniy
> 
> Hello @Liang! I've added this change to v3 patchset, it works correctly. I've used "Suggested-By" tag.
> https://lore.kernel.org/linux-mtd/20230510110835.26115-2-AVKrasnov@sberdevices.ru/T/#u
> Thanks for this fix!
> 
> Thanks, Arseniy

Small remark @Liang, i think this update is also needed:

@@ -1100,7 +1106,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
                        break;
 
                case NAND_OP_WAITRDY_INSTR:
-                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 0);
+                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
                        if (instr->delay_ns)
                                meson_nfc_cmd_idle(nfc, delay_idle);
                        break;

Otherwise i get this error:

[    1.732699] Could not find a valid ONFI parameter page, trying bit-wise majority to recover it
[    1.735961] ONFI parameter recovery failed, aborting
[    1.741043] nand: device found, Manufacturer ID: 0xc2, Chip ID: 0xda
[    1.747002] nand: Macronix NAND 256MiB 3,3V 8-bit
[    1.751674] nand: 256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64

And with change above:

[    1.731880] nand: device found, Manufacturer ID: 0xc2, Chip ID: 0xda
[    1.732666] nand: Macronix MX30LF2G18AC
[    1.736926] nand: 256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64

What do You think?

Thanks, Arseniy

> 
>>
>>>
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> old mode 100644
>>>> new mode 100755
>>>> index 074e14225c06..529d1a41a4a4
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -37,7 +37,7 @@
>>>>  #define NFC_CMD_SCRAMBLER_ENABLE       BIT(19)
>>>>  #define NFC_CMD_SCRAMBLER_DISABLE      0
>>>>  #define NFC_CMD_SHORTMODE_DISABLE      0
>>>> -#define NFC_CMD_RB_INT         BIT(14)
>>>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>>>
>>>>  #define NFC_CMD_GET_SIZE(x)    (((x) >> 22) & GENMASK(4, 0))
>>>>
>>>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>         }
>>>>  }
>>>>
>>>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>>  {
>>>>         u32 cmd, cfg;
>>>>         int ret = 0;
>>>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>>
>>>>         reinit_completion(&nfc->completion);
>>>>
>>>> +       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>>> +       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>> +       meson_nfc_cmd_idle(nfc, 5);
>>>> +
>>>>         /* use the max erase time as the maximum clock for waiting R/B */
>>>> -       cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>>> -               | nfc->param.chip_select | nfc->timing.tbers_max;
>>>> +       cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>> +       meson_nfc_cmd_idle(nfc, 2);
>>>>
>>>>         ret = wait_for_completion_timeout(&nfc->completion,
>>>>                                           msecs_to_jiffies(timeout_ms));
>>>>         if (ret == 0)
>>>> -               ret = -1;
>>>> +               return -1;
>>>>
>>>> -       return ret;
>>>> +       if (!cmd_read0)
>>>> +               return 0;
>>>> +
>>>> +       cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
>>>> +       writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>> +       meson_nfc_drain_cmd(nfc);
>>>> +       meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>>> +
>>>> +       return 0;
>>>>  }
>>>>
>>>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>>         if (in) {
>>>>                 nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>>>                 writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>>>> -               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>>>> +               meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>>>         } else {
>>>>                 meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>>>         }
>>>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>
>>>>         cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>         writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>> -       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>>> +       meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>>>
>>>>         meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>>
>>>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>>>                         break;
>>>>
>>>>                 case NAND_OP_WAITRDY_INSTR:
>>>> -                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>>>> +                       meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 0);
>>>>                         if (instr->delay_ns)
>>>>                                 meson_nfc_cmd_idle(nfc, delay_idle);
>>>>                         break;
>>>>>
>>>>>>
>>>>>>>
>>>>>>> also we need to check and return the return value for meson_nfc_queue_rb() in meson_nfc_rw_cmd_prepare_and_execute() and meson_nfc_write_page_sub().
>>>>>>>
>>>>>>>>
>>>>>>>> May be I can send v2 patchset for review without this change, as v2 already includes udpate for OOB
>>>>>>>> handling which is I think more critical?
>>>>>>>>
>>>>>>>
>>>>>>> sure, it is up to you. it is more important, thanks again.
>>>>>>>
>>>>>>>> Thanks, Arseniy
>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>         Thanks for reply! I'll try this code! One more question about OOB processing in this
>>>>>>>>>>>>> driver (as You are author of it):
>>>>>>>>>>>>>
>>>>>>>>>>>>>        OOB size is 64 bytes, but for example if I have 1K ECC, 2 bytes user bytes and 14
>>>>>>>>>>>>>        bytes for ECC code for each 1K. In this case I have access to only 32 bytes of OOB:
>>>>>>>>>>>>>        2 x (2 user bytes + 14 ECC bytes). Correct me if i'm wrong, but rest of OOB (next
>>>>>>>>>>>>>        32 bytes) become unavailable (in both raw and ECC modes) ?
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thanks, Arseniy
>>>>>>>>>>>>>
>>>>>>>>>>>>>>>       static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>>                          int page, int raw)
>>>>>>>>>>>>>>>       {
>>>>>>>>>>>>>>> @@ -734,10 +809,18 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>>           data_len =  mtd->writesize + mtd->oobsize;
>>>>>>>>>>>>>>>           info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>>>>>>>>>>>>       +    ret = meson_nfc_wait_dev_ready(nand);
>>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>           ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>>>>>>>>>>>>>>>           if (ret)
>>>>>>>>>>>>>>>               return ret;
>>>>>>>>>>>>>>>       +    ret = meson_nfc_send_read(nand);
>>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>           ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>>>>>>>>>>>>                            data_len, meson_chip->info_buf,
>>>>>>>>>>>>>>>                            info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>>>>> @@ -754,6 +837,9 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>>>>>>>>>>>>           }
>>>>>>>>>>>>>>>             ret = meson_nfc_wait_dma_finish(nfc);
>>>>>>>>>>>>>>> +    if (ret)
>>>>>>>>>>>>>>> +        return ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>           meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>>>>>>>>>>>>             meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
diff mbox series

Patch

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 074e14225c06..256c37c76526 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -26,6 +26,7 @@ 
 #define NFC_CMD_IDLE		(0xc << 14)
 #define NFC_CMD_CLE		(0x5 << 14)
 #define NFC_CMD_ALE		(0x6 << 14)
+#define NFC_CMD_DRD		(0x8 << 14)
 #define NFC_CMD_ADL		((0 << 16) | (3 << 20))
 #define NFC_CMD_ADH		((1 << 16) | (3 << 20))
 #define NFC_CMD_AIL		((2 << 16) | (3 << 20))
@@ -84,6 +85,7 @@ 
 
 #define DMA_BUSY_TIMEOUT	0x100000
 #define CMD_FIFO_EMPTY_TIMEOUT	1000
+#define DEVICE_READY_TIMEOUT	1000
 
 #define MAX_CE_NUM		2
 
@@ -255,8 +257,26 @@  static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
 	}
 }
 
+static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
+				     unsigned int timeout_ms)
+{
+	u32 cmd_size = 0;
+	int ret;
+
+	/* wait cmd fifo is empty */
+	ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
+					 !NFC_CMD_GET_SIZE(cmd_size),
+					 10, timeout_ms * 1000);
+	if (ret)
+		dev_err(nfc->dev, "wait for empty CMD FIFO timed out\n");
+
+	return ret;
+}
+
 static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
 {
+	meson_nfc_wait_cmd_finish(nfc, 0);
+
 	writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
 	       nfc->reg_base + NFC_REG_CMD);
 }
@@ -308,23 +328,9 @@  static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
 	 */
 	meson_nfc_cmd_idle(nfc, 0);
 	meson_nfc_cmd_idle(nfc, 0);
+	meson_nfc_wait_cmd_finish(nfc, 1000);
 }
 
-static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
-				     unsigned int timeout_ms)
-{
-	u32 cmd_size = 0;
-	int ret;
-
-	/* wait cmd fifo is empty */
-	ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
-					 !NFC_CMD_GET_SIZE(cmd_size),
-					 10, timeout_ms * 1000);
-	if (ret)
-		dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
-
-	return ret;
-}
 
 static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
 {
@@ -631,6 +637,48 @@  static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	return 0;
 }
 
+static uint8_t meson_nfc_read_byte(struct nand_chip *nand)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+	writel(NFC_CMD_DRD, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, nfc->timing.twb);
+	meson_nfc_drain_cmd(nfc);
+
+	return readl(nfc->reg_base + NFC_REG_BUF);
+}
+
+static int meson_nfc_wait_dev_ready(struct nand_chip *nand)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	u32 cs = nfc->param.chip_select;
+	unsigned long cnt = 0;
+
+	meson_nfc_drain_cmd(nfc);
+
+	writel(cs | NFC_CMD_CLE | NAND_CMD_STATUS, nfc->reg_base + NFC_REG_CMD);
+
+	/* 10 ms. */
+	while (cnt < DEVICE_READY_TIMEOUT) {
+		uint8_t status;
+
+		status = meson_nfc_read_byte(nand);
+
+		if (status & NAND_STATUS_READY)
+			break;
+
+		usleep_range(10, 11);
+		cnt++;
+	}
+
+	if (cnt == DEVICE_READY_TIMEOUT) {
+		dev_err(nfc->dev, "device ready timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
 static int meson_nfc_write_page_sub(struct nand_chip *nand,
 				    int page, int raw)
 {
@@ -643,6 +691,10 @@  static int meson_nfc_write_page_sub(struct nand_chip *nand,
 	u32 cmd;
 	int ret;
 
+	ret = meson_nfc_wait_dev_ready(nand);
+	if (ret)
+		return ret;
+
 	meson_nfc_select_chip(nand, nand->cur_cs);
 
 	data_len =  mtd->writesize + mtd->oobsize;
@@ -667,12 +719,20 @@  static int meson_nfc_write_page_sub(struct nand_chip *nand,
 				     NFC_CMD_SCRAMBLER_DISABLE);
 	}
 
+	ret = meson_nfc_wait_dma_finish(nfc);
+	if (ret)
+		return ret;
+
 	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
 
 	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
 
+	ret = meson_nfc_wait_dev_ready(nand);
+	if (ret)
+		return ret;
+
 	return ret;
 }
 
@@ -720,6 +780,21 @@  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
 	} while (!ret);
 }
 
+static inline int meson_nfc_send_read(struct nand_chip *nand)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	u32 cs = nfc->param.chip_select;
+	int ret;
+
+	ret = meson_nfc_wait_dev_ready(nand);
+	if (ret)
+		return ret;
+
+	writel(cs | NFC_CMD_CLE | NAND_CMD_READ0, nfc->reg_base + NFC_REG_CMD);
+
+	return 0;
+}
+
 static int meson_nfc_read_page_sub(struct nand_chip *nand,
 				   int page, int raw)
 {
@@ -734,10 +809,18 @@  static int meson_nfc_read_page_sub(struct nand_chip *nand,
 	data_len =  mtd->writesize + mtd->oobsize;
 	info_len = nand->ecc.steps * PER_INFO_BYTE;
 
+	ret = meson_nfc_wait_dev_ready(nand);
+	if (ret)
+		return ret;
+
 	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
 	if (ret)
 		return ret;
 
+	ret = meson_nfc_send_read(nand);
+	if (ret)
+		return ret;
+
 	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
 					 data_len, meson_chip->info_buf,
 					 info_len, DMA_FROM_DEVICE);
@@ -754,6 +837,9 @@  static int meson_nfc_read_page_sub(struct nand_chip *nand,
 	}
 
 	ret = meson_nfc_wait_dma_finish(nfc);
+	if (ret)
+		return ret;
+
 	meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
 
 	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);