494a1b7734ef44fef6e887f62b6eb30d0a494284
[grub.git] / grub-core / disk / ahci.c
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 2007, 2008, 2009, 2010  Free Software Foundation, Inc.
4  *
5  *  GRUB is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  GRUB is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <grub/dl.h>
20 #include <grub/disk.h>
21 #include <grub/mm.h>
22 #include <grub/time.h>
23 #include <grub/pci.h>
24 #include <grub/ata.h>
25 #include <grub/scsi.h>
26 #include <grub/misc.h>
27 #include <grub/list.h>
28 #include <grub/loader.h>
29
30 GRUB_MOD_LICENSE ("GPLv3+");
31
32 struct grub_ahci_cmd_head
33 {
34   grub_uint32_t config;
35   grub_uint32_t transferred;
36   grub_uint64_t command_table_base;
37   grub_uint32_t unused[4];
38 };
39
40 struct grub_ahci_prdt_entry
41 {
42   grub_uint64_t data_base;
43   grub_uint32_t unused;
44   grub_uint32_t size;
45 };
46
47 struct grub_ahci_cmd_table
48 {
49   grub_uint8_t cfis[0x40];
50   grub_uint8_t command[0x10];
51   grub_uint8_t reserved[0x30];
52   struct grub_ahci_prdt_entry prdt[1];
53 };
54
55 struct grub_ahci_hba_port
56 {
57   grub_uint64_t command_list_base;
58   grub_uint64_t fis_base;
59   grub_uint32_t intstatus;
60   grub_uint32_t inten;
61   grub_uint32_t command;
62   grub_uint32_t unused1;
63   grub_uint32_t task_file_data;
64   grub_uint32_t sig;
65   grub_uint32_t status;
66   grub_uint32_t unused2;
67   grub_uint32_t sata_error;
68   grub_uint32_t sata_active;
69   grub_uint32_t command_issue;
70   grub_uint32_t unused3;
71   grub_uint32_t fbs;
72   grub_uint32_t unused4[15];
73 };
74
75 enum grub_ahci_hba_port_command
76   {
77     GRUB_AHCI_HBA_PORT_CMD_ST  = 0x01,
78     GRUB_AHCI_HBA_PORT_CMD_SPIN_UP = 0x02,
79     GRUB_AHCI_HBA_PORT_CMD_POWER_ON = 0x04,
80     GRUB_AHCI_HBA_PORT_CMD_FRE = 0x10,
81     GRUB_AHCI_HBA_PORT_CMD_CR = 0x8000,
82     GRUB_AHCI_HBA_PORT_CMD_FR = 0x4000,
83   };
84
85 struct grub_ahci_hba
86 {
87   grub_uint32_t cap;
88   grub_uint32_t global_control;
89   grub_uint32_t intr_status;
90   grub_uint32_t ports_implemented;
91   grub_uint32_t unused1[6];
92   grub_uint32_t bios_handoff;
93   grub_uint32_t unused2[53];
94   struct grub_ahci_hba_port ports[32];
95 };
96
97 struct grub_ahci_received_fis
98 {
99   char raw[4096];
100 };
101
102 enum
103   {
104     GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f
105   };
106
107 enum
108   {
109     GRUB_AHCI_HBA_GLOBAL_CONTROL_RESET = 0x00000001,
110     GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002,
111     GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000,
112   };
113
114 enum
115   {
116     GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED = 1,
117     GRUB_AHCI_BIOS_HANDOFF_OS_OWNED = 2,
118     GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED = 8,
119     GRUB_AHCI_BIOS_HANDOFF_RWC = 8
120   };
121
122
123 struct grub_ahci_device
124 {
125   struct grub_ahci_device *next;
126   struct grub_ahci_device **prev;
127   volatile struct grub_ahci_hba *hba;
128   int port;
129   int num;
130   struct grub_pci_dma_chunk *command_list_chunk;
131   volatile struct grub_ahci_cmd_head *command_list;
132   struct grub_pci_dma_chunk *command_table_chunk;
133   volatile struct grub_ahci_cmd_table *command_table;
134   struct grub_pci_dma_chunk *rfis;
135   int present;
136   int atapi;
137 };
138
139 static grub_err_t 
140 grub_ahci_readwrite_real (struct grub_ahci_device *dev,
141                           struct grub_disk_ata_pass_through_parms *parms,
142                           int spinup, int reset);
143
144
145 enum
146   {
147     GRUB_AHCI_CONFIG_READ = 0,
148     GRUB_AHCI_CONFIG_CFIS_LENGTH_MASK = 0x1f,
149     GRUB_AHCI_CONFIG_ATAPI = 0x20,
150     GRUB_AHCI_CONFIG_WRITE = 0x40,
151     GRUB_AHCI_CONFIG_PREFETCH = 0x80,
152     GRUB_AHCI_CONFIG_RESET = 0x100,
153     GRUB_AHCI_CONFIG_BIST = 0x200,
154     GRUB_AHCI_CONFIG_CLEAR_R_OK = 0x400,
155     GRUB_AHCI_CONFIG_PMP_MASK = 0xf000,
156     GRUB_AHCI_CONFIG_PRDT_LENGTH_MASK = 0xffff0000,
157   };
158 #define GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT 0
159 #define GRUB_AHCI_CONFIG_PMP_SHIFT 12
160 #define GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT 16
161 #define GRUB_AHCI_INTERRUPT_ON_COMPLETE 0x80000000
162
163 #define GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH 0x200000
164
165 static struct grub_ahci_device *grub_ahci_devices;
166 static int numdevs;
167
168 static int
169 grub_ahci_pciinit (grub_pci_device_t dev,
170                    grub_pci_id_t pciid __attribute__ ((unused)),
171                    void *data __attribute__ ((unused)))
172 {
173   grub_pci_address_t addr;
174   grub_uint32_t class;
175   grub_uint32_t bar;
176   unsigned i, nports;
177   volatile struct grub_ahci_hba *hba;
178
179   /* Read class.  */
180   addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
181   class = grub_pci_read (addr);
182
183   /* Check if this class ID matches that of a PCI IDE Controller.  */
184   if (class >> 8 != 0x010601)
185     return 0;
186
187   addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG5);
188
189   bar = grub_pci_read (addr);
190
191   if ((bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK
192               | GRUB_PCI_ADDR_MEM_PREFETCH))
193       != (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32))
194     return 0;
195
196   addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
197   grub_pci_write_word (addr, grub_pci_read_word (addr)
198                     | GRUB_PCI_COMMAND_MEM_ENABLED | GRUB_PCI_COMMAND_BUS_MASTER);
199
200   hba = grub_pci_device_map_range (dev, bar & GRUB_PCI_ADDR_MEM_MASK,
201                                    sizeof (*hba));
202   grub_dprintf ("ahci", "dev: %x:%x.%x\n", dev.bus, dev.device, dev.function);
203
204   grub_dprintf ("ahci", "tfd[0]: %x\n",
205                 hba->ports[0].task_file_data);
206   grub_dprintf ("ahci", "cmd[0]: %x\n",
207                 hba->ports[0].command);
208   grub_dprintf ("ahci", "st[0]: %x\n",
209                 hba->ports[0].status);
210   grub_dprintf ("ahci", "err[0]: %x\n",
211                 hba->ports[0].sata_error);
212
213   grub_dprintf ("ahci", "tfd[1]: %x\n",
214                 hba->ports[1].task_file_data);
215   grub_dprintf ("ahci", "cmd[1]: %x\n",
216                 hba->ports[1].command);
217   grub_dprintf ("ahci", "st[1]: %x\n",
218                 hba->ports[1].status);
219   grub_dprintf ("ahci", "err[1]: %x\n",
220                 hba->ports[1].sata_error);
221
222   hba->ports[1].sata_error = hba->ports[1].sata_error;
223
224   grub_dprintf ("ahci", "err[1]: %x\n",
225                 hba->ports[1].sata_error);
226
227   grub_dprintf ("ahci", "BH:%x\n", hba->bios_handoff);
228
229   if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED))
230     {
231       grub_uint64_t endtime;
232
233       grub_dprintf ("ahci", "Requesting AHCI ownership\n");
234       hba->bios_handoff = (hba->bios_handoff & ~GRUB_AHCI_BIOS_HANDOFF_RWC)
235         | GRUB_AHCI_BIOS_HANDOFF_OS_OWNED;
236       grub_dprintf ("ahci", "Waiting for BIOS to give up ownership\n");
237       endtime = grub_get_time_ms () + 1000;
238       while ((hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED)
239              && grub_get_time_ms () < endtime);
240       if (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED)
241         {
242           grub_dprintf ("ahci", "Forcibly taking ownership\n");
243           hba->bios_handoff = GRUB_AHCI_BIOS_HANDOFF_OS_OWNED;
244           hba->bios_handoff |= GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED;
245         }
246       else
247         grub_dprintf ("ahci", "AHCI ownership obtained\n");
248     }
249   else
250     grub_dprintf ("ahci", "AHCI is already in OS mode\n");
251
252   grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);
253
254   grub_dprintf ("ahci", "err[1]: %x\n",
255                 hba->ports[1].sata_error);
256
257   if (!(hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN))
258     grub_dprintf ("ahci", "AHCI is in compat mode. Switching\n");
259   else
260     grub_dprintf ("ahci", "AHCI is in AHCI mode.\n");
261
262   grub_dprintf ("ahci", "err[1]: %x\n",
263                 hba->ports[1].sata_error);
264
265   grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);
266
267   /*  {
268       grub_uint64_t endtime;
269       hba->global_control |= 1;
270       endtime = grub_get_time_ms () + 1000;
271       while (hba->global_control & 1)
272         if (grub_get_time_ms () > endtime)
273           {
274             grub_dprintf ("ahci", "couldn't reset AHCI\n");
275             return 0;
276           }
277           }*/
278
279   grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);
280
281   grub_dprintf ("ahci", "err[1]: %x\n",
282                 hba->ports[1].sata_error);
283
284   for (i = 0; i < 5; i++)
285     {
286       hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
287       grub_millisleep (1);
288       if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)
289         break;
290     }
291   if (i == 5)
292     {
293       grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n");
294       return 0;
295     }
296
297   grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);
298
299   grub_dprintf ("ahci", "err[1]: %x\n",
300                 hba->ports[1].sata_error);
301
302   grub_dprintf ("ahci", "err[1]: %x\n",
303                 hba->ports[1].sata_error);
304
305   grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);
306
307   for (i = 0; i < 5; i++)
308     {
309       hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
310       grub_millisleep (1);
311       if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)
312         break;
313     }
314   if (i == 5)
315     {
316       grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n");
317       return 0;
318     }
319
320   grub_dprintf ("ahci", "err[1]: %x\n",
321                 hba->ports[1].sata_error);
322
323   grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);
324
325   nports = (GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1;
326
327   grub_dprintf ("ahci", "%d AHCI ports, PI = 0x%x\n", nports,
328                 hba->ports_implemented);
329
330   struct grub_ahci_device *adevs[GRUB_AHCI_HBA_CAP_NPORTS_MASK + 1];
331   struct grub_ahci_device *failed_adevs[GRUB_AHCI_HBA_CAP_NPORTS_MASK + 1];
332   grub_uint32_t fr_running = 0;
333
334   for (i = 0; i < nports; i++)
335     failed_adevs[i] = 0;
336   for (i = 0; i < nports; i++)
337     {
338       if (!(hba->ports_implemented & (1 << i)))
339         {
340           adevs[i] = 0;
341           continue;
342         }
343
344       adevs[i] = grub_zalloc (sizeof (*adevs[i]));
345       if (!adevs[i])
346         return 1;
347
348       adevs[i]->hba = hba;
349       adevs[i]->port = i;
350       adevs[i]->present = 1;
351       adevs[i]->num = numdevs++;
352     }
353
354   for (i = 0; i < nports; i++)
355     if (adevs[i])
356       {
357         adevs[i]->hba->ports[adevs[i]->port].sata_error = adevs[i]->hba->ports[adevs[i]->port].sata_error;
358         grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
359                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
360
361         adevs[i]->command_list_chunk = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head) * 32);
362         if (!adevs[i]->command_list_chunk)
363           {
364             adevs[i] = 0;
365             continue;
366           }
367
368         adevs[i]->command_table_chunk = grub_memalign_dma32 (1024,
369                                                             sizeof (struct grub_ahci_cmd_table));
370         if (!adevs[i]->command_table_chunk)
371           {
372             grub_dma_free (adevs[i]->command_list_chunk);
373             adevs[i] = 0;
374             continue;
375           }
376
377         adevs[i]->command_list = grub_dma_get_virt (adevs[i]->command_list_chunk);
378         adevs[i]->command_table = grub_dma_get_virt (adevs[i]->command_table_chunk);
379
380         grub_memset ((void *) adevs[i]->command_list, 0,
381                      sizeof (struct grub_ahci_cmd_table));
382         grub_memset ((void *) adevs[i]->command_table, 0,
383                      sizeof (struct grub_ahci_cmd_head) * 32);
384
385         adevs[i]->command_list->command_table_base
386           = grub_dma_get_phys (adevs[i]->command_table_chunk);
387
388         grub_dprintf ("ahci", "found device ahci%d (port %d), command_table = %p, command_list = %p\n",
389                       adevs[i]->num, adevs[i]->port, grub_dma_get_virt (adevs[i]->command_table_chunk),
390                       grub_dma_get_virt (adevs[i]->command_list_chunk));
391
392         adevs[i]->hba->ports[adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
393       }
394
395   grub_uint64_t endtime;
396   endtime = grub_get_time_ms () + 1000;
397
398   while (grub_get_time_ms () < endtime)
399     {
400       for (i = 0; i < nports; i++)
401         if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
402           break;
403       if (i == nports)
404         break;
405     }
406
407   for (i = 0; i < nports; i++)
408     if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
409       {
410         grub_dprintf ("ahci", "couldn't stop FR on port %d\n", i);
411         failed_adevs[i] = adevs[i];
412         adevs[i] = 0;
413       }
414
415   for (i = 0; i < nports; i++)
416     if (adevs[i])
417       adevs[i]->hba->ports[adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
418   endtime = grub_get_time_ms () + 1000;
419
420   while (grub_get_time_ms () < endtime)
421     {
422       for (i = 0; i < nports; i++)
423         if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
424           break;
425       if (i == nports)
426         break;
427     }
428
429   for (i = 0; i < nports; i++)
430     if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
431       {
432         grub_dprintf ("ahci", "couldn't stop CR on port %d\n", i);
433         failed_adevs[i] = adevs[i];
434         adevs[i] = 0;
435       }
436   for (i = 0; i < nports; i++)
437     if (adevs[i])
438       {
439         adevs[i]->hba->ports[adevs[i]->port].inten = 0;
440         adevs[i]->hba->ports[adevs[i]->port].intstatus = ~0;
441         //  adevs[i]->hba->ports[adevs[i]->port].fbs = 0;
442
443         grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
444                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
445
446         adevs[i]->rfis = grub_memalign_dma32 (4096, 
447                                              sizeof (struct grub_ahci_received_fis));
448         grub_memset ((char *) grub_dma_get_virt (adevs[i]->rfis), 0,
449                      sizeof (struct grub_ahci_received_fis));
450         grub_memset ((char *) grub_dma_get_virt (adevs[i]->command_list_chunk), 0,
451                      sizeof (struct grub_ahci_cmd_head));
452         grub_memset ((char *) grub_dma_get_virt (adevs[i]->command_table_chunk), 0,
453                      sizeof (struct grub_ahci_cmd_table));
454         adevs[i]->hba->ports[adevs[i]->port].fis_base = grub_dma_get_phys (adevs[i]->rfis);
455         adevs[i]->hba->ports[adevs[i]->port].command_list_base
456           = grub_dma_get_phys (adevs[i]->command_list_chunk);
457         adevs[i]->hba->ports[adevs[i]->port].command_issue = 0;
458         adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_FRE;
459       }
460
461   endtime = grub_get_time_ms () + 1000;
462
463   while (grub_get_time_ms () < endtime)
464     {
465       for (i = 0; i < nports; i++)
466         if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
467           break;
468       if (i == nports)
469         break;
470     }
471
472   for (i = 0; i < nports; i++)
473     if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
474       {
475         grub_dprintf ("ahci", "couldn't start FR on port %d\n", i);
476         failed_adevs[i] = adevs[i];
477         adevs[i] = 0;
478       }
479
480   for (i = 0; i < nports; i++)
481     if (adevs[i])
482       {
483         grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
484                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
485         fr_running |= (1 << i);
486
487         adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_SPIN_UP;
488         adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_POWER_ON;
489         adevs[i]->hba->ports[adevs[i]->port].command |= 1 << 28;
490
491         grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
492                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
493       }
494
495   /* 10ms should actually be enough.  */
496   endtime = grub_get_time_ms () + 100;
497
498   while (grub_get_time_ms () < endtime)
499     {
500       for (i = 0; i < nports; i++)
501         if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].status & 7) != 3)
502           break;
503       if (i == nports)
504         break;
505     }
506
507   for (i = 0; i < nports; i++)
508     if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].status & 7) != 3)
509       {
510         grub_dprintf ("ahci", "couldn't detect device on port %d\n", i);
511         failed_adevs[i] = adevs[i];
512         adevs[i] = 0;
513       }
514
515   for (i = 0; i < nports; i++)
516     if (adevs[i])
517       {
518         grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port,
519                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
520
521         adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_POWER_ON;
522         adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_SPIN_UP;
523
524         grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port,
525                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
526
527         adevs[i]->hba->ports[adevs[i]->port].sata_error = ~0;
528         grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port,
529                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
530
531         grub_dprintf ("ahci", "port %d, offset: %x, tfd:%x, CMD: %x\n", adevs[i]->port,
532                       (int) ((char *) &adevs[i]->hba->ports[adevs[i]->port].task_file_data - 
533                              (char *) adevs[i]->hba),
534                       adevs[i]->hba->ports[adevs[i]->port].task_file_data,
535                       adevs[i]->hba->ports[adevs[i]->port].command);
536
537         grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port,
538                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
539       }
540
541
542   for (i = 0; i < nports; i++)
543     if (adevs[i])
544       {
545         grub_dprintf ("ahci", "port %d, offset: %x, tfd:%x, CMD: %x\n", adevs[i]->port,
546                       (int) ((char *) &adevs[i]->hba->ports[adevs[i]->port].task_file_data - 
547                              (char *) adevs[i]->hba),
548                       adevs[i]->hba->ports[adevs[i]->port].task_file_data,
549                       adevs[i]->hba->ports[adevs[i]->port].command);
550
551         grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
552                       adevs[i]->hba->ports[adevs[i]->port].sata_error);
553
554         adevs[i]->hba->ports[adevs[i]->port].command
555           = (adevs[i]->hba->ports[adevs[i]->port].command & 0x0fffffff) | (1 << 28)
556           | GRUB_AHCI_HBA_PORT_CMD_SPIN_UP
557           | GRUB_AHCI_HBA_PORT_CMD_POWER_ON;
558
559         /*  struct grub_disk_ata_pass_through_parms parms2;
560             grub_memset (&parms2, 0, sizeof (parms2));
561             parms2.taskfile.cmd = 8;
562             grub_ahci_readwrite_real (dev, &parms2, 1, 1);*/
563       }
564
565   endtime = grub_get_time_ms () + 10000;
566
567   while (grub_get_time_ms () < endtime)
568     {
569       for (i = 0; i < nports; i++)
570         if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].task_file_data & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ)))
571           break;
572       if (i == nports)
573         break;
574     }
575
576   for (i = 0; i < nports; i++)
577     if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].task_file_data & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ)))
578       {
579         grub_dprintf ("ahci", "port %d is busy\n", i);
580         failed_adevs[i] = adevs[i];
581         adevs[i] = 0;
582       }
583
584   for (i = 0; i < nports; i++)
585     if (adevs[i])
586       adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;
587
588   endtime = grub_get_time_ms () + 1000;
589
590   while (grub_get_time_ms () < endtime)
591     {
592       for (i = 0; i < nports; i++)
593         if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
594           break;
595       if (i == nports)
596         break;
597     }
598
599   for (i = 0; i < nports; i++)
600     if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
601       {
602         grub_dprintf ("ahci", "couldn't start CR on port %d\n", i);
603         failed_adevs[i] = adevs[i];
604         adevs[i] = 0;
605       }
606
607   grub_dprintf ("ahci", "cleaning up failed devs\n");
608
609   for (i = 0; i < nports; i++)
610     if (failed_adevs[i] && (fr_running & (1 << i)))
611       failed_adevs[i]->hba->ports[failed_adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
612
613   endtime = grub_get_time_ms () + 1000;
614   while (grub_get_time_ms () < endtime)
615     {
616       for (i = 0; i < nports; i++)
617         if (failed_adevs[i] && (fr_running & (1 << i)) && (failed_adevs[i]->hba->ports[failed_adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
618           break;
619       if (i == nports)
620         break;
621     }
622   for (i = 0; i < nports; i++)
623     if (failed_adevs[i])
624       {
625         grub_dma_free (failed_adevs[i]->command_list_chunk);
626         grub_dma_free (failed_adevs[i]->command_table_chunk);
627         grub_dma_free (failed_adevs[i]->rfis);
628       }
629
630   for (i = 0; i < nports; i++)
631     if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].sig >> 16) == 0xeb14)
632       adevs[i]->atapi = 1;
633
634   addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
635   grub_pci_write_word (addr, grub_pci_read_word (addr)
636                     | GRUB_PCI_COMMAND_BUS_MASTER);
637
638   for (i = 0; i < nports; i++)
639     if (adevs[i])
640       {
641         grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices),
642                         GRUB_AS_LIST (adevs[i]));
643       }
644
645   return 0;
646 }
647
648 static grub_err_t
649 grub_ahci_initialize (void)
650 {
651   grub_pci_iterate (grub_ahci_pciinit, NULL);
652   return grub_errno;
653 }
654
655 static grub_err_t
656 grub_ahci_fini_hw (int noreturn __attribute__ ((unused)))
657 {
658   struct grub_ahci_device *dev;
659
660   for (dev = grub_ahci_devices; dev; dev = dev->next)
661     {
662       grub_uint64_t endtime;
663
664       dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
665       endtime = grub_get_time_ms () + 1000;
666       while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
667         if (grub_get_time_ms () > endtime)
668           {
669             grub_dprintf ("ahci", "couldn't stop FR\n");
670             break;
671           }
672
673       dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
674       endtime = grub_get_time_ms () + 1000;
675       while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
676         if (grub_get_time_ms () > endtime)
677           {
678             grub_dprintf ("ahci", "couldn't stop CR\n");
679             break;
680           }
681       grub_dma_free (dev->command_list_chunk);
682       grub_dma_free (dev->command_table_chunk);
683       grub_dma_free (dev->rfis);
684       dev->command_list_chunk = NULL;
685       dev->command_table_chunk = NULL;
686       dev->rfis = NULL;
687     }
688   return GRUB_ERR_NONE;
689 }
690
691 static int 
692 reinit_port (struct grub_ahci_device *dev)
693 {
694   struct grub_pci_dma_chunk *command_list;
695   struct grub_pci_dma_chunk *command_table;
696   grub_uint64_t endtime;
697
698   command_list = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head));
699   if (!command_list)
700     return 1;
701
702   command_table = grub_memalign_dma32 (1024,
703                                        sizeof (struct grub_ahci_cmd_table));
704   if (!command_table)
705     {
706       grub_dma_free (command_list);
707       return 1;
708     }
709
710   grub_dprintf ("ahci", "found device ahci%d (port %d)\n", dev->num, dev->port);
711
712   dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
713   endtime = grub_get_time_ms () + 1000;
714   while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
715     if (grub_get_time_ms () > endtime)
716       {
717         grub_dprintf ("ahci", "couldn't stop FR\n");
718         goto out;
719       }
720
721   dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
722   endtime = grub_get_time_ms () + 1000;
723   while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
724     if (grub_get_time_ms () > endtime)
725       {
726         grub_dprintf ("ahci", "couldn't stop CR\n");
727         goto out;
728       }
729
730   dev->hba->ports[dev->port].fbs = 2;
731
732   dev->rfis = grub_memalign_dma32 (4096, 
733                                    sizeof (struct grub_ahci_received_fis));
734   grub_memset ((char *) grub_dma_get_virt (dev->rfis), 0,
735                sizeof (struct grub_ahci_received_fis));
736   dev->hba->ports[dev->port].fis_base = grub_dma_get_phys (dev->rfis);
737   dev->hba->ports[dev->port].command_list_base
738     = grub_dma_get_phys (command_list);
739   dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_FRE;
740   while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
741     if (grub_get_time_ms () > endtime)
742       {
743         grub_dprintf ("ahci", "couldn't start FR\n");
744         dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
745         goto out;
746       }
747   dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;
748   while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
749     if (grub_get_time_ms () > endtime)
750       {
751         grub_dprintf ("ahci", "couldn't start CR\n");
752         dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_CR;
753         goto out_stop_fr;
754       }
755
756   dev->hba->ports[dev->port].command
757     = (dev->hba->ports[dev->port].command & 0x0fffffff) | (1 << 28) | 2 | 4;
758
759   dev->command_list_chunk = command_list;
760   dev->command_list = grub_dma_get_virt (command_list);
761   dev->command_table_chunk = command_table;
762   dev->command_table = grub_dma_get_virt (command_table);
763   dev->command_list->command_table_base
764     = grub_dma_get_phys (command_table);
765
766   return 0;
767  out_stop_fr:
768   dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
769   endtime = grub_get_time_ms () + 1000;
770   while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
771     if (grub_get_time_ms () > endtime)
772       {
773         grub_dprintf ("ahci", "couldn't stop FR\n");
774         break;
775       }
776  out:
777   grub_dma_free (command_list);
778   grub_dma_free (command_table);
779   grub_dma_free (dev->rfis);
780   return 1;
781 }
782
783 static grub_err_t
784 grub_ahci_restore_hw (void)
785 {
786   struct grub_ahci_device **pdev;
787
788   for (pdev = &grub_ahci_devices; *pdev; pdev = &((*pdev)->next))
789     if (reinit_port (*pdev))
790       {
791         struct grub_ahci_device *odev;
792         odev = *pdev;
793         *pdev = (*pdev)->next;
794         grub_free (odev);
795       }
796   return GRUB_ERR_NONE;
797 }
798
799
800 \f
801
802 static int
803 grub_ahci_iterate (grub_ata_dev_iterate_hook_t hook, void *hook_data,
804                    grub_disk_pull_t pull)
805 {
806   struct grub_ahci_device *dev;
807
808   if (pull != GRUB_DISK_PULL_NONE)
809     return 0;
810
811   FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
812     if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num, hook_data))
813       return 1;
814
815   return 0;
816 }
817
818 #if 0
819 static int
820 find_free_cmd_slot (struct grub_ahci_device *dev)
821 {
822   int i;
823   for (i = 0; i < 32; i++)
824     {
825       if (dev->hda->ports[dev->port].command_issue & (1 << i))
826         continue;
827       if (dev->hda->ports[dev->port].sata_active & (1 << i))
828         continue;
829       return i;
830     }
831   return -1;
832 }
833 #endif
834
835 enum
836   {
837     GRUB_AHCI_FIS_REG_H2D = 0x27
838   };
839
840 static const int register_map[11] = { 3 /* Features */,
841                                       12 /* Sectors */,
842                                       4 /* LBA low */,
843                                       5 /* LBA mid */,
844                                       6 /* LBA high */,
845                                       7 /* Device */,
846                                       2 /* CMD register */,
847                                       13 /* Sectors 48  */,
848                                       8 /* LBA48 low */,
849                                       9 /* LBA48 mid */,
850                                       10 /* LBA48 high */ }; 
851
852 static grub_err_t
853 grub_ahci_reset_port (struct grub_ahci_device *dev, int force)
854 {
855   grub_uint64_t endtime;
856   
857   dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error;
858
859   if (force || (dev->hba->ports[dev->port].command_issue & 1)
860       || (dev->hba->ports[dev->port].task_file_data & 0x80))
861     {
862       struct grub_disk_ata_pass_through_parms parms2;
863       dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
864       dev->hba->ports[dev->port].command_issue = 0;
865       dev->command_list[0].config = 0;
866       dev->command_table[0].prdt[0].unused = 0;
867       dev->command_table[0].prdt[0].size = 0;
868       dev->command_table[0].prdt[0].data_base = 0;
869
870       endtime = grub_get_time_ms () + 1000;
871       while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
872         if (grub_get_time_ms () > endtime)
873           {
874             grub_dprintf ("ahci", "couldn't stop CR");
875             return grub_error (GRUB_ERR_IO, "couldn't stop CR");
876           }
877       dev->hba->ports[dev->port].command |= 8;
878       while (dev->hba->ports[dev->port].command & 8)
879         if (grub_get_time_ms () > endtime)
880           {
881             grub_dprintf ("ahci", "couldn't set CLO\n");
882             dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
883             return grub_error (GRUB_ERR_IO, "couldn't set CLO");
884           }
885
886       dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;
887       while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
888         if (grub_get_time_ms () > endtime)
889           {
890             grub_dprintf ("ahci", "couldn't stop CR");
891             dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
892             return grub_error (GRUB_ERR_IO, "couldn't stop CR");
893           }
894       dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error;
895       grub_memset (&parms2, 0, sizeof (parms2));
896       parms2.taskfile.cmd = 8;
897       return grub_ahci_readwrite_real (dev, &parms2, 1, 1);
898     }
899   return GRUB_ERR_NONE;
900 }
901
902 static grub_err_t 
903 grub_ahci_readwrite_real (struct grub_ahci_device *dev,
904                           struct grub_disk_ata_pass_through_parms *parms,
905                           int spinup, int reset)
906 {
907   struct grub_pci_dma_chunk *bufc;
908   grub_uint64_t endtime;
909   unsigned i;
910   grub_err_t err = GRUB_ERR_NONE;
911
912   grub_dprintf ("ahci", "AHCI tfd = %x\n",
913                 dev->hba->ports[dev->port].task_file_data);
914
915   if (!reset)
916     grub_ahci_reset_port (dev, 0);
917  
918   grub_dprintf ("ahci", "AHCI tfd = %x\n",
919                 dev->hba->ports[dev->port].task_file_data);
920   dev->hba->ports[dev->port].task_file_data = 0;
921   dev->hba->ports[dev->port].command_issue = 0;
922   grub_dprintf ("ahci", "AHCI tfd = %x\n",
923                 dev->hba->ports[dev->port].task_file_data);
924
925   dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error;
926
927   grub_dprintf("ahci", "grub_ahci_read (size=%llu, cmdsize = %llu)\n",
928                (unsigned long long) parms->size,
929                (unsigned long long) parms->cmdsize);
930
931   if (parms->cmdsize != 0 && parms->cmdsize != 12 && parms->cmdsize != 16)
932     return grub_error (GRUB_ERR_BUG, "incorrect ATAPI command size");
933
934   if (parms->size > GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH)
935     return grub_error (GRUB_ERR_BUG, "too big data buffer");
936
937   if (parms->size)
938     bufc = grub_memalign_dma32 (1024, parms->size + (parms->size & 1));
939   else
940     bufc = grub_memalign_dma32 (1024, 512);
941
942   grub_dprintf ("ahci", "AHCI tfd = %x, CL=%p\n",
943                 dev->hba->ports[dev->port].task_file_data,
944                 dev->command_list);
945   /* FIXME: support port multipliers.  */
946   dev->command_list[0].config
947     = (5 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT)
948     //    | GRUB_AHCI_CONFIG_CLEAR_R_OK
949     | (0 << GRUB_AHCI_CONFIG_PMP_SHIFT)
950     | ((parms->size ? 1 : 0) << GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT)
951     | (parms->cmdsize ? GRUB_AHCI_CONFIG_ATAPI : 0)
952     | (parms->write ? GRUB_AHCI_CONFIG_WRITE : GRUB_AHCI_CONFIG_READ)
953     | (parms->taskfile.cmd == 8 ? (1 << 8) : 0);
954   grub_dprintf ("ahci", "AHCI tfd = %x\n",
955                 dev->hba->ports[dev->port].task_file_data);
956
957   dev->command_list[0].transferred = 0;
958   dev->command_list[0].command_table_base
959     = grub_dma_get_phys (dev->command_table_chunk);
960
961   grub_memset ((char *) dev->command_list[0].unused, 0,
962                sizeof (dev->command_list[0].unused));
963
964   grub_memset ((char *) &dev->command_table[0], 0,
965                sizeof (dev->command_table[0]));
966   grub_dprintf ("ahci", "AHCI tfd = %x\n",
967                 dev->hba->ports[dev->port].task_file_data);
968
969   if (parms->cmdsize)
970     grub_memcpy ((char *) dev->command_table[0].command, parms->cmd,
971                  parms->cmdsize);
972
973   grub_dprintf ("ahci", "AHCI tfd = %x\n",
974                 dev->hba->ports[dev->port].task_file_data);
975
976   dev->command_table[0].cfis[0] = GRUB_AHCI_FIS_REG_H2D;
977   dev->command_table[0].cfis[1] = 0x80;
978   for (i = 0; i < sizeof (parms->taskfile.raw); i++)
979     dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i]; 
980
981   grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
982                 dev->command_table[0].cfis[0], dev->command_table[0].cfis[1],
983                 dev->command_table[0].cfis[2], dev->command_table[0].cfis[3],
984                 dev->command_table[0].cfis[4], dev->command_table[0].cfis[5],
985                 dev->command_table[0].cfis[6], dev->command_table[0].cfis[7]);
986   grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
987                 dev->command_table[0].cfis[8], dev->command_table[0].cfis[9],
988                 dev->command_table[0].cfis[10], dev->command_table[0].cfis[11],
989                 dev->command_table[0].cfis[12], dev->command_table[0].cfis[13],
990                 dev->command_table[0].cfis[14], dev->command_table[0].cfis[15]);
991
992   dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc);
993   dev->command_table[0].prdt[0].unused = 0;
994   dev->command_table[0].prdt[0].size = (parms->size - 1);
995
996   grub_dprintf ("ahci", "PRDT = %" PRIxGRUB_UINT64_T ", %x, %x (%"
997                 PRIuGRUB_SIZE ")\n",
998                 dev->command_table[0].prdt[0].data_base,
999                 dev->command_table[0].prdt[0].unused,
1000                 dev->command_table[0].prdt[0].size,
1001                 (grub_size_t) ((char *) &dev->command_table[0].prdt[0]
1002                                - (char *) &dev->command_table[0]));
1003
1004   if (parms->write)
1005     grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size);
1006
1007   grub_dprintf ("ahci", "AHCI command scheduled\n");
1008   grub_dprintf ("ahci", "AHCI tfd = %x\n",
1009                 dev->hba->ports[dev->port].task_file_data);
1010   grub_dprintf ("ahci", "AHCI inten = %x\n",
1011                 dev->hba->ports[dev->port].inten);
1012   grub_dprintf ("ahci", "AHCI intstatus = %x\n",
1013                 dev->hba->ports[dev->port].intstatus);
1014
1015   dev->hba->ports[dev->port].inten = 0xffffffff;//(1 << 2) | (1 << 5);
1016   dev->hba->ports[dev->port].intstatus = 0xffffffff;//(1 << 2) | (1 << 5);
1017   grub_dprintf ("ahci", "AHCI inten = %x\n",
1018                 dev->hba->ports[dev->port].inten);
1019   grub_dprintf ("ahci", "AHCI tfd = %x\n",
1020                 dev->hba->ports[dev->port].task_file_data);
1021   dev->hba->ports[dev->port].sata_active = 1;
1022   dev->hba->ports[dev->port].command_issue = 1;
1023   grub_dprintf ("ahci", "AHCI sig = %x\n", dev->hba->ports[dev->port].sig);
1024   grub_dprintf ("ahci", "AHCI tfd = %x\n",
1025                 dev->hba->ports[dev->port].task_file_data);
1026
1027   endtime = grub_get_time_ms () + (spinup ? 20000 : 20000);
1028   while ((dev->hba->ports[dev->port].command_issue & 1))
1029     if (grub_get_time_ms () > endtime)
1030       {
1031         grub_dprintf ("ahci", "AHCI status <%x %x %x %x>\n",
1032                       dev->hba->ports[dev->port].command_issue,
1033                       dev->hba->ports[dev->port].sata_active,
1034                       dev->hba->ports[dev->port].intstatus,
1035                       dev->hba->ports[dev->port].task_file_data);
1036         dev->hba->ports[dev->port].command_issue = 0;
1037         err = grub_error (GRUB_ERR_IO, "AHCI transfer timed out");
1038         if (!reset)
1039           grub_ahci_reset_port (dev, 1);
1040         break;
1041       }
1042
1043   grub_dprintf ("ahci", "AHCI command completed <%x %x %x %x %x, %x %x>\n",
1044                 dev->hba->ports[dev->port].command_issue,
1045                 dev->hba->ports[dev->port].intstatus,
1046                 dev->hba->ports[dev->port].task_file_data,
1047                 dev->command_list[0].transferred,
1048                 dev->hba->ports[dev->port].sata_error,
1049                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x00],
1050                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x18]);
1051   grub_dprintf ("ahci",
1052                 "last PIO FIS %08x %08x %08x %08x %08x %08x %08x %08x\n",
1053                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x08],
1054                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x09],
1055                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0a],
1056                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0b],
1057                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0c],
1058                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0d],
1059                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0e],
1060                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0f]);
1061   grub_dprintf ("ahci",
1062                 "last REG FIS %08x %08x %08x %08x %08x %08x %08x %08x\n",
1063                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x10],
1064                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x11],
1065                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x12],
1066                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x13],
1067                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x14],
1068                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x15],
1069                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x16],
1070                 ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x17]);
1071
1072   if (!parms->write)
1073     grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size);
1074   grub_dma_free (bufc);
1075
1076   return err;
1077 }
1078
1079 static grub_err_t 
1080 grub_ahci_readwrite (grub_ata_t disk,
1081                      struct grub_disk_ata_pass_through_parms *parms,
1082                      int spinup)
1083 {
1084   return grub_ahci_readwrite_real (disk->data, parms, spinup, 0);
1085 }
1086
1087 static grub_err_t
1088 grub_ahci_open (int id, int devnum, struct grub_ata *ata)
1089 {
1090   struct grub_ahci_device *dev;
1091
1092   if (id != GRUB_SCSI_SUBSYSTEM_AHCI)
1093     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an AHCI device");
1094
1095   FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
1096     if (dev->num == devnum)
1097       break;
1098
1099   if (! dev)
1100     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device");
1101
1102   grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num);
1103
1104   ata->data = dev;
1105   ata->dma = 1;
1106   ata->atapi = dev->atapi;
1107   ata->maxbuffer = GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH;
1108   ata->present = &dev->present;
1109
1110   return GRUB_ERR_NONE;
1111 }
1112
1113 static struct grub_ata_dev grub_ahci_dev =
1114   {
1115     .iterate = grub_ahci_iterate,
1116     .open = grub_ahci_open,
1117     .readwrite = grub_ahci_readwrite,
1118   };
1119
1120 \f
1121
1122 static struct grub_preboot *fini_hnd;
1123
1124 GRUB_MOD_INIT(ahci)
1125 {
1126   grub_stop_disk_firmware ();
1127
1128   /* AHCI initialization.  */
1129   grub_ahci_initialize ();
1130
1131   /* AHCI devices are handled by scsi.mod.  */
1132   grub_ata_dev_register (&grub_ahci_dev);
1133
1134   fini_hnd = grub_loader_register_preboot_hook (grub_ahci_fini_hw,
1135                                                 grub_ahci_restore_hw,
1136                                                 GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
1137 }
1138
1139 GRUB_MOD_FINI(ahci)
1140 {
1141   grub_ahci_fini_hw (0);
1142   grub_loader_unregister_preboot_hook (fini_hnd);
1143
1144   grub_ata_dev_unregister (&grub_ahci_dev);
1145 }