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