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