Author: andrew
Date: Mon Jul  6 08:51:55 2020
New Revision: 362954
URL: https://svnweb.freebsd.org/changeset/base/362954

Log:
  Add a driver for bcm2838 PCI express controller
  
  This adds support for the Broadcom bcm2711 PCI express controller, found
  on the Raspberry Pi 4 (aka the bcm2838 SoC). The driver has only been
  developed against the soldered-on VIA XHCI controller and not tested
  with other end points.
  
  Submitted by: Robert Crowston <crowston_protonmail.com>
  Differential Revision:        https://reviews.freebsd.org/D25068

Added:
  head/sys/arm/broadcom/bcm2835/bcm2838_pci.c   (contents, props changed)
Modified:
  head/sys/arm/broadcom/bcm2835/files.bcm283x
  head/sys/conf/files.arm64
  head/sys/dev/pci/pci_host_generic_fdt.c
  head/sys/dev/pci/pci_host_generic_fdt.h

Added: head/sys/arm/broadcom/bcm2835/bcm2838_pci.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm/broadcom/bcm2835/bcm2838_pci.c Mon Jul  6 08:51:55 2020        
(r362954)
@@ -0,0 +1,743 @@
+/*-
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2020 Dr Robert Harvey Crowston <crows...@protonmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * BCM2838-compatible PCI-express controller.
+ *
+ * Broadcom likes to give the same chip lots of different names. The name of
+ * this driver is taken from the Raspberry Pi 4 Broadcom 2838 chip.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/intr.h>
+#include <sys/mutex.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/pci/pci_host_generic.h>
+#include <dev/pci/pci_host_generic_fdt.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include "pcib_if.h"
+#include "msi_if.h"
+
+extern struct bus_space memmap_bus;
+
+#define BUS_SPACE_3G_MAXADDR   0xc0000000
+#define PCI_ID_VAL3            0x43c
+#define CLASS_SHIFT            0x10
+#define SUBCLASS_SHIFT         0x8
+
+#define REG_CONTROLLER_HW_REV                  0x406c
+#define REG_BRIDGE_CTRL                                0x9210
+#define BRIDGE_DISABLE_FLAG    0x1
+#define BRIDGE_RESET_FLAG      0x2
+#define REG_BRIDGE_SERDES_MODE                 0x4204
+#define REG_BRIDGE_CONFIG                      0x4008
+#define REG_BRIDGE_MEM_WINDOW_LOW              0x4034
+#define REG_BRIDGE_MEM_WINDOW_HIGH             0x4038
+#define REG_BRIDGE_MEM_WINDOW_1                        0x403c
+#define REG_BRIDGE_GISB_WINDOW                 0x402c
+#define REG_BRIDGE_STATE                       0x4068
+#define REG_BRIDGE_LINK_STATE                  0x00bc
+#define REG_BRIDGE_BUS_WINDOW_LOW              0x400c
+#define REG_BRIDGE_BUS_WINDOW_HIGH             0x4010
+#define REG_BRIDGE_CPU_WINDOW_LOW              0x4070
+#define REG_BRIDGE_CPU_WINDOW_START_HIGH       0x4080
+#define REG_BRIDGE_CPU_WINDOW_END_HIGH         0x4084
+
+#define REG_MSI_ADDR_LOW                       0x4044
+#define REG_MSI_ADDR_HIGH                      0x4048
+#define REG_MSI_CONFIG                         0x404c
+#define REG_MSI_CLR                            0x4508
+#define REG_MSI_MASK_CLR                       0x4514
+#define REG_MSI_RAISED                         0x4500
+#define REG_MSI_EOI                            0x4060
+#define NUM_MSI                        32
+
+#define REG_EP_CONFIG_CHOICE                   0x9000
+#define REG_EP_CONFIG_DATA                     0x8000
+
+/*
+ * These values were obtained from runtime inspection of a Linux system using a
+ * JTAG. The very limited documentation I have obtained from Broadcom does not
+ * explain how to compute them.
+ */
+#define REG_VALUE_4GB_WINDOW   0x11
+#define REG_VALUE_4GB_CONFIG   0x88003000
+#define REG_VALUE_MSI_CONFIG   0xffe06540
+
+struct bcm_pcib_irqsrc {
+       struct intr_irqsrc      isrc;
+       u_int                   irq;
+       bool                    allocated;
+};
+
+struct bcm_pcib_softc {
+       struct generic_pcie_fdt_softc   base;
+       device_t                        dev;
+       struct mtx                      config_mtx;
+       struct mtx                      msi_mtx;
+       struct resource                 *msi_irq_res;
+       void                            *msi_intr_cookie;
+       struct bcm_pcib_irqsrc          *msi_isrcs;
+       pci_addr_t                      msi_addr;
+};
+
+static struct ofw_compat_data compat_data[] = {
+       {"brcm,bcm2711-pcie",                   1},
+       {"brcm,bcm7211-pcie",                   1},
+       {"brcm,bcm7445-pcie",                   1},
+
+       {NULL,                                  0}
+};
+
+static int
+bcm_pcib_probe(device_t dev)
+{
+
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+
+       if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+               return (ENXIO);
+
+       device_set_desc(dev,
+           "BCM2838-compatible PCI-express controller");
+       return (BUS_PROBE_DEFAULT);
+}
+
+static void
+bcm_pcib_set_reg(struct bcm_pcib_softc *sc, uint32_t reg, uint32_t val)
+{
+
+       bus_space_write_4(sc->base.base.bst, sc->base.base.bsh, reg,
+           htole32(val));
+}
+
+static uint32_t
+bcm_pcib_read_reg(struct bcm_pcib_softc *sc, uint32_t reg)
+{
+
+       return (le32toh(bus_space_read_4(sc->base.base.bst, sc->base.base.bsh,
+           reg)));
+}
+
+static void
+bcm_pcib_reset_controller(struct bcm_pcib_softc *sc)
+{
+       uint32_t val;
+
+       val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
+       val = val | BRIDGE_RESET_FLAG | BRIDGE_DISABLE_FLAG;
+       bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
+
+       DELAY(100);
+
+       val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
+       val = val & ~BRIDGE_RESET_FLAG;
+       bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
+
+       DELAY(100);
+
+       bcm_pcib_set_reg(sc, REG_BRIDGE_SERDES_MODE, 0);
+
+       DELAY(100);
+}
+
+static void
+bcm_pcib_enable_controller(struct bcm_pcib_softc *sc)
+{
+       uint32_t val;
+
+       val = bcm_pcib_read_reg(sc, REG_BRIDGE_CTRL);
+       val = val & ~BRIDGE_DISABLE_FLAG;
+       bcm_pcib_set_reg(sc, REG_BRIDGE_CTRL, val);
+
+       DELAY(100);
+}
+
+static int
+bcm_pcib_check_ranges(device_t dev)
+{
+       struct bcm_pcib_softc *sc;
+       struct pcie_range *ranges;
+       int error = 0, i;
+
+       sc = device_get_softc(dev);
+       ranges = &sc->base.base.ranges[0];
+
+       /* The first range needs to be non-zero. */
+       if (ranges[0].size == 0) {
+               device_printf(dev, "error: first outbound memory range "
+                   "(pci addr: 0x%jx, cpu addr: 0x%jx) has zero size.\n",
+                   ranges[0].pci_base, ranges[0].phys_base);
+               error = ENXIO;
+       }
+
+       /*
+        * The controller can actually handle three distinct ranges, but we
+        * only implement support for one.
+        */
+       for (i = 1; (bootverbose || error) && i < MAX_RANGES_TUPLES; ++i) {
+               if (ranges[i].size > 0)
+                       device_printf(dev,
+                           "note: outbound memory range %d (pci addr: 0x%jx, "
+                           "cpu addr: 0x%jx, size: 0x%jx) will be ignored.\n",
+                           i, ranges[i].pci_base, ranges[i].phys_base,
+                           ranges[i].size);
+       }
+
+       return (error);
+}
+
+static const char *
+bcm_pcib_link_state_string(uint32_t mode)
+{
+
+       switch(mode & PCIEM_LINK_STA_SPEED) {
+       case 0:
+               return ("not up");
+       case 1:
+               return ("2.5 GT/s");
+       case 2:
+               return ("5.0 GT/s");
+       case 4:
+               return ("8.0 GT/s");
+       default:
+               return ("unknown");
+       }
+}
+
+static bus_addr_t
+bcm_get_offset_and_prepare_config(struct bcm_pcib_softc *sc, u_int bus,
+    u_int slot, u_int func, u_int reg)
+{
+       /*
+        * Config for an end point is only available through a narrow window for
+        * one end point at a time. We first tell the controller which end point
+        * we want, then access it through the window.
+        */
+       uint32_t func_index;
+
+       if (bus == 0 && slot == 0 && func == 0)
+               /*
+                * Special case for root device; its config is always available
+                * through the zero-offset.
+                */
+               return (reg);
+
+       /* Tell the controller to show us the config in question. */
+       func_index = PCIE_ADDR_OFFSET(bus, slot, func, 0);
+       bcm_pcib_set_reg(sc, REG_EP_CONFIG_CHOICE, func_index);
+
+       return (REG_EP_CONFIG_DATA + reg);
+}
+
+static bool
+bcm_pcib_is_valid_quad(struct bcm_pcib_softc *sc, u_int bus, u_int slot,
+    u_int func, u_int reg)
+{
+
+       if ((bus < sc->base.base.bus_start) || (bus > sc->base.base.bus_end))
+               return (false);
+       if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX))
+               return (false);
+
+       if (bus == 0 && slot == 0 && func == 0)
+               return (true);
+       if (bus == 0)
+               /*
+                * Probing other slots and funcs on bus 0 will lock up the
+                * memory controller.
+                */
+               return (false);
+
+       return (true);
+}
+
+static uint32_t
+bcm_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int 
reg,
+    int bytes)
+{
+       struct bcm_pcib_softc *sc;
+       bus_space_handle_t h;
+       bus_space_tag_t t;
+       bus_addr_t offset;
+       uint32_t data;
+
+       sc = device_get_softc(dev);
+       if (!bcm_pcib_is_valid_quad(sc, bus, slot, func, reg))
+               return (~0U);
+
+       mtx_lock(&sc->config_mtx);
+       offset = bcm_get_offset_and_prepare_config(sc, bus, slot, func, reg);
+
+       t = sc->base.base.bst;
+       h = sc->base.base.bsh;
+
+       switch (bytes) {
+       case 1:
+               data = bus_space_read_1(t, h, offset);
+               break;
+       case 2:
+               data = le16toh(bus_space_read_2(t, h, offset));
+               break;
+       case 4:
+               data = le32toh(bus_space_read_4(t, h, offset));
+               break;
+       default:
+               data = ~0U;
+               break;
+       }
+
+       mtx_unlock(&sc->config_mtx);
+       return (data);
+}
+
+static void
+bcm_pcib_write_config(device_t dev, u_int bus, u_int slot,
+    u_int func, u_int reg, uint32_t val, int bytes)
+{
+       struct bcm_pcib_softc *sc;
+       bus_space_handle_t h;
+       bus_space_tag_t t;
+       uint32_t offset;
+
+       sc = device_get_softc(dev);
+       if (!bcm_pcib_is_valid_quad(sc, bus, slot, func, reg))
+               return;
+
+       mtx_lock(&sc->config_mtx);
+       offset = bcm_get_offset_and_prepare_config(sc, bus, slot, func, reg);
+
+       t = sc->base.base.bst;
+       h = sc->base.base.bsh;
+
+       switch (bytes) {
+       case 1:
+               bus_space_write_1(t, h, offset, val);
+               break;
+       case 2:
+               bus_space_write_2(t, h, offset, htole16(val));
+               break;
+       case 4:
+               bus_space_write_4(t, h, offset, htole32(val));
+               break;
+       default:
+               break;
+       }
+
+       mtx_unlock(&sc->config_mtx);
+}
+
+static void
+bcm_pcib_msi_intr_process(struct bcm_pcib_softc *sc, uint32_t interrupt_bitmap,
+    struct trapframe *tf)
+{
+       struct bcm_pcib_irqsrc *irqsrc;
+       uint32_t bit, irq;
+
+       while ((bit = ffs(interrupt_bitmap))) {
+               irq = bit - 1;
+
+               /* Acknowledge interrupt. */
+               bcm_pcib_set_reg(sc, REG_MSI_CLR, 1 << irq);
+
+               /* Send EOI. */
+               bcm_pcib_set_reg(sc, REG_MSI_EOI, 1);
+
+               /* Despatch to handler. */
+               irqsrc = &sc->msi_isrcs[irq];
+               if (intr_isrc_dispatch(&irqsrc->isrc, tf))
+                       device_printf(sc->dev,
+                           "note: unexpected interrupt (%d) triggered.\n",
+                           irq);
+
+               /* Done with this interrupt. */
+               interrupt_bitmap = interrupt_bitmap & ~(1 << irq);
+       }
+}
+
+static int
+bcm_pcib_msi_intr(void *arg)
+{
+       struct bcm_pcib_softc *sc;
+       struct trapframe *tf;
+       uint32_t interrupt_bitmap;
+
+       sc = (struct bcm_pcib_softc *) arg;
+       tf = curthread->td_intr_frame;
+
+       while ((interrupt_bitmap = bcm_pcib_read_reg(sc, REG_MSI_RAISED)))
+               bcm_pcib_msi_intr_process(sc, interrupt_bitmap, tf);
+
+       return (FILTER_HANDLED);
+}
+
+static int
+bcm_pcib_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+    device_t *pic, struct intr_irqsrc **srcs)
+{
+       struct bcm_pcib_softc *sc;
+       int first_int, i;
+
+       sc = device_get_softc(dev);
+       mtx_lock(&sc->msi_mtx);
+
+       /* Find a continguous region of free message-signalled interrupts. */
+       for (first_int = 0; first_int + count < NUM_MSI; ) {
+               for (i = first_int; i < first_int + count; ++i) {
+                       if (sc->msi_isrcs[i].allocated)
+                               goto next;
+               }
+               goto found;
+next:
+               first_int = i + 1;
+       }
+
+       /* No appropriate region available. */
+       mtx_unlock(&sc->msi_mtx);
+       device_printf(dev, "warning: failed to allocate %d MSI messages.\n",
+           count);
+       return (ENXIO);
+
+found:
+       /* Mark the messages as in use. */
+       for (i = 0; i < count; ++i) {
+               sc->msi_isrcs[i + first_int].allocated = true;
+               srcs[i] = &(sc->msi_isrcs[i + first_int].isrc);
+       }
+
+       mtx_unlock(&sc->msi_mtx);
+       *pic = device_get_parent(dev);
+
+       return (0);
+}
+
+static int
+bcm_pcib_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+    uint64_t *addr, uint32_t *data)
+{
+       struct bcm_pcib_softc *sc;
+       struct bcm_pcib_irqsrc *msi_msg;
+
+       sc = device_get_softc(dev);
+       msi_msg = (struct bcm_pcib_irqsrc *) isrc;
+
+       *addr = sc->msi_addr;
+       *data = (REG_VALUE_MSI_CONFIG & 0xffff) | msi_msg->irq;
+       return (0);
+}
+
+static int
+bcm_pcib_release_msi(device_t dev, device_t child, int count,
+    struct intr_irqsrc **isrc)
+{
+       struct bcm_pcib_softc *sc;
+       struct bcm_pcib_irqsrc *msi_isrc;
+       int i;
+
+       sc = device_get_softc(dev);
+       mtx_lock(&sc->msi_mtx);
+
+       for (i = 0; i < count; i++) {
+               msi_isrc = (struct bcm_pcib_irqsrc *) isrc[i];
+               msi_isrc->allocated = false;
+       }
+
+       mtx_unlock(&sc->msi_mtx);
+       return (0);
+}
+
+static int
+bcm_pcib_msi_attach(device_t dev)
+{
+       struct bcm_pcib_softc *sc;
+       phandle_t node, xref;
+       char const *bcm_name;
+       int i, rid;
+
+       sc = device_get_softc(dev);
+       sc->msi_addr = 0xffffffffc;
+
+       /* Clear any pending interrupts. */
+       bcm_pcib_set_reg(sc, REG_MSI_CLR, 0xffffffff);
+
+       rid = 1;
+       sc->msi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+           RF_ACTIVE);
+       if (sc->msi_irq_res == NULL) {
+               device_printf(dev, "could not allocate MSI irq resource.\n");
+               return (ENXIO);
+       }
+
+       sc->msi_isrcs = malloc(sizeof(*sc->msi_isrcs) * NUM_MSI, M_DEVBUF,
+           M_WAITOK | M_ZERO);
+
+       int error = bus_setup_intr(dev, sc->msi_irq_res, INTR_TYPE_BIO |
+           INTR_MPSAFE, bcm_pcib_msi_intr, NULL, sc, &sc->msi_intr_cookie);
+       if (error) {
+               device_printf(dev, "error: failed to setup MSI handler.\n");
+               return (ENXIO);
+       }
+
+       bcm_name = device_get_nameunit(dev);
+       for (i = 0; i < NUM_MSI; i++) {
+               sc->msi_isrcs[i].irq = i;
+               error = intr_isrc_register(&sc->msi_isrcs[i].isrc, dev, 0,
+                   "%s,%u", bcm_name, i);
+               if (error) {
+                       device_printf(dev,
+                       "error: failed to register interrupt %d.\n", i);
+                       return (ENXIO);
+               }
+       }
+
+       node = ofw_bus_get_node(dev);
+       xref = OF_xref_from_node(node);
+       OF_device_register_xref(xref, dev);
+
+       error = intr_msi_register(dev, xref);
+       if (error)
+               return (ENXIO);
+
+       mtx_init(&sc->msi_mtx, "bcm_pcib: msi_mtx", NULL, MTX_DEF);
+
+       bcm_pcib_set_reg(sc, REG_MSI_MASK_CLR, 0xffffffff);
+       bcm_pcib_set_reg(sc, REG_MSI_ADDR_LOW, (sc->msi_addr & 0xffffffff) | 1);
+       bcm_pcib_set_reg(sc, REG_MSI_ADDR_HIGH, (sc->msi_addr >> 32));
+       bcm_pcib_set_reg(sc, REG_MSI_CONFIG, REG_VALUE_MSI_CONFIG);
+
+       return (0);
+}
+
+static void
+bcm_pcib_relocate_bridge_window(device_t dev)
+{
+       /*
+        * In principle an out-of-bounds bridge window could be automatically
+        * adjusted at resource-activation time to lie within the bus address
+        * space by pcib_grow_window(), but that is not possible because the
+        * out-of-bounds resource allocation fails at allocation time. Instead,
+        * we will just fix up the window on the controller here, before it is
+        * re-discovered by pcib_probe_windows().
+        */
+
+       struct bcm_pcib_softc *sc;
+       pci_addr_t base, size, new_base, new_limit;
+       uint16_t val;
+
+       sc = device_get_softc(dev);
+
+       val = bcm_pcib_read_config(dev, 0, 0, 0, PCIR_MEMBASE_1, 2);
+       base = PCI_PPBMEMBASE(0, val);
+
+       val = bcm_pcib_read_config(dev, 0, 0, 0, PCIR_MEMLIMIT_1, 2);
+       size = PCI_PPBMEMLIMIT(0, val) - base;
+
+       new_base = sc->base.base.ranges[0].pci_base;
+       val = (uint16_t) (new_base >> 16);
+       bcm_pcib_write_config(dev, 0, 0, 0, PCIR_MEMBASE_1, val, 2);
+
+       new_limit = new_base + size;
+       val = (uint16_t) (new_limit >> 16);
+       bcm_pcib_write_config(dev, 0, 0, 0, PCIR_MEMLIMIT_1, val, 2);
+}
+
+static uint32_t
+encode_cpu_window_low(pci_addr_t phys_base, bus_size_t size)
+{
+
+       return (((phys_base >> 0x10) & 0xfff0) |
+           ((phys_base + size - 1) & 0xfff00000));
+}
+
+static uint32_t
+encode_cpu_window_start_high(pci_addr_t phys_base)
+{
+
+       return ((phys_base >> 0x20) & 0xff);
+}
+
+static uint32_t
+encode_cpu_window_end_high(pci_addr_t phys_base, bus_size_t size)
+{
+
+       return (((phys_base + size - 1) >> 0x20) & 0xff);
+}
+
+static int
+bcm_pcib_attach(device_t dev)
+{
+       struct bcm_pcib_softc *sc;
+       pci_addr_t phys_base, pci_base;
+       bus_size_t size;
+       uint32_t hardware_rev, bridge_state, link_state;
+       int error, tries;
+
+       sc = device_get_softc(dev);
+       sc->dev = dev;
+
+       error = pci_host_generic_setup_fdt(dev);
+       if (error)
+               return (error);
+
+       error = bcm_pcib_check_ranges(dev);
+       if (error)
+               return (error);
+
+       mtx_init(&sc->config_mtx, "bcm_pcib: config_mtx", NULL, MTX_DEF);
+
+       bcm_pcib_reset_controller(sc);
+
+       hardware_rev = bcm_pcib_read_reg(sc, REG_CONTROLLER_HW_REV) & 0xffff;
+       device_printf(dev, "hardware identifies as revision 0x%x.\n",
+           hardware_rev);
+
+       /*
+        * Set PCI->CPU memory window. This encodes the inbound window showing
+        * up to 4 GiB of system memory to the controller, with zero offset.
+        * Thus, from the perspective of a device on the PCI-E bus, there is a
+        * 1:1 map from PCI-E bus addresses to system memory addresses. However,
+        * a hardware limitation means that the controller can only perform DMA
+        * on the lower 3 GiB of system memory.
+        */
+       bcm_pcib_set_reg(sc, REG_BRIDGE_MEM_WINDOW_LOW, REG_VALUE_4GB_WINDOW);
+       bcm_pcib_set_reg(sc, REG_BRIDGE_MEM_WINDOW_HIGH, 0);
+       bcm_pcib_set_reg(sc, REG_BRIDGE_CONFIG, REG_VALUE_4GB_CONFIG);
+       bcm_pcib_set_reg(sc, REG_BRIDGE_GISB_WINDOW, 0);
+       bcm_pcib_set_reg(sc, REG_BRIDGE_MEM_WINDOW_1, 0);
+
+       bcm_pcib_enable_controller(sc);
+
+       /* Wait for controller to start. */
+       for(tries = 0; ; ++tries) {
+               bridge_state = bcm_pcib_read_reg(sc, REG_BRIDGE_STATE);
+
+               if ((bridge_state & 0x30) == 0x30)
+                       /* Controller ready. */
+                       break;
+
+               if (tries > 100) {
+                       device_printf(dev,
+                           "error: controller failed to start.\n");
+                       return (ENXIO);
+               }
+
+               DELAY(1000);
+       }
+
+       link_state = bcm_pcib_read_reg(sc, REG_BRIDGE_LINK_STATE) >> 0x10;
+       if (!link_state) {
+               device_printf(dev, "error: controller started but link is not "
+                   "up.\n");
+               return (ENXIO);
+       }
+       if (bootverbose)
+               device_printf(dev, "note: reported link speed is %s.\n",
+                   bcm_pcib_link_state_string(link_state));
+
+       /*
+        * Set the CPU->PCI memory window. The map in this direction is not 1:1.
+        * Addresses seen by the CPU need to be adjusted to make sense to the
+        * controller as they pass through the window.
+        */
+       pci_base  = sc->base.base.ranges[0].pci_base;
+       phys_base = sc->base.base.ranges[0].phys_base;
+       size      = sc->base.base.ranges[0].size;
+
+       bcm_pcib_set_reg(sc, REG_BRIDGE_BUS_WINDOW_LOW, pci_base & 0xffffffff);
+       bcm_pcib_set_reg(sc, REG_BRIDGE_BUS_WINDOW_HIGH, pci_base >> 32);
+
+       bcm_pcib_set_reg(sc, REG_BRIDGE_CPU_WINDOW_LOW,
+           encode_cpu_window_low(phys_base, size));
+       bcm_pcib_set_reg(sc, REG_BRIDGE_CPU_WINDOW_START_HIGH,
+           encode_cpu_window_start_high(phys_base));
+       bcm_pcib_set_reg(sc, REG_BRIDGE_CPU_WINDOW_END_HIGH,
+           encode_cpu_window_end_high(phys_base, size));
+
+       /*
+        * The controller starts up declaring itself an endpoint; readvertise it
+        * as a bridge.
+        */
+       bcm_pcib_set_reg(sc, PCI_ID_VAL3,
+           PCIC_BRIDGE << CLASS_SHIFT | PCIS_BRIDGE_PCI << SUBCLASS_SHIFT);
+
+       bcm_pcib_set_reg(sc, REG_BRIDGE_SERDES_MODE, 0x2);
+       DELAY(100);
+
+       bcm_pcib_relocate_bridge_window(dev);
+
+       /* Configure interrupts. */
+       error = bcm_pcib_msi_attach(dev);
+       if (error)
+               return (error);
+
+       /* Done. */
+       device_add_child(dev, "pci", -1);
+       return (bus_generic_attach(dev));
+}
+
+/*
+ * Device method table.
+ */
+static device_method_t bcm_pcib_methods[] = {
+       /* Device interface. */
+       DEVMETHOD(device_probe,                 bcm_pcib_probe),
+       DEVMETHOD(device_attach,                bcm_pcib_attach),
+
+       /* PCIB interface. */
+       DEVMETHOD(pcib_read_config,             bcm_pcib_read_config),
+       DEVMETHOD(pcib_write_config,            bcm_pcib_write_config),
+
+       /* MSI interface. */
+       DEVMETHOD(msi_alloc_msi,                bcm_pcib_alloc_msi),
+       DEVMETHOD(msi_release_msi,              bcm_pcib_release_msi),
+       DEVMETHOD(msi_map_msi,                  bcm_pcib_map_msi),
+
+       DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(pcib, bcm_pcib_driver, bcm_pcib_methods,
+    sizeof(struct bcm_pcib_softc), generic_pcie_fdt_driver);
+
+static devclass_t bcm_pcib_devclass;
+DRIVER_MODULE(pcib, simplebus, bcm_pcib_driver, bcm_pcib_devclass, 0, 0);
+

Modified: head/sys/arm/broadcom/bcm2835/files.bcm283x
==============================================================================
--- head/sys/arm/broadcom/bcm2835/files.bcm283x Mon Jul  6 08:50:11 2020        
(r362953)
+++ head/sys/arm/broadcom/bcm2835/files.bcm283x Mon Jul  6 08:51:55 2020        
(r362954)
@@ -17,6 +17,7 @@ arm/broadcom/bcm2835/bcm2835_spi.c            optional 
bcm2835_s
 arm/broadcom/bcm2835/bcm2835_vcbus.c           standard
 arm/broadcom/bcm2835/bcm2835_vcio.c            standard
 arm/broadcom/bcm2835/bcm2835_wdog.c            standard
+arm/broadcom/bcm2835/bcm2838_pci.c             optional pci
 arm/broadcom/bcm2835/bcm283x_dwc_fdt.c         optional dwcotg fdt
 
 dev/mbox/mbox_if.m                             standard

Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64   Mon Jul  6 08:50:11 2020        (r362953)
+++ head/sys/conf/files.arm64   Mon Jul  6 08:51:55 2020        (r362954)
@@ -105,6 +105,7 @@ arm/broadcom/bcm2835/bcm2835_vcio.c         optional 
soc_brcm
 arm/broadcom/bcm2835/bcm2835_wdog.c            optional soc_brcm_bcm2837 fdt | 
soc_brcm_bcm2838 fdt
 arm/broadcom/bcm2835/bcm2836.c                 optional soc_brcm_bcm2837 fdt | 
soc_brcm_bcm2838 fdt
 arm/broadcom/bcm2835/bcm283x_dwc_fdt.c         optional dwcotg fdt 
soc_brcm_bcm2837 | dwcotg fdt soc_brcm_bcm2838
+arm/broadcom/bcm2835/bcm2838_pci.c             optional soc_brcm_bcm2838 fdt 
pci
 arm/freescale/vybrid/vf_i2c.c                  optional vf_i2c iicbus 
SOC_NXP_LS
 arm/mv/a37x0_gpio.c                            optional a37x0_gpio gpio fdt
 arm/mv/a37x0_iic.c                             optional a37x0_iic iicbus fdt

Modified: head/sys/dev/pci/pci_host_generic_fdt.c
==============================================================================
--- head/sys/dev/pci/pci_host_generic_fdt.c     Mon Jul  6 08:50:11 2020        
(r362953)
+++ head/sys/dev/pci/pci_host_generic_fdt.c     Mon Jul  6 08:51:55 2020        
(r362954)
@@ -120,7 +120,7 @@ generic_pcie_fdt_probe(device_t dev)
 }
 
 int
