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