- HD_SCSICMD: assume there is no sense-data buffer if no AUTOSENSE
[AROS.git] / rom / devs / ata / ata_init.c
blob9d1107987e7f41adb9c8187cbf981bfec5a47efd
1 /*
2 Copyright © 2004-2012, The AROS Development Team. All rights reserved
3 $Id$
5 Desc:
6 Lang: English
7 */
9 #include <aros/bootloader.h>
10 #include <aros/debug.h>
11 #include <aros/symbolsets.h>
12 #include <exec/exec.h>
13 #include <exec/resident.h>
14 #include <exec/tasks.h>
15 #include <exec/memory.h>
16 #include <exec/nodes.h>
17 #include <utility/utility.h>
18 #include <libraries/expansion.h>
19 #include <libraries/configvars.h>
20 #include <dos/bptr.h>
21 #include <dos/dosextens.h>
22 #include <dos/filehandler.h>
24 #include <proto/exec.h>
25 #include <proto/timer.h>
26 #include <proto/bootloader.h>
27 #include <proto/expansion.h>
29 #include <string.h>
31 #include "ata.h"
32 #include "timer.h"
34 #include LC_LIBDEFS_FILE
36 /* Add a bootnode using expansion.library */
37 BOOL ata_RegisterVolume(ULONG StartCyl, ULONG EndCyl, struct ata_Unit *unit)
39 struct ExpansionBase *ExpansionBase;
40 struct DeviceNode *devnode;
41 TEXT dosdevname[4] = "HD0";
42 const ULONG IdDOS = AROS_MAKE_ID('D','O','S','\001');
43 const ULONG IdCDVD = AROS_MAKE_ID('C','D','V','D');
45 ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library",
46 40L);
48 if (ExpansionBase)
50 IPTR pp[24];
52 /* This should be dealt with using some sort of volume manager or such. */
53 switch (unit->au_DevType)
55 case DG_DIRECT_ACCESS:
56 break;
57 case DG_CDROM:
58 dosdevname[0] = 'C';
59 break;
60 default:
61 D(bug("[ATA>>]:-ata_RegisterVolume called on unknown devicetype\n"));
64 if (unit->au_UnitNum < 10)
65 dosdevname[2] += unit->au_UnitNum % 10;
66 else
67 dosdevname[2] = 'A' - 10 + unit->au_UnitNum;
69 pp[0] = (IPTR)dosdevname;
70 pp[1] = (IPTR)MOD_NAME_STRING;
71 pp[2] = unit->au_UnitNum;
72 pp[DE_TABLESIZE + 4] = DE_BOOTBLOCKS;
73 pp[DE_SIZEBLOCK + 4] = 1 << (unit->au_SectorShift - 2);
74 pp[DE_NUMHEADS + 4] = unit->au_Heads;
75 pp[DE_SECSPERBLOCK + 4] = 1;
76 pp[DE_BLKSPERTRACK + 4] = unit->au_Sectors;
77 pp[DE_RESERVEDBLKS + 4] = 2;
78 pp[DE_LOWCYL + 4] = StartCyl;
79 pp[DE_HIGHCYL + 4] = EndCyl;
80 pp[DE_NUMBUFFERS + 4] = 10;
81 pp[DE_BUFMEMTYPE + 4] = MEMF_PUBLIC | MEMF_31BIT;
82 pp[DE_MAXTRANSFER + 4] = 0x00200000;
83 pp[DE_MASK + 4] = 0x7FFFFFFE;
84 pp[DE_BOOTPRI + 4] = ((unit->au_DevType == DG_DIRECT_ACCESS) ? 0 : 10);
85 pp[DE_DOSTYPE + 4] = ((unit->au_DevType == DG_DIRECT_ACCESS) ? IdDOS : IdCDVD);
86 pp[DE_CONTROL + 4] = 0;
87 pp[DE_BOOTBLOCKS + 4] = 2;
89 devnode = MakeDosNode(pp);
91 if (devnode)
93 D(bug("[ATA>>]:-ata_RegisterVolume: '%b', type=0x%08lx with StartCyl=%d, EndCyl=%d .. ",
94 devnode->dn_Name, pp[DE_DOSTYPE + 4], StartCyl, EndCyl));
96 AddBootNode(pp[DE_BOOTPRI + 4], ADNF_STARTPROC, devnode, NULL);
97 D(bug("done\n"));
99 return TRUE;
102 CloseLibrary((struct Library *)ExpansionBase);
105 return FALSE;
108 static AROS_INTH1(ATAResetHandler,struct ata_Bus *, bus)
110 AROS_INTFUNC_INIT
112 struct ata_Unit *unit;
113 UWORD i;
115 /* Stop DMA */
116 for (i = 0; i < MAX_BUSUNITS; i++)
118 unit = bus->ab_Units[i];
119 if (unit != NULL)
121 if(unit->au_DMAPort != 0)
123 dma_StopDMA(unit);
124 BUS_OUTL(0, dma_PRD, unit->au_DMAPort);
129 /* Disable interrupts */
130 BUS_OUT(0x2, ata_AltControl, bus->ab_Alt);
132 return FALSE;
134 AROS_INTFUNC_EXIT
138 * This routine needs to be called by bus probe code in order to register a device.
139 * IOBase - base address of primary I/O registers on your bus.
140 * IOAlt - base address of secondary I/O register bank. Zero if no secondary bank
141 * is present. (IDE splitter on Amiga(tm), for example).
142 * DMABase - base address of DMA controller on your bus. Zero if DMA is not supported.
143 * Flags - Misc flags
144 * driver - structure holding pointers to I/O functions (for speedup)
145 * driverData - driver-specific data, whatever it needs.
147 * Flags: - ARBF_80Wire
148 * Set if your drive is connected using 80-wire cable. Enables high-speed
149 * UDMA modes (where appropriate).
150 * - ARBF_EarlyInterrupt
151 * Setup interrupt handler before IDE bus probe to catch possible spurious
152 * interrupts (IDE splitter disables access to ata_devcon register)
154 * When a HIDD subsystem is implemented, these parameters will become HIDD attributes.
156 void ata_RegisterBus(IPTR IOBase, IPTR IOAlt, IPTR INTLine, IPTR DMABase, ULONG Flags,
157 const struct ata_BusDriver *driver, APTR driverData, struct ataBase *ATABase)
160 * ata bus - this is going to be created and linked to the master list here
162 struct ata_Bus *ab;
164 UWORD i;
167 * initialize structure
169 ab = (struct ata_Bus*) AllocVecPooled(ATABase->ata_MemPool, sizeof(struct ata_Bus));
170 if (ab == NULL)
171 return;
173 ab->ab_Base = ATABase;
174 ab->ab_Port = IOBase;
175 ab->ab_Alt = IOAlt;
176 ab->ab_IRQ = INTLine;
177 ab->ab_Dev[0] = DEV_NONE;
178 ab->ab_Dev[1] = DEV_NONE;
179 ab->ab_Flags = 0;
180 ab->ab_SleepySignal = 0;
181 ab->ab_BusNum = ATABase->ata__buscount++;
182 ab->ab_Timeout = 0;
183 ab->ab_Units[0] = NULL;
184 ab->ab_Units[1] = NULL;
185 ab->ab_Task = NULL;
186 ab->ab_HandleIRQ = NULL;
187 ab->ab_Driver = driver;
188 ab->ab_DriverData = driverData;
190 D(bug("[ATA>>] ata_RegisterBus: Analysing bus %d, units %d and %d\n", ab->ab_BusNum, ab->ab_BusNum<<1, (ab->ab_BusNum<<1)+1));
191 D(bug("[ATA>>] ata_RegisterBus: IRQ %d, IO: %x:%x, DMA: %x\n", INTLine, IOBase, IOAlt, DMABase));
194 * DMABase is also used for reporting interrupt status, so NoDMA == TRUE
195 * is not equal to DMABase == 0.
197 if (DMABase && (!ATABase->ata_NoDMA))
199 /* Allocate DMA PRD. Due to the nature of PCI bus it must be in 32-bit memory. */
200 ab->ab_PRD = AllocMem((PRD_MAX + 1) * 2 * sizeof(struct PRDEntry), MEMF_PUBLIC|MEMF_CLEAR|MEMF_31BIT);
202 if (ab->ab_PRD)
204 if ((0x10000 - ((IPTR)ab->ab_PRD & 0xffff)) < PRD_MAX * sizeof(struct PRDEntry))
205 ab->ab_PRD = (void*)((((IPTR)ab->ab_PRD)+0xffff) &~ 0xffff);
207 else
209 D(bug("[ATA>>] Failed to allocate DMA PRD! Disabling DMA for the bus.\n"));
210 DMABase = 0;
215 * add reset handler for this bus
217 ab->ab_ResetInt.is_Code = (VOID_FUNC)ATAResetHandler;
218 ab->ab_ResetInt.is_Data = ab;
219 AddResetCallback(&ab->ab_ResetInt);
221 /* catch possible spurious interrupts */
222 if (Flags & ARBF_EarlyInterrupt)
223 ab->ab_Driver->CreateInterrupt(ab);
226 * scan bus - try to locate all devices (disables irq)
228 ata_InitBus(ab);
229 for (i = 0; i < MAX_BUSUNITS; i++)
231 if (ab->ab_Dev[i] > DEV_UNKNOWN)
233 ab->ab_Units[i] = AllocVecPooled(ATABase->ata_MemPool,
234 sizeof(struct ata_Unit));
235 ab->ab_Units[i]->au_DMAPort = DMABase;
236 ab->ab_Units[i]->au_Flags = (Flags & ARBF_80Wire) ? AF_80Wire : 0;
237 ata_init_unit(ab, i);
241 D(bug("[ATA>>] ata_RegisterBus: Bus %ld: Unit 0 - %x, Unit 1 - %x\n", ab->ab_BusNum, ab->ab_Dev[0], ab->ab_Dev[1]));
244 * start things up :)
245 * note: this happens no matter there are devices or not
246 * sort of almost-ready-for-hotplug ;)
248 AddTail((struct List*)&ATABase->ata_Buses, (struct Node*)ab);
252 * This init routine has +127 priority, so it runs after all
253 * bus scanners have done their job.
254 * It initializes all discovered units.
256 static int ata_Scan(struct ataBase *base)
258 struct SignalSemaphore ssem;
259 struct ata_Bus* node;
260 struct Task *parent = FindTask(NULL);
262 D(bug("[ATA--] ata_Scan: Initialising Bus Tasks..\n"));
263 InitSemaphore(&ssem);
264 ForeachNode(&base->ata_Buses, node)
266 NewCreateTask(TASKTAG_PC , BusTaskCode,
267 TASKTAG_NAME , "ATA[PI] Subsystem",
268 TASKTAG_STACKSIZE , STACK_SIZE,
269 TASKTAG_PRI , TASK_PRI,
270 TASKTAG_TASKMSGPORT, &node->ab_MsgPort,
271 TASKTAG_ARG1 , node,
272 TASKTAG_ARG2 , parent,
273 TASKTAG_ARG3 , &ssem,
274 TAG_DONE);
276 /* Initial handshake */
277 Wait(SIGBREAKF_CTRL_C);
281 * wait for all buses to complete their init
283 D(bug("[ATA--] ata_Scan: Waiting for Buses to finish Initialising\n"));
284 ObtainSemaphore(&ssem);
287 * and leave.
289 ReleaseSemaphore(&ssem);
290 D(bug("[ATA--] ata_Scan: Finished\n"));
292 /* Try to setup daemon task looking for diskchanges */
293 NewCreateTask(TASKTAG_PC , DaemonCode,
294 TASKTAG_NAME , "ATA.daemon",
295 TASKTAG_STACKSIZE, STACK_SIZE,
296 TASKTAG_PRI , TASK_PRI - 1, /* The daemon should have a little bit lower Pri as handler tasks */
297 TASKTAG_ARG1 , base,
298 TAG_DONE);
300 return TRUE;
304 Here shall we start. Make function static as it shouldn't be visible from
305 outside.
307 static int ata_init(LIBBASETYPEPTR LIBBASE)
309 struct BootLoaderBase *BootLoaderBase;
311 D(bug("[ATA--] ata_init: ata.device Initialization\n"));
314 * I've decided to use memory pools again. Alloc everything needed from
315 * a pool, so that we avoid memory fragmentation.
317 LIBBASE->ata_MemPool = CreatePool(MEMF_CLEAR | MEMF_PUBLIC | MEMF_SEM_PROTECTED , 8192, 4096);
318 if (LIBBASE->ata_MemPool == NULL)
319 return FALSE;
321 D(bug("[ATA--] ata_init: MemPool @ %p\n", LIBBASE->ata_MemPool));
323 /* Set default ata.device config options */
324 LIBBASE->ata_32bit = FALSE;
325 LIBBASE->ata_NoMulti = FALSE;
326 LIBBASE->ata_NoDMA = FALSE;
327 LIBBASE->ata_Poll = FALSE;
328 LIBBASE->ata_CmdLine = NULL;
331 * start initialization:
332 * obtain kernel parameters
334 BootLoaderBase = OpenResource("bootloader.resource");
335 D(bug("[ATA--] ata_init: BootloaderBase = %p\n", BootLoaderBase));
336 if (BootLoaderBase != NULL)
338 struct List *list;
339 struct Node *node;
341 list = (struct List *)GetBootInfo(BL_Args);
342 if (list)
344 ForeachNode(list, node)
346 if (strncmp(node->ln_Name, "ATA=", 4) == 0)
349 * Remember the entire command line.
350 * Bus drivers (for example PCI one) may want it.
352 LIBBASE->ata_CmdLine = &node->ln_Name[4];
354 if (strstr(LIBBASE->ata_CmdLine, "32bit"))
356 D(bug("[ATA ] ata_init: Using 32-bit IO transfers\n"));
357 LIBBASE->ata_32bit = TRUE;
359 if (strstr(LIBBASE->ata_CmdLine, "nomulti"))
361 D(bug("[ATA ] ata_init: Disabled multisector transfers\n"));
362 LIBBASE->ata_NoMulti = TRUE;
364 if (strstr(LIBBASE->ata_CmdLine, "nodma"))
366 D(bug("[ATA ] ata_init: Disabled DMA transfers\n"));
367 LIBBASE->ata_NoDMA = TRUE;
369 if (strstr(LIBBASE->ata_CmdLine, "poll"))
371 D(bug("[ATA ] ata_init: Using polling to detect end of busy state\n"));
372 LIBBASE->ata_Poll = TRUE;
379 /* Initialize BUS list */
380 NEWLIST(&LIBBASE->ata_Buses);
382 return TRUE;
385 static int open
387 LIBBASETYPEPTR LIBBASE,
388 struct IORequest *iorq,
389 ULONG unitnum,
390 ULONG flags
394 * device location
396 ULONG bus, dev;
399 * Assume it failed
401 iorq->io_Error = IOERR_OPENFAIL;
404 * actual bus
406 struct ata_Bus *b = (struct ata_Bus*)LIBBASE->ata_Buses.mlh_Head;
409 * Extract bus and device numbers
411 bus = unitnum >> 1; // 0xff00 >> 8
412 dev = (unitnum & 0x1); // 0x00ff
415 * locate bus
417 while (bus--)
419 b = (struct ata_Bus*)b->ab_Node.mln_Succ;
420 if (b == NULL)
421 return FALSE;
424 if (b->ab_Node.mln_Succ == NULL)
425 return FALSE;
428 * locate unit
430 if (b->ab_Units[dev] == NULL)
431 return FALSE;
434 * set up iorequest
436 iorq->io_Device = &LIBBASE->ata_Device;
437 iorq->io_Unit = &b->ab_Units[dev]->au_Unit;
438 iorq->io_Error = 0;
440 b->ab_Units[dev]->au_Unit.unit_OpenCnt++;
442 return TRUE;
445 /* Close given device */
446 static int close
448 LIBBASETYPEPTR LIBBASE,
449 struct IORequest *iorq
452 struct ata_Unit *unit = (struct ata_Unit *)iorq->io_Unit;
454 /* First of all make the important fields of struct IORequest invalid! */
455 iorq->io_Unit = (struct Unit *)~0;
457 /* Decrease use counters of unit */
458 unit->au_Unit.unit_OpenCnt--;
460 return TRUE;
463 ADD2INITLIB(ata_init, 0)
464 ADD2INITLIB(ata_Scan, 127)
465 ADD2OPENDEV(open, 0)
466 ADD2CLOSEDEV(close, 0)
467 /* vim: set ts=8 sts=4 et : */