#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/gfp.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/blkdev.h>
NV_MMIO_BAR = 5,
NV_PORTS = 2,
- NV_PIO_MASK = 0x1f,
- NV_MWDMA_MASK = 0x07,
- NV_UDMA_MASK = 0x7f,
+ NV_PIO_MASK = ATA_PIO4,
+ NV_MWDMA_MASK = ATA_MWDMA2,
+ NV_UDMA_MASK = ATA_UDMA6,
NV_PORT0_SCR_REG_OFFSET = 0x00,
NV_PORT1_SCR_REG_OFFSET = 0x40,
static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance);
static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance);
static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance);
-static int nv_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val);
-static int nv_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val);
+static int nv_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
+static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
+static int nv_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline);
static void nv_nf2_freeze(struct ata_port *ap);
static void nv_nf2_thaw(struct ata_port *ap);
static void nv_ck804_freeze(struct ata_port *ap);
static void nv_ck804_thaw(struct ata_port *ap);
-static int nv_hardreset(struct ata_link *link, unsigned int *class,
- unsigned long deadline);
static int nv_adma_slave_config(struct scsi_device *sdev);
static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc);
static void nv_adma_qc_prep(struct ata_queued_cmd *qc);
NFORCE3 = NFORCE2, /* NF2 == NF3 as far as sata_nv is concerned */
CK804,
ADMA,
+ MCP5x,
SWNCQ,
};
{ PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2), CK804 },
{ PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA), CK804 },
{ PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2), CK804 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), SWNCQ },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), SWNCQ },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), SWNCQ },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), SWNCQ },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), MCP5x },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), MCP5x },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), MCP5x },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), MCP5x },
{ PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA), GENERIC },
{ PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2), GENERIC },
{ PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3), GENERIC },
.slave_configure = nv_swncq_slave_config,
};
+/*
+ * NV SATA controllers have various different problems with hardreset
+ * protocol depending on the specific controller and device.
+ *
+ * GENERIC:
+ *
+ * bko11195 reports that link doesn't come online after hardreset on
+ * generic nv's and there have been several other similar reports on
+ * linux-ide.
+ *
+ * bko12351#c23 reports that warmplug on MCP61 doesn't work with
+ * softreset.
+ *
+ * NF2/3:
+ *
+ * bko3352 reports nf2/3 controllers can't determine device signature
+ * reliably after hardreset. The following thread reports detection
+ * failure on cold boot with the standard debouncing timing.
+ *
+ * http://thread.gmane.org/gmane.linux.ide/34098
+ *
+ * bko12176 reports that hardreset fails to bring up the link during
+ * boot on nf2.
+ *
+ * CK804:
+ *
+ * For initial probing after boot and hot plugging, hardreset mostly
+ * works fine on CK804 but curiously, reprobing on the initial port
+ * by rescanning or rmmod/insmod fails to acquire the initial D2H Reg
+ * FIS in somewhat undeterministic way.
+ *
+ * SWNCQ:
+ *
+ * bko12351 reports that when SWNCQ is enabled, for hotplug to work,
+ * hardreset should be used and hardreset can't report proper
+ * signature, which suggests that mcp5x is closer to nf2 as long as
+ * reset quirkiness is concerned.
+ *
+ * bko12703 reports that boot probing fails for intel SSD with
+ * hardreset. Link fails to come online. Softreset works fine.
+ *
+ * The failures are varied but the following patterns seem true for
+ * all flavors.
+ *
+ * - Softreset during boot always works.
+ *
+ * - Hardreset during boot sometimes fails to bring up the link on
+ * certain comibnations and device signature acquisition is
+ * unreliable.
+ *
+ * - Hardreset is often necessary after hotplug.
+ *
+ * So, preferring softreset for boot probing and error handling (as
+ * hardreset might bring down the link) but using hardreset for
+ * post-boot probing should work around the above issues in most
+ * cases. Define nv_hardreset() which only kicks in for post-boot
+ * probing and use it for all variants.
+ */
static struct ata_port_operations nv_generic_ops = {
.inherits = &ata_bmdma_port_ops,
- .hardreset = nv_hardreset,
+ .lost_interrupt = ATA_OP_NULL,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
+ .hardreset = nv_hardreset,
};
static struct ata_port_operations nv_nf2_ops = {
};
static struct ata_port_operations nv_adma_ops = {
- .inherits = &nv_generic_ops,
+ .inherits = &nv_ck804_ops,
.check_atapi_dma = nv_adma_check_atapi_dma,
- .tf_read = nv_adma_tf_read,
+ .sff_tf_read = nv_adma_tf_read,
.qc_defer = ata_std_qc_defer,
.qc_prep = nv_adma_qc_prep,
.qc_issue = nv_adma_qc_issue,
- .irq_clear = nv_adma_irq_clear,
+ .sff_irq_clear = nv_adma_irq_clear,
.freeze = nv_adma_freeze,
.thaw = nv_adma_thaw,
.port_ops = &nv_adma_ops,
.private_data = NV_PI_PRIV(nv_adma_interrupt, &nv_adma_sht),
},
+ /* MCP5x */
+ {
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+ .pio_mask = NV_PIO_MASK,
+ .mwdma_mask = NV_MWDMA_MASK,
+ .udma_mask = NV_UDMA_MASK,
+ .port_ops = &nv_generic_ops,
+ .private_data = NV_PI_PRIV(nv_generic_interrupt, &nv_sht),
+ },
/* SWNCQ */
{
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
static int adma_enabled;
static int swncq_enabled = 1;
+static int msi_enabled;
static void nv_adma_register_mode(struct ata_port *ap)
{
}
blk_queue_segment_boundary(sdev->request_queue, segment_boundary);
- blk_queue_max_hw_segments(sdev->request_queue, sg_tablesize);
+ blk_queue_max_segments(sdev->request_queue, sg_tablesize);
ata_port_printk(ap, KERN_INFO,
"DMA mask 0x%llX, segment boundary 0x%lX, hw segs %hu\n",
(unsigned long long)*ap->host->dev->dma_mask,
else
// No request pending? Clear interrupt status
// anyway, in case there's one pending.
- ap->ops->check_status(ap);
+ ap->ops->sff_check_status(ap);
}
}
return ret;
}
-static int nv_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val)
+static int nv_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
{
if (sc_reg > SCR_CONTROL)
return -EINVAL;
- *val = ioread32(ap->ioaddr.scr_addr + (sc_reg * 4));
+ *val = ioread32(link->ap->ioaddr.scr_addr + (sc_reg * 4));
return 0;
}
-static int nv_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val)
+static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
{
if (sc_reg > SCR_CONTROL)
return -EINVAL;
- iowrite32(val, ap->ioaddr.scr_addr + (sc_reg * 4));
+ iowrite32(val, link->ap->ioaddr.scr_addr + (sc_reg * 4));
return 0;
}
+static int nv_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
+{
+ struct ata_eh_context *ehc = &link->eh_context;
+
+ /* Do hardreset iff it's post-boot probing, please read the
+ * comment above port ops for details.
+ */
+ if (!(link->ap->pflags & ATA_PFLAG_LOADING) &&
+ !ata_dev_enabled(link->device))
+ sata_link_hardreset(link, sata_deb_timing_hotplug, deadline,
+ NULL, NULL);
+ else {
+ const unsigned long *timing = sata_ehc_deb_timing(ehc);
+ int rc;
+
+ if (!(ehc->i.flags & ATA_EHI_QUIET))
+ ata_link_printk(link, KERN_INFO, "nv: skipping "
+ "hardreset on occupied port\n");
+
+ /* make sure the link is online */
+ rc = sata_link_resume(link, timing, deadline);
+ /* whine about phy resume failure but proceed */
+ if (rc && rc != -EOPNOTSUPP)
+ ata_link_printk(link, KERN_WARNING, "failed to resume "
+ "link (errno=%d)\n", rc);
+ }
+
+ /* device signature acquisition is unreliable */
+ return -EAGAIN;
+}
+
static void nv_nf2_freeze(struct ata_port *ap)
{
void __iomem *scr_addr = ap->host->ports[0]->ioaddr.scr_addr;
ata_sff_thaw(ap);
}
-static int nv_hardreset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
-{
- unsigned int dummy;
-
- /* SATA hardreset fails to retrieve proper device signature on
- * some controllers. Don't classify on hardreset. For more
- * info, see http://bugzilla.kernel.org/show_bug.cgi?id=3352
- */
- return sata_sff_hardreset(link, &dummy, deadline);
-}
-
static void nv_adma_error_handler(struct ata_port *ap)
{
struct nv_adma_port_priv *pp = ap->private_data;
pp->dhfis_bits, pp->dmafis_bits, pp->sdbfis_bits);
ata_port_printk(ap, KERN_ERR, "ATA_REG 0x%X ERR_REG 0x%X\n",
- ap->ops->check_status(ap),
+ ap->ops->sff_check_status(ap),
ioread8(ap->ioaddr.error_addr));
sactive = readl(pp->sactive_block);
}
nv_swncq_pp_reinit(ap);
- ap->ops->irq_clear(ap);
+ ap->ops->sff_irq_clear(ap);
__ata_bmdma_stop(ap);
nv_swncq_irq_clear(ap, 0xffff);
}
ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
if (strncmp(model_num, "Maxtor", 6) == 0) {
- ata_scsi_change_queue_depth(sdev, 1);
+ ata_scsi_change_queue_depth(sdev, 1, SCSI_QDEPTH_DEFAULT);
ata_dev_printk(dev, KERN_NOTICE,
"Disabling SWNCQ mode (depth %x)\n", sdev->queue_depth);
}
pp->dmafis_bits &= ~(1 << qc->tag);
pp->qc_active |= (0x1 << qc->tag);
- ap->ops->tf_load(ap, &qc->tf); /* load tf registers */
- ap->ops->exec_command(ap, &qc->tf);
+ ap->ops->sff_tf_load(ap, &qc->tf); /* load tf registers */
+ ap->ops->sff_exec_command(ap, &qc->tf);
DPRINTK("Issued tag %u\n", qc->tag);
return -EINVAL;
}
- ap->ops->irq_clear(ap);
+ ap->ops->sff_irq_clear(ap);
__ata_bmdma_stop(ap);
sactive = readl(pp->sactive_block);
u8 ata_stat;
int rc = 0;
- ata_stat = ap->ops->check_status(ap);
+ ata_stat = ap->ops->sff_check_status(ap);
nv_swncq_irq_clear(ap, fis);
if (!fis)
return;
if (!pp->qc_active)
return;
- if (ap->ops->scr_read(ap, SCR_ERROR, &serror))
+ if (ap->ops->scr_read(&ap->link, SCR_ERROR, &serror))
return;
- ap->ops->scr_write(ap, SCR_ERROR, serror);
+ ap->ops->scr_write(&ap->link, SCR_ERROR, serror);
if (ata_stat & ATA_ERR) {
ata_ehi_clear_desc(ehi);
if (!(fis & NV_SWNCQ_IRQ_DMASETUP) &&
!(pp->ncq_flags & ncq_saw_dmas)) {
- ata_stat = ap->ops->check_status(ap);
+ ata_stat = ap->ops->sff_check_status(ap);
if (ata_stat & ATA_BUSY)
goto irq_exit;
if (type == CK804 && adma_enabled) {
dev_printk(KERN_NOTICE, &pdev->dev, "Using ADMA mode\n");
type = ADMA;
- }
-
- if (type == SWNCQ) {
- if (swncq_enabled)
- dev_printk(KERN_NOTICE, &pdev->dev,
- "Using SWNCQ mode\n");
- else
- type = GENERIC;
+ } else if (type == MCP5x && swncq_enabled) {
+ dev_printk(KERN_NOTICE, &pdev->dev, "Using SWNCQ mode\n");
+ type = SWNCQ;
}
ppi[0] = &nv_port_info[type];
} else if (type == SWNCQ)
nv_swncq_host_init(host);
+ if (msi_enabled) {
+ dev_printk(KERN_NOTICE, &pdev->dev, "Using MSI\n");
+ pci_enable_msi(pdev);
+ }
+
pci_set_master(pdev);
return ata_host_activate(host, pdev->irq, ipriv->irq_handler,
IRQF_SHARED, ipriv->sht);
module_init(nv_init);
module_exit(nv_exit);
module_param_named(adma, adma_enabled, bool, 0444);
-MODULE_PARM_DESC(adma, "Enable use of ADMA (Default: true)");
+MODULE_PARM_DESC(adma, "Enable use of ADMA (Default: false)");
module_param_named(swncq, swncq_enabled, bool, 0444);
MODULE_PARM_DESC(swncq, "Enable use of SWNCQ (Default: true)");
+module_param_named(msi, msi_enabled, bool, 0444);
+MODULE_PARM_DESC(msi, "Enable use of MSI (Default: false)");