- HD_SCSICMD: assume there is no sense-data buffer if no AUTOSENSE
[AROS.git] / rom / devs / ata / bus_pci.c
blob315ea610cb3ab6f84bf6046cd8b897c23ad064e9
1 /*
2 Copyright © 2004-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: PCI bus driver for ata.device
6 Lang: English
7 */
9 #define DSATA(x)
12 * What is done here is currently a draft.
13 * The whole thing is a good candidate to become a HIDD.
16 #include <aros/asmcall.h>
17 #include <aros/debug.h>
18 #include <aros/symbolsets.h>
19 #include <asm/io.h>
20 #include <exec/lists.h>
21 #include <hardware/ahci.h>
22 #include <hidd/pci.h>
23 #include <oop/oop.h>
24 #include <proto/exec.h>
25 #include <proto/oop.h>
27 #include <string.h>
29 #include "ata.h"
30 #include "pci.h"
32 typedef struct
34 struct ataBase *ATABase;
35 struct MinList legacybuses;
36 struct List probedbuses;
37 ULONG ata__buscount;
38 OOP_AttrBase HiddPCIDeviceAttrBase;
39 OOP_MethodID HiddPCIDriverMethodBase;
40 } EnumeratorArgs;
42 struct ata_LegacyBus
44 struct MinNode atalb_Node;
45 IPTR atalb_IOBase;
46 IPTR atalb_IOAlt;
47 IPTR atalb_INTLine;
48 UBYTE atalb_ControllerID;
49 UBYTE atalb_BusID;
52 struct ata_ProbedBus
54 struct Node atapb_Node;
55 IPTR atapb_IOBase;
56 IPTR atapb_IOAlt;
57 IPTR atapb_INTLine;
58 IPTR atapb_DMABase;
59 BOOL atapb_80wire;
62 #define ATABUSNODEPRI_PROBED 50
63 #define ATABUSNODEPRI_PROBEDLEGACY 100
64 #define ATABUSNODEPRI_LEGACY 0
66 #define RANGESIZE0 8
67 #define RANGESIZE1 4
68 #define DMASIZE 16
70 /* static list of io/irqs that we can handle */
71 struct ata__legacybus
73 ULONG lb_Port;
74 ULONG lb_Alt;
75 UBYTE lb_IRQ;
76 UBYTE lb_ControllerID;
77 UBYTE lb_Bus;
80 static const struct ata__legacybus LegacyBuses[] =
82 #if defined(__i386__) || defined(__x86_64__)
83 {0x1f0, 0x3f4, 14, 0, 0},
84 {0x170, 0x374, 15, 0, 1},
85 {0x168, 0x36c, 10, 1, 0},
86 {0x1e8, 0x3ec, 11, 1, 1},
87 #endif
88 {0, 0, 0, 0, 0},
92 * Low-level bus I/O functions.
93 * They are called directly via array of pointers supplied by our driver.
94 * This should be kept this way even if bus driver part is made into a HIDD
95 * some day, for speedup.
98 #if defined(__i386__) || defined(__x86_64__)
100 /* On x86 ATA devices use I/O space, data is ignored. */
102 static void ata_out(UBYTE val, UWORD offset, IPTR port, APTR data)
104 outb(val, offset + port);
107 static UBYTE ata_in(UWORD offset, IPTR port, APTR data)
109 return inb(offset + port);
112 static void ata_outl(ULONG val, UWORD offset, IPTR port, APTR data)
114 outl(val, offset + port);
117 static VOID ata_insw(APTR address, UWORD port, ULONG count, APTR data)
119 insw(port, address, count >> 1);
122 static VOID ata_insl(APTR address, UWORD port, ULONG count, APTR data)
124 if (count & 2)
125 insw(port, address, count >> 1);
126 else
127 insl(port, address, count >> 2);
130 static VOID ata_outsw(APTR address, UWORD port, ULONG count, APTR data)
132 outsw(port, address, count >> 1);
135 static VOID ata_outsl(APTR address, UWORD port, ULONG count, APTR data)
137 if (count & 2)
138 outsw(port, address, count >> 1);
139 else
140 outsl(port, address, count >> 2);
143 #else
146 * This came from SAM440 port. Data is a base address of mapped ISA I/O space.
147 * I believe this should work fine on all non-x86 architectures.
150 static VOID ata_out(UBYTE val, UWORD offset, IPTR port, APTR data)
152 outb(val, (uint8_t *)(port + offset + data));
155 static UBYTE ata_in(UWORD offset, IPTR port, APTR data)
157 return inb((uint8_t *)(port + offset + data));
160 static VOID ata_outl(ULONG val, UWORD offset, IPTR port, APTR data)
162 outl_le(val, (uint32_t *)(port + offset + data));
165 static VOID ata_insw(APTR address, UWORD port, ULONG count, APTR data)
167 UWORD *addr = address;
168 UWORD *p = (UWORD*)(port + data);
170 while(count)
172 *addr++ = inw(p);
173 count -= 2;
177 static VOID ata_insl(APTR address, UWORD port, ULONG count, APTR data)
179 if (count & 2)
180 ata_insw(address, port, count, data);
181 else
183 ULONG *addr = address;
184 ULONG *p = (ULONG*)(port + data);
186 while(count)
188 *addr++ = inl(p);
189 count -= 4;
194 static VOID ata_outsw(APTR address, UWORD port, ULONG count, APTR data)
196 UWORD *addr = address;
197 UWORD *p = (UWORD*)(port + data);
199 while(count)
201 outw(*addr++, p);
202 count -= 2;
206 static VOID ata_outsl(APTR address, UWORD port, ULONG count, APTR data)
208 if (count & 2)
209 ata_outsw(address, port, count, data);
210 else
212 ULONG *addr = address;
213 ULONG *p = (ULONG*)(port + data);
215 while(count)
217 outl(*addr++, p);
218 count -= 4;
223 #endif
225 AROS_INTH1(ata_Interrupt, APTR, data)
227 AROS_INTFUNC_INIT
229 * Our interrupt handler should call this function.
230 * It's our problem how to store bus pointer. Here we use h_Data for it.
232 ata_HandleIRQ(data);
234 return FALSE;
236 AROS_INTFUNC_EXIT
239 /* Actually a quick hack. Proper implementation really needs HIDDizing this code. */
240 static BOOL CreateInterrupt(struct ata_Bus *bus)
242 struct Interrupt *IntHandler = &bus->ab_IntHandler;
245 Prepare nice interrupt for our bus. Even if interrupt sharing is enabled,
246 it should work quite well
248 IntHandler->is_Node.ln_Type = NT_INTERRUPT;
249 IntHandler->is_Node.ln_Pri = 10;
250 IntHandler->is_Node.ln_Name = bus->ab_Task->tc_Node.ln_Name;
251 IntHandler->is_Code = (VOID_FUNC)ata_Interrupt;
252 IntHandler->is_Data = bus;
254 AddIntServer(INTB_KERNEL + bus->ab_IRQ, IntHandler);
255 return TRUE;
258 static const struct ata_BusDriver pci_driver =
260 ata_out,
261 ata_in,
262 ata_outl,
263 ata_insw,
264 ata_outsw,
265 ata_insl,
266 ata_outsl,
267 CreateInterrupt
271 * PCI BUS ENUMERATOR
272 * collect ALL ata/ide capable devices (including SATA and other) and
273 * spawn concurrent tasks.
275 * This function is growing too large. It will shorten drasticly once this whole mess gets converted into c++
278 static
279 AROS_UFH3(void, ata_PCIEnumerator_h,
280 AROS_UFHA(struct Hook *, hook, A0),
281 AROS_UFHA(OOP_Object *, Device, A2),
282 AROS_UFHA(APTR, message,A1))
284 AROS_USERFUNC_INIT
287 * parameters we will want to acquire
289 IPTR ProductID,
290 VendorID,
291 DMABase,
292 DMASize,
293 INTLine,
294 IOBase,
295 IOAlt,
296 IOSize,
297 AltSize,
298 SubClass,
299 Interface;
302 * enumerator params
304 EnumeratorArgs *a = hook->h_Data;
307 * the PCI Attr Base
309 OOP_AttrBase HiddPCIDeviceAttrBase = a->HiddPCIDeviceAttrBase;
312 * temporary variables
314 int x;
317 * obtain more or less useful data
319 OOP_GetAttr(Device, aHidd_PCIDevice_VendorID, &VendorID);
320 OOP_GetAttr(Device, aHidd_PCIDevice_ProductID, &ProductID);
321 OOP_GetAttr(Device, aHidd_PCIDevice_Base4, &DMABase);
322 OOP_GetAttr(Device, aHidd_PCIDevice_Size4, &DMASize);
323 OOP_GetAttr(Device, aHidd_PCIDevice_SubClass, &SubClass);
324 OOP_GetAttr(Device, aHidd_PCIDevice_Interface, &Interface);
326 D(bug("[PCI-ATA] ata_PCIEnumerator_h: Found IDE device %04x:%04x\n", VendorID, ProductID));
329 * SATA controllers are handled by the AHCI driver.
331 if (SubClass == PCI_SUBCLASS_SATA) {
332 DSATA(bug("[PCI-ATA] Device %04x:%04x is a SATA device, ignoring\n", VendorID, ProductID));
333 return;
337 * we can have up to two buses assigned to this device
339 for (x = 0; SubClass != 0 && SubClass != 7 && x < MAX_DEVICEBUSES; x++)
341 struct ata_LegacyBus *_legacyBus = NULL;
342 BOOL isLegacy = FALSE;
345 * obtain I/O bases and interrupt line
347 if ((Interface & (1 << (x << 1))) || SubClass != PCI_SUBCLASS_IDE)
349 switch (x)
351 case 0:
352 OOP_GetAttr(Device, aHidd_PCIDevice_Base0, &IOBase);
353 OOP_GetAttr(Device, aHidd_PCIDevice_Size0, &IOSize);
354 OOP_GetAttr(Device, aHidd_PCIDevice_Base1, &IOAlt);
355 OOP_GetAttr(Device, aHidd_PCIDevice_Size1, &AltSize);
356 break;
357 case 1:
358 OOP_GetAttr(Device, aHidd_PCIDevice_Base2, &IOBase);
359 OOP_GetAttr(Device, aHidd_PCIDevice_Size2, &IOSize);
360 OOP_GetAttr(Device, aHidd_PCIDevice_Base3, &IOAlt);
361 OOP_GetAttr(Device, aHidd_PCIDevice_Size3, &AltSize);
362 break;
364 OOP_GetAttr(Device, aHidd_PCIDevice_INTLine, &INTLine);
366 else if ((_legacyBus = (struct ata_LegacyBus *)
367 a->legacybuses.mlh_Head)->atalb_ControllerID == 0)
369 Remove((struct Node *)_legacyBus);
370 IOBase = _legacyBus->atalb_IOBase;
371 IOAlt = _legacyBus->atalb_IOAlt;
372 INTLine = _legacyBus->atalb_INTLine;
373 FreeMem(_legacyBus, sizeof(struct ata_LegacyBus));
374 isLegacy = TRUE;
375 IOSize = RANGESIZE0;
376 AltSize = RANGESIZE1;
378 else
380 bug("[PCI-ATA] ata_PCIEnumerator_h: Ran out of legacy buses\n");
381 IOBase = 0;
384 if (IOBase != 0 && IOSize == RANGESIZE0
385 && AltSize == RANGESIZE1
386 && (DMASize >= DMASIZE || DMABase == 0 || SubClass == PCI_SUBCLASS_IDE))
388 struct ata_ProbedBus *probedbus;
389 D(bug("[PCI-ATA] ata_PCIEnumerator_h: Adding Bus %d - IRQ %d, IO: %x:%x, DMA: %x\n", x, INTLine, IOBase, IOAlt, DMABase));
391 if ((probedbus = AllocMem(sizeof(struct ata_ProbedBus), MEMF_CLEAR | MEMF_PUBLIC)) != NULL)
393 probedbus->atapb_IOBase = IOBase;
394 probedbus->atapb_IOAlt = IOAlt;
395 probedbus->atapb_INTLine = INTLine;
396 if (DMABase != 0)
397 probedbus->atapb_DMABase = DMABase + (x << 3);
398 probedbus->atapb_80wire = TRUE;
400 if (isLegacy)
402 D(bug("[PCI-ATA] ata_PCIEnumerator_h: Device using Legacy-Bus IOPorts\n"));
403 probedbus->atapb_Node.ln_Pri = ATABUSNODEPRI_PROBEDLEGACY - (a->ata__buscount++);
405 else
406 probedbus->atapb_Node.ln_Pri = ATABUSNODEPRI_PROBED - (a->ata__buscount++);
408 Enqueue((struct List *)&a->probedbuses, &probedbus->atapb_Node);
410 OOP_SetAttrsTags(Device, aHidd_PCIDevice_isIO, TRUE,
411 aHidd_PCIDevice_isMaster, DMABase != 0,
412 TAG_DONE);
417 /* check dma status if applicable */
418 D(if (DMABase != 0) bug("[PCI-ATA] ata_PCIEnumerator_h: Bus0 DMA Status %02x, Bus1 DMA Status %02x\n", ata_in(2, DMABase, (APTR)0xe8000000), ata_in(10, DMABase, (APTR)0xe8000000));)
420 AROS_USERFUNC_EXIT
423 static const struct TagItem Requirements[] =
425 {tHidd_PCI_Class, PCI_CLASS_MASSSTORAGE},
426 {TAG_DONE, 0x00 }
429 static int ata_pci_Scan(struct ataBase *base)
431 OOP_Object *pci;
432 struct ata_LegacyBus *_legacybus;
433 struct ata_ProbedBus *probedbus;
434 BOOL scanpci = TRUE;
435 BOOL scanlegacy = TRUE;
436 EnumeratorArgs Args;
437 int i;
439 /* Prepare lists for probed/found ide buses */
440 NEWLIST(&Args.legacybuses);
441 NEWLIST(&Args.probedbuses);
442 Args.ata__buscount = 0;
444 /* Build the list of possible legacy-bus ports */
445 for (i = 0; LegacyBuses[i].lb_Port != 0 ; i++)
447 if ((_legacybus = AllocMem(sizeof(struct ata_LegacyBus), MEMF_CLEAR | MEMF_PUBLIC)) != NULL)
449 D(bug("[PCI-ATA] ata_init: Prepare Legacy Bus %d:%d entry [IOPorts %x:%x IRQ %d]\n", LegacyBuses[i].lb_ControllerID, LegacyBuses[i].lb_Bus, LegacyBuses[i].lb_Port, LegacyBuses[i].lb_Alt, LegacyBuses[i].lb_IRQ));
451 _legacybus->atalb_IOBase = (IPTR)LegacyBuses[i].lb_Port;
452 _legacybus->atalb_IOAlt = (IPTR)LegacyBuses[i].lb_Alt;
453 _legacybus->atalb_INTLine = (IPTR)LegacyBuses[i].lb_IRQ;
454 _legacybus->atalb_ControllerID = (IPTR)LegacyBuses[i].lb_ControllerID;
455 _legacybus->atalb_BusID = (IPTR)LegacyBuses[i].lb_Bus;
457 AddTail((struct List *)&Args.legacybuses, (struct Node *)_legacybus);
461 /* Obtain additional parameters */
462 if (base->ata_CmdLine)
464 if (strstr(base->ata_CmdLine, "nopci"))
466 D(bug("[PCI-ATA] ata_init: Disabling PCI device scan\n"));
467 scanpci = FALSE;
469 if (strstr(base->ata_CmdLine, "nolegacy"))
471 D(bug("[PCI-ATA] ata_init: Disabling Legacy ports\n"));
472 scanlegacy = FALSE;
476 D(bug("[PCI-ATA] ata_Scan: Enumerating devices\n"));
478 if (scanpci)
480 D(bug("[PCI-ATA] ata_Scan: Checking for supported PCI devices ..\n"));
482 Args.ATABase = base;
483 Args.HiddPCIDriverMethodBase = 0;
484 Args.HiddPCIDeviceAttrBase = OOP_ObtainAttrBase(IID_Hidd_PCIDevice);
486 if (Args.HiddPCIDeviceAttrBase)
488 pci = OOP_NewObject(NULL, CLID_Hidd_PCI, NULL);
490 if (pci)
492 struct Hook FindHook =
494 h_Entry: (IPTR (*)())ata_PCIEnumerator_h,
495 h_Data: &Args
498 struct pHidd_PCI_EnumDevices enummsg =
500 mID: OOP_GetMethodID(IID_Hidd_PCI, moHidd_PCI_EnumDevices),
501 callback: &FindHook,
502 requirements: Requirements,
505 OOP_DoMethod(pci, &enummsg.mID);
506 OOP_DisposeObject(pci);
509 OOP_ReleaseAttrBase(IID_Hidd_PCIDevice);
513 if (scanlegacy)
515 struct ata_LegacyBus *legacybus;
517 D(bug("[PCI-ATA] ata_Scan: Adding Remaining Legacy-Buses\n"));
518 while ((legacybus = (struct ata_LegacyBus *)
519 RemHead((struct List *)&Args.legacybuses)) != NULL)
521 if ((probedbus = AllocMem(sizeof(struct ata_ProbedBus), MEMF_CLEAR | MEMF_PUBLIC)) != NULL)
523 probedbus->atapb_IOBase = legacybus->atalb_IOBase;
524 probedbus->atapb_IOAlt = legacybus->atalb_IOAlt;
525 probedbus->atapb_INTLine = legacybus->atalb_INTLine;
526 probedbus->atapb_DMABase = 0;
527 probedbus->atapb_80wire = FALSE;
528 probedbus->atapb_Node.ln_Pri = ATABUSNODEPRI_LEGACY - (Args.ata__buscount++);
529 D(bug("[PCI-ATA] ata_Scan: Adding Legacy Bus - IO: %x:%x\n",
530 probedbus->atapb_IOBase, probedbus->atapb_IOAlt));
532 Enqueue(&Args.probedbuses, &probedbus->atapb_Node);
535 FreeMem(legacybus, sizeof(struct ata_LegacyBus));
538 D(bug("[PCI-ATA] ata_Scan: Registering Probed Buses..\n"));
540 while ((probedbus = (struct ata_ProbedBus *)RemHead(&Args.probedbuses)) != NULL)
543 * 0xe8000000 here is a temporary kludge for SAM440 port. It's base address
544 * of memory-mapped ISA I/O space.
545 * In fact our PCI subsystem needs an attribute to be able to query this value.
546 * We don't use definition from asm/amcc440.h because this file is available
547 * only when doing SAM440 build.
548 * On x86 this value is ignored, see I/O functions.
550 ata_RegisterBus(probedbus->atapb_IOBase, probedbus->atapb_IOAlt, probedbus->atapb_INTLine,
551 probedbus->atapb_DMABase, probedbus->atapb_80wire ? ARBF_80Wire : 0,
552 &pci_driver, (APTR)0xe8000000, base);
554 FreeMem(probedbus, sizeof(struct ata_ProbedBus));
557 return TRUE;
561 * ata.device main code has two init routines with 0 and 127 priorities.
562 * All bus scanners must run between them.
564 ADD2INITLIB(ata_pci_Scan, 30)