-pci_host_generic_attach(device_t dev)
+pci_host_generic_setup_fdt(device_t dev)
 {
        struct generic_pcie_fdt_softc *sc;
        phandle_t node;
@@ -149,11 +149,27 @@ pci_host_generic_attach(device_t dev)
        /* TODO parse FDT bus ranges */
        sc->base.bus_start = 0;
        sc->base.bus_end = 0xFF;
+
        error = pci_host_generic_core_attach(dev);
        if (error != 0)
                return (error);
 
        ofw_bus_setup_iinfo(node, &sc->pci_iinfo, sizeof(cell_t));
+
+       return (0);
+}
+
+int
+pci_host_generic_attach(device_t dev)
+{
+       struct generic_pcie_fdt_softc *sc;
+       int error;
+
+       sc = device_get_softc(dev);
+
+       error = pci_host_generic_setup_fdt(dev);
+       if (error != 0)
+               return (error);
 
        device_add_child(dev, "pci", -1);
        return (bus_generic_attach(dev));

Modified: head/sys/dev/pci/pci_host_generic_fdt.h
==============================================================================
--- head/sys/dev/pci/pci_host_generic_fdt.h     Mon Jul  6 08:50:11 2020        
(r362953)
+++ head/sys/dev/pci/pci_host_generic_fdt.h     Mon Jul  6 08:51:55 2020        
(r362954)
@@ -43,6 +43,7 @@ DECLARE_CLASS(generic_pcie_fdt_driver);
 
 struct resource *pci_host_generic_alloc_resource(device_t,
     device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int);
+int pci_host_generic_setup_fdt(device_t);
 int pci_host_generic_attach(device_t);
 int generic_pcie_get_id(device_t, device_t, enum pci_id_type, uintptr_t *);
 
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to