Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 110256
b: refs/heads/master
c: be77e43
h: refs/heads/master
v: v3
  • Loading branch information
Tejun Heo authored and Jeff Garzik committed Sep 29, 2008
1 parent b3e1369 commit 3b19c37
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 128 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: b1c72916abbdd0a55015c87358536ca0ebaf6735
refs/heads/master: be77e43abb433c2d6f2fc69352289e34dcbf040a
167 changes: 40 additions & 127 deletions trunk/drivers/ata/ata_piix.c
Original file line number Diff line number Diff line change
Expand Up @@ -887,149 +887,48 @@ static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev)
* Serial ATA Index/Data Pair Superset Registers access
*
* Beginning from ICH8, there's a sane way to access SCRs using index
* and data register pair located at BAR5. This creates an
* interesting problem of mapping two SCRs to one port.
*
* Although they have separate SCRs, the master and slave aren't
* independent enough to be treated as separate links - e.g. softreset
* resets both. Also, there's no protocol defined for hard resetting
* singled device sharing the virtual port (no defined way to acquire
* device signature). This is worked around by merging the SCR values
* into one sensible value and requesting follow-up SRST after
* hardreset.
*
* SCR merging is perfomed in nibbles which is the unit contents in
* SCRs are organized. If two values are equal, the value is used.
* When they differ, merge table which lists precedence of possible
* values is consulted and the first match or the last entry when
* nothing matches is used. When there's no merge table for the
* specific nibble, value from the first port is used.
* and data register pair located at BAR5 which means that we have
* separate SCRs for master and slave. This is handled using libata
* slave_link facility.
*/
static const int piix_sidx_map[] = {
[SCR_STATUS] = 0,
[SCR_ERROR] = 2,
[SCR_CONTROL] = 1,
};

