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