xref: /netbsd-src/sys/arch/mips/alchemy/dev/auspi.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1*c7fb772bSthorpej /* $NetBSD: auspi.c,v 1.11 2021/08/07 16:18:58 thorpej Exp $ */
2bcad0816Sgdamore 
3bcad0816Sgdamore /*-
4bcad0816Sgdamore  * Copyright (c) 2006 Urbana-Champaign Independent Media Center.
5bcad0816Sgdamore  * Copyright (c) 2006 Garrett D'Amore.
6bcad0816Sgdamore  * All rights reserved.
7bcad0816Sgdamore  *
8bcad0816Sgdamore  * Portions of this code were written by Garrett D'Amore for the
9bcad0816Sgdamore  * Champaign-Urbana Community Wireless Network Project.
10bcad0816Sgdamore  *
11bcad0816Sgdamore  * Redistribution and use in source and binary forms, with or
12bcad0816Sgdamore  * without modification, are permitted provided that the following
13bcad0816Sgdamore  * conditions are met:
14bcad0816Sgdamore  * 1. Redistributions of source code must retain the above copyright
15bcad0816Sgdamore  *    notice, this list of conditions and the following disclaimer.
16bcad0816Sgdamore  * 2. Redistributions in binary form must reproduce the above
17bcad0816Sgdamore  *    copyright notice, this list of conditions and the following
18bcad0816Sgdamore  *    disclaimer in the documentation and/or other materials provided
19bcad0816Sgdamore  *    with the distribution.
20bcad0816Sgdamore  * 3. All advertising materials mentioning features or use of this
21bcad0816Sgdamore  *    software must display the following acknowledgements:
22bcad0816Sgdamore  *      This product includes software developed by the Urbana-Champaign
23bcad0816Sgdamore  *      Independent Media Center.
24bcad0816Sgdamore  *	This product includes software developed by Garrett D'Amore.
25bcad0816Sgdamore  * 4. Urbana-Champaign Independent Media Center's name and Garrett
26bcad0816Sgdamore  *    D'Amore's name may not be used to endorse or promote products
27bcad0816Sgdamore  *    derived from this software without specific prior written permission.
28bcad0816Sgdamore  *
29bcad0816Sgdamore  * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
30bcad0816Sgdamore  * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR
31bcad0816Sgdamore  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32bcad0816Sgdamore  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33bcad0816Sgdamore  * ARE DISCLAIMED.  IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
34bcad0816Sgdamore  * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT,
35bcad0816Sgdamore  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36bcad0816Sgdamore  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37bcad0816Sgdamore  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38bcad0816Sgdamore  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39bcad0816Sgdamore  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40bcad0816Sgdamore  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41bcad0816Sgdamore  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42bcad0816Sgdamore  */
43bcad0816Sgdamore 
44bcad0816Sgdamore #include <sys/cdefs.h>
45*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: auspi.c,v 1.11 2021/08/07 16:18:58 thorpej Exp $");
46bcad0816Sgdamore 
47bcad0816Sgdamore #include "locators.h"
48bcad0816Sgdamore 
49bcad0816Sgdamore #include <sys/param.h>
50b6185cbdSmatt #include <sys/bus.h>
51b6185cbdSmatt #include <sys/cpu.h>
52bcad0816Sgdamore #include <sys/device.h>
53bcad0816Sgdamore #include <sys/errno.h>
54b6185cbdSmatt #include <sys/kernel.h>
55bcad0816Sgdamore #include <sys/proc.h>
56b6185cbdSmatt #include <sys/systm.h>
57bcad0816Sgdamore 
58bcad0816Sgdamore #include <mips/alchemy/include/aubusvar.h>
59bcad0816Sgdamore #include <mips/alchemy/include/auvar.h>
60bcad0816Sgdamore 
61bcad0816Sgdamore #include <mips/alchemy/dev/aupscreg.h>
62bcad0816Sgdamore #include <mips/alchemy/dev/aupscvar.h>
63bcad0816Sgdamore #include <mips/alchemy/dev/auspireg.h>
64bcad0816Sgdamore #include <mips/alchemy/dev/auspivar.h>
65bcad0816Sgdamore 
66bcad0816Sgdamore #include <dev/spi/spivar.h>
67bcad0816Sgdamore 
68bcad0816Sgdamore struct auspi_softc {
69f58fcf6aSkiyohara 	device_t		sc_dev;
70bcad0816Sgdamore 	struct aupsc_controller	sc_psc;		/* parent controller ops */
71bcad0816Sgdamore 	struct spi_controller	sc_spi;		/* SPI implementation ops */
72bcad0816Sgdamore 	struct auspi_machdep	sc_md;		/* board-specific support */
73bcad0816Sgdamore 	struct auspi_job	*sc_job;	/* current job */
74bcad0816Sgdamore 	struct spi_chunk	*sc_wchunk;
75bcad0816Sgdamore 	struct spi_chunk	*sc_rchunk;
76bcad0816Sgdamore 	void			*sc_ih;		/* interrupt handler */
77bcad0816Sgdamore 
78bcad0816Sgdamore 	struct spi_transfer	*sc_transfer;
79712239e3Sthorpej 	bool			sc_running;	/* is it processing stuff? */
80bcad0816Sgdamore 
81bcad0816Sgdamore 	SIMPLEQ_HEAD(,spi_transfer)	sc_q;
82bcad0816Sgdamore };
83bcad0816Sgdamore 
84bcad0816Sgdamore #define	auspi_select(sc, slave)	\
85bcad0816Sgdamore 	(sc)->sc_md.am_select((sc)->sc_md.am_cookie, (slave))
86bcad0816Sgdamore 
87bcad0816Sgdamore #define	STATIC
88bcad0816Sgdamore 
89f58fcf6aSkiyohara STATIC int auspi_match(device_t, struct cfdata *, void *);
90f58fcf6aSkiyohara STATIC void auspi_attach(device_t, device_t, void *);
91bcad0816Sgdamore STATIC int auspi_intr(void *);
92bcad0816Sgdamore 
93f58fcf6aSkiyohara CFATTACH_DECL_NEW(auspi, sizeof(struct auspi_softc),
94bcad0816Sgdamore     auspi_match, auspi_attach, NULL, NULL);
95bcad0816Sgdamore 
96bcad0816Sgdamore /* SPI service routines */
97bcad0816Sgdamore STATIC int auspi_configure(void *, int, int, int);
98bcad0816Sgdamore STATIC int auspi_transfer(void *, struct spi_transfer *);
99bcad0816Sgdamore 
100bcad0816Sgdamore /* internal stuff */
101bcad0816Sgdamore STATIC void auspi_done(struct auspi_softc *, int);
102bcad0816Sgdamore STATIC void auspi_send(struct auspi_softc *);
103bcad0816Sgdamore STATIC void auspi_recv(struct auspi_softc *);
104bcad0816Sgdamore STATIC void auspi_sched(struct auspi_softc *);
105bcad0816Sgdamore 
106bcad0816Sgdamore #define	GETREG(sc, x)	\
107bcad0816Sgdamore 	bus_space_read_4(sc->sc_psc.psc_bust, sc->sc_psc.psc_bush, x)
108bcad0816Sgdamore #define	PUTREG(sc, x, v)	\
109bcad0816Sgdamore 	bus_space_write_4(sc->sc_psc.psc_bust, sc->sc_psc.psc_bush, x, v)
110bcad0816Sgdamore 
111bcad0816Sgdamore int
auspi_match(device_t parent,struct cfdata * cf,void * aux)112f58fcf6aSkiyohara auspi_match(device_t parent, struct cfdata *cf, void *aux)
113bcad0816Sgdamore {
114bcad0816Sgdamore 	struct aupsc_attach_args *aa = aux;
115bcad0816Sgdamore 
116bcad0816Sgdamore 	if (strcmp(aa->aupsc_name, cf->cf_name) != 0)
117bcad0816Sgdamore 		return 0;
118bcad0816Sgdamore 
119bcad0816Sgdamore 	return 1;
120bcad0816Sgdamore }
121bcad0816Sgdamore 
122bcad0816Sgdamore void
auspi_attach(device_t parent,device_t self,void * aux)123f58fcf6aSkiyohara auspi_attach(device_t parent, device_t self, void *aux)
124bcad0816Sgdamore {
125bcad0816Sgdamore 	struct auspi_softc *sc = device_private(self);
126bcad0816Sgdamore 	struct aupsc_attach_args *aa = aux;
127bcad0816Sgdamore 	struct spibus_attach_args sba;
128bcad0816Sgdamore 	const struct auspi_machdep *md;
129bcad0816Sgdamore 
130f58fcf6aSkiyohara 	sc->sc_dev = self;
131f58fcf6aSkiyohara 
132bcad0816Sgdamore 	if ((md = auspi_machdep(aa->aupsc_addr)) != NULL) {
133bcad0816Sgdamore 		sc->sc_md = *md;
134bcad0816Sgdamore 	}
135bcad0816Sgdamore 
136bcad0816Sgdamore 	aprint_normal(": Alchemy PSC SPI protocol\n");
137bcad0816Sgdamore 
138bcad0816Sgdamore 	sc->sc_psc = aa->aupsc_ctrl;
139bcad0816Sgdamore 
140bcad0816Sgdamore 	/*
141bcad0816Sgdamore 	 * Initialize SPI controller
142bcad0816Sgdamore 	 */
143bcad0816Sgdamore 	sc->sc_spi.sct_cookie = sc;
144bcad0816Sgdamore 	sc->sc_spi.sct_configure = auspi_configure;
145bcad0816Sgdamore 	sc->sc_spi.sct_transfer = auspi_transfer;
146bcad0816Sgdamore 
147bcad0816Sgdamore 	/* fix this! */
148bcad0816Sgdamore 	sc->sc_spi.sct_nslaves = sc->sc_md.am_nslaves;
149bcad0816Sgdamore 
1509555f417Stnn 	memset(&sba, 0, sizeof(sba));
151bcad0816Sgdamore 	sba.sba_controller = &sc->sc_spi;
152bcad0816Sgdamore 
153bcad0816Sgdamore 	/* enable SPI mode */
154bcad0816Sgdamore 	sc->sc_psc.psc_enable(sc, AUPSC_SEL_SPI);
155bcad0816Sgdamore 
156bcad0816Sgdamore 	/* initialize the queue */
157bcad0816Sgdamore 	SIMPLEQ_INIT(&sc->sc_q);
158bcad0816Sgdamore 
159bcad0816Sgdamore 	/* make sure interrupts disabled at the SPI */
160bcad0816Sgdamore 	PUTREG(sc, AUPSC_SPIMSK, SPIMSK_ALL);
161bcad0816Sgdamore 
162bcad0816Sgdamore 	/* enable device interrupts */
163b1d9d10dSrmind 	sc->sc_ih = au_intr_establish(aa->aupsc_irq, 0, IPL_BIO, IST_LEVEL,
164bcad0816Sgdamore 	    auspi_intr, sc);
165bcad0816Sgdamore 
166*c7fb772bSthorpej 	config_found(self, &sba, spibus_print, CFARGS_NONE);
167bcad0816Sgdamore }
168bcad0816Sgdamore 
169bcad0816Sgdamore int
auspi_configure(void * arg,int slave,int mode,int speed)170bcad0816Sgdamore auspi_configure(void *arg, int slave, int mode, int speed)
171bcad0816Sgdamore {
172bcad0816Sgdamore 	struct auspi_softc *sc = arg;
173bcad0816Sgdamore 	int		brg, i;
174bcad0816Sgdamore 	uint32_t	reg;
175bcad0816Sgdamore 
176bcad0816Sgdamore 	/* setup interrupt registers */
177bcad0816Sgdamore 	PUTREG(sc, AUPSC_SPIMSK, SPIMSK_NORM);
178bcad0816Sgdamore 
179bcad0816Sgdamore 	reg = GETREG(sc, AUPSC_SPICFG);
180bcad0816Sgdamore 
181bcad0816Sgdamore 	reg &= ~(SPICFG_BRG_MASK);	/* clear BRG */
182bcad0816Sgdamore 	reg &= ~(SPICFG_DIV_MASK);	/* use pscn_mainclock/2 */
183bcad0816Sgdamore 	reg &= ~(SPICFG_PSE);		/* disable port swap */
184bcad0816Sgdamore 	reg &= ~(SPICFG_BI);		/* clear bit clock invert */
185bcad0816Sgdamore 	reg &= ~(SPICFG_CDE);		/* clear clock phase delay */
186bcad0816Sgdamore 	reg &= ~(SPICFG_CGE);		/* clear clock gate enable */
187bcad0816Sgdamore 	//reg |= SPICFG_MO;		/* master-only mode */
188bcad0816Sgdamore 	reg |= SPICFG_DE;		/* device enable */
189bcad0816Sgdamore 	reg |= SPICFG_DD;		/* disable DMA */
190bcad0816Sgdamore 	reg |= SPICFG_RT_1;		/* 1 byte rx fifo threshold */
191bcad0816Sgdamore 	reg |= SPICFG_TT_1;		/* 1 byte tx fifo threshold */
192bcad0816Sgdamore 	reg |= ((8-1) << SPICFG_LEN_SHIFT);/* always work in 8-bit chunks */
193bcad0816Sgdamore 
194bcad0816Sgdamore 	/*
195bcad0816Sgdamore 	 * We assume a base clock of 48MHz has been established by the
196bcad0816Sgdamore 	 * platform code.  The clock divider reduces this to 24MHz.
197bcad0816Sgdamore 	 * Next we have to figure out the BRG
198bcad0816Sgdamore 	 */
199bcad0816Sgdamore #define	BASECLK	24000000
200bcad0816Sgdamore 	for (brg = 0; brg < 64; brg++) {
201bcad0816Sgdamore 		if (speed >= (BASECLK / ((brg + 1) * 2))) {
202bcad0816Sgdamore 			break;
203bcad0816Sgdamore 		}
204bcad0816Sgdamore 	}
205bcad0816Sgdamore 
206bcad0816Sgdamore 	/*
207bcad0816Sgdamore 	 * Does the device want to go even slower?  Our minimum speed without
208bcad0816Sgdamore 	 * changing other assumptions, and complicating the code even further,
209bcad0816Sgdamore 	 * is 24MHz/128, or 187.5kHz.  That should be slow enough for any
210bcad0816Sgdamore 	 * device we're likely to encounter.
211bcad0816Sgdamore 	 */
212bcad0816Sgdamore 	if (speed < (BASECLK / ((brg + 1) * 2))) {
213bcad0816Sgdamore 		return EINVAL;
214bcad0816Sgdamore 	}
215bcad0816Sgdamore 	reg &= ~SPICFG_BRG_MASK;
216bcad0816Sgdamore 	reg |= (brg << SPICFG_BRG_SHIFT);
217bcad0816Sgdamore 
218bcad0816Sgdamore 	/*
219bcad0816Sgdamore 	 * I'm not entirely confident that these values are correct.
220bcad0816Sgdamore 	 * But at least mode 0 appears to work properly with the
221bcad0816Sgdamore 	 * devices I have tested.  The documentation seems to suggest
222bcad0816Sgdamore 	 * that I have the meaning of the clock delay bit inverted.
223bcad0816Sgdamore 	 */
224bcad0816Sgdamore 	switch (mode) {
225bcad0816Sgdamore 	case SPI_MODE_0:
226bcad0816Sgdamore 		reg |= 0;			/* CPHA = 0, CPOL = 0 */
227bcad0816Sgdamore 		break;
228bcad0816Sgdamore 	case SPI_MODE_1:
229bcad0816Sgdamore 		reg |= SPICFG_CDE;		/* CPHA = 1, CPOL = 0 */
230bcad0816Sgdamore 		break;
231bcad0816Sgdamore 	case SPI_MODE_2:
232bcad0816Sgdamore 		reg |= SPICFG_BI;		/* CPHA = 0, CPOL = 1 */
233bcad0816Sgdamore 		break;
234bcad0816Sgdamore 	case SPI_MODE_3:
235bcad0816Sgdamore 		reg |= SPICFG_CDE | SPICFG_BI;	/* CPHA = 1, CPOL = 1 */
236bcad0816Sgdamore 		break;
237bcad0816Sgdamore 	default:
238bcad0816Sgdamore 		return EINVAL;
239bcad0816Sgdamore 	}
240bcad0816Sgdamore 
241bcad0816Sgdamore 	PUTREG(sc, AUPSC_SPICFG, reg);
242bcad0816Sgdamore 
243bcad0816Sgdamore 	for (i = 1000000; i; i -= 10) {
244bcad0816Sgdamore 		if (GETREG(sc, AUPSC_SPISTAT) & SPISTAT_DR) {
245bcad0816Sgdamore 			return 0;
246bcad0816Sgdamore 		}
247bcad0816Sgdamore 	}
248bcad0816Sgdamore 
249bcad0816Sgdamore 	return ETIMEDOUT;
250bcad0816Sgdamore }
251bcad0816Sgdamore 
252bcad0816Sgdamore void
auspi_send(struct auspi_softc * sc)253bcad0816Sgdamore auspi_send(struct auspi_softc *sc)
254bcad0816Sgdamore {
255bcad0816Sgdamore 	uint32_t		data;
256bcad0816Sgdamore 	struct spi_chunk	*chunk;
257bcad0816Sgdamore 
258bcad0816Sgdamore 	/* fill the fifo */
259bcad0816Sgdamore 	while ((chunk = sc->sc_wchunk) != NULL) {
260bcad0816Sgdamore 
261bcad0816Sgdamore 		while (chunk->chunk_wresid) {
262bcad0816Sgdamore 
263bcad0816Sgdamore 			/* transmit fifo full? */
264bcad0816Sgdamore 			if (GETREG(sc, AUPSC_SPISTAT) & SPISTAT_TF) {
265bcad0816Sgdamore 				return;
266bcad0816Sgdamore 			}
267bcad0816Sgdamore 
268bcad0816Sgdamore 			if (chunk->chunk_wptr) {
269bcad0816Sgdamore 				data = *chunk->chunk_wptr++;
270bcad0816Sgdamore 			} else {
271bcad0816Sgdamore 				data = 0;
272bcad0816Sgdamore 			}
273bcad0816Sgdamore 			chunk->chunk_wresid--;
274bcad0816Sgdamore 
275bcad0816Sgdamore 			/* if the last outbound character, mark it */
276bcad0816Sgdamore 			if ((chunk->chunk_wresid == 0) &&
277bcad0816Sgdamore 			    (chunk->chunk_next == NULL)) {
278bcad0816Sgdamore 				data |=  SPITXRX_LC;
279bcad0816Sgdamore 			}
280bcad0816Sgdamore 			PUTREG(sc, AUPSC_SPITXRX, data);
281bcad0816Sgdamore 		}
282bcad0816Sgdamore 
283bcad0816Sgdamore 		/* advance to next transfer */
284bcad0816Sgdamore 		sc->sc_wchunk = sc->sc_wchunk->chunk_next;
285bcad0816Sgdamore 	}
286bcad0816Sgdamore }
287bcad0816Sgdamore 
288bcad0816Sgdamore void
auspi_recv(struct auspi_softc * sc)289bcad0816Sgdamore auspi_recv(struct auspi_softc *sc)
290bcad0816Sgdamore {
291bcad0816Sgdamore 	uint32_t		data;
292bcad0816Sgdamore 	struct spi_chunk	*chunk;
293bcad0816Sgdamore 
294bcad0816Sgdamore 	while ((chunk = sc->sc_rchunk) != NULL) {
295bcad0816Sgdamore 		while (chunk->chunk_rresid) {
296bcad0816Sgdamore 
297bcad0816Sgdamore 			/* rx fifo empty? */
298bcad0816Sgdamore 			if ((GETREG(sc, AUPSC_SPISTAT) & SPISTAT_RE) != 0) {
299bcad0816Sgdamore 				return;
300bcad0816Sgdamore 			}
301bcad0816Sgdamore 
302bcad0816Sgdamore 			/* collect rx data */
303bcad0816Sgdamore 			data = GETREG(sc, AUPSC_SPITXRX);
304bcad0816Sgdamore 			if (chunk->chunk_rptr) {
305bcad0816Sgdamore 				*chunk->chunk_rptr++ = data & 0xff;
306bcad0816Sgdamore 			}
307bcad0816Sgdamore 
308bcad0816Sgdamore 			chunk->chunk_rresid--;
309bcad0816Sgdamore 		}
310bcad0816Sgdamore 
311bcad0816Sgdamore 		/* advance next to next transfer */
312bcad0816Sgdamore 		sc->sc_rchunk = sc->sc_rchunk->chunk_next;
313bcad0816Sgdamore 	}
314bcad0816Sgdamore }
315bcad0816Sgdamore 
316bcad0816Sgdamore void
auspi_sched(struct auspi_softc * sc)317bcad0816Sgdamore auspi_sched(struct auspi_softc *sc)
318bcad0816Sgdamore {
319bcad0816Sgdamore 	struct spi_transfer	*st;
320bcad0816Sgdamore 	int			err;
321bcad0816Sgdamore 
322bcad0816Sgdamore 	while ((st = spi_transq_first(&sc->sc_q)) != NULL) {
323bcad0816Sgdamore 
324bcad0816Sgdamore 		/* remove the item */
325bcad0816Sgdamore 		spi_transq_dequeue(&sc->sc_q);
326bcad0816Sgdamore 
327bcad0816Sgdamore 		/* note that we are working on it */
328bcad0816Sgdamore 		sc->sc_transfer = st;
329bcad0816Sgdamore 
330bcad0816Sgdamore 		if ((err = auspi_select(sc, st->st_slave)) != 0) {
331bcad0816Sgdamore 			spi_done(st, err);
332bcad0816Sgdamore 			continue;
333bcad0816Sgdamore 		}
334bcad0816Sgdamore 
335bcad0816Sgdamore 		/* clear the fifos */
336bcad0816Sgdamore 		PUTREG(sc, AUPSC_SPIPCR, SPIPCR_RC | SPIPCR_TC);
337bcad0816Sgdamore 		/* setup chunks */
338bcad0816Sgdamore 		sc->sc_rchunk = sc->sc_wchunk = st->st_chunks;
339bcad0816Sgdamore 		auspi_send(sc);
340bcad0816Sgdamore 		/* now kick the master start to get the chip running */
341bcad0816Sgdamore 		PUTREG(sc, AUPSC_SPIPCR, SPIPCR_MS);
34209c5f9ccSthorpej 		sc->sc_running = true;
343bcad0816Sgdamore 		return;
344bcad0816Sgdamore 	}
345bcad0816Sgdamore 	auspi_select(sc, -1);
34609c5f9ccSthorpej 	sc->sc_running = false;
347bcad0816Sgdamore }
348bcad0816Sgdamore 
349bcad0816Sgdamore void
auspi_done(struct auspi_softc * sc,int err)350bcad0816Sgdamore auspi_done(struct auspi_softc *sc, int err)
351bcad0816Sgdamore {
352bcad0816Sgdamore 	struct spi_transfer	*st;
353bcad0816Sgdamore 
354bcad0816Sgdamore 	/* called from interrupt handler */
355bcad0816Sgdamore 	if ((st = sc->sc_transfer) != NULL) {
356bcad0816Sgdamore 		sc->sc_transfer = NULL;
357bcad0816Sgdamore 		spi_done(st, err);
358bcad0816Sgdamore 	}
359bcad0816Sgdamore 	/* make sure we clear these bits out */
360bcad0816Sgdamore 	sc->sc_wchunk = sc->sc_rchunk = NULL;
361bcad0816Sgdamore 	auspi_sched(sc);
362bcad0816Sgdamore }
363bcad0816Sgdamore 
364bcad0816Sgdamore int
auspi_intr(void * arg)365bcad0816Sgdamore auspi_intr(void *arg)
366bcad0816Sgdamore {
367bcad0816Sgdamore 	struct auspi_softc	*sc = arg;
368bcad0816Sgdamore 	uint32_t		ev;
369bcad0816Sgdamore 	int			err = 0;
370bcad0816Sgdamore 
371bcad0816Sgdamore 
372bcad0816Sgdamore 	if ((GETREG(sc, AUPSC_SPISTAT) & SPISTAT_DI) == 0) {
373bcad0816Sgdamore 		return 0;
374bcad0816Sgdamore 	}
375bcad0816Sgdamore 
376bcad0816Sgdamore 	ev = GETREG(sc, AUPSC_SPIEVNT);
377bcad0816Sgdamore 
378bcad0816Sgdamore 	if (ev & SPIMSK_MM) {
379bcad0816Sgdamore 		printf("%s: multiple masters detected!\n",
380133bfd25Skiyohara 		    device_xname(sc->sc_dev));
381bcad0816Sgdamore 		err = EIO;
382bcad0816Sgdamore 	}
383bcad0816Sgdamore 	if (ev & SPIMSK_RO) {
384133bfd25Skiyohara 		printf("%s: receive overflow\n", device_xname(sc->sc_dev));
385bcad0816Sgdamore 		err = EIO;
386bcad0816Sgdamore 	}
387bcad0816Sgdamore 	if (ev & SPIMSK_TU) {
388133bfd25Skiyohara 		printf("%s: transmit underflow\n", device_xname(sc->sc_dev));
389bcad0816Sgdamore 		err = EIO;
390bcad0816Sgdamore 	}
391bcad0816Sgdamore 	if (err) {
392bcad0816Sgdamore 		/* clear errors */
393bcad0816Sgdamore 		PUTREG(sc, AUPSC_SPIEVNT,
394bcad0816Sgdamore 		    ev & (SPIMSK_MM | SPIMSK_RO | SPIMSK_TU));
395bcad0816Sgdamore 		/* clear the fifos */
396bcad0816Sgdamore 		PUTREG(sc, AUPSC_SPIPCR, SPIPCR_RC | SPIPCR_TC);
397bcad0816Sgdamore 		auspi_done(sc, err);
398bcad0816Sgdamore 
399bcad0816Sgdamore 	} else {
400bcad0816Sgdamore 
401bcad0816Sgdamore 		/* do all data exchanges */
402bcad0816Sgdamore 		auspi_send(sc);
403bcad0816Sgdamore 		auspi_recv(sc);
404bcad0816Sgdamore 
405bcad0816Sgdamore 		/*
406bcad0816Sgdamore 		 * if the master done bit is set, make sure we do the
407bcad0816Sgdamore 		 * right processing.
408bcad0816Sgdamore 		 */
409bcad0816Sgdamore 		if (ev & SPIMSK_MD) {
410bcad0816Sgdamore 			if ((sc->sc_wchunk != NULL) ||
411bcad0816Sgdamore 			    (sc->sc_rchunk != NULL)) {
412bcad0816Sgdamore 				printf("%s: partial transfer?\n",
413133bfd25Skiyohara 				    device_xname(sc->sc_dev));
414bcad0816Sgdamore 				err = EIO;
415bcad0816Sgdamore 			}
416bcad0816Sgdamore 			auspi_done(sc, err);
417bcad0816Sgdamore 		}
418bcad0816Sgdamore 		/* clear interrupts */
419bcad0816Sgdamore 		PUTREG(sc, AUPSC_SPIEVNT,
420bcad0816Sgdamore 		    ev & (SPIMSK_TR | SPIMSK_RR | SPIMSK_MD));
421bcad0816Sgdamore 	}
422bcad0816Sgdamore 
423bcad0816Sgdamore 	return 1;
424bcad0816Sgdamore }
425bcad0816Sgdamore 
426bcad0816Sgdamore int
auspi_transfer(void * arg,struct spi_transfer * st)427bcad0816Sgdamore auspi_transfer(void *arg, struct spi_transfer *st)
428bcad0816Sgdamore {
429bcad0816Sgdamore 	struct auspi_softc	*sc = arg;
430bcad0816Sgdamore 	int			s;
431bcad0816Sgdamore 
432bcad0816Sgdamore 	/* make sure we select the right chip */
433b1d9d10dSrmind 	s = splbio();
434bcad0816Sgdamore 	spi_transq_enqueue(&sc->sc_q, st);
435bcad0816Sgdamore 	if (sc->sc_running == 0) {
436bcad0816Sgdamore 		auspi_sched(sc);
437bcad0816Sgdamore 	}
438bcad0816Sgdamore 	splx(s);
439bcad0816Sgdamore 	return 0;
440bcad0816Sgdamore }
441bcad0816Sgdamore 
442