]> err.no Git - linux-2.6/blobdiff - drivers/pci/hotplug/pciehp_hpc.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[linux-2.6] / drivers / pci / hotplug / pciehp_hpc.c
index fbc64aa2dd68a32f24d21fde630665e316150d65..016eea94a8a5729d1cf6e5908a2c6a205d4b6149 100644 (file)
@@ -71,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;
@@ -219,10 +221,7 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value)
 #define EMI_STATE              0x0080
 #define EMI_STATUS_BIT         7
 
-static spinlock_t hpc_event_lock;
-
 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);
@@ -276,11 +275,19 @@ static inline int pcie_wait_cmd(struct controller *ctrl)
        return retval;
 }
 
-static int pcie_write_cmd(struct slot *slot, u16 cmd)
+/**
+ * pcie_write_cmd - Issue controller command
+ * @slot: slot to which the command is issued
+ * @cmd:  command value written to slot control register
+ * @mask: bitmask of slot control register to be modified
+ */
+static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask)
 {
        struct controller *ctrl = slot->ctrl;
        int retval = 0;
        u16 slot_status;
+       u16 slot_ctrl;
+       unsigned long flags;
 
        DBG_ENTER_ROUTINE 
 
@@ -300,17 +307,29 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd)
                    __FUNCTION__);
        }
 
-       ctrl->cmd_busy = 1;
-       retval = pciehp_writew(ctrl, SLOTCTRL, (cmd | CMD_CMPL_INTR_ENABLE));
+       spin_lock_irqsave(&ctrl->lock, flags);
+       retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
        if (retval) {
-               err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
-               goto out;
+               err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
+               goto out_spin_unlock;
        }
 
+       slot_ctrl &= ~mask;
+       slot_ctrl |= ((cmd & mask) | CMD_CMPL_INTR_ENABLE);
+
+       ctrl->cmd_busy = 1;
+       retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl);
+       if (retval)
+               err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__);
+
+ out_spin_unlock:
+       spin_unlock_irqrestore(&ctrl->lock, flags);
+
        /*
         * Wait for command completion.
         */
-       retval = pcie_wait_cmd(ctrl);
+       if (!retval)
+               retval = pcie_wait_cmd(ctrl);
  out:
        mutex_unlock(&ctrl->ctrl_lock);
        DBG_LEAVE_ROUTINE 
