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