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