athflash.c revision 1.6 1 /* $NetBSD: athflash.c,v 1.6 2012/10/27 17:18:02 chs Exp $ */
2
3 /*
4 * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
5 * Copyright (c) 2006 Garrett D'Amore.
6 * All rights reserved.
7 *
8 * Portions of this code were written by Garrett D'Amore for the
9 * Champaign-Urbana Community Wireless Network Project.
10 *
11 * Redistribution and use in source and binary forms, with or
12 * without modification, are permitted provided that the following
13 * conditions are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided
19 * with the distribution.
20 * 3. All advertising materials mentioning features or use of this
21 * software must display the following acknowledgements:
22 * This product includes software developed by the Urbana-Champaign
23 * Independent Media Center.
24 * This product includes software developed by Garrett D'Amore.
25 * 4. Urbana-Champaign Independent Media Center's name and Garrett
26 * D'Amore's name may not be used to endorse or promote products
27 * derived from this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
30 * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR
31 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
34 * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT,
35 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43 /*
44 * Copyright (c) 2002 The NetBSD Foundation, Inc.
45 * All rights reserved.
46 *
47 * This code is derived from software contributed to The NetBSD Foundation
48 * by Naoto Shimazaki of YOKOGAWA Electric Corporation.
49 *
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 * 1. Redistributions of source code must retain the above copyright
54 * notice, this list of conditions and the following disclaimer.
55 * 2. Redistributions in binary form must reproduce the above copyright
56 * notice, this list of conditions and the following disclaimer in the
57 * documentation and/or other materials provided with the distribution.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
60 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
61 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
62 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
63 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
64 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
65 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
66 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
67 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
68 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
69 * POSSIBILITY OF SUCH DAMAGE.
70 */
71
72 /*
73 * Flash Memory Driver
74 *
75 * XXX This primitive flash driver does *not* support boot sectored devices,
76 * XXX and only supports a fairly limited set of devices, that we are likely to
77 * XXX to find in an AP30.
78 * XXX
79 * XXX We also are only supporting flash widths of 16 _for the moment_, and
80 * XXX we are only supporting flash devices that use the AMD command sets.
81 * XXX All this should be reviewed and improved to be much more generic.
82 */
83
84 #include <sys/cdefs.h>
85 __KERNEL_RCSID(0, "$NetBSD: athflash.c,v 1.6 2012/10/27 17:18:02 chs Exp $");
86
87 #include <sys/param.h>
88 #include <sys/conf.h>
89 #include <sys/device.h>
90 #include <sys/kernel.h>
91 #include <sys/malloc.h>
92 #include <sys/proc.h>
93 #include <sys/systm.h>
94
95 #include <sys/bus.h>
96
97 #include <mips/atheros/include/arbusvar.h>
98
99 #ifdef FLASH_DEBUG
100 int flash_debug = 0;
101 #define DPRINTF(x) if (flash_debug) printf x
102 #else
103 #define DPRINTF(x)
104 #endif
105
106 struct flash_softc {
107 bus_space_tag_t sc_iot;
108 bus_space_handle_t sc_ioh;
109 size_t sc_size;
110 size_t sc_sector_size;
111 int sc_status;
112 u_int8_t *sc_buf;
113 };
114
115 #define FLASH_ST_BUSY 0x1
116
117 static int flash_probe(device_t, cfdata_t, void *);
118 static void flash_attach(device_t, device_t, void *);
119
120 static int is_block_same(struct flash_softc *, bus_size_t, const void *);
121 static int toggle_bit_wait(struct flash_softc *, bus_size_t, int, int, int);
122
123 static int flash_sector_erase(struct flash_softc *, bus_size_t);
124 static int flash_sector_write(struct flash_softc *, bus_size_t);
125
126 extern struct cfdriver athflash_cd;
127
128 CFATTACH_DECL_NEW(athflash, sizeof(struct flash_softc),
129 flash_probe, flash_attach, NULL, NULL);
130
131 dev_type_open(flashopen);
132 dev_type_close(flashclose);
133 dev_type_read(flashread);
134 dev_type_write(flashwrite);
135
136 const struct cdevsw athflash_cdevsw = {
137 flashopen, flashclose, flashread, flashwrite, noioctl,
138 nostop, notty, nopoll, nommap, nokqfilter,
139 };
140
141 static struct {
142 uint16_t vendor_id;
143 uint16_t device_id;
144 const char *name;
145 int sector_size;
146 int flash_size;
147 } flash_ids[] = {
148 { 0x00bf, 0x2780, "SST 39VF400", 0x01000, 0x080000 }, /* 512KB */
149 { 0x00bf, 0x2782, "SST 39VF160", 0x01000, 0x200000 }, /* 2MB */
150 { 0xffff, 0xffff, NULL, 0, 0 } /* end list */
151 };
152
153 static int
154 flash_probe(device_t parent, cfdata_t cf, void *aux)
155 {
156 struct arbus_attach_args *aa = aux;
157 bus_space_handle_t ioh;
158 int rv = 0, i;
159 uint16_t venid, devid;
160
161 if (strcmp(aa->aa_name, cf->cf_name) != 0)
162 return 0;
163
164 DPRINTF(("trying to map address %x\n", (unsigned)aa->aa_addr));
165 if (bus_space_map(aa->aa_bst, aa->aa_addr, aa->aa_size, 0, &ioh))
166 return 0;
167
168 /* issue JEDEC query */
169 DPRINTF(("issuing JEDEC query\n"));
170 bus_space_write_2(aa->aa_bst, ioh, (0x5555 << 1), 0xAAAA);
171 bus_space_write_2(aa->aa_bst, ioh, (0x2AAA << 1), 0x5555);
172 bus_space_write_2(aa->aa_bst, ioh, (0x5555 << 1), 0x9090);
173
174 delay(100);
175 venid = bus_space_read_2(aa->aa_bst, ioh, 0);
176 devid = bus_space_read_2(aa->aa_bst, ioh, 2);
177
178 /* issue software exit */
179 bus_space_write_2(aa->aa_bst, ioh, 0x0, 0xF0F0);
180
181 for (i = 0; flash_ids[i].name != NULL; i++) {
182 if ((venid == flash_ids[i].vendor_id) &&
183 (devid == flash_ids[i].device_id)) {
184 rv = 1;
185 break;
186 }
187 }
188
189 bus_space_unmap(aa->aa_bst, ioh, aa->aa_size);
190 return rv;
191 }
192
193 static void
194 flash_attach(device_t parent, device_t self, void *aux)
195 {
196 char nbuf[32];
197 struct flash_softc *sc = device_private(self);
198 struct arbus_attach_args *aa = aux;
199 int i;
200 bus_space_tag_t iot = aa->aa_bst;
201 bus_space_handle_t ioh;
202 uint16_t venid, devid;
203
204 if (bus_space_map(iot, aa->aa_addr, aa->aa_size, 0, &ioh)) {
205 printf(": can't map i/o space\n");
206 return;
207 }
208
209 sc->sc_iot = iot;
210 sc->sc_ioh = ioh;
211 sc->sc_status = 0;
212
213 /* issue JEDEC query */
214 bus_space_write_2(aa->aa_bst, ioh, (0x5555 << 1), 0xAAAA);
215 bus_space_write_2(aa->aa_bst, ioh, (0x2AAA << 1), 0x5555);
216 bus_space_write_2(aa->aa_bst, ioh, (0x5555 << 1), 0x9090);
217
218 delay(100);
219 venid = bus_space_read_2(aa->aa_bst, ioh, 0);
220 devid = bus_space_read_2(aa->aa_bst, ioh, 2);
221
222 /* issue software exit */
223 bus_space_write_2(aa->aa_bst, ioh, 0x0, 0xF0F0);
224
225 for (i = 0; flash_ids[i].name != NULL; i++) {
226 if ((venid == flash_ids[i].vendor_id) &&
227 (devid == flash_ids[i].device_id)) {
228 break;
229 }
230 }
231
232 KASSERT(flash_ids[i].name != NULL);
233 printf(": %s", flash_ids[i].name);
234 if (humanize_number(nbuf, sizeof(nbuf), flash_ids[i].flash_size, "B",
235 1024) > 0)
236 printf(" (%s)", nbuf);
237
238 /*
239 * determine size of the largest block
240 */
241 sc->sc_size = flash_ids[i].flash_size;
242 sc->sc_sector_size = flash_ids[i].sector_size;
243
244 if ((sc->sc_buf = malloc(sc->sc_sector_size, M_DEVBUF, M_NOWAIT))
245 == NULL) {
246 printf(": can't alloc buffer space\n");
247 return;
248 }
249
250 printf("\n");
251 }
252
253 int
254 flashopen(dev_t dev, int flag, int mode, struct lwp *l)
255 {
256 struct flash_softc *sc;
257
258 sc = device_lookup_private(&athflash_cd, minor(dev));
259 if (sc == NULL)
260 return ENXIO;
261 if (sc->sc_status & FLASH_ST_BUSY)
262 return EBUSY;
263 sc->sc_status |= FLASH_ST_BUSY;
264 return 0;
265 }
266
267 int
268 flashclose(dev_t dev, int flag, int mode, struct lwp *l)
269 {
270 struct flash_softc *sc;
271
272 sc = device_lookup_private(&athflash_cd, minor(dev));
273 sc->sc_status &= ~FLASH_ST_BUSY;
274 return 0;
275 }
276
277 int
278 flashread(dev_t dev, struct uio *uio, int flag)
279 {
280 struct flash_softc *sc;
281 bus_space_tag_t iot;
282 bus_space_handle_t ioh;
283 bus_size_t off;
284 int total;
285 int count;
286 int error;
287
288 sc = device_lookup_private(&athflash_cd, minor(dev));
289 iot = sc->sc_iot;
290 ioh = sc->sc_ioh;
291
292 off = uio->uio_offset;
293 total = min(sc->sc_size - off, uio->uio_resid);
294
295 while (total > 0) {
296 count = min(sc->sc_sector_size, uio->uio_resid);
297 bus_space_read_region_1(iot, ioh, off, sc->sc_buf, count);
298 if ((error = uiomove(sc->sc_buf, count, uio)) != 0)
299 return error;
300 off += count;
301 total -= count;
302 }
303 return 0;
304 }
305
306
307 int
308 flashwrite(dev_t dev, struct uio *uio, int flag)
309 {
310 struct flash_softc *sc;
311 bus_space_tag_t iot;
312 bus_space_handle_t ioh;
313 bus_size_t off;
314 int stat;
315 int error;
316
317 sc = device_lookup_private(&athflash_cd, minor(dev));
318
319 if (sc->sc_size < uio->uio_offset + uio->uio_resid)
320 return ENOSPC;
321 if (uio->uio_offset % sc->sc_sector_size)
322 return EINVAL;
323 if (uio->uio_resid % sc->sc_sector_size)
324 return EINVAL;
325
326 iot = sc->sc_iot;
327 ioh = sc->sc_ioh;
328
329 for (off = uio->uio_offset;
330 uio->uio_resid > 0;
331 off += sc->sc_sector_size) {
332 error = uiomove(sc->sc_buf, sc->sc_sector_size, uio);
333 if (error != 0)
334 return error;
335 if (is_block_same(sc, off, sc->sc_buf))
336 continue;
337 if ((stat = flash_sector_erase(sc, off)) != 0) {
338 printf("sector erase failed status = 0x%x\n", stat);
339 return EIO;
340 }
341 if ((stat = flash_sector_write(sc, off)) != 0) {
342 printf("sector write failed status = 0x%x\n", stat);
343 return EIO;
344 }
345 }
346 return 0;
347 }
348
349 static int
350 is_block_same(struct flash_softc *sc, bus_size_t offset, const void *bufp)
351 {
352 bus_space_tag_t iot = sc->sc_iot;
353 bus_space_handle_t ioh = sc->sc_ioh;
354 const u_int8_t *p = bufp;
355 int count = sc->sc_sector_size;
356
357 while (count-- > 0) {
358 if (bus_space_read_1(iot, ioh, offset++) != *p++)
359 return 0;
360 }
361 return 1;
362 }
363
364 static int
365 toggle_bit_wait(struct flash_softc *sc, bus_size_t offset,
366 int typtmo, int maxtmo, int spin)
367 {
368 bus_space_tag_t iot = sc->sc_iot;
369 bus_space_handle_t ioh = sc->sc_ioh;
370 uint8_t d1, d2;
371
372 while (maxtmo > 0) {
373
374 if (spin) {
375 DELAY(typtmo);
376 } else {
377 tsleep(sc, PRIBIO, "blockerase",
378 (typtmo / hz) + 1);
379 }
380
381 d1 = bus_space_read_1(iot, ioh, offset);
382 d2 = bus_space_read_2(iot, ioh, offset);
383
384 /* watch for the toggle bit to stop toggling */
385 if ((d1 & 0x40) == (d2 & 0x40)) {
386 return 0;
387 }
388
389 maxtmo -= typtmo;
390 }
391 return (ETIMEDOUT);
392 }
393
394 static int
395 flash_sector_erase(struct flash_softc *sc, bus_size_t offset)
396 {
397 bus_space_tag_t iot = sc->sc_iot;
398 bus_space_handle_t ioh = sc->sc_ioh;
399
400 DPRINTF(("flash_sector_erase offset = %08lx\n", offset));
401
402 bus_space_write_2(iot, ioh, (0x5555 << 1), 0xAAAA);
403 bus_space_write_2(iot, ioh, (0x2AAA << 1), 0x5555);
404 bus_space_write_2(iot, ioh, (0x5555 << 1), 0x8080);
405 bus_space_write_2(iot, ioh, (0x5555 << 1), 0xAAAA);
406 bus_space_write_2(iot, ioh, (0x2AAA << 1), 0x5555);
407
408 bus_space_write_2(iot, ioh, offset, 0x3030);
409
410 /*
411 * NB: with CFI, we could get more meaningful timeout data for
412 * now we just assign reasonable values - 10 msec typical, and
413 * up to 60 secs to erase the whole sector.
414 */
415
416 return toggle_bit_wait(sc, offset, 10000, 60000000, 0);
417 }
418
419 static int
420 flash_sector_write(struct flash_softc *sc, bus_size_t offset)
421 {
422 bus_space_tag_t iot = sc->sc_iot;
423 bus_space_handle_t ioh = sc->sc_ioh;
424 bus_size_t fence;
425 const u_int16_t *p;
426
427 p = (u_int16_t *) sc->sc_buf;
428 fence = offset + sc->sc_sector_size;
429 do {
430 bus_space_write_2(iot, ioh, (0x5555 << 1), 0xAAAA);
431 bus_space_write_2(iot, ioh, (0x2AAA << 1), 0x5555);
432 bus_space_write_2(iot, ioh, (0xAAAA << 1), 0xA0A0);
433
434 bus_space_write_2(iot, ioh, offset, *p);
435
436 /* wait up to 1 msec, in 10 usec increments, no sleeping */
437 if (toggle_bit_wait(sc, offset, 10, 1000, 1) != 0)
438 return ETIMEDOUT;
439 p++;
440 offset += 2;
441 } while (offset < fence);
442
443 return 0;
444 }
445