1da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett/* 2da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett * USB-ACPI glue code 3da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett * 4da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett * Copyright 2012 Red Hat <mjg@redhat.com> 5da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett * 6da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett * This program is free software; you can redistribute it and/or modify it 7da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett * under the terms of the GNU General Public License as published by the Free 8da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett * Software Foundation, version 2. 9da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett * 10da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett */ 11da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett#include <linux/module.h> 12da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett#include <linux/usb.h> 13da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett#include <linux/device.h> 14da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett#include <linux/errno.h> 15da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett#include <linux/kernel.h> 16da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett#include <linux/acpi.h> 17da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett#include <linux/pci.h> 18bafcaf6d84b5d1bf92dabd1ffe7753ed36b7552eLan Tianyu#include <linux/usb/hcd.h> 19da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett 20d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams#include "hub.h" 21da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett 22f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu/** 23f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * usb_acpi_power_manageable - check whether usb port has 24f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * acpi power resource. 25f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * @hdev: USB device belonging to the usb hub 26f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * @index: port index based zero 27f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * 28f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * Return true if the port has acpi power resource and false if no. 29f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu */ 30f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyubool usb_acpi_power_manageable(struct usb_device *hdev, int index) 31f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu{ 32f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu acpi_handle port_handle; 33f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu int port1 = index + 1; 34f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu 35f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu port_handle = usb_get_hub_port_acpi_handle(hdev, 36f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu port1); 37f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu if (port_handle) 38f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu return acpi_bus_power_manageable(port_handle); 39f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu else 40f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu return false; 41f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu} 42f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan TianyuEXPORT_SYMBOL_GPL(usb_acpi_power_manageable); 43f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu 44f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu/** 45f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * usb_acpi_set_power_state - control usb port's power via acpi power 46f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * resource 47f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * @hdev: USB device belonging to the usb hub 48f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * @index: port index based zero 49f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * @enable: power state expected to be set 50f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * 51f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * Notice to use usb_acpi_power_manageable() to check whether the usb port 52f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * has acpi power resource before invoking this function. 53f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * 54f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu * Returns 0 on success, else negative errno. 55f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu */ 56f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyuint usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable) 57f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu{ 58d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams struct usb_hub *hub = usb_hub_to_struct_hub(hdev); 59d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams struct usb_port *port_dev; 60f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu acpi_handle port_handle; 61f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu unsigned char state; 62f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu int port1 = index + 1; 63f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu int error = -EINVAL; 64f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu 65d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams if (!hub) 66d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams return -ENODEV; 67d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams port_dev = hub->ports[port1 - 1]; 68d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams 69d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams port_handle = (acpi_handle) usb_get_hub_port_acpi_handle(hdev, port1); 70f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu if (!port_handle) 71f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu return error; 72f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu 73f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu if (enable) 74f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu state = ACPI_STATE_D0; 75f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu else 76f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu state = ACPI_STATE_D3_COLD; 77f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu 78f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu error = acpi_bus_set_power(port_handle, state); 79f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu if (!error) 80d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams dev_dbg(&port_dev->dev, "acpi: power was set to %d\n", enable); 81f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu else 82d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams dev_dbg(&port_dev->dev, "acpi: power failed to be set\n"); 83f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu 84f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu return error; 85f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu} 86f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan TianyuEXPORT_SYMBOL_GPL(usb_acpi_set_power_state); 87f7ac7787ad361e31a7972e2854ed8dc2eedfac3bLan Tianyu 883bfd659baec822f54e4acb0734669e671d853a35Dan Williamsstatic enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle, 893bfd659baec822f54e4acb0734669e671d853a35Dan Williams struct acpi_pld_info *pld) 9054d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett{ 91d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN; 9254d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 93d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams union acpi_object *upc; 94d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams acpi_status status; 95d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams 9605f916894a692f0cc0973aef21521133623b21c0Lan Tianyu /* 97025d44309f92bd5e3d1b2c7fab66836ab25b541bRahul Bedarkar * According to ACPI Spec 9.13. PLD indicates whether usb port is 9805f916894a692f0cc0973aef21521133623b21c0Lan Tianyu * user visible and _UPC indicates whether it is connectable. If 9905f916894a692f0cc0973aef21521133623b21c0Lan Tianyu * the port was visible and connectable, it could be freely connected 10005f916894a692f0cc0973aef21521133623b21c0Lan Tianyu * and disconnected with USB devices. If no visible and connectable, 10105f916894a692f0cc0973aef21521133623b21c0Lan Tianyu * a usb device is directly hard-wired to the port. If no visible and 10205f916894a692f0cc0973aef21521133623b21c0Lan Tianyu * no connectable, the port would be not used. 10305f916894a692f0cc0973aef21521133623b21c0Lan Tianyu */ 10405f916894a692f0cc0973aef21521133623b21c0Lan Tianyu status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); 10554d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett upc = buffer.pointer; 10654d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett if (!upc || (upc->type != ACPI_TYPE_PACKAGE) 10754d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett || upc->package.count != 4) { 10854d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett goto out; 10954d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett } 11054d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett 11154d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett if (upc->package.elements[0].integer.value) 112d8dc91b753b881c60c766c06aeec87675a07df4aLinus Torvalds if (pld->user_visible) 113d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG; 11405f916894a692f0cc0973aef21521133623b21c0Lan Tianyu else 115d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED; 116d8dc91b753b881c60c766c06aeec87675a07df4aLinus Torvalds else if (!pld->user_visible) 117d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams connect_type = USB_PORT_NOT_USED; 11854d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrettout: 11954d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett kfree(upc); 1203bfd659baec822f54e4acb0734669e671d853a35Dan Williams return connect_type; 12154d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett} 12254d3f8c63d6940966217b807972778fb17c3fa82Matthew Garrett 1233bfd659baec822f54e4acb0734669e671d853a35Dan Williams 1243bfd659baec822f54e4acb0734669e671d853a35Dan Williams/* 1253bfd659baec822f54e4acb0734669e671d853a35Dan Williams * Private to usb-acpi, all the core needs to know is that 1263bfd659baec822f54e4acb0734669e671d853a35Dan Williams * port_dev->location is non-zero when it has been set by the firmware. 1273bfd659baec822f54e4acb0734669e671d853a35Dan Williams */ 1283bfd659baec822f54e4acb0734669e671d853a35Dan Williams#define USB_ACPI_LOCATION_VALID (1 << 31) 1293bfd659baec822f54e4acb0734669e671d853a35Dan Williams 130e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysockistatic struct acpi_device *usb_acpi_find_companion(struct device *dev) 131da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett{ 132da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett struct usb_device *udev; 133a4204ff0bd576fc114357eed70e7c4e776ddf396Dan Williams struct acpi_device *adev; 134da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett acpi_handle *parent_handle; 135da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett 136d557542421da643358201664903e67fd01dfca1aLan Tianyu /* 137d557542421da643358201664903e67fd01dfca1aLan Tianyu * In the ACPI DSDT table, only usb root hub and usb ports are 138d557542421da643358201664903e67fd01dfca1aLan Tianyu * acpi device nodes. The hierarchy like following. 139d557542421da643358201664903e67fd01dfca1aLan Tianyu * Device (EHC1) 140d557542421da643358201664903e67fd01dfca1aLan Tianyu * Device (HUBN) 141d557542421da643358201664903e67fd01dfca1aLan Tianyu * Device (PR01) 142d557542421da643358201664903e67fd01dfca1aLan Tianyu * Device (PR11) 143d557542421da643358201664903e67fd01dfca1aLan Tianyu * Device (PR12) 144d557542421da643358201664903e67fd01dfca1aLan Tianyu * Device (PR13) 145d557542421da643358201664903e67fd01dfca1aLan Tianyu * ... 146d557542421da643358201664903e67fd01dfca1aLan Tianyu * So all binding process is divided into two parts. binding 147d557542421da643358201664903e67fd01dfca1aLan Tianyu * root hub and usb ports. 148d557542421da643358201664903e67fd01dfca1aLan Tianyu */ 149d557542421da643358201664903e67fd01dfca1aLan Tianyu if (is_usb_device(dev)) { 150d557542421da643358201664903e67fd01dfca1aLan Tianyu udev = to_usb_device(dev); 151a4204ff0bd576fc114357eed70e7c4e776ddf396Dan Williams if (udev->parent) 152e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki return NULL; 15305f916894a692f0cc0973aef21521133623b21c0Lan Tianyu 154a4204ff0bd576fc114357eed70e7c4e776ddf396Dan Williams /* root hub is only child (_ADR=0) under its parent, the HC */ 155a4204ff0bd576fc114357eed70e7c4e776ddf396Dan Williams adev = ACPI_COMPANION(dev->parent); 156a4204ff0bd576fc114357eed70e7c4e776ddf396Dan Williams return acpi_find_child_device(adev, 0, false); 157d557542421da643358201664903e67fd01dfca1aLan Tianyu } else if (is_usb_port(dev)) { 158d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams struct usb_port *port_dev = to_usb_port(dev); 159a4204ff0bd576fc114357eed70e7c4e776ddf396Dan Williams int port1 = port_dev->portnum; 1603bfd659baec822f54e4acb0734669e671d853a35Dan Williams struct acpi_pld_info *pld; 1613bfd659baec822f54e4acb0734669e671d853a35Dan Williams acpi_handle *handle; 1623bfd659baec822f54e4acb0734669e671d853a35Dan Williams acpi_status status; 163e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki 164d557542421da643358201664903e67fd01dfca1aLan Tianyu /* Get the struct usb_device point of port's hub */ 165d557542421da643358201664903e67fd01dfca1aLan Tianyu udev = to_usb_device(dev->parent->parent); 166d557542421da643358201664903e67fd01dfca1aLan Tianyu 167d557542421da643358201664903e67fd01dfca1aLan Tianyu /* 168d557542421da643358201664903e67fd01dfca1aLan Tianyu * The root hub ports' parent is the root hub. The non-root-hub 169d557542421da643358201664903e67fd01dfca1aLan Tianyu * ports' parent is the parent hub port which the hub is 170d557542421da643358201664903e67fd01dfca1aLan Tianyu * connected to. 171d557542421da643358201664903e67fd01dfca1aLan Tianyu */ 172d557542421da643358201664903e67fd01dfca1aLan Tianyu if (!udev->parent) { 173bafcaf6d84b5d1bf92dabd1ffe7753ed36b7552eLan Tianyu struct usb_hcd *hcd = bus_to_hcd(udev->bus); 174d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams int raw; 175bafcaf6d84b5d1bf92dabd1ffe7753ed36b7552eLan Tianyu 176d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams raw = usb_hcd_find_raw_port_number(hcd, port1); 177e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev), 178d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams raw, false); 179e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki if (!adev) 180e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki return NULL; 181d557542421da643358201664903e67fd01dfca1aLan Tianyu } else { 182d557542421da643358201664903e67fd01dfca1aLan Tianyu parent_handle = 183d557542421da643358201664903e67fd01dfca1aLan Tianyu usb_get_hub_port_acpi_handle(udev->parent, 184d557542421da643358201664903e67fd01dfca1aLan Tianyu udev->portnum); 185d557542421da643358201664903e67fd01dfca1aLan Tianyu if (!parent_handle) 186e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki return NULL; 187d557542421da643358201664903e67fd01dfca1aLan Tianyu 188e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki acpi_bus_get_device(parent_handle, &adev); 189d99f6b41308779244662109a9c2bad09a82e8ac6Dan Williams adev = acpi_find_child_device(adev, port1, false); 190e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki if (!adev) 191e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki return NULL; 192d557542421da643358201664903e67fd01dfca1aLan Tianyu } 1933bfd659baec822f54e4acb0734669e671d853a35Dan Williams handle = adev->handle; 1943bfd659baec822f54e4acb0734669e671d853a35Dan Williams status = acpi_get_physical_device_location(handle, &pld); 1953bfd659baec822f54e4acb0734669e671d853a35Dan Williams if (ACPI_FAILURE(status) || !pld) 1963bfd659baec822f54e4acb0734669e671d853a35Dan Williams return adev; 1973bfd659baec822f54e4acb0734669e671d853a35Dan Williams 1983bfd659baec822f54e4acb0734669e671d853a35Dan Williams port_dev->location = USB_ACPI_LOCATION_VALID 1993bfd659baec822f54e4acb0734669e671d853a35Dan Williams | pld->group_token << 8 | pld->group_position; 2003bfd659baec822f54e4acb0734669e671d853a35Dan Williams port_dev->connect_type = usb_acpi_get_connect_type(handle, pld); 2013bfd659baec822f54e4acb0734669e671d853a35Dan Williams ACPI_FREE(pld); 2023bfd659baec822f54e4acb0734669e671d853a35Dan Williams 203e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki return adev; 204e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki } 205da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett 206e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki return NULL; 207da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett} 208da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett 20953540098b23c3884b4a0b4f220b9d977bc496af3Rafael J. Wysockistatic bool usb_acpi_bus_match(struct device *dev) 21053540098b23c3884b4a0b4f220b9d977bc496af3Rafael J. Wysocki{ 21153540098b23c3884b4a0b4f220b9d977bc496af3Rafael J. Wysocki return is_usb_device(dev) || is_usb_port(dev); 21253540098b23c3884b4a0b4f220b9d977bc496af3Rafael J. Wysocki} 21353540098b23c3884b4a0b4f220b9d977bc496af3Rafael J. Wysocki 214da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrettstatic struct acpi_bus_type usb_acpi_bus = { 21553540098b23c3884b4a0b4f220b9d977bc496af3Rafael J. Wysocki .name = "USB", 21653540098b23c3884b4a0b4f220b9d977bc496af3Rafael J. Wysocki .match = usb_acpi_bus_match, 217e3f02c5228c4b600abf6ca243301176f25553bd5Rafael J. Wysocki .find_companion = usb_acpi_find_companion, 218da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett}; 219da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett 220da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrettint usb_acpi_register(void) 221da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett{ 222da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett return register_acpi_bus_type(&usb_acpi_bus); 223da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett} 224da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett 225da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrettvoid usb_acpi_unregister(void) 226da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett{ 227da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett unregister_acpi_bus_type(&usb_acpi_bus); 228da0af6e78ef311d97754aa03e10eade82cc99e16Matthew Garrett} 229