2013-01-11 20:10:38 +08:00
/*
* usb port device code
*
* Copyright ( C ) 2012 Intel Corp
*
* Author : Lan Tianyu < tianyu . lan @ intel . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
*/
2013-01-19 22:30:19 +08:00
# include <linux/slab.h>
2013-01-23 04:26:29 +08:00
# include <linux/pm_qos.h>
2013-01-19 22:30:19 +08:00
2013-01-11 20:10:38 +08:00
# include "hub.h"
2013-01-20 01:53:32 +08:00
static const struct attribute_group * port_dev_group [ ] ;
static ssize_t show_port_connect_type ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct usb_port * port_dev = to_usb_port ( dev ) ;
char * result ;
switch ( port_dev - > connect_type ) {
case USB_PORT_CONNECT_TYPE_HOT_PLUG :
result = " hotplug " ;
break ;
case USB_PORT_CONNECT_TYPE_HARD_WIRED :
result = " hardwired " ;
break ;
case USB_PORT_NOT_USED :
result = " not used " ;
break ;
default :
result = " unknown " ;
break ;
}
return sprintf ( buf , " %s \n " , result ) ;
}
static DEVICE_ATTR ( connect_type , S_IRUGO , show_port_connect_type ,
NULL ) ;
static struct attribute * port_dev_attrs [ ] = {
& dev_attr_connect_type . attr ,
NULL ,
} ;
static struct attribute_group port_dev_attr_grp = {
. attrs = port_dev_attrs ,
} ;
static const struct attribute_group * port_dev_group [ ] = {
& port_dev_attr_grp ,
NULL ,
} ;
2013-01-11 20:10:38 +08:00
static void usb_port_device_release ( struct device * dev )
{
struct usb_port * port_dev = to_usb_port ( dev ) ;
2013-01-23 04:26:31 +08:00
dev_pm_qos_hide_flags ( dev ) ;
2013-01-23 04:26:27 +08:00
usb_acpi_unregister_power_resources ( dev ) ;
2013-01-11 20:10:38 +08:00
kfree ( port_dev ) ;
}
2013-01-23 04:26:29 +08:00
# ifdef CONFIG_USB_SUSPEND
static int usb_port_runtime_resume ( struct device * dev )
{
struct usb_port * port_dev = to_usb_port ( dev ) ;
struct usb_device * hdev = to_usb_device ( dev - > parent - > parent ) ;
struct usb_interface * intf = to_usb_interface ( dev - > parent ) ;
2013-01-23 04:26:30 +08:00
struct usb_hub * hub = usb_hub_to_struct_hub ( hdev ) ;
int port1 = port_dev - > portnum ;
2013-01-23 04:26:29 +08:00
int retval ;
2013-01-23 04:26:30 +08:00
if ( ! hub )
return - EINVAL ;
2013-01-23 04:26:29 +08:00
usb_autopm_get_interface ( intf ) ;
2013-01-23 04:26:30 +08:00
set_bit ( port1 , hub - > busy_bits ) ;
retval = usb_hub_set_port_power ( hdev , port1 , true ) ;
if ( port_dev - > child & & ! retval ) {
/*
* Wait for usb hub port to be reconnected in order to make
* the resume procedure successful .
*/
retval = hub_port_debounce_be_connected ( hub , port1 ) ;
if ( retval < 0 ) {
dev_dbg ( & port_dev - > dev , " can't get reconnection after setting port power on, status %d \n " ,
retval ) ;
goto out ;
}
usb_clear_port_feature ( hdev , port1 , USB_PORT_FEAT_C_ENABLE ) ;
/* Set return value to 0 if debounce successful */
retval = 0 ;
}
out :
clear_bit ( port1 , hub - > busy_bits ) ;
2013-01-23 04:26:29 +08:00
usb_autopm_put_interface ( intf ) ;
return retval ;
}
static int usb_port_runtime_suspend ( struct device * dev )
{
struct usb_port * port_dev = to_usb_port ( dev ) ;
struct usb_device * hdev = to_usb_device ( dev - > parent - > parent ) ;
struct usb_interface * intf = to_usb_interface ( dev - > parent ) ;
2013-01-23 04:26:30 +08:00
struct usb_hub * hub = usb_hub_to_struct_hub ( hdev ) ;
int port1 = port_dev - > portnum ;
2013-01-23 04:26:29 +08:00
int retval ;
2013-01-23 04:26:30 +08:00
if ( ! hub )
return - EINVAL ;
2013-01-23 04:26:29 +08:00
if ( dev_pm_qos_flags ( & port_dev - > dev , PM_QOS_FLAG_NO_POWER_OFF )
= = PM_QOS_FLAGS_ALL )
return - EAGAIN ;
usb_autopm_get_interface ( intf ) ;
2013-01-23 04:26:30 +08:00
set_bit ( port1 , hub - > busy_bits ) ;
retval = usb_hub_set_port_power ( hdev , port1 , false ) ;
usb_clear_port_feature ( hdev , port1 , USB_PORT_FEAT_C_CONNECTION ) ;
usb_clear_port_feature ( hdev , port1 , USB_PORT_FEAT_C_ENABLE ) ;
clear_bit ( port1 , hub - > busy_bits ) ;
2013-01-23 04:26:29 +08:00
usb_autopm_put_interface ( intf ) ;
return retval ;
}
# endif
static const struct dev_pm_ops usb_port_pm_ops = {
# ifdef CONFIG_USB_SUSPEND
. runtime_suspend = usb_port_runtime_suspend ,
. runtime_resume = usb_port_runtime_resume ,
. runtime_idle = pm_generic_runtime_idle ,
# endif
} ;
2013-01-11 20:10:38 +08:00
struct device_type usb_port_device_type = {
. name = " usb_port " ,
. release = usb_port_device_release ,
2013-01-23 04:26:29 +08:00
. pm = & usb_port_pm_ops ,
2013-01-11 20:10:38 +08:00
} ;
int usb_hub_create_port_device ( struct usb_hub * hub , int port1 )
{
struct usb_port * port_dev = NULL ;
int retval ;
port_dev = kzalloc ( sizeof ( * port_dev ) , GFP_KERNEL ) ;
if ( ! port_dev ) {
retval = - ENOMEM ;
goto exit ;
}
hub - > ports [ port1 - 1 ] = port_dev ;
2013-01-23 04:26:29 +08:00
port_dev - > portnum = port1 ;
2013-01-23 04:26:30 +08:00
port_dev - > power_is_on = true ;
2013-01-11 20:10:38 +08:00
port_dev - > dev . parent = hub - > intfdev ;
2013-01-20 01:53:32 +08:00
port_dev - > dev . groups = port_dev_group ;
2013-01-11 20:10:38 +08:00
port_dev - > dev . type = & usb_port_device_type ;
dev_set_name ( & port_dev - > dev , " port%d " , port1 ) ;
retval = device_register ( & port_dev - > dev ) ;
if ( retval )
goto error_register ;
2013-01-23 04:26:29 +08:00
pm_runtime_set_active ( & port_dev - > dev ) ;
2013-01-23 04:26:31 +08:00
/* It would be dangerous if user space couldn't
* prevent usb device from being powered off . So don ' t
* enable port runtime pm if failed to expose port ' s pm qos .
*/
if ( ! dev_pm_qos_expose_flags ( & port_dev - > dev ,
PM_QOS_FLAG_NO_POWER_OFF ) )
pm_runtime_enable ( & port_dev - > dev ) ;
2013-01-23 04:26:29 +08:00
2013-01-23 04:26:27 +08:00
retval = usb_acpi_register_power_resources ( & port_dev - > dev ) ;
if ( retval & & retval ! = - ENODEV )
dev_warn ( & port_dev - > dev , " the port can't register its ACPI power resource. \n " ) ;
2013-01-11 20:10:38 +08:00
return 0 ;
error_register :
put_device ( & port_dev - > dev ) ;
exit :
return retval ;
}
void usb_hub_remove_port_device ( struct usb_hub * hub ,
int port1 )
{
device_unregister ( & hub - > ports [ port1 - 1 ] - > dev ) ;
}