[PATCH 3/4] nvmem: add a driver for the Amlogic Meson6/Meson8/Meson8b SoCs
Martin Blumenstingl
martin.blumenstingl at googlemail.com
Mon Oct 2 11:38:44 PDT 2017
On Mon, Oct 2, 2017 at 5:28 PM, Srinivas Kandagatla
<srinivas.kandagatla at linaro.org> wrote:
> Minor comments!!
that was a VERY quick code review (and by this I mean the time between
sending the patch and getting valuable feedback) - many thanks for
this!
>
> On 01/10/17 13:56, Martin Blumenstingl wrote:
>>
>> This adds a driver to access the efuse on Amlogic Meson6, Meson8 and
>> Meson8b SoCs.
>> These SoCs are accessing the efuse IP block directly through the
>> registers in the "secbus" region. This makes it different from the Meson
>> GX efuse driver which uses the "secure monitor" firmware to access the
>> efuse.
>>
>> The efuse on Meson6 can only read one byte at a time, while the efuse on
>> Meson8 and Meson8b always reads 4 bytes at a time. The new driver
>> supports both, but due to lack of hardware Meson6 support was not tested.
>>
>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl at googlemail.com>
>> ---
>> drivers/nvmem/Kconfig | 10 ++
>> drivers/nvmem/Makefile | 2 +
>> drivers/nvmem/meson-mx-efuse.c | 272
>> +++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 284 insertions(+)
>> create mode 100644 drivers/nvmem/meson-mx-efuse.c
>>
>> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
>> +
>> +
>> +static void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32
>> reg,
>> + u32 mask, u32 set)
>> +{
>> + u32 data;
>> +
>> + data = readl(efuse->base + reg);
>> + data &= ~mask;
>> + data |= (set & mask);
>> +
>> + writel(data, efuse->base + reg);
>> +}
>> +
>> +static int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse)
>> +{
>> + int err;
>> +
>> + err = clk_prepare_enable(efuse->core_clk);
>> + if (err)
>> + return err;
>> +
>> + /* power up the efuse */
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0);
>> +
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4,
>> + MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0);
>> +
>> + return 0;
>> +}
>> +
>> +static void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse)
>> +{
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_PD_ENABLE,
>> + MESON_MX_EFUSE_CNTL1_PD_ENABLE);
>> +
>> + clk_disable_unprepare(efuse->core_clk);
>> +}
>> +
>> +static int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse,
>> + unsigned int addr, u32 *value)
>> +{
>> + int err;
>> + u32 regval;
>> +
>> + /* write the address to read */
>> + regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr);
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK,
>> regval);
>> +
>> + /* inform the hardware that we changed the address */
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET,
>> + MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET);
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0);
>> +
>> + /* start the read process */
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_AUTO_RD_START,
>> + MESON_MX_EFUSE_CNTL1_AUTO_RD_START);
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0);
>> +
>> + /*
>> + * perform a dummy read to ensure that the HW has the RD_BUSY bit
>> set
>> + * when polling for the status below.
>> + */
>> + readl(efuse->base + MESON_MX_EFUSE_CNTL1);
>> +
>> + err = readl_poll_timeout_atomic(efuse->base +
>> MESON_MX_EFUSE_CNTL1,
>> + regval,
>> + (!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)),
>> + 1, 1000);
>> + if (err) {
>> + dev_err(efuse->config.dev,
>> + "Timeout while reading efuse address %u\n", addr);
>> + return err;
>> + }
>> +
>> + *value = readl(efuse->base + MESON_MX_EFUSE_CNTL2);
>> +
>> + return 0;
>> +}
>> +
>> +static int meson_mx_efuse_read(void *context, unsigned int offset,
>> + void *buf, size_t bytes)
>> +{
>> + struct meson_mx_efuse *efuse = context;
>> + u32 tmp;
>> + int err, i, addr;
>> +
>> + err = meson_mx_efuse_hw_enable(efuse);
>> + if (err)
>> + return err;
>> +
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE,
>> + MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE);
>> +
>> + for (i = offset; i < offset + bytes; i += efuse->config.word_size)
>> {
>> + addr = i / efuse->config.word_size;
>> +
>> + err = meson_mx_efuse_read_addr(efuse, addr, &tmp);
>> + if (err)
>> + break;
>> +
>> + memcpy(buf + i, &tmp, efuse->config.word_size);
>> + }
>> +
>> + meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
>> + MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0);
>> +
>> + meson_mx_efuse_hw_disable(efuse);
>> +
>> + return err;
>> +}
>
> ...
>>
>> +
>> +static int meson_mx_efuse_probe(struct platform_device *pdev)
>> +{
>> + const struct meson_mx_efuse_platform_data *drvdata;
>> + struct meson_mx_efuse *efuse;
>> + struct resource *res;
>> +
>
>
> ...
>
>> +
>> + efuse->core_clk = devm_clk_get(&pdev->dev, "core");
>> + if (IS_ERR(efuse->core_clk)) {
>> + dev_err(&pdev->dev, "Failed to get core clock\n");
>> + return PTR_ERR(efuse->core_clk);
>> + }
>> +
>> + efuse->nvmem = nvmem_register(&efuse->config);
>> + if (IS_ERR(efuse->nvmem)) {
>> + clk_unprepare(efuse->core_clk);
>
>
> Do you need this???
oh, this is a left-over from an earlier version where I only enabled
the clock in .probe (and disabled it again in .remove)
however, this could break during suspend and it keeps the clock
unnecessarily enabled when it's not needed
>
>> + return PTR_ERR(efuse->nvmem);
>> + }
>> +
>> + platform_set_drvdata(pdev, efuse);
>> +
>> + return 0;
>> +}
>> +
>> +static int meson_mx_efuse_remove(struct platform_device *pdev)
>> +{
>> + struct meson_mx_efuse *efuse = platform_get_drvdata(pdev);
>> + int err;
>> +
>> + err = nvmem_unregister(efuse->nvmem);
>> +
>> + clk_unprepare(efuse->core_clk);
>
>
> Do you need this disable here? AFAIU, this driver would never leave the clk
> prepared unless we are in the middle of read.
yes, good catch here as well - this clk_unprepare call has to be removed
>
>> +
>> + return err;
>> +}
>> +
More information about the linux-amlogic
mailing list