@@ -503,25 +522,20 @@ static int hpc_get_emi_status(struct slot *slot, u8 *status)
 
 static int hpc_toggle_emi(struct slot *slot)
 {
-       struct controller *ctrl = slot->ctrl;
-       u16 slot_cmd = 0;
-       u16 slot_ctrl;
-       int rc = 0;
+       u16 slot_cmd;
+       u16 cmd_mask;
+       int rc;
 
        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 = EMI_CTRL;
+       cmd_mask = EMI_CTRL;
+       if (!pciehp_poll_mode) {
                slot_cmd = slot_cmd | HP_INTR_ENABLE;
+               cmd_mask = cmd_mask | HP_INTR_ENABLE;
+       }
 
-       pcie_write_cmd(slot, slot_cmd);
+       rc = pcie_write_cmd(slot, slot_cmd, cmd_mask);
        slot->last_emi_toggle = get_seconds();
        DBG_LEAVE_ROUTINE
        return rc;
@@ -530,35 +544,32 @@ static int hpc_toggle_emi(struct slot *slot)
 static int hpc_set_attention_status(struct slot *slot, u8 value)
 {
        struct controller *ctrl = slot->ctrl;
-       u16 slot_cmd = 0;
-       u16 slot_ctrl;
-       int rc = 0;
+       u16 slot_cmd;
+       u16 cmd_mask;
+       int rc;
 
        DBG_ENTER_ROUTINE
 
-       rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
-       if (rc) {
-               err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
-               return rc;
-       }
-
+       cmd_mask = ATTN_LED_CTRL;
        switch (value) {
                case 0 :        /* turn off */
-                       slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x00C0;
+                       slot_cmd = 0x00C0;
                        break;
                case 1:         /* turn on */
-                       slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0040;
+                       slot_cmd = 0x0040;
                        break;
                case 2:         /* turn blink */
-                       slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0080;
+                       slot_cmd = 0x0080;
                        break;
                default:
                        return -1;
        }
-       if (!pciehp_poll_mode)
-               slot_cmd = slot_cmd | HP_INTR_ENABLE; 
+       if (!pciehp_poll_mode) {
+               slot_cmd = slot_cmd | HP_INTR_ENABLE;
+               cmd_mask = cmd_mask | HP_INTR_ENABLE;
+       }
 
-       pcie_write_cmd(slot, slot_cmd);
+       rc = pcie_write_cmd(slot, slot_cmd, cmd_mask);
        dbg("%s: SLOTCTRL %x write cmd %x\n",
            __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
        
@@ -571,21 +582,18 @@ static void hpc_set_green_led_on(struct slot *slot)
 {
        struct controller *ctrl = slot->ctrl;
        u16 slot_cmd;
-       u16 slot_ctrl;
-       int rc = 0;
+       u16 cmd_mask;
                
        DBG_ENTER_ROUTINE
 
-       rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
-       if (rc) {
-               err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
-               return;
+       slot_cmd = 0x0100;
+       cmd_mask = PWR_LED_CTRL;
+       if (!pciehp_poll_mode) {
+               slot_cmd = slot_cmd | HP_INTR_ENABLE;
+               cmd_mask = cmd_mask | HP_INTR_ENABLE;
        }
-       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);
+       pcie_write_cmd(slot, slot_cmd, cmd_mask);
 
        dbg("%s: SLOTCTRL %x write cmd %x\n",
            __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
@@ -597,22 +605,18 @@ static void hpc_set_green_led_off(struct slot *slot)
 {
        struct controller *ctrl = slot->ctrl;
        u16 slot_cmd;
-       u16 slot_ctrl;
-       int rc = 0;
+       u16 cmd_mask;
 
        DBG_ENTER_ROUTINE
 
-       rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
-       if (rc) {
-               err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
-               return;
+       slot_cmd = 0x0300;
+       cmd_mask = PWR_LED_CTRL;
+       if (!pciehp_poll_mode) {
+               slot_cmd = slot_cmd | HP_INTR_ENABLE;
+               cmd_mask = cmd_mask | HP_INTR_ENABLE;
        }
 
-       slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300;
-
-       if (!pciehp_poll_mode)
-               slot_cmd = slot_cmd | HP_INTR_ENABLE; 
-       pcie_write_cmd(slot, slot_cmd);
+       pcie_write_cmd(slot, slot_cmd, cmd_mask);
        dbg("%s: SLOTCTRL %x write cmd %x\n",
            __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
 
@@ -624,22 +628,18 @@ static void hpc_set_green_led_blink(struct slot *slot)
 {
        struct controller *ctrl = slot->ctrl;
        u16 slot_cmd;
-       u16 slot_ctrl;
-       int rc = 0; 
+       u16 cmd_mask;
        
        DBG_ENTER_ROUTINE
 
-       rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
-       if (rc) {
-               err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
-               return;
+       slot_cmd = 0x0200;
+       cmd_mask = PWR_LED_CTRL;
+       if (!pciehp_poll_mode) {
+               slot_cmd = slot_cmd | HP_INTR_ENABLE;
+               cmd_mask = cmd_mask | HP_INTR_ENABLE;
        }
 
-       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);
+       pcie_write_cmd(slot, slot_cmd, cmd_mask);
 
        dbg("%s: SLOTCTRL %x write cmd %x\n",
            __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd);
@@ -656,6 +656,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
 }
 
@@ -663,7 +670,8 @@ static int hpc_power_on_slot(struct slot * slot)
 {
        struct controller *ctrl = slot->ctrl;
        u16 slot_cmd;
-       u16 slot_ctrl, slot_status;
+       u16 cmd_mask;
+       u16 slot_status;
        int retval = 0;
 
        DBG_ENTER_ROUTINE 
@@ -686,23 +694,23 @@ static int hpc_power_on_slot(struct slot * slot)
                }
        }
 
-       retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
-       if (retval) {
-               err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
-               return retval;
-       }
-
-       slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_ON;
-
+       slot_cmd = POWER_ON;
+       cmd_mask = PWR_CTRL;
        /* Enable detection that we turned off at slot power-off time */
-       if (!pciehp_poll_mode)
+       if (!pciehp_poll_mode) {
                slot_cmd = slot_cmd |
                           PWR_FAULT_DETECT_ENABLE |
                           MRL_DETECT_ENABLE |
                           PRSN_DETECT_ENABLE |
                           HP_INTR_ENABLE;
+               cmd_mask = cmd_mask |
+                          PWR_FAULT_DETECT_ENABLE |
+                          MRL_DETECT_ENABLE |
+                          PRSN_DETECT_ENABLE |
+                          HP_INTR_ENABLE;
+       }
 
-       retval = pcie_write_cmd(slot, slot_cmd);
+       retval = pcie_write_cmd(slot, slot_cmd, cmd_mask);
 
        if (retval) {
                err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd);
@@ -720,21 +728,15 @@ static int hpc_power_off_slot(struct slot * slot)
 {
        struct controller *ctrl = slot->ctrl;
        u16 slot_cmd;
-       u16 slot_ctrl;
+       u16 cmd_mask;
        int retval = 0;
 
        DBG_ENTER_ROUTINE 
 
        dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
 
-       retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl);
-       if (retval) {
-               err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__);
-               return retval;
-       }
-
-       slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_OFF;
-
+       slot_cmd = POWER_OFF;
+       cmd_mask = PWR_CTRL;
        /*
         * If we get MRL or presence detect interrupts now, the isr
         * will notice the sticky power-fault bit too and issue power
@@ -742,14 +744,19 @@ static int hpc_power_off_slot(struct slot * slot)
         * of command completions, since the power-fault bit remains on
         * till the slot is powered on again.
         */
-       if (!pciehp_poll_mode)
+       if (!pciehp_poll_mode) {
                slot_cmd = (slot_cmd &
                            ~PWR_FAULT_DETECT_ENABLE &
                            ~MRL_DETECT_ENABLE &
                            ~PRSN_DETECT_ENABLE) | HP_INTR_ENABLE;
+               cmd_mask = cmd_mask |
+                          PWR_FAULT_DETECT_ENABLE |
+                          MRL_DETECT_ENABLE |
+                          PRSN_DETECT_ENABLE |
+                          HP_INTR_ENABLE;
+       }
 
-       retval = pcie_write_cmd(slot, slot_cmd);
-
+       retval = pcie_write_cmd(slot, slot_cmd, cmd_mask);
        if (retval) {
                err("%s: Write command failed!\n", __FUNCTION__);
                return -1;
@@ -769,6 +776,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
        u16 temp_word;
        int hp_slot = 0;        /* only 1 slot per PCI Express port */
        int rc = 0;
+       unsigned long flags;
 
        rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
        if (rc) {
@@ -788,10 +796,12 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
        dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc);
        /* Mask Hot-plug Interrupt Enable */
        if (!pciehp_poll_mode) {
+               spin_lock_irqsave(&ctrl->lock, flags);
                rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
                if (rc) {
                        err("%s: Cannot read SLOT_CTRL register\n",
                            __FUNCTION__);
+                       spin_unlock_irqrestore(&ctrl->lock, flags);
                        return IRQ_NONE;
                }
 
@@ -802,8 +812,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
                if (rc) {
                        err("%s: Cannot write to SLOTCTRL register\n",
                            __FUNCTION__);
+                       spin_unlock_irqrestore(&ctrl->lock, flags);
                        return IRQ_NONE;
                }
+               spin_unlock_irqrestore(&ctrl->lock, flags);
 
                rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
                if (rc) {
@@ -853,10 +865,12 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
        }
        /* Unmask Hot-plug Interrupt Enable */
        if (!pciehp_poll_mode) {
+               spin_lock_irqsave(&ctrl->lock, flags);
                rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word);
                if (rc) {
                        err("%s: Cannot read SLOTCTRL register\n",
                            __FUNCTION__);
+                       spin_unlock_irqrestore(&ctrl->lock, flags);
                        return IRQ_NONE;
                }
 
@@ -867,8 +881,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
                if (rc) {
                        err("%s: Cannot write to SLOTCTRL register\n",
                            __FUNCTION__);
+                       spin_unlock_irqrestore(&ctrl->lock, flags);
                        return IRQ_NONE;
                }
+               spin_unlock_irqrestore(&ctrl->lock, flags);
 
                rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status);
                if (rc) {
@@ -1152,7 +1168,6 @@ 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;
@@ -1221,11 +1236,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,
@@ -1237,6 +1247,7 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
 
        mutex_init(&ctrl->crit_sect);
        mutex_init(&ctrl->ctrl_lock);
+       spin_lock_init(&ctrl->lock);
 
        /* setup wait queue */
        init_waitqueue_head(&ctrl->queue);
@@ -1286,7 +1297,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);
@@ -1296,6 +1308,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__);
@@ -1349,7 +1373,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