ahci.c 34.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2007, 2008, 2009, 2010  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/mm.h>
#include <grub/time.h>
#include <grub/pci.h>
24
#include <grub/ata.h>
25
#include <grub/scsi.h>
26 27
#include <grub/misc.h>
#include <grub/list.h>
28
#include <grub/loader.h>
29

30 31
GRUB_MOD_LICENSE ("GPLv3+");

32 33
struct grub_ahci_cmd_head
{
34
  grub_uint32_t config;
Colin Watson's avatar
Colin Watson committed
35
  grub_uint32_t transferred;
36 37 38 39 40 41 42 43 44 45 46 47 48 49
  grub_uint64_t command_table_base;
  grub_uint32_t unused[4];
};

struct grub_ahci_prdt_entry
{
  grub_uint64_t data_base;
  grub_uint32_t unused;
  grub_uint32_t size;
};

struct grub_ahci_cmd_table
{
  grub_uint8_t cfis[0x40];
50 51
  grub_uint8_t command[0x10];
  grub_uint8_t reserved[0x30];
52
  struct grub_ahci_prdt_entry prdt[1];
53 54 55 56 57
};

struct grub_ahci_hba_port
{
  grub_uint64_t command_list_base;
58 59 60 61
  grub_uint64_t fis_base;
  grub_uint32_t intstatus;
  grub_uint32_t inten;
  grub_uint32_t command;
62 63 64
  grub_uint32_t unused1;
  grub_uint32_t task_file_data;
  grub_uint32_t sig;
65
  grub_uint32_t status;
66 67
  grub_uint32_t unused2;
  grub_uint32_t sata_error;
68 69
  grub_uint32_t sata_active;
  grub_uint32_t command_issue;
70 71 72
  grub_uint32_t unused3;
  grub_uint32_t fbs;
  grub_uint32_t unused4[15];
73 74
};

75 76 77
enum grub_ahci_hba_port_command
  {
    GRUB_AHCI_HBA_PORT_CMD_ST  = 0x01,
78 79
    GRUB_AHCI_HBA_PORT_CMD_SPIN_UP = 0x02,
    GRUB_AHCI_HBA_PORT_CMD_POWER_ON = 0x04,
80 81 82 83 84
    GRUB_AHCI_HBA_PORT_CMD_FRE = 0x10,
    GRUB_AHCI_HBA_PORT_CMD_CR = 0x8000,
    GRUB_AHCI_HBA_PORT_CMD_FR = 0x4000,
  };

85 86 87 88
struct grub_ahci_hba
{
  grub_uint32_t cap;
  grub_uint32_t global_control;
89
  grub_uint32_t intr_status;
90
  grub_uint32_t ports_implemented;
91
  grub_uint32_t unused1[6];
92 93 94 95 96
  grub_uint32_t bios_handoff;
  grub_uint32_t unused2[53];
  struct grub_ahci_hba_port ports[32];
};

97 98 99 100 101
struct grub_ahci_received_fis
{
  char raw[4096];
};

102 103 104 105 106 107 108
enum
  {
    GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f
  };

enum
  {
109
    GRUB_AHCI_HBA_GLOBAL_CONTROL_RESET = 0x00000001,
110 111 112 113 114 115 116 117 118 119 120 121 122
    GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002,
    GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000,
  };

enum
  {
    GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED = 1,
    GRUB_AHCI_BIOS_HANDOFF_OS_OWNED = 2,
    GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED = 8,
    GRUB_AHCI_BIOS_HANDOFF_RWC = 8
  };


123 124 125
struct grub_ahci_device
{
  struct grub_ahci_device *next;
126
  struct grub_ahci_device **prev;
127 128 129 130 131
  volatile struct grub_ahci_hba *hba;
  int port;
  int num;
  struct grub_pci_dma_chunk *command_list_chunk;
  volatile struct grub_ahci_cmd_head *command_list;
132 133
  struct grub_pci_dma_chunk *command_table_chunk;
  volatile struct grub_ahci_cmd_table *command_table;
134
  struct grub_pci_dma_chunk *rfis;
135
  int present;
136
  int atapi;
137 138
};

139 140
static grub_err_t 
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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
enum
  {
    GRUB_AHCI_CONFIG_READ = 0,
    GRUB_AHCI_CONFIG_CFIS_LENGTH_MASK = 0x1f,
    GRUB_AHCI_CONFIG_ATAPI = 0x20,
    GRUB_AHCI_CONFIG_WRITE = 0x40,
    GRUB_AHCI_CONFIG_PREFETCH = 0x80,
    GRUB_AHCI_CONFIG_RESET = 0x100,
    GRUB_AHCI_CONFIG_BIST = 0x200,
    GRUB_AHCI_CONFIG_CLEAR_R_OK = 0x400,
    GRUB_AHCI_CONFIG_PMP_MASK = 0xf000,
    GRUB_AHCI_CONFIG_PRDT_LENGTH_MASK = 0xffff0000,
  };
