1 /* $NetBSD: nvram_pnpbus.c,v 1.16 2012/02/18 23:51:27 rmind 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.16 2012/02/18 23:51:27 rmind 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 67 int prep_clock_mk48txx; 68 69 extern char bootpath[256]; 70 extern RESIDUAL resdata; 71 72 #define NVRAM_STD_DEV 0 73 74 static int nvram_pnpbus_probe(device_t, cfdata_t, void *); 75 static void nvram_pnpbus_attach(device_t, device_t, void *); 76 uint8_t prep_nvram_read_val(int); 77 char *prep_nvram_next_var(char *); 78 char *prep_nvram_find_var(const char *); 79 char *prep_nvram_get_var(const char *); 80 int prep_nvram_get_var_len(const char *); 81 int prep_nvram_count_vars(void); 82 void prep_nvram_write_val(int, uint8_t); 83 uint8_t mkclock_pnpbus_nvrd(struct mk48txx_softc *, int); 84 void mkclock_pnpbus_nvwr(struct mk48txx_softc *, int, uint8_t); 85 86 CFATTACH_DECL(nvram_pnpbus, sizeof(struct nvram_pnpbus_softc), 87 nvram_pnpbus_probe, nvram_pnpbus_attach, NULL, NULL); 88 89 dev_type_open(prep_nvramopen); 90 dev_type_ioctl(prep_nvramioctl); 91 dev_type_close(prep_nvramclose); 92 dev_type_read(prep_nvramread); 93 94 const struct cdevsw nvram_cdevsw = { 95 prep_nvramopen, prep_nvramclose, prep_nvramread, nowrite, 96 prep_nvramioctl, nostop, notty, nopoll, nommap, nokqfilter, D_OTHER, 97 }; 98 99 extern struct cfdriver nvram_cd; 100 101 static int 102 nvram_pnpbus_probe(device_t parent, cfdata_t match, void *aux) 103 { 104 struct pnpbus_dev_attach_args *pna = aux; 105 int ret = 0; 106 107 if (strcmp(pna->pna_devid, "IBM0008") == 0) 108 ret = 1; 109 110 if (ret) 111 pnpbus_scan(pna, pna->pna_ppc_dev); 112 113 return ret; 114 } 115 116 static void 117 nvram_pnpbus_attach(device_t parent, device_t self, void *aux) 118 { 119 struct nvram_pnpbus_softc *sc = device_private(self); 120 struct pnpbus_dev_attach_args *pna = aux; 121 int as_iobase, as_len, data_iobase, data_len, i, nvlen, cur; 122 uint8_t *p; 123 HEADER prep_nvram_header; 124 125 sc->sc_iot = pna->pna_iot; 126 127 pnpbus_getioport(&pna->pna_res, 0, &as_iobase, &as_len); 128 pnpbus_getioport(&pna->pna_res, 1, &data_iobase, &data_len); 129 130 if (pnpbus_io_map(&pna->pna_res, 0, &sc->sc_as, &sc->sc_ash) || 131 pnpbus_io_map(&pna->pna_res, 1, &sc->sc_data, &sc->sc_datah)) { 132 aprint_error("nvram: couldn't map registers\n"); 133 return; 134 } 135 136 /* Initialize the nvram header */ 137 p = (uint8_t *) &prep_nvram_header; 138 for (i = 0; i < sizeof(HEADER); i++) 139 *p++ = prep_nvram_read_val(i); 140 141 /* 142 * now that we have the header, we know how big the NVRAM part on 143 * this machine really is. Malloc space to save a copy. 144 */ 145 146 nvlen = 1024 * prep_nvram_header.Size; 147 nvramData = malloc(nvlen, M_DEVBUF, M_NOWAIT); 148 p = (uint8_t *) nvramData; 149 150 /* 151 * now read the whole nvram in, one chunk at a time, marking down 152 * the main start points as we go. 153 */ 154 for (i = 0; i < sizeof(HEADER) && i < nvlen; i++) 155 *p++ = prep_nvram_read_val(i); 156 nvramGEAp = p; 157 cur = i; 158 for (; i < cur + prep_nvram_header.GELength && i < nvlen; i++) 159 *p++ = prep_nvram_read_val(i); 160 nvramOSAp = p; 161 cur = i; 162 for (; i < cur + prep_nvram_header.OSAreaLength && i < nvlen; i++) 163 *p++ = prep_nvram_read_val(i); 164 nvramCAp = p; 165 cur = i; 166 for (; i < cur + prep_nvram_header.ConfigLength && i < nvlen; i++) 167 *p++ = prep_nvram_read_val(i); 168 169 /* we should be done here. umm.. yay? */ 170 nvram = (NVRAM_MAP *)&nvramData[0]; 171 aprint_normal("\n"); 172 aprint_verbose("%s: Read %d bytes from nvram of size %d\n", 173 device_xname(self), i, nvlen); 174 175 #if defined(NVRAM_DUMP) 176 printf("Boot device: %s\n", prep_nvram_get_var("fw-boot-device")); 177 printf("Dumping nvram\n"); 178 for (cur=0; cur < i; cur++) { 179 printf("%c", nvramData[cur]); 180 if (cur % 70 == 0) 181 printf("\n"); 182 } 183 #endif 184 strncpy(bootpath, prep_nvram_get_var("fw-boot-device"), 256); 185 186 187 if (prep_clock_mk48txx == 0) 188 return; 189 /* otherwise, we have a motorolla clock chip. Set it up. */ 190 sc->sc_mksc.sc_model = "mk48t18"; 191 sc->sc_mksc.sc_year0 = 1900; 192 sc->sc_mksc.sc_nvrd = mkclock_pnpbus_nvrd; 193 sc->sc_mksc.sc_nvwr = mkclock_pnpbus_nvwr; 194 /* copy down the bus space tags */ 195 sc->sc_mksc.sc_bst = sc->sc_as; 196 sc->sc_mksc.sc_bsh = sc->sc_ash; 197 sc->sc_mksc.sc_data = sc->sc_data; 198 sc->sc_mksc.sc_datah = sc->sc_datah; 199 200 aprint_normal("%s: attaching clock", device_xname(self)); 201 mk48txx_attach((struct mk48txx_softc *)&sc->sc_mksc); 202 aprint_normal("\n"); 203 } 204 205 /* 206 * This function should be called at a high spl only, as it interfaces with 207 * real hardware. 208 */ 209 210 uint8_t 211 prep_nvram_read_val(int addr) 212 { 213 struct nvram_pnpbus_softc *sc; 214 215 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV); 216 if (sc == NULL) 217 return 0; 218 219 /* tell the NVRAM what we want */ 220 bus_space_write_1(sc->sc_as, sc->sc_ash, 0, addr); 221 bus_space_write_1(sc->sc_as, sc->sc_ash, 1, addr>>8); 222 223 return bus_space_read_1(sc->sc_data, sc->sc_datah, 0); 224 } 225 226 /* 227 * This function should be called at a high spl only, as it interfaces with 228 * real hardware. 229 */ 230 231 void 232 prep_nvram_write_val(int addr, uint8_t val) 233 { 234 struct nvram_pnpbus_softc *sc; 235 236 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV); 237 if (sc == NULL) 238 return; 239 240 /* tell the NVRAM what we want */ 241 bus_space_write_1(sc->sc_as, sc->sc_ash, 0, addr); 242 bus_space_write_1(sc->sc_as, sc->sc_ash, 1, addr>>8); 243 244 bus_space_write_1(sc->sc_data, sc->sc_datah, 0, val); 245 } 246 247 /* the rest of these should all be called with the lock held */ 248 249 char * 250 prep_nvram_next_var(char *name) 251 { 252 char *cp; 253 254 if (name == NULL) 255 return NULL; 256 257 cp = name; 258 /* skip forward to the first null char */ 259 while ((cp - nvramGEAp) < nvram->Header.GELength && (*cp != '\0')) 260 cp++; 261 /* skip nulls */ 262 while ((cp - nvramGEAp) < nvram->Header.GELength && (*cp == '\0')) 263 cp++; 264 if ((cp - nvramGEAp) < nvram->Header.GELength) 265 return cp; 266 else 267 return NULL; 268 } 269 270 char * 271 prep_nvram_find_var(const char *name) 272 { 273 char *cp = nvramGEAp; 274 size_t len; 275 276 len = strlen(name); 277 while (cp != NULL) { 278 if ((strncmp(name, cp, len) == 0) && (cp[len] == '=')) 279 return cp; 280 cp = prep_nvram_next_var(cp); 281 } 282 return NULL; 283 } 284 285 char * 286 prep_nvram_get_var(const char *name) 287 { 288 char *cp = nvramGEAp; 289 size_t len; 290 291 if (name == NULL) 292 return NULL; 293 len = strlen(name); 294 while (cp != NULL) { 295 if ((strncmp(name, cp, len) == 0) && (cp[len] == '=')) 296 return cp+len+1; 297 cp = prep_nvram_next_var(cp); 298 } 299 return NULL; 300 } 301 302 int 303 prep_nvram_get_var_len(const char *name) 304 { 305 char *cp = nvramGEAp; 306 char *ep; 307 size_t len; 308 309 if (name == NULL) 310 return -1; 311 312 len = strlen(name); 313 while (cp != NULL) { 314 if ((strncmp(name, cp, len) == 0) && (cp[len] == '=')) 315 goto out; 316 cp = prep_nvram_next_var(cp); 317 } 318 return -1; 319 320 out: 321 ep = cp; 322 while (ep != NULL && *ep != '\0') 323 ep++; 324 return ep-cp; 325 } 326 327 int 328 prep_nvram_count_vars(void) 329 { 330 char *cp = nvramGEAp; 331 int i=0; 332 333 while (cp != NULL) { 334 i++; 335 cp = prep_nvram_next_var(cp); 336 } 337 return i; 338 } 339 340 static int 341 nvramgetstr(int len, char *user, char **cpp) 342 { 343 int error; 344 char *cp; 345 346 /* Reject obvious bogus requests */ 347 if ((u_int)len > (8 * 1024) - 1) 348 return ENAMETOOLONG; 349 350 *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK); 351 error = copyin(user, cp, len); 352 cp[len] = '\0'; 353 return error; 354 } 355 356 int 357 prep_nvramioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 358 { 359 int len, error; 360 struct pnviocdesc *pnv; 361 char *np, *cp, *name; 362 363 pnv = (struct pnviocdesc *)data; 364 error = 0; 365 cp = name = NULL; 366 367 switch (cmd) { 368 case PNVIOCGET: 369 if (pnv->pnv_name == NULL) 370 return EINVAL; 371 372 error = nvramgetstr(pnv->pnv_namelen, pnv->pnv_name, &name); 373 np = prep_nvram_get_var(name); 374 if (np == NULL) 375 return EINVAL; 376 len = prep_nvram_get_var_len(name); 377 378 if (len > pnv->pnv_buflen) { 379 error = ENOMEM; 380 break; 381 } 382 if (len <= 0) 383 break; 384 error = copyout(np, pnv->pnv_buf, len); 385 pnv->pnv_buflen = len; 386 break; 387 388 case PNVIOCGETNEXTNAME: 389 /* if the first one is null, we give them the first name */ 390 if (pnv->pnv_name == NULL) { 391 cp = nvramGEAp; 392 } else { 393 error = nvramgetstr(pnv->pnv_namelen, pnv->pnv_name, 394 &name); 395 if (!error) { 396 np = prep_nvram_find_var(name); 397 cp = prep_nvram_next_var(np); 398 } 399 } 400 if (cp == NULL) 401 error = EINVAL; 402 if (error) 403 break; 404 405 np = cp; 406 while (*np != '=') 407 np++; 408 len = np-cp; 409 if (len > pnv->pnv_buflen) { 410 error = ENOMEM; 411 break; 412 } 413 error = copyout(cp, pnv->pnv_buf, len); 414 if (error) 415 break; 416 pnv->pnv_buflen = len; 417 break; 418 419 case PNVIOCGETNUMGE: 420 /* count the GE variables */ 421 pnv->pnv_num = prep_nvram_count_vars(); 422 break; 423 case PNVIOCSET: 424 /* this will require some real work. Not ready yet */ 425 return ENOTSUP; 426 427 default: 428 return ENOTTY; 429 } 430 if (name) 431 free(name, M_TEMP); 432 return error; 433 } 434 435 int 436 prep_nvramread(dev_t dev, struct uio *uio, int flags) 437 { 438 int size, resid, error; 439 u_int c; 440 char *rdata; 441 442 error = 0; 443 rdata = (char *)&resdata; 444 445 if (uio->uio_rw == UIO_WRITE) { 446 uio->uio_resid = 0; 447 return 0; 448 } 449 450 switch (minor(dev)) { 451 case DEV_NVRAM: 452 size = nvram->Header.Size * 1024; 453 break; 454 case DEV_RESIDUAL: 455 size = res->ResidualLength; 456 break; 457 default: 458 return ENXIO; 459 } 460 resid = size; 461 if (uio->uio_resid < resid) 462 resid = uio->uio_resid; 463 while (resid > 0 && error == 0 && uio->uio_offset < size) { 464 switch (minor(dev)) { 465 case DEV_NVRAM: 466 c = min(resid, PAGE_SIZE); 467 error = uiomove(&nvramData[uio->uio_offset], c, uio); 468 break; 469 case DEV_RESIDUAL: 470 c = min(resid, PAGE_SIZE); 471 error = uiomove(&rdata[uio->uio_offset], c, uio); 472 break; 473 default: 474 return ENXIO; 475 } 476 } 477 return error; 478 } 479 480 int 481 prep_nvramopen(dev_t dev, int flags, int mode, struct lwp *l) 482 { 483 struct nvram_pnpbus_softc *sc; 484 485 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV); 486 if (sc == NULL) 487 return ENODEV; 488 489 if (sc->sc_open) 490 return EBUSY; 491 492 sc->sc_open = 1; 493 494 return 0; 495 } 496 497 int 498 prep_nvramclose(dev_t dev, int flags, int mode, struct lwp *l) 499 { 500 struct nvram_pnpbus_softc *sc; 501 502 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV); 503 if (sc == NULL) 504 return ENODEV; 505 sc->sc_open = 0; 506 return 0; 507 } 508 509 /* Motorola mk48txx clock routines */ 510 uint8_t 511 mkclock_pnpbus_nvrd(struct mk48txx_softc *osc, int off) 512 { 513 struct prep_mk48txx_softc *sc = (struct prep_mk48txx_softc *)osc; 514 uint8_t datum; 515 int s; 516 517 #ifdef DEBUG 518 aprint_debug("mkclock_pnpbus_nvrd(%d)", off); 519 #endif 520 s = splclock(); 521 bus_space_write_1(sc->sc_bst, sc->sc_bsh, 0, off & 0xff); 522 bus_space_write_1(sc->sc_bst, sc->sc_bsh, 1, off >> 8); 523 datum = bus_space_read_1(sc->sc_data, sc->sc_datah, 0); 524 splx(s); 525 #ifdef DEBUG 526 aprint_debug(" -> %02x\n", datum); 527 #endif 528 return datum; 529 } 530 531 void 532 mkclock_pnpbus_nvwr(struct mk48txx_softc *osc, int off, uint8_t datum) 533 { 534 struct prep_mk48txx_softc *sc = (struct prep_mk48txx_softc *)osc; 535 int s; 536 537 #ifdef DEBUG 538 aprint_debug("mkclock_isa_nvwr(%d, %02x)\n", off, datum); 539 #endif 540 s = splclock(); 541 bus_space_write_1(sc->sc_bst, sc->sc_bsh, 0, off & 0xff); 542 bus_space_write_1(sc->sc_bst, sc->sc_bsh, 1, off >> 8); 543 bus_space_write_1(sc->sc_data, sc->sc_datah, 0, datum); 544 splx(s); 545 } 546