auspi.c revision 1.12 1 /* $NetBSD: auspi.c,v 1.12 2025/09/10 01:55:07 thorpej 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: auspi.c,v 1.12 2025/09/10 01:55:07 thorpej Exp $");
46
47 #include "locators.h"
48
49 #include <sys/param.h>
50 #include <sys/bus.h>
51 #include <sys/cpu.h>
52 #include <sys/device.h>
53 #include <sys/errno.h>
54 #include <sys/kernel.h>
55 #include <sys/proc.h>
56 #include <sys/systm.h>
57
58 #include <mips/alchemy/include/aubusvar.h>
59 #include <mips/alchemy/include/auvar.h>
60
61 #include <mips/alchemy/dev/aupscreg.h>
62 #include <mips/alchemy/dev/aupscvar.h>
63 #include <mips/alchemy/dev/auspireg.h>
64 #include <mips/alchemy/dev/auspivar.h>
65
66 #include <dev/spi/spivar.h>
67
68 struct auspi_softc {
69 device_t sc_dev;
70 struct aupsc_controller sc_psc; /* parent controller ops */
71 struct spi_controller sc_spi; /* SPI implementation ops */
72 struct auspi_machdep sc_md; /* board-specific support */
73 struct auspi_job *sc_job; /* current job */
74 struct spi_chunk *sc_wchunk;
75 struct spi_chunk *sc_rchunk;
76 void *sc_ih; /* interrupt handler */
77
78 struct spi_transfer *sc_transfer;
79 bool sc_running; /* is it processing stuff? */
80
81 SIMPLEQ_HEAD(,spi_transfer) sc_q;
82 };
83
84 #define auspi_select(sc, slave) \
85 (sc)->sc_md.am_select((sc)->sc_md.am_cookie, (slave))
86
87 #define STATIC
88
89 STATIC int auspi_match(device_t, struct cfdata *, void *);
90 STATIC void auspi_attach(device_t, device_t, void *);
91 STATIC int auspi_intr(void *);
92
93 CFATTACH_DECL_NEW(auspi, sizeof(struct auspi_softc),
94 auspi_match, auspi_attach, NULL, NULL);
95
96 /* SPI service routines */
97 STATIC int auspi_configure(void *, int, int, int);
98 STATIC int auspi_transfer(void *, struct spi_transfer *);
99
100 /* internal stuff */
101 STATIC void auspi_done(struct auspi_softc *, int);
102 STATIC void auspi_send(struct auspi_softc *);
103 STATIC void auspi_recv(struct auspi_softc *);
104 STATIC void auspi_sched(struct auspi_softc *);
105
106 #define GETREG(sc, x) \
107 bus_space_read_4(sc->sc_psc.psc_bust, sc->sc_psc.psc_bush, x)
108 #define PUTREG(sc, x, v) \
109 bus_space_write_4(sc->sc_psc.psc_bust, sc->sc_psc.psc_bush, x, v)
110
111 int
112 auspi_match(device_t parent, struct cfdata *cf, void *aux)
113 {
114 struct aupsc_attach_args *aa = aux;
115
116 if (strcmp(aa->aupsc_name, cf->cf_name) != 0)
117 return 0;
118
119 return 1;
120 }
121
122 void
123 auspi_attach(device_t parent, device_t self, void *aux)
124 {
125 struct auspi_softc *sc = device_private(self);
126 struct aupsc_attach_args *aa = aux;
127 const struct auspi_machdep *md;
128
129 sc->sc_dev = self;
130
131 if ((md = auspi_machdep(aa->aupsc_addr)) != NULL) {
132 sc->sc_md = *md;
133 }
134
135 aprint_normal(": Alchemy PSC SPI protocol\n");
136
137 sc->sc_psc = aa->aupsc_ctrl;
138
139 /*
140 * Initialize SPI controller
141 */
142 sc->sc_spi.sct_cookie = sc;
143 sc->sc_spi.sct_configure = auspi_configure;
144 sc->sc_spi.sct_transfer = auspi_transfer;
145
146 /* fix this! */
147 sc->sc_spi.sct_nslaves = sc->sc_md.am_nslaves;
148
149 /* enable SPI mode */
150 sc->sc_psc.psc_enable(sc, AUPSC_SEL_SPI);
151
152 /* initialize the queue */
153 SIMPLEQ_INIT(&sc->sc_q);
154
155 /* make sure interrupts disabled at the SPI */
156 PUTREG(sc, AUPSC_SPIMSK, SPIMSK_ALL);
157
158 /* enable device interrupts */
159 sc->sc_ih = au_intr_establish(aa->aupsc_irq, 0, IPL_BIO, IST_LEVEL,
160 auspi_intr, sc);
161
162 spibus_attach(self, &sc->sc_spi);
163 }
164
165 int
166 auspi_configure(void *arg, int slave, int mode, int speed)
167 {
168 struct auspi_softc *sc = arg;
169 int brg, i;
170 uint32_t reg;
171
172 /* setup interrupt registers */
173 PUTREG(sc, AUPSC_SPIMSK, SPIMSK_NORM);
174
175 reg = GETREG(sc, AUPSC_SPICFG);
176
177 reg &= ~(SPICFG_BRG_MASK); /* clear BRG */
178 reg &= ~(SPICFG_DIV_MASK); /* use pscn_mainclock/2 */
179 reg &= ~(SPICFG_PSE); /* disable port swap */
180 reg &= ~(SPICFG_BI); /* clear bit clock invert */
181 reg &= ~(SPICFG_CDE); /* clear clock phase delay */
182 reg &= ~(SPICFG_CGE); /* clear clock gate enable */
183 //reg |= SPICFG_MO; /* master-only mode */
184 reg |= SPICFG_DE; /* device enable */
185 reg |= SPICFG_DD; /* disable DMA */
186 reg |= SPICFG_RT_1; /* 1 byte rx fifo threshold */
187 reg |= SPICFG_TT_1; /* 1 byte tx fifo threshold */
188 reg |= ((8-1) << SPICFG_LEN_SHIFT);/* always work in 8-bit chunks */
189
190 /*
191 * We assume a base clock of 48MHz has been established by the
192 * platform code. The clock divider reduces this to 24MHz.
193 * Next we have to figure out the BRG
194 */
195 #define BASECLK 24000000
196 for (brg = 0; brg < 64; brg++) {
197 if (speed >= (BASECLK / ((brg + 1) * 2))) {
198 break;
199 }
200 }
201
202 /*
203 * Does the device want to go even slower? Our minimum speed without
204 * changing other assumptions, and complicating the code even further,
205 * is 24MHz/128, or 187.5kHz. That should be slow enough for any
206 * device we're likely to encounter.
207 */
208 if (speed < (BASECLK / ((brg + 1) * 2))) {
209 return EINVAL;
210 }
211 reg &= ~SPICFG_BRG_MASK;
212 reg |= (brg << SPICFG_BRG_SHIFT);
213
214 /*
215 * I'm not entirely confident that these values are correct.
216 * But at least mode 0 appears to work properly with the
217 * devices I have tested. The documentation seems to suggest
218 * that I have the meaning of the clock delay bit inverted.
219 */
220 switch (mode) {
221 case SPI_MODE_0:
222 reg |= 0; /* CPHA = 0, CPOL = 0 */
223 break;
224 case SPI_MODE_1:
225 reg |= SPICFG_CDE; /* CPHA = 1, CPOL = 0 */
226 break;
227 case SPI_MODE_2:
228 reg |= SPICFG_BI; /* CPHA = 0, CPOL = 1 */
229 break;
230 case SPI_MODE_3:
231 reg |= SPICFG_CDE | SPICFG_BI; /* CPHA = 1, CPOL = 1 */
232 break;
233 default:
234 return EINVAL;
235 }
236
237 PUTREG(sc, AUPSC_SPICFG, reg);
238
239 for (i = 1000000; i; i -= 10) {
240 if (GETREG(sc, AUPSC_SPISTAT) & SPISTAT_DR) {
241 return 0;
242 }
243 }
244
245 return ETIMEDOUT;
246 }
247
248 void
249 auspi_send(struct auspi_softc *sc)
250 {
251 uint32_t data;
252 struct spi_chunk *chunk;
253
254 /* fill the fifo */
255 while ((chunk = sc->sc_wchunk) != NULL) {
256
257 while (chunk->chunk_wresid) {
258
259 /* transmit fifo full? */
260 if (GETREG(sc, AUPSC_SPISTAT) & SPISTAT_TF) {
261 return;
262 }
263
264 if (chunk->chunk_wptr) {
265 data = *chunk->chunk_wptr++;
266 } else {
267 data = 0;
268 }
269 chunk->chunk_wresid--;
270
271 /* if the last outbound character, mark it */
272 if ((chunk->chunk_wresid == 0) &&
273 (chunk->chunk_next == NULL)) {
274 data |= SPITXRX_LC;
275 }
276 PUTREG(sc, AUPSC_SPITXRX, data);
277 }
278
279 /* advance to next transfer */
280 sc->sc_wchunk = sc->sc_wchunk->chunk_next;
281 }
282 }
283
284 void
285 auspi_recv(struct auspi_softc *sc)
286 {
287 uint32_t data;
288 struct spi_chunk *chunk;
289
290 while ((chunk = sc->sc_rchunk) != NULL) {
291 while (chunk->chunk_rresid) {
292
293 /* rx fifo empty? */
294 if ((GETREG(sc, AUPSC_SPISTAT) & SPISTAT_RE) != 0) {
295 return;
296 }
297
298 /* collect rx data */
299 data = GETREG(sc, AUPSC_SPITXRX);
300 if (chunk->chunk_rptr) {
301 *chunk->chunk_rptr++ = data & 0xff;
302 }
303
304 chunk->chunk_rresid--;
305 }
306
307 /* advance next to next transfer */
308 sc->sc_rchunk = sc->sc_rchunk->chunk_next;
309 }
310 }
311
312 void
313 auspi_sched(struct auspi_softc *sc)
314 {
315 struct spi_transfer *st;
316 int err;
317
318 while ((st = spi_transq_first(&sc->sc_q)) != NULL) {
319
320 /* remove the item */
321 spi_transq_dequeue(&sc->sc_q);
322
323 /* note that we are working on it */
324 sc->sc_transfer = st;
325
326 if ((err = auspi_select(sc, st->st_slave)) != 0) {
327 spi_done(st, err);
328 continue;
329 }
330
331 /* clear the fifos */
332 PUTREG(sc, AUPSC_SPIPCR, SPIPCR_RC | SPIPCR_TC);
333 /* setup chunks */
334 sc->sc_rchunk = sc->sc_wchunk = st->st_chunks;
335 auspi_send(sc);
336 /* now kick the master start to get the chip running */
337 PUTREG(sc, AUPSC_SPIPCR, SPIPCR_MS);
338 sc->sc_running = true;
339 return;
340 }
341 auspi_select(sc, -1);
342 sc->sc_running = false;
343 }
344
345 void
346 auspi_done(struct auspi_softc *sc, int err)
347 {
348 struct spi_transfer *st;
349
350 /* called from interrupt handler */
351 if ((st = sc->sc_transfer) != NULL) {
352 sc->sc_transfer = NULL;
353 spi_done(st, err);
354 }
355 /* make sure we clear these bits out */
356 sc->sc_wchunk = sc->sc_rchunk = NULL;
357 auspi_sched(sc);
358 }
359
360 int
361 auspi_intr(void *arg)
362 {
363 struct auspi_softc *sc = arg;
364 uint32_t ev;
365 int err = 0;
366
367
368 if ((GETREG(sc, AUPSC_SPISTAT) & SPISTAT_DI) == 0) {
369 return 0;
370 }
371
372 ev = GETREG(sc, AUPSC_SPIEVNT);
373
374 if (ev & SPIMSK_MM) {
375 printf("%s: multiple masters detected!\n",
376 device_xname(sc->sc_dev));
377 err = EIO;
378 }
379 if (ev & SPIMSK_RO) {
380 printf("%s: receive overflow\n", device_xname(sc->sc_dev));
381 err = EIO;
382 }
383 if (ev & SPIMSK_TU) {
384 printf("%s: transmit underflow\n", device_xname(sc->sc_dev));
385 err = EIO;
386 }
387 if (err) {
388 /* clear errors */
389 PUTREG(sc, AUPSC_SPIEVNT,
390 ev & (SPIMSK_MM | SPIMSK_RO | SPIMSK_TU));
391 /* clear the fifos */
392 PUTREG(sc, AUPSC_SPIPCR, SPIPCR_RC | SPIPCR_TC);
393 auspi_done(sc, err);
394
395 } else {
396
397 /* do all data exchanges */
398 auspi_send(sc);
399 auspi_recv(sc);
400
401 /*
402 * if the master done bit is set, make sure we do the
403 * right processing.
404 */
405 if (ev & SPIMSK_MD) {
406 if ((sc->sc_wchunk != NULL) ||
407 (sc->sc_rchunk != NULL)) {
408 printf("%s: partial transfer?\n",
409 device_xname(sc->sc_dev));
410 err = EIO;
411 }
412 auspi_done(sc, err);
413 }
414 /* clear interrupts */
415 PUTREG(sc, AUPSC_SPIEVNT,
416 ev & (SPIMSK_TR | SPIMSK_RR | SPIMSK_MD));
417 }
418
419 return 1;
420 }
421
422 int
423 auspi_transfer(void *arg, struct spi_transfer *st)
424 {
425 struct auspi_softc *sc = arg;
426 int s;
427
428 /* make sure we select the right chip */
429 s = splbio();
430 spi_transq_enqueue(&sc->sc_q, st);
431 if (sc->sc_running == 0) {
432 auspi_sched(sc);
433 }
434 splx(s);
435 return 0;
436 }
437
438