No empty .Rs/.Re
[netbsd-mini2440.git] / sys / arch / prep / pnpbus / nvram_pnpbus.c
blob9729ea4c4b925b9b3ddc12a87ff2d486139cefb9
1 /* $NetBSD: nvram_pnpbus.c,v 1.13 2008/03/29 17:55:35 matt Exp $ */
3 /*-
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tim Rightnour
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.
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.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: nvram_pnpbus.c,v 1.13 2008/03/29 17:55:35 matt Exp $");
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>
47 #include <machine/isa_machdep.h>
48 /* clock stuff for motorolla machines */
49 #include <dev/clock_subr.h>
50 #include <dev/ic/mk48txxreg.h>
52 #include <uvm/uvm_extern.h>
54 #include <machine/residual.h>
55 #include <machine/nvram.h>
57 #include <prep/pnpbus/pnpbusvar.h>
59 #include "opt_nvram.h"
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 */
68 int prep_clock_mk48txx;
70 extern char bootpath[256];
71 extern RESIDUAL resdata;
73 #define NVRAM_STD_DEV 0
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);
87 CFATTACH_DECL(nvram_pnpbus, sizeof(struct nvram_pnpbus_softc),
88 nvram_pnpbus_probe, nvram_pnpbus_attach, NULL, NULL);
90 dev_type_open(prep_nvramopen);
91 dev_type_ioctl(prep_nvramioctl);
92 dev_type_close(prep_nvramclose);
93 dev_type_read(prep_nvramread);
95 const struct cdevsw nvram_cdevsw = {
96 prep_nvramopen, prep_nvramclose, prep_nvramread, nowrite,
97 prep_nvramioctl, nostop, notty, nopoll, nommap, nokqfilter, D_OTHER,
100 extern struct cfdriver nvram_cd;
102 static int
103 nvram_pnpbus_probe(struct device *parent, struct cfdata *match, void *aux)
105 struct pnpbus_dev_attach_args *pna = aux;
106 int ret = 0;
108 if (strcmp(pna->pna_devid, "IBM0008") == 0)
109 ret = 1;
111 if (ret)
112 pnpbus_scan(pna, pna->pna_ppc_dev);
114 return ret;
117 static void
118 nvram_pnpbus_attach(struct device *parent, struct device *self, void *aux)
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;
126 sc->sc_iot = pna->pna_iot;
128 pnpbus_getioport(&pna->pna_res, 0, &as_iobase, &as_len);
129 pnpbus_getioport(&pna->pna_res, 1, &data_iobase, &data_len);
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;
137 simple_lock_init(&nvram_slock);
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);
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.
149 nvlen = 1024 * prep_nvram_header.Size;
150 nvramData = malloc(nvlen, M_DEVBUF, M_NOWAIT);
151 p = (uint8_t *) nvramData;
154 * now read the whole nvram in, one chunk at a time, marking down
155 * the main start points as we go.
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);
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);
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");
186 #endif
187 strncpy(bootpath, prep_nvram_get_var("fw-boot-device"), 256);
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;
203 aprint_normal("%s: attaching clock", device_xname(self));
204 mk48txx_attach((struct mk48txx_softc *)&sc->sc_mksc);
205 aprint_normal("\n");
209 * This function should be called at a high spl only, as it interfaces with
210 * real hardware.
213 uint8_t
214 prep_nvram_read_val(int addr)
216 struct nvram_pnpbus_softc *sc;
218 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV);
219 if (sc == NULL)
220 return 0;
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);
226 return bus_space_read_1(sc->sc_data, sc->sc_datah, 0);
230 * This function should be called at a high spl only, as it interfaces with
231 * real hardware.
234 void
235 prep_nvram_write_val(int addr, uint8_t val)
237 struct nvram_pnpbus_softc *sc;
239 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV);
240 if (sc == NULL)
241 return;
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);
247 bus_space_write_1(sc->sc_data, sc->sc_datah, 0, val);
250 /* the rest of these should all be called with the lock held */
252 char *
253 prep_nvram_next_var(char *name)
255 char *cp;
257 if (name == NULL)
258 return NULL;
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;
273 char *
274 prep_nvram_find_var(const char *name)
276 char *cp = nvramGEAp;
277 size_t len;
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);
285 return NULL;
288 char *
289 prep_nvram_get_var(const char *name)
291 char *cp = nvramGEAp;
292 size_t len;
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);
302 return NULL;
306 prep_nvram_get_var_len(const char *name)
308 char *cp = nvramGEAp;
309 char *ep;
310 size_t len;
312 if (name == NULL)
313 return -1;
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);
321 return -1;
323 out:
324 ep = cp;
325 while (ep != NULL && *ep != '\0')
326 ep++;
327 return ep-cp;
331 prep_nvram_count_vars(void)
333 char *cp = nvramGEAp;
334 int i=0;
336 while (cp != NULL) {
337 i++;
338 cp = prep_nvram_next_var(cp);
340 return i;
343 static int
344 nvramgetstr(int len, char *user, char **cpp)
346 int error;
347 char *cp;
349 /* Reject obvious bogus requests */
350 if ((u_int)len > (8 * 1024) - 1)
351 return ENAMETOOLONG;
353 *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
354 error = copyin(user, cp, len);
355 cp[len] = '\0';
356 return error;
360 prep_nvramioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
362 int len, error;
363 struct pnviocdesc *pnv;
364 char *np, *cp, *name;
366 pnv = (struct pnviocdesc *)data;
367 error = 0;
368 cp = name = NULL;
370 switch (cmd) {
371 case PNVIOCGET:
372 if (pnv->pnv_name == NULL)
373 return EINVAL;
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);
385 if (len > pnv->pnv_buflen) {
386 error = ENOMEM;
387 break;
389 if (len <= 0)
390 break;
391 error = copyout(np, pnv->pnv_buf, len);
392 pnv->pnv_buflen = len;
393 break;
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);
408 simple_unlock(&nvram_slock);
409 if (cp == NULL)
410 error = EINVAL;
411 if (error)
412 break;
414 np = cp;
415 while (*np != '=')
416 np++;
417 len = np-cp;
418 if (len > pnv->pnv_buflen) {
419 error = ENOMEM;
420 break;
422 error = copyout(cp, pnv->pnv_buf, len);
423 if (error)
424 break;
425 pnv->pnv_buflen = len;
426 break;
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;
438 default:
439 return ENOTTY;
441 if (name)
442 free(name, M_TEMP);
443 return error;
447 prep_nvramread(dev_t dev, struct uio *uio, int flags)
449 int size, resid, error;
450 u_int c;
451 char *rdata;
453 error = 0;
454 rdata = (char *)&resdata;
456 if (uio->uio_rw == UIO_WRITE) {
457 uio->uio_resid = 0;
458 return 0;
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;
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;
488 return error;
492 prep_nvramopen(dev_t dev, int flags, int mode, struct lwp *l)
494 struct nvram_pnpbus_softc *sc;
496 sc = device_lookup_private(&nvram_cd, NVRAM_STD_DEV);
497 if (sc == NULL)
498 return ENODEV;
500 if (sc->sc_open)
501 return EBUSY;
503 sc->sc_open = 1;
505 return 0;
509 prep_nvramclose(dev_t dev, int flags, int mode, struct lwp *l)
511 struct nvram_pnpbus_softc *sc;
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;
520 /* Motorola mk48txx clock routines */
521 uint8_t
522 mkclock_pnpbus_nvrd(struct mk48txx_softc *osc, int off)
524 struct prep_mk48txx_softc *sc = (struct prep_mk48txx_softc *)osc;
525 uint8_t datum;
526 int s;
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;
542 void
543 mkclock_pnpbus_nvwr(struct mk48txx_softc *osc, int off, uint8_t datum)
545 struct prep_mk48txx_softc *sc = (struct prep_mk48txx_softc *)osc;
546 int s;
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);