2008-06-11 23:28:52 +04:00
|
|
|
/* $NetBSD: spiflash.c,v 1.9 2008/06/11 19:31:10 cegger Exp $ */
|
2006-10-07 11:21:13 +04:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2006 Urbana-Champaign Independent Media Center.
|
|
|
|
* Copyright (c) 2006 Garrett D'Amore.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Portions of this code were written by Garrett D'Amore for the
|
|
|
|
* Champaign-Urbana Community Wireless Network Project.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or
|
|
|
|
* without modification, are permitted provided that the following
|
|
|
|
* conditions are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above
|
|
|
|
* copyright notice, this list of conditions and the following
|
|
|
|
* disclaimer in the documentation and/or other materials provided
|
|
|
|
* with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this
|
|
|
|
* software must display the following acknowledgements:
|
|
|
|
* This product includes software developed by the Urbana-Champaign
|
|
|
|
* Independent Media Center.
|
|
|
|
* This product includes software developed by Garrett D'Amore.
|
|
|
|
* 4. Urbana-Champaign Independent Media Center's name and Garrett
|
|
|
|
* D'Amore's name may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
|
|
|
|
* MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
|
|
|
|
* MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2008-06-11 23:28:52 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: spiflash.c,v 1.9 2008/06/11 19:31:10 cegger Exp $");
|
2006-10-07 11:21:13 +04:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/disk.h>
|
|
|
|
#include <sys/disklabel.h>
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/bufq.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/kthread.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
|
|
|
|
#include <dev/spi/spivar.h>
|
|
|
|
#include <dev/spi/spiflash.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is an MI block driver for SPI flash devices. It could probably be
|
|
|
|
* converted to some more generic framework, if someone wanted to create one
|
|
|
|
* for NOR flashes. Note that some flashes have the ability to handle
|
|
|
|
* interrupts.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct spiflash_softc {
|
|
|
|
struct disk sc_dk;
|
|
|
|
|
|
|
|
struct spiflash_hw_if sc_hw;
|
|
|
|
void *sc_cookie;
|
|
|
|
|
|
|
|
const char *sc_name;
|
|
|
|
struct spi_handle *sc_handle;
|
|
|
|
int sc_device_size;
|
|
|
|
int sc_write_size;
|
|
|
|
int sc_erase_size;
|
|
|
|
int sc_read_size;
|
|
|
|
int sc_device_blks;
|
|
|
|
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
struct bufq_state *sc_waitq;
|
|
|
|
struct bufq_state *sc_workq;
|
|
|
|
struct bufq_state *sc_doneq;
|
2007-07-19 04:00:09 +04:00
|
|
|
lwp_t *sc_thread;
|
2006-10-07 11:21:13 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
#define sc_getname sc_hw.sf_getname
|
|
|
|
#define sc_gethandle sc_hw.sf_gethandle
|
|
|
|
#define sc_getsize sc_hw.sf_getsize
|
|
|
|
#define sc_getflags sc_hw.sf_getflags
|
|
|
|
#define sc_erase sc_hw.sf_erase
|
|
|
|
#define sc_write sc_hw.sf_write
|
|
|
|
#define sc_read sc_hw.sf_read
|
|
|
|
#define sc_getstatus sc_hw.sf_getstatus
|
|
|
|
#define sc_setstatus sc_hw.sf_setstatus
|
|
|
|
|
|
|
|
struct spiflash_attach_args {
|
|
|
|
const struct spiflash_hw_if *hw;
|
|
|
|
void *cookie;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define STATIC
|
2008-05-04 18:21:55 +04:00
|
|
|
STATIC int spiflash_match(device_t , cfdata_t , void *);
|
|
|
|
STATIC void spiflash_attach(device_t , device_t , void *);
|
2006-10-07 11:21:13 +04:00
|
|
|
STATIC int spiflash_print(void *, const char *);
|
|
|
|
STATIC int spiflash_common_erase(spiflash_handle_t, size_t, size_t);
|
|
|
|
STATIC int spiflash_common_write(spiflash_handle_t, size_t, size_t,
|
|
|
|
const uint8_t *);
|
|
|
|
STATIC int spiflash_common_read(spiflash_handle_t, size_t, size_t, uint8_t *);
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
STATIC void spiflash_process_done(spiflash_handle_t, int);
|
|
|
|
STATIC void spiflash_process_read(spiflash_handle_t);
|
|
|
|
STATIC void spiflash_process_write(spiflash_handle_t);
|
2006-10-07 11:21:13 +04:00
|
|
|
STATIC void spiflash_thread(void *);
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
STATIC int spiflash_nsectors(spiflash_handle_t, struct buf *);
|
|
|
|
STATIC int spiflash_nsectors(spiflash_handle_t, struct buf *);
|
|
|
|
STATIC int spiflash_sector(spiflash_handle_t, struct buf *);
|
2006-10-07 11:21:13 +04:00
|
|
|
|
2008-05-04 18:21:55 +04:00
|
|
|
CFATTACH_DECL_NEW(spiflash, sizeof(struct spiflash_softc),
|
2006-10-07 11:21:13 +04:00
|
|
|
spiflash_match, spiflash_attach, NULL, NULL);
|
|
|
|
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
#ifdef SPIFLASH_DEBUG
|
|
|
|
#define DPRINTF(x) do { printf x; } while (0/*CONSTCOND*/)
|
|
|
|
#else
|
|
|
|
#define DPRINTF(x) do { } while (0/*CONSTCOND*/)
|
|
|
|
#endif
|
|
|
|
|
2006-10-07 11:21:13 +04:00
|
|
|
extern struct cfdriver spiflash_cd;
|
|
|
|
|
|
|
|
dev_type_open(spiflash_open);
|
|
|
|
dev_type_close(spiflash_close);
|
|
|
|
dev_type_read(spiflash_read);
|
|
|
|
dev_type_write(spiflash_write);
|
|
|
|
dev_type_ioctl(spiflash_ioctl);
|
|
|
|
dev_type_strategy(spiflash_strategy);
|
|
|
|
|
|
|
|
const struct bdevsw spiflash_bdevsw = {
|
|
|
|
.d_open = spiflash_open,
|
|
|
|
.d_close = spiflash_close,
|
|
|
|
.d_strategy = spiflash_strategy,
|
|
|
|
.d_ioctl = spiflash_ioctl,
|
|
|
|
.d_dump = nodump,
|
|
|
|
.d_psize = nosize,
|
2007-07-10 00:51:58 +04:00
|
|
|
.d_flag = D_DISK,
|
2006-10-07 11:21:13 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
const struct cdevsw spiflash_cdevsw = {
|
|
|
|
.d_open = spiflash_open,
|
|
|
|
.d_close = spiflash_close,
|
|
|
|
.d_read = spiflash_read,
|
|
|
|
.d_write = spiflash_write,
|
|
|
|
.d_ioctl = spiflash_ioctl,
|
|
|
|
.d_stop = nostop,
|
|
|
|
.d_tty = notty,
|
|
|
|
.d_poll = nopoll,
|
|
|
|
.d_mmap = nommap,
|
|
|
|
.d_kqfilter = nokqfilter,
|
2007-07-10 00:51:58 +04:00
|
|
|
.d_flag = D_DISK,
|
2006-10-07 11:21:13 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct dkdriver spiflash_dkdriver = { spiflash_strategy, NULL };
|
|
|
|
|
|
|
|
spiflash_handle_t
|
|
|
|
spiflash_attach_mi(const struct spiflash_hw_if *hw, void *cookie,
|
2008-05-04 18:21:55 +04:00
|
|
|
device_t dev)
|
2006-10-07 11:21:13 +04:00
|
|
|
{
|
|
|
|
struct spiflash_attach_args sfa;
|
|
|
|
sfa.hw = hw;
|
|
|
|
sfa.cookie = cookie;
|
|
|
|
|
|
|
|
return (spiflash_handle_t)config_found(dev, &sfa, spiflash_print);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_print(void *aux, const char *pnp)
|
|
|
|
{
|
|
|
|
if (pnp != NULL)
|
|
|
|
printf("spiflash at %s\n", pnp);
|
|
|
|
|
|
|
|
return UNCONF;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-05-04 18:21:55 +04:00
|
|
|
spiflash_match(device_t parent, cfdata_t cf, void *aux)
|
2006-10-07 11:21:13 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-05-04 18:21:55 +04:00
|
|
|
spiflash_attach(device_t parent, device_t self, void *aux)
|
2006-10-07 11:21:13 +04:00
|
|
|
{
|
|
|
|
struct spiflash_softc *sc = device_private(self);
|
|
|
|
struct spiflash_attach_args *sfa = aux;
|
|
|
|
void *cookie = sfa->cookie;
|
|
|
|
|
|
|
|
sc->sc_hw = *sfa->hw;
|
|
|
|
sc->sc_cookie = cookie;
|
|
|
|
sc->sc_name = sc->sc_getname(cookie);
|
|
|
|
sc->sc_handle = sc->sc_gethandle(cookie);
|
|
|
|
sc->sc_device_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_DEVICE);
|
|
|
|
sc->sc_erase_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_ERASE);
|
|
|
|
sc->sc_write_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_WRITE);
|
|
|
|
sc->sc_read_size = sc->sc_getsize(cookie, SPIFLASH_SIZE_READ);
|
|
|
|
sc->sc_device_blks = sc->sc_device_size / DEV_BSIZE;
|
|
|
|
|
|
|
|
if (sc->sc_read == NULL)
|
|
|
|
sc->sc_read = spiflash_common_read;
|
|
|
|
if (sc->sc_write == NULL)
|
|
|
|
sc->sc_write = spiflash_common_write;
|
|
|
|
if (sc->sc_erase == NULL)
|
|
|
|
sc->sc_erase = spiflash_common_erase;
|
|
|
|
|
|
|
|
aprint_naive(": SPI flash\n");
|
|
|
|
aprint_normal(": %s SPI flash\n", sc->sc_name);
|
|
|
|
/* XXX: note that this has to change for boot-sectored flash */
|
2008-05-04 18:21:55 +04:00
|
|
|
aprint_normal_dev(self, "%d KB, %d sectors of %d KB each\n",
|
2008-04-05 20:35:35 +04:00
|
|
|
sc->sc_device_size / 1024,
|
2006-10-07 11:21:13 +04:00
|
|
|
sc->sc_device_size / sc->sc_erase_size,
|
|
|
|
sc->sc_erase_size / 1024);
|
|
|
|
|
|
|
|
/* first-come first-served strategy works best for us */
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
bufq_alloc(&sc->sc_waitq, "fcfs", BUFQ_SORT_RAWBLOCK);
|
|
|
|
bufq_alloc(&sc->sc_workq, "fcfs", BUFQ_SORT_RAWBLOCK);
|
|
|
|
bufq_alloc(&sc->sc_doneq, "fcfs", BUFQ_SORT_RAWBLOCK);
|
2006-10-07 11:21:13 +04:00
|
|
|
|
|
|
|
sc->sc_dk.dk_driver = &spiflash_dkdriver;
|
2008-05-04 18:21:55 +04:00
|
|
|
sc->sc_dk.dk_name = device_xname(self);
|
2006-10-07 11:21:13 +04:00
|
|
|
|
|
|
|
disk_attach(&sc->sc_dk);
|
2007-07-10 00:51:58 +04:00
|
|
|
|
|
|
|
/* arrange to allocate the kthread */
|
2007-07-19 04:00:09 +04:00
|
|
|
kthread_create(PRI_NONE, 0, NULL, spiflash_thread, sc,
|
2007-07-10 00:51:58 +04:00
|
|
|
&sc->sc_thread, "spiflash");
|
2006-10-07 11:21:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_open(dev_t dev, int flags, int mode, struct lwp *l)
|
|
|
|
{
|
|
|
|
spiflash_handle_t sc;
|
|
|
|
|
2008-06-11 23:28:52 +04:00
|
|
|
sc = device_lookup_private(&spiflash_cd, DISKUNIT(dev));
|
|
|
|
if (sc == NULL)
|
2006-10-07 11:21:13 +04:00
|
|
|
return ENXIO;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: We need to handle partitions here. The problem is
|
|
|
|
* that it isn't entirely clear to me how to deal with this.
|
|
|
|
* There are devices that could be used "in the raw" with a
|
|
|
|
* NetBSD label, but then you get into devices that have other
|
|
|
|
* kinds of data on them -- some have VxWorks data, some have
|
|
|
|
* RedBoot data, and some have other contraints -- for example
|
|
|
|
* some devices might have a portion that is read-only,
|
|
|
|
* whereas others might have a portion that is read-write.
|
|
|
|
*
|
|
|
|
* For now we just permit access to the entire device.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_close(dev_t dev, int flags, int mode, struct lwp *l)
|
|
|
|
{
|
|
|
|
spiflash_handle_t sc;
|
|
|
|
|
2008-06-11 23:28:52 +04:00
|
|
|
sc = device_lookup_private(&spiflash_cd, DISKUNIT(dev));
|
|
|
|
if (sc == NULL)
|
2006-10-07 11:21:13 +04:00
|
|
|
return ENXIO;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_read(dev_t dev, struct uio *uio, int ioflag)
|
|
|
|
{
|
|
|
|
|
|
|
|
return physio(spiflash_strategy, NULL, dev, B_READ, minphys, uio);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_write(dev_t dev, struct uio *uio, int ioflag)
|
|
|
|
{
|
|
|
|
|
|
|
|
return physio(spiflash_strategy, NULL, dev, B_WRITE, minphys, uio);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 08:59:00 +03:00
|
|
|
spiflash_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
|
2006-10-07 11:21:13 +04:00
|
|
|
{
|
|
|
|
spiflash_handle_t sc;
|
|
|
|
|
2008-06-11 23:28:52 +04:00
|
|
|
sc = device_lookup_private(&spiflash_cd, DISKUNIT(dev));
|
|
|
|
if (sc == NULL)
|
2006-10-07 11:21:13 +04:00
|
|
|
return ENXIO;
|
|
|
|
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
spiflash_strategy(struct buf *bp)
|
|
|
|
{
|
|
|
|
spiflash_handle_t sc;
|
|
|
|
int s;
|
|
|
|
|
2008-06-11 23:28:52 +04:00
|
|
|
sc = device_lookup_private(&spiflash_cd, DISKUNIT(bp->b_dev));
|
2006-10-07 11:21:13 +04:00
|
|
|
if (sc == NULL) {
|
|
|
|
bp->b_error = ENXIO;
|
|
|
|
biodone(bp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
if (((bp->b_bcount % sc->sc_write_size) != 0) ||
|
2006-10-07 11:21:13 +04:00
|
|
|
(bp->b_blkno < 0)) {
|
|
|
|
bp->b_error = EINVAL;
|
|
|
|
biodone(bp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no work? */
|
|
|
|
if (bp->b_bcount == 0) {
|
|
|
|
biodone(bp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
if (bounds_check_with_mediasize(bp, DEV_BSIZE,
|
|
|
|
sc->sc_device_blks) <= 0) {
|
|
|
|
biodone(bp);
|
|
|
|
return;
|
2006-10-07 11:21:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bp->b_resid = bp->b_bcount;
|
|
|
|
|
|
|
|
/* all ready, hand off to thread for async processing */
|
|
|
|
s = splbio();
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
BUFQ_PUT(sc->sc_waitq, bp);
|
2006-10-07 11:21:13 +04:00
|
|
|
wakeup(&sc->sc_thread);
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
spiflash_process_done(spiflash_handle_t sc, int err)
|
2006-10-07 11:21:13 +04:00
|
|
|
{
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
struct buf *bp;
|
|
|
|
int cnt = 0;
|
|
|
|
int flag = 0;
|
2006-10-07 11:21:13 +04:00
|
|
|
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
while ((bp = BUFQ_GET(sc->sc_doneq)) != NULL) {
|
|
|
|
flag = bp->b_flags & B_READ;
|
2007-07-29 16:15:35 +04:00
|
|
|
if ((bp->b_error = err) == 0)
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
bp->b_resid = 0;
|
|
|
|
cnt += bp->b_bcount - bp->b_resid;
|
|
|
|
biodone(bp);
|
|
|
|
}
|
|
|
|
disk_unbusy(&sc->sc_dk, cnt, flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
spiflash_process_read(spiflash_handle_t sc)
|
|
|
|
{
|
|
|
|
struct buf *bp;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
disk_busy(&sc->sc_dk);
|
|
|
|
while ((bp = BUFQ_GET(sc->sc_workq)) != NULL) {
|
|
|
|
size_t addr = bp->b_blkno * DEV_BSIZE;
|
|
|
|
uint8_t *data = bp->b_data;
|
|
|
|
int cnt = bp->b_resid;
|
|
|
|
|
|
|
|
BUFQ_PUT(sc->sc_doneq, bp);
|
|
|
|
|
|
|
|
DPRINTF(("read from addr %x, cnt %d\n", (unsigned)addr, cnt));
|
|
|
|
|
|
|
|
if ((err = sc->sc_read(sc, addr, cnt, data)) != 0) {
|
|
|
|
/* error occurred, fail all pending workq bufs */
|
|
|
|
bufq_move(sc->sc_doneq, sc->sc_workq);
|
|
|
|
break;
|
2006-10-07 11:21:13 +04:00
|
|
|
}
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
|
2006-10-07 11:21:13 +04:00
|
|
|
bp->b_resid -= cnt;
|
|
|
|
data += cnt;
|
|
|
|
addr += cnt;
|
|
|
|
}
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
spiflash_process_done(sc, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
spiflash_process_write(spiflash_handle_t sc)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
size_t base;
|
|
|
|
daddr_t blkno;
|
|
|
|
uint8_t *save;
|
|
|
|
int err = 0, neederase = 0;
|
|
|
|
struct buf *bp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* due to other considerations, we are guaranteed that
|
|
|
|
* we will only have multiple buffers if they are all in
|
|
|
|
* the same erase sector. Therefore we never need to look
|
|
|
|
* beyond the first block to determine how much data we need
|
|
|
|
* to save.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bp = BUFQ_PEEK(sc->sc_workq);
|
|
|
|
len = spiflash_nsectors(sc, bp) * sc->sc_erase_size;
|
|
|
|
blkno = bp->b_blkno;
|
|
|
|
base = (blkno * DEV_BSIZE) & ~ (sc->sc_erase_size - 1);
|
|
|
|
|
|
|
|
/* get ourself a scratch buffer */
|
|
|
|
save = malloc(len, M_DEVBUF, M_WAITOK);
|
|
|
|
|
|
|
|
disk_busy(&sc->sc_dk);
|
|
|
|
/* read in as much of the data as we need */
|
|
|
|
DPRINTF(("reading in %d bytes\n", len));
|
|
|
|
if ((err = sc->sc_read(sc, base, len, save)) != 0) {
|
|
|
|
bufq_move(sc->sc_doneq, sc->sc_workq);
|
|
|
|
spiflash_process_done(sc, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* now coalesce the writes into the save area, but also
|
|
|
|
* check to see if we need to do an erase
|
|
|
|
*/
|
|
|
|
while ((bp = BUFQ_GET(sc->sc_workq)) != NULL) {
|
|
|
|
uint8_t *data, *dst;
|
|
|
|
int resid = bp->b_resid;
|
|
|
|
|
|
|
|
DPRINTF(("coalesce write, blkno %x, count %d, resid %d\n",
|
|
|
|
(unsigned)bp->b_blkno, bp->b_bcount, resid));
|
|
|
|
|
|
|
|
data = bp->b_data;
|
|
|
|
dst = save + (bp->b_blkno - blkno) * DEV_BSIZE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOR flash bits. We can clear a bit, but we cannot
|
|
|
|
* set a bit, without erasing. This should help reduce
|
|
|
|
* unnecessary erases.
|
|
|
|
*/
|
|
|
|
while (resid) {
|
|
|
|
if ((*data) & ~(*dst))
|
|
|
|
neederase = 1;
|
|
|
|
*dst++ = *data++;
|
|
|
|
resid--;
|
|
|
|
}
|
|
|
|
|
|
|
|
BUFQ_PUT(sc->sc_doneq, bp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do the erase, if we need to.
|
|
|
|
*/
|
|
|
|
if (neederase) {
|
|
|
|
DPRINTF(("erasing from %x - %x\n", base, base + len));
|
|
|
|
if ((err = sc->sc_erase(sc, base, len)) != 0) {
|
|
|
|
spiflash_process_done(sc, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* now write our save area, and finish up.
|
|
|
|
*/
|
|
|
|
DPRINTF(("flashing %d bytes to %x from %x\n", len,
|
|
|
|
base, (unsigned)save));
|
|
|
|
err = sc->sc_write(sc, base, len, save);
|
|
|
|
spiflash_process_done(sc, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_nsectors(spiflash_handle_t sc, struct buf *bp)
|
|
|
|
{
|
|
|
|
unsigned addr, sector;
|
|
|
|
|
|
|
|
addr = bp->b_blkno * DEV_BSIZE;
|
|
|
|
sector = addr / sc->sc_erase_size;
|
|
|
|
|
|
|
|
addr += bp->b_bcount;
|
|
|
|
addr--;
|
|
|
|
return (((addr / sc->sc_erase_size) - sector) + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_sector(spiflash_handle_t sc, struct buf *bp)
|
|
|
|
{
|
|
|
|
unsigned addr, sector;
|
|
|
|
|
|
|
|
addr = bp->b_blkno * DEV_BSIZE;
|
|
|
|
sector = addr / sc->sc_erase_size;
|
|
|
|
|
|
|
|
/* if it spans multiple blocks, error it */
|
|
|
|
addr += bp->b_bcount;
|
|
|
|
addr--;
|
|
|
|
if (sector != (addr / sc->sc_erase_size))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return sector;
|
2006-10-07 11:21:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
spiflash_thread(void *arg)
|
|
|
|
{
|
|
|
|
spiflash_handle_t sc = arg;
|
|
|
|
struct buf *bp;
|
|
|
|
int s;
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
int sector;
|
2006-10-07 11:21:13 +04:00
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
for (;;) {
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
if ((bp = BUFQ_GET(sc->sc_waitq)) == NULL) {
|
2006-10-07 11:21:13 +04:00
|
|
|
tsleep(&sc->sc_thread, PRIBIO, "spiflash_thread", 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
BUFQ_PUT(sc->sc_workq, bp);
|
|
|
|
|
|
|
|
if (bp->b_flags & B_READ) {
|
|
|
|
/* just do the read */
|
|
|
|
spiflash_process_read(sc);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Because writing a flash filesystem is particularly
|
|
|
|
* painful, involving erase, modify, write, we prefer
|
|
|
|
* to coalesce writes to the same sector together.
|
|
|
|
*/
|
|
|
|
|
|
|
|
sector = spiflash_sector(sc, bp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if the write spans multiple sectors, skip
|
|
|
|
* coalescing. (It would be nice if we could break
|
|
|
|
* these up. minphys is honored for read/write, but
|
|
|
|
* not necessarily for bread.)
|
|
|
|
*/
|
|
|
|
if (sector < 0)
|
|
|
|
goto dowrite;
|
|
|
|
|
|
|
|
while ((bp = BUFQ_PEEK(sc->sc_waitq)) != NULL) {
|
|
|
|
/* can't deal with read requests! */
|
|
|
|
if (bp->b_flags & B_READ)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* is it for the same sector? */
|
|
|
|
if (spiflash_sector(sc, bp) != sector)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bp = BUFQ_GET(sc->sc_waitq);
|
|
|
|
BUFQ_PUT(sc->sc_workq, bp);
|
|
|
|
}
|
|
|
|
|
|
|
|
dowrite:
|
|
|
|
spiflash_process_write(sc);
|
2006-10-07 11:21:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* SPI flash common implementation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Most devices take on the order of 1 second for each block that they
|
|
|
|
* delete.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
spiflash_common_erase(spiflash_handle_t sc, size_t start, size_t size)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if ((start % sc->sc_erase_size) || (size % sc->sc_erase_size))
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
/* the second test is to test against wrap */
|
|
|
|
if ((start > sc->sc_device_size) ||
|
|
|
|
((start + size) > sc->sc_device_size))
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: check protection status? Requires master table mapping
|
|
|
|
* sectors to status bits, and so forth.
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (size) {
|
|
|
|
if ((rv = spiflash_write_enable(sc)) != 0) {
|
|
|
|
spiflash_write_disable(sc);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_ERASE, 3, start, 0,
|
|
|
|
NULL, NULL)) != 0) {
|
|
|
|
spiflash_write_disable(sc);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The devices I have all say typical for sector erase
|
|
|
|
* is ~1sec. We check ten times that often. (There
|
|
|
|
* is no way to interrupt on this.)
|
|
|
|
*/
|
|
|
|
if ((rv = spiflash_wait(sc, hz / 10)) != 0)
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
start += sc->sc_erase_size;
|
|
|
|
size -= sc->sc_erase_size;
|
|
|
|
|
|
|
|
/* NB: according to the docs I have, the write enable
|
|
|
|
* is automatically cleared upon completion of an erase
|
|
|
|
* command, so there is no need to explicitly disable it.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_common_write(spiflash_handle_t sc, size_t start, size_t size,
|
|
|
|
const uint8_t *data)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if ((start % sc->sc_write_size) || (size % sc->sc_write_size))
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
while (size) {
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
if ((rv = spiflash_write_enable(sc)) != 0) {
|
|
|
|
spiflash_write_disable(sc);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
cnt = min(size, sc->sc_write_size);
|
|
|
|
if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_PROGRAM, 3, start,
|
|
|
|
cnt, data, NULL)) != 0) {
|
|
|
|
spiflash_write_disable(sc);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It seems that most devices can write bits fairly
|
|
|
|
* quickly. For example, one part I have access to
|
|
|
|
* takes ~5msec to process the entire 256 byte page.
|
|
|
|
* Probably this should be modified to cope with
|
|
|
|
* device-specific timing, and maybe also take into
|
|
|
|
* account systems with higher values of HZ (which
|
|
|
|
* could benefit from sleeping.)
|
|
|
|
*/
|
|
|
|
if ((rv = spiflash_wait(sc, 0)) != 0)
|
|
|
|
return rv;
|
|
|
|
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
data += cnt;
|
2006-10-07 11:21:13 +04:00
|
|
|
start += cnt;
|
|
|
|
size -= cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_common_read(spiflash_handle_t sc, size_t start, size_t size,
|
|
|
|
uint8_t *data)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
while (size) {
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
if (sc->sc_read_size > 0)
|
|
|
|
cnt = min(size, sc->sc_read_size);
|
|
|
|
else
|
|
|
|
cnt = size;
|
|
|
|
|
|
|
|
if ((rv = spiflash_cmd(sc, SPIFLASH_CMD_READ, 3, start,
|
|
|
|
cnt, NULL, data)) != 0) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
start += cnt;
|
|
|
|
size -= cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read status register */
|
|
|
|
int
|
|
|
|
spiflash_read_status(spiflash_handle_t sc, uint8_t *sr)
|
|
|
|
{
|
|
|
|
|
|
|
|
return spiflash_cmd(sc, SPIFLASH_CMD_RDSR, 0, 0, 1, NULL, sr);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_write_enable(spiflash_handle_t sc)
|
|
|
|
{
|
|
|
|
|
|
|
|
return spiflash_cmd(sc, SPIFLASH_CMD_WREN, 0, 0, 0, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_write_disable(spiflash_handle_t sc)
|
|
|
|
{
|
|
|
|
|
|
|
|
return spiflash_cmd(sc, SPIFLASH_CMD_WRDI, 0, 0, 0, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_cmd(spiflash_handle_t sc, uint8_t cmd,
|
|
|
|
size_t addrlen, uint32_t addr,
|
|
|
|
size_t cnt, const uint8_t *wdata, uint8_t *rdata)
|
|
|
|
{
|
|
|
|
struct spi_transfer trans;
|
|
|
|
struct spi_chunk chunk1, chunk2;
|
|
|
|
char buf[4];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
buf[0] = cmd;
|
|
|
|
|
|
|
|
if (addrlen > 3)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
for (i = addrlen; i > 0; i--) {
|
This commit provides substantial fixes and functionality for SPI flash.
Specifically, the SPI flash now operates as a nearly fully functional block
device (other than lacking disklabel support). It does some basic translation
stuff, so that if you attempt to write a block, the underlying flash sectors
(usually 64k in size) will be read, erased and rewritten.
To minize thrashing, the spiflash strategy routine attempts to gather writes
to the same sector together, so that in the typical case you will not have to
repeatedly erase/rewrite the sector. It also attempts to check and verify
whether an erase cycle is truly needed. There are still access patterns that
will cause multiple erases to occur, and so I heartily discourage the use
of these flash devices for storing anything other than small configuration
data, or write-once images. If you want to do more than that, then someone
should try to write a real flash translation layer.
The drivers attempt to provide some level of asynchronous operation, so that
while you are erasing or writing to the flash, other things can reasonably
take place.
Note that spiflash does not do bad block remapping. It also doesn't detect
when a device is in read-only mode, or if some sectors are read-only. It
only supports uniform sectored NOR flash. It lacks any code to deal with
disklabels, and does not offer any disk related ioctls.
These limitations aside, it would not be terribly hard, I think, to break
out the code I've done to create a generic "norflash" driver, backed by
a "common" spiflash module. Then other flash drivers (e.g. athflash, etc.)
could benefit from the ability to use this as a block device. I've tried
to architect it to support that, if someone else wants to do the work.
(Hi Jared!)
The primary reason that I've not added code to deal with disklabels is that
I had a difficult time figuring out which framework (disklabels or wedges)
to use, and which bits of code were necessary to implement. In the case of
the flash devices I'm working with, a parser to deal with redboot FIS images
(partitions) would need to be added. I was prepared to do this, but gave
up owing to the complete and total lack of any API or design documentation
pertaining to the requirements for disk drivers and disklabel management or
wedges. I would strongly encourage someone who knows something about
wedges or disklabels to write a simple document (or even a dummy driver)
showing which interfaces should be provided in new mass storage drivers.
This work was funded by the Champaign-Urbana Community Wireless Network
Project.
2006-10-20 10:41:46 +04:00
|
|
|
buf[i] = addr & 0xff;
|
|
|
|
addr >>= 8;
|
2006-10-07 11:21:13 +04:00
|
|
|
}
|
|
|
|
spi_transfer_init(&trans);
|
|
|
|
spi_chunk_init(&chunk1, addrlen + 1, buf, NULL);
|
|
|
|
spi_transfer_add(&trans, &chunk1);
|
|
|
|
if (cnt) {
|
|
|
|
spi_chunk_init(&chunk2, cnt, wdata, rdata);
|
|
|
|
spi_transfer_add(&trans, &chunk2);
|
|
|
|
}
|
|
|
|
|
|
|
|
spi_transfer(sc->sc_handle, &trans);
|
|
|
|
spi_wait(&trans);
|
|
|
|
|
|
|
|
if (trans.st_flags & SPI_F_ERROR)
|
|
|
|
return trans.st_errno;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
spiflash_wait(spiflash_handle_t sc, int tmo)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
uint8_t sr;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if ((rv = spiflash_read_status(sc, &sr)) != 0)
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
if ((sr & SPIFLASH_SR_BUSY) == 0)
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* The devices I have all say typical for sector
|
|
|
|
* erase is ~1sec. We check time times that often.
|
|
|
|
* (There is no way to interrupt on this.)
|
|
|
|
*/
|
|
|
|
if (tmo)
|
|
|
|
tsleep(&sr, PWAIT, "spiflash_wait", tmo);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|