xref: /openbmc/linux/drivers/macintosh/mediabay.c (revision 306e352ab1b59335c56657f4d59cbe8edc040a8c)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Driver for the media bay on the PowerBook 3400 and 2400.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 1998 Paul Mackerras.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Various evolutions by Benjamin Herrenschmidt & Henry Worth
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *  This program is free software; you can redistribute it and/or
91da177e4SLinus Torvalds  *  modify it under the terms of the GNU General Public License
101da177e4SLinus Torvalds  *  as published by the Free Software Foundation; either version
111da177e4SLinus Torvalds  *  2 of the License, or (at your option) any later version.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds #include <linux/types.h>
141da177e4SLinus Torvalds #include <linux/errno.h>
151da177e4SLinus Torvalds #include <linux/kernel.h>
161da177e4SLinus Torvalds #include <linux/delay.h>
171da177e4SLinus Torvalds #include <linux/sched.h>
181da177e4SLinus Torvalds #include <linux/timer.h>
191da177e4SLinus Torvalds #include <linux/stddef.h>
201da177e4SLinus Torvalds #include <linux/init.h>
2133f6e794SPaul Mackerras #include <linux/kthread.h>
229a24729dSDaniel Walker #include <linux/mutex.h>
231da177e4SLinus Torvalds #include <asm/prom.h>
241da177e4SLinus Torvalds #include <asm/pgtable.h>
251da177e4SLinus Torvalds #include <asm/io.h>
261da177e4SLinus Torvalds #include <asm/machdep.h>
271da177e4SLinus Torvalds #include <asm/pmac_feature.h>
281da177e4SLinus Torvalds #include <asm/mediabay.h>
291da177e4SLinus Torvalds #include <asm/sections.h>
301da177e4SLinus Torvalds #include <asm/ohare.h>
311da177e4SLinus Torvalds #include <asm/heathrow.h>
321da177e4SLinus Torvalds #include <asm/keylargo.h>
331da177e4SLinus Torvalds #include <linux/adb.h>
341da177e4SLinus Torvalds #include <linux/pmu.h>
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #define MB_FCR32(bay, r)	((bay)->base + ((r) >> 2))
371da177e4SLinus Torvalds #define MB_FCR8(bay, r)		(((volatile u8 __iomem *)((bay)->base)) + (r))
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds #define MB_IN32(bay,r)		(in_le32(MB_FCR32(bay,r)))
401da177e4SLinus Torvalds #define MB_OUT32(bay,r,v)	(out_le32(MB_FCR32(bay,r), (v)))
411da177e4SLinus Torvalds #define MB_BIS(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
421da177e4SLinus Torvalds #define MB_BIC(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
431da177e4SLinus Torvalds #define MB_IN8(bay,r)		(in_8(MB_FCR8(bay,r)))
441da177e4SLinus Torvalds #define MB_OUT8(bay,r,v)	(out_8(MB_FCR8(bay,r), (v)))
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds struct media_bay_info;
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds struct mb_ops {
491da177e4SLinus Torvalds 	char*	name;
501da177e4SLinus Torvalds 	void	(*init)(struct media_bay_info *bay);
511da177e4SLinus Torvalds 	u8	(*content)(struct media_bay_info *bay);
521da177e4SLinus Torvalds 	void	(*power)(struct media_bay_info *bay, int on_off);
531da177e4SLinus Torvalds 	int	(*setup_bus)(struct media_bay_info *bay, u8 device_id);
541da177e4SLinus Torvalds 	void	(*un_reset)(struct media_bay_info *bay);
551da177e4SLinus Torvalds 	void	(*un_reset_ide)(struct media_bay_info *bay);
561da177e4SLinus Torvalds };
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds struct media_bay_info {
591da177e4SLinus Torvalds 	u32 __iomem			*base;
601da177e4SLinus Torvalds 	int				content_id;
611da177e4SLinus Torvalds 	int				state;
621da177e4SLinus Torvalds 	int				last_value;
631da177e4SLinus Torvalds 	int				value_count;
641da177e4SLinus Torvalds 	int				timer;
651da177e4SLinus Torvalds 	struct macio_dev		*mdev;
66519a6510SUwe Kleine-König 	const struct mb_ops*		ops;
671da177e4SLinus Torvalds 	int				index;
681da177e4SLinus Torvalds 	int				cached_gpio;
691da177e4SLinus Torvalds 	int				sleeping;
70d58b0c39SBenjamin Herrenschmidt 	int				user_lock;
719a24729dSDaniel Walker 	struct mutex			lock;
721da177e4SLinus Torvalds };
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds #define MAX_BAYS	2
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static struct media_bay_info media_bays[MAX_BAYS];
77d58b0c39SBenjamin Herrenschmidt static int media_bay_count = 0;
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds /*
801da177e4SLinus Torvalds  * Wait that number of ms between each step in normal polling mode
811da177e4SLinus Torvalds  */
821da177e4SLinus Torvalds #define MB_POLL_DELAY	25
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds /*
851da177e4SLinus Torvalds  * Consider the media-bay ID value stable if it is the same for
861da177e4SLinus Torvalds  * this number of milliseconds
871da177e4SLinus Torvalds  */
881da177e4SLinus Torvalds #define MB_STABLE_DELAY	100
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds /* Wait after powering up the media bay this delay in ms
911da177e4SLinus Torvalds  * timeout bumped for some powerbooks
921da177e4SLinus Torvalds  */
931da177e4SLinus Torvalds #define MB_POWER_DELAY	200
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds /*
961da177e4SLinus Torvalds  * Hold the media-bay reset signal true for this many ticks
971da177e4SLinus Torvalds  * after a device is inserted before releasing it.
981da177e4SLinus Torvalds  */
991da177e4SLinus Torvalds #define MB_RESET_DELAY	50
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds /*
1021da177e4SLinus Torvalds  * Wait this long after the reset signal is released and before doing
1031da177e4SLinus Torvalds  * further operations. After this delay, the IDE reset signal is released
1041da177e4SLinus Torvalds  * too for an IDE device
1051da177e4SLinus Torvalds  */
1061da177e4SLinus Torvalds #define MB_SETUP_DELAY	100
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds /*
1091da177e4SLinus Torvalds  * Wait this many ticks after an IDE device (e.g. CD-ROM) is inserted
110d58b0c39SBenjamin Herrenschmidt  * (or until the device is ready) before calling into the driver
1111da177e4SLinus Torvalds  */
1121da177e4SLinus Torvalds #define MB_IDE_WAIT	1000
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds /*
1151da177e4SLinus Torvalds  * States of a media bay
1161da177e4SLinus Torvalds  */
1171da177e4SLinus Torvalds enum {
1181da177e4SLinus Torvalds 	mb_empty = 0,		/* Idle */
1191da177e4SLinus Torvalds 	mb_powering_up,		/* power bit set, waiting MB_POWER_DELAY */
1201da177e4SLinus Torvalds 	mb_enabling_bay,	/* enable bits set, waiting MB_RESET_DELAY */
1211da177e4SLinus Torvalds 	mb_resetting,		/* reset bit unset, waiting MB_SETUP_DELAY */
1221da177e4SLinus Torvalds 	mb_ide_resetting,	/* IDE reset bit unser, waiting MB_IDE_WAIT */
1231da177e4SLinus Torvalds 	mb_up,			/* Media bay full */
1241da177e4SLinus Torvalds 	mb_powering_down	/* Powering down (avoid too fast down/up) */
1251da177e4SLinus Torvalds };
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds #define MB_POWER_SOUND		0x08
1281da177e4SLinus Torvalds #define MB_POWER_FLOPPY		0x04
1291da177e4SLinus Torvalds #define MB_POWER_ATA		0x02
1301da177e4SLinus Torvalds #define MB_POWER_PCI		0x01
1311da177e4SLinus Torvalds #define MB_POWER_OFF		0x00
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds /*
1341da177e4SLinus Torvalds  * Functions for polling content of media bay
1351da177e4SLinus Torvalds  */
1361da177e4SLinus Torvalds 
137aacaf9bdSJon Loeliger static u8
1381da177e4SLinus Torvalds ohare_mb_content(struct media_bay_info *bay)
1391da177e4SLinus Torvalds {
1401da177e4SLinus Torvalds 	return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
143aacaf9bdSJon Loeliger static u8
1441da177e4SLinus Torvalds heathrow_mb_content(struct media_bay_info *bay)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds 
149aacaf9bdSJon Loeliger static u8
1501da177e4SLinus Torvalds keylargo_mb_content(struct media_bay_info *bay)
1511da177e4SLinus Torvalds {
1521da177e4SLinus Torvalds 	int new_gpio;
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	new_gpio = MB_IN8(bay, KL_GPIO_MEDIABAY_IRQ) & KEYLARGO_GPIO_INPUT_DATA;
1551da177e4SLinus Torvalds 	if (new_gpio) {
1561da177e4SLinus Torvalds 		bay->cached_gpio = new_gpio;
1571da177e4SLinus Torvalds 		return MB_NO;
1581da177e4SLinus Torvalds 	} else if (bay->cached_gpio != new_gpio) {
1591da177e4SLinus Torvalds 		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
1601da177e4SLinus Torvalds 		(void)MB_IN32(bay, KEYLARGO_MBCR);
1611da177e4SLinus Torvalds 		udelay(5);
1621da177e4SLinus Torvalds 		MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
1631da177e4SLinus Torvalds 		(void)MB_IN32(bay, KEYLARGO_MBCR);
1641da177e4SLinus Torvalds 		udelay(5);
1651da177e4SLinus Torvalds 		bay->cached_gpio = new_gpio;
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 	return (MB_IN32(bay, KEYLARGO_MBCR) >> 4) & 7;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds /*
1711da177e4SLinus Torvalds  * Functions for powering up/down the bay, puts the bay device
1721da177e4SLinus Torvalds  * into reset state as well
1731da177e4SLinus Torvalds  */
1741da177e4SLinus Torvalds 
175aacaf9bdSJon Loeliger static void
1761da177e4SLinus Torvalds ohare_mb_power(struct media_bay_info* bay, int on_off)
1771da177e4SLinus Torvalds {
1781da177e4SLinus Torvalds 	if (on_off) {
1791da177e4SLinus Torvalds 		/* Power up device, assert it's reset line */
1801da177e4SLinus Torvalds 		MB_BIC(bay, OHARE_FCR, OH_BAY_RESET_N);
1811da177e4SLinus Torvalds 		MB_BIC(bay, OHARE_FCR, OH_BAY_POWER_N);
1821da177e4SLinus Torvalds 	} else {
1831da177e4SLinus Torvalds 		/* Disable all devices */
1841da177e4SLinus Torvalds 		MB_BIC(bay, OHARE_FCR, OH_BAY_DEV_MASK);
1851da177e4SLinus Torvalds 		MB_BIC(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
1861da177e4SLinus Torvalds 		/* Cut power from bay, release reset line */
1871da177e4SLinus Torvalds 		MB_BIS(bay, OHARE_FCR, OH_BAY_POWER_N);
1881da177e4SLinus Torvalds 		MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
1891da177e4SLinus Torvalds 		MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
1901da177e4SLinus Torvalds 	}
1911da177e4SLinus Torvalds 	MB_BIC(bay, OHARE_MBCR, 0x00000F00);
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
194aacaf9bdSJon Loeliger static void
1951da177e4SLinus Torvalds heathrow_mb_power(struct media_bay_info* bay, int on_off)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	if (on_off) {
1981da177e4SLinus Torvalds 		/* Power up device, assert it's reset line */
1991da177e4SLinus Torvalds 		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
2001da177e4SLinus Torvalds 		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
2011da177e4SLinus Torvalds 	} else {
2021da177e4SLinus Torvalds 		/* Disable all devices */
2031da177e4SLinus Torvalds 		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_DEV_MASK);
2041da177e4SLinus Torvalds 		MB_BIC(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
2051da177e4SLinus Torvalds 		/* Cut power from bay, release reset line */
2061da177e4SLinus Torvalds 		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
2071da177e4SLinus Torvalds 		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
2081da177e4SLinus Torvalds 		MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
2091da177e4SLinus Torvalds 	}
2101da177e4SLinus Torvalds 	MB_BIC(bay, HEATHROW_MBCR, 0x00000F00);
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds 
213aacaf9bdSJon Loeliger static void
2141da177e4SLinus Torvalds keylargo_mb_power(struct media_bay_info* bay, int on_off)
2151da177e4SLinus Torvalds {
2161da177e4SLinus Torvalds 	if (on_off) {
2171da177e4SLinus Torvalds 		/* Power up device, assert it's reset line */
2181da177e4SLinus Torvalds             	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
2191da177e4SLinus Torvalds             	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
2201da177e4SLinus Torvalds 	} else {
2211da177e4SLinus Torvalds 		/* Disable all devices */
2221da177e4SLinus Torvalds 		MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
2231da177e4SLinus Torvalds 		MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
2241da177e4SLinus Torvalds 		/* Cut power from bay, release reset line */
2251da177e4SLinus Torvalds 		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
2261da177e4SLinus Torvalds 		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
2271da177e4SLinus Torvalds 		MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
2281da177e4SLinus Torvalds 	}
2291da177e4SLinus Torvalds 	MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds /*
2331da177e4SLinus Torvalds  * Functions for configuring the media bay for a given type of device,
2341da177e4SLinus Torvalds  * enable the related busses
2351da177e4SLinus Torvalds  */
2361da177e4SLinus Torvalds 
237aacaf9bdSJon Loeliger static int
2381da177e4SLinus Torvalds ohare_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
2391da177e4SLinus Torvalds {
2401da177e4SLinus Torvalds 	switch(device_id) {
2411da177e4SLinus Torvalds 		case MB_FD:
2421da177e4SLinus Torvalds 		case MB_FD1:
2431da177e4SLinus Torvalds 			MB_BIS(bay, OHARE_FCR, OH_BAY_FLOPPY_ENABLE);
2441da177e4SLinus Torvalds 			MB_BIS(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
2451da177e4SLinus Torvalds 			return 0;
2461da177e4SLinus Torvalds 		case MB_CD:
2471da177e4SLinus Torvalds 			MB_BIC(bay, OHARE_FCR, OH_IDE1_RESET_N);
2481da177e4SLinus Torvalds 			MB_BIS(bay, OHARE_FCR, OH_BAY_IDE_ENABLE);
2491da177e4SLinus Torvalds 			return 0;
2501da177e4SLinus Torvalds 		case MB_PCI:
2511da177e4SLinus Torvalds 			MB_BIS(bay, OHARE_FCR, OH_BAY_PCI_ENABLE);
2521da177e4SLinus Torvalds 			return 0;
2531da177e4SLinus Torvalds 	}
2541da177e4SLinus Torvalds 	return -ENODEV;
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds 
257aacaf9bdSJon Loeliger static int
2581da177e4SLinus Torvalds heathrow_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
2591da177e4SLinus Torvalds {
2601da177e4SLinus Torvalds 	switch(device_id) {
2611da177e4SLinus Torvalds 		case MB_FD:
2621da177e4SLinus Torvalds 		case MB_FD1:
2631da177e4SLinus Torvalds 			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_FLOPPY_ENABLE);
2641da177e4SLinus Torvalds 			MB_BIS(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
2651da177e4SLinus Torvalds 			return 0;
2661da177e4SLinus Torvalds 		case MB_CD:
2671da177e4SLinus Torvalds 			MB_BIC(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
2681da177e4SLinus Torvalds 			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_IDE_ENABLE);
2691da177e4SLinus Torvalds 			return 0;
2701da177e4SLinus Torvalds 		case MB_PCI:
2711da177e4SLinus Torvalds 			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_PCI_ENABLE);
2721da177e4SLinus Torvalds 			return 0;
2731da177e4SLinus Torvalds 	}
2741da177e4SLinus Torvalds 	return -ENODEV;
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds 
277aacaf9bdSJon Loeliger static int
2781da177e4SLinus Torvalds keylargo_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
2791da177e4SLinus Torvalds {
2801da177e4SLinus Torvalds 	switch(device_id) {
2811da177e4SLinus Torvalds 		case MB_CD:
2821da177e4SLinus Torvalds 			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
2831da177e4SLinus Torvalds 			MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
2841da177e4SLinus Torvalds 			MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
2851da177e4SLinus Torvalds 			return 0;
2861da177e4SLinus Torvalds 		case MB_PCI:
2871da177e4SLinus Torvalds 			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_PCI_ENABLE);
2881da177e4SLinus Torvalds 			return 0;
2891da177e4SLinus Torvalds 		case MB_SOUND:
2901da177e4SLinus Torvalds 			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_SOUND_ENABLE);
2911da177e4SLinus Torvalds 			return 0;
2921da177e4SLinus Torvalds 	}
2931da177e4SLinus Torvalds 	return -ENODEV;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds /*
2971da177e4SLinus Torvalds  * Functions for tweaking resets
2981da177e4SLinus Torvalds  */
2991da177e4SLinus Torvalds 
300aacaf9bdSJon Loeliger static void
3011da177e4SLinus Torvalds ohare_mb_un_reset(struct media_bay_info* bay)
3021da177e4SLinus Torvalds {
3031da177e4SLinus Torvalds 	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
306aacaf9bdSJon Loeliger static void keylargo_mb_init(struct media_bay_info *bay)
3071da177e4SLinus Torvalds {
3081da177e4SLinus Torvalds 	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
3091da177e4SLinus Torvalds }
3101da177e4SLinus Torvalds 
311aacaf9bdSJon Loeliger static void heathrow_mb_un_reset(struct media_bay_info* bay)
3121da177e4SLinus Torvalds {
3131da177e4SLinus Torvalds 	MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
316aacaf9bdSJon Loeliger static void keylargo_mb_un_reset(struct media_bay_info* bay)
3171da177e4SLinus Torvalds {
3181da177e4SLinus Torvalds 	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
3191da177e4SLinus Torvalds }
3201da177e4SLinus Torvalds 
321aacaf9bdSJon Loeliger static void ohare_mb_un_reset_ide(struct media_bay_info* bay)
3221da177e4SLinus Torvalds {
3231da177e4SLinus Torvalds 	MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
326aacaf9bdSJon Loeliger static void heathrow_mb_un_reset_ide(struct media_bay_info* bay)
3271da177e4SLinus Torvalds {
3281da177e4SLinus Torvalds 	MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds 
331aacaf9bdSJon Loeliger static void keylargo_mb_un_reset_ide(struct media_bay_info* bay)
3321da177e4SLinus Torvalds {
3331da177e4SLinus Torvalds 	MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds 
336aacaf9bdSJon Loeliger static inline void set_mb_power(struct media_bay_info* bay, int onoff)
3371da177e4SLinus Torvalds {
3381da177e4SLinus Torvalds 	/* Power up up and assert the bay reset line */
3391da177e4SLinus Torvalds 	if (onoff) {
3401da177e4SLinus Torvalds 		bay->ops->power(bay, 1);
3411da177e4SLinus Torvalds 		bay->state = mb_powering_up;
342d58b0c39SBenjamin Herrenschmidt 		pr_debug("mediabay%d: powering up\n", bay->index);
3431da177e4SLinus Torvalds 	} else {
3441da177e4SLinus Torvalds 		/* Make sure everything is powered down & disabled */
3451da177e4SLinus Torvalds 		bay->ops->power(bay, 0);
3461da177e4SLinus Torvalds 		bay->state = mb_powering_down;
347d58b0c39SBenjamin Herrenschmidt 		pr_debug("mediabay%d: powering down\n", bay->index);
3481da177e4SLinus Torvalds 	}
3491da177e4SLinus Torvalds 	bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds 
352aacaf9bdSJon Loeliger static void poll_media_bay(struct media_bay_info* bay)
3531da177e4SLinus Torvalds {
3541da177e4SLinus Torvalds 	int id = bay->ops->content(bay);
3551da177e4SLinus Torvalds 
356d58b0c39SBenjamin Herrenschmidt 	static char *mb_content_types[] = {
357d58b0c39SBenjamin Herrenschmidt 		"a floppy drive",
358d58b0c39SBenjamin Herrenschmidt 		"a floppy drive",
359ec16f3dfSMasanari Iida 		"an unsupported audio device",
360d58b0c39SBenjamin Herrenschmidt 		"an ATA device",
361d58b0c39SBenjamin Herrenschmidt 		"an unsupported PCI device",
362d58b0c39SBenjamin Herrenschmidt 		"an unknown device",
363d58b0c39SBenjamin Herrenschmidt 	};
364d58b0c39SBenjamin Herrenschmidt 
365d58b0c39SBenjamin Herrenschmidt 	if (id != bay->last_value) {
366d58b0c39SBenjamin Herrenschmidt 		bay->last_value = id;
367d58b0c39SBenjamin Herrenschmidt 		bay->value_count = 0;
368d58b0c39SBenjamin Herrenschmidt 		return;
369d58b0c39SBenjamin Herrenschmidt 	}
370d58b0c39SBenjamin Herrenschmidt 	if (id == bay->content_id)
371d58b0c39SBenjamin Herrenschmidt 		return;
372d58b0c39SBenjamin Herrenschmidt 
3731da177e4SLinus Torvalds 	bay->value_count += msecs_to_jiffies(MB_POLL_DELAY);
3741da177e4SLinus Torvalds 	if (bay->value_count >= msecs_to_jiffies(MB_STABLE_DELAY)) {
3751da177e4SLinus Torvalds 		/* If the device type changes without going thru
3761da177e4SLinus Torvalds 		 * "MB_NO", we force a pass by "MB_NO" to make sure
3771da177e4SLinus Torvalds 		 * things are properly reset
3781da177e4SLinus Torvalds 		 */
3791da177e4SLinus Torvalds 		if ((id != MB_NO) && (bay->content_id != MB_NO)) {
3801da177e4SLinus Torvalds 			id = MB_NO;
381d58b0c39SBenjamin Herrenschmidt 			pr_debug("mediabay%d: forcing MB_NO\n", bay->index);
3821da177e4SLinus Torvalds 		}
383d58b0c39SBenjamin Herrenschmidt 		pr_debug("mediabay%d: switching to %d\n", bay->index, id);
3841da177e4SLinus Torvalds 		set_mb_power(bay, id != MB_NO);
3851da177e4SLinus Torvalds 		bay->content_id = id;
386d58b0c39SBenjamin Herrenschmidt 		if (id >= MB_NO || id < 0)
387d58b0c39SBenjamin Herrenschmidt 			printk(KERN_INFO "mediabay%d: Bay is now empty\n", bay->index);
388d58b0c39SBenjamin Herrenschmidt 		else
389d58b0c39SBenjamin Herrenschmidt 			printk(KERN_INFO "mediabay%d: Bay contains %s\n",
390d58b0c39SBenjamin Herrenschmidt 			       bay->index, mb_content_types[id]);
3911da177e4SLinus Torvalds 	}
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds 
394d58b0c39SBenjamin Herrenschmidt int check_media_bay(struct macio_dev *baydev)
3951da177e4SLinus Torvalds {
396d58b0c39SBenjamin Herrenschmidt 	struct media_bay_info* bay;
397d58b0c39SBenjamin Herrenschmidt 	int id;
3981da177e4SLinus Torvalds 
399d58b0c39SBenjamin Herrenschmidt 	if (baydev == NULL)
400d58b0c39SBenjamin Herrenschmidt 		return MB_NO;
4011da177e4SLinus Torvalds 
402d58b0c39SBenjamin Herrenschmidt 	/* This returns an instant snapshot, not locking, sine
403d58b0c39SBenjamin Herrenschmidt 	 * we may be called with the bay lock held. The resulting
404d58b0c39SBenjamin Herrenschmidt 	 * fuzzyness of the result if called at the wrong time is
405d58b0c39SBenjamin Herrenschmidt 	 * not actually a huge deal
406d58b0c39SBenjamin Herrenschmidt 	 */
407d58b0c39SBenjamin Herrenschmidt 	bay = macio_get_drvdata(baydev);
408d58b0c39SBenjamin Herrenschmidt 	if (bay == NULL)
409d58b0c39SBenjamin Herrenschmidt 		return MB_NO;
410d58b0c39SBenjamin Herrenschmidt 	id = bay->content_id;
411d58b0c39SBenjamin Herrenschmidt 	if (bay->state != mb_up)
412d58b0c39SBenjamin Herrenschmidt 		return MB_NO;
413d58b0c39SBenjamin Herrenschmidt 	if (id == MB_FD1)
414d58b0c39SBenjamin Herrenschmidt 		return MB_FD;
415d58b0c39SBenjamin Herrenschmidt 	return id;
416d58b0c39SBenjamin Herrenschmidt }
417d58b0c39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(check_media_bay);
418d58b0c39SBenjamin Herrenschmidt 
419d58b0c39SBenjamin Herrenschmidt void lock_media_bay(struct macio_dev *baydev)
4201da177e4SLinus Torvalds {
421d58b0c39SBenjamin Herrenschmidt 	struct media_bay_info* bay;
4221da177e4SLinus Torvalds 
423d58b0c39SBenjamin Herrenschmidt 	if (baydev == NULL)
424d58b0c39SBenjamin Herrenschmidt 		return;
425d58b0c39SBenjamin Herrenschmidt 	bay = macio_get_drvdata(baydev);
426d58b0c39SBenjamin Herrenschmidt 	if (bay == NULL)
427d58b0c39SBenjamin Herrenschmidt 		return;
4289a24729dSDaniel Walker 	mutex_lock(&bay->lock);
429d58b0c39SBenjamin Herrenschmidt 	bay->user_lock = 1;
430d58b0c39SBenjamin Herrenschmidt }
431d58b0c39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(lock_media_bay);
4321da177e4SLinus Torvalds 
433d58b0c39SBenjamin Herrenschmidt void unlock_media_bay(struct macio_dev *baydev)
434d58b0c39SBenjamin Herrenschmidt {
435d58b0c39SBenjamin Herrenschmidt 	struct media_bay_info* bay;
4361da177e4SLinus Torvalds 
437d58b0c39SBenjamin Herrenschmidt 	if (baydev == NULL)
438d58b0c39SBenjamin Herrenschmidt 		return;
439d58b0c39SBenjamin Herrenschmidt 	bay = macio_get_drvdata(baydev);
440d58b0c39SBenjamin Herrenschmidt 	if (bay == NULL)
441d58b0c39SBenjamin Herrenschmidt 		return;
442d58b0c39SBenjamin Herrenschmidt 	if (bay->user_lock) {
443d58b0c39SBenjamin Herrenschmidt 		bay->user_lock = 0;
4449a24729dSDaniel Walker 		mutex_unlock(&bay->lock);
445d58b0c39SBenjamin Herrenschmidt 	}
446d58b0c39SBenjamin Herrenschmidt }
447d58b0c39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(unlock_media_bay);
448d58b0c39SBenjamin Herrenschmidt 
449d58b0c39SBenjamin Herrenschmidt static int mb_broadcast_hotplug(struct device *dev, void *data)
450d58b0c39SBenjamin Herrenschmidt {
451d58b0c39SBenjamin Herrenschmidt 	struct media_bay_info* bay = data;
452d58b0c39SBenjamin Herrenschmidt 	struct macio_dev *mdev;
453d58b0c39SBenjamin Herrenschmidt 	struct macio_driver *drv;
454d58b0c39SBenjamin Herrenschmidt 	int state;
455d58b0c39SBenjamin Herrenschmidt 
456d58b0c39SBenjamin Herrenschmidt 	if (dev->bus != &macio_bus_type)
457d58b0c39SBenjamin Herrenschmidt 		return 0;
458d58b0c39SBenjamin Herrenschmidt 
459d58b0c39SBenjamin Herrenschmidt 	state = bay->state == mb_up ? bay->content_id : MB_NO;
460d58b0c39SBenjamin Herrenschmidt 	if (state == MB_FD1)
461d58b0c39SBenjamin Herrenschmidt 		state = MB_FD;
462d58b0c39SBenjamin Herrenschmidt 	mdev = to_macio_device(dev);
463d58b0c39SBenjamin Herrenschmidt 	drv = to_macio_driver(dev->driver);
464d58b0c39SBenjamin Herrenschmidt 	if (dev->driver && drv->mediabay_event)
465d58b0c39SBenjamin Herrenschmidt 		drv->mediabay_event(mdev, state);
4661da177e4SLinus Torvalds 	return 0;
4671da177e4SLinus Torvalds }
4681da177e4SLinus Torvalds 
469aacaf9bdSJon Loeliger static void media_bay_step(int i)
4701da177e4SLinus Torvalds {
4711da177e4SLinus Torvalds 	struct media_bay_info* bay = &media_bays[i];
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds 	/* We don't poll when powering down */
4741da177e4SLinus Torvalds 	if (bay->state != mb_powering_down)
4751da177e4SLinus Torvalds 	    poll_media_bay(bay);
4761da177e4SLinus Torvalds 
477d58b0c39SBenjamin Herrenschmidt 	/* If timer expired run state machine */
478d58b0c39SBenjamin Herrenschmidt 	if (bay->timer != 0) {
4791da177e4SLinus Torvalds 		bay->timer -= msecs_to_jiffies(MB_POLL_DELAY);
4801da177e4SLinus Torvalds 		if (bay->timer > 0)
4811da177e4SLinus Torvalds 			return;
4821da177e4SLinus Torvalds 		bay->timer = 0;
4831da177e4SLinus Torvalds 	}
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	switch(bay->state) {
4861da177e4SLinus Torvalds 	case mb_powering_up:
4871da177e4SLinus Torvalds 	    	if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
488d58b0c39SBenjamin Herrenschmidt 			pr_debug("mediabay%d: device not supported (kind:%d)\n",
489d58b0c39SBenjamin Herrenschmidt 				 i, bay->content_id);
4901da177e4SLinus Torvalds 	    		set_mb_power(bay, 0);
4911da177e4SLinus Torvalds 	    		break;
4921da177e4SLinus Torvalds 	    	}
4931da177e4SLinus Torvalds 	    	bay->timer = msecs_to_jiffies(MB_RESET_DELAY);
4941da177e4SLinus Torvalds 	    	bay->state = mb_enabling_bay;
495d58b0c39SBenjamin Herrenschmidt 		pr_debug("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
4961da177e4SLinus Torvalds 		break;
4971da177e4SLinus Torvalds 	case mb_enabling_bay:
4981da177e4SLinus Torvalds 		bay->ops->un_reset(bay);
4991da177e4SLinus Torvalds 	    	bay->timer = msecs_to_jiffies(MB_SETUP_DELAY);
5001da177e4SLinus Torvalds 	    	bay->state = mb_resetting;
501d58b0c39SBenjamin Herrenschmidt 		pr_debug("mediabay%d: releasing bay reset (kind:%d)\n",
502d58b0c39SBenjamin Herrenschmidt 			 i, bay->content_id);
5031da177e4SLinus Torvalds 	    	break;
5041da177e4SLinus Torvalds 	case mb_resetting:
5051da177e4SLinus Torvalds 		if (bay->content_id != MB_CD) {
506d58b0c39SBenjamin Herrenschmidt 			pr_debug("mediabay%d: bay is up (kind:%d)\n", i,
507d58b0c39SBenjamin Herrenschmidt 				 bay->content_id);
5081da177e4SLinus Torvalds 			bay->state = mb_up;
509d58b0c39SBenjamin Herrenschmidt 			device_for_each_child(&bay->mdev->ofdev.dev,
510d58b0c39SBenjamin Herrenschmidt 					      bay, mb_broadcast_hotplug);
5111da177e4SLinus Torvalds 			break;
5121da177e4SLinus Torvalds 	    	}
513d58b0c39SBenjamin Herrenschmidt 		pr_debug("mediabay%d: releasing ATA reset (kind:%d)\n",
514d58b0c39SBenjamin Herrenschmidt 			 i, bay->content_id);
5151da177e4SLinus Torvalds 		bay->ops->un_reset_ide(bay);
5161da177e4SLinus Torvalds 	    	bay->timer = msecs_to_jiffies(MB_IDE_WAIT);
5171da177e4SLinus Torvalds 	    	bay->state = mb_ide_resetting;
5181da177e4SLinus Torvalds 	    	break;
519d58b0c39SBenjamin Herrenschmidt 
5201da177e4SLinus Torvalds 	case mb_ide_resetting:
521d58b0c39SBenjamin Herrenschmidt 		pr_debug("mediabay%d: bay is up (kind:%d)\n", i, bay->content_id);
5221da177e4SLinus Torvalds 		bay->state = mb_up;
523d58b0c39SBenjamin Herrenschmidt 		device_for_each_child(&bay->mdev->ofdev.dev,
524d58b0c39SBenjamin Herrenschmidt 				      bay, mb_broadcast_hotplug);
5251da177e4SLinus Torvalds 	    	break;
526d58b0c39SBenjamin Herrenschmidt 
5271da177e4SLinus Torvalds 	case mb_powering_down:
5281da177e4SLinus Torvalds 	    	bay->state = mb_empty;
529d58b0c39SBenjamin Herrenschmidt 		device_for_each_child(&bay->mdev->ofdev.dev,
530d58b0c39SBenjamin Herrenschmidt 				      bay, mb_broadcast_hotplug);
531d58b0c39SBenjamin Herrenschmidt 		pr_debug("mediabay%d: end of power down\n", i);
5321da177e4SLinus Torvalds 	    	break;
5331da177e4SLinus Torvalds 	}
5341da177e4SLinus Torvalds }
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds /*
5371da177e4SLinus Torvalds  * This procedure runs as a kernel thread to poll the media bay
5381da177e4SLinus Torvalds  * once each tick and register and unregister the IDE interface
5391da177e4SLinus Torvalds  * with the IDE driver.  It needs to be a thread because
5401da177e4SLinus Torvalds  * ide_register can't be called from interrupt context.
5411da177e4SLinus Torvalds  */
542aacaf9bdSJon Loeliger static int media_bay_task(void *x)
5431da177e4SLinus Torvalds {
5441da177e4SLinus Torvalds 	int	i;
5451da177e4SLinus Torvalds 
54633f6e794SPaul Mackerras 	while (!kthread_should_stop()) {
5471da177e4SLinus Torvalds 		for (i = 0; i < media_bay_count; ++i) {
5489a24729dSDaniel Walker 			mutex_lock(&media_bays[i].lock);
5491da177e4SLinus Torvalds 			if (!media_bays[i].sleeping)
5501da177e4SLinus Torvalds 				media_bay_step(i);
5519a24729dSDaniel Walker 			mutex_unlock(&media_bays[i].lock);
5521da177e4SLinus Torvalds 		}
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 		msleep_interruptible(MB_POLL_DELAY);
5551da177e4SLinus Torvalds 	}
55633f6e794SPaul Mackerras 	return 0;
5571da177e4SLinus Torvalds }
5581da177e4SLinus Torvalds 
5595e655772SJeff Mahoney static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_device_id *match)
5601da177e4SLinus Torvalds {
5611da177e4SLinus Torvalds 	struct media_bay_info* bay;
5621da177e4SLinus Torvalds 	u32 __iomem *regbase;
5631da177e4SLinus Torvalds 	struct device_node *ofnode;
564cc5d0189SBenjamin Herrenschmidt 	unsigned long base;
5651da177e4SLinus Torvalds 	int i;
5661da177e4SLinus Torvalds 
56761c7a080SGrant Likely 	ofnode = mdev->ofdev.dev.of_node;
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 	if (macio_resource_count(mdev) < 1)
5701da177e4SLinus Torvalds 		return -ENODEV;
5711da177e4SLinus Torvalds 	if (macio_request_resources(mdev, "media-bay"))
5721da177e4SLinus Torvalds 		return -EBUSY;
5731da177e4SLinus Torvalds 	/* Media bay registers are located at the beginning of the
574cc5d0189SBenjamin Herrenschmidt          * mac-io chip, for now, we trick and align down the first
575cc5d0189SBenjamin Herrenschmidt 	 * resource passed in
5761da177e4SLinus Torvalds          */
577cc5d0189SBenjamin Herrenschmidt 	base = macio_resource_start(mdev, 0) & 0xffff0000u;
578cc5d0189SBenjamin Herrenschmidt 	regbase = (u32 __iomem *)ioremap(base, 0x100);
5791da177e4SLinus Torvalds 	if (regbase == NULL) {
5801da177e4SLinus Torvalds 		macio_release_resources(mdev);
5811da177e4SLinus Torvalds 		return -ENOMEM;
5821da177e4SLinus Torvalds 	}
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	i = media_bay_count++;
5851da177e4SLinus Torvalds 	bay = &media_bays[i];
5861da177e4SLinus Torvalds 	bay->mdev = mdev;
5871da177e4SLinus Torvalds 	bay->base = regbase;
5881da177e4SLinus Torvalds 	bay->index = i;
5891da177e4SLinus Torvalds 	bay->ops = match->data;
5901da177e4SLinus Torvalds 	bay->sleeping = 0;
5919a24729dSDaniel Walker 	mutex_init(&bay->lock);
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 	/* Init HW probing */
5941da177e4SLinus Torvalds 	if (bay->ops->init)
5951da177e4SLinus Torvalds 		bay->ops->init(bay);
5961da177e4SLinus Torvalds 
5971da177e4SLinus Torvalds 	printk(KERN_INFO "mediabay%d: Registered %s media-bay\n", i, bay->ops->name);
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds 	/* Force an immediate detect */
6001da177e4SLinus Torvalds 	set_mb_power(bay, 0);
6011da177e4SLinus Torvalds 	msleep(MB_POWER_DELAY);
6021da177e4SLinus Torvalds 	bay->content_id = MB_NO;
6031da177e4SLinus Torvalds 	bay->last_value = bay->ops->content(bay);
6041da177e4SLinus Torvalds 	bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
6051da177e4SLinus Torvalds 	bay->state = mb_empty;
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 	/* Mark us ready by filling our mdev data */
6081da177e4SLinus Torvalds 	macio_set_drvdata(mdev, bay);
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds 	/* Startup kernel thread */
6111da177e4SLinus Torvalds 	if (i == 0)
61233f6e794SPaul Mackerras 		kthread_run(media_bay_task, NULL, "media-bay");
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	return 0;
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds }
6171da177e4SLinus Torvalds 
618aacaf9bdSJon Loeliger static int media_bay_suspend(struct macio_dev *mdev, pm_message_t state)
6191da177e4SLinus Torvalds {
6201da177e4SLinus Torvalds 	struct media_bay_info	*bay = macio_get_drvdata(mdev);
6211da177e4SLinus Torvalds 
6223a2d5b70SRafael J. Wysocki 	if (state.event != mdev->ofdev.dev.power.power_state.event
6233a2d5b70SRafael J. Wysocki 	    && (state.event & PM_EVENT_SLEEP)) {
6249a24729dSDaniel Walker 		mutex_lock(&bay->lock);
6251da177e4SLinus Torvalds 		bay->sleeping = 1;
6261da177e4SLinus Torvalds 		set_mb_power(bay, 0);
6279a24729dSDaniel Walker 		mutex_unlock(&bay->lock);
6281da177e4SLinus Torvalds 		msleep(MB_POLL_DELAY);
6291da177e4SLinus Torvalds 		mdev->ofdev.dev.power.power_state = state;
6301da177e4SLinus Torvalds 	}
6311da177e4SLinus Torvalds 	return 0;
6321da177e4SLinus Torvalds }
6331da177e4SLinus Torvalds 
634aacaf9bdSJon Loeliger static int media_bay_resume(struct macio_dev *mdev)
6351da177e4SLinus Torvalds {
6361da177e4SLinus Torvalds 	struct media_bay_info	*bay = macio_get_drvdata(mdev);
6371da177e4SLinus Torvalds 
638ca078baeSPavel Machek 	if (mdev->ofdev.dev.power.power_state.event != PM_EVENT_ON) {
639829ca9a3SPavel Machek 		mdev->ofdev.dev.power.power_state = PMSG_ON;
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	       	/* We re-enable the bay using it's previous content
6421da177e4SLinus Torvalds 	       	   only if it did not change. Note those bozo timings,
6431da177e4SLinus Torvalds 	       	   they seem to help the 3400 get it right.
6441da177e4SLinus Torvalds 	       	 */
6451da177e4SLinus Torvalds 	       	/* Force MB power to 0 */
6469a24729dSDaniel Walker 		mutex_lock(&bay->lock);
6471da177e4SLinus Torvalds 	       	set_mb_power(bay, 0);
6481da177e4SLinus Torvalds 		msleep(MB_POWER_DELAY);
6491da177e4SLinus Torvalds 	       	if (bay->ops->content(bay) != bay->content_id) {
650d58b0c39SBenjamin Herrenschmidt 			printk("mediabay%d: Content changed during sleep...\n", bay->index);
6519a24729dSDaniel Walker 			mutex_unlock(&bay->lock);
6521da177e4SLinus Torvalds 	       		return 0;
6531da177e4SLinus Torvalds 		}
6541da177e4SLinus Torvalds 	       	set_mb_power(bay, 1);
6551da177e4SLinus Torvalds 	       	bay->last_value = bay->content_id;
6561da177e4SLinus Torvalds 	       	bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
6571da177e4SLinus Torvalds 	       	bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
6581da177e4SLinus Torvalds 	       	do {
6591da177e4SLinus Torvalds 			msleep(MB_POLL_DELAY);
6601da177e4SLinus Torvalds 	       		media_bay_step(bay->index);
6611da177e4SLinus Torvalds 	       	} while((bay->state != mb_empty) &&
6621da177e4SLinus Torvalds 	       		(bay->state != mb_up));
6631da177e4SLinus Torvalds 		bay->sleeping = 0;
6649a24729dSDaniel Walker 		mutex_unlock(&bay->lock);
6651da177e4SLinus Torvalds 	}
6661da177e4SLinus Torvalds 	return 0;
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds /* Definitions of "ops" structures.
6711da177e4SLinus Torvalds  */
672*306e352aSArnd Bergmann static const struct mb_ops ohare_mb_ops = {
6731da177e4SLinus Torvalds 	.name		= "Ohare",
6741da177e4SLinus Torvalds 	.content	= ohare_mb_content,
6751da177e4SLinus Torvalds 	.power		= ohare_mb_power,
6761da177e4SLinus Torvalds 	.setup_bus	= ohare_mb_setup_bus,
6771da177e4SLinus Torvalds 	.un_reset	= ohare_mb_un_reset,
6781da177e4SLinus Torvalds 	.un_reset_ide	= ohare_mb_un_reset_ide,
6791da177e4SLinus Torvalds };
6801da177e4SLinus Torvalds 
681*306e352aSArnd Bergmann static const struct mb_ops heathrow_mb_ops = {
6821da177e4SLinus Torvalds 	.name		= "Heathrow",
6831da177e4SLinus Torvalds 	.content	= heathrow_mb_content,
6841da177e4SLinus Torvalds 	.power		= heathrow_mb_power,
6851da177e4SLinus Torvalds 	.setup_bus	= heathrow_mb_setup_bus,
6861da177e4SLinus Torvalds 	.un_reset	= heathrow_mb_un_reset,
6871da177e4SLinus Torvalds 	.un_reset_ide	= heathrow_mb_un_reset_ide,
6881da177e4SLinus Torvalds };
6891da177e4SLinus Torvalds 
690*306e352aSArnd Bergmann static const struct mb_ops keylargo_mb_ops = {
6911da177e4SLinus Torvalds 	.name		= "KeyLargo",
6921da177e4SLinus Torvalds 	.init		= keylargo_mb_init,
6931da177e4SLinus Torvalds 	.content	= keylargo_mb_content,
6941da177e4SLinus Torvalds 	.power		= keylargo_mb_power,
6951da177e4SLinus Torvalds 	.setup_bus	= keylargo_mb_setup_bus,
6961da177e4SLinus Torvalds 	.un_reset	= keylargo_mb_un_reset,
6971da177e4SLinus Torvalds 	.un_reset_ide	= keylargo_mb_un_reset_ide,
6981da177e4SLinus Torvalds };
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds /*
7011da177e4SLinus Torvalds  * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
7021da177e4SLinus Torvalds  * register is always set when there is something in the media bay.
7031da177e4SLinus Torvalds  * This causes problems for the interrupt code if we attach an interrupt
7041da177e4SLinus Torvalds  * handler to the media-bay interrupt, because it tends to go into
7051da177e4SLinus Torvalds  * an infinite loop calling the media bay interrupt handler.
7061da177e4SLinus Torvalds  * Therefore we do it all by polling the media bay once each tick.
7071da177e4SLinus Torvalds  */
7081da177e4SLinus Torvalds 
7095e655772SJeff Mahoney static struct of_device_id media_bay_match[] =
7101da177e4SLinus Torvalds {
7111da177e4SLinus Torvalds 	{
7121da177e4SLinus Torvalds 	.name		= "media-bay",
7131da177e4SLinus Torvalds 	.compatible	= "keylargo-media-bay",
7141da177e4SLinus Torvalds 	.data		= &keylargo_mb_ops,
7151da177e4SLinus Torvalds 	},
7161da177e4SLinus Torvalds 	{
7171da177e4SLinus Torvalds 	.name		= "media-bay",
7181da177e4SLinus Torvalds 	.compatible	= "heathrow-media-bay",
7191da177e4SLinus Torvalds 	.data		= &heathrow_mb_ops,
7201da177e4SLinus Torvalds 	},
7211da177e4SLinus Torvalds 	{
7221da177e4SLinus Torvalds 	.name		= "media-bay",
7231da177e4SLinus Torvalds 	.compatible	= "ohare-media-bay",
7241da177e4SLinus Torvalds 	.data		= &ohare_mb_ops,
7251da177e4SLinus Torvalds 	},
7261da177e4SLinus Torvalds 	{},
7271da177e4SLinus Torvalds };
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds static struct macio_driver media_bay_driver =
7301da177e4SLinus Torvalds {
731c2cdf6abSBenjamin Herrenschmidt 	.driver = {
7321da177e4SLinus Torvalds 		.name		= "media-bay",
733c2cdf6abSBenjamin Herrenschmidt 		.of_match_table	= media_bay_match,
734c2cdf6abSBenjamin Herrenschmidt 	},
7351da177e4SLinus Torvalds 	.probe		= media_bay_attach,
7361da177e4SLinus Torvalds 	.suspend	= media_bay_suspend,
7371da177e4SLinus Torvalds 	.resume		= media_bay_resume
7381da177e4SLinus Torvalds };
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds static int __init media_bay_init(void)
7411da177e4SLinus Torvalds {
7421da177e4SLinus Torvalds 	int i;
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds 	for (i=0; i<MAX_BAYS; i++) {
7451da177e4SLinus Torvalds 		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
7461da177e4SLinus Torvalds 		media_bays[i].content_id	= -1;
7471da177e4SLinus Torvalds 	}
748e8222502SBenjamin Herrenschmidt 	if (!machine_is(powermac))
749e8222502SBenjamin Herrenschmidt 		return 0;
7501da177e4SLinus Torvalds 
7511da177e4SLinus Torvalds 	macio_register_driver(&media_bay_driver);
7521da177e4SLinus Torvalds 
7531da177e4SLinus Torvalds 	return 0;
7541da177e4SLinus Torvalds }
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds device_initcall(media_bay_init);
757