xref: /openbmc/linux/drivers/acpi/acpi_platform.c (revision bf6067a6)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
291e56878SMika Westerberg /*
391e56878SMika Westerberg  * ACPI support for platform bus type.
491e56878SMika Westerberg  *
591e56878SMika Westerberg  * Copyright (C) 2012, Intel Corporation
691e56878SMika Westerberg  * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
791e56878SMika Westerberg  *          Mathias Nyman <mathias.nyman@linux.intel.com>
891e56878SMika Westerberg  *          Rafael J. Wysocki <rafael.j.wysocki@intel.com>
991e56878SMika Westerberg  */
1091e56878SMika Westerberg 
1191e56878SMika Westerberg #include <linux/acpi.h>
12cefbd80bSAndy Shevchenko #include <linux/bits.h>
1391e56878SMika Westerberg #include <linux/device.h>
14e375325cSMika Westerberg #include <linux/err.h>
1591e56878SMika Westerberg #include <linux/kernel.h>
1691e56878SMika Westerberg #include <linux/module.h>
178a2f38ddSHeikki Krogerus #include <linux/dma-mapping.h>
18a252d881SMika Westerberg #include <linux/pci.h>
1991e56878SMika Westerberg #include <linux/platform_device.h>
2091e56878SMika Westerberg 
215923f986SAndy Shevchenko #include "internal.h"
225923f986SAndy Shevchenko 
23cefbd80bSAndy Shevchenko /* Exclude devices that have no _CRS resources provided */
24cefbd80bSAndy Shevchenko #define ACPI_ALLOW_WO_RESOURCES		BIT(0)
25cefbd80bSAndy Shevchenko 
2648459340SZhang Rui static const struct acpi_device_id forbidden_id_list[] = {
271902d158SAndy Shevchenko 	{"ACPI0009", 0},	/* IOxAPIC */
281902d158SAndy Shevchenko 	{"ACPI000A", 0},	/* IOAPIC */
2948459340SZhang Rui 	{"PNP0000",  0},	/* PIC */
3048459340SZhang Rui 	{"PNP0100",  0},	/* Timer */
3148459340SZhang Rui 	{"PNP0200",  0},	/* AT DMA Controller */
32*bf6067a6SAndy Shevchenko 	{ACPI_SMBUS_MS_HID,  ACPI_ALLOW_WO_RESOURCES},	/* ACPI SMBUS virtual device */
331902d158SAndy Shevchenko 	{ }
34141a297bSRafael J. Wysocki };
35141a297bSRafael J. Wysocki 
acpi_platform_device_find_by_companion(struct acpi_device * adev)36cb0701acSAndy Shevchenko static struct platform_device *acpi_platform_device_find_by_companion(struct acpi_device *adev)
37cb0701acSAndy Shevchenko {
38cb0701acSAndy Shevchenko 	struct device *dev;
39cb0701acSAndy Shevchenko 
40cb0701acSAndy Shevchenko 	dev = bus_find_device_by_acpi_dev(&platform_bus_type, adev);
41cb0701acSAndy Shevchenko 	return dev ? to_platform_device(dev) : NULL;
42cb0701acSAndy Shevchenko }
43cb0701acSAndy Shevchenko 
acpi_platform_device_remove_notify(struct notifier_block * nb,unsigned long value,void * arg)44cb0701acSAndy Shevchenko static int acpi_platform_device_remove_notify(struct notifier_block *nb,
45cb0701acSAndy Shevchenko 					      unsigned long value, void *arg)
46cb0701acSAndy Shevchenko {
47cb0701acSAndy Shevchenko 	struct acpi_device *adev = arg;
48cb0701acSAndy Shevchenko 	struct platform_device *pdev;
49cb0701acSAndy Shevchenko 
50cb0701acSAndy Shevchenko 	switch (value) {
51cb0701acSAndy Shevchenko 	case ACPI_RECONFIG_DEVICE_ADD:
52cb0701acSAndy Shevchenko 		/* Nothing to do here */
53cb0701acSAndy Shevchenko 		break;
54cb0701acSAndy Shevchenko 	case ACPI_RECONFIG_DEVICE_REMOVE:
55cb0701acSAndy Shevchenko 		if (!acpi_device_enumerated(adev))
56cb0701acSAndy Shevchenko 			break;
57cb0701acSAndy Shevchenko 
58cb0701acSAndy Shevchenko 		pdev = acpi_platform_device_find_by_companion(adev);
59cb0701acSAndy Shevchenko 		if (!pdev)
60cb0701acSAndy Shevchenko 			break;
61cb0701acSAndy Shevchenko 
62cb0701acSAndy Shevchenko 		platform_device_unregister(pdev);
63cb0701acSAndy Shevchenko 		put_device(&pdev->dev);
64cb0701acSAndy Shevchenko 		break;
65cb0701acSAndy Shevchenko 	}
66cb0701acSAndy Shevchenko 
67cb0701acSAndy Shevchenko 	return NOTIFY_OK;
68cb0701acSAndy Shevchenko }
69cb0701acSAndy Shevchenko 
70cb0701acSAndy Shevchenko static struct notifier_block acpi_platform_notifier = {
71cb0701acSAndy Shevchenko 	.notifier_call = acpi_platform_device_remove_notify,
72cb0701acSAndy Shevchenko };
73cb0701acSAndy Shevchenko 
acpi_platform_fill_resource(struct acpi_device * adev,const struct resource * src,struct resource * dest)74a252d881SMika Westerberg static void acpi_platform_fill_resource(struct acpi_device *adev,
75a252d881SMika Westerberg 	const struct resource *src, struct resource *dest)
76a252d881SMika Westerberg {
77a252d881SMika Westerberg 	struct device *parent;
78a252d881SMika Westerberg 
79a252d881SMika Westerberg 	*dest = *src;
80a252d881SMika Westerberg 
81a252d881SMika Westerberg 	/*
82a252d881SMika Westerberg 	 * If the device has parent we need to take its resources into
83a252d881SMika Westerberg 	 * account as well because this device might consume part of those.
84a252d881SMika Westerberg 	 */
8562fcb99bSRafael J. Wysocki 	parent = acpi_get_first_physical_node(acpi_dev_parent(adev));
86a252d881SMika Westerberg 	if (parent && dev_is_pci(parent))
87a252d881SMika Westerberg 		dest->parent = pci_find_resource(to_pci_dev(parent), dest);
88a252d881SMika Westerberg }
89a252d881SMika Westerberg 
acpi_platform_resource_count(struct acpi_resource * ares,void * data)90cefbd80bSAndy Shevchenko static unsigned int acpi_platform_resource_count(struct acpi_resource *ares, void *data)
91cefbd80bSAndy Shevchenko {
92cefbd80bSAndy Shevchenko 	bool *has_resources = data;
93cefbd80bSAndy Shevchenko 
94cefbd80bSAndy Shevchenko 	*has_resources = true;
95cefbd80bSAndy Shevchenko 
96cefbd80bSAndy Shevchenko 	return AE_CTRL_TERMINATE;
97cefbd80bSAndy Shevchenko }
98cefbd80bSAndy Shevchenko 
9991e56878SMika Westerberg /**
10091e56878SMika Westerberg  * acpi_create_platform_device - Create platform device for ACPI device node
10191e56878SMika Westerberg  * @adev: ACPI device node to create a platform device for.
1021571875bSHeikki Krogerus  * @properties: Optional collection of build-in properties.
10391e56878SMika Westerberg  *
10491e56878SMika Westerberg  * Check if the given @adev can be represented as a platform device and, if
10591e56878SMika Westerberg  * that's the case, create and register a platform device, populate its common
10691e56878SMika Westerberg  * resources and returns a pointer to it.  Otherwise, return %NULL.
10791e56878SMika Westerberg  *
1087eaa2800SMika Westerberg  * Name of the platform device will be the same as @adev's.
10991e56878SMika Westerberg  */
acpi_create_platform_device(struct acpi_device * adev,const struct property_entry * properties)1101571875bSHeikki Krogerus struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
1112cbfae0fSAndy Shevchenko 						    const struct property_entry *properties)
11291e56878SMika Westerberg {
11362fcb99bSRafael J. Wysocki 	struct acpi_device *parent = acpi_dev_parent(adev);
11491e56878SMika Westerberg 	struct platform_device *pdev = NULL;
115863f9f30SRafael J. Wysocki 	struct platform_device_info pdevinfo;
116cefbd80bSAndy Shevchenko 	const struct acpi_device_id *match;
11790e97820SJiang Liu 	struct resource_entry *rentry;
1188e345c99SRafael J. Wysocki 	struct list_head resource_list;
11962eb4b07SKuppuswamy Sathyanarayanan 	struct resource *resources = NULL;
1208e345c99SRafael J. Wysocki 	int count;
12191e56878SMika Westerberg 
12291e56878SMika Westerberg 	/* If the ACPI node already has a physical device attached, skip it. */
12391e56878SMika Westerberg 	if (adev->physical_node_count)
1248ce62f85SRafael J. Wysocki 		return NULL;
12591e56878SMika Westerberg 
126cefbd80bSAndy Shevchenko 	match = acpi_match_acpi_device(forbidden_id_list, adev);
127cefbd80bSAndy Shevchenko 	if (match) {
128cefbd80bSAndy Shevchenko 		if (match->driver_data & ACPI_ALLOW_WO_RESOURCES) {
129cefbd80bSAndy Shevchenko 			bool has_resources = false;
130cefbd80bSAndy Shevchenko 
131cefbd80bSAndy Shevchenko 			acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
132cefbd80bSAndy Shevchenko 					    acpi_platform_resource_count, &has_resources);
133cefbd80bSAndy Shevchenko 			if (has_resources)
13448459340SZhang Rui 				return ERR_PTR(-EINVAL);
135cefbd80bSAndy Shevchenko 		} else {
136cefbd80bSAndy Shevchenko 			return ERR_PTR(-EINVAL);
137cefbd80bSAndy Shevchenko 		}
138cefbd80bSAndy Shevchenko 	}
13948459340SZhang Rui 
1408e345c99SRafael J. Wysocki 	INIT_LIST_HEAD(&resource_list);
1418e345c99SRafael J. Wysocki 	count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
142f3bc9ca5SAndy Shevchenko 	if (count < 0)
1438ce62f85SRafael J. Wysocki 		return NULL;
144f3bc9ca5SAndy Shevchenko 	if (count > 0) {
145895a4d6cSAndy Shevchenko 		resources = kcalloc(count, sizeof(*resources), GFP_KERNEL);
1468e345c99SRafael J. Wysocki 		if (!resources) {
1478e345c99SRafael J. Wysocki 			acpi_dev_free_resource_list(&resource_list);
1488ce62f85SRafael J. Wysocki 			return ERR_PTR(-ENOMEM);
14991e56878SMika Westerberg 		}
1508e345c99SRafael J. Wysocki 		count = 0;
1518e345c99SRafael J. Wysocki 		list_for_each_entry(rentry, &resource_list, node)
152a252d881SMika Westerberg 			acpi_platform_fill_resource(adev, rentry->res,
153a252d881SMika Westerberg 						    &resources[count++]);
15491e56878SMika Westerberg 
1558e345c99SRafael J. Wysocki 		acpi_dev_free_resource_list(&resource_list);
156d2fe7251SRafael J. Wysocki 	}
15791e56878SMika Westerberg 
158863f9f30SRafael J. Wysocki 	memset(&pdevinfo, 0, sizeof(pdevinfo));
15991e56878SMika Westerberg 	/*
16091e56878SMika Westerberg 	 * If the ACPI node has a parent and that parent has a physical device
16191e56878SMika Westerberg 	 * attached to it, that physical device should be the parent of the
16291e56878SMika Westerberg 	 * platform device we are about to create.
16391e56878SMika Westerberg 	 */
16462fcb99bSRafael J. Wysocki 	pdevinfo.parent = parent ? acpi_get_first_physical_node(parent) : NULL;
165863f9f30SRafael J. Wysocki 	pdevinfo.name = dev_name(&adev->dev);
1662814108cSJohn Garry 	pdevinfo.id = PLATFORM_DEVID_NONE;
167863f9f30SRafael J. Wysocki 	pdevinfo.res = resources;
168863f9f30SRafael J. Wysocki 	pdevinfo.num_res = count;
169ce793486SRafael J. Wysocki 	pdevinfo.fwnode = acpi_fwnode_handle(adev);
1701571875bSHeikki Krogerus 	pdevinfo.properties = properties;
1711831eff8SSuthikulpanit, Suravee 
1721831eff8SSuthikulpanit, Suravee 	if (acpi_dma_supported(adev))
1731831eff8SSuthikulpanit, Suravee 		pdevinfo.dma_mask = DMA_BIT_MASK(32);
1741831eff8SSuthikulpanit, Suravee 	else
1751831eff8SSuthikulpanit, Suravee 		pdevinfo.dma_mask = 0;
1761831eff8SSuthikulpanit, Suravee 
177863f9f30SRafael J. Wysocki 	pdev = platform_device_register_full(&pdevinfo);
1788ce62f85SRafael J. Wysocki 	if (IS_ERR(pdev))
17991e56878SMika Westerberg 		dev_err(&adev->dev, "platform device creation failed: %ld\n",
18091e56878SMika Westerberg 			PTR_ERR(pdev));
18179f97c30SShanker Donthineni 	else {
18279f97c30SShanker Donthineni 		set_dev_node(&pdev->dev, acpi_get_node(adev->handle));
18391e56878SMika Westerberg 		dev_dbg(&adev->dev, "created platform device %s\n",
18491e56878SMika Westerberg 			dev_name(&pdev->dev));
18579f97c30SShanker Donthineni 	}
18691e56878SMika Westerberg 
1878e345c99SRafael J. Wysocki 	kfree(resources);
18879f97c30SShanker Donthineni 
1898ce62f85SRafael J. Wysocki 	return pdev;
1908ce62f85SRafael J. Wysocki }
191083bf668SZhang Rui EXPORT_SYMBOL_GPL(acpi_create_platform_device);
192cb0701acSAndy Shevchenko 
acpi_platform_init(void)193cb0701acSAndy Shevchenko void __init acpi_platform_init(void)
194cb0701acSAndy Shevchenko {
195cb0701acSAndy Shevchenko 	acpi_reconfig_notifier_register(&acpi_platform_notifier);
196cb0701acSAndy Shevchenko }
197