Blame view

drivers/macintosh/mediabay.c 18.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * Driver for the media bay on the PowerBook 3400 and 2400.
   *
   * Copyright (C) 1998 Paul Mackerras.
   *
   * Various evolutions by Benjamin Herrenschmidt & Henry Worth
   *
   *  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.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/kernel.h>
  #include <linux/delay.h>
  #include <linux/sched.h>
  #include <linux/timer.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  #include <linux/stddef.h>
  #include <linux/init.h>
33f6e7940   Paul Mackerras   [POWERPC] Convert...
21
  #include <linux/kthread.h>
9a24729d8   Daniel Walker   macintosh/media b...
22
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
29
30
31
32
33
34
  #include <asm/prom.h>
  #include <asm/pgtable.h>
  #include <asm/io.h>
  #include <asm/machdep.h>
  #include <asm/pmac_feature.h>
  #include <asm/mediabay.h>
  #include <asm/sections.h>
  #include <asm/ohare.h>
  #include <asm/heathrow.h>
  #include <asm/keylargo.h>
  #include <linux/adb.h>
  #include <linux/pmu.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  #define MB_FCR32(bay, r)	((bay)->base + ((r) >> 2))
  #define MB_FCR8(bay, r)		(((volatile u8 __iomem *)((bay)->base)) + (r))
  
  #define MB_IN32(bay,r)		(in_le32(MB_FCR32(bay,r)))
  #define MB_OUT32(bay,r,v)	(out_le32(MB_FCR32(bay,r), (v)))
  #define MB_BIS(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
  #define MB_BIC(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
  #define MB_IN8(bay,r)		(in_8(MB_FCR8(bay,r)))
  #define MB_OUT8(bay,r,v)	(out_8(MB_FCR8(bay,r), (v)))
  
  struct media_bay_info;
  
  struct mb_ops {
  	char*	name;
  	void	(*init)(struct media_bay_info *bay);
  	u8	(*content)(struct media_bay_info *bay);
  	void	(*power)(struct media_bay_info *bay, int on_off);
  	int	(*setup_bus)(struct media_bay_info *bay, u8 device_id);
  	void	(*un_reset)(struct media_bay_info *bay);
  	void	(*un_reset_ide)(struct media_bay_info *bay);
  };
  
  struct media_bay_info {
  	u32 __iomem			*base;
  	int				content_id;
  	int				state;
  	int				last_value;
  	int				value_count;
  	int				timer;
  	struct macio_dev		*mdev;
  	struct mb_ops*			ops;
  	int				index;
  	int				cached_gpio;
  	int				sleeping;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
69
  	int				user_lock;
9a24729d8   Daniel Walker   macintosh/media b...
70
  	struct mutex			lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
  };
  
  #define MAX_BAYS	2
  
  static struct media_bay_info media_bays[MAX_BAYS];
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
76
  static int media_bay_count = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  
  /*
   * Wait that number of ms between each step in normal polling mode
   */
  #define MB_POLL_DELAY	25
  
  /*
   * Consider the media-bay ID value stable if it is the same for
   * this number of milliseconds
   */
  #define MB_STABLE_DELAY	100
  
  /* 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_DELAY	50
  
  /*
   * 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
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
109
   * (or until the device is ready) before calling into the driver
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
   */
  #define MB_IDE_WAIT	1000
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
117
118
119
120
121
   * States of a media bay
   */
  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 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  	mb_up,			/* Media bay full */
  	mb_powering_down	/* Powering down (avoid too fast down/up) */
  };
  
  #define MB_POWER_SOUND		0x08
  #define MB_POWER_FLOPPY		0x04
  #define MB_POWER_ATA		0x02
  #define MB_POWER_PCI		0x01
  #define MB_POWER_OFF		0x00
  
  /*
   * Functions for polling content of media bay
   */
   
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
136
  static u8
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
  ohare_mb_content(struct media_bay_info *bay)
  {
  	return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
141
  static u8
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
  heathrow_mb_content(struct media_bay_info *bay)
  {
  	return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
146
  static u8
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  keylargo_mb_content(struct media_bay_info *bay)
  {
  	int new_gpio;
  
  	new_gpio = MB_IN8(bay, KL_GPIO_MEDIABAY_IRQ) & KEYLARGO_GPIO_INPUT_DATA;
  	if (new_gpio) {
  		bay->cached_gpio = new_gpio;
  		return MB_NO;
  	} else if (bay->cached_gpio != new_gpio) {
  		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
  		(void)MB_IN32(bay, KEYLARGO_MBCR);
  		udelay(5);
  		MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
  		(void)MB_IN32(bay, KEYLARGO_MBCR);
  		udelay(5);
  		bay->cached_gpio = new_gpio;
  	}
  	return (MB_IN32(bay, KEYLARGO_MBCR) >> 4) & 7;
  }
  
  /*
   * Functions for powering up/down the bay, puts the bay device
   * into reset state as well
   */
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
171
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  ohare_mb_power(struct media_bay_info* bay, int on_off)
  {
  	if (on_off) {
  		/* Power up device, assert it's reset line */
  		MB_BIC(bay, OHARE_FCR, OH_BAY_RESET_N);
  		MB_BIC(bay, OHARE_FCR, OH_BAY_POWER_N);
  	} else {
  		/* Disable all devices */
  		MB_BIC(bay, OHARE_FCR, OH_BAY_DEV_MASK);
  		MB_BIC(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
  		/* Cut power from bay, release reset line */
  		MB_BIS(bay, OHARE_FCR, OH_BAY_POWER_N);
  		MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
  		MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
  	}
  	MB_BIC(bay, OHARE_MBCR, 0x00000F00);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
189
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  heathrow_mb_power(struct media_bay_info* bay, int on_off)
  {
  	if (on_off) {
  		/* Power up device, assert it's reset line */
  		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
  		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
  	} else {
  		/* Disable all devices */
  		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_DEV_MASK);
  		MB_BIC(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
  		/* Cut power from bay, release reset line */
  		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
  		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
  		MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
  	}
  	MB_BIC(bay, HEATHROW_MBCR, 0x00000F00);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
207
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  keylargo_mb_power(struct media_bay_info* bay, int on_off)
  {
  	if (on_off) {
  		/* Power up device, assert it's reset line */
              	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
              	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
  	} else {
  		/* Disable all devices */
  		MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
  		MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
  		/* Cut power from bay, release reset line */
  		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
  		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
  		MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
  	}
  	MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
  }
  
  /*
   * Functions for configuring the media bay for a given type of device,
   * enable the related busses
   */
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
230
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  ohare_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
  {
  	switch(device_id) {
  		case MB_FD:
  		case MB_FD1:
  			MB_BIS(bay, OHARE_FCR, OH_BAY_FLOPPY_ENABLE);
  			MB_BIS(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
  			return 0;
  		case MB_CD:
  			MB_BIC(bay, OHARE_FCR, OH_IDE1_RESET_N);
  			MB_BIS(bay, OHARE_FCR, OH_BAY_IDE_ENABLE);
  			return 0;
  		case MB_PCI:
  			MB_BIS(bay, OHARE_FCR, OH_BAY_PCI_ENABLE);
  			return 0;
  	}
  	return -ENODEV;
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
249
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  heathrow_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
  {
  	switch(device_id) {
  		case MB_FD:
  		case MB_FD1:
  			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_FLOPPY_ENABLE);
  			MB_BIS(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
  			return 0;
  		case MB_CD:
  			MB_BIC(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
  			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_IDE_ENABLE);
  			return 0;
  		case MB_PCI:
  			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_PCI_ENABLE);
  			return 0;
  	}
  	return -ENODEV;
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
268
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  keylargo_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
  {
  	switch(device_id) {
  		case MB_CD:
  			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
  			MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
  			MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
  			return 0;
  		case MB_PCI:
  			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_PCI_ENABLE);
  			return 0;
  		case MB_SOUND:
  			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_SOUND_ENABLE);
  			return 0;
  	}
  	return -ENODEV;
  }
  
  /*
   * Functions for tweaking resets
   */
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
290
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
  ohare_mb_un_reset(struct media_bay_info* bay)
  {
  	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
295
  static void keylargo_mb_init(struct media_bay_info *bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
  {
  	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
299
  static void heathrow_mb_un_reset(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
  {
  	MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
303
  static void keylargo_mb_un_reset(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
  {
  	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
307
  static void ohare_mb_un_reset_ide(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
  {
  	MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
311
  static void heathrow_mb_un_reset_ide(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
  {
  	MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
315
  static void keylargo_mb_un_reset_ide(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
  {
  	MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
319
  static inline void set_mb_power(struct media_bay_info* bay, int onoff)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
324
  {
  	/* Power up up and assert the bay reset line */
  	if (onoff) {
  		bay->ops->power(bay, 1);
  		bay->state = mb_powering_up;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
325
326
  		pr_debug("mediabay%d: powering up
  ", bay->index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
  	} else { 
  		/* Make sure everything is powered down & disabled */
  		bay->ops->power(bay, 0);
  		bay->state = mb_powering_down;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
331
332
  		pr_debug("mediabay%d: powering down
  ", bay->index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
  	}
  	bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
336
  static void poll_media_bay(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
  {
  	int id = bay->ops->content(bay);
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
339
340
341
342
343
344
345
346
347
348
  	static char *mb_content_types[] = {
  		"a floppy drive",
  		"a floppy drive",
  		"an unsuported audio device",
  		"an ATA device",
  		"an unsupported PCI device",
  		"an unknown device",
  	};
  
  	if (id != bay->last_value) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
350
  		bay->last_value = id;
  		bay->value_count = 0;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  		return;
  	}
  	if (id == bay->content_id)
  		return;
  
  	bay->value_count += msecs_to_jiffies(MB_POLL_DELAY);
  	if (bay->value_count >= msecs_to_jiffies(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) && (bay->content_id != MB_NO)) {
  			id = MB_NO;
  			pr_debug("mediabay%d: forcing MB_NO
  ", bay->index);
  		}
  		pr_debug("mediabay%d: switching to %d
  ", bay->index, id);
  		set_mb_power(bay, id != MB_NO);
  		bay->content_id = id;
  		if (id >= MB_NO || id < 0)
  			printk(KERN_INFO "mediabay%d: Bay is now empty
  ", bay->index);
  		else
  			printk(KERN_INFO "mediabay%d: Bay contains %s
  ",
  			       bay->index, mb_content_types[id]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
  	}
  }
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
380
  int check_media_bay(struct macio_dev *baydev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
  {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
382
383
  	struct media_bay_info* bay;
  	int id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  	if (baydev == NULL)
  		return MB_NO;
  
  	/* This returns an instant snapshot, not locking, sine
  	 * we may be called with the bay lock held. The resulting
  	 * fuzzyness of the result if called at the wrong time is
  	 * not actually a huge deal
  	 */
  	bay = macio_get_drvdata(baydev);
  	if (bay == NULL)
  		return MB_NO;
  	id = bay->content_id;
  	if (bay->state != mb_up)
  		return MB_NO;
  	if (id == MB_FD1)
  		return MB_FD;
  	return id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
  }
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
403
  EXPORT_SYMBOL_GPL(check_media_bay);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
405
  void lock_media_bay(struct macio_dev *baydev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
407
  	struct media_bay_info* bay;
34394e45c   Bartlomiej Zolnierkiewicz   ppc: fix #ifdef-s...
408

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
409
410
411
412
413
414
415
  	if (baydev == NULL)
  		return;
  	bay = macio_get_drvdata(baydev);
  	if (bay == NULL)
  		return;
  	mutex_lock(&bay->lock);
  	bay->user_lock = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
  }
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
417
  EXPORT_SYMBOL_GPL(lock_media_bay);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
419
  void unlock_media_bay(struct macio_dev *baydev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
421
  	struct media_bay_info* bay;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
423
424
425
426
427
428
429
430
  	if (baydev == NULL)
  		return;
  	bay = macio_get_drvdata(baydev);
  	if (bay == NULL)
  		return;
  	if (bay->user_lock) {
  		bay->user_lock = 0;
  		mutex_unlock(&bay->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  	}
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
432
433
  }
  EXPORT_SYMBOL_GPL(unlock_media_bay);
34394e45c   Bartlomiej Zolnierkiewicz   ppc: fix #ifdef-s...
434

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  static int mb_broadcast_hotplug(struct device *dev, void *data)
  {
  	struct media_bay_info* bay = data;
  	struct macio_dev *mdev;
  	struct macio_driver *drv;
  	int state;
  
  	if (dev->bus != &macio_bus_type)
  		return 0;
  
  	state = bay->state == mb_up ? bay->content_id : MB_NO;
  	if (state == MB_FD1)
  		state = MB_FD;
  	mdev = to_macio_device(dev);
  	drv = to_macio_driver(dev->driver);
  	if (dev->driver && drv->mediabay_event)
  		drv->mediabay_event(mdev, state);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
454
  static void media_bay_step(int i)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
457
458
459
460
  {
  	struct media_bay_info* bay = &media_bays[i];
  
  	/* We don't poll when powering down */
  	if (bay->state != mb_powering_down)
  	    poll_media_bay(bay);
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
461
462
  	/* If timer expired run state machine */
  	if (bay->timer != 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
467
468
469
470
471
  		bay->timer -= msecs_to_jiffies(MB_POLL_DELAY);
  		if (bay->timer > 0)
  			return;
  		bay->timer = 0;
  	}
  
  	switch(bay->state) {
  	case mb_powering_up:
  	    	if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
472
473
474
  			pr_debug("mediabay%d: device not supported (kind:%d)
  ",
  				 i, bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
478
479
  	    		set_mb_power(bay, 0);
  	    		break;
  	    	}
  	    	bay->timer = msecs_to_jiffies(MB_RESET_DELAY);
  	    	bay->state = mb_enabling_bay;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
480
481
  		pr_debug("mediabay%d: enabling (kind:%d)
  ", i, bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
485
486
  		break;
  	case mb_enabling_bay:
  		bay->ops->un_reset(bay);
  	    	bay->timer = msecs_to_jiffies(MB_SETUP_DELAY);
  	    	bay->state = mb_resetting;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
487
488
489
  		pr_debug("mediabay%d: releasing bay reset (kind:%d)
  ",
  			 i, bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
  	    	break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
492
  	case mb_resetting:
  		if (bay->content_id != MB_CD) {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
493
494
495
  			pr_debug("mediabay%d: bay is up (kind:%d)
  ", i,
  				 bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
  			bay->state = mb_up;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
497
498
  			device_for_each_child(&bay->mdev->ofdev.dev,
  					      bay, mb_broadcast_hotplug);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
  			break;
  	    	}
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
501
502
503
  		pr_debug("mediabay%d: releasing ATA reset (kind:%d)
  ",
  			 i, bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
505
506
  		bay->ops->un_reset_ide(bay);
  	    	bay->timer = msecs_to_jiffies(MB_IDE_WAIT);
  	    	bay->state = mb_ide_resetting;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
  	    	break;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
508

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
  	case mb_ide_resetting:
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
510
511
512
513
514
  		pr_debug("mediabay%d: bay is up (kind:%d)
  ", i, bay->content_id);
  		bay->state = mb_up;
  		device_for_each_child(&bay->mdev->ofdev.dev,
  				      bay, mb_broadcast_hotplug);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
  	    	break;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
516

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
518
  	case mb_powering_down:
  	    	bay->state = mb_empty;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
519
520
521
522
  		device_for_each_child(&bay->mdev->ofdev.dev,
  				      bay, mb_broadcast_hotplug);
  		pr_debug("mediabay%d: end of power down
  ", i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
528
529
530
531
532
  	    	break;
  	}
  }
  
  /*
   * 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.
   */
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
533
  static int media_bay_task(void *x)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
  {
  	int	i;
33f6e7940   Paul Mackerras   [POWERPC] Convert...
536
  	while (!kthread_should_stop()) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
  		for (i = 0; i < media_bay_count; ++i) {
9a24729d8   Daniel Walker   macintosh/media b...
538
  			mutex_lock(&media_bays[i].lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539
540
  			if (!media_bays[i].sleeping)
  				media_bay_step(i);
9a24729d8   Daniel Walker   macintosh/media b...
541
  			mutex_unlock(&media_bays[i].lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
  		}
  
  		msleep_interruptible(MB_POLL_DELAY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
  	}
33f6e7940   Paul Mackerras   [POWERPC] Convert...
546
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
  }
5e6557722   Jeff Mahoney   [PATCH] openfirmw...
548
  static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_device_id *match)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
550
551
552
  {
  	struct media_bay_info* bay;
  	u32 __iomem *regbase;
  	struct device_node *ofnode;
cc5d0189b   Benjamin Herrenschmidt   [PATCH] powerpc: ...
553
  	unsigned long base;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
  	int i;
61c7a080a   Grant Likely   of: Always use 's...
555
  	ofnode = mdev->ofdev.dev.of_node;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
559
560
561
  
  	if (macio_resource_count(mdev) < 1)
  		return -ENODEV;
  	if (macio_request_resources(mdev, "media-bay"))
  		return -EBUSY;
  	/* Media bay registers are located at the beginning of the
cc5d0189b   Benjamin Herrenschmidt   [PATCH] powerpc: ...
562
563
           * mac-io chip, for now, we trick and align down the first
  	 * resource passed in
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
           */
cc5d0189b   Benjamin Herrenschmidt   [PATCH] powerpc: ...
565
566
  	base = macio_resource_start(mdev, 0) & 0xffff0000u;
  	regbase = (u32 __iomem *)ioremap(base, 0x100);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
569
570
571
572
573
574
575
576
577
578
  	if (regbase == NULL) {
  		macio_release_resources(mdev);
  		return -ENOMEM;
  	}
  	
  	i = media_bay_count++;
  	bay = &media_bays[i];
  	bay->mdev = mdev;
  	bay->base = regbase;
  	bay->index = i;
  	bay->ops = match->data;
  	bay->sleeping = 0;
9a24729d8   Daniel Walker   macintosh/media b...
579
  	mutex_init(&bay->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
  
  	/* Init HW probing */
  	if (bay->ops->init)
  		bay->ops->init(bay);
  
  	printk(KERN_INFO "mediabay%d: Registered %s media-bay
  ", i, bay->ops->name);
  
  	/* Force an immediate detect */
  	set_mb_power(bay, 0);
  	msleep(MB_POWER_DELAY);
  	bay->content_id = MB_NO;
  	bay->last_value = bay->ops->content(bay);
  	bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
  	bay->state = mb_empty;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
597
598
599
600
  
  	/* Mark us ready by filling our mdev data */
  	macio_set_drvdata(mdev, bay);
  
  	/* Startup kernel thread */
  	if (i == 0)
33f6e7940   Paul Mackerras   [POWERPC] Convert...
601
  		kthread_run(media_bay_task, NULL, "media-bay");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
603
604
605
  
  	return 0;
  
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
606
  static int media_bay_suspend(struct macio_dev *mdev, pm_message_t state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
  {
  	struct media_bay_info	*bay = macio_get_drvdata(mdev);
3a2d5b700   Rafael J. Wysocki   PM: Introduce PM_...
609
610
  	if (state.event != mdev->ofdev.dev.power.power_state.event
  	    && (state.event & PM_EVENT_SLEEP)) {
9a24729d8   Daniel Walker   macintosh/media b...
611
  		mutex_lock(&bay->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
  		bay->sleeping = 1;
  		set_mb_power(bay, 0);
9a24729d8   Daniel Walker   macintosh/media b...
614
  		mutex_unlock(&bay->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
617
618
619
  		msleep(MB_POLL_DELAY);
  		mdev->ofdev.dev.power.power_state = state;
  	}
  	return 0;
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
620
  static int media_bay_resume(struct macio_dev *mdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
622
  {
  	struct media_bay_info	*bay = macio_get_drvdata(mdev);
ca078bae8   Pavel Machek   [PATCH] swsusp: s...
623
  	if (mdev->ofdev.dev.power.power_state.event != PM_EVENT_ON) {
829ca9a30   Pavel Machek   [PATCH] swsusp: f...
624
  		mdev->ofdev.dev.power.power_state = PMSG_ON;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
627
628
629
630
  
  	       	/* 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.
  	       	 */
  	       	/* Force MB power to 0 */
9a24729d8   Daniel Walker   macintosh/media b...
631
  		mutex_lock(&bay->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
633
634
  	       	set_mb_power(bay, 0);
  		msleep(MB_POWER_DELAY);
  	       	if (bay->ops->content(bay) != bay->content_id) {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
635
636
  			printk("mediabay%d: Content changed during sleep...
  ", bay->index);
9a24729d8   Daniel Walker   macintosh/media b...
637
  			mutex_unlock(&bay->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
643
  	       		return 0;
  		}
  	       	set_mb_power(bay, 1);
  	       	bay->last_value = bay->content_id;
  	       	bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
  	       	bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
645
646
647
648
649
  	       	do {
  			msleep(MB_POLL_DELAY);
  	       		media_bay_step(bay->index);
  	       	} while((bay->state != mb_empty) &&
  	       		(bay->state != mb_up));
  		bay->sleeping = 0;
9a24729d8   Daniel Walker   macintosh/media b...
650
  		mutex_unlock(&bay->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
655
656
657
  	}
  	return 0;
  }
  
  
  /* Definitions of "ops" structures.
   */
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
658
  static struct mb_ops ohare_mb_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
660
661
662
663
664
665
  	.name		= "Ohare",
  	.content	= ohare_mb_content,
  	.power		= ohare_mb_power,
  	.setup_bus	= ohare_mb_setup_bus,
  	.un_reset	= ohare_mb_un_reset,
  	.un_reset_ide	= ohare_mb_un_reset_ide,
  };
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
666
  static struct mb_ops heathrow_mb_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
669
670
671
672
673
  	.name		= "Heathrow",
  	.content	= heathrow_mb_content,
  	.power		= heathrow_mb_power,
  	.setup_bus	= heathrow_mb_setup_bus,
  	.un_reset	= heathrow_mb_un_reset,
  	.un_reset_ide	= heathrow_mb_un_reset_ide,
  };
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
674
  static struct mb_ops keylargo_mb_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  	.name		= "KeyLargo",
  	.init		= keylargo_mb_init,
  	.content	= keylargo_mb_content,
  	.power		= keylargo_mb_power,
  	.setup_bus	= keylargo_mb_setup_bus,
  	.un_reset	= keylargo_mb_un_reset,
  	.un_reset_ide	= keylargo_mb_un_reset_ide,
  };
  
  /*
   * 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.
   */
5e6557722   Jeff Mahoney   [PATCH] openfirmw...
692
  static struct of_device_id media_bay_match[] =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
694
695
  {
  	{
  	.name		= "media-bay",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
697
698
699
700
  	.compatible	= "keylargo-media-bay",
  	.data		= &keylargo_mb_ops,
  	},
  	{
  	.name		= "media-bay",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
703
704
705
  	.compatible	= "heathrow-media-bay",
  	.data		= &heathrow_mb_ops,
  	},
  	{
  	.name		= "media-bay",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
707
708
709
710
711
712
713
  	.compatible	= "ohare-media-bay",
  	.data		= &ohare_mb_ops,
  	},
  	{},
  };
  
  static struct macio_driver media_bay_driver =
  {
c2cdf6aba   Benjamin Herrenschmidt   powerpc/macio: Fi...
714
715
716
717
  	.driver = {
  		.name		= "media-bay",
  		.of_match_table	= media_bay_match,
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
719
720
721
722
723
724
725
726
727
728
729
  	.probe		= media_bay_attach,
  	.suspend	= media_bay_suspend,
  	.resume		= media_bay_resume
  };
  
  static int __init media_bay_init(void)
  {
  	int 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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
  	}
e8222502e   Benjamin Herrenschmidt   [PATCH] powerpc: ...
731
732
  	if (!machine_is(powermac))
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
735
736
737
738
739
  
  	macio_register_driver(&media_bay_driver);	
  
  	return 0;
  }
  
  device_initcall(media_bay_init);