linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / pci / hotplug / pciehp_hpc.c
index 1cda30b..77e5303 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * Send feedback to <greg@kroah.com>,<dely.l.sy@intel.com>
+ * Send feedback to <greg@kroah.com>,<kristen.c.accardi@intel.com>
  *
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
+#include <linux/signal.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
 #include <linux/pci.h>
-#include <asm/system.h>
+#include <linux/interrupt.h>
+
 #include "../pci.h"
 #include "pciehp.h"
 
@@ -217,23 +216,6 @@ static int pcie_cap_base = 0;              /* Base of the PCI Express capability item struct
 #define MRL_STATE              0x0020
 #define PRSN_STATE             0x0040
 
-struct php_ctlr_state_s {
-       struct php_ctlr_state_s *pnext;
-       struct pci_dev *pci_dev;
-       unsigned int irq;
-       unsigned long flags;                            /* spinlock's */
-       u32 slot_device_offset;
-       u32 num_slots;
-       struct timer_list       int_poll_timer;         /* Added for poll event */
-       php_intr_callback_t     attention_button_callback;
-       php_intr_callback_t     switch_change_callback;
-       php_intr_callback_t     presence_change_callback;
-       php_intr_callback_t     power_fault_callback;
-       void                    *callback_instance_id;
-       struct ctrl_reg         *creg;                          /* Ptr to controller register space */
-};
-
-
 static spinlock_t hpc_event_lock;
 
 DEFINE_DBG_BUFFER              /* Debug string buffer for entire HPC defined here */
@@ -297,7 +279,6 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd)
 
        DBG_ENTER_ROUTINE 
        
-       dbg("%s : Enter\n", __FUNCTION__);
        if (!php_ctlr) {
                err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
                return -1;
@@ -308,7 +289,6 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd)
                        err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
                        return retval;
                }
-       dbg("%s : hp_register_read_word SLOT_STATUS %x\n", __FUNCTION__, slot_status);
        
        if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) { 
                /* After 1 sec and CMD_COMPLETED still not set, just proceed forward to issue 
@@ -316,14 +296,11 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd)
                dbg("%s : CMD_COMPLETED not clear after 1 sec.\n", __FUNCTION__);
        }
 
-       dbg("%s: Before hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd);
        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;
        }
-       dbg("%s : hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd | CMD_CMPL_INTR_ENABLE);
-       dbg("%s : Exit\n", __FUNCTION__);
 
        DBG_LEAVE_ROUTINE 
        return retval;
@@ -509,7 +486,6 @@ static int hpc_query_power_fault(struct slot * slot)
        u16 slot_status;
        u8 pwr_fault;
        int retval = 0;
-       u8 status;
 
        DBG_ENTER_ROUTINE 
 
@@ -521,15 +497,13 @@ static int hpc_query_power_fault(struct slot * slot)
        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__);
+               err("%s : Cannot check for power fault\n", __FUNCTION__);
                return retval;
        }
        pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1);
-       status = (pwr_fault != 1) ? 1 : 0;
        
        DBG_LEAVE_ROUTINE
-       /* Note: Logic 0 => fault */
-       return status;
+       return pwr_fault;
 }
 
 static int hpc_set_attention_status(struct slot *slot, u8 value)
@@ -539,7 +513,8 @@ static int hpc_set_attention_status(struct slot *slot, u8 value)
        u16 slot_ctrl;
        int rc = 0;
 
-       dbg("%s: \n", __FUNCTION__);
+       DBG_ENTER_ROUTINE
+
        if (!php_ctlr) {
                err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
                return -1;
@@ -555,7 +530,6 @@ static int hpc_set_attention_status(struct slot *slot, u8 value)
                err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
                return rc;
        }
-       dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
 
        switch (value) {
                case 0 :        /* turn off */
@@ -576,6 +550,7 @@ static int hpc_set_attention_status(struct slot *slot, u8 value)
        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;
 }
 
@@ -587,7 +562,8 @@ static void hpc_set_green_led_on(struct slot *slot)
        u16 slot_ctrl;
        int rc = 0;
                
-       dbg("%s: \n", __FUNCTION__);    
+       DBG_ENTER_ROUTINE
+
        if (!php_ctlr) {
                err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
                return ;
@@ -604,7 +580,6 @@ static void hpc_set_green_led_on(struct slot *slot)
                err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
                return;
        }
-       dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
        slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0100;
        if (!pciehp_poll_mode)
                slot_cmd = slot_cmd | HP_INTR_ENABLE; 
