Module Name: src Committed By: matt Date: Fri Sep 14 04:53:58 UTC 2012
Modified Files: src/sys/arch/arm/broadcom: bcm53xx_pax.c Log Message: Beginnings of PCI support. Interrupts still need to be done. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/broadcom/bcm53xx_pax.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/broadcom/bcm53xx_pax.c diff -u src/sys/arch/arm/broadcom/bcm53xx_pax.c:1.1 src/sys/arch/arm/broadcom/bcm53xx_pax.c:1.2 --- src/sys/arch/arm/broadcom/bcm53xx_pax.c:1.1 Sat Sep 1 00:04:44 2012 +++ src/sys/arch/arm/broadcom/bcm53xx_pax.c Fri Sep 14 04:53:58 2012 @@ -33,19 +33,30 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: bcm53xx_pax.c,v 1.1 2012/09/01 00:04:44 matt Exp $"); +__KERNEL_RCSID(1, "$NetBSD: bcm53xx_pax.c,v 1.2 2012/09/14 04:53:58 matt Exp $"); #include <sys/bus.h> #include <sys/device.h> +#include <sys/extent.h> #include <sys/intr.h> #include <sys/systm.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#include <dev/pci/pciconf.h> #include <arm/broadcom/bcm53xx_reg.h> #include <arm/broadcom/bcm53xx_var.h> +static const struct { + paddr_t owin_base; + psize_t owin_size; +} bcmpax_owins[] = { + [0] = { BCM53XX_PCIE0_OWIN_PBASE, BCM53XX_PCIE0_OWIN_SIZE }, + [1] = { BCM53XX_PCIE1_OWIN_PBASE, BCM53XX_PCIE1_OWIN_SIZE }, + [2] = { BCM53XX_PCIE2_OWIN_PBASE, BCM53XX_PCIE2_OWIN_SIZE }, +}; + static int bcmpax_ccb_match(device_t, cfdata_t, void *); static void bcmpax_ccb_attach(device_t, device_t, void *); @@ -54,6 +65,11 @@ struct bcmpax_softc { bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; bus_dma_tag_t sc_dmat; + kmutex_t *sc_lock; + kmutex_t *sc_cfg_lock; + bool sc_linkup; + int sc_pba_flags; + struct arm32_pci_chipset sc_pc; }; static inline uint32_t @@ -68,6 +84,26 @@ bcmpax_write_4(struct bcmpax_softc *sc, bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v); } +static void bcmpax_attach_hook(device_t, device_t, struct pcibus_attach_args *); +static int bcmpax_bus_maxdevs(void *, int); +static pcitag_t bcmpax_make_tag(void *, int, int, int); +static void bcmpax_decompose_tag(void *, pcitag_t, int *, int *, int *); +static pcireg_t bcmpax_conf_read(void *, pcitag_t, int); +static void bcmpax_conf_write(void *, pcitag_t, int, pcireg_t); + +static int bcmpax_intr_map(const struct pci_attach_args *, pci_intr_handle_t *); +static const char *bcmpax_intr_string(void *, pci_intr_handle_t); +static const struct evcnt *bcmpax_intr_evcnt(void *, pci_intr_handle_t); +static void *bcmpax_intr_establish(void *, pci_intr_handle_t, int, + int (*)(void *), void *); +static void bcmpax_intr_disestablish(void *, void *); + +#ifdef __HAVE_PCI_CONF_HOOK +static int bcmpax_conf_hook(void *, int, int, int, pcireg_t); +#endif +static void bcmpax_conf_interrupt(void *, int, int, int, int, int *); + + CFATTACH_DECL_NEW(bcmpax_ccb, sizeof(struct bcmpax_softc), bcmpax_ccb_match, bcmpax_ccb_attach, NULL, NULL); @@ -99,9 +135,15 @@ bcmpax_ccb_attach(device_t parent, devic sc->sc_bst = ccbaa->ccbaa_ccb_bst; sc->sc_dmat = ccbaa->ccbaa_dmat; + bus_space_subregion(sc->sc_bst, ccbaa->ccbaa_ccb_bsh, loc->loc_offset, loc->loc_size, &sc->sc_bsh); + bcmpax_write_4(sc, PCIE_CLK_CONTROL, 3); + delay(250); + bcmpax_write_4(sc, PCIE_CLK_CONTROL, 1); + // delay(100*1000); + uint32_t v = bcmpax_read_4(sc, PCIE_STRAP_STATUS); const bool enabled = (v & STRAP_PCIE_IF_ENABLE) != 0; const bool is_v2_p = (v & STRAP_PCIE_USER_FOR_CE_GEN1) == 0; @@ -116,8 +158,114 @@ bcmpax_ccb_attach(device_t parent, devic enabled ? "" : "(disabled)"); if (!enabled || !is_rc_p) return; + + sc->sc_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_VM); + sc->sc_cfg_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_VM); + + sc->sc_pc.pc_conf_v = sc; + sc->sc_pc.pc_attach_hook = bcmpax_attach_hook; + sc->sc_pc.pc_bus_maxdevs = bcmpax_bus_maxdevs; + sc->sc_pc.pc_make_tag = bcmpax_make_tag; + sc->sc_pc.pc_decompose_tag = bcmpax_decompose_tag; + sc->sc_pc.pc_conf_read = bcmpax_conf_read; + sc->sc_pc.pc_conf_write = bcmpax_conf_write; + + sc->sc_pc.pc_intr_v = sc; + sc->sc_pc.pc_intr_map = bcmpax_intr_map; + sc->sc_pc.pc_intr_string = bcmpax_intr_string; + sc->sc_pc.pc_intr_evcnt = bcmpax_intr_evcnt; + sc->sc_pc.pc_intr_establish = bcmpax_intr_establish; + sc->sc_pc.pc_intr_disestablish = bcmpax_intr_disestablish; + +#ifdef __HAVE_PCI_CONF_HOOK + sc->sc_pc.pc_conf_hook = bcmpax_conf_hook; +#endif + sc->sc_pc.pc_conf_interrupt = bcmpax_conf_interrupt; + + // sc->sc_pba_flags |= PCI_FLAGS_MSI_OKAY; + // sc->sc_pba_flags |= PCI_FLAGS_MSIX_OKAY; + + int offset; + const bool ok = pci_get_capability(&sc->sc_pc, 0, PCI_CAP_PCIEXPRESS, + &offset, NULL); + KASSERT(ok); + + /* + * Now we wait (.1sec) for the link to come up. + */ + offset += PCI_PCIE_LCSR; + for (size_t timo = 0;; timo++) { + const pcireg_t lcsr = bcmpax_conf_read(sc, 0, offset); + sc->sc_linkup = __SHIFTOUT(lcsr, PCI_PCIE_LCSR_NLW) != 0 + && (1 || (lcsr & PCI_PCIE_LCSR_DLACTIVE) != 0); + if (sc->sc_linkup || timo == 250) { + aprint_debug_dev(self, + "lcsr=%#x nlw=%jd linkup=%d, timo=%zu\n", + lcsr, __SHIFTOUT(lcsr, PCI_PCIE_LCSR_NLW), + sc->sc_linkup, timo); + break; + } + DELAY(1000); + } + + if (sc->sc_linkup) { + paddr_t base = bcmpax_owins[loc->loc_port].owin_base; + psize_t size = bcmpax_owins[loc->loc_port].owin_size; + KASSERT((size & ~PCIE_OARR_ADDR) == 0); + if (size > 0) { + bcmpax_write_4(sc, PCIE_OARR_0, base); + bcmpax_write_4(sc, PCIE_OMAP_0_LOWER, base | 1); + } + if (size > __LOWEST_SET_BIT(PCIE_OARR_ADDR)) { + paddr_t base1 = base + __LOWEST_SET_BIT(PCIE_OARR_ADDR); + bcmpax_write_4(sc, PCIE_OARR_1, base1); + bcmpax_write_4(sc, PCIE_OMAP_1_LOWER, base1 | 1); + } + + struct extent *memext = extent_create("pcimem", base, + base + size, NULL, 0, EX_NOWAIT); + + int error = pci_configure_bus(&sc->sc_pc, + NULL, memext, NULL, 0, arm_pcache.dcache_line_size); + + extent_destroy(memext); + + if (error) { + aprint_normal_dev(self, "configuration failed\n"); + return; + } + } + + struct pcibus_attach_args pba; + memset(&pba, 0, sizeof(pba)); + + pba.pba_flags = sc->sc_pba_flags; + pba.pba_flags |= PCI_FLAGS_MEM_OKAY; + pba.pba_memt = sc->sc_bst; + pba.pba_dmat = sc->sc_dmat; + pba.pba_pc = &sc->sc_pc; + pba.pba_bus = 0; + + config_found_ia(self, "pcibus", &pba, pcibusprint); +} + +static void +bcmpax_attach_hook(device_t parent, device_t self, + struct pcibus_attach_args *pba) +{ +} + +static int +bcmpax_bus_maxdevs(void *v, int bus) +{ + struct bcmpax_softc * const sc = v; + + if (__predict_true(sc->sc_linkup)) + return bus > 1 ? 32 : 1; + + return bus ? 0 : 1; } -#if 0 + static void bcmpax_decompose_tag(void *v, pcitag_t tag, int *busp, int *devp, int *funcp) { @@ -138,19 +286,122 @@ bcmpax_make_tag(void *v, int bus, int de | (bus == 0 ? CFG_ADDR_TYPE0 : CFG_ADDR_TYPE1); } -static inline void +static inline bus_size_t bcmpax_conf_addr_write(struct bcmpax_softc *sc, pcitag_t tag) { if ((tag & (CFG_ADDR_BUS|CFG_ADDR_DEV)) == 0) { uint32_t reg = __SHIFTOUT(tag, CFG_ADDR_REG); uint32_t func = __SHIFTOUT(tag, CFG_ADDR_FUNC); - bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIE_CFG_IND_ADDR, + bcmpax_write_4(sc, PCIE_CFG_IND_ADDR, __SHIFTIN(func, CFG_IND_ADDR_FUNC) | __SHIFTIN(reg, CFG_IND_ADDR_REG)); - } else { - bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIE_CFG_ADDR, tag); + __asm __volatile("dsb"); + return PCIE_CFG_IND_DATA; } - __asm __volatile("dsb"); + if (sc->sc_linkup) { + bcmpax_write_4(sc, PCIE_CFG_ADDR, tag); + __asm __volatile("dsb"); + return PCIE_CFG_DATA; + } + return 0; } -#endif +static pcireg_t +bcmpax_conf_read(void *v, pcitag_t tag, int reg) +{ + struct bcmpax_softc * const sc = v; + + /* + * Even in RC mode, the PCI Express Root Complex return itself + * as BCM Ethernet Controller!. We could change ppb.c to match it + * but we'll just lie and say we are a PPB bridge. + */ + if ((tag & (CFG_ADDR_BUS|CFG_ADDR_DEV|CFG_ADDR_FUNC)) == 0 + && reg == PCI_CLASS_REG) { + return PCI_CLASS_CODE(PCI_CLASS_BRIDGE, + PCI_SUBCLASS_BRIDGE_PCI, 0); + } + + //printf("%s: tag %#lx reg %#x:", __func__, tag, reg); + + mutex_enter(sc->sc_cfg_lock); + bus_size_t data_reg = bcmpax_conf_addr_write(sc, tag | reg); + + //printf(" [from %#lx]:\n", data_reg); + + pcireg_t rv; + if (data_reg) + rv = bcmpax_read_4(sc, data_reg); + else + rv = 0xffffffff; + + mutex_exit(sc->sc_cfg_lock); + + //printf(" %#x\n", rv); + + return rv; +} + +static void +bcmpax_conf_write(void *v, pcitag_t tag, int reg, pcireg_t val) +{ + struct bcmpax_softc * const sc = v; + + mutex_enter(sc->sc_cfg_lock); + bus_size_t data_reg = bcmpax_conf_addr_write(sc, tag | reg); + + //printf("%s: tag %#lx reg %#x:", __func__, tag, reg); + + if (data_reg) { + //printf(" [to %#lx]:\n", data_reg); + bcmpax_write_4(sc, data_reg, val); + //printf(" %#x\n", val); + } + + mutex_exit(sc->sc_cfg_lock); +} + +static void +bcmpax_conf_interrupt(void *v, int bus, int dev, int ipin, int swiz, int *ilinep) +{ + *ilinep = 5; /* (ipin + swiz) & 3; */ +} + +static int +bcmpax_conf_hook(void *v, int bus, int dev, int func, pcireg_t id) +{ + if (func > 0) + return 0; + + return PCI_CONF_ENABLE_MEM | PCI_CONF_MAP_MEM; +} + +static int +bcmpax_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *pihp) +{ + return EINVAL; +} + +static const char * +bcmpax_intr_string(void *v, pci_intr_handle_t pih) +{ + return NULL; +} + +static const struct evcnt * +bcmpax_intr_evcnt(void *v, pci_intr_handle_t pih) +{ + return NULL; +} + +static void * +bcmpax_intr_establish(void *v, pci_intr_handle_t pih, int ipl, + int (*func)(void *), void *arg) +{ + return NULL; +} + +static void +bcmpax_intr_disestablish(void *v, void *ih) +{ +}