1190681Snwhitehorn/*- 2208149Snwhitehorn * Copyright (C) 2008-2010 Nathan Whitehorn 3190681Snwhitehorn * All rights reserved. 4190681Snwhitehorn * 5190681Snwhitehorn * Redistribution and use in source and binary forms, with or without 6190681Snwhitehorn * modification, are permitted provided that the following conditions 7190681Snwhitehorn * are met: 8190681Snwhitehorn * 1. Redistributions of source code must retain the above copyright 9190681Snwhitehorn * notice, this list of conditions and the following disclaimer. 10190681Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 11190681Snwhitehorn * notice, this list of conditions and the following disclaimer in the 12190681Snwhitehorn * documentation and/or other materials provided with the distribution. 13190681Snwhitehorn * 14190681Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15190681Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16190681Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17190681Snwhitehorn * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18190681Snwhitehorn * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19190681Snwhitehorn * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20190681Snwhitehorn * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21190681Snwhitehorn * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22190681Snwhitehorn * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23190681Snwhitehorn * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24190681Snwhitehorn */ 25190681Snwhitehorn 26229093Shselasky#include <sys/cdefs.h> 27229093Shselasky__FBSDID("$FreeBSD$"); 28229093Shselasky 29190681Snwhitehorn#include <sys/param.h> 30190681Snwhitehorn#include <sys/systm.h> 31190681Snwhitehorn#include <sys/module.h> 32190681Snwhitehorn#include <sys/bus.h> 33190681Snwhitehorn#include <sys/conf.h> 34190681Snwhitehorn#include <sys/kernel.h> 35209310Snwhitehorn#include <sys/pciio.h> 36209310Snwhitehorn#include <sys/rman.h> 37190681Snwhitehorn 38190681Snwhitehorn#include <dev/ofw/openfirm.h> 39190681Snwhitehorn#include <dev/ofw/ofw_pci.h> 40190681Snwhitehorn 41190681Snwhitehorn#include <dev/pci/pcivar.h> 42190681Snwhitehorn#include <dev/pci/pcireg.h> 43190681Snwhitehorn 44190681Snwhitehorn#include <machine/bus.h> 45208149Snwhitehorn#include <machine/intr_machdep.h> 46190681Snwhitehorn#include <machine/md_var.h> 47208149Snwhitehorn#include <machine/openpicvar.h> 48190681Snwhitehorn#include <machine/pio.h> 49190681Snwhitehorn#include <machine/resource.h> 50190681Snwhitehorn 51190681Snwhitehorn#include <dev/ofw/ofw_bus.h> 52190681Snwhitehorn#include <dev/ofw/ofw_bus_subr.h> 53235060Snwhitehorn#include <powerpc/ofw/ofw_pci.h> 54190681Snwhitehorn 55190681Snwhitehorn#include <vm/vm.h> 56190681Snwhitehorn#include <vm/pmap.h> 57190681Snwhitehorn 58190681Snwhitehorn#include "pcib_if.h" 59208149Snwhitehorn#include "pic_if.h" 60190681Snwhitehorn 61190681Snwhitehorn/* 62208149Snwhitehorn * IBM CPC9X5 Hypertransport Device interface. 63190681Snwhitehorn */ 64190681Snwhitehornstatic int cpcht_probe(device_t); 65190681Snwhitehornstatic int cpcht_attach(device_t); 66190681Snwhitehorn 67208149Snwhitehornstatic void cpcht_configure_htbridge(device_t, phandle_t); 68190681Snwhitehorn 69190681Snwhitehorn/* 70190681Snwhitehorn * pcib interface. 71190681Snwhitehorn */ 72208149Snwhitehornstatic u_int32_t cpcht_read_config(device_t, u_int, u_int, u_int, 73190681Snwhitehorn u_int, int); 74208149Snwhitehornstatic void cpcht_write_config(device_t, u_int, u_int, u_int, 75190681Snwhitehorn u_int, u_int32_t, int); 76235060Snwhitehornstatic int cpcht_route_interrupt(device_t, device_t, int); 77209310Snwhitehornstatic int cpcht_alloc_msi(device_t dev, device_t child, 78209310Snwhitehorn int count, int maxcount, int *irqs); 79209310Snwhitehornstatic int cpcht_release_msi(device_t dev, device_t child, 80209310Snwhitehorn int count, int *irqs); 81209310Snwhitehornstatic int cpcht_alloc_msix(device_t dev, device_t child, 82209310Snwhitehorn int *irq); 83209310Snwhitehornstatic int cpcht_release_msix(device_t dev, device_t child, 84209310Snwhitehorn int irq); 85209310Snwhitehornstatic int cpcht_map_msi(device_t dev, device_t child, 86209310Snwhitehorn int irq, uint64_t *addr, uint32_t *data); 87190681Snwhitehorn 88190681Snwhitehorn/* 89190681Snwhitehorn * Driver methods. 90190681Snwhitehorn */ 91208149Snwhitehornstatic device_method_t cpcht_methods[] = { 92190681Snwhitehorn /* Device interface */ 93208149Snwhitehorn DEVMETHOD(device_probe, cpcht_probe), 94208149Snwhitehorn DEVMETHOD(device_attach, cpcht_attach), 95190681Snwhitehorn 96190681Snwhitehorn /* pcib interface */ 97208149Snwhitehorn DEVMETHOD(pcib_read_config, cpcht_read_config), 98208149Snwhitehorn DEVMETHOD(pcib_write_config, cpcht_write_config), 99208149Snwhitehorn DEVMETHOD(pcib_route_interrupt, cpcht_route_interrupt), 100209310Snwhitehorn DEVMETHOD(pcib_alloc_msi, cpcht_alloc_msi), 101209310Snwhitehorn DEVMETHOD(pcib_release_msi, cpcht_release_msi), 102209310Snwhitehorn DEVMETHOD(pcib_alloc_msix, cpcht_alloc_msix), 103209310Snwhitehorn DEVMETHOD(pcib_release_msix, cpcht_release_msix), 104209310Snwhitehorn DEVMETHOD(pcib_map_msi, cpcht_map_msi), 105190681Snwhitehorn 106229093Shselasky DEVMETHOD_END 107190681Snwhitehorn}; 108190681Snwhitehorn 109208149Snwhitehornstruct cpcht_irq { 110209310Snwhitehorn enum { 111209310Snwhitehorn IRQ_NONE, IRQ_HT, IRQ_MSI, IRQ_INTERNAL 112209310Snwhitehorn } irq_type; 113209310Snwhitehorn 114208149Snwhitehorn int ht_source; 115208149Snwhitehorn 116208149Snwhitehorn vm_offset_t ht_base; 117208149Snwhitehorn vm_offset_t apple_eoi; 118208149Snwhitehorn uint32_t eoi_data; 119208149Snwhitehorn int edge; 120208149Snwhitehorn}; 121208149Snwhitehorn 122208149Snwhitehornstatic struct cpcht_irq *cpcht_irqmap = NULL; 123209310Snwhitehornuint32_t cpcht_msipic = 0; 124208149Snwhitehorn 125208149Snwhitehornstruct cpcht_softc { 126235060Snwhitehorn struct ofw_pci_softc pci_sc; 127208149Snwhitehorn vm_offset_t sc_data; 128208149Snwhitehorn uint64_t sc_populated_slots; 129208149Snwhitehorn 130208149Snwhitehorn struct cpcht_irq htirq_map[128]; 131209310Snwhitehorn struct mtx htirq_mtx; 132208149Snwhitehorn}; 133208149Snwhitehorn 134208149Snwhitehornstatic devclass_t cpcht_devclass; 135235060SnwhitehornDEFINE_CLASS_1(pcib, cpcht_driver, cpcht_methods, sizeof(struct cpcht_softc), 136235060Snwhitehorn ofw_pci_driver); 137208149SnwhitehornDRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0); 138190681Snwhitehorn 139214575Snwhitehorn#define CPCHT_IOPORT_BASE 0xf4000000UL /* Hardwired */ 140214575Snwhitehorn#define CPCHT_IOPORT_SIZE 0x00400000UL 141214575Snwhitehorn 142208149Snwhitehorn#define HTAPIC_REQUEST_EOI 0x20 143208149Snwhitehorn#define HTAPIC_TRIGGER_LEVEL 0x02 144208149Snwhitehorn#define HTAPIC_MASK 0x01 145208149Snwhitehorn 146190681Snwhitehornstatic int 147208149Snwhitehorncpcht_probe(device_t dev) 148190681Snwhitehorn{ 149208149Snwhitehorn const char *type, *compatible; 150190681Snwhitehorn 151190681Snwhitehorn type = ofw_bus_get_type(dev); 152208149Snwhitehorn compatible = ofw_bus_get_compat(dev); 153190681Snwhitehorn 154208149Snwhitehorn if (type == NULL || compatible == NULL) 155190681Snwhitehorn return (ENXIO); 156190681Snwhitehorn 157208149Snwhitehorn if (strcmp(type, "ht") != 0) 158190681Snwhitehorn return (ENXIO); 159190681Snwhitehorn 160208149Snwhitehorn if (strcmp(compatible, "u3-ht") != 0) 161208149Snwhitehorn return (ENXIO); 162208149Snwhitehorn 163208149Snwhitehorn device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel"); 164190681Snwhitehorn return (0); 165190681Snwhitehorn} 166190681Snwhitehorn 167190681Snwhitehornstatic int 168208149Snwhitehorncpcht_attach(device_t dev) 169190681Snwhitehorn{ 170208149Snwhitehorn struct cpcht_softc *sc; 171208149Snwhitehorn phandle_t node, child; 172208149Snwhitehorn u_int32_t reg[3]; 173235060Snwhitehorn int i; 174190681Snwhitehorn 175190681Snwhitehorn node = ofw_bus_get_node(dev); 176190681Snwhitehorn sc = device_get_softc(dev); 177190681Snwhitehorn 178208149Snwhitehorn if (OF_getprop(node, "reg", reg, sizeof(reg)) < 12) 179190681Snwhitehorn return (ENXIO); 180190681Snwhitehorn 181235060Snwhitehorn if (OF_getproplen(node, "ranges") <= 0) 182235060Snwhitehorn sc->pci_sc.sc_quirks = OFW_PCI_QUIRK_RANGES_ON_CHILDREN; 183208149Snwhitehorn sc->sc_populated_slots = 0; 184208149Snwhitehorn sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]); 185190681Snwhitehorn 186208149Snwhitehorn /* 187208149Snwhitehorn * Set up the resource manager and the HT->MPIC mapping. For cpcht, 188208149Snwhitehorn * the ranges are properties of the child bridges, and this is also 189208149Snwhitehorn * where we get the HT interrupts properties. 190208149Snwhitehorn */ 191190681Snwhitehorn 192235060Snwhitehorn#if 0 193214575Snwhitehorn /* I/O port mappings are usually not in the device tree */ 194235060Snwhitehorn rman_manage_region(&sc->pci_sc.sc_io_rman, 0, CPCHT_IOPORT_SIZE - 1); 195235060Snwhitehorn#endif 196214575Snwhitehorn 197208149Snwhitehorn bzero(sc->htirq_map, sizeof(sc->htirq_map)); 198209310Snwhitehorn mtx_init(&sc->htirq_mtx, "cpcht irq", NULL, MTX_DEF); 199209310Snwhitehorn for (i = 0; i < 8; i++) 200209310Snwhitehorn sc->htirq_map[i].irq_type = IRQ_INTERNAL; 201208149Snwhitehorn for (child = OF_child(node); child != 0; child = OF_peer(child)) 202208149Snwhitehorn cpcht_configure_htbridge(dev, child); 203208149Snwhitehorn 204208149Snwhitehorn /* Now make the mapping table available to the MPIC */ 205208149Snwhitehorn cpcht_irqmap = sc->htirq_map; 206208149Snwhitehorn 207235060Snwhitehorn return (ofw_pci_attach(dev)); 208208149Snwhitehorn} 209208149Snwhitehorn 210208149Snwhitehornstatic void 211208149Snwhitehorncpcht_configure_htbridge(device_t dev, phandle_t child) 212208149Snwhitehorn{ 213208149Snwhitehorn struct cpcht_softc *sc; 214208149Snwhitehorn struct ofw_pci_register pcir; 215235060Snwhitehorn int ptr, nextptr; 216208149Snwhitehorn uint32_t vend, val; 217208149Snwhitehorn int i, nirq, irq; 218235060Snwhitehorn u_int b, f, s; 219208149Snwhitehorn 220208149Snwhitehorn sc = device_get_softc(dev); 221208149Snwhitehorn if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1) 222208149Snwhitehorn return; 223208149Snwhitehorn 224235060Snwhitehorn b = OFW_PCI_PHYS_HI_BUS(pcir.phys_hi); 225208149Snwhitehorn s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); 226208149Snwhitehorn f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); 227208149Snwhitehorn 228208149Snwhitehorn /* 229208149Snwhitehorn * Mark this slot is populated. The remote south bridge does 230208149Snwhitehorn * not like us talking to unpopulated slots on the root bus. 231208149Snwhitehorn */ 232208149Snwhitehorn sc->sc_populated_slots |= (1 << s); 233208149Snwhitehorn 234208149Snwhitehorn /* 235208149Snwhitehorn * Next build up any HT->MPIC mappings for this sub-bus. One would 236208149Snwhitehorn * naively hope that enabling, disabling, and EOIing interrupts would 237208149Snwhitehorn * cause the appropriate HT bus transactions to that effect. This is 238208149Snwhitehorn * not the case. 239208149Snwhitehorn * 240208149Snwhitehorn * Instead, we have to muck about on the HT peer's root PCI bridges, 241208149Snwhitehorn * figure out what interrupts they send, enable them, and cache 242208149Snwhitehorn * the location of their WaitForEOI registers so that we can 243208149Snwhitehorn * send EOIs later. 244208149Snwhitehorn */ 245190681Snwhitehorn 246208149Snwhitehorn /* All the devices we are interested in have caps */ 247235060Snwhitehorn if (!(PCIB_READ_CONFIG(dev, b, s, f, PCIR_STATUS, 2) 248208149Snwhitehorn & PCIM_STATUS_CAPPRESENT)) 249208149Snwhitehorn return; 250190681Snwhitehorn 251235060Snwhitehorn nextptr = PCIB_READ_CONFIG(dev, b, s, f, PCIR_CAP_PTR, 1); 252208149Snwhitehorn while (nextptr != 0) { 253208149Snwhitehorn ptr = nextptr; 254235060Snwhitehorn nextptr = PCIB_READ_CONFIG(dev, b, s, f, 255208149Snwhitehorn ptr + PCICAP_NEXTPTR, 1); 256190681Snwhitehorn 257208149Snwhitehorn /* Find the HT IRQ capabilities */ 258235060Snwhitehorn if (PCIB_READ_CONFIG(dev, b, s, f, 259208149Snwhitehorn ptr + PCICAP_ID, 1) != PCIY_HT) 260208149Snwhitehorn continue; 261190681Snwhitehorn 262235060Snwhitehorn val = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 2); 263208149Snwhitehorn if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT) 264208149Snwhitehorn continue; 265190681Snwhitehorn 266208149Snwhitehorn /* Ask for the IRQ count */ 267235060Snwhitehorn PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1); 268235060Snwhitehorn nirq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4); 269208149Snwhitehorn nirq = ((nirq >> 16) & 0xff) + 1; 270208149Snwhitehorn 271208149Snwhitehorn device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f); 272208149Snwhitehorn 273208149Snwhitehorn for (i = 0; i < nirq; i++) { 274235060Snwhitehorn PCIB_WRITE_CONFIG(dev, b, s, f, 275208149Snwhitehorn ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1); 276235060Snwhitehorn irq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4); 277208149Snwhitehorn 278208149Snwhitehorn /* 279208149Snwhitehorn * Mask this interrupt for now. 280208149Snwhitehorn */ 281235060Snwhitehorn PCIB_WRITE_CONFIG(dev, b, s, f, ptr + 4, 282208149Snwhitehorn irq | HTAPIC_MASK, 4); 283208149Snwhitehorn irq = (irq >> 16) & 0xff; 284208149Snwhitehorn 285209310Snwhitehorn sc->htirq_map[irq].irq_type = IRQ_HT; 286208149Snwhitehorn sc->htirq_map[irq].ht_source = i; 287208149Snwhitehorn sc->htirq_map[irq].ht_base = sc->sc_data + 288208149Snwhitehorn (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr)); 289208149Snwhitehorn 290235060Snwhitehorn PCIB_WRITE_CONFIG(dev, b, s, f, 291208149Snwhitehorn ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1); 292208149Snwhitehorn sc->htirq_map[irq].eoi_data = 293235060Snwhitehorn PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4) | 294208149Snwhitehorn 0x80000000; 295208149Snwhitehorn 296208149Snwhitehorn /* 297208149Snwhitehorn * Apple uses a non-compliant IO/APIC that differs 298208149Snwhitehorn * in how we signal EOIs. Check if this device was 299208149Snwhitehorn * made by Apple, and act accordingly. 300208149Snwhitehorn */ 301235060Snwhitehorn vend = PCIB_READ_CONFIG(dev, b, s, f, 302208149Snwhitehorn PCIR_DEVVENDOR, 4); 303208149Snwhitehorn if ((vend & 0xffff) == 0x106b) 304208149Snwhitehorn sc->htirq_map[irq].apple_eoi = 305208149Snwhitehorn (sc->htirq_map[irq].ht_base - ptr) + 0x60; 306208149Snwhitehorn } 307208149Snwhitehorn } 308190681Snwhitehorn} 309190681Snwhitehorn 310190681Snwhitehornstatic u_int32_t 311208149Snwhitehorncpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, 312190681Snwhitehorn int width) 313190681Snwhitehorn{ 314208149Snwhitehorn struct cpcht_softc *sc; 315190681Snwhitehorn vm_offset_t caoff; 316190681Snwhitehorn 317190681Snwhitehorn sc = device_get_softc(dev); 318190681Snwhitehorn caoff = sc->sc_data + 319190681Snwhitehorn (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); 320190681Snwhitehorn 321208149Snwhitehorn if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) 322208149Snwhitehorn return (0xffffffff); 323208149Snwhitehorn 324208149Snwhitehorn if (bus > 0) 325208149Snwhitehorn caoff += 0x01000000UL + (bus << 16); 326208149Snwhitehorn 327190681Snwhitehorn switch (width) { 328190681Snwhitehorn case 1: 329190681Snwhitehorn return (in8rb(caoff)); 330190681Snwhitehorn break; 331190681Snwhitehorn case 2: 332190681Snwhitehorn return (in16rb(caoff)); 333190681Snwhitehorn break; 334190681Snwhitehorn case 4: 335190681Snwhitehorn return (in32rb(caoff)); 336190681Snwhitehorn break; 337190681Snwhitehorn } 338190681Snwhitehorn 339190681Snwhitehorn return (0xffffffff); 340190681Snwhitehorn} 341190681Snwhitehorn 342190681Snwhitehornstatic void 343208149Snwhitehorncpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func, 344190681Snwhitehorn u_int reg, u_int32_t val, int width) 345190681Snwhitehorn{ 346208149Snwhitehorn struct cpcht_softc *sc; 347190681Snwhitehorn vm_offset_t caoff; 348190681Snwhitehorn 349190681Snwhitehorn sc = device_get_softc(dev); 350190681Snwhitehorn caoff = sc->sc_data + 351190681Snwhitehorn (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); 352190681Snwhitehorn 353208149Snwhitehorn if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) 354208149Snwhitehorn return; 355208149Snwhitehorn 356208149Snwhitehorn if (bus > 0) 357208149Snwhitehorn caoff += 0x01000000UL + (bus << 16); 358208149Snwhitehorn 359190681Snwhitehorn switch (width) { 360190681Snwhitehorn case 1: 361190681Snwhitehorn out8rb(caoff, val); 362190681Snwhitehorn break; 363190681Snwhitehorn case 2: 364190681Snwhitehorn out16rb(caoff, val); 365190681Snwhitehorn break; 366190681Snwhitehorn case 4: 367190681Snwhitehorn out32rb(caoff, val); 368190681Snwhitehorn break; 369190681Snwhitehorn } 370190681Snwhitehorn} 371190681Snwhitehorn 372190681Snwhitehornstatic int 373208149Snwhitehorncpcht_route_interrupt(device_t bus, device_t dev, int pin) 374208149Snwhitehorn{ 375208149Snwhitehorn return (pin); 376208149Snwhitehorn} 377208149Snwhitehorn 378190681Snwhitehornstatic int 379209310Snwhitehorncpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount, 380209310Snwhitehorn int *irqs) 381209310Snwhitehorn{ 382209310Snwhitehorn struct cpcht_softc *sc; 383209310Snwhitehorn int i, j; 384209310Snwhitehorn 385209310Snwhitehorn sc = device_get_softc(dev); 386209310Snwhitehorn j = 0; 387209310Snwhitehorn 388209310Snwhitehorn /* Bail if no MSI PIC yet */ 389209310Snwhitehorn if (cpcht_msipic == 0) 390209310Snwhitehorn return (ENXIO); 391209310Snwhitehorn 392209310Snwhitehorn mtx_lock(&sc->htirq_mtx); 393209310Snwhitehorn for (i = 8; i < 124 - count; i++) { 394209310Snwhitehorn for (j = 0; j < count; j++) { 395209310Snwhitehorn if (sc->htirq_map[i+j].irq_type != IRQ_NONE) 396209310Snwhitehorn break; 397209310Snwhitehorn } 398209310Snwhitehorn if (j == count) 399209310Snwhitehorn break; 400209310Snwhitehorn 401209310Snwhitehorn i += j; /* We know there isn't a large enough run */ 402209310Snwhitehorn } 403209310Snwhitehorn 404209310Snwhitehorn if (j != count) { 405209310Snwhitehorn mtx_unlock(&sc->htirq_mtx); 406209310Snwhitehorn return (ENXIO); 407209310Snwhitehorn } 408209310Snwhitehorn 409209310Snwhitehorn for (j = 0; j < count; j++) { 410218184Smarcel irqs[j] = MAP_IRQ(cpcht_msipic, i+j); 411209310Snwhitehorn sc->htirq_map[i+j].irq_type = IRQ_MSI; 412209310Snwhitehorn } 413209310Snwhitehorn mtx_unlock(&sc->htirq_mtx); 414209310Snwhitehorn 415209310Snwhitehorn return (0); 416209310Snwhitehorn} 417209310Snwhitehorn 418209310Snwhitehornstatic int 419209310Snwhitehorncpcht_release_msi(device_t dev, device_t child, int count, int *irqs) 420209310Snwhitehorn{ 421209310Snwhitehorn struct cpcht_softc *sc; 422209310Snwhitehorn int i; 423209310Snwhitehorn 424209310Snwhitehorn sc = device_get_softc(dev); 425209310Snwhitehorn 426209310Snwhitehorn mtx_lock(&sc->htirq_mtx); 427209310Snwhitehorn for (i = 0; i < count; i++) 428209310Snwhitehorn sc->htirq_map[irqs[i] & 0xff].irq_type = IRQ_NONE; 429209310Snwhitehorn mtx_unlock(&sc->htirq_mtx); 430209310Snwhitehorn 431209310Snwhitehorn return (0); 432209310Snwhitehorn} 433209310Snwhitehorn 434209310Snwhitehornstatic int 435209310Snwhitehorncpcht_alloc_msix(device_t dev, device_t child, int *irq) 436209310Snwhitehorn{ 437209310Snwhitehorn struct cpcht_softc *sc; 438209310Snwhitehorn int i; 439209310Snwhitehorn 440209310Snwhitehorn sc = device_get_softc(dev); 441209310Snwhitehorn 442209310Snwhitehorn /* Bail if no MSI PIC yet */ 443209310Snwhitehorn if (cpcht_msipic == 0) 444209310Snwhitehorn return (ENXIO); 445209310Snwhitehorn 446209310Snwhitehorn mtx_lock(&sc->htirq_mtx); 447209310Snwhitehorn for (i = 8; i < 124; i++) { 448209310Snwhitehorn if (sc->htirq_map[i].irq_type == IRQ_NONE) { 449209310Snwhitehorn sc->htirq_map[i].irq_type = IRQ_MSI; 450218184Smarcel *irq = MAP_IRQ(cpcht_msipic, i); 451209310Snwhitehorn 452209310Snwhitehorn mtx_unlock(&sc->htirq_mtx); 453209310Snwhitehorn return (0); 454209310Snwhitehorn } 455209310Snwhitehorn } 456209310Snwhitehorn mtx_unlock(&sc->htirq_mtx); 457209310Snwhitehorn 458209310Snwhitehorn return (ENXIO); 459209310Snwhitehorn} 460209310Snwhitehorn 461209310Snwhitehornstatic int 462209310Snwhitehorncpcht_release_msix(device_t dev, device_t child, int irq) 463209310Snwhitehorn{ 464209310Snwhitehorn struct cpcht_softc *sc; 465209310Snwhitehorn 466209310Snwhitehorn sc = device_get_softc(dev); 467209310Snwhitehorn 468209310Snwhitehorn mtx_lock(&sc->htirq_mtx); 469209310Snwhitehorn sc->htirq_map[irq & 0xff].irq_type = IRQ_NONE; 470209310Snwhitehorn mtx_unlock(&sc->htirq_mtx); 471209310Snwhitehorn 472209310Snwhitehorn return (0); 473209310Snwhitehorn} 474209310Snwhitehorn 475209310Snwhitehornstatic int 476209310Snwhitehorncpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, 477209310Snwhitehorn uint32_t *data) 478209310Snwhitehorn{ 479209310Snwhitehorn device_t pcib; 480209310Snwhitehorn struct pci_devinfo *dinfo; 481209310Snwhitehorn struct pcicfg_ht *ht = NULL; 482209310Snwhitehorn 483209310Snwhitehorn for (pcib = child; pcib != dev; pcib = 484209310Snwhitehorn device_get_parent(device_get_parent(pcib))) { 485209310Snwhitehorn dinfo = device_get_ivars(pcib); 486209310Snwhitehorn ht = &dinfo->cfg.ht; 487209310Snwhitehorn 488209310Snwhitehorn if (ht == NULL) 489209310Snwhitehorn continue; 490209310Snwhitehorn } 491209310Snwhitehorn 492209310Snwhitehorn if (ht == NULL) 493209310Snwhitehorn return (ENXIO); 494209310Snwhitehorn 495209310Snwhitehorn *addr = ht->ht_msiaddr; 496209310Snwhitehorn *data = irq & 0xff; 497209310Snwhitehorn 498209310Snwhitehorn return (0); 499209310Snwhitehorn} 500209310Snwhitehorn 501208149Snwhitehorn/* 502208149Snwhitehorn * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945) 503208149Snwhitehorn */ 504208149Snwhitehorn 505208149Snwhitehornstatic int openpic_cpcht_probe(device_t); 506208149Snwhitehornstatic int openpic_cpcht_attach(device_t); 507208149Snwhitehornstatic void openpic_cpcht_config(device_t, u_int irq, 508208149Snwhitehorn enum intr_trigger trig, enum intr_polarity pol); 509208149Snwhitehornstatic void openpic_cpcht_enable(device_t, u_int irq, u_int vector); 510208149Snwhitehornstatic void openpic_cpcht_unmask(device_t, u_int irq); 511208149Snwhitehornstatic void openpic_cpcht_eoi(device_t, u_int irq); 512208149Snwhitehorn 513208149Snwhitehornstatic device_method_t openpic_cpcht_methods[] = { 514208149Snwhitehorn /* Device interface */ 515208149Snwhitehorn DEVMETHOD(device_probe, openpic_cpcht_probe), 516208149Snwhitehorn DEVMETHOD(device_attach, openpic_cpcht_attach), 517208149Snwhitehorn 518208149Snwhitehorn /* PIC interface */ 519209486Snwhitehorn DEVMETHOD(pic_bind, openpic_bind), 520208149Snwhitehorn DEVMETHOD(pic_config, openpic_cpcht_config), 521208149Snwhitehorn DEVMETHOD(pic_dispatch, openpic_dispatch), 522208149Snwhitehorn DEVMETHOD(pic_enable, openpic_cpcht_enable), 523208149Snwhitehorn DEVMETHOD(pic_eoi, openpic_cpcht_eoi), 524208149Snwhitehorn DEVMETHOD(pic_ipi, openpic_ipi), 525208149Snwhitehorn DEVMETHOD(pic_mask, openpic_mask), 526208149Snwhitehorn DEVMETHOD(pic_unmask, openpic_cpcht_unmask), 527208149Snwhitehorn 528208149Snwhitehorn { 0, 0 }, 529208149Snwhitehorn}; 530208149Snwhitehorn 531208149Snwhitehornstruct openpic_cpcht_softc { 532208149Snwhitehorn struct openpic_softc sc_openpic; 533208149Snwhitehorn 534208149Snwhitehorn struct mtx sc_ht_mtx; 535208149Snwhitehorn}; 536208149Snwhitehorn 537208149Snwhitehornstatic driver_t openpic_cpcht_driver = { 538208149Snwhitehorn "htpic", 539208149Snwhitehorn openpic_cpcht_methods, 540208285Snwhitehorn sizeof(struct openpic_cpcht_softc), 541208149Snwhitehorn}; 542208149Snwhitehorn 543208149SnwhitehornDRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0); 544208149Snwhitehorn 545208149Snwhitehornstatic int 546208149Snwhitehornopenpic_cpcht_probe(device_t dev) 547208149Snwhitehorn{ 548208149Snwhitehorn const char *type = ofw_bus_get_type(dev); 549208149Snwhitehorn 550208149Snwhitehorn if (strcmp(type, "open-pic") != 0) 551208149Snwhitehorn return (ENXIO); 552208149Snwhitehorn 553208149Snwhitehorn device_set_desc(dev, OPENPIC_DEVSTR); 554208149Snwhitehorn return (0); 555208149Snwhitehorn} 556208149Snwhitehorn 557208149Snwhitehornstatic int 558208149Snwhitehornopenpic_cpcht_attach(device_t dev) 559208149Snwhitehorn{ 560208149Snwhitehorn struct openpic_cpcht_softc *sc; 561218075Smarcel phandle_t node; 562208149Snwhitehorn int err, irq; 563208149Snwhitehorn 564218075Smarcel node = ofw_bus_get_node(dev); 565218075Smarcel err = openpic_common_attach(dev, node); 566208149Snwhitehorn if (err != 0) 567208149Snwhitehorn return (err); 568208149Snwhitehorn 569208149Snwhitehorn /* 570208149Snwhitehorn * The HT APIC stuff is not thread-safe, so we need a mutex to 571208149Snwhitehorn * protect it. 572208149Snwhitehorn */ 573208149Snwhitehorn sc = device_get_softc(dev); 574208149Snwhitehorn mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN); 575208149Snwhitehorn 576208149Snwhitehorn /* 577208149Snwhitehorn * Interrupts 0-3 are internally sourced and are level triggered 578208149Snwhitehorn * active low. Interrupts 4-123 are connected to a pulse generator 579208149Snwhitehorn * and should be programmed as edge triggered low-to-high. 580208149Snwhitehorn * 581208149Snwhitehorn * IBM CPC945 Manual, Section 9.3. 582208149Snwhitehorn */ 583208149Snwhitehorn 584208149Snwhitehorn for (irq = 0; irq < 4; irq++) 585208149Snwhitehorn openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); 586208149Snwhitehorn for (irq = 4; irq < 124; irq++) 587208149Snwhitehorn openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); 588208149Snwhitehorn 589209310Snwhitehorn /* 590209310Snwhitehorn * Use this PIC for MSI only if it is the root PIC. This may not 591209310Snwhitehorn * be necessary, but Linux does it, and I cannot find any U3 machines 592209310Snwhitehorn * with MSI devices to test. 593209310Snwhitehorn */ 594209310Snwhitehorn if (dev == root_pic) 595218075Smarcel cpcht_msipic = node; 596209310Snwhitehorn 597208149Snwhitehorn return (0); 598208149Snwhitehorn} 599208149Snwhitehorn 600208149Snwhitehornstatic void 601208149Snwhitehornopenpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig, 602208149Snwhitehorn enum intr_polarity pol) 603208149Snwhitehorn{ 604208149Snwhitehorn struct openpic_cpcht_softc *sc; 605208149Snwhitehorn uint32_t ht_irq; 606208149Snwhitehorn 607208149Snwhitehorn /* 608208149Snwhitehorn * The interrupt settings for the MPIC are completely determined 609208149Snwhitehorn * by the internal wiring in the northbridge. Real changes to these 610208149Snwhitehorn * settings need to be negotiated with the remote IO-APIC on the HT 611208149Snwhitehorn * link. 612208149Snwhitehorn */ 613208149Snwhitehorn 614208149Snwhitehorn sc = device_get_softc(dev); 615208149Snwhitehorn 616208149Snwhitehorn if (cpcht_irqmap != NULL && irq < 128 && 617208149Snwhitehorn cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { 618208149Snwhitehorn mtx_lock_spin(&sc->sc_ht_mtx); 619208149Snwhitehorn 620208149Snwhitehorn /* Program the data port */ 621208149Snwhitehorn out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 622208149Snwhitehorn 0x10 + (cpcht_irqmap[irq].ht_source << 1)); 623208149Snwhitehorn 624208149Snwhitehorn /* Grab the IRQ config register */ 625208149Snwhitehorn ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); 626208149Snwhitehorn 627208149Snwhitehorn /* Mask the IRQ while we fiddle settings */ 628208149Snwhitehorn out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK); 629208149Snwhitehorn 630208149Snwhitehorn /* Program the interrupt sense */ 631208149Snwhitehorn ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI); 632208149Snwhitehorn if (trig == INTR_TRIGGER_EDGE) { 633208149Snwhitehorn cpcht_irqmap[irq].edge = 1; 634208149Snwhitehorn } else { 635208149Snwhitehorn cpcht_irqmap[irq].edge = 0; 636208149Snwhitehorn ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI; 637208149Snwhitehorn } 638208149Snwhitehorn out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); 639208149Snwhitehorn 640208149Snwhitehorn mtx_unlock_spin(&sc->sc_ht_mtx); 641208149Snwhitehorn } 642208149Snwhitehorn} 643208149Snwhitehorn 644208149Snwhitehornstatic void 645208149Snwhitehornopenpic_cpcht_enable(device_t dev, u_int irq, u_int vec) 646208149Snwhitehorn{ 647208149Snwhitehorn struct openpic_cpcht_softc *sc; 648208149Snwhitehorn uint32_t ht_irq; 649208149Snwhitehorn 650208149Snwhitehorn openpic_enable(dev, irq, vec); 651208149Snwhitehorn 652208149Snwhitehorn sc = device_get_softc(dev); 653208149Snwhitehorn 654208149Snwhitehorn if (cpcht_irqmap != NULL && irq < 128 && 655208149Snwhitehorn cpcht_irqmap[irq].ht_base > 0) { 656208149Snwhitehorn mtx_lock_spin(&sc->sc_ht_mtx); 657208149Snwhitehorn 658208149Snwhitehorn /* Program the data port */ 659208149Snwhitehorn out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 660208149Snwhitehorn 0x10 + (cpcht_irqmap[irq].ht_source << 1)); 661208149Snwhitehorn 662208149Snwhitehorn /* Unmask the interrupt */ 663208149Snwhitehorn ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); 664208149Snwhitehorn ht_irq &= ~HTAPIC_MASK; 665208149Snwhitehorn out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); 666208149Snwhitehorn 667208149Snwhitehorn mtx_unlock_spin(&sc->sc_ht_mtx); 668208149Snwhitehorn } 669208149Snwhitehorn 670208149Snwhitehorn openpic_cpcht_eoi(dev, irq); 671208149Snwhitehorn} 672208149Snwhitehorn 673208149Snwhitehornstatic void 674208149Snwhitehornopenpic_cpcht_unmask(device_t dev, u_int irq) 675208149Snwhitehorn{ 676208149Snwhitehorn struct openpic_cpcht_softc *sc; 677208149Snwhitehorn uint32_t ht_irq; 678208149Snwhitehorn 679208149Snwhitehorn openpic_unmask(dev, irq); 680208149Snwhitehorn 681208149Snwhitehorn sc = device_get_softc(dev); 682208149Snwhitehorn 683208149Snwhitehorn if (cpcht_irqmap != NULL && irq < 128 && 684208149Snwhitehorn cpcht_irqmap[irq].ht_base > 0) { 685208149Snwhitehorn mtx_lock_spin(&sc->sc_ht_mtx); 686208149Snwhitehorn 687208149Snwhitehorn /* Program the data port */ 688208149Snwhitehorn out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 689208149Snwhitehorn 0x10 + (cpcht_irqmap[irq].ht_source << 1)); 690208149Snwhitehorn 691208149Snwhitehorn /* Unmask the interrupt */ 692208149Snwhitehorn ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); 693208149Snwhitehorn ht_irq &= ~HTAPIC_MASK; 694208149Snwhitehorn out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); 695208149Snwhitehorn 696208149Snwhitehorn mtx_unlock_spin(&sc->sc_ht_mtx); 697208149Snwhitehorn } 698208149Snwhitehorn 699208149Snwhitehorn openpic_cpcht_eoi(dev, irq); 700208149Snwhitehorn} 701208149Snwhitehorn 702208149Snwhitehornstatic void 703208149Snwhitehornopenpic_cpcht_eoi(device_t dev, u_int irq) 704208149Snwhitehorn{ 705208149Snwhitehorn struct openpic_cpcht_softc *sc; 706208149Snwhitehorn uint32_t off, mask; 707208149Snwhitehorn 708208149Snwhitehorn if (irq == 255) 709208149Snwhitehorn return; 710208149Snwhitehorn 711208149Snwhitehorn sc = device_get_softc(dev); 712208149Snwhitehorn 713208149Snwhitehorn if (cpcht_irqmap != NULL && irq < 128 && 714208149Snwhitehorn cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { 715208149Snwhitehorn /* If this is an HT IRQ, acknowledge it at the remote APIC */ 716208149Snwhitehorn 717208149Snwhitehorn if (cpcht_irqmap[irq].apple_eoi) { 718208149Snwhitehorn off = (cpcht_irqmap[irq].ht_source >> 3) & ~3; 719208149Snwhitehorn mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f); 720208149Snwhitehorn out32rb(cpcht_irqmap[irq].apple_eoi + off, mask); 721208149Snwhitehorn } else { 722208149Snwhitehorn mtx_lock_spin(&sc->sc_ht_mtx); 723208149Snwhitehorn 724208149Snwhitehorn out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 725208149Snwhitehorn 0x11 + (cpcht_irqmap[irq].ht_source << 1)); 726208149Snwhitehorn out32rb(cpcht_irqmap[irq].ht_base + 4, 727208149Snwhitehorn cpcht_irqmap[irq].eoi_data); 728208149Snwhitehorn 729208149Snwhitehorn mtx_unlock_spin(&sc->sc_ht_mtx); 730208149Snwhitehorn } 731208149Snwhitehorn } 732208149Snwhitehorn 733208149Snwhitehorn openpic_eoi(dev, irq); 734208149Snwhitehorn} 735