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

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

diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/mediabay.c linux/drivers/macintosh/mediabay.c
@@ -25,9 +25,13 @@
 #include <asm/prom.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/mediabay.h>
 #include <asm/sections.h>
+#include <asm/ohare.h>
+#include <asm/heathrow.h>
+#include <asm/keylargo.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
 
@@ -40,7 +44,7 @@
 #endif
 
 #undef MB_USE_INTERRUPTS
-#undef MB_DEBUG
+#define MB_DEBUG
 #define MB_IGNORE_SIGNALS
 
 #ifdef MB_DEBUG
@@ -49,24 +53,46 @@
 #define MBDBG(fmt, arg...)	do { } while (0)
 #endif
 
-struct media_bay_hw {
-	unsigned char	b0;
-	unsigned char	contents;
-	unsigned char	b2;
-	unsigned char	b3;
+/* Type of media bay */
+enum {
+	mb_ohare,
+	mb_heathrow,
+	mb_keylargo
+};
+
+#define MB_FCR32(bay, r)	((bay)->base + ((r) >> 2))
+#define MB_FCR8(bay, r)		(((volatile u8*)((bay)->base)) + (r))
+
+#define MB_IN32(bay,r)		(in_le32(MB_FCR32(bay,r)))
+#define MB_OUT32(bay,r,v)	(out_le32(MB_FCR32(bay,r), (v)))
+#define MB_BIS(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
+#define MB_BIC(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
+#define MB_IN8(bay,r)		(in_8(MB_FCR8(bay,r)))
+#define MB_OUT8(bay,r,v)	(out_8(MB_FCR8(bay,r), (v)))
+
+struct media_bay_info;
+
+struct mb_ops {
+	char*	name;
+	u8	(*content)(struct media_bay_info* bay);
+	void	(*power)(struct media_bay_info* bay, int on_off);
+	int	(*setup_bus)(struct media_bay_info* bay, u8 device_id);
+	void	(*un_reset)(struct media_bay_info* bay);
+	void	(*un_reset_ide)(struct media_bay_info* bay);
 };
 
 struct media_bay_info {
-	volatile struct media_bay_hw*	addr;
-	volatile u8*			extint_gpio;
+	volatile u32*			base;
 	int				content_id;
 	int				state;
 	int				last_value;
 	int				value_count;
 	int				timer;
 	struct device_node*		dev_node;
-	int				pismo;	/* New PowerBook3,1 */
-	int				gpio_cache;
+	int				mb_type;
+	struct mb_ops*			ops;
+	int				index;
+	int				cached_gpio;
 #ifdef CONFIG_BLK_DEV_IDE
 	unsigned long			cd_base;
 	int 				cd_index;
@@ -77,33 +103,12 @@
 
 #define MAX_BAYS	2
 
-static volatile struct media_bay_info media_bays[MAX_BAYS];
+static struct media_bay_info media_bays[MAX_BAYS];
 int media_bay_count = 0;
 
-inline int mb_content(volatile struct media_bay_info *bay)
-{
-        if (bay->pismo) {
-                unsigned char new_gpio = in_8(bay->extint_gpio + 0xe) & 2;
-                if (new_gpio) {
-                	bay->gpio_cache = new_gpio;
-                        return MB_NO;
-                } else if (bay->gpio_cache != new_gpio) {
-                        /* make sure content bits are set */
-                        feature_set(bay->dev_node, FEATURE_Mediabay_content);
-                        udelay(5);
-                        bay->gpio_cache = new_gpio;
-                }
-                return (in_le32((unsigned*)bay->addr) >> 4) & 0xf;
-        } else {
-                int cont = (in_8(&bay->addr->contents) >> 4) & 7;
-                return (cont == 7) ? MB_NO : cont;
-        }
-}
-
 #ifdef CONFIG_BLK_DEV_IDE
 /* check the busy bit in the media-bay ide interface
    (assumes the media-bay contains an ide device) */
-//#define MB_IDE_READY(i)	((inb(media_bays[i].cd_base + 0x70) & 0xc0) == 0x40)
 #define MB_IDE_READY(i)	((inb(media_bays[i].cd_base + 0x70) & 0x80) == 0)
 #endif
 
@@ -116,7 +121,7 @@
  * Consider the media-bay ID value stable if it is the same for
  * this number of milliseconds
  */
-#define MB_STABLE_DELAY	40
+#define MB_STABLE_DELAY	100
 
 /* Wait after powering up the media bay this delay in ms
  * timeout bumped for some powerbooks
@@ -166,175 +171,259 @@
 	mb_powering_down	/* Powering down (avoid too fast down/up) */
 };
 
-static void poll_media_bay(int which);
-static void set_media_bay(int which, int id);
-static void set_mb_power(int which, int onoff);
-static void media_bay_step(int i);
-static int media_bay_task(void *);
+#define MB_POWER_SOUND		0x08
+#define MB_POWER_FLOPPY		0x04
+#define MB_POWER_ATA		0x02
+#define MB_POWER_PCI		0x01
+#define MB_POWER_OFF		0x00
 
-#ifdef MB_USE_INTERRUPTS
-static void media_bay_intr(int irq, void *devid, struct pt_regs *regs);
-#endif
+/*
+ * Functions for polling content of media bay
+ */
+ 
+static u8 __pmac
+ohare_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+heathrow_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+keylargo_mb_content(struct media_bay_info *bay)
+{
+	int new_gpio;
+
+	new_gpio = MB_IN8(bay, KL_GPIO_MEDIABAY_IRQ) & KEYLARGO_GPIO_INPUT_DATA;
+	if (new_gpio) {
+		bay->cached_gpio = new_gpio;
+		return MB_NO;
+	} else if (bay->cached_gpio != new_gpio) {
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		bay->cached_gpio = new_gpio;
+	}
+	return (MB_IN32(bay, KEYLARGO_MBCR) >> 4) & 7;
+}
 
 /*
- * 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.
+ * Functions for powering up/down the bay, puts the bay device
+ * into reset state as well
  */
 
-void __pmac
-media_bay_init(void)
+static void __pmac
+ohare_mb_power(struct media_bay_info* bay, int on_off)
 {
-	struct device_node *np;
-	int		n,i;
-	
-	for (i=0; i<MAX_BAYS; i++) {
-		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
-		media_bays[i].content_id	= -1;
-#ifdef CONFIG_BLK_DEV_IDE
-		media_bays[i].cd_index		= -1;
-#endif
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIC(bay, OHARE_FCR, OH_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_DEV_MASK);
+		MB_BIC(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, OHARE_FCR, OH_BAY_POWER_N);
+		MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
 	}
-	
-	np = find_devices("media-bay");
-	n = 0;
-	while(np && (n<MAX_BAYS)) {
-		if (np->n_addrs == 0)
-			continue;
-		media_bays[n].addr = (volatile struct media_bay_hw *)
-			ioremap(np->addrs[0].address, sizeof(struct media_bay_hw));
+	MB_BIC(bay, OHARE_MBCR, 0x00000F00);
+}
 
-		media_bays[n].pismo = device_is_compatible(np, "keylargo-media-bay");
-		if (media_bays[n].pismo) {
-			if (!np->parent || strcmp(np->parent->name, "mac-io")) {
-				printk(KERN_ERR "Pismo media-bay has no mac-io parent !\n");
-				continue;
-			}
-			media_bays[n].extint_gpio = ioremap(np->parent->addrs[0].address
-				+ 0x58, 0x10);
-		}
+static void __pmac
+heathrow_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_DEV_MASK);
+		MB_BIC(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+	}
+	MB_BIC(bay, HEATHROW_MBCR, 0x00000F00);
+}
 
