From f7c04f16d76e795f52a8fdbf11d351346fe7032c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 11 Sep 2017 11:00:13 +0200 Subject: nvmem: remove inline in drivers/nvmem/core.c These two functions are defined in .c file, but called just once (at least for now). So, the compiler will fold them into their callers even without the "inline" markers. However, this kind of optimization should not be done by hand. It is compiler's judge after all. Signed-off-by: Masahiro Yamada Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index de54c7f5048a..730cc06656c8 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -945,8 +945,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) } EXPORT_SYMBOL_GPL(nvmem_cell_put); -static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, - void *buf) +static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf) { u8 *p, *b; int i, bit_offset = cell->bit_offset; @@ -1027,8 +1026,8 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) } EXPORT_SYMBOL_GPL(nvmem_cell_read); -static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, - u8 *_buf, int len) +static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, + u8 *_buf, int len) { struct nvmem_device *nvmem = cell->nvmem; int i, rc, nbits, bit_offset = cell->bit_offset; -- cgit v1.2.3 From e701c67c30e65cc887b1ebdd6ef91010f0ca46fa Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 11 Sep 2017 11:00:14 +0200 Subject: nvmem: remove unneeded IS_ENABLED(CONFIG_NVMEM) conditional As you see in drivers/nvmem/Makefile, this C file is compiled only when CONFIG_NVMEM is y or m. So, IS_ENABLED(CONFIG_NVMEM) is always evaluated to 1 in this file. Signed-off-by: Masahiro Yamada Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 730cc06656c8..0a3464f647a4 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -615,7 +615,7 @@ static struct nvmem_device *nvmem_find(const char *name) return to_nvmem_device(d); } -#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +#if IS_ENABLED(CONFIG_OF) /** * of_nvmem_device_get() - Get nvmem device from a given id * @@ -753,7 +753,7 @@ static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) return cell; } -#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +#if IS_ENABLED(CONFIG_OF) /** * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id * -- cgit v1.2.3 From 988437aec0e517f683b2491d43aa1cb4b231f8b8 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sun, 17 Sep 2017 12:33:43 +0200 Subject: nvmem: add snvs_lpgpr driver This is a driver for Low Power General Purpose Register (LPGPR) available on i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip. It is a 32-bit read/write register located in the low power domain. Since LPGPR is located in the battery-backed power domain, LPGPR can be used by any application for retaining data during an SoC power-down mode. Signed-off-by: Oleksij Rempel Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 10 +++ drivers/nvmem/Makefile | 2 + drivers/nvmem/snvs_lpgpr.c | 156 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/nvmem/snvs_lpgpr.c (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 101ced4c84be..ea3044c5d6ee 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -144,4 +144,14 @@ config MESON_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_meson_efuse. +config NVMEM_SNVS_LPGPR + tristate "Support for Low Power General Purpose Register" + depends on SOC_IMX6 || COMPILE_TEST + help + This is a driver for Low Power General Purpose Register (LPGPR) available on + i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip. + + This driver can also be built as a module. If so, the module + will be called nvmem-snvs-lpgpr. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 173140658693..4c589184acee 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -30,3 +30,5 @@ obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o nvmem-vf610-ocotp-y := vf610-ocotp.o obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o nvmem_meson_efuse-y := meson-efuse.o +obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o +nvmem_snvs_lpgpr-y := snvs_lpgpr.o diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c new file mode 100644 index 000000000000..e5c2a4a17f03 --- /dev/null +++ b/drivers/nvmem/snvs_lpgpr.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015 Pengutronix, Steffen Trumtrar + * Copyright (c) 2017 Pengutronix, Oleksij Rempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#define IMX6Q_SNVS_HPLR 0x00 +#define IMX6Q_GPR_SL BIT(5) +#define IMX6Q_SNVS_LPLR 0x34 +#define IMX6Q_GPR_HL BIT(5) +#define IMX6Q_SNVS_LPGPR 0x68 + +struct snvs_lpgpr_cfg { + int offset; + int offset_hplr; + int offset_lplr; +}; + +struct snvs_lpgpr_priv { + struct device_d *dev; + struct regmap *regmap; + struct nvmem_config cfg; + const struct snvs_lpgpr_cfg *dcfg; +}; + +static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = { + .offset = IMX6Q_SNVS_LPGPR, + .offset_hplr = IMX6Q_SNVS_HPLR, + .offset_lplr = IMX6Q_SNVS_LPLR, +}; + +static int snvs_lpgpr_write(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct snvs_lpgpr_priv *priv = context; + const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; + unsigned int lock_reg; + int ret; + + ret = regmap_read(priv->regmap, dcfg->offset_hplr, &lock_reg); + if (ret < 0) + return ret; + + if (lock_reg & IMX6Q_GPR_SL) + return -EPERM; + + ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg); + if (ret < 0) + return ret; + + if (lock_reg & IMX6Q_GPR_HL) + return -EPERM; + + return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val, + bytes / 4); +} + +static int snvs_lpgpr_read(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct snvs_lpgpr_priv *priv = context; + const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; + + return regmap_bulk_read(priv->regmap, dcfg->offset + offset, + val, bytes / 4); +} + +static int snvs_lpgpr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *syscon_node; + struct snvs_lpgpr_priv *priv; + struct nvmem_config *cfg; + struct nvmem_device *nvmem; + const struct snvs_lpgpr_cfg *dcfg; + + if (!node) + return -ENOENT; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dcfg = of_device_get_match_data(dev); + if (!dcfg) + return -EINVAL; + + syscon_node = of_get_parent(node); + if (!syscon_node) + return -ENODEV; + + priv->regmap = syscon_node_to_regmap(syscon_node); + of_node_put(syscon_node); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->dcfg = dcfg; + + cfg = &priv->cfg; + cfg->priv = priv; + cfg->name = dev_name(dev); + cfg->dev = dev; + cfg->stride = 4, + cfg->word_size = 4, + cfg->size = 4, + cfg->owner = THIS_MODULE, + cfg->reg_read = snvs_lpgpr_read, + cfg->reg_write = snvs_lpgpr_write, + + nvmem = nvmem_register(cfg); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + platform_set_drvdata(pdev, nvmem); + + return 0; +} + +static int snvs_lpgpr_remove(struct platform_device *pdev) +{ + struct nvmem_device *nvmem = platform_get_drvdata(pdev); + + return nvmem_unregister(nvmem); +} + +static const struct of_device_id snvs_lpgpr_dt_ids[] = { + { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q }, + { .compatible = "fsl,imx6ul-snvs-lpgpr", + .data = &snvs_lpgpr_cfg_imx6q }, + { }, +}; +MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids); + +static struct platform_driver snvs_lpgpr_driver = { + .probe = snvs_lpgpr_probe, + .remove = snvs_lpgpr_remove, + .driver = { + .name = "snvs_lpgpr", + .of_match_table = snvs_lpgpr_dt_ids, + }, +}; +module_platform_driver(snvs_lpgpr_driver); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 7a15cf2af480440bacf2fc0010f1fa1a1f3980d6 Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Mon, 9 Oct 2017 15:26:38 +0200 Subject: nvmem: rockchip: add support for RK3368 This adds the necessary function for handling support on RK3368 SoCs Signed-off-by: Romain Perier Acked-by: Rob Herring Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt | 1 + drivers/nvmem/rockchip-efuse.c | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'drivers/nvmem') diff --git a/Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt b/Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt index 1ff02afdc55a..60bec4782806 100644 --- a/Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt +++ b/Documentation/devicetree/bindings/nvmem/rockchip-efuse.txt @@ -6,6 +6,7 @@ Required properties: - "rockchip,rk3188-efuse" - for RK3188 SoCs. - "rockchip,rk3228-efuse" - for RK3228 SoCs. - "rockchip,rk3288-efuse" - for RK3288 SoCs. + - "rockchip,rk3368-efuse" - for RK3368 SoCs. - "rockchip,rk3399-efuse" - for RK3399 SoCs. - reg: Should contain the registers location and exact eFuse size - clocks: Should be the clock id of eFuse diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index 63e3eb55f3ac..eb4c530c2564 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c @@ -177,6 +177,10 @@ static const struct of_device_id rockchip_efuse_match[] = { .compatible = "rockchip,rk3288-efuse", .data = (void *)&rockchip_rk3288_efuse_read, }, + { + .compatible = "rockchip,rk3368-efuse", + .data = (void *)&rockchip_rk3288_efuse_read, + }, { .compatible = "rockchip,rk3399-efuse", .data = (void *)&rockchip_rk3399_efuse_read, -- cgit v1.2.3 From 9593ad32b8be82f3abd6002336a93c405361b9fd Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Mon, 9 Oct 2017 15:26:40 +0200 Subject: nvmem: meson-efuse: indicate that this driver is only for Meson GX SoCs The current Amlogic Meson eFuse driver only supports the 64-bit SoCs (GXBB and newer). Older SoCs cannot be supported by the same driver because they do not use the meson secure monitor firmware to access the hardware. Signed-off-by: Martin Blumenstingl Signed-off-by: Srinivas Kandagatla Acked-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt | 2 +- drivers/nvmem/Kconfig | 4 ++-- drivers/nvmem/meson-efuse.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/nvmem') diff --git a/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt b/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt index fafd85bd67a6..e3298e18de26 100644 --- a/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt +++ b/Documentation/devicetree/bindings/nvmem/amlogic-efuse.txt @@ -1,4 +1,4 @@ -= Amlogic eFuse device tree bindings = += Amlogic Meson GX eFuse device tree bindings = Required properties: - compatible: should be "amlogic,meson-gxbb-efuse" diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index ea3044c5d6ee..2a07cf169b52 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -135,11 +135,11 @@ config NVMEM_VF610_OCOTP be called nvmem-vf610-ocotp. config MESON_EFUSE - tristate "Amlogic eFuse Support" + tristate "Amlogic Meson GX eFuse Support" depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM help This is a driver to retrieve specific values from the eFuse found on - the Amlogic Meson SoCs. + the Amlogic Meson GX SoCs. This driver can also be built as a module. If so, the module will be called nvmem_meson_efuse. diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c index 70bfc9839bb2..1ea3cd24a508 100644 --- a/drivers/nvmem/meson-efuse.c +++ b/drivers/nvmem/meson-efuse.c @@ -1,5 +1,5 @@ /* - * Amlogic eFuse Driver + * Amlogic Meson GX eFuse Driver * * Copyright (c) 2016 Endless Computers, Inc. * Author: Carlo Caione @@ -89,5 +89,5 @@ static struct platform_driver meson_efuse_driver = { module_platform_driver(meson_efuse_driver); MODULE_AUTHOR("Carlo Caione "); -MODULE_DESCRIPTION("Amlogic Meson NVMEM driver"); +MODULE_DESCRIPTION("Amlogic Meson GX NVMEM driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8caef1fa9176c4789b74c806434517b3adf7544a Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Mon, 9 Oct 2017 15:26:41 +0200 Subject: nvmem: add a driver for the Amlogic Meson6/Meson8/Meson8b SoCs 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. The hardware also supports writing. However, this is currently not supported by the driver. Signed-off-by: Martin Blumenstingl Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 10 ++ drivers/nvmem/Makefile | 2 + drivers/nvmem/meson-mx-efuse.c | 265 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 drivers/nvmem/meson-mx-efuse.c (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 2a07cf169b52..eb09916744bf 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -144,6 +144,16 @@ config MESON_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_meson_efuse. +config MESON_MX_EFUSE + tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support" + depends on ARCH_MESON || COMPILE_TEST + help + This is a driver to retrieve specific values from the eFuse found on + the Amlogic Meson6, Meson8 and Meson8b SoCs. + + This driver can also be built as a module. If so, the module + will be called nvmem_meson_mx_efuse. + config NVMEM_SNVS_LPGPR tristate "Support for Low Power General Purpose Register" depends on SOC_IMX6 || COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 4c589184acee..362f394da65d 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -30,5 +30,7 @@ obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o nvmem-vf610-ocotp-y := vf610-ocotp.o obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o nvmem_meson_efuse-y := meson-efuse.o +obj-$(CONFIG_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o +nvmem_meson_mx_efuse-y := meson-mx-efuse.o obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o nvmem_snvs_lpgpr-y := snvs_lpgpr.o diff --git a/drivers/nvmem/meson-mx-efuse.c b/drivers/nvmem/meson-mx-efuse.c new file mode 100644 index 000000000000..a346b4923550 --- /dev/null +++ b/drivers/nvmem/meson-mx-efuse.c @@ -0,0 +1,265 @@ +/* + * Amlogic Meson6, Meson8 and Meson8b eFuse Driver + * + * Copyright (c) 2017 Martin Blumenstingl + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MESON_MX_EFUSE_CNTL1 0x04 +#define MESON_MX_EFUSE_CNTL1_PD_ENABLE BIT(27) +#define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY BIT(26) +#define MESON_MX_EFUSE_CNTL1_AUTO_RD_START BIT(25) +#define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE BIT(24) +#define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA GENMASK(23, 16) +#define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY BIT(14) +#define MESON_MX_EFUSE_CNTL1_AUTO_WR_START BIT(13) +#define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE BIT(12) +#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET BIT(11) +#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK GENMASK(10, 0) + +#define MESON_MX_EFUSE_CNTL2 0x08 + +#define MESON_MX_EFUSE_CNTL4 0x10 +#define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE BIT(10) + +struct meson_mx_efuse_platform_data { + const char *name; + unsigned int word_size; +}; + +struct meson_mx_efuse { + void __iomem *base; + struct clk *core_clk; + struct nvmem_device *nvmem; + struct nvmem_config config; +}; + +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 const struct meson_mx_efuse_platform_data meson6_efuse_data = { + .name = "meson6-efuse", + .word_size = 1, +}; + +static const struct meson_mx_efuse_platform_data meson8_efuse_data = { + .name = "meson8-efuse", + .word_size = 4, +}; + +static const struct meson_mx_efuse_platform_data meson8b_efuse_data = { + .name = "meson8b-efuse", + .word_size = 4, +}; + +static const struct of_device_id meson_mx_efuse_match[] = { + { .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data }, + { .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data }, + { .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, meson_mx_efuse_match); + +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; + + drvdata = of_device_get_match_data(&pdev->dev); + if (!drvdata) + return -EINVAL; + + efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); + if (!efuse) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + efuse->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(efuse->base)) + return PTR_ERR(efuse->base); + + efuse->config.name = devm_kstrdup(&pdev->dev, drvdata->name, + GFP_KERNEL); + efuse->config.owner = THIS_MODULE; + efuse->config.dev = &pdev->dev; + efuse->config.priv = efuse; + efuse->config.stride = drvdata->word_size; + efuse->config.word_size = drvdata->word_size; + efuse->config.size = SZ_512; + efuse->config.read_only = true; + efuse->config.reg_read = meson_mx_efuse_read; + + 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)) + 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); + + return nvmem_unregister(efuse->nvmem); +} + +static struct platform_driver meson_mx_efuse_driver = { + .probe = meson_mx_efuse_probe, + .remove = meson_mx_efuse_remove, + .driver = { + .name = "meson-mx-efuse", + .of_match_table = meson_mx_efuse_match, + }, +}; + +module_platform_driver(meson_mx_efuse_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 01d35cabd5abda28c352f63c87109c477fc84886 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 21 Oct 2017 01:57:38 +0900 Subject: nvmem: imx-iim: use stack for nvmem_config instead of malloc'ing it nvmem_register() copies all the members of nvmem_config to nvmem_device. So, nvmem_config is one-time use data during probing. There is no point to keep it until the driver detach. Using stack should be no problem because nvmem_config is pretty small. Signed-off-by: Masahiro Yamada Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-iim.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/imx-iim.c b/drivers/nvmem/imx-iim.c index 52ff65e0673f..a5992602709a 100644 --- a/drivers/nvmem/imx-iim.c +++ b/drivers/nvmem/imx-iim.c @@ -34,7 +34,6 @@ struct imx_iim_drvdata { struct iim_priv { void __iomem *base; struct clk *clk; - struct nvmem_config nvmem; }; static int imx_iim_read(void *context, unsigned int offset, @@ -108,7 +107,7 @@ static int imx_iim_probe(struct platform_device *pdev) struct resource *res; struct iim_priv *iim; struct nvmem_device *nvmem; - struct nvmem_config *cfg; + struct nvmem_config cfg = {}; const struct imx_iim_drvdata *drvdata = NULL; iim = devm_kzalloc(dev, sizeof(*iim), GFP_KERNEL); @@ -130,19 +129,17 @@ static int imx_iim_probe(struct platform_device *pdev) if (IS_ERR(iim->clk)) return PTR_ERR(iim->clk); - cfg = &iim->nvmem; - - cfg->name = "imx-iim", - cfg->read_only = true, - cfg->word_size = 1, - cfg->stride = 1, - cfg->owner = THIS_MODULE, - cfg->reg_read = imx_iim_read, - cfg->dev = dev; - cfg->size = drvdata->nregs; - cfg->priv = iim; - - nvmem = nvmem_register(cfg); + cfg.name = "imx-iim", + cfg.read_only = true, + cfg.word_size = 1, + cfg.stride = 1, + cfg.owner = THIS_MODULE, + cfg.reg_read = imx_iim_read, + cfg.dev = dev; + cfg.size = drvdata->nregs; + cfg.priv = iim; + + nvmem = nvmem_register(&cfg); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); -- cgit v1.2.3 From 4dd5f60e9a841da5c14c2a0a23290adddb4b5087 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 21 Oct 2017 01:57:39 +0900 Subject: nvmem: mtk-efuse: use stack for nvmem_config instead of malloc'ing it nvmem_register() copies all the members of nvmem_config to nvmem_device. So, nvmem_config is one-time use data during probing. There is no point to keep it until the driver detach. Using stack should be no problem because nvmem_config is pretty small. Signed-off-by: Masahiro Yamada Acked-by: Sean Wang Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/mtk-efuse.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index 32fd572e18c5..fa7a0f66b37e 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -49,7 +49,7 @@ static int mtk_efuse_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct nvmem_device *nvmem; - struct nvmem_config *econfig; + struct nvmem_config econfig = {}; void __iomem *base; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -57,19 +57,15 @@ static int mtk_efuse_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL); - if (!econfig) - return -ENOMEM; - - econfig->stride = 4; - econfig->word_size = 4; - econfig->reg_read = mtk_reg_read; - econfig->reg_write = mtk_reg_write; - econfig->size = resource_size(res); - econfig->priv = base; - econfig->dev = dev; - econfig->owner = THIS_MODULE; - nvmem = nvmem_register(econfig); + econfig.stride = 4; + econfig.word_size = 4; + econfig.reg_read = mtk_reg_read; + econfig.reg_write = mtk_reg_write; + econfig.size = resource_size(res); + econfig.priv = base; + econfig.dev = dev; + econfig.owner = THIS_MODULE; + nvmem = nvmem_register(&econfig); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); -- cgit v1.2.3 From a48f1fff7e2fe5fbaf0295ff7705909fd31f1c35 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 21 Oct 2017 01:57:40 +0900 Subject: nvmem: mtk-efuse: fix different address space warnings of sparse Fix the following sparse warnings: drivers/nvmem/mtk-efuse.c:24:30: warning: incorrect type in initializer (different address spaces) drivers/nvmem/mtk-efuse.c:24:30: expected void [noderef] *base drivers/nvmem/mtk-efuse.c:24:30: got void *context drivers/nvmem/mtk-efuse.c:37:30: warning: incorrect type in initializer (different address spaces) drivers/nvmem/mtk-efuse.c:37:30: expected void [noderef] *base drivers/nvmem/mtk-efuse.c:37:30: got void *context drivers/nvmem/mtk-efuse.c:69:23: warning: incorrect type in assignment (different address spaces) drivers/nvmem/mtk-efuse.c:69:23: expected void *priv drivers/nvmem/mtk-efuse.c:69:23: got void [noderef] *[assigned] base The type of nvmem_config->priv is (void *), so sparse complains about assignment of the base address with (void __iomem *) type. Even if we cast it out, sparse still warns: warning: cast removes address space of expression Of course, we can shut up the sparse by marking __force, but a more correct way is to put the base address into driver private data. Signed-off-by: Masahiro Yamada Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/mtk-efuse.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index fa7a0f66b37e..c4058b598703 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -18,15 +18,19 @@ #include #include +struct mtk_efuse_priv { + void __iomem *base; +}; + static int mtk_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) { - void __iomem *base = context; + struct mtk_efuse_priv *priv = context; u32 *val = _val; int i = 0, words = bytes / 4; while (words--) - *val++ = readl(base + reg + (i++ * 4)); + *val++ = readl(priv->base + reg + (i++ * 4)); return 0; } @@ -34,12 +38,12 @@ static int mtk_reg_read(void *context, static int mtk_reg_write(void *context, unsigned int reg, void *_val, size_t bytes) { - void __iomem *base = context; + struct mtk_efuse_priv *priv = context; u32 *val = _val; int i = 0, words = bytes / 4; while (words--) - writel(*val++, base + reg + (i++ * 4)); + writel(*val++, priv->base + reg + (i++ * 4)); return 0; } @@ -50,19 +54,23 @@ static int mtk_efuse_probe(struct platform_device *pdev) struct resource *res; struct nvmem_device *nvmem; struct nvmem_config econfig = {}; - void __iomem *base; + struct mtk_efuse_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); econfig.stride = 4; econfig.word_size = 4; econfig.reg_read = mtk_reg_read; econfig.reg_write = mtk_reg_write; econfig.size = resource_size(res); - econfig.priv = base; + econfig.priv = priv; econfig.dev = dev; econfig.owner = THIS_MODULE; nvmem = nvmem_register(&econfig); -- cgit v1.2.3 From ec3672b81fc8ff490e478bc5124f68c2be7174ee Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 21 Oct 2017 01:57:41 +0900 Subject: nvmem: qfprom: fix different address space warnings of sparse Fix the following sparse warnings: drivers/nvmem/qfprom.c:23:30: warning: incorrect type in initializer (different address spaces) drivers/nvmem/qfprom.c:23:30: expected void [noderef] *base drivers/nvmem/qfprom.c:23:30: got void *context drivers/nvmem/qfprom.c:36:30: warning: incorrect type in initializer (different address spaces) drivers/nvmem/qfprom.c:36:30: expected void [noderef] *base drivers/nvmem/qfprom.c:36:30: got void *context drivers/nvmem/qfprom.c:76:22: warning: incorrect type in assignment (different address spaces) drivers/nvmem/qfprom.c:76:22: expected void *static [toplevel] [assigned] priv drivers/nvmem/qfprom.c:76:22: got void [noderef] *[assigned] base The type of nvmem_config->priv is (void *), so sparse complains about assignment of the base address with (void __iomem *) type. Even if we cast it out, sparse still warns: warning: cast removes address space of expression Of course, we can shut up the sparse by marking __force, but a more correct way is to put the base address into driver private data. Signed-off-by: Masahiro Yamada Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/qfprom.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index 2bdb6c389328..b96730e99580 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -17,15 +17,19 @@ #include #include +struct qfprom_priv { + void __iomem *base; +}; + static int qfprom_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) { - void __iomem *base = context; + struct qfprom_priv *priv = context; u8 *val = _val; int i = 0, words = bytes; while (words--) - *val++ = readb(base + reg + i++); + *val++ = readb(priv->base + reg + i++); return 0; } @@ -33,12 +37,12 @@ static int qfprom_reg_read(void *context, static int qfprom_reg_write(void *context, unsigned int reg, void *_val, size_t bytes) { - void __iomem *base = context; + struct qfprom_priv *priv = context; u8 *val = _val; int i = 0, words = bytes; while (words--) - writeb(*val++, base + reg + i++); + writeb(*val++, priv->base + reg + i++); return 0; } @@ -64,16 +68,20 @@ static int qfprom_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct nvmem_device *nvmem; - void __iomem *base; + struct qfprom_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); econfig.size = resource_size(res); econfig.dev = dev; - econfig.priv = base; + econfig.priv = priv; nvmem = nvmem_register(&econfig); if (IS_ERR(nvmem)) -- cgit v1.2.3 From 17eb18d674d586e609a3e268975edd728d5c84a3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 21 Oct 2017 01:57:42 +0900 Subject: nvmem: set nvmem->owner to nvmem->dev->driver->owner if unset All nvmem drivers are supposed to set the owner field of struct nvmem_config, but this matches nvmem->dev->driver->owner. As far as I see in drivers/nvmem/ directory, all the drivers are the case. So, make nvmem_register() set the nvmem's owner to the associated driver's owner unless nvmem_config sets otherwise. Remove .owner settings in the drivers that are now redundant. Signed-off-by: Masahiro Yamada Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/bcm-ocotp.c | 1 - drivers/nvmem/core.c | 2 ++ drivers/nvmem/imx-iim.c | 1 - drivers/nvmem/imx-ocotp.c | 1 - drivers/nvmem/lpc18xx_eeprom.c | 1 - drivers/nvmem/lpc18xx_otp.c | 1 - drivers/nvmem/meson-efuse.c | 1 - drivers/nvmem/mtk-efuse.c | 1 - drivers/nvmem/mxs-ocotp.c | 1 - drivers/nvmem/qfprom.c | 1 - drivers/nvmem/rockchip-efuse.c | 1 - drivers/nvmem/sunxi_sid.c | 1 - drivers/nvmem/vf610-ocotp.c | 1 - 13 files changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c index 3c56e3b2bd65..5e9e324427f9 100644 --- a/drivers/nvmem/bcm-ocotp.c +++ b/drivers/nvmem/bcm-ocotp.c @@ -232,7 +232,6 @@ static struct nvmem_config bcm_otpc_nvmem_config = { .read_only = false, .word_size = 4, .stride = 4, - .owner = THIS_MODULE, .reg_read = bcm_otpc_read, .reg_write = bcm_otpc_write, }; diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 864904594063..5a5cefd12153 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -462,6 +462,8 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->id = rval; nvmem->owner = config->owner; + if (!nvmem->owner && config->dev->driver) + nvmem->owner = config->dev->driver->owner; nvmem->stride = config->stride; nvmem->word_size = config->word_size; nvmem->size = config->size; diff --git a/drivers/nvmem/imx-iim.c b/drivers/nvmem/imx-iim.c index a5992602709a..52cfe91d9762 100644 --- a/drivers/nvmem/imx-iim.c +++ b/drivers/nvmem/imx-iim.c @@ -133,7 +133,6 @@ static int imx_iim_probe(struct platform_device *pdev) cfg.read_only = true, cfg.word_size = 1, cfg.stride = 1, - cfg.owner = THIS_MODULE, cfg.reg_read = imx_iim_read, cfg.dev = dev; cfg.size = drvdata->nregs; diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 193ca8fd350a..e57e2a57aa3f 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -303,7 +303,6 @@ static struct nvmem_config imx_ocotp_nvmem_config = { .read_only = false, .word_size = 4, .stride = 4, - .owner = THIS_MODULE, .reg_read = imx_ocotp_read, .reg_write = imx_ocotp_write, }; diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c index 6c7e2c424a4e..b1af966206a6 100644 --- a/drivers/nvmem/lpc18xx_eeprom.c +++ b/drivers/nvmem/lpc18xx_eeprom.c @@ -159,7 +159,6 @@ static struct nvmem_config lpc18xx_nvmem_config = { .word_size = 4, .reg_read = lpc18xx_eeprom_read, .reg_write = lpc18xx_eeprom_gather_write, - .owner = THIS_MODULE, }; static int lpc18xx_eeprom_probe(struct platform_device *pdev) diff --git a/drivers/nvmem/lpc18xx_otp.c b/drivers/nvmem/lpc18xx_otp.c index be8d07403ffc..95268db155e9 100644 --- a/drivers/nvmem/lpc18xx_otp.c +++ b/drivers/nvmem/lpc18xx_otp.c @@ -64,7 +64,6 @@ static struct nvmem_config lpc18xx_otp_nvmem_config = { .read_only = true, .word_size = LPC18XX_OTP_WORD_SIZE, .stride = LPC18XX_OTP_WORD_SIZE, - .owner = THIS_MODULE, .reg_read = lpc18xx_otp_read, }; diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c index 1ea3cd24a508..a43c68f90937 100644 --- a/drivers/nvmem/meson-efuse.c +++ b/drivers/nvmem/meson-efuse.c @@ -37,7 +37,6 @@ static int meson_efuse_read(void *context, unsigned int offset, static struct nvmem_config econfig = { .name = "meson-efuse", - .owner = THIS_MODULE, .stride = 1, .word_size = 1, .read_only = true, diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index c4058b598703..9ee3479cfc7b 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -72,7 +72,6 @@ static int mtk_efuse_probe(struct platform_device *pdev) econfig.size = resource_size(res); econfig.priv = priv; econfig.dev = dev; - econfig.owner = THIS_MODULE; nvmem = nvmem_register(&econfig); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); diff --git a/drivers/nvmem/mxs-ocotp.c b/drivers/nvmem/mxs-ocotp.c index d26dd03cec80..7018e2ef5714 100644 --- a/drivers/nvmem/mxs-ocotp.c +++ b/drivers/nvmem/mxs-ocotp.c @@ -118,7 +118,6 @@ static struct nvmem_config ocotp_config = { .name = "mxs-ocotp", .stride = 16, .word_size = 4, - .owner = THIS_MODULE, .reg_read = mxs_ocotp_read, }; diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index b96730e99580..cb3b48b47d64 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -56,7 +56,6 @@ static int qfprom_remove(struct platform_device *pdev) static struct nvmem_config econfig = { .name = "qfprom", - .owner = THIS_MODULE, .stride = 1, .word_size = 1, .reg_read = qfprom_reg_read, diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index eb4c530c2564..123de77ca5d6 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c @@ -149,7 +149,6 @@ static int rockchip_rk3399_efuse_read(void *context, unsigned int offset, static struct nvmem_config econfig = { .name = "rockchip-efuse", - .owner = THIS_MODULE, .stride = 1, .word_size = 1, .read_only = true, diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 0d6648be93b8..1c3b5cf89212 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -40,7 +40,6 @@ static struct nvmem_config econfig = { .read_only = true, .stride = 4, .word_size = 1, - .owner = THIS_MODULE, }; struct sunxi_sid_cfg { diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c index 72e4faabce29..5ae9e002f195 100644 --- a/drivers/nvmem/vf610-ocotp.c +++ b/drivers/nvmem/vf610-ocotp.c @@ -206,7 +206,6 @@ static int vf610_ocotp_read(void *context, unsigned int offset, static struct nvmem_config ocotp_config = { .name = "ocotp", - .owner = THIS_MODULE, .stride = 4, .word_size = 4, .reg_read = vf610_ocotp_read, -- cgit v1.2.3 From 71c5dd5002b11d240c9b0e4adc972903183000aa Mon Sep 17 00:00:00 2001 From: Keiji Hayashibara Date: Tue, 24 Oct 2017 10:54:26 +0100 Subject: nvmem: uniphier: add UniPhier eFuse driver Add eFuse driver for Socionext UniPhier series SoC. Note that eFuse device is under soc-glue and this register implements as read only. Signed-off-by: Keiji Hayashibara Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 11 +++++ drivers/nvmem/Makefile | 2 + drivers/nvmem/uniphier-efuse.c | 97 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 drivers/nvmem/uniphier-efuse.c (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index eb09916744bf..ff505af064ba 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -123,6 +123,17 @@ config NVMEM_SUNXI_SID This driver can also be built as a module. If so, the module will be called nvmem_sunxi_sid. +config UNIPHIER_EFUSE + tristate "UniPhier SoCs eFuse support" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on HAS_IOMEM + help + This is a simple driver to dump specified values of UniPhier SoC + from eFuse. + + This driver can also be built as a module. If so, the module + will be called nvmem-uniphier-efuse. + config NVMEM_VF610_OCOTP tristate "VF610 SoC OCOTP support" depends on SOC_VF610 || COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 362f394da65d..64849e946d50 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -26,6 +26,8 @@ obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o nvmem_rockchip_efuse-y := rockchip-efuse.o obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o nvmem_sunxi_sid-y := sunxi_sid.o +obj-$(CONFIG_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o +nvmem-uniphier-efuse-y := uniphier-efuse.o obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o nvmem-vf610-ocotp-y := vf610-ocotp.o obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o diff --git a/drivers/nvmem/uniphier-efuse.c b/drivers/nvmem/uniphier-efuse.c new file mode 100644 index 000000000000..9d278b4e1dc7 --- /dev/null +++ b/drivers/nvmem/uniphier-efuse.c @@ -0,0 +1,97 @@ +/* + * UniPhier eFuse driver + * + * Copyright (C) 2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include + +struct uniphier_efuse_priv { + void __iomem *base; +}; + +static int uniphier_reg_read(void *context, + unsigned int reg, void *_val, size_t bytes) +{ + struct uniphier_efuse_priv *priv = context; + u32 *val = _val; + int offs; + + for (offs = 0; offs < bytes; offs += sizeof(u32)) + *val++ = readl(priv->base + reg + offs); + + return 0; +} + +static int uniphier_efuse_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct nvmem_device *nvmem; + struct nvmem_config econfig = {}; + struct uniphier_efuse_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + econfig.stride = 4; + econfig.word_size = 4; + econfig.read_only = true; + econfig.reg_read = uniphier_reg_read; + econfig.size = resource_size(res); + econfig.priv = priv; + econfig.dev = dev; + nvmem = nvmem_register(&econfig); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + platform_set_drvdata(pdev, nvmem); + + return 0; +} + +static int uniphier_efuse_remove(struct platform_device *pdev) +{ + struct nvmem_device *nvmem = platform_get_drvdata(pdev); + + return nvmem_unregister(nvmem); +} + +static const struct of_device_id uniphier_efuse_of_match[] = { + { .compatible = "socionext,uniphier-efuse",}, + {/* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, uniphier_efuse_of_match); + +static struct platform_driver uniphier_efuse_driver = { + .probe = uniphier_efuse_probe, + .remove = uniphier_efuse_remove, + .driver = { + .name = "uniphier-efuse", + .of_match_table = uniphier_efuse_of_match, + }, +}; +module_platform_driver(uniphier_efuse_driver); + +MODULE_AUTHOR("Keiji Hayashibara "); +MODULE_DESCRIPTION("UniPhier eFuse driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 9d6a8dab8d8b94975d7d78a7866053bd1c9444ca Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 24 Oct 2017 10:54:27 +0100 Subject: nvmem: imx-ocotp: Restrict OTP write to IMX6 processors i.MX7S/D have a different scheme for addressing the OTP registers inside the OCOTP block. Currently it's possible to address the wrong OTP registers given the disparity between IMX6 and IMX7 OTP addressing. Since OTP programming is one-time destructive its important we restrict this interface ASAP. Fixes: 0642bac7da42 ("nvmem: imx-ocotp: add write support") Signed-off-by: Bryan O'Donoghue Acked-by: Philipp Zabel Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index e57e2a57aa3f..653cff9d9927 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -346,6 +346,8 @@ static int imx_ocotp_probe(struct platform_device *pdev) imx_ocotp_nvmem_config.dev = dev; imx_ocotp_nvmem_config.priv = priv; priv->config = &imx_ocotp_nvmem_config; + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx7d-ocotp")) + imx_ocotp_nvmem_config.read_only = true; nvmem = nvmem_register(&imx_ocotp_nvmem_config); if (IS_ERR(nvmem)) -- cgit v1.2.3 From e20d2b291ba2f5441fd4aacd746c21e60d48b559 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 24 Oct 2017 10:54:28 +0100 Subject: nvmem: imx-ocotp: Pass parameters via a struct It will be useful in later patches to know the register access mode and bit-shift to apply to a given input offset. Fixes: 0642bac7da42 ("nvmem: imx-ocotp: add write support") Signed-off-by: Bryan O'Donoghue Reviewed-by: Philipp Zabel Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 653cff9d9927..d94ed2d69a10 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -53,11 +53,15 @@ static DEFINE_MUTEX(ocotp_mutex); +struct ocotp_params { + unsigned int nregs; +}; + struct ocotp_priv { struct device *dev; struct clk *clk; void __iomem *base; - unsigned int nregs; + const struct ocotp_params *params; struct nvmem_config *config; }; @@ -121,8 +125,8 @@ static int imx_ocotp_read(void *context, unsigned int offset, index = offset >> 2; count = bytes >> 2; - if (count > (priv->nregs - index)) - count = priv->nregs - index; + if (count > (priv->params->nregs - index)) + count = priv->params->nregs - index; mutex_lock(&ocotp_mutex); @@ -307,12 +311,32 @@ static struct nvmem_config imx_ocotp_nvmem_config = { .reg_write = imx_ocotp_write, }; +static const struct ocotp_params imx6q_params = { + .nregs = 128, +}; + +static const struct ocotp_params imx6sl_params = { + .nregs = 64, +}; + +static const struct ocotp_params imx6sx_params = { + .nregs = 128, +}; + +static const struct ocotp_params imx6ul_params = { + .nregs = 128, +}; + +static const struct ocotp_params imx7d_params = { + .nregs = 64, +}; + static const struct of_device_id imx_ocotp_dt_ids[] = { - { .compatible = "fsl,imx6q-ocotp", (void *)128 }, - { .compatible = "fsl,imx6sl-ocotp", (void *)64 }, - { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, - { .compatible = "fsl,imx6ul-ocotp", (void *)128 }, - { .compatible = "fsl,imx7d-ocotp", (void *)64 }, + { .compatible = "fsl,imx6q-ocotp", .data = &imx6q_params }, + { .compatible = "fsl,imx6sl-ocotp", .data = &imx6sl_params }, + { .compatible = "fsl,imx6sx-ocotp", .data = &imx6sx_params }, + { .compatible = "fsl,imx6ul-ocotp", .data = &imx6ul_params }, + { .compatible = "fsl,imx7d-ocotp", .data = &imx7d_params }, { }, }; MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); @@ -341,8 +365,8 @@ static int imx_ocotp_probe(struct platform_device *pdev) return PTR_ERR(priv->clk); of_id = of_match_device(imx_ocotp_dt_ids, dev); - priv->nregs = (unsigned long)of_id->data; - imx_ocotp_nvmem_config.size = 4 * priv->nregs; + priv->params = of_device_get_match_data(&pdev->dev); + imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; imx_ocotp_nvmem_config.dev = dev; imx_ocotp_nvmem_config.priv = priv; priv->config = &imx_ocotp_nvmem_config; -- cgit v1.2.3 From ffd9115f6548c51d347489e76bfb7d10e728f43d Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 24 Oct 2017 10:54:29 +0100 Subject: nvmem: imx-ocotp: Add support for banked OTP addressing The i.MX7S/D takes the bank address in the CTRLn.ADDR field and the data value in one of the DATAx {0, 1, 2, 3} register fields. The current write routine is based on writing the CTRLn.ADDR field and writing a single DATA register only. Fixes: 0642bac7da42 ("nvmem: imx-ocotp: add write support") Signed-off-by: Bryan O'Donoghue Reviewed-by: Philipp Zabel Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 68 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index d94ed2d69a10..bf95a0ecd0dc 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -40,7 +40,10 @@ #define IMX_OCOTP_ADDR_CTRL_SET 0x0004 #define IMX_OCOTP_ADDR_CTRL_CLR 0x0008 #define IMX_OCOTP_ADDR_TIMING 0x0010 -#define IMX_OCOTP_ADDR_DATA 0x0020 +#define IMX_OCOTP_ADDR_DATA0 0x0020 +#define IMX_OCOTP_ADDR_DATA1 0x0030 +#define IMX_OCOTP_ADDR_DATA2 0x0040 +#define IMX_OCOTP_ADDR_DATA3 0x0050 #define IMX_OCOTP_BM_CTRL_ADDR 0x0000007F #define IMX_OCOTP_BM_CTRL_BUSY 0x00000100 @@ -55,6 +58,7 @@ static DEFINE_MUTEX(ocotp_mutex); struct ocotp_params { unsigned int nregs; + unsigned int bank_address_words; }; struct ocotp_priv { @@ -176,6 +180,7 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, u32 timing = 0; u32 ctrl; u8 waddr; + u8 word = 0; /* allow only writing one complete OTP word at a time */ if ((bytes != priv->config->word_size) || @@ -228,8 +233,23 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, * description. Both the unlock code and address can be written in the * same operation. */ - /* OTP write/read address specifies one of 128 word address locations */ - waddr = offset / 4; + if (priv->params->bank_address_words != 0) { + /* + * In banked/i.MX7 mode the OTP register bank goes into waddr + * see i.MX 7Solo Applications Processor Reference Manual, Rev. + * 0.1 section 6.4.3.1 + */ + offset = offset / priv->config->word_size; + waddr = offset / priv->params->bank_address_words; + word = offset & (priv->params->bank_address_words - 1); + } else { + /* + * Non-banked i.MX6 mode. + * OTP write/read address specifies one of 128 word address + * locations + */ + waddr = offset / 4; + } ctrl = readl(priv->base + IMX_OCOTP_ADDR_CTRL); ctrl &= ~IMX_OCOTP_BM_CTRL_ADDR; @@ -255,8 +275,43 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, * shift right (with zero fill). This shifting is required to program * the OTP serially. During the write operation, HW_OCOTP_DATA cannot be * modified. + * Note: on i.MX7 there are four data fields to write for banked write + * with the fuse blowing operation only taking place after data0 + * has been written. This is why data0 must always be the last + * register written. */ - writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA); + if (priv->params->bank_address_words != 0) { + /* Banked/i.MX7 mode */ + switch (word) { + case 0: + writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0); + break; + case 1: + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA1); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); + break; + case 2: + writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA2); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); + break; + case 3: + writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA3); + writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); + break; + } + } else { + /* Non-banked i.MX6 mode */ + writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0); + } /* 47.4.1.4.5 * Once complete, the controller will clear BUSY. A write request to a @@ -313,22 +368,27 @@ static struct nvmem_config imx_ocotp_nvmem_config = { static const struct ocotp_params imx6q_params = { .nregs = 128, + .bank_address_words = 0, }; static const struct ocotp_params imx6sl_params = { .nregs = 64, + .bank_address_words = 0, }; static const struct ocotp_params imx6sx_params = { .nregs = 128, + .bank_address_words = 0, }; static const struct ocotp_params imx6ul_params = { .nregs = 128, + .bank_address_words = 0, }; static const struct ocotp_params imx7d_params = { .nregs = 64, + .bank_address_words = 4, }; static const struct of_device_id imx_ocotp_dt_ids[] = { -- cgit v1.2.3 From b50cb68f16ce393db040f755dcb26b9a246180c4 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 24 Oct 2017 10:54:30 +0100 Subject: nvmem: imx-ocotp: Move i.MX6 write clock setup to dedicated function The i.MX7S/D has a different set of timing requirements, as a pre-cursor to adding the i.MX7 timing parameters, move the i.MX6 stuff to a dedicated function. Fixes: 0642bac7da42 ("nvmem: imx-ocotp: add write support") Signed-off-by: Bryan O'Donoghue Reviewed-by: Philipp Zabel Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index bf95a0ecd0dc..8136ce8e77cd 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -168,6 +168,31 @@ read_end: return ret; } +static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv) +{ + unsigned long clk_rate = 0; + unsigned long strobe_read, relax, strobe_prog; + u32 timing = 0; + + /* 47.3.1.3.1 + * Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX] + * fields with timing values to match the current frequency of the + * ipg_clk. OTP writes will work at maximum bus frequencies as long + * as the HW_OCOTP_TIMING parameters are set correctly. + */ + clk_rate = clk_get_rate(priv->clk); + + relax = clk_rate / (1000000000 / DEF_RELAX) - 1; + strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; + strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; + + timing = strobe_prog & 0x00000FFF; + timing |= (relax << 12) & 0x0000F000; + timing |= (strobe_read << 16) & 0x003F0000; + + writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); +} + static int imx_ocotp_write(void *context, unsigned int offset, void *val, size_t bytes) { @@ -175,9 +200,6 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, u32 *buf = val; int ret; - unsigned long clk_rate = 0; - unsigned long strobe_read, relax, strobe_prog; - u32 timing = 0; u32 ctrl; u8 waddr; u8 word = 0; @@ -196,23 +218,8 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, return ret; } - /* 47.3.1.3.1 - * Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX] - * fields with timing values to match the current frequency of the - * ipg_clk. OTP writes will work at maximum bus frequencies as long - * as the HW_OCOTP_TIMING parameters are set correctly. - */ - clk_rate = clk_get_rate(priv->clk); - - relax = clk_rate / (1000000000 / DEF_RELAX) - 1; - strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; - strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; - - timing = strobe_prog & 0x00000FFF; - timing |= (relax << 12) & 0x0000F000; - timing |= (strobe_read << 16) & 0x003F0000; - - writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); + /* Setup the write timing values */ + imx_ocotp_set_imx6_timing(priv); /* 47.3.1.3.2 * Check that HW_OCOTP_CTRL[BUSY] and HW_OCOTP_CTRL[ERROR] are clear. -- cgit v1.2.3 From 828ae7a47caf86570f19b78d0923b3ea89714168 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 24 Oct 2017 10:54:31 +0100 Subject: nvmem: imx-ocotp: Add i.MX7D timing write clock setup support This patch adds logic to correctly setup the write timing parameters when blowing an OTP fuse for the i.MX7S/D. Fixes: 0642bac7da42 ("nvmem: imx-ocotp: add write support") Signed-off-by: Bryan O'Donoghue Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 8136ce8e77cd..10fc4a70a6e5 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -50,17 +50,14 @@ #define IMX_OCOTP_BM_CTRL_ERROR 0x00000200 #define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400 -#define DEF_RELAX 20 /* > 16.5ns */ +#define DEF_RELAX 20 /* > 16.5ns */ +#define DEF_FSOURCE 1001 /* > 1000 ns */ +#define DEF_STROBE_PROG 10000 /* IPG clocks */ #define IMX_OCOTP_WR_UNLOCK 0x3E770000 #define IMX_OCOTP_READ_LOCKED_VAL 0xBADABADA static DEFINE_MUTEX(ocotp_mutex); -struct ocotp_params { - unsigned int nregs; - unsigned int bank_address_words; -}; - struct ocotp_priv { struct device *dev; struct clk *clk; @@ -69,6 +66,12 @@ struct ocotp_priv { struct nvmem_config *config; }; +struct ocotp_params { + unsigned int nregs; + unsigned int bank_address_words; + void (*set_timing)(struct ocotp_priv *priv); +}; + static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags) { int count; @@ -193,6 +196,27 @@ static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv) writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); } +static void imx_ocotp_set_imx7_timing(struct ocotp_priv *priv) +{ + unsigned long clk_rate = 0; + u64 fsource, strobe_prog; + u32 timing = 0; + + /* i.MX 7Solo Applications Processor Reference Manual, Rev. 0.1 + * 6.4.3.3 + */ + clk_rate = clk_get_rate(priv->clk); + fsource = DIV_ROUND_UP_ULL((u64)clk_rate * DEF_FSOURCE, + NSEC_PER_SEC) + 1; + strobe_prog = DIV_ROUND_CLOSEST_ULL((u64)clk_rate * DEF_STROBE_PROG, + NSEC_PER_SEC) + 1; + + timing = strobe_prog & 0x00000FFF; + timing |= (fsource << 12) & 0x000FF000; + + writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); +} + static int imx_ocotp_write(void *context, unsigned int offset, void *val, size_t bytes) { @@ -219,7 +243,7 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, } /* Setup the write timing values */ - imx_ocotp_set_imx6_timing(priv); + priv->params->set_timing(priv); /* 47.3.1.3.2 * Check that HW_OCOTP_CTRL[BUSY] and HW_OCOTP_CTRL[ERROR] are clear. @@ -376,26 +400,31 @@ static struct nvmem_config imx_ocotp_nvmem_config = { static const struct ocotp_params imx6q_params = { .nregs = 128, .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, }; static const struct ocotp_params imx6sl_params = { .nregs = 64, .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, }; static const struct ocotp_params imx6sx_params = { .nregs = 128, .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, }; static const struct ocotp_params imx6ul_params = { .nregs = 128, .bank_address_words = 0, + .set_timing = imx_ocotp_set_imx6_timing, }; static const struct ocotp_params imx7d_params = { .nregs = 64, .bank_address_words = 4, + .set_timing = imx_ocotp_set_imx7_timing, }; static const struct of_device_id imx_ocotp_dt_ids[] = { -- cgit v1.2.3 From a32bab320fe1e9d5c4fd70f1d7ad92be46a9fcd3 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 24 Oct 2017 10:54:32 +0100 Subject: nvmem: imx-ocotp: Enable i.MX7D OTP write support After applying patches for both banked access and write timings we can re-enable the OTP write interface on i.MX7D processors. Fixes: 0642bac7da42 ("nvmem: imx-ocotp: add write support") Signed-off-by: Bryan O'Donoghue Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 10fc4a70a6e5..d56d481eca57 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -466,8 +466,6 @@ static int imx_ocotp_probe(struct platform_device *pdev) imx_ocotp_nvmem_config.dev = dev; imx_ocotp_nvmem_config.priv = priv; priv->config = &imx_ocotp_nvmem_config; - if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx7d-ocotp")) - imx_ocotp_nvmem_config.read_only = true; nvmem = nvmem_register(&imx_ocotp_nvmem_config); if (IS_ERR(nvmem)) -- cgit v1.2.3 From aef9a4de2afa25c02b1d112211ce29e6453ad210 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 24 Oct 2017 10:54:33 +0100 Subject: nvmem: imx-ocotp: Update module description This imx-ocotp driver encapsulates support for a subset of both i.MX6 and i.MX7 processors. Update the module description to reflect. Fixes: 711d45477931 ("nvmem: octop: Add i.MX7D support") Signed-off-by: Bryan O'Donoghue Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/nvmem') diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index d56d481eca57..d7ba351a70c9 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -494,5 +494,5 @@ static struct platform_driver imx_ocotp_driver = { module_platform_driver(imx_ocotp_driver); MODULE_AUTHOR("Philipp Zabel "); -MODULE_DESCRIPTION("i.MX6 OCOTP fuse box driver"); +MODULE_DESCRIPTION("i.MX6/i.MX7 OCOTP fuse box driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From b7fe57b802c4f12da20920229acb9fff92300ad4 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 24 Oct 2017 10:54:34 +0100 Subject: nvmem: sunxi-sid: add support for A64/H5's SID controller Allwinner A64/H5 SoCs come with a SID controller like the one in H3, but without the silicon bug that makes the initial value at 0x200 wrong, so the value at 0x200 can be directly read. Add support for this kind of SID controller. Signed-off-by: Icenowy Zheng Acked-by: Rob Herring Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt | 1 + drivers/nvmem/sunxi_sid.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'drivers/nvmem') diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt index ef06d061913c..6ea0836939ee 100644 --- a/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt @@ -5,6 +5,7 @@ Required properties: "allwinner,sun4i-a10-sid" "allwinner,sun7i-a20-sid" "allwinner,sun8i-h3-sid" + "allwinner,sun50i-a64-sid" - reg: Should contain registers location and length diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 1c3b5cf89212..99bd54d85fcb 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -198,10 +198,16 @@ static const struct sunxi_sid_cfg sun8i_h3_cfg = { .need_register_readout = true, }; +static const struct sunxi_sid_cfg sun50i_a64_cfg = { + .value_offset = 0x200, + .size = 0x100, +}; + static const struct of_device_id sunxi_sid_of_match[] = { { .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, + { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, {/* sentinel */}, }; MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); -- cgit v1.2.3