Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[cascardo/linux.git] / drivers / pci / hotplug / pciehp_hpc.c
index 98eee63..9aac6a8 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/timer.h>
 #include <linux/pci.h>
 #include <linux/interrupt.h>
+#include <linux/time.h>
 
 #include "../pci.h"
 #include "pciehp.h"
@@ -70,6 +71,8 @@
 #define DBG_LEAVE_ROUTINE
 #endif                         /* DEBUG */
 
+static atomic_t pciehp_num_controllers = ATOMIC_INIT(0);
+
 struct ctrl_reg {
        u8 cap_id;
        u8 nxt_ptr;
@@ -105,7 +108,6 @@ enum ctrl_offsets {
        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 */ 
 
 static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value)
 {
@@ -193,6 +195,7 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value)
 #define ATTN_LED_CTRL                  0x00C0
 #define PWR_LED_CTRL                   0x0300
 #define PWR_CTRL                       0x0400
+#define EMI_CTRL                       0x0800
 
 /* Attention indicator and Power indicator states */
 #define LED_ON         0x01
@@ -203,6 +206,10 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value)
 #define POWER_ON       0
 #define POWER_OFF      0x0400
 
+/* EMI Status defines */
+#define EMI_DISENGAGED 0
+#define EMI_ENGAGED    1
+
 /* Field definitions in Slot Status Register */
 #define ATTN_BUTTN_PRESSED     0x0001
 #define PWR_FAULT_DETECTED     0x0002
@@ -211,11 +218,10 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value)
 #define CMD_COMPLETED          0x0010
 #define MRL_STATE              0x0020
 #define PRSN_STATE             0x0040
-
-static spinlock_t hpc_event_lock;
+#define EMI_STATE              0x0080
+#define EMI_STATUS_BIT         7
 
 DEFINE_DBG_BUFFER              /* Debug string buffer for entire HPC defined here */
-static int ctlr_seq_num = 0;   /* Controller sequence # */
 
 static irqreturn_t pcie_isr(int irq, void *dev_id);
 static void start_int_poll_timer(struct controller *ctrl, int sec);
@@ -250,6 +256,25 @@ static void start_int_poll_timer(struct controller *ctrl, int sec)
        add_timer(&ctrl->poll_timer);
 }
 
+static inline int pcie_wait_cmd(struct controller *ctrl)
+{
+       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");
+       }
+
+       return retval;
+}
+
 static int pcie_write_cmd(struct slot *slot, u16 cmd)
 {
        struct controller *ctrl = slot->ctrl;
@@ -258,24 +283,35 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd)
 
        DBG_ENTER_ROUTINE 
 
+       mutex_lock(&ctrl->ctrl_lock);
+
        retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
        if (retval) {
                err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__);
-               return retval;
+               goto out;
        }
 
        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__);
+               /* 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__);
        }
 
+       ctrl->cmd_busy = 1;
        retval = pciehp_writew(ctrl, SLOTCTRL, (cmd | CMD_CMPL_INTR_ENABLE));
        if (retval) {
                err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
-               return retval;
+               goto out;
        }
 
+       /*
+        * Wait for command completion.
+        */
+       retval = pcie_wait_cmd(ctrl);
+ out:
+       mutex_unlock(&ctrl->ctrl_lock);
        DBG_LEAVE_ROUTINE 
        return retval;
 }
@@ -445,6 +481,51 @@ static int hpc_query_power_fault(struct slot *slot)
        return pwr_fault;
 }
 
