1 /* $NetBSD: nvram_pnpbus.c,v 1.14 2008/04/28 20:23:33 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tim Rightnour 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: nvram_pnpbus.c,v 1.14 2008/04/28 20:23:33 martin Exp $"); 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/ioctl.h> 39 #include <sys/conf.h> 40 #include <sys/kthread.h> 41 #include <sys/device.h> 42 #include <sys/malloc.h> 43 #include <sys/simplelock.h> 44 #include <sys/bus.h> 45 #include <sys/intr.h> 46 47 #include <machine/isa_machdep.h> 48 /* clock stuff for motorolla machines */ 49 #include <dev/clock_subr.h> 50 #include <dev/ic/mk48txxreg.h> 51 52 #include <uvm/uvm_extern.h> 53 54 #include <machine/residual.h> 55 #include <machine/nvram.h> 56 57 #include <prep/pnpbus/pnpbusvar.h> 58 59 #include "opt_nvram.h" 60 61 static char *nvramData; 62 static NVRAM_MAP *nvram; 63 static char *nvramGEAp; /* pointer to the GE area */ 64 static char *nvramCAp; /* pointer to the Config area */ 65 static char *nvramOSAp; /* pointer to the OSArea */ 66 struct simplelock nvram_slock; /* lock */ 67 68 int prep_clock_mk48txx; 69 70 extern char bootpath[256]; 71 extern RESIDUAL resdata; 72 73 #define NVRAM_STD_DEV 0 74 75 static int nvram_pnpbus_probe(struct device *, struct cfdata *, void *); 76 static void nvram_pnpbus_attach(struct device *, struct device *, void *); 77 uint8_t prep_nvram_read_val(int); 78 char *prep_nvram_next_var(char *); 79 char *prep_nvram_find_var(const char *); 80 char *prep_nvram_get_var(const char *); 81 int prep_nvram_get_var_len(const char *); 82 int prep_nvram_count_vars(void); 83 void prep_nvram_write_val(int, uint8_t); 84 uint8_t mkclock_pnpbus_nvrd(struct mk48txx_softc *, int); 85 void mkclock_pnpbus_nvwr(struct mk48txx_softc *, int, uint8_t); 86 87 CFATTACH_DECL(nvram_pnpbus, sizeof(struct nvram_pnpbus_softc), 88 nvram_pnpbus_probe, nvram_pnpbus_attach, NULL, NULL); 89 90 dev_type_open(prep_nvramopen); 91 dev_type_ioctl(prep_nvramioctl); 92 dev_type_close(prep_nvramclose); 93 dev_type_read(prep_nvramread); 94 95 const struct cdevsw nvram_cdevsw = { 96 prep_nvramopen, prep_nvramclose, prep_nvramread, nowrite, 97 prep_nvramioctl, nostop, notty, nopoll, nommap, nokqfilter, D_OTHER, 98 }; 99 100 extern struct cfdriver nvram_cd; 101 102 static int 103 nvram_pnpbus_probe(struct device *parent, struct cfdata *match, void *aux) 104 { 105 struct pnpbus_dev_attach_args *pna = aux; 106 int ret = 0; 107 108 if (strcmp(pna->pna_devid, "IBM0008") == 0) 109 ret = 1; 110 111 if (ret) 112 pnpbus_scan(pna, pna->pna_ppc_dev); 113 114 return ret; 115 } 116 117 static void 118 nvram_pnpbus_attach(struct device *parent, struct device *self, void *aux) 119 { 120 struct nvram_pnpbus_softc *sc = (void *)self; 121 struct pnpbus_dev_attach_args *pna = aux; 122 int as_iobase, as_len, data_iobase, data_len, i, nvlen, cur; 123 uint8_t *p; 124 HEADER prep_nvram_header; 125 126 sc->sc_iot = pna->pna_iot; 127 128 pnpbus_getioport(&pna->pna_res, 0, &as_iobase, &as_len); 129 pnpbus_getioport(&pna->pna_res, 1, &data_iobase, &data_len); 130 131 if (pnpbus_io_map(&pna->pna_res, 0, &sc->sc_as, &sc->sc_ash) || 132 pnpbus_io_map(&pna->pna_res, 1, &sc->sc_data, &sc->sc_datah)) { 133 aprint_error("nvram: couldn't map registers\n"); 134 return; 135 } 136 137 simple_lock_init(&nvram_slock); 138 139 /* Initialize the nvram header */ 140 p = (uint8_t *) &prep_nvram_header; 141 for (i = 0; i < sizeof(HEADER); i++) 142 *p++ = prep_nvram_read_val(i); 143 144 /* 145 * now that we have the header, we know how big the NVRAM part on 146 * this machine really is. Malloc space to save a copy. 147 */ 148 149 nvlen = 1024 * prep_nvram_header.Size; 150 nvramData = malloc(nvlen, M_DEVBUF, M_NOWAIT); 151 p = (uint8_t *) nvramData; 152 153 /* 154 * now read the whole nvram in, one chunk at a time, marking down 155 * the main start points as we go. 156 */ 157 for (i = 0; i < sizeof(HEADER) && i < nvlen; i++) 158 *p++ = prep_nvram_read_val(i); 159 nvramGEAp = p; 160 cur = i; 161 for (; i < cur + prep_nvram_header.GELength && i < nvlen; i++) 162 *p++ = prep_nvram_read_val(i); 163 nvramOSAp = p; 164 cur = i; 165 for (; i < cur + prep_nvram_header.OSAreaLength && i < nvlen; i++) 166 *p++ = prep_nvram_read_val(i); 167 nvramCAp = p; 168 cur = i; 169 for (; i < cur + prep_nvram_header.ConfigLength && i < nvlen; i++) 170 *p++ = prep_nvram_read_val(i); 171 172 /* we should be done here. umm.. yay? */ 173 nvram = (NVRAM_MAP *)&nvramData[0]; 174 aprint_normal("\n"); 175 aprint_verbose("%s: Read %d bytes from nvram of size %d\n", 176 device_xname(self), i, nvlen); 177 178 #if defined(NVRAM_DUMP) 179 printf("Boot device: %s\n", prep_nvram_get_var("fw-boot-device")); 180 printf("Dumping nvram\n"); 181 for (cur=0; cur < i; cur++) { 182 printf("%c", nvramData[cur]); 183 if (cur % 70 == 0) 184 printf("\n"); 185 } 186 #endif 187 strncpy(bootpath, prep_nvram_get_var("fw-boot-device"), 256); 188 189 190 if (prep_clock_mk48txx == 0) 191 return; 192 /* otherwise, we have a motorolla clock chip. Set it up. */ 193 sc->sc_mksc.sc_model = "mk48t18"; 194 sc->sc_mksc.sc_year0 = 1900; 195 sc->sc_mksc.sc_nvrd = mkclock_pnpbus_nvrd; 196 sc->sc_mksc.sc_nvwr = mkclock_pnpbus_nvwr; 197 /* copy down the bus space tags */ 198 sc->sc_mksc.sc_bst = sc->sc_as; 199 sc->sc_mksc.sc_bsh = sc->sc_ash; 200 sc->sc_mksc.sc_data = sc->sc_data; 201 sc->sc_mksc.sc_datah = sc->sc_datah; 202 203 aprint_normal("%s: attaching clock", device_xname(self)); 204 mk48txx_attach((struct mk48txx_softc *)&sc->sc_mksc); 205 aprint_normal("\n"); 206 } 207 208 /* 209 * This function should be called at a high spl only, as it interfaces with 210 * real hardware. 211 */ 212 213 uint8_t 214 prep_nvram_read_val(int addr) 215 { 216 struct nvram_pnpbus_softc *sc; 217 218 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV); 219 if (sc == NULL) 220 return 0; 221 222 /* tell the NVRAM what we want */ 223 bus_space_write_1(sc->sc_as, sc->sc_ash, 0, addr); 224 bus_space_write_1(sc->sc_as, sc->sc_ash, 1, addr>>8); 225 226 return bus_space_read_1(sc->sc_data, sc->sc_datah, 0); 227 } 228 229 /* 230 * This function should be called at a high spl only, as it interfaces with 231 * real hardware. 232 */ 233 234 void 235 prep_nvram_write_val(int addr, uint8_t val) 236 { 237 struct nvram_pnpbus_softc *sc; 238 239 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV); 240 if (sc == NULL) 241 return; 242 243 /* tell the NVRAM what we want */ 244 bus_space_write_1(sc->sc_as, sc->sc_ash, 0, addr); 245 bus_space_write_1(sc->sc_as, sc->sc_ash, 1, addr>>8); 246 247 bus_space_write_1(sc->sc_data, sc->sc_datah, 0, val); 248 } 249 250 /* the rest of these should all be called with the lock held */ 251 252 char * 253 prep_nvram_next_var(char *name) 254 { 255 char *cp; 256 257 if (name == NULL) 258 return NULL; 259 260 cp = name; 261 /* skip forward to the first null char */ 262 while ((cp - nvramGEAp) < nvram->Header.GELength && (*cp != '\0')) 263 cp++; 264 /* skip nulls */ 265 while ((cp - nvramGEAp) < nvram->Header.GELength && (*cp == '\0')) 266 cp++; 267 if ((cp - nvramGEAp) < nvram->Header.GELength) 268 return cp; 269 else 270 return NULL; 271 } 272 273 char * 274 prep_nvram_find_var(const char *name) 275 { 276 char *cp = nvramGEAp; 277 size_t len; 278 279 len = strlen(name); 280 while (cp != NULL) { 281 if ((strncmp(name, cp, len) == 0) && (cp[len] == '=')) 282 return cp; 283 cp = prep_nvram_next_var(cp); 284 } 285 return NULL; 286 } 287 288 char * 289 prep_nvram_get_var(const char *name) 290 { 291 char *cp = nvramGEAp; 292 size_t len; 293 294 if (name == NULL) 295 return NULL; 296 len = strlen(name); 297 while (cp != NULL) { 298 if ((strncmp(name, cp, len) == 0) && (cp[len] == '=')) 299 return cp+len+1; 300 cp = prep_nvram_next_var(cp); 301 } 302 return NULL; 303 } 304 305 int 306 prep_nvram_get_var_len(const char *name) 307 { 308 char *cp = nvramGEAp; 309 char *ep; 310 size_t len; 311 312 if (name == NULL) 313 return -1; 314 315 len = strlen(name); 316 while (cp != NULL) { 317 if ((strncmp(name, cp, len) == 0) && (cp[len] == '=')) 318 goto out; 319 cp = prep_nvram_next_var(cp); 320 } 321 return -1; 322 323 out: 324 ep = cp; 325 while (ep != NULL && *ep != '\0') 326 ep++; 327 return ep-cp; 328 } 329 330 int 331 prep_nvram_count_vars(void) 332 { 333 char *cp = nvramGEAp; 334 int i=0; 335 336 while (cp != NULL) { 337 i++; 338 cp = prep_nvram_next_var(cp); 339 } 340 return i; 341 } 342 343 static int 344 nvramgetstr(int len, char *user, char **cpp) 345 { 346 int error; 347 char *cp; 348 349 /* Reject obvious bogus requests */ 350 if ((u_int)len > (8 * 1024) - 1) 351 return ENAMETOOLONG; 352 353 *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK); 354 error = copyin(user, cp, len); 355 cp[len] = '\0'; 356 return error; 357 } 358 359 int 360 prep_nvramioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 361 { 362 int len, error; 363 struct pnviocdesc *pnv; 364 char *np, *cp, *name; 365 366 pnv = (struct pnviocdesc *)data; 367 error = 0; 368 cp = name = NULL; 369 370 switch (cmd) { 371 case PNVIOCGET: 372 if (pnv->pnv_name == NULL) 373 return EINVAL; 374 375 error = nvramgetstr(pnv->pnv_namelen, pnv->pnv_name, &name); 376 simple_lock(&nvram_slock); 377 np = prep_nvram_get_var(name); 378 simple_unlock(&nvram_slock); 379 if (np == NULL) 380 return EINVAL; 381 simple_lock(&nvram_slock); 382 len = prep_nvram_get_var_len(name); 383 simple_unlock(&nvram_slock); 384 385 if (len > pnv->pnv_buflen) { 386 error = ENOMEM; 387 break; 388 } 389 if (len <= 0) 390 break; 391 error = copyout(np, pnv->pnv_buf, len); 392 pnv->pnv_buflen = len; 393 break; 394 395 case PNVIOCGETNEXTNAME: 396 /* if the first one is null, we give them the first name */ 397 simple_lock(&nvram_slock); 398 if (pnv->pnv_name == NULL) { 399 cp = nvramGEAp; 400 } else { 401 error = nvramgetstr(pnv->pnv_namelen, pnv->pnv_name, 402 &name); 403 if (!error) { 404 np = prep_nvram_find_var(name); 405 cp = prep_nvram_next_var(np); 406 } 407 } 408 simple_unlock(&nvram_slock); 409 if (cp == NULL) 410 error = EINVAL; 411 if (error) 412 break; 413 414 np = cp; 415 while (*np != '=') 416 np++; 417 len = np-cp; 418 if (len > pnv->pnv_buflen) { 419 error = ENOMEM; 420 break; 421 } 422 error = copyout(cp, pnv->pnv_buf, len); 423 if (error) 424 break; 425 pnv->pnv_buflen = len; 426 break; 427 428 case PNVIOCGETNUMGE: 429 /* count the GE variables */ 430 simple_lock(&nvram_slock); 431 pnv->pnv_num = prep_nvram_count_vars(); 432 simple_unlock(&nvram_slock); 433 break; 434 case PNVIOCSET: 435 /* this will require some real work. Not ready yet */ 436 return ENOTSUP; 437 438 default: 439 return ENOTTY; 440 } 441 if (name) 442 free(name, M_TEMP); 443 return error; 444 } 445 446 int 447 prep_nvramread(dev_t dev, struct uio *uio, int flags) 448 { 449 int size, resid, error; 450 u_int c; 451 char *rdata; 452 453 error = 0; 454 rdata = (char *)&resdata; 455 456 if (uio->uio_rw == UIO_WRITE) { 457 uio->uio_resid = 0; 458 return 0; 459 } 460 461 switch (minor(dev)) { 462 case DEV_NVRAM: 463 size = nvram->Header.Size * 1024; 464 break; 465 case DEV_RESIDUAL: 466 size = res->ResidualLength; 467 break; 468 default: 469 return ENXIO; 470 } 471 resid = size; 472 if (uio->uio_resid < resid) 473 resid = uio->uio_resid; 474 while (resid > 0 && error == 0 && uio->uio_offset < size) { 475 switch (minor(dev)) { 476 case DEV_NVRAM: 477 c = min(resid, PAGE_SIZE); 478 error = uiomove(&nvramData[uio->uio_offset], c, uio); 479 break; 480 case DEV_RESIDUAL: 481 c = min(resid, PAGE_SIZE); 482 error = uiomove(&rdata[uio->uio_offset], c, uio); 483 break; 484 default: 485 return ENXIO; 486 } 487 } 488 return error; 489 } 490 491 int 492 prep_nvramopen(dev_t dev, int flags, int mode, struct lwp *l) 493 { 494 struct nvram_pnpbus_softc *sc; 495 496 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV); 497 if (sc == NULL) 498 return ENODEV; 499 500 if (sc->sc_open) 501 return EBUSY; 502 503 sc->sc_open = 1; 504 505 return 0; 506 } 507 508 int 509 prep_nvramclose(dev_t dev, int flags, int mode, struct lwp *l) 510 { 511 struct nvram_pnpbus_softc *sc; 512 513 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV); 514 if (sc == NULL) 515 return ENODEV; 516 sc->sc_open = 0; 517 return 0; 518 } 519 520 /* Motorola mk48txx clock routines */ 521 uint8_t 522 mkclock_pnpbus_nvrd(struct mk48txx_softc *osc, int off) 523 { 524 struct prep_mk48txx_softc *sc = (struct prep_mk48txx_softc *)osc; 525 uint8_t datum; 526 int s; 527 528 #ifdef DEBUG 529 aprint_debug("mkclock_pnpbus_nvrd(%d)", off); 530 #endif 531 s = splclock(); 532 bus_space_write_1(sc->sc_bst, sc->sc_bsh, 0, off & 0xff); 533 bus_space_write_1(sc->sc_bst, sc->sc_bsh, 1, off >> 8); 534 datum = bus_space_read_1(sc->sc_data, sc->sc_datah, 0); 535 splx(s); 536 #ifdef DEBUG 537 aprint_debug(" -> %02x\n", datum); 538 #endif 539 return datum; 540 } 541 542 void 543 mkclock_pnpbus_nvwr(struct mk48txx_softc *osc, int off, uint8_t datum) 544 { 545 struct prep_mk48txx_softc *sc = (struct prep_mk48txx_softc *)osc; 546 int s; 547 548 #ifdef DEBUG 549 aprint_debug("mkclock_isa_nvwr(%d, %02x)\n", off, datum); 550 #endif 551 s = splclock(); 552 bus_space_write_1(sc->sc_bst, sc->sc_bsh, 0, off & 0xff); 553 bus_space_write_1(sc->sc_bst, sc->sc_bsh, 1, off >> 8); 554 bus_space_write_1(sc->sc_data, sc->sc_datah, 0, datum); 555 splx(s); 556 } 557