2005-09-24 04:14:37 +04:00
/*
* This file contains code to reset and initialize USB host controllers .
* Some of it includes work - arounds for PCI hardware and BIOS quirks .
* It may need to run early during booting - - before USB would normally
* initialize - - to ensure that Linux doesn ' t use any legacy modes .
*
* Copyright ( c ) 1999 Martin Mares < mj @ ucw . cz >
* ( and others )
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/acpi.h>
2011-05-12 02:52:38 +04:00
# include <linux/dmi.h>
2006-03-25 20:01:53 +03:00
# include "pci-quirks.h"
2009-04-28 06:52:28 +04:00
# include "xhci-ext-caps.h"
2005-09-24 04:14:37 +04:00
# define UHCI_USBLEGSUP 0xc0 /* legacy support */
# define UHCI_USBCMD 0 /* command register */
# define UHCI_USBINTR 4 /* interrupt register */
2005-10-04 00:36:29 +04:00
# define UHCI_USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
# define UHCI_USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
# define UHCI_USBCMD_RUN 0x0001 /* RUN/STOP bit */
# define UHCI_USBCMD_HCRESET 0x0002 /* Host Controller reset */
# define UHCI_USBCMD_EGSM 0x0008 /* Global Suspend Mode */
# define UHCI_USBCMD_CONFIGURE 0x0040 /* Config Flag */
# define UHCI_USBINTR_RESUME 0x0002 /* Resume interrupt enable */
2005-09-24 04:14:37 +04:00
# define OHCI_CONTROL 0x04
# define OHCI_CMDSTATUS 0x08
# define OHCI_INTRSTATUS 0x0c
# define OHCI_INTRENABLE 0x10
# define OHCI_INTRDISABLE 0x14
# define OHCI_OCR (1 << 3) /* ownership change request */
2005-09-23 09:43:30 +04:00
# define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
2005-09-24 04:14:37 +04:00
# define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
# define OHCI_INTR_OC (1 << 30) /* ownership change */
# define EHCI_HCC_PARAMS 0x08 /* extended capabilities */
# define EHCI_USBCMD 0 /* command register */
# define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
# define EHCI_USBSTS 4 /* status register */
# define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */
# define EHCI_USBINTR 8 /* interrupt register */
2007-04-06 00:06:53 +04:00
# define EHCI_CONFIGFLAG 0x40 /* configured flag register */
2005-09-24 04:14:37 +04:00
# define EHCI_USBLEGSUP 0 /* legacy support register */
# define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */
# define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */
# define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
# define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
2011-03-01 09:57:05 +03:00
/* AMD quirk use */
# define AB_REG_BAR_LOW 0xe0
# define AB_REG_BAR_HIGH 0xe1
# define AB_REG_BAR_SB700 0xf0
# define AB_INDX(addr) ((addr) + 0x00)
# define AB_DATA(addr) ((addr) + 0x04)
# define AX_INDXC 0x30
# define AX_DATAC 0x34
# define NB_PCIE_INDX_ADDR 0xe0
# define NB_PCIE_INDX_DATA 0xe4
# define PCIE_P_CNTL 0x10040
# define BIF_NB 0x10002
# define NB_PIF0_PWRDOWN_0 0x01100012
# define NB_PIF0_PWRDOWN_1 0x01100013
static struct amd_chipset_info {
struct pci_dev * nb_dev ;
struct pci_dev * smbus_dev ;
int nb_type ;
int sb_type ;
int isoc_reqs ;
int probe_count ;
int probe_result ;
} amd_chipset ;
static DEFINE_SPINLOCK ( amd_lock ) ;
int usb_amd_find_chipset_info ( void )
{
u8 rev = 0 ;
unsigned long flags ;
2011-04-13 10:38:16 +04:00
struct amd_chipset_info info ;
int ret ;
2011-03-01 09:57:05 +03:00
spin_lock_irqsave ( & amd_lock , flags ) ;
/* probe only once */
2011-04-13 10:38:16 +04:00
if ( amd_chipset . probe_count > 0 ) {
amd_chipset . probe_count + + ;
2011-03-01 09:57:05 +03:00
spin_unlock_irqrestore ( & amd_lock , flags ) ;
return amd_chipset . probe_result ;
}
2011-04-13 10:38:16 +04:00
memset ( & info , 0 , sizeof ( info ) ) ;
spin_unlock_irqrestore ( & amd_lock , flags ) ;
2011-03-01 09:57:05 +03:00
2011-04-13 10:38:16 +04:00
info . smbus_dev = pci_get_device ( PCI_VENDOR_ID_ATI , 0x4385 , NULL ) ;
if ( info . smbus_dev ) {
rev = info . smbus_dev - > revision ;
2011-03-01 09:57:05 +03:00
if ( rev > = 0x40 )
2011-04-13 10:38:16 +04:00
info . sb_type = 1 ;
2011-03-01 09:57:05 +03:00
else if ( rev > = 0x30 & & rev < = 0x3b )
2011-04-13 10:38:16 +04:00
info . sb_type = 3 ;
2011-03-01 09:57:05 +03:00
} else {
2011-04-13 10:38:16 +04:00
info . smbus_dev = pci_get_device ( PCI_VENDOR_ID_AMD ,
0x780b , NULL ) ;
if ( ! info . smbus_dev ) {
ret = 0 ;
goto commit ;
2011-03-01 09:57:05 +03:00
}
2011-04-13 10:38:16 +04:00
rev = info . smbus_dev - > revision ;
2011-03-01 09:57:05 +03:00
if ( rev > = 0x11 & & rev < = 0x18 )
2011-04-13 10:38:16 +04:00
info . sb_type = 2 ;
2011-03-01 09:57:05 +03:00
}
2011-04-13 10:38:16 +04:00
if ( info . sb_type = = 0 ) {
if ( info . smbus_dev ) {
pci_dev_put ( info . smbus_dev ) ;
info . smbus_dev = NULL ;
2011-03-01 09:57:05 +03:00
}
2011-04-13 10:38:16 +04:00
ret = 0 ;
goto commit ;
2011-03-01 09:57:05 +03:00
}
2011-04-13 10:38:16 +04:00
info . nb_dev = pci_get_device ( PCI_VENDOR_ID_AMD , 0x9601 , NULL ) ;
if ( info . nb_dev ) {
info . nb_type = 1 ;
2011-03-01 09:57:05 +03:00
} else {
2011-04-13 10:38:16 +04:00
info . nb_dev = pci_get_device ( PCI_VENDOR_ID_AMD , 0x1510 , NULL ) ;
if ( info . nb_dev ) {
info . nb_type = 2 ;
} else {
info . nb_dev = pci_get_device ( PCI_VENDOR_ID_AMD ,
0x9600 , NULL ) ;
if ( info . nb_dev )
info . nb_type = 3 ;
2011-03-01 09:57:05 +03:00
}
}
2011-04-13 10:38:16 +04:00
ret = info . probe_result = 1 ;
2011-03-01 09:57:05 +03:00
printk ( KERN_DEBUG " QUIRK: Enable AMD PLL fix \n " ) ;
2011-04-13 10:38:16 +04:00
commit :
spin_lock_irqsave ( & amd_lock , flags ) ;
if ( amd_chipset . probe_count > 0 ) {
/* race - someone else was faster - drop devices */
/* Mark that we where here */
amd_chipset . probe_count + + ;
ret = amd_chipset . probe_result ;
spin_unlock_irqrestore ( & amd_lock , flags ) ;
if ( info . nb_dev )
pci_dev_put ( info . nb_dev ) ;
if ( info . smbus_dev )
pci_dev_put ( info . smbus_dev ) ;
} else {
/* no race - commit the result */
info . probe_count + + ;
amd_chipset = info ;
spin_unlock_irqrestore ( & amd_lock , flags ) ;
}
return ret ;
2011-03-01 09:57:05 +03:00
}
EXPORT_SYMBOL_GPL ( usb_amd_find_chipset_info ) ;
/*
* The hardware normally enables the A - link power management feature , which
* lets the system lower the power consumption in idle states .
*
* This USB quirk prevents the link going into that lower power state
* during isochronous transfers .
*
* Without this quirk , isochronous stream on OHCI / EHCI / xHCI controllers of
* some AMD platforms may stutter or have breaks occasionally .
*/
static void usb_amd_quirk_pll ( int disable )
{
u32 addr , addr_low , addr_high , val ;
u32 bit = disable ? 0 : 1 ;
unsigned long flags ;
spin_lock_irqsave ( & amd_lock , flags ) ;
if ( disable ) {
amd_chipset . isoc_reqs + + ;
if ( amd_chipset . isoc_reqs > 1 ) {
spin_unlock_irqrestore ( & amd_lock , flags ) ;
return ;
}
} else {
amd_chipset . isoc_reqs - - ;
if ( amd_chipset . isoc_reqs > 0 ) {
spin_unlock_irqrestore ( & amd_lock , flags ) ;
return ;
}
}
if ( amd_chipset . sb_type = = 1 | | amd_chipset . sb_type = = 2 ) {
outb_p ( AB_REG_BAR_LOW , 0xcd6 ) ;
addr_low = inb_p ( 0xcd7 ) ;
outb_p ( AB_REG_BAR_HIGH , 0xcd6 ) ;
addr_high = inb_p ( 0xcd7 ) ;
addr = addr_high < < 8 | addr_low ;
outl_p ( 0x30 , AB_INDX ( addr ) ) ;
outl_p ( 0x40 , AB_DATA ( addr ) ) ;
outl_p ( 0x34 , AB_INDX ( addr ) ) ;
val = inl_p ( AB_DATA ( addr ) ) ;
} else if ( amd_chipset . sb_type = = 3 ) {
pci_read_config_dword ( amd_chipset . smbus_dev ,
AB_REG_BAR_SB700 , & addr ) ;
outl ( AX_INDXC , AB_INDX ( addr ) ) ;
outl ( 0x40 , AB_DATA ( addr ) ) ;
outl ( AX_DATAC , AB_INDX ( addr ) ) ;
val = inl ( AB_DATA ( addr ) ) ;
} else {
spin_unlock_irqrestore ( & amd_lock , flags ) ;
return ;
}
if ( disable ) {
val & = ~ 0x08 ;
val | = ( 1 < < 4 ) | ( 1 < < 9 ) ;
} else {
val | = 0x08 ;
val & = ~ ( ( 1 < < 4 ) | ( 1 < < 9 ) ) ;
}
outl_p ( val , AB_DATA ( addr ) ) ;
if ( ! amd_chipset . nb_dev ) {
spin_unlock_irqrestore ( & amd_lock , flags ) ;
return ;
}
if ( amd_chipset . nb_type = = 1 | | amd_chipset . nb_type = = 3 ) {
addr = PCIE_P_CNTL ;
pci_write_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_ADDR , addr ) ;
pci_read_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_DATA , & val ) ;
val & = ~ ( 1 | ( 1 < < 3 ) | ( 1 < < 4 ) | ( 1 < < 9 ) | ( 1 < < 12 ) ) ;
val | = bit | ( bit < < 3 ) | ( bit < < 12 ) ;
val | = ( ( ! bit ) < < 4 ) | ( ( ! bit ) < < 9 ) ;
pci_write_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_DATA , val ) ;
addr = BIF_NB ;
pci_write_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_ADDR , addr ) ;
pci_read_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_DATA , & val ) ;
val & = ~ ( 1 < < 8 ) ;
val | = bit < < 8 ;
pci_write_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_DATA , val ) ;
} else if ( amd_chipset . nb_type = = 2 ) {
addr = NB_PIF0_PWRDOWN_0 ;
pci_write_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_ADDR , addr ) ;
pci_read_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_DATA , & val ) ;
if ( disable )
val & = ~ ( 0x3f < < 7 ) ;
else
val | = 0x3f < < 7 ;
pci_write_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_DATA , val ) ;
addr = NB_PIF0_PWRDOWN_1 ;
pci_write_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_ADDR , addr ) ;
pci_read_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_DATA , & val ) ;
if ( disable )
val & = ~ ( 0x3f < < 7 ) ;
else
val | = 0x3f < < 7 ;
pci_write_config_dword ( amd_chipset . nb_dev ,
NB_PCIE_INDX_DATA , val ) ;
}
spin_unlock_irqrestore ( & amd_lock , flags ) ;
return ;
}
void usb_amd_quirk_pll_disable ( void )
{
usb_amd_quirk_pll ( 1 ) ;
}
EXPORT_SYMBOL_GPL ( usb_amd_quirk_pll_disable ) ;
void usb_amd_quirk_pll_enable ( void )
{
usb_amd_quirk_pll ( 0 ) ;
}
EXPORT_SYMBOL_GPL ( usb_amd_quirk_pll_enable ) ;
void usb_amd_dev_put ( void )
{
2011-04-13 10:38:16 +04:00
struct pci_dev * nb , * smbus ;
2011-03-01 09:57:05 +03:00
unsigned long flags ;
spin_lock_irqsave ( & amd_lock , flags ) ;
amd_chipset . probe_count - - ;
if ( amd_chipset . probe_count > 0 ) {
spin_unlock_irqrestore ( & amd_lock , flags ) ;
return ;
}
2011-04-13 10:38:16 +04:00
/* save them to pci_dev_put outside of spinlock */
nb = amd_chipset . nb_dev ;
smbus = amd_chipset . smbus_dev ;
amd_chipset . nb_dev = NULL ;
amd_chipset . smbus_dev = NULL ;
2011-03-01 09:57:05 +03:00
amd_chipset . nb_type = 0 ;
amd_chipset . sb_type = 0 ;
amd_chipset . isoc_reqs = 0 ;
amd_chipset . probe_result = 0 ;
spin_unlock_irqrestore ( & amd_lock , flags ) ;
2011-04-13 10:38:16 +04:00
if ( nb )
pci_dev_put ( nb ) ;
if ( smbus )
pci_dev_put ( smbus ) ;
2011-03-01 09:57:05 +03:00
}
EXPORT_SYMBOL_GPL ( usb_amd_dev_put ) ;
2005-09-24 04:14:37 +04:00
2005-10-04 00:36:29 +04:00
/*
* Make sure the controller is completely inactive , unable to
* generate interrupts or do DMA .
*/
void uhci_reset_hc ( struct pci_dev * pdev , unsigned long base )
{
/* Turn off PIRQ enable and SMI enable. (This also turns off the
* BIOS ' s USB Legacy Support . ) Turn off all the R / WC bits too .
*/
pci_write_config_word ( pdev , UHCI_USBLEGSUP , UHCI_USBLEGSUP_RWC ) ;
/* Reset the HC - this will force us to get a
* new notification of any already connected
* ports due to the virtual disconnect that it
* implies .
*/
outw ( UHCI_USBCMD_HCRESET , base + UHCI_USBCMD ) ;
mb ( ) ;
udelay ( 5 ) ;
if ( inw ( base + UHCI_USBCMD ) & UHCI_USBCMD_HCRESET )
dev_warn ( & pdev - > dev , " HCRESET not completed yet! \n " ) ;
/* Just to be safe, disable interrupt requests and
* make sure the controller is stopped .
*/
outw ( 0 , base + UHCI_USBINTR ) ;
outw ( 0 , base + UHCI_USBCMD ) ;
}
EXPORT_SYMBOL_GPL ( uhci_reset_hc ) ;
/*
* Initialize a controller that was newly discovered or has just been
* resumed . In either case we can ' t be sure of its previous state .
*
* Returns : 1 if the controller was reset , 0 otherwise .
*/
int uhci_check_and_reset_hc ( struct pci_dev * pdev , unsigned long base )
{
u16 legsup ;
unsigned int cmd , intr ;
/*
* When restarting a suspended controller , we expect all the
* settings to be the same as we left them :
*
* PIRQ and SMI disabled , no R / W bits set in USBLEGSUP ;
* Controller is stopped and configured with EGSM set ;
* No interrupts enabled except possibly Resume Detect .
*
* If any of these conditions are violated we do a complete reset .
*/
pci_read_config_word ( pdev , UHCI_USBLEGSUP , & legsup ) ;
if ( legsup & ~ ( UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC ) ) {
dev_dbg ( & pdev - > dev , " %s: legsup = 0x%04x \n " ,
2008-03-04 03:08:34 +03:00
__func__ , legsup ) ;
2005-10-04 00:36:29 +04:00
goto reset_needed ;
}
cmd = inw ( base + UHCI_USBCMD ) ;
if ( ( cmd & UHCI_USBCMD_RUN ) | | ! ( cmd & UHCI_USBCMD_CONFIGURE ) | |
! ( cmd & UHCI_USBCMD_EGSM ) ) {
dev_dbg ( & pdev - > dev , " %s: cmd = 0x%04x \n " ,
2008-03-04 03:08:34 +03:00
__func__ , cmd ) ;
2005-10-04 00:36:29 +04:00
goto reset_needed ;
}
intr = inw ( base + UHCI_USBINTR ) ;
if ( intr & ( ~ UHCI_USBINTR_RESUME ) ) {
dev_dbg ( & pdev - > dev , " %s: intr = 0x%04x \n " ,
2008-03-04 03:08:34 +03:00
__func__ , intr ) ;
2005-10-04 00:36:29 +04:00
goto reset_needed ;
}
return 0 ;
reset_needed :
dev_dbg ( & pdev - > dev , " Performing full reset \n " ) ;
uhci_reset_hc ( pdev , base ) ;
return 1 ;
}
EXPORT_SYMBOL_GPL ( uhci_check_and_reset_hc ) ;
2005-11-01 08:12:40 +03:00
static inline int io_type_enabled ( struct pci_dev * pdev , unsigned int mask )
{
u16 cmd ;
return ! pci_read_config_word ( pdev , PCI_COMMAND , & cmd ) & & ( cmd & mask ) ;
}
# define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO)
# define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY)
2005-09-24 04:14:37 +04:00
static void __devinit quirk_usb_handoff_uhci ( struct pci_dev * pdev )
{
unsigned long base = 0 ;
int i ;
2005-11-01 08:12:40 +03:00
if ( ! pio_enabled ( pdev ) )
return ;
2005-09-24 04:14:37 +04:00
for ( i = 0 ; i < PCI_ROM_RESOURCE ; i + + )
if ( ( pci_resource_flags ( pdev , i ) & IORESOURCE_IO ) ) {
base = pci_resource_start ( pdev , i ) ;
break ;
}
2005-10-04 00:36:29 +04:00
if ( base )
uhci_check_and_reset_hc ( pdev , base ) ;
2005-09-24 04:14:37 +04:00
}
2005-11-01 08:12:40 +03:00
static int __devinit mmio_resource_enabled ( struct pci_dev * pdev , int idx )
{
return pci_resource_start ( pdev , idx ) & & mmio_enabled ( pdev ) ;
}
2005-09-24 04:14:37 +04:00
static void __devinit quirk_usb_handoff_ohci ( struct pci_dev * pdev )
{
void __iomem * base ;
2010-09-11 00:37:05 +04:00
u32 control ;
2005-09-24 04:14:37 +04:00
2005-11-01 08:12:40 +03:00
if ( ! mmio_resource_enabled ( pdev , 0 ) )
return ;
2008-10-21 08:46:01 +04:00
base = pci_ioremap_bar ( pdev , 0 ) ;
if ( base = = NULL )
return ;
2005-09-24 04:14:37 +04:00
2010-09-11 00:37:05 +04:00
control = readl ( base + OHCI_CONTROL ) ;
2005-09-23 09:43:30 +04:00
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
2010-09-11 00:37:05 +04:00
# ifdef __hppa__
# define OHCI_CTRL_MASK (OHCI_CTRL_RWC | OHCI_CTRL_IR)
# else
# define OHCI_CTRL_MASK OHCI_CTRL_RWC
2005-09-23 09:43:30 +04:00
if ( control & OHCI_CTRL_IR ) {
2006-06-26 02:45:29 +04:00
int wait_time = 500 ; /* arbitrary; 5 seconds */
2005-09-24 04:14:37 +04:00
writel ( OHCI_INTR_OC , base + OHCI_INTRENABLE ) ;
writel ( OHCI_OCR , base + OHCI_CMDSTATUS ) ;
while ( wait_time > 0 & &
readl ( base + OHCI_CONTROL ) & OHCI_CTRL_IR ) {
wait_time - = 10 ;
msleep ( 10 ) ;
}
2005-09-23 09:43:30 +04:00
if ( wait_time < = 0 )
2007-12-18 00:09:39 +03:00
dev_warn ( & pdev - > dev , " OHCI: BIOS handoff failed "
" (BIOS bug?) %08x \n " ,
2006-02-10 00:35:31 +03:00
readl ( base + OHCI_CONTROL ) ) ;
2005-09-24 04:14:37 +04:00
}
2005-09-23 09:43:30 +04:00
# endif
2005-09-24 04:14:37 +04:00
2010-09-11 00:37:05 +04:00
/* reset controller, preserving RWC (and possibly IR) */
writel ( control & OHCI_CTRL_MASK , base + OHCI_CONTROL ) ;
2005-09-24 04:14:37 +04:00
/*
* disable interrupts
*/
writel ( ~ ( u32 ) 0 , base + OHCI_INTRDISABLE ) ;
writel ( ~ ( u32 ) 0 , base + OHCI_INTRSTATUS ) ;
iounmap ( base ) ;
}
2011-05-12 02:15:51 +04:00
static void __devinit ehci_bios_handoff ( struct pci_dev * pdev ,
void __iomem * op_reg_base ,
u32 cap , u8 offset )
{
2011-05-12 02:52:38 +04:00
int try_handoff = 1 , tried_handoff = 0 ;
/* The Pegatron Lucid (ExoPC) tablet sporadically waits for 90
* seconds trying the handoff on its unused controller . Skip
* it . */
if ( pdev - > vendor = = 0x8086 & & pdev - > device = = 0x283a ) {
const char * dmi_bn = dmi_get_system_info ( DMI_BOARD_NAME ) ;
const char * dmi_bv = dmi_get_system_info ( DMI_BIOS_VERSION ) ;
if ( dmi_bn & & ! strcmp ( dmi_bn , " EXOPG06411 " ) & &
dmi_bv & & ! strcmp ( dmi_bv , " Lucid-CE-133 " ) )
try_handoff = 0 ;
}
2011-05-12 02:15:51 +04:00
2011-05-12 02:52:38 +04:00
if ( try_handoff & & ( cap & EHCI_USBLEGSUP_BIOS ) ) {
2011-05-12 02:15:51 +04:00
dev_dbg ( & pdev - > dev , " EHCI: BIOS handoff \n " ) ;
#if 0
/* aleksey_gorelov@phoenix.com reports that some systems need SMI forced on,
* but that seems dubious in general ( the BIOS left it off intentionally )
* and is known to prevent some systems from booting . so we won ' t do this
* unless maybe we can determine when we ' re on a system that needs SMI forced .
*/
/* BIOS workaround (?): be sure the pre-Linux code
* receives the SMI
*/
pci_read_config_dword ( pdev , offset + EHCI_USBLEGCTLSTS , & val ) ;
pci_write_config_dword ( pdev , offset + EHCI_USBLEGCTLSTS ,
val | EHCI_USBLEGCTLSTS_SOOE ) ;
# endif
/* some systems get upset if this semaphore is
* set for any other reason than forcing a BIOS
* handoff . .
*/
pci_write_config_byte ( pdev , offset + 3 , 1 ) ;
}
/* if boot firmware now owns EHCI, spin till it hands it over. */
2011-05-12 02:52:38 +04:00
if ( try_handoff ) {
int msec = 1000 ;
while ( ( cap & EHCI_USBLEGSUP_BIOS ) & & ( msec > 0 ) ) {
tried_handoff = 1 ;
msleep ( 10 ) ;
msec - = 10 ;
pci_read_config_dword ( pdev , offset , & cap ) ;
}
2011-05-12 02:15:51 +04:00
}
if ( cap & EHCI_USBLEGSUP_BIOS ) {
/* well, possibly buggy BIOS... try to shut it down,
* and hope nothing goes too wrong
*/
2011-05-12 02:52:38 +04:00
if ( try_handoff )
dev_warn ( & pdev - > dev , " EHCI: BIOS handoff failed "
" (BIOS bug?) %08x \n " , cap ) ;
2011-05-12 02:15:51 +04:00
pci_write_config_byte ( pdev , offset + 2 , 0 ) ;
}
/* just in case, always disable EHCI SMIs */
pci_write_config_dword ( pdev , offset + EHCI_USBLEGCTLSTS , 0 ) ;
/* If the BIOS ever owned the controller then we can't expect
* any power sessions to remain intact .
*/
if ( tried_handoff )
writel ( 0 , op_reg_base + EHCI_CONFIGFLAG ) ;
}
2005-09-24 04:14:37 +04:00
static void __devinit quirk_usb_disable_ehci ( struct pci_dev * pdev )
{
void __iomem * base , * op_reg_base ;
2011-05-12 02:15:51 +04:00
u32 hcc_params , cap , val ;
2006-01-24 18:15:30 +03:00
u8 offset , cap_length ;
2011-05-12 02:15:51 +04:00
int wait_time , delta , count = 256 / 4 ;
2005-09-24 04:14:37 +04:00
2005-11-01 08:12:40 +03:00
if ( ! mmio_resource_enabled ( pdev , 0 ) )
return ;
2008-10-21 08:46:01 +04:00
base = pci_ioremap_bar ( pdev , 0 ) ;
if ( base = = NULL )
return ;
2005-09-24 04:14:37 +04:00
cap_length = readb ( base ) ;
op_reg_base = base + cap_length ;
2006-01-24 18:15:30 +03:00
/* EHCI 0.96 and later may have "extended capabilities"
* spec section 5.1 explains the bios handoff , e . g . for
* booting from USB disk or using a usb keyboard
*/
2005-09-24 04:14:37 +04:00
hcc_params = readl ( base + EHCI_HCC_PARAMS ) ;
2006-01-24 18:15:30 +03:00
offset = ( hcc_params > > 8 ) & 0xff ;
2009-01-31 14:37:04 +03:00
while ( offset & & - - count ) {
2006-01-24 18:15:30 +03:00
pci_read_config_dword ( pdev , offset , & cap ) ;
2011-05-12 02:15:51 +04:00
switch ( cap & 0xff ) {
case 1 :
ehci_bios_handoff ( pdev , op_reg_base , cap , offset ) ;
2006-01-24 18:15:30 +03:00
break ;
2011-05-12 02:15:51 +04:00
case 0 : /* Illegal reserved cap, set cap=0 so we exit */
cap = 0 ; /* then fallthrough... */
2006-01-24 18:15:30 +03:00
default :
2007-12-18 00:09:39 +03:00
dev_warn ( & pdev - > dev , " EHCI: unrecognized capability "
2011-05-12 02:15:51 +04:00
" %02x \n " , cap & 0xff ) ;
2005-09-24 04:14:37 +04:00
}
2006-01-24 18:15:30 +03:00
offset = ( cap > > 8 ) & 0xff ;
2005-09-24 04:14:37 +04:00
}
2006-01-24 18:15:30 +03:00
if ( ! count )
2007-12-18 00:09:39 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " EHCI: capability loop? \n " ) ;
2005-09-24 04:14:37 +04:00
/*
* halt EHCI & disable its interrupts in any case
*/
val = readl ( op_reg_base + EHCI_USBSTS ) ;
if ( ( val & EHCI_USBSTS_HALTED ) = = 0 ) {
val = readl ( op_reg_base + EHCI_USBCMD ) ;
val & = ~ EHCI_USBCMD_RUN ;
writel ( val , op_reg_base + EHCI_USBCMD ) ;
wait_time = 2000 ;
delta = 100 ;
do {
writel ( 0x3f , op_reg_base + EHCI_USBSTS ) ;
udelay ( delta ) ;
wait_time - = delta ;
val = readl ( op_reg_base + EHCI_USBSTS ) ;
if ( ( val = = ~ ( u32 ) 0 ) | | ( val & EHCI_USBSTS_HALTED ) ) {
break ;
}
} while ( wait_time > 0 ) ;
}
writel ( 0 , op_reg_base + EHCI_USBINTR ) ;
writel ( 0x3f , op_reg_base + EHCI_USBSTS ) ;
iounmap ( base ) ;
}
2009-04-28 06:52:28 +04:00
/*
* handshake - spin reading a register until handshake completes
* @ ptr : address of hc register to be read
* @ mask : bits to look at in result of read
* @ done : value of those bits when handshake succeeds
* @ wait_usec : timeout in microseconds
* @ delay_usec : delay in microseconds to wait between polling
*
* Polls a register every delay_usec microseconds .
* Returns 0 when the mask bits have the value done .
* Returns - ETIMEDOUT if this condition is not true after
* wait_usec microseconds have passed .
*/
static int handshake ( void __iomem * ptr , u32 mask , u32 done ,
int wait_usec , int delay_usec )
{
u32 result ;
do {
result = readl ( ptr ) ;
result & = mask ;
if ( result = = done )
return 0 ;
udelay ( delay_usec ) ;
wait_usec - = delay_usec ;
} while ( wait_usec > 0 ) ;
return - ETIMEDOUT ;
}
/**
* PCI Quirks for xHCI .
*
* Takes care of the handoff between the Pre - OS ( i . e . BIOS ) and the OS .
* It signals to the BIOS that the OS wants control of the host controller ,
* and then waits 5 seconds for the BIOS to hand over control .
* If we timeout , assume the BIOS is broken and take control anyway .
*/
static void __devinit quirk_usb_handoff_xhci ( struct pci_dev * pdev )
{
void __iomem * base ;
int ext_cap_offset ;
void __iomem * op_reg_base ;
u32 val ;
int timeout ;
if ( ! mmio_resource_enabled ( pdev , 0 ) )
return ;
base = ioremap_nocache ( pci_resource_start ( pdev , 0 ) ,
pci_resource_len ( pdev , 0 ) ) ;
if ( base = = NULL )
return ;
2005-09-24 04:14:37 +04:00
2009-04-28 06:52:28 +04:00
/*
* Find the Legacy Support Capability register -
* this is optional for xHCI host controllers .
*/
ext_cap_offset = xhci_find_next_cap_offset ( base , XHCI_HCC_PARAMS_OFFSET ) ;
do {
if ( ! ext_cap_offset )
/* We've reached the end of the extended capabilities */
goto hc_init ;
val = readl ( base + ext_cap_offset ) ;
if ( XHCI_EXT_CAPS_ID ( val ) = = XHCI_EXT_CAPS_LEGACY )
break ;
ext_cap_offset = xhci_find_next_cap_offset ( base , ext_cap_offset ) ;
} while ( 1 ) ;
/* If the BIOS owns the HC, signal that the OS wants it, and wait */
if ( val & XHCI_HC_BIOS_OWNED ) {
writel ( val & XHCI_HC_OS_OWNED , base + ext_cap_offset ) ;
/* Wait for 5 seconds with 10 microsecond polling interval */
timeout = handshake ( base + ext_cap_offset , XHCI_HC_BIOS_OWNED ,
0 , 5000 , 10 ) ;
/* Assume a buggy BIOS and take HC ownership anyway */
if ( timeout ) {
dev_warn ( & pdev - > dev , " xHCI BIOS handoff failed "
" (BIOS bug ?) %08x \n " , val ) ;
writel ( val & ~ XHCI_HC_BIOS_OWNED , base + ext_cap_offset ) ;
}
}
/* Disable any BIOS SMIs */
writel ( XHCI_LEGACY_DISABLE_SMI ,
base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET ) ;
hc_init :
op_reg_base = base + XHCI_HC_LENGTH ( readl ( base ) ) ;
/* Wait for the host controller to be ready before writing any
* operational or runtime registers . Wait 5 seconds and no more .
*/
timeout = handshake ( op_reg_base + XHCI_STS_OFFSET , XHCI_STS_CNR , 0 ,
5000 , 10 ) ;
/* Assume a buggy HC and start HC initialization anyway */
if ( timeout ) {
val = readl ( op_reg_base + XHCI_STS_OFFSET ) ;
dev_warn ( & pdev - > dev ,
" xHCI HW not ready after 5 sec (HC bug?) "
" status = 0x%x \n " , val ) ;
}
/* Send the halt and disable interrupts command */
val = readl ( op_reg_base + XHCI_CMD_OFFSET ) ;
val & = ~ ( XHCI_CMD_RUN | XHCI_IRQS ) ;
writel ( val , op_reg_base + XHCI_CMD_OFFSET ) ;
/* Wait for the HC to halt - poll every 125 usec (one microframe). */
timeout = handshake ( op_reg_base + XHCI_STS_OFFSET , XHCI_STS_HALT , 1 ,
XHCI_MAX_HALT_USEC , 125 ) ;
if ( timeout ) {
val = readl ( op_reg_base + XHCI_STS_OFFSET ) ;
dev_warn ( & pdev - > dev ,
" xHCI HW did not halt within %d usec "
" status = 0x%x \n " , XHCI_MAX_HALT_USEC , val ) ;
}
iounmap ( base ) ;
}
2005-09-24 04:14:37 +04:00
static void __devinit quirk_usb_early_handoff ( struct pci_dev * pdev )
{
2005-10-19 20:52:02 +04:00
if ( pdev - > class = = PCI_CLASS_SERIAL_USB_UHCI )
2005-09-24 04:14:37 +04:00
quirk_usb_handoff_uhci ( pdev ) ;
2005-10-19 20:52:02 +04:00
else if ( pdev - > class = = PCI_CLASS_SERIAL_USB_OHCI )
2005-09-24 04:14:37 +04:00
quirk_usb_handoff_ohci ( pdev ) ;
2005-10-19 20:52:02 +04:00
else if ( pdev - > class = = PCI_CLASS_SERIAL_USB_EHCI )
2005-09-24 04:14:37 +04:00
quirk_usb_disable_ehci ( pdev ) ;
2009-04-28 06:52:28 +04:00
else if ( pdev - > class = = PCI_CLASS_SERIAL_USB_XHCI )
quirk_usb_handoff_xhci ( pdev ) ;
2005-09-24 04:14:37 +04:00
}
2009-10-12 02:57:57 +04:00
DECLARE_PCI_FIXUP_FINAL ( PCI_ANY_ID , PCI_ANY_ID , quirk_usb_early_handoff ) ;