diff options
Diffstat (limited to 'drivers/pci/hotplug/pciehp_hpc.c')
| -rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 1861 |
1 files changed, 603 insertions, 1258 deletions
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 77e530321de..42914e04d11 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -35,1450 +35,795 @@ #include <linux/timer.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/slab.h> #include "../pci.h" #include "pciehp.h" -#ifdef DEBUG -#define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */ -#define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */ -#define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */ -#define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */ -#define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT) -#define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE) -/* Redefine this flagword to set debug level */ -#define DEBUG_LEVEL DBG_K_STANDARD - -#define DEFINE_DBG_BUFFER char __dbg_str_buf[256]; - -#define DBG_PRINT( dbg_flags, args... ) \ - do { \ - if ( DEBUG_LEVEL & ( dbg_flags ) ) \ - { \ - int len; \ - len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \ - __FILE__, __LINE__, __FUNCTION__ ); \ - sprintf( __dbg_str_buf + len, args ); \ - printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \ - } \ - } while (0) - -#define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]"); -#define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]"); -#else -#define DEFINE_DBG_BUFFER -#define DBG_ENTER_ROUTINE -#define DBG_LEAVE_ROUTINE -#endif /* DEBUG */ - -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), -}; -static int pcie_cap_base = 0; /* Base of the PCI Express capability item structure */ - -#define PCIE_CAP_ID(cb) ( cb + PCIECAPID ) -#define NXT_CAP_PTR(cb) ( cb + NXTCAPPTR ) -#define CAP_REG(cb) ( cb + CAPREG ) -#define DEV_CAP(cb) ( cb + DEVCAP ) -#define DEV_CTRL(cb) ( cb + DEVCTRL ) -#define DEV_STATUS(cb) ( cb + DEVSTATUS ) -#define LNK_CAP(cb) ( cb + LNKCAP ) -#define LNK_CTRL(cb) ( cb + LNKCTRL ) -#define LNK_STATUS(cb) ( cb + LNKSTATUS ) -#define SLOT_CAP(cb) ( cb + SLOTCAP ) -#define SLOT_CTRL(cb) ( cb + SLOTCTRL ) -#define SLOT_STATUS(cb) ( cb + SLOTSTATUS ) -#define ROOT_CTRL(cb) ( cb + ROOTCTRL ) -#define ROOT_STATUS(cb) ( cb + ROOTSTATUS ) - -#define hp_register_read_word(pdev, reg , value) \ - pci_read_config_word(pdev, reg, &value) - -#define hp_register_read_dword(pdev, reg , value) \ - pci_read_config_dword(pdev, reg, &value) - -#define hp_register_write_word(pdev, reg , value) \ - pci_write_config_word(pdev, reg, value) - -#define hp_register_dwrite_word(pdev, reg , value) \ - pci_write_config_dword(pdev, reg, value) - -/* 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 -#define LNK_X4 0x04 -#define LNK_X8 0x08 -#define LNK_X12 0x0C -#define LNK_X16 0x10 -#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 - -/* 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 - -/* 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 - -static spinlock_t hpc_event_lock; - -DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */ -static struct php_ctlr_state_s *php_ctlr_list_head; /* HPC state linked list */ -static int ctlr_seq_num = 0; /* Controller sequence # */ -static spinlock_t list_lock; - -static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs); - -static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds); - -/* This is the interrupt polling timeout function. */ -static void int_poll_timeout(unsigned long lphp_ctlr) +static inline struct pci_dev *ctrl_dev(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr; + return ctrl->pcie->port; +} - DBG_ENTER_ROUTINE +static irqreturn_t pcie_isr(int irq, void *dev_id); +static void start_int_poll_timer(struct controller *ctrl, int sec); - if ( !php_ctlr ) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return; - } +/* This is the interrupt polling timeout function. */ +static void int_poll_timeout(unsigned long data) +{ + struct controller *ctrl = (struct controller *)data; /* Poll for interrupt events. regs == NULL => polling */ - pcie_isr( 0, (void *)php_ctlr, NULL ); - - init_timer(&php_ctlr->int_poll_timer); + pcie_isr(0, ctrl); + init_timer(&ctrl->poll_timer); if (!pciehp_poll_time) - pciehp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/ + pciehp_poll_time = 2; /* default polling interval is 2 sec */ - start_int_poll_timer(php_ctlr, pciehp_poll_time); - - return; + start_int_poll_timer(ctrl, pciehp_poll_time); } /* This function starts the interrupt polling timer. */ -static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds) +static void start_int_poll_timer(struct controller *ctrl, int sec) { - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return; - } - - if ( ( seconds <= 0 ) || ( seconds > 60 ) ) - seconds = 2; /* Clamp to sane value */ - - php_ctlr->int_poll_timer.function = &int_poll_timeout; - php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */ - php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ; - add_timer(&php_ctlr->int_poll_timer); - - return; + /* 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); } -static int pcie_write_cmd(struct slot *slot, u16 cmd) +static inline int pciehp_request_irq(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - int retval = 0; - u16 slot_status; + int retval, irq = ctrl->pcie->irq; - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } - - retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(slot->ctrl->cap_base), slot_status); - if (retval) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - return retval; - } - - if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) { - /* 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", __FUNCTION__); - } - - retval = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), cmd | CMD_CMPL_INTR_ENABLE); - if (retval) { - err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); - return retval; + /* 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; } - DBG_LEAVE_ROUTINE + /* Installs the interrupt handler */ + retval = request_irq(irq, pcie_isr, IRQF_SHARED, MY_NAME, ctrl); + if (retval) + ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n", + irq); return retval; } -static int hpc_check_lnk_status(struct controller *ctrl) +static inline void pciehp_free_irq(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle; - u16 lnk_status; - int retval = 0; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } - - retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS(ctrl->cap_base), lnk_status); + if (pciehp_poll_mode) + del_timer_sync(&ctrl->poll_timer); + else + free_irq(ctrl->pcie->irq, ctrl); +} - if (retval) { - err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); - return retval; +static int pcie_poll_cmd(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + int timeout = 1000; + + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_CC) { + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC); + return 1; + } + while (timeout > 0) { + msleep(10); + timeout -= 10; + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_CC) { + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC); + return 1; + } } + return 0; /* timeout */ +} - dbg("%s: lnk_status = %x\n", __FUNCTION__, lnk_status); - if ( (lnk_status & LNK_TRN) || (lnk_status & LNK_TRN_ERR) || - !(lnk_status & NEG_LINK_WD)) { - err("%s : Link Training Error occurs \n", __FUNCTION__); - retval = -1; - return retval; - } +static void pcie_wait_cmd(struct controller *ctrl, int poll) +{ + unsigned int msecs = pciehp_poll_mode ? 2500 : 1000; + unsigned long timeout = msecs_to_jiffies(msecs); + int rc; - DBG_LEAVE_ROUTINE - return retval; + if (poll) + rc = pcie_poll_cmd(ctrl); + else + rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout); + if (!rc) + ctrl_dbg(ctrl, "Command not completed in 1000 msec\n"); } - -static int hpc_get_attention_status(struct slot *slot, u8 *status) +/** + * pcie_write_cmd - Issue controller command + * @ctrl: controller to which the command is issued + * @cmd: command value written to slot control register + * @mask: bitmask of slot control register to be modified + */ +static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; u16 slot_ctrl; - u8 atten_led_state; - int retval = 0; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } - retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); - - if (retval) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return retval; + mutex_lock(&ctrl->ctrl_lock); + + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_CC) { + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC); + if (!ctrl->no_cmd_complete) { + /* + * 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. + */ + ctrl_dbg(ctrl, "CMD_COMPLETED not clear after 1 sec\n"); + } else if (!NO_CMD_CMPL(ctrl)) { + /* + * This controller seems to notify of command completed + * event even though it supports none of power + * controller, attention led, power led and EMI. + */ + ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Need to wait for command completed event\n"); + ctrl->no_cmd_complete = 0; + } else { + ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Maybe the controller is broken\n"); + } } - dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__,SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + slot_ctrl &= ~mask; + slot_ctrl |= (cmd & mask); + ctrl->cmd_busy = 1; + smp_mb(); + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); - 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; + /* + * Wait for command completion. + */ + if (!ctrl->no_cmd_complete) { + int poll = 0; + /* + * if hotplug interrupt is not enabled or command + * completed interrupt is not enabled, we need to poll + * command completed event. + */ + if (!(slot_ctrl & PCI_EXP_SLTCTL_HPIE) || + !(slot_ctrl & PCI_EXP_SLTCTL_CCIE)) + poll = 1; + pcie_wait_cmd(ctrl, poll); } - - DBG_LEAVE_ROUTINE - return 0; + mutex_unlock(&ctrl->ctrl_lock); } -static int hpc_get_power_status(struct slot * slot, u8 *status) +bool pciehp_check_link_active(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_ctrl; - u8 pwr_state; - int retval = 0; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 lnk_status; + bool ret; - retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); - if (retval) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return retval; - } - dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); + if (ret) + ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); - pwr_state = (slot_ctrl & PWR_CTRL) >> 10; + return ret; +} - switch (pwr_state) { - case 0: - *status = 1; - break; - case 1: - *status = 0; - break; - default: - *status = 0xFF; - break; - } +static void __pcie_wait_link_active(struct controller *ctrl, bool active) +{ + int timeout = 1000; - DBG_LEAVE_ROUTINE - return retval; + if (pciehp_check_link_active(ctrl) == active) + return; + while (timeout > 0) { + msleep(10); + timeout -= 10; + if (pciehp_check_link_active(ctrl) == active) + return; + } + ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n", + active ? "set" : "cleared"); } +static void pcie_wait_link_active(struct controller *ctrl) +{ + __pcie_wait_link_active(ctrl, true); +} -static int hpc_get_latch_status(struct slot *slot, u8 *status) +static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_status; - int retval = 0; + u32 l; + int count = 0; + int delay = 1000, step = 20; + bool found = false; - DBG_ENTER_ROUTINE + do { + found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0); + count++; - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } - - retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(slot->ctrl->cap_base), slot_status); + if (found) + break; - if (retval) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - return retval; - } + msleep(step); + delay -= step; + } while (delay > 0); - *status = (((slot_status & MRL_STATE) >> 5) == 0) ? 0 : 1; + if (count > 1 && pciehp_debug) + printk(KERN_DEBUG "pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n", + pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), count, step, l); - DBG_LEAVE_ROUTINE - return 0; + return found; } -static int hpc_get_adapter_status(struct slot *slot, u8 *status) +int pciehp_check_link_status(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_status; - u8 card_state; - int retval = 0; - - DBG_ENTER_ROUTINE + struct pci_dev *pdev = ctrl_dev(ctrl); + bool found; + u16 lnk_status; - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + /* + * Data Link Layer Link Active Reporting must be capable for + * hot-plug capable downstream port. But old controller might + * not implement it. In this case, we wait for 1000 ms. + */ + if (ctrl->link_active_reporting) + pcie_wait_link_active(ctrl); + else + msleep(1000); + + /* wait 100ms before read pci conf, and try in 1s */ + msleep(100); + found = pci_bus_check_dev(ctrl->pcie->port->subordinate, + PCI_DEVFN(0, 0)); + + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); + if ((lnk_status & PCI_EXP_LNKSTA_LT) || + !(lnk_status & PCI_EXP_LNKSTA_NLW)) { + ctrl_err(ctrl, "Link Training Error occurs\n"); return -1; } - retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(slot->ctrl->cap_base), slot_status); + pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); - if (retval) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - return retval; - } - card_state = (u8)((slot_status & PRSN_STATE) >> 6); - *status = (card_state == 1) ? 1 : 0; + if (!found) + return -1; - DBG_LEAVE_ROUTINE return 0; } -static int hpc_query_power_fault(struct slot * slot) +static int __pciehp_link_set(struct controller *ctrl, bool enable) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_status; - u8 pwr_fault; - int retval = 0; - - DBG_ENTER_ROUTINE + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 lnk_ctrl; - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl); - retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(slot->ctrl->cap_base), slot_status); + if (enable) + lnk_ctrl &= ~PCI_EXP_LNKCTL_LD; + else + lnk_ctrl |= PCI_EXP_LNKCTL_LD; - if (retval) { - err("%s : Cannot check for power fault\n", __FUNCTION__); - return retval; - } - pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1); - - DBG_LEAVE_ROUTINE - return pwr_fault; + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl); + ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl); + return 0; } -static int hpc_set_attention_status(struct slot *slot, u8 value) +static int pciehp_link_enable(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_cmd = 0; - u16 slot_ctrl; - int rc = 0; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } - - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return -1; - } - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); - - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return rc; - } - - switch (value) { - case 0 : /* turn off */ - slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x00C0; - break; - case 1: /* turn on */ - slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0040; - break; - case 2: /* turn blink */ - slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0080; - break; - default: - return -1; - } - if (!pciehp_poll_mode) - slot_cmd = slot_cmd | HP_INTR_ENABLE; - - pcie_write_cmd(slot, slot_cmd); - dbg("%s: SLOT_CTRL %x write cmd %x\n", __FUNCTION__, SLOT_CTRL(slot->ctrl->cap_base), slot_cmd); - - DBG_LEAVE_ROUTINE - return rc; + return __pciehp_link_set(ctrl, true); } - -static void hpc_set_green_led_on(struct slot *slot) +void pciehp_get_attention_status(struct slot *slot, u8 *status) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_cmd; + struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; - int rc = 0; - - DBG_ENTER_ROUTINE - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return ; - } - - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return ; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); - - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return; + switch (slot_ctrl & PCI_EXP_SLTCTL_AIC) { + case PCI_EXP_SLTCTL_ATTN_IND_ON: + *status = 1; /* On */ + break; + case PCI_EXP_SLTCTL_ATTN_IND_BLINK: + *status = 2; /* Blink */ + break; + case PCI_EXP_SLTCTL_ATTN_IND_OFF: + *status = 0; /* Off */ + break; + default: + *status = 0xFF; + break; } - slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0100; - if (!pciehp_poll_mode) - slot_cmd = slot_cmd | HP_INTR_ENABLE; - - pcie_write_cmd(slot, slot_cmd); - - dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL(slot->ctrl->cap_base), slot_cmd); - DBG_LEAVE_ROUTINE - return; } -static void hpc_set_green_led_off(struct slot *slot) +void pciehp_get_power_status(struct slot *slot, u8 *status) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_cmd; + struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; - int rc = 0; - DBG_ENTER_ROUTINE + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return ; + switch (slot_ctrl & PCI_EXP_SLTCTL_PCC) { + case PCI_EXP_SLTCTL_PWR_ON: + *status = 1; /* On */ + break; + case PCI_EXP_SLTCTL_PWR_OFF: + *status = 0; /* Off */ + break; + default: + *status = 0xFF; + break; } +} - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return ; - } +void pciehp_get_latch_status(struct slot *slot, u8 *status) +{ + struct pci_dev *pdev = ctrl_dev(slot->ctrl); + u16 slot_status; - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); +} - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return; - } +void pciehp_get_adapter_status(struct slot *slot, u8 *status) +{ + struct pci_dev *pdev = ctrl_dev(slot->ctrl); + u16 slot_status; - slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300; + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + *status = !!(slot_status & PCI_EXP_SLTSTA_PDS); +} - if (!pciehp_poll_mode) - slot_cmd = slot_cmd | HP_INTR_ENABLE; - pcie_write_cmd(slot, slot_cmd); - dbg("%s: SLOT_CTRL %x write cmd %x\n", __FUNCTION__, SLOT_CTRL(slot->ctrl->cap_base), slot_cmd); +int pciehp_query_power_fault(struct slot *slot) +{ + struct pci_dev *pdev = ctrl_dev(slot->ctrl); + u16 slot_status; - DBG_LEAVE_ROUTINE - return; + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + return !!(slot_status & PCI_EXP_SLTSTA_PFD); } -static void hpc_set_green_led_blink(struct slot *slot) +void pciehp_set_attention_status(struct slot *slot, u8 value) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; + struct controller *ctrl = slot->ctrl; u16 slot_cmd; - u16 slot_ctrl; - int rc = 0; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return ; - } - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return ; - } - - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); + if (!ATTN_LED(ctrl)) + return; - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + switch (value) { + case 0: /* turn off */ + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF; + break; + case 1: /* turn on */ + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_ON; + break; + case 2: /* turn blink */ + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_BLINK; + break; + default: return; } - - slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0200; - - if (!pciehp_poll_mode) - slot_cmd = slot_cmd | HP_INTR_ENABLE; - pcie_write_cmd(slot, slot_cmd); - - dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL(slot->ctrl->cap_base), slot_cmd); - DBG_LEAVE_ROUTINE - return; + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC); } -int pcie_get_ctlr_slot_config(struct controller *ctrl, - int *num_ctlr_slots, /* number of slots in this HPC; only 1 in PCIE */ - int *first_device_num, /* PCI dev num of the first slot in this PCIE */ - int *physical_slot_num, /* phy slot num of the first slot in this PCIE */ - u8 *ctrlcap) +void pciehp_green_led_on(struct slot *slot) { - struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle; - u32 slot_cap; - int rc = 0; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } - - *first_device_num = 0; - *num_ctlr_slots = 1; - - rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP(ctrl->cap_base), slot_cap); + struct controller *ctrl = slot->ctrl; - if (rc) { - err("%s : hp_register_read_dword SLOT_CAP failed\n", __FUNCTION__); - return -1; - } - - *physical_slot_num = slot_cap >> 19; - dbg("%s: PSN %d \n", __FUNCTION__, *physical_slot_num); - - *ctrlcap = slot_cap & 0x0000007f; + if (!PWR_LED(ctrl)) + return; - DBG_LEAVE_ROUTINE - return 0; + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PIC); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_ON); } -static void hpc_release_ctlr(struct controller *ctrl) +void pciehp_green_led_off(struct slot *slot) { - struct php_ctlr_state_s *php_ctlr = ctrl->hpc_ctlr_handle; - struct php_ctlr_state_s *p, *p_prev; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return ; - } + struct controller *ctrl = slot->ctrl; - if (pciehp_poll_mode) { - del_timer(&php_ctlr->int_poll_timer); - } else { - if (php_ctlr->irq) { - free_irq(php_ctlr->irq, ctrl); - php_ctlr->irq = 0; - if (!pcie_mch_quirk) - pci_disable_msi(php_ctlr->pci_dev); - } - } - if (php_ctlr->pci_dev) - php_ctlr->pci_dev = NULL; - - spin_lock(&list_lock); - p = php_ctlr_list_head; - p_prev = NULL; - while (p) { - if (p == php_ctlr) { - if (p_prev) - p_prev->pnext = p->pnext; - else - php_ctlr_list_head = p->pnext; - break; - } else { - p_prev = p; - p = p->pnext; - } - } - spin_unlock(&list_lock); - - kfree(php_ctlr); + if (!PWR_LED(ctrl)) + return; - DBG_LEAVE_ROUTINE - + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PIC); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_OFF); } -static int hpc_power_on_slot(struct slot * slot) +void pciehp_green_led_blink(struct slot *slot) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_cmd; - u16 slot_ctrl, slot_status; - - int retval = 0; + struct controller *ctrl = slot->ctrl; - DBG_ENTER_ROUTINE + if (!PWR_LED(ctrl)) + return; - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PIC); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_BLINK); +} - dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return -1; - } +int pciehp_power_on_slot(struct slot *slot) +{ + struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + int retval; /* Clear sticky power-fault bit from previous power failures */ - hp_register_read_word(php_ctlr->pci_dev, - SLOT_STATUS(slot->ctrl->cap_base), slot_status); - slot_status &= PWR_FAULT_DETECTED; - if (slot_status) - hp_register_write_word(php_ctlr->pci_dev, - SLOT_STATUS(slot->ctrl->cap_base), slot_status); - - retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); - - if (retval) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_PFD) + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PFD); + ctrl->power_fault_detected = 0; - slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_ON; - - /* Enable detection that we turned off at slot power-off time */ - if (!pciehp_poll_mode) - slot_cmd = slot_cmd | - PWR_FAULT_DETECT_ENABLE | - MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE | - HP_INTR_ENABLE; + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_ON, PCI_EXP_SLTCTL_PCC); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_ON); - retval = pcie_write_cmd(slot, slot_cmd); - - if (retval) { - err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd); - return -1; - } - dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL(slot->ctrl->cap_base), slot_cmd); - - DBG_LEAVE_ROUTINE + retval = pciehp_link_enable(ctrl); + if (retval) + ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__); return retval; } -static int hpc_power_off_slot(struct slot * slot) +void pciehp_power_off_slot(struct slot *slot) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - u16 slot_cmd; - u16 slot_ctrl; - - int retval = 0; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } + struct controller *ctrl = slot->ctrl; - dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); - slot->hp_slot = 0; - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return -1; - } - retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(slot->ctrl->cap_base), slot_ctrl); - - if (retval) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return retval; - } + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_OFF); +} - slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_OFF; +static irqreturn_t pcie_isr(int irq, void *dev_id) +{ + struct controller *ctrl = (struct controller *)dev_id; + struct pci_dev *pdev = ctrl_dev(ctrl); + struct slot *slot = ctrl->slot; + u16 detected, intr_loc; /* - * 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. + * 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. */ - if (!pciehp_poll_mode) - slot_cmd = (slot_cmd & - ~PWR_FAULT_DETECT_ENABLE & - ~MRL_DETECT_ENABLE & - ~PRSN_DETECT_ENABLE) | HP_INTR_ENABLE; - - retval = pcie_write_cmd(slot, slot_cmd); - - if (retval) { - err("%s: Write command failed!\n", __FUNCTION__); - return -1; - } - dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL(slot->ctrl->cap_base), slot_cmd); - - DBG_LEAVE_ROUTINE - - return retval; -} - -static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs) -{ - struct controller *ctrl = NULL; - struct php_ctlr_state_s *php_ctlr; - u8 schedule_flag = 0; - u16 slot_status, intr_detect, intr_loc; - u16 temp_word; - int hp_slot = 0; /* only 1 slot per PCI Express port */ - int rc = 0; - - if (!dev_id) - return IRQ_NONE; - - if (!pciehp_poll_mode) { - ctrl = dev_id; - php_ctlr = ctrl->hpc_ctlr_handle; - } else { - php_ctlr = dev_id; - ctrl = (struct controller *)php_ctlr->callback_instance_id; - } + intr_loc = 0; + do { + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected); + + detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | + PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC); + detected &= ~intr_loc; + intr_loc |= detected; + if (!intr_loc) + return IRQ_NONE; + if (detected) + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + intr_loc); + } while (detected); - if (!ctrl) { - dbg("%s: dev_id %p ctlr == NULL\n", __FUNCTION__, (void*) dev_id); - return IRQ_NONE; - } - - if (!php_ctlr) { - dbg("%s: php_ctlr == NULL\n", __FUNCTION__); - return IRQ_NONE; - } + ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc); - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), slot_status); - if (rc) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - return IRQ_NONE; + /* Check Command Complete Interrupt Pending */ + if (intr_loc & PCI_EXP_SLTSTA_CC) { + ctrl->cmd_busy = 0; + smp_mb(); + wake_up(&ctrl->queue); } - intr_detect = ( ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | MRL_SENS_CHANGED | - PRSN_DETECT_CHANGED | CMD_COMPLETED ); + if (!(intr_loc & ~PCI_EXP_SLTSTA_CC)) + return IRQ_HANDLED; - intr_loc = slot_status & intr_detect; + /* Check MRL Sensor Changed */ + if (intr_loc & PCI_EXP_SLTSTA_MRLSC) + pciehp_handle_switch_change(slot); - /* Check to see if it was our interrupt */ - if ( !intr_loc ) - return IRQ_NONE; + /* Check Attention Button Pressed */ + if (intr_loc & PCI_EXP_SLTSTA_ABP) + pciehp_handle_attention_button(slot); - dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc); - /* Mask Hot-plug Interrupt Enable */ - if (!pciehp_poll_mode) { - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return IRQ_NONE; - } + /* Check Presence Detect Changed */ + if (intr_loc & PCI_EXP_SLTSTA_PDC) + pciehp_handle_presence_change(slot); - dbg("%s: hp_register_read_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); - temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 0x00; - - rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); - return IRQ_NONE; - } - - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), slot_status); - if (rc) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - return IRQ_NONE; - } - dbg("%s: hp_register_read_word SLOT_STATUS with value %x\n", __FUNCTION__, slot_status); - - /* Clear command complete interrupt caused by this write */ - temp_word = 0x1f; - rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); - return IRQ_NONE; - } - } - - if (intr_loc & CMD_COMPLETED) { - /* - * Command Complete Interrupt Pending - */ - wake_up_interruptible(&ctrl->queue); + /* Check Power Fault Detected */ + if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { + ctrl->power_fault_detected = 1; + pciehp_handle_power_fault(slot); } - if ((php_ctlr->switch_change_callback) && (intr_loc & MRL_SENS_CHANGED)) - schedule_flag += php_ctlr->switch_change_callback( - hp_slot, php_ctlr->callback_instance_id); - if ((php_ctlr->attention_button_callback) && (intr_loc & ATTN_BUTTN_PRESSED)) - schedule_flag += php_ctlr->attention_button_callback( - hp_slot, php_ctlr->callback_instance_id); - if ((php_ctlr->presence_change_callback) && (intr_loc & PRSN_DETECT_CHANGED)) - schedule_flag += php_ctlr->presence_change_callback( - hp_slot , php_ctlr->callback_instance_id); - if ((php_ctlr->power_fault_callback) && (intr_loc & PWR_FAULT_DETECTED)) - schedule_flag += php_ctlr->power_fault_callback( - hp_slot, php_ctlr->callback_instance_id); - - /* Clear all events after serving them */ - temp_word = 0x1F; - rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); - return IRQ_NONE; - } - /* Unmask Hot-plug Interrupt Enable */ - if (!pciehp_poll_mode) { - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - return IRQ_NONE; - } - - dbg("%s: Unmask Hot-plug Interrupt Enable\n", __FUNCTION__); - temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; + if (intr_loc & PCI_EXP_SLTSTA_DLLSC) + pciehp_handle_linkstate_change(slot); - rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); - return IRQ_NONE; - } - - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), slot_status); - if (rc) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - return IRQ_NONE; - } - - /* Clear command complete interrupt caused by this write */ - temp_word = 0x1F; - rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); - return IRQ_NONE; - } - dbg("%s: hp_register_write_word SLOT_STATUS with value %x\n", __FUNCTION__, temp_word); - } - return IRQ_HANDLED; } -static int hpc_get_max_lnk_speed (struct slot *slot, enum pci_bus_speed *value) +void pcie_enable_notification(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - enum pcie_link_speed lnk_speed; - u32 lnk_cap; - int retval = 0; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } - - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return -1; - } + u16 cmd, mask; - retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP(slot->ctrl->cap_base), lnk_cap); + /* + * TBD: Power fault detected software notification support. + * + * Power fault detected software notification is not enabled + * now, because it caused power fault detected interrupt storm + * on some machines. On those machines, power fault detected + * bit in the slot status register was set again immediately + * when it is cleared in the interrupt service routine, and + * next power fault detected interrupt was notified again. + */ - if (retval) { - err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__); - return retval; - } + /* + * Always enable link events: thus link-up and link-down shall + * always be treated as hotplug and unplug respectively. Enable + * presence detect only if Attention Button is not present. + */ + cmd = PCI_EXP_SLTCTL_DLLSCE; + if (ATTN_BUTTN(ctrl)) + cmd |= PCI_EXP_SLTCTL_ABPE; + else + cmd |= PCI_EXP_SLTCTL_PDCE; + if (MRL_SENS(ctrl)) + cmd |= PCI_EXP_SLTCTL_MRLSCE; + if (!pciehp_poll_mode) + cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE; - switch (lnk_cap & 0x000F) { - case 1: - lnk_speed = PCIE_2PT5GB; - break; - default: - lnk_speed = PCIE_LNK_SPEED_UNKNOWN; - break; - } + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_DLLSCE); - *value = lnk_speed; - dbg("Max link speed = %d\n", lnk_speed); - DBG_LEAVE_ROUTINE - return retval; + pcie_write_cmd(ctrl, cmd, mask); } -static int hpc_get_max_lnk_width (struct slot *slot, enum pcie_link_width *value) +static void pcie_disable_notification(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - enum pcie_link_width lnk_wdth; - u32 lnk_cap; - int retval = 0; + u16 mask; - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; - } - - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return -1; - } - - retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP(slot->ctrl->cap_base), lnk_cap); - - if (retval) { - err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__); - 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); - DBG_LEAVE_ROUTINE - return retval; + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_DLLSCE); + pcie_write_cmd(ctrl, 0, mask); } -static int hpc_get_cur_lnk_speed (struct slot *slot, enum pci_bus_speed *value) +/* + * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary + * bus reset of the bridge, but at the same time we want to ensure that it is + * not seen as a hot-unplug, followed by the hot-plug of the device. Thus, + * disable link state notification and presence detection change notification + * momentarily, if we see that they could interfere. Also, clear any spurious + * events after. + */ +int pciehp_reset_slot(struct slot *slot, int probe) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN; - int retval = 0; - u16 lnk_status; + struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; - DBG_ENTER_ROUTINE + if (probe) + return 0; - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); - return -1; + if (!ATTN_BUTTN(ctrl)) { + ctrl_mask |= PCI_EXP_SLTCTL_PDCE; + stat_mask |= PCI_EXP_SLTSTA_PDC; } + ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; + stat_mask |= PCI_EXP_SLTSTA_DLLSC; - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return -1; - } + pcie_write_cmd(ctrl, 0, ctrl_mask); + if (pciehp_poll_mode) + del_timer_sync(&ctrl->poll_timer); - retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS(slot->ctrl->cap_base), lnk_status); + pci_reset_bridge_secondary_bus(ctrl->pcie->port); - if (retval) { - err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); - return retval; - } + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask); + pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask); + if (pciehp_poll_mode) + int_poll_timeout(ctrl->poll_timer.data); - 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); - DBG_LEAVE_ROUTINE - return retval; + return 0; } -static int hpc_get_cur_lnk_width (struct slot *slot, enum pcie_link_width *value) +int pcie_init_notification(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle; - enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; - int retval = 0; - u16 lnk_status; - - DBG_ENTER_ROUTINE - - if (!php_ctlr) { - err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + if (pciehp_request_irq(ctrl)) return -1; - } - - if (slot->hp_slot >= php_ctlr->num_slots) { - err("%s: Invalid HPC slot number!\n", __FUNCTION__); - return -1; - } - - retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS(slot->ctrl->cap_base), lnk_status); - - if (retval) { - err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); - return retval; - } - - 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); - DBG_LEAVE_ROUTINE - return retval; + pcie_enable_notification(ctrl); + ctrl->notification_enabled = 1; + return 0; } -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, - - .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, - - .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, - - .release_ctlr = hpc_release_ctlr, - .check_lnk_status = hpc_check_lnk_status, -}; - -int pcie_init(struct controller * ctrl, struct pcie_device *dev) +static void pcie_shutdown_notification(struct controller *ctrl) { - struct php_ctlr_state_s *php_ctlr, *p; - void *instance_id = ctrl; - int rc; - static int first = 1; - u16 temp_word; - u16 cap_reg; - u16 intr_enable = 0; - u32 slot_cap; - int cap_base, saved_cap_base; - u16 slot_status, slot_ctrl; - struct pci_dev *pdev; - - DBG_ENTER_ROUTINE - - spin_lock_init(&list_lock); - php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL); - - if (!php_ctlr) { /* allocate controller state data */ - err("%s: HPC controller memory allocation error!\n", __FUNCTION__); - goto abort; + if (ctrl->notification_enabled) { + pcie_disable_notification(ctrl); + pciehp_free_irq(ctrl); + ctrl->notification_enabled = 0; } +} - memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s)); - - pdev = dev->port; - php_ctlr->pci_dev = pdev; /* save pci_dev in context */ - - dbg("%s: hotplug controller vendor id 0x%x device id 0x%x\n", - __FUNCTION__, pdev->vendor, pdev->device); +static int pcie_init_slot(struct controller *ctrl) +{ + struct slot *slot; - saved_cap_base = pcie_cap_base; + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) + return -ENOMEM; - if ((cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP)) == 0) { - dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__); - goto abort_free_ctlr; - } + slot->wq = alloc_workqueue("pciehp-%u", 0, 0, PSN(ctrl)); + if (!slot->wq) + goto abort; - ctrl->cap_base = cap_base; + slot->ctrl = ctrl; + mutex_init(&slot->lock); + mutex_init(&slot->hotplug_lock); + INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); + ctrl->slot = slot; + return 0; +abort: + kfree(slot); + return -ENOMEM; +} - dbg("%s: pcie_cap_base %x\n", __FUNCTION__, pcie_cap_base); +static void pcie_cleanup_slot(struct controller *ctrl) +{ + struct slot *slot = ctrl->slot; + cancel_delayed_work(&slot->work); + destroy_workqueue(slot->wq); + kfree(slot); +} - rc = hp_register_read_word(pdev, CAP_REG(ctrl->cap_base), cap_reg); - if (rc) { - err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - dbg("%s: CAP_REG offset %x cap_reg %x\n", __FUNCTION__, CAP_REG(ctrl->cap_base), cap_reg); +static inline void dbg_ctrl(struct controller *ctrl) +{ + int i; + u16 reg16; + struct pci_dev *pdev = ctrl->pcie->port; - if (((cap_reg & SLOT_IMPL) == 0) || (((cap_reg & DEV_PORT_TYPE) != 0x0040) - && ((cap_reg & DEV_PORT_TYPE) != 0x0060))) { - dbg("%s : This is not a root port or the port is not connected to a slot\n", __FUNCTION__); - goto abort_free_ctlr; - } + if (!pciehp_debug) + return; - rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP(ctrl->cap_base), slot_cap); - if (rc) { - err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - dbg("%s: SLOT_CAP offset %x slot_cap %x\n", __FUNCTION__, SLOT_CAP(ctrl->cap_base), slot_cap); + ctrl_info(ctrl, "Hotplug Controller:\n"); + ctrl_info(ctrl, " Seg/Bus/Dev/Func/IRQ : %s IRQ %d\n", + pci_name(pdev), pdev->irq); + ctrl_info(ctrl, " Vendor ID : 0x%04x\n", pdev->vendor); + ctrl_info(ctrl, " Device ID : 0x%04x\n", pdev->device); + ctrl_info(ctrl, " Subsystem ID : 0x%04x\n", + pdev->subsystem_device); + ctrl_info(ctrl, " Subsystem Vendor ID : 0x%04x\n", + pdev->subsystem_vendor); + ctrl_info(ctrl, " PCIe Cap offset : 0x%02x\n", + pci_pcie_cap(pdev)); + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + if (!pci_resource_len(pdev, i)) + continue; + ctrl_info(ctrl, " PCI resource [%d] : %pR\n", + i, &pdev->resource[i]); + } + ctrl_info(ctrl, "Slot Capabilities : 0x%08x\n", ctrl->slot_cap); + ctrl_info(ctrl, " Physical Slot Number : %d\n", PSN(ctrl)); + ctrl_info(ctrl, " Attention Button : %3s\n", + ATTN_BUTTN(ctrl) ? "yes" : "no"); + ctrl_info(ctrl, " Power Controller : %3s\n", + POWER_CTRL(ctrl) ? "yes" : "no"); + ctrl_info(ctrl, " MRL Sensor : %3s\n", + MRL_SENS(ctrl) ? "yes" : "no"); + ctrl_info(ctrl, " Attention Indicator : %3s\n", + ATTN_LED(ctrl) ? "yes" : "no"); + ctrl_info(ctrl, " Power Indicator : %3s\n", + PWR_LED(ctrl) ? "yes" : "no"); + ctrl_info(ctrl, " Hot-Plug Surprise : %3s\n", + HP_SUPR_RM(ctrl) ? "yes" : "no"); + ctrl_info(ctrl, " EMI Present : %3s\n", + EMI(ctrl) ? "yes" : "no"); + ctrl_info(ctrl, " Command Completed : %3s\n", + NO_CMD_CMPL(ctrl) ? "no" : "yes"); + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, ®16); + ctrl_info(ctrl, "Slot Status : 0x%04x\n", reg16); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, ®16); + ctrl_info(ctrl, "Slot Control : 0x%04x\n", reg16); +} - if (!(slot_cap & HP_CAP)) { - dbg("%s : This slot is not hot-plug capable\n", __FUNCTION__); - goto abort_free_ctlr; - } - /* For debugging purpose */ - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), slot_status); - if (rc) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - dbg("%s: SLOT_STATUS offset %x slot_status %x\n", __FUNCTION__, SLOT_STATUS(ctrl->cap_base), slot_status); +#define FLAG(x, y) (((x) & (y)) ? '+' : '-') - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL(ctrl->cap_base), slot_ctrl); - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - dbg("%s: SLOT_CTRL offset %x slot_ctrl %x\n", __FUNCTION__, SLOT_CTRL(ctrl->cap_base), slot_ctrl); +struct controller *pcie_init(struct pcie_device *dev) +{ + struct controller *ctrl; + u32 slot_cap, link_cap; + struct pci_dev *pdev = dev->port; - if (first) { - spin_lock_init(&hpc_event_lock); - first = 0; + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) { + dev_err(&dev->device, "%s: Out of memory\n", __func__); + goto abort; } - - for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++) - if (pci_resource_len(pdev, rc) > 0) - dbg("pci resource[%d] start=0x%lx(len=0x%lx)\n", rc, - pci_resource_start(pdev, rc), pci_resource_len(pdev, rc)); - - 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); - - if (pci_enable_device(pdev)) - goto abort_free_ctlr; - - init_MUTEX(&ctrl->crit_sect); - /* setup wait queue */ + ctrl->pcie = dev; + pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); + ctrl->slot_cap = slot_cap; + mutex_init(&ctrl->ctrl_lock); init_waitqueue_head(&ctrl->queue); - - /* find the IRQ */ - php_ctlr->irq = dev->irq; - - /* Save interrupt callback info */ - php_ctlr->attention_button_callback = pciehp_handle_attention_button; - php_ctlr->switch_change_callback = pciehp_handle_switch_change; - php_ctlr->presence_change_callback = pciehp_handle_presence_change; - php_ctlr->power_fault_callback = pciehp_handle_power_fault; - php_ctlr->callback_instance_id = instance_id; - - /* return PCI Controller Info */ - php_ctlr->slot_device_offset = 0; - php_ctlr->num_slots = 1; - - /* Mask Hot-plug Interrupt Enable */ - rc = hp_register_read_word(pdev, SLOT_CTRL(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - - dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL(ctrl->cap_base), temp_word); - temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 0x00; - - rc = hp_register_write_word(pdev, SLOT_CTRL(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), slot_status); - if (rc) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - - temp_word = 0x1F; /* Clear all events */ - rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - - if (pciehp_poll_mode) {/* Install interrupt polling code */ - /* Install and start the interrupt polling timer */ - init_timer(&php_ctlr->int_poll_timer); - start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */ - } else { - /* Installs the interrupt handler */ - rc = request_irq(php_ctlr->irq, pcie_isr, SA_SHIRQ, MY_NAME, (void *) ctrl); - dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc); - if (rc) { - err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq); - goto abort_free_ctlr; - } - } - - dbg("pciehp ctrl b:d:f:irq=0x%x:%x:%x:%x\n", pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq); - - rc = hp_register_read_word(pdev, SLOT_CTRL(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - - intr_enable = intr_enable | PRSN_DETECT_ENABLE; - - if (ATTN_BUTTN(slot_cap)) - intr_enable = intr_enable | ATTN_BUTTN_ENABLE; - - if (POWER_CTRL(slot_cap)) - intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE; - - if (MRL_SENS(slot_cap)) - intr_enable = intr_enable | MRL_DETECT_ENABLE; - - temp_word = (temp_word & ~intr_enable) | intr_enable; - - if (pciehp_poll_mode) { - temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0; - } else { - temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; - } - - /* Unmask Hot-plug Interrupt Enable for the interrupt notification mechanism case */ - rc = hp_register_write_word(pdev, SLOT_CTRL(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), slot_status); - if (rc) { - err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - - temp_word = 0x1F; /* Clear all events */ - rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), temp_word); - if (rc) { - err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); - goto abort_free_ctlr; - } - - if (pciehp_force) { - dbg("Bypassing BIOS check for pciehp use on %s\n", - pci_name(ctrl->pci_dev)); - } else { - rc = pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev); - if (rc) - goto abort_free_ctlr; - } - - /* Add this HPC instance into the HPC list */ - spin_lock(&list_lock); - if (php_ctlr_list_head == 0) { - php_ctlr_list_head = php_ctlr; - p = php_ctlr_list_head; - p->pnext = NULL; - } else { - p = php_ctlr_list_head; - - while (p->pnext) - p = p->pnext; - - p->pnext = php_ctlr; - } - spin_unlock(&list_lock); - - ctlr_seq_num++; - ctrl->hpc_ctlr_handle = php_ctlr; - ctrl->hpc_ops = &pciehp_hpc_ops; - - DBG_LEAVE_ROUTINE - return 0; - - /* We end up here for the many possible ways to fail this API. */ -abort_free_ctlr: - pcie_cap_base = saved_cap_base; - kfree(php_ctlr); + dbg_ctrl(ctrl); + /* + * Controller doesn't notify of command completion if the "No + * Command Completed Support" bit is set in Slot Capability + * register or the controller supports none of power + * controller, attention led, power led and EMI. + */ + if (NO_CMD_CMPL(ctrl) || + !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl))) + ctrl->no_cmd_complete = 1; + + /* Check if Data Link Layer Link Active Reporting is implemented */ + pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); + if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { + ctrl_dbg(ctrl, "Link Active Reporting supported\n"); + ctrl->link_active_reporting = 1; + } + + /* Clear all remaining event bits in Slot Status register */ + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | + PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_CC); + + /* Disable software notification */ + pcie_disable_notification(ctrl); + + ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n", + (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, + FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), + FLAG(slot_cap, PCI_EXP_SLTCAP_AIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_PIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), + FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP), + FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), + FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC)); + + if (pcie_init_slot(ctrl)) + goto abort_ctrl; + + return ctrl; + +abort_ctrl: + kfree(ctrl); abort: - DBG_LEAVE_ROUTINE - return -1; + return NULL; +} + +void pciehp_release_ctrl(struct controller *ctrl) +{ + pcie_shutdown_notification(ctrl); + pcie_cleanup_slot(ctrl); + kfree(ctrl); } |
