📄 pciehp_hpc.c
字号:
u16 cmd_mask; slot_cmd = 0x0200; cmd_mask = PWR_LED_CTRL; pcie_write_cmd(ctrl, slot_cmd, cmd_mask); dbg("%s: SLOTCTRL %x write cmd %x\n", __func__, ctrl->cap_base + SLOTCTRL, slot_cmd);}static int hpc_power_on_slot(struct slot * slot){ struct controller *ctrl = slot->ctrl; u16 slot_cmd; u16 cmd_mask; u16 slot_status; int retval = 0; dbg("%s: slot->hp_slot %x\n", __func__, slot->hp_slot); /* Clear sticky power-fault bit from previous power failures */ retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); if (retval) { err("%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } slot_status &= PWR_FAULT_DETECTED; if (slot_status) { retval = pciehp_writew(ctrl, SLOTSTATUS, slot_status); if (retval) { err("%s: Cannot write to SLOTSTATUS register\n", __func__); return retval; } } slot_cmd = POWER_ON; cmd_mask = PWR_CTRL; /* Enable detection that we turned off at slot power-off time */ if (!pciehp_poll_mode) { slot_cmd |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | PRSN_DETECT_ENABLE); cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | PRSN_DETECT_ENABLE); } retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); if (retval) { err("%s: Write %x command failed!\n", __func__, slot_cmd); return -1; } dbg("%s: SLOTCTRL %x write cmd %x\n", __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); return retval;}static inline int pcie_mask_bad_dllp(struct controller *ctrl){ struct pci_dev *dev = ctrl->pci_dev; int pos; u32 reg; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (!pos) return 0; pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®); if (reg & PCI_ERR_COR_BAD_DLLP) return 0; reg |= PCI_ERR_COR_BAD_DLLP; pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg); return 1;}static inline void pcie_unmask_bad_dllp(struct controller *ctrl){ struct pci_dev *dev = ctrl->pci_dev; u32 reg; int pos; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (!pos) return; pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®); if (!(reg & PCI_ERR_COR_BAD_DLLP)) return; reg &= ~PCI_ERR_COR_BAD_DLLP; pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);}static int hpc_power_off_slot(struct slot * slot){ struct controller *ctrl = slot->ctrl; u16 slot_cmd; u16 cmd_mask; int retval = 0; int changed; dbg("%s: slot->hp_slot %x\n", __func__, slot->hp_slot); /* * Set Bad DLLP Mask bit in Correctable Error Mask * Register. This is the workaround against Bad DLLP error * that sometimes happens during turning power off the slot * which conforms to PCI Express 1.0a spec. */ changed = pcie_mask_bad_dllp(ctrl); 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 * 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 &= ~(PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | PRSN_DETECT_ENABLE); cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | PRSN_DETECT_ENABLE); } retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); if (retval) { err("%s: Write command failed!\n", __func__); retval = -1; goto out; } dbg("%s: SLOTCTRL %x write cmd %x\n", __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); out: if (changed) pcie_unmask_bad_dllp(ctrl); return retval;}static irqreturn_t pcie_isr(int irq, void *dev_id){ struct controller *ctrl = (struct controller *)dev_id; u16 detected, intr_loc; struct slot *p_slot; /* * In order to guarantee that all interrupt events are * serviced, we need to re-inspect Slot Status register after * clearing what is presumed to be the last pending interrupt. */ intr_loc = 0; do { if (pciehp_readw(ctrl, SLOTSTATUS, &detected)) { err("%s: Cannot read SLOTSTATUS\n", __func__); return IRQ_NONE; } detected &= (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | MRL_SENS_CHANGED | PRSN_DETECT_CHANGED | CMD_COMPLETED); intr_loc |= detected; if (!intr_loc) return IRQ_NONE; if (detected && pciehp_writew(ctrl, SLOTSTATUS, detected)) { err("%s: Cannot write to SLOTSTATUS\n", __func__); return IRQ_NONE; } } while (detected); dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc); /* Check Command Complete Interrupt Pending */ if (intr_loc & CMD_COMPLETED) { ctrl->cmd_busy = 0; smp_mb(); wake_up(&ctrl->queue); } if (!(intr_loc & ~CMD_COMPLETED)) return IRQ_HANDLED; p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); /* Check MRL Sensor Changed */ if (intr_loc & MRL_SENS_CHANGED) pciehp_handle_switch_change(p_slot); /* Check Attention Button Pressed */ if (intr_loc & ATTN_BUTTN_PRESSED) pciehp_handle_attention_button(p_slot); /* Check Presence Detect Changed */ if (intr_loc & PRSN_DETECT_CHANGED) pciehp_handle_presence_change(p_slot); /* Check Power Fault Detected */ if (intr_loc & PWR_FAULT_DETECTED) pciehp_handle_power_fault(p_slot); return IRQ_HANDLED;}static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value){ struct controller *ctrl = slot->ctrl; enum pcie_link_speed lnk_speed; u32 lnk_cap; int retval = 0; retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap); if (retval) { err("%s: Cannot read LNKCAP register\n", __func__); return retval; } switch (lnk_cap & 0x000F) { case 1: lnk_speed = PCIE_2PT5GB; break; default: lnk_speed = PCIE_LNK_SPEED_UNKNOWN; break; } *value = lnk_speed; dbg("Max link speed = %d\n", lnk_speed); return retval;}static int hpc_get_max_lnk_width(struct slot *slot, enum pcie_link_width *value){ struct controller *ctrl = slot->ctrl; enum pcie_link_width lnk_wdth; u32 lnk_cap; int retval = 0; retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap); if (retval) { err("%s: Cannot read LNKCAP register\n", __func__); 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); return retval;}static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value){ struct controller *ctrl = slot->ctrl; enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN; int retval = 0; u16 lnk_status; retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); if (retval) { err("%s: Cannot read LNKSTATUS register\n", __func__); return retval; } switch (lnk_status & 0x0F) { case 1: lnk_speed = PCIE_2PT5GB; break; default: lnk_speed = PCIE_LNK_SPEED_UNKNOWN; break; } *value = lnk_speed; dbg("Current link speed = %d\n", lnk_speed); return retval;}static int hpc_get_cur_lnk_width(struct slot *slot, enum pcie_link_width *value){ struct controller *ctrl = slot->ctrl; enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; int retval = 0; u16 lnk_status; retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); if (retval) { err("%s: Cannot read LNKSTATUS register\n", __func__); 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); return retval;}static void pcie_release_ctrl(struct controller *ctrl);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_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, .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 = pcie_release_ctrl, .check_lnk_status = hpc_check_lnk_status,};int pcie_enable_notification(struct controller *ctrl){ u16 cmd, mask; cmd = PRSN_DETECT_ENABLE; if (ATTN_BUTTN(ctrl)) cmd |= ATTN_BUTTN_ENABLE; if (POWER_CTRL(ctrl)) cmd |= PWR_FAULT_DETECT_ENABLE; if (MRL_SENS(ctrl)) cmd |= MRL_DETECT_ENABLE; if (!pciehp_poll_mode) cmd |= HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE | PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; if (pcie_write_cmd(ctrl, cmd, mask)) { err("%s: Cannot enable software notification\n", __func__); return -1; } return 0;}static void pcie_disable_notification(struct controller *ctrl){ u16 mask; mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE | PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; if (pcie_write_cmd(ctrl, 0, mask)) warn("%s: Cannot disable software notification\n", __func__);}static int pcie_init_notification(struct controller *ctrl){ if (pciehp_request_irq(ctrl)) return -1; if (pcie_enable_notification(ctrl)) { pciehp_free_irq(ctrl); return -1; } return 0;}static void pcie_shutdown_notification(struct controller *ctrl){ pcie_disable_notification(ctrl); pciehp_free_irq(ctrl);}static int pcie_init_slot(struct controller *ctrl){ struct slot *slot; slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; slot->hp_slot = 0; slot->ctrl = ctrl; slot->bus = ctrl->pci_dev->subordinate->number; slot->device = ctrl->slot_device_offset + slot->hp_slot; slot->hpc_ops = ctrl->hpc_ops; slot->number = ctrl->first_slot; mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); list_add(&slot->slot_list, &ctrl->slot_list); return 0;}static void pcie_cleanup_slot(struct controller *ctrl){ struct slot *slot; slot = list_first_entry(&ctrl->slot_list, struct slot, slot_list); list_del(&slot->slot_list); cancel_delayed_work(&slot->work); flush_scheduled_work(); flush_workqueue(pciehp_wq); kfree(slot);}static inline void dbg_ctrl(struct controller *ctrl){ int i; u16 reg16; struct pci_dev *pdev = ctrl->pci_dev; if (!pciehp_debug) return; dbg("Hotplug Controller:\n"); dbg(" Seg/Bus/Dev/Func/IRQ : %s IRQ %d\n", pci_name(pdev), pdev->irq); dbg(" Vendor ID : 0x%04x\n", pdev->vendor); dbg(" Device ID : 0x%04x\n", pdev->device); dbg(" Subsystem ID : 0x%04x\n", pdev->subsystem_device); dbg(" Subsystem Vendor ID : 0x%04x\n", pdev->subsystem_vendor); dbg(" PCIe Cap offset : 0x%02x\n", ctrl->cap_base); for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { if (!pci_resource_len(pdev, i)) continue; dbg(" PCI resource [%d] : 0x%llx@0x%llx\n", i, (unsigned long long)pci_resource_len(pdev, i), (unsigned long long)pci_resource_start(pdev, i)); } dbg("Slot Capabilities : 0x%08x\n", ctrl->slot_cap); dbg(" Physical Slot Number : %d\n", ctrl->first_slot); dbg(" Attention Button : %3s\n", ATTN_BUTTN(ctrl) ? "yes" : "no"); dbg(" Power Controller : %3s\n", POWER_CTRL(ctrl) ? "yes" : "no"); dbg(" MRL Sensor : %3s\n", MRL_SENS(ctrl) ? "yes" : "no"); dbg(" Attention Indicator : %3s\n", ATTN_LED(ctrl) ? "yes" : "no"); dbg(" Power Indicator : %3s\n", PWR_LED(ctrl) ? "yes" : "no"); dbg(" Hot-Plug Surprise : %3s\n", HP_SUPR_RM(ctrl) ? "yes" : "no"); dbg(" EMI Present : %3s\n", EMI(ctrl) ? "yes" : "no"); dbg(" Command Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes"); pciehp_readw(ctrl, SLOTSTATUS, ®16); dbg("Slot Status : 0x%04x\n", reg16); pciehp_readw(ctrl, SLOTCTRL, ®16); dbg("Slot Control : 0x%04x\n", reg16);}struct controller *pcie_init(struct pcie_device *dev){ struct controller *ctrl; u32 slot_cap; struct pci_dev *pdev = dev->port; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) { err("%s : out of memory\n", __func__); goto abort; } INIT_LIST_HEAD(&ctrl->slot_list); ctrl->pci_dev = pdev; ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP); if (!ctrl->cap_base) { err("%s: Cannot find PCI Express capability\n", __func__); goto abort; } if (pciehp_readl(ctrl, SLOTCAP, &slot_cap)) { err("%s: Cannot read SLOTCAP register\n", __func__); goto abort; } ctrl->slot_cap = slot_cap; ctrl->first_slot = slot_cap >> 19; ctrl->slot_device_offset = 0; ctrl->num_slots = 1; ctrl->hpc_ops = &pciehp_hpc_ops; mutex_init(&ctrl->crit_sect); mutex_init(&ctrl->ctrl_lock); init_waitqueue_head(&ctrl->queue); 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; /* Clear all remaining event bits in Slot Status register */ if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) goto abort_ctrl; /* Disable sotfware notification */ pcie_disable_notification(ctrl); /* * If this is the first controller to be initialized, * initialize the pciehp work queue */ if (atomic_add_return(1, &pciehp_num_controllers) == 1) { pciehp_wq = create_singlethread_workqueue("pciehpd"); if (!pciehp_wq) goto abort_ctrl; } 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 (pcie_init_slot(ctrl)) goto abort_ctrl; if (pcie_init_notification(ctrl)) goto abort_slot; return ctrl;abort_slot: pcie_cleanup_slot(ctrl);abort_ctrl: kfree(ctrl);abort: return NULL;}void pcie_release_ctrl(struct controller *ctrl){ pcie_shutdown_notification(ctrl); pcie_cleanup_slot(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); kfree(ctrl);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -