]>
Commit | Line | Data |
---|---|---|
fc29fd82 MR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * NVMEM layout bus handling | |
4 | * | |
5 | * Copyright (C) 2023 Bootlin | |
6 | * Author: Miquel Raynal <[email protected] | |
7 | */ | |
8 | ||
9 | #include <linux/device.h> | |
10 | #include <linux/dma-mapping.h> | |
11 | #include <linux/nvmem-consumer.h> | |
12 | #include <linux/nvmem-provider.h> | |
13 | #include <linux/of.h> | |
14 | #include <linux/of_device.h> | |
15 | #include <linux/of_irq.h> | |
16 | ||
17 | #include "internals.h" | |
18 | ||
19 | #define to_nvmem_layout_driver(drv) \ | |
d69d8048 | 20 | (container_of_const((drv), struct nvmem_layout_driver, driver)) |
fc29fd82 MR |
21 | #define to_nvmem_layout_device(_dev) \ |
22 | container_of((_dev), struct nvmem_layout, dev) | |
23 | ||
d69d8048 | 24 | static int nvmem_layout_bus_match(struct device *dev, const struct device_driver *drv) |
fc29fd82 MR |
25 | { |
26 | return of_driver_match_device(dev, drv); | |
27 | } | |
28 | ||
29 | static int nvmem_layout_bus_probe(struct device *dev) | |
30 | { | |
31 | struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver); | |
32 | struct nvmem_layout *layout = to_nvmem_layout_device(dev); | |
33 | ||
34 | if (!drv->probe || !drv->remove) | |
35 | return -EINVAL; | |
36 | ||
37 | return drv->probe(layout); | |
38 | } | |
39 | ||
40 | static void nvmem_layout_bus_remove(struct device *dev) | |
41 | { | |
42 | struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver); | |
43 | struct nvmem_layout *layout = to_nvmem_layout_device(dev); | |
44 | ||
45 | return drv->remove(layout); | |
46 | } | |
47 | ||
8ec0faf2 | 48 | static const struct bus_type nvmem_layout_bus_type = { |
fc29fd82 MR |
49 | .name = "nvmem-layout", |
50 | .match = nvmem_layout_bus_match, | |
51 | .probe = nvmem_layout_bus_probe, | |
52 | .remove = nvmem_layout_bus_remove, | |
53 | }; | |
54 | ||
6d0ca4a2 KK |
55 | int __nvmem_layout_driver_register(struct nvmem_layout_driver *drv, |
56 | struct module *owner) | |
fc29fd82 MR |
57 | { |
58 | drv->driver.bus = &nvmem_layout_bus_type; | |
6d0ca4a2 | 59 | drv->driver.owner = owner; |
fc29fd82 MR |
60 | |
61 | return driver_register(&drv->driver); | |
62 | } | |
6d0ca4a2 | 63 | EXPORT_SYMBOL_GPL(__nvmem_layout_driver_register); |
fc29fd82 MR |
64 | |
65 | void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv) | |
66 | { | |
67 | driver_unregister(&drv->driver); | |
68 | } | |
69 | EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister); | |
70 | ||
71 | static void nvmem_layout_release_device(struct device *dev) | |
72 | { | |
73 | struct nvmem_layout *layout = to_nvmem_layout_device(dev); | |
74 | ||
75 | of_node_put(layout->dev.of_node); | |
76 | kfree(layout); | |
77 | } | |
78 | ||
79 | static int nvmem_layout_create_device(struct nvmem_device *nvmem, | |
80 | struct device_node *np) | |
81 | { | |
82 | struct nvmem_layout *layout; | |
83 | struct device *dev; | |
84 | int ret; | |
85 | ||
86 | layout = kzalloc(sizeof(*layout), GFP_KERNEL); | |
87 | if (!layout) | |
88 | return -ENOMEM; | |
89 | ||
90 | /* Create a bidirectional link */ | |
91 | layout->nvmem = nvmem; | |
92 | nvmem->layout = layout; | |
93 | ||
94 | /* Device model registration */ | |
95 | dev = &layout->dev; | |
96 | device_initialize(dev); | |
97 | dev->parent = &nvmem->dev; | |
98 | dev->bus = &nvmem_layout_bus_type; | |
99 | dev->release = nvmem_layout_release_device; | |
100 | dev->coherent_dma_mask = DMA_BIT_MASK(32); | |
101 | dev->dma_mask = &dev->coherent_dma_mask; | |
102 | device_set_node(dev, of_fwnode_handle(of_node_get(np))); | |
103 | of_device_make_bus_id(dev); | |
104 | of_msi_configure(dev, dev->of_node); | |
105 | ||
106 | ret = device_add(dev); | |
107 | if (ret) { | |
108 | put_device(dev); | |
109 | return ret; | |
110 | } | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | static const struct of_device_id of_nvmem_layout_skip_table[] = { | |
116 | { .compatible = "fixed-layout", }, | |
117 | {} | |
118 | }; | |
119 | ||
120 | static int nvmem_layout_bus_populate(struct nvmem_device *nvmem, | |
121 | struct device_node *layout_dn) | |
122 | { | |
123 | int ret; | |
124 | ||
125 | /* Make sure it has a compatible property */ | |
126 | if (!of_get_property(layout_dn, "compatible", NULL)) { | |
127 | pr_debug("%s() - skipping %pOF, no compatible prop\n", | |
128 | __func__, layout_dn); | |
129 | return 0; | |
130 | } | |
131 | ||
132 | /* Fixed layouts are parsed manually somewhere else for now */ | |
133 | if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) { | |
134 | pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn); | |
135 | return 0; | |
136 | } | |
137 | ||
138 | if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) { | |
139 | pr_debug("%s() - skipping %pOF, already populated\n", | |
140 | __func__, layout_dn); | |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
145 | /* NVMEM layout buses expect only a single device representing the layout */ | |
146 | ret = nvmem_layout_create_device(nvmem, layout_dn); | |
147 | if (ret) | |
148 | return ret; | |
149 | ||
150 | of_node_set_flag(layout_dn, OF_POPULATED_BUS); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) | |
156 | { | |
157 | return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); | |
158 | } | |
159 | EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container); | |
160 | ||
161 | /* | |
162 | * Returns the number of devices populated, 0 if the operation was not relevant | |
163 | * for this nvmem device, an error code otherwise. | |
164 | */ | |
165 | int nvmem_populate_layout(struct nvmem_device *nvmem) | |
166 | { | |
167 | struct device_node *layout_dn; | |
168 | int ret; | |
169 | ||
170 | layout_dn = of_nvmem_layout_get_container(nvmem); | |
171 | if (!layout_dn) | |
172 | return 0; | |
173 | ||
174 | /* Populate the layout device */ | |
175 | device_links_supplier_sync_state_pause(); | |
176 | ret = nvmem_layout_bus_populate(nvmem, layout_dn); | |
177 | device_links_supplier_sync_state_resume(); | |
178 | ||
179 | of_node_put(layout_dn); | |
180 | return ret; | |
181 | } | |
182 | ||
183 | void nvmem_destroy_layout(struct nvmem_device *nvmem) | |
184 | { | |
185 | struct device *dev; | |
186 | ||
187 | if (!nvmem->layout) | |
188 | return; | |
189 | ||
190 | dev = &nvmem->layout->dev; | |
191 | of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); | |
192 | device_unregister(dev); | |
193 | } | |
194 | ||
195 | int nvmem_layout_bus_register(void) | |
196 | { | |
197 | return bus_register(&nvmem_layout_bus_type); | |
198 | } | |
199 | ||
200 | void nvmem_layout_bus_unregister(void) | |
201 | { | |
202 | bus_unregister(&nvmem_layout_bus_type); | |
203 | } |