2005-04-17 02:20:36 +04:00
/*
* PCI Express PCI Hot Plug Driver
*
* Copyright ( C ) 1995 , 2001 Compaq Computer Corporation
* Copyright ( C ) 2001 Greg Kroah - Hartman ( greg @ kroah . com )
* Copyright ( C ) 2001 IBM Corp .
* Copyright ( C ) 2003 - 2004 Intel Corporation
*
* All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* 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 , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
2005-08-17 02:16:10 +04:00
* Send feedback to < greg @ kroah . com > , < kristen . c . accardi @ intel . com >
2005-04-17 02:20:36 +04:00
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/types.h>
2006-01-08 12:02:05 +03:00
# include <linux/signal.h>
# include <linux/jiffies.h>
# include <linux/timer.h>
2005-04-17 02:20:36 +04:00
# include <linux/pci.h>
2005-11-14 03:06:39 +03:00
# include <linux/interrupt.h>
2007-01-10 00:02:36 +03:00
# include <linux/time.h>
2005-11-14 03:06:39 +03:00
2005-04-17 02:20:36 +04:00
# include "../pci.h"
# include "pciehp.h"
2007-03-07 02:02:26 +03:00
static atomic_t pciehp_num_controllers = ATOMIC_INIT ( 0 ) ;
2005-04-17 02:20:36 +04:00
struct ctrl_reg {
u8 cap_id ;
u8 nxt_ptr ;
u16 cap_reg ;
u32 dev_cap ;
u16 dev_ctrl ;
u16 dev_status ;
u32 lnk_cap ;
u16 lnk_ctrl ;
u16 lnk_status ;
u32 slot_cap ;
u16 slot_ctrl ;
u16 slot_status ;
u16 root_ctrl ;
u16 rsvp ;
u32 root_status ;
} __attribute__ ( ( packed ) ) ;
/* offsets to the controller registers based on the above structure layout */
enum ctrl_offsets {
PCIECAPID = offsetof ( struct ctrl_reg , cap_id ) ,
NXTCAPPTR = offsetof ( struct ctrl_reg , nxt_ptr ) ,
CAPREG = offsetof ( struct ctrl_reg , cap_reg ) ,
DEVCAP = offsetof ( struct ctrl_reg , dev_cap ) ,
DEVCTRL = offsetof ( struct ctrl_reg , dev_ctrl ) ,
DEVSTATUS = offsetof ( struct ctrl_reg , dev_status ) ,
LNKCAP = offsetof ( struct ctrl_reg , lnk_cap ) ,
LNKCTRL = offsetof ( struct ctrl_reg , lnk_ctrl ) ,
LNKSTATUS = offsetof ( struct ctrl_reg , lnk_status ) ,
SLOTCAP = offsetof ( struct ctrl_reg , slot_cap ) ,
SLOTCTRL = offsetof ( struct ctrl_reg , slot_ctrl ) ,
SLOTSTATUS = offsetof ( struct ctrl_reg , slot_status ) ,
ROOTCTRL = offsetof ( struct ctrl_reg , root_ctrl ) ,
ROOTSTATUS = offsetof ( struct ctrl_reg , root_status ) ,
} ;
2006-12-22 04:01:06 +03:00
static inline int pciehp_readw ( struct controller * ctrl , int reg , u16 * value )
{
struct pci_dev * dev = ctrl - > pci_dev ;
return pci_read_config_word ( dev , ctrl - > cap_base + reg , value ) ;
}
static inline int pciehp_readl ( struct controller * ctrl , int reg , u32 * value )
{
struct pci_dev * dev = ctrl - > pci_dev ;
return pci_read_config_dword ( dev , ctrl - > cap_base + reg , value ) ;
}
static inline int pciehp_writew ( struct controller * ctrl , int reg , u16 value )
{
struct pci_dev * dev = ctrl - > pci_dev ;
return pci_write_config_word ( dev , ctrl - > cap_base + reg , value ) ;
}
static inline int pciehp_writel ( struct controller * ctrl , int reg , u32 value )
{
struct pci_dev * dev = ctrl - > pci_dev ;
return pci_write_config_dword ( dev , ctrl - > cap_base + reg , value ) ;
}
2005-04-17 02:20:36 +04:00
/* Field definitions in PCI Express Capabilities Register */
# define CAP_VER 0x000F
# define DEV_PORT_TYPE 0x00F0
# define SLOT_IMPL 0x0100
# define MSG_NUM 0x3E00
/* Device or Port Type */
# define NAT_ENDPT 0x00
# define LEG_ENDPT 0x01
# define ROOT_PORT 0x04
# define UP_STREAM 0x05
# define DN_STREAM 0x06
# define PCIE_PCI_BRDG 0x07
# define PCI_PCIE_BRDG 0x10
/* Field definitions in Device Capabilities Register */
# define DATTN_BUTTN_PRSN 0x1000
# define DATTN_LED_PRSN 0x2000
# define DPWR_LED_PRSN 0x4000
/* Field definitions in Link Capabilities Register */
# define MAX_LNK_SPEED 0x000F
# define MAX_LNK_WIDTH 0x03F0
/* Link Width Encoding */
# define LNK_X1 0x01
# define LNK_X2 0x02
2007-08-10 03:09:34 +04:00
# define LNK_X4 0x04
2005-04-17 02:20:36 +04:00
# define LNK_X8 0x08
# define LNK_X12 0x0C
2007-08-10 03:09:34 +04:00
# define LNK_X16 0x10
2005-04-17 02:20:36 +04:00
# define LNK_X32 0x20
/*Field definitions of Link Status Register */
# define LNK_SPEED 0x000F
# define NEG_LINK_WD 0x03F0
# define LNK_TRN_ERR 0x0400
# define LNK_TRN 0x0800
# define SLOT_CLK_CONF 0x1000
/* Field definitions in Slot Capabilities Register */
# define ATTN_BUTTN_PRSN 0x00000001
# define PWR_CTRL_PRSN 0x00000002
# define MRL_SENS_PRSN 0x00000004
# define ATTN_LED_PRSN 0x00000008
# define PWR_LED_PRSN 0x00000010
# define HP_SUPR_RM_SUP 0x00000020
# define HP_CAP 0x00000040
# define SLOT_PWR_VALUE 0x000003F8
# define SLOT_PWR_LIMIT 0x00000C00
# define PSN 0xFFF80000 /* PSN: Physical Slot Number */
/* Field definitions in Slot Control Register */
# define ATTN_BUTTN_ENABLE 0x0001
# define PWR_FAULT_DETECT_ENABLE 0x0002
# define MRL_DETECT_ENABLE 0x0004
# define PRSN_DETECT_ENABLE 0x0008
# define CMD_CMPL_INTR_ENABLE 0x0010
# define HP_INTR_ENABLE 0x0020
# define ATTN_LED_CTRL 0x00C0
# define PWR_LED_CTRL 0x0300
# define PWR_CTRL 0x0400
2007-01-10 00:02:36 +03:00
# define EMI_CTRL 0x0800
2005-04-17 02:20:36 +04:00
/* Attention indicator and Power indicator states */
# define LED_ON 0x01
# define LED_BLINK 0x10
# define LED_OFF 0x11
/* Power Control Command */
# define POWER_ON 0
# define POWER_OFF 0x0400
2007-01-10 00:02:36 +03:00
/* EMI Status defines */
# define EMI_DISENGAGED 0
# define EMI_ENGAGED 1
2005-04-17 02:20:36 +04:00
/* Field definitions in Slot Status Register */
# define ATTN_BUTTN_PRESSED 0x0001
# define PWR_FAULT_DETECTED 0x0002
# define MRL_SENS_CHANGED 0x0004
# define PRSN_DETECT_CHANGED 0x0008
# define CMD_COMPLETED 0x0010
# define MRL_STATE 0x0020
# define PRSN_STATE 0x0040
2007-01-10 00:02:36 +03:00
# define EMI_STATE 0x0080
# define EMI_STATUS_BIT 7
2005-04-17 02:20:36 +04:00
2006-12-22 04:01:04 +03:00
static irqreturn_t pcie_isr ( int irq , void * dev_id ) ;
static void start_int_poll_timer ( struct controller * ctrl , int sec ) ;
2005-04-17 02:20:36 +04:00
/* This is the interrupt polling timeout function. */
2006-12-22 04:01:04 +03:00
static void int_poll_timeout ( unsigned long data )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = ( struct controller * ) data ;
2005-04-17 02:20:36 +04:00
/* Poll for interrupt events. regs == NULL => polling */
2006-12-22 04:01:04 +03:00
pcie_isr ( 0 , ctrl ) ;
2005-04-17 02:20:36 +04:00
2006-12-22 04:01:04 +03:00
init_timer ( & ctrl - > poll_timer ) ;
2005-04-17 02:20:36 +04:00
if ( ! pciehp_poll_time )
2007-08-10 03:09:38 +04:00
pciehp_poll_time = 2 ; /* default polling interval is 2 sec */
2005-04-17 02:20:36 +04:00
2006-12-22 04:01:04 +03:00
start_int_poll_timer ( ctrl , pciehp_poll_time ) ;
2005-04-17 02:20:36 +04:00
}
/* This function starts the interrupt polling timer. */
2006-12-22 04:01:04 +03:00
static void start_int_poll_timer ( struct controller * ctrl , int sec )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
/* Clamp to sane value */
if ( ( sec < = 0 ) | | ( sec > 60 ) )
sec = 2 ;
ctrl - > poll_timer . function = & int_poll_timeout ;
ctrl - > poll_timer . data = ( unsigned long ) ctrl ;
ctrl - > poll_timer . expires = jiffies + sec * HZ ;
add_timer ( & ctrl - > poll_timer ) ;
2005-04-17 02:20:36 +04:00
}
2008-04-26 01:39:08 +04:00
static inline int pciehp_request_irq ( struct controller * ctrl )
{
int retval , irq = ctrl - > pci_dev - > irq ;
/* Install interrupt polling timer. Start with 10 sec delay */
if ( pciehp_poll_mode ) {
init_timer ( & ctrl - > poll_timer ) ;
start_int_poll_timer ( ctrl , 10 ) ;
return 0 ;
}
/* Installs the interrupt handler */
retval = request_irq ( irq , pcie_isr , IRQF_SHARED , MY_NAME , ctrl ) ;
if ( retval )
err ( " Cannot get irq %d for the hotplug controller \n " , irq ) ;
return retval ;
}
static inline void pciehp_free_irq ( struct controller * ctrl )
{
if ( pciehp_poll_mode )
del_timer_sync ( & ctrl - > poll_timer ) ;
else
free_irq ( ctrl - > pci_dev - > irq , ctrl ) ;
}
2006-12-22 04:01:09 +03:00
static inline int pcie_wait_cmd ( struct controller * ctrl )
{
2006-12-22 04:01:10 +03:00
int retval = 0 ;
unsigned int msecs = pciehp_poll_mode ? 2500 : 1000 ;
unsigned long timeout = msecs_to_jiffies ( msecs ) ;
int rc ;
rc = wait_event_interruptible_timeout ( ctrl - > queue ,
! ctrl - > cmd_busy , timeout ) ;
if ( ! rc )
dbg ( " Command not completed in 1000 msec \n " ) ;
else if ( rc < 0 ) {
retval = - EINTR ;
info ( " Command was interrupted by a signal \n " ) ;
}
2006-12-22 04:01:09 +03:00
2006-12-22 04:01:10 +03:00
return retval ;
2006-12-22 04:01:09 +03:00
}
2007-05-31 20:43:34 +04:00
/**
* pcie_write_cmd - Issue controller command
2008-04-26 01:39:05 +04:00
* @ ctrl : controller to which the command is issued
2007-05-31 20:43:34 +04:00
* @ cmd : command value written to slot control register
* @ mask : bitmask of slot control register to be modified
*/
2008-04-26 01:39:05 +04:00
static int pcie_write_cmd ( struct controller * ctrl , u16 cmd , u16 mask )
2005-04-17 02:20:36 +04:00
{
int retval = 0 ;
u16 slot_status ;
2007-05-31 20:43:34 +04:00
u16 slot_ctrl ;
2005-04-17 02:20:36 +04:00
2006-12-22 04:01:09 +03:00
mutex_lock ( & ctrl - > ctrl_lock ) ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , SLOTSTATUS , & slot_status ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read SLOTSTATUS register \n " , __func__ ) ;
2006-12-22 04:01:09 +03:00
goto out ;
2006-12-22 04:01:06 +03:00
}
2007-08-10 03:09:34 +04:00
if ( ( slot_status & CMD_COMPLETED ) = = CMD_COMPLETED ) {
2006-12-22 04:01:09 +03:00
/* After 1 sec and CMD_COMPLETED still not set, just
proceed forward to issue the next command according
to spec . Just print out the error message */
dbg ( " %s: CMD_COMPLETED not clear after 1 sec. \n " ,
2008-03-04 06:09:46 +03:00
__func__ ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-31 20:43:34 +04:00
retval = pciehp_readw ( ctrl , SLOTCTRL , & slot_ctrl ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read SLOTCTRL register \n " , __func__ ) ;
2008-04-26 01:38:57 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2007-05-31 20:43:34 +04:00
slot_ctrl & = ~ mask ;
2008-04-26 01:39:14 +04:00
slot_ctrl | = ( cmd & mask ) ;
/* Don't enable command completed if caller is changing it. */
if ( ! ( mask & CMD_CMPL_INTR_ENABLE ) )
slot_ctrl | = CMD_CMPL_INTR_ENABLE ;
2007-05-31 20:43:34 +04:00
ctrl - > cmd_busy = 1 ;
2008-04-26 01:39:02 +04:00
smp_mb ( ) ;
2007-05-31 20:43:34 +04:00
retval = pciehp_writew ( ctrl , SLOTCTRL , slot_ctrl ) ;
if ( retval )
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot write to SLOTCTRL register \n " , __func__ ) ;
2007-05-31 20:43:34 +04:00
2006-12-22 04:01:09 +03:00
/*
* Wait for command completion .
*/
2007-05-31 20:43:34 +04:00
if ( ! retval )
retval = pcie_wait_cmd ( ctrl ) ;
2006-12-22 04:01:09 +03:00
out :
mutex_unlock ( & ctrl - > ctrl_lock ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
static int hpc_check_lnk_status ( struct controller * ctrl )
{
u16 lnk_status ;
int retval = 0 ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , LNKSTATUS , & lnk_status ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read LNKSTATUS register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2008-03-04 06:09:46 +03:00
dbg ( " %s: lnk_status = %x \n " , __func__ , lnk_status ) ;
2007-08-10 03:09:34 +04:00
if ( ( lnk_status & LNK_TRN ) | | ( lnk_status & LNK_TRN_ERR ) | |
2005-04-17 02:20:36 +04:00
! ( lnk_status & NEG_LINK_WD ) ) {
2008-03-04 06:09:46 +03:00
err ( " %s : Link Training Error occurs \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
retval = - 1 ;
return retval ;
}
return retval ;
}
static int hpc_get_attention_status ( struct slot * slot , u8 * status )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_ctrl ;
u8 atten_led_state ;
int retval = 0 ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , SLOTCTRL , & slot_ctrl ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read SLOTCTRL register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2006-12-22 04:01:06 +03:00
dbg ( " %s: SLOTCTRL %x, value read %x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ctrl - > cap_base + SLOTCTRL , slot_ctrl ) ;
2005-04-17 02:20:36 +04:00
atten_led_state = ( slot_ctrl & ATTN_LED_CTRL ) > > 6 ;
switch ( atten_led_state ) {
case 0 :
* status = 0xFF ; /* Reserved */
break ;
case 1 :
* status = 1 ; /* On */
break ;
case 2 :
* status = 2 ; /* Blink */
break ;
case 3 :
* status = 0 ; /* Off */
break ;
default :
* status = 0xFF ;
break ;
}
return 0 ;
}
2006-12-22 04:01:04 +03:00
static int hpc_get_power_status ( struct slot * slot , u8 * status )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_ctrl ;
u8 pwr_state ;
int retval = 0 ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , SLOTCTRL , & slot_ctrl ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read SLOTCTRL register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2006-12-22 04:01:06 +03:00
dbg ( " %s: SLOTCTRL %x value read %x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ctrl - > cap_base + SLOTCTRL , slot_ctrl ) ;
2005-04-17 02:20:36 +04:00
pwr_state = ( slot_ctrl & PWR_CTRL ) > > 10 ;
switch ( pwr_state ) {
case 0 :
* status = 1 ;
break ;
case 1 :
2007-08-10 03:09:34 +04:00
* status = 0 ;
2005-04-17 02:20:36 +04:00
break ;
default :
* status = 0xFF ;
break ;
}
return retval ;
}
static int hpc_get_latch_status ( struct slot * slot , u8 * status )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_status ;
int retval = 0 ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , SLOTSTATUS , & slot_status ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read SLOTSTATUS register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2007-08-10 03:09:34 +04:00
* status = ( ( ( slot_status & MRL_STATE ) > > 5 ) = = 0 ) ? 0 : 1 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int hpc_get_adapter_status ( struct slot * slot , u8 * status )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_status ;
u8 card_state ;
int retval = 0 ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , SLOTSTATUS , & slot_status ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read SLOTSTATUS register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
card_state = ( u8 ) ( ( slot_status & PRSN_STATE ) > > 6 ) ;
* status = ( card_state = = 1 ) ? 1 : 0 ;
return 0 ;
}
2006-12-22 04:01:04 +03:00
static int hpc_query_power_fault ( struct slot * slot )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_status ;
u8 pwr_fault ;
int retval = 0 ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , SLOTSTATUS , & slot_status ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot check for power fault \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
pwr_fault = ( u8 ) ( ( slot_status & PWR_FAULT_DETECTED ) > > 1 ) ;
2007-08-10 03:09:34 +04:00
2005-11-01 03:20:13 +03:00
return pwr_fault ;
2005-04-17 02:20:36 +04:00
}
2007-01-10 00:02:36 +03:00
static int hpc_get_emi_status ( struct slot * slot , u8 * status )
{
struct controller * ctrl = slot - > ctrl ;
u16 slot_status ;
int retval = 0 ;
retval = pciehp_readw ( ctrl , SLOTSTATUS , & slot_status ) ;
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s : Cannot check EMI status \n " , __func__ ) ;
2007-01-10 00:02:36 +03:00
return retval ;
}
* status = ( slot_status & EMI_STATE ) > > EMI_STATUS_BIT ;
return retval ;
}
static int hpc_toggle_emi ( struct slot * slot )
{
2007-05-31 20:43:34 +04:00
u16 slot_cmd ;
u16 cmd_mask ;
int rc ;
2007-01-10 00:02:36 +03:00
2007-05-31 20:43:34 +04:00
slot_cmd = EMI_CTRL ;
cmd_mask = EMI_CTRL ;
2008-04-26 01:39:05 +04:00
rc = pcie_write_cmd ( slot - > ctrl , slot_cmd , cmd_mask ) ;
2007-01-10 00:02:36 +03:00
slot - > last_emi_toggle = get_seconds ( ) ;
2007-08-10 03:09:33 +04:00
2007-01-10 00:02:36 +03:00
return rc ;
}
2005-04-17 02:20:36 +04:00
static int hpc_set_attention_status ( struct slot * slot , u8 value )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2007-05-31 20:43:34 +04:00
u16 slot_cmd ;
u16 cmd_mask ;
int rc ;
2005-04-17 02:20:36 +04:00
2007-05-31 20:43:34 +04:00
cmd_mask = ATTN_LED_CTRL ;
2005-04-17 02:20:36 +04:00
switch ( value ) {
case 0 : /* turn off */
2007-05-31 20:43:34 +04:00
slot_cmd = 0x00C0 ;
2005-04-17 02:20:36 +04:00
break ;
case 1 : /* turn on */
2007-05-31 20:43:34 +04:00
slot_cmd = 0x0040 ;
2005-04-17 02:20:36 +04:00
break ;
case 2 : /* turn blink */
2007-05-31 20:43:34 +04:00
slot_cmd = 0x0080 ;
2005-04-17 02:20:36 +04:00
break ;
default :
return - 1 ;
}
2008-04-26 01:39:05 +04:00
rc = pcie_write_cmd ( ctrl , slot_cmd , cmd_mask ) ;
2006-12-22 04:01:06 +03:00
dbg ( " %s: SLOTCTRL %x write cmd %x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ctrl - > cap_base + SLOTCTRL , slot_cmd ) ;
2007-08-10 03:09:34 +04:00
2005-04-17 02:20:36 +04:00
return rc ;
}
static void hpc_set_green_led_on ( struct slot * slot )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_cmd ;
2007-05-31 20:43:34 +04:00
u16 cmd_mask ;
2007-08-10 03:09:34 +04:00
2007-05-31 20:43:34 +04:00
slot_cmd = 0x0100 ;
cmd_mask = PWR_LED_CTRL ;
2008-04-26 01:39:05 +04:00
pcie_write_cmd ( ctrl , slot_cmd , cmd_mask ) ;
2006-12-22 04:01:06 +03:00
dbg ( " %s: SLOTCTRL %x write cmd %x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ctrl - > cap_base + SLOTCTRL , slot_cmd ) ;
2005-04-17 02:20:36 +04:00
}
static void hpc_set_green_led_off ( struct slot * slot )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_cmd ;
2007-05-31 20:43:34 +04:00
u16 cmd_mask ;
2005-04-17 02:20:36 +04:00
2007-05-31 20:43:34 +04:00
slot_cmd = 0x0300 ;
cmd_mask = PWR_LED_CTRL ;
2008-04-26 01:39:05 +04:00
pcie_write_cmd ( ctrl , slot_cmd , cmd_mask ) ;
2006-12-22 04:01:06 +03:00
dbg ( " %s: SLOTCTRL %x write cmd %x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ctrl - > cap_base + SLOTCTRL , slot_cmd ) ;
2005-04-17 02:20:36 +04:00
}
static void hpc_set_green_led_blink ( struct slot * slot )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_cmd ;
2007-05-31 20:43:34 +04:00
u16 cmd_mask ;
2007-08-10 03:09:34 +04:00
2007-05-31 20:43:34 +04:00
slot_cmd = 0x0200 ;
cmd_mask = PWR_LED_CTRL ;
2008-04-26 01:39:05 +04:00
pcie_write_cmd ( ctrl , slot_cmd , cmd_mask ) ;
2006-12-22 04:01:06 +03:00
dbg ( " %s: SLOTCTRL %x write cmd %x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ctrl - > cap_base + SLOTCTRL , slot_cmd ) ;
2005-04-17 02:20:36 +04:00
}
static void hpc_release_ctlr ( struct controller * ctrl )
{
2008-04-26 01:39:07 +04:00
/* Mask Hot-plug Interrupt Enable */
if ( pcie_write_cmd ( ctrl , 0 , HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE ) )
err ( " %s: Cannot mask hotplut interrupt enable \n " , __func__ ) ;
2008-04-26 01:39:08 +04:00
/* Free interrupt handler or interrupt polling timer */
pciehp_free_irq ( ctrl ) ;
2005-04-17 02:20:36 +04:00
2007-03-07 02:02:26 +03:00
/*
* If this is the last controller to be released , destroy the
* pciehp work queue
*/
if ( atomic_dec_and_test ( & pciehp_num_controllers ) )
destroy_workqueue ( pciehp_wq ) ;
2005-04-17 02:20:36 +04:00
}
static int hpc_power_on_slot ( struct slot * slot )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_cmd ;
2007-05-31 20:43:34 +04:00
u16 cmd_mask ;
u16 slot_status ;
2005-04-17 02:20:36 +04:00
int retval = 0 ;
2008-03-04 06:09:46 +03:00
dbg ( " %s: slot->hp_slot %x \n " , __func__ , slot - > hp_slot ) ;
2005-04-17 02:20:36 +04:00
2005-11-24 02:44:54 +03:00
/* Clear sticky power-fault bit from previous power failures */
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , SLOTSTATUS , & slot_status ) ;
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read SLOTSTATUS register \n " , __func__ ) ;
2006-12-22 04:01:06 +03:00
return retval ;
}
2005-11-24 02:44:54 +03:00
slot_status & = PWR_FAULT_DETECTED ;
2006-12-22 04:01:06 +03:00
if ( slot_status ) {
retval = pciehp_writew ( ctrl , SLOTSTATUS , slot_status ) ;
if ( retval ) {
err ( " %s: Cannot write to SLOTSTATUS register \n " ,
2008-03-04 06:09:46 +03:00
__func__ ) ;
2006-12-22 04:01:06 +03:00
return retval ;
}
}
2005-04-17 02:20:36 +04:00
2007-05-31 20:43:34 +04:00
slot_cmd = POWER_ON ;
cmd_mask = PWR_CTRL ;
2005-12-08 22:55:57 +03:00
/* Enable detection that we turned off at slot power-off time */
2007-05-31 20:43:34 +04:00
if ( ! pciehp_poll_mode ) {
2008-04-26 01:39:06 +04:00
slot_cmd | = ( PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |
PRSN_DETECT_ENABLE ) ;
cmd_mask | = ( PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |
PRSN_DETECT_ENABLE ) ;
2007-05-31 20:43:34 +04:00
}
2005-04-17 02:20:36 +04:00
2008-04-26 01:39:05 +04:00
retval = pcie_write_cmd ( ctrl , slot_cmd , cmd_mask ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Write %x command failed! \n " , __func__ , slot_cmd ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2006-12-22 04:01:06 +03:00
dbg ( " %s: SLOTCTRL %x write cmd %x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ctrl - > cap_base + SLOTCTRL , slot_cmd ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2007-12-20 13:45:09 +03:00
static inline int pcie_mask_bad_dllp ( struct controller * ctrl )
{
struct pci_dev * dev = ctrl - > pci_dev ;
int pos ;
u32 reg ;
pos = pci_find_ext_capability ( dev , PCI_EXT_CAP_ID_ERR ) ;
if ( ! pos )
return 0 ;
pci_read_config_dword ( dev , pos + PCI_ERR_COR_MASK , & reg ) ;
if ( reg & PCI_ERR_COR_BAD_DLLP )
return 0 ;
reg | = PCI_ERR_COR_BAD_DLLP ;
pci_write_config_dword ( dev , pos + PCI_ERR_COR_MASK , reg ) ;
return 1 ;
}
static inline void pcie_unmask_bad_dllp ( struct controller * ctrl )
{
struct pci_dev * dev = ctrl - > pci_dev ;
u32 reg ;
int pos ;
pos = pci_find_ext_capability ( dev , PCI_EXT_CAP_ID_ERR ) ;
if ( ! pos )
return ;
pci_read_config_dword ( dev , pos + PCI_ERR_COR_MASK , & reg ) ;
if ( ! ( reg & PCI_ERR_COR_BAD_DLLP ) )
return ;
reg & = ~ PCI_ERR_COR_BAD_DLLP ;
pci_write_config_dword ( dev , pos + PCI_ERR_COR_MASK , reg ) ;
}
2005-04-17 02:20:36 +04:00
static int hpc_power_off_slot ( struct slot * slot )
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
u16 slot_cmd ;
2007-05-31 20:43:34 +04:00
u16 cmd_mask ;
2005-04-17 02:20:36 +04:00
int retval = 0 ;
2007-12-20 13:45:09 +03:00
int changed ;
2005-04-17 02:20:36 +04:00
2008-03-04 06:09:46 +03:00
dbg ( " %s: slot->hp_slot %x \n " , __func__ , slot - > hp_slot ) ;
2005-04-17 02:20:36 +04:00
2007-12-20 13:45:09 +03:00
/*
* Set Bad DLLP Mask bit in Correctable Error Mask
* Register . This is the workaround against Bad DLLP error
* that sometimes happens during turning power off the slot
* which conforms to PCI Express 1.0 a spec .
*/
changed = pcie_mask_bad_dllp ( ctrl ) ;
2007-05-31 20:43:34 +04:00
slot_cmd = POWER_OFF ;
cmd_mask = PWR_CTRL ;
2005-12-08 22:55:57 +03:00
/*
* If we get MRL or presence detect interrupts now , the isr
* will notice the sticky power - fault bit too and issue power
* indicator change commands . This will lead to an endless loop
* of command completions , since the power - fault bit remains on
* till the slot is powered on again .
*/
2007-05-31 20:43:34 +04:00
if ( ! pciehp_poll_mode ) {
2008-04-26 01:39:06 +04:00
slot_cmd & = ~ ( PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |
PRSN_DETECT_ENABLE ) ;
cmd_mask | = ( PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |
PRSN_DETECT_ENABLE ) ;
2007-05-31 20:43:34 +04:00
}
2005-04-17 02:20:36 +04:00
2008-04-26 01:39:05 +04:00
retval = pcie_write_cmd ( ctrl , slot_cmd , cmd_mask ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Write command failed! \n " , __func__ ) ;
2008-03-05 00:01:14 +03:00
retval = - 1 ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2006-12-22 04:01:06 +03:00
dbg ( " %s: SLOTCTRL %x write cmd %x \n " ,
2008-03-04 06:09:46 +03:00
__func__ , ctrl - > cap_base + SLOTCTRL , slot_cmd ) ;
2005-04-17 02:20:36 +04:00
2007-12-20 13:43:56 +03:00
/*
* After turning power off , we must wait for at least 1 second
* before taking any action that relies on power having been
* removed from the slot / adapter .
*/
msleep ( 1000 ) ;
2008-03-05 00:01:14 +03:00
out :
2007-12-20 13:45:09 +03:00
if ( changed )
pcie_unmask_bad_dllp ( ctrl ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2006-12-22 04:01:04 +03:00
static irqreturn_t pcie_isr ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = ( struct controller * ) dev_id ;
2008-04-26 01:38:57 +04:00
u16 detected , intr_loc ;
2008-05-27 14:03:16 +04:00
struct slot * p_slot ;
2005-04-17 02:20:36 +04:00
2008-04-26 01:38:57 +04:00
/*
* In order to guarantee that all interrupt events are
* serviced , we need to re - inspect Slot Status register after
* clearing what is presumed to be the last pending interrupt .
*/
intr_loc = 0 ;
do {
if ( pciehp_readw ( ctrl , SLOTSTATUS , & detected ) ) {
err ( " %s: Cannot read SLOTSTATUS \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return IRQ_NONE ;
}
2008-04-26 01:38:57 +04:00
detected & = ( ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED |
MRL_SENS_CHANGED | PRSN_DETECT_CHANGED |
CMD_COMPLETED ) ;
intr_loc | = detected ;
if ( ! intr_loc )
2005-04-17 02:20:36 +04:00
return IRQ_NONE ;
2008-04-26 01:38:57 +04:00
if ( pciehp_writew ( ctrl , SLOTSTATUS , detected ) ) {
err ( " %s: Cannot write to SLOTSTATUS \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return IRQ_NONE ;
}
2008-04-26 01:38:57 +04:00
} while ( detected ) ;
2007-08-10 03:09:34 +04:00
2008-04-26 01:38:57 +04:00
dbg ( " %s: intr_loc %x \n " , __FUNCTION__ , intr_loc ) ;
2007-08-10 03:09:34 +04:00
2008-04-26 01:38:57 +04:00
/* Check Command Complete Interrupt Pending */
2005-04-17 02:20:36 +04:00
if ( intr_loc & CMD_COMPLETED ) {
2006-12-22 04:01:10 +03:00
ctrl - > cmd_busy = 0 ;
2008-04-26 01:39:02 +04:00
smp_mb ( ) ;
2005-04-17 02:20:36 +04:00
wake_up_interruptible ( & ctrl - > queue ) ;
}
2008-05-27 14:03:16 +04:00
if ( ! ( intr_loc & ~ CMD_COMPLETED ) )
return IRQ_HANDLED ;
/*
* Return without handling events if this handler routine is
* called before controller initialization is done . This may
* happen if hotplug event or another interrupt that shares
* the IRQ with pciehp arrives before slot initialization is
* done after interrupt handler is registered .
*
* FIXME - Need more structural fixes . We need to be ready to
* handle the event before installing interrupt handler .
*/
p_slot = pciehp_find_slot ( ctrl , ctrl - > slot_device_offset ) ;
if ( ! p_slot | | ! p_slot - > hpc_ops )
return IRQ_HANDLED ;
2008-04-26 01:38:57 +04:00
/* Check MRL Sensor Changed */
2006-12-22 04:01:04 +03:00
if ( intr_loc & MRL_SENS_CHANGED )
2008-05-27 14:03:16 +04:00
pciehp_handle_switch_change ( p_slot ) ;
2006-12-22 04:01:04 +03:00
2008-04-26 01:38:57 +04:00
/* Check Attention Button Pressed */
2006-12-22 04:01:04 +03:00
if ( intr_loc & ATTN_BUTTN_PRESSED )
2008-05-27 14:03:16 +04:00
pciehp_handle_attention_button ( p_slot ) ;
2006-12-22 04:01:04 +03:00
2008-04-26 01:38:57 +04:00
/* Check Presence Detect Changed */
2006-12-22 04:01:04 +03:00
if ( intr_loc & PRSN_DETECT_CHANGED )
2008-05-27 14:03:16 +04:00
pciehp_handle_presence_change ( p_slot ) ;
2006-12-22 04:01:04 +03:00
2008-04-26 01:38:57 +04:00
/* Check Power Fault Detected */
2006-12-22 04:01:04 +03:00
if ( intr_loc & PWR_FAULT_DETECTED )
2008-05-27 14:03:16 +04:00
pciehp_handle_power_fault ( p_slot ) ;
2007-08-10 03:09:34 +04:00
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
2007-08-10 03:09:38 +04:00
static int hpc_get_max_lnk_speed ( struct slot * slot , enum pci_bus_speed * value )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
enum pcie_link_speed lnk_speed ;
u32 lnk_cap ;
int retval = 0 ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readl ( ctrl , LNKCAP , & lnk_cap ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read LNKCAP register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
switch ( lnk_cap & 0x000F ) {
case 1 :
lnk_speed = PCIE_2PT5GB ;
break ;
default :
lnk_speed = PCIE_LNK_SPEED_UNKNOWN ;
break ;
}
* value = lnk_speed ;
dbg ( " Max link speed = %d \n " , lnk_speed ) ;
2007-08-10 03:09:33 +04:00
2005-04-17 02:20:36 +04:00
return retval ;
}
2007-08-10 03:09:38 +04:00
static int hpc_get_max_lnk_width ( struct slot * slot ,
enum pcie_link_width * value )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
enum pcie_link_width lnk_wdth ;
u32 lnk_cap ;
int retval = 0 ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readl ( ctrl , LNKCAP , & lnk_cap ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read LNKCAP register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
switch ( ( lnk_cap & 0x03F0 ) > > 4 ) {
case 0 :
lnk_wdth = PCIE_LNK_WIDTH_RESRV ;
break ;
case 1 :
lnk_wdth = PCIE_LNK_X1 ;
break ;
case 2 :
lnk_wdth = PCIE_LNK_X2 ;
break ;
case 4 :
lnk_wdth = PCIE_LNK_X4 ;
break ;
case 8 :
lnk_wdth = PCIE_LNK_X8 ;
break ;
case 12 :
lnk_wdth = PCIE_LNK_X12 ;
break ;
case 16 :
lnk_wdth = PCIE_LNK_X16 ;
break ;
case 32 :
lnk_wdth = PCIE_LNK_X32 ;
break ;
default :
lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN ;
break ;
}
* value = lnk_wdth ;
dbg ( " Max link width = %d \n " , lnk_wdth ) ;
2007-08-10 03:09:33 +04:00
2005-04-17 02:20:36 +04:00
return retval ;
}
2007-08-10 03:09:38 +04:00
static int hpc_get_cur_lnk_speed ( struct slot * slot , enum pci_bus_speed * value )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN ;
int retval = 0 ;
u16 lnk_status ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , LNKSTATUS , & lnk_status ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read LNKSTATUS register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
switch ( lnk_status & 0x0F ) {
case 1 :
lnk_speed = PCIE_2PT5GB ;
break ;
default :
lnk_speed = PCIE_LNK_SPEED_UNKNOWN ;
break ;
}
* value = lnk_speed ;
dbg ( " Current link speed = %d \n " , lnk_speed ) ;
2007-08-10 03:09:33 +04:00
2005-04-17 02:20:36 +04:00
return retval ;
}
2007-08-10 03:09:38 +04:00
static int hpc_get_cur_lnk_width ( struct slot * slot ,
enum pcie_link_width * value )
2005-04-17 02:20:36 +04:00
{
2006-12-22 04:01:04 +03:00
struct controller * ctrl = slot - > ctrl ;
2005-04-17 02:20:36 +04:00
enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN ;
int retval = 0 ;
u16 lnk_status ;
2006-12-22 04:01:06 +03:00
retval = pciehp_readw ( ctrl , LNKSTATUS , & lnk_status ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read LNKSTATUS register \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2007-08-10 03:09:34 +04:00
2005-04-17 02:20:36 +04:00
switch ( ( lnk_status & 0x03F0 ) > > 4 ) {
case 0 :
lnk_wdth = PCIE_LNK_WIDTH_RESRV ;
break ;
case 1 :
lnk_wdth = PCIE_LNK_X1 ;
break ;
case 2 :
lnk_wdth = PCIE_LNK_X2 ;
break ;
case 4 :
lnk_wdth = PCIE_LNK_X4 ;
break ;
case 8 :
lnk_wdth = PCIE_LNK_X8 ;
break ;
case 12 :
lnk_wdth = PCIE_LNK_X12 ;
break ;
case 16 :
lnk_wdth = PCIE_LNK_X16 ;
break ;
case 32 :
lnk_wdth = PCIE_LNK_X32 ;
break ;
default :
lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN ;
break ;
}
* value = lnk_wdth ;
dbg ( " Current link width = %d \n " , lnk_wdth ) ;
2007-08-10 03:09:33 +04:00
2005-04-17 02:20:36 +04:00
return retval ;
}
static struct hpc_ops pciehp_hpc_ops = {
. power_on_slot = hpc_power_on_slot ,
. power_off_slot = hpc_power_off_slot ,
. set_attention_status = hpc_set_attention_status ,
. get_power_status = hpc_get_power_status ,
. get_attention_status = hpc_get_attention_status ,
. get_latch_status = hpc_get_latch_status ,
. get_adapter_status = hpc_get_adapter_status ,
2007-01-10 00:02:36 +03:00
. get_emi_status = hpc_get_emi_status ,
. toggle_emi = hpc_toggle_emi ,
2005-04-17 02:20:36 +04:00
. get_max_bus_speed = hpc_get_max_lnk_speed ,
. get_cur_bus_speed = hpc_get_cur_lnk_speed ,
. get_max_lnk_width = hpc_get_max_lnk_width ,
. get_cur_lnk_width = hpc_get_cur_lnk_width ,
2007-08-10 03:09:34 +04:00
2005-04-17 02:20:36 +04:00
. query_power_fault = hpc_query_power_fault ,
. green_led_on = hpc_set_green_led_on ,
. green_led_off = hpc_set_green_led_off ,
. green_led_blink = hpc_set_green_led_blink ,
2007-08-10 03:09:34 +04:00
2005-04-17 02:20:36 +04:00
. release_ctlr = hpc_release_ctlr ,
. check_lnk_status = hpc_check_lnk_status ,
} ;
2006-03-03 21:16:05 +03:00
# ifdef CONFIG_ACPI
2008-04-26 01:39:10 +04:00
static int pciehp_acpi_get_hp_hw_control_from_firmware ( struct pci_dev * dev )
2006-03-03 21:16:05 +03:00
{
acpi_status status ;
acpi_handle chandle , handle = DEVICE_ACPI_HANDLE ( & ( dev - > dev ) ) ;
struct pci_dev * pdev = dev ;
struct pci_bus * parent ;
2006-03-17 03:18:39 +03:00
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER , NULL } ;
2006-03-03 21:16:05 +03:00
/*
* Per PCI firmware specification , we should run the ACPI _OSC
* method to get control of hotplug hardware before using it .
* If an _OSC is missing , we look for an OSHP to do the same thing .
* To handle different BIOS behavior , we look for _OSC and OSHP
* within the scope of the hotplug controller and its parents , upto
* the host bridge under which this controller exists .
*/
while ( ! handle ) {
/*
* This hotplug controller was not listed in the ACPI name
* space at all . Try to get acpi handle of parent pci bus .
*/
if ( ! pdev | | ! pdev - > bus - > parent )
break ;
parent = pdev - > bus - > parent ;
dbg ( " Could not find %s in acpi namespace, trying parent \n " ,
pci_name ( pdev ) ) ;
if ( ! parent - > self )
/* Parent must be a host bridge */
handle = acpi_get_pci_rootbridge_handle (
pci_domain_nr ( parent ) ,
parent - > number ) ;
else
handle = DEVICE_ACPI_HANDLE (
& ( parent - > self - > dev ) ) ;
pdev = parent - > self ;
}
while ( handle ) {
2006-03-17 03:18:39 +03:00
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & string ) ;
dbg ( " Trying to get hotplug control for %s \n " ,
( char * ) string . pointer ) ;
2006-03-03 21:16:05 +03:00
status = pci_osc_control_set ( handle ,
2007-08-10 03:09:32 +04:00
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL |
2006-03-03 21:16:05 +03:00
OSC_PCI_EXPRESS_NATIVE_HP_CONTROL ) ;
if ( status = = AE_NOT_FOUND )
status = acpi_run_oshp ( handle ) ;
if ( ACPI_SUCCESS ( status ) ) {
dbg ( " Gained control for hotplug HW for pci %s (%s) \n " ,
2006-03-17 03:18:39 +03:00
pci_name ( dev ) , ( char * ) string . pointer ) ;
2006-04-19 01:36:43 +04:00
kfree ( string . pointer ) ;
2006-03-03 21:16:05 +03:00
return 0 ;
}
if ( acpi_root_bridge ( handle ) )
break ;
chandle = handle ;
status = acpi_get_parent ( chandle , & handle ) ;
if ( ACPI_FAILURE ( status ) )
break ;
}
2008-04-29 20:15:04 +04:00
dbg ( " Cannot get control of hotplug hardware for pci %s \n " ,
2006-03-03 21:16:05 +03:00
pci_name ( dev ) ) ;
2006-03-17 03:18:39 +03:00
2006-04-19 01:36:43 +04:00
kfree ( string . pointer ) ;
2006-03-03 21:16:05 +03:00
return - 1 ;
}
# endif
2007-11-22 02:07:55 +03:00
static int pcie_init_hardware_part1 ( struct controller * ctrl ,
struct pcie_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-05-27 14:03:16 +04:00
/* Clear all remaining event bits in Slot Status register */
if ( pciehp_writew ( ctrl , SLOTSTATUS , 0x1f ) ) {
err ( " %s: Cannot write to SLOTSTATUS register \n " , __func__ ) ;
return - 1 ;
}
2005-04-17 02:20:36 +04:00
/* Mask Hot-plug Interrupt Enable */
2008-04-26 01:39:05 +04:00
if ( pcie_write_cmd ( ctrl , 0 , HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE ) ) {
err ( " %s: Cannot mask hotplug interrupt enable \n " , __func__ ) ;
2007-11-22 02:07:55 +03:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
2007-11-22 02:07:55 +03:00
return 0 ;
}
2005-11-01 03:20:10 +03:00
2007-11-22 02:07:55 +03:00
int pcie_init_hardware_part2 ( struct controller * ctrl , struct pcie_device * dev )
{
2008-04-26 01:39:05 +04:00
u16 cmd , mask ;
2005-04-17 02:20:36 +04:00
2008-04-26 01:39:05 +04:00
cmd = PRSN_DETECT_ENABLE ;
2008-04-26 01:39:06 +04:00
if ( ATTN_BUTTN ( ctrl ) )
2008-04-26 01:39:05 +04:00
cmd | = ATTN_BUTTN_ENABLE ;
2008-04-26 01:39:06 +04:00
if ( POWER_CTRL ( ctrl ) )
2008-04-26 01:39:05 +04:00
cmd | = PWR_FAULT_DETECT_ENABLE ;
2008-04-26 01:39:06 +04:00
if ( MRL_SENS ( ctrl ) )
2008-04-26 01:39:05 +04:00
cmd | = MRL_DETECT_ENABLE ;
if ( ! pciehp_poll_mode )
cmd | = HP_INTR_ENABLE ;
mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE |
PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | HP_INTR_ENABLE ;
if ( pcie_write_cmd ( ctrl , cmd , mask ) ) {
err ( " %s: Cannot enable software notification \n " , __func__ ) ;
goto abort ;
2005-04-17 02:20:36 +04:00
}
2007-08-10 03:09:34 +04:00
2008-04-26 01:39:05 +04:00
if ( pciehp_force )
2005-11-01 03:20:12 +03:00
dbg ( " Bypassing BIOS check for pciehp use on %s \n " ,
pci_name ( ctrl - > pci_dev ) ) ;
2008-04-26 01:39:05 +04:00
else if ( pciehp_get_hp_hw_control_from_firmware ( ctrl - > pci_dev ) )
goto abort_disable_intr ;
2005-11-01 03:20:07 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
2007-08-10 03:09:38 +04:00
/* We end up here for the many possible ways to fail this API. */
2006-05-09 11:50:31 +04:00
abort_disable_intr :
2008-04-26 01:39:05 +04:00
if ( pcie_write_cmd ( ctrl , 0 , HP_INTR_ENABLE ) )
2008-03-04 06:09:46 +03:00
err ( " %s : disabling interrupts failed \n " , __func__ ) ;
2007-11-22 02:07:55 +03:00
abort :
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2007-11-29 02:11:46 +03:00
2008-04-26 01:39:08 +04:00
static inline void dbg_ctrl ( struct controller * ctrl )
2007-11-29 02:11:46 +03:00
{
2008-04-26 01:39:08 +04:00
int i ;
u16 reg16 ;
struct pci_dev * pdev = ctrl - > pci_dev ;
2007-11-29 02:11:46 +03:00
2008-04-26 01:39:08 +04:00
if ( ! pciehp_debug )
return ;
2007-11-29 02:11:46 +03:00
2008-04-26 01:39:08 +04:00
dbg ( " Hotplug Controller: \n " ) ;
dbg ( " Seg/Bus/Dev/Func/IRQ : %s IRQ %d \n " , pci_name ( pdev ) , pdev - > irq ) ;
dbg ( " Vendor ID : 0x%04x \n " , pdev - > vendor ) ;
dbg ( " Device ID : 0x%04x \n " , pdev - > device ) ;
dbg ( " Subsystem ID : 0x%04x \n " , pdev - > subsystem_device ) ;
dbg ( " Subsystem Vendor ID : 0x%04x \n " , pdev - > subsystem_vendor ) ;
dbg ( " PCIe Cap offset : 0x%02x \n " , ctrl - > cap_base ) ;
for ( i = 0 ; i < DEVICE_COUNT_RESOURCE ; i + + ) {
if ( ! pci_resource_len ( pdev , i ) )
continue ;
dbg ( " PCI resource [%d] : 0x%llx@0x%llx \n " , i ,
( unsigned long long ) pci_resource_len ( pdev , i ) ,
( unsigned long long ) pci_resource_start ( pdev , i ) ) ;
2007-11-29 02:11:46 +03:00
}
2008-04-26 01:39:08 +04:00
dbg ( " Slot Capabilities : 0x%08x \n " , ctrl - > slot_cap ) ;
dbg ( " Physical Slot Number : %d \n " , ctrl - > first_slot ) ;
dbg ( " Attention Button : %3s \n " , ATTN_BUTTN ( ctrl ) ? " yes " : " no " ) ;
dbg ( " Power Controller : %3s \n " , POWER_CTRL ( ctrl ) ? " yes " : " no " ) ;
dbg ( " MRL Sensor : %3s \n " , MRL_SENS ( ctrl ) ? " yes " : " no " ) ;
dbg ( " Attention Indicator : %3s \n " , ATTN_LED ( ctrl ) ? " yes " : " no " ) ;
dbg ( " Power Indicator : %3s \n " , PWR_LED ( ctrl ) ? " yes " : " no " ) ;
dbg ( " Hot-Plug Surprise : %3s \n " , HP_SUPR_RM ( ctrl ) ? " yes " : " no " ) ;
dbg ( " EMI Present : %3s \n " , EMI ( ctrl ) ? " yes " : " no " ) ;
pciehp_readw ( ctrl , SLOTSTATUS , & reg16 ) ;
dbg ( " Slot Status : 0x%04x \n " , reg16 ) ;
pciehp_readw ( ctrl , SLOTSTATUS , & reg16 ) ;
dbg ( " Slot Control : 0x%04x \n " , reg16 ) ;
}
2007-11-29 02:11:46 +03:00
2008-04-26 01:39:08 +04:00
int pcie_init ( struct controller * ctrl , struct pcie_device * dev )
{
u32 slot_cap ;
struct pci_dev * pdev = dev - > port ;
2007-11-29 02:11:46 +03:00
2008-04-26 01:39:08 +04:00
ctrl - > pci_dev = pdev ;
ctrl - > cap_base = pci_find_capability ( pdev , PCI_CAP_ID_EXP ) ;
if ( ! ctrl - > cap_base ) {
err ( " %s: Cannot find PCI Express capability \n " , __func__ ) ;
2007-11-29 02:11:46 +03:00
goto abort ;
}
2008-04-26 01:39:08 +04:00
if ( pciehp_readl ( ctrl , SLOTCAP , & slot_cap ) ) {
2008-03-04 06:09:46 +03:00
err ( " %s: Cannot read SLOTCAP register \n " , __func__ ) ;
2007-11-29 02:11:46 +03:00
goto abort ;
}
2008-04-26 01:39:08 +04:00
ctrl - > slot_cap = slot_cap ;
ctrl - > first_slot = slot_cap > > 19 ;
ctrl - > slot_device_offset = 0 ;
ctrl - > num_slots = 1 ;
ctrl - > hpc_ops = & pciehp_hpc_ops ;
2007-11-29 02:11:46 +03:00
mutex_init ( & ctrl - > crit_sect ) ;
mutex_init ( & ctrl - > ctrl_lock ) ;
init_waitqueue_head ( & ctrl - > queue ) ;
2008-04-26 01:39:08 +04:00
dbg_ctrl ( ctrl ) ;
2007-11-29 02:11:46 +03:00
2008-04-26 01:39:08 +04:00
info ( " HPC vendor_id %x device_id %x ss_vid %x ss_did %x \n " ,
pdev - > vendor , pdev - > device ,
pdev - > subsystem_vendor , pdev - > subsystem_device ) ;
2007-11-29 02:11:46 +03:00
2008-04-26 01:39:08 +04:00
if ( pcie_init_hardware_part1 ( ctrl , dev ) )
2007-11-22 02:07:55 +03:00
goto abort ;
2008-04-26 01:39:08 +04:00
if ( pciehp_request_irq ( ctrl ) )
goto abort ;
2007-11-22 02:07:55 +03:00
/*
* If this is the first controller to be initialized ,
* initialize the pciehp work queue
*/
if ( atomic_add_return ( 1 , & pciehp_num_controllers ) = = 1 ) {
pciehp_wq = create_singlethread_workqueue ( " pciehpd " ) ;
if ( ! pciehp_wq ) {
goto abort_free_irq ;
}
}
2008-04-26 01:39:08 +04:00
if ( pcie_init_hardware_part2 ( ctrl , dev ) )
goto abort_free_irq ;
return 0 ;
2007-11-22 02:07:55 +03:00
abort_free_irq :
2008-04-26 01:39:08 +04:00
pciehp_free_irq ( ctrl ) ;
2007-11-29 02:11:46 +03:00
abort :
return - 1 ;
}