#define GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT 0
#define GRUB_AHCI_CONFIG_PMP_SHIFT 12
#define GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT 16
#define GRUB_AHCI_INTERRUPT_ON_COMPLETE 0x80000000

#define GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH 0x200000

165 166 167
static struct grub_ahci_device *grub_ahci_devices;
static int numdevs;

168
static int
169
grub_ahci_pciinit (grub_pci_device_t dev,
170 171
		   grub_pci_id_t pciid __attribute__ ((unused)),
		   void *data __attribute__ ((unused)))
172 173 174 175
{
  grub_pci_address_t addr;
  grub_uint32_t class;
  grub_uint32_t bar;
176
  unsigned i, nports;
177 178 179 180 181 182 183 184 185 186 187
  volatile struct grub_ahci_hba *hba;

  /* Read class.  */
  addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
  class = grub_pci_read (addr);

  /* Check if this class ID matches that of a PCI IDE Controller.  */
  if (class >> 8 != 0x010601)
    return 0;

  addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG5);
188

189 190
  bar = grub_pci_read (addr);

191 192
  if ((bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK
	      | GRUB_PCI_ADDR_MEM_PREFETCH))
193 194 195
      != (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32))
    return 0;

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 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  grub_dprintf ("ahci", "dev: %x:%x.%x\n", dev.bus, dev.device, dev.function);

  grub_dprintf ("ahci", "tfd[0]: %x\n",
		hba->ports[0].task_file_data);
  grub_dprintf ("ahci", "cmd[0]: %x\n",
		hba->ports[0].command);
  grub_dprintf ("ahci", "st[0]: %x\n",
		hba->ports[0].status);
  grub_dprintf ("ahci", "err[0]: %x\n",
		hba->ports[0].sata_error);

  grub_dprintf ("ahci", "tfd[1]: %x\n",
		hba->ports[1].task_file_data);
  grub_dprintf ("ahci", "cmd[1]: %x\n",
		hba->ports[1].command);
  grub_dprintf ("ahci", "st[1]: %x\n",
		hba->ports[1].status);
  grub_dprintf ("ahci", "err[1]: %x\n",
		hba->ports[1].sata_error);

  hba->ports[1].sata_error = hba->ports[1].sata_error;

  grub_dprintf ("ahci", "err[1]: %x\n",
		hba->ports[1].sata_error);

  grub_dprintf ("ahci", "BH:%x\n", hba->bios_handoff);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250

  if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED))
    {
      grub_uint64_t endtime;

      grub_dprintf ("ahci", "Requesting AHCI ownership\n");
      hba->bios_handoff = (hba->bios_handoff & ~GRUB_AHCI_BIOS_HANDOFF_RWC)
	| GRUB_AHCI_BIOS_HANDOFF_OS_OWNED;
      grub_dprintf ("ahci", "Waiting for BIOS to give up ownership\n");
      endtime = grub_get_time_ms () + 1000;
      while ((hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED)
	     && grub_get_time_ms () < endtime);
      if (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED)
	{
	  grub_dprintf ("ahci", "Forcibly taking ownership\n");
	  hba->bios_handoff = GRUB_AHCI_BIOS_HANDOFF_OS_OWNED;
	  hba->bios_handoff |= GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED;
	}
      else
	grub_dprintf ("ahci", "AHCI ownership obtained\n");
    }
  else
    grub_dprintf ("ahci", "AHCI is already in OS mode\n");
251

252 253 254 255 256
  grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);

  grub_dprintf ("ahci", "err[1]: %x\n",
		hba->ports[1].sata_error);

257
  if (!(hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN))
258 259 260 261
    grub_dprintf ("ahci", "AHCI is in compat mode. Switching\n");
  else
    grub_dprintf ("ahci", "AHCI is in AHCI mode.\n");

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
  grub_dprintf ("ahci", "err[1]: %x\n",
		hba->ports[1].sata_error);

  grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);

  /*  {
      grub_uint64_t endtime;
      hba->global_control |= 1;
      endtime = grub_get_time_ms () + 1000;
      while (hba->global_control & 1)
	if (grub_get_time_ms () > endtime)
	  {
	    grub_dprintf ("ahci", "couldn't reset AHCI\n");
	    return 0;
	  }
	  }*/

  grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);

  grub_dprintf ("ahci", "err[1]: %x\n",
		hba->ports[1].sata_error);

