diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-11-29 13:22:53 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-12-07 02:04:02 -0500 |
commit | cee72d5bb48952f2e50acd2610d52ea82f7092c9 (patch) | |
tree | f4e29eb7e997e29a333750a241c99e282ffda8c8 /arch | |
parent | f11fe5524aa7427ae8f23dbede8a29fc4f613a71 (diff) |
powerpc/powernv: Display diag data on p7ioc EEH errors
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-ioda.c | 25 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-p5ioc2.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.c | 117 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.h | 16 |
4 files changed, 140 insertions, 19 deletions
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 425c2b297945..f31162cfdaa9 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #define DEBUG | 12 | #undef DEBUG |
13 | 13 | ||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/pci.h> | 15 | #include <linux/pci.h> |
@@ -467,14 +467,13 @@ static void __devinit pnv_ioda_update_resources(struct pci_bus *bus) | |||
467 | struct pci_bus *cbus; | 467 | struct pci_bus *cbus; |
468 | struct pci_dev *cdev; | 468 | struct pci_dev *cdev; |
469 | unsigned int i; | 469 | unsigned int i; |
470 | u16 cmd; | ||
471 | 470 | ||
472 | /* Clear all device enables */ | 471 | /* We used to clear all device enables here. However it looks like |
473 | list_for_each_entry(cdev, &bus->devices, bus_list) { | 472 | * clearing MEM enable causes Obsidian (IPR SCS) to go bonkers, |
474 | pci_read_config_word(cdev, PCI_COMMAND, &cmd); | 473 | * and shoot fatal errors to the PHB which in turns fences itself |
475 | cmd &= ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER); | 474 | * and we can't recover from that ... yet. So for now, let's leave |
476 | pci_write_config_word(cdev, PCI_COMMAND, cmd); | 475 | * the enables as-is and hope for the best. |
477 | } | 476 | */ |
478 | 477 | ||
479 | /* Check if bus resources fit in our IO or M32 range */ | 478 | /* Check if bus resources fit in our IO or M32 range */ |
480 | for (i = 0; bus->self && (i < 2); i++) { | 479 | for (i = 0; bus->self && (i < 2); i++) { |
@@ -618,7 +617,7 @@ static int __devinit pnv_ioda_configure_pe(struct pnv_phb *phb, | |||
618 | struct pci_dn *pdn = pnv_ioda_get_pdn(parent); | 617 | struct pci_dn *pdn = pnv_ioda_get_pdn(parent); |
619 | if (pdn && pdn->pe_number != IODA_INVALID_PE) { | 618 | if (pdn && pdn->pe_number != IODA_INVALID_PE) { |
620 | rc = opal_pci_set_peltv(phb->opal_id, pdn->pe_number, | 619 | rc = opal_pci_set_peltv(phb->opal_id, pdn->pe_number, |
621 | pe->pe_number, 1); | 620 | pe->pe_number, OPAL_ADD_PE_TO_DOMAIN); |
622 | /* XXX What to do in case of error ? */ | 621 | /* XXX What to do in case of error ? */ |
623 | } | 622 | } |
624 | parent = parent->bus->self; | 623 | parent = parent->bus->self; |
@@ -638,7 +637,7 @@ static int __devinit pnv_ioda_configure_pe(struct pnv_phb *phb, | |||
638 | pe->mve_number = -1; | 637 | pe->mve_number = -1; |
639 | } else { | 638 | } else { |
640 | rc = opal_pci_set_mve_enable(phb->opal_id, | 639 | rc = opal_pci_set_mve_enable(phb->opal_id, |
641 | pe->mve_number, 1); | 640 | pe->mve_number, OPAL_ENABLE_MVE); |
642 | if (rc) { | 641 | if (rc) { |
643 | pe_err(pe, "OPAL error %ld enabling MVE %d\n", | 642 | pe_err(pe, "OPAL error %ld enabling MVE %d\n", |
644 | rc, pe->mve_number); | 643 | rc, pe->mve_number); |
@@ -1187,6 +1186,12 @@ void __init pnv_pci_init_ioda1_phb(struct device_node *np) | |||
1187 | phb->opal_id = phb_id; | 1186 | phb->opal_id = phb_id; |
1188 | phb->type = PNV_PHB_IODA1; | 1187 | phb->type = PNV_PHB_IODA1; |
1189 | 1188 | ||
1189 | /* Detect specific models for error handling */ | ||
1190 | if (of_device_is_compatible(np, "ibm,p7ioc-pciex")) | ||
1191 | phb->model = PNV_PHB_MODEL_P7IOC; | ||
1192 | else | ||
1193 | phb->model = PNV_PHB_MODEL_UNKNOWN; | ||
1194 | |||
1190 | /* We parse "ranges" now since we need to deduce the register base | 1195 | /* We parse "ranges" now since we need to deduce the register base |
1191 | * from the IO base | 1196 | * from the IO base |
1192 | */ | 1197 | */ |
diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c index 4c80f7c77d56..264967770c3a 100644 --- a/arch/powerpc/platforms/powernv/pci-p5ioc2.c +++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c | |||
@@ -137,6 +137,7 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, | |||
137 | phb->hose->private_data = phb; | 137 | phb->hose->private_data = phb; |
138 | phb->opal_id = phb_id; | 138 | phb->opal_id = phb_id; |
139 | phb->type = PNV_PHB_P5IOC2; | 139 | phb->type = PNV_PHB_P5IOC2; |
140 | phb->model = PNV_PHB_MODEL_P5IOC2; | ||
140 | 141 | ||
141 | phb->regs = of_iomap(np, 0); | 142 | phb->regs = of_iomap(np, 0); |
142 | 143 | ||
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index c0ed379498a0..a70bc1e385eb 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c | |||
@@ -144,6 +144,112 @@ static void pnv_teardown_msi_irqs(struct pci_dev *pdev) | |||
144 | } | 144 | } |
145 | #endif /* CONFIG_PCI_MSI */ | 145 | #endif /* CONFIG_PCI_MSI */ |
146 | 146 | ||
147 | static void pnv_pci_dump_p7ioc_diag_data(struct pnv_phb *phb) | ||
148 | { | ||
149 | struct OpalIoP7IOCPhbErrorData *data = &phb->diag.p7ioc; | ||
150 | int i; | ||
151 | |||
152 | pr_info("PHB %d diagnostic data:\n", phb->hose->global_number); | ||
153 | |||
154 | pr_info(" brdgCtl = 0x%08x\n", data->brdgCtl); | ||
155 | |||
156 | pr_info(" portStatusReg = 0x%08x\n", data->portStatusReg); | ||
157 | pr_info(" rootCmplxStatus = 0x%08x\n", data->rootCmplxStatus); | ||
158 | pr_info(" busAgentStatus = 0x%08x\n", data->busAgentStatus); | ||
159 | |||
160 | pr_info(" deviceStatus = 0x%08x\n", data->deviceStatus); | ||
161 | pr_info(" slotStatus = 0x%08x\n", data->slotStatus); | ||
162 | pr_info(" linkStatus = 0x%08x\n", data->linkStatus); | ||
163 | pr_info(" devCmdStatus = 0x%08x\n", data->devCmdStatus); | ||
164 | pr_info(" devSecStatus = 0x%08x\n", data->devSecStatus); | ||
165 | |||
166 | pr_info(" rootErrorStatus = 0x%08x\n", data->rootErrorStatus); | ||
167 | pr_info(" uncorrErrorStatus = 0x%08x\n", data->uncorrErrorStatus); | ||
168 | pr_info(" corrErrorStatus = 0x%08x\n", data->corrErrorStatus); | ||
169 | pr_info(" tlpHdr1 = 0x%08x\n", data->tlpHdr1); | ||
170 | pr_info(" tlpHdr2 = 0x%08x\n", data->tlpHdr2); | ||
171 | pr_info(" tlpHdr3 = 0x%08x\n", data->tlpHdr3); | ||
172 | pr_info(" tlpHdr4 = 0x%08x\n", data->tlpHdr4); | ||
173 | pr_info(" sourceId = 0x%08x\n", data->sourceId); | ||
174 | |||
175 | pr_info(" errorClass = 0x%016llx\n", data->errorClass); | ||
176 | pr_info(" correlator = 0x%016llx\n", data->correlator); | ||
177 | |||
178 | pr_info(" p7iocPlssr = 0x%016llx\n", data->p7iocPlssr); | ||
179 | pr_info(" p7iocCsr = 0x%016llx\n", data->p7iocCsr); | ||
180 | pr_info(" lemFir = 0x%016llx\n", data->lemFir); | ||
181 | pr_info(" lemErrorMask = 0x%016llx\n", data->lemErrorMask); | ||
182 | pr_info(" lemWOF = 0x%016llx\n", data->lemWOF); | ||
183 | pr_info(" phbErrorStatus = 0x%016llx\n", data->phbErrorStatus); | ||
184 | pr_info(" phbFirstErrorStatus = 0x%016llx\n", data->phbFirstErrorStatus); | ||
185 | pr_info(" phbErrorLog0 = 0x%016llx\n", data->phbErrorLog0); | ||
186 | pr_info(" phbErrorLog1 = 0x%016llx\n", data->phbErrorLog1); | ||
187 | pr_info(" mmioErrorStatus = 0x%016llx\n", data->mmioErrorStatus); | ||
188 | pr_info(" mmioFirstErrorStatus = 0x%016llx\n", data->mmioFirstErrorStatus); | ||
189 | pr_info(" mmioErrorLog0 = 0x%016llx\n", data->mmioErrorLog0); | ||
190 | pr_info(" mmioErrorLog1 = 0x%016llx\n", data->mmioErrorLog1); | ||
191 | pr_info(" dma0ErrorStatus = 0x%016llx\n", data->dma0ErrorStatus); | ||
192 | pr_info(" dma0FirstErrorStatus = 0x%016llx\n", data->dma0FirstErrorStatus); | ||
193 | pr_info(" dma0ErrorLog0 = 0x%016llx\n", data->dma0ErrorLog0); | ||
194 | pr_info(" dma0ErrorLog1 = 0x%016llx\n", data->dma0ErrorLog1); | ||
195 | pr_info(" dma1ErrorStatus = 0x%016llx\n", data->dma1ErrorStatus); | ||
196 | pr_info(" dma1FirstErrorStatus = 0x%016llx\n", data->dma1FirstErrorStatus); | ||
197 | pr_info(" dma1ErrorLog0 = 0x%016llx\n", data->dma1ErrorLog0); | ||
198 | pr_info(" dma1ErrorLog1 = 0x%016llx\n", data->dma1ErrorLog1); | ||
199 | |||
200 | for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) { | ||
201 | if ((data->pestA[i] >> 63) == 0 && | ||
202 | (data->pestB[i] >> 63) == 0) | ||
203 | continue; | ||
204 | pr_info(" PE[%3d] PESTA = 0x%016llx\n", i, data->pestA[i]); | ||
205 | pr_info(" PESTB = 0x%016llx\n", data->pestB[i]); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static void pnv_pci_dump_phb_diag_data(struct pnv_phb *phb) | ||
210 | { | ||
211 | switch(phb->model) { | ||
212 | case PNV_PHB_MODEL_P7IOC: | ||
213 | pnv_pci_dump_p7ioc_diag_data(phb); | ||
214 | break; | ||
215 | default: | ||
216 | pr_warning("PCI %d: Can't decode this PHB diag data\n", | ||
217 | phb->hose->global_number); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static void pnv_pci_handle_eeh_config(struct pnv_phb *phb, u32 pe_no) | ||
222 | { | ||
223 | unsigned long flags, rc; | ||
224 | int has_diag; | ||
225 | |||
226 | spin_lock_irqsave(&phb->lock, flags); | ||
227 | |||
228 | rc = opal_pci_get_phb_diag_data(phb->opal_id, phb->diag.blob, PNV_PCI_DIAG_BUF_SIZE); | ||
229 | has_diag = (rc == OPAL_SUCCESS); | ||
230 | |||
231 | rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no, | ||
232 | OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); | ||
233 | if (rc) { | ||
234 | pr_warning("PCI %d: Failed to clear EEH freeze state" | ||
235 | " for PE#%d, err %ld\n", | ||
236 | phb->hose->global_number, pe_no, rc); | ||
237 | |||
238 | /* For now, let's only display the diag buffer when we fail to clear | ||
239 | * the EEH status. We'll do more sensible things later when we have | ||
240 | * proper EEH support. We need to make sure we don't pollute ourselves | ||
241 | * with the normal errors generated when probing empty slots | ||
242 | */ | ||
243 | if (has_diag) | ||
244 | pnv_pci_dump_phb_diag_data(phb); | ||
245 | else | ||
246 | pr_warning("PCI %d: No diag data available\n", | ||
247 | phb->hose->global_number); | ||
248 | } | ||
249 | |||
250 | spin_unlock_irqrestore(&phb->lock, flags); | ||
251 | } | ||
252 | |||
147 | static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, | 253 | static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, |
148 | u32 bdfn) | 254 | u32 bdfn) |
149 | { | 255 | { |
@@ -165,15 +271,8 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, | |||
165 | } | 271 | } |
166 | cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n", | 272 | cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n", |
167 | bdfn, pe_no, fstate); | 273 | bdfn, pe_no, fstate); |
168 | if (fstate != 0) { | 274 | if (fstate != 0) |
169 | rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no, | 275 | pnv_pci_handle_eeh_config(phb, pe_no); |
170 | OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); | ||
171 | if (rc) { | ||
172 | pr_warning("PCI %d: Failed to clear EEH freeze state" | ||
173 | " for PE#%d, err %lld\n", | ||
174 | phb->hose->global_number, pe_no, rc); | ||
175 | } | ||
176 | } | ||
177 | } | 276 | } |
178 | 277 | ||
179 | static int pnv_pci_read_config(struct pci_bus *bus, | 278 | static int pnv_pci_read_config(struct pci_bus *bus, |
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 28ae4ca512c4..8bc479634643 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h | |||
@@ -9,6 +9,15 @@ enum pnv_phb_type { | |||
9 | PNV_PHB_IODA2, | 9 | PNV_PHB_IODA2, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | /* Precise PHB model for error management */ | ||
13 | enum pnv_phb_model { | ||
14 | PNV_PHB_MODEL_UNKNOWN, | ||
15 | PNV_PHB_MODEL_P5IOC2, | ||
16 | PNV_PHB_MODEL_P7IOC, | ||
17 | }; | ||
18 | |||
19 | #define PNV_PCI_DIAG_BUF_SIZE 4096 | ||
20 | |||
12 | /* Data associated with a PE, including IOMMU tracking etc.. */ | 21 | /* Data associated with a PE, including IOMMU tracking etc.. */ |
13 | struct pnv_ioda_pe { | 22 | struct pnv_ioda_pe { |
14 | /* A PE can be associated with a single device or an | 23 | /* A PE can be associated with a single device or an |
@@ -56,6 +65,7 @@ struct pnv_ioda_pe { | |||
56 | struct pnv_phb { | 65 | struct pnv_phb { |
57 | struct pci_controller *hose; | 66 | struct pci_controller *hose; |
58 | enum pnv_phb_type type; | 67 | enum pnv_phb_type type; |
68 | enum pnv_phb_model model; | ||
59 | u64 opal_id; | 69 | u64 opal_id; |
60 | void __iomem *regs; | 70 | void __iomem *regs; |
61 | spinlock_t lock; | 71 | spinlock_t lock; |
@@ -118,6 +128,12 @@ struct pnv_phb { | |||
118 | struct list_head pe_list; | 128 | struct list_head pe_list; |
119 | } ioda; | 129 | } ioda; |
120 | }; | 130 | }; |
131 | |||
132 | /* PHB status structure */ | ||
133 | union { | ||
134 | unsigned char blob[PNV_PCI_DIAG_BUF_SIZE]; | ||
135 | struct OpalIoP7IOCPhbErrorData p7ioc; | ||
136 | } diag; | ||
121 | }; | 137 | }; |
122 | 138 | ||
123 | extern struct pci_ops pnv_pci_ops; | 139 | extern struct pci_ops pnv_pci_ops; |