[PATCH v2 2/4] nvmem: NXP LPC18xx EEPROM memory NVMEM driver
Ariel D'Alessandro
ariel at vanguardiasur.com.ar
Fri Oct 30 07:55:07 PDT 2015
Joachim,
El 24/10/15 a las 19:04, Joachim Eastwood escribió:
> Hi Ariel,
>
> On 19 October 2015 at 19:32, Ariel D'Alessandro
> <ariel at vanguardiasur.com.ar> wrote:
>> This commit adds support for NXP LPC18xx EEPROM memory found in NXP
>> LPC185x/3x and LPC435x/3x/2x/1x devices.
>>
>> EEPROM size is 16384 bytes and it can be entirely read and
>> written/erased with 1 word (4 bytes) granularity. The last page
>> (128 bytes) contains the EEPROM initialization data and is not writable.
>>
>> Erase/program time is less than 3ms. The EEPROM device requires a
>> ~1500 kHz clock (min 800 kHz, max 1600 kHz) that is generated dividing
>> the system bus clock by the division factor, contained in the divider
>> register (minus 1 encoded).
>>
>> Signed-off-by: Ariel D'Alessandro <ariel at vanguardiasur.com.ar>
>> ---
>> drivers/nvmem/Kconfig | 9 ++
>> drivers/nvmem/Makefile | 2 +
>> drivers/nvmem/lpc18xx_eeprom.c | 266 +++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 277 insertions(+)
>> create mode 100644 drivers/nvmem/lpc18xx_eeprom.c
>
>> +static int lpc18xx_eeprom_gather_write(void *context, const void *reg,
>> + size_t reg_size, const void *val,
>> + size_t val_size)
>> +{
>> + struct lpc18xx_eeprom_dev *eeprom = context;
>> + unsigned int offset = *(u32 *)reg;
>> +
>> + /* 3 ms of erase/program time between each writing */
>> + while (val_size) {
>> + writel(*(u32 *)val, eeprom->mem_base + offset);
>> + usleep_range(3000, 4000);
>> + val_size -= eeprom->val_bytes;
>> + val += eeprom->val_bytes;
>> + offset += eeprom->val_bytes;
>> + }
>
> What happens here if 'val_size' is less than 4 or not dividable by 4?
AFAIK, before calling to gather_write, the caller ensures that
'val_size' % map->format.val_bytes == 0
Like nvmem_device_write() do this in
drivers/base/regmap/regmap.c:regmap_raw_write()
Then, if val_size == 0, nothing will happen.
> Same thing for 'offset'.
Let me check this one.
>
> I tested the driver from sysfs by writing strings into the nvmem-file
> with echo. Writing a string not dividable by 4 seems to hang the
> system.
Yeah, I saw that too. I think that's a nvmem core issue and Srinivas's
patch solves this. Will confirm that as soon as I have the board back.
>
>
>> +static int lpc18xx_eeprom_read(void *context, const void *reg, size_t reg_size,
>> + void *val, size_t val_size)
>> +{
>> + struct lpc18xx_eeprom_dev *eeprom = context;
>> + unsigned int offset = *(u32 *)reg;
>> +
>> + while (val_size) {
>> + *(u32 *)val = readl(eeprom->mem_base + offset);
>> + val_size -= eeprom->val_bytes;
>> + val += eeprom->val_bytes;
>> + offset += eeprom->val_bytes;
>> + }
>> +
>> + return 0;
>> +}
>
> Same comments as for lpc18xx_eeprom_gather_write().
Same answer.
>
>
>> +static const struct of_device_id lpc18xx_eeprom_of_match[] = {
>> + { .compatible = "nxp,lpc1857-eeprom" },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, lpc18xx_eeprom_of_match);
>
> nit: It's usual to place of_device_id struct just above the
> platform_driver struct.
I see.
>
>
>> + eeprom->val_bytes = lpc18xx_regmap_config.val_bits / 8;
>> + eeprom->reg_bytes = lpc18xx_regmap_config.reg_bits / 8;
>
> There is a BITS_PER_BYTE define in bitops.h that you might want to use here.
Right. Thanks.
>
>
>> + /*
>> + * Clock rate is generated by dividing the system bus clock by the
>> + * division factor, contained in the divider register (minus 1 encoded).
>> + */
>> + clk_rate = clk_get_rate(eeprom->clk);
>> + clk_rate = DIV_ROUND_UP(clk_rate, LPC18XX_EEPROM_CLOCK_HZ) - 1;
>> + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_CLKDIV, clk_rate);
>> +
>> + /*
>> + * Writing a single word to the page will start the erase/program cycle
>> + * automatically
>> + */
>> + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_AUTOPROG,
>> + LPC18XX_EEPROM_AUTOPROG_WORD);
>> +
>> + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
>> + LPC18XX_EEPROM_PWRDWN_NO);
>> +
>> + lpc18xx_regmap_config.max_register = resource_size(res) - 1;
>> + lpc18xx_regmap_config.writeable_reg = lpc18xx_eeprom_writeable_reg;
>> + lpc18xx_regmap_config.readable_reg = lpc18xx_eeprom_readable_reg;
>> +
>> + regmap = devm_regmap_init(dev, &lpc18xx_eeprom_bus, eeprom,
>> + &lpc18xx_regmap_config);
>> + if (IS_ERR(regmap)) {
>> + dev_err(dev, "regmap init failed: %ld\n", PTR_ERR(regmap));
>> + ret = PTR_ERR(regmap);
>> + goto err_clk;
>> + }
>> +
>> + lpc18xx_nvmem_config.dev = dev;
>> +
>> + eeprom->nvmem = nvmem_register(&lpc18xx_nvmem_config);
>> + if (IS_ERR(eeprom->nvmem)) {
>> + ret = PTR_ERR(eeprom->nvmem);
>> + goto err_clk;
>> + }
>> +
>> + platform_set_drvdata(pdev, eeprom);
>> +
>> + return 0;
>> +
>> +err_clk:
>> + clk_disable_unprepare(eeprom->clk);
>> +
>> + return ret;
>> +}
>> +
>> +static int lpc18xx_eeprom_remove(struct platform_device *pdev)
>> +{
>> + struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev);
>> +
>> + lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
>> + LPC18XX_EEPROM_PWRDWN_YES);
>> +
>> + clk_disable_unprepare(eeprom->clk);
>> +
>> + return nvmem_unregister(eeprom->nvmem);
>
> Normally you do tear down in the reverse order of initialization.
>
> Consider what happens here when you power down and disable the clock
> while there still are nvmem users of the eeprom.
You're right! Will fix this. Thanks again.
--
Ariel D'Alessandro, VanguardiaSur
www.vanguardiasur.com.ar
More information about the linux-arm-kernel
mailing list