Add userland driver to spi framework.

Previously spi would configure the controller to use the lowest speed of
all connected devices since the kernel started and to fail attempted mode
changes. This is now improved to keep individual modes and speeds for each
slave and to reconfigure the controller as necessary for each transfer.

Added man page for spi(9).
This commit is contained in:
mlelstv 2019-02-23 10:43:25 +00:00
parent 65e39aee46
commit 975ce0ba8d
9 changed files with 435 additions and 30 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.1200 2019/02/02 22:39:32 mrg Exp $
# $NetBSD: mi,v 1.1201 2019/02/23 10:43:25 mlelstv Exp $
#
# Note: Don't delete entries from here - mark them as "obsolete" instead,
# unless otherwise stated below.
@ -1112,6 +1112,7 @@
./usr/include/dev/rcons base-obsolete obsolete
./usr/include/dev/sbus base-c-usr
./usr/include/dev/scsipi base-c-usr
./usr/include/dev/spi base-c-usr
./usr/include/dev/sun base-c-usr
./usr/include/dev/tc base-c-usr
./usr/include/dev/usb base-c-usr

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.2259 2019/02/23 03:10:05 kamil Exp $
# $NetBSD: mi,v 1.2260 2019/02/23 10:43:25 mlelstv Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
./etc/mtree/set.comp comp-sys-root
@ -709,6 +709,7 @@
./usr/include/dev/scsipi/ss_mustek.h comp-obsolete obsolete
./usr/include/dev/scsipi/ssvar.h comp-obsolete obsolete
./usr/include/dev/spkrio.h comp-c-include
./usr/include/dev/spi/spi_io.h comp-c-include
./usr/include/dev/sun/disklabel.h comp-c-include
./usr/include/dev/sun/eeprom.h comp-c-include
./usr/include/dev/sun/event_var.h comp-obsolete obsolete
@ -19594,6 +19595,7 @@
./usr/share/man/html9/specificdata_key_delete.html comp-sys-htmlman html
./usr/share/man/html9/specificdata_setspecific.html comp-sys-htmlman html
./usr/share/man/html9/specificdata_setspecific_nowait.html comp-sys-htmlman html
./usr/share/man/html9/spi.html comp-sys-htmlman html
./usr/share/man/html9/spinlockinit.html comp-sys-htmlman html
./usr/share/man/html9/spinlockmgr.html comp-sys-htmlman html
./usr/share/man/html9/spl.html comp-sys-htmlman html
@ -27626,6 +27628,7 @@
./usr/share/man/man9/specificdata_key_delete.9 comp-sys-man .man
./usr/share/man/man9/specificdata_setspecific.9 comp-sys-man .man
./usr/share/man/man9/specificdata_setspecific_nowait.9 comp-sys-man .man
./usr/share/man/man9/spi.9 comp-sys-man .man
./usr/share/man/man9/spinlockinit.9 comp-sys-man .man
./usr/share/man/man9/spinlockmgr.9 comp-sys-man .man
./usr/share/man/man9/spl.9 comp-sys-man .man

View File

