1184299Snwhitehorn/*-
2184299Snwhitehorn * Copyright (C) 2008 Nathan Whitehorn
3184299Snwhitehorn * All rights reserved.
4184299Snwhitehorn *
5184299Snwhitehorn * Redistribution and use in source and binary forms, with or without
6184299Snwhitehorn * modification, are permitted provided that the following conditions
7184299Snwhitehorn * are met:
8184299Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9184299Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10184299Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11184299Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12184299Snwhitehorn *    documentation and/or other materials provided with the distribution.
13184299Snwhitehorn *
14184299Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15184299Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16184299Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17184299Snwhitehorn * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18184299Snwhitehorn * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19184299Snwhitehorn * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20184299Snwhitehorn * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21184299Snwhitehorn * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22184299Snwhitehorn * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23184299Snwhitehorn * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24184299Snwhitehorn *
25184299Snwhitehorn * $FreeBSD$
26184299Snwhitehorn */
27184299Snwhitehorn
28184299Snwhitehorn#include <sys/cdefs.h>
29184299Snwhitehorn#include <sys/param.h>
30184299Snwhitehorn#include <sys/systm.h>
31184299Snwhitehorn#include <sys/module.h>
32184299Snwhitehorn#include <sys/bus.h>
33184299Snwhitehorn#include <sys/conf.h>
34184299Snwhitehorn#include <sys/kernel.h>
35184299Snwhitehorn
36184299Snwhitehorn#include <machine/bus.h>
37184299Snwhitehorn
38184299Snwhitehorn#include <vm/vm.h>
39184299Snwhitehorn#include <vm/pmap.h>
40184299Snwhitehorn
41184299Snwhitehorn#include "adb.h"
42184299Snwhitehorn#include "adbvar.h"
43184299Snwhitehorn
44184299Snwhitehornstatic int adb_bus_probe(device_t dev);
45184299Snwhitehornstatic int adb_bus_attach(device_t dev);
46184299Snwhitehornstatic int adb_bus_detach(device_t dev);
47184473Snwhitehornstatic void adb_bus_enumerate(void *xdev);
48184299Snwhitehornstatic void adb_probe_nomatch(device_t dev, device_t child);
49184299Snwhitehornstatic int adb_print_child(device_t dev, device_t child);
50184299Snwhitehorn
51185724Snwhitehornstatic int adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, uint8_t reg, int len, u_char *data, u_char *reply);
52184299Snwhitehorn
53184299Snwhitehornstatic char *adb_device_string[] = {
54184299Snwhitehorn	"HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc"
55184299Snwhitehorn};
56184299Snwhitehorn
57184299Snwhitehornstatic device_method_t adb_bus_methods[] = {
58184299Snwhitehorn	/* Device interface */
59184299Snwhitehorn	DEVMETHOD(device_probe,		adb_bus_probe),
60184299Snwhitehorn	DEVMETHOD(device_attach,	adb_bus_attach),
61184299Snwhitehorn	DEVMETHOD(device_detach,        adb_bus_detach),
62184299Snwhitehorn        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
63184299Snwhitehorn        DEVMETHOD(device_suspend,       bus_generic_suspend),
64184299Snwhitehorn        DEVMETHOD(device_resume,        bus_generic_resume),
65184299Snwhitehorn
66184299Snwhitehorn	/* Bus Interface */
67184299Snwhitehorn        DEVMETHOD(bus_probe_nomatch,    adb_probe_nomatch),
68184299Snwhitehorn        DEVMETHOD(bus_print_child,	adb_print_child),
69184299Snwhitehorn
70184299Snwhitehorn	{ 0, 0 },
71184299Snwhitehorn};
72184299Snwhitehorn
73184299Snwhitehorndriver_t adb_driver = {
74184299Snwhitehorn	"adb",
75184299Snwhitehorn	adb_bus_methods,
76184299Snwhitehorn	sizeof(struct adb_softc),
77184299Snwhitehorn};
78184299Snwhitehorn
79184299Snwhitehorndevclass_t adb_devclass;
80184299Snwhitehorn
81184299Snwhitehornstatic int
82184299Snwhitehornadb_bus_probe(device_t dev)
83184299Snwhitehorn{
84184299Snwhitehorn	device_set_desc(dev, "Apple Desktop Bus");
85184299Snwhitehorn	return (0);
86184299Snwhitehorn}
87184299Snwhitehorn
88184299Snwhitehornstatic int
89184299Snwhitehornadb_bus_attach(device_t dev)
90184299Snwhitehorn{
91184299Snwhitehorn	struct adb_softc *sc = device_get_softc(dev);
92184473Snwhitehorn	sc->enum_hook.ich_func = adb_bus_enumerate;
93184473Snwhitehorn	sc->enum_hook.ich_arg = dev;
94184473Snwhitehorn
95184473Snwhitehorn	/*
96184473Snwhitehorn	 * We should wait until interrupts are enabled to try to probe
97184473Snwhitehorn	 * the bus. Enumerating the ADB involves receiving packets,
98184473Snwhitehorn	 * which works best with interrupts enabled.
99184473Snwhitehorn	 */
100184473Snwhitehorn
101184473Snwhitehorn	if (config_intrhook_establish(&sc->enum_hook) != 0)
102184473Snwhitehorn		return (ENOMEM);
103184473Snwhitehorn
104184473Snwhitehorn	return (0);
105184473Snwhitehorn}
106184473Snwhitehorn
107184473Snwhitehornstatic void
108184473Snwhitehornadb_bus_enumerate(void *xdev)
109184473Snwhitehorn{
110184473Snwhitehorn	device_t dev = (device_t)xdev;
111184473Snwhitehorn
112184473Snwhitehorn	struct adb_softc *sc = device_get_softc(dev);
113184299Snwhitehorn	uint8_t i, next_free;
114184299Snwhitehorn	uint16_t r3;
115184299Snwhitehorn
116184299Snwhitehorn	sc->sc_dev = dev;
117184299Snwhitehorn	sc->parent = device_get_parent(dev);
118184299Snwhitehorn
119184299Snwhitehorn	sc->packet_reply = 0;
120184299Snwhitehorn	sc->autopoll_mask = 0;
121185724Snwhitehorn	sc->sync_packet = 0xffff;
122184299Snwhitehorn
123184299Snwhitehorn	/* Initialize devinfo */
124184299Snwhitehorn	for (i = 0; i < 16; i++) {
125184299Snwhitehorn		sc->devinfo[i].address = i;
126184299Snwhitehorn		sc->devinfo[i].default_address = 0;
127184299Snwhitehorn	}
128184299Snwhitehorn
129184299Snwhitehorn	/* Reset ADB bus */
130185724Snwhitehorn	adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL,NULL);
131184299Snwhitehorn	DELAY(1500);
132184299Snwhitehorn
133184299Snwhitehorn	/* Enumerate bus */
134184299Snwhitehorn	next_free = 8;
135184299Snwhitehorn
136184890Snwhitehorn	for (i = 1; i <= 7; i++) {
137184299Snwhitehorn	    int8_t first_relocated = -1;
138184299Snwhitehorn	    int reply = 0;
139184299Snwhitehorn
140184299Snwhitehorn	    do {
141184299Snwhitehorn		reply = adb_send_raw_packet_sync(dev,i,
142185724Snwhitehorn			    ADB_COMMAND_TALK,3,0,NULL,NULL);
143184299Snwhitehorn
144184299Snwhitehorn		if (reply) {
145184299Snwhitehorn			/* If we got a response, relocate to next_free */
146184299Snwhitehorn			r3 = sc->devinfo[i].register3;
147184299Snwhitehorn			r3 &= 0xf000;
148184299Snwhitehorn			r3 |= ((uint16_t)(next_free) & 0x000f) << 8;
149184299Snwhitehorn			r3 |= 0x00fe;
150184299Snwhitehorn
151184299Snwhitehorn			adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3,
152185724Snwhitehorn			    sizeof(uint16_t),(u_char *)(&r3),NULL);
153184299Snwhitehorn
154184299Snwhitehorn			adb_send_raw_packet_sync(dev,next_free,
155185724Snwhitehorn			    ADB_COMMAND_TALK,3,0,NULL,NULL);
156184299Snwhitehorn
157184299Snwhitehorn			sc->devinfo[next_free].default_address = i;
158184299Snwhitehorn			if (first_relocated < 0)
159184299Snwhitehorn				first_relocated = next_free;
160184299Snwhitehorn
161184299Snwhitehorn			next_free++;
162184299Snwhitehorn		} else if (first_relocated > 0) {
163184299Snwhitehorn			/* Collisions removed, relocate first device back */
164184299Snwhitehorn
165184299Snwhitehorn			r3 = sc->devinfo[i].register3;
166184299Snwhitehorn			r3 &= 0xf000;
167184299Snwhitehorn			r3 |= ((uint16_t)(i) & 0x000f) << 8;
168184299Snwhitehorn
169184299Snwhitehorn			adb_send_raw_packet_sync(dev,first_relocated,
170184299Snwhitehorn			    ADB_COMMAND_LISTEN,3,
171185724Snwhitehorn			    sizeof(uint16_t),(u_char *)(&r3),NULL);
172184299Snwhitehorn			adb_send_raw_packet_sync(dev,i,
173185724Snwhitehorn			    ADB_COMMAND_TALK,3,0,NULL,NULL);
174184299Snwhitehorn
175184299Snwhitehorn			sc->devinfo[i].default_address = i;
176184299Snwhitehorn			sc->devinfo[(int)(first_relocated)].default_address = 0;
177184299Snwhitehorn			break;
178184299Snwhitehorn		}
179184299Snwhitehorn	    } while (reply);
180184299Snwhitehorn	}
181184299Snwhitehorn
182184299Snwhitehorn	for (i = 0; i < 16; i++) {
183184299Snwhitehorn		if (sc->devinfo[i].default_address) {
184184299Snwhitehorn			sc->children[i] = device_add_child(dev, NULL, -1);
185184299Snwhitehorn			device_set_ivars(sc->children[i], &sc->devinfo[i]);
186184299Snwhitehorn		}
187184299Snwhitehorn	}
188184299Snwhitehorn
189184473Snwhitehorn	bus_generic_attach(dev);
190184473Snwhitehorn
191184473Snwhitehorn	config_intrhook_disestablish(&sc->enum_hook);
192184299Snwhitehorn}
193184299Snwhitehorn
194184299Snwhitehornstatic int adb_bus_detach(device_t dev)
195184299Snwhitehorn{
196184299Snwhitehorn	return (bus_generic_detach(dev));
197184299Snwhitehorn}
198184299Snwhitehorn
199184299Snwhitehorn
200184299Snwhitehornstatic void
201184299Snwhitehornadb_probe_nomatch(device_t dev, device_t child)
202184299Snwhitehorn{
203184299Snwhitehorn	struct adb_devinfo *dinfo;
204184299Snwhitehorn
205184299Snwhitehorn	if (bootverbose) {
206184299Snwhitehorn		dinfo = device_get_ivars(child);
207184299Snwhitehorn
208184299Snwhitehorn		device_printf(dev,"ADB %s at device %d (no driver attached)\n",
209184299Snwhitehorn		    adb_device_string[dinfo->default_address],dinfo->address);
210184299Snwhitehorn	}
211184299Snwhitehorn}
212184299Snwhitehorn
213184299Snwhitehornu_int
214184299Snwhitehornadb_receive_raw_packet(device_t dev, u_char status, u_char command, int len,
215184299Snwhitehorn    u_char *data)
216184299Snwhitehorn{
217184299Snwhitehorn	struct adb_softc *sc = device_get_softc(dev);
218184299Snwhitehorn	u_char addr = command >> 4;
219184299Snwhitehorn
220184299Snwhitehorn	if (len > 0 && (command & 0x0f) == ((ADB_COMMAND_TALK << 2) | 3)) {
221184299Snwhitehorn		memcpy(&sc->devinfo[addr].register3,data,2);
222184299Snwhitehorn		sc->devinfo[addr].handler_id = data[1];
223184299Snwhitehorn	}
224184299Snwhitehorn
225184299Snwhitehorn	if (sc->sync_packet == command)  {
226184299Snwhitehorn		memcpy(sc->syncreg,data,(len > 8) ? 8 : len);
227184299Snwhitehorn		atomic_store_rel_int(&sc->packet_reply,len + 1);
228185724Snwhitehorn		wakeup(sc);
229184299Snwhitehorn	}
230184299Snwhitehorn
231184299Snwhitehorn	if (sc->children[addr] != NULL) {
232184299Snwhitehorn		ADB_RECEIVE_PACKET(sc->children[addr],status,
233184299Snwhitehorn			(command & 0x0f) >> 2,command & 0x03,len,data);
234184299Snwhitehorn	}
235184299Snwhitehorn
236184299Snwhitehorn	return (0);
237184299Snwhitehorn}
238184299Snwhitehorn
239184299Snwhitehornstatic int
240184299Snwhitehornadb_print_child(device_t dev, device_t child)
241184299Snwhitehorn{
242184299Snwhitehorn	struct adb_devinfo *dinfo;
243184299Snwhitehorn	int retval = 0;
244184299Snwhitehorn
245184299Snwhitehorn	dinfo = device_get_ivars(child);
246184299Snwhitehorn
247184299Snwhitehorn	retval += bus_print_child_header(dev,child);
248184299Snwhitehorn	printf(" at device %d",dinfo->address);
249184299Snwhitehorn	retval += bus_print_child_footer(dev, child);
250184299Snwhitehorn
251184299Snwhitehorn	return (retval);
252184299Snwhitehorn}
253184299Snwhitehorn
254184299Snwhitehornu_int
255184299Snwhitehornadb_send_packet(device_t dev, u_char command, u_char reg, int len, u_char *data)
256184299Snwhitehorn{
257184299Snwhitehorn	u_char command_byte = 0;
258184299Snwhitehorn	struct adb_devinfo *dinfo;
259184299Snwhitehorn	struct adb_softc *sc;
260184299Snwhitehorn
261184299Snwhitehorn	sc = device_get_softc(device_get_parent(dev));
262184299Snwhitehorn	dinfo = device_get_ivars(dev);
263184299Snwhitehorn
264184299Snwhitehorn	command_byte |= dinfo->address << 4;
265184299Snwhitehorn	command_byte |= command << 2;
266184299Snwhitehorn	command_byte |= reg;
267184299Snwhitehorn
268184299Snwhitehorn	ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
269184299Snwhitehorn
270184299Snwhitehorn	return (0);
271184299Snwhitehorn}
272184299Snwhitehorn
273184299Snwhitehornu_int
274184299Snwhitehornadb_set_autopoll(device_t dev, u_char enable)
275184299Snwhitehorn{
276184299Snwhitehorn	struct adb_devinfo *dinfo;
277184299Snwhitehorn	struct adb_softc *sc;
278184299Snwhitehorn	uint16_t mod = 0;
279184299Snwhitehorn
280184299Snwhitehorn	sc = device_get_softc(device_get_parent(dev));
281184299Snwhitehorn	dinfo = device_get_ivars(dev);
282184299Snwhitehorn
283184299Snwhitehorn	mod = enable << dinfo->address;
284184299Snwhitehorn	if (enable) {
285184299Snwhitehorn		sc->autopoll_mask |= mod;
286184299Snwhitehorn	} else {
287184299Snwhitehorn		mod = ~mod;
288184299Snwhitehorn		sc->autopoll_mask &= mod;
289184299Snwhitehorn	}
290184299Snwhitehorn
291184299Snwhitehorn	ADB_HB_SET_AUTOPOLL_MASK(sc->parent,sc->autopoll_mask);
292184299Snwhitehorn
293184299Snwhitehorn	return (0);
294184299Snwhitehorn}
295184299Snwhitehorn
296184299Snwhitehornuint8_t
297184299Snwhitehornadb_get_device_type(device_t dev)
298184299Snwhitehorn{
299184299Snwhitehorn	struct adb_devinfo *dinfo;
300184299Snwhitehorn
301184299Snwhitehorn	dinfo = device_get_ivars(dev);
302184299Snwhitehorn	return (dinfo->default_address);
303184299Snwhitehorn}
304184299Snwhitehorn
305184299Snwhitehornuint8_t
306184299Snwhitehornadb_get_device_handler(device_t dev)
307184299Snwhitehorn{
308184299Snwhitehorn	struct adb_devinfo *dinfo;
309184299Snwhitehorn
310184299Snwhitehorn	dinfo = device_get_ivars(dev);
311184299Snwhitehorn	return (dinfo->handler_id);
312184299Snwhitehorn}
313184299Snwhitehorn
314184299Snwhitehornstatic int
315184299Snwhitehornadb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command,
316185724Snwhitehorn    uint8_t reg, int len, u_char *data, u_char *reply)
317184299Snwhitehorn{
318184299Snwhitehorn	u_char command_byte = 0;
319184299Snwhitehorn	struct adb_softc *sc;
320184299Snwhitehorn	int result = -1;
321185724Snwhitehorn	int i = 1;
322184299Snwhitehorn
323184299Snwhitehorn	sc = device_get_softc(dev);
324184299Snwhitehorn
325184299Snwhitehorn	command_byte |= to << 4;
326184299Snwhitehorn	command_byte |= command << 2;
327184299Snwhitehorn	command_byte |= reg;
328184299Snwhitehorn
329184299Snwhitehorn	/* Wait if someone else has a synchronous request pending */
330185724Snwhitehorn	while (!atomic_cmpset_int(&sc->sync_packet, 0xffff, command_byte))
331185724Snwhitehorn		tsleep(sc, 0, "ADB sync", hz/10);
332184299Snwhitehorn
333184299Snwhitehorn	sc->packet_reply = 0;
334184299Snwhitehorn	sc->sync_packet = command_byte;
335184299Snwhitehorn
336184299Snwhitehorn	ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
337184299Snwhitehorn
338184299Snwhitehorn	while (!atomic_fetchadd_int(&sc->packet_reply,0)) {
339184473Snwhitehorn		/*
340184473Snwhitehorn		 * Maybe the command got lost? Try resending and polling the
341184473Snwhitehorn		 * controller.
342184473Snwhitehorn		 */
343185724Snwhitehorn		if (i % 40 == 0)
344184473Snwhitehorn			ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte,
345184473Snwhitehorn			    len, data, 1);
346184299Snwhitehorn
347185724Snwhitehorn		tsleep(sc, 0, "ADB sync", hz/10);
348184299Snwhitehorn		i++;
349184299Snwhitehorn	}
350184299Snwhitehorn
351184299Snwhitehorn	result = sc->packet_reply - 1;
352184299Snwhitehorn
353185724Snwhitehorn	if (reply != NULL && result > 0)
354185724Snwhitehorn		memcpy(reply,sc->syncreg,result);
355185724Snwhitehorn
356184299Snwhitehorn	/* Clear packet sync */
357184299Snwhitehorn	sc->packet_reply = 0;
358184299Snwhitehorn
359185724Snwhitehorn	/*
360185724Snwhitehorn	 * We can't match a value beyond 8 bits, so set sync_packet to
361185724Snwhitehorn	 * 0xffff to avoid collisions.
362185724Snwhitehorn	 */
363185724Snwhitehorn	atomic_set_int(&sc->sync_packet, 0xffff);
364184299Snwhitehorn
365184299Snwhitehorn	return (result);
366184299Snwhitehorn}
367184299Snwhitehorn
368184299Snwhitehornuint8_t
369184299Snwhitehornadb_set_device_handler(device_t dev, uint8_t newhandler)
370184299Snwhitehorn{
371184299Snwhitehorn	struct adb_softc *sc;
372184299Snwhitehorn	struct adb_devinfo *dinfo;
373184299Snwhitehorn	uint16_t newr3;
374184299Snwhitehorn
375184299Snwhitehorn	dinfo = device_get_ivars(dev);
376184299Snwhitehorn	sc = device_get_softc(device_get_parent(dev));
377184299Snwhitehorn
378184299Snwhitehorn	newr3 = dinfo->register3 & 0xff00;
379184299Snwhitehorn	newr3 |= (uint16_t)(newhandler);
380184299Snwhitehorn
381185724Snwhitehorn	adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, ADB_COMMAND_LISTEN,
382185724Snwhitehorn	    3, sizeof(uint16_t), (u_char *)(&newr3), NULL);
383184299Snwhitehorn	adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
384185724Snwhitehorn	    ADB_COMMAND_TALK, 3, 0, NULL, NULL);
385184299Snwhitehorn
386184299Snwhitehorn	return (dinfo->handler_id);
387184299Snwhitehorn}
388184299Snwhitehorn
389185724Snwhitehornsize_t
390185724Snwhitehornadb_read_register(device_t dev, u_char reg, void *data)
391184299Snwhitehorn{
392184299Snwhitehorn	struct adb_softc *sc;
393184299Snwhitehorn	struct adb_devinfo *dinfo;
394185724Snwhitehorn	size_t result;
395184299Snwhitehorn
396184299Snwhitehorn	dinfo = device_get_ivars(dev);
397184299Snwhitehorn	sc = device_get_softc(device_get_parent(dev));
398184299Snwhitehorn
399185724Snwhitehorn	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
400185724Snwhitehorn	           ADB_COMMAND_TALK, reg, 0, NULL, data);
401184299Snwhitehorn
402185724Snwhitehorn	return (result);
403184299Snwhitehorn}
404184299Snwhitehorn
405199888Snwhitehornsize_t
406199888Snwhitehornadb_write_register(device_t dev, u_char reg, size_t len, void *data)
407199888Snwhitehorn{
408199888Snwhitehorn	struct adb_softc *sc;
409199888Snwhitehorn	struct adb_devinfo *dinfo;
410199888Snwhitehorn	size_t result;
411199888Snwhitehorn
412199888Snwhitehorn	dinfo = device_get_ivars(dev);
413199888Snwhitehorn	sc = device_get_softc(device_get_parent(dev));
414199888Snwhitehorn
415199888Snwhitehorn	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
416199888Snwhitehorn		   ADB_COMMAND_LISTEN, reg, len, (u_char *)data, NULL);
417199888Snwhitehorn
418199888Snwhitehorn	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
419199888Snwhitehorn	           ADB_COMMAND_TALK, reg, 0, NULL, NULL);
420199888Snwhitehorn
421199888Snwhitehorn	return (result);
422199888Snwhitehorn}
423