1 /* $NetBSD: tx39io.c,v 1.23 2012/10/27 17:17:54 chs Exp $ */ 2 3 /*- 4 * Copyright (c) 1999-2001 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: tx39io.c,v 1.23 2012/10/27 17:17:54 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 41 #include <hpcmips/tx/tx39var.h> 42 #include <hpcmips/tx/tx39icureg.h> 43 #define __TX39IO_PRIVATE 44 #include <hpcmips/tx/tx39iovar.h> 45 #include <hpcmips/tx/tx39ioreg.h> 46 47 #ifdef TX39IO_DEBUG 48 #define DPRINTF_ENABLE 49 #define DPRINTF_DEBUG tx39io_debug 50 #endif 51 #include <machine/debug.h> 52 53 #define ISBITSET(x, s) ((x) & (1 << (s))) 54 55 int tx39io_match(device_t, cfdata_t, void *); 56 void tx39io_attach(device_t, device_t, void *); 57 58 CFATTACH_DECL_NEW(tx39io, sizeof(struct tx39io_softc), 59 tx39io_match, tx39io_attach, NULL, NULL); 60 61 /* IO/MFIO common */ 62 static void port_intr_disestablish(hpcio_chip_t, hpcio_intr_handle_t); 63 static void port_intr_clear(hpcio_chip_t, hpcio_intr_handle_t); 64 /* MFIO */ 65 static void *mfio_intr_establish(hpcio_chip_t, int, int, int (*)(void *), 66 void *); 67 static int mfio_in(hpcio_chip_t, int); 68 static void mfio_out(hpcio_chip_t, int, int); 69 static int mfio_intr_map(int *, int, int); 70 static void mfio_dump(hpcio_chip_t); 71 static void mfio_update(hpcio_chip_t); 72 /* IO */ 73 static void *io_intr_establish(hpcio_chip_t, int, int, int (*)(void *), 74 void *); 75 #ifdef TX391X 76 static int tx391x_io_in(hpcio_chip_t, int); 77 static void tx391x_io_out(hpcio_chip_t, int, int); 78 static void tx391x_io_update(hpcio_chip_t); 79 static int tx391x_io_intr_map(int *, int, int); 80 #endif 81 #ifdef TX392X 82 static int tx392x_io_in(hpcio_chip_t, int); 83 static void tx392x_io_out(hpcio_chip_t, int, int); 84 static void tx392x_io_update(hpcio_chip_t); 85 static int tx392x_io_intr_map(int *, int, int); 86 #endif 87 #if defined TX391X && defined TX392X 88 #define tx39_io_intr_map(t, s, p, m) \ 89 (IS_TX391X(t) \ 90 ? tx391x_io_intr_map(s, p, m) : tx392x_io_intr_map(s, p, m)) 91 #elif defined TX391X 92 #define tx39io_intr_map(t, s, p, m) tx391x_io_intr_map(s, p, m) 93 #elif defined TX392X 94 #define tx39io_intr_map(t, s, p, m) tx392x_io_intr_map(s, p, m) 95 #endif 96 static void io_dump(hpcio_chip_t); 97 98 static void __print_port_status(struct tx39io_port_status *, int); 99 100 int 101 tx39io_match(device_t parent, cfdata_t cf, void *aux) 102 { 103 return (ATTACH_FIRST); /* 1st attach group of txsim */ 104 } 105 106 void 107 tx39io_attach(device_t parent, device_t self, void *aux) 108 { 109 struct txsim_attach_args *ta = aux; 110 struct tx39io_softc *sc = device_private(self); 111 tx_chipset_tag_t tc; 112 struct hpcio_chip *io_hc = &sc->sc_io_ops; 113 struct hpcio_chip *mfio_hc = &sc->sc_mfio_ops; 114 115 sc->sc_dev = self; 116 tc = sc->sc_tc = ta->ta_tc; 117 118 printf("\n"); 119 sc->sc_stat_io_mask = ~(1 << 5); /* exclude Plum2 INT */ 120 sc->sc_stat_mfio_mask = ~(0x3|(0x3 << 23)); 121 122 /* IO */ 123 io_hc->hc_chipid = IO; 124 io_hc->hc_name = "IO"; 125 io_hc->hc_sc = sc; 126 io_hc->hc_intr_establish = io_intr_establish; 127 io_hc->hc_intr_disestablish = port_intr_disestablish; 128 io_hc->hc_intr_clear = port_intr_clear; 129 io_hc->hc_dump = io_dump; 130 if (IS_TX391X(tc)) { 131 #ifdef TX391X 132 io_hc->hc_portread = tx391x_io_in; 133 io_hc->hc_portwrite = tx391x_io_out; 134 io_hc->hc_update = tx391x_io_update; 135 #endif 136 } else if (IS_TX392X(tc)) { 137 #ifdef TX392X 138 io_hc->hc_portread = tx392x_io_in; 139 io_hc->hc_portwrite = tx392x_io_out; 140 io_hc->hc_update = tx392x_io_update; 141 #endif 142 } 143 tx_conf_register_ioman(tc, io_hc); 144 145 /* MFIO */ 146 mfio_hc->hc_chipid = MFIO; 147 mfio_hc->hc_name = "MFIO"; 148 mfio_hc->hc_sc = sc; 149 mfio_hc->hc_portread = mfio_in; 150 mfio_hc->hc_portwrite = mfio_out; 151 mfio_hc->hc_intr_establish = mfio_intr_establish; 152 mfio_hc->hc_intr_disestablish = port_intr_disestablish; 153 mfio_hc->hc_update = mfio_update; 154 mfio_hc->hc_dump = mfio_dump; 155 156 tx_conf_register_ioman(tc, mfio_hc); 157 158 hpcio_update(io_hc); 159 hpcio_update(mfio_hc); 160 161 #ifdef TX39IO_DEBUG 162 hpcio_dump(io_hc); 163 hpcio_dump(mfio_hc); 164 printf("IO i0x%08x o0x%08x MFIO i0x%08x o0x%08x\n", 165 sc->sc_stat_io.in, sc->sc_stat_io.out, 166 sc->sc_stat_mfio.in, sc->sc_stat_mfio.out); 167 #endif /* TX39IO_DEBUG */ 168 } 169 170 /* 171 * TX391X, TX392X common 172 */ 173 static void * 174 io_intr_establish(hpcio_chip_t arg, int port, int mode, int (*func)(void *), 175 void *func_arg) 176 { 177 struct tx39io_softc *sc = arg->hc_sc; 178 int src; 179 180 if (tx39io_intr_map(sc->sc_tc, &src, port, mode) != 0) 181 return (0); 182 183 return (tx_intr_establish(sc->sc_tc, src, IST_EDGE, IPL_CLOCK, func, 184 func_arg)); 185 } 186 187 static void * 188 mfio_intr_establish(hpcio_chip_t arg, int port, int mode, int (*func)(void *), 189 void *func_arg) 190 { 191 struct tx39io_softc *sc = arg->hc_sc; 192 int src; 193 194 if (mfio_intr_map(&src, port, mode) != 0) 195 return (0); 196 197 return (tx_intr_establish(sc->sc_tc, src, IST_EDGE, IPL_CLOCK, func, 198 func_arg)); 199 } 200 201 static void 202 port_intr_disestablish(hpcio_chip_t arg, void *ih) 203 { 204 struct tx39io_softc *sc = arg->hc_sc; 205 tx_intr_disestablish(sc->sc_tc, ih); 206 } 207 208 static void 209 port_intr_clear(hpcio_chip_t arg, void *ih) 210 { 211 } 212 213 static void 214 mfio_out(hpcio_chip_t arg, int port, int onoff) 215 { 216 struct tx39io_softc *sc = arg->hc_sc; 217 tx_chipset_tag_t tc; 218 txreg_t reg, pos; 219 220 DPRINTF("port #%d\n", port); 221 tc = sc->sc_tc; 222 /* MFIO */ 223 pos = 1 << port; 224 #ifdef DIAGNOSTIC 225 if (!(sc->sc_stat_mfio.dir & pos)) { 226 panic("%s: MFIO%d is not output port.", 227 device_xname(sc->sc_dev), port); 228 } 229 #endif 230 reg = tx_conf_read(tc, TX39_IOMFIODATAOUT_REG); 231 if (onoff) 232 reg |= pos; 233 else 234 reg &= ~pos; 235 tx_conf_write(tc, TX39_IOMFIODATAOUT_REG, reg); 236 } 237 238 static int 239 mfio_in(hpcio_chip_t arg, int port) 240 { 241 struct tx39io_softc *sc __attribute__((__unused__)) = arg->hc_sc ; 242 243 DPRINTF("port #%d\n", port); 244 return (tx_conf_read(sc->sc_tc, TX39_IOMFIODATAIN_REG) & (1 << port)); 245 } 246 247 static int 248 mfio_intr_map(int *src, int port, int mode) 249 { 250 251 if (mode & HPCIO_INTR_POSEDGE) { 252 *src = MAKEINTR(3, (1 << port)); 253 return (0); 254 } else if (mode & HPCIO_INTR_NEGEDGE) { 255 *src = MAKEINTR(4, (1 << port)); 256 return (0); 257 } 258 259 DPRINTF("invalid interrupt mode.\n"); 260 261 return (1); 262 } 263 264 static void 265 mfio_update(hpcio_chip_t arg) 266 { 267 struct tx39io_softc *sc = arg->hc_sc; 268 tx_chipset_tag_t tc = sc->sc_tc; 269 struct tx39io_port_status *stat_mfio = &sc->sc_stat_mfio; 270 271 sc->sc_ostat_mfio = *stat_mfio; /* save old status */ 272 stat_mfio->dir = tx_conf_read(tc, TX39_IOMFIODATADIR_REG); 273 stat_mfio->in = tx_conf_read(tc, TX39_IOMFIODATAIN_REG); 274 stat_mfio->out = tx_conf_read(tc, TX39_IOMFIODATAOUT_REG); 275 stat_mfio->power = tx_conf_read(tc, TX39_IOMFIOPOWERDWN_REG); 276 stat_mfio->u.select = tx_conf_read(tc, TX39_IOMFIODATASEL_REG); 277 } 278 279 #ifdef TX391X 280 /* 281 * TMPR3912 specific 282 */ 283 int 284 tx391x_io_in(hpcio_chip_t arg, int port) 285 { 286 struct tx39io_softc *sc __attribute__((__unused__)) = arg->hc_sc; 287 txreg_t reg = tx_conf_read(sc->sc_tc, TX39_IOCTRL_REG); 288 289 DPRINTF("port #%d\n", port); 290 return (TX391X_IOCTRL_IODIN(reg) & (1 << port)); 291 } 292 293 void 294 tx391x_io_out(hpcio_chip_t arg, int port, int onoff) 295 { 296 struct tx39io_softc *sc = arg->hc_sc; 297 tx_chipset_tag_t tc; 298 txreg_t reg, pos, iostat; 299 300 KASSERT(sc); 301 DPRINTF("port #%d\n", port); 302 303 tc = sc->sc_tc; 304 305 /* IO [0:6] */ 306 pos = 1 << port; 307 #ifdef DIAGNOSTIC 308 if (!(sc->sc_stat_io.dir & pos)) 309 panic("%s: IO%d is not output port.", device_xname(sc->sc_dev), 310 port); 311 #endif 312 reg = tx_conf_read(tc, TX39_IOCTRL_REG); 313 iostat = TX391X_IOCTRL_IODOUT(reg); 314 if (onoff) 315 iostat |= pos; 316 else 317 iostat &= ~pos; 318 TX391X_IOCTRL_IODOUT_CLR(reg); 319 reg = TX391X_IOCTRL_IODOUT_SET(reg, iostat); 320 tx_conf_write(tc, TX39_IOCTRL_REG, reg); 321 } 322 323 void 324 tx391x_io_update(hpcio_chip_t arg) 325 { 326 struct tx39io_softc *sc = arg->hc_sc; 327 struct tx39io_port_status *stat_io = &sc->sc_stat_io; 328 tx_chipset_tag_t tc = sc->sc_tc; 329 txreg_t reg; 330 331 /* IO [0:6] */ 332 sc->sc_ostat_io = *stat_io; /* save old status */ 333 reg = tx_conf_read(tc, TX39_IOCTRL_REG); 334 stat_io->dir = TX391X_IOCTRL_IODIREC(reg); 335 stat_io->in = TX391X_IOCTRL_IODIN(reg); 336 stat_io->out = TX391X_IOCTRL_IODOUT(reg); 337 stat_io->u.debounce = TX391X_IOCTRL_IODEBSEL(reg); 338 reg = tx_conf_read(tc, TX39_IOIOPOWERDWN_REG); 339 stat_io->power = TX391X_IOIOPOWERDWN_IOPD(reg); 340 } 341 342 int 343 tx391x_io_intr_map(int *src, int port, int mode) 344 { 345 346 if (mode & HPCIO_INTR_POSEDGE) { 347 *src = MAKEINTR(5, (1 << (port + 7))); 348 return (0); 349 } else if (mode & HPCIO_INTR_NEGEDGE) { 350 *src = MAKEINTR(5, (1 << port)); 351 return (0); 352 } 353 354 DPRINTF("invalid interrupt mode.\n"); 355 356 return (1); 357 } 358 #endif /* TX391X */ 359 360 #ifdef TX392X 361 /* 362 * TMPR3922 specific 363 */ 364 int 365 tx392x_io_in(hpcio_chip_t arg, int port) 366 { 367 struct tx39io_softc *sc __attribute__((__unused__)) = arg->hc_sc; 368 txreg_t reg = tx_conf_read(sc->sc_tc, TX392X_IODATAINOUT_REG); 369 370 DPRINTF("port #%d\n", port); 371 372 return (TX392X_IODATAINOUT_DIN(reg) & (1 << port)); 373 } 374 375 void 376 tx392x_io_out(hpcio_chip_t arg, int port, int onoff) 377 { 378 struct tx39io_softc *sc = arg->hc_sc; 379 #ifdef DIAGNOSTIC 380 const char *devname = device_xname(sc->sc_dev); 381 #endif 382 tx_chipset_tag_t tc = sc->sc_tc; 383 txreg_t reg, pos, iostat; 384 385 DPRINTF("port #%d\n", port); 386 /* IO [0:15] */ 387 pos = 1 << port; 388 #ifdef DIAGNOSTIC 389 if (!(sc->sc_stat_io.dir & pos)) 390 panic("%s: IO%d is not output port.", devname, port); 391 #endif 392 reg = tx_conf_read(tc, TX392X_IODATAINOUT_REG); 393 iostat = TX392X_IODATAINOUT_DOUT(reg); 394 if (onoff) 395 iostat |= pos; 396 else 397 iostat &= ~pos; 398 TX392X_IODATAINOUT_DOUT_CLR(reg); 399 reg = TX392X_IODATAINOUT_DOUT_SET(reg, iostat); 400 tx_conf_write(tc, TX392X_IODATAINOUT_REG, reg); 401 } 402 403 int 404 tx392x_io_intr_map(int *src, int port, int mode) 405 { 406 407 if (mode & HPCIO_INTR_POSEDGE) { 408 *src = MAKEINTR(8, (1 << (port + 16))); 409 return (0); 410 } else if (mode & HPCIO_INTR_NEGEDGE) { 411 *src = MAKEINTR(8, (1 << port)); 412 return (0); 413 } 414 415 DPRINTF("invalid interrupt mode.\n"); 416 417 return (1); 418 } 419 420 void 421 tx392x_io_update(hpcio_chip_t arg) 422 { 423 struct tx39io_softc *sc = arg->hc_sc; 424 struct tx39io_port_status *stat_io = &sc->sc_stat_io; 425 tx_chipset_tag_t tc = sc->sc_tc; 426 txreg_t reg; 427 428 sc->sc_ostat_io = *stat_io; /* save old status */ 429 /* IO [0:15] */ 430 reg = tx_conf_read(tc, TX39_IOCTRL_REG); 431 stat_io->dir = TX392X_IOCTRL_IODIREC(reg); 432 stat_io->u.debounce = TX392X_IOCTRL_IODEBSEL(reg); 433 reg = tx_conf_read(tc, TX392X_IODATAINOUT_REG); 434 stat_io->in = TX392X_IODATAINOUT_DIN(reg); 435 stat_io->out = TX392X_IODATAINOUT_DOUT(reg); 436 reg = tx_conf_read(tc, TX39_IOIOPOWERDWN_REG); 437 stat_io->power = TX392X_IOIOPOWERDWN_IOPD(reg); 438 } 439 440 #endif /* TX392X */ 441 442 static const char *line = "--------------------------------------------------" 443 "------------\n"; 444 static void 445 mfio_dump(hpcio_chip_t arg) 446 { 447 struct tx39io_softc *sc = arg->hc_sc; 448 const struct tx39io_mfio_map *map = tx39io_get_mfio_map(tc); 449 struct tx39io_port_status *stat; 450 int i; 451 452 printf("%s", line); 453 stat = &sc->sc_stat_mfio; 454 for (i = TX39_IO_MFIO_MAX - 1; i >= 0 ; i--) { 455 /* MFIO port has power down state */ 456 printf("MFIO %2d: - ", i); 457 __print_port_status(stat, i); 458 printf(ISBITSET(stat->u.select, i) ? " MFIO(%s)\n" : " %s\n", 459 map[i].std_pin_name); 460 } 461 printf("%s", line); 462 } 463 464 static void 465 io_dump(hpcio_chip_t arg) 466 { 467 struct tx39io_softc *sc = arg->hc_sc; 468 struct tx39io_port_status *stat; 469 int i; 470 471 printf("%s Debounce Direction DataOut DataIn PowerDown Select" 472 "\n%s", line, line); 473 stat = &sc->sc_stat_io; 474 for (i = tx39io_get_io_max(tc) - 1; i >= 0 ; i--) { 475 /* IO port has debouncer */ 476 printf("IO %2d: %s ", i, 477 ISBITSET(stat->u.debounce, i) ? "On " : "Off"); 478 __print_port_status(stat, i); 479 printf(" -\n"); 480 } 481 } 482 483 static void 484 __print_port_status(struct tx39io_port_status *stat, int i) 485 { 486 printf("%s %d %d %s", 487 ISBITSET(stat->dir, i) ? "Out" : "In ", 488 ISBITSET(stat->out, i) ? 1 : 0, 489 ISBITSET(stat->in, i) ? 1 : 0, 490 ISBITSET(stat->power, i) ? "Down ": "Active"); 491 } 492