284 285 286 287 288 289 290 291 292 293 294 295 296
  for (i = 0; i < 5; i++)
    {
      hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
      grub_millisleep (1);
      if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)
	break;
    }
  if (i == 5)
    {
      grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n");
      return 0;
    }

297 298 299 300 301 302 303 304 305
  grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);

  grub_dprintf ("ahci", "err[1]: %x\n",
		hba->ports[1].sata_error);

  grub_dprintf ("ahci", "err[1]: %x\n",
		hba->ports[1].sata_error);

  grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);
306 307 308 309 310 311 312 313 314 315 316 317 318

  for (i = 0; i < 5; i++)
    {
      hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
      grub_millisleep (1);
      if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)
	break;
    }
  if (i == 5)
    {
      grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n");
      return 0;
    }
319 320 321 322 323

  grub_dprintf ("ahci", "err[1]: %x\n",
		hba->ports[1].sata_error);

  grub_dprintf ("ahci", "GLC:%x\n", hba->global_control);
324

325
  nports = (GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1;
326

327 328 329 330 331 332
  grub_dprintf ("ahci", "%d AHCI ports, PI = 0x%x\n", nports,
		hba->ports_implemented);

  struct grub_ahci_device *adevs[GRUB_AHCI_HBA_CAP_NPORTS_MASK + 1];
  struct grub_ahci_device *failed_adevs[GRUB_AHCI_HBA_CAP_NPORTS_MASK + 1];
  grub_uint32_t fr_running = 0;
333

334 335
  for (i = 0; i < nports; i++)
    failed_adevs[i] = 0;
336 337 338
  for (i = 0; i < nports; i++)
    {
      if (!(hba->ports_implemented & (1 << i)))
339 340 341 342
	{
	  adevs[i] = 0;
	  continue;
	}
343

344
      adevs[i] = grub_zalloc (sizeof (*adevs[i]));
345
      if (!adevs[i])
346 347
	return 1;

348 349 350 351 352
      adevs[i]->hba = hba;
      adevs[i]->port = i;
      adevs[i]->present = 1;
      adevs[i]->num = numdevs++;
    }
353

354 355 356 357
  for (i = 0; i < nports; i++)
    if (adevs[i])
      {
	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 360
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);

361
	adevs[i]->command_list_chunk = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head) * 32);
362 363 364 365 366
	if (!adevs[i]->command_list_chunk)
	  {
	    adevs[i] = 0;
	    continue;
	  }
367

368 369 370 371 372 373 374 375 376 377 378
	adevs[i]->command_table_chunk = grub_memalign_dma32 (1024,
							    sizeof (struct grub_ahci_cmd_table));
	if (!adevs[i]->command_table_chunk)
	  {
	    grub_dma_free (adevs[i]->command_list_chunk);
	    adevs[i] = 0;
	    continue;
	  }

	adevs[i]->command_list = grub_dma_get_virt (adevs[i]->command_list_chunk);
	adevs[i]->command_table = grub_dma_get_virt (adevs[i]->command_table_chunk);
379 380 381 382 383 384

	grub_memset ((void *) adevs[i]->command_list, 0,
		     sizeof (struct grub_ahci_cmd_table));
	grub_memset ((void *) adevs[i]->command_table, 0,
		     sizeof (struct grub_ahci_cmd_head) * 32);

385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
	adevs[i]->command_list->command_table_base
	  = grub_dma_get_phys (adevs[i]->command_table_chunk);

	grub_dprintf ("ahci", "found device ahci%d (port %d), command_table = %p, command_list = %p\n",
		      adevs[i]->num, adevs[i]->port, grub_dma_get_virt (adevs[i]->command_table_chunk),
		      grub_dma_get_virt (adevs[i]->command_list_chunk));

	adevs[i]->hba->ports[adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
      }

  grub_uint64_t endtime;
  endtime = grub_get_time_ms () + 1000;

  while (grub_get_time_ms () < endtime)
    {
      for (i = 0; i < nports; i++)
	if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
	  break;
      if (i == nports)
	break;
405 406
    }

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
  for (i = 0; i < nports; i++)
    if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
      {
	grub_dprintf ("ahci", "couldn't stop FR on port %d\n", i);
	failed_adevs[i] = adevs[i];
	adevs[i] = 0;
      }

  for (i = 0; i < nports; i++)
    if (adevs[i])
      adevs[i]->hba->ports[adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
  endtime = grub_get_time_ms () + 1000;

  while (grub_get_time_ms () < endtime)
    {
      for (i = 0; i < nports; i++)
	if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
	  break;
      if (i == nports)
	break;
    }

  for (i = 0; i < nports; i++)
    if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
      {
	grub_dprintf ("ahci", "couldn't stop CR on port %d\n", i);
	failed_adevs[i] = adevs[i];
	adevs[i] = 0;
      }
  for (i = 0; i < nports; i++)
    if (adevs[i])
      {
	adevs[i]->hba->ports[adevs[i]->port].inten = 0;
	adevs[i]->hba->ports[adevs[i]->port].intstatus = ~0;
	//  adevs[i]->hba->ports[adevs[i]->port].fbs = 0;

443
	grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
444 445 446 447 448 449 450 451 452 453 454 455 456
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);

	adevs[i]->rfis = grub_memalign_dma32 (4096, 
					     sizeof (struct grub_ahci_received_fis));
	grub_memset ((char *) grub_dma_get_virt (adevs[i]->rfis), 0,
		     sizeof (struct grub_ahci_received_fis));
	grub_memset ((char *) grub_dma_get_virt (adevs[i]->command_list_chunk), 0,
		     sizeof (struct grub_ahci_cmd_head));
	grub_memset ((char *) grub_dma_get_virt (adevs[i]->command_table_chunk), 0,
		     sizeof (struct grub_ahci_cmd_table));
	adevs[i]->hba->ports[adevs[i]->port].fis_base = grub_dma_get_phys (adevs[i]->rfis);
	adevs[i]->hba->ports[adevs[i]->port].command_list_base
	  = grub_dma_get_phys (adevs[i]->command_list_chunk);
457
	adevs[i]->hba->ports[adevs[i]->port].command_issue = 0;
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_FRE;
      }

  endtime = grub_get_time_ms () + 1000;

  while (grub_get_time_ms () < endtime)
    {
      for (i = 0; i < nports; i++)
	if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
	  break;
      if (i == nports)
	break;
    }

  for (i = 0; i < nports; i++)
    if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
      {
	grub_dprintf ("ahci", "couldn't start FR on port %d\n", i);
	failed_adevs[i] = adevs[i];
	adevs[i] = 0;
      }

  for (i = 0; i < nports; i++)
    if (adevs[i])
      {
483
	grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
484 485 486 487 488 489 490
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);
	fr_running |= (1 << i);

	adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_SPIN_UP;
	adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_POWER_ON;
	adevs[i]->hba->ports[adevs[i]->port].command |= 1 << 28;

491
	grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);
      }

  /* 10ms should actually be enough.  */
  endtime = grub_get_time_ms () + 100;

  while (grub_get_time_ms () < endtime)
    {
      for (i = 0; i < nports; i++)
	if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].status & 7) != 3)
	  break;
      if (i == nports)
	break;
    }

  for (i = 0; i < nports; i++)
    if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].status & 7) != 3)
      {
	grub_dprintf ("ahci", "couldn't detect device on port %d\n", i);
	failed_adevs[i] = adevs[i];
	adevs[i] = 0;
      }

  for (i = 0; i < nports; i++)
    if (adevs[i])
      {
518
	grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port,
519 520 521 522 523
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);

	adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_POWER_ON;
	adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_SPIN_UP;

524
	grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port,
525 526 527
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);

	adevs[i]->hba->ports[adevs[i]->port].sata_error = ~0;
528
	grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port,
529 530
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);

531
	grub_dprintf ("ahci", "port %d, offset: %x, tfd:%x, CMD: %x\n", adevs[i]->port,
532 533
		      (int) ((char *) &adevs[i]->hba->ports[adevs[i]->port].task_file_data - 
			     (char *) adevs[i]->hba),
534 535 536
		      adevs[i]->hba->ports[adevs[i]->port].task_file_data,
		      adevs[i]->hba->ports[adevs[i]->port].command);

537
	grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port,
538 539 540 541 542 543 544
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);
      }


  for (i = 0; i < nports; i++)
    if (adevs[i])
      {
545
	grub_dprintf ("ahci", "port %d, offset: %x, tfd:%x, CMD: %x\n", adevs[i]->port,
546 547
		      (int) ((char *) &adevs[i]->hba->ports[adevs[i]->port].task_file_data - 
			     (char *) adevs[i]->hba),
548 549 550
		      adevs[i]->hba->ports[adevs[i]->port].task_file_data,
		      adevs[i]->hba->ports[adevs[i]->port].command);

551
	grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port,
552 553 554
		      adevs[i]->hba->ports[adevs[i]->port].sata_error);

	adevs[i]->hba->ports[adevs[i]->port].command
555 556 557
	  = (adevs[i]->hba->ports[adevs[i]->port].command & 0x0fffffff) | (1 << 28)
	  | GRUB_AHCI_HBA_PORT_CMD_SPIN_UP
	  | GRUB_AHCI_HBA_PORT_CMD_POWER_ON;
