diff mbox

[v6,10/15] nand: spi: add basic blocks for infrastructure

Message ID 1495609631-18880-11-git-send-email-peterpandong@micron.com
State Changes Requested
Delegated to: Boris Brezillon
Headers show

Commit Message

Peter Pan 潘栋 (peterpandong) May 24, 2017, 7:07 a.m. UTC
This is the first commit for spi nand framkework.
This commit is to add add basic building blocks
for the SPI NAND infrastructure.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 drivers/mtd/nand/Kconfig      |   1 +
 drivers/mtd/nand/Makefile     |   1 +
 drivers/mtd/nand/spi/Kconfig  |   5 +
 drivers/mtd/nand/spi/Makefile |   1 +
 drivers/mtd/nand/spi/core.c   | 419 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/spinand.h   | 267 +++++++++++++++++++++++++++
 6 files changed, 694 insertions(+)
 create mode 100644 drivers/mtd/nand/spi/Kconfig
 create mode 100644 drivers/mtd/nand/spi/Makefile
 create mode 100644 drivers/mtd/nand/spi/core.c
 create mode 100644 include/linux/mtd/spinand.h

Comments

Boris Brezillon May 29, 2017, 9:51 p.m. UTC | #1
On Wed, 24 May 2017 15:07:06 +0800
Peter Pan <peterpandong@micron.com> wrote:

> This is the first commit for spi nand framkework.

					^ framework

> This commit is to add add basic building blocks

	      is adding basic ...

> for the SPI NAND infrastructure.
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---

[...]

> +
> +/**
> + * devm_spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance

Let's drop those [SPI NAND Interface] specifier. It's pretty obvious
that this is part of the spi-nand API, since those symbols are exported.

> + * @dev: pointer to device model structure
> + */
> +struct spinand_device *devm_spinand_alloc(struct device *dev)

You can pass a pointer to the nand_controller object driving the
nand_device here.

