Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: res_fire_pcie.c | |
5 | * | |
6 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
7 | * | |
8 | * - Do no alter or remove copyright notices | |
9 | * | |
10 | * - Redistribution and use of this software in source and binary forms, with | |
11 | * or without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistribution of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * | |
17 | * - Redistribution in binary form must reproduce the above copyright notice, | |
18 | * this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * | |
21 | * Neither the name of Sun Microsystems, Inc. or the names of contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * This software is provided "AS IS," without a warranty of any kind. | |
26 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
27 | * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
28 | * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
29 | * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
30 | * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
31 | * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
32 | * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
33 | * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
34 | * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
35 | * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
36 | * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
37 | * | |
38 | * You acknowledge that this software is not designed, licensed or | |
39 | * intended for use in the design, construction, operation or maintenance of | |
40 | * any nuclear facility. | |
41 | * | |
42 | * ========== Copyright Header End ============================================ | |
43 | */ | |
44 | /* | |
45 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
46 | * Use is subject to license terms. | |
47 | */ | |
48 | ||
49 | /* | |
50 | * (re)-configuration code to handle HV Fire PCI-E resources | |
51 | */ | |
52 | ||
53 | #pragma ident "@(#)res_fire_pcie.c 1.5 07/06/07 SMI" | |
54 | ||
55 | #include <stdarg.h> | |
56 | #include <sys/htypes.h> | |
57 | #include <hypervisor.h> | |
58 | #include <traps.h> | |
59 | #include <cache.h> | |
60 | #include <mmu.h> | |
61 | #include <sun4v/asi.h> | |
62 | #include <sun4v/errs_defs.h> | |
63 | #include <cpu_errs_defs.h> | |
64 | #include <cpu_errs.h> | |
65 | #include <vpci_errs_defs.h> | |
66 | #include <vdev_intr.h> | |
67 | #include <ncs.h> | |
68 | #include <cyclic.h> | |
69 | #include <support.h> | |
70 | #include <strand.h> | |
71 | #include <vcpu.h> | |
72 | #include <guest.h> | |
73 | #include <pcie.h> | |
74 | #include <vdev_ops.h> | |
75 | #include <fpga.h> | |
76 | #include <ldc.h> | |
77 | #include <config.h> | |
78 | #include <offsets.h> | |
79 | #include <hvctl.h> | |
80 | #include <md.h> | |
81 | #include <abort.h> | |
82 | #include <hypervisor.h> | |
83 | #include <proto.h> | |
84 | #include <fire.h> | |
85 | ||
86 | #ifdef CONFIG_PCIE | |
87 | ||
88 | #define PCIE_LEAF_A FIRE_LEAF(A) | |
89 | #define PCIE_LEAF_B FIRE_LEAF(B) | |
90 | ||
91 | #define MAX_RESETS 10 | |
92 | ||
93 | ||
94 | /* | |
95 | * resource processing support | |
96 | */ | |
97 | void config_a_guest_pcie_bus(pcie_device_t *pciep); | |
98 | void unconfig_a_guest_pcie_bus(pcie_device_t *pciep); | |
99 | ||
100 | void c_fire_leaf_soft_reset(const struct fire_cookie *, int root); | |
101 | ||
102 | void | |
103 | reset_platform_pcie_busses(guest_t *guestp, pcie_device_t *pciep) | |
104 | { | |
105 | extern const struct fire_cookie fire_dev[]; | |
106 | int i; | |
107 | ||
108 | for (i = 0; i < NUM_PCIE_BUSSES; i++) { | |
109 | ||
110 | DBG(c_printf("pcie 0x%x assigned to guest 0x%x\n", | |
111 | &pciep[i], pciep[i].guestp)); | |
112 | ||
113 | /* if bus is assigned to this guest, soft reset the bus */ | |
114 | if (pciep[i].guestp == guestp) { | |
115 | DBG(c_printf("Soft Reset PCI leaf 0x%x\n", i)); | |
116 | ||
117 | /* we dont care if this works really - if it fails */ | |
118 | /* the bus is likely dead. The reset on guest restart */ | |
119 | /* will likely set it up to function again ... */ | |
120 | (void) pcie_bus_reset(i); | |
121 | ||
122 | /* regardless of the bus reset we shutdown Fire's */ | |
123 | /* IOMMU and interrupt logic for this bus */ | |
124 | c_fire_leaf_soft_reset( | |
125 | (struct fire_cookie *)&fire_dev[i], i); | |
126 | } | |
127 | } | |
128 | } | |
129 | ||
130 | ||
131 | void | |
132 | fire_map_tte(fire_dev_t *firep, uint64_t idx, uint64_t pa) | |
133 | { | |
134 | uint64_t *ttep; | |
135 | ||
136 | ttep = &firep->iotsb[idx]; | |
137 | *ttep = FIRE_IO_TTE(pa); | |
138 | } | |
139 | ||
140 | void | |
141 | fire_set_eqbase(fire_dev_t *firep, bool_t bypass) | |
142 | { | |
143 | volatile uint64_t *basep; | |
144 | uint64_t addr; | |
145 | ||
146 | basep = (uint64_t *)(firep->pcie + FIRE_DLC_IMU_EQS_EQ_BASE_ADDRESS); | |
147 | ||
148 | if (bypass) | |
149 | addr = (uint64_t)firep->msieqbase | MSI_EQ_BASE_BYPASS_ADDR; | |
150 | else | |
151 | addr = firep->guest_pci_va_limit; | |
152 | ||
153 | DBGEQ(c_printf("Setting eq base 0x%x, addr 0x%x\n", basep, addr)); | |
154 | ||
155 | *basep = addr; | |
156 | ||
157 | } | |
158 | ||
159 | void | |
160 | init_fire_msi_eq(int busnum) | |
161 | { | |
162 | fire_dev_t *firep; | |
163 | uint64_t max_virtaddr, base_virtaddr; | |
164 | uint64_t offset_tte_idx, nttes; | |
165 | int i; | |
166 | ||
167 | DBGEQ(c_printf("Initializing MSI EQ mappings for bus 0x%x\n", busnum)); | |
168 | ||
169 | ASSERT(busnum >= 0 && busnum < NFIRELEAVES); | |
170 | ||
171 | firep = (fire_dev_t *)&(fire_dev[busnum]); | |
172 | ||
173 | max_virtaddr = FIRE_DVMA_RANGE_MAX - IOMMU_EQ_RESERVE; | |
174 | base_virtaddr = FIRE_DVMA_RANGE_MAX - IOMMU_SPACE; | |
175 | ||
176 | firep->guest_pci_va_limit = max_virtaddr; | |
177 | ||
178 | DBGEQ(c_printf("Basevirtaddr 0x%x, max_virtaddr 0x%x, reserve 0x%x\n", | |
179 | base_virtaddr, max_virtaddr, IOMMU_EQ_RESERVE)); | |
180 | ||
181 | DBGEQ(c_printf("Guest idx max 0x%x\n", IOTSB_INDEX_MAX)); | |
182 | ||
183 | offset_tte_idx = (max_virtaddr - base_virtaddr) >> IOMMU_PAGESHIFT; | |
184 | nttes = EQ_MAX_SIZE >> IOMMU_PAGESHIFT; | |
185 | ||
186 | DBGEQ(c_printf("Offset idx 0x%x, nttes 0x%x\n", | |
187 | offset_tte_idx, nttes)); | |
188 | ||
189 | ASSERT((offset_tte_idx + nttes) <= FIRE_IOMMU_SIZE(FIRE_TSB_SIZE)); | |
190 | ||
191 | for (i = 0; i < nttes; i++) { | |
192 | uint64_t pa; | |
193 | ||
194 | pa = (uint64_t)firep->msieqbase + | |
195 | ((uint64_t)i << IOMMU_PAGESHIFT); | |
196 | ||
197 | fire_map_tte(firep, (uint64_t)(offset_tte_idx + i), pa); | |
198 | } | |
199 | } | |
200 | ||
201 | void | |
202 | init_pcie_buses(void) | |
203 | { | |
204 | DBGEQ(c_printf("initializing pcie buses\n")); | |
205 | init_fire_msi_eq(PCIE_LEAF_A); | |
206 | init_fire_msi_eq(PCIE_LEAF_B); | |
207 | } | |
208 | ||
209 | void | |
210 | plat_config_pcie_bypass(pcie_device_t *pciep) | |
211 | { | |
212 | int id; | |
213 | fire_dev_t *firep; | |
214 | ||
215 | id = pciep->id; | |
216 | firep = (fire_dev_t *)&fire_dev[id]; | |
217 | ||
218 | fire_config_bypass(firep, pciep->allow_bypass); | |
219 | ||
220 | fire_set_eqbase(firep, pciep->allow_bypass); | |
221 | } | |
222 | ||
223 | #ifdef CONFIG_IOBYPASS | |
224 | /* | |
225 | * For a guest which is allowed direct access to the I/O bridges (Tomatillo, | |
226 | * Fire), this sets up the segments for the I/O physical addresses. | |
227 | */ | |
228 | #define AID_TO_PCIE_LEAF_A_BASE 0x800e000000 | |
229 | #define AID_TO_PCIE_LEAF_A_LIMIT 0x8010000000 | |
230 | #define AID_TO_PCIE_LEAF_B_BASE 0xc000000000 | |
231 | #define AID_TO_PCIE_LEAF_B_LIMIT 0xff00000000 | |
232 | ||
233 | #define ASSIGN_LEAFA_SEGMENTS(_guestp) \ | |
234 | assign_ra2pa_segments(_guestp, AID_TO_PCIE_LEAF_A_BASE, \ | |
235 | AID_TO_PCIE_LEAF_A_LIMIT - AID_TO_PCIE_LEAF_A_BASE, \ | |
236 | 0, IO_SEGMENT); | |
237 | ||
238 | #define ASSIGN_LEAFB_SEGMENTS(_guestp) \ | |
239 | assign_ra2pa_segments(_guestp, AID_TO_PCIE_LEAF_B_BASE, \ | |
240 | AID_TO_PCIE_LEAF_B_LIMIT - AID_TO_PCIE_LEAF_B_BASE, \ | |
241 | 0, IO_SEGMENT); | |
242 | ||
243 | #define UNASSIGN_LEAFA_SEGMENTS(_guestp) \ | |
244 | assign_ra2pa_segments(guestp, AID_TO_PCIE_LEAF_A_BASE, \ | |
245 | INVALID_SEGMENT_SIZE, 0, INVALID_SEGMENT); | |
246 | ||
247 | #define UNASSIGN_LEAFB_SEGMENTS(_guestp) \ | |
248 | assign_ra2pa_segments(guestp, AID_TO_PCIE_LEAF_B_BASE, \ | |
249 | INVALID_SEGMENT_SIZE, 0, INVALID_SEGMENT); | |
250 | #else | |
251 | ||
252 | #define FIRE_IOBASE_A 0xe810000000 | |
253 | #define FIRE_IOLIMIT_A 0xf000000000 | |
254 | ||
255 | #define FIRE_IOBASE_B 0xf010000000 | |
256 | #define FIRE_IOLIMIT_B 0xf800000000 | |
257 | ||
258 | #define FIRE_EBUS_BASE 0xf820000000 | |
259 | #define FIRE_EBUS_LIMIT 0xf828000000 | |
260 | ||
261 | #define ASSIGN_LEAFA_SEGMENTS(_guestp) \ | |
262 | assign_ra2pa_segments(_guestp, FIRE_IOBASE_A, \ | |
263 | FIRE_IOLIMIT_A - FIRE_IOBASE_A, 0, IO_SEGMENT); \ | |
264 | assign_ra2pa_segments(_guestp, FIRE_EBUS_BASE, \ | |
265 | (FIRE_EBUS_LIMIT - FIRE_EBUS_BASE), 0, IO_SEGMENT); | |
266 | ||
267 | #define ASSIGN_LEAFB_SEGMENTS(_guestp) \ | |
268 | assign_ra2pa_segments(_guestp, FIRE_IOBASE_B, \ | |
269 | FIRE_IOLIMIT_B - FIRE_IOBASE_B, 0, IO_SEGMENT); | |
270 | ||
271 | #define UNASSIGN_LEAFA_SEGMENTS(_guestp) \ | |
272 | assign_ra2pa_segments(_guestp, FIRE_IOBASE_A, \ | |
273 | INVALID_SEGMENT_SIZE, 0, INVALID_SEGMENT); \ | |
274 | assign_ra2pa_segments(_guestp, FIRE_EBUS_BASE, \ | |
275 | INVALID_SEGMENT_SIZE, 0, INVALID_SEGMENT); | |
276 | ||
277 | #define UNASSIGN_LEAFB_SEGMENTS(_guestp) \ | |
278 | assign_ra2pa_segments(_guestp, FIRE_IOBASE_B, \ | |
279 | INVALID_SEGMENT_SIZE, 0, INVALID_SEGMENT); | |
280 | ||
281 | #endif /* !CONFIG_IOBYPASS */ | |
282 | ||
283 | void | |
284 | config_a_guest_pcie_bus(pcie_device_t *pciep) | |
285 | { | |
286 | guest_t *guestp; | |
287 | int id, vinobase, x; | |
288 | uint8_t devid; | |
289 | ||
290 | guestp = pciep->guestp; | |
291 | id = pciep->id; | |
292 | ||
293 | ASSERT(guestp != NULL); | |
294 | ||
295 | devid = (pciep->cfg_handle) >> DEVCFGPA_SHIFT; | |
296 | vinobase = pciep->cfg_handle; | |
297 | ||
298 | plat_config_pcie_bypass(pciep); | |
299 | ||
300 | switch (id) { | |
301 | case PCIE_LEAF_A: | |
302 | guestp->dev2inst[devid] = DEVOPS_FIRE_A; | |
303 | ASSIGN_LEAFA_SEGMENTS(guestp); | |
304 | DBG(c_printf("\tNOTICE pcie LEAF 0x%x " \ | |
305 | "configured for guest 0x%x\n", id, | |
306 | guestp->guestid)); | |
307 | break; | |
308 | case PCIE_LEAF_B: | |
309 | guestp->dev2inst[devid] = DEVOPS_FIRE_B; | |
310 | ASSIGN_LEAFB_SEGMENTS(guestp); | |
311 | DBG(c_printf("\tNOTICE pcie LEAF 0x%x " \ | |
312 | "configured for guest 0x%x\n", id, | |
313 | guestp->guestid)); | |
314 | break; | |
315 | default: | |
316 | DBG(c_printf("\tWARNING pcie 0x%x not " | |
317 | "supported for guest 0x%x\n", id, guestp->guestid)); | |
318 | /* should probably panic here */ | |
319 | break; | |
320 | } | |
321 | ||
322 | for (x = 0; x < NINOSPERDEV; x++) { | |
323 | guestp->vino2inst.vino[vinobase + x] = | |
324 | config_pcie_vinos.vino[id][x]; | |
325 | } | |
326 | } | |
327 | ||
328 | void | |
329 | unconfig_a_guest_pcie_bus(pcie_device_t *pciep) | |
330 | { | |
331 | guest_t *guestp; | |
332 | int id, vinobase, x; | |
333 | uint8_t devid; | |
334 | ||
335 | guestp = pciep->guestp; | |
336 | id = pciep->id; | |
337 | ||
338 | ASSERT(guestp != NULL); | |
339 | ||
340 | devid = (pciep->cfg_handle) >> DEVCFGPA_SHIFT; | |
341 | vinobase = pciep->cfg_handle; | |
342 | ||
343 | guestp->dev2inst[devid] = DEVOPS_RESERVED; | |
344 | for (x = 0; x < NINOSPERDEV; x++) { | |
345 | guestp->vino2inst.vino[vinobase + x] = DEVOPS_RESERVED; | |
346 | } | |
347 | ||
348 | /* | |
349 | * We clear out the PCI I/O address segments by setting their size | |
350 | * to INVALID_SEGMENT_SIZE and flags to INVALID_SEGMENT. | |
351 | */ | |
352 | switch (id) { | |
353 | case (PCIE_LEAF_A): | |
354 | UNASSIGN_LEAFA_SEGMENTS(guestp); | |
355 | break; | |
356 | case (PCIE_LEAF_B): | |
357 | UNASSIGN_LEAFB_SEGMENTS(guestp); | |
358 | break; | |
359 | default: | |
360 | break; | |
361 | } | |
362 | } | |
363 | ||
364 | ||
365 | ||
366 | #define CFGWR(_offset, _size, _val) \ | |
367 | do { \ | |
368 | if (!pci_config_put(firep, (_offset), _size, _val)) { \ | |
369 | DBGBR(c_printf("CFGWR fail "#_offset \ | |
370 | " ("#_size") <- "#_val"\n")); \ | |
371 | return (false); \ | |
372 | } \ | |
373 | } while (0) | |
374 | ||
375 | static bool_t plx_8532_aa_reset(int busnum); | |
376 | static bool_t plx_upstream_port_reset(fire_dev_t *firep); | |
377 | static bool_t is_plx_downstream_port_ready(fire_dev_t *firep, int port); | |
378 | static bool_t plx_set_elec_idle(int busnum, int port, bool_t on); | |
379 | #define NUM_PLX_PORTS 16 | |
380 | static void plx_port_check(fire_dev_t *firep, int port); | |
381 | static void config_fire_pcie_bus(int busnum); | |
382 | ||
383 | static bool_t ontario_southbridge_reset(fire_dev_t *firep); | |
384 | ||
385 | ||
386 | /* | |
387 | * This function does an initial test of the PCI-E links attached to | |
388 | * Fire - primarily to determine if we have any PLX 8532 AA switches | |
389 | * directly attached ... if so, we need to determine which ports are | |
390 | * live so that we can guarantee a re-train in the event of a reset | |
391 | */ | |
392 | ||
393 | void | |
394 | config_platform_pcie() | |
395 | { | |
396 | config_fire_pcie_bus(0); | |
397 | config_fire_pcie_bus(1); | |
398 | } | |
399 | ||
400 | ||
401 | /* | |
402 | * This function examines the specified PCI-E bus on fire, and looks | |
403 | * for PLX 8532 AA switches ... and determines which downstream ports on | |
404 | * those switches are live. | |
405 | */ | |
406 | ||
407 | void | |
408 | config_fire_pcie_bus(int busnum) | |
409 | { | |
410 | uint64_t res; | |
411 | fire_dev_t *firep; | |
412 | ||
413 | firep = (fire_dev_t *)&fire_dev[busnum]; | |
414 | ||
415 | /* Becomes true after bus is used, false after it is reset */ | |
416 | firep->needs_warm_reset = false; | |
417 | firep->blacklist = false; | |
418 | ||
419 | DBGBR(c_printf("Probing Fire bus %d\n", busnum)); | |
420 | ||
421 | /* | |
422 | * If the link is down, we don't do anything | |
423 | * since it may mean there is no HW on the bus. | |
424 | */ | |
425 | ||
426 | /* | |
427 | * Just bail if nothing connected to the bus - Erie ? | |
428 | * We indicate a failure on the bus in this case to ensure | |
429 | * that we mark the bus to be ignored. Even though the link | |
430 | * status is checked on each config write, we try to prevent the | |
431 | * case where a link is down here and comes up later ... without | |
432 | * the bus being properly reset. By returning a failure here we hope | |
433 | * that the HV marks the bus as dead and prevents further | |
434 | * guest accesses. | |
435 | */ | |
436 | if (!is_fire_port_link_up(firep)) | |
437 | return; | |
438 | ||
439 | /* | |
440 | * If we are dealing with something other than | |
441 | * a PLX or bridge (network card, for example) | |
442 | * we simply toggle the link down and up. | |
443 | * Otherwise we have to do the full secondary | |
444 | * reset technique | |
445 | */ | |
446 | ||
447 | /* | |
448 | * We look for a bridge under fire. | |
449 | * If the read fails, we have a problem ... abort for now, but | |
450 | * need to handle this as simply the bus being inactive. | |
451 | * We may need a fire reset to resolve this - i.e. power cycle. | |
452 | */ | |
453 | ||
454 | if (!pci_config_get(firep, UPST_CFG_BASE + CFG_CLASS_CODE, 4, &res)) { | |
455 | DBGBR(c_printf("Bridge read failed ... abandoning bus\n")); | |
456 | return; | |
457 | } | |
458 | ||
459 | DBGBR(c_printf("Bridge class code 0x%x\n", res)); | |
460 | ||
461 | /* | |
462 | * If the device class code indicates there is no bridge, then we | |
463 | * simply stop here since we're not going to be doing a | |
464 | * secondary reset. | |
465 | */ | |
466 | ||
467 | if ((res>>8) != BRIDGE_CLASS_CODE) { | |
468 | DBGBR(c_printf("Not a bridge ...")); | |
469 | return; | |
470 | } | |
471 | ||
472 | /* | |
473 | * We discovered a bridge, but is it a PLX ? indicating | |
474 | * ... if this read fails something bad | |
475 | * has happened given that we already read from the device. | |
476 | * In this case we abandon the bus ! | |
477 | */ | |
478 | ||
479 | if (!pci_config_get(firep, UPST_CFG_BASE + 0, 4, &res)) { | |
480 | DBGBR(c_printf("Bridge vendor ID read failed !!\n")); | |
481 | return; | |
482 | } | |
483 | ||
484 | DBGBR(c_printf("1st bridge vendorid = 0x%x\n", res)); | |
485 | ||
486 | if (res != PLX_8532_DEV_VEND_ID) | |
487 | return; | |
488 | ||
489 | /* | |
490 | * Scan the downstream ports on the PLX parts to "discover" | |
491 | * which ones are live. | |
492 | */ | |
493 | ||
494 | firep->live_port = 0; | |
495 | ||
496 | switch (busnum) { | |
497 | case 0: | |
498 | plx_port_check(firep, 1); | |
499 | plx_port_check(firep, 8); | |
500 | break; | |
501 | case 1: | |
502 | plx_port_check(firep, 1); | |
503 | plx_port_check(firep, 2); | |
504 | plx_port_check(firep, 8); | |
505 | plx_port_check(firep, 9); | |
506 | break; | |
507 | } | |
508 | ||
509 | DBGBR(c_printf("Done probing bus 0x%x\n", busnum)); | |
510 | } | |
511 | ||
512 | ||
513 | void | |
514 | plx_port_check(fire_dev_t *firep, int port) | |
515 | { | |
516 | if (is_plx_downstream_port_ready(firep, port)) { | |
517 | DBGBR(c_printf("Leaf %d has port %d\n", | |
518 | firep - (fire_dev_t *)&(fire_dev[0]), port)); | |
519 | firep->live_port |= 1<<port; | |
520 | } | |
521 | } | |
522 | ||
523 | ||
524 | bool_t | |
525 | toggle_fire_link(fire_dev_t *firep) | |
526 | { | |
527 | DBGBR(c_printf("...toggle link\n")); | |
528 | ||
529 | if (!fire_link_down(firep)) | |
530 | return (false); | |
531 | ||
532 | c_usleep(500000); | |
533 | ||
534 | if (!fire_link_up(firep)) | |
535 | return (false); | |
536 | ||
537 | return (true); | |
538 | } | |
539 | ||
540 | /* | |
541 | * This function resets a given Fire leaf | |
542 | * | |
543 | * (It should be based on rsetting a given pci-e resource). | |
544 | * | |
545 | * Inputs: | |
546 | * | |
547 | * bus - root complex (0 = leaf A, 1 = leaf B) | |
548 | * | |
549 | * Returns true on success, false on failure. | |
550 | * Caller should mark bus unusable after failure. | |
551 | */ | |
552 | ||
553 | ||
554 | bool_t | |
555 | pcie_bus_reset(int busnum) | |
556 | { | |
557 | uint64_t res, brcode; | |
558 | fire_dev_t *firep; | |
559 | ||
560 | firep = (fire_dev_t *)&fire_dev[busnum]; | |
561 | ||
562 | /* | |
563 | * If the bus just came out of a power on cycle | |
564 | * then we don't need to reset it again here. | |
565 | */ | |
566 | if (!firep->needs_warm_reset) { | |
567 | firep->needs_warm_reset = true; | |
568 | return (true); | |
569 | } | |
570 | ||
571 | if (firep->blacklist) | |
572 | return (false); | |
573 | ||
574 | DBGBR(c_printf("Resetting bus %d (firep=0x%x)\n", | |
575 | busnum, (uint64_t)firep)); | |
576 | ||
577 | /* | |
578 | * If the link is down, we don't do anything | |
579 | * since it may mean there is no HW on the bus. | |
580 | */ | |
581 | ||
582 | /* | |
583 | * Just bail if nothing connected to the bus - Erie ? | |
584 | * We indicate a failure on the bus in this case to ensure | |
585 | * that we mark the bus to be ignored. Even though the link | |
586 | * status is checked on each config write, we try to prevent the | |
587 | * case where a link is down here and comes up later ... without | |
588 | * the bus being properly reset. By returning a failure here we hope | |
589 | * that the HV marks the bus as dead and prevents further | |
590 | * guest accesses. | |
591 | */ | |
592 | if (!is_fire_port_link_up(firep)) { | |
593 | goto fail; | |
594 | } | |
595 | ||
596 | /* | |
597 | * If we are dealing with something other than | |
598 | * a bridge (network card, for example) | |
599 | * we simply toggle the link down and up. | |
600 | * Otherwise we have to do the full secondary | |
601 | * reset technique | |
602 | */ | |
603 | ||
604 | /* | |
605 | * We look for a bridge under fire. | |
606 | * If the read fails, we have a problem ... abort for now, but | |
607 | * need to handle this as simply the bus being inactive. | |
608 | * We may need a fire reset to resolve this - i.e. power cycle. | |
609 | */ | |
610 | ||
611 | if (!pci_config_get(firep, UPST_CFG_BASE + CFG_CLASS_CODE, | |
612 | 4, &brcode)) { | |
613 | DBGBR(c_printf("Bridge read failed ... abandoning bus\n")); | |
614 | goto fail; | |
615 | } | |
616 | ||
617 | DBGBR(c_printf("Bridge class code 0x%x\n", brcode)); | |
618 | ||
619 | /* | |
620 | * If the device class code indicates there is no bridge, then we | |
621 | * simply reset the link by toggling it | |
622 | */ | |
623 | ||
624 | if ((brcode >> 8) != BRIDGE_CLASS_CODE) { | |
625 | DBGBR(c_printf("Not a bridge ...")); | |
626 | goto link_down_up; | |
627 | } | |
628 | ||
629 | /* | |
630 | * We discovered a bridge, determine if it is a | |
631 | * PLX 8532 also on Ontario we need to reset the south bridge. | |
632 | * If the bridge is a PLX 8532 AA part we do a secondary bus reset. | |
633 | * On other bridges and versions of PLX 8532 we just toggle | |
634 | * the fire link. | |
635 | */ | |
636 | ||
637 | if (!pci_config_get(firep, UPST_CFG_BASE + 0, 4, &res)) { | |
638 | DBGBR(c_printf("Bridge vendor ID read failed !!\n")); | |
639 | goto fail; | |
640 | } | |
641 | ||
642 | DBGBR(c_printf("1st bridge vendorid = 0x%x\n", res)); | |
643 | ||
644 | switch (res) { | |
645 | ||
646 | case PLX_8532_DEV_VEND_ID : | |
647 | ||
648 | /* | |
649 | * Reset Southbridge if found. | |
650 | */ | |
651 | if ((busnum == 1) && firep->live_port & 0x2) { | |
652 | DBGBR(c_printf("Southbridge reset\n")); | |
653 | if (!ontario_southbridge_reset(firep)) { | |
654 | DBGBR(c_printf( | |
655 | "Southbridge reset failed\n")); | |
656 | goto fail; | |
657 | } | |
658 | } | |
659 | ||
660 | if ((brcode & 0xff) == 0xaa) { | |
661 | /* | |
662 | * Perform secondary reset. | |
663 | */ | |
664 | ||
665 | if (!plx_8532_aa_reset(busnum)) { | |
666 | DBGBR(c_printf( | |
667 | "Failed PLX 8532 AA reset\n")); | |
668 | goto fail; | |
669 | } | |
670 | } else { | |
671 | DBGBR(c_printf( | |
672 | "Found 0x%x PLX part toggling fire link.\n", | |
673 | brcode &0xff)); | |
674 | if (!toggle_fire_link(firep)) | |
675 | goto fail; | |
676 | c_usleep(200000); /* 200ms */ | |
677 | } | |
678 | break; | |
679 | default : | |
680 | goto link_down_up; | |
681 | }; | |
682 | ||
683 | DBGBR(c_printf("Fire port reset\n")); | |
684 | ||
685 | return (true); | |
686 | ||
687 | link_down_up: | |
688 | if (!toggle_fire_link(firep)) | |
689 | goto fail; | |
690 | ||
691 | return (true); | |
692 | fail: | |
693 | firep->blacklist = true; | |
694 | DBGBR(c_printf("Fire Leaf reset FAILED BLACKLISTING\n")); | |
695 | return (false); | |
696 | } | |
697 | ||
698 | ||
699 | static bool_t | |
700 | plx_8532_aa_reset(int busnum) | |
701 | { | |
702 | int reset_count; | |
703 | fire_dev_t *firep; | |
704 | int i; | |
705 | uint64_t res; | |
706 | ||
707 | firep = (fire_dev_t *)&fire_dev[busnum]; | |
708 | ||
709 | /* | |
710 | * We discovered a bridge, but is it a PLX ? indicating | |
711 | * ... if this read fails something bad | |
712 | * has happened given that we already read from the device. | |
713 | * In this case we abandon the bus ! | |
714 | */ | |
715 | ||
716 | DBGBR(c_printf("Resetting with SBR\n")); | |
717 | if (!pci_config_get(firep, UPST_CFG_BASE + 0, 4, &res)) { | |
718 | DBGBR(c_printf("Bridge vendor ID read failed !!\n")); | |
719 | return (false); | |
720 | } | |
721 | ||
722 | DBGBR(c_printf("1st bridge vendorid = 0x%x\n", res)); | |
723 | ||
724 | reset_count = 0; | |
725 | ||
726 | DBGBR(c_printf("\tdetect elect idle on station 0\n")); | |
727 | (void) plx_set_elec_idle(busnum, 0, false); | |
728 | DBGBR(c_printf("\tdetect elect idle on station 1\n")); | |
729 | (void) plx_set_elec_idle(busnum, 8, false); | |
730 | ||
731 | reset_again:; | |
732 | ||
733 | c_usleep(500000); | |
734 | DBGBR(c_printf("Attempting PLX upstream reset for bus %d - try %d\n", | |
735 | busnum, reset_count)); | |
736 | ||
737 | if (!plx_upstream_port_reset(firep)) | |
738 | return (false); | |
739 | ||
740 | /* wait for everything to settle/retrain */ | |
741 | c_usleep(1500000); | |
742 | ||
743 | for (i = 0; i < NUM_PLX_PORTS; i++) { | |
744 | if (((firep->live_port >> i) & 1) == 0) | |
745 | continue; | |
746 | ||
747 | /* Enter here if port i requires resetting */ | |
748 | ||
749 | DBGBR(c_printf("Secondary reset for leaf %d port %d\n", | |
750 | busnum, i)); | |
751 | ||
752 | if (is_plx_downstream_port_ready(firep, i)) | |
753 | continue; | |
754 | ||
755 | /* Failed - we need another upstream reset */ | |
756 | ||
757 | if (++reset_count < MAX_RESETS) | |
758 | goto reset_again; | |
759 | ||
760 | /* After max resets we'll blacklist the port instead */ | |
761 | } | |
762 | ||
763 | DBGBR(c_printf("\tdont detect elect idle on station 0\n")); | |
764 | (void) plx_set_elec_idle(busnum, 0, true); | |
765 | DBGBR(c_printf("\tdont detect elect idle on station 1\n")); | |
766 | (void) plx_set_elec_idle(busnum, 8, true); | |
767 | ||
768 | ||
769 | /* initialize up stream port */ | |
770 | CFGWR(UPST_CFG_BASE + CFG_CMD_REG, 2, 0x0); | |
771 | CFGWR(UPST_CFG_BASE + CFG_BAR0, 4, 0x0); | |
772 | CFGWR(UPST_CFG_BASE + CFG_BAR1, 4, 0x0); | |
773 | CFGWR(UPST_CFG_BASE + CFG_PS_BUS, 4, 0x0); | |
774 | CFGWR(UPST_CFG_BASE + CFG_IOBASE_LIM, 2, 0xff); | |
775 | CFGWR(UPST_CFG_BASE + CFG_MEMBASE, 2, 0xffff); | |
776 | CFGWR(UPST_CFG_BASE + CFG_MEMLIM, 2, 0x0); | |
777 | CFGWR(UPST_CFG_BASE + CFG_PFBASE, 2, 0xffff); | |
778 | CFGWR(UPST_CFG_BASE + CFG_PFLIM, 2, 0x0); | |
779 | CFGWR(UPST_CFG_BASE + CFG_PF_UBASE, 4, 0xffffffff); | |
780 | CFGWR(UPST_CFG_BASE + CFG_PF_ULIM, 4, 0x0); | |
781 | CFGWR(UPST_CFG_BASE + CFG_IO_UBASE, 2, 0xffff); | |
782 | CFGWR(UPST_CFG_BASE + CFG_IO_ULIM, 2, 0x0); | |
783 | ||
784 | /* Finally clear any accumulated errors */ | |
785 | CFGWR(UPST_CFG_BASE + CFG_STAT_CTRL, 4, 0x000f0000); | |
786 | ||
787 | return (true); | |
788 | } | |
789 | ||
790 | static bool_t | |
791 | plx_upstream_port_reset(fire_dev_t *firep) | |
792 | { | |
793 | uint64_t res; | |
794 | ||
795 | DBGBR(c_printf("plx_upstream_port_reset\n")); | |
796 | /* If the loads and stores fail - abandon the bus */ | |
797 | if (!pci_config_get(firep, UPST_CFG_BASE+CFG_SECONDARY_RESET, | |
798 | 4, &res)) { | |
799 | DBGBR(c_printf("SECONDARY_RESET set, read fail\n")); | |
800 | return (false); | |
801 | } | |
802 | ||
803 | if (!pci_config_put(firep, UPST_CFG_BASE+CFG_SECONDARY_RESET, | |
804 | 4, res | (1LL<<22))) { | |
805 | DBGBR(c_printf("SECONDARY_RESET set, write fail\n")); | |
806 | #if 0 /* { FIXME: */ | |
807 | return (false); | |
808 | #endif /* } */ | |
809 | } | |
810 | ||
811 | DBGBR(c_printf("\tBus down ... waiting 200ms\n")); | |
812 | ||
813 | c_usleep(200000); | |
814 | ||
815 | DBGBR(c_printf("\tWakeup bus\n")); | |
816 | ||
817 | if (!pci_config_get(firep, UPST_CFG_BASE+CFG_SECONDARY_RESET, | |
818 | 4, &res)) { | |
819 | DBGBR(c_printf("SECONDARY_RESET clear, read fail\n")); | |
820 | return (false); | |
821 | } | |
822 | ||
823 | DBGBR(c_printf("\tsecondary reset register status 0x%x\n", res)); | |
824 | ||
825 | if (!pci_config_put(firep, UPST_CFG_BASE+CFG_SECONDARY_RESET, | |
826 | 4, res & ~(1LL<<22))) { | |
827 | DBGBR(c_printf("SECONDARY_RESET clear, write fail\n")); | |
828 | return (false); | |
829 | } | |
830 | ||
831 | DBGBR(c_printf("\tSecondary reset completed\n")); | |
832 | return (true); | |
833 | } | |
834 | ||
835 | ||
836 | static bool_t | |
837 | is_plx_downstream_port_ready(fire_dev_t *firep, int port) | |
838 | { | |
839 | uint64_t port_cfg_base; | |
840 | uint64_t res; | |
841 | ||
842 | port_cfg_base = DNST_CFG_PORT_BASE(port); | |
843 | ||
844 | DBGBR(c_printf("is_plx_downstream_port_ready : port %d\n", port)); | |
845 | ||
846 | /* setup config access to the downstream port */ | |
847 | CFGWR(UPST_CFG_BASE + CFG_PS_BUS, 4, UPST_CFG_PS_BUS_VAL); | |
848 | ||
849 | DBGBR(c_printf("\tChecking link up ..\n")); | |
850 | ||
851 | if (!pci_config_get(firep, port_cfg_base + CFG_VC0_STATUS, | |
852 | 4, &res)) { | |
853 | DBGBR(c_printf("VC0 status register read failed\n")); | |
854 | return (false); | |
855 | } | |
856 | ||
857 | DBGBR(c_printf("VC0 status = 0x%x\n", res)); | |
858 | ||
859 | if ((res & CFG_VC0_STATUS_MASK) != 0) { | |
860 | DBGBR(c_printf("\n\nPLX link failed to train\n\n\n")); | |
861 | return (false); | |
862 | } | |
863 | ||
864 | return (true); | |
865 | } | |
866 | ||
867 | ||
868 | /* | |
869 | * On an Ontario, to reset the Southbridge we have to: | |
870 | * - initiate secondary reset | |
871 | * - program bus to allow Southbridge IO access | |
872 | * - reset southbridge device | |
873 | * - initiate secondary reset (again) | |
874 | */ | |
875 | static bool_t | |
876 | ontario_southbridge_reset(fire_dev_t *firep) | |
877 | { | |
878 | uint64_t res; | |
879 | ||
880 | /* setup config access */ | |
881 | CFGWR(UPST_CFG_BASE + CFG_PS_BUS, 4, UPST_CFG_PS_BUS_VAL); | |
882 | ||
883 | /* I/O base and Limit : 0 - 1000 */ | |
884 | CFGWR(UPST_CFG_BASE + CFG_IOBASE_LIM, 2, 0x0100); | |
885 | ||
886 | /* IO upper base */ | |
887 | CFGWR(UPST_CFG_BASE + CFG_IO_UBASE, 4, 0x0); | |
888 | ||
889 | /* command register: allow IO access */ | |
890 | CFGWR(UPST_CFG_BASE + CFG_CMD_REG, 4, 0x5); | |
891 | ||
892 | /* config downstream bus for Southbridge IO access */ | |
893 | CFGWR(DNST_CFG_BASE + CFG_PS_BUS, 4, DNST_CFG_PS_BUS_VAL); | |
894 | ||
895 | /* I/O base and Limit: 0 - 1000 */ | |
896 | CFGWR(DNST_CFG_BASE + CFG_IOBASE_LIM, 2, 0x0100); | |
897 | ||
898 | /* IO upper base */ | |
899 | CFGWR(DNST_CFG_BASE + CFG_IO_UBASE, 2, 0x0); | |
900 | ||
901 | /* command register: allow IO access */ | |
902 | CFGWR(DNST_CFG_BASE + CFG_CMD_REG, 2, 0x5); | |
903 | ||
904 | /* | |
905 | * Let's check and see if we have an Intel PCI-E -> PCI-X | |
906 | * bridge .. if not, then we have no southbridge either | |
907 | */ | |
908 | ||
909 | if (!pci_config_get(firep, PE2X_CFG_BASE + 0x0, 4, &res)) { | |
910 | DBGBR(c_printf("Failed reading second bridge vendor id\n")); | |
911 | return (false); | |
912 | } | |
913 | ||
914 | /* | |
915 | * If no Intel bridge, then we have no Southbridge to reset. | |
916 | */ | |
917 | if (res != INTEL_BRG_DEV_VEND_ID) { | |
918 | DBGBR(c_printf("No Intel bridge - ergo no southbridge\n")); | |
919 | return (true); | |
920 | } | |
921 | ||
922 | ||
923 | /* | |
924 | * Now we do the south bridge clobber | |
925 | */ | |
926 | ||
927 | /* | |
928 | * config PCIE->PCIX for Southbridge IO access | |
929 | * setup config access | |
930 | */ | |
931 | CFGWR(PE2X_CFG_BASE + CFG_PS_BUS, 4, PE2X_CFG_PS_BUS_VAL); | |
932 | ||
933 | /* I/O base and Limit: 0 - 1000 */ | |
934 | CFGWR(PE2X_CFG_BASE + CFG_IOBASE_LIM, 2, 0x0100); | |
935 | ||
936 | /* IO upper base */ | |
937 | CFGWR(PE2X_CFG_BASE + CFG_IO_UBASE, 4, 0x0); | |
938 | ||
939 | /* command register: allow IO access */ | |
940 | CFGWR(PE2X_CFG_BASE + CFG_CMD_REG, 2, 0x5); | |
941 | ||
942 | ||
943 | /* | |
944 | * Check the vendor ID of the southbridge | |
945 | */ | |
946 | ||
947 | if (!pci_config_get(firep, SOUTHBRIDGE_CFG_BASE + 0x0, 4, &res)) { | |
948 | DBGBR(c_printf("Failed reading southbridge vendor ID\n")); | |
949 | return (false); | |
950 | } | |
951 | ||
952 | if (res != ALI_SB_DEV_VEND_ID) { | |
953 | DBGBR(c_printf("Success - no southbridge to reset\n")); | |
954 | return (true); | |
955 | } | |
956 | ||
957 | DBGBR(c_printf("Resetting southbridge NOW !\n")); | |
958 | ||
959 | ||
960 | /* Enable IO space access */ | |
961 | CFGWR(SOUTHBRIDGE_CFG_BASE + SOUTHBRIDGE_CFG_RESET, 1, 1LL<<7); | |
962 | ||
963 | #define MB * (1024LL*1024LL) /* used by CFG_SIZE! */ | |
964 | ||
965 | /* Now we do an IO write to the Southbridge */ | |
966 | if (!pci_io_poke(firep, firep->cfg + CFG_SIZE + SOUTHBRIDGE_IO_RESET, | |
967 | 1, SOUTHBRIDGE_RESET_VAL, SOUTHBRIDGE_CFG_BASE)) { | |
968 | DBGBR(c_printf("Couldn't poke SB reset\n")); | |
969 | } | |
970 | ||
971 | /* Cleanup after ourselves */ | |
972 | CFGWR(PE2X_CFG_BASE + CFG_PS_BUS, 4, 0x0); | |
973 | CFGWR(PE2X_CFG_BASE + CFG_IOBASE_LIM, 2, 0xff); | |
974 | CFGWR(PE2X_CFG_BASE + CFG_IO_UBASE, 4, 0xffff); | |
975 | CFGWR(PE2X_CFG_BASE + CFG_CMD_REG, 2, 0x0); | |
976 | CFGWR(DNST_CFG_BASE + CFG_PS_BUS, 4, 0x0); | |
977 | CFGWR(DNST_CFG_BASE + CFG_IOBASE_LIM, 2, 0xff); | |
978 | CFGWR(DNST_CFG_BASE + CFG_IO_UBASE, 2, 0xffff); | |
979 | CFGWR(DNST_CFG_BASE + CFG_CMD_REG, 2, 0x0); | |
980 | CFGWR(UPST_CFG_BASE + CFG_IOBASE_LIM, 2, 0xff); | |
981 | CFGWR(UPST_CFG_BASE + CFG_IO_UBASE, 4, 0xffff); | |
982 | CFGWR(UPST_CFG_BASE + CFG_CMD_REG, 4, 0x0); | |
983 | ||
984 | c_usleep(1000000); | |
985 | return (true); | |
986 | } | |
987 | ||
988 | ||
989 | /* | |
990 | * This function only works for PLX 8532 AA parts. | |
991 | */ | |
992 | ||
993 | static bool_t | |
994 | plx_set_elec_idle(int busnum, int port, bool_t post_reset) | |
995 | { | |
996 | uint64_t fire_membase, offset, res; | |
997 | uint64_t port_cfg_offset; | |
998 | fire_dev_t *firep; | |
999 | ||
1000 | firep = (fire_dev_t *)&fire_dev[busnum]; | |
1001 | ||
1002 | fire_membase = busnum ? FIRE_BAR(0xf200ull) : FIRE_BAR(0xea00ull); | |
1003 | ||
1004 | offset = port * 4096 + 0x22C; /* electric idle bit offset */ | |
1005 | ||
1006 | port_cfg_offset = (port == 0) ? UPST_CFG_BASE : | |
1007 | DNST_CFG_PORT_BASE(port); | |
1008 | ||
1009 | DBGBR(c_printf("Fire leaf 0x%x membase 0x%x offset 0x%x = 0x%x\n", | |
1010 | busnum, fire_membase, offset, fire_membase +offset)); | |
1011 | ||
1012 | CFGWR(UPST_CFG_BASE + CFG_PS_BUS, 4, UPST_CFG_PS_BUS_VAL); | |
1013 | CFGWR(UPST_CFG_BASE + CFG_CMD_REG, 2, 2); | |
1014 | CFGWR(UPST_CFG_BASE + CFG_BAR0, 4, 0); | |
1015 | if (!pci_io_peek(firep, fire_membase + offset, 4, &res)) { | |
1016 | DBGBR(c_printf("Couldn't peek elec idle\n")); | |
1017 | } | |
1018 | ||
1019 | DBGBR(c_printf("PHY elec idle reg 0x%x\n", res)); | |
1020 | ||
1021 | if (!post_reset) { | |
1022 | res &= ~0x10LL; | |
1023 | } else { | |
1024 | res |= 0x10LL; | |
1025 | } | |
1026 | ||
1027 | DBGBR(c_printf("PHY elec idle reg after op %d 0x%x\n", post_reset, | |
1028 | res)); | |
1029 | if (!pci_io_poke(firep, fire_membase + offset, | |
1030 | 4, res, port_cfg_offset)) { | |
1031 | DBGBR(c_printf("Couldn't poke elec idle\n")); | |
1032 | } | |
1033 | ||
1034 | return (true); | |
1035 | } | |
1036 | ||
1037 | #endif |