]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/powermac_nvram/powermac_nvram.c
Replace all calls to minor() with dev2unit().
[FreeBSD/FreeBSD.git] / sys / dev / powermac_nvram / powermac_nvram.c
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_pci.h>
39
40 #include <machine/bus.h>
41 #include <machine/md_var.h>
42 #include <machine/nexusvar.h>
43 #include <machine/pio.h>
44 #include <machine/resource.h>
45
46 #include <sys/rman.h>
47
48 #include <powerpc/ofw/ofw_pci.h>
49 #include <dev/powermac_nvram/powermac_nvramvar.h>
50
51 #include <vm/vm.h>
52 #include <vm/pmap.h>
53
54 /*
55  * Device interface.
56  */
57 static int              powermac_nvram_probe(device_t);
58 static int              powermac_nvram_attach(device_t);
59 static int              powermac_nvram_detach(device_t);
60
61 /* Helper functions */
62 static int              powermac_nvram_check(void *data);
63 static int              chrp_checksum(int sum, uint8_t *, uint8_t *);
64 static uint32_t         adler_checksum(uint8_t *, int);
65 static int              erase_bank(device_t, uint8_t *);
66 static int              write_bank(device_t, uint8_t *, uint8_t *);
67
68 /*
69  * Driver methods.
70  */
71 static device_method_t  powermac_nvram_methods[] = {
72         /* Device interface */
73         DEVMETHOD(device_probe,         powermac_nvram_probe),
74         DEVMETHOD(device_attach,        powermac_nvram_attach),
75         DEVMETHOD(device_detach,        powermac_nvram_detach),
76
77         { 0, 0 }
78 };
79
80 static driver_t powermac_nvram_driver = {
81         "powermac_nvram",
82         powermac_nvram_methods,
83         sizeof(struct powermac_nvram_softc)
84 };
85
86 static devclass_t powermac_nvram_devclass;
87
88 DRIVER_MODULE(powermac_nvram, nexus, powermac_nvram_driver, powermac_nvram_devclass, 0, 0);
89
90 /*
91  * Cdev methods.
92  */
93
94 #define NVRAM_UNIT(dev)         dev2unit(dev)
95 #define NVRAM_SOFTC(unit)       ((struct powermac_nvram_softc *) \
96      devclass_get_softc(powermac_nvram_devclass, unit))
97
98 static  d_open_t        powermac_nvram_open;
99 static  d_close_t       powermac_nvram_close;
100 static  d_read_t        powermac_nvram_read;
101 static  d_write_t       powermac_nvram_write;
102
103 static struct cdevsw powermac_nvram_cdevsw = {
104         .d_version =    D_VERSION,
105         .d_flags =      D_NEEDGIANT,
106         .d_open =       powermac_nvram_open,
107         .d_close =      powermac_nvram_close,
108         .d_read =       powermac_nvram_read,
109         .d_write =      powermac_nvram_write,
110         .d_name =       "powermac_nvram",
111 };
112
113 static int
114 powermac_nvram_probe(device_t dev)
115 {
116         char    *type, *compatible;
117
118         type = nexus_get_device_type(dev);
119         compatible = nexus_get_compatible(dev);
120
121         if (type == NULL || compatible == NULL)
122                 return ENXIO;
123
124         if (strcmp(type, "nvram") != 0 || strcmp(compatible, "amd-0137") != 0)
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         phandle_t node;
136         u_int32_t reg[2];
137         int gen0, gen1;
138
139         node = nexus_get_node(dev);
140         sc = device_get_softc(dev);
141
142         if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8)
143                 return ENXIO;
144
145         sc->sc_dev = dev;
146         sc->sc_node = node;
147
148         sc->sc_bank0 = (vm_offset_t)pmap_mapdev(reg[0], NVRAM_SIZE * 2);
149         sc->sc_bank1 = sc->sc_bank0 + NVRAM_SIZE;
150
151         gen0 = powermac_nvram_check((void *)sc->sc_bank0);
152         gen1 = powermac_nvram_check((void *)sc->sc_bank1);
153
154         if (gen0 == -1 && gen1 == -1) {
155                 if ((void *)sc->sc_bank0 != NULL)
156                         pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
157                 device_printf(dev, "both banks appear to be corrupt\n");
158                 return ENXIO;
159         }
160         device_printf(dev, "bank0 generation %d, bank1 generation %d\n",
161             gen0, gen1);
162
163         sc->sc_bank = (gen0 > gen1) ? sc->sc_bank0 : sc->sc_bank1;
164         bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE);
165
166         sc->sc_cdev = make_dev(&powermac_nvram_cdevsw, 0, 0, 0, 0600,
167             "powermac_nvram");
168
169         return 0;
170 }
171
172 static int
173 powermac_nvram_detach(device_t dev)
174 {
175         struct powermac_nvram_softc *sc;
176
177         sc = device_get_softc(dev);
178
179         if ((void *)sc->sc_bank0 != NULL)
180                 pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
181
182         if (sc->sc_cdev != NULL)
183                 destroy_dev(sc->sc_cdev);
184         
185         return 0;
186 }
187
188 static int
189 powermac_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td)
190 {
191         struct powermac_nvram_softc *sc;
192
193         sc = NVRAM_SOFTC(NVRAM_UNIT(dev));
194         if (sc->sc_isopen)
195                 return EBUSY;
196         sc->sc_isopen = 1;
197         sc->sc_rpos = sc->sc_wpos = 0;
198         return 0;
199 }
200
201 static int
202 powermac_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
203 {
204         struct powermac_nvram_softc *sc;
205         struct core99_header *header;
206         vm_offset_t bank;
207
208         sc = NVRAM_SOFTC(NVRAM_UNIT(dev));
209
210         if (sc->sc_wpos != sizeof(sc->sc_data)) {
211                 /* Short write, restore in-memory copy */
212                 bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE);
213                 sc->sc_isopen = 0;
214                 return 0;
215         }
216
217         header = (struct core99_header *)sc->sc_data;
218
219         header->generation = ((struct core99_header *)sc->sc_bank)->generation;
220         header->generation++;
221         header->chrp_header.signature = CORE99_SIGNATURE;
222
223         header->adler_checksum =
224             adler_checksum((uint8_t *)&(header->generation),
225             NVRAM_SIZE - offsetof(struct core99_header, generation));
226         header->chrp_header.chrp_checksum = chrp_checksum(header->chrp_header.signature,
227             (uint8_t *)&(header->chrp_header.length),
228             (uint8_t *)&(header->adler_checksum));
229
230         bank = (sc->sc_bank == sc->sc_bank0) ? sc->sc_bank1 : sc->sc_bank0;
231         if (erase_bank(sc->sc_dev, (uint8_t *)bank) != 0 ||
232             write_bank(sc->sc_dev, (uint8_t *)bank, sc->sc_data) != 0) {
233                 sc->sc_isopen = 0;
234                 return ENOSPC;
235         }
236         sc->sc_bank = bank;
237         sc->sc_isopen = 0;
238         return 0;
239 }
240
241 static int
242 powermac_nvram_read(struct cdev *dev, struct uio *uio, int ioflag)
243 {
244         int rv, amnt, data_available;
245         struct powermac_nvram_softc *sc;
246
247         sc = NVRAM_SOFTC(NVRAM_UNIT(dev));
248
249         rv = 0;
250         while (uio->uio_resid > 0) {
251                 data_available = sizeof(sc->sc_data) - sc->sc_rpos;
252                 if (data_available > 0) {
253                         amnt = MIN(uio->uio_resid, data_available);
254                         rv = uiomove((void *)(sc->sc_data + sc->sc_rpos),
255                             amnt, uio);
256                         if (rv != 0)
257                                 break;
258                         sc->sc_rpos += amnt;
259                 } else {
260                         break;
261                 }
262         }
263         return rv;
264 }
265
266 static int
267 powermac_nvram_write(struct cdev *dev, struct uio *uio, int ioflag)
268 {
269         int rv, amnt, data_available;
270         struct powermac_nvram_softc *sc;
271
272         sc = NVRAM_SOFTC(NVRAM_UNIT(dev));
273
274         if (sc->sc_wpos >= sizeof(sc->sc_data))
275                 return EINVAL;
276
277         rv = 0;
278         while (uio->uio_resid > 0) {
279                 data_available = sizeof(sc->sc_data) - sc->sc_wpos;
280                 if (data_available > 0) {
281                         amnt = MIN(uio->uio_resid, data_available);
282                         rv = uiomove((void *)(sc->sc_data + sc->sc_wpos),
283                             amnt, uio);
284                         if (rv != 0)
285                                 break;
286                         sc->sc_wpos += amnt;
287                 } else {
288                         break;
289                 }
290         }
291         return rv;
292 }
293
294 static int
295 powermac_nvram_check(void *data)
296 {
297         struct core99_header *header;
298
299         header = (struct core99_header *)data;
300
301         if (header->chrp_header.signature != CORE99_SIGNATURE)
302                 return -1;
303         if (header->chrp_header.chrp_checksum !=
304             chrp_checksum(header->chrp_header.signature,
305             (uint8_t *)&(header->chrp_header.length),
306             (uint8_t *)&(header->adler_checksum)))
307                 return -1;
308         if (header->adler_checksum !=
309             adler_checksum((uint8_t *)&(header->generation),
310             NVRAM_SIZE - offsetof(struct core99_header, generation)))
311                 return -1;
312         return header->generation;
313 }
314
315 static int
316 chrp_checksum(int sum, uint8_t *data, uint8_t *end)
317 {
318
319         for (; data < end; data++)
320                 sum += data[0];
321         while (sum > 0xff)
322                 sum = (sum & 0xff) + (sum >> 8);
323         return sum;
324 }
325
326 static uint32_t
327 adler_checksum(uint8_t *data, int len)
328 {
329         uint32_t low, high;
330         int i;
331
332         low = 1;
333         high = 0;
334         for (i = 0; i < len; i++) {
335                 if ((i % 5000) == 0) {
336                         high %= 65521UL;
337                         high %= 65521UL;
338                 }
339                 low += data[i];
340                 high += low;
341         }
342         low %= 65521UL;
343         high %= 65521UL;
344
345         return (high << 16) | low;
346 }
347
348 #define OUTB_DELAY(a, v)        outb(a, v); DELAY(1);
349
350 static int
351 wait_operation_complete(uint8_t *bank)
352 {
353         int i;
354
355         for (i = 1000000; i != 0; i--)
356                 if ((inb(bank) ^ inb(bank)) == 0)
357                         return 0;
358         return -1;
359 }
360
361 static int
362 erase_bank(device_t dev, uint8_t *bank)
363 {
364         unsigned int i;
365
366         /* Unlock 1 */
367         OUTB_DELAY(bank + 0x555, 0xaa);
368         /* Unlock 2 */
369         OUTB_DELAY(bank + 0x2aa, 0x55);
370
371         /* Sector-Erase */
372         OUTB_DELAY(bank + 0x555, 0x80);
373         OUTB_DELAY(bank + 0x555, 0xaa);
374         OUTB_DELAY(bank + 0x2aa, 0x55);
375         OUTB_DELAY(bank, 0x30);
376
377         if (wait_operation_complete(bank) != 0) {
378                 device_printf(dev, "flash erase timeout\n");
379                 return -1;
380         }
381
382         /* Reset */
383         OUTB_DELAY(bank, 0xf0);
384
385         for (i = 0; i < NVRAM_SIZE; i++) {
386                 if (bank[i] != 0xff) {
387                         device_printf(dev, "flash erase has failed\n");
388                         return -1;
389                 }
390         }
391         return 0;
392 }
393
394 static int
395 write_bank(device_t dev, uint8_t *bank, uint8_t *data)
396 {
397         unsigned int i;
398
399         for (i = 0; i < NVRAM_SIZE; i++) {
400                 /* Unlock 1 */
401                 OUTB_DELAY(bank + 0x555, 0xaa);
402                 /* Unlock 2 */
403                 OUTB_DELAY(bank + 0x2aa, 0x55);
404
405                 /* Write single word */
406                 OUTB_DELAY(bank + 0x555, 0xa0);
407                 OUTB_DELAY(bank + i, data[i]);
408                 if (wait_operation_complete(bank) != 0) {
409                         device_printf(dev, "flash write timeout\n");
410                         return -1;
411                 }
412         }
413
414         /* Reset */
415         OUTB_DELAY(bank, 0xf0);
416
417         for (i = 0; i < NVRAM_SIZE; i++) {
418                 if (bank[i] != data[i]) {
419                         device_printf(dev, "flash write has failed\n");
420                         return -1;
421                 }
422         }
423         return 0;
424 }