2 * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/malloc.h>
37 #include <machine/vmparam.h>
42 #include <contrib/dev/acpica/include/acpi.h>
43 #include <contrib/dev/acpica/include/accommon.h>
44 #include <dev/acpica/acpivar.h>
47 #include "amdvi_priv.h"
49 device_t *ivhd_devs; /* IVHD or AMD-Vi device list. */
50 int ivhd_count; /* Number of IVHD or AMD-Vi devices. */
52 extern int amdvi_ptp_level; /* Page table levels. */
54 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER * ptr, void *arg);
57 * Iterate IVRS table for IVHD and IVMD device type.
60 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
62 ACPI_TABLE_IVRS *ivrs;
63 ACPI_IVRS_HEADER *ivrs_hdr, *end;
66 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
67 if (ACPI_FAILURE(status))
70 if (ivrs->Header.Length == 0) {
74 ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
75 end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
77 while (ivrs_hdr < end) {
78 if ((uint8_t *)ivrs_hdr + ivrs_hdr->Length > (uint8_t *)end) {
79 printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n",
84 switch (ivrs_hdr->Type) {
85 case ACPI_IVRS_TYPE_HARDWARE: /* Legacy */
87 case 0x40: /* ACPI HID */
88 if (!iter(ivrs_hdr, arg))
92 case ACPI_IVRS_TYPE_MEMORY1:
93 case ACPI_IVRS_TYPE_MEMORY2:
94 case ACPI_IVRS_TYPE_MEMORY3:
95 if (!iter(ivrs_hdr, arg))
101 printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
105 ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
111 ivrs_is_ivhd(UINT8 type)
114 if ((type == ACPI_IVRS_TYPE_HARDWARE) || (type == 0x11) || (type == 0x40))
120 /* Count the number of AMD-Vi devices in the system. */
122 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
125 if (ivrs_is_ivhd(ivrs_he->Type))
131 struct find_ivrs_hdr_args {
133 ACPI_IVRS_HEADER *ptr;
137 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
139 struct find_ivrs_hdr_args *fi;
141 fi = (struct find_ivrs_hdr_args *)args;
142 if (ivrs_is_ivhd(ivrs_hdr->Type)) {
153 static ACPI_IVRS_HARDWARE *
154 ivhd_find_by_index(int idx)
156 struct find_ivrs_hdr_args fi;
161 ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
163 return ((ACPI_IVRS_HARDWARE *)fi.ptr);
167 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
168 uint32_t end_id, uint8_t cfg, bool ats)
170 struct ivhd_dev_cfg *dev_cfg;
172 /* If device doesn't have special data, don't add it. */
176 dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
177 dev_cfg->start_id = start_id;
178 dev_cfg->end_id = end_id;
180 dev_cfg->enable_ats = ats;
184 * Record device attributes as suggested by BIOS.
187 ivhd_dev_parse(ACPI_IVRS_HARDWARE * ivhd, struct amdvi_softc *softc)
189 ACPI_IVRS_DE_HEADER *de, *end;
190 int range_start_id = 0, range_end_id = 0;
192 uint8_t all_data = 0, range_data = 0;
193 bool range_enable_ats = false, enable_ats;
195 softc->start_dev_rid = ~0;
196 softc->end_dev_rid = 0;
198 de = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
199 sizeof(ACPI_IVRS_HARDWARE));
200 end = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
201 ivhd->Header.Length);
203 while (de < (ACPI_IVRS_DE_HEADER *) end) {
204 softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
205 softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
207 case ACPI_IVRS_TYPE_ALL:
208 all_data = de->DataSetting;
211 case ACPI_IVRS_TYPE_SELECT:
212 case ACPI_IVRS_TYPE_ALIAS_SELECT:
213 case ACPI_IVRS_TYPE_EXT_SELECT:
215 if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
216 extended = (uint32_t *)(de + 1);
218 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
221 ivhd_dev_add_entry(softc, de->Id, de->Id,
222 de->DataSetting | all_data, enable_ats);
225 case ACPI_IVRS_TYPE_START:
226 case ACPI_IVRS_TYPE_ALIAS_START:
227 case ACPI_IVRS_TYPE_EXT_START:
228 range_start_id = de->Id;
229 range_data = de->DataSetting;
230 if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
231 extended = (uint32_t *)(de + 1);
233 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
238 case ACPI_IVRS_TYPE_END:
239 range_end_id = de->Id;
240 ivhd_dev_add_entry(softc, range_start_id, range_end_id,
241 range_data | all_data, range_enable_ats);
242 range_start_id = range_end_id = 0;
247 case ACPI_IVRS_TYPE_PAD4:
250 case ACPI_IVRS_TYPE_SPECIAL:
254 if ((de->Type < 5) ||
255 (de->Type >= ACPI_IVRS_TYPE_PAD8))
256 device_printf(softc->dev,
257 "Unknown dev entry:0x%x\n", de->Type);
260 if (softc->dev_cfg_cnt >
261 (sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
262 device_printf(softc->dev,
263 "WARN Too many device entries.\n");
269 KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
270 ("Device end[0x%x] < start[0x%x.\n",
271 softc->end_dev_rid, softc->start_dev_rid));
277 ivhd_identify(driver_t *driver, device_t parent)
279 ACPI_TABLE_IVRS *ivrs;
280 ACPI_IVRS_HARDWARE *ivhd;
285 if (acpi_disabled("ivhd"))
288 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
289 if (ACPI_FAILURE(status))
292 if (ivrs->Header.Length == 0) {
297 printf("AMD-Vi IVRS VAsize = %d PAsize = %d GVAsize = %d flags:%b\n",
298 REG_BITS(info, 21, 15), REG_BITS(info, 14, 8),
299 REG_BITS(info, 7, 5), REG_BITS(info, 22, 22),
300 "\020\001HtAtsResv");
302 ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
306 ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
308 for (i = 0; i < ivhd_count; i++) {
309 ivhd = ivhd_find_by_index(i);
311 printf("Can't find IVHD entry%d\n", i);
315 ivhd_devs[i] = BUS_ADD_CHILD(parent, 1, "ivhd", i);
317 * XXX: In case device was not destroyed before, add will fail.
318 * locate the old device instance.
320 if (ivhd_devs[i] == NULL) {
321 ivhd_devs[i] = device_find_child(parent, "ivhd", i);
322 if (ivhd_devs[i] == NULL) {
323 printf("AMD-Vi: cant find AMD-Vi dev%d\n", i);
331 * Update device count in case failed to attach.
337 ivhd_probe(device_t dev)
340 if (acpi_get_handle(dev) != NULL)
342 device_set_desc(dev, "AMD-Vi/IOMMU or ivhd");
344 return (BUS_PROBE_NOWILDCARD);
348 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd)
354 device_printf(dev, "Flag:%b\n", softc->ivhd_flag,
355 "\020\001HtTunEn\002PassPW\003ResPassPW\004Isoc\005IotlbSup"
356 "\006Coherent\007PreFSup\008PPRSup");
358 * If no extended feature[EFR], its rev1 with maximum paging level as 7.
361 if (softc->ivhd_efr) {
362 device_printf(dev, "EFR HATS = %d GATS = %d GLXSup = %d "
363 "MsiNumPr = %d PNBanks= %d PNCounters= %d\n"
364 "max PASID = %d EFR: %b \n",
365 REG_BITS(softc->ivhd_efr, 31, 30),
366 REG_BITS(softc->ivhd_efr, 29, 28),
367 REG_BITS(softc->ivhd_efr, 4, 3),
368 REG_BITS(softc->ivhd_efr, 27, 23),
369 REG_BITS(softc->ivhd_efr, 22, 17),
370 REG_BITS(softc->ivhd_efr, 16, 13),
371 REG_BITS(softc->ivhd_efr, 12, 8),
372 softc->ivhd_efr, "\020\001XTSup\002NXSup\003GTSup\005IASup"
373 "\006GASup\007HESup\008PPRSup");
375 max_ptp_level = REG_BITS(softc->ivhd_efr, 31, 30) + 4;
378 /* Make sure device support minimum page level as requested by user. */
379 if (max_ptp_level < amdvi_ptp_level) {
380 device_printf(dev, "Insufficient PTP level:%d\n",
385 device_printf(softc->dev, "max supported paging level:%d restricting to: %d\n",
386 max_ptp_level, amdvi_ptp_level);
387 device_printf(softc->dev, "device supported range "
388 "[0x%x - 0x%x]\n", softc->start_dev_rid, softc->end_dev_rid);
394 ivhd_attach(device_t dev)
396 ACPI_IVRS_HARDWARE *ivhd;
397 struct amdvi_softc *softc;
400 unit = device_get_unit(dev);
401 /* Make sure its same device for which attach is called. */
402 if (ivhd_devs[unit] != dev)
403 panic("Not same device old %p new %p", ivhd_devs[unit], dev);
405 softc = device_get_softc(dev);
407 ivhd = ivhd_find_by_index(unit);
411 softc->pci_seg = ivhd->PciSegmentGroup;
412 softc->pci_rid = ivhd->Header.DeviceId;
413 softc->ivhd_flag = ivhd->Header.Flags;
414 softc->ivhd_efr = ivhd->Reserved;
416 * PCI capability has more capabilities that are not part of IVRS.
418 softc->cap_off = ivhd->CapabilityOffset;
421 /* IVHD Info bit[4:0] is event MSI/X number. */
422 softc->event_msix = ivhd->Info & 0x1F;
424 softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
425 status = ivhd_dev_parse(ivhd, softc);
428 "endpoint device parsing error=%d\n", status);
431 status = ivhd_print_cap(softc, ivhd);
436 status = amdvi_setup_hw(softc);
438 device_printf(dev, "couldn't be initialised, error=%d\n",
447 ivhd_detach(device_t dev)
449 struct amdvi_softc *softc;
451 softc = device_get_softc(dev);
453 amdvi_teardown_hw(softc);
456 * XXX: delete the device.
457 * don't allow detach, return EBUSY.
463 ivhd_suspend(device_t dev)
470 ivhd_resume(device_t dev)
476 static device_method_t ivhd_methods[] = {
477 DEVMETHOD(device_identify, ivhd_identify),
478 DEVMETHOD(device_probe, ivhd_probe),
479 DEVMETHOD(device_attach, ivhd_attach),
480 DEVMETHOD(device_detach, ivhd_detach),
481 DEVMETHOD(device_suspend, ivhd_suspend),
482 DEVMETHOD(device_resume, ivhd_resume),
486 static driver_t ivhd_driver = {
489 sizeof(struct amdvi_softc),
492 static devclass_t ivhd_devclass;
495 * Load this module at the end after PCI re-probing to configure interrupt.
497 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
499 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
500 MODULE_DEPEND(ivhd, pci, 1, 1, 1);