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