diff options
| author | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2012-06-13 11:24:38 +0300 | 
|---|---|---|
| committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2012-06-13 11:24:38 +0300 | 
| commit | 289733ed456f7c1cbdb1f1ca58312f77c239953b (patch) | |
| tree | 99bed0a5c21d0bdc5f6c10bc80a1ae8dbbd56be6 /drivers/usb/core/usb-acpi.c | |
| parent | c3a21fc79b6bc097d8b0e47498903a649a111127 (diff) | |
| parent | cfaf025112d3856637ff34a767ef785ef5cf2ca9 (diff) | |
Merge tag 'v3.5-rc2'
Merge v3.5-rc2 to get latest device tree and dynamic debug changes.
Diffstat (limited to 'drivers/usb/core/usb-acpi.c')
| -rw-r--r-- | drivers/usb/core/usb-acpi.c | 117 | 
1 files changed, 117 insertions, 0 deletions
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c new file mode 100644 index 00000000000..8947b203d5a --- /dev/null +++ b/drivers/usb/core/usb-acpi.c @@ -0,0 +1,117 @@ +/* + * USB-ACPI glue code + * + * Copyright 2012 Red Hat <mjg@redhat.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + */ +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/pci.h> +#include <acpi/acpi_bus.h> + +#include "usb.h" + +static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) +{ +	acpi_status status; +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; +	union acpi_object *upc; +	int ret = 0; + +	status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); + +	if (ACPI_FAILURE(status)) +		return -ENODEV; + +	upc = buffer.pointer; + +	if (!upc || (upc->type != ACPI_TYPE_PACKAGE) +		|| upc->package.count != 4) { +		ret = -EINVAL; +		goto out; +	} + +	if (upc->package.elements[0].integer.value) +		udev->removable = USB_DEVICE_REMOVABLE; +	else +		udev->removable = USB_DEVICE_FIXED; + +out: +	kfree(upc); +	return ret; +} + +static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle) +{ +	acpi_status status; +	struct acpi_pld pld; + +	status = acpi_get_physical_device_location(handle, &pld); + +	if (ACPI_FAILURE(status)) +		return -ENODEV; + +	if (pld.user_visible) +		udev->removable = USB_DEVICE_REMOVABLE; +	else +		udev->removable = USB_DEVICE_FIXED; + +	return 0; +} + +static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) +{ +	struct usb_device *udev; +	struct device *parent; +	acpi_handle *parent_handle; + +	if (!is_usb_device(dev)) +		return -ENODEV; + +	udev = to_usb_device(dev); +	parent = dev->parent; +	parent_handle = DEVICE_ACPI_HANDLE(parent); + +	if (!parent_handle) +		return -ENODEV; + +	*handle = acpi_get_child(parent_handle, udev->portnum); + +	if (!*handle) +		return -ENODEV; + +	/* +	 * PLD will tell us whether a port is removable to the user or +	 * not. If we don't get an answer from PLD (it's not present +	 * or it's malformed) then try to infer it from UPC. If a +	 * device isn't connectable then it's probably not removable. +	 */ +	if (usb_acpi_check_pld(udev, *handle) != 0) +		usb_acpi_check_upc(udev, *handle); + +	return 0; +} + +static struct acpi_bus_type usb_acpi_bus = { +	.bus = &usb_bus_type, +	.find_bridge = NULL, +	.find_device = usb_acpi_find_device, +}; + +int usb_acpi_register(void) +{ +	return register_acpi_bus_type(&usb_acpi_bus); +} + +void usb_acpi_unregister(void) +{ +	unregister_acpi_bus_type(&usb_acpi_bus); +}  | 
