[PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
Boris Brezillon
boris.brezillon at free-electrons.com
Thu Mar 23 08:40:00 PDT 2017
On Thu, 23 Mar 2017 12:29:58 +0100
Marek Vasut <marex at denx.de> wrote:
> > +
> > +/*
> > + * 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.
If the timeout is big enough, no. But it does not hurt to do one last
check.
>
> > + 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 ...
The underlying dev is not ready/initialized the first time
spinand_reset() is called.
>
> > + goto out;
> > + }
> > + ret = spinand_wait(chip, NULL);
> > +
> > +out:
> > + return ret;
> > +}
> > +/*
> > + * 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.
I agree. Let's assume ->detect is always != NULL. Same goes for ->ops
(assuming you use the ARRAY_SIZE() approach I suggested below).
>
> > + return -EINVAL;
> > + }
> > + if (spinand_manufacturers[i]->ops->detect(chip)) {
> > + chip->manufacturer.manu = spinand_manufacturers[i];
> > + return 0;
> > + }
> > + }
> > +
> > + return -ENODEV;
> > +}
[...]
> > +/*
> > + * 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 .
Actually, the ECC engine allocation/initialization should not be done
here. At this point, you don't know yet which ECC engine will be used
(on-die, external engine, software ECC, ...).
Let the real ECC implementation allocate the ecc.engine struct
(and overload it if needed).
>
> > + ecc_engine->mode = SPINAND_ECC_ONDIE;
The engine mode (which is more a type than a mode IMO) should be
stored in chip->ecc.type (or chip->ecc.mode) and be used by the
different components (core, vendor or controller code) to determine who
is supposed to provide the ECC engine.
> > + 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 at 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
manufacturers.c, but I don't think I want to keep this file anyway.
> > @@ -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 .
BTW, you don't need this sentinel. Just use ARRAY_SIZE() and put this
table directly in the core.
More information about the linux-mtd
mailing list