-#ifdef MB_USE_INTERRUPTS
-		if (np->n_intrs == 0) {
-			printk(KERN_ERR "media bay %d has no irq\n",n);
-			continue;
-		}
+static void __pmac
+keylargo_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+		MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+		MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+	}
+	MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+}
 
-		if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
-			printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
-				np->intrs[0].line, n);
-			continue;
-		}
-#endif	
-		media_bay_count++;
-	
-		media_bays[n].dev_node		= np;
+/*
+ * Functions for configuring the media bay for a given type of device,
+ * enable the related busses
+ */
 
-		/* Force an immediate detect */
-		set_mb_power(n,0);
-		mdelay(MB_POWER_DELAY);
-		if(!media_bays[n].pismo)
-			out_8(&media_bays[n].addr->contents, 0x70);
-		mdelay(MB_STABLE_DELAY);
-		media_bays[n].content_id = MB_NO;
-		media_bays[n].last_value = mb_content(&media_bays[n]);
-		media_bays[n].value_count = MS_TO_HZ(MB_STABLE_DELAY);
-		media_bays[n].state = mb_empty;
-		do {
-			mdelay(1000/HZ);
-			media_bay_step(n);
-		} while((media_bays[n].state != mb_empty) &&
-			(media_bays[n].state != mb_up));
+static int __pmac
+ohare_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, OHARE_FCR, OH_IDE1_RESET_N);
+			MB_BIS(bay, OHARE_FCR, OH_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_PCI_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
 
-		n++;
-		np=np->next;
+static int __pmac
+heathrow_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_PCI_ENABLE);
+			return 0;
 	}
