2013-01-11 16:10:38 +04: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 18:30:19 +04:00
# include <linux/slab.h>
2013-01-23 00:26:29 +04:00
# include <linux/pm_qos.h>
2013-01-19 18:30:19 +04:00
2013-01-11 16:10:38 +04:00
# include "hub.h"
2013-01-19 21:53:32 +04: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 16:10:38 +04:00
static void usb_port_device_release ( struct device * dev )
{
struct usb_port * port_dev = to_usb_port ( dev ) ;
2013-01-23 00:26:27 +04:00
usb_acpi_unregister_power_resources ( dev ) ;
2013-01-11 16:10:38 +04:00
kfree ( port_dev ) ;
}
2013-01-23 00:26:29 +04: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 ) ;
int retval ;
usb_autopm_get_interface ( intf ) ;
retval = usb_hub_set_port_power ( hdev , port_dev - > portnum , true ) ;
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 ) ;
int retval ;
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 ) ;
retval = usb_hub_set_port_power ( hdev , port_dev - > portnum , false ) ;
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 16:10:38 +04:00
struct device_type usb_port_device_type = {
. name = " usb_port " ,
. release = usb_port_device_release ,
2013-01-23 00:26:29 +04:00
. pm = & usb_port_pm_ops ,
2013-01-11 16:10:38 +04: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 00:26:29 +04:00
port_dev - > portnum = port1 ;
2013-01-11 16:10:38 +04:00
port_dev - > dev . parent = hub - > intfdev ;
2013-01-19 21:53:32 +04:00
port_dev - > dev . groups = port_dev_group ;
2013-01-11 16:10:38 +04: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 00:26:29 +04:00
pm_runtime_set_active ( & port_dev - > dev ) ;
pm_runtime_enable ( & port_dev - > dev ) ;
2013-01-23 00:26:27 +04: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 16:10:38 +04: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 ) ;
}