/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2024 Jari Sihvola */ /* JH7110 PCIe controller driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "msi_if.h" #include "ofw_bus_if.h" #include "pcib_if.h" #include "pic_if.h" #include "syscon_if.h" #define IRQ_LOCAL_MASK 0x180 #define IRQ_LOCAL_STATUS 0x184 #define IRQ_MSI_BASE 0x190 #define IRQ_MSI_STATUS 0x194 #define MSI_MASK 0x10000000 #define INTX_MASK 0xf000000 #define ERROR_MASK 0x80770000 #define MSI_COUNT 32 #define MSI_USED 0x1 #define MSI_PCIE0_MASK_OFFSET 0xa0; #define MSI_PCIE1_MASK_OFFSET 0xf0; #define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800 #define ATR0_AXI4_SLV0_SRC_ADDR 0x804 #define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808 #define ATR0_AXI4_SLV0_TRSL_PARAM 0x810 #define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80c #define ATR_ENTRY_SIZE 0x20 #define ATR0_PCIE_ATR_SIZE 0x25 #define ATR0_PCIE_ATR_SIZE_SHIFT 1 #define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600 #define ATR0_PCIE_WIN0_SRC_ADDR 0x604 #define ATR0_ENABLE 1 #define PCIE_TXRX_INTERFACE 0x0 #define PCIE_CONF_INTERFACE 0x1 #define PCIE_WINCONF 0xfc #define PREF_MEM_WIN_64_SUPPORT (1U << 3) #define STG_AXI4_SLVL_AW_MASK 0x7fff #define STG_AXI4_SLVL_AR_MASK 0x7fff00 #define STG_PCIE0_BASE 0x48 #define STG_PCIE1_BASE 0x1f8 #define STG_RP_NEP_OFFSET 0xe8 #define STG_K_RP_NEP (1U << 8) #define STG_CKREF_MASK 0xC0000 #define STG_CKREF_VAL 0x80000 #define STG_CLKREQ (1U << 22) #define STG_AR_OFFSET 0x78 #define STG_AW_OFFSET 0x7c #define STG_AXI4_SLVL_ARFUNC_SHIFT 0x8 #define STG_LNKSTA_OFFSET 0x170 #define STG_LINK_UP (1U << 5) #define PHY_FUNC_SHIFT 9 #define PHY_FUNC_DIS (1U << 15) #define PCI_MISC_REG 0xb4 #define PCI_GENERAL_SETUP_REG 0x80 #define PCI_CONF_SPACE_REGS 0x1000 #define ROOTPORT_ENABLE 0x1 #define PMSG_RX_SUPPORT_REG 0x3f0 #define PMSG_LTR_SUPPORT (1U << 2) #define PCI_CLASS_BRIDGE_PCI 0x0604 #define PCI_IDS_CLASS_CODE_SHIFT 16 #define PCIE_PCI_IDS_REG 0x9c #define REV_ID_MASK 0xff #define PLDA_AXI_POST_ERR (1U << 16) #define PLDA_AXI_FETCH_ERR (1U << 17) #define PLDA_AXI_DISCARD_ERR (1U << 18) #define PLDA_PCIE_POST_ERR (1U << 20) #define PLDA_PCIE_FETCH_ERR (1U << 21) #define PLDA_PCIE_DISCARD_ERR (1U << 22) #define PLDA_SYS_ERR (1U << 31) /* Compatible devices. */ static struct ofw_compat_data compat_data[] = { {"starfive,jh7110-pcie", 1}, {NULL, 0}, }; struct jh7110_pcie_irqsrc { struct intr_irqsrc isrc; u_int irq; u_int is_used; }; struct jh7110_pcie_softc { struct ofw_pci_softc ofw_pci; device_t dev; phandle_t node; struct resource *reg_mem_res; struct resource *cfg_mem_res; struct resource *irq_res; struct jh7110_pcie_irqsrc *isrcs; void *irq_cookie; struct syscon *stg_syscon; uint64_t stg_baddr; struct ofw_pci_range range_mem32; struct ofw_pci_range range_mem64; struct mtx msi_mtx; uint64_t msi_mask_offset; gpio_pin_t perst_pin; clk_t clk_noc; clk_t clk_tl; clk_t clk_axi; clk_t clk_apb; hwreset_t rst_mst0; hwreset_t rst_slv0; hwreset_t rst_slv; hwreset_t rst_brg; hwreset_t rst_core; hwreset_t rst_apb; }; #define LOW32(val) (uint32_t)(val) #define HI32(val) (uint32_t)(val >> 32) #define RD4(sc, reg) bus_read_4((sc)->reg_mem_res, (reg)) #define WR4(sc, reg, val) bus_write_4((sc)->reg_mem_res, (reg), (val)) static uint32_t jh7110_pcie_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct jh7110_pcie_softc *sc; uint32_t data, offset; sc = device_get_softc(dev); offset = PCIE_ADDR_OFFSET(bus, slot, func, reg); /* Certain config registers are not supposed to be accessed from here */ if (bus == 0 && (offset == PCIR_BAR(0) || offset == PCIR_BAR(1))) return (~0U); switch (bytes) { case 1: data = bus_read_1(sc->cfg_mem_res, offset); break; case 2: data = le16toh(bus_read_2(sc->cfg_mem_res, offset)); break; case 4: data = le32toh(bus_read_4(sc->cfg_mem_res, offset)); break; default: return (~0U); } return (data); } static void jh7110_pcie_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct jh7110_pcie_softc *sc; uint32_t offset; sc = device_get_softc(dev); offset = PCIE_ADDR_OFFSET(bus, slot, func, reg); /* Certain config registers are not supposed to be accessed from here */ if (bus == 0 && (offset == PCIR_BAR(0) || offset == PCIR_BAR(1))) return; switch (bytes) { case 1: bus_write_1(sc->cfg_mem_res, offset, val); break; case 2: bus_write_2(sc->cfg_mem_res, offset, htole16(val)); break; case 4: bus_write_4(sc->cfg_mem_res, offset, htole32(val)); break; default: return; } } static int jh7110_pcie_intr(void *arg) { struct jh7110_pcie_softc *sc; struct trapframe *tf; struct jh7110_pcie_irqsrc *irq; uint32_t reg, irqbits; int err, i; sc = (struct jh7110_pcie_softc *)arg; tf = curthread->td_intr_frame; reg = RD4(sc, IRQ_LOCAL_STATUS); if (reg == 0) return (ENXIO); if ((reg & MSI_MASK) != 0) { WR4(sc, IRQ_LOCAL_STATUS, MSI_MASK); irqbits = RD4(sc, IRQ_MSI_STATUS); for (i = 0; irqbits != 0; i++) { if ((irqbits & (1U << i)) != 0) { irq = &sc->isrcs[i]; err = intr_isrc_dispatch(&irq->isrc, tf); if (err != 0) device_printf(sc->dev, "MSI 0x%x gives error %d\n", i, err); irqbits &= ~(1U << i); } } } if ((reg & INTX_MASK) != 0) { irqbits = (reg & INTX_MASK); WR4(sc, IRQ_LOCAL_STATUS, irqbits); } if ((reg & ERROR_MASK) != 0) { irqbits = (reg & ERROR_MASK); if ((reg & PLDA_AXI_POST_ERR) != 0) device_printf(sc->dev, "axi post error\n"); if ((reg & PLDA_AXI_FETCH_ERR) != 0) device_printf(sc->dev, "axi fetch error\n"); if ((reg & PLDA_AXI_DISCARD_ERR) != 0) device_printf(sc->dev, "axi discard error\n"); if ((reg & PLDA_PCIE_POST_ERR) != 0) device_printf(sc->dev, "pcie post error\n"); if ((reg & PLDA_PCIE_FETCH_ERR) != 0) device_printf(sc->dev, "pcie fetch error\n"); if ((reg & PLDA_PCIE_DISCARD_ERR) != 0) device_printf(sc->dev, "pcie discard error\n"); if ((reg & PLDA_SYS_ERR) != 0) device_printf(sc->dev, "pcie sys error\n"); WR4(sc, IRQ_LOCAL_STATUS, irqbits); } return (FILTER_HANDLED); } static int jh7110_pcie_route_interrupt(device_t bus, device_t dev, int pin) { struct jh7110_pcie_softc *sc; u_int irq; sc = device_get_softc(bus); irq = intr_map_clone_irq(rman_get_start(sc->irq_res)); device_printf(bus, "route pin %d for device %d.%d to %u\n", pin, pci_get_slot(dev), pci_get_function(dev), irq); return (irq); } static int jh7110_pcie_maxslots(device_t dev) { return (PCI_SLOTMAX); } static int jh7110_pcie_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount, device_t *pic, struct intr_irqsrc **srcs) { struct jh7110_pcie_softc *sc; int i, beg; sc = device_get_softc(dev); mtx_lock(&sc->msi_mtx); /* Search for a requested contiguous region */ for (beg = 0; beg + count < MSI_COUNT; ) { for (i = beg; i < beg + count; i++) { if (sc->isrcs[i].is_used == MSI_USED) goto next; } goto found; next: beg = i + 1; } /* Requested area not found */ mtx_unlock(&sc->msi_mtx); device_printf(dev, "warning: failed to allocate %d MSIs.\n", count); return (ENXIO); found: /* Mark and allocate messages */ for (i = 0; i < count; ++i) { sc->isrcs[i + beg].is_used = MSI_USED; srcs[i] = &(sc->isrcs[i + beg].isrc); } mtx_unlock(&sc->msi_mtx); *pic = device_get_parent(dev); return (0); } static int jh7110_pcie_alloc_msi(device_t pci, device_t child, int count, int maxcount, int *irqs) { phandle_t msi_parent; int err; msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); err = intr_alloc_msi(pci, child, msi_parent, count, maxcount, irqs); return (err); } static int jh7110_pcie_release_msi(device_t pci, device_t child, int count, int *irqs) { phandle_t msi_parent; int err; msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); err = intr_release_msi(pci, child, msi_parent, count, irqs); return (err); } static int jh7110_pcie_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, uint64_t *addr, uint32_t *data) { struct jh7110_pcie_irqsrc *jhirq = (struct jh7110_pcie_irqsrc *)isrc; *addr = IRQ_MSI_BASE; *data = jhirq->irq; return (0); } static int jh7110_pcie_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, uint32_t *data) { phandle_t msi_parent; int err; msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); err = intr_map_msi(pci, child, msi_parent, irq, addr, data); if (err != 0) { device_printf(pci, "intr_map_msi() failed\n"); return (err); } return (err); } static int jh7110_pcie_alloc_msix(device_t pci, device_t child, int *irq) { return (jh7110_pcie_alloc_msi(pci, child, 1, 32, irq)); } static int jh7110_pcie_release_msix(device_t pci, device_t child, int irq) { phandle_t msi_parent; int err; msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); err = intr_release_msix(pci, child, msi_parent, irq); return (err); } static int jh7110_pcie_msi_alloc_msix(device_t dev, device_t child, device_t *pic, struct intr_irqsrc **isrcp) { return (jh7110_pcie_msi_alloc_msi(dev, child, 1, 32, pic, isrcp)); } static int jh7110_pcie_msi_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **isrc) { struct jh7110_pcie_softc *sc; struct jh7110_pcie_irqsrc *irq; int i; sc = device_get_softc(dev); mtx_lock(&sc->msi_mtx); for (i = 0; i < count; i++) { irq = (struct jh7110_pcie_irqsrc *)isrc[i]; KASSERT((irq->is_used & MSI_USED) == MSI_USED, ("%s: Trying to release an unused MSI(-X) interrupt", __func__)); irq->is_used = 0; } mtx_unlock(&sc->msi_mtx); return (0); } static int jh7110_pcie_msi_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) { return (jh7110_pcie_msi_release_msi(dev, child, 1, &isrc)); } static void jh7110_pcie_msi_mask(device_t dev, struct intr_irqsrc *isrc, bool mask) { struct jh7110_pcie_softc *sc; struct jh7110_pcie_irqsrc *jhirq = (struct jh7110_pcie_irqsrc *)isrc; uint32_t reg, irq; sc = device_get_softc(dev); irq = jhirq->irq; reg = bus_read_4(sc->cfg_mem_res, sc->msi_mask_offset); if (mask != 0) reg &= ~(1U << irq); else reg |= (1U << irq); bus_write_4(sc->cfg_mem_res, sc->msi_mask_offset, reg); } static void jh7110_pcie_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc) { jh7110_pcie_msi_mask(dev, isrc, true); } static void jh7110_pcie_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc) { jh7110_pcie_msi_mask(dev, isrc, false); } static void jh7110_pcie_msi_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static void jh7110_pcie_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc) { } static void jh7110_pcie_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { struct jh7110_pcie_softc *sc; struct jh7110_pcie_irqsrc *jhirq = (struct jh7110_pcie_irqsrc *)isrc; uint32_t irq; sc = device_get_softc(dev); irq = jhirq->irq; /* MSI bottom ack */ WR4(sc, IRQ_MSI_STATUS, (1U << irq)); } static int jh7110_pcie_decode_ranges(struct jh7110_pcie_softc *sc, struct ofw_pci_range *ranges, int nranges) { int i; for (i = 0; i < nranges; i++) { if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == OFW_PCI_PHYS_HI_SPACE_MEM64)) { if (sc->range_mem64.size != 0) { device_printf(sc->dev, "Duplicate range mem64 found in DT\n"); return (ENXIO); } sc->range_mem64 = ranges[i]; } else if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == OFW_PCI_PHYS_HI_SPACE_MEM32)) { if (sc->range_mem32.size != 0) { device_printf(sc->dev, "Duplicated range mem32 found in DT\n"); return (ENXIO); } sc->range_mem32 = ranges[i]; } } return (0); } static int jh7110_pcie_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Starfive JH7110 PCIe controller"); return (BUS_PROBE_DEFAULT); } static void jh7110_pcie_set_atr(device_t dev, uint64_t axi_begin, uint64_t pci_begin, uint64_t win_size, uint32_t win_idx) { struct jh7110_pcie_softc *sc; uint32_t val, taddr_size; sc = device_get_softc(dev); if (win_idx == 0) val = PCIE_CONF_INTERFACE; else val = PCIE_TXRX_INTERFACE; WR4(sc, ATR0_AXI4_SLV0_TRSL_PARAM + win_idx * ATR_ENTRY_SIZE, val); taddr_size = ilog2(win_size) - 1; val = LOW32(axi_begin) | taddr_size << ATR0_PCIE_ATR_SIZE_SHIFT | ATR0_ENABLE; WR4(sc, ATR0_AXI4_SLV0_SRCADDR_PARAM + win_idx * ATR_ENTRY_SIZE, val); val = HI32(axi_begin); WR4(sc, ATR0_AXI4_SLV0_SRC_ADDR + win_idx * ATR_ENTRY_SIZE, val); val = LOW32(pci_begin); WR4(sc, ATR0_AXI4_SLV0_TRSL_ADDR_LSB + win_idx * ATR_ENTRY_SIZE, val); val = HI32(pci_begin); WR4(sc, ATR0_AXI4_SLV0_TRSL_ADDR_UDW + win_idx * ATR_ENTRY_SIZE, val); val = RD4(sc, ATR0_PCIE_WIN0_SRCADDR_PARAM); val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); WR4(sc, ATR0_PCIE_WIN0_SRCADDR_PARAM, val); WR4(sc, ATR0_PCIE_WIN0_SRC_ADDR, 0); } static int jh7110_pcie_parse_fdt_resources(struct jh7110_pcie_softc *sc) { uint32_t val; int err; /* Getting clocks */ if (clk_get_by_ofw_name(sc->dev, 0, "noc", &sc->clk_noc) != 0) { device_printf(sc->dev, "could not get noc clock\n"); sc->clk_noc = NULL; return (ENXIO); } if (clk_get_by_ofw_name(sc->dev, 0, "tl", &sc->clk_tl) != 0) { device_printf(sc->dev, "could not get tl clock\n"); sc->clk_tl = NULL; return (ENXIO); } if (clk_get_by_ofw_name(sc->dev, 0, "axi_mst0", &sc->clk_axi) != 0) { device_printf(sc->dev, "could not get axi_mst0 clock\n"); sc->clk_axi = NULL; return (ENXIO); } if (clk_get_by_ofw_name(sc->dev, 0, "apb", &sc->clk_apb) != 0) { device_printf(sc->dev, "could not get apb clock\n"); sc->clk_apb = NULL; return (ENXIO); } /* Getting resets */ err = hwreset_get_by_ofw_name(sc->dev, 0, "mst0", &sc->rst_mst0); if (err != 0) { device_printf(sc->dev, "cannot get 'rst_mst0' reset\n"); return (ENXIO); } err = hwreset_get_by_ofw_name(sc->dev, 0, "slv0", &sc->rst_slv0); if (err != 0) { device_printf(sc->dev, "cannot get 'rst_slv0' reset\n"); return (ENXIO); } err = hwreset_get_by_ofw_name(sc->dev, 0, "slv", &sc->rst_slv); if (err != 0) { device_printf(sc->dev, "cannot get 'rst_slv' reset\n"); return (ENXIO); } err = hwreset_get_by_ofw_name(sc->dev, 0, "brg", &sc->rst_brg); if (err != 0) { device_printf(sc->dev, "cannot get 'rst_brg' reset\n"); return (ENXIO); } err = hwreset_get_by_ofw_name(sc->dev, 0, "core", &sc->rst_core); if (err != 0) { device_printf(sc->dev, "cannot get 'rst_core' reset\n"); return (ENXIO); } err = hwreset_get_by_ofw_name(sc->dev, 0, "apb", &sc->rst_apb); if (err != 0) { device_printf(sc->dev, "cannot get 'rst_apb' reset\n"); return (ENXIO); } /* Getting PCI endpoint reset pin */ err = gpio_pin_get_by_ofw_property(sc->dev, sc->node, "perst-gpios", &sc->perst_pin); if (err != 0) { device_printf(sc->dev, "Cannot get perst-gpios\n"); return (ENXIO); } /* Getting syscon property */ if (syscon_get_by_ofw_property(sc->dev, sc->node, "starfive,stg-syscon", &sc->stg_syscon) != 0) { device_printf(sc->dev, "Cannot get starfive,stg-syscon\n"); return (ENXIO); } /* Assigning syscon base address and MSI mask offset */ err = OF_getencprop(sc->node, "linux,pci-domain", &val, sizeof(val)); if (err == -1) { device_printf(sc->dev, "Couldn't get pci-domain property, error: %d\n", err); return (ENXIO); } if (val == 0) { sc->stg_baddr = STG_PCIE0_BASE; sc->msi_mask_offset = MSI_PCIE0_MASK_OFFSET; } else if (val == 1) { sc->stg_baddr = STG_PCIE1_BASE; sc->msi_mask_offset = MSI_PCIE1_MASK_OFFSET; } else { device_printf(sc->dev, "Error: an invalid pci-domain value\n"); return (ENXIO); } return (0); } static void jh7110_pcie_release_resources(device_t dev) { struct jh7110_pcie_softc *sc; sc = device_get_softc(dev); if (sc->irq_res != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); if (sc->irq_res != NULL) bus_free_resource(dev, SYS_RES_IRQ, sc->irq_res); if (sc->reg_mem_res != NULL) bus_free_resource(dev, SYS_RES_MEMORY, sc->reg_mem_res); if (sc->cfg_mem_res != NULL) bus_free_resource(dev, SYS_RES_MEMORY, sc->cfg_mem_res); if (sc->clk_noc != NULL) clk_release(sc->clk_noc); if (sc->clk_tl != NULL) clk_release(sc->clk_tl); if (sc->clk_axi != NULL) clk_release(sc->clk_axi); if (sc->clk_apb != NULL) clk_release(sc->clk_apb); gpio_pin_release(sc->perst_pin); hwreset_release(sc->rst_mst0); hwreset_release(sc->rst_slv0); hwreset_release(sc->rst_slv); hwreset_release(sc->rst_brg); hwreset_release(sc->rst_core); hwreset_release(sc->rst_apb); mtx_destroy(&sc->msi_mtx); } static int jh7110_pcie_detach(device_t dev) { ofw_pcib_fini(dev); jh7110_pcie_release_resources(dev); return (0); } static int jh7110_pcie_attach(device_t dev) { struct jh7110_pcie_softc *sc; phandle_t xref; uint32_t val; int i, err, rid, irq, win_idx = 0; char name[INTR_ISRC_NAMELEN]; sc = device_get_softc(dev); sc->dev = dev; sc->node = ofw_bus_get_node(dev); sc->irq_res = NULL; sc->reg_mem_res = NULL; sc->cfg_mem_res = NULL; sc->clk_noc = NULL; sc->clk_tl = NULL; sc->clk_axi = NULL; sc->clk_apb = NULL; mtx_init(&sc->msi_mtx, "jh7110_pcie, msi_mtx", NULL, MTX_DEF); /* Allocating memory */ err = ofw_bus_find_string_index(sc->node, "reg-names", "apb", &rid); if (err != 0) { device_printf(dev, "Cannot get apb memory\n"); goto out; } sc->reg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->reg_mem_res == NULL) { device_printf(dev, "Cannot allocate apb memory\n"); err = ENXIO; goto out; } err = ofw_bus_find_string_index(sc->node, "reg-names", "cfg", &rid); if (err != 0) { device_printf(dev, "Cannot get cfg memory\n"); err = ENXIO; goto out; } sc->cfg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->cfg_mem_res == NULL) { device_printf(dev, "Cannot allocate cfg memory\n"); err = ENXIO; goto out; } /* Getting device tree properties */ if (jh7110_pcie_parse_fdt_resources(sc) != 0) goto out; /* Clearing interrupts, enabling MSI */ WR4(sc, IRQ_LOCAL_STATUS, 0xffffffff); WR4(sc, IRQ_LOCAL_MASK, INTX_MASK | ERROR_MASK | MSI_MASK); /* Setting host up */ SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_RP_NEP_OFFSET, STG_K_RP_NEP, STG_K_RP_NEP); SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AW_OFFSET, STG_CKREF_MASK, STG_CKREF_VAL); SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AW_OFFSET, STG_CLKREQ, STG_CLKREQ); /* Enabling clocks */ if (clk_enable(sc->clk_noc) != 0) { device_printf(dev, "could not enable noc clock\n"); goto out; } if (clk_enable(sc->clk_tl) != 0) { device_printf(dev, "could not enable tl clock\n"); goto out; } if (clk_enable(sc->clk_axi) != 0) { device_printf(dev, "could not enable axi_mst0 clock\n"); goto out; } if (clk_enable(sc->clk_apb) != 0) { device_printf(dev, "could not enable apb clock\n"); goto out; } /* Deasserting resets */ err = hwreset_deassert(sc->rst_mst0); if (err != 0) { device_printf(sc->dev, "cannot deassert 'mst0' reset\n"); goto out; } err = hwreset_deassert(sc->rst_slv0); if (err != 0) { device_printf(sc->dev, "cannot deassert 'slv0' reset\n"); goto out; } err = hwreset_deassert(sc->rst_slv); if (err != 0) { device_printf(sc->dev, "cannot deassert 'slv' reset\n"); goto out; } err = hwreset_deassert(sc->rst_brg); if (err != 0) { device_printf(sc->dev, "cannot deassert 'brg' reset\n"); goto out; } err = hwreset_deassert(sc->rst_core); if (err != 0) { device_printf(sc->dev, "cannot deassert 'core' reset\n"); goto out; } err = hwreset_deassert(sc->rst_apb); if (err != 0) { device_printf(sc->dev, "cannot deassert 'apb' reset\n"); goto out; } err = gpio_pin_set_active(sc->perst_pin, true); if (err != 0) { device_printf(dev, "Cannot activate gpio pin, error %d\n", err); goto out; } /* Switching off PHY functions 1-3 */ for (i = 1; i != 4; i++) { SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AR_OFFSET, STG_AXI4_SLVL_AR_MASK, (i << PHY_FUNC_SHIFT) << STG_AXI4_SLVL_ARFUNC_SHIFT); SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AW_OFFSET, STG_AXI4_SLVL_AW_MASK, i << PHY_FUNC_SHIFT); val = RD4(sc, PCI_MISC_REG); WR4(sc, PCI_MISC_REG, val | PHY_FUNC_DIS); } SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AR_OFFSET, STG_AXI4_SLVL_AR_MASK, 0); SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AW_OFFSET, STG_AXI4_SLVL_AW_MASK, 0); /* Enabling root port */ val = RD4(sc, PCI_GENERAL_SETUP_REG); WR4(sc, PCI_GENERAL_SETUP_REG, val | ROOTPORT_ENABLE); /* Zeroing RC BAR */ WR4(sc, PCI_CONF_SPACE_REGS + PCIR_BAR(0), 0); WR4(sc, PCI_CONF_SPACE_REGS + PCIR_BAR(1), 0); /* Setting standard class */ val = RD4(sc, PCIE_PCI_IDS_REG); val &= REV_ID_MASK; val |= (PCI_CLASS_BRIDGE_PCI << PCI_IDS_CLASS_CODE_SHIFT); WR4(sc, PCIE_PCI_IDS_REG, val); /* Disabling latency tolerance reporting */ val = RD4(sc, PMSG_RX_SUPPORT_REG); WR4(sc, PMSG_RX_SUPPORT_REG, val & ~PMSG_LTR_SUPPORT); /* Setting support for 64-bit pref window */ val = RD4(sc, PCIE_WINCONF); WR4(sc, PCIE_WINCONF, val | PREF_MEM_WIN_64_SUPPORT); /* Holding PCI endpoint reset (perst) for 100ms, setting the pin */ DELAY(100); err = gpio_pin_set_active(sc->perst_pin, false); if (err != 0) { device_printf(dev, "Cannot deassert perst pin: %d\n", err); goto out; } /* Setting up an address translation window */ jh7110_pcie_set_atr(dev, rman_get_start(sc->cfg_mem_res), 0, rman_get_size(sc->cfg_mem_res), win_idx); err = ofw_pcib_init(dev); if (err != 0) { device_printf(dev, "ofw_pcib_init() fails\n"); goto out; } jh7110_pcie_decode_ranges(sc, sc->ofw_pci.sc_range, sc->ofw_pci.sc_nrange); jh7110_pcie_set_atr(dev, sc->range_mem32.pci, sc->range_mem32.pci, sc->range_mem32.size, ++win_idx); jh7110_pcie_set_atr(dev, sc->range_mem64.pci, sc->range_mem64.pci, sc->range_mem64.size, ++win_idx); /* Checking data link status */ for (i = 0; i != 1000; i++) { val = SYSCON_READ_4(sc->stg_syscon, sc->stg_baddr + STG_LNKSTA_OFFSET); if ((val & STG_LINK_UP) != 0) { device_printf(dev, "Link up\n"); break; } DELAY(100); } if ((val & STG_LINK_UP) == 0) { device_printf(dev, "Cannot establish data link\n"); goto out; } /* Setup interrupts */ rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resource\n"); err = ENXIO; goto out_full; } err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, jh7110_pcie_intr, NULL, sc, &sc->irq_cookie); if (err != 0) { device_printf(dev, "Cannot setup interrupt handler\n"); err = ENXIO; goto out_full; } sc->isrcs = malloc(sizeof(*sc->isrcs) * MSI_COUNT, M_DEVBUF, M_WAITOK | M_ZERO); snprintf(name, INTR_ISRC_NAMELEN, "%s, MSI", device_get_nameunit(sc->dev)); for (irq = 0; irq < MSI_COUNT; irq++) { sc->isrcs[irq].irq = irq; err = intr_isrc_register(&sc->isrcs[irq].isrc, sc->dev, 0, "%s,%u", name, irq); if (err != 0) { device_printf(dev, "intr_isrs_register failed for MSI irq %d\n", irq); goto out_full; } } xref = OF_xref_from_node(sc->node); OF_device_register_xref(xref, dev); err = intr_msi_register(dev, xref); if (err != 0) { device_printf(dev, "intr_msi_register() fails\n"); goto out_full; } device_add_child(dev, "pci", DEVICE_UNIT_ANY); bus_attach_children(dev); return (0); out_full: ofw_pcib_fini(dev); out: jh7110_pcie_release_resources(dev); return (err); } static device_method_t jh7110_pcie_methods[] = { /* Device interface */ DEVMETHOD(device_probe, jh7110_pcie_probe), DEVMETHOD(device_attach, jh7110_pcie_attach), DEVMETHOD(device_detach, jh7110_pcie_detach), /* pcib interface */ DEVMETHOD(pcib_maxslots, jh7110_pcie_maxslots), DEVMETHOD(pcib_read_config, jh7110_pcie_read_config), DEVMETHOD(pcib_write_config, jh7110_pcie_write_config), DEVMETHOD(pcib_route_interrupt, jh7110_pcie_route_interrupt), DEVMETHOD(pcib_map_msi, jh7110_pcie_map_msi), DEVMETHOD(pcib_alloc_msi, jh7110_pcie_alloc_msi), DEVMETHOD(pcib_release_msi, jh7110_pcie_release_msi), DEVMETHOD(pcib_alloc_msix, jh7110_pcie_alloc_msix), DEVMETHOD(pcib_release_msix, jh7110_pcie_release_msix), DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), /* MSI/MSI-X */ DEVMETHOD(msi_alloc_msi, jh7110_pcie_msi_alloc_msi), DEVMETHOD(msi_alloc_msix, jh7110_pcie_msi_alloc_msix), DEVMETHOD(msi_release_msi, jh7110_pcie_msi_release_msi), DEVMETHOD(msi_release_msix, jh7110_pcie_msi_release_msix), DEVMETHOD(msi_map_msi, jh7110_pcie_msi_map_msi), /* Interrupt controller interface */ DEVMETHOD(pic_enable_intr, jh7110_pcie_msi_enable_intr), DEVMETHOD(pic_disable_intr, jh7110_pcie_msi_disable_intr), DEVMETHOD(pic_post_filter, jh7110_pcie_msi_post_filter), DEVMETHOD(pic_post_ithread, jh7110_pcie_msi_post_ithread), DEVMETHOD(pic_pre_ithread, jh7110_pcie_msi_pre_ithread), /* OFW bus interface */ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, jh7110_pcie_driver, jh7110_pcie_methods, sizeof(struct jh7110_pcie_softc), ofw_pcib_driver); DRIVER_MODULE(jh7110_pcie, simplebus, jh7110_pcie_driver, NULL, NULL);