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