558 559 560 561 562 563 564 565 566 567 568 569

	/*  struct grub_disk_ata_pass_through_parms parms2;
	    grub_memset (&parms2, 0, sizeof (parms2));
	    parms2.taskfile.cmd = 8;
	    grub_ahci_readwrite_real (dev, &parms2, 1, 1);*/
      }

  endtime = grub_get_time_ms () + 10000;

  while (grub_get_time_ms () < endtime)
    {
      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 572 573 574 575 576
	  break;
      if (i == nports)
	break;
    }

  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 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
      {
	grub_dprintf ("ahci", "port %d is busy\n", i);
	failed_adevs[i] = adevs[i];
	adevs[i] = 0;
      }

  for (i = 0; i < nports; i++)
    if (adevs[i])
      adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;

  endtime = grub_get_time_ms () + 1000;

  while (grub_get_time_ms () < endtime)
    {
      for (i = 0; i < nports; i++)
	if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
	  break;
      if (i == nports)
	break;
    }

  for (i = 0; i < nports; i++)
    if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
      {
	grub_dprintf ("ahci", "couldn't start CR on port %d\n", i);
	failed_adevs[i] = adevs[i];
	adevs[i] = 0;
      }
606 607 608

  grub_dprintf ("ahci", "cleaning up failed devs\n");

609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
  for (i = 0; i < nports; i++)
    if (failed_adevs[i] && (fr_running & (1 << i)))
      failed_adevs[i]->hba->ports[failed_adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;

  endtime = grub_get_time_ms () + 1000;
  while (grub_get_time_ms () < endtime)
    {
      for (i = 0; i < nports; i++)
	if (failed_adevs[i] && (fr_running & (1 << i)) && (failed_adevs[i]->hba->ports[failed_adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
	  break;
      if (i == nports)
	break;
    }
  for (i = 0; i < nports; i++)
    if (failed_adevs[i])
      {
625 626 627
	grub_dma_free (failed_adevs[i]->command_list_chunk);
	grub_dma_free (failed_adevs[i]->command_table_chunk);
	grub_dma_free (failed_adevs[i]->rfis);
628 629
      }

630 631 632 633
  for (i = 0; i < nports; i++)
    if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].sig >> 16) == 0xeb14)
      adevs[i]->atapi = 1;

634 635 636 637
  addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
  grub_pci_write_word (addr, grub_pci_read_word (addr)
		    | GRUB_PCI_COMMAND_BUS_MASTER);

638 639 640 641 642 643 644
  for (i = 0; i < nports; i++)
    if (adevs[i])
      {
	grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices),
			GRUB_AS_LIST (adevs[i]));
      }

645 646 647 648 649 650
  return 0;
}

static grub_err_t
grub_ahci_initialize (void)
{
651
  grub_pci_iterate (grub_ahci_pciinit, NULL);
652 653 654
  return grub_errno;
}

655 656 657
static grub_err_t
grub_ahci_fini_hw (int noreturn __attribute__ ((unused)))
{
658
  struct grub_ahci_device *dev;
659

660
  for (dev = grub_ahci_devices; dev; dev = dev->next)
661
    {
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
      grub_uint64_t endtime;

      dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
      endtime = grub_get_time_ms () + 1000;
      while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
	if (grub_get_time_ms () > endtime)
	  {
	    grub_dprintf ("ahci", "couldn't stop FR\n");
	    break;
	  }

      dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
      endtime = grub_get_time_ms () + 1000;
      while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
	if (grub_get_time_ms () > endtime)
	  {
	    grub_dprintf ("ahci", "couldn't stop CR\n");
	    break;
	  }
681 682 683
      grub_dma_free (dev->command_list_chunk);
      grub_dma_free (dev->command_table_chunk);
      grub_dma_free (dev->rfis);
684 685 686
      dev->command_list_chunk = NULL;
      dev->command_table_chunk = NULL;
      dev->rfis = NULL;
687 688 689 690
    }
  return GRUB_ERR_NONE;
}

