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