2 Copyright © 2004-2008, The AROS Development Team. All rights reserved
11 * ---------- ------------------ -------------------------------------------------------------------
12 * 2008-04-25 P. Fedin Brought back device discovery for old machines without PCI IDE controllers
13 * 2008-01-25 T. Wiszkowski Rebuilt, rearranged and partially fixed 60% of the code here
14 * Enabled implementation to scan for other PCI IDE controllers
15 * Implemented ATAPI Packet Support for both read and write
16 * Corrected ATAPI DMA handling
17 * Fixed major IDE enumeration bugs severely handicapping transfers with more than one controller
18 * Compacted source and implemented major ATA support procedure
19 * Improved DMA and Interrupt management
20 * Removed obsolete code
21 * 2008-01-26 T. Wiszkowski Added 'nodma' flag for ata driver
22 * Moved variables out of global scope
23 * Replaced static variables
24 * 2008-02-08 T. Wiszkowski Fixed DMA accesses for direct scsi devices,
25 * Corrected IO Areas to allow ATA to talk to PCI controllers
26 * 2008-02-24 T. Wiszkowski Corrected unit open function
27 * 2008-03-03 T. Wiszkowski Added drive reselection + setup delay on Init
28 * 2008-03-23 T. Wiszkowski Corrected Alternative Command block position
29 * 2008-03-30 T. Wiszkowski Added workaround for interrupt collision handling; fixed SATA in LEGACY mode.
30 * nForce and Intel SATA chipsets should now be operational.
31 * 2008-04-03 T. Wiszkowski Fixed IRQ flood issue, eliminated and reduced obsolete / redundant code
32 * 2008-04-07 T. Wiszkowski Changed bus timeout mechanism
33 * 2008-04-07 M. Schulz The SiL3114 chip yields Class 0x01 and SubClass 0x80. Therefore it will
34 * not be find with the generic enumeration. Do an explicit search after it
35 * since ata.device may handle it in legacy mode without any issues.
36 * 2008-05-11 T. Wiszkowski Remade the ata trannsfers altogether, corrected the pio/irq handling
37 * medium removal, device detection, bus management and much more
38 * 2008-05-18 T. Wiszkowski corrected device naming to handle cases where more than 10 physical units may be available
42 #include <aros/debug.h>
44 #include <aros/symbolsets.h>
46 #include <exec/types.h>
47 #include <exec/exec.h>
48 #include <exec/resident.h>
49 #include <exec/tasks.h>
50 #include <exec/memory.h>
51 #include <exec/nodes.h>
52 #include <utility/utility.h>
54 #include <libraries/expansion.h>
55 #include <libraries/configvars.h>
58 #include <dos/filehandler.h>
61 #include <proto/exec.h>
62 #include <proto/timer.h>
63 #include <proto/bootloader.h>
64 #include <proto/expansion.h>
68 #include <proto/oop.h>
72 #include LC_LIBDEFS_FILE
77 struct ataBase
*ATABase
;
82 /* Add a bootnode using expansion.library */
83 BOOL
AddVolume(ULONG StartCyl
, ULONG EndCyl
, struct ata_Unit
*unit
)
85 struct ExpansionBase
*ExpansionBase
;
86 struct DeviceNode
*devnode
;
88 TEXT dosdevname
[4] = "HD0", *handler
;
91 ExpansionBase
= (struct ExpansionBase
*)OpenLibrary("expansion.library",
96 pp
= AllocMem(24*sizeof(IPTR
), MEMF_PUBLIC
| MEMF_CLEAR
);
100 /* This should be dealt with using some sort of volume manager or such. */
101 switch (unit
->au_DevType
)
103 case DG_DIRECT_ACCESS
:
109 D(bug("IDE: AddVolume called on unknown devicetype\n"));
112 if (unit
->au_UnitNum
< 10)
113 dosdevname
[2] += unit
->au_UnitNum
% 10;
115 dosdevname
[2] = 'A' - 10 + unit
->au_UnitNum
;
116 pp
[0] = (IPTR
)dosdevname
;
117 pp
[1] = (IPTR
)MOD_NAME_STRING
;
118 pp
[2] = unit
->au_UnitNum
;
119 pp
[DE_TABLESIZE
+ 4] = DE_BOOTBLOCKS
;
120 pp
[DE_SIZEBLOCK
+ 4] = 1 << (unit
->au_SectorShift
- 2);
121 pp
[DE_NUMHEADS
+ 4] = unit
->au_Heads
;
122 pp
[DE_SECSPERBLOCK
+ 4] = 1;
123 pp
[DE_BLKSPERTRACK
+ 4] = unit
->au_Sectors
;
124 pp
[DE_RESERVEDBLKS
+ 4] = 2;
125 pp
[DE_LOWCYL
+ 4] = StartCyl
;
126 pp
[DE_HIGHCYL
+ 4] = EndCyl
;
127 pp
[DE_NUMBUFFERS
+ 4] = 10;
128 pp
[DE_BUFMEMTYPE
+ 4] = MEMF_PUBLIC
| MEMF_CHIP
;
129 pp
[DE_MAXTRANSFER
+ 4] = 0x00200000;
130 pp
[DE_MASK
+ 4] = 0x7FFFFFFE;
131 pp
[DE_BOOTPRI
+ 4] = ((!unit
->au_DevType
) ? 0 : 10);
132 pp
[DE_DOSTYPE
+ 4] = 0x444F5301;
133 pp
[DE_BOOTBLOCKS
+ 4] = 2;
134 devnode
= MakeDosNode(pp
);
138 if(unit
->au_DevType
== DG_DIRECT_ACCESS
)
139 handler
= "afs.handler";
141 handler
= "cdrom.handler";
142 len
= strlen(handler
);
143 if ((devnode
->dn_Handler
=
144 MKBADDR(AllocMem(AROS_BSTR_MEMSIZE4LEN(len
),
145 MEMF_PUBLIC
| MEMF_CLEAR
150 CopyMem(handler
, AROS_BSTR_ADDR(devnode
->dn_Handler
), len
);
151 AROS_BSTR_setstrlen(devnode
->dn_Handler
, len
);
153 D(bug("-Adding volume %s with SC=%d, EC=%d\n",
154 &(devnode
->dn_Ext
.dn_AROS
.dn_DevName
[0]), StartCyl
, EndCyl
));
155 AddBootNode(pp
[DE_BOOTPRI
+ 4], 0, devnode
, 0);
163 CloseLibrary((struct Library
*)ExpansionBase
);
169 static void Add_Device(IPTR IOBase
, IPTR IOAlt
, IPTR INTLine
,
170 IPTR DMABase
, int x
, EnumeratorArgs
*a
)
173 * static list of io/irqs that we can handle
189 * ata bus - this is going to be created and linked to the master list here
194 * see if IO Base is valid. otherwise pick device from static list
195 * (this most likely means the device is right there)
199 if (a
->PredefBus
< (sizeof(Buses
) / sizeof(Buses
[0])))
202 * collect IOBase and interrupt from the above list
204 IOBase
= Buses
[a
->PredefBus
].port
;
205 IOAlt
= Buses
[a
->PredefBus
].alt
;
206 INTLine
= Buses
[a
->PredefBus
].irq
;
215 bug("[ATA>>] Found more controllers\n");
217 * we're all done. no idea what else they want from us
223 D(bug("[ATA>>] IO: %x:%x DMA: %x\n", IOBase
, IOAlt
, DMABase
));
226 * initialize structure
228 ab
= (struct ata_Bus
*) AllocVecPooled(a
->ATABase
->ata_MemPool
, sizeof(struct ata_Bus
));
232 ab
->ab_Base
= a
->ATABase
;
233 ab
->ab_Port
= IOBase
;
235 ab
->ab_Irq
= INTLine
;
236 ab
->ab_Dev
[0] = DEV_NONE
;
237 ab
->ab_Dev
[1] = DEV_NONE
;
239 ab
->ab_SleepySignal
= 0;
240 ab
->ab_BusNum
= a
->CurrentBus
++;
244 ab
->ab_IntHandler
= (HIDDT_IRQ_Handler
*)AllocVecPooled(a
->ATABase
->ata_MemPool
, sizeof(HIDDT_IRQ_Handler
));
246 ab
->ab_HandleIRQ
= 0;
248 D(bug("[ATA>>] Analysing bus %d, units %d and %d\n", ab
->ab_BusNum
, ab
->ab_BusNum
<<1, (ab
->ab_BusNum
<<1)+1));
253 ab
->ab_PRD
= AllocVecPooled(a
->ATABase
->ata_MemPool
, (PRD_MAX
+1) * 2 * sizeof(struct PRDEntry
));
254 if ((0x10000 - ((ULONG
)ab
->ab_PRD
& 0xffff)) < PRD_MAX
* sizeof(struct PRDEntry
))
255 ab
->ab_PRD
= (void*)((((IPTR
)ab
->ab_PRD
)+0xffff) &~ 0xffff);
258 * scan bus - try to locate all devices (disables irq)
261 if (ab
->ab_Dev
[0] > DEV_UNKNOWN
)
263 ab
->ab_Units
[0] = AllocVecPooled(a
->ATABase
->ata_MemPool
, sizeof(struct ata_Unit
));
264 ab
->ab_Units
[0]->au_DMAPort
= (DMABase
!= 0 ? DMABase
+ (x
<<3) : 0);
265 ata_init_unit(ab
, 0);
267 if (ab
->ab_Dev
[1] > DEV_UNKNOWN
)
269 ab
->ab_Units
[1] = AllocVecPooled(a
->ATABase
->ata_MemPool
, sizeof(struct ata_Unit
));
270 ab
->ab_Units
[1]->au_DMAPort
= (DMABase
!= 0 ? DMABase
+ (x
<<3) : 0);
271 ata_init_unit(ab
, 1);
274 D(bug("[ATA>>] Bus %ld: Unit 0 - %x, Unit 1 - %x\n", ab
->ab_BusNum
, ab
->ab_Dev
[0], ab
->ab_Dev
[1]));
278 * note: this happens no matter there are devices or not
279 * sort of almost-ready-for-hotplug ;)
281 AddTail((struct List
*)&a
->ATABase
->ata_Buses
, (struct Node
*)ab
);
286 * collect ALL ata/ide capable devices (including SATA and other) and spawn consecutive tasks
288 * This function is growing too large. It will shorten drasticly once this whole mess gets converted into c++
292 AROS_UFH3(void, Enumerator
,
293 AROS_UFHA(struct Hook
*, hook
, A0
),
294 AROS_UFHA(OOP_Object
*, Device
, A2
),
295 AROS_UFHA(APTR
, message
,A1
))
300 * parameters we will want to acquire
312 OOP_AttrBase HiddPCIDeviceAttrBase
= OOP_ObtainAttrBase(IID_Hidd_PCIDevice
);
315 * new parameters for every device:
316 * - allow bus mastering
318 struct TagItem attrs
[] =
320 { aHidd_PCIDevice_isMaster
, TRUE
},
327 EnumeratorArgs
*a
= (EnumeratorArgs
*)hook
->h_Data
;
330 * temporary variables
335 * obtain more or less useful data
337 OOP_GetAttr(Device
, aHidd_PCIDevice_ProductID
, &ProductID
);
338 OOP_GetAttr(Device
, aHidd_PCIDevice_VendorID
, &VendorID
);
339 OOP_GetAttr(Device
, aHidd_PCIDevice_Base4
, &DMABase
);
341 if (a
->ATABase
->ata_NoDMA
)
345 * we can have as many as four ports assigned to this device
350 * obtain base and interrupt
355 OOP_GetAttr(Device
, aHidd_PCIDevice_Base0
, &IOBase
);
356 OOP_GetAttr(Device
, aHidd_PCIDevice_Base1
, &IOAlt
);
359 OOP_GetAttr(Device
, aHidd_PCIDevice_Base2
, &IOBase
);
360 OOP_GetAttr(Device
, aHidd_PCIDevice_Base3
, &IOAlt
);
363 OOP_GetAttr(Device
, aHidd_PCIDevice_INTLine
, &INTLine
);
365 D(bug("[ATA.scanbus] IDE device %04x:%04x - IO: %x:%x DMA: %x\n", ProductID
, VendorID
, IOBase
, IOAlt
, DMABase
));
366 Add_Device(IOBase
, IOAlt
, INTLine
, DMABase
, x
, a
);
374 D(bug("[ATA ] Bus0 status says %02x, Bus1 status says %02x\n", ata_in(2, DMABase
), ata_in(10, DMABase
)));
376 OOP_SetAttrs(Device
, attrs
);
377 OOP_ReleaseAttrBase(IID_Hidd_PCIDevice
);
383 void ata_Scan(struct ataBase
*base
)
386 struct SignalSemaphore ssem
;
397 D(bug("[ATA--] Enumerating devices\n"));
399 pci
= OOP_NewObject(NULL
, CLID_Hidd_PCI
, NULL
);
403 struct Hook FindHook
= {
404 h_Entry
: (IPTR (*)())Enumerator
,
408 struct TagItem Requirements
[] = {
409 {tHidd_PCI_Class
, 0x01},
410 {tHidd_PCI_SubClass
, 0x01},
414 struct pHidd_PCI_EnumDevices enummsg
= {
415 mID
: OOP_GetMethodID(IID_Hidd_PCI
, moHidd_PCI_EnumDevices
),
417 requirements
: (struct TagItem
*)&Requirements
,
420 OOP_DoMethod(pci
, (OOP_Msg
)msg
);
423 * The SiL3114 chip yields Class 0x01 and SubClass 0x80. Therefore it will not be find
424 * with the enumeration above. Do an explicit search now since ata.device may handle it
425 * in legacy mode without any issues.
427 * Note: This chip is used on Sam440 board.
429 Requirements
[0].ti_Tag
= tHidd_PCI_VendorID
;
430 Requirements
[0].ti_Data
= 0x1095;
431 Requirements
[1].ti_Tag
= tHidd_PCI_ProductID
;
432 Requirements
[1].ti_Data
= 0x3114;
434 OOP_DoMethod(pci
, (OOP_Msg
)msg
);
436 Requirements
[0].ti_Tag
= tHidd_PCI_VendorID
;
437 Requirements
[0].ti_Data
= 0x1095;
438 Requirements
[1].ti_Tag
= tHidd_PCI_ProductID
;
439 Requirements
[1].ti_Data
= 0x3512;
441 OOP_DoMethod(pci
, (OOP_Msg
)msg
);
443 OOP_DisposeObject(pci
);
445 if (!Args
.CurrentBus
) {
446 D(bug("[ATA--] No PCI devices found, attempting defaults\n"));
448 Add_Device(0, 0, 0, 0, i
& 1, &Args
);
452 InitSemaphore(&ssem
);
453 ForeachNode(&base
->ata_Buses
, node
)
455 ata_InitBusTask((struct ata_Bus
*)node
, &ssem
);
459 * wait for all buses to complete their init
461 ObtainSemaphore(&ssem
);
466 ReleaseSemaphore(&ssem
);
470 Here shall we start. Make function static as it shouldn't be visible from
473 static int ata_init(LIBBASETYPEPTR LIBBASE
)
475 struct BootLoaderBase
*BootLoaderBase
;
478 * I've decided to use memory pools again. Alloc everything needed from
479 * a pool, so that we avoid memory fragmentation.
481 LIBBASE
->ata_MemPool
= CreatePool(MEMF_CLEAR
| MEMF_PUBLIC
| MEMF_SEM_PROTECTED
, 8192, 4096);
482 if (LIBBASE
->ata_MemPool
== NULL
)
486 * store library pointer so we can use it later
488 LIBBASE
->ata_32bit
= FALSE
;
489 LIBBASE
->ata_NoDMA
= FALSE
;
492 * start initialization:
493 * obtain kernel parameters
495 D(bug("[ATA--] ata.device initialization\n"));
496 BootLoaderBase
= OpenResource("bootloader.resource");
497 D(bug("[ATA--] BootloaderBase = %p\n", BootLoaderBase
));
498 if (BootLoaderBase
!= NULL
)
503 list
= (struct List
*)GetBootInfo(BL_Args
);
506 ForeachNode(list
, node
)
508 if (strncmp(node
->ln_Name
, "ATA=", 4) == 0)
510 if (strstr(node
->ln_Name
, "32bit"))
512 D(bug("[ATA ] Using 32-bit IO transfers\n"));
513 LIBBASE
->ata_32bit
= TRUE
;
515 if (strstr(node
->ln_Name
, "nodma"))
517 D(bug("[ATA ] Disabled DMA transfers\n"));
518 LIBBASE
->ata_NoDMA
= TRUE
;
526 * Initialize BUS list
528 LIBBASE
->ata_Buses
.mlh_Head
= (struct MinNode
*) &LIBBASE
->ata_Buses
.mlh_Tail
;
529 LIBBASE
->ata_Buses
.mlh_Tail
= NULL
;
530 LIBBASE
->ata_Buses
.mlh_TailPred
= (struct MinNode
*) &LIBBASE
->ata_Buses
.mlh_Head
;
534 /* Try to setup daemon task looking for diskchanges */
535 ata_InitDaemonTask(LIBBASE
);
541 LIBBASETYPEPTR LIBBASE
,
542 struct IORequest
*iorq
,
555 iorq
->io_Error
= IOERR_OPENFAIL
;
560 struct ata_Bus
*b
= (struct ata_Bus
*)LIBBASE
->ata_Buses
.mlh_Head
;
563 * Extract bus and device numbers
565 bus
= unitnum
>> 1; // 0xff00 >> 8
566 dev
= (unitnum
& 0x1); // 0x00ff
573 b
= (struct ata_Bus
*)b
->ab_Node
.mln_Succ
;
578 if (b
->ab_Node
.mln_Succ
== NULL
)
584 if (b
->ab_Units
[dev
] == NULL
)
590 iorq
->io_Device
= &LIBBASE
->ata_Device
;
591 iorq
->io_Unit
= &b
->ab_Units
[dev
]->au_Unit
;
594 b
->ab_Units
[dev
]->au_Unit
.unit_OpenCnt
++;
599 /* Close given device */
602 LIBBASETYPEPTR LIBBASE
,
603 struct IORequest
*iorq
606 struct ata_Unit
*unit
= (struct ata_Unit
*)iorq
->io_Unit
;
608 /* First of all make the important fields of struct IORequest invalid! */
609 iorq
->io_Unit
= (struct Unit
*)~0;
611 /* Decrease use counters of unit */
612 unit
->au_Unit
.unit_OpenCnt
--;
617 ADD2INITLIB(ata_init
, 0)
619 ADD2CLOSEDEV(close
, 0)
620 ADD2LIBS("irq.hidd", 0, static struct Library
*, __irqhidd
)
621 /* vim: set ts=8 sts=4 et : */