+static int hpc_get_emi_status(struct slot *slot, u8 *status)
+{
+       struct controller *ctrl = slot->ctrl;
+       u16 slot_status;
+       int retval = 0;
+
+       DBG_ENTER_ROUTINE
+
+       retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
+       if (retval) {
+               err("%s : Cannot check EMI status\n", __FUNCTION__);
+               return retval;
+       }
+       *status = (slot_status & EMI_STATE) >> EMI_STATUS_BIT;
+
+       DBG_LEAVE_ROUTINE
+       return retval;
+}
+
+static int hpc_toggle_emi(struct slot *slot)
+{
+       struct controller *ctrl = slot->ctrl;
+       u16 slot_cmd = 0;
+       u16 slot_ctrl;
+       int rc = 0;
+
+       DBG_ENTER_ROUTINE
+
+       rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
+       if (rc) {
+               err("%s : hp_register_read_word SLOT_CTRL failed\n",
+                       __FUNCTION__);
+               return rc;
+       }
+
+       slot_cmd = (slot_ctrl | EMI_CTRL);
+       if (!pciehp_poll_mode)
+               slot_cmd = slot_cmd | HP_INTR_ENABLE;
+
+       pcie_write_cmd(slot, slot_cmd);
+       slot->last_emi_toggle = get_seconds();
+       DBG_LEAVE_ROUTINE
+       return rc;
+}
+
 static int hpc_set_attention_status(struct slot *slot, u8 value)
 {
        struct controller *ctrl = slot->ctrl;
@@ -574,6 +655,13 @@ static void hpc_release_ctlr(struct controller *ctrl)
        else
                free_irq(ctrl->pci_dev->irq, ctrl);
 
+       /*
+        * 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);
+
        DBG_LEAVE_ROUTINE
 }
 
@@ -746,6 +834,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
                /* 
                 * Command Complete Interrupt Pending 
                 */
+               ctrl->cmd_busy = 0;
                wake_up_interruptible(&ctrl->queue);
        }
 
@@ -979,6 +1068,8 @@ static struct hpc_ops pciehp_hpc_ops = {
        .get_attention_status           = hpc_get_attention_status,
        .get_latch_status               = hpc_get_latch_status,
        .get_adapter_status             = hpc_get_adapter_status,
+       .get_emi_status                 = hpc_get_emi_status,
+       .toggle_emi                     = hpc_toggle_emi,
 
        .get_max_bus_speed              = hpc_get_max_lnk_speed,
        .get_cur_bus_speed              = hpc_get_cur_lnk_speed,
@@ -1067,12 +1158,11 @@ int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
 int pcie_init(struct controller * ctrl, struct pcie_device *dev)
 {
        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;
+       int cap_base;
        u16 slot_status, slot_ctrl;
        struct pci_dev *pdev;
 
@@ -1084,8 +1174,6 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
        dbg("%s: hotplug controller vendor id 0x%x device id 0x%x\n",
                        __FUNCTION__, pdev->vendor, pdev->device);
 
-       saved_cap_base = pcie_cap_base;
-
        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;
@@ -1093,7 +1181,7 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
 
        ctrl->cap_base = cap_base;
 
-       dbg("%s: pcie_cap_base %x\n", __FUNCTION__, pcie_cap_base);
+       dbg("%s: pcie_cap_base %x\n", __FUNCTION__, cap_base);
 
        rc = pciehp_readw(ctrl, CAPREG, &cap_reg);
        if (rc) {
@@ -1138,11 +1226,6 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
        dbg("%s: SLOTCTRL offset %x slot_ctrl %x\n",
            __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl);
 
-       if (first) {
-               spin_lock_init(&hpc_event_lock);
-               first = 0;
-       }
-
        for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++)
                if (pci_resource_len(pdev, rc) > 0)
                        dbg("pci resource[%d] start=0x%llx(len=0x%llx)\n", rc,
@@ -1203,7 +1286,8 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
                rc = request_irq(ctrl->pci_dev->irq, pcie_isr, IRQF_SHARED,
                                 MY_NAME, (void *)ctrl);
                dbg("%s: request_irq %d for hpc%d (returns %d)\n",
-                   __FUNCTION__, ctrl->pci_dev->irq, ctlr_seq_num, rc);
+                   __FUNCTION__, ctrl->pci_dev->irq,
+                   atomic_read(&pciehp_num_controllers), rc);
                if (rc) {
                        err("Can't get irq %d for the hotplug controller\n",
                            ctrl->pci_dev->irq);
@@ -1213,6 +1297,18 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
        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);
 
+       /*
+        * 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) {
+                       rc = -ENOMEM;
+                       goto abort_free_irq;
+               }
+       }
+
        rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
        if (rc) {
                err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
@@ -1266,7 +1362,6 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
                        goto abort_disable_intr;
        }
 
-       ctlr_seq_num++;
        ctrl->hpc_ops = &pciehp_hpc_ops;
 
        DBG_LEAVE_ROUTINE
@@ -1289,8 +1384,6 @@ abort_free_irq:
                free_irq(ctrl->pci_dev->irq, ctrl);
 
 abort_free_ctlr:
-       pcie_cap_base = saved_cap_base;
-
        DBG_LEAVE_ROUTINE
        return -1;
 }