Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/pci/pci_host_generic_acpi.c
/*- | /*- | ||||
* Copyright (C) 2018 Cavium Inc. | |||||
* Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com> | * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com> | ||||
* Copyright (c) 2014 The FreeBSD Foundation | * Copyright (c) 2014 The FreeBSD Foundation | ||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* This software was developed by Semihalf under | * Developed by Semihalf. | ||||
* the sponsorship of the FreeBSD Foundation. | |||||
* | * | ||||
emaste: The previous statement is the standard one for Foundation-sponsored work, however I am not… | |||||
Not Done Inline ActionsYou are right. I should have kept this. Fixed. pdk_semihalf.com: You are right. I should have kept this. Fixed. | |||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
#define PROPS_CELL_SIZE 1 | #define PROPS_CELL_SIZE 1 | ||||
#define PCI_ADDR_CELL_SIZE 2 | #define PCI_ADDR_CELL_SIZE 2 | ||||
struct generic_pcie_acpi_softc { | struct generic_pcie_acpi_softc { | ||||
struct generic_pcie_core_softc base; | struct generic_pcie_core_softc base; | ||||
ACPI_BUFFER ap_prt; /* interrupt routing table */ | ACPI_BUFFER ap_prt; /* interrupt routing table */ | ||||
}; | }; | ||||
typedef void (*pci_host_generic_acpi_quirk_function)(device_t); | |||||
struct pci_host_generic_acpi_quirk_entry { | |||||
int impl; | |||||
int part; | |||||
int var; | |||||
int rev; | |||||
pci_host_generic_acpi_quirk_function func; | |||||
}; | |||||
struct pci_host_generic_acpi_block_entry { | |||||
int impl; | |||||
int part; | |||||
int var; | |||||
int rev; | |||||
int bus; | |||||
int slot; | |||||
}; | |||||
/* Forward prototypes */ | /* Forward prototypes */ | ||||
static int generic_pcie_acpi_probe(device_t dev); | static int generic_pcie_acpi_probe(device_t dev); | ||||
static uint32_t generic_pcie_read_config(device_t dev, u_int bus, u_int slot, | uint32_t generic_pcie_read_config(device_t dev, u_int bus, u_int slot, | ||||
u_int func, u_int reg, int bytes); | u_int func, u_int reg, int bytes); | ||||
static void generic_pcie_write_config(device_t dev, u_int bus, u_int slot, | static void generic_pcie_write_config(device_t dev, u_int bus, u_int slot, | ||||
u_int func, u_int reg, uint32_t val, int bytes); | u_int func, u_int reg, uint32_t val, int bytes); | ||||
static int generic_pcie_release_resource(device_t dev, device_t child, | static int generic_pcie_release_resource(device_t dev, device_t child, | ||||
int type, int rid, struct resource *res); | int type, int rid, struct resource *res); | ||||
static ACPI_STATUS pci_host_generic_acpi_parse_resource(ACPI_RESOURCE *, void *); | |||||
static void pci_host_generic_acpi_apply_quirks(device_t); | |||||
static void thunderx2_ahci_bar_quirk(device_t); | |||||
static void thunderx2_ecam_base_quirk(device_t); | |||||
struct pci_host_generic_acpi_quirk_entry pci_host_generic_acpi_quirks[] = | |||||
{ | |||||
{CPU_IMPL_CAVIUM, CPU_PART_THUNDERX2, 0, 0, thunderx2_ecam_base_quirk}, | |||||
{CPU_IMPL_CAVIUM, CPU_PART_THUNDERX2, 0, 0, thunderx2_ahci_bar_quirk}, | |||||
{0, 0, 0, 0, NULL} | |||||
Not Done Inline ActionsCan you split the quirks out into a new review? It is mostly separate from the other changes. andrew: Can you split the quirks out into a new review? It is mostly separate from the other changes. | |||||
}; | |||||
struct pci_host_generic_acpi_block_entry pci_host_generic_acpi_blocked[] = | |||||
{ | |||||
/* ThunderX2 AHCI on second socket */ | |||||
{CPU_IMPL_CAVIUM, CPU_PART_THUNDERX2, 0, 0, 0x80, 0x10}, | |||||
{0, 0, 0, 0, 0, 0} | |||||
}; | |||||
static int | static int | ||||
generic_pcie_acpi_probe(device_t dev) | generic_pcie_acpi_probe(device_t dev) | ||||
{ | { | ||||
ACPI_DEVICE_INFO *devinfo; | ACPI_DEVICE_INFO *devinfo; | ||||
ACPI_HANDLE h; | ACPI_HANDLE h; | ||||
int root; | int root; | ||||
if (acpi_disabled("pcib") || (h = acpi_get_handle(dev)) == NULL || | if (acpi_disabled("pcib") || (h = acpi_get_handle(dev)) == NULL || | ||||
ACPI_FAILURE(AcpiGetObjectInfo(h, &devinfo))) | ACPI_FAILURE(AcpiGetObjectInfo(h, &devinfo))) | ||||
return (ENXIO); | return (ENXIO); | ||||
root = (devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0; | root = (devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0; | ||||
AcpiOsFree(devinfo); | AcpiOsFree(devinfo); | ||||
if (!root) | if (!root) | ||||
return (ENXIO); | return (ENXIO); | ||||
device_set_desc(dev, "Generic PCI host controller"); | device_set_desc(dev, "Generic PCI host controller"); | ||||
return (BUS_PROBE_GENERIC); | return (BUS_PROBE_GENERIC); | ||||
} | } | ||||
int | int | ||||
pci_host_generic_acpi_attach(device_t dev) | pci_host_generic_acpi_attach(device_t dev) | ||||
{ | { | ||||
struct generic_pcie_acpi_softc *sc; | struct generic_pcie_acpi_softc *sc; | ||||
ACPI_HANDLE handle; | ACPI_HANDLE handle; | ||||
ACPI_STATUS status; | |||||
int error; | int error; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
handle = acpi_get_handle(dev); | handle = acpi_get_handle(dev); | ||||
if (ACPI_FAILURE(acpi_GetInteger(handle, "_CCA", &sc->base.coherent))) | if (ACPI_FAILURE(acpi_GetInteger(handle, "_CCA", &sc->base.coherent))) | ||||
sc->base.coherent = 0; | sc->base.coherent = 0; | ||||
if (bootverbose) | if (bootverbose) | ||||
device_printf(dev, "Bus is%s cache-coherent\n", | device_printf(dev, "Bus is%s cache-coherent\n", | ||||
sc->base.coherent ? "" : " not"); | sc->base.coherent ? "" : " not"); | ||||
acpi_pcib_fetch_prt(dev, &sc->ap_prt); | acpi_pcib_fetch_prt(dev, &sc->ap_prt); | ||||
error = pci_host_generic_core_attach(dev); | error = pci_host_generic_core_attach(dev); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
status = AcpiWalkResources(handle, "_CRS", | |||||
pci_host_generic_acpi_parse_resource, (void *)dev); | |||||
Not Done Inline ActionsAre these encoded as Producer resources instead of Consumer resources? jhb: Are these encoded as Producer resources instead of Consumer resources? | |||||
Not Done Inline ActionsThey are encoded as Producer resources. pdk_semihalf.com: They are encoded as Producer resources. | |||||
if (ACPI_FAILURE(status)) | |||||
return (ENXIO); | |||||
device_add_child(dev, "pci", -1); | device_add_child(dev, "pci", -1); | ||||
pci_host_generic_acpi_apply_quirks(dev); | |||||
return (bus_generic_attach(dev)); | return (bus_generic_attach(dev)); | ||||
} | } | ||||
static ACPI_STATUS | |||||
pci_host_generic_acpi_parse_resource(ACPI_RESOURCE *res, void *arg) | |||||
{ | |||||
device_t dev = (device_t)arg; | |||||
struct generic_pcie_acpi_softc *sc; | |||||
rman_res_t min, max; | |||||
int error; | |||||
switch (res->Type) { | |||||
case ACPI_RESOURCE_TYPE_ADDRESS32: | |||||
min = (rman_res_t)res->Data.Address32.Address.Minimum; | |||||
max = (rman_res_t)res->Data.Address32.Address.Maximum; | |||||
break; | |||||
case ACPI_RESOURCE_TYPE_ADDRESS64: | |||||
min = (rman_res_t)res->Data.Address64.Address.Minimum; | |||||
max = (rman_res_t)res->Data.Address64.Address.Maximum; | |||||
break; | |||||
default: | |||||
return (AE_OK); | |||||
} | |||||
sc = device_get_softc(dev); | |||||
error = rman_manage_region(&sc->base.mem_rman, min, max); | |||||
if (error) { | |||||
device_printf(dev, "unable to allocate %lx-%lx range\n", min, max); | |||||
return (AE_NOT_FOUND); | |||||
} | |||||
device_printf(dev, "allocating %lx-%lx range\n", min, max); | |||||
return (AE_OK); | |||||
} | |||||
static void | |||||
pci_host_generic_acpi_apply_quirks(device_t dev) | |||||
{ | |||||
struct pci_host_generic_acpi_quirk_entry *quirk; | |||||
quirk = pci_host_generic_acpi_quirks; | |||||
while (1) { | |||||
if (quirk->impl == 0) | |||||
break; | |||||
if (CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK, | |||||
quirk->impl, quirk->part, quirk->var, quirk->rev) && | |||||
quirk->func != NULL) | |||||
quirk->func(dev); | |||||
quirk++; | |||||
} | |||||
} | |||||
static uint32_t | |||||
pci_host_generic_acpi_read_config(device_t dev, u_int bus, u_int slot, | |||||
u_int func, u_int reg, int bytes) | |||||
{ | |||||
struct pci_host_generic_acpi_block_entry *block; | |||||
block = pci_host_generic_acpi_blocked; | |||||
while (1) { | |||||
if (block->impl == 0) | |||||
break; | |||||
if (CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK, | |||||
block->impl, block->part, block->var, block->rev) && | |||||
block->bus == bus && block->slot == slot) | |||||
return (~0); | |||||
block++; | |||||
} | |||||
return generic_pcie_read_config(dev, bus, slot, func, reg, bytes); | |||||
} | |||||
static int | static int | ||||
generic_pcie_acpi_route_interrupt(device_t bus, device_t dev, int pin) | generic_pcie_acpi_route_interrupt(device_t bus, device_t dev, int pin) | ||||
{ | { | ||||
struct generic_pcie_acpi_softc *sc; | struct generic_pcie_acpi_softc *sc; | ||||
sc = device_get_softc(bus); | sc = device_get_softc(bus); | ||||
return (acpi_pcib_route_interrupt(bus, dev, pin, &sc->ap_prt)); | return (acpi_pcib_route_interrupt(bus, dev, pin, &sc->ap_prt)); | ||||
Show All 14 Lines | generic_pcie_acpi_rman(struct generic_pcie_acpi_softc *sc, int type) | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static struct resource * | static struct resource * | ||||
pci_host_generic_acpi_alloc_resource(device_t dev, device_t child, int type, | pci_host_generic_acpi_alloc_resource(device_t dev, device_t child, int type, | ||||
int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) | int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) | ||||
{ | { | ||||
struct resource *res = NULL; | |||||
#if defined(NEW_PCIB) && defined(PCI_RES_BUS) | #if defined(NEW_PCIB) && defined(PCI_RES_BUS) | ||||
struct generic_pcie_acpi_softc *sc; | struct generic_pcie_acpi_softc *sc; | ||||
if (type == PCI_RES_BUS) { | if (type == PCI_RES_BUS) { | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
return (pci_domain_alloc_bus(sc->base.ecam, child, rid, start, | return (pci_domain_alloc_bus(sc->base.ecam, child, rid, start, | ||||
end, count, flags)); | end, count, flags)); | ||||
} | } | ||||
#endif | #endif | ||||
return (bus_generic_alloc_resource(dev, child, type, rid, start, end, | if (type == SYS_RES_MEMORY) | ||||
count, flags)); | res = pci_host_generic_core_alloc_resource(dev, child, type, | ||||
rid, start, end, count, flags); | |||||
if (res == NULL) | |||||
res = bus_generic_alloc_resource(dev, child, type, rid, start, end, | |||||
count, flags); | |||||
return (res); | |||||
Not Done Inline ActionsWhy is this only needed for memory? andrew: Why is this only needed for memory? | |||||
Not Done Inline ActionsIn pci_host_generic_acpi_parse_resource we add memory regions to rman. Here we are allocating memory from it. pdk_semihalf.com: In `pci_host_generic_acpi_parse_resource` we add memory regions to rman. Here we are… | |||||
} | } | ||||
static int | static int | ||||
generic_pcie_acpi_activate_resource(device_t dev, device_t child, int type, | generic_pcie_acpi_activate_resource(device_t dev, device_t child, int type, | ||||
int rid, struct resource *r) | int rid, struct resource *r) | ||||
{ | { | ||||
struct generic_pcie_acpi_softc *sc; | struct generic_pcie_acpi_softc *sc; | ||||
int res; | int res; | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | static device_method_t generic_pcie_acpi_methods[] = { | ||||
DEVMETHOD(bus_activate_resource, generic_pcie_acpi_activate_resource), | DEVMETHOD(bus_activate_resource, generic_pcie_acpi_activate_resource), | ||||
DEVMETHOD(bus_deactivate_resource, generic_pcie_acpi_deactivate_resource), | DEVMETHOD(bus_deactivate_resource, generic_pcie_acpi_deactivate_resource), | ||||
/* pcib interface */ | /* pcib interface */ | ||||
DEVMETHOD(pcib_route_interrupt, generic_pcie_acpi_route_interrupt), | DEVMETHOD(pcib_route_interrupt, generic_pcie_acpi_route_interrupt), | ||||
DEVMETHOD(pcib_alloc_msi, generic_pcie_acpi_alloc_msi), | DEVMETHOD(pcib_alloc_msi, generic_pcie_acpi_alloc_msi), | ||||
DEVMETHOD(pcib_release_msi, generic_pcie_acpi_release_msi), | DEVMETHOD(pcib_release_msi, generic_pcie_acpi_release_msi), | ||||
DEVMETHOD(pcib_alloc_msix, generic_pcie_acpi_alloc_msix), | DEVMETHOD(pcib_alloc_msix, generic_pcie_acpi_alloc_msix), | ||||
DEVMETHOD(pcib_read_config, pci_host_generic_acpi_read_config), | |||||
DEVMETHOD(pcib_release_msix, generic_pcie_acpi_release_msix), | DEVMETHOD(pcib_release_msix, generic_pcie_acpi_release_msix), | ||||
DEVMETHOD(pcib_map_msi, generic_pcie_acpi_map_msi), | DEVMETHOD(pcib_map_msi, generic_pcie_acpi_map_msi), | ||||
DEVMETHOD(pcib_get_id, generic_pcie_acpi_get_id), | DEVMETHOD(pcib_get_id, generic_pcie_acpi_get_id), | ||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
DEFINE_CLASS_1(pcib, generic_pcie_acpi_driver, generic_pcie_acpi_methods, | DEFINE_CLASS_1(pcib, generic_pcie_acpi_driver, generic_pcie_acpi_methods, | ||||
sizeof(struct generic_pcie_acpi_softc), generic_pcie_core_driver); | sizeof(struct generic_pcie_acpi_softc), generic_pcie_core_driver); | ||||
static devclass_t generic_pcie_acpi_devclass; | static devclass_t generic_pcie_acpi_devclass; | ||||
DRIVER_MODULE(pcib, acpi, generic_pcie_acpi_driver, generic_pcie_acpi_devclass, | DRIVER_MODULE(pcib, acpi, generic_pcie_acpi_driver, generic_pcie_acpi_devclass, | ||||
0, 0); | 0, 0); | ||||
static void thunderx2_ahci_bar_quirk(device_t dev) | |||||
Not Done Inline Actionsdo you know if these are temporary quirks for pre-prod systems only? emaste: do you know if these are temporary quirks for pre-prod systems only? | |||||
Not Done Inline ActionsThese quirks are necessary for current ThunderX2 revision. I have no information if there will be some future revisions with these things fixed. pdk_semihalf.com: These quirks are necessary for current ThunderX2 revision. I have no information if there will… | |||||
{ | |||||
/* | |||||
* XXX: | |||||
* On ThunderX2, AHCI BAR2 address is wrong. It needs to precisely | |||||
* match the one described in datasheet. Fixup it unconditionally. | |||||
Not Done Inline ActionsIs there an erratum for this? I don't see anything in Linux, or my copy of the ThunderX2 known issues document. andrew: Is there an erratum for this? I don't see anything in Linux, or my copy of the ThunderX2 known… | |||||
*/ | |||||
if (device_get_unit(dev) == 0) { | |||||
device_printf(dev, "running AHCI BAR fixup\n"); | |||||
PCIB_WRITE_CONFIG(dev, 0, 16, 0, 0x18, 0x01440000, 4); | |||||
PCIB_WRITE_CONFIG(dev, 0, 16, 0, 0x1c, 0x40, 4); | |||||
PCIB_WRITE_CONFIG(dev, 0, 16, 1, 0x18, 0x01450000, 4); | |||||
PCIB_WRITE_CONFIG(dev, 0, 16, 1, 0x1c, 0x40, 4); | |||||
} | |||||
} | |||||
static void thunderx2_ecam_base_quirk(device_t dev) | |||||
{ | |||||
struct generic_pcie_acpi_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
/* | |||||
* XXX: | |||||
* On Thunder X2, pcib16 is a beginning of second ECAM domain. | |||||
*/ | |||||
if (device_get_unit(dev) == 16) | |||||
sc->base.ecam = 1; | |||||
} |
The previous statement is the standard one for Foundation-sponsored work, however I am not aware of the history of this file off hand.