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
52device_t *ivhd_devs;			/* IVHD or AMD-Vi device list. */
53int	ivhd_count;			/* Number of IVHD header. */
54/*
55 * Cached IVHD header list.
56 * Single entry for each IVHD, filtered the legacy one.
57 */
58ACPI_IVRS_HARDWARE1 **ivhd_hdrs;
59
60extern int amdvi_ptp_level;		/* Page table levels. */
61
62typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER *ptr, void *arg);
63/*
64 * Iterate IVRS table for IVHD and IVMD device type.
65 */
66static void
67ivrs_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
116static bool
117ivrs_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. */
132static int
133ivhd_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
144struct find_ivrs_hdr_args {
145	int	i;
146	ACPI_IVRS_HEADER *ptr;
147};
148
149static int
150ivrs_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
166static ACPI_IVRS_HARDWARE1 *
167ivhd_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
179static void
180ivhd_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 */
207static int
208ivhd_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
322static bool
323ivhd_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
346static void
347ivhd_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
432static int
433ivhd_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
465static void
466ivhd_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 */
508static void
509ivhd_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. */
551static void
552ivhd_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
608static int
609ivhd_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
633static int
634ivhd_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
704fail:
705	free(softc->dev_cfg, M_DEVBUF);
706	return (status);
707}
708
709static int
710ivhd_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
726static int
727ivhd_suspend(device_t dev)
728{
729
730	return (0);
731}
732
733static int
734ivhd_resume(device_t dev)
735{
736
737	return (0);
738}
739
740static 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
750static 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 */
759DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, 0, 0, SI_ORDER_ANY);
760MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
761MODULE_DEPEND(ivhd, pci, 1, 1, 1);
762