691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
static int 
reinit_port (struct grub_ahci_device *dev)
{
  struct grub_pci_dma_chunk *command_list;
  struct grub_pci_dma_chunk *command_table;
  grub_uint64_t endtime;

  command_list = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head));
  if (!command_list)
    return 1;

  command_table = grub_memalign_dma32 (1024,
				       sizeof (struct grub_ahci_cmd_table));
  if (!command_table)
    {
      grub_dma_free (command_list);
      return 1;
    }

  grub_dprintf ("ahci", "found device ahci%d (port %d)\n", dev->num, dev->port);

  dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
  endtime = grub_get_time_ms () + 1000;
  while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
    if (grub_get_time_ms () > endtime)
      {
	grub_dprintf ("ahci", "couldn't stop FR\n");
	goto out;
      }

  dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
  endtime = grub_get_time_ms () + 1000;
  while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
    if (grub_get_time_ms () > endtime)
      {
	grub_dprintf ("ahci", "couldn't stop CR\n");
	goto out;
      }

  dev->hba->ports[dev->port].fbs = 2;

  dev->rfis = grub_memalign_dma32 (4096, 
				   sizeof (struct grub_ahci_received_fis));
  grub_memset ((char *) grub_dma_get_virt (dev->rfis), 0,
	       sizeof (struct grub_ahci_received_fis));
  dev->hba->ports[dev->port].fis_base = grub_dma_get_phys (dev->rfis);
  dev->hba->ports[dev->port].command_list_base
    = grub_dma_get_phys (command_list);
  dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_FRE;
  while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
    if (grub_get_time_ms () > endtime)
      {
	grub_dprintf ("ahci", "couldn't start FR\n");
	dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
	goto out;
      }
  dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;
  while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
    if (grub_get_time_ms () > endtime)
      {
	grub_dprintf ("ahci", "couldn't start CR\n");
	dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_CR;
	goto out_stop_fr;
      }

  dev->hba->ports[dev->port].command
    = (dev->hba->ports[dev->port].command & 0x0fffffff) | (1 << 28) | 2 | 4;

  dev->command_list_chunk = command_list;
  dev->command_list = grub_dma_get_virt (command_list);
  dev->command_table_chunk = command_table;
  dev->command_table = grub_dma_get_virt (command_table);
  dev->command_list->command_table_base
    = grub_dma_get_phys (command_table);

  return 0;
 out_stop_fr:
  dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
  endtime = grub_get_time_ms () + 1000;
  while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
    if (grub_get_time_ms () > endtime)
      {
	grub_dprintf ("ahci", "couldn't stop FR\n");
	break;
      }
 out:
  grub_dma_free (command_list);
  grub_dma_free (command_table);
  grub_dma_free (dev->rfis);
  return 1;
}

783 784 785
static grub_err_t
grub_ahci_restore_hw (void)
{
786 787 788
  struct grub_ahci_device **pdev;

  for (pdev = &grub_ahci_devices; *pdev; pdev = &((*pdev)->next))
789
    if (reinit_port (*pdev))
790 791 792 793 794 795 796
      {
	struct grub_ahci_device *odev;
	odev = *pdev;
	*pdev = (*pdev)->next;
	grub_free (odev);
      }
  return GRUB_ERR_NONE;
797 798
}

799 800 801 802



static int
803 804
grub_ahci_iterate (grub_ata_dev_iterate_hook_t hook, void *hook_data,
		   grub_disk_pull_t pull)
805 806 807
{
  struct grub_ahci_device *dev;

808 809 810
  if (pull != GRUB_DISK_PULL_NONE)
    return 0;

811
  FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
812
    if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num, hook_data))
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
      return 1;

  return 0;
}

#if 0
static int
find_free_cmd_slot (struct grub_ahci_device *dev)
{
  int i;
  for (i = 0; i < 32; i++)
    {
      if (dev->hda->ports[dev->port].command_issue & (1 << i))
	continue;
      if (dev->hda->ports[dev->port].sata_active & (1 << i))
	continue;
      return i;
    }
  return -1;
}
#endif

835 836 837 838
enum
  {
    GRUB_AHCI_FIS_REG_H2D = 0x27
  };
839

840 841 842 843 844 845 846 847 848 849 850 851
static const int register_map[11] = { 3 /* Features */,
				      12 /* Sectors */,
				      4 /* LBA low */,
				      5 /* LBA mid */,
				      6 /* LBA high */,
				      7 /* Device */,
				      2 /* CMD register */,
				      13 /* Sectors 48  */,
				      8 /* LBA48 low */,
				      9 /* LBA48 mid */,
				      10 /* LBA48 high */ }; 

