111819Sjulian/*-
211819Sjulian * Copyright (C) 2008 Nathan Whitehorn
311819Sjulian * All rights reserved.
411819Sjulian *
511819Sjulian * Redistribution and use in source and binary forms, with or without
611819Sjulian * modification, are permitted provided that the following conditions
711819Sjulian * are met:
811819Sjulian * 1. Redistributions of source code must retain the above copyright
911819Sjulian *    notice, this list of conditions and the following disclaimer.
1011819Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1111819Sjulian *    notice, this list of conditions and the following disclaimer in the
1211819Sjulian *    documentation and/or other materials provided with the distribution.
1311819Sjulian *
1411819Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1511819Sjulian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1611819Sjulian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1711819Sjulian * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1811819Sjulian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1911819Sjulian * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2011819Sjulian * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2111819Sjulian * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2211819Sjulian * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2311819Sjulian * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2411819Sjulian *
2511819Sjulian * $FreeBSD: releng/10.2/sys/dev/adb/adb_bus.c 199888 2009-11-28 17:48:25Z nwhitehorn $
2611819Sjulian */
2711819Sjulian
2811819Sjulian#include <sys/cdefs.h>
2911819Sjulian#include <sys/param.h>
3011819Sjulian#include <sys/systm.h>
3111819Sjulian#include <sys/module.h>
3211819Sjulian#include <sys/bus.h>
3311819Sjulian#include <sys/conf.h>
3412057Sjulian#include <sys/kernel.h>
3512057Sjulian
3650477Speter#include <machine/bus.h>
3711819Sjulian
3811819Sjulian#include <vm/vm.h>
3911819Sjulian#include <vm/pmap.h>
4011819Sjulian
4129024Sbde#include "adb.h"
4225345Sjhay#include "adbvar.h"
4311819Sjulian
4411819Sjulianstatic int adb_bus_probe(device_t dev);
4511819Sjulianstatic int adb_bus_attach(device_t dev);
4611819Sjulianstatic int adb_bus_detach(device_t dev);
4711819Sjulianstatic void adb_bus_enumerate(void *xdev);
4811819Sjulianstatic void adb_probe_nomatch(device_t dev, device_t child);
4911819Sjulianstatic int adb_print_child(device_t dev, device_t child);
5011819Sjulian
5111819Sjulianstatic 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);
5225652Sjhay
5311819Sjulianstatic char *adb_device_string[] = {
5433181Seivind	"HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc"
5511819Sjulian};
5611819Sjulian
5725345Sjhaystatic device_method_t adb_bus_methods[] = {
5811819Sjulian	/* Device interface */
5911819Sjulian	DEVMETHOD(device_probe,		adb_bus_probe),
6025345Sjhay	DEVMETHOD(device_attach,	adb_bus_attach),
6111819Sjulian	DEVMETHOD(device_detach,        adb_bus_detach),
6211819Sjulian        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
6311819Sjulian        DEVMETHOD(device_suspend,       bus_generic_suspend),
6428270Swollman        DEVMETHOD(device_resume,        bus_generic_resume),
6528270Swollman
6611819Sjulian	/* Bus Interface */
6728270Swollman        DEVMETHOD(bus_probe_nomatch,    adb_probe_nomatch),
6811819Sjulian        DEVMETHOD(bus_print_child,	adb_print_child),
6950519Sjhay
7050519Sjhay	{ 0, 0 },
7111819Sjulian};
7211819Sjulian
7311819Sjuliandriver_t adb_driver = {
7411819Sjulian	"adb",
7511819Sjulian	adb_bus_methods,
7611819Sjulian	sizeof(struct adb_softc),
7725345Sjhay};
7811819Sjulian
7928270Swollmandevclass_t adb_devclass;
8025345Sjhay
8111819Sjulianstatic int
8211819Sjulianadb_bus_probe(device_t dev)
8311819Sjulian{
8411819Sjulian	device_set_desc(dev, "Apple Desktop Bus");
8511819Sjulian	return (0);
8611819Sjulian}
8725652Sjhay
8811819Sjulianstatic int
8928270Swollmanadb_bus_attach(device_t dev)
9011819Sjulian{
9111819Sjulian	struct adb_softc *sc = device_get_softc(dev);
9211819Sjulian	sc->enum_hook.ich_func = adb_bus_enumerate;
9311819Sjulian	sc->enum_hook.ich_arg = dev;
9411819Sjulian
9511819Sjulian	/*
9611819Sjulian	 * We should wait until interrupts are enabled to try to probe
9711819Sjulian	 * the bus. Enumerating the ADB involves receiving packets,
9811819Sjulian	 * which works best with interrupts enabled.
9911819Sjulian	 */
10011819Sjulian
10125345Sjhay	if (config_intrhook_establish(&sc->enum_hook) != 0)
10211819Sjulian		return (ENOMEM);
10325652Sjhay
10446112Sphk	return (0);
10525345Sjhay}
10611819Sjulian
10711819Sjulianstatic void
10811819Sjulianadb_bus_enumerate(void *xdev)
10911819Sjulian{
11011819Sjulian	device_t dev = (device_t)xdev;
11111819Sjulian
11211819Sjulian	struct adb_softc *sc = device_get_softc(dev);
11325652Sjhay	uint8_t i, next_free;
11425652Sjhay	uint16_t r3;
11525652Sjhay
11625652Sjhay	sc->sc_dev = dev;
11711819Sjulian	sc->parent = device_get_parent(dev);
11811819Sjulian
11911819Sjulian	sc->packet_reply = 0;
12011819Sjulian	sc->autopoll_mask = 0;
12111819Sjulian	sc->sync_packet = 0xffff;
12211819Sjulian
12311819Sjulian	/* Initialize devinfo */
12411819Sjulian	for (i = 0; i < 16; i++) {
12511819Sjulian		sc->devinfo[i].address = i;
12611819Sjulian		sc->devinfo[i].default_address = 0;
12711819Sjulian	}
12811819Sjulian
12911819Sjulian	/* Reset ADB bus */
13025345Sjhay	adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL,NULL);
13111819Sjulian	DELAY(1500);
13228270Swollman
13325345Sjhay	/* Enumerate bus */
13411819Sjulian	next_free = 8;
13511819Sjulian
13628270Swollman	for (i = 1; i <= 7; i++) {
13711819Sjulian	    int8_t first_relocated = -1;
13811819Sjulian	    int reply = 0;
13911819Sjulian
14011819Sjulian	    do {
14125652Sjhay		reply = adb_send_raw_packet_sync(dev,i,
14225652Sjhay			    ADB_COMMAND_TALK,3,0,NULL,NULL);
14311819Sjulian
14411819Sjulian		if (reply) {
14525652Sjhay			/* If we got a response, relocate to next_free */
14611819Sjulian			r3 = sc->devinfo[i].register3;
14711819Sjulian			r3 &= 0xf000;
14811819Sjulian			r3 |= ((uint16_t)(next_free) & 0x000f) << 8;
14911819Sjulian			r3 |= 0x00fe;
15011819Sjulian
15111819Sjulian			adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3,
15211819Sjulian			    sizeof(uint16_t),(u_char *)(&r3),NULL);
15311819Sjulian
15411819Sjulian			adb_send_raw_packet_sync(dev,next_free,
15511819Sjulian			    ADB_COMMAND_TALK,3,0,NULL,NULL);
15611819Sjulian
15711819Sjulian			sc->devinfo[next_free].default_address = i;
15811819Sjulian			if (first_relocated < 0)
15911819Sjulian				first_relocated = next_free;
16011819Sjulian
16111819Sjulian			next_free++;
16211819Sjulian		} else if (first_relocated > 0) {
16311819Sjulian			/* Collisions removed, relocate first device back */
16411819Sjulian
16511819Sjulian			r3 = sc->devinfo[i].register3;
16625652Sjhay			r3 &= 0xf000;
16711819Sjulian			r3 |= ((uint16_t)(i) & 0x000f) << 8;
16811819Sjulian
16911819Sjulian			adb_send_raw_packet_sync(dev,first_relocated,
17011819Sjulian			    ADB_COMMAND_LISTEN,3,
17125652Sjhay			    sizeof(uint16_t),(u_char *)(&r3),NULL);
17211819Sjulian			adb_send_raw_packet_sync(dev,i,
17325652Sjhay			    ADB_COMMAND_TALK,3,0,NULL,NULL);
17411819Sjulian
17511819Sjulian			sc->devinfo[i].default_address = i;
17611819Sjulian			sc->devinfo[(int)(first_relocated)].default_address = 0;
17711819Sjulian			break;
17825652Sjhay		}
17911819Sjulian	    } while (reply);
18011819Sjulian	}
18111819Sjulian
18211819Sjulian	for (i = 0; i < 16; i++) {
18311819Sjulian		if (sc->devinfo[i].default_address) {
18411819Sjulian			sc->children[i] = device_add_child(dev, NULL, -1);
18511819Sjulian			device_set_ivars(sc->children[i], &sc->devinfo[i]);
18611819Sjulian		}
18711819Sjulian	}
18811819Sjulian
18911819Sjulian	bus_generic_attach(dev);
19011819Sjulian
19111819Sjulian	config_intrhook_disestablish(&sc->enum_hook);
19211819Sjulian}
19311819Sjulian
19411819Sjulianstatic int adb_bus_detach(device_t dev)
19511819Sjulian{
19625652Sjhay	return (bus_generic_detach(dev));
19725652Sjhay}
19811819Sjulian
19911819Sjulian
20025652Sjhaystatic void
20111819Sjulianadb_probe_nomatch(device_t dev, device_t child)
20211819Sjulian{
20311819Sjulian	struct adb_devinfo *dinfo;
20411819Sjulian
20511819Sjulian	if (bootverbose) {
20625652Sjhay		dinfo = device_get_ivars(child);
20711819Sjulian
20825652Sjhay		device_printf(dev,"ADB %s at device %d (no driver attached)\n",
20911819Sjulian		    adb_device_string[dinfo->default_address],dinfo->address);
21025652Sjhay	}
21111819Sjulian}
21211819Sjulian
21311819Sjulianu_int
21411819Sjulianadb_receive_raw_packet(device_t dev, u_char status, u_char command, int len,
21525652Sjhay    u_char *data)
21625652Sjhay{
21725652Sjhay	struct adb_softc *sc = device_get_softc(dev);
21825652Sjhay	u_char addr = command >> 4;
21925652Sjhay
22025652Sjhay	if (len > 0 && (command & 0x0f) == ((ADB_COMMAND_TALK << 2) | 3)) {
22125652Sjhay		memcpy(&sc->devinfo[addr].register3,data,2);
22225652Sjhay		sc->devinfo[addr].handler_id = data[1];
22325652Sjhay	}
22425652Sjhay
22525652Sjhay	if (sc->sync_packet == command)  {
22625652Sjhay		memcpy(sc->syncreg,data,(len > 8) ? 8 : len);
22725652Sjhay		atomic_store_rel_int(&sc->packet_reply,len + 1);
22825652Sjhay		wakeup(sc);
22925652Sjhay	}
23025652Sjhay
23125652Sjhay	if (sc->children[addr] != NULL) {
23225652Sjhay		ADB_RECEIVE_PACKET(sc->children[addr],status,
23325652Sjhay			(command & 0x0f) >> 2,command & 0x03,len,data);
23425652Sjhay	}
23525652Sjhay
23625652Sjhay	return (0);
23725652Sjhay}
23825652Sjhay
23925652Sjhaystatic int
24025652Sjhayadb_print_child(device_t dev, device_t child)
24125652Sjhay{
24225652Sjhay	struct adb_devinfo *dinfo;
24325652Sjhay	int retval = 0;
24411819Sjulian
24511819Sjulian	dinfo = device_get_ivars(child);
24625652Sjhay
24728270Swollman	retval += bus_print_child_header(dev,child);
24825652Sjhay	printf(" at device %d",dinfo->address);
24925652Sjhay	retval += bus_print_child_footer(dev, child);
25025652Sjhay
25111819Sjulian	return (retval);
25211819Sjulian}
25311819Sjulian
25411819Sjulianu_int
25511819Sjulianadb_send_packet(device_t dev, u_char command, u_char reg, int len, u_char *data)
25611819Sjulian{
25711819Sjulian	u_char command_byte = 0;
25811819Sjulian	struct adb_devinfo *dinfo;
25911819Sjulian	struct adb_softc *sc;
26011819Sjulian
26111819Sjulian	sc = device_get_softc(device_get_parent(dev));
26211819Sjulian	dinfo = device_get_ivars(dev);
26311819Sjulian
26411819Sjulian	command_byte |= dinfo->address << 4;
26511819Sjulian	command_byte |= command << 2;
26611819Sjulian	command_byte |= reg;
26711819Sjulian
26811819Sjulian	ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
26911819Sjulian
27011819Sjulian	return (0);
27111819Sjulian}
27211819Sjulian
27311819Sjulianu_int
27425652Sjhayadb_set_autopoll(device_t dev, u_char enable)
27511819Sjulian{
27611819Sjulian	struct adb_devinfo *dinfo;
27728270Swollman	struct adb_softc *sc;
27811819Sjulian	uint16_t mod = 0;
27911819Sjulian
28011819Sjulian	sc = device_get_softc(device_get_parent(dev));
28111819Sjulian	dinfo = device_get_ivars(dev);
28211819Sjulian
28328270Swollman	mod = enable << dinfo->address;
28411819Sjulian	if (enable) {
28528270Swollman		sc->autopoll_mask |= mod;
28611819Sjulian	} else {
28728270Swollman		mod = ~mod;
28825652Sjhay		sc->autopoll_mask &= mod;
28911819Sjulian	}
29011819Sjulian
29111819Sjulian	ADB_HB_SET_AUTOPOLL_MASK(sc->parent,sc->autopoll_mask);
29228270Swollman
29311819Sjulian	return (0);
29411819Sjulian}
29511819Sjulian
29611819Sjulianuint8_t
29711819Sjulianadb_get_device_type(device_t dev)
29828270Swollman{
29911819Sjulian	struct adb_devinfo *dinfo;
30028270Swollman
30111819Sjulian	dinfo = device_get_ivars(dev);
30228270Swollman	return (dinfo->default_address);
30325652Sjhay}
30411819Sjulian
30511819Sjulianuint8_t
30611819Sjulianadb_get_device_handler(device_t dev)
30728270Swollman{
30811819Sjulian	struct adb_devinfo *dinfo;
30911819Sjulian
31011819Sjulian	dinfo = device_get_ivars(dev);
31111819Sjulian	return (dinfo->handler_id);
31211819Sjulian}
31311819Sjulian
31411819Sjulianstatic int
31511819Sjulianadb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command,
31611819Sjulian    uint8_t reg, int len, u_char *data, u_char *reply)
31711819Sjulian{
31811819Sjulian	u_char command_byte = 0;
31911819Sjulian	struct adb_softc *sc;
32011991Sjulian	int result = -1;
32111991Sjulian	int i = 1;
32211819Sjulian
32311819Sjulian	sc = device_get_softc(dev);
32411819Sjulian
32511819Sjulian	command_byte |= to << 4;
32611819Sjulian	command_byte |= command << 2;
32711819Sjulian	command_byte |= reg;
32811819Sjulian
32911819Sjulian	/* Wait if someone else has a synchronous request pending */
33011819Sjulian	while (!atomic_cmpset_int(&sc->sync_packet, 0xffff, command_byte))
33111819Sjulian		tsleep(sc, 0, "ADB sync", hz/10);
33211819Sjulian
33311819Sjulian	sc->packet_reply = 0;
33411819Sjulian	sc->sync_packet = command_byte;
33511819Sjulian
33611819Sjulian	ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
33711819Sjulian
33811819Sjulian	while (!atomic_fetchadd_int(&sc->packet_reply,0)) {
33911819Sjulian		/*
34011819Sjulian		 * Maybe the command got lost? Try resending and polling the
34111819Sjulian		 * controller.
34211819Sjulian		 */
34311819Sjulian		if (i % 40 == 0)
34411819Sjulian			ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte,
34511819Sjulian			    len, data, 1);
34611819Sjulian
34711819Sjulian		tsleep(sc, 0, "ADB sync", hz/10);
34811819Sjulian		i++;
34911819Sjulian	}
35011819Sjulian
35111819Sjulian	result = sc->packet_reply - 1;
35211819Sjulian
35325652Sjhay	if (reply != NULL && result > 0)
35411819Sjulian		memcpy(reply,sc->syncreg,result);
35525652Sjhay
35611819Sjulian	/* Clear packet sync */
35711819Sjulian	sc->packet_reply = 0;
35811819Sjulian
35911819Sjulian	/*
36011819Sjulian	 * We can't match a value beyond 8 bits, so set sync_packet to
36111819Sjulian	 * 0xffff to avoid collisions.
36211819Sjulian	 */
36311819Sjulian	atomic_set_int(&sc->sync_packet, 0xffff);
36411819Sjulian
36511819Sjulian	return (result);
36611819Sjulian}
36711819Sjulian
36811819Sjulianuint8_t
36911819Sjulianadb_set_device_handler(device_t dev, uint8_t newhandler)
37011819Sjulian{
37111819Sjulian	struct adb_softc *sc;
37211819Sjulian	struct adb_devinfo *dinfo;
37311819Sjulian	uint16_t newr3;
37411819Sjulian
37511819Sjulian	dinfo = device_get_ivars(dev);
37611819Sjulian	sc = device_get_softc(device_get_parent(dev));
37711819Sjulian
37811819Sjulian	newr3 = dinfo->register3 & 0xff00;
37911819Sjulian	newr3 |= (uint16_t)(newhandler);
38011819Sjulian
38111819Sjulian	adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, ADB_COMMAND_LISTEN,
38211819Sjulian	    3, sizeof(uint16_t), (u_char *)(&newr3), NULL);
38311819Sjulian	adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
38411819Sjulian	    ADB_COMMAND_TALK, 3, 0, NULL, NULL);
38511819Sjulian
38611819Sjulian	return (dinfo->handler_id);
38711819Sjulian}
38811819Sjulian
38911819Sjuliansize_t
39011819Sjulianadb_read_register(device_t dev, u_char reg, void *data)
39111819Sjulian{
39211819Sjulian	struct adb_softc *sc;
39311819Sjulian	struct adb_devinfo *dinfo;
39411819Sjulian	size_t result;
39511819Sjulian
39611819Sjulian	dinfo = device_get_ivars(dev);
39725652Sjhay	sc = device_get_softc(device_get_parent(dev));
39811819Sjulian
39911819Sjulian	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
40011819Sjulian	           ADB_COMMAND_TALK, reg, 0, NULL, data);
40111819Sjulian
40211819Sjulian	return (result);
40311819Sjulian}
40411819Sjulian
40511819Sjuliansize_t
40611819Sjulianadb_write_register(device_t dev, u_char reg, size_t len, void *data)
40711819Sjulian{
408	struct adb_softc *sc;
409	struct adb_devinfo *dinfo;
410	size_t result;
411
412	dinfo = device_get_ivars(dev);
413	sc = device_get_softc(device_get_parent(dev));
414
415	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
416		   ADB_COMMAND_LISTEN, reg, len, (u_char *)data, NULL);
417
418	result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
419	           ADB_COMMAND_TALK, reg, 0, NULL, NULL);
420
421	return (result);
422}
423