Blame view

drivers/macintosh/mediabay.c 18.7 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
  /*
   * Driver for the media bay on the PowerBook 3400 and 2400.
   *
   * Copyright (C) 1998 Paul Mackerras.
   *
   * Various evolutions by Benjamin Herrenschmidt & Henry Worth
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
  #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
15
16
  #include <linux/stddef.h>
  #include <linux/init.h>
33f6e7940   Paul Mackerras   [POWERPC] Convert...
17
  #include <linux/kthread.h>
9a24729d8   Daniel Walker   macintosh/media b...
18
  #include <linux/mutex.h>
ca5999fde   Mike Rapoport   mm: introduce inc...
19
  #include <linux/pgtable.h>
65fddcfca   Mike Rapoport   mm: reorder inclu...
20
  #include <asm/prom.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
27
28
29
30
  #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
31
32
33
34
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
  #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;
519a65109   Uwe Kleine-König   macintosh/mediaba...
61
  	const struct mb_ops*		ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  	int				index;
  	int				cached_gpio;
  	int				sleeping;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
65
  	int				user_lock;
9a24729d8   Daniel Walker   macintosh/media b...
66
  	struct mutex			lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
  };
  
  #define MAX_BAYS	2
  
  static struct media_bay_info media_bays[MAX_BAYS];
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
72
  static int media_bay_count = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
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
  
  /*
   * 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...
105
   * (or until the device is ready) before calling into the driver
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
   */
  #define MB_IDE_WAIT	1000
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
114
115
116
117
   * 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  	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: ...
132
  static u8
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
  ohare_mb_content(struct media_bay_info *bay)
  {
  	return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
137
  static u8
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
  heathrow_mb_content(struct media_bay_info *bay)
  {
  	return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
142
  static u8
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  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: ...
167
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  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: ...
185
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  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: ...
203
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
  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: ...
226
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  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: ...
245
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  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: ...
264
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  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: ...
286
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
  ohare_mb_un_reset(struct media_bay_info* bay)
  {
  	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
291
  static void keylargo_mb_init(struct media_bay_info *bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
  {
  	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
295
  static void heathrow_mb_un_reset(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
  {
  	MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
299
  static void keylargo_mb_un_reset(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
  {
  	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
303
  static void ohare_mb_un_reset_ide(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
  {
  	MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
307
  static void heathrow_mb_un_reset_ide(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
  {
  	MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
311
  static void keylargo_mb_un_reset_ide(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
  {
  	MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
315
  static inline void set_mb_power(struct media_bay_info* bay, int onoff)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
319
320
  {
  	/* 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...
321
322
  		pr_debug("mediabay%d: powering up
  ", bay->index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
  	} else { 
  		/* Make sure everything is powered down & disabled */
  		bay->ops->power(bay, 0);
  		bay->state = mb_powering_down;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
327
328
  		pr_debug("mediabay%d: powering down
  ", bay->index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
  	}
  	bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
332
  static void poll_media_bay(struct media_bay_info* bay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
  {
  	int id = bay->ops->content(bay);
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
335
336
337
  	static char *mb_content_types[] = {
  		"a floppy drive",
  		"a floppy drive",
ec16f3df1   Masanari Iida   macintosh: fix st...
338
  		"an unsupported audio device",
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
339
340
341
342
343
344
  		"an ATA device",
  		"an unsupported PCI device",
  		"an unknown device",
  	};
  
  	if (id != bay->last_value) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
  		bay->last_value = id;
  		bay->value_count = 0;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  		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
374
375
  	}
  }
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
376
  int check_media_bay(struct macio_dev *baydev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
378
379
  	struct media_bay_info* bay;
  	int id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
  	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
398
  }
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
399
  EXPORT_SYMBOL_GPL(check_media_bay);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400

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

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
405
406
407
408
409
410
411
  	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
412
  }
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
413
  EXPORT_SYMBOL_GPL(lock_media_bay);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
415
  void unlock_media_bay(struct macio_dev *baydev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
  {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
417
  	struct media_bay_info* bay;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
419
420
421
422
423
424
425
426
  	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
427
  	}
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
428
429
  }
  EXPORT_SYMBOL_GPL(unlock_media_bay);
34394e45c   Bartlomiej Zolnierkiewicz   ppc: fix #ifdef-s...
430

d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  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
449
  }
aacaf9bd9   Jon Loeliger   [PATCH] powerpc: ...
450
  static void media_bay_step(int i)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
456
  {
  	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...
457
458
  	/* If timer expired run state machine */
  	if (bay->timer != 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
463
464
465
466
467
  		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...
468
469
470
  			pr_debug("mediabay%d: device not supported (kind:%d)
  ",
  				 i, bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
472
473
474
475
  	    		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...
476
477
  		pr_debug("mediabay%d: enabling (kind:%d)
  ", i, bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
480
481
482
  		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...
483
484
485
  		pr_debug("mediabay%d: releasing bay reset (kind:%d)
  ",
  			 i, bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
  	    	break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
  	case mb_resetting:
  		if (bay->content_id != MB_CD) {
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
489
490
491
  			pr_debug("mediabay%d: bay is up (kind:%d)
  ", i,
  				 bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
  			bay->state = mb_up;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
493
494
  			device_for_each_child(&bay->mdev->ofdev.dev,
  					      bay, mb_broadcast_hotplug);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
  			break;
  	    	}
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
497
498
499
  		pr_debug("mediabay%d: releasing ATA reset (kind:%d)
  ",
  			 i, bay->content_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
  		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
503
  	    	break;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
504

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
  	case mb_ide_resetting:
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
506
507
508
509
510
  		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
511
  	    	break;
d58b0c39e   Benjamin Herrenschmidt   powerpc/macio: Re...
512

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