diff options
Diffstat (limited to 'drivers/pci/hotplug/pciehp_hpc.c')
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 179 |
1 files changed, 113 insertions, 66 deletions
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 59c28093d291..1ce52437e1ed 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -609,23 +609,6 @@ static void hpc_set_green_led_blink(struct slot *slot) | |||
609 | __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); | 609 | __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); |
610 | } | 610 | } |
611 | 611 | ||
612 | static void hpc_release_ctlr(struct controller *ctrl) | ||
613 | { | ||
614 | /* Mask Hot-plug Interrupt Enable */ | ||
615 | if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) | ||
616 | err("%s: Cannot mask hotplug interrupt enable\n", __func__); | ||
617 | |||
618 | /* Free interrupt handler or interrupt polling timer */ | ||
619 | pciehp_free_irq(ctrl); | ||
620 | |||
621 | /* | ||
622 | * If this is the last controller to be released, destroy the | ||
623 | * pciehp work queue | ||
624 | */ | ||
625 | if (atomic_dec_and_test(&pciehp_num_controllers)) | ||
626 | destroy_workqueue(pciehp_wq); | ||
627 | } | ||
628 | |||
629 | static int hpc_power_on_slot(struct slot * slot) | 612 | static int hpc_power_on_slot(struct slot * slot) |
630 | { | 613 | { |
631 | struct controller *ctrl = slot->ctrl; | 614 | struct controller *ctrl = slot->ctrl; |
@@ -798,19 +781,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) | |||
798 | if (!(intr_loc & ~CMD_COMPLETED)) | 781 | if (!(intr_loc & ~CMD_COMPLETED)) |
799 | return IRQ_HANDLED; | 782 | return IRQ_HANDLED; |
800 | 783 | ||
801 | /* | ||
802 | * Return without handling events if this handler routine is | ||
803 | * called before controller initialization is done. This may | ||
804 | * happen if hotplug event or another interrupt that shares | ||
805 | * the IRQ with pciehp arrives before slot initialization is | ||
806 | * done after interrupt handler is registered. | ||
807 | * | ||
808 | * FIXME - Need more structural fixes. We need to be ready to | ||
809 | * handle the event before installing interrupt handler. | ||
810 | */ | ||
811 | p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); | 784 | p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); |
812 | if (!p_slot || !p_slot->hpc_ops) | ||
813 | return IRQ_HANDLED; | ||
814 | 785 | ||
815 | /* Check MRL Sensor Changed */ | 786 | /* Check MRL Sensor Changed */ |
816 | if (intr_loc & MRL_SENS_CHANGED) | 787 | if (intr_loc & MRL_SENS_CHANGED) |
@@ -987,6 +958,7 @@ static int hpc_get_cur_lnk_width(struct slot *slot, | |||
987 | return retval; | 958 | return retval; |
988 | } | 959 | } |
989 | 960 | ||
961 | static void pcie_release_ctrl(struct controller *ctrl); | ||
990 | static struct hpc_ops pciehp_hpc_ops = { | 962 | static struct hpc_ops pciehp_hpc_ops = { |
991 | .power_on_slot = hpc_power_on_slot, | 963 | .power_on_slot = hpc_power_on_slot, |
992 | .power_off_slot = hpc_power_off_slot, | 964 | .power_off_slot = hpc_power_off_slot, |
@@ -1008,28 +980,11 @@ static struct hpc_ops pciehp_hpc_ops = { | |||
1008 | .green_led_off = hpc_set_green_led_off, | 980 | .green_led_off = hpc_set_green_led_off, |
1009 | .green_led_blink = hpc_set_green_led_blink, | 981 | .green_led_blink = hpc_set_green_led_blink, |
1010 | 982 | ||
1011 | .release_ctlr = hpc_release_ctlr, | 983 | .release_ctlr = pcie_release_ctrl, |
1012 | .check_lnk_status = hpc_check_lnk_status, | 984 | .check_lnk_status = hpc_check_lnk_status, |
1013 | }; | 985 | }; |
1014 | 986 | ||
1015 | static int pcie_init_hardware_part1(struct controller *ctrl, | 987 | int pcie_enable_notification(struct controller *ctrl) |
1016 | struct pcie_device *dev) | ||
1017 | { | ||
1018 | /* Clear all remaining event bits in Slot Status register */ | ||
1019 | if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) { | ||
1020 | err("%s: Cannot write to SLOTSTATUS register\n", __func__); | ||
1021 | return -1; | ||
1022 | } | ||
1023 | |||
1024 | /* Mask Hot-plug Interrupt Enable */ | ||
1025 | if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) { | ||
1026 | err("%s: Cannot mask hotplug interrupt enable\n", __func__); | ||
1027 | return -1; | ||
1028 | } | ||
1029 | return 0; | ||
1030 | } | ||
1031 | |||
1032 | int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev) | ||
1033 | { | 988 | { |
1034 | u16 cmd, mask; | 989 | u16 cmd, mask; |
1035 | 990 | ||
@@ -1050,10 +1005,76 @@ int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev) | |||
1050 | err("%s: Cannot enable software notification\n", __func__); | 1005 | err("%s: Cannot enable software notification\n", __func__); |
1051 | return -1; | 1006 | return -1; |
1052 | } | 1007 | } |
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | static void pcie_disable_notification(struct controller *ctrl) | ||
1012 | { | ||
1013 | u16 mask; | ||
1014 | mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE | | ||
1015 | PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; | ||
1016 | if (pcie_write_cmd(ctrl, 0, mask)) | ||
1017 | warn("%s: Cannot disable software notification\n", __func__); | ||
1018 | } | ||
1019 | |||
1020 | static int pcie_init_notification(struct controller *ctrl) | ||
1021 | { | ||
1022 | if (pciehp_request_irq(ctrl)) | ||
1023 | return -1; | ||
1024 | if (pcie_enable_notification(ctrl)) { | ||
1025 | pciehp_free_irq(ctrl); | ||
1026 | return -1; | ||
1027 | } | ||
1028 | return 0; | ||
1029 | } | ||
1030 | |||
1031 | static void pcie_shutdown_notification(struct controller *ctrl) | ||
1032 | { | ||
1033 | pcie_disable_notification(ctrl); | ||
1034 | pciehp_free_irq(ctrl); | ||
1035 | } | ||
1036 | |||
1037 | static void make_slot_name(struct slot *slot) | ||
1038 | { | ||
1039 | if (pciehp_slot_with_bus) | ||
1040 | snprintf(slot->name, SLOT_NAME_SIZE, "%04d_%04d", | ||
1041 | slot->bus, slot->number); | ||
1042 | else | ||
1043 | snprintf(slot->name, SLOT_NAME_SIZE, "%d", slot->number); | ||
1044 | } | ||
1053 | 1045 | ||
1046 | static int pcie_init_slot(struct controller *ctrl) | ||
1047 | { | ||
1048 | struct slot *slot; | ||
1049 | |||
1050 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | ||
1051 | if (!slot) | ||
1052 | return -ENOMEM; | ||
1053 | |||
1054 | slot->hp_slot = 0; | ||
1055 | slot->ctrl = ctrl; | ||
1056 | slot->bus = ctrl->pci_dev->subordinate->number; | ||
1057 | slot->device = ctrl->slot_device_offset + slot->hp_slot; | ||
1058 | slot->hpc_ops = ctrl->hpc_ops; | ||
1059 | slot->number = ctrl->first_slot; | ||
1060 | make_slot_name(slot); | ||
1061 | mutex_init(&slot->lock); | ||
1062 | INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); | ||
1063 | list_add(&slot->slot_list, &ctrl->slot_list); | ||
1054 | return 0; | 1064 | return 0; |
1055 | } | 1065 | } |
1056 | 1066 | ||
1067 | static void pcie_cleanup_slot(struct controller *ctrl) | ||
1068 | { | ||
1069 | struct slot *slot; | ||
1070 | slot = list_first_entry(&ctrl->slot_list, struct slot, slot_list); | ||
1071 | list_del(&slot->slot_list); | ||
1072 | cancel_delayed_work(&slot->work); | ||
1073 | flush_scheduled_work(); | ||
1074 | flush_workqueue(pciehp_wq); | ||
1075 | kfree(slot); | ||
1076 | } | ||
1077 | |||
1057 | static inline void dbg_ctrl(struct controller *ctrl) | 1078 | static inline void dbg_ctrl(struct controller *ctrl) |
1058 | { | 1079 | { |
1059 | int i; | 1080 | int i; |
@@ -1093,11 +1114,19 @@ static inline void dbg_ctrl(struct controller *ctrl) | |||
1093 | dbg("Slot Control : 0x%04x\n", reg16); | 1114 | dbg("Slot Control : 0x%04x\n", reg16); |
1094 | } | 1115 | } |
1095 | 1116 | ||
1096 | int pcie_init(struct controller *ctrl, struct pcie_device *dev) | 1117 | struct controller *pcie_init(struct pcie_device *dev) |
1097 | { | 1118 | { |
1119 | struct controller *ctrl; | ||
1098 | u32 slot_cap; | 1120 | u32 slot_cap; |
1099 | struct pci_dev *pdev = dev->port; | 1121 | struct pci_dev *pdev = dev->port; |
1100 | 1122 | ||
1123 | ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); | ||
1124 | if (!ctrl) { | ||
1125 | err("%s : out of memory\n", __func__); | ||
1126 | goto abort; | ||
1127 | } | ||
1128 | INIT_LIST_HEAD(&ctrl->slot_list); | ||
1129 | |||
1101 | ctrl->pci_dev = pdev; | 1130 | ctrl->pci_dev = pdev; |
1102 | ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP); | 1131 | ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP); |
1103 | if (!ctrl->cap_base) { | 1132 | if (!ctrl->cap_base) { |
@@ -1128,15 +1157,12 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev) | |||
1128 | !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl))) | 1157 | !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl))) |
1129 | ctrl->no_cmd_complete = 1; | 1158 | ctrl->no_cmd_complete = 1; |
1130 | 1159 | ||
1131 | info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", | 1160 | /* Clear all remaining event bits in Slot Status register */ |
1132 | pdev->vendor, pdev->device, | 1161 | if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) |
1133 | pdev->subsystem_vendor, pdev->subsystem_device); | 1162 | goto abort_ctrl; |
1134 | 1163 | ||
1135 | if (pcie_init_hardware_part1(ctrl, dev)) | 1164 | /* Disable sotfware notification */ |
1136 | goto abort; | 1165 | pcie_disable_notification(ctrl); |
1137 | |||
1138 | if (pciehp_request_irq(ctrl)) | ||
1139 | goto abort; | ||
1140 | 1166 | ||
1141 | /* | 1167 | /* |
1142 | * If this is the first controller to be initialized, | 1168 | * If this is the first controller to be initialized, |
@@ -1144,18 +1170,39 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev) | |||
1144 | */ | 1170 | */ |
1145 | if (atomic_add_return(1, &pciehp_num_controllers) == 1) { | 1171 | if (atomic_add_return(1, &pciehp_num_controllers) == 1) { |
1146 | pciehp_wq = create_singlethread_workqueue("pciehpd"); | 1172 | pciehp_wq = create_singlethread_workqueue("pciehpd"); |
1147 | if (!pciehp_wq) { | 1173 | if (!pciehp_wq) |
1148 | goto abort_free_irq; | 1174 | goto abort_ctrl; |
1149 | } | ||
1150 | } | 1175 | } |
1151 | 1176 | ||
1152 | if (pcie_init_hardware_part2(ctrl, dev)) | 1177 | info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", |
1153 | goto abort_free_irq; | 1178 | pdev->vendor, pdev->device, |
1179 | pdev->subsystem_vendor, pdev->subsystem_device); | ||
1180 | |||
1181 | if (pcie_init_slot(ctrl)) | ||
1182 | goto abort_ctrl; | ||
1154 | 1183 | ||
1155 | return 0; | 1184 | if (pcie_init_notification(ctrl)) |
1185 | goto abort_slot; | ||
1156 | 1186 | ||
1157 | abort_free_irq: | 1187 | return ctrl; |
1158 | pciehp_free_irq(ctrl); | 1188 | |
1189 | abort_slot: | ||
1190 | pcie_cleanup_slot(ctrl); | ||
1191 | abort_ctrl: | ||
1192 | kfree(ctrl); | ||
1159 | abort: | 1193 | abort: |
1160 | return -1; | 1194 | return NULL; |
1195 | } | ||
1196 | |||
1197 | void pcie_release_ctrl(struct controller *ctrl) | ||
1198 | { | ||
1199 | pcie_shutdown_notification(ctrl); | ||
1200 | pcie_cleanup_slot(ctrl); | ||
1201 | /* | ||
1202 | * If this is the last controller to be released, destroy the | ||
1203 | * pciehp work queue | ||
1204 | */ | ||
1205 | if (atomic_dec_and_test(&pciehp_num_controllers)) | ||
1206 | destroy_workqueue(pciehp_wq); | ||
1207 | kfree(ctrl); | ||
1161 | } | 1208 | } |