1 /* $NetBSD: plumicu.c,v 1.12 2012/10/27 17:17:52 chs Exp $ */ 2 3 /*- 4 * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by UCHIYAMA Yasushi. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: plumicu.c,v 1.12 2012/10/27 17:17:52 chs Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/device.h> 38 39 #include <machine/bus.h> 40 #include <machine/intr.h> 41 42 #include <hpcmips/tx/tx39var.h> 43 #include <hpcmips/dev/plumvar.h> 44 #include <hpcmips/dev/plumicuvar.h> 45 #include <hpcmips/dev/plumicureg.h> 46 47 #ifdef PLUMICUDEBUG 48 #define DPRINTF(arg) printf arg 49 #else 50 #define DPRINTF(arg) 51 #endif 52 53 int plumicu_match(device_t, cfdata_t, void *); 54 void plumicu_attach(device_t, device_t, void *); 55 int plumicu_intr(void *); 56 57 static inline void plum_di(plum_chipset_tag_t); 58 static inline void plum_ei(plum_chipset_tag_t); 59 60 const struct plum_intr_ctrl { 61 plumreg_t ic_ackpat1; 62 plumreg_t ic_ackpat2; int ic_ackreg2; 63 plumreg_t ic_ienpat; int ic_ienreg; 64 plumreg_t ic_senpat; int ic_senreg; 65 } pi_ctrl[PLUM_INTR_MAX] = { 66 [PLUM_INT_C1IO] = {PLUM_INT_INTSTA_PCCINT, 67 PLUM_INT_PCCINTS_C1IO, PLUM_INT_PCCINTS_REG, 68 PLUM_INT_PCCIEN_IENC1IO, PLUM_INT_PCCIEN_REG, 69 PLUM_INT_PCCIEN_SENC1IO, PLUM_INT_PCCIEN_REG 70 }, 71 [PLUM_INT_C1RI] = {PLUM_INT_INTSTA_PCCINT, 72 PLUM_INT_PCCINTS_C1RI, PLUM_INT_PCCINTS_REG, 73 PLUM_INT_PCCIEN_IENC1RI, PLUM_INT_PCCIEN_REG, 74 PLUM_INT_PCCIEN_SENC1RI, PLUM_INT_PCCIEN_REG 75 }, 76 [PLUM_INT_C1SC] = {PLUM_INT_INTSTA_C1SCINT, 0, 0, 0, 0, 0, 0}, 77 [PLUM_INT_C2IO] = {PLUM_INT_INTSTA_PCCINT, 78 PLUM_INT_PCCINTS_C2IO, PLUM_INT_PCCINTS_REG, 79 PLUM_INT_PCCIEN_IENC2IO, PLUM_INT_PCCIEN_REG, 80 PLUM_INT_PCCIEN_SENC2IO, PLUM_INT_PCCIEN_REG 81 }, 82 [PLUM_INT_C2RI] = {PLUM_INT_INTSTA_PCCINT, 83 PLUM_INT_PCCINTS_C2RI, PLUM_INT_PCCINTS_REG, 84 PLUM_INT_PCCIEN_IENC2RI, PLUM_INT_PCCIEN_REG, 85 PLUM_INT_PCCIEN_SENC2RI, PLUM_INT_PCCIEN_REG 86 }, 87 [PLUM_INT_C2SC] = {PLUM_INT_INTSTA_C2SCINT, 0, 0, 0, 0, 0, 0}, 88 [PLUM_INT_DISP] = {PLUM_INT_INTSTA_DISPINT, 0, 0, 0, 0, 0, 0}, 89 [PLUM_INT_USB] = {PLUM_INT_INTSTA_USBINT, 90 0, 0, 91 PLUM_INT_USBINTEN_IEN, PLUM_INT_USBINTEN_REG, 92 0, 0 93 }, 94 [PLUM_INT_USBWAKE] = {PLUM_INT_INTSTA_USBWAKE, 95 0, 0, 96 PLUM_INT_USBINTEN_WIEN, PLUM_INT_USBINTEN_REG, 97 0, 0 98 }, 99 [PLUM_INT_SM] = {PLUM_INT_INTSTA_SMINT, 100 0, 0, 101 PLUM_INT_SMIEN, PLUM_INT_SMIEN_REG, 102 0, 0 103 }, 104 [PLUM_INT_EXT5IO0] = {PLUM_INT_INTSTA_EXTINT, 105 PLUM_INT_EXTINTS_IO5INT0, PLUM_INT_EXTINTS_REG, 106 PLUM_INT_EXTIEN_IENIO5INT0, PLUM_INT_EXTIEN_REG, 107 PLUM_INT_EXTIEN_SENIO5INT0, PLUM_INT_EXTIEN_REG, 108 }, 109 [PLUM_INT_EXT5IO1] = {PLUM_INT_INTSTA_EXTINT, 110 PLUM_INT_EXTINTS_IO5INT1, PLUM_INT_EXTINTS_REG, 111 PLUM_INT_EXTIEN_IENIO5INT1, PLUM_INT_EXTIEN_REG, 112 PLUM_INT_EXTIEN_SENIO5INT1, PLUM_INT_EXTIEN_REG, 113 }, 114 [PLUM_INT_EXT5IO2] = {PLUM_INT_INTSTA_EXTINT, 115 PLUM_INT_EXTINTS_IO5INT2, PLUM_INT_EXTINTS_REG, 116 PLUM_INT_EXTIEN_IENIO5INT2, PLUM_INT_EXTIEN_REG, 117 PLUM_INT_EXTIEN_SENIO5INT2, PLUM_INT_EXTIEN_REG, 118 }, 119 [PLUM_INT_EXT5IO3] = {PLUM_INT_INTSTA_EXTINT, 120 PLUM_INT_EXTINTS_IO5INT3, PLUM_INT_EXTINTS_REG, 121 PLUM_INT_EXTIEN_IENIO5INT0, PLUM_INT_EXTIEN_REG, 122 PLUM_INT_EXTIEN_SENIO5INT0, PLUM_INT_EXTIEN_REG, 123 }, 124 [PLUM_INT_EXT3IO0] = {PLUM_INT_INTSTA_EXTINT, 125 PLUM_INT_EXTINTS_IO3INT0, PLUM_INT_EXTINTS_REG, 126 PLUM_INT_EXTIEN_IENIO3INT0, PLUM_INT_EXTIEN_REG, 127 PLUM_INT_EXTIEN_SENIO3INT0, PLUM_INT_EXTIEN_REG, 128 }, 129 [PLUM_INT_EXT3IO1] = {PLUM_INT_INTSTA_EXTINT, 130 PLUM_INT_EXTINTS_IO3INT1, PLUM_INT_EXTINTS_REG, 131 PLUM_INT_EXTIEN_IENIO3INT1, PLUM_INT_EXTIEN_REG, 132 PLUM_INT_EXTIEN_SENIO3INT1, PLUM_INT_EXTIEN_REG, 133 } 134 }; 135 136 struct plum_intr_entry { 137 int pi_enabled; 138 int pi_line; 139 int (*pi_fun)(void *); 140 void *pi_arg; 141 const struct plum_intr_ctrl *pi_ctrl; 142 }; 143 144 struct plumicu_softc { 145 plum_chipset_tag_t sc_pc; 146 bus_space_tag_t sc_regt; 147 bus_space_handle_t sc_regh; 148 void *sc_ih; 149 int sc_enable_count; 150 struct plum_intr_entry sc_intr[PLUM_INTR_MAX]; 151 }; 152 153 CFATTACH_DECL_NEW(plumicu, sizeof(struct plumicu_softc), 154 plumicu_match, plumicu_attach, NULL, NULL); 155 156 #ifdef PLUMICUDEBUG 157 void plumicu_dump(struct plumicu_softc *); 158 #endif 159 160 int 161 plumicu_match(device_t parent, cfdata_t cf, void *aux) 162 { 163 164 return (2); /* 1st attach group */ 165 } 166 167 void 168 plumicu_attach(device_t parent, device_t self, void *aux) 169 { 170 struct plum_attach_args *pa = aux; 171 struct plumicu_softc *sc = device_private(self); 172 const struct plum_intr_ctrl *pic; 173 bus_space_tag_t regt; 174 bus_space_handle_t regh; 175 plumreg_t reg; 176 int i; 177 178 sc->sc_pc = pa->pa_pc; 179 sc->sc_regt = pa->pa_regt; 180 181 /* map plum2 interrupt controller register space */ 182 if (bus_space_map(sc->sc_regt, PLUM_INT_REGBASE, 183 PLUM_INT_REGSIZE, 0, &sc->sc_regh)) { 184 printf(":interrupt register map failed\n"); 185 return; 186 } 187 #ifdef PLUMICUDEBUG 188 plumicu_dump(sc); 189 #endif 190 /* disable all interrupt */ 191 regt = sc->sc_regt; 192 regh = sc->sc_regh; 193 for (i = 0; i < PLUM_INTR_MAX; i++) { 194 pic = &pi_ctrl[i]; 195 if (pic->ic_ienreg) { 196 reg = plum_conf_read(regt, regh, pic->ic_ienreg); 197 reg &= ~pic->ic_ienpat; 198 plum_conf_write(regt, regh, pic->ic_ienreg, reg); 199 } 200 if (pic->ic_senreg) { 201 reg = plum_conf_read(regt, regh, pic->ic_senreg); 202 reg &= ~pic->ic_senpat; 203 plum_conf_write(regt, regh, pic->ic_senreg, reg); 204 } 205 } 206 207 /* register handle to plum_chipset_tag */ 208 plum_conf_register_intr(sc->sc_pc, (void*)sc); 209 210 /* disable interrupt redirect to TX39 core */ 211 plum_di(sc->sc_pc); 212 213 if (!(sc->sc_ih = tx_intr_establish(sc->sc_pc->pc_tc, pa->pa_irq, 214 IST_EDGE, IPL_BIO, 215 plumicu_intr, sc))) { 216 printf(": can't establish interrupt\n"); 217 } 218 printf("\n"); 219 } 220 221 inline void 222 plum_di(plum_chipset_tag_t pc) 223 { 224 struct plumicu_softc *sc = pc->pc_intrt; 225 226 plum_conf_write(sc->sc_regt, sc->sc_regh, PLUM_INT_INTIEN_REG, 0); 227 } 228 229 inline void 230 plum_ei(plum_chipset_tag_t pc) 231 { 232 struct plumicu_softc *sc = pc->pc_intrt; 233 234 plum_conf_write(sc->sc_regt, sc->sc_regh, PLUM_INT_INTIEN_REG, 235 PLUM_INT_INTIEN); 236 } 237 238 void* 239 plum_intr_establish(plum_chipset_tag_t pc, int line, int mode, int level, 240 int (*ih_fun)(void *), void *ih_arg) 241 { 242 struct plumicu_softc *sc = pc->pc_intrt; 243 bus_space_tag_t regt = sc->sc_regt; 244 bus_space_handle_t regh = sc->sc_regh; 245 plumreg_t reg; 246 struct plum_intr_entry *pi; 247 248 if (!LEGAL_PRUM_INTR(line)) { 249 panic("plum_intr_establish: bogus interrupt line"); 250 } 251 252 pi = &sc->sc_intr[line]; 253 pi->pi_line = line; 254 pi->pi_fun = ih_fun; 255 pi->pi_arg = ih_arg; 256 pi->pi_ctrl = &pi_ctrl[line]; 257 258 /* Enable interrupt */ 259 260 /* status enable */ 261 if (pi->pi_ctrl->ic_senreg) { 262 reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_senreg); 263 reg |= pi->pi_ctrl->ic_senpat; 264 plum_conf_write(regt, regh, pi->pi_ctrl->ic_senreg, reg); 265 } 266 /* interrupt enable */ 267 if (pi->pi_ctrl->ic_ienreg) { 268 reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_ienreg); 269 reg |= pi->pi_ctrl->ic_ienpat; 270 plum_conf_write(regt, regh, pi->pi_ctrl->ic_ienreg, reg); 271 } 272 273 /* Enable redirect to TX39 core */ 274 DPRINTF(("plum_intr_establish: %d (count=%d)\n", line, 275 sc->sc_enable_count)); 276 277 if (sc->sc_enable_count++ == 0) 278 plum_ei(pc); 279 280 pi->pi_enabled = 1; 281 282 return (ih_fun); 283 } 284 285 void 286 plum_intr_disestablish(plum_chipset_tag_t pc, void *arg) 287 { 288 struct plumicu_softc *sc = pc->pc_intrt; 289 bus_space_tag_t regt = sc->sc_regt; 290 bus_space_handle_t regh = sc->sc_regh; 291 plumreg_t reg; 292 struct plum_intr_entry *pi; 293 int i; 294 295 sc = pc->pc_intrt; 296 297 for (i = 0; i < PLUM_INTR_MAX; i++) { 298 pi = &sc->sc_intr[i]; 299 if (pi->pi_fun != arg) 300 continue; 301 DPRINTF(("plum_intr_disestablish: %d (count=%d)\n", 302 pi->pi_line, sc->sc_enable_count - 1)); 303 goto found; 304 } 305 panic("plum_intr_disestablish: can't find entry."); 306 /* NOTREACHED */ 307 found: 308 pi->pi_enabled = 0; 309 /* Disable interrupt */ 310 if (pi->pi_ctrl->ic_ienreg) { 311 reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_ienreg); 312 reg &= ~(pi->pi_ctrl->ic_ienpat); 313 plum_conf_write(regt, regh, pi->pi_ctrl->ic_ienreg, reg); 314 } 315 if (pi->pi_ctrl->ic_senreg) { 316 reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_senreg); 317 reg &= ~(pi->pi_ctrl->ic_senpat); 318 plum_conf_write(regt, regh, pi->pi_ctrl->ic_senreg, reg); 319 } 320 321 /* Disable/Enable interrupt redirect to TX39 core */ 322 if (--sc->sc_enable_count == 0) 323 plum_di(pc); 324 } 325 326 int 327 plumicu_intr(void *arg) 328 { 329 struct plumicu_softc *sc = arg; 330 bus_space_tag_t regt = sc->sc_regt; 331 bus_space_handle_t regh = sc->sc_regh; 332 plumreg_t reg1, reg2, reg_ext, reg_pccard; 333 int i; 334 335 plum_di(sc->sc_pc); 336 /* read level 1 status */ 337 reg1 = plum_conf_read(regt, regh, PLUM_INT_INTSTA_REG); 338 339 /* read level 2 status and acknowledge */ 340 reg_ext = plum_conf_read(regt, regh, PLUM_INT_EXTINTS_REG); 341 plum_conf_write(regt, regh, PLUM_INT_EXTINTS_REG, reg_ext); 342 343 reg_pccard = plum_conf_read(regt, regh, PLUM_INT_PCCINTS_REG); 344 plum_conf_write(regt, regh, PLUM_INT_PCCINTS_REG, reg_pccard); 345 346 for (i = 0; i < PLUM_INTR_MAX; i++) { 347 register struct plum_intr_entry *pi; 348 register const struct plum_intr_ctrl *pic = &pi_ctrl[i]; 349 350 if (!(pic->ic_ackpat1 & reg1)) 351 continue; 352 353 pi = &sc->sc_intr[i]; 354 if (!pi->pi_enabled) 355 continue; 356 357 if (pic->ic_ackreg2 == 0) { 358 (*pi->pi_fun)(pi->pi_arg); 359 continue; 360 } 361 362 reg2 = pic->ic_ackreg2 == PLUM_INT_PCCINTS_REG 363 ? reg_pccard : reg_ext; 364 365 if (pic->ic_ackpat2 & reg2) 366 (*pi->pi_fun)(pi->pi_arg); 367 } 368 plum_ei(sc->sc_pc); 369 370 return (0); 371 } 372 373 #ifdef PLUMICUDEBUG 374 void 375 plumicu_dump(struct plumicu_softc *sc) 376 { 377 bus_space_tag_t regt = sc->sc_regt; 378 bus_space_handle_t regh = sc->sc_regh; 379 plumreg_t reg; 380 381 printf("status:"); 382 reg = plum_conf_read(regt, regh, PLUM_INT_INTSTA_REG); 383 dbg_bit_print(reg); 384 printf("ExtIO\n"); 385 printf("status:"); 386 reg = plum_conf_read(regt, regh, PLUM_INT_EXTINTS_REG); 387 dbg_bit_print(reg); 388 printf("enable:"); 389 reg = plum_conf_read(regt, regh, PLUM_INT_EXTIEN_REG); 390 dbg_bit_print(reg); 391 392 } 393 #endif /* PLUMICUDEBUG */ 394