spiflash.c revision 1.1 1 1.1 gdamore /* $NetBSD: spiflash.c,v 1.1 2006/10/07 07:21:13 gdamore 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.1 gdamore __KERNEL_RCSID(0, "$NetBSD: spiflash.c,v 1.1 2006/10/07 07:21:13 gdamore 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 device sc_dev;
76 1.1 gdamore struct disk sc_dk;
77 1.1 gdamore
78 1.1 gdamore struct spiflash_hw_if sc_hw;
79 1.1 gdamore void *sc_cookie;
80 1.1 gdamore
81 1.1 gdamore const char *sc_name;
82 1.1 gdamore struct spi_handle *sc_handle;
83 1.1 gdamore int sc_device_size;
84 1.1 gdamore int sc_write_size;
85 1.1 gdamore int sc_erase_size;
86 1.1 gdamore int sc_read_size;
87 1.1 gdamore int sc_device_blks;
88 1.1 gdamore
89 1.1 gdamore struct bufq_state *sc_bufq;
90 1.1 gdamore struct proc *sc_thread;
91 1.1 gdamore };
92 1.1 gdamore
93 1.1 gdamore #define sc_getname sc_hw.sf_getname
94 1.1 gdamore #define sc_gethandle sc_hw.sf_gethandle
95 1.1 gdamore #define sc_getsize sc_hw.sf_getsize
96 1.1 gdamore #define sc_getflags sc_hw.sf_getflags
97 1.1 gdamore #define sc_erase sc_hw.sf_erase
98 1.1 gdamore #define sc_write sc_hw.sf_write
99 1.1 gdamore #define sc_read sc_hw.sf_read
100 1.1 gdamore #define sc_getstatus sc_hw.sf_getstatus
101 1.1 gdamore #define sc_setstatus sc_hw.sf_setstatus
102 1.1 gdamore
103 1.1 gdamore struct spiflash_attach_args {
104 1.1 gdamore const struct spiflash_hw_if *hw;
105 1.1 gdamore void *cookie;
106 1.1 gdamore };
107 1.1 gdamore
108 1.1 gdamore #define STATIC
109 1.1 gdamore STATIC int spiflash_match(struct device *, struct cfdata *, void *);
110 1.1 gdamore STATIC void spiflash_attach(struct device *, struct device *, void *);
111 1.1 gdamore STATIC int spiflash_print(void *, const char *);
112 1.1 gdamore STATIC int spiflash_common_erase(spiflash_handle_t, size_t, size_t);
113 1.1 gdamore STATIC int spiflash_common_write(spiflash_handle_t, size_t, size_t,
114 1.1 gdamore const uint8_t *);
115 1.1 gdamore STATIC int spiflash_common_read(spiflash_handle_t, size_t, size_t, uint8_t *);
116 1.1 gdamore STATIC void spiflash_process(spiflash_handle_t, struct buf *);
117 1.1 gdamore STATIC void spiflash_thread(void *);
118 1.1 gdamore STATIC void spiflash_thread_create(void *);
119 1.1 gdamore
120 1.1 gdamore CFATTACH_DECL(spiflash, sizeof(struct spiflash_softc),
121 1.1 gdamore spiflash_match, spiflash_attach, NULL, NULL);
122 1.1 gdamore
123 1.1 gdamore extern struct cfdriver spiflash_cd;
124 1.1 gdamore
125 1.1 gdamore dev_type_open(spiflash_open);
126 1.1 gdamore dev_type_close(spiflash_close);
127 1.1 gdamore dev_type_read(spiflash_read);
128 1.1 gdamore dev_type_write(spiflash_write);
129 1.1 gdamore dev_type_ioctl(spiflash_ioctl);
130 1.1 gdamore dev_type_strategy(spiflash_strategy);
131 1.1 gdamore
132 1.1 gdamore const struct bdevsw spiflash_bdevsw = {
133 1.1 gdamore .d_open = spiflash_open,
134 1.1 gdamore .d_close = spiflash_close,
135 1.1 gdamore .d_strategy = spiflash_strategy,
136 1.1 gdamore .d_ioctl = spiflash_ioctl,
137 1.1 gdamore .d_dump = nodump,
138 1.1 gdamore .d_psize = nosize,
139 1.1 gdamore .d_type = D_DISK,
140 1.1 gdamore };
141 1.1 gdamore
142 1.1 gdamore const struct cdevsw spiflash_cdevsw = {
143 1.1 gdamore .d_open = spiflash_open,
144 1.1 gdamore .d_close = spiflash_close,
145 1.1 gdamore .d_read = spiflash_read,
146 1.1 gdamore .d_write = spiflash_write,
147 1.1 gdamore .d_ioctl = spiflash_ioctl,
148 1.1 gdamore .d_stop = nostop,
149 1.1 gdamore .d_tty = notty,
150 1.1 gdamore .d_poll = nopoll,
151 1.1 gdamore .d_mmap = nommap,
152 1.1 gdamore .d_kqfilter = nokqfilter,
153 1.1 gdamore .d_type = D_DISK,
154 1.1 gdamore };
155 1.1 gdamore
156 1.1 gdamore static struct dkdriver spiflash_dkdriver = { spiflash_strategy, NULL };
157 1.1 gdamore
158 1.1 gdamore spiflash_handle_t
159 1.1 gdamore spiflash_attach_mi(const struct spiflash_hw_if *hw, void *cookie,
160 1.1 gdamore struct device *dev)
161 1.1 gdamore {
162 1.1 gdamore struct spiflash_attach_args sfa;
163 1.1 gdamore sfa.hw = hw;
164 1.1 gdamore sfa.cookie = cookie;
165 1.1 gdamore
166 1.1 gdamore return (spiflash_handle_t)config_found(dev, &sfa, spiflash_print);
167 1.1 gdamore }
168 1.1 gdamore
169 1.1 gdamore int
170 1.1 gdamore spiflash_print(void *aux, const char *pnp)
171 1.1 gdamore {
172 1.1 gdamore if (pnp != NULL)
173 1.1 gdamore printf("spiflash at %s\n", pnp);
174 1.1 gdamore
175 1.1 gdamore return UNCONF;
176 1.1 gdamore }
177 1.1 gdamore
178 1.1 gdamore int
179 1.1 gdamore spiflash_match(struct device *parent, struct cfdata *cf, void *aux)
180 1.1 gdamore {
181 1.1 gdamore
182 1.1 gdamore return 1;
183 1.1 gdamore }
184 1.1 gdamore
185 1.1 gdamore void
186 1.1 gdamore spiflash_attach(struct device *parent, struct device *self, void *aux)
187 1.1 gdamore {
188 1.1 gdamore struct spiflash_softc *sc = device_private(self);
189 1.1 gdamore struct spiflash_attach_args *sfa = aux;
190 1.1 gdamore void *cookie = sfa->cookie;
191 1.1 gdamore
192 1.1 gdamore sc->sc_hw = *sfa->hw;
193 1.1 gdamore sc->sc_cookie = cookie;
194 1.1 gdamore sc->sc_name = sc->sc_getname(cookie);
195 1.1 gdamore sc->sc_handle = sc->sc_gethandle(cookie);
196 1.1 gdamore sc->sc_device_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_DEVICE);
197 1.1 gdamore sc->sc_erase_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_ERASE);
198 1.1 gdamore sc->sc_write_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_WRITE);
199 1.1 gdamore sc->sc_read_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_READ);
200 1.1 gdamore sc->sc_device_blks = sc->sc_device_size / DEV_BSIZE;
201 1.1 gdamore
202 1.1 gdamore if (sc->sc_read == NULL)
203 1.1 gdamore sc->sc_read = spiflash_common_read;
204 1.1 gdamore if (sc->sc_write == NULL)
205 1.1 gdamore sc->sc_write = spiflash_common_write;
206 1.1 gdamore if (sc->sc_erase == NULL)
207 1.1 gdamore sc->sc_erase = spiflash_common_erase;
208 1.1 gdamore
209 1.1 gdamore aprint_naive(": SPI flash\n");
210 1.1 gdamore aprint_normal(": %s SPI flash\n", sc->sc_name);
211 1.1 gdamore /* XXX: note that this has to change for boot-sectored flash */
212 1.1 gdamore aprint_normal("%s: %d KB, %d sectors of %d KB each\n",
213 1.1 gdamore sc->sc_dev.dv_xname, sc->sc_device_size / 1024,
214 1.1 gdamore sc->sc_device_size / sc->sc_erase_size,
215 1.1 gdamore sc->sc_erase_size / 1024);
216 1.1 gdamore
217 1.1 gdamore /* first-come first-served strategy works best for us */
218 1.1 gdamore bufq_alloc(&sc->sc_bufq, "fcfs", BUFQ_SORT_RAWBLOCK);
219 1.1 gdamore
220 1.1 gdamore /* arrange to allocate the kthread */
221 1.1 gdamore kthread_create(spiflash_thread_create, sc);
222 1.1 gdamore
223 1.1 gdamore sc->sc_dk.dk_driver = &spiflash_dkdriver;
224 1.1 gdamore sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
225 1.1 gdamore
226 1.1 gdamore disk_attach(&sc->sc_dk);
227 1.1 gdamore }
228 1.1 gdamore
229 1.1 gdamore int
230 1.1 gdamore spiflash_open(dev_t dev, int flags, int mode, struct lwp *l)
231 1.1 gdamore {
232 1.1 gdamore spiflash_handle_t sc;
233 1.1 gdamore
234 1.1 gdamore if ((sc = device_lookup(&spiflash_cd, DISKUNIT(dev))) == NULL)
235 1.1 gdamore return ENXIO;
236 1.1 gdamore
237 1.1 gdamore /*
238 1.1 gdamore * XXX: We need to handle partitions here. The problem is
239 1.1 gdamore * that it isn't entirely clear to me how to deal with this.
240 1.1 gdamore * There are devices that could be used "in the raw" with a
241 1.1 gdamore * NetBSD label, but then you get into devices that have other
242 1.1 gdamore * kinds of data on them -- some have VxWorks data, some have
243 1.1 gdamore * RedBoot data, and some have other contraints -- for example
244 1.1 gdamore * some devices might have a portion that is read-only,
245 1.1 gdamore * whereas others might have a portion that is read-write.
246 1.1 gdamore *
247 1.1 gdamore * For now we just permit access to the entire device.
248 1.1 gdamore */
249 1.1 gdamore return 0;
250 1.1 gdamore }
251 1.1 gdamore
252 1.1 gdamore int
253 1.1 gdamore spiflash_close(dev_t dev, int flags, int mode, struct lwp *l)
254 1.1 gdamore {
255 1.1 gdamore spiflash_handle_t sc;
256 1.1 gdamore
257 1.1 gdamore if ((sc = device_lookup(&spiflash_cd, DISKUNIT(dev))) == NULL)
258 1.1 gdamore return ENXIO;
259 1.1 gdamore
260 1.1 gdamore return 0;
261 1.1 gdamore }
262 1.1 gdamore
263 1.1 gdamore int
264 1.1 gdamore spiflash_read(dev_t dev, struct uio *uio, int ioflag)
265 1.1 gdamore {
266 1.1 gdamore
267 1.1 gdamore return physio(spiflash_strategy, NULL, dev, B_READ, minphys, uio);
268 1.1 gdamore }
269 1.1 gdamore
270 1.1 gdamore int
271 1.1 gdamore spiflash_write(dev_t dev, struct uio *uio, int ioflag)
272 1.1 gdamore {
273 1.1 gdamore
274 1.1 gdamore return physio(spiflash_strategy, NULL, dev, B_WRITE, minphys, uio);
275 1.1 gdamore }
276 1.1 gdamore
277 1.1 gdamore int
278 1.1 gdamore spiflash_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct lwp *l)
279 1.1 gdamore {
280 1.1 gdamore spiflash_handle_t sc;
281 1.1 gdamore
282 1.1 gdamore if ((sc = device_lookup(&spiflash_cd, DISKUNIT(dev))) == NULL)
283 1.1 gdamore return ENXIO;
284 1.1 gdamore
285 1.1 gdamore return EINVAL;
286 1.1 gdamore }
287 1.1 gdamore
288 1.1 gdamore void
289 1.1 gdamore spiflash_strategy(struct buf *bp)
290 1.1 gdamore {
291 1.1 gdamore spiflash_handle_t sc;
292 1.1 gdamore int sz;
293 1.1 gdamore int s;
294 1.1 gdamore
295 1.1 gdamore sc = device_lookup(&spiflash_cd, DISKUNIT(bp->b_dev));
296 1.1 gdamore if (sc == NULL) {
297 1.1 gdamore bp->b_error = ENXIO;
298 1.1 gdamore bp->b_flags |= B_ERROR;
299 1.1 gdamore biodone(bp);
300 1.1 gdamore return;
301 1.1 gdamore }
302 1.1 gdamore
303 1.1 gdamore if ((bp->b_bcount % sc->sc_write_size) ||
304 1.1 gdamore (bp->b_blkno < 0)) {
305 1.1 gdamore bp->b_error = EINVAL;
306 1.1 gdamore bp->b_flags |= B_ERROR;
307 1.1 gdamore biodone(bp);
308 1.1 gdamore return;
309 1.1 gdamore }
310 1.1 gdamore
311 1.1 gdamore /* no work? */
312 1.1 gdamore if (bp->b_bcount == 0) {
313 1.1 gdamore biodone(bp);
314 1.1 gdamore return;
315 1.1 gdamore }
316 1.1 gdamore
317 1.1 gdamore sz = bp->b_bcount / DEV_BSIZE;
318 1.1 gdamore
319 1.1 gdamore if ((bp->b_blkno + sz) > sc->sc_device_blks) {
320 1.1 gdamore sz = sc->sc_device_blks - bp->b_blkno;
321 1.1 gdamore /* exactly at end of media? return EOF */
322 1.1 gdamore if (sz == 0) {
323 1.1 gdamore biodone(bp);
324 1.1 gdamore return;
325 1.1 gdamore }
326 1.1 gdamore if (sz < 0) {
327 1.1 gdamore /* past end of disk */
328 1.1 gdamore bp->b_error = EINVAL;
329 1.1 gdamore bp->b_flags |= B_ERROR;
330 1.1 gdamore biodone(bp);
331 1.1 gdamore }
332 1.1 gdamore /* otherwise truncate it */
333 1.1 gdamore bp->b_bcount = sz << DEV_BSHIFT;
334 1.1 gdamore }
335 1.1 gdamore
336 1.1 gdamore bp->b_resid = bp->b_bcount;
337 1.1 gdamore
338 1.1 gdamore /* all ready, hand off to thread for async processing */
339 1.1 gdamore s = splbio();
340 1.1 gdamore BUFQ_PUT(sc->sc_bufq, bp);
341 1.1 gdamore wakeup(&sc->sc_thread);
342 1.1 gdamore splx(s);
343 1.1 gdamore }
344 1.1 gdamore
345 1.1 gdamore void
346 1.1 gdamore spiflash_process(spiflash_handle_t sc, struct buf *bp)
347 1.1 gdamore {
348 1.1 gdamore int cnt;
349 1.1 gdamore size_t addr;
350 1.1 gdamore uint8_t *data;
351 1.1 gdamore
352 1.1 gdamore addr = bp->b_blkno * DEV_BSIZE;
353 1.1 gdamore data = bp->b_data;
354 1.1 gdamore
355 1.1 gdamore while (bp->b_resid > 0) {
356 1.1 gdamore cnt = max(sc->sc_write_size, DEV_BSIZE);
357 1.1 gdamore if (bp->b_flags & B_READ) {
358 1.1 gdamore bp->b_error = sc->sc_read(sc, addr, cnt, data);
359 1.1 gdamore } else {
360 1.1 gdamore bp->b_error = sc->sc_write(sc, addr, cnt, data);
361 1.1 gdamore }
362 1.1 gdamore if (bp->b_error) {
363 1.1 gdamore bp->b_flags |= B_ERROR;
364 1.1 gdamore biodone(bp);
365 1.1 gdamore return;
366 1.1 gdamore }
367 1.1 gdamore bp->b_resid -= cnt;
368 1.1 gdamore data += cnt;
369 1.1 gdamore addr += cnt;
370 1.1 gdamore }
371 1.1 gdamore biodone(bp);
372 1.1 gdamore }
373 1.1 gdamore
374 1.1 gdamore void
375 1.1 gdamore spiflash_thread(void *arg)
376 1.1 gdamore {
377 1.1 gdamore spiflash_handle_t sc = arg;
378 1.1 gdamore struct buf *bp;
379 1.1 gdamore int s;
380 1.1 gdamore
381 1.1 gdamore s = splbio();
382 1.1 gdamore for (;;) {
383 1.1 gdamore if ((bp = BUFQ_GET(sc->sc_bufq)) == NULL) {
384 1.1 gdamore tsleep(&sc->sc_thread, PRIBIO, "spiflash_thread", 0);
385 1.1 gdamore continue;
386 1.1 gdamore }
387 1.1 gdamore
388 1.1 gdamore spiflash_process(sc, bp);
389 1.1 gdamore }
390 1.1 gdamore }
391 1.1 gdamore
392 1.1 gdamore void
393 1.1 gdamore spiflash_thread_create(void *arg)
394 1.1 gdamore {
395 1.1 gdamore spiflash_handle_t sc = arg;
396 1.1 gdamore
397 1.1 gdamore kthread_create1(spiflash_thread, arg, &sc->sc_thread,
398 1.1 gdamore "spiflash_thread");
399 1.1 gdamore }
400 1.1 gdamore
401 1.1 gdamore /*
402 1.1 gdamore * SPI flash common implementation.
403 1.1 gdamore */
404 1.1 gdamore
405 1.1 gdamore /*
406 1.1 gdamore * Most devices take on the order of 1 second for each block that they
407 1.1 gdamore * delete.
408 1.1 gdamore */
409 1.1 gdamore int
410 1.1 gdamore spiflash_common_erase(spiflash_handle_t sc, size_t start, size_t size)
411 1.1 gdamore {
412 1.1 gdamore int rv;
413 1.1 gdamore
414 1.1 gdamore if ((start % sc->sc_erase_size) || (size % sc->sc_erase_size))
415 1.1 gdamore return EINVAL;
416 1.1 gdamore
417 1.1 gdamore /* the second test is to test against wrap */
418 1.1 gdamore if ((start > sc->sc_device_size) ||
419 1.1 gdamore ((start + size) > sc->sc_device_size))
420 1.1 gdamore return EINVAL;
421 1.1 gdamore
422 1.1 gdamore /*
423 1.1 gdamore * XXX: check protection status? Requires master table mapping
424 1.1 gdamore * sectors to status bits, and so forth.
425 1.1 gdamore */
426 1.1 gdamore
427 1.1 gdamore while (size) {
428 1.1 gdamore if ((rv = spiflash_write_enable(sc)) != 0) {
429 1.1 gdamore spiflash_write_disable(sc);
430 1.1 gdamore return rv;
431 1.1 gdamore }
432 1.1 gdamore if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_ERASE, 3, start, 0,
433 1.1 gdamore NULL, NULL)) != 0) {
434 1.1 gdamore spiflash_write_disable(sc);
435 1.1 gdamore return rv;
436 1.1 gdamore }
437 1.1 gdamore
438 1.1 gdamore /*
439 1.1 gdamore * The devices I have all say typical for sector erase
440 1.1 gdamore * is ~1sec. We check ten times that often. (There
441 1.1 gdamore * is no way to interrupt on this.)
442 1.1 gdamore */
443 1.1 gdamore if ((rv = spiflash_wait(sc, hz / 10)) != 0)
444 1.1 gdamore return rv;
445 1.1 gdamore
446 1.1 gdamore start += sc->sc_erase_size;
447 1.1 gdamore size -= sc->sc_erase_size;
448 1.1 gdamore
449 1.1 gdamore /* NB: according to the docs I have, the write enable
450 1.1 gdamore * is automatically cleared upon completion of an erase
451 1.1 gdamore * command, so there is no need to explicitly disable it.
452 1.1 gdamore */
453 1.1 gdamore }
454 1.1 gdamore
455 1.1 gdamore return 0;
456 1.1 gdamore }
457 1.1 gdamore
458 1.1 gdamore int
459 1.1 gdamore spiflash_common_write(spiflash_handle_t sc, size_t start, size_t size,
460 1.1 gdamore const uint8_t *data)
461 1.1 gdamore {
462 1.1 gdamore int rv;
463 1.1 gdamore
464 1.1 gdamore if ((start % sc->sc_write_size) || (size % sc->sc_write_size))
465 1.1 gdamore return EINVAL;
466 1.1 gdamore
467 1.1 gdamore /* the second test is to test against wrap */
468 1.1 gdamore if ((start > sc->sc_device_size) ||
469 1.1 gdamore ((start + size) > sc->sc_device_size))
470 1.1 gdamore return EINVAL;
471 1.1 gdamore
472 1.1 gdamore while (size) {
473 1.1 gdamore int cnt;
474 1.1 gdamore
475 1.1 gdamore if ((rv = spiflash_write_enable(sc)) != 0) {
476 1.1 gdamore spiflash_write_disable(sc);
477 1.1 gdamore return rv;
478 1.1 gdamore }
479 1.1 gdamore
480 1.1 gdamore cnt = min(size, sc->sc_write_size);
481 1.1 gdamore if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_PROGRAM, 3, start,
482 1.1 gdamore cnt, data, NULL)) != 0) {
483 1.1 gdamore spiflash_write_disable(sc);
484 1.1 gdamore return rv;
485 1.1 gdamore }
486 1.1 gdamore
487 1.1 gdamore /*
488 1.1 gdamore * It seems that most devices can write bits fairly
489 1.1 gdamore * quickly. For example, one part I have access to
490 1.1 gdamore * takes ~5msec to process the entire 256 byte page.
491 1.1 gdamore * Probably this should be modified to cope with
492 1.1 gdamore * device-specific timing, and maybe also take into
493 1.1 gdamore * account systems with higher values of HZ (which
494 1.1 gdamore * could benefit from sleeping.)
495 1.1 gdamore */
496 1.1 gdamore if ((rv = spiflash_wait(sc, 0)) != 0)
497 1.1 gdamore return rv;
498 1.1 gdamore
499 1.1 gdamore start += cnt;
500 1.1 gdamore size -= cnt;
501 1.1 gdamore }
502 1.1 gdamore
503 1.1 gdamore return 0;
504 1.1 gdamore }
505 1.1 gdamore
506 1.1 gdamore int
507 1.1 gdamore spiflash_common_read(spiflash_handle_t sc, size_t start, size_t size,
508 1.1 gdamore uint8_t *data)
509 1.1 gdamore {
510 1.1 gdamore int rv;
511 1.1 gdamore int align;
512 1.1 gdamore
513 1.1 gdamore align = sc->sc_write_size;
514 1.1 gdamore if (sc->sc_read_size > 0)
515 1.1 gdamore align = sc->sc_read_size;
516 1.1 gdamore
517 1.1 gdamore if ((start % align) || (size % align))
518 1.1 gdamore return EINVAL;
519 1.1 gdamore
520 1.1 gdamore /* the second test is to test against wrap */
521 1.1 gdamore if ((start > sc->sc_device_size) ||
522 1.1 gdamore ((start + size) > sc->sc_device_size))
523 1.1 gdamore return EINVAL;
524 1.1 gdamore
525 1.1 gdamore while (size) {
526 1.1 gdamore int cnt;
527 1.1 gdamore
528 1.1 gdamore if ((rv = spiflash_write_enable(sc)) != 0) {
529 1.1 gdamore spiflash_write_disable(sc);
530 1.1 gdamore return rv;
531 1.1 gdamore }
532 1.1 gdamore
533 1.1 gdamore if (sc->sc_read_size > 0)
534 1.1 gdamore cnt = min(size, sc->sc_read_size);
535 1.1 gdamore else
536 1.1 gdamore cnt = size;
537 1.1 gdamore
538 1.1 gdamore if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_READ, 3, start,
539 1.1 gdamore cnt, NULL, data)) != 0) {
540 1.1 gdamore spiflash_write_disable(sc);
541 1.1 gdamore return rv;
542 1.1 gdamore }
543 1.1 gdamore
544 1.1 gdamore start += cnt;
545 1.1 gdamore size -= cnt;
546 1.1 gdamore }
547 1.1 gdamore
548 1.1 gdamore return 0;
549 1.1 gdamore }
550 1.1 gdamore
551 1.1 gdamore /* read status register */
552 1.1 gdamore int
553 1.1 gdamore spiflash_read_status(spiflash_handle_t sc, uint8_t *sr)
554 1.1 gdamore {
555 1.1 gdamore
556 1.1 gdamore return spiflash_cmd(sc, SPIFLASH_CMD_RDSR, 0, 0, 1, NULL, sr);
557 1.1 gdamore }
558 1.1 gdamore
559 1.1 gdamore int
560 1.1 gdamore spiflash_write_enable(spiflash_handle_t sc)
561 1.1 gdamore {
562 1.1 gdamore
563 1.1 gdamore return spiflash_cmd(sc, SPIFLASH_CMD_WREN, 0, 0, 0, NULL, NULL);
564 1.1 gdamore }
565 1.1 gdamore
566 1.1 gdamore int
567 1.1 gdamore spiflash_write_disable(spiflash_handle_t sc)
568 1.1 gdamore {
569 1.1 gdamore
570 1.1 gdamore return spiflash_cmd(sc, SPIFLASH_CMD_WRDI, 0, 0, 0, NULL, NULL);
571 1.1 gdamore }
572 1.1 gdamore
573 1.1 gdamore int
574 1.1 gdamore spiflash_cmd(spiflash_handle_t sc, uint8_t cmd,
575 1.1 gdamore size_t addrlen, uint32_t addr,
576 1.1 gdamore size_t cnt, const uint8_t *wdata, uint8_t *rdata)
577 1.1 gdamore {
578 1.1 gdamore struct spi_transfer trans;
579 1.1 gdamore struct spi_chunk chunk1, chunk2;
580 1.1 gdamore char buf[4];
581 1.1 gdamore int i;
582 1.1 gdamore
583 1.1 gdamore buf[0] = cmd;
584 1.1 gdamore
585 1.1 gdamore if (addrlen > 3)
586 1.1 gdamore return EINVAL;
587 1.1 gdamore
588 1.1 gdamore for (i = addrlen; i > 0; i--) {
589 1.1 gdamore buf[i] = (addr >> ((i - 1) * 8)) & 0xff;
590 1.1 gdamore }
591 1.1 gdamore spi_transfer_init(&trans);
592 1.1 gdamore spi_chunk_init(&chunk1, addrlen + 1, buf, NULL);
593 1.1 gdamore spi_transfer_add(&trans, &chunk1);
594 1.1 gdamore if (cnt) {
595 1.1 gdamore spi_chunk_init(&chunk2, cnt, wdata, rdata);
596 1.1 gdamore spi_transfer_add(&trans, &chunk2);
597 1.1 gdamore }
598 1.1 gdamore
599 1.1 gdamore spi_transfer(sc->sc_handle, &trans);
600 1.1 gdamore spi_wait(&trans);
601 1.1 gdamore
602 1.1 gdamore if (trans.st_flags & SPI_F_ERROR)
603 1.1 gdamore return trans.st_errno;
604 1.1 gdamore return 0;
605 1.1 gdamore }
606 1.1 gdamore
607 1.1 gdamore int
608 1.1 gdamore spiflash_wait(spiflash_handle_t sc, int tmo)
609 1.1 gdamore {
610 1.1 gdamore int rv;
611 1.1 gdamore uint8_t sr;
612 1.1 gdamore
613 1.1 gdamore for (;;) {
614 1.1 gdamore if ((rv = spiflash_read_status(sc, &sr)) != 0)
615 1.1 gdamore return rv;
616 1.1 gdamore
617 1.1 gdamore if ((sr & SPIFLASH_SR_BUSY) == 0)
618 1.1 gdamore break;
619 1.1 gdamore /*
620 1.1 gdamore * The devices I have all say typical for sector
621 1.1 gdamore * erase is ~1sec. We check time times that often.
622 1.1 gdamore * (There is no way to interrupt on this.)
623 1.1 gdamore */
624 1.1 gdamore if (tmo)
625 1.1 gdamore tsleep(&sr, PWAIT, "spiflash_wait", tmo);
626 1.1 gdamore }
627 1.1 gdamore return 0;
628 1.1 gdamore }
629