From c1ea44aa33b29f74daed89eee82b3dfeb105d376 Mon Sep 17 00:00:00 2001 From: Greg V Date: Fri, 22 May 2020 19:01:28 +0300 Subject: [PATCH] WIP: Add support for the NXP Layerscape PCIe Gen4 controller --- sys/arm64/conf/GENERIC | 1 + sys/conf/files.arm64 | 1 + sys/dev/pci/controller/pci_layerscape.c | 319 ++++++++++++++++++++++++ 3 files changed, 321 insertions(+) create mode 100644 sys/dev/pci/controller/pci_layerscape.c diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC index 5e16c8f419900a..37b8583fd67739 100644 --- a/sys/arm64/conf/GENERIC +++ b/sys/arm64/conf/GENERIC @@ -150,6 +150,7 @@ device cpufreq # Bus drivers device pci device pci_n1sdp # ARM Neoverse N1 SDP PCI +device pci_layerscape # NXP Layerscape LX2160 PCI device al_pci # Annapurna Alpine PCI-E options PCI_HP # PCI-Express native HotPlug options PCI_IOV # PCI SR-IOV support diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index ffbe87877b732c..dfe319d9b58c4c 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -344,6 +344,7 @@ dev/neta/if_mvneta.c optional neta mdio mii dev/ofw/ofw_cpu.c optional fdt dev/ofw/ofwpci.c optional fdt pci dev/pci/controller/pci_n1sdp.c optional pci_n1sdp acpi +dev/pci/controller/pci_layerscape.c optional pci_layerscape acpi dev/pci/pci_host_generic.c optional pci dev/pci/pci_host_generic_acpi.c optional pci acpi dev/pci/pci_host_generic_fdt.c optional pci fdt diff --git a/sys/dev/pci/controller/pci_layerscape.c b/sys/dev/pci/controller/pci_layerscape.c new file mode 100644 index 00000000000000..6eb394ee082df2 --- /dev/null +++ b/sys/dev/pci/controller/pci_layerscape.c @@ -0,0 +1,319 @@ +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jared McNeill . + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * NXP Layerscape PCIe Gen4 controller (not ECAM compliant) + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "pcib_if.h" + +#define __BIT(__n) (((__n) == 32) ? 0 : ((uint32_t)1 << (__n))) +#define __BITS(__m, __n) \ + ((__BIT(MAX((__m), (__n)) + 1) - 1) ^ (__BIT(MIN((__m), (__n))) - 1)) +#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) +#define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask)) + +#define PCI_ID_REG 0 + +#define PAB_CTRL 0x808 +#define PAB_CTRL_PAGE_SEL __BITS(18,13) +#define PAB_AXI_AMAP_PEX_WIN_L(x) (0xba8 + 0x10 * (x)) +#define PAB_AXI_AMAP_PEX_WIN_H(x) (0xbac + 0x10 * (x)) +#define INDIRECT_ADDR_BOUNDARY 0xc00 + +#define LUT_BASE 0x80000 +#define LUT_GCR 0x28 +#define LUT_GCR_RRE __BIT(0) + +#define REG_TO_PAGE_INDEX(reg) (((reg) >> 10) & 0x3ff) +#define REG_TO_PAGE_ADDR(reg) (((reg) & 0x3ff) | INDIRECT_ADDR_BOUNDARY) + +#define PAB_TARGET_BUS(b) ((b) << 24) +#define PAB_TARGET_DEV(d) ((d) << 19) +#define PAB_TARGET_FUNC(f) ((f) << 16) + +struct layerscape_pcie_acpi_softc { + struct generic_pcie_acpi_softc acpi; + + bus_space_handle_t bsh; + uint8_t rev; + struct mtx lock; +}; + +static void +layerscape_ccsr_setpage(struct layerscape_pcie_acpi_softc *sc, u_int page_index) +{ + uint32_t val; + + val = bus_space_read_4(sc->acpi.base.bst, sc->bsh, PAB_CTRL); + val &= ~PAB_CTRL_PAGE_SEL; + val |= __SHIFTIN(page_index, PAB_CTRL_PAGE_SEL); + bus_space_write_4(sc->acpi.base.bst, sc->bsh, PAB_CTRL, val); +} + +static uint32_t +layerscape_ccsr_read4(struct layerscape_pcie_acpi_softc *sc, bus_size_t reg) +{ + const bool indirect = reg >= INDIRECT_ADDR_BOUNDARY; + const u_int page_index = indirect ? REG_TO_PAGE_INDEX(reg) : 0; + const bus_size_t page_addr = indirect ? REG_TO_PAGE_ADDR(reg) : reg; + + layerscape_ccsr_setpage(sc, page_index); + return (bus_space_read_4(sc->acpi.base.bst, sc->bsh, page_addr)); +} + +static void +layerscape_ccsr_write4(struct layerscape_pcie_acpi_softc *sc, + bus_size_t reg, uint32_t data) +{ + const bool indirect = reg >= INDIRECT_ADDR_BOUNDARY; + const u_int page_index = indirect ? REG_TO_PAGE_INDEX(reg) : 0; + const bus_size_t page_addr = indirect ? REG_TO_PAGE_ADDR(reg) : reg; + + layerscape_ccsr_setpage(sc, page_index); + bus_space_write_4(sc->acpi.base.bst, sc->bsh, page_addr, data); +} + +static void +layerscape_select_target(struct layerscape_pcie_acpi_softc *sc, + u_int bus, u_int slot, u_int func) +{ + const uint32_t target = PAB_TARGET_BUS(bus) | + PAB_TARGET_DEV(slot) | PAB_TARGET_FUNC(func); + + layerscape_ccsr_write4(sc, PAB_AXI_AMAP_PEX_WIN_L(0), target); + layerscape_ccsr_write4(sc, PAB_AXI_AMAP_PEX_WIN_H(0), 0); +} + + +static uint32_t +layerscape_pcie_read_config(device_t dev, u_int bus, u_int slot, + u_int func, u_int reg, int bytes) +{ + struct layerscape_pcie_acpi_softc *sc = device_get_softc(dev); + uint32_t result; + + if ((bus <= sc->acpi.base.bus_start + 1) && (slot > 0)) + return (~0U); + if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) || + (reg > PCIE_REGMAX)) + return (~0U); + /* XXX: NetBSD does acpimcfg_conf_valid for bus != bus_start */ + + mtx_lock(&sc->lock); + + if (sc->rev == 0x10 && reg == PCI_ID_REG) + bus_space_write_4(sc->acpi.base.bst, sc->bsh, LUT_BASE + LUT_GCR, 0); + + if (bus == sc->acpi.base.bus_start) { + result = layerscape_ccsr_read4(sc, reg); + } else { + layerscape_select_target(sc, bus, slot, func); + result = bus_space_read_4(sc->acpi.base.bst, sc->acpi.base.bsh, reg); + } + + if (sc->rev == 0x10 && reg == PCI_ID_REG) + bus_space_write_4(sc->acpi.base.bst, sc->bsh, LUT_BASE + LUT_GCR, LUT_GCR_RRE); + + mtx_unlock(&sc->lock); + return (result); +} + +static void +layerscape_pcie_write_config(device_t dev, u_int bus, u_int slot, + u_int func, u_int reg, uint32_t data, int bytes) +{ + struct layerscape_pcie_acpi_softc *sc = device_get_softc(dev); + + if ((bus <= sc->acpi.base.bus_start + 1) && (slot > 0)) + return; + if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) || + (reg > PCIE_REGMAX)) + return; + /* XXX: NetBSD does acpimcfg_conf_valid for bus != bus_start */ + + mtx_lock(&sc->lock); + + if (bus == sc->acpi.base.bus_start) { + layerscape_ccsr_write4(sc, reg, data); + } else { + layerscape_select_target(sc, bus, slot, func); + bus_space_write_4(sc->acpi.base.bst, sc->acpi.base.bsh, reg, data); + } + + mtx_unlock(&sc->lock); +} + +static int +layerscape_pcie_acpi_probe(device_t dev) +{ + ACPI_DEVICE_INFO *devinfo; + ACPI_TABLE_HEADER *hdr; + ACPI_STATUS status; + ACPI_HANDLE h; + int 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); + + /* TODO: Move this to an ACPI quirk? */ + status = AcpiGetTable(ACPI_SIG_MCFG, 1, &hdr); + if (ACPI_FAILURE(status)) + return (ENXIO); + + if (memcmp(hdr->OemId, "NXP ", ACPI_OEM_ID_SIZE) != 0 || + memcmp(hdr->OemTableId, "LX2160 ", ACPI_OEM_TABLE_ID_SIZE) != 0) + return (ENXIO); + + device_set_desc(dev, "NXP Layerscape PCI host controller"); + return (BUS_PROBE_DEFAULT); +} + +static ACPI_STATUS +nxp0016_resource_handler(ACPI_RESOURCE *res, void *context) +{ + device_t dev; + struct layerscape_pcie_acpi_softc *sc; + vm_offset_t base_addr; + size_t length; + + if (res->Type != ACPI_RESOURCE_TYPE_FIXED_MEMORY32) + return (AE_OK); + + dev = context; + device_printf(dev, "found the memory resource!"); + sc = device_get_softc(dev); + + base_addr = (vm_offset_t)res->Data.FixedMemory32.Address; + length = res->Data.FixedMemory32.AddressLength; + + if (bus_space_map(sc->acpi.base.bst, base_addr, length, 0, &sc->bsh) != 0) + return (AE_ERROR); + + device_printf(dev, "mapped the memory resource!"); + + return (AE_OK); +} + +static ACPI_STATUS +child_device_handler(ACPI_HANDLE handle, UINT32 level, + void *context, void **status) +{ + // ACPI_HANDLE h; + + // ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + if (!acpi_MatchHid(handle, "NXP0016")) + return (AE_OK); + + device_printf((device_t)context, "found the nxp0016!"); + + return (AcpiWalkResources(handle, "_CRS", + nxp0016_resource_handler, context)); +} + +static int +layerscape_pcie_acpi_attach(device_t dev) +{ + struct layerscape_pcie_acpi_softc *sc; + // ACPI_HANDLE handle; + // ACPI_STATUS status; + int err; + + err = pci_host_generic_acpi_init(dev); + if (err != 0) + return (err); + + sc = device_get_softc(dev); + // handle = acpi_get_handle(dev); + + // _SEG: sc->acpi.base.ecam + AcpiWalkNamespace(ACPI_TYPE_DEVICE, acpi_get_handle(dev), 100, + child_device_handler, NULL, dev, NULL); + + sc->rev = bus_space_read_4(sc->acpi.base.bst, sc->bsh, 0x08) & 0xff; + device_printf(dev, "Controller revision %#x\n", sc->rev); + + device_add_child(dev, "pci", -1); + return (bus_generic_attach(dev)); +} + +static device_method_t layerscape_pcie_acpi_methods[] = { + DEVMETHOD(device_probe, layerscape_pcie_acpi_probe), + DEVMETHOD(device_attach, layerscape_pcie_acpi_attach), + + /* pcib interface */ + DEVMETHOD(pcib_read_config, layerscape_pcie_read_config), + DEVMETHOD(pcib_write_config, layerscape_pcie_write_config), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(pcib, layerscape_pcie_acpi_driver, layerscape_pcie_acpi_methods, + sizeof(struct layerscape_pcie_acpi_softc), generic_pcie_acpi_driver); + +static devclass_t layerscape_pcie_acpi_devclass; + +DRIVER_MODULE(layerscape_pcib, acpi, layerscape_pcie_acpi_driver, + layerscape_pcie_acpi_devclass, 0, 0);