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