summaryrefslogtreecommitdiff
path: root/sys/dev/ahci
diff options
context:
space:
mode:
authorAlexander Motin <mav@FreeBSD.org>2009-12-02 10:32:34 +0000
committerAlexander Motin <mav@FreeBSD.org>2009-12-02 10:32:34 +0000
commit03b5c37446e98d28424d501baf5fdca99e4d3f62 (patch)
treeb59006fe49c8b5cac1c0e186eb5ed08a12272834 /sys/dev/ahci
parent7e7ac267c9d95820990dbf395438521bbed8ba2a (diff)
Notes
Diffstat (limited to 'sys/dev/ahci')
-rw-r--r--sys/dev/ahci/ahci.c112
-rw-r--r--sys/dev/ahci/ahci.h12
2 files changed, 87 insertions, 37 deletions
diff --git a/sys/dev/ahci/ahci.c b/sys/dev/ahci/ahci.c
index 459aa64a4654..3f6c9fcdd886 100644
--- a/sys/dev/ahci/ahci.c
+++ b/sys/dev/ahci/ahci.c
@@ -72,7 +72,7 @@ static void ahci_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int er
static void ahci_execute_transaction(struct ahci_slot *slot);
static void ahci_timeout(struct ahci_slot *slot);
static void ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et);
-static int ahci_setup_fis(struct ahci_cmd_tab *ctp, union ccb *ccb, int tag);
+static int ahci_setup_fis(device_t dev, struct ahci_cmd_tab *ctp, union ccb *ccb, int tag);
static void ahci_dmainit(device_t dev);
static void ahci_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
static void ahci_dmafini(device_t dev);
@@ -776,7 +776,7 @@ ahci_ch_attach(device_t dev)
struct ahci_controller *ctlr = device_get_softc(device_get_parent(dev));
struct ahci_channel *ch = device_get_softc(dev);
struct cam_devq *devq;
- int rid, error;
+ int rid, error, i, sata_rev = 0;
ch->dev = dev;
ch->unit = (intptr_t)device_get_ivars(dev);
@@ -795,9 +795,16 @@ ahci_ch_attach(device_t dev)
pci_get_subvendor(ctlr->dev) == 0x1043 &&
pci_get_subdevice(ctlr->dev) == 0x81e4 &&
ch->unit == 0)
- ch->sata_rev = 1;
+ sata_rev = 1;
resource_int_value(device_get_name(dev),
- device_get_unit(dev), "sata_rev", &ch->sata_rev);
+ device_get_unit(dev), "sata_rev", &sata_rev);
+ for (i = 0; i < 16; i++) {
+ ch->user[i].revision = sata_rev;
+ ch->user[i].mode = 0;
+ ch->user[i].bytecount = 8192;
+ ch->user[i].tags = ch->numslots;
+ ch->curr[i] = ch->user[i];
+ }
rid = ch->unit;
if (!(ch->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&rid, RF_ACTIVE)))
@@ -1275,6 +1282,10 @@ ahci_check_collision(device_t dev, union ccb *ccb)
if (ch->numtslots != 0 &&
ch->taggedtarget != ccb->ccb_h.target_id)
return (1);
+ /* Tagged command while we have no supported tag free. */
+ if (((~ch->oslots) & (0xffffffff >> (32 -
+ ch->curr[ccb->ccb_h.target_id].tags))) == 0)
+ return (1);
} else {
/* Untagged command while tagged are active. */
if (ch->numrslots != 0 && ch->numtslots != 0)
@@ -1298,15 +1309,21 @@ ahci_begin_transaction(device_t dev, union ccb *ccb)
{
struct ahci_channel *ch = device_get_softc(dev);
struct ahci_slot *slot;
- int tag;
+ int tag, tags;
/* Choose empty slot. */
+ tags = ch->numslots;
+ if ((ccb->ccb_h.func_code == XPT_ATA_IO) &&
+ (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA))
+ tags = ch->curr[ccb->ccb_h.target_id].tags;
tag = ch->lastslot;
- while (ch->slot[tag].state != AHCI_SLOT_EMPTY) {
- if (++tag >= ch->numslots)
+ while (1) {
+ if (tag >= tags)
tag = 0;
- KASSERT(tag != ch->lastslot, ("ahci: ALL SLOTS BUSY!"));
- }
+ if (ch->slot[tag].state == AHCI_SLOT_EMPTY)
+ break;
+ tag++;
+ };
ch->lastslot = tag;
/* Occupy chosen slot. */
slot = &ch->slot[tag];
@@ -1315,6 +1332,7 @@ ahci_begin_transaction(device_t dev, union ccb *ccb)
if (ch->numrslots == 0 && ch->pm_level > 3)
callout_stop(&ch->pm_timer);
/* Update channel stats. */
+ ch->oslots |= (1 << slot->slot);
ch->numrslots++;
if ((ccb->ccb_h.func_code == XPT_ATA_IO) &&
(ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) {
@@ -1392,7 +1410,7 @@ ahci_execute_transaction(struct ahci_slot *slot)
ctp = (struct ahci_cmd_tab *)
(ch->dma.work + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot));
/* Setup the FIS for this request */
- if (!(fis_size = ahci_setup_fis(ctp, ccb, slot->slot))) {
+ if (!(fis_size = ahci_setup_fis(dev, ctp, ccb, slot->slot))) {
device_printf(ch->dev, "Setting up SATA FIS failed\n");
ahci_end_transaction(slot, AHCI_ERR_INVALID);
return;
@@ -1635,6 +1653,7 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
ccb->ccb_h.status |= CAM_REQ_CMP_ERR;
}
/* Free slot. */
+ ch->oslots &= ~(1 << slot->slot);
ch->rslots &= ~(1 << slot->slot);
ch->aslots &= ~(1 << slot->slot);
slot->state = AHCI_SLOT_EMPTY;
@@ -1664,7 +1683,7 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
} else
xpt_done(ccb);
/* Unfreeze frozen command. */
- if (ch->frozen && ch->numrslots == 0) {
+ if (ch->frozen && !ahci_check_collision(dev, ch->frozen)) {
union ccb *fccb = ch->frozen;
ch->frozen = NULL;
ahci_begin_transaction(dev, fccb);
@@ -1964,8 +1983,9 @@ ahci_reset(device_t dev)
}
static int
-ahci_setup_fis(struct ahci_cmd_tab *ctp, union ccb *ccb, int tag)
+ahci_setup_fis(device_t dev, struct ahci_cmd_tab *ctp, union ccb *ccb, int tag)
{
+ struct ahci_channel *ch = device_get_softc(dev);
u_int8_t *fis = &ctp->cfis[0];
bzero(ctp->cfis, 64);
@@ -1974,7 +1994,8 @@ ahci_setup_fis(struct ahci_cmd_tab *ctp, union ccb *ccb, int tag)
if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
fis[1] |= 0x80;
fis[2] = ATA_PACKET_CMD;
- if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE &&
+ ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA)
fis[3] = ATA_F_DMA;
else {
fis[5] = ccb->csio.dxfer_len;
@@ -2054,6 +2075,7 @@ static int
ahci_sata_phy_reset(device_t dev, int quick)
{
struct ahci_channel *ch = device_get_softc(dev);
+ int sata_rev;
uint32_t val;
if (quick) {
@@ -2064,11 +2086,12 @@ ahci_sata_phy_reset(device_t dev, int quick)
if (bootverbose)
device_printf(dev, "hardware reset ...\n");
- if (ch->sata_rev == 1)
+ sata_rev = ch->user[ch->pm_present ? 15 : 0].revision;
+ if (sata_rev == 1)
val = ATA_SC_SPD_SPEED_GEN1;
- else if (ch->sata_rev == 2)
+ else if (sata_rev == 2)
val = ATA_SC_SPD_SPEED_GEN2;
- else if (ch->sata_rev == 3)
+ else if (sata_rev == 3)
val = ATA_SC_SPD_SPEED_GEN3;
else
val = 0;
@@ -2125,10 +2148,22 @@ ahciaction(struct cam_sim *sim, union ccb *ccb)
case XPT_SET_TRAN_SETTINGS:
{
struct ccb_trans_settings *cts = &ccb->cts;
+ struct ahci_device *d;
- if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) {
+ if (cts->type == CTS_TYPE_CURRENT_SETTINGS)
+ d = &ch->curr[ccb->ccb_h.target_id];
+ else
+ d = &ch->user[ccb->ccb_h.target_id];
+ if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION)
+ d->revision = cts->xport_specific.sata.revision;
+ if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE)
+ d->mode = cts->xport_specific.sata.mode;
+ if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT)
+ d->bytecount = min(8192, cts->xport_specific.sata.bytecount);
+ if (cts->xport_specific.sata.valid & CTS_SATA_VALID_TAGS)
+ d->tags = min(ch->numslots, cts->xport_specific.sata.tags);
+ if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM)
ch->pm_present = cts->xport_specific.sata.pm_present;
- }
ccb->ccb_h.status = CAM_REQ_CMP;
xpt_done(ccb);
break;
@@ -2137,36 +2172,41 @@ ahciaction(struct cam_sim *sim, union ccb *ccb)
/* Get default/user set transfer settings for the target */
{
struct ccb_trans_settings *cts = &ccb->cts;
+ struct ahci_device *d;
uint32_t status;
+ if (cts->type == CTS_TYPE_CURRENT_SETTINGS)
+ d = &ch->curr[ccb->ccb_h.target_id];
+ else
+ d = &ch->user[ccb->ccb_h.target_id];
cts->protocol = PROTO_ATA;
cts->protocol_version = PROTO_VERSION_UNSPECIFIED;
cts->transport = XPORT_SATA;
cts->transport_version = XPORT_VERSION_UNSPECIFIED;
cts->proto_specific.valid = 0;
cts->xport_specific.sata.valid = 0;
- if (cts->type == CTS_TYPE_CURRENT_SETTINGS)
+ if (cts->type == CTS_TYPE_CURRENT_SETTINGS &&
+ (ccb->ccb_h.target_id == 15 ||
+ (ccb->ccb_h.target_id == 0 && !ch->pm_present))) {
status = ATA_INL(ch->r_mem, AHCI_P_SSTS) & ATA_SS_SPD_MASK;
- else
- status = ATA_INL(ch->r_mem, AHCI_P_SCTL) & ATA_SC_SPD_MASK;
- if (status & ATA_SS_SPD_GEN3) {
- cts->xport_specific.sata.bitrate = 600000;
- cts->xport_specific.sata.valid |= CTS_SATA_VALID_SPEED;
- } else if (status & ATA_SS_SPD_GEN2) {
- cts->xport_specific.sata.bitrate = 300000;
- cts->xport_specific.sata.valid |= CTS_SATA_VALID_SPEED;
- } else if (status & ATA_SS_SPD_GEN1) {
- cts->xport_specific.sata.bitrate = 150000;
- cts->xport_specific.sata.valid |= CTS_SATA_VALID_SPEED;
- }
- if (cts->type == CTS_TYPE_CURRENT_SETTINGS) {
- cts->xport_specific.sata.pm_present =
- (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_PMA) ?
- 1 : 0;
+ if (status & 0x0f0) {
+ cts->xport_specific.sata.revision =
+ (status & 0x0f0) >> 4;
+ cts->xport_specific.sata.valid |=
+ CTS_SATA_VALID_REVISION;
+ }
} else {
- cts->xport_specific.sata.pm_present = ch->pm_present;
+ cts->xport_specific.sata.revision = d->revision;
+ cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION;
}
+ cts->xport_specific.sata.mode = d->mode;
+ cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE;
+ cts->xport_specific.sata.bytecount = d->bytecount;
+ cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT;
+ cts->xport_specific.sata.pm_present = ch->pm_present;
cts->xport_specific.sata.valid |= CTS_SATA_VALID_PM;
+ cts->xport_specific.sata.tags = d->tags;
+ cts->xport_specific.sata.valid |= CTS_SATA_VALID_TAGS;
ccb->ccb_h.status = CAM_REQ_CMP;
xpt_done(ccb);
break;
diff --git a/sys/dev/ahci/ahci.h b/sys/dev/ahci/ahci.h
index cda907812dfe..e11f84f489d3 100644
--- a/sys/dev/ahci/ahci.h
+++ b/sys/dev/ahci/ahci.h
@@ -340,6 +340,13 @@ struct ahci_slot {
struct callout timeout; /* Execution timeout */
};
+struct ahci_device {
+ int revision;
+ int mode;
+ u_int bytecount;
+ u_int tags;
+};
+
/* structure describing an ATA channel */
struct ahci_channel {
device_t dev; /* Device handle */
@@ -355,13 +362,13 @@ struct ahci_channel {
int quirks;
int numslots; /* Number of present slots */
int pm_level; /* power management level */
- int sata_rev; /* Maximum allowed SATA generation */
struct ahci_slot slot[AHCI_MAX_SLOTS];
union ccb *hold[AHCI_MAX_SLOTS];
struct mtx mtx; /* state lock */
int devices; /* What is present */
int pm_present; /* PM presence reported */
+ uint32_t oslots; /* Occupied slots */
uint32_t rslots; /* Running slots */
uint32_t aslots; /* Slots with atomic commands */
int numrslots; /* Number of running slots */
@@ -372,6 +379,9 @@ struct ahci_channel {
int taggedtarget; /* Last tagged target */
union ccb *frozen; /* Frozen command */
struct callout pm_timer; /* Power management events */
+
+ struct ahci_device user[16]; /* User-specified settings */
+ struct ahci_device curr[16]; /* Current settings */
};
/* structure describing a AHCI controller */