cuda.c revision 184299
1/*-
2 * Copyright (c) 2006 Michael Lorenz
3 * Copyright 2008 by Nathan Whitehorn
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/sys/powerpc/powermac/cuda.c 184299 2008-10-26 19:37:38Z nwhitehorn $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/module.h>
37#include <sys/bus.h>
38#include <sys/conf.h>
39#include <sys/kernel.h>
40
41#include <dev/ofw/ofw_bus.h>
42#include <dev/ofw/openfirm.h>
43
44#include <machine/bus.h>
45#include <machine/intr.h>
46#include <machine/intr_machdep.h>
47#include <machine/md_var.h>
48#include <machine/pio.h>
49#include <machine/resource.h>
50
51#include <vm/vm.h>
52#include <vm/pmap.h>
53
54#include <sys/rman.h>
55
56#include <dev/adb/adb.h>
57
58#include "cudavar.h"
59#include "viareg.h"
60
61/*
62 * MacIO interface
63 */
64static int	cuda_probe(device_t);
65static int	cuda_attach(device_t);
66static int	cuda_detach(device_t);
67
68static u_int cuda_adb_send(device_t dev, u_char command_byte, int len,
69    u_char *data, u_char poll);
70static u_int cuda_adb_autopoll(device_t dev, uint16_t mask);
71static void cuda_poll(device_t dev);
72
73static device_method_t  cuda_methods[] = {
74	/* Device interface */
75	DEVMETHOD(device_probe,		cuda_probe),
76	DEVMETHOD(device_attach,	cuda_attach),
77        DEVMETHOD(device_detach,        cuda_detach),
78        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
79        DEVMETHOD(device_suspend,       bus_generic_suspend),
80        DEVMETHOD(device_resume,        bus_generic_resume),
81
82	/* bus interface, for ADB root */
83        DEVMETHOD(bus_print_child,      bus_generic_print_child),
84        DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
85
86	/* ADB bus interface */
87	DEVMETHOD(adb_hb_send_raw_packet,	cuda_adb_send),
88	DEVMETHOD(adb_hb_controller_poll,	cuda_poll),
89	DEVMETHOD(adb_hb_set_autopoll_mask,	cuda_adb_autopoll),
90
91	{ 0, 0 },
92};
93
94static driver_t cuda_driver = {
95	"cuda",
96	cuda_methods,
97	sizeof(struct cuda_softc),
98};
99
100static devclass_t cuda_devclass;
101
102DRIVER_MODULE(cuda, macio, cuda_driver, cuda_devclass, 0, 0);
103DRIVER_MODULE(adb, cuda, adb_driver, adb_devclass, 0, 0);
104
105static void cuda_intr(void *arg);
106static uint8_t cuda_read_reg(struct cuda_softc *sc, u_int offset);
107static void cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value);
108static void cuda_idle(struct cuda_softc *);
109static void cuda_tip(struct cuda_softc *);
110static void cuda_clear_tip(struct cuda_softc *);
111static void cuda_in(struct cuda_softc *);
112static void cuda_out(struct cuda_softc *);
113static void cuda_toggle_ack(struct cuda_softc *);
114static void cuda_ack_off(struct cuda_softc *);
115static int cuda_intr_state(struct cuda_softc *);
116
117static int
118cuda_probe(device_t dev)
119{
120	const char *type = ofw_bus_get_type(dev);
121
122	if (strcmp(type, "via-cuda") != 0)
123                return (ENXIO);
124
125	device_set_desc(dev, CUDA_DEVSTR);
126	return (0);
127}
128
129static int
130cuda_attach(device_t dev)
131{
132	struct cuda_softc *sc;
133
134	volatile int i;
135	uint8_t reg;
136	phandle_t node,child;
137
138	sc = device_get_softc(dev);
139	sc->sc_dev = dev;
140
141	sc->sc_memrid = 0;
142	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
143	    &sc->sc_memrid, RF_ACTIVE);
144
145	if (sc->sc_memr == NULL) {
146		device_printf(dev, "Could not alloc mem resource!\n");
147		return (ENXIO);
148	}
149
150	sc->sc_irqrid = 0;
151	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqrid,
152            	RF_ACTIVE);
153        if (sc->sc_irq == NULL) {
154                device_printf(dev, "could not allocate interrupt\n");
155                return (ENXIO);
156        }
157
158	if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE
159	    | INTR_ENTROPY, NULL, cuda_intr, dev, &sc->sc_ih) != 0) {
160                device_printf(dev, "could not setup interrupt\n");
161                bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
162                    sc->sc_irq);
163                return (ENXIO);
164        }
165
166	mtx_init(&sc->sc_mutex,"cuda",NULL,MTX_DEF | MTX_RECURSE);
167
168	sc->sc_sent = 0;
169	sc->sc_received = 0;
170	sc->sc_waiting = 0;
171	sc->sc_polling = 0;
172	sc->sc_state = CUDA_NOTREADY;
173	sc->sc_error = 0;
174	sc->sc_autopoll = 0;
175
176	/* Init CUDA */
177
178	reg = cuda_read_reg(sc, vDirB);
179	reg |= 0x30;	/* register B bits 4 and 5: outputs */
180	cuda_write_reg(sc, vDirB, reg);
181
182	reg = cuda_read_reg(sc, vDirB);
183	reg &= 0xf7;	/* register B bit 3: input */
184	cuda_write_reg(sc, vDirB, reg);
185
186	reg = cuda_read_reg(sc, vACR);
187	reg &= ~vSR_OUT;	/* make sure SR is set to IN */
188	cuda_write_reg(sc, vACR, reg);
189
190	cuda_write_reg(sc, vACR, (cuda_read_reg(sc, vACR) | 0x0c) & ~0x10);
191
192	sc->sc_state = CUDA_IDLE;	/* used by all types of hardware */
193
194	cuda_write_reg(sc, vIER, 0x84); /* make sure VIA interrupts are on */
195
196	cuda_idle(sc);	/* reset ADB */
197
198	/* Reset CUDA */
199
200	i = cuda_read_reg(sc, vSR);	/* clear interrupt */
201	cuda_write_reg(sc, vIER, 0x04); /* no interrupts while clearing */
202	cuda_idle(sc);	/* reset state to idle */
203	DELAY(150);
204	cuda_tip(sc);	/* signal start of frame */
205	DELAY(150);
206	cuda_toggle_ack(sc);
207	DELAY(150);
208	cuda_clear_tip(sc);
209	DELAY(150);
210	cuda_idle(sc);	/* back to idle state */
211	i = cuda_read_reg(sc, vSR);	/* clear interrupt */
212	cuda_write_reg(sc, vIER, 0x84);	/* ints ok now */
213
214	/* Initialize child buses (ADB) */
215	node = ofw_bus_get_node(dev);
216
217	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
218		char name[32];
219
220		memset(name, 0, sizeof(name));
221		OF_getprop(child, "name", name, sizeof(name));
222
223		if (bootverbose)
224			device_printf(dev, "CUDA child <%s>\n",name);
225
226		if (strncmp(name, "adb", 4) == 0) {
227			sc->adb_bus = device_add_child(dev,"adb",-1);
228		}
229	}
230
231	return (bus_generic_attach(dev));
232}
233
234static int cuda_detach(device_t dev) {
235	struct cuda_softc *sc;
236
237	sc = device_get_softc(dev);
238
239	bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
240	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
241	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
242	mtx_destroy(&sc->sc_mutex);
243
244	return (bus_generic_detach(dev));
245}
246
247static uint8_t
248cuda_read_reg(struct cuda_softc *sc, u_int offset) {
249	return (bus_read_1(sc->sc_memr, offset));
250}
251
252static void
253cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value) {
254	bus_write_1(sc->sc_memr, offset, value);
255}
256
257static void
258cuda_idle(struct cuda_softc *sc)
259{
260	uint8_t reg;
261
262	reg = cuda_read_reg(sc, vBufB);
263	reg |= (vPB4 | vPB5);
264	cuda_write_reg(sc, vBufB, reg);
265}
266
267static void
268cuda_tip(struct cuda_softc *sc)
269{
270	uint8_t reg;
271
272	reg = cuda_read_reg(sc, vBufB);
273	reg &= ~vPB5;
274	cuda_write_reg(sc, vBufB, reg);
275}
276
277static void
278cuda_clear_tip(struct cuda_softc *sc)
279{
280	uint8_t reg;
281
282	reg = cuda_read_reg(sc, vBufB);
283	reg |= vPB5;
284	cuda_write_reg(sc, vBufB, reg);
285}
286
287static void
288cuda_in(struct cuda_softc *sc)
289{
290	uint8_t reg;
291
292	reg = cuda_read_reg(sc, vACR);
293	reg &= ~vSR_OUT;
294	cuda_write_reg(sc, vACR, reg);
295}
296
297static void
298cuda_out(struct cuda_softc *sc)
299{
300	uint8_t reg;
301
302	reg = cuda_read_reg(sc, vACR);
303	reg |= vSR_OUT;
304	cuda_write_reg(sc, vACR, reg);
305}
306
307static void
308cuda_toggle_ack(struct cuda_softc *sc)
309{
310	uint8_t reg;
311
312	reg = cuda_read_reg(sc, vBufB);
313	reg ^= vPB4;
314	cuda_write_reg(sc, vBufB, reg);
315}
316
317static void
318cuda_ack_off(struct cuda_softc *sc)
319{
320	uint8_t reg;
321
322	reg = cuda_read_reg(sc, vBufB);
323	reg |= vPB4;
324	cuda_write_reg(sc, vBufB, reg);
325}
326
327static int
328cuda_intr_state(struct cuda_softc *sc)
329{
330	return ((cuda_read_reg(sc, vBufB) & vPB3) == 0);
331}
332
333static int
334cuda_send(void *cookie, int poll, int length, uint8_t *msg)
335{
336	struct cuda_softc *sc = cookie;
337	device_t dev = sc->sc_dev;
338
339	if (sc->sc_state == CUDA_NOTREADY)
340		return -1;
341
342	mtx_lock(&sc->sc_mutex);
343
344	if ((sc->sc_state == CUDA_IDLE) /*&&
345	    ((cuda_read_reg(sc, vBufB) & vPB3) == vPB3)*/) {
346		/* fine */
347	} else {
348		if (sc->sc_waiting == 0) {
349			sc->sc_waiting = 1;
350		} else {
351			mtx_unlock(&sc->sc_mutex);
352			return -1;
353		}
354	}
355
356	sc->sc_error = 0;
357	memcpy(sc->sc_out, msg, length);
358	sc->sc_out_length = length;
359	sc->sc_sent = 0;
360
361	if (sc->sc_waiting != 1) {
362		DELAY(150);
363		sc->sc_state = CUDA_OUT;
364		cuda_out(sc);
365		cuda_write_reg(sc, vSR, sc->sc_out[0]);
366		cuda_ack_off(sc);
367		cuda_tip(sc);
368	}
369	sc->sc_waiting = 1;
370	mtx_unlock(&sc->sc_mutex);
371
372	if (sc->sc_polling || poll || cold) {
373		cuda_poll(dev);
374	}
375
376	return 0;
377}
378
379static void
380cuda_poll(device_t dev)
381{
382	struct cuda_softc *sc = device_get_softc(dev);
383
384	while ((sc->sc_state != CUDA_IDLE) ||
385	       (cuda_intr_state(sc)) ||
386	       (sc->sc_waiting == 1)) {
387		if ((cuda_read_reg(sc, vIFR) & vSR_INT) == vSR_INT)
388			cuda_intr(dev);
389	}
390}
391
392static void
393cuda_intr(void *arg)
394{
395	device_t        dev;
396	struct cuda_softc *sc;
397
398	int i, ending, type, restart_send;
399	uint8_t reg;
400
401        dev = (device_t)arg;
402	sc = device_get_softc(dev);
403
404	mtx_lock(&sc->sc_mutex);
405
406	restart_send = 0;
407	reg = cuda_read_reg(sc, vIFR);
408	cuda_write_reg(sc, vIFR, 0x7f);	/* Clear interrupt */
409
410switch_start:
411	switch (sc->sc_state) {
412	case CUDA_IDLE:
413		/*
414		 * This is an unexpected packet, so grab the first (dummy)
415		 * byte, set up the proper vars, and tell the chip we are
416		 * starting to receive the packet by setting the TIP bit.
417		 */
418		sc->sc_in[1] = cuda_read_reg(sc, vSR);
419
420		if (cuda_intr_state(sc) == 0) {
421			/* must have been a fake start */
422
423			if (sc->sc_waiting) {
424				/* start over */
425				DELAY(150);
426				sc->sc_state = CUDA_OUT;
427				sc->sc_sent = 0;
428				cuda_out(sc);
429				cuda_write_reg(sc, vSR, sc->sc_out[1]);
430				cuda_ack_off(sc);
431				cuda_tip(sc);
432			}
433			break;
434		}
435
436		cuda_in(sc);
437		cuda_tip(sc);
438
439		sc->sc_received = 1;
440		sc->sc_state = CUDA_IN;
441		break;
442
443	case CUDA_IN:
444		sc->sc_in[sc->sc_received] = cuda_read_reg(sc, vSR);
445		ending = 0;
446
447		if (sc->sc_received > 255) {
448			/* bitch only once */
449			if (sc->sc_received == 256) {
450				device_printf(dev,"input overflow\n");
451				ending = 1;
452			}
453		} else
454			sc->sc_received++;
455
456		if (sc->sc_received > 3) {
457			if ((sc->sc_in[3] == CMD_IIC) &&
458			    (sc->sc_received > (sc->sc_i2c_read_len + 4))) {
459				ending = 1;
460			}
461		}
462
463		/* intr off means this is the last byte (end of frame) */
464		if (cuda_intr_state(sc) == 0) {
465			ending = 1;
466		} else {
467			cuda_toggle_ack(sc);
468		}
469
470		if (ending == 1) {	/* end of message? */
471			sc->sc_in[0] = sc->sc_received - 1;
472
473			/* reset vars and signal the end of this frame */
474			cuda_idle(sc);
475
476			/* check if we have a handler for this message */
477			type = sc->sc_in[1];
478
479			switch (type) {
480			   case CUDA_ADB:
481				if (sc->sc_received > 4) {
482					adb_receive_raw_packet(sc->adb_bus,
483					    sc->sc_in[2],sc->sc_in[3],
484					    sc->sc_received - 4,&sc->sc_in[4]);
485				} else {
486					adb_receive_raw_packet(sc->adb_bus,
487					    sc->sc_in[2],sc->sc_in[3],0,NULL);
488				}
489				break;
490			   case CUDA_PSEUDO:
491				if (sc->sc_in[3] == CMD_AUTOPOLL)
492					sc->sc_autopoll = 1;
493				break;
494			   case CUDA_ERROR:
495				device_printf(dev,"CUDA Error\n");
496				sc->sc_error = 1;
497				break;
498			   default:
499				device_printf(dev,"unknown CUDA command %d\n",
500				    type);
501				break;
502			}
503
504			sc->sc_state = CUDA_IDLE;
505
506			sc->sc_received = 0;
507
508			/*
509			 * If there is something waiting to be sent out,
510			 * set everything up and send the first byte.
511			 */
512			if (sc->sc_waiting == 1) {
513				DELAY(1500);	/* required */
514				sc->sc_sent = 0;
515				sc->sc_state = CUDA_OUT;
516
517				/*
518				 * If the interrupt is on, we were too slow
519				 * and the chip has already started to send
520				 * something to us, so back out of the write
521				 * and start a read cycle.
522				 */
523				if (cuda_intr_state(sc)) {
524					cuda_in(sc);
525					cuda_idle(sc);
526					sc->sc_sent = 0;
527					sc->sc_state = CUDA_IDLE;
528					sc->sc_received = 0;
529					DELAY(150);
530					goto switch_start;
531				}
532				/*
533				 * If we got here, it's ok to start sending
534				 * so load the first byte and tell the chip
535				 * we want to send.
536				 */
537				cuda_out(sc);
538				cuda_write_reg(sc, vSR,
539				    sc->sc_out[sc->sc_sent]);
540				cuda_ack_off(sc);
541				cuda_tip(sc);
542			}
543		}
544		break;
545
546	case CUDA_OUT:
547		i = cuda_read_reg(sc, vSR);	/* reset SR-intr in IFR */
548
549		sc->sc_sent++;
550		if (cuda_intr_state(sc)) {	/* ADB intr low during write */
551			cuda_in(sc);	/* make sure SR is set to IN */
552			cuda_idle(sc);
553			sc->sc_sent = 0;	/* must start all over */
554			sc->sc_state = CUDA_IDLE;	/* new state */
555			sc->sc_received = 0;
556			sc->sc_waiting = 1;	/* must retry when done with
557						 * read */
558			DELAY(150);
559			goto switch_start;	/* process next state right
560						 * now */
561			break;
562		}
563		if (sc->sc_out_length == sc->sc_sent) {	/* check for done */
564
565			sc->sc_waiting = 0;	/* done writing */
566			sc->sc_state = CUDA_IDLE;	/* signal bus is idle */
567			cuda_in(sc);
568			cuda_idle(sc);
569		} else {
570			/* send next byte */
571			cuda_write_reg(sc, vSR, sc->sc_out[sc->sc_sent]);
572			cuda_toggle_ack(sc);	/* signal byte ready to
573							 * shift */
574		}
575		break;
576
577	case CUDA_NOTREADY:
578		break;
579
580	default:
581		break;
582	}
583
584	mtx_unlock(&sc->sc_mutex);
585}
586
587static u_int
588cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll)
589{
590	struct cuda_softc *sc = device_get_softc(dev);
591	int i;
592	uint8_t packet[16];
593
594	/* construct an ADB command packet and send it */
595	packet[0] = CUDA_ADB;
596	packet[1] = command_byte;
597	for (i = 0; i < len; i++)
598		packet[i + 2] = data[i];
599
600	if (poll)
601		cuda_poll(dev);
602
603	cuda_send(sc, poll, len + 2, packet);
604
605	if (poll)
606		cuda_poll(dev);
607
608	return 0;
609}
610
611static u_int
612cuda_adb_autopoll(device_t dev, uint16_t mask) {
613	struct cuda_softc *sc = device_get_softc(dev);
614
615	uint8_t cmd[] = {CUDA_PSEUDO, CMD_AUTOPOLL, mask != 0};
616
617	mtx_lock(&sc->sc_mutex);
618	if (cmd[2] == sc->sc_autopoll) {
619		mtx_unlock(&sc->sc_mutex);
620		return 0;
621	}
622
623	while (sc->sc_state != CUDA_IDLE)
624		mtx_sleep(dev,&sc->sc_mutex,0,"cuda",1);
625
626	sc->sc_autopoll = -1;
627	mtx_unlock(&sc->sc_mutex);
628
629	cuda_send(sc, 0, 3, cmd);
630
631	mtx_lock(&sc->sc_mutex);
632	while(sc->sc_autopoll == -1) {
633		mtx_sleep(dev,&sc->sc_mutex,0,"cuda",100);
634		cuda_poll(dev);
635	}
636
637	mtx_unlock(&sc->sc_mutex);
638
639	return 0;
640}
641
642