Skip to content

Commit fc29fd8

Browse files
miquelraynalgregkh
authored andcommitted
nvmem: core: Rework layouts to become regular devices
Current layout support was initially written without modules support in mind. When the requirement for module support rose, the existing base was improved to adopt modularization support, but kind of a design flaw was introduced. With the existing implementation, when a storage device registers into NVMEM, the core tries to hook a layout (if any) and populates its cells immediately. This means, if the hardware description expects a layout to be hooked up, but no driver was provided for that, the storage medium will fail to probe and try later from scratch. Even if we consider that the hardware description shall be correct, we could still probe the storage device (especially if it contains the rootfs). One way to overcome this situation is to consider the layouts as devices, and leverage the native notifier mechanism. When a new NVMEM device is registered, we can populate its nvmem-layout child, if any, and wait for the matching to be done in order to get the cells (the waiting can be easily done with the NVMEM notifiers). If the layout driver is compiled as a module, it should automatically be loaded. This way, there is no strong order to enforce, any NVMEM device creation or NVMEM layout driver insertion will be observed as a new event which may lead to the creation of additional cells, without disturbing the probes with costly (and sometimes endless) deferrals. In order to achieve that goal we create a new bus for the nvmem-layouts with minimal logic to match nvmem-layout devices with nvmem-layout drivers. All this infrastructure code is created in the layouts.c file. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Tested-by: Rafał Miłecki <rafal@milecki.pl> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 1172460 commit fc29fd8

File tree

9 files changed

+354
-135
lines changed

9 files changed

+354
-135
lines changed

drivers/nvmem/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
menuconfig NVMEM
33
bool "NVMEM Support"
4+
imply NVMEM_LAYOUTS
45
help
56
Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
67

drivers/nvmem/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
obj-$(CONFIG_NVMEM) += nvmem_core.o
77
nvmem_core-y := core.o
8+
obj-$(CONFIG_NVMEM_LAYOUTS) += nvmem_layouts.o
9+
nvmem_layouts-y := layouts.o
810
obj-y += layouts/
911

1012
# Devices

drivers/nvmem/core.c

Lines changed: 61 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ static LIST_HEAD(nvmem_lookup_list);
5555

5656
static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
5757

58-
static DEFINE_SPINLOCK(nvmem_layout_lock);
59-
static LIST_HEAD(nvmem_layouts);
60-
6158
static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
6259
void *val, size_t bytes)
6360
{
@@ -740,105 +737,30 @@ static int nvmem_add_cells_from_fixed_layout(struct nvmem_device *nvmem)
740737
return err;
741738
}
742739

743-
int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
740+
int nvmem_layout_register(struct nvmem_layout *layout)
744741
{
745-
layout->owner = owner;
746-
747-
spin_lock(&nvmem_layout_lock);
748-
list_add(&layout->node, &nvmem_layouts);
749-
spin_unlock(&nvmem_layout_lock);
750-
751-
blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout);
742+
if (!layout->add_cells)
743+
return -EINVAL;
752744

753-
return 0;
745+
/* Populate the cells */
746+
return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
754747
}
755-
EXPORT_SYMBOL_GPL(__nvmem_layout_register);
748+
EXPORT_SYMBOL_GPL(nvmem_layout_register);
756749

757750
void nvmem_layout_unregister(struct nvmem_layout *layout)
758751
{
759-
blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout);
760-
761-
spin_lock(&nvmem_layout_lock);
762-
list_del(&layout->node);
763-
spin_unlock(&nvmem_layout_lock);
752+
/* Keep the API even with an empty stub in case we need it later */
764753
}
765754
EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
766755

