Re: [PATCH v2 4/6] nvmem: add support for the Khadas MCU Programmable User Memory

From: Neil Armstrong
Date: Tue Jun 02 2020 - 04:29:23 EST


On 15/05/2020 12:55, Srinivas Kandagatla wrote:
>
>
> On 13/05/2020 13:33, Neil Armstrong wrote:
>> On 13/05/2020 12:34, Srinivas Kandagatla wrote:
>>>
>>>
>>> On 12/05/2020 14:26, Neil Armstrong wrote:
>>>> The new Khadas VIM2, VIM3 and Edge boards embeds an on-board microcontroller
>>>> offering a 56bytes User Programmable NVMEM array.
>>>>
>>>> This array needs a password to be writable, thus a password sysfs file
>>>> has been added on the device node to unlock the NVMEM.
>>>
>>> Is this one time programmable? Or does it need to be unlocked for every read/write?
>>
>> It can be rewritten, and needs the password to read & write.
>>
>>>
>>> How can you make sure that the memory is unlocked before making attempt to read/write?
>>
>> The only way to know it's unlock is reading back the password when unlocked.
>
>
> Current code has no such checks for every read/write?
> and looks very disconnected w.r.t to password and read/writes.
>
> If user attempts to read/write he will not be aware that he should program the password first!
>
> Also if the password is static or un-changed then why not just statically program this from the driver rather than providing sysfs file?

The passworsd can be changed, how should this be taken in account ?

>
> I dont see how kernel nvmem read/write interface can make sure that memory is unlocked?

Not sure how to be sure, if locked all the user memory and password is read as 0xff

>
> Who is the real consumer for this provider?

well, it's more user-space, indeed without the in-kernel password unlock, kernel won't be able
to make great use of the data.

I'll drop for next serie until we have a clearer view.

Neil