@@ -612,6 +587,7 @@ static void hpc_set_green_led_on(struct slot *slot)
        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;
 }
 
@@ -622,7 +598,8 @@ static void hpc_set_green_led_off(struct slot *slot)
        u16 slot_ctrl;
        int rc = 0;
 
-       dbg("%s: \n", __FUNCTION__);    
+       DBG_ENTER_ROUTINE
+
        if (!php_ctlr) {
                err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
                return ;
@@ -639,7 +616,6 @@ static void hpc_set_green_led_off(struct slot *slot)
                err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
                return;
        }
-       dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
 
        slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300;
 
@@ -648,6 +624,7 @@ static void hpc_set_green_led_off(struct slot *slot)
        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;
 }
 
@@ -658,7 +635,8 @@ static void hpc_set_green_led_blink(struct slot *slot)
        u16 slot_ctrl;
        int rc = 0; 
        
-       dbg("%s: \n", __FUNCTION__);    
+       DBG_ENTER_ROUTINE
+
        if (!php_ctlr) {
                err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
                return ;
@@ -675,7 +653,6 @@ static void hpc_set_green_led_blink(struct slot *slot)
                err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
                return;
        }
-       dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
 
        slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0200;
 
@@ -684,6 +661,7 @@ static void hpc_set_green_led_blink(struct slot *slot)
        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;
 }
 
@@ -775,12 +753,11 @@ static int hpc_power_on_slot(struct slot * slot)
 {
        struct php_ctlr_state_s *php_ctlr = slot->ctrl->hpc_ctlr_handle;
        u16 slot_cmd;
-       u16 slot_ctrl;
+       u16 slot_ctrl, slot_status;
 
        int retval = 0;
 
        DBG_ENTER_ROUTINE 
-       dbg("%s: \n", __FUNCTION__);    
 
        if (!php_ctlr) {
                err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
@@ -793,19 +770,30 @@ static int hpc_power_on_slot(struct slot * slot)
                return -1;
        }
 
+       /* 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;
        }
-       dbg("%s: SLOT_CTRL %x, value read %xn", __FUNCTION__, SLOT_CTRL(slot->ctrl->cap_base),
-               slot_ctrl);
 
        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 | HP_INTR_ENABLE; 
+               slot_cmd = slot_cmd |
+                          PWR_FAULT_DETECT_ENABLE |
+                          MRL_DETECT_ENABLE |
+                          PRSN_DETECT_ENABLE |
+                          HP_INTR_ENABLE;
 
        retval = pcie_write_cmd(slot, slot_cmd);
 
@@ -829,7 +817,6 @@ static int hpc_power_off_slot(struct slot * slot)
        int retval = 0;
 
        DBG_ENTER_ROUTINE 
-       dbg("%s: \n", __FUNCTION__);    
 
        if (!php_ctlr) {
                err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
@@ -848,13 +835,21 @@ static int hpc_power_off_slot(struct slot * slot)
                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);
 
        slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_OFF;
 
+       /*
+        * 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.
+        */
        if (!pciehp_poll_mode)
-               slot_cmd = slot_cmd | HP_INTR_ENABLE; 
+               slot_cmd = (slot_cmd &
+                           ~PWR_FAULT_DETECT_ENABLE &
+                           ~MRL_DETECT_ENABLE &
+                           ~PRSN_DETECT_ENABLE) | HP_INTR_ENABLE;
 
        retval = pcie_write_cmd(slot, slot_cmd);
 
@@ -924,7 +919,6 @@ static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs)
                        return IRQ_NONE;
                }
 
-               dbg("%s: Set Mask Hot-plug Interrupt Enable\n", __FUNCTION__);
                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;
 
@@ -933,7 +927,6 @@ static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs)
                        err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
                        return IRQ_NONE;
                }
-               dbg("%s: hp_register_write_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word);
                
                rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), slot_status);
                if (rc) {
@@ -949,14 +942,12 @@ static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs)
                        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);
        }
        
        if (intr_loc & CMD_COMPLETED) {
                /* 
                 * Command Complete Interrupt Pending 
                 */
-               dbg("%s: In Command Complete Interrupt Pending\n", __FUNCTION__);
                wake_up_interruptible(&ctrl->queue);
        }
 
@@ -989,7 +980,6 @@ static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs)
                }
 
                dbg("%s: Unmask Hot-plug Interrupt Enable\n", __FUNCTION__);
