[U-Boot] [PATCH v10 00/27] dm: Generic MTD Subsystem, with SPI-NOR interface

Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR: ======= Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
Changes for v10: - Update Simon's R-B tag - Add mtd dm test case - implemented entire code wrt MTD, with interface type
code size: ========== before: $ arm-linux-gnueabi-size u-boot text data bss dec hex filename 473712 15152 222568 711432 adb08 u-boot $ du -hs u-boot-dtb.img 488K u-boot-dtb.img
after: $ arm-linux-gnueabi-size u-boot text data bss dec hex filename 470124 14352 222584 707060 ac9f4 u-boot $ du -hs u-boot-dtb.img 484K u-boot-dtb.img
approximately 4KiB but DM_SPI_FLASH still there which can be removed once support added in SPL
test log: ======== Zynq> spinor spinor - SPI-NOR Sub-system
Usage: spinor list - show list of spinor devices spinor info - show current spinor device info spinor dev [devnum] - show or set current spinor device spinor erase offset len - erase 'len' bytes from 'offset' spinor write addr to len - write 'len' bytes to 'to' from 'addr' spinor read addr from len - read 'len' bytes from 'from' to 'addr' spinor protect lock/unlock sector len - protect/unprotect 'len' bytes starting at address 'sector' Zynq> spinor list flash@0: 0 spi-nor@e000d000: 1 Zynq> spinor dev 0 switch to dev #0, OK spinor0 is current device Zynq> spinor info bus: flash@0: 0 device: s25fl128s_64k page size: 256 B erase size: 64 KiB size: 16 MiB Zynq> spinor erase 0xE00000 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Erased: OK Zynq> mw.b 0x100 0xcc 0x100000 Zynq> spinor write 0x100 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Written: OK Zynq> spinor read 0x3000000 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Read: OK Zynq> cmp.b 0x3000000 0x100 0x100000 Total of 1048576 byte(s) were the same Zynq> spinor dev 1 switch to dev #1, OK spinor1 is current device Zynq> spinor info bus: spi-nor@e000d000: 1 device: s25fl128s_64k page size: 256 B erase size: 64 KiB size: 16 MiB Zynq> spinor erase 0xE00000 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Erased: OK Zynq> mw.b 0x100 0xbb 0x100000 Zynq> spinor write 0x100 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Written: OK Zynq> spinor read 0x3000000 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Read: OK Zynq> cmp.b 0x3000000 0x100 0x100000 Total of 1048576 byte(s) were the same
WIP: === - to support non-dm code - to support spinor SPL
Repo: ==== $ git clone git://git.denx.de/u-boot-spi.git $ cd u-boot-spi $ git checkout -b mtd-spinor-working origin/mtd-spinor-working
[1] https://lists.denx.de/pipermail/u-boot/2016-October/271459.html [2] http://lists.denx.de/pipermail/u-boot/2016-March/249286.html [3] http://lists.denx.de/pipermail/u-boot/2016-February/245418.html [4] [PATCH RFC v8 00/16] SPI-NOR/MTD addition
Jagan Teki (27): mtd: Add mtd core ops mtd: add mtd device create operations mtd: add SPI-NOR core support mtd: spi-nor: sync/modify sst write operations mtd: spi-nor: sync/modify lock operations mtd: spi-nor: Kconfig: Add MTD_SPI_NOR entry mtd: spi-nor: Kconfig: Add MTD_SPI_NOR_USE_4K_SECTORS mtd: spi-nor: Kconfig: Add SPI_NOR_MISC entry mtd: spi-nor: Kconfig: Add SPI_NOR_MACRONIX entry mtd: spi-nor: Kconfig: Add SPI_NOR_SPANSION entry mtd: spi-nor: Kconfig: Add SPI_NOR_STMICRO entry mtd: spi-nor: Kconfig: Add SPI_NOR_SST entry mtd: spi-nor: Kconfig: Add SPI_NOR_WINBOND entry mtd-uclass: use platdata_auto_alloc spi: Add spi_write_then_read mtd: spi-nor: Add m25p80 driver mtd: spi-nor: Kconfig: Add MTD_M25P80 entry mtd: spi-nor: Add zynq qspinor driver mtd: spi-nor: zynq_qspi: Kconfig: Add MTD_ZYNQ mtd: spi-nor: Add 4-byte addresswidth support cmd: add spinor cmd support cmd: spinor: sync/update protect command board_r: initialize spi_nor env: add spi-nor environment arm: dts: zynq: Add zynq-qspinor node dm: zynq: microzed: enable MTD/SPI-NOR framework test: dm: add tests for mtd devices
Makefile | 1 + arch/arm/dts/zynq-7000.dtsi | 12 + arch/arm/dts/zynq-microzed.dts | 12 +- cmd/Kconfig | 5 + cmd/Makefile | 1 + cmd/nvedit.c | 1 + cmd/spinor.c | 326 ++++++++++++ common/board_r.c | 13 + configs/sandbox_defconfig | 8 +- configs/zynq_microzed_defconfig | 17 +- drivers/mtd/Kconfig | 2 + drivers/mtd/Makefile | 7 +- drivers/mtd/mtd-uclass.c | 203 +++++++- drivers/mtd/spi-nor/Kconfig | 89 ++++ drivers/mtd/spi-nor/Makefile | 15 + drivers/mtd/spi-nor/m25p80.c | 251 ++++++++++ drivers/mtd/spi-nor/spi-nor-ids.c | 184 +++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 160 ++++++ drivers/mtd/spi-nor/spi-nor.c | 940 +++++++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/zynq_qspinor.c | 622 +++++++++++++++++++++++ drivers/spi/spi-uclass.c | 24 + env/Kconfig | 27 + env/Makefile | 1 + env/env.c | 2 + env/spinor.c | 113 +++++ include/dm/uclass-id.h | 1 + include/environment.h | 1 + include/linux/mtd/mtd.h | 14 + include/linux/mtd/spi-nor.h | 223 +++++++++ include/mtd.h | 176 +++++++ include/spi.h | 20 + test/dm/Makefile | 1 + test/dm/mtd.c | 33 ++ 33 files changed, 3493 insertions(+), 12 deletions(-) create mode 100644 cmd/spinor.c create mode 100644 drivers/mtd/spi-nor/Kconfig create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/m25p80.c create mode 100644 drivers/mtd/spi-nor/spi-nor-ids.c create mode 100644 drivers/mtd/spi-nor/spi-nor-uclass.c create mode 100644 drivers/mtd/spi-nor/spi-nor.c create mode 100644 drivers/mtd/spi-nor/zynq_qspinor.c create mode 100644 env/spinor.c create mode 100644 include/linux/mtd/spi-nor.h create mode 100644 test/dm/mtd.c

- Add generic mtd operations - Add mtd_dread|derase|dwrite
The respetive MTD_UCLASS drivers must install the hooks to these mtd_ops and other core ops are act as a interface b/w drivers vs command code.
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/mtd-uclass.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ include/mtd.h | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+)
diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c index 7b7c48e..d2c587f 100644 --- a/drivers/mtd/mtd-uclass.c +++ b/drivers/mtd/mtd-uclass.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2016 Jagan Teki jagan@openedev.com * Copyright (C) 2015 Thomas Chou thomas@wytron.com.tw * * SPDX-License-Identifier: GPL-2.0+ @@ -8,6 +9,66 @@ #include <dm.h> #include <errno.h> #include <mtd.h> +#include <linux/log2.h> + +int mtd_dread(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, + u_char *buf) +{ + struct udevice *dev = mtd->dev; + const struct mtd_ops *ops = mtd_get_ops(dev); + + if (!ops->read) + return -ENOSYS; + + *retlen = 0; + if (from < 0 || from > mtd->size || len > mtd->size - from) + return -EINVAL; + if (!len) + return 0; + + return ops->read(dev, from, len, retlen, buf); +} + +int mtd_derase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct udevice *dev = mtd->dev; + const struct mtd_ops *ops = mtd_get_ops(dev); + + if (!ops->erase) + return -ENOSYS; + + if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr) + return -EINVAL; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + if (!instr->len) { + instr->state = MTD_ERASE_DONE; + return 0; + } + + return ops->erase(dev, instr); +} + +int mtd_dwrite(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, + const u_char *buf) +{ + struct udevice *dev = mtd->dev; + const struct mtd_ops *ops = mtd_get_ops(dev); + + if (!ops->write) + return -ENOSYS; + + *retlen = 0; + if (to < 0 || to > mtd->size || len > mtd->size - to) + return -EINVAL; + if (!ops->write || !(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (!len) + return 0; + + return ops->write(dev, to, len, retlen, buf); +}
/* * Implement a MTD uclass which should include most flash drivers. diff --git a/include/mtd.h b/include/mtd.h index 3f8c293..32b11c3 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -20,4 +20,50 @@ static inline struct mtd_info *mtd_get_info(struct udevice *dev) return dev_get_uclass_priv(dev); }
+struct mtd_ops { + int (*erase)(struct udevice *dev, struct erase_info *instr); + int (*read)(struct udevice *dev, loff_t from, size_t len, + size_t *retlen, u_char *buf); + int (*write)(struct udevice *dev, loff_t to, size_t len, + size_t *retlen, const u_char *buf); +}; + +/* Access the serial operations for a device */ +#define mtd_get_ops(dev) ((struct mtd_ops *)(dev)->driver->ops) + +/** + * mtd_dread() - read data from MTD device + * + * @mtd: MTD device + * @from: offset into device in bytes to read from + * @len: length of bytes to read + * @retlen: length of return bytes read to + * @buf: buffer to put the data that is read + * @return 0 if OK, -ve on error + */ +int mtd_dread(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, + u_char *buf); + +/** + * mtd_dwrite() - write data to MTD device + * + * @mtd: MTD device + * @to: offset into device in bytes to write to + * @len: length of bytes to write + * @retlen: length of return bytes to write to + * @buf: buffer containing bytes to write + * @return 0 if OK, -ve on error + */ +int mtd_dwrite(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, + const u_char *buf); + +/** + * mtd_derase() - erase blocks of the MTD device + * + * @mtd: MTD device + * @instr: erase info details of MTD device + * @return 0 if OK, -ve on error + */ +int mtd_derase(struct mtd_info *mtd, struct erase_info *instr); + #endif /* _MTD_H_ */

mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spi-nor, dataflash etc, these are terminated as interface types in u-boot driver model.
This patch add common way of creating mtd device for respective underlying interface type. once the interface driver bind happen, the receptive uclass will pass an interface type to mtd layer to create a device for it.
MTD_IF_TYPE_SPI_NOR in an interface type for all spi nor devices.
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/mtd-uclass.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/mtd.h | 14 ++++++ include/mtd.h | 116 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+)
diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c index d2c587f..c94afe9 100644 --- a/drivers/mtd/mtd-uclass.c +++ b/drivers/mtd/mtd-uclass.c @@ -11,6 +11,9 @@ #include <mtd.h> #include <linux/log2.h>
+#include <dm/device-internal.h> +#include <dm/lists.h> + int mtd_dread(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -70,6 +73,124 @@ int mtd_dwrite(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, return ops->write(dev, to, len, retlen, buf); }
+int mtd_find_device(int mtd_if_type, int devnum, struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + ret = uclass_get(UCLASS_MTD, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + + debug("%s: mtd_if_type=%d, devnum=%d: %s, %d, %d\n", __func__, + mtd_if_type, devnum, dev->name, mtd->mtd_if_type, mtd->devnum); + if (mtd->mtd_if_type == mtd_if_type && mtd->devnum == devnum) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int mtd_get_device(int mtd_if_type, int devnum, struct udevice **devp) +{ + int ret; + + ret = mtd_find_device(mtd_if_type, devnum, devp); + if (ret) + return ret; + + return device_probe(*devp); +} + +int mtd_select_devnum(enum mtd_if_type mtd_if_type, int devnum) +{ + struct udevice *dev; + + return mtd_get_device(mtd_if_type, devnum, &dev); +} + +int mtd_find_max_devnum(enum mtd_if_type mtd_if_type) +{ + struct udevice *dev; + int max_devnum = -ENODEV; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_MTD, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + + if (mtd->mtd_if_type == mtd_if_type && mtd->devnum > max_devnum) + max_devnum = mtd->devnum; + } + + return max_devnum; +} + +static int mtd_next_free_devnum(enum mtd_if_type mtd_if_type) +{ + int ret; + + ret = mtd_find_max_devnum(mtd_if_type); + if (ret == -ENODEV) + return 0; + if (ret < 0) + return ret; + + return ret + 1; +} + +int mtd_create_device(struct udevice *parent, const char *drv_name, + const char *name, int mtd_if_type, struct udevice **devp) +{ + struct mtd_info *mtd; + struct udevice *dev; + int devnum, ret; + + devnum = mtd_next_free_devnum(mtd_if_type); + if (devnum < 0) + return devnum; + ret = device_bind_driver(parent, drv_name, name, &dev); + if (ret) + return ret; + mtd = dev_get_uclass_platdata(dev); + mtd->mtd_if_type = mtd_if_type; + mtd->dev = dev; + mtd->devnum = devnum; + *devp = dev; + + return 0; +} + +int mtd_create_devicef(struct udevice *parent, const char *drv_name, + const char *name, int mtd_if_type, + struct udevice **devp) +{ + char dev_name[30], *str; + int ret; + + snprintf(dev_name, sizeof(dev_name), "%s.%s", parent->name, name); + str = strdup(dev_name); + if (!str) + return -ENOMEM; + + ret = mtd_create_device(parent, drv_name, str, mtd_if_type, devp); + if (ret) { + free(str); + return ret; + } + device_set_name_alloced(*devp); + + return ret; +} + /* * Implement a MTD uclass which should include most flash drivers. * The uclass private is pointed to mtd_info. diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index ba4cbba..6ec9763 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -149,6 +149,16 @@ struct nand_ecclayout {
struct module; /* only needed for owner field in mtd_info */
+#ifdef CONFIG_MTD +/* Interface types: */ +enum mtd_if_type { + MTD_IF_TYPE_UNKNOWN = 0, + MTD_IF_TYPE_SPI_NOR, + + MTD_IF_TYPE_COUNT, +}; +#endif + struct mtd_info { u_char type; uint32_t flags; @@ -309,6 +319,10 @@ struct mtd_info { struct udevice *dev; #endif int usecount; +#ifdef CONFIG_MTD + enum mtd_if_type mtd_if_type; /* type of mtd interface */ + int devnum; /* device number */ +#endif };
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, diff --git a/include/mtd.h b/include/mtd.h index 32b11c3..273b3a6 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -66,4 +66,120 @@ int mtd_dwrite(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, */ int mtd_derase(struct mtd_info *mtd, struct erase_info *instr);
+/** + * mtd_find_device() - find a mtd device + * + * This function does not activate the device. The device will be returned + * whether or not it is activated. + * + * @mtd_if_type: interface type (enum mtd_if_type_t) + * @devnum: device number (specific to each interface type) + * @devp: the device, if found + * @return 0 if found, -ENODEV if no device found, or other -ve error value + */ +int mtd_find_device(int mtd_if_type, int devnum, struct udevice **devp); + +/** + * mtd_get_device() - find and probe a mtd device ready for use + * + * @mtd_if_type: interface type (enum mtd_if_type_t) + * @devnum: device number (specific to each interface type) + * @devp: the device, if found + * @return 0 if found, -ENODEV if no device found, or other -ve error value + */ +int mtd_get_device(int mtd_if_type, int devnum, struct udevice **devp); + +/** + * mtd_first_device() - find the first device for a given interface + * + * The device is probed ready for use + * + * @devnum: device number (specific to each interface type) + * @devp: the device, if found + * @return 0 if found, -ENODEV if no device, or other -ve error value + */ +int mtd_first_device(int mtd_if_type, struct udevice **devp); + +/** + * mtd_next_device() - find the next device for a given interface + * + * This can be called repeatedly after mtd_first_device() to iterate through + * all devices of the given interface type. + * + * The device is probed ready for use + * + * @devp: on entry, the previous device returned. On exit, the next + * device, if found + * @return 0 if found, -ENODEV if no device, or other -ve error value + */ +int mtd_next_device(struct udevice **devp); + +/** + * mtd_create_device() - create a new mtd device + * + * @parent: parent of the new device + * @drv_name: driver name to use for the mtd device + * @name: name for the device + * @mtd_if_type: Interface type (enum mtd_if_type_t) + * @devp: the new device (which has not been probed) + */ +int mtd_create_device(struct udevice *parent, const char *drv_name, + const char *name, int mtd_if_type, + struct udevice **devp); + +/** + * mtd_create_devicef() - Cceate a new named mtd device + * + * @parent: parent of the new device + * @drv_name: driver name to use for the mtd device + * @name: name for the device (parent name is prepended) + * @mtd_if_type: interface type (enum mtd_if_type_t) + * @devp: the new device (which has not been probed) + */ +int mtd_create_devicef(struct udevice *parent, const char *drv_name, + const char *name, int mtd_if_type, + struct udevice **devp); + +/** + * mtd_prepare_device() - prepare a mtd device for use + * + * This reads partition information from the device if supported. + * + * @dev: device to prepare + * @return 0 if ok, -ve on error + */ +int mtd_prepare_device(struct udevice *dev); + +/** + * mtd_unbind_all() - unbind all device of the given interface type + * + * The devices are removed and then unbound. + * + * @mtd_if_type: interface type to unbind + * @return 0 if OK, -ve on error + */ +int mtd_unbind_all(int mtd_if_type); + +/** + * mtd_find_max_devnum() - find the maximum device number for an interface type + * + * Finds the last allocated device number for an interface type @mtd_if_type. The + * next number is safe to use for a newly allocated device. + * + * @mtd_if_type: interface type to scan + * @return maximum device number found, or -ENODEV if none, or other -ve on + * error + */ +int mtd_find_max_devnum(enum mtd_if_type mtd_if_type); + +/** + * mtd_select_devnum() - select the mtd device from device number + * + * @mtd_if_type: interface type to scan + * @devnum: device number, specific to the interface type, or -1 to + * @return maximum device number found, or -ENODEV if none, or other -ve on + * error + */ +int mtd_select_devnum(enum mtd_if_type mtd_if_type, int devnum); + #endif /* _MTD_H_ */

Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- Makefile | 1 + drivers/mtd/spi-nor/Makefile | 7 + drivers/mtd/spi-nor/spi-nor-ids.c | 184 +++++++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 143 +++++++++ drivers/mtd/spi-nor/spi-nor.c | 569 +++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/linux/mtd/spi-nor.h | 217 +++++++++++++ 7 files changed, 1122 insertions(+) create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/spi-nor-ids.c create mode 100644 drivers/mtd/spi-nor/spi-nor-uclass.c create mode 100644 drivers/mtd/spi-nor/spi-nor.c create mode 100644 include/linux/mtd/spi-nor.h
diff --git a/Makefile b/Makefile index e6d309a..70b5202 100644 --- a/Makefile +++ b/Makefile @@ -662,6 +662,7 @@ libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/ libs-y += drivers/mtd/onenand/ libs-$(CONFIG_CMD_UBI) += drivers/mtd/ubi/ libs-y += drivers/mtd/spi/ +libs-y += drivers/mtd/spi-nor/ libs-y += drivers/net/ libs-y += drivers/net/phy/ libs-y += drivers/pci/ diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 0000000..c1212a8 --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (C) 2016 Jagan Teki jagan@openedev.com +# +# SPDX-License-Identifier: GPL-2.0+ + +## spi-nor core +obj-y += spi-nor.o spi-nor-uclass.o spi-nor-ids.o diff --git a/drivers/mtd/spi-nor/spi-nor-ids.c b/drivers/mtd/spi-nor/spi-nor-ids.c new file mode 100644 index 0000000..db95655 --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-ids.c @@ -0,0 +1,184 @@ +/* + * SPI NOR IDs. + * + * Copyright (C) 2016 Jagan Teki jagan@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/mtd/spi-nor.h> + +/* Used when the "_ext_id" is two bytes at most */ +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 16) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = 6, \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +const struct spi_nor_info spi_nor_ids[] = { +#ifdef CONFIG_SPI_NOR_MACRONIX /* MACRONIX */ + {"mx25l2006e", INFO(0xc22012, 0x0, 64 * 1024, 4, 0) }, + {"mx25l4005", INFO(0xc22013, 0x0, 64 * 1024, 8, 0) }, + {"mx25l8005", INFO(0xc22014, 0x0, 64 * 1024, 16, 0) }, + {"mx25l1605d", INFO(0xc22015, 0x0, 64 * 1024, 32, 0) }, + {"mx25l3205d", INFO(0xc22016, 0x0, 64 * 1024, 64, 0) }, + {"mx25l6405d", INFO(0xc22017, 0x0, 64 * 1024, 128, 0) }, + {"mx25l12805", INFO(0xc22018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"mx25l25635f", INFO(0xc22019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP) }, + {"mx25l51235f", INFO(0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"mx25u6435f", INFO(0xc22537, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP) }, + {"mx25l12855e", INFO(0xc22618, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"mx25u1635e", INFO(0xc22535, 0x0, 64 * 1024, 32, SECT_4K) }, + {"mx66u51235f", INFO(0xc2253a, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"mx66l1g45g", INFO(0xc2201b, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP) }, +#endif +#ifdef CONFIG_SPI_NOR_SPANSION /* SPANSION */ + {"s25fl008a", INFO(0x010213, 0x0, 64 * 1024, 16, 0) }, + {"s25fl016a", INFO(0x010214, 0x0, 64 * 1024, 32, 0) }, + {"s25fl032a", INFO(0x010215, 0x0, 64 * 1024, 64, 0) }, + {"s25fl064a", INFO(0x010216, 0x0, 64 * 1024, 128, 0) }, + {"s25fl116k", INFO(0x014015, 0x0, 64 * 1024, 32, 0) }, + {"s25fl164k", INFO(0x014017, 0x0140, 64 * 1024, 128, 0) }, + {"s25fl128p_256k", INFO(0x012018, 0x0300, 256 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl128p_64k", INFO(0x012018, 0x0301, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, RD_FULL | WR_QPP) }, + {"s25fl128s_256k", INFO(0x012018, 0x4d00, 256 * 1024, 64, RD_FULL | WR_QPP) }, + {"s25fl128s_64k", INFO(0x012018, 0x4d01, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl256s_256k", INFO(0x010219, 0x4d00, 256 * 1024, 128, RD_FULL | WR_QPP) }, + {"s25fs256s_64k", INFO6(0x010219, 0x4d0181, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"s25fl256s_64k", INFO(0x010219, 0x4d01, 64 * 1024, 512, RD_FULL | WR_QPP) }, + {"s25fs512s", INFO6(0x010220, 0x4d0081, 128 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"s25fl512s_256k", INFO(0x010220, 0x4d00, 256 * 1024, 256, RD_FULL | WR_QPP) }, + {"s25fl512s_64k", INFO(0x010220, 0x4d01, 64 * 1024, 1024, RD_FULL | WR_QPP) }, + {"s25fl512s_512k", INFO(0x010220, 0x4f00, 256 * 1024, 256, RD_FULL | WR_QPP) }, +#endif +#ifdef CONFIG_SPI_NOR_STMICRO /* STMICRO */ + {"m25p10", INFO(0x202011, 0x0, 32 * 1024, 4, 0) }, + {"m25p20", INFO(0x202012, 0x0, 64 * 1024, 4, 0) }, + {"m25p40", INFO(0x202013, 0x0, 64 * 1024, 8, 0) }, + {"m25p80", INFO(0x202014, 0x0, 64 * 1024, 16, 0) }, + {"m25p16", INFO(0x202015, 0x0, 64 * 1024, 32, 0) }, + {"m25pE16", INFO(0x208015, 0x1000, 64 * 1024, 32, 0) }, + {"m25pX16", INFO(0x207115, 0x1000, 64 * 1024, 32, RD_QUAD | RD_DUAL) }, + {"m25p32", INFO(0x202016, 0x0, 64 * 1024, 64, 0) }, + {"m25p64", INFO(0x202017, 0x0, 64 * 1024, 128, 0) }, + {"m25p128", INFO(0x202018, 0x0, 256 * 1024, 64, 0) }, + {"m25pX64", INFO(0x207117, 0x0, 64 * 1024, 128, SECT_4K) }, + {"n25q016a", INFO(0x20bb15, 0x0, 64 * 1024, 32, SECT_4K) }, + {"n25q32", INFO(0x20ba16, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q32a", INFO(0x20bb16, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q64", INFO(0x20ba17, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q64a", INFO(0x20bb17, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q128", INFO(0x20ba18, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"n25q128a", INFO(0x20bb18, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP) }, + {"n25q256", INFO(0x20ba19, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q256a", INFO(0x20bb19, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"n25q512", INFO(0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q512a", INFO(0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q1024", INFO(0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"n25q1024a", INFO(0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"mt25qu02g", INFO(0x20bb22, 0x0, 64 * 1024, 4096, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"mt25ql02g", INFO(0x20ba22, 0x0, 64 * 1024, 4096, RD_FULL | WR_QPP | E_FSR | SECT_4K) }, + {"mt35xu512g", INFO6(0x2c5b1a, 0x104100, 128 * 1024, 512, E_FSR | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_NOR_SST /* SST */ + {"sst25vf040b", INFO(0xbf258d, 0x0, 64 * 1024, 8, SECT_4K | SST_WR) }, + {"sst25vf080b", INFO(0xbf258e, 0x0, 64 * 1024, 16, SECT_4K | SST_WR) }, + {"sst25vf016b", INFO(0xbf2541, 0x0, 64 * 1024, 32, SECT_4K | SST_WR) }, + {"sst25vf032b", INFO(0xbf254a, 0x0, 64 * 1024, 64, SECT_4K | SST_WR) }, + {"sst25vf064c", INFO(0xbf254b, 0x0, 64 * 1024, 128, SECT_4K) }, + {"sst25wf512", INFO(0xbf2501, 0x0, 64 * 1024, 1, SECT_4K | SST_WR) }, + {"sst25wf010", INFO(0xbf2502, 0x0, 64 * 1024, 2, SECT_4K | SST_WR) }, + {"sst25wf020", INFO(0xbf2503, 0x0, 64 * 1024, 4, SECT_4K | SST_WR) }, + {"sst25wf040", INFO(0xbf2504, 0x0, 64 * 1024, 8, SECT_4K | SST_WR) }, + {"sst25wf040b", INFO(0x621613, 0x0, 64 * 1024, 8, SECT_4K) }, + {"sst25wf080", INFO(0xbf2505, 0x0, 64 * 1024, 16, SECT_4K | SST_WR) }, +#endif +#ifdef CONFIG_SPI_NOR_WINBOND /* WINBOND */ + {"w25p80", INFO(0xef2014, 0x0, 64 * 1024, 16, 0) }, + {"w25p16", INFO(0xef2015, 0x0, 64 * 1024, 32, 0) }, + {"w25p32", INFO(0xef2016, 0x0, 64 * 1024, 64, 0) }, + {"w25x40", INFO(0xef3013, 0x0, 64 * 1024, 8, SECT_4K) }, + {"w25x16", INFO(0xef3015, 0x0, 64 * 1024, 32, SECT_4K) }, + {"w25x32", INFO(0xef3016, 0x0, 64 * 1024, 64, SECT_4K) }, + {"w25x64", INFO(0xef3017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"w25q80bl", INFO(0xef4014, 0x0, 64 * 1024, 16, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q16cl", INFO(0xef4015, 0x0, 64 * 1024, 32, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q32bv", INFO(0xef4016, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q64cv", INFO(0xef4017, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q128bv", INFO(0xef4018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q256", INFO(0xef4019, 0x0, 64 * 1024, 512, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q80bw", INFO(0xef5014, 0x0, 64 * 1024, 16, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q16dw", INFO(0xef6015, 0x0, 64 * 1024, 32, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q32dw", INFO(0xef6016, 0x0, 64 * 1024, 64, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q64dw", INFO(0xef6017, 0x0, 64 * 1024, 128, RD_FULL | WR_QPP | SECT_4K) }, + {"w25q128fw", INFO(0xef6018, 0x0, 64 * 1024, 256, RD_FULL | WR_QPP | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_NOR_MISC + /* ATMEL */ + {"at45db011d", INFO(0x1f2200, 0x0, 64 * 1024, 4, SECT_4K) }, + {"at45db021d", INFO(0x1f2300, 0x0, 64 * 1024, 8, SECT_4K) }, + {"at45db041d", INFO(0x1f2400, 0x0, 64 * 1024, 8, SECT_4K) }, + {"at45db081d", INFO(0x1f2500, 0x0, 64 * 1024, 16, SECT_4K) }, + {"at45db161d", INFO(0x1f2600, 0x0, 64 * 1024, 32, SECT_4K) }, + {"at45db321d", INFO(0x1f2700, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at45db641d", INFO(0x1f2800, 0x0, 64 * 1024, 128, SECT_4K) }, + {"at25df321a", INFO(0x1f4701, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at25df321", INFO(0x1f4700, 0x0, 64 * 1024, 64, SECT_4K) }, + {"at26df081a", INFO(0x1f4501, 0x0, 64 * 1024, 16, SECT_4K) }, + + /* EON */ + {"en25q32b", INFO(0x1c3016, 0x0, 64 * 1024, 64, 0) }, + {"en25q64", INFO(0x1c3017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"en25q128b", INFO(0x1c3018, 0x0, 64 * 1024, 256, 0) }, + {"en25s64", INFO(0x1c3817, 0x0, 64 * 1024, 128, 0) }, + + /* GIGADEVICE */ + {"gd25q64b", INFO(0xc84017, 0x0, 64 * 1024, 128, SECT_4K) }, + {"gd25lq32", INFO(0xc86016, 0x0, 64 * 1024, 64, SECT_4K) }, + + /* ISSI */ + {"is25lq040b", INFO(0x9d4013, 0x0, 64 * 1024, 8, 0) }, + {"is25lp032", INFO(0x9d6016, 0x0, 64 * 1024, 64, 0) }, + {"is25lp064", INFO(0x9d6017, 0x0, 64 * 1024, 128, 0) }, + {"is25lp128", INFO(0x9d6018, 0x0, 64 * 1024, 256, 0) }, +#endif + {}, /* Empty entry to terminate the list */ + /* + * Note: + * Below paired flash devices has similar spi_nor params. + * (s25fl129p_64k, s25fl128s_64k) + * (w25q80bl, w25q80bv) + * (w25q16cl, w25q16dv) + * (w25q32bv, w25q32fv_spi) + * (w25q64cv, w25q64fv_spi) + * (w25q128bv, w25q128fv_spi) + * (w25q32dw, w25q32fv_qpi) + * (w25q64dw, w25q64fv_qpi) + * (w25q128fw, w25q128fv_qpi) + */ +}; diff --git a/drivers/mtd/spi-nor/spi-nor-uclass.c b/drivers/mtd/spi-nor/spi-nor-uclass.c new file mode 100644 index 0000000..919682d --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-uclass.c @@ -0,0 +1,143 @@ +/* + * SPI NOR Core framework. + * + * Copyright (C) 2016 Jagan Teki jagan@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <mtd.h> + +#include <dm/device-internal.h> +#include <linux/mtd/spi-nor.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev) +{ + struct spi_nor_uclass_priv *upriv; + + if (!device_active(dev)) + return NULL; + upriv = dev_get_uclass_priv(dev); + return upriv->spi_nor; +} + +struct spi_nor *find_spi_nor_device(int dev_num) +{ + struct udevice *dev, *spi_nor_dev; + int ret; + + ret = mtd_find_device(MTD_IF_TYPE_SPI_NOR, dev_num, &dev); + if (ret) { + printf("SPI-NOR Device %d not found\n", dev_num); + return NULL; + } + + spi_nor_dev = dev_get_parent(dev); + + struct spi_nor *nor = spi_nor_get_spi_nor_dev(spi_nor_dev); + + return nor; +} + +int get_spi_nor_num(void) +{ + return max((mtd_find_max_devnum(MTD_IF_TYPE_SPI_NOR) + 1), 0); +} + +struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor) +{ + struct mtd_info *mtd; + struct udevice *dev; + + device_find_first_child(nor->dev, &dev); + if (!dev) + return NULL; + mtd = dev_get_uclass_platdata(dev); + + return mtd; +} + +void print_spi_nor_devices(char separator) +{ + struct udevice *dev; + bool first = true; + + for (uclass_first_device(UCLASS_SPI_NOR, &dev); + dev; + uclass_next_device(&dev), first = false) { + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + + if (!first) { + printf("%c", separator); + if (separator != '\n') + puts(" "); + } + + printf("%s: %d", dev->name, spi_nor_get_mtd_info(nor)->devnum); + } + + printf("\n"); +} + +int spi_nor_bind(struct udevice *dev, struct spi_nor *nor) +{ + struct udevice *mdev; + int ret; + + if (!spi_nor_get_ops(dev)) + return -ENOSYS; + + ret = mtd_create_devicef(dev, "spinor_mtd", "mtd", MTD_IF_TYPE_SPI_NOR, + &mdev); + if (ret) { + debug("Cannot create mtd device\n"); + return ret; + } + nor->dev = dev; + + return 0; +} + +static int spi_nor_mtd_probe(struct udevice *dev) +{ + struct udevice *spi_nor_dev = dev_get_parent(dev); + struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(spi_nor_dev); + struct spi_nor *nor = upriv->spi_nor; + int ret; + + ret = spi_nor_scan(nor); + if (ret) { + debug("%s: spi_nor_scan() failed (err=%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +static const struct mtd_ops spi_nor_mtd_ops = { + .read = spi_nor_mread, + .erase = spi_nor_merase, +}; + +U_BOOT_DRIVER(spinor_mtd) = { + .name = "spinor_mtd", + .id = UCLASS_MTD, + .ops = &spi_nor_mtd_ops, + .probe = spi_nor_mtd_probe, +}; + +U_BOOT_DRIVER(spinor) = { + .name = "spinor", + .id = UCLASS_SPI_NOR, +}; + +UCLASS_DRIVER(spinor) = { + .id = UCLASS_SPI_NOR, + .name = "spinor", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .per_device_auto_alloc_size = sizeof(struct spi_nor_uclass_priv), +}; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c new file mode 100644 index 0000000..09fb8ca --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -0,0 +1,569 @@ +/* + * SPI NOR Core framework. + * + * Copyright (C) 2016 Jagan Teki jagan@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <mapmem.h> +#include <mtd.h> + +#include <dm/device-internal.h> +#include <linux/math64.h> +#include <linux/types.h> +#include <linux/mtd/spi-nor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Set write enable latch with Write Enable command */ +static inline int write_enable(struct udevice *dev) +{ + return spi_nor_get_ops(dev)->write_reg(dev, SNOR_OP_WREN, NULL, 0); +} + +/* Re-set write enable latch with Write Disable command */ +static inline int write_disable(struct udevice *dev) +{ + return spi_nor_get_ops(dev)->write_reg(dev, SNOR_OP_WRDI, NULL, 0); +} + +static int read_sr(struct udevice *dev) +{ + u8 sr; + int ret; + + ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDSR, &sr, 1); + if (ret < 0) { + debug("spi-nor: fail to read status register\n"); + return ret; + } + + return sr; +} + +static int read_fsr(struct udevice *dev) +{ + u8 fsr; + int ret; + + ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDFSR, &fsr, 1); + if (ret < 0) { + debug("spi-nor: fail to read flag status register\n"); + return ret; + } + + return fsr; +} + +static int write_sr(struct udevice *dev, u8 ws) +{ + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + const struct spi_nor_ops *ops = spi_nor_get_ops(dev); + + nor->cmd_buf[0] = ws; + return ops->write_reg(dev, SNOR_OP_WRSR, nor->cmd_buf, 1); +} + +#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) +static int read_cr(struct udevice *dev) +{ + u8 cr; + int ret; + + ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDCR, &cr, 1); + if (ret < 0) { + debug("spi-nor: fail to read config register\n"); + return ret; + } + + return cr; +} + +/* + * Write status Register and configuration register with 2 bytes + * - First byte will be written to the status register. + * - Second byte will be written to the configuration register. + * Return negative if error occured. + */ +static int write_sr_cr(struct udevice *dev, u16 val) +{ + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + const struct spi_nor_ops *ops = spi_nor_get_ops(dev); + + nor->cmd_buf[0] = val & 0xff; + nor->cmd_buf[1] = (val >> 8); + + return ops->write_reg(dev, SNOR_OP_WRSR, nor->cmd_buf, 2); +} +#endif + +static int spi_nor_sr_ready(struct udevice *dev) +{ + int sr = read_sr(dev); + if (sr < 0) + return sr; + else + return !(sr & SR_WIP); +} + +static int spi_nor_fsr_ready(struct udevice *dev) +{ + int fsr = read_fsr(dev); + if (fsr < 0) + return fsr; + else + return fsr & FSR_READY; +} + +static int spi_nor_ready(struct udevice *dev) +{ + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + int sr, fsr; + + sr = spi_nor_sr_ready(dev); + if (sr < 0) + return sr; + + fsr = 1; + if (nor->flags & SNOR_F_USE_FSR) { + fsr = spi_nor_fsr_ready(dev); + if (fsr < 0) + return fsr; + } + + return sr && fsr; +} + +static int spi_nor_wait_till_ready(struct udevice *dev, unsigned long timeout) +{ + int timebase, ret; + + timebase = get_timer(0); + + while (get_timer(timebase) < timeout) { + ret = spi_nor_ready(dev); + if (ret < 0) + return ret; + if (ret) + return 0; + } + + printf("spi-nor: Timeout!\n"); + + return -ETIMEDOUT; +} + +static const struct spi_nor_info *spi_nor_id(struct udevice *dev) +{ + int tmp; + u8 id[SPI_NOR_MAX_ID_LEN]; + const struct spi_nor_info *info; + const struct spi_nor_ops *ops = spi_nor_get_ops(dev); + + tmp = ops->read_reg(dev, SNOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + if (tmp < 0) { + printf("spi-nor: error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + + info = spi_nor_ids; + for (; info->name != NULL; info++) { + if (info->id_len) { + if (!memcmp(info->id, id, info->id_len)) + return info; + } + } + + printf("spi-nor: unrecognized JEDEC id bytes: %02x, %2x, %2x\n", + id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); +} + +int spi_nor_merase(struct udevice *dev, struct erase_info *instr) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + const struct spi_nor_ops *ops; + u32 addr, len, erase_addr; + uint32_t rem; + int ret = -1; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + ops = spi_nor_get_ops(nor->dev); + + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) + return -EINVAL; + + addr = instr->addr; + len = instr->len; + + while (len) { + erase_addr = addr; + + write_enable(nor->dev); + + ret = ops->write(nor->dev, erase_addr, 0, NULL); + if (ret < 0) + goto erase_err; + + ret = spi_nor_wait_till_ready(nor->dev, SNOR_READY_WAIT_ERASE); + if (ret < 0) + goto erase_err; + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + + write_disable(nor->dev); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; + +erase_err: + instr->state = MTD_ERASE_FAILED; + return ret; +} + +static int spi_nor_mwrite(struct udevice *dev, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + const struct spi_nor_ops *ops; + size_t addr, byte_addr; + size_t chunk_len, actual; + uint32_t page_size; + int ret = -1; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + ops = spi_nor_get_ops(nor->dev); + + page_size = mtd->writebufsize; + + for (actual = 0; actual < len; actual += chunk_len) { + addr = to; + + byte_addr = addr % page_size; + chunk_len = min(len - actual, (size_t)(page_size - byte_addr)); + + if (nor->max_write_size) + chunk_len = min(chunk_len, + (size_t)nor->max_write_size); + + write_enable(nor->dev); + + ret = ops->write(nor->dev, addr, chunk_len, buf + actual); + if (ret < 0) + break; + + ret = spi_nor_wait_till_ready(nor->dev, SNOR_READY_WAIT_PROG); + if (ret < 0) + return ret; + + to += chunk_len; + *retlen += chunk_len; + } + + return ret; +} + +int spi_nor_mread(struct udevice *dev, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + const struct spi_nor_ops *ops; + int ret; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + ops = spi_nor_get_ops(nor->dev); + + /* Handle memory-mapped SPI */ + if (nor->memory_map) { + ret = ops->read(nor->dev, from, len, buf); + if (ret) { + debug("spi-nor: mmap read failed\n"); + return ret; + } + + return ret; + } + + ret = ops->read(nor->dev, from, len, buf); + if (ret < 0) { + printf("%s ret = %d\n", __func__, ret); + return ret; + } + + *retlen += len; + + return ret; +} + +#ifdef CONFIG_SPI_NOR_MACRONIX +static int macronix_quad_enable(struct udevice *dev) +{ + int ret, val; + + val = read_sr(dev); + if (val < 0) + return val; + + if (val & SR_QUAD_EN_MX) + return 0; + + write_enable(dev); + + ret = write_sr(dev, val | SR_QUAD_EN_MX); + if (ret < 0) + return ret; + + if (spi_nor_wait_till_ready(dev, SNOR_READY_WAIT_PROG)) + return 1; + + ret = read_sr(dev); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + printf("spi-nor: Macronix Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} +#endif + +#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) +static int spansion_quad_enable(struct udevice *dev) +{ + int ret, val; + + val = read_cr(dev); + if (val < 0) + return val; + + if (val & CR_QUAD_EN_SPAN) + return 0; + + write_enable(dev); + + ret = write_sr_cr(dev, val | CR_QUAD_EN_SPAN); + if (ret < 0) + return ret; + + if (spi_nor_wait_till_ready(dev, SNOR_READY_WAIT_PROG)) + return 1; + + /* read back and check it */ + ret = read_cr(dev); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + printf("spi-nor: Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} +#endif + +static int set_quad_mode(struct udevice *dev, const struct spi_nor_info *info) +{ + switch (JEDEC_MFR(info)) { +#ifdef CONFIG_SPI_NOR_MACRONIX + case SNOR_MFR_MACRONIX: + return macronix_quad_enable(dev); +#endif +#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) + case SNOR_MFR_SPANSION: + case SNOR_MFR_WINBOND: + return spansion_quad_enable(dev); +#endif +#ifdef CONFIG_SPI_NOR_STMICRO + case SNOR_MFR_MICRON: + return 0; +#endif + default: + printf("spi-nor: Need set QEB func for %02x flash\n", + JEDEC_MFR(info)); + return -1; + } +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +int spi_nor_decode_fdt(const void *blob, struct spi_nor *nor) +{ + struct udevice *dev = nor->dev; + struct mtd_info *mtd = mtd_get_info(dev); + fdt_addr_t addr; + fdt_size_t size; + int node; + + /* If there is no node, do nothing */ + node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH); + if (node < 0) + return 0; + + addr = fdtdec_get_addr_size(blob, node, "memory-map", &size); + if (addr == FDT_ADDR_T_NONE) { + debug("%s: Cannot decode address\n", __func__); + return 0; + } + + if (mtd->size != size) { + debug("%s: Memory map must cover entire device\n", __func__); + return -1; + } + nor->memory_map = map_sysmem(addr, size); + + return 0; +} +#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ + +int spi_nor_scan(struct spi_nor *nor) +{ + struct mtd_info *mtd = spi_nor_get_mtd_info(nor); + struct mtd_ops *ops = mtd_get_ops(mtd->dev); + const struct spi_nor_info *info = NULL; + int ret; + + struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(nor->dev); + upriv->spi_nor = nor; + + if (nor->init_done) + return 0; + + info = spi_nor_id(nor->dev); + if (IS_ERR_OR_NULL(info)) { + ret = -ENOENT; + goto err; + } + + /* + * Flash powers up read-only, so clear BP# bits. + * + * Note on some flash (like Macronix), QE (quad enable) bit is in the + * same status register as BP# bits, and we need preserve its original + * value during a reboot cycle as this is required by some platforms + * (like Intel ICH SPI controller working under descriptor mode). + */ + if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || + (JEDEC_MFR(info) == SNOR_MFR_SST) || + (JEDEC_MFR(info) == SNOR_MFR_MACRONIX)) { + u8 sr = 0; + + if (JEDEC_MFR(info) == SNOR_MFR_MACRONIX) + sr = read_sr(nor->dev) & SR_QUAD_EN_MX; + write_sr(nor->dev, sr); + } + + mtd->name = info->name; + mtd->priv = nor; + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; + + if (info->flags & E_FSR) + nor->flags |= SNOR_F_USE_FSR; + + if (info->flags & SST_WR) + nor->flags |= SNOR_F_SST_WRITE; + + ops->write = spi_nor_mwrite; + + /* compute the flash size */ + nor->page_size = info->page_size; + /* + * The Spansion S25FL032P and S25FL064P have 256b pages, yet use the + * 0x4d00 Extended JEDEC code. The rest of the Spansion flashes with + * the 0x4d00 Extended JEDEC code have 512b pages. All of the others + * have 256b pages. + */ + if (JEDEC_EXT(info) == 0x4d00) { + if ((JEDEC_ID(info) != 0x0215) && + (JEDEC_ID(info) != 0x0216)) + nor->page_size = 512; + } + mtd->writebufsize = nor->page_size; + mtd->size = info->sector_size * info->n_sectors; + +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + nor->erase_opcode = SNOR_OP_BE_4K; + mtd->erasesize = 4096; + } else +#endif + { + nor->erase_opcode = SNOR_OP_SE; + mtd->erasesize = info->sector_size; + } + + /* Look for read opcode */ + nor->read_opcode = SNOR_OP_READ_FAST; + if (nor->mode & SNOR_READ) + nor->read_opcode = SNOR_OP_READ; + else if (nor->mode & SNOR_READ_1_1_4 && info->flags & RD_QUAD) + nor->read_opcode = SNOR_OP_READ_1_1_4; + else if (nor->mode & SNOR_READ_1_1_2 && info->flags & RD_DUAL) + nor->read_opcode = SNOR_OP_READ_1_1_2; + + /* Look for program opcode */ + if (info->flags & WR_QPP && nor->mode & SNOR_WRITE_1_1_4) + nor->program_opcode = SNOR_OP_QPP; + else + /* Go for default supported write cmd */ + nor->program_opcode = SNOR_OP_PP; + + /* Set the quad enable bit - only for quad commands */ + if ((nor->read_opcode == SNOR_OP_READ_1_1_4) || + (nor->read_opcode == SNOR_OP_READ_1_1_4_IO) || + (nor->program_opcode == SNOR_OP_QPP)) { + ret = set_quad_mode(nor->dev, info); + if (ret) { + debug("spi-nor: quad mode not supported for %02x\n", + JEDEC_MFR(info)); + goto err; + } + } + + nor->addr_width = 3; + + /* Dummy cycles for read */ + switch (nor->read_opcode) { + case SNOR_OP_READ_1_1_4_IO: + nor->read_dummy = 16; + break; + case SNOR_OP_READ: + nor->read_dummy = 0; + break; + default: + nor->read_dummy = 8; + } + +#if CONFIG_IS_ENABLED(OF_CONTROL) + ret = spi_nor_decode_fdt(gd->fdt_blob, nor); + if (ret) { + debug("spi-nor: FDT decode error\n"); + goto err; + } +#endif + + nor->init_done = 1; + return 0; +err: + nor->init_done = 0; + return ret; +} diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 3fc2083..06541a4 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -75,6 +75,7 @@ enum uclass_id { UCLASS_SERIAL, /* Serial UART */ UCLASS_SPI, /* SPI bus */ UCLASS_SPMI, /* System Power Management Interface bus */ + UCLASS_SPI_NOR, /* SPI NOR flash */ UCLASS_SPI_FLASH, /* SPI flash */ UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SYSCON, /* System configuration device */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1688e2 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,217 @@ +/* + * SPI NOR Core header file. + * + * Copyright (C) 2016 Jagan Teki jagan@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __MTD_SPI_NOR_H +#define __MTD_SPI_NOR_H + +#include <common.h> +#include <linux/mtd/mtd.h> + +/* + * Manufacturer IDs + * + * The first byte returned from the flash after sending opcode SPINOR_OP_RDID. + * Sometimes these are the same as CFI IDs, but sometimes they aren't. + */ +#define SNOR_MFR_ATMEL 0x1f +#define SNOR_MFR_MACRONIX 0xc2 +#define SNOR_MFR_MICRON 0x20 /* ST Micro <--> Micron */ +#define SNOR_MFR_SPANSION 0x01 +#define SNOR_MFR_SST 0xbf +#define SNOR_MFR_WINBOND 0xef + +/** + * SPI NOR opcodes. + * + * Note on opcode nomenclature: some opcodes have a format like + * SNOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number + * of I/O lines used for the opcode, address, and data (respectively). The + * FUNCTION has an optional suffix of '4', to represent an opcode which + * requires a 4-byte (32-bit) address. + */ +#define SNOR_OP_WRDI 0x04 /* Write disable */ +#define SNOR_OP_WREN 0x06 /* Write enable */ +#define SNOR_OP_RDSR 0x05 /* Read status register */ +#define SNOR_OP_WRSR 0x01 /* Write status register 1 byte */ +#define SNOR_OP_READ 0x03 /* Read data bytes (low frequency) */ +#define SNOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ +#define SNOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +#define SNOR_OP_READ_1_1_2_IO 0xbb /* Read data bytes (Dual IO SPI) */ +#define SNOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ +#define SNOR_OP_READ_1_1_4_IO 0xeb /* Read data bytes (Quad IO SPI) */ +#define SNOR_OP_BRWR 0x17 /* Bank register write */ +#define SNOR_OP_BRRD 0x16 /* Bank register read */ +#define SNOR_OP_WREAR 0xC5 /* Write extended address register */ +#define SNOR_OP_RDEAR 0xC8 /* Read extended address register */ +#define SNOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ +#define SNOR_OP_QPP 0x32 /* Quad Page program */ +#define SNOR_OP_BE_4K 0x20 /* Erase 4KiB block */ +#define SNOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define SNOR_OP_BE_32K 0x52 /* Erase 32KiB block */ +#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define SNOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define SNOR_OP_RDID 0x9f /* Read JEDEC ID */ +#define SNOR_OP_RDCR 0x35 /* Read configuration register */ +#define SNOR_OP_RDFSR 0x70 /* Read flag status register */ + +/* Used for SST flashes only. */ +#define SNOR_OP_BP 0x02 /* Byte program */ +#define SNOR_OP_AAI_WP 0xad /* Auto addr increment word program */ + +/* Status Register bits. */ +#define SR_WIP BIT(0) /* Write in progress */ +#define SR_WEL BIT(1) /* Write enable latch */ + +/* meaning of other SR_* bits may differ between vendors */ +#define SR_BP0 BIT(2) /* Block protect 0 */ +#define SR_BP1 BIT(3) /* Block protect 1 */ +#define SR_BP2 BIT(4) /* Block protect 2 */ +#define SR_SRWD BIT(7) /* SR write protect */ + +#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ + +/* Flag Status Register bits */ +#define FSR_READY BIT(7) + +/* Configuration Register bits. */ +#define CR_QUAD_EN_SPAN BIT(1) /* Spansion/Winbond Quad I/O */ + +/* Flash timeout values */ +#define SNOR_READY_WAIT_PROG (2 * CONFIG_SYS_HZ) +#define SNOR_READY_WAIT_ERASE (5 * CONFIG_SYS_HZ) +#define SNOR_MAX_CMD_SIZE 4 +#define SNOR_16MB_BOUN 0x1000000 + +/** + * struct spi_nor_uclass_priv - Holds information about a device used by the uclass + */ +struct spi_nor_uclass_priv { + struct spi_nor *spi_nor; +}; + +enum snor_option_flags { + SNOR_F_SST_WRITE = BIT(0), + SNOR_F_USE_FSR = BIT(1), + SNOR_F_U_PAGE = BIT(1), +}; + +enum mode { + SNOR_READ = BIT(0), + SNOR_READ_1_1_2 = BIT(1), + SNOR_READ_1_1_4 = BIT(2), + SNOR_READ_1_1_2_IO = BIT(3), + SNOR_READ_1_1_4_IO = BIT(4), + SNOR_WRITE_1_1_BYTE = BIT(5), + SNOR_WRITE_1_1_4 = BIT(6), +}; + +#define JEDEC_MFR(info) ((info)->id[0]) +#define JEDEC_ID(info) (((info)->id[1]) << 8 | ((info)->id[2])) +#define JEDEC_EXT(info) (((info)->id[3]) << 8 | ((info)->id[4])) +#define SPI_NOR_MAX_ID_LEN 6 + +struct spi_nor_info { + char *name; + + /* + * This array stores the ID bytes. + * The first three bytes are the JEDIC ID. + * JEDEC ID zero means "no ID" (mostly older chips). + */ + u8 id[SPI_NOR_MAX_ID_LEN]; + u8 id_len; + + /* The size listed here is what works with SNOR_OP_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + + u16 page_size; + + u16 flags; +#define SECT_4K BIT(0) +#define E_FSR BIT(1) +#define SST_WR BIT(2) +#define WR_QPP BIT(3) +#define RD_QUAD BIT(4) +#define RD_DUAL BIT(5) +#define RD_QUADIO BIT(6) +#define RD_DUALIO BIT(7) +#define RD_FULL (RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO) +}; + +extern const struct spi_nor_info spi_nor_ids[]; + +/** + * struct spi_nor - Structure for defining a the SPI NOR layer + * + * @dev: SPI NOR device + * @name: name of the SPI NOR device + * @page_size: the page size of the SPI NOR + * @addr_width: number of address bytes + * @erase_opcode: the opcode for erasing a sector + * @read_opcode: the read opcode + * @read_dummy: the dummy bytes needed by the read operation + * @program_opcode: the program opcode + * @max_write_size: If non-zero, the maximum number of bytes which can + * be written at once, excluding command bytes. + * @flags: flag options for the current SPI-NOR (SNOR_F_*) + * @mode: read, write mode or any other mode bits. + * @read_mode: read mode. + * @cmd_buf: used by the write_reg + * @read_reg: [DRIVER-SPECIFIC] read out the register + * @write_reg: [DRIVER-SPECIFIC] write data to the register + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @memory_map: address of read-only SPI NOR access + */ +struct spi_nor { + struct udevice *dev; + const char *name; + u8 init_done; + u32 page_size; + u8 addr_width; + u8 erase_opcode; + u8 read_opcode; + u8 read_dummy; + u8 program_opcode; + u32 max_write_size; + u32 flags; + u8 mode; + u8 read_mode; + u8 cmd_buf[SNOR_MAX_CMD_SIZE]; + + void *memory_map; +}; + +struct spi_nor_ops { + int (*read_reg)(struct udevice *dev, u8 cmd, u8 *val, int len); + int (*write_reg)(struct udevice *dev, u8 cmd, u8 *data, int len); + + int (*read)(struct udevice *dev, loff_t from, size_t len, + u_char *buf); + int (*write)(struct udevice *dev, loff_t to, size_t len, + const u_char *buf); +}; + +#define spi_nor_get_ops(dev) ((struct spi_nor_ops *)(dev)->driver->ops) + +int spi_nor_merase(struct udevice *dev, struct erase_info *instr); +int spi_nor_mread(struct udevice *dev, loff_t from, size_t len, + size_t *retlen, u_char *buf); + +int spi_nor_scan(struct spi_nor *nor); +int spi_nor_bind(struct udevice *dev, struct spi_nor *nor); + +struct spi_nor *find_spi_nor_device(int dev_num); +int get_spi_nor_num(void); +struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev); +struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor); + +#endif /* __MTD_SPI_NOR_H */

Hi Jagan:
2017-12-28 14:12 GMT+08:00 Jagan Teki jagan@amarulasolutions.com:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com
Makefile | 1 + drivers/mtd/spi-nor/Makefile | 7 + drivers/mtd/spi-nor/spi-nor-ids.c | 184 +++++++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 143 +++++++++ drivers/mtd/spi-nor/spi-nor.c | 569 ++++++++++++++++++++++++++++++ +++++ include/dm/uclass-id.h | 1 + include/linux/mtd/spi-nor.h | 217 +++++++++++++ 7 files changed, 1122 insertions(+) create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/spi-nor-ids.c create mode 100644 drivers/mtd/spi-nor/spi-nor-uclass.c create mode 100644 drivers/mtd/spi-nor/spi-nor.c create mode 100644 include/linux/mtd/spi-nor.h
diff --git a/Makefile b/Makefile index e6d309a..70b5202 100644 --- a/Makefile +++ b/Makefile @@ -662,6 +662,7 @@ libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/ libs-y += drivers/mtd/onenand/ libs-$(CONFIG_CMD_UBI) += drivers/mtd/ubi/ libs-y += drivers/mtd/spi/ +libs-y += drivers/mtd/spi-nor/ libs-y += drivers/net/ libs-y += drivers/net/phy/ libs-y += drivers/pci/ diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 0000000..c1212a8 --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (C) 2016 Jagan Teki jagan@openedev.com +# +# SPDX-License-Identifier: GPL-2.0+
+## spi-nor core +obj-y += spi-nor.o spi-nor-uclass.o spi-nor-ids.o diff --git a/drivers/mtd/spi-nor/spi-nor-ids.c b/drivers/mtd/spi-nor/spi-nor-ids.c new file mode 100644 index 0000000..db95655 --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-ids.c @@ -0,0 +1,184 @@ +/*
- SPI NOR IDs.
- Copyright (C) 2016 Jagan Teki jagan@openedev.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <linux/mtd/spi-nor.h>
+/* Used when the "_ext_id" is two bytes at most */ +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff, \
((_ext_id) >> 8) & 0xff, \
(_ext_id) & 0xff, \
}, \
\.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags),
+#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff, \
((_ext_id) >> 16) & 0xff, \
((_ext_id) >> 8) & 0xff, \
(_ext_id) & 0xff, \
}, \
.id_len = 6, \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags),
+const struct spi_nor_info spi_nor_ids[] = { +#ifdef CONFIG_SPI_NOR_MACRONIX /* MACRONIX */
{"mx25l2006e", INFO(0xc22012, 0x0, 64 * 1024, 4, 0) },
{"mx25l4005", INFO(0xc22013, 0x0, 64 * 1024, 8, 0) },
{"mx25l8005", INFO(0xc22014, 0x0, 64 * 1024, 16, 0) },
{"mx25l1605d", INFO(0xc22015, 0x0, 64 * 1024, 32, 0) },
{"mx25l3205d", INFO(0xc22016, 0x0, 64 * 1024, 64, 0) },
{"mx25l6405d", INFO(0xc22017, 0x0, 64 * 1024, 128, 0) },
{"mx25l12805", INFO(0xc22018, 0x0, 64 * 1024, 256, RD_FULL |
WR_QPP) },
{"mx25l25635f", INFO(0xc22019, 0x0, 64 * 1024, 512, RD_FULL |
WR_QPP) },
{"mx25l51235f", INFO(0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL |
WR_QPP) },
{"mx25u6435f", INFO(0xc22537, 0x0, 64 * 1024, 128, RD_FULL |
WR_QPP) },
{"mx25l12855e", INFO(0xc22618, 0x0, 64 * 1024, 256, RD_FULL |
WR_QPP) },
{"mx25u1635e", INFO(0xc22535, 0x0, 64 * 1024, 32, SECT_4K) },
{"mx66u51235f", INFO(0xc2253a, 0x0, 64 * 1024, 1024, RD_FULL |
WR_QPP) },
{"mx66l1g45g", INFO(0xc2201b, 0x0, 64 * 1024, 2048, RD_FULL |
WR_QPP) }, +#endif +#ifdef CONFIG_SPI_NOR_SPANSION /* SPANSION */
{"s25fl008a", INFO(0x010213, 0x0, 64 * 1024, 16, 0) },
{"s25fl016a", INFO(0x010214, 0x0, 64 * 1024, 32, 0) },
{"s25fl032a", INFO(0x010215, 0x0, 64 * 1024, 64, 0) },
{"s25fl064a", INFO(0x010216, 0x0, 64 * 1024, 128, 0) },
{"s25fl116k", INFO(0x014015, 0x0, 64 * 1024, 32, 0) },
{"s25fl164k", INFO(0x014017, 0x0140, 64 * 1024, 128, 0) },
{"s25fl128p_256k", INFO(0x012018, 0x0300, 256 * 1024, 64,
RD_FULL | WR_QPP) },
{"s25fl128p_64k", INFO(0x012018, 0x0301, 64 * 1024, 256,
RD_FULL | WR_QPP) },
{"s25fl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64,
RD_FULL | WR_QPP) },
{"s25fl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128,
RD_FULL | WR_QPP) },
{"s25fl128s_256k", INFO(0x012018, 0x4d00, 256 * 1024, 64,
RD_FULL | WR_QPP) },
{"s25fl128s_64k", INFO(0x012018, 0x4d01, 64 * 1024, 256,
RD_FULL | WR_QPP) },
{"s25fl256s_256k", INFO(0x010219, 0x4d00, 256 * 1024, 128,
RD_FULL | WR_QPP) },
{"s25fs256s_64k", INFO6(0x010219, 0x4d0181, 64 * 1024, 512,
RD_FULL | WR_QPP | SECT_4K) },
{"s25fl256s_64k", INFO(0x010219, 0x4d01, 64 * 1024, 512,
RD_FULL | WR_QPP) },
{"s25fs512s", INFO6(0x010220, 0x4d0081, 128 * 1024, 512,
RD_FULL | WR_QPP | SECT_4K) },
{"s25fl512s_256k", INFO(0x010220, 0x4d00, 256 * 1024, 256,
RD_FULL | WR_QPP) },
{"s25fl512s_64k", INFO(0x010220, 0x4d01, 64 * 1024, 1024,
RD_FULL | WR_QPP) },
{"s25fl512s_512k", INFO(0x010220, 0x4f00, 256 * 1024, 256,
RD_FULL | WR_QPP) }, +#endif +#ifdef CONFIG_SPI_NOR_STMICRO /* STMICRO */
{"m25p10", INFO(0x202011, 0x0, 32 * 1024, 4, 0) },
{"m25p20", INFO(0x202012, 0x0, 64 * 1024, 4, 0) },
{"m25p40", INFO(0x202013, 0x0, 64 * 1024, 8, 0) },
{"m25p80", INFO(0x202014, 0x0, 64 * 1024, 16, 0) },
{"m25p16", INFO(0x202015, 0x0, 64 * 1024, 32, 0) },
{"m25pE16", INFO(0x208015, 0x1000, 64 * 1024, 32, 0) },
{"m25pX16", INFO(0x207115, 0x1000, 64 * 1024, 32, RD_QUAD |
RD_DUAL) },
{"m25p32", INFO(0x202016, 0x0, 64 * 1024, 64, 0) },
{"m25p64", INFO(0x202017, 0x0, 64 * 1024, 128, 0) },
{"m25p128", INFO(0x202018, 0x0, 256 * 1024, 64, 0) },
{"m25pX64", INFO(0x207117, 0x0, 64 * 1024, 128, SECT_4K)
},
{"n25q016a", INFO(0x20bb15, 0x0, 64 * 1024, 32, SECT_4K)
},
{"n25q32", INFO(0x20ba16, 0x0, 64 * 1024, 64, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q32a", INFO(0x20bb16, 0x0, 64 * 1024, 64, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q64", INFO(0x20ba17, 0x0, 64 * 1024, 128, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q64a", INFO(0x20bb17, 0x0, 64 * 1024, 128, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q128", INFO(0x20ba18, 0x0, 64 * 1024, 256, RD_FULL
| WR_QPP) },
{"n25q128a", INFO(0x20bb18, 0x0, 64 * 1024, 256, RD_FULL
| WR_QPP) },
{"n25q256", INFO(0x20ba19, 0x0, 64 * 1024, 512, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q256a", INFO(0x20bb19, 0x0, 64 * 1024, 512, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q512", INFO(0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"n25q512a", INFO(0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"n25q1024", INFO(0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"n25q1024a", INFO(0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"mt25qu02g", INFO(0x20bb22, 0x0, 64 * 1024, 4096, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"mt25ql02g", INFO(0x20ba22, 0x0, 64 * 1024, 4096, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"mt35xu512g", INFO6(0x2c5b1a, 0x104100, 128 * 1024, 512,
E_FSR | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_NOR_SST /* SST */
{"sst25vf040b", INFO(0xbf258d, 0x0, 64 * 1024, 8, SECT_4K
| SST_WR) },
{"sst25vf080b", INFO(0xbf258e, 0x0, 64 * 1024, 16, SECT_4K
| SST_WR) },
{"sst25vf016b", INFO(0xbf2541, 0x0, 64 * 1024, 32, SECT_4K
| SST_WR) },
{"sst25vf032b", INFO(0xbf254a, 0x0, 64 * 1024, 64, SECT_4K
| SST_WR) },
{"sst25vf064c", INFO(0xbf254b, 0x0, 64 * 1024, 128, SECT_4K)
},
{"sst25wf512", INFO(0xbf2501, 0x0, 64 * 1024, 1, SECT_4K
| SST_WR) },
{"sst25wf010", INFO(0xbf2502, 0x0, 64 * 1024, 2, SECT_4K
| SST_WR) },
{"sst25wf020", INFO(0xbf2503, 0x0, 64 * 1024, 4, SECT_4K
| SST_WR) },
{"sst25wf040", INFO(0xbf2504, 0x0, 64 * 1024, 8, SECT_4K
| SST_WR) },
{"sst25wf040b", INFO(0x621613, 0x0, 64 * 1024, 8, SECT_4K)
},
{"sst25wf080", INFO(0xbf2505, 0x0, 64 * 1024, 16, SECT_4K
| SST_WR) }, +#endif +#ifdef CONFIG_SPI_NOR_WINBOND /* WINBOND */
{"w25p80", INFO(0xef2014, 0x0, 64 * 1024, 16, 0) },
{"w25p16", INFO(0xef2015, 0x0, 64 * 1024, 32, 0) },
{"w25p32", INFO(0xef2016, 0x0, 64 * 1024, 64, 0) },
{"w25x40", INFO(0xef3013, 0x0, 64 * 1024, 8, SECT_4K)
},
{"w25x16", INFO(0xef3015, 0x0, 64 * 1024, 32, SECT_4K)
},
{"w25x32", INFO(0xef3016, 0x0, 64 * 1024, 64, SECT_4K)
},
{"w25x64", INFO(0xef3017, 0x0, 64 * 1024, 128, SECT_4K)
},
{"w25q80bl", INFO(0xef4014, 0x0, 64 * 1024, 16, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q16cl", INFO(0xef4015, 0x0, 64 * 1024, 32, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q32bv", INFO(0xef4016, 0x0, 64 * 1024, 64, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q64cv", INFO(0xef4017, 0x0, 64 * 1024, 128, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q128bv", INFO(0xef4018, 0x0, 64 * 1024, 256, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q256", INFO(0xef4019, 0x0, 64 * 1024, 512, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q80bw", INFO(0xef5014, 0x0, 64 * 1024, 16, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q16dw", INFO(0xef6015, 0x0, 64 * 1024, 32, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q32dw", INFO(0xef6016, 0x0, 64 * 1024, 64, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q64dw", INFO(0xef6017, 0x0, 64 * 1024, 128, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q128fw", INFO(0xef6018, 0x0, 64 * 1024, 256, RD_FULL
| WR_QPP | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_NOR_MISC
/* ATMEL */
{"at45db011d", INFO(0x1f2200, 0x0, 64 * 1024, 4, SECT_4K)
},
{"at45db021d", INFO(0x1f2300, 0x0, 64 * 1024, 8, SECT_4K)
},
{"at45db041d", INFO(0x1f2400, 0x0, 64 * 1024, 8, SECT_4K)
},
{"at45db081d", INFO(0x1f2500, 0x0, 64 * 1024, 16, SECT_4K)
},
{"at45db161d", INFO(0x1f2600, 0x0, 64 * 1024, 32, SECT_4K)
},
{"at45db321d", INFO(0x1f2700, 0x0, 64 * 1024, 64, SECT_4K)
},
{"at45db641d", INFO(0x1f2800, 0x0, 64 * 1024, 128, SECT_4K)
},
{"at25df321a", INFO(0x1f4701, 0x0, 64 * 1024, 64, SECT_4K)
},
{"at25df321", INFO(0x1f4700, 0x0, 64 * 1024, 64, SECT_4K)
},
{"at26df081a", INFO(0x1f4501, 0x0, 64 * 1024, 16, SECT_4K)
},
/* EON */
{"en25q32b", INFO(0x1c3016, 0x0, 64 * 1024, 64, 0) },
{"en25q64", INFO(0x1c3017, 0x0, 64 * 1024, 128, SECT_4K)
},
{"en25q128b", INFO(0x1c3018, 0x0, 64 * 1024, 256, 0) },
{"en25s64", INFO(0x1c3817, 0x0, 64 * 1024, 128, 0) },
/* GIGADEVICE */
{"gd25q64b", INFO(0xc84017, 0x0, 64 * 1024, 128, SECT_4K)
},
{"gd25lq32", INFO(0xc86016, 0x0, 64 * 1024, 64, SECT_4K)
},
/* ISSI */
{"is25lq040b", INFO(0x9d4013, 0x0, 64 * 1024, 8, 0) },
{"is25lp032", INFO(0x9d6016, 0x0, 64 * 1024, 64, 0) },
{"is25lp064", INFO(0x9d6017, 0x0, 64 * 1024, 128, 0) },
{"is25lp128", INFO(0x9d6018, 0x0, 64 * 1024, 256, 0) },
+#endif
{}, /* Empty entry to terminate the list */
/*
* Note:
* Below paired flash devices has similar spi_nor params.
* (s25fl129p_64k, s25fl128s_64k)
* (w25q80bl, w25q80bv)
* (w25q16cl, w25q16dv)
* (w25q32bv, w25q32fv_spi)
* (w25q64cv, w25q64fv_spi)
* (w25q128bv, w25q128fv_spi)
* (w25q32dw, w25q32fv_qpi)
* (w25q64dw, w25q64fv_qpi)
* (w25q128fw, w25q128fv_qpi)
*/
+}; diff --git a/drivers/mtd/spi-nor/spi-nor-uclass.c b/drivers/mtd/spi-nor/spi-nor-uclass.c new file mode 100644 index 0000000..919682d --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-uclass.c @@ -0,0 +1,143 @@ +/*
- SPI NOR Core framework.
- Copyright (C) 2016 Jagan Teki jagan@openedev.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <mtd.h>
+#include <dm/device-internal.h> +#include <linux/mtd/spi-nor.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev) +{
struct spi_nor_uclass_priv *upriv;
if (!device_active(dev))
return NULL;
upriv = dev_get_uclass_priv(dev);
return upriv->spi_nor;
+}
+struct spi_nor *find_spi_nor_device(int dev_num) +{
struct udevice *dev, *spi_nor_dev;
int ret;
ret = mtd_find_device(MTD_IF_TYPE_SPI_NOR, dev_num, &dev);
if (ret) {
printf("SPI-NOR Device %d not found\n", dev_num);
return NULL;
}
spi_nor_dev = dev_get_parent(dev);
struct spi_nor *nor = spi_nor_get_spi_nor_dev(spi_nor_dev);
return nor;
+}
+int get_spi_nor_num(void) +{
return max((mtd_find_max_devnum(MTD_IF_TYPE_SPI_NOR) + 1), 0);
+}
+struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor) +{
struct mtd_info *mtd;
struct udevice *dev;
device_find_first_child(nor->dev, &dev);
if (!dev)
return NULL;
mtd = dev_get_uclass_platdata(dev);
return mtd;
+}
+void print_spi_nor_devices(char separator) +{
struct udevice *dev;
bool first = true;
for (uclass_first_device(UCLASS_SPI_NOR, &dev);
dev;
uclass_next_device(&dev), first = false) {
struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
if (!first) {
printf("%c", separator);
if (separator != '\n')
puts(" ");
}
printf("%s: %d", dev->name, spi_nor_get_mtd_info(nor)->
devnum);
}
printf("\n");
+}
+int spi_nor_bind(struct udevice *dev, struct spi_nor *nor) +{
struct udevice *mdev;
int ret;
if (!spi_nor_get_ops(dev))
return -ENOSYS;
ret = mtd_create_devicef(dev, "spinor_mtd", "mtd",
MTD_IF_TYPE_SPI_NOR,
&mdev);
if (ret) {
debug("Cannot create mtd device\n");
return ret;
}
nor->dev = dev;
return 0;
+}
+static int spi_nor_mtd_probe(struct udevice *dev) +{
struct udevice *spi_nor_dev = dev_get_parent(dev);
struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(spi_nor_
dev);
struct spi_nor *nor = upriv->spi_nor;
int ret;
ret = spi_nor_scan(nor);
if (ret) {
debug("%s: spi_nor_scan() failed (err=%d)\n", __func__,
ret);
return ret;
}
return 0;
+}
+static const struct mtd_ops spi_nor_mtd_ops = {
.read = spi_nor_mread,
.erase = spi_nor_merase,
+};
+U_BOOT_DRIVER(spinor_mtd) = {
.name = "spinor_mtd",
.id = UCLASS_MTD,
.ops = &spi_nor_mtd_ops,
.probe = spi_nor_mtd_probe,
+};
+U_BOOT_DRIVER(spinor) = {
.name = "spinor",
.id = UCLASS_SPI_NOR,
+};
+UCLASS_DRIVER(spinor) = {
.id = UCLASS_SPI_NOR,
.name = "spinor",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.per_device_auto_alloc_size = sizeof(struct spi_nor_uclass_priv),
+}; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c new file mode 100644 index 0000000..09fb8ca --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -0,0 +1,569 @@ +/*
- SPI NOR Core framework.
- Copyright (C) 2016 Jagan Teki jagan@openedev.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <mapmem.h> +#include <mtd.h>
+#include <dm/device-internal.h> +#include <linux/math64.h> +#include <linux/types.h> +#include <linux/mtd/spi-nor.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* Set write enable latch with Write Enable command */ +static inline int write_enable(struct udevice *dev) +{
return spi_nor_get_ops(dev)->write_reg(dev, SNOR_OP_WREN, NULL,
0); +}
+/* Re-set write enable latch with Write Disable command */ +static inline int write_disable(struct udevice *dev) +{
return spi_nor_get_ops(dev)->write_reg(dev, SNOR_OP_WRDI, NULL,
0); +}
+static int read_sr(struct udevice *dev) +{
u8 sr;
int ret;
ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDSR, &sr, 1);
if (ret < 0) {
debug("spi-nor: fail to read status register\n");
return ret;
}
return sr;
+}
+static int read_fsr(struct udevice *dev) +{
u8 fsr;
int ret;
ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDFSR, &fsr, 1);
if (ret < 0) {
debug("spi-nor: fail to read flag status register\n");
return ret;
}
return fsr;
+}
+static int write_sr(struct udevice *dev, u8 ws) +{
struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
const struct spi_nor_ops *ops = spi_nor_get_ops(dev);
nor->cmd_buf[0] = ws;
return ops->write_reg(dev, SNOR_OP_WRSR, nor->cmd_buf, 1);
+}
+#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) +static int read_cr(struct udevice *dev) +{
u8 cr;
int ret;
ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDCR, &cr, 1);
if (ret < 0) {
debug("spi-nor: fail to read config register\n");
return ret;
}
return cr;
+}
+/*
- Write status Register and configuration register with 2 bytes
- First byte will be written to the status register.
- Second byte will be written to the configuration register.
- Return negative if error occured.
s/occured/occurred
- */
+static int write_sr_cr(struct udevice *dev, u16 val) +{
struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
const struct spi_nor_ops *ops = spi_nor_get_ops(dev);
nor->cmd_buf[0] = val & 0xff;
nor->cmd_buf[1] = (val >> 8);
return ops->write_reg(dev, SNOR_OP_WRSR, nor->cmd_buf, 2);
+} +#endif
+static int spi_nor_sr_ready(struct udevice *dev) +{
int sr = read_sr(dev);
if (sr < 0)
return sr;
else
return !(sr & SR_WIP);
+}
+static int spi_nor_fsr_ready(struct udevice *dev) +{
int fsr = read_fsr(dev);
if (fsr < 0)
return fsr;
else
return fsr & FSR_READY;
+}
+static int spi_nor_ready(struct udevice *dev) +{
struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
int sr, fsr;
sr = spi_nor_sr_ready(dev);
if (sr < 0)
return sr;
fsr = 1;
if (nor->flags & SNOR_F_USE_FSR) {
fsr = spi_nor_fsr_ready(dev);
if (fsr < 0)
return fsr;
}
return sr && fsr;
+}
+static int spi_nor_wait_till_ready(struct udevice *dev, unsigned long timeout) +{
int timebase, ret;
timebase = get_timer(0);
while (get_timer(timebase) < timeout) {
ret = spi_nor_ready(dev);
if (ret < 0)
return ret;
if (ret)
return 0;
}
printf("spi-nor: Timeout!\n");
return -ETIMEDOUT;
+}
+static const struct spi_nor_info *spi_nor_id(struct udevice *dev) +{
int tmp;
u8 id[SPI_NOR_MAX_ID_LEN];
const struct spi_nor_info *info;
const struct spi_nor_ops *ops = spi_nor_get_ops(dev);
tmp = ops->read_reg(dev, SNOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
if (tmp < 0) {
printf("spi-nor: error %d reading JEDEC ID\n", tmp);
return ERR_PTR(tmp);
}
info = spi_nor_ids;
for (; info->name != NULL; info++) {
if (info->id_len) {
if (!memcmp(info->id, id, info->id_len))
return info;
}
}
printf("spi-nor: unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
id[0], id[1], id[2]);
return ERR_PTR(-ENODEV);
+}
+int spi_nor_merase(struct udevice *dev, struct erase_info *instr) +{
struct mtd_info *mtd = dev_get_uclass_platdata(dev);
int devnum = mtd->devnum;
struct spi_nor *nor;
const struct spi_nor_ops *ops;
u32 addr, len, erase_addr;
uint32_t rem;
int ret = -1;
nor = find_spi_nor_device(devnum);
if (!nor)
return -ENODEV;
ops = spi_nor_get_ops(nor->dev);
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
return -EINVAL;
addr = instr->addr;
len = instr->len;
while (len) {
erase_addr = addr;
write_enable(nor->dev);
ret = ops->write(nor->dev, erase_addr, 0, NULL);
if (ret < 0)
goto erase_err;
ret = spi_nor_wait_till_ready(nor->dev,
SNOR_READY_WAIT_ERASE);
if (ret < 0)
goto erase_err;
addr += mtd->erasesize;
len -= mtd->erasesize;
}
write_disable(nor->dev);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return ret;
+erase_err:
instr->state = MTD_ERASE_FAILED;
return ret;
+}
+static int spi_nor_mwrite(struct udevice *dev, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
+{
struct mtd_info *mtd = dev_get_uclass_platdata(dev);
int devnum = mtd->devnum;
struct spi_nor *nor;
const struct spi_nor_ops *ops;
size_t addr, byte_addr;
size_t chunk_len, actual;
uint32_t page_size;
int ret = -1;
nor = find_spi_nor_device(devnum);
if (!nor)
return -ENODEV;
ops = spi_nor_get_ops(nor->dev);
page_size = mtd->writebufsize;
for (actual = 0; actual < len; actual += chunk_len) {
addr = to;
byte_addr = addr % page_size;
chunk_len = min(len - actual, (size_t)(page_size -
byte_addr));
if (nor->max_write_size)
chunk_len = min(chunk_len,
(size_t)nor->max_write_size);
write_enable(nor->dev);
ret = ops->write(nor->dev, addr, chunk_len, buf + actual);
if (ret < 0)
break;
ret = spi_nor_wait_till_ready(nor->dev,
SNOR_READY_WAIT_PROG);
if (ret < 0)
return ret;
to += chunk_len;
*retlen += chunk_len;
}
return ret;
+}
+int spi_nor_mread(struct udevice *dev, loff_t from, size_t len,
size_t *retlen, u_char *buf)
+{
struct mtd_info *mtd = dev_get_uclass_platdata(dev);
int devnum = mtd->devnum;
struct spi_nor *nor;
const struct spi_nor_ops *ops;
int ret;
nor = find_spi_nor_device(devnum);
if (!nor)
return -ENODEV;
ops = spi_nor_get_ops(nor->dev);
/* Handle memory-mapped SPI */
if (nor->memory_map) {
ret = ops->read(nor->dev, from, len, buf);
if (ret) {
debug("spi-nor: mmap read failed\n");
return ret;
}
return ret;
}
ret = ops->read(nor->dev, from, len, buf);
if (ret < 0) {
printf("%s ret = %d\n", __func__, ret);
return ret;
}
*retlen += len;
return ret;
+}
+#ifdef CONFIG_SPI_NOR_MACRONIX +static int macronix_quad_enable(struct udevice *dev) +{
int ret, val;
val = read_sr(dev);
if (val < 0)
return val;
if (val & SR_QUAD_EN_MX)
return 0;
write_enable(dev);
ret = write_sr(dev, val | SR_QUAD_EN_MX);
if (ret < 0)
return ret;
if (spi_nor_wait_till_ready(dev, SNOR_READY_WAIT_PROG))
return 1;
ret = read_sr(dev);
if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
printf("spi-nor: Macronix Quad bit not set\n");
return -EINVAL;
}
return 0;
+} +#endif
+#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) +static int spansion_quad_enable(struct udevice *dev) +{
int ret, val;
val = read_cr(dev);
if (val < 0)
return val;
if (val & CR_QUAD_EN_SPAN)
return 0;
write_enable(dev);
ret = write_sr_cr(dev, val | CR_QUAD_EN_SPAN);
if (ret < 0)
return ret;
if (spi_nor_wait_till_ready(dev, SNOR_READY_WAIT_PROG))
return 1;
/* read back and check it */
ret = read_cr(dev);
if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
printf("spi-nor: Spansion Quad bit not set\n");
return -EINVAL;
}
return 0;
+} +#endif
+static int set_quad_mode(struct udevice *dev, const struct spi_nor_info *info) +{
switch (JEDEC_MFR(info)) {
+#ifdef CONFIG_SPI_NOR_MACRONIX
case SNOR_MFR_MACRONIX:
return macronix_quad_enable(dev);
+#endif +#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND)
case SNOR_MFR_SPANSION:
case SNOR_MFR_WINBOND:
return spansion_quad_enable(dev);
+#endif +#ifdef CONFIG_SPI_NOR_STMICRO
case SNOR_MFR_MICRON:
return 0;
+#endif
default:
printf("spi-nor: Need set QEB func for %02x flash\n",
JEDEC_MFR(info));
return -1;
}
+}
+#if CONFIG_IS_ENABLED(OF_CONTROL) +int spi_nor_decode_fdt(const void *blob, struct spi_nor *nor) +{
struct udevice *dev = nor->dev;
struct mtd_info *mtd = mtd_get_info(dev);
fdt_addr_t addr;
fdt_size_t size;
int node;
/* If there is no node, do nothing */
node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
if (node < 0)
return 0;
addr = fdtdec_get_addr_size(blob, node, "memory-map", &size);
if (addr == FDT_ADDR_T_NONE) {
debug("%s: Cannot decode address\n", __func__);
return 0;
}
if (mtd->size != size) {
debug("%s: Memory map must cover entire device\n",
__func__);
return -1;
}
nor->memory_map = map_sysmem(addr, size);
return 0;
+} +#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
+int spi_nor_scan(struct spi_nor *nor) +{
struct mtd_info *mtd = spi_nor_get_mtd_info(nor);
struct mtd_ops *ops = mtd_get_ops(mtd->dev);
const struct spi_nor_info *info = NULL;
int ret;
struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(nor->dev);
upriv->spi_nor = nor;
if (nor->init_done)
return 0;
trailing whitespace
+ info = spi_nor_id(nor->dev);
if (IS_ERR_OR_NULL(info)) {
ret = -ENOENT;
goto err;
}
/*
* Flash powers up read-only, so clear BP# bits.
*
* Note on some flash (like Macronix), QE (quad enable) bit is in
the
* same status register as BP# bits, and we need preserve its
original
* value during a reboot cycle as this is required by some
platforms
* (like Intel ICH SPI controller working under descriptor mode).
*/
if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
(JEDEC_MFR(info) == SNOR_MFR_SST) ||
(JEDEC_MFR(info) == SNOR_MFR_MACRONIX)) {
u8 sr = 0;
if (JEDEC_MFR(info) == SNOR_MFR_MACRONIX)
sr = read_sr(nor->dev) & SR_QUAD_EN_MX;
write_sr(nor->dev, sr);
}
mtd->name = info->name;
mtd->priv = nor;
mtd->type = MTD_NORFLASH;
mtd->writesize = 1;
mtd->flags = MTD_CAP_NORFLASH;
if (info->flags & E_FSR)
nor->flags |= SNOR_F_USE_FSR;
if (info->flags & SST_WR)
nor->flags |= SNOR_F_SST_WRITE;
ops->write = spi_nor_mwrite;
/* compute the flash size */
nor->page_size = info->page_size;
/*
* The Spansion S25FL032P and S25FL064P have 256b pages, yet use
the
* 0x4d00 Extended JEDEC code. The rest of the Spansion flashes
with
* the 0x4d00 Extended JEDEC code have 512b pages. All of the
others
* have 256b pages.
*/
if (JEDEC_EXT(info) == 0x4d00) {
if ((JEDEC_ID(info) != 0x0215) &&
(JEDEC_ID(info) != 0x0216))
nor->page_size = 512;
}
mtd->writebufsize = nor->page_size;
mtd->size = info->sector_size * info->n_sectors;
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
nor->erase_opcode = SNOR_OP_BE_4K;
mtd->erasesize = 4096;
} else
+#endif
{
nor->erase_opcode = SNOR_OP_SE;
mtd->erasesize = info->sector_size;
}
/* Look for read opcode */
nor->read_opcode = SNOR_OP_READ_FAST;
if (nor->mode & SNOR_READ)
nor->read_opcode = SNOR_OP_READ;
else if (nor->mode & SNOR_READ_1_1_4 && info->flags & RD_QUAD)
nor->read_opcode = SNOR_OP_READ_1_1_4;
else if (nor->mode & SNOR_READ_1_1_2 && info->flags & RD_DUAL)
nor->read_opcode = SNOR_OP_READ_1_1_2;
/* Look for program opcode */
if (info->flags & WR_QPP && nor->mode & SNOR_WRITE_1_1_4)
nor->program_opcode = SNOR_OP_QPP;
else
/* Go for default supported write cmd */
nor->program_opcode = SNOR_OP_PP;
/* Set the quad enable bit - only for quad commands */
if ((nor->read_opcode == SNOR_OP_READ_1_1_4) ||
(nor->read_opcode == SNOR_OP_READ_1_1_4_IO) ||
(nor->program_opcode == SNOR_OP_QPP)) {
ret = set_quad_mode(nor->dev, info);
if (ret) {
debug("spi-nor: quad mode not supported for
%02x\n",
JEDEC_MFR(info));
goto err;
}
}
nor->addr_width = 3;
/* Dummy cycles for read */
switch (nor->read_opcode) {
case SNOR_OP_READ_1_1_4_IO:
nor->read_dummy = 16;
break;
case SNOR_OP_READ:
nor->read_dummy = 0;
break;
default:
nor->read_dummy = 8;
}
+#if CONFIG_IS_ENABLED(OF_CONTROL)
ret = spi_nor_decode_fdt(gd->fdt_blob, nor);
if (ret) {
debug("spi-nor: FDT decode error\n");
goto err;
}
+#endif
nor->init_done = 1;
return 0;
+err:
trailing whitespace
nor->init_done = 0;
return ret;
+} diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 3fc2083..06541a4 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -75,6 +75,7 @@ enum uclass_id { UCLASS_SERIAL, /* Serial UART */ UCLASS_SPI, /* SPI bus */ UCLASS_SPMI, /* System Power Management Interface bus */
UCLASS_SPI_NOR, /* SPI NOR flash */ UCLASS_SPI_FLASH, /* SPI flash */ UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SYSCON, /* System configuration device */
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1688e2 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,217 @@ +/*
- SPI NOR Core header file.
- Copyright (C) 2016 Jagan Teki jagan@openedev.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __MTD_SPI_NOR_H +#define __MTD_SPI_NOR_H
+#include <common.h> +#include <linux/mtd/mtd.h>
+/*
- Manufacturer IDs
- The first byte returned from the flash after sending opcode
SPINOR_OP_RDID.
- Sometimes these are the same as CFI IDs, but sometimes they aren't.
- */
+#define SNOR_MFR_ATMEL 0x1f +#define SNOR_MFR_MACRONIX 0xc2 +#define SNOR_MFR_MICRON 0x20 /* ST Micro <--> Micron */ +#define SNOR_MFR_SPANSION 0x01 +#define SNOR_MFR_SST 0xbf +#define SNOR_MFR_WINBOND 0xef
+/**
- SPI NOR opcodes.
- Note on opcode nomenclature: some opcodes have a format like
- SNOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the
number
- of I/O lines used for the opcode, address, and data (respectively). The
- FUNCTION has an optional suffix of '4', to represent an opcode which
- requires a 4-byte (32-bit) address.
- */
+#define SNOR_OP_WRDI 0x04 /* Write disable */ +#define SNOR_OP_WREN 0x06 /* Write enable */ +#define SNOR_OP_RDSR 0x05 /* Read status register */ +#define SNOR_OP_WRSR 0x01 /* Write status register 1 byte */ +#define SNOR_OP_READ 0x03 /* Read data bytes (low frequency) */ +#define SNOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ +#define SNOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +#define SNOR_OP_READ_1_1_2_IO 0xbb /* Read data bytes (Dual IO SPI) */ +#define SNOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ +#define SNOR_OP_READ_1_1_4_IO 0xeb /* Read data bytes (Quad IO SPI) */ +#define SNOR_OP_BRWR 0x17 /* Bank register write */ +#define SNOR_OP_BRRD 0x16 /* Bank register read */ +#define SNOR_OP_WREAR 0xC5 /* Write extended address register */ +#define SNOR_OP_RDEAR 0xC8 /* Read extended address register */ +#define SNOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ +#define SNOR_OP_QPP 0x32 /* Quad Page program */ +#define SNOR_OP_BE_4K 0x20 /* Erase 4KiB block */ +#define SNOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define SNOR_OP_BE_32K 0x52 /* Erase 32KiB block */ +#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define SNOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define SNOR_OP_RDID 0x9f /* Read JEDEC ID */ +#define SNOR_OP_RDCR 0x35 /* Read configuration register */ +#define SNOR_OP_RDFSR 0x70 /* Read flag status register */
+/* Used for SST flashes only. */ +#define SNOR_OP_BP 0x02 /* Byte program */ +#define SNOR_OP_AAI_WP 0xad /* Auto addr increment word program */
+/* Status Register bits. */ +#define SR_WIP BIT(0) /* Write in progress */ +#define SR_WEL BIT(1) /* Write enable latch */
+/* meaning of other SR_* bits may differ between vendors */ +#define SR_BP0 BIT(2) /* Block protect 0 */ +#define SR_BP1 BIT(3) /* Block protect 1 */ +#define SR_BP2 BIT(4) /* Block protect 2 */ +#define SR_SRWD BIT(7) /* SR write protect */
+#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
+/* Flag Status Register bits */ +#define FSR_READY BIT(7)
+/* Configuration Register bits. */ +#define CR_QUAD_EN_SPAN BIT(1) /* Spansion/Winbond Quad I/O */
+/* Flash timeout values */ +#define SNOR_READY_WAIT_PROG (2 * CONFIG_SYS_HZ) +#define SNOR_READY_WAIT_ERASE (5 * CONFIG_SYS_HZ) +#define SNOR_MAX_CMD_SIZE 4 +#define SNOR_16MB_BOUN 0x1000000
+/**
- struct spi_nor_uclass_priv - Holds information about a device used by
the uclass
- */
+struct spi_nor_uclass_priv {
struct spi_nor *spi_nor;
+};
+enum snor_option_flags {
SNOR_F_SST_WRITE = BIT(0),
SNOR_F_USE_FSR = BIT(1),
SNOR_F_U_PAGE = BIT(1),
+};
+enum mode {
SNOR_READ = BIT(0),
SNOR_READ_1_1_2 = BIT(1),
SNOR_READ_1_1_4 = BIT(2),
SNOR_READ_1_1_2_IO = BIT(3),
SNOR_READ_1_1_4_IO = BIT(4),
SNOR_WRITE_1_1_BYTE = BIT(5),
SNOR_WRITE_1_1_4 = BIT(6),
+};
+#define JEDEC_MFR(info) ((info)->id[0]) +#define JEDEC_ID(info) (((info)->id[1]) << 8 | ((info)->id[2])) +#define JEDEC_EXT(info) (((info)->id[3]) << 8 | ((info)->id[4])) +#define SPI_NOR_MAX_ID_LEN 6
+struct spi_nor_info {
char *name;
/*
* This array stores the ID bytes.
* The first three bytes are the JEDIC ID.
* JEDEC ID zero means "no ID" (mostly older chips).
*/
u8 id[SPI_NOR_MAX_ID_LEN];
u8 id_len;
/* The size listed here is what works with SNOR_OP_SE, which isn't
* necessarily called a "sector" by the vendor.
*/
unsigned sector_size;
u16 n_sectors;
u16 page_size;
u16 flags;
+#define SECT_4K BIT(0) +#define E_FSR BIT(1) +#define SST_WR BIT(2) +#define WR_QPP BIT(3) +#define RD_QUAD BIT(4) +#define RD_DUAL BIT(5) +#define RD_QUADIO BIT(6) +#define RD_DUALIO BIT(7) +#define RD_FULL (RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO) +};
+extern const struct spi_nor_info spi_nor_ids[];
+/**
- struct spi_nor - Structure for defining a the SPI NOR layer
- @dev: SPI NOR device
- @name: name of the SPI NOR device
- @page_size: the page size of the SPI NOR
- @addr_width: number of address bytes
- @erase_opcode: the opcode for erasing a sector
- @read_opcode: the read opcode
- @read_dummy: the dummy bytes needed by the read
operation
- @program_opcode: the program opcode
- @max_write_size: If non-zero, the maximum number of bytes which can
be written at once, excluding command bytes.
- @flags: flag options for the current SPI-NOR (SNOR_F_*)
- @mode: read, write mode or any other mode bits.
- @read_mode: read mode.
- @cmd_buf: used by the write_reg
- @read_reg: [DRIVER-SPECIFIC] read out the register
- @write_reg: [DRIVER-SPECIFIC] write data to the register
- @read: [DRIVER-SPECIFIC] read data from the SPI NOR
- @write: [DRIVER-SPECIFIC] write data to the SPI NOR
- @memory_map: address of read-only SPI NOR access
- */
+struct spi_nor {
struct udevice *dev;
const char *name;
u8 init_done;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
u32 max_write_size;
u32 flags;
u8 mode;
u8 read_mode;
u8 cmd_buf[SNOR_MAX_CMD_SIZE];
void *memory_map;
+};
+struct spi_nor_ops {
int (*read_reg)(struct udevice *dev, u8 cmd, u8 *val, int len);
int (*write_reg)(struct udevice *dev, u8 cmd, u8 *data, int len);
int (*read)(struct udevice *dev, loff_t from, size_t len,
u_char *buf);
int (*write)(struct udevice *dev, loff_t to, size_t len,
const u_char *buf);
+};
+#define spi_nor_get_ops(dev) ((struct spi_nor_ops *)(dev)->driver->ops)
+int spi_nor_merase(struct udevice *dev, struct erase_info *instr); +int spi_nor_mread(struct udevice *dev, loff_t from, size_t len,
size_t *retlen, u_char *buf);
+int spi_nor_scan(struct spi_nor *nor); +int spi_nor_bind(struct udevice *dev, struct spi_nor *nor);
+struct spi_nor *find_spi_nor_device(int dev_num); +int get_spi_nor_num(void); +struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev); +struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor);
+#endif /* __MTD_SPI_NOR_H */
2.7.4
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

Hi Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Thursday, December 28, 2017 11:42 AM To: u-boot@lists.denx.de Cc: Tom Rini trini@konsulko.com Subject: [U-Boot] [PATCH v10 03/27] mtd: add SPI-NOR core support
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
<snip>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <mapmem.h> +#include <mtd.h>
+#include <dm/device-internal.h> +#include <linux/math64.h> +#include <linux/types.h> +#include <linux/mtd/spi-nor.h>
+int spi_nor_scan(struct spi_nor *nor) +{
- struct mtd_info *mtd = spi_nor_get_mtd_info(nor);
- struct mtd_ops *ops = mtd_get_ops(mtd->dev);
- const struct spi_nor_info *info = NULL;
- int ret;
- struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(nor->dev);
- upriv->spi_nor = nor;
- if (nor->init_done)
return 0;
- info = spi_nor_id(nor->dev);
- if (IS_ERR_OR_NULL(info)) {
ret = -ENOENT;
goto err;
- }
- /*
* Flash powers up read-only, so clear BP# bits.
*
* Note on some flash (like Macronix), QE (quad enable) bit is in the
* same status register as BP# bits, and we need preserve its original
* value during a reboot cycle as this is required by some platforms
* (like Intel ICH SPI controller working under descriptor mode).
*/
- if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
(JEDEC_MFR(info) == SNOR_MFR_SST) ||
(JEDEC_MFR(info) == SNOR_MFR_MACRONIX)) {
u8 sr = 0;
if (JEDEC_MFR(info) == SNOR_MFR_MACRONIX)
sr = read_sr(nor->dev) & SR_QUAD_EN_MX;
write_sr(nor->dev, sr);
- }
- mtd->name = info->name;
- mtd->priv = nor;
- mtd->type = MTD_NORFLASH;
- mtd->writesize = 1;
- mtd->flags = MTD_CAP_NORFLASH;
- if (info->flags & E_FSR)
nor->flags |= SNOR_F_USE_FSR;
- if (info->flags & SST_WR)
nor->flags |= SNOR_F_SST_WRITE;
- ops->write = spi_nor_mwrite;
- /* compute the flash size */
- nor->page_size = info->page_size;
- /*
* The Spansion S25FL032P and S25FL064P have 256b pages, yet use the
* 0x4d00 Extended JEDEC code. The rest of the Spansion flashes with
* the 0x4d00 Extended JEDEC code have 512b pages. All of the others
* have 256b pages.
*/
- if (JEDEC_EXT(info) == 0x4d00) {
if ((JEDEC_ID(info) != 0x0215) &&
(JEDEC_ID(info) != 0x0216))
nor->page_size = 512;
- }
- mtd->writebufsize = nor->page_size;
- mtd->size = info->sector_size * info->n_sectors;
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
- /* prefer "small sector" erase if possible */
- if (info->flags & SECT_4K) {
nor->erase_opcode = SNOR_OP_BE_4K;
mtd->erasesize = 4096;
- } else
+#endif
- {
nor->erase_opcode = SNOR_OP_SE;
mtd->erasesize = info->sector_size;
- }
- /* Look for read opcode */
- nor->read_opcode = SNOR_OP_READ_FAST;
- if (nor->mode & SNOR_READ)
nor->read_opcode = SNOR_OP_READ;
- else if (nor->mode & SNOR_READ_1_1_4 && info->flags & RD_QUAD)
nor->read_opcode = SNOR_OP_READ_1_1_4;
- else if (nor->mode & SNOR_READ_1_1_2 && info->flags & RD_DUAL)
nor->read_opcode = SNOR_OP_READ_1_1_2;
Are you planning to store read_protocol i.e. 1-1-1. 1-1-2, 1-4-4, 4-4-4 etc information in nor structure? It will help low level controller driver to configure number of lines to be used during read in instruction, address and data phase.
Similar framework is present in Linux.
struct spi_nor : * @read_proto: the SPI protocol for read operations * @write_proto: the SPI protocol for write operations * @reg_proto the SPI protocol for read_reg/write_reg/erase operations
--pk

On Tue, Jan 2, 2018 at 3:48 PM, Prabhakar Kushwaha prabhakar.kushwaha@nxp.com wrote:
Hi Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Thursday, December 28, 2017 11:42 AM To: u-boot@lists.denx.de Cc: Tom Rini trini@konsulko.com Subject: [U-Boot] [PATCH v10 03/27] mtd: add SPI-NOR core support
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
<snip>
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <mapmem.h> +#include <mtd.h>
+#include <dm/device-internal.h> +#include <linux/math64.h> +#include <linux/types.h> +#include <linux/mtd/spi-nor.h>
+int spi_nor_scan(struct spi_nor *nor) +{
struct mtd_info *mtd = spi_nor_get_mtd_info(nor);
struct mtd_ops *ops = mtd_get_ops(mtd->dev);
const struct spi_nor_info *info = NULL;
int ret;
struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(nor->dev);
upriv->spi_nor = nor;
if (nor->init_done)
return 0;
info = spi_nor_id(nor->dev);
if (IS_ERR_OR_NULL(info)) {
ret = -ENOENT;
goto err;
}
/*
* Flash powers up read-only, so clear BP# bits.
*
* Note on some flash (like Macronix), QE (quad enable) bit is in the
* same status register as BP# bits, and we need preserve its original
* value during a reboot cycle as this is required by some platforms
* (like Intel ICH SPI controller working under descriptor mode).
*/
if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
(JEDEC_MFR(info) == SNOR_MFR_SST) ||
(JEDEC_MFR(info) == SNOR_MFR_MACRONIX)) {
u8 sr = 0;
if (JEDEC_MFR(info) == SNOR_MFR_MACRONIX)
sr = read_sr(nor->dev) & SR_QUAD_EN_MX;
write_sr(nor->dev, sr);
}
mtd->name = info->name;
mtd->priv = nor;
mtd->type = MTD_NORFLASH;
mtd->writesize = 1;
mtd->flags = MTD_CAP_NORFLASH;
if (info->flags & E_FSR)
nor->flags |= SNOR_F_USE_FSR;
if (info->flags & SST_WR)
nor->flags |= SNOR_F_SST_WRITE;
ops->write = spi_nor_mwrite;
/* compute the flash size */
nor->page_size = info->page_size;
/*
* The Spansion S25FL032P and S25FL064P have 256b pages, yet use the
* 0x4d00 Extended JEDEC code. The rest of the Spansion flashes with
* the 0x4d00 Extended JEDEC code have 512b pages. All of the others
* have 256b pages.
*/
if (JEDEC_EXT(info) == 0x4d00) {
if ((JEDEC_ID(info) != 0x0215) &&
(JEDEC_ID(info) != 0x0216))
nor->page_size = 512;
}
mtd->writebufsize = nor->page_size;
mtd->size = info->sector_size * info->n_sectors;
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
nor->erase_opcode = SNOR_OP_BE_4K;
mtd->erasesize = 4096;
} else
+#endif
{
nor->erase_opcode = SNOR_OP_SE;
mtd->erasesize = info->sector_size;
}
/* Look for read opcode */
nor->read_opcode = SNOR_OP_READ_FAST;
if (nor->mode & SNOR_READ)
nor->read_opcode = SNOR_OP_READ;
else if (nor->mode & SNOR_READ_1_1_4 && info->flags & RD_QUAD)
nor->read_opcode = SNOR_OP_READ_1_1_4;
else if (nor->mode & SNOR_READ_1_1_2 && info->flags & RD_DUAL)
nor->read_opcode = SNOR_OP_READ_1_1_2;
Are you planning to store read_protocol i.e. 1-1-1. 1-1-2, 1-4-4, 4-4-4 etc information in nor structure? It will help low level controller driver to configure number of lines to be used during read in instruction, address and data phase.
Similar framework is present in Linux.
struct spi_nor :
- @read_proto: the SPI protocol for read operations
- @write_proto: the SPI protocol for write operations
- @reg_proto the SPI protocol for read_reg/write_reg/erase operations
ok try these and mention code sizes for the same.

On Thursday 28 December 2017 11:42 AM, Jagan Teki wrote: [...]
+static const struct mtd_ops spi_nor_mtd_ops = {
- .read = spi_nor_mread,
- .erase = spi_nor_merase,
+};
Wondering why spi_nor_mwrite is not hooked up here?
+U_BOOT_DRIVER(spinor_mtd) = {
- .name = "spinor_mtd",
- .id = UCLASS_MTD,
- .ops = &spi_nor_mtd_ops,
- .probe = spi_nor_mtd_probe,
+};
+U_BOOT_DRIVER(spinor) = {
- .name = "spinor",
- .id = UCLASS_SPI_NOR,
+};
+UCLASS_DRIVER(spinor) = {
- .id = UCLASS_SPI_NOR,
- .name = "spinor",
- .flags = DM_UC_FLAG_SEQ_ALIAS,
- .per_device_auto_alloc_size = sizeof(struct spi_nor_uclass_priv),
+};
[...]
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1688e2 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,217 @@
[...]
+struct spi_nor {
- struct udevice *dev;
- const char *name;
- u8 init_done;
- u32 page_size;
- u8 addr_width;
- u8 erase_opcode;
- u8 read_opcode;
- u8 read_dummy;
- u8 program_opcode;
- u32 max_write_size;
- u32 flags;
- u8 mode;
- u8 read_mode;
Where is this field used? Shouldn't this field carry single/dual/quad mode information?
- u8 cmd_buf[SNOR_MAX_CMD_SIZE];
- void *memory_map;
+};
+struct spi_nor_ops {
- int (*read_reg)(struct udevice *dev, u8 cmd, u8 *val, int len);
- int (*write_reg)(struct udevice *dev, u8 cmd, u8 *data, int len);
- int (*read)(struct udevice *dev, loff_t from, size_t len,
u_char *buf);
- int (*write)(struct udevice *dev, loff_t to, size_t len,
const u_char *buf);
+};
+#define spi_nor_get_ops(dev) ((struct spi_nor_ops *)(dev)->driver->ops)
+int spi_nor_merase(struct udevice *dev, struct erase_info *instr); +int spi_nor_mread(struct udevice *dev, loff_t from, size_t len,
size_t *retlen, u_char *buf);
+int spi_nor_scan(struct spi_nor *nor); +int spi_nor_bind(struct udevice *dev, struct spi_nor *nor);
+struct spi_nor *find_spi_nor_device(int dev_num); +int get_spi_nor_num(void); +struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev); +struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor);
+#endif /* __MTD_SPI_NOR_H */

On Wed, Jan 3, 2018 at 2:19 PM, Vignesh R vigneshr@ti.com wrote:
On Thursday 28 December 2017 11:42 AM, Jagan Teki wrote: [...]
+static const struct mtd_ops spi_nor_mtd_ops = {
.read = spi_nor_mread,
.erase = spi_nor_merase,
+};
Wondering why spi_nor_mwrite is not hooked up here?
yes, because of sst we need to assign the write hook during scan, see spi-nor.c
+U_BOOT_DRIVER(spinor_mtd) = {
.name = "spinor_mtd",
.id = UCLASS_MTD,
.ops = &spi_nor_mtd_ops,
.probe = spi_nor_mtd_probe,
+};
+U_BOOT_DRIVER(spinor) = {
.name = "spinor",
.id = UCLASS_SPI_NOR,
+};
+UCLASS_DRIVER(spinor) = {
.id = UCLASS_SPI_NOR,
.name = "spinor",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.per_device_auto_alloc_size = sizeof(struct spi_nor_uclass_priv),
+};
[...]
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1688e2 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,217 @@
[...]
+struct spi_nor {
struct udevice *dev;
const char *name;
u8 init_done;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
u32 max_write_size;
u32 flags;
u8 mode;
u8 read_mode;
Where is this field used? Shouldn't this field carry single/dual/quad mode information?
I think it came up from previous versions, no real use as of now will check and update in next version
thanks!

On Wednesday 03 January 2018 03:02 PM, Jagan Teki wrote:
On Wed, Jan 3, 2018 at 2:19 PM, Vignesh R vigneshr@ti.com wrote:
On Thursday 28 December 2017 11:42 AM, Jagan Teki wrote: [...]
+static const struct mtd_ops spi_nor_mtd_ops = {
.read = spi_nor_mread,
.erase = spi_nor_merase,
+};
Wondering why spi_nor_mwrite is not hooked up here?
yes, because of sst we need to assign the write hook during scan, see spi-nor.c
+U_BOOT_DRIVER(spinor_mtd) = {
.name = "spinor_mtd",
.id = UCLASS_MTD,
.ops = &spi_nor_mtd_ops,
.probe = spi_nor_mtd_probe,
+};
+U_BOOT_DRIVER(spinor) = {
.name = "spinor",
.id = UCLASS_SPI_NOR,
+};
+UCLASS_DRIVER(spinor) = {
.id = UCLASS_SPI_NOR,
.name = "spinor",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.per_device_auto_alloc_size = sizeof(struct spi_nor_uclass_priv),
+};
[...]
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1688e2 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,217 @@
[...]
+struct spi_nor {
struct udevice *dev;
const char *name;
u8 init_done;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
u32 max_write_size;
u32 flags;
u8 mode;
u8 read_mode;
Where is this field used? Shouldn't this field carry single/dual/quad mode information?
I think it came up from previous versions, no real use as of now will check and update in next version
This field is very much needed to inform SPI NOR controllers where to use 1-1-2 or 1-1-4 or 1-1-1 mode for reading data from flash. It should be the SPI NOR framework providing this info and not expecting driver to assume this or infer from DT.

On Wed, Jan 3, 2018 at 3:26 PM, Vignesh R vigneshr@ti.com wrote:
On Wednesday 03 January 2018 03:02 PM, Jagan Teki wrote:
On Wed, Jan 3, 2018 at 2:19 PM, Vignesh R vigneshr@ti.com wrote:
On Thursday 28 December 2017 11:42 AM, Jagan Teki wrote: [...]
+static const struct mtd_ops spi_nor_mtd_ops = {
.read = spi_nor_mread,
.erase = spi_nor_merase,
+};
Wondering why spi_nor_mwrite is not hooked up here?
yes, because of sst we need to assign the write hook during scan, see spi-nor.c
+U_BOOT_DRIVER(spinor_mtd) = {
.name = "spinor_mtd",
.id = UCLASS_MTD,
.ops = &spi_nor_mtd_ops,
.probe = spi_nor_mtd_probe,
+};
+U_BOOT_DRIVER(spinor) = {
.name = "spinor",
.id = UCLASS_SPI_NOR,
+};
+UCLASS_DRIVER(spinor) = {
.id = UCLASS_SPI_NOR,
.name = "spinor",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.per_device_auto_alloc_size = sizeof(struct spi_nor_uclass_priv),
+};
[...]
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1688e2 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,217 @@
[...]
+struct spi_nor {
struct udevice *dev;
const char *name;
u8 init_done;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
u32 max_write_size;
u32 flags;
u8 mode;
u8 read_mode;
Where is this field used? Shouldn't this field carry single/dual/quad mode information?
I think it came up from previous versions, no real use as of now will check and update in next version
This field is very much needed to inform SPI NOR controllers where to use 1-1-2 or 1-1-4 or 1-1-1 mode for reading data from flash. It should be the SPI NOR framework providing this info and not expecting driver to assume this or infer from DT.
yes remember, we have common mode(for both read and write) ie initialized and used in spi-nor instead have read_mode.

Hi Jagan:
2017-12-28 14:12 GMT+08:00 Jagan Teki jagan@amarulasolutions.com:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com
Makefile | 1 + drivers/mtd/spi-nor/Makefile | 7 + drivers/mtd/spi-nor/spi-nor-ids.c | 184 +++++++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 143 +++++++++ drivers/mtd/spi-nor/spi-nor.c | 569 ++++++++++++++++++++++++++++++ +++++ include/dm/uclass-id.h | 1 + include/linux/mtd/spi-nor.h | 217 +++++++++++++ 7 files changed, 1122 insertions(+) create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/spi-nor-ids.c create mode 100644 drivers/mtd/spi-nor/spi-nor-uclass.c create mode 100644 drivers/mtd/spi-nor/spi-nor.c create mode 100644 include/linux/mtd/spi-nor.h
diff --git a/Makefile b/Makefile index e6d309a..70b5202 100644 --- a/Makefile +++ b/Makefile @@ -662,6 +662,7 @@ libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/ libs-y += drivers/mtd/onenand/ libs-$(CONFIG_CMD_UBI) += drivers/mtd/ubi/ libs-y += drivers/mtd/spi/ +libs-y += drivers/mtd/spi-nor/ libs-y += drivers/net/ libs-y += drivers/net/phy/ libs-y += drivers/pci/ diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 0000000..c1212a8 --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (C) 2016 Jagan Teki jagan@openedev.com +# +# SPDX-License-Identifier: GPL-2.0+
+## spi-nor core +obj-y += spi-nor.o spi-nor-uclass.o spi-nor-ids.o diff --git a/drivers/mtd/spi-nor/spi-nor-ids.c b/drivers/mtd/spi-nor/spi-nor-ids.c new file mode 100644 index 0000000..db95655 --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-ids.c @@ -0,0 +1,184 @@ +/*
- SPI NOR IDs.
- Copyright (C) 2016 Jagan Teki jagan@openedev.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <linux/mtd/spi-nor.h>
+/* Used when the "_ext_id" is two bytes at most */ +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff, \
((_ext_id) >> 8) & 0xff, \
(_ext_id) & 0xff, \
}, \
\.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags),
+#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff, \
((_ext_id) >> 16) & 0xff, \
((_ext_id) >> 8) & 0xff, \
(_ext_id) & 0xff, \
}, \
.id_len = 6, \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags),
+const struct spi_nor_info spi_nor_ids[] = { +#ifdef CONFIG_SPI_NOR_MACRONIX /* MACRONIX */
{"mx25l2006e", INFO(0xc22012, 0x0, 64 * 1024, 4, 0) },
{"mx25l4005", INFO(0xc22013, 0x0, 64 * 1024, 8, 0) },
{"mx25l8005", INFO(0xc22014, 0x0, 64 * 1024, 16, 0) },
{"mx25l1605d", INFO(0xc22015, 0x0, 64 * 1024, 32, 0) },
{"mx25l3205d", INFO(0xc22016, 0x0, 64 * 1024, 64, 0) },
{"mx25l6405d", INFO(0xc22017, 0x0, 64 * 1024, 128, 0) },
{"mx25l12805", INFO(0xc22018, 0x0, 64 * 1024, 256, RD_FULL |
WR_QPP) },
{"mx25l25635f", INFO(0xc22019, 0x0, 64 * 1024, 512, RD_FULL |
WR_QPP) },
{"mx25l51235f", INFO(0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL |
WR_QPP) },
{"mx25u6435f", INFO(0xc22537, 0x0, 64 * 1024, 128, RD_FULL |
WR_QPP) },
{"mx25l12855e", INFO(0xc22618, 0x0, 64 * 1024, 256, RD_FULL |
WR_QPP) },
{"mx25u1635e", INFO(0xc22535, 0x0, 64 * 1024, 32, SECT_4K) },
{"mx66u51235f", INFO(0xc2253a, 0x0, 64 * 1024, 1024, RD_FULL |
WR_QPP) },
{"mx66l1g45g", INFO(0xc2201b, 0x0, 64 * 1024, 2048, RD_FULL |
WR_QPP) }, +#endif +#ifdef CONFIG_SPI_NOR_SPANSION /* SPANSION */
{"s25fl008a", INFO(0x010213, 0x0, 64 * 1024, 16, 0) },
{"s25fl016a", INFO(0x010214, 0x0, 64 * 1024, 32, 0) },
{"s25fl032a", INFO(0x010215, 0x0, 64 * 1024, 64, 0) },
{"s25fl064a", INFO(0x010216, 0x0, 64 * 1024, 128, 0) },
{"s25fl116k", INFO(0x014015, 0x0, 64 * 1024, 32, 0) },
{"s25fl164k", INFO(0x014017, 0x0140, 64 * 1024, 128, 0) },
{"s25fl128p_256k", INFO(0x012018, 0x0300, 256 * 1024, 64,
RD_FULL | WR_QPP) },
{"s25fl128p_64k", INFO(0x012018, 0x0301, 64 * 1024, 256,
RD_FULL | WR_QPP) },
{"s25fl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64,
RD_FULL | WR_QPP) },
{"s25fl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128,
RD_FULL | WR_QPP) },
{"s25fl128s_256k", INFO(0x012018, 0x4d00, 256 * 1024, 64,
RD_FULL | WR_QPP) },
{"s25fl128s_64k", INFO(0x012018, 0x4d01, 64 * 1024, 256,
RD_FULL | WR_QPP) },
{"s25fl256s_256k", INFO(0x010219, 0x4d00, 256 * 1024, 128,
RD_FULL | WR_QPP) },
{"s25fs256s_64k", INFO6(0x010219, 0x4d0181, 64 * 1024, 512,
RD_FULL | WR_QPP | SECT_4K) },
{"s25fl256s_64k", INFO(0x010219, 0x4d01, 64 * 1024, 512,
RD_FULL | WR_QPP) },
{"s25fs512s", INFO6(0x010220, 0x4d0081, 128 * 1024, 512,
RD_FULL | WR_QPP | SECT_4K) },
{"s25fl512s_256k", INFO(0x010220, 0x4d00, 256 * 1024, 256,
RD_FULL | WR_QPP) },
{"s25fl512s_64k", INFO(0x010220, 0x4d01, 64 * 1024, 1024,
RD_FULL | WR_QPP) },
{"s25fl512s_512k", INFO(0x010220, 0x4f00, 256 * 1024, 256,
RD_FULL | WR_QPP) }, +#endif +#ifdef CONFIG_SPI_NOR_STMICRO /* STMICRO */
{"m25p10", INFO(0x202011, 0x0, 32 * 1024, 4, 0) },
{"m25p20", INFO(0x202012, 0x0, 64 * 1024, 4, 0) },
{"m25p40", INFO(0x202013, 0x0, 64 * 1024, 8, 0) },
{"m25p80", INFO(0x202014, 0x0, 64 * 1024, 16, 0) },
{"m25p16", INFO(0x202015, 0x0, 64 * 1024, 32, 0) },
{"m25pE16", INFO(0x208015, 0x1000, 64 * 1024, 32, 0) },
{"m25pX16", INFO(0x207115, 0x1000, 64 * 1024, 32, RD_QUAD |
RD_DUAL) },
{"m25p32", INFO(0x202016, 0x0, 64 * 1024, 64, 0) },
{"m25p64", INFO(0x202017, 0x0, 64 * 1024, 128, 0) },
{"m25p128", INFO(0x202018, 0x0, 256 * 1024, 64, 0) },
{"m25pX64", INFO(0x207117, 0x0, 64 * 1024, 128, SECT_4K)
},
{"n25q016a", INFO(0x20bb15, 0x0, 64 * 1024, 32, SECT_4K)
},
{"n25q32", INFO(0x20ba16, 0x0, 64 * 1024, 64, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q32a", INFO(0x20bb16, 0x0, 64 * 1024, 64, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q64", INFO(0x20ba17, 0x0, 64 * 1024, 128, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q64a", INFO(0x20bb17, 0x0, 64 * 1024, 128, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q128", INFO(0x20ba18, 0x0, 64 * 1024, 256, RD_FULL
| WR_QPP) },
{"n25q128a", INFO(0x20bb18, 0x0, 64 * 1024, 256, RD_FULL
| WR_QPP) },
{"n25q256", INFO(0x20ba19, 0x0, 64 * 1024, 512, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q256a", INFO(0x20bb19, 0x0, 64 * 1024, 512, RD_FULL
| WR_QPP | SECT_4K) },
{"n25q512", INFO(0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"n25q512a", INFO(0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"n25q1024", INFO(0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"n25q1024a", INFO(0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"mt25qu02g", INFO(0x20bb22, 0x0, 64 * 1024, 4096, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"mt25ql02g", INFO(0x20ba22, 0x0, 64 * 1024, 4096, RD_FULL
| WR_QPP | E_FSR | SECT_4K) },
{"mt35xu512g", INFO6(0x2c5b1a, 0x104100, 128 * 1024, 512,
E_FSR | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_NOR_SST /* SST */
{"sst25vf040b", INFO(0xbf258d, 0x0, 64 * 1024, 8, SECT_4K
| SST_WR) },
{"sst25vf080b", INFO(0xbf258e, 0x0, 64 * 1024, 16, SECT_4K
| SST_WR) },
{"sst25vf016b", INFO(0xbf2541, 0x0, 64 * 1024, 32, SECT_4K
| SST_WR) },
{"sst25vf032b", INFO(0xbf254a, 0x0, 64 * 1024, 64, SECT_4K
| SST_WR) },
{"sst25vf064c", INFO(0xbf254b, 0x0, 64 * 1024, 128, SECT_4K)
},
{"sst25wf512", INFO(0xbf2501, 0x0, 64 * 1024, 1, SECT_4K
| SST_WR) },
{"sst25wf010", INFO(0xbf2502, 0x0, 64 * 1024, 2, SECT_4K
| SST_WR) },
{"sst25wf020", INFO(0xbf2503, 0x0, 64 * 1024, 4, SECT_4K
| SST_WR) },
{"sst25wf040", INFO(0xbf2504, 0x0, 64 * 1024, 8, SECT_4K
| SST_WR) },
{"sst25wf040b", INFO(0x621613, 0x0, 64 * 1024, 8, SECT_4K)
},
{"sst25wf080", INFO(0xbf2505, 0x0, 64 * 1024, 16, SECT_4K
| SST_WR) }, +#endif +#ifdef CONFIG_SPI_NOR_WINBOND /* WINBOND */
{"w25p80", INFO(0xef2014, 0x0, 64 * 1024, 16, 0) },
{"w25p16", INFO(0xef2015, 0x0, 64 * 1024, 32, 0) },
{"w25p32", INFO(0xef2016, 0x0, 64 * 1024, 64, 0) },
{"w25x40", INFO(0xef3013, 0x0, 64 * 1024, 8, SECT_4K)
},
{"w25x16", INFO(0xef3015, 0x0, 64 * 1024, 32, SECT_4K)
},
{"w25x32", INFO(0xef3016, 0x0, 64 * 1024, 64, SECT_4K)
},
{"w25x64", INFO(0xef3017, 0x0, 64 * 1024, 128, SECT_4K)
},
{"w25q80bl", INFO(0xef4014, 0x0, 64 * 1024, 16, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q16cl", INFO(0xef4015, 0x0, 64 * 1024, 32, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q32bv", INFO(0xef4016, 0x0, 64 * 1024, 64, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q64cv", INFO(0xef4017, 0x0, 64 * 1024, 128, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q128bv", INFO(0xef4018, 0x0, 64 * 1024, 256, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q256", INFO(0xef4019, 0x0, 64 * 1024, 512, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q80bw", INFO(0xef5014, 0x0, 64 * 1024, 16, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q16dw", INFO(0xef6015, 0x0, 64 * 1024, 32, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q32dw", INFO(0xef6016, 0x0, 64 * 1024, 64, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q64dw", INFO(0xef6017, 0x0, 64 * 1024, 128, RD_FULL
| WR_QPP | SECT_4K) },
{"w25q128fw", INFO(0xef6018, 0x0, 64 * 1024, 256, RD_FULL
| WR_QPP | SECT_4K) }, +#endif +#ifdef CONFIG_SPI_NOR_MISC
/* ATMEL */
{"at45db011d", INFO(0x1f2200, 0x0, 64 * 1024, 4, SECT_4K)
},
{"at45db021d", INFO(0x1f2300, 0x0, 64 * 1024, 8, SECT_4K)
},
{"at45db041d", INFO(0x1f2400, 0x0, 64 * 1024, 8, SECT_4K)
},
{"at45db081d", INFO(0x1f2500, 0x0, 64 * 1024, 16, SECT_4K)
},
{"at45db161d", INFO(0x1f2600, 0x0, 64 * 1024, 32, SECT_4K)
},
{"at45db321d", INFO(0x1f2700, 0x0, 64 * 1024, 64, SECT_4K)
},
{"at45db641d", INFO(0x1f2800, 0x0, 64 * 1024, 128, SECT_4K)
},
{"at25df321a", INFO(0x1f4701, 0x0, 64 * 1024, 64, SECT_4K)
},
{"at25df321", INFO(0x1f4700, 0x0, 64 * 1024, 64, SECT_4K)
},
{"at26df081a", INFO(0x1f4501, 0x0, 64 * 1024, 16, SECT_4K)
},
/* EON */
{"en25q32b", INFO(0x1c3016, 0x0, 64 * 1024, 64, 0) },
{"en25q64", INFO(0x1c3017, 0x0, 64 * 1024, 128, SECT_4K)
},
{"en25q128b", INFO(0x1c3018, 0x0, 64 * 1024, 256, 0) },
{"en25s64", INFO(0x1c3817, 0x0, 64 * 1024, 128, 0) },
/* GIGADEVICE */
{"gd25q64b", INFO(0xc84017, 0x0, 64 * 1024, 128, SECT_4K)
},
{"gd25lq32", INFO(0xc86016, 0x0, 64 * 1024, 64, SECT_4K)
},
/* ISSI */
{"is25lq040b", INFO(0x9d4013, 0x0, 64 * 1024, 8, 0) },
{"is25lp032", INFO(0x9d6016, 0x0, 64 * 1024, 64, 0) },
{"is25lp064", INFO(0x9d6017, 0x0, 64 * 1024, 128, 0) },
{"is25lp128", INFO(0x9d6018, 0x0, 64 * 1024, 256, 0) },
+#endif
{}, /* Empty entry to terminate the list */
/*
* Note:
* Below paired flash devices has similar spi_nor params.
* (s25fl129p_64k, s25fl128s_64k)
* (w25q80bl, w25q80bv)
* (w25q16cl, w25q16dv)
* (w25q32bv, w25q32fv_spi)
* (w25q64cv, w25q64fv_spi)
* (w25q128bv, w25q128fv_spi)
* (w25q32dw, w25q32fv_qpi)
* (w25q64dw, w25q64fv_qpi)
* (w25q128fw, w25q128fv_qpi)
*/
+}; diff --git a/drivers/mtd/spi-nor/spi-nor-uclass.c b/drivers/mtd/spi-nor/spi-nor-uclass.c new file mode 100644 index 0000000..919682d --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-uclass.c @@ -0,0 +1,143 @@ +/*
- SPI NOR Core framework.
- Copyright (C) 2016 Jagan Teki jagan@openedev.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <mtd.h>
+#include <dm/device-internal.h> +#include <linux/mtd/spi-nor.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev) +{
struct spi_nor_uclass_priv *upriv;
if (!device_active(dev))
return NULL;
upriv = dev_get_uclass_priv(dev);
return upriv->spi_nor;
+}
+struct spi_nor *find_spi_nor_device(int dev_num) +{
struct udevice *dev, *spi_nor_dev;
int ret;
ret = mtd_find_device(MTD_IF_TYPE_SPI_NOR, dev_num, &dev);
if (ret) {
printf("SPI-NOR Device %d not found\n", dev_num);
return NULL;
}
spi_nor_dev = dev_get_parent(dev);
struct spi_nor *nor = spi_nor_get_spi_nor_dev(spi_nor_dev);
return nor;
+}
+int get_spi_nor_num(void) +{
return max((mtd_find_max_devnum(MTD_IF_TYPE_SPI_NOR) + 1), 0);
+}
+struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor) +{
struct mtd_info *mtd;
struct udevice *dev;
device_find_first_child(nor->dev, &dev);
if (!dev)
return NULL;
mtd = dev_get_uclass_platdata(dev);
return mtd;
+}
+void print_spi_nor_devices(char separator) +{
struct udevice *dev;
bool first = true;
for (uclass_first_device(UCLASS_SPI_NOR, &dev);
dev;
uclass_next_device(&dev), first = false) {
struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
if (!first) {
printf("%c", separator);
if (separator != '\n')
puts(" ");
}
printf("%s: %d", dev->name, spi_nor_get_mtd_info(nor)->
devnum);
}
printf("\n");
+}
+int spi_nor_bind(struct udevice *dev, struct spi_nor *nor) +{
struct udevice *mdev;
int ret;
if (!spi_nor_get_ops(dev))
return -ENOSYS;
ret = mtd_create_devicef(dev, "spinor_mtd", "mtd",
MTD_IF_TYPE_SPI_NOR,
&mdev);
if (ret) {
debug("Cannot create mtd device\n");
return ret;
}
nor->dev = dev;
return 0;
+}
+static int spi_nor_mtd_probe(struct udevice *dev) +{
struct udevice *spi_nor_dev = dev_get_parent(dev);
struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(spi_nor_
dev);
struct spi_nor *nor = upriv->spi_nor;
int ret;
ret = spi_nor_scan(nor);
if (ret) {
debug("%s: spi_nor_scan() failed (err=%d)\n", __func__,
ret);
return ret;
}
return 0;
+}
+static const struct mtd_ops spi_nor_mtd_ops = {
.read = spi_nor_mread,
.erase = spi_nor_merase,
+};
+U_BOOT_DRIVER(spinor_mtd) = {
.name = "spinor_mtd",
.id = UCLASS_MTD,
.ops = &spi_nor_mtd_ops,
.probe = spi_nor_mtd_probe,
+};
+U_BOOT_DRIVER(spinor) = {
.name = "spinor",
.id = UCLASS_SPI_NOR,
+};
+UCLASS_DRIVER(spinor) = {
.id = UCLASS_SPI_NOR,
.name = "spinor",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.per_device_auto_alloc_size = sizeof(struct spi_nor_uclass_priv),
+}; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c new file mode 100644 index 0000000..09fb8ca --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -0,0 +1,569 @@ +/*
- SPI NOR Core framework.
- Copyright (C) 2016 Jagan Teki jagan@openedev.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <mapmem.h> +#include <mtd.h>
+#include <dm/device-internal.h> +#include <linux/math64.h> +#include <linux/types.h> +#include <linux/mtd/spi-nor.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* Set write enable latch with Write Enable command */ +static inline int write_enable(struct udevice *dev) +{
return spi_nor_get_ops(dev)->write_reg(dev, SNOR_OP_WREN, NULL,
0); +}
+/* Re-set write enable latch with Write Disable command */ +static inline int write_disable(struct udevice *dev) +{
return spi_nor_get_ops(dev)->write_reg(dev, SNOR_OP_WRDI, NULL,
0); +}
+static int read_sr(struct udevice *dev) +{
u8 sr;
int ret;
ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDSR, &sr, 1);
if (ret < 0) {
debug("spi-nor: fail to read status register\n");
return ret;
}
return sr;
+}
+static int read_fsr(struct udevice *dev) +{
u8 fsr;
int ret;
ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDFSR, &fsr, 1);
if (ret < 0) {
debug("spi-nor: fail to read flag status register\n");
return ret;
}
return fsr;
+}
+static int write_sr(struct udevice *dev, u8 ws) +{
struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
const struct spi_nor_ops *ops = spi_nor_get_ops(dev);
nor->cmd_buf[0] = ws;
return ops->write_reg(dev, SNOR_OP_WRSR, nor->cmd_buf, 1);
+}
+#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) +static int read_cr(struct udevice *dev) +{
u8 cr;
int ret;
ret = spi_nor_get_ops(dev)->read_reg(dev, SNOR_OP_RDCR, &cr, 1);
if (ret < 0) {
debug("spi-nor: fail to read config register\n");
return ret;
}
return cr;
+}
+/*
- Write status Register and configuration register with 2 bytes
- First byte will be written to the status register.
- Second byte will be written to the configuration register.
- Return negative if error occured.
- */
+static int write_sr_cr(struct udevice *dev, u16 val) +{
struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
const struct spi_nor_ops *ops = spi_nor_get_ops(dev);
nor->cmd_buf[0] = val & 0xff;
nor->cmd_buf[1] = (val >> 8);
return ops->write_reg(dev, SNOR_OP_WRSR, nor->cmd_buf, 2);
+} +#endif
+static int spi_nor_sr_ready(struct udevice *dev) +{
int sr = read_sr(dev);
if (sr < 0)
return sr;
else
return !(sr & SR_WIP);
+}
+static int spi_nor_fsr_ready(struct udevice *dev) +{
int fsr = read_fsr(dev);
if (fsr < 0)
return fsr;
else
return fsr & FSR_READY;
+}
+static int spi_nor_ready(struct udevice *dev) +{
struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
int sr, fsr;
sr = spi_nor_sr_ready(dev);
if (sr < 0)
return sr;
fsr = 1;
if (nor->flags & SNOR_F_USE_FSR) {
fsr = spi_nor_fsr_ready(dev);
if (fsr < 0)
return fsr;
}
return sr && fsr;
+}
+static int spi_nor_wait_till_ready(struct udevice *dev, unsigned long timeout) +{
int timebase, ret;
timebase = get_timer(0);
while (get_timer(timebase) < timeout) {
ret = spi_nor_ready(dev);
if (ret < 0)
return ret;
if (ret)
return 0;
}
printf("spi-nor: Timeout!\n");
return -ETIMEDOUT;
+}
+static const struct spi_nor_info *spi_nor_id(struct udevice *dev) +{
int tmp;
u8 id[SPI_NOR_MAX_ID_LEN];
const struct spi_nor_info *info;
const struct spi_nor_ops *ops = spi_nor_get_ops(dev);
tmp = ops->read_reg(dev, SNOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
if (tmp < 0) {
printf("spi-nor: error %d reading JEDEC ID\n", tmp);
return ERR_PTR(tmp);
}
info = spi_nor_ids;
for (; info->name != NULL; info++) {
if (info->id_len) {
if (!memcmp(info->id, id, info->id_len))
return info;
}
}
printf("spi-nor: unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
id[0], id[1], id[2]);
return ERR_PTR(-ENODEV);
+}
+int spi_nor_merase(struct udevice *dev, struct erase_info *instr) +{
struct mtd_info *mtd = dev_get_uclass_platdata(dev);
int devnum = mtd->devnum;
struct spi_nor *nor;
const struct spi_nor_ops *ops;
u32 addr, len, erase_addr;
uint32_t rem;
int ret = -1;
nor = find_spi_nor_device(devnum);
if (!nor)
return -ENODEV;
ops = spi_nor_get_ops(nor->dev);
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
return -EINVAL;
addr = instr->addr;
len = instr->len;
while (len) {
erase_addr = addr;
write_enable(nor->dev);
ret = ops->write(nor->dev, erase_addr, 0, NULL);
Can we considering use ops->write_reg(nor->dev, nor->erase_opcode, &addr, len) here , keep sync with the linux upstream spi-nor does.[0]
[0] https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree...
+ if (ret < 0)
goto erase_err;
ret = spi_nor_wait_till_ready(nor->dev,
SNOR_READY_WAIT_ERASE);
if (ret < 0)
goto erase_err;
addr += mtd->erasesize;
len -= mtd->erasesize;
}
write_disable(nor->dev);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return ret;
+erase_err:
instr->state = MTD_ERASE_FAILED;
return ret;
+}
+static int spi_nor_mwrite(struct udevice *dev, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
+{
struct mtd_info *mtd = dev_get_uclass_platdata(dev);
int devnum = mtd->devnum;
struct spi_nor *nor;
const struct spi_nor_ops *ops;
size_t addr, byte_addr;
size_t chunk_len, actual;
uint32_t page_size;
int ret = -1;
nor = find_spi_nor_device(devnum);
if (!nor)
return -ENODEV;
ops = spi_nor_get_ops(nor->dev);
page_size = mtd->writebufsize;
for (actual = 0; actual < len; actual += chunk_len) {
addr = to;
byte_addr = addr % page_size;
chunk_len = min(len - actual, (size_t)(page_size -
byte_addr));
if (nor->max_write_size)
chunk_len = min(chunk_len,
(size_t)nor->max_write_size);
write_enable(nor->dev);
ret = ops->write(nor->dev, addr, chunk_len, buf + actual);
if (ret < 0)
break;
ret = spi_nor_wait_till_ready(nor->dev,
SNOR_READY_WAIT_PROG);
if (ret < 0)
return ret;
to += chunk_len;
*retlen += chunk_len;
}
return ret;
+}
+int spi_nor_mread(struct udevice *dev, loff_t from, size_t len,
size_t *retlen, u_char *buf)
+{
struct mtd_info *mtd = dev_get_uclass_platdata(dev);
int devnum = mtd->devnum;
struct spi_nor *nor;
const struct spi_nor_ops *ops;
int ret;
nor = find_spi_nor_device(devnum);
if (!nor)
return -ENODEV;
ops = spi_nor_get_ops(nor->dev);
/* Handle memory-mapped SPI */
if (nor->memory_map) {
ret = ops->read(nor->dev, from, len, buf);
if (ret) {
debug("spi-nor: mmap read failed\n");
return ret;
}
return ret;
}
ret = ops->read(nor->dev, from, len, buf);
if (ret < 0) {
printf("%s ret = %d\n", __func__, ret);
return ret;
}
*retlen += len;
return ret;
+}
+#ifdef CONFIG_SPI_NOR_MACRONIX +static int macronix_quad_enable(struct udevice *dev) +{
int ret, val;
val = read_sr(dev);
if (val < 0)
return val;
if (val & SR_QUAD_EN_MX)
return 0;
write_enable(dev);
ret = write_sr(dev, val | SR_QUAD_EN_MX);
if (ret < 0)
return ret;
if (spi_nor_wait_till_ready(dev, SNOR_READY_WAIT_PROG))
return 1;
ret = read_sr(dev);
if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
printf("spi-nor: Macronix Quad bit not set\n");
return -EINVAL;
}
return 0;
+} +#endif
+#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND) +static int spansion_quad_enable(struct udevice *dev) +{
int ret, val;
val = read_cr(dev);
if (val < 0)
return val;
if (val & CR_QUAD_EN_SPAN)
return 0;
write_enable(dev);
ret = write_sr_cr(dev, val | CR_QUAD_EN_SPAN);
if (ret < 0)
return ret;
if (spi_nor_wait_till_ready(dev, SNOR_READY_WAIT_PROG))
return 1;
/* read back and check it */
ret = read_cr(dev);
if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
printf("spi-nor: Spansion Quad bit not set\n");
return -EINVAL;
}
return 0;
+} +#endif
+static int set_quad_mode(struct udevice *dev, const struct spi_nor_info *info) +{
switch (JEDEC_MFR(info)) {
+#ifdef CONFIG_SPI_NOR_MACRONIX
case SNOR_MFR_MACRONIX:
return macronix_quad_enable(dev);
+#endif +#if defined(CONFIG_SPI_NOR_SPANSION) || defined(CONFIG_SPI_NOR_WINBOND)
case SNOR_MFR_SPANSION:
case SNOR_MFR_WINBOND:
return spansion_quad_enable(dev);
+#endif +#ifdef CONFIG_SPI_NOR_STMICRO
case SNOR_MFR_MICRON:
return 0;
+#endif
default:
printf("spi-nor: Need set QEB func for %02x flash\n",
JEDEC_MFR(info));
return -1;
}
+}
+#if CONFIG_IS_ENABLED(OF_CONTROL) +int spi_nor_decode_fdt(const void *blob, struct spi_nor *nor) +{
struct udevice *dev = nor->dev;
struct mtd_info *mtd = mtd_get_info(dev);
fdt_addr_t addr;
fdt_size_t size;
int node;
/* If there is no node, do nothing */
node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
if (node < 0)
return 0;
addr = fdtdec_get_addr_size(blob, node, "memory-map", &size);
if (addr == FDT_ADDR_T_NONE) {
debug("%s: Cannot decode address\n", __func__);
return 0;
}
if (mtd->size != size) {
debug("%s: Memory map must cover entire device\n",
__func__);
return -1;
}
nor->memory_map = map_sysmem(addr, size);
return 0;
+} +#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
+int spi_nor_scan(struct spi_nor *nor) +{
struct mtd_info *mtd = spi_nor_get_mtd_info(nor);
struct mtd_ops *ops = mtd_get_ops(mtd->dev);
const struct spi_nor_info *info = NULL;
int ret;
struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(nor->dev);
upriv->spi_nor = nor;
if (nor->init_done)
return 0;
info = spi_nor_id(nor->dev);
if (IS_ERR_OR_NULL(info)) {
ret = -ENOENT;
goto err;
}
/*
* Flash powers up read-only, so clear BP# bits.
*
* Note on some flash (like Macronix), QE (quad enable) bit is in
the
* same status register as BP# bits, and we need preserve its
original
* value during a reboot cycle as this is required by some
platforms
* (like Intel ICH SPI controller working under descriptor mode).
*/
if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
(JEDEC_MFR(info) == SNOR_MFR_SST) ||
(JEDEC_MFR(info) == SNOR_MFR_MACRONIX)) {
u8 sr = 0;
if (JEDEC_MFR(info) == SNOR_MFR_MACRONIX)
sr = read_sr(nor->dev) & SR_QUAD_EN_MX;
write_sr(nor->dev, sr);
}
mtd->name = info->name;
mtd->priv = nor;
mtd->type = MTD_NORFLASH;
mtd->writesize = 1;
mtd->flags = MTD_CAP_NORFLASH;
if (info->flags & E_FSR)
nor->flags |= SNOR_F_USE_FSR;
if (info->flags & SST_WR)
nor->flags |= SNOR_F_SST_WRITE;
ops->write = spi_nor_mwrite;
/* compute the flash size */
nor->page_size = info->page_size;
/*
* The Spansion S25FL032P and S25FL064P have 256b pages, yet use
the
* 0x4d00 Extended JEDEC code. The rest of the Spansion flashes
with
* the 0x4d00 Extended JEDEC code have 512b pages. All of the
others
* have 256b pages.
*/
if (JEDEC_EXT(info) == 0x4d00) {
if ((JEDEC_ID(info) != 0x0215) &&
(JEDEC_ID(info) != 0x0216))
nor->page_size = 512;
}
mtd->writebufsize = nor->page_size;
mtd->size = info->sector_size * info->n_sectors;
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
nor->erase_opcode = SNOR_OP_BE_4K;
mtd->erasesize = 4096;
} else
+#endif
{
nor->erase_opcode = SNOR_OP_SE;
mtd->erasesize = info->sector_size;
}
/* Look for read opcode */
nor->read_opcode = SNOR_OP_READ_FAST;
if (nor->mode & SNOR_READ)
nor->read_opcode = SNOR_OP_READ;
else if (nor->mode & SNOR_READ_1_1_4 && info->flags & RD_QUAD)
nor->read_opcode = SNOR_OP_READ_1_1_4;
else if (nor->mode & SNOR_READ_1_1_2 && info->flags & RD_DUAL)
nor->read_opcode = SNOR_OP_READ_1_1_2;
/* Look for program opcode */
if (info->flags & WR_QPP && nor->mode & SNOR_WRITE_1_1_4)
nor->program_opcode = SNOR_OP_QPP;
else
/* Go for default supported write cmd */
nor->program_opcode = SNOR_OP_PP;
/* Set the quad enable bit - only for quad commands */
if ((nor->read_opcode == SNOR_OP_READ_1_1_4) ||
(nor->read_opcode == SNOR_OP_READ_1_1_4_IO) ||
(nor->program_opcode == SNOR_OP_QPP)) {
ret = set_quad_mode(nor->dev, info);
if (ret) {
debug("spi-nor: quad mode not supported for
%02x\n",
JEDEC_MFR(info));
goto err;
}
}
nor->addr_width = 3;
/* Dummy cycles for read */
switch (nor->read_opcode) {
case SNOR_OP_READ_1_1_4_IO:
nor->read_dummy = 16;
break;
case SNOR_OP_READ:
nor->read_dummy = 0;
break;
default:
nor->read_dummy = 8;
}
+#if CONFIG_IS_ENABLED(OF_CONTROL)
ret = spi_nor_decode_fdt(gd->fdt_blob, nor);
if (ret) {
debug("spi-nor: FDT decode error\n");
goto err;
}
+#endif
nor->init_done = 1;
return 0;
+err:
nor->init_done = 0;
return ret;
+} diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 3fc2083..06541a4 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -75,6 +75,7 @@ enum uclass_id { UCLASS_SERIAL, /* Serial UART */ UCLASS_SPI, /* SPI bus */ UCLASS_SPMI, /* System Power Management Interface bus */
UCLASS_SPI_NOR, /* SPI NOR flash */ UCLASS_SPI_FLASH, /* SPI flash */ UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SYSCON, /* System configuration device */
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1688e2 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,217 @@ +/*
- SPI NOR Core header file.
- Copyright (C) 2016 Jagan Teki jagan@openedev.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __MTD_SPI_NOR_H +#define __MTD_SPI_NOR_H
+#include <common.h> +#include <linux/mtd/mtd.h>
+/*
- Manufacturer IDs
- The first byte returned from the flash after sending opcode
SPINOR_OP_RDID.
- Sometimes these are the same as CFI IDs, but sometimes they aren't.
- */
+#define SNOR_MFR_ATMEL 0x1f +#define SNOR_MFR_MACRONIX 0xc2 +#define SNOR_MFR_MICRON 0x20 /* ST Micro <--> Micron */ +#define SNOR_MFR_SPANSION 0x01 +#define SNOR_MFR_SST 0xbf +#define SNOR_MFR_WINBOND 0xef
+/**
- SPI NOR opcodes.
- Note on opcode nomenclature: some opcodes have a format like
- SNOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the
number
- of I/O lines used for the opcode, address, and data (respectively). The
- FUNCTION has an optional suffix of '4', to represent an opcode which
- requires a 4-byte (32-bit) address.
- */
+#define SNOR_OP_WRDI 0x04 /* Write disable */ +#define SNOR_OP_WREN 0x06 /* Write enable */ +#define SNOR_OP_RDSR 0x05 /* Read status register */ +#define SNOR_OP_WRSR 0x01 /* Write status register 1 byte */ +#define SNOR_OP_READ 0x03 /* Read data bytes (low frequency) */ +#define SNOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ +#define SNOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +#define SNOR_OP_READ_1_1_2_IO 0xbb /* Read data bytes (Dual IO SPI) */ +#define SNOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ +#define SNOR_OP_READ_1_1_4_IO 0xeb /* Read data bytes (Quad IO SPI) */ +#define SNOR_OP_BRWR 0x17 /* Bank register write */ +#define SNOR_OP_BRRD 0x16 /* Bank register read */ +#define SNOR_OP_WREAR 0xC5 /* Write extended address register */ +#define SNOR_OP_RDEAR 0xC8 /* Read extended address register */ +#define SNOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ +#define SNOR_OP_QPP 0x32 /* Quad Page program */ +#define SNOR_OP_BE_4K 0x20 /* Erase 4KiB block */ +#define SNOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define SNOR_OP_BE_32K 0x52 /* Erase 32KiB block */ +#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define SNOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define SNOR_OP_RDID 0x9f /* Read JEDEC ID */ +#define SNOR_OP_RDCR 0x35 /* Read configuration register */ +#define SNOR_OP_RDFSR 0x70 /* Read flag status register */
+/* Used for SST flashes only. */ +#define SNOR_OP_BP 0x02 /* Byte program */ +#define SNOR_OP_AAI_WP 0xad /* Auto addr increment word program */
+/* Status Register bits. */ +#define SR_WIP BIT(0) /* Write in progress */ +#define SR_WEL BIT(1) /* Write enable latch */
+/* meaning of other SR_* bits may differ between vendors */ +#define SR_BP0 BIT(2) /* Block protect 0 */ +#define SR_BP1 BIT(3) /* Block protect 1 */ +#define SR_BP2 BIT(4) /* Block protect 2 */ +#define SR_SRWD BIT(7) /* SR write protect */
+#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
+/* Flag Status Register bits */ +#define FSR_READY BIT(7)
+/* Configuration Register bits. */ +#define CR_QUAD_EN_SPAN BIT(1) /* Spansion/Winbond Quad I/O */
+/* Flash timeout values */ +#define SNOR_READY_WAIT_PROG (2 * CONFIG_SYS_HZ) +#define SNOR_READY_WAIT_ERASE (5 * CONFIG_SYS_HZ) +#define SNOR_MAX_CMD_SIZE 4 +#define SNOR_16MB_BOUN 0x1000000
+/**
- struct spi_nor_uclass_priv - Holds information about a device used by
the uclass
- */
+struct spi_nor_uclass_priv {
struct spi_nor *spi_nor;
+};
+enum snor_option_flags {
SNOR_F_SST_WRITE = BIT(0),
SNOR_F_USE_FSR = BIT(1),
SNOR_F_U_PAGE = BIT(1),
+};
+enum mode {
SNOR_READ = BIT(0),
SNOR_READ_1_1_2 = BIT(1),
SNOR_READ_1_1_4 = BIT(2),
SNOR_READ_1_1_2_IO = BIT(3),
SNOR_READ_1_1_4_IO = BIT(4),
SNOR_WRITE_1_1_BYTE = BIT(5),
SNOR_WRITE_1_1_4 = BIT(6),
+};
+#define JEDEC_MFR(info) ((info)->id[0]) +#define JEDEC_ID(info) (((info)->id[1]) << 8 | ((info)->id[2])) +#define JEDEC_EXT(info) (((info)->id[3]) << 8 | ((info)->id[4])) +#define SPI_NOR_MAX_ID_LEN 6
+struct spi_nor_info {
char *name;
/*
* This array stores the ID bytes.
* The first three bytes are the JEDIC ID.
* JEDEC ID zero means "no ID" (mostly older chips).
*/
u8 id[SPI_NOR_MAX_ID_LEN];
u8 id_len;
/* The size listed here is what works with SNOR_OP_SE, which isn't
* necessarily called a "sector" by the vendor.
*/
unsigned sector_size;
u16 n_sectors;
u16 page_size;
u16 flags;
+#define SECT_4K BIT(0) +#define E_FSR BIT(1) +#define SST_WR BIT(2) +#define WR_QPP BIT(3) +#define RD_QUAD BIT(4) +#define RD_DUAL BIT(5) +#define RD_QUADIO BIT(6) +#define RD_DUALIO BIT(7) +#define RD_FULL (RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO) +};
+extern const struct spi_nor_info spi_nor_ids[];
+/**
- struct spi_nor - Structure for defining a the SPI NOR layer
- @dev: SPI NOR device
- @name: name of the SPI NOR device
- @page_size: the page size of the SPI NOR
- @addr_width: number of address bytes
- @erase_opcode: the opcode for erasing a sector
- @read_opcode: the read opcode
- @read_dummy: the dummy bytes needed by the read
operation
- @program_opcode: the program opcode
- @max_write_size: If non-zero, the maximum number of bytes which can
be written at once, excluding command bytes.
- @flags: flag options for the current SPI-NOR (SNOR_F_*)
- @mode: read, write mode or any other mode bits.
- @read_mode: read mode.
- @cmd_buf: used by the write_reg
- @read_reg: [DRIVER-SPECIFIC] read out the register
- @write_reg: [DRIVER-SPECIFIC] write data to the register
- @read: [DRIVER-SPECIFIC] read data from the SPI NOR
- @write: [DRIVER-SPECIFIC] write data to the SPI NOR
- @memory_map: address of read-only SPI NOR access
- */
+struct spi_nor {
struct udevice *dev;
const char *name;
u8 init_done;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
u32 max_write_size;
u32 flags;
u8 mode;
u8 read_mode;
u8 cmd_buf[SNOR_MAX_CMD_SIZE];
void *memory_map;
+};
+struct spi_nor_ops {
int (*read_reg)(struct udevice *dev, u8 cmd, u8 *val, int len);
int (*write_reg)(struct udevice *dev, u8 cmd, u8 *data, int len);
int (*read)(struct udevice *dev, loff_t from, size_t len,
u_char *buf);
int (*write)(struct udevice *dev, loff_t to, size_t len,
const u_char *buf);
+};
+#define spi_nor_get_ops(dev) ((struct spi_nor_ops *)(dev)->driver->ops)
+int spi_nor_merase(struct udevice *dev, struct erase_info *instr); +int spi_nor_mread(struct udevice *dev, loff_t from, size_t len,
size_t *retlen, u_char *buf);
+int spi_nor_scan(struct spi_nor *nor); +int spi_nor_bind(struct udevice *dev, struct spi_nor *nor);
+struct spi_nor *find_spi_nor_device(int dev_num); +int get_spi_nor_num(void); +struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev); +struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor);
+#endif /* __MTD_SPI_NOR_H */
2.7.4
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

[Resending on correct patch of the patch-set]
Dear Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Thursday, December 28, 2017 11:42 AM To: u-boot@lists.denx.de Cc: Tom Rini trini@konsulko.com Subject: [U-Boot] [PATCH v10 03/27] mtd: add SPI-NOR core support
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
As per this patch-set, fsl_qspi is looks to be getting proposed in driver/mtd/spi-nor/ folder.
fsl_qspi is supporting both flash and fpga. We are not sure about its location.
There is an on-going effort for similar type of requirement in Linux by Boris Brezillon . It is dealing with controllers supporting NORs, NANDs, SRAMs etc. http://patchwork.ozlabs.org/project/linux-mtd/list/?series=27088
Borris has also ported fsl_qspi in driver/spi to use this new framework. It is still not completely tested. https://github.com/bbrezillon/linux/commit/43cc45764b975bfbb191de3f6a37e073d...
Will you also follow similar approach as being done in http://patchwork.ozlabs.org/project/linux-mtd/list/?series=27088 for longer term?
For Now, We are planning to update fsl_qspi driver to support dynamic LUT. Similar patch is in progress in Linux http://patchwork.ozlabs.org/project/linux-mtd/list/?series=26084 Now we are confused, should be port fsl_qspi in driver/mtd/spi-nor and then update driver Or We update driver/spi/fsl_qspi for dynamic LUTs. We may need to modify existing framework to get all required info for dynamic LUT.
Also we want some change in framework like support of - 4 byte address and SFDP : http://patchwork.ozlabs.org/project/uboot/list/?series=19621&state=* - SMPT : in discussion in Linux http://patchwork.ozlabs.org/patch/869718/ Which code base should we use? u-boot-spi.git branch mtd-spinor-working or u-boot.git master branch
--pk

Prabhakar, Jagan,
On Wed, 28 Feb 2018 09:42:11 +0000 Prabhakar Kushwaha prabhakar.kushwaha@nxp.com wrote:
[Resending on correct patch of the patch-set]
Dear Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Thursday, December 28, 2017 11:42 AM To: u-boot@lists.denx.de Cc: Tom Rini trini@konsulko.com Subject: [U-Boot] [PATCH v10 03/27] mtd: add SPI-NOR core support
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
Okay, I've been working a bit with this controller, and can say it's not quite true. This controller supports any kind of device that expects memory-like operations (or whatever you want to call them), that is, everything that is formed of one opcode, X address cycles, Y dummy cycles and Z data in/out cycles (X, Y and Z can be zero).
Actually, I even think it could support regular SPI transfers, all we'd have to do is use READ/WRITE instructions to do the transfers.
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
I've discussed it privately with Cyrille before I sent the spi-mem extension proposal, and he seemed to agree with the approach. I don't know what his opinion is now that the RFC has been posted, but if he hasn't changed his mind, that means Linux implementation is going in the opposite direction. So it's probably worth reconsidering this move.
Cyrille, could you clarify your opinion? I'm also waiting your feedback on the RFC to send a v2 addressing the comments I had from other people.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
As per this patch-set, fsl_qspi is looks to be getting proposed in driver/mtd/spi-nor/ folder.
fsl_qspi is supporting both flash and fpga. We are not sure about its location.
There is an on-going effort for similar type of requirement in Linux by Boris Brezillon . It is dealing with controllers supporting NORs, NANDs, SRAMs etc. http://patchwork.ozlabs.org/project/linux-mtd/list/?series=27088
Borris has also ported fsl_qspi in driver/spi to use this new framework. It is still not completely tested. https://github.com/bbrezillon/linux/commit/43cc45764b975bfbb191de3f6a37e073d...
Will you also follow similar approach as being done in http://patchwork.ozlabs.org/project/linux-mtd/list/?series=27088 for longer term?
That would be great if the code base could converge.
Regards,
Boris

Dear Jagan,
-----Original Message----- From: Boris Brezillon [mailto:boris.brezillon@bootlin.com] Sent: Wednesday, February 28, 2018 5:40 PM To: Prabhakar Kushwaha prabhakar.kushwaha@nxp.com; Jagan Teki jagan@amarulasolutions.com; Cyrille Pitchen cyrille.pitchen@wedev4u.fr Cc: u-boot@lists.denx.de; Suresh Gupta suresh.gupta@nxp.com; Yogesh Narayan Gaur yogeshnarayan.gaur@nxp.com; Poonam Aggrwal poonam.aggrwal@nxp.com; Ashish Kumar ashish.kumar@nxp.com; Han Xu han.xu@nxp.com; Frieder Schrempf frieder.schrempf@exceet.de; Boris Brezillon <boris.brezillon@free- electrons.com> Subject: Re: [U-Boot] [PATCH v10 03/27] mtd: add SPI-NOR core support
Prabhakar, Jagan,
On Wed, 28 Feb 2018 09:42:11 +0000 Prabhakar Kushwaha prabhakar.kushwaha@nxp.com wrote:
[Resending on correct patch of the patch-set]
Dear Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Thursday, December 28, 2017 11:42 AM To: u-boot@lists.denx.de Cc: Tom Rini trini@konsulko.com Subject: [U-Boot] [PATCH v10 03/27] mtd: add SPI-NOR core support
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
Okay, I've been working a bit with this controller, and can say it's not quite true. This controller supports any kind of device that expects memory-like operations (or whatever you want to call them), that is, everything that is formed of one opcode, X address cycles, Y dummy cycles and Z data in/out cycles (X, Y and Z can be zero).
Actually, I even think it could support regular SPI transfers, all we'd have to do is use READ/WRITE instructions to do the transfers.
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
I've discussed it privately with Cyrille before I sent the spi-mem extension proposal, and he seemed to agree with the approach. I don't know what his opinion is now that the RFC has been posted, but if he hasn't changed his mind, that means Linux implementation is going in the opposite direction. So it's probably worth reconsidering this move.
Cyrille, could you clarify your opinion? I'm also waiting your feedback on the RFC to send a v2 addressing the comments I had from other people.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
As per this patch-set, fsl_qspi is looks to be getting proposed in driver/mtd/spi-nor/ folder.
fsl_qspi is supporting both flash and fpga. We are not sure about its location.
There is an on-going effort for similar type of requirement in Linux by Boris Brezillon . It is dealing with controllers supporting NORs, NANDs, SRAMs etc. http://patchwork.ozlabs.org/project/linux-mtd/list/?series=27088
Borris has also ported fsl_qspi in driver/spi to use this new framework. It is still not completely tested. https://github.com/bbrezillon/linux/commit/43cc45764b975bfbb191de3f6a37e073d...
Will you also follow similar approach as being done in http://patchwork.ozlabs.org/project/linux-mtd/list/?series=27088 for longer term?
That would be great if the code base could converge.
Please help us with your strategy towards qspi framework. We are waiting for your direction before creating/sending patches of qspi driver & framework in upstream.
--pk

On Wed, Feb 28, 2018 at 3:12 PM, Prabhakar Kushwaha prabhakar.kushwaha@nxp.com wrote:
[Resending on correct patch of the patch-set]
Dear Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Thursday, December 28, 2017 11:42 AM To: u-boot@lists.denx.de Cc: Tom Rini trini@konsulko.com Subject: [U-Boot] [PATCH v10 03/27] mtd: add SPI-NOR core support
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. The idea is taken from Linux spi-nor framework.
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
As per this patch-set, fsl_qspi is looks to be getting proposed in driver/mtd/spi-nor/ folder.
fsl_qspi is supporting both flash and fpga. We are not sure about its location.
There is an on-going effort for similar type of requirement in Linux by Boris Brezillon . It is dealing with controllers supporting NORs, NANDs, SRAMs etc. http://patchwork.ozlabs.org/project/linux-mtd/list/?series=27088
Borris has also ported fsl_qspi in driver/spi to use this new framework. It is still not completely tested. https://github.com/bbrezillon/linux/commit/43cc45764b975bfbb191de3f6a37e073d...
These look going in reverse direction, since drivers/mtd/spi-nor/fsl_qspi was meant for SPI-NOR flash(from initial version SPI-NOR framework patches) and now it going in driver/spi.. look like it need more discussion with respective people who involved on these.
Will you also follow similar approach as being done in http://patchwork.ozlabs.org/project/linux-mtd/list/?series=27088 for longer term?
For Now, We are planning to update fsl_qspi driver to support dynamic LUT. Similar patch is in progress in Linux http://patchwork.ozlabs.org/project/linux-mtd/list/?series=26084 Now we are confused, should be port fsl_qspi in driver/mtd/spi-nor and then update driver Or We update driver/spi/fsl_qspi for dynamic LUTs. We may need to modify existing framework to get all required info for dynamic LUT.
As of now, I would like to write it in driver/mtd/spi-nor assuming all LUT or fsl related changes donesn't harm core areas so-that moving back to drivers/spi might be easy if Linux does.
Also we want some change in framework like support of
- 4 byte address and SFDP : http://patchwork.ozlabs.org/project/uboot/list/?series=19621&state=*
- SMPT : in discussion in Linux http://patchwork.ozlabs.org/patch/869718/
Which code base should we use? u-boot-spi.git branch mtd-spinor-working or u-boot.git master branch
Pls use this https://github.com/openedev/u-boot-spi/commits/master

sst write operations can be changed based on the mode set from the controller with byte program or word program, so sync the similar code from legacy spi_flash.c and update accordingly with mtd operations.
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/spi-nor.c | 115 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 09fb8ca..cfd21fb 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -316,6 +316,113 @@ int spi_nor_mread(struct udevice *dev, loff_t from, size_t len, return ret; }
+#ifdef CONFIG_SPI_NOR_SST +static int sst_byte_write(struct spi_nor *nor, u32 addr, const void *buf, + size_t *retlen) +{ + const struct spi_nor_ops *ops = spi_nor_get_ops(nor->dev); + int ret; + + ret = write_enable(nor->dev); + if (ret) + return ret; + + nor->program_opcode = SNOR_OP_BP; + + ret = ops->write(nor->dev, addr, 1, buf); + if (ret) + return ret; + + *retlen += 1; + + return spi_nor_wait_till_ready(nor->dev, SNOR_READY_WAIT_PROG); +} + +static int sst_mwrite_wp(struct udevice *dev, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_info *mtd = mtd_get_info(dev); + int devnum = mtd->devnum; + const struct spi_nor_ops *ops; + struct spi_nor *nor; + size_t actual; + int ret; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + ops = spi_nor_get_ops(nor->dev); + + /* If the data is not word aligned, write out leading single byte */ + actual = to % 2; + if (actual) { + ret = sst_byte_write(nor, to, buf, retlen); + if (ret) + goto done; + } + to += actual; + + ret = write_enable(nor->dev); + if (ret) + goto done; + + for (; actual < len - 1; actual += 2) { + nor->program_opcode = SNOR_OP_AAI_WP; + + ret = ops->write(nor->dev, to, 2, buf + actual); + if (ret) { + debug("spi-nor: sst word program failed\n"); + break; + } + + ret = spi_nor_wait_till_ready(nor->dev, SNOR_READY_WAIT_PROG); + if (ret) + break; + + to += 2; + *retlen += 2; + } + + if (!ret) + ret = write_disable(nor->dev); + + /* If there is a single trailing byte, write it out */ + if (!ret && actual != len) + ret = sst_byte_write(nor, to, buf + actual, retlen); + + done: + return ret; +} + +static int sst_mwrite_bp(struct udevice *dev, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_info *mtd = mtd_get_info(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + size_t actual; + int ret; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + + for (actual = 0; actual < len; actual++) { + ret = sst_byte_write(nor, to, buf + actual, retlen); + if (ret) { + debug("spi-nor: sst byte program failed\n"); + break; + } + to++; + } + + if (!ret) + ret = write_disable(nor->dev); + + return ret; +} +#endif + #ifdef CONFIG_SPI_NOR_MACRONIX static int macronix_quad_enable(struct udevice *dev) { @@ -482,6 +589,14 @@ int spi_nor_scan(struct spi_nor *nor) nor->flags |= SNOR_F_SST_WRITE;
ops->write = spi_nor_mwrite; +#if defined(CONFIG_SPI_NOR_SST) + if (nor->flags & SNOR_F_SST_WRITE) { + if (nor->mode & SNOR_WRITE_1_1_BYTE) + ops->write = sst_mwrite_bp; + else + ops->write = sst_mwrite_wp; + } +#endif
/* compute the flash size */ nor->page_size = info->page_size;

sync the lock operations from legacy spi_flash.c and update them according to current spi-nor framework.
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/spi-nor.c | 218 ++++++++++++++++++++++++++++++++++++++++++ include/mtd.h | 3 + 2 files changed, 221 insertions(+)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index cfd21fb..8bf9e67 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -186,6 +186,7 @@ static const struct spi_nor_info *spi_nor_id(struct udevice *dev) int spi_nor_merase(struct udevice *dev, struct erase_info *instr) { struct mtd_info *mtd = dev_get_uclass_platdata(dev); + const struct mtd_ops *mops = mtd_get_ops(mtd->dev); int devnum = mtd->devnum; struct spi_nor *nor; const struct spi_nor_ops *ops; @@ -205,6 +206,14 @@ int spi_nor_merase(struct udevice *dev, struct erase_info *instr) addr = instr->addr; len = instr->len;
+ if (mops->is_locked) { + ret = mops->is_locked(mtd->dev, addr, len); + if (ret > 0) { + printf("spi-nor: offset 0x%x is locked, cannot be erased\n", addr); + return -EINVAL; + } + } + while (len) { erase_addr = addr;
@@ -238,6 +247,7 @@ static int spi_nor_mwrite(struct udevice *dev, loff_t to, size_t len, size_t *retlen, const u_char *buf) { struct mtd_info *mtd = dev_get_uclass_platdata(dev); + const struct mtd_ops *mops = mtd_get_ops(mtd->dev); int devnum = mtd->devnum; struct spi_nor *nor; const struct spi_nor_ops *ops; @@ -253,6 +263,14 @@ static int spi_nor_mwrite(struct udevice *dev, loff_t to, size_t len,
page_size = mtd->writebufsize;
+ if (mops->is_locked) { + ret = mops->is_locked(mtd->dev, to, len); + if (ret > 0) { + printf("spi-nor: offset 0x%llx is locked, cannot be written\n", to); + return -EINVAL; + } + } + for (actual = 0; actual < len; actual += chunk_len) { addr = to;
@@ -423,6 +441,197 @@ static int sst_mwrite_bp(struct udevice *dev, loff_t to, size_t len, } #endif
+#if defined(CONFIG_SPI_NOR_STMICRO) || defined(CONFIG_SPI_NOR_SST) +static void stm_get_locked_range(struct mtd_info *mtd, u8 sr, loff_t *ofs, + u64 *len) +{ + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + int shift = ffs(mask) - 1; + int pow; + + if (!(sr & mask)) { + /* No protection */ + *ofs = 0; + *len = 0; + } else { + pow = ((sr & mask) ^ mask) >> shift; + *len = mtd->size >> pow; + *ofs = mtd->size - *len; + } +} + +/* Return 1 if the entire region is locked, 0 otherwise */ +static int stm_is_locked_sr(struct mtd_info *mtd, loff_t ofs, u64 len, + u8 sr) +{ + loff_t lock_offs; + u64 lock_len; + + stm_get_locked_range(mtd, sr, &lock_offs, &lock_len); + + return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); +} + +/* + * Check if a region of the flash is (completely) locked. See stm_lock() for + * more info. + * + * Returns 1 if entire region is locked, 0 if any portion is unlocked, and + * negative on errors. + */ +static int stm_is_locked(struct udevice *dev, loff_t ofs, uint64_t len) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + int status; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + + status = read_sr(nor->dev); + if (status < 0) + return status; + + return stm_is_locked_sr(mtd, ofs, len, status); +} + +/* + * Lock a region of the flash. Compatible with ST Micro and similar flash. + * Supports only the block protection bits BP{0,1,2} in the status register + * (SR). Does not support these features found in newer SR bitfields: + * - TB: top/bottom protect - only handle TB=0 (top protect) + * - SEC: sector/block protect - only handle SEC=0 (block protect) + * - CMP: complement protect - only support CMP=0 (range is not complemented) + * + * Sample table portion for 8MB flash (Winbond w25q64fw): + * + * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion + * -------------------------------------------------------------------------- + * X | X | 0 | 0 | 0 | NONE | NONE + * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64 + * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32 + * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16 + * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8 + * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 + * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 + * X | X | 1 | 1 | 1 | 8 MB | ALL + * + * Returns negative on errors, 0 on success. + */ +static int stm_lock(struct udevice *dev, loff_t ofs, uint64_t len) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + u8 status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + + status_old = read_sr(nor->dev); + if (status_old < 0) + return status_old; + + /* SPI NOR always locks to the end */ + if (ofs + len != mtd->size) { + /* Does combined region extend to end? */ + if (!stm_is_locked_sr(mtd, ofs + len, mtd->size - ofs - len, + status_old)) + return -EINVAL; + len = mtd->size - ofs; + } + + /* + * Need smallest pow such that: + * + * 1 / (2^pow) <= (len / size) + * + * so (assuming power-of-2 size) we do: + * + * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) + */ + pow = ilog2(mtd->size) - ilog2(len); + val = mask - (pow << shift); + if (val & ~mask) + return -EINVAL; + + /* Don't "lock" with no region! */ + if (!(val & mask)) + return -EINVAL; + + status_new = (status_old & ~mask) | val; + + /* Only modify protection if it will not unlock other areas */ + if ((status_new & mask) <= (status_old & mask)) + return -EINVAL; + + write_sr(nor->dev, status_new); + + return 0; +} + +/* + * Unlock a region of the flash. See stm_lock() for more info + * + * Returns negative on errors, 0 on success. + */ +static int stm_unlock(struct udevice *dev, loff_t ofs, uint64_t len) +{ + struct mtd_info *mtd = dev_get_uclass_platdata(dev); + int devnum = mtd->devnum; + struct spi_nor *nor; + uint8_t status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; + + nor = find_spi_nor_device(devnum); + if (!nor) + return -ENODEV; + + status_old = read_sr(nor->dev); + if (status_old < 0) + return status_old; + + /* Cannot unlock; would unlock larger region than requested */ + if (stm_is_locked_sr(mtd, ofs - mtd->erasesize, mtd->erasesize, + status_old)) + return -EINVAL; + /* + * Need largest pow such that: + * + * 1 / (2^pow) >= (len / size) + * + * so (assuming power-of-2 size) we do: + * + * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) + */ + pow = ilog2(mtd->size) - order_base_2(mtd->size - (ofs + len)); + if (ofs + len == mtd->size) { + val = 0; /* fully unlocked */ + } else { + val = mask - (pow << shift); + /* Some power-of-two sizes are not supported */ + if (val & ~mask) + return -EINVAL; + } + + status_new = (status_old & ~mask) | val; + + /* Only modify protection if it will not lock other areas */ + if ((status_new & mask) >= (status_old & mask)) + return -EINVAL; + + write_sr(nor->dev, status_new); + + return 0; +} +#endif + #ifdef CONFIG_SPI_NOR_MACRONIX static int macronix_quad_enable(struct udevice *dev) { @@ -598,6 +807,15 @@ int spi_nor_scan(struct spi_nor *nor) } #endif
+#if defined(CONFIG_SPI_NOR_STMICRO) || defined(CONFIG_SPI_NOR_SST) + /* NOR protection support for STmicro/Micron chips and similar */ + if (JEDEC_MFR(info) == SNOR_MFR_MICRON || + JEDEC_MFR(info) == SNOR_MFR_SST) { + ops->lock = stm_lock; + ops->unlock = stm_unlock; + ops->is_locked = stm_is_locked; + } +#endif /* compute the flash size */ nor->page_size = info->page_size; /* diff --git a/include/mtd.h b/include/mtd.h index 273b3a6..acb2256 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -26,6 +26,9 @@ struct mtd_ops { size_t *retlen, u_char *buf); int (*write)(struct udevice *dev, loff_t to, size_t len, size_t *retlen, const u_char *buf); + int (*lock) (struct udevice *dev, loff_t ofs, uint64_t len); + int (*unlock) (struct udevice *dev, loff_t ofs, uint64_t len); + int (*is_locked) (struct udevice *dev, loff_t ofs, uint64_t len); };
/* Access the serial operations for a device */

Added CONFIG_MTD_SPI_NOR kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/Kconfig | 2 ++ drivers/mtd/spi-nor/Kconfig | 14 ++++++++++++++ drivers/mtd/spi-nor/Makefile | 2 ++ 3 files changed, 18 insertions(+) create mode 100644 drivers/mtd/spi-nor/Kconfig
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 1957980..1d5a248 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -46,4 +46,6 @@ source "drivers/mtd/nand/Kconfig"
source "drivers/mtd/spi/Kconfig"
+source "drivers/mtd/spi-nor/Kconfig" + source "drivers/mtd/ubi/Kconfig" diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig new file mode 100644 index 0000000..130b0a4 --- /dev/null +++ b/drivers/mtd/spi-nor/Kconfig @@ -0,0 +1,14 @@ +menuconfig MTD_SPI_NOR + tristate "SPI-NOR device support" + depends on MTD + help + This is the core SPI NOR framework which can be used to interact SPI-NOR + to SPI driver interface layer and the SPI-NOR controller driver. + + Unlike normal/generic spi controllers, they are few controllers which are + exclusively used to connect SPI-NOR devices, called SPI-NOR controllers. + So technically these controllers shouldn't reside at drivers/spi as these + may effect the generic SPI bus functionalities, so this SPI-NOR core acts + as a common core framework between the generic SPI controller drivers vs + SPI-NOR controller drivers for SPI-NOR device access. Note that from SPI-NOR + core to SPI drivers there should be an interface layer. diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index c1212a8..661f2f6 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -4,4 +4,6 @@ # SPDX-License-Identifier: GPL-2.0+
## spi-nor core +ifdef CONFIG_MTD_SPI_NOR obj-y += spi-nor.o spi-nor-uclass.o spi-nor-ids.o +endif

Added CONFIG_MTD_SPI_NOR_USE_4K_SECTORS kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 130b0a4..40cd5ba 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -12,3 +12,21 @@ menuconfig MTD_SPI_NOR as a common core framework between the generic SPI controller drivers vs SPI-NOR controller drivers for SPI-NOR device access. Note that from SPI-NOR core to SPI drivers there should be an interface layer. + +if MTD_SPI_NOR + +config MTD_SPI_NOR_USE_4K_SECTORS + bool "Use small 4096 B erase sectors" + default y + help + Many flash memories support erasing small (4096 B) sectors. Depending + on the usage this feature may provide performance gain in comparison + to erasing whole blocks (32/64 KiB). + Changing a small part of the flash's contents is usually faster with + small sectors. On the other hand erasing should be faster when using + 64 KiB block instead of 16 × 4 KiB sectors. + + Please note that some tools/drivers/filesystems may not work with + 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum). + +endif # MTD_SPI_NOR

Added CONFIG_SPI_NOR_MISC kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 40cd5ba..348709b 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -29,4 +29,10 @@ config MTD_SPI_NOR_USE_4K_SECTORS Please note that some tools/drivers/filesystems may not work with 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
+config SPI_NOR_MISC + bool "Miscellaneous SPI NOR flash's support" + help + Add SPI-NOR support for various flash chips like Atmel, EON, + GigaDevice, and ISSI. + endif # MTD_SPI_NOR

Added CONFIG_SPI_NOR_MACRONIX kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 348709b..c0ca14b 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -35,4 +35,9 @@ config SPI_NOR_MISC Add SPI-NOR support for various flash chips like Atmel, EON, GigaDevice, and ISSI.
+config SPI_NOR_MACRONIX + bool "Macronix SPI NOR flash support" + help + Add support for various Macronix SPI flash chips (MX25Lxxx) + endif # MTD_SPI_NOR

Added CONFIG_SPI_NOR_SPANSION kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index c0ca14b..d4303db 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -40,4 +40,9 @@ config SPI_NOR_MACRONIX help Add support for various Macronix SPI flash chips (MX25Lxxx)
+config SPI_NOR_SPANSION + bool "Spansion SPI NOR flash support" + help + Add support for various Spansion SPI flash chips (S25FLxxx) + endif # MTD_SPI_NOR

Added CONFIG_SPI_NOR_STMICRO kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index d4303db..8ed4891 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -45,4 +45,9 @@ config SPI_NOR_SPANSION help Add support for various Spansion SPI flash chips (S25FLxxx)
+config SPI_NOR_STMICRO + bool "STMicro SPI NOR flash support" + help + Add support for various STMicro SPI flash chips (M25Pxxx and N25Qxxx) + endif # MTD_SPI_NOR

Added CONFIG_SPI_NOR_SST kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 8ed4891..edcc47e 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -50,4 +50,9 @@ config SPI_NOR_STMICRO help Add support for various STMicro SPI flash chips (M25Pxxx and N25Qxxx)
+config SPI_NOR_SST + bool "SST SPI NOR flash support" + help + Add support for various SST SPI flash chips (SST25xxx) + endif # MTD_SPI_NOR

Added CONFIG_SPI_NOR_WINBOND kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index edcc47e..3ad2b16 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -55,4 +55,9 @@ config SPI_NOR_SST help Add support for various SST SPI flash chips (SST25xxx)
+config SPI_NOR_WINBOND + bool "Winbond SPI NOR flash support" + help + Add support for various Winbond SPI flash chips (W25xxx) + endif # MTD_SPI_NOR

Now, mtd uclass can hold platdata for underlying interface uclass drivers so use platdata_auto_alloc and access via dev->uclass_platdata.
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/mtd-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c index c94afe9..d9ecfea 100644 --- a/drivers/mtd/mtd-uclass.c +++ b/drivers/mtd/mtd-uclass.c @@ -199,5 +199,5 @@ int mtd_create_devicef(struct udevice *parent, const char *drv_name, UCLASS_DRIVER(mtd) = { .id = UCLASS_MTD, .name = "mtd", - .per_device_auto_alloc_size = sizeof(struct mtd_info), + .per_device_platdata_auto_alloc_size = sizeof(struct mtd_info), };

Add support for SPI synchronous write followed by read, this is common interface call from spi-nor to spi drivers.
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/spi/spi-uclass.c | 24 ++++++++++++++++++++++++ include/spi.h | 20 ++++++++++++++++++++ 2 files changed, 44 insertions(+)
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index e06a603..b86c15b 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -107,6 +107,30 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return dm_spi_xfer(slave->dev, bitlen, dout, din, flags); }
+int spi_write_then_read(struct spi_slave *slave, const u8 *opcode, + size_t n_opcode, const u8 *txbuf, u8 *rxbuf, + size_t n_buf) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (n_buf == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(slave, n_opcode * 8, opcode, NULL, flags); + if (ret) { + debug("spi: failed to send command (%zu bytes): %d\n", + n_opcode, ret); + } else if (n_buf != 0) { + ret = spi_xfer(slave, n_buf * 8, txbuf, rxbuf, SPI_XFER_END); + if (ret) + debug("spi: failed to transfer %zu bytes of data: %d\n", + n_buf, ret); + } + + return ret; +} + #if !CONFIG_IS_ENABLED(OF_PLATDATA) static int spi_child_post_bind(struct udevice *dev) { diff --git a/include/spi.h b/include/spi.h index 8c4b882..c67ca23 100644 --- a/include/spi.h +++ b/include/spi.h @@ -252,6 +252,26 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen); int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags);
+/** + * spi_write_then_read - SPI synchronous write followed by read + * + * This performs a half duplex transaction in which the first transaction + * is to send the opcode and if the length of buf is non-zero then it start + * the second transaction as tx or rx based on the need from respective slave. + * + * @slave: slave device with which opcode/data will be exchanged + * @opcode: opcode used for specific transfer + * @n_opcode: size of opcode, in bytes + * @txbuf: buffer into which data to be written + * @rxbuf: buffer into which data will be read + * @n_buf: size of buf (whether it's [tx|rx]buf), in bytes + * + * Returns: 0 on success, not 0 on failure + */ +int spi_write_then_read(struct spi_slave *slave, const u8 *opcode, + size_t n_opcode, const u8 *txbuf, u8 *rxbuf, + size_t n_buf); + /* Copy memory mapped data */ void spi_flash_copy_mmap(void *data, void *offset, size_t len);

This is MTD SPI-NOR driver for ST M25Pxx (and similar) serial flash chips which is written as MTD_UCLASS.
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Makefile | 3 + drivers/mtd/spi-nor/m25p80.c | 250 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 drivers/mtd/spi-nor/m25p80.c
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 661f2f6..d578be0 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -7,3 +7,6 @@ ifdef CONFIG_MTD_SPI_NOR obj-y += spi-nor.o spi-nor-uclass.o spi-nor-ids.o endif + +## spi-nor to spi interface driver +obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c new file mode 100644 index 0000000..5465921 --- /dev/null +++ b/drivers/mtd/spi-nor/m25p80.c @@ -0,0 +1,250 @@ +/* + * MTD SPI-NOR driver for ST M25Pxx (and similar) serial flash chips + * + * Copyright (C) 2016 Jagan Teki jagan@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dma.h> +#include <errno.h> +#include <mtd.h> +#include <spi.h> + +#include <dm/device-internal.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/spi-nor.h> + +#define MAX_CMD_SIZE 6 + +struct m25p_platdata { + struct spi_nor spi_nor; +}; + +struct m25p_priv { + struct spi_slave *spi; + u8 command[MAX_CMD_SIZE]; +}; + +static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) +{ + /* opcode is in cmd[0] */ + cmd[1] = addr >> (nor->addr_width * 8 - 8); + cmd[2] = addr >> (nor->addr_width * 8 - 16); + cmd[3] = addr >> (nor->addr_width * 8 - 24); +} + +static int m25p_cmdsz(struct spi_nor *nor) +{ + return 1 + nor->addr_width; +} + +static int m25p_read_reg(struct udevice *dev, u8 opcode, u8 *val, int len) +{ + struct m25p_priv *priv = dev_get_priv(dev); + struct spi_slave *spi = priv->spi; + int ret; + + ret = spi_claim_bus(spi); + if (ret) { + debug("m25p: failed to claim SPI bus: %d\n", ret); + return ret; + } + + ret = spi_write_then_read(spi, &opcode, 1, NULL, val, len); + if (ret < 0) { + debug("m25p: error %d reading register %x\n", ret, opcode); + return ret; + } + + spi_release_bus(spi); + return ret; +} + +static int m25p_write_reg(struct udevice *dev, u8 opcode, u8 *buf, int len) +{ + struct m25p_priv *priv = dev_get_priv(dev); + struct spi_slave *spi = priv->spi; + int ret; + + ret = spi_claim_bus(spi); + if (ret) { + debug("m25p: failed to claim SPI bus: %d\n", ret); + return ret; + } + + ret = spi_write_then_read(spi, &opcode, 1, buf, NULL, len); + if (ret < 0) { + debug("m25p: error %d writing register %x\n", ret, opcode); + return ret; + } + + spi_release_bus(spi); + return ret; +} + +/* + * TODO: remove the weak after all the other spi_flash_copy_mmap + * implementations removed from drivers + */ +void __weak flash_copy_mmap(void *data, void *offset, size_t len) +{ +#ifdef CONFIG_DMA + if (!dma_memcpy(data, offset, len)) + return; +#endif + memcpy(data, offset, len); +} + +static int m25p_read(struct udevice *dev, loff_t from, size_t len, + u_char *buf) +{ + struct m25p_priv *priv = dev_get_priv(dev); + struct spi_slave *spi = priv->spi; + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + unsigned int dummy; + int ret; + + ret = spi_claim_bus(spi); + if (ret) { + debug("m25p: failed to claim SPI bus: %d\n", ret); + return ret; + } + + if (nor->memory_map) { + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP); + flash_copy_mmap(buf, nor->memory_map + from, len); + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP_END); + spi_release_bus(spi); + return 0; + } + + /* convert the dummy cycles to the number of bytes */ + dummy = nor->read_dummy; + dummy /= 8; + + priv->command[0] = nor->read_opcode; + m25p_addr2cmd(nor, from, priv->command); + + debug("m25p: (read) %2x %2x %2x %2x %2x (%llx)\n", + priv->command[0], priv->command[1], priv->command[2], + priv->command[3], priv->command[4], from); + ret = spi_write_then_read(spi, priv->command, m25p_cmdsz(nor) + dummy, + NULL, buf, len); + if (ret < 0) { + debug("m25p: error %d reading %x\n", ret, priv->command[0]); + return ret; + } + + spi_release_bus(spi); + return ret; +} + +static int m25p_write(struct udevice *dev, loff_t to, size_t len, + const u_char *buf) +{ + struct m25p_priv *priv = dev_get_priv(dev); + struct spi_slave *spi = priv->spi; + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + int cmd_sz, ret; + + ret = spi_claim_bus(spi); + if (ret) { + debug("m25p: failed to claim SPI bus: %d\n", ret); + return ret; + } + + cmd_sz = m25p_cmdsz(nor); + if ((nor->program_opcode == SNOR_OP_AAI_WP) && (buf != NULL)) + cmd_sz = 1; + + priv->command[0] = nor->program_opcode; + if (buf == NULL) + priv->command[0] = nor->erase_opcode; + m25p_addr2cmd(nor, to, priv->command); + + debug("m25p: (%s) %2x %2x %2x %2x %2x (%llx)\n", + (buf == NULL) ? "erase" : "write", priv->command[0], + priv->command[1], priv->command[2], priv->command[3], + priv->command[4], to); + ret = spi_write_then_read(spi, priv->command, cmd_sz, buf, NULL, len); + if (ret < 0) { + debug("m25p: error %d writing %x\n", ret, priv->command[0]); + return ret; + } + + spi_release_bus(spi); + return ret; +} + +const struct spi_nor_ops m25p_ops = { + .read = m25p_read, + .write = m25p_write, + .read_reg = m25p_read_reg, + .write_reg = m25p_write_reg, +}; + +static int m25p_probe(struct udevice *dev) +{ + struct m25p_platdata *plat = dev_get_platdata(dev); + struct m25p_priv *priv = dev_get_priv(dev); + struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct spi_slave *spi = dev_get_parent_priv(dev); + int ret; + + ret = spi_claim_bus(spi); + if (ret) { + debug("m25p: failed to claim SPI bus: %d\n", ret); + return ret; + } + + if (spi->mode & SPI_RX_SLOW) + plat->spi_nor.mode = SNOR_READ; + else if (spi->mode & SPI_RX_DUAL) + plat->spi_nor.mode = SNOR_READ_1_1_2; + else if (spi->mode & SPI_RX_QUAD) + plat->spi_nor.mode = SNOR_READ_1_1_4; + + if (spi->mode & SPI_TX_BYTE) + plat->spi_nor.mode |= SNOR_WRITE_1_1_BYTE; + else if (spi->mode & SPI_TX_QUAD) + plat->spi_nor.mode |= SNOR_WRITE_1_1_4; + + plat->spi_nor.memory_map = spi->memory_map; + plat->spi_nor.max_write_size = spi->max_write_size; + + priv->spi = spi; + upriv->spi_nor = &plat->spi_nor; + + return 0; +} + +static int m25p_bind(struct udevice *dev) +{ + struct m25p_platdata *plat = dev_get_platdata(dev); + + return spi_nor_bind(dev, &plat->spi_nor); +} + +static const struct udevice_id m25p_ids[] = { + /* + * Generic compatibility for SPI NOR that can be identified by the + * JEDEC READ ID opcode (0x9F). Use this, if possible. + */ + { .compatible = "jedec,spi-nor" }, + { } +}; + +U_BOOT_DRIVER(m25p) = { + .name = "m25p", + .id = UCLASS_SPI_NOR, + .of_match = m25p_ids, + .ops = &m25p_ops, + .bind = m25p_bind, + .probe = m25p_probe, + .priv_auto_alloc_size = sizeof(struct m25p_priv), + .platdata_auto_alloc_size = sizeof(struct m25p_platdata), +};

Add CONFIG_MTD_M25P80 kconfig entry
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 3ad2b16..64d5553 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -15,6 +15,23 @@ menuconfig MTD_SPI_NOR
if MTD_SPI_NOR
+config MTD_M25P80 + tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" + depends on DM_SPI + help + This enables access to most modern SPI flash chips, used for + program and data storage. Series supported include Atmel AT26DF, + Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips + are supported as well. See the driver source for the current list, + or to add other chips. + + Note that the original DataFlash chips (AT45 series, not AT26DF), + need an entirely different driver. + + Set up your spi devices with the right board-specific platform data, + if you want to specify device partitioning or to use a device which + doesn't support the JEDEC ID instruction. + config MTD_SPI_NOR_USE_4K_SECTORS bool "Use small 4096 B erase sectors" default y

Zynq qspinor controller is works similar way as generic spi controller with additional features that make this controller work more specific to flash chips as salve devices.
Why, zynq qspi written as spi-nor controller driver.
(1) dual flash: This describes two/dual memories are connected with a single chip select line from a controller like dual stack and dual parallel connections see doc/SPI/README.dual-flash for more details.
Adding this support to spi-nor core looks quite managable and other generic code might effect and more over this is controller specific.
Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Makefile | 3 + drivers/mtd/spi-nor/zynq_qspinor.c | 622 +++++++++++++++++++++++++++++++++++++ 2 files changed, 625 insertions(+) create mode 100644 drivers/mtd/spi-nor/zynq_qspinor.c
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index d578be0..ded3bfa 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -10,3 +10,6 @@ endif
## spi-nor to spi interface driver obj-$(CONFIG_MTD_M25P80) += m25p80.o + +## spi-nor drivers +obj-$(CONFIG_MTD_ZYNQ_QSPI) += zynq_qspinor.o diff --git a/drivers/mtd/spi-nor/zynq_qspinor.c b/drivers/mtd/spi-nor/zynq_qspinor.c new file mode 100644 index 0000000..278168b --- /dev/null +++ b/drivers/mtd/spi-nor/zynq_qspinor.c @@ -0,0 +1,622 @@ +/* + * (C) Copyright 2016 Jagan Teki jagan@openedev.com + * + * Xilinx Zynq Quad-SPI(QSPI) NOR controller driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <mtd.h> +#include <asm/io.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/spi-nor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* zynq qspi register bit masks ZYNQ_QSPI_<REG>_<BIT>_MASK */ +#define ZYNQ_QSPI_CR_IFMODE_MASK BIT(31) /* Flash intrface mode*/ +#define ZYNQ_QSPI_CR_MSA_MASK BIT(15) /* Manual start enb */ +#define ZYNQ_QSPI_CR_MCS_MASK BIT(14) /* Manual chip select */ +#define ZYNQ_QSPI_CR_PCS_MASK BIT(10) /* Peri chip select */ +#define ZYNQ_QSPI_CR_FW_MASK GENMASK(7, 6) /* FIFO width */ +#define ZYNQ_QSPI_CR_SS_MASK GENMASK(13, 10) /* Slave Select */ +#define ZYNQ_QSPI_CR_BAUD_MASK GENMASK(5, 3) /* Baud rate div */ +#define ZYNQ_QSPI_CR_CPHA_MASK BIT(2) /* Clock phase */ +#define ZYNQ_QSPI_CR_CPOL_MASK BIT(1) /* Clock polarity */ +#define ZYNQ_QSPI_CR_MSTREN_MASK BIT(0) /* Mode select */ +#define ZYNQ_QSPI_IXR_RXNEMPTY_MASK BIT(4) /* RX_FIFO_not_empty */ +#define ZYNQ_QSPI_IXR_TXOW_MASK BIT(2) /* TX_FIFO_not_full */ +#define ZYNQ_QSPI_IXR_ALL_MASK GENMASK(6, 0) /* All IXR bits */ +#define ZYNQ_QSPI_ENR_SPI_EN_MASK BIT(0) /* SPI Enable */ +#define ZYNQ_QSPI_LQSPICFG_LQMODE_MASK BIT(31) /* Linear QSPI Mode */ + +/* zynq qspi Transmit Data Register */ +#define ZYNQ_QSPI_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst */ +#define ZYNQ_QSPI_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst */ +#define ZYNQ_QSPI_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst */ +#define ZYNQ_QSPI_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst */ + +#define ZYNQ_QSPI_XFER_BEGIN BIT(0) +#define ZYNQ_QSPI_XFER_END BIT(1) +#define ZYNQ_QSPI_TXFIFO_THRESHOLD 1 /* Tx FIFO threshold level*/ +#define ZYNQ_QSPI_RXFIFO_THRESHOLD 32 /* Rx FIFO threshold level */ + +#define ZYNQ_QSPI_CR_BAUD_MAX 8 /* Baud rate divisor max val */ +#define ZYNQ_QSPI_CR_BAUD_SHIFT 3 /* Baud rate divisor shift */ +#define ZYNQ_QSPI_CR_SS_SHIFT 10 /* Slave select shift */ +#define ZYNQ_QSPI_MAX_CMDSZ 4 /* 1 byte opcode,3 byte addr */ + +#define ZYNQ_QSPI_FIFO_DEPTH 63 +#ifndef CONFIG_SYS_ZYNQ_QSPI_WAIT +#define CONFIG_SYS_ZYNQ_QSPI_WAIT CONFIG_SYS_HZ/100 /* 10 ms */ +#endif + +/* zynq qspi register set */ +struct zynq_qspinor_regs { + u32 cr; /* 0x00 */ + u32 isr; /* 0x04 */ + u32 ier; /* 0x08 */ + u32 idr; /* 0x0C */ + u32 imr; /* 0x10 */ + u32 enr; /* 0x14 */ + u32 dr; /* 0x18 */ + u32 txd0r; /* 0x1C */ + u32 drxr; /* 0x20 */ + u32 sicr; /* 0x24 */ + u32 txftr; /* 0x28 */ + u32 rxftr; /* 0x2C */ + u32 gpior; /* 0x30 */ + u32 reserved0[19]; + u32 txd1r; /* 0x80 */ + u32 txd2r; /* 0x84 */ + u32 txd3r; /* 0x88 */ + u32 reserved1[5]; + u32 lqspicfg; /* 0xA0 */ + u32 lqspists; /* 0xA4 */ +}; + +/* zynq qspi platform data */ +struct zynq_qspinor_platdata { + struct spi_nor spi_nor; +}; + +/* zynq qspi priv */ +struct zynq_qspinor_priv { + struct zynq_qspinor_regs *regs; + u8 cs; + u8 mode; + u8 fifo_depth; + u32 freq; /* required frequency */ + u8 cmd[4]; /* 1 byte opcode + 3-byte address */ + const void *tx_buf; + void *rx_buf; + unsigned len; + int bytes_to_transfer; + int bytes_to_receive; + unsigned int is_inst; + unsigned cs_change:1; +}; + +static void zynq_qspinor_addr(u32 addr, u8 *cmd) +{ + /* opcode is in cmd[0] */ + cmd[1] = addr >> 16; + cmd[2] = addr >> 8; + cmd[3] = addr >> 0; +} + +/* + * zynq_qspinor_read_data - Copy data to RX buffer + * @zqspi: Pointer to the zynq_qspinor structure + * @data: The 32 bit variable where data is stored + * @size: Number of bytes to be copied from data to RX buffer + */ +static void zynq_qspinor_read_data(struct zynq_qspinor_priv *priv, + u32 data, u8 size) +{ + u8 byte3; + + debug("%s: data 0x%04x rx_buf addr: 0x%08x size %d\n", __func__ , + data, (unsigned)(priv->rx_buf), size); + + if (priv->rx_buf) { + switch (size) { + case 1: + *((u8 *)priv->rx_buf) = data; + priv->rx_buf += 1; + break; + case 2: + *((u16 *)priv->rx_buf) = data; + priv->rx_buf += 2; + break; + case 3: + *((u16 *)priv->rx_buf) = data; + priv->rx_buf += 2; + byte3 = (u8)(data >> 16); + *((u8 *)priv->rx_buf) = byte3; + priv->rx_buf += 1; + break; + case 4: + /* Can not assume word aligned buffer */ + memcpy(priv->rx_buf, &data, size); + priv->rx_buf += 4; + break; + default: + /* This will never execute */ + break; + } + } + priv->bytes_to_receive -= size; + if (priv->bytes_to_receive < 0) + priv->bytes_to_receive = 0; +} + +/* + * zynq_qspinor_write_data - Copy data from TX buffer + * @zqspi: Pointer to the zynq_qspinor structure + * @data: Pointer to the 32 bit variable where data is to be copied + * @size: Number of bytes to be copied from TX buffer to data + */ +static void zynq_qspinor_write_data(struct zynq_qspinor_priv *priv, + u32 *data, u8 size) +{ + if (priv->tx_buf) { + switch (size) { + case 1: + *data = *((u8 *)priv->tx_buf); + priv->tx_buf += 1; + *data |= 0xFFFFFF00; + break; + case 2: + *data = *((u16 *)priv->tx_buf); + priv->tx_buf += 2; + *data |= 0xFFFF0000; + break; + case 3: + *data = *((u16 *)priv->tx_buf); + priv->tx_buf += 2; + *data |= (*((u8 *)priv->tx_buf) << 16); + priv->tx_buf += 1; + *data |= 0xFF000000; + break; + case 4: + /* Can not assume word aligned buffer */ + memcpy(data, priv->tx_buf, size); + priv->tx_buf += 4; + break; + default: + /* This will never execute */ + break; + } + } else { + *data = 0; + } + + debug("%s: data 0x%08x tx_buf addr: 0x%08x size %d\n", __func__, + *data, (u32)priv->tx_buf, size); + + priv->bytes_to_transfer -= size; + if (priv->bytes_to_transfer < 0) + priv->bytes_to_transfer = 0; +} + +static void zynq_qspinor_chipselect(struct zynq_qspinor_priv *priv, int is_on) +{ + u32 confr; + struct zynq_qspinor_regs *regs = priv->regs; + + confr = readl(®s->cr); + + if (is_on) { + /* Select the slave */ + confr &= ~ZYNQ_QSPI_CR_SS_MASK; + confr |= (~(1 << priv->cs) << ZYNQ_QSPI_CR_SS_SHIFT) & + ZYNQ_QSPI_CR_SS_MASK; + } else + /* Deselect the slave */ + confr |= ZYNQ_QSPI_CR_SS_MASK; + + writel(confr, ®s->cr); +} + +/* + * zynq_qspinor_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible + * @zqspi: Pointer to the zynq_qspinor structure + */ +static void zynq_qspinor_fill_tx_fifo(struct zynq_qspinor_priv *priv, u32 size) +{ + u32 data = 0; + u32 fifocount = 0; + unsigned len, offset; + struct zynq_qspinor_regs *regs = priv->regs; + static const unsigned offsets[4] = { + ZYNQ_QSPI_TXD_00_00_OFFSET, ZYNQ_QSPI_TXD_00_01_OFFSET, + ZYNQ_QSPI_TXD_00_10_OFFSET, ZYNQ_QSPI_TXD_00_11_OFFSET }; + + while ((fifocount < size) && + (priv->bytes_to_transfer > 0)) { + if (priv->bytes_to_transfer >= 4) { + if (priv->tx_buf) { + memcpy(&data, priv->tx_buf, 4); + priv->tx_buf += 4; + } else { + data = 0; + } + writel(data, ®s->txd0r); + priv->bytes_to_transfer -= 4; + fifocount++; + } else { + /* Write TXD1, TXD2, TXD3 only if TxFIFO is empty. */ + if (!(readl(®s->isr) + & ZYNQ_QSPI_IXR_TXOW_MASK) && + !priv->rx_buf) + return; + len = priv->bytes_to_transfer; + zynq_qspinor_write_data(priv, &data, len); + offset = (priv->rx_buf) ? offsets[0] : offsets[len]; + writel(data, ®s->cr + (offset / 4)); + } + } +} + +/* + * zynq_qspinor_irq_poll - Interrupt service routine of the QSPI controller + * @zqspi: Pointer to the zynq_qspinor structure + * + * This function handles TX empty and Mode Fault interrupts only. + * On TX empty interrupt this function reads the received data from RX FIFO and + * fills the TX FIFO if there is any data remaining to be transferred. + * On Mode Fault interrupt this function indicates that transfer is completed, + * the SPI subsystem will identify the error as the remaining bytes to be + * transferred is non-zero. + * + * returns: 0 for poll timeout + * 1 transfer operation complete + */ +static int zynq_qspinor_irq_poll(struct zynq_qspinor_priv *priv) +{ + struct zynq_qspinor_regs *regs = priv->regs; + u32 rxindex = 0; + u32 rxcount; + u32 status, timeout; + + /* Poll until any of the interrupt status bits are set */ + timeout = get_timer(0); + do { + status = readl(®s->isr); + } while ((status == 0) && + (get_timer(timeout) < CONFIG_SYS_ZYNQ_QSPI_WAIT)); + + if (status == 0) { + printf("zynq_qspinor_irq_poll: Timeout!\n"); + return -ETIMEDOUT; + } + + writel(status, ®s->isr); + + /* Disable all interrupts */ + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->idr); + if ((status & ZYNQ_QSPI_IXR_TXOW_MASK) || + (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)) { + /* + * This bit is set when Tx FIFO has < THRESHOLD entries. We have + * the THRESHOLD value set to 1, so this bit indicates Tx FIFO + * is empty + */ + rxcount = priv->bytes_to_receive - priv->bytes_to_transfer; + rxcount = (rxcount % 4) ? ((rxcount/4)+1) : (rxcount/4); + while ((rxindex < rxcount) && + (rxindex < ZYNQ_QSPI_RXFIFO_THRESHOLD)) { + /* Read out the data from the RX FIFO */ + u32 data; + data = readl(®s->drxr); + + if (priv->bytes_to_receive >= 4) { + if (priv->rx_buf) { + memcpy(priv->rx_buf, &data, 4); + priv->rx_buf += 4; + } + priv->bytes_to_receive -= 4; + } else { + zynq_qspinor_read_data(priv, data, + priv->bytes_to_receive); + } + rxindex++; + } + + if (priv->bytes_to_transfer) { + /* There is more data to send */ + zynq_qspinor_fill_tx_fifo(priv, + ZYNQ_QSPI_RXFIFO_THRESHOLD); + + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->ier); + } else { + /* + * If transfer and receive is completed then only send + * complete signal + */ + if (!priv->bytes_to_receive) { + /* return operation complete */ + writel(ZYNQ_QSPI_IXR_ALL_MASK, + ®s->idr); + return 1; + } + } + } + + return 0; +} + +/* + * zynq_qspinor_start_transfer - Initiates the QSPI transfer + * @qspi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provide information + * about next transfer parameters + * + * This function fills the TX FIFO, starts the QSPI transfer, and waits for the + * transfer to be completed. + * + * returns: Number of bytes transferred in the last transfer + */ +static int zynq_qspinor_start_transfer(struct zynq_qspinor_priv *priv) +{ + u32 data = 0; + struct zynq_qspinor_regs *regs = priv->regs; + + debug("%s: qspi: 0x%08x transfer: 0x%08x len: %d\n", __func__, + (u32)priv, (u32)priv, priv->len); + + priv->bytes_to_transfer = priv->len; + priv->bytes_to_receive = priv->len; + + if (priv->len < 4) + zynq_qspinor_fill_tx_fifo(priv, priv->len); + else + zynq_qspinor_fill_tx_fifo(priv, priv->fifo_depth); + + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->ier); + + /* wait for completion */ + do { + data = zynq_qspinor_irq_poll(priv); + } while (data == 0); + + return (priv->len) - (priv->bytes_to_transfer); +} + +static int zynq_qspinor_transfer(struct zynq_qspinor_priv *priv) +{ + unsigned cs_change = 1; + int status = 0; + + while (1) { + /* Select the chip if required */ + if (cs_change) + zynq_qspinor_chipselect(priv, 1); + + cs_change = priv->cs_change; + + if (!priv->tx_buf && !priv->rx_buf && priv->len) { + status = -1; + break; + } + + /* Request the transfer */ + if (priv->len) { + status = zynq_qspinor_start_transfer(priv); + priv->is_inst = 0; + } + + if (status != priv->len) { + if (status > 0) + status = -EMSGSIZE; + debug("zynq_qspinor_transfer:%d len:%d\n", + status, priv->len); + break; + } + status = 0; + + if (cs_change) + /* Deselect the chip */ + zynq_qspinor_chipselect(priv, 0); + + break; + } + + return 0; +} + +static int zynq_qspinor_xfer(struct zynq_qspinor_priv *priv, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + priv->tx_buf = dout; + priv->rx_buf = din; + priv->len = bitlen / 8; + + /* + * Festering sore. + * Assume that the beginning of a transfer with bits to + * transmit must contain a device command. + */ + if (dout && flags & ZYNQ_QSPI_XFER_BEGIN) + priv->is_inst = 1; + else + priv->is_inst = 0; + + if (flags & ZYNQ_QSPI_XFER_END) + priv->cs_change = 1; + else + priv->cs_change = 0; + + zynq_qspinor_transfer(priv); + + return 0; +} + +static int zynq_qspinor_tx_then_rx(struct zynq_qspinor_priv *priv, + const u8 *opcode, size_t n_opcode, + const u8 *txbuf, u8 *rxbuf, size_t n_buf) +{ + struct zynq_qspinor_regs *regs = priv->regs; + unsigned long flags = ZYNQ_QSPI_XFER_BEGIN; + int ret; + + /* enable spi */ + writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->enr); + + if (n_buf == 0) + flags |= ZYNQ_QSPI_XFER_END; + + ret = zynq_qspinor_xfer(priv, n_opcode * 8, opcode, NULL, flags); + if (ret) { + debug("%s: failed to send command (%zu bytes): %d\n", + __func__, n_opcode, ret); + } else if (n_buf != 0) { + ret = zynq_qspinor_xfer(priv, n_buf * 8, txbuf, rxbuf, + ZYNQ_QSPI_XFER_END); + if (ret) + debug("%s: failed to transfer %zu bytes of data: %d\n", + __func__, n_buf, ret); + } + + /* disable spi */ + writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->enr); + + return ret; +} + +static int zynq_qspinor_read_reg(struct udevice *dev, u8 opcode, + u8 *val, int len) +{ + struct zynq_qspinor_priv *priv = dev_get_priv(dev); + + return zynq_qspinor_tx_then_rx(priv, &opcode, 1, NULL, val, len); +} + +static int zynq_qspinor_write_reg(struct udevice *dev, u8 opcode, + u8 *buf, int len) +{ + struct zynq_qspinor_priv *priv = dev_get_priv(dev); + + return zynq_qspinor_tx_then_rx(priv, &opcode, 1, buf, NULL, len); +} + +static int zynq_qspinor_read(struct udevice *dev, loff_t from, + size_t len, u_char *buf) +{ + struct zynq_qspinor_priv *priv = dev_get_priv(dev); + unsigned int cmd_sz; + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + + priv->cmd[0] = nor->read_opcode; + zynq_qspinor_addr(from, priv->cmd); + cmd_sz = sizeof(priv->cmd) + (nor->read_dummy / 8); + + debug("zynq_qspinor: (read) %2x %2x %2x %2x (%llx)\n", priv->cmd[0], + priv->cmd[1], priv->cmd[2], priv->cmd[3], from); + return zynq_qspinor_tx_then_rx(priv, priv->cmd, cmd_sz, NULL, buf, len); +} + +static int zynq_qspinor_write(struct udevice *dev, loff_t to, + size_t len, const u_char *buf) +{ + struct zynq_qspinor_priv *priv = dev_get_priv(dev); + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + + priv->cmd[0] = nor->program_opcode; + if (buf == NULL) + priv->cmd[0] = nor->erase_opcode; + + zynq_qspinor_addr(to, priv->cmd); + + debug("zynq_qspinor: (%s) %2x %2x %2x %2x (%llx)\n", + (buf == NULL) ? "erase" : "write", priv->cmd[0], priv->cmd[1], + priv->cmd[2], priv->cmd[3], to); + return zynq_qspinor_tx_then_rx(priv, priv->cmd, sizeof(priv->cmd), + buf, NULL, len); +} + +const struct spi_nor_ops zynq_qspinor_ops = { + .read = zynq_qspinor_read, + .write = zynq_qspinor_write, + .read_reg = zynq_qspinor_read_reg, + .write_reg = zynq_qspinor_write_reg, +}; + +static void zynq_qspinor_init_hw(struct zynq_qspinor_priv *priv) +{ + struct zynq_qspinor_regs *regs = priv->regs; + u32 confr; + + /* disable QSPI */ + writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->enr); + + /* disable Interrupts */ + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->idr); + + /* clear the TX and RX threshold reg */ + writel(ZYNQ_QSPI_TXFIFO_THRESHOLD, ®s->txftr); + writel(ZYNQ_QSPI_RXFIFO_THRESHOLD, ®s->rxftr); + + /* clear the RX FIFO */ + while (readl(®s->isr) & ZYNQ_QSPI_IXR_RXNEMPTY_MASK) + readl(®s->drxr); + + /* clear Interrupts */ + writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->isr); + + /* manual slave select and Auto start */ + confr = readl(®s->cr); + confr &= ~ZYNQ_QSPI_CR_MSA_MASK; + confr |= ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK | + ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK | + ZYNQ_QSPI_CR_MSTREN_MASK; + writel(confr, ®s->cr); + + /* enable SPI */ + writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->enr); +} + +static int zynq_qspinor_probe(struct udevice *dev) +{ + struct zynq_qspinor_platdata *plat = dev_get_platdata(dev); + struct zynq_qspinor_priv *priv = dev_get_priv(dev); + struct spi_nor_uclass_priv *upriv = dev_get_uclass_priv(dev); + + priv->regs = (void *)dev_read_addr(dev); + priv->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH; + + /* init the zynq spi hw */ + zynq_qspinor_init_hw(priv); + + upriv->spi_nor = &plat->spi_nor; + + return 0; +} + +static int zynq_qspinor_bind(struct udevice *dev) +{ + struct zynq_qspinor_platdata *plat = dev_get_platdata(dev); + + return spi_nor_bind(dev, &plat->spi_nor); +} + +static const struct udevice_id zynq_qspinor_ids[] = { + { .compatible = "xlnx,zynq-qspinor-1.0" }, + { } +}; + +U_BOOT_DRIVER(zynq_qspinor) = { + .name = "zynq_qspinor", + .id = UCLASS_SPI_NOR, + .of_match = zynq_qspinor_ids, + .ops = &zynq_qspinor_ops, + .bind = zynq_qspinor_bind, + .probe = zynq_qspinor_probe, + .priv_auto_alloc_size = sizeof(struct zynq_qspinor_priv), + .platdata_auto_alloc_size = sizeof(struct zynq_qspinor_platdata), +};

Add CONFIG_MTD_ZYNQ_QSPI kconfig entry
Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 64d5553..4b2a5e8 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -46,6 +46,15 @@ config MTD_SPI_NOR_USE_4K_SECTORS Please note that some tools/drivers/filesystems may not work with 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
+config MTD_ZYNQ_QSPI + bool "Zynq QSPI NOR controller driver" + depends on ARCH_ZYNQ + help + Enable the Zynq Quad-SPI (QSPI) driver. This driver can be + used to access the SPI NOR flash on platforms embedding this + Zynq QSPI IP core. This IP is used to connect the flash in + 4-bit qspi, 8-bit dual stacked and shared 4-bit dual parallel. + config SPI_NOR_MISC bool "Miscellaneous SPI NOR flash's support" help

Add 4-byte address supports, so-that SPI-NOR chips has > 16MiB should accessible.
Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- drivers/mtd/spi-nor/m25p80.c | 1 + drivers/mtd/spi-nor/spi-nor.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 6 +++++- 3 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c index 5465921..7af6f59 100644 --- a/drivers/mtd/spi-nor/m25p80.c +++ b/drivers/mtd/spi-nor/m25p80.c @@ -35,6 +35,7 @@ static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) cmd[1] = addr >> (nor->addr_width * 8 - 8); cmd[2] = addr >> (nor->addr_width * 8 - 16); cmd[3] = addr >> (nor->addr_width * 8 - 24); + cmd[4] = addr >> (nor->addr_width * 8 - 32); }
static int m25p_cmdsz(struct spi_nor *nor) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 8bf9e67..e0085cf 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -632,6 +632,38 @@ static int stm_unlock(struct udevice *dev, loff_t ofs, uint64_t len) } #endif
+/* Enable/disable 4-byte addressing mode. */ +static int set_4byte(struct udevice *dev, const struct spi_nor_info *info, + int enable) +{ + struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev); + const struct spi_nor_ops *ops = spi_nor_get_ops(dev); + int status; + bool need_wren = false; + u8 cmd; + + switch (JEDEC_MFR(info)) { + case SNOR_MFR_MICRON: + /* Some Micron need WREN command; all will accept it */ + need_wren = true; + case SNOR_MFR_MACRONIX: + case SNOR_MFR_WINBOND: + if (need_wren) + write_enable(dev); + + cmd = enable ? SNOR_OP_EN4B : SNOR_OP_EX4B; + status = ops->write_reg(dev, cmd, NULL, 0); + if (need_wren) + write_disable(dev); + + return status; + default: + /* Spansion style */ + nor->cmd_buf[0] = enable << 7; + return ops->write_reg(dev, SNOR_OP_BRWR, nor->cmd_buf, 1); + } +} + #ifdef CONFIG_SPI_NOR_MACRONIX static int macronix_quad_enable(struct udevice *dev) { @@ -873,6 +905,12 @@ int spi_nor_scan(struct spi_nor *nor) }
nor->addr_width = 3; + if (mtd->size > SNOR_16MB_BOUN) { + nor->addr_width = 4; + ret = set_4byte(nor->dev, info, true); + if (ret) + goto err; + }
/* Dummy cycles for read */ switch (nor->read_opcode) { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index e1688e2..fc4a649 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -63,6 +63,10 @@ #define SNOR_OP_BP 0x02 /* Byte program */ #define SNOR_OP_AAI_WP 0xad /* Auto addr increment word program */
+/* Used for Macronix and Winbond flashes. */ +#define SNOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ +#define SNOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ + /* Status Register bits. */ #define SR_WIP BIT(0) /* Write in progress */ #define SR_WEL BIT(1) /* Write enable latch */ @@ -84,7 +88,7 @@ /* Flash timeout values */ #define SNOR_READY_WAIT_PROG (2 * CONFIG_SYS_HZ) #define SNOR_READY_WAIT_ERASE (5 * CONFIG_SYS_HZ) -#define SNOR_MAX_CMD_SIZE 4 +#define SNOR_MAX_CMD_SIZE 6 #define SNOR_16MB_BOUN 0x1000000
/**

Hi Jagan,
Le 28/12/2017 à 07:12, Jagan Teki a écrit :
Add 4-byte address supports, so-that SPI-NOR chips has > 16MiB should accessible.
Signed-off-by: Jagan Teki jagan@amarulasolutions.com
drivers/mtd/spi-nor/m25p80.c | 1 + drivers/mtd/spi-nor/spi-nor.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 6 +++++- 3 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c index 5465921..7af6f59 100644 --- a/drivers/mtd/spi-nor/m25p80.c +++ b/drivers/mtd/spi-nor/m25p80.c @@ -35,6 +35,7 @@ static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) cmd[1] = addr >> (nor->addr_width * 8 - 8); cmd[2] = addr >> (nor->addr_width * 8 - 16); cmd[3] = addr >> (nor->addr_width * 8 - 24);
- cmd[4] = addr >> (nor->addr_width * 8 - 32);
}
static int m25p_cmdsz(struct spi_nor *nor) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 8bf9e67..e0085cf 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -632,6 +632,38 @@ static int stm_unlock(struct udevice *dev, loff_t ofs, uint64_t len) } #endif
+/* Enable/disable 4-byte addressing mode. */ +static int set_4byte(struct udevice *dev, const struct spi_nor_info *info,
int enable)
+{
- struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
- const struct spi_nor_ops *ops = spi_nor_get_ops(dev);
- int status;
- bool need_wren = false;
- u8 cmd;
- switch (JEDEC_MFR(info)) {
- case SNOR_MFR_MICRON:
/* Some Micron need WREN command; all will accept it */
need_wren = true;
- case SNOR_MFR_MACRONIX:
- case SNOR_MFR_WINBOND:
if (need_wren)
write_enable(dev);
cmd = enable ? SNOR_OP_EN4B : SNOR_OP_EX4B;
status = ops->write_reg(dev, cmd, NULL, 0);
if (need_wren)
write_disable(dev);
return status;
- default:
/* Spansion style */
nor->cmd_buf[0] = enable << 7;
return ops->write_reg(dev, SNOR_OP_BRWR, nor->cmd_buf, 1);
- }
+}
#ifdef CONFIG_SPI_NOR_MACRONIX static int macronix_quad_enable(struct udevice *dev) { @@ -873,6 +905,12 @@ int spi_nor_scan(struct spi_nor *nor) }
nor->addr_width = 3;
- if (mtd->size > SNOR_16MB_BOUN) {
nor->addr_width = 4;
ret = set_4byte(nor->dev, info, true);
if (ret)
goto err;
- }
This is a bad idea: you make the SPI NOR memory enter its 4-byte address mode, which is statefull. Then, once in Linux for instance, the memory would still be in its 4-byte address mode but expected to be in its reset state, hence in 3-byte address mode.
At this point, this might not be an issue yet because spi-nor under linux is likely to use the 4-byte address instruction set if available, which is stateless by the way, so whatever the memory is in its 3-byte or in 4-byte address mode, the op codes of the 4-byte address instruction set are always expected to be followed by a 4-byte address. So Linux won't notice that the SPI NOR memory is in its 4-byte mode but not in its 3-byte mode as expected.
However, if a spurious reboot occurs at the CPU side after the SPI NOR memory has entered its 4-byte address mode, either still in u-boot or already in linux or whatever running code, the SPI NOR memory, likely not reset on its side, would still be in its 4-byte address mode, whereas many earlier boot-loaders would expect the memory to be in it's reset state, ie in its 3-byte address mode. Hence those boot-loaders, running before u-boot, would not be able to read data (load u-boot) from the SPI NOR memory: the boot would fail!
Examples of those early boot-loaders are the ROM Codes of many Atmel SAMA5Dx SoCs but I'm pretty sure this issue also applies to other manufacturers based on patches I've seen on the linux-mtd mailing list to add the SPI_NOR_4B_OPCODES flag to some memory parts: https://patchwork.ozlabs.org/patch/750305/
Think about a watchdog resetting the CPU for instance.
TL;DR You should avoid making the SPI NOR memory enter its statefull 4-byte address mode but consider using the stateless 4-byte address instruction set instead, when possible.
Almost all recent SPI NOR memories with sizes > 16MiB support the 4-byte address mode.
Best regards,
Cyrille
/* Dummy cycles for read */ switch (nor->read_opcode) { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index e1688e2..fc4a649 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -63,6 +63,10 @@ #define SNOR_OP_BP 0x02 /* Byte program */ #define SNOR_OP_AAI_WP 0xad /* Auto addr increment word program */
+/* Used for Macronix and Winbond flashes. */ +#define SNOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ +#define SNOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
/* Status Register bits. */ #define SR_WIP BIT(0) /* Write in progress */ #define SR_WEL BIT(1) /* Write enable latch */ @@ -84,7 +88,7 @@ /* Flash timeout values */ #define SNOR_READY_WAIT_PROG (2 * CONFIG_SYS_HZ) #define SNOR_READY_WAIT_ERASE (5 * CONFIG_SYS_HZ) -#define SNOR_MAX_CMD_SIZE 4 +#define SNOR_MAX_CMD_SIZE 6 #define SNOR_16MB_BOUN 0x1000000
/**

Le 28/12/2017 à 09:06, Cyrille Pitchen a écrit :
Hi Jagan,
Le 28/12/2017 à 07:12, Jagan Teki a écrit :
Add 4-byte address supports, so-that SPI-NOR chips has > 16MiB should accessible.
Signed-off-by: Jagan Teki jagan@amarulasolutions.com
drivers/mtd/spi-nor/m25p80.c | 1 + drivers/mtd/spi-nor/spi-nor.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 6 +++++- 3 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c index 5465921..7af6f59 100644 --- a/drivers/mtd/spi-nor/m25p80.c +++ b/drivers/mtd/spi-nor/m25p80.c @@ -35,6 +35,7 @@ static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) cmd[1] = addr >> (nor->addr_width * 8 - 8); cmd[2] = addr >> (nor->addr_width * 8 - 16); cmd[3] = addr >> (nor->addr_width * 8 - 24);
- cmd[4] = addr >> (nor->addr_width * 8 - 32);
}
static int m25p_cmdsz(struct spi_nor *nor) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 8bf9e67..e0085cf 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -632,6 +632,38 @@ static int stm_unlock(struct udevice *dev, loff_t ofs, uint64_t len) } #endif
+/* Enable/disable 4-byte addressing mode. */ +static int set_4byte(struct udevice *dev, const struct spi_nor_info *info,
int enable)
+{
- struct spi_nor *nor = spi_nor_get_spi_nor_dev(dev);
- const struct spi_nor_ops *ops = spi_nor_get_ops(dev);
- int status;
- bool need_wren = false;
- u8 cmd;
- switch (JEDEC_MFR(info)) {
- case SNOR_MFR_MICRON:
/* Some Micron need WREN command; all will accept it */
need_wren = true;
- case SNOR_MFR_MACRONIX:
- case SNOR_MFR_WINBOND:
if (need_wren)
write_enable(dev);
cmd = enable ? SNOR_OP_EN4B : SNOR_OP_EX4B;
status = ops->write_reg(dev, cmd, NULL, 0);
if (need_wren)
write_disable(dev);
return status;
- default:
/* Spansion style */
nor->cmd_buf[0] = enable << 7;
return ops->write_reg(dev, SNOR_OP_BRWR, nor->cmd_buf, 1);
- }
+}
#ifdef CONFIG_SPI_NOR_MACRONIX static int macronix_quad_enable(struct udevice *dev) { @@ -873,6 +905,12 @@ int spi_nor_scan(struct spi_nor *nor) }
nor->addr_width = 3;
- if (mtd->size > SNOR_16MB_BOUN) {
nor->addr_width = 4;
ret = set_4byte(nor->dev, info, true);
if (ret)
goto err;
- }
This is a bad idea: you make the SPI NOR memory enter its 4-byte address mode, which is statefull. Then, once in Linux for instance, the memory would still be in its 4-byte address mode but expected to be in its reset state, hence in 3-byte address mode.
At this point, this might not be an issue yet because spi-nor under linux is likely to use the 4-byte address instruction set if available, which is stateless by the way, so whatever the memory is in its 3-byte or in 4-byte address mode, the op codes of the 4-byte address instruction set are always expected to be followed by a 4-byte address. So Linux won't notice that the SPI NOR memory is in its 4-byte mode but not in its 3-byte mode as expected.
However, if a spurious reboot occurs at the CPU side after the SPI NOR memory has entered its 4-byte address mode, either still in u-boot or already in linux or whatever running code, the SPI NOR memory, likely not reset on its side, would still be in its 4-byte address mode, whereas many earlier boot-loaders would expect the memory to be in it's reset state, ie in its 3-byte address mode. Hence those boot-loaders, running before u-boot, would not be able to read data (load u-boot) from the SPI NOR memory: the boot would fail!
Examples of those early boot-loaders are the ROM Codes of many Atmel SAMA5Dx SoCs but I'm pretty sure this issue also applies to other manufacturers based on patches I've seen on the linux-mtd mailing list to add the SPI_NOR_4B_OPCODES flag to some memory parts: https://patchwork.ozlabs.org/patch/750305/
Think about a watchdog resetting the CPU for instance.
TL;DR You should avoid making the SPI NOR memory enter its statefull 4-byte address mode but consider using the stateless 4-byte address instruction set instead, when possible.
Almost all recent SPI NOR memories with sizes > 16MiB support the 4-byte address mode.
I meant the 4-byte address instruction set ;)
Best regards,
Cyrille
/* Dummy cycles for read */ switch (nor->read_opcode) { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index e1688e2..fc4a649 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -63,6 +63,10 @@ #define SNOR_OP_BP 0x02 /* Byte program */ #define SNOR_OP_AAI_WP 0xad /* Auto addr increment word program */
+/* Used for Macronix and Winbond flashes. */ +#define SNOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ +#define SNOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
/* Status Register bits. */ #define SR_WIP BIT(0) /* Write in progress */ #define SR_WEL BIT(1) /* Write enable latch */ @@ -84,7 +88,7 @@ /* Flash timeout values */ #define SNOR_READY_WAIT_PROG (2 * CONFIG_SYS_HZ) #define SNOR_READY_WAIT_ERASE (5 * CONFIG_SYS_HZ) -#define SNOR_MAX_CMD_SIZE 4 +#define SNOR_MAX_CMD_SIZE 6 #define SNOR_16MB_BOUN 0x1000000
/**

Now, spi-nor framework is up so access spi-nor device from command prompt via 'spinor'
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- cmd/Kconfig | 5 + cmd/Makefile | 1 + cmd/spinor.c | 281 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/Makefile | 7 +- include/linux/mtd/spi-nor.h | 1 + 5 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 cmd/spinor.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index c033223..130c226 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -870,6 +870,11 @@ config CMD_SDRAM SDRAM has an EEPROM with information that can be read using the I2C bus. This is only available on some boards.
+config CMD_SPINOR + bool "spinor" + help + SPI NOR Flash support + config CMD_SF bool "sf" help diff --git a/cmd/Makefile b/cmd/Makefile index 00e3869..81ab932 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -108,6 +108,7 @@ obj-$(CONFIG_CMD_REMOTEPROC) += remoteproc.o obj-$(CONFIG_SANDBOX) += host.o obj-$(CONFIG_CMD_SATA) += sata.o obj-$(CONFIG_CMD_NVME) += nvme.o +obj-$(CONFIG_CMD_SPINOR) += spinor.o obj-$(CONFIG_CMD_SF) += sf.o obj-$(CONFIG_CMD_SCSI) += scsi.o disk.o obj-$(CONFIG_CMD_SHA1SUM) += sha1sum.o diff --git a/cmd/spinor.c b/cmd/spinor.c new file mode 100644 index 0000000..b781151 --- /dev/null +++ b/cmd/spinor.c @@ -0,0 +1,281 @@ +/* + * Command for accessing SPI-NOR device. + * + * Copyright (C) 2016 Jagan Teki jagan@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <mtd.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/spi-nor.h> + +#include <asm/io.h> +#include <jffs2/jffs2.h> + +static int curr_device = 0; + +static int do_spinor_list(void) +{ + print_spi_nor_devices('\n'); + return CMD_RET_SUCCESS; +} + +static struct spi_nor *init_spinor_device(int dev, bool force_init) +{ + struct spi_nor *nor; + + nor = find_spi_nor_device(dev); + if (!nor) { + printf("No SPI-NOR device found! %x\n", dev); + return NULL; + } + + if (force_init) + nor->init_done = 0; + if (spi_nor_scan(nor)) + return NULL; + + return nor; +} + +static void print_spinor_info(struct spi_nor *nor) +{ + struct mtd_info *mtd = spi_nor_get_mtd_info(nor); + + printf("bus: %s: %d\n", nor->dev->name, mtd->devnum); + printf("device: %s\n", mtd->name); + printf("page size: %d B\nerase size: ", mtd->writebufsize); + print_size(mtd->erasesize, "\nsize: "); + print_size(mtd->size, ""); + if (nor->memory_map) + printf(", mapped at %p", nor->memory_map); + printf("\n"); +} + +static int do_spinor_info(void) +{ + struct spi_nor *nor; + + if (curr_device < 0) { + if (get_spi_nor_num() > 0) + curr_device = 0; + else { + puts("No SPI-NOR device available\n"); + return 1; + } + } + + nor = init_spinor_device(curr_device, false); + if (!nor) + return CMD_RET_FAILURE; + + print_spinor_info(nor); + return CMD_RET_SUCCESS; +} + +static int do_spinor_dev(int argc, char * const argv[]) +{ + struct spi_nor *nor; + int devnum = 0; + int ret; + + if (argc == 2) + devnum = curr_device; + else if (argc == 3) + devnum = simple_strtoul(argv[2], NULL, 10); + + nor = init_spinor_device(devnum, true); + if (!nor) + return CMD_RET_FAILURE; + + ret = mtd_select_devnum(MTD_IF_TYPE_SPI_NOR, devnum); + printf("switch to dev #%d, %s\n", devnum, (!ret) ? "OK" : "ERROR"); + if (ret) + return CMD_RET_FAILURE; + + curr_device = devnum; + printf("spinor%d is current device\n", curr_device); + + return CMD_RET_SUCCESS; +} + +static int do_spinor_write_read(int argc, char * const argv[]) +{ + struct mtd_info *mtd; + struct spi_nor *nor; + loff_t offset, addr, len, maxsize; + u_char *buf; + char *endp; + int idx = 0; + int ret = CMD_RET_FAILURE; + + if (argc != 4) + return CMD_RET_USAGE; + + nor = init_spinor_device(curr_device, false); + if (!nor) + return CMD_RET_FAILURE; + + addr = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return CMD_RET_FAILURE; + + mtd = spi_nor_get_mtd_info(nor); + if (mtd_arg_off_size(argc - 2, &argv[2], &idx, &offset, &len, + &maxsize, MTD_DEV_TYPE_NOR, mtd->size)) + return CMD_RET_FAILURE; + + buf = map_physmem(addr, len, MAP_WRBACK); + if (!buf) { + puts("failed to map physical memory\n"); + return 1; + } + + if (strcmp(argv[0], "write") == 0) + ret = mtd_dwrite(mtd, offset, len, (size_t *)&len, buf); + else if (strcmp(argv[0], "read") == 0) + ret = mtd_dread(mtd, offset, len, (size_t *)&len, buf); + + printf("SPI-NOR: %zu bytes @ %#llx %s: ", (size_t)len, offset, + (strcmp(argv[0], "read") == 0) ? "Read" : "Written"); + if (ret) + printf("ERROR %d\n", ret); + else + printf("OK\n"); + + unmap_physmem(buf, len); + + return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + +static int mtd_parse_len_arg(struct mtd_info *mtd, char *arg, loff_t *len) +{ + char *ep; + char round_up_len; /* indicates if the "+length" form used */ + ulong len_arg; + + round_up_len = 0; + if (*arg == '+') { + round_up_len = 1; + ++arg; + } + + len_arg = simple_strtoul(arg, &ep, 16); + if (ep == arg || *ep != '\0') + return -1; + + if (round_up_len && mtd->erasesize > 0) + *len = ROUND(len_arg, mtd->erasesize); + else + *len = len_arg; + + return 1; +} + +static int do_spinor_erase(int argc, char * const argv[]) +{ + struct mtd_info *mtd; + struct spi_nor *nor; + struct erase_info instr; + loff_t addr, len, maxsize; + int idx = 0; + int ret; + + if (argc != 3) + return CMD_RET_USAGE; + + nor = init_spinor_device(curr_device, false); + if (!nor) + return CMD_RET_FAILURE; + + mtd = spi_nor_get_mtd_info(nor); + if (mtd_arg_off(argv[1], &idx, &addr, &len, &maxsize, + MTD_DEV_TYPE_NOR, mtd->size)) + return CMD_RET_FAILURE; + + ret = mtd_parse_len_arg(mtd, argv[2], &len); + if (ret != 1) + return CMD_RET_FAILURE; + + instr.mtd = mtd; + instr.addr = addr; + instr.len = len; + instr.callback = 0; + ret = mtd_derase(mtd, &instr); + printf("SPI-NOR: %zu bytes @ %#llx Erased: %s\n", (size_t)len, addr, + ret ? "ERROR" : "OK"); + + return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + +static int do_spinor(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + const char *cmd; + int ret = 0; + + cmd = argv[1]; + if (strcmp(cmd, "list") == 0) { + if (argc > 2) + goto usage; + + ret = do_spinor_list(); + goto done; + } + + if (strcmp(cmd, "dev") == 0) { + if (argc > 3) + goto usage; + + ret = do_spinor_dev(argc, argv); + goto done; + } + + if (strcmp(cmd, "info") == 0) { + if (argc > 2) + goto usage; + + ret = do_spinor_info(); + goto done; + } + + if (argc < 3) + goto usage; + + --argc; + ++argv; + + if (strcmp(cmd, "erase") == 0) { + ret = do_spinor_erase(argc, argv); + goto done; + } + + if (strcmp(cmd, "write") == 0 || strcmp(cmd, "read") == 0) { + ret = do_spinor_write_read(argc, argv); + goto done; + } + +done: + if (ret != -1) + return ret; + +usage: + return CMD_RET_USAGE; +} + +static char spinor_help_text[] = + "list - show list of spinor devices\n" + "spinor info - show current spinor device info\n" + "spinor dev [devnum] - show or set current spinor device\n" + "spinor erase offset len - erase 'len' bytes from 'offset'\n" + "spinor write addr to len - write 'len' bytes to 'to' from 'addr'\n" + "spinor read addr from len - read 'len' bytes from 'from' to 'addr'"; + +U_BOOT_CMD( + spinor, 5, 1, do_spinor, + "SPI-NOR Sub-system", + spinor_help_text +); diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 20c0d0a..c5209ad 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -5,8 +5,11 @@ # SPDX-License-Identifier: GPL-2.0+ #
-ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF))) -obj-y += mtdcore.o mtd_uboot.o +ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF),$(CONFIG_CMD_SPINOR))) +obj-y += mtd_uboot.o +ifndef CONFIG_MTD +obj-y += mtdcore.o +endif endif obj-$(CONFIG_MTD) += mtd-uclass.o obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index fc4a649..9c3da70 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -217,5 +217,6 @@ struct spi_nor *find_spi_nor_device(int dev_num); int get_spi_nor_num(void); struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev); struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor); +void print_spi_nor_devices(char separator);
#endif /* __MTD_SPI_NOR_H */

sync the protect command from legacy cmd/sf.c and update them according to current spi-nor framework.
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- cmd/spinor.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- drivers/mtd/mtd-uclass.c | 19 +++++++++++++++++++ include/mtd.h | 11 +++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/cmd/spinor.c b/cmd/spinor.c index b781151..ef8decf 100644 --- a/cmd/spinor.c +++ b/cmd/spinor.c @@ -151,6 +151,44 @@ static int do_spinor_write_read(int argc, char * const argv[]) return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; }
+static int do_spinor_protect(int argc, char * const argv[]) +{ + struct mtd_info *mtd; + struct spi_nor *nor; + loff_t sector, len, maxsize; + char *endp; + int idx = 0; + bool prot = false; + int ret = CMD_RET_FAILURE; + + if (argc != 4) + return CMD_RET_USAGE; + + nor = init_spinor_device(curr_device, false); + if (!nor) + return CMD_RET_FAILURE; + + sector = simple_strtoul(argv[2], &endp, 16); + if (*argv[2] == 0 || *endp != 0) + return CMD_RET_FAILURE; + + mtd = spi_nor_get_mtd_info(nor); + if (mtd_arg_off_size(argc - 3, &argv[3], &idx, §or, &len, + &maxsize, MTD_DEV_TYPE_NOR, mtd->size)) + return CMD_RET_FAILURE; + + if (strcmp(argv[1], "lock") == 0) + prot = true; + else if (strcmp(argv[1], "unlock") == 0) + prot = false; + else + return -1; /* Unknown parameter */ + + ret = mtd_dprotect(mtd, sector, len, prot); + + return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + static int mtd_parse_len_arg(struct mtd_info *mtd, char *arg, loff_t *len) { char *ep; @@ -258,6 +296,11 @@ static int do_spinor(cmd_tbl_t *cmdtp, int flag, int argc, goto done; }
+ if (strcmp(cmd, "protect") == 0) { + ret = do_spinor_protect(argc, argv); + goto done; + } + done: if (ret != -1) return ret; @@ -272,7 +315,9 @@ static char spinor_help_text[] = "spinor dev [devnum] - show or set current spinor device\n" "spinor erase offset len - erase 'len' bytes from 'offset'\n" "spinor write addr to len - write 'len' bytes to 'to' from 'addr'\n" - "spinor read addr from len - read 'len' bytes from 'from' to 'addr'"; + "spinor read addr from len - read 'len' bytes from 'from' to 'addr'\n" + "spinor protect lock/unlock sector len - protect/unprotect 'len' bytes starting\n" + " at address 'sector'";
U_BOOT_CMD( spinor, 5, 1, do_spinor, diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c index d9ecfea..535f68e 100644 --- a/drivers/mtd/mtd-uclass.c +++ b/drivers/mtd/mtd-uclass.c @@ -73,6 +73,25 @@ int mtd_dwrite(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, return ops->write(dev, to, len, retlen, buf); }
+int mtd_dprotect(struct mtd_info *mtd, loff_t ofs, uint64_t len, bool prot) +{ + struct udevice *dev = mtd->dev; + const struct mtd_ops *ops = mtd_get_ops(dev); + + if (!ops->lock || !ops->unlock) + return -ENOSYS; + + if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) + return -EINVAL; + if (!len) + return 0; + + if (prot) + return ops->lock(dev, ofs, len); + else + return ops->unlock(dev, ofs, len); +} + int mtd_find_device(int mtd_if_type, int devnum, struct udevice **devp) { struct uclass *uc; diff --git a/include/mtd.h b/include/mtd.h index acb2256..6390133 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -61,6 +61,17 @@ int mtd_dwrite(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
/** + * mtd_dprotect - protect area of MTD device + * + * @mtd: MTD device + * @ofs: sector start offset into device in bytes to protect to + * @len: len of bytes to protect + * @prot: protect boolean, true for lock, false for unlock + * @return 0 if OK, -ve on error + */ +int mtd_dprotect(struct mtd_info *mtd, loff_t ofs, uint64_t len, bool prot); + +/** * mtd_derase() - erase blocks of the MTD device * * @mtd: MTD device

initialize spi-nor framework during boot, so-that detected buses can appears at boot log.
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- common/board_r.c | 13 +++++++++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 17 +++++++++++++++++ include/linux/mtd/spi-nor.h | 1 + 3 files changed, 31 insertions(+)
diff --git a/common/board_r.c b/common/board_r.c index 09167c1..8030548 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -42,6 +42,7 @@ #include <scsi.h> #include <serial.h> #include <spi.h> +#include <linux/mtd/spi-nor.h> #include <stdio_dev.h> #include <timer.h> #include <trace.h> @@ -400,6 +401,15 @@ static int initr_spi(void) } #endif
+#ifdef CONFIG_MTD_SPI_NOR +static int initr_spi_nor(void) +{ + puts("SPI-NOR: "); + spi_nor_init(); + return 0; +} +#endif + #ifdef CONFIG_CMD_NAND /* go init the NAND */ static int initr_nand(void) @@ -762,6 +772,9 @@ static init_fnc_t init_sequence_r[] = { #ifdef CONFIG_PPC initr_spi, #endif +#ifdef CONFIG_MTD_SPI_NOR + initr_spi_nor, +#endif #ifdef CONFIG_CMD_NAND initr_nand, #endif diff --git a/drivers/mtd/spi-nor/spi-nor-uclass.c b/drivers/mtd/spi-nor/spi-nor-uclass.c index 919682d..8a37be0 100644 --- a/drivers/mtd/spi-nor/spi-nor-uclass.c +++ b/drivers/mtd/spi-nor/spi-nor-uclass.c @@ -15,6 +15,23 @@
DECLARE_GLOBAL_DATA_PTR;
+int spi_nor_init(void) +{ + struct udevice *bus; + + for (uclass_first_device(UCLASS_SPI_NOR, &bus); + bus; + uclass_next_device(&bus)) { + ; + } + +#ifndef CONFIG_SPL_BUILD + print_spi_nor_devices(','); +#endif + + return 0; +} + struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev) { struct spi_nor_uclass_priv *upriv; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 9c3da70..2e8e52d 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -218,5 +218,6 @@ int get_spi_nor_num(void); struct spi_nor *spi_nor_get_spi_nor_dev(struct udevice *dev); struct mtd_info *spi_nor_get_mtd_info(struct spi_nor *nor); void print_spi_nor_devices(char separator); +int spi_nor_init(void);
#endif /* __MTD_SPI_NOR_H */

Hi Jagan,
On 27 December 2017 at 23:12, Jagan Teki jagan@amarulasolutions.com wrote:
initialize spi-nor framework during boot, so-that detected buses can appears at boot log.
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com
common/board_r.c | 13 +++++++++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 17 +++++++++++++++++ include/linux/mtd/spi-nor.h | 1 + 3 files changed, 31 insertions(+)
We should rely on driver model to init this when it is first probed, so this should not be needed.
Regards, Simon

Now spi-nor framework is up so access the environment through en/spinor.c
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- cmd/nvedit.c | 1 + env/Kconfig | 27 ++++++++++++ env/Makefile | 1 + env/env.c | 2 + env/spinor.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/environment.h | 1 + 6 files changed, 145 insertions(+) create mode 100644 env/spinor.c
diff --git a/cmd/nvedit.c b/cmd/nvedit.c index 4e79d03..2e5e88d 100644 --- a/cmd/nvedit.c +++ b/cmd/nvedit.c @@ -50,6 +50,7 @@ DECLARE_GLOBAL_DATA_PTR; !defined(CONFIG_ENV_IS_IN_ONENAND) && \ !defined(CONFIG_ENV_IS_IN_SATA) && \ !defined(CONFIG_ENV_IS_IN_SPI_FLASH) && \ + !defined(CONFIG_ENV_IS_IN_SPI_NOR) && \ !defined(CONFIG_ENV_IS_IN_REMOTE) && \ !defined(CONFIG_ENV_IS_IN_UBI) && \ !defined(CONFIG_ENV_IS_NOWHERE) diff --git a/env/Kconfig b/env/Kconfig index 2477bf8..1b694d5 100644 --- a/env/Kconfig +++ b/env/Kconfig @@ -329,6 +329,22 @@ config ENV_IS_IN_SPI_FLASH
Define the SPI work mode. If not defined then use SPI_MODE_3.
+config ENV_IS_IN_SPI_NOR + bool "Environment is in SPI-NOR flash" + depends on !CHAIN_OF_TRUST + help + Define this if you have a SPI NOR Flash memory device which you + want to use for the environment. + + - CONFIG_ENV_OFFSET: + - CONFIG_ENV_SIZE: + + These two #defines specify the offset and size of the + environment area within the SPI NOR Flash. CONFIG_ENV_OFFSET must be + aligned to an erase sector boundary. + + - CONFIG_ENV_SECT_SIZE: + config ENV_IS_IN_UBI bool "Environment in a UBI volume" depends on !CHAIN_OF_TRUST @@ -396,6 +412,17 @@ config ENV_FAT_FILE It's a string of the FAT file name. This file use to store the environment.
+config ENV_SPI_NOR_DEVNUM + int "SPI-NOR device number for the environment" + depends on ENV_IS_IN_SPI_NOR + default 0 + help + Define this an integer of spi nor device number for the environment. + Since spi nor framework assign device numbers automatically by devicetree + node definition which start from 0, so assign 0 as default. + + If board need different devnum then, this can be change. + if ARCH_SUNXI
config ENV_OFFSET diff --git a/env/Makefile b/env/Makefile index 7ce8231..21fe9a4 100644 --- a/env/Makefile +++ b/env/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_ENV_IS_IN_NVRAM) += nvram.o obj-$(CONFIG_ENV_IS_IN_ONENAND) += onenand.o obj-$(CONFIG_ENV_IS_IN_SATA) += sata.o obj-$(CONFIG_ENV_IS_IN_SPI_FLASH) += sf.o +obj-$(CONFIG_ENV_IS_IN_SPI_NOR) += spinor.o obj-$(CONFIG_ENV_IS_IN_REMOTE) += remote.o obj-$(CONFIG_ENV_IS_IN_UBI) += ubi.o obj-$(CONFIG_ENV_IS_NOWHERE) += nowhere.o diff --git a/env/env.c b/env/env.c index 76a5608..be1672c 100644 --- a/env/env.c +++ b/env/env.c @@ -44,6 +44,8 @@ static enum env_location env_get_default_location(void) return ENVL_REMOTE; else if IS_ENABLED(CONFIG_ENV_IS_IN_SPI_FLASH) return ENVL_SPI_FLASH; + else if IS_ENABLED(CONFIG_ENV_IS_IN_SPI_NOR) + return ENVL_SPI_NOR; else if IS_ENABLED(CONFIG_ENV_IS_IN_UBI) return ENVL_UBI; else if IS_ENABLED(CONFIG_ENV_IS_NOWHERE) diff --git a/env/spinor.c b/env/spinor.c new file mode 100644 index 0000000..bc6fdfa --- /dev/null +++ b/env/spinor.c @@ -0,0 +1,113 @@ +/* + * SPI-NOR Environment. + * + * Copyright (C) 2017 Jagan Teki jagan@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <environment.h> +#include <memalign.h> +#include <mtd.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/spi-nor.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct spi_nor *env_init_spinor(void) +{ + struct spi_nor *nor; + int devnum = CONFIG_ENV_SPI_NOR_DEVNUM; + + nor = find_spi_nor_device(devnum); + if (!nor) + return NULL; + + if (spi_nor_scan(nor)) + return NULL; + + return nor; +} + +static int env_spinor_save(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); + struct spi_nor *nor; + struct mtd_info *mtd; + struct erase_info instr; + loff_t len, sector; + int ret; + + nor = env_init_spinor(); + if (!nor) + return -EIO; + + ret = env_export(env_new); + if (ret) + return -EINVAL; + + mtd = spi_nor_get_mtd_info(nor); + if (!mtd) + return -EIO; + + sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE); + len = sector * CONFIG_ENV_SECT_SIZE; + instr.mtd = mtd; + instr.addr = CONFIG_ENV_OFFSET; + instr.len = len; + instr.callback = 0; + + puts("erasing spinor flash...\n"); + ret = mtd_derase(mtd, &instr); + if (ret) + return -EIO; + + len = CONFIG_ENV_SIZE; + puts("writing spinor flash...\n"); + ret = mtd_dwrite(mtd, CONFIG_ENV_OFFSET, len, + (size_t *)&len, (u_char *)env_new); + if (ret) + return -EIO; + + puts("done\n"); + return ret; +} + +static int env_spinor_load(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); + struct spi_nor *nor; + struct mtd_info *mtd; + loff_t offset, len; + int ret; + + nor = env_init_spinor(); + if (!nor) + return -EIO; + + offset = CONFIG_ENV_OFFSET; + len = CONFIG_ENV_SIZE; + + mtd = spi_nor_get_mtd_info(nor); + if (!mtd) + return -EIO; + + ret = mtd_dread(mtd, offset, len, (size_t *)&len, (uchar *)buf); + if (ret) + return -EIO; + + ret = env_import(buf, 1); + if (ret) + gd->env_valid = ENV_VALID; + + return ret; +} + +U_BOOT_ENV_LOCATION(spinor) = { + .location = ENVL_SPI_NOR, + ENV_NAME("SPI-NOR Flash") + .load = env_spinor_load, + .save = env_save_ptr(env_spinor_save), +}; diff --git a/include/environment.h b/include/environment.h index d29f82c..8e8be26 100644 --- a/include/environment.h +++ b/include/environment.h @@ -198,6 +198,7 @@ enum env_location { ENVL_ONENAND, ENVL_REMOTE, ENVL_SPI_FLASH, + ENVL_SPI_NOR, ENVL_UBI, ENVL_NOWHERE,

Hi Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Thursday, December 28, 2017 11:43 AM To: u-boot@lists.denx.de Cc: Tom Rini trini@konsulko.com Subject: [U-Boot] [PATCH v10 24/27] env: add spi-nor environment
Now spi-nor framework is up so access the environment through en/spinor.c
Signed-off-by: Suneel Garapati suneelglinux@gmail.com Signed-off-by: Jagan Teki jagan@amarulasolutions.com
This patch is only considering environment to be used after relocation to DDR.
What about the scenario where environment is required during SPL. SPL may not be having MTD sub-system ready for env read. In SPL case raw read is required. Similar to below patch.
http://patchwork.ozlabs.org/patch/848511/
I request you to consider this patch also.
--pk

zynq qspi controller can be considered as spi-nor controller since controller meant for accessing spi-nor chips along with flash related functionalities.
Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- arch/arm/dts/zynq-7000.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/arch/arm/dts/zynq-7000.dtsi b/arch/arm/dts/zynq-7000.dtsi index d9774d8..fa7965d 100644 --- a/arch/arm/dts/zynq-7000.dtsi +++ b/arch/arm/dts/zynq-7000.dtsi @@ -213,6 +213,18 @@ #size-cells = <0>; };
+ qspinor: spi-nor@e000d000 { + clock-names = "ref_clk", "pclk"; + clocks = <&clkc 10>, <&clkc 43>; + compatible = "xlnx,zynq-qspinor-1.0"; + status = "disabled"; + interrupt-parent = <&intc>; + interrupts = <0 19 4>; + reg = <0xe000d000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + }; + gem0: ethernet@e000b000 { compatible = "cdns,zynq-gem", "cdns,gem"; reg = <0xe000b000 0x1000>;

Enable spi-nor framework for zynq microzed board, the zynq_qspi controller at spi still there for testing once the framework is mature will drop.
test log: ======== Zynq> spinor spinor - SPI-NOR Sub-system
Usage: spinor list - show list of spinor devices spinor info - show current spinor device info spinor dev [devnum] - show or set current spinor device spinor erase offset len - erase 'len' bytes from 'offset' spinor write addr to len - write 'len' bytes to 'to' from 'addr' spinor read addr from len - read 'len' bytes from 'from' to 'addr' spinor protect lock/unlock sector len - protect/unprotect 'len' bytes starting at address 'sector' Zynq> spinor list flash@0: 0 spi-nor@e000d000: 1 Zynq> spinor dev 0 switch to dev #0, OK spinor0 is current device Zynq> spinor info bus: flash@0: 0 device: s25fl128s_64k page size: 256 B erase size: 64 KiB size: 16 MiB Zynq> spinor erase 0xE00000 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Erased: OK Zynq> mw.b 0x100 0xcc 0x100000 Zynq> spinor write 0x100 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Written: OK Zynq> spinor read 0x3000000 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Read: OK Zynq> cmp.b 0x3000000 0x100 0x100000 Total of 1048576 byte(s) were the same Zynq> spinor dev 1 switch to dev #1, OK spinor1 is current device Zynq> spinor info bus: spi-nor@e000d000: 1 device: s25fl128s_64k page size: 256 B erase size: 64 KiB size: 16 MiB Zynq> spinor erase 0xE00000 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Erased: OK Zynq> mw.b 0x100 0xbb 0x100000 Zynq> spinor write 0x100 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Written: OK Zynq> spinor read 0x3000000 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Read: OK Zynq> cmp.b 0x3000000 0x100 0x100000 Total of 1048576 byte(s) were the same
Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- arch/arm/dts/zynq-microzed.dts | 12 +++++++++++- configs/zynq_microzed_defconfig | 17 ++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/arch/arm/dts/zynq-microzed.dts b/arch/arm/dts/zynq-microzed.dts index 2d07b92..ff973dd 100644 --- a/arch/arm/dts/zynq-microzed.dts +++ b/arch/arm/dts/zynq-microzed.dts @@ -14,7 +14,6 @@
aliases { serial0 = &uart1; - spi0 = &qspi; mmc0 = &sdhci0; };
@@ -41,6 +40,17 @@ &qspi { u-boot,dm-pre-reloc; status = "okay"; + + s25fl128s_64k: flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + }; +}; + +&qspinor { + status = "okay"; };
&uart1 { diff --git a/configs/zynq_microzed_defconfig b/configs/zynq_microzed_defconfig index a713314..56908fe 100644 --- a/configs/zynq_microzed_defconfig +++ b/configs/zynq_microzed_defconfig @@ -19,7 +19,7 @@ CONFIG_CMD_FPGA_LOADMK=y CONFIG_CMD_FPGA_LOADP=y CONFIG_CMD_GPIO=y CONFIG_CMD_MMC=y -CONFIG_CMD_SF=y +CONFIG_CMD_SPINOR=y CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_TFTPPUT=y @@ -32,7 +32,7 @@ CONFIG_CMD_EXT4=y CONFIG_CMD_EXT4_WRITE=y CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y -CONFIG_ENV_IS_IN_SPI_FLASH=y +CONFIG_ENV_IS_IN_SPI_NOR=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_DFU_MMC=y @@ -40,14 +40,17 @@ CONFIG_DFU_RAM=y CONFIG_FPGA_XILINX=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_ZYNQ=y -CONFIG_SPI_FLASH=y -CONFIG_SPI_FLASH_BAR=y -CONFIG_SPI_FLASH_SPANSION=y -CONFIG_SPI_FLASH_STMICRO=y -CONFIG_SPI_FLASH_WINBOND=y CONFIG_ZYNQ_GEM=y CONFIG_ZYNQ_SERIAL=y CONFIG_ZYNQ_QSPI=y +CONFIG_MTD=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_ZYNQ_QSPI=y +CONFIG_SPI_NOR_STMICRO=y +CONFIG_SPI_NOR_SPANSION=y +CONFIG_SPI_NOR_WINBOND=y +CONFIG_SPI_NOR_MACRONIX=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_ULPI_VIEWPORT=y

Add some tests to check that mtd devices work as expected, more tests will add it in future.
Also removed CMD_MTDPARTS which breaking driver-model MTD.
Signed-off-by: Jagan Teki jagan@amarulasolutions.com --- configs/sandbox_defconfig | 8 +++++++- test/dm/Makefile | 1 + test/dm/mtd.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/dm/mtd.c
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 7efb4eb..658d9f2 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -65,7 +65,6 @@ CONFIG_CMD_BTRFS=y CONFIG_CMD_CBFS=y CONFIG_CMD_CRAMFS=y CONFIG_CMD_EXT4_WRITE=y -CONFIG_CMD_MTDPARTS=y CONFIG_CMD_LOG=y CONFIG_MAC_PARTITION=y CONFIG_AMIGA_PARTITION=y @@ -197,3 +196,10 @@ CONFIG_UT_TIME=y CONFIG_UT_DM=y CONFIG_UT_ENV=y CONFIG_UT_OVERLAY=y +CONFIG_MTD=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_M25P80=y +CONFIG_SPI_NOR_STMICRO=y +CONFIG_SPI_NOR_SPANSION=y +CONFIG_SPI_NOR_WINBOND=y +CONFIG_SPI_NOR_MACRONIX=y diff --git a/test/dm/Makefile b/test/dm/Makefile index 513c456..55f7450 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_DM_GPIO) += gpio.o obj-$(CONFIG_DM_I2C) += i2c.o obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o +obj-$(CONFIG_MTD) += mtd.o obj-$(CONFIG_DM_MMC) += mmc.o obj-$(CONFIG_DM_PCI) += pci.o obj-$(CONFIG_PHY) += phy.o diff --git a/test/dm/mtd.c b/test/dm/mtd.c new file mode 100644 index 0000000..fe98593 --- /dev/null +++ b/test/dm/mtd.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 Jagan Teki jagan@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <asm/state.h> +#include <dm/test.h> +#include <test/ut.h> + +#include <mtd.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/spi-nor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Test that we can find mtd devices without probing them */ +static int dm_test_mtd_find(struct unit_test_state *uts) +{ + struct udevice *mtd, *spinor, *dev; + + ut_assertok(mtd_create_device(mtd, "spinor_mtd", "test", + MTD_IF_TYPE_SPI_NOR, &spinor)); + + ut_asserteq(-ENODEV, mtd_find_device(MTD_IF_TYPE_SPI_NOR, 1, &dev)); + ut_assertok(mtd_find_device(MTD_IF_TYPE_SPI_NOR, 0, &dev)); + ut_asserteq_ptr(spinor, dev); + + return 0; +} +DM_TEST(dm_test_mtd_find, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to comply with DM?
What about "sf" command which we use now to get access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
======================================= mtd-uclass.c
^^^^^ - here we will have a generic MTD layer (as it is in Linux)
======================================= spi-nor-uclass.c ======================================= spi-nor.c
^^^^^^ - why do we need to have spi-nor.c ? Cannot we have its functionality in the spi-nor-uclass.c ? (I'm just curious)
======================================= m25p80.c zynq_qspinor.c
^^^^^ - this is the ^^^^^^^^ - [*] "generic" spi-nor driver used in Linux. I suppose that it will be reused here?
======================================= spi-uclass.c
^^^^^^^ - why do we need this uclass?
======================================= zynq_qspi.c
^^^^^ - This is probably the low-level driver for zynq QSPI IP block - why do we have zynq_qspinor.c file above [*]? What is the difference/need of such division?
======================================= #####SPI NOR chip###### =======================================
Changes for v10:
- Update Simon's R-B tag
- Add mtd dm test case
- implemented entire code wrt MTD, with interface type
code size:
before: $ arm-linux-gnueabi-size u-boot text data bss dec hex filename 473712 15152 222568 711432 adb08 u-boot $ du -hs u-boot-dtb.img 488K u-boot-dtb.img
after: $ arm-linux-gnueabi-size u-boot text data bss dec hex filename 470124 14352 222584 707060 ac9f4 u-boot $ du -hs u-boot-dtb.img 484K u-boot-dtb.img
approximately 4KiB but DM_SPI_FLASH still there which can be removed once support added in SPL
test log:
Zynq> spinor spinor - SPI-NOR Sub-system
Usage: spinor list - show list of spinor devices spinor info - show current spinor device info spinor dev [devnum] - show or set current spinor device spinor erase offset len - erase 'len' bytes from 'offset' spinor write addr to len - write 'len' bytes to 'to' from
^^^^^^^^^^^^^^^^ I would love to see support for "mtdparts=" It would facilitate handling SPI-NOR "partitions" like u-boot/SPI/envs/ etc.
I also suppose that "sf probe" (or any its replacement with spinor) would be not necessary to call before using other SPI-NOR related commands?
Maybe the biggest request - would it be possible to add ./doc/README.spi-nor entry with good explanation of this?
'addr' spinor read addr from len - read 'len' bytes from 'from' to 'addr' spinor protect lock/unlock sector len - protect/unprotect 'len' bytes starting at address 'sector' Zynq> spinor list flash@0: 0 spi-nor@e000d000: 1 Zynq> spinor dev 0 switch to dev #0, OK spinor0 is current device Zynq> spinor info bus: flash@0: 0 device: s25fl128s_64k page size: 256 B erase size: 64 KiB size: 16 MiB Zynq> spinor erase 0xE00000 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Erased: OK Zynq> mw.b 0x100 0xcc 0x100000 Zynq> spinor write 0x100 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Written: OK Zynq> spinor read 0x3000000 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Read: OK Zynq> cmp.b 0x3000000 0x100 0x100000 Total of 1048576 byte(s) were the same Zynq> spinor dev 1 switch to dev #1, OK spinor1 is current device Zynq> spinor info bus: spi-nor@e000d000: 1 device: s25fl128s_64k page size: 256 B erase size: 64 KiB size: 16 MiB Zynq> spinor erase 0xE00000 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Erased: OK Zynq> mw.b 0x100 0xbb 0x100000 Zynq> spinor write 0x100 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Written: OK Zynq> spinor read 0x3000000 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Read: OK Zynq> cmp.b 0x3000000 0x100 0x100000 Total of 1048576 byte(s) were the same
WIP:
- to support non-dm code
- to support spinor SPL
Repo:
$ git clone git://git.denx.de/u-boot-spi.git $ cd u-boot-spi $ git checkout -b mtd-spinor-working origin/mtd-spinor-working
[1] https://lists.denx.de/pipermail/u-boot/2016-October/271459.html [2] http://lists.denx.de/pipermail/u-boot/2016-March/249286.html [3] http://lists.denx.de/pipermail/u-boot/2016-February/245418.html [4] [PATCH RFC v8 00/16] SPI-NOR/MTD addition
Jagan Teki (27): mtd: Add mtd core ops mtd: add mtd device create operations mtd: add SPI-NOR core support mtd: spi-nor: sync/modify sst write operations mtd: spi-nor: sync/modify lock operations mtd: spi-nor: Kconfig: Add MTD_SPI_NOR entry mtd: spi-nor: Kconfig: Add MTD_SPI_NOR_USE_4K_SECTORS mtd: spi-nor: Kconfig: Add SPI_NOR_MISC entry mtd: spi-nor: Kconfig: Add SPI_NOR_MACRONIX entry mtd: spi-nor: Kconfig: Add SPI_NOR_SPANSION entry mtd: spi-nor: Kconfig: Add SPI_NOR_STMICRO entry mtd: spi-nor: Kconfig: Add SPI_NOR_SST entry mtd: spi-nor: Kconfig: Add SPI_NOR_WINBOND entry mtd-uclass: use platdata_auto_alloc spi: Add spi_write_then_read mtd: spi-nor: Add m25p80 driver mtd: spi-nor: Kconfig: Add MTD_M25P80 entry mtd: spi-nor: Add zynq qspinor driver mtd: spi-nor: zynq_qspi: Kconfig: Add MTD_ZYNQ mtd: spi-nor: Add 4-byte addresswidth support cmd: add spinor cmd support cmd: spinor: sync/update protect command board_r: initialize spi_nor env: add spi-nor environment arm: dts: zynq: Add zynq-qspinor node dm: zynq: microzed: enable MTD/SPI-NOR framework test: dm: add tests for mtd devices
Makefile | 1 + arch/arm/dts/zynq-7000.dtsi | 12 + arch/arm/dts/zynq-microzed.dts | 12 +- cmd/Kconfig | 5 + cmd/Makefile | 1 + cmd/nvedit.c | 1 + cmd/spinor.c | 326 ++++++++++++ common/board_r.c | 13 + configs/sandbox_defconfig | 8 +- configs/zynq_microzed_defconfig | 17 +- drivers/mtd/Kconfig | 2 + drivers/mtd/Makefile | 7 +- drivers/mtd/mtd-uclass.c | 203 +++++++- drivers/mtd/spi-nor/Kconfig | 89 ++++ drivers/mtd/spi-nor/Makefile | 15 + drivers/mtd/spi-nor/m25p80.c | 251 ++++++++++ drivers/mtd/spi-nor/spi-nor-ids.c | 184 +++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 160 ++++++ drivers/mtd/spi-nor/spi-nor.c | 940 +++++++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/zynq_qspinor.c | 622 +++++++++++++++++++++++ drivers/spi/spi-uclass.c | 24 + env/Kconfig | 27 + env/Makefile | 1 + env/env.c | 2 + env/spinor.c | 113 +++++ include/dm/uclass-id.h | 1 + include/environment.h | 1 + include/linux/mtd/mtd.h | 14 + include/linux/mtd/spi-nor.h | 223 +++++++++ include/mtd.h | 176 +++++++ include/spi.h | 20 + test/dm/Makefile | 1 + test/dm/mtd.c | 33 ++ 33 files changed, 3493 insertions(+), 12 deletions(-) create mode 100644 cmd/spinor.c create mode 100644 drivers/mtd/spi-nor/Kconfig create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/m25p80.c create mode 100644 drivers/mtd/spi-nor/spi-nor-ids.c create mode 100644 drivers/mtd/spi-nor/spi-nor-uclass.c create mode 100644 drivers/mtd/spi-nor/spi-nor.c create mode 100644 drivers/mtd/spi-nor/zynq_qspinor.c create mode 100644 env/spinor.c create mode 100644 include/linux/mtd/spi-nor.h create mode 100644 test/dm/mtd.c
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

On Thu, Dec 28, 2017 at 8:14 PM, Lukasz Majewski lukma@denx.de wrote:
Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to comply with DM?
with this series yes, and we're working on supporting the same with non-dm.
What about "sf" command which we use now to get access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
======================================= mtd-uclass.c
^^^^^ - here we will have a generic MTD layer (as it is in Linux)
it is not like in Linux, leaving ops names apart all the code is for creating mtd device for underlying flash devices in the form of interface type during bind. the interface types as of now is spi-nor it can be nand, nor, dataflash etc in future if could be.
======================================= spi-nor-uclass.c ======================================= spi-nor.c
^^^^^^ - why do we need to have spi-nor.c ? Cannot we have its functionality in the spi-nor-uclass.c ? (I'm just curious)
spi-nor-uclass.c is an uclass driver for underlying UCLASS_SPI_NOR drivers like m25p80, zynq_qspinor etc but the spi-nor.c is the actual spi-nor core code which handle actual flash specific stuff. and also syncing Linux stuff(if require) becomes easy with spi-nor.c as a separate file.
======================================= m25p80.c zynq_qspinor.c
^^^^^ - this is the ^^^^^^^^ - [*] "generic" spi-nor driver used in Linux. I suppose that it will be reused here?
yes - name reused because, I do think the Linux and U-Boot development on spi-nor will follow in-line at some point but the most of code is following U-Boot driver-model.
======================================= spi-uclass.c
^^^^^^^ - why do we need this uclass?
This is an existing uclass spi driver for underlying UCLASS_SPI drivers like drivers/spi/zynq_qspi.c etc
======================================= zynq_qspi.c
^^^^^ - This is probably the low-level driver for zynq QSPI IP block - why do we have zynq_qspinor.c file above [*]? What is the difference/need of such division?
Both the drivers are same, the reason for zynq_qspinor is for testing framework work for two directions like - spi driver interface side through m25p80 cmd/spinor->mtd->spi-nor-uclass->spi-nor->m25p80->zynq_qspi.c - direct spi-nor controller side spinor->mtd->spi-nor-uclass->spi-nor->zynq_qspinor
Will remove drivers/spi/zynq_qspi.c once all set.
Jagan.

On Tuesday 02 January 2018 03:39 PM, Jagan Teki wrote:
On Thu, Dec 28, 2017 at 8:14 PM, Lukasz Majewski lukma@denx.de wrote:
Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to comply with DM?
with this series yes, and we're working on supporting the same with non-dm.
What about "sf" command which we use now to get access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
I don't agree on adding new cmd and removing sf. It would be impractical to change all boot cmds to replace sf with spinor cmd all over U-Boot. Not to forget the envs already stored on non volatile media need updation to work with new cmd. If SPI NOR framework is to abstract all accesses to nor flash devices in U-Boot, then why cannot it replace the logic implementing cmd sf? All that is changing is that mtd/spi/* is replaced by spi-nor.c + m25p80.c. sf probe can be modified achieve what spinor dev does sf read for spinor read and so on. Board configs would just need to enable MTD related configs.

On Wed, Jan 3, 2018 at 2:18 PM, Vignesh R vigneshr@ti.com wrote:
On Tuesday 02 January 2018 03:39 PM, Jagan Teki wrote:
On Thu, Dec 28, 2017 at 8:14 PM, Lukasz Majewski lukma@denx.de wrote:
Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to comply with DM?
with this series yes, and we're working on supporting the same with non-dm.
What about "sf" command which we use now to get access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
I don't agree on adding new cmd and removing sf. It would be impractical to change all boot cmds to replace sf with spinor cmd all over U-Boot. Not to forget the envs already stored on non volatile media need updation to work with new cmd. If SPI NOR framework is to abstract all accesses to nor flash devices in U-Boot, then why cannot it replace the logic implementing cmd sf? All that is changing is that mtd/spi/* is replaced by spi-nor.c + m25p80.c. sf probe can be modified achieve what spinor dev does sf read for spinor read and so on. Board configs would just need to enable MTD related configs.
I know how hard it is adding new command in u-boot, you can understood the reason for adding new command if you follow the previous versions on this series and I hope you does. we have been adding mtd, spi-nor changes to existing mtd/spi and sf.c since from first series and observed many issue with respective to framework design(where we move spi-nor controller drivers on to mtd side) along with driver model. In v9 we worked on designing MTD UCLASS where mtd command can be commonly interfaced to all underlying flash devices. and from continuous evaluation on driver model this series we designed MTD uclass can be a generic and make run-time creation of underlying flash devices with interface type, spi-nor is one of the interface. So to make the framework suitable to command interface the new command named as spinor. adding/supporting all these new design on top of mtd/spi or sf doesn’t smooth easy which is proved on previous version. technically sf termed as spi-flash comprise of spinor and spinand, spinand can be another interface type with spinand command and adding new features on legacy code doesn't make sense to me it will eventually breaking legacy dependencies.
Will all these new framework design, driver model, feasibility to sync Linux spi-nor.c we adding relevant command, and env.

Hi Jagan,
On Wed, Jan 3, 2018 at 2:18 PM, Vignesh R vigneshr@ti.com wrote:
On Tuesday 02 January 2018 03:39 PM, Jagan Teki wrote:
On Thu, Dec 28, 2017 at 8:14 PM, Lukasz Majewski lukma@denx.de wrote:
Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to
comply with DM?
with this series yes, and we're working on supporting the same with non-dm.
What about "sf" command which we use now to get
access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
I don't agree on adding new cmd and removing sf. It would be impractical to change all boot cmds to replace sf with spinor cmd all over U-Boot. Not to forget the envs already stored on non volatile media need updation to work with new cmd. If SPI NOR framework is to abstract all accesses to nor flash devices in U-Boot, then why cannot it replace the logic implementing cmd sf? All that is changing is that mtd/spi/* is replaced by spi-nor.c + m25p80.c. sf probe can be modified achieve what spinor dev does sf read for spinor read and so on. Board configs would just need to enable MTD related configs.
I know how hard it is adding new command in u-boot,
Yes, it is. Especially command which duplicates already present functionality.
you can understood the reason for adding new command if you follow the previous versions on this series and I hope you does.
I must admit that this is the first version, which I reviewed.
The only feasible way would be to have new Kconfig option for this framework, force spinor for new boards and ..... wait.
After some long time (how long do we struggle with DM conversion/Kconfig?) we could think about removing "sf" command.
I do want to emphasis that I do agree with Vignesh - there are many boards out there, which do use sf command, with even more dangerous situation when company X only plan to update u-boot, but has envs in a separate partition. Breaking "sf" equals to brick of on-field devices....
IMHO - we shall have sf command as an alias to new "spinor" command (even by calling run_command("spinor....");).
we have been adding mtd, spi-nor changes to existing mtd/spi and sf.c since from first series and observed many issue with respective to framework design(where we move spi-nor controller drivers on to mtd side) along with driver model. In v9 we worked on designing MTD UCLASS where mtd command can be commonly interfaced to all underlying flash devices. and from continuous evaluation on driver model this series we designed MTD uclass can be a generic and make run-time creation of underlying flash devices with interface type, spi-nor is one of the interface. So to make the framework suitable to command interface the new command named as spinor. adding/supporting all these new design on top of mtd/spi or sf doesn’t smooth easy which is proved on previous version. technically sf termed as spi-flash comprise of spinor and spinand, spinand can be another interface type with spinand command and adding new features on legacy code doesn't make sense to me it will eventually breaking legacy dependencies.
Will all these new framework design, driver model, feasibility to sync Linux spi-nor.c we adding relevant command, and env.
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

Hi :
2018-01-03 18:59 GMT+08:00 Lukasz Majewski lukma@denx.de:
Hi Jagan,
On Wed, Jan 3, 2018 at 2:18 PM, Vignesh R vigneshr@ti.com wrote:
On Tuesday 02 January 2018 03:39 PM, Jagan Teki wrote:
On Thu, Dec 28, 2017 at 8:14 PM, Lukasz Majewski lukma@denx.de wrote:
Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to
comply with DM?
with this series yes, and we're working on supporting the same with non-dm.
What about "sf" command which we use now to get
access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
I don't agree on adding new cmd and removing sf. It would be impractical to change all boot cmds to replace sf with spinor cmd all over U-Boot. Not to forget the envs already stored on non volatile media need updation to work with new cmd. If SPI NOR framework is to abstract all accesses to nor flash devices in U-Boot, then why cannot it replace the logic implementing cmd sf? All that is changing is that mtd/spi/* is replaced by spi-nor.c + m25p80.c. sf probe can be modified achieve what spinor dev does sf read for spinor read and so on. Board configs would just need to enable MTD related configs.
I know how hard it is adding new command in u-boot,
Yes, it is. Especially command which duplicates already present functionality.
you can understood the reason for adding new command if you follow the previous versions on this series and I hope you does.
I must admit that this is the first version, which I reviewed.
The only feasible way would be to have new Kconfig option for this framework, force spinor for new boards and ..... wait.
After some long time (how long do we struggle with DM conversion/Kconfig?) we could think about removing "sf" command.
I do want to emphasis that I do agree with Vignesh - there are many boards out there, which do use sf command, with even more dangerous situation when company X only plan to update u-boot, but has envs in a separate partition. Breaking "sf" equals to brick of on-field devices....
IMHO - we shall have sf command as an alias to new "spinor" command (even by calling run_command("spinor....");).
we have been adding mtd, spi-nor changes to existing mtd/spi and sf.c since from first series and observed many issue with respective to framework design(where we move spi-nor controller drivers on to mtd side) along with driver model. In v9 we worked on designing MTD UCLASS where mtd command can be commonly interfaced to all underlying flash devices. and from continuous evaluation on driver model this series we designed MTD uclass can be a generic and make run-time creation of underlying flash devices with interface type, spi-nor is one of the interface. So to make the framework suitable to command interface the new command named as spinor. adding/supporting all these new design on top of mtd/spi or sf doesn’t smooth easy which is proved on previous version. technically sf termed as spi-flash comprise of spinor and spinand, spinand can be another interface type with spinand command and adding new features on legacy code doesn't make sense to me it will eventually breaking legacy dependencies.
Will all these new framework design, driver model, feasibility to sync Linux spi-nor.c we adding relevant command, and env.
Can we make a finall direction now, more than one year passed since Jagan's first version, and now it gets V10.
From the Linux kernel upstream, the spi nor framework switch to mtd/spi-nor
for a long time, and it works fine now. I hope u-boot spi nor can switch to mtd/spi-nor too, so we can share many logic with the kernel code. Besides, I have a rockchip SPI-NOR controller(SFC), I wrote two different version drivers, one is based on drivers/spi, another is based on Jagan's mtd/spi-nor , but I don't know how to move forward, as the direction is not clear.
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

On Wednesday 03 January 2018 02:54 PM, Jagan Teki wrote:
On Wed, Jan 3, 2018 at 2:18 PM, Vignesh R vigneshr@ti.com wrote:
[...]
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to comply with DM?
with this series yes, and we're working on supporting the same with non-dm.
What about "sf" command which we use now to get access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
I don't agree on adding new cmd and removing sf. It would be impractical to change all boot cmds to replace sf with spinor cmd all over U-Boot. Not to forget the envs already stored on non volatile media need updation to work with new cmd. If SPI NOR framework is to abstract all accesses to nor flash devices in U-Boot, then why cannot it replace the logic implementing cmd sf? All that is changing is that mtd/spi/* is replaced by spi-nor.c + m25p80.c. sf probe can be modified achieve what spinor dev does sf read for spinor read and so on. Board configs would just need to enable MTD related configs.
I know how hard it is adding new command in u-boot, you can understood the reason for adding new command if you follow the previous versions on this series and I hope you does. we have been adding mtd, spi-nor changes to existing mtd/spi and sf.c since from first series and observed many issue with respective to framework design(where we move spi-nor controller drivers on to mtd side) along with driver model.
I don't remember. Its been a year since RFC v8 which used cmd_sf.c. Looking at the mailing list archives, I don't see any comments opposing cmd_sf.c and move to mtd command, that you used in v9
In v9 we worked on designing MTD UCLASS where mtd command can be commonly interfaced to all underlying flash devices. and from continuous evaluation on driver model this series we designed MTD uclass can be a generic and make run-time creation of underlying flash devices with interface type, spi-nor is one of the interface. So to make the framework suitable to command interface the new command named as spinor. adding/supporting all these new design on top of mtd/spi or sf doesn’t smooth easy which is proved on previous version.
Could you point to/summarize the exact problems that force us to move to spinor cmd? I failed to find them on archives.
technically sf termed as spi-flash comprise of spinor and spinand, spinand can be another interface type with spinand command and adding new features on legacy code doesn't make sense to me it will eventually breaking legacy dependencies.
AFAIK, spinand is not yet supported and can be added under MTD with different U-Boot cmd to access it.

Hi Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Tuesday, January 02, 2018 3:39 PM To: Lukasz Majewski lukma@denx.de Cc: U-Boot Mailing List u-boot@lists.denx.de; Tom Rini trini@konsulko.com Subject: Re: [U-Boot] [PATCH v10 00/27] dm: Generic MTD Subsystem, with SPI-NOR interface
On Thu, Dec 28, 2017 at 8:14 PM, Lukasz Majewski lukma@denx.de wrote:
Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to comply with DM?
with this series yes, and we're working on supporting the same with non- dm.
What about "sf" command which we use now to get access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
======================================= mtd-uclass.c
^^^^^ - here we will have a generic MTD layer (as it is in Linux)
it is not like in Linux, leaving ops names apart all the code is for creating mtd device for underlying flash devices in the form of interface type during bind. the interface types as of now is spi-nor it can be nand, nor, dataflash etc in future if could be.
======================================= spi-nor-uclass.c ======================================= spi-nor.c
^^^^^^ - why do we need to have spi-nor.c ? Cannot we have its functionality in the spi-nor-uclass.c ? (I'm just curious)
spi-nor-uclass.c is an uclass driver for underlying UCLASS_SPI_NOR drivers like m25p80, zynq_qspinor etc but the spi-nor.c is the actual spi-nor core code which handle actual flash specific stuff. and also syncing Linux stuff(if require) becomes easy with spi-nor.c as a separate file.
======================================= m25p80.c zynq_qspinor.c
^^^^^ - this is the ^^^^^^^^ - [*] "generic" spi-nor driver used in Linux. I suppose that it will be reused here?
yes - name reused because, I do think the Linux and U-Boot development on spi-nor will follow in-line at some point but the most of code is following U- Boot driver-model.
======================================= spi-uclass.c
^^^^^^^ - why do we need this uclass?
This is an existing uclass spi driver for underlying UCLASS_SPI drivers like drivers/spi/zynq_qspi.c etc
======================================= zynq_qspi.c
^^^^^ - This is probably the low-level driver for zynq QSPI IP block - why do we have zynq_qspinor.c file above [*]? What is the difference/need of such division?
Both the drivers are same, the reason for zynq_qspinor is for testing framework work for two directions like
- spi driver interface side through m25p80 cmd/spinor->mtd->spi-nor-
uclass->spi-nor->m25p80->zynq_qspi.c
- direct spi-nor controller side
spinor->mtd->spi-nor-uclass->spi-nor->zynq_qspinor
Will remove drivers/spi/zynq_qspi.c once all set.
As mentioned in Zynq TRM, Zynq qspi controller can work in legacy spi mode as well which is same as regular spi controller. As you mentioned if you remove from drivers/spi/zynq_qspi and moving it to spi-nor, none can use it in spi legacy mode. Right?
Thanks, Siva
Jagan.
-- Jagan Teki Free Software Engineer | www.openedev.com U-Boot, Linux | Upstream Maintainer Hyderabad, India. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

On Tue, Jan 23, 2018 at 3:20 PM, Siva Durga Prasad Paladugu sivadur@xilinx.com wrote:
Hi Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Tuesday, January 02, 2018 3:39 PM To: Lukasz Majewski lukma@denx.de Cc: U-Boot Mailing List u-boot@lists.denx.de; Tom Rini trini@konsulko.com Subject: Re: [U-Boot] [PATCH v10 00/27] dm: Generic MTD Subsystem, with SPI-NOR interface
On Thu, Dec 28, 2017 at 8:14 PM, Lukasz Majewski lukma@denx.de wrote:
Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to comply with DM?
with this series yes, and we're working on supporting the same with non- dm.
What about "sf" command which we use now to get access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
======================================= mtd-uclass.c
^^^^^ - here we will have a generic MTD layer (as it is in Linux)
it is not like in Linux, leaving ops names apart all the code is for creating mtd device for underlying flash devices in the form of interface type during bind. the interface types as of now is spi-nor it can be nand, nor, dataflash etc in future if could be.
======================================= spi-nor-uclass.c ======================================= spi-nor.c
^^^^^^ - why do we need to have spi-nor.c ? Cannot we have its functionality in the spi-nor-uclass.c ? (I'm just curious)
spi-nor-uclass.c is an uclass driver for underlying UCLASS_SPI_NOR drivers like m25p80, zynq_qspinor etc but the spi-nor.c is the actual spi-nor core code which handle actual flash specific stuff. and also syncing Linux stuff(if require) becomes easy with spi-nor.c as a separate file.
======================================= m25p80.c zynq_qspinor.c
^^^^^ - this is the ^^^^^^^^ - [*] "generic" spi-nor driver used in Linux. I suppose that it will be reused here?
yes - name reused because, I do think the Linux and U-Boot development on spi-nor will follow in-line at some point but the most of code is following U- Boot driver-model.
======================================= spi-uclass.c
^^^^^^^ - why do we need this uclass?
This is an existing uclass spi driver for underlying UCLASS_SPI drivers like drivers/spi/zynq_qspi.c etc
======================================= zynq_qspi.c
^^^^^ - This is probably the low-level driver for zynq QSPI IP block - why do we have zynq_qspinor.c file above [*]? What is the difference/need of such division?
Both the drivers are same, the reason for zynq_qspinor is for testing framework work for two directions like
- spi driver interface side through m25p80 cmd/spinor->mtd->spi-nor-
uclass->spi-nor->m25p80->zynq_qspi.c
- direct spi-nor controller side
spinor->mtd->spi-nor-uclass->spi-nor->zynq_qspinor
Will remove drivers/spi/zynq_qspi.c once all set.
As mentioned in Zynq TRM, Zynq qspi controller can work in legacy spi mode as well which is same as regular spi controller. As you mentioned if you remove from drivers/spi/zynq_qspi and moving it to spi-nor, none can use it in spi legacy mode. Right?
AFAIK, none used till know for generic slaves except for spi-nor. and main motive here to have support for controller features like BAR, dual flash however writing this on drivers/spi make difficult have these.

Hi Jagan,
-----Original Message----- From: Jagan Teki [mailto:jagannadh.teki@gmail.com] Sent: Tuesday, January 23, 2018 3:29 PM To: Siva Durga Prasad Paladugu sivadur@xilinx.com Cc: Lukasz Majewski lukma@denx.de; U-Boot Mailing List <u- boot@lists.denx.de>; Tom Rini trini@konsulko.com Subject: Re: [U-Boot] [PATCH v10 00/27] dm: Generic MTD Subsystem, with SPI-NOR interface
On Tue, Jan 23, 2018 at 3:20 PM, Siva Durga Prasad Paladugu sivadur@xilinx.com wrote:
Hi Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Tuesday, January 02, 2018 3:39 PM To: Lukasz Majewski lukma@denx.de Cc: U-Boot Mailing List u-boot@lists.denx.de; Tom Rini trini@konsulko.com Subject: Re: [U-Boot] [PATCH v10 00/27] dm: Generic MTD Subsystem, with SPI-NOR interface
On Thu, Dec 28, 2017 at 8:14 PM, Lukasz Majewski lukma@denx.de wrote:
Hi Jagan,
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI-NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
I must admit that I'm a bit confused....
If you don't mind I would like to ask for clarification of a few things:
======================================= cmd/spinor.c
^^^^^ - this would be a new set of commands to comply with DM?
with this series yes, and we're working on supporting the same with non- dm.
What about "sf" command which we use now to get access to SPI-NOR memory? A lot of boards already use "sf" command... which may be tricky to replace.
end-goal will be replace sf with spinor command and removal of 'sf' will be done when the new spi-nor framework stable enough to handle all scenarios which are spi-flash supporting as of now.
======================================= mtd-uclass.c
^^^^^ - here we will have a generic MTD layer (as it is in Linux)
it is not like in Linux, leaving ops names apart all the code is for creating mtd device for underlying flash devices in the form of interface type during bind. the interface types as of now is spi-nor it can be nand, nor, dataflash etc in future if could be.
======================================= spi-nor-uclass.c ======================================= spi-nor.c
^^^^^^ - why do we need to have spi-nor.c ? Cannot we have its functionality in the spi-nor-uclass.c ? (I'm just curious)
spi-nor-uclass.c is an uclass driver for underlying UCLASS_SPI_NOR drivers like m25p80, zynq_qspinor etc but the spi-nor.c is the actual spi-nor core code which handle actual flash specific stuff. and also syncing Linux stuff(if require) becomes easy with spi-nor.c as a separate file.
======================================= m25p80.c zynq_qspinor.c
^^^^^ - this is the ^^^^^^^^ - [*] "generic" spi-nor driver used in Linux. I suppose that it will be reused here?
yes - name reused because, I do think the Linux and U-Boot development on spi-nor will follow in-line at some point but the most of code is following U- Boot driver-model.
======================================= spi-uclass.c
^^^^^^^ - why do we need this uclass?
This is an existing uclass spi driver for underlying UCLASS_SPI drivers like drivers/spi/zynq_qspi.c etc
======================================= zynq_qspi.c
^^^^^ - This is probably the low-level driver for zynq QSPI IP block - why do we have zynq_qspinor.c file above [*]? What is the difference/need of such division?
Both the drivers are same, the reason for zynq_qspinor is for testing framework work for two directions like
- spi driver interface side through m25p80 cmd/spinor->mtd->spi-nor-
uclass->spi-nor->m25p80->zynq_qspi.c
- direct spi-nor controller side
spinor->mtd->spi-nor-uclass->spi-nor->zynq_qspinor
Will remove drivers/spi/zynq_qspi.c once all set.
As mentioned in Zynq TRM, Zynq qspi controller can work in legacy spi
mode as well which is same as regular spi controller.
As you mentioned if you remove from drivers/spi/zynq_qspi and moving
it to spi-nor, none can use it in spi legacy mode. Right?
AFAIK, none used till know for generic slaves except for spi-nor. and main motive here to have support for controller features like BAR, dual flash however writing this on drivers/spi make difficult have these.
This means, we have to duplicate driver if someone wants to use for legacy spi which is bothering to me.
Thanks, Siva

Hi Jagan,
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Jagan Teki Sent: Thursday, December 28, 2017 11:42 AM To: u-boot@lists.denx.de Cc: Tom Rini trini@konsulko.com Subject: [U-Boot] [PATCH v10 00/27] dm: Generic MTD Subsystem, with SPI-NOR interface
Compared to previous series’s [1], [2], [3] and [4] this patch set redefined most of the implementation suitable to fit into existing driver-model.
MTD is generic subsystem for underlying flash devices like nand, parallel nor, spinor, dataflash etc. So to drive this theory with driver model(with an example of block layer) mtd is common device interaction for most of memory technology flashes like nand, parallel nor, spinor, dataflash etc, these are treated as interface types wrt u-boot driver model.
Once the respective interface driver bind happen, the uclass driver will pass an 'interface type' to mtd layer to create device for it, for example once spinor ULASS_SPI_NOR driver bind happen, the uclass driver of spinor will pass MTD_IF_TYPE_SPI_NOR interface type to create mtd device for spinor devices.
SPI-NOR:
Some of the SPI device drivers at drivers/spi not a real spi controllers, Unlike normal/generic SPI controllers they operates only with SPI-NOR flash devices. these were technically termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c
The problem with these were resides at drivers/spi is entire SPI layer becomes SPI-NOR flash oriented which is absolutely a wrong indication where SPI layer getting effected more with flash operations - So this SPI- NOR core will resolve this issue by separating all SPI-NOR flash operations from spi layer and creats a generic layer called SPI-NOR core which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver.
Sorry for very late response. What's your approach on a controller that supports both flash with some flash specific functionalities(Like different flash connection topologies) and legacy spi devices.
Thanks, Siva
======================================= cmd/spinor.c ======================================= mtd-uclass.c ======================================= spi-nor-uclass.c ======================================= spi-nor.c ======================================= m25p80.c zynq_qspinor.c ======================================= spi-uclass.c ======================================= zynq_qspi.c ======================================= #####SPI NOR chip###### =======================================
Changes for v10:
- Update Simon's R-B tag
- Add mtd dm test case
- implemented entire code wrt MTD, with interface type
code size:
before: $ arm-linux-gnueabi-size u-boot text data bss dec hex filename 473712 15152 222568 711432 adb08 u- boot $ du -hs u-boot-dtb.img 488K u-boot-dtb.img
after: $ arm-linux-gnueabi-size u-boot text data bss dec hex filename 470124 14352 222584 707060 ac9f4 u- boot $ du -hs u-boot-dtb.img 484K u-boot-dtb.img
approximately 4KiB but DM_SPI_FLASH still there which can be removed once support added in SPL
test log:
Zynq> spinor spinor - SPI-NOR Sub-system
Usage: spinor list - show list of spinor devices spinor info - show current spinor device info spinor dev [devnum] - show or set current spinor device spinor erase offset len - erase 'len' bytes from 'offset' spinor write addr to len - write 'len' bytes to 'to' from 'addr' spinor read addr from len - read 'len' bytes from 'from' to 'addr' spinor protect lock/unlock sector len - protect/unprotect 'len' bytes starting at address 'sector' Zynq> spinor list flash@0: 0 spi-nor@e000d000: 1 Zynq> spinor dev 0 switch to dev #0, OK spinor0 is current device Zynq> spinor info bus: flash@0: 0 device: s25fl128s_64k page size: 256 B erase size: 64 KiB size: 16 MiB Zynq> spinor erase 0xE00000 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Erased: OK Zynq> mw.b 0x100 0xcc 0x100000 Zynq> spinor write 0x100 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Written: OK Zynq> spinor read 0x3000000 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Read: OK Zynq> cmp.b 0x3000000 0x100 0x100000 Total of 1048576 byte(s) were the same Zynq> spinor dev 1 switch to dev #1, OK spinor1 is current device Zynq> spinor info bus: spi-nor@e000d000: 1 device: s25fl128s_64k page size: 256 B erase size: 64 KiB size: 16 MiB Zynq> spinor erase 0xE00000 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Erased: OK Zynq> mw.b 0x100 0xbb 0x100000 Zynq> spinor write 0x100 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Written: OK Zynq> spinor read 0x3000000 0xE00000 0x100000 device 0 offset 0xe00000, size 0x100000 SPI-NOR: 1048576 bytes @ 0xe00000 Read: OK Zynq> cmp.b 0x3000000 0x100 0x100000 Total of 1048576 byte(s) were the same
WIP:
- to support non-dm code
- to support spinor SPL
Repo:
$ git clone git://git.denx.de/u-boot-spi.git $ cd u-boot-spi $ git checkout -b mtd-spinor-working origin/mtd-spinor-working
[1] https://lists.denx.de/pipermail/u-boot/2016-October/271459.html [2] http://lists.denx.de/pipermail/u-boot/2016-March/249286.html [3] http://lists.denx.de/pipermail/u-boot/2016-February/245418.html [4] [PATCH RFC v8 00/16] SPI-NOR/MTD addition
Jagan Teki (27): mtd: Add mtd core ops mtd: add mtd device create operations mtd: add SPI-NOR core support mtd: spi-nor: sync/modify sst write operations mtd: spi-nor: sync/modify lock operations mtd: spi-nor: Kconfig: Add MTD_SPI_NOR entry mtd: spi-nor: Kconfig: Add MTD_SPI_NOR_USE_4K_SECTORS mtd: spi-nor: Kconfig: Add SPI_NOR_MISC entry mtd: spi-nor: Kconfig: Add SPI_NOR_MACRONIX entry mtd: spi-nor: Kconfig: Add SPI_NOR_SPANSION entry mtd: spi-nor: Kconfig: Add SPI_NOR_STMICRO entry mtd: spi-nor: Kconfig: Add SPI_NOR_SST entry mtd: spi-nor: Kconfig: Add SPI_NOR_WINBOND entry mtd-uclass: use platdata_auto_alloc spi: Add spi_write_then_read mtd: spi-nor: Add m25p80 driver mtd: spi-nor: Kconfig: Add MTD_M25P80 entry mtd: spi-nor: Add zynq qspinor driver mtd: spi-nor: zynq_qspi: Kconfig: Add MTD_ZYNQ mtd: spi-nor: Add 4-byte addresswidth support cmd: add spinor cmd support cmd: spinor: sync/update protect command board_r: initialize spi_nor env: add spi-nor environment arm: dts: zynq: Add zynq-qspinor node dm: zynq: microzed: enable MTD/SPI-NOR framework test: dm: add tests for mtd devices
Makefile | 1 + arch/arm/dts/zynq-7000.dtsi | 12 + arch/arm/dts/zynq-microzed.dts | 12 +- cmd/Kconfig | 5 + cmd/Makefile | 1 + cmd/nvedit.c | 1 + cmd/spinor.c | 326 ++++++++++++ common/board_r.c | 13 + configs/sandbox_defconfig | 8 +- configs/zynq_microzed_defconfig | 17 +- drivers/mtd/Kconfig | 2 + drivers/mtd/Makefile | 7 +- drivers/mtd/mtd-uclass.c | 203 +++++++- drivers/mtd/spi-nor/Kconfig | 89 ++++ drivers/mtd/spi-nor/Makefile | 15 + drivers/mtd/spi-nor/m25p80.c | 251 ++++++++++ drivers/mtd/spi-nor/spi-nor-ids.c | 184 +++++++ drivers/mtd/spi-nor/spi-nor-uclass.c | 160 ++++++ drivers/mtd/spi-nor/spi-nor.c | 940 +++++++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/zynq_qspinor.c | 622 +++++++++++++++++++++++ drivers/spi/spi-uclass.c | 24 + env/Kconfig | 27 + env/Makefile | 1 + env/env.c | 2 + env/spinor.c | 113 +++++ include/dm/uclass-id.h | 1 + include/environment.h | 1 + include/linux/mtd/mtd.h | 14 + include/linux/mtd/spi-nor.h | 223 +++++++++ include/mtd.h | 176 +++++++ include/spi.h | 20 + test/dm/Makefile | 1 + test/dm/mtd.c | 33 ++ 33 files changed, 3493 insertions(+), 12 deletions(-) create mode 100644 cmd/spinor.c create mode 100644 drivers/mtd/spi-nor/Kconfig create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/m25p80.c create mode 100644 drivers/mtd/spi- nor/spi-nor-ids.c create mode 100644 drivers/mtd/spi-nor/spi-nor-uclass.c create mode 100644 drivers/mtd/spi-nor/spi-nor.c create mode 100644 drivers/mtd/spi-nor/zynq_qspinor.c create mode 100644 env/spinor.c create mode 100644 include/linux/mtd/spi-nor.h create mode 100644 test/dm/mtd.c
-- 2.7.4
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot
participants (10)
-
Andy Yan
-
Boris Brezillon
-
Cyrille Pitchen
-
Jagan Teki
-
Jagan Teki
-
Lukasz Majewski
-
Prabhakar Kushwaha
-
Simon Glass
-
Siva Durga Prasad Paladugu
-
Vignesh R