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