static int ata_ahci_ctlr_reset(device_t dev);
static int ata_ahci_status(device_t dev);
static int ata_ahci_begin_transaction(struct ata_request *request);
static int ata_ahci_end_transaction(struct ata_request *request);
static u_int32_t ata_ahci_softreset(device_t dev, int port);
static void ata_ahci_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
static int ata_ahci_setup_fis(struct ata_ahci_cmd_tab *ctp, struct ata_request *request);
int
ata_ahci_ident(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(dev);
static const struct ata_chip_id id = {0, 0, 0, 0x00, ATA_SA300, "AHCI"};
char buffer[64];
if (pci_get_class(dev) != PCIC_STORAGE ||
pci_get_subclass(dev) != PCIS_STORAGE_SATA)
return ENXIO;
if (pci_read_config(dev, PCIR_PROGIF, 1) != PCIP_STORAGE_SATA_AHCI_1_0)
return ENXIO;
if (bootverbose)
ksnprintf(buffer, sizeof(buffer), "%s (ID=%08x) AHCI controller",
ata_pcivendor2str(dev), pci_get_devid(dev));
else
ksnprintf(buffer, sizeof(buffer), "%s AHCI controller",
ata_pcivendor2str(dev));
device_set_desc_copy(dev, buffer);
ctlr->chip = &id;
ctlr->chipinit = ata_ahci_chipinit;
return 0;
}
static int
ata_ahci_chipinit(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(dev);
int error;
u_int32_t version;
ctlr->r_type2 = SYS_RES_MEMORY;
ctlr->r_rid2 = PCIR_BAR(5);
if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
&ctlr->r_rid2, RF_ACTIVE)))
return ENXIO;
if (!ctlr->r_irq) {
if (ata_setup_interrupt(dev, ata_generic_intr)) {
bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
return ENXIO;
}
}
else
device_printf(dev, "AHCI called from vendor specific driver\n");
if ((error = ata_ahci_ctlr_reset(dev)) != 0) {
bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
return (error);
}
ctlr->channels =
MAX(flsl(ATA_INL(ctlr->r_res2, ATA_AHCI_PI)),
(ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_NPMASK) + 1);
ctlr->reset = ata_ahci_reset;
ctlr->dmainit = ata_ahci_dmainit;
ctlr->allocate = ata_ahci_allocate;
ctlr->setmode = ata_sata_setmode;
pci_write_config(dev, PCIR_COMMAND,
pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400, 2);
version = ATA_INL(ctlr->r_res2, ATA_AHCI_VS);
device_printf(dev,
"AHCI Version %x%x.%x%x controller with %d ports detected\n",
(version >> 24) & 0xff, (version >> 16) & 0xff,
(version >> 8) & 0xff, version & 0xff,
(ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_NPMASK) + 1);
return 0;
}
static int
ata_ahci_ctlr_reset(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(dev);
int timeout;
ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE|ATA_AHCI_GHC_HR);
for (timeout = 1000; timeout > 0; timeout--) {
DELAY(1000);
if ((ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) & ATA_AHCI_GHC_HR) == 0)
break;
}
if (timeout == 0) {
device_printf(dev, "AHCI controller reset failure\n");
return ENXIO;
}
ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, ATA_INL(ctlr->r_res2, ATA_AHCI_IS));
ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) | ATA_AHCI_GHC_IE);
return 0;
}
static int
ata_ahci_allocate(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
struct ata_channel *ch = device_get_softc(dev);
int offset = ch->unit << 7;
ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
ch->r_io[ATA_SSTATUS].offset = ATA_AHCI_P_SSTS + offset;
ch->r_io[ATA_SERROR].res = ctlr->r_res2;
ch->r_io[ATA_SERROR].offset = ATA_AHCI_P_SERR + offset;
ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
ch->r_io[ATA_SCONTROL].offset = ATA_AHCI_P_SCTL + offset;
ch->r_io[ATA_SACTIVE].res = ctlr->r_res2;
ch->r_io[ATA_SACTIVE].offset = ATA_AHCI_P_SACT + offset;
ch->hw.status = ata_ahci_status;
ch->hw.begin_transaction = ata_ahci_begin_transaction;
ch->hw.end_transaction = ata_ahci_end_transaction;
ch->hw.command = NULL;
ch->hw.softreset = ata_ahci_softreset;
return 0;
}
static int
ata_ahci_status(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
struct ata_channel *ch = device_get_softc(dev);
u_int32_t action = ATA_INL(ctlr->r_res2, ATA_AHCI_IS);
int offset = ch->unit << 7;
#define ATA_AHCI_STATBITS \
(ATA_AHCI_P_IX_IF|ATA_AHCI_P_IX_HBD|ATA_AHCI_P_IX_HBF|ATA_AHCI_P_IX_TFE)
if (action & (1 << ch->unit)) {
u_int32_t istatus = ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset);
u_int32_t cstatus = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CI + offset);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset, istatus);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, 1 << ch->unit);
if (istatus & (ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC))
ata_sata_phy_check_events(dev);
if ((istatus & ATA_AHCI_STATBITS) && (cstatus & 1)) {
u_int32_t cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
int timeout = 0;
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
do {
DELAY(1000);
if (timeout++ > 1000) {
device_printf(dev, "stopping AHCI engine failed\n");
break;
}
} while (ATA_INL(ctlr->r_res2,
ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
cmd | (ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
return 1;
}
else
return (!(cstatus & 1));
}
return 0;
}
static int
ata_ahci_begin_transaction(struct ata_request *request)
{
struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
struct ata_channel *ch = device_get_softc(request->parent);
struct ata_device *atadev = device_get_softc(request->dev);
struct ata_ahci_cmd_tab *ctp;
struct ata_ahci_cmd_list *clp;
int offset = ch->unit << 7;
int port = atadev->unit & 0x0f;
int tag = 0, entries = 0;
int fis_size;
ctp = (struct ata_ahci_cmd_tab *)
(ch->dma->work + ATA_AHCI_CT_OFFSET + (ATA_AHCI_CT_SIZE * tag));
if (!(fis_size = ata_ahci_setup_fis(ctp, request))) {
device_printf(request->dev, "setting up SATA FIS failed\n");
request->result = EIO;
return ATA_OP_FINISHED;
}
if (request->flags & (ATA_R_READ | ATA_R_WRITE)) {
if (ch->dma->load(ch->dev, request->data, request->bytecount,
request->flags & ATA_R_READ,
ctp->prd_tab, &entries)) {
device_printf(request->dev, "setting up DMA failed\n");
request->result = EIO;
return ATA_OP_FINISHED;
}
}
clp = (struct ata_ahci_cmd_list *)
(ch->dma->work + ATA_AHCI_CL_OFFSET + (ATA_AHCI_CL_SIZE * tag));
clp->prd_length = entries;
clp->cmd_flags = (request->flags & ATA_R_WRITE ? ATA_AHCI_CMD_WRITE : 0) |
(request->flags & ATA_R_ATAPI ?
(ATA_AHCI_CMD_ATAPI | ATA_AHCI_CMD_PREFETCH) : 0) |
(fis_size / sizeof(u_int32_t)) |
(port << 12);
clp->bytecount = 0;
clp->cmd_table_phys = htole64(ch->dma->work_bus + ATA_AHCI_CT_OFFSET +
(ATA_AHCI_CT_SIZE * tag));
ATA_IDX_OUTL(ch, ATA_SACTIVE, ATA_IDX_INL(ch, ATA_SACTIVE) & (1 << tag));
if (request->flags & ATA_R_ATAPI)
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) |
ATA_AHCI_P_CMD_ATAPI);
else
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) &
~ATA_AHCI_P_CMD_ATAPI);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CI + offset, (1 << tag));
if (!(request->flags & ATA_R_ATAPI)) {
if (request->u.ata.command == ATA_DEVICE_RESET) {
u_int32_t tf_data;
int timeout = 1000000;
do {
DELAY(10);
tf_data = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + (ch->unit<<7));
} while ((tf_data & ATA_S_BUSY) && timeout--);
if (bootverbose)
device_printf(ch->dev, "device_reset timeout=%dus\n",
(1000000-timeout)*10);
request->status = tf_data;
if (request->status & ATA_S_ERROR)
request->error = tf_data >> 8;
return ATA_OP_FINISHED;
}
}
callout_reset(&request->callout, request->timeout * hz,
(timeout_t*)ata_timeout, request);
return ATA_OP_CONTINUES;
}
static int
ata_ahci_end_transaction(struct ata_request *request)
{
struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
struct ata_channel *ch = device_get_softc(request->parent);
struct ata_ahci_cmd_list *clp;
u_int32_t tf_data;
int offset = ch->unit << 7;
int tag = 0;
callout_cancel(&request->callout);
tf_data = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + offset);
request->status = tf_data;
if (request->status & ATA_S_ERROR)
request->error = tf_data >> 8;
clp = (struct ata_ahci_cmd_list *)
(ch->dma->work + ATA_AHCI_CL_OFFSET + (ATA_AHCI_CL_SIZE * tag));
request->donecount = clp->bytecount;
ch->dma->unload(ch->dev);
return ATA_OP_FINISHED;
}
static void
ata_ahci_restart(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
struct ata_channel *ch = device_get_softc(dev);
u_int32_t cmd;
int offset = ch->unit << 7;
int timeout;
cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
timeout = 0;
do {
DELAY(1000);
if (timeout++ > 1000) {
device_printf(dev, "stopping AHCI engine failed\n");
break;
}
}
while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_CLO) {
cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
cmd |= ATA_AHCI_P_CMD_CLO;
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, cmd);
timeout = 0;
do {
DELAY(1000);
if (timeout++ > 1000) {
device_printf(dev, "executing CLO failed\n");
break;
}
}
while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD+offset)&ATA_AHCI_P_CMD_CLO);
}
ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
cmd | (ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
}
static u_int32_t
ata_ahci_softreset(device_t dev, int port __unused)
{
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
struct ata_channel *ch = device_get_softc(dev);
int offset = ch->unit << 7;
int timeout = 0;
uint32_t val;
#ifdef AHCI_PM
#endif
while ((val = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + offset)) &
(ATA_S_BUSY | ATA_S_DRQ)) {
DELAY(1000);
if (timeout++ > 1000) {
device_printf(dev, "port is not ready (timeout 1s) tfd = %08x\n", val);
break;
}
}
if (bootverbose)
device_printf(dev, "ready wait time=%dms\n", timeout);
return ATA_INL(ctlr->r_res2, ATA_AHCI_P_SIG + offset);
}
static void
ata_ahci_reset(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
struct ata_channel *ch = device_get_softc(dev);
u_int64_t work;
u_int32_t cmd, signature;
int offset = ch->unit << 7;
if (bootverbose)
device_printf(dev, "AHCI reset...\n");
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset, 0);
if (!(ATA_INL(ctlr->r_res2, ATA_AHCI_PI) & (1 << ch->unit))) {
device_printf(dev, "port not implemented\n");
return;
}
work = ch->dma->work_bus + ATA_AHCI_CL_OFFSET;
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work & 0xffffffff);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
work = ch->dma->work_bus + ATA_AHCI_FB_OFFSET;
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work & 0xffffffff);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, work >> 32);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset,
(ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF | ATA_AHCI_P_IX_OF |
ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC | ATA_AHCI_P_IX_DP |
ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB | ATA_AHCI_P_IX_DS |
ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
(ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD));
ata_ahci_restart(dev);
if (!ata_sata_phy_reset(dev)) {
if (bootverbose)
device_printf(dev, "AHCI reset done: phy reset found no device\n");
ch->devices = 0;
cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
return;
}
signature = ata_ahci_softreset(dev, 0);
if (bootverbose)
device_printf(ch->dev, "SIGNATURE: %08x\n", signature);
switch (signature >> 16) {
case 0x0000:
ch->devices = ATA_ATA_MASTER;
break;
case 0x9669:
ch->devices = ATA_PORTMULTIPLIER;
device_printf(ch->dev, "Portmultipliers not supported yet\n");
ch->devices = 0;
break;
case 0xeb14:
ch->devices = ATA_ATAPI_MASTER;
break;
default:
if (bootverbose)
device_printf(ch->dev, "Unknown signature, assuming disk device\n");
ch->devices = ATA_ATA_MASTER;
}
if (bootverbose)
device_printf(dev, "AHCI reset done: devices=%08x\n", ch->devices);
}
static void
ata_ahci_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
{
struct ata_dmasetprd_args *args = xsc;
struct ata_ahci_dma_prd *prd = args->dmatab;
int i;
if (!(args->error = error)) {
for (i = 0; i < nsegs; i++) {
prd[i].dba = htole64(segs[i].ds_addr);
prd[i].dbc = htole32((segs[i].ds_len - 1) & ATA_AHCI_PRD_MASK);
}
}
KASSERT(nsegs <= ATA_AHCI_DMA_ENTRIES, ("too many DMA segment entries\n"));
args->nsegs = nsegs;
}
static void
ata_ahci_dmainit(device_t dev)
{
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
struct ata_channel *ch = device_get_softc(dev);
ata_dmainit(dev);
if (ch->dma) {
ch->dma->setprd = ata_ahci_dmasetprd;
ch->dma->max_iosize = (ATA_AHCI_DMA_ENTRIES - 1) * PAGE_SIZE;
if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_64BIT)
ch->dma->max_address = BUS_SPACE_MAXADDR;
}
}
static int
ata_ahci_setup_fis(struct ata_ahci_cmd_tab *ctp, struct ata_request *request)
{
bzero(ctp->cfis, 64);
if (request->flags & ATA_R_ATAPI) {
bzero(ctp->acmd, 32);
bcopy(request->u.atapi.ccb, ctp->acmd, 16);
}
return ata_request2fis_h2d(request, &ctp->cfis[0]);
}