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