1 /* $NetBSD: spi.c,v 1.2 2006/10/07 07:21:13 gdamore Exp $ */
4 * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
5 * Copyright (c) 2006 Garrett D'Amore.
8 * Portions of this code were written by Garrett D'Amore for the
9 * Champaign-Urbana Community Wireless Network Project.
11 * Redistribution and use in source and binary forms, with or
12 * without modification, are permitted provided that the following
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.
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.
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.2 2006/10/07 07:21:13 gdamore Exp $");
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/device.h>
52 #include <sys/malloc.h>
54 #include <sys/errno.h>
56 #include <dev/spi/spivar.h>
59 struct spi_controller sc_controller
;
63 struct spi_handle
*sc_slaves
;
67 * SPI slave device. We have one of these per slave.
70 struct spi_softc
*sh_sc
;
71 struct spi_controller
*sh_controller
;
76 * API for bus drivers.
80 spibus_print(void *aux
, const char *pnp
)
84 aprint_normal("spi at %s", pnp
);
91 spi_match(device_t parent
, cfdata_t cf
, void *aux
)
98 spi_print(void *aux
, const char *pnp
)
100 struct spi_attach_args
*sa
= aux
;
102 if (sa
->sa_handle
->sh_slave
!= -1)
103 aprint_normal(" slave %d", sa
->sa_handle
->sh_slave
);
109 spi_search(device_t parent
, cfdata_t cf
, const int *ldesc
, void *aux
)
111 struct spi_softc
*sc
= device_private(parent
);
112 struct spi_attach_args sa
;
115 addr
= cf
->cf_loc
[SPICF_SLAVE
];
116 if ((addr
< 0) || (addr
>= sc
->sc_controller
.sct_nslaves
)) {
120 sa
.sa_handle
= &sc
->sc_slaves
[addr
];
122 if (config_match(parent
, cf
, &sa
) > 0)
123 config_attach(parent
, cf
, &sa
, spi_print
);
129 * API for device drivers.
131 * We provide wrapper routines to decouple the ABI for the SPI
132 * device drivers from the ABI for the SPI bus drivers.
135 spi_attach(device_t parent
, device_t self
, void *aux
)
137 struct spi_softc
*sc
= device_private(self
);
138 struct spibus_attach_args
*sba
= aux
;
141 aprint_naive(": SPI bus\n");
142 aprint_normal(": SPI bus\n");
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
);
153 * Initialize slave handles
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
;
163 * Locate and attach child devices
165 config_search_ia(spi_search
, self
, "spi", NULL
);
168 CFATTACH_DECL_NEW(spi
, sizeof(struct spi_softc
),
169 spi_match
, spi_attach
, NULL
, NULL
);
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
181 spi_configure(struct spi_handle
*sh
, int mode
, int speed
)
184 struct spi_softc
*sc
= sh
->sh_sc
;
185 struct spi_controller
*tag
= sh
->sh_controller
;
187 /* ensure that request is compatible with other devices on the bus */
188 if ((sc
->sc_mode
>= 0) && (sc
->sc_mode
!= mode
))
192 /* pick lowest configured speed */
194 speed
= sc
->sc_speed
;
196 speed
= min(sc
->sc_speed
, speed
);
198 rv
= (*tag
->sct_configure
)(tag
->sct_cookie
, sh
->sh_slave
,
203 sc
->sc_speed
= speed
;
210 spi_transfer_init(struct spi_transfer
*st
)
213 simple_lock_init(&st
->st_lock
);
217 st
->st_chunks
= NULL
;
218 st
->st_private
= NULL
;
223 spi_chunk_init(struct spi_chunk
*chunk
, int cnt
, const uint8_t *wptr
,
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
;
234 spi_transfer_add(struct spi_transfer
*st
, struct spi_chunk
*chunk
)
236 struct spi_chunk
**cpp
;
238 /* this is an O(n) insert -- perhaps we should use a simpleq? */
239 for (cpp
= &st
->st_chunks
; *cpp
; cpp
= &(*cpp
)->chunk_next
);
244 spi_transfer(struct spi_handle
*sh
, struct spi_transfer
*st
)
246 struct spi_controller
*tag
= sh
->sh_controller
;
247 struct spi_chunk
*chunk
;
250 * Initialize "resid" counters and pointers, so that callers
251 * and bus drivers don't have to.
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
;
260 * Match slave to handle's slave.
262 st
->st_slave
= sh
->sh_slave
;
264 return (*tag
->sct_transfer
)(tag
->sct_cookie
, st
);
268 spi_wait(struct spi_transfer
*st
)
273 simple_lock(&st
->st_lock
);
274 while (!st
->st_flags
& SPI_F_DONE
) {
275 ltsleep(st
, PWAIT
, "spi_wait", 0, &st
->st_lock
);
277 simple_unlock(&st
->st_lock
);
282 spi_done(struct spi_transfer
*st
, int err
)
288 if ((st
->st_errno
= err
) != 0) {
289 st
->st_flags
|= SPI_F_ERROR
;
291 st
->st_flags
|= SPI_F_DONE
;
292 if (st
->st_done
!= NULL
) {
296 simple_lock(&st
->st_lock
);
298 simple_unlock(&st
->st_lock
);
304 * Some convenience routines. These routines block until the work
307 * spi_recv - receives data from the bus
309 * spi_send - sends data to the bus
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
317 spi_recv(struct spi_handle
*sh
, int cnt
, uint8_t *data
)
319 struct spi_transfer trans
;
320 struct spi_chunk chunk
;
322 spi_transfer_init(&trans
);
323 spi_chunk_init(&chunk
, cnt
, NULL
, data
);
324 spi_transfer_add(&trans
, &chunk
);
326 /* enqueue it and wait for it to complete */
327 spi_transfer(sh
, &trans
);
330 if (trans
.st_flags
& SPI_F_ERROR
)
331 return trans
.st_errno
;
337 spi_send(struct spi_handle
*sh
, int cnt
, const uint8_t *data
)
339 struct spi_transfer trans
;
340 struct spi_chunk chunk
;
342 spi_transfer_init(&trans
);
343 spi_chunk_init(&chunk
, cnt
, data
, NULL
);
344 spi_transfer_add(&trans
, &chunk
);
346 /* enqueue it and wait for it to complete */
347 spi_transfer(sh
, &trans
);
350 if (trans
.st_flags
& SPI_F_ERROR
)
351 return trans
.st_errno
;
357 spi_send_recv(struct spi_handle
*sh
, int scnt
, const uint8_t *snd
,
358 int rcnt
, uint8_t *rcv
)
360 struct spi_transfer trans
;
361 struct spi_chunk chunk1
, chunk2
;
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
);
369 /* enqueue it and wait for it to complete */
370 spi_transfer(sh
, &trans
);
373 if (trans
.st_flags
& SPI_F_ERROR
)
374 return trans
.st_errno
;