Merge branch 'acpi-bus'

Merge ACPI device object management changes for v5.20-rc1.

 - Use the facilities provided by the driver core and some additional
   helpers to handle the children of a given ACPI device object in
   multiple places instead of using the children and node list heads in
   struct acpi_device which is error prone (Rafael Wysocki).

 - Fix ACPI-related device reference counting issue in the hisi_lpc bus
   driver (Yang Yingliang).

 - Drop the children and node list heads that are not needed any more
   from struct acpi_device (Rafael Wysocki).

 - Drop driver member from struct acpi_device (Uwe Kleine-König).

 - Drop redundant check from acpi_device_remove() (Uwe Kleine-König).

* acpi-bus:
  ACPI: bus: Drop unused list heads from struct acpi_device
  hisi_lpc: Use acpi_dev_for_each_child()
  bus: hisi_lpc: fix missing platform_device_put() in hisi_lpc_acpi_probe()
  ACPI: bus: Drop driver member of struct acpi_device
  ACPI: bus: Drop redundant check in acpi_device_remove()
  mfd: core: Use acpi_dev_for_each_child()
  ACPI / MMC: PM: Unify fixing up device power
  soundwire: Use acpi_dev_for_each_child()
  platform/x86/thinkpad_acpi: Use acpi_dev_for_each_child()
  ACPI: scan: Walk ACPI device's children using driver core
  ACPI: bus: Introduce acpi_dev_for_each_child_reverse()
  ACPI: video: Use acpi_dev_for_each_child()
  ACPI: bus: Export acpi_dev_for_each_child() to modules
  ACPI: property: Use acpi_dev_for_each_child() for child lookup
  ACPI: container: Use acpi_dev_for_each_child()
  USB: ACPI: Replace usb_acpi_find_port() with acpi_find_child_by_adr()
  thunderbolt: ACPI: Replace tb_acpi_find_port() with acpi_find_child_by_adr()
  ACPI: glue: Introduce acpi_find_child_by_adr()
  ACPI: glue: Introduce acpi_dev_has_children()
  ACPI: glue: Use acpi_dev_for_each_child()
This commit is contained in:
Rafael J. Wysocki 2022-07-29 19:58:52 +02:00
commit d60b6b0bc0
17 changed files with 466 additions and 397 deletions

View file

@ -1150,24 +1150,25 @@ acpi_video_get_device_type(struct acpi_video_bus *video,
return 0;
}
static int
acpi_video_bus_get_one_device(struct acpi_device *device,
struct acpi_video_bus *video)
static int acpi_video_bus_get_one_device(struct acpi_device *device, void *arg)
{
unsigned long long device_id;
int status, device_type;
struct acpi_video_device *data;
struct acpi_video_bus *video = arg;
struct acpi_video_device_attrib *attribute;
struct acpi_video_device *data;
unsigned long long device_id;
acpi_status status;
int device_type;
status =
acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
/* Some device omits _ADR, we skip them instead of fail */
status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
/* Skip devices without _ADR instead of failing. */
if (ACPI_FAILURE(status))
return 0;
goto exit;
data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
if (!data)
if (!data) {
dev_dbg(&device->dev, "Cannot attach\n");
return -ENOMEM;
}
strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
@ -1230,7 +1231,9 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
list_add_tail(&data->entry, &video->video_device_list);
mutex_unlock(&video->device_list_lock);
return status;
exit:
video->child_count++;
return 0;
}
/*
@ -1542,9 +1545,6 @@ static int
acpi_video_bus_get_devices(struct acpi_video_bus *video,
struct acpi_device *device)
{
int status = 0;
struct acpi_device *dev;
/*
* There are systems where video module known to work fine regardless
* of broken _DOD and ignoring returned value here doesn't cause
@ -1552,16 +1552,7 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,
*/
acpi_video_device_enumerate(video);
list_for_each_entry(dev, &device->children, node) {
status = acpi_video_bus_get_one_device(dev, video);
if (status) {
dev_err(&dev->dev, "Can't attach device\n");
break;
}
video->child_count++;
}
return status;
return acpi_dev_for_each_child(device, acpi_video_bus_get_one_device, video);
}
/* acpi_video interface */

View file

