1 /*
2 * Copyright (c) 2006-2024, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-06-04 GuEe-GUI the first version
9 */
10
11 #include <rtthread.h>
12
13 #define DBG_TAG "drv.platform"
14 #define DBG_LVL DBG_INFO
15 #include <rtdbg.h>
16
17 #include <drivers/ofw_io.h>
18 #include <drivers/ofw_fdt.h>
19 #include <drivers/platform.h>
20 #include <drivers/core/bus.h>
21 #include <drivers/core/dm.h>
22
23 #include "../ofw/ofw_internal.h"
24
25 static const struct rt_ofw_node_id platform_ofw_ids[] =
26 {
27 { .compatible = "simple-bus", },
28 #ifdef RT_USING_MFD
29 { .compatible = "simple-mfd", },
30 #endif
31 #ifdef RT_USING_ISA
32 { .compatible = "isa", },
33 #endif
34 #ifdef RT_USING_AMBA_BUS
35 /*
36 * Maybe ARM has replaced it with compatible: "arm,primecell" and will not
37 * used anymore in the future.
38 */
39 { .compatible = "arm,amba-bus", },
40 #endif
41 { /* sentinel */ }
42 };
43
ofw_device_rename(struct rt_device * dev)44 static void ofw_device_rename(struct rt_device *dev)
45 {
46 rt_uint32_t mask;
47 rt_uint64_t addr;
48 const char *dev_name = dev->parent.name;
49 struct rt_ofw_node *np = dev->ofw_node;
50
51 #if RT_NAME_MAX > 0
52 if (dev_name[0] == '\0')
53 {
54 dev_name = RT_NULL;
55 }
56 #endif
57
58 while (np->parent)
59 {
60 if (!rt_ofw_get_address(np, 0, &addr, RT_NULL))
61 {
62 const char *node_name = rt_fdt_node_name(np->full_name);
63 rt_size_t tag_len = strchrnul(node_name, '@') - node_name;
64
65 if (!rt_ofw_prop_read_u32(np, "mask", &mask))
66 {
67 rt_dm_dev_set_name(dev, dev_name ? "%lx.%x.%.*s:%s" : "%lx.%x.%.*s",
68 addr, __rt_ffs(mask) - 1, tag_len, node_name, dev_name);
69 }
70 else
71 {
72 rt_dm_dev_set_name(dev, dev_name ? "%lx.%.*s:%s" : "%lx.%.*s",
73 addr, tag_len, node_name, dev_name);
74 }
75
76 return;
77 }
78
79 rt_dm_dev_set_name(dev, dev_name ? "%s:%s" : "%s",
80 rt_fdt_node_name(np->full_name), dev_name);
81
82 np = np->parent;
83 }
84 }
85
alloc_ofw_platform_device(struct rt_ofw_node * np)86 static struct rt_platform_device *alloc_ofw_platform_device(struct rt_ofw_node *np)
87 {
88 struct rt_platform_device *pdev = rt_platform_device_alloc("");
89
90 if (pdev)
91 {
92 /* inc reference of dt-node */
93 rt_ofw_node_get(np);
94 rt_ofw_node_set_flag(np, RT_OFW_F_PLATFORM);
95
96 pdev->parent.ofw_node = np;
97
98 ofw_device_rename(&pdev->parent);
99 }
100 else
101 {
102 LOG_E("Alloc device fail for %s", rt_ofw_node_full_name(np));
103 }
104
105 return pdev;
106 }
107
platform_ofw_device_probe_once(struct rt_ofw_node * parent_np)108 static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np)
109 {
110 rt_err_t err = RT_EOK;
111 struct rt_ofw_node *np;
112 struct rt_platform_device *pdev;
113
114 rt_ofw_foreach_available_child_node(parent_np, np)
115 {
116 const char *name;
117 struct rt_ofw_node_id *id;
118 struct rt_ofw_prop *compat_prop = RT_NULL;
119
120 if (np->dev)
121 {
122 /* Check first */
123 continue;
124 }
125
126 LOG_D("%s found in %s", np->full_name, parent_np->full_name);
127
128 /* Is system node or have driver */
129 if (rt_ofw_node_test_flag(np, RT_OFW_F_SYSTEM) ||
130 rt_ofw_node_test_flag(np, RT_OFW_F_READLY))
131 {
132 continue;
133 }
134
135 compat_prop = rt_ofw_get_prop(np, "compatible", RT_NULL);
136 name = rt_ofw_node_name(np);
137
138 /* Not have name and compatible */
139 if (!compat_prop && (name == (const char *)"<NULL>" || !rt_strcmp(name, "<NULL>")))
140 {
141 continue;
142 }
143
144 id = rt_ofw_prop_match(compat_prop, platform_ofw_ids);
145
146 if (id && np->child)
147 {
148 /* scan next level */
149 err = platform_ofw_device_probe_once(np);
150
151 if (err)
152 {
153 rt_ofw_node_put(np);
154 LOG_E("%s bus probe fail", np->full_name);
155
156 break;
157 }
158 }
159
160 if (np->dev)
161 {
162 /* Maybe the childs have requested this node */
163 continue;
164 }
165
166 pdev = alloc_ofw_platform_device(np);
167
168 if (!pdev)
169 {
170 rt_ofw_node_put(np);
171 err = -RT_ENOMEM;
172
173 break;
174 }
175
176 pdev->dev_id = ofw_alias_node_id(np);
177 np->dev = &pdev->parent;
178 LOG_D("%s register to bus", np->full_name);
179
180 rt_platform_device_register(pdev);
181 }
182
183 return err;
184 }
185
rt_platform_ofw_device_probe_child(struct rt_ofw_node * np)186 rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np)
187 {
188 rt_err_t err;
189 struct rt_ofw_node *parent = rt_ofw_get_parent(np);
190
191 if (parent && rt_strcmp(parent->name, "/") &&
192 rt_ofw_get_prop(np, "compatible", RT_NULL) &&
193 !rt_ofw_node_test_flag(np, RT_OFW_F_PLATFORM))
194 {
195 struct rt_platform_device *pdev = alloc_ofw_platform_device(np);
196
197 if (pdev)
198 {
199 err = rt_platform_device_register(pdev);
200 }
201 else
202 {
203 err = -RT_ENOMEM;
204 }
205 }
206 else
207 {
208 err = -RT_EINVAL;
209 }
210
211 rt_ofw_node_put(parent);
212
213 return err;
214 }
215
rt_platform_ofw_request(struct rt_ofw_node * np)216 rt_err_t rt_platform_ofw_request(struct rt_ofw_node *np)
217 {
218 rt_err_t err;
219
220 if (np)
221 {
222 struct rt_device *dev = np->dev;
223
224 if (dev)
225 {
226 /* Was create */
227 if (dev->drv)
228 {
229 /* Was probe OK */
230 err = RT_EOK;
231 }
232 else
233 {
234 err = rt_bus_reload_driver_device(dev->bus, dev);
235 }
236 }
237 else
238 {
239 struct rt_platform_device *pdev = alloc_ofw_platform_device(np);
240
241 if (pdev)
242 {
243 pdev->dev_id = ofw_alias_node_id(np);
244 np->dev = &pdev->parent;
245 LOG_D("%s register to bus", np->full_name);
246
247 err = rt_platform_device_register(pdev);
248 }
249 else
250 {
251 err = -RT_ENOMEM;
252 }
253 }
254 }
255 else
256 {
257 err = -RT_EINVAL;
258 }
259
260 return err;
261 }
262
platform_ofw_device_probe(void)263 static int platform_ofw_device_probe(void)
264 {
265 rt_err_t err = RT_EOK;
266 struct rt_ofw_node *node;
267
268 if (ofw_node_root)
269 {
270 rt_ofw_node_get(ofw_node_root);
271
272 err = platform_ofw_device_probe_once(ofw_node_root);
273
274 rt_ofw_node_put(ofw_node_root);
275
276 if ((node = rt_ofw_find_node_by_path("/firmware")))
277 {
278 platform_ofw_device_probe_once(node);
279 rt_ofw_node_put(node);
280 }
281
282 if ((node = rt_ofw_find_node_by_path("/clocks")))
283 {
284 platform_ofw_device_probe_once(node);
285 rt_ofw_node_put(node);
286 }
287
288 rt_ofw_node_get(ofw_node_chosen);
289 if ((node = rt_ofw_get_child_by_compatible(ofw_node_chosen, "simple-framebuffer")))
290 {
291 platform_ofw_device_probe_once(node);
292 rt_ofw_node_put(node);
293 }
294 rt_ofw_node_get(ofw_node_chosen);
295 }
296 else
297 {
298 err = -RT_ENOSYS;
299 }
300
301 return (int)err;
302 }
303 INIT_PLATFORM_EXPORT(platform_ofw_device_probe);
304
rt_platform_ofw_free(struct rt_platform_device * pdev)305 rt_err_t rt_platform_ofw_free(struct rt_platform_device *pdev)
306 {
307 rt_err_t err = RT_EOK;
308
309 if (pdev)
310 {
311 struct rt_ofw_node *np = pdev->parent.ofw_node;
312
313 if (np)
314 {
315 rt_ofw_node_clear_flag(np, RT_OFW_F_PLATFORM);
316 rt_ofw_node_put(np);
317
318 rt_free(pdev);
319 }
320 }
321 else
322 {
323 err = -RT_EINVAL;
324 }
325
326 return err;
327 }
328