2 * Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/module.h>
34 #include <sys/kernel.h>
37 #include <dev/ofw/openfirm.h>
38 #include <dev/ofw/ofw_bus.h>
40 #include <machine/bus.h>
41 #include <machine/md_var.h>
42 #include <machine/pio.h>
43 #include <machine/resource.h>
47 #include <dev/powermac_nvram/powermac_nvramvar.h>
55 static int powermac_nvram_probe(device_t);
56 static int powermac_nvram_attach(device_t);
57 static int powermac_nvram_detach(device_t);
59 /* Helper functions */
60 static int powermac_nvram_check(void *data);
61 static int chrp_checksum(int sum, uint8_t *, uint8_t *);
62 static uint32_t adler_checksum(uint8_t *, int);
63 static int erase_bank(device_t, uint8_t *);
64 static int write_bank(device_t, uint8_t *, uint8_t *);
69 static device_method_t powermac_nvram_methods[] = {
70 /* Device interface */
71 DEVMETHOD(device_probe, powermac_nvram_probe),
72 DEVMETHOD(device_attach, powermac_nvram_attach),
73 DEVMETHOD(device_detach, powermac_nvram_detach),
78 static driver_t powermac_nvram_driver = {
80 powermac_nvram_methods,
81 sizeof(struct powermac_nvram_softc)
84 static devclass_t powermac_nvram_devclass;
86 DRIVER_MODULE(powermac_nvram, nexus, powermac_nvram_driver, powermac_nvram_devclass, 0, 0);
92 static d_open_t powermac_nvram_open;
93 static d_close_t powermac_nvram_close;
94 static d_read_t powermac_nvram_read;
95 static d_write_t powermac_nvram_write;
97 static struct cdevsw powermac_nvram_cdevsw = {
98 .d_version = D_VERSION,
99 .d_flags = D_NEEDGIANT,
100 .d_open = powermac_nvram_open,
101 .d_close = powermac_nvram_close,
102 .d_read = powermac_nvram_read,
103 .d_write = powermac_nvram_write,
104 .d_name = "powermac_nvram",
108 powermac_nvram_probe(device_t dev)
110 const char *type, *compatible;
112 type = ofw_bus_get_type(dev);
113 compatible = ofw_bus_get_compat(dev);
115 if (type == NULL || compatible == NULL)
118 if (strcmp(type, "nvram") != 0)
120 if (strcmp(compatible, "amd-0137") != 0 &&
121 strcmp(compatible, "nvram,flash") != 0)
124 device_set_desc(dev, "Apple NVRAM");
129 powermac_nvram_attach(device_t dev)
131 struct powermac_nvram_softc *sc;
132 const char *compatible;
137 node = ofw_bus_get_node(dev);
138 sc = device_get_softc(dev);
140 if ((i = OF_getprop(node, "reg", reg, sizeof(reg))) < 8)
146 compatible = ofw_bus_get_compat(dev);
147 if (strcmp(compatible, "amd-0137") == 0)
148 sc->sc_type = FLASH_TYPE_AMD;
150 sc->sc_type = FLASH_TYPE_SM;
153 * Find which byte of reg corresponds to the 32-bit physical address.
154 * We should probably read #address-cells from /chosen instead.
158 sc->sc_bank0 = (vm_offset_t)pmap_mapdev(reg[i], NVRAM_SIZE * 2);
159 sc->sc_bank1 = sc->sc_bank0 + NVRAM_SIZE;
161 gen0 = powermac_nvram_check((void *)sc->sc_bank0);
162 gen1 = powermac_nvram_check((void *)sc->sc_bank1);
164 if (gen0 == -1 && gen1 == -1) {
165 if ((void *)sc->sc_bank0 != NULL)
166 pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
167 device_printf(dev, "both banks appear to be corrupt\n");
170 device_printf(dev, "bank0 generation %d, bank1 generation %d\n",
173 sc->sc_bank = (gen0 > gen1) ? sc->sc_bank0 : sc->sc_bank1;
174 bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE);
176 sc->sc_cdev = make_dev(&powermac_nvram_cdevsw, 0, 0, 0, 0600,
178 sc->sc_cdev->si_drv1 = sc;
184 powermac_nvram_detach(device_t dev)
186 struct powermac_nvram_softc *sc;
188 sc = device_get_softc(dev);
190 if ((void *)sc->sc_bank0 != NULL)
191 pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
193 if (sc->sc_cdev != NULL)
194 destroy_dev(sc->sc_cdev);
200 powermac_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td)
202 struct powermac_nvram_softc *sc = dev->si_drv1;
207 sc->sc_rpos = sc->sc_wpos = 0;
212 powermac_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
214 struct powermac_nvram_softc *sc = dev->si_drv1;
215 struct core99_header *header;
218 if (sc->sc_wpos != sizeof(sc->sc_data)) {
219 /* Short write, restore in-memory copy */
220 bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE);
225 header = (struct core99_header *)sc->sc_data;
227 header->generation = ((struct core99_header *)sc->sc_bank)->generation;
228 header->generation++;
229 header->chrp_header.signature = CORE99_SIGNATURE;
231 header->adler_checksum =
232 adler_checksum((uint8_t *)&(header->generation),
233 NVRAM_SIZE - offsetof(struct core99_header, generation));
234 header->chrp_header.chrp_checksum = chrp_checksum(header->chrp_header.signature,
235 (uint8_t *)&(header->chrp_header.length),
236 (uint8_t *)&(header->adler_checksum));
238 bank = (sc->sc_bank == sc->sc_bank0) ? sc->sc_bank1 : sc->sc_bank0;
239 if (erase_bank(sc->sc_dev, (uint8_t *)bank) != 0 ||
240 write_bank(sc->sc_dev, (uint8_t *)bank, sc->sc_data) != 0) {
250 powermac_nvram_read(struct cdev *dev, struct uio *uio, int ioflag)
252 int rv, amnt, data_available;
253 struct powermac_nvram_softc *sc = dev->si_drv1;
256 while (uio->uio_resid > 0) {
257 data_available = sizeof(sc->sc_data) - sc->sc_rpos;
258 if (data_available > 0) {
259 amnt = MIN(uio->uio_resid, data_available);
260 rv = uiomove((void *)(sc->sc_data + sc->sc_rpos),
273 powermac_nvram_write(struct cdev *dev, struct uio *uio, int ioflag)
275 int rv, amnt, data_available;
276 struct powermac_nvram_softc *sc = dev->si_drv1;
278 if (sc->sc_wpos >= sizeof(sc->sc_data))
282 while (uio->uio_resid > 0) {
283 data_available = sizeof(sc->sc_data) - sc->sc_wpos;
284 if (data_available > 0) {
285 amnt = MIN(uio->uio_resid, data_available);
286 rv = uiomove((void *)(sc->sc_data + sc->sc_wpos),
299 powermac_nvram_check(void *data)
301 struct core99_header *header;
303 header = (struct core99_header *)data;
305 if (header->chrp_header.signature != CORE99_SIGNATURE)
307 if (header->chrp_header.chrp_checksum !=
308 chrp_checksum(header->chrp_header.signature,
309 (uint8_t *)&(header->chrp_header.length),
310 (uint8_t *)&(header->adler_checksum)))
312 if (header->adler_checksum !=
313 adler_checksum((uint8_t *)&(header->generation),
314 NVRAM_SIZE - offsetof(struct core99_header, generation)))
316 return header->generation;
320 chrp_checksum(int sum, uint8_t *data, uint8_t *end)
323 for (; data < end; data++)
326 sum = (sum & 0xff) + (sum >> 8);
331 adler_checksum(uint8_t *data, int len)
338 for (i = 0; i < len; i++) {
339 if ((i % 5000) == 0) {
349 return (high << 16) | low;
352 #define OUTB_DELAY(a, v) outb(a, v); DELAY(1);
355 wait_operation_complete_amd(uint8_t *bank)
359 for (i = 1000000; i != 0; i--)
360 if ((inb(bank) ^ inb(bank)) == 0)
366 erase_bank_amd(device_t dev, uint8_t *bank)
371 OUTB_DELAY(bank + 0x555, 0xaa);
373 OUTB_DELAY(bank + 0x2aa, 0x55);
376 OUTB_DELAY(bank + 0x555, 0x80);
377 OUTB_DELAY(bank + 0x555, 0xaa);
378 OUTB_DELAY(bank + 0x2aa, 0x55);
379 OUTB_DELAY(bank, 0x30);
381 if (wait_operation_complete_amd(bank) != 0) {
382 device_printf(dev, "flash erase timeout\n");
387 OUTB_DELAY(bank, 0xf0);
389 for (i = 0; i < NVRAM_SIZE; i++) {
390 if (bank[i] != 0xff) {
391 device_printf(dev, "flash erase has failed\n");
399 write_bank_amd(device_t dev, uint8_t *bank, uint8_t *data)
403 for (i = 0; i < NVRAM_SIZE; i++) {
405 OUTB_DELAY(bank + 0x555, 0xaa);
407 OUTB_DELAY(bank + 0x2aa, 0x55);
409 /* Write single word */
410 OUTB_DELAY(bank + 0x555, 0xa0);
411 OUTB_DELAY(bank + i, data[i]);
412 if (wait_operation_complete_amd(bank) != 0) {
413 device_printf(dev, "flash write timeout\n");
419 OUTB_DELAY(bank, 0xf0);
421 for (i = 0; i < NVRAM_SIZE; i++) {
422 if (bank[i] != data[i]) {
423 device_printf(dev, "flash write has failed\n");
431 wait_operation_complete_sm(uint8_t *bank)
435 for (i = 1000000; i != 0; i--) {
436 outb(bank, SM_FLASH_CMD_READ_STATUS);
437 if (inb(bank) & SM_FLASH_STATUS_DONE)
444 erase_bank_sm(device_t dev, uint8_t *bank)
448 outb(bank, SM_FLASH_CMD_ERASE_SETUP);
449 outb(bank, SM_FLASH_CMD_ERASE_CONFIRM);
451 if (wait_operation_complete_sm(bank) != 0) {
452 device_printf(dev, "flash erase timeout\n");
456 outb(bank, SM_FLASH_CMD_CLEAR_STATUS);
457 outb(bank, SM_FLASH_CMD_RESET);
459 for (i = 0; i < NVRAM_SIZE; i++) {
460 if (bank[i] != 0xff) {
461 device_printf(dev, "flash write has failed\n");
469 write_bank_sm(device_t dev, uint8_t *bank, uint8_t *data)
473 for (i = 0; i < NVRAM_SIZE; i++) {
474 OUTB_DELAY(bank + i, SM_FLASH_CMD_WRITE_SETUP);
475 outb(bank + i, data[i]);
476 if (wait_operation_complete_sm(bank) != 0) {
477 device_printf(dev, "flash write error/timeout\n");
482 outb(bank, SM_FLASH_CMD_CLEAR_STATUS);
483 outb(bank, SM_FLASH_CMD_RESET);
485 for (i = 0; i < NVRAM_SIZE; i++) {
486 if (bank[i] != data[i]) {
487 device_printf(dev, "flash write has failed\n");
495 erase_bank(device_t dev, uint8_t *bank)
497 struct powermac_nvram_softc *sc;
499 sc = device_get_softc(dev);
500 if (sc->sc_type == FLASH_TYPE_AMD)
501 return (erase_bank_amd(dev, bank));
503 return (erase_bank_sm(dev, bank));
507 write_bank(device_t dev, uint8_t *bank, uint8_t *data)
509 struct powermac_nvram_softc *sc;
511 sc = device_get_softc(dev);
512 if (sc->sc_type == FLASH_TYPE_AMD)
513 return (write_bank_amd(dev, bank, data));
515 return (write_bank_sm(dev, bank, data));