@ -464,7 +464,6 @@ static void acpi_bus_osc_negotiate_usb_control(void)
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
{
struct acpi_device *adev;
struct acpi_driver *driver;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
bool hotplug_event = false;
@ -516,10 +515,13 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
if (!adev)
goto err;
driver = adev->driver;
if (driver && driver->ops.notify &&
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
driver->ops.notify(adev, type);
if (adev->dev.driver) {
struct acpi_driver *driver = to_acpi_driver(adev->dev.driver);
if (driver && driver->ops.notify &&
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
driver->ops.notify(adev, type);
}
if (!hotplug_event) {
acpi_bus_put_acpi_device(adev);
@ -538,8 +540,9 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
static void acpi_notify_device(acpi_handle handle, u32 event, void *data)
{
struct acpi_device *device = data;
struct acpi_driver *acpi_drv = to_acpi_driver(device->dev.driver);
device->driver->ops.notify(device, event);
acpi_drv->ops.notify(device, event);
}
static void acpi_notify_device_fixed(void *data)
@ -1032,8 +1035,6 @@ static int acpi_device_probe(struct device *dev)
if (ret)
return ret;
acpi_dev->driver = acpi_drv;
pr_debug("Driver [%s] successfully bound to device [%s]\n",
acpi_drv->name, acpi_dev->pnp.bus_id);
@ -1043,7 +1044,6 @@ static int acpi_device_probe(struct device *dev)
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev);
acpi_dev->driver = NULL;
acpi_dev->driver_data = NULL;
return ret;
}
@ -1059,15 +1059,14 @@ static int acpi_device_probe(struct device *dev)
static void acpi_device_remove(struct device *dev)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = acpi_dev->driver;
struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
if (acpi_drv->ops.notify)
acpi_device_remove_notify_handler(acpi_dev);
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev);
if (acpi_drv) {
if (acpi_drv->ops.notify)
acpi_device_remove_notify_handler(acpi_dev);
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev);
}
acpi_dev->driver = NULL;
acpi_dev->driver_data = NULL;
put_device(dev);
@ -1101,6 +1100,7 @@ static int acpi_dev_for_one_check(struct device *dev, void *context)
return adwc->fn(to_acpi_device(dev), adwc->data);
}
EXPORT_SYMBOL_GPL(acpi_dev_for_each_child);
int acpi_dev_for_each_child(struct acpi_device *adev,
int (*fn)(struct acpi_device *, void *), void *data)
@ -1113,6 +1113,18 @@ int acpi_dev_for_each_child(struct acpi_device *adev,
return device_for_each_child(&adev->dev, &adwc, acpi_dev_for_one_check);
}
int acpi_dev_for_each_child_reverse(struct acpi_device *adev,
int (*fn)(struct acpi_device *, void *),
void *data)
{
struct acpi_dev_walk_context adwc = {
.fn = fn,
.data = data,
};
return device_for_each_child_reverse(&adev->dev, &adwc, acpi_dev_for_one_check);
}
/* --------------------------------------------------------------------------
Initialization/Cleanup
-------------------------------------------------------------------------- */

View file

@ -23,17 +23,18 @@ static const struct acpi_device_id container_device_ids[] = {
#ifdef CONFIG_ACPI_CONTAINER
static int check_offline(struct acpi_device *adev, void *not_used)
{
if (acpi_scan_is_offline(adev, false))
return 0;
return -EBUSY;
}
static int acpi_container_offline(struct container_dev *cdev)
{
struct acpi_device *adev = ACPI_COMPANION(&cdev->dev);
struct acpi_device *child;
/* Check all of the dependent devices' physical companions. */
list_for_each_entry(child, &adev->children, node)
if (!acpi_scan_is_offline(child, false))
return -EBUSY;
return 0;
return acpi_dev_for_each_child(ACPI_COMPANION(&cdev->dev), check_offline, NULL);
}
static void acpi_container_release(struct device *dev)

View file

@ -369,6 +369,28 @@ int acpi_device_fix_up_power(struct acpi_device *device)
}
EXPORT_SYMBOL_GPL(acpi_device_fix_up_power);
static int fix_up_power_if_applicable(struct acpi_device *adev, void *not_used)
{
if (adev->status.present && adev->status.enabled)
acpi_device_fix_up_power(adev);
return 0;
}
/**
* acpi_device_fix_up_power_extended - Force device and its children into D0.
* @adev: Parent device object whose power state is to be fixed up.
*
* Call acpi_device_fix_up_power() for @adev and its children so long as they
* are reported as present and enabled.
*/
void acpi_device_fix_up_power_extended(struct acpi_device *adev)
{
acpi_device_fix_up_power(adev);
acpi_dev_for_each_child(adev, fix_up_power_if_applicable, NULL);
}
EXPORT_SYMBOL_GPL(acpi_device_fix_up_power_extended);
int acpi_device_update_power(struct acpi_device *device, int *state_p)
{
int state;

View file

@ -376,7 +376,7 @@ eject_store(struct device *d, struct device_attribute *attr,
return -EINVAL;
if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)
&& !acpi_device->driver)
&& !d->driver)
return -ENODEV;
status = acpi_get_type(acpi_device->handle, &not_used);

View file

