2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0
2012-05-11 16:08:27 +08:00
/*
* USB - ACPI glue code
*
* Copyright 2012 Red Hat < mjg @ redhat . com >
*/
# 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>
2013-03-19 16:48:13 +08:00
# include <linux/usb/hcd.h>
2012-05-11 16:08:27 +08:00
2014-05-20 18:08:17 -07:00
# include "hub.h"
2012-05-11 16:08:27 +08:00
2012-09-05 13:44:36 +08:00
/**
* usb_acpi_power_manageable - check whether usb port has
* acpi power resource .
* @ hdev : USB device belonging to the usb hub
* @ index : port index based zero
*
* Return true if the port has acpi power resource and false if no .
*/
bool usb_acpi_power_manageable ( struct usb_device * hdev , int index )
{
acpi_handle port_handle ;
int port1 = index + 1 ;
port_handle = usb_get_hub_port_acpi_handle ( hdev ,
port1 ) ;
if ( port_handle )
return acpi_bus_power_manageable ( port_handle ) ;
else
return false ;
}
EXPORT_SYMBOL_GPL ( usb_acpi_power_manageable ) ;
/**
* usb_acpi_set_power_state - control usb port ' s power via acpi power
* resource
* @ hdev : USB device belonging to the usb hub
* @ index : port index based zero
* @ enable : power state expected to be set
*
* Notice to use usb_acpi_power_manageable ( ) to check whether the usb port
* has acpi power resource before invoking this function .
*
* Returns 0 on success , else negative errno .
*/
int usb_acpi_set_power_state ( struct usb_device * hdev , int index , bool enable )
{
2014-05-20 18:08:17 -07:00
struct usb_hub * hub = usb_hub_to_struct_hub ( hdev ) ;
struct usb_port * port_dev ;
2012-09-05 13:44:36 +08:00
acpi_handle port_handle ;
unsigned char state ;
int port1 = index + 1 ;
int error = - EINVAL ;
2014-05-20 18:08:17 -07:00
if ( ! hub )
return - ENODEV ;
port_dev = hub - > ports [ port1 - 1 ] ;
port_handle = ( acpi_handle ) usb_get_hub_port_acpi_handle ( hdev , port1 ) ;
2012-09-05 13:44:36 +08:00
if ( ! port_handle )
return error ;
if ( enable )
state = ACPI_STATE_D0 ;
else
state = ACPI_STATE_D3_COLD ;
error = acpi_bus_set_power ( port_handle , state ) ;
if ( ! error )
2014-05-20 18:08:17 -07:00
dev_dbg ( & port_dev - > dev , " acpi: power was set to %d \n " , enable ) ;
2012-09-05 13:44:36 +08:00
else
2014-05-20 18:08:17 -07:00
dev_dbg ( & port_dev - > dev , " acpi: power failed to be set \n " ) ;
2012-09-05 13:44:36 +08:00
return error ;
}
EXPORT_SYMBOL_GPL ( usb_acpi_set_power_state ) ;
2014-05-20 18:08:40 -07:00
static enum usb_port_connect_type usb_acpi_get_connect_type ( acpi_handle handle ,
struct acpi_pld_info * pld )
2012-05-11 16:08:28 +08:00
{
2014-05-20 18:08:17 -07:00
enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN ;
2012-05-11 16:08:28 +08:00
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
2014-05-20 18:08:17 -07:00
union acpi_object * upc ;
acpi_status status ;
2012-09-05 13:44:34 +08:00
/*
2018-03-04 21:57:06 -08:00
* According to 9.14 in ACPI Spec 6.2 . _PLD indicates whether usb port
* is user visible and _UPC indicates whether it is connectable . If
2012-09-05 13:44:34 +08:00
* the port was visible and connectable , it could be freely connected
* and disconnected with USB devices . If no visible and connectable ,
* a usb device is directly hard - wired to the port . If no visible and
* no connectable , the port would be not used .
*/
status = acpi_evaluate_object ( handle , " _UPC " , NULL , & buffer ) ;
2012-05-11 16:08:28 +08:00
upc = buffer . pointer ;
if ( ! upc | | ( upc - > type ! = ACPI_TYPE_PACKAGE )
| | upc - > package . count ! = 4 ) {
goto out ;
}
if ( upc - > package . elements [ 0 ] . integer . value )
2012-10-08 07:14:06 +09:00
if ( pld - > user_visible )
2014-05-20 18:08:17 -07:00
connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG ;
2012-09-05 13:44:34 +08:00
else
2014-05-20 18:08:17 -07:00
connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED ;
2012-10-08 07:14:06 +09:00
else if ( ! pld - > user_visible )
2014-05-20 18:08:17 -07:00
connect_type = USB_PORT_NOT_USED ;
2012-05-11 16:08:28 +08:00
out :
kfree ( upc ) ;
2014-05-20 18:08:40 -07:00
return connect_type ;
2012-05-11 16:08:28 +08:00
}
2014-05-20 18:08:40 -07:00
/*
* Private to usb - acpi , all the core needs to know is that
* port_dev - > location is non - zero when it has been set by the firmware .
*/
# define USB_ACPI_LOCATION_VALID (1 << 31)
2017-06-02 16:36:26 +03:00
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 ) ;
}
2019-01-24 15:28:11 -08:00
static struct acpi_device *
usb_acpi_get_companion_for_port ( struct usb_port * port_dev )
2012-05-11 16:08:27 +08:00
{
struct usb_device * udev ;
2014-05-20 18:08:22 -07:00
struct acpi_device * adev ;
2012-05-11 16:08:27 +08:00
acpi_handle * parent_handle ;
2019-01-24 15:28:11 -08:00
int port1 ;
/* Get the struct usb_device point of port's hub */
udev = to_usb_device ( port_dev - > dev . parent - > parent ) ;
/*
* The root hub ports ' parent is the root hub . The non - root - hub
* ports ' parent is the parent hub port which the hub is
* connected to .
*/
if ( ! udev - > parent ) {
adev = ACPI_COMPANION ( & udev - > dev ) ;
port1 = usb_hcd_find_raw_port_number ( bus_to_hcd ( udev - > bus ) ,
port_dev - > portnum ) ;
} else {
parent_handle = usb_get_hub_port_acpi_handle ( udev - > parent ,
udev - > portnum ) ;
if ( ! parent_handle )
return NULL ;
acpi_bus_get_device ( parent_handle , & adev ) ;
port1 = port_dev - > portnum ;
}
return usb_acpi_find_port ( adev , port1 ) ;
}
static struct acpi_device *
usb_acpi_find_companion_for_port ( struct usb_port * port_dev )
{
struct acpi_device * adev ;
struct acpi_pld_info * pld ;
acpi_handle * handle ;
acpi_status status ;
adev = usb_acpi_get_companion_for_port ( port_dev ) ;
if ( ! adev )
return NULL ;
handle = adev - > handle ;
status = acpi_get_physical_device_location ( handle , & pld ) ;
if ( ! ACPI_FAILURE ( status ) & & pld ) {
port_dev - > location = USB_ACPI_LOCATION_VALID
| pld - > group_token < < 8 | pld - > group_position ;
port_dev - > connect_type = usb_acpi_get_connect_type ( handle , pld ) ;
ACPI_FREE ( pld ) ;
}
2012-05-11 16:08:27 +08:00
2019-01-24 15:28:11 -08:00
return adev ;
}
static struct acpi_device *
usb_acpi_find_companion_for_device ( struct usb_device * udev )
{
struct acpi_device * adev ;
2019-01-24 15:28:12 -08:00
struct usb_port * port_dev ;
struct usb_hub * hub ;
if ( ! udev - > parent ) {
/* root hub is only child (_ADR=0) under its parent, the HC */
adev = ACPI_COMPANION ( udev - > dev . parent ) ;
return acpi_find_child_device ( adev , 0 , false ) ;
}
2019-01-24 15:28:11 -08:00
2019-01-24 15:28:12 -08:00
hub = usb_hub_to_struct_hub ( udev - > parent ) ;
if ( ! hub )
2019-01-24 15:28:11 -08:00
return NULL ;
2019-01-24 15:28:12 -08:00
/*
* This is an embedded USB device connected to a port and such
* devices share port ' s ACPI companion .
*/
port_dev = hub - > ports [ udev - > portnum - 1 ] ;
return usb_acpi_get_companion_for_port ( port_dev ) ;
2019-01-24 15:28:11 -08:00
}
static struct acpi_device * usb_acpi_find_companion ( struct device * dev )
{
2012-09-05 13:44:33 +08:00
/*
2019-01-24 15:28:12 -08:00
* The USB hierarchy like following :
*
2012-09-05 13:44:33 +08:00
* Device ( EHC1 )
* Device ( HUBN )
* Device ( PR01 )
* Device ( PR11 )
* Device ( PR12 )
2019-01-24 15:28:12 -08:00
* Device ( FN12 )
* Device ( FN13 )
2012-09-05 13:44:33 +08:00
* Device ( PR13 )
* . . .
2019-01-24 15:28:12 -08:00
* where HUBN is root hub , and PRNN are USB ports and devices
* connected to them , and FNNN are individualk functions for
* connected composite USB devices . PRNN and FNNN may contain
* _CRS and other methods describing sideband resources for
* the connected device .
*
* On the kernel side both root hub and embedded USB devices are
* represented as instances of usb_device structure , and ports
* are represented as usb_port structures , so the whole process
* is split into 2 parts : finding companions for devices and
* finding companions for ports .
*
* Note that we do not handle individual functions of composite
* devices yet , for that we would need to assign companions to
* devices corresponding to USB interfaces .
2012-09-05 13:44:33 +08:00
*/
2019-01-24 15:28:11 -08:00
if ( is_usb_device ( dev ) )
return usb_acpi_find_companion_for_device ( to_usb_device ( dev ) ) ;
else if ( is_usb_port ( dev ) )
return usb_acpi_find_companion_for_port ( to_usb_port ( dev ) ) ;
2012-05-11 16:08:27 +08:00
2013-11-29 16:27:34 +01:00
return NULL ;
2012-05-11 16:08:27 +08:00
}
2013-03-03 22:35:20 +01:00
static bool usb_acpi_bus_match ( struct device * dev )
{
return is_usb_device ( dev ) | | is_usb_port ( dev ) ;
}
2012-05-11 16:08:27 +08:00
static struct acpi_bus_type usb_acpi_bus = {
2013-03-03 22:35:20 +01:00
. name = " USB " ,
. match = usb_acpi_bus_match ,
2013-11-29 16:27:34 +01:00
. find_companion = usb_acpi_find_companion ,
2012-05-11 16:08:27 +08:00
} ;
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 ) ;
}