>
> --srini
>
>
>>
>>>
>>>>
>>>> The default 6bytes password id: "Khadas"
>>>>
>>>> This implements the user NVMEM devices as cell of the Khadas MCU MFD driver.
>>>>
>>>> Signed-off-by: Neil Armstrong <narmstrong@xxxxxxxxxxxx>
>>>> ---
>>>> ÂÂ drivers/nvmem/KconfigÂÂÂÂÂÂÂÂÂÂÂÂÂÂ |ÂÂ 8 ++
>>>> ÂÂ drivers/nvmem/MakefileÂÂÂÂÂÂÂÂÂÂÂÂÂ |ÂÂ 2 +
>>>> ÂÂ drivers/nvmem/khadas-mcu-user-mem.c | 128 ++++++++++++++++++++++++++++
>>>> ÂÂ 3 files changed, 138 insertions(+)
>>>> ÂÂ create mode 100644 drivers/nvmem/khadas-mcu-user-mem.c
>>>>
>>>> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
>>>> index d7b7f6d688e7..92cd4f6aa931 100644
>>>> --- a/drivers/nvmem/Kconfig
>>>> +++ b/drivers/nvmem/Kconfig
>>>> @@ -67,6 +67,14 @@ config JZ4780_EFUSE
>>>> ÂÂÂÂÂÂÂÂ To compile this driver as a module, choose M here: the module
>>>> ÂÂÂÂÂÂÂÂ will be called nvmem_jz4780_efuse.
>>>> ÂÂ +config NVMEM_KHADAS_MCU_USER_MEM
>>>> +ÂÂÂ tristate "Khadas MCU User programmable memory support"
>>>> +ÂÂÂ depends on MFD_KHADAS_MCU
>>>> +ÂÂÂ depends on REGMAP
>>>> +ÂÂÂ help
>>>> +ÂÂÂÂÂ This is a driver for the MCU User programmable memory
>>>> +ÂÂÂÂÂ available on the Khadas VIM and Edge boards.
>>>> +
>>>> ÂÂ config NVMEM_LPC18XX_EEPROM
>>>> ÂÂÂÂÂÂ tristate "NXP LPC18XX EEPROM Memory Support"
>>>> ÂÂÂÂÂÂ depends on ARCH_LPC18XX || COMPILE_TEST
>>>> diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
>>>> index a7c377218341..0516a309542d 100644
>>>> --- a/drivers/nvmem/Makefile
>>>> +++ b/drivers/nvmem/Makefile
>>>> @@ -17,6 +17,8 @@ obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU)ÂÂÂ += nvmem-imx-ocotp-scu.o
>>>> ÂÂ nvmem-imx-ocotp-scu-yÂÂÂÂÂÂÂ := imx-ocotp-scu.o
>>>> ÂÂ obj-$(CONFIG_JZ4780_EFUSE)ÂÂÂÂÂÂÂ += nvmem_jz4780_efuse.o
>>>> ÂÂ nvmem_jz4780_efuse-yÂÂÂÂÂÂÂ := jz4780-efuse.o
>>>> +obj-$(CONFIG_NVMEM_KHADAS_MCU_USER_MEM)ÂÂÂ += nvmem-khadas-mcu-user-mem.o
>>>> +nvmem-khadas-mcu-user-mem-yÂÂÂ := khadas-mcu-user-mem.o
>>>> ÂÂ obj-$(CONFIG_NVMEM_LPC18XX_EEPROM)ÂÂÂ += nvmem_lpc18xx_eeprom.o
>>>> ÂÂ nvmem_lpc18xx_eeprom-yÂÂÂ := lpc18xx_eeprom.o
>>>> ÂÂ obj-$(CONFIG_NVMEM_LPC18XX_OTP)ÂÂÂ += nvmem_lpc18xx_otp.o
>>>> diff --git a/drivers/nvmem/khadas-mcu-user-mem.c b/drivers/nvmem/khadas-mcu-user-mem.c
>>>> new file mode 100644
>>>> index 000000000000..a1d5ae9a030c
>>>> --- /dev/null
>>>> +++ b/drivers/nvmem/khadas-mcu-user-mem.c
>>>> @@ -0,0 +1,128 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Driver for Khadas MCU User programmable Memory
>>>> + *
>>>> + * Copyright (C) 2020 BayLibre SAS
>>>> + * Author(s): Neil Armstrong <narmstrong@xxxxxxxxxxxx>
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>
>>> Why do we need this header?
>>
>> Will drop
>>
>>>
>>>> +#include <linux/module.h>
>>>> +#include <linux/nvmem-provider.h>
>>>> +#include <linux/mfd/khadas-mcu.h>
>>>> +#include <linux/regmap.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/platform_device.h>
>>>> +
>>>> +static int khadas_mcu_user_mem_read(void *context, unsigned int offset,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ void *val, size_t bytes)
>>>> +{
>>>> +ÂÂÂ struct khadas_mcu *khadas_mcu = context;
>>>> +
>>>> +ÂÂÂ return regmap_bulk_read(khadas_mcu->map,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ KHADAS_MCU_USER_DATA_0_REG + offset,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ val, bytes);
>>>> +}
>>>> +
>>>> +static int khadas_mcu_user_mem_write(void *context, unsigned int offset,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ void *val, size_t bytes)
>>>> +{
>>>> +ÂÂÂ struct khadas_mcu *khadas_mcu = context;
>>>> +
>>>> +ÂÂÂ return regmap_bulk_write(khadas_mcu->map,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ KHADAS_MCU_USER_DATA_0_REG + offset,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ val, bytes);
>>>> +}
>>>> +
>>>> +static ssize_t password_store(struct device *dev, struct device_attribute *attr,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ const char *buf, size_t count)
>>>> +{
>>>> +ÂÂÂ struct khadas_mcu *khadas_mcu = dev_get_drvdata(dev);
>>>> +ÂÂÂ int i, ret;
>>>> +
>>>> +ÂÂÂ if (count < 6)
>>>> +ÂÂÂÂÂÂÂ return -EINVAL;
>>>
>>> Possibly this magic 6 value needs proper define. An additional check here for > 6 would be better as well.
>>
>> Ok
>>
>>>
>>>> +
>>>> +ÂÂÂ ret = regmap_write(khadas_mcu->map, KHADAS_MCU_PASSWD_START_REG, 1);
>>>> +ÂÂÂ if (ret)
>>>> +ÂÂÂÂÂÂÂ return ret;
>>>> +
>>>> +ÂÂÂ for (i = 0 ; i < 6 ; ++i) {
>>>> +ÂÂÂÂÂÂÂ ret = regmap_write(khadas_mcu->map,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ KHADAS_MCU_CHECK_USER_PASSWD_REG,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ buf[i]);
>>>> +ÂÂÂÂÂÂÂ if (ret)
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ goto out;
>>>> +ÂÂÂ }
>>>> +
>>>> +ÂÂÂ ret = regmap_write(khadas_mcu->map, KHADAS_MCU_PASSWD_START_REG, 0);
>>>> +ÂÂÂ if (ret)
>>>> +ÂÂÂÂÂÂÂ return ret;
>>>> +
>>>> +ÂÂÂ return count;
>>>> +out:
>>>> +ÂÂÂ regmap_write(khadas_mcu->map, KHADAS_MCU_PASSWD_START_REG, 0);
>>>> +
>>>> +ÂÂÂ return ret;
>>>> +}
>>>> +
>>>> +static DEVICE_ATTR_WO(password);
>>>> +
>>>> +static struct attribute *khadas_mcu_user_mem_sysfs_attributes[] = {
>>>> +ÂÂÂ &dev_attr_password.attr,
>>>> +ÂÂÂ NULL,
>>>> +};
>>>> +
>>>> +static const struct attribute_group khadas_mcu_user_mem_sysfs_attr_group = {
>>>> +ÂÂÂ .attrs = khadas_mcu_user_mem_sysfs_attributes,
>>>> +};
>>>> +
>>>> +static int khadas_mcu_user_mem_probe(struct platform_device *pdev)
>>>> +{
>>>> +ÂÂÂ struct khadas_mcu *khadas_mcu = dev_get_drvdata(pdev->dev.parent);
>>>
>>> You could probably get away with dependency of this structure and the header itself by directly getting hold of regmap from parent device.
>>
>> Ok
>>
>>>
>>>
>>>> +ÂÂÂ struct device *dev = &pdev->dev;
>>>> +ÂÂÂ struct nvmem_device *nvmem;
>>>> +ÂÂÂ struct nvmem_config *econfig;
>>>> +
>>>> +ÂÂÂ econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL);
>>>> +ÂÂÂ if (!econfig)
>>>> +ÂÂÂÂÂÂÂ return -ENOMEM;
>>>> +
>>>> +ÂÂÂ econfig->dev = pdev->dev.parent;
>>>> +ÂÂÂ econfig->name = dev_name(pdev->dev.parent);
>>>> +ÂÂÂ econfig->stride = 1;
>>>> +ÂÂÂ econfig->word_size = 1;
>>>> +ÂÂÂ econfig->reg_read = khadas_mcu_user_mem_read;
>>>> +ÂÂÂ econfig->reg_write = khadas_mcu_user_mem_write;
>>>> +ÂÂÂ econfig->size = 56;
>>> define 56 to make it more readable!
>>
>> Ok
>>
>>>
>>>> +ÂÂÂ econfig->priv = khadas_mcu;
>>>> +
>>>> +ÂÂÂ platform_set_drvdata(pdev, khadas_mcu);
>>>> +
>>>> +ÂÂÂ nvmem = devm_nvmem_register(&pdev->dev, econfig);
>>>> +ÂÂÂ if (IS_ERR(nvmem))
>>>> +ÂÂÂÂÂÂÂ return PTR_ERR(nvmem);
>>>> +
>>>> +ÂÂÂ return sysfs_create_group(&pdev->dev.kobj,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &khadas_mcu_user_mem_sysfs_attr_group);
>>>> +}
>>>> +
>>>> +static const struct platform_device_id khadas_mcu_user_mem_id_table[] = {
>>>> +ÂÂÂ { .name = "khadas-mcu-user-mem", },
>>>> +ÂÂÂ {},
>>>> +};
>>>> +MODULE_DEVICE_TABLE(platform, khadas_mcu_user_mem_id_table);
>>>> +
>>>> +static struct platform_driver khadas_mcu_user_mem_driver = {
>>>> +ÂÂÂ .probe = khadas_mcu_user_mem_probe,
>>>> +ÂÂÂ .driver = {
>>>> +ÂÂÂÂÂÂÂ .name = "khadas-mcu-user-mem",
>>>> +ÂÂÂ },
>>>> +ÂÂÂ .id_table = khadas_mcu_user_mem_id_table,
>>>> +};
>>>> +
>>>> +module_platform_driver(khadas_mcu_user_mem_driver);
>>>> +
>>>> +MODULE_AUTHOR("Neil Armstrong <narmstrong@xxxxxxxxxxxx>");
>>>> +MODULE_DESCRIPTION("Khadas MCU User MEM driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>>
>>
>> Thanks for the review.
>>
>> Neil
>>