1 1.28 thorpej /* $NetBSD: spiflash.c,v 1.28 2025/09/13 14:10:44 thorpej Exp $ */ 2 1.1 gdamore 3 1.1 gdamore /*- 4 1.1 gdamore * Copyright (c) 2006 Urbana-Champaign Independent Media Center. 5 1.1 gdamore * Copyright (c) 2006 Garrett D'Amore. 6 1.1 gdamore * All rights reserved. 7 1.1 gdamore * 8 1.1 gdamore * Portions of this code were written by Garrett D'Amore for the 9 1.1 gdamore * Champaign-Urbana Community Wireless Network Project. 10 1.1 gdamore * 11 1.1 gdamore * Redistribution and use in source and binary forms, with or 12 1.1 gdamore * without modification, are permitted provided that the following 13 1.1 gdamore * conditions are met: 14 1.1 gdamore * 1. Redistributions of source code must retain the above copyright 15 1.1 gdamore * notice, this list of conditions and the following disclaimer. 16 1.1 gdamore * 2. Redistributions in binary form must reproduce the above 17 1.1 gdamore * copyright notice, this list of conditions and the following 18 1.1 gdamore * disclaimer in the documentation and/or other materials provided 19 1.1 gdamore * with the distribution. 20 1.1 gdamore * 3. All advertising materials mentioning features or use of this 21 1.1 gdamore * software must display the following acknowledgements: 22 1.1 gdamore * This product includes software developed by the Urbana-Champaign 23 1.1 gdamore * Independent Media Center. 24 1.1 gdamore * This product includes software developed by Garrett D'Amore. 25 1.1 gdamore * 4. Urbana-Champaign Independent Media Center's name and Garrett 26 1.1 gdamore * D'Amore's name may not be used to endorse or promote products 27 1.1 gdamore * derived from this software without specific prior written permission. 28 1.1 gdamore * 29 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT 30 1.1 gdamore * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR 31 1.1 gdamore * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 32 1.1 gdamore * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 1.1 gdamore * ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT 34 1.1 gdamore * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT, 35 1.1 gdamore * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 1.1 gdamore * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 1.1 gdamore * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 1.1 gdamore * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 1.1 gdamore * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 1.1 gdamore * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 1.1 gdamore * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 1.1 gdamore */ 43 1.1 gdamore 44 1.1 gdamore #include <sys/cdefs.h> 45 1.28 thorpej __KERNEL_RCSID(0, "$NetBSD: spiflash.c,v 1.28 2025/09/13 14:10:44 thorpej Exp $"); 46 1.1 gdamore 47 1.1 gdamore #include <sys/param.h> 48 1.1 gdamore #include <sys/conf.h> 49 1.1 gdamore #include <sys/proc.h> 50 1.1 gdamore #include <sys/systm.h> 51 1.1 gdamore #include <sys/device.h> 52 1.1 gdamore #include <sys/kernel.h> 53 1.1 gdamore #include <sys/file.h> 54 1.1 gdamore #include <sys/ioctl.h> 55 1.1 gdamore #include <sys/disk.h> 56 1.1 gdamore #include <sys/disklabel.h> 57 1.1 gdamore #include <sys/buf.h> 58 1.1 gdamore #include <sys/bufq.h> 59 1.1 gdamore #include <sys/uio.h> 60 1.1 gdamore #include <sys/kthread.h> 61 1.1 gdamore #include <sys/malloc.h> 62 1.1 gdamore #include <sys/errno.h> 63 1.1 gdamore 64 1.1 gdamore #include <dev/spi/spivar.h> 65 1.1 gdamore #include <dev/spi/spiflash.h> 66 1.1 gdamore 67 1.1 gdamore /* 68 1.1 gdamore * This is an MI block driver for SPI flash devices. It could probably be 69 1.1 gdamore * converted to some more generic framework, if someone wanted to create one 70 1.1 gdamore * for NOR flashes. Note that some flashes have the ability to handle 71 1.1 gdamore * interrupts. 72 1.1 gdamore */ 73 1.1 gdamore 74 1.1 gdamore struct spiflash_softc { 75 1.1 gdamore struct disk sc_dk; 76 1.1 gdamore 77 1.1 gdamore struct spiflash_hw_if sc_hw; 78 1.1 gdamore void *sc_cookie; 79 1.1 gdamore 80 1.1 gdamore const char *sc_name; 81 1.28 thorpej spi_handle_t sc_handle; 82 1.1 gdamore int sc_device_size; 83 1.1 gdamore int sc_write_size; 84 1.1 gdamore int sc_erase_size; 85 1.1 gdamore int sc_read_size; 86 1.1 gdamore int sc_device_blks; 87 1.1 gdamore 88 1.2 gdamore struct bufq_state *sc_waitq; 89 1.2 gdamore struct bufq_state *sc_workq; 90 1.2 gdamore struct bufq_state *sc_doneq; 91 1.5 dyoung lwp_t *sc_thread; 92 1.1 gdamore }; 93 1.1 gdamore 94 1.1 gdamore #define sc_getname sc_hw.sf_getname 95 1.1 gdamore #define sc_gethandle sc_hw.sf_gethandle 96 1.1 gdamore #define sc_getsize sc_hw.sf_getsize 97 1.1 gdamore #define sc_getflags sc_hw.sf_getflags 98 1.1 gdamore #define sc_erase sc_hw.sf_erase 99 1.1 gdamore #define sc_write sc_hw.sf_write 100 1.1 gdamore #define sc_read sc_hw.sf_read 101 1.1 gdamore #define sc_getstatus sc_hw.sf_getstatus 102 1.1 gdamore #define sc_setstatus sc_hw.sf_setstatus 103 1.1 gdamore 104 1.1 gdamore struct spiflash_attach_args { 105 1.1 gdamore const struct spiflash_hw_if *hw; 106 1.1 gdamore void *cookie; 107 1.1 gdamore }; 108 1.1 gdamore 109 1.1 gdamore #define STATIC 110 1.8 xtraeme STATIC int spiflash_match(device_t , cfdata_t , void *); 111 1.8 xtraeme STATIC void spiflash_attach(device_t , device_t , void *); 112 1.1 gdamore STATIC int spiflash_print(void *, const char *); 113 1.1 gdamore STATIC int spiflash_common_erase(spiflash_handle_t, size_t, size_t); 114 1.1 gdamore STATIC int spiflash_common_write(spiflash_handle_t, size_t, size_t, 115 1.1 gdamore const uint8_t *); 116 1.1 gdamore STATIC int spiflash_common_read(spiflash_handle_t, size_t, size_t, uint8_t *); 117 1.2 gdamore STATIC void spiflash_process_done(spiflash_handle_t, int); 118 1.2 gdamore STATIC void spiflash_process_read(spiflash_handle_t); 119 1.2 gdamore STATIC void spiflash_process_write(spiflash_handle_t); 120 1.1 gdamore STATIC void spiflash_thread(void *); 121 1.2 gdamore STATIC int spiflash_nsectors(spiflash_handle_t, struct buf *); 122 1.2 gdamore STATIC int spiflash_nsectors(spiflash_handle_t, struct buf *); 123 1.2 gdamore STATIC int spiflash_sector(spiflash_handle_t, struct buf *); 124 1.1 gdamore 125 1.8 xtraeme CFATTACH_DECL_NEW(spiflash, sizeof(struct spiflash_softc), 126 1.1 gdamore spiflash_match, spiflash_attach, NULL, NULL); 127 1.1 gdamore 128 1.2 gdamore #ifdef SPIFLASH_DEBUG 129 1.2 gdamore #define DPRINTF(x) do { printf x; } while (0/*CONSTCOND*/) 130 1.2 gdamore #else 131 1.2 gdamore #define DPRINTF(x) do { } while (0/*CONSTCOND*/) 132 1.2 gdamore #endif 133 1.2 gdamore 134 1.1 gdamore extern struct cfdriver spiflash_cd; 135 1.1 gdamore 136 1.1 gdamore dev_type_open(spiflash_open); 137 1.1 gdamore dev_type_close(spiflash_close); 138 1.1 gdamore dev_type_read(spiflash_read); 139 1.1 gdamore dev_type_write(spiflash_write); 140 1.1 gdamore dev_type_ioctl(spiflash_ioctl); 141 1.1 gdamore dev_type_strategy(spiflash_strategy); 142 1.1 gdamore 143 1.1 gdamore const struct bdevsw spiflash_bdevsw = { 144 1.1 gdamore .d_open = spiflash_open, 145 1.1 gdamore .d_close = spiflash_close, 146 1.1 gdamore .d_strategy = spiflash_strategy, 147 1.1 gdamore .d_ioctl = spiflash_ioctl, 148 1.1 gdamore .d_dump = nodump, 149 1.1 gdamore .d_psize = nosize, 150 1.14 dholland .d_discard = nodiscard, 151 1.4 ad .d_flag = D_DISK, 152 1.1 gdamore }; 153 1.1 gdamore 154 1.1 gdamore const struct cdevsw spiflash_cdevsw = { 155 1.1 gdamore .d_open = spiflash_open, 156 1.1 gdamore .d_close = spiflash_close, 157 1.1 gdamore .d_read = spiflash_read, 158 1.1 gdamore .d_write = spiflash_write, 159 1.1 gdamore .d_ioctl = spiflash_ioctl, 160 1.1 gdamore .d_stop = nostop, 161 1.1 gdamore .d_tty = notty, 162 1.1 gdamore .d_poll = nopoll, 163 1.1 gdamore .d_mmap = nommap, 164 1.1 gdamore .d_kqfilter = nokqfilter, 165 1.15 dholland .d_discard = nodiscard, 166 1.4 ad .d_flag = D_DISK, 167 1.1 gdamore }; 168 1.1 gdamore 169 1.17 mlelstv static struct dkdriver spiflash_dkdriver = { 170 1.17 mlelstv .d_strategy = spiflash_strategy 171 1.17 mlelstv }; 172 1.1 gdamore 173 1.1 gdamore spiflash_handle_t 174 1.1 gdamore spiflash_attach_mi(const struct spiflash_hw_if *hw, void *cookie, 175 1.8 xtraeme device_t dev) 176 1.1 gdamore { 177 1.1 gdamore struct spiflash_attach_args sfa; 178 1.1 gdamore sfa.hw = hw; 179 1.1 gdamore sfa.cookie = cookie; 180 1.1 gdamore 181 1.24 thorpej return (spiflash_handle_t)config_found(dev, &sfa, spiflash_print, 182 1.25 thorpej CFARGS_NONE); 183 1.1 gdamore } 184 1.1 gdamore 185 1.1 gdamore int 186 1.1 gdamore spiflash_print(void *aux, const char *pnp) 187 1.1 gdamore { 188 1.1 gdamore if (pnp != NULL) 189 1.1 gdamore printf("spiflash at %s\n", pnp); 190 1.1 gdamore 191 1.1 gdamore return UNCONF; 192 1.1 gdamore } 193 1.1 gdamore 194 1.1 gdamore int 195 1.8 xtraeme spiflash_match(device_t parent, cfdata_t cf, void *aux) 196 1.1 gdamore { 197 1.1 gdamore 198 1.1 gdamore return 1; 199 1.1 gdamore } 200 1.1 gdamore 201 1.1 gdamore void 202 1.8 xtraeme spiflash_attach(device_t parent, device_t self, void *aux) 203 1.1 gdamore { 204 1.1 gdamore struct spiflash_softc *sc = device_private(self); 205 1.1 gdamore struct spiflash_attach_args *sfa = aux; 206 1.1 gdamore void *cookie = sfa->cookie; 207 1.1 gdamore 208 1.1 gdamore sc->sc_hw = *sfa->hw; 209 1.1 gdamore sc->sc_cookie = cookie; 210 1.1 gdamore sc->sc_name = sc->sc_getname(cookie); 211 1.1 gdamore sc->sc_handle = sc->sc_gethandle(cookie); 212 1.1 gdamore sc->sc_device_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_DEVICE); 213 1.1 gdamore sc->sc_erase_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_ERASE); 214 1.1 gdamore sc->sc_write_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_WRITE); 215 1.1 gdamore sc->sc_read_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_READ); 216 1.1 gdamore sc->sc_device_blks = sc->sc_device_size / DEV_BSIZE; 217 1.1 gdamore 218 1.1 gdamore if (sc->sc_read == NULL) 219 1.1 gdamore sc->sc_read = spiflash_common_read; 220 1.1 gdamore if (sc->sc_write == NULL) 221 1.1 gdamore sc->sc_write = spiflash_common_write; 222 1.1 gdamore if (sc->sc_erase == NULL) 223 1.1 gdamore sc->sc_erase = spiflash_common_erase; 224 1.1 gdamore 225 1.1 gdamore aprint_naive(": SPI flash\n"); 226 1.1 gdamore aprint_normal(": %s SPI flash\n", sc->sc_name); 227 1.1 gdamore /* XXX: note that this has to change for boot-sectored flash */ 228 1.23 tnn aprint_normal_dev(self, "%d KB, %d sectors of %d KB each\n", 229 1.7 cegger sc->sc_device_size / 1024, 230 1.1 gdamore sc->sc_device_size / sc->sc_erase_size, 231 1.1 gdamore sc->sc_erase_size / 1024); 232 1.1 gdamore 233 1.1 gdamore /* first-come first-served strategy works best for us */ 234 1.2 gdamore bufq_alloc(&sc->sc_waitq, "fcfs", BUFQ_SORT_RAWBLOCK); 235 1.2 gdamore bufq_alloc(&sc->sc_workq, "fcfs", BUFQ_SORT_RAWBLOCK); 236 1.2 gdamore bufq_alloc(&sc->sc_doneq, "fcfs", BUFQ_SORT_RAWBLOCK); 237 1.1 gdamore 238 1.16 mlelstv disk_init(&sc->sc_dk, device_xname(self), &spiflash_dkdriver); 239 1.1 gdamore disk_attach(&sc->sc_dk); 240 1.4 ad 241 1.4 ad /* arrange to allocate the kthread */ 242 1.5 dyoung kthread_create(PRI_NONE, 0, NULL, spiflash_thread, sc, 243 1.4 ad &sc->sc_thread, "spiflash"); 244 1.1 gdamore } 245 1.1 gdamore 246 1.1 gdamore int 247 1.1 gdamore spiflash_open(dev_t dev, int flags, int mode, struct lwp *l) 248 1.1 gdamore { 249 1.1 gdamore spiflash_handle_t sc; 250 1.1 gdamore 251 1.9 cegger sc = device_lookup_private(&spiflash_cd, DISKUNIT(dev)); 252 1.9 cegger if (sc == NULL) 253 1.1 gdamore return ENXIO; 254 1.1 gdamore 255 1.1 gdamore /* 256 1.1 gdamore * XXX: We need to handle partitions here. The problem is 257 1.1 gdamore * that it isn't entirely clear to me how to deal with this. 258 1.1 gdamore * There are devices that could be used "in the raw" with a 259 1.1 gdamore * NetBSD label, but then you get into devices that have other 260 1.1 gdamore * kinds of data on them -- some have VxWorks data, some have 261 1.27 andvar * RedBoot data, and some have other constraints -- for example 262 1.1 gdamore * some devices might have a portion that is read-only, 263 1.1 gdamore * whereas others might have a portion that is read-write. 264 1.1 gdamore * 265 1.1 gdamore * For now we just permit access to the entire device. 266 1.1 gdamore */ 267 1.1 gdamore return 0; 268 1.1 gdamore } 269 1.1 gdamore 270 1.1 gdamore int 271 1.1 gdamore spiflash_close(dev_t dev, int flags, int mode, struct lwp *l) 272 1.1 gdamore { 273 1.1 gdamore spiflash_handle_t sc; 274 1.1 gdamore 275 1.9 cegger sc = device_lookup_private(&spiflash_cd, DISKUNIT(dev)); 276 1.9 cegger if (sc == NULL) 277 1.1 gdamore return ENXIO; 278 1.1 gdamore 279 1.1 gdamore return 0; 280 1.1 gdamore } 281 1.1 gdamore 282 1.1 gdamore int 283 1.1 gdamore spiflash_read(dev_t dev, struct uio *uio, int ioflag) 284 1.1 gdamore { 285 1.1 gdamore 286 1.1 gdamore return physio(spiflash_strategy, NULL, dev, B_READ, minphys, uio); 287 1.1 gdamore } 288 1.1 gdamore 289 1.1 gdamore int 290 1.1 gdamore spiflash_write(dev_t dev, struct uio *uio, int ioflag) 291 1.1 gdamore { 292 1.1 gdamore 293 1.1 gdamore return physio(spiflash_strategy, NULL, dev, B_WRITE, minphys, uio); 294 1.1 gdamore } 295 1.1 gdamore 296 1.1 gdamore int 297 1.3 christos spiflash_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 298 1.1 gdamore { 299 1.1 gdamore spiflash_handle_t sc; 300 1.1 gdamore 301 1.9 cegger sc = device_lookup_private(&spiflash_cd, DISKUNIT(dev)); 302 1.9 cegger if (sc == NULL) 303 1.1 gdamore return ENXIO; 304 1.1 gdamore 305 1.1 gdamore return EINVAL; 306 1.1 gdamore } 307 1.1 gdamore 308 1.1 gdamore void 309 1.1 gdamore spiflash_strategy(struct buf *bp) 310 1.1 gdamore { 311 1.1 gdamore spiflash_handle_t sc; 312 1.1 gdamore int s; 313 1.1 gdamore 314 1.20 jakllsch bp->b_resid = bp->b_bcount; 315 1.20 jakllsch 316 1.9 cegger sc = device_lookup_private(&spiflash_cd, DISKUNIT(bp->b_dev)); 317 1.1 gdamore if (sc == NULL) { 318 1.1 gdamore bp->b_error = ENXIO; 319 1.1 gdamore biodone(bp); 320 1.1 gdamore return; 321 1.1 gdamore } 322 1.1 gdamore 323 1.2 gdamore if (((bp->b_bcount % sc->sc_write_size) != 0) || 324 1.1 gdamore (bp->b_blkno < 0)) { 325 1.1 gdamore bp->b_error = EINVAL; 326 1.1 gdamore biodone(bp); 327 1.1 gdamore return; 328 1.1 gdamore } 329 1.1 gdamore 330 1.1 gdamore /* no work? */ 331 1.1 gdamore if (bp->b_bcount == 0) { 332 1.1 gdamore biodone(bp); 333 1.1 gdamore return; 334 1.1 gdamore } 335 1.1 gdamore 336 1.2 gdamore if (bounds_check_with_mediasize(bp, DEV_BSIZE, 337 1.2 gdamore sc->sc_device_blks) <= 0) { 338 1.2 gdamore biodone(bp); 339 1.2 gdamore return; 340 1.1 gdamore } 341 1.1 gdamore 342 1.1 gdamore /* all ready, hand off to thread for async processing */ 343 1.1 gdamore s = splbio(); 344 1.10 yamt bufq_put(sc->sc_waitq, bp); 345 1.1 gdamore wakeup(&sc->sc_thread); 346 1.1 gdamore splx(s); 347 1.1 gdamore } 348 1.1 gdamore 349 1.1 gdamore void 350 1.2 gdamore spiflash_process_done(spiflash_handle_t sc, int err) 351 1.1 gdamore { 352 1.2 gdamore struct buf *bp; 353 1.2 gdamore int cnt = 0; 354 1.2 gdamore int flag = 0; 355 1.2 gdamore 356 1.10 yamt while ((bp = bufq_get(sc->sc_doneq)) != NULL) { 357 1.2 gdamore flag = bp->b_flags & B_READ; 358 1.6 ad if ((bp->b_error = err) == 0) 359 1.2 gdamore bp->b_resid = 0; 360 1.2 gdamore cnt += bp->b_bcount - bp->b_resid; 361 1.2 gdamore biodone(bp); 362 1.2 gdamore } 363 1.2 gdamore disk_unbusy(&sc->sc_dk, cnt, flag); 364 1.2 gdamore } 365 1.1 gdamore 366 1.2 gdamore void 367 1.2 gdamore spiflash_process_read(spiflash_handle_t sc) 368 1.2 gdamore { 369 1.2 gdamore struct buf *bp; 370 1.2 gdamore int err = 0; 371 1.2 gdamore 372 1.2 gdamore disk_busy(&sc->sc_dk); 373 1.10 yamt while ((bp = bufq_get(sc->sc_workq)) != NULL) { 374 1.2 gdamore size_t addr = bp->b_blkno * DEV_BSIZE; 375 1.2 gdamore uint8_t *data = bp->b_data; 376 1.2 gdamore int cnt = bp->b_resid; 377 1.2 gdamore 378 1.10 yamt bufq_put(sc->sc_doneq, bp); 379 1.2 gdamore 380 1.2 gdamore DPRINTF(("read from addr %x, cnt %d\n", (unsigned)addr, cnt)); 381 1.1 gdamore 382 1.2 gdamore if ((err = sc->sc_read(sc, addr, cnt, data)) != 0) { 383 1.2 gdamore /* error occurred, fail all pending workq bufs */ 384 1.2 gdamore bufq_move(sc->sc_doneq, sc->sc_workq); 385 1.2 gdamore break; 386 1.1 gdamore } 387 1.26 skrll 388 1.1 gdamore bp->b_resid -= cnt; 389 1.1 gdamore data += cnt; 390 1.1 gdamore addr += cnt; 391 1.1 gdamore } 392 1.2 gdamore spiflash_process_done(sc, err); 393 1.2 gdamore } 394 1.2 gdamore 395 1.2 gdamore void 396 1.2 gdamore spiflash_process_write(spiflash_handle_t sc) 397 1.2 gdamore { 398 1.2 gdamore int len; 399 1.2 gdamore size_t base; 400 1.2 gdamore daddr_t blkno; 401 1.2 gdamore uint8_t *save; 402 1.2 gdamore int err = 0, neederase = 0; 403 1.2 gdamore struct buf *bp; 404 1.2 gdamore 405 1.2 gdamore /* 406 1.2 gdamore * due to other considerations, we are guaranteed that 407 1.2 gdamore * we will only have multiple buffers if they are all in 408 1.2 gdamore * the same erase sector. Therefore we never need to look 409 1.2 gdamore * beyond the first block to determine how much data we need 410 1.2 gdamore * to save. 411 1.2 gdamore */ 412 1.2 gdamore 413 1.10 yamt bp = bufq_peek(sc->sc_workq); 414 1.2 gdamore len = spiflash_nsectors(sc, bp) * sc->sc_erase_size; 415 1.2 gdamore blkno = bp->b_blkno; 416 1.2 gdamore base = (blkno * DEV_BSIZE) & ~ (sc->sc_erase_size - 1); 417 1.2 gdamore 418 1.2 gdamore /* get ourself a scratch buffer */ 419 1.2 gdamore save = malloc(len, M_DEVBUF, M_WAITOK); 420 1.2 gdamore 421 1.2 gdamore disk_busy(&sc->sc_dk); 422 1.2 gdamore /* read in as much of the data as we need */ 423 1.2 gdamore DPRINTF(("reading in %d bytes\n", len)); 424 1.2 gdamore if ((err = sc->sc_read(sc, base, len, save)) != 0) { 425 1.26 skrll bufq_move(sc->sc_doneq, sc->sc_workq); 426 1.2 gdamore spiflash_process_done(sc, err); 427 1.2 gdamore return; 428 1.2 gdamore } 429 1.2 gdamore 430 1.2 gdamore /* 431 1.2 gdamore * now coalesce the writes into the save area, but also 432 1.2 gdamore * check to see if we need to do an erase 433 1.2 gdamore */ 434 1.10 yamt while ((bp = bufq_get(sc->sc_workq)) != NULL) { 435 1.2 gdamore uint8_t *data, *dst; 436 1.2 gdamore int resid = bp->b_resid; 437 1.2 gdamore 438 1.2 gdamore DPRINTF(("coalesce write, blkno %x, count %d, resid %d\n", 439 1.2 gdamore (unsigned)bp->b_blkno, bp->b_bcount, resid)); 440 1.2 gdamore 441 1.2 gdamore data = bp->b_data; 442 1.11 rkujawa dst = save + (bp->b_blkno * DEV_BSIZE) - base; 443 1.2 gdamore 444 1.2 gdamore /* 445 1.2 gdamore * NOR flash bits. We can clear a bit, but we cannot 446 1.2 gdamore * set a bit, without erasing. This should help reduce 447 1.2 gdamore * unnecessary erases. 448 1.2 gdamore */ 449 1.2 gdamore while (resid) { 450 1.2 gdamore if ((*data) & ~(*dst)) 451 1.2 gdamore neederase = 1; 452 1.2 gdamore *dst++ = *data++; 453 1.2 gdamore resid--; 454 1.2 gdamore } 455 1.2 gdamore 456 1.10 yamt bufq_put(sc->sc_doneq, bp); 457 1.2 gdamore } 458 1.26 skrll 459 1.2 gdamore /* 460 1.2 gdamore * do the erase, if we need to. 461 1.2 gdamore */ 462 1.2 gdamore if (neederase) { 463 1.13 hkenken DPRINTF(("erasing from %zx - %zx\n", base, base + len)); 464 1.2 gdamore if ((err = sc->sc_erase(sc, base, len)) != 0) { 465 1.2 gdamore spiflash_process_done(sc, err); 466 1.2 gdamore return; 467 1.2 gdamore } 468 1.2 gdamore } 469 1.2 gdamore 470 1.2 gdamore /* 471 1.2 gdamore * now write our save area, and finish up. 472 1.2 gdamore */ 473 1.13 hkenken DPRINTF(("flashing %d bytes to %zx from %p\n", len, base, save)); 474 1.2 gdamore err = sc->sc_write(sc, base, len, save); 475 1.2 gdamore spiflash_process_done(sc, err); 476 1.2 gdamore } 477 1.2 gdamore 478 1.2 gdamore 479 1.2 gdamore int 480 1.2 gdamore spiflash_nsectors(spiflash_handle_t sc, struct buf *bp) 481 1.2 gdamore { 482 1.2 gdamore unsigned addr, sector; 483 1.2 gdamore 484 1.2 gdamore addr = bp->b_blkno * DEV_BSIZE; 485 1.2 gdamore sector = addr / sc->sc_erase_size; 486 1.2 gdamore 487 1.2 gdamore addr += bp->b_bcount; 488 1.2 gdamore addr--; 489 1.2 gdamore return (((addr / sc->sc_erase_size) - sector) + 1); 490 1.2 gdamore } 491 1.2 gdamore 492 1.2 gdamore int 493 1.2 gdamore spiflash_sector(spiflash_handle_t sc, struct buf *bp) 494 1.2 gdamore { 495 1.2 gdamore unsigned addr, sector; 496 1.2 gdamore 497 1.2 gdamore addr = bp->b_blkno * DEV_BSIZE; 498 1.2 gdamore sector = addr / sc->sc_erase_size; 499 1.2 gdamore 500 1.2 gdamore /* if it spans multiple blocks, error it */ 501 1.2 gdamore addr += bp->b_bcount; 502 1.2 gdamore addr--; 503 1.2 gdamore if (sector != (addr / sc->sc_erase_size)) 504 1.2 gdamore return -1; 505 1.2 gdamore 506 1.2 gdamore return sector; 507 1.1 gdamore } 508 1.1 gdamore 509 1.1 gdamore void 510 1.1 gdamore spiflash_thread(void *arg) 511 1.1 gdamore { 512 1.1 gdamore spiflash_handle_t sc = arg; 513 1.1 gdamore struct buf *bp; 514 1.2 gdamore int sector; 515 1.1 gdamore 516 1.12 martin (void)splbio(); 517 1.1 gdamore for (;;) { 518 1.10 yamt if ((bp = bufq_get(sc->sc_waitq)) == NULL) { 519 1.1 gdamore tsleep(&sc->sc_thread, PRIBIO, "spiflash_thread", 0); 520 1.1 gdamore continue; 521 1.1 gdamore } 522 1.1 gdamore 523 1.10 yamt bufq_put(sc->sc_workq, bp); 524 1.2 gdamore 525 1.2 gdamore if (bp->b_flags & B_READ) { 526 1.2 gdamore /* just do the read */ 527 1.2 gdamore spiflash_process_read(sc); 528 1.2 gdamore continue; 529 1.2 gdamore } 530 1.2 gdamore 531 1.2 gdamore /* 532 1.2 gdamore * Because writing a flash filesystem is particularly 533 1.2 gdamore * painful, involving erase, modify, write, we prefer 534 1.2 gdamore * to coalesce writes to the same sector together. 535 1.2 gdamore */ 536 1.2 gdamore 537 1.2 gdamore sector = spiflash_sector(sc, bp); 538 1.2 gdamore 539 1.2 gdamore /* 540 1.2 gdamore * if the write spans multiple sectors, skip 541 1.2 gdamore * coalescing. (It would be nice if we could break 542 1.2 gdamore * these up. minphys is honored for read/write, but 543 1.2 gdamore * not necessarily for bread.) 544 1.2 gdamore */ 545 1.2 gdamore if (sector < 0) 546 1.2 gdamore goto dowrite; 547 1.2 gdamore 548 1.10 yamt while ((bp = bufq_peek(sc->sc_waitq)) != NULL) { 549 1.2 gdamore /* can't deal with read requests! */ 550 1.2 gdamore if (bp->b_flags & B_READ) 551 1.2 gdamore break; 552 1.2 gdamore 553 1.2 gdamore /* is it for the same sector? */ 554 1.2 gdamore if (spiflash_sector(sc, bp) != sector) 555 1.2 gdamore break; 556 1.2 gdamore 557 1.10 yamt bp = bufq_get(sc->sc_waitq); 558 1.10 yamt bufq_put(sc->sc_workq, bp); 559 1.2 gdamore } 560 1.2 gdamore 561 1.2 gdamore dowrite: 562 1.2 gdamore spiflash_process_write(sc); 563 1.1 gdamore } 564 1.1 gdamore } 565 1.1 gdamore /* 566 1.1 gdamore * SPI flash common implementation. 567 1.1 gdamore */ 568 1.1 gdamore 569 1.1 gdamore /* 570 1.1 gdamore * Most devices take on the order of 1 second for each block that they 571 1.1 gdamore * delete. 572 1.1 gdamore */ 573 1.1 gdamore int 574 1.1 gdamore spiflash_common_erase(spiflash_handle_t sc, size_t start, size_t size) 575 1.1 gdamore { 576 1.1 gdamore int rv; 577 1.1 gdamore 578 1.1 gdamore if ((start % sc->sc_erase_size) || (size % sc->sc_erase_size)) 579 1.1 gdamore return EINVAL; 580 1.1 gdamore 581 1.26 skrll /* the second test is to test against wrap */ 582 1.1 gdamore if ((start > sc->sc_device_size) || 583 1.1 gdamore ((start + size) > sc->sc_device_size)) 584 1.1 gdamore return EINVAL; 585 1.1 gdamore 586 1.1 gdamore /* 587 1.1 gdamore * XXX: check protection status? Requires master table mapping 588 1.1 gdamore * sectors to status bits, and so forth. 589 1.1 gdamore */ 590 1.1 gdamore 591 1.1 gdamore while (size) { 592 1.1 gdamore if ((rv = spiflash_write_enable(sc)) != 0) { 593 1.1 gdamore spiflash_write_disable(sc); 594 1.1 gdamore return rv; 595 1.1 gdamore } 596 1.1 gdamore if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_ERASE, 3, start, 0, 597 1.1 gdamore NULL, NULL)) != 0) { 598 1.1 gdamore spiflash_write_disable(sc); 599 1.1 gdamore return rv; 600 1.1 gdamore } 601 1.1 gdamore 602 1.1 gdamore /* 603 1.1 gdamore * The devices I have all say typical for sector erase 604 1.1 gdamore * is ~1sec. We check ten times that often. (There 605 1.1 gdamore * is no way to interrupt on this.) 606 1.1 gdamore */ 607 1.1 gdamore if ((rv = spiflash_wait(sc, hz / 10)) != 0) 608 1.1 gdamore return rv; 609 1.1 gdamore 610 1.1 gdamore start += sc->sc_erase_size; 611 1.1 gdamore size -= sc->sc_erase_size; 612 1.1 gdamore 613 1.1 gdamore /* NB: according to the docs I have, the write enable 614 1.1 gdamore * is automatically cleared upon completion of an erase 615 1.1 gdamore * command, so there is no need to explicitly disable it. 616 1.1 gdamore */ 617 1.1 gdamore } 618 1.1 gdamore 619 1.1 gdamore return 0; 620 1.1 gdamore } 621 1.1 gdamore 622 1.1 gdamore int 623 1.1 gdamore spiflash_common_write(spiflash_handle_t sc, size_t start, size_t size, 624 1.1 gdamore const uint8_t *data) 625 1.1 gdamore { 626 1.1 gdamore int rv; 627 1.1 gdamore 628 1.1 gdamore if ((start % sc->sc_write_size) || (size % sc->sc_write_size)) 629 1.1 gdamore return EINVAL; 630 1.1 gdamore 631 1.1 gdamore while (size) { 632 1.1 gdamore int cnt; 633 1.1 gdamore 634 1.1 gdamore if ((rv = spiflash_write_enable(sc)) != 0) { 635 1.1 gdamore spiflash_write_disable(sc); 636 1.1 gdamore return rv; 637 1.1 gdamore } 638 1.1 gdamore 639 1.21 riastrad cnt = uimin(size, sc->sc_write_size); 640 1.1 gdamore if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_PROGRAM, 3, start, 641 1.1 gdamore cnt, data, NULL)) != 0) { 642 1.1 gdamore spiflash_write_disable(sc); 643 1.1 gdamore return rv; 644 1.1 gdamore } 645 1.1 gdamore 646 1.1 gdamore /* 647 1.1 gdamore * It seems that most devices can write bits fairly 648 1.1 gdamore * quickly. For example, one part I have access to 649 1.1 gdamore * takes ~5msec to process the entire 256 byte page. 650 1.1 gdamore * Probably this should be modified to cope with 651 1.1 gdamore * device-specific timing, and maybe also take into 652 1.1 gdamore * account systems with higher values of HZ (which 653 1.1 gdamore * could benefit from sleeping.) 654 1.1 gdamore */ 655 1.1 gdamore if ((rv = spiflash_wait(sc, 0)) != 0) 656 1.1 gdamore return rv; 657 1.1 gdamore 658 1.2 gdamore data += cnt; 659 1.1 gdamore start += cnt; 660 1.1 gdamore size -= cnt; 661 1.1 gdamore } 662 1.1 gdamore 663 1.1 gdamore return 0; 664 1.1 gdamore } 665 1.1 gdamore 666 1.1 gdamore int 667 1.1 gdamore spiflash_common_read(spiflash_handle_t sc, size_t start, size_t size, 668 1.1 gdamore uint8_t *data) 669 1.1 gdamore { 670 1.1 gdamore int rv; 671 1.1 gdamore 672 1.1 gdamore while (size) { 673 1.1 gdamore int cnt; 674 1.1 gdamore 675 1.1 gdamore if (sc->sc_read_size > 0) 676 1.21 riastrad cnt = uimin(size, sc->sc_read_size); 677 1.26 skrll else 678 1.1 gdamore cnt = size; 679 1.1 gdamore 680 1.1 gdamore if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_READ, 3, start, 681 1.1 gdamore cnt, NULL, data)) != 0) { 682 1.1 gdamore return rv; 683 1.1 gdamore } 684 1.1 gdamore 685 1.18 ryo data += cnt; 686 1.1 gdamore start += cnt; 687 1.1 gdamore size -= cnt; 688 1.1 gdamore } 689 1.1 gdamore 690 1.1 gdamore return 0; 691 1.1 gdamore } 692 1.1 gdamore 693 1.1 gdamore /* read status register */ 694 1.1 gdamore int 695 1.1 gdamore spiflash_read_status(spiflash_handle_t sc, uint8_t *sr) 696 1.1 gdamore { 697 1.1 gdamore 698 1.1 gdamore return spiflash_cmd(sc, SPIFLASH_CMD_RDSR, 0, 0, 1, NULL, sr); 699 1.1 gdamore } 700 1.1 gdamore 701 1.1 gdamore int 702 1.1 gdamore spiflash_write_enable(spiflash_handle_t sc) 703 1.1 gdamore { 704 1.1 gdamore 705 1.1 gdamore return spiflash_cmd(sc, SPIFLASH_CMD_WREN, 0, 0, 0, NULL, NULL); 706 1.1 gdamore } 707 1.1 gdamore 708 1.1 gdamore int 709 1.1 gdamore spiflash_write_disable(spiflash_handle_t sc) 710 1.1 gdamore { 711 1.1 gdamore 712 1.1 gdamore return spiflash_cmd(sc, SPIFLASH_CMD_WRDI, 0, 0, 0, NULL, NULL); 713 1.1 gdamore } 714 1.1 gdamore 715 1.1 gdamore int 716 1.1 gdamore spiflash_cmd(spiflash_handle_t sc, uint8_t cmd, 717 1.1 gdamore size_t addrlen, uint32_t addr, 718 1.1 gdamore size_t cnt, const uint8_t *wdata, uint8_t *rdata) 719 1.1 gdamore { 720 1.1 gdamore struct spi_transfer trans; 721 1.1 gdamore struct spi_chunk chunk1, chunk2; 722 1.1 gdamore char buf[4]; 723 1.1 gdamore int i; 724 1.1 gdamore 725 1.1 gdamore buf[0] = cmd; 726 1.1 gdamore 727 1.1 gdamore if (addrlen > 3) 728 1.1 gdamore return EINVAL; 729 1.1 gdamore 730 1.1 gdamore for (i = addrlen; i > 0; i--) { 731 1.2 gdamore buf[i] = addr & 0xff; 732 1.2 gdamore addr >>= 8; 733 1.1 gdamore } 734 1.1 gdamore spi_transfer_init(&trans); 735 1.1 gdamore spi_chunk_init(&chunk1, addrlen + 1, buf, NULL); 736 1.1 gdamore spi_transfer_add(&trans, &chunk1); 737 1.1 gdamore if (cnt) { 738 1.1 gdamore spi_chunk_init(&chunk2, cnt, wdata, rdata); 739 1.1 gdamore spi_transfer_add(&trans, &chunk2); 740 1.1 gdamore } 741 1.1 gdamore 742 1.1 gdamore spi_transfer(sc->sc_handle, &trans); 743 1.1 gdamore spi_wait(&trans); 744 1.1 gdamore 745 1.1 gdamore if (trans.st_flags & SPI_F_ERROR) 746 1.1 gdamore return trans.st_errno; 747 1.1 gdamore return 0; 748 1.1 gdamore } 749 1.1 gdamore 750 1.1 gdamore int 751 1.1 gdamore spiflash_wait(spiflash_handle_t sc, int tmo) 752 1.1 gdamore { 753 1.1 gdamore int rv; 754 1.1 gdamore uint8_t sr; 755 1.1 gdamore 756 1.1 gdamore for (;;) { 757 1.1 gdamore if ((rv = spiflash_read_status(sc, &sr)) != 0) 758 1.1 gdamore return rv; 759 1.1 gdamore 760 1.1 gdamore if ((sr & SPIFLASH_SR_BUSY) == 0) 761 1.1 gdamore break; 762 1.1 gdamore /* 763 1.1 gdamore * The devices I have all say typical for sector 764 1.1 gdamore * erase is ~1sec. We check time times that often. 765 1.1 gdamore * (There is no way to interrupt on this.) 766 1.1 gdamore */ 767 1.1 gdamore if (tmo) 768 1.1 gdamore tsleep(&sr, PWAIT, "spiflash_wait", tmo); 769 1.1 gdamore } 770 1.1 gdamore return 0; 771 1.1 gdamore } 772