diff options
Diffstat (limited to 'arch/powerpc/kernel/eeh.c')
-rw-r--r-- | arch/powerpc/kernel/eeh.c | 366 |
1 files changed, 343 insertions, 23 deletions
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 86e25702aaca..59a64f8dc85f 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/init.h> | 27 | #include <linux/init.h> |
28 | #include <linux/list.h> | 28 | #include <linux/list.h> |
29 | #include <linux/pci.h> | 29 | #include <linux/pci.h> |
30 | #include <linux/iommu.h> | ||
30 | #include <linux/proc_fs.h> | 31 | #include <linux/proc_fs.h> |
31 | #include <linux/rbtree.h> | 32 | #include <linux/rbtree.h> |
32 | #include <linux/reboot.h> | 33 | #include <linux/reboot.h> |
@@ -40,6 +41,7 @@ | |||
40 | #include <asm/eeh.h> | 41 | #include <asm/eeh.h> |
41 | #include <asm/eeh_event.h> | 42 | #include <asm/eeh_event.h> |
42 | #include <asm/io.h> | 43 | #include <asm/io.h> |
44 | #include <asm/iommu.h> | ||
43 | #include <asm/machdep.h> | 45 | #include <asm/machdep.h> |
44 | #include <asm/ppc-pci.h> | 46 | #include <asm/ppc-pci.h> |
45 | #include <asm/rtas.h> | 47 | #include <asm/rtas.h> |
@@ -108,6 +110,9 @@ struct eeh_ops *eeh_ops = NULL; | |||
108 | /* Lock to avoid races due to multiple reports of an error */ | 110 | /* Lock to avoid races due to multiple reports of an error */ |
109 | DEFINE_RAW_SPINLOCK(confirm_error_lock); | 111 | DEFINE_RAW_SPINLOCK(confirm_error_lock); |
110 | 112 | ||
113 | /* Lock to protect passed flags */ | ||
114 | static DEFINE_MUTEX(eeh_dev_mutex); | ||
115 | |||
111 | /* Buffer for reporting pci register dumps. Its here in BSS, and | 116 | /* Buffer for reporting pci register dumps. Its here in BSS, and |
112 | * not dynamically alloced, so that it ends up in RMO where RTAS | 117 | * not dynamically alloced, so that it ends up in RMO where RTAS |
113 | * can access it. | 118 | * can access it. |
@@ -137,7 +142,7 @@ static struct eeh_stats eeh_stats; | |||
137 | static int __init eeh_setup(char *str) | 142 | static int __init eeh_setup(char *str) |
138 | { | 143 | { |
139 | if (!strcmp(str, "off")) | 144 | if (!strcmp(str, "off")) |
140 | eeh_subsystem_flags |= EEH_FORCE_DISABLED; | 145 | eeh_add_flag(EEH_FORCE_DISABLED); |
141 | 146 | ||
142 | return 1; | 147 | return 1; |
143 | } | 148 | } |
@@ -152,12 +157,13 @@ __setup("eeh=", eeh_setup); | |||
152 | * This routine captures assorted PCI configuration space data, | 157 | * This routine captures assorted PCI configuration space data, |
153 | * and puts them into a buffer for RTAS error logging. | 158 | * and puts them into a buffer for RTAS error logging. |
154 | */ | 159 | */ |
155 | static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) | 160 | static size_t eeh_gather_pci_data(struct eeh_dev *edev, char *buf, size_t len) |
156 | { | 161 | { |
157 | struct device_node *dn = eeh_dev_to_of_node(edev); | 162 | struct device_node *dn = eeh_dev_to_of_node(edev); |
158 | u32 cfg; | 163 | u32 cfg; |
159 | int cap, i; | 164 | int cap, i; |
160 | int n = 0; | 165 | int n = 0, l = 0; |
166 | char buffer[128]; | ||
161 | 167 | ||
162 | n += scnprintf(buf+n, len-n, "%s\n", dn->full_name); | 168 | n += scnprintf(buf+n, len-n, "%s\n", dn->full_name); |
163 | pr_warn("EEH: of node=%s\n", dn->full_name); | 169 | pr_warn("EEH: of node=%s\n", dn->full_name); |
@@ -202,8 +208,22 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) | |||
202 | for (i=0; i<=8; i++) { | 208 | for (i=0; i<=8; i++) { |
203 | eeh_ops->read_config(dn, cap+4*i, 4, &cfg); | 209 | eeh_ops->read_config(dn, cap+4*i, 4, &cfg); |
204 | n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); | 210 | n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); |
205 | pr_warn("EEH: PCI-E %02x: %08x\n", i, cfg); | 211 | |
212 | if ((i % 4) == 0) { | ||
213 | if (i != 0) | ||
214 | pr_warn("%s\n", buffer); | ||
215 | |||
216 | l = scnprintf(buffer, sizeof(buffer), | ||
217 | "EEH: PCI-E %02x: %08x ", | ||
218 | 4*i, cfg); | ||
219 | } else { | ||
220 | l += scnprintf(buffer+l, sizeof(buffer)-l, | ||
221 | "%08x ", cfg); | ||
222 | } | ||
223 | |||
206 | } | 224 | } |
225 | |||
226 | pr_warn("%s\n", buffer); | ||
207 | } | 227 | } |
208 | 228 | ||
209 | /* If AER capable, dump it */ | 229 | /* If AER capable, dump it */ |
@@ -212,11 +232,24 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) | |||
212 | n += scnprintf(buf+n, len-n, "pci-e AER:\n"); | 232 | n += scnprintf(buf+n, len-n, "pci-e AER:\n"); |
213 | pr_warn("EEH: PCI-E AER capability register set follows:\n"); | 233 | pr_warn("EEH: PCI-E AER capability register set follows:\n"); |
214 | 234 | ||
215 | for (i=0; i<14; i++) { | 235 | for (i=0; i<=13; i++) { |
216 | eeh_ops->read_config(dn, cap+4*i, 4, &cfg); | 236 | eeh_ops->read_config(dn, cap+4*i, 4, &cfg); |
217 | n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); | 237 | n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); |
218 | pr_warn("EEH: PCI-E AER %02x: %08x\n", i, cfg); | 238 | |
239 | if ((i % 4) == 0) { | ||
240 | if (i != 0) | ||
241 | pr_warn("%s\n", buffer); | ||
242 | |||
243 | l = scnprintf(buffer, sizeof(buffer), | ||
244 | "EEH: PCI-E AER %02x: %08x ", | ||
245 | 4*i, cfg); | ||
246 | } else { | ||
247 | l += scnprintf(buffer+l, sizeof(buffer)-l, | ||
248 | "%08x ", cfg); | ||
249 | } | ||
219 | } | 250 | } |
251 | |||
252 | pr_warn("%s\n", buffer); | ||
220 | } | 253 | } |
221 | 254 | ||
222 | return n; | 255 | return n; |
@@ -247,7 +280,7 @@ void eeh_slot_error_detail(struct eeh_pe *pe, int severity) | |||
247 | * 0xFF's is always returned from PCI config space. | 280 | * 0xFF's is always returned from PCI config space. |
248 | */ | 281 | */ |
249 | if (!(pe->type & EEH_PE_PHB)) { | 282 | if (!(pe->type & EEH_PE_PHB)) { |
250 | if (eeh_probe_mode_devtree()) | 283 | if (eeh_has_flag(EEH_ENABLE_IO_FOR_LOG)) |
251 | eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); | 284 | eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); |
252 | eeh_ops->configure_bridge(pe); | 285 | eeh_ops->configure_bridge(pe); |
253 | eeh_pe_restore_bars(pe); | 286 | eeh_pe_restore_bars(pe); |
@@ -298,14 +331,14 @@ static int eeh_phb_check_failure(struct eeh_pe *pe) | |||
298 | unsigned long flags; | 331 | unsigned long flags; |
299 | int ret; | 332 | int ret; |
300 | 333 | ||
301 | if (!eeh_probe_mode_dev()) | 334 | if (!eeh_has_flag(EEH_PROBE_MODE_DEV)) |
302 | return -EPERM; | 335 | return -EPERM; |
303 | 336 | ||
304 | /* Find the PHB PE */ | 337 | /* Find the PHB PE */ |
305 | phb_pe = eeh_phb_pe_get(pe->phb); | 338 | phb_pe = eeh_phb_pe_get(pe->phb); |
306 | if (!phb_pe) { | 339 | if (!phb_pe) { |
307 | pr_warning("%s Can't find PE for PHB#%d\n", | 340 | pr_warn("%s Can't find PE for PHB#%d\n", |
308 | __func__, pe->phb->global_number); | 341 | __func__, pe->phb->global_number); |
309 | return -EEXIST; | 342 | return -EEXIST; |
310 | } | 343 | } |
311 | 344 | ||
@@ -400,6 +433,14 @@ int eeh_dev_check_failure(struct eeh_dev *edev) | |||
400 | if (ret > 0) | 433 | if (ret > 0) |
401 | return ret; | 434 | return ret; |
402 | 435 | ||
436 | /* | ||
437 | * If the PE isn't owned by us, we shouldn't check the | ||
438 | * state. Instead, let the owner handle it if the PE has | ||
439 | * been frozen. | ||
440 | */ | ||
441 | if (eeh_pe_passed(pe)) | ||
442 | return 0; | ||
443 | |||
403 | /* If we already have a pending isolation event for this | 444 | /* If we already have a pending isolation event for this |
404 | * slot, we know it's bad already, we don't need to check. | 445 | * slot, we know it's bad already, we don't need to check. |
405 | * Do this checking under a lock; as multiple PCI devices | 446 | * Do this checking under a lock; as multiple PCI devices |
@@ -746,13 +787,13 @@ void eeh_save_bars(struct eeh_dev *edev) | |||
746 | int __init eeh_ops_register(struct eeh_ops *ops) | 787 | int __init eeh_ops_register(struct eeh_ops *ops) |
747 | { | 788 | { |
748 | if (!ops->name) { | 789 | if (!ops->name) { |
749 | pr_warning("%s: Invalid EEH ops name for %p\n", | 790 | pr_warn("%s: Invalid EEH ops name for %p\n", |
750 | __func__, ops); | 791 | __func__, ops); |
751 | return -EINVAL; | 792 | return -EINVAL; |
752 | } | 793 | } |
753 | 794 | ||
754 | if (eeh_ops && eeh_ops != ops) { | 795 | if (eeh_ops && eeh_ops != ops) { |
755 | pr_warning("%s: EEH ops of platform %s already existing (%s)\n", | 796 | pr_warn("%s: EEH ops of platform %s already existing (%s)\n", |
756 | __func__, eeh_ops->name, ops->name); | 797 | __func__, eeh_ops->name, ops->name); |
757 | return -EEXIST; | 798 | return -EEXIST; |
758 | } | 799 | } |
@@ -772,7 +813,7 @@ int __init eeh_ops_register(struct eeh_ops *ops) | |||
772 | int __exit eeh_ops_unregister(const char *name) | 813 | int __exit eeh_ops_unregister(const char *name) |
773 | { | 814 | { |
774 | if (!name || !strlen(name)) { | 815 | if (!name || !strlen(name)) { |
775 | pr_warning("%s: Invalid EEH ops name\n", | 816 | pr_warn("%s: Invalid EEH ops name\n", |
776 | __func__); | 817 | __func__); |
777 | return -EINVAL; | 818 | return -EINVAL; |
778 | } | 819 | } |
@@ -788,7 +829,7 @@ int __exit eeh_ops_unregister(const char *name) | |||
788 | static int eeh_reboot_notifier(struct notifier_block *nb, | 829 | static int eeh_reboot_notifier(struct notifier_block *nb, |
789 | unsigned long action, void *unused) | 830 | unsigned long action, void *unused) |
790 | { | 831 | { |
791 | eeh_set_enable(false); | 832 | eeh_clear_flag(EEH_ENABLED); |
792 | return NOTIFY_DONE; | 833 | return NOTIFY_DONE; |
793 | } | 834 | } |
794 | 835 | ||
@@ -837,11 +878,11 @@ int eeh_init(void) | |||
837 | 878 | ||
838 | /* call platform initialization function */ | 879 | /* call platform initialization function */ |
839 | if (!eeh_ops) { | 880 | if (!eeh_ops) { |
840 | pr_warning("%s: Platform EEH operation not found\n", | 881 | pr_warn("%s: Platform EEH operation not found\n", |
841 | __func__); | 882 | __func__); |
842 | return -EEXIST; | 883 | return -EEXIST; |
843 | } else if ((ret = eeh_ops->init())) { | 884 | } else if ((ret = eeh_ops->init())) { |
844 | pr_warning("%s: Failed to call platform init function (%d)\n", | 885 | pr_warn("%s: Failed to call platform init function (%d)\n", |
845 | __func__, ret); | 886 | __func__, ret); |
846 | return ret; | 887 | return ret; |
847 | } | 888 | } |
@@ -852,13 +893,13 @@ int eeh_init(void) | |||
852 | return ret; | 893 | return ret; |
853 | 894 | ||
854 | /* Enable EEH for all adapters */ | 895 | /* Enable EEH for all adapters */ |
855 | if (eeh_probe_mode_devtree()) { | 896 | if (eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) { |
856 | list_for_each_entry_safe(hose, tmp, | 897 | list_for_each_entry_safe(hose, tmp, |
857 | &hose_list, list_node) { | 898 | &hose_list, list_node) { |
858 | phb = hose->dn; | 899 | phb = hose->dn; |
859 | traverse_pci_devices(phb, eeh_ops->of_probe, NULL); | 900 | traverse_pci_devices(phb, eeh_ops->of_probe, NULL); |
860 | } | 901 | } |
861 | } else if (eeh_probe_mode_dev()) { | 902 | } else if (eeh_has_flag(EEH_PROBE_MODE_DEV)) { |
862 | list_for_each_entry_safe(hose, tmp, | 903 | list_for_each_entry_safe(hose, tmp, |
863 | &hose_list, list_node) | 904 | &hose_list, list_node) |
864 | pci_walk_bus(hose->bus, eeh_ops->dev_probe, NULL); | 905 | pci_walk_bus(hose->bus, eeh_ops->dev_probe, NULL); |
@@ -882,7 +923,7 @@ int eeh_init(void) | |||
882 | if (eeh_enabled()) | 923 | if (eeh_enabled()) |
883 | pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n"); | 924 | pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n"); |
884 | else | 925 | else |
885 | pr_warning("EEH: No capable adapters found\n"); | 926 | pr_warn("EEH: No capable adapters found\n"); |
886 | 927 | ||
887 | return ret; | 928 | return ret; |
888 | } | 929 | } |
@@ -910,7 +951,7 @@ void eeh_add_device_early(struct device_node *dn) | |||
910 | * would delay the probe until late stage because | 951 | * would delay the probe until late stage because |
911 | * the PCI device isn't available this moment. | 952 | * the PCI device isn't available this moment. |
912 | */ | 953 | */ |
913 | if (!eeh_probe_mode_devtree()) | 954 | if (!eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) |
914 | return; | 955 | return; |
915 | 956 | ||
916 | if (!of_node_to_eeh_dev(dn)) | 957 | if (!of_node_to_eeh_dev(dn)) |
@@ -996,7 +1037,7 @@ void eeh_add_device_late(struct pci_dev *dev) | |||
996 | * We have to do the EEH probe here because the PCI device | 1037 | * We have to do the EEH probe here because the PCI device |
997 | * hasn't been created yet in the early stage. | 1038 | * hasn't been created yet in the early stage. |
998 | */ | 1039 | */ |
999 | if (eeh_probe_mode_dev()) | 1040 | if (eeh_has_flag(EEH_PROBE_MODE_DEV)) |
1000 | eeh_ops->dev_probe(dev, NULL); | 1041 | eeh_ops->dev_probe(dev, NULL); |
1001 | 1042 | ||
1002 | eeh_addr_cache_insert_dev(dev); | 1043 | eeh_addr_cache_insert_dev(dev); |
@@ -1100,6 +1141,285 @@ void eeh_remove_device(struct pci_dev *dev) | |||
1100 | edev->mode &= ~EEH_DEV_SYSFS; | 1141 | edev->mode &= ~EEH_DEV_SYSFS; |
1101 | } | 1142 | } |
1102 | 1143 | ||
1144 | /** | ||
1145 | * eeh_dev_open - Increase count of pass through devices for PE | ||
1146 | * @pdev: PCI device | ||
1147 | * | ||
1148 | * Increase count of passed through devices for the indicated | ||
1149 | * PE. In the result, the EEH errors detected on the PE won't be | ||
1150 | * reported. The PE owner will be responsible for detection | ||
1151 | * and recovery. | ||
1152 | */ | ||
1153 | int eeh_dev_open(struct pci_dev *pdev) | ||
1154 | { | ||
1155 | struct eeh_dev *edev; | ||
1156 | |||
1157 | mutex_lock(&eeh_dev_mutex); | ||
1158 | |||
1159 | /* No PCI device ? */ | ||
1160 | if (!pdev) | ||
1161 | goto out; | ||
1162 | |||
1163 | /* No EEH device or PE ? */ | ||
1164 | edev = pci_dev_to_eeh_dev(pdev); | ||
1165 | if (!edev || !edev->pe) | ||
1166 | goto out; | ||
1167 | |||
1168 | /* Increase PE's pass through count */ | ||
1169 | atomic_inc(&edev->pe->pass_dev_cnt); | ||
1170 | mutex_unlock(&eeh_dev_mutex); | ||
1171 | |||
1172 | return 0; | ||
1173 | out: | ||
1174 | mutex_unlock(&eeh_dev_mutex); | ||
1175 | return -ENODEV; | ||
1176 | } | ||
1177 | EXPORT_SYMBOL_GPL(eeh_dev_open); | ||
1178 | |||
1179 | /** | ||
1180 | * eeh_dev_release - Decrease count of pass through devices for PE | ||
1181 | * @pdev: PCI device | ||
1182 | * | ||
1183 | * Decrease count of pass through devices for the indicated PE. If | ||
1184 | * there is no passed through device in PE, the EEH errors detected | ||
1185 | * on the PE will be reported and handled as usual. | ||
1186 | */ | ||
1187 | void eeh_dev_release(struct pci_dev *pdev) | ||
1188 | { | ||
1189 | struct eeh_dev *edev; | ||
1190 | |||
1191 | mutex_lock(&eeh_dev_mutex); | ||
1192 | |||
1193 | /* No PCI device ? */ | ||
1194 | if (!pdev) | ||
1195 | goto out; | ||
1196 | |||
1197 | /* No EEH device ? */ | ||
1198 | edev = pci_dev_to_eeh_dev(pdev); | ||
1199 | if (!edev || !edev->pe || !eeh_pe_passed(edev->pe)) | ||
1200 | goto out; | ||
1201 | |||
1202 | /* Decrease PE's pass through count */ | ||
1203 | atomic_dec(&edev->pe->pass_dev_cnt); | ||
1204 | WARN_ON(atomic_read(&edev->pe->pass_dev_cnt) < 0); | ||
1205 | out: | ||
1206 | mutex_unlock(&eeh_dev_mutex); | ||
1207 | } | ||
1208 | EXPORT_SYMBOL(eeh_dev_release); | ||
1209 | |||
1210 | #ifdef CONFIG_IOMMU_API | ||
1211 | |||
1212 | static int dev_has_iommu_table(struct device *dev, void *data) | ||
1213 | { | ||
1214 | struct pci_dev *pdev = to_pci_dev(dev); | ||
1215 | struct pci_dev **ppdev = data; | ||
1216 | struct iommu_table *tbl; | ||
1217 | |||
1218 | if (!dev) | ||
1219 | return 0; | ||
1220 | |||
1221 | tbl = get_iommu_table_base(dev); | ||
1222 | if (tbl && tbl->it_group) { | ||
1223 | *ppdev = pdev; | ||
1224 | return 1; | ||
1225 | } | ||
1226 | |||
1227 | return 0; | ||
1228 | } | ||
1229 | |||
1230 | /** | ||
1231 | * eeh_iommu_group_to_pe - Convert IOMMU group to EEH PE | ||
1232 | * @group: IOMMU group | ||
1233 | * | ||
1234 | * The routine is called to convert IOMMU group to EEH PE. | ||
1235 | */ | ||
1236 | struct eeh_pe *eeh_iommu_group_to_pe(struct iommu_group *group) | ||
1237 | { | ||
1238 | struct pci_dev *pdev = NULL; | ||
1239 | struct eeh_dev *edev; | ||
1240 | int ret; | ||
1241 | |||
1242 | /* No IOMMU group ? */ | ||
1243 | if (!group) | ||
1244 | return NULL; | ||
1245 | |||
1246 | ret = iommu_group_for_each_dev(group, &pdev, dev_has_iommu_table); | ||
1247 | if (!ret || !pdev) | ||
1248 | return NULL; | ||
1249 | |||
1250 | /* No EEH device or PE ? */ | ||
1251 | edev = pci_dev_to_eeh_dev(pdev); | ||
1252 | if (!edev || !edev->pe) | ||
1253 | return NULL; | ||
1254 | |||
1255 | return edev->pe; | ||
1256 | } | ||
1257 | EXPORT_SYMBOL_GPL(eeh_iommu_group_to_pe); | ||
1258 | |||
1259 | #endif /* CONFIG_IOMMU_API */ | ||
1260 | |||
1261 | /** | ||
1262 | * eeh_pe_set_option - Set options for the indicated PE | ||
1263 | * @pe: EEH PE | ||
1264 | * @option: requested option | ||
1265 | * | ||
1266 | * The routine is called to enable or disable EEH functionality | ||
1267 | * on the indicated PE, to enable IO or DMA for the frozen PE. | ||
1268 | */ | ||
1269 | int eeh_pe_set_option(struct eeh_pe *pe, int option) | ||
1270 | { | ||
1271 | int ret = 0; | ||
1272 | |||
1273 | /* Invalid PE ? */ | ||
1274 | if (!pe) | ||
1275 | return -ENODEV; | ||
1276 | |||
1277 | /* | ||
1278 | * EEH functionality could possibly be disabled, just | ||
1279 | * return error for the case. And the EEH functinality | ||
1280 | * isn't expected to be disabled on one specific PE. | ||
1281 | */ | ||
1282 | switch (option) { | ||
1283 | case EEH_OPT_ENABLE: | ||
1284 | if (eeh_enabled()) | ||
1285 | break; | ||
1286 | ret = -EIO; | ||
1287 | break; | ||
1288 | case EEH_OPT_DISABLE: | ||
1289 | break; | ||
1290 | case EEH_OPT_THAW_MMIO: | ||
1291 | case EEH_OPT_THAW_DMA: | ||
1292 | if (!eeh_ops || !eeh_ops->set_option) { | ||
1293 | ret = -ENOENT; | ||
1294 | break; | ||
1295 | } | ||
1296 | |||
1297 | ret = eeh_ops->set_option(pe, option); | ||
1298 | break; | ||
1299 | default: | ||
1300 | pr_debug("%s: Option %d out of range (%d, %d)\n", | ||
1301 | __func__, option, EEH_OPT_DISABLE, EEH_OPT_THAW_DMA); | ||
1302 | ret = -EINVAL; | ||
1303 | } | ||
1304 | |||
1305 | return ret; | ||
1306 | } | ||
1307 | EXPORT_SYMBOL_GPL(eeh_pe_set_option); | ||
1308 | |||
1309 | /** | ||
1310 | * eeh_pe_get_state - Retrieve PE's state | ||
1311 | * @pe: EEH PE | ||
1312 | * | ||
1313 | * Retrieve the PE's state, which includes 3 aspects: enabled | ||
1314 | * DMA, enabled IO and asserted reset. | ||
1315 | */ | ||
1316 | int eeh_pe_get_state(struct eeh_pe *pe) | ||
1317 | { | ||
1318 | int result, ret = 0; | ||
1319 | bool rst_active, dma_en, mmio_en; | ||
1320 | |||
1321 | /* Existing PE ? */ | ||
1322 | if (!pe) | ||
1323 | return -ENODEV; | ||
1324 | |||
1325 | if (!eeh_ops || !eeh_ops->get_state) | ||
1326 | return -ENOENT; | ||
1327 | |||
1328 | result = eeh_ops->get_state(pe, NULL); | ||
1329 | rst_active = !!(result & EEH_STATE_RESET_ACTIVE); | ||
1330 | dma_en = !!(result & EEH_STATE_DMA_ENABLED); | ||
1331 | mmio_en = !!(result & EEH_STATE_MMIO_ENABLED); | ||
1332 | |||
1333 | if (rst_active) | ||
1334 | ret = EEH_PE_STATE_RESET; | ||
1335 | else if (dma_en && mmio_en) | ||
1336 | ret = EEH_PE_STATE_NORMAL; | ||
1337 | else if (!dma_en && !mmio_en) | ||
1338 | ret = EEH_PE_STATE_STOPPED_IO_DMA; | ||
1339 | else if (!dma_en && mmio_en) | ||
1340 | ret = EEH_PE_STATE_STOPPED_DMA; | ||
1341 | else | ||
1342 | ret = EEH_PE_STATE_UNAVAIL; | ||
1343 | |||
1344 | return ret; | ||
1345 | } | ||
1346 | EXPORT_SYMBOL_GPL(eeh_pe_get_state); | ||
1347 | |||
1348 | /** | ||
1349 | * eeh_pe_reset - Issue PE reset according to specified type | ||
1350 | * @pe: EEH PE | ||
1351 | * @option: reset type | ||
1352 | * | ||
1353 | * The routine is called to reset the specified PE with the | ||
1354 | * indicated type, either fundamental reset or hot reset. | ||
1355 | * PE reset is the most important part for error recovery. | ||
1356 | */ | ||
1357 | int eeh_pe_reset(struct eeh_pe *pe, int option) | ||
1358 | { | ||
1359 | int ret = 0; | ||
1360 | |||
1361 | /* Invalid PE ? */ | ||
1362 | if (!pe) | ||
1363 | return -ENODEV; | ||
1364 | |||
1365 | if (!eeh_ops || !eeh_ops->set_option || !eeh_ops->reset) | ||
1366 | return -ENOENT; | ||
1367 | |||
1368 | switch (option) { | ||
1369 | case EEH_RESET_DEACTIVATE: | ||
1370 | ret = eeh_ops->reset(pe, option); | ||
1371 | if (ret) | ||
1372 | break; | ||
1373 | |||
1374 | /* | ||
1375 | * The PE is still in frozen state and we need to clear | ||
1376 | * that. It's good to clear frozen state after deassert | ||
1377 | * to avoid messy IO access during reset, which might | ||
1378 | * cause recursive frozen PE. | ||
1379 | */ | ||
1380 | ret = eeh_ops->set_option(pe, EEH_OPT_THAW_MMIO); | ||
1381 | if (!ret) | ||
1382 | ret = eeh_ops->set_option(pe, EEH_OPT_THAW_DMA); | ||
1383 | if (!ret) | ||
1384 | eeh_pe_state_clear(pe, EEH_PE_ISOLATED); | ||
1385 | break; | ||
1386 | case EEH_RESET_HOT: | ||
1387 | case EEH_RESET_FUNDAMENTAL: | ||
1388 | ret = eeh_ops->reset(pe, option); | ||
1389 | break; | ||
1390 | default: | ||
1391 | pr_debug("%s: Unsupported option %d\n", | ||
1392 | __func__, option); | ||
1393 | ret = -EINVAL; | ||
1394 | } | ||
1395 | |||
1396 | return ret; | ||
1397 | } | ||
1398 | EXPORT_SYMBOL_GPL(eeh_pe_reset); | ||
1399 | |||
1400 | /** | ||
1401 | * eeh_pe_configure - Configure PCI bridges after PE reset | ||
1402 | * @pe: EEH PE | ||
1403 | * | ||
1404 | * The routine is called to restore the PCI config space for | ||
1405 | * those PCI devices, especially PCI bridges affected by PE | ||
1406 | * reset issued previously. | ||
1407 | */ | ||
1408 | int eeh_pe_configure(struct eeh_pe *pe) | ||
1409 | { | ||
1410 | int ret = 0; | ||
1411 | |||
1412 | /* Invalid PE ? */ | ||
1413 | if (!pe) | ||
1414 | return -ENODEV; | ||
1415 | |||
1416 | /* Restore config space for the affected devices */ | ||
1417 | eeh_pe_restore_bars(pe); | ||
1418 | |||
1419 | return ret; | ||
1420 | } | ||
1421 | EXPORT_SYMBOL_GPL(eeh_pe_configure); | ||
1422 | |||
1103 | static int proc_eeh_show(struct seq_file *m, void *v) | 1423 | static int proc_eeh_show(struct seq_file *m, void *v) |
1104 | { | 1424 | { |
1105 | if (!eeh_enabled()) { | 1425 | if (!eeh_enabled()) { |
@@ -1143,9 +1463,9 @@ static const struct file_operations proc_eeh_operations = { | |||
1143 | static int eeh_enable_dbgfs_set(void *data, u64 val) | 1463 | static int eeh_enable_dbgfs_set(void *data, u64 val) |
1144 | { | 1464 | { |
1145 | if (val) | 1465 | if (val) |
1146 | eeh_subsystem_flags &= ~EEH_FORCE_DISABLED; | 1466 | eeh_clear_flag(EEH_FORCE_DISABLED); |
1147 | else | 1467 | else |
1148 | eeh_subsystem_flags |= EEH_FORCE_DISABLED; | 1468 | eeh_add_flag(EEH_FORCE_DISABLED); |
1149 | 1469 | ||
1150 | /* Notify the backend */ | 1470 | /* Notify the backend */ |
1151 | if (eeh_ops->post_init) | 1471 | if (eeh_ops->post_init) |