diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -359,6 +359,7 @@ dev/pci/controller/pci_n1sdp.c optional pci_n1sdp acpi dev/pci/pci_host_generic.c optional pci dev/pci/pci_host_generic_acpi.c optional pci acpi +dev/pci/pci_host_generic_den0115.c optional pci acpi dev/pci/pci_host_generic_fdt.c optional pci fdt dev/pci/pci_dw_mv.c optional pci fdt dev/pci/pci_dw.c optional pci fdt diff --git a/sys/dev/pci/pci_host_generic_den0115.c b/sys/dev/pci/pci_host_generic_den0115.c new file mode 100644 --- /dev/null +++ b/sys/dev/pci/pci_host_generic_den0115.c @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2022 Andrew Turner + * Copyright (c) 2023 Arm Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "pcib_if.h" + +static device_probe_t pci_host_acpi_smccc_probe; +static device_attach_t pci_host_acpi_smccc_attach; +static pcib_read_config_t pci_host_acpi_smccc_read_config; +static pcib_write_config_t pci_host_acpi_smccc_write_config; + +static int +pci_host_acpi_smccc_probe(device_t dev) +{ + ACPI_DEVICE_INFO *devinfo; + struct resource *res; + ACPI_HANDLE h; + uint32_t smccc_version; + int rid, root; + + if (acpi_disabled("pcib") || (h = acpi_get_handle(dev)) == NULL || + ACPI_FAILURE(AcpiGetObjectInfo(h, &devinfo))) + return (ENXIO); + root = (devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0; + AcpiOsFree(devinfo); + if (!root) + return (ENXIO); + + /* + * Check we have memory resources. We may have a non-memory + * mapped device, e.g. using the Arm PCI Configuration Space + * Access Firmware Interface (DEN0115). + */ + res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 0); + if (res != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, rid, res); + return (ENXIO); + } + + /* Check the SMCCC is at least v1.1 */ + smccc_version = smccc_get_version(); + if (SMCCC_VERSION_MAJOR(smccc_version) > 1 || + (SMCCC_VERSION_MAJOR(smccc_version) == 1 && + SMCCC_VERSION_MINOR(smccc_version) == 0)) { + device_set_desc(dev, + "ARM PCI Firmware config space host controller"); + return (BUS_PROBE_SPECIFIC); + } + + return (EINVAL); +} + +#define SMCCC_PCI_VERSION \ + SMCCC_FUNC_ID(SMCCC_FAST_CALL, SMCCC_32BIT_CALL, \ + SMCCC_STD_SECURE_SERVICE_CALLS, 0x130) +#define SMCCC_PCI_FEATURES \ + SMCCC_FUNC_ID(SMCCC_FAST_CALL, SMCCC_32BIT_CALL, \ + SMCCC_STD_SECURE_SERVICE_CALLS, 0x131) +#define SMCCC_PCI_READ \ + SMCCC_FUNC_ID(SMCCC_FAST_CALL, SMCCC_32BIT_CALL, \ + SMCCC_STD_SECURE_SERVICE_CALLS, 0x132) +#define SMCCC_PCI_WRITE \ + SMCCC_FUNC_ID(SMCCC_FAST_CALL, SMCCC_32BIT_CALL, \ + SMCCC_STD_SECURE_SERVICE_CALLS, 0x133) +#define SMCCC_PCI_GET_SEG_INFO \ + SMCCC_FUNC_ID(SMCCC_FAST_CALL, SMCCC_32BIT_CALL, \ + SMCCC_STD_SECURE_SERVICE_CALLS, 0x134) + +CTASSERT(SMCCC_PCI_VERSION == 0x84000130); +CTASSERT(SMCCC_PCI_FEATURES == 0x84000131); +CTASSERT(SMCCC_PCI_READ == 0x84000132); +CTASSERT(SMCCC_PCI_WRITE == 0x84000133); +CTASSERT(SMCCC_PCI_GET_SEG_INFO == 0x84000134); + +#define SMCCC_PCI_MAJOR(x) (((x) >> 16) & 0x7fff) +#define SMCCC_PCI_MINOR(x) ((x) & 0xffff) + +#define SMCCC_PCI_SEG_END(x) (((x) >> 8) & 0xff) +#define SMCCC_PCI_SEG_START(x) ((x) & 0xff) + +static bool +pci_host_acpi_smccc_has_feature(uint32_t pci_func_id) +{ + struct arm_smccc_res result; + + if (psci_callfn(SMCCC_PCI_FEATURES, pci_func_id, 0, 0, 0, 0, 0, 0, + &result) < 0) { + return (false); + } + + return (true); +} + +static int +pci_host_acpi_smccc_attach(device_t dev) +{ + struct generic_pcie_acpi_softc *sc; + struct arm_smccc_res result; + int end, start; + int error; + + sc = device_get_softc(dev); + sc->base.quirks |= PCIE_CUSTOM_CONFIG_SPACE_QUIRK; + + MPASS(psci_callfn != NULL); + + /* Check there is a version */ + if (psci_callfn(SMCCC_PCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &result) < 0) { + device_printf(dev, + "Failed to read the SMCCC PCI version: %lx\n", result.a0); + return (ENXIO); + } + + if (bootverbose) { + device_printf(dev, "Firmware v%d.%d\n", + SMCCC_PCI_MAJOR((uint32_t)result.a0), + SMCCC_PCI_MINOR((uint32_t)result.a0)); + } + + if (!pci_host_acpi_smccc_has_feature(SMCCC_PCI_READ) || + !pci_host_acpi_smccc_has_feature(SMCCC_PCI_WRITE)) { + device_printf(dev, "Missing read/write functions\n"); + return (ENXIO); + } + + error = pci_host_generic_acpi_init(dev); + if (error != 0) + return (error); + + if (pci_host_acpi_smccc_has_feature(SMCCC_PCI_GET_SEG_INFO) && + psci_callfn(SMCCC_PCI_GET_SEG_INFO, sc->base.ecam, 0, 0, 0, 0, 0, + 0, &result) == SMCCC_RET_SUCCESS) { + start = SMCCC_PCI_SEG_START(result.a1); + end = SMCCC_PCI_SEG_END(result.a1); + + sc->base.bus_start = MAX(sc->base.bus_start, start); + sc->base.bus_end = MIN(sc->base.bus_end, end); + } + + device_add_child(dev, "pci", -1); + return (bus_generic_attach(dev)); +} + +static uint32_t +pci_host_acpi_smccc_read_config(device_t dev, u_int bus, u_int slot, + u_int func, u_int reg, int bytes) +{ + struct generic_pcie_acpi_softc *sc; + struct arm_smccc_res result; + uint32_t addr; + + sc = device_get_softc(dev); + + if ((bus < sc->base.bus_start) || (bus > sc->base.bus_end)) + return (~0U); + if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) || + (reg > PCIE_REGMAX)) + return (~0U); + + addr = (sc->base.ecam << 16) | (bus << 8) | (slot << 3) | (func << 0); + if (psci_callfn(SMCCC_PCI_READ, addr, reg, bytes, 0, 0, 0, 0, + &result) < 0) { + return (~0U); + } + + return (result.a1); +} + +static void +pci_host_acpi_smccc_write_config(device_t dev, u_int bus, u_int slot, + u_int func, u_int reg, uint32_t val, int bytes) +{ + struct generic_pcie_acpi_softc *sc; + struct arm_smccc_res result; + uint32_t addr; + + sc = device_get_softc(dev); + + if ((bus < sc->base.bus_start) || (bus > sc->base.bus_end)) + return; + if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) || + (reg > PCIE_REGMAX)) + return; + + addr = (sc->base.ecam << 16) | (bus << 8) | (slot << 3) | (func << 0); + psci_callfn(SMCCC_PCI_WRITE, addr, reg, bytes, val, 0, 0, 0, &result); +} + +static device_method_t generic_pcie_acpi_smccc_methods[] = { + DEVMETHOD(device_probe, pci_host_acpi_smccc_probe), + DEVMETHOD(device_attach, pci_host_acpi_smccc_attach), + + /* pcib interface */ + DEVMETHOD(pcib_read_config, pci_host_acpi_smccc_read_config), + DEVMETHOD(pcib_write_config, pci_host_acpi_smccc_write_config), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(pcib, generic_pcie_acpi_smccc_driver, + generic_pcie_acpi_smccc_methods, + sizeof(struct generic_pcie_acpi_softc), generic_pcie_acpi_driver); + +DRIVER_MODULE(pcib_smccc, acpi, generic_pcie_acpi_smccc_driver, 0, 0);