summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/conf/files.powerpc8
-rw-r--r--sys/dev/adb/adb.h78
-rw-r--r--sys/dev/adb/adb_bus.c384
-rw-r--r--sys/dev/adb/adb_hb_if.m58
-rw-r--r--sys/dev/adb/adb_if.m49
-rw-r--r--sys/dev/adb/adb_kbd.c692
-rw-r--r--sys/dev/adb/adb_mouse.c539
-rw-r--r--sys/dev/adb/adbvar.h56
-rw-r--r--sys/powerpc/conf/GENERIC5
-rw-r--r--sys/powerpc/powermac/cuda.c641
-rw-r--r--sys/powerpc/powermac/cudavar.h94
-rw-r--r--sys/powerpc/powermac/macgpio.c340
-rw-r--r--sys/powerpc/powermac/macgpiovar.h44
-rw-r--r--sys/powerpc/powermac/macio.c5
-rw-r--r--sys/powerpc/powermac/pmu.c617
-rw-r--r--sys/powerpc/powermac/pmuvar.h171
-rw-r--r--sys/powerpc/powermac/viareg.h49
17 files changed, 3830 insertions, 0 deletions
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc
index 25b193e..343471d 100644
--- a/sys/conf/files.powerpc
+++ b/sys/conf/files.powerpc
@@ -28,6 +28,11 @@ opt_ah.h optional ath_hal \
crypto/blowfish/bf_enc.c optional crypto | ipsec
crypto/des/des_enc.c optional crypto | ipsec | netsmb
dev/bm/if_bm.c optional bm powermac
+dev/adb/adb_bus.c optional adb
+dev/adb/adb_kbd.c optional adb
+dev/adb/adb_mouse.c optional adb
+dev/adb/adb_hb_if.m optional adb
+dev/adb/adb_if.m optional adb
dev/cfi/cfi_bus_lbc.c optional cfi
dev/fb/fb.c optional sc
dev/hwpmc/hwpmc_powerpc.c optional hwpmc
@@ -119,6 +124,9 @@ powerpc/powermac/macio.c optional powermac pci
powerpc/powermac/openpic_macio.c optional powermac pci
powerpc/powermac/pswitch.c optional powermac pswitch
powerpc/powermac/uninorth.c optional powermac pci
+powerpc/powermac/cuda.c optional powermac cuda
+powerpc/powermac/pmu.c optional powermac pmu
+powerpc/powermac/macgpio.c optional powermac pci
powerpc/powerpc/atomic.S standard
powerpc/powerpc/autoconf.c standard
powerpc/powerpc/bcopy.c standard
diff --git a/sys/dev/adb/adb.h b/sys/dev/adb/adb.h
new file mode 100644
index 0000000..8d68a87
--- /dev/null
+++ b/sys/dev/adb/adb.h
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (C) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _POWERPC_ADB_H_
+#define _POWERPC_ADB_H_
+
+#include "adb_hb_if.h"
+#include "adb_if.h"
+
+enum {
+ ADB_COMMAND_FLUSH = 0,
+ ADB_COMMAND_LISTEN = 2,
+ ADB_COMMAND_TALK = 3,
+};
+
+enum {
+ ADB_DEVICE_DONGLE = 0x01,
+ ADB_DEVICE_KEYBOARD = 0x02,
+ ADB_DEVICE_MOUSE = 0x03,
+ ADB_DEVICE_TABLET = 0x04,
+ ADB_DEVICE_MODEM = 0x05,
+
+ ADB_DEVICE_MISC = 0x07
+};
+
+struct adb_devinfo {
+ uint8_t address;
+ uint8_t default_address;
+ uint8_t handler_id;
+
+ uint16_t register3;
+};
+
+/* Pass packets down through the bus manager */
+u_int adb_send_packet(device_t dev, u_char command, u_char reg, int len,
+ u_char *data);
+u_int adb_set_autopoll(device_t dev, u_char enable);
+
+/* Pass packets up from the interface */
+u_int adb_receive_raw_packet(device_t dev, u_char status, u_char command,
+ int len, u_char *data);
+
+uint8_t adb_get_device_type(device_t dev);
+uint8_t adb_get_device_handler(device_t dev);
+uint8_t adb_set_device_handler(device_t dev, uint8_t newhandler);
+
+uint8_t adb_read_register(device_t dev, u_char reg, size_t *len, void *data);
+
+/* Bits for implementing ADB host bus adapters */
+extern devclass_t adb_devclass;
+extern driver_t adb_driver;
+
+#endif
+
diff --git a/sys/dev/adb/adb_bus.c b/sys/dev/adb/adb_bus.c
new file mode 100644
index 0000000..cc31a9d
--- /dev/null
+++ b/sys/dev/adb/adb_bus.c
@@ -0,0 +1,384 @@
+/*-
+ * Copyright (C) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include "adb.h"
+#include "adbvar.h"
+
+static int adb_bus_probe(device_t dev);
+static int adb_bus_attach(device_t dev);
+static int adb_bus_detach(device_t dev);
+static void adb_probe_nomatch(device_t dev, device_t child);
+static int adb_print_child(device_t dev, device_t child);
+
+static int adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, uint8_t reg, int len, u_char *data);
+
+static char *adb_device_string[] = {
+ "HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc"
+};
+
+static device_method_t adb_bus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, adb_bus_probe),
+ DEVMETHOD(device_attach, adb_bus_attach),
+ DEVMETHOD(device_detach, adb_bus_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus Interface */
+ DEVMETHOD(bus_probe_nomatch, adb_probe_nomatch),
+ DEVMETHOD(bus_print_child, adb_print_child),
+
+ { 0, 0 },
+};
+
+driver_t adb_driver = {
+ "adb",
+ adb_bus_methods,
+ sizeof(struct adb_softc),
+};
+
+devclass_t adb_devclass;
+
+static int
+adb_bus_probe(device_t dev)
+{
+ device_set_desc(dev, "Apple Desktop Bus");
+ return (0);
+}
+
+static int
+adb_bus_attach(device_t dev)
+{
+ struct adb_softc *sc = device_get_softc(dev);
+ uint8_t i, next_free;
+ uint16_t r3;
+
+ sc->sc_dev = dev;
+ sc->parent = device_get_parent(dev);
+
+ sc->packet_reply = 0;
+ sc->autopoll_mask = 0;
+
+ mtx_init(&sc->sc_sync_mtx,"adbsyn",NULL,MTX_DEF | MTX_RECURSE);
+
+ /* Initialize devinfo */
+ for (i = 0; i < 16; i++) {
+ sc->devinfo[i].address = i;
+ sc->devinfo[i].default_address = 0;
+ }
+
+ /* Reset ADB bus */
+ adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL);
+ DELAY(1500);
+
+ /* Enumerate bus */
+ next_free = 8;
+
+ for (i = 1; i < 7; i++) {
+ int8_t first_relocated = -1;
+ int reply = 0;
+
+ do {
+ reply = adb_send_raw_packet_sync(dev,i,
+ ADB_COMMAND_TALK,3,0,NULL);
+
+ if (reply) {
+ /* If we got a response, relocate to next_free */
+ r3 = sc->devinfo[i].register3;
+ r3 &= 0xf000;
+ r3 |= ((uint16_t)(next_free) & 0x000f) << 8;
+ r3 |= 0x00fe;
+
+ adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3,
+ sizeof(uint16_t),(u_char *)(&r3));
+
+ adb_send_raw_packet_sync(dev,next_free,
+ ADB_COMMAND_TALK,3,0,NULL);
+
+ sc->devinfo[next_free].default_address = i;
+ if (first_relocated < 0)
+ first_relocated = next_free;
+
+ next_free++;
+ } else if (first_relocated > 0) {
+ /* Collisions removed, relocate first device back */
+
+ r3 = sc->devinfo[i].register3;
+ r3 &= 0xf000;
+ r3 |= ((uint16_t)(i) & 0x000f) << 8;
+
+ adb_send_raw_packet_sync(dev,first_relocated,
+ ADB_COMMAND_LISTEN,3,
+ sizeof(uint16_t),(u_char *)(&r3));
+ adb_send_raw_packet_sync(dev,i,
+ ADB_COMMAND_TALK,3,0,NULL);
+
+ sc->devinfo[i].default_address = i;
+ sc->devinfo[(int)(first_relocated)].default_address = 0;
+ break;
+ }
+ } while (reply);
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (sc->devinfo[i].default_address) {
+ sc->children[i] = device_add_child(dev, NULL, -1);
+ device_set_ivars(sc->children[i], &sc->devinfo[i]);
+ }
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int adb_bus_detach(device_t dev)
+{
+ struct adb_softc *sc = device_get_softc(dev);
+
+ mtx_destroy(&sc->sc_sync_mtx);
+
+ return (bus_generic_detach(dev));
+}
+
+
+static void
+adb_probe_nomatch(device_t dev, device_t child)
+{
+ struct adb_devinfo *dinfo;
+
+ if (bootverbose) {
+ dinfo = device_get_ivars(child);
+
+ device_printf(dev,"ADB %s at device %d (no driver attached)\n",
+ adb_device_string[dinfo->default_address],dinfo->address);
+ }
+}
+
+u_int
+adb_receive_raw_packet(device_t dev, u_char status, u_char command, int len,
+ u_char *data)
+{
+ struct adb_softc *sc = device_get_softc(dev);
+ u_char addr = command >> 4;
+
+ if (len > 0 && (command & 0x0f) == ((ADB_COMMAND_TALK << 2) | 3)) {
+ memcpy(&sc->devinfo[addr].register3,data,2);
+ sc->devinfo[addr].handler_id = data[1];
+ }
+
+ if (sc->sync_packet == command) {
+ memcpy(sc->syncreg,data,(len > 8) ? 8 : len);
+ atomic_store_rel_int(&sc->packet_reply,len + 1);
+ }
+
+ if (sc->children[addr] != NULL) {
+ ADB_RECEIVE_PACKET(sc->children[addr],status,
+ (command & 0x0f) >> 2,command & 0x03,len,data);
+ }
+
+ return (0);
+}
+
+static int
+adb_print_child(device_t dev, device_t child)
+{
+ struct adb_devinfo *dinfo;
+ int retval = 0;
+
+ dinfo = device_get_ivars(child);
+
+ retval += bus_print_child_header(dev,child);
+ printf(" at device %d",dinfo->address);
+ retval += bus_print_child_footer(dev, child);
+
+ return (retval);
+}
+
+u_int
+adb_send_packet(device_t dev, u_char command, u_char reg, int len, u_char *data)
+{
+ u_char command_byte = 0;
+ struct adb_devinfo *dinfo;
+ struct adb_softc *sc;
+
+ sc = device_get_softc(device_get_parent(dev));
+ dinfo = device_get_ivars(dev);
+
+ command_byte |= dinfo->address << 4;
+ command_byte |= command << 2;
+ command_byte |= reg;
+
+ ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
+
+ return (0);
+}
+
+u_int
+adb_set_autopoll(device_t dev, u_char enable)
+{
+ struct adb_devinfo *dinfo;
+ struct adb_softc *sc;
+ uint16_t mod = 0;
+
+ sc = device_get_softc(device_get_parent(dev));
+ dinfo = device_get_ivars(dev);
+
+ mod = enable << dinfo->address;
+ if (enable) {
+ sc->autopoll_mask |= mod;
+ } else {
+ mod = ~mod;
+ sc->autopoll_mask &= mod;
+ }
+
+ ADB_HB_SET_AUTOPOLL_MASK(sc->parent,sc->autopoll_mask);
+
+ return (0);
+}
+
+uint8_t
+adb_get_device_type(device_t dev)
+{
+ struct adb_devinfo *dinfo;
+
+ dinfo = device_get_ivars(dev);
+ return (dinfo->default_address);
+}
+
+uint8_t
+adb_get_device_handler(device_t dev)
+{
+ struct adb_devinfo *dinfo;
+
+ dinfo = device_get_ivars(dev);
+ return (dinfo->handler_id);
+}
+
+static 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 command_byte = 0;
+ struct adb_softc *sc;
+ int result = -1;
+ int i = 0;
+
+ sc = device_get_softc(dev);
+
+ command_byte |= to << 4;
+ command_byte |= command << 2;
+ command_byte |= reg;
+
+ /* Wait if someone else has a synchronous request pending */
+ mtx_lock(&sc->sc_sync_mtx);
+
+ sc->packet_reply = 0;
+ sc->sync_packet = command_byte;
+
+ ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
+
+ while (!atomic_fetchadd_int(&sc->packet_reply,0)) {
+ /* Sometimes CUDA controllers hang up during cold boots.
+ Try poking them. */
+ if (i > 10)
+ ADB_HB_CONTROLLER_POLL(sc->parent);
+
+ DELAY(100);
+ i++;
+ }
+
+ result = sc->packet_reply - 1;
+
+ /* Clear packet sync */
+ sc->packet_reply = 0;
+ sc->sync_packet = 0xffff; /* We can't match a 16 bit value */
+
+ mtx_unlock(&sc->sc_sync_mtx);
+
+ return (result);
+}
+
+uint8_t
+adb_set_device_handler(device_t dev, uint8_t newhandler)
+{
+ struct adb_softc *sc;
+ struct adb_devinfo *dinfo;
+ uint16_t newr3;
+
+ dinfo = device_get_ivars(dev);
+ sc = device_get_softc(device_get_parent(dev));
+
+ newr3 = dinfo->register3 & 0xff00;
+ newr3 |= (uint16_t)(newhandler);
+
+ adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
+ ADB_COMMAND_LISTEN, 3, sizeof(uint16_t), (u_char *)(&newr3));
+ adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
+ ADB_COMMAND_TALK, 3, 0, NULL);
+
+ return (dinfo->handler_id);
+}
+
+uint8_t
+adb_read_register(device_t dev, u_char reg,
+ size_t *len, void *data)
+{
+ struct adb_softc *sc;
+ struct adb_devinfo *dinfo;
+ size_t orig_len;
+
+ dinfo = device_get_ivars(dev);
+ sc = device_get_softc(device_get_parent(dev));
+
+ orig_len = *len;
+
+ mtx_lock(&sc->sc_sync_mtx);
+
+ *len = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
+ ADB_COMMAND_TALK, reg, 0, NULL);
+
+ if (*len > 0)
+ memcpy(data,sc->syncreg,*len);
+
+ mtx_unlock(&sc->sc_sync_mtx);
+
+ return ((*len > 0) ? 0 : -1);
+}
+
diff --git a/sys/dev/adb/adb_hb_if.m b/sys/dev/adb/adb_hb_if.m
new file mode 100644
index 0000000..e27094d
--- /dev/null
+++ b/sys/dev/adb/adb_hb_if.m
@@ -0,0 +1,58 @@
+#-
+# Copyright (c) 2008 Nathan Whitehorn
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+
+#include <dev/adb/adb.h>
+
+INTERFACE adb_hb;
+
+#
+# Bus level operations. These are used by the ADB bus manager, and are
+# required to inplement an ADB interface.
+#
+
+METHOD u_int send_raw_packet {
+ device_t dev;
+ u_char command_byte;
+
+ int len;
+ u_char *data;
+ u_char poll;
+};
+
+METHOD u_int controller_poll {
+ device_t dev;
+};
+
+METHOD u_int set_autopoll_mask {
+ device_t dev;
+
+ uint16_t mask;
+};
+
diff --git a/sys/dev/adb/adb_if.m b/sys/dev/adb/adb_if.m
new file mode 100644
index 0000000..ef21fd8
--- /dev/null
+++ b/sys/dev/adb/adb_if.m
@@ -0,0 +1,49 @@
+#-
+# Copyright (c) 2008 Nathan Whitehorn
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+
+#include <dev/adb/adb.h>
+
+INTERFACE adb;
+
+#
+# Driver level operations
+#
+
+METHOD u_int receive_packet {
+ device_t dev;
+
+ u_char status;
+ u_char command;
+ u_char reg;
+
+ int len;
+ u_char *data;
+};
+
diff --git a/sys/dev/adb/adb_kbd.c b/sys/dev/adb/adb_kbd.c
new file mode 100644
index 0000000..db95cfe
--- /dev/null
+++ b/sys/dev/adb/adb_kbd.c
@@ -0,0 +1,692 @@
+/*-
+ * Copyright (C) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kbio.h>
+#include <sys/condvar.h>
+#include <sys/callout.h>
+#include <sys/kernel.h>
+
+#include <machine/bus.h>
+
+#include "opt_kbd.h"
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/kbdtables.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include "adb.h"
+
+#define KBD_DRIVER_NAME "akbd"
+
+#define AKBD_EMULATE_ATKBD 1
+
+static int adb_kbd_probe(device_t dev);
+static int adb_kbd_attach(device_t dev);
+static int adb_kbd_detach(device_t dev);
+static void akbd_repeat(void *xsc);
+
+static u_int adb_kbd_receive_packet(device_t dev, u_char status,
+ u_char command, u_char reg, int len, u_char *data);
+
+struct adb_kbd_softc {
+ keyboard_t sc_kbd;
+
+ device_t sc_dev;
+ struct mtx sc_mutex;
+ struct cv sc_cv;
+
+ int sc_mode;
+ int sc_state;
+
+ int have_led_control;
+
+ uint8_t buffer[8];
+ volatile int buffers;
+
+ struct callout sc_repeater;
+ int sc_repeatstart;
+ int sc_repeatcontinue;
+ uint8_t last_press;
+};
+
+static device_method_t adb_kbd_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, adb_kbd_probe),
+ DEVMETHOD(device_attach, adb_kbd_attach),
+ DEVMETHOD(device_detach, adb_kbd_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* ADB interface */
+ DEVMETHOD(adb_receive_packet, adb_kbd_receive_packet),
+
+ { 0, 0 }
+};
+
+static driver_t adb_kbd_driver = {
+ "akbd",
+ adb_kbd_methods,
+ sizeof(struct adb_kbd_softc),
+};
+
+static devclass_t adb_kbd_devclass;
+
+DRIVER_MODULE(akbd, adb, adb_kbd_driver, adb_kbd_devclass, 0, 0);
+
+static const uint8_t adb_to_at_scancode_map[128] = { 30, 31, 32, 33, 35, 34,
+ 44, 45, 46, 47, 0, 48, 16, 17, 18, 19, 21, 20, 2, 3, 4, 5, 7, 6, 13,
+ 10, 8, 12, 9, 11, 27, 24, 22, 26, 23, 25, 28, 38, 36, 40, 37, 39, 43,
+ 51, 53, 49, 50, 52, 15, 57, 41, 14, 0, 1, 29, 0, 42, 58, 56, 97, 98,
+ 100, 95, 0, 0, 83, 0, 55, 0, 78, 0, 69, 0, 0, 0, 91, 89, 0, 74, 13, 0,
+ 0, 82, 79, 80, 81, 75, 76, 77, 71, 0, 72, 73, 0, 0, 0, 63, 64, 65, 61,
+ 66, 67, 0, 87, 0, 105, 0, 70, 0, 68, 0, 88, 0, 107, 102, 94, 96, 103,
+ 62, 99, 60, 101, 59, 54, 93, 90, 0, 0 };
+
+/* keyboard driver declaration */
+static int akbd_configure(int flags);
+static kbd_probe_t akbd_probe;
+static kbd_init_t akbd_init;
+static kbd_term_t akbd_term;
+static kbd_intr_t akbd_interrupt;
+static kbd_test_if_t akbd_test_if;
+static kbd_enable_t akbd_enable;
+static kbd_disable_t akbd_disable;
+static kbd_read_t akbd_read;
+static kbd_check_t akbd_check;
+static kbd_read_char_t akbd_read_char;
+static kbd_check_char_t akbd_check_char;
+static kbd_ioctl_t akbd_ioctl;
+static kbd_lock_t akbd_lock;
+static kbd_clear_state_t akbd_clear_state;
+static kbd_get_state_t akbd_get_state;
+static kbd_set_state_t akbd_set_state;
+static kbd_poll_mode_t akbd_poll;
+
+keyboard_switch_t akbdsw = {
+ akbd_probe,
+ akbd_init,
+ akbd_term,
+ akbd_interrupt,
+ akbd_test_if,
+ akbd_enable,
+ akbd_disable,
+ akbd_read,
+ akbd_check,
+ akbd_read_char,
+ akbd_check_char,
+ akbd_ioctl,
+ akbd_lock,
+ akbd_clear_state,
+ akbd_get_state,
+ akbd_set_state,
+ genkbd_get_fkeystr,
+ akbd_poll,
+ genkbd_diag,
+};
+
+KEYBOARD_DRIVER(akbd, akbdsw, akbd_configure);
+
+static int
+adb_kbd_probe(device_t dev)
+{
+ uint8_t type;
+
+ type = adb_get_device_type(dev);
+
+ if (type != ADB_DEVICE_KEYBOARD)
+ return (ENXIO);
+
+ switch(adb_get_device_handler(dev)) {
+ case 1:
+ device_set_desc(dev,"Apple Standard Keyboard");
+ break;
+ case 2:
+ device_set_desc(dev,"Apple Extended Keyboard");
+ break;
+ case 4:
+ device_set_desc(dev,"Apple ISO Keyboard");
+ break;
+ case 5:
+ device_set_desc(dev,"Apple Extended ISO Keyboard");
+ break;
+ case 8:
+ device_set_desc(dev,"Apple Keyboard II");
+ break;
+ case 9:
+ device_set_desc(dev,"Apple ISO Keyboard II");
+ break;
+ case 12:
+ device_set_desc(dev,"PowerBook Keyboard");
+ break;
+ case 13:
+ device_set_desc(dev,"PowerBook ISO Keyboard");
+ break;
+ case 24:
+ device_set_desc(dev,"PowerBook Extended Keyboard");
+ break;
+ case 27:
+ device_set_desc(dev,"Apple Design Keyboard");
+ break;
+ case 195:
+ device_set_desc(dev,"PowerBook G3 Keyboard");
+ break;
+ case 196:
+ device_set_desc(dev,"iBook Keyboard");
+ break;
+ default:
+ device_set_desc(dev,"ADB Keyboard");
+ break;
+ }
+
+ return (0);
+}
+
+static int
+ms_to_ticks(int ms)
+{
+ if (hz > 1000)
+ return ms*(hz/1000);
+
+ return ms/(1000/hz);
+}
+
+static int
+adb_kbd_attach(device_t dev)
+{
+ struct adb_kbd_softc *sc;
+ keyboard_switch_t *sw;
+
+ sw = kbd_get_switch(KBD_DRIVER_NAME);
+ if (sw == NULL) {
+ return ENXIO;
+ }
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_mode = K_RAW;
+ sc->sc_state = 0;
+ sc->have_led_control = 0;
+ sc->buffers = 0;
+
+ /* Try stepping forward to the extended keyboard protocol */
+ adb_set_device_handler(dev,3);
+
+ mtx_init(&sc->sc_mutex,KBD_DRIVER_NAME,MTX_DEF,0);
+ cv_init(&sc->sc_cv,KBD_DRIVER_NAME);
+ callout_init(&sc->sc_repeater, 0);
+
+#ifdef AKBD_EMULATE_ATKBD
+ kbd_init_struct(&sc->sc_kbd, KBD_DRIVER_NAME, KB_101, 0, 0, 0, 0);
+ kbd_set_maps(&sc->sc_kbd, &key_map, &accent_map, fkey_tab,
+ sizeof(fkey_tab) / sizeof(fkey_tab[0]));
+#else
+ #error ADB raw mode not implemented
+#endif
+
+ KBD_FOUND_DEVICE(&sc->sc_kbd);
+ KBD_PROBE_DONE(&sc->sc_kbd);
+ KBD_INIT_DONE(&sc->sc_kbd);
+ KBD_CONFIG_DONE(&sc->sc_kbd);
+
+ (*sw->enable)(&sc->sc_kbd);
+
+ kbd_register(&sc->sc_kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (kbd_attach(&sc->sc_kbd)) {
+ adb_kbd_detach(dev);
+ return ENXIO;
+ }
+#endif
+
+ adb_set_autopoll(dev,1);
+
+ /* Check (asynchronously) if we can read out the LED state from
+ this keyboard by reading the key state register */
+ adb_send_packet(dev,ADB_COMMAND_TALK,2,0,NULL);
+
+ return (0);
+}
+
+static int
+adb_kbd_detach(device_t dev)
+{
+ struct adb_kbd_softc *sc;
+ keyboard_t *kbd;
+
+ sc = device_get_softc(dev);
+
+ adb_set_autopoll(dev,0);
+ callout_stop(&sc->sc_repeater);
+
+ mtx_lock(&sc->sc_mutex);
+
+ kbd = kbd_get_keyboard(kbd_find_keyboard(KBD_DRIVER_NAME,
+ device_get_unit(dev)));
+
+ kbdd_disable(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ kbd_detach(kbd);
+#endif
+
+ kbdd_term(kbd);
+
+ mtx_unlock(&sc->sc_mutex);
+
+ mtx_destroy(&sc->sc_mutex);
+ cv_destroy(&sc->sc_cv);
+
+ return (0);
+}
+
+static u_int
+adb_kbd_receive_packet(device_t dev, u_char status,
+ u_char command, u_char reg, int len, u_char *data)
+{
+ struct adb_kbd_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (command != ADB_COMMAND_TALK)
+ return 0;
+
+ if (reg == 2 && len == 2) {
+ sc->have_led_control = 1;
+ return 0;
+ }
+
+ if (reg != 0 || len != 2)
+ return (0);
+
+ mtx_lock(&sc->sc_mutex);
+ if ((data[0] & 0x7f) == 57 && sc->buffers < 7) {
+ /* Fake the down/up cycle for caps lock */
+ sc->buffer[sc->buffers++] = data[0] & 0x7f;
+ sc->buffer[sc->buffers++] = (data[0] & 0x7f) | (1 << 7);
+ } else {
+ sc->buffer[sc->buffers++] = data[0];
+ }
+
+ if (sc->buffer[sc->buffers-1] < 0xff)
+ sc->last_press = sc->buffer[sc->buffers-1];
+
+ if ((data[1] & 0x7f) == 57 && sc->buffers < 7) {
+ /* Fake the down/up cycle for caps lock */
+ sc->buffer[sc->buffers++] = data[1] & 0x7f;
+ sc->buffer[sc->buffers++] = (data[1] & 0x7f) | (1 << 7);
+ } else {
+ sc->buffer[sc->buffers++] = data[1];
+ }
+
+ if (sc->buffer[sc->buffers-1] < 0xff)
+ sc->last_press = sc->buffer[sc->buffers-1];
+
+ /* Stop any existing key repeating */
+ callout_stop(&sc->sc_repeater);
+
+ /* Schedule a repeat callback on keydown */
+ if (!(sc->last_press & (1 << 7))) {
+ callout_reset(&sc->sc_repeater,
+ ms_to_ticks(sc->sc_kbd.kb_delay1), akbd_repeat, sc);
+ }
+ mtx_unlock(&sc->sc_mutex);
+
+ cv_broadcast(&sc->sc_cv);
+
+ if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) {
+ sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
+ KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
+ }
+
+ return (0);
+}
+
+static void
+akbd_repeat(void *xsc) {
+ struct adb_kbd_softc *sc = xsc;
+ int notify_kbd = 0;
+
+ /* Fake an up/down key repeat so long as we have the
+ free buffers */
+ mtx_lock(&sc->sc_mutex);
+ if (sc->buffers < 7) {
+ sc->buffer[sc->buffers++] = sc->last_press | (1 << 7);
+ sc->buffer[sc->buffers++] = sc->last_press;
+
+ notify_kbd = 1;
+ }
+ mtx_unlock(&sc->sc_mutex);
+
+ if (notify_kbd && KBD_IS_ACTIVE(&sc->sc_kbd)
+ && KBD_IS_BUSY(&sc->sc_kbd)) {
+ sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
+ KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
+ }
+
+ /* Reschedule the callout */
+ callout_reset(&sc->sc_repeater, ms_to_ticks(sc->sc_kbd.kb_delay2),
+ akbd_repeat, sc);
+}
+
+static int
+akbd_configure(int flags)
+{
+ return 0;
+}
+
+static int
+akbd_probe(int unit, void *arg, int flags)
+{
+ return 0;
+}
+
+static int
+akbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+ return 0;
+}
+
+static int
+akbd_term(keyboard_t *kbd)
+{
+ return 0;
+}
+
+static int
+akbd_interrupt(keyboard_t *kbd, void *arg)
+{
+ return 0;
+}
+
+static int
+akbd_test_if(keyboard_t *kbd)
+{
+ return 0;
+}
+
+static int
+akbd_enable(keyboard_t *kbd)
+{
+ KBD_ACTIVATE(kbd);
+ return (0);
+}
+
+static int
+akbd_disable(keyboard_t *kbd)
+{
+ struct adb_kbd_softc *sc;
+ sc = (struct adb_kbd_softc *)(kbd);
+
+ callout_stop(&sc->sc_repeater);
+ KBD_DEACTIVATE(kbd);
+ return (0);
+}
+
+static int
+akbd_read(keyboard_t *kbd, int wait)
+{
+ return (0);
+}
+
+static int
+akbd_check(keyboard_t *kbd)
+{
+ struct adb_kbd_softc *sc;
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (FALSE);
+
+ sc = (struct adb_kbd_softc *)(kbd);
+
+ mtx_lock(&sc->sc_mutex);
+ if (sc->buffers > 0) {
+ mtx_unlock(&sc->sc_mutex);
+ return (TRUE);
+ }
+ mtx_unlock(&sc->sc_mutex);
+
+ return (FALSE);
+}
+
+static u_int
+akbd_read_char(keyboard_t *kbd, int wait)
+{
+ struct adb_kbd_softc *sc;
+ uint8_t adb_code, final_scancode;
+ int i;
+
+ sc = (struct adb_kbd_softc *)(kbd);
+
+ mtx_lock(&sc->sc_mutex);
+ if (!sc->buffers && wait)
+ cv_wait(&sc->sc_cv,&sc->sc_mutex);
+
+ if (!sc->buffers) {
+ mtx_unlock(&sc->sc_mutex);
+ return (0);
+ }
+
+ adb_code = sc->buffer[0];
+
+ for (i = 1; i < sc->buffers; i++)
+ sc->buffer[i-1] = sc->buffer[i];
+
+ sc->buffers--;
+ mtx_unlock(&sc->sc_mutex);
+
+ #ifdef AKBD_EMULATE_ATKBD
+ final_scancode = adb_to_at_scancode_map[adb_code & 0x7f];
+ final_scancode |= adb_code & 0x80;
+ #else
+ final_scancode = adb_code;
+ #endif
+
+ return (final_scancode);
+}
+
+static int
+akbd_check_char(keyboard_t *kbd)
+{
+ if (!KBD_IS_ACTIVE(kbd))
+ return (FALSE);
+
+ return (akbd_check(kbd));
+}
+
+static int
+set_typematic(keyboard_t *kbd, int code)
+{
+ /* These numbers are in microseconds, so convert to ticks */
+
+ static int delays[] = { 250, 500, 750, 1000 };
+ static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504 };
+
+ if (code & ~0x7f)
+ return EINVAL;
+ kbd->kb_delay1 = delays[(code >> 5) & 3];
+ kbd->kb_delay2 = rates[code & 0x1f];
+ return 0;
+}
+
+static int akbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t data)
+{
+ struct adb_kbd_softc *sc;
+ uint16_t r2;
+ int error;
+
+ sc = (struct adb_kbd_softc *)(kbd);
+ error = 0;
+
+ switch (cmd) {
+ case KDGKBMODE:
+ *(int *)data = sc->sc_mode;
+ break;
+ case KDSKBMODE:
+ switch (*(int *)data) {
+ case K_XLATE:
+ if (sc->sc_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALLTHROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (sc->sc_mode != *(int *)data)
+ sc->sc_mode = *(int *)data;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ break;
+
+ case KDGETLED:
+ *(int *)data = KBD_LED_VAL(kbd);
+ break;
+
+ case KDSKBSTATE:
+ if (*(int *)data & ~LOCK_MASK) {
+ error = EINVAL;
+ break;
+ }
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= *(int *)data;
+
+ /* FALLTHROUGH */
+
+ case KDSETLED:
+ KBD_LED_VAL(kbd) = *(int *)data;
+
+ if (!sc->have_led_control)
+ break;
+
+ r2 = (~0 & 0x04) | 3;
+
+ if (*(int *)data & NLKED)
+ r2 &= ~1;
+ if (*(int *)data & CLKED)
+ r2 &= ~2;
+ if (*(int *)data & SLKED)
+ r2 &= ~4;
+
+ adb_send_packet(sc->sc_dev,ADB_COMMAND_LISTEN,2,
+ sizeof(uint16_t),(u_char *)&r2);
+
+ break;
+
+ case KDGKBSTATE:
+ *(int *)data = sc->sc_state & LOCK_MASK;
+ break;
+
+ case KDSETREPEAT:
+ if (!KBD_HAS_DEVICE(kbd))
+ return 0;
+ if (((int *)data)[1] < 0)
+ return EINVAL;
+ if (((int *)data)[0] < 0)
+ return EINVAL;
+ else if (((int *)data)[0] == 0) /* fastest possible value */
+ kbd->kb_delay1 = 200;
+ else
+ kbd->kb_delay1 = ((int *)data)[0];
+ kbd->kb_delay2 = ((int *)data)[1];
+
+ break;
+
+ case KDSETRAD:
+ error = set_typematic(kbd, *(int *)data);
+ break;
+
+ case PIO_KEYMAP:
+ case PIO_KEYMAPENT:
+ case PIO_DEADKEYMAP:
+ default:
+ return (genkbd_commonioctl(kbd, cmd, data));
+ }
+
+ return (error);
+}
+
+static int akbd_lock(keyboard_t *kbd, int lock)
+{
+ return (0);
+}
+
+static void akbd_clear_state(keyboard_t *kbd)
+{
+}
+
+static int akbd_get_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ return (0);
+}
+
+static int akbd_set_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ return (0);
+}
+
+static int akbd_poll(keyboard_t *kbd, int on)
+{
+ return (0);
+}
+
+static int
+akbd_modevent(module_t mod, int type, void *data)
+{
+ switch (type) {
+ case MOD_LOAD:
+ kbd_add_driver(&akbd_kbd_driver);
+ break;
+
+ case MOD_UNLOAD:
+ kbd_delete_driver(&akbd_kbd_driver);
+ break;
+
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ return (0);
+}
+
+DEV_MODULE(akbd, akbd_modevent, NULL);
+
diff --git a/sys/dev/adb/adb_mouse.c b/sys/dev/adb/adb_mouse.c
new file mode 100644
index 0000000..8b9c7d1
--- /dev/null
+++ b/sys/dev/adb/adb_mouse.c
@@ -0,0 +1,539 @@
+/*-
+ * Copyright (C) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/mouse.h>
+#include <sys/poll.h>
+#include <sys/condvar.h>
+#include <sys/selinfo.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include "adb.h"
+
+#define CDEV_GET_SOFTC(x) devclass_get_softc(adb_mouse_devclass, minor(x) & 0x1f)
+
+static int adb_mouse_probe(device_t dev);
+static int adb_mouse_attach(device_t dev);
+static int adb_mouse_detach(device_t dev);
+
+static d_open_t ams_open;
+static d_close_t ams_close;
+static d_read_t ams_read;
+static d_ioctl_t ams_ioctl;
+static d_poll_t ams_poll;
+
+static u_int adb_mouse_receive_packet(device_t dev, u_char status,
+ u_char command, u_char reg, int len, u_char *data);
+
+struct adb_mouse_softc {
+ device_t sc_dev;
+
+ struct mtx sc_mtx;
+ struct cv sc_cv;
+
+ int extended;
+ uint16_t dpi;
+
+ mousehw_t hw;
+ mousemode_t mode;
+ u_char id[4];
+
+ int buttons;
+ int last_buttons;
+ int xdelta, ydelta;
+
+ int8_t packet[8];
+ size_t packet_read_len;
+
+ struct cdev *cdev;
+ struct selinfo rsel;
+};
+
+static device_method_t adb_mouse_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, adb_mouse_probe),
+ DEVMETHOD(device_attach, adb_mouse_attach),
+ DEVMETHOD(device_detach, adb_mouse_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* ADB interface */
+ DEVMETHOD(adb_receive_packet, adb_mouse_receive_packet),
+
+ { 0, 0 }
+};
+
+static driver_t adb_mouse_driver = {
+ "ams",
+ adb_mouse_methods,
+ sizeof(struct adb_mouse_softc),
+};
+
+static devclass_t adb_mouse_devclass;
+
+DRIVER_MODULE(ams, adb, adb_mouse_driver, adb_mouse_devclass, 0, 0);
+
+static struct cdevsw ams_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = 0,
+ .d_open = ams_open,
+ .d_close = ams_close,
+ .d_read = ams_read,
+ .d_ioctl = ams_ioctl,
+ .d_poll = ams_poll,
+ .d_name = "ams",
+};
+
+static int
+adb_mouse_probe(device_t dev)
+{
+ uint8_t type;
+
+ type = adb_get_device_type(dev);
+
+ if (type != ADB_DEVICE_MOUSE)
+ return (ENXIO);
+
+ device_set_desc(dev,"ADB Mouse");
+ return (0);
+}
+
+static int
+adb_mouse_attach(device_t dev)
+{
+ struct adb_mouse_softc *sc;
+ char *description = "Unknown Pointing Device";
+
+ size_t r1_len;
+ u_char r1[8];
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ mtx_init(&sc->sc_mtx,"ams",MTX_DEF,0);
+ cv_init(&sc->sc_cv,"ams");
+
+ sc->extended = 0;
+
+ sc->hw.buttons = 2;
+ sc->hw.iftype = MOUSE_IF_UNKNOWN;
+ sc->hw.type = MOUSE_UNKNOWN;
+ sc->hw.model = sc->hw.hwid = 0;
+
+ sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->mode.rate = -1;
+ sc->mode.resolution = 100;
+ sc->mode.accelfactor = 0;
+ sc->mode.level = 0;
+ sc->mode.packetsize = 5;
+
+ sc->buttons = 0;
+ sc->last_buttons = 0;
+ sc->packet_read_len = 0;
+
+ /* Try to switch to extended protocol */
+ adb_set_device_handler(dev,4);
+
+ switch(adb_get_device_handler(dev)) {
+ case 1:
+ sc->mode.resolution = 100;
+ break;
+ case 2:
+ sc->mode.resolution = 200;
+ break;
+ case 4:
+ adb_read_register(dev,1,&r1_len,r1);
+ if (r1_len < 8)
+ break;
+
+ sc->extended = 1;
+ memcpy(&sc->hw.hwid,r1,4);
+ sc->mode.resolution = (r1[4] << 8) | r1[5];
+
+ switch (r1[6]) {
+ case 0:
+ sc->hw.type = MOUSE_PAD;
+ description = "Tablet";
+ break;
+ case 1:
+ sc->hw.type = MOUSE_MOUSE;
+ description = "Mouse";
+ break;
+ case 2:
+ sc->hw.type = MOUSE_TRACKBALL;
+ description = "Trackball";
+ break;
+ }
+
+ sc->hw.buttons = r1[7];
+
+ device_printf(dev,"%d-button %d-dpi %s\n",
+ sc->hw.buttons, sc->mode.resolution,description);
+
+ /*
+ * Check for one of MacAlly's non-compliant 2-button mice.
+ * These claim to speak the extended mouse protocol, but
+ * instead speak the standard protocol and only when their
+ * handler is set to 0x42.
+ */
+
+ if (sc->hw.hwid == 0x4b4f4954) {
+ adb_set_device_handler(dev,0x42);
+
+ if (adb_get_device_handler(dev) == 0x42) {
+ device_printf(dev, "MacAlly 2-Button Mouse\n");
+ sc->extended = 0;
+ }
+ }
+
+ break;
+ }
+
+ sc->cdev = make_dev(&ams_cdevsw, device_get_unit(dev),
+ UID_ROOT, GID_OPERATOR, 0644, "ams%d",
+ device_get_unit(dev));
+
+ adb_set_autopoll(dev,1);
+
+ return (0);
+}
+
+static int
+adb_mouse_detach(device_t dev)
+{
+ struct adb_mouse_softc *sc;
+
+ adb_set_autopoll(dev,0);
+
+ sc = device_get_softc(dev);
+ destroy_dev(sc->cdev);
+
+ mtx_destroy(&sc->sc_mtx);
+ cv_destroy(&sc->sc_cv);
+
+ return (0);
+}
+
+static u_int
+adb_mouse_receive_packet(device_t dev, u_char status, u_char command,
+ u_char reg, int len, u_char *data)
+{
+ struct adb_mouse_softc *sc;
+ int i = 0;
+ int xdelta, ydelta;
+ int buttons;
+
+ sc = device_get_softc(dev);
+
+ if (command != ADB_COMMAND_TALK || reg != 0 || len < 2)
+ return (0);
+
+ ydelta = data[0] & 0x7f;
+ xdelta = data[1] & 0x7f;
+
+ buttons = 0;
+ buttons |= !(data[0] & 0x80);
+ buttons |= !(data[1] & 0x80) << 1;
+
+ if (sc->extended) {
+ for (i = 2; i < len && i < 5; i++) {
+ xdelta |= (data[i] & 0x07) << (3*i + 1);
+ ydelta |= (data[i] & 0x70) << (3*i - 3);
+
+ buttons |= !(data[i] & 0x08) << (2*i - 2);
+ buttons |= !(data[i] & 0x80) << (2*i - 1);
+ }
+ } else {
+ len = 2; /* Ignore extra data */
+ }
+
+ /* Do sign extension as necessary */
+ if (xdelta & (0x40 << 3*(len-2)))
+ xdelta |= 0xffffffc0 << 3*(len - 2);
+ if (ydelta & (0x40 << 3*(len-2)))
+ ydelta |= 0xffffffc0 << 3*(len - 2);
+
+ /*
+ * Some mice report high-numbered buttons on the wrong button number,
+ * so set the highest-numbered real button as pressed if there are
+ * mysterious high-numbered ones set.
+ */
+
+ if (buttons & ~((1 << sc->hw.buttons) - 1)) {
+ buttons |= 1 << (sc->hw.buttons - 1);
+ buttons &= (1 << sc->hw.buttons) - 1;
+ }
+
+ mtx_lock(&sc->sc_mtx);
+
+ /* Add in our new deltas, and take into account
+ Apple's opposite meaning for Y axis motion */
+
+ sc->xdelta += xdelta;
+ sc->ydelta -= ydelta;
+
+ sc->buttons = buttons;
+
+ mtx_unlock(&sc->sc_mtx);
+
+ cv_broadcast(&sc->sc_cv);
+ selwakeuppri(&sc->rsel, PZERO);
+
+ return (0);
+}
+
+static int
+ams_open(struct cdev *dev, int flag, int fmt, struct thread *p)
+{
+ struct adb_mouse_softc *sc;
+
+ sc = CDEV_GET_SOFTC(dev);
+ if (sc == NULL)
+ return (ENXIO);
+
+ mtx_lock(&sc->sc_mtx);
+ sc->packet_read_len = 0;
+ sc->xdelta = 0;
+ sc->ydelta = 0;
+ sc->buttons = 0;
+ mtx_unlock(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+ams_close(struct cdev *dev, int flag, int fmt, struct thread *p)
+{
+ struct adb_mouse_softc *sc;
+
+ sc = CDEV_GET_SOFTC(dev);
+
+ cv_broadcast(&sc->sc_cv);
+ selwakeuppri(&sc->rsel, PZERO);
+ return (0);
+}
+
+static int
+ams_poll(struct cdev *dev, int events, struct thread *p)
+{
+ struct adb_mouse_softc *sc;
+
+ sc = CDEV_GET_SOFTC(dev);
+ if (sc == NULL)
+ return (EIO);
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ mtx_lock(&sc->sc_mtx);
+
+ if (sc->xdelta == 0 && sc->ydelta == 0 &&
+ sc->buttons == sc->last_buttons) {
+ selrecord(p, &sc->rsel);
+ events = 0;
+ } else {
+ events &= (POLLIN | POLLRDNORM);
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+ }
+
+ return events;
+}
+
+static int
+ams_read(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct adb_mouse_softc *sc;
+ size_t len;
+ int8_t outpacket[8];
+
+ sc = CDEV_GET_SOFTC(dev);
+ if (sc == NULL)
+ return (EIO);
+
+ if (uio->uio_resid <= 0)
+ return (0);
+
+ mtx_lock(&sc->sc_mtx);
+
+ if (!sc->packet_read_len) {
+ if (sc->xdelta == 0 && sc->ydelta == 0 &&
+ sc->buttons == sc->last_buttons) {
+
+ if (flag & O_NONBLOCK) {
+ mtx_unlock(&sc->sc_mtx);
+ return EWOULDBLOCK;
+ }
+
+
+ /* Otherwise, block on new data */
+ cv_wait(&sc->sc_cv,&sc->sc_mtx);
+ }
+
+ sc->packet[0] = 1 << 7;
+ sc->packet[0] |= (!(sc->buttons & 1)) << 2;
+ sc->packet[0] |= (!(sc->buttons & 4)) << 1;
+ sc->packet[0] |= (!(sc->buttons & 2));
+
+ if (sc->xdelta > 127) {
+ sc->packet[1] = 127;
+ sc->packet[3] = sc->xdelta - 127;
+ } else if (sc->xdelta < -127) {
+ sc->packet[1] = -127;
+ sc->packet[3] = sc->xdelta + 127;
+ } else {
+ sc->packet[1] = sc->xdelta;
+ sc->packet[3] = 0;
+ }
+
+ if (sc->ydelta > 127) {
+ sc->packet[2] = 127;
+ sc->packet[4] = sc->ydelta - 127;
+ } else if (sc->ydelta < -127) {
+ sc->packet[2] = -127;
+ sc->packet[4] = sc->ydelta + 127;
+ } else {
+ sc->packet[2] = sc->ydelta;
+ sc->packet[4] = 0;
+ }
+
+ /* No Z movement */
+ sc->packet[5] = 0;
+ sc->packet[6] = 0;
+
+ sc->packet[7] = ~((uint8_t)(sc->buttons >> 3)) & 0x7f;
+
+
+ sc->last_buttons = sc->buttons;
+ sc->xdelta = 0;
+ sc->ydelta = 0;
+
+ sc->packet_read_len = sc->mode.packetsize;
+ }
+
+ len = (sc->packet_read_len > uio->uio_resid) ?
+ uio->uio_resid : sc->packet_read_len;
+
+ memcpy(outpacket,sc->packet +
+ (sc->mode.packetsize - sc->packet_read_len),len);
+ sc->packet_read_len -= len;
+
+ mtx_unlock(&sc->sc_mtx);
+
+ uiomove(outpacket,len,uio);
+
+ return (0);
+}
+
+
+static int
+ams_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
+ struct thread *p)
+{
+ struct adb_mouse_softc *sc;
+ mousemode_t mode;
+
+ sc = CDEV_GET_SOFTC(dev);
+ if (sc == NULL)
+ return (EIO);
+
+ switch (cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->hw;
+ break;
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->mode;
+ break;
+ case MOUSE_SETMODE:
+ mode = *(mousemode_t *)addr;
+ addr = (caddr_t)&mode.level;
+
+ /* Fallthrough */
+
+ case MOUSE_SETLEVEL:
+ if (*(int *)addr == -1)
+ break;
+ else if (*(int *)addr == 1) {
+ sc->mode.level = 1;
+ sc->mode.packetsize = 8;
+ break;
+ } else if (*(int *)addr == 0) {
+ sc->mode.level = 0;
+ sc->mode.packetsize = 5;
+ break;
+ }
+
+ return EINVAL;
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->mode.level;
+ break;
+
+ case MOUSE_GETSTATUS: {
+ mousestatus_t *status = (mousestatus_t *) addr;
+
+ mtx_lock(&sc->sc_mtx);
+
+ status->button = sc->buttons;
+ status->obutton = sc->last_buttons;
+
+ status->flags = status->button ^ status->obutton;
+
+ if (sc->xdelta != 0 || sc->ydelta)
+ status->flags |= MOUSE_POSCHANGED;
+ if (status->button != status->obutton)
+ status->flags |= MOUSE_BUTTONSCHANGED;
+
+ status->dx = sc->xdelta;
+ status->dy = sc->ydelta;
+ status->dz = 0;
+
+ sc->xdelta = 0;
+ sc->ydelta = 0;
+ sc->last_buttons = sc->buttons;
+
+ mtx_unlock(&sc->sc_mtx);
+
+ break; }
+ default:
+ return ENOTTY;
+ }
+
+ return (0);
+}
+
diff --git a/sys/dev/adb/adbvar.h b/sys/dev/adb/adbvar.h
new file mode 100644
index 0000000..da0965d
--- /dev/null
+++ b/sys/dev/adb/adbvar.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (C) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _POWERPC_ADBVAR_H_
+#define _POWERPC_ADBVAR_H_
+
+#include "adb.h"
+
+enum {
+ ADB_COMMAND_BUS_RESET = 0
+};
+
+
+struct adb_softc {
+ device_t sc_dev;
+
+ device_t parent;
+
+ struct mtx sc_sync_mtx;
+
+ volatile int sync_packet;
+ volatile int packet_reply;
+
+ uint16_t autopoll_mask;
+
+ uint8_t syncreg[8];
+
+ device_t children[16];
+ struct adb_devinfo devinfo[16];
+};
+
+#endif /* _POWERPC_ADBVAR_H_ */
diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC
index d1de70e..f339c9c 100644
--- a/sys/powerpc/conf/GENERIC
+++ b/sys/powerpc/conf/GENERIC
@@ -157,6 +157,11 @@ device fwe # Ethernet over FireWire (non-standard!)
# Misc
device powermac_nvram # Open Firmware configuration NVRAM
+# ADB support
+device adb
+device cuda
+device pmu
+
options KTR
options KTR_COMPILE=0xffffffff
#options KTR_MASK=KTR_SIG
diff --git a/sys/powerpc/powermac/cuda.c b/sys/powerpc/powermac/cuda.c
new file mode 100644
index 0000000..162c639
--- /dev/null
+++ b/sys/powerpc/powermac/cuda.c
@@ -0,0 +1,641 @@
+/*-
+ * Copyright (c) 2006 Michael Lorenz
+ * Copyright 2008 by Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/openfirm.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/intr_machdep.h>
+#include <machine/md_var.h>
+#include <machine/pio.h>
+#include <machine/resource.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <sys/rman.h>
+
+#include <dev/adb/adb.h>
+
+#include "cudavar.h"
+#include "viareg.h"
+
+/*
+ * MacIO interface
+ */
+static int cuda_probe(device_t);
+static int cuda_attach(device_t);
+static int cuda_detach(device_t);
+
+static u_int cuda_adb_send(device_t dev, u_char command_byte, int len,
+ u_char *data, u_char poll);
+static u_int cuda_adb_autopoll(device_t dev, uint16_t mask);
+static void cuda_poll(device_t dev);
+
+static device_method_t cuda_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cuda_probe),
+ DEVMETHOD(device_attach, cuda_attach),
+ DEVMETHOD(device_detach, cuda_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* bus interface, for ADB root */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* ADB bus interface */
+ DEVMETHOD(adb_hb_send_raw_packet, cuda_adb_send),
+ DEVMETHOD(adb_hb_controller_poll, cuda_poll),
+ DEVMETHOD(adb_hb_set_autopoll_mask, cuda_adb_autopoll),
+
+ { 0, 0 },
+};
+
+static driver_t cuda_driver = {
+ "cuda",
+ cuda_methods,
+ sizeof(struct cuda_softc),
+};
+
+static devclass_t cuda_devclass;
+
+DRIVER_MODULE(cuda, macio, cuda_driver, cuda_devclass, 0, 0);
+DRIVER_MODULE(adb, cuda, adb_driver, adb_devclass, 0, 0);
+
+static void cuda_intr(void *arg);
+static uint8_t cuda_read_reg(struct cuda_softc *sc, u_int offset);
+static void cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value);
+static void cuda_idle(struct cuda_softc *);
+static void cuda_tip(struct cuda_softc *);
+static void cuda_clear_tip(struct cuda_softc *);
+static void cuda_in(struct cuda_softc *);
+static void cuda_out(struct cuda_softc *);
+static void cuda_toggle_ack(struct cuda_softc *);
+static void cuda_ack_off(struct cuda_softc *);
+static int cuda_intr_state(struct cuda_softc *);
+
+static int
+cuda_probe(device_t dev)
+{
+ const char *type = ofw_bus_get_type(dev);
+
+ if (strcmp(type, "via-cuda") != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, CUDA_DEVSTR);
+ return (0);
+}
+
+static int
+cuda_attach(device_t dev)
+{
+ struct cuda_softc *sc;
+
+ volatile int i;
+ uint8_t reg;
+ phandle_t node,child;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ sc->sc_memrid = 0;
+ sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_memrid, RF_ACTIVE);
+
+ if (sc->sc_memr == NULL) {
+ device_printf(dev, "Could not alloc mem resource!\n");
+ return (ENXIO);
+ }
+
+ sc->sc_irqrid = 0;
+ sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqrid,
+ RF_ACTIVE);
+ if (sc->sc_irq == NULL) {
+ device_printf(dev, "could not allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE
+ | INTR_ENTROPY, NULL, cuda_intr, dev, &sc->sc_ih) != 0) {
+ device_printf(dev, "could not setup interrupt\n");
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
+ sc->sc_irq);
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mutex,"cuda",NULL,MTX_DEF | MTX_RECURSE);
+
+ sc->sc_sent = 0;
+ sc->sc_received = 0;
+ sc->sc_waiting = 0;
+ sc->sc_polling = 0;
+ sc->sc_state = CUDA_NOTREADY;
+ sc->sc_error = 0;
+ sc->sc_autopoll = 0;
+
+ /* Init CUDA */
+
+ reg = cuda_read_reg(sc, vDirB);
+ reg |= 0x30; /* register B bits 4 and 5: outputs */
+ cuda_write_reg(sc, vDirB, reg);
+
+ reg = cuda_read_reg(sc, vDirB);
+ reg &= 0xf7; /* register B bit 3: input */
+ cuda_write_reg(sc, vDirB, reg);
+
+ reg = cuda_read_reg(sc, vACR);
+ reg &= ~vSR_OUT; /* make sure SR is set to IN */
+ cuda_write_reg(sc, vACR, reg);
+
+ cuda_write_reg(sc, vACR, (cuda_read_reg(sc, vACR) | 0x0c) & ~0x10);
+
+ sc->sc_state = CUDA_IDLE; /* used by all types of hardware */
+
+ cuda_write_reg(sc, vIER, 0x84); /* make sure VIA interrupts are on */
+
+ cuda_idle(sc); /* reset ADB */
+
+ /* Reset CUDA */
+
+ i = cuda_read_reg(sc, vSR); /* clear interrupt */
+ cuda_write_reg(sc, vIER, 0x04); /* no interrupts while clearing */
+ cuda_idle(sc); /* reset state to idle */
+ DELAY(150);
+ cuda_tip(sc); /* signal start of frame */
+ DELAY(150);
+ cuda_toggle_ack(sc);
+ DELAY(150);
+ cuda_clear_tip(sc);
+ DELAY(150);
+ cuda_idle(sc); /* back to idle state */
+ i = cuda_read_reg(sc, vSR); /* clear interrupt */
+ cuda_write_reg(sc, vIER, 0x84); /* ints ok now */
+
+ /* Initialize child buses (ADB) */
+ node = ofw_bus_get_node(dev);
+
+ for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+ char name[32];
+
+ memset(name, 0, sizeof(name));
+ OF_getprop(child, "name", name, sizeof(name));
+
+ if (bootverbose)
+ device_printf(dev, "CUDA child <%s>\n",name);
+
+ if (strncmp(name, "adb", 4) == 0) {
+ sc->adb_bus = device_add_child(dev,"adb",-1);
+ }
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int cuda_detach(device_t dev) {
+ struct cuda_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
+ mtx_destroy(&sc->sc_mutex);
+
+ return (bus_generic_detach(dev));
+}
+
+static uint8_t
+cuda_read_reg(struct cuda_softc *sc, u_int offset) {
+ return (bus_read_1(sc->sc_memr, offset));
+}
+
+static void
+cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value) {
+ bus_write_1(sc->sc_memr, offset, value);
+}
+
+static void
+cuda_idle(struct cuda_softc *sc)
+{
+ uint8_t reg;
+
+ reg = cuda_read_reg(sc, vBufB);
+ reg |= (vPB4 | vPB5);
+ cuda_write_reg(sc, vBufB, reg);
+}
+
+static void
+cuda_tip(struct cuda_softc *sc)
+{
+ uint8_t reg;
+
+ reg = cuda_read_reg(sc, vBufB);
+ reg &= ~vPB5;
+ cuda_write_reg(sc, vBufB, reg);
+}
+
+static void
+cuda_clear_tip(struct cuda_softc *sc)
+{
+ uint8_t reg;
+
+ reg = cuda_read_reg(sc, vBufB);
+ reg |= vPB5;
+ cuda_write_reg(sc, vBufB, reg);
+}
+
+static void
+cuda_in(struct cuda_softc *sc)
+{
+ uint8_t reg;
+
+ reg = cuda_read_reg(sc, vACR);
+ reg &= ~vSR_OUT;
+ cuda_write_reg(sc, vACR, reg);
+}
+
+static void
+cuda_out(struct cuda_softc *sc)
+{
+ uint8_t reg;
+
+ reg = cuda_read_reg(sc, vACR);
+ reg |= vSR_OUT;
+ cuda_write_reg(sc, vACR, reg);
+}
+
+static void
+cuda_toggle_ack(struct cuda_softc *sc)
+{
+ uint8_t reg;
+
+ reg = cuda_read_reg(sc, vBufB);
+ reg ^= vPB4;
+ cuda_write_reg(sc, vBufB, reg);
+}
+
+static void
+cuda_ack_off(struct cuda_softc *sc)
+{
+ uint8_t reg;
+
+ reg = cuda_read_reg(sc, vBufB);
+ reg |= vPB4;
+ cuda_write_reg(sc, vBufB, reg);
+}
+
+static int
+cuda_intr_state(struct cuda_softc *sc)
+{
+ return ((cuda_read_reg(sc, vBufB) & vPB3) == 0);
+}
+
+static int
+cuda_send(void *cookie, int poll, int length, uint8_t *msg)
+{
+ struct cuda_softc *sc = cookie;
+ device_t dev = sc->sc_dev;
+
+ if (sc->sc_state == CUDA_NOTREADY)
+ return -1;
+
+ mtx_lock(&sc->sc_mutex);
+
+ if ((sc->sc_state == CUDA_IDLE) /*&&
+ ((cuda_read_reg(sc, vBufB) & vPB3) == vPB3)*/) {
+ /* fine */
+ } else {
+ if (sc->sc_waiting == 0) {
+ sc->sc_waiting = 1;
+ } else {
+ mtx_unlock(&sc->sc_mutex);
+ return -1;
+ }
+ }
+
+ sc->sc_error = 0;
+ memcpy(sc->sc_out, msg, length);
+ sc->sc_out_length = length;
+ sc->sc_sent = 0;
+
+ if (sc->sc_waiting != 1) {
+ DELAY(150);
+ sc->sc_state = CUDA_OUT;
+ cuda_out(sc);
+ cuda_write_reg(sc, vSR, sc->sc_out[0]);
+ cuda_ack_off(sc);
+ cuda_tip(sc);
+ }
+ sc->sc_waiting = 1;
+ mtx_unlock(&sc->sc_mutex);
+
+ if (sc->sc_polling || poll || cold) {
+ cuda_poll(dev);
+ }
+
+ return 0;
+}
+
+static void
+cuda_poll(device_t dev)
+{
+ struct cuda_softc *sc = device_get_softc(dev);
+
+ while ((sc->sc_state != CUDA_IDLE) ||
+ (cuda_intr_state(sc)) ||
+ (sc->sc_waiting == 1)) {
+ if ((cuda_read_reg(sc, vIFR) & vSR_INT) == vSR_INT)
+ cuda_intr(dev);
+ }
+}
+
+static void
+cuda_intr(void *arg)
+{
+ device_t dev;
+ struct cuda_softc *sc;
+
+ int i, ending, type, restart_send;
+ uint8_t reg;
+
+ dev = (device_t)arg;
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->sc_mutex);
+
+ restart_send = 0;
+ reg = cuda_read_reg(sc, vIFR);
+ cuda_write_reg(sc, vIFR, 0x7f); /* Clear interrupt */
+
+switch_start:
+ switch (sc->sc_state) {
+ case CUDA_IDLE:
+ /*
+ * This is an unexpected packet, so grab the first (dummy)
+ * byte, set up the proper vars, and tell the chip we are
+ * starting to receive the packet by setting the TIP bit.
+ */
+ sc->sc_in[1] = cuda_read_reg(sc, vSR);
+
+ if (cuda_intr_state(sc) == 0) {
+ /* must have been a fake start */
+
+ if (sc->sc_waiting) {
+ /* start over */
+ DELAY(150);
+ sc->sc_state = CUDA_OUT;
+ sc->sc_sent = 0;
+ cuda_out(sc);
+ cuda_write_reg(sc, vSR, sc->sc_out[1]);
+ cuda_ack_off(sc);
+ cuda_tip(sc);
+ }
+ break;
+ }
+
+ cuda_in(sc);
+ cuda_tip(sc);
+
+ sc->sc_received = 1;
+ sc->sc_state = CUDA_IN;
+ break;
+
+ case CUDA_IN:
+ sc->sc_in[sc->sc_received] = cuda_read_reg(sc, vSR);
+ ending = 0;
+
+ if (sc->sc_received > 255) {
+ /* bitch only once */
+ if (sc->sc_received == 256) {
+ device_printf(dev,"input overflow\n");
+ ending = 1;
+ }
+ } else
+ sc->sc_received++;
+
+ if (sc->sc_received > 3) {
+ if ((sc->sc_in[3] == CMD_IIC) &&
+ (sc->sc_received > (sc->sc_i2c_read_len + 4))) {
+ ending = 1;
+ }
+ }
+
+ /* intr off means this is the last byte (end of frame) */
+ if (cuda_intr_state(sc) == 0) {
+ ending = 1;
+ } else {
+ cuda_toggle_ack(sc);
+ }
+
+ if (ending == 1) { /* end of message? */
+ sc->sc_in[0] = sc->sc_received - 1;
+
+ /* reset vars and signal the end of this frame */
+ cuda_idle(sc);
+
+ /* check if we have a handler for this message */
+ type = sc->sc_in[1];
+
+ switch (type) {
+ case CUDA_ADB:
+ if (sc->sc_received > 4) {
+ adb_receive_raw_packet(sc->adb_bus,
+ sc->sc_in[2],sc->sc_in[3],
+ sc->sc_received - 4,&sc->sc_in[4]);
+ } else {
+ adb_receive_raw_packet(sc->adb_bus,
+ sc->sc_in[2],sc->sc_in[3],0,NULL);
+ }
+ break;
+ case CUDA_PSEUDO:
+ if (sc->sc_in[3] == CMD_AUTOPOLL)
+ sc->sc_autopoll = 1;
+ break;
+ case CUDA_ERROR:
+ device_printf(dev,"CUDA Error\n");
+ sc->sc_error = 1;
+ break;
+ default:
+ device_printf(dev,"unknown CUDA command %d\n",
+ type);
+ break;
+ }
+
+ sc->sc_state = CUDA_IDLE;
+
+ sc->sc_received = 0;
+
+ /*
+ * If there is something waiting to be sent out,
+ * set everything up and send the first byte.
+ */
+ if (sc->sc_waiting == 1) {
+ DELAY(1500); /* required */
+ sc->sc_sent = 0;
+ sc->sc_state = CUDA_OUT;
+
+ /*
+ * If the interrupt is on, we were too slow
+ * and the chip has already started to send
+ * something to us, so back out of the write
+ * and start a read cycle.
+ */
+ if (cuda_intr_state(sc)) {
+ cuda_in(sc);
+ cuda_idle(sc);
+ sc->sc_sent = 0;
+ sc->sc_state = CUDA_IDLE;
+ sc->sc_received = 0;
+ DELAY(150);
+ goto switch_start;
+ }
+ /*
+ * If we got here, it's ok to start sending
+ * so load the first byte and tell the chip
+ * we want to send.
+ */
+ cuda_out(sc);
+ cuda_write_reg(sc, vSR,
+ sc->sc_out[sc->sc_sent]);
+ cuda_ack_off(sc);
+ cuda_tip(sc);
+ }
+ }
+ break;
+
+ case CUDA_OUT:
+ i = cuda_read_reg(sc, vSR); /* reset SR-intr in IFR */
+
+ sc->sc_sent++;
+ if (cuda_intr_state(sc)) { /* ADB intr low during write */
+ cuda_in(sc); /* make sure SR is set to IN */
+ cuda_idle(sc);
+ sc->sc_sent = 0; /* must start all over */
+ sc->sc_state = CUDA_IDLE; /* new state */
+ sc->sc_received = 0;
+ sc->sc_waiting = 1; /* must retry when done with
+ * read */
+ DELAY(150);
+ goto switch_start; /* process next state right
+ * now */
+ break;
+ }
+ if (sc->sc_out_length == sc->sc_sent) { /* check for done */
+
+ sc->sc_waiting = 0; /* done writing */
+ sc->sc_state = CUDA_IDLE; /* signal bus is idle */
+ cuda_in(sc);
+ cuda_idle(sc);
+ } else {
+ /* send next byte */
+ cuda_write_reg(sc, vSR, sc->sc_out[sc->sc_sent]);
+ cuda_toggle_ack(sc); /* signal byte ready to
+ * shift */
+ }
+ break;
+
+ case CUDA_NOTREADY:
+ break;
+
+ default:
+ break;
+ }
+
+ mtx_unlock(&sc->sc_mutex);
+}
+
+static u_int
+cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll)
+{
+ struct cuda_softc *sc = device_get_softc(dev);
+ int i;
+ uint8_t packet[16];
+
+ /* construct an ADB command packet and send it */
+ packet[0] = CUDA_ADB;
+ packet[1] = command_byte;
+ for (i = 0; i < len; i++)
+ packet[i + 2] = data[i];
+
+ if (poll)
+ cuda_poll(dev);
+
+ cuda_send(sc, poll, len + 2, packet);
+
+ if (poll)
+ cuda_poll(dev);
+
+ return 0;
+}
+
+static u_int
+cuda_adb_autopoll(device_t dev, uint16_t mask) {
+ struct cuda_softc *sc = device_get_softc(dev);
+
+ uint8_t cmd[] = {CUDA_PSEUDO, CMD_AUTOPOLL, mask != 0};
+
+ mtx_lock(&sc->sc_mutex);
+ if (cmd[2] == sc->sc_autopoll) {
+ mtx_unlock(&sc->sc_mutex);
+ return 0;
+ }
+
+ while (sc->sc_state != CUDA_IDLE)
+ mtx_sleep(dev,&sc->sc_mutex,0,"cuda",1);
+
+ sc->sc_autopoll = -1;
+ mtx_unlock(&sc->sc_mutex);
+
+ cuda_send(sc, 0, 3, cmd);
+
+ mtx_lock(&sc->sc_mutex);
+ while(sc->sc_autopoll == -1) {
+ mtx_sleep(dev,&sc->sc_mutex,0,"cuda",100);
+ cuda_poll(dev);
+ }
+
+ mtx_unlock(&sc->sc_mutex);
+
+ return 0;
+}
+
diff --git a/sys/powerpc/powermac/cudavar.h b/sys/powerpc/powermac/cudavar.h
new file mode 100644
index 0000000..90f0eae
--- /dev/null
+++ b/sys/powerpc/powermac/cudavar.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2006 Michael Lorenz
+ * Copyright (c) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _POWERPC_CUDAVAR_H_
+#define _POWERPC_CUDAVAR_H_
+
+#define CUDA_DEVSTR "Apple CUDA I/O Controller"
+
+/* Cuda addresses */
+#define CUDA_ADB 0
+#define CUDA_PSEUDO 1
+#define CUDA_ERROR 2 /* error codes? */
+#define CUDA_TIMER 3
+#define CUDA_POWER 4
+#define CUDA_IIC 5 /* XXX ??? */
+#define CUDA_PMU 6
+#define CUDA_ADB_QUERY 7
+
+/* Cuda commands */
+#define CMD_AUTOPOLL 1
+#define CMD_READ_RTC 3
+#define CMD_WRITE_RTC 9
+#define CMD_POWEROFF 10
+#define CMD_RESET 17
+#define CMD_IIC 34
+
+/* Cuda state codes */
+#define CUDA_NOTREADY 0x1 /* has not been initialized yet */
+#define CUDA_IDLE 0x2 /* the bus is currently idle */
+#define CUDA_OUT 0x3 /* sending out a command */
+#define CUDA_IN 0x4 /* receiving data */
+#define CUDA_POLLING 0x5 /* polling - II only */
+
+struct cuda_softc {
+ device_t sc_dev;
+ int sc_memrid;
+ struct resource *sc_memr;
+ int sc_irqrid;
+ struct resource *sc_irq;
+ void *sc_ih;
+
+ struct mtx sc_mutex;
+
+ device_t adb_bus;
+
+ int sc_node;
+ volatile int sc_state;
+ int sc_waiting;
+ int sc_polling;
+ int sc_sent;
+ int sc_out_length;
+ int sc_received;
+ int sc_iic_done;
+ int sc_error;
+ volatile int sc_autopoll;
+
+ int sc_i2c_read_len;
+
+ /* internal buffers */
+ uint8_t sc_in[256];
+ uint8_t sc_out[256];
+};
+
+#endif /* _POWERPC_CUDAVAR_H_ */
diff --git a/sys/powerpc/powermac/macgpio.c b/sys/powerpc/powermac/macgpio.c
new file mode 100644
index 0000000..facb390
--- /dev/null
+++ b/sys/powerpc/powermac/macgpio.c
@@ -0,0 +1,340 @@
+/*-
+ * Copyright 2002 by Peter Grehan. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Driver for MacIO GPIO controller
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <machine/vmparam.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+
+#include <machine/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+/*
+ * Macgpio softc
+ */
+struct macgpio_softc {
+ phandle_t sc_node;
+ struct resource *sc_gpios;
+ int sc_gpios_rid;
+};
+
+static MALLOC_DEFINE(M_MACGPIO, "macgpio", "macgpio device information");
+
+static int macgpio_probe(device_t);
+static int macgpio_attach(device_t);
+static int macgpio_print_child(device_t dev, device_t child);
+static void macgpio_probe_nomatch(device_t, device_t);
+static struct resource *macgpio_alloc_resource(device_t, device_t, int, int *,
+ u_long, u_long, u_long, u_int);
+static int macgpio_activate_resource(device_t, device_t, int, int,
+ struct resource *);
+static int macgpio_deactivate_resource(device_t, device_t, int, int,
+ struct resource *);
+static ofw_bus_get_devinfo_t macgpio_get_devinfo;
+
+uint8_t macgpio_read(device_t dev);
+void macgpio_write(device_t dev,uint8_t);
+
+/*
+ * Bus interface definition
+ */
+static device_method_t macgpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, macgpio_probe),
+ DEVMETHOD(device_attach, macgpio_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, macgpio_print_child),
+ DEVMETHOD(bus_probe_nomatch, macgpio_probe_nomatch),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ DEVMETHOD(bus_alloc_resource, macgpio_alloc_resource),
+ DEVMETHOD(bus_activate_resource, macgpio_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, macgpio_deactivate_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_devinfo, macgpio_get_devinfo),
+ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
+ DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
+ DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
+ DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
+ DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
+
+ { 0, 0 }
+};
+
+static driver_t macgpio_pci_driver = {
+ "macgpio",
+ macgpio_methods,
+ sizeof(struct macgpio_softc)
+};
+
+devclass_t macgpio_devclass;
+
+DRIVER_MODULE(macgpio, macio, macgpio_pci_driver, macgpio_devclass, 0, 0);
+
+struct macgpio_devinfo {
+ struct ofw_bus_devinfo mdi_obdinfo;
+ struct resource_list mdi_resources;
+
+ int gpio_num;
+};
+
+static int
+macgpio_probe(device_t dev)
+{
+ const char *name;
+
+ name = ofw_bus_get_name(dev);
+ if (name && strcmp(name, "gpio") == 0) {
+ device_set_desc(dev, "MacIO GPIO Controller");
+ return (0);
+ }
+
+ return (ENXIO);
+}
+
+/*
+ * Scan Open Firmware child nodes, and attach these as children
+ * of the macgpio bus
+ */
+static int
+macgpio_attach(device_t dev)
+{
+ struct macgpio_softc *sc;
+ struct macgpio_devinfo *dinfo;
+ phandle_t root;
+ phandle_t child;
+ device_t cdev;
+ uint32_t irq;
+
+ sc = device_get_softc(dev);
+ root = sc->sc_node = ofw_bus_get_node(dev);
+
+ sc->sc_gpios = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_gpios_rid, RF_ACTIVE);
+
+ /*
+ * Iterate through the sub-devices
+ */
+ for (child = OF_child(root); child != 0; child = OF_peer(child)) {
+ dinfo = malloc(sizeof(*dinfo), M_MACGPIO, M_WAITOK | M_ZERO);
+ if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) !=
+ 0) {
+ free(dinfo, M_MACGPIO);
+ continue;
+ }
+
+ if (OF_getprop(child,"reg",&dinfo->gpio_num,
+ sizeof(dinfo->gpio_num)) != sizeof(dinfo->gpio_num)) {
+ free(dinfo, M_MACGPIO);
+ continue;
+ }
+
+ resource_list_init(&dinfo->mdi_resources);
+
+ if (OF_getprop(child,"interrupts",&irq, sizeof(irq)) ==
+ sizeof(irq)) {
+ resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ,
+ 0, irq, irq, 1);
+ }
+
+ /* Fix messed-up offsets */
+ if (dinfo->gpio_num > 0x50)
+ dinfo->gpio_num -= 0x50;
+
+ cdev = device_add_child(dev, NULL, -1);
+ if (cdev == NULL) {
+ device_printf(dev, "<%s>: device_add_child failed\n",
+ dinfo->mdi_obdinfo.obd_name);
+ ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo);
+ free(dinfo, M_MACGPIO);
+ continue;
+ }
+ device_set_ivars(cdev, dinfo);
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+
+static int
+macgpio_print_child(device_t dev, device_t child)
+{
+ struct macgpio_devinfo *dinfo;
+ int retval = 0;
+
+ dinfo = device_get_ivars(child);
+
+ retval += bus_print_child_header(dev, child);
+
+ printf(" gpio %d",dinfo->gpio_num);
+ resource_list_print_type(&dinfo->mdi_resources, "irq", SYS_RES_IRQ,
+ "%ld");
+ retval += bus_print_child_footer(dev, child);
+
+ return (retval);
+}
+
+
+static void
+macgpio_probe_nomatch(device_t dev, device_t child)
+{
+ struct macgpio_devinfo *dinfo;
+ const char *type;
+
+ if (bootverbose) {
+ dinfo = device_get_ivars(child);
+
+ if ((type = ofw_bus_get_type(child)) == NULL)
+ type = "(unknown)";
+ device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child));
+ printf(" gpio %d",dinfo->gpio_num);
+ resource_list_print_type(&dinfo->mdi_resources, "irq",
+ SYS_RES_IRQ, "%ld");
+ printf(" (no driver attached)\n");
+ }
+}
+
+
+static struct resource *
+macgpio_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct macgpio_softc *sc;
+ struct macgpio_devinfo *dinfo;
+
+ sc = device_get_softc(bus);
+ dinfo = device_get_ivars(child);
+
+ if (type != SYS_RES_IRQ)
+ return (NULL);
+
+ return (resource_list_alloc(&dinfo->mdi_resources, bus, child, type,
+ rid, start, end, count, flags));
+}
+
+static int
+macgpio_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ struct macgpio_softc *sc;
+ struct macgpio_devinfo *dinfo;
+ u_char val;
+
+ sc = device_get_softc(bus);
+ dinfo = device_get_ivars(child);
+
+ if (type != SYS_RES_IRQ)
+ return ENXIO;
+
+ val = bus_read_1(sc->sc_gpios,dinfo->gpio_num);
+ val |= 0x80;
+ bus_write_1(sc->sc_gpios,dinfo->gpio_num,val);
+
+ return (bus_activate_resource(bus, type, rid, res));
+}
+
+
+static int
+macgpio_deactivate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ struct macgpio_softc *sc;
+ struct macgpio_devinfo *dinfo;
+ u_char val;
+
+ sc = device_get_softc(bus);
+ dinfo = device_get_ivars(child);
+
+ if (type != SYS_RES_IRQ)
+ return ENXIO;
+
+ val = bus_read_1(sc->sc_gpios,dinfo->gpio_num);
+ val &= ~0x80;
+ bus_write_1(sc->sc_gpios,dinfo->gpio_num,val);
+
+ return (bus_deactivate_resource(bus, type, rid, res));
+}
+
+uint8_t
+macgpio_read(device_t dev)
+{
+ struct macgpio_softc *sc;
+ struct macgpio_devinfo *dinfo;
+
+ sc = device_get_softc(device_get_parent(dev));
+ dinfo = device_get_ivars(dev);
+
+ return (bus_read_1(sc->sc_gpios,dinfo->gpio_num));
+}
+
+void
+macgpio_write(device_t dev, uint8_t val)
+{
+ struct macgpio_softc *sc;
+ struct macgpio_devinfo *dinfo;
+
+ sc = device_get_softc(device_get_parent(dev));
+ dinfo = device_get_ivars(dev);
+
+ bus_write_1(sc->sc_gpios,dinfo->gpio_num,val);
+}
+
+static const struct ofw_bus_devinfo *
+macgpio_get_devinfo(device_t dev, device_t child)
+{
+ struct macgpio_devinfo *dinfo;
+
+ dinfo = device_get_ivars(child);
+ return (&dinfo->mdi_obdinfo);
+}
+
diff --git a/sys/powerpc/powermac/macgpiovar.h b/sys/powerpc/powermac/macgpiovar.h
new file mode 100644
index 0000000..0544bbc
--- /dev/null
+++ b/sys/powerpc/powermac/macgpiovar.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright 2008 by Nathan Whitehorn. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MACIO_MACGPIOVAR_H_
+#define _MACIO_MACGPIOVAR_H_
+
+struct macgpio_devinfo {
+ struct ofw_bus_devinfo mdi_obdinfo;
+ struct resource_list mdi_resources;
+
+ int gpio_num;
+};
+
+
+uint8_t macgpio_read(device_t dev);
+void macgpio_write(device_t dev,uint8_t);
+
+#endif /* _MACIO_MACGPIOVAR_H_ */
diff --git a/sys/powerpc/powermac/macio.c b/sys/powerpc/powermac/macio.c
index b0f04e0..9a4b3ac 100644
--- a/sys/powerpc/powermac/macio.c
+++ b/sys/powerpc/powermac/macio.c
@@ -430,6 +430,11 @@ macio_alloc_resource(device_t bus, device_t child, int type, int *rid,
break;
case SYS_RES_IRQ:
+ /* Check for passthrough from subattachments like macgpio */
+ if (device_get_parent(child) != bus)
+ return BUS_ALLOC_RESOURCE(device_get_parent(bus), child,
+ type, rid, start, end, count, flags);
+
rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_IRQ,
*rid);
if (rle == NULL) {
diff --git a/sys/powerpc/powermac/pmu.c b/sys/powerpc/powermac/pmu.c
new file mode 100644
index 0000000..a5a5450
--- /dev/null
+++ b/sys/powerpc/powermac/pmu.c
@@ -0,0 +1,617 @@
+/*-
+ * Copyright (c) 2006 Michael Lorenz
+ * Copyright 2008 by Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/openfirm.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/intr_machdep.h>
+#include <machine/md_var.h>
+#include <machine/pio.h>
+#include <machine/resource.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <sys/rman.h>
+
+#include <dev/adb/adb.h>
+
+#include "pmuvar.h"
+#include "viareg.h"
+
+/*
+ * MacIO interface
+ */
+static int pmu_probe(device_t);
+static int pmu_attach(device_t);
+static int pmu_detach(device_t);
+
+static u_int pmu_adb_send(device_t dev, u_char command_byte, int len,
+ u_char *data, u_char poll);
+static u_int pmu_adb_autopoll(device_t dev, uint16_t mask);
+static void pmu_poll(device_t dev);
+
+static device_method_t pmu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pmu_probe),
+ DEVMETHOD(device_attach, pmu_attach),
+ DEVMETHOD(device_detach, pmu_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* bus interface, for ADB root */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* ADB bus interface */
+ DEVMETHOD(adb_hb_send_raw_packet, pmu_adb_send),
+ DEVMETHOD(adb_hb_controller_poll, pmu_poll),
+ DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll),
+
+ { 0, 0 },
+};
+
+static driver_t pmu_driver = {
+ "pmu",
+ pmu_methods,
+ sizeof(struct pmu_softc),
+};
+
+static devclass_t pmu_devclass;
+
+DRIVER_MODULE(pmu, macio, pmu_driver, pmu_devclass, 0, 0);
+DRIVER_MODULE(adb, pmu, adb_driver, adb_devclass, 0, 0);
+
+static int pmuextint_probe(device_t);
+static int pmuextint_attach(device_t);
+
+static device_method_t pmuextint_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pmuextint_probe),
+ DEVMETHOD(device_attach, pmuextint_attach),
+
+ {0,0}
+};
+
+static driver_t pmuextint_driver = {
+ "pmuextint",
+ pmuextint_methods,
+ 0
+};
+
+static devclass_t pmuextint_devclass;
+
+DRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, pmuextint_devclass, 0, 0);
+
+/* Make sure uhid is loaded, as it turns off some of the ADB emulation */
+MODULE_DEPEND(pmu, usb, 1, 1, 1);
+
+static void pmu_intr(void *arg);
+static void pmu_in(struct pmu_softc *sc);
+static void pmu_out(struct pmu_softc *sc);
+static void pmu_ack_on(struct pmu_softc *sc);
+static void pmu_ack_off(struct pmu_softc *sc);
+static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg,
+ int rlen, uint8_t *out_msg);
+static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset);
+static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value);
+static int pmu_intr_state(struct pmu_softc *);
+
+/* these values shows that number of data returned after 'send' cmd is sent */
+static signed char pm_send_cmd_type[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x01, 0x01, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, -1, -1, -1, -1, -1, 0x00,
+ -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1,
+ 0x00, -1, -1, -1, -1, -1, -1, -1,
+ 0x04, 0x14, -1, 0x03, -1, -1, -1, -1,
+ 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1,
+ 0x01, 0x01, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, -1, -1, 0x01, -1, -1, -1,
+ 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1,
+ 0x02, -1, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1,
+ 0x01, 0x01, 0x01, -1, -1, -1, -1, -1,
+ 0x00, 0x00, -1, -1, -1, -1, 0x04, 0x04,
+ 0x04, -1, 0x00, -1, -1, -1, -1, -1,
+ 0x00, -1, -1, -1, -1, -1, -1, -1,
+ 0x01, 0x02, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, -1, -1, -1, -1, -1, -1,
+ 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1,
+ 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00, -1, -1, -1, -1, -1, -1, -1,
+ 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1,
+ -1, 0x04, 0x00, -1, -1, -1, -1, -1,
+ 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/* these values shows that number of data returned after 'receive' cmd is sent */
+static signed char pm_receive_cmd_type[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, -1, -1, -1, -1, -1, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x15, -1, 0x02, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ -1, -1, -1, -1, -1, -1, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, -1, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, -1, -1, 0x02, -1, -1, -1,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ -1, -1, 0x02, -1, -1, -1, -1, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+/* We only have one of each device, so globals are safe */
+static device_t pmu = NULL;
+static device_t pmu_extint = NULL;
+
+static int
+pmuextint_probe(device_t dev)
+{
+ const char *type = ofw_bus_get_type(dev);
+
+ if (strcmp(type, "extint-gpio1") != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Apple PMU99 External Interrupt");
+ return (0);
+}
+
+static int
+pmu_probe(device_t dev)
+{
+ const char *type = ofw_bus_get_type(dev);
+
+ if (strcmp(type, "via-pmu") != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Apple PMU99 Controller");
+ return (0);
+}
+
+
+static int
+setup_pmu_intr(device_t dev, device_t extint)
+{
+ struct pmu_softc *sc;
+ sc = device_get_softc(dev);
+
+ sc->sc_irqrid = 0;
+ sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid,
+ RF_ACTIVE);
+ if (sc->sc_irq == NULL) {
+ device_printf(dev, "could not allocate interrupt\n");
+ return (ENXIO);
+ }
+
+ if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE
+ | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) {
+ device_printf(dev, "could not setup interrupt\n");
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
+ sc->sc_irq);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+pmuextint_attach(device_t dev)
+{
+ pmu_extint = dev;
+ if (pmu)
+ return (setup_pmu_intr(pmu,dev));
+
+ return (0);
+}
+
+static int
+pmu_attach(device_t dev)
+{
+ struct pmu_softc *sc;
+
+ uint8_t reg;
+ uint8_t cmd[2] = {2, 0};
+ uint8_t resp[16];
+ phandle_t node,child;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ sc->sc_memrid = 0;
+ sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_memrid, RF_ACTIVE);
+
+ mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE);
+
+ if (sc->sc_memr == NULL) {
+ device_printf(dev, "Could not alloc mem resource!\n");
+ return (ENXIO);
+ }
+
+ /*
+ * Our interrupt is attached to a GPIO pin. Depending on probe order,
+ * we may not have found it yet. If we haven't, it will find us, and
+ * attach our interrupt then.
+ */
+ pmu = dev;
+ if (pmu_extint != NULL) {
+ if (setup_pmu_intr(dev,pmu_extint) != 0)
+ return (ENXIO);
+ }
+
+ sc->sc_error = 0;
+ sc->sc_polling = 0;
+ sc->sc_autopoll = 0;
+
+ /* Init PMU */
+
+ reg = PMU_INT_TICK | PMU_INT_ADB | PMU_INT_PCEJECT | PMU_INT_SNDBRT;
+ reg |= PMU_INT_BATTERY;
+ reg |= PMU_INT_ENVIRONMENT;
+ pmu_send(sc, PMU_SET_IMASK, 1, &reg, 16, resp);
+
+ pmu_write_reg(sc, vIER, 0x90); /* make sure VIA interrupts are on */
+
+ pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp);
+ pmu_send(sc, PMU_GET_VERSION, 1, cmd, 16, resp);
+
+ /* Initialize child buses (ADB) */
+ node = ofw_bus_get_node(dev);
+
+ for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+ char name[32];
+
+ memset(name, 0, sizeof(name));
+ OF_getprop(child, "name", name, sizeof(name));
+
+ if (bootverbose)
+ device_printf(dev, "PMU child <%s>\n",name);
+
+ if (strncmp(name, "adb", 4) == 0) {
+ sc->adb_bus = device_add_child(dev,"adb",-1);
+ }
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+pmu_detach(device_t dev)
+{
+ struct pmu_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr);
+ mtx_destroy(&sc->sc_mutex);
+
+ return (bus_generic_detach(dev));
+}
+
+static uint8_t
+pmu_read_reg(struct pmu_softc *sc, u_int offset)
+{
+ return (bus_read_1(sc->sc_memr, offset));
+}
+
+static void
+pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value)
+{
+ bus_write_1(sc->sc_memr, offset, value);
+}
+
+static int
+pmu_send_byte(struct pmu_softc *sc, uint8_t data)
+{
+
+ pmu_out(sc);
+ pmu_write_reg(sc, vSR, data);
+ pmu_ack_off(sc);
+ /* wait for intr to come up */
+ /* XXX should add a timeout and bail if it expires */
+ do {} while (pmu_intr_state(sc) == 0);
+ pmu_ack_on(sc);
+ do {} while (pmu_intr_state(sc));
+ pmu_ack_on(sc);
+ return 0;
+}
+
+static inline int
+pmu_read_byte(struct pmu_softc *sc, uint8_t *data)
+{
+ volatile uint8_t scratch;
+ pmu_in(sc);
+ scratch = pmu_read_reg(sc, vSR);
+ pmu_ack_off(sc);
+ /* wait for intr to come up */
+ do {} while (pmu_intr_state(sc) == 0);
+ pmu_ack_on(sc);
+ do {} while (pmu_intr_state(sc));
+ *data = pmu_read_reg(sc, vSR);
+ return 0;
+}
+
+static int
+pmu_intr_state(struct pmu_softc *sc)
+{
+ return ((pmu_read_reg(sc, vBufB) & vPB3) == 0);
+}
+
+static int
+pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen,
+ uint8_t *out_msg)
+{
+ struct pmu_softc *sc = cookie;
+ int i, rcv_len = -1;
+ uint8_t out_len, intreg;
+
+ intreg = pmu_read_reg(sc, vIER);
+ intreg &= 0x10;
+ pmu_write_reg(sc, vIER, intreg);
+
+ /* wait idle */
+ do {} while (pmu_intr_state(sc));
+ sc->sc_error = 0;
+
+ /* send command */
+ pmu_send_byte(sc, cmd);
+
+ /* send length if necessary */
+ if (pm_send_cmd_type[cmd] < 0) {
+ pmu_send_byte(sc, length);
+ }
+
+ for (i = 0; i < length; i++) {
+ pmu_send_byte(sc, in_msg[i]);
+ }
+
+ /* see if there's data to read */
+ rcv_len = pm_receive_cmd_type[cmd];
+ if (rcv_len == 0)
+ goto done;
+
+ /* read command */
+ if (rcv_len == 1) {
+ pmu_read_byte(sc, out_msg);
+ goto done;
+ } else
+ out_msg[0] = cmd;
+ if (rcv_len < 0) {
+ pmu_read_byte(sc, &out_len);
+ rcv_len = out_len + 1;
+ }
+ for (i = 1; i < min(rcv_len, rlen); i++)
+ pmu_read_byte(sc, &out_msg[i]);
+
+done:
+ pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90);
+
+ return rcv_len;
+}
+
+
+static void
+pmu_poll(device_t dev)
+{
+ pmu_intr(dev);
+}
+
+static void
+pmu_in(struct pmu_softc *sc)
+{
+ uint8_t reg;
+
+ reg = pmu_read_reg(sc, vACR);
+ reg &= ~vSR_OUT;
+ reg |= 0x0c;
+ pmu_write_reg(sc, vACR, reg);
+}
+
+static void
+pmu_out(struct pmu_softc *sc)
+{
+ uint8_t reg;
+
+ reg = pmu_read_reg(sc, vACR);
+ reg |= vSR_OUT;
+ reg |= 0x0c;
+ pmu_write_reg(sc, vACR, reg);
+}
+
+static void
+pmu_ack_off(struct pmu_softc *sc)
+{
+ uint8_t reg;
+
+ reg = pmu_read_reg(sc, vBufB);
+ reg &= ~vPB4;
+ pmu_write_reg(sc, vBufB, reg);
+}
+
+static void
+pmu_ack_on(struct pmu_softc *sc)
+{
+ uint8_t reg;
+
+ reg = pmu_read_reg(sc, vBufB);
+ reg |= vPB4;
+ pmu_write_reg(sc, vBufB, reg);
+}
+
+static void
+pmu_intr(void *arg)
+{
+ device_t dev;
+ struct pmu_softc *sc;
+
+ unsigned int len;
+ uint8_t resp[16];
+ uint8_t junk[16];
+
+ dev = (device_t)arg;
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->sc_mutex);
+
+ pmu_write_reg(sc, vIFR, 0x90); /* Clear 'em */
+ len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp);
+
+ mtx_unlock(&sc->sc_mutex);
+
+ if ((len < 1) || (resp[1] == 0)) {
+ return;
+ }
+
+ if (resp[1] & PMU_INT_ADB) {
+ /*
+ * the PMU will turn off autopolling after each command that
+ * it did not issue, so we assume any but TALK R0 is ours and
+ * re-enable autopoll here whenever we receive an ACK for a
+ * non TR0 command.
+ */
+ mtx_lock(&sc->sc_mutex);
+
+ if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) {
+ if (sc->sc_autopoll) {
+ uint8_t cmd[] = {0, PMU_SET_POLL_MASK,
+ (sc->sc_autopoll >> 8) & 0xff,
+ sc->sc_autopoll & 0xff};
+
+ pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk);
+ }
+ }
+
+ mtx_unlock(&sc->sc_mutex);
+
+ adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2],
+ len - 3,&resp[3]);
+ }
+}
+
+static u_int
+pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data,
+ u_char poll)
+{
+ struct pmu_softc *sc = device_get_softc(dev);
+ int i,replen;
+ uint8_t packet[16], resp[16];
+
+ /* construct an ADB command packet and send it */
+
+ packet[0] = command_byte;
+
+ packet[1] = 0;
+ packet[2] = len;
+ for (i = 0; i < len; i++)
+ packet[i + 3] = data[i];
+
+ mtx_lock(&sc->sc_mutex);
+ replen = pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp);
+ mtx_unlock(&sc->sc_mutex);
+
+ if (poll)
+ pmu_poll(dev);
+
+ return 0;
+}
+
+static u_int
+pmu_adb_autopoll(device_t dev, uint16_t mask)
+{
+ struct pmu_softc *sc = device_get_softc(dev);
+
+ mask = 0xffff;
+
+ /* magical incantation to re-enable autopolling */
+ uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff};
+ uint8_t resp[16];
+
+ mtx_lock(&sc->sc_mutex);
+
+ if (sc->sc_autopoll == mask) {
+ mtx_unlock(&sc->sc_mutex);
+ return 0;
+ }
+
+ sc->sc_autopoll = mask & 0xffff;
+
+ if (mask)
+ pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp);
+ else
+ pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp);
+
+ mtx_unlock(&sc->sc_mutex);
+
+ return 0;
+}
diff --git a/sys/powerpc/powermac/pmuvar.h b/sys/powerpc/powermac/pmuvar.h
new file mode 100644
index 0000000..4ee0ff9
--- /dev/null
+++ b/sys/powerpc/powermac/pmuvar.h
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (c) 2006 Michael Lorenz
+ * Copyright (c) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef PMUVAR_H
+#define PMUVAR_H
+
+/* PMU commands */
+#define PMU_POWER_CTRL0 0x10 /* control power of some devices */
+#define PMU_POWER_CTRL 0x11 /* control power of some devices */
+
+#define PMU_POWER_OFF 0x7e /* Turn Power off */
+#define PMU_RESET_CPU 0xd0 /* Reset CPU */
+
+#define PMU_SET_RTC 0x30 /* Set realtime clock */
+#define PMU_READ_RTC 0x38 /* Read realtime clock */
+
+#define PMU_WRITE_PRAM 0x32 /* Write PRAM */
+#define PMU_READ_PRAM 0x3a /* Read PRAM */
+
+#define PMU_WRITE_NVRAM 0x33 /* Write NVRAM */
+#define PMU_READ_NVRAM 0x3b /* Read NVRAM */
+
+#define PMU_EJECT_PCMCIA 0x4c /* Eject PCMCIA slot */
+
+#define PMU_SET_BRIGHTNESS 0x41 /* Set backlight brightness */
+#define PMU_READ_BRIGHTNESS 0xd9 /* Read brightness button position */
+
+#define PMU_POWER_EVENTS 0x8f /* Send power-event commands to PMU */
+#define PMU_SYSTEM_READY 0xdf /* tell PMU we are awake */
+
+#define PMU_BATTERY_STATE 0x6b /* Read old battery state */
+#define PMU_SMART_BATTERY_STATE 0x6f /* Read battery state */
+
+#define PMU_ADB_CMD 0x20 /* Send ADB packet */
+#define PMU_ADB_POLL_OFF 0x21 /* Disable ADB auto-poll */
+#define PMU_SET_VOL 0x40 /* Set volume button position */
+#define PMU_GET_VOL 0x48 /* Get volume button position */
+#define PMU_SET_IMASK 0x70 /* Set interrupt mask */
+#define PMU_INT_ACK 0x78 /* Read interrupt bits */
+#define PMU_CPU_SPEED 0x7d /* Control CPU speed on some models */
+#define PMU_SLEEP 0x7f /* Put CPU to sleep */
+#define PMU_SET_POLL_MASK 0x86 /*
+ * 16bit mask enables autopolling per
+ * device
+ */
+#define PMU_I2C_CMD 0x9a /* i2c commands */
+#define PMU_GET_LID_STATE 0xdc /* Report lid state */
+#define PMU_GET_VERSION 0xea /* Identify thyself */
+
+/* Bits in PMU interrupt and interrupt mask bytes */
+#define PMU_INT_ADB_AUTO 0x04 /* ADB autopoll, when PMU_INT_ADB */
+#define PMU_INT_PCEJECT 0x04 /* PC-card eject buttons */
+#define PMU_INT_SNDBRT 0x08 /* sound/brightness up/down buttons */
+#define PMU_INT_ADB 0x10 /* ADB autopoll or reply data */
+#define PMU_INT_BATTERY 0x20
+#define PMU_INT_ENVIRONMENT 0x40
+#define PMU_INT_TICK 0x80 /* 1-second tick interrupt */
+
+/* Bits to use with the PMU_POWER_CTRL0 command */
+#define PMU_POW0_ON 0x80 /* OR this to power ON the device */
+#define PMU_POW0_OFF 0x00 /* leave bit 7 to 0 to power it OFF */
+#define PMU_POW0_HARD_DRIVE 0x04 /* wallstreet/lombard? */
+
+/* Bits to use with the PMU_POWER_CTRL command */
+#define PMU_POW_ON 0x80 /* OR this to power ON the device */
+#define PMU_POW_OFF 0x00 /* leave bit 7 to 0 to power it OFF */
+#define PMU_POW_BACKLIGHT 0x01 /* backlight power */
+#define PMU_POW_CHARGER 0x02 /* battery charger power */
+#define PMU_POW_IRLED 0x04 /* IR led power (on wallstreet) */
+#define PMU_POW_MEDIABAY 0x08 /* media bay power (wallstreet/lombard ?) */
+
+/* Bits from PMU_GET_LID_STATE or PMU_INT_ENVIRONMENT on core99 */
+#define PMU_ENV_LID_CLOSED 0x01 /* The lid is closed */
+
+/* PMU PMU_POWER_EVENTS commands */
+enum {
+ PMU_PWR_GET_POWERUP_EVENTS = 0x00,
+ PMU_PWR_SET_POWERUP_EVENTS = 0x01,
+ PMU_PWR_CLR_POWERUP_EVENTS = 0x02,
+ PMU_PWR_GET_WAKEUP_EVENTS = 0x03,
+ PMU_PWR_SET_WAKEUP_EVENTS = 0x04,
+ PMU_PWR_CLR_WAKEUP_EVENTS = 0x05,
+};
+
+/* PMU Power Information */
+
+#define PMU_PWR_AC_PRESENT (1 << 0)
+#define PMU_PWR_BATT_CHARGING (1 << 1)
+#define PMU_PWR_BATT_PRESENT (1 << 2)
+#define PMU_PWR_BATT_FULL (1 << 5)
+#define PMU_PWR_PCHARGE_RESET (1 << 6)
+#define PMU_PWR_BATT_EXIST (1 << 7)
+
+
+/* I2C related definitions */
+#define PMU_I2C_MODE_SIMPLE 0
+#define PMU_I2C_MODE_STDSUB 1
+#define PMU_I2C_MODE_COMBINED 2
+
+#define PMU_I2C_BUS_STATUS 0
+#define PMU_I2C_BUS_SYSCLK 1
+#define PMU_I2C_BUS_POWER 2
+
+#define PMU_I2C_STATUS_OK 0
+#define PMU_I2C_STATUS_DATAREAD 1
+#define PMU_I2C_STATUS_BUSY 0xfe
+
+/* Power events wakeup bits */
+enum {
+ PMU_PWR_WAKEUP_KEY = 0x01, /* Wake on key press */
+ PMU_PWR_WAKEUP_AC_INSERT = 0x02, /* Wake on AC adapter plug */
+ PMU_PWR_WAKEUP_AC_CHANGE = 0x04,
+ PMU_PWR_WAKEUP_LID_OPEN = 0x08,
+ PMU_PWR_WAKEUP_RING = 0x10,
+};
+
+#define PMU_NOTREADY 0x1 /* has not been initialized yet */
+#define PMU_IDLE 0x2 /* the bus is currently idle */
+#define PMU_OUT 0x3 /* sending out a command */
+#define PMU_IN 0x4 /* receiving data */
+
+struct pmu_softc {
+ device_t sc_dev;
+ int sc_memrid;
+ struct resource *sc_memr;
+ int sc_irqrid;
+ struct resource *sc_irq;
+ void *sc_ih;
+
+ struct mtx sc_mutex;
+
+ device_t adb_bus;
+
+ int sc_node;
+ volatile int sc_state;
+ int sc_polling;
+ int sc_error;
+ volatile int sc_autopoll;
+};
+
+#endif /* PMUVAR_H */
diff --git a/sys/powerpc/powermac/viareg.h b/sys/powerpc/powermac/viareg.h
new file mode 100644
index 0000000..85739cd
--- /dev/null
+++ b/sys/powerpc/powermac/viareg.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (C) 2008 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _POWERPC_VIAREG_H_
+#define _POWERPC_VIAREG_H_
+
+/* VIA interface registers */
+#define vBufB 0x0000 /* register B */
+#define vBufA 0x0200 /* register A */
+#define vDirB 0x0400 /* data direction register */
+#define vDirA 0x0600 /* data direction register */
+#define vSR 0x1400 /* shift register */
+#define vACR 0x1600 /* aux control register */
+#define vPCR 0x1800 /* peripheral control register */
+#define vIFR 0x1a00 /* interrupt flag register */
+#define vIER 0x1c00 /* interrupt enable register */
+
+#define vPB 0x0000
+#define vPB3 0x08
+#define vPB4 0x10
+#define vPB5 0x20
+#define vSR_INT 0x04
+#define vSR_OUT 0x10
+
+#endif /* _POWERPC_VIAREG_H_ */
OpenPOWER on IntegriCloud