12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Driver for the media bay on the PowerBook 3400 and 2400.
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1998 Paul Mackerras.
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Various evolutions by Benjamin Herrenschmidt & Henry Worth
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds #include <linux/types.h>
101da177e4SLinus Torvalds #include <linux/errno.h>
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/delay.h>
131da177e4SLinus Torvalds #include <linux/sched.h>
141da177e4SLinus Torvalds #include <linux/timer.h>
151da177e4SLinus Torvalds #include <linux/stddef.h>
161da177e4SLinus Torvalds #include <linux/init.h>
1733f6e794SPaul Mackerras #include <linux/kthread.h>
189a24729dSDaniel Walker #include <linux/mutex.h>
19ca5999fdSMike Rapoport #include <linux/pgtable.h>
20*a486e512SChristophe Leroy
211da177e4SLinus Torvalds #include <asm/io.h>
221da177e4SLinus Torvalds #include <asm/machdep.h>
231da177e4SLinus Torvalds #include <asm/pmac_feature.h>
241da177e4SLinus Torvalds #include <asm/mediabay.h>
251da177e4SLinus Torvalds #include <asm/sections.h>
261da177e4SLinus Torvalds #include <asm/ohare.h>
271da177e4SLinus Torvalds #include <asm/heathrow.h>
281da177e4SLinus Torvalds #include <asm/keylargo.h>
291da177e4SLinus Torvalds #include <linux/adb.h>
301da177e4SLinus Torvalds #include <linux/pmu.h>
311da177e4SLinus Torvalds
321da177e4SLinus Torvalds #define MB_FCR32(bay, r) ((bay)->base + ((r) >> 2))
331da177e4SLinus Torvalds #define MB_FCR8(bay, r) (((volatile u8 __iomem *)((bay)->base)) + (r))
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds #define MB_IN32(bay,r) (in_le32(MB_FCR32(bay,r)))
361da177e4SLinus Torvalds #define MB_OUT32(bay,r,v) (out_le32(MB_FCR32(bay,r), (v)))
371da177e4SLinus Torvalds #define MB_BIS(bay,r,v) (MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
381da177e4SLinus Torvalds #define MB_BIC(bay,r,v) (MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
391da177e4SLinus Torvalds #define MB_IN8(bay,r) (in_8(MB_FCR8(bay,r)))
401da177e4SLinus Torvalds #define MB_OUT8(bay,r,v) (out_8(MB_FCR8(bay,r), (v)))
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds struct media_bay_info;
431da177e4SLinus Torvalds
441da177e4SLinus Torvalds struct mb_ops {
451da177e4SLinus Torvalds char* name;
461da177e4SLinus Torvalds void (*init)(struct media_bay_info *bay);
471da177e4SLinus Torvalds u8 (*content)(struct media_bay_info *bay);
481da177e4SLinus Torvalds void (*power)(struct media_bay_info *bay, int on_off);
491da177e4SLinus Torvalds int (*setup_bus)(struct media_bay_info *bay, u8 device_id);
501da177e4SLinus Torvalds void (*un_reset)(struct media_bay_info *bay);
511da177e4SLinus Torvalds void (*un_reset_ide)(struct media_bay_info *bay);
521da177e4SLinus Torvalds };
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds struct media_bay_info {
551da177e4SLinus Torvalds u32 __iomem *base;
561da177e4SLinus Torvalds int content_id;
571da177e4SLinus Torvalds int state;
581da177e4SLinus Torvalds int last_value;
591da177e4SLinus Torvalds int value_count;
601da177e4SLinus Torvalds int timer;
611da177e4SLinus Torvalds struct macio_dev *mdev;
62519a6510SUwe Kleine-König const struct mb_ops* ops;
631da177e4SLinus Torvalds int index;
641da177e4SLinus Torvalds int cached_gpio;
651da177e4SLinus Torvalds int sleeping;
66d58b0c39SBenjamin Herrenschmidt int user_lock;
679a24729dSDaniel Walker struct mutex lock;
681da177e4SLinus Torvalds };
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds #define MAX_BAYS 2
711da177e4SLinus Torvalds
721da177e4SLinus Torvalds static struct media_bay_info media_bays[MAX_BAYS];
73d58b0c39SBenjamin Herrenschmidt static int media_bay_count = 0;
741da177e4SLinus Torvalds
751da177e4SLinus Torvalds /*
761da177e4SLinus Torvalds * Wait that number of ms between each step in normal polling mode
771da177e4SLinus Torvalds */
781da177e4SLinus Torvalds #define MB_POLL_DELAY 25
791da177e4SLinus Torvalds
801da177e4SLinus Torvalds /*
811da177e4SLinus Torvalds * Consider the media-bay ID value stable if it is the same for
821da177e4SLinus Torvalds * this number of milliseconds
831da177e4SLinus Torvalds */
841da177e4SLinus Torvalds #define MB_STABLE_DELAY 100
851da177e4SLinus Torvalds
861da177e4SLinus Torvalds /* Wait after powering up the media bay this delay in ms
871da177e4SLinus Torvalds * timeout bumped for some powerbooks
881da177e4SLinus Torvalds */
891da177e4SLinus Torvalds #define MB_POWER_DELAY 200
901da177e4SLinus Torvalds
911da177e4SLinus Torvalds /*
921da177e4SLinus Torvalds * Hold the media-bay reset signal true for this many ticks
931da177e4SLinus Torvalds * after a device is inserted before releasing it.
941da177e4SLinus Torvalds */
951da177e4SLinus Torvalds #define MB_RESET_DELAY 50
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds /*
981da177e4SLinus Torvalds * Wait this long after the reset signal is released and before doing
991da177e4SLinus Torvalds * further operations. After this delay, the IDE reset signal is released
1001da177e4SLinus Torvalds * too for an IDE device
1011da177e4SLinus Torvalds */
1021da177e4SLinus Torvalds #define MB_SETUP_DELAY 100
1031da177e4SLinus Torvalds
1041da177e4SLinus Torvalds /*
1051da177e4SLinus Torvalds * Wait this many ticks after an IDE device (e.g. CD-ROM) is inserted
106d58b0c39SBenjamin Herrenschmidt * (or until the device is ready) before calling into the driver
1071da177e4SLinus Torvalds */
1081da177e4SLinus Torvalds #define MB_IDE_WAIT 1000
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds /*
1111da177e4SLinus Torvalds * States of a media bay
1121da177e4SLinus Torvalds */
1131da177e4SLinus Torvalds enum {
1141da177e4SLinus Torvalds mb_empty = 0, /* Idle */
1151da177e4SLinus Torvalds mb_powering_up, /* power bit set, waiting MB_POWER_DELAY */
1161da177e4SLinus Torvalds mb_enabling_bay, /* enable bits set, waiting MB_RESET_DELAY */
1171da177e4SLinus Torvalds mb_resetting, /* reset bit unset, waiting MB_SETUP_DELAY */
1181da177e4SLinus Torvalds mb_ide_resetting, /* IDE reset bit unser, waiting MB_IDE_WAIT */
1191da177e4SLinus Torvalds mb_up, /* Media bay full */
1201da177e4SLinus Torvalds mb_powering_down /* Powering down (avoid too fast down/up) */
1211da177e4SLinus Torvalds };
1221da177e4SLinus Torvalds
1231da177e4SLinus Torvalds #define MB_POWER_SOUND 0x08
1241da177e4SLinus Torvalds #define MB_POWER_FLOPPY 0x04
1251da177e4SLinus Torvalds #define MB_POWER_ATA 0x02
1261da177e4SLinus Torvalds #define MB_POWER_PCI 0x01
1271da177e4SLinus Torvalds #define MB_POWER_OFF 0x00
1281da177e4SLinus Torvalds
1291da177e4SLinus Torvalds /*
1301da177e4SLinus Torvalds * Functions for polling content of media bay
1311da177e4SLinus Torvalds */
1321da177e4SLinus Torvalds
133aacaf9bdSJon Loeliger static u8
ohare_mb_content(struct media_bay_info * bay)1341da177e4SLinus Torvalds ohare_mb_content(struct media_bay_info *bay)
1351da177e4SLinus Torvalds {
1361da177e4SLinus Torvalds return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds
139aacaf9bdSJon Loeliger static u8
heathrow_mb_content(struct media_bay_info * bay)1401da177e4SLinus Torvalds heathrow_mb_content(struct media_bay_info *bay)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
1431da177e4SLinus Torvalds }
1441da177e4SLinus Torvalds
145aacaf9bdSJon Loeliger static u8
keylargo_mb_content(struct media_bay_info * bay)1461da177e4SLinus Torvalds keylargo_mb_content(struct media_bay_info *bay)
1471da177e4SLinus Torvalds {
1481da177e4SLinus Torvalds int new_gpio;
1491da177e4SLinus Torvalds
1501da177e4SLinus Torvalds new_gpio = MB_IN8(bay, KL_GPIO_MEDIABAY_IRQ) & KEYLARGO_GPIO_INPUT_DATA;
1511da177e4SLinus Torvalds if (new_gpio) {
1521da177e4SLinus Torvalds bay->cached_gpio = new_gpio;
1531da177e4SLinus Torvalds return MB_NO;
1541da177e4SLinus Torvalds } else if (bay->cached_gpio != new_gpio) {
1551da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
1561da177e4SLinus Torvalds (void)MB_IN32(bay, KEYLARGO_MBCR);
1571da177e4SLinus Torvalds udelay(5);
1581da177e4SLinus Torvalds MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
1591da177e4SLinus Torvalds (void)MB_IN32(bay, KEYLARGO_MBCR);
1601da177e4SLinus Torvalds udelay(5);
1611da177e4SLinus Torvalds bay->cached_gpio = new_gpio;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds return (MB_IN32(bay, KEYLARGO_MBCR) >> 4) & 7;
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds
1661da177e4SLinus Torvalds /*
1671da177e4SLinus Torvalds * Functions for powering up/down the bay, puts the bay device
1681da177e4SLinus Torvalds * into reset state as well
1691da177e4SLinus Torvalds */
1701da177e4SLinus Torvalds
171aacaf9bdSJon Loeliger static void
ohare_mb_power(struct media_bay_info * bay,int on_off)1721da177e4SLinus Torvalds ohare_mb_power(struct media_bay_info* bay, int on_off)
1731da177e4SLinus Torvalds {
1741da177e4SLinus Torvalds if (on_off) {
1751da177e4SLinus Torvalds /* Power up device, assert it's reset line */
1761da177e4SLinus Torvalds MB_BIC(bay, OHARE_FCR, OH_BAY_RESET_N);
1771da177e4SLinus Torvalds MB_BIC(bay, OHARE_FCR, OH_BAY_POWER_N);
1781da177e4SLinus Torvalds } else {
1791da177e4SLinus Torvalds /* Disable all devices */
1801da177e4SLinus Torvalds MB_BIC(bay, OHARE_FCR, OH_BAY_DEV_MASK);
1811da177e4SLinus Torvalds MB_BIC(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
1821da177e4SLinus Torvalds /* Cut power from bay, release reset line */
1831da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_BAY_POWER_N);
1841da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
1851da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
1861da177e4SLinus Torvalds }
1871da177e4SLinus Torvalds MB_BIC(bay, OHARE_MBCR, 0x00000F00);
1881da177e4SLinus Torvalds }
1891da177e4SLinus Torvalds
190aacaf9bdSJon Loeliger static void
heathrow_mb_power(struct media_bay_info * bay,int on_off)1911da177e4SLinus Torvalds heathrow_mb_power(struct media_bay_info* bay, int on_off)
1921da177e4SLinus Torvalds {
1931da177e4SLinus Torvalds if (on_off) {
1941da177e4SLinus Torvalds /* Power up device, assert it's reset line */
1951da177e4SLinus Torvalds MB_BIC(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
1961da177e4SLinus Torvalds MB_BIC(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
1971da177e4SLinus Torvalds } else {
1981da177e4SLinus Torvalds /* Disable all devices */
1991da177e4SLinus Torvalds MB_BIC(bay, HEATHROW_FCR, HRW_BAY_DEV_MASK);
2001da177e4SLinus Torvalds MB_BIC(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
2011da177e4SLinus Torvalds /* Cut power from bay, release reset line */
2021da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
2031da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
2041da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds MB_BIC(bay, HEATHROW_MBCR, 0x00000F00);
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds
209aacaf9bdSJon Loeliger static void
keylargo_mb_power(struct media_bay_info * bay,int on_off)2101da177e4SLinus Torvalds keylargo_mb_power(struct media_bay_info* bay, int on_off)
2111da177e4SLinus Torvalds {
2121da177e4SLinus Torvalds if (on_off) {
2131da177e4SLinus Torvalds /* Power up device, assert it's reset line */
2141da177e4SLinus Torvalds MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
2151da177e4SLinus Torvalds MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
2161da177e4SLinus Torvalds } else {
2171da177e4SLinus Torvalds /* Disable all devices */
2181da177e4SLinus Torvalds MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
2191da177e4SLinus Torvalds MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
2201da177e4SLinus Torvalds /* Cut power from bay, release reset line */
2211da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
2221da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
2231da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
2241da177e4SLinus Torvalds }
2251da177e4SLinus Torvalds MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds
2281da177e4SLinus Torvalds /*
2291da177e4SLinus Torvalds * Functions for configuring the media bay for a given type of device,
2301da177e4SLinus Torvalds * enable the related busses
2311da177e4SLinus Torvalds */
2321da177e4SLinus Torvalds
233aacaf9bdSJon Loeliger static int
ohare_mb_setup_bus(struct media_bay_info * bay,u8 device_id)2341da177e4SLinus Torvalds ohare_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
2351da177e4SLinus Torvalds {
2361da177e4SLinus Torvalds switch(device_id) {
2371da177e4SLinus Torvalds case MB_FD:
2381da177e4SLinus Torvalds case MB_FD1:
2391da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_BAY_FLOPPY_ENABLE);
2401da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
2411da177e4SLinus Torvalds return 0;
2421da177e4SLinus Torvalds case MB_CD:
2431da177e4SLinus Torvalds MB_BIC(bay, OHARE_FCR, OH_IDE1_RESET_N);
2441da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_BAY_IDE_ENABLE);
2451da177e4SLinus Torvalds return 0;
2461da177e4SLinus Torvalds case MB_PCI:
2471da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_BAY_PCI_ENABLE);
2481da177e4SLinus Torvalds return 0;
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds return -ENODEV;
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds
253aacaf9bdSJon Loeliger static int
heathrow_mb_setup_bus(struct media_bay_info * bay,u8 device_id)2541da177e4SLinus Torvalds heathrow_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
2551da177e4SLinus Torvalds {
2561da177e4SLinus Torvalds switch(device_id) {
2571da177e4SLinus Torvalds case MB_FD:
2581da177e4SLinus Torvalds case MB_FD1:
2591da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_BAY_FLOPPY_ENABLE);
2601da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
2611da177e4SLinus Torvalds return 0;
2621da177e4SLinus Torvalds case MB_CD:
2631da177e4SLinus Torvalds MB_BIC(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
2641da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_BAY_IDE_ENABLE);
2651da177e4SLinus Torvalds return 0;
2661da177e4SLinus Torvalds case MB_PCI:
2671da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_BAY_PCI_ENABLE);
2681da177e4SLinus Torvalds return 0;
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds return -ENODEV;
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds
273aacaf9bdSJon Loeliger static int
keylargo_mb_setup_bus(struct media_bay_info * bay,u8 device_id)2741da177e4SLinus Torvalds keylargo_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
2751da177e4SLinus Torvalds {
2761da177e4SLinus Torvalds switch(device_id) {
2771da177e4SLinus Torvalds case MB_CD:
2781da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
2791da177e4SLinus Torvalds MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
2801da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
2811da177e4SLinus Torvalds return 0;
2821da177e4SLinus Torvalds case MB_PCI:
2831da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_PCI_ENABLE);
2841da177e4SLinus Torvalds return 0;
2851da177e4SLinus Torvalds case MB_SOUND:
2861da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_SOUND_ENABLE);
2871da177e4SLinus Torvalds return 0;
2881da177e4SLinus Torvalds }
2891da177e4SLinus Torvalds return -ENODEV;
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds
2921da177e4SLinus Torvalds /*
2931da177e4SLinus Torvalds * Functions for tweaking resets
2941da177e4SLinus Torvalds */
2951da177e4SLinus Torvalds
296aacaf9bdSJon Loeliger static void
ohare_mb_un_reset(struct media_bay_info * bay)2971da177e4SLinus Torvalds ohare_mb_un_reset(struct media_bay_info* bay)
2981da177e4SLinus Torvalds {
2991da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds
keylargo_mb_init(struct media_bay_info * bay)302aacaf9bdSJon Loeliger static void keylargo_mb_init(struct media_bay_info *bay)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds
heathrow_mb_un_reset(struct media_bay_info * bay)307aacaf9bdSJon Loeliger static void heathrow_mb_un_reset(struct media_bay_info* bay)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds
keylargo_mb_un_reset(struct media_bay_info * bay)312aacaf9bdSJon Loeliger static void keylargo_mb_un_reset(struct media_bay_info* bay)
3131da177e4SLinus Torvalds {
3141da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds
ohare_mb_un_reset_ide(struct media_bay_info * bay)317aacaf9bdSJon Loeliger static void ohare_mb_un_reset_ide(struct media_bay_info* bay)
3181da177e4SLinus Torvalds {
3191da177e4SLinus Torvalds MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds
heathrow_mb_un_reset_ide(struct media_bay_info * bay)322aacaf9bdSJon Loeliger static void heathrow_mb_un_reset_ide(struct media_bay_info* bay)
3231da177e4SLinus Torvalds {
3241da177e4SLinus Torvalds MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
3251da177e4SLinus Torvalds }
3261da177e4SLinus Torvalds
keylargo_mb_un_reset_ide(struct media_bay_info * bay)327aacaf9bdSJon Loeliger static void keylargo_mb_un_reset_ide(struct media_bay_info* bay)
3281da177e4SLinus Torvalds {
3291da177e4SLinus Torvalds MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds
set_mb_power(struct media_bay_info * bay,int onoff)332aacaf9bdSJon Loeliger static inline void set_mb_power(struct media_bay_info* bay, int onoff)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds /* Power up up and assert the bay reset line */
3351da177e4SLinus Torvalds if (onoff) {
3361da177e4SLinus Torvalds bay->ops->power(bay, 1);
3371da177e4SLinus Torvalds bay->state = mb_powering_up;
338d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: powering up\n", bay->index);
3391da177e4SLinus Torvalds } else {
3401da177e4SLinus Torvalds /* Make sure everything is powered down & disabled */
3411da177e4SLinus Torvalds bay->ops->power(bay, 0);
3421da177e4SLinus Torvalds bay->state = mb_powering_down;
343d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: powering down\n", bay->index);
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds
poll_media_bay(struct media_bay_info * bay)348aacaf9bdSJon Loeliger static void poll_media_bay(struct media_bay_info* bay)
3491da177e4SLinus Torvalds {
3501da177e4SLinus Torvalds int id = bay->ops->content(bay);
3511da177e4SLinus Torvalds
352d58b0c39SBenjamin Herrenschmidt static char *mb_content_types[] = {
353d58b0c39SBenjamin Herrenschmidt "a floppy drive",
354d58b0c39SBenjamin Herrenschmidt "a floppy drive",
355ec16f3dfSMasanari Iida "an unsupported audio device",
356d58b0c39SBenjamin Herrenschmidt "an ATA device",
357d58b0c39SBenjamin Herrenschmidt "an unsupported PCI device",
358d58b0c39SBenjamin Herrenschmidt "an unknown device",
359d58b0c39SBenjamin Herrenschmidt };
360d58b0c39SBenjamin Herrenschmidt
361d58b0c39SBenjamin Herrenschmidt if (id != bay->last_value) {
362d58b0c39SBenjamin Herrenschmidt bay->last_value = id;
363d58b0c39SBenjamin Herrenschmidt bay->value_count = 0;
364d58b0c39SBenjamin Herrenschmidt return;
365d58b0c39SBenjamin Herrenschmidt }
366d58b0c39SBenjamin Herrenschmidt if (id == bay->content_id)
367d58b0c39SBenjamin Herrenschmidt return;
368d58b0c39SBenjamin Herrenschmidt
3691da177e4SLinus Torvalds bay->value_count += msecs_to_jiffies(MB_POLL_DELAY);
3701da177e4SLinus Torvalds if (bay->value_count >= msecs_to_jiffies(MB_STABLE_DELAY)) {
3711da177e4SLinus Torvalds /* If the device type changes without going thru
3721da177e4SLinus Torvalds * "MB_NO", we force a pass by "MB_NO" to make sure
3731da177e4SLinus Torvalds * things are properly reset
3741da177e4SLinus Torvalds */
3751da177e4SLinus Torvalds if ((id != MB_NO) && (bay->content_id != MB_NO)) {
3761da177e4SLinus Torvalds id = MB_NO;
377d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: forcing MB_NO\n", bay->index);
3781da177e4SLinus Torvalds }
379d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: switching to %d\n", bay->index, id);
3801da177e4SLinus Torvalds set_mb_power(bay, id != MB_NO);
3811da177e4SLinus Torvalds bay->content_id = id;
382d58b0c39SBenjamin Herrenschmidt if (id >= MB_NO || id < 0)
383d58b0c39SBenjamin Herrenschmidt printk(KERN_INFO "mediabay%d: Bay is now empty\n", bay->index);
384d58b0c39SBenjamin Herrenschmidt else
385d58b0c39SBenjamin Herrenschmidt printk(KERN_INFO "mediabay%d: Bay contains %s\n",
386d58b0c39SBenjamin Herrenschmidt bay->index, mb_content_types[id]);
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds
check_media_bay(struct macio_dev * baydev)390d58b0c39SBenjamin Herrenschmidt int check_media_bay(struct macio_dev *baydev)
3911da177e4SLinus Torvalds {
392d58b0c39SBenjamin Herrenschmidt struct media_bay_info* bay;
393d58b0c39SBenjamin Herrenschmidt int id;
3941da177e4SLinus Torvalds
395d58b0c39SBenjamin Herrenschmidt if (baydev == NULL)
396d58b0c39SBenjamin Herrenschmidt return MB_NO;
3971da177e4SLinus Torvalds
398d58b0c39SBenjamin Herrenschmidt /* This returns an instant snapshot, not locking, sine
399d58b0c39SBenjamin Herrenschmidt * we may be called with the bay lock held. The resulting
400d58b0c39SBenjamin Herrenschmidt * fuzzyness of the result if called at the wrong time is
401d58b0c39SBenjamin Herrenschmidt * not actually a huge deal
402d58b0c39SBenjamin Herrenschmidt */
403d58b0c39SBenjamin Herrenschmidt bay = macio_get_drvdata(baydev);
404d58b0c39SBenjamin Herrenschmidt if (bay == NULL)
405d58b0c39SBenjamin Herrenschmidt return MB_NO;
406d58b0c39SBenjamin Herrenschmidt id = bay->content_id;
407d58b0c39SBenjamin Herrenschmidt if (bay->state != mb_up)
408d58b0c39SBenjamin Herrenschmidt return MB_NO;
409d58b0c39SBenjamin Herrenschmidt if (id == MB_FD1)
410d58b0c39SBenjamin Herrenschmidt return MB_FD;
411d58b0c39SBenjamin Herrenschmidt return id;
412d58b0c39SBenjamin Herrenschmidt }
413d58b0c39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(check_media_bay);
414d58b0c39SBenjamin Herrenschmidt
lock_media_bay(struct macio_dev * baydev)415d58b0c39SBenjamin Herrenschmidt void lock_media_bay(struct macio_dev *baydev)
4161da177e4SLinus Torvalds {
417d58b0c39SBenjamin Herrenschmidt struct media_bay_info* bay;
4181da177e4SLinus Torvalds
419d58b0c39SBenjamin Herrenschmidt if (baydev == NULL)
420d58b0c39SBenjamin Herrenschmidt return;
421d58b0c39SBenjamin Herrenschmidt bay = macio_get_drvdata(baydev);
422d58b0c39SBenjamin Herrenschmidt if (bay == NULL)
423d58b0c39SBenjamin Herrenschmidt return;
4249a24729dSDaniel Walker mutex_lock(&bay->lock);
425d58b0c39SBenjamin Herrenschmidt bay->user_lock = 1;
426d58b0c39SBenjamin Herrenschmidt }
427d58b0c39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(lock_media_bay);
4281da177e4SLinus Torvalds
unlock_media_bay(struct macio_dev * baydev)429d58b0c39SBenjamin Herrenschmidt void unlock_media_bay(struct macio_dev *baydev)
430d58b0c39SBenjamin Herrenschmidt {
431d58b0c39SBenjamin Herrenschmidt struct media_bay_info* bay;
4321da177e4SLinus Torvalds
433d58b0c39SBenjamin Herrenschmidt if (baydev == NULL)
434d58b0c39SBenjamin Herrenschmidt return;
435d58b0c39SBenjamin Herrenschmidt bay = macio_get_drvdata(baydev);
436d58b0c39SBenjamin Herrenschmidt if (bay == NULL)
437d58b0c39SBenjamin Herrenschmidt return;
438d58b0c39SBenjamin Herrenschmidt if (bay->user_lock) {
439d58b0c39SBenjamin Herrenschmidt bay->user_lock = 0;
4409a24729dSDaniel Walker mutex_unlock(&bay->lock);
441d58b0c39SBenjamin Herrenschmidt }
442d58b0c39SBenjamin Herrenschmidt }
443d58b0c39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(unlock_media_bay);
444d58b0c39SBenjamin Herrenschmidt
mb_broadcast_hotplug(struct device * dev,void * data)445d58b0c39SBenjamin Herrenschmidt static int mb_broadcast_hotplug(struct device *dev, void *data)
446d58b0c39SBenjamin Herrenschmidt {
447d58b0c39SBenjamin Herrenschmidt struct media_bay_info* bay = data;
448d58b0c39SBenjamin Herrenschmidt struct macio_dev *mdev;
449d58b0c39SBenjamin Herrenschmidt struct macio_driver *drv;
450d58b0c39SBenjamin Herrenschmidt int state;
451d58b0c39SBenjamin Herrenschmidt
452d58b0c39SBenjamin Herrenschmidt if (dev->bus != &macio_bus_type)
453d58b0c39SBenjamin Herrenschmidt return 0;
454d58b0c39SBenjamin Herrenschmidt
455d58b0c39SBenjamin Herrenschmidt state = bay->state == mb_up ? bay->content_id : MB_NO;
456d58b0c39SBenjamin Herrenschmidt if (state == MB_FD1)
457d58b0c39SBenjamin Herrenschmidt state = MB_FD;
458d58b0c39SBenjamin Herrenschmidt mdev = to_macio_device(dev);
459d58b0c39SBenjamin Herrenschmidt drv = to_macio_driver(dev->driver);
460d58b0c39SBenjamin Herrenschmidt if (dev->driver && drv->mediabay_event)
461d58b0c39SBenjamin Herrenschmidt drv->mediabay_event(mdev, state);
4621da177e4SLinus Torvalds return 0;
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds
media_bay_step(int i)465aacaf9bdSJon Loeliger static void media_bay_step(int i)
4661da177e4SLinus Torvalds {
4671da177e4SLinus Torvalds struct media_bay_info* bay = &media_bays[i];
4681da177e4SLinus Torvalds
4691da177e4SLinus Torvalds /* We don't poll when powering down */
4701da177e4SLinus Torvalds if (bay->state != mb_powering_down)
4711da177e4SLinus Torvalds poll_media_bay(bay);
4721da177e4SLinus Torvalds
473d58b0c39SBenjamin Herrenschmidt /* If timer expired run state machine */
474d58b0c39SBenjamin Herrenschmidt if (bay->timer != 0) {
4751da177e4SLinus Torvalds bay->timer -= msecs_to_jiffies(MB_POLL_DELAY);
4761da177e4SLinus Torvalds if (bay->timer > 0)
4771da177e4SLinus Torvalds return;
4781da177e4SLinus Torvalds bay->timer = 0;
4791da177e4SLinus Torvalds }
4801da177e4SLinus Torvalds
4811da177e4SLinus Torvalds switch(bay->state) {
4821da177e4SLinus Torvalds case mb_powering_up:
4831da177e4SLinus Torvalds if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
484d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: device not supported (kind:%d)\n",
485d58b0c39SBenjamin Herrenschmidt i, bay->content_id);
4861da177e4SLinus Torvalds set_mb_power(bay, 0);
4871da177e4SLinus Torvalds break;
4881da177e4SLinus Torvalds }
4891da177e4SLinus Torvalds bay->timer = msecs_to_jiffies(MB_RESET_DELAY);
4901da177e4SLinus Torvalds bay->state = mb_enabling_bay;
491d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
4921da177e4SLinus Torvalds break;
4931da177e4SLinus Torvalds case mb_enabling_bay:
4941da177e4SLinus Torvalds bay->ops->un_reset(bay);
4951da177e4SLinus Torvalds bay->timer = msecs_to_jiffies(MB_SETUP_DELAY);
4961da177e4SLinus Torvalds bay->state = mb_resetting;
497d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: releasing bay reset (kind:%d)\n",
498d58b0c39SBenjamin Herrenschmidt i, bay->content_id);
4991da177e4SLinus Torvalds break;
5001da177e4SLinus Torvalds case mb_resetting:
5011da177e4SLinus Torvalds if (bay->content_id != MB_CD) {
502d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: bay is up (kind:%d)\n", i,
503d58b0c39SBenjamin Herrenschmidt bay->content_id);
5041da177e4SLinus Torvalds bay->state = mb_up;
505d58b0c39SBenjamin Herrenschmidt device_for_each_child(&bay->mdev->ofdev.dev,
506d58b0c39SBenjamin Herrenschmidt bay, mb_broadcast_hotplug);
5071da177e4SLinus Torvalds break;
5081da177e4SLinus Torvalds }
509d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: releasing ATA reset (kind:%d)\n",
510d58b0c39SBenjamin Herrenschmidt i, bay->content_id);
5111da177e4SLinus Torvalds bay->ops->un_reset_ide(bay);
5121da177e4SLinus Torvalds bay->timer = msecs_to_jiffies(MB_IDE_WAIT);
5131da177e4SLinus Torvalds bay->state = mb_ide_resetting;
5141da177e4SLinus Torvalds break;
515d58b0c39SBenjamin Herrenschmidt
5161da177e4SLinus Torvalds case mb_ide_resetting:
517d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: bay is up (kind:%d)\n", i, bay->content_id);
5181da177e4SLinus Torvalds bay->state = mb_up;
519d58b0c39SBenjamin Herrenschmidt device_for_each_child(&bay->mdev->ofdev.dev,
520d58b0c39SBenjamin Herrenschmidt bay, mb_broadcast_hotplug);
5211da177e4SLinus Torvalds break;
522d58b0c39SBenjamin Herrenschmidt
5231da177e4SLinus Torvalds case mb_powering_down:
5241da177e4SLinus Torvalds bay->state = mb_empty;
525d58b0c39SBenjamin Herrenschmidt device_for_each_child(&bay->mdev->ofdev.dev,
526d58b0c39SBenjamin Herrenschmidt bay, mb_broadcast_hotplug);
527d58b0c39SBenjamin Herrenschmidt pr_debug("mediabay%d: end of power down\n", i);
5281da177e4SLinus Torvalds break;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds }
5311da177e4SLinus Torvalds
5321da177e4SLinus Torvalds /*
5331da177e4SLinus Torvalds * This procedure runs as a kernel thread to poll the media bay
5341da177e4SLinus Torvalds * once each tick and register and unregister the IDE interface
5351da177e4SLinus Torvalds * with the IDE driver. It needs to be a thread because
5361da177e4SLinus Torvalds * ide_register can't be called from interrupt context.
5371da177e4SLinus Torvalds */
media_bay_task(void * x)538aacaf9bdSJon Loeliger static int media_bay_task(void *x)
5391da177e4SLinus Torvalds {
5401da177e4SLinus Torvalds int i;
5411da177e4SLinus Torvalds
54233f6e794SPaul Mackerras while (!kthread_should_stop()) {
5431da177e4SLinus Torvalds for (i = 0; i < media_bay_count; ++i) {
5449a24729dSDaniel Walker mutex_lock(&media_bays[i].lock);
5451da177e4SLinus Torvalds if (!media_bays[i].sleeping)
5461da177e4SLinus Torvalds media_bay_step(i);
5479a24729dSDaniel Walker mutex_unlock(&media_bays[i].lock);
5481da177e4SLinus Torvalds }
5491da177e4SLinus Torvalds
5501da177e4SLinus Torvalds msleep_interruptible(MB_POLL_DELAY);
5511da177e4SLinus Torvalds }
55233f6e794SPaul Mackerras return 0;
5531da177e4SLinus Torvalds }
5541da177e4SLinus Torvalds
media_bay_attach(struct macio_dev * mdev,const struct of_device_id * match)5551da42fb6SGreg Kroah-Hartman static int media_bay_attach(struct macio_dev *mdev,
5561da42fb6SGreg Kroah-Hartman const struct of_device_id *match)
5571da177e4SLinus Torvalds {
5581da177e4SLinus Torvalds struct media_bay_info* bay;
5591da177e4SLinus Torvalds u32 __iomem *regbase;
5601da177e4SLinus Torvalds struct device_node *ofnode;
561cc5d0189SBenjamin Herrenschmidt unsigned long base;
5621da177e4SLinus Torvalds int i;
5631da177e4SLinus Torvalds
56461c7a080SGrant Likely ofnode = mdev->ofdev.dev.of_node;
5651da177e4SLinus Torvalds
5661da177e4SLinus Torvalds if (macio_resource_count(mdev) < 1)
5671da177e4SLinus Torvalds return -ENODEV;
5681da177e4SLinus Torvalds if (macio_request_resources(mdev, "media-bay"))
5691da177e4SLinus Torvalds return -EBUSY;
5701da177e4SLinus Torvalds /* Media bay registers are located at the beginning of the
571cc5d0189SBenjamin Herrenschmidt * mac-io chip, for now, we trick and align down the first
572cc5d0189SBenjamin Herrenschmidt * resource passed in
5731da177e4SLinus Torvalds */
574cc5d0189SBenjamin Herrenschmidt base = macio_resource_start(mdev, 0) & 0xffff0000u;
575cc5d0189SBenjamin Herrenschmidt regbase = (u32 __iomem *)ioremap(base, 0x100);
5761da177e4SLinus Torvalds if (regbase == NULL) {
5771da177e4SLinus Torvalds macio_release_resources(mdev);
5781da177e4SLinus Torvalds return -ENOMEM;
5791da177e4SLinus Torvalds }
5801da177e4SLinus Torvalds
5811da177e4SLinus Torvalds i = media_bay_count++;
5821da177e4SLinus Torvalds bay = &media_bays[i];
5831da177e4SLinus Torvalds bay->mdev = mdev;
5841da177e4SLinus Torvalds bay->base = regbase;
5851da177e4SLinus Torvalds bay->index = i;
5861da177e4SLinus Torvalds bay->ops = match->data;
5871da177e4SLinus Torvalds bay->sleeping = 0;
5889a24729dSDaniel Walker mutex_init(&bay->lock);
5891da177e4SLinus Torvalds
5901da177e4SLinus Torvalds /* Init HW probing */
5911da177e4SLinus Torvalds if (bay->ops->init)
5921da177e4SLinus Torvalds bay->ops->init(bay);
5931da177e4SLinus Torvalds
5941da177e4SLinus Torvalds printk(KERN_INFO "mediabay%d: Registered %s media-bay\n", i, bay->ops->name);
5951da177e4SLinus Torvalds
5961da177e4SLinus Torvalds /* Force an immediate detect */
5971da177e4SLinus Torvalds set_mb_power(bay, 0);
5981da177e4SLinus Torvalds msleep(MB_POWER_DELAY);
5991da177e4SLinus Torvalds bay->content_id = MB_NO;
6001da177e4SLinus Torvalds bay->last_value = bay->ops->content(bay);
6011da177e4SLinus Torvalds bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
6021da177e4SLinus Torvalds bay->state = mb_empty;
6031da177e4SLinus Torvalds
6041da177e4SLinus Torvalds /* Mark us ready by filling our mdev data */
6051da177e4SLinus Torvalds macio_set_drvdata(mdev, bay);
6061da177e4SLinus Torvalds
6071da177e4SLinus Torvalds /* Startup kernel thread */
6081da177e4SLinus Torvalds if (i == 0)
60933f6e794SPaul Mackerras kthread_run(media_bay_task, NULL, "media-bay");
6101da177e4SLinus Torvalds
6111da177e4SLinus Torvalds return 0;
6121da177e4SLinus Torvalds
6131da177e4SLinus Torvalds }
6141da177e4SLinus Torvalds
media_bay_suspend(struct macio_dev * mdev,pm_message_t state)615aacaf9bdSJon Loeliger static int media_bay_suspend(struct macio_dev *mdev, pm_message_t state)
6161da177e4SLinus Torvalds {
6171da177e4SLinus Torvalds struct media_bay_info *bay = macio_get_drvdata(mdev);
6181da177e4SLinus Torvalds
6193a2d5b70SRafael J. Wysocki if (state.event != mdev->ofdev.dev.power.power_state.event
6203a2d5b70SRafael J. Wysocki && (state.event & PM_EVENT_SLEEP)) {
6219a24729dSDaniel Walker mutex_lock(&bay->lock);
6221da177e4SLinus Torvalds bay->sleeping = 1;
6231da177e4SLinus Torvalds set_mb_power(bay, 0);
6249a24729dSDaniel Walker mutex_unlock(&bay->lock);
6251da177e4SLinus Torvalds msleep(MB_POLL_DELAY);
6261da177e4SLinus Torvalds mdev->ofdev.dev.power.power_state = state;
6271da177e4SLinus Torvalds }
6281da177e4SLinus Torvalds return 0;
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds
media_bay_resume(struct macio_dev * mdev)631aacaf9bdSJon Loeliger static int media_bay_resume(struct macio_dev *mdev)
6321da177e4SLinus Torvalds {
6331da177e4SLinus Torvalds struct media_bay_info *bay = macio_get_drvdata(mdev);
6341da177e4SLinus Torvalds
635ca078baeSPavel Machek if (mdev->ofdev.dev.power.power_state.event != PM_EVENT_ON) {
636829ca9a3SPavel Machek mdev->ofdev.dev.power.power_state = PMSG_ON;
6371da177e4SLinus Torvalds
6381da177e4SLinus Torvalds /* We re-enable the bay using it's previous content
6391da177e4SLinus Torvalds only if it did not change. Note those bozo timings,
6401da177e4SLinus Torvalds they seem to help the 3400 get it right.
6411da177e4SLinus Torvalds */
6421da177e4SLinus Torvalds /* Force MB power to 0 */
6439a24729dSDaniel Walker mutex_lock(&bay->lock);
6441da177e4SLinus Torvalds set_mb_power(bay, 0);
6451da177e4SLinus Torvalds msleep(MB_POWER_DELAY);
6461da177e4SLinus Torvalds if (bay->ops->content(bay) != bay->content_id) {
647d58b0c39SBenjamin Herrenschmidt printk("mediabay%d: Content changed during sleep...\n", bay->index);
6489a24729dSDaniel Walker mutex_unlock(&bay->lock);
6491da177e4SLinus Torvalds return 0;
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds set_mb_power(bay, 1);
6521da177e4SLinus Torvalds bay->last_value = bay->content_id;
6531da177e4SLinus Torvalds bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
6541da177e4SLinus Torvalds bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
6551da177e4SLinus Torvalds do {
6561da177e4SLinus Torvalds msleep(MB_POLL_DELAY);
6571da177e4SLinus Torvalds media_bay_step(bay->index);
6581da177e4SLinus Torvalds } while((bay->state != mb_empty) &&
6591da177e4SLinus Torvalds (bay->state != mb_up));
6601da177e4SLinus Torvalds bay->sleeping = 0;
6619a24729dSDaniel Walker mutex_unlock(&bay->lock);
6621da177e4SLinus Torvalds }
6631da177e4SLinus Torvalds return 0;
6641da177e4SLinus Torvalds }
6651da177e4SLinus Torvalds
6661da177e4SLinus Torvalds
6671da177e4SLinus Torvalds /* Definitions of "ops" structures.
6681da177e4SLinus Torvalds */
669306e352aSArnd Bergmann static const struct mb_ops ohare_mb_ops = {
6701da177e4SLinus Torvalds .name = "Ohare",
6711da177e4SLinus Torvalds .content = ohare_mb_content,
6721da177e4SLinus Torvalds .power = ohare_mb_power,
6731da177e4SLinus Torvalds .setup_bus = ohare_mb_setup_bus,
6741da177e4SLinus Torvalds .un_reset = ohare_mb_un_reset,
6751da177e4SLinus Torvalds .un_reset_ide = ohare_mb_un_reset_ide,
6761da177e4SLinus Torvalds };
6771da177e4SLinus Torvalds
678306e352aSArnd Bergmann static const struct mb_ops heathrow_mb_ops = {
6791da177e4SLinus Torvalds .name = "Heathrow",
6801da177e4SLinus Torvalds .content = heathrow_mb_content,
6811da177e4SLinus Torvalds .power = heathrow_mb_power,
6821da177e4SLinus Torvalds .setup_bus = heathrow_mb_setup_bus,
6831da177e4SLinus Torvalds .un_reset = heathrow_mb_un_reset,
6841da177e4SLinus Torvalds .un_reset_ide = heathrow_mb_un_reset_ide,
6851da177e4SLinus Torvalds };
6861da177e4SLinus Torvalds
687306e352aSArnd Bergmann static const struct mb_ops keylargo_mb_ops = {
6881da177e4SLinus Torvalds .name = "KeyLargo",
6891da177e4SLinus Torvalds .init = keylargo_mb_init,
6901da177e4SLinus Torvalds .content = keylargo_mb_content,
6911da177e4SLinus Torvalds .power = keylargo_mb_power,
6921da177e4SLinus Torvalds .setup_bus = keylargo_mb_setup_bus,
6931da177e4SLinus Torvalds .un_reset = keylargo_mb_un_reset,
6941da177e4SLinus Torvalds .un_reset_ide = keylargo_mb_un_reset_ide,
6951da177e4SLinus Torvalds };
6961da177e4SLinus Torvalds
6971da177e4SLinus Torvalds /*
6981da177e4SLinus Torvalds * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
6991da177e4SLinus Torvalds * register is always set when there is something in the media bay.
7001da177e4SLinus Torvalds * This causes problems for the interrupt code if we attach an interrupt
7011da177e4SLinus Torvalds * handler to the media-bay interrupt, because it tends to go into
7021da177e4SLinus Torvalds * an infinite loop calling the media bay interrupt handler.
7031da177e4SLinus Torvalds * Therefore we do it all by polling the media bay once each tick.
7041da177e4SLinus Torvalds */
7051da177e4SLinus Torvalds
7068cffe0b0SXiang wangx static const struct of_device_id media_bay_match[] =
7071da177e4SLinus Torvalds {
7081da177e4SLinus Torvalds {
7091da177e4SLinus Torvalds .name = "media-bay",
7101da177e4SLinus Torvalds .compatible = "keylargo-media-bay",
7111da177e4SLinus Torvalds .data = &keylargo_mb_ops,
7121da177e4SLinus Torvalds },
7131da177e4SLinus Torvalds {
7141da177e4SLinus Torvalds .name = "media-bay",
7151da177e4SLinus Torvalds .compatible = "heathrow-media-bay",
7161da177e4SLinus Torvalds .data = &heathrow_mb_ops,
7171da177e4SLinus Torvalds },
7181da177e4SLinus Torvalds {
7191da177e4SLinus Torvalds .name = "media-bay",
7201da177e4SLinus Torvalds .compatible = "ohare-media-bay",
7211da177e4SLinus Torvalds .data = &ohare_mb_ops,
7221da177e4SLinus Torvalds },
7231da177e4SLinus Torvalds {},
7241da177e4SLinus Torvalds };
7251da177e4SLinus Torvalds
7261da177e4SLinus Torvalds static struct macio_driver media_bay_driver =
7271da177e4SLinus Torvalds {
728c2cdf6abSBenjamin Herrenschmidt .driver = {
7291da177e4SLinus Torvalds .name = "media-bay",
730c2cdf6abSBenjamin Herrenschmidt .of_match_table = media_bay_match,
731c2cdf6abSBenjamin Herrenschmidt },
7321da177e4SLinus Torvalds .probe = media_bay_attach,
7331da177e4SLinus Torvalds .suspend = media_bay_suspend,
7341da177e4SLinus Torvalds .resume = media_bay_resume
7351da177e4SLinus Torvalds };
7361da177e4SLinus Torvalds
media_bay_init(void)7371da177e4SLinus Torvalds static int __init media_bay_init(void)
7381da177e4SLinus Torvalds {
7391da177e4SLinus Torvalds int i;
7401da177e4SLinus Torvalds
7411da177e4SLinus Torvalds for (i=0; i<MAX_BAYS; i++) {
7421da177e4SLinus Torvalds memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
7431da177e4SLinus Torvalds media_bays[i].content_id = -1;
7441da177e4SLinus Torvalds }
745e8222502SBenjamin Herrenschmidt if (!machine_is(powermac))
746e8222502SBenjamin Herrenschmidt return 0;
7471da177e4SLinus Torvalds
7481da177e4SLinus Torvalds macio_register_driver(&media_bay_driver);
7491da177e4SLinus Torvalds
7501da177e4SLinus Torvalds return 0;
7511da177e4SLinus Torvalds }
7521da177e4SLinus Torvalds
7531da177e4SLinus Torvalds device_initcall(media_bay_init);
754