1 /* $OpenBSD: hpb.c,v 1.1 2015/06/02 13:53:43 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Martin Pieuchot 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 23 #include <machine/intr.h> 24 25 #include <dev/pci/pcireg.h> 26 #include <dev/pci/pcivar.h> 27 #include <dev/pci/pcidevs.h> 28 #include <dev/pci/ppbreg.h> 29 30 /* 31 * Section 7.6.2 Interrupt Message Definition Register 32 */ 33 #define PCI_HT_LAST_ISMG (0x01 << 16) 34 #define PCI_HT_IMSG_LO(idx) (((2 * (idx)) + 0x10) << 16) 35 #define HT_MASK (1 << 0) 36 #define HT_ACTIVELOW (1 << 1) 37 #define HT_EOI (1 << 5) 38 39 #define PCI_HT_IMSG_HI(idx) (PCI_HT_IMSG_LO(idx) + 1) 40 #define HT_PASSPW (1U << 30) 41 #define HT_WAITEOI (1U << 31) /* Waiting for EOI */ 42 43 44 /* Apple hardware is special... */ 45 #define HT_APPL_EOIOFF(idx) (0x60 + (((idx) >> 3) & ~3)) 46 #define HT_APPL_WEOI(idx) (1 << ((idx) & 0x1f)) 47 48 struct ht_intr_msg { 49 unsigned int him_idx; /* Index */ 50 int him_ist; /* Share type */ 51 pcireg_t him_weoi; /* Cached wait for interrupt data */ 52 }; 53 54 #define hpb_MAX_IMSG 128 55 56 struct hpb_softc { 57 struct device sc_dev; 58 pci_chipset_tag_t sc_pc; 59 pcitag_t sc_tag; 60 pcireg_t sc_id; /* Needed for Apple hardware */ 61 unsigned int sc_off; /* Interrupt cap. offset */ 62 unsigned int sc_nirq; 63 struct ht_intr_msg sc_imap[hpb_MAX_IMSG]; 64 }; 65 66 int hpb_match(struct device *, void *, void *); 67 void hpb_attach(struct device *, struct device *, void *); 68 69 int hpb_print(void *, const char *); 70 71 void hpb_eoi(int); 72 int hpb_enable_irq(int, int); 73 int hpb_disable_irq(int, int); 74 75 const struct cfattach hpb_ca = { 76 sizeof(struct hpb_softc), hpb_match, hpb_attach 77 }; 78 79 struct cfdriver hpb_cd = { 80 NULL, "hpb", DV_DULL, 81 }; 82 83 int 84 hpb_match(struct device *parent, void *match, void *aux) 85 { 86 struct pci_attach_args *pa = aux; 87 pci_chipset_tag_t pc = pa->pa_pc; 88 pcitag_t tag = pa->pa_tag; 89 90 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE || 91 PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_PCI) 92 return (0); 93 94 if (!pci_get_ht_capability(pc, tag, PCI_HT_CAP_INTR, NULL, NULL)) 95 return (0); 96 97 return (10); 98 } 99 100 void 101 hpb_attach(struct device *parent, struct device *self, void *aux) 102 { 103 struct hpb_softc *sc = (struct hpb_softc *)self; 104 struct pci_attach_args *pa = aux; 105 struct pcibus_attach_args pba; 106 pci_chipset_tag_t pc = pa->pa_pc; 107 pcitag_t tag = pa->pa_tag; 108 int idx, irq, off; 109 pcireg_t busdata, reg; 110 111 if (!pci_get_ht_capability(pc, tag, PCI_HT_CAP_INTR, &off, NULL)) 112 panic("A clown ate your HT capability"); 113 114 /* Interrupt definitions are numbered beginning with 0. */ 115 pci_conf_write(pc, tag, off, PCI_HT_LAST_ISMG); 116 reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA); 117 118 sc->sc_pc = pc; 119 sc->sc_tag = tag; 120 sc->sc_id = pa->pa_id; 121 sc->sc_off = off; 122 sc->sc_nirq = ((reg >> 16) & 0xff); 123 124 if (sc->sc_nirq == 0 || sc->sc_nirq > hpb_MAX_IMSG) 125 return; 126 127 printf(": %u sources\n", sc->sc_nirq); 128 129 for (idx = 0; idx < sc->sc_nirq; idx++) { 130 pci_conf_write(pc, tag, off, PCI_HT_IMSG_LO(idx)); 131 reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA); 132 133 pci_conf_write(pc, tag, off + PCI_HT_INTR_DATA, reg | HT_MASK); 134 irq = (reg >> 16) & 0xff; 135 136 #ifdef DIAGNOSTIC 137 if (sc->sc_imap[irq].him_idx != 0) { 138 printf("%s: multiple definition for irq %d\n", 139 sc->sc_dev.dv_xname, irq); 140 continue; 141 } 142 #endif 143 pci_conf_write(pc, tag, off, PCI_HT_IMSG_HI(idx)); 144 reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA); 145 146 sc->sc_imap[irq].him_idx = idx; 147 sc->sc_imap[irq].him_weoi = reg | HT_WAITEOI; 148 } 149 150 busdata = pci_conf_read(pc, pa->pa_tag, PPB_REG_BUSINFO); 151 152 memset(&pba, 0, sizeof(pba)); 153 pba.pba_busname = "pci"; 154 pba.pba_iot = pa->pa_iot; 155 pba.pba_memt = pa->pa_memt; 156 pba.pba_dmat = pa->pa_dmat; 157 pba.pba_pc = pc; 158 pba.pba_domain = pa->pa_domain; 159 pba.pba_bus = PPB_BUSINFO_SECONDARY(busdata); 160 pba.pba_bridgetag = &sc->sc_tag; 161 162 config_found(self, &pba, hpb_print); 163 } 164 165 int 166 hpb_print(void *aux, const char *pnp) 167 { 168 struct pcibus_attach_args *pba = aux; 169 170 if (pnp) 171 printf("%s at %s", pba->pba_busname, pnp); 172 printf(" bus %d", pba->pba_bus); 173 return (UNCONF); 174 } 175 176 void 177 hpb_eoi(int irq) 178 { 179 struct hpb_softc *sc = hpb_cd.cd_devs[0]; 180 pci_chipset_tag_t pc = sc->sc_pc; 181 pcitag_t tag = sc->sc_tag; 182 int idx; 183 184 if (irq >= sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0 || 185 sc->sc_imap[irq].him_ist != IST_LEVEL) 186 return; 187 188 idx = sc->sc_imap[irq].him_idx; 189 190 if (PCI_VENDOR(sc->sc_id) == PCI_VENDOR_APPLE) { 191 pci_conf_write(pc, tag, HT_APPL_EOIOFF(idx), HT_APPL_WEOI(idx)); 192 } else { 193 pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_HI(idx)); 194 pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, 195 sc->sc_imap[irq].him_weoi); 196 } 197 } 198 199 int 200 hpb_enable_irq(int irq, int ist) 201 { 202 struct hpb_softc *sc = hpb_cd.cd_devs[0]; 203 pci_chipset_tag_t pc = sc->sc_pc; 204 pcitag_t tag = sc->sc_tag; 205 pcireg_t reg; 206 int idx; 207 208 if (irq >= sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0) 209 return (0); 210 211 idx = sc->sc_imap[irq].him_idx; 212 sc->sc_imap[irq].him_ist = ist; 213 214 pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_LO(idx)); 215 reg = pci_conf_read(pc, tag, sc->sc_off + PCI_HT_INTR_DATA); 216 217 pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg | HT_MASK); 218 219 reg &= ~(HT_ACTIVELOW | HT_EOI | HT_MASK); 220 if (ist == IST_LEVEL) 221 reg |= HT_ACTIVELOW | HT_EOI; 222 223 pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg); 224 225 return (1); 226 } 227 228 int 229 hpb_disable_irq(int irq, int ist) 230 { 231 struct hpb_softc *sc = hpb_cd.cd_devs[0]; 232 pci_chipset_tag_t pc = sc->sc_pc; 233 pcitag_t tag = sc->sc_tag; 234 pcireg_t reg; 235 int idx; 236 237 if (irq > sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0) 238 return (0); 239 240 idx = sc->sc_imap[irq].him_idx; 241 242 pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_LO(idx)); 243 reg = pci_conf_read(pc, tag, sc->sc_off + PCI_HT_INTR_DATA); 244 245 pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg | HT_MASK); 246 247 return (1); 248 } 249