summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2023-11-16 12:46:09 -0500
committerTom Rini <[email protected]>2023-11-16 13:49:13 -0500
commit5e6a112e1187ebc570b8befd1dd6eef3a64dec39 (patch)
tree0f87bf8f8b38403cc7b331ecc1660a2ef6b3af45 /drivers
parenta9a73799731807cca117d234c4338b710db3cfdd (diff)
parent8502b5bf20505408773d98fbc6e9307cb962e8b0 (diff)
Merge patch series "nand: Add sandbox tests"
To quote the author: This series tests raw nand flash in sandbox and fixes various bugs discovered in the process. I've tried to do things in a contemporary manner, avoiding the (numerous) variations present on only a few boards. The test is pretty minimal. Future work could test the rest of the nand API as well as the MTD API. Bloat (for v1) at [1] (for boards with SPL_NAND_SUPPORT enabled). Almost everything grows by a few bytes due to nand_page_size. A few boards grow more, mostly those using nand_spl_loaders.c. CI at [2]. [1] https://gist.github.com/Forty-Bot/9694f3401893c9e706ccc374922de6c2 [2] https://source.denx.de/u-boot/custodians/u-boot-clk/-/pipelines/18443
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/Makefile2
-rw-r--r--drivers/mtd/nand/raw/Kconfig27
-rw-r--r--drivers/mtd/nand/raw/Makefile1
-rw-r--r--drivers/mtd/nand/raw/am335x_spl_bch.c8
-rw-r--r--drivers/mtd/nand/raw/atmel_nand.c10
-rw-r--r--drivers/mtd/nand/raw/denali_spl.c5
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_spl.c8
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_nand_mlc.c5
-rw-r--r--drivers/mtd/nand/raw/mt7621_nand_spl.c5
-rw-r--r--drivers/mtd/nand/raw/mxc_nand_spl.c10
-rw-r--r--drivers/mtd/nand/raw/mxs_nand_spl.c5
-rw-r--r--drivers/mtd/nand/raw/nand.c66
-rw-r--r--drivers/mtd/nand/raw/nand_base.c7
-rw-r--r--drivers/mtd/nand/raw/nand_spl_loaders.c5
-rw-r--r--drivers/mtd/nand/raw/nand_spl_simple.c10
-rw-r--r--drivers/mtd/nand/raw/omap_gpmc.c3
-rw-r--r--drivers/mtd/nand/raw/sand_nand.c707
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand_spl.c8
-rw-r--r--drivers/mtd/onenand/onenand_uboot.c2
19 files changed, 858 insertions, 36 deletions
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index c638980ea2b..c2fc80b10f0 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -31,7 +31,7 @@ obj-$(CONFIG_NVMXIP) += nvmxip/
else
ifneq ($(mtd-y),)
-obj-$(CONFIG_SPL_MTD_SUPPORT) += mtd.o
+obj-$(CONFIG_SPL_MTD) += mtd.o
endif
obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += nand/
obj-$(CONFIG_SPL_ONENAND_SUPPORT) += onenand/
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index a13e6f59cbd..bb9994b8626 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -447,6 +447,22 @@ config NAND_PXA3XX
This enables the driver for the NAND flash device found on
PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
+config NAND_SANDBOX
+ bool "Support for NAND in sandbox"
+ depends on SANDBOX
+ select SYS_NAND_SELF_INIT
+ select SPL_SYS_NAND_SELF_INIT
+ select SPL_NAND_INIT
+ select SYS_NAND_SOFT_ECC
+ select BCH
+ select NAND_ECC_BCH
+ imply CMD_NAND
+ help
+ Enable a dummy NAND driver for sandbox. It simulates any number of
+ arbitrary NAND chips with a RAM buffer. It will also inject errors to
+ test ECC. At the moment, only 8-bit busses and single-chip devices are
+ supported.
+
config NAND_SUNXI
bool "Support for NAND on Allwinner SoCs"
default ARCH_SUNXI
@@ -659,20 +675,13 @@ config SYS_NAND_ONFI_DETECTION
And fetching device parameters flashed on device, by parsing
ONFI parameter page.
-config SYS_NAND_PAGE_COUNT
- hex "NAND chip page count"
- depends on SPL_NAND_SUPPORT && (NAND_ATMEL || NAND_MXC || \
- SPL_NAND_AM33XX_BCH || SPL_NAND_LOAD || SPL_NAND_SIMPLE || \
- NAND_OMAP_GPMC)
- help
- Number of pages in the NAND chip.
-
config SYS_NAND_PAGE_SIZE
hex "NAND chip page size"
depends on ARCH_SUNXI || NAND_OMAP_GPMC || NAND_LPC32XX_SLC || \
SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \
MVEBU_SPL_BOOT_DEVICE_NAND || \
- (NAND_ATMEL && SPL_NAND_SUPPORT) || SPL_GENERATE_ATMEL_PMECC_HEADER
+ (NAND_ATMEL && SPL_NAND_SUPPORT) || \
+ SPL_GENERATE_ATMEL_PMECC_HEADER || NAND_SANDBOX
depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_MT7621
help
Number of data bytes in one page for the NAND chip on the
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index add2b4cf655..ddbba899e58 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o
obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o
obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
+obj-$(CONFIG_NAND_SANDBOX) += sand_nand.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_NAND_MXIC) += mxic_nand.o
obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
diff --git a/drivers/mtd/nand/raw/am335x_spl_bch.c b/drivers/mtd/nand/raw/am335x_spl_bch.c
index 6ab3f1f42c5..6831af98b73 100644
--- a/drivers/mtd/nand/raw/am335x_spl_bch.c
+++ b/drivers/mtd/nand/raw/am335x_spl_bch.c
@@ -11,6 +11,7 @@
#include <common.h>
#include <nand.h>
+#include <system-constants.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/mtd/nand_ecc.h>
@@ -32,7 +33,7 @@ static int nand_command(int block, int page, uint32_t offs,
u8 cmd)
{
struct nand_chip *this = mtd_to_nand(mtd);
- int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ int page_addr = page + block * SYS_NAND_BLOCK_PAGES;
void (*hwctrl)(struct mtd_info *mtd, int cmd,
unsigned int ctrl) = this->cmd_ctrl;
@@ -217,6 +218,11 @@ void nand_init(void)
nand_command(0, 0, 0, NAND_CMD_RESET);
}
+unsigned int nand_page_size(void)
+{
+ return nand_to_mtd(&nand_chip)->writesize;
+}
+
/* Unselect after operation */
void nand_deselect(void)
{
diff --git a/drivers/mtd/nand/raw/atmel_nand.c b/drivers/mtd/nand/raw/atmel_nand.c
index 6b17e744a69..6d94e7af38e 100644
--- a/drivers/mtd/nand/raw/atmel_nand.c
+++ b/drivers/mtd/nand/raw/atmel_nand.c
@@ -12,6 +12,7 @@
#include <common.h>
#include <log.h>
+#include <system-constants.h>
#include <asm/gpio.h>
#include <asm/arch/gpio.h>
#include <dm/device_compat.h>
@@ -1258,7 +1259,7 @@ static struct nand_chip nand_chip;
static int nand_command(int block, int page, uint32_t offs, u8 cmd)
{
struct nand_chip *this = mtd_to_nand(mtd);
- int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ int page_addr = page + block * SYS_NAND_BLOCK_PAGES;
void (*hwctrl)(struct mtd_info *mtd, int cmd,
unsigned int ctrl) = this->cmd_ctrl;
@@ -1359,7 +1360,7 @@ int spl_nand_erase_one(int block, int page)
if (nand_chip.select_chip)
nand_chip.select_chip(mtd, 0);
- page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ page_addr = page + block * SYS_NAND_BLOCK_PAGES;
hwctrl(mtd, NAND_CMD_ERASE1, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
/* Row address */
hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE | NAND_CTRL_CHANGE);
@@ -1451,6 +1452,11 @@ void nand_init(void)
nand_chip.select_chip(mtd, 0);
}
+unsigned int nand_page_size(void)
+{
+ return nand_to_mtd(&nand_chip)->writesize;
+}
+
void nand_deselect(void)
{
if (nand_chip.select_chip)
diff --git a/drivers/mtd/nand/raw/denali_spl.c b/drivers/mtd/nand/raw/denali_spl.c
index 690279c9976..165a23312cb 100644
--- a/drivers/mtd/nand/raw/denali_spl.c
+++ b/drivers/mtd/nand/raw/denali_spl.c
@@ -234,4 +234,9 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
return 0;
}
+unsigned int nand_page_size(void)
+{
+ return page_size;
+}
+
void nand_deselect(void) {}
diff --git a/drivers/mtd/nand/raw/fsl_ifc_spl.c b/drivers/mtd/nand/raw/fsl_ifc_spl.c
index c67065eaf8c..69d26f1f79a 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_spl.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_spl.c
@@ -106,6 +106,8 @@ static inline int bad_block(uchar *marker, int port_size)
return __raw_readw((u16 *)marker) != 0xffff;
}
+static int saved_page_size;
+
int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
{
struct fsl_ifc_fcm *gregs = (void *)CFG_SYS_IFC_ADDR;
@@ -150,6 +152,7 @@ int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
if (port_size == 8)
bad_marker = 5;
}
+ saved_page_size = page_size;
ver = ifc_in32(&gregs->ifc_rev);
if (ver >= FSL_IFC_V2_0_0)
@@ -302,6 +305,11 @@ void nand_init(void)
{
}
+unsigned int nand_page_size(void)
+{
+ return saved_page_size;
+}
+
void nand_deselect(void)
{
}
diff --git a/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c
index ac2e669d46b..f8ae216d56c 100644
--- a/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c
@@ -765,4 +765,9 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
return 0;
}
+unsigned int nand_page_size(void)
+{
+ return BYTES_PER_PAGE;
+}
+
#endif /* CONFIG_SPL_BUILD */
diff --git a/drivers/mtd/nand/raw/mt7621_nand_spl.c b/drivers/mtd/nand/raw/mt7621_nand_spl.c
index 114fc8b7cea..a2be9ba80e0 100644
--- a/drivers/mtd/nand/raw/mt7621_nand_spl.c
+++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c
@@ -203,6 +203,11 @@ unsigned long nand_size(void)
return SZ_2G;
}
+unsigned int nand_page_size(void)
+{
+ return nfc_dev.nand.mtd.writesize;
+}
+
void nand_deselect(void)
{
}
diff --git a/drivers/mtd/nand/raw/mxc_nand_spl.c b/drivers/mtd/nand/raw/mxc_nand_spl.c
index 309e75d01e5..a855c9987f8 100644
--- a/drivers/mtd/nand/raw/mxc_nand_spl.c
+++ b/drivers/mtd/nand/raw/mxc_nand_spl.c
@@ -13,6 +13,7 @@
#include <common.h>
#include <hang.h>
#include <nand.h>
+#include <system-constants.h>
#include <linux/mtd/rawnand.h>
#include <asm/arch/imx-regs.h>
#include <asm/io.h>
@@ -304,13 +305,13 @@ int nand_spl_load_image(uint32_t from, unsigned int size, void *buf)
* Check if we have crossed a block boundary, and if so
* check for bad block.
*/
- if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) {
+ if (!(page % SYS_NAND_BLOCK_PAGES)) {
/*
* Yes, new block. See if this block is good. If not,
* loop until we find a good block.
*/
while (is_badblock(page)) {
- page = page + CONFIG_SYS_NAND_PAGE_COUNT;
+ page = page + SYS_NAND_BLOCK_PAGES;
/* Check i we've reached the end of flash. */
if (page >= maxpages)
return -1;
@@ -350,3 +351,8 @@ __used void nand_boot(void)
void nand_init(void) {}
void nand_deselect(void) {}
+
+unsigned int nand_page_size(void)
+{
+ return CONFIG_SYS_NAND_PAGE_SIZE;
+}
diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
index 300662994cf..f7d3f02f85a 100644
--- a/drivers/mtd/nand/raw/mxs_nand_spl.c
+++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
@@ -295,6 +295,11 @@ int nand_default_bbt(struct mtd_info *mtd)
return 0;
}
+unsigned int nand_page_size(void)
+{
+ return nand_to_mtd(&nand_chip)->writesize;
+}
+
void nand_deselect(void)
{
}
diff --git a/drivers/mtd/nand/raw/nand.c b/drivers/mtd/nand/raw/nand.c
index eacd99c4e27..4c18861aa25 100644
--- a/drivers/mtd/nand/raw/nand.c
+++ b/drivers/mtd/nand/raw/nand.c
@@ -60,13 +60,11 @@ int nand_register(int devnum, struct mtd_info *mtd)
sprintf(dev_name[devnum], "nand%d", devnum);
mtd->name = dev_name[devnum];
-#ifdef CONFIG_MTD
/*
* Add MTD device so that we can reference it later
* via the mtdcore infrastructure (e.g. ubi).
*/
add_mtd_device(mtd);
-#endif
total_nand_size += mtd->size / 1024;
@@ -76,6 +74,23 @@ int nand_register(int devnum, struct mtd_info *mtd)
return 0;
}
+void nand_unregister(struct mtd_info *mtd)
+{
+ int devnum = nand_mtd_to_devnum(mtd);
+
+ if (devnum < 0)
+ return;
+
+ if (nand_curr_device == devnum)
+ nand_curr_device = -1;
+
+ total_nand_size -= mtd->size / 1024;
+
+ del_mtd_device(nand_info[devnum]);
+
+ nand_info[devnum] = NULL;
+}
+
#if !CONFIG_IS_ENABLED(SYS_NAND_SELF_INIT)
static void nand_init_chip(int i)
{
@@ -100,6 +115,8 @@ static void nand_init_chip(int i)
#endif
#ifdef CONFIG_MTD_CONCAT
+struct mtd_info *concat_mtd;
+
static void create_mtd_concat(void)
{
struct mtd_info *nand_info_list[CONFIG_SYS_MAX_NAND_DEVICE];
@@ -114,28 +131,40 @@ static void create_mtd_concat(void)
}
}
if (nand_devices_found > 1) {
- struct mtd_info *mtd;
char c_mtd_name[16];
/*
* We detected multiple devices. Concatenate them together.
*/
sprintf(c_mtd_name, "nand%d", nand_devices_found);
- mtd = mtd_concat_create(nand_info_list, nand_devices_found,
- c_mtd_name);
+ concat_mtd = mtd_concat_create(nand_info_list,
+ nand_devices_found, c_mtd_name);
- if (mtd == NULL)
+ if (!concat_mtd)
return;
- nand_register(nand_devices_found, mtd);
+ nand_register(nand_devices_found, concat_mtd);
}
return;
}
+
+static void destroy_mtd_concat(void)
+{
+ if (!concat_mtd)
+ return;
+
+ mtd_concat_destroy(concat_mtd);
+ concat_mtd = NULL;
+}
#else
static void create_mtd_concat(void)
{
}
+
+static void destroy_mtd_concat(void)
+{
+}
#endif
unsigned long nand_size(void)
@@ -143,10 +172,10 @@ unsigned long nand_size(void)
return total_nand_size;
}
+static int initialized;
+
void nand_init(void)
{
- static int initialized;
-
/*
* Avoid initializing NAND Flash multiple times,
* otherwise it will calculate a wrong total size.
@@ -174,3 +203,22 @@ void nand_init(void)
create_mtd_concat();
}
+
+void nand_reinit(void)
+{
+ int i;
+
+ destroy_mtd_concat();
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+ assert(!nand_info[i]);
+
+ initialized = 0;
+ nand_init();
+}
+
+unsigned int nand_page_size(void)
+{
+ struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device);
+
+ return mtd ? mtd->writesize : 1;
+}
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 6b4adcf6bdc..44b6cb63a01 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -4462,17 +4462,14 @@ ident_done:
else if (chip->jedec_version)
pr_info("%s %s\n", manufacturer_desc->name,
chip->jedec_params.model);
- else
+ else if (manufacturer_desc)
pr_info("%s %s\n", manufacturer_desc->name, type->name);
#else
if (chip->jedec_version)
pr_info("%s %s\n", manufacturer_desc->name,
chip->jedec_params.model);
- else
+ else if (manufacturer_desc)
pr_info("%s %s\n", manufacturer_desc->name, type->name);
-
- pr_info("%s %s\n", manufacturer_desc->name,
- type->name);
#endif
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
diff --git a/drivers/mtd/nand/raw/nand_spl_loaders.c b/drivers/mtd/nand/raw/nand_spl_loaders.c
index 156b44d8358..db4213ea3dc 100644
--- a/drivers/mtd/nand/raw/nand_spl_loaders.c
+++ b/drivers/mtd/nand/raw/nand_spl_loaders.c
@@ -12,8 +12,11 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
while (block <= lastblock) {
if (!nand_is_bad_block(block)) {
/* Skip bad blocks */
- while (page < CONFIG_SYS_NAND_PAGE_COUNT) {
+ while (size && page < SYS_NAND_BLOCK_PAGES) {
nand_read_page(block, page, dst);
+
+ size -= min(size, CONFIG_SYS_NAND_PAGE_SIZE -
+ page_offset);
/*
* When offs is not aligned to page address the
* extra offset is copied to dst as well. Copy
diff --git a/drivers/mtd/nand/raw/nand_spl_simple.c b/drivers/mtd/nand/raw/nand_spl_simple.c
index 2f3af9edd4c..80d6e0e1e4e 100644
--- a/drivers/mtd/nand/raw/nand_spl_simple.c
+++ b/drivers/mtd/nand/raw/nand_spl_simple.c
@@ -6,6 +6,7 @@
#include <common.h>
#include <nand.h>
+#include <system-constants.h>
#include <asm/io.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/rawnand.h>
@@ -27,7 +28,7 @@ static int nand_command(int block, int page, uint32_t offs,
u8 cmd)
{
struct nand_chip *this = mtd_to_nand(mtd);
- int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ int page_addr = page + block * SYS_NAND_BLOCK_PAGES;
while (!this->dev_ready(mtd))
;
@@ -59,7 +60,7 @@ static int nand_command(int block, int page, uint32_t offs,
u8 cmd)
{
struct nand_chip *this = mtd_to_nand(mtd);
- int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ int page_addr = page + block * SYS_NAND_BLOCK_PAGES;
void (*hwctrl)(struct mtd_info *mtd, int cmd,
unsigned int ctrl) = this->cmd_ctrl;
@@ -226,6 +227,11 @@ void nand_init(void)
nand_chip.select_chip(mtd, 0);
}
+unsigned int nand_page_size(void)
+{
+ return nand_to_mtd(&nand_chip)->writesize;
+}
+
/* Unselect after operation */
void nand_deselect(void)
{
diff --git a/drivers/mtd/nand/raw/omap_gpmc.c b/drivers/mtd/nand/raw/omap_gpmc.c
index 1a5ed0de31a..0e25bd5dc28 100644
--- a/drivers/mtd/nand/raw/omap_gpmc.c
+++ b/drivers/mtd/nand/raw/omap_gpmc.c
@@ -6,6 +6,7 @@
#include <common.h>
#include <log.h>
+#include <system-constants.h>
#include <asm/io.h>
#include <dm/uclass.h>
#include <linux/errno.h>
@@ -1298,7 +1299,7 @@ static int nand_is_bad_block(int block)
static int nand_read_page(int block, int page, uchar *dst)
{
- int page_addr = block * CONFIG_SYS_NAND_PAGE_COUNT + page;
+ int page_addr = block * SYS_NAND_BLOCK_PAGES + page;
loff_t ofs = page_addr * CONFIG_SYS_NAND_PAGE_SIZE;
int ret;
size_t len = CONFIG_SYS_NAND_PAGE_SIZE;
diff --git a/drivers/mtd/nand/raw/sand_nand.c b/drivers/mtd/nand/raw/sand_nand.c
new file mode 100644
index 00000000000..229d7b5b65a
--- /dev/null
+++ b/drivers/mtd/nand/raw/sand_nand.c
@@ -0,0 +1,707 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Sean Anderson <[email protected]>
+ */
+
+#define LOG_CATEGORY UCLASS_MTD
+#include <errno.h>
+#include <hexdump.h>
+#include <log.h>
+#include <nand.h>
+#include <os.h>
+#include <rand.h>
+#include <spl.h>
+#include <system-constants.h>
+#include <dm/device_compat.h>
+#include <dm/read.h>
+#include <dm/uclass.h>
+#include <asm/bitops.h>
+#include <linux/bitmap.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/sizes.h>
+
+enum sand_nand_state {
+ STATE_READY,
+ STATE_IDLE,
+ STATE_READ,
+ STATE_READ_ID,
+ STATE_READ_ONFI,
+ STATE_PARAM_ONFI,
+ STATE_STATUS,
+ STATE_PROG,
+ STATE_ERASE,
+};
+
+static const char *const state_name[] = {
+ [STATE_READY] = "READY",
+ [STATE_IDLE] = "IDLE",
+ [STATE_READ] = "READ",
+ [STATE_READ_ID] = "READ_ID",
+ [STATE_READ_ONFI] = "READ_ONFI",
+ [STATE_PARAM_ONFI] = "PARAM_ONFI",
+ [STATE_STATUS] = "STATUS",
+ [STATE_PROG] = "PROG",
+ [STATE_ERASE] = "ERASE",
+};
+
+/**
+ * struct sand_nand_chip - Per-device private data
+ * @nand: The nand chip
+ * @node: The next device in this controller
+ * @programmed: Bitmap of whether sectors are programmed
+ * @id: ID to report for NAND_CMD_READID
+ * @id_len: Length of @id
+ * @onfi: Three copies of ONFI parameter page
+ * @status: Status to report for NAND_CMD_STATUS
+ * @chunksize: Size of one "chunk" (page + oob) in bytes
+ * @pageize: Size of one page in bytes
+ * @pages: Total number of pages
+ * @pages_per_erase: Number of pages per eraseblock
+ * @err_count: Number of errors to inject per @err_step_bits of data
+ * @err_step_bits: Number of data bits per error "step"
+ * @err_steps: Number of err steps in a page
+ * @cs: Chip select for this device
+ * @state: Current state of the device
+ * @column: Column of the most-recent command
+ * @page_addr: Page address of the most-recent command
+ * @fd: File descriptor for the backing data
+ * @fd_page_addr: Page address that @fd is seek'd to
+ * @selected: Whether this device is selected
+ * @tmp: "Cache" buffer used to store transferred data before committing it
+ * @tmp_dirty: Whether @tmp is dirty (modified) or clean (all ones)
+ *
+ * Data is stored with the OOB area in-line. For example, with 512-byte pages
+ * and and 16-byte OOB areas, the first page would start at offset 0, the second
+ * at offset 528, the third at offset 1056, and so on
+ */
+struct sand_nand_chip {
+ struct nand_chip nand;
+ struct list_head node;
+ long *programmed;
+ const u8 *id;
+ u32 chunksize, pagesize, pages, pages_per_erase;
+ u32 err_count, err_step_bits, err_steps, ecc_bits;
+ unsigned int cs;
+ enum sand_nand_state state;
+ int column, page_addr, fd, fd_page_addr;
+ bool selected, tmp_dirty;
+ u8 status;
+ u8 id_len;
+ u8 tmp[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];
+ u8 onfi[sizeof(struct nand_onfi_params) * 3];
+};
+
+#define SAND_DEBUG(chip, fmt, ...) \
+ dev_dbg((chip)->nand.mtd.dev, "%u (%s): " fmt, (chip)->cs, \
+ state_name[(chip)->state], ##__VA_ARGS__)
+
+static inline void to_state(struct sand_nand_chip *chip,
+ enum sand_nand_state new_state)
+{
+ if (new_state != chip->state)
+ SAND_DEBUG(chip, "to state %s\n", state_name[new_state]);
+ chip->state = new_state;
+}
+
+static inline struct sand_nand_chip *to_sand_nand(struct nand_chip *nand)
+{
+ return container_of(nand, struct sand_nand_chip, nand);
+}
+
+struct sand_nand_priv {
+ struct list_head chips;
+};
+
+static int sand_nand_dev_ready(struct mtd_info *mtd)
+{
+ return 1;
+}
+
+static int sand_nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ u8 status;
+
+ return nand_status_op(chip, &status) ?: status;
+}
+
+static int sand_nand_seek(struct sand_nand_chip *chip)
+{
+ if (chip->fd_page_addr == chip->page_addr)
+ return 0;
+
+ if (os_lseek(chip->fd, (off_t)chip->page_addr * chip->chunksize,
+ OS_SEEK_SET) < 0) {
+ SAND_DEBUG(chip, "could not seek: %d\n", errno);
+ return -EIO;
+ }
+
+ chip->fd_page_addr = chip->page_addr;
+ return 0;
+}
+
+static void sand_nand_inject_error(struct sand_nand_chip *chip,
+ unsigned int step, unsigned int pos)
+{
+ int byte, index;
+
+ if (pos < chip->err_step_bits) {
+ __change_bit(step * chip->err_step_bits + pos, chip->tmp);
+ return;
+ }
+
+ /*
+ * Only ECC bytes are covered in the OOB area, so
+ * pretend that those are the only bytes which can have
+ * errors.
+ */
+ byte = (pos - chip->err_step_bits + step * chip->ecc_bits) / 8;
+ index = chip->nand.ecc.layout->eccpos[byte];
+ /* Avoid endianness issues by working with bytes */
+ chip->tmp[chip->pagesize + index] ^= BIT(pos & 0x7);
+}
+
+static int sand_nand_read(struct sand_nand_chip *chip)
+{
+ unsigned int i, stop = 0;
+
+ if (chip->column == chip->pagesize)
+ stop = chip->err_step_bits;
+
+ if (test_bit(chip->page_addr, chip->programmed)) {
+ if (sand_nand_seek(chip))
+ return -EIO;
+
+ if (os_read(chip->fd, chip->tmp, chip->chunksize) !=
+ chip->chunksize) {
+ SAND_DEBUG(chip, "could not read: %d\n", errno);
+ return -EIO;
+ }
+ chip->fd_page_addr++;
+ } else if (chip->tmp_dirty) {
+ memset(chip->tmp + chip->column, 0xff,
+ chip->chunksize - chip->column);
+ }
+
+ /*
+ * Inject some errors; this is Method A from "An Efficient Algorithm for
+ * Sequential Random Sampling" (Vitter 87). This is still slow when
+ * generating a lot (dozens) of ECC errors.
+ *
+ * To avoid generating too many errors in any one ECC step, we separate
+ * our error generation by ECC step.
+ */
+ chip->tmp_dirty = true;
+ for (i = 0; i < chip->err_steps; i++) {
+ u32 bit_errors = chip->err_count;
+ unsigned int j = chip->err_step_bits + chip->ecc_bits;
+
+ while (bit_errors) {
+ unsigned int u = rand();
+ float quot = 1ULL << 32;
+
+ do {
+ quot *= j - bit_errors;
+ quot /= j;
+ j--;
+
+ if (j < stop)
+ goto next;
+ } while (u < quot);
+
+ sand_nand_inject_error(chip, i, j);
+ bit_errors--;
+ }
+next:
+ ;
+ }
+
+ return 0;
+}
+
+static void sand_nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sand_nand_chip *chip = to_sand_nand(nand);
+ enum sand_nand_state new_state = chip->state;
+
+ SAND_DEBUG(chip, "command=%02x column=%d page_addr=%d\n", command,
+ column, page_addr);
+
+ if (!chip->selected)
+ return;
+
+ switch (chip->state) {
+ case STATE_READY:
+ if (command == NAND_CMD_RESET)
+ goto reset;
+ break;
+ case STATE_PROG:
+ new_state = STATE_IDLE;
+ if (command != NAND_CMD_PAGEPROG ||
+ test_and_set_bit(chip->page_addr, chip->programmed)) {
+ chip->status |= NAND_STATUS_FAIL;
+ break;
+ }
+
+ if (sand_nand_seek(chip)) {
+ chip->status |= NAND_STATUS_FAIL;
+ break;
+ }
+
+ if (os_write(chip->fd, chip->tmp, chip->chunksize) !=
+ chip->chunksize) {
+ SAND_DEBUG(chip, "could not write: %d\n", errno);
+ chip->status |= NAND_STATUS_FAIL;
+ break;
+ }
+
+ chip->fd_page_addr++;
+ break;
+ case STATE_ERASE:
+ new_state = STATE_IDLE;
+ if (command != NAND_CMD_ERASE2) {
+ chip->status |= NAND_STATUS_FAIL;
+ break;
+ }
+
+ if (chip->page_addr < 0 ||
+ chip->page_addr >= chip->pages ||
+ chip->page_addr % chip->pages_per_erase)
+ chip->status |= NAND_STATUS_FAIL;
+ else
+ bitmap_clear(chip->programmed, chip->page_addr,
+ chip->pages_per_erase);
+ break;
+ default:
+ chip->column = column;
+ chip->page_addr = page_addr;
+ switch (command) {
+ case NAND_CMD_READOOB:
+ if (column >= 0)
+ chip->column += chip->pagesize;
+ fallthrough;
+ case NAND_CMD_READ0:
+ new_state = STATE_IDLE;
+ if (page_addr < 0 || page_addr >= chip->pages)
+ break;
+
+ if (chip->column < 0 || chip->column >= chip->chunksize)
+ break;
+
+ if (sand_nand_read(chip))
+ break;
+
+ chip->page_addr = page_addr;
+ new_state = STATE_READ;
+ break;
+ case NAND_CMD_ERASE1:
+ new_state = STATE_ERASE;
+ chip->status = ~NAND_STATUS_FAIL;
+ break;
+ case NAND_CMD_STATUS:
+ new_state = STATE_STATUS;
+ chip->column = 0;
+ break;
+ case NAND_CMD_SEQIN:
+ new_state = STATE_PROG;
+ chip->status = ~NAND_STATUS_FAIL;
+ if (page_addr < 0 || page_addr >= chip->pages ||
+ chip->column < 0 ||
+ chip->column >= chip->chunksize) {
+ chip->status |= NAND_STATUS_FAIL;
+ } else if (chip->tmp_dirty) {
+ memset(chip->tmp, 0xff, chip->chunksize);
+ chip->tmp_dirty = false;
+ }
+ break;
+ case NAND_CMD_READID:
+ if (chip->onfi[0] && column == 0x20)
+ new_state = STATE_READ_ONFI;
+ else
+ new_state = STATE_READ_ID;
+ chip->column = 0;
+ break;
+ case NAND_CMD_PARAM:
+ if (chip->onfi[0] && !column)
+ new_state = STATE_PARAM_ONFI;
+ else
+ new_state = STATE_IDLE;
+ break;
+ case NAND_CMD_RESET:
+reset:
+ new_state = STATE_IDLE;
+ chip->column = -1;
+ chip->page_addr = -1;
+ chip->status = ~NAND_STATUS_FAIL;
+ break;
+ default:
+ new_state = STATE_IDLE;
+ SAND_DEBUG(chip, "Unsupported command %02x\n", command);
+ }
+ }
+
+ to_state(chip, new_state);
+}
+
+static void sand_nand_select_chip(struct mtd_info *mtd, int n)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sand_nand_chip *chip = to_sand_nand(nand);
+
+ chip->selected = !n;
+}
+
+static void sand_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sand_nand_chip *chip = to_sand_nand(nand);
+ unsigned int to_copy;
+ int src_len = 0;
+ const u8 *src = NULL;
+
+ if (!chip->selected)
+ goto copy;
+
+ switch (chip->state) {
+ case STATE_READ:
+ src = chip->tmp;
+ src_len = chip->chunksize;
+ break;
+ case STATE_READ_ID:
+ src = chip->id;
+ src_len = chip->id_len;
+ break;
+ case STATE_READ_ONFI:
+ src = "ONFI";
+ src_len = 4;
+ break;
+ case STATE_PARAM_ONFI:
+ src = chip->onfi;
+ src_len = sizeof(chip->onfi);
+ break;
+ case STATE_STATUS:
+ src = &chip->status;
+ src_len = 1;
+ break;
+ default:
+ break;
+ }
+
+copy:
+ if (chip->column >= 0)
+ to_copy = max(min(len, src_len - chip->column), 0);
+ else
+ to_copy = 0;
+ memcpy(buf, src + chip->column, to_copy);
+ memset(buf + to_copy, 0xff, len - to_copy);
+ chip->column += to_copy;
+
+ if (len == 1) {
+ SAND_DEBUG(chip, "read [ %02x ]\n", buf[0]);
+ } else if (src_len) {
+ SAND_DEBUG(chip, "read %d bytes\n", len);
+#ifdef VERBOSE_DEBUG
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
+#endif
+ }
+
+ if (src_len && chip->column == src_len)
+ to_state(chip, STATE_IDLE);
+}
+
+static u8 sand_nand_read_byte(struct mtd_info *mtd)
+{
+ u8 ret;
+
+ sand_nand_read_buf(mtd, &ret, 1);
+ return ret;
+}
+
+static u16 sand_nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sand_nand_chip *chip = to_sand_nand(nand);
+
+ SAND_DEBUG(chip, "16-bit access unsupported\n");
+ return sand_nand_read_byte(mtd) | 0xff00;
+}
+
+static void sand_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sand_nand_chip *chip = to_sand_nand(nand);
+
+ SAND_DEBUG(chip, "write %d bytes\n", len);
+#ifdef VERBOSE_DEBUG
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
+#endif
+
+ if (chip->state != STATE_PROG || chip->status & NAND_STATUS_FAIL)
+ return;
+
+ chip->tmp_dirty = true;
+ len = min((unsigned int)len, chip->chunksize - chip->column);
+ memcpy(chip->tmp + chip->column, buf, len);
+ chip->column += len;
+}
+
+static struct nand_chip *nand_chip;
+
+int sand_nand_remove(struct udevice *dev)
+{
+ struct sand_nand_priv *priv = dev_get_priv(dev);
+ struct sand_nand_chip *chip;
+
+ list_for_each_entry(chip, &priv->chips, node) {
+ struct nand_chip *nand = &chip->nand;
+
+ if (nand_chip == nand)
+ nand_chip = NULL;
+
+ nand_unregister(nand_to_mtd(nand));
+ free(chip->programmed);
+ os_close(chip->fd);
+ free(chip);
+ }
+
+ return 0;
+}
+
+static int sand_nand_probe(struct udevice *dev)
+{
+ struct sand_nand_priv *priv = dev_get_priv(dev);
+ struct sand_nand_chip *chip;
+ int ret, devnum = 0;
+ ofnode np;
+
+ INIT_LIST_HEAD(&priv->chips);
+
+ dev_for_each_subnode(np, dev) {
+ struct nand_chip *nand;
+ struct mtd_info *mtd;
+ u32 erasesize, oobsize, pagesize, pages;
+ u32 err_count, err_step_size;
+ off_t expected_size;
+ char filename[30];
+ fdt_addr_t cs;
+ const u8 *id, *onfi;
+ int id_len, onfi_len;
+
+ cs = ofnode_get_addr_size_index_notrans(np, 0, NULL);
+ if (cs == FDT_ADDR_T_NONE) {
+ dev_dbg(dev, "Invalid cs for chip %s\n",
+ ofnode_get_name(np));
+ ret = -ENOENT;
+ goto err;
+ }
+
+ id = ofnode_read_prop(np, "sandbox,id", &id_len);
+ if (!id) {
+ dev_dbg(dev, "No sandbox,id property for chip %s\n",
+ ofnode_get_name(np));
+ ret = -EINVAL;
+ goto err;
+ }
+
+ onfi = ofnode_read_prop(np, "sandbox,onfi", &onfi_len);
+ if (onfi && onfi_len != sizeof(struct nand_onfi_params)) {
+ dev_dbg(dev, "Invalid length %d for onfi params\n",
+ onfi_len);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = ofnode_read_u32(np, "sandbox,erasesize", &erasesize);
+ if (ret) {
+ dev_dbg(dev, "No sandbox,erasesize property for chip %s",
+ ofnode_get_name(np));
+ goto err;
+ }
+
+ ret = ofnode_read_u32(np, "sandbox,oobsize", &oobsize);
+ if (ret) {
+ dev_dbg(dev, "No sandbox,oobsize property for chip %s",
+ ofnode_get_name(np));
+ goto err;
+ }
+
+ ret = ofnode_read_u32(np, "sandbox,pagesize", &pagesize);
+ if (ret) {
+ dev_dbg(dev, "No sandbox,pagesize property for chip %s",
+ ofnode_get_name(np));
+ goto err;
+ }
+
+ ret = ofnode_read_u32(np, "sandbox,pages", &pages);
+ if (ret) {
+ dev_dbg(dev, "No sandbox,pages property for chip %s",
+ ofnode_get_name(np));
+ goto err;
+ }
+
+ ret = ofnode_read_u32(np, "sandbox,err-count", &err_count);
+ if (ret) {
+ dev_dbg(dev,
+ "No sandbox,err-count property for chip %s",
+ ofnode_get_name(np));
+ goto err;
+ }
+
+ ret = ofnode_read_u32(np, "sandbox,err-step-size",
+ &err_step_size);
+ if (ret) {
+ dev_dbg(dev,
+ "No sandbox,err-step-size property for chip %s",
+ ofnode_get_name(np));
+ goto err;
+ }
+
+ chip = calloc(sizeof(*chip), 1);
+ if (!chip) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ chip->cs = cs;
+ chip->id = id;
+ chip->id_len = id_len;
+ chip->chunksize = pagesize + oobsize;
+ chip->pagesize = pagesize;
+ chip->pages = pages;
+ chip->pages_per_erase = erasesize / pagesize;
+ memset(chip->tmp, 0xff, chip->chunksize);
+
+ chip->err_count = err_count;
+ chip->err_step_bits = err_step_size * 8;
+ chip->err_steps = pagesize / err_step_size;
+
+ expected_size = (off_t)pages * chip->chunksize;
+ snprintf(filename, sizeof(filename),
+ "/tmp/u-boot.nand%d.XXXXXX", devnum);
+ chip->fd = os_mktemp(filename, expected_size);
+ if (chip->fd < 0) {
+ dev_dbg(dev, "Could not create temp file %s\n",
+ filename);
+ ret = chip->fd;
+ goto err_chip;
+ }
+
+ chip->programmed = calloc(sizeof(long),
+ BITS_TO_LONGS(pages));
+ if (!chip->programmed) {
+ ret = -ENOMEM;
+ goto err_fd;
+ }
+
+ if (onfi) {
+ memcpy(chip->onfi, onfi, onfi_len);
+ memcpy(chip->onfi + onfi_len, onfi, onfi_len);
+ memcpy(chip->onfi + 2 * onfi_len, onfi, onfi_len);
+ }
+
+ nand = &chip->nand;
+ nand->options = spl_in_proper() ? 0 : NAND_SKIP_BBTSCAN;
+ nand->flash_node = np;
+ nand->dev_ready = sand_nand_dev_ready;
+ nand->cmdfunc = sand_nand_command;
+ nand->waitfunc = sand_nand_wait;
+ nand->select_chip = sand_nand_select_chip;
+ nand->read_byte = sand_nand_read_byte;
+ nand->read_word = sand_nand_read_word;
+ nand->read_buf = sand_nand_read_buf;
+ nand->write_buf = sand_nand_write_buf;
+ nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
+
+ mtd = nand_to_mtd(nand);
+ mtd->dev = dev;
+
+ ret = nand_scan(mtd, CONFIG_SYS_NAND_MAX_CHIPS);
+ if (ret) {
+ dev_dbg(dev, "Could not scan chip %s: %d\n",
+ ofnode_get_name(np), ret);
+ goto err_prog;
+ }
+ chip->ecc_bits = nand->ecc.layout->eccbytes * 8 /
+ chip->err_steps;
+
+ ret = nand_register(devnum, mtd);
+ if (ret) {
+ dev_dbg(dev, "Could not register nand %d: %d\n", devnum,
+ ret);
+ goto err_prog;
+ }
+
+ if (!nand_chip)
+ nand_chip = nand;
+
+ list_add_tail(&chip->node, &priv->chips);
+ devnum++;
+ continue;
+
+err_prog:
+ free(chip->programmed);
+err_fd:
+ os_close(chip->fd);
+err_chip:
+ free(chip);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ sand_nand_remove(dev);
+ return ret;
+}
+
+static const struct udevice_id sand_nand_ids[] = {
+ { .compatible = "sandbox,nand" },
+ { }
+};
+
+U_BOOT_DRIVER(sand_nand) = {
+ .name = "sand-nand",
+ .id = UCLASS_MTD,
+ .of_match = sand_nand_ids,
+ .probe = sand_nand_probe,
+ .remove = sand_nand_remove,
+ .priv_auto = sizeof(struct sand_nand_priv),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int err;
+
+ err = uclass_get_device_by_driver(UCLASS_MTD, DM_DRIVER_REF(sand_nand),
+ &dev);
+ if (err && err != -ENODEV)
+ log_info("Failed to get sandbox NAND: %d\n", err);
+}
+
+#if IS_ENABLED(CONFIG_SPL_BUILD) && IS_ENABLED(CONFIG_SPL_NAND_INIT)
+void nand_deselect(void)
+{
+ nand_chip->select_chip(nand_to_mtd(nand_chip), -1);
+}
+
+static int nand_is_bad_block(int block)
+{
+ struct mtd_info *mtd = nand_to_mtd(nand_chip);
+
+ return mtd_block_isbad(mtd, block << mtd->erasesize_shift);
+}
+
+static int nand_read_page(int block, int page, uchar *dst)
+{
+ struct mtd_info *mtd = nand_to_mtd(nand_chip);
+ loff_t ofs = ((loff_t)block << mtd->erasesize_shift) +
+ ((loff_t)page << mtd->writesize_shift);
+ size_t len = mtd->writesize;
+
+ return nand_read(mtd, ofs, &len, dst);
+}
+
+#include "nand_spl_loaders.c"
+#endif /* CONFIG_SPL_NAND_INIT */
diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c
index 6de0b0a3554..c9b8c78ed75 100644
--- a/drivers/mtd/nand/raw/sunxi_nand_spl.c
+++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c
@@ -524,9 +524,10 @@ static int nand_read_buffer(struct nfc_config *conf, uint32_t offs,
return 0;
}
+static struct nfc_config conf;
+
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
{
- static struct nfc_config conf = { };
int ret;
ret = nand_detect_config(&conf, offs, dest);
@@ -536,6 +537,11 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
return nand_read_buffer(&conf, offs, size, dest);
}
+unsigned int nand_page_size(void)
+{
+ return conf.page_size;
+}
+
void nand_deselect(void)
{
struct sunxi_ccm_reg *const ccm =
diff --git a/drivers/mtd/onenand/onenand_uboot.c b/drivers/mtd/onenand/onenand_uboot.c
index 04791df69bb..ecacabefadc 100644
--- a/drivers/mtd/onenand/onenand_uboot.c
+++ b/drivers/mtd/onenand/onenand_uboot.c
@@ -44,14 +44,12 @@ void onenand_init(void)
puts("Flex-");
puts("OneNAND: ");
-#ifdef CONFIG_MTD
/*
* Add MTD device so that we can reference it later
* via the mtdcore infrastructure (e.g. ubi).
*/
onenand_mtd.name = dev_name;
add_mtd_device(&onenand_mtd);
-#endif
}
print_size(onenand_chip.chipsize, "\n");
}