+	return -ENODEV;
+}
 
-	if (media_bay_count)
-	{
-		printk(KERN_INFO "Registered %d media-bay(s)\n", media_bay_count);
+static int __pmac
+keylargo_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_CD:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
+			MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+			MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_PCI_ENABLE);
+			return 0;
+		case MB_SOUND:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_SOUND_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
 
-#ifdef CONFIG_PMAC_PBOOK
-		pmu_register_sleep_notifier(&mb_sleep_notifier);
-#endif /* CONFIG_PMAC_PBOOK */
+/*
+ * Functions for tweaking resets
+ */
 
-		kernel_thread(media_bay_task, NULL,
-			      CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-	}
+static void __pmac
+ohare_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
 }
 
-#ifdef MB_USE_INTERRUPTS
 static void __pmac
-media_bay_intr(int irq, void *devid, struct pt_regs *regs)
+heathrow_mb_un_reset(struct media_bay_info* bay)
 {
+	MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+}
+
+static void __pmac
+keylargo_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+}
+
+static void __pmac
+ohare_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
+}
+
+static void __pmac
+heathrow_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
 }
-#endif
 
 static void __pmac
-set_mb_power(int which, int onoff)
+keylargo_mb_un_reset_ide(struct media_bay_info* bay)
 {
-	volatile struct media_bay_info*	mb = &media_bays[which];
+	MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+}
 
+static inline void __pmac
+set_mb_power(struct media_bay_info* bay, int onoff)
+{
+	/* Power up up and assert the bay reset line */
 	if (onoff) {
-		feature_set(mb->dev_node, FEATURE_Mediabay_power);
-		udelay(10);
-		feature_set(mb->dev_node, FEATURE_Mediabay_reset);
-		udelay(10);
-		mb->state = mb_powering_up;
-		MBDBG("mediabay%d: powering up\n", which);
-	} else {
-		feature_clear(mb->dev_node, FEATURE_Mediabay_floppy_enable);
-		if (mb->pismo)
-			feature_clear(mb->dev_node, FEATURE_IDE0_enable);
-		else
-			feature_clear(mb->dev_node, FEATURE_IDE1_enable);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_IDE_switch);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_PCI_enable);
-		feature_clear(mb->dev_node, FEATURE_SWIM3_enable);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_power);
-		mb->state = mb_powering_down;
-		MBDBG("mediabay%d: powering down\n", which);
+		bay->ops->power(bay, 1);
+		bay->state = mb_powering_up;
+		MBDBG("mediabay%d: powering up\n", bay->index);
+	} else { 
+		/* Make sure everything is powered down & disabled */
+		bay->ops->power(bay, 0);
+		bay->state = mb_powering_down;
+		MBDBG("mediabay%d: powering down\n", bay->index);
 	}
-	mb->timer = MS_TO_HZ(MB_POWER_DELAY);
+	bay->timer = MS_TO_HZ(MB_POWER_DELAY);
 }
 
 static void __pmac
-set_media_bay(int which, int id)
+poll_media_bay(struct media_bay_info* bay)
 {
-	volatile struct media_bay_info* bay;
+	int id = bay->ops->content(bay);
 
-	bay = &media_bays[which];
-	
-	switch (id) {
-	case MB_CD:
-		if (bay->pismo) {
-			feature_set(bay->dev_node, FEATURE_Mediabay_IDE_switch);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE0_enable);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE0_reset);
-		} else {
-			feature_set(bay->dev_node, FEATURE_IDE1_enable);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE1_reset);
+	if (id == bay->last_value) {
+	    if (id != bay->content_id
+	        && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
+	        /* If the device type changes without going thru "MB_NO", we force
+	           a pass by "MB_NO" to make sure things are properly reset */
+	        if ((id != MB_NO) && (bay->content_id != MB_NO)) {
+	            id = MB_NO;
+		    MBDBG("mediabay%d: forcing MB_NO\n", bay->index);
 		}
-		printk(KERN_INFO "media bay %d contains a CD-ROM drive\n", which);
-		break;
-	case MB_FD:
-	case MB_FD1:
-		feature_set(bay->dev_node, FEATURE_Mediabay_floppy_enable);
-		feature_set(bay->dev_node, FEATURE_SWIM3_enable);
-		printk(KERN_INFO "media bay %d contains a floppy disk drive\n", which);
-		break;
-	case MB_NO:
-		break;
-	default:
-		printk(KERN_INFO "media bay %d contains an unknown device (%d)\n",
-		       which, id);
-		break;
+		MBDBG("mediabay%d: switching to %d\n", bay->index, id);
+		set_mb_power(bay, id != MB_NO);
+		bay->content_id = id;
+		if (id == MB_NO) {
+#ifdef CONFIG_BLK_DEV_IDE
+		    bay->cd_retry = 0;
+#endif
+		    printk(KERN_INFO "media bay %d is empty\n", bay->index);
+		}
+ 	    }
+	} else {
+		bay->last_value = id;
+		bay->value_count = 0;
 	}
 }
 
@@ -412,11 +501,11 @@
 static void __pmac
 media_bay_step(int i)
 {
-	volatile struct media_bay_info* bay = &media_bays[i];
+	struct media_bay_info* bay = &media_bays[i];
 
 	/* We don't poll when powering down */
 	if (bay->state != mb_powering_down)
-	    poll_media_bay(i);
+	    poll_media_bay(bay);
 
 	/* If timer expired or polling IDE busy, run state machine */
 	if ((bay->state != mb_ide_waiting) && (bay->timer != 0) && ((--bay->timer) != 0))
@@ -424,13 +513,17 @@
 
 	switch(bay->state) {
 	case mb_powering_up:
-	    	set_media_bay(i, bay->last_value);
+	    	if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
+			MBDBG("mediabay%d: device not supported (kind:%d)\n", i, bay->content_id);
+	    		set_mb_power(bay, 0);
+	    		break;
+	    	}
 	    	bay->timer = MS_TO_HZ(MB_RESET_DELAY);
 	    	bay->state = mb_enabling_bay;
 		MBDBG("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
 		break;
 	case mb_enabling_bay:
-	    	feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
+		bay->ops->un_reset(bay);
 	    	bay->timer = MS_TO_HZ(MB_SETUP_DELAY);
 	    	bay->state = mb_resetting;
 		MBDBG("mediabay%d: waiting reset (kind:%d)\n", i, bay->content_id);
@@ -444,16 +537,13 @@
 	    	}
 #ifdef CONFIG_BLK_DEV_IDE
 		MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id);
-		if (bay->pismo)
-	    		feature_clear(bay->dev_node, FEATURE_IDE0_reset);
-		else
-	    		feature_clear(bay->dev_node, FEATURE_IDE1_reset);
+		bay->ops->un_reset_ide(bay);
 	    	bay->timer = MS_TO_HZ(MB_IDE_WAIT);
 	    	bay->state = mb_ide_resetting;
 #else
 		printk(KERN_DEBUG "media-bay %d is ide (not compiled in kernel)\n", i);
-		set_mb_power(i, 0);
-#endif // #ifdef CONFIG_BLK_DEV_IDE
+		set_mb_power(bay, 0);
+#endif /* CONFIG_BLK_DEV_IDE */
 	    	break;
 	    
 #ifdef CONFIG_BLK_DEV_IDE
