patch-2.3.43 linux/drivers/macintosh/mediabay.c
Next file: linux/drivers/macintosh/nvram.c
Previous file: linux/drivers/macintosh/macserial.c
Back to the patch index
Back to the overall index
- Lines: 638
- Date:
Wed Feb 9 19:43:53 2000
- Orig file:
v2.3.42/linux/drivers/macintosh/mediabay.c
- Orig date:
Fri Oct 15 15:25:13 1999
diff -u --recursive --new-file v2.3.42/linux/drivers/macintosh/mediabay.c linux/drivers/macintosh/mediabay.c
@@ -38,6 +38,14 @@
#endif
#undef MB_USE_INTERRUPTS
+#undef MB_DEBUG
+#define MB_IGNORE_SIGNALS
+
+#ifdef MB_DEBUG
+#define MBDBG(fmt, arg...) printk(KERN_INFO fmt , ## arg)
+#else
+#define MBDBG(fmt, arg...) do { } while (0)
+#endif
struct media_bay_hw {
unsigned char b0;
@@ -49,17 +57,16 @@
struct media_bay_info {
volatile struct media_bay_hw* addr;
int content_id;
- int previous_id;
- int ready;
+ int state;
int last_value;
int value_count;
- int reset_timer;
+ int timer;
struct device_node* dev_node;
#ifdef CONFIG_BLK_DEV_IDE
unsigned long cd_base;
int cd_index;
int cd_irq;
- int cd_timer;
+ int cd_retry;
#endif
};
@@ -73,31 +80,79 @@
#ifdef CONFIG_BLK_DEV_IDE
/* check the busy bit in the media-bay ide interface
(assumes the media-bay contains an ide device) */
+//#define MB_IDE_READY(i) ((inb(media_bays[i].cd_base + 0x70) & 0xc0) == 0x40)
#define MB_IDE_READY(i) ((inb(media_bays[i].cd_base + 0x70) & 0x80) == 0)
#endif
+/* Note: All delays are not in milliseconds and converted to HZ relative
+ * values by the macro below
+ */
+#define MS_TO_HZ(ms) ((ms * HZ) / 1000)
+
/*
* Consider the media-bay ID value stable if it is the same for
- * this many consecutive samples (at intervals of 1/HZ seconds).
+ * this number of milliseconds
*/
-#define MB_STABLE_COUNT 4
+#define MB_STABLE_DELAY 40
+
+/* Wait after powering up the media bay this delay in ms
+ * timeout bumped for some powerbooks
+ */
+#define MB_POWER_DELAY 200
/*
* Hold the media-bay reset signal true for this many ticks
* after a device is inserted before releasing it.
*/
-#define MB_RESET_COUNT 40
+#define MB_RESET_DELAY 40
+
+/*
+ * Wait this long after the reset signal is released and before doing
+ * further operations. After this delay, the IDE reset signal is released
+ * too for an IDE device
+ */
+#define MB_SETUP_DELAY 100
/*
* 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.
+ * (or until the device is ready) before waiting for busy bit to disappear
+ */
+#define MB_IDE_WAIT 1000
+
+/*
+ * Timeout waiting for busy bit of an IDE device to go down
+ */
+#define MB_IDE_TIMEOUT 5000
+
+/*
+ * Max retries of the full power up/down sequence for an IDE device
+ */
+#define MAX_CD_RETRIES 3
+
+/*
+ * States of a media bay
*/
-#define MB_IDE_WAIT 1500
+enum {
+ mb_empty = 0, /* Idle */
+ mb_powering_up, /* power bit set, waiting MB_POWER_DELAY */
+ mb_enabling_bay, /* enable bits set, waiting MB_RESET_DELAY */
+ mb_resetting, /* reset bit unset, waiting MB_SETUP_DELAY */
+ mb_ide_resetting, /* IDE reset bit unser, waiting MB_IDE_WAIT */
+ mb_ide_waiting, /* Waiting for BUSY bit to go away until MB_IDE_TIMEOUT */
+ mb_up, /* Media bay full */
+ mb_powering_down /* Powering down (avoid too fast down/up) */
+};
static void poll_media_bay(int which);
static void set_media_bay(int which, int id);
+static void set_mb_power(int which, int onoff);
+static void media_bay_step(int i);
static int media_bay_task(void *);
+#ifdef MB_USE_INTERRUPTS
+static void media_bay_intr(int irq, void *devid, struct pt_regs *regs);
+#endif
+
/*
* 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.
@@ -113,8 +168,7 @@
struct device_node *np;
int n,i;
- for (i=0; i<MAX_BAYS; i++)
- {
+ for (i=0; i<MAX_BAYS; i++) {
memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
media_bays[i].content_id = -1;
#ifdef CONFIG_BLK_DEV_IDE
@@ -124,40 +178,43 @@
np = find_devices("media-bay");
n = 0;
- while(np && (n<MAX_BAYS))
- {
+ while(np && (n<MAX_BAYS)) {
if (np->n_addrs == 0)
continue;
media_bays[n].addr = (volatile struct media_bay_hw *)
ioremap(np->addrs[0].address, sizeof(struct media_bay_hw));
#ifdef MB_USE_INTERRUPTS
- if (np->n_intrs == 0)
- {
+ if (np->n_intrs == 0) {
printk(KERN_ERR "media bay %d has no irq\n",n);
continue;
}
- if (request_irq(np_intrs[0].line, media_bay_intr, 0, "Media bay", NULL))
- {
- printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n", irq, n);
+ if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
+ printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
+ np->intrs[0].line, n);
continue;
}
#endif
media_bay_count++;
- set_media_bay(n, MB_CONTENTS(n));
- if (media_bays[n].content_id != MB_NO) {
- feature_clear(media_bays[n].dev_node, FEATURE_Mediabay_reset);
- udelay(500);
- }
- media_bays[n].ready = 1;
- media_bays[n].previous_id = media_bays[n].content_id;
- media_bays[n].reset_timer = 0;
media_bays[n].dev_node = np;
-#ifdef CONFIG_BLK_DEV_IDE
- media_bays[n].cd_timer = 0;
-#endif
+
+ /* Force an immediate detect */
+ set_mb_power(n,0);
+ mdelay(MB_POWER_DELAY);
+ out_8(&media_bays[n].addr->contents, 0x70);
+ mdelay(MB_STABLE_DELAY);
+ media_bays[n].content_id = MB_NO;
+ media_bays[n].last_value = MB_CONTENTS(n);
+ media_bays[n].value_count = MS_TO_HZ(MB_STABLE_DELAY);
+ media_bays[n].state = mb_empty;
+ do {
+ mdelay(1000/HZ);
+ media_bay_step(n);
+ } while((media_bays[n].state != mb_empty) &&
+ (media_bays[n].state != mb_up));
+
n++;
np=np->next;
}
@@ -174,17 +231,66 @@
}
}
-#if 0
-static void
+#ifdef MB_USE_INTERRUPTS
+static void __pmac
media_bay_intr(int irq, void *devid, struct pt_regs *regs)
{
- int id = MB_CONTENTS();
-
- if (id == MB_NO)
- set_media_bay(id);
}
#endif
+static void __pmac
+set_mb_power(int which, int onoff)
+{
+ volatile struct media_bay_info* mb = &media_bays[which];
+
+ if (onoff) {
+ feature_set(mb->dev_node, FEATURE_Mediabay_power);
+ udelay(10);
+ feature_set(mb->dev_node, FEATURE_Mediabay_reset);
+ udelay(10);
+ mb->state = mb_powering_up;
+ MBDBG("mediabay%d: powering up\n", which);
+ } else {
+ feature_clear(mb->dev_node, FEATURE_Mediabay_floppy_enable);
+ feature_clear(mb->dev_node, FEATURE_Mediabay_IDE_enable);
+ feature_clear(mb->dev_node, FEATURE_Mediabay_PCI_enable);
+ feature_clear(mb->dev_node, FEATURE_SWIM3_enable);
+ feature_clear(mb->dev_node, FEATURE_Mediabay_power);
+ mb->state = mb_powering_down;
+ MBDBG("mediabay%d: powering down\n", which);
+ }
+ mb->timer = MS_TO_HZ(MB_POWER_DELAY);
+}
+
+static void __pmac
+set_media_bay(int which, int id)
+{
+ volatile struct media_bay_info* bay;
+
+ bay = &media_bays[which];
+
+ switch (id) {
+ case MB_CD:
+ feature_set(bay->dev_node, FEATURE_Mediabay_IDE_enable);
+ udelay(10);
+ feature_set(bay->dev_node, FEATURE_Mediabay_IDE_reset);
+ printk(KERN_INFO "media bay %d contains a CD-ROM drive\n", which);
+ break;
+ case MB_FD:
+ case MB_FD1:
+ feature_set(bay->dev_node, FEATURE_Mediabay_floppy_enable);
+ feature_set(bay->dev_node, FEATURE_SWIM3_enable);
+ printk(KERN_INFO "media bay %d contains a floppy disk drive\n", which);
+ break;
+ case MB_NO:
+ break;
+ default:
+ printk(KERN_INFO "media bay %d contains an unknown device (%d)\n",
+ which, id);
+ break;
+ }
+}
+
int __pmac
check_media_bay(struct device_node *which_bay, int what)
{
@@ -194,7 +300,7 @@
for (i=0; i<media_bay_count; i++)
if (which_bay == media_bays[i].dev_node)
{
- if ((what == media_bays[i].content_id) && media_bays[i].ready)
+ if ((what == media_bays[i].content_id) && media_bays[i].state == mb_up)
return 0;
media_bays[i].cd_index = -1;
return -EINVAL;
@@ -212,7 +318,7 @@
for (i=0; i<media_bay_count; i++)
if (base == media_bays[i].cd_base)
{
- if ((what == media_bays[i].content_id) && media_bays[i].ready)
+ if ((what == media_bays[i].content_id) && media_bays[i].state == mb_up)
return 0;
media_bays[i].cd_index = -1;
return -EINVAL;
@@ -232,17 +338,135 @@
for (i=0; i<media_bay_count; i++)
if (which_bay == media_bays[i].dev_node)
{
+ int timeout = 5000;
+
media_bays[i].cd_base = base;
media_bays[i].cd_irq = irq;
- media_bays[i].cd_index = index;
+
+ if ((MB_CD != media_bays[i].content_id) || media_bays[i].state != mb_up)
+ return 0;
+
printk(KERN_DEBUG "Registered ide %d for media bay %d\n", index, i);
- return 0;
+ do {
+ if (MB_IDE_READY(i)) {
+ media_bays[i].cd_index = index;
+ return 0;
+ }
+ mdelay(1);
+ } while(--timeout);
+ printk(KERN_DEBUG "Timeout waiting IDE in bay %d\n", i);
+ return -ENODEV;
}
#endif
return -ENODEV;
}
+static void __pmac
+media_bay_step(int i)
+{
+ volatile struct media_bay_info* bay = &media_bays[i];
+
+ /* We don't poll when powering down */
+ if (bay->state != mb_powering_down)
+ poll_media_bay(i);
+
+ /* If timer expired or polling IDE busy, run state machine */
+ if ((bay->state != mb_ide_waiting) && (bay->timer != 0) && ((--bay->timer) != 0))
+ return;
+
+ switch(bay->state) {
+ case mb_powering_up:
+ set_media_bay(i, bay->last_value);
+ bay->timer = MS_TO_HZ(MB_RESET_DELAY);
+ bay->state = mb_enabling_bay;
+ MBDBG("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
+ break;
+ case mb_enabling_bay:
+ feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
+ bay->timer = MS_TO_HZ(MB_SETUP_DELAY);
+ bay->state = mb_resetting;
+ MBDBG("mediabay%d: waiting reset (kind:%d)\n", i, bay->content_id);
+ break;
+
+ case mb_resetting:
+ if (bay->content_id != MB_CD) {
+ MBDBG("mediabay%d: bay is up (kind:%d)\n", i, bay->content_id);
+ bay->state = mb_up;
+ break;
+ }
+#ifdef CONFIG_BLK_DEV_IDE
+ MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id);
+ feature_clear(bay->dev_node, FEATURE_Mediabay_IDE_reset);
+ bay->timer = MS_TO_HZ(MB_IDE_WAIT);
+ bay->state = mb_ide_resetting;
+#else
+ printk(KERN_DEBUG "media-bay %d is ide (not compiled in kernel)\n", i);
+ set_mb_power(i, 0);
+#endif // #ifdef CONFIG_BLK_DEV_IDE
+ break;
+
+#ifdef CONFIG_BLK_DEV_IDE
+ case mb_ide_resetting:
+ bay->timer = MS_TO_HZ(MB_IDE_TIMEOUT);
+ bay->state = mb_ide_waiting;
+ MBDBG("mediabay%d: waiting IDE ready (kind:%d)\n", i, bay->content_id);
+ break;
+
+ case mb_ide_waiting:
+ if (bay->cd_base == 0) {
+ bay->timer = 0;
+ bay->state = mb_up;
+ MBDBG("mediabay%d: up before IDE init\n", i);
+ break;
+ } else if (MB_IDE_READY(i)) {
+ bay->timer = 0;
+ bay->state = mb_up;
+ if (bay->cd_index < 0)
+ bay->cd_index = ide_register(bay->cd_base, 0, bay->cd_irq);
+ if (bay->cd_index == -1) {
+ /* We eventually do a retry */
+ bay->cd_retry++;
+ printk("IDE register error\n");
+ set_mb_power(i, 0);
+ } else {
+ printk(KERN_DEBUG "media-bay %d is ide %d\n", i, bay->cd_index);
+ MBDBG("mediabay %d IDE ready\n", i);
+ }
+ break;
+ }
+ if (bay->timer == 0) {
+ printk("\nIDE Timeout in bay %d !\n", i);
+ MBDBG("mediabay%d: nIDE Timeout !\n", i);
+ set_mb_power(i, 0);
+ }
+ break;
+#endif // #ifdef CONFIG_BLK_DEV_IDE
+
+ case mb_powering_down:
+ bay->state = mb_empty;
+#ifdef CONFIG_BLK_DEV_IDE
+ if (bay->cd_index >= 0) {
+ printk(KERN_DEBUG "Unregistering mb %d ide, index:%d\n", i,
+ bay->cd_index);
+ ide_unregister(bay->cd_index);
+ bay->cd_index = -1;
+ }
+ if (bay->cd_retry) {
+ if (bay->cd_retry > MAX_CD_RETRIES) {
+ /* Should add an error sound (sort of beep in dmasound) */
+ printk("\nmedia-bay %d, IDE device badly inserted or unrecognised\n", i);
+ } else {
+ /* Force a new power down/up sequence */
+ bay->content_id = MB_NO;
+ }
+ }
+#endif
+ MBDBG("mediabay%d: end of power down\n", i);
+ break;
+ }
+}
+
/*
* This procedure runs as a kernel thread to poll the media bay
* once each tick and register and unregister the IDE interface
@@ -252,123 +476,57 @@
int __pmac
media_bay_task(void *x)
{
- volatile struct media_bay_info* bay;
int i = 0;
strcpy(current->comm, "media-bay");
- for (;;)
- {
- bay = &media_bays[i];
- poll_media_bay(i);
- if (bay->content_id != bay->previous_id) {
- bay->reset_timer = (bay->content_id != MB_NO) ?
- MB_RESET_COUNT: 0;
- bay->ready = 0;
-#ifdef CONFIG_BLK_DEV_IDE
- bay->cd_timer = 0;
- if (bay->content_id != MB_CD && bay->cd_index >= 0) {
- printk(KERN_DEBUG "Unregistering mb %d ide, index:%d\n", i, bay->cd_index);
- ide_unregister(bay->cd_index);
- bay->cd_index = -1;
- }
-#endif
- } else if (bay->reset_timer) {
- if (--bay->reset_timer == 0) {
- feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
- bay->ready = 1;
-#ifdef CONFIG_BLK_DEV_IDE
- bay->cd_timer = 0;
- if (bay->content_id == MB_CD && bay->cd_base != 0)
- bay->cd_timer = MB_IDE_WAIT;
-#endif
- }
-#ifdef CONFIG_BLK_DEV_IDE
- } else if (bay->cd_timer && (--bay->cd_timer == 0 || MB_IDE_READY(i))
- && bay->cd_index < 0) {
- bay->cd_timer = 0;
- printk(KERN_DEBUG "Registering IDE, base:0x%08lx, irq:%d\n", bay->cd_base, bay->cd_irq);
- printk("\n");
- bay->cd_index = ide_register(bay->cd_base, 0, bay->cd_irq);
- if (bay->cd_index == -1)
- printk("\nCD-ROM badly inserted. Remove it and try again !\n");
- else
- printk(KERN_DEBUG "media-bay %d is ide %d\n", i, bay->cd_index);
+#ifdef MB_IGNORE_SIGNALS
+ sigfillset(¤t->blocked);
#endif
- }
- bay->previous_id = bay->content_id;
+ for (;;) {
+ media_bay_step(i);
+
+ if (++i >= media_bay_count) {
+ i = 0;
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
if (signal_pending(current))
return 0;
- i = (i+1)%media_bay_count;
+ }
}
}
void __pmac
poll_media_bay(int which)
{
+ volatile struct media_bay_info* bay = &media_bays[which];
int id = MB_CONTENTS(which);
- if (id == media_bays[which].last_value) {
- if (id != media_bays[which].content_id
- && ++media_bays[which].value_count >= MB_STABLE_COUNT) {
+ if (id == bay->last_value) {
+ if (id != bay->content_id
+ && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
/* If the device type changes without going thru "MB_NO", we force
a pass by "MB_NO" to make sure things are properly reset */
- if ((id != MB_NO) && (media_bays[which].content_id != MB_NO)) {
- set_media_bay(which, MB_NO);
- udelay(500);
+ if ((id != MB_NO) && (bay->content_id != MB_NO)) {
+ id = MB_NO;
+ MBDBG("mediabay%d: forcing MB_NO\n", which);
+ }
+ MBDBG("mediabay%d: switching to %d\n", which, id);
+ set_mb_power(which, id != MB_NO);
+ bay->content_id = id;
+ if (id == MB_NO) {
+#ifdef CONFIG_BLK_DEV_IDE
+ bay->cd_retry = 0;
+#endif
+ printk(KERN_INFO "media bay %d is empty\n", which);
}
- set_media_bay(which, id);
}
} else {
- media_bays[which].last_value = id;
- media_bays[which].value_count = 0;
+ bay->last_value = id;
+ bay->value_count = 0;
}
}
-static void __pmac
-set_media_bay(int which, int id)
-{
- volatile struct media_bay_info* bay;
-
- bay = &media_bays[which];
-
- bay->content_id = id;
- bay->last_value = id;
-
- switch (id) {
- case MB_CD:
- feature_set(bay->dev_node, FEATURE_Mediabay_enable);
- feature_set(bay->dev_node, FEATURE_Mediabay_IDE_enable);
- udelay(500);
- feature_set(bay->dev_node, FEATURE_CD_power);
- printk(KERN_INFO "media bay %d contains a CD-ROM drive\n", which);
- break;
- case MB_FD:
- feature_set(bay->dev_node, FEATURE_Mediabay_enable);
- feature_set(bay->dev_node, FEATURE_Mediabay_floppy_enable);
- feature_set(bay->dev_node, FEATURE_SWIM3_enable);
- printk(KERN_INFO "media bay %d contains a floppy disk drive\n", which);
- break;
- case MB_NO:
- feature_clear(bay->dev_node, FEATURE_CD_power);
- feature_clear(bay->dev_node, FEATURE_Mediabay_enable);
- feature_clear(bay->dev_node, FEATURE_Mediabay_floppy_enable);
- feature_clear(bay->dev_node, FEATURE_Mediabay_IDE_enable);
- feature_clear(bay->dev_node, FEATURE_SWIM3_enable);
- feature_set(bay->dev_node, FEATURE_Mediabay_reset);
- printk(KERN_INFO "media bay %d is empty\n", which);
- break;
- default:
- feature_set(bay->dev_node, FEATURE_Mediabay_enable);
- printk(KERN_INFO "media bay %d contains an unknown device (%d)\n",
- which, id);
- break;
- }
-
- udelay(500);
-}
#ifdef CONFIG_PMAC_PBOOK
/*
@@ -388,42 +546,34 @@
case PBOOK_SLEEP_NOW:
for (i=0; i<media_bay_count; i++) {
bay = &media_bays[i];
- feature_clear(bay->dev_node, FEATURE_Mediabay_enable);
- feature_clear(bay->dev_node, FEATURE_Mediabay_IDE_enable);
- feature_clear(bay->dev_node, FEATURE_SWIM3_enable);
- feature_clear(bay->dev_node, FEATURE_Mediabay_floppy_enable);
- feature_set(bay->dev_node, FEATURE_Mediabay_reset);
- feature_clear(bay->dev_node, FEATURE_CD_power);
- out_8(&media_bays[i].addr->contents, 0x70);
+ set_mb_power(i, 0);
+ mdelay(10);
+ out_8(&bay->addr->contents, 0x70);
}
break;
case PBOOK_WAKE:
for (i=0; i<media_bay_count; i++) {
bay = &media_bays[i];
- feature_set(bay->dev_node, FEATURE_Mediabay_enable);
- /* I suppose this is enough delay to stabilize MB_CONTENT ... */
- mdelay(10);
- /* We re-enable the bay using it's previous content only if
- it did not change */
- if (MB_CONTENTS(i) == bay->content_id) {
- set_media_bay(i, bay->content_id);
- if (bay->content_id != MB_NO) {
- mdelay(400);
- /* Clear the bay reset */
- feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
- /* This small delay makes sure the device has time
- to assert the BUSY bit (used by IDE sleep) */
- udelay(100);
- /* We reset the state machine timers in case we were in the
- middle of a wait loop */
- if (bay->reset_timer)
- bay->reset_timer = MB_RESET_COUNT;
-#ifdef CONFIG_BLK_DEV_IDE
- if (bay->cd_timer)
- bay->cd_timer = MB_IDE_WAIT;
-#endif
- }
- }
+ /* We re-enable the bay using it's previous content
+ only if it did not change. Note those bozo timings, they seem
+ to help the 3400 get it right
+ */
+ mdelay(MB_STABLE_DELAY);
+ out_8(&bay->addr->contents, 0x70);
+ mdelay(MB_STABLE_DELAY);
+ if (MB_CONTENTS(i) != bay->content_id)
+ continue;
+ set_mb_power(i, 1);
+ mdelay(MB_POWER_DELAY);
+ media_bays[i].last_value = bay->content_id;
+ media_bays[i].value_count = MS_TO_HZ(MB_STABLE_DELAY);
+ media_bays[i].timer = 0;
+ media_bays[i].cd_retry = 0;
+ do {
+ mdelay(1000/HZ);
+ media_bay_step(i);
+ } while((media_bays[i].state != mb_empty) &&
+ (media_bays[i].state != mb_up));
}
break;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)