1 /* $NetBSD: pioc.c,v 1.10 2005/06/30 17:03:52 drochner Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Mark Brinicombe. 5 * Copyright (c) 1997 Causality Limited. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Mark Brinicombe. 19 * 4. The name of the company nor the name of the author may be used to 20 * endorse or promote products derived from this software without specific 21 * prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 24 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * Peripheral I/O controller - wd, fd, com, lpt Combo chip 36 * 37 * Parent device for combo chip I/O drivers 38 * Currently supports the SMC FDC37GT66[56] controllers. 39 */ 40 41 /*#define PIOC_DEBUG*/ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: pioc.c,v 1.10 2005/06/30 17:03:52 drochner Exp $"); 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/kernel.h> 49 #include <sys/device.h> 50 51 #include <machine/bus.h> 52 #include <arm/mainbus/mainbus.h> 53 #include <acorn32/mainbus/piocreg.h> 54 #include <acorn32/mainbus/piocvar.h> 55 56 #include "locators.h" 57 58 /* 59 * PIOC device. 60 * 61 * This probes and attaches the top level pioc device. 62 * It then configures any children of the pioc device. 63 */ 64 65 /* 66 * pioc softc structure. 67 * 68 * Contains the device node, bus space tag, handle and address along with 69 * other global information such as id and config registers. 70 */ 71 72 struct pioc_softc { 73 struct device sc_dev; /* device node */ 74 bus_space_tag_t sc_iot; /* bus tag */ 75 bus_space_handle_t sc_ioh; /* bus handle */ 76 bus_addr_t sc_iobase; /* IO base address */ 77 int sc_id; /* chip ID */ 78 int sc_config[PIOC_CM_REGS];/* config regs */ 79 }; 80 81 /* 82 * The pioc device is a parent to the com device. 83 * This means that it needs to provide a bus space tag for 84 * a serial console. 85 * 86 * XXX - This is not fully tested yet. 87 */ 88 89 extern struct bus_space mainbus_bs_tag; 90 bus_space_tag_t comconstag = &mainbus_bs_tag; 91 92 /* Prototypes for functions */ 93 94 static int piocmatch __P((struct device *, struct cfdata *, void *)); 95 static void piocattach __P((struct device *, struct device *, void *)); 96 static int piocprint __P((void *aux, const char *name)); 97 #if 0 98 static int piocsearch __P((struct device *, struct cfdata *, void *)); 99 #endif 100 static int piocsubmatch __P((struct device *, struct cfdata *, 101 const locdesc_t *, void *)); 102 static void piocgetid __P((bus_space_tag_t iot, bus_space_handle_t ioh, 103 int config_entry, int *id, int *revision)); 104 105 /* device attach and driver structure */ 106 107 CFATTACH_DECL(pioc, sizeof(struct pioc_softc), 108 piocmatch, piocattach, NULL, NULL); 109 110 /* 111 * void piocgetid(bus_space_tag_t iot, bus_space_handle_t ioh, 112 * int config_entry, int *id, int *revision) 113 * 114 * Enter config mode and return the id and revision 115 */ 116 117 static void 118 piocgetid(iot, ioh, config_entry, id, revision) 119 bus_space_tag_t iot; 120 bus_space_handle_t ioh; 121 int config_entry; 122 int *id; 123 int *revision; 124 { 125 /* 126 * Put the chip info configuration mode and read the ID and revision 127 */ 128 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, config_entry); 129 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, config_entry); 130 131 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_CRD); 132 *id = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG); 133 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_CRE); 134 *revision = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG); 135 136 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_EXIT); 137 } 138 139 /* 140 * int piocmatch(struct device *parent, struct cfdata *cf, void *aux) 141 * 142 * Put the controller into config mode and probe the ID to see if 143 * we recognise it. 144 * 145 * XXX - INTRUSIVE PROBE 146 */ 147 148 static int 149 piocmatch(parent, cf, aux) 150 struct device *parent; 151 struct cfdata *cf; 152 void *aux; 153 { 154 struct mainbus_attach_args *mb = aux; 155 bus_space_tag_t iot; 156 bus_space_handle_t ioh; 157 int id, rev; 158 int rv = 1; 159 160 /* We need a base address */ 161 if (mb->mb_iobase == MAINBUSCF_BASE_DEFAULT) 162 return(0); 163 164 iot = mb->mb_iot; 165 if (bus_space_map(iot, mb->mb_iobase, PIOC_SIZE, 0, &ioh)) 166 return(0); 167 168 mb->mb_iosize = PIOC_SIZE; 169 170 piocgetid(iot, ioh, PIOC_CM_ENTER_665, &id, &rev); 171 if (id == PIOC_CM_ID_665) 172 goto out; 173 174 piocgetid(iot, ioh, PIOC_CM_ENTER_666, &id, &rev); 175 if (id == PIOC_CM_ID_666) 176 goto out; 177 178 rv = 0; 179 180 out: 181 bus_space_unmap(iot, ioh, PIOC_SIZE); 182 return(rv); 183 } 184 185 /* 186 * int piocprint(void *aux, const char *name) 187 * 188 * print routine used during child configuration 189 */ 190 191 static int 192 piocprint(aux, name) 193 void *aux; 194 const char *name; 195 { 196 struct pioc_attach_args *pa = aux; 197 198 if (!name) { 199 if (pa->pa_offset) 200 aprint_normal(" offset 0x%x", pa->pa_offset >> 2); 201 if (pa->pa_iosize > 1) 202 aprint_normal("-0x%x", 203 ((pa->pa_offset >> 2) + pa->pa_iosize) - 1); 204 if (pa->pa_irq != -1) 205 aprint_normal(" irq %d", pa->pa_irq); 206 if (pa->pa_drq != -1) 207 aprint_normal(" drq 0x%08x", pa->pa_drq); 208 } 209 210 /* XXX print flags */ 211 return (QUIET); 212 } 213 214 #if 0 215 /* 216 * int piocsearch(struct device *parent, struct cfdata *cf, void *aux) 217 * 218 * search function used to probe and attach the child devices. 219 * 220 * Note: since the offsets of the devices need to be specified in the 221 * config file we ignore the FSTAT_STAR. 222 */ 223 224 static int 225 piocsearch(parent, cf, ldesc, aux) 226 struct device *parent; 227 struct cfdata *cf; 228 const locdesc_t *ldesc; 229 void *aux; 230 { 231 struct pioc_softc *sc = (struct pioc_softc *)parent; 232 struct pioc_attach_args pa; 233 int tryagain; 234 235 do { 236 pa.pa_name = NULL; 237 pa.pa_iobase = sc->sc_iobase; 238 pa.pa_iosize = 0; 239 pa.pa_iot = sc->sc_iot; 240 if (cf->cf_loc[PIOCCF_OFFSET] == PIOCCF_OFFSET_DEFAULT) { 241 pa.pa_offset = PIOCCF_OFFSET_DEFAULT; 242 pa.pa_drq = PIOCCF_DACK_DEFAULT; 243 pa.pa_irq = PIOCCF_IRQ_DEFAULT; 244 } else { 245 pa.pa_offset = (cf->cf_loc[PIOCCF_OFFSET] << 2); 246 pa.pa_drq = cf->cf_loc[PIOCCF_DACK]; 247 pa.pa_irq = cf->cf_loc[PIOCCF_IRQ]; 248 } 249 250 tryagain = 0; 251 if (config_match(parent, cf, &pa) > 0) { 252 config_attach(parent, cf, &pa, piocprint); 253 /* tryagain = (cf->cf_fstate == FSTATE_STAR);*/ 254 } 255 } while (tryagain); 256 257 return (0); 258 } 259 #endif 260 261 /* 262 * int piocsubmatch(struct device *parent, struct cfdata *cf, void *aux) 263 * 264 * search function used to probe and attach the child devices. 265 * 266 * Note: since the offsets of the devices need to be specified in the 267 * config file we ignore the FSTAT_STAR. 268 */ 269 270 static int 271 piocsubmatch(parent, cf, ldesc, aux) 272 struct device *parent; 273 struct cfdata *cf; 274 const locdesc_t *ldesc; 275 void *aux; 276 { 277 struct pioc_attach_args *pa = aux; 278 int tryagain; 279 280 if ((pa->pa_offset >> 2) != cf->cf_loc[PIOCCF_OFFSET]) 281 return(0); 282 do { 283 if (pa->pa_drq == -1) 284 pa->pa_drq = cf->cf_loc[PIOCCF_DACK]; 285 if (pa->pa_irq == -1) 286 pa->pa_irq = cf->cf_loc[PIOCCF_IRQ]; 287 tryagain = 0; 288 if (config_match(parent, cf, pa) > 0) { 289 config_attach(parent, cf, pa, piocprint); 290 /* tryagain = (cf->cf_fstate == FSTATE_STAR);*/ 291 } 292 } while (tryagain); 293 294 return (0); 295 } 296 297 /* 298 * void piocattach(struct device *parent, struct device *dev, void *aux) 299 * 300 * Identify the PIOC and read the config registers into the softc. 301 * Search and configure all children 302 */ 303 304 static void 305 piocattach(parent, self, aux) 306 struct device *parent; 307 struct device *self; 308 void *aux; 309 { 310 struct mainbus_attach_args *mb = aux; 311 struct pioc_softc *sc = (struct pioc_softc *)self; 312 bus_space_tag_t iot; 313 bus_space_handle_t ioh; 314 int id, rev; 315 int loop; 316 struct pioc_attach_args pa; 317 318 sc->sc_iobase = mb->mb_iobase; 319 iot = sc->sc_iot = mb->mb_iot; 320 321 if (bus_space_map(iot, sc->sc_iobase, PIOC_SIZE, 0, &ioh)) 322 panic("%s: couldn't map I/O space", self->dv_xname); 323 sc->sc_ioh = ioh; 324 325 piocgetid(iot, ioh, PIOC_CM_ENTER_665, &id, &rev); 326 if (id != PIOC_CM_ID_665) 327 piocgetid(iot, ioh, PIOC_CM_ENTER_666, &id, &rev); 328 329 printf("\n%s: ", self->dv_xname); 330 331 /* Do we recognise it ? */ 332 switch (id) { 333 case PIOC_CM_ID_665: 334 case PIOC_CM_ID_666: 335 printf("SMC FDC37C6%xGT peripheral controller rev %d\n", id, rev); 336 break; 337 default: 338 printf("Unrecognised peripheral controller id=%2x rev=%2x\n", id, rev); 339 return; 340 } 341 342 sc->sc_id = id; 343 344 /* 345 * Put the chip info configuration mode and save all the registers 346 */ 347 348 switch (id) { 349 case PIOC_CM_ID_665: 350 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_665); 351 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_665); 352 break; 353 354 case PIOC_CM_ID_666: 355 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_666); 356 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_666); 357 break; 358 } 359 360 for (loop = 0; loop < PIOC_CM_REGS; ++loop) { 361 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, loop); 362 sc->sc_config[loop] = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG); 363 } 364 365 bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_EXIT); 366 367 #ifdef PIOC_DEBUG 368 printf("%s: ", self->dv_xname); 369 370 for (loop = 0; loop < PIOC_CM_REGS; ++loop) 371 printf("%02x ", sc->sc_config[loop]); 372 printf("\n"); 373 #endif 374 375 /* 376 * Ok as yet we cannot do specific config_found() calls 377 * for the children yet. This is because the pioc device does 378 * not know the interrupt numbers to use. 379 * Eventually this information will have to be provided by the 380 * riscpc specific code. 381 * Until then just do a config_search_ia() and pick the info up 382 * from the cfdata. 383 * Note the child devices require some modifications as well. 384 */ 385 386 /* 387 * Ok Now configure the child devices of the pioc device 388 * Use the pioc config registers to determine the addressing 389 * of the children 390 */ 391 392 /* 393 * Start by configuring the IDE controller 394 */ 395 396 if (sc->sc_config[PIOC_CM_CR0] & PIOC_WDC_ENABLE) { 397 pa.pa_name = "wdc"; 398 pa.pa_iobase = sc->sc_iobase; 399 pa.pa_iosize = 0; 400 pa.pa_iot = iot; 401 if (sc->sc_config[PIOC_CM_CR5] & PIOC_WDC_SECONDARY) 402 pa.pa_offset = (PIOC_WDC_SECONDARY_OFFSET << 2); 403 else 404 pa.pa_offset = (PIOC_WDC_PRIMARY_OFFSET << 2); 405 pa.pa_drq = -1; 406 pa.pa_irq = -1; 407 config_found_sm_loc(self, "pioc", NULL, &pa, piocprint, 408 piocsubmatch); 409 } 410 411 /* 412 * Next configure the floppy controller 413 */ 414 415 if (sc->sc_config[PIOC_CM_CR0] & PIOC_FDC_ENABLE) { 416 pa.pa_name = "fdc"; 417 pa.pa_iobase = sc->sc_iobase; 418 pa.pa_iosize = 0; 419 pa.pa_iot = iot; 420 if (sc->sc_config[PIOC_CM_CR5] & PIOC_FDC_SECONDARY) 421 pa.pa_offset = (PIOC_FDC_SECONDARY_OFFSET << 2); 422 else 423 pa.pa_offset = (PIOC_FDC_PRIMARY_OFFSET << 2); 424 pa.pa_drq = -1; 425 pa.pa_irq = -1; 426 config_found_sm_loc(self, "pioc", NULL, &pa, piocprint, 427 piocsubmatch); 428 } 429 430 /* 431 * Next configure the serial ports 432 */ 433 434 /* 435 * XXX - There is a deficiency in the serial configuration 436 * If the PIOC has the serial ports configured for COM3 and COM4 437 * the standard COM3 and COM4 addresses are assumed rather than 438 * examining CR1 to determine the COM3 and COM4 addresses. 439 */ 440 441 if (sc->sc_config[PIOC_CM_CR2] & PIOC_UART1_ENABLE) { 442 pa.pa_name = "com"; 443 pa.pa_iobase = sc->sc_iobase; 444 pa.pa_iosize = 0; 445 pa.pa_iot = iot; 446 switch (sc->sc_config[PIOC_CM_CR2] & PIOC_UART1_ADDR_MASK) { 447 case PIOC_UART1_ADDR_COM1: 448 pa.pa_offset = (PIOC_COM1_OFFSET << 2); 449 break; 450 case PIOC_UART1_ADDR_COM2: 451 pa.pa_offset = (PIOC_COM2_OFFSET << 2); 452 break; 453 case PIOC_UART1_ADDR_COM3: 454 pa.pa_offset = (PIOC_COM3_OFFSET << 2); 455 break; 456 case PIOC_UART1_ADDR_COM4: 457 pa.pa_offset = (PIOC_COM4_OFFSET << 2); 458 break; 459 } 460 pa.pa_drq = -1; 461 pa.pa_irq = -1; 462 config_found_sm_loc(self, "pioc", NULL, &pa, piocprint, 463 piocsubmatch); 464 } 465 466 if (sc->sc_config[PIOC_CM_CR2] & PIOC_UART2_ENABLE) { 467 pa.pa_name = "com"; 468 pa.pa_iobase = sc->sc_iobase; 469 pa.pa_iosize = 0; 470 pa.pa_iot = iot; 471 switch (sc->sc_config[PIOC_CM_CR2] & PIOC_UART2_ADDR_MASK) { 472 case PIOC_UART2_ADDR_COM1: 473 pa.pa_offset = (PIOC_COM1_OFFSET << 2); 474 break; 475 case PIOC_UART2_ADDR_COM2: 476 pa.pa_offset = (PIOC_COM2_OFFSET << 2); 477 break; 478 case PIOC_UART2_ADDR_COM3: 479 pa.pa_offset = (PIOC_COM3_OFFSET << 2); 480 break; 481 case PIOC_UART2_ADDR_COM4: 482 pa.pa_offset = (PIOC_COM4_OFFSET << 2); 483 break; 484 } 485 pa.pa_drq = -1; 486 pa.pa_irq = -1; 487 config_found_sm_loc(self, "pioc", NULL, &pa, piocprint, 488 piocsubmatch); 489 } 490 491 /* 492 * Next configure the printer port 493 */ 494 495 if ((sc->sc_config[PIOC_CM_CR1] & PIOC_LPT_ADDR_MASK) != PIOC_LPT_ADDR_DISABLE) { 496 pa.pa_name = "lpt"; 497 pa.pa_iobase = sc->sc_iobase; 498 pa.pa_iosize = 0; 499 pa.pa_iot = iot; 500 switch (sc->sc_config[PIOC_CM_CR1] & PIOC_LPT_ADDR_MASK) { 501 case PIOC_LPT_ADDR_1: 502 pa.pa_offset = (PIOC_LPT1_OFFSET << 2); 503 break; 504 case PIOC_LPT_ADDR_2: 505 pa.pa_offset = (PIOC_LPT2_OFFSET << 2); 506 break; 507 case PIOC_LPT_ADDR_3: 508 pa.pa_offset = (PIOC_LPT3_OFFSET << 2); 509 break; 510 } 511 pa.pa_drq = -1; 512 pa.pa_irq = -1; 513 config_found_sm_loc(self, "pioc", NULL, &pa, piocprint, 514 piocsubmatch); 515 } 516 517 #if 0 518 config_search_ia(piocsearch, self, "pioc", NULL); 519 #endif 520 } 521 522 /* End of pioc.c */ 523