brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · a194f30 Raw
415 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Link physical devices with ACPI devices support4 *5 * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com>6 * Copyright (c) 2005 Intel Corp.7 */8 9#define pr_fmt(fmt) "ACPI: " fmt10 11#include <linux/acpi_iort.h>12#include <linux/export.h>13#include <linux/init.h>14#include <linux/list.h>15#include <linux/device.h>16#include <linux/slab.h>17#include <linux/rwsem.h>18#include <linux/acpi.h>19#include <linux/dma-mapping.h>20#include <linux/pci.h>21#include <linux/pci-acpi.h>22#include <linux/platform_device.h>23 24#include "internal.h"25 26static LIST_HEAD(bus_type_list);27static DECLARE_RWSEM(bus_type_sem);28 29#define PHYSICAL_NODE_STRING "physical_node"30#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10)31 32int register_acpi_bus_type(struct acpi_bus_type *type)33{34	if (acpi_disabled)35		return -ENODEV;36	if (type && type->match && type->find_companion) {37		down_write(&bus_type_sem);38		list_add_tail(&type->list, &bus_type_list);39		up_write(&bus_type_sem);40		pr_info("bus type %s registered\n", type->name);41		return 0;42	}43	return -ENODEV;44}45EXPORT_SYMBOL_GPL(register_acpi_bus_type);46 47int unregister_acpi_bus_type(struct acpi_bus_type *type)48{49	if (acpi_disabled)50		return 0;51	if (type) {52		down_write(&bus_type_sem);53		list_del_init(&type->list);54		up_write(&bus_type_sem);55		pr_info("bus type %s unregistered\n", type->name);56		return 0;57	}58	return -ENODEV;59}60EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);61 62static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)63{64	struct acpi_bus_type *tmp, *ret = NULL;65 66	down_read(&bus_type_sem);67	list_for_each_entry(tmp, &bus_type_list, list) {68		if (tmp->match(dev)) {69			ret = tmp;70			break;71		}72	}73	up_read(&bus_type_sem);74	return ret;75}76 77#define FIND_CHILD_MIN_SCORE	178#define FIND_CHILD_MID_SCORE	279#define FIND_CHILD_MAX_SCORE	380 81static int match_any(struct acpi_device *adev, void *not_used)82{83	return 1;84}85 86static bool acpi_dev_has_children(struct acpi_device *adev)87{88	return acpi_dev_for_each_child(adev, match_any, NULL) > 0;89}90 91static int find_child_checks(struct acpi_device *adev, bool check_children)92{93	unsigned long long sta;94	acpi_status status;95 96	if (check_children && !acpi_dev_has_children(adev))97		return -ENODEV;98 99	status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);100	if (status == AE_NOT_FOUND) {101		/*102		 * Special case: backlight device objects without _STA are103		 * preferred to other objects with the same _ADR value, because104		 * it is more likely that they are actually useful.105		 */106		if (adev->pnp.type.backlight)107			return FIND_CHILD_MID_SCORE;108 109		return FIND_CHILD_MIN_SCORE;110	}111 112	if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))113		return -ENODEV;114 115	/*116	 * If the device has a _HID returning a valid ACPI/PNP device ID, it is117	 * better to make it look less attractive here, so that the other device118	 * with the same _ADR value (that may not have a valid device ID) can be119	 * matched going forward.  [This means a second spec violation in a row,120	 * so whatever we do here is best effort anyway.]121	 */122	if (adev->pnp.type.platform_id)123		return FIND_CHILD_MIN_SCORE;124 125	return FIND_CHILD_MAX_SCORE;126}127 128struct find_child_walk_data {129	struct acpi_device *adev;130	u64 address;131	int score;132	bool check_sta;133	bool check_children;134};135 136static int check_one_child(struct acpi_device *adev, void *data)137{138	struct find_child_walk_data *wd = data;139	int score;140 141	if (!adev->pnp.type.bus_address || acpi_device_adr(adev) != wd->address)142		return 0;143 144	if (!wd->adev) {145		/*146		 * This is the first matching object, so save it.  If it is not147		 * necessary to look for any other matching objects, stop the148		 * search.149		 */150		wd->adev = adev;151		return !(wd->check_sta || wd->check_children);152	}153 154	/*155	 * There is more than one matching device object with the same _ADR156	 * value.  That really is unexpected, so we are kind of beyond the scope157	 * of the spec here.  We have to choose which one to return, though.158	 *159	 * First, get the score for the previously found object and terminate160	 * the walk if it is maximum.161	*/162	if (!wd->score) {163		score = find_child_checks(wd->adev, wd->check_children);164		if (score == FIND_CHILD_MAX_SCORE)165			return 1;166 167		wd->score = score;168	}169	/*170	 * Second, if the object that has just been found has a better score,171	 * replace the previously found one with it and terminate the walk if172	 * the new score is maximum.173	 */174	score = find_child_checks(adev, wd->check_children);175	if (score > wd->score) {176		wd->adev = adev;177		if (score == FIND_CHILD_MAX_SCORE)178			return 1;179 180		wd->score = score;181	}182 183	/* Continue, because there may be better matches. */184	return 0;185}186 187static struct acpi_device *acpi_find_child(struct acpi_device *parent,188					   u64 address, bool check_children,189					   bool check_sta)190{191	struct find_child_walk_data wd = {192		.address = address,193		.check_children = check_children,194		.check_sta = check_sta,195		.adev = NULL,196		.score = 0,197	};198 199	if (parent)200		acpi_dev_for_each_child(parent, check_one_child, &wd);201 202	return wd.adev;203}204 205struct acpi_device *acpi_find_child_device(struct acpi_device *parent,206					   u64 address, bool check_children)207{208	return acpi_find_child(parent, address, check_children, true);209}210EXPORT_SYMBOL_GPL(acpi_find_child_device);211 212struct acpi_device *acpi_find_child_by_adr(struct acpi_device *adev,213					   acpi_bus_address adr)214{215	return acpi_find_child(adev, adr, false, false);216}217EXPORT_SYMBOL_GPL(acpi_find_child_by_adr);218 219static void acpi_physnode_link_name(char *buf, unsigned int node_id)220{221	if (node_id > 0)222		snprintf(buf, PHYSICAL_NODE_NAME_SIZE,223			 PHYSICAL_NODE_STRING "%u", node_id);224	else225		strcpy(buf, PHYSICAL_NODE_STRING);226}227 228int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)229{230	struct acpi_device_physical_node *physical_node, *pn;231	char physical_node_name[PHYSICAL_NODE_NAME_SIZE];232	struct list_head *physnode_list;233	unsigned int node_id;234	int retval = -EINVAL;235 236	if (has_acpi_companion(dev)) {237		if (acpi_dev) {238			dev_warn(dev, "ACPI companion already set\n");239			return -EINVAL;240		} else {241			acpi_dev = ACPI_COMPANION(dev);242		}243	}244	if (!acpi_dev)245		return -EINVAL;246 247	acpi_dev_get(acpi_dev);248	get_device(dev);249	physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);250	if (!physical_node) {251		retval = -ENOMEM;252		goto err;253	}254 255	mutex_lock(&acpi_dev->physical_node_lock);256 257	/*258	 * Keep the list sorted by node_id so that the IDs of removed nodes can259	 * be recycled easily.260	 */261	physnode_list = &acpi_dev->physical_node_list;262	node_id = 0;263	list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {264		/* Sanity check. */265		if (pn->dev == dev) {266			mutex_unlock(&acpi_dev->physical_node_lock);267 268			dev_warn(dev, "Already associated with ACPI node\n");269			kfree(physical_node);270			if (ACPI_COMPANION(dev) != acpi_dev)271				goto err;272 273			put_device(dev);274			acpi_dev_put(acpi_dev);275			return 0;276		}277		if (pn->node_id == node_id) {278			physnode_list = &pn->node;279			node_id++;280		}281	}282 283	physical_node->node_id = node_id;284	physical_node->dev = dev;285	list_add(&physical_node->node, physnode_list);286	acpi_dev->physical_node_count++;287 288	if (!has_acpi_companion(dev))289		ACPI_COMPANION_SET(dev, acpi_dev);290 291	acpi_physnode_link_name(physical_node_name, node_id);292	retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,293				   physical_node_name);294	if (retval)295		dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n",296			physical_node_name, retval);297 298	retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,299				   "firmware_node");300	if (retval)301		dev_err(dev, "Failed to create link firmware_node (%d)\n",302			retval);303 304	mutex_unlock(&acpi_dev->physical_node_lock);305 306	if (acpi_dev->wakeup.flags.valid)307		device_set_wakeup_capable(dev, true);308 309	return 0;310 311 err:312	ACPI_COMPANION_SET(dev, NULL);313	put_device(dev);314	acpi_dev_put(acpi_dev);315	return retval;316}317EXPORT_SYMBOL_GPL(acpi_bind_one);318 319int acpi_unbind_one(struct device *dev)320{321	struct acpi_device *acpi_dev = ACPI_COMPANION(dev);322	struct acpi_device_physical_node *entry;323 324	if (!acpi_dev)325		return 0;326 327	mutex_lock(&acpi_dev->physical_node_lock);328 329	list_for_each_entry(entry, &acpi_dev->physical_node_list, node)330		if (entry->dev == dev) {331			char physnode_name[PHYSICAL_NODE_NAME_SIZE];332 333			list_del(&entry->node);334			acpi_dev->physical_node_count--;335 336			acpi_physnode_link_name(physnode_name, entry->node_id);337			sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);338			sysfs_remove_link(&dev->kobj, "firmware_node");339			ACPI_COMPANION_SET(dev, NULL);340			/* Drop references taken by acpi_bind_one(). */341			put_device(dev);342			acpi_dev_put(acpi_dev);343			kfree(entry);344			break;345		}346 347	mutex_unlock(&acpi_dev->physical_node_lock);348	return 0;349}350EXPORT_SYMBOL_GPL(acpi_unbind_one);351 352void acpi_device_notify(struct device *dev)353{354	struct acpi_device *adev;355	int ret;356 357	ret = acpi_bind_one(dev, NULL);358	if (ret) {359		struct acpi_bus_type *type = acpi_get_bus_type(dev);360 361		if (!type)362			goto err;363 364		adev = type->find_companion(dev);365		if (!adev) {366			dev_dbg(dev, "ACPI companion not found\n");367			goto err;368		}369		ret = acpi_bind_one(dev, adev);370		if (ret)371			goto err;372 373		if (type->setup) {374			type->setup(dev);375			goto done;376		}377	} else {378		adev = ACPI_COMPANION(dev);379 380		if (dev_is_pci(dev)) {381			pci_acpi_setup(dev, adev);382			goto done;383		} else if (dev_is_platform(dev)) {384			acpi_configure_pmsi_domain(dev);385		}386	}387 388	if (adev->handler && adev->handler->bind)389		adev->handler->bind(dev);390 391done:392	acpi_handle_debug(ACPI_HANDLE(dev), "Bound to device %s\n",393			  dev_name(dev));394 395	return;396 397err:398	dev_dbg(dev, "No ACPI support\n");399}400 401void acpi_device_notify_remove(struct device *dev)402{403	struct acpi_device *adev = ACPI_COMPANION(dev);404 405	if (!adev)406		return;407 408	if (dev_is_pci(dev))409		pci_acpi_cleanup(dev, adev);410	else if (adev->handler && adev->handler->unbind)411		adev->handler->unbind(dev);412 413	acpi_unbind_one(dev);414}415