18c2ecf20Sopenharmony_ci// SPDX--License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include <asm/platform_early.h>
48c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
58c2ecf20Sopenharmony_ci#include <linux/pm.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_cistatic __initdata LIST_HEAD(sh_early_platform_driver_list);
88c2ecf20Sopenharmony_cistatic __initdata LIST_HEAD(sh_early_platform_device_list);
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistatic const struct platform_device_id *
118c2ecf20Sopenharmony_ciplatform_match_id(const struct platform_device_id *id,
128c2ecf20Sopenharmony_ci		  struct platform_device *pdev)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	while (id->name[0]) {
158c2ecf20Sopenharmony_ci		if (strcmp(pdev->name, id->name) == 0) {
168c2ecf20Sopenharmony_ci			pdev->id_entry = id;
178c2ecf20Sopenharmony_ci			return id;
188c2ecf20Sopenharmony_ci		}
198c2ecf20Sopenharmony_ci		id++;
208c2ecf20Sopenharmony_ci	}
218c2ecf20Sopenharmony_ci	return NULL;
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int platform_match(struct device *dev, struct device_driver *drv)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
278c2ecf20Sopenharmony_ci	struct platform_driver *pdrv = to_platform_driver(drv);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	/* When driver_override is set, only bind to the matching driver */
308c2ecf20Sopenharmony_ci	if (pdev->driver_override)
318c2ecf20Sopenharmony_ci		return !strcmp(pdev->driver_override, drv->name);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/* Then try to match against the id table */
348c2ecf20Sopenharmony_ci	if (pdrv->id_table)
358c2ecf20Sopenharmony_ci		return platform_match_id(pdrv->id_table, pdev) != NULL;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* fall-back to driver name match */
388c2ecf20Sopenharmony_ci	return (strcmp(pdev->name, drv->name) == 0);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
428c2ecf20Sopenharmony_cistatic void device_pm_init_common(struct device *dev)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	if (!dev->power.early_init) {
458c2ecf20Sopenharmony_ci		spin_lock_init(&dev->power.lock);
468c2ecf20Sopenharmony_ci		dev->power.qos = NULL;
478c2ecf20Sopenharmony_ci		dev->power.early_init = true;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void pm_runtime_early_init(struct device *dev)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	dev->power.disable_depth = 1;
548c2ecf20Sopenharmony_ci	device_pm_init_common(dev);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci#else
578c2ecf20Sopenharmony_cistatic void pm_runtime_early_init(struct device *dev) {}
588c2ecf20Sopenharmony_ci#endif
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/**
618c2ecf20Sopenharmony_ci * sh_early_platform_driver_register - register early platform driver
628c2ecf20Sopenharmony_ci * @epdrv: sh_early_platform driver structure
638c2ecf20Sopenharmony_ci * @buf: string passed from early_param()
648c2ecf20Sopenharmony_ci *
658c2ecf20Sopenharmony_ci * Helper function for sh_early_platform_init() / sh_early_platform_init_buffer()
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ciint __init sh_early_platform_driver_register(struct sh_early_platform_driver *epdrv,
688c2ecf20Sopenharmony_ci					  char *buf)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	char *tmp;
718c2ecf20Sopenharmony_ci	int n;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Simply add the driver to the end of the global list.
748c2ecf20Sopenharmony_ci	 * Drivers will by default be put on the list in compiled-in order.
758c2ecf20Sopenharmony_ci	 */
768c2ecf20Sopenharmony_ci	if (!epdrv->list.next) {
778c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&epdrv->list);
788c2ecf20Sopenharmony_ci		list_add_tail(&epdrv->list, &sh_early_platform_driver_list);
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/* If the user has specified device then make sure the driver
828c2ecf20Sopenharmony_ci	 * gets prioritized. The driver of the last device specified on
838c2ecf20Sopenharmony_ci	 * command line will be put first on the list.
848c2ecf20Sopenharmony_ci	 */
858c2ecf20Sopenharmony_ci	n = strlen(epdrv->pdrv->driver.name);
868c2ecf20Sopenharmony_ci	if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) {
878c2ecf20Sopenharmony_ci		list_move(&epdrv->list, &sh_early_platform_driver_list);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		/* Allow passing parameters after device name */
908c2ecf20Sopenharmony_ci		if (buf[n] == '\0' || buf[n] == ',')
918c2ecf20Sopenharmony_ci			epdrv->requested_id = -1;
928c2ecf20Sopenharmony_ci		else {
938c2ecf20Sopenharmony_ci			epdrv->requested_id = simple_strtoul(&buf[n + 1],
948c2ecf20Sopenharmony_ci							     &tmp, 10);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci			if (buf[n] != '.' || (tmp == &buf[n + 1])) {
978c2ecf20Sopenharmony_ci				epdrv->requested_id = EARLY_PLATFORM_ID_ERROR;
988c2ecf20Sopenharmony_ci				n = 0;
998c2ecf20Sopenharmony_ci			} else
1008c2ecf20Sopenharmony_ci				n += strcspn(&buf[n + 1], ",") + 1;
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		if (buf[n] == ',')
1048c2ecf20Sopenharmony_ci			n++;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		if (epdrv->bufsize) {
1078c2ecf20Sopenharmony_ci			memcpy(epdrv->buffer, &buf[n],
1088c2ecf20Sopenharmony_ci			       min_t(int, epdrv->bufsize, strlen(&buf[n]) + 1));
1098c2ecf20Sopenharmony_ci			epdrv->buffer[epdrv->bufsize - 1] = '\0';
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/**
1178c2ecf20Sopenharmony_ci * sh_early_platform_add_devices - adds a number of early platform devices
1188c2ecf20Sopenharmony_ci * @devs: array of early platform devices to add
1198c2ecf20Sopenharmony_ci * @num: number of early platform devices in array
1208c2ecf20Sopenharmony_ci *
1218c2ecf20Sopenharmony_ci * Used by early architecture code to register early platform devices and
1228c2ecf20Sopenharmony_ci * their platform data.
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_civoid __init sh_early_platform_add_devices(struct platform_device **devs, int num)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct device *dev;
1278c2ecf20Sopenharmony_ci	int i;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* simply add the devices to list */
1308c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
1318c2ecf20Sopenharmony_ci		dev = &devs[i]->dev;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		if (!dev->devres_head.next) {
1348c2ecf20Sopenharmony_ci			pm_runtime_early_init(dev);
1358c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(&dev->devres_head);
1368c2ecf20Sopenharmony_ci			list_add_tail(&dev->devres_head,
1378c2ecf20Sopenharmony_ci				      &sh_early_platform_device_list);
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/**
1438c2ecf20Sopenharmony_ci * sh_early_platform_driver_register_all - register early platform drivers
1448c2ecf20Sopenharmony_ci * @class_str: string to identify early platform driver class
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * Used by architecture code to register all early platform drivers
1478c2ecf20Sopenharmony_ci * for a certain class. If omitted then only early platform drivers
1488c2ecf20Sopenharmony_ci * with matching kernel command line class parameters will be registered.
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_civoid __init sh_early_platform_driver_register_all(char *class_str)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	/* The "class_str" parameter may or may not be present on the kernel
1538c2ecf20Sopenharmony_ci	 * command line. If it is present then there may be more than one
1548c2ecf20Sopenharmony_ci	 * matching parameter.
1558c2ecf20Sopenharmony_ci	 *
1568c2ecf20Sopenharmony_ci	 * Since we register our early platform drivers using early_param()
1578c2ecf20Sopenharmony_ci	 * we need to make sure that they also get registered in the case
1588c2ecf20Sopenharmony_ci	 * when the parameter is missing from the kernel command line.
1598c2ecf20Sopenharmony_ci	 *
1608c2ecf20Sopenharmony_ci	 * We use parse_early_options() to make sure the early_param() gets
1618c2ecf20Sopenharmony_ci	 * called at least once. The early_param() may be called more than
1628c2ecf20Sopenharmony_ci	 * once since the name of the preferred device may be specified on
1638c2ecf20Sopenharmony_ci	 * the kernel command line. sh_early_platform_driver_register() handles
1648c2ecf20Sopenharmony_ci	 * this case for us.
1658c2ecf20Sopenharmony_ci	 */
1668c2ecf20Sopenharmony_ci	parse_early_options(class_str);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/**
1708c2ecf20Sopenharmony_ci * sh_early_platform_match - find early platform device matching driver
1718c2ecf20Sopenharmony_ci * @epdrv: early platform driver structure
1728c2ecf20Sopenharmony_ci * @id: id to match against
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_cistatic struct platform_device * __init
1758c2ecf20Sopenharmony_cish_early_platform_match(struct sh_early_platform_driver *epdrv, int id)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct platform_device *pd;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	list_for_each_entry(pd, &sh_early_platform_device_list, dev.devres_head)
1808c2ecf20Sopenharmony_ci		if (platform_match(&pd->dev, &epdrv->pdrv->driver))
1818c2ecf20Sopenharmony_ci			if (pd->id == id)
1828c2ecf20Sopenharmony_ci				return pd;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return NULL;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/**
1888c2ecf20Sopenharmony_ci * sh_early_platform_left - check if early platform driver has matching devices
1898c2ecf20Sopenharmony_ci * @epdrv: early platform driver structure
1908c2ecf20Sopenharmony_ci * @id: return true if id or above exists
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_cistatic int __init sh_early_platform_left(struct sh_early_platform_driver *epdrv,
1938c2ecf20Sopenharmony_ci				       int id)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct platform_device *pd;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	list_for_each_entry(pd, &sh_early_platform_device_list, dev.devres_head)
1988c2ecf20Sopenharmony_ci		if (platform_match(&pd->dev, &epdrv->pdrv->driver))
1998c2ecf20Sopenharmony_ci			if (pd->id >= id)
2008c2ecf20Sopenharmony_ci				return 1;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/**
2068c2ecf20Sopenharmony_ci * sh_early_platform_driver_probe_id - probe drivers matching class_str and id
2078c2ecf20Sopenharmony_ci * @class_str: string to identify early platform driver class
2088c2ecf20Sopenharmony_ci * @id: id to match against
2098c2ecf20Sopenharmony_ci * @nr_probe: number of platform devices to successfully probe before exiting
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_cistatic int __init sh_early_platform_driver_probe_id(char *class_str,
2128c2ecf20Sopenharmony_ci						 int id,
2138c2ecf20Sopenharmony_ci						 int nr_probe)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct sh_early_platform_driver *epdrv;
2168c2ecf20Sopenharmony_ci	struct platform_device *match;
2178c2ecf20Sopenharmony_ci	int match_id;
2188c2ecf20Sopenharmony_ci	int n = 0;
2198c2ecf20Sopenharmony_ci	int left = 0;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	list_for_each_entry(epdrv, &sh_early_platform_driver_list, list) {
2228c2ecf20Sopenharmony_ci		/* only use drivers matching our class_str */
2238c2ecf20Sopenharmony_ci		if (strcmp(class_str, epdrv->class_str))
2248c2ecf20Sopenharmony_ci			continue;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		if (id == -2) {
2278c2ecf20Sopenharmony_ci			match_id = epdrv->requested_id;
2288c2ecf20Sopenharmony_ci			left = 1;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		} else {
2318c2ecf20Sopenharmony_ci			match_id = id;
2328c2ecf20Sopenharmony_ci			left += sh_early_platform_left(epdrv, id);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci			/* skip requested id */
2358c2ecf20Sopenharmony_ci			switch (epdrv->requested_id) {
2368c2ecf20Sopenharmony_ci			case EARLY_PLATFORM_ID_ERROR:
2378c2ecf20Sopenharmony_ci			case EARLY_PLATFORM_ID_UNSET:
2388c2ecf20Sopenharmony_ci				break;
2398c2ecf20Sopenharmony_ci			default:
2408c2ecf20Sopenharmony_ci				if (epdrv->requested_id == id)
2418c2ecf20Sopenharmony_ci					match_id = EARLY_PLATFORM_ID_UNSET;
2428c2ecf20Sopenharmony_ci			}
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		switch (match_id) {
2468c2ecf20Sopenharmony_ci		case EARLY_PLATFORM_ID_ERROR:
2478c2ecf20Sopenharmony_ci			pr_warn("%s: unable to parse %s parameter\n",
2488c2ecf20Sopenharmony_ci				class_str, epdrv->pdrv->driver.name);
2498c2ecf20Sopenharmony_ci			fallthrough;
2508c2ecf20Sopenharmony_ci		case EARLY_PLATFORM_ID_UNSET:
2518c2ecf20Sopenharmony_ci			match = NULL;
2528c2ecf20Sopenharmony_ci			break;
2538c2ecf20Sopenharmony_ci		default:
2548c2ecf20Sopenharmony_ci			match = sh_early_platform_match(epdrv, match_id);
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		if (match) {
2588c2ecf20Sopenharmony_ci			/*
2598c2ecf20Sopenharmony_ci			 * Set up a sensible init_name to enable
2608c2ecf20Sopenharmony_ci			 * dev_name() and others to be used before the
2618c2ecf20Sopenharmony_ci			 * rest of the driver core is initialized.
2628c2ecf20Sopenharmony_ci			 */
2638c2ecf20Sopenharmony_ci			if (!match->dev.init_name && slab_is_available()) {
2648c2ecf20Sopenharmony_ci				if (match->id != -1)
2658c2ecf20Sopenharmony_ci					match->dev.init_name =
2668c2ecf20Sopenharmony_ci						kasprintf(GFP_KERNEL, "%s.%d",
2678c2ecf20Sopenharmony_ci							  match->name,
2688c2ecf20Sopenharmony_ci							  match->id);
2698c2ecf20Sopenharmony_ci				else
2708c2ecf20Sopenharmony_ci					match->dev.init_name =
2718c2ecf20Sopenharmony_ci						kasprintf(GFP_KERNEL, "%s",
2728c2ecf20Sopenharmony_ci							  match->name);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci				if (!match->dev.init_name)
2758c2ecf20Sopenharmony_ci					return -ENOMEM;
2768c2ecf20Sopenharmony_ci			}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci			if (epdrv->pdrv->probe(match))
2798c2ecf20Sopenharmony_ci				pr_warn("%s: unable to probe %s early.\n",
2808c2ecf20Sopenharmony_ci					class_str, match->name);
2818c2ecf20Sopenharmony_ci			else
2828c2ecf20Sopenharmony_ci				n++;
2838c2ecf20Sopenharmony_ci		}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		if (n >= nr_probe)
2868c2ecf20Sopenharmony_ci			break;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (left)
2908c2ecf20Sopenharmony_ci		return n;
2918c2ecf20Sopenharmony_ci	else
2928c2ecf20Sopenharmony_ci		return -ENODEV;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/**
2968c2ecf20Sopenharmony_ci * sh_early_platform_driver_probe - probe a class of registered drivers
2978c2ecf20Sopenharmony_ci * @class_str: string to identify early platform driver class
2988c2ecf20Sopenharmony_ci * @nr_probe: number of platform devices to successfully probe before exiting
2998c2ecf20Sopenharmony_ci * @user_only: only probe user specified early platform devices
3008c2ecf20Sopenharmony_ci *
3018c2ecf20Sopenharmony_ci * Used by architecture code to probe registered early platform drivers
3028c2ecf20Sopenharmony_ci * within a certain class. For probe to happen a registered early platform
3038c2ecf20Sopenharmony_ci * device matching a registered early platform driver is needed.
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_ciint __init sh_early_platform_driver_probe(char *class_str,
3068c2ecf20Sopenharmony_ci				       int nr_probe,
3078c2ecf20Sopenharmony_ci				       int user_only)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	int k, n, i;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	n = 0;
3128c2ecf20Sopenharmony_ci	for (i = -2; n < nr_probe; i++) {
3138c2ecf20Sopenharmony_ci		k = sh_early_platform_driver_probe_id(class_str, i, nr_probe - n);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		if (k < 0)
3168c2ecf20Sopenharmony_ci			break;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		n += k;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		if (user_only)
3218c2ecf20Sopenharmony_ci			break;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return n;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci/**
3288c2ecf20Sopenharmony_ci * early_platform_cleanup - clean up early platform code
3298c2ecf20Sopenharmony_ci */
3308c2ecf20Sopenharmony_civoid __init early_platform_cleanup(void)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct platform_device *pd, *pd2;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/* clean up the devres list used to chain devices */
3358c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pd, pd2, &sh_early_platform_device_list,
3368c2ecf20Sopenharmony_ci				 dev.devres_head) {
3378c2ecf20Sopenharmony_ci		list_del(&pd->dev.devres_head);
3388c2ecf20Sopenharmony_ci		memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head));
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci}
341