Home | History | Annotate | Line # | Download | only in spi
spi.c revision 1.3
      1 /* $NetBSD: spi.c,v 1.3 2008/05/04 14:21:56 xtraeme Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
      5  * Copyright (c) 2006 Garrett D'Amore.
      6  * All rights reserved.
      7  *
      8  * Portions of this code were written by Garrett D'Amore for the
      9  * Champaign-Urbana Community Wireless Network Project.
     10  *
     11  * Redistribution and use in source and binary forms, with or
     12  * without modification, are permitted provided that the following
     13  * conditions are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above
     17  *    copyright notice, this list of conditions and the following
     18  *    disclaimer in the documentation and/or other materials provided
     19  *    with the distribution.
     20  * 3. All advertising materials mentioning features or use of this
     21  *    software must display the following acknowledgements:
     22  *      This product includes software developed by the Urbana-Champaign
     23  *      Independent Media Center.
     24  *	This product includes software developed by Garrett D'Amore.
     25  * 4. Urbana-Champaign Independent Media Center's name and Garrett
     26  *    D'Amore's name may not be used to endorse or promote products
     27  *    derived from this software without specific prior written permission.
     28  *
     29  * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
     30  * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR
     31  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     32  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
     34  * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT,
     35  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     36  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     37  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     38  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     40  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     41  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     42  */
     43 
     44 #include <sys/cdefs.h>
     45 __KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.3 2008/05/04 14:21:56 xtraeme Exp $");
     46 
     47 #include "locators.h"
     48 
     49 #include <sys/param.h>
     50 #include <sys/systm.h>
     51 #include <sys/device.h>
     52 #include <sys/malloc.h>
     53 #include <sys/proc.h>
     54 #include <sys/errno.h>
     55 
     56 #include <dev/spi/spivar.h>
     57 
     58 struct spi_softc {
     59 	struct spi_controller	sc_controller;
     60 	int			sc_mode;
     61 	int			sc_speed;
     62 	int			sc_nslaves;
     63 	struct spi_handle	*sc_slaves;
     64 };
     65 
     66 /*
     67  * SPI slave device.  We have one of these per slave.
     68  */
     69 struct spi_handle {
     70 	struct spi_softc	*sh_sc;
     71 	struct spi_controller	*sh_controller;
     72 	int			sh_slave;
     73 };
     74 
     75 /*
     76  * API for bus drivers.
     77  */
     78 
     79 int
     80 spibus_print(void *aux, const char *pnp)
     81 {
     82 
     83 	if (pnp != NULL)
     84 		aprint_normal("spi at %s", pnp);
     85 
     86 	return (UNCONF);
     87 }
     88 
     89 
     90 static int
     91 spi_match(device_t parent, cfdata_t cf, void *aux)
     92 {
     93 
     94 	return 1;
     95 }
     96 
     97 static int
     98 spi_print(void *aux, const char *pnp)
     99 {
    100 	struct spi_attach_args *sa = aux;
    101 
    102 	if (sa->sa_handle->sh_slave != -1)
    103 		aprint_normal(" slave %d", sa->sa_handle->sh_slave);
    104 
    105 	return (UNCONF);
    106 }
    107 
    108 static int
    109 spi_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
    110 {
    111 	struct spi_softc *sc = device_private(parent);
    112 	struct spi_attach_args sa;
    113 	int addr;
    114 
    115 	addr = cf->cf_loc[SPICF_SLAVE];
    116 	if ((addr < 0) || (addr >= sc->sc_controller.sct_nslaves)) {
    117 		return -1;
    118 	}
    119 
    120 	sa.sa_handle = &sc->sc_slaves[addr];
    121 
    122 	if (config_match(parent, cf, &sa) > 0)
    123 		config_attach(parent, cf, &sa, spi_print);
    124 
    125 	return 0;
    126 }
    127 
    128 /*
    129  * API for device drivers.
    130  *
    131  * We provide wrapper routines to decouple the ABI for the SPI
    132  * device drivers from the ABI for the SPI bus drivers.
    133  */
    134 static void
    135 spi_attach(device_t parent, device_t self, void *aux)
    136 {
    137 	struct spi_softc *sc = device_private(self);
    138 	struct spibus_attach_args *sba = aux;
    139 	int i;
    140 
    141 	aprint_naive(": SPI bus\n");
    142 	aprint_normal(": SPI bus\n");
    143 
    144 	sc->sc_controller = *sba->sba_controller;
    145 	/* allocate slave structures */
    146 	sc->sc_slaves = malloc(sizeof (struct spi_handle) * sc->sc_nslaves,
    147 	    M_DEVBUF, M_WAITOK | M_ZERO);
    148 
    149 	sc->sc_speed = 0;
    150 	sc->sc_mode = -1;
    151 
    152 	/*
    153 	 * Initialize slave handles
    154 	 */
    155 	sc->sc_nslaves = sba->sba_controller->sct_nslaves;
    156 	for (i = 0; i < sc->sc_nslaves; i++) {
    157 		sc->sc_slaves[i].sh_slave = i;
    158 		sc->sc_slaves[i].sh_sc = sc;
    159 		sc->sc_slaves[i].sh_controller = &sc->sc_controller;
    160 	}
    161 
    162 	/*
    163 	 * Locate and attach child devices
    164 	 */
    165 	config_search_ia(spi_search, self, "spi", NULL);
    166 }
    167 
    168 CFATTACH_DECL_NEW(spi, sizeof(struct spi_softc),
    169     spi_match, spi_attach, NULL, NULL);
    170 
    171 /*
    172  * Configure.  This should be the first thing that the SPI driver
    173  * should do, to configure which mode (e.g. SPI_MODE_0, which is the
    174  * same as Philips Microwire mode), and speed.  If the bus driver
    175  * cannot run fast enough, then it should just configure the fastest
    176  * mode that it can support.  If the bus driver cannot run slow
    177  * enough, then the device is incompatible and an error should be
    178  * returned.
    179  */
    180 int
    181 spi_configure(struct spi_handle *sh, int mode, int speed)
    182 {
    183 	int			s, rv;
    184 	struct spi_softc	*sc = sh->sh_sc;
    185 	struct spi_controller	*tag = sh->sh_controller;
    186 
    187 	/* ensure that request is compatible with other devices on the bus */
    188 	if ((sc->sc_mode >= 0) && (sc->sc_mode != mode))
    189 		return EINVAL;
    190 
    191 	s = splserial();
    192 	/* pick lowest configured speed */
    193 	if (speed == 0)
    194 		speed = sc->sc_speed;
    195 	if (sc->sc_speed)
    196 		speed = min(sc->sc_speed, speed);
    197 
    198 	rv = (*tag->sct_configure)(tag->sct_cookie, sh->sh_slave,
    199 	    mode, speed);
    200 
    201 	if (rv == 0) {
    202 		sc->sc_mode = mode;
    203 		sc->sc_speed = speed;
    204 	}
    205 	splx(s);
    206 	return rv;
    207 }
    208 
    209 void
    210 spi_transfer_init(struct spi_transfer *st)
    211 {
    212 
    213 	simple_lock_init(&st->st_lock);
    214 	st->st_flags = 0;
    215 	st->st_errno = 0;
    216 	st->st_done = NULL;
    217 	st->st_chunks = NULL;
    218 	st->st_private = NULL;
    219 	st->st_slave = -1;
    220 }
    221 
    222 void
    223 spi_chunk_init(struct spi_chunk *chunk, int cnt, const uint8_t *wptr,
    224     uint8_t *rptr)
    225 {
    226 
    227 	chunk->chunk_write = chunk->chunk_wptr = wptr;
    228 	chunk->chunk_read = chunk->chunk_read = rptr;
    229 	chunk->chunk_rresid = chunk->chunk_wresid = chunk->chunk_count = cnt;
    230 	chunk->chunk_next = NULL;
    231 }
    232 
    233 void
    234 spi_transfer_add(struct spi_transfer *st, struct spi_chunk *chunk)
    235 {
    236 	struct spi_chunk **cpp;
    237 
    238 	/* this is an O(n) insert -- perhaps we should use a simpleq? */
    239 	for (cpp = &st->st_chunks; *cpp; cpp = &(*cpp)->chunk_next);
    240 	*cpp = chunk;
    241 }
    242 
    243 int
    244 spi_transfer(struct spi_handle *sh, struct spi_transfer *st)
    245 {
    246 	struct spi_controller	*tag = sh->sh_controller;
    247 	struct spi_chunk	*chunk;
    248 
    249 	/*
    250 	 * Initialize "resid" counters and pointers, so that callers
    251 	 * and bus drivers don't have to.
    252 	 */
    253 	for (chunk = st->st_chunks; chunk; chunk = chunk->chunk_next) {
    254 		chunk->chunk_wresid = chunk->chunk_rresid = chunk->chunk_count;
    255 		chunk->chunk_wptr = chunk->chunk_write;
    256 		chunk->chunk_rptr = chunk->chunk_read;
    257 	}
    258 
    259 	/*
    260 	 * Match slave to handle's slave.
    261 	 */
    262 	st->st_slave = sh->sh_slave;
    263 
    264 	return (*tag->sct_transfer)(tag->sct_cookie, st);
    265 }
    266 
    267 void
    268 spi_wait(struct spi_transfer *st)
    269 {
    270 	int	s;
    271 
    272 	s = splserial();
    273 	simple_lock(&st->st_lock);
    274 	while (!st->st_flags & SPI_F_DONE) {
    275 		ltsleep(st, PWAIT, "spi_wait", 0, &st->st_lock);
    276 	}
    277 	simple_unlock(&st->st_lock);
    278 	splx(s);
    279 }
    280 
    281 void
    282 spi_done(struct spi_transfer *st, int err)
    283 {
    284 	int	s;
    285 
    286 	s = splserial();
    287 
    288 	if ((st->st_errno = err) != 0) {
    289 		st->st_flags |= SPI_F_ERROR;
    290 	}
    291 	st->st_flags |= SPI_F_DONE;
    292 	if (st->st_done != NULL) {
    293 		(*st->st_done)(st);
    294 	} else {
    295 
    296 		simple_lock(&st->st_lock);
    297 		wakeup(st);
    298 		simple_unlock(&st->st_lock);
    299 	}
    300 	splx(s);
    301 }
    302 
    303 /*
    304  * Some convenience routines.  These routines block until the work
    305  * is done.
    306  *
    307  * spi_recv - receives data from the bus
    308  *
    309  * spi_send - sends data to the bus
    310  *
    311  * spi_send_recv - sends data to the bus, and then receives.  Note that this is
    312  * done synchronously, i.e. send a command and get the response.  This is
    313  * not full duplex.  If you wnat full duplex, you can't use these convenience
    314  * wrappers.
    315  */
    316 int
    317 spi_recv(struct spi_handle *sh, int cnt, uint8_t *data)
    318 {
    319 	struct spi_transfer	trans;
    320 	struct spi_chunk	chunk;
    321 
    322 	spi_transfer_init(&trans);
    323 	spi_chunk_init(&chunk, cnt, NULL, data);
    324 	spi_transfer_add(&trans, &chunk);
    325 
    326 	/* enqueue it and wait for it to complete */
    327 	spi_transfer(sh, &trans);
    328 	spi_wait(&trans);
    329 
    330 	if (trans.st_flags & SPI_F_ERROR)
    331 		return trans.st_errno;
    332 
    333 	return 0;
    334 }
    335 
    336 int
    337 spi_send(struct spi_handle *sh, int cnt, const uint8_t *data)
    338 {
    339 	struct spi_transfer	trans;
    340 	struct spi_chunk	chunk;
    341 
    342 	spi_transfer_init(&trans);
    343 	spi_chunk_init(&chunk, cnt, data, NULL);
    344 	spi_transfer_add(&trans, &chunk);
    345 
    346 	/* enqueue it and wait for it to complete */
    347 	spi_transfer(sh, &trans);
    348 	spi_wait(&trans);
    349 
    350 	if (trans.st_flags & SPI_F_ERROR)
    351 		return trans.st_errno;
    352 
    353 	return 0;
    354 }
    355 
    356 int
    357 spi_send_recv(struct spi_handle *sh, int scnt, const uint8_t *snd,
    358     int rcnt, uint8_t *rcv)
    359 {
    360 	struct spi_transfer	trans;
    361 	struct spi_chunk	chunk1, chunk2;
    362 
    363 	spi_transfer_init(&trans);
    364 	spi_chunk_init(&chunk1, scnt, snd, NULL);
    365 	spi_chunk_init(&chunk2, rcnt, NULL, rcv);
    366 	spi_transfer_add(&trans, &chunk1);
    367 	spi_transfer_add(&trans, &chunk2);
    368 
    369 	/* enqueue it and wait for it to complete */
    370 	spi_transfer(sh, &trans);
    371 	spi_wait(&trans);
    372 
    373 	if (trans.st_flags & SPI_F_ERROR)
    374 		return trans.st_errno;
    375 
    376 	return 0;
    377 }
    378