Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure

Marek Vasut
2017-03-23T14:17:12Z

On 03/23/2017 10:43 AM, Peter Pan wrote:
> 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>
> ---

[...]

> +#define pr_fmt(fmt) "SPI NAND: " fmt

This looks awful, please drop.

> +#include <linux/kernel.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
> + * @chip: SPI NAND device structure
> + * @op: pointer to spinand_op struct
> + */
> +static inline int spinand_exec_op(struct spinand_device *chip,
> + struct spinand_op *op)
> +{
> + return chip->controller.controller->ops->exec_op(chip, op);
> +}
> +
> +/*
> + * spinand_op_init - initialize spinand_op struct
> + * @op: pointer to spinand_op struct
> + */
> +static inline void spinand_op_init(struct spinand_op *op)

Can we be consistent with op_foo or foo_op ? Pick one , but not both ...
I mean, exec_op vs op_init is inconsistent. Probably spinand_op_exec and
spinand_op_init would be better .

> +{
> + memset(op, 0, sizeof(struct spinand_op));
> + op->addr_nbits = 1;
> + op->data_nbits = 1;
> +}
> +
> +/*
> + * spinand_read_reg - send command 0Fh to read register

I don't think we care which commmand is sent, just fix the comment to
say "read SPI NAND register" or something .

> + * @chip: SPI NAND device structure
> + * @reg; register to read
> + * @buf: buffer to store value
> + */
> +static int spinand_read_reg(struct spinand_device *chip, u8 reg, u8 *buf)
> +{
> + struct spinand_op op;
> + int ret;
> +
> + spinand_op_init(&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(chip, &op);
> + if (ret < 0)
> + pr_err("err: %d read register %d\n", ret, reg);
> +
> + return ret;
> +}
> +
> +/*
> + * spinand_write_reg - send command 1Fh to write register

DTTO

> + * @chip: SPI NAND device structure
> + * @reg; register to write
> + * @buf: buffer stored value
> + */
> +static int spinand_write_reg(struct spinand_device *chip, u8 reg, u8 *buf)

Buffer length is always 1 , just use u8 buf .

> +{
> + struct spinand_op op;
> + int ret;
> +
> + spinand_op_init(&op);
> + op.cmd = SPINAND_CMD_SET_FEATURE;
> + op.n_addr = 1;
> + op.addr[0] = reg;
> + op.n_tx = 1,
> + op.tx_buf = buf,
> +
> + ret = spinand_exec_op(chip, &op);
> + if (ret < 0)
> + pr_err("err: %d write register %d\n", ret, reg);
> +
> + return ret;
> +}
> +
> +/*
> + * spinand_read_status - get status register value
> + * @chip: 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 *chip, uint8_t *status)
> +{
> + return spinand_read_reg(chip, REG_STATUS, status);
> +}
> +
> +/*
> + * spinand_wait - wait until the command is done
> + * @chip: SPI NAND device structure
> + * @s: buffer to store status register value (can be NULL)
> + */
> +static int spinand_wait(struct spinand_device *chip, u8 *s)
> +{
> + unsigned long timeo = msecs_to_jiffies(400);
> + u8 status;
> +
> + do {
> + spinand_read_status(chip, &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
> + */

Is this likely to happen ? Probably not ... so in case you reach this
place here, timeout happened.

> + spinand_read_status(chip, &status);
> +out:
> + if (s)
> + *s = status;
> +
> + return (status & STATUS_OIP_MASK) == STATUS_READY ? 0 : -ETIMEDOUT;
> +}
> +
> +/*
> + * spinand_read_id - send 9Fh command to get ID
> + * @chip: 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 *chip, u8 *buf)
> +{
> + struct spinand_op op;
> +
> + spinand_op_init(&op);
> + op.cmd = SPINAND_CMD_READ_ID;
> + op.n_rx = SPINAND_MAX_ID_LEN;
> + op.rx_buf = buf;
> +
> + return spinand_exec_op(chip, &op);
> +}
> +
> +/*
> + * spinand_reset - reset device by FFh command.
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_reset(struct spinand_device *chip)
> +{
> + struct spinand_op op;
> + int ret;
> +
> + spinand_op_init(&op);
> + op.cmd = SPINAND_CMD_RESET;
> +
> + ret = spinand_exec_op(chip, &op);
> + if (ret < 0) {
> + pr_err("spinand reset failed!\n");

Can we use dev_err() ? Also , previous patch had some pr_fmt which added
"SPI NAND:" prefix, so replace "spinand" with "SPI NAND: " to be
consistent ...

> + goto out;
> + }
> + ret = spinand_wait(chip, NULL);
> +
> +out:
> + return ret;
> +}
> +
> +/*
> + * spinand_lock_block - write block lock register to lock/unlock device
> + * @chip: SPI NAND device structure
> + * @lock: value to set to block lock register
> + */
> +static int spinand_lock_block(struct spinand_device *chip, u8 lock)
> +{
> + return spinand_write_reg(chip, REG_BLOCK_LOCK, &lock);
> +}
> +
> +/*
> + * spinand_set_rd_wr_op - choose the best read write command
> + * @chip: 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 *chip)
> +{
> + u32 controller_cap = chip->controller.controller->caps;
> + u32 rw_mode = chip->rw_mode;

What if controller can do quad, but chip can only do dual , is this
handled ?

> + if ((controller_cap & SPINAND_CAP_RD_QUAD) &&
> + (rw_mode & SPINAND_RD_QUAD))
> + chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_QUAD_IO;
> + else if ((controller_cap & SPINAND_CAP_RD_X4) &&
> + (rw_mode & SPINAND_RD_X4))
> + chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X4;
> + else if ((controller_cap & SPINAND_CAP_RD_DUAL) &&
> + (rw_mode & SPINAND_RD_DUAL))
> + chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_DUAL_IO;
> + else if ((controller_cap & SPINAND_CAP_RD_X2) &&
> + (rw_mode & SPINAND_RD_X2))
> + chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X2;
> + else
> + chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_FAST;
> +
> + if ((controller_cap & SPINAND_CAP_WR_X4) &&
> + (rw_mode & SPINAND_WR_X4))
> + chip->write_cache_op = SPINAND_CMD_PROG_LOAD_X4;
> + else
> + chip->write_cache_op = SPINAND_CMD_PROG_LOAD;
> +}
> +
> +/*
> + * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer
> + * @chip: SPI NAND device structure
> + *
> + * ->detect() should decode raw id in chip->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 *chip)
> +{
> + int i = 0;
> +
> + for (; spinand_manufacturers[i]->id != 0x0; i++) {
> + if (!spinand_manufacturers[i]->ops ||
> + !spinand_manufacturers[i]->ops->detect) {
> + pr_err("%s's ops or ops->detect() is be NULL!\n",
> + spinand_manufacturers[i]->name);

Can this case happen, EVER ? I'd mandate the ->detect to be always set.

> + return -EINVAL;
> + }
> + if (spinand_manufacturers[i]->ops->detect(chip)) {
> + chip->manufacturer.manu = spinand_manufacturers[i];
> + return 0;
> + }
> + }
> +
> + return -ENODEV;
> +}
> +
> +/*
> + * spinand_manufacturer_init - manufacturer initialization function.
> + * @chip: 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 *chip)
> +{
> + if (chip->manufacturer.manu->ops->init)
> + return chip->manufacturer.manu->ops->init(chip);
> +
> + return 0;
> +}
> +
> +/*
> + * spinand_manufacturer_cleanup - manufacturer cleanup function.
> + * @chip: 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 *chip)
> +{
> + /* Release manufacturer private data */
> + if (chip->manufacturer.manu->ops->cleanup)
> + return chip->manufacturer.manu->ops->cleanup(chip);
> +}
> +
> +/*
> + * spinand_dt_init - Initialize SPI NAND by device tree node
> + * @chip: SPI NAND device structure
> + *
> + * TODO: put ecc_mode, ecc_strength, ecc_step, bbt, etc in here
> + * and move it in generic NAND core.
> + */
> +static void spinand_dt_init(struct spinand_device *chip)
> +{
> +}
> +
> +/*
> + * spinand_detect - detect the SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_detect(struct spinand_device *chip)
> +{
> + struct nand_device *nand = &chip->base;
> + int ret;
> +
> + spinand_reset(chip);
> + spinand_read_id(chip, chip->id.data);
> + chip->id.len = SPINAND_MAX_ID_LEN;
> +
> + ret = spinand_manufacturer_detect(chip);
> + if (ret) {
> + pr_err("SPI NAND: unknown raw ID %*phN\n",
> + SPINAND_MAX_ID_LEN, chip->id.data);
> + goto out;
> + }
> +
> + pr_info("%s (%s) is found.\n",
> + chip->name, chip->manufacturer.manu->name);
> + pr_info("%d MiB, block size: %d KiB, page size: %d, OOB size: %d\n",
> + (int)(nand_size(nand) >> 20)

Is the case needed?

> , nand_eraseblock_size(nand) >> 10,
> + nand_page_size(nand), nand_per_page_oobsize(nand));
> +
> +out:
> + return ret;
> +}
> +
> +/*
> + * spinand_init - initialize the SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_init(struct spinand_device *chip)
> +{
> + struct mtd_info *mtd = spinand_to_mtd(chip);
> + struct nand_device *nand = mtd_to_nand(mtd);
> + struct spinand_ecc_engine *ecc_engine;
> + int ret;
> +
> + spinand_dt_init(chip);
> + spinand_set_rd_wr_op(chip);
> +
> + chip->buf = kzalloc(nand_page_size(nand) + nand_per_page_oobsize(nand),
> + GFP_KERNEL);
> + if (!chip->buf) {
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + chip->oobbuf = chip->buf + nand_page_size(nand);
> +
> + spinand_manufacturer_init(chip);
> +
> + mtd->name = chip->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;
> + if (!mtd->ecc_strength)
> + mtd->ecc_strength = ecc_engine->strength ?
> + ecc_engine->strength : 1;
> +
> + mtd->oobsize = nand_per_page_oobsize(nand);
> + ret = mtd_ooblayout_count_freebytes(mtd);
> + if (ret < 0)
> + ret = 0;
> + mtd->oobavail = ret;
> +
> + if (!mtd->bitflip_threshold)
> + mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3,
> + 4);
> + /* After power up, all blocks are locked, so unlock it here. */
> + spinand_lock_block(chip, BL_ALL_UNLOCKED);
> +
> + return nand_register(nand);
> +
> +err:
> + return ret;
> +}
> +
> +/*
> + * spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance
> + * @dev: pointer to device model structure
> + */
> +struct spinand_device *spinand_alloc(struct device *dev)
> +{
> + struct spinand_device *chip;
> + struct spinand_ecc_engine *ecc_engine;
> +
> + chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> + if (!chip)
> + goto err1;
> +
> + ecc_engine = kzalloc(sizeof(*ecc_engine), GFP_KERNEL);
> + if (!ecc_engine)
> + goto err2;

Just allocate a structure with both chip and ecc_engine in them, then
you don't have to kzalloc/kfree twice .

> + ecc_engine->mode = SPINAND_ECC_ONDIE;
> + chip->ecc.engine = ecc_engine;
> + spinand_set_of_node(chip, dev->of_node);
> + mutex_init(&chip->lock);
> +
> + return chip;
> +
> +err2:
> + kfree(chip);
> +err1:
> + return ERR_PTR(-ENOMEM);
> +}
> +EXPORT_SYMBOL_GPL(spinand_alloc);
> +
> +/*
> + * spinand_free - [SPI NAND Interface] free SPI NAND device instance
> + * @chip: SPI NAND device structure
> + */
> +void spinand_free(struct spinand_device *chip)
> +{
> + kfree(chip->ecc.engine);
> + kfree(chip);
> +}
> +EXPORT_SYMBOL_GPL(spinand_free);
> +
> +/*
> + * spinand_register - [SPI NAND Interface] register SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +int spinand_register(struct spinand_device *chip)
> +{
> + int ret;
> +
> + ret = spinand_detect(chip);
> + if (ret) {
> + pr_err("Detect SPI NAND failed with error %d.\n", ret);
> + return ret;
> + }
> +
> + ret = spinand_init(chip);
> + if (ret)
> + pr_err("Init SPI NAND failed with error %d.\n", ret);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(spinand_register);
> +
> +/*
> + * spinand_unregister - [SPI NAND Interface] unregister SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +int spinand_unregister(struct spinand_device *chip)
> +{
> + struct nand_device *nand = &chip->base;
> +
> + nand_unregister(nand);
> + spinand_manufacturer_cleanup(chip);
> + kfree(chip->buf);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(spinand_unregister);
> +
> +MODULE_DESCRIPTION("SPI NAND framework");
> +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
> new file mode 100644
> index 0000000..7e0b42d
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/manufactures.c
> @@ -0,0 +1,24 @@
> +/**
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.

2009-2017 ? Wow :)

> + * 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/module.h>
> +#include <linux/mtd/spinand.h>
> +
> +struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
> +
> +struct spinand_manufacturer *spinand_manufacturers[] = {
> + &spinand_manufacturer_end,

Just populate the array directly .

> +};
> +EXPORT_SYMBOL(spinand_manufacturers);
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> new file mode 100644
> index 0000000..44748b4
> --- /dev/null
> +++ b/include/linux/mtd/spinand.h
> @@ -0,0 +1,270 @@
> +/**
> + *
> + * Copyright (c) 2009-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/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_PAGE_CACHE_RDM 0x30
> +#define SPINAND_CMD_READ_PAGE_CACHE_LAST 0x3f
> +#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 registers */
> +#define REG_BLOCK_LOCK 0xa0
> +#define REG_CFG 0xb0
> +#define REG_STATUS 0xc0
> +#define REG_DIE_SELECT 0xd0
> +
> +/* status */
> +#define STATUS_OIP_MASK 0x01
> +#define STATUS_CRBSY_MASK 0x80
> +#define STATUS_READY (0 << 0)
> +#define STATUS_BUSY (1 << 0)
> +
> +#define STATUS_E_FAIL_MASK 0x04
> +#define STATUS_E_FAIL (1 << 2)

BIT() , fix globally

> +
> +#define STATUS_P_FAIL_MASK 0x08
> +#define STATUS_P_FAIL (1 << 3)
> +
> +/*Configuration register defines*/
> +#define CFG_QE_MASK 0x01
> +#define CFG_QE_ENABLE 0x01
> +#define CFG_ECC_MASK 0x10
> +#define CFG_ECC_ENABLE 0x10
> +#define CFG_LOT_MASK 0x20
> +#define CFG_LOT_ENABLE 0x20
> +#define CFG_OTP_MASK 0xc2
> +#define CFG_OTP_ENTER 0x40
> +#define CFG_OTP_EXIT 0x00
> +
> +/* block lock */
> +#define BL_ALL_LOCKED 0x7c
> +#define BL_U_1_1024_LOCKED 0x08
> +#define BL_U_1_512_LOCKED 0x10
> +#define BL_U_1_256_LOCKED 0x18
> +#define BL_U_1_128_LOCKED 0x20
> +#define BL_U_1_64_LOCKED 0x28
> +#define BL_U_1_32_LOCKED 0x30
> +#define BL_U_1_16_LOCKED 0x38
> +#define BL_U_1_8_LOCKED 0x40
> +#define BL_U_1_4_LOCKED 0x48
> +#define BL_U_1_2_LOCKED 0x50
> +#define BL_L_1_1024_LOCKED 0x0c
> +#define BL_L_1_512_LOCKED 0x14
> +#define BL_L_1_256_LOCKED 0x1c
> +#define BL_L_1_128_LOCKED 0x24
> +#define BL_L_1_64_LOCKED 0x2c
> +#define BL_L_1_32_LOCKED 0x34
> +#define BL_L_1_16_LOCKED 0x3c
> +#define BL_L_1_8_LOCKED 0x44
> +#define BL_L_1_4_LOCKED 0x4c
> +#define BL_L_1_2_LOCKED 0x54
> +#define BL_ALL_UNLOCKED 0X00
> +
> +/* die select */
> +#define DIE_SELECT_MASK 0x40
> +#define DIE_SELECT_DS0 0x00
> +#define DIE_SELECT_DS1 0x40
> +
> +struct spinand_op;
> +struct spinand_device;
> +
> +#define SPINAND_MAX_ID_LEN 4
> +
> +/**
> + * struct nand_id - NAND id structure
> + * @data: buffer containing the id bytes. Currently 8 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 {
> + int (*exec_op)(struct spinand_device *chip,
> + struct spinand_op *op);
> +};
> +
> +struct spinand_manufacturer_ops {
> + bool (*detect)(struct spinand_device *chip);
> + int (*init)(struct spinand_device *chip);
> + void (*cleanup)(struct spinand_device *chip);
> +};
> +
> +struct spinand_manufacturer {
> + u8 id;
> + char *name;
> + const struct spinand_manufacturer_ops *ops;
> +};
> +
> +extern struct spinand_manufacturer *spinand_manufacturers[];
> +
> +struct spinand_ecc_engine_ops {
> + void (*get_ecc_status)(struct spinand_device *chip,
> + unsigned int status, unsigned int *corrected,
> + unsigned int *ecc_errors);
> + void (*disable_ecc)(struct spinand_device *chip);
> + void (*enable_ecc)(struct spinand_device *chip);
> +};
> +
> +enum spinand_ecc_mode {
> + SPINAND_ECC_ONDIE,
> + SPINAND_ECC_HW,
> +};
> +
> +struct spinand_ecc_engine {
> + enum spinand_ecc_mode mode;
> + u32 strength;
> + u32 steps;
> + struct spinand_ecc_engine_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)
> +#define SPINAND_CAP_HW_ECC BIT(10)
> +
> +struct spinand_controller {
> + struct spinand_controller_ops *ops;
> + u32 caps;
> +};
> +
> +/**
> + * struct spinand_device - SPI-NAND Private Flash Chip Data
> + * @base: NAND device instance
> + * @lock: protection lock
> + * @name: name of the chip
> + * @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 chip
> + * @controller: SPI NAND controller instance
> + * @manufacturer: SPI NAND manufacturer instance, describe
> + * manufacturer related objects
> + * @ecc_engine: SPI NAND ECC engine instance
> + */
> +struct spinand_device {
> + struct nand_device base;
> + struct mutex lock;
> + char *name;
> + 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;
> + struct {
> + struct spinand_ecc_engine *engine;
> + void *context;
> + } ecc;
> +};
> +
> +static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd)
> +{
> + return container_of(mtd_to_nand(mtd), struct spinand_device, base);
> +}
> +
> +static inline struct mtd_info *spinand_to_mtd(struct spinand_device *chip)
> +{
> + return nand_to_mtd(&chip->base);
> +}
> +
> +static inline void spinand_set_of_node(struct spinand_device *chip,
> + struct device_node *np)
> +{
> + nand_set_of_node(&chip->base, np);
> +}
> +
> +#define SPINAND_MAX_ADDR_LEN 4
> +
> +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 0x00000001
> +#define SPINAND_RD_X2 0x00000002
> +#define SPINAND_RD_X4 0x00000004
> +#define SPINAND_RD_DUAL 0x00000008
> +#define SPINAND_RD_QUAD 0x00000010
> +#define SPINAND_WR_X1 0x00000020
> +#define SPINAND_WR_X2 0x00000040
> +#define SPINAND_WR_X4 0x00000080
> +#define SPINAND_WR_DUAL 0x00000100
> +#define SPINAND_WR_QUAD 0x00000200
> +
> +#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 *spinand_alloc(struct device *dev);
> +void spinand_free(struct spinand_device *chip);
> +int spinand_register(struct spinand_device *chip);
> +int spinand_unregister(struct spinand_device *chip);
> +#endif /* __LINUX_MTD_SPINAND_H */
>

--
Best regards,
Marek Vasut