[PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
Marek Vasut
marex at denx.de
Thu Mar 23 09:33:05 PDT 2017
On 03/23/2017 04:40 PM, Boris Brezillon wrote:
> 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.
400 mSec is not big enough ? It's huge ... this seems like a duplication
to me. btw the ad-hoc 400 mSec delay value should be replaced with a macro.
>>> + 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.
OK, then at least be consistent with the prefix.
>>> + 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).
OK
>>> + 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.
>
--
Best regards,
Marek Vasut
More information about the linux-mtd
mailing list