patch-2.1.79 linux/drivers/macintosh/macio-adb.c

Next file: linux/drivers/macintosh/mackeymap.c
Previous file: linux/drivers/macintosh/mac_keyb.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.78/linux/drivers/macintosh/macio-adb.c linux/drivers/macintosh/macio-adb.c
@@ -0,0 +1,211 @@
+/*
+ * Driver for the ADB controller in the Mac I/O (Hydra) chip.
+ */
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/prom.h>
+#include <asm/adb.h>
+#include <asm/io.h>
+#include <asm/hydra.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+struct preg {
+	unsigned char r;
+	char pad[15];
+};
+
+struct adb_regs {
+	struct preg intr;
+	struct preg data[9];
+	struct preg intr_enb;
+	struct preg dcount;
+	struct preg error;
+	struct preg ctrl;
+	struct preg autopoll;
+	struct preg active_hi;
+	struct preg active_lo;
+	struct preg test;
+};
+
+/* Bits in intr and intr_enb registers */
+#define DFB	1		/* data from bus */
+#define TAG	2		/* transfer access grant */
+
+/* Bits in dcount register */
+#define HMB	0x0f		/* how many bytes */
+#define APD	0x10		/* auto-poll data */
+
+/* Bits in error register */
+#define NRE	1		/* no response error */
+#define DLE	2		/* data lost error */
+
+/* Bits in ctrl register */
+#define TAR	1		/* transfer access request */
+#define DTB	2		/* data to bus */
+#define CRE	4		/* command response expected */
+#define ADB_RST	8		/* ADB reset */
+
+/* Bits in autopoll register */
+#define APE	1		/* autopoll enable */
+
+static volatile struct adb_regs *adb;
+static struct adb_request *current_req, *last_req;
+static unsigned char adb_rbuf[16];
+
+static void macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int macio_adb_send_request(struct adb_request *req, int sync);
+static int macio_adb_autopoll(int on);
+static void macio_adb_poll(void);
+static void completed(void);
+
+void macio_adb_init(void)
+{
+	struct device_node *adbs;
+
+	adbs = find_compatible_devices("adb", "chrp,adb0");
+	if (adbs == 0)
+		return;
+
+#if 1
+	{ int i;
+
+	printk("macio_adb_init: node = %p, addrs =", adbs->node);
+	for (i = 0; i < adbs->n_addrs; ++i)
+		printk(" %x(%x)", adbs->addrs[i].address, adbs->addrs[i].size);
+	printk(", intrs =");
+	for (i = 0; i < adbs->n_intrs; ++i)
+		printk(" %x", adbs->intrs[i]);
+	printk("\n"); }
+#endif
+	
+	adb = (volatile struct adb_regs *) adbs->addrs->address;
+
+	if (request_irq(openpic_to_irq(adbs->intrs[0]), macio_adb_interrupt,
+			0, "ADB", (void *)0)) {
+		printk(KERN_ERR "ADB: can't get irq %d\n",
+		       openpic_to_irq(adbs->intrs[0]));
+		return;
+	}
+
+	out_8(&adb->ctrl.r, 0);
+	out_8(&adb->intr.r, 0);
+	out_8(&adb->error.r, 0);
+	out_8(&adb->active_hi.r, 0xff); /* for now, set all devices active */
+	out_8(&adb->active_lo.r, 0xff);
+	out_8(&adb->autopoll.r, APE);
+	out_8(&adb->intr_enb.r, DFB | TAG);
+
+	adb_hardware = ADB_MACIO;
+	adb_send_request = macio_adb_send_request;
+	adb_autopoll = macio_adb_autopoll;
+}
+
+static int macio_adb_autopoll(int on)
+{
+	out_8(&adb->autopoll.r, on? APE: 0);
+	return 0;
+}
+
+
+/* Send an ADB command */
+static int macio_adb_send_request(struct adb_request *req, int sync)
+{
+	unsigned long mflags;
+
+	req->next = 0;
+	req->sent = 0;
+	req->complete = 0;
+	req->reply_len = 0;
+
+	save_flags(mflags);
+	cli();
+	if (current_req != 0) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = last_req = req;
+		out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
+	}
+	restore_flags(mflags);
+	
+	if (sync) {
+		while (!req->complete)
+			macio_adb_poll();
+	}
+
+	return 0;
+}
+
+static void macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	int i, n, err;
+	struct adb_request *req;
+
+	if (in_8(&adb->intr.r) & TAG) {
+		if ((req = current_req) != 0) {
+			/* put the current request in */
+			for (i = 0; i < req->nbytes; ++i)
+				out_8(&adb->data[i].r, req->data[i]);
+			out_8(&adb->dcount.r, req->nbytes & HMB);
+			req->sent = 1;
+			if (req->reply_expected) {
+				out_8(&adb->ctrl.r, DTB + CRE);
+			} else {
+				out_8(&adb->ctrl.r, DTB);
+				completed();
+			}
+		}
+		out_8(&adb->intr.r, 0);
+	}
+
+	if (in_8(&adb->intr.r) & DFB) {
+		err = in_8(&adb->error.r);
+		if (current_req && current_req->sent) {
+			/* this is the response to a command */
+			req = current_req;
+			if (err == 0) {
+				req->reply_len = in_8(&adb->dcount.r) & HMB;
+				for (i = 0; i < req->reply_len; ++i)
+					req->reply[i] = in_8(&adb->data[i].r);
+			}
+			completed();
+		} else if (err == 0) {
+			/* autopoll data */
+			n = in_8(&adb->dcount.r) & HMB;
+			for (i = 0; i < n; ++i)
+				adb_rbuf[i] = in_8(&adb->data[i].r);
+			adb_input(adb_rbuf, n, regs,
+				  in_8(&adb->dcount.r) & APD);
+		}
+		out_8(&adb->error.r, 0);
+		out_8(&adb->intr.r, 0);
+	}
+}
+
+static void completed(void)
+{
+	struct adb_request *req = current_req;
+
+	req->complete = 1;
+	current_req = req->next;
+	if (current_req)
+		out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
+	if (req->done)
+		(*req->done)(req);
+}
+
+static void macio_adb_poll(void)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (in_8(&adb->intr.r) != 0)
+		macio_adb_interrupt(0, 0, 0);
+	restore_flags(flags);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov