2 Copyright © 2004-2011, The AROS Development Team. All rights reserved.
5 Desc: PCI bus driver for ata.device
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>
20 #include <exec/lists.h>
21 #include <hardware/ahci.h>
24 #include <proto/exec.h>
25 #include <proto/oop.h>
34 struct ataBase
*ATABase
;
35 struct MinList legacybuses
;
36 struct List probedbuses
;
38 OOP_AttrBase HiddPCIDeviceAttrBase
;
39 OOP_MethodID HiddPCIDriverMethodBase
;
44 struct MinNode atalb_Node
;
48 UBYTE atalb_ControllerID
;
54 struct Node atapb_Node
;
62 #define ATABUSNODEPRI_PROBED 50
63 #define ATABUSNODEPRI_PROBEDLEGACY 100
64 #define ATABUSNODEPRI_LEGACY 0
70 /* static list of io/irqs that we can handle */
76 UBYTE lb_ControllerID
;
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},
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
)
125 insw(port
, address
, count
>> 1);
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
)
138 outsw(port
, address
, count
>> 1);
140 outsl(port
, address
, count
>> 2);
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
);
177 static VOID
ata_insl(APTR address
, UWORD port
, ULONG count
, APTR data
)
180 ata_insw(address
, port
, count
, data
);
183 ULONG
*addr
= address
;
184 ULONG
*p
= (ULONG
*)(port
+ data
);
194 static VOID
ata_outsw(APTR address
, UWORD port
, ULONG count
, APTR data
)
196 UWORD
*addr
= address
;
197 UWORD
*p
= (UWORD
*)(port
+ data
);
206 static VOID
ata_outsl(APTR address
, UWORD port
, ULONG count
, APTR data
)
209 ata_outsw(address
, port
, count
, data
);
212 ULONG
*addr
= address
;
213 ULONG
*p
= (ULONG
*)(port
+ data
);
225 AROS_INTH1(ata_Interrupt
, APTR
, data
)
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.
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
);
258 static const struct ata_BusDriver pci_driver
=
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++
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
))
287 * parameters we will want to acquire
304 EnumeratorArgs
*a
= hook
->h_Data
;
309 OOP_AttrBase HiddPCIDeviceAttrBase
= a
->HiddPCIDeviceAttrBase
;
312 * temporary variables
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
));
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
)
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
);
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
);
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
));
376 AltSize
= RANGESIZE1
;
380 bug("[PCI-ATA] ata_PCIEnumerator_h: Ran out of legacy buses\n");
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
;
397 probedbus
->atapb_DMABase
= DMABase
+ (x
<< 3);
398 probedbus
->atapb_80wire
= TRUE
;
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
++);
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,
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));)
423 static const struct TagItem Requirements
[] =
425 {tHidd_PCI_Class
, PCI_CLASS_MASSSTORAGE
},
429 static int ata_pci_Scan(struct ataBase
*base
)
432 struct ata_LegacyBus
*_legacybus
;
433 struct ata_ProbedBus
*probedbus
;
435 BOOL scanlegacy
= TRUE
;
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"));
469 if (strstr(base
->ata_CmdLine
, "nolegacy"))
471 D(bug("[PCI-ATA] ata_init: Disabling Legacy ports\n"));
476 D(bug("[PCI-ATA] ata_Scan: Enumerating devices\n"));
480 D(bug("[PCI-ATA] ata_Scan: Checking for supported PCI devices ..\n"));
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
);
492 struct Hook FindHook
=
494 h_Entry
: (IPTR (*)())ata_PCIEnumerator_h
,
498 struct pHidd_PCI_EnumDevices enummsg
=
500 mID
: OOP_GetMethodID(IID_Hidd_PCI
, moHidd_PCI_EnumDevices
),
502 requirements
: Requirements
,
505 OOP_DoMethod(pci
, &enummsg
.mID
);
506 OOP_DisposeObject(pci
);
509 OOP_ReleaseAttrBase(IID_Hidd_PCIDevice
);
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
));
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)