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