767-
static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
768-
{
769-
struct device_node *layout_np;
770-
struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
771-
772-
layout_np = of_nvmem_layout_get_container(nvmem);
773-
if (!layout_np)
774-
return NULL;
775-
776-
/* Fixed layouts don't have a matching driver */
777-
if (of_device_is_compatible(layout_np, "fixed-layout")) {
778-
of_node_put(layout_np);
779-
return NULL;
780-
}
781-
782-
/*
783-
* In case the nvmem device was built-in while the layout was built as a
784-
* module, we shall manually request the layout driver loading otherwise
785-
* we'll never have any match.
786-
*/
787-
of_request_module(layout_np);
788-
789-
spin_lock(&nvmem_layout_lock);
790-
791-
list_for_each_entry(l, &nvmem_layouts, node) {
792-
if (of_match_node(l->of_match_table, layout_np)) {
793-
if (try_module_get(l->owner))
794-
layout = l;
795-
796-
break;
797-
}
798-
}
799-
800-
spin_unlock(&nvmem_layout_lock);
801-
of_node_put(layout_np);
802-
803-
return layout;
804-
}
805-
806-
static void nvmem_layout_put(struct nvmem_layout *layout)
807-
{
808-
if (layout)
809-
module_put(layout->owner);
810-
}
811-
812-
static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
813-
{
814-
struct nvmem_layout *layout = nvmem->layout;
815-
int ret;
816-
817-
if (layout && layout->add_cells) {
818-
ret = layout->add_cells(&nvmem->dev, nvmem);
819-
if (ret)
820-
return ret;
821-
}
822-
823-
return 0;
824-
}
825-
826-
#if IS_ENABLED(CONFIG_OF)
827-
struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
828-
{
829-
return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
830-
}
831-
EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
832-
#endif
833-
834756
const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
835757
struct nvmem_layout *layout)
836758
{
837759
struct device_node __maybe_unused *layout_np;
838760
const struct of_device_id *match;
839761

840762
layout_np = of_nvmem_layout_get_container(nvmem);
841-
match = of_match_node(layout->of_match_table, layout_np);
763+
match = of_match_node(layout->dev.driver->of_match_table, layout_np);
842764

843765
return match ? match->data : NULL;
844766
}
@@ -950,19 +872,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
950872
goto err_put_device;
951873
}
952874

953-
/*
954-
* If the driver supplied a layout by config->layout, the module
955-
* pointer will be NULL and nvmem_layout_put() will be a noop.
956-
*/
957-
nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
958-
if (IS_ERR(nvmem->layout)) {
959-
rval = PTR_ERR(nvmem->layout);
960-
nvmem->layout = NULL;
961-
962-
if (rval == -EPROBE_DEFER)
963-
goto err_teardown_compat;
964-
}
965-
966875
if (config->cells) {
967876
rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
968877
if (rval)
@@ -983,24 +892,24 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
983892
if (rval)
984893
goto err_remove_cells;
985894

986-
rval = nvmem_add_cells_from_layout(nvmem);
987-
if (rval)
988-
goto err_remove_cells;
989-
990895
dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
991896

992897
rval = device_add(&nvmem->dev);
993898
if (rval)
994899
goto err_remove_cells;
995900

901+
rval = nvmem_populate_layout(nvmem);
902+
if (rval)
903+
goto err_remove_dev;
904+
996905
blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
997906

998907
return nvmem;
999908

909+
err_remove_dev:
910+
device_del(&nvmem->dev);
1000911
err_remove_cells:
1001912
nvmem_device_remove_all_cells(nvmem);
1002-
nvmem_layout_put(nvmem->layout);
1003-
err_teardown_compat:
1004913
if (config->compat)
1005914
nvmem_sysfs_remove_compat(nvmem, config);
1006915
err_put_device:
@@ -1022,7 +931,7 @@ static void nvmem_device_release(struct kref *kref)
1022931
device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
1023932

1024933
nvmem_device_remove_all_cells(nvmem);
1025-
nvmem_layout_put(nvmem->layout);
934+
nvmem_destroy_layout(nvmem);
1026935
device_unregister(&nvmem->dev);
1027936
}
1028937

@@ -1324,6 +1233,12 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
13241233
return cell;
13251234
}
13261235

