18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * platform.c - platform 'pseudo' bus for legacy devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2002-3 Patrick Mochel 68c2ecf20Sopenharmony_ci * Copyright (c) 2002-3 Open Source Development Labs 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Please see Documentation/driver-api/driver-model/platform.rst for more 98c2ecf20Sopenharmony_ci * information. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci#include <linux/memblock.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 248c2ecf20Sopenharmony_ci#include <linux/idr.h> 258c2ecf20Sopenharmony_ci#include <linux/acpi.h> 268c2ecf20Sopenharmony_ci#include <linux/clk/clk-conf.h> 278c2ecf20Sopenharmony_ci#include <linux/limits.h> 288c2ecf20Sopenharmony_ci#include <linux/property.h> 298c2ecf20Sopenharmony_ci#include <linux/kmemleak.h> 308c2ecf20Sopenharmony_ci#include <linux/types.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "base.h" 338c2ecf20Sopenharmony_ci#include "power/power.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* For automatically allocated device IDs */ 368c2ecf20Sopenharmony_cistatic DEFINE_IDA(platform_devid_ida); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct device platform_bus = { 398c2ecf20Sopenharmony_ci .init_name = "platform", 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_bus); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * platform_get_resource - get a resource for a device 458c2ecf20Sopenharmony_ci * @dev: platform device 468c2ecf20Sopenharmony_ci * @type: resource type 478c2ecf20Sopenharmony_ci * @num: resource index 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * Return: a pointer to the resource or NULL on failure. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistruct resource *platform_get_resource(struct platform_device *dev, 528c2ecf20Sopenharmony_ci unsigned int type, unsigned int num) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci u32 i; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_resources; i++) { 578c2ecf20Sopenharmony_ci struct resource *r = &dev->resource[i]; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (type == resource_type(r) && num-- == 0) 608c2ecf20Sopenharmony_ci return r; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci return NULL; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_resource); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#ifdef CONFIG_HAS_IOMEM 678c2ecf20Sopenharmony_ci/** 688c2ecf20Sopenharmony_ci * devm_platform_get_and_ioremap_resource - call devm_ioremap_resource() for a 698c2ecf20Sopenharmony_ci * platform device and get resource 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * @pdev: platform device to use both for memory resource lookup as well as 728c2ecf20Sopenharmony_ci * resource management 738c2ecf20Sopenharmony_ci * @index: resource index 748c2ecf20Sopenharmony_ci * @res: optional output parameter to store a pointer to the obtained resource. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 778c2ecf20Sopenharmony_ci * on failure. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_civoid __iomem * 808c2ecf20Sopenharmony_cidevm_platform_get_and_ioremap_resource(struct platform_device *pdev, 818c2ecf20Sopenharmony_ci unsigned int index, struct resource **res) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct resource *r; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, index); 868c2ecf20Sopenharmony_ci if (res) 878c2ecf20Sopenharmony_ci *res = r; 888c2ecf20Sopenharmony_ci return devm_ioremap_resource(&pdev->dev, r); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_platform_get_and_ioremap_resource); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/** 938c2ecf20Sopenharmony_ci * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform 948c2ecf20Sopenharmony_ci * device 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * @pdev: platform device to use both for memory resource lookup as well as 978c2ecf20Sopenharmony_ci * resource management 988c2ecf20Sopenharmony_ci * @index: resource index 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 1018c2ecf20Sopenharmony_ci * on failure. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_civoid __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, 1048c2ecf20Sopenharmony_ci unsigned int index) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci return devm_platform_get_and_ioremap_resource(pdev, index, NULL); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/** 1118c2ecf20Sopenharmony_ci * devm_platform_ioremap_resource_wc - write-combined variant of 1128c2ecf20Sopenharmony_ci * devm_platform_ioremap_resource() 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * @pdev: platform device to use both for memory resource lookup as well as 1158c2ecf20Sopenharmony_ci * resource management 1168c2ecf20Sopenharmony_ci * @index: resource index 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 1198c2ecf20Sopenharmony_ci * on failure. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_civoid __iomem *devm_platform_ioremap_resource_wc(struct platform_device *pdev, 1228c2ecf20Sopenharmony_ci unsigned int index) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct resource *res; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, index); 1278c2ecf20Sopenharmony_ci return devm_ioremap_resource_wc(&pdev->dev, res); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/** 1318c2ecf20Sopenharmony_ci * devm_platform_ioremap_resource_byname - call devm_ioremap_resource for 1328c2ecf20Sopenharmony_ci * a platform device, retrieve the 1338c2ecf20Sopenharmony_ci * resource by name 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * @pdev: platform device to use both for memory resource lookup as well as 1368c2ecf20Sopenharmony_ci * resource management 1378c2ecf20Sopenharmony_ci * @name: name of the resource 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 1408c2ecf20Sopenharmony_ci * on failure. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_civoid __iomem * 1438c2ecf20Sopenharmony_cidevm_platform_ioremap_resource_byname(struct platform_device *pdev, 1448c2ecf20Sopenharmony_ci const char *name) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct resource *res; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); 1498c2ecf20Sopenharmony_ci return devm_ioremap_resource(&pdev->dev, res); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname); 1528c2ecf20Sopenharmony_ci#endif /* CONFIG_HAS_IOMEM */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/** 1558c2ecf20Sopenharmony_ci * platform_get_irq_optional - get an optional IRQ for a device 1568c2ecf20Sopenharmony_ci * @dev: platform device 1578c2ecf20Sopenharmony_ci * @num: IRQ number index 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * Gets an IRQ for a platform device. Device drivers should check the return 1608c2ecf20Sopenharmony_ci * value for errors so as to not pass a negative integer value to the 1618c2ecf20Sopenharmony_ci * request_irq() APIs. This is the same as platform_get_irq(), except that it 1628c2ecf20Sopenharmony_ci * does not print an error message if an IRQ can not be obtained. 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * For example:: 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * int irq = platform_get_irq_optional(pdev, 0); 1678c2ecf20Sopenharmony_ci * if (irq < 0) 1688c2ecf20Sopenharmony_ci * return irq; 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Return: non-zero IRQ number on success, negative error number on failure. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ciint platform_get_irq_optional(struct platform_device *dev, unsigned int num) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int ret; 1758c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARC 1768c2ecf20Sopenharmony_ci /* sparc does not have irqs represented as IORESOURCE_IRQ resources */ 1778c2ecf20Sopenharmony_ci if (!dev || num >= dev->archdata.num_irqs) 1788c2ecf20Sopenharmony_ci return -ENXIO; 1798c2ecf20Sopenharmony_ci ret = dev->archdata.irqs[num]; 1808c2ecf20Sopenharmony_ci goto out; 1818c2ecf20Sopenharmony_ci#else 1828c2ecf20Sopenharmony_ci struct resource *r; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) { 1858c2ecf20Sopenharmony_ci ret = of_irq_get(dev->dev.of_node, num); 1868c2ecf20Sopenharmony_ci if (ret > 0 || ret == -EPROBE_DEFER) 1878c2ecf20Sopenharmony_ci goto out; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci r = platform_get_resource(dev, IORESOURCE_IRQ, num); 1918c2ecf20Sopenharmony_ci if (has_acpi_companion(&dev->dev)) { 1928c2ecf20Sopenharmony_ci if (r && r->flags & IORESOURCE_DISABLED) { 1938c2ecf20Sopenharmony_ci ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r); 1948c2ecf20Sopenharmony_ci if (ret) 1958c2ecf20Sopenharmony_ci goto out; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * The resources may pass trigger flags to the irqs that need 2018c2ecf20Sopenharmony_ci * to be set up. It so happens that the trigger flags for 2028c2ecf20Sopenharmony_ci * IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER* 2038c2ecf20Sopenharmony_ci * settings. 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci if (r && r->flags & IORESOURCE_BITS) { 2068c2ecf20Sopenharmony_ci struct irq_data *irqd; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci irqd = irq_get_irq_data(r->start); 2098c2ecf20Sopenharmony_ci if (!irqd) { 2108c2ecf20Sopenharmony_ci ret = -ENXIO; 2118c2ecf20Sopenharmony_ci goto out; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (r) { 2178c2ecf20Sopenharmony_ci ret = r->start; 2188c2ecf20Sopenharmony_ci goto out; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * For the index 0 interrupt, allow falling back to GpioInt 2238c2ecf20Sopenharmony_ci * resources. While a device could have both Interrupt and GpioInt 2248c2ecf20Sopenharmony_ci * resources, making this fallback ambiguous, in many common cases 2258c2ecf20Sopenharmony_ci * the device will only expose one IRQ, and this fallback 2268c2ecf20Sopenharmony_ci * allows a common code path across either kind of resource. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci if (num == 0 && has_acpi_companion(&dev->dev)) { 2298c2ecf20Sopenharmony_ci ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); 2308c2ecf20Sopenharmony_ci /* Our callers expect -ENXIO for missing IRQs. */ 2318c2ecf20Sopenharmony_ci if (ret >= 0 || ret == -EPROBE_DEFER) 2328c2ecf20Sopenharmony_ci goto out; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = -ENXIO; 2368c2ecf20Sopenharmony_ci#endif 2378c2ecf20Sopenharmony_ciout: 2388c2ecf20Sopenharmony_ci WARN(ret == 0, "0 is an invalid IRQ number\n"); 2398c2ecf20Sopenharmony_ci return ret; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_irq_optional); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/** 2448c2ecf20Sopenharmony_ci * platform_get_irq - get an IRQ for a device 2458c2ecf20Sopenharmony_ci * @dev: platform device 2468c2ecf20Sopenharmony_ci * @num: IRQ number index 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Gets an IRQ for a platform device and prints an error message if finding the 2498c2ecf20Sopenharmony_ci * IRQ fails. Device drivers should check the return value for errors so as to 2508c2ecf20Sopenharmony_ci * not pass a negative integer value to the request_irq() APIs. 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * For example:: 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * int irq = platform_get_irq(pdev, 0); 2558c2ecf20Sopenharmony_ci * if (irq < 0) 2568c2ecf20Sopenharmony_ci * return irq; 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * Return: non-zero IRQ number on success, negative error number on failure. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ciint platform_get_irq(struct platform_device *dev, unsigned int num) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci int ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ret = platform_get_irq_optional(dev, num); 2658c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EPROBE_DEFER) 2668c2ecf20Sopenharmony_ci dev_err(&dev->dev, "IRQ index %u not found\n", num); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_irq); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/** 2738c2ecf20Sopenharmony_ci * platform_irq_count - Count the number of IRQs a platform device uses 2748c2ecf20Sopenharmony_ci * @dev: platform device 2758c2ecf20Sopenharmony_ci * 2768c2ecf20Sopenharmony_ci * Return: Number of IRQs a platform device uses or EPROBE_DEFER 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ciint platform_irq_count(struct platform_device *dev) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci int ret, nr = 0; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci while ((ret = platform_get_irq_optional(dev, nr)) >= 0) 2838c2ecf20Sopenharmony_ci nr++; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return nr; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_irq_count); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/** 2938c2ecf20Sopenharmony_ci * platform_get_resource_byname - get a resource for a device by name 2948c2ecf20Sopenharmony_ci * @dev: platform device 2958c2ecf20Sopenharmony_ci * @type: resource type 2968c2ecf20Sopenharmony_ci * @name: resource name 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_cistruct resource *platform_get_resource_byname(struct platform_device *dev, 2998c2ecf20Sopenharmony_ci unsigned int type, 3008c2ecf20Sopenharmony_ci const char *name) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci u32 i; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_resources; i++) { 3058c2ecf20Sopenharmony_ci struct resource *r = &dev->resource[i]; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (unlikely(!r->name)) 3088c2ecf20Sopenharmony_ci continue; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (type == resource_type(r) && !strcmp(r->name, name)) 3118c2ecf20Sopenharmony_ci return r; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci return NULL; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_resource_byname); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int __platform_get_irq_byname(struct platform_device *dev, 3188c2ecf20Sopenharmony_ci const char *name) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct resource *r; 3218c2ecf20Sopenharmony_ci int ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) { 3248c2ecf20Sopenharmony_ci ret = of_irq_get_byname(dev->dev.of_node, name); 3258c2ecf20Sopenharmony_ci if (ret > 0 || ret == -EPROBE_DEFER) 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); 3308c2ecf20Sopenharmony_ci if (r) { 3318c2ecf20Sopenharmony_ci WARN(r->start == 0, "0 is an invalid IRQ number\n"); 3328c2ecf20Sopenharmony_ci return r->start; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return -ENXIO; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/** 3398c2ecf20Sopenharmony_ci * platform_get_irq_byname - get an IRQ for a device by name 3408c2ecf20Sopenharmony_ci * @dev: platform device 3418c2ecf20Sopenharmony_ci * @name: IRQ name 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * Get an IRQ like platform_get_irq(), but then by name rather then by index. 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * Return: non-zero IRQ number on success, negative error number on failure. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ciint platform_get_irq_byname(struct platform_device *dev, const char *name) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci int ret; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ret = __platform_get_irq_byname(dev, name); 3528c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EPROBE_DEFER) 3538c2ecf20Sopenharmony_ci dev_err(&dev->dev, "IRQ %s not found\n", name); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return ret; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_irq_byname); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/** 3608c2ecf20Sopenharmony_ci * platform_get_irq_byname_optional - get an optional IRQ for a device by name 3618c2ecf20Sopenharmony_ci * @dev: platform device 3628c2ecf20Sopenharmony_ci * @name: IRQ name 3638c2ecf20Sopenharmony_ci * 3648c2ecf20Sopenharmony_ci * Get an optional IRQ by name like platform_get_irq_byname(). Except that it 3658c2ecf20Sopenharmony_ci * does not print an error message if an IRQ can not be obtained. 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci * Return: non-zero IRQ number on success, negative error number on failure. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ciint platform_get_irq_byname_optional(struct platform_device *dev, 3708c2ecf20Sopenharmony_ci const char *name) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci return __platform_get_irq_byname(dev, name); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_irq_byname_optional); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/** 3778c2ecf20Sopenharmony_ci * platform_add_devices - add a numbers of platform devices 3788c2ecf20Sopenharmony_ci * @devs: array of platform devices to add 3798c2ecf20Sopenharmony_ci * @num: number of platform devices in array 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ciint platform_add_devices(struct platform_device **devs, int num) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int i, ret = 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 3868c2ecf20Sopenharmony_ci ret = platform_device_register(devs[i]); 3878c2ecf20Sopenharmony_ci if (ret) { 3888c2ecf20Sopenharmony_ci while (--i >= 0) 3898c2ecf20Sopenharmony_ci platform_device_unregister(devs[i]); 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_add_devices); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistruct platform_object { 3998c2ecf20Sopenharmony_ci struct platform_device pdev; 4008c2ecf20Sopenharmony_ci char name[]; 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/* 4048c2ecf20Sopenharmony_ci * Set up default DMA mask for platform devices if the they weren't 4058c2ecf20Sopenharmony_ci * previously set by the architecture / DT. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic void setup_pdev_dma_masks(struct platform_device *pdev) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci pdev->dev.dma_parms = &pdev->dma_parms; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!pdev->dev.coherent_dma_mask) 4128c2ecf20Sopenharmony_ci pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 4138c2ecf20Sopenharmony_ci if (!pdev->dev.dma_mask) { 4148c2ecf20Sopenharmony_ci pdev->platform_dma_mask = DMA_BIT_MASK(32); 4158c2ecf20Sopenharmony_ci pdev->dev.dma_mask = &pdev->platform_dma_mask; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci}; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/** 4208c2ecf20Sopenharmony_ci * platform_device_put - destroy a platform device 4218c2ecf20Sopenharmony_ci * @pdev: platform device to free 4228c2ecf20Sopenharmony_ci * 4238c2ecf20Sopenharmony_ci * Free all memory associated with a platform device. This function must 4248c2ecf20Sopenharmony_ci * _only_ be externally called in error cases. All other usage is a bug. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_civoid platform_device_put(struct platform_device *pdev) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(pdev)) 4298c2ecf20Sopenharmony_ci put_device(&pdev->dev); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_put); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void platform_device_release(struct device *dev) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct platform_object *pa = container_of(dev, struct platform_object, 4368c2ecf20Sopenharmony_ci pdev.dev); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci of_device_node_put(&pa->pdev.dev); 4398c2ecf20Sopenharmony_ci kfree(pa->pdev.dev.platform_data); 4408c2ecf20Sopenharmony_ci kfree(pa->pdev.mfd_cell); 4418c2ecf20Sopenharmony_ci kfree(pa->pdev.resource); 4428c2ecf20Sopenharmony_ci kfree(pa->pdev.driver_override); 4438c2ecf20Sopenharmony_ci kfree(pa); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/** 4478c2ecf20Sopenharmony_ci * platform_device_alloc - create a platform device 4488c2ecf20Sopenharmony_ci * @name: base name of the device we're adding 4498c2ecf20Sopenharmony_ci * @id: instance id 4508c2ecf20Sopenharmony_ci * 4518c2ecf20Sopenharmony_ci * Create a platform device object which can have other objects attached 4528c2ecf20Sopenharmony_ci * to it, and which will have attached objects freed when it is released. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistruct platform_device *platform_device_alloc(const char *name, int id) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct platform_object *pa; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci pa = kzalloc(sizeof(*pa) + strlen(name) + 1, GFP_KERNEL); 4598c2ecf20Sopenharmony_ci if (pa) { 4608c2ecf20Sopenharmony_ci strcpy(pa->name, name); 4618c2ecf20Sopenharmony_ci pa->pdev.name = pa->name; 4628c2ecf20Sopenharmony_ci pa->pdev.id = id; 4638c2ecf20Sopenharmony_ci device_initialize(&pa->pdev.dev); 4648c2ecf20Sopenharmony_ci pa->pdev.dev.release = platform_device_release; 4658c2ecf20Sopenharmony_ci setup_pdev_dma_masks(&pa->pdev); 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return pa ? &pa->pdev : NULL; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_alloc); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/** 4738c2ecf20Sopenharmony_ci * platform_device_add_resources - add resources to a platform device 4748c2ecf20Sopenharmony_ci * @pdev: platform device allocated by platform_device_alloc to add resources to 4758c2ecf20Sopenharmony_ci * @res: set of resources that needs to be allocated for the device 4768c2ecf20Sopenharmony_ci * @num: number of resources 4778c2ecf20Sopenharmony_ci * 4788c2ecf20Sopenharmony_ci * Add a copy of the resources to the platform device. The memory 4798c2ecf20Sopenharmony_ci * associated with the resources will be freed when the platform device is 4808c2ecf20Sopenharmony_ci * released. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ciint platform_device_add_resources(struct platform_device *pdev, 4838c2ecf20Sopenharmony_ci const struct resource *res, unsigned int num) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct resource *r = NULL; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (res) { 4888c2ecf20Sopenharmony_ci r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL); 4898c2ecf20Sopenharmony_ci if (!r) 4908c2ecf20Sopenharmony_ci return -ENOMEM; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci kfree(pdev->resource); 4948c2ecf20Sopenharmony_ci pdev->resource = r; 4958c2ecf20Sopenharmony_ci pdev->num_resources = num; 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_add_resources); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/** 5018c2ecf20Sopenharmony_ci * platform_device_add_data - add platform-specific data to a platform device 5028c2ecf20Sopenharmony_ci * @pdev: platform device allocated by platform_device_alloc to add resources to 5038c2ecf20Sopenharmony_ci * @data: platform specific data for this platform device 5048c2ecf20Sopenharmony_ci * @size: size of platform specific data 5058c2ecf20Sopenharmony_ci * 5068c2ecf20Sopenharmony_ci * Add a copy of platform specific data to the platform device's 5078c2ecf20Sopenharmony_ci * platform_data pointer. The memory associated with the platform data 5088c2ecf20Sopenharmony_ci * will be freed when the platform device is released. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ciint platform_device_add_data(struct platform_device *pdev, const void *data, 5118c2ecf20Sopenharmony_ci size_t size) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci void *d = NULL; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (data) { 5168c2ecf20Sopenharmony_ci d = kmemdup(data, size, GFP_KERNEL); 5178c2ecf20Sopenharmony_ci if (!d) 5188c2ecf20Sopenharmony_ci return -ENOMEM; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci kfree(pdev->dev.platform_data); 5228c2ecf20Sopenharmony_ci pdev->dev.platform_data = d; 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_add_data); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/** 5288c2ecf20Sopenharmony_ci * platform_device_add_properties - add built-in properties to a platform device 5298c2ecf20Sopenharmony_ci * @pdev: platform device to add properties to 5308c2ecf20Sopenharmony_ci * @properties: null terminated array of properties to add 5318c2ecf20Sopenharmony_ci * 5328c2ecf20Sopenharmony_ci * The function will take deep copy of @properties and attach the copy to the 5338c2ecf20Sopenharmony_ci * platform device. The memory associated with properties will be freed when the 5348c2ecf20Sopenharmony_ci * platform device is released. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_ciint platform_device_add_properties(struct platform_device *pdev, 5378c2ecf20Sopenharmony_ci const struct property_entry *properties) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci return device_add_properties(&pdev->dev, properties); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_add_properties); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/** 5448c2ecf20Sopenharmony_ci * platform_device_add - add a platform device to device hierarchy 5458c2ecf20Sopenharmony_ci * @pdev: platform device we're adding 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci * This is part 2 of platform_device_register(), though may be called 5488c2ecf20Sopenharmony_ci * separately _iff_ pdev was allocated by platform_device_alloc(). 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_ciint platform_device_add(struct platform_device *pdev) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci u32 i; 5538c2ecf20Sopenharmony_ci int ret; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (!pdev) 5568c2ecf20Sopenharmony_ci return -EINVAL; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!pdev->dev.parent) 5598c2ecf20Sopenharmony_ci pdev->dev.parent = &platform_bus; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci pdev->dev.bus = &platform_bus_type; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci switch (pdev->id) { 5648c2ecf20Sopenharmony_ci default: 5658c2ecf20Sopenharmony_ci dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci case PLATFORM_DEVID_NONE: 5688c2ecf20Sopenharmony_ci dev_set_name(&pdev->dev, "%s", pdev->name); 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci case PLATFORM_DEVID_AUTO: 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * Automatically allocated device ID. We mark it as such so 5738c2ecf20Sopenharmony_ci * that we remember it must be freed, and we append a suffix 5748c2ecf20Sopenharmony_ci * to avoid namespace collision with explicit IDs. 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci ret = ida_alloc(&platform_devid_ida, GFP_KERNEL); 5778c2ecf20Sopenharmony_ci if (ret < 0) 5788c2ecf20Sopenharmony_ci goto err_out; 5798c2ecf20Sopenharmony_ci pdev->id = ret; 5808c2ecf20Sopenharmony_ci pdev->id_auto = true; 5818c2ecf20Sopenharmony_ci dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci for (i = 0; i < pdev->num_resources; i++) { 5868c2ecf20Sopenharmony_ci struct resource *p, *r = &pdev->resource[i]; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (r->name == NULL) 5898c2ecf20Sopenharmony_ci r->name = dev_name(&pdev->dev); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci p = r->parent; 5928c2ecf20Sopenharmony_ci if (!p) { 5938c2ecf20Sopenharmony_ci if (resource_type(r) == IORESOURCE_MEM) 5948c2ecf20Sopenharmony_ci p = &iomem_resource; 5958c2ecf20Sopenharmony_ci else if (resource_type(r) == IORESOURCE_IO) 5968c2ecf20Sopenharmony_ci p = &ioport_resource; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (p) { 6008c2ecf20Sopenharmony_ci ret = insert_resource(p, r); 6018c2ecf20Sopenharmony_ci if (ret) { 6028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r); 6038c2ecf20Sopenharmony_ci goto failed; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci pr_debug("Registering platform device '%s'. Parent at %s\n", 6098c2ecf20Sopenharmony_ci dev_name(&pdev->dev), dev_name(pdev->dev.parent)); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci ret = device_add(&pdev->dev); 6128c2ecf20Sopenharmony_ci if (ret == 0) 6138c2ecf20Sopenharmony_ci return ret; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci failed: 6168c2ecf20Sopenharmony_ci if (pdev->id_auto) { 6178c2ecf20Sopenharmony_ci ida_free(&platform_devid_ida, pdev->id); 6188c2ecf20Sopenharmony_ci pdev->id = PLATFORM_DEVID_AUTO; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci while (i--) { 6228c2ecf20Sopenharmony_ci struct resource *r = &pdev->resource[i]; 6238c2ecf20Sopenharmony_ci if (r->parent) 6248c2ecf20Sopenharmony_ci release_resource(r); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci err_out: 6288c2ecf20Sopenharmony_ci return ret; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_add); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/** 6338c2ecf20Sopenharmony_ci * platform_device_del - remove a platform-level device 6348c2ecf20Sopenharmony_ci * @pdev: platform device we're removing 6358c2ecf20Sopenharmony_ci * 6368c2ecf20Sopenharmony_ci * Note that this function will also release all memory- and port-based 6378c2ecf20Sopenharmony_ci * resources owned by the device (@dev->resource). This function must 6388c2ecf20Sopenharmony_ci * _only_ be externally called in error cases. All other usage is a bug. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_civoid platform_device_del(struct platform_device *pdev) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci u32 i; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(pdev)) { 6458c2ecf20Sopenharmony_ci device_del(&pdev->dev); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (pdev->id_auto) { 6488c2ecf20Sopenharmony_ci ida_free(&platform_devid_ida, pdev->id); 6498c2ecf20Sopenharmony_ci pdev->id = PLATFORM_DEVID_AUTO; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci for (i = 0; i < pdev->num_resources; i++) { 6538c2ecf20Sopenharmony_ci struct resource *r = &pdev->resource[i]; 6548c2ecf20Sopenharmony_ci if (r->parent) 6558c2ecf20Sopenharmony_ci release_resource(r); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_del); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/** 6628c2ecf20Sopenharmony_ci * platform_device_register - add a platform-level device 6638c2ecf20Sopenharmony_ci * @pdev: platform device we're adding 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_ciint platform_device_register(struct platform_device *pdev) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci device_initialize(&pdev->dev); 6688c2ecf20Sopenharmony_ci setup_pdev_dma_masks(pdev); 6698c2ecf20Sopenharmony_ci return platform_device_add(pdev); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_register); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/** 6748c2ecf20Sopenharmony_ci * platform_device_unregister - unregister a platform-level device 6758c2ecf20Sopenharmony_ci * @pdev: platform device we're unregistering 6768c2ecf20Sopenharmony_ci * 6778c2ecf20Sopenharmony_ci * Unregistration is done in 2 steps. First we release all resources 6788c2ecf20Sopenharmony_ci * and remove it from the subsystem, then we drop reference count by 6798c2ecf20Sopenharmony_ci * calling platform_device_put(). 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_civoid platform_device_unregister(struct platform_device *pdev) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci platform_device_del(pdev); 6848c2ecf20Sopenharmony_ci platform_device_put(pdev); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_unregister); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/** 6898c2ecf20Sopenharmony_ci * platform_device_register_full - add a platform-level device with 6908c2ecf20Sopenharmony_ci * resources and platform-specific data 6918c2ecf20Sopenharmony_ci * 6928c2ecf20Sopenharmony_ci * @pdevinfo: data used to create device 6938c2ecf20Sopenharmony_ci * 6948c2ecf20Sopenharmony_ci * Returns &struct platform_device pointer on success, or ERR_PTR() on error. 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_cistruct platform_device *platform_device_register_full( 6978c2ecf20Sopenharmony_ci const struct platform_device_info *pdevinfo) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci int ret; 7008c2ecf20Sopenharmony_ci struct platform_device *pdev; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id); 7038c2ecf20Sopenharmony_ci if (!pdev) 7048c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci pdev->dev.parent = pdevinfo->parent; 7078c2ecf20Sopenharmony_ci pdev->dev.fwnode = pdevinfo->fwnode; 7088c2ecf20Sopenharmony_ci pdev->dev.of_node = of_node_get(to_of_node(pdev->dev.fwnode)); 7098c2ecf20Sopenharmony_ci pdev->dev.of_node_reused = pdevinfo->of_node_reused; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (pdevinfo->dma_mask) { 7128c2ecf20Sopenharmony_ci pdev->platform_dma_mask = pdevinfo->dma_mask; 7138c2ecf20Sopenharmony_ci pdev->dev.dma_mask = &pdev->platform_dma_mask; 7148c2ecf20Sopenharmony_ci pdev->dev.coherent_dma_mask = pdevinfo->dma_mask; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci ret = platform_device_add_resources(pdev, 7188c2ecf20Sopenharmony_ci pdevinfo->res, pdevinfo->num_res); 7198c2ecf20Sopenharmony_ci if (ret) 7208c2ecf20Sopenharmony_ci goto err; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci ret = platform_device_add_data(pdev, 7238c2ecf20Sopenharmony_ci pdevinfo->data, pdevinfo->size_data); 7248c2ecf20Sopenharmony_ci if (ret) 7258c2ecf20Sopenharmony_ci goto err; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (pdevinfo->properties) { 7288c2ecf20Sopenharmony_ci ret = platform_device_add_properties(pdev, 7298c2ecf20Sopenharmony_ci pdevinfo->properties); 7308c2ecf20Sopenharmony_ci if (ret) 7318c2ecf20Sopenharmony_ci goto err; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ret = platform_device_add(pdev); 7358c2ecf20Sopenharmony_ci if (ret) { 7368c2ecf20Sopenharmony_cierr: 7378c2ecf20Sopenharmony_ci ACPI_COMPANION_SET(&pdev->dev, NULL); 7388c2ecf20Sopenharmony_ci platform_device_put(pdev); 7398c2ecf20Sopenharmony_ci return ERR_PTR(ret); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return pdev; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_register_full); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int platform_drv_probe(struct device *_dev) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct platform_driver *drv = to_platform_driver(_dev->driver); 7498c2ecf20Sopenharmony_ci struct platform_device *dev = to_platform_device(_dev); 7508c2ecf20Sopenharmony_ci int ret; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci ret = of_clk_set_defaults(_dev->of_node, false); 7538c2ecf20Sopenharmony_ci if (ret < 0) 7548c2ecf20Sopenharmony_ci return ret; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci ret = dev_pm_domain_attach(_dev, true); 7578c2ecf20Sopenharmony_ci if (ret) 7588c2ecf20Sopenharmony_ci goto out; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (drv->probe) { 7618c2ecf20Sopenharmony_ci ret = drv->probe(dev); 7628c2ecf20Sopenharmony_ci if (ret) 7638c2ecf20Sopenharmony_ci dev_pm_domain_detach(_dev, true); 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ciout: 7678c2ecf20Sopenharmony_ci if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { 7688c2ecf20Sopenharmony_ci dev_warn(_dev, "probe deferral not supported\n"); 7698c2ecf20Sopenharmony_ci ret = -ENXIO; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci return ret; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int platform_drv_probe_fail(struct device *_dev) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci return -ENXIO; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic int platform_drv_remove(struct device *_dev) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci struct platform_driver *drv = to_platform_driver(_dev->driver); 7838c2ecf20Sopenharmony_ci struct platform_device *dev = to_platform_device(_dev); 7848c2ecf20Sopenharmony_ci int ret = 0; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (drv->remove) 7878c2ecf20Sopenharmony_ci ret = drv->remove(dev); 7888c2ecf20Sopenharmony_ci dev_pm_domain_detach(_dev, true); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return ret; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic void platform_drv_shutdown(struct device *_dev) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci struct platform_driver *drv = to_platform_driver(_dev->driver); 7968c2ecf20Sopenharmony_ci struct platform_device *dev = to_platform_device(_dev); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (drv->shutdown) 7998c2ecf20Sopenharmony_ci drv->shutdown(dev); 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci/** 8038c2ecf20Sopenharmony_ci * __platform_driver_register - register a driver for platform-level devices 8048c2ecf20Sopenharmony_ci * @drv: platform driver structure 8058c2ecf20Sopenharmony_ci * @owner: owning module/driver 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_ciint __platform_driver_register(struct platform_driver *drv, 8088c2ecf20Sopenharmony_ci struct module *owner) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci drv->driver.owner = owner; 8118c2ecf20Sopenharmony_ci drv->driver.bus = &platform_bus_type; 8128c2ecf20Sopenharmony_ci drv->driver.probe = platform_drv_probe; 8138c2ecf20Sopenharmony_ci drv->driver.remove = platform_drv_remove; 8148c2ecf20Sopenharmony_ci drv->driver.shutdown = platform_drv_shutdown; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci return driver_register(&drv->driver); 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__platform_driver_register); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci/** 8218c2ecf20Sopenharmony_ci * platform_driver_unregister - unregister a driver for platform-level devices 8228c2ecf20Sopenharmony_ci * @drv: platform driver structure 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_civoid platform_driver_unregister(struct platform_driver *drv) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci driver_unregister(&drv->driver); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_driver_unregister); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/** 8318c2ecf20Sopenharmony_ci * __platform_driver_probe - register driver for non-hotpluggable device 8328c2ecf20Sopenharmony_ci * @drv: platform driver structure 8338c2ecf20Sopenharmony_ci * @probe: the driver probe routine, probably from an __init section 8348c2ecf20Sopenharmony_ci * @module: module which will be the owner of the driver 8358c2ecf20Sopenharmony_ci * 8368c2ecf20Sopenharmony_ci * Use this instead of platform_driver_register() when you know the device 8378c2ecf20Sopenharmony_ci * is not hotpluggable and has already been registered, and you want to 8388c2ecf20Sopenharmony_ci * remove its run-once probe() infrastructure from memory after the driver 8398c2ecf20Sopenharmony_ci * has bound to the device. 8408c2ecf20Sopenharmony_ci * 8418c2ecf20Sopenharmony_ci * One typical use for this would be with drivers for controllers integrated 8428c2ecf20Sopenharmony_ci * into system-on-chip processors, where the controller devices have been 8438c2ecf20Sopenharmony_ci * configured as part of board setup. 8448c2ecf20Sopenharmony_ci * 8458c2ecf20Sopenharmony_ci * Note that this is incompatible with deferred probing. 8468c2ecf20Sopenharmony_ci * 8478c2ecf20Sopenharmony_ci * Returns zero if the driver registered and bound to a device, else returns 8488c2ecf20Sopenharmony_ci * a negative error code and with the driver not registered. 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_ciint __init_or_module __platform_driver_probe(struct platform_driver *drv, 8518c2ecf20Sopenharmony_ci int (*probe)(struct platform_device *), struct module *module) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci int retval, code; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (drv->driver.probe_type == PROBE_PREFER_ASYNCHRONOUS) { 8568c2ecf20Sopenharmony_ci pr_err("%s: drivers registered with %s can not be probed asynchronously\n", 8578c2ecf20Sopenharmony_ci drv->driver.name, __func__); 8588c2ecf20Sopenharmony_ci return -EINVAL; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* 8628c2ecf20Sopenharmony_ci * We have to run our probes synchronously because we check if 8638c2ecf20Sopenharmony_ci * we find any devices to bind to and exit with error if there 8648c2ecf20Sopenharmony_ci * are any. 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_ci drv->driver.probe_type = PROBE_FORCE_SYNCHRONOUS; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* 8698c2ecf20Sopenharmony_ci * Prevent driver from requesting probe deferral to avoid further 8708c2ecf20Sopenharmony_ci * futile probe attempts. 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_ci drv->prevent_deferred_probe = true; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* make sure driver won't have bind/unbind attributes */ 8758c2ecf20Sopenharmony_ci drv->driver.suppress_bind_attrs = true; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* temporary section violation during probe() */ 8788c2ecf20Sopenharmony_ci drv->probe = probe; 8798c2ecf20Sopenharmony_ci retval = code = __platform_driver_register(drv, module); 8808c2ecf20Sopenharmony_ci if (retval) 8818c2ecf20Sopenharmony_ci return retval; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* 8848c2ecf20Sopenharmony_ci * Fixup that section violation, being paranoid about code scanning 8858c2ecf20Sopenharmony_ci * the list of drivers in order to probe new devices. Check to see 8868c2ecf20Sopenharmony_ci * if the probe was successful, and make sure any forced probes of 8878c2ecf20Sopenharmony_ci * new devices fail. 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_ci spin_lock(&drv->driver.bus->p->klist_drivers.k_lock); 8908c2ecf20Sopenharmony_ci drv->probe = NULL; 8918c2ecf20Sopenharmony_ci if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list)) 8928c2ecf20Sopenharmony_ci retval = -ENODEV; 8938c2ecf20Sopenharmony_ci drv->driver.probe = platform_drv_probe_fail; 8948c2ecf20Sopenharmony_ci spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (code != retval) 8978c2ecf20Sopenharmony_ci platform_driver_unregister(drv); 8988c2ecf20Sopenharmony_ci return retval; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__platform_driver_probe); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/** 9038c2ecf20Sopenharmony_ci * __platform_create_bundle - register driver and create corresponding device 9048c2ecf20Sopenharmony_ci * @driver: platform driver structure 9058c2ecf20Sopenharmony_ci * @probe: the driver probe routine, probably from an __init section 9068c2ecf20Sopenharmony_ci * @res: set of resources that needs to be allocated for the device 9078c2ecf20Sopenharmony_ci * @n_res: number of resources 9088c2ecf20Sopenharmony_ci * @data: platform specific data for this platform device 9098c2ecf20Sopenharmony_ci * @size: size of platform specific data 9108c2ecf20Sopenharmony_ci * @module: module which will be the owner of the driver 9118c2ecf20Sopenharmony_ci * 9128c2ecf20Sopenharmony_ci * Use this in legacy-style modules that probe hardware directly and 9138c2ecf20Sopenharmony_ci * register a single platform device and corresponding platform driver. 9148c2ecf20Sopenharmony_ci * 9158c2ecf20Sopenharmony_ci * Returns &struct platform_device pointer on success, or ERR_PTR() on error. 9168c2ecf20Sopenharmony_ci */ 9178c2ecf20Sopenharmony_cistruct platform_device * __init_or_module __platform_create_bundle( 9188c2ecf20Sopenharmony_ci struct platform_driver *driver, 9198c2ecf20Sopenharmony_ci int (*probe)(struct platform_device *), 9208c2ecf20Sopenharmony_ci struct resource *res, unsigned int n_res, 9218c2ecf20Sopenharmony_ci const void *data, size_t size, struct module *module) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct platform_device *pdev; 9248c2ecf20Sopenharmony_ci int error; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci pdev = platform_device_alloc(driver->driver.name, -1); 9278c2ecf20Sopenharmony_ci if (!pdev) { 9288c2ecf20Sopenharmony_ci error = -ENOMEM; 9298c2ecf20Sopenharmony_ci goto err_out; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci error = platform_device_add_resources(pdev, res, n_res); 9338c2ecf20Sopenharmony_ci if (error) 9348c2ecf20Sopenharmony_ci goto err_pdev_put; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci error = platform_device_add_data(pdev, data, size); 9378c2ecf20Sopenharmony_ci if (error) 9388c2ecf20Sopenharmony_ci goto err_pdev_put; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci error = platform_device_add(pdev); 9418c2ecf20Sopenharmony_ci if (error) 9428c2ecf20Sopenharmony_ci goto err_pdev_put; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci error = __platform_driver_probe(driver, probe, module); 9458c2ecf20Sopenharmony_ci if (error) 9468c2ecf20Sopenharmony_ci goto err_pdev_del; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci return pdev; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cierr_pdev_del: 9518c2ecf20Sopenharmony_ci platform_device_del(pdev); 9528c2ecf20Sopenharmony_cierr_pdev_put: 9538c2ecf20Sopenharmony_ci platform_device_put(pdev); 9548c2ecf20Sopenharmony_cierr_out: 9558c2ecf20Sopenharmony_ci return ERR_PTR(error); 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__platform_create_bundle); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci/** 9608c2ecf20Sopenharmony_ci * __platform_register_drivers - register an array of platform drivers 9618c2ecf20Sopenharmony_ci * @drivers: an array of drivers to register 9628c2ecf20Sopenharmony_ci * @count: the number of drivers to register 9638c2ecf20Sopenharmony_ci * @owner: module owning the drivers 9648c2ecf20Sopenharmony_ci * 9658c2ecf20Sopenharmony_ci * Registers platform drivers specified by an array. On failure to register a 9668c2ecf20Sopenharmony_ci * driver, all previously registered drivers will be unregistered. Callers of 9678c2ecf20Sopenharmony_ci * this API should use platform_unregister_drivers() to unregister drivers in 9688c2ecf20Sopenharmony_ci * the reverse order. 9698c2ecf20Sopenharmony_ci * 9708c2ecf20Sopenharmony_ci * Returns: 0 on success or a negative error code on failure. 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_ciint __platform_register_drivers(struct platform_driver * const *drivers, 9738c2ecf20Sopenharmony_ci unsigned int count, struct module *owner) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci unsigned int i; 9768c2ecf20Sopenharmony_ci int err; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 9798c2ecf20Sopenharmony_ci pr_debug("registering platform driver %ps\n", drivers[i]); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci err = __platform_driver_register(drivers[i], owner); 9828c2ecf20Sopenharmony_ci if (err < 0) { 9838c2ecf20Sopenharmony_ci pr_err("failed to register platform driver %ps: %d\n", 9848c2ecf20Sopenharmony_ci drivers[i], err); 9858c2ecf20Sopenharmony_ci goto error; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci return 0; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cierror: 9928c2ecf20Sopenharmony_ci while (i--) { 9938c2ecf20Sopenharmony_ci pr_debug("unregistering platform driver %ps\n", drivers[i]); 9948c2ecf20Sopenharmony_ci platform_driver_unregister(drivers[i]); 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return err; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__platform_register_drivers); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci/** 10028c2ecf20Sopenharmony_ci * platform_unregister_drivers - unregister an array of platform drivers 10038c2ecf20Sopenharmony_ci * @drivers: an array of drivers to unregister 10048c2ecf20Sopenharmony_ci * @count: the number of drivers to unregister 10058c2ecf20Sopenharmony_ci * 10068c2ecf20Sopenharmony_ci * Unregisters platform drivers specified by an array. This is typically used 10078c2ecf20Sopenharmony_ci * to complement an earlier call to platform_register_drivers(). Drivers are 10088c2ecf20Sopenharmony_ci * unregistered in the reverse order in which they were registered. 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_civoid platform_unregister_drivers(struct platform_driver * const *drivers, 10118c2ecf20Sopenharmony_ci unsigned int count) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci while (count--) { 10148c2ecf20Sopenharmony_ci pr_debug("unregistering platform driver %ps\n", drivers[count]); 10158c2ecf20Sopenharmony_ci platform_driver_unregister(drivers[count]); 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_unregister_drivers); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci/* modalias support enables more hands-off userspace setup: 10218c2ecf20Sopenharmony_ci * (a) environment variable lets new-style hotplug events work once system is 10228c2ecf20Sopenharmony_ci * fully running: "modprobe $MODALIAS" 10238c2ecf20Sopenharmony_ci * (b) sysfs attribute lets new-style coldplug recover from hotplug events 10248c2ecf20Sopenharmony_ci * mishandled before system is fully running: "modprobe $(cat modalias)" 10258c2ecf20Sopenharmony_ci */ 10268c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, 10278c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 10308c2ecf20Sopenharmony_ci int len; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci len = of_device_modalias(dev, buf, PAGE_SIZE); 10338c2ecf20Sopenharmony_ci if (len != -ENODEV) 10348c2ecf20Sopenharmony_ci return len; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); 10378c2ecf20Sopenharmony_ci if (len != -ENODEV) 10388c2ecf20Sopenharmony_ci return len; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return sysfs_emit(buf, "platform:%s\n", pdev->name); 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev, 10458c2ecf20Sopenharmony_ci struct device_attribute *attr, 10468c2ecf20Sopenharmony_ci const char *buf, size_t count) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 10498c2ecf20Sopenharmony_ci int ret; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci ret = driver_set_override(dev, &pdev->driver_override, buf, count); 10528c2ecf20Sopenharmony_ci if (ret) 10538c2ecf20Sopenharmony_ci return ret; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci return count; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev, 10598c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 10628c2ecf20Sopenharmony_ci ssize_t len; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci device_lock(dev); 10658c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", pdev->driver_override); 10668c2ecf20Sopenharmony_ci device_unlock(dev); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci return len; 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic ssize_t numa_node_show(struct device *dev, 10738c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%d\n", dev_to_node(dev)); 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(numa_node); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cistatic umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a, 10808c2ecf20Sopenharmony_ci int n) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci struct device *dev = container_of(kobj, typeof(*dev), kobj); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (a == &dev_attr_numa_node.attr && 10858c2ecf20Sopenharmony_ci dev_to_node(dev) == NUMA_NO_NODE) 10868c2ecf20Sopenharmony_ci return 0; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci return a->mode; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic struct attribute *platform_dev_attrs[] = { 10928c2ecf20Sopenharmony_ci &dev_attr_modalias.attr, 10938c2ecf20Sopenharmony_ci &dev_attr_numa_node.attr, 10948c2ecf20Sopenharmony_ci &dev_attr_driver_override.attr, 10958c2ecf20Sopenharmony_ci NULL, 10968c2ecf20Sopenharmony_ci}; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic struct attribute_group platform_dev_group = { 10998c2ecf20Sopenharmony_ci .attrs = platform_dev_attrs, 11008c2ecf20Sopenharmony_ci .is_visible = platform_dev_attrs_visible, 11018c2ecf20Sopenharmony_ci}; 11028c2ecf20Sopenharmony_ci__ATTRIBUTE_GROUPS(platform_dev); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic int platform_uevent(struct device *dev, struct kobj_uevent_env *env) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 11078c2ecf20Sopenharmony_ci int rc; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* Some devices have extra OF data and an OF-style MODALIAS */ 11108c2ecf20Sopenharmony_ci rc = of_device_uevent_modalias(dev, env); 11118c2ecf20Sopenharmony_ci if (rc != -ENODEV) 11128c2ecf20Sopenharmony_ci return rc; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci rc = acpi_device_uevent_modalias(dev, env); 11158c2ecf20Sopenharmony_ci if (rc != -ENODEV) 11168c2ecf20Sopenharmony_ci return rc; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, 11198c2ecf20Sopenharmony_ci pdev->name); 11208c2ecf20Sopenharmony_ci return 0; 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cistatic const struct platform_device_id *platform_match_id( 11248c2ecf20Sopenharmony_ci const struct platform_device_id *id, 11258c2ecf20Sopenharmony_ci struct platform_device *pdev) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci while (id->name[0]) { 11288c2ecf20Sopenharmony_ci if (strcmp(pdev->name, id->name) == 0) { 11298c2ecf20Sopenharmony_ci pdev->id_entry = id; 11308c2ecf20Sopenharmony_ci return id; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci id++; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci return NULL; 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci/** 11388c2ecf20Sopenharmony_ci * platform_match - bind platform device to platform driver. 11398c2ecf20Sopenharmony_ci * @dev: device. 11408c2ecf20Sopenharmony_ci * @drv: driver. 11418c2ecf20Sopenharmony_ci * 11428c2ecf20Sopenharmony_ci * Platform device IDs are assumed to be encoded like this: 11438c2ecf20Sopenharmony_ci * "<name><instance>", where <name> is a short description of the type of 11448c2ecf20Sopenharmony_ci * device, like "pci" or "floppy", and <instance> is the enumerated 11458c2ecf20Sopenharmony_ci * instance of the device, like '0' or '42'. Driver IDs are simply 11468c2ecf20Sopenharmony_ci * "<name>". So, extract the <name> from the platform_device structure, 11478c2ecf20Sopenharmony_ci * and compare it against the name of the driver. Return whether they match 11488c2ecf20Sopenharmony_ci * or not. 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_cistatic int platform_match(struct device *dev, struct device_driver *drv) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 11538c2ecf20Sopenharmony_ci struct platform_driver *pdrv = to_platform_driver(drv); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* When driver_override is set, only bind to the matching driver */ 11568c2ecf20Sopenharmony_ci if (pdev->driver_override) 11578c2ecf20Sopenharmony_ci return !strcmp(pdev->driver_override, drv->name); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci /* Attempt an OF style match first */ 11608c2ecf20Sopenharmony_ci if (of_driver_match_device(dev, drv)) 11618c2ecf20Sopenharmony_ci return 1; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* Then try ACPI style match */ 11648c2ecf20Sopenharmony_ci if (acpi_driver_match_device(dev, drv)) 11658c2ecf20Sopenharmony_ci return 1; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci /* Then try to match against the id table */ 11688c2ecf20Sopenharmony_ci if (pdrv->id_table) 11698c2ecf20Sopenharmony_ci return platform_match_id(pdrv->id_table, pdev) != NULL; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci /* fall-back to driver name match */ 11728c2ecf20Sopenharmony_ci return (strcmp(pdev->name, drv->name) == 0); 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistatic int platform_legacy_suspend(struct device *dev, pm_message_t mesg) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci struct platform_driver *pdrv = to_platform_driver(dev->driver); 11808c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 11818c2ecf20Sopenharmony_ci int ret = 0; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (dev->driver && pdrv->suspend) 11848c2ecf20Sopenharmony_ci ret = pdrv->suspend(pdev, mesg); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return ret; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic int platform_legacy_resume(struct device *dev) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci struct platform_driver *pdrv = to_platform_driver(dev->driver); 11928c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 11938c2ecf20Sopenharmony_ci int ret = 0; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci if (dev->driver && pdrv->resume) 11968c2ecf20Sopenharmony_ci ret = pdrv->resume(pdev); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return ret; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ciint platform_pm_suspend(struct device *dev) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci struct device_driver *drv = dev->driver; 12088c2ecf20Sopenharmony_ci int ret = 0; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (!drv) 12118c2ecf20Sopenharmony_ci return 0; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if (drv->pm) { 12148c2ecf20Sopenharmony_ci if (drv->pm->suspend) 12158c2ecf20Sopenharmony_ci ret = drv->pm->suspend(dev); 12168c2ecf20Sopenharmony_ci } else { 12178c2ecf20Sopenharmony_ci ret = platform_legacy_suspend(dev, PMSG_SUSPEND); 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci return ret; 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ciint platform_pm_resume(struct device *dev) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci struct device_driver *drv = dev->driver; 12268c2ecf20Sopenharmony_ci int ret = 0; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (!drv) 12298c2ecf20Sopenharmony_ci return 0; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (drv->pm) { 12328c2ecf20Sopenharmony_ci if (drv->pm->resume) 12338c2ecf20Sopenharmony_ci ret = drv->pm->resume(dev); 12348c2ecf20Sopenharmony_ci } else { 12358c2ecf20Sopenharmony_ci ret = platform_legacy_resume(dev); 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci return ret; 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci#ifdef CONFIG_HIBERNATE_CALLBACKS 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ciint platform_pm_freeze(struct device *dev) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci struct device_driver *drv = dev->driver; 12488c2ecf20Sopenharmony_ci int ret = 0; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci if (!drv) 12518c2ecf20Sopenharmony_ci return 0; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (drv->pm) { 12548c2ecf20Sopenharmony_ci if (drv->pm->freeze) 12558c2ecf20Sopenharmony_ci ret = drv->pm->freeze(dev); 12568c2ecf20Sopenharmony_ci } else { 12578c2ecf20Sopenharmony_ci ret = platform_legacy_suspend(dev, PMSG_FREEZE); 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci return ret; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ciint platform_pm_thaw(struct device *dev) 12648c2ecf20Sopenharmony_ci{ 12658c2ecf20Sopenharmony_ci struct device_driver *drv = dev->driver; 12668c2ecf20Sopenharmony_ci int ret = 0; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci if (!drv) 12698c2ecf20Sopenharmony_ci return 0; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if (drv->pm) { 12728c2ecf20Sopenharmony_ci if (drv->pm->thaw) 12738c2ecf20Sopenharmony_ci ret = drv->pm->thaw(dev); 12748c2ecf20Sopenharmony_ci } else { 12758c2ecf20Sopenharmony_ci ret = platform_legacy_resume(dev); 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci return ret; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ciint platform_pm_poweroff(struct device *dev) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci struct device_driver *drv = dev->driver; 12848c2ecf20Sopenharmony_ci int ret = 0; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (!drv) 12878c2ecf20Sopenharmony_ci return 0; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (drv->pm) { 12908c2ecf20Sopenharmony_ci if (drv->pm->poweroff) 12918c2ecf20Sopenharmony_ci ret = drv->pm->poweroff(dev); 12928c2ecf20Sopenharmony_ci } else { 12938c2ecf20Sopenharmony_ci ret = platform_legacy_suspend(dev, PMSG_HIBERNATE); 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci return ret; 12978c2ecf20Sopenharmony_ci} 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ciint platform_pm_restore(struct device *dev) 13008c2ecf20Sopenharmony_ci{ 13018c2ecf20Sopenharmony_ci struct device_driver *drv = dev->driver; 13028c2ecf20Sopenharmony_ci int ret = 0; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (!drv) 13058c2ecf20Sopenharmony_ci return 0; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci if (drv->pm) { 13088c2ecf20Sopenharmony_ci if (drv->pm->restore) 13098c2ecf20Sopenharmony_ci ret = drv->pm->restore(dev); 13108c2ecf20Sopenharmony_ci } else { 13118c2ecf20Sopenharmony_ci ret = platform_legacy_resume(dev); 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci return ret; 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci#endif /* CONFIG_HIBERNATE_CALLBACKS */ 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ciint platform_dma_configure(struct device *dev) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci enum dev_dma_attr attr; 13228c2ecf20Sopenharmony_ci int ret = 0; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci if (dev->of_node) { 13258c2ecf20Sopenharmony_ci ret = of_dma_configure(dev, dev->of_node, true); 13268c2ecf20Sopenharmony_ci } else if (has_acpi_companion(dev)) { 13278c2ecf20Sopenharmony_ci attr = acpi_get_dma_attr(to_acpi_device_node(dev->fwnode)); 13288c2ecf20Sopenharmony_ci ret = acpi_dma_configure(dev, attr); 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci return ret; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_cistatic const struct dev_pm_ops platform_dev_pm_ops = { 13358c2ecf20Sopenharmony_ci .runtime_suspend = pm_generic_runtime_suspend, 13368c2ecf20Sopenharmony_ci .runtime_resume = pm_generic_runtime_resume, 13378c2ecf20Sopenharmony_ci USE_PLATFORM_PM_SLEEP_OPS 13388c2ecf20Sopenharmony_ci}; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistruct bus_type platform_bus_type = { 13418c2ecf20Sopenharmony_ci .name = "platform", 13428c2ecf20Sopenharmony_ci .dev_groups = platform_dev_groups, 13438c2ecf20Sopenharmony_ci .match = platform_match, 13448c2ecf20Sopenharmony_ci .uevent = platform_uevent, 13458c2ecf20Sopenharmony_ci .dma_configure = platform_dma_configure, 13468c2ecf20Sopenharmony_ci .pm = &platform_dev_pm_ops, 13478c2ecf20Sopenharmony_ci}; 13488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_bus_type); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_cistatic inline int __platform_match(struct device *dev, const void *drv) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci return platform_match(dev, (struct device_driver *)drv); 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci/** 13568c2ecf20Sopenharmony_ci * platform_find_device_by_driver - Find a platform device with a given 13578c2ecf20Sopenharmony_ci * driver. 13588c2ecf20Sopenharmony_ci * @start: The device to start the search from. 13598c2ecf20Sopenharmony_ci * @drv: The device driver to look for. 13608c2ecf20Sopenharmony_ci */ 13618c2ecf20Sopenharmony_cistruct device *platform_find_device_by_driver(struct device *start, 13628c2ecf20Sopenharmony_ci const struct device_driver *drv) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci return bus_find_device(&platform_bus_type, start, drv, 13658c2ecf20Sopenharmony_ci __platform_match); 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_find_device_by_driver); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_civoid __weak __init early_platform_cleanup(void) { } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ciint __init platform_bus_init(void) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci int error; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci early_platform_cleanup(); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci error = device_register(&platform_bus); 13788c2ecf20Sopenharmony_ci if (error) { 13798c2ecf20Sopenharmony_ci put_device(&platform_bus); 13808c2ecf20Sopenharmony_ci return error; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci error = bus_register(&platform_bus_type); 13838c2ecf20Sopenharmony_ci if (error) 13848c2ecf20Sopenharmony_ci device_unregister(&platform_bus); 13858c2ecf20Sopenharmony_ci of_platform_register_reconfig_notifier(); 13868c2ecf20Sopenharmony_ci return error; 13878c2ecf20Sopenharmony_ci} 1388