Skip to content
This repository has been archived by the owner on Jun 15, 2023. It is now read-only.

Commit

Permalink
WIP: Add support for the NXP Layerscape PCIe Gen4 controller
Browse files Browse the repository at this point in the history
  • Loading branch information
valpackett committed Jul 3, 2020
1 parent 5ef7056 commit c1ea44a
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 0 deletions.
1 change: 1 addition & 0 deletions sys/arm64/conf/GENERIC
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions sys/conf/files.arm64
Expand Up @@ -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
Expand Down
319 changes: 319 additions & 0 deletions 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 <jmcneill@invisible.ca>.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/mutex.h>

#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_page.h>

#include <contrib/dev/acpica/include/acpi.h>
#include <contrib/dev/acpica/include/accommon.h>

#include <dev/acpica/acpivar.h>
#include <dev/acpica/acpi_pcibvar.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcib_private.h>
#include <dev/pci/pci_host_generic.h>
#include <dev/pci/pci_host_generic_acpi.h>

#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);

0 comments on commit c1ea44a

Please sign in to comment.