Add spiflash driver, and M25P instance, used for STMicro flash devices
found on Meraki Mini (for example).
This commit is contained in:
parent
5bf8068cbb
commit
c36ea7cf37
@ -1,4 +1,4 @@
|
||||
# $NetBSD: files.spi,v 1.1 2006/10/02 07:18:19 gdamore Exp $
|
||||
# $NetBSD: files.spi,v 1.2 2006/10/07 07:21:13 gdamore Exp $
|
||||
|
||||
define spibus { }
|
||||
|
||||
@ -10,6 +10,17 @@ file dev/spi/spi.c spi | spibus
|
||||
# SPI client devices
|
||||
#
|
||||
|
||||
# Common SPI flash support
|
||||
define spiflashbus { }
|
||||
device spiflash: disk
|
||||
attach spiflash at spiflashbus
|
||||
file dev/spi/spiflash.c spiflash | spiflashbus
|
||||
|
||||
# STMicro M25P SPI flash
|
||||
device m25p: spiflashbus
|
||||
attach m25p at spi
|
||||
file dev/spi/m25p.c m25p
|
||||
|
||||
# TI TMP121 digital temperature sensor
|
||||
device tmp121temp: sysmon_envsys
|
||||
attach tmp121temp at spi
|
||||
|
206
sys/dev/spi/m25p.c
Normal file
206
sys/dev/spi/m25p.c
Normal file
@ -0,0 +1,206 @@
|
||||
/* $NetBSD: m25p.c,v 1.1 2006/10/07 07:21:13 gdamore Exp $ */
|
||||
|
||||
/*-
|
||||
* 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>
|
||||
__KERNEL_RCSID(0, "$NetBSD: m25p.c,v 1.1 2006/10/07 07:21:13 gdamore Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/kernel.h>
|
||||
|
||||
#include <dev/sysmon/sysmonvar.h>
|
||||
|
||||
#include <dev/spi/spivar.h>
|
||||
#include <dev/spi/spiflash.h>
|
||||
|
||||
/*
|
||||
* Device driver for STMicroelectronics M25P Family SPI Flash Devices
|
||||
*/
|
||||
|
||||
static int m25p_match(struct device *, struct cfdata *, void *);
|
||||
static void m25p_attach(struct device *, struct device *, void *);
|
||||
static const char *m25p_getname(void *);
|
||||
static struct spi_handle *m25p_gethandle(void *);
|
||||
static int m25p_getflags(void *);
|
||||
static int m25p_getsize(void *, int);
|
||||
|
||||
struct m25p_softc {
|
||||
struct device sc_dev;
|
||||
struct spi_handle *sc_sh;
|
||||
const char *sc_name;
|
||||
int sc_sizes[SPIFLASH_SIZE_COUNT];
|
||||
int sc_flags;
|
||||
};
|
||||
|
||||
CFATTACH_DECL(m25p, sizeof(struct m25p_softc),
|
||||
m25p_match, m25p_attach, NULL, NULL);
|
||||
|
||||
static const struct spiflash_hw_if m25p_hw_if = {
|
||||
.sf_getname = m25p_getname,
|
||||
.sf_gethandle = m25p_gethandle,
|
||||
.sf_getsize = m25p_getsize,
|
||||
.sf_getflags = m25p_getflags,
|
||||
};
|
||||
|
||||
static const struct m25p_info {
|
||||
uint8_t sig;
|
||||
uint8_t mfgid;
|
||||
uint16_t devid;
|
||||
const char *name;
|
||||
uint16_t size; /* in KB */
|
||||
uint16_t sector; /* in KB */
|
||||
uint16_t mhz;
|
||||
} m25p_infos[] = {
|
||||
{ 0x16, 0x20, 0x2017, "STMicro M25P64", 8192, 64 }, /* 64Mbit */
|
||||
{ 0x12, 0x20, 0x2013, "STMicro M25P40", 512, 64 }, /* 4Mbit */
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
m25p_match(struct device *parent, struct cfdata *cf, void *aux)
|
||||
{
|
||||
struct spi_attach_args *sa = aux;
|
||||
|
||||
/* configure for 20MHz, which is the max for normal reads */
|
||||
if (spi_configure(sa->sa_handle, SPI_MODE_0, 20000000))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
m25p_attach(struct device *parent, struct device *self, void *aux)
|
||||
{
|
||||
struct m25p_softc *sc = device_private(self);
|
||||
struct spi_attach_args *sa = aux;
|
||||
const struct m25p_info *info;
|
||||
uint8_t buf[4];
|
||||
uint8_t cmd;
|
||||
uint8_t mfgid;
|
||||
uint8_t sig;
|
||||
uint16_t devid;
|
||||
|
||||
sc->sc_sh = sa->sa_handle;
|
||||
|
||||
/* first we try JEDEC ID read */
|
||||
|
||||
cmd = SPIFLASH_CMD_RDJI;
|
||||
if (spi_send_recv(sa->sa_handle, 1, &cmd, 3, buf)) {
|
||||
aprint_error(": failed to get JEDEC identification\n");
|
||||
return;
|
||||
}
|
||||
mfgid = buf[0];
|
||||
devid = ((uint16_t)buf[1] << 8) | buf[2];
|
||||
|
||||
if ((mfgid == 0xff) || (mfgid == 0)) {
|
||||
cmd = SPIFLASH_CMD_RDID;
|
||||
if (spi_send_recv(sa->sa_handle, 1, &cmd, 4, buf)) {
|
||||
aprint_error(": failed to get legacy signature\n");
|
||||
return;
|
||||
}
|
||||
sig = buf[3];
|
||||
} else {
|
||||
sig = 0;
|
||||
}
|
||||
|
||||
/* okay did it match */
|
||||
for (info = m25p_infos; info->name; info++) {
|
||||
if ((info->mfgid == mfgid) && (info->devid == devid))
|
||||
break;
|
||||
if (sig == info->sig)
|
||||
break;
|
||||
}
|
||||
|
||||
if (info->name == NULL) {
|
||||
aprint_error(": unknown or unsupported device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sc->sc_name = info->name;
|
||||
sc->sc_sh = sa->sa_handle;
|
||||
sc->sc_sizes[SPIFLASH_SIZE_DEVICE] = info->size * 1024;
|
||||
sc->sc_sizes[SPIFLASH_SIZE_ERASE] = info->sector * 1024;
|
||||
sc->sc_sizes[SPIFLASH_SIZE_WRITE] = 256;
|
||||
sc->sc_sizes[SPIFLASH_SIZE_READ] = -1;
|
||||
|
||||
sc->sc_flags = SPIFLASH_FLAG_FAST_READ;
|
||||
|
||||
printf("\n");
|
||||
|
||||
spiflash_attach_mi(&m25p_hw_if, sc, self);
|
||||
}
|
||||
|
||||
const char *
|
||||
m25p_getname(void *cookie)
|
||||
{
|
||||
struct m25p_softc *sc = cookie;
|
||||
|
||||
return (sc->sc_name);
|
||||
}
|
||||
|
||||
struct spi_handle *
|
||||
m25p_gethandle(void *cookie)
|
||||
{
|
||||
struct m25p_softc *sc = cookie;
|
||||
|
||||
return (sc->sc_sh);
|
||||
}
|
||||
|
||||
int
|
||||
m25p_getflags(void *cookie)
|
||||
{
|
||||
struct m25p_softc *sc = cookie;
|
||||
|
||||
return (sc->sc_flags);
|
||||
}
|
||||
|
||||
int
|
||||
m25p_getsize(void *cookie, int idx)
|
||||
{
|
||||
struct m25p_softc *sc = cookie;
|
||||
|
||||
if ((idx < 0) || (idx >= SPIFLASH_SIZE_COUNT))
|
||||
return -1;
|
||||
return (sc->sc_sizes[idx]);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: spi.c,v 1.1 2006/10/02 07:18:19 gdamore Exp $ */
|
||||
/* $NetBSD: spi.c,v 1.2 2006/10/07 07:21:13 gdamore Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2006 Urbana-Champaign Independent Media Center.
|
||||
@ -42,7 +42,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.1 2006/10/02 07:18:19 gdamore Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.2 2006/10/07 07:21:13 gdamore Exp $");
|
||||
|
||||
#include "locators.h"
|
||||
|
||||
@ -149,6 +149,7 @@ spi_attach(struct device *parent, struct device *self, void *aux)
|
||||
M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
|
||||
sc->sc_speed = 0;
|
||||
sc->sc_mode = -1;
|
||||
|
||||
/*
|
||||
* Initialize slave handles
|
||||
|
628
sys/dev/spi/spiflash.c
Normal file
628
sys/dev/spi/spiflash.c
Normal file
@ -0,0 +1,628 @@
|
||||
/* $NetBSD: spiflash.c,v 1.1 2006/10/07 07:21:13 gdamore Exp $ */
|
||||
|
||||
/*-
|
||||
* 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>
|
||||
__KERNEL_RCSID(0, "$NetBSD: spiflash.c,v 1.1 2006/10/07 07:21:13 gdamore Exp $");
|
||||
|
||||
#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 device sc_dev;
|
||||
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;
|
||||
|
||||
struct bufq_state *sc_bufq;
|
||||
struct proc *sc_thread;
|
||||
};
|
||||
|
||||
#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
|
||||
STATIC int spiflash_match(struct device *, struct cfdata *, void *);
|
||||
STATIC void spiflash_attach(struct device *, struct device *, void *);
|
||||
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 *);
|
||||
STATIC void spiflash_process(spiflash_handle_t, struct buf *);
|
||||
STATIC void spiflash_thread(void *);
|
||||
STATIC void spiflash_thread_create(void *);
|
||||
|
||||
CFATTACH_DECL(spiflash, sizeof(struct spiflash_softc),
|
||||
spiflash_match, spiflash_attach, NULL, NULL);
|
||||
|
||||
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,
|
||||
.d_type = D_DISK,
|
||||
};
|
||||
|
||||
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,
|
||||
.d_type = D_DISK,
|
||||
};
|
||||
|
||||
static struct dkdriver spiflash_dkdriver = { spiflash_strategy, NULL };
|
||||
|
||||
spiflash_handle_t
|
||||
spiflash_attach_mi(const struct spiflash_hw_if *hw, void *cookie,
|
||||
struct device *dev)
|
||||
{
|
||||
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
|
||||
spiflash_match(struct device *parent, struct cfdata *cf, void *aux)
|
||||
{
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
spiflash_attach(struct device *parent, struct device *self, void *aux)
|
||||
{
|
||||
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 */
|
||||
aprint_normal("%s: %d KB, %d sectors of %d KB each\n",
|
||||
sc->sc_dev.dv_xname, sc->sc_device_size / 1024,
|
||||
sc->sc_device_size / sc->sc_erase_size,
|
||||
sc->sc_erase_size / 1024);
|
||||
|
||||
/* first-come first-served strategy works best for us */
|
||||
bufq_alloc(&sc->sc_bufq, "fcfs", BUFQ_SORT_RAWBLOCK);
|
||||
|
||||
/* arrange to allocate the kthread */
|
||||
kthread_create(spiflash_thread_create, sc);
|
||||
|
||||
sc->sc_dk.dk_driver = &spiflash_dkdriver;
|
||||
sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
|
||||
|
||||
disk_attach(&sc->sc_dk);
|
||||
}
|
||||
|
||||
int
|
||||
spiflash_open(dev_t dev, int flags, int mode, struct lwp *l)
|
||||
{
|
||||
spiflash_handle_t sc;
|
||||
|
||||
if ((sc = device_lookup(&spiflash_cd, DISKUNIT(dev))) == NULL)
|
||||
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;
|
||||
|
||||
if ((sc = device_lookup(&spiflash_cd, DISKUNIT(dev))) == NULL)
|
||||
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
|
||||
spiflash_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct lwp *l)
|
||||
{
|
||||
spiflash_handle_t sc;
|
||||
|
||||
if ((sc = device_lookup(&spiflash_cd, DISKUNIT(dev))) == NULL)
|
||||
return ENXIO;
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
void
|
||||
spiflash_strategy(struct buf *bp)
|
||||
{
|
||||
spiflash_handle_t sc;
|
||||
int sz;
|
||||
int s;
|
||||
|
||||
sc = device_lookup(&spiflash_cd, DISKUNIT(bp->b_dev));
|
||||
if (sc == NULL) {
|
||||
bp->b_error = ENXIO;
|
||||
bp->b_flags |= B_ERROR;
|
||||
biodone(bp);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bp->b_bcount % sc->sc_write_size) ||
|
||||
(bp->b_blkno < 0)) {
|
||||
bp->b_error = EINVAL;
|
||||
bp->b_flags |= B_ERROR;
|
||||
biodone(bp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* no work? */
|
||||
if (bp->b_bcount == 0) {
|
||||
biodone(bp);
|
||||
return;
|
||||
}
|
||||
|
||||
sz = bp->b_bcount / DEV_BSIZE;
|
||||
|
||||
if ((bp->b_blkno + sz) > sc->sc_device_blks) {
|
||||
sz = sc->sc_device_blks - bp->b_blkno;
|
||||
/* exactly at end of media? return EOF */
|
||||
if (sz == 0) {
|
||||
biodone(bp);
|
||||
return;
|
||||
}
|
||||
if (sz < 0) {
|
||||
/* past end of disk */
|
||||
bp->b_error = EINVAL;
|
||||
bp->b_flags |= B_ERROR;
|
||||
biodone(bp);
|
||||
}
|
||||
/* otherwise truncate it */
|
||||
bp->b_bcount = sz << DEV_BSHIFT;
|
||||
}
|
||||
|
||||
bp->b_resid = bp->b_bcount;
|
||||
|
||||
/* all ready, hand off to thread for async processing */
|
||||
s = splbio();
|
||||
BUFQ_PUT(sc->sc_bufq, bp);
|
||||
wakeup(&sc->sc_thread);
|
||||
splx(s);
|
||||
}
|
||||
|
||||
void
|
||||
spiflash_process(spiflash_handle_t sc, struct buf *bp)
|
||||
{
|
||||
int cnt;
|
||||
size_t addr;
|
||||
uint8_t *data;
|
||||
|
||||
addr = bp->b_blkno * DEV_BSIZE;
|
||||
data = bp->b_data;
|
||||
|
||||
while (bp->b_resid > 0) {
|
||||
cnt = max(sc->sc_write_size, DEV_BSIZE);
|
||||
if (bp->b_flags & B_READ) {
|
||||
bp->b_error = sc->sc_read(sc, addr, cnt, data);
|
||||
} else {
|
||||
bp->b_error = sc->sc_write(sc, addr, cnt, data);
|
||||
}
|
||||
if (bp->b_error) {
|
||||
bp->b_flags |= B_ERROR;
|
||||
biodone(bp);
|
||||
return;
|
||||
}
|
||||
bp->b_resid -= cnt;
|
||||
data += cnt;
|
||||
addr += cnt;
|
||||
}
|
||||
biodone(bp);
|
||||
}
|
||||
|
||||
void
|
||||
spiflash_thread(void *arg)
|
||||
{
|
||||
spiflash_handle_t sc = arg;
|
||||
struct buf *bp;
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
for (;;) {
|
||||
if ((bp = BUFQ_GET(sc->sc_bufq)) == NULL) {
|
||||
tsleep(&sc->sc_thread, PRIBIO, "spiflash_thread", 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
spiflash_process(sc, bp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
spiflash_thread_create(void *arg)
|
||||
{
|
||||
spiflash_handle_t sc = arg;
|
||||
|
||||
kthread_create1(spiflash_thread, arg, &sc->sc_thread,
|
||||
"spiflash_thread");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* the second test is to test against wrap */
|
||||
if ((start > sc->sc_device_size) ||
|
||||
((start + size) > sc->sc_device_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;
|
||||
|
||||
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;
|
||||
int align;
|
||||
|
||||
align = sc->sc_write_size;
|
||||
if (sc->sc_read_size > 0)
|
||||
align = sc->sc_read_size;
|
||||
|
||||
if ((start % align) || (size % align))
|
||||
return EINVAL;
|
||||
|
||||
/* the second test is to test against wrap */
|
||||
if ((start > sc->sc_device_size) ||
|
||||
((start + size) > sc->sc_device_size))
|
||||
return EINVAL;
|
||||
|
||||
while (size) {
|
||||
int cnt;
|
||||
|
||||
if ((rv = spiflash_write_enable(sc)) != 0) {
|
||||
spiflash_write_disable(sc);
|
||||
return rv;
|
||||
}
|
||||
|
||||
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) {
|
||||
spiflash_write_disable(sc);
|
||||
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--) {
|
||||
buf[i] = (addr >> ((i - 1) * 8)) & 0xff;
|
||||
}
|
||||
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;
|
||||
}
|
153
sys/dev/spi/spiflash.h
Normal file
153
sys/dev/spi/spiflash.h
Normal file
@ -0,0 +1,153 @@
|
||||
/* $NetBSD: spiflash.h,v 1.1 2006/10/07 07:21:13 gdamore Exp $ */
|
||||
|
||||
/*-
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_SPI_SPIFLASH_H_
|
||||
#define _DEV_SPI_SPIFLASH_H_
|
||||
|
||||
#define SPIFLASH_CMD_RDSR 0x05 /* read status register */
|
||||
#define SPIFLASH_CMD_WRSR 0x01 /* write status register */
|
||||
#define SPIFLASH_CMD_WREN 0x06 /* enable WRSR */
|
||||
#define SPIFLASH_CMD_WRDI 0x04 /* disable WRSR */
|
||||
|
||||
/*
|
||||
* Different chips offer different ways to read a device ID, although
|
||||
* newer parts should all offer the standard JEDEC variant.
|
||||
* Additionally, many parts have a faster read, though how to make use
|
||||
* of this sometimes requires special hacks. E.g. some parts use an
|
||||
* extra data pin, and some crank the clock rate up.
|
||||
*/
|
||||
#define SPIFLASH_CMD_READ 0x03 /* read data (normal) */
|
||||
#define SPIFLASH_CMD_RDID 0xab /* read id */
|
||||
#define SPIFLASH_CMD_RDID2 0x90 /* read id (alternate) */
|
||||
#define SPIFLASH_CMD_RDJI 0x9f /* read JEDEC id */
|
||||
#define SPIFLASH_CMD_READFAST 0x0b /* fast read */
|
||||
|
||||
/*
|
||||
* Different chips offer different variations on the sector erase.
|
||||
* E.g. SST parts offer 4k, 32k, and 64k erase sizes on the same part,
|
||||
* with just different cmds. However, at least SST, AMD, and Winbond
|
||||
* all offer at least the main (0xd8) variant.
|
||||
*/
|
||||
#define SPIFLASH_CMD_ERASE 0xd8 /* sector erase */
|
||||
#define SPIFLASH_CMD_ERASE2 0x52 /* sector erase (alternate) */
|
||||
#define SPIFLASH_CMD_ERASE3 0x20 /* sector erase (alternate) */
|
||||
#define SPIFLASH_CMD_ERASE4 0x81 /* page erase */
|
||||
#define SPIFLASH_CMD_CHIPERASE 0xc7 /* chip erase */
|
||||
|
||||
/*
|
||||
* Some parts can stream bytes with the program command, whereas others require
|
||||
* a seperate command sequence for each byte.
|
||||
*/
|
||||
#define SPIFLASH_CMD_PROGRAM 0x02 /* page or byte program */
|
||||
#define SPIFLASH_CMD_PROGRAM_AA 0xad /* program (autoincrement) */
|
||||
|
||||
/*
|
||||
* Some additional commands. Again, mostly device specific.
|
||||
*/
|
||||
#define SPIFLASH_CMD_EBSY 0x70 /* output busy signal (SST) */
|
||||
#define SPIFLASH_CMD_DBSY 0x80 /* disable busy signal (SST) */
|
||||
|
||||
/*
|
||||
* Status register bits. Not all devices implement all bits. In
|
||||
* addition, the meanings of the BP bits seem to vary from device to
|
||||
* device.
|
||||
*/
|
||||
#define SPIFLASH_SR_BUSY 0x01 /* program in progress */
|
||||
#define SPIFLASH_SR_WEL 0x02 /* write enable latch */
|
||||
#define SPIFLASH_SR_BP0 0x04 /* block protect bits */
|
||||
#define SPIFLASH_SR_BP1 0x08
|
||||
#define SPIFLASH_SR_BP2 0x10
|
||||
#define SPIFLASH_SR_BP3 0x20
|
||||
#define SPIFLASH_SR_AAI 0x40 /* auto-increment mode */
|
||||
#define SPIFLASH_SR_SRP 0x80 /* SR write protected */
|
||||
|
||||
/*
|
||||
* This needs to change to accomodate boot-sectored devices.
|
||||
*/
|
||||
|
||||
typedef struct spiflash_softc *spiflash_handle_t;
|
||||
|
||||
struct spiflash_hw_if {
|
||||
/*
|
||||
* Driver MUST provide these.
|
||||
*/
|
||||
const char *(*sf_getname)(void *);
|
||||
struct spi_handle *(*sf_gethandle)(void *);
|
||||
int (*sf_getflags)(void *);
|
||||
int (*sf_getsize)(void *, int);
|
||||
|
||||
/*
|
||||
* SPI framework will provide these if the driver does not.
|
||||
*/
|
||||
int (*sf_erase)(spiflash_handle_t, size_t, size_t);
|
||||
int (*sf_write)(spiflash_handle_t, size_t, size_t,
|
||||
const uint8_t *);
|
||||
int (*sf_read)(spiflash_handle_t, size_t, size_t, uint8_t *);
|
||||
/*
|
||||
* Not implemented yet.
|
||||
*/
|
||||
int (*sf_getstatus)(spiflash_handle_t, int, int);
|
||||
int (*sf_setstatus)(spiflash_handle_t, int, int, int);
|
||||
};
|
||||
|
||||
#define SPIFLASH_SIZE_DEVICE 0
|
||||
#define SPIFLASH_SIZE_ERASE 1
|
||||
#define SPIFLASH_SIZE_WRITE 2 /* return -1 for unlimited */
|
||||
#define SPIFLASH_SIZE_READ 3 /* return -1 for unlimited */
|
||||
#define SPIFLASH_SIZE_COUNT 4
|
||||
|
||||
#define SPIFLASH_FLAG_FAST_READ 0x0004 /* use fast read sequence */
|
||||
|
||||
spiflash_handle_t spiflash_attach_mi(const struct spiflash_hw_if *, void *,
|
||||
struct device *);
|
||||
void spiflash_set_private(spiflash_handle_t, void *);
|
||||
void *spiflash_get_private(spiflash_handle_t);
|
||||
int spiflash_read_status(spiflash_handle_t, uint8_t *);
|
||||
int spiflash_write_disable(spiflash_handle_t);
|
||||
int spiflash_write_enable(spiflash_handle_t);
|
||||
int spiflash_cmd(spiflash_handle_t, uint8_t, size_t, uint32_t, size_t,
|
||||
const uint8_t *, uint8_t *);
|
||||
int spiflash_wait(spiflash_handle_t, int);
|
||||
|
||||
|
||||
#endif /* _DEV_SPI_SPIFLASH_H_ */
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: spivar.h,v 1.1 2006/10/02 07:18:19 gdamore Exp $ */
|
||||
/* $NetBSD: spivar.h,v 1.2 2006/10/07 07:21:13 gdamore Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2006 Urbana-Champaign Independent Media Center.
|
||||
@ -109,11 +109,15 @@ struct spi_transfer {
|
||||
void *st_private;
|
||||
void (*st_done)(struct spi_transfer *);
|
||||
struct simplelock st_lock;
|
||||
void *st_busprivate;
|
||||
};
|
||||
|
||||
/* declare a list of transfers */
|
||||
SIMPLEQ_HEAD(spi_transq, spi_transfer);
|
||||
|
||||
#define spi_transq_init(q) \
|
||||
SIMPLEQ_INIT(q)
|
||||
|
||||
#define spi_transq_enqueue(q, trans) \
|
||||
SIMPLEQ_INSERT_TAIL(q, trans, st_chain)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user