Hi Jagan: 2017-12-28 14:12 GMT+08:00 Jagan Teki <ja...@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 <suneelgli...@gmail.com> > Signed-off-by: Jagan Teki <ja...@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 <ja...@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 <ja...@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 <ja...@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 <ja...@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/drivers/mtd/spi-nor/spi-nor.c + 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 <ja...@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 > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot