xref: /netbsd/sys/arch/prep/pnpbus/nvram_pnpbus.c (revision 6550d01e)
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