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