xref: /freebsd/sys/dev/flash/mx25l.c (revision c3655ab0d0a3a84bcc6b962e15ad09eafffc973e)
1cd5bdf03SOleksandr Tymoshenko /*-
2cd5bdf03SOleksandr Tymoshenko  * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3cd5bdf03SOleksandr Tymoshenko  * Copyright (c) 2009 Oleksandr Tymoshenko.  All rights reserved.
4cd5bdf03SOleksandr Tymoshenko  *
5cd5bdf03SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
6cd5bdf03SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
7cd5bdf03SOleksandr Tymoshenko  * are met:
8cd5bdf03SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
9cd5bdf03SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
10cd5bdf03SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
11cd5bdf03SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
12cd5bdf03SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
13cd5bdf03SOleksandr Tymoshenko  *
14cd5bdf03SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15cd5bdf03SOleksandr Tymoshenko  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16cd5bdf03SOleksandr Tymoshenko  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17cd5bdf03SOleksandr Tymoshenko  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18cd5bdf03SOleksandr Tymoshenko  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19cd5bdf03SOleksandr Tymoshenko  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20cd5bdf03SOleksandr Tymoshenko  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21cd5bdf03SOleksandr Tymoshenko  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22cd5bdf03SOleksandr Tymoshenko  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23cd5bdf03SOleksandr Tymoshenko  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24cd5bdf03SOleksandr Tymoshenko  */
25cd5bdf03SOleksandr Tymoshenko 
26cd5bdf03SOleksandr Tymoshenko #include <sys/cdefs.h>
27cd5bdf03SOleksandr Tymoshenko __FBSDID("$FreeBSD$");
28cd5bdf03SOleksandr Tymoshenko 
29cd5bdf03SOleksandr Tymoshenko #include <sys/param.h>
30cd5bdf03SOleksandr Tymoshenko #include <sys/systm.h>
31cd5bdf03SOleksandr Tymoshenko #include <sys/bio.h>
32cd5bdf03SOleksandr Tymoshenko #include <sys/bus.h>
33cd5bdf03SOleksandr Tymoshenko #include <sys/conf.h>
34cd5bdf03SOleksandr Tymoshenko #include <sys/kernel.h>
35cd5bdf03SOleksandr Tymoshenko #include <sys/kthread.h>
36cd5bdf03SOleksandr Tymoshenko #include <sys/lock.h>
37cd5bdf03SOleksandr Tymoshenko #include <sys/mbuf.h>
38cd5bdf03SOleksandr Tymoshenko #include <sys/malloc.h>
39cd5bdf03SOleksandr Tymoshenko #include <sys/module.h>
40cd5bdf03SOleksandr Tymoshenko #include <sys/mutex.h>
41cd5bdf03SOleksandr Tymoshenko #include <geom/geom_disk.h>
42cd5bdf03SOleksandr Tymoshenko 
43cd5bdf03SOleksandr Tymoshenko #include <dev/spibus/spi.h>
44cd5bdf03SOleksandr Tymoshenko #include "spibus_if.h"
45cd5bdf03SOleksandr Tymoshenko 
46cd5bdf03SOleksandr Tymoshenko #include <dev/flash/mx25lreg.h>
47cd5bdf03SOleksandr Tymoshenko 
48cd5bdf03SOleksandr Tymoshenko struct mx25l_flash_ident
49cd5bdf03SOleksandr Tymoshenko {
50cd5bdf03SOleksandr Tymoshenko 	const char	*name;
51cd5bdf03SOleksandr Tymoshenko 	uint8_t		manufacturer_id;
52cd5bdf03SOleksandr Tymoshenko 	uint16_t	device_id;
53cd5bdf03SOleksandr Tymoshenko 	unsigned int	sectorsize;
54cd5bdf03SOleksandr Tymoshenko 	unsigned int	sectorcount;
55cd5bdf03SOleksandr Tymoshenko };
56cd5bdf03SOleksandr Tymoshenko 
57cd5bdf03SOleksandr Tymoshenko struct mx25l_softc
58cd5bdf03SOleksandr Tymoshenko {
59cd5bdf03SOleksandr Tymoshenko 	device_t	sc_dev;
60cd5bdf03SOleksandr Tymoshenko 	uint8_t		sc_manufacturer_id;
61cd5bdf03SOleksandr Tymoshenko 	uint16_t	sc_device_id;
62cd5bdf03SOleksandr Tymoshenko 	struct mtx	sc_mtx;
63cd5bdf03SOleksandr Tymoshenko 	struct disk	*sc_disk;
64cd5bdf03SOleksandr Tymoshenko 	struct proc	*sc_p;
65cd5bdf03SOleksandr Tymoshenko 	struct bio_queue_head sc_bio_queue;
66cd5bdf03SOleksandr Tymoshenko };
67cd5bdf03SOleksandr Tymoshenko 
68cd5bdf03SOleksandr Tymoshenko #define M25PXX_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
69cd5bdf03SOleksandr Tymoshenko #define	M25PXX_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
70cd5bdf03SOleksandr Tymoshenko #define M25PXX_LOCK_INIT(_sc) \
71cd5bdf03SOleksandr Tymoshenko 	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
72cd5bdf03SOleksandr Tymoshenko 	    "mx25l", MTX_DEF)
73cd5bdf03SOleksandr Tymoshenko #define M25PXX_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
74cd5bdf03SOleksandr Tymoshenko #define M25PXX_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
75cd5bdf03SOleksandr Tymoshenko #define M25PXX_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
76cd5bdf03SOleksandr Tymoshenko 
77cd5bdf03SOleksandr Tymoshenko /* disk routines */
78cd5bdf03SOleksandr Tymoshenko static int mx25l_open(struct disk *dp);
79cd5bdf03SOleksandr Tymoshenko static int mx25l_close(struct disk *dp);
80cd5bdf03SOleksandr Tymoshenko static int mx25l_ioctl(struct disk *, u_long, void *, int, struct thread *);
81cd5bdf03SOleksandr Tymoshenko static void mx25l_strategy(struct bio *bp);
82cd5bdf03SOleksandr Tymoshenko static void mx25l_task(void *arg);
83cd5bdf03SOleksandr Tymoshenko 
84cd5bdf03SOleksandr Tymoshenko struct mx25l_flash_ident flash_devices[] = {
85cd5bdf03SOleksandr Tymoshenko 	{ "mx25ll32",  0xc2, 0x2016, 64 * 1024,  64 },
86cd5bdf03SOleksandr Tymoshenko 	{ "mx25ll64",  0xc2, 0x2017, 64 * 1024, 128 },
87cd5bdf03SOleksandr Tymoshenko 	{ "mx25ll128", 0xc2, 0x2018, 64 * 1024, 256 },
88cd5bdf03SOleksandr Tymoshenko };
89cd5bdf03SOleksandr Tymoshenko 
90cd5bdf03SOleksandr Tymoshenko static uint8_t
91cd5bdf03SOleksandr Tymoshenko mx25l_get_status(device_t dev)
92cd5bdf03SOleksandr Tymoshenko {
93cd5bdf03SOleksandr Tymoshenko 	uint8_t txBuf[2], rxBuf[2];
94cd5bdf03SOleksandr Tymoshenko 	struct spi_command cmd;
95cd5bdf03SOleksandr Tymoshenko 	int err;
96cd5bdf03SOleksandr Tymoshenko 
97cd5bdf03SOleksandr Tymoshenko 	memset(&cmd, 0, sizeof(cmd));
98cd5bdf03SOleksandr Tymoshenko 	memset(txBuf, 0, sizeof(txBuf));
99cd5bdf03SOleksandr Tymoshenko 	memset(rxBuf, 0, sizeof(rxBuf));
100cd5bdf03SOleksandr Tymoshenko 
101cd5bdf03SOleksandr Tymoshenko 	txBuf[0] = CMD_READ_STATUS;
102cd5bdf03SOleksandr Tymoshenko 	cmd.tx_cmd = txBuf;
103cd5bdf03SOleksandr Tymoshenko 	cmd.rx_cmd = rxBuf;
104cd5bdf03SOleksandr Tymoshenko 	cmd.rx_cmd_sz = 2;
105cd5bdf03SOleksandr Tymoshenko 	cmd.tx_cmd_sz = 2;
106cd5bdf03SOleksandr Tymoshenko 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
107cd5bdf03SOleksandr Tymoshenko 	return (rxBuf[1]);
108cd5bdf03SOleksandr Tymoshenko }
109cd5bdf03SOleksandr Tymoshenko 
110cd5bdf03SOleksandr Tymoshenko static void
111cd5bdf03SOleksandr Tymoshenko mx25l_wait_for_device_ready(device_t dev)
112cd5bdf03SOleksandr Tymoshenko {
113cd5bdf03SOleksandr Tymoshenko 	while ((mx25l_get_status(dev) & STATUS_WIP))
114cd5bdf03SOleksandr Tymoshenko 		continue;
115cd5bdf03SOleksandr Tymoshenko }
116cd5bdf03SOleksandr Tymoshenko 
117cd5bdf03SOleksandr Tymoshenko static struct mx25l_flash_ident*
118cd5bdf03SOleksandr Tymoshenko mx25l_get_device_ident(struct mx25l_softc *sc)
119cd5bdf03SOleksandr Tymoshenko {
120cd5bdf03SOleksandr Tymoshenko 	device_t dev = sc->sc_dev;
121cd5bdf03SOleksandr Tymoshenko 	uint8_t txBuf[8], rxBuf[8];
122cd5bdf03SOleksandr Tymoshenko 	struct spi_command cmd;
123cd5bdf03SOleksandr Tymoshenko 	uint8_t manufacturer_id;
124cd5bdf03SOleksandr Tymoshenko 	uint16_t dev_id;
125cd5bdf03SOleksandr Tymoshenko 	int err, i;
126cd5bdf03SOleksandr Tymoshenko 
127cd5bdf03SOleksandr Tymoshenko 	memset(&cmd, 0, sizeof(cmd));
128cd5bdf03SOleksandr Tymoshenko 	memset(txBuf, 0, sizeof(txBuf));
129cd5bdf03SOleksandr Tymoshenko 	memset(rxBuf, 0, sizeof(rxBuf));
130cd5bdf03SOleksandr Tymoshenko 
131cd5bdf03SOleksandr Tymoshenko 	txBuf[0] = CMD_READ_IDENT;
132cd5bdf03SOleksandr Tymoshenko 	cmd.tx_cmd = &txBuf;
133cd5bdf03SOleksandr Tymoshenko 	cmd.rx_cmd = &rxBuf;
134cd5bdf03SOleksandr Tymoshenko 	/*
135cd5bdf03SOleksandr Tymoshenko 	 * Some compatible devices has extended two-bytes ID
136cd5bdf03SOleksandr Tymoshenko 	 * We'll use only manufacturer/deviceid atm
137cd5bdf03SOleksandr Tymoshenko 	 */
138cd5bdf03SOleksandr Tymoshenko 	cmd.tx_cmd_sz = 4;
139cd5bdf03SOleksandr Tymoshenko 	cmd.rx_cmd_sz = 4;
140cd5bdf03SOleksandr Tymoshenko 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
141cd5bdf03SOleksandr Tymoshenko 	if (err)
142cd5bdf03SOleksandr Tymoshenko 		return (NULL);
143cd5bdf03SOleksandr Tymoshenko 
144cd5bdf03SOleksandr Tymoshenko 	manufacturer_id = rxBuf[1];
145cd5bdf03SOleksandr Tymoshenko 	dev_id = (rxBuf[2] << 8) | (rxBuf[3]);
146cd5bdf03SOleksandr Tymoshenko 
147cd5bdf03SOleksandr Tymoshenko 	for (i = 0;
148cd5bdf03SOleksandr Tymoshenko 	    i < sizeof(flash_devices)/sizeof(struct mx25l_flash_ident); i++) {
149cd5bdf03SOleksandr Tymoshenko 		if ((flash_devices[i].manufacturer_id == manufacturer_id) &&
150cd5bdf03SOleksandr Tymoshenko 		    (flash_devices[i].device_id == dev_id))
151cd5bdf03SOleksandr Tymoshenko 			return &flash_devices[i];
152cd5bdf03SOleksandr Tymoshenko 	}
153cd5bdf03SOleksandr Tymoshenko 
154cd5bdf03SOleksandr Tymoshenko 	printf("Unknown SPI flash device. Vendor: %02x, device id: %04x\n",
155cd5bdf03SOleksandr Tymoshenko 	    manufacturer_id, dev_id);
156cd5bdf03SOleksandr Tymoshenko 	return (NULL);
157cd5bdf03SOleksandr Tymoshenko }
158cd5bdf03SOleksandr Tymoshenko 
159c3655ab0SOleksandr Tymoshenko static void
160c3655ab0SOleksandr Tymoshenko mx25l_set_writable(device_t dev, int writable)
161c3655ab0SOleksandr Tymoshenko {
162c3655ab0SOleksandr Tymoshenko 	uint8_t txBuf[1], rxBuf[1];
163c3655ab0SOleksandr Tymoshenko 	struct spi_command cmd;
164c3655ab0SOleksandr Tymoshenko 	int err;
165c3655ab0SOleksandr Tymoshenko 
166c3655ab0SOleksandr Tymoshenko 	memset(&cmd, 0, sizeof(cmd));
167c3655ab0SOleksandr Tymoshenko 	memset(txBuf, 0, sizeof(txBuf));
168c3655ab0SOleksandr Tymoshenko 	memset(rxBuf, 0, sizeof(rxBuf));
169c3655ab0SOleksandr Tymoshenko 
170c3655ab0SOleksandr Tymoshenko 	txBuf[0] = writable ? CMD_WRITE_ENABLE : CMD_WRITE_DISABLE;
171c3655ab0SOleksandr Tymoshenko 	cmd.tx_cmd = txBuf;
172c3655ab0SOleksandr Tymoshenko 	cmd.rx_cmd = rxBuf;
173c3655ab0SOleksandr Tymoshenko 	cmd.rx_cmd_sz = 1;
174c3655ab0SOleksandr Tymoshenko 	cmd.tx_cmd_sz = 1;
175c3655ab0SOleksandr Tymoshenko 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
176c3655ab0SOleksandr Tymoshenko }
177c3655ab0SOleksandr Tymoshenko 
178c3655ab0SOleksandr Tymoshenko static void
179c3655ab0SOleksandr Tymoshenko mx25l_erase_sector(device_t dev, off_t sector)
180c3655ab0SOleksandr Tymoshenko {
181c3655ab0SOleksandr Tymoshenko 	uint8_t txBuf[4], rxBuf[4];
182c3655ab0SOleksandr Tymoshenko 	struct spi_command cmd;
183c3655ab0SOleksandr Tymoshenko 	int err;
184c3655ab0SOleksandr Tymoshenko 
185c3655ab0SOleksandr Tymoshenko 	mx25l_wait_for_device_ready(dev);
186c3655ab0SOleksandr Tymoshenko 	mx25l_set_writable(dev, 1);
187c3655ab0SOleksandr Tymoshenko 
188c3655ab0SOleksandr Tymoshenko 	memset(&cmd, 0, sizeof(cmd));
189c3655ab0SOleksandr Tymoshenko 	memset(txBuf, 0, sizeof(txBuf));
190c3655ab0SOleksandr Tymoshenko 	memset(rxBuf, 0, sizeof(rxBuf));
191c3655ab0SOleksandr Tymoshenko 
192c3655ab0SOleksandr Tymoshenko 	txBuf[0] = CMD_SECTOR_ERASE;
193c3655ab0SOleksandr Tymoshenko 	cmd.tx_cmd = txBuf;
194c3655ab0SOleksandr Tymoshenko 	cmd.rx_cmd = rxBuf;
195c3655ab0SOleksandr Tymoshenko 	cmd.rx_cmd_sz = 4;
196c3655ab0SOleksandr Tymoshenko 	cmd.tx_cmd_sz = 4;
197c3655ab0SOleksandr Tymoshenko 	txBuf[1] = ((sector >> 16) & 0xff);
198c3655ab0SOleksandr Tymoshenko 	txBuf[2] = ((sector >> 8) & 0xff);
199c3655ab0SOleksandr Tymoshenko 	txBuf[3] = (sector & 0xff);
200c3655ab0SOleksandr Tymoshenko 	err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
201c3655ab0SOleksandr Tymoshenko }
202c3655ab0SOleksandr Tymoshenko 
203cd5bdf03SOleksandr Tymoshenko static int
204cd5bdf03SOleksandr Tymoshenko mx25l_probe(device_t dev)
205cd5bdf03SOleksandr Tymoshenko {
206cd5bdf03SOleksandr Tymoshenko 	device_set_desc(dev, "M25Pxx Flash Family");
207cd5bdf03SOleksandr Tymoshenko 	return (0);
208cd5bdf03SOleksandr Tymoshenko }
209cd5bdf03SOleksandr Tymoshenko 
210cd5bdf03SOleksandr Tymoshenko static int
211cd5bdf03SOleksandr Tymoshenko mx25l_attach(device_t dev)
212cd5bdf03SOleksandr Tymoshenko {
213cd5bdf03SOleksandr Tymoshenko 	struct mx25l_softc *sc;
214cd5bdf03SOleksandr Tymoshenko 	struct mx25l_flash_ident *ident;
215cd5bdf03SOleksandr Tymoshenko 
216cd5bdf03SOleksandr Tymoshenko 	sc = device_get_softc(dev);
217cd5bdf03SOleksandr Tymoshenko 	sc->sc_dev = dev;
218cd5bdf03SOleksandr Tymoshenko 	M25PXX_LOCK_INIT(sc);
219cd5bdf03SOleksandr Tymoshenko 
220cd5bdf03SOleksandr Tymoshenko 	ident = mx25l_get_device_ident(sc);
221cd5bdf03SOleksandr Tymoshenko 	if (ident == NULL)
222cd5bdf03SOleksandr Tymoshenko 		return (ENXIO);
223cd5bdf03SOleksandr Tymoshenko 
224cd5bdf03SOleksandr Tymoshenko 	mx25l_wait_for_device_ready(sc->sc_dev);
225cd5bdf03SOleksandr Tymoshenko 
226cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk = disk_alloc();
227cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_open = mx25l_open;
228cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_close = mx25l_close;
229cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_strategy = mx25l_strategy;
230cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_ioctl = mx25l_ioctl;
231cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_name = "flash/spi";
232cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_drv1 = sc;
233cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_maxsize = DFLTPHYS;
234cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_sectorsize = ident->sectorsize;
235cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_mediasize = ident->sectorsize * ident->sectorcount;
236cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_unit = device_get_unit(sc->sc_dev);
237cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_dump = NULL;		/* NB: no dumps */
238cd5bdf03SOleksandr Tymoshenko 
239cd5bdf03SOleksandr Tymoshenko         /* NB: use stripesize to hold the erase/region size for RedBoot */
240cd5bdf03SOleksandr Tymoshenko 	sc->sc_disk->d_stripesize = ident->sectorsize;
241cd5bdf03SOleksandr Tymoshenko 
242cd5bdf03SOleksandr Tymoshenko 	disk_create(sc->sc_disk, DISK_VERSION);
243cd5bdf03SOleksandr Tymoshenko 	bioq_init(&sc->sc_bio_queue);
244cd5bdf03SOleksandr Tymoshenko 
245cd5bdf03SOleksandr Tymoshenko 	kproc_create(&mx25l_task, sc, &sc->sc_p, 0, 0, "task: mx25l flash");
246cd5bdf03SOleksandr Tymoshenko 	device_printf(sc->sc_dev, "%s, sector %d bytes, %d sectors\n",
247cd5bdf03SOleksandr Tymoshenko 	    ident->name, ident->sectorsize, ident->sectorcount);
248cd5bdf03SOleksandr Tymoshenko 
249cd5bdf03SOleksandr Tymoshenko 	return (0);
250cd5bdf03SOleksandr Tymoshenko }
251cd5bdf03SOleksandr Tymoshenko 
252cd5bdf03SOleksandr Tymoshenko static int
253cd5bdf03SOleksandr Tymoshenko mx25l_detach(device_t dev)
254cd5bdf03SOleksandr Tymoshenko {
255cd5bdf03SOleksandr Tymoshenko 
256cd5bdf03SOleksandr Tymoshenko 	return (EIO);
257cd5bdf03SOleksandr Tymoshenko }
258cd5bdf03SOleksandr Tymoshenko 
259cd5bdf03SOleksandr Tymoshenko static int
260cd5bdf03SOleksandr Tymoshenko mx25l_open(struct disk *dp)
261cd5bdf03SOleksandr Tymoshenko {
262cd5bdf03SOleksandr Tymoshenko 	return (0);
263cd5bdf03SOleksandr Tymoshenko }
264cd5bdf03SOleksandr Tymoshenko 
265cd5bdf03SOleksandr Tymoshenko static int
266cd5bdf03SOleksandr Tymoshenko mx25l_close(struct disk *dp)
267cd5bdf03SOleksandr Tymoshenko {
268cd5bdf03SOleksandr Tymoshenko 
269cd5bdf03SOleksandr Tymoshenko 	return (0);
270cd5bdf03SOleksandr Tymoshenko }
271cd5bdf03SOleksandr Tymoshenko 
272cd5bdf03SOleksandr Tymoshenko static int
273cd5bdf03SOleksandr Tymoshenko mx25l_ioctl(struct disk *dp, u_long cmd, void *data, int fflag,
274cd5bdf03SOleksandr Tymoshenko 	struct thread *td)
275cd5bdf03SOleksandr Tymoshenko {
276cd5bdf03SOleksandr Tymoshenko 
277cd5bdf03SOleksandr Tymoshenko 	return (EINVAL);
278cd5bdf03SOleksandr Tymoshenko }
279cd5bdf03SOleksandr Tymoshenko 
280cd5bdf03SOleksandr Tymoshenko static void
281cd5bdf03SOleksandr Tymoshenko mx25l_strategy(struct bio *bp)
282cd5bdf03SOleksandr Tymoshenko {
283cd5bdf03SOleksandr Tymoshenko 	struct mx25l_softc *sc;
284cd5bdf03SOleksandr Tymoshenko 
285cd5bdf03SOleksandr Tymoshenko 	sc = (struct mx25l_softc *)bp->bio_disk->d_drv1;
286cd5bdf03SOleksandr Tymoshenko 	M25PXX_LOCK(sc);
287cd5bdf03SOleksandr Tymoshenko 	bioq_disksort(&sc->sc_bio_queue, bp);
288cd5bdf03SOleksandr Tymoshenko 	wakeup(sc);
289cd5bdf03SOleksandr Tymoshenko 	M25PXX_UNLOCK(sc);
290cd5bdf03SOleksandr Tymoshenko }
291cd5bdf03SOleksandr Tymoshenko 
292cd5bdf03SOleksandr Tymoshenko static void
293cd5bdf03SOleksandr Tymoshenko mx25l_task(void *arg)
294cd5bdf03SOleksandr Tymoshenko {
295cd5bdf03SOleksandr Tymoshenko 	struct mx25l_softc *sc = (struct mx25l_softc*)arg;
296cd5bdf03SOleksandr Tymoshenko 	struct bio *bp;
297cd5bdf03SOleksandr Tymoshenko 	uint8_t txBuf[8], rxBuf[8];
298cd5bdf03SOleksandr Tymoshenko 	struct spi_command cmd;
299cd5bdf03SOleksandr Tymoshenko 	device_t dev, pdev;
300c3655ab0SOleksandr Tymoshenko 	off_t write_offset;
301c3655ab0SOleksandr Tymoshenko 	long bytes_to_write, bytes_writen;
302cd5bdf03SOleksandr Tymoshenko 
303cd5bdf03SOleksandr Tymoshenko 	for (;;) {
304cd5bdf03SOleksandr Tymoshenko 		dev = sc->sc_dev;
305cd5bdf03SOleksandr Tymoshenko 		pdev = device_get_parent(dev);
306cd5bdf03SOleksandr Tymoshenko 		M25PXX_LOCK(sc);
307cd5bdf03SOleksandr Tymoshenko 		do {
308cd5bdf03SOleksandr Tymoshenko 			bp = bioq_first(&sc->sc_bio_queue);
309cd5bdf03SOleksandr Tymoshenko 			if (bp == NULL)
310cd5bdf03SOleksandr Tymoshenko 				msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
311cd5bdf03SOleksandr Tymoshenko 		} while (bp == NULL);
312cd5bdf03SOleksandr Tymoshenko 		bioq_remove(&sc->sc_bio_queue, bp);
313cd5bdf03SOleksandr Tymoshenko 		M25PXX_UNLOCK(sc);
314cd5bdf03SOleksandr Tymoshenko 
315cd5bdf03SOleksandr Tymoshenko 		if (bp->bio_cmd == BIO_READ) {
316cd5bdf03SOleksandr Tymoshenko 			txBuf[0] = CMD_FAST_READ;
317cd5bdf03SOleksandr Tymoshenko 			cmd.tx_cmd_sz = 5;
318cd5bdf03SOleksandr Tymoshenko 			cmd.rx_cmd_sz = 5;
319cd5bdf03SOleksandr Tymoshenko 
320cd5bdf03SOleksandr Tymoshenko 			txBuf[1] = (((bp->bio_offset) >> 16) & 0xff);
321cd5bdf03SOleksandr Tymoshenko 			txBuf[2] = (((bp->bio_offset) >> 8) & 0xff);
322cd5bdf03SOleksandr Tymoshenko 			txBuf[3] = ((bp->bio_offset) & 0xff);
323cd5bdf03SOleksandr Tymoshenko 			/* Dummy byte */
324cd5bdf03SOleksandr Tymoshenko 			txBuf[4] = 0;
325cd5bdf03SOleksandr Tymoshenko 
326cd5bdf03SOleksandr Tymoshenko 			cmd.tx_cmd = txBuf;
327cd5bdf03SOleksandr Tymoshenko 			cmd.rx_cmd = rxBuf;
328cd5bdf03SOleksandr Tymoshenko 			cmd.tx_data = bp->bio_data;
329cd5bdf03SOleksandr Tymoshenko 			cmd.tx_data_sz = bp->bio_bcount;
330cd5bdf03SOleksandr Tymoshenko 			cmd.rx_data = bp->bio_data;
331cd5bdf03SOleksandr Tymoshenko 			cmd.rx_data_sz = bp->bio_bcount;
332c3655ab0SOleksandr Tymoshenko 
333cd5bdf03SOleksandr Tymoshenko 			bp->bio_error = SPIBUS_TRANSFER(pdev, dev, &cmd);
334cd5bdf03SOleksandr Tymoshenko 		}
335c3655ab0SOleksandr Tymoshenko 		else if (bp->bio_cmd == BIO_WRITE) {
336c3655ab0SOleksandr Tymoshenko 			mx25l_erase_sector(dev, bp->bio_offset);
337c3655ab0SOleksandr Tymoshenko 
338c3655ab0SOleksandr Tymoshenko 			cmd.tx_cmd_sz = 4;
339c3655ab0SOleksandr Tymoshenko 			cmd.rx_cmd_sz = 4;
340c3655ab0SOleksandr Tymoshenko 
341c3655ab0SOleksandr Tymoshenko 			bytes_writen = 0;
342c3655ab0SOleksandr Tymoshenko 			write_offset = bp->bio_offset;
343c3655ab0SOleksandr Tymoshenko 
344c3655ab0SOleksandr Tymoshenko 			/*
345c3655ab0SOleksandr Tymoshenko 			 * I assume here that we write per-sector only
346c3655ab0SOleksandr Tymoshenko 			 * and sector size should be 256 bytes aligned
347c3655ab0SOleksandr Tymoshenko 			 */
348c3655ab0SOleksandr Tymoshenko 			KASSERT(write_offset % FLASH_PAGE_SIZE == 0,
349c3655ab0SOleksandr Tymoshenko 			    ("offset for BIO_WRITE is not %d bytes aliIgned",
350c3655ab0SOleksandr Tymoshenko 				FLASH_PAGE_SIZE));
351c3655ab0SOleksandr Tymoshenko 
352c3655ab0SOleksandr Tymoshenko 			/*
353c3655ab0SOleksandr Tymoshenko 			 * Maximum write size for CMD_PAGE_PROGRAM is
354c3655ab0SOleksandr Tymoshenko 			 * FLASH_PAGE_SIZE, so split data to chunks
355c3655ab0SOleksandr Tymoshenko 			 * FLASH_PAGE_SIZE bytes eash and write them
356c3655ab0SOleksandr Tymoshenko 			 * one by one
357c3655ab0SOleksandr Tymoshenko 			 */
358c3655ab0SOleksandr Tymoshenko 			while (bytes_writen < bp->bio_bcount) {
359c3655ab0SOleksandr Tymoshenko 				txBuf[0] = CMD_PAGE_PROGRAM;
360c3655ab0SOleksandr Tymoshenko 				txBuf[1] = ((write_offset >> 16) & 0xff);
361c3655ab0SOleksandr Tymoshenko 				txBuf[2] = ((write_offset >> 8) & 0xff);
362c3655ab0SOleksandr Tymoshenko 				txBuf[3] = (write_offset & 0xff);
363c3655ab0SOleksandr Tymoshenko 
364c3655ab0SOleksandr Tymoshenko 				bytes_to_write = MIN(FLASH_PAGE_SIZE,
365c3655ab0SOleksandr Tymoshenko 				    bp->bio_bcount - bytes_writen);
366c3655ab0SOleksandr Tymoshenko 				cmd.tx_cmd = txBuf;
367c3655ab0SOleksandr Tymoshenko 				cmd.rx_cmd = rxBuf;
368c3655ab0SOleksandr Tymoshenko 				cmd.tx_data = bp->bio_data + bytes_writen;
369c3655ab0SOleksandr Tymoshenko 				cmd.tx_data_sz = bytes_to_write;
370c3655ab0SOleksandr Tymoshenko 				cmd.rx_data = bp->bio_data + bytes_writen;
371c3655ab0SOleksandr Tymoshenko 				cmd.rx_data_sz = bytes_to_write;
372c3655ab0SOleksandr Tymoshenko 
373c3655ab0SOleksandr Tymoshenko 				/*
374c3655ab0SOleksandr Tymoshenko 				 * Eash completed write operation resets WEL
375c3655ab0SOleksandr Tymoshenko 				 * (write enable latch) to disabled state,
376c3655ab0SOleksandr Tymoshenko 				 * so we re-enable it here
377c3655ab0SOleksandr Tymoshenko 				 */
378c3655ab0SOleksandr Tymoshenko 				mx25l_wait_for_device_ready(dev);
379c3655ab0SOleksandr Tymoshenko 				mx25l_set_writable(dev, 1);
380c3655ab0SOleksandr Tymoshenko 
381c3655ab0SOleksandr Tymoshenko 				bp->bio_error = SPIBUS_TRANSFER(pdev, dev, &cmd);
382c3655ab0SOleksandr Tymoshenko 				if (bp->bio_error)
383c3655ab0SOleksandr Tymoshenko 					break;
384c3655ab0SOleksandr Tymoshenko 
385c3655ab0SOleksandr Tymoshenko 				bytes_writen += bytes_to_write;
386c3655ab0SOleksandr Tymoshenko 				write_offset += bytes_to_write;
387c3655ab0SOleksandr Tymoshenko 			}
388c3655ab0SOleksandr Tymoshenko 		}
389cd5bdf03SOleksandr Tymoshenko 		else
390cd5bdf03SOleksandr Tymoshenko 			bp->bio_error = EINVAL;
391cd5bdf03SOleksandr Tymoshenko 
392c3655ab0SOleksandr Tymoshenko 
393cd5bdf03SOleksandr Tymoshenko 		biodone(bp);
394cd5bdf03SOleksandr Tymoshenko 	}
395cd5bdf03SOleksandr Tymoshenko }
396cd5bdf03SOleksandr Tymoshenko 
397cd5bdf03SOleksandr Tymoshenko static devclass_t mx25l_devclass;
398cd5bdf03SOleksandr Tymoshenko 
399cd5bdf03SOleksandr Tymoshenko static device_method_t mx25l_methods[] = {
400cd5bdf03SOleksandr Tymoshenko 	/* Device interface */
401cd5bdf03SOleksandr Tymoshenko 	DEVMETHOD(device_probe,		mx25l_probe),
402cd5bdf03SOleksandr Tymoshenko 	DEVMETHOD(device_attach,	mx25l_attach),
403cd5bdf03SOleksandr Tymoshenko 	DEVMETHOD(device_detach,	mx25l_detach),
404cd5bdf03SOleksandr Tymoshenko 
405cd5bdf03SOleksandr Tymoshenko 	{ 0, 0 }
406cd5bdf03SOleksandr Tymoshenko };
407cd5bdf03SOleksandr Tymoshenko 
408cd5bdf03SOleksandr Tymoshenko static driver_t mx25l_driver = {
409cd5bdf03SOleksandr Tymoshenko 	"mx25l",
410cd5bdf03SOleksandr Tymoshenko 	mx25l_methods,
411cd5bdf03SOleksandr Tymoshenko 	sizeof(struct mx25l_softc),
412cd5bdf03SOleksandr Tymoshenko };
413cd5bdf03SOleksandr Tymoshenko 
414cd5bdf03SOleksandr Tymoshenko DRIVER_MODULE(mx25l, spibus, mx25l_driver, mx25l_devclass, 0, 0);
415