> +{
> +	struct spinand_device *spinand;
> +	struct mtd_info *mtd;
> +
> +	spinand = devm_kzalloc(dev, sizeof(*spinand), GFP_KERNEL);
> +	if (!spinand)
> +		return ERR_PTR(-ENOMEM);
> +
> +	spinand_set_of_node(spinand, dev->of_node);
> +	mutex_init(&spinand->lock);
> +	spinand->dev = dev;

Hm, I don't think this is correct. The device here is likely to
represent the controller not the SPI NAND device. For the generic spi
nand controller, I agree, it's the same, but, in case you have one
controller that is attached several spi devices, it's not.

How about putting the struct device pointer in nand_controller and then
pass the controller to this spinand_alloc() function.

> +	mtd = spinand_to_mtd(spinand);
> +	mtd->dev.parent = dev;
> +
> +	return spinand;
> +}
> +EXPORT_SYMBOL_GPL(devm_spinand_alloc);
> +
> +/**
> + * spinand_init - [SPI NAND Interface] initialize the SPI NAND device
> + * @spinand: SPI NAND device structure
> + */
> +int spinand_init(struct spinand_device *spinand)
> +{
> +	struct mtd_info *mtd = spinand_to_mtd(spinand);
> +	struct nand_device *nand = mtd_to_nand(mtd);
> +	int ret;
> +
> +	ret = spinand_detect(spinand);
> +	if (ret) {
> +		dev_err(spinand->dev,
> +			"Detect SPI NAND failed with error %d.\n", ret);
> +		goto err_out;

		return ret;


> +	}

I'd still prefer to move the detection step out of this _init()
function even if this implies duplicating the _detect()+_init()
sequence in all drivers. Maybe you can provide a wrapper called
spinand_detect_and_init() to do both in one go.

> +
> +	spinand_set_rd_wr_op(spinand);
> +
> +	/*
> +	 * Use kzalloc() instead of devm_kzalloc() here, beacause some drivers
> +	 * may use this buffer for DMA access.
> +	 * Memory allocated by devm_ does not guarantee DMA-safe alignment.
> +	 */
> +	spinand->buf = kzalloc(nand_page_size(nand) +
> +			       nand_per_page_oobsize(nand),
> +			       GFP_KERNEL);
> +	if (!spinand->buf) {
> +		ret = -ENOMEM;
> +		goto err_out;

		return -ENOMEM;

> +	}
> +
> +	spinand->oobbuf = spinand->buf + nand_page_size(nand);
> +
> +	ret = spinand_manufacturer_init(spinand);
> +	if (ret) {
> +		dev_err(spinand->dev,
> +			"Manufacurer init SPI NAND failed with err %d.\n",
> +			ret);
> +		goto err_free_buf;
> +	}
> +
> +	mtd->name = spinand->name;
> +	mtd->size = nand_size(nand);
> +	mtd->erasesize = nand_eraseblock_size(nand);
> +	mtd->writesize = nand_page_size(nand);
> +	mtd->writebufsize = mtd->writesize;
> +	mtd->owner = THIS_MODULE;
> +	mtd->type = MTD_NANDFLASH;
> +	mtd->flags = MTD_CAP_NANDFLASH;
> +	mtd->oobsize = nand_per_page_oobsize(nand);
> +	/*
> +	 * Right now, we don't support ECC, so let the whole oob
> +	 * area is available for user.
> +	 */
> +	mtd->oobavail = mtd->oobsize;
> +
> +	/* After power up, all blocks are locked, so unlock it here. */
> +	spinand_lock_block(spinand, BL_ALL_UNLOCKED);
> +
> +	return 0;
> +
> +err_free_buf:
> +	kfree(spinand->buf);
> +err_out:

You can get rid of err_out.

> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(spinand_init);
> +
Peter Pan 潘栋 (peterpandong) May 31, 2017, 7:02 a.m. UTC | #2
Hi Boris,

> On 30 May 2017, at 05:51, Boris Brezillon <boris.brezillon@free-electrons.com> wrote:
> 
> On Wed, 24 May 2017 15:07:06 +0800
> Peter Pan <peterpandong@micron.com> wrote:
> 
>> This is the first commit for spi nand framkework.
> 
>                    ^ framework
> 
>> This commit is to add add basic building blocks
> 
>          is adding basic ...
> 
>> for the SPI NAND infrastructure.
>> 
>> Signed-off-by: Peter Pan <peterpandong@micron.com>
>> ---
> 
> [...]
> 
>> +
>> +/**
>> + * devm_spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance
> 
> Let's drop those [SPI NAND Interface] specifier. It's pretty obvious
> that this is part of the spi-nand API, since those symbols are exported.

Okay 

> 
>> + * @dev: pointer to device model structure
>> + */
>> +struct spinand_device *devm_spinand_alloc(struct device *dev)
> 
> You can pass a pointer to the nand_controller object driving the
> nand_device here.
> 
>> +{
>> +    struct spinand_device *spinand;
>> +    struct mtd_info *mtd;
>> +
>> +    spinand = devm_kzalloc(dev, sizeof(*spinand), GFP_KERNEL);
>> +    if (!spinand)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    spinand_set_of_node(spinand, dev->of_node);
>> +    mutex_init(&spinand->lock);
>> +    spinand->dev = dev;
> 
> Hm, I don't think this is correct. The device here is likely to
> represent the controller not the SPI NAND device. For the generic spi
> nand controller, I agree, it's the same, but, in case you have one
> controller that is attached several spi devices, it's not.

Yes, you are right. I only thought about generic spi bus when I was
writing the code.

> 
> How about putting the struct device pointer in nand_controller and then
> pass the controller to this spinand_alloc() function.

Good solution

> 
>> +    mtd = spinand_to_mtd(spinand);
>> +    mtd->dev.parent = dev;
>> +
>> +    return spinand;
>> +}
>> +EXPORT_SYMBOL_GPL(devm_spinand_alloc);
>> +
>> +/**
>> + * spinand_init - [SPI NAND Interface] initialize the SPI NAND device
>> + * @spinand: SPI NAND device structure
>> + */
>> +int spinand_init(struct spinand_device *spinand)
>> +{
>> +    struct mtd_info *mtd = spinand_to_mtd(spinand);
>> +    struct nand_device *nand = mtd_to_nand(mtd);
>> +    int ret;
>> +
>> +    ret = spinand_detect(spinand);
>> +    if (ret) {
>> +        dev_err(spinand->dev,
>> +            "Detect SPI NAND failed with error %d.\n", ret);
>> +        goto err_out;
> 
>        return ret;
> 
> 
>> +    }
> 
> I'd still prefer to move the detection step out of this _init()
> function even if this implies duplicating the _detect()+_init()
> sequence in all drivers. Maybe you can provide a wrapper called
> spinand_detect_and_init() to do both in one go.

Okay for me 
> 
>> +
>> +    spinand_set_rd_wr_op(spinand);
>> +
>> +    /*
>> +     * Use kzalloc() instead of devm_kzalloc() here, beacause some drivers
>> +     * may use this buffer for DMA access.
>> +     * Memory allocated by devm_ does not guarantee DMA-safe alignment.
>> +     */
>> +    spinand->buf = kzalloc(nand_page_size(nand) +
>> +                   nand_per_page_oobsize(nand),
>> +                   GFP_KERNEL);
>> +    if (!spinand->buf) {
>> +        ret = -ENOMEM;
>> +        goto err_out;
> 
>        return -ENOMEM;
> 
>> +    }
>> +
>> +    spinand->oobbuf = spinand->buf + nand_page_size(nand);
>> +
>> +    ret = spinand_manufacturer_init(spinand);
>> +    if (ret) {
>> +        dev_err(spinand->dev,
>> +            "Manufacurer init SPI NAND failed with err %d.\n",
>> +            ret);
>> +        goto err_free_buf;
>> +    }
>> +
>> +    mtd->name = spinand->name;
>> +    mtd->size = nand_size(nand);
>> +    mtd->erasesize = nand_eraseblock_size(nand);
>> +    mtd->writesize = nand_page_size(nand);
>> +    mtd->writebufsize = mtd->writesize;
>> +    mtd->owner = THIS_MODULE;
>> +    mtd->type = MTD_NANDFLASH;
>> +    mtd->flags = MTD_CAP_NANDFLASH;
>> +    mtd->oobsize = nand_per_page_oobsize(nand);
>> +    /*
>> +     * Right now, we don't support ECC, so let the whole oob
>> +     * area is available for user.
>> +     */
>> +    mtd->oobavail = mtd->oobsize;
>> +
>> +    /* After power up, all blocks are locked, so unlock it here. */
>> +    spinand_lock_block(spinand, BL_ALL_UNLOCKED);
>> +
>> +    return 0;
>> +
>> +err_free_buf:
>> +    kfree(spinand->buf);
>> +err_out:
> 
> You can get rid of err_out.
> 

Okay

Thanks 
Peter Pan 
>> +    return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(spinand_init);
>> +
>
Cyrille Pitchen May 31, 2017, 9:45 p.m. UTC | #3
Hi Peter,

Le 24/05/2017 à 09:07, Peter Pan a écrit :
> This is the first commit for spi nand framkework.
> This commit is to add add basic building blocks
> for the SPI NAND infrastructure.
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>  drivers/mtd/nand/Kconfig      |   1 +
>  drivers/mtd/nand/Makefile     |   1 +
>  drivers/mtd/nand/spi/Kconfig  |   5 +
>  drivers/mtd/nand/spi/Makefile |   1 +
>  drivers/mtd/nand/spi/core.c   | 419 ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mtd/spinand.h   | 267 +++++++++++++++++++++++++++
>  6 files changed, 694 insertions(+)
>  create mode 100644 drivers/mtd/nand/spi/Kconfig
>  create mode 100644 drivers/mtd/nand/spi/Makefile
>  create mode 100644 drivers/mtd/nand/spi/core.c
>  create mode 100644 include/linux/mtd/spinand.h
> 
[...]
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> new file mode 100644
> index 0000000..dd9da71
> --- /dev/null
> +++ b/include/linux/mtd/spinand.h
> @@ -0,0 +1,267 @@
[...]
> +#define SPINAND_MAX_ADDR_LEN	4
> +
> +/**
> + * struct spinand_op - SPI NAND operation description
> + * @cmd: opcode to send
> + * @n_addr: address bytes
> + * @addr_nbits: number of bit used to transfer address
> + * @dummy_types: dummy bytes followed address
> + * @addr: buffer held address
> + * @n_tx: size of tx_buf
> + * @tx_buf: data to be written
> + * @n_rx: size of rx_buf
> + * @rx_buf: data to be read
> + * @data_nbits: number of bit used to transfer data
> + */
> +struct spinand_op {
> +	u8 cmd;
> +	u8 n_addr;
> +	u8 addr_nbits;
> +	u8 dummy_bytes;
> +	u8 addr[SPINAND_MAX_ADDR_LEN];

I think it would be better to use some integral type (maybe loff_t)
rather than an array of u8. Indeed integers are better suited to some
(Q)SPI controllers, especially those using some kind of memory area
mapped into the system bus.

For other SPI controllers, it would still be easy to build an array of
u8 if needed from a loff_t.


> +	u32 n_tx;
> +	const u8 *tx_buf;
> +	u32 n_rx;

just a detail but n_tx and n_rx could be merged into a single n_buf:
- at the spi-nor side, tx_buf and rx_buf can never be both != NULL
- at the spi-nand side, looking at the gen_spi_spinand_exec_op()
  function, this is still true.

> +	u8 *rx_buf;
> +	u8 data_nbits;
> +};

Besides, this structure is really close to
'struct spi_flash_read_message' from include/spi/spi.h.
So I think it would be interesting to rework a little bit the structure
from the SPI sub-system so it can fit your needs and support all kind of
SPI flash commands, not only read commands.
Then this structure could be used by both the spi-nand and spi-nor
sub-systems.

Hence 'struct spi_flash_read_message' could be renamed into 'struct
spi_flash_message', 'struct spi_flash_op' 'struct spi_flash_command' or
whatever... as long as we just remove the "read" part.

In the reworked structure, I propose to add some enum or flags to
provide the SPI controller with the kind of SPI flash command we want it
to execute, actually a SPI flash command type:
- SFLASH_TYPE_READ:  (Fast) Read (spi-nor) / Read From Cache (spi-nand)
- SFLASH_TYPE_WRITE:  Page Program (spi-nor) or Program Load (spi-nand)
- SFLASH_TYPE_ERASE: Sector/Block Erase
- SFLASH_TYPE_READ_REG: Read ID, Read Status, Page Read, ...
- SFLASH_TYPE_WRITE_REG: Write Enable, Write Status, ...

Based on my experience with SPI NOR memories, it's not reliable to guess
the type of the SPI flash command only from its instruction op code:
most of the instruction op codes are pretty standard but *really* often
many SPI flash manufacturers have their own quirks and use different and
unexpected op codes...

So providing the type of SPI flash command would clarify and allow the
(Q)SPI controller driver to tell the spi-nand/spi-nor frameworks whether
this SPI flash command is supported by spi_flash_command_exec() / exec_op().

That's why I suggest to add 2 new optional handlers in 'struct
spi_master' from the SPI sub-system:

bool (*spi_flash_is_command_supported)(struct spi_device *spi,
				       const struct spi_flash_command *)

and

int (*spi_flash_command_exec)(struct spi_device *spi,
			      const struct spi_flash_command *cmd);

For some (Q)SPI controllers, like those from Cadence and TI I guess,
spi_flash_is_command_supported() is likely to return true only for a SPI
flash command type of SFLASH_TYPE_READ.

Other (Q)SPI controllers, like the one from Atmel, would return true for
any command. Indeed, the regular spi_sync() API based on
'struct spi_message' and 'struct spi_transfer' is not suited at all for
the Atmel QSPI controller.

If spi_flash_is_command_supported() returns false or is not implemented
by the SPI controller driver, then you can use a default implementation
based on spi_sync(), actually the one you propose through your
gen_spi_spinand_exec_op() function.

The idea behind that is, in term, to replace the current
flash_read_supported() / spi_flash_read() functions by the more generic
spi_flash_is_command_supported() / spi_flash_command_exec().
We just want to extend the already existing SPI API to support more SPI
flash commands than the only (Fast) Read commands.

Also, if done at the SPI sub-system side, this API could be used by both
the spi-nand and spi-nor sub-systems, then we could imagine moving some
(Q)SPI controller drivers, like drivers/mtd/spi-nor/atmel-quadspi.c,
back into the SPI sub-system, where they should belong.

Currently, the Atmel QSPI driver dwells in the spi-nor sub-system simply
because, as I said, the spi-sync() API is not suited at all for the
hardware register interface. However with the proper SPI API, this
controller could also be used with SPI NAND memories.

Best regards,

Cyrille
Boris Brezillon June 1, 2017, 7:24 a.m. UTC | #4
Hi Cyrille,

Le Wed, 31 May 2017 23:45:55 +0200,
Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> a écrit :

> Hi Peter,
> 
> Le 24/05/2017 à 09:07, Peter Pan a écrit :
> > This is the first commit for spi nand framkework.
> > This commit is to add add basic building blocks
> > for the SPI NAND infrastructure.
> > 
> > Signed-off-by: Peter Pan <peterpandong@micron.com>
> > ---
> >  drivers/mtd/nand/Kconfig      |   1 +
> >  drivers/mtd/nand/Makefile     |   1 +
> >  drivers/mtd/nand/spi/Kconfig  |   5 +
> >  drivers/mtd/nand/spi/Makefile |   1 +
> >  drivers/mtd/nand/spi/core.c   | 419 ++++++++++++++++++++++++++++++++++++++++++
> >  include/linux/mtd/spinand.h   | 267 +++++++++++++++++++++++++++
> >  6 files changed, 694 insertions(+)
> >  create mode 100644 drivers/mtd/nand/spi/Kconfig
> >  create mode 100644 drivers/mtd/nand/spi/Makefile
> >  create mode 100644 drivers/mtd/nand/spi/core.c
> >  create mode 100644 include/linux/mtd/spinand.h
> >   
> [...]
> > diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> > new file mode 100644
> > index 0000000..dd9da71
> > --- /dev/null
> > +++ b/include/linux/mtd/spinand.h
> > @@ -0,0 +1,267 @@  
> [...]
> > +#define SPINAND_MAX_ADDR_LEN	4
> > +
> > +/**
> > + * struct spinand_op - SPI NAND operation description
> > + * @cmd: opcode to send
> > + * @n_addr: address bytes
> > + * @addr_nbits: number of bit used to transfer address
> > + * @dummy_types: dummy bytes followed address
> > + * @addr: buffer held address
> > + * @n_tx: size of tx_buf
> > + * @tx_buf: data to be written
> > + * @n_rx: size of rx_buf
> > + * @rx_buf: data to be read
> > + * @data_nbits: number of bit used to transfer data
> > + */
> > +struct spinand_op {
> > +	u8 cmd;
> > +	u8 n_addr;
> > +	u8 addr_nbits;
> > +	u8 dummy_bytes;
> > +	u8 addr[SPINAND_MAX_ADDR_LEN];  
> 
> I think it would be better to use some integral type (maybe loff_t)
> rather than an array of u8. Indeed integers are better suited to some
> (Q)SPI controllers, especially those using some kind of memory area
> mapped into the system bus.

I had a look at at least 2 SPI NAND datasheets and they all express the
number of addr cycles in bytes. I really think we should stay as
close as possible to the specs even if this implies a small penalty for
some controller drivers. BTW, I could argue that some controllers are
expecting an array of bytes and not an integer :-P. 

> 
> For other SPI controllers, it would still be easy to build an array of
> u8 if needed from a loff_t.

Just as easy as creating an loff_t from an array of bytes ;-).

> 
> 
> > +	u32 n_tx;
> > +	const u8 *tx_buf;
> > +	u32 n_rx;  
> 
> just a detail but n_tx and n_rx could be merged into a single n_buf:
> - at the spi-nor side, tx_buf and rx_buf can never be both != NULL
> - at the spi-nand side, looking at the gen_spi_spinand_exec_op()
>   function, this is still true.

Indeed. How about having a union to still enforce constness on the tx
buf and an extra variable to specify the direction.

	union {
		const u8 *tx;
		u8 *rx;
	} buf;

	u32 datalen;
	enum spinand_data_dir datadir;

> 
> > +	u8 *rx_buf;
> > +	u8 data_nbits;
> > +};
> 
> Besides, this structure is really close to
> 'struct spi_flash_read_message' from include/spi/spi.h.
> So I think it would be interesting to rework a little bit the structure
> from the SPI sub-system so it can fit your needs and support all kind of
> SPI flash commands, not only read commands.
> Then this structure could be used by both the spi-nand and spi-nor
> sub-systems.

I like this idea, but I'd like to get the SPI NAND framework merge
first. Peter has been working on this for several years and I think
it's time to get it merged even if it's not perfect. We can still
improve things afterwards.

> 
> Hence 'struct spi_flash_read_message' could be renamed into 'struct
> spi_flash_message', 'struct spi_flash_op' 'struct spi_flash_command' or
> whatever... as long as we just remove the "read" part.
> 
> In the reworked structure, I propose to add some enum or flags to
> provide the SPI controller with the kind of SPI flash command we want it
> to execute, actually a SPI flash command type:
> - SFLASH_TYPE_READ:  (Fast) Read (spi-nor) / Read From Cache (spi-nand)
> - SFLASH_TYPE_WRITE:  Page Program (spi-nor) or Program Load (spi-nand)
> - SFLASH_TYPE_ERASE: Sector/Block Erase
> - SFLASH_TYPE_READ_REG: Read ID, Read Status, Page Read, ...
> - SFLASH_TYPE_WRITE_REG: Write Enable, Write Status, ...

Why is this needed? The opcode[+addr-cycles][+data-cycles] sequence
should be enough.

> 
> Based on my experience with SPI NOR memories, it's not reliable to guess
> the type of the SPI flash command only from its instruction op code:
> most of the instruction op codes are pretty standard but *really* often
> many SPI flash manufacturers have their own quirks and use different and
> unexpected op codes...

SPI controllers (even if they're QSPI/flash oriented) should be able to
send any kind of command. A command (or operation) is a specific
sequence of opcode[+addr-cycles][+data-cycles].

Vendors can have their own private commands or even decide to adapt
the standard command sequence, but that's all flash specific, and the
controller code should not be impacted here. All the controller needs
to know is the sequence it's supposed to execute, and that's all.

> 
> So providing the type of SPI flash command would clarify and allow the
> (Q)SPI controller driver to tell the spi-nand/spi-nor frameworks whether
> this SPI flash command is supported by spi_flash_command_exec() / exec_op().
> 
> That's why I suggest to add 2 new optional handlers in 'struct
> spi_master' from the SPI sub-system:
> 
> bool (*spi_flash_is_command_supported)(struct spi_device *spi,
> 				       const struct spi_flash_command *)
> 
> and
> 
> int (*spi_flash_command_exec)(struct spi_device *spi,
> 			      const struct spi_flash_command *cmd);

I agree on this.

> 
> For some (Q)SPI controllers, like those from Cadence and TI I guess,
> spi_flash_is_command_supported() is likely to return true only for a SPI
> flash command type of SFLASH_TYPE_READ.

But why do you need those high-level definitions? With the full
sequence (including the datalen+datadir) + the number of lines
(quad/dual/single), you should be able to know whether the controller
supports the command/operation or not.

> 
> Other (Q)SPI controllers, like the one from Atmel, would return true for
> any command. Indeed, the regular spi_sync() API based on
> 'struct spi_message' and 'struct spi_transfer' is not suited at all for
> the Atmel QSPI controller.
> 
> If spi_flash_is_command_supported() returns false or is not implemented
> by the SPI controller driver, then you can use a default implementation
> based on spi_sync(), actually the one you propose through your
> gen_spi_spinand_exec_op() function.

I'm not sure I understand the purpose of
->spi_flash_is_command_supported() anymore. Is this about reporting
when the controller can optimize things, and if it returns false the
framework falls back to the less optimized spi_sync() approach, or is
this here to say 'no, I can't send this command, you should return
-ENOTSUPP to the caller'.

AFAICT, if you can fallback to spi_sync() that means you support all
commands except those that require Quad or Dual mode. Am I wrong?

> 
> The idea behind that is, in term, to replace the current
> flash_read_supported() / spi_flash_read() functions by the more generic
> spi_flash_is_command_supported() / spi_flash_command_exec().
> We just want to extend the already existing SPI API to support more SPI
> flash commands than the only (Fast) Read commands.
> 
> Also, if done at the SPI sub-system side, this API could be used by both
> the spi-nand and spi-nor sub-systems, then we could imagine moving some
> (Q)SPI controller drivers, like drivers/mtd/spi-nor/atmel-quadspi.c,
> back into the SPI sub-system, where they should belong.
> 
> Currently, the Atmel QSPI driver dwells in the spi-nor sub-system simply
> because, as I said, the spi-sync() API is not suited at all for the
> hardware register interface. However with the proper SPI API, this
> controller could also be used with SPI NAND memories.

Your solution sounds promising, but I'm focusing on short-term
solutions. I think we'll keep going with the current implementation
(with only minor modifications) and see how things evolve. Once your
generic solution is ready it should be pretty easy to adapt the
spi-nand framework.

Thanks for your inputs.

BTW, you didn't answer my questions on dummy-bits in address cycles,
but I think I got my answers by reading the datasheets ;-).

Boris
diff mbox

Patch

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 1c1a1f4..7695fd8 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -2,3 +2,4 @@  config MTD_NAND_CORE
 	tristate
 
 source "drivers/mtd/nand/raw/Kconfig"
+source "drivers/mtd/nand/spi/Kconfig"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index fe430d9..6221958 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -3,3 +3,4 @@  obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
 nandcore-objs :=  bbt.o
 
 obj-y	+= raw/
+obj-$(CONFIG_MTD_SPI_NAND)	+= spi/
diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
new file mode 100644
index 0000000..d77c46e
--- /dev/null
+++ b/drivers/mtd/nand/spi/Kconfig
@@ -0,0 +1,5 @@ 
+menuconfig MTD_SPI_NAND
+	tristate "SPI NAND device Support"
+	depends on MTD_NAND
+	help
+	  This is the framework for the SPI NAND device drivers.
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
new file mode 100644
index 0000000..a677a4d
--- /dev/null
+++ b/drivers/mtd/nand/spi/Makefile
@@ -0,0 +1 @@ 
+obj-$(CONFIG_MTD_SPI_NAND) += core.o
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
new file mode 100644
index 0000000..93ce212
--- /dev/null
+++ b/drivers/mtd/nand/spi/core.c
@@ -0,0 +1,419 @@ 
+/*
+ *
+ * Copyright (c) 2016-2017 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/mtd/spinand.h>
+#include <linux/slab.h>
+
+/**
+ * spinand_exec_op - execute SPI NAND operation by controller ->exec_op() hook
+ * @spinand: SPI NAND device structure
+ * @op: pointer to spinand_op struct
+ */
+static inline int spinand_exec_op(struct spinand_device *spinand,
+				  struct spinand_op *op)
+{
+	return spinand->controller.controller->ops->exec_op(spinand, op);
+}
+
+/**
+ * spinand_init_op - initialize spinand_op struct
+ * @op: pointer to spinand_op struct
+ */
+static inline void spinand_init_op(struct spinand_op *op)
+{
+	memset(op, 0, sizeof(struct spinand_op));
+	op->addr_nbits = 1;
+	op->data_nbits = 1;
+}
+
+/**
+ * spinand_read_reg - read SPI NAND register
+ * @spinand: SPI NAND device structure
+ * @reg; register to read
+ * @buf: buffer to store value
+ */
+static int spinand_read_reg(struct spinand_device *spinand, u8 reg, u8 *buf)
+{
+	struct spinand_op op;
+	int ret;
+
+	spinand_init_op(&op);
+	op.cmd = SPINAND_CMD_GET_FEATURE;
+	op.n_addr = 1;
+	op.addr[0] = reg;
+	op.n_rx = 1;
+	op.rx_buf = buf;
+
+	ret = spinand_exec_op(spinand, &op);
+	if (ret < 0)
+		dev_err(spinand->dev, "err: %d read register %d\n", ret, reg);
+
+	return ret;
+}
+
+/**
+ * spinand_write_reg - write SPI NAND register
+ * @spinand: SPI NAND device structure
+ * @reg; register to write
+ * @value: value to write
+ */
+static int spinand_write_reg(struct spinand_device *spinand, u8 reg, u8 value)
+{
+	struct spinand_op op;
+	int ret;
+
+	spinand_init_op(&op);
+	op.cmd = SPINAND_CMD_SET_FEATURE;
+	op.n_addr = 1;
+	op.addr[0] = reg;
+	op.n_tx = 1;
+	op.tx_buf = &value;
+
+	ret = spinand_exec_op(spinand, &op);
+	if (ret < 0)
+		dev_err(spinand->dev, "err: %d write register %d\n", ret, reg);
+
+	return ret;
+}
+
+/**
+ * spinand_read_status - get status register value
+ * @spinand: SPI NAND device structure
+ * @status: buffer to store value
+ * Description:
+ *   After read, write, or erase, the NAND device is expected to set the
+ *   busy status.
+ *   This function is to allow reading the status of the command: read,
+ *   write, and erase.
+ */
+static int spinand_read_status(struct spinand_device *spinand, u8 *status)
+{
+	return spinand_read_reg(spinand, REG_STATUS, status);
+}
+
+/**
+ * spinand_wait - wait until the command is done
+ * @spinand: SPI NAND device structure
+ * @s: buffer to store status register value (can be NULL)
+ */
+static int spinand_wait(struct spinand_device *spinand, u8 *s)
+{
+	unsigned long timeo =  jiffies + msecs_to_jiffies(400);
+	u8 status;
+
+	do {
+		spinand_read_status(spinand, &status);
+		if ((status & STATUS_OIP_MASK) == STATUS_READY)
+			goto out;
+	} while (time_before(jiffies, timeo));
+
+	/*
+	 * Extra read, just in case the STATUS_READY bit has changed
+	 * since our last check
+	 */
+	spinand_read_status(spinand, &status);
+out:
+	if (s)
+		*s = status;
+
+	return (status & STATUS_OIP_MASK) == STATUS_READY ? 0 :	-ETIMEDOUT;
+}
+
+/**
+ * spinand_read_id - read SPI NAND ID
+ * @spinand: SPI NAND device structure
+ * @buf: buffer to store id
+ * Description:
+ *   Manufacturers' read ID method is not unique. Some need a dummy before
+ *   reading, some's ID has three byte.
+ *   This function send one byte opcode (9Fh) and then read
+ *   SPINAND_MAX_ID_LEN (4 currently) bytes. Manufacturer's detect function
+ *   need to filter out real ID from the 4 bytes.
+ */
+static int spinand_read_id(struct spinand_device *spinand, u8 *buf)
+{
+	struct spinand_op op;
+
+	spinand_init_op(&op);
+	op.cmd = SPINAND_CMD_READ_ID;
+	op.n_rx = SPINAND_MAX_ID_LEN;
+	op.rx_buf = buf;
+
+	return spinand_exec_op(spinand, &op);
+}
+
+/**
+ * spinand_reset - reset SPI NAND device
+ * @spinand: SPI NAND device structure
+ */
+static int spinand_reset(struct spinand_device *spinand)
+{
+	struct spinand_op op;
+	int ret;
+
+	spinand_init_op(&op);
+	op.cmd = SPINAND_CMD_RESET;
+
+	ret = spinand_exec_op(spinand, &op);
+	if (ret < 0) {
+		dev_err(spinand->dev, "reset failed!\n");
+		goto out;
+	}
+
+	ret = spinand_wait(spinand, NULL);
+
+out:
+	return ret;
+}
+
+/**
+ * spinand_lock_block - write block lock register to lock/unlock device
+ * @spinand: SPI NAND device structure
+ * @lock: value to set to block lock register
+ */
+static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
+{
+	return spinand_write_reg(spinand, REG_BLOCK_LOCK, lock);
+}
+
+/**
+ * spinand_set_rd_wr_op - choose the best read write command
+ * @spinand: SPI NAND device structure
+ * Description:
+ *   Chose the fastest r/w command according to spi controller's and
+ *   device's ability.
+ */
+static void spinand_set_rd_wr_op(struct spinand_device *spinand)
+{
+	u32 controller_cap = spinand->controller.controller->caps;
+	u32 rw_mode = spinand->rw_mode;
+
+	if ((controller_cap & SPINAND_CAP_RD_QUAD) &&
+	    (rw_mode & SPINAND_RD_QUAD))
+		spinand->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_QUAD_IO;
+	else if ((controller_cap & SPINAND_CAP_RD_X4) &&
+		 (rw_mode & SPINAND_RD_X4))
+		spinand->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X4;
+	else if ((controller_cap & SPINAND_CAP_RD_DUAL) &&
+		 (rw_mode & SPINAND_RD_DUAL))
+		spinand->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_DUAL_IO;
+	else if ((controller_cap & SPINAND_CAP_RD_X2) &&
+		 (rw_mode & SPINAND_RD_X2))
+		spinand->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X2;
+	else
+		spinand->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_FAST;
+
+	if ((controller_cap & SPINAND_CAP_WR_X4) &&
+	    (rw_mode & SPINAND_WR_X4))
+		spinand->write_cache_op = SPINAND_CMD_PROG_LOAD_X4;
+	else
+		spinand->write_cache_op = SPINAND_CMD_PROG_LOAD;
+}
+
+static const struct spinand_manufacturer *spinand_manufacturers[] = {};
+
+/**
+ * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer
+ * @spinand: SPI NAND device structure
+ *
+ * ->detect() should decode raw id in spinand->id.data and initialize device
+ * related part in spinand_device structure if it is the right device.
+ * ->detect() can not be NULL.
+ */
+static int spinand_manufacturer_detect(struct spinand_device *spinand)
+{
+	int i = 0;
+
+	for (; i < ARRAY_SIZE(spinand_manufacturers); i++) {
+		if (spinand_manufacturers[i]->ops->detect(spinand)) {
+			spinand->manufacturer.manu = spinand_manufacturers[i];
+
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+/**
+ * spinand_manufacturer_init - manufacturer initialization function.
+ * @spinand: SPI NAND device structure
+ *
+ * Manufacturer drivers should put all their specific initialization code in
+ * their ->init() hook.
+ */
+static int spinand_manufacturer_init(struct spinand_device *spinand)
+{
+	if (spinand->manufacturer.manu->ops->init)
+		return spinand->manufacturer.manu->ops->init(spinand);
+
+	return 0;
+}
+
+/**
+ * spinand_manufacturer_cleanup - manufacturer cleanup function.
+ * @spinand: SPI NAND device structure
+ *
+ * Manufacturer drivers should put all their specific cleanup code in their
+ * ->cleanup() hook.
+ */
+static void spinand_manufacturer_cleanup(struct spinand_device *spinand)
+{
+	/* Release manufacturer private data */
+	if (spinand->manufacturer.manu->ops->cleanup)
+		return spinand->manufacturer.manu->ops->cleanup(spinand);
+}
+
+/**
+ * spinand_detect - detect the SPI NAND device
+ * @spinand: SPI NAND device structure
+ */
+static int spinand_detect(struct spinand_device *spinand)
+{
+	struct nand_device *nand = &spinand->base;
+	int ret;
+
+	spinand_reset(spinand);
+	spinand_read_id(spinand, spinand->id.data);
+	spinand->id.len = SPINAND_MAX_ID_LEN;
+
+	ret = spinand_manufacturer_detect(spinand);
+	if (ret) {
+		dev_err(spinand->dev, "unknown raw ID %*phN\n",
+			SPINAND_MAX_ID_LEN, spinand->id.data);
+		goto failed;
+	}
+
+	dev_info(spinand->dev, "%s (%s) is found.\n", spinand->name,
+		 spinand->manufacturer.manu->name);
+	dev_info(spinand->dev,
+		 "%d MiB, block size: %d KiB, page size: %d, OOB size: %d\n",
+		 (int)(nand_size(nand) >> 20), nand_eraseblock_size(nand) >> 10,
+		 nand_page_size(nand), nand_per_page_oobsize(nand));
+
+failed:
+	return ret;
+}
+
+/**
+ * devm_spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance
+ * @dev: pointer to device model structure
+ */
+struct spinand_device *devm_spinand_alloc(struct device *dev)
+{
+	struct spinand_device *spinand;
+	struct mtd_info *mtd;
+
+	spinand = devm_kzalloc(dev, sizeof(*spinand), GFP_KERNEL);
+	if (!spinand)
+		return ERR_PTR(-ENOMEM);
+
+	spinand_set_of_node(spinand, dev->of_node);
+	mutex_init(&spinand->lock);
+	spinand->dev = dev;
+	mtd = spinand_to_mtd(spinand);
+	mtd->dev.parent = dev;
+
+	return spinand;
+}
+EXPORT_SYMBOL_GPL(devm_spinand_alloc);
+
+/**
+ * spinand_init - [SPI NAND Interface] initialize the SPI NAND device
+ * @spinand: SPI NAND device structure
+ */
+int spinand_init(struct spinand_device *spinand)
+{
+	struct mtd_info *mtd = spinand_to_mtd(spinand);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	int ret;
+
+	ret = spinand_detect(spinand);
+	if (ret) {
+		dev_err(spinand->dev,
+			"Detect SPI NAND failed with error %d.\n", ret);
+		goto err_out;
+	}
+
+	spinand_set_rd_wr_op(spinand);
+
+	/*
+	 * Use kzalloc() instead of devm_kzalloc() here, beacause some drivers
+	 * may use this buffer for DMA access.
+	 * Memory allocated by devm_ does not guarantee DMA-safe alignment.
+	 */
+	spinand->buf = kzalloc(nand_page_size(nand) +
+			       nand_per_page_oobsize(nand),
+			       GFP_KERNEL);
+	if (!spinand->buf) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+
+	spinand->oobbuf = spinand->buf + nand_page_size(nand);
+
+	ret = spinand_manufacturer_init(spinand);
+	if (ret) {
+		dev_err(spinand->dev,
+			"Manufacurer init SPI NAND failed with err %d.\n",
+			ret);
+		goto err_free_buf;
+	}
+
+	mtd->name = spinand->name;
+	mtd->size = nand_size(nand);
+	mtd->erasesize = nand_eraseblock_size(nand);
+	mtd->writesize = nand_page_size(nand);
+	mtd->writebufsize = mtd->writesize;
+	mtd->owner = THIS_MODULE;
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->oobsize = nand_per_page_oobsize(nand);
+	/*
+	 * Right now, we don't support ECC, so let the whole oob
+	 * area is available for user.
+	 */
+	mtd->oobavail = mtd->oobsize;
+
+	/* After power up, all blocks are locked, so unlock it here. */
+	spinand_lock_block(spinand, BL_ALL_UNLOCKED);
+
+	return 0;
+
+err_free_buf:
+	kfree(spinand->buf);
+err_out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spinand_init);
+
+/**
+ * spinand_cleanup - [SPI NAND Interface] clean SPI NAND device
+ * @spinand: SPI NAND device structure
+ */
+void spinand_cleanup(struct spinand_device *spinand)
+{
+	spinand_manufacturer_cleanup(spinand);
+	kfree(spinand->buf);
+}
+EXPORT_SYMBOL_GPL(spinand_cleanup);
+
+MODULE_DESCRIPTION("SPI NAND framework");
+MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
new file mode 100644
index 0000000..dd9da71
--- /dev/null
+++ b/include/linux/mtd/spinand.h
@@ -0,0 +1,267 @@ 
+/*
+ *
+ * Copyright (c) 2016-2017 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __LINUX_MTD_SPINAND_H
+#define __LINUX_MTD_SPINAND_H
+
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+
+/**
+ * Standard SPI NAND flash commands
+ */
+#define SPINAND_CMD_RESET			0xff
+#define SPINAND_CMD_GET_FEATURE			0x0f
+#define SPINAND_CMD_SET_FEATURE			0x1f
+#define SPINAND_CMD_PAGE_READ			0x13
+#define SPINAND_CMD_READ_FROM_CACHE		0x03
+#define SPINAND_CMD_READ_FROM_CACHE_FAST	0x0b
+#define SPINAND_CMD_READ_FROM_CACHE_X2		0x3b
+#define SPINAND_CMD_READ_FROM_CACHE_DUAL_IO	0xbb
+#define SPINAND_CMD_READ_FROM_CACHE_X4		0x6b
+#define SPINAND_CMD_READ_FROM_CACHE_QUAD_IO	0xeb
+#define SPINAND_CMD_BLK_ERASE			0xd8
+#define SPINAND_CMD_PROG_EXC			0x10
+#define SPINAND_CMD_PROG_LOAD			0x02
+#define SPINAND_CMD_PROG_LOAD_RDM_DATA		0x84
+#define SPINAND_CMD_PROG_LOAD_X4		0x32
+#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4	0x34
+#define SPINAND_CMD_READ_ID			0x9f
+#define SPINAND_CMD_WR_DISABLE			0x04
+#define SPINAND_CMD_WR_ENABLE			0x06
+
+/* feature register */
+#define REG_BLOCK_LOCK		0xa0
+#define REG_CFG			0xb0
+#define REG_STATUS		0xc0
+
+/* status register */
+#define STATUS_OIP_MASK		BIT(0)
+#define STATUS_CRBSY_MASK	BIT(7)
+#define STATUS_READY		0
+#define STATUS_BUSY		BIT(0)
+
+#define STATUS_E_FAIL_MASK	BIT(2)
+#define STATUS_E_FAIL		BIT(2)
+
+#define STATUS_P_FAIL_MASK	BIT(3)
+#define STATUS_P_FAIL		BIT(3)
+
+/* configuration register */
+#define CFG_ECC_MASK		BIT(4)
+#define CFG_ECC_ENABLE		BIT(4)
+
+/* block lock register */
+#define BL_ALL_UNLOCKED		0X00
+
+struct spinand_op;
+struct spinand_device;
+
+#define SPINAND_MAX_ID_LEN	4
+
+/**
+ * struct spinand_id - SPI NAND id structure
+ * @data: buffer containing the id bytes. Currently 4 bytes large, but can
+ *	  be extended if required.
+ * @len: ID length
+ */
+struct spinand_id {
+	u8 data[SPINAND_MAX_ID_LEN];
+	int len;
+};
+
+/**
+ * struct spinand_controller_ops - SPI NAND controller operations
+ * @exec_op: executute SPI NAND operation
+ */
+struct spinand_controller_ops {
+	int (*exec_op)(struct spinand_device *spinand,
+		       struct spinand_op *op);
+};
+
+/**
+ * struct manufacurer_ops - SPI NAND manufacturer specified operations
+ * @detect: detect SPI NAND device, should bot be NULL.
+ *          ->detect() implementation for manufacturer A never sends
+ *          any manufacturer specific SPI command to a SPI NAND from
+ *          manufacturer B, so the proper way is to decode the raw id
+ *          data in spinand->id.data first, if manufacture ID dismatch,
+ *          return directly and let others to detect.
+ * @init: initialize SPI NAND device.
+ * @cleanup: clean SPI NAND device footprint.
+ */
+struct spinand_manufacturer_ops {
+	bool (*detect)(struct spinand_device *spinand);
+	int (*init)(struct spinand_device *spinand);
+	void (*cleanup)(struct spinand_device *spinand);
+};
+
+/**
+ * struct spinand_manufacturer - SPI NAND manufacturer instance
+ * @id: manufacturer ID
+ * @name: manufacturer name
+ * @ops: point to manufacturer operations
+ */
+struct spinand_manufacturer {
+	u8 id;
+	char *name;
+	const struct spinand_manufacturer_ops *ops;
+};
+
+#define SPINAND_CAP_RD_X1	BIT(0)
+#define SPINAND_CAP_RD_X2	BIT(1)
+#define SPINAND_CAP_RD_X4	BIT(2)
+#define SPINAND_CAP_RD_DUAL	BIT(3)
+#define SPINAND_CAP_RD_QUAD	BIT(4)
+#define SPINAND_CAP_WR_X1	BIT(5)
+#define SPINAND_CAP_WR_X2	BIT(6)
+#define SPINAND_CAP_WR_X4	BIT(7)
+#define SPINAND_CAP_WR_DUAL	BIT(8)
+#define SPINAND_CAP_WR_QUAD	BIT(9)
+
+/**
+ * struct spinand_controller - SPI NAND controller instance
+ * @ops: point to controller operations
+ * @caps: controller capabilities
+ */
+struct spinand_controller {
+	struct spinand_controller_ops *ops;
+	u32 caps;
+};
+
+/**
+ * struct spinand_device - SPI NAND device instance
+ * @base: NAND device instance
+ * @lock: protection lock
+ * @name: name of the device
+ * @dev: struct device pointer
+ * @id: ID structure
+ * @read_cache_op: Opcode of read from cache
+ * @write_cache_op: Opcode of program load
+ * @buf: buffer for read/write data
+ * @oobbuf: buffer for read/write oob
+ * @rw_mode: read/write mode of SPI NAND device
+ * @controller: SPI NAND controller instance
+ * @manufacturer: SPI NAND manufacturer instance, describe
+ *                manufacturer related objects
+ */
+struct spinand_device {
+	struct nand_device base;
+	struct mutex lock;
+	char *name;
+	struct device *dev;
+	struct spinand_id id;
+	u8 read_cache_op;
+	u8 write_cache_op;
+	u8 *buf;
+	u8 *oobbuf;
+	u32 rw_mode;
+	struct {
+		struct spinand_controller *controller;
+		void *priv;
+	} controller;
+	struct {
+		const struct spinand_manufacturer *manu;
+		void *priv;
+	} manufacturer;
+};
+
+/**
+ * mtd_to_spinand - Get the SPI NAND device attached to the MTD instance
+ * @mtd: MTD instance
+ *
+ * Returns the SPI NAND device attached to @mtd.
+ */
+static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd)
+{
+	return container_of(mtd_to_nand(mtd), struct spinand_device, base);
+}
+
+/**
+ * spinand_to_mtd - Get the MTD device attached to the SPI NAND device
+ * @spinand: SPI NAND device
+ *
+ * Returns the MTD device attached to @spinand.
+ */
+static inline struct mtd_info *spinand_to_mtd(struct spinand_device *spinand)
+{
+	return nand_to_mtd(&spinand->base);
+}
+
+/**
+ * spinand_set_of_node - Attach a DT node to a SPI NAND device
+ * @spinand: SPI NAND device
+ * @np: DT node
+ *
+ * Attach a DT node to a SPI NAND device.
+ */
+static inline void spinand_set_of_node(struct spinand_device *spinand,
+				       struct device_node *np)
+{
+	nand_set_of_node(&spinand->base, np);
+}
+
+#define SPINAND_MAX_ADDR_LEN	4
+
+/**
+ * struct spinand_op - SPI NAND operation description
+ * @cmd: opcode to send
+ * @n_addr: address bytes
+ * @addr_nbits: number of bit used to transfer address
+ * @dummy_types: dummy bytes followed address
+ * @addr: buffer held address
+ * @n_tx: size of tx_buf
+ * @tx_buf: data to be written
+ * @n_rx: size of rx_buf
+ * @rx_buf: data to be read
+ * @data_nbits: number of bit used to transfer data
+ */
+struct spinand_op {
+	u8 cmd;
+	u8 n_addr;
+	u8 addr_nbits;
+	u8 dummy_bytes;
+	u8 addr[SPINAND_MAX_ADDR_LEN];
+	u32 n_tx;
+	const u8 *tx_buf;
+	u32 n_rx;
+	u8 *rx_buf;
+	u8 data_nbits;
+};
+
+/* SPI NAND supported OP mode */
+#define SPINAND_RD_X1		BIT(0)
+#define SPINAND_RD_X2		BIT(1)
+#define SPINAND_RD_X4		BIT(2)
+#define SPINAND_RD_DUAL		BIT(3)
+#define SPINAND_RD_QUAD		BIT(4)
+#define SPINAND_WR_X1		BIT(5)
+#define SPINAND_WR_X2		BIT(6)
+#define SPINAND_WR_X4		BIT(7)
+#define SPINAND_WR_DUAL		BIT(8)
+#define SPINAND_WR_QUAD		BIT(9)
+
+#define SPINAND_RD_COMMON	(SPINAND_RD_X1 | SPINAND_RD_X2 | \
+				 SPINAND_RD_X4 | SPINAND_RD_DUAL | \
+				 SPINAND_RD_QUAD)
+#define SPINAND_WR_COMMON	(SPINAND_WR_X1 | SPINAND_WR_X4)
+#define SPINAND_OP_COMMON	(SPINAND_RD_COMMON | SPINAND_WR_COMMON)
+
+struct spinand_device *devm_spinand_alloc(struct device *dev);
+int spinand_init(struct spinand_device *spinand);
+void spinand_cleanup(struct spinand_device *spinand);
+#endif /* __LINUX_MTD_SPINAND_H */