This patchset adds the ability to add/remove nvmem cells to an nvmem
device using device tree overlays. This can be useful in situations
where an nvmem device is present in the base device tree and the
specific overlay that's loaded knows how to interpret the data within
the nvmem device.
Michael Auchter (2):
nvmem: core: extract function to add cell from OF
nvmem: core: add OF_RECONFIG handler for nvmem cells
drivers/nvmem/core.c | 149 ++++++++++++++++++++++++++++++++-----------
1 file changed, 110 insertions(+), 39 deletions(-)
--
2.25.4
CONFIG_OF_DYNAMIC allows runtime changes to the device tree. This patch
adds a OF_RECONFIG handler to receive notifications on tree changes to
support adding/removing cells from an nvmem device based on these
changes.
Signed-off-by: Michael Auchter <[email protected]>
---
drivers/nvmem/core.c | 61 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 60 insertions(+), 1 deletion(-)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 91979529cb07..859431c15d5b 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -1629,6 +1629,53 @@ void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
}
EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups);
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+static int of_nvmem_notify(struct notifier_block *nb, unsigned long action,
+ void *arg)
+{
+
+ struct of_reconfig_data *rd = arg;
+ struct nvmem_device *nvmem;
+ struct nvmem_cell *cell;
+ int rval;
+
+ switch (of_reconfig_get_state_change(action, rd)) {
+ case OF_RECONFIG_CHANGE_ADD:
+ if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK;
+
+ nvmem = __nvmem_device_get(rd->dn->parent, device_match_of_node);
+ if (IS_ERR(nvmem))
+ return NOTIFY_OK;
+
+ rval = nvmem_add_cell_from_of(nvmem, rd->dn);
+ return notifier_from_errno(rval);
+ break;
+ case OF_RECONFIG_CHANGE_REMOVE:
+ if (!of_node_check_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK;
+
+ nvmem = __nvmem_device_get(rd->dn->parent, device_match_of_node);
+ if (IS_ERR(nvmem))
+ return NOTIFY_OK;
+
+ cell = nvmem_find_cell_by_node(nvmem, rd->dn);
+ if (!cell)
+ return NOTIFY_OK;
+
+ nvmem_cell_drop(cell);
+ of_node_clear_flag(rd->dn, OF_POPULATED);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+struct notifier_block nvmem_of_notifier = {
+ .notifier_call = of_nvmem_notify,
+};
+#endif /* CONFIG_OF_DYNAMIC */
+
/**
* nvmem_dev_name() - Get the name of a given nvmem device.
*
@@ -1644,11 +1691,23 @@ EXPORT_SYMBOL_GPL(nvmem_dev_name);
static int __init nvmem_init(void)
{
- return bus_register(&nvmem_bus_type);
+ int rval;
+
+ rval = bus_register(&nvmem_bus_type);
+ if (rval)
+ return rval;
+
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_register(&nvmem_of_notifier));
+
+ return 0;
}
static void __exit nvmem_exit(void)
{
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_unregister(&nvmem_of_notifier));
+
bus_unregister(&nvmem_bus_type);
}
--
2.25.4
Hi Michael,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linus/master]
[also build test WARNING on v5.9 next-20201013]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Michael-Auchter/nvmem-add-DT-overlay-support-for-cells/20201015-054223
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 37187df45af7d28d27b5c130c23f407ca9dbefa2
config: i386-randconfig-s001-20201014 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce:
# apt-get install sparse
# sparse version: v0.6.3-rc1-dirty
# https://github.com/0day-ci/linux/commit/a3191767e8e4e0480e36126ce93e6ab41ab6f498
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Michael-Auchter/nvmem-add-DT-overlay-support-for-cells/20201015-054223
git checkout a3191767e8e4e0480e36126ce93e6ab41ab6f498
# save the attached .config to linux build tree
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=i386
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
"sparse warnings: (new ones prefixed by >>)"
>> drivers/nvmem/core.c:1674:23: sparse: sparse: symbol 'nvmem_of_notifier' was not declared. Should it be static?
Please review and possibly fold the followup patch.
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
Hi Michael,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v5.9 next-20201013]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Michael-Auchter/nvmem-add-DT-overlay-support-for-cells/20201015-054223
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 37187df45af7d28d27b5c130c23f407ca9dbefa2
config: x86_64-randconfig-a006-20201014 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project e7b4feea8e1bf520b34ad8c116abab6677344b74)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install x86_64 cross compiling tool for clang build
# apt-get install binutils-x86-64-linux-gnu
# https://github.com/0day-ci/linux/commit/a3191767e8e4e0480e36126ce93e6ab41ab6f498
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Michael-Auchter/nvmem-add-DT-overlay-support-for-cells/20201015-054223
git checkout a3191767e8e4e0480e36126ce93e6ab41ab6f498
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
>> drivers/nvmem/core.c:1701:42: error: use of undeclared identifier 'nvmem_of_notifier'; did you mean 'nvmem_notifier'?
WARN_ON(of_reconfig_notifier_register(&nvmem_of_notifier));
^~~~~~~~~~~~~~~~~
nvmem_notifier
include/asm-generic/bug.h:119:25: note: expanded from macro 'WARN_ON'
int __ret_warn_on = !!(condition); \
^
drivers/nvmem/core.c:67:31: note: 'nvmem_notifier' declared here
static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
^
drivers/nvmem/core.c:1709:44: error: use of undeclared identifier 'nvmem_of_notifier'; did you mean 'nvmem_notifier'?
WARN_ON(of_reconfig_notifier_unregister(&nvmem_of_notifier));
^~~~~~~~~~~~~~~~~
nvmem_notifier
include/asm-generic/bug.h:119:25: note: expanded from macro 'WARN_ON'
int __ret_warn_on = !!(condition); \
^
drivers/nvmem/core.c:67:31: note: 'nvmem_notifier' declared here
static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
^
2 errors generated.
vim +1701 drivers/nvmem/core.c
1691
1692 static int __init nvmem_init(void)
1693 {
1694 int rval;
1695
1696 rval = bus_register(&nvmem_bus_type);
1697 if (rval)
1698 return rval;
1699
1700 if (IS_ENABLED(CONFIG_OF_DYNAMIC))
> 1701 WARN_ON(of_reconfig_notifier_register(&nvmem_of_notifier));
1702
1703 return 0;
1704 }
1705
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
Extract the logic needed to add a single cell described by a device tree
node from nvmem_add_cells_from_of, and call this new function for each
child node.
Signed-off-by: Michael Auchter <[email protected]>
---
drivers/nvmem/core.c | 84 +++++++++++++++++++++++++-------------------
1 file changed, 48 insertions(+), 36 deletions(-)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 6cd3edb2eaf6..91979529cb07 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -518,54 +518,66 @@ nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id)
return cell;
}
-static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
+static int nvmem_add_cell_from_of(struct nvmem_device *nvmem,
+ struct device_node *child)
{
- struct device_node *parent, *child;
struct device *dev = &nvmem->dev;
struct nvmem_cell *cell;
const __be32 *addr;
int len;
- parent = dev->of_node;
+ addr = of_get_property(child, "reg", &len);
+ if (!addr || (len < 2 * sizeof(u32))) {
+ dev_err(dev, "nvmem: invalid reg on %pOF\n", child);
+ return -EINVAL;
+ }
- for_each_child_of_node(parent, child) {
- addr = of_get_property(child, "reg", &len);
- if (!addr || (len < 2 * sizeof(u32))) {
- dev_err(dev, "nvmem: invalid reg on %pOF\n", child);
- return -EINVAL;
- }
+ cell = kzalloc(sizeof(*cell), GFP_KERNEL);
+ if (!cell)
+ return -ENOMEM;
- cell = kzalloc(sizeof(*cell), GFP_KERNEL);
- if (!cell)
- return -ENOMEM;
+ cell->nvmem = nvmem;
+ cell->np = of_node_get(child);
+ cell->offset = be32_to_cpup(addr++);
+ cell->bytes = be32_to_cpup(addr);
+ cell->name = kasprintf(GFP_KERNEL, "%pOFn", child);
+
+ addr = of_get_property(child, "bits", &len);
+ if (addr && len == (2 * sizeof(u32))) {
+ cell->bit_offset = be32_to_cpup(addr++);
+ cell->nbits = be32_to_cpup(addr);
+ }
- cell->nvmem = nvmem;
- cell->np = of_node_get(child);
- cell->offset = be32_to_cpup(addr++);
- cell->bytes = be32_to_cpup(addr);
- cell->name = kasprintf(GFP_KERNEL, "%pOFn", child);
+ if (cell->nbits)
+ cell->bytes = DIV_ROUND_UP(
+ cell->nbits + cell->bit_offset,
+ BITS_PER_BYTE);
- addr = of_get_property(child, "bits", &len);
- if (addr && len == (2 * sizeof(u32))) {
- cell->bit_offset = be32_to_cpup(addr++);
- cell->nbits = be32_to_cpup(addr);
- }
+ if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
+ dev_err(dev, "cell %s unaligned to nvmem stride %d\n",
+ cell->name, nvmem->stride);
+ /* Cells already added will be freed later. */
+ kfree_const(cell->name);
+ kfree(cell);
+ return -EINVAL;
+ }
- if (cell->nbits)
- cell->bytes = DIV_ROUND_UP(
- cell->nbits + cell->bit_offset,
- BITS_PER_BYTE);
-
- if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
- dev_err(dev, "cell %s unaligned to nvmem stride %d\n",
- cell->name, nvmem->stride);
- /* Cells already added will be freed later. */
- kfree_const(cell->name);
- kfree(cell);
- return -EINVAL;
- }
+ nvmem_cell_add(cell);
- nvmem_cell_add(cell);
+ return 0;
+}
+
+static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
+{
+ struct device_node *parent, *child;
+ int rval;
+
+ parent = nvmem->dev.of_node;
+
+ for_each_child_of_node(parent, child) {
+ rval = nvmem_add_cell_from_of(nvmem, child);
+ if (rval)
+ return rval;
}
return 0;
--
2.25.4
Signed-off-by: kernel test robot <[email protected]>
---
core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 859431c15d5bc3..6dd79075fdd9b0 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -1671,7 +1671,7 @@ static int of_nvmem_notify(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
-struct notifier_block nvmem_of_notifier = {
+static struct notifier_block nvmem_of_notifier = {
.notifier_call = of_nvmem_notify,
};
#endif /* CONFIG_OF_DYNAMIC */