162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * platform.c - platform 'pseudo' bus for legacy devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2002-3 Patrick Mochel 662306a36Sopenharmony_ci * Copyright (c) 2002-3 Open Source Development Labs 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Please see Documentation/driver-api/driver-model/platform.rst for more 962306a36Sopenharmony_ci * information. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/of_device.h> 1562306a36Sopenharmony_ci#include <linux/of_irq.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/ioport.h> 2062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2162306a36Sopenharmony_ci#include <linux/memblock.h> 2262306a36Sopenharmony_ci#include <linux/err.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2562306a36Sopenharmony_ci#include <linux/pm_domain.h> 2662306a36Sopenharmony_ci#include <linux/idr.h> 2762306a36Sopenharmony_ci#include <linux/acpi.h> 2862306a36Sopenharmony_ci#include <linux/clk/clk-conf.h> 2962306a36Sopenharmony_ci#include <linux/limits.h> 3062306a36Sopenharmony_ci#include <linux/property.h> 3162306a36Sopenharmony_ci#include <linux/kmemleak.h> 3262306a36Sopenharmony_ci#include <linux/types.h> 3362306a36Sopenharmony_ci#include <linux/iommu.h> 3462306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "base.h" 3762306a36Sopenharmony_ci#include "power/power.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* For automatically allocated device IDs */ 4062306a36Sopenharmony_cistatic DEFINE_IDA(platform_devid_ida); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct device platform_bus = { 4362306a36Sopenharmony_ci .init_name = "platform", 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_bus); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * platform_get_resource - get a resource for a device 4962306a36Sopenharmony_ci * @dev: platform device 5062306a36Sopenharmony_ci * @type: resource type 5162306a36Sopenharmony_ci * @num: resource index 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Return: a pointer to the resource or NULL on failure. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistruct resource *platform_get_resource(struct platform_device *dev, 5662306a36Sopenharmony_ci unsigned int type, unsigned int num) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci u32 i; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci for (i = 0; i < dev->num_resources; i++) { 6162306a36Sopenharmony_ci struct resource *r = &dev->resource[i]; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (type == resource_type(r) && num-- == 0) 6462306a36Sopenharmony_ci return r; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci return NULL; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_resource); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct resource *platform_get_mem_or_io(struct platform_device *dev, 7162306a36Sopenharmony_ci unsigned int num) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u32 i; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci for (i = 0; i < dev->num_resources; i++) { 7662306a36Sopenharmony_ci struct resource *r = &dev->resource[i]; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if ((resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) && num-- == 0) 7962306a36Sopenharmony_ci return r; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci return NULL; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_mem_or_io); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#ifdef CONFIG_HAS_IOMEM 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * devm_platform_get_and_ioremap_resource - call devm_ioremap_resource() for a 8862306a36Sopenharmony_ci * platform device and get resource 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * @pdev: platform device to use both for memory resource lookup as well as 9162306a36Sopenharmony_ci * resource management 9262306a36Sopenharmony_ci * @index: resource index 9362306a36Sopenharmony_ci * @res: optional output parameter to store a pointer to the obtained resource. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 9662306a36Sopenharmony_ci * on failure. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_civoid __iomem * 9962306a36Sopenharmony_cidevm_platform_get_and_ioremap_resource(struct platform_device *pdev, 10062306a36Sopenharmony_ci unsigned int index, struct resource **res) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct resource *r; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, index); 10562306a36Sopenharmony_ci if (res) 10662306a36Sopenharmony_ci *res = r; 10762306a36Sopenharmony_ci return devm_ioremap_resource(&pdev->dev, r); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_platform_get_and_ioremap_resource); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/** 11262306a36Sopenharmony_ci * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform 11362306a36Sopenharmony_ci * device 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * @pdev: platform device to use both for memory resource lookup as well as 11662306a36Sopenharmony_ci * resource management 11762306a36Sopenharmony_ci * @index: resource index 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 12062306a36Sopenharmony_ci * on failure. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_civoid __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, 12362306a36Sopenharmony_ci unsigned int index) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return devm_platform_get_and_ioremap_resource(pdev, index, NULL); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * devm_platform_ioremap_resource_byname - call devm_ioremap_resource for 13162306a36Sopenharmony_ci * a platform device, retrieve the 13262306a36Sopenharmony_ci * resource by name 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * @pdev: platform device to use both for memory resource lookup as well as 13562306a36Sopenharmony_ci * resource management 13662306a36Sopenharmony_ci * @name: name of the resource 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci * Return: a pointer to the remapped memory or an ERR_PTR() encoded error code 13962306a36Sopenharmony_ci * on failure. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_civoid __iomem * 14262306a36Sopenharmony_cidevm_platform_ioremap_resource_byname(struct platform_device *pdev, 14362306a36Sopenharmony_ci const char *name) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct resource *res; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); 14862306a36Sopenharmony_ci return devm_ioremap_resource(&pdev->dev, res); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname); 15162306a36Sopenharmony_ci#endif /* CONFIG_HAS_IOMEM */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * platform_get_irq_optional - get an optional IRQ for a device 15562306a36Sopenharmony_ci * @dev: platform device 15662306a36Sopenharmony_ci * @num: IRQ number index 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Gets an IRQ for a platform device. Device drivers should check the return 15962306a36Sopenharmony_ci * value for errors so as to not pass a negative integer value to the 16062306a36Sopenharmony_ci * request_irq() APIs. This is the same as platform_get_irq(), except that it 16162306a36Sopenharmony_ci * does not print an error message if an IRQ can not be obtained. 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * For example:: 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * int irq = platform_get_irq_optional(pdev, 0); 16662306a36Sopenharmony_ci * if (irq < 0) 16762306a36Sopenharmony_ci * return irq; 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * Return: non-zero IRQ number on success, negative error number on failure. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ciint platform_get_irq_optional(struct platform_device *dev, unsigned int num) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int ret; 17462306a36Sopenharmony_ci#ifdef CONFIG_SPARC 17562306a36Sopenharmony_ci /* sparc does not have irqs represented as IORESOURCE_IRQ resources */ 17662306a36Sopenharmony_ci if (!dev || num >= dev->archdata.num_irqs) 17762306a36Sopenharmony_ci goto out_not_found; 17862306a36Sopenharmony_ci ret = dev->archdata.irqs[num]; 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci#else 18162306a36Sopenharmony_ci struct resource *r; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) { 18462306a36Sopenharmony_ci ret = of_irq_get(dev->dev.of_node, num); 18562306a36Sopenharmony_ci if (ret > 0 || ret == -EPROBE_DEFER) 18662306a36Sopenharmony_ci goto out; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci r = platform_get_resource(dev, IORESOURCE_IRQ, num); 19062306a36Sopenharmony_ci if (has_acpi_companion(&dev->dev)) { 19162306a36Sopenharmony_ci if (r && r->flags & IORESOURCE_DISABLED) { 19262306a36Sopenharmony_ci ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r); 19362306a36Sopenharmony_ci if (ret) 19462306a36Sopenharmony_ci goto out; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * The resources may pass trigger flags to the irqs that need 20062306a36Sopenharmony_ci * to be set up. It so happens that the trigger flags for 20162306a36Sopenharmony_ci * IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER* 20262306a36Sopenharmony_ci * settings. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci if (r && r->flags & IORESOURCE_BITS) { 20562306a36Sopenharmony_ci struct irq_data *irqd; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci irqd = irq_get_irq_data(r->start); 20862306a36Sopenharmony_ci if (!irqd) 20962306a36Sopenharmony_ci goto out_not_found; 21062306a36Sopenharmony_ci irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (r) { 21462306a36Sopenharmony_ci ret = r->start; 21562306a36Sopenharmony_ci goto out; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * For the index 0 interrupt, allow falling back to GpioInt 22062306a36Sopenharmony_ci * resources. While a device could have both Interrupt and GpioInt 22162306a36Sopenharmony_ci * resources, making this fallback ambiguous, in many common cases 22262306a36Sopenharmony_ci * the device will only expose one IRQ, and this fallback 22362306a36Sopenharmony_ci * allows a common code path across either kind of resource. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci if (num == 0 && has_acpi_companion(&dev->dev)) { 22662306a36Sopenharmony_ci ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); 22762306a36Sopenharmony_ci /* Our callers expect -ENXIO for missing IRQs. */ 22862306a36Sopenharmony_ci if (ret >= 0 || ret == -EPROBE_DEFER) 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#endif 23362306a36Sopenharmony_ciout_not_found: 23462306a36Sopenharmony_ci ret = -ENXIO; 23562306a36Sopenharmony_ciout: 23662306a36Sopenharmony_ci if (WARN(!ret, "0 is an invalid IRQ number\n")) 23762306a36Sopenharmony_ci return -EINVAL; 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_irq_optional); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/** 24362306a36Sopenharmony_ci * platform_get_irq - get an IRQ for a device 24462306a36Sopenharmony_ci * @dev: platform device 24562306a36Sopenharmony_ci * @num: IRQ number index 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Gets an IRQ for a platform device and prints an error message if finding the 24862306a36Sopenharmony_ci * IRQ fails. Device drivers should check the return value for errors so as to 24962306a36Sopenharmony_ci * not pass a negative integer value to the request_irq() APIs. 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * For example:: 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * int irq = platform_get_irq(pdev, 0); 25462306a36Sopenharmony_ci * if (irq < 0) 25562306a36Sopenharmony_ci * return irq; 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * Return: non-zero IRQ number on success, negative error number on failure. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ciint platform_get_irq(struct platform_device *dev, unsigned int num) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = platform_get_irq_optional(dev, num); 26462306a36Sopenharmony_ci if (ret < 0) 26562306a36Sopenharmony_ci return dev_err_probe(&dev->dev, ret, 26662306a36Sopenharmony_ci "IRQ index %u not found\n", num); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_irq); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/** 27362306a36Sopenharmony_ci * platform_irq_count - Count the number of IRQs a platform device uses 27462306a36Sopenharmony_ci * @dev: platform device 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * Return: Number of IRQs a platform device uses or EPROBE_DEFER 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ciint platform_irq_count(struct platform_device *dev) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci int ret, nr = 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci while ((ret = platform_get_irq_optional(dev, nr)) >= 0) 28362306a36Sopenharmony_ci nr++; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 28662306a36Sopenharmony_ci return ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return nr; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_irq_count); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistruct irq_affinity_devres { 29362306a36Sopenharmony_ci unsigned int count; 29462306a36Sopenharmony_ci unsigned int irq[]; 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void platform_disable_acpi_irq(struct platform_device *pdev, int index) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct resource *r; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_IRQ, index); 30262306a36Sopenharmony_ci if (r) 30362306a36Sopenharmony_ci irqresource_disabled(r, 0); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void devm_platform_get_irqs_affinity_release(struct device *dev, 30762306a36Sopenharmony_ci void *res) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct irq_affinity_devres *ptr = res; 31062306a36Sopenharmony_ci int i; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci for (i = 0; i < ptr->count; i++) { 31362306a36Sopenharmony_ci irq_dispose_mapping(ptr->irq[i]); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (has_acpi_companion(dev)) 31662306a36Sopenharmony_ci platform_disable_acpi_irq(to_platform_device(dev), i); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/** 32162306a36Sopenharmony_ci * devm_platform_get_irqs_affinity - devm method to get a set of IRQs for a 32262306a36Sopenharmony_ci * device using an interrupt affinity descriptor 32362306a36Sopenharmony_ci * @dev: platform device pointer 32462306a36Sopenharmony_ci * @affd: affinity descriptor 32562306a36Sopenharmony_ci * @minvec: minimum count of interrupt vectors 32662306a36Sopenharmony_ci * @maxvec: maximum count of interrupt vectors 32762306a36Sopenharmony_ci * @irqs: pointer holder for IRQ numbers 32862306a36Sopenharmony_ci * 32962306a36Sopenharmony_ci * Gets a set of IRQs for a platform device, and updates IRQ afffinty according 33062306a36Sopenharmony_ci * to the passed affinity descriptor 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * Return: Number of vectors on success, negative error number on failure. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ciint devm_platform_get_irqs_affinity(struct platform_device *dev, 33562306a36Sopenharmony_ci struct irq_affinity *affd, 33662306a36Sopenharmony_ci unsigned int minvec, 33762306a36Sopenharmony_ci unsigned int maxvec, 33862306a36Sopenharmony_ci int **irqs) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct irq_affinity_devres *ptr; 34162306a36Sopenharmony_ci struct irq_affinity_desc *desc; 34262306a36Sopenharmony_ci size_t size; 34362306a36Sopenharmony_ci int i, ret, nvec; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!affd) 34662306a36Sopenharmony_ci return -EPERM; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (maxvec < minvec) 34962306a36Sopenharmony_ci return -ERANGE; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci nvec = platform_irq_count(dev); 35262306a36Sopenharmony_ci if (nvec < 0) 35362306a36Sopenharmony_ci return nvec; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (nvec < minvec) 35662306a36Sopenharmony_ci return -ENOSPC; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci nvec = irq_calc_affinity_vectors(minvec, nvec, affd); 35962306a36Sopenharmony_ci if (nvec < minvec) 36062306a36Sopenharmony_ci return -ENOSPC; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (nvec > maxvec) 36362306a36Sopenharmony_ci nvec = maxvec; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci size = sizeof(*ptr) + sizeof(unsigned int) * nvec; 36662306a36Sopenharmony_ci ptr = devres_alloc(devm_platform_get_irqs_affinity_release, size, 36762306a36Sopenharmony_ci GFP_KERNEL); 36862306a36Sopenharmony_ci if (!ptr) 36962306a36Sopenharmony_ci return -ENOMEM; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ptr->count = nvec; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci for (i = 0; i < nvec; i++) { 37462306a36Sopenharmony_ci int irq = platform_get_irq(dev, i); 37562306a36Sopenharmony_ci if (irq < 0) { 37662306a36Sopenharmony_ci ret = irq; 37762306a36Sopenharmony_ci goto err_free_devres; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci ptr->irq[i] = irq; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci desc = irq_create_affinity_masks(nvec, affd); 38362306a36Sopenharmony_ci if (!desc) { 38462306a36Sopenharmony_ci ret = -ENOMEM; 38562306a36Sopenharmony_ci goto err_free_devres; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci for (i = 0; i < nvec; i++) { 38962306a36Sopenharmony_ci ret = irq_update_affinity_desc(ptr->irq[i], &desc[i]); 39062306a36Sopenharmony_ci if (ret) { 39162306a36Sopenharmony_ci dev_err(&dev->dev, "failed to update irq%d affinity descriptor (%d)\n", 39262306a36Sopenharmony_ci ptr->irq[i], ret); 39362306a36Sopenharmony_ci goto err_free_desc; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci devres_add(&dev->dev, ptr); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci kfree(desc); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci *irqs = ptr->irq; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return nvec; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cierr_free_desc: 40662306a36Sopenharmony_ci kfree(desc); 40762306a36Sopenharmony_cierr_free_devres: 40862306a36Sopenharmony_ci devres_free(ptr); 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_platform_get_irqs_affinity); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/** 41462306a36Sopenharmony_ci * platform_get_resource_byname - get a resource for a device by name 41562306a36Sopenharmony_ci * @dev: platform device 41662306a36Sopenharmony_ci * @type: resource type 41762306a36Sopenharmony_ci * @name: resource name 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_cistruct resource *platform_get_resource_byname(struct platform_device *dev, 42062306a36Sopenharmony_ci unsigned int type, 42162306a36Sopenharmony_ci const char *name) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci u32 i; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci for (i = 0; i < dev->num_resources; i++) { 42662306a36Sopenharmony_ci struct resource *r = &dev->resource[i]; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (unlikely(!r->name)) 42962306a36Sopenharmony_ci continue; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (type == resource_type(r) && !strcmp(r->name, name)) 43262306a36Sopenharmony_ci return r; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci return NULL; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_resource_byname); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int __platform_get_irq_byname(struct platform_device *dev, 43962306a36Sopenharmony_ci const char *name) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct resource *r; 44262306a36Sopenharmony_ci int ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ret = fwnode_irq_get_byname(dev_fwnode(&dev->dev), name); 44562306a36Sopenharmony_ci if (ret > 0 || ret == -EPROBE_DEFER) 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); 44962306a36Sopenharmony_ci if (r) { 45062306a36Sopenharmony_ci if (WARN(!r->start, "0 is an invalid IRQ number\n")) 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci return r->start; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return -ENXIO; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci/** 45962306a36Sopenharmony_ci * platform_get_irq_byname - get an IRQ for a device by name 46062306a36Sopenharmony_ci * @dev: platform device 46162306a36Sopenharmony_ci * @name: IRQ name 46262306a36Sopenharmony_ci * 46362306a36Sopenharmony_ci * Get an IRQ like platform_get_irq(), but then by name rather then by index. 46462306a36Sopenharmony_ci * 46562306a36Sopenharmony_ci * Return: non-zero IRQ number on success, negative error number on failure. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ciint platform_get_irq_byname(struct platform_device *dev, const char *name) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci int ret; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ret = __platform_get_irq_byname(dev, name); 47262306a36Sopenharmony_ci if (ret < 0) 47362306a36Sopenharmony_ci return dev_err_probe(&dev->dev, ret, "IRQ %s not found\n", 47462306a36Sopenharmony_ci name); 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_irq_byname); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/** 48062306a36Sopenharmony_ci * platform_get_irq_byname_optional - get an optional IRQ for a device by name 48162306a36Sopenharmony_ci * @dev: platform device 48262306a36Sopenharmony_ci * @name: IRQ name 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * Get an optional IRQ by name like platform_get_irq_byname(). Except that it 48562306a36Sopenharmony_ci * does not print an error message if an IRQ can not be obtained. 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * Return: non-zero IRQ number on success, negative error number on failure. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ciint platform_get_irq_byname_optional(struct platform_device *dev, 49062306a36Sopenharmony_ci const char *name) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci return __platform_get_irq_byname(dev, name); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_get_irq_byname_optional); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/** 49762306a36Sopenharmony_ci * platform_add_devices - add a numbers of platform devices 49862306a36Sopenharmony_ci * @devs: array of platform devices to add 49962306a36Sopenharmony_ci * @num: number of platform devices in array 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * Return: 0 on success, negative error number on failure. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ciint platform_add_devices(struct platform_device **devs, int num) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci int i, ret = 0; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci for (i = 0; i < num; i++) { 50862306a36Sopenharmony_ci ret = platform_device_register(devs[i]); 50962306a36Sopenharmony_ci if (ret) { 51062306a36Sopenharmony_ci while (--i >= 0) 51162306a36Sopenharmony_ci platform_device_unregister(devs[i]); 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return ret; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_add_devices); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistruct platform_object { 52162306a36Sopenharmony_ci struct platform_device pdev; 52262306a36Sopenharmony_ci char name[]; 52362306a36Sopenharmony_ci}; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci/* 52662306a36Sopenharmony_ci * Set up default DMA mask for platform devices if the they weren't 52762306a36Sopenharmony_ci * previously set by the architecture / DT. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_cistatic void setup_pdev_dma_masks(struct platform_device *pdev) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci pdev->dev.dma_parms = &pdev->dma_parms; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (!pdev->dev.coherent_dma_mask) 53462306a36Sopenharmony_ci pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 53562306a36Sopenharmony_ci if (!pdev->dev.dma_mask) { 53662306a36Sopenharmony_ci pdev->platform_dma_mask = DMA_BIT_MASK(32); 53762306a36Sopenharmony_ci pdev->dev.dma_mask = &pdev->platform_dma_mask; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci}; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci/** 54262306a36Sopenharmony_ci * platform_device_put - destroy a platform device 54362306a36Sopenharmony_ci * @pdev: platform device to free 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * Free all memory associated with a platform device. This function must 54662306a36Sopenharmony_ci * _only_ be externally called in error cases. All other usage is a bug. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_civoid platform_device_put(struct platform_device *pdev) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(pdev)) 55162306a36Sopenharmony_ci put_device(&pdev->dev); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_put); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic void platform_device_release(struct device *dev) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct platform_object *pa = container_of(dev, struct platform_object, 55862306a36Sopenharmony_ci pdev.dev); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci of_node_put(pa->pdev.dev.of_node); 56162306a36Sopenharmony_ci kfree(pa->pdev.dev.platform_data); 56262306a36Sopenharmony_ci kfree(pa->pdev.mfd_cell); 56362306a36Sopenharmony_ci kfree(pa->pdev.resource); 56462306a36Sopenharmony_ci kfree(pa->pdev.driver_override); 56562306a36Sopenharmony_ci kfree(pa); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci/** 56962306a36Sopenharmony_ci * platform_device_alloc - create a platform device 57062306a36Sopenharmony_ci * @name: base name of the device we're adding 57162306a36Sopenharmony_ci * @id: instance id 57262306a36Sopenharmony_ci * 57362306a36Sopenharmony_ci * Create a platform device object which can have other objects attached 57462306a36Sopenharmony_ci * to it, and which will have attached objects freed when it is released. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_cistruct platform_device *platform_device_alloc(const char *name, int id) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct platform_object *pa; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci pa = kzalloc(sizeof(*pa) + strlen(name) + 1, GFP_KERNEL); 58162306a36Sopenharmony_ci if (pa) { 58262306a36Sopenharmony_ci strcpy(pa->name, name); 58362306a36Sopenharmony_ci pa->pdev.name = pa->name; 58462306a36Sopenharmony_ci pa->pdev.id = id; 58562306a36Sopenharmony_ci device_initialize(&pa->pdev.dev); 58662306a36Sopenharmony_ci pa->pdev.dev.release = platform_device_release; 58762306a36Sopenharmony_ci setup_pdev_dma_masks(&pa->pdev); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return pa ? &pa->pdev : NULL; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_alloc); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci/** 59562306a36Sopenharmony_ci * platform_device_add_resources - add resources to a platform device 59662306a36Sopenharmony_ci * @pdev: platform device allocated by platform_device_alloc to add resources to 59762306a36Sopenharmony_ci * @res: set of resources that needs to be allocated for the device 59862306a36Sopenharmony_ci * @num: number of resources 59962306a36Sopenharmony_ci * 60062306a36Sopenharmony_ci * Add a copy of the resources to the platform device. The memory 60162306a36Sopenharmony_ci * associated with the resources will be freed when the platform device is 60262306a36Sopenharmony_ci * released. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ciint platform_device_add_resources(struct platform_device *pdev, 60562306a36Sopenharmony_ci const struct resource *res, unsigned int num) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct resource *r = NULL; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (res) { 61062306a36Sopenharmony_ci r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL); 61162306a36Sopenharmony_ci if (!r) 61262306a36Sopenharmony_ci return -ENOMEM; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci kfree(pdev->resource); 61662306a36Sopenharmony_ci pdev->resource = r; 61762306a36Sopenharmony_ci pdev->num_resources = num; 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_add_resources); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci/** 62362306a36Sopenharmony_ci * platform_device_add_data - add platform-specific data to a platform device 62462306a36Sopenharmony_ci * @pdev: platform device allocated by platform_device_alloc to add resources to 62562306a36Sopenharmony_ci * @data: platform specific data for this platform device 62662306a36Sopenharmony_ci * @size: size of platform specific data 62762306a36Sopenharmony_ci * 62862306a36Sopenharmony_ci * Add a copy of platform specific data to the platform device's 62962306a36Sopenharmony_ci * platform_data pointer. The memory associated with the platform data 63062306a36Sopenharmony_ci * will be freed when the platform device is released. 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ciint platform_device_add_data(struct platform_device *pdev, const void *data, 63362306a36Sopenharmony_ci size_t size) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci void *d = NULL; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (data) { 63862306a36Sopenharmony_ci d = kmemdup(data, size, GFP_KERNEL); 63962306a36Sopenharmony_ci if (!d) 64062306a36Sopenharmony_ci return -ENOMEM; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci kfree(pdev->dev.platform_data); 64462306a36Sopenharmony_ci pdev->dev.platform_data = d; 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_add_data); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci/** 65062306a36Sopenharmony_ci * platform_device_add - add a platform device to device hierarchy 65162306a36Sopenharmony_ci * @pdev: platform device we're adding 65262306a36Sopenharmony_ci * 65362306a36Sopenharmony_ci * This is part 2 of platform_device_register(), though may be called 65462306a36Sopenharmony_ci * separately _iff_ pdev was allocated by platform_device_alloc(). 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_ciint platform_device_add(struct platform_device *pdev) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci u32 i; 65962306a36Sopenharmony_ci int ret; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (!pdev) 66262306a36Sopenharmony_ci return -EINVAL; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (!pdev->dev.parent) 66562306a36Sopenharmony_ci pdev->dev.parent = &platform_bus; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci pdev->dev.bus = &platform_bus_type; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci switch (pdev->id) { 67062306a36Sopenharmony_ci default: 67162306a36Sopenharmony_ci dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case PLATFORM_DEVID_NONE: 67462306a36Sopenharmony_ci dev_set_name(&pdev->dev, "%s", pdev->name); 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci case PLATFORM_DEVID_AUTO: 67762306a36Sopenharmony_ci /* 67862306a36Sopenharmony_ci * Automatically allocated device ID. We mark it as such so 67962306a36Sopenharmony_ci * that we remember it must be freed, and we append a suffix 68062306a36Sopenharmony_ci * to avoid namespace collision with explicit IDs. 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_ci ret = ida_alloc(&platform_devid_ida, GFP_KERNEL); 68362306a36Sopenharmony_ci if (ret < 0) 68462306a36Sopenharmony_ci goto err_out; 68562306a36Sopenharmony_ci pdev->id = ret; 68662306a36Sopenharmony_ci pdev->id_auto = true; 68762306a36Sopenharmony_ci dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci for (i = 0; i < pdev->num_resources; i++) { 69262306a36Sopenharmony_ci struct resource *p, *r = &pdev->resource[i]; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (r->name == NULL) 69562306a36Sopenharmony_ci r->name = dev_name(&pdev->dev); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci p = r->parent; 69862306a36Sopenharmony_ci if (!p) { 69962306a36Sopenharmony_ci if (resource_type(r) == IORESOURCE_MEM) 70062306a36Sopenharmony_ci p = &iomem_resource; 70162306a36Sopenharmony_ci else if (resource_type(r) == IORESOURCE_IO) 70262306a36Sopenharmony_ci p = &ioport_resource; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (p) { 70662306a36Sopenharmony_ci ret = insert_resource(p, r); 70762306a36Sopenharmony_ci if (ret) { 70862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r); 70962306a36Sopenharmony_ci goto failed; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci pr_debug("Registering platform device '%s'. Parent at %s\n", 71562306a36Sopenharmony_ci dev_name(&pdev->dev), dev_name(pdev->dev.parent)); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci ret = device_add(&pdev->dev); 71862306a36Sopenharmony_ci if (ret == 0) 71962306a36Sopenharmony_ci return ret; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci failed: 72262306a36Sopenharmony_ci if (pdev->id_auto) { 72362306a36Sopenharmony_ci ida_free(&platform_devid_ida, pdev->id); 72462306a36Sopenharmony_ci pdev->id = PLATFORM_DEVID_AUTO; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci while (i--) { 72862306a36Sopenharmony_ci struct resource *r = &pdev->resource[i]; 72962306a36Sopenharmony_ci if (r->parent) 73062306a36Sopenharmony_ci release_resource(r); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci err_out: 73462306a36Sopenharmony_ci return ret; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_add); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci/** 73962306a36Sopenharmony_ci * platform_device_del - remove a platform-level device 74062306a36Sopenharmony_ci * @pdev: platform device we're removing 74162306a36Sopenharmony_ci * 74262306a36Sopenharmony_ci * Note that this function will also release all memory- and port-based 74362306a36Sopenharmony_ci * resources owned by the device (@dev->resource). This function must 74462306a36Sopenharmony_ci * _only_ be externally called in error cases. All other usage is a bug. 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_civoid platform_device_del(struct platform_device *pdev) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci u32 i; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(pdev)) { 75162306a36Sopenharmony_ci device_del(&pdev->dev); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (pdev->id_auto) { 75462306a36Sopenharmony_ci ida_free(&platform_devid_ida, pdev->id); 75562306a36Sopenharmony_ci pdev->id = PLATFORM_DEVID_AUTO; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci for (i = 0; i < pdev->num_resources; i++) { 75962306a36Sopenharmony_ci struct resource *r = &pdev->resource[i]; 76062306a36Sopenharmony_ci if (r->parent) 76162306a36Sopenharmony_ci release_resource(r); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_del); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/** 76862306a36Sopenharmony_ci * platform_device_register - add a platform-level device 76962306a36Sopenharmony_ci * @pdev: platform device we're adding 77062306a36Sopenharmony_ci * 77162306a36Sopenharmony_ci * NOTE: _Never_ directly free @pdev after calling this function, even if it 77262306a36Sopenharmony_ci * returned an error! Always use platform_device_put() to give up the 77362306a36Sopenharmony_ci * reference initialised in this function instead. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ciint platform_device_register(struct platform_device *pdev) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci device_initialize(&pdev->dev); 77862306a36Sopenharmony_ci setup_pdev_dma_masks(pdev); 77962306a36Sopenharmony_ci return platform_device_add(pdev); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_register); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci/** 78462306a36Sopenharmony_ci * platform_device_unregister - unregister a platform-level device 78562306a36Sopenharmony_ci * @pdev: platform device we're unregistering 78662306a36Sopenharmony_ci * 78762306a36Sopenharmony_ci * Unregistration is done in 2 steps. First we release all resources 78862306a36Sopenharmony_ci * and remove it from the subsystem, then we drop reference count by 78962306a36Sopenharmony_ci * calling platform_device_put(). 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_civoid platform_device_unregister(struct platform_device *pdev) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci platform_device_del(pdev); 79462306a36Sopenharmony_ci platform_device_put(pdev); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_unregister); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci/** 79962306a36Sopenharmony_ci * platform_device_register_full - add a platform-level device with 80062306a36Sopenharmony_ci * resources and platform-specific data 80162306a36Sopenharmony_ci * 80262306a36Sopenharmony_ci * @pdevinfo: data used to create device 80362306a36Sopenharmony_ci * 80462306a36Sopenharmony_ci * Returns &struct platform_device pointer on success, or ERR_PTR() on error. 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_cistruct platform_device *platform_device_register_full( 80762306a36Sopenharmony_ci const struct platform_device_info *pdevinfo) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci int ret; 81062306a36Sopenharmony_ci struct platform_device *pdev; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id); 81362306a36Sopenharmony_ci if (!pdev) 81462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci pdev->dev.parent = pdevinfo->parent; 81762306a36Sopenharmony_ci pdev->dev.fwnode = pdevinfo->fwnode; 81862306a36Sopenharmony_ci pdev->dev.of_node = of_node_get(to_of_node(pdev->dev.fwnode)); 81962306a36Sopenharmony_ci pdev->dev.of_node_reused = pdevinfo->of_node_reused; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (pdevinfo->dma_mask) { 82262306a36Sopenharmony_ci pdev->platform_dma_mask = pdevinfo->dma_mask; 82362306a36Sopenharmony_ci pdev->dev.dma_mask = &pdev->platform_dma_mask; 82462306a36Sopenharmony_ci pdev->dev.coherent_dma_mask = pdevinfo->dma_mask; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = platform_device_add_resources(pdev, 82862306a36Sopenharmony_ci pdevinfo->res, pdevinfo->num_res); 82962306a36Sopenharmony_ci if (ret) 83062306a36Sopenharmony_ci goto err; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci ret = platform_device_add_data(pdev, 83362306a36Sopenharmony_ci pdevinfo->data, pdevinfo->size_data); 83462306a36Sopenharmony_ci if (ret) 83562306a36Sopenharmony_ci goto err; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (pdevinfo->properties) { 83862306a36Sopenharmony_ci ret = device_create_managed_software_node(&pdev->dev, 83962306a36Sopenharmony_ci pdevinfo->properties, NULL); 84062306a36Sopenharmony_ci if (ret) 84162306a36Sopenharmony_ci goto err; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci ret = platform_device_add(pdev); 84562306a36Sopenharmony_ci if (ret) { 84662306a36Sopenharmony_cierr: 84762306a36Sopenharmony_ci ACPI_COMPANION_SET(&pdev->dev, NULL); 84862306a36Sopenharmony_ci platform_device_put(pdev); 84962306a36Sopenharmony_ci return ERR_PTR(ret); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci return pdev; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_device_register_full); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci/** 85762306a36Sopenharmony_ci * __platform_driver_register - register a driver for platform-level devices 85862306a36Sopenharmony_ci * @drv: platform driver structure 85962306a36Sopenharmony_ci * @owner: owning module/driver 86062306a36Sopenharmony_ci */ 86162306a36Sopenharmony_ciint __platform_driver_register(struct platform_driver *drv, 86262306a36Sopenharmony_ci struct module *owner) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci drv->driver.owner = owner; 86562306a36Sopenharmony_ci drv->driver.bus = &platform_bus_type; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return driver_register(&drv->driver); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__platform_driver_register); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/** 87262306a36Sopenharmony_ci * platform_driver_unregister - unregister a driver for platform-level devices 87362306a36Sopenharmony_ci * @drv: platform driver structure 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_civoid platform_driver_unregister(struct platform_driver *drv) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci driver_unregister(&drv->driver); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_driver_unregister); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int platform_probe_fail(struct platform_device *pdev) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci return -ENXIO; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic int is_bound_to_driver(struct device *dev, void *driver) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci if (dev->driver == driver) 88962306a36Sopenharmony_ci return 1; 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/** 89462306a36Sopenharmony_ci * __platform_driver_probe - register driver for non-hotpluggable device 89562306a36Sopenharmony_ci * @drv: platform driver structure 89662306a36Sopenharmony_ci * @probe: the driver probe routine, probably from an __init section 89762306a36Sopenharmony_ci * @module: module which will be the owner of the driver 89862306a36Sopenharmony_ci * 89962306a36Sopenharmony_ci * Use this instead of platform_driver_register() when you know the device 90062306a36Sopenharmony_ci * is not hotpluggable and has already been registered, and you want to 90162306a36Sopenharmony_ci * remove its run-once probe() infrastructure from memory after the driver 90262306a36Sopenharmony_ci * has bound to the device. 90362306a36Sopenharmony_ci * 90462306a36Sopenharmony_ci * One typical use for this would be with drivers for controllers integrated 90562306a36Sopenharmony_ci * into system-on-chip processors, where the controller devices have been 90662306a36Sopenharmony_ci * configured as part of board setup. 90762306a36Sopenharmony_ci * 90862306a36Sopenharmony_ci * Note that this is incompatible with deferred probing. 90962306a36Sopenharmony_ci * 91062306a36Sopenharmony_ci * Returns zero if the driver registered and bound to a device, else returns 91162306a36Sopenharmony_ci * a negative error code and with the driver not registered. 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ciint __init_or_module __platform_driver_probe(struct platform_driver *drv, 91462306a36Sopenharmony_ci int (*probe)(struct platform_device *), struct module *module) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci int retval; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (drv->driver.probe_type == PROBE_PREFER_ASYNCHRONOUS) { 91962306a36Sopenharmony_ci pr_err("%s: drivers registered with %s can not be probed asynchronously\n", 92062306a36Sopenharmony_ci drv->driver.name, __func__); 92162306a36Sopenharmony_ci return -EINVAL; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* 92562306a36Sopenharmony_ci * We have to run our probes synchronously because we check if 92662306a36Sopenharmony_ci * we find any devices to bind to and exit with error if there 92762306a36Sopenharmony_ci * are any. 92862306a36Sopenharmony_ci */ 92962306a36Sopenharmony_ci drv->driver.probe_type = PROBE_FORCE_SYNCHRONOUS; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* 93262306a36Sopenharmony_ci * Prevent driver from requesting probe deferral to avoid further 93362306a36Sopenharmony_ci * futile probe attempts. 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_ci drv->prevent_deferred_probe = true; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* make sure driver won't have bind/unbind attributes */ 93862306a36Sopenharmony_ci drv->driver.suppress_bind_attrs = true; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* temporary section violation during probe() */ 94162306a36Sopenharmony_ci drv->probe = probe; 94262306a36Sopenharmony_ci retval = __platform_driver_register(drv, module); 94362306a36Sopenharmony_ci if (retval) 94462306a36Sopenharmony_ci return retval; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* Force all new probes of this driver to fail */ 94762306a36Sopenharmony_ci drv->probe = platform_probe_fail; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* Walk all platform devices and see if any actually bound to this driver. 95062306a36Sopenharmony_ci * If not, return an error as the device should have done so by now. 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_ci if (!bus_for_each_dev(&platform_bus_type, NULL, &drv->driver, is_bound_to_driver)) { 95362306a36Sopenharmony_ci retval = -ENODEV; 95462306a36Sopenharmony_ci platform_driver_unregister(drv); 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci return retval; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__platform_driver_probe); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/** 96262306a36Sopenharmony_ci * __platform_create_bundle - register driver and create corresponding device 96362306a36Sopenharmony_ci * @driver: platform driver structure 96462306a36Sopenharmony_ci * @probe: the driver probe routine, probably from an __init section 96562306a36Sopenharmony_ci * @res: set of resources that needs to be allocated for the device 96662306a36Sopenharmony_ci * @n_res: number of resources 96762306a36Sopenharmony_ci * @data: platform specific data for this platform device 96862306a36Sopenharmony_ci * @size: size of platform specific data 96962306a36Sopenharmony_ci * @module: module which will be the owner of the driver 97062306a36Sopenharmony_ci * 97162306a36Sopenharmony_ci * Use this in legacy-style modules that probe hardware directly and 97262306a36Sopenharmony_ci * register a single platform device and corresponding platform driver. 97362306a36Sopenharmony_ci * 97462306a36Sopenharmony_ci * Returns &struct platform_device pointer on success, or ERR_PTR() on error. 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_cistruct platform_device * __init_or_module __platform_create_bundle( 97762306a36Sopenharmony_ci struct platform_driver *driver, 97862306a36Sopenharmony_ci int (*probe)(struct platform_device *), 97962306a36Sopenharmony_ci struct resource *res, unsigned int n_res, 98062306a36Sopenharmony_ci const void *data, size_t size, struct module *module) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct platform_device *pdev; 98362306a36Sopenharmony_ci int error; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci pdev = platform_device_alloc(driver->driver.name, -1); 98662306a36Sopenharmony_ci if (!pdev) { 98762306a36Sopenharmony_ci error = -ENOMEM; 98862306a36Sopenharmony_ci goto err_out; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci error = platform_device_add_resources(pdev, res, n_res); 99262306a36Sopenharmony_ci if (error) 99362306a36Sopenharmony_ci goto err_pdev_put; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci error = platform_device_add_data(pdev, data, size); 99662306a36Sopenharmony_ci if (error) 99762306a36Sopenharmony_ci goto err_pdev_put; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci error = platform_device_add(pdev); 100062306a36Sopenharmony_ci if (error) 100162306a36Sopenharmony_ci goto err_pdev_put; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci error = __platform_driver_probe(driver, probe, module); 100462306a36Sopenharmony_ci if (error) 100562306a36Sopenharmony_ci goto err_pdev_del; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci return pdev; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cierr_pdev_del: 101062306a36Sopenharmony_ci platform_device_del(pdev); 101162306a36Sopenharmony_cierr_pdev_put: 101262306a36Sopenharmony_ci platform_device_put(pdev); 101362306a36Sopenharmony_cierr_out: 101462306a36Sopenharmony_ci return ERR_PTR(error); 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__platform_create_bundle); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci/** 101962306a36Sopenharmony_ci * __platform_register_drivers - register an array of platform drivers 102062306a36Sopenharmony_ci * @drivers: an array of drivers to register 102162306a36Sopenharmony_ci * @count: the number of drivers to register 102262306a36Sopenharmony_ci * @owner: module owning the drivers 102362306a36Sopenharmony_ci * 102462306a36Sopenharmony_ci * Registers platform drivers specified by an array. On failure to register a 102562306a36Sopenharmony_ci * driver, all previously registered drivers will be unregistered. Callers of 102662306a36Sopenharmony_ci * this API should use platform_unregister_drivers() to unregister drivers in 102762306a36Sopenharmony_ci * the reverse order. 102862306a36Sopenharmony_ci * 102962306a36Sopenharmony_ci * Returns: 0 on success or a negative error code on failure. 103062306a36Sopenharmony_ci */ 103162306a36Sopenharmony_ciint __platform_register_drivers(struct platform_driver * const *drivers, 103262306a36Sopenharmony_ci unsigned int count, struct module *owner) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci unsigned int i; 103562306a36Sopenharmony_ci int err; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci for (i = 0; i < count; i++) { 103862306a36Sopenharmony_ci pr_debug("registering platform driver %ps\n", drivers[i]); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci err = __platform_driver_register(drivers[i], owner); 104162306a36Sopenharmony_ci if (err < 0) { 104262306a36Sopenharmony_ci pr_err("failed to register platform driver %ps: %d\n", 104362306a36Sopenharmony_ci drivers[i], err); 104462306a36Sopenharmony_ci goto error; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return 0; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cierror: 105162306a36Sopenharmony_ci while (i--) { 105262306a36Sopenharmony_ci pr_debug("unregistering platform driver %ps\n", drivers[i]); 105362306a36Sopenharmony_ci platform_driver_unregister(drivers[i]); 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return err; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__platform_register_drivers); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci/** 106162306a36Sopenharmony_ci * platform_unregister_drivers - unregister an array of platform drivers 106262306a36Sopenharmony_ci * @drivers: an array of drivers to unregister 106362306a36Sopenharmony_ci * @count: the number of drivers to unregister 106462306a36Sopenharmony_ci * 106562306a36Sopenharmony_ci * Unregisters platform drivers specified by an array. This is typically used 106662306a36Sopenharmony_ci * to complement an earlier call to platform_register_drivers(). Drivers are 106762306a36Sopenharmony_ci * unregistered in the reverse order in which they were registered. 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_civoid platform_unregister_drivers(struct platform_driver * const *drivers, 107062306a36Sopenharmony_ci unsigned int count) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci while (count--) { 107362306a36Sopenharmony_ci pr_debug("unregistering platform driver %ps\n", drivers[count]); 107462306a36Sopenharmony_ci platform_driver_unregister(drivers[count]); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_unregister_drivers); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic const struct platform_device_id *platform_match_id( 108062306a36Sopenharmony_ci const struct platform_device_id *id, 108162306a36Sopenharmony_ci struct platform_device *pdev) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci while (id->name[0]) { 108462306a36Sopenharmony_ci if (strcmp(pdev->name, id->name) == 0) { 108562306a36Sopenharmony_ci pdev->id_entry = id; 108662306a36Sopenharmony_ci return id; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci id++; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci return NULL; 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic int platform_legacy_suspend(struct device *dev, pm_message_t mesg) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct platform_driver *pdrv = to_platform_driver(dev->driver); 109862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 109962306a36Sopenharmony_ci int ret = 0; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (dev->driver && pdrv->suspend) 110262306a36Sopenharmony_ci ret = pdrv->suspend(pdev, mesg); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci return ret; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic int platform_legacy_resume(struct device *dev) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct platform_driver *pdrv = to_platform_driver(dev->driver); 111062306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 111162306a36Sopenharmony_ci int ret = 0; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (dev->driver && pdrv->resume) 111462306a36Sopenharmony_ci ret = pdrv->resume(pdev); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return ret; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ciint platform_pm_suspend(struct device *dev) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct device_driver *drv = dev->driver; 112662306a36Sopenharmony_ci int ret = 0; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (!drv) 112962306a36Sopenharmony_ci return 0; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (drv->pm) { 113262306a36Sopenharmony_ci if (drv->pm->suspend) 113362306a36Sopenharmony_ci ret = drv->pm->suspend(dev); 113462306a36Sopenharmony_ci } else { 113562306a36Sopenharmony_ci ret = platform_legacy_suspend(dev, PMSG_SUSPEND); 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci return ret; 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ciint platform_pm_resume(struct device *dev) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct device_driver *drv = dev->driver; 114462306a36Sopenharmony_ci int ret = 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (!drv) 114762306a36Sopenharmony_ci return 0; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (drv->pm) { 115062306a36Sopenharmony_ci if (drv->pm->resume) 115162306a36Sopenharmony_ci ret = drv->pm->resume(dev); 115262306a36Sopenharmony_ci } else { 115362306a36Sopenharmony_ci ret = platform_legacy_resume(dev); 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci return ret; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci#ifdef CONFIG_HIBERNATE_CALLBACKS 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ciint platform_pm_freeze(struct device *dev) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct device_driver *drv = dev->driver; 116662306a36Sopenharmony_ci int ret = 0; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (!drv) 116962306a36Sopenharmony_ci return 0; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (drv->pm) { 117262306a36Sopenharmony_ci if (drv->pm->freeze) 117362306a36Sopenharmony_ci ret = drv->pm->freeze(dev); 117462306a36Sopenharmony_ci } else { 117562306a36Sopenharmony_ci ret = platform_legacy_suspend(dev, PMSG_FREEZE); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci return ret; 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ciint platform_pm_thaw(struct device *dev) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci struct device_driver *drv = dev->driver; 118462306a36Sopenharmony_ci int ret = 0; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (!drv) 118762306a36Sopenharmony_ci return 0; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (drv->pm) { 119062306a36Sopenharmony_ci if (drv->pm->thaw) 119162306a36Sopenharmony_ci ret = drv->pm->thaw(dev); 119262306a36Sopenharmony_ci } else { 119362306a36Sopenharmony_ci ret = platform_legacy_resume(dev); 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return ret; 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ciint platform_pm_poweroff(struct device *dev) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct device_driver *drv = dev->driver; 120262306a36Sopenharmony_ci int ret = 0; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (!drv) 120562306a36Sopenharmony_ci return 0; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (drv->pm) { 120862306a36Sopenharmony_ci if (drv->pm->poweroff) 120962306a36Sopenharmony_ci ret = drv->pm->poweroff(dev); 121062306a36Sopenharmony_ci } else { 121162306a36Sopenharmony_ci ret = platform_legacy_suspend(dev, PMSG_HIBERNATE); 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci return ret; 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ciint platform_pm_restore(struct device *dev) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci struct device_driver *drv = dev->driver; 122062306a36Sopenharmony_ci int ret = 0; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (!drv) 122362306a36Sopenharmony_ci return 0; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (drv->pm) { 122662306a36Sopenharmony_ci if (drv->pm->restore) 122762306a36Sopenharmony_ci ret = drv->pm->restore(dev); 122862306a36Sopenharmony_ci } else { 122962306a36Sopenharmony_ci ret = platform_legacy_resume(dev); 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci return ret; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci#endif /* CONFIG_HIBERNATE_CALLBACKS */ 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci/* modalias support enables more hands-off userspace setup: 123862306a36Sopenharmony_ci * (a) environment variable lets new-style hotplug events work once system is 123962306a36Sopenharmony_ci * fully running: "modprobe $MODALIAS" 124062306a36Sopenharmony_ci * (b) sysfs attribute lets new-style coldplug recover from hotplug events 124162306a36Sopenharmony_ci * mishandled before system is fully running: "modprobe $(cat modalias)" 124262306a36Sopenharmony_ci */ 124362306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, 124462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 124762306a36Sopenharmony_ci int len; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci len = of_device_modalias(dev, buf, PAGE_SIZE); 125062306a36Sopenharmony_ci if (len != -ENODEV) 125162306a36Sopenharmony_ci return len; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); 125462306a36Sopenharmony_ci if (len != -ENODEV) 125562306a36Sopenharmony_ci return len; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci return sysfs_emit(buf, "platform:%s\n", pdev->name); 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic ssize_t numa_node_show(struct device *dev, 126262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", dev_to_node(dev)); 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(numa_node); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev, 126962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 127262306a36Sopenharmony_ci ssize_t len; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci device_lock(dev); 127562306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", pdev->driver_override); 127662306a36Sopenharmony_ci device_unlock(dev); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci return len; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev, 128262306a36Sopenharmony_ci struct device_attribute *attr, 128362306a36Sopenharmony_ci const char *buf, size_t count) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 128662306a36Sopenharmony_ci int ret; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci ret = driver_set_override(dev, &pdev->driver_override, buf, count); 128962306a36Sopenharmony_ci if (ret) 129062306a36Sopenharmony_ci return ret; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci return count; 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic struct attribute *platform_dev_attrs[] = { 129762306a36Sopenharmony_ci &dev_attr_modalias.attr, 129862306a36Sopenharmony_ci &dev_attr_numa_node.attr, 129962306a36Sopenharmony_ci &dev_attr_driver_override.attr, 130062306a36Sopenharmony_ci NULL, 130162306a36Sopenharmony_ci}; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a, 130462306a36Sopenharmony_ci int n) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci struct device *dev = container_of(kobj, typeof(*dev), kobj); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci if (a == &dev_attr_numa_node.attr && 130962306a36Sopenharmony_ci dev_to_node(dev) == NUMA_NO_NODE) 131062306a36Sopenharmony_ci return 0; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci return a->mode; 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic const struct attribute_group platform_dev_group = { 131662306a36Sopenharmony_ci .attrs = platform_dev_attrs, 131762306a36Sopenharmony_ci .is_visible = platform_dev_attrs_visible, 131862306a36Sopenharmony_ci}; 131962306a36Sopenharmony_ci__ATTRIBUTE_GROUPS(platform_dev); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci/** 132362306a36Sopenharmony_ci * platform_match - bind platform device to platform driver. 132462306a36Sopenharmony_ci * @dev: device. 132562306a36Sopenharmony_ci * @drv: driver. 132662306a36Sopenharmony_ci * 132762306a36Sopenharmony_ci * Platform device IDs are assumed to be encoded like this: 132862306a36Sopenharmony_ci * "<name><instance>", where <name> is a short description of the type of 132962306a36Sopenharmony_ci * device, like "pci" or "floppy", and <instance> is the enumerated 133062306a36Sopenharmony_ci * instance of the device, like '0' or '42'. Driver IDs are simply 133162306a36Sopenharmony_ci * "<name>". So, extract the <name> from the platform_device structure, 133262306a36Sopenharmony_ci * and compare it against the name of the driver. Return whether they match 133362306a36Sopenharmony_ci * or not. 133462306a36Sopenharmony_ci */ 133562306a36Sopenharmony_cistatic int platform_match(struct device *dev, struct device_driver *drv) 133662306a36Sopenharmony_ci{ 133762306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 133862306a36Sopenharmony_ci struct platform_driver *pdrv = to_platform_driver(drv); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* When driver_override is set, only bind to the matching driver */ 134162306a36Sopenharmony_ci if (pdev->driver_override) 134262306a36Sopenharmony_ci return !strcmp(pdev->driver_override, drv->name); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci /* Attempt an OF style match first */ 134562306a36Sopenharmony_ci if (of_driver_match_device(dev, drv)) 134662306a36Sopenharmony_ci return 1; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* Then try ACPI style match */ 134962306a36Sopenharmony_ci if (acpi_driver_match_device(dev, drv)) 135062306a36Sopenharmony_ci return 1; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci /* Then try to match against the id table */ 135362306a36Sopenharmony_ci if (pdrv->id_table) 135462306a36Sopenharmony_ci return platform_match_id(pdrv->id_table, pdev) != NULL; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci /* fall-back to driver name match */ 135762306a36Sopenharmony_ci return (strcmp(pdev->name, drv->name) == 0); 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic int platform_uevent(const struct device *dev, struct kobj_uevent_env *env) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci const struct platform_device *pdev = to_platform_device(dev); 136362306a36Sopenharmony_ci int rc; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci /* Some devices have extra OF data and an OF-style MODALIAS */ 136662306a36Sopenharmony_ci rc = of_device_uevent_modalias(dev, env); 136762306a36Sopenharmony_ci if (rc != -ENODEV) 136862306a36Sopenharmony_ci return rc; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci rc = acpi_device_uevent_modalias(dev, env); 137162306a36Sopenharmony_ci if (rc != -ENODEV) 137262306a36Sopenharmony_ci return rc; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, 137562306a36Sopenharmony_ci pdev->name); 137662306a36Sopenharmony_ci return 0; 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic int platform_probe(struct device *_dev) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct platform_driver *drv = to_platform_driver(_dev->driver); 138262306a36Sopenharmony_ci struct platform_device *dev = to_platform_device(_dev); 138362306a36Sopenharmony_ci int ret; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci /* 138662306a36Sopenharmony_ci * A driver registered using platform_driver_probe() cannot be bound 138762306a36Sopenharmony_ci * again later because the probe function usually lives in __init code 138862306a36Sopenharmony_ci * and so is gone. For these drivers .probe is set to 138962306a36Sopenharmony_ci * platform_probe_fail in __platform_driver_probe(). Don't even prepare 139062306a36Sopenharmony_ci * clocks and PM domains for these to match the traditional behaviour. 139162306a36Sopenharmony_ci */ 139262306a36Sopenharmony_ci if (unlikely(drv->probe == platform_probe_fail)) 139362306a36Sopenharmony_ci return -ENXIO; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci ret = of_clk_set_defaults(_dev->of_node, false); 139662306a36Sopenharmony_ci if (ret < 0) 139762306a36Sopenharmony_ci return ret; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci ret = dev_pm_domain_attach(_dev, true); 140062306a36Sopenharmony_ci if (ret) 140162306a36Sopenharmony_ci goto out; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci if (drv->probe) { 140462306a36Sopenharmony_ci ret = drv->probe(dev); 140562306a36Sopenharmony_ci if (ret) 140662306a36Sopenharmony_ci dev_pm_domain_detach(_dev, true); 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ciout: 141062306a36Sopenharmony_ci if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { 141162306a36Sopenharmony_ci dev_warn(_dev, "probe deferral not supported\n"); 141262306a36Sopenharmony_ci ret = -ENXIO; 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci return ret; 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic void platform_remove(struct device *_dev) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci struct platform_driver *drv = to_platform_driver(_dev->driver); 142162306a36Sopenharmony_ci struct platform_device *dev = to_platform_device(_dev); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (drv->remove_new) { 142462306a36Sopenharmony_ci drv->remove_new(dev); 142562306a36Sopenharmony_ci } else if (drv->remove) { 142662306a36Sopenharmony_ci int ret = drv->remove(dev); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci if (ret) 142962306a36Sopenharmony_ci dev_warn(_dev, "remove callback returned a non-zero value. This will be ignored.\n"); 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci dev_pm_domain_detach(_dev, true); 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistatic void platform_shutdown(struct device *_dev) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci struct platform_device *dev = to_platform_device(_dev); 143762306a36Sopenharmony_ci struct platform_driver *drv; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (!_dev->driver) 144062306a36Sopenharmony_ci return; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci drv = to_platform_driver(_dev->driver); 144362306a36Sopenharmony_ci if (drv->shutdown) 144462306a36Sopenharmony_ci drv->shutdown(dev); 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic int platform_dma_configure(struct device *dev) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci struct platform_driver *drv = to_platform_driver(dev->driver); 145062306a36Sopenharmony_ci enum dev_dma_attr attr; 145162306a36Sopenharmony_ci int ret = 0; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (dev->of_node) { 145462306a36Sopenharmony_ci ret = of_dma_configure(dev, dev->of_node, true); 145562306a36Sopenharmony_ci } else if (has_acpi_companion(dev)) { 145662306a36Sopenharmony_ci attr = acpi_get_dma_attr(to_acpi_device_node(dev->fwnode)); 145762306a36Sopenharmony_ci ret = acpi_dma_configure(dev, attr); 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (!ret && !drv->driver_managed_dma) { 146162306a36Sopenharmony_ci ret = iommu_device_use_default_domain(dev); 146262306a36Sopenharmony_ci if (ret) 146362306a36Sopenharmony_ci arch_teardown_dma_ops(dev); 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci return ret; 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_cistatic void platform_dma_cleanup(struct device *dev) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct platform_driver *drv = to_platform_driver(dev->driver); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci if (!drv->driver_managed_dma) 147462306a36Sopenharmony_ci iommu_device_unuse_default_domain(dev); 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic const struct dev_pm_ops platform_dev_pm_ops = { 147862306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, pm_generic_runtime_resume, NULL) 147962306a36Sopenharmony_ci USE_PLATFORM_PM_SLEEP_OPS 148062306a36Sopenharmony_ci}; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_cistruct bus_type platform_bus_type = { 148362306a36Sopenharmony_ci .name = "platform", 148462306a36Sopenharmony_ci .dev_groups = platform_dev_groups, 148562306a36Sopenharmony_ci .match = platform_match, 148662306a36Sopenharmony_ci .uevent = platform_uevent, 148762306a36Sopenharmony_ci .probe = platform_probe, 148862306a36Sopenharmony_ci .remove = platform_remove, 148962306a36Sopenharmony_ci .shutdown = platform_shutdown, 149062306a36Sopenharmony_ci .dma_configure = platform_dma_configure, 149162306a36Sopenharmony_ci .dma_cleanup = platform_dma_cleanup, 149262306a36Sopenharmony_ci .pm = &platform_dev_pm_ops, 149362306a36Sopenharmony_ci}; 149462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_bus_type); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic inline int __platform_match(struct device *dev, const void *drv) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci return platform_match(dev, (struct device_driver *)drv); 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci/** 150262306a36Sopenharmony_ci * platform_find_device_by_driver - Find a platform device with a given 150362306a36Sopenharmony_ci * driver. 150462306a36Sopenharmony_ci * @start: The device to start the search from. 150562306a36Sopenharmony_ci * @drv: The device driver to look for. 150662306a36Sopenharmony_ci */ 150762306a36Sopenharmony_cistruct device *platform_find_device_by_driver(struct device *start, 150862306a36Sopenharmony_ci const struct device_driver *drv) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci return bus_find_device(&platform_bus_type, start, drv, 151162306a36Sopenharmony_ci __platform_match); 151262306a36Sopenharmony_ci} 151362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(platform_find_device_by_driver); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_civoid __weak __init early_platform_cleanup(void) { } 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ciint __init platform_bus_init(void) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci int error; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci early_platform_cleanup(); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci error = device_register(&platform_bus); 152462306a36Sopenharmony_ci if (error) { 152562306a36Sopenharmony_ci put_device(&platform_bus); 152662306a36Sopenharmony_ci return error; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci error = bus_register(&platform_bus_type); 152962306a36Sopenharmony_ci if (error) 153062306a36Sopenharmony_ci device_unregister(&platform_bus); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci return error; 153362306a36Sopenharmony_ci} 1534