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