make the linux-ppc packags be in synch with other platforms
[tangerine.git] / arch / common / ata.device / ata_init.c
blob51957c6afb3039c9632737784efc4d140a62f814
1 /*
2 Copyright © 2004-2008, The AROS Development Team. All rights reserved
3 $Id$
5 Desc:
6 Lang: English
7 */
8 /*
9 * CHANGELOG:
10 * DATE NAME ENTRY
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.
38 #define DEBUG 1
39 #include <aros/debug.h>
41 #include <aros/symbolsets.h>
43 #include <exec/types.h>
44 #include <exec/exec.h>
45 #include <exec/resident.h>
46 #include <exec/tasks.h>
47 #include <exec/memory.h>
48 #include <exec/nodes.h>
49 #include <utility/utility.h>
50 #include <oop/oop.h>
51 #include <libraries/expansion.h>
52 #include <libraries/configvars.h>
54 #include <dos/bptr.h>
55 #include <dos/filehandler.h>
56 #include <string.h>
58 #include <proto/exec.h>
59 #include <proto/timer.h>
60 #include <proto/bootloader.h>
61 #include <proto/expansion.h>
63 #include <oop/oop.h>
64 #include <hidd/pci.h>
65 #include <proto/oop.h>
68 #include "ata.h"
69 #include LC_LIBDEFS_FILE
72 typedef struct
74 struct ataBase *ATABase;
75 UWORD CurrentBus;
76 UWORD PredefBus;
77 } EnumeratorArgs;
79 /* Add a bootnode using expansion.library */
80 BOOL AddVolume(ULONG StartCyl, ULONG EndCyl, struct ata_Unit *unit)
82 struct ExpansionBase *ExpansionBase;
83 struct DeviceNode *devnode;
84 IPTR *pp;
85 static int volnum;
86 TEXT dosdevname[4] = "HD0", *handler;
87 UWORD len;
89 ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library",
90 40L);
92 if (ExpansionBase)
94 pp = AllocMem(24*sizeof(IPTR), MEMF_PUBLIC | MEMF_CLEAR);
96 if (pp)
98 /* This should be dealt with using some sort of volume manager or such. */
99 switch (unit->au_DevType)
101 case DG_DIRECT_ACCESS:
102 break;
103 case DG_CDROM:
104 dosdevname[0] = 'C';
105 break;
106 default:
107 D(bug("IDE: AddVolume called on unknown devicetype\n"));
109 dosdevname[2] += volnum;
110 pp[0] = (IPTR)dosdevname;
111 pp[1] = (IPTR)MOD_NAME_STRING;
112 pp[2] = unit->au_UnitNum;
113 pp[DE_TABLESIZE + 4] = DE_BOOTBLOCKS;
114 pp[DE_SIZEBLOCK + 4] = 1 << (unit->au_SectorShift - 2);
115 pp[DE_NUMHEADS + 4] = unit->au_Heads;
116 pp[DE_SECSPERBLOCK + 4] = 1;
117 pp[DE_BLKSPERTRACK + 4] = unit->au_Sectors;
118 pp[DE_RESERVEDBLKS + 4] = 2;
119 pp[DE_LOWCYL + 4] = StartCyl;
120 pp[DE_HIGHCYL + 4] = EndCyl;
121 pp[DE_NUMBUFFERS + 4] = 10;
122 pp[DE_BUFMEMTYPE + 4] = MEMF_PUBLIC | MEMF_CHIP;
123 pp[DE_MAXTRANSFER + 4] = 0x00200000;
124 pp[DE_MASK + 4] = 0x7FFFFFFE;
125 pp[DE_BOOTPRI + 4] = ((!unit->au_DevType) ? 0 : 10);
126 pp[DE_DOSTYPE + 4] = 0x444F5301;
127 pp[DE_BOOTBLOCKS + 4] = 2;
128 devnode = MakeDosNode(pp);
130 if (devnode)
132 if(unit->au_DevType == DG_DIRECT_ACCESS)
133 handler = "afs.handler";
134 else
135 handler = "cdrom.handler";
136 len = strlen(handler);
137 if ((devnode->dn_Handler =
138 MKBADDR(AllocMem(AROS_BSTR_MEMSIZE4LEN(len),
139 MEMF_PUBLIC | MEMF_CLEAR
144 CopyMem(handler, AROS_BSTR_ADDR(devnode->dn_Handler), len);
145 AROS_BSTR_setstrlen(devnode->dn_Handler, len);
147 D(bug("-Adding volume %s with SC=%d, EC=%d\n",
148 &(devnode->dn_Ext.dn_AROS.dn_DevName[0]), StartCyl, EndCyl));
149 AddBootNode(pp[DE_BOOTPRI + 4], 0, devnode, 0);
150 D(bug("done\n"));
152 volnum++;
154 return TRUE;
159 CloseLibrary((struct Library *)ExpansionBase);
162 return FALSE;
165 static void Add_Device(IPTR IOBase, IPTR IOAlt, IPTR INTLine,
166 IPTR DMABase, int x, EnumeratorArgs *a)
169 * static list of io/irqs that we can handle
171 static struct __bus
173 ULONG port;
174 ULONG alt;
175 UBYTE irq;
176 } Buses[] =
178 {0x1f0, 0x3f4, 14},
179 {0x170, 0x374, 15},
180 {0x168, 0x36c, 10},
181 {0x1e8, 0x3ec, 11},
185 * ata bus - this is going to be created and linked to the master list here
187 struct ata_Bus *ab;
190 * see if IO Base is valid. otherwise pick device from static list
191 * (this most likely means the device is right there)
193 if (IOBase == 0)
195 if (a->PredefBus < (sizeof(Buses) / sizeof(Buses[0])))
198 * collect IOBase and interrupt from the above list
200 IOBase = Buses[a->PredefBus].port;
201 IOAlt = Buses[a->PredefBus].alt;
202 INTLine = Buses[a->PredefBus].irq;
203 a->PredefBus++;
205 else
207 IOBase = 0;
208 IOAlt = 0;
209 DMABase = 0;
210 INTLine = 0;
211 bug("[ATA ] Found more controllers\n");
213 * we're all done. no idea what else they want from us
215 return;
219 D(bug("[ATA.Add_Device] IO: %x:%x DMA: %x\n", IOBase, IOAlt, DMABase));
222 * initialize structure
224 ab = (struct ata_Bus*) AllocVecPooled(a->ATABase->ata_MemPool, sizeof(struct ata_Bus));
225 if (ab == NULL)
226 return;
228 ab->ab_Base = a->ATABase;
229 ab->ab_Port = IOBase;
230 ab->ab_Alt = IOAlt;
231 ab->ab_Irq = INTLine;
232 ab->ab_Dev[0] = DEV_NONE;
233 ab->ab_Dev[1] = DEV_NONE;
234 ab->ab_Flags = 0;
235 ab->ab_SleepySignal = 0;
236 ab->ab_BusNum = a->CurrentBus++;
237 ab->ab_Waiting = FALSE;
238 ab->ab_Timeout = 0;
239 ab->ab_Units[0] = 0;
240 ab->ab_Units[1] = 0;
241 ab->ab_IntHandler = (HIDDT_IRQ_Handler *)AllocVecPooled(a->ATABase->ata_MemPool, sizeof(HIDDT_IRQ_Handler));
242 D(bug("[ATA ] Analysing bus %d, units %d and %d\n", ab->ab_BusNum, ab->ab_BusNum<<1, (ab->ab_BusNum<<1)+1));
245 * allocate DMA PRD
247 ab->ab_PRD = AllocVecPooled(a->ATABase->ata_MemPool, (PRD_MAX+1) * 2 * sizeof(struct PRDEntry));
248 if ((0x10000 - ((ULONG)ab->ab_PRD & 0xffff)) < PRD_MAX * sizeof(struct PRDEntry))
249 ab->ab_PRD = (void*)((((IPTR)ab->ab_PRD)+0xffff) &~ 0xffff);
251 InitSemaphore(&ab->ab_Lock);
254 * scan bus - try to locate all devices
256 ata_ScanBus(ab);
257 if (ab->ab_Dev[0] > DEV_UNKNOWN)
259 ab->ab_Units[0] = AllocVecPooled(a->ATABase->ata_MemPool, sizeof(struct ata_Unit));
260 ab->ab_Units[0]->au_DMAPort = (DMABase != 0 ? DMABase + (x<<3) : 0);
261 ata_init_unit(ab, 0);
263 if (ab->ab_Dev[1] > DEV_UNKNOWN)
265 ab->ab_Units[1] = AllocVecPooled(a->ATABase->ata_MemPool, sizeof(struct ata_Unit));
266 ab->ab_Units[1]->au_DMAPort = (DMABase != 0 ? DMABase + (x<<3) : 0);
267 ata_init_unit(ab, 1);
270 D(bug("[ATA ] Bus %ld: Unit 0 - %x, Unit 1 - %x\n", ab->ab_BusNum, ab->ab_Dev[0], ab->ab_Dev[1]));
273 * start things up :)
274 * note: this happens no matter there are devices or not
275 * sort of almost-ready-for-hotplug ;)
277 AddTail((struct List*)&a->ATABase->ata_Buses, (struct Node*)ab);
281 * PCI BUS ENUMERATOR
282 * collect ALL ata/ide capable devices (including SATA and other) and spawn consecutive tasks
284 * This function is growing too large. It will shorten drasticly once this whole mess gets converted into c++
287 static
288 AROS_UFH3(void, Enumerator,
289 AROS_UFHA(struct Hook *, hook, A0),
290 AROS_UFHA(OOP_Object *, Device, A2),
291 AROS_UFHA(APTR, message,A1))
293 AROS_USERFUNC_INIT
296 * parameters we will want to acquire
298 IPTR ProductID,
299 VendorID,
300 DMABase,
301 INTLine,
302 IOBase,
303 IOAlt;
306 * the PCI Attr Base
308 OOP_AttrBase HiddPCIDeviceAttrBase = OOP_ObtainAttrBase(IID_Hidd_PCIDevice);
311 * new parameters for every device:
312 * - allow bus mastering
314 struct TagItem attrs[] =
316 { aHidd_PCIDevice_isMaster, TRUE },
317 { TAG_DONE, 0UL }
321 * enumerator params
323 EnumeratorArgs *a = (EnumeratorArgs*)hook->h_Data;
326 * temporary variables
328 int x;
331 * obtain more or less useful data
333 OOP_GetAttr(Device, aHidd_PCIDevice_ProductID, &ProductID);
334 OOP_GetAttr(Device, aHidd_PCIDevice_VendorID, &VendorID);
335 OOP_GetAttr(Device, aHidd_PCIDevice_Base4, &DMABase);
337 if (a->ATABase->ata_NoDMA)
338 DMABase = 0;
341 * we can have as many as four ports assigned to this device
343 for (x=0; x<2; x++)
346 * obtain base and interrupt
348 switch (x)
350 case 0:
351 OOP_GetAttr(Device, aHidd_PCIDevice_Base0, &IOBase);
352 OOP_GetAttr(Device, aHidd_PCIDevice_Base1, &IOAlt);
353 break;
354 case 1:
355 OOP_GetAttr(Device, aHidd_PCIDevice_Base2, &IOBase);
356 OOP_GetAttr(Device, aHidd_PCIDevice_Base3, &IOAlt);
357 break;
359 OOP_GetAttr(Device, aHidd_PCIDevice_INTLine, &INTLine);
361 D(bug("[ATA.scanbus] IDE device %04x:%04x - IO: %x:%x DMA: %x\n", ProductID, VendorID, IOBase, IOAlt, DMABase));
362 Add_Device(IOBase, IOAlt, INTLine, DMABase, x, a);
367 * check dma status
369 if (DMABase != 0)
370 D(bug("[ATA ] Bus0 status says %02x, Bus1 status says %02x\n", ata_in(2, DMABase), ata_in(10, DMABase)));
372 OOP_SetAttrs(Device, attrs);
373 OOP_ReleaseAttrBase(IID_Hidd_PCIDevice);
375 AROS_USERFUNC_EXIT
379 void ata_Scan(struct ataBase *base)
381 OOP_Object *pci;
382 struct Node* node;
383 int i;
384 EnumeratorArgs Args=
386 base,
391 D(bug("[ATA--] Enumerating devices\n"));
393 pci = OOP_NewObject(NULL, CLID_Hidd_PCI, NULL);
395 if (pci)
397 struct Hook FindHook = {
398 h_Entry: (IPTR (*)())Enumerator,
399 h_Data: &Args
402 struct TagItem Requirements[] = {
403 {tHidd_PCI_Class, 0x01},
404 {tHidd_PCI_SubClass, 0x01},
405 {TAG_DONE, 0x00}
408 struct pHidd_PCI_EnumDevices enummsg = {
409 mID: OOP_GetMethodID(IID_Hidd_PCI, moHidd_PCI_EnumDevices),
410 callback: &FindHook,
411 requirements: (struct TagItem *)&Requirements,
412 }, *msg = &enummsg;
414 OOP_DoMethod(pci, (OOP_Msg)msg);
417 * The SiL3114 chip yields Class 0x01 and SubClass 0x80. Therefore it will not be find
418 * with the enumeration above. Do an explicit search now since ata.device may handle it
419 * in legacy mode without any issues.
421 * Note: This chip is used on Sam440 board.
423 Requirements[0].ti_Tag = tHidd_PCI_VendorID;
424 Requirements[0].ti_Data = 0x1095;
425 Requirements[1].ti_Tag = tHidd_PCI_ProductID;
426 Requirements[1].ti_Data = 0x3114;
428 OOP_DoMethod(pci, (OOP_Msg)msg);
430 OOP_DisposeObject(pci);
432 if (!Args.CurrentBus) {
433 D(bug("[ATA--] No PCI devices found, attempting defaults\n"));
434 for (i=0; i<4; i++)
435 Add_Device(0, 0, 0, 0, i & 1, &Args);
438 ForeachNode(&base->ata_Buses, node)
440 ata_InitBusTask((struct ata_Bus*)node);
445 Here shall we start. Make function static as it shouldn't be visible from
446 outside.
448 static int ata_init(LIBBASETYPEPTR LIBBASE)
450 struct BootLoaderBase *BootLoaderBase;
453 * I've decided to use memory pools again. Alloc everything needed from
454 * a pool, so that we avoid memory fragmentation.
456 LIBBASE->ata_MemPool = CreatePool(MEMF_CLEAR | MEMF_PUBLIC | MEMF_SEM_PROTECTED , 8192, 4096);
457 if (LIBBASE->ata_MemPool == NULL)
458 return FALSE;
461 * store library pointer so we can use it later
463 LIBBASE->ata_32bit = FALSE;
464 LIBBASE->ata_NoDMA = FALSE;
467 * start initialization:
468 * obtain kernel parameters
470 D(bug("[ATA--] ata.device initialization\n"));
471 BootLoaderBase = OpenResource("bootloader.resource");
472 D(bug("[ATA--] BootloaderBase = %p\n", BootLoaderBase));
473 if (BootLoaderBase != NULL)
475 struct List *list;
476 struct Node *node;
478 list = (struct List *)GetBootInfo(BL_Args);
479 if (list)
481 ForeachNode(list, node)
483 if (strncmp(node->ln_Name, "ATA=", 4) == 0)
485 if (strstr(node->ln_Name, "32bit"))
487 D(bug("[ATA ] Using 32-bit IO transfers\n"));
488 LIBBASE->ata_32bit = TRUE;
490 if (strstr(node->ln_Name, "nodma"))
492 D(bug("[ATA ] Disabled DMA transfers\n"));
493 LIBBASE->ata_NoDMA = TRUE;
501 * Initialize BUS list
503 LIBBASE->ata_Buses.mlh_Head = (struct MinNode*) &LIBBASE->ata_Buses.mlh_Tail;
504 LIBBASE->ata_Buses.mlh_Tail = NULL;
505 LIBBASE->ata_Buses.mlh_TailPred = (struct MinNode*) &LIBBASE->ata_Buses.mlh_Head;
507 ata_Scan(LIBBASE);
509 /* Try to setup daemon task looking for diskchanges */
510 ata_InitDaemonTask(LIBBASE);
511 return TRUE;
514 static int open
516 LIBBASETYPEPTR LIBBASE,
517 struct IORequest *iorq,
518 ULONG unitnum,
519 ULONG flags
523 * device location
525 ULONG bus, dev;
528 * Assume it failed
530 iorq->io_Error = IOERR_OPENFAIL;
533 * actual bus
535 struct ata_Bus *b = (struct ata_Bus*)LIBBASE->ata_Buses.mlh_Head;
538 * Extract bus and device numbers
540 bus = unitnum >> 1; // 0xff00 >> 8
541 dev = (unitnum & 0x1); // 0x00ff
544 * locate bus
546 while (bus--)
548 b = (struct ata_Bus*)b->ab_Node.mln_Succ;
549 if (b == NULL)
550 return FALSE;
553 if (b->ab_Node.mln_Succ == NULL)
554 return FALSE;
557 * locate unit
559 if (b->ab_Units[dev] == NULL)
560 return FALSE;
563 * set up iorequest
565 iorq->io_Device = &LIBBASE->ata_Device;
566 iorq->io_Unit = &b->ab_Units[dev]->au_Unit;
567 iorq->io_Error = 0;
569 b->ab_Units[dev]->au_Unit.unit_OpenCnt++;
571 return TRUE;
574 /* Close given device */
575 static int close
577 LIBBASETYPEPTR LIBBASE,
578 struct IORequest *iorq
581 struct ata_Unit *unit = (struct ata_Unit *)iorq->io_Unit;
583 /* First of all make the important fields of struct IORequest invalid! */
584 iorq->io_Unit = (struct Unit *)~0;
586 /* Decrease use counters of unit */
587 unit->au_Unit.unit_OpenCnt--;
589 return TRUE;
592 ADD2INITLIB(ata_init, 0)
593 ADD2OPENDEV(open, 0)
594 ADD2CLOSEDEV(close, 0)
595 ADD2LIBS("irq.hidd", 0, static struct Library *, __irqhidd)
596 /* vim: set ts=8 sts=4 et : */