diff mbox series

[U-Boot,v10,03/27] mtd: add SPI-NOR core support

Message ID 1514441553-27543-4-git-send-email-jagan@amarulasolutions.com
State Rejected
Delegated to: Tom Rini
Headers show
Series dm: Generic MTD Subsystem, with SPI-NOR interface | expand

Commit Message

Jagan Teki Dec. 28, 2017, 6:12 a.m. UTC
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.

Comments

Andyshrk Dec. 29, 2017, 9:25 a.m. UTC | #1
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
>
Prabhakar Kushwaha Jan. 2, 2018, 10:18 a.m. UTC | #2
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
Jagan Teki Jan. 2, 2018, 10:31 a.m. UTC | #3
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.
Raghavendra, Vignesh Jan. 3, 2018, 8:49 a.m. UTC | #4
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 */
>
Jagan Teki Jan. 3, 2018, 9:32 a.m. UTC | #5
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!
Raghavendra, Vignesh Jan. 3, 2018, 9:56 a.m. UTC | #6
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.
Jagan Teki Jan. 3, 2018, 10:07 a.m. UTC | #7
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.
Andyshrk Jan. 4, 2018, 2:33 a.m. UTC | #8
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/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 <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
>
Prabhakar Kushwaha Feb. 28, 2018, 9:42 a.m. UTC | #9
[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/43cc45764b975bfbb191de3f6a37e073da1a2706


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
Boris Brezillon Feb. 28, 2018, 12:10 p.m. UTC | #10
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/43cc45764b975bfbb191de3f6a37e073da1a2706
> 
> 
> 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
Prabhakar Kushwaha March 6, 2018, 5:59 a.m. UTC | #11
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/43cc45764b975bfbb191de3f6a37e073da1a2706
> > 
> > 
> > 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
Jagan Teki March 6, 2018, 6:38 a.m. UTC | #12
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/43cc45764b975bfbb191de3f6a37e073da1a2706

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
diff mbox series

Patch

=======================================
             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 */