[U-Boot] dm: Introduce SPI-NOR framework

Jagan Teki jteki at openedev.com
Thu Dec 3 11:10:56 CET 2015


Hi Simon,

I re-phrase all the question from previous thread and continue in this 
for more discussion on spi-nor development.

 > Is it intended that SPI flash should be a driver for the MTD uclass?
 > Others would be NAND and CFI. From what I can tell MTD has the same
 > operations as SPI flash (erase, read, write) and adds a lot more.
 >
 > Or is SPI flash really a separate uclass from MTD, with SPI flash
 > being at a higher level?

Based on my "sf: MTD support" series SPI flash is a separate uclass from 
MTD and it uses mtd_info operations.

 >> cmd_sf
 >> =======
 >> MTD
 >> =======
 >> spi-nor or spi-flash
 >> ===============
 >> "spi-nor to spi drivers" and spi-nor controller driver
 >> =======================================
 > Your diagram above suggests that MTD calls into SPI NOR or SPI flash,
 > but when I look at (for exampe) spi_flash_erase(), it is calling
 > mtd_erase(), suggesting that it is above MTD in the software stack,
 > the opposite of your diagram above.
 >

Will explain this clearly in below description.

 > Conceptually this seems problematic.
 >
 > SPI flash is a uclass and supports driver model. It has operations,
 > etc. Your patches remove the operations in favour of calling MTD. But
 > MTD does not support driver model. This is getting really messy.
 >

Will explain this clearly in below description.


Introducing SPI-NOR:
********************

Some of the spi drivers or spi controllers at drivers/spi/*
not a real spi controllers, unlike normal spi controllers
those operates varienty of spi devices among spi-nor flash is
one of them but instead these were specially designed for
to operate spi-nor flash devices - these are spi-nor controllers.
example: drivers/spi/fsl_qspi.c

The problem with these were sitting at drivers/spi is entire
spi layer becomes spi-nor flash oriented which is absolutely
wrong indication where spi layer gets effected more with
flash operations - So this SPI-NOR will resolve this issue
by separating all spi-nor flash operations from spi layer
and by creating a generic layer called SPI-NOR core where it
deals with all SPI-NOR flash activities. The basic idea is
taken from Linux spi-nor framework.

SPI-NOR Block diagram:
*********************

=========
cmd_sf.c
===========
spi_flash.h
===========
mtdcore.c
===========
spi-nor.c
========================
m25p80.c     fsl_qspi.c
==========   ===========
spi-uclass   spi-nor hw
==========   ===========
spi_drivers
===========
spi-bus hw
==========

Note:
- spi-nor.c is a common core for m25p80.c which is a spi-nor to spi 
driver interface layer and for fsl_qspi.c which is a typical spi-nor
controller driver.
- Challenging task is about probe.

SPI-NOR Development plan:
*************************

a) Adding MTD core support:
    From command point of view the spi-flash is like a mtd device 
having erase/write/read ops with offset, addr and size, but from lower 
layers the spi-flash becomes either spi-nor or spi-nand so they have 
their own specific features like struct spi_nor {}.

    This is the reason for calling MTD from command layer and the lower 
layer as SPI_NOR uclass.

b) Drop spi_flash uclass:
    Since spi_flash is a generic term for serial flash devices among
    these spi-nor and spi-nand are the real device categories so add
    driver model to these categories.

I sent the series [1] for above a) and b)

c) Add spi-nor support (mean Adding SPI-NOR dm drivers) the next step.

Detailed SPI-NOR with examples:
******************************

include/spi_flash.h
-------------------

struct spi_flash {
    struct spi_nor  *nor;
};

include/linux/mtd/spi-nor.h
---------------------------

struct spi_nor {
    struct udevice   *dev;
    struct mtd_info  *info;

    // spi-nor hooks
    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_mmap)(struct udevice *dev, void *data, void *offset,
                 size_t len);
    int (*read)(struct udevice *dev, const u8 *opcode, size_t cmd_len,
		void *data, size_t data_len);
    int (*write)(struct udevice *dev, const u8 *cmd, size_t cmd_len,
                 const void *data, size_t data_len);
};

drivers/mtd/spi-nor/spi-nor.c
-----------------------------

static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	// call to spi-nor erase nor->erase()	
}

static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
                         size_t *retlen, u_char *buf)
{
	// call to spi-nor erase nor->read()
}

static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
         size_t *retlen, const u_char *buf)
{
	// call to spi-nor erase nor->erase()
}

int spi_nor_scan(struct spi_nor *nor)
{
     struct mtd_info *mtd = nor->mtd;

     // read_jedec

     // assign mtd hooks

     // other spi-nor stuff

}

drivers/mtd/spi-nor/m25p80.c
----------------------------

struct m25p80 {
         struct spi_slave *spi;
         struct mtd_info   mtd;
};

static int m25p80_probe(struct udevice *dev)
{
     struct spi_slave *spi   = dev_get_parentdata(dev);
     struct spi_nor   *nor   = dev_get_uclass_priv(dev);
     struct m25p80    *flash = dev_get_priv(dev);
     struct spi_flash *sflash;

     spi_nor_scan(nor);

     add_mtd_device(&flash->mtd);

}

// spi-nor hooks
static const struct dm_spi_flash_ops m25p80_ops = {
	.read  = m25p80_read,
	.write = m25p80_write,
	.erase = m25p80_erase,
};

static const struct udevice_id m25p80_ids[] = {
	{ .compatible = "jedec,spi-nor" },
	{ }
};

U_BOOT_DRIVER(m25p80) = {
	.name		= "m25p80",
	.id		= UCLASS_SPI_NOR,
	.of_match	= m25p80_ids,
	.probe		= m25p80_probe,
	.priv_auto_alloc_size = sizeof(struct m25p80),
         .ops		= &m25p80_ops,
};

drivers/mtd/spi-nor/fsl-qspi.c
------------------------------

struct fsl_qspi {
         struct mtd_info   mtd;
};

static int fsl_probe(struct udevice *dev)
{
     struct spi_nor   *nor = dev_get_uclass_priv(dev);
     struct fsl_qspi   *q  = dev_get_priv(dev);
     struct spi_flash *sflash;

     spi_nor_scan(nor);

     add_mtd_device(&q->mtd);

}

// spi-nor hooks
static const struct dm_spi_nor_ops fsl_qspi_ops = {
	.read  = fsl_qspi_read,
	.write = fsl_qspi_write,
	.erase = fsl_qspi_erase,
};

static const struct udevice_id fsl_qspi_ids[] = {
	{ .compatible = "fsl,vf610-qspi" },
	{ }
};

U_BOOT_DRIVER(fsl_qspi) = {
	.name		= "fsl_qspi",
	.id		= UCLASS_SPI_NOR,
	.of_match	= fsl_qspi_ids,
	.probe		= fsl_qspi_probe,
	.priv_auto_alloc_size = sizeof(struct fsl_qspi),
	ops		= &fsl_qspi_ops,
};

About non-dm handling
*********************

We still maintain the driver similar to m25p80.c for non-dm interface 
till all spi-drivers converted into dm.

[1] https://www.mail-archive.com/u-boot@lists.denx.de/msg193802.html


More information about the U-Boot mailing list