1 /* $NetBSD: auspi.c,v 1.2 2007/02/21 22:59:47 thorpej 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: auspi.c,v 1.2 2007/02/21 22:59:47 thorpej Exp $");
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/kernel.h>
52 #include <sys/device.h>
53 #include <sys/errno.h>
56 #include <machine/bus.h>
57 #include <machine/cpu.h>
59 #include <mips/alchemy/include/aubusvar.h>
60 #include <mips/alchemy/include/auvar.h>
62 #include <mips/alchemy/dev/aupscreg.h>
63 #include <mips/alchemy/dev/aupscvar.h>
64 #include <mips/alchemy/dev/auspireg.h>
65 #include <mips/alchemy/dev/auspivar.h>
67 #include <dev/spi/spivar.h>
71 struct aupsc_controller sc_psc
; /* parent controller ops */
72 struct spi_controller sc_spi
; /* SPI implementation ops */
73 struct auspi_machdep sc_md
; /* board-specific support */
74 struct auspi_job
*sc_job
; /* current job */
75 struct spi_chunk
*sc_wchunk
;
76 struct spi_chunk
*sc_rchunk
;
77 void *sc_ih
; /* interrupt handler */
79 struct spi_transfer
*sc_transfer
;
80 bool sc_running
; /* is it processing stuff? */
82 SIMPLEQ_HEAD(,spi_transfer
) sc_q
;
85 #define auspi_select(sc, slave) \
86 (sc)->sc_md.am_select((sc)->sc_md.am_cookie, (slave))
90 STATIC
int auspi_match(struct device
*, struct cfdata
*, void *);
91 STATIC
void auspi_attach(struct device
*, struct device
*, void *);
92 STATIC
int auspi_intr(void *);
94 CFATTACH_DECL(auspi
, sizeof(struct auspi_softc
),
95 auspi_match
, auspi_attach
, NULL
, NULL
);
97 /* SPI service routines */
98 STATIC
int auspi_configure(void *, int, int, int);
99 STATIC
int auspi_transfer(void *, struct spi_transfer
*);
102 STATIC
void auspi_done(struct auspi_softc
*, int);
103 STATIC
void auspi_send(struct auspi_softc
*);
104 STATIC
void auspi_recv(struct auspi_softc
*);
105 STATIC
void auspi_sched(struct auspi_softc
*);
107 #define GETREG(sc, x) \
108 bus_space_read_4(sc->sc_psc.psc_bust, sc->sc_psc.psc_bush, x)
109 #define PUTREG(sc, x, v) \
110 bus_space_write_4(sc->sc_psc.psc_bust, sc->sc_psc.psc_bush, x, v)
113 auspi_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
115 struct aupsc_attach_args
*aa
= aux
;
117 if (strcmp(aa
->aupsc_name
, cf
->cf_name
) != 0)
124 auspi_attach(struct device
*parent
, struct device
*self
, void *aux
)
126 struct auspi_softc
*sc
= device_private(self
);
127 struct aupsc_attach_args
*aa
= aux
;
128 struct spibus_attach_args sba
;
129 const struct auspi_machdep
*md
;
131 if ((md
= auspi_machdep(aa
->aupsc_addr
)) != NULL
) {
135 aprint_normal(": Alchemy PSC SPI protocol\n");
137 sc
->sc_psc
= aa
->aupsc_ctrl
;
140 * Initialize SPI controller
142 sc
->sc_spi
.sct_cookie
= sc
;
143 sc
->sc_spi
.sct_configure
= auspi_configure
;
144 sc
->sc_spi
.sct_transfer
= auspi_transfer
;
147 sc
->sc_spi
.sct_nslaves
= sc
->sc_md
.am_nslaves
;
149 sba
.sba_controller
= &sc
->sc_spi
;
151 /* enable SPI mode */
152 sc
->sc_psc
.psc_enable(sc
, AUPSC_SEL_SPI
);
154 /* initialize the queue */
155 SIMPLEQ_INIT(&sc
->sc_q
);
157 /* make sure interrupts disabled at the SPI */
158 PUTREG(sc
, AUPSC_SPIMSK
, SPIMSK_ALL
);
160 /* enable device interrupts */
161 sc
->sc_ih
= au_intr_establish(aa
->aupsc_irq
, 0, IPL_SERIAL
, IST_LEVEL
,
164 (void) config_found_ia(&sc
->sc_dev
, "spibus", &sba
, spibus_print
);
168 auspi_configure(void *arg
, int slave
, int mode
, int speed
)
170 struct auspi_softc
*sc
= arg
;
174 /* setup interrupt registers */
175 PUTREG(sc
, AUPSC_SPIMSK
, SPIMSK_NORM
);
177 reg
= GETREG(sc
, AUPSC_SPICFG
);
179 reg
&= ~(SPICFG_BRG_MASK
); /* clear BRG */
180 reg
&= ~(SPICFG_DIV_MASK
); /* use pscn_mainclock/2 */
181 reg
&= ~(SPICFG_PSE
); /* disable port swap */
182 reg
&= ~(SPICFG_BI
); /* clear bit clock invert */
183 reg
&= ~(SPICFG_CDE
); /* clear clock phase delay */
184 reg
&= ~(SPICFG_CGE
); /* clear clock gate enable */
185 //reg |= SPICFG_MO; /* master-only mode */
186 reg
|= SPICFG_DE
; /* device enable */
187 reg
|= SPICFG_DD
; /* disable DMA */
188 reg
|= SPICFG_RT_1
; /* 1 byte rx fifo threshold */
189 reg
|= SPICFG_TT_1
; /* 1 byte tx fifo threshold */
190 reg
|= ((8-1) << SPICFG_LEN_SHIFT
);/* always work in 8-bit chunks */
193 * We assume a base clock of 48MHz has been established by the
194 * platform code. The clock divider reduces this to 24MHz.
195 * Next we have to figure out the BRG
197 #define BASECLK 24000000
198 for (brg
= 0; brg
< 64; brg
++) {
199 if (speed
>= (BASECLK
/ ((brg
+ 1) * 2))) {
205 * Does the device want to go even slower? Our minimum speed without
206 * changing other assumptions, and complicating the code even further,
207 * is 24MHz/128, or 187.5kHz. That should be slow enough for any
208 * device we're likely to encounter.
210 if (speed
< (BASECLK
/ ((brg
+ 1) * 2))) {
213 reg
&= ~SPICFG_BRG_MASK
;
214 reg
|= (brg
<< SPICFG_BRG_SHIFT
);
217 * I'm not entirely confident that these values are correct.
218 * But at least mode 0 appears to work properly with the
219 * devices I have tested. The documentation seems to suggest
220 * that I have the meaning of the clock delay bit inverted.
224 reg
|= 0; /* CPHA = 0, CPOL = 0 */
227 reg
|= SPICFG_CDE
; /* CPHA = 1, CPOL = 0 */
230 reg
|= SPICFG_BI
; /* CPHA = 0, CPOL = 1 */
233 reg
|= SPICFG_CDE
| SPICFG_BI
; /* CPHA = 1, CPOL = 1 */
239 PUTREG(sc
, AUPSC_SPICFG
, reg
);
241 for (i
= 1000000; i
; i
-= 10) {
242 if (GETREG(sc
, AUPSC_SPISTAT
) & SPISTAT_DR
) {
251 auspi_send(struct auspi_softc
*sc
)
254 struct spi_chunk
*chunk
;
257 while ((chunk
= sc
->sc_wchunk
) != NULL
) {
259 while (chunk
->chunk_wresid
) {
261 /* transmit fifo full? */
262 if (GETREG(sc
, AUPSC_SPISTAT
) & SPISTAT_TF
) {
266 if (chunk
->chunk_wptr
) {
267 data
= *chunk
->chunk_wptr
++;
271 chunk
->chunk_wresid
--;
273 /* if the last outbound character, mark it */
274 if ((chunk
->chunk_wresid
== 0) &&
275 (chunk
->chunk_next
== NULL
)) {
278 PUTREG(sc
, AUPSC_SPITXRX
, data
);
281 /* advance to next transfer */
282 sc
->sc_wchunk
= sc
->sc_wchunk
->chunk_next
;
287 auspi_recv(struct auspi_softc
*sc
)
290 struct spi_chunk
*chunk
;
292 while ((chunk
= sc
->sc_rchunk
) != NULL
) {
293 while (chunk
->chunk_rresid
) {
296 if ((GETREG(sc
, AUPSC_SPISTAT
) & SPISTAT_RE
) != 0) {
300 /* collect rx data */
301 data
= GETREG(sc
, AUPSC_SPITXRX
);
302 if (chunk
->chunk_rptr
) {
303 *chunk
->chunk_rptr
++ = data
& 0xff;
306 chunk
->chunk_rresid
--;
309 /* advance next to next transfer */
310 sc
->sc_rchunk
= sc
->sc_rchunk
->chunk_next
;
315 auspi_sched(struct auspi_softc
*sc
)
317 struct spi_transfer
*st
;
320 while ((st
= spi_transq_first(&sc
->sc_q
)) != NULL
) {
322 /* remove the item */
323 spi_transq_dequeue(&sc
->sc_q
);
325 /* note that we are working on it */
326 sc
->sc_transfer
= st
;
328 if ((err
= auspi_select(sc
, st
->st_slave
)) != 0) {
333 /* clear the fifos */
334 PUTREG(sc
, AUPSC_SPIPCR
, SPIPCR_RC
| SPIPCR_TC
);
336 sc
->sc_rchunk
= sc
->sc_wchunk
= st
->st_chunks
;
338 /* now kick the master start to get the chip running */
339 PUTREG(sc
, AUPSC_SPIPCR
, SPIPCR_MS
);
340 sc
->sc_running
= true;
343 auspi_select(sc
, -1);
344 sc
->sc_running
= false;
348 auspi_done(struct auspi_softc
*sc
, int err
)
350 struct spi_transfer
*st
;
352 /* called from interrupt handler */
353 if ((st
= sc
->sc_transfer
) != NULL
) {
354 sc
->sc_transfer
= NULL
;
357 /* make sure we clear these bits out */
358 sc
->sc_wchunk
= sc
->sc_rchunk
= NULL
;
363 auspi_intr(void *arg
)
365 struct auspi_softc
*sc
= arg
;
370 if ((GETREG(sc
, AUPSC_SPISTAT
) & SPISTAT_DI
) == 0) {
374 ev
= GETREG(sc
, AUPSC_SPIEVNT
);
376 if (ev
& SPIMSK_MM
) {
377 printf("%s: multiple masters detected!\n",
378 sc
->sc_dev
.dv_xname
);
381 if (ev
& SPIMSK_RO
) {
382 printf("%s: receive overflow\n", sc
->sc_dev
.dv_xname
);
385 if (ev
& SPIMSK_TU
) {
386 printf("%s: transmit underflow\n", sc
->sc_dev
.dv_xname
);
391 PUTREG(sc
, AUPSC_SPIEVNT
,
392 ev
& (SPIMSK_MM
| SPIMSK_RO
| SPIMSK_TU
));
393 /* clear the fifos */
394 PUTREG(sc
, AUPSC_SPIPCR
, SPIPCR_RC
| SPIPCR_TC
);
399 /* do all data exchanges */
404 * if the master done bit is set, make sure we do the
407 if (ev
& SPIMSK_MD
) {
408 if ((sc
->sc_wchunk
!= NULL
) ||
409 (sc
->sc_rchunk
!= NULL
)) {
410 printf("%s: partial transfer?\n",
411 sc
->sc_dev
.dv_xname
);
416 /* clear interrupts */
417 PUTREG(sc
, AUPSC_SPIEVNT
,
418 ev
& (SPIMSK_TR
| SPIMSK_RR
| SPIMSK_MD
));
425 auspi_transfer(void *arg
, struct spi_transfer
*st
)
427 struct auspi_softc
*sc
= arg
;
430 /* make sure we select the right chip */
432 spi_transq_enqueue(&sc
->sc_q
, st
);
433 if (sc
->sc_running
== 0) {