@ -77,12 +77,22 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
#define FIND_CHILD_MIN_SCORE 1
#define FIND_CHILD_MAX_SCORE 2
static int match_any(struct acpi_device *adev, void *not_used)
{
return 1;
}
static bool acpi_dev_has_children(struct acpi_device *adev)
{
return acpi_dev_for_each_child(adev, match_any, NULL) > 0;
}
static int find_child_checks(struct acpi_device *adev, bool check_children)
{
unsigned long long sta;
acpi_status status;
if (check_children && list_empty(&adev->children))
if (check_children && !acpi_dev_has_children(adev))
return -ENODEV;
status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
@ -105,54 +115,97 @@ static int find_child_checks(struct acpi_device *adev, bool check_children)
return FIND_CHILD_MAX_SCORE;
}
struct find_child_walk_data {
struct acpi_device *adev;
u64 address;
int score;
bool check_sta;
bool check_children;
};
static int check_one_child(struct acpi_device *adev, void *data)
{
struct find_child_walk_data *wd = data;
int score;
if (!adev->pnp.type.bus_address || acpi_device_adr(adev) != wd->address)
return 0;
if (!wd->adev) {
/*
* This is the first matching object, so save it. If it is not
* necessary to look for any other matching objects, stop the
* search.
*/
wd->adev = adev;
return !(wd->check_sta || wd->check_children);
}
/*
* There is more than one matching device object with the same _ADR
* value. That really is unexpected, so we are kind of beyond the scope
* of the spec here. We have to choose which one to return, though.
*
* First, get the score for the previously found object and terminate
* the walk if it is maximum.
*/
if (!wd->score) {
score = find_child_checks(wd->adev, wd->check_children);
if (score == FIND_CHILD_MAX_SCORE)
return 1;
wd->score = score;
}
/*
* Second, if the object that has just been found has a better score,
* replace the previously found one with it and terminate the walk if
* the new score is maximum.
*/
score = find_child_checks(adev, wd->check_children);
if (score > wd->score) {
wd->adev = adev;
if (score == FIND_CHILD_MAX_SCORE)
return 1;
wd->score = score;
}
/* Continue, because there may be better matches. */
return 0;
}
static struct acpi_device *acpi_find_child(struct acpi_device *parent,
u64 address, bool check_children,
bool check_sta)
{
struct find_child_walk_data wd = {
.address = address,
.check_children = check_children,
.check_sta = check_sta,
.adev = NULL,
.score = 0,
};
if (parent)
acpi_dev_for_each_child(parent, check_one_child, &wd);
return wd.adev;
}
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
u64 address, bool check_children)
{
struct acpi_device *adev, *ret = NULL;
int ret_score = 0;
if (!parent)
return NULL;
list_for_each_entry(adev, &parent->children, node) {
acpi_bus_address addr = acpi_device_adr(adev);
int score;
if (!adev->pnp.type.bus_address || addr != address)
continue;
if (!ret) {
/* This is the first matching object. Save it. */
ret = adev;
continue;
}
/*
* There is more than one matching device object with the same
* _ADR value. That really is unexpected, so we are kind of
* beyond the scope of the spec here. We have to choose which
* one to return, though.
*
* First, check if the previously found object is good enough
* and return it if so. Second, do the same for the object that
* we've just found.
*/
if (!ret_score) {
ret_score = find_child_checks(ret, check_children);
if (ret_score == FIND_CHILD_MAX_SCORE)
return ret;
}
score = find_child_checks(adev, check_children);
if (score == FIND_CHILD_MAX_SCORE) {
return adev;
} else if (score > ret_score) {
ret = adev;
ret_score = score;
}
}
return ret;
return acpi_find_child(parent, address, check_children, true);
}
EXPORT_SYMBOL_GPL(acpi_find_child_device);
struct acpi_device *acpi_find_child_by_adr(struct acpi_device *adev,
acpi_bus_address adr)
{
return acpi_find_child(adev, adr, false, false);
}
EXPORT_SYMBOL_GPL(acpi_find_child_by_adr);
static void acpi_physnode_link_name(char *buf, unsigned int node_id)
{
if (node_id > 0)

View file

@ -1012,6 +1012,22 @@ static int acpi_node_prop_read(const struct fwnode_handle *fwnode,
propname, proptype, val, nval);
}
static int stop_on_next(struct acpi_device *adev, void *data)
{
struct acpi_device **ret_p = data;
if (!*ret_p) {
*ret_p = adev;
return 1;
}
/* Skip until the "previous" object is found. */
if (*ret_p == adev)
*ret_p = NULL;
return 0;
}
/**
* acpi_get_next_subnode - Return the next child node handle for a fwnode
* @fwnode: Firmware node to find the next child node for.
@ -1020,35 +1036,22 @@ static int acpi_node_prop_read(const struct fwnode_handle *fwnode,
struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode,
struct fwnode_handle *child)
{
const struct acpi_device *adev = to_acpi_device_node(fwnode);
const struct list_head *head;
struct list_head *next;
struct acpi_device *adev = to_acpi_device_node(fwnode);
if ((!child || is_acpi_device_node(child)) && adev) {
struct acpi_device *child_adev;
struct acpi_device *child_adev = to_acpi_device_node(child);
head = &adev->children;
if (list_empty(head))
goto nondev;
acpi_dev_for_each_child(adev, stop_on_next, &child_adev);
if (child_adev)
return acpi_fwnode_handle(child_adev);
if (child) {
adev = to_acpi_device_node(child);
next = adev->node.next;
if (next == head) {
child = NULL;
goto nondev;
}
child_adev = list_entry(next, struct acpi_device, node);
} else {
child_adev = list_first_entry(head, struct acpi_device,
node);
}
return acpi_fwnode_handle(child_adev);
child = NULL;
}
nondev:
if (!child || is_acpi_data_node(child)) {
const struct acpi_data_node *data = to_acpi_data_node(fwnode);
const struct list_head *head;
struct list_head *next;
struct acpi_data_node *dn;
/*

View file

@ -334,10 +334,9 @@ static int acpi_scan_device_check(struct acpi_device *adev)
return error;
}
static int acpi_scan_bus_check(struct acpi_device *adev)
static int acpi_scan_bus_check(struct acpi_device *adev, void *not_used)
{
struct acpi_scan_handler *handler = adev->handler;
struct acpi_device *child;
int error;
acpi_bus_get_status(adev);
@ -353,19 +352,14 @@ static int acpi_scan_bus_check(struct acpi_device *adev)
dev_warn(&adev->dev, "Namespace scan failure\n");
return error;
}
list_for_each_entry(child, &adev->children, node) {
error = acpi_scan_bus_check(child);
if (error)
return error;
}
return 0;
return acpi_dev_for_each_child(adev, acpi_scan_bus_check, NULL);
}
static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
{
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
return acpi_scan_bus_check(adev);
return acpi_scan_bus_check(adev, NULL);
case ACPI_NOTIFY_DEVICE_CHECK:
return acpi_scan_device_check(adev);
case ACPI_NOTIFY_EJECT_REQUEST:
@ -471,8 +465,6 @@ static void acpi_device_del(struct acpi_device *device)
struct acpi_device_bus_id *acpi_device_bus_id;
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node)
if (!strcmp(acpi_device_bus_id->bus_id,
@ -488,6 +480,7 @@ static void acpi_device_del(struct acpi_device *device)
}
list_del(&device->wakeup_list);
mutex_unlock(&acpi_device_lock);
acpi_power_add_remove_device(device, false);
@ -680,8 +673,6 @@ static int __acpi_device_add(struct acpi_device *device,
* -------
* Link this device to its parent and siblings.
*/
INIT_LIST_HEAD(&device->children);
INIT_LIST_HEAD(&device->node);
INIT_LIST_HEAD(&device->wakeup_list);
INIT_LIST_HEAD(&device->physical_node_list);
INIT_LIST_HEAD(&device->del_list);
@ -721,9 +712,6 @@ static int __acpi_device_add(struct acpi_device *device,
list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);
}
if (device->parent)
list_add_tail(&device->node, &device->parent->children);
if (device->wakeup.flags.valid)
list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);
@ -752,9 +740,6 @@ static int __acpi_device_add(struct acpi_device *device,
err:
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_del(&device->wakeup_list);
err_unlock:
@ -2187,9 +2172,8 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
return ret;
}
static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
static int acpi_bus_attach(struct acpi_device *device, void *first_pass)
{
struct acpi_device *child;
bool skip = !first_pass && device->flags.visited;
acpi_handle ejd;
int ret;
@ -2206,7 +2190,7 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
device->flags.initialized = false;
acpi_device_clear_enumerated(device);
device->flags.power_manageable = 0;
return;
return 0;
}
if (device->handler)
goto ok;
@ -2224,7 +2208,7 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
ret = acpi_scan_attach_handler(device);
if (ret < 0)
return;
return 0;
device->flags.match_driver = true;
if (ret > 0 && !device->flags.enumeration_by_parent) {
@ -2234,19 +2218,20 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
ret = device_attach(&device->dev);
if (ret < 0)
return;
return 0;
if (device->pnp.type.platform_id || device->flags.enumeration_by_parent)
acpi_default_enumeration(device);
else
acpi_device_set_enumerated(device);
ok:
list_for_each_entry(child, &device->children, node)
acpi_bus_attach(child, first_pass);
ok:
acpi_dev_for_each_child(device, acpi_bus_attach, first_pass);
if (!skip && device->handler && device->handler->hotplug.notify_online)
device->handler->hotplug.notify_online(device);
return 0;
}
static int acpi_dev_get_first_consumer_dev_cb(struct acpi_dep_data *dep, void *data)
@ -2274,7 +2259,7 @@ static void acpi_scan_clear_dep_fn(struct work_struct *work)
cdw = container_of(work, struct acpi_scan_clear_dep_work, work);
acpi_scan_lock_acquire();
acpi_bus_attach(cdw->adev, true);
acpi_bus_attach(cdw->adev, (void *)true);
acpi_scan_lock_release();
acpi_dev_put(cdw->adev);
@ -2432,7 +2417,7 @@ int acpi_bus_scan(acpi_handle handle)
if (!device)
return -ENODEV;
acpi_bus_attach(device, true);
acpi_bus_attach(device, (void *)true);
if (!acpi_bus_scan_second_pass)
return 0;
@ -2446,25 +2431,17 @@ int acpi_bus_scan(acpi_handle handle)
acpi_bus_check_add_2, NULL, NULL,
(void **)&device);
acpi_bus_attach(device, false);
acpi_bus_attach(device, NULL);
return 0;
}
EXPORT_SYMBOL(acpi_bus_scan);
/**
* acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects.
* @adev: Root of the ACPI namespace scope to walk.
*
* Must be called under acpi_scan_lock.
*/
void acpi_bus_trim(struct acpi_device *adev)
static int acpi_bus_trim_one(struct acpi_device *adev, void *not_used)
{
struct acpi_scan_handler *handler = adev->handler;
struct acpi_device *child;
list_for_each_entry_reverse(child, &adev->children, node)
acpi_bus_trim(child);
acpi_dev_for_each_child_reverse(adev, acpi_bus_trim_one, NULL);
adev->flags.match_driver = false;
if (handler) {
@ -2482,6 +2459,19 @@ void acpi_bus_trim(struct acpi_device *adev)
acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
adev->flags.initialized = false;
acpi_device_clear_enumerated(adev);
return 0;
}
/**
* acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects.
* @adev: Root of the ACPI namespace scope to walk.
*
* Must be called under acpi_scan_lock.
*/
void acpi_bus_trim(struct acpi_device *adev)
{
acpi_bus_trim_one(adev, NULL);
}
EXPORT_SYMBOL_GPL(acpi_bus_trim);

View file

@ -379,7 +379,7 @@ static void hisi_lpc_acpi_fixup_child_resource(struct device *hostdev,
/*
* hisi_lpc_acpi_set_io_res - set the resources for a child
* @child: the device node to be updated the I/O resource
* @adev: ACPI companion of the device node to be updated the I/O resource
* @hostdev: the device node associated with host controller
* @res: double pointer to be set to the address of translated resources
* @num_res: pointer to variable to hold the number of translated resources
@ -390,31 +390,24 @@ static void hisi_lpc_acpi_fixup_child_resource(struct device *hostdev,
* host-relative address resource. This function will return the translated
* logical PIO addresses for each child devices resources.
*/
static int hisi_lpc_acpi_set_io_res(struct device *child,
static int hisi_lpc_acpi_set_io_res(struct acpi_device *adev,
struct device *hostdev,
const struct resource **res, int *num_res)
{
struct acpi_device *adev;
struct acpi_device *host;
struct acpi_device *host = to_acpi_device(adev->dev.parent);
struct resource_entry *rentry;
LIST_HEAD(resource_list);
struct resource *resources;
int count;
int i;
if (!child || !hostdev)
return -EINVAL;
host = to_acpi_device(hostdev);
adev = to_acpi_device(child);
if (!adev->status.present) {
dev_dbg(child, "device is not present\n");
dev_dbg(&adev->dev, "device is not present\n");
return -EIO;
}
if (acpi_device_enumerated(adev)) {
dev_dbg(child, "has been enumerated\n");
dev_dbg(&adev->dev, "has been enumerated\n");
return -EIO;
}
@ -425,7 +418,7 @@ static int hisi_lpc_acpi_set_io_res(struct device *child,
*/
count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
if (count <= 0) {
dev_dbg(child, "failed to get resources\n");
dev_dbg(&adev->dev, "failed to get resources\n");
return count ? count : -EIO;
}
@ -454,7 +447,7 @@ static int hisi_lpc_acpi_set_io_res(struct device *child,
continue;
ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]);
if (ret) {
dev_err(child, "translate IO range %pR failed (%d)\n",
dev_err(&adev->dev, "translate IO range %pR failed (%d)\n",
&resources[i], ret);
return ret;
}
@ -471,6 +464,12 @@ static int hisi_lpc_acpi_remove_subdev(struct device *dev, void *unused)
return 0;
}
static int hisi_lpc_acpi_clear_enumerated(struct acpi_device *adev, void *not_used)
{
acpi_device_clear_enumerated(adev);
return 0;
}
struct hisi_lpc_acpi_cell {
const char *hid;
const char *name;
@ -480,13 +479,92 @@ struct hisi_lpc_acpi_cell {
static void hisi_lpc_acpi_remove(struct device *hostdev)
{
struct acpi_device *adev = ACPI_COMPANION(hostdev);
struct acpi_device *child;
device_for_each_child(hostdev, NULL, hisi_lpc_acpi_remove_subdev);
acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
hisi_lpc_acpi_clear_enumerated, NULL);
}
list_for_each_entry(child, &adev->children, node)
acpi_device_clear_enumerated(child);
static int hisi_lpc_acpi_add_child(struct acpi_device *child, void *data)
{
const char *hid = acpi_device_hid(child);
struct device *hostdev = data;
const struct hisi_lpc_acpi_cell *cell;
struct platform_device *pdev;
const struct resource *res;
bool found = false;
int num_res;
int ret;
ret = hisi_lpc_acpi_set_io_res(child, hostdev, &res, &num_res);
if (ret) {
dev_warn(hostdev, "set resource fail (%d)\n", ret);
return ret;
}
cell = (struct hisi_lpc_acpi_cell []){
/* ipmi */
{
.hid = "IPI0001",
.name = "hisi-lpc-ipmi",
},
/* 8250-compatible uart */
{
.hid = "HISI1031",
.name = "serial8250",
.pdata = (struct plat_serial8250_port []) {
{
.iobase = res->start,
.uartclk = 1843200,
.iotype = UPIO_PORT,
.flags = UPF_BOOT_AUTOCONF,
},
{}
},
.pdata_size = 2 *
sizeof(struct plat_serial8250_port),
},
{}
};
for (; cell && cell->name; cell++) {
if (!strcmp(cell->hid, hid)) {
found = true;
break;
}
}
if (!found) {
dev_warn(hostdev,
"could not find cell for child device (%s), discarding\n",
hid);
return 0;
}
pdev = platform_device_alloc(cell->name, PLATFORM_DEVID_AUTO);
if (!pdev)
return -ENOMEM;
pdev->dev.parent = hostdev;
ACPI_COMPANION_SET(&pdev->dev, child);
ret = platform_device_add_resources(pdev, res, num_res);
if (ret)
goto fail;
ret = platform_device_add_data(pdev, cell->pdata, cell->pdata_size);
if (ret)
goto fail;
ret = platform_device_add(pdev);
if (ret)
goto fail;
acpi_device_set_enumerated(child);
return 0;
fail:
platform_device_put(pdev);
return ret;
}
/*
@ -501,94 +579,14 @@ static void hisi_lpc_acpi_remove(struct device *hostdev)
*/
static int hisi_lpc_acpi_probe(struct device *hostdev)
{
struct acpi_device *adev = ACPI_COMPANION(hostdev);
struct acpi_device *child;
int ret;
/* Only consider the children of the host */
list_for_each_entry(child, &adev->children, node) {
const char *hid = acpi_device_hid(child);
const struct hisi_lpc_acpi_cell *cell;
struct platform_device *pdev;
const struct resource *res;
bool found = false;
int num_res;
ret = acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
hisi_lpc_acpi_add_child, hostdev);
if (ret)
hisi_lpc_acpi_remove(hostdev);
ret = hisi_lpc_acpi_set_io_res(&child->dev, &adev->dev, &res,
&num_res);
if (ret) {
dev_warn(hostdev, "set resource fail (%d)\n", ret);
goto fail;
}
cell = (struct hisi_lpc_acpi_cell []){
/* ipmi */
{
.hid = "IPI0001",
.name = "hisi-lpc-ipmi",
},
/* 8250-compatible uart */
{
.hid = "HISI1031",
.name = "serial8250",
.pdata = (struct plat_serial8250_port []) {
{
.iobase = res->start,
.uartclk = 1843200,
.iotype = UPIO_PORT,
.flags = UPF_BOOT_AUTOCONF,
},
{}
},
.pdata_size = 2 *
sizeof(struct plat_serial8250_port),
},
{}
};
for (; cell && cell->name; cell++) {
if (!strcmp(cell->hid, hid)) {
found = true;
break;
}
}
if (!found) {
dev_warn(hostdev,
"could not find cell for child device (%s), discarding\n",
hid);
continue;
}
pdev = platform_device_alloc(cell->name, PLATFORM_DEVID_AUTO);
if (!pdev) {
ret = -ENOMEM;
goto fail;
}
pdev->dev.parent = hostdev;
ACPI_COMPANION_SET(&pdev->dev, child);
ret = platform_device_add_resources(pdev, res, num_res);
if (ret)
goto fail;
ret = platform_device_add_data(pdev, cell->pdata,
cell->pdata_size);
if (ret)
goto fail;
ret = platform_device_add(pdev);
if (ret)
goto fail;
acpi_device_set_enumerated(child);
}
return 0;
fail:
hisi_lpc_acpi_remove(hostdev);
return ret;
}

View file

@ -60,12 +60,29 @@ int mfd_cell_disable(struct platform_device *pdev)
EXPORT_SYMBOL(mfd_cell_disable);
#if IS_ENABLED(CONFIG_ACPI)
struct match_ids_walk_data {
struct acpi_device_id *ids;
struct acpi_device *adev;
};
static int match_device_ids(struct acpi_device *adev, void *data)
{
struct match_ids_walk_data *wd = data;
if (!acpi_match_device_ids(adev, wd->ids)) {
wd->adev = adev;
return 1;
}
return 0;
}
static void mfd_acpi_add_device(const struct mfd_cell *cell,
struct platform_device *pdev)
{
const struct mfd_cell_acpi_match *match = cell->acpi_match;
struct acpi_device *parent, *child;
struct acpi_device *adev = NULL;
struct acpi_device *parent;
parent = ACPI_COMPANION(pdev->dev.parent);
if (!parent)
@ -83,14 +100,14 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
if (match) {
if (match->pnpid) {
struct acpi_device_id ids[2] = {};
struct match_ids_walk_data wd = {
.adev = NULL,
.ids = ids,
};
strlcpy(ids[0].id, match->pnpid, sizeof(ids[0].id));
list_for_each_entry(child, &parent->children, node) {
if (!acpi_match_device_ids(child, ids)) {
adev = child;
break;
}
}
acpi_dev_for_each_child(parent, match_device_ids, &wd);
adev = wd.adev;
} else {
adev = acpi_find_child_device(parent, match->adr, false);
}

View file

@ -775,8 +775,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct sdhci_acpi_slot *slot;
struct acpi_device *device, *child;
const struct dmi_system_id *id;
struct acpi_device *device;
struct sdhci_acpi_host *c;
struct sdhci_host *host;
struct resource *iomem;
@ -796,10 +796,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
slot = sdhci_acpi_get_slot(device);
/* Power on the SDHCI controller and its children */
acpi_device_fix_up_power(device);
list_for_each_entry(child, &device->children, node)
if (child->status.present && child->status.enabled)
acpi_device_fix_up_power(child);
acpi_device_fix_up_power_extended(device);
if (sdhci_acpi_byt_defer(dev))
return -EPROBE_DEFER;

View file

@ -1240,16 +1240,11 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
#ifdef CONFIG_ACPI
static void intel_mrfld_mmc_fix_up_power_slot(struct sdhci_pci_slot *slot)
{
struct acpi_device *device, *child;
struct acpi_device *device;
device = ACPI_COMPANION(&slot->chip->pdev->dev);
if (!device)
return;
acpi_device_fix_up_power(device);
list_for_each_entry(child, &device->children, node)
if (child->status.present && child->status.enabled)
acpi_device_fix_up_power(child);
if (device)
acpi_device_fix_up_power_extended(device);
}
#else
static inline void intel_mrfld_mmc_fix_up_power_slot(struct sdhci_pci_slot *slot) {}

View file

@ -6842,6 +6842,31 @@ static const struct backlight_ops ibm_backlight_data = {
/* --------------------------------------------------------------------- */
static int __init tpacpi_evaluate_bcl(struct acpi_device *adev, void *not_used)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
int rc;
status = acpi_evaluate_object(adev->handle, "_BCL", NULL, &buffer);
if (ACPI_FAILURE(status))
return 0;
obj = buffer.pointer;
if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
acpi_handle_info(adev->handle,
"Unknown _BCL data, please report this to %s\n",
TPACPI_MAIL);
rc = 0;
} else {
rc = obj->package.count;
}
kfree(obj);
return rc;
}
/*
* Call _BCL method of video device. On some ThinkPads this will
* switch the firmware to the ACPI brightness control mode.
@ -6849,37 +6874,13 @@ static const struct backlight_ops ibm_backlight_data = {
static int __init tpacpi_query_bcl_levels(acpi_handle handle)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
struct acpi_device *device, *child;
int rc;
struct acpi_device *device;
device = acpi_fetch_acpi_dev(handle);
if (!device)
return 0;
rc = 0;
list_for_each_entry(child, &device->children, node) {
acpi_status status = acpi_evaluate_object(child->handle, "_BCL",
NULL, &buffer);
if (ACPI_FAILURE(status)) {
buffer.length = ACPI_ALLOCATE_BUFFER;
continue;
}
obj = (union acpi_object *)buffer.pointer;
if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
pr_err("Unknown _BCL data, please report this to %s\n",
TPACPI_MAIL);
rc = 0;
} else {
rc = obj->package.count;
}
break;
}
kfree(buffer.pointer);
return rc;
return acpi_dev_for_each_child(device, tpacpi_evaluate_bcl, NULL);
}

View file

@ -127,6 +127,71 @@ static bool find_slave(struct sdw_bus *bus,
return true;
}
struct sdw_acpi_child_walk_data {
struct sdw_bus *bus;
struct acpi_device *adev;
struct sdw_slave_id id;
bool ignore_unique_id;
};
static int sdw_acpi_check_duplicate(struct acpi_device *adev, void *data)
{
struct sdw_acpi_child_walk_data *cwd = data;
struct sdw_bus *bus = cwd->bus;
struct sdw_slave_id id;
if (adev == cwd->adev)
return 0;
if (!find_slave(bus, adev, &id))
return 0;
if (cwd->id.sdw_version != id.sdw_version || cwd->id.mfg_id != id.mfg_id ||
cwd->id.part_id != id.part_id || cwd->id.class_id != id.class_id)
return 0;
if (cwd->id.unique_id != id.unique_id) {
dev_dbg(bus->dev,
"Valid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n",
cwd->id.unique_id, id.unique_id, cwd->id.mfg_id,
cwd->id.part_id);
cwd->ignore_unique_id = false;
return 0;
}
dev_err(bus->dev,
"Invalid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n",
cwd->id.unique_id, id.unique_id, cwd->id.mfg_id, cwd->id.part_id);
return -ENODEV;
}
static int sdw_acpi_find_one(struct acpi_device *adev, void *data)
{
struct sdw_bus *bus = data;
struct sdw_acpi_child_walk_data cwd = {
.bus = bus,
.adev = adev,
.ignore_unique_id = true,
};
int ret;
if (!find_slave(bus, adev, &cwd.id))
return 0;
/* Brute-force O(N^2) search for duplicates. */
ret = acpi_dev_for_each_child(ACPI_COMPANION(bus->dev),
sdw_acpi_check_duplicate, &cwd);
if (ret)
return ret;
if (cwd.ignore_unique_id)
cwd.id.unique_id = SDW_IGNORED_UNIQUE_ID;
/* Ignore errors and continue. */
sdw_slave_add(bus, &cwd.id, acpi_fwnode_handle(adev));
return 0;
}
/*
* sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node
* @bus: SDW bus instance
@ -135,8 +200,7 @@ static bool find_slave(struct sdw_bus *bus,
*/
int sdw_acpi_find_slaves(struct sdw_bus *bus)
{
struct acpi_device *adev, *parent;
struct acpi_device *adev2, *parent2;
struct acpi_device *parent;
parent = ACPI_COMPANION(bus->dev);
if (!parent) {
@ -144,54 +208,7 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus)
return -ENODEV;
}
list_for_each_entry(adev, &parent->children, node) {
struct sdw_slave_id id;
struct sdw_slave_id id2;
bool ignore_unique_id = true;
if (!find_slave(bus, adev, &id))
continue;
/* brute-force O(N^2) search for duplicates */
parent2 = parent;
list_for_each_entry(adev2, &parent2->children, node) {
if (adev == adev2)
continue;
if (!find_slave(bus, adev2, &id2))
continue;
if (id.sdw_version != id2.sdw_version ||
id.mfg_id != id2.mfg_id ||
id.part_id != id2.part_id ||
id.class_id != id2.class_id)
continue;
if (id.unique_id != id2.unique_id) {
dev_dbg(bus->dev,
"Valid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n",
id.unique_id, id2.unique_id, id.mfg_id, id.part_id);
ignore_unique_id = false;
} else {
dev_err(bus->dev,
"Invalid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n",
id.unique_id, id2.unique_id, id.mfg_id, id.part_id);
return -ENODEV;
}
}
if (ignore_unique_id)
id.unique_id = SDW_IGNORED_UNIQUE_ID;
/*
* don't error check for sdw_slave_add as we want to continue
* adding Slaves
*/
sdw_slave_add(bus, &id, acpi_fwnode_handle(adev));
}
return 0;
return acpi_dev_for_each_child(parent, sdw_acpi_find_one, bus);
}
#endif

View file

@ -301,37 +301,22 @@ static bool tb_acpi_bus_match(struct device *dev)
return tb_is_switch(dev) || tb_is_usb4_port_device(dev);
}
static struct acpi_device *tb_acpi_find_port(struct acpi_device *adev,
const struct tb_port *port)
{
struct acpi_device *port_adev;
if (!adev)
return NULL;
/*
* Device routers exists under the downstream facing USB4 port
* of the parent router. Their _ADR is always 0.
*/
list_for_each_entry(port_adev, &adev->children, node) {
if (acpi_device_adr(port_adev) == port->port)
return port_adev;
}
return NULL;
}
static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw)
{
struct acpi_device *adev = NULL;
struct tb_switch *parent_sw;
/*
* Device routers exists under the downstream facing USB4 port
* of the parent router. Their _ADR is always 0.
*/
parent_sw = tb_switch_parent(sw);
if (parent_sw) {
struct tb_port *port = tb_port_at(tb_route(sw), parent_sw);
struct acpi_device *port_adev;
port_adev = tb_acpi_find_port(ACPI_COMPANION(&parent_sw->dev), port);
port_adev = acpi_find_child_by_adr(ACPI_COMPANION(&parent_sw->dev),
port->port);
if (port_adev)
adev = acpi_find_child_device(port_adev, 0, false);
} else {
@ -364,8 +349,8 @@ static struct acpi_device *tb_acpi_find_companion(struct device *dev)
if (tb_is_switch(dev))
return tb_acpi_switch_find_companion(tb_to_switch(dev));
else if (tb_is_usb4_port_device(dev))
return tb_acpi_find_port(ACPI_COMPANION(dev->parent),
tb_to_usb4_port_device(dev)->port);
return acpi_find_child_by_adr(ACPI_COMPANION(dev->parent),
tb_to_usb4_port_device(dev)->port->port);
return NULL;
}

View file

@ -124,22 +124,6 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
*/
#define USB_ACPI_LOCATION_VALID (1 << 31)
static struct acpi_device *usb_acpi_find_port(struct acpi_device *parent,
int raw)
{
struct acpi_device *adev;
if (!parent)
return NULL;
list_for_each_entry(adev, &parent->children, node) {
if (acpi_device_adr(adev) == raw)
return adev;
}
return acpi_find_child_device(parent, raw, false);
}
static struct acpi_device *
usb_acpi_get_companion_for_port(struct usb_port *port_dev)
{
@ -170,7 +154,7 @@ usb_acpi_get_companion_for_port(struct usb_port *port_dev)
port1 = port_dev->portnum;
}
return usb_acpi_find_port(adev, port1);
return acpi_find_child_by_adr(adev, port1);
}
static struct acpi_device *

View file

@ -365,8 +365,6 @@ struct acpi_device {
acpi_handle handle; /* no handle for fixed hardware */
struct fwnode_handle fwnode;
struct acpi_device *parent;
struct list_head children;
struct list_head node;
struct list_head wakeup_list;
struct list_head del_list;
struct acpi_device_status status;
@ -379,7 +377,6 @@ struct acpi_device {
struct acpi_device_data data;
struct acpi_scan_handler *handler;
struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
const struct acpi_gpio_mapping *driver_gpios;
void *driver_data;
struct device dev;
@ -483,6 +480,9 @@ extern struct bus_type acpi_bus_type;
int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data);
int acpi_dev_for_each_child(struct acpi_device *adev,
int (*fn)(struct acpi_device *, void *), void *data);
int acpi_dev_for_each_child_reverse(struct acpi_device *adev,
int (*fn)(struct acpi_device *, void *),
void *data);
/*
* Events
@ -521,6 +521,7 @@ const char *acpi_power_state_string(int state);
int acpi_device_set_power(struct acpi_device *device, int state);
int acpi_bus_init_power(struct acpi_device *device);
int acpi_device_fix_up_power(struct acpi_device *device);
void acpi_device_fix_up_power_extended(struct acpi_device *adev);
int acpi_bus_update_power(acpi_handle handle, int *state_p);
int acpi_device_update_power(struct acpi_device *device, int *state_p);
bool acpi_bus_power_manageable(acpi_handle handle);
@ -622,6 +623,8 @@ static inline int acpi_dma_configure(struct device *dev,
}
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
u64 address, bool check_children);
struct acpi_device *acpi_find_child_by_adr(struct acpi_device *adev,
acpi_bus_address adr);
int acpi_is_root_bridge(acpi_handle);
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle);