Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev
* 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev: ahci: Workaround HW bug for SB600/700 SATA controller PMP support ahci: workarounds for mcp65
This commit is contained in:
@@ -89,6 +89,7 @@ enum {
|
|||||||
board_ahci_sb600 = 3,
|
board_ahci_sb600 = 3,
|
||||||
board_ahci_mv = 4,
|
board_ahci_mv = 4,
|
||||||
board_ahci_sb700 = 5,
|
board_ahci_sb700 = 5,
|
||||||
|
board_ahci_mcp65 = 6,
|
||||||
|
|
||||||
/* global controller registers */
|
/* global controller registers */
|
||||||
HOST_CAP = 0x00, /* host capabilities */
|
HOST_CAP = 0x00, /* host capabilities */
|
||||||
@@ -190,6 +191,7 @@ enum {
|
|||||||
AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */
|
AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */
|
||||||
AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */
|
AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */
|
||||||
AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */
|
AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */
|
||||||
|
AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */
|
||||||
|
|
||||||
/* ap->flags bits */
|
/* ap->flags bits */
|
||||||
|
|
||||||
@@ -253,6 +255,8 @@ static void ahci_pmp_attach(struct ata_port *ap);
|
|||||||
static void ahci_pmp_detach(struct ata_port *ap);
|
static void ahci_pmp_detach(struct ata_port *ap);
|
||||||
static int ahci_softreset(struct ata_link *link, unsigned int *class,
|
static int ahci_softreset(struct ata_link *link, unsigned int *class,
|
||||||
unsigned long deadline);
|
unsigned long deadline);
|
||||||
|
static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
|
||||||
|
unsigned long deadline);
|
||||||
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
|
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
|
||||||
unsigned long deadline);
|
unsigned long deadline);
|
||||||
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
|
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
|
||||||
@@ -329,6 +333,12 @@ static struct ata_port_operations ahci_p5wdh_ops = {
|
|||||||
.hardreset = ahci_p5wdh_hardreset,
|
.hardreset = ahci_p5wdh_hardreset,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct ata_port_operations ahci_sb600_ops = {
|
||||||
|
.inherits = &ahci_ops,
|
||||||
|
.softreset = ahci_sb600_softreset,
|
||||||
|
.pmp_softreset = ahci_sb600_softreset,
|
||||||
|
};
|
||||||
|
|
||||||
#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
|
#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
|
||||||
|
|
||||||
static const struct ata_port_info ahci_port_info[] = {
|
static const struct ata_port_info ahci_port_info[] = {
|
||||||
@@ -359,11 +369,11 @@ static const struct ata_port_info ahci_port_info[] = {
|
|||||||
{
|
{
|
||||||
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL |
|
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL |
|
||||||
AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI |
|
AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI |
|
||||||
AHCI_HFLAG_SECT255 | AHCI_HFLAG_NO_PMP),
|
AHCI_HFLAG_SECT255),
|
||||||
.flags = AHCI_FLAG_COMMON,
|
.flags = AHCI_FLAG_COMMON,
|
||||||
.pio_mask = 0x1f, /* pio0-4 */
|
.pio_mask = 0x1f, /* pio0-4 */
|
||||||
.udma_mask = ATA_UDMA6,
|
.udma_mask = ATA_UDMA6,
|
||||||
.port_ops = &ahci_ops,
|
.port_ops = &ahci_sb600_ops,
|
||||||
},
|
},
|
||||||
/* board_ahci_mv */
|
/* board_ahci_mv */
|
||||||
{
|
{
|
||||||
@@ -377,8 +387,15 @@ static const struct ata_port_info ahci_port_info[] = {
|
|||||||
},
|
},
|
||||||
/* board_ahci_sb700 */
|
/* board_ahci_sb700 */
|
||||||
{
|
{
|
||||||
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL |
|
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL),
|
||||||
AHCI_HFLAG_NO_PMP),
|
.flags = AHCI_FLAG_COMMON,
|
||||||
|
.pio_mask = 0x1f, /* pio0-4 */
|
||||||
|
.udma_mask = ATA_UDMA6,
|
||||||
|
.port_ops = &ahci_sb600_ops,
|
||||||
|
},
|
||||||
|
/* board_ahci_mcp65 */
|
||||||
|
{
|
||||||
|
AHCI_HFLAGS (AHCI_HFLAG_YES_NCQ),
|
||||||
.flags = AHCI_FLAG_COMMON,
|
.flags = AHCI_FLAG_COMMON,
|
||||||
.pio_mask = 0x1f, /* pio0-4 */
|
.pio_mask = 0x1f, /* pio0-4 */
|
||||||
.udma_mask = ATA_UDMA6,
|
.udma_mask = ATA_UDMA6,
|
||||||
@@ -438,14 +455,14 @@ static const struct pci_device_id ahci_pci_tbl[] = {
|
|||||||
{ PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
|
{ PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
|
||||||
|
|
||||||
/* NVIDIA */
|
/* NVIDIA */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x044c), board_ahci }, /* MCP65 */
|
{ PCI_VDEVICE(NVIDIA, 0x044c), board_ahci_mcp65 }, /* MCP65 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x044d), board_ahci }, /* MCP65 */
|
{ PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 }, /* MCP65 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x044e), board_ahci }, /* MCP65 */
|
{ PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 }, /* MCP65 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x044f), board_ahci }, /* MCP65 */
|
{ PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 }, /* MCP65 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x045c), board_ahci }, /* MCP65 */
|
{ PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 }, /* MCP65 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x045d), board_ahci }, /* MCP65 */
|
{ PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 }, /* MCP65 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x045e), board_ahci }, /* MCP65 */
|
{ PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 }, /* MCP65 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x045f), board_ahci }, /* MCP65 */
|
{ PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 }, /* MCP65 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x0550), board_ahci }, /* MCP67 */
|
{ PCI_VDEVICE(NVIDIA, 0x0550), board_ahci }, /* MCP67 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x0551), board_ahci }, /* MCP67 */
|
{ PCI_VDEVICE(NVIDIA, 0x0551), board_ahci }, /* MCP67 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x0552), board_ahci }, /* MCP67 */
|
{ PCI_VDEVICE(NVIDIA, 0x0552), board_ahci }, /* MCP67 */
|
||||||
@@ -624,6 +641,12 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
|
|||||||
cap &= ~HOST_CAP_NCQ;
|
cap &= ~HOST_CAP_NCQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) {
|
||||||
|
dev_printk(KERN_INFO, &pdev->dev,
|
||||||
|
"controller can do NCQ, turning on CAP_NCQ\n");
|
||||||
|
cap |= HOST_CAP_NCQ;
|
||||||
|
}
|
||||||
|
|
||||||
if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) {
|
if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) {
|
||||||
dev_printk(KERN_INFO, &pdev->dev,
|
dev_printk(KERN_INFO, &pdev->dev,
|
||||||
"controller can't do PMP, turning off CAP_PMP\n");
|
"controller can't do PMP, turning off CAP_PMP\n");
|
||||||
@@ -1262,19 +1285,11 @@ static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ahci_check_ready(struct ata_link *link)
|
static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
|
||||||
{
|
int pmp, unsigned long deadline,
|
||||||
void __iomem *port_mmio = ahci_port_base(link->ap);
|
int (*check_ready)(struct ata_link *link))
|
||||||
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
|
|
||||||
|
|
||||||
return ata_check_ready(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ahci_softreset(struct ata_link *link, unsigned int *class,
|
|
||||||
unsigned long deadline)
|
|
||||||
{
|
{
|
||||||
struct ata_port *ap = link->ap;
|
struct ata_port *ap = link->ap;
|
||||||
int pmp = sata_srst_pmp(link);
|
|
||||||
const char *reason = NULL;
|
const char *reason = NULL;
|
||||||
unsigned long now, msecs;
|
unsigned long now, msecs;
|
||||||
struct ata_taskfile tf;
|
struct ata_taskfile tf;
|
||||||
@@ -1312,7 +1327,7 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class,
|
|||||||
ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);
|
ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);
|
||||||
|
|
||||||
/* wait for link to become ready */
|
/* wait for link to become ready */
|
||||||
rc = ata_wait_after_reset(link, deadline, ahci_check_ready);
|
rc = ata_wait_after_reset(link, deadline, check_ready);
|
||||||
/* link occupied, -ENODEV too is an error */
|
/* link occupied, -ENODEV too is an error */
|
||||||
if (rc) {
|
if (rc) {
|
||||||
reason = "device not ready";
|
reason = "device not ready";
|
||||||
@@ -1328,6 +1343,72 @@ static int ahci_softreset(struct ata_link *link, unsigned int *class,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ahci_check_ready(struct ata_link *link)
|
||||||
|
{
|
||||||
|
void __iomem *port_mmio = ahci_port_base(link->ap);
|
||||||
|
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
|
||||||
|
|
||||||
|
return ata_check_ready(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ahci_softreset(struct ata_link *link, unsigned int *class,
|
||||||
|
unsigned long deadline)
|
||||||
|
{
|
||||||
|
int pmp = sata_srst_pmp(link);
|
||||||
|
|
||||||
|
DPRINTK("ENTER\n");
|
||||||
|
|
||||||
|
return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ahci_sb600_check_ready(struct ata_link *link)
|
||||||
|
{
|
||||||
|
void __iomem *port_mmio = ahci_port_base(link->ap);
|
||||||
|
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
|
||||||
|
u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is no need to check TFDATA if BAD PMP is found due to HW bug,
|
||||||
|
* which can save timeout delay.
|
||||||
|
*/
|
||||||
|
if (irq_status & PORT_IRQ_BAD_PMP)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
return ata_check_ready(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
|
||||||
|
unsigned long deadline)
|
||||||
|
{
|
||||||
|
struct ata_port *ap = link->ap;
|
||||||
|
void __iomem *port_mmio = ahci_port_base(ap);
|
||||||
|
int pmp = sata_srst_pmp(link);
|
||||||
|
int rc;
|
||||||
|
u32 irq_sts;
|
||||||
|
|
||||||
|
DPRINTK("ENTER\n");
|
||||||
|
|
||||||
|
rc = ahci_do_softreset(link, class, pmp, deadline,
|
||||||
|
ahci_sb600_check_ready);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Soft reset fails on some ATI chips with IPMS set when PMP
|
||||||
|
* is enabled but SATA HDD/ODD is connected to SATA port,
|
||||||
|
* do soft reset again to port 0.
|
||||||
|
*/
|
||||||
|
if (rc == -EIO) {
|
||||||
|
irq_sts = readl(port_mmio + PORT_IRQ_STAT);
|
||||||
|
if (irq_sts & PORT_IRQ_BAD_PMP) {
|
||||||
|
ata_link_printk(link, KERN_WARNING,
|
||||||
|
"failed due to HW bug, retry pmp=0\n");
|
||||||
|
rc = ahci_do_softreset(link, class, 0, deadline,
|
||||||
|
ahci_check_ready);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
|
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
|
||||||
unsigned long deadline)
|
unsigned long deadline)
|
||||||
{
|
{
|
||||||
@@ -2118,7 +2199,8 @@ static void ahci_p5wdh_workaround(struct ata_host *host)
|
|||||||
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
static int printed_version;
|
static int printed_version;
|
||||||
struct ata_port_info pi = ahci_port_info[ent->driver_data];
|
unsigned int board_id = ent->driver_data;
|
||||||
|
struct ata_port_info pi = ahci_port_info[board_id];
|
||||||
const struct ata_port_info *ppi[] = { &pi, NULL };
|
const struct ata_port_info *ppi[] = { &pi, NULL };
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct ahci_host_priv *hpriv;
|
struct ahci_host_priv *hpriv;
|
||||||
@@ -2167,6 +2249,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
hpriv->flags |= (unsigned long)pi.private_data;
|
hpriv->flags |= (unsigned long)pi.private_data;
|
||||||
|
|
||||||
|
/* MCP65 revision A1 and A2 can't do MSI */
|
||||||
|
if (board_id == board_ahci_mcp65 &&
|
||||||
|
(pdev->revision == 0xa1 || pdev->revision == 0xa2))
|
||||||
|
hpriv->flags |= AHCI_HFLAG_NO_MSI;
|
||||||
|
|
||||||
if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev))
|
if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev))
|
||||||
pci_intx(pdev, 1);
|
pci_intx(pdev, 1);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user