diff options
Diffstat (limited to 'drivers/ide/arm/icside.c')
-rw-r--r-- | drivers/ide/arm/icside.c | 870 |
1 files changed, 870 insertions, 0 deletions
diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c new file mode 100644 index 000000000000..308897e57e4d --- /dev/null +++ b/drivers/ide/arm/icside.c | |||
@@ -0,0 +1,870 @@ | |||
1 | /* | ||
2 | * linux/drivers/ide/arm/icside.c | ||
3 | * | ||
4 | * Copyright (c) 1996-2004 Russell King. | ||
5 | * | ||
6 | * Please note that this platform does not support 32-bit IDE IO. | ||
7 | */ | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | #include <linux/string.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/ioport.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/blkdev.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/hdreg.h> | ||
17 | #include <linux/ide.h> | ||
18 | #include <linux/dma-mapping.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/scatterlist.h> | ||
22 | |||
23 | #include <asm/dma.h> | ||
24 | #include <asm/ecard.h> | ||
25 | #include <asm/io.h> | ||
26 | |||
27 | #define ICS_IDENT_OFFSET 0x2280 | ||
28 | |||
29 | #define ICS_ARCIN_V5_INTRSTAT 0x0000 | ||
30 | #define ICS_ARCIN_V5_INTROFFSET 0x0004 | ||
31 | #define ICS_ARCIN_V5_IDEOFFSET 0x2800 | ||
32 | #define ICS_ARCIN_V5_IDEALTOFFSET 0x2b80 | ||
33 | #define ICS_ARCIN_V5_IDESTEPPING 6 | ||
34 | |||
35 | #define ICS_ARCIN_V6_IDEOFFSET_1 0x2000 | ||
36 | #define ICS_ARCIN_V6_INTROFFSET_1 0x2200 | ||
37 | #define ICS_ARCIN_V6_INTRSTAT_1 0x2290 | ||
38 | #define ICS_ARCIN_V6_IDEALTOFFSET_1 0x2380 | ||
39 | #define ICS_ARCIN_V6_IDEOFFSET_2 0x3000 | ||
40 | #define ICS_ARCIN_V6_INTROFFSET_2 0x3200 | ||
41 | #define ICS_ARCIN_V6_INTRSTAT_2 0x3290 | ||
42 | #define ICS_ARCIN_V6_IDEALTOFFSET_2 0x3380 | ||
43 | #define ICS_ARCIN_V6_IDESTEPPING 6 | ||
44 | |||
45 | struct cardinfo { | ||
46 | unsigned int dataoffset; | ||
47 | unsigned int ctrloffset; | ||
48 | unsigned int stepping; | ||
49 | }; | ||
50 | |||
51 | static struct cardinfo icside_cardinfo_v5 = { | ||
52 | .dataoffset = ICS_ARCIN_V5_IDEOFFSET, | ||
53 | .ctrloffset = ICS_ARCIN_V5_IDEALTOFFSET, | ||
54 | .stepping = ICS_ARCIN_V5_IDESTEPPING, | ||
55 | }; | ||
56 | |||
57 | static struct cardinfo icside_cardinfo_v6_1 = { | ||
58 | .dataoffset = ICS_ARCIN_V6_IDEOFFSET_1, | ||
59 | .ctrloffset = ICS_ARCIN_V6_IDEALTOFFSET_1, | ||
60 | .stepping = ICS_ARCIN_V6_IDESTEPPING, | ||
61 | }; | ||
62 | |||
63 | static struct cardinfo icside_cardinfo_v6_2 = { | ||
64 | .dataoffset = ICS_ARCIN_V6_IDEOFFSET_2, | ||
65 | .ctrloffset = ICS_ARCIN_V6_IDEALTOFFSET_2, | ||
66 | .stepping = ICS_ARCIN_V6_IDESTEPPING, | ||
67 | }; | ||
68 | |||
69 | struct icside_state { | ||
70 | unsigned int channel; | ||
71 | unsigned int enabled; | ||
72 | void __iomem *irq_port; | ||
73 | void __iomem *ioc_base; | ||
74 | unsigned int type; | ||
75 | /* parent device... until the IDE core gets one of its own */ | ||
76 | struct device *dev; | ||
77 | ide_hwif_t *hwif[2]; | ||
78 | }; | ||
79 | |||
80 | #define ICS_TYPE_A3IN 0 | ||
81 | #define ICS_TYPE_A3USER 1 | ||
82 | #define ICS_TYPE_V6 3 | ||
83 | #define ICS_TYPE_V5 15 | ||
84 | #define ICS_TYPE_NOTYPE ((unsigned int)-1) | ||
85 | |||
86 | /* ---------------- Version 5 PCB Support Functions --------------------- */ | ||
87 | /* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) | ||
88 | * Purpose : enable interrupts from card | ||
89 | */ | ||
90 | static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) | ||
91 | { | ||
92 | struct icside_state *state = ec->irq_data; | ||
93 | |||
94 | writeb(0, state->irq_port + ICS_ARCIN_V5_INTROFFSET); | ||
95 | } | ||
96 | |||
97 | /* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) | ||
98 | * Purpose : disable interrupts from card | ||
99 | */ | ||
100 | static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) | ||
101 | { | ||
102 | struct icside_state *state = ec->irq_data; | ||
103 | |||
104 | readb(state->irq_port + ICS_ARCIN_V5_INTROFFSET); | ||
105 | } | ||
106 | |||
107 | static const expansioncard_ops_t icside_ops_arcin_v5 = { | ||
108 | .irqenable = icside_irqenable_arcin_v5, | ||
109 | .irqdisable = icside_irqdisable_arcin_v5, | ||
110 | }; | ||
111 | |||
112 | |||
113 | /* ---------------- Version 6 PCB Support Functions --------------------- */ | ||
114 | /* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) | ||
115 | * Purpose : enable interrupts from card | ||
116 | */ | ||
117 | static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) | ||
118 | { | ||
119 | struct icside_state *state = ec->irq_data; | ||
120 | void __iomem *base = state->irq_port; | ||
121 | |||
122 | state->enabled = 1; | ||
123 | |||
124 | switch (state->channel) { | ||
125 | case 0: | ||
126 | writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1); | ||
127 | readb(base + ICS_ARCIN_V6_INTROFFSET_2); | ||
128 | break; | ||
129 | case 1: | ||
130 | writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2); | ||
131 | readb(base + ICS_ARCIN_V6_INTROFFSET_1); | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | /* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) | ||
137 | * Purpose : disable interrupts from card | ||
138 | */ | ||
139 | static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) | ||
140 | { | ||
141 | struct icside_state *state = ec->irq_data; | ||
142 | |||
143 | state->enabled = 0; | ||
144 | |||
145 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); | ||
146 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); | ||
147 | } | ||
148 | |||
149 | /* Prototype: icside_irqprobe(struct expansion_card *ec) | ||
150 | * Purpose : detect an active interrupt from card | ||
151 | */ | ||
152 | static int icside_irqpending_arcin_v6(struct expansion_card *ec) | ||
153 | { | ||
154 | struct icside_state *state = ec->irq_data; | ||
155 | |||
156 | return readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || | ||
157 | readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; | ||
158 | } | ||
159 | |||
160 | static const expansioncard_ops_t icside_ops_arcin_v6 = { | ||
161 | .irqenable = icside_irqenable_arcin_v6, | ||
162 | .irqdisable = icside_irqdisable_arcin_v6, | ||
163 | .irqpending = icside_irqpending_arcin_v6, | ||
164 | }; | ||
165 | |||
166 | /* | ||
167 | * Handle routing of interrupts. This is called before | ||
168 | * we write the command to the drive. | ||
169 | */ | ||
170 | static void icside_maskproc(ide_drive_t *drive, int mask) | ||
171 | { | ||
172 | ide_hwif_t *hwif = HWIF(drive); | ||
173 | struct icside_state *state = hwif->hwif_data; | ||
174 | unsigned long flags; | ||
175 | |||
176 | local_irq_save(flags); | ||
177 | |||
178 | state->channel = hwif->channel; | ||
179 | |||
180 | if (state->enabled && !mask) { | ||
181 | switch (hwif->channel) { | ||
182 | case 0: | ||
183 | writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); | ||
184 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); | ||
185 | break; | ||
186 | case 1: | ||
187 | writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); | ||
188 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); | ||
189 | break; | ||
190 | } | ||
191 | } else { | ||
192 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); | ||
193 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); | ||
194 | } | ||
195 | |||
196 | local_irq_restore(flags); | ||
197 | } | ||
198 | |||
199 | #ifdef CONFIG_BLK_DEV_IDEDMA_ICS | ||
200 | |||
201 | #ifndef CONFIG_IDEDMA_ICS_AUTO | ||
202 | #warning CONFIG_IDEDMA_ICS_AUTO=n support is obsolete, and will be removed soon. | ||
203 | #endif | ||
204 | |||
205 | /* | ||
206 | * SG-DMA support. | ||
207 | * | ||
208 | * Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers. | ||
209 | * There is only one DMA controller per card, which means that only | ||
210 | * one drive can be accessed at one time. NOTE! We do not enforce that | ||
211 | * here, but we rely on the main IDE driver spotting that both | ||
212 | * interfaces use the same IRQ, which should guarantee this. | ||
213 | */ | ||
214 | |||
215 | static void icside_build_sglist(ide_drive_t *drive, struct request *rq) | ||
216 | { | ||
217 | ide_hwif_t *hwif = drive->hwif; | ||
218 | struct icside_state *state = hwif->hwif_data; | ||
219 | struct scatterlist *sg = hwif->sg_table; | ||
220 | |||
221 | ide_map_sg(drive, rq); | ||
222 | |||
223 | if (rq_data_dir(rq) == READ) | ||
224 | hwif->sg_dma_direction = DMA_FROM_DEVICE; | ||
225 | else | ||
226 | hwif->sg_dma_direction = DMA_TO_DEVICE; | ||
227 | |||
228 | hwif->sg_nents = dma_map_sg(state->dev, sg, hwif->sg_nents, | ||
229 | hwif->sg_dma_direction); | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * Configure the IOMD to give the appropriate timings for the transfer | ||
234 | * mode being requested. We take the advice of the ATA standards, and | ||
235 | * calculate the cycle time based on the transfer mode, and the EIDE | ||
236 | * MW DMA specs that the drive provides in the IDENTIFY command. | ||
237 | * | ||
238 | * We have the following IOMD DMA modes to choose from: | ||
239 | * | ||
240 | * Type Active Recovery Cycle | ||
241 | * A 250 (250) 312 (550) 562 (800) | ||
242 | * B 187 250 437 | ||
243 | * C 125 (125) 125 (375) 250 (500) | ||
244 | * D 62 125 187 | ||
245 | * | ||
246 | * (figures in brackets are actual measured timings) | ||
247 | * | ||
248 | * However, we also need to take care of the read/write active and | ||
249 | * recovery timings: | ||
250 | * | ||
251 | * Read Write | ||
252 | * Mode Active -- Recovery -- Cycle IOMD type | ||
253 | * MW0 215 50 215 480 A | ||
254 | * MW1 80 50 50 150 C | ||
255 | * MW2 70 25 25 120 C | ||
256 | */ | ||
257 | static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode) | ||
258 | { | ||
259 | int on = 0, cycle_time = 0, use_dma_info = 0; | ||
260 | |||
261 | /* | ||
262 | * Limit the transfer speed to MW_DMA_2. | ||
263 | */ | ||
264 | if (xfer_mode > XFER_MW_DMA_2) | ||
265 | xfer_mode = XFER_MW_DMA_2; | ||
266 | |||
267 | switch (xfer_mode) { | ||
268 | case XFER_MW_DMA_2: | ||
269 | cycle_time = 250; | ||
270 | use_dma_info = 1; | ||
271 | break; | ||
272 | |||
273 | case XFER_MW_DMA_1: | ||
274 | cycle_time = 250; | ||
275 | use_dma_info = 1; | ||
276 | break; | ||
277 | |||
278 | case XFER_MW_DMA_0: | ||
279 | cycle_time = 480; | ||
280 | break; | ||
281 | |||
282 | case XFER_SW_DMA_2: | ||
283 | case XFER_SW_DMA_1: | ||
284 | case XFER_SW_DMA_0: | ||
285 | cycle_time = 480; | ||
286 | break; | ||
287 | } | ||
288 | |||
289 | /* | ||
290 | * If we're going to be doing MW_DMA_1 or MW_DMA_2, we should | ||
291 | * take care to note the values in the ID... | ||
292 | */ | ||
293 | if (use_dma_info && drive->id->eide_dma_time > cycle_time) | ||
294 | cycle_time = drive->id->eide_dma_time; | ||
295 | |||
296 | drive->drive_data = cycle_time; | ||
297 | |||
298 | if (cycle_time && ide_config_drive_speed(drive, xfer_mode) == 0) | ||
299 | on = 1; | ||
300 | else | ||
301 | drive->drive_data = 480; | ||
302 | |||
303 | printk("%s: %s selected (peak %dMB/s)\n", drive->name, | ||
304 | ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data); | ||
305 | |||
306 | drive->current_speed = xfer_mode; | ||
307 | |||
308 | return on; | ||
309 | } | ||
310 | |||
311 | static int icside_dma_host_off(ide_drive_t *drive) | ||
312 | { | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int icside_dma_off_quietly(ide_drive_t *drive) | ||
317 | { | ||
318 | drive->using_dma = 0; | ||
319 | return icside_dma_host_off(drive); | ||
320 | } | ||
321 | |||
322 | static int icside_dma_host_on(ide_drive_t *drive) | ||
323 | { | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static int icside_dma_on(ide_drive_t *drive) | ||
328 | { | ||
329 | drive->using_dma = 1; | ||
330 | return icside_dma_host_on(drive); | ||
331 | } | ||
332 | |||
333 | static int icside_dma_check(ide_drive_t *drive) | ||
334 | { | ||
335 | struct hd_driveid *id = drive->id; | ||
336 | ide_hwif_t *hwif = HWIF(drive); | ||
337 | int xfer_mode = XFER_PIO_2; | ||
338 | int on; | ||
339 | |||
340 | if (!(id->capability & 1) || !hwif->autodma) | ||
341 | goto out; | ||
342 | |||
343 | /* | ||
344 | * Consult the list of known "bad" drives | ||
345 | */ | ||
346 | if (__ide_dma_bad_drive(drive)) | ||
347 | goto out; | ||
348 | |||
349 | /* | ||
350 | * Enable DMA on any drive that has multiword DMA | ||
351 | */ | ||
352 | if (id->field_valid & 2) { | ||
353 | xfer_mode = ide_dma_speed(drive, 0); | ||
354 | goto out; | ||
355 | } | ||
356 | |||
357 | /* | ||
358 | * Consult the list of known "good" drives | ||
359 | */ | ||
360 | if (__ide_dma_good_drive(drive)) { | ||
361 | if (id->eide_dma_time > 150) | ||
362 | goto out; | ||
363 | xfer_mode = XFER_MW_DMA_1; | ||
364 | } | ||
365 | |||
366 | out: | ||
367 | on = icside_set_speed(drive, xfer_mode); | ||
368 | |||
369 | if (on) | ||
370 | return icside_dma_on(drive); | ||
371 | else | ||
372 | return icside_dma_off_quietly(drive); | ||
373 | } | ||
374 | |||
375 | static int icside_dma_end(ide_drive_t *drive) | ||
376 | { | ||
377 | ide_hwif_t *hwif = HWIF(drive); | ||
378 | struct icside_state *state = hwif->hwif_data; | ||
379 | |||
380 | drive->waiting_for_dma = 0; | ||
381 | |||
382 | disable_dma(hwif->hw.dma); | ||
383 | |||
384 | /* Teardown mappings after DMA has completed. */ | ||
385 | dma_unmap_sg(state->dev, hwif->sg_table, hwif->sg_nents, | ||
386 | hwif->sg_dma_direction); | ||
387 | |||
388 | return get_dma_residue(hwif->hw.dma) != 0; | ||
389 | } | ||
390 | |||
391 | static void icside_dma_start(ide_drive_t *drive) | ||
392 | { | ||
393 | ide_hwif_t *hwif = HWIF(drive); | ||
394 | |||
395 | /* We can not enable DMA on both channels simultaneously. */ | ||
396 | BUG_ON(dma_channel_active(hwif->hw.dma)); | ||
397 | enable_dma(hwif->hw.dma); | ||
398 | } | ||
399 | |||
400 | static int icside_dma_setup(ide_drive_t *drive) | ||
401 | { | ||
402 | ide_hwif_t *hwif = HWIF(drive); | ||
403 | struct request *rq = hwif->hwgroup->rq; | ||
404 | unsigned int dma_mode; | ||
405 | |||
406 | if (rq_data_dir(rq)) | ||
407 | dma_mode = DMA_MODE_WRITE; | ||
408 | else | ||
409 | dma_mode = DMA_MODE_READ; | ||
410 | |||
411 | /* | ||
412 | * We can not enable DMA on both channels. | ||
413 | */ | ||
414 | BUG_ON(dma_channel_active(hwif->hw.dma)); | ||
415 | |||
416 | icside_build_sglist(drive, rq); | ||
417 | |||
418 | /* | ||
419 | * Ensure that we have the right interrupt routed. | ||
420 | */ | ||
421 | icside_maskproc(drive, 0); | ||
422 | |||
423 | /* | ||
424 | * Route the DMA signals to the correct interface. | ||
425 | */ | ||
426 | writeb(hwif->select_data, hwif->config_data); | ||
427 | |||
428 | /* | ||
429 | * Select the correct timing for this drive. | ||
430 | */ | ||
431 | set_dma_speed(hwif->hw.dma, drive->drive_data); | ||
432 | |||
433 | /* | ||
434 | * Tell the DMA engine about the SG table and | ||
435 | * data direction. | ||
436 | */ | ||
437 | set_dma_sg(hwif->hw.dma, hwif->sg_table, hwif->sg_nents); | ||
438 | set_dma_mode(hwif->hw.dma, dma_mode); | ||
439 | |||
440 | drive->waiting_for_dma = 1; | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static void icside_dma_exec_cmd(ide_drive_t *drive, u8 cmd) | ||
446 | { | ||
447 | /* issue cmd to drive */ | ||
448 | ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD, NULL); | ||
449 | } | ||
450 | |||
451 | static int icside_dma_test_irq(ide_drive_t *drive) | ||
452 | { | ||
453 | ide_hwif_t *hwif = HWIF(drive); | ||
454 | struct icside_state *state = hwif->hwif_data; | ||
455 | |||
456 | return readb(state->irq_port + | ||
457 | (hwif->channel ? | ||
458 | ICS_ARCIN_V6_INTRSTAT_2 : | ||
459 | ICS_ARCIN_V6_INTRSTAT_1)) & 1; | ||
460 | } | ||
461 | |||
462 | static int icside_dma_timeout(ide_drive_t *drive) | ||
463 | { | ||
464 | printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); | ||
465 | |||
466 | if (icside_dma_test_irq(drive)) | ||
467 | return 0; | ||
468 | |||
469 | ide_dump_status(drive, "DMA timeout", | ||
470 | HWIF(drive)->INB(IDE_STATUS_REG)); | ||
471 | |||
472 | return icside_dma_end(drive); | ||
473 | } | ||
474 | |||
475 | static int icside_dma_lostirq(ide_drive_t *drive) | ||
476 | { | ||
477 | printk(KERN_ERR "%s: IRQ lost\n", drive->name); | ||
478 | return 1; | ||
479 | } | ||
480 | |||
481 | static void icside_dma_init(ide_hwif_t *hwif) | ||
482 | { | ||
483 | int autodma = 0; | ||
484 | |||
485 | #ifdef CONFIG_IDEDMA_ICS_AUTO | ||
486 | autodma = 1; | ||
487 | #endif | ||
488 | |||
489 | printk(" %s: SG-DMA", hwif->name); | ||
490 | |||
491 | hwif->atapi_dma = 1; | ||
492 | hwif->mwdma_mask = 7; /* MW0..2 */ | ||
493 | hwif->swdma_mask = 7; /* SW0..2 */ | ||
494 | |||
495 | hwif->dmatable_cpu = NULL; | ||
496 | hwif->dmatable_dma = 0; | ||
497 | hwif->speedproc = icside_set_speed; | ||
498 | hwif->autodma = autodma; | ||
499 | |||
500 | hwif->ide_dma_check = icside_dma_check; | ||
501 | hwif->ide_dma_host_off = icside_dma_host_off; | ||
502 | hwif->ide_dma_off_quietly = icside_dma_off_quietly; | ||
503 | hwif->ide_dma_host_on = icside_dma_host_on; | ||
504 | hwif->ide_dma_on = icside_dma_on; | ||
505 | hwif->dma_setup = icside_dma_setup; | ||
506 | hwif->dma_exec_cmd = icside_dma_exec_cmd; | ||
507 | hwif->dma_start = icside_dma_start; | ||
508 | hwif->ide_dma_end = icside_dma_end; | ||
509 | hwif->ide_dma_test_irq = icside_dma_test_irq; | ||
510 | hwif->ide_dma_timeout = icside_dma_timeout; | ||
511 | hwif->ide_dma_lostirq = icside_dma_lostirq; | ||
512 | |||
513 | hwif->drives[0].autodma = hwif->autodma; | ||
514 | hwif->drives[1].autodma = hwif->autodma; | ||
515 | |||
516 | printk(" capable%s\n", hwif->autodma ? ", auto-enable" : ""); | ||
517 | } | ||
518 | #else | ||
519 | #define icside_dma_init(hwif) (0) | ||
520 | #endif | ||
521 | |||
522 | static ide_hwif_t *icside_find_hwif(unsigned long dataport) | ||
523 | { | ||
524 | ide_hwif_t *hwif; | ||
525 | int index; | ||
526 | |||
527 | for (index = 0; index < MAX_HWIFS; ++index) { | ||
528 | hwif = &ide_hwifs[index]; | ||
529 | if (hwif->io_ports[IDE_DATA_OFFSET] == dataport) | ||
530 | goto found; | ||
531 | } | ||
532 | |||
533 | for (index = 0; index < MAX_HWIFS; ++index) { | ||
534 | hwif = &ide_hwifs[index]; | ||
535 | if (!hwif->io_ports[IDE_DATA_OFFSET]) | ||
536 | goto found; | ||
537 | } | ||
538 | |||
539 | hwif = NULL; | ||
540 | found: | ||
541 | return hwif; | ||
542 | } | ||
543 | |||
544 | static ide_hwif_t * | ||
545 | icside_setup(void __iomem *base, struct cardinfo *info, struct expansion_card *ec) | ||
546 | { | ||
547 | unsigned long port = (unsigned long)base + info->dataoffset; | ||
548 | ide_hwif_t *hwif; | ||
549 | |||
550 | hwif = icside_find_hwif(port); | ||
551 | if (hwif) { | ||
552 | int i; | ||
553 | |||
554 | memset(&hwif->hw, 0, sizeof(hw_regs_t)); | ||
555 | |||
556 | /* | ||
557 | * Ensure we're using MMIO | ||
558 | */ | ||
559 | default_hwif_mmiops(hwif); | ||
560 | hwif->mmio = 2; | ||
561 | |||
562 | for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { | ||
563 | hwif->hw.io_ports[i] = port; | ||
564 | hwif->io_ports[i] = port; | ||
565 | port += 1 << info->stepping; | ||
566 | } | ||
567 | hwif->hw.io_ports[IDE_CONTROL_OFFSET] = (unsigned long)base + info->ctrloffset; | ||
568 | hwif->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)base + info->ctrloffset; | ||
569 | hwif->hw.irq = ec->irq; | ||
570 | hwif->irq = ec->irq; | ||
571 | hwif->noprobe = 0; | ||
572 | hwif->chipset = ide_acorn; | ||
573 | hwif->gendev.parent = &ec->dev; | ||
574 | } | ||
575 | |||
576 | return hwif; | ||
577 | } | ||
578 | |||
579 | static int __init | ||
580 | icside_register_v5(struct icside_state *state, struct expansion_card *ec) | ||
581 | { | ||
582 | ide_hwif_t *hwif; | ||
583 | void __iomem *base; | ||
584 | |||
585 | base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), | ||
586 | ecard_resource_len(ec, ECARD_RES_MEMC)); | ||
587 | if (!base) | ||
588 | return -ENOMEM; | ||
589 | |||
590 | state->irq_port = base; | ||
591 | |||
592 | ec->irqaddr = base + ICS_ARCIN_V5_INTRSTAT; | ||
593 | ec->irqmask = 1; | ||
594 | ec->irq_data = state; | ||
595 | ec->ops = &icside_ops_arcin_v5; | ||
596 | |||
597 | /* | ||
598 | * Be on the safe side - disable interrupts | ||
599 | */ | ||
600 | icside_irqdisable_arcin_v5(ec, 0); | ||
601 | |||
602 | hwif = icside_setup(base, &icside_cardinfo_v5, ec); | ||
603 | if (!hwif) { | ||
604 | iounmap(base); | ||
605 | return -ENODEV; | ||
606 | } | ||
607 | |||
608 | state->hwif[0] = hwif; | ||
609 | |||
610 | probe_hwif_init(hwif); | ||
611 | create_proc_ide_interfaces(); | ||
612 | |||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | static int __init | ||
617 | icside_register_v6(struct icside_state *state, struct expansion_card *ec) | ||
618 | { | ||
619 | ide_hwif_t *hwif, *mate; | ||
620 | void __iomem *ioc_base, *easi_base; | ||
621 | unsigned int sel = 0; | ||
622 | int ret; | ||
623 | |||
624 | ioc_base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST), | ||
625 | ecard_resource_len(ec, ECARD_RES_IOCFAST)); | ||
626 | if (!ioc_base) { | ||
627 | ret = -ENOMEM; | ||
628 | goto out; | ||
629 | } | ||
630 | |||
631 | easi_base = ioc_base; | ||
632 | |||
633 | if (ecard_resource_flags(ec, ECARD_RES_EASI)) { | ||
634 | easi_base = ioremap(ecard_resource_start(ec, ECARD_RES_EASI), | ||
635 | ecard_resource_len(ec, ECARD_RES_EASI)); | ||
636 | if (!easi_base) { | ||
637 | ret = -ENOMEM; | ||
638 | goto unmap_slot; | ||
639 | } | ||
640 | |||
641 | /* | ||
642 | * Enable access to the EASI region. | ||
643 | */ | ||
644 | sel = 1 << 5; | ||
645 | } | ||
646 | |||
647 | writeb(sel, ioc_base); | ||
648 | |||
649 | ec->irq_data = state; | ||
650 | ec->ops = &icside_ops_arcin_v6; | ||
651 | |||
652 | state->irq_port = easi_base; | ||
653 | state->ioc_base = ioc_base; | ||
654 | |||
655 | /* | ||
656 | * Be on the safe side - disable interrupts | ||
657 | */ | ||
658 | icside_irqdisable_arcin_v6(ec, 0); | ||
659 | |||
660 | /* | ||
661 | * Find and register the interfaces. | ||
662 | */ | ||
663 | hwif = icside_setup(easi_base, &icside_cardinfo_v6_1, ec); | ||
664 | mate = icside_setup(easi_base, &icside_cardinfo_v6_2, ec); | ||
665 | |||
666 | if (!hwif || !mate) { | ||
667 | ret = -ENODEV; | ||
668 | goto unmap_port; | ||
669 | } | ||
670 | |||
671 | state->hwif[0] = hwif; | ||
672 | state->hwif[1] = mate; | ||
673 | |||
674 | hwif->maskproc = icside_maskproc; | ||
675 | hwif->channel = 0; | ||
676 | hwif->hwif_data = state; | ||
677 | hwif->mate = mate; | ||
678 | hwif->serialized = 1; | ||
679 | hwif->config_data = (unsigned long)ioc_base; | ||
680 | hwif->select_data = sel; | ||
681 | hwif->hw.dma = ec->dma; | ||
682 | |||
683 | mate->maskproc = icside_maskproc; | ||
684 | mate->channel = 1; | ||
685 | mate->hwif_data = state; | ||
686 | mate->mate = hwif; | ||
687 | mate->serialized = 1; | ||
688 | mate->config_data = (unsigned long)ioc_base; | ||
689 | mate->select_data = sel | 1; | ||
690 | mate->hw.dma = ec->dma; | ||
691 | |||
692 | if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) { | ||
693 | icside_dma_init(hwif); | ||
694 | icside_dma_init(mate); | ||
695 | } | ||
696 | |||
697 | probe_hwif_init(hwif); | ||
698 | probe_hwif_init(mate); | ||
699 | create_proc_ide_interfaces(); | ||
700 | |||
701 | return 0; | ||
702 | |||
703 | unmap_port: | ||
704 | if (easi_base != ioc_base) | ||
705 | iounmap(easi_base); | ||
706 | unmap_slot: | ||
707 | iounmap(ioc_base); | ||
708 | out: | ||
709 | return ret; | ||
710 | } | ||
711 | |||
712 | static int __devinit | ||
713 | icside_probe(struct expansion_card *ec, const struct ecard_id *id) | ||
714 | { | ||
715 | struct icside_state *state; | ||
716 | void __iomem *idmem; | ||
717 | int ret; | ||
718 | |||
719 | ret = ecard_request_resources(ec); | ||
720 | if (ret) | ||
721 | goto out; | ||
722 | |||
723 | state = kmalloc(sizeof(struct icside_state), GFP_KERNEL); | ||
724 | if (!state) { | ||
725 | ret = -ENOMEM; | ||
726 | goto release; | ||
727 | } | ||
728 | |||
729 | memset(state, 0, sizeof(state)); | ||
730 | state->type = ICS_TYPE_NOTYPE; | ||
731 | state->dev = &ec->dev; | ||
732 | |||
733 | idmem = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST), | ||
734 | ecard_resource_len(ec, ECARD_RES_IOCFAST)); | ||
735 | if (idmem) { | ||
736 | unsigned int type; | ||
737 | |||
738 | type = readb(idmem + ICS_IDENT_OFFSET) & 1; | ||
739 | type |= (readb(idmem + ICS_IDENT_OFFSET + 4) & 1) << 1; | ||
740 | type |= (readb(idmem + ICS_IDENT_OFFSET + 8) & 1) << 2; | ||
741 | type |= (readb(idmem + ICS_IDENT_OFFSET + 12) & 1) << 3; | ||
742 | iounmap(idmem); | ||
743 | |||
744 | state->type = type; | ||
745 | } | ||
746 | |||
747 | switch (state->type) { | ||
748 | case ICS_TYPE_A3IN: | ||
749 | dev_warn(&ec->dev, "A3IN unsupported\n"); | ||
750 | ret = -ENODEV; | ||
751 | break; | ||
752 | |||
753 | case ICS_TYPE_A3USER: | ||
754 | dev_warn(&ec->dev, "A3USER unsupported\n"); | ||
755 | ret = -ENODEV; | ||
756 | break; | ||
757 | |||
758 | case ICS_TYPE_V5: | ||
759 | ret = icside_register_v5(state, ec); | ||
760 | break; | ||
761 | |||
762 | case ICS_TYPE_V6: | ||
763 | ret = icside_register_v6(state, ec); | ||
764 | break; | ||
765 | |||
766 | default: | ||
767 | dev_warn(&ec->dev, "unknown interface type\n"); | ||
768 | ret = -ENODEV; | ||
769 | break; | ||
770 | } | ||
771 | |||
772 | if (ret == 0) { | ||
773 | ecard_set_drvdata(ec, state); | ||
774 | goto out; | ||
775 | } | ||
776 | |||
777 | kfree(state); | ||
778 | release: | ||
779 | ecard_release_resources(ec); | ||
780 | out: | ||
781 | return ret; | ||
782 | } | ||
783 | |||
784 | static void __devexit icside_remove(struct expansion_card *ec) | ||
785 | { | ||
786 | struct icside_state *state = ecard_get_drvdata(ec); | ||
787 | |||
788 | switch (state->type) { | ||
789 | case ICS_TYPE_V5: | ||
790 | /* FIXME: tell IDE to stop using the interface */ | ||
791 | |||
792 | /* Disable interrupts */ | ||
793 | icside_irqdisable_arcin_v5(ec, 0); | ||
794 | break; | ||
795 | |||
796 | case ICS_TYPE_V6: | ||
797 | /* FIXME: tell IDE to stop using the interface */ | ||
798 | if (ec->dma != NO_DMA) | ||
799 | free_dma(ec->dma); | ||
800 | |||
801 | /* Disable interrupts */ | ||
802 | icside_irqdisable_arcin_v6(ec, 0); | ||
803 | |||
804 | /* Reset the ROM pointer/EASI selection */ | ||
805 | writeb(0, state->ioc_base); | ||
806 | break; | ||
807 | } | ||
808 | |||
809 | ecard_set_drvdata(ec, NULL); | ||
810 | ec->ops = NULL; | ||
811 | ec->irq_data = NULL; | ||
812 | |||
813 | if (state->ioc_base) | ||
814 | iounmap(state->ioc_base); | ||
815 | if (state->ioc_base != state->irq_port) | ||
816 | iounmap(state->irq_port); | ||
817 | |||
818 | kfree(state); | ||
819 | ecard_release_resources(ec); | ||
820 | } | ||
821 | |||
822 | static void icside_shutdown(struct expansion_card *ec) | ||
823 | { | ||
824 | struct icside_state *state = ecard_get_drvdata(ec); | ||
825 | unsigned long flags; | ||
826 | |||
827 | /* | ||
828 | * Disable interrupts from this card. We need to do | ||
829 | * this before disabling EASI since we may be accessing | ||
830 | * this register via that region. | ||
831 | */ | ||
832 | local_irq_save(flags); | ||
833 | ec->ops->irqdisable(ec, 0); | ||
834 | local_irq_restore(flags); | ||
835 | |||
836 | /* | ||
837 | * Reset the ROM pointer so that we can read the ROM | ||
838 | * after a soft reboot. This also disables access to | ||
839 | * the IDE taskfile via the EASI region. | ||
840 | */ | ||
841 | if (state->ioc_base) | ||
842 | writeb(0, state->ioc_base); | ||
843 | } | ||
844 | |||
845 | static const struct ecard_id icside_ids[] = { | ||
846 | { MANU_ICS, PROD_ICS_IDE }, | ||
847 | { MANU_ICS2, PROD_ICS2_IDE }, | ||
848 | { 0xffff, 0xffff } | ||
849 | }; | ||
850 | |||
851 | static struct ecard_driver icside_driver = { | ||
852 | .probe = icside_probe, | ||
853 | .remove = __devexit_p(icside_remove), | ||
854 | .shutdown = icside_shutdown, | ||
855 | .id_table = icside_ids, | ||
856 | .drv = { | ||
857 | .name = "icside", | ||
858 | }, | ||
859 | }; | ||
860 | |||
861 | static int __init icside_init(void) | ||
862 | { | ||
863 | return ecard_register_driver(&icside_driver); | ||
864 | } | ||
865 | |||
866 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | ||
867 | MODULE_LICENSE("GPL"); | ||
868 | MODULE_DESCRIPTION("ICS IDE driver"); | ||
869 | |||
870 | module_init(icside_init); | ||