aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/pci
diff options
context:
space:
mode:
authorJustin Hibbits <jhibbits@FreeBSD.org>2019-08-04 19:28:10 +0000
committerJustin Hibbits <jhibbits@FreeBSD.org>2019-08-04 19:28:10 +0000
commit937a05ba81c3d6fc5c9558b98b882fd61fa56e7b (patch)
tree26ef59b7f1915afab357ad092a3605618b441b8a /sys/dev/pci
parent69a277d98e6c67ad95cc7b32e9029cee124f3871 (diff)
Add necessary bits for Linux KPI to work correctly on powerpc
PowerPC, and possibly other architectures, use different address ranges for PCI space vs physical address space, which is only mapped at resource activation time, when the BAR gets written. The DRM kernel modules do not activate the rman resources, soas not to waste KVA, instead only mapping parts of the PCI memory at a time. This introduces a BUS_TRANSLATE_RESOURCE() method, implemented in the Open Firmware/FDT PCI driver, to perform this necessary translation without activating the resource. In addition to system KPI changes, LinuxKPI is updated to handle a big-endian host, by adding proper endian swaps to the I/O functions. Submitted by: mmacy Reported by: hselasky Differential Revision: https://reviews.freebsd.org/D21096
Notes
Notes: svn path=/head/; revision=350570
Diffstat (limited to 'sys/dev/pci')
-rw-r--r--sys/dev/pci/vga_pci.c123
1 files changed, 117 insertions, 6 deletions
diff --git a/sys/dev/pci/vga_pci.c b/sys/dev/pci/vga_pci.c
index 6019ffb67701..bde5cd7a55ca 100644
--- a/sys/dev/pci/vga_pci.c
+++ b/sys/dev/pci/vga_pci.c
@@ -142,11 +142,35 @@ vga_pci_is_boot_display(device_t dev)
return (1);
}
+static void
+vga_pci_reset(device_t dev)
+{
+ int ps;
+ /*
+ * FLR is unsupported on GPUs so attempt a power-management reset by cycling
+ * the device in/out of D3 state.
+ * PCI spec says we can only go into D3 state from D0 state.
+ * Transition from D[12] into D0 before going to D3 state.
+ */
+ ps = pci_get_powerstate(dev);
+ if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3)
+ pci_set_powerstate(dev, PCI_POWERSTATE_D0);
+ if (pci_get_powerstate(dev) != PCI_POWERSTATE_D3)
+ pci_set_powerstate(dev, PCI_POWERSTATE_D3);
+ pci_set_powerstate(dev, ps);
+}
+
+
void *
vga_pci_map_bios(device_t dev, size_t *size)
{
- int rid;
+ struct vga_resource *vr;
struct resource *res;
+ device_t pcib;
+ uint32_t rom_addr;
+ uint16_t config;
+ volatile char *bios;
+ int i, rid, found;
#if defined(__amd64__) || defined(__i386__)
if (vga_pci_is_boot_display(dev)) {
@@ -164,21 +188,96 @@ vga_pci_map_bios(device_t dev, size_t *size)
}
#endif
- rid = PCIR_BIOS;
+ pcib = device_get_parent(device_get_parent(dev));
+ if (device_get_devclass(device_get_parent(pcib)) ==
+ devclass_find("pci")) {
+ /*
+ * The parent bridge is a PCI-to-PCI bridge: check the
+ * value of the "VGA Enable" bit.
+ */
+ config = pci_read_config(pcib, PCIR_BRIDGECTL_1, 2);
+ if ((config & PCIB_BCR_VGA_ENABLE) == 0) {
+ config |= PCIB_BCR_VGA_ENABLE;
+ pci_write_config(pcib, PCIR_BRIDGECTL_1, config, 2);
+ }
+ }
+
+ switch(pci_read_config(dev, PCIR_HDRTYPE, 1)) {
+ case PCIM_HDRTYPE_BRIDGE:
+ rid = PCIR_BIOS_1;
+ break;
+ case PCIM_HDRTYPE_CARDBUS:
+ rid = 0;
+ break;
+ default:
+ rid = PCIR_BIOS;
+ break;
+ }
+ if (rid == 0)
+ return (NULL);
res = vga_pci_alloc_resource(dev, NULL, SYS_RES_MEMORY, &rid, 0,
~0, 1, RF_ACTIVE);
+
if (res == NULL) {
+ device_printf(dev, "vga_pci_alloc_resource failed\n");
return (NULL);
}
+ bios = rman_get_virtual(res);
+ *size = rman_get_size(res);
+ for (found = i = 0; i < hz; i++) {
+ found = (bios[0] == 0x55 && bios[1] == 0xaa);
+ if (found)
+ break;
+ pause("vgabios", 1);
+ }
+ if (found)
+ return (__DEVOLATILE(void *, bios));
+ if (bootverbose)
+ device_printf(dev, "initial ROM mapping failed -- resetting\n");
+ /*
+ * Enable ROM decode
+ */
+ vga_pci_reset(dev);
+ rom_addr = pci_read_config(dev, rid, 4);
+ rom_addr &= 0x7ff;
+ rom_addr |= rman_get_start(res) | 0x1;
+ pci_write_config(dev, rid, rom_addr, 4);
+ vr = lookup_res(device_get_softc(dev), rid);
+ vga_pci_release_resource(dev, NULL, SYS_RES_MEMORY, rid,
+ vr->vr_res);
+
+ /*
+ * re-allocate
+ */
+ res = vga_pci_alloc_resource(dev, NULL, SYS_RES_MEMORY, &rid, 0,
+ ~0, 1, RF_ACTIVE);
+ if (res == NULL) {
+ device_printf(dev, "vga_pci_alloc_resource failed\n");
+ return (NULL);
+ }
+ bios = rman_get_virtual(res);
*size = rman_get_size(res);
- return (rman_get_virtual(res));
+ for (found = i = 0; i < 3*hz; i++) {
+ found = (bios[0] == 0x55 && bios[1] == 0xaa);
+ if (found)
+ break;
+ pause("vgabios", 1);
+ }
+ if (found)
+ return (__DEVOLATILE(void *, bios));
+ device_printf(dev, "ROM mapping failed\n");
+ vr = lookup_res(device_get_softc(dev), rid);
+ vga_pci_release_resource(dev, NULL, SYS_RES_MEMORY, rid,
+ vr->vr_res);
+ return (NULL);
}
void
vga_pci_unmap_bios(device_t dev, void *bios)
{
struct vga_resource *vr;
+ int rid;
if (bios == NULL) {
return;
@@ -192,16 +291,28 @@ vga_pci_unmap_bios(device_t dev, void *bios)
return;
}
#endif
-
+ switch(pci_read_config(dev, PCIR_HDRTYPE, 1)) {
+ case PCIM_HDRTYPE_BRIDGE:
+ rid = PCIR_BIOS_1;
+ break;
+ case PCIM_HDRTYPE_CARDBUS:
+ rid = 0;
+ break;
+ default:
+ rid = PCIR_BIOS;
+ break;
+ }
+ if (rid == 0)
+ return;
/*
* Look up the PCIR_BIOS resource in our softc. It should match
* the address we returned previously.
*/
- vr = lookup_res(device_get_softc(dev), PCIR_BIOS);
+ vr = lookup_res(device_get_softc(dev), rid);
KASSERT(vr->vr_res != NULL, ("vga_pci_unmap_bios: bios not mapped"));
KASSERT(rman_get_virtual(vr->vr_res) == bios,
("vga_pci_unmap_bios: mismatch"));
- vga_pci_release_resource(dev, NULL, SYS_RES_MEMORY, PCIR_BIOS,
+ vga_pci_release_resource(dev, NULL, SYS_RES_MEMORY, rid,
vr->vr_res);
}