patch-2.1.97 linux/drivers/macintosh/mediabay.c

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

diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/mediabay.c linux/drivers/macintosh/mediabay.c
@@ -0,0 +1,236 @@
+/*
+ * Driver for the media bay on the PowerBook 3400 and 2400.
+ *
+ * Copyright (C) 1998 Paul Mackerras.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/hdreg.h>
+#include <asm/prom.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/ohare.h>
+#include <asm/mediabay.h>
+
+struct media_bay_hw {
+	unsigned char	b0;
+	unsigned char	contents;
+	unsigned char	b2;
+	unsigned char	b3;
+	unsigned	feature;
+};
+
+static volatile struct media_bay_hw *mb_addr;
+
+#define MB_CONTENTS()	((in_8(&mb_addr->contents) >> 4) & 7)
+#define SET_FEATURES(set, clr) \
+	out_le32(&mb_addr->feature, \
+		 (in_le32(&mb_addr->feature) & ~(clr)) | (set));
+
+static int media_bay_id = -1;
+static int mb_ready;
+static int mb_last_value;
+static int mb_value_count;
+
+int media_bay_present;
+
+#ifdef CONFIG_BLK_DEV_IDE
+unsigned long mb_cd_base;
+int mb_cd_index = -1;
+int mb_cd_irq;
+
+/* check the busy bit in the media-bay ide interface
+   (assumes the media-bay contains an ide device) */
+#define MB_IDE_READY()	((in_8((volatile unsigned char *) \
+			       (mb_cd_base + 0x70)) & 0x80) == 0)
+#endif
+
+/*
+ * Consider the media-bay ID value stable if it is the same for
+ * this many consecutive samples (at intervals of 1/HZ seconds).
+ */
+#define MB_STABLE_COUNT	4
+
+/*
+ * Hold the media-bay reset signal true for this many ticks
+ * after a device is inserted before releasing it.
+ */
+#define MB_RESET_COUNT	10
+
+/*
+ * Wait this many ticks after an IDE device (e.g. CD-ROM) is inserted
+ * (or until the device is ready) before registering the IDE interface.
+ */
+#define MB_IDE_WAIT	500
+
+static void poll_media_bay(void);
+static void set_media_bay(int id);
+
+/*
+ * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
+ * register is always set when there is something in the media bay.
+ * This causes problems for the interrupt code if we attach an interrupt
+ * handler to the media-bay interrupt, because it tends to go into
+ * an infinite loop calling the media bay interrupt handler.
+ * Therefore we do it all by polling the media bay once each tick.
+ */
+
+void
+media_bay_init(void)
+{
+	struct device_node *np;
+
+	np = find_devices("media-bay");
+	if (np == NULL || np->n_addrs == 0)
+		return;
+	mb_addr = (volatile struct media_bay_hw *)
+		ioremap(np->addrs[0].address, sizeof(struct media_bay_hw));
+
+#if 0
+	if (np->n_intrs == 0) {
+		printk(KERN_WARNING "No interrupt for media bay?\n");
+	} else {
+		if (request_irq(np->intrs[0].line, media_bay_intr, 0,
+				"Media bay", NULL))
+			printk(KERN_WARNING "Couldn't get IRQ %d for "
+			       "media bay\n", np->intrs[0].line);
+	}
+#endif
+
+	media_bay_present = 1;
+	set_media_bay(MB_CONTENTS());
+	if (media_bay_id != MB_NO) {
+		SET_FEATURES(0, OH_BAY_RESET);
+		mb_ready = 1;
+	}
+}
+
+#if 0
+static void
+media_bay_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	int id = MB_CONTENTS();
+
+	if (id == MB_NO)
+		set_media_bay(id);
+}
+#endif
+
+int
+check_media_bay(int what)
+{
+	return what == media_bay_id && mb_ready;
+}
+
+/*
+ * This procedure runs as a kernel thread to poll the media bay
+ * once each tick and register and unregister the IDE interface
+ * with the IDE driver.  It needs to be a thread because
+ * ide_register can't be called from interrupt context.
+ */
+int
+media_bay_task(void *x)
+{
+	int prev = media_bay_id;
+	int reset_timer = 0;
+#ifdef CONFIG_BLK_DEV_IDE
+	int cd_timer = 0;
+#endif
+
+	strcpy(current->comm, "media-bay");
+	for (;;) {
+		poll_media_bay();
+		if (media_bay_id != prev) {
+			reset_timer = (media_bay_id != MB_NO)?
+				MB_RESET_COUNT: 0;
+			mb_ready = 0;
+#ifdef CONFIG_BLK_DEV_IDE
+			cd_timer = 0;
+			if (media_bay_id != MB_CD && mb_cd_index >= 0) {
+				printk(KERN_DEBUG "Unregistering mb ide\n");
+				ide_unregister(mb_cd_index);
+				mb_cd_index = -1;
+			}
+#endif
+		} else if (reset_timer) {
+			if (--reset_timer == 0) {
+				SET_FEATURES(0, OH_BAY_RESET);
+				mb_ready = 1;
+#ifdef CONFIG_BLK_DEV_IDE
+				if (media_bay_id == MB_CD && mb_cd_base != 0)
+					cd_timer = MB_IDE_WAIT;
+#endif
+			}
+#ifdef CONFIG_BLK_DEV_IDE
+		} else if (cd_timer && (--cd_timer == 0 || MB_IDE_READY())
+			   && mb_cd_index < 0) {
+			mb_cd_index = ide_register(mb_cd_base, 0, mb_cd_irq);
+			printk(KERN_DEBUG "media-bay is ide %d\n", mb_cd_index);
+#endif
+		}
+
+		prev = media_bay_id;
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 1;
+		schedule();
+		if (signal_pending(current))
+			return 0;
+	}
+}
+
+void
+poll_media_bay(void)
+{
+	int id = MB_CONTENTS();
+
+	if (id == mb_last_value) {
+		if (id != media_bay_id
+		    && ++mb_value_count >= MB_STABLE_COUNT)
+			set_media_bay(id);
+	} else {
+		mb_last_value = id;
+		mb_value_count = 0;
+	}
+}
+
+static void
+set_media_bay(int id)
+{
+	u32 clr, set;
+
+	media_bay_id = id;
+	mb_last_value = id;
+	clr = OH_FLOPPY_ENABLE | OH_IDECD_POWER;
+	set = 0;
+	switch (id) {
+	case MB_CD:
+		set = OH_BAY_ENABLE | OH_IDECD_POWER | OH_BAY_IDE_ENABLE;
+		printk(KERN_INFO "media bay contains a CD-ROM drive\n");
+		break;
+	case MB_FD:
+		set = OH_BAY_ENABLE | OH_BAY_FLOPPY_ENABLE | OH_FLOPPY_ENABLE;
+		printk(KERN_INFO "media bay contains a floppy disk drive\n");
+		break;
+	case MB_NO:
+		printk(KERN_INFO "media bay is empty\n");
+		break;
+	default:
+		set = OH_BAY_ENABLE;
+		printk(KERN_INFO "media bay contains an unknown device (%d)\n",
+		       id);
+		break;
+	}
+
+	SET_FEATURES(set, clr);
+	printk(KERN_DEBUG "feature reg now %x\n", in_le32(&mb_addr->feature));
+}

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