1 /* $NetBSD: nvram_pnpbus.c,v 1.13 2008/03/29 17:55:35 matt Exp $ */
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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>
40 #include <sys/kthread.h>
41 #include <sys/device.h>
42 #include <sys/malloc.h>
43 #include <sys/simplelock.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
;
103 nvram_pnpbus_probe(struct device
*parent
, struct cfdata
*match
, void *aux
)
105 struct pnpbus_dev_attach_args
*pna
= aux
;
108 if (strcmp(pna
->pna_devid
, "IBM0008") == 0)
112 pnpbus_scan(pna
, pna
->pna_ppc_dev
);
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
;
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");
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
);
161 for (; i
< cur
+ prep_nvram_header
.GELength
&& i
< nvlen
; i
++)
162 *p
++ = prep_nvram_read_val(i
);
165 for (; i
< cur
+ prep_nvram_header
.OSAreaLength
&& i
< nvlen
; i
++)
166 *p
++ = prep_nvram_read_val(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];
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
]);
187 strncpy(bootpath
, prep_nvram_get_var("fw-boot-device"), 256);
190 if (prep_clock_mk48txx
== 0)
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
);
209 * This function should be called at a high spl only, as it interfaces with
214 prep_nvram_read_val(int addr
)
216 struct nvram_pnpbus_softc
*sc
;
218 sc
= device_lookup_private(&nvram_cd
, NVRAM_STD_DEV
);
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
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
);
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 */
253 prep_nvram_next_var(char *name
)
261 /* skip forward to the first null char */
262 while ((cp
- nvramGEAp
) < nvram
->Header
.GELength
&& (*cp
!= '\0'))
265 while ((cp
- nvramGEAp
) < nvram
->Header
.GELength
&& (*cp
== '\0'))
267 if ((cp
- nvramGEAp
) < nvram
->Header
.GELength
)
274 prep_nvram_find_var(const char *name
)
276 char *cp
= nvramGEAp
;
281 if ((strncmp(name
, cp
, len
) == 0) && (cp
[len
] == '='))
283 cp
= prep_nvram_next_var(cp
);
289 prep_nvram_get_var(const char *name
)
291 char *cp
= nvramGEAp
;
298 if ((strncmp(name
, cp
, len
) == 0) && (cp
[len
] == '='))
300 cp
= prep_nvram_next_var(cp
);
306 prep_nvram_get_var_len(const char *name
)
308 char *cp
= nvramGEAp
;
317 if ((strncmp(name
, cp
, len
) == 0) && (cp
[len
] == '='))
319 cp
= prep_nvram_next_var(cp
);
325 while (ep
!= NULL
&& *ep
!= '\0')
331 prep_nvram_count_vars(void)
333 char *cp
= nvramGEAp
;
338 cp
= prep_nvram_next_var(cp
);
344 nvramgetstr(int len
, char *user
, char **cpp
)
349 /* Reject obvious bogus requests */
350 if ((u_int
)len
> (8 * 1024) - 1)
353 *cpp
= cp
= malloc(len
+ 1, M_TEMP
, M_WAITOK
);
354 error
= copyin(user
, cp
, len
);
360 prep_nvramioctl(dev_t dev
, u_long cmd
, void *data
, int flags
, struct lwp
*l
)
363 struct pnviocdesc
*pnv
;
364 char *np
, *cp
, *name
;
366 pnv
= (struct pnviocdesc
*)data
;
372 if (pnv
->pnv_name
== NULL
)
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
);
381 simple_lock(&nvram_slock
);
382 len
= prep_nvram_get_var_len(name
);
383 simple_unlock(&nvram_slock
);
385 if (len
> pnv
->pnv_buflen
) {
391 error
= copyout(np
, pnv
->pnv_buf
, len
);
392 pnv
->pnv_buflen
= len
;
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
) {
401 error
= nvramgetstr(pnv
->pnv_namelen
, pnv
->pnv_name
,
404 np
= prep_nvram_find_var(name
);
405 cp
= prep_nvram_next_var(np
);
408 simple_unlock(&nvram_slock
);
418 if (len
> pnv
->pnv_buflen
) {
422 error
= copyout(cp
, pnv
->pnv_buf
, len
);
425 pnv
->pnv_buflen
= len
;
429 /* count the GE variables */
430 simple_lock(&nvram_slock
);
431 pnv
->pnv_num
= prep_nvram_count_vars();
432 simple_unlock(&nvram_slock
);
435 /* this will require some real work. Not ready yet */
447 prep_nvramread(dev_t dev
, struct uio
*uio
, int flags
)
449 int size
, resid
, error
;
454 rdata
= (char *)&resdata
;
456 if (uio
->uio_rw
== UIO_WRITE
) {
461 switch (minor(dev
)) {
463 size
= nvram
->Header
.Size
* 1024;
466 size
= res
->ResidualLength
;
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
)) {
477 c
= min(resid
, PAGE_SIZE
);
478 error
= uiomove(&nvramData
[uio
->uio_offset
], c
, uio
);
481 c
= min(resid
, PAGE_SIZE
);
482 error
= uiomove(&rdata
[uio
->uio_offset
], c
, uio
);
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
);
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
);
520 /* Motorola mk48txx clock routines */
522 mkclock_pnpbus_nvrd(struct mk48txx_softc
*osc
, int off
)
524 struct prep_mk48txx_softc
*sc
= (struct prep_mk48txx_softc
*)osc
;
529 aprint_debug("mkclock_pnpbus_nvrd(%d)", off
);
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);
537 aprint_debug(" -> %02x\n", datum
);
543 mkclock_pnpbus_nvwr(struct mk48txx_softc
*osc
, int off
, uint8_t datum
)
545 struct prep_mk48txx_softc
*sc
= (struct prep_mk48txx_softc
*)osc
;
549 aprint_debug("mkclock_isa_nvwr(%d, %02x)\n", off
, datum
);
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
);