852 853
static grub_err_t
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 860
  if (force || (dev->hba->ports[dev->port].command_issue & 1)
      || (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 865 866 867 868 869
      dev->hba->ports[dev->port].command_issue = 0;
      dev->command_list[0].config = 0;
      dev->command_table[0].prdt[0].unused = 0;
      dev->command_table[0].prdt[0].size = 0;
      dev->command_table[0].prdt[0].data_base = 0;

870 871 872 873
      endtime = grub_get_time_ms () + 1000;
      while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
	if (grub_get_time_ms () > endtime)
	  {
874 875 876 877 878 879 880 881 882 883
	    grub_dprintf ("ahci", "couldn't stop CR");
	    return grub_error (GRUB_ERR_IO, "couldn't stop CR");
	  }
      dev->hba->ports[dev->port].command |= 8;
      while (dev->hba->ports[dev->port].command & 8)
	if (grub_get_time_ms () > endtime)
	  {
	    grub_dprintf ("ahci", "couldn't set CLO\n");
	    dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
	    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 888 889
      while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
	if (grub_get_time_ms () > endtime)
	  {
890 891 892
	    grub_dprintf ("ahci", "couldn't stop CR");
	    dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
	    return grub_error (GRUB_ERR_IO, "couldn't stop CR");
893
	  }
894 895 896 897
      dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error;
      grub_memset (&parms2, 0, sizeof (parms2));
      parms2.taskfile.cmd = 8;
      return grub_ahci_readwrite_real (dev, &parms2, 1, 1);
898
    }
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
  return GRUB_ERR_NONE;
}

static grub_err_t 
grub_ahci_readwrite_real (struct grub_ahci_device *dev,
			  struct grub_disk_ata_pass_through_parms *parms,
			  int spinup, int reset)
{
  struct grub_pci_dma_chunk *bufc;
  grub_uint64_t endtime;
  unsigned i;
  grub_err_t err = GRUB_ERR_NONE;

  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		dev->hba->ports[dev->port].task_file_data);

  if (!reset)
    grub_ahci_reset_port (dev, 0);
 
  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		dev->hba->ports[dev->port].task_file_data);
  dev->hba->ports[dev->port].task_file_data = 0;
  dev->hba->ports[dev->port].command_issue = 0;
  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		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 928 929 930 931

  grub_dprintf("ahci", "grub_ahci_read (size=%llu, cmdsize = %llu)\n",
	       (unsigned long long) parms->size,
	       (unsigned long long) parms->cmdsize);

  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 938 939 940
  if (parms->size)
    bufc = grub_memalign_dma32 (1024, parms->size + (parms->size & 1));
  else
    bufc = grub_memalign_dma32 (1024, 512);
941

942 943 944
  grub_dprintf ("ahci", "AHCI tfd = %x, CL=%p\n",
		dev->hba->ports[dev->port].task_file_data,
		dev->command_list);
945 946
  /* FIXME: support port multipliers.  */
  dev->command_list[0].config
947 948
    = (5 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT)
    //    | 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 953
    | (parms->write ? GRUB_AHCI_CONFIG_WRITE : GRUB_AHCI_CONFIG_READ)
    | (parms->taskfile.cmd == 8 ? (1 << 8) : 0);
954 955 956
  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		dev->hba->ports[dev->port].task_file_data);

Colin Watson's avatar
Colin Watson committed
957
  dev->command_list[0].transferred = 0;
958 959
  dev->command_list[0].command_table_base
    = grub_dma_get_phys (dev->command_table_chunk);
960

961 962
  grub_memset ((char *) dev->command_list[0].unused, 0,
	       sizeof (dev->command_list[0].unused));
963

964 965
  grub_memset ((char *) &dev->command_table[0], 0,
	       sizeof (dev->command_table[0]));
966 967 968
  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		dev->hba->ports[dev->port].task_file_data);

969 970 971 972
  if (parms->cmdsize)
    grub_memcpy ((char *) dev->command_table[0].command, parms->cmd,
		 parms->cmdsize);

973 974 975
  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		dev->hba->ports[dev->port].task_file_data);

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 980
    dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i]; 

981 982 983 984 985 986 987 988 989 990
  grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
		dev->command_table[0].cfis[0], dev->command_table[0].cfis[1],
		dev->command_table[0].cfis[2], dev->command_table[0].cfis[3],
		dev->command_table[0].cfis[4], dev->command_table[0].cfis[5],
		dev->command_table[0].cfis[6], dev->command_table[0].cfis[7]);
  grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
		dev->command_table[0].cfis[8], dev->command_table[0].cfis[9],
		dev->command_table[0].cfis[10], dev->command_table[0].cfis[11],
		dev->command_table[0].cfis[12], dev->command_table[0].cfis[13],
		dev->command_table[0].cfis[14], dev->command_table[0].cfis[15]);
991 992 993

  dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc);
  dev->command_table[0].prdt[0].unused = 0;
994
  dev->command_table[0].prdt[0].size = (parms->size - 1);
995

