]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/powermac_nvram/powermac_nvram.c
sysctl(9): Fix a few mandoc related issues
[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         { 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 }