Message ID | 20230608044728.1328506-3-AVKrasnov@sberdevices.ru (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Meson NAND: waiting w/o wired ready/busy pin | expand |
On Thu, 2023-06-08 at 04:47:28 UTC, Arseniy Krasnov wrote: > 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> Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next, thanks. Miquel
Hi Arseniy, AVKrasnov@sberdevices.ru wrote on Thu, 8 Jun 2023 07:47:28 +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> > --- > drivers/mtd/nand/raw/meson_nand.c | 77 +++++++++++++++++++++++++++++-- > 1 file changed, 73 insertions(+), 4 deletions(-) > > diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c > index 074e14225c06..9f05e113b4ea 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; > + bool 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); > + } I forgot about this, you should avoid open coding core helpers, can you please send a followup patch to use nand_status_op() and nand_exit_status_op() ? > + > + 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,27 @@ 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 if > + * needed (for all cases except page programming - this > + * is reason of 'need_cmd_read0' flag). > + */ > + 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 +681,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 +727,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 +1010,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; > @@ -1248,6 +1307,7 @@ meson_nfc_nand_chip_init(struct device *dev, > struct mtd_info *mtd; > int ret, i; > u32 tmp, nsels; > + u32 nand_rb_val = 0; > > nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32)); > if (!nsels || nsels > MAX_CE_NUM) { > @@ -1287,6 +1347,15 @@ meson_nfc_nand_chip_init(struct device *dev, > mtd->owner = THIS_MODULE; > mtd->dev.parent = dev; > > + ret = of_property_read_u32(np, "nand-rb", &nand_rb_val); > + if (ret == -EINVAL) > + nfc->no_rb_pin = true; > + else if (ret) > + return ret; > + > + if (nand_rb_val) > + return -EINVAL; > + > ret = nand_scan(nand, nsels); > if (ret) > return ret; Thanks, Miquèl
On 04.07.2023 15:43, Miquel Raynal wrote: > Hi Arseniy, > > AVKrasnov@sberdevices.ru wrote on Thu, 8 Jun 2023 07:47:28 +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> >> --- >> drivers/mtd/nand/raw/meson_nand.c | 77 +++++++++++++++++++++++++++++-- >> 1 file changed, 73 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c >> index 074e14225c06..9f05e113b4ea 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; >> + bool 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); >> + } > > I forgot about this, you should avoid open coding core helpers, can you > please send a followup patch to use nand_status_op() and > nand_exit_status_op() ? A ok, so: 1) Sending NAND_CMD_STATUS goes to nand_status_op() 2) Sending NAND_CMD_READ0 goes to nand_exit_status_op() Ok, no problem! I'll prepare and send it on this week! Thanks, Arseniy > >> + >> + 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,27 @@ 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 if >> + * needed (for all cases except page programming - this >> + * is reason of 'need_cmd_read0' flag). >> + */ >> + 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 +681,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 +727,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 +1010,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; >> @@ -1248,6 +1307,7 @@ meson_nfc_nand_chip_init(struct device *dev, >> struct mtd_info *mtd; >> int ret, i; >> u32 tmp, nsels; >> + u32 nand_rb_val = 0; >> >> nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32)); >> if (!nsels || nsels > MAX_CE_NUM) { >> @@ -1287,6 +1347,15 @@ meson_nfc_nand_chip_init(struct device *dev, >> mtd->owner = THIS_MODULE; >> mtd->dev.parent = dev; >> >> + ret = of_property_read_u32(np, "nand-rb", &nand_rb_val); >> + if (ret == -EINVAL) >> + nfc->no_rb_pin = true; >> + else if (ret) >> + return ret; >> + >> + if (nand_rb_val) >> + return -EINVAL; >> + >> ret = nand_scan(nand, nsels); >> if (ret) >> return ret; > > > Thanks, > Miquèl
On 04.07.2023 16:12, Miquel Raynal wrote: > Hi Arseniy, > > avkrasnov@sberdevices.ru wrote on Tue, 4 Jul 2023 15:46:18 +0300: > >> On 04.07.2023 15:43, Miquel Raynal wrote: >>> Hi Arseniy, >>> >>> AVKrasnov@sberdevices.ru wrote on Thu, 8 Jun 2023 07:47:28 +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> >>>> --- >>>> drivers/mtd/nand/raw/meson_nand.c | 77 +++++++++++++++++++++++++++++-- >>>> 1 file changed, 73 insertions(+), 4 deletions(-) >>>> >>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c >>>> index 074e14225c06..9f05e113b4ea 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; >>>> + bool 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); >>>> + } >>> >>> I forgot about this, you should avoid open coding core helpers, can you >>> please send a followup patch to use nand_status_op() and >>> nand_exit_status_op() ? >> >> A ok, so: >> 1) Sending NAND_CMD_STATUS goes to nand_status_op() >> 2) Sending NAND_CMD_READ0 goes to nand_exit_status_op() >> >> Ok, no problem! I'll prepare and send it on this week! > > Exactly. Sorry I had this in mind but I likely forgot to write it > down. Ok, got it! Thanks, Arseniy > > Thanks, > Miquèl
Hi Arseniy, avkrasnov@sberdevices.ru wrote on Tue, 4 Jul 2023 15:46:18 +0300: > On 04.07.2023 15:43, Miquel Raynal wrote: > > Hi Arseniy, > > > > AVKrasnov@sberdevices.ru wrote on Thu, 8 Jun 2023 07:47:28 +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> > >> --- > >> drivers/mtd/nand/raw/meson_nand.c | 77 +++++++++++++++++++++++++++++-- > >> 1 file changed, 73 insertions(+), 4 deletions(-) > >> > >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c > >> index 074e14225c06..9f05e113b4ea 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; > >> + bool 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); > >> + } > > > > I forgot about this, you should avoid open coding core helpers, can you > > please send a followup patch to use nand_status_op() and > > nand_exit_status_op() ? > > A ok, so: > 1) Sending NAND_CMD_STATUS goes to nand_status_op() > 2) Sending NAND_CMD_READ0 goes to nand_exit_status_op() > > Ok, no problem! I'll prepare and send it on this week! Exactly. Sorry I had this in mind but I likely forgot to write it down. Thanks, Miquèl
On 04.07.2023 16:07, Arseniy Krasnov wrote: > > > On 04.07.2023 16:12, Miquel Raynal wrote: >> Hi Arseniy, >> >> avkrasnov@sberdevices.ru wrote on Tue, 4 Jul 2023 15:46:18 +0300: >> >>> On 04.07.2023 15:43, Miquel Raynal wrote: >>>> Hi Arseniy, >>>> >>>> AVKrasnov@sberdevices.ru wrote on Thu, 8 Jun 2023 07:47:28 +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> >>>>> --- >>>>> drivers/mtd/nand/raw/meson_nand.c | 77 +++++++++++++++++++++++++++++-- >>>>> 1 file changed, 73 insertions(+), 4 deletions(-) >>>>> >>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c >>>>> index 074e14225c06..9f05e113b4ea 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; >>>>> + bool 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); >>>>> + } >>>> >>>> I forgot about this, you should avoid open coding core helpers, can you >>>> please send a followup patch to use nand_status_op() and >>>> nand_exit_status_op() ? >>> >>> A ok, so: >>> 1) Sending NAND_CMD_STATUS goes to nand_status_op() >>> 2) Sending NAND_CMD_READ0 goes to nand_exit_status_op() >>> >>> Ok, no problem! I'll prepare and send it on this week! >> >> Exactly. Sorry I had this in mind but I likely forgot to write it >> down. > > Ok, got it! Hm, seems 'int nand_exit_status_op(struct nand_chip *chip)' is not exported, so I can't use it in the Meson module. While 'nand_status_op()' works ok. May I can export 'nand_exit_status_op()?' Thanks, Arseniy > > Thanks, Arseniy > >> >> Thanks, >> Miquèl
Hi Arseniy, avkrasnov@sberdevices.ru wrote on Tue, 4 Jul 2023 20:32:13 +0300: > On 04.07.2023 16:07, Arseniy Krasnov wrote: > > > > > > On 04.07.2023 16:12, Miquel Raynal wrote: > >> Hi Arseniy, > >> > >> avkrasnov@sberdevices.ru wrote on Tue, 4 Jul 2023 15:46:18 +0300: > >> > >>> On 04.07.2023 15:43, Miquel Raynal wrote: > >>>> Hi Arseniy, > >>>> > >>>> AVKrasnov@sberdevices.ru wrote on Thu, 8 Jun 2023 07:47:28 +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> > >>>>> --- > >>>>> drivers/mtd/nand/raw/meson_nand.c | 77 +++++++++++++++++++++++++++++-- > >>>>> 1 file changed, 73 insertions(+), 4 deletions(-) > >>>>> > >>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c > >>>>> index 074e14225c06..9f05e113b4ea 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; > >>>>> + bool 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); > >>>>> + } > >>>> > >>>> I forgot about this, you should avoid open coding core helpers, can you > >>>> please send a followup patch to use nand_status_op() and > >>>> nand_exit_status_op() ? > >>> > >>> A ok, so: > >>> 1) Sending NAND_CMD_STATUS goes to nand_status_op() > >>> 2) Sending NAND_CMD_READ0 goes to nand_exit_status_op() > >>> > >>> Ok, no problem! I'll prepare and send it on this week! > >> > >> Exactly. Sorry I had this in mind but I likely forgot to write it > >> down. > > > > Ok, got it! > > Hm, seems 'int nand_exit_status_op(struct nand_chip *chip)' is not exported, > so I can't use it in the Meson module. While 'nand_status_op()' works ok. > May I can export 'nand_exit_status_op()?' Yes, in a separate patch please. > > Thanks, Arseniy > > > > > > Thanks, Arseniy > > > >> > >> Thanks, > >> Miquèl Thanks, Miquèl
diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 074e14225c06..9f05e113b4ea 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; + bool 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,27 @@ 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 if + * needed (for all cases except page programming - this + * is reason of 'need_cmd_read0' flag). + */ + 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 +681,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 +727,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 +1010,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; @@ -1248,6 +1307,7 @@ meson_nfc_nand_chip_init(struct device *dev, struct mtd_info *mtd; int ret, i; u32 tmp, nsels; + u32 nand_rb_val = 0; nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32)); if (!nsels || nsels > MAX_CE_NUM) { @@ -1287,6 +1347,15 @@ meson_nfc_nand_chip_init(struct device *dev, mtd->owner = THIS_MODULE; mtd->dev.parent = dev; + ret = of_property_read_u32(np, "nand-rb", &nand_rb_val); + if (ret == -EINVAL) + nfc->no_rb_pin = true; + else if (ret) + return ret; + + if (nand_rb_val) + return -EINVAL; + ret = nand_scan(nand, nsels); if (ret) return ret;
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> --- drivers/mtd/nand/raw/meson_nand.c | 77 +++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-)