996 997
  grub_dprintf ("ahci", "PRDT = %" PRIxGRUB_UINT64_T ", %x, %x (%"
		PRIuGRUB_SIZE ")\n",
998 999 1000
		dev->command_table[0].prdt[0].data_base,
		dev->command_table[0].prdt[0].unused,
		dev->command_table[0].prdt[0].size,
1001 1002
		(grub_size_t) ((char *) &dev->command_table[0].prdt[0]
			       - (char *) &dev->command_table[0]));
1003

1004 1005 1006
  if (parms->write)
    grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size);

1007
  grub_dprintf ("ahci", "AHCI command scheduled\n");
1008 1009
  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		dev->hba->ports[dev->port].task_file_data);
1010 1011 1012 1013 1014
  grub_dprintf ("ahci", "AHCI inten = %x\n",
		dev->hba->ports[dev->port].inten);
  grub_dprintf ("ahci", "AHCI intstatus = %x\n",
		dev->hba->ports[dev->port].intstatus);

1015 1016
  dev->hba->ports[dev->port].inten = 0xffffffff;//(1 << 2) | (1 << 5);
  dev->hba->ports[dev->port].intstatus = 0xffffffff;//(1 << 2) | (1 << 5);
1017 1018
  grub_dprintf ("ahci", "AHCI inten = %x\n",
		dev->hba->ports[dev->port].inten);
1019 1020
  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		dev->hba->ports[dev->port].task_file_data);
1021 1022
  dev->hba->ports[dev->port].sata_active = 1;
  dev->hba->ports[dev->port].command_issue = 1;
1023 1024 1025
  grub_dprintf ("ahci", "AHCI sig = %x\n", dev->hba->ports[dev->port].sig);
  grub_dprintf ("ahci", "AHCI tfd = %x\n",
		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 1030
    if (grub_get_time_ms () > endtime)
      {
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 1035
		      dev->hba->ports[dev->port].intstatus,
		      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 1039
	if (!reset)
	  grub_ahci_reset_port (dev, 1);
1040
	break;
1041 1042
      }

1043 1044 1045 1046
  grub_dprintf ("ahci", "AHCI command completed <%x %x %x %x %x, %x %x>\n",
		dev->hba->ports[dev->port].command_issue,
		dev->hba->ports[dev->port].intstatus,
		dev->hba->ports[dev->port].task_file_data,
Colin Watson's avatar
Colin Watson committed
1047
		dev->command_list[0].transferred,
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
		dev->hba->ports[dev->port].sata_error,
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x00],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x18]);
  grub_dprintf ("ahci",
		"last PIO FIS %08x %08x %08x %08x %08x %08x %08x %08x\n",
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x08],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x09],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0a],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0b],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0c],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0d],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0e],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0f]);
  grub_dprintf ("ahci",
		"last REG FIS %08x %08x %08x %08x %08x %08x %08x %08x\n",
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x10],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x11],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x12],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x13],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x14],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x15],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x16],
		((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x17]);
1071 1072 1073 1074

  if (!parms->write)
    grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size);
  grub_dma_free (bufc);
1075

1076 1077 1078 1079 1080
  return err;
}

static grub_err_t 
grub_ahci_readwrite (grub_ata_t disk,
1081 1082
		     struct grub_disk_ata_pass_through_parms *parms,
		     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 1091
{
  struct grub_ahci_device *dev;

1092 1093 1094
  if (id != GRUB_SCSI_SUBSYSTEM_AHCI)
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an AHCI device");

1095
  FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
1096 1097
    if (dev->num == devnum)
      break;
1098 1099 1100 1101

  if (! dev)
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device");

1102 1103 1104
  grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num);

  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 1111 1112

  return GRUB_ERR_NONE;
}

1113
static struct grub_ata_dev grub_ahci_dev =
1114 1115 1116
  {
    .iterate = grub_ahci_iterate,
    .open = grub_ahci_open,
1117
    .readwrite = grub_ahci_readwrite,
1118 1119 1120 1121
  };



1122
static struct grub_preboot *fini_hnd;
1123

1124 1125
GRUB_MOD_INIT(ahci)
{
1126
  grub_stop_disk_firmware ();
1127 1128 1129 1130 1131

  /* AHCI initialization.  */
  grub_ahci_initialize ();

  /* AHCI devices are handled by scsi.mod.  */
1132
  grub_ata_dev_register (&grub_ahci_dev);
1133 1134 1135 1136

  fini_hnd = grub_loader_register_preboot_hook (grub_ahci_fini_hw,
						grub_ahci_restore_hw,
						GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
1137 1138 1139 1140
}

GRUB_MOD_FINI(ahci)
{
1141 1142 1143
  grub_ahci_fini_hw (0);
  grub_loader_unregister_preboot_hook (fini_hnd);

1144
  grub_ata_dev_unregister (&grub_ahci_dev);
1145
}