1507fd01dSBartosz Golaszewski // SPDX--License-Identifier: GPL-2.0
2507fd01dSBartosz Golaszewski
3507fd01dSBartosz Golaszewski #include <asm/platform_early.h>
4507fd01dSBartosz Golaszewski #include <linux/mod_devicetable.h>
5507fd01dSBartosz Golaszewski #include <linux/pm.h>
6507fd01dSBartosz Golaszewski
7201e9109SBartosz Golaszewski static __initdata LIST_HEAD(sh_early_platform_driver_list);
8201e9109SBartosz Golaszewski static __initdata LIST_HEAD(sh_early_platform_device_list);
9507fd01dSBartosz Golaszewski
10507fd01dSBartosz Golaszewski static const struct platform_device_id *
platform_match_id(const struct platform_device_id * id,struct platform_device * pdev)11507fd01dSBartosz Golaszewski platform_match_id(const struct platform_device_id *id,
12507fd01dSBartosz Golaszewski struct platform_device *pdev)
13507fd01dSBartosz Golaszewski {
14507fd01dSBartosz Golaszewski while (id->name[0]) {
15507fd01dSBartosz Golaszewski if (strcmp(pdev->name, id->name) == 0) {
16507fd01dSBartosz Golaszewski pdev->id_entry = id;
17507fd01dSBartosz Golaszewski return id;
18507fd01dSBartosz Golaszewski }
19507fd01dSBartosz Golaszewski id++;
20507fd01dSBartosz Golaszewski }
21507fd01dSBartosz Golaszewski return NULL;
22507fd01dSBartosz Golaszewski }
23507fd01dSBartosz Golaszewski
platform_match(struct device * dev,struct device_driver * drv)24507fd01dSBartosz Golaszewski static int platform_match(struct device *dev, struct device_driver *drv)
25507fd01dSBartosz Golaszewski {
26507fd01dSBartosz Golaszewski struct platform_device *pdev = to_platform_device(dev);
27507fd01dSBartosz Golaszewski struct platform_driver *pdrv = to_platform_driver(drv);
28507fd01dSBartosz Golaszewski
29507fd01dSBartosz Golaszewski /* When driver_override is set, only bind to the matching driver */
30507fd01dSBartosz Golaszewski if (pdev->driver_override)
31507fd01dSBartosz Golaszewski return !strcmp(pdev->driver_override, drv->name);
32507fd01dSBartosz Golaszewski
33507fd01dSBartosz Golaszewski /* Then try to match against the id table */
34507fd01dSBartosz Golaszewski if (pdrv->id_table)
35507fd01dSBartosz Golaszewski return platform_match_id(pdrv->id_table, pdev) != NULL;
36507fd01dSBartosz Golaszewski
37507fd01dSBartosz Golaszewski /* fall-back to driver name match */
38507fd01dSBartosz Golaszewski return (strcmp(pdev->name, drv->name) == 0);
39507fd01dSBartosz Golaszewski }
40507fd01dSBartosz Golaszewski
41507fd01dSBartosz Golaszewski #ifdef CONFIG_PM
device_pm_init_common(struct device * dev)42507fd01dSBartosz Golaszewski static void device_pm_init_common(struct device *dev)
43507fd01dSBartosz Golaszewski {
44507fd01dSBartosz Golaszewski if (!dev->power.early_init) {
45507fd01dSBartosz Golaszewski spin_lock_init(&dev->power.lock);
46507fd01dSBartosz Golaszewski dev->power.qos = NULL;
47507fd01dSBartosz Golaszewski dev->power.early_init = true;
48507fd01dSBartosz Golaszewski }
49507fd01dSBartosz Golaszewski }
50507fd01dSBartosz Golaszewski
pm_runtime_early_init(struct device * dev)51507fd01dSBartosz Golaszewski static void pm_runtime_early_init(struct device *dev)
52507fd01dSBartosz Golaszewski {
53507fd01dSBartosz Golaszewski dev->power.disable_depth = 1;
54507fd01dSBartosz Golaszewski device_pm_init_common(dev);
55507fd01dSBartosz Golaszewski }
56507fd01dSBartosz Golaszewski #else
pm_runtime_early_init(struct device * dev)57507fd01dSBartosz Golaszewski static void pm_runtime_early_init(struct device *dev) {}
58507fd01dSBartosz Golaszewski #endif
59507fd01dSBartosz Golaszewski
60507fd01dSBartosz Golaszewski /**
61201e9109SBartosz Golaszewski * sh_early_platform_driver_register - register early platform driver
62201e9109SBartosz Golaszewski * @epdrv: sh_early_platform driver structure
63507fd01dSBartosz Golaszewski * @buf: string passed from early_param()
64507fd01dSBartosz Golaszewski *
65201e9109SBartosz Golaszewski * Helper function for sh_early_platform_init() / sh_early_platform_init_buffer()
66507fd01dSBartosz Golaszewski */
sh_early_platform_driver_register(struct sh_early_platform_driver * epdrv,char * buf)67201e9109SBartosz Golaszewski int __init sh_early_platform_driver_register(struct sh_early_platform_driver *epdrv,
68507fd01dSBartosz Golaszewski char *buf)
69507fd01dSBartosz Golaszewski {
70507fd01dSBartosz Golaszewski char *tmp;
71507fd01dSBartosz Golaszewski int n;
72507fd01dSBartosz Golaszewski
73507fd01dSBartosz Golaszewski /* Simply add the driver to the end of the global list.
74507fd01dSBartosz Golaszewski * Drivers will by default be put on the list in compiled-in order.
75507fd01dSBartosz Golaszewski */
76507fd01dSBartosz Golaszewski if (!epdrv->list.next) {
77507fd01dSBartosz Golaszewski INIT_LIST_HEAD(&epdrv->list);
78201e9109SBartosz Golaszewski list_add_tail(&epdrv->list, &sh_early_platform_driver_list);
79507fd01dSBartosz Golaszewski }
80507fd01dSBartosz Golaszewski
81507fd01dSBartosz Golaszewski /* If the user has specified device then make sure the driver
82507fd01dSBartosz Golaszewski * gets prioritized. The driver of the last device specified on
83507fd01dSBartosz Golaszewski * command line will be put first on the list.
84507fd01dSBartosz Golaszewski */
85507fd01dSBartosz Golaszewski n = strlen(epdrv->pdrv->driver.name);
86507fd01dSBartosz Golaszewski if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) {
87201e9109SBartosz Golaszewski list_move(&epdrv->list, &sh_early_platform_driver_list);
88507fd01dSBartosz Golaszewski
89507fd01dSBartosz Golaszewski /* Allow passing parameters after device name */
90507fd01dSBartosz Golaszewski if (buf[n] == '\0' || buf[n] == ',')
91507fd01dSBartosz Golaszewski epdrv->requested_id = -1;
92507fd01dSBartosz Golaszewski else {
93507fd01dSBartosz Golaszewski epdrv->requested_id = simple_strtoul(&buf[n + 1],
94507fd01dSBartosz Golaszewski &tmp, 10);
95507fd01dSBartosz Golaszewski
96507fd01dSBartosz Golaszewski if (buf[n] != '.' || (tmp == &buf[n + 1])) {
97507fd01dSBartosz Golaszewski epdrv->requested_id = EARLY_PLATFORM_ID_ERROR;
98507fd01dSBartosz Golaszewski n = 0;
99507fd01dSBartosz Golaszewski } else
100507fd01dSBartosz Golaszewski n += strcspn(&buf[n + 1], ",") + 1;
101507fd01dSBartosz Golaszewski }
102507fd01dSBartosz Golaszewski
103507fd01dSBartosz Golaszewski if (buf[n] == ',')
104507fd01dSBartosz Golaszewski n++;
105507fd01dSBartosz Golaszewski
106507fd01dSBartosz Golaszewski if (epdrv->bufsize) {
107507fd01dSBartosz Golaszewski memcpy(epdrv->buffer, &buf[n],
108507fd01dSBartosz Golaszewski min_t(int, epdrv->bufsize, strlen(&buf[n]) + 1));
109507fd01dSBartosz Golaszewski epdrv->buffer[epdrv->bufsize - 1] = '\0';
110507fd01dSBartosz Golaszewski }
111507fd01dSBartosz Golaszewski }
112507fd01dSBartosz Golaszewski
113507fd01dSBartosz Golaszewski return 0;
114507fd01dSBartosz Golaszewski }
115507fd01dSBartosz Golaszewski
116507fd01dSBartosz Golaszewski /**
117201e9109SBartosz Golaszewski * sh_early_platform_add_devices - adds a number of early platform devices
118507fd01dSBartosz Golaszewski * @devs: array of early platform devices to add
119507fd01dSBartosz Golaszewski * @num: number of early platform devices in array
120507fd01dSBartosz Golaszewski *
121507fd01dSBartosz Golaszewski * Used by early architecture code to register early platform devices and
122507fd01dSBartosz Golaszewski * their platform data.
123507fd01dSBartosz Golaszewski */
sh_early_platform_add_devices(struct platform_device ** devs,int num)124201e9109SBartosz Golaszewski void __init sh_early_platform_add_devices(struct platform_device **devs, int num)
125507fd01dSBartosz Golaszewski {
126507fd01dSBartosz Golaszewski struct device *dev;
127507fd01dSBartosz Golaszewski int i;
128507fd01dSBartosz Golaszewski
129507fd01dSBartosz Golaszewski /* simply add the devices to list */
130507fd01dSBartosz Golaszewski for (i = 0; i < num; i++) {
131507fd01dSBartosz Golaszewski dev = &devs[i]->dev;
132507fd01dSBartosz Golaszewski
133507fd01dSBartosz Golaszewski if (!dev->devres_head.next) {
134507fd01dSBartosz Golaszewski pm_runtime_early_init(dev);
135507fd01dSBartosz Golaszewski INIT_LIST_HEAD(&dev->devres_head);
136507fd01dSBartosz Golaszewski list_add_tail(&dev->devres_head,
137201e9109SBartosz Golaszewski &sh_early_platform_device_list);
138507fd01dSBartosz Golaszewski }
139507fd01dSBartosz Golaszewski }
140507fd01dSBartosz Golaszewski }
141507fd01dSBartosz Golaszewski
142507fd01dSBartosz Golaszewski /**
143201e9109SBartosz Golaszewski * sh_early_platform_driver_register_all - register early platform drivers
144507fd01dSBartosz Golaszewski * @class_str: string to identify early platform driver class
145507fd01dSBartosz Golaszewski *
146507fd01dSBartosz Golaszewski * Used by architecture code to register all early platform drivers
147507fd01dSBartosz Golaszewski * for a certain class. If omitted then only early platform drivers
148507fd01dSBartosz Golaszewski * with matching kernel command line class parameters will be registered.
149507fd01dSBartosz Golaszewski */
sh_early_platform_driver_register_all(char * class_str)150201e9109SBartosz Golaszewski void __init sh_early_platform_driver_register_all(char *class_str)
151507fd01dSBartosz Golaszewski {
152507fd01dSBartosz Golaszewski /* The "class_str" parameter may or may not be present on the kernel
153507fd01dSBartosz Golaszewski * command line. If it is present then there may be more than one
154507fd01dSBartosz Golaszewski * matching parameter.
155507fd01dSBartosz Golaszewski *
156507fd01dSBartosz Golaszewski * Since we register our early platform drivers using early_param()
157507fd01dSBartosz Golaszewski * we need to make sure that they also get registered in the case
158507fd01dSBartosz Golaszewski * when the parameter is missing from the kernel command line.
159507fd01dSBartosz Golaszewski *
160507fd01dSBartosz Golaszewski * We use parse_early_options() to make sure the early_param() gets
161507fd01dSBartosz Golaszewski * called at least once. The early_param() may be called more than
162507fd01dSBartosz Golaszewski * once since the name of the preferred device may be specified on
163201e9109SBartosz Golaszewski * the kernel command line. sh_early_platform_driver_register() handles
164507fd01dSBartosz Golaszewski * this case for us.
165507fd01dSBartosz Golaszewski */
166507fd01dSBartosz Golaszewski parse_early_options(class_str);
167507fd01dSBartosz Golaszewski }
168507fd01dSBartosz Golaszewski
169507fd01dSBartosz Golaszewski /**
170201e9109SBartosz Golaszewski * sh_early_platform_match - find early platform device matching driver
171507fd01dSBartosz Golaszewski * @epdrv: early platform driver structure
172507fd01dSBartosz Golaszewski * @id: id to match against
173507fd01dSBartosz Golaszewski */
174507fd01dSBartosz Golaszewski static struct platform_device * __init
sh_early_platform_match(struct sh_early_platform_driver * epdrv,int id)175201e9109SBartosz Golaszewski sh_early_platform_match(struct sh_early_platform_driver *epdrv, int id)
176507fd01dSBartosz Golaszewski {
177507fd01dSBartosz Golaszewski struct platform_device *pd;
178507fd01dSBartosz Golaszewski
179201e9109SBartosz Golaszewski list_for_each_entry(pd, &sh_early_platform_device_list, dev.devres_head)
180507fd01dSBartosz Golaszewski if (platform_match(&pd->dev, &epdrv->pdrv->driver))
181507fd01dSBartosz Golaszewski if (pd->id == id)
182507fd01dSBartosz Golaszewski return pd;
183507fd01dSBartosz Golaszewski
184507fd01dSBartosz Golaszewski return NULL;
185507fd01dSBartosz Golaszewski }
186507fd01dSBartosz Golaszewski
187507fd01dSBartosz Golaszewski /**
188201e9109SBartosz Golaszewski * sh_early_platform_left - check if early platform driver has matching devices
189507fd01dSBartosz Golaszewski * @epdrv: early platform driver structure
190507fd01dSBartosz Golaszewski * @id: return true if id or above exists
191507fd01dSBartosz Golaszewski */
sh_early_platform_left(struct sh_early_platform_driver * epdrv,int id)192201e9109SBartosz Golaszewski static int __init sh_early_platform_left(struct sh_early_platform_driver *epdrv,
193507fd01dSBartosz Golaszewski int id)
194507fd01dSBartosz Golaszewski {
195507fd01dSBartosz Golaszewski struct platform_device *pd;
196507fd01dSBartosz Golaszewski
197201e9109SBartosz Golaszewski list_for_each_entry(pd, &sh_early_platform_device_list, dev.devres_head)
198507fd01dSBartosz Golaszewski if (platform_match(&pd->dev, &epdrv->pdrv->driver))
199507fd01dSBartosz Golaszewski if (pd->id >= id)
200507fd01dSBartosz Golaszewski return 1;
201507fd01dSBartosz Golaszewski
202507fd01dSBartosz Golaszewski return 0;
203507fd01dSBartosz Golaszewski }
204507fd01dSBartosz Golaszewski
205507fd01dSBartosz Golaszewski /**
206201e9109SBartosz Golaszewski * sh_early_platform_driver_probe_id - probe drivers matching class_str and id
207507fd01dSBartosz Golaszewski * @class_str: string to identify early platform driver class
208507fd01dSBartosz Golaszewski * @id: id to match against
209507fd01dSBartosz Golaszewski * @nr_probe: number of platform devices to successfully probe before exiting
210507fd01dSBartosz Golaszewski */
sh_early_platform_driver_probe_id(char * class_str,int id,int nr_probe)211201e9109SBartosz Golaszewski static int __init sh_early_platform_driver_probe_id(char *class_str,
212507fd01dSBartosz Golaszewski int id,
213507fd01dSBartosz Golaszewski int nr_probe)
214507fd01dSBartosz Golaszewski {
215201e9109SBartosz Golaszewski struct sh_early_platform_driver *epdrv;
216507fd01dSBartosz Golaszewski struct platform_device *match;
217507fd01dSBartosz Golaszewski int match_id;
218507fd01dSBartosz Golaszewski int n = 0;
219507fd01dSBartosz Golaszewski int left = 0;
220507fd01dSBartosz Golaszewski
221201e9109SBartosz Golaszewski list_for_each_entry(epdrv, &sh_early_platform_driver_list, list) {
222507fd01dSBartosz Golaszewski /* only use drivers matching our class_str */
223507fd01dSBartosz Golaszewski if (strcmp(class_str, epdrv->class_str))
224507fd01dSBartosz Golaszewski continue;
225507fd01dSBartosz Golaszewski
226507fd01dSBartosz Golaszewski if (id == -2) {
227507fd01dSBartosz Golaszewski match_id = epdrv->requested_id;
228507fd01dSBartosz Golaszewski left = 1;
229507fd01dSBartosz Golaszewski
230507fd01dSBartosz Golaszewski } else {
231507fd01dSBartosz Golaszewski match_id = id;
232201e9109SBartosz Golaszewski left += sh_early_platform_left(epdrv, id);
233507fd01dSBartosz Golaszewski
234507fd01dSBartosz Golaszewski /* skip requested id */
235507fd01dSBartosz Golaszewski switch (epdrv->requested_id) {
236507fd01dSBartosz Golaszewski case EARLY_PLATFORM_ID_ERROR:
237507fd01dSBartosz Golaszewski case EARLY_PLATFORM_ID_UNSET:
238507fd01dSBartosz Golaszewski break;
239507fd01dSBartosz Golaszewski default:
240507fd01dSBartosz Golaszewski if (epdrv->requested_id == id)
241507fd01dSBartosz Golaszewski match_id = EARLY_PLATFORM_ID_UNSET;
242507fd01dSBartosz Golaszewski }
243507fd01dSBartosz Golaszewski }
244507fd01dSBartosz Golaszewski
245507fd01dSBartosz Golaszewski switch (match_id) {
246507fd01dSBartosz Golaszewski case EARLY_PLATFORM_ID_ERROR:
247507fd01dSBartosz Golaszewski pr_warn("%s: unable to parse %s parameter\n",
248507fd01dSBartosz Golaszewski class_str, epdrv->pdrv->driver.name);
249*df561f66SGustavo A. R. Silva fallthrough;
250507fd01dSBartosz Golaszewski case EARLY_PLATFORM_ID_UNSET:
251507fd01dSBartosz Golaszewski match = NULL;
252507fd01dSBartosz Golaszewski break;
253507fd01dSBartosz Golaszewski default:
254201e9109SBartosz Golaszewski match = sh_early_platform_match(epdrv, match_id);
255507fd01dSBartosz Golaszewski }
256507fd01dSBartosz Golaszewski
257507fd01dSBartosz Golaszewski if (match) {
258507fd01dSBartosz Golaszewski /*
259507fd01dSBartosz Golaszewski * Set up a sensible init_name to enable
260507fd01dSBartosz Golaszewski * dev_name() and others to be used before the
261507fd01dSBartosz Golaszewski * rest of the driver core is initialized.
262507fd01dSBartosz Golaszewski */
263507fd01dSBartosz Golaszewski if (!match->dev.init_name && slab_is_available()) {
264507fd01dSBartosz Golaszewski if (match->id != -1)
265507fd01dSBartosz Golaszewski match->dev.init_name =
266507fd01dSBartosz Golaszewski kasprintf(GFP_KERNEL, "%s.%d",
267507fd01dSBartosz Golaszewski match->name,
268507fd01dSBartosz Golaszewski match->id);
269507fd01dSBartosz Golaszewski else
270507fd01dSBartosz Golaszewski match->dev.init_name =
271507fd01dSBartosz Golaszewski kasprintf(GFP_KERNEL, "%s",
272507fd01dSBartosz Golaszewski match->name);
273507fd01dSBartosz Golaszewski
274507fd01dSBartosz Golaszewski if (!match->dev.init_name)
275507fd01dSBartosz Golaszewski return -ENOMEM;
276507fd01dSBartosz Golaszewski }
277507fd01dSBartosz Golaszewski
278507fd01dSBartosz Golaszewski if (epdrv->pdrv->probe(match))
279507fd01dSBartosz Golaszewski pr_warn("%s: unable to probe %s early.\n",
280507fd01dSBartosz Golaszewski class_str, match->name);
281507fd01dSBartosz Golaszewski else
282507fd01dSBartosz Golaszewski n++;
283507fd01dSBartosz Golaszewski }
284507fd01dSBartosz Golaszewski
285507fd01dSBartosz Golaszewski if (n >= nr_probe)
286507fd01dSBartosz Golaszewski break;
287507fd01dSBartosz Golaszewski }
288507fd01dSBartosz Golaszewski
289507fd01dSBartosz Golaszewski if (left)
290507fd01dSBartosz Golaszewski return n;
291507fd01dSBartosz Golaszewski else
292507fd01dSBartosz Golaszewski return -ENODEV;
293507fd01dSBartosz Golaszewski }
294507fd01dSBartosz Golaszewski
295507fd01dSBartosz Golaszewski /**
296201e9109SBartosz Golaszewski * sh_early_platform_driver_probe - probe a class of registered drivers
297507fd01dSBartosz Golaszewski * @class_str: string to identify early platform driver class
298507fd01dSBartosz Golaszewski * @nr_probe: number of platform devices to successfully probe before exiting
299507fd01dSBartosz Golaszewski * @user_only: only probe user specified early platform devices
300507fd01dSBartosz Golaszewski *
301507fd01dSBartosz Golaszewski * Used by architecture code to probe registered early platform drivers
302507fd01dSBartosz Golaszewski * within a certain class. For probe to happen a registered early platform
303507fd01dSBartosz Golaszewski * device matching a registered early platform driver is needed.
304507fd01dSBartosz Golaszewski */
sh_early_platform_driver_probe(char * class_str,int nr_probe,int user_only)305201e9109SBartosz Golaszewski int __init sh_early_platform_driver_probe(char *class_str,
306507fd01dSBartosz Golaszewski int nr_probe,
307507fd01dSBartosz Golaszewski int user_only)
308507fd01dSBartosz Golaszewski {
309507fd01dSBartosz Golaszewski int k, n, i;
310507fd01dSBartosz Golaszewski
311507fd01dSBartosz Golaszewski n = 0;
312507fd01dSBartosz Golaszewski for (i = -2; n < nr_probe; i++) {
313201e9109SBartosz Golaszewski k = sh_early_platform_driver_probe_id(class_str, i, nr_probe - n);
314507fd01dSBartosz Golaszewski
315507fd01dSBartosz Golaszewski if (k < 0)
316507fd01dSBartosz Golaszewski break;
317507fd01dSBartosz Golaszewski
318507fd01dSBartosz Golaszewski n += k;
319507fd01dSBartosz Golaszewski
320507fd01dSBartosz Golaszewski if (user_only)
321507fd01dSBartosz Golaszewski break;
322507fd01dSBartosz Golaszewski }
323507fd01dSBartosz Golaszewski
324507fd01dSBartosz Golaszewski return n;
325507fd01dSBartosz Golaszewski }
326507fd01dSBartosz Golaszewski
327507fd01dSBartosz Golaszewski /**
328eecd37e1SGuenter Roeck * early_platform_cleanup - clean up early platform code
329507fd01dSBartosz Golaszewski */
early_platform_cleanup(void)330eecd37e1SGuenter Roeck void __init early_platform_cleanup(void)
331507fd01dSBartosz Golaszewski {
332507fd01dSBartosz Golaszewski struct platform_device *pd, *pd2;
333507fd01dSBartosz Golaszewski
334507fd01dSBartosz Golaszewski /* clean up the devres list used to chain devices */
335201e9109SBartosz Golaszewski list_for_each_entry_safe(pd, pd2, &sh_early_platform_device_list,
336507fd01dSBartosz Golaszewski dev.devres_head) {
337507fd01dSBartosz Golaszewski list_del(&pd->dev.devres_head);
338507fd01dSBartosz Golaszewski memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head));
339507fd01dSBartosz Golaszewski }
340507fd01dSBartosz Golaszewski }
341