xref: /freebsd/sys/dev/powermac_nvram/powermac_nvram.c (revision fcb560670601b2a4d87bb31d7531c8dcc37ee71b)
1 /*
2  * Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/module.h>
32 #include <sys/bus.h>
33 #include <sys/conf.h>
34 #include <sys/kernel.h>
35 #include <sys/uio.h>
36 
37 #include <dev/ofw/openfirm.h>
38 #include <dev/ofw/ofw_bus.h>
39 #include <dev/ofw/ofw_bus_subr.h>
40 
41 #include <machine/bus.h>
42 #include <machine/md_var.h>
43 #include <machine/pio.h>
44 #include <machine/resource.h>
45 
46 #include <sys/rman.h>
47 
48 #include <dev/powermac_nvram/powermac_nvramvar.h>
49 
50 #include <vm/vm.h>
51 #include <vm/pmap.h>
52 
53 /*
54  * Device interface.
55  */
56 static int		powermac_nvram_probe(device_t);
57 static int		powermac_nvram_attach(device_t);
58 static int		powermac_nvram_detach(device_t);
59 
60 /* Helper functions */
61 static int		powermac_nvram_check(void *data);
62 static int		chrp_checksum(int sum, uint8_t *, uint8_t *);
63 static uint32_t		adler_checksum(uint8_t *, int);
64 static int		erase_bank(device_t, uint8_t *);
65 static int		write_bank(device_t, uint8_t *, uint8_t *);
66 
67 /*
68  * Driver methods.
69  */
70 static device_method_t	powermac_nvram_methods[] = {
71 	/* Device interface */
72 	DEVMETHOD(device_probe,		powermac_nvram_probe),
73 	DEVMETHOD(device_attach,	powermac_nvram_attach),
74 	DEVMETHOD(device_detach,	powermac_nvram_detach),
75 
76 	{ 0, 0 }
77 };
78 
79 static driver_t	powermac_nvram_driver = {
80 	"powermac_nvram",
81 	powermac_nvram_methods,
82 	sizeof(struct powermac_nvram_softc)
83 };
84 
85 static devclass_t powermac_nvram_devclass;
86 
87 DRIVER_MODULE(powermac_nvram, ofwbus, powermac_nvram_driver, powermac_nvram_devclass, 0, 0);
88 
89 /*
90  * Cdev methods.
91  */
92 
93 static	d_open_t	powermac_nvram_open;
94 static	d_close_t	powermac_nvram_close;
95 static	d_read_t	powermac_nvram_read;
96 static	d_write_t	powermac_nvram_write;
97 
98 static struct cdevsw powermac_nvram_cdevsw = {
99 	.d_version =	D_VERSION,
100 	.d_flags =	D_NEEDGIANT,
101 	.d_open =	powermac_nvram_open,
102 	.d_close =	powermac_nvram_close,
103 	.d_read =	powermac_nvram_read,
104 	.d_write =	powermac_nvram_write,
105 	.d_name =	"powermac_nvram",
106 };
107 
108 static int
109 powermac_nvram_probe(device_t dev)
110 {
111 	const char	*type, *compatible;
112 
113 	type = ofw_bus_get_type(dev);
114 	compatible = ofw_bus_get_compat(dev);
115 
116 	if (type == NULL || compatible == NULL)
117 		return ENXIO;
118 
119 	if (strcmp(type, "nvram") != 0)
120 		return ENXIO;
121 	if (strcmp(compatible, "amd-0137") != 0 &&
122 	    !ofw_bus_is_compatible(dev, "nvram,flash"))
123 		return ENXIO;
124 
125 	device_set_desc(dev, "Apple NVRAM");
126 	return 0;
127 }
128 
129 static int
130 powermac_nvram_attach(device_t dev)
131 {
132 	struct powermac_nvram_softc *sc;
133 	const char	*compatible;
134 	phandle_t node;
135 	u_int32_t reg[3];
136 	int gen0, gen1, i;
137 
138 	node = ofw_bus_get_node(dev);
139 	sc = device_get_softc(dev);
140 
141 	if ((i = OF_getprop(node, "reg", reg, sizeof(reg))) < 8)
142 		return ENXIO;
143 
144 	sc->sc_dev = dev;
145 	sc->sc_node = node;
146 
147 	compatible = ofw_bus_get_compat(dev);
148 	if (strcmp(compatible, "amd-0137") == 0)
149 		sc->sc_type = FLASH_TYPE_AMD;
150 	else
151 		sc->sc_type = FLASH_TYPE_SM;
152 
153 	/*
154 	 * Find which byte of reg corresponds to the 32-bit physical address.
155 	 * We should probably read #address-cells from /chosen instead.
156 	 */
157 	i = (i/4) - 2;
158 
159 	sc->sc_bank0 = (vm_offset_t)pmap_mapdev(reg[i], NVRAM_SIZE * 2);
160 	sc->sc_bank1 = sc->sc_bank0 + NVRAM_SIZE;
161 
162 	gen0 = powermac_nvram_check((void *)sc->sc_bank0);
163 	gen1 = powermac_nvram_check((void *)sc->sc_bank1);
164 
165 	if (gen0 == -1 && gen1 == -1) {
166 		if ((void *)sc->sc_bank0 != NULL)
167 			pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
168 		device_printf(dev, "both banks appear to be corrupt\n");
169 		return ENXIO;
170 	}
171 	device_printf(dev, "bank0 generation %d, bank1 generation %d\n",
172 	    gen0, gen1);
173 
174 	sc->sc_bank = (gen0 > gen1) ? sc->sc_bank0 : sc->sc_bank1;
175 	bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE);
176 
177 	sc->sc_cdev = make_dev(&powermac_nvram_cdevsw, 0, 0, 0, 0600,
178 	    "powermac_nvram");
179 	sc->sc_cdev->si_drv1 = sc;
180 
181 	return 0;
182 }
183 
184 static int
185 powermac_nvram_detach(device_t dev)
186 {
187 	struct powermac_nvram_softc *sc;
188 
189 	sc = device_get_softc(dev);
190 
191 	if ((void *)sc->sc_bank0 != NULL)
192 		pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
193 
194 	if (sc->sc_cdev != NULL)
195 		destroy_dev(sc->sc_cdev);
196 
197 	return 0;
198 }
199 
200 static int
201 powermac_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td)
202 {
203 	struct powermac_nvram_softc *sc = dev->si_drv1;
204 
205 	if (sc->sc_isopen)
206 		return EBUSY;
207 	sc->sc_isopen = 1;
208 	sc->sc_rpos = sc->sc_wpos = 0;
209 	return 0;
210 }
211 
212 static int
213 powermac_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
214 {
215 	struct powermac_nvram_softc *sc = dev->si_drv1;
216 	struct core99_header *header;
217 	vm_offset_t bank;
218 
219 	if (sc->sc_wpos != sizeof(sc->sc_data)) {
220 		/* Short write, restore in-memory copy */
221 		bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE);
222 		sc->sc_isopen = 0;
223 		return 0;
224 	}
225 
226 	header = (struct core99_header *)sc->sc_data;
227 
228 	header->generation = ((struct core99_header *)sc->sc_bank)->generation;
229 	header->generation++;
230 	header->chrp_header.signature = CORE99_SIGNATURE;
231 
232 	header->adler_checksum =
233 	    adler_checksum((uint8_t *)&(header->generation),
234 	    NVRAM_SIZE - offsetof(struct core99_header, generation));
235 	header->chrp_header.chrp_checksum = chrp_checksum(header->chrp_header.signature,
236 	    (uint8_t *)&(header->chrp_header.length),
237 	    (uint8_t *)&(header->adler_checksum));
238 
239 	bank = (sc->sc_bank == sc->sc_bank0) ? sc->sc_bank1 : sc->sc_bank0;
240 	if (erase_bank(sc->sc_dev, (uint8_t *)bank) != 0 ||
241 	    write_bank(sc->sc_dev, (uint8_t *)bank, sc->sc_data) != 0) {
242 		sc->sc_isopen = 0;
243 		return ENOSPC;
244 	}
245 	sc->sc_bank = bank;
246 	sc->sc_isopen = 0;
247 	return 0;
248 }
249 
250 static int
251 powermac_nvram_read(struct cdev *dev, struct uio *uio, int ioflag)
252 {
253 	int rv, amnt, data_available;
254 	struct powermac_nvram_softc *sc = dev->si_drv1;
255 
256 	rv = 0;
257 	while (uio->uio_resid > 0) {
258 		data_available = sizeof(sc->sc_data) - sc->sc_rpos;
259 		if (data_available > 0) {
260 			amnt = MIN(uio->uio_resid, data_available);
261 			rv = uiomove((void *)(sc->sc_data + sc->sc_rpos),
262 			    amnt, uio);
263 			if (rv != 0)
264 				break;
265 			sc->sc_rpos += amnt;
266 		} else {
267 			break;
268 		}
269 	}
270 	return rv;
271 }
272 
273 static int
274 powermac_nvram_write(struct cdev *dev, struct uio *uio, int ioflag)
275 {
276 	int rv, amnt, data_available;
277 	struct powermac_nvram_softc *sc = dev->si_drv1;
278 
279 	if (sc->sc_wpos >= sizeof(sc->sc_data))
280 		return EINVAL;
281 
282 	rv = 0;
283 	while (uio->uio_resid > 0) {
284 		data_available = sizeof(sc->sc_data) - sc->sc_wpos;
285 		if (data_available > 0) {
286 			amnt = MIN(uio->uio_resid, data_available);
287 			rv = uiomove((void *)(sc->sc_data + sc->sc_wpos),
288 			    amnt, uio);
289 			if (rv != 0)
290 				break;
291 			sc->sc_wpos += amnt;
292 		} else {
293 			break;
294 		}
295 	}
296 	return rv;
297 }
298 
299 static int
300 powermac_nvram_check(void *data)
301 {
302 	struct core99_header *header;
303 
304 	header = (struct core99_header *)data;
305 
306 	if (header->chrp_header.signature != CORE99_SIGNATURE)
307 		return -1;
308 	if (header->chrp_header.chrp_checksum !=
309 	    chrp_checksum(header->chrp_header.signature,
310 	    (uint8_t *)&(header->chrp_header.length),
311 	    (uint8_t *)&(header->adler_checksum)))
312 		return -1;
313 	if (header->adler_checksum !=
314 	    adler_checksum((uint8_t *)&(header->generation),
315 	    NVRAM_SIZE - offsetof(struct core99_header, generation)))
316 		return -1;
317 	return header->generation;
318 }
319 
320 static int
321 chrp_checksum(int sum, uint8_t *data, uint8_t *end)
322 {
323 
324 	for (; data < end; data++)
325 		sum += data[0];
326 	while (sum > 0xff)
327 		sum = (sum & 0xff) + (sum >> 8);
328 	return sum;
329 }
330 
331 static uint32_t
332 adler_checksum(uint8_t *data, int len)
333 {
334 	uint32_t low, high;
335 	int i;
336 
337 	low = 1;
338 	high = 0;
339 	for (i = 0; i < len; i++) {
340 		if ((i % 5000) == 0) {
341 			high %= 65521UL;
342 			high %= 65521UL;
343 		}
344 		low += data[i];
345 		high += low;
346 	}
347 	low %= 65521UL;
348 	high %= 65521UL;
349 
350 	return (high << 16) | low;
351 }
352 
353 #define	OUTB_DELAY(a, v)	outb(a, v); DELAY(1);
354 
355 static int
356 wait_operation_complete_amd(uint8_t *bank)
357 {
358 	int i;
359 
360 	for (i = 1000000; i != 0; i--)
361 		if ((inb(bank) ^ inb(bank)) == 0)
362 			return 0;
363 	return -1;
364 }
365 
366 static int
367 erase_bank_amd(device_t dev, uint8_t *bank)
368 {
369 	unsigned int i;
370 
371 	/* Unlock 1 */
372 	OUTB_DELAY(bank + 0x555, 0xaa);
373 	/* Unlock 2 */
374 	OUTB_DELAY(bank + 0x2aa, 0x55);
375 
376 	/* Sector-Erase */
377 	OUTB_DELAY(bank + 0x555, 0x80);
378 	OUTB_DELAY(bank + 0x555, 0xaa);
379 	OUTB_DELAY(bank + 0x2aa, 0x55);
380 	OUTB_DELAY(bank, 0x30);
381 
382 	if (wait_operation_complete_amd(bank) != 0) {
383 		device_printf(dev, "flash erase timeout\n");
384 		return -1;
385 	}
386 
387 	/* Reset */
388 	OUTB_DELAY(bank, 0xf0);
389 
390 	for (i = 0; i < NVRAM_SIZE; i++) {
391 		if (bank[i] != 0xff) {
392 			device_printf(dev, "flash erase has failed\n");
393 			return -1;
394 		}
395 	}
396 	return 0;
397 }
398 
399 static int
400 write_bank_amd(device_t dev, uint8_t *bank, uint8_t *data)
401 {
402 	unsigned int i;
403 
404 	for (i = 0; i < NVRAM_SIZE; i++) {
405 		/* Unlock 1 */
406 		OUTB_DELAY(bank + 0x555, 0xaa);
407 		/* Unlock 2 */
408 		OUTB_DELAY(bank + 0x2aa, 0x55);
409 
410 		/* Write single word */
411 		OUTB_DELAY(bank + 0x555, 0xa0);
412 		OUTB_DELAY(bank + i, data[i]);
413 		if (wait_operation_complete_amd(bank) != 0) {
414 			device_printf(dev, "flash write timeout\n");
415 			return -1;
416 		}
417 	}
418 
419 	/* Reset */
420 	OUTB_DELAY(bank, 0xf0);
421 
422 	for (i = 0; i < NVRAM_SIZE; i++) {
423 		if (bank[i] != data[i]) {
424 			device_printf(dev, "flash write has failed\n");
425 			return -1;
426 		}
427 	}
428 	return 0;
429 }
430 
431 static int
432 wait_operation_complete_sm(uint8_t *bank)
433 {
434 	int i;
435 
436 	for (i = 1000000; i != 0; i--) {
437 		outb(bank, SM_FLASH_CMD_READ_STATUS);
438 		if (inb(bank) & SM_FLASH_STATUS_DONE)
439 			return (0);
440 	}
441 	return (-1);
442 }
443 
444 static int
445 erase_bank_sm(device_t dev, uint8_t *bank)
446 {
447 	unsigned int i;
448 
449 	outb(bank, SM_FLASH_CMD_ERASE_SETUP);
450 	outb(bank, SM_FLASH_CMD_ERASE_CONFIRM);
451 
452 	if (wait_operation_complete_sm(bank) != 0) {
453 		device_printf(dev, "flash erase timeout\n");
454 		return (-1);
455 	}
456 
457 	outb(bank, SM_FLASH_CMD_CLEAR_STATUS);
458 	outb(bank, SM_FLASH_CMD_RESET);
459 
460 	for (i = 0; i < NVRAM_SIZE; i++) {
461 		if (bank[i] != 0xff) {
462 			device_printf(dev, "flash write has failed\n");
463 			return (-1);
464 		}
465 	}
466 	return (0);
467 }
468 
469 static int
470 write_bank_sm(device_t dev, uint8_t *bank, uint8_t *data)
471 {
472 	unsigned int i;
473 
474 	for (i = 0; i < NVRAM_SIZE; i++) {
475 		OUTB_DELAY(bank + i, SM_FLASH_CMD_WRITE_SETUP);
476 		outb(bank + i, data[i]);
477 		if (wait_operation_complete_sm(bank) != 0) {
478 			device_printf(dev, "flash write error/timeout\n");
479 			break;
480 		}
481 	}
482 
483 	outb(bank, SM_FLASH_CMD_CLEAR_STATUS);
484 	outb(bank, SM_FLASH_CMD_RESET);
485 
486 	for (i = 0; i < NVRAM_SIZE; i++) {
487 		if (bank[i] != data[i]) {
488 			device_printf(dev, "flash write has failed\n");
489 			return (-1);
490 		}
491 	}
492 	return (0);
493 }
494 
495 static int
496 erase_bank(device_t dev, uint8_t *bank)
497 {
498 	struct powermac_nvram_softc *sc;
499 
500 	sc = device_get_softc(dev);
501 	if (sc->sc_type == FLASH_TYPE_AMD)
502 		return (erase_bank_amd(dev, bank));
503 	else
504 		return (erase_bank_sm(dev, bank));
505 }
506 
507 static int
508 write_bank(device_t dev, uint8_t *bank, uint8_t *data)
509 {
510 	struct powermac_nvram_softc *sc;
511 
512 	sc = device_get_softc(dev);
513 	if (sc->sc_type == FLASH_TYPE_AMD)
514 		return (write_bank_amd(dev, bank, data));
515 	else
516 		return (write_bank_sm(dev, bank, data));
517 }
518