diff options
| author | Srinivas Kandagatla <srini@kernel.org> | 2026-09-25 11:53:41 +0100 |
|---|---|---|
| committer | Srinivas Kandagatla <srini@kernel.org> | 2026-09-25 11:53:41 +0100 |
| commit | 41f42ff4ae134eba8dbe3424cf2ca1824c652edd (patch) | |
| tree | ede4855c911ba5a0ac4ca441155b7a09e8294a04 | |
| parent | 841ea6e56cc88ec2c28826d62fbab88b234524c5 (diff) | |
| parent | d30804cbeea7ebb6277badc8d6210af5383ac1a4 (diff) | |
| download | linux-next-41f42ff4ae134eba8dbe3424cf2ca1824c652edd.tar.gz linux-next-41f42ff4ae134eba8dbe3424cf2ca1824c652edd.zip | |
Merge branches 'nvmem-fixes' and 'nvmem-for-v7.4' into nvmem-for-next
* nvmem-fixes:
nvmem: core: Fix OOB read for bit offsets of more than one byte
nvmem: layouts: onie-tlv: fix device_node reference leak in error path
nvmem: layouts: sl28vpd: fix device_node reference leak in error path
nvmem: rockchip-otp: Serialize reads
* nvmem-for-v7.4:
nvmem: protect nvmem_device::ops with SRCU
nvmem: remove duplicated reference counting
nvmem: add mt6323 PMIC EFUSE driver
dt-bindings: mfd: mediatek: mt6397: add mt6323 PMIC EFUSE
nvmem: layouts: Support fixed-layout as the nvmem device node itself
nvmem: core: Add const to pattrs allocation type
nvmem: uniphier-efuse: Enable for K3 SoCs
dt-bindings: nvmem: uniphier-efuse: Add ti,am62p-efuse compatible
dt-bindings: nvmem: qfprom: Add compatible for Qualcomm Maili
| -rw-r--r-- | Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml | 21 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml | 6 | ||||
| -rw-r--r-- | MAINTAINERS | 5 | ||||
| -rw-r--r-- | drivers/nvmem/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/nvmem/Makefile | 2 | ||||
| -rw-r--r-- | drivers/nvmem/core.c | 122 | ||||
| -rw-r--r-- | drivers/nvmem/internals.h | 5 | ||||
| -rw-r--r-- | drivers/nvmem/layouts.c | 13 | ||||
| -rw-r--r-- | drivers/nvmem/mt6323-efuse.c | 83 |
10 files changed, 216 insertions, 56 deletions
diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml index 3cbc0dc12c31..d2de227ae7a6 100644 --- a/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6397.yaml @@ -159,6 +159,23 @@ properties: required: - compatible + efuse: + type: object + unevaluatedProperties: false + description: + The efuse is responsible for storing calibration data, such as thermal + sensor calibration. + + properties: + compatible: + const: mediatek,mt6323-efuse + + nvmem-layout: + $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# + + required: + - compatible + leds: type: object additionalProperties: false @@ -307,6 +324,10 @@ examples: interrupt-controller; #interrupt-cells = <2>; + efuse { + compatible = "mediatek,mt6323-efuse"; + }; + leds { compatible = "mediatek,mt6323-led"; #address-cells = <1>; diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index bc0a0898b9e4..566304ed4c78 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -31,6 +31,7 @@ properties: - qcom,ipq9574-qfprom - qcom,ipq9650-qfprom - qcom,kaanapali-qfprom + - qcom,maili-qfprom - qcom,milos-qfprom - qcom,msm8226-qfprom - qcom,msm8916-qfprom diff --git a/Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml b/Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml index e27cbae2d63a..d26b3d2a3eba 100644 --- a/Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/socionext,uniphier-efuse.yaml @@ -16,7 +16,11 @@ allOf: properties: compatible: - const: socionext,uniphier-efuse + oneOf: + - const: socionext,uniphier-efuse + - items: + - const: ti,am62p-efuse + - const: socionext,uniphier-efuse reg: maxItems: 1 diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..6ec0ed61260c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16718,6 +16718,11 @@ S: Maintained F: drivers/iio/adc/mt6323-auxadc.c F: include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h +MEDIATEK MT6323 PMIC NVMEM DRIVER +M: Roman Vivchar <rva333@protonmail.com> +S: Maintained +F: drivers/nvmem/mt6323-efuse.c + MEDIATEK MT6735 CLOCK & RESET DRIVERS M: Yassine Oudjana <y.oudjana@protonmail.com> L: linux-clk@vger.kernel.org diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index c36c2a4c2a0b..083a5e528960 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -328,6 +328,17 @@ config NVMEM_MTK_EFUSE This driver can also be built as a module. If so, the module will be called efuse-mtk. +config NVMEM_MT6323_EFUSE + tristate "MediaTek MT6323 PMIC EFUSE support" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on MFD_MT6397 + help + This is a driver to access hardware related data like sensor + calibration, etc. + + This driver can also be built as a module. If so, the module + will be called efuse-mt6323. + config NVMEM_MXS_OCOTP tristate "Freescale MXS On-Chip OTP Memory Support" depends on ARCH_MXS || COMPILE_TEST @@ -541,8 +552,9 @@ config NVMEM_U_BOOT_ENV config NVMEM_UNIPHIER_EFUSE tristate "UniPhier SoCs eFuse support" - depends on ARCH_UNIPHIER || COMPILE_TEST + depends on ARCH_UNIPHIER || ARCH_K3 || COMPILE_TEST depends on HAS_IOMEM + default y if ARCH_K3 help This is a simple driver to dump specified values of UniPhier SoC from eFuse. diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 2bbfb9ff1885..5e70bad9f0f9 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -53,6 +53,8 @@ obj-$(CONFIG_NVMEM_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o nvmem_meson_mx_efuse-y := meson-mx-efuse.o obj-$(CONFIG_NVMEM_MICROCHIP_OTPC) += nvmem-microchip-otpc.o nvmem-microchip-otpc-y := microchip-otpc.o +obj-$(CONFIG_NVMEM_MT6323_EFUSE) += nvmem_mt6323-efuse.o +nvmem_mt6323-efuse-y := mt6323-efuse.o obj-$(CONFIG_NVMEM_MTK_EFUSE) += nvmem_mtk-efuse.o nvmem_mtk-efuse-y := mtk-efuse.o obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index b4e5069d9e38..7d4cce8e2742 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -57,7 +57,12 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - struct nvmem_operations *ops = nvmem->ops; + struct nvmem_operations *ops; + + guard(srcu)(&nvmem->srcu); + ops = srcu_dereference(nvmem->ops, &nvmem->srcu); + if (!ops) + return -ENODEV; if (!ops->reg_read) return -EOPNOTSUPP; @@ -68,9 +73,14 @@ static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { - struct nvmem_operations *ops = nvmem->ops; + struct nvmem_operations *ops; int ret, wr_ok; + guard(srcu)(&nvmem->srcu); + ops = srcu_dereference(nvmem->ops, &nvmem->srcu); + if (!ops) + return -ENODEV; + if (!ops->reg_write) return -EOPNOTSUPP; @@ -289,7 +299,7 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem) { - struct nvmem_operations *ops = nvmem->ops; + struct nvmem_operations *ops = rcu_dereference_raw(nvmem->ops); umode_t mode = 0400; @@ -333,7 +343,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj, { struct device *dev = kobj_to_dev(kobj); struct nvmem_device *nvmem = to_nvmem_device(dev); - struct nvmem_operations *ops = nvmem->ops; + struct nvmem_operations *ops = rcu_dereference_raw(nvmem->ops); /* * If the device has no .reg_write operation, do not allow @@ -485,7 +495,7 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) /* Allocate an array of attributes with a sentinel */ ncells = list_count_nodes(&nvmem->cells); pattrs = devm_kcalloc(&nvmem->dev, ncells + 1, - sizeof(struct bin_attribute *), GFP_KERNEL); + sizeof(*pattrs), GFP_KERNEL); if (!pattrs) return -ENOMEM; @@ -535,24 +545,6 @@ static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem) #endif /* CONFIG_NVMEM_SYSFS */ -static void nvmem_release(struct device *dev) -{ - struct nvmem_device *nvmem = to_nvmem_device(dev); - - ida_free(&nvmem_ida, nvmem->id); - gpiod_put(nvmem->wp_gpio); - kfree(nvmem->ops); - kfree(nvmem); -} - -static const struct device_type nvmem_provider_type = { - .release = nvmem_release, -}; - -static const struct bus_type nvmem_bus_type = { - .name = "nvmem", -}; - static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell) { blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell); @@ -571,6 +563,25 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) nvmem_cell_entry_drop(cell); } +static void nvmem_release(struct device *dev) +{ + struct nvmem_device *nvmem = to_nvmem_device(dev); + + gpiod_put(nvmem->wp_gpio); + nvmem_device_remove_all_cells(nvmem); + ida_free(&nvmem_ida, nvmem->id); + cleanup_srcu_struct(&nvmem->srcu); + kfree(nvmem); +} + +static const struct device_type nvmem_provider_type = { + .release = nvmem_release, +}; + +static const struct bus_type nvmem_bus_type = { + .name = "nvmem", +}; + static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell) { scoped_guard(mutex, &nvmem_mutex) @@ -918,7 +929,21 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->dev.type = &nvmem_provider_type; nvmem->dev.bus = &nvmem_bus_type; nvmem->dev.parent = config->dev; - nvmem->ops = ops; + INIT_LIST_HEAD(&nvmem->cells); + + /* + * Must happen before we assign the release() callback in + * device_initialize(). + */ + rval = init_srcu_struct(&nvmem->srcu); + if (rval) { + ida_free(&nvmem_ida, nvmem->id); + kfree(ops); + kfree(nvmem); + return ERR_PTR(rval); + } + + rcu_assign_pointer(nvmem->ops, ops); device_initialize(&nvmem->dev); @@ -931,8 +956,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_put_device; } - kref_init(&nvmem->refcnt); - INIT_LIST_HEAD(&nvmem->cells); nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; ops->reg_read = config->reg_read; @@ -993,20 +1016,20 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (config->cells) { rval = nvmem_add_cells(nvmem, config->cells, config->ncells); if (rval) - goto err_remove_cells; + goto err_remove_compat; } if (config->add_legacy_fixed_of_cells) { rval = nvmem_add_cells_from_legacy_of(nvmem); if (rval) - goto err_remove_cells; + goto err_remove_compat; } dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); rval = device_add(&nvmem->dev); if (rval) - goto err_remove_cells; + goto err_remove_compat; rval = nvmem_populate_layout(nvmem); if (rval) @@ -1032,40 +1055,40 @@ err_destroy_layout: #endif err_remove_dev: device_del(&nvmem->dev); -err_remove_cells: - nvmem_device_remove_all_cells(nvmem); +err_remove_compat: nvmem_sysfs_remove_compat(nvmem); err_put_device: + ops = rcu_replace_pointer(nvmem->ops, NULL, true); + synchronize_srcu(&nvmem->srcu); put_device(&nvmem->dev); + kfree(ops); return ERR_PTR(rval); } EXPORT_SYMBOL_GPL(nvmem_register); -static void nvmem_device_release(struct kref *kref) +/** + * nvmem_unregister() - Unregister previously registered nvmem device + * + * @nvmem: Pointer to previously registered nvmem device. + */ +void nvmem_unregister(struct nvmem_device *nvmem) { - struct nvmem_device *nvmem; + struct nvmem_operations *ops; - nvmem = container_of(kref, struct nvmem_device, refcnt); + if (!nvmem) + return; blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem); - nvmem_sysfs_remove_compat(nvmem); + ops = rcu_replace_pointer(nvmem->ops, NULL, true); + synchronize_srcu(&nvmem->srcu); - nvmem_device_remove_all_cells(nvmem); + nvmem_sysfs_remove_compat(nvmem); nvmem_destroy_layout(nvmem); - device_unregister(&nvmem->dev); -} + kfree(ops); -/** - * nvmem_unregister() - Unregister previously registered nvmem device - * - * @nvmem: Pointer to previously registered nvmem device. - */ -void nvmem_unregister(struct nvmem_device *nvmem) -{ - if (nvmem) - kref_put(&nvmem->refcnt, nvmem_device_release); + device_unregister(&nvmem->dev); } EXPORT_SYMBOL_GPL(nvmem_unregister); @@ -1126,8 +1149,6 @@ static struct nvmem_device *nvmem_device_match(void *data, return ERR_PTR(-EINVAL); } - kref_get(&nvmem->refcnt); - return nvmem; } @@ -1243,9 +1264,8 @@ EXPORT_SYMBOL_GPL(devm_nvmem_device_put); */ void nvmem_device_put(struct nvmem_device *nvmem) { - put_device(&nvmem->dev); module_put(nvmem->owner); - kref_put(&nvmem->refcnt, nvmem_device_release); + put_device(&nvmem->dev); } EXPORT_SYMBOL_GPL(nvmem_device_put); diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h index 4e610deeaa7b..bc7a99f5aefb 100644 --- a/drivers/nvmem/internals.h +++ b/drivers/nvmem/internals.h @@ -6,6 +6,7 @@ #include <linux/device.h> #include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> +#include <linux/srcu.h> /* Hold pointers to callbacks owned by the nvmem provider module. */ struct nvmem_operations { @@ -16,10 +17,10 @@ struct nvmem_operations { struct nvmem_device { struct module *owner; struct device dev; + struct srcu_struct srcu; int stride; int word_size; int id; - struct kref refcnt; size_t size; bool read_only; bool root_only; @@ -34,7 +35,7 @@ struct nvmem_device { unsigned int nkeepout; struct gpio_desc *wp_gpio; struct nvmem_layout *layout; - struct nvmem_operations *ops; + struct nvmem_operations __rcu *ops; void *priv; bool sysfs_cells_populated; }; diff --git a/drivers/nvmem/layouts.c b/drivers/nvmem/layouts.c index 07a34be9669c..409909e3244f 100644 --- a/drivers/nvmem/layouts.c +++ b/drivers/nvmem/layouts.c @@ -156,7 +156,18 @@ static int nvmem_layout_bus_populate(struct nvmem_device *nvmem, struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) { - return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); + struct device_node *np; + + /* Search for nvmem-layout child */ + np = of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); + if (np) + return np; + + /* The nvmem of_node is itself a fixed-layout node */ + if (of_device_is_compatible(nvmem->dev.of_node, "fixed-layout")) + return of_node_get(nvmem->dev.of_node); + + return NULL; } EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container); diff --git a/drivers/nvmem/mt6323-efuse.c b/drivers/nvmem/mt6323-efuse.c new file mode 100644 index 000000000000..de5e30215c22 --- /dev/null +++ b/drivers/nvmem/mt6323-efuse.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2026 Roman Vivchar <rva333@protonmail.com> + */ + +#include <linux/err.h> +#include <linux/mfd/mt6397/core.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <linux/mfd/mt6323/registers.h> + +#define MT6323_EFUSE_DOUT_BASE MT6323_EFUSE_DOUT_0_15 +#define MT6323_EFUSE_SIZE 24 + +static int mt6323_efuse_read(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *map = context; + u16 *buf = val; + u32 tmp; + int ret; + + /* + * A manual loop using regmap_read is required because PWRAP is not + * a continuous MMIO space, but rather a FSM that doesn't implement the + * necessary read callback for the regmap_read_raw and regmap_read_bulk + * functions. + */ + for (size_t i = 0; i < bytes; i += sizeof(*buf)) { + ret = regmap_read(map, MT6323_EFUSE_DOUT_BASE + offset + i, &tmp); + if (ret) + return ret; + + *buf++ = tmp; + } + + return 0; +} + +static int mt6323_efuse_probe(struct platform_device *pdev) +{ + struct mt6397_chip *mt6323 = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct nvmem_config config = { + .name = "mt6323-efuse", + .stride = 2, + .word_size = 2, + .size = MT6323_EFUSE_SIZE, + .reg_read = mt6323_efuse_read, + }; + struct nvmem_device *nvmem; + + if (!mt6323 || !mt6323->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get parent\n"); + + config.dev = dev; + config.priv = mt6323->regmap; + + nvmem = devm_nvmem_register(dev, &config); + return PTR_ERR_OR_ZERO(nvmem); +} + +static const struct of_device_id mt6323_efuse_of_match[] = { + { .compatible = "mediatek,mt6323-efuse" }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6323_efuse_of_match); + +static struct platform_driver mt6323_efuse_driver = { + .probe = mt6323_efuse_probe, + .driver = { + .name = "mt6323-efuse", + .of_match_table = mt6323_efuse_of_match, + }, +}; +module_platform_driver(mt6323_efuse_driver); + +MODULE_DESCRIPTION("MediaTek MT6323 PMIC EFUSE driver"); +MODULE_LICENSE("GPL"); |