@@ -481,7 +571,7 @@
 				/* We eventually do a retry */
 				bay->cd_retry++;
 				printk("IDE register error\n");
-				set_mb_power(i, 0);
+				set_mb_power(bay, 0);
 			} else {
 				printk(KERN_DEBUG "media-bay %d is ide %d\n", i, bay->cd_index);
 				MBDBG("mediabay %d IDE ready\n", i);
@@ -491,10 +581,10 @@
 	    	if (bay->timer == 0) {
 			printk("\nIDE Timeout in bay %d !\n", i);
 			MBDBG("mediabay%d: nIDE Timeout !\n", i);
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 	    	}
 		break;
-#endif // #ifdef CONFIG_BLK_DEV_IDE
+#endif /* CONFIG_BLK_DEV_IDE */
 
 	case mb_powering_down:
 	    	bay->state = mb_empty;
@@ -514,7 +604,7 @@
 				bay->content_id = MB_NO;
 			}
 	    	}
-#endif	    
+#endif /* CONFIG_BLK_DEV_IDE */    
 		MBDBG("mediabay%d: end of power down\n", i);
 	    	break;
 	}
@@ -526,7 +616,7 @@
  * with the IDE driver.  It needs to be a thread because
  * ide_register can't be called from interrupt context.
  */
-int __pmac
+static int __pmac
 media_bay_task(void *x)
 {
 	int	i;
@@ -547,37 +637,12 @@
 	}
 }
 
-void __pmac
-poll_media_bay(int which)
+#ifdef MB_USE_INTERRUPTS
+static void __pmac
+media_bay_intr(int irq, void *devid, struct pt_regs *regs)
 {
-	volatile struct media_bay_info* bay = &media_bays[which];
-	int id = mb_content(bay);
-
-	if (id == bay->last_value) {
-	    if (id != bay->content_id
-	        && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
-	        /* If the device type changes without going thru "MB_NO", we force
-	           a pass by "MB_NO" to make sure things are properly reset */
-	        if ((id != MB_NO) && (bay->content_id != MB_NO)) {
-	            id = MB_NO;
-		    MBDBG("mediabay%d: forcing MB_NO\n", which);
-		}
-		MBDBG("mediabay%d: switching to %d\n", which, id);
-		set_mb_power(which, id != MB_NO);
-		bay->content_id = id;
-		if (id == MB_NO) {
-#ifdef CONFIG_BLK_DEV_IDE
-		    bay->cd_retry = 0;
-#endif
-		    printk(KERN_INFO "media bay %d is empty\n", which);
-		}
- 	    }
-	} else {
-		bay->last_value = id;
-		bay->value_count = 0;
-	}
 }
-
+#endif
 
 #ifdef CONFIG_PMAC_PBOOK
 /*
@@ -586,7 +651,7 @@
 int __pmac
 mb_notify_sleep(struct pmu_sleep_notifier *self, int when)
 {
-	volatile struct media_bay_info* bay;
+	struct media_bay_info* bay;
 	int i;
 	
 	switch (when) {
@@ -597,7 +662,7 @@
 	case PBOOK_SLEEP_NOW:
 		for (i=0; i<media_bay_count; i++) {
 			bay = &media_bays[i];
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 			mdelay(10);
 		}
 		break;
@@ -609,14 +674,11 @@
 			   they seem to help the 3400 get it right.
 			 */
 			/* Force MB power to 0 */
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 			mdelay(MB_POWER_DELAY);
-			if (!bay->pismo)
-				out_8(&bay->addr->contents, 0x70);
-			mdelay(MB_STABLE_DELAY);
-			if (mb_content(bay) != bay->content_id)
+			if (bay->ops->content(bay) != bay->content_id)
 				continue;
-			set_mb_power(i, 1);
+			set_mb_power(bay, 1);
 			bay->last_value = bay->content_id;
 			bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
 			bay->timer = MS_TO_HZ(MB_POWER_DELAY);
@@ -634,4 +696,135 @@
 	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PMAC_PBOOK */
