1 /* $NetBSD: spi.c,v 1.2 2006/10/07 07:21:13 gdamore 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.2 2006/10/07 07:21:13 gdamore 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 device sc_dev; 60 struct spi_controller sc_controller; 61 int sc_mode; 62 int sc_speed; 63 int sc_nslaves; 64 struct spi_handle *sc_slaves; 65 }; 66 67 /* 68 * SPI slave device. We have one of these per slave. 69 */ 70 struct spi_handle { 71 struct spi_softc *sh_sc; 72 struct spi_controller *sh_controller; 73 int sh_slave; 74 }; 75 76 /* 77 * API for bus drivers. 78 */ 79 80 int 81 spibus_print(void *aux, const char *pnp) 82 { 83 84 if (pnp != NULL) 85 aprint_normal("spi at %s", pnp); 86 87 return (UNCONF); 88 } 89 90 91 static int 92 spi_match(struct device *parent, struct cfdata *cf, void *aux) 93 { 94 95 return 1; 96 } 97 98 static int 99 spi_print(void *aux, const char *pnp) 100 { 101 struct spi_attach_args *sa = aux; 102 103 if (sa->sa_handle->sh_slave != -1) 104 aprint_normal(" slave %d", sa->sa_handle->sh_slave); 105 106 return (UNCONF); 107 } 108 109 static int 110 spi_search(struct device *parent, struct cfdata *cf, const int *ldesc, 111 void *aux) 112 { 113 struct spi_softc *sc = (void *)parent; 114 struct spi_attach_args sa; 115 int addr; 116 117 addr = cf->cf_loc[SPICF_SLAVE]; 118 if ((addr < 0) || (addr >= sc->sc_controller.sct_nslaves)) { 119 return -1; 120 } 121 122 sa.sa_handle = &sc->sc_slaves[addr]; 123 124 if (config_match(parent, cf, &sa) > 0) 125 config_attach(parent, cf, &sa, spi_print); 126 127 return 0; 128 } 129 130 /* 131 * API for device drivers. 132 * 133 * We provide wrapper routines to decouple the ABI for the SPI 134 * device drivers from the ABI for the SPI bus drivers. 135 */ 136 static void 137 spi_attach(struct device *parent, struct device *self, void *aux) 138 { 139 struct spi_softc *sc = device_private(self); 140 struct spibus_attach_args *sba = aux; 141 int i; 142 143 aprint_naive(": SPI bus\n"); 144 aprint_normal(": SPI bus\n"); 145 146 sc->sc_controller = *sba->sba_controller; 147 /* allocate slave structures */ 148 sc->sc_slaves = malloc(sizeof (struct spi_handle) * sc->sc_nslaves, 149 M_DEVBUF, M_WAITOK | M_ZERO); 150 151 sc->sc_speed = 0; 152 sc->sc_mode = -1; 153 154 /* 155 * Initialize slave handles 156 */ 157 sc->sc_nslaves = sba->sba_controller->sct_nslaves; 158 for (i = 0; i < sc->sc_nslaves; i++) { 159 sc->sc_slaves[i].sh_slave = i; 160 sc->sc_slaves[i].sh_sc = sc; 161 sc->sc_slaves[i].sh_controller = &sc->sc_controller; 162 } 163 164 /* 165 * Locate and attach child devices 166 */ 167 config_search_ia(spi_search, self, "spi", NULL); 168 } 169 170 CFATTACH_DECL(spi, sizeof(struct spi_softc), 171 spi_match, spi_attach, NULL, NULL); 172 173 /* 174 * Configure. This should be the first thing that the SPI driver 175 * should do, to configure which mode (e.g. SPI_MODE_0, which is the 176 * same as Philips Microwire mode), and speed. If the bus driver 177 * cannot run fast enough, then it should just configure the fastest 178 * mode that it can support. If the bus driver cannot run slow 179 * enough, then the device is incompatible and an error should be 180 * returned. 181 */ 182 int 183 spi_configure(struct spi_handle *sh, int mode, int speed) 184 { 185 int s, rv; 186 struct spi_softc *sc = sh->sh_sc; 187 struct spi_controller *tag = sh->sh_controller; 188 189 /* ensure that request is compatible with other devices on the bus */ 190 if ((sc->sc_mode >= 0) && (sc->sc_mode != mode)) 191 return EINVAL; 192 193 s = splserial(); 194 /* pick lowest configured speed */ 195 if (speed == 0) 196 speed = sc->sc_speed; 197 if (sc->sc_speed) 198 speed = min(sc->sc_speed, speed); 199 200 rv = (*tag->sct_configure)(tag->sct_cookie, sh->sh_slave, 201 mode, speed); 202 203 if (rv == 0) { 204 sc->sc_mode = mode; 205 sc->sc_speed = speed; 206 } 207 splx(s); 208 return rv; 209 } 210 211 void 212 spi_transfer_init(struct spi_transfer *st) 213 { 214 215 simple_lock_init(&st->st_lock); 216 st->st_flags = 0; 217 st->st_errno = 0; 218 st->st_done = NULL; 219 st->st_chunks = NULL; 220 st->st_private = NULL; 221 st->st_slave = -1; 222 } 223 224 void 225 spi_chunk_init(struct spi_chunk *chunk, int cnt, const uint8_t *wptr, 226 uint8_t *rptr) 227 { 228 229 chunk->chunk_write = chunk->chunk_wptr = wptr; 230 chunk->chunk_read = chunk->chunk_read = rptr; 231 chunk->chunk_rresid = chunk->chunk_wresid = chunk->chunk_count = cnt; 232 chunk->chunk_next = NULL; 233 } 234 235 void 236 spi_transfer_add(struct spi_transfer *st, struct spi_chunk *chunk) 237 { 238 struct spi_chunk **cpp; 239 240 /* this is an O(n) insert -- perhaps we should use a simpleq? */ 241 for (cpp = &st->st_chunks; *cpp; cpp = &(*cpp)->chunk_next); 242 *cpp = chunk; 243 } 244 245 int 246 spi_transfer(struct spi_handle *sh, struct spi_transfer *st) 247 { 248 struct spi_controller *tag = sh->sh_controller; 249 struct spi_chunk *chunk; 250 251 /* 252 * Initialize "resid" counters and pointers, so that callers 253 * and bus drivers don't have to. 254 */ 255 for (chunk = st->st_chunks; chunk; chunk = chunk->chunk_next) { 256 chunk->chunk_wresid = chunk->chunk_rresid = chunk->chunk_count; 257 chunk->chunk_wptr = chunk->chunk_write; 258 chunk->chunk_rptr = chunk->chunk_read; 259 } 260 261 /* 262 * Match slave to handle's slave. 263 */ 264 st->st_slave = sh->sh_slave; 265 266 return (*tag->sct_transfer)(tag->sct_cookie, st); 267 } 268 269 void 270 spi_wait(struct spi_transfer *st) 271 { 272 int s; 273 274 s = splserial(); 275 simple_lock(&st->st_lock); 276 while (!st->st_flags & SPI_F_DONE) { 277 ltsleep(st, PWAIT, "spi_wait", 0, &st->st_lock); 278 } 279 simple_unlock(&st->st_lock); 280 splx(s); 281 } 282 283 void 284 spi_done(struct spi_transfer *st, int err) 285 { 286 int s; 287 288 s = splserial(); 289 290 if ((st->st_errno = err) != 0) { 291 st->st_flags |= SPI_F_ERROR; 292 } 293 st->st_flags |= SPI_F_DONE; 294 if (st->st_done != NULL) { 295 (*st->st_done)(st); 296 } else { 297 298 simple_lock(&st->st_lock); 299 wakeup(st); 300 simple_unlock(&st->st_lock); 301 } 302 splx(s); 303 } 304 305 /* 306 * Some convenience routines. These routines block until the work 307 * is done. 308 * 309 * spi_recv - receives data from the bus 310 * 311 * spi_send - sends data to the bus 312 * 313 * spi_send_recv - sends data to the bus, and then receives. Note that this is 314 * done synchronously, i.e. send a command and get the response. This is 315 * not full duplex. If you wnat full duplex, you can't use these convenience 316 * wrappers. 317 */ 318 int 319 spi_recv(struct spi_handle *sh, int cnt, uint8_t *data) 320 { 321 struct spi_transfer trans; 322 struct spi_chunk chunk; 323 324 spi_transfer_init(&trans); 325 spi_chunk_init(&chunk, cnt, NULL, data); 326 spi_transfer_add(&trans, &chunk); 327 328 /* enqueue it and wait for it to complete */ 329 spi_transfer(sh, &trans); 330 spi_wait(&trans); 331 332 if (trans.st_flags & SPI_F_ERROR) 333 return trans.st_errno; 334 335 return 0; 336 } 337 338 int 339 spi_send(struct spi_handle *sh, int cnt, const uint8_t *data) 340 { 341 struct spi_transfer trans; 342 struct spi_chunk chunk; 343 344 spi_transfer_init(&trans); 345 spi_chunk_init(&chunk, cnt, data, NULL); 346 spi_transfer_add(&trans, &chunk); 347 348 /* enqueue it and wait for it to complete */ 349 spi_transfer(sh, &trans); 350 spi_wait(&trans); 351 352 if (trans.st_flags & SPI_F_ERROR) 353 return trans.st_errno; 354 355 return 0; 356 } 357 358 int 359 spi_send_recv(struct spi_handle *sh, int scnt, const uint8_t *snd, 360 int rcnt, uint8_t *rcv) 361 { 362 struct spi_transfer trans; 363 struct spi_chunk chunk1, chunk2; 364 365 spi_transfer_init(&trans); 366 spi_chunk_init(&chunk1, scnt, snd, NULL); 367 spi_chunk_init(&chunk2, rcnt, NULL, rcv); 368 spi_transfer_add(&trans, &chunk1); 369 spi_transfer_add(&trans, &chunk2); 370 371 /* enqueue it and wait for it to complete */ 372 spi_transfer(sh, &trans); 373 spi_wait(&trans); 374 375 if (trans.st_flags & SPI_F_ERROR) 376 return trans.st_errno; 377 378 return 0; 379 } 380