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