xref: /freebsd/sys/dev/flash/mx25l.c (revision 6b7b2d80ed4d728d3ffd12c422e57798c1b63a84)
1 /*-
2  * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3  * Copyright (c) 2009 Oleksandr Tymoshenko.  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 WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include "opt_platform.h"
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bio.h>
34 #include <sys/bus.h>
35 #include <sys/conf.h>
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/lock.h>
39 #include <sys/mbuf.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/mutex.h>
43 #include <geom/geom_disk.h>
44 
45 #ifdef FDT
46 #include <dev/fdt/fdt_common.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48 #include <dev/ofw/openfirm.h>
49 #endif
50 
51 #include <dev/spibus/spi.h>
52 #include "spibus_if.h"
53 
54 #include <dev/flash/mx25lreg.h>
55 
56 #define	FL_NONE			0x00
57 #define	FL_ERASE_4K		0x01
58 #define	FL_ERASE_32K		0x02
59 
60 /*
61  * Define the sectorsize to be a smaller size rather than the flash
62  * sector size. Trying to run FFS off of a 64k flash sector size
63  * results in a completely un-usable system.
64  */
65 #define	MX25L_SECTORSIZE	512
66 
67 struct mx25l_flash_ident
68 {
69 	const char	*name;
70 	uint8_t		manufacturer_id;
71 	uint16_t	device_id;
72 	unsigned int	sectorsize;
73 	unsigned int	sectorcount;
74 	unsigned int	flags;
75 };
76 
77 struct mx25l_softc
78 {
79 	device_t	sc_dev;
80 	uint8_t		sc_manufacturer_id;
81 	uint16_t	sc_device_id;
82 	unsigned int	sc_sectorsize;
83 	struct mtx	sc_mtx;
84 	struct disk	*sc_disk;
85 	struct proc	*sc_p;
86 	struct bio_queue_head sc_bio_queue;
87 	unsigned int	sc_flags;
88 };
89 
90 #define M25PXX_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
91 #define	M25PXX_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
92 #define M25PXX_LOCK_INIT(_sc) \
93 	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
94 	    "mx25l", MTX_DEF)
95 #define M25PXX_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
96 #define M25PXX_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
97 #define M25PXX_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
98 
99 /* disk routines */
100 static int mx25l_open(struct disk *dp);
101 static int mx25l_close(struct disk *dp);
102 static int mx25l_ioctl(struct disk *, u_long, void *, int, struct thread *);
103 static void mx25l_strategy(struct bio *bp);
104 static int mx25l_getattr(struct bio *bp);
105 static void mx25l_task(void *arg);
106 
107 struct mx25l_flash_ident flash_devices[] = {
108 	{ "en25f32",	0x1c, 0x3116, 64 * 1024, 64, FL_NONE },
109 	{ "en25p32",	0x1c, 0x2016, 64 * 1024, 64, FL_NONE },
110 	{ "en25p64",	0x1c, 0x2017, 64 * 1024, 128, FL_NONE },
111 	{ "en25q64",	0x1c, 0x3017, 64 * 1024, 128, FL_ERASE_4K },
112 	{ "m25p64",	0x20, 0x2017, 64 * 1024, 128, FL_NONE },
113 	{ "mx25ll32",	0xc2, 0x2016, 64 * 1024, 64, FL_NONE },
114 	{ "mx25ll64",	0xc2, 0x2017, 64 * 1024, 128, FL_NONE },
115 	{ "mx25ll128",	0xc2, 0x2018, 64 * 1024, 256, FL_ERASE_4K | FL_ERASE_32K },
116 	{ "s25fl032",	0x01, 0x0215, 64 * 1024, 64, FL_NONE },
117 	{ "s25fl064",	0x01, 0x0216, 64 * 1024, 128, FL_NONE },
118 	{ "s25fl128",	0x01, 0x2018, 64 * 1024, 256, FL_NONE },
119 	{ "s25fl256s",	0x01, 0x0219, 64 * 1024, 512, FL_NONE },
120 	{ "SST25VF032B", 0xbf, 0x254a, 64 * 1024, 64, FL_ERASE_4K | FL_ERASE_32K },
121 
122 	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
123 	{ "w25x32",	0xef, 0x3016, 64 * 1024, 64, FL_ERASE_4K },
124 	{ "w25q32",	0xef, 0x4016, 64 * 1024, 64, FL_ERASE_4K },
125 	{ "w25q64",	0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K },
126 	{ "w25q64bv",	0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K },
127 	{ "w25q128",	0xef, 0x4018, 64 * 1024, 256, FL_ERASE_4K },
128 	{ "w25q256",	0xef, 0x4019, 64 * 1024, 512, FL_ERASE_4K },
129 };
130 
131 static uint8_t
132 mx25l_get_status(device_t dev)
133 {
134 	uint8_t txBuf[2], rxBuf[2];
135 	struct spi_command cmd;
136 	int err;
137 
138 	memset(&cmd, 0, sizeof(cmd));
139 	memset(txBuf, 0, sizeof(txBuf));
140 	memset(rxBuf, 0, sizeof(rxBuf));
141 
142 	txBuf[0] = CMD_READ_STATUS;
143 	cmd.tx_cmd = txBuf;
144 	cmd.rx_cmd = rxBuf;
145 	cmd.rx_cmd_sz = 2;
146 	cmd.tx_cmd_sz = 2;
147 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
148 	return (rxBuf[1]);
149 }
150 
151 static void
152 mx25l_wait_for_device_ready(device_t dev)
153 {
154 	while ((mx25l_get_status(dev) & STATUS_WIP))
155 		continue;
156 }
157 
158 static struct mx25l_flash_ident*
159 mx25l_get_device_ident(struct mx25l_softc *sc)
160 {
161 	device_t dev = sc->sc_dev;
162 	uint8_t txBuf[8], rxBuf[8];
163 	struct spi_command cmd;
164 	uint8_t manufacturer_id;
165 	uint16_t dev_id;
166 	int err, i;
167 
168 	memset(&cmd, 0, sizeof(cmd));
169 	memset(txBuf, 0, sizeof(txBuf));
170 	memset(rxBuf, 0, sizeof(rxBuf));
171 
172 	txBuf[0] = CMD_READ_IDENT;
173 	cmd.tx_cmd = &txBuf;
174 	cmd.rx_cmd = &rxBuf;
175 	/*
176 	 * Some compatible devices has extended two-bytes ID
177 	 * We'll use only manufacturer/deviceid atm
178 	 */
179 	cmd.tx_cmd_sz = 4;
180 	cmd.rx_cmd_sz = 4;
181 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
182 	if (err)
183 		return (NULL);
184 
185 	manufacturer_id = rxBuf[1];
186 	dev_id = (rxBuf[2] << 8) | (rxBuf[3]);
187 
188 	for (i = 0;
189 	    i < sizeof(flash_devices)/sizeof(struct mx25l_flash_ident); i++) {
190 		if ((flash_devices[i].manufacturer_id == manufacturer_id) &&
191 		    (flash_devices[i].device_id == dev_id))
192 			return &flash_devices[i];
193 	}
194 
195 	printf("Unknown SPI flash device. Vendor: %02x, device id: %04x\n",
196 	    manufacturer_id, dev_id);
197 	return (NULL);
198 }
199 
200 static void
201 mx25l_set_writable(device_t dev, int writable)
202 {
203 	uint8_t txBuf[1], rxBuf[1];
204 	struct spi_command cmd;
205 	int err;
206 
207 	memset(&cmd, 0, sizeof(cmd));
208 	memset(txBuf, 0, sizeof(txBuf));
209 	memset(rxBuf, 0, sizeof(rxBuf));
210 
211 	txBuf[0] = writable ? CMD_WRITE_ENABLE : CMD_WRITE_DISABLE;
212 	cmd.tx_cmd = txBuf;
213 	cmd.rx_cmd = rxBuf;
214 	cmd.rx_cmd_sz = 1;
215 	cmd.tx_cmd_sz = 1;
216 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
217 }
218 
219 static void
220 mx25l_erase_cmd(device_t dev, off_t sector, uint8_t ecmd)
221 {
222 	uint8_t txBuf[4], rxBuf[4];
223 	struct spi_command cmd;
224 	int err;
225 
226 	mx25l_wait_for_device_ready(dev);
227 	mx25l_set_writable(dev, 1);
228 
229 	memset(&cmd, 0, sizeof(cmd));
230 	memset(txBuf, 0, sizeof(txBuf));
231 	memset(rxBuf, 0, sizeof(rxBuf));
232 
233 	txBuf[0] = ecmd;
234 	cmd.tx_cmd = txBuf;
235 	cmd.rx_cmd = rxBuf;
236 	cmd.rx_cmd_sz = 4;
237 	cmd.tx_cmd_sz = 4;
238 	txBuf[1] = ((sector >> 16) & 0xff);
239 	txBuf[2] = ((sector >> 8) & 0xff);
240 	txBuf[3] = (sector & 0xff);
241 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
242 }
243 
244 static int
245 mx25l_write(device_t dev, off_t offset, caddr_t data, off_t count)
246 {
247 	struct mx25l_softc *sc;
248 	uint8_t txBuf[8], rxBuf[8];
249 	struct spi_command cmd;
250 	off_t write_offset;
251 	long bytes_to_write, bytes_writen;
252 	device_t pdev;
253 	int err = 0;
254 
255 	pdev = device_get_parent(dev);
256 	sc = device_get_softc(dev);
257 
258 	cmd.tx_cmd_sz = 4;
259 	cmd.rx_cmd_sz = 4;
260 
261 	bytes_writen = 0;
262 	write_offset = offset;
263 
264 	/*
265 	 * Use the erase sectorsize here since blocks are fully erased
266 	 * first before they're written to.
267 	 */
268 	if (count % sc->sc_sectorsize != 0 || offset % sc->sc_sectorsize != 0)
269 		return (EIO);
270 
271 	/*
272 	 * Assume here that we write per-sector only
273 	 * and sector size should be 256 bytes aligned
274 	 */
275 	KASSERT(write_offset % FLASH_PAGE_SIZE == 0,
276 	    ("offset for BIO_WRITE is not page size (%d bytes) aligned",
277 		FLASH_PAGE_SIZE));
278 
279 	/*
280 	 * Maximum write size for CMD_PAGE_PROGRAM is
281 	 * FLASH_PAGE_SIZE, so split data to chunks
282 	 * FLASH_PAGE_SIZE bytes eash and write them
283 	 * one by one
284 	 */
285 	while (bytes_writen < count) {
286 		/*
287 		 * If we crossed sector boundary - erase next sector
288 		 */
289 		if (((offset + bytes_writen) % sc->sc_sectorsize) == 0)
290 			mx25l_erase_cmd(dev, offset + bytes_writen, CMD_SECTOR_ERASE);
291 
292 		txBuf[0] = CMD_PAGE_PROGRAM;
293 		txBuf[1] = ((write_offset >> 16) & 0xff);
294 		txBuf[2] = ((write_offset >> 8) & 0xff);
295 		txBuf[3] = (write_offset & 0xff);
296 
297 		bytes_to_write = MIN(FLASH_PAGE_SIZE,
298 		    count - bytes_writen);
299 		cmd.tx_cmd = txBuf;
300 		cmd.rx_cmd = rxBuf;
301 		cmd.tx_data = data + bytes_writen;
302 		cmd.tx_data_sz = bytes_to_write;
303 		cmd.rx_data = data + bytes_writen;
304 		cmd.rx_data_sz = bytes_to_write;
305 
306 		/*
307 		 * Eash completed write operation resets WEL
308 		 * (write enable latch) to disabled state,
309 		 * so we re-enable it here
310 		 */
311 		mx25l_wait_for_device_ready(dev);
312 		mx25l_set_writable(dev, 1);
313 
314 		err = SPIBUS_TRANSFER(pdev, dev, &cmd);
315 		if (err)
316 			break;
317 
318 		bytes_writen += bytes_to_write;
319 		write_offset += bytes_to_write;
320 	}
321 
322 	return (err);
323 }
324 
325 static int
326 mx25l_read(device_t dev, off_t offset, caddr_t data, off_t count)
327 {
328 	struct mx25l_softc *sc;
329 	uint8_t txBuf[8], rxBuf[8];
330 	struct spi_command cmd;
331 	device_t pdev;
332 	int err = 0;
333 
334 	pdev = device_get_parent(dev);
335 	sc = device_get_softc(dev);
336 
337 	/*
338 	 * Enforce the disk read sectorsize not the erase sectorsize.
339 	 * In this way, smaller read IO is possible,dramatically
340 	 * speeding up filesystem/geom_compress access.
341 	 */
342 	if (count % sc->sc_disk->d_sectorsize != 0
343 	    || offset % sc->sc_disk->d_sectorsize != 0)
344 		return (EIO);
345 
346 	txBuf[0] = CMD_FAST_READ;
347 	cmd.tx_cmd_sz = 5;
348 	cmd.rx_cmd_sz = 5;
349 
350 	txBuf[1] = ((offset >> 16) & 0xff);
351 	txBuf[2] = ((offset >> 8) & 0xff);
352 	txBuf[3] = (offset & 0xff);
353 	/* Dummy byte */
354 	txBuf[4] = 0;
355 
356 	cmd.tx_cmd = txBuf;
357 	cmd.rx_cmd = rxBuf;
358 	cmd.tx_data = data;
359 	cmd.tx_data_sz = count;
360 	cmd.rx_data = data;
361 	cmd.rx_data_sz = count;
362 
363 	err = SPIBUS_TRANSFER(pdev, dev, &cmd);
364 
365 	return (err);
366 }
367 
368 static int
369 mx25l_probe(device_t dev)
370 {
371 
372 #ifdef FDT
373 	if (!ofw_bus_status_okay(dev))
374 		return (ENXIO);
375 	if (!ofw_bus_is_compatible(dev, "st,m25p"))
376 		return (ENXIO);
377 #endif
378 	device_set_desc(dev, "M25Pxx Flash Family");
379 
380 	return (0);
381 }
382 
383 static int
384 mx25l_attach(device_t dev)
385 {
386 	struct mx25l_softc *sc;
387 	struct mx25l_flash_ident *ident;
388 
389 	sc = device_get_softc(dev);
390 	sc->sc_dev = dev;
391 	M25PXX_LOCK_INIT(sc);
392 
393 	ident = mx25l_get_device_ident(sc);
394 	if (ident == NULL)
395 		return (ENXIO);
396 
397 	mx25l_wait_for_device_ready(sc->sc_dev);
398 
399 	sc->sc_disk = disk_alloc();
400 	sc->sc_disk->d_open = mx25l_open;
401 	sc->sc_disk->d_close = mx25l_close;
402 	sc->sc_disk->d_strategy = mx25l_strategy;
403 	sc->sc_disk->d_getattr = mx25l_getattr;
404 	sc->sc_disk->d_ioctl = mx25l_ioctl;
405 	sc->sc_disk->d_name = "flash/spi";
406 	sc->sc_disk->d_drv1 = sc;
407 	sc->sc_disk->d_maxsize = DFLTPHYS;
408 	sc->sc_disk->d_sectorsize = MX25L_SECTORSIZE;
409 	sc->sc_disk->d_mediasize = ident->sectorsize * ident->sectorcount;
410 	sc->sc_disk->d_unit = device_get_unit(sc->sc_dev);
411 	sc->sc_disk->d_dump = NULL;		/* NB: no dumps */
412 	/* Sectorsize for erase operations */
413 	sc->sc_sectorsize =  ident->sectorsize;
414 	sc->sc_flags = ident->flags;
415 
416         /* NB: use stripesize to hold the erase/region size for RedBoot */
417 	sc->sc_disk->d_stripesize = ident->sectorsize;
418 
419 	disk_create(sc->sc_disk, DISK_VERSION);
420 	bioq_init(&sc->sc_bio_queue);
421 
422 	kproc_create(&mx25l_task, sc, &sc->sc_p, 0, 0, "task: mx25l flash");
423 	device_printf(sc->sc_dev, "%s, sector %d bytes, %d sectors\n",
424 	    ident->name, ident->sectorsize, ident->sectorcount);
425 
426 	return (0);
427 }
428 
429 static int
430 mx25l_detach(device_t dev)
431 {
432 
433 	return (EIO);
434 }
435 
436 static int
437 mx25l_open(struct disk *dp)
438 {
439 	return (0);
440 }
441 
442 static int
443 mx25l_close(struct disk *dp)
444 {
445 
446 	return (0);
447 }
448 
449 static int
450 mx25l_ioctl(struct disk *dp, u_long cmd, void *data, int fflag,
451 	struct thread *td)
452 {
453 
454 	return (EINVAL);
455 }
456 
457 static void
458 mx25l_strategy(struct bio *bp)
459 {
460 	struct mx25l_softc *sc;
461 
462 	sc = (struct mx25l_softc *)bp->bio_disk->d_drv1;
463 	M25PXX_LOCK(sc);
464 	bioq_disksort(&sc->sc_bio_queue, bp);
465 	wakeup(sc);
466 	M25PXX_UNLOCK(sc);
467 }
468 
469 static int
470 mx25l_getattr(struct bio *bp)
471 {
472 	struct mx25l_softc *sc;
473 	device_t dev;
474 
475 	if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
476 		return (ENXIO);
477 
478 	sc = bp->bio_disk->d_drv1;
479 	dev = sc->sc_dev;
480 
481 	if (strcmp(bp->bio_attribute, "SPI::device") == 0) {
482 		if (bp->bio_length != sizeof(dev))
483 			return (EFAULT);
484 		bcopy(&dev, bp->bio_data, sizeof(dev));
485 	} else
486 		return (-1);
487 	return (0);
488 }
489 
490 static void
491 mx25l_task(void *arg)
492 {
493 	struct mx25l_softc *sc = (struct mx25l_softc*)arg;
494 	struct bio *bp;
495 	device_t dev;
496 
497 	for (;;) {
498 		dev = sc->sc_dev;
499 		M25PXX_LOCK(sc);
500 		do {
501 			bp = bioq_first(&sc->sc_bio_queue);
502 			if (bp == NULL)
503 				msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
504 		} while (bp == NULL);
505 		bioq_remove(&sc->sc_bio_queue, bp);
506 		M25PXX_UNLOCK(sc);
507 
508 		switch (bp->bio_cmd) {
509 		case BIO_READ:
510 			bp->bio_error = mx25l_read(dev, bp->bio_offset,
511 			    bp->bio_data, bp->bio_bcount);
512 			break;
513 		case BIO_WRITE:
514 			bp->bio_error = mx25l_write(dev, bp->bio_offset,
515 			    bp->bio_data, bp->bio_bcount);
516 			break;
517 		default:
518 			bp->bio_error = EINVAL;
519 		}
520 
521 
522 		biodone(bp);
523 	}
524 }
525 
526 static devclass_t mx25l_devclass;
527 
528 static device_method_t mx25l_methods[] = {
529 	/* Device interface */
530 	DEVMETHOD(device_probe,		mx25l_probe),
531 	DEVMETHOD(device_attach,	mx25l_attach),
532 	DEVMETHOD(device_detach,	mx25l_detach),
533 
534 	{ 0, 0 }
535 };
536 
537 static driver_t mx25l_driver = {
538 	"mx25l",
539 	mx25l_methods,
540 	sizeof(struct mx25l_softc),
541 };
542 
543 DRIVER_MODULE(mx25l, spibus, mx25l_driver, mx25l_devclass, 0, 0);
544