+
+
+/* Definitions of "ops" structures.
+ */
+static struct mb_ops ohare_mb_ops __pmacdata = {
+	name:		"Ohare",
+	content:	ohare_mb_content,
+	power:		ohare_mb_power,
+	setup_bus:	ohare_mb_setup_bus,
+	un_reset:	ohare_mb_un_reset,
+	un_reset_ide:	ohare_mb_un_reset_ide,
+};
+
+static struct mb_ops heathrow_mb_ops __pmacdata = {
+	name:		"Heathrow",
+	content:	heathrow_mb_content,
+	power:		heathrow_mb_power,
+	setup_bus:	heathrow_mb_setup_bus,
+	un_reset:	heathrow_mb_un_reset,
+	un_reset_ide:	heathrow_mb_un_reset_ide,
+};
+
+static struct mb_ops keylargo_mb_ops __pmacdata = {
+	name:		"KeyLargo",
+	content:	keylargo_mb_content,
+	power:		keylargo_mb_power,
+	setup_bus:	keylargo_mb_setup_bus,
+	un_reset:	keylargo_mb_un_reset,
+	un_reset_ide:	keylargo_mb_un_reset_ide,
+};
+
+/*
+ * 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 __pmac
+media_bay_init(void)
+{
+	struct device_node *np;
+	int		n,i;
+	
+	for (i=0; i<MAX_BAYS; i++) {
+		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
+		media_bays[i].content_id	= -1;
+#ifdef CONFIG_BLK_DEV_IDE
+		media_bays[i].cd_index		= -1;
+#endif
+	}
+	
+	np = find_devices("media-bay");
+	n = 0;
+	while(np && (n<MAX_BAYS)) {
+		struct media_bay_info* bay = &media_bays[n];
+		if (!np->parent || np->n_addrs == 0 || !request_OF_resource(np, 0, NULL)) {
+			np = np->next;
+			printk(KERN_ERR "media-bay: Can't request IO resource !\n");
+			continue;
+		}
+		bay->mb_type = mb_ohare;
+
+		if (device_is_compatible(np, "keylargo-media-bay")) {
+			bay->mb_type = mb_keylargo;
+			bay->ops = &keylargo_mb_ops;
+		} else if (device_is_compatible(np, "heathrow-media-bay")) {
+			bay->mb_type = mb_heathrow;
+			bay->ops = &heathrow_mb_ops;
+		} else if (device_is_compatible(np, "ohare-media-bay")) {
+			bay->mb_type = mb_ohare;
+			bay->ops = &ohare_mb_ops;
+		} else {
+			printk(KERN_ERR "mediabay: Unknown bay type !\n");
+			np = np->next;
+			continue;
+		}
+		bay->base = (volatile u32*)ioremap(np->parent->addrs[0].address, 0x1000);
+
+		/* Enable probe logic on keylargo */
+		if (bay->mb_type == mb_keylargo)
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+#ifdef MB_USE_INTERRUPTS
+		if (np->n_intrs == 0) {
+			printk(KERN_ERR "media bay %d has no irq\n",n);
+			np = np->next;
+			continue;
+		}
+
+		if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
+			printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
+				np->intrs[0].line, n);
+			np = np->next;
+			continue;
+		}
+#endif	
+		media_bay_count++;
+	
+		printk(KERN_INFO "mediabay%d: Registered %s media-bay\n", n, bay->ops->name);
+		bay->dev_node = np;
+		bay->index = n;
+
+		/* Force an immediate detect */
+		set_mb_power(bay, 0);
+		mdelay(MB_POWER_DELAY);
+		bay->content_id = MB_NO;
+		bay->last_value = bay->ops->content(bay);
+		bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
+		bay->state = mb_empty;
+		do {
+			mdelay(1000/HZ);
+			media_bay_step(n);
+		} while((bay->state != mb_empty) &&
+			(bay->state != mb_up));
+
+		n++;
+		np=np->next;
+	}
+
+	if (media_bay_count)
+	{
+#ifdef CONFIG_PMAC_PBOOK
+		pmu_register_sleep_notifier(&mb_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+
+		kernel_thread(media_bay_task, NULL,
+			      CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	}
+}
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)