-               dbg("%s: hp_register_read_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word);
                temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
 
                rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL(ctrl->cap_base), temp_word);
@@ -997,14 +987,12 @@ static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs)
                        err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
                        return IRQ_NONE;
                }
-               dbg("%s: hp_register_write_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word);   
        
                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;
@@ -1248,12 +1236,7 @@ static struct hpc_ops pciehp_hpc_ops = {
        .check_lnk_status               = hpc_check_lnk_status,
 };
 
-int pcie_init(struct controller * ctrl,
-       struct pcie_device *dev,
-       php_intr_callback_t attention_button_callback,
-       php_intr_callback_t switch_change_callback,
-       php_intr_callback_t presence_change_callback,
-       php_intr_callback_t power_fault_callback)
+int pcie_init(struct controller * ctrl, struct pcie_device *dev)
 {
        struct php_ctlr_state_s *php_ctlr, *p;
        void *instance_id = ctrl;
@@ -1282,8 +1265,8 @@ int pcie_init(struct controller * ctrl,
        pdev = dev->port;
        php_ctlr->pci_dev = pdev;       /* save pci_dev in context */
 
-       dbg("%s: pdev->vendor %x pdev->device %x\n", __FUNCTION__,
-               pdev->vendor, pdev->device);
+       dbg("%s: hotplug controller vendor id 0x%x device id 0x%x\n",
+                       __FUNCTION__, pdev->vendor, pdev->device);
 
        saved_cap_base = pcie_cap_base;
 
@@ -1340,8 +1323,6 @@ int pcie_init(struct controller * ctrl,
                first = 0;
        }
 
-       dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number, 
-               PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq);
        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,
@@ -1359,13 +1340,12 @@ int pcie_init(struct controller * ctrl,
 
        /* find the IRQ */
        php_ctlr->irq = dev->irq;
-       dbg("HPC interrupt = %d\n", php_ctlr->irq);
 
        /* Save interrupt callback info */
-       php_ctlr->attention_button_callback = attention_button_callback;
-       php_ctlr->switch_change_callback = switch_change_callback;
-       php_ctlr->presence_change_callback = presence_change_callback;
-       php_ctlr->power_fault_callback = power_fault_callback;
+       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 */
@@ -1387,15 +1367,12 @@ int pcie_init(struct controller * ctrl,
                err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
                goto abort_free_ctlr;
        }
-       dbg("%s : Mask HPIE hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, temp_word);
 
        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: Mask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__, SLOT_STATUS(ctrl->cap_base)
-               , slot_status);
 
        temp_word = 0x1F; /* Clear all events */
        rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), temp_word);
@@ -1403,7 +1380,6 @@ int pcie_init(struct controller * ctrl,
                err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
                goto abort_free_ctlr;
        }
-       dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS(ctrl->cap_base), temp_word);
 
        if (pciehp_poll_mode)  {/* Install interrupt polling code */
                /* Install and start the interrupt polling timer */
@@ -1419,13 +1395,14 @@ int pcie_init(struct controller * ctrl,
                }
        }
 
+       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;
        }
-       dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL(ctrl->cap_base), temp_word);
-       dbg("%s: slot_cap %x\n", __FUNCTION__, slot_cap);
 
        intr_enable = intr_enable | PRSN_DETECT_ENABLE;
 
@@ -1445,7 +1422,6 @@ int pcie_init(struct controller * ctrl,
        } else {
                temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
        }
-       dbg("%s: temp_word %x\n", __FUNCTION__, temp_word);
 
        /* Unmask Hot-plug Interrupt Enable for the interrupt notification mechanism case */
        rc = hp_register_write_word(pdev, SLOT_CTRL(ctrl->cap_base), temp_word);
@@ -1453,14 +1429,11 @@ int pcie_init(struct controller * ctrl,
                err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
                goto abort_free_ctlr;
        }
-       dbg("%s : Unmask HPIE hp_register_write_word SLOT_CTRL with %x\n", __FUNCTION__, temp_word);
        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: Unmask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__, 
-               SLOT_STATUS(ctrl->cap_base), slot_status);
        
        temp_word =  0x1F; /* Clear all events */
        rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS(ctrl->cap_base), temp_word);
@@ -1468,8 +1441,16 @@ int pcie_init(struct controller * ctrl,
                err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
                goto abort_free_ctlr;
        }
-       dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS(ctrl->cap_base), temp_word);
        
+       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) {