Ignore:
Timestamp:
Mar 1, 2011, 10:10:11 AM (14 years ago)
Author:
chris
Message:

Version 1.06
============

  • Finally came across a BIOS which accesses the ICH7/8 controller via SATA registers (i.e. not AHCI mode). This required a few changes to the code at boot time because it turned out that COMRESETs are required whenever switching to/from AHCI mode to allow the AHCI or SATA controller to re-discover the attached devices:
  • 'init_reset' will now be forced on when finding a controller in non-AHCI mode at boot time.
  • A COMRESET is initiated for each implemented port after turning off AHCI mode when restoring the BIOS configuration; this is done only for Intel controllers at this point because they map the AHCI port SCR MMIO registers even when not in AHCI mode.
  • apm_suspend() has been adjusted to restore the BIOS configuration to prevent needless timeouts when the BIOS takes over during suspend or power-off operations.
  • Small changes to the functions which save/restore BIOS/port settings to avoid pitfalls; among others, the port save/restore code now also saves and restores the port's engine status.
  • Improvements to debug logging around port resets.
  • Moved code to clear pending interrupts from ahci_reset_port() to ahci_stop_port() because both need it and resetting a port involves stopping it, first.
  • NCQ mode has found to cause problems on a Dell D630. This may be related to the hard disk used for the test but since I've never seen more than one queued command regardless of the I/O load (even during simulaneous xcopy operations), NCQ mode is now off by default and needs to be turned on via the /N switch (i.e. the the /N switch now has a reversed meaning).
  • Removed the code which attempts to establish another MMIO base address in case the one assigned by the BIOS can't be reserved via resource manager; if there's a conflict, it's extremely unlikely we would ever be able to restore the BIOS MMIO address at boot time without the BIOS clashing with whatever conflicts with the MMIO address, thus there's no point trying to do any of this.
  • Implemented a reset context hook watchdog; in the early boot phase, some components apparently don't yield the CPU so the context hook will never execute without the watchdog. Now we'll give the context hook 10 seconds to execute, otherwise the watchdog will expire and we'll call the context hook directly from the corresponding timer callback.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/os2ahci/ahci.c

    r79 r80  
    135135  for (i = 0; i < HOST_CAP2; i += sizeof(u32)) {
    136136    ai->bios_config[i / sizeof(u32)] = readl(ai->mmio + i);
     137  }
     138
     139  if ((ai->bios_config[HOST_CTL / sizeof(u32)] & HOST_AHCI_EN) == 0 &&
     140      ai->pci->vendor == PCI_VENDOR_ID_INTEL) {
     141    /* Adapter is not in AHCI mode and the spec says a COMRESET is
     142     * required when switching from SATA to AHCI mode and vice versa.
     143     */
     144    init_reset = 1;
    137145  }
    138146
     
    184192        if (val & HOST_CAP2_NVMHCI)    printf(" nvmhci");
    185193        if (val & HOST_CAP2_APST)      printf(" apst");
    186 
    187194      }
    188195      printf("\n");
     
    277284  ddprintf("restoring AHCI BIOS configuration\n");
    278285
    279   /* restore saved BIOS configuration */
    280   writel(ai->mmio + HOST_CTL,       ai->bios_config[HOST_CTL / sizeof(u32)]);
     286  /* Restore saved BIOS configuration; please note that HOST_CTL is restored
     287   * last because it may cause AHCI mode to be turned off again.
     288   */
    281289  writel(ai->mmio + HOST_CCC,       ai->bios_config[HOST_CCC / sizeof(u32)]);
    282290  writel(ai->mmio + HOST_CCC_PORTS, ai->bios_config[HOST_CCC_PORTS / sizeof(u32)]);
    283291  writel(ai->mmio + HOST_EM_CTL,    ai->bios_config[HOST_EM_CTL / sizeof(u32)]);
     292  writel(ai->mmio + HOST_CTL,       ai->bios_config[HOST_CTL / sizeof(u32)]);
    284293
    285294  /* flush PCI MMIO delayed write buffers */
    286   readl(ai->mmio + HOST_EM_CTL);
     295  readl(ai->mmio + HOST_CTL);
     296
     297  if ((ai->bios_config[HOST_CTL / sizeof(u32)] & HOST_AHCI_EN) == 0 &&
     298      ai->pci->vendor == PCI_VENDOR_ID_INTEL) {
     299
     300    /* This BIOS apparently accesses this controller via SATA registers and
     301     * the AHCI spec says that we should issue a COMRESET on each port after
     302     * disabling AHCI mode to allow the SATA controller to re-recognize attached
     303     * devices. How to do this depends on the controller, of course, but so
     304     * far I've only seen Dell notebook BIOSs with Intel chipsets to behave
     305     * like this; all other BIOS implementations I've seen so far seem to take
     306     * AHCI mode literally and operate the controller in AHCI mode from the
     307     * beginning.
     308     *
     309     * We'll use a feature on Intel ICH7/8 controllers which provides MMIO
     310     * mappings for the AHCI SCR registers even when not in AHCI mode.
     311     */
     312    int p;
     313
     314    for (p = 0; p < AHCI_MAX_PORTS; p++) {
     315      if (ai->port_map & (1UL << p)) {
     316        u8 _far *port_mmio = port_base(ai, p);
     317        u32 tmp;
     318
     319        tmp = readl(port_mmio + PORT_SCR_CTL) & ~0x0000000fUL;
     320        writel(port_mmio + PORT_SCR_CTL, tmp | 1);
     321        readl(port_mmio + PORT_SCR_CTL);  /* flush */
     322
     323        /* spec says "leave reset bit on for at least 1ms"; make it 2ms */
     324        mdelay(2);
     325
     326        writel(port_mmio + PORT_SCR_CTL, tmp);
     327        readl(port_mmio + PORT_SCR_CTL);  /* flush */
     328      }
     329    }
     330
     331    /* Wait some time to give the COMRESET a chance to complete (usually, at
     332     * least hard disks complete the reset within a few milliseonds)
     333     */
     334    mdelay(20);
     335  }
    287336
    288337  return(0);
     
    298347
    299348  /* restore saved BIOS configuration */
    300   writel(ai->mmio + HOST_CTL,       ai->bios_config[HOST_CTL / sizeof(u32)]);
    301349  writel(ai->mmio + HOST_CCC,       ai->bios_config[HOST_CCC / sizeof(u32)]);
    302350  writel(ai->mmio + HOST_CCC_PORTS, ai->bios_config[HOST_CCC_PORTS / sizeof(u32)]);
    303351  writel(ai->mmio + HOST_EM_CTL,    ai->bios_config[HOST_EM_CTL / sizeof(u32)]);
     352  writel(ai->mmio + HOST_CTL,       ai->bios_config[HOST_CTL / sizeof(u32)]);
    304353
    305354  /* flush PCI MMIO delayed write buffers */
    306   readl(ai->mmio + HOST_EM_CTL);
     355  readl(ai->mmio + HOST_CTL);
    307356
    308357  /* (re-)enable AHCI mode */
     
    333382  pc->fis_rx_h   = readl(port_mmio + PORT_FIS_ADDR_HI);
    334383  pc->irq_mask   = readl(port_mmio + PORT_IRQ_MASK);
     384  pc->port_cmd   = readl(port_mmio + PORT_CMD);
    335385
    336386  return(pc);
     
    341391 * configuration (command list and FIS buffers and the IRQ mask).
    342392 *
    343  * The port configuration automatically freed.
     393 * The port configuration is automatically freed.
    344394 */
    345395void ahci_restore_port_config(AD_INFO *ai, int p, AHCI_PORT_CFG *pc)
     
    347397  u8 _far *port_mmio = port_base(ai, p);
    348398
    349   writel(port_mmio + PORT_LST_ADDR,    pc->cmd_list);
    350   writel(port_mmio + PORT_LST_ADDR_HI, pc->cmd_list_h);
    351   writel(port_mmio + PORT_FIS_ADDR,    pc->fis_rx);
    352   writel(port_mmio + PORT_FIS_ADDR_HI, pc->fis_rx_h);
    353   writel(port_mmio + PORT_IRQ_MASK,    pc->irq_mask);
    354 
    355   readl(port_base(ai, p) + PORT_IRQ_MASK); /* flush */
     399  /* stop the port, first */
     400  ahci_stop_port(ai, p);
     401
     402  if (ai->bios_config[HOST_CTL / sizeof(u32)] & HOST_AHCI_EN) {
     403    /* BIOS uses AHCI, too, so we need to restore the port settings;
     404     * restoring PORT_CMD way well start the port again but that's what
     405     * this function is all about.
     406     */
     407    writel(port_mmio + PORT_LST_ADDR,    pc->cmd_list);
     408    writel(port_mmio + PORT_LST_ADDR_HI, pc->cmd_list_h);
     409    writel(port_mmio + PORT_FIS_ADDR,    pc->fis_rx);
     410    writel(port_mmio + PORT_FIS_ADDR_HI, pc->fis_rx_h);
     411    writel(port_mmio + PORT_IRQ_MASK,    pc->irq_mask);
     412    writel(port_mmio + PORT_CMD,         pc->port_cmd);
     413
     414    readl(port_base(ai, p) + PORT_IRQ_MASK); /* flush */
     415  }
    356416
    357417  free(pc);
     
    439499      /* start/reset port; if no device is attached, this is expected to fail */
    440500      if (init_reset) {
    441         ddprintf("init-resetting port #%d\n", p);
    442501        rc = ahci_reset_port(ai, p, 0);
    443502      } else {
     
    547606  for (p = 0; p < AHCI_MAX_PORTS; p++) {
    548607    if (ai->port_map & (1UL << p)) {
    549       dprintf("restarting port #%d\n", p);
    550       ahci_stop_port(ai, p);
    551       ahci_start_port(ai, p, 1);
     608      if (init_reset) {
     609        ahci_reset_port(ai, p, 1);
     610      } else {
     611        dprintf("restarting port #%d\n", p);
     612        ahci_stop_port(ai, p);
     613        ahci_start_port(ai, p, 1);
     614      }
    552615    }
    553616  }
     
    561624  readl(ai->mmio + HOST_CTL); /* flush */
    562625
    563   /* enable interrupts on PCI-level (PCI 2.3 added a feature to disable ints) */
     626  /* enable interrupts on PCI-level (PCI 2.3 added a feature to disable INTs) */
    564627  /* pci_enable_int(ai->bus, ai->dev_func); */
    565628
     
    586649
    587650  dprintf("resetting port %d.%d\n", ad_no(ai), p);
    588   ddprintf(" PORT_CMD_ISSUE = 0x%lx\n", readl(port_mmio + PORT_CMD_ISSUE));
    589   ddprintf(" PORT_SCR_ACT   = 0x%lx\n", readl(port_mmio + PORT_SCR_ACT));
    590   ddprintf(" PORT_SCR_ERR   = 0x%lx\n", readl(port_mmio + PORT_SCR_ERR));
    591   ddprintf(" PORT_TFDATA    = 0x%lx\n", readl(port_mmio + PORT_TFDATA));
    592   ddprintf(" PORT_IRQ_STAT  = 0x%lx\n", readl(port_mmio + PORT_IRQ_STAT));
    593   ddprintf(" PORT_IRQ_MASK  = 0x%lx\n", readl(port_mmio + PORT_IRQ_MASK));
    594   ddprintf(" HOST_IRQ_STAT  = 0x%lx\n", readl(ai->mmio + HOST_IRQ_STAT));
     651  if (debug > 1) {
     652    printf("command engine status:\n");
     653    printf(" PORT_SCR_ACT   = 0x%lx\n", readl(port_mmio + PORT_SCR_ACT));
     654    printf(" PORT_CMD_ISSUE = 0x%lx\n", readl(port_mmio + PORT_CMD_ISSUE));
     655    printf("link/device status:\n");
     656    printf(" PORT_SCR_STAT  = 0x%lx\n", readl(port_mmio + PORT_SCR_STAT));
     657    printf(" PORT_SCR_CTL   = 0x%lx\n", readl(port_mmio + PORT_SCR_CTL));
     658    printf(" PORT_SCR_ERR   = 0x%lx\n", readl(port_mmio + PORT_SCR_ERR));
     659    printf(" PORT_TFDATA    = 0x%lx\n", readl(port_mmio + PORT_TFDATA));
     660    printf("interrupt status:\n");
     661    printf(" PORT_IRQ_STAT  = 0x%lx\n", readl(port_mmio + PORT_IRQ_STAT));
     662    printf(" PORT_IRQ_MASK  = 0x%lx\n", readl(port_mmio + PORT_IRQ_MASK));
     663    printf(" HOST_IRQ_STAT  = 0x%lx\n", readl(ai->mmio + HOST_IRQ_STAT));
     664  }
    595665
    596666  /* stop port engines (we don't care whether there is an error doing so) */
     
    600670  tmp = readl(port_mmio + PORT_SCR_ERR);
    601671  writel(port_mmio + PORT_SCR_ERR, tmp);
    602 
    603   /* clear pending port IRQs */
    604   tmp = readl(port_mmio + PORT_IRQ_STAT);
    605   if (tmp) {
    606     writel(port_mmio + PORT_IRQ_STAT, tmp);
    607   }
    608   writel(ai->mmio + HOST_IRQ_STAT, 1UL << p);
    609672
    610673  /* set link speed and power management options */
     
    721784  u32 tmp;
    722785
    723   /* set comand header and FIS address registers */
     786  /* set command header and FIS address registers */
    724787  writel(port_mmio + PORT_LST_ADDR, port_dma + offsetof(AHCI_PORT_DMA, cmd_hdr));
    725788  writel(port_mmio + PORT_LST_ADDR_HI, 0);
     
    757820{
    758821  u8 _far *port_mmio = port_base(ai, p);
     822  u32 tmp;
    759823  int rc;
    760824
     
    773837    return(rc);
    774838  }
     839
     840  /* clear any pending port IRQs */
     841  tmp = readl(port_mmio + PORT_IRQ_STAT);
     842  if (tmp) {
     843    writel(port_mmio + PORT_IRQ_STAT, tmp);
     844  }
     845  writel(ai->mmio + HOST_IRQ_STAT, 1UL << p);
    775846
    776847  /* reset PxSACT register (tagged command queues, not reset by COMRESET) */
     
    10231094  }
    10241095
    1025   /* restart port (includes the necessary port configuration) */
    1026   if (ahci_stop_port(ai, p) || ahci_start_port(ai, p, 0)) {
     1096  /* restart/reset port (includes the necessary port configuration) */
     1097  if ((ai->bios_config[HOST_CTL / sizeof(u32)] & HOST_AHCI_EN) == 0 &&
     1098      ai->pci->vendor == PCI_VENDOR_ID_INTEL) {
     1099    /* As outlined in ahci_restore_bios_config(), switching back and
     1100     * forth between SATA and AHCI mode requires a COMRESET to force
     1101     * the corresponding controller subsystem to rediscover attached
     1102     * devices. Thus, we'll reset the port instead of stopping and
     1103     * starting it.
     1104     */
     1105    if (ahci_reset_port(ai, p, 0)) {
     1106      iorb_seterr(iorb, IOERR_ADAPTER_NONSPECIFIC);
     1107      goto restore_bios_config;
     1108    }
     1109
     1110  } else if (ahci_stop_port(ai, p) || ahci_start_port(ai, p, 0)) {
    10271111    iorb_seterr(iorb, IOERR_ADAPTER_NONSPECIFIC);
    10281112    goto restore_bios_config;
     
    15001584  } else {
    15011585    /* complete ATA-specific device information */
    1502     if (disable_ncq[ad_no(ai)][p]) {
    1503       /* MT: set ncq_max to 1 if NCQ is disabled for this port */
     1586    if (enable_ncq[ad_no(ai)][p]) {
     1587      ai->ports[p].devs[d].ncq_max = id_buf[ATA_ID_QUEUE_DEPTH] & 0x001fU;
     1588    }
     1589    if (ai->ports[p].devs[d].ncq_max < 1) {
     1590      /* NCQ not enabled for this device, or device doesn't support NCQ */
    15041591      ai->ports[p].devs[d].ncq_max = 1;
    1505       dprintf("NCQ off for a:%d p:%d\n", (int) ad_no(ai), p);
    1506     } else {
    1507       ai->ports[p].devs[d].ncq_max = max(id_buf[ATA_ID_QUEUE_DEPTH] & 0x001fU, 1);
    1508       dprintf("NCQ max=%d for a:%d p:%d\n", ai->ports[p].devs[d].ncq_max,
    1509                                             (int) ad_no(ai), p);
    1510     }
    1511 
     1592    }
    15121593    if (id_buf[ATA_ID_CFS_ENABLE_2] & 0x0400U) {
    15131594      ai->ports[p].devs[d].lba48   = 1;
     
    15151596  }
    15161597
    1517   dprintf("found device %d.%d.%d: removable = %d, dev_type = %d, atapi = %d\n",
    1518           ad_no(ai), p, d,
     1598  dprintf("found device %d.%d.%d: removable = %d, dev_type = %d, atapi = %d, "
     1599          "ncq_max = %d\n", ad_no(ai), p, d,
    15191600          ai->ports[p].devs[d].removable,
    15201601          ai->ports[p].devs[d].dev_type,
    1521           ai->ports[p].devs[d].atapi);
     1602          ai->ports[p].devs[d].atapi,
     1603          ai->ports[p].devs[d].ncq_max);
    15221604
    15231605  /* add device to resource manager; we don't really care about errors here */
Note: See TracChangeset for help on using the changeset viewer.