From: Miquel Raynal <miquel.raynal@bootlin.com>
To: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
Cc: Liang Yang <liang.yang@amlogic.com>,
Richard Weinberger <richard@nod.at>,
Vignesh Raghavendra <vigneshr@ti.com>,
Neil Armstrong <neil.armstrong@linaro.org>,
Kevin Hilman <khilman@baylibre.com>,
Jerome Brunet <jbrunet@baylibre.com>,
Martin Blumenstingl <martin.blumenstingl@googlemail.com>,
<oxffffaa@gmail.com>, <kernel@sberdevices.ru>,
<linux-mtd@lists.infradead.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-amlogic@lists.infradead.org>,
<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2] mtd: rawnand: meson: waiting w/o wired ready/busy pin
Date: Wed, 7 Jun 2023 10:02:57 +0200 [thread overview]
Message-ID: <20230607100257.627fc438@xps-13> (raw)
In-Reply-To: <20230607073015.1280085-1-AVKrasnov@sberdevices.ru>
Hi Arseniy,
AVKrasnov@sberdevices.ru wrote on Wed, 7 Jun 2023 10:30:15 +0300:
> If there is no wired ready/busy pin, classic way to wait for command
> completion is to use function 'nand_soft_waitrdy()'. Meson NAND has
> special command which allows to wait for NAND_STATUS_READY bit without
> reading status in a software loop (as 'nand_soft_waitrdy()' does). To
> use it send this command along with NAND_CMD_STATUS, then wait for an
> interrupt, and after interrupt send NAND_CMD_READ0. So this feature
> allows to use interrupt driven waiting without wired ready/busy pin.
>
> Suggested-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
> Changelog:
> v1 -> v2:
> * Remove small delays from 'meson_nfc_wait_no_rb_pin()'. Both have no
> effect according Liang's message.
> * Type of 'no_rb_pin' is u32, the same as for 'nand-rb' property.
> * 'meson_nfc_wait_no_rb_pin()' doesn't send NAND_CMD_READ0 in case of
> page programming. Extra argument is added to 'meson_nfc_queue_rb()'
> to check that case.
>
> drivers/mtd/nand/raw/meson_nand.c | 74 +++++++++++++++++++++++++++++--
> 1 file changed, 70 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..ae404655b68c 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -38,6 +38,7 @@
> #define NFC_CMD_SCRAMBLER_DISABLE 0
> #define NFC_CMD_SHORTMODE_DISABLE 0
> #define NFC_CMD_RB_INT BIT(14)
> +#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16))
>
> #define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0))
>
> @@ -179,6 +180,7 @@ struct meson_nfc {
> u32 info_bytes;
>
> unsigned long assigned_cs;
> + u32 no_rb_pin;
> };
>
> enum {
> @@ -392,7 +394,42 @@ 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_wait_no_rb_pin(struct meson_nfc *nfc, int timeout_ms,
> + bool need_cmd_read0)
> +{
> + u32 cmd, cfg;
> +
> + 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);
> + cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> + /* use the max erase time as the maximum clock for waiting R/B */
> + cmd = NFC_CMD_RB | NFC_CMD_RB_INT_NO_PIN | nfc->timing.tbers_max;
> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> + if (!wait_for_completion_timeout(&nfc->completion,
> + msecs_to_jiffies(timeout_ms)))
> + return -ETIMEDOUT;
> +
> + if (need_cmd_read0) {
> + 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 int meson_nfc_wait_rb_pin(struct meson_nfc *nfc, int timeout_ms)
> {
> u32 cmd, cfg;
> int ret = 0;
> @@ -420,6 +457,25 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> return ret;
> }
>
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms,
> + bool need_cmd_read0)
> +{
> + if (nfc->no_rb_pin) {
> + /* This mode is used when there is no wired R/B pin.
> + * It works like 'nand_soft_waitrdy()', but instead of
> + * polling NAND_CMD_STATUS bit in the software loop,
> + * it will wait for interrupt - controllers checks IO
> + * bus and when it detects NAND_CMD_STATUS on it, it
> + * raises interrupt. After interrupt, NAND_CMD_READ0 is
> + * sent as terminator of the ready waiting procedure.
Please also tell us in which case this is needed/not needed.
> + */
> + return meson_nfc_wait_no_rb_pin(nfc, timeout_ms,
> + need_cmd_read0);
> + } else {
> + return meson_nfc_wait_rb_pin(nfc, timeout_ms);
> + }
> +}
> +
> static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> {
> struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> @@ -623,7 +679,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), true);
> } else {
> meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
> }
> @@ -669,7 +725,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), false);
>
> meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>
> @@ -952,7 +1008,8 @@ 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,
> + true);
> if (instr->delay_ns)
> meson_nfc_cmd_idle(nfc, delay_idle);
> break;
> @@ -1412,6 +1469,15 @@ static int meson_nfc_probe(struct platform_device *pdev)
> return ret;
> }
>
> + ret = of_property_read_u32(dev->of_node, "nand-rb", &nfc->no_rb_pin);
> + if (ret) {
> + /* If property was not found, don't use rb pin. */
> + if (ret == -EINVAL)
> + nfc->no_rb_pin = 1;
You mix two things:
- the presence of the RB pin
- the RB line
Just turn no_rb_pin into a boolean but d'ont use it to read the
property. Use another local u32 and just check the value is 0. If the
value is not 0, then return -EINVAL.
Also you should constrain the dt property in the binding to be either
absent or 0 but nothing else, with something like:
maxItems: 1
minimum: 0
maximum: 0
> + else
> + return ret;
> + }
> +
> writel(0, nfc->reg_base + NFC_REG_CFG);
> ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
> if (ret) {
Thanks,
Miquèl
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
WARNING: multiple messages have this Message-ID (diff)
From: Miquel Raynal <miquel.raynal@bootlin.com>
To: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
Cc: Liang Yang <liang.yang@amlogic.com>,
Richard Weinberger <richard@nod.at>,
Vignesh Raghavendra <vigneshr@ti.com>,
Neil Armstrong <neil.armstrong@linaro.org>,
Kevin Hilman <khilman@baylibre.com>,
Jerome Brunet <jbrunet@baylibre.com>,
Martin Blumenstingl <martin.blumenstingl@googlemail.com>,
<oxffffaa@gmail.com>, <kernel@sberdevices.ru>,
<linux-mtd@lists.infradead.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-amlogic@lists.infradead.org>,
<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2] mtd: rawnand: meson: waiting w/o wired ready/busy pin
Date: Wed, 7 Jun 2023 10:02:57 +0200 [thread overview]
Message-ID: <20230607100257.627fc438@xps-13> (raw)
In-Reply-To: <20230607073015.1280085-1-AVKrasnov@sberdevices.ru>
Hi Arseniy,
AVKrasnov@sberdevices.ru wrote on Wed, 7 Jun 2023 10:30:15 +0300:
> If there is no wired ready/busy pin, classic way to wait for command
> completion is to use function 'nand_soft_waitrdy()'. Meson NAND has
> special command which allows to wait for NAND_STATUS_READY bit without
> reading status in a software loop (as 'nand_soft_waitrdy()' does). To
> use it send this command along with NAND_CMD_STATUS, then wait for an
> interrupt, and after interrupt send NAND_CMD_READ0. So this feature
> allows to use interrupt driven waiting without wired ready/busy pin.
>
> Suggested-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
> Changelog:
> v1 -> v2:
> * Remove small delays from 'meson_nfc_wait_no_rb_pin()'. Both have no
> effect according Liang's message.
> * Type of 'no_rb_pin' is u32, the same as for 'nand-rb' property.
> * 'meson_nfc_wait_no_rb_pin()' doesn't send NAND_CMD_READ0 in case of
> page programming. Extra argument is added to 'meson_nfc_queue_rb()'
> to check that case.
>
> drivers/mtd/nand/raw/meson_nand.c | 74 +++++++++++++++++++++++++++++--
> 1 file changed, 70 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..ae404655b68c 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -38,6 +38,7 @@
> #define NFC_CMD_SCRAMBLER_DISABLE 0
> #define NFC_CMD_SHORTMODE_DISABLE 0
> #define NFC_CMD_RB_INT BIT(14)
> +#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16))
>
> #define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0))
>
> @@ -179,6 +180,7 @@ struct meson_nfc {
> u32 info_bytes;
>
> unsigned long assigned_cs;
> + u32 no_rb_pin;
> };
>
> enum {
> @@ -392,7 +394,42 @@ 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_wait_no_rb_pin(struct meson_nfc *nfc, int timeout_ms,
> + bool need_cmd_read0)
> +{
> + u32 cmd, cfg;
> +
> + 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);
> + cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> + /* use the max erase time as the maximum clock for waiting R/B */
> + cmd = NFC_CMD_RB | NFC_CMD_RB_INT_NO_PIN | nfc->timing.tbers_max;
> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> + if (!wait_for_completion_timeout(&nfc->completion,
> + msecs_to_jiffies(timeout_ms)))
> + return -ETIMEDOUT;
> +
> + if (need_cmd_read0) {
> + 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 int meson_nfc_wait_rb_pin(struct meson_nfc *nfc, int timeout_ms)
> {
> u32 cmd, cfg;
> int ret = 0;
> @@ -420,6 +457,25 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> return ret;
> }
>
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms,
> + bool need_cmd_read0)
> +{
> + if (nfc->no_rb_pin) {
> + /* This mode is used when there is no wired R/B pin.
> + * It works like 'nand_soft_waitrdy()', but instead of
> + * polling NAND_CMD_STATUS bit in the software loop,
> + * it will wait for interrupt - controllers checks IO
> + * bus and when it detects NAND_CMD_STATUS on it, it
> + * raises interrupt. After interrupt, NAND_CMD_READ0 is
> + * sent as terminator of the ready waiting procedure.
Please also tell us in which case this is needed/not needed.
> + */
> + return meson_nfc_wait_no_rb_pin(nfc, timeout_ms,
> + need_cmd_read0);
> + } else {
> + return meson_nfc_wait_rb_pin(nfc, timeout_ms);
> + }
> +}
> +
> static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> {
> struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> @@ -623,7 +679,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), true);
> } else {
> meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
> }
> @@ -669,7 +725,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), false);
>
> meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>
> @@ -952,7 +1008,8 @@ 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,
> + true);
> if (instr->delay_ns)
> meson_nfc_cmd_idle(nfc, delay_idle);
> break;
> @@ -1412,6 +1469,15 @@ static int meson_nfc_probe(struct platform_device *pdev)
> return ret;
> }
>
> + ret = of_property_read_u32(dev->of_node, "nand-rb", &nfc->no_rb_pin);
> + if (ret) {
> + /* If property was not found, don't use rb pin. */
> + if (ret == -EINVAL)
> + nfc->no_rb_pin = 1;
You mix two things:
- the presence of the RB pin
- the RB line
Just turn no_rb_pin into a boolean but d'ont use it to read the
property. Use another local u32 and just check the value is 0. If the
value is not 0, then return -EINVAL.
Also you should constrain the dt property in the binding to be either
absent or 0 but nothing else, with something like:
maxItems: 1
minimum: 0
maximum: 0
> + else
> + return ret;
> + }
> +
> writel(0, nfc->reg_base + NFC_REG_CFG);
> ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
> if (ret) {
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
WARNING: multiple messages have this Message-ID (diff)
From: Miquel Raynal <miquel.raynal@bootlin.com>
To: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
Cc: Liang Yang <liang.yang@amlogic.com>,
Richard Weinberger <richard@nod.at>,
Vignesh Raghavendra <vigneshr@ti.com>,
Neil Armstrong <neil.armstrong@linaro.org>,
Kevin Hilman <khilman@baylibre.com>,
Jerome Brunet <jbrunet@baylibre.com>,
Martin Blumenstingl <martin.blumenstingl@googlemail.com>,
<oxffffaa@gmail.com>, <kernel@sberdevices.ru>,
<linux-mtd@lists.infradead.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-amlogic@lists.infradead.org>,
<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2] mtd: rawnand: meson: waiting w/o wired ready/busy pin
Date: Wed, 7 Jun 2023 10:02:57 +0200 [thread overview]
Message-ID: <20230607100257.627fc438@xps-13> (raw)
In-Reply-To: <20230607073015.1280085-1-AVKrasnov@sberdevices.ru>
Hi Arseniy,
AVKrasnov@sberdevices.ru wrote on Wed, 7 Jun 2023 10:30:15 +0300:
> If there is no wired ready/busy pin, classic way to wait for command
> completion is to use function 'nand_soft_waitrdy()'. Meson NAND has
> special command which allows to wait for NAND_STATUS_READY bit without
> reading status in a software loop (as 'nand_soft_waitrdy()' does). To
> use it send this command along with NAND_CMD_STATUS, then wait for an
> interrupt, and after interrupt send NAND_CMD_READ0. So this feature
> allows to use interrupt driven waiting without wired ready/busy pin.
>
> Suggested-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
> Changelog:
> v1 -> v2:
> * Remove small delays from 'meson_nfc_wait_no_rb_pin()'. Both have no
> effect according Liang's message.
> * Type of 'no_rb_pin' is u32, the same as for 'nand-rb' property.
> * 'meson_nfc_wait_no_rb_pin()' doesn't send NAND_CMD_READ0 in case of
> page programming. Extra argument is added to 'meson_nfc_queue_rb()'
> to check that case.
>
> drivers/mtd/nand/raw/meson_nand.c | 74 +++++++++++++++++++++++++++++--
> 1 file changed, 70 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..ae404655b68c 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -38,6 +38,7 @@
> #define NFC_CMD_SCRAMBLER_DISABLE 0
> #define NFC_CMD_SHORTMODE_DISABLE 0
> #define NFC_CMD_RB_INT BIT(14)
> +#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16))
>
> #define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0))
>
> @@ -179,6 +180,7 @@ struct meson_nfc {
> u32 info_bytes;
>
> unsigned long assigned_cs;
> + u32 no_rb_pin;
> };
>
> enum {
> @@ -392,7 +394,42 @@ 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_wait_no_rb_pin(struct meson_nfc *nfc, int timeout_ms,
> + bool need_cmd_read0)
> +{
> + u32 cmd, cfg;
> +
> + 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);
> + cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> + /* use the max erase time as the maximum clock for waiting R/B */
> + cmd = NFC_CMD_RB | NFC_CMD_RB_INT_NO_PIN | nfc->timing.tbers_max;
> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> + if (!wait_for_completion_timeout(&nfc->completion,
> + msecs_to_jiffies(timeout_ms)))
> + return -ETIMEDOUT;
> +
> + if (need_cmd_read0) {
> + 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 int meson_nfc_wait_rb_pin(struct meson_nfc *nfc, int timeout_ms)
> {
> u32 cmd, cfg;
> int ret = 0;
> @@ -420,6 +457,25 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> return ret;
> }
>
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms,
> + bool need_cmd_read0)
> +{
> + if (nfc->no_rb_pin) {
> + /* This mode is used when there is no wired R/B pin.
> + * It works like 'nand_soft_waitrdy()', but instead of
> + * polling NAND_CMD_STATUS bit in the software loop,
> + * it will wait for interrupt - controllers checks IO
> + * bus and when it detects NAND_CMD_STATUS on it, it
> + * raises interrupt. After interrupt, NAND_CMD_READ0 is
> + * sent as terminator of the ready waiting procedure.
Please also tell us in which case this is needed/not needed.
> + */
> + return meson_nfc_wait_no_rb_pin(nfc, timeout_ms,
> + need_cmd_read0);
> + } else {
> + return meson_nfc_wait_rb_pin(nfc, timeout_ms);
> + }
> +}
> +
> static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> {
> struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> @@ -623,7 +679,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), true);
> } else {
> meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
> }
> @@ -669,7 +725,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), false);
>
> meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>
> @@ -952,7 +1008,8 @@ 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,
> + true);
> if (instr->delay_ns)
> meson_nfc_cmd_idle(nfc, delay_idle);
> break;
> @@ -1412,6 +1469,15 @@ static int meson_nfc_probe(struct platform_device *pdev)
> return ret;
> }
>
> + ret = of_property_read_u32(dev->of_node, "nand-rb", &nfc->no_rb_pin);
> + if (ret) {
> + /* If property was not found, don't use rb pin. */
> + if (ret == -EINVAL)
> + nfc->no_rb_pin = 1;
You mix two things:
- the presence of the RB pin
- the RB line
Just turn no_rb_pin into a boolean but d'ont use it to read the
property. Use another local u32 and just check the value is 0. If the
value is not 0, then return -EINVAL.
Also you should constrain the dt property in the binding to be either
absent or 0 but nothing else, with something like:
maxItems: 1
minimum: 0
maximum: 0
> + else
> + return ret;
> + }
> +
> writel(0, nfc->reg_base + NFC_REG_CFG);
> ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
> if (ret) {
Thanks,
Miquèl
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
WARNING: multiple messages have this Message-ID (diff)
From: Miquel Raynal <miquel.raynal@bootlin.com>
To: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
Cc: Liang Yang <liang.yang@amlogic.com>,
Richard Weinberger <richard@nod.at>,
Vignesh Raghavendra <vigneshr@ti.com>,
Neil Armstrong <neil.armstrong@linaro.org>,
Kevin Hilman <khilman@baylibre.com>,
Jerome Brunet <jbrunet@baylibre.com>,
Martin Blumenstingl <martin.blumenstingl@googlemail.com>,
<oxffffaa@gmail.com>, <kernel@sberdevices.ru>,
<linux-mtd@lists.infradead.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-amlogic@lists.infradead.org>,
<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2] mtd: rawnand: meson: waiting w/o wired ready/busy pin
Date: Wed, 7 Jun 2023 10:02:57 +0200 [thread overview]
Message-ID: <20230607100257.627fc438@xps-13> (raw)
In-Reply-To: <20230607073015.1280085-1-AVKrasnov@sberdevices.ru>
Hi Arseniy,
AVKrasnov@sberdevices.ru wrote on Wed, 7 Jun 2023 10:30:15 +0300:
> If there is no wired ready/busy pin, classic way to wait for command
> completion is to use function 'nand_soft_waitrdy()'. Meson NAND has
> special command which allows to wait for NAND_STATUS_READY bit without
> reading status in a software loop (as 'nand_soft_waitrdy()' does). To
> use it send this command along with NAND_CMD_STATUS, then wait for an
> interrupt, and after interrupt send NAND_CMD_READ0. So this feature
> allows to use interrupt driven waiting without wired ready/busy pin.
>
> Suggested-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
> Changelog:
> v1 -> v2:
> * Remove small delays from 'meson_nfc_wait_no_rb_pin()'. Both have no
> effect according Liang's message.
> * Type of 'no_rb_pin' is u32, the same as for 'nand-rb' property.
> * 'meson_nfc_wait_no_rb_pin()' doesn't send NAND_CMD_READ0 in case of
> page programming. Extra argument is added to 'meson_nfc_queue_rb()'
> to check that case.
>
> drivers/mtd/nand/raw/meson_nand.c | 74 +++++++++++++++++++++++++++++--
> 1 file changed, 70 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..ae404655b68c 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -38,6 +38,7 @@
> #define NFC_CMD_SCRAMBLER_DISABLE 0
> #define NFC_CMD_SHORTMODE_DISABLE 0
> #define NFC_CMD_RB_INT BIT(14)
> +#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16))
>
> #define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0))
>
> @@ -179,6 +180,7 @@ struct meson_nfc {
> u32 info_bytes;
>
> unsigned long assigned_cs;
> + u32 no_rb_pin;
> };
>
> enum {
> @@ -392,7 +394,42 @@ 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_wait_no_rb_pin(struct meson_nfc *nfc, int timeout_ms,
> + bool need_cmd_read0)
> +{
> + u32 cmd, cfg;
> +
> + 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);
> + cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> + /* use the max erase time as the maximum clock for waiting R/B */
> + cmd = NFC_CMD_RB | NFC_CMD_RB_INT_NO_PIN | nfc->timing.tbers_max;
> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> + if (!wait_for_completion_timeout(&nfc->completion,
> + msecs_to_jiffies(timeout_ms)))
> + return -ETIMEDOUT;
> +
> + if (need_cmd_read0) {
> + 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 int meson_nfc_wait_rb_pin(struct meson_nfc *nfc, int timeout_ms)
> {
> u32 cmd, cfg;
> int ret = 0;
> @@ -420,6 +457,25 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> return ret;
> }
>
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms,
> + bool need_cmd_read0)
> +{
> + if (nfc->no_rb_pin) {
> + /* This mode is used when there is no wired R/B pin.
> + * It works like 'nand_soft_waitrdy()', but instead of
> + * polling NAND_CMD_STATUS bit in the software loop,
> + * it will wait for interrupt - controllers checks IO
> + * bus and when it detects NAND_CMD_STATUS on it, it
> + * raises interrupt. After interrupt, NAND_CMD_READ0 is
> + * sent as terminator of the ready waiting procedure.
Please also tell us in which case this is needed/not needed.
> + */
> + return meson_nfc_wait_no_rb_pin(nfc, timeout_ms,
> + need_cmd_read0);
> + } else {
> + return meson_nfc_wait_rb_pin(nfc, timeout_ms);
> + }
> +}
> +
> static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> {
> struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> @@ -623,7 +679,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), true);
> } else {
> meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
> }
> @@ -669,7 +725,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), false);
>
> meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>
> @@ -952,7 +1008,8 @@ 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,
> + true);
> if (instr->delay_ns)
> meson_nfc_cmd_idle(nfc, delay_idle);
> break;
> @@ -1412,6 +1469,15 @@ static int meson_nfc_probe(struct platform_device *pdev)
> return ret;
> }
>
> + ret = of_property_read_u32(dev->of_node, "nand-rb", &nfc->no_rb_pin);
> + if (ret) {
> + /* If property was not found, don't use rb pin. */
> + if (ret == -EINVAL)
> + nfc->no_rb_pin = 1;
You mix two things:
- the presence of the RB pin
- the RB line
Just turn no_rb_pin into a boolean but d'ont use it to read the
property. Use another local u32 and just check the value is 0. If the
value is not 0, then return -EINVAL.
Also you should constrain the dt property in the binding to be either
absent or 0 but nothing else, with something like:
maxItems: 1
minimum: 0
maximum: 0
> + else
> + return ret;
> + }
> +
> writel(0, nfc->reg_base + NFC_REG_CFG);
> ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
> if (ret) {
Thanks,
Miquèl
next prev parent reply other threads:[~2023-06-07 8:03 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-06-07 7:30 [PATCH v2] mtd: rawnand: meson: waiting w/o wired ready/busy pin Arseniy Krasnov
2023-06-07 7:30 ` Arseniy Krasnov
2023-06-07 7:30 ` Arseniy Krasnov
2023-06-07 7:30 ` Arseniy Krasnov
2023-06-07 8:02 ` Miquel Raynal [this message]
2023-06-07 8:02 ` Miquel Raynal
2023-06-07 8:02 ` Miquel Raynal
2023-06-07 8:02 ` Miquel Raynal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230607100257.627fc438@xps-13 \
--to=miquel.raynal@bootlin.com \
--cc=AVKrasnov@sberdevices.ru \
--cc=jbrunet@baylibre.com \
--cc=kernel@sberdevices.ru \
--cc=khilman@baylibre.com \
--cc=liang.yang@amlogic.com \
--cc=linux-amlogic@lists.infradead.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=martin.blumenstingl@googlemail.com \
--cc=neil.armstrong@linaro.org \
--cc=oxffffaa@gmail.com \
--cc=richard@nod.at \
--cc=vigneshr@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.