@ -1,5 +1,5 @@
#!/bin/sh -
# $NetBSD: MAKEDEV.tmpl,v 1.198 2019/02/23 03:10:06 kamil Exp $
# $NetBSD: MAKEDEV.tmpl,v 1.199 2019/02/23 10:43:25 mlelstv Exp $
#
# Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc.
# All rights reserved.
@ -272,6 +272,7 @@
# se* SCSI Ethernet
# ses* SES/SAF-TE SCSI Devices
# speaker PC speaker (XXX - installed)
# spi* SPI bus device
# sram battery backuped memory (x68k)
# ss* SCSI scanner
# stic* PixelStamp interface chip
@ -1584,6 +1585,12 @@ iic[0-9]*)
mkdev iic$unit c %iic_chr% $unit 600
;;
spi[0-9]*)
unit=${i#spi}
: ${unit:-0}
mkdev spi$unit c %spi_chr% $unit 600
;;
amr[0-9]*)
unit=${i#amr}
mkdev amr$unit c %amr_chr% $unit

View File

@ -1,4 +1,4 @@
.\" $NetBSD: spi.4,v 1.5 2015/08/18 19:40:21 phx Exp $
.\" $NetBSD: spi.4,v 1.6 2019/02/23 10:43:25 mlelstv Exp $
.\"
.\" Copyright (c) 2006 Urbana-Champaign Independent Media Center.
.\" Copyright (c) 2006 Garrett D'Amore.
@ -107,6 +107,7 @@ Microchip MCP4801/MCP4811/MCP4821 digital to analog converter.
Texas Instruments TMP121 temperature sensor.
.El
.Sh SEE ALSO
.Xr spi 9 ,
.Xr m25p 4 ,
.Xr mcp23s17gpio 4 ,
.Xr mcp3kadc 4 ,

163
share/man/man9/spi.9 Normal file
View File

@ -0,0 +1,163 @@
.\" $NetBSD: spi.9,v 1.1 2019/02/23 10:43:25 mlelstv Exp $
.\"
.\" Copyright (c) 2019 The NetBSD Foundation
.\" All rights reserved.
.\"
.\" Written by Michael van Elst
.\"
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
.\" 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.
.\"
.Dd February 23, 2019
.Dt SPI 9
.Os
.Sh NAME
.Nm spi_configure ,
.Nm spi_transfer ,
.Nm spi_transfer_init ,
.Nm spi_chunk_init ,
.Nm spi_transfer_add ,
.Nm spi_wait ,
.Nm spi_done ,
.Nm spi_send ,
.Nm spi_recv ,
.Nm spi_send_recv
.Sh SYNOPSIS
.In dev/spi/spivar.h
.Ft int
.Fo spi_configure
.Fa "struct spi_handle *sh"
.Fa "int mode"
.Fa "int speed"
.Fc
.Ft int
.Fo spi_transfer
.Fa "struct spi_handle *sh"
.Fa "struct spi_transfer *st"
.Fc
.Ft void
.Fo spi_transfer_init
.Fa "struct spi_transfer *st"
.Fc
.Ft void
.Fo spi_chunk_init
.Fa "struct spi_chunk *chunk"
.Fa "int cnt"
.Fa "const uint8_t *wptr"
.Fa "uint8_t *rptr"
.Fc
.Ft void
.Fo spi_transfer_add
.Fa "struct spi_transfer *st"
.Fa "struct spi_chunk *chunk"
.Fc
.Ft void
.Fo spi_wait
.Fa "struct spi_transfer *st"
.Fc
.Ft void
.Fo spi_done
.Fa "struct spi_transfer *st"
.Fa "int err"
.Fc
.Ft int
.Fo spi_recv
.Fa "struct spi_handle *sh"
.Fa "int cnt"
.Fa "uint8_t *data"
.Fc
.Ft int
.Fo spi_send
.Fa "struct spi_handle *sh"
.Fa "int cnt"
.Fa "const uint8_t *data"
.Fc
.Ft int
.Fo spi_send_recv
.Fa "struct spi_handle *sh"
.Fa "int scnt"
.Fa "const uint8_t *snd"
.Fa "int rcnt"
.Fa "const uint8_t *rcv"
.Fc
.Sh DESCRIPTION
SPI is a 4-wire synchronous full-duplex serial bus.
It is commonly used for connecting devices such as EEPROMs,
displays, and other types of integrated circuits.
The
.Nm spi
interface provides a means of communicating with SPI-connected devices.
.Sh FUNCTIONS
The following functions comprise the API provided to drivers of
SPI-connected devices.
.Pp
The
.Fa struct spi_handle
corresponding to the device is passed in the driver attachment.
.Bl -tag -width spi_transfer_init
.It Fn spi_configure "sh" "mode" "speed"
Sets mode and speed for subsequent communication with a SPI slave.
.It Fn spi_transfer "sh" "st"
Queue transfer to SPI controller.
.Fn spi_transfer
returns an errno value when the transfer couldn't be queued.
.It Fn spi_transfer_init "st"
Initialize a transfer structure.
.It Fn spi_chunk_init "chunk" "cnt" "wptr" rptr"
Initialize a chunk structure, each chunk corresponds to
a possibly bi-directional transfer where the same amount
of bytes is shifted in and out.
.It Fn spi_transfer_add "st" "chunk"
Append a chunk to transfer structure.
.It Fn spi_wait "st"
Wait for a transfer to complete. When the transfer has failed
for some reason, the field
.Va st->st_errno
is set to a non-zero value.
.It Fn spi_done "st" "err"
Called back machine-dependent backend to signal completion
of a transfer.
.El
.Pp
For simplicity there are convenience functions that combine
common operations.
These functions return an errno value when the transfer failed.
.Bl -tag -width spi_transfer_init
.It Fn spi_recv "sh" "cnt" "data"
Prepares a chunk for receiving data, queues a transfer and
waits for it to complete.
.It Fn spi_send "sh" "cnt" "data"
Prepares a chunk for sending data, queues a transfer and
waits for it to complete.
.It Fn spi_send_recv "sh" "scnt" "snd" "rcnt" "rcv"
Prepares two chunks for sending data first and then receiving
an answer,
queues a transfer and waits for it to complete.
This is not a full-duplex operation.
.El
.Sh SEE ALSO
.Xr spi 4
.Rs
.Sh HISTORY
The
.Nm spi
API first appeared in
.Nx 4.0 .

6
sys/dev/spi/Makefile Normal file
View File

@ -0,0 +1,6 @@
# $NetBSD: Makefile,v 1.1 2019/02/23 10:43:25 mlelstv Exp $
INCSDIR= /usr/include/dev/spi
INCS= spi_io.h
.include <bsd.kinc.mk>

View File

@ -1,4 +1,4 @@
/* $NetBSD: spi.c,v 1.9 2018/09/03 16:29:33 riastradh Exp $ */
/* $NetBSD: spi.c,v 1.10 2019/02/23 10:43:25 mlelstv Exp $ */
/*-
* Copyright (c) 2006 Urbana-Champaign Independent Media Center.
@ -42,26 +42,55 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.9 2018/09/03 16:29:33 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.10 2019/02/23 10:43:25 mlelstv Exp $");
#include "locators.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/errno.h>
#include <dev/spi/spivar.h>
#include <dev/spi/spi_io.h>
#include "ioconf.h"
#include "locators.h"
struct spi_softc {
struct spi_controller sc_controller;
int sc_mode;
int sc_speed;
int sc_slave;
int sc_nslaves;
struct spi_handle *sc_slaves;
kmutex_t sc_lock;
kcondvar_t sc_cv;
int sc_flags;
#define SPIC_BUSY 1
};
static dev_type_open(spi_open);
static dev_type_close(spi_close);
static dev_type_ioctl(spi_ioctl);
const struct cdevsw spi_cdevsw = {
.d_open = spi_open,
.d_close = spi_close,
.d_read = noread,
.d_write = nowrite,
.d_ioctl = spi_ioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = D_OTHER
};
/*
@ -71,8 +100,12 @@ struct spi_handle {
struct spi_softc *sh_sc;
struct spi_controller *sh_controller;
int sh_slave;
int sh_mode;
int sh_speed;
};
#define SPI_MAXDATA 4096
/*
* API for bus drivers.
*/
@ -142,6 +175,9 @@ spi_attach(device_t parent, device_t self, void *aux)
aprint_naive(": SPI bus\n");
aprint_normal(": SPI bus\n");
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_BIO);
cv_init(&sc->sc_cv, "spictl");
sc->sc_controller = *sba->sba_controller;
sc->sc_nslaves = sba->sba_controller->sct_nslaves;
/* allocate slave structures */
@ -150,6 +186,7 @@ spi_attach(device_t parent, device_t self, void *aux)
sc->sc_speed = 0;
sc->sc_mode = -1;
sc->sc_slave = -1;
/*
* Initialize slave handles
@ -166,6 +203,93 @@ spi_attach(device_t parent, device_t self, void *aux)
config_search_ia(spi_search, self, "spi", NULL);
}
static int
spi_open(dev_t dev, int flag, int fmt, lwp_t *l)
{
struct spi_softc *sc = device_lookup_private(&spi_cd, minor(dev));
if (sc == NULL)
return ENXIO;
return 0;
}
static int
spi_close(dev_t dev, int flag, int fmt, lwp_t *l)
{
return 0;
}
static int
spi_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
{
struct spi_softc *sc = device_lookup_private(&spi_cd, minor(dev));
struct spi_handle *sh;
spi_ioctl_configure_t *sic;
spi_ioctl_transfer_t *sit;
uint8_t *sbuf, *rbuf;
int error;
if (sc == NULL)
return ENXIO;
switch (cmd) {
case SPI_IOCTL_CONFIGURE:
sic = (spi_ioctl_configure_t *)data;
if (sic->sic_addr < 0 || sic->sic_addr >= sc->sc_nslaves) {
error = EINVAL;
break;
}
sh = &sc->sc_slaves[sic->sic_addr];
error = spi_configure(sh, sic->sic_mode, sic->sic_speed);
break;
case SPI_IOCTL_TRANSFER:
sit = (spi_ioctl_transfer_t *)data;
if (sit->sit_addr < 0 || sit->sit_addr >= sc->sc_nslaves) {
error = EINVAL;
break;
}
sh = &sc->sc_slaves[sit->sit_addr];
sbuf = rbuf = NULL;
error = 0;
if (sit->sit_send && sit->sit_sendlen < SPI_MAXDATA) {
sbuf = malloc(sit->sit_sendlen, M_DEVBUF, M_WAITOK);
error = copyin(sit->sit_send, sbuf, sit->sit_sendlen);
}
if (sit->sit_recv && sit->sit_recvlen < SPI_MAXDATA) {
rbuf = malloc(sit->sit_recvlen, M_DEVBUF, M_WAITOK);
}
if (error == 0) {
if (sbuf && rbuf)
error = spi_send_recv(sh,
sit->sit_sendlen, sbuf,
sit->sit_recvlen, rbuf);
else if (sbuf)
error = spi_send(sh,
sit->sit_sendlen, sbuf);
else if (rbuf)
error = spi_recv(sh,
sit->sit_recvlen, rbuf);
}
if (rbuf) {
if (error == 0)
error = copyout(rbuf, sit->sit_recv,
sit->sit_recvlen);
free(rbuf, M_DEVBUF);
}
if (sbuf) {
free(sbuf, M_DEVBUF);
}
break;
default:
error = ENODEV;
break;
}
return error;
}
CFATTACH_DECL_NEW(spi, sizeof(struct spi_softc),
spi_match, spi_attach, NULL, NULL);
@ -181,30 +305,39 @@ CFATTACH_DECL_NEW(spi, sizeof(struct spi_softc),
int
spi_configure(struct spi_handle *sh, int mode, int speed)
{
int s, rv;
struct spi_softc *sc = sh->sh_sc;
struct spi_controller *tag = sh->sh_controller;
/* ensure that request is compatible with other devices on the bus */
if ((sc->sc_mode >= 0) && (sc->sc_mode != mode))
return EINVAL;
sh->sh_mode = mode;
sh->sh_speed = speed;
return 0;
}
s = splbio();
/* pick lowest configured speed */
if (speed == 0)
speed = sc->sc_speed;
if (sc->sc_speed)
speed = uimin(sc->sc_speed, speed);
/*
* Acquire controller
*/
static void
spi_acquire(struct spi_handle *sh)
{
struct spi_softc *sc = sh->sh_sc;
rv = (*tag->sct_configure)(tag->sct_cookie, sh->sh_slave,
mode, speed);
mutex_enter(&sc->sc_lock);
while ((sc->sc_flags & SPIC_BUSY) != 0)
cv_wait(&sc->sc_cv, &sc->sc_lock);
sc->sc_flags |= SPIC_BUSY;
mutex_exit(&sc->sc_lock);
}
if (rv == 0) {
sc->sc_mode = mode;
sc->sc_speed = speed;
}
splx(s);
return rv;
/*
* Release controller
*/
static void
spi_release(struct spi_handle *sh)
{
struct spi_softc *sc = sh->sh_sc;
mutex_enter(&sc->sc_lock);
sc->sc_flags &= ~SPIC_BUSY;
cv_broadcast(&sc->sc_cv);
mutex_exit(&sc->sc_lock);
}
void
@ -212,7 +345,7 @@ spi_transfer_init(struct spi_transfer *st)
{
mutex_init(&st->st_lock, MUTEX_DEFAULT, IPL_BIO);
cv_init(&st->st_cv, "spicv");
cv_init(&st->st_cv, "spixfr");
st->st_flags = 0;
st->st_errno = 0;
@ -246,8 +379,10 @@ spi_transfer_add(struct spi_transfer *st, struct spi_chunk *chunk)
int
spi_transfer(struct spi_handle *sh, struct spi_transfer *st)
{
struct spi_softc *sc = sh->sh_sc;
struct spi_controller *tag = sh->sh_controller;
struct spi_chunk *chunk;
int error;
/*
* Initialize "resid" counters and pointers, so that callers
@ -260,16 +395,45 @@ spi_transfer(struct spi_handle *sh, struct spi_transfer *st)
}
/*
* Match slave to handle's slave.
* Match slave and parameters to handle
*/
st->st_slave = sh->sh_slave;
return (*tag->sct_transfer)(tag->sct_cookie, st);
/*
* Reserve controller during transaction
*/
spi_acquire(sh);
st->st_spiprivate = (void *)sh;
/*
* Reconfigure controller
*
* XXX backends don't configure per-slave parameters
* Whenever we switch slaves or change mode or speed, we
* need to tell the backend.
*/
if (sc->sc_slave != sh->sh_slave
|| sc->sc_mode != sh->sh_mode
|| sc->sc_speed != sh->sh_speed) {
error = (*tag->sct_configure)(tag->sct_cookie,
sh->sh_slave, sh->sh_mode, sh->sh_speed);
if (error)
return error;
}
sc->sc_mode = sh->sh_mode;
sc->sc_speed = sh->sh_speed;
sc->sc_slave = sh->sh_slave;
error = (*tag->sct_transfer)(tag->sct_cookie, st);
return error;
}
void
spi_wait(struct spi_transfer *st)
{
struct spi_handle *sh = st->st_spiprivate;
mutex_enter(&st->st_lock);
while (!(st->st_flags & SPI_F_DONE)) {
@ -278,6 +442,11 @@ spi_wait(struct spi_transfer *st)
mutex_exit(&st->st_lock);
cv_destroy(&st->st_cv);
mutex_destroy(&st->st_lock);
/*
* End transaction
*/
spi_release(sh);
}
void
@ -372,3 +541,4 @@ spi_send_recv(struct spi_handle *sh, int scnt, const uint8_t *snd,
return 0;
}

53
sys/dev/spi/spi_io.h Normal file
View File

@ -0,0 +1,53 @@
/* $NetBSD: spi_io.h,v 1.1 2019/02/23 10:43:25 mlelstv Exp $ */
/*
* Copyright (c) 2019 Michael van Elst
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
* 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_SPI_IO_H_
#define _DEV_SPI_SPI_IO_H_
#include <sys/types.h>
#include <sys/ioccom.h>
#include <sys/uio.h>
typedef struct spi_ioctl_configure {
int sic_addr;
int sic_mode;
int sic_speed;
} spi_ioctl_configure_t;
typedef struct spi_ioctl_transfer {
int sit_addr;
const void *sit_send;
size_t sit_sendlen;
void *sit_recv;
size_t sit_recvlen;
} spi_ioctl_transfer_t;
#define SPI_IOCTL_CONFIGURE _IOW('S', 0, spi_ioctl_configure_t)
#define SPI_IOCTL_TRANSFER _IOW('S', 1, spi_ioctl_transfer_t)
#endif /* _DEV_SPI_SPI_IO_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: spivar.h,v 1.6 2014/07/13 17:12:23 dholland Exp $ */
/* $NetBSD: spivar.h,v 1.7 2019/02/23 10:43:25 mlelstv Exp $ */
/*-
* Copyright (c) 2006 Urbana-Champaign Independent Media Center.
@ -111,6 +111,7 @@ struct spi_transfer {
kmutex_t st_lock;
kcondvar_t st_cv;
void *st_busprivate;
void *st_spiprivate;
};
/* declare a list of transfers */