1/* $NetBSD: plumicu.c,v 1.10 2005/12/24 23:24:00 perry 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.10 2005/12/24 23:24:00 perry 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 53int plumicu_match(struct device *, struct cfdata *, void *); 54void plumicu_attach(struct device *, struct device *, void *); 55int plumicu_intr(void *); 56 57static inline void plum_di(plum_chipset_tag_t); 58static inline void plum_ei(plum_chipset_tag_t); 59 60const 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 136struct 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 144struct plumicu_softc { 145 struct device sc_dev; 146 plum_chipset_tag_t sc_pc; 147 bus_space_tag_t sc_regt; 148 bus_space_handle_t sc_regh; 149 void *sc_ih; 150 int sc_enable_count; 151 struct plum_intr_entry sc_intr[PLUM_INTR_MAX]; 152}; 153 154CFATTACH_DECL(plumicu, sizeof(struct plumicu_softc), 155 plumicu_match, plumicu_attach, NULL, NULL); 156 157#ifdef PLUMICUDEBUG 158void plumicu_dump(struct plumicu_softc *); 159#endif 160 161int 162plumicu_match(struct device *parent, struct cfdata *cf, void *aux) 163{ 164 165 return (2); /* 1st attach group */ 166} 167 168void 169plumicu_attach(struct device *parent, struct device *self, void *aux) 170{ 171 struct plum_attach_args *pa = aux; 172 struct plumicu_softc *sc = (void*)self; 173 const struct plum_intr_ctrl *pic; 174 bus_space_tag_t regt; 175 bus_space_handle_t regh; 176 plumreg_t reg; 177 int i; 178 179 sc->sc_pc = pa->pa_pc; 180 sc->sc_regt = pa->pa_regt; 181 182 /* map plum2 interrupt controller register space */ 183 if (bus_space_map(sc->sc_regt, PLUM_INT_REGBASE, 184 PLUM_INT_REGSIZE, 0, &sc->sc_regh)) { 185 printf(":interrupt register map failed\n"); 186 return; 187 } 188#ifdef PLUMICUDEBUG 189 plumicu_dump(sc); 190#endif 191 /* disable all interrupt */ 192 regt = sc->sc_regt; 193 regh = sc->sc_regh; 194 for (i = 0; i < PLUM_INTR_MAX; i++) { 195 pic = &pi_ctrl[i]; 196 if (pic->ic_ienreg) { 197 reg = plum_conf_read(regt, regh, pic->ic_ienreg); 198 reg &= ~pic->ic_ienpat; 199 plum_conf_write(regt, regh, pic->ic_ienreg, reg); 200 } 201 if (pic->ic_senreg) { 202 reg = plum_conf_read(regt, regh, pic->ic_senreg); 203 reg &= ~pic->ic_senpat; 204 plum_conf_write(regt, regh, pic->ic_senreg, reg); 205 } 206 } 207 208 /* register handle to plum_chipset_tag */ 209 plum_conf_register_intr(sc->sc_pc, (void*)sc); 210 211 /* disable interrupt redirect to TX39 core */ 212 plum_di(sc->sc_pc); 213 214 if (!(sc->sc_ih = tx_intr_establish(sc->sc_pc->pc_tc, pa->pa_irq, 215 IST_EDGE, IPL_BIO, 216 plumicu_intr, sc))) { 217 printf(": can't establish interrupt\n"); 218 } 219 printf("\n"); 220} 221 222inline void 223plum_di(plum_chipset_tag_t pc) 224{ 225 struct plumicu_softc *sc = pc->pc_intrt; 226 227 plum_conf_write(sc->sc_regt, sc->sc_regh, PLUM_INT_INTIEN_REG, 0); 228} 229 230inline void 231plum_ei(plum_chipset_tag_t pc) 232{ 233 struct plumicu_softc *sc = pc->pc_intrt; 234 235 plum_conf_write(sc->sc_regt, sc->sc_regh, PLUM_INT_INTIEN_REG, 236 PLUM_INT_INTIEN); 237} 238 239void* 240plum_intr_establish(plum_chipset_tag_t pc, int line, int mode, int level, 241 int (*ih_fun)(void *), void *ih_arg) 242{ 243 struct plumicu_softc *sc = pc->pc_intrt; 244 bus_space_tag_t regt = sc->sc_regt; 245 bus_space_handle_t regh = sc->sc_regh; 246 plumreg_t reg; 247 struct plum_intr_entry *pi; 248 249 if (!LEGAL_PRUM_INTR(line)) { 250 panic("plum_intr_establish: bogus interrupt line"); 251 } 252 253 pi = &sc->sc_intr[line]; 254 pi->pi_line = line; 255 pi->pi_fun = ih_fun; 256 pi->pi_arg = ih_arg; 257 pi->pi_ctrl = &pi_ctrl[line]; 258 259 /* Enable interrupt */ 260 261 /* status enable */ 262 if (pi->pi_ctrl->ic_senreg) { 263 reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_senreg); 264 reg |= pi->pi_ctrl->ic_senpat; 265 plum_conf_write(regt, regh, pi->pi_ctrl->ic_senreg, reg); 266 } 267 /* interrupt enable */ 268 if (pi->pi_ctrl->ic_ienreg) { 269 reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_ienreg); 270 reg |= pi->pi_ctrl->ic_ienpat; 271 plum_conf_write(regt, regh, pi->pi_ctrl->ic_ienreg, reg); 272 } 273 274 /* Enable redirect to TX39 core */ 275 DPRINTF(("plum_intr_establish: %d (count=%d)\n", line, 276 sc->sc_enable_count)); 277 278 if (sc->sc_enable_count++ == 0) 279 plum_ei(pc); 280 281 pi->pi_enabled = 1; 282 283 return (ih_fun); 284} 285 286void 287plum_intr_disestablish(plum_chipset_tag_t pc, void *arg) 288{ 289 struct plumicu_softc *sc = pc->pc_intrt; 290 bus_space_tag_t regt = sc->sc_regt; 291 bus_space_handle_t regh = sc->sc_regh; 292 plumreg_t reg; 293 struct plum_intr_entry *pi; 294 int i; 295 296 sc = pc->pc_intrt; 297 298 for (i = 0; i < PLUM_INTR_MAX; i++) { 299 pi = &sc->sc_intr[i]; 300 if (pi->pi_fun != arg) 301 continue; 302 DPRINTF(("plum_intr_disestablish: %d (count=%d)\n", 303 pi->pi_line, sc->sc_enable_count - 1)); 304 goto found; 305 } 306 panic("plum_intr_disestablish: can't find entry."); 307 /* NOTREACHED */ 308 found: 309 pi->pi_enabled = 0; 310 /* Disable interrupt */ 311 if (pi->pi_ctrl->ic_ienreg) { 312 reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_ienreg); 313 reg &= ~(pi->pi_ctrl->ic_ienpat); 314 plum_conf_write(regt, regh, pi->pi_ctrl->ic_ienreg, reg); 315 } 316 if (pi->pi_ctrl->ic_senreg) { 317 reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_senreg); 318 reg &= ~(pi->pi_ctrl->ic_senpat); 319 plum_conf_write(regt, regh, pi->pi_ctrl->ic_senreg, reg); 320 } 321 322 /* Disable/Enable interrupt redirect to TX39 core */ 323 if (--sc->sc_enable_count == 0) 324 plum_di(pc); 325} 326 327int 328plumicu_intr(void *arg) 329{ 330 struct plumicu_softc *sc = arg; 331 bus_space_tag_t regt = sc->sc_regt; 332 bus_space_handle_t regh = sc->sc_regh; 333 plumreg_t reg1, reg2, reg_ext, reg_pccard; 334 int i; 335 336 plum_di(sc->sc_pc); 337 /* read level 1 status */ 338 reg1 = plum_conf_read(regt, regh, PLUM_INT_INTSTA_REG); 339 340 /* read level 2 status and acknowledge */ 341 reg_ext = plum_conf_read(regt, regh, PLUM_INT_EXTINTS_REG); 342 plum_conf_write(regt, regh, PLUM_INT_EXTINTS_REG, reg_ext); 343 344 reg_pccard = plum_conf_read(regt, regh, PLUM_INT_PCCINTS_REG); 345 plum_conf_write(regt, regh, PLUM_INT_PCCINTS_REG, reg_pccard); 346 347 for (i = 0; i < PLUM_INTR_MAX; i++) { 348 register struct plum_intr_entry *pi; 349 register const struct plum_intr_ctrl *pic = &pi_ctrl[i]; 350 351 if (!(pic->ic_ackpat1 & reg1)) 352 continue; 353 354 pi = &sc->sc_intr[i]; 355 if (!pi->pi_enabled) 356 continue; 357 358 if (pic->ic_ackreg2 == 0) { 359 (*pi->pi_fun)(pi->pi_arg); 360 continue; 361 } 362 363 reg2 = pic->ic_ackreg2 == PLUM_INT_PCCINTS_REG 364 ? reg_pccard : reg_ext; 365 366 if (pic->ic_ackpat2 & reg2) 367 (*pi->pi_fun)(pi->pi_arg); 368 } 369 plum_ei(sc->sc_pc); 370 371 return (0); 372} 373 374#ifdef PLUMICUDEBUG 375void 376plumicu_dump(struct plumicu_softc *sc) 377{ 378 bus_space_tag_t regt = sc->sc_regt; 379 bus_space_handle_t regh = sc->sc_regh; 380 plumreg_t reg; 381 382 printf("status:"); 383 reg = plum_conf_read(regt, regh, PLUM_INT_INTSTA_REG); 384 dbg_bit_print(reg); 385 printf("ExtIO\n"); 386 printf("status:"); 387 reg = plum_conf_read(regt, regh, PLUM_INT_EXTINTS_REG); 388 dbg_bit_print(reg); 389 printf("enable:"); 390 reg = plum_conf_read(regt, regh, PLUM_INT_EXTIEN_REG); 391 dbg_bit_print(reg); 392 393} 394#endif /* PLUMICUDEBUG */ 395