#include "ahci.h"
static int ahci_vt8251_attach(device_t);
static int ahci_ati_sb600_attach(device_t);
static int ahci_nvidia_mcp_attach(device_t);
static int ahci_pci_attach(device_t);
static int ahci_pci_detach(device_t);
static int ahci_samsung_apple_attach(device_t dev);
static const struct ahci_device ahci_devices[] = {
{ PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8251_SATA,
ahci_vt8251_attach, ahci_pci_detach, "ViaTech-VT8251-SATA" },
{ PCI_VENDOR_SAMSUNG, PCI_PRODUCT_SAMSUNG_APPLE_AHCI,
ahci_samsung_apple_attach, ahci_pci_detach, "Samsung-Apple-PCIe-SSD" },
{ PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB600_SATA,
ahci_ati_sb600_attach, ahci_pci_detach, "ATI-SB600-SATA" },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_2,
ahci_nvidia_mcp_attach, ahci_pci_detach, "NVidia-MCP65-SATA" },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP67_AHCI_1,
ahci_nvidia_mcp_attach, ahci_pci_detach, "NVidia-MCP67-SATA" },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP77_AHCI_5,
ahci_nvidia_mcp_attach, ahci_pci_detach, "NVidia-MCP77-SATA" },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP79_AHCI_1,
ahci_nvidia_mcp_attach, ahci_pci_detach, "NVidia-MCP79-SATA" },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP79_AHCI_9,
ahci_nvidia_mcp_attach, ahci_pci_detach, "NVidia-MCP79-SATA" },
{ 0, 0,
ahci_pci_attach, ahci_pci_detach, "AHCI-PCI-SATA" }
};
struct ahci_pciid {
uint16_t ahci_vid;
uint16_t ahci_did;
int ahci_rev;
};
static const struct ahci_pciid ahci_msi_blacklist[] = {
{ PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB600_SATA, -1 },
{ PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB700_AHCI, -1 },
{ PCI_VENDOR_MARVELL, PCI_PRODUCT_MARVELL_88SE6121, -1 },
{ PCI_VENDOR_MARVELL, PCI_PRODUCT_MARVELL_88SE6145, -1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_1, 0xa1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_2, 0xa1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_3, 0xa1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_4, 0xa1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_5, 0xa1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_6, 0xa1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_7, 0xa1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_8, 0xa1 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_1, 0xa2 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_2, 0xa2 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_3, 0xa2 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_4, 0xa2 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_5, 0xa2 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_6, 0xa2 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_7, 0xa2 },
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_AHCI_8, 0xa2 },
{ PCI_VENDOR_SAMSUNG, PCI_PRODUCT_SAMSUNG_APPLE_AHCI, -1 }
};
static int ahci_msi_enable = 1;
int ahci_synchronous_boot = 1;
TUNABLE_INT("hw.ahci.msi.enable", &ahci_msi_enable);
TUNABLE_INT("hw.ahci.synchronous_boot", &ahci_synchronous_boot);
const struct ahci_device *
ahci_lookup_device(device_t dev)
{
const struct ahci_device *ad;
u_int16_t vendor = pci_get_vendor(dev);
u_int16_t product = pci_get_device(dev);
u_int8_t class = pci_get_class(dev);
u_int8_t subclass = pci_get_subclass(dev);
u_int8_t progif = pci_read_config(dev, PCIR_PROGIF, 1);
int is_ahci;
if (class == PCIC_STORAGE && subclass == PCIS_STORAGE_SATA &&
progif == PCIP_STORAGE_SATA_AHCI_1_0) {
is_ahci = 1;
} else {
is_ahci = 0;
}
for (ad = &ahci_devices[0]; ad->ad_vendor; ++ad) {
if (ad->ad_vendor == vendor && ad->ad_product == product)
return (ad);
}
if (is_ahci == 0)
ad = NULL;
return (ad);
}
static int
ahci_vt8251_attach(device_t dev)
{
struct ahci_softc *sc = device_get_softc(dev);
sc->sc_flags |= AHCI_F_NO_NCQ;
return (ahci_pci_attach(dev));
}
static int
ahci_ati_sb600_attach(device_t dev)
{
struct ahci_softc *sc = device_get_softc(dev);
pcireg_t magic;
u_int8_t subclass = pci_get_subclass(dev);
u_int8_t revid;
if (subclass == PCIS_STORAGE_IDE) {
revid = pci_read_config(dev, PCIR_REVID, 1);
magic = pci_read_config(dev, AHCI_PCI_ATI_SB600_MAGIC, 4);
pci_write_config(dev, AHCI_PCI_ATI_SB600_MAGIC,
magic | AHCI_PCI_ATI_SB600_LOCKED, 4);
pci_write_config(dev, PCIR_REVID,
(PCIC_STORAGE << 24) |
(PCIS_STORAGE_SATA << 16) |
(PCIP_STORAGE_SATA_AHCI_1_0 << 8) |
revid, 4);
pci_write_config(dev, AHCI_PCI_ATI_SB600_MAGIC, magic, 4);
}
sc->sc_flags |= AHCI_F_IGN_FR;
return (ahci_pci_attach(dev));
}
static int
ahci_nvidia_mcp_attach(device_t dev)
{
struct ahci_softc *sc = device_get_softc(dev);
sc->sc_flags |= AHCI_F_IGN_FR;
return (ahci_pci_attach(dev));
}
static int
ahci_samsung_apple_attach(device_t dev)
{
struct ahci_softc *sc = device_get_softc(dev);
sc->sc_flags |= AHCI_F_IGN_FR | AHCI_F_IGN_CR | AHCI_F_FAST_COMRESET;
return (ahci_pci_attach(dev));
}
static int
ahci_pci_attach(device_t dev)
{
struct ahci_softc *sc = device_get_softc(dev);
struct ahci_port *ap;
const char *gen;
uint16_t vid, did;
u_int32_t pi, reg;
u_int32_t cap, cap2;
u_int32_t chip;
u_int irq_flags;
bus_addr_t addr;
int i, error, msi_enable, rev, fbs;
char revbuf[32];
if (pci_read_config(dev, PCIR_COMMAND, 2) & 0x0400) {
device_printf(dev, "BIOS disabled PCI interrupt, "
"re-enabling\n");
pci_write_config(dev, PCIR_COMMAND,
pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400, 2);
}
chip = ((uint16_t)pci_get_device(dev) << 16) |
(uint16_t)pci_get_vendor(dev);
switch(chip) {
case 0x91721b4b:
device_printf(dev,
"Enable 88SE9172 workarounds for broken chip\n");
sc->sc_flags |= AHCI_F_IGN_FR;
sc->sc_flags |= AHCI_F_IGN_CR;
break;
case 0x92151b4b:
device_printf(dev,
"Enable 88SE9215 workarounds for broken chip\n");
sc->sc_flags |= AHCI_F_IGN_FR;
sc->sc_flags |= AHCI_F_IGN_CR;
break;
case 0x92301b4b:
device_printf(dev,
"Enable 88SE9230 workarounds for broken chip\n");
sc->sc_flags |= AHCI_F_CYCLE_FR;
break;
case 0x07f410de:
device_printf(dev,
"Enable nForce 630i workarounds for broken chip\n");
sc->sc_flags |= AHCI_F_IGN_FR;
sc->sc_flags |= AHCI_F_IGN_CR;
break;
}
sc->sc_dev = dev;
msi_enable = ahci_msi_enable;
vid = pci_get_vendor(dev);
did = pci_get_device(dev);
rev = pci_get_revid(dev);
for (i = 0; i < NELEM(ahci_msi_blacklist); ++i) {
const struct ahci_pciid *id = &ahci_msi_blacklist[i];
if (vid == id->ahci_vid && did == id->ahci_did) {
if (id->ahci_rev < 0 || id->ahci_rev == rev) {
msi_enable = 0;
break;
}
}
}
sc->sc_irq_type = pci_alloc_1intr(dev, msi_enable,
&sc->sc_rid_irq, &irq_flags);
sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_rid_irq,
irq_flags);
if (sc->sc_irq == NULL) {
device_printf(dev, "unable to map interrupt\n");
ahci_pci_detach(dev);
return (ENXIO);
}
sc->sc_rid_regs = PCIR_BAR(5);
sc->sc_regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&sc->sc_rid_regs, RF_ACTIVE);
if (sc->sc_regs == NULL) {
device_printf(dev, "unable to map registers\n");
ahci_pci_detach(dev);
return (ENXIO);
}
sc->sc_iot = rman_get_bustag(sc->sc_regs);
sc->sc_ioh = rman_get_bushandle(sc->sc_regs);
error = ahci_init(sc);
if (error) {
ahci_pci_detach(dev);
return (ENXIO);
}
cap = ahci_read(sc, AHCI_REG_CAP);
if (sc->sc_flags & AHCI_F_NO_NCQ)
cap &= ~AHCI_REG_CAP_SNCQ;
if (sc->sc_flags & AHCI_F_FORCE_FBSS)
cap |= AHCI_REG_CAP_FBSS;
if (sc->sc_flags & AHCI_F_FORCE_SCLO)
cap |= AHCI_REG_CAP_SCLO;
sc->sc_cap = cap;
sc->sc_ncmds = AHCI_REG_CAP_NCS(cap);
if (sc->sc_ncmds < 4) {
device_printf(dev, "NCS must probe a value >= 4\n");
ahci_pci_detach(dev);
return (ENXIO);
}
addr = (cap & AHCI_REG_CAP_S64A) ?
BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT;
fbs = (cap & AHCI_REG_CAP_FBSS) ? 16 : 1;
error = 0;
sc->sc_rfis_size = sizeof(struct ahci_rfis) * fbs;
error += bus_dma_tag_create(
NULL,
sc->sc_rfis_size,
PAGE_SIZE,
addr,
BUS_SPACE_MAXADDR,
sc->sc_rfis_size,
1,
sc->sc_rfis_size,
0,
&sc->sc_tag_rfis);
sc->sc_cmdlist_size = sc->sc_ncmds * sizeof(struct ahci_cmd_hdr);
error += bus_dma_tag_create(
NULL,
32,
4096 * 1024,
addr,
BUS_SPACE_MAXADDR,
sc->sc_cmdlist_size,
1,
sc->sc_cmdlist_size,
0,
&sc->sc_tag_cmdh);
error += bus_dma_tag_create(
NULL,
sizeof(struct ahci_cmd_table),
4096 * 1024,
addr,
BUS_SPACE_MAXADDR,
sc->sc_ncmds * sizeof(struct ahci_cmd_table),
1,
sc->sc_ncmds * sizeof(struct ahci_cmd_table),
0,
&sc->sc_tag_cmdt);
error += bus_dma_tag_create(
NULL,
4,
0,
addr,
BUS_SPACE_MAXADDR,
4096 * 1024,
AHCI_MAX_PRDT,
65536,
0,
&sc->sc_tag_data);
if (error) {
device_printf(dev, "unable to create dma tags\n");
ahci_pci_detach(dev);
return (ENXIO);
}
switch (cap & AHCI_REG_CAP_ISS) {
case AHCI_REG_CAP_ISS_G1:
gen = "1 (1.5Gbps)";
break;
case AHCI_REG_CAP_ISS_G2:
gen = "2 (3Gbps)";
break;
case AHCI_REG_CAP_ISS_G3:
gen = "3 (6Gbps)";
break;
default:
gen = "unknown";
break;
}
reg = ahci_read(sc, AHCI_REG_VS);
if (reg & 0x0000FF) {
ksnprintf(revbuf, sizeof(revbuf), "AHCI %d.%d.%d",
(reg >> 16), (uint8_t)(reg >> 8), (uint8_t)reg);
} else {
ksnprintf(revbuf, sizeof(revbuf), "AHCI %d.%d",
(reg >> 16), (uint8_t)(reg >> 8));
}
sc->sc_vers = reg;
if (reg >= AHCI_REG_VS_1_3) {
cap2 = ahci_read(sc, AHCI_REG_CAP2);
device_printf(dev,
"%s cap 0x%pb%i cap2 0x%pb%i, %d ports, "
"%d tags/port, gen %s\n",
revbuf,
AHCI_FMT_CAP, cap,
AHCI_FMT_CAP2, cap2,
AHCI_REG_CAP_NP(cap), sc->sc_ncmds, gen);
} else {
cap2 = 0;
device_printf(dev,
"%s cap 0x%pb%i, %d ports, "
"%d tags/port, gen %s\n",
revbuf,
AHCI_FMT_CAP, cap,
AHCI_REG_CAP_NP(cap), sc->sc_ncmds, gen);
}
sc->sc_cap2 = cap2;
pi = ahci_read(sc, AHCI_REG_PI);
DPRINTF(AHCI_D_VERBOSE, "%s: ports implemented: 0x%08x\n",
device_get_nameunit(sc->sc_dev), pi);
sc->sc_ipm_disable = AHCI_PREG_SCTL_IPM_NOPARTIAL |
AHCI_PREG_SCTL_IPM_NOSLUMBER;
if (sc->sc_cap2 & AHCI_REG_CAP2_SDS)
sc->sc_ipm_disable |= AHCI_PREG_SCTL_IPM_NODEVSLP;
for (i = 0; error == 0 && i < AHCI_MAX_PORTS; i++) {
if ((pi & (1 << i)) == 0) {
continue;
}
error = ahci_port_alloc(sc, i);
}
if (error == 0) {
error = bus_setup_intr(dev, sc->sc_irq, INTR_MPSAFE |
INTR_HIFREQ,
ahci_intr, sc,
&sc->sc_irq_handle, NULL);
}
if (error) {
device_printf(dev, "unable to install interrupt\n");
ahci_pci_detach(dev);
return (ENXIO);
}
for (i = 0; i < AHCI_MAX_PORTS; i++) {
if ((ap = sc->sc_ports[i]) != NULL) {
while (ap->ap_signal & AP_SIGF_THREAD_SYNC)
tsleep(&ap->ap_signal, 0, "ahprb1", hz);
}
}
crit_enter();
ahci_write(sc, AHCI_REG_GHC, AHCI_REG_GHC_AE | AHCI_REG_GHC_IE);
sc->sc_flags |= AHCI_F_INT_GOOD;
crit_exit();
ahci_intr(sc);
for (i = 0; i < AHCI_MAX_PORTS && ahci_synchronous_boot; i++) {
if ((ap = sc->sc_ports[i]) != NULL) {
while (ap->ap_signal & AP_SIGF_INIT)
tsleep(&ap->ap_signal, 0, "ahprb2", hz);
ahci_os_lock_port(ap);
if (ahci_cam_attach(ap) == 0) {
ahci_cam_changed(ap, NULL, -1);
ahci_os_unlock_port(ap);
while ((ap->ap_flags & AP_F_SCAN_COMPLETED) == 0) {
tsleep(&ap->ap_flags, 0, "ahprb3", hz);
}
} else {
ahci_os_unlock_port(ap);
}
}
}
return(0);
}
static int
ahci_pci_detach(device_t dev)
{
struct ahci_softc *sc = device_get_softc(dev);
struct ahci_port *ap;
int i;
sc->sc_flags &= ~AHCI_F_INT_GOOD;
if (sc->sc_regs)
ahci_write(sc, AHCI_REG_GHC, 0);
if (sc->sc_irq_handle) {
bus_teardown_intr(dev, sc->sc_irq, sc->sc_irq_handle);
sc->sc_irq_handle = NULL;
}
for (i = 0; i < AHCI_MAX_PORTS; i++) {
ap = sc->sc_ports[i];
if (ap) {
ahci_cam_detach(ap);
ahci_port_free(sc, i);
}
}
if (sc->sc_irq) {
bus_release_resource(dev, SYS_RES_IRQ,
sc->sc_rid_irq, sc->sc_irq);
sc->sc_irq = NULL;
}
if (sc->sc_irq_type == PCI_INTR_TYPE_MSI)
pci_release_msi(dev);
if (sc->sc_regs) {
bus_release_resource(dev, SYS_RES_MEMORY,
sc->sc_rid_regs, sc->sc_regs);
sc->sc_regs = NULL;
}
if (sc->sc_tag_rfis) {
bus_dma_tag_destroy(sc->sc_tag_rfis);
sc->sc_tag_rfis = NULL;
}
if (sc->sc_tag_cmdh) {
bus_dma_tag_destroy(sc->sc_tag_cmdh);
sc->sc_tag_cmdh = NULL;
}
if (sc->sc_tag_cmdt) {
bus_dma_tag_destroy(sc->sc_tag_cmdt);
sc->sc_tag_cmdt = NULL;
}
if (sc->sc_tag_data) {
bus_dma_tag_destroy(sc->sc_tag_data);
sc->sc_tag_data = NULL;
}
return (0);
}