[PATCH v4 05/24] mtd: spinand: simulate behavior of linux's function spinand_wait()
Frieder Schrempf
frieder.schrempf at kontron.de
Tue Aug 12 11:42:34 CEST 2025
Am 09.08.25 um 03:04 schrieb Mikhail Kshevetskiy:
> also call schedule() to allow periodic actions
I don't think "simulate" is a good verb here. What about the following:
###
mtd: spinand: Extend spinand_wait() to match Linux kernel implementation
This aligns spinand_wait() with the kernel. Instead of calling into
spi_mem_poll_status() which is not implemented in U-Boot, we open-code
the polling logic and make sure that schedule() is called periodically.
###
> Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
> ---
> drivers/mtd/nand/spi/core.c | 35 ++++++++++++++++++++++++++++-------
> include/linux/mtd/spinand.h | 22 ++++++++++++++++++++++
> 2 files changed, 50 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> index 34e5ac33c63..f99568050fb 100644
> --- a/drivers/mtd/nand/spi/core.c
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -32,6 +32,7 @@
> #include <linux/bug.h>
> #include <linux/mtd/spinand.h>
> #include <linux/printk.h>
> +#include <linux/delay.h>
> #endif
>
> struct spinand_plat {
> @@ -362,21 +363,29 @@ static int spinand_erase_op(struct spinand_device *spinand,
> return spi_mem_exec_op(spinand->slave, &op);
> }
>
> -static int spinand_wait(struct spinand_device *spinand, u8 *s)
> +static int spinand_wait(struct spinand_device *spinand,
You are adding the kdoc header for this function in patch 17 of this
series. It would be nice to include it here in this patch instead.
> + unsigned long initial_delay_us,
> + unsigned long poll_delay_us,
> + u8 *s)
> {
> unsigned long start, stop;
> u8 status;
> int ret;
>
> + udelay(initial_delay_us);
> start = get_timer(0);
> - stop = 400;
> + stop = SPINAND_WAITRDY_TIMEOUT_MS;
> do {
> + schedule();
> +
> ret = spinand_read_status(spinand, &status);
> if (ret)
> return ret;
>
> if (!(status & STATUS_BUSY))
> goto out;
> +
> + udelay(poll_delay_us);
> } while (get_timer(start) < stop);
>
> /*
> @@ -418,7 +427,10 @@ static int spinand_reset_op(struct spinand_device *spinand)
> if (ret)
> return ret;
>
> - return spinand_wait(spinand, NULL);
> + return spinand_wait(spinand,
> + SPINAND_RESET_INITIAL_DELAY_US,
> + SPINAND_RESET_POLL_DELAY_US,
> + NULL);
> }
>
> static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
> @@ -466,7 +478,10 @@ static int spinand_read_page(struct spinand_device *spinand,
> if (ret)
> return ret;
>
> - ret = spinand_wait(spinand, &status);
> + ret = spinand_wait(spinand,
> + SPINAND_READ_INITIAL_DELAY_US,
> + SPINAND_READ_POLL_DELAY_US,
> + &status);
> if (ret < 0)
> return ret;
>
> @@ -498,9 +513,12 @@ static int spinand_write_page(struct spinand_device *spinand,
> if (ret)
> return ret;
>
> - ret = spinand_wait(spinand, &status);
> + ret = spinand_wait(spinand,
> + SPINAND_WRITE_INITIAL_DELAY_US,
> + SPINAND_WRITE_POLL_DELAY_US,
> + &status);
> if (!ret && (status & STATUS_PROG_FAILED))
> - ret = -EIO;
> + return -EIO;
>
> return ret;
> }
> @@ -702,7 +720,10 @@ static int spinand_erase(struct nand_device *nand, const struct nand_pos *pos)
> if (ret)
> return ret;
>
> - ret = spinand_wait(spinand, &status);
> + ret = spinand_wait(spinand,
> + SPINAND_ERASE_INITIAL_DELAY_US,
> + SPINAND_ERASE_POLL_DELAY_US,
> + &status);
> if (!ret && (status & STATUS_ERASE_FAILED))
> ret = -EIO;
>
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> index 163269313f6..ba6c2ba149d 100644
> --- a/include/linux/mtd/spinand.h
> +++ b/include/linux/mtd/spinand.h
> @@ -176,6 +176,28 @@ struct spinand_op;
> struct spinand_device;
>
> #define SPINAND_MAX_ID_LEN 4
> +/*
> + * For erase, write and read operation, we got the following timings :
> + * tBERS (erase) 1ms to 4ms
> + * tPROG 300us to 400us
> + * tREAD 25us to 100us
> + * In order to minimize latency, the min value is divided by 4 for the
> + * initial delay, and dividing by 20 for the poll delay.
> + * For reset, 5us/10us/500us if the device is respectively
> + * reading/programming/erasing when the RESET occurs. Since we always
> + * issue a RESET when the device is IDLE, 5us is selected for both initial
> + * and poll delay.
> + */
> +#define SPINAND_READ_INITIAL_DELAY_US 6
> +#define SPINAND_READ_POLL_DELAY_US 5
> +#define SPINAND_RESET_INITIAL_DELAY_US 5
> +#define SPINAND_RESET_POLL_DELAY_US 5
> +#define SPINAND_WRITE_INITIAL_DELAY_US 75
> +#define SPINAND_WRITE_POLL_DELAY_US 15
> +#define SPINAND_ERASE_INITIAL_DELAY_US 250
> +#define SPINAND_ERASE_POLL_DELAY_US 50
> +
> +#define SPINAND_WAITRDY_TIMEOUT_MS 400
>
> /**
> * struct spinand_id - SPI NAND id structure
More information about the U-Boot
mailing list