1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2016, Anish Gupta ([email protected])
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "opt_acpi.h"
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/malloc.h>
38
39 #include <machine/vmparam.h>
40
41 #include <vm/vm.h>
42 #include <vm/pmap.h>
43
44 #include <contrib/dev/acpica/include/acpi.h>
45 #include <contrib/dev/acpica/include/accommon.h>
46 #include <dev/acpica/acpivar.h>
47
48 #include "io/iommu.h"
49 #include "amdvi_priv.h"
50
51 device_t *ivhd_devs; /* IVHD or AMD-Vi device list. */
52 int ivhd_count; /* Number of IVHD header. */
53 /*
54 * Cached IVHD header list.
55 * Single entry for each IVHD, filtered the legacy one.
56 */
57 ACPI_IVRS_HARDWARE1 *ivhd_hdrs[10];
58
59 extern int amdvi_ptp_level; /* Page table levels. */
60
61 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER *ptr, void *arg);
62 /*
63 * Iterate IVRS table for IVHD and IVMD device type.
64 */
65 static void
ivrs_hdr_iterate_tbl(ivhd_iter_t iter,void * arg)66 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
67 {
68 ACPI_TABLE_IVRS *ivrs;
69 ACPI_IVRS_HEADER *ivrs_hdr, *end;
70 ACPI_STATUS status;
71
72 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
73 if (ACPI_FAILURE(status))
74 return;
75
76 if (ivrs->Header.Length == 0) {
77 return;
78 }
79
80 ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
81 end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
82
83 while (ivrs_hdr < end) {
84 if ((uint8_t *)ivrs_hdr + ivrs_hdr->Length > (uint8_t *)end) {
85 printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n",
86 ivrs_hdr->Length);
87 break;
88 }
89
90 switch (ivrs_hdr->Type) {
91 case IVRS_TYPE_HARDWARE_LEGACY: /* Legacy */
92 case IVRS_TYPE_HARDWARE_EFR:
93 case IVRS_TYPE_HARDWARE_MIXED:
94 if (!iter(ivrs_hdr, arg))
95 return;
96 break;
97
98 case ACPI_IVRS_TYPE_MEMORY1:
99 case ACPI_IVRS_TYPE_MEMORY2:
100 case ACPI_IVRS_TYPE_MEMORY3:
101 if (!iter(ivrs_hdr, arg))
102 return;
103
104 break;
105
106 default:
107 printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
108 }
109
110 ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
111 ivrs_hdr->Length);
112 }
113 }
114
115 static bool
ivrs_is_ivhd(UINT8 type)116 ivrs_is_ivhd(UINT8 type)
117 {
118
119 switch(type) {
120 case IVRS_TYPE_HARDWARE_LEGACY:
121 case IVRS_TYPE_HARDWARE_EFR:
122 case IVRS_TYPE_HARDWARE_MIXED:
123 return (true);
124
125 default:
126 return (false);
127 }
128 }
129
130 /* Count the number of AMD-Vi devices in the system. */
131 static int
ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he,void * arg)132 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
133 {
134
135 if (ivrs_is_ivhd(ivrs_he->Type))
136 ivhd_count++;
137
138 return (1);
139 }
140
141 struct find_ivrs_hdr_args {
142 int i;
143 ACPI_IVRS_HEADER *ptr;
144 };
145
146 static int
ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr,void * args)147 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
148 {
149 struct find_ivrs_hdr_args *fi;
150
151 fi = (struct find_ivrs_hdr_args *)args;
152 if (ivrs_is_ivhd(ivrs_hdr->Type)) {
153 if (fi->i == 0) {
154 fi->ptr = ivrs_hdr;
155 return (0);
156 }
157 fi->i--;
158 }
159
160 return (1);
161 }
162
163 static ACPI_IVRS_HARDWARE1 *
ivhd_find_by_index(int idx)164 ivhd_find_by_index(int idx)
165 {
166 struct find_ivrs_hdr_args fi;
167
168 fi.i = idx;
169 fi.ptr = NULL;
170
171 ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
172
173 return ((ACPI_IVRS_HARDWARE1 *)fi.ptr);
174 }
175
176 static void
ivhd_dev_add_entry(struct amdvi_softc * softc,uint32_t start_id,uint32_t end_id,uint8_t cfg,bool ats)177 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
178 uint32_t end_id, uint8_t cfg, bool ats)
179 {
180 struct ivhd_dev_cfg *dev_cfg;
181
182 /* If device doesn't have special data, don't add it. */
183 if (!cfg)
184 return;
185
186 dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
187 dev_cfg->start_id = start_id;
188 dev_cfg->end_id = end_id;
189 dev_cfg->data = cfg;
190 dev_cfg->enable_ats = ats;
191 }
192
193 /*
194 * Record device attributes as suggested by BIOS.
195 */
196 static int
ivhd_dev_parse(ACPI_IVRS_HARDWARE1 * ivhd,struct amdvi_softc * softc)197 ivhd_dev_parse(ACPI_IVRS_HARDWARE1 *ivhd, struct amdvi_softc *softc)
198 {
199 ACPI_IVRS_DE_HEADER *de;
200 uint8_t *p, *end;
201 int range_start_id = 0, range_end_id = 0;
202 uint32_t *extended;
203 uint8_t all_data = 0, range_data = 0;
204 bool range_enable_ats = false, enable_ats;
205
206 softc->start_dev_rid = ~0;
207 softc->end_dev_rid = 0;
208
209 switch (ivhd->Header.Type) {
210 case IVRS_TYPE_HARDWARE_LEGACY:
211 p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE1);
212 break;
213
214 case IVRS_TYPE_HARDWARE_EFR:
215 case IVRS_TYPE_HARDWARE_MIXED:
216 p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE2);
217 break;
218
219 default:
220 device_printf(softc->dev,
221 "unknown type: 0x%x\n", ivhd->Header.Type);
222 return (-1);
223 }
224
225 end = (uint8_t *)ivhd + ivhd->Header.Length;
226
227 while (p < end) {
228 de = (ACPI_IVRS_DE_HEADER *)p;
229 softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
230 softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
231 switch (de->Type) {
232 case ACPI_IVRS_TYPE_ALL:
233 all_data = de->DataSetting;
234 break;
235
236 case ACPI_IVRS_TYPE_SELECT:
237 case ACPI_IVRS_TYPE_ALIAS_SELECT:
238 case ACPI_IVRS_TYPE_EXT_SELECT:
239 enable_ats = false;
240 if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
241 extended = (uint32_t *)(de + 1);
242 enable_ats =
243 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
244 false : true;
245 }
246 ivhd_dev_add_entry(softc, de->Id, de->Id,
247 de->DataSetting | all_data, enable_ats);
248 break;
249
250 case ACPI_IVRS_TYPE_START:
251 case ACPI_IVRS_TYPE_ALIAS_START:
252 case ACPI_IVRS_TYPE_EXT_START:
253 range_start_id = de->Id;
254 range_data = de->DataSetting;
255 if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
256 extended = (uint32_t *)(de + 1);
257 range_enable_ats =
258 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
259 false : true;
260 }
261 break;
262
263 case ACPI_IVRS_TYPE_END:
264 range_end_id = de->Id;
265 ivhd_dev_add_entry(softc, range_start_id, range_end_id,
266 range_data | all_data, range_enable_ats);
267 range_start_id = range_end_id = 0;
268 range_data = 0;
269 all_data = 0;
270 break;
271
272 case ACPI_IVRS_TYPE_PAD4:
273 break;
274
275 case ACPI_IVRS_TYPE_SPECIAL:
276 /* HPET or IOAPIC */
277 break;
278 default:
279 if ((de->Type < 5) ||
280 (de->Type >= ACPI_IVRS_TYPE_PAD8))
281 device_printf(softc->dev,
282 "Unknown dev entry:0x%x\n", de->Type);
283 }
284
285 if (softc->dev_cfg_cnt >
286 (sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
287 device_printf(softc->dev,
288 "WARN Too many device entries.\n");
289 return (EINVAL);
290 }
291 if (de->Type < 0x40)
292 p += sizeof(ACPI_IVRS_DEVICE4);
293 else if (de->Type < 0x80)
294 p += sizeof(ACPI_IVRS_DEVICE8A);
295 else {
296 printf("Variable size IVHD type 0x%x not supported\n",
297 de->Type);
298 break;
299 }
300 }
301
302 KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
303 ("Device end[0x%x] < start[0x%x.\n",
304 softc->end_dev_rid, softc->start_dev_rid));
305
306 return (0);
307 }
308
309 static bool
ivhd_is_newer(ACPI_IVRS_HEADER * old,ACPI_IVRS_HEADER * new)310 ivhd_is_newer(ACPI_IVRS_HEADER *old, ACPI_IVRS_HEADER *new)
311 {
312 /*
313 * Newer IVRS header type take precedence.
314 */
315 if ((old->DeviceId == new->DeviceId) &&
316 (old->Type == IVRS_TYPE_HARDWARE_LEGACY) &&
317 ((new->Type == IVRS_TYPE_HARDWARE_EFR) ||
318 (new->Type == IVRS_TYPE_HARDWARE_MIXED))) {
319 return (true);
320 }
321
322 return (false);
323 }
324
325 static void
ivhd_identify(driver_t * driver,device_t parent)326 ivhd_identify(driver_t *driver, device_t parent)
327 {
328 ACPI_TABLE_IVRS *ivrs;
329 ACPI_IVRS_HARDWARE1 *ivhd;
330 ACPI_STATUS status;
331 int i, count = 0;
332 uint32_t ivrs_ivinfo;
333
334 if (acpi_disabled("ivhd"))
335 return;
336
337 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
338 if (ACPI_FAILURE(status))
339 return;
340
341 if (ivrs->Header.Length == 0) {
342 return;
343 }
344
345 ivrs_ivinfo = ivrs->Info;
346 printf("AMD-Vi: IVRS Info VAsize = %d PAsize = %d GVAsize = %d"
347 " flags:%b\n",
348 REG_BITS(ivrs_ivinfo, 21, 15), REG_BITS(ivrs_ivinfo, 14, 8),
349 REG_BITS(ivrs_ivinfo, 7, 5), REG_BITS(ivrs_ivinfo, 22, 22),
350 "\020\001EFRSup");
351
352 ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
353 if (!ivhd_count)
354 return;
355
356 for (i = 0; i < ivhd_count; i++) {
357 ivhd = ivhd_find_by_index(i);
358 KASSERT(ivhd, ("ivhd%d is NULL\n", i));
359 ivhd_hdrs[i] = ivhd;
360 }
361
362 /*
363 * Scan for presence of legacy and non-legacy device type
364 * for same AMD-Vi device and override the old one.
365 */
366 for (i = ivhd_count - 1 ; i > 0 ; i--){
367 if (ivhd_is_newer(&ivhd_hdrs[i-1]->Header,
368 &ivhd_hdrs[i]->Header)) {
369 ivhd_hdrs[i-1] = ivhd_hdrs[i];
370 ivhd_count--;
371 }
372 }
373
374 ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
375 M_WAITOK | M_ZERO);
376 for (i = 0; i < ivhd_count; i++) {
377 ivhd = ivhd_hdrs[i];
378 KASSERT(ivhd, ("ivhd%d is NULL\n", i));
379
380 /*
381 * Use a high order to ensure that this driver is probed after
382 * the Host-PCI bridge and the root PCI bus.
383 */
384 ivhd_devs[i] = BUS_ADD_CHILD(parent,
385 ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i);
386
387 /*
388 * XXX: In case device was not destroyed before, add will fail.
389 * locate the old device instance.
390 */
391 if (ivhd_devs[i] == NULL) {
392 ivhd_devs[i] = device_find_child(parent, "ivhd", i);
393 if (ivhd_devs[i] == NULL) {
394 printf("AMD-Vi: cant find ivhd%d\n", i);
395 break;
396 }
397 }
398 count++;
399 }
400
401 /*
402 * Update device count in case failed to attach.
403 */
404 ivhd_count = count;
405 }
406
407 static int
ivhd_probe(device_t dev)408 ivhd_probe(device_t dev)
409 {
410 ACPI_IVRS_HARDWARE1 *ivhd;
411 int unit;
412
413 if (acpi_get_handle(dev) != NULL)
414 return (ENXIO);
415
416 unit = device_get_unit(dev);
417 KASSERT((unit < ivhd_count),
418 ("ivhd unit %d > count %d", unit, ivhd_count));
419 ivhd = ivhd_hdrs[unit];
420 KASSERT(ivhd, ("ivhd is NULL"));
421
422 switch (ivhd->Header.Type) {
423 case IVRS_TYPE_HARDWARE_EFR:
424 device_set_desc(dev, "AMD-Vi/IOMMU ivhd with EFR");
425 break;
426
427 case IVRS_TYPE_HARDWARE_MIXED:
428 device_set_desc(dev, "AMD-Vi/IOMMU ivhd in mixed format");
429 break;
430
431 case IVRS_TYPE_HARDWARE_LEGACY:
432 default:
433 device_set_desc(dev, "AMD-Vi/IOMMU ivhd");
434 break;
435 }
436
437 return (BUS_PROBE_NOWILDCARD);
438 }
439
440 static void
ivhd_print_flag(device_t dev,enum IvrsType ivhd_type,uint8_t flag)441 ivhd_print_flag(device_t dev, enum IvrsType ivhd_type, uint8_t flag)
442 {
443 /*
444 * IVHD lgeacy type has two extra high bits in flag which has
445 * been moved to EFR for non-legacy device.
446 */
447 switch (ivhd_type) {
448 case IVRS_TYPE_HARDWARE_LEGACY:
449 device_printf(dev, "Flag:%b\n", flag,
450 "\020"
451 "\001HtTunEn"
452 "\002PassPW"
453 "\003ResPassPW"
454 "\004Isoc"
455 "\005IotlbSup"
456 "\006Coherent"
457 "\007PreFSup"
458 "\008PPRSup");
459 break;
460
461 case IVRS_TYPE_HARDWARE_EFR:
462 case IVRS_TYPE_HARDWARE_MIXED:
463 device_printf(dev, "Flag:%b\n", flag,
464 "\020"
465 "\001HtTunEn"
466 "\002PassPW"
467 "\003ResPassPW"
468 "\004Isoc"
469 "\005IotlbSup"
470 "\006Coherent");
471 break;
472
473 default:
474 device_printf(dev, "Can't decode flag of ivhd type :0x%x\n",
475 ivhd_type);
476 break;
477 }
478 }
479
480 /*
481 * Feature in legacy IVHD type(0x10) and attribute in newer type(0x11 and 0x40).
482 */
483 static void
ivhd_print_feature(device_t dev,enum IvrsType ivhd_type,uint32_t feature)484 ivhd_print_feature(device_t dev, enum IvrsType ivhd_type, uint32_t feature)
485 {
486 switch (ivhd_type) {
487 case IVRS_TYPE_HARDWARE_LEGACY:
488 device_printf(dev, "Features(type:0x%x) HATS = %d GATS = %d"
489 " MsiNumPPR = %d PNBanks= %d PNCounters= %d\n",
490 ivhd_type,
491 REG_BITS(feature, 31, 30),
492 REG_BITS(feature, 29, 28),
493 REG_BITS(feature, 27, 23),
494 REG_BITS(feature, 22, 17),
495 REG_BITS(feature, 16, 13));
496 device_printf(dev, "max PASID = %d GLXSup = %d Feature:%b\n",
497 REG_BITS(feature, 12, 8),
498 REG_BITS(feature, 4, 3),
499 feature,
500 "\020"
501 "\002NXSup"
502 "\003GTSup"
503 "\004<b4>"
504 "\005IASup"
505 "\006GASup"
506 "\007HESup");
507 break;
508
509 /* Fewer features or attributes are reported in non-legacy type. */
510 case IVRS_TYPE_HARDWARE_EFR:
511 case IVRS_TYPE_HARDWARE_MIXED:
512 device_printf(dev, "Features(type:0x%x) MsiNumPPR = %d"
513 " PNBanks= %d PNCounters= %d\n",
514 ivhd_type,
515 REG_BITS(feature, 27, 23),
516 REG_BITS(feature, 22, 17),
517 REG_BITS(feature, 16, 13));
518 break;
519
520 default: /* Other ivhd type features are not decoded. */
521 device_printf(dev, "Can't decode ivhd type :0x%x\n", ivhd_type);
522 }
523 }
524
525 /* Print extended features of IOMMU. */
526 static void
ivhd_print_ext_feature(device_t dev,uint64_t ext_feature)527 ivhd_print_ext_feature(device_t dev, uint64_t ext_feature)
528 {
529 uint32_t ext_low, ext_high;
530
531 if (!ext_feature)
532 return;
533
534 ext_low = ext_feature;
535 device_printf(dev, "Extended features[31:0]:%b "
536 "HATS = 0x%x GATS = 0x%x "
537 "GLXSup = 0x%x SmiFSup = 0x%x SmiFRC = 0x%x "
538 "GAMSup = 0x%x DualPortLogSup = 0x%x DualEventLogSup = 0x%x\n",
539 (int)ext_low,
540 "\020"
541 "\001PreFSup"
542 "\002PPRSup"
543 "\003<b2>"
544 "\004NXSup"
545 "\005GTSup"
546 "\006<b5>"
547 "\007IASup"
548 "\008GASup"
549 "\009HESup"
550 "\010PCSup",
551 REG_BITS(ext_low, 11, 10),
552 REG_BITS(ext_low, 13, 12),
553 REG_BITS(ext_low, 15, 14),
554 REG_BITS(ext_low, 17, 16),
555 REG_BITS(ext_low, 20, 18),
556 REG_BITS(ext_low, 23, 21),
557 REG_BITS(ext_low, 25, 24),
558 REG_BITS(ext_low, 29, 28));
559
560 ext_high = ext_feature >> 32;
561 device_printf(dev, "Extended features[62:32]:%b "
562 "Max PASID: 0x%x DevTblSegSup = 0x%x "
563 "MarcSup = 0x%x\n",
564 (int)(ext_high),
565 "\020"
566 "\006USSup"
567 "\009PprOvrflwEarlySup"
568 "\010PPRAutoRspSup"
569 "\013BlKStopMrkSup"
570 "\014PerfOptSup"
571 "\015MsiCapMmioSup"
572 "\017GIOSup"
573 "\018HASup"
574 "\019EPHSup"
575 "\020AttrFWSup"
576 "\021HDSup"
577 "\023InvIotlbSup",
578 REG_BITS(ext_high, 5, 0),
579 REG_BITS(ext_high, 8, 7),
580 REG_BITS(ext_high, 11, 10));
581 }
582
583 static int
ivhd_print_cap(struct amdvi_softc * softc,ACPI_IVRS_HARDWARE1 * ivhd)584 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE1 * ivhd)
585 {
586 device_t dev;
587 int max_ptp_level;
588
589 dev = softc->dev;
590
591 ivhd_print_flag(dev, softc->ivhd_type, softc->ivhd_flag);
592 ivhd_print_feature(dev, softc->ivhd_type, softc->ivhd_feature);
593 ivhd_print_ext_feature(dev, softc->ext_feature);
594 max_ptp_level = 7;
595 /* Make sure device support minimum page level as requested by user. */
596 if (max_ptp_level < amdvi_ptp_level) {
597 device_printf(dev, "insufficient PTP level:%d\n",
598 max_ptp_level);
599 return (EINVAL);
600 } else {
601 device_printf(softc->dev, "supported paging level:%d, will use only: %d\n",
602 max_ptp_level, amdvi_ptp_level);
603 }
604
605 device_printf(softc->dev, "device range: 0x%x - 0x%x\n",
606 softc->start_dev_rid, softc->end_dev_rid);
607
608 return (0);
609 }
610
611 static int
ivhd_attach(device_t dev)612 ivhd_attach(device_t dev)
613 {
614 ACPI_IVRS_HARDWARE1 *ivhd;
615 ACPI_IVRS_HARDWARE2 *ivhd_efr;
616 struct amdvi_softc *softc;
617 int status, unit;
618
619 unit = device_get_unit(dev);
620 KASSERT((unit < ivhd_count),
621 ("ivhd unit %d > count %d", unit, ivhd_count));
622 /* Make sure its same device for which attach is called. */
623 KASSERT((ivhd_devs[unit] == dev),
624 ("Not same device old %p new %p", ivhd_devs[unit], dev));
625
626 softc = device_get_softc(dev);
627 softc->dev = dev;
628 ivhd = ivhd_hdrs[unit];
629 KASSERT(ivhd, ("ivhd is NULL"));
630
631 softc->ivhd_type = ivhd->Header.Type;
632 softc->pci_seg = ivhd->PciSegmentGroup;
633 softc->pci_rid = ivhd->Header.DeviceId;
634 softc->ivhd_flag = ivhd->Header.Flags;
635 /*
636 * On lgeacy IVHD type(0x10), it is documented as feature
637 * but in newer type it is attribute.
638 */
639 softc->ivhd_feature = ivhd->FeatureReporting;
640 /*
641 * PCI capability has more capabilities that are not part of IVRS.
642 */
643 softc->cap_off = ivhd->CapabilityOffset;
644
645 #ifdef notyet
646 /* IVHD Info bit[4:0] is event MSI/X number. */
647 softc->event_msix = ivhd->Info & 0x1F;
648 #endif
649 switch (ivhd->Header.Type) {
650 case IVRS_TYPE_HARDWARE_EFR:
651 case IVRS_TYPE_HARDWARE_MIXED:
652 ivhd_efr = (ACPI_IVRS_HARDWARE2 *)ivhd;
653 softc->ext_feature = ivhd_efr->EfrRegisterImage;
654 break;
655 }
656
657 softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
658 status = ivhd_dev_parse(ivhd, softc);
659 if (status != 0) {
660 device_printf(dev,
661 "endpoint device parsing error=%d\n", status);
662 }
663
664 status = ivhd_print_cap(softc, ivhd);
665 if (status != 0) {
666 return (status);
667 }
668
669 status = amdvi_setup_hw(softc);
670 if (status != 0) {
671 device_printf(dev, "couldn't be initialised, error=%d\n",
672 status);
673 return (status);
674 }
675
676 return (0);
677 }
678
679 static int
ivhd_detach(device_t dev)680 ivhd_detach(device_t dev)
681 {
682 struct amdvi_softc *softc;
683
684 softc = device_get_softc(dev);
685
686 amdvi_teardown_hw(softc);
687
688 /*
689 * XXX: delete the device.
690 * don't allow detach, return EBUSY.
691 */
692 return (0);
693 }
694
695 static int
ivhd_suspend(device_t dev)696 ivhd_suspend(device_t dev)
697 {
698
699 return (0);
700 }
701
702 static int
ivhd_resume(device_t dev)703 ivhd_resume(device_t dev)
704 {
705
706 return (0);
707 }
708
709 static device_method_t ivhd_methods[] = {
710 DEVMETHOD(device_identify, ivhd_identify),
711 DEVMETHOD(device_probe, ivhd_probe),
712 DEVMETHOD(device_attach, ivhd_attach),
713 DEVMETHOD(device_detach, ivhd_detach),
714 DEVMETHOD(device_suspend, ivhd_suspend),
715 DEVMETHOD(device_resume, ivhd_resume),
716 DEVMETHOD_END
717 };
718
719 static driver_t ivhd_driver = {
720 "ivhd",
721 ivhd_methods,
722 sizeof(struct amdvi_softc),
723 };
724
725 static devclass_t ivhd_devclass;
726
727 /*
728 * Load this module at the end after PCI re-probing to configure interrupt.
729 */
730 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
731 SI_ORDER_ANY);
732 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
733 MODULE_DEPEND(ivhd, pci, 1, 1, 1);
734