summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata
diff options
context:
space:
mode:
authorsos <sos@FreeBSD.org>2005-05-11 16:10:08 +0000
committersos <sos@FreeBSD.org>2005-05-11 16:10:08 +0000
commiteae1ea62ece9bf50ee32441fbabf913a0c8b6004 (patch)
tree7a431939e3c9973e4513bb07692a1fbd3bb16e48 /sys/dev/ata
parent107f3a74d1aae30f5d860bc59a6adf679a57517a (diff)
downloadFreeBSD-src-eae1ea62ece9bf50ee32441fbabf913a0c8b6004.zip
FreeBSD-src-eae1ea62ece9bf50ee32441fbabf913a0c8b6004.tar.gz
Add support for AHCI compliant ATA devices.
For now just support the Intel ICH6 as that the HW at hand. Sponsored by: pair.com
Diffstat (limited to 'sys/dev/ata')
-rw-r--r--sys/dev/ata/ata-all.h98
-rw-r--r--sys/dev/ata/ata-chipset.c379
2 files changed, 463 insertions, 14 deletions
diff --git a/sys/dev/ata/ata-all.h b/sys/dev/ata/ata-all.h
index 32f0e35..47b9604 100644
--- a/sys/dev/ata/ata-all.h
+++ b/sys/dev/ata/ata-all.h
@@ -163,16 +163,98 @@
#define ATA_SC_IPM_DIS_PARTIAL 0x00000100
#define ATA_SC_IPM_DIS_SLUMBER 0x00000200
+#define ATA_SACTIVE 16
+
+/* SATA AHCI v1.0 register defines */
+#define ATA_AHCI_CAP 0x00
+#define ATA_AHCI_NPMASK 0x1f
+
+#define ATA_AHCI_GHC 0x04
+#define ATA_AHCI_GHC_AE 0x80000000
+#define ATA_AHCI_GHC_IE 0x00000002
+#define ATA_AHCI_GHC_HR 0x80000001
+
+#define ATA_AHCI_IS 0x08
+#define ATA_AHCI_PI 0x0c
+#define ATA_AHCI_VS 0x10
+
+#define ATA_AHCI_OFFSET 0x80
+
+#define ATA_AHCI_P_CLB 0x100
+#define ATA_AHCI_P_CLBU 0x104
+#define ATA_AHCI_P_FB 0x108
+#define ATA_AHCI_P_FBU 0x10c
+#define ATA_AHCI_P_IS 0x110
+#define ATA_AHCI_P_IE 0x114
+#define ATA_AHCI_P_IX_DHR 0x00000001
+#define ATA_AHCI_P_IX_PS 0x00000002
+#define ATA_AHCI_P_IX_DS 0x00000004
+#define ATA_AHCI_P_IX_SDB 0x00000008
+#define ATA_AHCI_P_IX_UF 0x00000010
+#define ATA_AHCI_P_IX_DP 0x00000020
+#define ATA_AHCI_P_IX_PC 0x00000040
+#define ATA_AHCI_P_IX_DI 0x00000080
+
+#define ATA_AHCI_P_IX_PRC 0x00400000
+#define ATA_AHCI_P_IX_IPM 0x00800000
+#define ATA_AHCI_P_IX_OF 0x01000000
+#define ATA_AHCI_P_IX_INF 0x04000000
+#define ATA_AHCI_P_IX_IF 0x08000000
+#define ATA_AHCI_P_IX_HBD 0x10000000
+#define ATA_AHCI_P_IX_HBF 0x20000000
+#define ATA_AHCI_P_IX_TFE 0x40000000
+#define ATA_AHCI_P_IX_CPD 0x80000000
+
+#define ATA_AHCI_P_CMD 0x118
+#define ATA_AHCI_P_CMD_ST 0x00000001
+#define ATA_AHCI_P_CMD_SUD 0x00000002
+#define ATA_AHCI_P_CMD_POD 0x00000004
+#define ATA_AHCI_P_CMD_CLO 0x00000008
+#define ATA_AHCI_P_CMD_FRE 0x00000010
+#define ATA_AHCI_P_CMD_CCS_MASK 0x00001f00
+#define ATA_AHCI_P_CMD_ISS 0x00002000
+#define ATA_AHCI_P_CMD_FR 0x00004000
+#define ATA_AHCI_P_CMD_CR 0x00008000
+#define ATA_AHCI_P_CMD_CPS 0x00010000
+#define ATA_AHCI_P_CMD_PMA 0x00020000
+#define ATA_AHCI_P_CMD_HPCP 0x00040000
+#define ATA_AHCI_P_CMD_ISP 0x00080000
+#define ATA_AHCI_P_CMD_CPD 0x00100000
+#define ATA_AHCI_P_CMD_ATAPI 0x01000000
+#define ATA_AHCI_P_CMD_DLAE 0x02000000
+#define ATA_AHCI_P_CMD_ALPE 0x04000000
+#define ATA_AHCI_P_CMD_ASP 0x08000000
+#define ATA_AHCI_P_CMD_ICC_MASK 0xf0000000
+#define ATA_AHCI_P_CMD_NOOP 0x00000000
+#define ATA_AHCI_P_CMD_ACTIVE 0x10000000
+#define ATA_AHCI_P_CMD_PARTIAL 0x20000000
+#define ATA_AHCI_P_CMD_SLUMPER 0x60000000
+
+#define ATA_AHCI_P_TFD 0x120
+#define ATA_AHCI_P_SIG 0x124
+#define ATA_AHCI_P_SSTS 0x128
+#define ATA_AHCI_P_SCTL 0x12c
+#define ATA_AHCI_P_SERR 0x130
+#define ATA_AHCI_P_SACT 0x134
+#define ATA_AHCI_P_CI 0x138
+
+#define ATA_AHCI_CL_SIZE 32
+#define ATA_AHCI_CL_OFFSET 0
+#define ATA_AHCI_FB_OFFSET 1024
+#define ATA_AHCI_CT_OFFSET 1024+256
+#define ATA_AHCI_CT_SG_OFFSET 128
+#define ATA_AHCI_CT_SIZE 256
+
/* DMA register defines */
#define ATA_DMA_ENTRIES 256
#define ATA_DMA_EOT 0x80000000
-#define ATA_BMCMD_PORT 16
+#define ATA_BMCMD_PORT 17
#define ATA_BMCMD_START_STOP 0x01
#define ATA_BMCMD_WRITE_READ 0x08
-#define ATA_BMDEVSPEC_0 17
-#define ATA_BMSTAT_PORT 18
+#define ATA_BMDEVSPEC_0 18
+#define ATA_BMSTAT_PORT 19
#define ATA_BMSTAT_ACTIVE 0x01
#define ATA_BMSTAT_ERROR 0x02
#define ATA_BMSTAT_INTERRUPT 0x04
@@ -181,12 +263,12 @@
#define ATA_BMSTAT_DMA_SLAVE 0x40
#define ATA_BMSTAT_DMA_SIMPLEX 0x80
-#define ATA_BMDEVSPEC_1 19
-#define ATA_BMDTP_PORT 20
+#define ATA_BMDEVSPEC_1 20
+#define ATA_BMDTP_PORT 21
-#define ATA_IDX_ADDR 21
-#define ATA_IDX_DATA 22
-#define ATA_MAX_RES 23
+#define ATA_IDX_ADDR 22
+#define ATA_IDX_DATA 23
+#define ATA_MAX_RES 24
/* misc defines */
#define ATA_PRIMARY 0x1f0
diff --git a/sys/dev/ata/ata-chipset.c b/sys/dev/ata/ata-chipset.c
index 85f8c35..5042b0b 100644
--- a/sys/dev/ata/ata-chipset.c
+++ b/sys/dev/ata/ata-chipset.c
@@ -56,6 +56,11 @@ __FBSDID("$FreeBSD$");
static int ata_generic_chipinit(device_t);
static void ata_generic_intr(void *);
static void ata_generic_setmode(device_t, int);
+static int ata_ahci_allocate(device_t dev);
+static int ata_ahci_begin_transaction(struct ata_request *);
+static int ata_ahci_end_transaction(struct ata_request *);
+static void ata_ahci_intr(void *);
+static void ata_ahci_reset(device_t);
static int ata_acard_chipinit(device_t);
static void ata_acard_intr(void *);
static void ata_acard_850_setmode(device_t, int);
@@ -243,17 +248,16 @@ ata_sata_connect(struct ata_channel *ch)
else
break;
}
- if (1 | bootverbose)
+ if (bootverbose)
device_printf(ch->dev, "SATA connect ready time=%dms\n", timeout * 10);
if (timeout < 1000) {
if ((ATA_IDX_INB(ch, ATA_CYL_LSB) == ATAPI_MAGIC_LSB) &&
(ATA_IDX_INB(ch, ATA_CYL_MSB) == ATAPI_MAGIC_MSB))
ch->devices = ATA_ATAPI_MASTER;
- else /*if ((ATA_IDX_INB(ch, ATA_COUNT) == 0x01) &&
- (ATA_IDX_INB(ch, ATA_CYL_LSB) == 0x01)) */
+ else
ch->devices = ATA_ATA_MASTER;
}
- if (1 | bootverbose)
+ if (bootverbose)
device_printf(ch->dev, "sata_connect devices=0x%b\n",
ch->devices, "\20\3ATAPI_MASTER\1ATA_MASTER");
return 1;
@@ -319,6 +323,337 @@ ata_sata_phy_event(void *context, int dummy)
/*
+ * AHCI v1.0 compliant SATA chipset support functions
+ */
+struct ata_ahci_dma_prd {
+ u_int64_t dba;
+ u_int32_t reserved;
+ u_int32_t dbc; /* 0 based */
+#define ATA_AHCI_PRD_MASK 0x003fffff /* max 4MB */
+#define ATA_AHCI_PRD_IPC (1<<31)
+} __packed;
+
+struct ata_ahci_cmd_tab {
+ u_int8_t cfis[64];
+ u_int8_t acmd[32];
+ u_int8_t reserved[32];
+ struct ata_ahci_dma_prd prd_tab[16];
+} __packed;
+
+struct ata_ahci_cmd_list {
+ u_int16_t cmd_flags;
+ u_int16_t prd_length; /* PRD entries */
+ u_int32_t bytecount;
+ u_int64_t cmd_table_phys; /* 128byte aligned */
+} __packed;
+
+
+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);
+
+ /* XXX SOS this is a hack to satisfy various legacy cruft */
+ ch->r_io[ATA_CYL_LSB].res = ctlr->r_res2;
+ ch->r_io[ATA_CYL_LSB].offset = ATA_AHCI_P_SIG + 1 + offset;
+ ch->r_io[ATA_CYL_LSB].res = ctlr->r_res2;
+ ch->r_io[ATA_CYL_MSB].offset = ATA_AHCI_P_SIG + 3 + offset;
+ ch->r_io[ATA_STATUS].res = ctlr->r_res2;
+ ch->r_io[ATA_STATUS].offset = ATA_AHCI_P_TFD + offset;
+ ch->r_io[ATA_ALTSTAT].res = ctlr->r_res2;
+ ch->r_io[ATA_ALTSTAT].offset = ATA_AHCI_P_TFD + offset;
+
+ /* set the SATA resources */
+ 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.begin_transaction = ata_ahci_begin_transaction;
+ ch->hw.end_transaction = ata_ahci_end_transaction;
+ ch->hw.command = NULL; /* not used here */
+
+ /* setup the work areas */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset,
+ ch->dma->work_bus + ATA_AHCI_CL_OFFSET);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, 0x00000000);
+
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset,
+ ch->dma->work_bus + ATA_AHCI_FB_OFFSET);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, 0x00000000);
+
+ /* enable wanted port interrupts */
+ 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));
+
+ /* start operations on this channel */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+ (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
+ ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+ return 0;
+}
+
+static int
+ata_ahci_setup_fis(u_int8_t *fis, struct ata_request *request)
+{
+ struct ata_device *atadev = device_get_softc(request->dev);
+ int idx = 0;
+
+ /* XXX SOS add ATAPI commands support later */
+ fis[idx++] = 0x27; /* host to device */
+ fis[idx++] = 0x80; /* command FIS (note PM goes here) */
+ fis[idx++] = ata_modify_if_48bit(request);
+ fis[idx++] = request->u.ata.feature;
+
+ fis[idx++] = request->u.ata.lba;
+ fis[idx++] = request->u.ata.lba >> 8;
+ fis[idx++] = request->u.ata.lba >> 16;
+ fis[idx++] = ATA_D_LBA | atadev->unit;
+
+ fis[idx++] = request->u.ata.lba >> 24;
+ fis[idx++] = request->u.ata.lba >> 32;
+ fis[idx++] = request->u.ata.lba >> 40;
+ fis[idx++] = request->u.ata.feature >> 8;
+
+ fis[idx++] = request->u.ata.count;
+ fis[idx++] = request->u.ata.count >> 8;
+ fis[idx++] = 0x00;
+ fis[idx++] = ATA_A_4BIT;
+
+ fis[idx++] = 0x00;
+ fis[idx++] = 0x00;
+ fis[idx++] = 0x00;
+ fis[idx++] = 0x00;
+ return idx;
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+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(device_get_parent(request->dev));
+ struct ata_ahci_cmd_tab *ctp;
+ struct ata_ahci_cmd_list *clp;
+ int fis_size, entries;
+ int tag = 0;
+
+ /* get a piece of the workspace for this request */
+ ctp = (struct ata_ahci_cmd_tab *)
+ (ch->dma->work + ATA_AHCI_CT_OFFSET + (ATA_AHCI_CT_SIZE * tag));
+
+ /* setup the FIS for this request */ /* XXX SOS ATAPI missing still */
+ if (!(fis_size = ata_ahci_setup_fis(&ctp->cfis[0], request))) {
+ device_printf(request->dev, "setting up SATA FIS failed\n");
+ request->result = EIO;
+ return ATA_OP_FINISHED;
+ }
+
+ /* if request moves data setup and load SG list */
+ 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;
+ }
+ }
+
+ /* setup the command list entry */
+ 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 ? (1<<6) : 0) |
+ (request->flags & ATA_R_ATAPI ? (1<<5) : 0) |
+ (fis_size / sizeof(u_int32_t));
+ clp->bytecount = 0;
+ clp->cmd_table_phys = htole64(ch->dma->work_bus + ATA_AHCI_CT_OFFSET +
+ (ATA_AHCI_CT_SIZE * tag));
+
+ /* clear eventual ACTIVE bit */
+ ATA_IDX_OUTL(ch, ATA_SACTIVE, ATA_IDX_INL(ch, ATA_SACTIVE) & (1 << tag));
+
+ /* issue the command */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CI + (ch->unit << 7), (1 << tag));
+
+ /* start the timeout */
+ callout_reset(&request->callout, request->timeout * hz,
+ (timeout_t*)ata_timeout, request);
+ return ATA_OP_CONTINUES;
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+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(device_get_parent(request->dev));
+ struct ata_ahci_cmd_list *clp;
+ u_int32_t tf_data;
+ int tag = 0;
+
+ /* kill the timeout */
+ callout_stop(&request->callout);
+
+ /* get status */
+ tf_data = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + (ch->unit << 7));
+ request->status = tf_data;
+
+ /* if error status get details */
+ if (request->status & ATA_S_ERROR)
+ request->error = tf_data >> 8;
+
+ /* record how much data we actually moved */
+ clp = (struct ata_ahci_cmd_list *)
+ (ch->dma->work + ATA_AHCI_CL_OFFSET + (ATA_AHCI_CL_SIZE * tag));
+ request->donecount = clp->bytecount;
+
+ /* release SG list etc */
+ ch->dma->unload(ch->dev);
+
+ return ATA_OP_FINISHED;
+}
+
+static void
+ata_ahci_intr(void *data)
+{
+ struct ata_pci_controller *ctlr = data;
+ struct ata_channel *ch;
+ u_int32_t port, status, error, issued;
+ int unit;
+ int tag = 0;
+
+ port = ATA_INL(ctlr->r_res2, ATA_AHCI_IS);
+
+ /* implement this as a toggle instead to balance load XXX */
+ for (unit = 0; unit < ctlr->channels; unit++) {
+ if (port & (1 << unit)) {
+ if ((ch = ctlr->interrupt[unit].argument)) {
+ struct ata_connect_task *tp;
+ int offset = (ch->unit << 7);
+
+ error = ATA_INL(ctlr->r_res2, ATA_AHCI_P_SERR + offset);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_SERR + offset, error);
+ status = ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset, status);
+ issued = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CI + offset);
+
+ /* do we have cold connect surprise */
+ if (status & ATA_AHCI_P_IX_CPD) {
+ printf("ata_ahci_intr status=%08x error=%08x issued=%08x\n",
+ status, error, issued);
+ }
+
+ /* check for and handle connect events */
+ if ((status & ATA_AHCI_P_IX_PC) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ device_printf(ch->dev, "CONNECT requested\n");
+ tp->action = ATA_C_ATTACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* check for and handle disconnect events */
+ if (((status & (ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC)) ==
+ ATA_AHCI_P_IX_PRC) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ device_printf(ch->dev, "DISCONNECT requested\n");
+ tp->action = ATA_C_DETACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* any drive action to take care of ? */
+ if (!(issued & (1<<tag)))
+ ctlr->interrupt[unit].function(ch);
+ }
+ }
+ }
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, port);
+}
+
+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_int32_t cmd;
+ int offset = (ch->unit << 7);
+
+ /* kill off all activity on this channel */
+ 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_CR | ATA_AHCI_P_CMD_FR |
+ ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
+
+ DELAY(500000); /* XXX SOS */
+
+ /* spin up device */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, ATA_AHCI_P_CMD_SUD);
+
+ ata_sata_phy_enable(ch);
+
+ /* clear any interrupts pending on this channel */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
+ ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
+
+ /* start operations on this channel */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+ (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
+ ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+}
+
+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);
+ }
+ }
+ args->nsegs = nsegs;
+}
+
+static void
+ata_ahci_dmainit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_dmainit(dev);
+ if (ch->dma) {
+ /* note start and stop are not used here */
+ ch->dma->setprd = ata_ahci_dmasetprd;
+ ch->dma->max_iosize = 8192 * DEV_BSIZE;
+ }
+}
+
+
+/*
* Acard chipset support functions
*/
int
@@ -1083,10 +1418,42 @@ ata_intel_chipinit(device_t dev)
ctlr->setmode = ata_intel_new_setmode;
}
else {
+ /* if we have BAR(5) as a memory resource we should use AHCI mode */
+ 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))) {
+ if (bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle) ||
+ bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS,
+ ata_ahci_intr, ctlr, &ctlr->handle)) {
+ device_printf(dev, "unable to setup interrupt\n");
+ return ENXIO;
+ }
+
+ /* force all ports active "the legacy way" */
+ pci_write_config(dev, 0x92, pci_read_config(dev, 0x92, 2) | 0x0f,2);
+
+ /* enable AHCI mode */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC, ATA_AHCI_GHC_AE);
+
+ /* get the number of HW channels */
+ ctlr->channels = (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) &
+ ATA_AHCI_NPMASK) + 1;
+
+ /* enable AHCI interrupts */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
+ ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) | ATA_AHCI_GHC_IE);
+
+ ctlr->reset = ata_ahci_reset;
+ ctlr->dmainit = ata_ahci_dmainit;
+ ctlr->allocate = ata_ahci_allocate;
+ }
+ else {
+ ctlr->reset = ata_intel_reset;
+ }
+ ctlr->setmode = ata_sata_setmode;
pci_write_config(dev, PCIR_COMMAND,
pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400, 2);
- ctlr->reset = ata_intel_reset;
- ctlr->setmode = ata_sata_setmode;
}
return 0;
}
OpenPOWER on IntegriCloud