@ -3815,6 +3815,56 @@ int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1)
return raw_port ;
}
/*
* Issue an Evaluate Context command to change the Maximum Exit Latency in the
* slot context . If that succeeds , store the new MEL in the xhci_virt_device .
*/
static int xhci_change_max_exit_latency ( struct xhci_hcd * xhci ,
struct usb_device * udev , u16 max_exit_latency )
{
struct xhci_virt_device * virt_dev ;
struct xhci_command * command ;
struct xhci_input_control_ctx * ctrl_ctx ;
struct xhci_slot_ctx * slot_ctx ;
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & xhci - > lock , flags ) ;
if ( max_exit_latency = = xhci - > devs [ udev - > slot_id ] - > current_mel ) {
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
return 0 ;
}
/* Attempt to issue an Evaluate Context command to change the MEL. */
virt_dev = xhci - > devs [ udev - > slot_id ] ;
command = xhci - > lpm_command ;
xhci_slot_copy ( xhci , command - > in_ctx , virt_dev - > out_ctx ) ;
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
ctrl_ctx = xhci_get_input_control_ctx ( xhci , command - > in_ctx ) ;
ctrl_ctx - > add_flags | = cpu_to_le32 ( SLOT_FLAG ) ;
slot_ctx = xhci_get_slot_ctx ( xhci , command - > in_ctx ) ;
slot_ctx - > dev_info2 & = cpu_to_le32 ( ~ ( ( u32 ) MAX_EXIT ) ) ;
slot_ctx - > dev_info2 | = cpu_to_le32 ( max_exit_latency ) ;
xhci_dbg ( xhci , " Set up evaluate context for LPM MEL change. \n " ) ;
xhci_dbg ( xhci , " Slot %u Input Context: \n " , udev - > slot_id ) ;
xhci_dbg_ctx ( xhci , command - > in_ctx , 0 ) ;
/* Issue and wait for the evaluate context command. */
ret = xhci_configure_endpoint ( xhci , udev , command ,
true , true ) ;
xhci_dbg ( xhci , " Slot %u Output Context: \n " , udev - > slot_id ) ;
xhci_dbg_ctx ( xhci , virt_dev - > out_ctx , 0 ) ;
if ( ! ret ) {
spin_lock_irqsave ( & xhci - > lock , flags ) ;
virt_dev - > current_mel = max_exit_latency ;
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
}
return ret ;
}
# ifdef CONFIG_PM_RUNTIME
/* BESL to HIRD Encoding array for USB2 LPM */
@ -3856,6 +3906,28 @@ static int xhci_calculate_hird_besl(struct xhci_hcd *xhci,
return besl ;
}
/* Calculate BESLD, L1 timeout and HIRDM for USB2 PORTHLPMC */
static int xhci_calculate_usb2_hw_lpm_params ( struct usb_device * udev )
{
u32 field ;
int l1 ;
int besld = 0 ;
int hirdm = 0 ;
field = le32_to_cpu ( udev - > bos - > ext_cap - > bmAttributes ) ;
/* xHCI l1 is set in steps of 256us, xHCI 1.0 section 5.4.11.2 */
l1 = XHCI_L1_TIMEOUT / 256 ;
/* device has preferred BESLD */
if ( field & USB_BESL_DEEP_VALID ) {
besld = USB_GET_BESL_DEEP ( field ) ;
hirdm = 1 ;
}
return PORT_BESLD ( besld ) | PORT_L1_TIMEOUT ( l1 ) | PORT_HIRDM ( hirdm ) ;
}
static int xhci_usb2_software_lpm_test ( struct usb_hcd * hcd ,
struct usb_device * udev )
{
@ -3988,11 +4060,12 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
{
struct xhci_hcd * xhci = hcd_to_xhci ( hcd ) ;
__le32 __iomem * * port_array ;
__le32 __iomem * pm_addr ;
u32 temp ;
__le32 __iomem * pm_addr , * hlpm_addr ;
u32 pm_val , hlpm_val , field ;
unsigned int port_num ;
unsigned long flags ;
int hird ;
int hird , exit_latency ;
int ret ;
if ( hcd - > speed = = HCD_USB3 | | ! xhci - > hw_lpm_support | |
! udev - > lpm_capable )
@ -4010,23 +4083,73 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
port_array = xhci - > usb2_ports ;
port_num = udev - > portnum - 1 ;
pm_addr = port_array [ port_num ] + PORTPMSC ;
temp = xhci_readl ( xhci , pm_addr ) ;
pm_val = xhci_readl ( xhci , pm_addr ) ;
hlpm_addr = port_array [ port_num ] + PORTHLPMC ;
field = le32_to_cpu ( udev - > bos - > ext_cap - > bmAttributes ) ;
xhci_dbg ( xhci , " %s port %d USB2 hardware LPM \n " ,
enable ? " enable " : " disable " , port_num ) ;
hird = xhci_calculate_hird_besl ( xhci , udev ) ;
if ( enable ) {
temp & = ~ PORT_HIRD_MASK ;
temp | = PORT_HIRD ( hird ) | PORT_RWE ;
xhci_writel ( xhci , temp , pm_addr ) ;
temp = xhci_readl ( xhci , pm_addr ) ;
temp | = PORT_HLE ;
xhci_writel ( xhci , temp , pm_addr ) ;
/* Host supports BESL timeout instead of HIRD */
if ( udev - > usb2_hw_lpm_besl_capable ) {
/* if device doesn't have a preferred BESL value use a
* default one which works with mixed HIRD and BESL
* systems . See XHCI_DEFAULT_BESL definition in xhci . h
*/
if ( ( field & USB_BESL_SUPPORT ) & &
( field & USB_BESL_BASELINE_VALID ) )
hird = USB_GET_BESL_BASELINE ( field ) ;
else
hird = XHCI_DEFAULT_BESL ;
exit_latency = xhci_besl_encoding [ hird ] ;
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
/* USB 3.0 code dedicate one xhci->lpm_command->in_ctx
* input context for link powermanagement evaluate
* context commands . It is protected by hcd - > bandwidth
* mutex and is shared by all devices . We need to set
* the max ext latency in USB 2 BESL LPM as well , so
* use the same mutex and xhci_change_max_exit_latency ( )
*/
mutex_lock ( hcd - > bandwidth_mutex ) ;
ret = xhci_change_max_exit_latency ( xhci , udev ,
exit_latency ) ;
mutex_unlock ( hcd - > bandwidth_mutex ) ;
if ( ret < 0 )
return ret ;
spin_lock_irqsave ( & xhci - > lock , flags ) ;
hlpm_val = xhci_calculate_usb2_hw_lpm_params ( udev ) ;
xhci_writel ( xhci , hlpm_val , hlpm_addr ) ;
/* flush write */
xhci_readl ( xhci , hlpm_addr ) ;
} else {
hird = xhci_calculate_hird_besl ( xhci , udev ) ;
}
pm_val & = ~ PORT_HIRD_MASK ;
pm_val | = PORT_HIRD ( hird ) | PORT_RWE ;
xhci_writel ( xhci , pm_val , pm_addr ) ;
pm_val = xhci_readl ( xhci , pm_addr ) ;
pm_val | = PORT_HLE ;
xhci_writel ( xhci , pm_val , pm_addr ) ;
/* flush write */
xhci_readl ( xhci , pm_addr ) ;
} else {
temp & = ~ ( PORT_HLE | PORT_RWE | PORT_HIRD_MASK ) ;
xhci_writel ( xhci , temp , pm_addr ) ;
pm_val & = ~ ( PORT_HLE | PORT_RWE | PORT_HIRD_MASK ) ;
xhci_writel ( xhci , pm_val , pm_addr ) ;
/* flush write */
xhci_readl ( xhci , pm_addr ) ;
if ( udev - > usb2_hw_lpm_besl_capable ) {
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
mutex_lock ( hcd - > bandwidth_mutex ) ;
xhci_change_max_exit_latency ( xhci , udev , 0 ) ;
mutex_unlock ( hcd - > bandwidth_mutex ) ;
return 0 ;
}
}
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
@ -4068,6 +4191,9 @@ int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
if ( xhci - > hw_lpm_support = = 1 & &
xhci_check_usb2_port_capability ( xhci , portnum , XHCI_HLC ) ) {
udev - > usb2_hw_lpm_capable = 1 ;
if ( xhci_check_usb2_port_capability ( xhci , portnum ,
XHCI_BLC ) )
udev - > usb2_hw_lpm_besl_capable = 1 ;
ret = xhci_set_usb2_hardware_lpm ( hcd , udev , 1 ) ;
if ( ! ret )
udev - > usb2_hw_lpm_enabled = 1 ;
@ -4398,56 +4524,6 @@ static u16 xhci_calculate_lpm_timeout(struct usb_hcd *hcd,
return timeout ;
}
/*
* Issue an Evaluate Context command to change the Maximum Exit Latency in the
* slot context . If that succeeds , store the new MEL in the xhci_virt_device .
*/
static int xhci_change_max_exit_latency ( struct xhci_hcd * xhci ,
struct usb_device * udev , u16 max_exit_latency )
{
struct xhci_virt_device * virt_dev ;
struct xhci_command * command ;
struct xhci_input_control_ctx * ctrl_ctx ;
struct xhci_slot_ctx * slot_ctx ;
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & xhci - > lock , flags ) ;
if ( max_exit_latency = = xhci - > devs [ udev - > slot_id ] - > current_mel ) {
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
return 0 ;
}
/* Attempt to issue an Evaluate Context command to change the MEL. */
virt_dev = xhci - > devs [ udev - > slot_id ] ;
command = xhci - > lpm_command ;
xhci_slot_copy ( xhci , command - > in_ctx , virt_dev - > out_ctx ) ;
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
ctrl_ctx = xhci_get_input_control_ctx ( xhci , command - > in_ctx ) ;
ctrl_ctx - > add_flags | = cpu_to_le32 ( SLOT_FLAG ) ;
slot_ctx = xhci_get_slot_ctx ( xhci , command - > in_ctx ) ;
slot_ctx - > dev_info2 & = cpu_to_le32 ( ~ ( ( u32 ) MAX_EXIT ) ) ;
slot_ctx - > dev_info2 | = cpu_to_le32 ( max_exit_latency ) ;
xhci_dbg ( xhci , " Set up evaluate context for LPM MEL change. \n " ) ;
xhci_dbg ( xhci , " Slot %u Input Context: \n " , udev - > slot_id ) ;
xhci_dbg_ctx ( xhci , command - > in_ctx , 0 ) ;
/* Issue and wait for the evaluate context command. */
ret = xhci_configure_endpoint ( xhci , udev , command ,
true , true ) ;
xhci_dbg ( xhci , " Slot %u Output Context: \n " , udev - > slot_id ) ;
xhci_dbg_ctx ( xhci , virt_dev - > out_ctx , 0 ) ;
if ( ! ret ) {
spin_lock_irqsave ( & xhci - > lock , flags ) ;
virt_dev - > current_mel = max_exit_latency ;
spin_unlock_irqrestore ( & xhci - > lock , flags ) ;
}
return ret ;
}
static int calculate_max_exit_latency ( struct usb_device * udev ,
enum usb3_link_state state_changed ,
u16 hub_encoded_timeout )