Amiga IDE driver update. A4000 and IDE splitter confirmed working. Some devices,...
[AROS.git] / arch / m68k-amiga / devs / ata / ata_amiga.c
blob049c7af4c4b28d3c06bb4ef5889ce1a411c1107e
2 #define DEBUG 1
3 #define DEBUG2 0
5 #include <aros/debug.h>
6 #include <exec/types.h>
7 #include <exec/exec.h>
8 #include <proto/exec.h>
9 #include <proto/cardres.h>
10 #include <graphics/gfxbase.h>
11 #include <hardware/custom.h>
12 #include <hardware/intbits.h>
13 #include <resources/card.h>
14 #include <libraries/pccard.h>
15 #include <aros/symbolsets.h>
17 #include "ata.h"
18 #include "timer.h"
20 #define GAYLE_BASE_4000 0xdd2022 /* 0xdd2020.W, 0xdd2026.B, 0xdd202a.B ... (argh!) */
21 #define GAYLE_IRQ_4000 0xdd3020
23 #define GAYLE_BASE_1200 0xda0000 /* 0xda0000.W, 0xda0004.B, 0xda0008.B ... */
24 #define GAYLE_IRQ_1200 0xda9000
25 #define GAYLE_INT_1200 0xdaa000
27 #define GAYLE_IRQ_IDE 0x80
28 #define GAYLE_INT_IDE 0x80
30 struct amiga_driverdata
32 struct amiga_busdata *bus[2];
33 struct Interrupt ideint;
34 BOOL ideintdone;
35 UBYTE *gaylebase;
36 UBYTE *gayleirqbase;
37 BOOL a4000;
38 UBYTE doubler;
41 struct amiga_pcmcia_driverdata
43 struct amiga_busdata *bus[1];
44 struct CardHandle cardhandle;
45 struct Interrupt statusint;
46 struct Interrupt insertint;
47 struct Interrupt removalint;
48 struct CardResource *CardResource;
49 BOOL intena;
50 BOOL poststatus;
51 ULONG configbase, configmask;
52 struct DeviceTData dtd;
53 struct CardMemoryMap *cmm;
57 struct amiga_busdata
59 void *ddata;
60 struct ata_Bus *bus;
61 UBYTE *port;
62 BOOL reset;
65 static void ata_insw(APTR address, UWORD port, ULONG count, void *data)
67 struct amiga_busdata *bdata = data;
68 volatile UWORD *addr = (UWORD*)(bdata->port + (port & ~3));
69 UWORD *dst = address;
71 count /= 2;
72 while (count-- != 0)
73 *dst++ = *addr;
76 static void ata_outsw(APTR address, UWORD port, ULONG count, APTR data)
78 struct amiga_busdata *bdata = data;
79 volatile UWORD *addr = (UWORD*)(bdata->port + (port & ~3));
80 UWORD *dst = address;
82 count /= 2;
83 while (count-- != 0)
84 *addr = *dst++;
87 static void ata_pcmcia_insw(APTR address, UWORD port, ULONG count, void *data)
89 struct amiga_busdata *bdata = data;
90 volatile UWORD *addr = (UWORD*)(bdata->port + 8);
91 UWORD *dst = address;
93 count /= 2;
94 while (count-- != 0)
95 *dst++ = *addr;
98 static void ata_pcmcia_outsw(APTR address, UWORD port, ULONG count, APTR data)
100 struct amiga_busdata *bdata = data;
101 volatile UWORD *addr = (UWORD*)(bdata->port + 8);
102 UWORD *dst = address;
104 count /= 2;
105 while (count-- != 0)
106 *addr = *dst++;
110 static void ata_outl(ULONG val, UWORD offset, IPTR port, APTR data)
114 static void ata_out(UBYTE val, UWORD offset, IPTR port, APTR data)
116 struct amiga_busdata *bdata = data;
117 volatile UBYTE *addr;
119 #if DEBUG2
120 bug("ata_out(%x,%x)=%x:%x\n", offset, port, (UBYTE*)(bdata->port + port) + offset * 4, val);
121 #endif
122 /* IDE doubler hides Alternate Status/Device Control register */
123 if (port == -1) {
124 if (bdata->reset == 0 && (val & 4)) {
125 ata_out(0x40, ata_DevHead, 0, bdata);
126 D(bug("[ATA] Emulating reset\n"));
127 ata_out(ATA_EXECUTE_DIAG, ata_Command, 0, bdata);
129 bdata->reset = (val & 4) != 0;
130 return;
132 addr = (UBYTE*)(bdata->port + port);
133 addr[offset * 4] = val;
136 static UBYTE ata_in(UWORD offset, IPTR port, APTR data)
138 struct amiga_busdata *bdata = data;
139 volatile UBYTE *addr;
140 UBYTE v;
142 #if DEBUG2
143 bug("ata_in(%x,%x)=%x\n", offset, port, (UBYTE*)(bdata->port + port) + offset * 4);
144 #endif
145 if (port == -1) {
146 port = 0;
147 offset = ata_Status;
149 addr = (UBYTE*)(bdata->port + port);
150 v = addr[offset * 4];
151 #if DEBUG2
152 bug("=%x\n", v);
153 #endif
154 return v;
157 static void ata_pcmcia_out(UBYTE val, UWORD offset, IPTR port, APTR data)
159 volatile UBYTE *addr;
161 if (offset == ata_Feature)
162 offset = 13;
164 addr = (UBYTE*)port;
165 addr[offset] = val;
168 static UBYTE ata_pcmcia_in(UWORD offset, IPTR port, APTR data)
170 volatile UBYTE *addr;
172 if (offset == ata_Feature)
173 offset = 13;
175 addr = (UBYTE*)port;
176 return addr[offset];
180 static BOOL custom_check(APTR addr)
182 volatile struct Custom *custom = (struct Custom*)0xdff000;
183 volatile struct Custom *maybe_custom = (struct Custom*)addr;
184 UWORD intena;
185 BOOL iscustom = TRUE;
187 intena = custom->intenar;
188 custom->intena = 0x7fff;
189 custom->intena = 0xc000;
190 maybe_custom->intena = 0x7fff;
191 if (custom->intenar == 0x4000) {
192 maybe_custom->intena = 0x7fff;
193 if (custom->intenar == 0x4000)
194 iscustom = FALSE;
196 custom->intena = 0x7fff;
197 custom->intena = intena | 0x8000;
198 return iscustom;
201 static UBYTE *getport(struct amiga_driverdata *ddata)
203 UBYTE id, status1, status2;
204 volatile UBYTE *port, *altport;
205 struct GfxBase *gfx;
207 port = NULL;
208 gfx = (struct GfxBase*)TaggedOpenLibrary(TAGGEDOPEN_GRAPHICS);
209 Disable();
210 id = ReadGayle();
211 if (id) {
212 port = (UBYTE*)GAYLE_BASE_1200;
213 ddata->gayleirqbase = (UBYTE*)GAYLE_IRQ_1200;
214 } else {
215 // in AGA this area is never custom mirror but lets make sure..
216 if (!custom_check((APTR)0xdd4000) && (gfx->ChipRevBits0 & GFXF_AA_ALICE)) {
217 port = (UBYTE*)GAYLE_BASE_4000;
218 ddata->a4000 = TRUE;
219 ddata->gayleirqbase = (UBYTE*)GAYLE_IRQ_4000;
222 Enable();
223 CloseLibrary((struct Library*)gfx);
225 D(bug("[ATA] Gayle ID=%02x. Possible IDE port=%08x.\n", id, (ULONG)port & ~3));
226 if (port == NULL)
227 return NULL;
229 altport = port + 0x1010;
230 Disable();
231 port[atapi_DevSel * 4] = ATAF_ERROR;
232 /* If nothing connected, we get back what we wrote, ATAF_ERROR set */
233 status1 = port[ata_Status * 4];
234 port[atapi_DevSel * 4] = ATAF_DATAREQ;
235 status2 = port[ata_Status * 4];
236 port[atapi_DevSel * 4] = 0;
237 Enable();
238 D(bug("[ATA] Status=%02x,%02x\n", status1, status2));
239 // BUSY and DRDY both active or ERROR/DATAREQ = no drive(s) = do not install driver
240 if ( (((status1 | status2) & (ATAF_BUSY | ATAF_DRDY)) == (ATAF_BUSY | ATAF_DRDY))
241 || ((status1 | status2) & (ATAF_ERROR | ATAF_DATAREQ)))
243 D(bug("[ATA] Drives not detected\n"));
244 return NULL;
246 if (ddata->doubler) {
247 UBYTE v1, v2;
248 /* check if AltControl is both readable and writable
249 * It is either floating or DevHead if IDE doubler is connected.
250 * AltControl = DevHead (R)
251 * Device Control = DevHead (W)
253 Disable();
254 altport[ata_AltControl * 4] = 0;
255 port[atapi_DevSel * 4] = 1;
256 v1 = altport[ata_AltControl * 4];
257 altport[ata_AltControl * 4] = 2;
258 port[atapi_DevSel * 4] = 4;
259 v2 = altport[ata_AltControl * 4];
260 altport[ata_AltControl * 4] = 0;
261 port[atapi_DevSel * 4] = 0;
262 Enable();
263 if ((v1 == 0 && v2 == 2) || (v1 == 1 && v2 == 4) || (v1 == 0xff && v2 == 0xff)) {
264 ddata->doubler = 2;
265 } else {
266 ddata->doubler = 0;
268 D(bug("[ATA] IDE doubler check (%02X, %02X) = %d\n", v1, v2, ddata->doubler));
270 /* we may have connected drives */
271 return (UBYTE*)port;
274 static void callbusirq(struct amiga_driverdata *ddata)
276 volatile UBYTE *port;
277 UBYTE status1, status2;
278 BOOL handled = FALSE;
280 if (ddata->bus[0])
281 handled |= ata_HandleIRQ(ddata->bus[0]->bus);
282 if (ddata->bus[1])
283 handled |= ata_HandleIRQ(ddata->bus[1]->bus);
284 if (handled)
285 return;
287 /* Handle spurious interrupt */
288 port = ddata->gaylebase;
289 status1 = port[ata_Status * 4];
290 status2 = 0;
291 if (ddata->doubler == 2)
292 status2 = port[0x1000 + ata_Status * 4];
293 bug("[ATA] Spurious interrupt: %02X %02X\n", status1, status2);
296 AROS_UFH4(APTR, IDE_Handler_A1200,
297 AROS_UFHA(ULONG, dummy, A0),
298 AROS_UFHA(void *, data, A1),
299 AROS_UFHA(ULONG, dummy2, A5),
300 AROS_UFHA(struct ExecBase *, mySysBase, A6))
302 AROS_USERFUNC_INIT
304 struct amiga_driverdata *ddata = data;
305 UBYTE irqmask = *ddata->gayleirqbase;
306 if (irqmask & GAYLE_IRQ_IDE) {
307 /* Clear interrupt */
308 *ddata->gayleirqbase = 0x7c | (*ddata->gayleirqbase & 3);
309 callbusirq(ddata);
311 return 0;
313 AROS_USERFUNC_EXIT
316 AROS_UFH4(APTR, IDE_Handler_A4000,
317 AROS_UFHA(ULONG, dummy, A0),
318 AROS_UFHA(void *, data, A1),
319 AROS_UFHA(ULONG, dummy2, A5),
320 AROS_UFHA(struct ExecBase *, mySysBase, A6))
322 AROS_USERFUNC_INIT
324 struct amiga_driverdata *ddata = data;
325 /* A4000 interrupt clears when register is read */
326 UWORD irqmask = *((UWORD*)ddata->gayleirqbase);
327 if (irqmask & (GAYLE_IRQ_IDE << 8)) {
328 callbusirq(ddata);
330 return 0;
332 AROS_USERFUNC_EXIT
335 AROS_UFH4(UBYTE, IDE_PCMCIA_Handler,
336 AROS_UFHA(UBYTE, status, D0),
337 AROS_UFHA(void *, data, A1),
338 AROS_UFHA(ULONG, dummy2, A5),
339 AROS_UFHA(struct ExecBase *, mySysBase, A6))
341 AROS_USERFUNC_INIT
343 struct amiga_pcmcia_driverdata *ddata = data;
344 if (ddata->poststatus) {
345 if (ddata->intena)
346 ata_HandleIRQ(ddata->bus[0]->bus);
347 } else if (status & CARD_INTF_IRQ) {
348 ddata->poststatus = TRUE;
351 return status;
353 AROS_USERFUNC_EXIT
356 static APTR ata_CreateInterrupt(struct ata_Bus *bus, UBYTE num)
358 struct amiga_busdata *bdata = bus->ab_DriverData;
359 struct amiga_driverdata *ddata = bdata->ddata;
360 struct Interrupt *irq = &ddata->ideint;
361 volatile UBYTE *gayleintbase = NULL;
363 bdata->bus = bus;
364 ddata->bus[num] = bdata;
366 if (ddata->ideintdone)
367 return bdata;
368 ddata->ideintdone = TRUE;
370 if (ddata->a4000) {
371 irq->is_Code = (APTR)IDE_Handler_A4000;
372 } else {
373 gayleintbase = (UBYTE*)GAYLE_INT_1200;
374 irq->is_Code = (APTR)IDE_Handler_A1200;
377 irq->is_Node.ln_Pri = 20;
378 irq->is_Node.ln_Type = NT_INTERRUPT;
379 irq->is_Node.ln_Name = "AT-IDE";
380 irq->is_Data = ddata;
381 AddIntServer(INTB_PORTS, irq);
383 if (gayleintbase)
384 *gayleintbase |= GAYLE_INT_IDE;
386 return bdata;
388 static APTR ata_CreateInterrupt0(struct ata_Bus *bus)
390 return ata_CreateInterrupt(bus, 0);
392 static APTR ata_CreateInterrupt1(struct ata_Bus *bus)
394 return ata_CreateInterrupt(bus, 1);
396 static APTR ata_CreateInterrupt_pcmcia(struct ata_Bus *bus)
398 struct amiga_busdata *bdata = bus->ab_DriverData;
399 struct amiga_pcmcia_driverdata *ddata = bdata->ddata;
401 bdata->bus = bus;
402 ddata->bus[0] = bdata;
404 ddata->intena = 1;
405 return bdata;
408 static const struct ata_BusDriver amiga_driver0 =
410 ata_out,
411 ata_in,
412 ata_outl,
413 ata_insw,
414 ata_outsw,
415 ata_insw, /* These are intentionally the same as 16-bit routines */
416 ata_outsw,
417 ata_CreateInterrupt0
419 static const struct ata_BusDriver amiga_driver1 =
421 ata_out,
422 ata_in,
423 ata_outl,
424 ata_insw,
425 ata_outsw,
426 ata_insw, /* These are intentionally the same as 16-bit routines */
427 ata_outsw,
428 ata_CreateInterrupt1
430 static const struct ata_BusDriver amiga_driver_pcmcia =
432 ata_pcmcia_out,
433 ata_pcmcia_in,
434 ata_outl,
435 ata_pcmcia_insw,
436 ata_pcmcia_outsw,
437 ata_pcmcia_insw,
438 ata_pcmcia_outsw,
439 ata_CreateInterrupt_pcmcia
442 static BOOL ata_amiga_ide_init(struct ataBase *LIBBASE)
444 struct amiga_driverdata *ddata;
445 struct amiga_busdata *bdata;
447 ddata = AllocVec(sizeof(struct amiga_driverdata), MEMF_CLEAR | MEMF_PUBLIC);
448 if (!ddata)
449 return FALSE;
450 ddata->doubler = 1;
452 ddata->gaylebase = getport(ddata);
453 bdata = AllocVec(sizeof(struct amiga_busdata) * (ddata->doubler == 2 ? 2 : 1), MEMF_CLEAR | MEMF_PUBLIC);
454 if (bdata && ddata->gaylebase) {
455 LIBBASE->ata_NoDMA = TRUE;
456 bdata->ddata = ddata;
457 bdata->port = ddata->gaylebase;
458 ata_RegisterBus(0, ddata->doubler ? -1 : 0x1010, 2, 0, ARBF_EarlyInterrupt, &amiga_driver0, bdata, LIBBASE);
459 if (ddata->doubler == 2) {
460 D(bug("[ATA] Adding secondary bus\n"));
461 bdata++;
462 bdata->ddata = ddata;
463 bdata->port = ddata->gaylebase + 0x1000;
464 ata_RegisterBus(0, -1, 2, 0, ARBF_EarlyInterrupt, &amiga_driver1, bdata, LIBBASE);
466 return TRUE;
468 FreeVec(bdata);
469 FreeVec(ddata);
470 return FALSE;
473 static BOOL detectcard(struct amiga_pcmcia_driverdata *ddata)
475 APTR CardResource;
476 struct CardHandle *ch;
477 UBYTE tuple[256 + 2];
478 WORD cnt1, cnt2;
479 UBYTE *tp;
480 BOOL got;
482 ch = &ddata->cardhandle;
483 CardResource = ddata->CardResource;
485 ddata->configmask = 1;
486 ddata->configbase = 0x0200;
488 CardResetCard(ch);
489 CardMiscControl(ch, CARD_ENABLEF_DIGAUDIO | CARD_DISABLEF_WP);
491 got = FALSE;
492 for (;;) {
493 if (!CopyTuple(ch, tuple, PCCARD_TPL_DEVICE, sizeof(tuple) - 2))
494 break;
495 if (!DeviceTuple(tuple, &ddata->dtd))
496 break;
497 if (ddata->dtd.dtd_DTtype != PCCARD_DTYPE_FUNCSPEC)
498 break;
499 tuple[2] = 0;
500 if (!CopyTuple(ch, tuple, PCCARD_TPL_FUNCID, sizeof(tuple) - 2))
501 break;
502 if (tuple[2] != PCCARD_FUNC_FIXED)
503 break;
504 got = FALSE;
505 for (cnt1 = 0; TRUE; cnt1++) {
506 if (!CopyTuple(ch, tuple, PCCARD_TPL_FUNCE | (cnt1 << 16), sizeof(tuple) - 2))
507 break;
508 if (tuple[2] != 1 || tuple[3] != 1)
509 break;
510 got = TRUE;
511 break;
513 if (!got)
514 break;
515 got = FALSE;
516 if (!CopyTuple(ch, tuple, PCCARD_TPL_CONFIG, sizeof(tuple) - 2))
517 break;
518 if (tuple[1] < 5)
519 break;
520 //lastindex = tuple[3] & 0x3f;
521 tp = &tuple[4];
522 cnt2 = (tuple[2] & 3) + 1;
523 for (cnt1 = 0; cnt1 < cnt2; cnt1++) {
524 ddata->configbase |= (*tp) << (cnt1 * 8);
525 tp++;
527 cnt2 = ((tuple[2] >> 3) & 15) + 1;
528 for (cnt1 = 0; cnt1 < cnt2; cnt1++) {
529 ddata->configmask |= (*tp) << (cnt1 * 8);
530 tp++;
532 return TRUE;
534 return FALSE;
537 static void initializecard(struct amiga_pcmcia_driverdata *ddata)
539 struct CardHandle *ch;
540 UBYTE tuple[256 + 2];
541 UBYTE *tp;
542 APTR CardResource;
543 volatile UBYTE *attrbase;
545 ch = &ddata->cardhandle;
546 CardResource = ddata->CardResource;
548 D(bug("Detected PCMCIA IDE. ConfigBase=%08x RMask=%08x\n", ddata->configbase, ddata->configmask);
549 memset(tuple, 0, sizeof tuple);
550 if (CopyTuple(ch, tuple, PCCARD_TPL_VERS1, sizeof(tuple) - 2)) {
551 if (tuple[2] == 4) {
552 tp = &tuple[4];
553 while (*tp != 0xff) {
554 bug("%s ", tp);
555 tp += strlen(tp) + 1;
557 D(bug("\n"));
560 CardAccessSpeed(ch, ddata->dtd.dtd_DTspeed);
561 attrbase = ddata->cmm->cmm_AttributeMemory;
562 attrbase[ddata->configbase + 2 * 3] = 0; /* Socket and copy. Must be written first. */
563 attrbase[ddata->configbase + 2 * 2] = 0x0f; /* Pin replacement. */
564 attrbase[ddata->configbase + 2 * 1] = 0; /* Configuration and Status. */
565 attrbase[ddata->configbase + 2 * 0] = 0x41; /* Configure option. Configure as IO linear mode. */
566 /* Now we have IDE registers at iobase */
569 static BOOL ata_amiga_pcmcia_init(struct ataBase *LIBBASE)
571 struct CardResource *CardResource;
572 struct amiga_pcmcia_driverdata *ddata;
573 struct amiga_busdata *bdata;
574 struct CardHandle *ch;
576 CardResource = OpenResource("card.resource");
577 if (!CardResource)
578 return FALSE;
579 if (CardInterface() != CARD_INTERFACE_AMIGA_0)
580 return FALSE;
582 ddata = AllocVec(sizeof(struct amiga_pcmcia_driverdata) + sizeof(struct amiga_busdata), MEMF_CLEAR | MEMF_PUBLIC);
583 if (!ddata)
584 return FALSE;
585 bdata = (struct amiga_busdata*)(ddata + 1);
587 ch = &ddata->cardhandle;
588 ddata->CardResource = CardResource;
589 ddata->cmm = GetCardMap();
591 ch->cah_CardFlags = CARDF_IFAVAILABLE | CARDF_POSTSTATUS;
592 ch->cah_CardNode.ln_Name = LIBBASE->ata_Device.dd_Library.lib_Node.ln_Name;
593 ch->cah_CardStatus = &ddata->statusint;
594 ch->cah_CardRemoved = &ddata->removalint;
595 ch->cah_CardInserted = &ddata->insertint;
596 ch->cah_CardStatus->is_Data = ddata;
597 ch->cah_CardStatus->is_Code = (void*)IDE_PCMCIA_Handler;
598 #if 0
599 ch->cah_CardRemoved->is_Data = ddata;
600 ch->cah_CardRemoved->is_Code = (void*)IDE_PCMCIA_Removed;
601 ch->cah_CardInserted->is_Data = ddata;
602 ch->cah_CardInserted->is_Code = (void*)IDE_PCMCIA_Inserted
603 #endif
605 if (!OwnCard(ch)) {
606 BeginCardAccess(ch);
608 if (detectcard(ddata)) {
609 initializecard(ddata);
610 bdata->ddata = ddata;
611 bdata->port = (UBYTE*)ddata->cmm->cmm_IOMemory;
612 LIBBASE->ata_NoDMA = TRUE;
613 ata_RegisterBus((IPTR)ddata->cmm->cmm_IOMemory, (IPTR)(ddata->cmm->cmm_IOMemory + 14 - ata_AltControl), 2, 0, ARBF_EarlyInterrupt, &amiga_driver_pcmcia, bdata, LIBBASE);
614 return TRUE;
617 EndCardAccess(ch);
618 ReleaseCard(ch, CARDF_REMOVEHANDLE);
621 FreeVec(ddata);
623 return FALSE;
626 static int ata_amiga_init(struct ataBase *LIBBASE)
628 BOOL r_ide, r_pcmcia;
630 r_ide = ata_amiga_ide_init(LIBBASE);
631 r_pcmcia = ata_amiga_pcmcia_init(LIBBASE);
632 return (r_ide || r_pcmcia) ? 1 : 0;
635 ADD2INITLIB(ata_amiga_init, 20)