1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2006 Maxim Sobolev <[email protected]>
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
powermac_nvram_probe(device_t dev)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
powermac_nvram_attach(device_t dev)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
powermac_nvram_detach(device_t dev)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
powermac_nvram_open(struct cdev * dev,int flags,int fmt,struct thread * td)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
powermac_nvram_close(struct cdev * dev,int fflag,int devtype,struct thread * td)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
powermac_nvram_read(struct cdev * dev,struct uio * uio,int ioflag)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
powermac_nvram_write(struct cdev * dev,struct uio * uio,int ioflag)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
powermac_nvram_check(void * data)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
chrp_checksum(int sum,uint8_t * data,uint8_t * end)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
adler_checksum(uint8_t * data,int len)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
wait_operation_complete_amd(uint8_t * bank)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
erase_bank_amd(device_t dev,uint8_t * bank)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
write_bank_amd(device_t dev,uint8_t * bank,uint8_t * data)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
wait_operation_complete_sm(uint8_t * bank)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
erase_bank_sm(device_t dev,uint8_t * bank)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
write_bank_sm(device_t dev,uint8_t * bank,uint8_t * data)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
erase_bank(device_t dev,uint8_t * bank)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
write_bank(device_t dev,uint8_t * bank,uint8_t * data)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