/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include 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)); }