1236+
static void nvmem_layout_module_put(struct nvmem_device *nvmem)
1237+
{
1238+
if (nvmem->layout && nvmem->layout->dev.driver)
1239+
module_put(nvmem->layout->dev.driver->owner);
1240+
}
1241+
13271242
#if IS_ENABLED(CONFIG_OF)
13281243
static struct nvmem_cell_entry *
13291244
nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
@@ -1342,6 +1257,18 @@ nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np
13421257
return cell;
13431258
}
13441259

1260+
static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem)
1261+
{
1262+
if (!nvmem->layout)
1263+
return 0;
1264+
1265+
if (!nvmem->layout->dev.driver ||
1266+
!try_module_get(nvmem->layout->dev.driver->owner))
1267+
return -EPROBE_DEFER;
1268+
1269+
return 0;
1270+
}
1271+
13451272
/**
13461273
* of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
13471274
*
@@ -1404,16 +1331,29 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
14041331
return ERR_CAST(nvmem);
14051332
}
14061333

1334+
ret = nvmem_layout_module_get_optional(nvmem);
1335+
if (ret) {
1336+
of_node_put(cell_np);
1337+
__nvmem_device_put(nvmem);
1338+
return ERR_PTR(ret);
1339+
}
1340+
14071341
cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
14081342
of_node_put(cell_np);
14091343
if (!cell_entry) {
14101344
__nvmem_device_put(nvmem);
1411-
return ERR_PTR(-ENOENT);
1345+
nvmem_layout_module_put(nvmem);
1346+
if (nvmem->layout)
1347+
return ERR_PTR(-EPROBE_DEFER);
1348+
else
1349+
return ERR_PTR(-ENOENT);
14121350
}
14131351

14141352
cell = nvmem_create_cell(cell_entry, id, cell_index);
1415-
if (IS_ERR(cell))
1353+
if (IS_ERR(cell)) {
14161354
__nvmem_device_put(nvmem);
1355+
nvmem_layout_module_put(nvmem);
1356+
}
14171357

14181358
return cell;
14191359
}
@@ -1527,6 +1467,7 @@ void nvmem_cell_put(struct nvmem_cell *cell)
15271467

15281468
kfree(cell);
15291469
__nvmem_device_put(nvmem);
1470+
nvmem_layout_module_put(nvmem);
15301471
}
15311472
EXPORT_SYMBOL_GPL(nvmem_cell_put);
15321473

@@ -2104,11 +2045,22 @@ EXPORT_SYMBOL_GPL(nvmem_dev_name);
21042045

21052046
static int __init nvmem_init(void)
21062047
{
2107-
return bus_register(&nvmem_bus_type);
2048+
int ret;
2049+
2050+
ret = bus_register(&nvmem_bus_type);
2051+
if (ret)
2052+
return ret;
2053+
2054+
ret = nvmem_layout_bus_register();
2055+
if (ret)
2056+
bus_unregister(&nvmem_bus_type);
2057+
2058+
return ret;
21082059
}
21092060

21102061
static void __exit nvmem_exit(void)
21112062
{
2063+
nvmem_layout_bus_unregister();
21122064
bus_unregister(&nvmem_bus_type);
21132065
}
21142066

drivers/nvmem/internals.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,25 @@ struct nvmem_device {
3434
void *priv;
3535
};
3636

37+
#if IS_ENABLED(CONFIG_OF)
38+
int nvmem_layout_bus_register(void);
39+
void nvmem_layout_bus_unregister(void);
40+
int nvmem_populate_layout(struct nvmem_device *nvmem);
41+
void nvmem_destroy_layout(struct nvmem_device *nvmem);
42+
#else /* CONFIG_OF */
43+
static inline int nvmem_layout_bus_register(void)
44+
{
45+
return 0;
46+
}
47+
48+
static inline void nvmem_layout_bus_unregister(void) {}
49+
50+
static inline int nvmem_populate_layout(struct nvmem_device *nvmem)
51+
{
52+
return 0;
53+
}
54+
55+
static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { }
56+
#endif /* CONFIG_OF */
57+
3758
#endif /* ifndef _LINUX_NVMEM_INTERNALS_H */

0 commit comments

Comments
 (0)