xlp_pci.c revision 233563
1225394Sjchandra/*- 2233563Sjchandra * Copyright (c) 2003-2012 Broadcom Corporation 3233563Sjchandra * All Rights Reserved 4225394Sjchandra * 5225394Sjchandra * Redistribution and use in source and binary forms, with or without 6225394Sjchandra * modification, are permitted provided that the following conditions 7225394Sjchandra * are met: 8233563Sjchandra * 9225394Sjchandra * 1. Redistributions of source code must retain the above copyright 10225394Sjchandra * notice, this list of conditions and the following disclaimer. 11225394Sjchandra * 2. Redistributions in binary form must reproduce the above copyright 12233563Sjchandra * notice, this list of conditions and the following disclaimer in 13233563Sjchandra * the documentation and/or other materials provided with the 14233563Sjchandra * distribution. 15233563Sjchandra * 16233563Sjchandra * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR 17233563Sjchandra * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18233563Sjchandra * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19233563Sjchandra * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE 20233563Sjchandra * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21233563Sjchandra * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22233563Sjchandra * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23233563Sjchandra * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24233563Sjchandra * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25233563Sjchandra * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26233563Sjchandra * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27233563Sjchandra */ 28233563Sjchandra 29225394Sjchandra#include <sys/cdefs.h> 30225394Sjchandra__FBSDID("$FreeBSD: head/sys/mips/nlm/xlp_pci.c 233563 2012-03-27 15:39:55Z jchandra $"); 31225394Sjchandra 32225394Sjchandra#include <sys/param.h> 33225394Sjchandra#include <sys/systm.h> 34225394Sjchandra#include <sys/types.h> 35225394Sjchandra#include <sys/kernel.h> 36225394Sjchandra#include <sys/module.h> 37225394Sjchandra#include <sys/malloc.h> 38225394Sjchandra#include <sys/bus.h> 39225394Sjchandra#include <sys/endian.h> 40225394Sjchandra#include <sys/rman.h> 41233563Sjchandra#include <sys/pciio.h> 42225394Sjchandra 43225394Sjchandra#include <vm/vm.h> 44225394Sjchandra#include <vm/vm_param.h> 45225394Sjchandra#include <vm/pmap.h> 46225394Sjchandra 47225394Sjchandra#include <dev/pci/pcivar.h> 48225394Sjchandra#include <dev/pci/pcireg.h> 49233563Sjchandra#include <dev/pci/pci_private.h> 50233563Sjchandra 51225394Sjchandra#include <dev/uart/uart.h> 52225394Sjchandra#include <dev/uart/uart_bus.h> 53225394Sjchandra#include <dev/uart/uart_cpu.h> 54225394Sjchandra 55225394Sjchandra#include <machine/bus.h> 56225394Sjchandra#include <machine/md_var.h> 57225394Sjchandra#include <machine/intr_machdep.h> 58225394Sjchandra#include <machine/cpuregs.h> 59225394Sjchandra 60225394Sjchandra#include <mips/nlm/hal/haldefs.h> 61225394Sjchandra#include <mips/nlm/interrupt.h> 62225394Sjchandra#include <mips/nlm/hal/iomap.h> 63225394Sjchandra#include <mips/nlm/hal/mips-extns.h> 64225394Sjchandra#include <mips/nlm/hal/pic.h> 65233536Sjchandra#include <mips/nlm/hal/bridge.h> 66233556Sjchandra#include <mips/nlm/hal/gbu.h> 67225394Sjchandra#include <mips/nlm/hal/pcibus.h> 68225394Sjchandra#include <mips/nlm/hal/uart.h> 69225394Sjchandra#include <mips/nlm/xlp.h> 70225394Sjchandra 71225394Sjchandra#include "pcib_if.h" 72233563Sjchandra#include "pci_if.h" 73225394Sjchandra 74233563Sjchandra#define EMUL_MEM_START 0x16000000UL 75233563Sjchandra#define EMUL_MEM_END 0x18ffffffUL 76233563Sjchandra 77233563Sjchandra/* SoC device qurik handling */ 78233563Sjchandrastatic int irt_irq_map[4 * 256]; 79233563Sjchandrastatic int irq_irt_map[64]; 80233563Sjchandra 81233563Sjchandrastatic void 82233563Sjchandraxlp_add_irq(int node, int irt, int irq) 83233563Sjchandra{ 84233563Sjchandra int nodeirt = node * 256 + irt; 85233563Sjchandra 86233563Sjchandra irt_irq_map[nodeirt] = irq; 87233563Sjchandra irq_irt_map[irq] = nodeirt; 88233563Sjchandra} 89233563Sjchandra 90233563Sjchandraint 91233563Sjchandraxlp_irq_to_irt(int irq) 92233563Sjchandra{ 93233563Sjchandra return irq_irt_map[irq]; 94233563Sjchandra} 95233563Sjchandra 96233563Sjchandraint 97233563Sjchandraxlp_irt_to_irq(int nodeirt) 98233563Sjchandra{ 99233563Sjchandra return irt_irq_map[nodeirt]; 100233563Sjchandra} 101233563Sjchandra 102233563Sjchandra/* Override PCI a bit for SoC devices */ 103233563Sjchandra 104233563Sjchandraenum { 105233563Sjchandra INTERNAL_DEV = 0x1, /* internal device, skip on enumeration */ 106233563Sjchandra MEM_RES_EMUL = 0x2, /* no MEM or IO bar, custom res alloc */ 107233563Sjchandra SHARED_IRQ = 0x4, 108233563Sjchandra DEV_MMIO32 = 0x8, /* byte access not allowed to mmio */ 109225394Sjchandra}; 110225394Sjchandra 111233563Sjchandrastruct soc_dev_desc { 112233563Sjchandra u_int devid; /* device ID */ 113233563Sjchandra int irqbase; /* start IRQ */ 114233563Sjchandra u_int flags; /* flags */ 115233563Sjchandra int ndevs; /* to keep track of number of devices */ 116233563Sjchandra}; 117233563Sjchandra 118233563Sjchandrastruct soc_dev_desc xlp_dev_desc[] = { 119233563Sjchandra { PCI_DEVICE_ID_NLM_ICI, 0, INTERNAL_DEV }, 120233563Sjchandra { PCI_DEVICE_ID_NLM_PIC, 0, INTERNAL_DEV }, 121233563Sjchandra { PCI_DEVICE_ID_NLM_FMN, 0, INTERNAL_DEV }, 122233563Sjchandra { PCI_DEVICE_ID_NLM_UART, PIC_UART_0_IRQ, MEM_RES_EMUL | DEV_MMIO32}, 123233563Sjchandra { PCI_DEVICE_ID_NLM_I2C, 0, MEM_RES_EMUL | DEV_MMIO32 }, 124233563Sjchandra { PCI_DEVICE_ID_NLM_NOR, 0, MEM_RES_EMUL }, 125233563Sjchandra { PCI_DEVICE_ID_NLM_MMC, PIC_MMC_IRQ, MEM_RES_EMUL }, 126233563Sjchandra { PCI_DEVICE_ID_NLM_EHCI, PIC_EHCI_0_IRQ, 0 } 127233563Sjchandra}; 128233563Sjchandra 129233563Sjchandrastruct xlp_devinfo { 130233563Sjchandra struct pci_devinfo pcidev; 131233563Sjchandra int irq; 132233563Sjchandra int flags; 133233563Sjchandra u_long mem_res_start; 134233563Sjchandra}; 135233563Sjchandra 136233563Sjchandrastatic __inline struct soc_dev_desc * 137233563Sjchandraxlp_find_soc_desc(int devid) 138233563Sjchandra{ 139233563Sjchandra struct soc_dev_desc *p; 140233563Sjchandra int i, n; 141233563Sjchandra 142233563Sjchandra n = sizeof(xlp_dev_desc) / sizeof(xlp_dev_desc[0]); 143233563Sjchandra for (i = 0, p = xlp_dev_desc; i < n; i++, p++) 144233563Sjchandra if (p->devid == devid) 145233563Sjchandra return (p); 146233563Sjchandra return (NULL); 147233563Sjchandra} 148233563Sjchandra 149233563Sjchandrastatic struct resource * 150233563Sjchandraxlp_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, 151233563Sjchandra u_long start, u_long end, u_long count, u_int flags) 152233563Sjchandra{ 153233563Sjchandra struct resource *r; 154233563Sjchandra struct xlp_devinfo *xlp_devinfo; 155233563Sjchandra int busno; 156233563Sjchandra 157233563Sjchandra /* 158233563Sjchandra * Do custom allocation for MEMORY resource for SoC device if 159233563Sjchandra * MEM_RES_EMUL flag is set 160233563Sjchandra */ 161233563Sjchandra busno = pci_get_bus(child); 162233563Sjchandra if ((type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) && busno == 0) { 163233563Sjchandra xlp_devinfo = (struct xlp_devinfo *)device_get_ivars(child); 164233563Sjchandra if ((xlp_devinfo->flags & MEM_RES_EMUL) != 0) { 165233563Sjchandra /* no emulation for IO ports */ 166233563Sjchandra if (type == SYS_RES_IOPORT) 167233563Sjchandra return (NULL); 168233563Sjchandra start = xlp_devinfo->mem_res_start; 169233563Sjchandra count = XLP_PCIE_CFG_SIZE - XLP_IO_PCI_HDRSZ; 170233563Sjchandra end = start + count - 1; 171233563Sjchandra r = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 172233563Sjchandra type, rid, start, end, count, flags); 173233563Sjchandra if (r == NULL) 174233563Sjchandra return (NULL); 175233563Sjchandra if ((xlp_devinfo->flags & DEV_MMIO32) != 0) 176233563Sjchandra rman_set_bustag(r, rmi_uart_bus_space); 177233563Sjchandra return (r); 178233563Sjchandra } 179233563Sjchandra } 180233563Sjchandra 181233563Sjchandra /* Not custom alloc, use PCI code */ 182233563Sjchandra return (pci_alloc_resource(bus, child, type, rid, start, end, count, 183233563Sjchandra flags)); 184233563Sjchandra} 185233563Sjchandra 186233563Sjchandrastatic int 187233563Sjchandraxlp_pci_release_resource(device_t bus, device_t child, int type, int rid, 188233563Sjchandra struct resource *r) 189233563Sjchandra{ 190233563Sjchandra u_long start; 191233563Sjchandra 192233563Sjchandra /* If custom alloc, handle that */ 193233563Sjchandra start = rman_get_start(r); 194233563Sjchandra if (type == SYS_RES_MEMORY && pci_get_bus(child) == 0 && 195233563Sjchandra start >= EMUL_MEM_START && start <= EMUL_MEM_END) 196233563Sjchandra return (BUS_RELEASE_RESOURCE(device_get_parent(bus), child, 197233563Sjchandra type, rid, r)); 198233563Sjchandra 199233563Sjchandra /* use default PCI function */ 200233563Sjchandra return (bus_generic_rl_release_resource(bus, child, type, rid, r)); 201233563Sjchandra} 202233563Sjchandra 203233563Sjchandrastatic void 204233563Sjchandraxlp_add_soc_child(device_t pcib, device_t dev, int b, int s, int f) 205233563Sjchandra{ 206233563Sjchandra struct pci_devinfo *dinfo; 207233563Sjchandra struct xlp_devinfo *xlp_dinfo; 208233563Sjchandra struct soc_dev_desc *si; 209233563Sjchandra uint64_t pcibase; 210233563Sjchandra int domain, node, irt, irq, flags, devoffset, num; 211233563Sjchandra uint16_t devid; 212233563Sjchandra 213233563Sjchandra domain = pcib_get_domain(dev); 214233563Sjchandra node = s / 8; 215233563Sjchandra devoffset = XLP_HDR_OFFSET(node, 0, s % 8, f); 216233563Sjchandra if (!nlm_dev_exists(devoffset)) 217233563Sjchandra return; 218233563Sjchandra 219233563Sjchandra /* Find if there is a desc for the SoC device */ 220233563Sjchandra devid = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_DEVICE, 2); 221233563Sjchandra si = xlp_find_soc_desc(devid); 222233563Sjchandra 223233563Sjchandra /* update flags and irq from desc if available */ 224233563Sjchandra irq = 0; 225233563Sjchandra flags = 0; 226233563Sjchandra if (si != NULL) { 227233563Sjchandra if (si->irqbase != 0) 228233563Sjchandra irq = si->irqbase + si->ndevs; 229233563Sjchandra flags = si->flags; 230233563Sjchandra si->ndevs++; 231233563Sjchandra } 232233563Sjchandra 233233563Sjchandra /* skip internal devices */ 234233563Sjchandra if ((flags & INTERNAL_DEV) != 0) 235233563Sjchandra return; 236233563Sjchandra 237233563Sjchandra /* PCIe interfaces are special, bug in Ax */ 238233563Sjchandra if (devid == PCI_DEVICE_ID_NLM_PCIE) { 239233563Sjchandra xlp_add_irq(node, xlp_pcie_link_irt(f), PIC_PCIE_0_IRQ + f); 240233563Sjchandra } else { 241233563Sjchandra /* Stash intline and pin in shadow reg for devices */ 242233563Sjchandra pcibase = nlm_pcicfg_base(devoffset); 243233563Sjchandra irt = nlm_irtstart(pcibase); 244233563Sjchandra num = nlm_irtnum(pcibase); 245233563Sjchandra if (irq != 0 && num > 0) { 246233563Sjchandra xlp_add_irq(node, irt, irq); 247233563Sjchandra nlm_write_reg(pcibase, XLP_PCI_DEVSCRATCH_REG0, 248233563Sjchandra (1 << 8) | irq); 249233563Sjchandra } 250233563Sjchandra } 251233563Sjchandra dinfo = pci_read_device(pcib, domain, b, s, f, sizeof(*xlp_dinfo)); 252233563Sjchandra if (dinfo == NULL) 253233563Sjchandra return; 254233563Sjchandra xlp_dinfo = (struct xlp_devinfo *)dinfo; 255233563Sjchandra xlp_dinfo->irq = irq; 256233563Sjchandra xlp_dinfo->flags = flags; 257233563Sjchandra if ((flags & MEM_RES_EMUL) != 0) 258233563Sjchandra xlp_dinfo->mem_res_start = XLP_DEFAULT_IO_BASE + devoffset + 259233563Sjchandra XLP_IO_PCI_HDRSZ; 260233563Sjchandra pci_add_child(dev, dinfo); 261233563Sjchandra} 262233563Sjchandra 263233563Sjchandrastatic int 264233563Sjchandraxlp_pci_attach(device_t dev) 265233563Sjchandra{ 266233563Sjchandra device_t pcib = device_get_parent(dev); 267233563Sjchandra int maxslots, s, f, pcifunchigh; 268233563Sjchandra int busno; 269233563Sjchandra uint8_t hdrtype; 270233563Sjchandra 271233563Sjchandra /* 272233563Sjchandra * The on-chip devices are on a bus that is almost, but not 273233563Sjchandra * quite, completely like PCI. Add those things by hand. 274233563Sjchandra */ 275233563Sjchandra busno = pcib_get_bus(dev); 276233563Sjchandra maxslots = PCIB_MAXSLOTS(pcib); 277233563Sjchandra for (s = 0; s <= maxslots; s++) { 278233563Sjchandra pcifunchigh = 0; 279233563Sjchandra f = 0; 280233563Sjchandra hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1); 281233563Sjchandra if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 282233563Sjchandra continue; 283233563Sjchandra if (hdrtype & PCIM_MFDEV) 284233563Sjchandra pcifunchigh = PCI_FUNCMAX; 285233563Sjchandra for (f = 0; f <= pcifunchigh; f++) 286233563Sjchandra xlp_add_soc_child(pcib, dev, busno, s, f); 287233563Sjchandra } 288233563Sjchandra return (bus_generic_attach(dev)); 289233563Sjchandra} 290233563Sjchandra 291233563Sjchandrastatic int 292233563Sjchandraxlp_pci_probe(device_t dev) 293233563Sjchandra{ 294233563Sjchandra device_t pcib; 295233563Sjchandra 296233563Sjchandra pcib = device_get_parent(dev); 297233563Sjchandra /* 298233563Sjchandra * Only the top level bus has SoC devices, leave the rest to 299233563Sjchandra * Generic PCI code 300233563Sjchandra */ 301233563Sjchandra if (strcmp(device_get_nameunit(pcib), "pcib0") != 0) 302233563Sjchandra return (ENXIO); 303233563Sjchandra device_set_desc(dev, "XLP SoCbus"); 304233563Sjchandra return (BUS_PROBE_DEFAULT); 305233563Sjchandra} 306233563Sjchandra 307233563Sjchandrastatic devclass_t pci_devclass; 308233563Sjchandrastatic device_method_t xlp_pci_methods[] = { 309233563Sjchandra /* Device interface */ 310233563Sjchandra DEVMETHOD(device_probe, xlp_pci_probe), 311233563Sjchandra DEVMETHOD(device_attach, xlp_pci_attach), 312233563Sjchandra DEVMETHOD(bus_alloc_resource, xlp_pci_alloc_resource), 313233563Sjchandra DEVMETHOD(bus_release_resource, xlp_pci_release_resource), 314233563Sjchandra 315233563Sjchandra DEVMETHOD_END 316233563Sjchandra}; 317233563Sjchandra 318233563SjchandraDEFINE_CLASS_1(pci, xlp_pci_driver, xlp_pci_methods, 0, pci_driver); 319233563SjchandraDRIVER_MODULE(xlp_pci, pcib, xlp_pci_driver, pci_devclass, 0, 0); 320233563Sjchandra 321225394Sjchandrastatic devclass_t pcib_devclass; 322225394Sjchandrastatic struct rman irq_rman, port_rman, mem_rman, emul_rman; 323225394Sjchandra 324225394Sjchandrastatic void 325233563Sjchandraxlp_pcib_init_resources(void) 326225394Sjchandra{ 327225394Sjchandra irq_rman.rm_start = 0; 328225394Sjchandra irq_rman.rm_end = 255; 329225394Sjchandra irq_rman.rm_type = RMAN_ARRAY; 330225394Sjchandra irq_rman.rm_descr = "PCI Mapped Interrupts"; 331225394Sjchandra if (rman_init(&irq_rman) 332225394Sjchandra || rman_manage_region(&irq_rman, 0, 255)) 333225394Sjchandra panic("pci_init_resources irq_rman"); 334225394Sjchandra 335225394Sjchandra port_rman.rm_start = 0; 336225394Sjchandra port_rman.rm_end = ~0ul; 337225394Sjchandra port_rman.rm_type = RMAN_ARRAY; 338225394Sjchandra port_rman.rm_descr = "I/O ports"; 339225394Sjchandra if (rman_init(&port_rman) 340233536Sjchandra || rman_manage_region(&port_rman, PCIE_IO_BASE, PCIE_IO_LIMIT)) 341225394Sjchandra panic("pci_init_resources port_rman"); 342225394Sjchandra 343225394Sjchandra mem_rman.rm_start = 0; 344225394Sjchandra mem_rman.rm_end = ~0ul; 345225394Sjchandra mem_rman.rm_type = RMAN_ARRAY; 346225394Sjchandra mem_rman.rm_descr = "I/O memory"; 347225394Sjchandra if (rman_init(&mem_rman) 348233536Sjchandra || rman_manage_region(&mem_rman, PCIE_MEM_BASE, PCIE_MEM_LIMIT)) 349225394Sjchandra panic("pci_init_resources mem_rman"); 350225394Sjchandra 351233536Sjchandra /* 352233536Sjchandra * This includes the GBU (nor flash) memory range and the PCIe 353233536Sjchandra * memory area. 354233536Sjchandra */ 355225394Sjchandra emul_rman.rm_start = 0; 356225394Sjchandra emul_rman.rm_end = ~0ul; 357225394Sjchandra emul_rman.rm_type = RMAN_ARRAY; 358225394Sjchandra emul_rman.rm_descr = "Emulated MEMIO"; 359225394Sjchandra if (rman_init(&emul_rman) 360233563Sjchandra || rman_manage_region(&emul_rman, EMUL_MEM_START, EMUL_MEM_END)) 361225394Sjchandra panic("pci_init_resources emul_rman"); 362225394Sjchandra} 363225394Sjchandra 364225394Sjchandrastatic int 365225394Sjchandraxlp_pcib_probe(device_t dev) 366225394Sjchandra{ 367225394Sjchandra 368225394Sjchandra device_set_desc(dev, "XLP PCI bus"); 369233563Sjchandra xlp_pcib_init_resources(); 370225394Sjchandra return (0); 371225394Sjchandra} 372225394Sjchandra 373225394Sjchandrastatic int 374225394Sjchandraxlp_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 375225394Sjchandra{ 376225394Sjchandra 377225394Sjchandra switch (which) { 378225394Sjchandra case PCIB_IVAR_DOMAIN: 379225394Sjchandra *result = 0; 380225394Sjchandra return (0); 381225394Sjchandra case PCIB_IVAR_BUS: 382225394Sjchandra *result = 0; 383225394Sjchandra return (0); 384225394Sjchandra } 385225394Sjchandra return (ENOENT); 386225394Sjchandra} 387225394Sjchandra 388225394Sjchandrastatic int 389225394Sjchandraxlp_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t result) 390225394Sjchandra{ 391225394Sjchandra switch (which) { 392225394Sjchandra case PCIB_IVAR_DOMAIN: 393225394Sjchandra return (EINVAL); 394225394Sjchandra case PCIB_IVAR_BUS: 395225394Sjchandra return (EINVAL); 396225394Sjchandra } 397225394Sjchandra return (ENOENT); 398225394Sjchandra} 399225394Sjchandra 400225394Sjchandrastatic int 401225394Sjchandraxlp_pcib_maxslots(device_t dev) 402225394Sjchandra{ 403225394Sjchandra 404225394Sjchandra return (PCI_SLOTMAX); 405225394Sjchandra} 406225394Sjchandra 407225394Sjchandrastatic u_int32_t 408225394Sjchandraxlp_pcib_read_config(device_t dev, u_int b, u_int s, u_int f, 409225394Sjchandra u_int reg, int width) 410225394Sjchandra{ 411225394Sjchandra uint32_t data = 0; 412225394Sjchandra uint64_t cfgaddr; 413225394Sjchandra int regindex = reg/sizeof(uint32_t); 414225394Sjchandra 415225394Sjchandra cfgaddr = nlm_pcicfg_base(XLP_HDR_OFFSET(0, b, s, f)); 416225394Sjchandra if ((width == 2) && (reg & 1)) 417225394Sjchandra return 0xFFFFFFFF; 418225394Sjchandra else if ((width == 4) && (reg & 3)) 419225394Sjchandra return 0xFFFFFFFF; 420225394Sjchandra 421225394Sjchandra /* 422233563Sjchandra * The intline and int pin of SoC devices are DOA, except 423233563Sjchandra * for bridges (slot %8 == 1). 424233563Sjchandra * use the values we stashed in a writable PCI scratch reg. 425225394Sjchandra */ 426233563Sjchandra if (b == 0 && regindex == 0xf && s % 8 > 1) 427233563Sjchandra regindex = XLP_PCI_DEVSCRATCH_REG0; 428225394Sjchandra 429233563Sjchandra data = nlm_read_pci_reg(cfgaddr, regindex); 430225394Sjchandra if (width == 1) 431225394Sjchandra return ((data >> ((reg & 3) << 3)) & 0xff); 432225394Sjchandra else if (width == 2) 433225394Sjchandra return ((data >> ((reg & 3) << 3)) & 0xffff); 434225394Sjchandra else 435225394Sjchandra return (data); 436225394Sjchandra} 437225394Sjchandra 438225394Sjchandrastatic void 439225394Sjchandraxlp_pcib_write_config(device_t dev, u_int b, u_int s, u_int f, 440225394Sjchandra u_int reg, u_int32_t val, int width) 441225394Sjchandra{ 442225394Sjchandra uint64_t cfgaddr; 443225394Sjchandra uint32_t data = 0; 444225394Sjchandra int regindex = reg / sizeof(uint32_t); 445225394Sjchandra 446225394Sjchandra cfgaddr = nlm_pcicfg_base(XLP_HDR_OFFSET(0, b, s, f)); 447225394Sjchandra if ((width == 2) && (reg & 1)) 448225394Sjchandra return; 449225394Sjchandra else if ((width == 4) && (reg & 3)) 450225394Sjchandra return; 451225394Sjchandra 452225394Sjchandra if (width == 1) { 453225394Sjchandra data = nlm_read_pci_reg(cfgaddr, regindex); 454225394Sjchandra data = (data & ~(0xff << ((reg & 3) << 3))) | 455225394Sjchandra (val << ((reg & 3) << 3)); 456225394Sjchandra } else if (width == 2) { 457225394Sjchandra data = nlm_read_pci_reg(cfgaddr, regindex); 458225394Sjchandra data = (data & ~(0xffff << ((reg & 3) << 3))) | 459225394Sjchandra (val << ((reg & 3) << 3)); 460225394Sjchandra } else { 461225394Sjchandra data = val; 462225394Sjchandra } 463225394Sjchandra 464233563Sjchandra /* 465233563Sjchandra * use shadow reg for intpin/intline which are dead 466233563Sjchandra */ 467233563Sjchandra if (b == 0 && regindex == 0xf && s % 8 > 1) 468233563Sjchandra regindex = XLP_PCI_DEVSCRATCH_REG0; 469225394Sjchandra nlm_write_pci_reg(cfgaddr, regindex, data); 470225394Sjchandra} 471225394Sjchandra 472233536Sjchandra/* 473233536Sjchandra * Enable byte swap in hardware. Program a link's PCIe SWAP regions 474233536Sjchandra * from the link's IO and MEM address ranges. 475233536Sjchandra */ 476233536Sjchandrastatic void 477233563Sjchandraxlp_pcib_hardware_swap_enable(int node, int link) 478233536Sjchandra{ 479233536Sjchandra uint64_t bbase, linkpcibase; 480233536Sjchandra uint32_t bar; 481233536Sjchandra int pcieoffset; 482233536Sjchandra 483233536Sjchandra pcieoffset = XLP_IO_PCIE_OFFSET(node, link); 484233536Sjchandra if (!nlm_dev_exists(pcieoffset)) 485233536Sjchandra return; 486233536Sjchandra 487233536Sjchandra bbase = nlm_get_bridge_regbase(node); 488233536Sjchandra linkpcibase = nlm_pcicfg_base(pcieoffset); 489233536Sjchandra bar = nlm_read_bridge_reg(bbase, BRIDGE_PCIEMEM_BASE0 + link); 490233536Sjchandra nlm_write_pci_reg(linkpcibase, PCIE_BYTE_SWAP_MEM_BASE, bar); 491233536Sjchandra 492233536Sjchandra bar = nlm_read_bridge_reg(bbase, BRIDGE_PCIEMEM_LIMIT0 + link); 493233536Sjchandra nlm_write_pci_reg(linkpcibase, PCIE_BYTE_SWAP_MEM_LIM, bar); 494233536Sjchandra 495233536Sjchandra bar = nlm_read_bridge_reg(bbase, BRIDGE_PCIEIO_BASE0 + link); 496233536Sjchandra nlm_write_pci_reg(linkpcibase, PCIE_BYTE_SWAP_IO_BASE, bar); 497233536Sjchandra 498233536Sjchandra bar = nlm_read_bridge_reg(bbase, BRIDGE_PCIEIO_LIMIT0 + link); 499233536Sjchandra nlm_write_pci_reg(linkpcibase, PCIE_BYTE_SWAP_IO_LIM, bar); 500233536Sjchandra} 501233536Sjchandra 502225394Sjchandrastatic int 503225394Sjchandraxlp_pcib_attach(device_t dev) 504225394Sjchandra{ 505233536Sjchandra int node, link; 506225394Sjchandra 507233536Sjchandra /* enable hardware swap on all nodes/links */ 508233536Sjchandra for (node = 0; node < XLP_MAX_NODES; node++) 509233536Sjchandra for (link = 0; link < 4; link++) 510233563Sjchandra xlp_pcib_hardware_swap_enable(node, link); 511233536Sjchandra 512225394Sjchandra device_add_child(dev, "pci", 0); 513225394Sjchandra bus_generic_attach(dev); 514225394Sjchandra return (0); 515225394Sjchandra} 516225394Sjchandra 517225394Sjchandrastatic void 518225394Sjchandraxlp_pcib_identify(driver_t * driver, device_t parent) 519225394Sjchandra{ 520225394Sjchandra 521225394Sjchandra BUS_ADD_CHILD(parent, 0, "pcib", 0); 522225394Sjchandra} 523225394Sjchandra 524225394Sjchandra/* 525225394Sjchandra * XLS PCIe can have upto 4 links, and each link has its on IRQ 526225394Sjchandra * Find the link on which the device is on 527225394Sjchandra */ 528225394Sjchandrastatic int 529225394Sjchandraxlp_pcie_link(device_t pcib, device_t dev) 530225394Sjchandra{ 531225394Sjchandra device_t parent, tmp; 532225394Sjchandra 533225394Sjchandra /* find the lane on which the slot is connected to */ 534225394Sjchandra tmp = dev; 535225394Sjchandra while (1) { 536225394Sjchandra parent = device_get_parent(tmp); 537225394Sjchandra if (parent == NULL || parent == pcib) { 538225394Sjchandra device_printf(dev, "Cannot find parent bus\n"); 539225394Sjchandra return (-1); 540225394Sjchandra } 541225394Sjchandra if (strcmp(device_get_nameunit(parent), "pci0") == 0) 542225394Sjchandra break; 543225394Sjchandra tmp = parent; 544225394Sjchandra } 545225394Sjchandra return (pci_get_function(tmp)); 546225394Sjchandra} 547225394Sjchandra 548225394Sjchandrastatic int 549225394Sjchandraxlp_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, int *irqs) 550225394Sjchandra{ 551225394Sjchandra int i, link; 552225394Sjchandra 553225394Sjchandra /* 554225394Sjchandra * Each link has 32 MSIs that can be allocated, but for now 555225394Sjchandra * we only support one device per link. 556225394Sjchandra * msi_alloc() equivalent is needed when we start supporting 557225394Sjchandra * bridges on the PCIe link. 558225394Sjchandra */ 559225394Sjchandra link = xlp_pcie_link(pcib, dev); 560225394Sjchandra if (link == -1) 561225394Sjchandra return (ENXIO); 562225394Sjchandra 563225394Sjchandra /* 564225394Sjchandra * encode the irq so that we know it is a MSI interrupt when we 565225394Sjchandra * setup interrupts 566225394Sjchandra */ 567225394Sjchandra for (i = 0; i < count; i++) 568225394Sjchandra irqs[i] = 64 + link * 32 + i; 569225394Sjchandra 570225394Sjchandra return (0); 571225394Sjchandra} 572225394Sjchandra 573225394Sjchandrastatic int 574225394Sjchandraxlp_release_msi(device_t pcib, device_t dev, int count, int *irqs) 575225394Sjchandra{ 576225394Sjchandra return (0); 577225394Sjchandra} 578225394Sjchandra 579225394Sjchandrastatic int 580225394Sjchandraxlp_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr, 581225394Sjchandra uint32_t *data) 582225394Sjchandra{ 583225394Sjchandra int msi, irt; 584225394Sjchandra 585225394Sjchandra if (irq >= 64) { 586225394Sjchandra msi = irq - 64; 587225394Sjchandra *addr = MIPS_MSI_ADDR(0); 588225394Sjchandra 589225394Sjchandra irt = xlp_pcie_link_irt(msi/32); 590225394Sjchandra if (irt != -1) 591225394Sjchandra *data = MIPS_MSI_DATA(xlp_irt_to_irq(irt)); 592225394Sjchandra return (0); 593225394Sjchandra } else { 594225394Sjchandra device_printf(dev, "%s: map_msi for irq %d - ignored", 595225394Sjchandra device_get_nameunit(pcib), irq); 596225394Sjchandra return (ENXIO); 597225394Sjchandra } 598225394Sjchandra} 599225394Sjchandra 600225394Sjchandrastatic void 601225394Sjchandrabridge_pcie_ack(int irq) 602225394Sjchandra{ 603225394Sjchandra uint32_t node,reg; 604225394Sjchandra uint64_t base; 605225394Sjchandra 606225394Sjchandra node = nlm_nodeid(); 607225394Sjchandra reg = PCIE_MSI_STATUS; 608225394Sjchandra 609227783Sjchandra switch (irq) { 610225394Sjchandra case PIC_PCIE_0_IRQ: 611225394Sjchandra base = nlm_pcicfg_base(XLP_IO_PCIE0_OFFSET(node)); 612225394Sjchandra break; 613225394Sjchandra case PIC_PCIE_1_IRQ: 614225394Sjchandra base = nlm_pcicfg_base(XLP_IO_PCIE1_OFFSET(node)); 615225394Sjchandra break; 616225394Sjchandra case PIC_PCIE_2_IRQ: 617225394Sjchandra base = nlm_pcicfg_base(XLP_IO_PCIE2_OFFSET(node)); 618225394Sjchandra break; 619225394Sjchandra case PIC_PCIE_3_IRQ: 620225394Sjchandra base = nlm_pcicfg_base(XLP_IO_PCIE3_OFFSET(node)); 621225394Sjchandra break; 622225394Sjchandra default: 623225394Sjchandra return; 624225394Sjchandra } 625225394Sjchandra 626225394Sjchandra nlm_write_pci_reg(base, reg, 0xFFFFFFFF); 627225394Sjchandra return; 628225394Sjchandra} 629225394Sjchandra 630225394Sjchandrastatic int 631233563Sjchandramips_platform_pcib_setup_intr(device_t dev, device_t child, 632225394Sjchandra struct resource *irq, int flags, driver_filter_t *filt, 633225394Sjchandra driver_intr_t *intr, void *arg, void **cookiep) 634225394Sjchandra{ 635225394Sjchandra int error = 0; 636225394Sjchandra int xlpirq; 637225394Sjchandra void *extra_ack; 638225394Sjchandra 639225394Sjchandra error = rman_activate_resource(irq); 640225394Sjchandra if (error) 641225394Sjchandra return error; 642225394Sjchandra if (rman_get_start(irq) != rman_get_end(irq)) { 643225394Sjchandra device_printf(dev, "Interrupt allocation %lu != %lu\n", 644225394Sjchandra rman_get_start(irq), rman_get_end(irq)); 645225394Sjchandra return (EINVAL); 646225394Sjchandra } 647225394Sjchandra xlpirq = rman_get_start(irq); 648233563Sjchandra if (xlpirq == 0) 649233563Sjchandra return (0); 650225394Sjchandra 651233563Sjchandra if (strcmp(device_get_name(dev), "pcib") != 0) 652225394Sjchandra return (0); 653225394Sjchandra 654225394Sjchandra /* 655225394Sjchandra * temporary hack for MSI, we support just one device per 656225394Sjchandra * link, and assign the link interrupt to the device interrupt 657225394Sjchandra */ 658225394Sjchandra if (xlpirq >= 64) { 659227783Sjchandra int node, val, link; 660227783Sjchandra uint64_t base; 661227783Sjchandra 662225394Sjchandra xlpirq -= 64; 663225394Sjchandra if (xlpirq % 32 != 0) 664225394Sjchandra return (0); 665225394Sjchandra 666225394Sjchandra node = nlm_nodeid(); 667233536Sjchandra link = xlpirq / 32; 668225394Sjchandra base = nlm_pcicfg_base(XLP_IO_PCIE_OFFSET(node,link)); 669225394Sjchandra 670225394Sjchandra /* MSI Interrupt Vector enable at bridge's configuration */ 671225394Sjchandra nlm_write_pci_reg(base, PCIE_MSI_EN, PCIE_MSI_VECTOR_INT_EN); 672225394Sjchandra 673225394Sjchandra val = nlm_read_pci_reg(base, PCIE_INT_EN0); 674225394Sjchandra /* MSI Interrupt enable at bridge's configuration */ 675225394Sjchandra nlm_write_pci_reg(base, PCIE_INT_EN0, 676233536Sjchandra (val | PCIE_MSI_INT_EN)); 677225394Sjchandra 678225394Sjchandra /* legacy interrupt disable at bridge */ 679225394Sjchandra val = nlm_read_pci_reg(base, PCIE_BRIDGE_CMD); 680225394Sjchandra nlm_write_pci_reg(base, PCIE_BRIDGE_CMD, 681233536Sjchandra (val | PCIM_CMD_INTxDIS)); 682225394Sjchandra 683225394Sjchandra /* MSI address update at bridge */ 684225394Sjchandra nlm_write_pci_reg(base, PCIE_BRIDGE_MSI_ADDRL, 685233536Sjchandra MSI_MIPS_ADDR_BASE); 686233536Sjchandra nlm_write_pci_reg(base, PCIE_BRIDGE_MSI_ADDRH, 0); 687225394Sjchandra 688225394Sjchandra val = nlm_read_pci_reg(base, PCIE_BRIDGE_MSI_CAP); 689225394Sjchandra /* MSI capability enable at bridge */ 690225394Sjchandra nlm_write_pci_reg(base, PCIE_BRIDGE_MSI_CAP, 691233536Sjchandra (val | (PCIM_MSICTRL_MSI_ENABLE << 16) | 692233536Sjchandra (PCIM_MSICTRL_MMC_32 << 16))); 693225394Sjchandra 694225394Sjchandra xlpirq = xlp_pcie_link_irt(xlpirq / 32); 695225394Sjchandra if (xlpirq == -1) 696225394Sjchandra return (EINVAL); 697225394Sjchandra xlpirq = xlp_irt_to_irq(xlpirq); 698225394Sjchandra } 699225394Sjchandra /* Set all irqs to CPU 0 for now */ 700225394Sjchandra nlm_pic_write_irt_direct(xlp_pic_base, xlp_irq_to_irt(xlpirq), 1, 0, 701233536Sjchandra PIC_LOCAL_SCHEDULING, xlpirq, 0); 702225394Sjchandra extra_ack = NULL; 703233536Sjchandra if (xlpirq >= PIC_PCIE_0_IRQ && xlpirq <= PIC_PCIE_3_IRQ) 704225394Sjchandra extra_ack = bridge_pcie_ack; 705225394Sjchandra xlp_establish_intr(device_get_name(child), filt, 706225394Sjchandra intr, arg, xlpirq, flags, cookiep, extra_ack); 707225394Sjchandra 708225394Sjchandra return (0); 709225394Sjchandra} 710225394Sjchandra 711225394Sjchandrastatic int 712233563Sjchandramips_platform_pcib_teardown_intr(device_t dev, device_t child, 713225394Sjchandra struct resource *irq, void *cookie) 714225394Sjchandra{ 715225394Sjchandra if (strcmp(device_get_name(child), "pci") == 0) { 716225394Sjchandra /* if needed reprogram the pic to clear pcix related entry */ 717225394Sjchandra device_printf(dev, "teardown intr\n"); 718225394Sjchandra } 719225394Sjchandra return (bus_generic_teardown_intr(dev, child, irq, cookie)); 720225394Sjchandra} 721225394Sjchandra 722225394Sjchandrastatic struct resource * 723233563Sjchandraxlp_pcib_alloc_resource(device_t bus, device_t child, int type, int *rid, 724225394Sjchandra u_long start, u_long end, u_long count, u_int flags) 725225394Sjchandra{ 726225394Sjchandra struct rman *rm = NULL; 727225394Sjchandra struct resource *rv; 728233563Sjchandra void *va; 729225394Sjchandra int needactivate = flags & RF_ACTIVE; 730225394Sjchandra 731233563Sjchandra switch (type) { 732233563Sjchandra case SYS_RES_IRQ: 733233563Sjchandra rm = &irq_rman; 734233563Sjchandra break; 735225394Sjchandra 736233563Sjchandra case SYS_RES_IOPORT: 737233563Sjchandra rm = &port_rman; 738233563Sjchandra break; 739225394Sjchandra 740233563Sjchandra case SYS_RES_MEMORY: 741233563Sjchandra if (start >= EMUL_MEM_START && start <= EMUL_MEM_END) 742233563Sjchandra rm = &emul_rman; 743233563Sjchandra else 744225394Sjchandra rm = &mem_rman; 745225394Sjchandra break; 746225394Sjchandra 747233563Sjchandra default: 748233563Sjchandra return (0); 749225394Sjchandra } 750225394Sjchandra 751225394Sjchandra rv = rman_reserve_resource(rm, start, end, count, flags, child); 752233563Sjchandra if (rv == NULL) 753233563Sjchandra return (NULL); 754225394Sjchandra 755225394Sjchandra rman_set_rid(rv, *rid); 756225394Sjchandra 757225394Sjchandra if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 758233563Sjchandra va = pmap_mapdev(start, count); 759233563Sjchandra rman_set_bushandle(rv, (bus_space_handle_t)va); 760233563Sjchandra rman_set_bustag(rv, rmi_bus_space); 761225394Sjchandra } 762225394Sjchandra if (needactivate) { 763225394Sjchandra if (bus_activate_resource(child, type, *rid, rv)) { 764225394Sjchandra rman_release_resource(rv); 765225394Sjchandra return (NULL); 766225394Sjchandra } 767225394Sjchandra } 768225394Sjchandra return (rv); 769225394Sjchandra} 770225394Sjchandra 771225394Sjchandrastatic int 772233563Sjchandraxlp_pcib_release_resource(device_t bus, device_t child, int type, int rid, 773225394Sjchandra struct resource *r) 774225394Sjchandra{ 775225394Sjchandra 776225394Sjchandra return (rman_release_resource(r)); 777225394Sjchandra} 778225394Sjchandra 779225394Sjchandrastatic int 780233563Sjchandraxlp_pcib_activate_resource(device_t bus, device_t child, int type, int rid, 781225394Sjchandra struct resource *r) 782225394Sjchandra{ 783225394Sjchandra 784225394Sjchandra return (rman_activate_resource(r)); 785225394Sjchandra} 786225394Sjchandra 787225394Sjchandrastatic int 788233563Sjchandraxlp_pcib_deactivate_resource(device_t bus, device_t child, int type, int rid, 789225394Sjchandra struct resource *r) 790225394Sjchandra{ 791225394Sjchandra 792225394Sjchandra return (rman_deactivate_resource(r)); 793225394Sjchandra} 794225394Sjchandra 795225394Sjchandrastatic int 796233563Sjchandramips_pcib_route_interrupt(device_t bus, device_t dev, int pin) 797225394Sjchandra{ 798225394Sjchandra int irt, link; 799225394Sjchandra 800225394Sjchandra /* 801225394Sjchandra * Validate requested pin number. 802225394Sjchandra */ 803225394Sjchandra if ((pin < 1) || (pin > 4)) 804225394Sjchandra return (255); 805225394Sjchandra 806227783Sjchandra if (pci_get_bus(dev) == 0 && 807227783Sjchandra pci_get_vendor(dev) == PCI_VENDOR_NETLOGIC) { 808227783Sjchandra /* SoC devices */ 809227783Sjchandra uint64_t pcibase; 810227783Sjchandra int f, n, d, num; 811227783Sjchandra 812227783Sjchandra f = pci_get_function(dev); 813227783Sjchandra n = pci_get_slot(dev) / 8; 814227783Sjchandra d = pci_get_slot(dev) % 8; 815227783Sjchandra 816227783Sjchandra /* 817227783Sjchandra * For PCIe links, return link IRT, for other SoC devices 818227783Sjchandra * get the IRT from its PCIe header 819227783Sjchandra */ 820227783Sjchandra if (d == 1) { 821227783Sjchandra irt = xlp_pcie_link_irt(f); 822227783Sjchandra } else { 823227783Sjchandra pcibase = nlm_pcicfg_base(XLP_HDR_OFFSET(n, 0, d, f)); 824227783Sjchandra irt = nlm_irtstart(pcibase); 825227783Sjchandra num = nlm_irtnum(pcibase); 826227783Sjchandra if (num != 1) 827227783Sjchandra device_printf(bus, "[%d:%d:%d] Error %d IRQs\n", 828227783Sjchandra n, d, f, num); 829227783Sjchandra } 830227783Sjchandra } else { 831227783Sjchandra /* Regular PCI devices */ 832227783Sjchandra link = xlp_pcie_link(bus, dev); 833227783Sjchandra irt = xlp_pcie_link_irt(link); 834227783Sjchandra } 835227783Sjchandra 836225394Sjchandra if (irt != -1) 837225394Sjchandra return (xlp_irt_to_irq(irt)); 838225394Sjchandra 839225394Sjchandra return (255); 840225394Sjchandra} 841225394Sjchandra 842225394Sjchandrastatic device_method_t xlp_pcib_methods[] = { 843225394Sjchandra /* Device interface */ 844225394Sjchandra DEVMETHOD(device_identify, xlp_pcib_identify), 845225394Sjchandra DEVMETHOD(device_probe, xlp_pcib_probe), 846225394Sjchandra DEVMETHOD(device_attach, xlp_pcib_attach), 847225394Sjchandra 848225394Sjchandra /* Bus interface */ 849225394Sjchandra DEVMETHOD(bus_read_ivar, xlp_pcib_read_ivar), 850225394Sjchandra DEVMETHOD(bus_write_ivar, xlp_pcib_write_ivar), 851233563Sjchandra DEVMETHOD(bus_alloc_resource, xlp_pcib_alloc_resource), 852233563Sjchandra DEVMETHOD(bus_release_resource, xlp_pcib_release_resource), 853233563Sjchandra DEVMETHOD(bus_activate_resource, xlp_pcib_activate_resource), 854233563Sjchandra DEVMETHOD(bus_deactivate_resource, xlp_pcib_deactivate_resource), 855233563Sjchandra DEVMETHOD(bus_setup_intr, mips_platform_pcib_setup_intr), 856233563Sjchandra DEVMETHOD(bus_teardown_intr, mips_platform_pcib_teardown_intr), 857225394Sjchandra 858225394Sjchandra /* pcib interface */ 859225394Sjchandra DEVMETHOD(pcib_maxslots, xlp_pcib_maxslots), 860225394Sjchandra DEVMETHOD(pcib_read_config, xlp_pcib_read_config), 861225394Sjchandra DEVMETHOD(pcib_write_config, xlp_pcib_write_config), 862233563Sjchandra DEVMETHOD(pcib_route_interrupt, mips_pcib_route_interrupt), 863225394Sjchandra 864225394Sjchandra DEVMETHOD(pcib_alloc_msi, xlp_alloc_msi), 865225394Sjchandra DEVMETHOD(pcib_release_msi, xlp_release_msi), 866225394Sjchandra DEVMETHOD(pcib_map_msi, xlp_map_msi), 867225394Sjchandra 868227843Smarius DEVMETHOD_END 869225394Sjchandra}; 870225394Sjchandra 871225394Sjchandrastatic driver_t xlp_pcib_driver = { 872225394Sjchandra "pcib", 873225394Sjchandra xlp_pcib_methods, 874233563Sjchandra 1, /* no softc */ 875225394Sjchandra}; 876225394Sjchandra 877225394SjchandraDRIVER_MODULE(pcib, nexus, xlp_pcib_driver, pcib_devclass, 0, 0); 878