static void piix_sidpr_sel(struct ata_device *dev, unsigned int reg)
static void piix_sidpr_sel(struct ata_link *link, unsigned int reg)
{
struct ata_port *ap = dev->link->ap;
struct ata_port *ap = link->ap;
struct piix_host_priv *hpriv = ap->host->private_data;

iowrite32(((ap->port_no * 2 + dev->devno) << 8) | piix_sidx_map[reg],
iowrite32(((ap->port_no * 2 + link->pmp) << 8) | piix_sidx_map[reg],
hpriv->sidpr + PIIX_SIDPR_IDX);
}

static int piix_sidpr_read(struct ata_device *dev, unsigned int reg)
{
struct piix_host_priv *hpriv = dev->link->ap->host->private_data;

piix_sidpr_sel(dev, reg);
return ioread32(hpriv->sidpr + PIIX_SIDPR_DATA);
}

static void piix_sidpr_write(struct ata_device *dev, unsigned int reg, u32 val)
{
struct piix_host_priv *hpriv = dev->link->ap->host->private_data;

piix_sidpr_sel(dev, reg);
iowrite32(val, hpriv->sidpr + PIIX_SIDPR_DATA);
}

static u32 piix_merge_scr(u32 val0, u32 val1, const int * const *merge_tbl)
{
u32 val = 0;
int i, mi;

for (i = 0, mi = 0; i < 32 / 4; i++) {
u8 c0 = (val0 >> (i * 4)) & 0xf;
u8 c1 = (val1 >> (i * 4)) & 0xf;
u8 merged = c0;
const int *cur;

/* if no merge preference, assume the first value */
cur = merge_tbl[mi];
if (!cur)
goto done;
mi++;

/* if two values equal, use it */
if (c0 == c1)
goto done;

/* choose the first match or the last from the merge table */
while (*cur != -1) {
if (c0 == *cur || c1 == *cur)
break;
cur++;
}
if (*cur == -1)
cur--;
merged = *cur;
done:
val |= merged << (i * 4);
}

return val;
}

static int piix_sidpr_scr_read(struct ata_link *link,
unsigned int reg, u32 *val)
{
struct ata_port *ap = link->ap;
const int * const sstatus_merge_tbl[] = {
/* DET */ (const int []){ 1, 3, 0, 4, 3, -1 },
/* SPD */ (const int []){ 2, 1, 0, -1 },
/* IPM */ (const int []){ 6, 2, 1, 0, -1 },
NULL,
};
const int * const scontrol_merge_tbl[] = {
/* DET */ (const int []){ 1, 0, 4, 0, -1 },
/* SPD */ (const int []){ 0, 2, 1, 0, -1 },
/* IPM */ (const int []){ 0, 1, 2, 3, 0, -1 },
NULL,
};
u32 v0, v1;
struct piix_host_priv *hpriv = link->ap->host->private_data;

if (reg >= ARRAY_SIZE(piix_sidx_map))
return -EINVAL;

if (!(ap->flags & ATA_FLAG_SLAVE_POSS)) {
*val = piix_sidpr_read(&ap->link.device[0], reg);
return 0;
}

v0 = piix_sidpr_read(&ap->link.device[0], reg);
v1 = piix_sidpr_read(&ap->link.device[1], reg);

switch (reg) {
case SCR_STATUS:
*val = piix_merge_scr(v0, v1, sstatus_merge_tbl);
break;
case SCR_ERROR:
*val = v0 | v1;
break;
case SCR_CONTROL:
*val = piix_merge_scr(v0, v1, scontrol_merge_tbl);
break;
}

piix_sidpr_sel(link, reg);
*val = ioread32(hpriv->sidpr + PIIX_SIDPR_DATA);
return 0;
}

static int piix_sidpr_scr_write(struct ata_link *link,
unsigned int reg, u32 val)
{
struct ata_port *ap = link->ap;
struct piix_host_priv *hpriv = link->ap->host->private_data;

if (reg >= ARRAY_SIZE(piix_sidx_map))
return -EINVAL;

piix_sidpr_write(&ap->link.device[0], reg, val);

if (ap->flags & ATA_FLAG_SLAVE_POSS)
piix_sidpr_write(&ap->link.device[1], reg, val);

piix_sidpr_sel(link, reg);
iowrite32(val, hpriv->sidpr + PIIX_SIDPR_DATA);
return 0;
}

Expand Down Expand Up @@ -1370,55 +1269,67 @@ static const int *__devinit piix_init_sata_map(struct pci_dev *pdev,
return map;
}

static void __devinit piix_init_sidpr(struct ata_host *host)
static int __devinit piix_init_sidpr(struct ata_host *host)
{
struct pci_dev *pdev = to_pci_dev(host->dev);
struct piix_host_priv *hpriv = host->private_data;
struct ata_device *dev0 = &host->ports[0]->link.device[0];
struct ata_link *link0 = &host->ports[0]->link;
u32 scontrol;
int i;
int i, rc;

/* check for availability */
for (i = 0; i < 4; i++)
if (hpriv->map[i] == IDE)
return;
return 0;

if (!(host->ports[0]->flags & PIIX_FLAG_SIDPR))
return;
return 0;

if (pci_resource_start(pdev, PIIX_SIDPR_BAR) == 0 ||
pci_resource_len(pdev, PIIX_SIDPR_BAR) != PIIX_SIDPR_LEN)
return;
return 0;

if (pcim_iomap_regions(pdev, 1 << PIIX_SIDPR_BAR, DRV_NAME))
return;
return 0;

hpriv->sidpr = pcim_iomap_table(pdev)[PIIX_SIDPR_BAR];

/* SCR access via SIDPR doesn't work on some configurations.
* Give it a test drive by inhibiting power save modes which
* we'll do anyway.
*/
scontrol = piix_sidpr_read(dev0, SCR_CONTROL);
piix_sidpr_scr_read(link0, SCR_CONTROL, &scontrol);

/* if IPM is already 3, SCR access is probably working. Don't
* un-inhibit power save modes as BIOS might have inhibited
* them for a reason.
*/
if ((scontrol & 0xf00) != 0x300) {
scontrol |= 0x300;
piix_sidpr_write(dev0, SCR_CONTROL, scontrol);
scontrol = piix_sidpr_read(dev0, SCR_CONTROL);
piix_sidpr_scr_write(link0, SCR_CONTROL, scontrol);
piix_sidpr_scr_read(link0, SCR_CONTROL, &scontrol);

if ((scontrol & 0xf00) != 0x300) {
dev_printk(KERN_INFO, host->dev, "SCR access via "
"SIDPR is available but doesn't work\n");
return;
return 0;
}
}

host->ports[0]->ops = &piix_sidpr_sata_ops;
host->ports[1]->ops = &piix_sidpr_sata_ops;
/* okay, SCRs available, set ops and ask libata for slave_link */
for (i = 0; i < 2; i++) {
struct ata_port *ap = host->ports[i];

ap->ops = &piix_sidpr_sata_ops;

if (ap->flags & ATA_FLAG_SLAVE_POSS) {
rc = ata_slave_link_init(ap);
if (rc)
return rc;
}
}

return 0;
}

static void piix_iocfg_bit18_quirk(struct pci_dev *pdev)
Expand Down Expand Up @@ -1528,7 +1439,9 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
/* initialize controller */
if (port_flags & ATA_FLAG_SATA) {
piix_init_pcs(host, piix_map_db_table[ent->driver_data]);
piix_init_sidpr(host);
rc = piix_init_sidpr(host);
if (rc)
return rc;
}

/* apply IOCFG bit18 quirk */
Expand Down

0 comments on commit 3b19c37

Please sign in to comment.