* [PATCH][INCOMPLETE] sata_nv: merge ADMA support
@ 2006-03-17 23:23 Bill Rugolsky Jr.
2006-03-18 0:56 ` Jeff Garzik
0 siblings, 1 reply; 14+ messages in thread
From: Bill Rugolsky Jr. @ 2006-03-17 23:23 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linux-kernel
Jeff,
Here is an incomplete attempt to get the sata_nv ADMA code working.
I made another pass through my merge of the sata_nv driver to fix and
clean up a few things. I've only made the minimal changes necessary
to get it into a testable state; methinks it could do with a lot of
refactoring and cleanup, instead of all the
"if (host->...host_type == ADMA)" tests.
There are several FIXMEs from the original code.
I added an "adma" boolean flag to simplify testing the different code paths.
It boots OK with ADMA enabled on the disk attached to port 0, but under
heavy I/O it will stall for tens of seconds. Moderate I/O to ports 2 and 3
generates some timeouts:
ata3: command 0x25 timeout, stat 0x50
ata4: command 0x25 timeout, stat 0x50
ata3: command 0x25 timeout, stat 0x50
ata3: command 0x35 timeout, stat 0x50
ata3: command 0x25 timeout, stat 0x50
ata3: command 0x35 timeout, stat 0x50
ata3: command 0x35 timeout, stat 0x50
ata3: command 0x35 timeout, stat 0x50
ata3: command 0x35 timeout, stat 0x50
ata3: command 0x25 timeout, stat 0x50
I haven't yet bothered to enable debugging in the driver.
I'm currently running the merged driver with ADMA turned off; it should
have no functional changes.
Regards,
Bill Rugolsky
sata_nv.c | 1119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 1075 insertions(+), 44 deletions(-)
--
--- linux-2.6.16-rc6-git4/drivers/scsi/sata_nv.c~ 2006-03-15 17:19:18.000000000 -0500
+++ linux-2.6.16-rc6-git4/drivers/scsi/sata_nv.c 2006-03-17 18:13:17.000000000 -0500
@@ -29,6 +29,10 @@
* NV-specific details such as register offsets, SATA phy location,
* hotplug info, etc.
*
+ * 0.11-alpha
+ * - Added support for ADMA. Disabled by default. Use the module
+ * option adma=1 to enable it for supported chipsets.
+ *
* 0.10
* - Fixed spurious interrupts issue seen with the Maxtor 6H500F0 500GB
* drive. Also made the check_hotplug() callbacks return whether there
@@ -68,11 +72,14 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
+#include "scsi.h"
#include <scsi/scsi_host.h>
#include <linux/libata.h>
+//#define DEBUG
+
#define DRV_NAME "sata_nv"
-#define DRV_VERSION "0.8"
+#define DRV_VERSION "0.11-alpha"
#define NV_PORTS 2
#define NV_PIO_MASK 0x1f
@@ -121,6 +128,140 @@
// For PCI config register 20
#define NV_MCP_SATA_CFG_20 0x50
#define NV_MCP_SATA_CFG_20_SATA_SPACE_EN 0x04
+#define NV_MCP_SATA_CFG_20_PORT0_EN (1 << 17)
+#define NV_MCP_SATA_CFG_20_PORT1_EN (1 << 16)
+#define NV_MCP_SATA_CFG_20_PORT0_PWB_EN (1 << 14)
+#define NV_MCP_SATA_CFG_20_PORT1_PWB_EN (1 << 12)
+
+//#define NV_ADMA_NCQ
+
+#ifdef NV_ADMA_NCQ
+#define NV_ADMA_CAN_QUEUE ATA_MAX_QUEUE
+#else
+#define NV_ADMA_CAN_QUEUE ATA_DEF_QUEUE
+#endif
+
+#define NV_ADMA_CPB_SZ 128
+#define NV_ADMA_APRD_SZ 16
+#define NV_ADMA_SGTBL_LEN (1024 - NV_ADMA_CPB_SZ) / NV_ADMA_APRD_SZ
+#define NV_ADMA_SGTBL_SZ NV_ADMA_SGTBL_LEN * NV_ADMA_APRD_SZ
+#define NV_ADMA_PORT_PRIV_DMA_SZ NV_ADMA_CAN_QUEUE * (NV_ADMA_CPB_SZ + NV_ADMA_SGTBL_SZ)
+//#define NV_ADMA_MAX_CPBS 32
+
+// BAR5 offset to ADMA general registers
+#define NV_ADMA_GEN 0x400
+#define NV_ADMA_GEN_CTL 0x00
+#define NV_ADMA_NOTIFIER_CLEAR 0x30
+
+#define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & ( 1 << (19 + (12 * (PORT)))))
+
+// BAR5 offset to ADMA ports
+#define NV_ADMA_PORT 0x480
+
+// size of ADMA port register space
+#define NV_ADMA_PORT_SIZE 0x100
+
+// ADMA port registers
+#define NV_ADMA_CTL 0x40
+#define NV_ADMA_CPB_COUNT 0x42
+#define NV_ADMA_NEXT_CPB_IDX 0x43
+#define NV_ADMA_STAT 0x44
+#define NV_ADMA_CPB_BASE_LOW 0x48
+#define NV_ADMA_CPB_BASE_HIGH 0x4C
+#define NV_ADMA_APPEND 0x50
+#define NV_ADMA_NOTIFIER 0x68
+#define NV_ADMA_NOTIFIER_ERROR 0x6C
+
+// NV_ADMA_CTL register bits
+#define NV_ADMA_CTL_HOTPLUG_IEN (1 << 0)
+#define NV_ADMA_CTL_CHANNEL_RESET (1 << 5)
+#define NV_ADMA_CTL_GO (1 << 7)
+#define NV_ADMA_CTL_AIEN (1 << 8)
+#define NV_ADMA_CTL_READ_NON_COHERENT (2 << 11)
+#define NV_ADMA_CTL_WRITE_NON_COHERENT (1 << 12)
+
+// CPB response flag bits
+#define NV_CPB_RESP_DONE (1 << 0)
+#define NV_CPB_RESP_ATA_ERR (1 << 3)
+#define NV_CPB_RESP_CMD_ERR (1 << 4)
+#define NV_CPB_RESP_CPB_ERR (1 << 7)
+
+// CPB control flag bits
+#define NV_CPB_CTL_CPB_VALID (1 << 0)
+#define NV_CPB_CTL_QUEUE (1 << 1)
+#define NV_CPB_CTL_APRD_VALID (1 << 2)
+#define NV_CPB_CTL_IEN (1 << 3)
+#define NV_CPB_CTL_FPDMA (1 << 4)
+
+// APRD flags
+#define NV_APRD_WRITE (1 << 1)
+#define NV_APRD_END (1 << 2)
+#define NV_APRD_CONT (1 << 3)
+
+// NV_ADMA_STAT flags
+#define NV_ADMA_STAT_TIMEOUT (1 << 0)
+#define NV_ADMA_STAT_HOTUNPLUG (1 << 1)
+#define NV_ADMA_STAT_HOTPLUG (1 << 2)
+#define NV_ADMA_STAT_CPBERR (1 << 4)
+#define NV_ADMA_STAT_SERROR (1 << 5)
+#define NV_ADMA_STAT_CMD_COMPLETE (1 << 6)
+#define NV_ADMA_STAT_IDLE (1 << 8)
+#define NV_ADMA_STAT_LEGACY (1 << 9)
+#define NV_ADMA_STAT_STOPPED (1 << 10)
+#define NV_ADMA_STAT_DONE (1 << 12)
+#define NV_ADMA_STAT_ERR (NV_ADMA_STAT_CPBERR | NV_ADMA_STAT_TIMEOUT)
+
+// port flags
+#define NV_ADMA_PORT_REGISTER_MODE (1 << 0)
+
+#ifndef min
+#define min(x,y) ((x) < (y) ? x : y)
+#endif
+
+struct nv_adma_prd {
+ u64 addr;
+ u32 len;
+ u8 flags;
+ u8 packet_len;
+ u16 reserved;
+};
+
+enum nv_adma_regbits {
+ CMDEND = (1 << 15), /* end of command list */
+ WNB = (1 << 14), /* wait-not-BSY */
+ IGN = (1 << 13), /* ignore this entry */
+ CS1n = (1 << (4 + 8)), /* std. PATA signals follow... */
+ DA2 = (1 << (2 + 8)),
+ DA1 = (1 << (1 + 8)),
+ DA0 = (1 << (0 + 8)),
+};
+
+struct nv_adma_cpb {
+ u8 resp_flags; //0
+ u8 reserved1; //1
+ u8 ctl_flags; //2
+ // len is length of taskfile in 64 bit words
+ u8 len; //3
+ u8 tag; //4
+ u8 next_cpb_idx; //5
+ u16 reserved2; //6-7
+ u16 tf[12]; //8-31
+ struct nv_adma_prd aprd[5]; //32-111
+ u64 next_aprd; //112-119
+ u64 reserved3; //120-127
+};
+
+
+struct nv_adma_port_priv {
+ struct nv_adma_cpb *cpb;
+ // u8 cpb_idx;
+ u8 flags;
+ u32 notifier;
+ u32 notifier_error;
+ dma_addr_t cpb_dma;
+ struct nv_adma_prd *aprd;
+ dma_addr_t aprd_dma;
+};
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
static irqreturn_t nv_interrupt (int irq, void *dev_instance,
@@ -128,36 +269,70 @@ static irqreturn_t nv_interrupt (int irq
static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static void nv_host_stop (struct ata_host_set *host_set);
+static int nv_port_start(struct ata_port *ap);
+static void nv_port_stop(struct ata_port *ap);
+static int nv_adma_port_start(struct ata_port *ap);
+static void nv_adma_port_stop(struct ata_port *ap);
+static void nv_irq_clear(struct ata_port *ap);
+static void nv_adma_irq_clear(struct ata_port *ap);
static void nv_enable_hotplug(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug(struct ata_host_set *host_set);
static int nv_check_hotplug(struct ata_host_set *host_set);
static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug_ck804(struct ata_host_set *host_set);
static int nv_check_hotplug_ck804(struct ata_host_set *host_set);
+static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent);
+static void nv_disable_hotplug_adma(struct ata_host_set *host_set);
+static int nv_check_hotplug_adma(struct ata_host_set *host_set);
+static void nv_qc_prep(struct ata_queued_cmd *qc);
+static int nv_qc_issue(struct ata_queued_cmd *qc);
+static int nv_adma_qc_issue(struct ata_queued_cmd *qc);
+static void nv_adma_qc_prep(struct ata_queued_cmd *qc);
+static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb);
+static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb);
+static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, struct scatterlist *sg, int idx, struct nv_adma_prd *aprd);
+static void nv_adma_register_mode(struct ata_port *ap);
+static void nv_adma_mode(struct ata_port *ap);
+static u8 nv_bmdma_status(struct ata_port *ap);
+static u8 nv_adma_bmdma_status(struct ata_port *ap);
+static void nv_bmdma_stop(struct ata_queued_cmd *qc);
+static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc);
+static void nv_eng_timeout(struct ata_port *ap);
+static void nv_adma_eng_timeout(struct ata_port *ap);
+#ifdef DEBUG
+static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb);
+static void nv_adma_dump_aprd(struct nv_adma_prd *aprd);
+static void nv_adma_dump_cpb_tf(u16 tf);
+static void nv_adma_dump_port(struct ata_port *ap);
+static void nv_adma_dump_iomem(void __iomem *m, int len);
+#endif
+
+
+static int adma_enabled;
enum nv_host_type
{
- GENERIC,
- NFORCE2,
- NFORCE3,
- CK804
+ GENERIC = 0x0,
+ NFORCE = 0x1,
+ CK804 = 0x2,
+ ADMA = 0x4
};
static const struct pci_device_id nv_pci_tbl[] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 | ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 | ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE | ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE | ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2,
@@ -193,13 +368,7 @@ static struct nv_host_desc nv_device_tbl
.check_hotplug = NULL,
},
{
- .host_type = NFORCE2,
- .enable_hotplug = nv_enable_hotplug,
- .disable_hotplug= nv_disable_hotplug,
- .check_hotplug = nv_check_hotplug,
- },
- {
- .host_type = NFORCE3,
+ .host_type = NFORCE,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
@@ -209,6 +378,11 @@ static struct nv_host_desc nv_device_tbl
.disable_hotplug= nv_disable_hotplug_ck804,
.check_hotplug = nv_check_hotplug_ck804,
},
+ { .host_type = ADMA,
+ .enable_hotplug = nv_enable_hotplug_adma,
+ .disable_hotplug= nv_disable_hotplug_adma,
+ .check_hotplug = nv_check_hotplug_adma,
+ },
};
struct nv_host
@@ -253,20 +427,187 @@ static const struct ata_port_operations
.phy_reset = sata_phy_reset,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
- .bmdma_stop = ata_bmdma_stop,
- .bmdma_status = ata_bmdma_status,
- .qc_prep = ata_qc_prep,
- .qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+ .bmdma_stop = nv_bmdma_stop,
+ .bmdma_status = nv_bmdma_status,
+ .qc_prep = nv_qc_prep,
+ .qc_issue = nv_qc_issue,
+ .eng_timeout = nv_eng_timeout,
.irq_handler = nv_interrupt,
- .irq_clear = ata_bmdma_irq_clear,
+ .irq_clear = nv_irq_clear,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
- .port_start = ata_port_start,
- .port_stop = ata_port_stop,
+ .port_start = nv_port_start,
+ .port_stop = nv_port_stop,
.host_stop = nv_host_stop,
};
+static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb)
+{
+ unsigned int idx = 0;
+
+ cpb[idx++] = cpu_to_le16((ATA_REG_DEVICE << 8) | tf->device | WNB);
+
+ if ((tf->flags & ATA_TFLAG_LBA48) == 0) {
+ cpb[idx++] = cpu_to_le16(IGN);
+ cpb[idx++] = cpu_to_le16(IGN);
+ cpb[idx++] = cpu_to_le16(IGN);
+ cpb[idx++] = cpu_to_le16(IGN);
+ cpb[idx++] = cpu_to_le16(IGN);
+ }
+ else {
+ cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->hob_feature);
+ cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->hob_nsect);
+ cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->hob_lbal);
+ cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->hob_lbam);
+ cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->hob_lbah);
+ }
+ cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->feature);
+ cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->nsect);
+ cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->lbal);
+ cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->lbam);
+ cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->lbah);
+
+ cpb[idx++] = cpu_to_le16((ATA_REG_CMD << 8) | tf->command | CMDEND);
+
+ return idx;
+}
+
+static inline void __iomem *__nv_adma_ctl_block(void __iomem *mmio,
+ unsigned int port_no)
+{
+ mmio += NV_ADMA_PORT + port_no * NV_ADMA_PORT_SIZE;
+ return mmio;
+}
+
+static inline void __iomem *nv_adma_ctl_block(struct ata_port *ap)
+{
+ return __nv_adma_ctl_block(ap->host_set->mmio_base, ap->port_no);
+}
+
+static inline void __iomem *nv_adma_gen_block(struct ata_port *ap)
+{
+ return (ap->host_set->mmio_base + NV_ADMA_GEN);
+}
+
+static inline void __iomem *nv_adma_notifier_clear_block(struct ata_port *ap)
+{
+ return (nv_adma_gen_block(ap) + NV_ADMA_NOTIFIER_CLEAR + (4 * ap->port_no));
+}
+
+static inline void nv_adma_reset_channel(struct ata_port *ap)
+{
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+ u16 tmp;
+
+ // clear CPB fetch count
+ writew(0, mmio + NV_ADMA_CPB_COUNT);
+
+ // clear GO
+ tmp = readw(mmio + NV_ADMA_CTL);
+ writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
+
+ tmp = readw(mmio + NV_ADMA_CTL);
+ writew(tmp | NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL);
+ udelay(1);
+ writew(tmp & ~NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL);
+}
+
+static inline int nv_adma_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+ struct nv_adma_port_priv *pp = ap->private_data;
+ struct nv_adma_cpb *cpb = &pp->cpb[qc->tag];
+ u16 status;
+ u32 gen_ctl;
+ u16 flags;
+ int have_err = 0;
+ int handled = 0;
+
+ status = readw(mmio + NV_ADMA_STAT);
+
+ // if in ATA register mode, use standard ata interrupt handler
+ if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
+ VPRINTK("in ATA register mode\n");
+ return ata_host_intr(ap, qc);
+ }
+
+ gen_ctl = readl(nv_adma_gen_block(ap) + NV_ADMA_GEN_CTL);
+ if (!NV_ADMA_CHECK_INTR(gen_ctl, ap->port_no)) {
+ return 0;
+ }
+
+ if (!pp->notifier && !pp->notifier_error) {
+ if (status) {
+ VPRINTK("XXX no notifier, but status 0x%x\n", status);
+#ifdef DEBUG
+ nv_adma_dump_port(ap);
+ nv_adma_dump_cpb(cpb);
+#endif
+ } else {
+ return 0;
+ }
+ }
+ if (pp->notifier_error) {
+ have_err = 1;
+ handled = 1;
+ }
+
+ if (status & NV_ADMA_STAT_TIMEOUT) {
+ VPRINTK("timeout, stat = 0x%x\n", status);
+ have_err = 1;
+ handled = 1;
+ }
+ if (status & NV_ADMA_STAT_CPBERR) {
+ VPRINTK("CPB error, stat = 0x%x\n", status);
+ have_err = 1;
+ handled = 1;
+ }
+ if (status & NV_ADMA_STAT_STOPPED) {
+ VPRINTK("ADMA stopped, stat = 0x%x, resp_flags = 0x%x\n", status, cpb->resp_flags);
+ if (!(status & NV_ADMA_STAT_DONE)) {
+ have_err = 1;
+ handled = 1;
+ }
+ }
+ if (status & NV_ADMA_STAT_CMD_COMPLETE) {
+ VPRINTK("ADMA command complete, stat = 0x%x\n", status);
+ }
+ if (status & NV_ADMA_STAT_DONE) {
+ flags = cpb->resp_flags;
+ VPRINTK("CPB done, stat = 0x%x, flags = 0x%x\n", status, flags);
+ handled = 1;
+ if (!(status & NV_ADMA_STAT_IDLE)) {
+ VPRINTK("XXX CPB done, but not idle\n");
+ }
+ if (flags & NV_CPB_RESP_DONE) {
+ VPRINTK("CPB flags done, flags = 0x%x\n", flags);
+ }
+ if (flags & NV_CPB_RESP_ATA_ERR) {
+ VPRINTK("CPB flags ATA err, flags = 0x%x\n", flags);
+ have_err = 1;
+ }
+ if (flags & NV_CPB_RESP_CMD_ERR) {
+ VPRINTK("CPB flags CMD err, flags = 0x%x\n", flags);
+ have_err = 1;
+ }
+ if (flags & NV_CPB_RESP_CPB_ERR) {
+ VPRINTK("CPB flags CPB err, flags = 0x%x\n", flags);
+ have_err = 1;
+ }
+ }
+
+ // clear status
+ writew(status, mmio + NV_ADMA_STAT);
+
+ if (handled) {
+ u8 ata_status = readb(mmio + (ATA_REG_STATUS * 4));
+ qc->err_mask |= ac_err_mask(have_err ? (ata_status | ATA_ERR) : ata_status);
+ ata_qc_complete(qc);
+ }
+
+ return handled; /* irq handled */
+}
+
/* FIXME: The hardware provides the necessary SATA PHY controls
* to support ATA_FLAG_SATA_RESET. However, it is currently
* necessary to disable that flag, to solve misdetection problems.
@@ -275,6 +616,7 @@ static const struct ata_port_operations
* This problem really needs to be investigated further. But in the
* meantime, we avoid ATA_FLAG_SATA_RESET to get people working.
*/
+
static struct ata_port_info nv_port_info = {
.sht = &nv_sht,
.host_flags = ATA_FLAG_SATA |
@@ -293,6 +635,80 @@ MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, nv_pci_tbl);
MODULE_VERSION(DRV_VERSION);
+static inline void nv_enable_adma_space (struct pci_dev *pdev)
+{
+ u8 regval;
+
+ VPRINTK("ENTER\n");
+
+ pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val);
+ regval |= NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
+ pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
+}
+
+static inline void nv_disable_adma_space (struct pci_dev *pdev)
+{
+ u8 regval;
+
+ VPRINTK("ENTER\n");
+
+ pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val);
+ regval &= ~NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
+ pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
+}
+
+static void nv_irq_clear(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA) {
+ nv_adma_irq_clear(ap);
+ } else {
+ ata_bmdma_irq_clear(ap);
+ }
+}
+
+static void nv_adma_irq_clear(struct ata_port *ap)
+{
+ /* FIXME: TODO */
+}
+
+static u8 nv_bmdma_status(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA) {
+ return nv_adma_bmdma_status(ap);
+ } else {
+ return ata_bmdma_status(ap);
+ }
+}
+
+static u8 nv_adma_bmdma_status(struct ata_port *ap)
+{
+ // FIXME: This is no different than ata_bmdma_status for PIO
+ return inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+}
+
+static void nv_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct ata_host_set *host_set = qc->ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA) {
+ nv_adma_bmdma_stop(qc);
+ } else {
+ ata_bmdma_stop(qc);
+ }
+}
+
+static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ /* FIXME: TODO */
+}
+
static irqreturn_t nv_interrupt (int irq, void *dev_instance,
struct pt_regs *regs)
{
@@ -305,26 +721,48 @@ static irqreturn_t nv_interrupt (int irq
spin_lock_irqsave(&host_set->lock, flags);
for (i = 0; i < host_set->n_ports; i++) {
- struct ata_port *ap;
+ struct ata_port *ap = host_set->ports[i];
- ap = host_set->ports[i];
if (ap &&
!(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) {
struct ata_queued_cmd *qc;
+ if (host->host_desc->host_type == ADMA) {
+ struct nv_adma_port_priv *pp = ap->private_data;
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+ // read notifiers
+ pp->notifier = readl(mmio + NV_ADMA_NOTIFIER);
+ pp->notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
+ }
+
qc = ata_qc_from_tag(ap, ap->active_tag);
- if (qc && (!(qc->tf.ctl & ATA_NIEN)))
- handled += ata_host_intr(ap, qc);
- else
+ if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
+ if (host->host_desc->host_type == ADMA)
+ handled += nv_adma_host_intr(ap, qc);
+ else
+ handled += ata_host_intr(ap, qc);
+ } else {
// No request pending? Clear interrupt status
// anyway, in case there's one pending.
ap->ops->check_status(ap);
+ }
+
}
}
if (host->host_desc->check_hotplug)
- handled += host->host_desc->check_hotplug(host_set);
+ (void) host->host_desc->check_hotplug(host_set);
+
+ // clear notifier
+ if (handled && host->host_desc->host_type == ADMA) {
+ for (i = 0; i < host_set->n_ports; i++) {
+ struct ata_port *ap = host_set->ports[i];
+ struct nv_adma_port_priv *pp = ap->private_data;
+ writel(pp->notifier | pp->notifier_error,
+ nv_adma_notifier_clear_block(ap));
+ }
+ }
spin_unlock_irqrestore(&host_set->lock, flags);
@@ -335,14 +773,22 @@ static u32 nv_scr_read (struct ata_port
{
struct ata_host_set *host_set = ap->host_set;
struct nv_host *host = host_set->private_data;
+ u32 val = 0;
+
+ VPRINTK("ENTER\n");
+
+ VPRINTK("reading SCR reg %d, got 0x%08x\n", sc_reg, val);
if (sc_reg > SCR_CONTROL)
return 0xffffffffU;
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
- return readl((void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4));
+ val = readl((void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4));
else
- return inl(ap->ioaddr.scr_addr + (sc_reg * 4));
+ val = inl(ap->ioaddr.scr_addr + (sc_reg * 4));
+
+ VPRINTK("reading SCR reg %d, got 0x%08x\n", sc_reg, val);
+ return val;
}
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
@@ -350,6 +796,9 @@ static void nv_scr_write (struct ata_por
struct ata_host_set *host_set = ap->host_set;
struct nv_host *host = host_set->private_data;
+ VPRINTK("ENTER\n");
+
+ VPRINTK("writing SCR reg %d with 0x%08x\n", sc_reg, val);
if (sc_reg > SCR_CONTROL)
return;
@@ -364,6 +813,8 @@ static void nv_host_stop (struct ata_hos
struct nv_host *host = host_set->private_data;
struct pci_dev *pdev = to_pci_dev(host_set->dev);
+ VPRINTK("ENTER\n");
+
// Disable hotplug event interrupts.
if (host->host_desc->disable_hotplug)
host->host_desc->disable_hotplug(host_set);
@@ -374,16 +825,218 @@ static void nv_host_stop (struct ata_hos
pci_iounmap(pdev, host_set->mmio_base);
}
+static int nv_port_start(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA) {
+ return nv_adma_port_start(ap);
+ } else {
+ return ata_port_start(ap);
+ }
+}
+
+static void nv_port_stop(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA) {
+ nv_adma_port_stop(ap);
+ } else {
+ ata_port_stop(ap);
+ }
+}
+
+static int nv_adma_port_start(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct nv_adma_port_priv *pp;
+ int rc;
+ void *mem;
+ dma_addr_t mem_dma;
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+
+ VPRINTK("ENTER\n");
+
+ nv_adma_reset_channel(ap);
+
+#ifdef DEBUG
+ VPRINTK("after reset:\n");
+ nv_adma_dump_port(ap);
+#endif
+
+ rc = ata_port_start(ap);
+ if (rc)
+ return rc;
+
+ pp = kmalloc(sizeof(*pp), GFP_KERNEL);
+ if (!pp) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ memset(pp, 0, sizeof(*pp));
+
+ mem = dma_alloc_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ,
+ &mem_dma, GFP_KERNEL);
+
+ VPRINTK("dma memory: vaddr = 0x%08x, paddr = 0x%08x\n", (u32)mem, (u32)mem_dma);
+
+ if (!mem) {
+ rc = -ENOMEM;
+ goto err_out_kfree;
+ }
+ memset(mem, 0, NV_ADMA_PORT_PRIV_DMA_SZ);
+
+ /*
+ * First item in chunk of DMA memory:
+ * 128-byte command parameter block (CPB)
+ * one for each command tag
+ */
+ pp->cpb = mem;
+ pp->cpb_dma = mem_dma;
+
+ VPRINTK("cpb = 0x%08x, cpb_dma = 0x%08x\n", (u32)pp->cpb, (u32)pp->cpb_dma);
+
+ writel(mem_dma, mmio + NV_ADMA_CPB_BASE_LOW);
+ writel(0, mmio + NV_ADMA_CPB_BASE_HIGH);
+
+ mem += NV_ADMA_CAN_QUEUE * NV_ADMA_CPB_SZ;
+ mem_dma += NV_ADMA_CAN_QUEUE * NV_ADMA_CPB_SZ;
+
+ /*
+ * Second item: block of ADMA_SGTBL_LEN s/g entries
+ */
+ pp->aprd = mem;
+ pp->aprd_dma = mem_dma;
+
+ VPRINTK("aprd = 0x%08x, aprd_dma = 0x%08x\n", (u32)pp->aprd, (u32)pp->aprd_dma);
+
+ ap->private_data = pp;
+
+ // clear any outstanding interrupt conditions
+ writew(0xffff, mmio + NV_ADMA_STAT);
+
+ // initialize port variables
+ // pp->cpb_idx = 0;
+ pp->flags = NV_ADMA_PORT_REGISTER_MODE;
+
+ // make sure controller is in ATA register mode
+ nv_adma_register_mode(ap);
+
+ return 0;
+
+err_out_kfree:
+ kfree(pp);
+err_out:
+ ata_port_stop(ap);
+ return rc;
+}
+
+static void nv_adma_port_stop(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct nv_adma_port_priv *pp = ap->private_data;
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+
+ VPRINTK("ENTER\n");
+
+ writew(0, mmio + NV_ADMA_CTL);
+
+ ap->private_data = NULL;
+ dma_free_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ, pp->cpb, pp->cpb_dma);
+ kfree(pp);
+ ata_port_stop(ap);
+}
+
+
+static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port)
+{
+ void __iomem *mmio = probe_ent->mmio_base;
+ struct ata_ioports *ioport = &probe_ent->port[port];
+
+ VPRINTK("ENTER\n");
+
+ mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE;
+
+ ioport->cmd_addr = (unsigned long) mmio;
+ ioport->data_addr = (unsigned long) mmio + (ATA_REG_DATA * 4);
+ ioport->error_addr =
+ ioport->feature_addr = (unsigned long) mmio + (ATA_REG_ERR * 4);
+ ioport->nsect_addr = (unsigned long) mmio + (ATA_REG_NSECT * 4);
+ ioport->lbal_addr = (unsigned long) mmio + (ATA_REG_LBAL * 4);
+ ioport->lbam_addr = (unsigned long) mmio + (ATA_REG_LBAM * 4);
+ ioport->lbah_addr = (unsigned long) mmio + (ATA_REG_LBAH * 4);
+ ioport->device_addr = (unsigned long) mmio + (ATA_REG_DEVICE * 4);
+ ioport->status_addr =
+ ioport->command_addr = (unsigned long) mmio + (ATA_REG_STATUS * 4);
+ ioport->altstatus_addr =
+ ioport->ctl_addr = (unsigned long) mmio + 0x20;
+}
+
+static int nv_adma_host_init(struct ata_probe_ent *probe_ent)
+{
+ struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+ unsigned int i;
+ u32 tmp32;
+
+ VPRINTK("ENTER\n");
+
+ probe_ent->n_ports = NV_PORTS;
+
+ nv_enable_adma_space(pdev);
+
+ // enable ADMA on the ports
+ pci_read_config_dword(pdev, NV_MCP_SATA_CFG_20, &tmp32);
+ tmp32 |= NV_MCP_SATA_CFG_20_PORT0_EN |
+ NV_MCP_SATA_CFG_20_PORT0_PWB_EN |
+ NV_MCP_SATA_CFG_20_PORT1_EN |
+ NV_MCP_SATA_CFG_20_PORT1_PWB_EN;
+
+ pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32);
+
+ for (i = 0; i < probe_ent->n_ports; i++)
+ nv_adma_setup_port(probe_ent, i);
+
+ for (i = 0; i < probe_ent->n_ports; i++) {
+ void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i);
+ u16 tmp;
+
+ /* enable interrupt, clear reset if not already clear */
+ tmp = readw(mmio + NV_ADMA_CTL);
+ writew(tmp | NV_ADMA_CTL_AIEN, mmio + NV_ADMA_CTL);
+ }
+
+ pci_set_master(pdev);
+
+ return 0;
+}
+
+static struct nv_host_desc *nv_find_host_desc(unsigned int host_type)
+{
+ int i;
+
+ for (i = 0 ; i < (sizeof nv_device_tbl)/(sizeof nv_device_tbl[0]); i++)
+ if (nv_device_tbl[i].host_type == host_type)
+ return &nv_device_tbl[i];
+ return NULL;
+}
+
+
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
struct nv_host *host;
struct ata_port_info *ppi;
struct ata_probe_ent *probe_ent;
+ struct nv_host_desc *host_desc;
int pci_dev_busy = 0;
int rc;
u32 bar;
+ VPRINTK("ENTER\n");
+
// Make sure this is a SATA controller by counting the number of bars
// (NVIDIA SATA controllers will always have six bars). Otherwise,
// it's an IDE controller and we ignore it.
@@ -414,6 +1067,23 @@ static int nv_init_one (struct pci_dev *
rc = -ENOMEM;
ppi = &nv_port_info;
+
+ if (adma_enabled && (ent->driver_data & ADMA))
+ host_desc = nv_find_host_desc(ADMA);
+ else
+ host_desc = nv_find_host_desc(ent->driver_data & ~ADMA);
+
+ if (host_desc->host_type == ADMA) {
+ // ADMA overrides
+ ppi->host_flags |= ATA_FLAG_MMIO | ATA_FLAG_SATA_RESET;
+#ifdef NV_ADMA_NCQ
+ ppi->host_flags |= ATA_FLAG_NCQ;
+#endif
+ ppi->sht->can_queue = NV_ADMA_CAN_QUEUE;
+ ppi->sht->sg_tablesize = NV_ADMA_SGTBL_LEN;
+// ppi->port_ops->irq_handler = nv_adma_interrupt;
+ }
+
probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent)
goto err_out_regions;
@@ -423,7 +1093,7 @@ static int nv_init_one (struct pci_dev *
goto err_out_free_ent;
memset(host, 0, sizeof(struct nv_host));
- host->host_desc = &nv_device_tbl[ent->driver_data];
+ host->host_desc = host_desc;
probe_ent->private_data = host;
@@ -440,6 +1110,7 @@ static int nv_init_one (struct pci_dev *
}
base = (unsigned long)probe_ent->mmio_base;
+ VPRINTK("BAR5 base is at 0x%x\n", (u32)base);
probe_ent->port[0].scr_addr =
base + NV_PORT0_SCR_REG_OFFSET;
@@ -455,6 +1126,12 @@ static int nv_init_one (struct pci_dev *
pci_set_master(pdev);
+ if (host_desc->host_type == ADMA) {
+ rc = nv_adma_host_init(probe_ent);
+ if (rc)
+ goto err_out_iounmap;
+ }
+
rc = ata_device_add(probe_ent);
if (rc != NV_PORTS)
goto err_out_iounmap;
@@ -483,6 +1160,239 @@ err_out:
return rc;
}
+static void nv_eng_timeout(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA) {
+ nv_adma_eng_timeout(ap);
+ } else {
+ return ata_eng_timeout(ap);
+ }
+}
+
+static void nv_adma_eng_timeout(struct ata_port *ap)
+{
+ struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
+ struct nv_adma_port_priv *pp = ap->private_data;
+ u8 drv_stat;
+
+ VPRINTK("ENTER\n");
+
+ if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
+ ata_eng_timeout(ap);
+ goto out;
+ }
+
+
+ if (!qc) {
+ printk(KERN_ERR "ata%u: BUG: timeout without command\n",
+ ap->id);
+ goto out;
+ }
+
+
+// spin_lock_irqsave(&host_set->lock, flags);
+
+ qc->scsidone = scsi_finish_command;
+
+ drv_stat = ata_chk_status(ap);
+
+ printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x\n",
+ ap->id, qc->tf.command, drv_stat);
+
+ // reset channel
+ nv_adma_reset_channel(ap);
+
+ /* complete taskfile transaction */
+ qc->err_mask |= ac_err_mask(drv_stat);
+ ata_qc_complete(qc);
+
+// spin_unlock_irqrestore(&host_set->lock, flags);
+
+out:
+ DPRINTK("EXIT\n");
+}
+
+static void nv_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct ata_host_set *host_set = qc->ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA) {
+ nv_adma_qc_prep(qc);
+ } else {
+ ata_qc_prep(qc);
+ }
+}
+
+static void nv_adma_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct nv_adma_port_priv *pp = qc->ap->private_data;
+ struct nv_adma_cpb *cpb = &pp->cpb[qc->tag];
+
+ VPRINTK("ENTER\n");
+
+ VPRINTK("qc->flags = 0x%x\n", (u32)qc->flags);
+
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP)) {
+ ata_qc_prep(qc);
+ return;
+ }
+
+ memset(cpb, 0, sizeof(struct nv_adma_cpb));
+
+ cpb->ctl_flags = NV_CPB_CTL_CPB_VALID |
+ NV_CPB_CTL_APRD_VALID |
+ NV_CPB_CTL_IEN;
+ cpb->len = 3;
+ cpb->tag = qc->tag;
+ cpb->next_cpb_idx = 0;
+
+#ifdef NV_ADMA_NCQ
+ // turn on NCQ flags for NCQ commands
+ if (qc->flags & ATA_QCFLAG_NCQ)
+ cpb->ctl_flags |= NV_CPB_CTL_QUEUE | NV_CPB_CTL_FPDMA;
+#endif
+
+ nv_adma_tf_to_cpb(&qc->tf, cpb->tf);
+
+ nv_adma_fill_sg(qc, cpb);
+}
+
+static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb)
+{
+ struct nv_adma_port_priv *pp = qc->ap->private_data;
+ unsigned int idx;
+ struct nv_adma_prd *aprd;
+ struct scatterlist *sg;
+
+ VPRINTK("ENTER\n");
+
+ idx = 0;
+
+ ata_for_each_sg(sg, qc) {
+ aprd = (idx < 5) ? &cpb->aprd[idx] : &pp->aprd[idx-5];
+ nv_adma_fill_aprd(qc, sg, idx, aprd);
+ idx++;
+ }
+ if (idx > 5) {
+ cpb->next_aprd = (u64)(pp->aprd_dma + NV_ADMA_APRD_SZ * qc->tag);
+ }
+}
+
+static void nv_adma_fill_aprd(struct ata_queued_cmd *qc,
+ struct scatterlist *sg,
+ int idx,
+ struct nv_adma_prd *aprd)
+{
+ u32 sg_len, addr, flags;
+
+ memset(aprd, 0, sizeof(struct nv_adma_prd));
+
+ addr = sg_dma_address(sg);
+ sg_len = sg_dma_len(sg);
+
+ flags = 0;
+ if (qc->tf.flags & ATA_TFLAG_WRITE)
+ flags |= NV_APRD_WRITE;
+ if (idx == qc->n_elem - 1) {
+ flags |= NV_APRD_END;
+ } else if (idx != 4) {
+ flags |= NV_APRD_CONT;
+ }
+
+ aprd->addr = cpu_to_le32(addr);
+ aprd->len = cpu_to_le32(sg_len); /* len in bytes */
+ aprd->flags = cpu_to_le32(flags);
+}
+
+static void nv_adma_register_mode(struct ata_port *ap)
+{
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+ struct nv_adma_port_priv *pp = ap->private_data;
+ u16 tmp;
+
+ tmp = readw(mmio + NV_ADMA_CTL);
+ writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
+
+ pp->flags |= NV_ADMA_PORT_REGISTER_MODE;
+}
+
+static void nv_adma_mode(struct ata_port *ap)
+{
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+ struct nv_adma_port_priv *pp = ap->private_data;
+ u16 tmp;
+
+ if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) {
+ return;
+ }
+
+#if 0
+ nv_adma_reset_channel(ap);
+#endif
+
+ tmp = readw(mmio + NV_ADMA_CTL);
+ writew(tmp | NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
+
+ pp->flags &= ~NV_ADMA_PORT_REGISTER_MODE;
+}
+
+static int nv_qc_issue(struct ata_queued_cmd *qc)
+{
+ struct ata_host_set *host_set = qc->ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA) {
+ return nv_adma_qc_issue(qc);
+ } else {
+ return ata_qc_issue_prot(qc);
+ }
+}
+
+static int nv_adma_qc_issue(struct ata_queued_cmd *qc)
+{
+#if 0
+ struct nv_adma_port_priv *pp = qc->ap->private_data;
+#endif
+ void __iomem *mmio = nv_adma_ctl_block(qc->ap);
+
+ VPRINTK("ENTER\n");
+
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP)) {
+ VPRINTK("no dmamap, using ATA register mode: 0x%x\n", (u32)qc->flags);
+ // use ATA register mode
+ nv_adma_register_mode(qc->ap);
+ return ata_qc_issue_prot(qc);
+ } else {
+ nv_adma_mode(qc->ap);
+ }
+
+#if 0
+ nv_adma_dump_port(qc->ap);
+ nv_adma_dump_cpb(&pp->cpb[qc->tag]);
+ if (qc->n_elem > 5) {
+ int i;
+ for (i = 0; i < qc->n_elem - 5; i++) {
+ nv_adma_dump_aprd(&pp->aprd[i]);
+ }
+ }
+#endif
+
+ //
+ // write append register, command tag in lower 8 bits
+ // and (number of cpbs to append -1) in top 8 bits
+ //
+ mb();
+ writew(qc->tag, mmio + NV_ADMA_APPEND);
+
+ VPRINTK("EXIT\n");
+
+ return 0;
+}
+
static void nv_enable_hotplug(struct ata_probe_ent *probe_ent)
{
u8 intr_mask;
@@ -543,11 +1453,8 @@ static void nv_enable_hotplug_ck804(stru
{
struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
u8 intr_mask;
- u8 regval;
- pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val);
- regval |= NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
- pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
+ nv_enable_adma_space(pdev);
writeb(NV_INT_STATUS_HOTPLUG, probe_ent->mmio_base + NV_INT_STATUS_CK804);
@@ -561,7 +1468,6 @@ static void nv_disable_hotplug_ck804(str
{
struct pci_dev *pdev = to_pci_dev(host_set->dev);
u8 intr_mask;
- u8 regval;
intr_mask = readb(host_set->mmio_base + NV_INT_ENABLE_CK804);
@@ -569,9 +1475,7 @@ static void nv_disable_hotplug_ck804(str
writeb(intr_mask, host_set->mmio_base + NV_INT_ENABLE_CK804);
- pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val);
- regval &= ~NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
- pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
+ nv_disable_adma_space(pdev);
}
static int nv_check_hotplug_ck804(struct ata_host_set *host_set)
@@ -606,6 +1510,65 @@ static int nv_check_hotplug_ck804(struct
return 0;
}
+static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent)
+{
+ struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+ unsigned int i;
+ u16 tmp;
+
+ nv_enable_adma_space(pdev);
+
+ for (i = 0; i < probe_ent->n_ports; i++) {
+ void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i);
+ writew(NV_ADMA_STAT_HOTPLUG | NV_ADMA_STAT_HOTUNPLUG,
+ mmio + NV_ADMA_STAT);
+
+ tmp = readw(mmio + NV_ADMA_CTL);
+ writew(tmp | NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL);
+
+ }
+}
+
+static void nv_disable_hotplug_adma(struct ata_host_set *host_set)
+{
+ unsigned int i;
+ u16 tmp;
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i);
+
+ tmp = readw(mmio + NV_ADMA_CTL);
+ writew(tmp & ~NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL);
+
+ }
+}
+
+static int nv_check_hotplug_adma(struct ata_host_set *host_set)
+{
+ unsigned int i;
+ u16 adma_status;
+ int hotplugged = 0;
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i);
+ adma_status = readw(mmio + NV_ADMA_STAT);
+ if (adma_status & NV_ADMA_STAT_HOTPLUG) {
+ printk(KERN_WARNING "nv_sata: "
+ "port %d device added\n", i);
+ writew(NV_ADMA_STAT_HOTPLUG, mmio + NV_ADMA_STAT);
+ hotplugged = 1;
+ }
+ if (adma_status & NV_ADMA_STAT_HOTUNPLUG) {
+ printk(KERN_WARNING "nv_sata: "
+ "port %d device removed\n", i);
+ writew(NV_ADMA_STAT_HOTUNPLUG, mmio + NV_ADMA_STAT);
+ hotplugged = 1;
+ }
+ }
+
+ return hotplugged;
+}
+
static int __init nv_init(void)
{
return pci_module_init(&nv_pci_driver);
@@ -618,3 +1581,71 @@ static void __exit nv_exit(void)
module_init(nv_init);
module_exit(nv_exit);
+module_param_named(adma, adma_enabled, bool, 0);
+MODULE_PARM_DESC(adma, "Enable use of ADMA (Default: false)");
+
+
+#ifdef DEBUG
+static void nv_adma_dump_aprd(struct nv_adma_prd *aprd)
+{
+ printk("%016llx %08x %02x %s %s %s\n",
+ aprd->addr,
+ aprd->len,
+ aprd->flags,
+ (aprd->flags & NV_APRD_WRITE) ? "WRITE" : " ",
+ (aprd->flags & NV_APRD_END) ? "END" : " ",
+ (aprd->flags & NV_APRD_CONT) ? "CONT" : " ");
+}
+static void nv_adma_dump_iomem(void __iomem *m, int len)
+{
+ int i, j;
+
+ for (i = 0; i < len/16; i++) {
+ printk(KERN_WARNING "%02x: ", 16*i);
+ for (j = 0; j < 16; j++) {
+ printk("%02x%s", (u32)readb(m + 16*i + j),
+ (j == 7) ? "-" : " ");
+ }
+ printk("\n");
+ }
+}
+
+static void nv_adma_dump_cpb_tf(u16 tf)
+{
+ printk("0x%04x %s %s %s 0x%02x 0x%02x\n",
+ tf,
+ (tf & CMDEND) ? "END" : " ",
+ (tf & WNB) ? "WNB" : " ",
+ (tf & IGN) ? "IGN" : " ",
+ ((tf >> 8) & 0x1f),
+ (tf & 0xff));
+}
+
+static void nv_adma_dump_port(struct ata_port *ap)
+{
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+ nv_adma_dump_iomem(mmio, NV_ADMA_PORT_SIZE);
+}
+
+static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb)
+{
+ int i;
+
+ printk("resp_flags: 0x%02x\n", cpb->resp_flags);
+ printk("ctl_flags: 0x%02x\n", cpb->ctl_flags);
+ printk("len: 0x%02x\n", cpb->len);
+ printk("tag: 0x%02x\n", cpb->tag);
+ printk("next_cpb_idx: 0x%02x\n", cpb->next_cpb_idx);
+ printk("tf:\n");
+ for (i=0; i<12; i++) {
+ nv_adma_dump_cpb_tf(cpb->tf[i]);
+ }
+ printk("aprd:\n");
+ for (i=0; i<5; i++) {
+ nv_adma_dump_aprd(&cpb->aprd[i]);
+ }
+ printk("next_aprd: 0x%016llx\n", cpb->next_aprd);
+}
+
+#endif
+
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-17 23:23 [PATCH][INCOMPLETE] sata_nv: merge ADMA support Bill Rugolsky Jr.
@ 2006-03-18 0:56 ` Jeff Garzik
2006-03-18 8:06 ` Bill Rugolsky Jr.
0 siblings, 1 reply; 14+ messages in thread
From: Jeff Garzik @ 2006-03-18 0:56 UTC (permalink / raw)
To: Bill Rugolsky Jr.; +Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 612 bytes --]
Bill Rugolsky Jr. wrote:
> Jeff,
>
> Here is an incomplete attempt to get the sata_nv ADMA code working.
>
> I made another pass through my merge of the sata_nv driver to fix and
> clean up a few things. I've only made the minimal changes necessary
> to get it into a testable state; methinks it could do with a lot of
> refactoring and cleanup, instead of all the
> "if (host->...host_type == ADMA)" tests.
>
> There are several FIXMEs from the original code.
>
> I added an "adma" boolean flag to simplify testing the different code paths.
Could I get you to diff against the attached version?
Jeff
[-- Attachment #2: sata_nv.c --]
[-- Type: text/x-csrc, Size: 41140 bytes --]
/*
* sata_nv.c - NVIDIA nForce SATA
*
* Copyright 2004 NVIDIA Corp. All rights reserved.
* Copyright 2004 Andrew Chew
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* libata documentation is available via 'make {ps|pdf}docs',
* as Documentation/DocBook/libata.*
*
* No hardware documentation available outside of NVIDIA.
* This driver programs the NVIDIA SATA controller in a similar
* fashion as with other PCI IDE BMDMA controllers, with a few
* NV-specific details such as register offsets, SATA phy location,
* hotplug info, etc.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include "scsi.h"
#include <scsi/scsi_host.h>
#include <linux/libata.h>
//#define DEBUG
#define DRV_NAME "sata_nv"
#define DRV_VERSION "0.8"
enum {
NV_PORTS = 2,
NV_PIO_MASK = 0x1f,
NV_MWDMA_MASK = 0x07,
NV_UDMA_MASK = 0x7f,
NV_PORT0_SCR_REG_OFFSET = 0x00,
NV_PORT1_SCR_REG_OFFSET = 0x40,
NV_INT_STATUS = 0x10,
NV_INT_STATUS_CK804 = 0x440,
NV_INT_STATUS_PDEV_INT = 0x01,
NV_INT_STATUS_PDEV_PM = 0x02,
NV_INT_STATUS_PDEV_ADDED = 0x04,
NV_INT_STATUS_PDEV_REMOVED = 0x08,
NV_INT_STATUS_SDEV_INT = 0x10,
NV_INT_STATUS_SDEV_PM = 0x20,
NV_INT_STATUS_SDEV_ADDED = 0x40,
NV_INT_STATUS_SDEV_REMOVED = 0x80,
NV_INT_STATUS_PDEV_HOTPLUG = (NV_INT_STATUS_PDEV_ADDED |
NV_INT_STATUS_PDEV_REMOVED),
NV_INT_STATUS_SDEV_HOTPLUG = (NV_INT_STATUS_SDEV_ADDED |
NV_INT_STATUS_SDEV_REMOVED),
NV_INT_STATUS_HOTPLUG = (NV_INT_STATUS_PDEV_HOTPLUG |
NV_INT_STATUS_SDEV_HOTPLUG),
NV_INT_ENABLE = 0x11,
NV_INT_ENABLE_CK804 = 0x441,
NV_INT_ENABLE_PDEV_MASK = 0x01,
NV_INT_ENABLE_PDEV_PM = 0x02,
NV_INT_ENABLE_PDEV_ADDED = 0x04,
NV_INT_ENABLE_PDEV_REMOVED = 0x08,
NV_INT_ENABLE_SDEV_MASK = 0x10,
NV_INT_ENABLE_SDEV_PM = 0x20,
NV_INT_ENABLE_SDEV_ADDED = 0x40,
NV_INT_ENABLE_SDEV_REMOVED = 0x80,
NV_INT_ENABLE_PDEV_HOTPLUG = (NV_INT_ENABLE_PDEV_ADDED |
NV_INT_ENABLE_PDEV_REMOVED),
NV_INT_ENABLE_SDEV_HOTPLUG = (NV_INT_ENABLE_SDEV_ADDED |
NV_INT_ENABLE_SDEV_REMOVED),
NV_INT_ENABLE_HOTPLUG = (NV_INT_ENABLE_PDEV_HOTPLUG |
NV_INT_ENABLE_SDEV_HOTPLUG),
NV_INT_CONFIG = 0x12,
NV_INT_CONFIG_METHD = 0x01, // 0 = INT, 1 = SMI
// For PCI config register 20
NV_MCP_SATA_CFG_20 = 0x50,
NV_MCP_SATA_CFG_20_SATA_SPACE_EN = 0x04,
NV_MCP_SATA_CFG_20_PORT0_EN = (1 << 17),
NV_MCP_SATA_CFG_20_PORT1_EN = (1 << 16),
NV_MCP_SATA_CFG_20_PORT0_PWB_EN = (1 << 14),
NV_MCP_SATA_CFG_20_PORT1_PWB_EN = (1 << 12),
};
//#define NV_ADMA_NCQ
#ifdef NV_ADMA_NCQ
#define NV_ADMA_CAN_QUEUE ATA_MAX_QUEUE
#else
#define NV_ADMA_CAN_QUEUE ATA_DEF_QUEUE
#endif
enum {
NV_ADMA_CPB_SZ = 128,
NV_ADMA_APRD_SZ = 16,
NV_ADMA_SGTBL_LEN = (1024 - NV_ADMA_CPB_SZ) / NV_ADMA_APRD_SZ,
NV_ADMA_SGTBL_SZ = NV_ADMA_SGTBL_LEN * NV_ADMA_APRD_SZ,
NV_ADMA_PORT_PRIV_DMA_SZ = NV_ADMA_CAN_QUEUE * (NV_ADMA_CPB_SZ + NV_ADMA_SGTBL_SZ),
// NV_ADMA_MAX_CPBS = 32,
// BAR5 offset to ADMA general registers
NV_ADMA_GEN = 0x400,
NV_ADMA_GEN_CTL = 0x00,
NV_ADMA_NOTIFIER_CLEAR = 0x30,
#define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & ( 1 << (19 + (12 * (PORT)))))
// BAR5 offset to ADMA ports
NV_ADMA_PORT = 0x480,
// size of ADMA port register space
NV_ADMA_PORT_SIZE = 0x100,
// ADMA port registers
NV_ADMA_CTL = 0x40,
NV_ADMA_CPB_COUNT = 0x42,
NV_ADMA_NEXT_CPB_IDX = 0x43,
NV_ADMA_STAT = 0x44,
NV_ADMA_CPB_BASE_LOW = 0x48,
NV_ADMA_CPB_BASE_HIGH = 0x4C,
NV_ADMA_APPEND = 0x50,
NV_ADMA_NOTIFIER = 0x68,
NV_ADMA_NOTIFIER_ERROR = 0x6C,
// NV_ADMA_CTL register bits
NV_ADMA_CTL_HOTPLUG_IEN = (1 << 0),
NV_ADMA_CTL_CHANNEL_RESET = (1 << 5),
NV_ADMA_CTL_GO = (1 << 7),
NV_ADMA_CTL_AIEN = (1 << 8),
NV_ADMA_CTL_READ_NON_COHERENT = (1 << 11),
NV_ADMA_CTL_WRITE_NON_COHERENT = (1 << 12),
// CPB response flag bits
NV_CPB_RESP_DONE = (1 << 0),
NV_CPB_RESP_ATA_ERR = (1 << 3),
NV_CPB_RESP_CMD_ERR = (1 << 4),
NV_CPB_RESP_CPB_ERR = (1 << 7),
// CPB control flag bits
NV_CPB_CTL_CPB_VALID = (1 << 0),
NV_CPB_CTL_QUEUE = (1 << 1),
NV_CPB_CTL_APRD_VALID = (1 << 2),
NV_CPB_CTL_IEN = (1 << 3),
NV_CPB_CTL_FPDMA = (1 << 4),
// APRD flags
NV_APRD_WRITE = (1 << 1),
NV_APRD_END = (1 << 2),
NV_APRD_CONT = (1 << 3),
// NV_ADMA_STAT flags
NV_ADMA_STAT_TIMEOUT = (1 << 0),
NV_ADMA_STAT_HOTUNPLUG = (1 << 1),
NV_ADMA_STAT_HOTPLUG = (1 << 2),
NV_ADMA_STAT_CPBERR = (1 << 4),
NV_ADMA_STAT_SERROR = (1 << 5),
NV_ADMA_STAT_CMD_COMPLETE = (1 << 6),
NV_ADMA_STAT_IDLE = (1 << 8),
NV_ADMA_STAT_LEGACY = (1 << 9),
NV_ADMA_STAT_STOPPED = (1 << 10),
NV_ADMA_STAT_DONE = (1 << 12),
NV_ADMA_STAT_ERR = (NV_ADMA_STAT_CPBERR | NV_ADMA_STAT_TIMEOUT),
// port flags
NV_ADMA_PORT_REGISTER_MODE = (1 << 0),
};
struct nv_adma_prd {
u64 addr;
u32 len;
u8 flags;
u8 packet_len;
u16 reserved;
};
enum nv_adma_regbits {
CMDEND = (1 << 15), /* end of command list */
WNB = (1 << 14), /* wait-not-BSY */
IGN = (1 << 13), /* ignore this entry */
CS1n = (1 << (4 + 8)), /* std. PATA signals follow... */
DA2 = (1 << (2 + 8)),
DA1 = (1 << (1 + 8)),
DA0 = (1 << (0 + 8)),
};
struct nv_adma_cpb {
u8 resp_flags; //0
u8 reserved1; //1
u8 ctl_flags; //2
// len is length of taskfile in 64 bit words
u8 len; //3
u8 tag; //4
u8 next_cpb_idx; //5
u16 reserved2; //6-7
u16 tf[12]; //8-31
struct nv_adma_prd aprd[5]; //32-111
u64 next_aprd; //112-119
u64 reserved3; //120-127
};
struct nv_adma_port_priv {
struct nv_adma_cpb *cpb;
// u8 cpb_idx;
u8 flags;
u32 notifier;
u32 notifier_error;
dma_addr_t cpb_dma;
struct nv_adma_prd *aprd;
dma_addr_t aprd_dma;
};
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
static irqreturn_t nv_interrupt (int irq, void *dev_instance,
struct pt_regs *regs);
static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static void nv_host_stop (struct ata_host_set *host_set);
static int nv_adma_port_start(struct ata_port *ap);
static void nv_adma_port_stop(struct ata_port *ap);
static void nv_adma_irq_clear(struct ata_port *ap);
static void nv_enable_hotplug(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug(struct ata_host_set *host_set);
static void nv_check_hotplug(struct ata_host_set *host_set);
static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug_ck804(struct ata_host_set *host_set);
static void nv_check_hotplug_ck804(struct ata_host_set *host_set);
static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug_adma(struct ata_host_set *host_set);
static void nv_check_hotplug_adma(struct ata_host_set *host_set);
static int nv_adma_qc_issue(struct ata_queued_cmd *qc);
static void nv_adma_qc_prep(struct ata_queued_cmd *qc);
static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb);
static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb);
static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, struct scatterlist *sg, int idx, struct nv_adma_prd *aprd);
static void nv_adma_register_mode(struct ata_port *ap);
static void nv_adma_mode(struct ata_port *ap);
static u8 nv_adma_bmdma_status(struct ata_port *ap);
static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc);
static void nv_adma_eng_timeout(struct ata_port *ap);
#ifdef DEBUG
static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb);
static void nv_adma_dump_aprd(struct nv_adma_prd *aprd);
static void nv_adma_dump_cpb_tf(u16 tf);
static void nv_adma_dump_port(struct ata_port *ap);
static void nv_adma_dump_iomem(void __iomem *m, int len);
#endif
enum nv_host_type
{
GENERIC,
NFORCE2,
NFORCE3,
CK804,
MCP51,
MCP55,
ADMA
};
static const struct pci_device_id nv_pci_tbl[] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 },
{ PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC },
{ PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID<<8, 0xffff00, GENERIC },
{ 0, } /* terminate list */
};
#define NV_HOST_FLAGS_SCR_MMIO 0x00000001
struct nv_host_desc
{
enum nv_host_type host_type;
void (*enable_hotplug)(struct ata_probe_ent *probe_ent);
void (*disable_hotplug)(struct ata_host_set *host_set);
void (*check_hotplug)(struct ata_host_set *host_set);
};
static struct nv_host_desc nv_device_tbl[] = {
{
.host_type = GENERIC,
.enable_hotplug = NULL,
.disable_hotplug= NULL,
.check_hotplug = NULL,
},
{
.host_type = NFORCE2,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
},
{
.host_type = NFORCE3,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
},
{ .host_type = CK804,
.enable_hotplug = nv_enable_hotplug_ck804,
.disable_hotplug= nv_disable_hotplug_ck804,
.check_hotplug = nv_check_hotplug_ck804,
},
{ .host_type = MCP51,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
},
{ .host_type = MCP55,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
},
{ .host_type = ADMA,
.enable_hotplug = nv_enable_hotplug_adma,
.disable_hotplug= nv_disable_hotplug_adma,
.check_hotplug = nv_check_hotplug_adma,
},
};
struct nv_host
{
struct nv_host_desc *host_desc;
unsigned long host_flags;
};
static struct pci_driver nv_pci_driver = {
.name = DRV_NAME,
.id_table = nv_pci_tbl,
.probe = nv_init_one,
.remove = ata_pci_remove_one,
};
static struct scsi_host_template nv_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
.ioctl = ata_scsi_ioctl,
.queuecommand = ata_scsi_queuecmd,
.eh_strategy_handler = ata_scsi_error,
.can_queue = ATA_DEF_QUEUE,
.this_id = ATA_SHT_THIS_ID,
.sg_tablesize = LIBATA_MAX_PRD,
.max_sectors = ATA_MAX_SECTORS,
.cmd_per_lun = ATA_SHT_CMD_PER_LUN,
.emulated = ATA_SHT_EMULATED,
.use_clustering = ATA_SHT_USE_CLUSTERING,
.proc_name = DRV_NAME,
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
};
static struct scsi_host_template nv_adma_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
.ioctl = ata_scsi_ioctl,
.queuecommand = ata_scsi_queuecmd,
.eh_strategy_handler = ata_scsi_error,
.can_queue = ATA_DEF_QUEUE,
.this_id = ATA_SHT_THIS_ID,
.sg_tablesize = NV_ADMA_SGTBL_LEN,
.max_sectors = ATA_MAX_SECTORS,
.cmd_per_lun = ATA_SHT_CMD_PER_LUN,
.emulated = ATA_SHT_EMULATED,
.use_clustering = ATA_SHT_USE_CLUSTERING,
.proc_name = DRV_NAME,
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
};
static const struct ata_port_operations nv_ops = {
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
.tf_read = ata_tf_read,
.exec_command = ata_exec_command,
.check_status = ata_check_status,
.dev_select = ata_std_dev_select,
.phy_reset = sata_phy_reset,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
.bmdma_stop = ata_bmdma_stop,
.bmdma_status = ata_bmdma_status,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
.eng_timeout = ata_eng_timeout,
.irq_handler = nv_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
.port_start = ata_port_start,
.port_stop = ata_port_stop,
.host_stop = nv_host_stop,
};
static const struct ata_port_operations nv_adma_ops = {
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
.tf_read = ata_tf_read,
.exec_command = ata_exec_command,
.check_status = ata_check_status,
.dev_select = ata_std_dev_select,
.phy_reset = sata_phy_reset,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
.bmdma_stop = nv_adma_bmdma_stop,
.bmdma_status = nv_adma_bmdma_status,
.qc_prep = nv_adma_qc_prep,
.qc_issue = nv_adma_qc_issue,
.eng_timeout = nv_adma_eng_timeout,
.irq_handler = nv_interrupt,
.irq_clear = nv_adma_irq_clear,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
.port_start = nv_adma_port_start,
.port_stop = nv_adma_port_stop,
.host_stop = nv_host_stop,
};
static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb)
{
unsigned int idx = 0;
cpb[idx++] = cpu_to_le16((ATA_REG_DEVICE << 8) | tf->device | WNB);
if ((tf->flags & ATA_TFLAG_LBA48) == 0) {
cpb[idx++] = cpu_to_le16(IGN);
cpb[idx++] = cpu_to_le16(IGN);
cpb[idx++] = cpu_to_le16(IGN);
cpb[idx++] = cpu_to_le16(IGN);
cpb[idx++] = cpu_to_le16(IGN);
}
else {
cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->hob_feature);
cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->hob_nsect);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->hob_lbal);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->hob_lbam);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->hob_lbah);
}
cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->feature);
cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->nsect);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->lbal);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->lbam);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->lbah);
cpb[idx++] = cpu_to_le16((ATA_REG_CMD << 8) | tf->command | CMDEND);
return idx;
}
static inline void __iomem *__nv_adma_ctl_block(void __iomem *mmio,
unsigned int port_no)
{
mmio += NV_ADMA_PORT + port_no * NV_ADMA_PORT_SIZE;
return mmio;
}
static inline void __iomem *nv_adma_ctl_block(struct ata_port *ap)
{
return __nv_adma_ctl_block(ap->host_set->mmio_base, ap->port_no);
}
static inline void __iomem *nv_adma_gen_block(struct ata_port *ap)
{
return (ap->host_set->mmio_base + NV_ADMA_GEN);
}
static inline void __iomem *nv_adma_notifier_clear_block(struct ata_port *ap)
{
return (nv_adma_gen_block(ap) + NV_ADMA_NOTIFIER_CLEAR + (4 * ap->port_no));
}
static inline void nv_adma_reset_channel(struct ata_port *ap)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
u16 tmp;
// clear CPB fetch count
writew(0, mmio + NV_ADMA_CPB_COUNT);
// clear GO
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL);
udelay(1);
writew(tmp & ~NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL);
}
static inline int nv_adma_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
struct nv_adma_port_priv *pp = ap->private_data;
struct nv_adma_cpb *cpb = &pp->cpb[qc->tag];
u16 status;
u32 gen_ctl;
u16 flags;
int have_err = 0;
int handled = 0;
status = readw(mmio + NV_ADMA_STAT);
// if in ATA register mode, use standard ata interrupt handler
if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
VPRINTK("in ATA register mode\n");
return ata_host_intr(ap, qc);
}
gen_ctl = readl(nv_adma_gen_block(ap) + NV_ADMA_GEN_CTL);
if (!NV_ADMA_CHECK_INTR(gen_ctl, ap->port_no))
return 0;
if (!pp->notifier && !pp->notifier_error) {
if (status) {
VPRINTK("XXX no notifier, but status 0x%x\n", status);
#ifdef DEBUG
nv_adma_dump_port(ap);
nv_adma_dump_cpb(cpb);
#endif
} else
return 0;
}
if (pp->notifier_error) {
have_err = 1;
handled = 1;
}
if (status & NV_ADMA_STAT_TIMEOUT) {
VPRINTK("timeout, stat = 0x%x\n", status);
have_err = 1;
handled = 1;
}
if (status & NV_ADMA_STAT_CPBERR) {
VPRINTK("CPB error, stat = 0x%x\n", status);
have_err = 1;
handled = 1;
}
if (status & NV_ADMA_STAT_STOPPED) {
VPRINTK("ADMA stopped, stat = 0x%x, resp_flags = 0x%x\n", status, cpb->resp_flags);
if (!(status & NV_ADMA_STAT_DONE)) {
have_err = 1;
handled = 1;
}
}
if (status & NV_ADMA_STAT_CMD_COMPLETE) {
VPRINTK("ADMA command complete, stat = 0x%x\n", status);
}
if (status & NV_ADMA_STAT_DONE) {
flags = cpb->resp_flags;
VPRINTK("CPB done, stat = 0x%x, flags = 0x%x\n", status, flags);
handled = 1;
if (!(status & NV_ADMA_STAT_IDLE)) {
VPRINTK("XXX CPB done, but not idle\n");
}
if (flags & NV_CPB_RESP_DONE) {
VPRINTK("CPB flags done, flags = 0x%x\n", flags);
}
if (flags & NV_CPB_RESP_ATA_ERR) {
VPRINTK("CPB flags ATA err, flags = 0x%x\n", flags);
have_err = 1;
}
if (flags & NV_CPB_RESP_CMD_ERR) {
VPRINTK("CPB flags CMD err, flags = 0x%x\n", flags);
have_err = 1;
}
if (flags & NV_CPB_RESP_CPB_ERR) {
VPRINTK("CPB flags CPB err, flags = 0x%x\n", flags);
have_err = 1;
}
}
// clear status
writew(status, mmio + NV_ADMA_STAT);
if (handled) {
u8 ata_status = readb(mmio + (ATA_REG_STATUS * 4));
qc->err_mask |= ac_err_mask(have_err ? (ata_status | ATA_ERR) : ata_status);
ata_qc_complete(qc);
}
return handled; /* irq handled */
}
/* FIXME: The hardware provides the necessary SATA PHY controls
* to support ATA_FLAG_SATA_RESET. However, it is currently
* necessary to disable that flag, to solve misdetection problems.
* See http://bugme.osdl.org/show_bug.cgi?id=3352 for more info.
*
* This problem really needs to be investigated further. But in the
* meantime, we avoid ATA_FLAG_SATA_RESET to get people working.
*/
static struct ata_port_info nv_port_info = {
.sht = &nv_sht,
.host_flags = ATA_FLAG_SATA |
/* ATA_FLAG_SATA_RESET | */
ATA_FLAG_SRST |
ATA_FLAG_NO_LEGACY,
.pio_mask = NV_PIO_MASK,
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
.port_ops = &nv_ops,
};
static struct ata_port_info nv_adma_port_info = {
.sht = &nv_adma_sht,
.host_flags = ATA_FLAG_SATA |
/* ATA_FLAG_SATA_RESET | */
ATA_FLAG_SRST |
ATA_FLAG_MMIO |
ATA_FLAG_NO_LEGACY,
.pio_mask = NV_PIO_MASK,
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
.port_ops = &nv_adma_ops,
};
MODULE_AUTHOR("NVIDIA");
MODULE_DESCRIPTION("low-level driver for NVIDIA nForce SATA controller");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, nv_pci_tbl);
MODULE_VERSION(DRV_VERSION);
static inline void nv_enable_adma_space (struct pci_dev *pdev)
{
u8 regval;
VPRINTK("ENTER\n");
pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val);
regval |= NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
}
static inline void nv_disable_adma_space (struct pci_dev *pdev)
{
u8 regval;
VPRINTK("ENTER\n");
pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val);
regval &= ~NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
}
static void nv_adma_irq_clear(struct ata_port *ap)
{
/* TODO */
}
static u8 nv_adma_bmdma_status(struct ata_port *ap)
{
return inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
}
static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc)
{
/* TODO */
}
static irqreturn_t nv_interrupt (int irq, void *dev_instance,
struct pt_regs *regs)
{
struct ata_host_set *host_set = dev_instance;
struct nv_host *host = host_set->private_data;
unsigned int i;
unsigned int handled = 0;
unsigned long flags;
spin_lock_irqsave(&host_set->lock, flags);
for (i = 0; i < host_set->n_ports; i++) {
struct ata_port *ap = host_set->ports[i];
struct nv_adma_port_priv *pp = ap->private_data;
if (ap &&
!(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) {
void __iomem *mmio = nv_adma_ctl_block(ap);
struct ata_queued_cmd *qc;
// read notifiers
pp->notifier = readl(mmio + NV_ADMA_NOTIFIER);
pp->notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
qc = ata_qc_from_tag(ap, ap->active_tag);
if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
if (host->host_desc->host_type == ADMA)
handled += nv_adma_host_intr(ap, qc);
else
handled += ata_host_intr(ap, qc);
}
}
}
if (host->host_desc->check_hotplug)
host->host_desc->check_hotplug(host_set);
// clear notifier
if (handled) {
for (i = 0; i < host_set->n_ports; i++) {
struct ata_port *ap = host_set->ports[i];
struct nv_adma_port_priv *pp = ap->private_data;
writel(pp->notifier | pp->notifier_error,
nv_adma_notifier_clear_block(ap));
}
}
spin_unlock_irqrestore(&host_set->lock, flags);
return IRQ_RETVAL(handled);
}
static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg)
{
struct ata_host_set *host_set = ap->host_set;
struct nv_host *host = host_set->private_data;
u32 val = 0;
VPRINTK("ENTER\n");
VPRINTK("reading SCR reg %d, got 0x%08x\n", sc_reg, val);
if (sc_reg > SCR_CONTROL)
return 0xffffffffU;
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
val = readl((void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4));
else
val = inl(ap->ioaddr.scr_addr + (sc_reg * 4));
VPRINTK("reading SCR reg %d, got 0x%08x\n", sc_reg, val);
return val;
}
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
{
struct ata_host_set *host_set = ap->host_set;
struct nv_host *host = host_set->private_data;
VPRINTK("ENTER\n");
VPRINTK("writing SCR reg %d with 0x%08x\n", sc_reg, val);
if (sc_reg > SCR_CONTROL)
return;
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
writel(val, (void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4));
else
outl(val, ap->ioaddr.scr_addr + (sc_reg * 4));
}
static void nv_host_stop (struct ata_host_set *host_set)
{
struct nv_host *host = host_set->private_data;
struct pci_dev *pdev = to_pci_dev(host_set->dev);
VPRINTK("ENTER\n");
// Disable hotplug event interrupts.
if (host->host_desc->disable_hotplug)
host->host_desc->disable_hotplug(host_set);
kfree(host);
if (host_set->mmio_base)
pci_iounmap(pdev, host_set->mmio_base);
}
static int nv_adma_port_start(struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
struct nv_adma_port_priv *pp;
int rc;
void *mem;
dma_addr_t mem_dma;
void __iomem *mmio = nv_adma_ctl_block(ap);
VPRINTK("ENTER\n");
nv_adma_reset_channel(ap);
#ifdef DEBUG
VPRINTK("after reset:\n");
nv_adma_dump_port(ap);
#endif
rc = ata_port_start(ap);
if (rc)
return rc;
pp = kmalloc(sizeof(*pp), GFP_KERNEL);
if (!pp) {
rc = -ENOMEM;
goto err_out;
}
memset(pp, 0, sizeof(*pp));
mem = dma_alloc_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ,
&mem_dma, GFP_KERNEL);
VPRINTK("dma memory: vaddr = 0x%08x, paddr = 0x%08x\n", (u32)mem, (u32)mem_dma);
if (!mem) {
rc = -ENOMEM;
goto err_out_kfree;
}
memset(mem, 0, NV_ADMA_PORT_PRIV_DMA_SZ);
/*
* First item in chunk of DMA memory:
* 128-byte command parameter block (CPB)
* one for each command tag
*/
pp->cpb = mem;
pp->cpb_dma = mem_dma;
VPRINTK("cpb = 0x%08x, cpb_dma = 0x%08x\n", (u32)pp->cpb, (u32)pp->cpb_dma);
writel(mem_dma, mmio + NV_ADMA_CPB_BASE_LOW);
writel(0, mmio + NV_ADMA_CPB_BASE_HIGH);
mem += NV_ADMA_CAN_QUEUE * NV_ADMA_CPB_SZ;
mem_dma += NV_ADMA_CAN_QUEUE * NV_ADMA_CPB_SZ;
/*
* Second item: block of ADMA_SGTBL_LEN s/g entries
*/
pp->aprd = mem;
pp->aprd_dma = mem_dma;
VPRINTK("aprd = 0x%08x, aprd_dma = 0x%08x\n", (u32)pp->aprd, (u32)pp->aprd_dma);
ap->private_data = pp;
// clear any outstanding interrupt conditions
writew(0xffff, mmio + NV_ADMA_STAT);
// initialize port variables
// pp->cpb_idx = 0;
pp->flags = NV_ADMA_PORT_REGISTER_MODE;
// make sure controller is in ATA register mode
nv_adma_register_mode(ap);
return 0;
err_out_kfree:
kfree(pp);
err_out:
ata_port_stop(ap);
return rc;
}
static void nv_adma_port_stop(struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
struct nv_adma_port_priv *pp = ap->private_data;
void __iomem *mmio = nv_adma_ctl_block(ap);
VPRINTK("ENTER\n");
writew(0, mmio + NV_ADMA_CTL);
ap->private_data = NULL;
dma_free_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ, pp->cpb, pp->cpb_dma);
kfree(pp);
ata_port_stop(ap);
}
static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port)
{
void __iomem *mmio = probe_ent->mmio_base;
struct ata_ioports *ioport = &probe_ent->port[port];
VPRINTK("ENTER\n");
mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE;
ioport->cmd_addr = (unsigned long) mmio;
ioport->data_addr = (unsigned long) mmio + (ATA_REG_DATA * 4);
ioport->error_addr =
ioport->feature_addr = (unsigned long) mmio + (ATA_REG_ERR * 4);
ioport->nsect_addr = (unsigned long) mmio + (ATA_REG_NSECT * 4);
ioport->lbal_addr = (unsigned long) mmio + (ATA_REG_LBAL * 4);
ioport->lbam_addr = (unsigned long) mmio + (ATA_REG_LBAM * 4);
ioport->lbah_addr = (unsigned long) mmio + (ATA_REG_LBAH * 4);
ioport->device_addr = (unsigned long) mmio + (ATA_REG_DEVICE * 4);
ioport->status_addr =
ioport->command_addr = (unsigned long) mmio + (ATA_REG_STATUS * 4);
ioport->altstatus_addr =
ioport->ctl_addr = (unsigned long) mmio + 0x20;
}
static int nv_adma_host_init(struct ata_probe_ent *probe_ent)
{
struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
unsigned int i;
u32 tmp32;
VPRINTK("ENTER\n");
probe_ent->n_ports = NV_PORTS;
nv_enable_adma_space(pdev);
// enable ADMA on the ports
pci_read_config_dword(pdev, NV_MCP_SATA_CFG_20, &tmp32);
tmp32 |= NV_MCP_SATA_CFG_20_PORT0_EN |
NV_MCP_SATA_CFG_20_PORT0_PWB_EN |
NV_MCP_SATA_CFG_20_PORT1_EN |
NV_MCP_SATA_CFG_20_PORT1_PWB_EN;
pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32);
for (i = 0; i < probe_ent->n_ports; i++)
nv_adma_setup_port(probe_ent, i);
for (i = 0; i < probe_ent->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i);
u16 tmp;
/* enable interrupt, clear reset if not already clear */
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_AIEN, mmio + NV_ADMA_CTL);
}
pci_set_master(pdev);
return 0;
}
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
struct nv_host *host;
struct ata_port_info *ppi;
struct ata_probe_ent *probe_ent;
struct nv_host_desc *host_desc;
int pci_dev_busy = 0;
int rc;
u32 bar;
VPRINTK("ENTER\n");
// Make sure this is a SATA controller by counting the number of bars
// (NVIDIA SATA controllers will always have six bars). Otherwise,
// it's an IDE controller and we ignore it.
for (bar=0; bar<6; bar++)
if (pci_resource_start(pdev, bar) == 0)
return -ENODEV;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
rc = pci_enable_device(pdev);
if (rc)
goto err_out;
rc = pci_request_regions(pdev, DRV_NAME);
if (rc) {
pci_dev_busy = 1;
goto err_out_disable;
}
rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
goto err_out_regions;
rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
goto err_out_regions;
rc = -ENOMEM;
host_desc = &nv_device_tbl[ent->driver_data];
/* select ADMA or legacy PCI IDE BMDMA controller operation */
if (host_desc->host_type == ADMA) {
#ifdef NV_ADMA_NCQ
ppi->host_flags |= ATA_FLAG_NCQ;
ppi->sht->can_queue = NV_ADMA_CAN_QUEUE;
#endif
ppi = &nv_adma_port_info;
} else
ppi = &nv_port_info;
probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent)
goto err_out_regions;
host = kmalloc(sizeof(struct nv_host), GFP_KERNEL);
if (!host)
goto err_out_free_ent;
memset(host, 0, sizeof(struct nv_host));
host->host_desc = host_desc;
probe_ent->private_data = host;
if (pci_resource_flags(pdev, 5) & IORESOURCE_MEM)
host->host_flags |= NV_HOST_FLAGS_SCR_MMIO;
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) {
unsigned long base;
probe_ent->mmio_base = pci_iomap(pdev, 5, 0);
if (probe_ent->mmio_base == NULL) {
rc = -EIO;
goto err_out_free_host;
}
base = (unsigned long)probe_ent->mmio_base;
VPRINTK("BAR5 base is at 0x%x\n", (u32)base);
probe_ent->port[0].scr_addr =
base + NV_PORT0_SCR_REG_OFFSET;
probe_ent->port[1].scr_addr =
base + NV_PORT1_SCR_REG_OFFSET;
} else {
probe_ent->port[0].scr_addr =
pci_resource_start(pdev, 5) | NV_PORT0_SCR_REG_OFFSET;
probe_ent->port[1].scr_addr =
pci_resource_start(pdev, 5) | NV_PORT1_SCR_REG_OFFSET;
}
pci_set_master(pdev);
if (ent->driver_data == ADMA) {
rc = nv_adma_host_init(probe_ent);
if (rc)
goto err_out_iounmap;
}
rc = ata_device_add(probe_ent);
if (rc != NV_PORTS)
goto err_out_iounmap;
// Enable hotplug event interrupts.
if (host->host_desc->enable_hotplug)
host->host_desc->enable_hotplug(probe_ent);
kfree(probe_ent);
return 0;
err_out_iounmap:
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
pci_iounmap(pdev, probe_ent->mmio_base);
err_out_free_host:
kfree(host);
err_out_free_ent:
kfree(probe_ent);
err_out_regions:
pci_release_regions(pdev);
err_out_disable:
if (!pci_dev_busy)
pci_disable_device(pdev);
err_out:
return rc;
}
static void nv_adma_eng_timeout(struct ata_port *ap)
{
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
struct nv_adma_port_priv *pp = ap->private_data;
u8 drv_stat;
VPRINTK("ENTER\n");
if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
ata_eng_timeout(ap);
goto out;
}
if (!qc) {
printk(KERN_ERR "ata%u: BUG: timeout without command\n",
ap->id);
goto out;
}
// spin_lock_irqsave(&host_set->lock, flags);
qc->scsidone = scsi_finish_command;
drv_stat = ata_chk_status(ap);
printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x\n",
ap->id, qc->tf.command, drv_stat);
// reset channel
nv_adma_reset_channel(ap);
/* complete taskfile transaction */
qc->err_mask |= ac_err_mask(drv_stat);
ata_qc_complete(qc);
// spin_unlock_irqrestore(&host_set->lock, flags);
out:
DPRINTK("EXIT\n");
}
static void nv_adma_qc_prep(struct ata_queued_cmd *qc)
{
struct nv_adma_port_priv *pp = qc->ap->private_data;
struct nv_adma_cpb *cpb = &pp->cpb[qc->tag];
VPRINTK("ENTER\n");
VPRINTK("qc->flags = 0x%x\n", (u32)qc->flags);
if (!(qc->flags & ATA_QCFLAG_DMAMAP)) {
ata_qc_prep(qc);
return;
}
memset(cpb, 0, sizeof(struct nv_adma_cpb));
cpb->ctl_flags = NV_CPB_CTL_CPB_VALID |
NV_CPB_CTL_APRD_VALID |
NV_CPB_CTL_IEN;
cpb->len = 3;
cpb->tag = qc->tag;
cpb->next_cpb_idx = 0;
#ifdef NV_ADMA_NCQ
// turn on NCQ flags for NCQ commands
if (qc->flags & ATA_QCFLAG_NCQ)
cpb->ctl_flags |= NV_CPB_CTL_QUEUE | NV_CPB_CTL_FPDMA;
#endif
nv_adma_tf_to_cpb(&qc->tf, cpb->tf);
nv_adma_fill_sg(qc, cpb);
}
static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb)
{
struct nv_adma_port_priv *pp = qc->ap->private_data;
unsigned int idx;
struct nv_adma_prd *aprd;
struct scatterlist *sg;
VPRINTK("ENTER\n");
idx = 0;
ata_for_each_sg(sg, qc) {
aprd = (idx < 5) ? &cpb->aprd[idx] : &pp->aprd[idx-5];
nv_adma_fill_aprd(qc, sg, idx, aprd);
idx++;
}
if (idx > 5)
cpb->next_aprd = (u64)(pp->aprd_dma + NV_ADMA_APRD_SZ * qc->tag);
}
static void nv_adma_fill_aprd(struct ata_queued_cmd *qc,
struct scatterlist *sg,
int idx,
struct nv_adma_prd *aprd)
{
u32 sg_len, addr, flags;
memset(aprd, 0, sizeof(struct nv_adma_prd));
addr = sg_dma_address(sg);
sg_len = sg_dma_len(sg);
flags = 0;
if (qc->tf.flags & ATA_TFLAG_WRITE)
flags |= NV_APRD_WRITE;
if (idx == qc->n_elem - 1)
flags |= NV_APRD_END;
else if (idx != 4)
flags |= NV_APRD_CONT;
aprd->addr = cpu_to_le32(addr);
aprd->len = cpu_to_le32(sg_len); /* len in bytes */
aprd->flags = cpu_to_le32(flags);
}
static void nv_adma_register_mode(struct ata_port *ap)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
struct nv_adma_port_priv *pp = ap->private_data;
u16 tmp;
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
pp->flags |= NV_ADMA_PORT_REGISTER_MODE;
}
static void nv_adma_mode(struct ata_port *ap)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
struct nv_adma_port_priv *pp = ap->private_data;
u16 tmp;
if (!(pp->flags & NV_ADMA_PORT_REGISTER_MODE))
return;
#if 0
nv_adma_reset_channel(ap);
#endif
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
pp->flags &= ~NV_ADMA_PORT_REGISTER_MODE;
}
static int nv_adma_qc_issue(struct ata_queued_cmd *qc)
{
#if 0
struct nv_adma_port_priv *pp = qc->ap->private_data;
#endif
void __iomem *mmio = nv_adma_ctl_block(qc->ap);
VPRINTK("ENTER\n");
if (!(qc->flags & ATA_QCFLAG_DMAMAP)) {
VPRINTK("no dmamap, using ATA register mode: 0x%x\n", (u32)qc->flags);
// use ATA register mode
nv_adma_register_mode(qc->ap);
return ata_qc_issue_prot(qc);
} else
nv_adma_mode(qc->ap);
#if 0
nv_adma_dump_port(qc->ap);
nv_adma_dump_cpb(&pp->cpb[qc->tag]);
if (qc->n_elem > 5) {
int i;
for (i = 0; i < qc->n_elem - 5; i++) {
nv_adma_dump_aprd(&pp->aprd[i]);
}
}
#endif
//
// write append register, command tag in lower 8 bits
// and (number of cpbs to append -1) in top 8 bits
//
mb();
writew(qc->tag, mmio + NV_ADMA_APPEND);
VPRINTK("EXIT\n");
return 0;
}
static void nv_enable_hotplug(struct ata_probe_ent *probe_ent)
{
u8 intr_mask;
outb(NV_INT_STATUS_HOTPLUG,
probe_ent->port[0].scr_addr + NV_INT_STATUS);
intr_mask = inb(probe_ent->port[0].scr_addr + NV_INT_ENABLE);
intr_mask |= NV_INT_ENABLE_HOTPLUG;
outb(intr_mask, probe_ent->port[0].scr_addr + NV_INT_ENABLE);
}
static void nv_disable_hotplug(struct ata_host_set *host_set)
{
u8 intr_mask;
intr_mask = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE);
intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE);
}
static void nv_check_hotplug(struct ata_host_set *host_set)
{
u8 intr_status;
intr_status = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS);
// Clear interrupt status.
outb(0xff, host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS);
if (intr_status & NV_INT_STATUS_HOTPLUG) {
if (intr_status & NV_INT_STATUS_PDEV_ADDED)
printk(KERN_WARNING "nv_sata: "
"Primary device added\n");
if (intr_status & NV_INT_STATUS_PDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Primary device removed\n");
if (intr_status & NV_INT_STATUS_SDEV_ADDED)
printk(KERN_WARNING "nv_sata: "
"Secondary device added\n");
if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Secondary device removed\n");
}
}
static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent)
{
struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
u8 intr_mask;
nv_enable_adma_space(pdev);
writeb(NV_INT_STATUS_HOTPLUG, probe_ent->mmio_base + NV_INT_STATUS_CK804);
intr_mask = readb(probe_ent->mmio_base + NV_INT_ENABLE_CK804);
intr_mask |= NV_INT_ENABLE_HOTPLUG;
writeb(intr_mask, probe_ent->mmio_base + NV_INT_ENABLE_CK804);
}
static void nv_disable_hotplug_ck804(struct ata_host_set *host_set)
{
struct pci_dev *pdev = to_pci_dev(host_set->dev);
u8 intr_mask;
intr_mask = readb(host_set->mmio_base + NV_INT_ENABLE_CK804);
intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
writeb(intr_mask, host_set->mmio_base + NV_INT_ENABLE_CK804);
nv_disable_adma_space(pdev);
}
static void nv_check_hotplug_ck804(struct ata_host_set *host_set)
{
u8 intr_status;
intr_status = readb(host_set->mmio_base + NV_INT_STATUS_CK804);
// Clear interrupt status.
writeb(0xff, host_set->mmio_base + NV_INT_STATUS_CK804);
if (intr_status & NV_INT_STATUS_HOTPLUG) {
if (intr_status & NV_INT_STATUS_PDEV_ADDED)
printk(KERN_WARNING "nv_sata: "
"Primary device added\n");
if (intr_status & NV_INT_STATUS_PDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Primary device removed\n");
if (intr_status & NV_INT_STATUS_SDEV_ADDED)
printk(KERN_WARNING "nv_sata: "
"Secondary device added\n");
if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Secondary device removed\n");
}
}
static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent)
{
struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
unsigned int i;
u16 tmp;
nv_enable_adma_space(pdev);
for (i = 0; i < probe_ent->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i);
writew(NV_ADMA_STAT_HOTPLUG | NV_ADMA_STAT_HOTUNPLUG,
mmio + NV_ADMA_STAT);
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL);
}
}
static void nv_disable_hotplug_adma(struct ata_host_set *host_set)
{
unsigned int i;
u16 tmp;
for (i = 0; i < host_set->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i);
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp & ~NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL);
}
}
static void nv_check_hotplug_adma(struct ata_host_set *host_set)
{
unsigned int i;
u16 adma_status;
for (i = 0; i < host_set->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i);
adma_status = readw(mmio + NV_ADMA_STAT);
if (adma_status & NV_ADMA_STAT_HOTPLUG) {
printk(KERN_WARNING "nv_sata: "
"port %d device added\n", i);
writew(NV_ADMA_STAT_HOTPLUG, mmio + NV_ADMA_STAT);
}
if (adma_status & NV_ADMA_STAT_HOTUNPLUG) {
printk(KERN_WARNING "nv_sata: "
"port %d device removed\n", i);
writew(NV_ADMA_STAT_HOTUNPLUG, mmio + NV_ADMA_STAT);
}
}
}
static int __init nv_init(void)
{
return pci_module_init(&nv_pci_driver);
}
static void __exit nv_exit(void)
{
pci_unregister_driver(&nv_pci_driver);
}
module_init(nv_init);
module_exit(nv_exit);
#ifdef DEBUG
static void nv_adma_dump_aprd(struct nv_adma_prd *aprd)
{
printk("%016llx %08x %02x %s %s %s\n",
aprd->addr,
aprd->len,
aprd->flags,
(aprd->flags & NV_APRD_WRITE) ? "WRITE" : " ",
(aprd->flags & NV_APRD_END) ? "END" : " ",
(aprd->flags & NV_APRD_CONT) ? "CONT" : " ");
}
static void nv_adma_dump_iomem(void __iomem *m, int len)
{
int i, j;
for (i = 0; i < len/16; i++) {
printk(KERN_WARNING "%02x: ", 16*i);
for (j = 0; j < 16; j++) {
printk("%02x%s", (u32)readb(m + 16*i + j),
(j == 7) ? "-" : " ");
}
printk("\n");
}
}
static void nv_adma_dump_cpb_tf(u16 tf)
{
printk("0x%04x %s %s %s 0x%02x 0x%02x\n",
tf,
(tf & CMDEND) ? "END" : " ",
(tf & WNB) ? "WNB" : " ",
(tf & IGN) ? "IGN" : " ",
((tf >> 8) & 0x1f),
(tf & 0xff));
}
static void nv_adma_dump_port(struct ata_port *ap)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
nv_adma_dump_iomem(mmio, NV_ADMA_PORT_SIZE);
}
static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb)
{
int i;
printk("resp_flags: 0x%02x\n", cpb->resp_flags);
printk("ctl_flags: 0x%02x\n", cpb->ctl_flags);
printk("len: 0x%02x\n", cpb->len);
printk("tag: 0x%02x\n", cpb->tag);
printk("next_cpb_idx: 0x%02x\n", cpb->next_cpb_idx);
printk("tf:\n");
for (i=0; i<12; i++) {
nv_adma_dump_cpb_tf(cpb->tf[i]);
}
printk("aprd:\n");
for (i=0; i<5; i++) {
nv_adma_dump_aprd(&cpb->aprd[i]);
}
printk("next_aprd: 0x%016llx\n", cpb->next_aprd);
}
#endif
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-18 0:56 ` Jeff Garzik
@ 2006-03-18 8:06 ` Bill Rugolsky Jr.
2006-03-18 8:56 ` Jeff Garzik
0 siblings, 1 reply; 14+ messages in thread
From: Bill Rugolsky Jr. @ 2006-03-18 8:06 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linux-kernel
On Fri, Mar 17, 2006 at 07:56:53PM -0500, Jeff Garzik wrote:
> Could I get you to diff against the attached version?
Certainly.
I took the opportunity to modify my source file to fix a pair of typos in
the pci_device_id changes, and make cosmetic changes by switching to enums
rather than #defines, removing unnecessary braces around single statements,
and fixing whitespace. That reduced the noise considerably, and made the
NV_ADMA_CTL_READ_NON_COHERENT difference manifest.
-Bill
--- sata_nv.c.garzik 2006-03-18 01:50:18.518554000 -0500
+++ sata_nv.c 2006-03-18 02:53:18.000000000 -0500
@@ -29,6 +29,38 @@
* NV-specific details such as register offsets, SATA phy location,
* hotplug info, etc.
*
+ * 0.11
+ * - Added support for ADMA. Disabled by default. Use the module
+ * option adma=on to enable it for supported chipsets.
+ *
+ * 0.10
+ * - Fixed spurious interrupts issue seen with the Maxtor 6H500F0 500GB
+ * drive. Also made the check_hotplug() callbacks return whether there
+ * was a hotplug interrupt or not. This was not the source of the
+ * spurious interrupts, but is the right thing to do anyway.
+ *
+ * 0.09
+ * - Fixed bug introduced by 0.08's MCP51 and MCP55 support.
+ *
+ * 0.08
+ * - Added support for MCP51 and MCP55.
+ *
+ * 0.07
+ * - Added support for RAID class code.
+ *
+ * 0.06
+ * - Added generic SATA support by using a pci_device_id that filters on
+ * the IDE storage class code.
+ *
+ * 0.03
+ * - Fixed a bug where the hotplug handlers for non-CK804/MCP04 were using
+ * mmio_base, which is only set for the CK804/MCP04 case.
+ *
+ * 0.02
+ * - Added support for CK804 SATA controller.
+ *
+ * 0.01
+ * - Initial revision.
*/
#include <linux/config.h>
@@ -47,7 +79,7 @@
//#define DEBUG
#define DRV_NAME "sata_nv"
-#define DRV_VERSION "0.8"
+#define DRV_VERSION "0.11-alpha"
enum {
NV_PORTS = 2,
@@ -148,7 +180,7 @@ enum {
NV_ADMA_CTL_CHANNEL_RESET = (1 << 5),
NV_ADMA_CTL_GO = (1 << 7),
NV_ADMA_CTL_AIEN = (1 << 8),
- NV_ADMA_CTL_READ_NON_COHERENT = (1 << 11),
+ NV_ADMA_CTL_READ_NON_COHERENT = (2 << 11),
NV_ADMA_CTL_WRITE_NON_COHERENT = (1 << 12),
// CPB response flag bits
@@ -186,6 +218,10 @@ enum {
NV_ADMA_PORT_REGISTER_MODE = (1 << 0),
};
+#ifndef min
+#define min(x,y) ((x) < (y) ? x : y)
+#endif
+
struct nv_adma_prd {
u64 addr;
u32 len;
@@ -237,18 +273,23 @@ static irqreturn_t nv_interrupt (int irq
static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static void nv_host_stop (struct ata_host_set *host_set);
+static int nv_port_start(struct ata_port *ap);
+static void nv_port_stop(struct ata_port *ap);
static int nv_adma_port_start(struct ata_port *ap);
static void nv_adma_port_stop(struct ata_port *ap);
+static void nv_irq_clear(struct ata_port *ap);
static void nv_adma_irq_clear(struct ata_port *ap);
static void nv_enable_hotplug(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug(struct ata_host_set *host_set);
-static void nv_check_hotplug(struct ata_host_set *host_set);
+static int nv_check_hotplug(struct ata_host_set *host_set);
static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug_ck804(struct ata_host_set *host_set);
-static void nv_check_hotplug_ck804(struct ata_host_set *host_set);
+static int nv_check_hotplug_ck804(struct ata_host_set *host_set);
static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug_adma(struct ata_host_set *host_set);
-static void nv_check_hotplug_adma(struct ata_host_set *host_set);
+static int nv_check_hotplug_adma(struct ata_host_set *host_set);
+static void nv_qc_prep(struct ata_queued_cmd *qc);
+static int nv_qc_issue(struct ata_queued_cmd *qc);
static int nv_adma_qc_issue(struct ata_queued_cmd *qc);
static void nv_adma_qc_prep(struct ata_queued_cmd *qc);
static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb);
@@ -256,8 +297,11 @@ static void nv_adma_fill_sg(struct ata_q
static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, struct scatterlist *sg, int idx, struct nv_adma_prd *aprd);
static void nv_adma_register_mode(struct ata_port *ap);
static void nv_adma_mode(struct ata_port *ap);
+static u8 nv_bmdma_status(struct ata_port *ap);
static u8 nv_adma_bmdma_status(struct ata_port *ap);
+static void nv_bmdma_stop(struct ata_queued_cmd *qc);
static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc);
+static void nv_eng_timeout(struct ata_port *ap);
static void nv_adma_eng_timeout(struct ata_port *ap);
#ifdef DEBUG
static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb);
@@ -267,40 +311,40 @@ static void nv_adma_dump_port(struct ata
static void nv_adma_dump_iomem(void __iomem *m, int len);
#endif
+
+static int adma_enabled;
+
enum nv_host_type
{
- GENERIC,
- NFORCE2,
- NFORCE3,
- CK804,
- MCP51,
- MCP55,
- ADMA
+ GENERIC = 0x0,
+ NFORCE = 0x1,
+ CK804 = 0x2,
+ ADMA = 0x4
};
static const struct pci_device_id nv_pci_tbl[] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 | ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 | ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 | ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 | ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC },
{ PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC },
@@ -317,7 +361,7 @@ struct nv_host_desc
enum nv_host_type host_type;
void (*enable_hotplug)(struct ata_probe_ent *probe_ent);
void (*disable_hotplug)(struct ata_host_set *host_set);
- void (*check_hotplug)(struct ata_host_set *host_set);
+ int (*check_hotplug)(struct ata_host_set *host_set);
};
static struct nv_host_desc nv_device_tbl[] = {
@@ -328,13 +372,7 @@ static struct nv_host_desc nv_device_tbl
.check_hotplug = NULL,
},
{
- .host_type = NFORCE2,
- .enable_hotplug = nv_enable_hotplug,
- .disable_hotplug= nv_disable_hotplug,
- .check_hotplug = nv_check_hotplug,
- },
- {
- .host_type = NFORCE3,
+ .host_type = NFORCE,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
@@ -344,16 +382,6 @@ static struct nv_host_desc nv_device_tbl
.disable_hotplug= nv_disable_hotplug_ck804,
.check_hotplug = nv_check_hotplug_ck804,
},
- { .host_type = MCP51,
- .enable_hotplug = nv_enable_hotplug,
- .disable_hotplug= nv_disable_hotplug,
- .check_hotplug = nv_check_hotplug,
- },
- { .host_type = MCP55,
- .enable_hotplug = nv_enable_hotplug,
- .disable_hotplug= nv_disable_hotplug,
- .check_hotplug = nv_check_hotplug,
- },
{ .host_type = ADMA,
.enable_hotplug = nv_enable_hotplug_adma,
.disable_hotplug= nv_disable_hotplug_adma,
@@ -393,25 +421,6 @@ static struct scsi_host_template nv_sht
.bios_param = ata_std_bios_param,
};
-static struct scsi_host_template nv_adma_sht = {
- .module = THIS_MODULE,
- .name = DRV_NAME,
- .ioctl = ata_scsi_ioctl,
- .queuecommand = ata_scsi_queuecmd,
- .eh_strategy_handler = ata_scsi_error,
- .can_queue = ATA_DEF_QUEUE,
- .this_id = ATA_SHT_THIS_ID,
- .sg_tablesize = NV_ADMA_SGTBL_LEN,
- .max_sectors = ATA_MAX_SECTORS,
- .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
- .emulated = ATA_SHT_EMULATED,
- .use_clustering = ATA_SHT_USE_CLUSTERING,
- .proc_name = DRV_NAME,
- .dma_boundary = ATA_DMA_BOUNDARY,
- .slave_configure = ata_scsi_slave_config,
- .bios_param = ata_std_bios_param,
-};
-
static const struct ata_port_operations nv_ops = {
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
@@ -422,41 +431,17 @@ static const struct ata_port_operations
.phy_reset = sata_phy_reset,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
- .bmdma_stop = ata_bmdma_stop,
- .bmdma_status = ata_bmdma_status,
- .qc_prep = ata_qc_prep,
- .qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
- .irq_handler = nv_interrupt,
- .irq_clear = ata_bmdma_irq_clear,
- .scr_read = nv_scr_read,
- .scr_write = nv_scr_write,
- .port_start = ata_port_start,
- .port_stop = ata_port_stop,
- .host_stop = nv_host_stop,
-};
-
-static const struct ata_port_operations nv_adma_ops = {
- .port_disable = ata_port_disable,
- .tf_load = ata_tf_load,
- .tf_read = ata_tf_read,
- .exec_command = ata_exec_command,
- .check_status = ata_check_status,
- .dev_select = ata_std_dev_select,
- .phy_reset = sata_phy_reset,
- .bmdma_setup = ata_bmdma_setup,
- .bmdma_start = ata_bmdma_start,
- .bmdma_stop = nv_adma_bmdma_stop,
- .bmdma_status = nv_adma_bmdma_status,
- .qc_prep = nv_adma_qc_prep,
- .qc_issue = nv_adma_qc_issue,
- .eng_timeout = nv_adma_eng_timeout,
+ .bmdma_stop = nv_bmdma_stop,
+ .bmdma_status = nv_bmdma_status,
+ .qc_prep = nv_qc_prep,
+ .qc_issue = nv_qc_issue,
+ .eng_timeout = nv_eng_timeout,
.irq_handler = nv_interrupt,
- .irq_clear = nv_adma_irq_clear,
+ .irq_clear = nv_irq_clear,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
- .port_start = nv_adma_port_start,
- .port_stop = nv_adma_port_stop,
+ .port_start = nv_port_start,
+ .port_stop = nv_port_stop,
.host_stop = nv_host_stop,
};
@@ -646,19 +631,6 @@ static struct ata_port_info nv_port_info
.port_ops = &nv_ops,
};
-static struct ata_port_info nv_adma_port_info = {
- .sht = &nv_adma_sht,
- .host_flags = ATA_FLAG_SATA |
- /* ATA_FLAG_SATA_RESET | */
- ATA_FLAG_SRST |
- ATA_FLAG_MMIO |
- ATA_FLAG_NO_LEGACY,
- .pio_mask = NV_PIO_MASK,
- .mwdma_mask = NV_MWDMA_MASK,
- .udma_mask = NV_UDMA_MASK,
- .port_ops = &nv_adma_ops,
-};
-
MODULE_AUTHOR("NVIDIA");
MODULE_DESCRIPTION("low-level driver for NVIDIA nForce SATA controller");
MODULE_LICENSE("GPL");
@@ -687,19 +659,53 @@ static inline void nv_disable_adma_space
pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
}
+static void nv_irq_clear(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA)
+ nv_adma_irq_clear(ap);
+ else
+ ata_bmdma_irq_clear(ap);
+}
+
static void nv_adma_irq_clear(struct ata_port *ap)
{
- /* TODO */
+ /* FIXME: TODO */
+}
+
+static u8 nv_bmdma_status(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA)
+ return nv_adma_bmdma_status(ap);
+ else
+ return ata_bmdma_status(ap);
}
static u8 nv_adma_bmdma_status(struct ata_port *ap)
{
+ // FIXME: This is no different than ata_bmdma_status for PIO
return inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
}
+static void nv_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct ata_host_set *host_set = qc->ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA)
+ nv_adma_bmdma_stop(qc);
+ else
+ ata_bmdma_stop(qc);
+}
+
static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc)
{
- /* TODO */
+ /* FIXME: TODO */
}
static irqreturn_t nv_interrupt (int irq, void *dev_instance,
@@ -715,16 +721,18 @@ static irqreturn_t nv_interrupt (int irq
for (i = 0; i < host_set->n_ports; i++) {
struct ata_port *ap = host_set->ports[i];
- struct nv_adma_port_priv *pp = ap->private_data;
if (ap &&
!(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) {
- void __iomem *mmio = nv_adma_ctl_block(ap);
struct ata_queued_cmd *qc;
- // read notifiers
- pp->notifier = readl(mmio + NV_ADMA_NOTIFIER);
- pp->notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
+ if (host->host_desc->host_type == ADMA) {
+ struct nv_adma_port_priv *pp = ap->private_data;
+ void __iomem *mmio = nv_adma_ctl_block(ap);
+ // read notifiers
+ pp->notifier = readl(mmio + NV_ADMA_NOTIFIER);
+ pp->notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
+ }
qc = ata_qc_from_tag(ap, ap->active_tag);
if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
@@ -732,16 +740,21 @@ static irqreturn_t nv_interrupt (int irq
handled += nv_adma_host_intr(ap, qc);
else
handled += ata_host_intr(ap, qc);
+ } else {
+ // No request pending? Clear interrupt status
+ // anyway, in case there's one pending.
+ ap->ops->check_status(ap);
}
}
}
if (host->host_desc->check_hotplug)
- host->host_desc->check_hotplug(host_set);
+ // FIXME: do something with the return value
+ (void) host->host_desc->check_hotplug(host_set);
// clear notifier
- if (handled) {
+ if (handled && host->host_desc->host_type == ADMA) {
for (i = 0; i < host_set->n_ports; i++) {
struct ata_port *ap = host_set->ports[i];
struct nv_adma_port_priv *pp = ap->private_data;
@@ -811,6 +824,28 @@ static void nv_host_stop (struct ata_hos
pci_iounmap(pdev, host_set->mmio_base);
}
+static int nv_port_start(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA)
+ return nv_adma_port_start(ap);
+ else
+ return ata_port_start(ap);
+}
+
+static void nv_port_stop(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA)
+ nv_adma_port_stop(ap);
+ else
+ ata_port_stop(ap);
+}
+
static int nv_adma_port_start(struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
@@ -975,6 +1010,17 @@ static int nv_adma_host_init(struct ata_
return 0;
}
+static struct nv_host_desc *nv_find_host_desc(unsigned int host_type)
+{
+ int i;
+
+ for (i = 0 ; i < (sizeof nv_device_tbl)/(sizeof nv_device_tbl[0]); i++)
+ if (nv_device_tbl[i].host_type == host_type)
+ return &nv_device_tbl[i];
+ return NULL;
+}
+
+
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
@@ -1017,18 +1063,23 @@ static int nv_init_one (struct pci_dev *
rc = -ENOMEM;
- host_desc = &nv_device_tbl[ent->driver_data];
+ ppi = &nv_port_info;
+
+ if (adma_enabled && (ent->driver_data & ADMA))
+ host_desc = nv_find_host_desc(ADMA);
+ else
+ host_desc = nv_find_host_desc(ent->driver_data & ~ADMA);
- /* select ADMA or legacy PCI IDE BMDMA controller operation */
if (host_desc->host_type == ADMA) {
+ // ADMA overrides
+ ppi->host_flags |= ATA_FLAG_MMIO | ATA_FLAG_SATA_RESET;
#ifdef NV_ADMA_NCQ
ppi->host_flags |= ATA_FLAG_NCQ;
- ppi->sht->can_queue = NV_ADMA_CAN_QUEUE;
#endif
-
- ppi = &nv_adma_port_info;
- } else
- ppi = &nv_port_info;
+ ppi->sht->can_queue = NV_ADMA_CAN_QUEUE;
+ ppi->sht->sg_tablesize = NV_ADMA_SGTBL_LEN;
+// ppi->port_ops->irq_handler = nv_adma_interrupt;
+ }
probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent)
@@ -1072,7 +1123,7 @@ static int nv_init_one (struct pci_dev *
pci_set_master(pdev);
- if (ent->driver_data == ADMA) {
+ if (host_desc->host_type == ADMA) {
rc = nv_adma_host_init(probe_ent);
if (rc)
goto err_out_iounmap;
@@ -1106,6 +1157,17 @@ err_out:
return rc;
}
+static void nv_eng_timeout(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA)
+ nv_adma_eng_timeout(ap);
+ else
+ ata_eng_timeout(ap);
+}
+
static void nv_adma_eng_timeout(struct ata_port *ap)
{
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
@@ -1149,6 +1211,17 @@ out:
DPRINTK("EXIT\n");
}
+static void nv_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct ata_host_set *host_set = qc->ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA)
+ nv_adma_qc_prep(qc);
+ else
+ ata_qc_prep(qc);
+}
+
static void nv_adma_qc_prep(struct ata_queued_cmd *qc)
{
struct nv_adma_port_priv *pp = qc->ap->private_data;
@@ -1259,6 +1332,17 @@ static void nv_adma_mode(struct ata_port
pp->flags &= ~NV_ADMA_PORT_REGISTER_MODE;
}
+static int nv_qc_issue(struct ata_queued_cmd *qc)
+{
+ struct ata_host_set *host_set = qc->ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (host->host_desc->host_type == ADMA)
+ return nv_adma_qc_issue(qc);
+ else
+ return ata_qc_issue_prot(qc);
+}
+
static int nv_adma_qc_issue(struct ata_queued_cmd *qc)
{
#if 0
@@ -1281,9 +1365,8 @@ static int nv_adma_qc_issue(struct ata_q
nv_adma_dump_cpb(&pp->cpb[qc->tag]);
if (qc->n_elem > 5) {
int i;
- for (i = 0; i < qc->n_elem - 5; i++) {
+ for (i = 0; i < qc->n_elem - 5; i++)
nv_adma_dump_aprd(&pp->aprd[i]);
- }
}
#endif
@@ -1323,7 +1406,7 @@ static void nv_disable_hotplug(struct at
outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE);
}
-static void nv_check_hotplug(struct ata_host_set *host_set)
+static int nv_check_hotplug(struct ata_host_set *host_set)
{
u8 intr_status;
@@ -1348,7 +1431,9 @@ static void nv_check_hotplug(struct ata_
if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Secondary device removed\n");
+ return 1;
}
+ return 0;
}
static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent)
@@ -1380,7 +1465,7 @@ static void nv_disable_hotplug_ck804(str
nv_disable_adma_space(pdev);
}
-static void nv_check_hotplug_ck804(struct ata_host_set *host_set)
+static int nv_check_hotplug_ck804(struct ata_host_set *host_set)
{
u8 intr_status;
@@ -1405,7 +1490,9 @@ static void nv_check_hotplug_ck804(struc
if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Secondary device removed\n");
+ return 1;
}
+ return 0;
}
static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent)
@@ -1439,10 +1526,11 @@ static void nv_disable_hotplug_adma(stru
}
}
-static void nv_check_hotplug_adma(struct ata_host_set *host_set)
+static int nv_check_hotplug_adma(struct ata_host_set *host_set)
{
unsigned int i;
u16 adma_status;
+ int hotplugged = 0;
for (i = 0; i < host_set->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i);
@@ -1451,13 +1539,16 @@ static void nv_check_hotplug_adma(struct
printk(KERN_WARNING "nv_sata: "
"port %d device added\n", i);
writew(NV_ADMA_STAT_HOTPLUG, mmio + NV_ADMA_STAT);
+ hotplugged = 1;
}
if (adma_status & NV_ADMA_STAT_HOTUNPLUG) {
printk(KERN_WARNING "nv_sata: "
"port %d device removed\n", i);
writew(NV_ADMA_STAT_HOTUNPLUG, mmio + NV_ADMA_STAT);
+ hotplugged = 1;
}
}
+ return hotplugged;
}
static int __init nv_init(void)
@@ -1472,6 +1563,9 @@ static void __exit nv_exit(void)
module_init(nv_init);
module_exit(nv_exit);
+module_param_named(adma, adma_enabled, bool, 0);
+MODULE_PARM_DESC(adma, "Enable use of ADMA (Default: false)");
+
#ifdef DEBUG
static void nv_adma_dump_aprd(struct nv_adma_prd *aprd)
@@ -1525,13 +1619,11 @@ static void nv_adma_dump_cpb(struct nv_a
printk("tag: 0x%02x\n", cpb->tag);
printk("next_cpb_idx: 0x%02x\n", cpb->next_cpb_idx);
printk("tf:\n");
- for (i=0; i<12; i++) {
+ for (i=0; i<12; i++)
nv_adma_dump_cpb_tf(cpb->tf[i]);
- }
printk("aprd:\n");
- for (i=0; i<5; i++) {
+ for (i=0; i<5; i++)
nv_adma_dump_aprd(&cpb->aprd[i]);
- }
printk("next_aprd: 0x%016llx\n", cpb->next_aprd);
}
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-18 8:06 ` Bill Rugolsky Jr.
@ 2006-03-18 8:56 ` Jeff Garzik
2006-03-19 23:23 ` Bill Rugolsky Jr.
0 siblings, 1 reply; 14+ messages in thread
From: Jeff Garzik @ 2006-03-18 8:56 UTC (permalink / raw)
To: Bill Rugolsky Jr.; +Cc: linux-kernel, linux-ide@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 1031 bytes --]
Bill Rugolsky Jr. wrote:
> On Fri, Mar 17, 2006 at 07:56:53PM -0500, Jeff Garzik wrote:
>
>>Could I get you to diff against the attached version?
>
>
> Certainly.
>
> I took the opportunity to modify my source file to fix a pair of typos in
> the pci_device_id changes, and make cosmetic changes by switching to enums
> rather than #defines, removing unnecessary braces around single statements,
> and fixing whitespace. That reduced the noise considerably, and made the
> NV_ADMA_CTL_READ_NON_COHERENT difference manifest.
OK, can you try the attached sata_nv.c? Does it perform to the level
that yours does?
It should contain all the functional changes in your patch, while
ignoring all the noise such as
+ if (host->host_desc->host_type == ADMA)
+ return nv_adma_port_start(ap);
+ else
+ return ata_port_start(ap);
and the hotplug return code changes.
I also intentionally excluded the READ_NON_COHERENT and
ap->ops->check_status() paranoia function call, to see if those actually
solve some problems.
Jeff
[-- Attachment #2: sata_nv.c --]
[-- Type: text/x-csrc, Size: 41448 bytes --]
/*
* sata_nv.c - NVIDIA nForce SATA
*
* Copyright 2004 NVIDIA Corp. All rights reserved.
* Copyright 2004 Andrew Chew
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* libata documentation is available via 'make {ps|pdf}docs',
* as Documentation/DocBook/libata.*
*
* No hardware documentation available outside of NVIDIA.
* This driver programs the NVIDIA SATA controller in a similar
* fashion as with other PCI IDE BMDMA controllers, with a few
* NV-specific details such as register offsets, SATA phy location,
* hotplug info, etc.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include "scsi.h"
#include <scsi/scsi_host.h>
#include <linux/libata.h>
//#define DEBUG
#define DRV_NAME "sata_nv"
#define DRV_VERSION "0.8-adma"
enum {
NV_PORTS = 2,
NV_PIO_MASK = 0x1f,
NV_MWDMA_MASK = 0x07,
NV_UDMA_MASK = 0x7f,
NV_PORT0_SCR_REG_OFFSET = 0x00,
NV_PORT1_SCR_REG_OFFSET = 0x40,
NV_INT_STATUS = 0x10,
NV_INT_STATUS_CK804 = 0x440,
NV_INT_STATUS_PDEV_INT = 0x01,
NV_INT_STATUS_PDEV_PM = 0x02,
NV_INT_STATUS_PDEV_ADDED = 0x04,
NV_INT_STATUS_PDEV_REMOVED = 0x08,
NV_INT_STATUS_SDEV_INT = 0x10,
NV_INT_STATUS_SDEV_PM = 0x20,
NV_INT_STATUS_SDEV_ADDED = 0x40,
NV_INT_STATUS_SDEV_REMOVED = 0x80,
NV_INT_STATUS_PDEV_HOTPLUG = (NV_INT_STATUS_PDEV_ADDED |
NV_INT_STATUS_PDEV_REMOVED),
NV_INT_STATUS_SDEV_HOTPLUG = (NV_INT_STATUS_SDEV_ADDED |
NV_INT_STATUS_SDEV_REMOVED),
NV_INT_STATUS_HOTPLUG = (NV_INT_STATUS_PDEV_HOTPLUG |
NV_INT_STATUS_SDEV_HOTPLUG),
NV_INT_ENABLE = 0x11,
NV_INT_ENABLE_CK804 = 0x441,
NV_INT_ENABLE_PDEV_MASK = 0x01,
NV_INT_ENABLE_PDEV_PM = 0x02,
NV_INT_ENABLE_PDEV_ADDED = 0x04,
NV_INT_ENABLE_PDEV_REMOVED = 0x08,
NV_INT_ENABLE_SDEV_MASK = 0x10,
NV_INT_ENABLE_SDEV_PM = 0x20,
NV_INT_ENABLE_SDEV_ADDED = 0x40,
NV_INT_ENABLE_SDEV_REMOVED = 0x80,
NV_INT_ENABLE_PDEV_HOTPLUG = (NV_INT_ENABLE_PDEV_ADDED |
NV_INT_ENABLE_PDEV_REMOVED),
NV_INT_ENABLE_SDEV_HOTPLUG = (NV_INT_ENABLE_SDEV_ADDED |
NV_INT_ENABLE_SDEV_REMOVED),
NV_INT_ENABLE_HOTPLUG = (NV_INT_ENABLE_PDEV_HOTPLUG |
NV_INT_ENABLE_SDEV_HOTPLUG),
NV_INT_CONFIG = 0x12,
NV_INT_CONFIG_METHD = 0x01, // 0 = INT, 1 = SMI
// For PCI config register 20
NV_MCP_SATA_CFG_20 = 0x50,
NV_MCP_SATA_CFG_20_SATA_SPACE_EN = 0x04,
NV_MCP_SATA_CFG_20_PORT0_EN = (1 << 17),
NV_MCP_SATA_CFG_20_PORT1_EN = (1 << 16),
NV_MCP_SATA_CFG_20_PORT0_PWB_EN = (1 << 14),
NV_MCP_SATA_CFG_20_PORT1_PWB_EN = (1 << 12),
};
//#define NV_ADMA_NCQ
#ifdef NV_ADMA_NCQ
#define NV_ADMA_CAN_QUEUE ATA_MAX_QUEUE
#else
#define NV_ADMA_CAN_QUEUE ATA_DEF_QUEUE
#endif
enum {
NV_ADMA_CPB_SZ = 128,
NV_ADMA_APRD_SZ = 16,
NV_ADMA_SGTBL_LEN = (1024 - NV_ADMA_CPB_SZ) / NV_ADMA_APRD_SZ,
NV_ADMA_SGTBL_SZ = NV_ADMA_SGTBL_LEN * NV_ADMA_APRD_SZ,
NV_ADMA_PORT_PRIV_DMA_SZ = NV_ADMA_CAN_QUEUE * (NV_ADMA_CPB_SZ + NV_ADMA_SGTBL_SZ),
// NV_ADMA_MAX_CPBS = 32,
// BAR5 offset to ADMA general registers
NV_ADMA_GEN = 0x400,
NV_ADMA_GEN_CTL = 0x00,
NV_ADMA_NOTIFIER_CLEAR = 0x30,
#define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & ( 1 << (19 + (12 * (PORT)))))
// BAR5 offset to ADMA ports
NV_ADMA_PORT = 0x480,
// size of ADMA port register space
NV_ADMA_PORT_SIZE = 0x100,
// ADMA port registers
NV_ADMA_CTL = 0x40,
NV_ADMA_CPB_COUNT = 0x42,
NV_ADMA_NEXT_CPB_IDX = 0x43,
NV_ADMA_STAT = 0x44,
NV_ADMA_CPB_BASE_LOW = 0x48,
NV_ADMA_CPB_BASE_HIGH = 0x4C,
NV_ADMA_APPEND = 0x50,
NV_ADMA_NOTIFIER = 0x68,
NV_ADMA_NOTIFIER_ERROR = 0x6C,
// NV_ADMA_CTL register bits
NV_ADMA_CTL_HOTPLUG_IEN = (1 << 0),
NV_ADMA_CTL_CHANNEL_RESET = (1 << 5),
NV_ADMA_CTL_GO = (1 << 7),
NV_ADMA_CTL_AIEN = (1 << 8),
NV_ADMA_CTL_READ_NON_COHERENT = (1 << 11),
NV_ADMA_CTL_WRITE_NON_COHERENT = (1 << 12),
// CPB response flag bits
NV_CPB_RESP_DONE = (1 << 0),
NV_CPB_RESP_ATA_ERR = (1 << 3),
NV_CPB_RESP_CMD_ERR = (1 << 4),
NV_CPB_RESP_CPB_ERR = (1 << 7),
// CPB control flag bits
NV_CPB_CTL_CPB_VALID = (1 << 0),
NV_CPB_CTL_QUEUE = (1 << 1),
NV_CPB_CTL_APRD_VALID = (1 << 2),
NV_CPB_CTL_IEN = (1 << 3),
NV_CPB_CTL_FPDMA = (1 << 4),
// APRD flags
NV_APRD_WRITE = (1 << 1),
NV_APRD_END = (1 << 2),
NV_APRD_CONT = (1 << 3),
// NV_ADMA_STAT flags
NV_ADMA_STAT_TIMEOUT = (1 << 0),
NV_ADMA_STAT_HOTUNPLUG = (1 << 1),
NV_ADMA_STAT_HOTPLUG = (1 << 2),
NV_ADMA_STAT_CPBERR = (1 << 4),
NV_ADMA_STAT_SERROR = (1 << 5),
NV_ADMA_STAT_CMD_COMPLETE = (1 << 6),
NV_ADMA_STAT_IDLE = (1 << 8),
NV_ADMA_STAT_LEGACY = (1 << 9),
NV_ADMA_STAT_STOPPED = (1 << 10),
NV_ADMA_STAT_DONE = (1 << 12),
NV_ADMA_STAT_ERR = (NV_ADMA_STAT_CPBERR | NV_ADMA_STAT_TIMEOUT),
// port flags
NV_ADMA_PORT_REGISTER_MODE = (1 << 0),
};
struct nv_adma_prd {
u64 addr;
u32 len;
u8 flags;
u8 packet_len;
u16 reserved;
};
enum nv_adma_regbits {
CMDEND = (1 << 15), /* end of command list */
WNB = (1 << 14), /* wait-not-BSY */
IGN = (1 << 13), /* ignore this entry */
CS1n = (1 << (4 + 8)), /* std. PATA signals follow... */
DA2 = (1 << (2 + 8)),
DA1 = (1 << (1 + 8)),
DA0 = (1 << (0 + 8)),
};
struct nv_adma_cpb {
u8 resp_flags; //0
u8 reserved1; //1
u8 ctl_flags; //2
// len is length of taskfile in 64 bit words
u8 len; //3
u8 tag; //4
u8 next_cpb_idx; //5
u16 reserved2; //6-7
u16 tf[12]; //8-31
struct nv_adma_prd aprd[5]; //32-111
u64 next_aprd; //112-119
u64 reserved3; //120-127
};
struct nv_adma_port_priv {
struct nv_adma_cpb *cpb;
// u8 cpb_idx;
u8 flags;
u32 notifier;
u32 notifier_error;
dma_addr_t cpb_dma;
struct nv_adma_prd *aprd;
dma_addr_t aprd_dma;
};
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
static irqreturn_t nv_interrupt (int irq, void *dev_instance,
struct pt_regs *regs);
static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static void nv_host_stop (struct ata_host_set *host_set);
static int nv_adma_port_start(struct ata_port *ap);
static void nv_adma_port_stop(struct ata_port *ap);
static void nv_adma_irq_clear(struct ata_port *ap);
static void nv_enable_hotplug(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug(struct ata_host_set *host_set);
static void nv_check_hotplug(struct ata_host_set *host_set);
static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug_ck804(struct ata_host_set *host_set);
static void nv_check_hotplug_ck804(struct ata_host_set *host_set);
static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent);
static void nv_disable_hotplug_adma(struct ata_host_set *host_set);
static void nv_check_hotplug_adma(struct ata_host_set *host_set);
static int nv_adma_qc_issue(struct ata_queued_cmd *qc);
static void nv_adma_qc_prep(struct ata_queued_cmd *qc);
static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb);
static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb);
static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, struct scatterlist *sg, int idx, struct nv_adma_prd *aprd);
static void nv_adma_register_mode(struct ata_port *ap);
static void nv_adma_mode(struct ata_port *ap);
static u8 nv_adma_bmdma_status(struct ata_port *ap);
static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc);
static void nv_adma_eng_timeout(struct ata_port *ap);
#ifdef DEBUG
static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb);
static void nv_adma_dump_aprd(struct nv_adma_prd *aprd);
static void nv_adma_dump_cpb_tf(u16 tf);
static void nv_adma_dump_port(struct ata_port *ap);
static void nv_adma_dump_iomem(void __iomem *m, int len);
#endif
enum nv_host_type
{
GENERIC,
NFORCE2,
NFORCE3,
CK804,
MCP51,
MCP55,
ADMA
};
static const struct pci_device_id nv_pci_tbl[] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADMA },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 },
{ PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC },
{ PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID<<8, 0xffff00, GENERIC },
{ 0, } /* terminate list */
};
#define NV_HOST_FLAGS_SCR_MMIO 0x00000001
struct nv_host_desc
{
enum nv_host_type host_type;
void (*enable_hotplug)(struct ata_probe_ent *probe_ent);
void (*disable_hotplug)(struct ata_host_set *host_set);
void (*check_hotplug)(struct ata_host_set *host_set);
};
static struct nv_host_desc nv_device_tbl[] = {
{
.host_type = GENERIC,
.enable_hotplug = NULL,
.disable_hotplug= NULL,
.check_hotplug = NULL,
},
{
.host_type = NFORCE2,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
},
{
.host_type = NFORCE3,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
},
{ .host_type = CK804,
.enable_hotplug = nv_enable_hotplug_ck804,
.disable_hotplug= nv_disable_hotplug_ck804,
.check_hotplug = nv_check_hotplug_ck804,
},
{ .host_type = MCP51,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
},
{ .host_type = MCP55,
.enable_hotplug = nv_enable_hotplug,
.disable_hotplug= nv_disable_hotplug,
.check_hotplug = nv_check_hotplug,
},
{ .host_type = ADMA,
.enable_hotplug = nv_enable_hotplug_adma,
.disable_hotplug= nv_disable_hotplug_adma,
.check_hotplug = nv_check_hotplug_adma,
},
};
struct nv_host
{
struct nv_host_desc *host_desc;
unsigned long host_flags;
};
static struct pci_driver nv_pci_driver = {
.name = DRV_NAME,
.id_table = nv_pci_tbl,
.probe = nv_init_one,
.remove = ata_pci_remove_one,
};
static struct scsi_host_template nv_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
.ioctl = ata_scsi_ioctl,
.queuecommand = ata_scsi_queuecmd,
.eh_strategy_handler = ata_scsi_error,
.can_queue = ATA_DEF_QUEUE,
.this_id = ATA_SHT_THIS_ID,
.sg_tablesize = LIBATA_MAX_PRD,
.max_sectors = ATA_MAX_SECTORS,
.cmd_per_lun = ATA_SHT_CMD_PER_LUN,
.emulated = ATA_SHT_EMULATED,
.use_clustering = ATA_SHT_USE_CLUSTERING,
.proc_name = DRV_NAME,
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
};
static struct scsi_host_template nv_adma_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
.ioctl = ata_scsi_ioctl,
.queuecommand = ata_scsi_queuecmd,
.eh_strategy_handler = ata_scsi_error,
.can_queue = ATA_DEF_QUEUE,
.this_id = ATA_SHT_THIS_ID,
.sg_tablesize = NV_ADMA_SGTBL_LEN,
.max_sectors = ATA_MAX_SECTORS,
.cmd_per_lun = ATA_SHT_CMD_PER_LUN,
.emulated = ATA_SHT_EMULATED,
.use_clustering = ATA_SHT_USE_CLUSTERING,
.proc_name = DRV_NAME,
.dma_boundary = ATA_DMA_BOUNDARY,
.slave_configure = ata_scsi_slave_config,
.bios_param = ata_std_bios_param,
};
static const struct ata_port_operations nv_ops = {
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
.tf_read = ata_tf_read,
.exec_command = ata_exec_command,
.check_status = ata_check_status,
.dev_select = ata_std_dev_select,
.phy_reset = sata_phy_reset,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
.bmdma_stop = ata_bmdma_stop,
.bmdma_status = ata_bmdma_status,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
.eng_timeout = ata_eng_timeout,
.irq_handler = nv_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
.port_start = ata_port_start,
.port_stop = ata_port_stop,
.host_stop = nv_host_stop,
};
static const struct ata_port_operations nv_adma_ops = {
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
.tf_read = ata_tf_read,
.exec_command = ata_exec_command,
.check_status = ata_check_status,
.dev_select = ata_std_dev_select,
.phy_reset = sata_phy_reset,
.bmdma_setup = ata_bmdma_setup,
.bmdma_start = ata_bmdma_start,
.bmdma_stop = nv_adma_bmdma_stop,
.bmdma_status = nv_adma_bmdma_status,
.qc_prep = nv_adma_qc_prep,
.qc_issue = nv_adma_qc_issue,
.eng_timeout = nv_adma_eng_timeout,
.irq_handler = nv_interrupt,
.irq_clear = nv_adma_irq_clear,
.scr_read = nv_scr_read,
.scr_write = nv_scr_write,
.port_start = nv_adma_port_start,
.port_stop = nv_adma_port_stop,
.host_stop = nv_host_stop,
};
static int adma_enabled;
static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, u16 *cpb)
{
unsigned int idx = 0;
cpb[idx++] = cpu_to_le16((ATA_REG_DEVICE << 8) | tf->device | WNB);
if ((tf->flags & ATA_TFLAG_LBA48) == 0) {
cpb[idx++] = cpu_to_le16(IGN);
cpb[idx++] = cpu_to_le16(IGN);
cpb[idx++] = cpu_to_le16(IGN);
cpb[idx++] = cpu_to_le16(IGN);
cpb[idx++] = cpu_to_le16(IGN);
}
else {
cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->hob_feature);
cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->hob_nsect);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->hob_lbal);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->hob_lbam);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->hob_lbah);
}
cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->feature);
cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->nsect);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->lbal);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->lbam);
cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->lbah);
cpb[idx++] = cpu_to_le16((ATA_REG_CMD << 8) | tf->command | CMDEND);
return idx;
}
static inline void __iomem *__nv_adma_ctl_block(void __iomem *mmio,
unsigned int port_no)
{
mmio += NV_ADMA_PORT + port_no * NV_ADMA_PORT_SIZE;
return mmio;
}
static inline void __iomem *nv_adma_ctl_block(struct ata_port *ap)
{
return __nv_adma_ctl_block(ap->host_set->mmio_base, ap->port_no);
}
static inline void __iomem *nv_adma_gen_block(struct ata_port *ap)
{
return (ap->host_set->mmio_base + NV_ADMA_GEN);
}
static inline void __iomem *nv_adma_notifier_clear_block(struct ata_port *ap)
{
return (nv_adma_gen_block(ap) + NV_ADMA_NOTIFIER_CLEAR + (4 * ap->port_no));
}
static inline void nv_adma_reset_channel(struct ata_port *ap)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
u16 tmp;
// clear CPB fetch count
writew(0, mmio + NV_ADMA_CPB_COUNT);
// clear GO
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL);
udelay(1);
writew(tmp & ~NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL);
}
static inline int nv_adma_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
struct nv_adma_port_priv *pp = ap->private_data;
struct nv_adma_cpb *cpb = &pp->cpb[qc->tag];
u16 status;
u32 gen_ctl;
u16 flags;
int have_err = 0;
int handled = 0;
status = readw(mmio + NV_ADMA_STAT);
// if in ATA register mode, use standard ata interrupt handler
if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
VPRINTK("in ATA register mode\n");
return ata_host_intr(ap, qc);
}
gen_ctl = readl(nv_adma_gen_block(ap) + NV_ADMA_GEN_CTL);
if (!NV_ADMA_CHECK_INTR(gen_ctl, ap->port_no))
return 0;
if (!pp->notifier && !pp->notifier_error) {
if (status) {
VPRINTK("XXX no notifier, but status 0x%x\n", status);
#ifdef DEBUG
nv_adma_dump_port(ap);
nv_adma_dump_cpb(cpb);
#endif
} else
return 0;
}
if (pp->notifier_error) {
have_err = 1;
handled = 1;
}
if (status & NV_ADMA_STAT_TIMEOUT) {
VPRINTK("timeout, stat = 0x%x\n", status);
have_err = 1;
handled = 1;
}
if (status & NV_ADMA_STAT_CPBERR) {
VPRINTK("CPB error, stat = 0x%x\n", status);
have_err = 1;
handled = 1;
}
if (status & NV_ADMA_STAT_STOPPED) {
VPRINTK("ADMA stopped, stat = 0x%x, resp_flags = 0x%x\n", status, cpb->resp_flags);
if (!(status & NV_ADMA_STAT_DONE)) {
have_err = 1;
handled = 1;
}
}
if (status & NV_ADMA_STAT_CMD_COMPLETE) {
VPRINTK("ADMA command complete, stat = 0x%x\n", status);
}
if (status & NV_ADMA_STAT_DONE) {
flags = cpb->resp_flags;
VPRINTK("CPB done, stat = 0x%x, flags = 0x%x\n", status, flags);
handled = 1;
if (!(status & NV_ADMA_STAT_IDLE)) {
VPRINTK("XXX CPB done, but not idle\n");
}
if (flags & NV_CPB_RESP_DONE) {
VPRINTK("CPB flags done, flags = 0x%x\n", flags);
}
if (flags & NV_CPB_RESP_ATA_ERR) {
VPRINTK("CPB flags ATA err, flags = 0x%x\n", flags);
have_err = 1;
}
if (flags & NV_CPB_RESP_CMD_ERR) {
VPRINTK("CPB flags CMD err, flags = 0x%x\n", flags);
have_err = 1;
}
if (flags & NV_CPB_RESP_CPB_ERR) {
VPRINTK("CPB flags CPB err, flags = 0x%x\n", flags);
have_err = 1;
}
}
// clear status
writew(status, mmio + NV_ADMA_STAT);
if (handled) {
u8 ata_status = readb(mmio + (ATA_REG_STATUS * 4));
qc->err_mask |= ac_err_mask(have_err ? (ata_status | ATA_ERR) : ata_status);
ata_qc_complete(qc);
}
return handled; /* irq handled */
}
/* FIXME: The hardware provides the necessary SATA PHY controls
* to support ATA_FLAG_SATA_RESET. However, it is currently
* necessary to disable that flag, to solve misdetection problems.
* See http://bugme.osdl.org/show_bug.cgi?id=3352 for more info.
*
* This problem really needs to be investigated further. But in the
* meantime, we avoid ATA_FLAG_SATA_RESET to get people working.
*/
static struct ata_port_info nv_port_info = {
.sht = &nv_sht,
.host_flags = ATA_FLAG_SATA |
/* ATA_FLAG_SATA_RESET | */
ATA_FLAG_SRST |
ATA_FLAG_NO_LEGACY,
.pio_mask = NV_PIO_MASK,
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
.port_ops = &nv_ops,
};
static struct ata_port_info nv_adma_port_info = {
.sht = &nv_adma_sht,
.host_flags = ATA_FLAG_SATA |
/* ATA_FLAG_SATA_RESET | */
ATA_FLAG_SRST |
ATA_FLAG_MMIO |
ATA_FLAG_NO_LEGACY,
.pio_mask = NV_PIO_MASK,
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
.port_ops = &nv_adma_ops,
};
MODULE_AUTHOR("NVIDIA");
MODULE_DESCRIPTION("low-level driver for NVIDIA nForce SATA controller");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, nv_pci_tbl);
MODULE_VERSION(DRV_VERSION);
static inline void nv_enable_adma_space (struct pci_dev *pdev)
{
u8 regval;
VPRINTK("ENTER\n");
pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val);
regval |= NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
}
static inline void nv_disable_adma_space (struct pci_dev *pdev)
{
u8 regval;
VPRINTK("ENTER\n");
pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, ®val);
regval &= ~NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
}
static void nv_adma_irq_clear(struct ata_port *ap)
{
/* TODO */
}
static u8 nv_adma_bmdma_status(struct ata_port *ap)
{
return inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
}
static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc)
{
/* TODO */
}
static irqreturn_t nv_interrupt (int irq, void *dev_instance,
struct pt_regs *regs)
{
struct ata_host_set *host_set = dev_instance;
struct nv_host *host = host_set->private_data;
unsigned int i;
unsigned int handled = 0;
unsigned long flags;
spin_lock_irqsave(&host_set->lock, flags);
for (i = 0; i < host_set->n_ports; i++) {
struct ata_port *ap = host_set->ports[i];
if (ap &&
!(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) {
struct ata_queued_cmd *qc;
if (host->host_desc->host_type == ADMA) {
struct nv_adma_port_priv *pp = ap->private_data;
void __iomem *mmio = nv_adma_ctl_block(ap);
// read notifiers
pp->notifier = readl(mmio + NV_ADMA_NOTIFIER);
pp->notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
}
qc = ata_qc_from_tag(ap, ap->active_tag);
if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
if (host->host_desc->host_type == ADMA)
handled += nv_adma_host_intr(ap, qc);
else
handled += ata_host_intr(ap, qc);
}
}
}
if (host->host_desc->check_hotplug)
host->host_desc->check_hotplug(host_set);
// clear notifier
if (handled && host->host_desc->host_type == ADMA) {
for (i = 0; i < host_set->n_ports; i++) {
struct ata_port *ap = host_set->ports[i];
struct nv_adma_port_priv *pp = ap->private_data;
writel(pp->notifier | pp->notifier_error,
nv_adma_notifier_clear_block(ap));
}
}
spin_unlock_irqrestore(&host_set->lock, flags);
return IRQ_RETVAL(handled);
}
static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg)
{
struct ata_host_set *host_set = ap->host_set;
struct nv_host *host = host_set->private_data;
u32 val = 0;
VPRINTK("ENTER\n");
VPRINTK("reading SCR reg %d, got 0x%08x\n", sc_reg, val);
if (sc_reg > SCR_CONTROL)
return 0xffffffffU;
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
val = readl((void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4));
else
val = inl(ap->ioaddr.scr_addr + (sc_reg * 4));
VPRINTK("reading SCR reg %d, got 0x%08x\n", sc_reg, val);
return val;
}
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
{
struct ata_host_set *host_set = ap->host_set;
struct nv_host *host = host_set->private_data;
VPRINTK("ENTER\n");
VPRINTK("writing SCR reg %d with 0x%08x\n", sc_reg, val);
if (sc_reg > SCR_CONTROL)
return;
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
writel(val, (void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4));
else
outl(val, ap->ioaddr.scr_addr + (sc_reg * 4));
}
static void nv_host_stop (struct ata_host_set *host_set)
{
struct nv_host *host = host_set->private_data;
struct pci_dev *pdev = to_pci_dev(host_set->dev);
VPRINTK("ENTER\n");
// Disable hotplug event interrupts.
if (host->host_desc->disable_hotplug)
host->host_desc->disable_hotplug(host_set);
kfree(host);
if (host_set->mmio_base)
pci_iounmap(pdev, host_set->mmio_base);
}
static int nv_adma_port_start(struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
struct nv_adma_port_priv *pp;
int rc;
void *mem;
dma_addr_t mem_dma;
void __iomem *mmio = nv_adma_ctl_block(ap);
VPRINTK("ENTER\n");
nv_adma_reset_channel(ap);
#ifdef DEBUG
VPRINTK("after reset:\n");
nv_adma_dump_port(ap);
#endif
rc = ata_port_start(ap);
if (rc)
return rc;
pp = kmalloc(sizeof(*pp), GFP_KERNEL);
if (!pp) {
rc = -ENOMEM;
goto err_out;
}
memset(pp, 0, sizeof(*pp));
mem = dma_alloc_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ,
&mem_dma, GFP_KERNEL);
VPRINTK("dma memory: vaddr = 0x%08x, paddr = 0x%08x\n", (u32)mem, (u32)mem_dma);
if (!mem) {
rc = -ENOMEM;
goto err_out_kfree;
}
memset(mem, 0, NV_ADMA_PORT_PRIV_DMA_SZ);
/*
* First item in chunk of DMA memory:
* 128-byte command parameter block (CPB)
* one for each command tag
*/
pp->cpb = mem;
pp->cpb_dma = mem_dma;
VPRINTK("cpb = 0x%08x, cpb_dma = 0x%08x\n", (u32)pp->cpb, (u32)pp->cpb_dma);
writel(mem_dma, mmio + NV_ADMA_CPB_BASE_LOW);
writel(0, mmio + NV_ADMA_CPB_BASE_HIGH);
mem += NV_ADMA_CAN_QUEUE * NV_ADMA_CPB_SZ;
mem_dma += NV_ADMA_CAN_QUEUE * NV_ADMA_CPB_SZ;
/*
* Second item: block of ADMA_SGTBL_LEN s/g entries
*/
pp->aprd = mem;
pp->aprd_dma = mem_dma;
VPRINTK("aprd = 0x%08x, aprd_dma = 0x%08x\n", (u32)pp->aprd, (u32)pp->aprd_dma);
ap->private_data = pp;
// clear any outstanding interrupt conditions
writew(0xffff, mmio + NV_ADMA_STAT);
// initialize port variables
// pp->cpb_idx = 0;
pp->flags = NV_ADMA_PORT_REGISTER_MODE;
// make sure controller is in ATA register mode
nv_adma_register_mode(ap);
return 0;
err_out_kfree:
kfree(pp);
err_out:
ata_port_stop(ap);
return rc;
}
static void nv_adma_port_stop(struct ata_port *ap)
{
struct device *dev = ap->host_set->dev;
struct nv_adma_port_priv *pp = ap->private_data;
void __iomem *mmio = nv_adma_ctl_block(ap);
VPRINTK("ENTER\n");
writew(0, mmio + NV_ADMA_CTL);
ap->private_data = NULL;
dma_free_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ, pp->cpb, pp->cpb_dma);
kfree(pp);
ata_port_stop(ap);
}
static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port)
{
void __iomem *mmio = probe_ent->mmio_base;
struct ata_ioports *ioport = &probe_ent->port[port];
VPRINTK("ENTER\n");
mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE;
ioport->cmd_addr = (unsigned long) mmio;
ioport->data_addr = (unsigned long) mmio + (ATA_REG_DATA * 4);
ioport->error_addr =
ioport->feature_addr = (unsigned long) mmio + (ATA_REG_ERR * 4);
ioport->nsect_addr = (unsigned long) mmio + (ATA_REG_NSECT * 4);
ioport->lbal_addr = (unsigned long) mmio + (ATA_REG_LBAL * 4);
ioport->lbam_addr = (unsigned long) mmio + (ATA_REG_LBAM * 4);
ioport->lbah_addr = (unsigned long) mmio + (ATA_REG_LBAH * 4);
ioport->device_addr = (unsigned long) mmio + (ATA_REG_DEVICE * 4);
ioport->status_addr =
ioport->command_addr = (unsigned long) mmio + (ATA_REG_STATUS * 4);
ioport->altstatus_addr =
ioport->ctl_addr = (unsigned long) mmio + 0x20;
}
static int nv_adma_host_init(struct ata_probe_ent *probe_ent)
{
struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
unsigned int i;
u32 tmp32;
VPRINTK("ENTER\n");
probe_ent->n_ports = NV_PORTS;
nv_enable_adma_space(pdev);
// enable ADMA on the ports
pci_read_config_dword(pdev, NV_MCP_SATA_CFG_20, &tmp32);
tmp32 |= NV_MCP_SATA_CFG_20_PORT0_EN |
NV_MCP_SATA_CFG_20_PORT0_PWB_EN |
NV_MCP_SATA_CFG_20_PORT1_EN |
NV_MCP_SATA_CFG_20_PORT1_PWB_EN;
pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32);
for (i = 0; i < probe_ent->n_ports; i++)
nv_adma_setup_port(probe_ent, i);
for (i = 0; i < probe_ent->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i);
u16 tmp;
/* enable interrupt, clear reset if not already clear */
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_AIEN, mmio + NV_ADMA_CTL);
}
pci_set_master(pdev);
return 0;
}
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
struct nv_host *host;
struct ata_port_info *ppi;
struct ata_probe_ent *probe_ent;
struct nv_host_desc *host_desc;
int pci_dev_busy = 0;
int rc;
u32 bar;
VPRINTK("ENTER\n");
// Make sure this is a SATA controller by counting the number of bars
// (NVIDIA SATA controllers will always have six bars). Otherwise,
// it's an IDE controller and we ignore it.
for (bar=0; bar<6; bar++)
if (pci_resource_start(pdev, bar) == 0)
return -ENODEV;
if (!printed_version++)
dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
rc = pci_enable_device(pdev);
if (rc)
goto err_out;
rc = pci_request_regions(pdev, DRV_NAME);
if (rc) {
pci_dev_busy = 1;
goto err_out_disable;
}
rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
goto err_out_regions;
rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
if (rc)
goto err_out_regions;
rc = -ENOMEM;
host_desc = &nv_device_tbl[ent->driver_data];
if (!adma_enabled && host_desc->host_type == ADMA)
host_desc->host_type--;
/* select ADMA or legacy PCI IDE BMDMA controller operation */
if (host_desc->host_type == ADMA) {
#ifdef NV_ADMA_NCQ
ppi->host_flags |= ATA_FLAG_NCQ;
ppi->sht->can_queue = NV_ADMA_CAN_QUEUE;
#endif
ppi = &nv_adma_port_info;
} else
ppi = &nv_port_info;
probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent)
goto err_out_regions;
host = kmalloc(sizeof(struct nv_host), GFP_KERNEL);
if (!host)
goto err_out_free_ent;
memset(host, 0, sizeof(struct nv_host));
host->host_desc = host_desc;
probe_ent->private_data = host;
if (pci_resource_flags(pdev, 5) & IORESOURCE_MEM)
host->host_flags |= NV_HOST_FLAGS_SCR_MMIO;
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) {
unsigned long base;
probe_ent->mmio_base = pci_iomap(pdev, 5, 0);
if (probe_ent->mmio_base == NULL) {
rc = -EIO;
goto err_out_free_host;
}
base = (unsigned long)probe_ent->mmio_base;
VPRINTK("BAR5 base is at 0x%x\n", (u32)base);
probe_ent->port[0].scr_addr =
base + NV_PORT0_SCR_REG_OFFSET;
probe_ent->port[1].scr_addr =
base + NV_PORT1_SCR_REG_OFFSET;
} else {
probe_ent->port[0].scr_addr =
pci_resource_start(pdev, 5) | NV_PORT0_SCR_REG_OFFSET;
probe_ent->port[1].scr_addr =
pci_resource_start(pdev, 5) | NV_PORT1_SCR_REG_OFFSET;
}
pci_set_master(pdev);
if (host_desc->host_type == ADMA) {
rc = nv_adma_host_init(probe_ent);
if (rc)
goto err_out_iounmap;
}
rc = ata_device_add(probe_ent);
if (rc != NV_PORTS)
goto err_out_iounmap;
// Enable hotplug event interrupts.
if (host->host_desc->enable_hotplug)
host->host_desc->enable_hotplug(probe_ent);
kfree(probe_ent);
return 0;
err_out_iounmap:
if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
pci_iounmap(pdev, probe_ent->mmio_base);
err_out_free_host:
kfree(host);
err_out_free_ent:
kfree(probe_ent);
err_out_regions:
pci_release_regions(pdev);
err_out_disable:
if (!pci_dev_busy)
pci_disable_device(pdev);
err_out:
return rc;
}
static void nv_adma_eng_timeout(struct ata_port *ap)
{
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
struct nv_adma_port_priv *pp = ap->private_data;
u8 drv_stat;
VPRINTK("ENTER\n");
if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
ata_eng_timeout(ap);
goto out;
}
if (!qc) {
printk(KERN_ERR "ata%u: BUG: timeout without command\n",
ap->id);
goto out;
}
// spin_lock_irqsave(&host_set->lock, flags);
qc->scsidone = scsi_finish_command;
drv_stat = ata_chk_status(ap);
printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x\n",
ap->id, qc->tf.command, drv_stat);
// reset channel
nv_adma_reset_channel(ap);
/* complete taskfile transaction */
qc->err_mask |= ac_err_mask(drv_stat);
ata_qc_complete(qc);
// spin_unlock_irqrestore(&host_set->lock, flags);
out:
DPRINTK("EXIT\n");
}
static void nv_adma_qc_prep(struct ata_queued_cmd *qc)
{
struct nv_adma_port_priv *pp = qc->ap->private_data;
struct nv_adma_cpb *cpb = &pp->cpb[qc->tag];
VPRINTK("ENTER\n");
VPRINTK("qc->flags = 0x%x\n", (u32)qc->flags);
if (!(qc->flags & ATA_QCFLAG_DMAMAP)) {
ata_qc_prep(qc);
return;
}
memset(cpb, 0, sizeof(struct nv_adma_cpb));
cpb->ctl_flags = NV_CPB_CTL_CPB_VALID |
NV_CPB_CTL_APRD_VALID |
NV_CPB_CTL_IEN;
cpb->len = 3;
cpb->tag = qc->tag;
cpb->next_cpb_idx = 0;
#ifdef NV_ADMA_NCQ
// turn on NCQ flags for NCQ commands
if (qc->flags & ATA_QCFLAG_NCQ)
cpb->ctl_flags |= NV_CPB_CTL_QUEUE | NV_CPB_CTL_FPDMA;
#endif
nv_adma_tf_to_cpb(&qc->tf, cpb->tf);
nv_adma_fill_sg(qc, cpb);
}
static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb)
{
struct nv_adma_port_priv *pp = qc->ap->private_data;
unsigned int idx;
struct nv_adma_prd *aprd;
struct scatterlist *sg;
VPRINTK("ENTER\n");
idx = 0;
ata_for_each_sg(sg, qc) {
aprd = (idx < 5) ? &cpb->aprd[idx] : &pp->aprd[idx-5];
nv_adma_fill_aprd(qc, sg, idx, aprd);
idx++;
}
if (idx > 5)
cpb->next_aprd = (u64)(pp->aprd_dma + NV_ADMA_APRD_SZ * qc->tag);
}
static void nv_adma_fill_aprd(struct ata_queued_cmd *qc,
struct scatterlist *sg,
int idx,
struct nv_adma_prd *aprd)
{
u32 sg_len, addr, flags;
memset(aprd, 0, sizeof(struct nv_adma_prd));
addr = sg_dma_address(sg);
sg_len = sg_dma_len(sg);
flags = 0;
if (qc->tf.flags & ATA_TFLAG_WRITE)
flags |= NV_APRD_WRITE;
if (idx == qc->n_elem - 1)
flags |= NV_APRD_END;
else if (idx != 4)
flags |= NV_APRD_CONT;
aprd->addr = cpu_to_le32(addr);
aprd->len = cpu_to_le32(sg_len); /* len in bytes */
aprd->flags = cpu_to_le32(flags);
}
static void nv_adma_register_mode(struct ata_port *ap)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
struct nv_adma_port_priv *pp = ap->private_data;
u16 tmp;
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
pp->flags |= NV_ADMA_PORT_REGISTER_MODE;
}
static void nv_adma_mode(struct ata_port *ap)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
struct nv_adma_port_priv *pp = ap->private_data;
u16 tmp;
if (!(pp->flags & NV_ADMA_PORT_REGISTER_MODE))
return;
#if 0
nv_adma_reset_channel(ap);
#endif
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL);
pp->flags &= ~NV_ADMA_PORT_REGISTER_MODE;
}
static int nv_adma_qc_issue(struct ata_queued_cmd *qc)
{
#if 0
struct nv_adma_port_priv *pp = qc->ap->private_data;
#endif
void __iomem *mmio = nv_adma_ctl_block(qc->ap);
VPRINTK("ENTER\n");
if (!(qc->flags & ATA_QCFLAG_DMAMAP)) {
VPRINTK("no dmamap, using ATA register mode: 0x%x\n", (u32)qc->flags);
// use ATA register mode
nv_adma_register_mode(qc->ap);
return ata_qc_issue_prot(qc);
} else
nv_adma_mode(qc->ap);
#if 0
nv_adma_dump_port(qc->ap);
nv_adma_dump_cpb(&pp->cpb[qc->tag]);
if (qc->n_elem > 5) {
int i;
for (i = 0; i < qc->n_elem - 5; i++)
nv_adma_dump_aprd(&pp->aprd[i]);
}
#endif
//
// write append register, command tag in lower 8 bits
// and (number of cpbs to append -1) in top 8 bits
//
mb();
writew(qc->tag, mmio + NV_ADMA_APPEND);
VPRINTK("EXIT\n");
return 0;
}
static void nv_enable_hotplug(struct ata_probe_ent *probe_ent)
{
u8 intr_mask;
outb(NV_INT_STATUS_HOTPLUG,
probe_ent->port[0].scr_addr + NV_INT_STATUS);
intr_mask = inb(probe_ent->port[0].scr_addr + NV_INT_ENABLE);
intr_mask |= NV_INT_ENABLE_HOTPLUG;
outb(intr_mask, probe_ent->port[0].scr_addr + NV_INT_ENABLE);
}
static void nv_disable_hotplug(struct ata_host_set *host_set)
{
u8 intr_mask;
intr_mask = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE);
intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE);
}
static void nv_check_hotplug(struct ata_host_set *host_set)
{
u8 intr_status;
intr_status = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS);
// Clear interrupt status.
outb(0xff, host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS);
if (intr_status & NV_INT_STATUS_HOTPLUG) {
if (intr_status & NV_INT_STATUS_PDEV_ADDED)
printk(KERN_WARNING "nv_sata: "
"Primary device added\n");
if (intr_status & NV_INT_STATUS_PDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Primary device removed\n");
if (intr_status & NV_INT_STATUS_SDEV_ADDED)
printk(KERN_WARNING "nv_sata: "
"Secondary device added\n");
if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Secondary device removed\n");
}
}
static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent)
{
struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
u8 intr_mask;
nv_enable_adma_space(pdev);
writeb(NV_INT_STATUS_HOTPLUG, probe_ent->mmio_base + NV_INT_STATUS_CK804);
intr_mask = readb(probe_ent->mmio_base + NV_INT_ENABLE_CK804);
intr_mask |= NV_INT_ENABLE_HOTPLUG;
writeb(intr_mask, probe_ent->mmio_base + NV_INT_ENABLE_CK804);
}
static void nv_disable_hotplug_ck804(struct ata_host_set *host_set)
{
struct pci_dev *pdev = to_pci_dev(host_set->dev);
u8 intr_mask;
intr_mask = readb(host_set->mmio_base + NV_INT_ENABLE_CK804);
intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
writeb(intr_mask, host_set->mmio_base + NV_INT_ENABLE_CK804);
nv_disable_adma_space(pdev);
}
static void nv_check_hotplug_ck804(struct ata_host_set *host_set)
{
u8 intr_status;
intr_status = readb(host_set->mmio_base + NV_INT_STATUS_CK804);
// Clear interrupt status.
writeb(0xff, host_set->mmio_base + NV_INT_STATUS_CK804);
if (intr_status & NV_INT_STATUS_HOTPLUG) {
if (intr_status & NV_INT_STATUS_PDEV_ADDED)
printk(KERN_WARNING "nv_sata: "
"Primary device added\n");
if (intr_status & NV_INT_STATUS_PDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Primary device removed\n");
if (intr_status & NV_INT_STATUS_SDEV_ADDED)
printk(KERN_WARNING "nv_sata: "
"Secondary device added\n");
if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
printk(KERN_WARNING "nv_sata: "
"Secondary device removed\n");
}
}
static void nv_enable_hotplug_adma(struct ata_probe_ent *probe_ent)
{
struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
unsigned int i;
u16 tmp;
nv_enable_adma_space(pdev);
for (i = 0; i < probe_ent->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i);
writew(NV_ADMA_STAT_HOTPLUG | NV_ADMA_STAT_HOTUNPLUG,
mmio + NV_ADMA_STAT);
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp | NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL);
}
}
static void nv_disable_hotplug_adma(struct ata_host_set *host_set)
{
unsigned int i;
u16 tmp;
for (i = 0; i < host_set->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i);
tmp = readw(mmio + NV_ADMA_CTL);
writew(tmp & ~NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL);
}
}
static void nv_check_hotplug_adma(struct ata_host_set *host_set)
{
unsigned int i;
u16 adma_status;
for (i = 0; i < host_set->n_ports; i++) {
void __iomem *mmio = __nv_adma_ctl_block(host_set->mmio_base, i);
adma_status = readw(mmio + NV_ADMA_STAT);
if (adma_status & NV_ADMA_STAT_HOTPLUG) {
printk(KERN_WARNING "nv_sata: "
"port %d device added\n", i);
writew(NV_ADMA_STAT_HOTPLUG, mmio + NV_ADMA_STAT);
}
if (adma_status & NV_ADMA_STAT_HOTUNPLUG) {
printk(KERN_WARNING "nv_sata: "
"port %d device removed\n", i);
writew(NV_ADMA_STAT_HOTUNPLUG, mmio + NV_ADMA_STAT);
}
}
}
static int __init nv_init(void)
{
return pci_module_init(&nv_pci_driver);
}
static void __exit nv_exit(void)
{
pci_unregister_driver(&nv_pci_driver);
}
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: false)");
#ifdef DEBUG
static void nv_adma_dump_aprd(struct nv_adma_prd *aprd)
{
printk("%016llx %08x %02x %s %s %s\n",
aprd->addr,
aprd->len,
aprd->flags,
(aprd->flags & NV_APRD_WRITE) ? "WRITE" : " ",
(aprd->flags & NV_APRD_END) ? "END" : " ",
(aprd->flags & NV_APRD_CONT) ? "CONT" : " ");
}
static void nv_adma_dump_iomem(void __iomem *m, int len)
{
int i, j;
for (i = 0; i < len/16; i++) {
printk(KERN_WARNING "%02x: ", 16*i);
for (j = 0; j < 16; j++) {
printk("%02x%s", (u32)readb(m + 16*i + j),
(j == 7) ? "-" : " ");
}
printk("\n");
}
}
static void nv_adma_dump_cpb_tf(u16 tf)
{
printk("0x%04x %s %s %s 0x%02x 0x%02x\n",
tf,
(tf & CMDEND) ? "END" : " ",
(tf & WNB) ? "WNB" : " ",
(tf & IGN) ? "IGN" : " ",
((tf >> 8) & 0x1f),
(tf & 0xff));
}
static void nv_adma_dump_port(struct ata_port *ap)
{
void __iomem *mmio = nv_adma_ctl_block(ap);
nv_adma_dump_iomem(mmio, NV_ADMA_PORT_SIZE);
}
static void nv_adma_dump_cpb(struct nv_adma_cpb *cpb)
{
int i;
printk("resp_flags: 0x%02x\n", cpb->resp_flags);
printk("ctl_flags: 0x%02x\n", cpb->ctl_flags);
printk("len: 0x%02x\n", cpb->len);
printk("tag: 0x%02x\n", cpb->tag);
printk("next_cpb_idx: 0x%02x\n", cpb->next_cpb_idx);
printk("tf:\n");
for (i=0; i<12; i++)
nv_adma_dump_cpb_tf(cpb->tf[i]);
printk("aprd:\n");
for (i=0; i<5; i++)
nv_adma_dump_aprd(&cpb->aprd[i]);
printk("next_aprd: 0x%016llx\n", cpb->next_aprd);
}
#endif
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-18 8:56 ` Jeff Garzik
@ 2006-03-19 23:23 ` Bill Rugolsky Jr.
2006-03-21 1:28 ` Jeff Garzik
0 siblings, 1 reply; 14+ messages in thread
From: Bill Rugolsky Jr. @ 2006-03-19 23:23 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linux-kernel, linux-ide@vger.kernel.org
On Sat, Mar 18, 2006 at 03:56:28AM -0500, Jeff Garzik wrote:
> OK, can you try the attached sata_nv.c? Does it perform to the level
> that yours does?
Yes, the results are approximately the same. Booting from port 0 (sda)
with ADMA enabled still results in timeouts on port 3 (sdc) while
running tars on the RAID1 array on ports 2&3.
ata4: command 0x25 timeout, stat 0x50
ata4: command 0x25 timeout, stat 0x50
( xterm-3349 |#0): new 355 us maximum-latency wakeup.
( watchdog/0-4 |#0): new 468 us maximum-latency wakeup.
ata4: command 0x35 timeout, stat 0x50
ata4: command 0x35 timeout, stat 0x50
ata4: command 0x35 timeout, stat 0x50
ata4: command 0x35 timeout, stat 0x50
ata4: command 0x35 timeout, stat 0x50
ata4: command 0x35 timeout, stat 0x50
After a while, syncing the filesystems hangs the sync process, though
the system continues to function, and I can log in on another VC.
The good news: no long latencies from the status inb() during the
period that it is functional! :-p
Booting without ADMA gives the usual stable behavior, with the long
latencies from the status inb().
I was a little disconcerted when I saw this this in the trace with ADMA
disabled,
tar-21466 0dnh. 3979us : nv_check_hotplug_adma (nv_interrupt)
until I realized that this
if (!adma_enabled && host_desc->host_type == ADMA)
host_desc->host_type--;
only alters the outcome of the "host_desc->host_type == ADMA" test, but
still uses the ADMA-based hotplug functions.
-Bill
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-19 23:23 ` Bill Rugolsky Jr.
@ 2006-03-21 1:28 ` Jeff Garzik
2006-03-21 12:48 ` Bill Rugolsky Jr.
2006-03-27 1:14 ` Matt Heler
0 siblings, 2 replies; 14+ messages in thread
From: Jeff Garzik @ 2006-03-21 1:28 UTC (permalink / raw)
To: Bill Rugolsky Jr.; +Cc: linux-kernel, linux-ide@vger.kernel.org
Bill Rugolsky Jr. wrote:
> On Sat, Mar 18, 2006 at 03:56:28AM -0500, Jeff Garzik wrote:
>
>>OK, can you try the attached sata_nv.c? Does it perform to the level
>>that yours does?
>
>
> Yes, the results are approximately the same. Booting from port 0 (sda)
> with ADMA enabled still results in timeouts on port 3 (sdc) while
> running tars on the RAID1 array on ports 2&3.
Thanks a lot for testing.
I've stored the sata_nv updates I sent you in the 'nv-adma' branch of
git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
> ata4: command 0x25 timeout, stat 0x50
> ata4: command 0x25 timeout, stat 0x50
> ( xterm-3349 |#0): new 355 us maximum-latency wakeup.
> ( watchdog/0-4 |#0): new 468 us maximum-latency wakeup.
> ata4: command 0x35 timeout, stat 0x50
> ata4: command 0x35 timeout, stat 0x50
> ata4: command 0x35 timeout, stat 0x50
> ata4: command 0x35 timeout, stat 0x50
> ata4: command 0x35 timeout, stat 0x50
> ata4: command 0x35 timeout, stat 0x50
>
> After a while, syncing the filesystems hangs the sync process, though
> the system continues to function, and I can log in on another VC.
hmmm. Sounds like some attention should be paid to the error handling
portion of the code.
> The good news: no long latencies from the status inb() during the
> period that it is functional! :-p
heh :)
Dumb question, to be certain that I understood your first paragraph:
you do indeed see at least -some- success talking to devices on port 1,
2, 3... ? i.e. not just port 0 works?
> Booting without ADMA gives the usual stable behavior, with the long
> latencies from the status inb().
Weird. Well, now that we appear to have narrowed the non-ADMA case down
to inb(), I'm going to punt this one as not-my-problem ;-)
> I was a little disconcerted when I saw this this in the trace with ADMA
> disabled,
>
> tar-21466 0dnh. 3979us : nv_check_hotplug_adma (nv_interrupt)
>
> until I realized that this
>
> if (!adma_enabled && host_desc->host_type == ADMA)
> host_desc->host_type--;
>
> only alters the outcome of the "host_desc->host_type == ADMA" test, but
> still uses the ADMA-based hotplug functions.
Yep. That's part of my general plan to eliminate all the
if (adma)
foo
else
bar
type code in favor to create separate ADMA and non-ADMA hooks.
Particularly in the key hot paths, sata_nv should avoid asking "are we
ADMA?" all the time.
Jeff
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-21 1:28 ` Jeff Garzik
@ 2006-03-21 12:48 ` Bill Rugolsky Jr.
2006-03-27 1:14 ` Matt Heler
1 sibling, 0 replies; 14+ messages in thread
From: Bill Rugolsky Jr. @ 2006-03-21 12:48 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linux-kernel, linux-ide@vger.kernel.org
On Mon, Mar 20, 2006 at 08:28:13PM -0500, Jeff Garzik wrote:
> Thanks a lot for testing.
>
> I've stored the sata_nv updates I sent you in the 'nv-adma' branch of
> git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
OK, I'll pull from there for further testing.
> Dumb question, to be certain that I understood your first paragraph:
> you do indeed see at least -some- success talking to devices on port 1,
> 2, 3... ? i.e. not just port 0 works?
I can start up the RAID1's on ports 2 and 3, activate the VG on top
of /dev/md5, mount the filesystems, and use them. I was able to
cp -a the mounted root filesystem from /dev/md2 to a subdir on /dev/sda1.
It was only when I started up a few copies of "tar cf /dev/zero ... &"
that the timeouts began.
> Weird. Well, now that we appear to have narrowed the non-ADMA case down
> to inb(), I'm going to punt this one as not-my-problem ;-)
Roger. I'll stare at it a bit longer, but I probably need to order three
SATA adapters. Any recommendations? These Tyan 2895 machines all have
2-4 74GB WD Raptors in them. I don't really need expensive 3ware or
Areca cards for these workstations, just reasonable latencies.
Thanks.
-Bill
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-21 1:28 ` Jeff Garzik
2006-03-21 12:48 ` Bill Rugolsky Jr.
@ 2006-03-27 1:14 ` Matt Heler
2006-03-27 16:08 ` Bill Rugolsky Jr.
1 sibling, 1 reply; 14+ messages in thread
From: Matt Heler @ 2006-03-27 1:14 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Bill Rugolsky Jr., linux-kernel, linux-ide@vger.kernel.org
Hey Jeff,
Using Bill's original patch I was able to boot up perfectly with adma support
enabled on my workstation. Even after several stress tests (
tar -cf /dev/null . , and dd if=/dev/sda of=/dev/null ), everything seems to
be a-ok. However when I tried the sata_nv.c file that you sent to Bill, I
kept on getting hardlocks, and thus was unable to stress test your version.
Also for note, I heve not received any of the timeout problems reported by
Bill. Nor have I had any latency problems with adma enabled.
Matt Heler
On Monday 20 March 2006 8:28 pm, Jeff Garzik wrote:
> Bill Rugolsky Jr. wrote:
> > On Sat, Mar 18, 2006 at 03:56:28AM -0500, Jeff Garzik wrote:
> >>OK, can you try the attached sata_nv.c? Does it perform to the level
> >>that yours does?
> >
> > Yes, the results are approximately the same. Booting from port 0 (sda)
> > with ADMA enabled still results in timeouts on port 3 (sdc) while
> > running tars on the RAID1 array on ports 2&3.
>
> Thanks a lot for testing.
>
> I've stored the sata_nv updates I sent you in the 'nv-adma' branch of
> git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
>
> > ata4: command 0x25 timeout, stat 0x50
> > ata4: command 0x25 timeout, stat 0x50
> > ( xterm-3349 |#0): new 355 us maximum-latency wakeup.
> > ( watchdog/0-4 |#0): new 468 us maximum-latency wakeup.
> > ata4: command 0x35 timeout, stat 0x50
> > ata4: command 0x35 timeout, stat 0x50
> > ata4: command 0x35 timeout, stat 0x50
> > ata4: command 0x35 timeout, stat 0x50
> > ata4: command 0x35 timeout, stat 0x50
> > ata4: command 0x35 timeout, stat 0x50
> >
> > After a while, syncing the filesystems hangs the sync process, though
> > the system continues to function, and I can log in on another VC.
>
> hmmm. Sounds like some attention should be paid to the error handling
> portion of the code.
>
> > The good news: no long latencies from the status inb() during the
> > period that it is functional! :-p
>
> heh :)
>
> Dumb question, to be certain that I understood your first paragraph:
> you do indeed see at least -some- success talking to devices on port 1,
> 2, 3... ? i.e. not just port 0 works?
>
> > Booting without ADMA gives the usual stable behavior, with the long
> > latencies from the status inb().
>
> Weird. Well, now that we appear to have narrowed the non-ADMA case down
> to inb(), I'm going to punt this one as not-my-problem ;-)
>
> > I was a little disconcerted when I saw this this in the trace with ADMA
> > disabled,
> >
> > tar-21466 0dnh. 3979us : nv_check_hotplug_adma (nv_interrupt)
> >
> > until I realized that this
> >
> > if (!adma_enabled && host_desc->host_type == ADMA)
> > host_desc->host_type--;
> >
> > only alters the outcome of the "host_desc->host_type == ADMA" test, but
> > still uses the ADMA-based hotplug functions.
>
> Yep. That's part of my general plan to eliminate all the
>
> if (adma)
> foo
> else
> bar
>
> type code in favor to create separate ADMA and non-ADMA hooks.
> Particularly in the key hot paths, sata_nv should avoid asking "are we
> ADMA?" all the time.
>
> Jeff
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-27 1:14 ` Matt Heler
@ 2006-03-27 16:08 ` Bill Rugolsky Jr.
2006-03-27 21:46 ` Matt Heler
2006-06-07 22:09 ` Dan Carpenter
0 siblings, 2 replies; 14+ messages in thread
From: Bill Rugolsky Jr. @ 2006-03-27 16:08 UTC (permalink / raw)
To: Matt Heler; +Cc: Jeff Garzik, linux-kernel, linux-ide@vger.kernel.org
On Sun, Mar 26, 2006 at 08:14:35PM -0500, Matt Heler wrote:
> Using Bill's original patch I was able to boot up perfectly with adma support
> enabled on my workstation. Even after several stress tests (
> tar -cf /dev/null . , and dd if=/dev/sda of=/dev/null ), everything seems to
> be a-ok. However when I tried the sata_nv.c file that you sent to Bill, I
> kept on getting hardlocks, and thus was unable to stress test your version.
>
> Also for note, I heve not received any of the timeout problems reported by
> Bill. Nor have I had any latency problems with adma enabled.
Matt,
Nice to see some value falling out of this sata_nv thread. Did you see
latency problems before enabling ADMA?
Would you provide some specifics on your setup?
Which motherboard, #CPUs, BIOS revision, kernel, MD/LVM2/fs?
On two of my Tyan S2895 machines, including the one that I'm using for testing,
lspci says:
00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev f2)
00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev f3)
00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev f3)
and dmidecode says:
BIOS Information
Vendor: Phoenix Technologies Ltd.
Version: 2004Q3
Release Date: 10/12/2005
The other, where I first had lost tick problems, says:
00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev a2)
00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev a3)
00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev a3)
BIOS Information
Vendor: Phoenix Technologies Ltd.
Version: 2004Q3
Release Date: 06/07/2005
Thanks,
Bill
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-27 16:08 ` Bill Rugolsky Jr.
@ 2006-03-27 21:46 ` Matt Heler
2006-03-27 22:00 ` Matt Heler
2006-06-07 22:09 ` Dan Carpenter
1 sibling, 1 reply; 14+ messages in thread
From: Matt Heler @ 2006-03-27 21:46 UTC (permalink / raw)
To: Bill Rugolsky Jr., Jeff Garzik, linux-kernel,
linux-ide@vger.kernel.org
Hey Bill,
I spoke abit to soon though regarding that stability.. After booting up X and
running Azerues and kde, my system stalled and locked. However it lasted much
longer then Jeff's version.
I'm running the DFI Lanparty with an Athlon 4400. I also have the following
setup
2x300gb seagate drives 7200.8 in a raid0 format with ext3
2x400gb seagate drives 7200.9 again in a raid0 format with ext3
and lspci gives me the following ::
00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev a2)
00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev a3)
00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev a3)
On Monday 27 March 2006 11:08 am, Bill Rugolsky Jr. wrote:
> On Sun, Mar 26, 2006 at 08:14:35PM -0500, Matt Heler wrote:
> > Using Bill's original patch I was able to boot up perfectly with adma
> > support enabled on my workstation. Even after several stress tests (
> > tar -cf /dev/null . , and dd if=/dev/sda of=/dev/null ), everything seems
> > to be a-ok. However when I tried the sata_nv.c file that you sent to
> > Bill, I kept on getting hardlocks, and thus was unable to stress test
> > your version.
> >
> > Also for note, I heve not received any of the timeout problems reported
> > by Bill. Nor have I had any latency problems with adma enabled.
>
> Matt,
>
> Nice to see some value falling out of this sata_nv thread. Did you see
> latency problems before enabling ADMA?
>
> Would you provide some specifics on your setup?
>
> Which motherboard, #CPUs, BIOS revision, kernel, MD/LVM2/fs?
>
> On two of my Tyan S2895 machines, including the one that I'm using for
> testing, lspci says:
>
> 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev f2)
> 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev
> f3) 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller
> (rev f3)
>
> and dmidecode says:
>
> BIOS Information
> Vendor: Phoenix Technologies Ltd.
> Version: 2004Q3
> Release Date: 10/12/2005
>
> The other, where I first had lost tick problems, says:
>
> 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev a2)
> 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev
> a3) 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller
> (rev a3)
>
> BIOS Information
> Vendor: Phoenix Technologies Ltd.
> Version: 2004Q3
> Release Date: 06/07/2005
>
>
> Thanks,
>
> Bill
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-27 21:46 ` Matt Heler
@ 2006-03-27 22:00 ` Matt Heler
2006-03-30 1:23 ` Alexander Samad
0 siblings, 1 reply; 14+ messages in thread
From: Matt Heler @ 2006-03-27 22:00 UTC (permalink / raw)
To: Bill Rugolsky Jr.; +Cc: Jeff Garzik, linux-kernel, linux-ide@vger.kernel.org
I forgot to include this.. but here's my bios version
Vendor: Phoenix Technologies, LTD
Version: 6.00 PG
Release Date: 06/23/2005
On Monday 27 March 2006 4:46 pm, Matt Heler wrote:
> Hey Bill,
>
> I spoke abit to soon though regarding that stability.. After booting up X
> and running Azerues and kde, my system stalled and locked. However it
> lasted much longer then Jeff's version.
>
> I'm running the DFI Lanparty with an Athlon 4400. I also have the following
> setup
>
> 2x300gb seagate drives 7200.8 in a raid0 format with ext3
> 2x400gb seagate drives 7200.9 again in a raid0 format with ext3
>
> and lspci gives me the following ::
>
> 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev a2)
> 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev
> a3) 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller
> (rev a3)
>
> On Monday 27 March 2006 11:08 am, Bill Rugolsky Jr. wrote:
> > On Sun, Mar 26, 2006 at 08:14:35PM -0500, Matt Heler wrote:
> > > Using Bill's original patch I was able to boot up perfectly with adma
> > > support enabled on my workstation. Even after several stress tests (
> > > tar -cf /dev/null . , and dd if=/dev/sda of=/dev/null ), everything
> > > seems to be a-ok. However when I tried the sata_nv.c file that you sent
> > > to Bill, I kept on getting hardlocks, and thus was unable to stress
> > > test your version.
> > >
> > > Also for note, I heve not received any of the timeout problems reported
> > > by Bill. Nor have I had any latency problems with adma enabled.
> >
> > Matt,
> >
> > Nice to see some value falling out of this sata_nv thread. Did you see
> > latency problems before enabling ADMA?
> >
> > Would you provide some specifics on your setup?
> >
> > Which motherboard, #CPUs, BIOS revision, kernel, MD/LVM2/fs?
> >
> > On two of my Tyan S2895 machines, including the one that I'm using for
> > testing, lspci says:
> >
> > 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev f2)
> > 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller
> > (rev f3) 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA
> > Controller (rev f3)
> >
> > and dmidecode says:
> >
> > BIOS Information
> > Vendor: Phoenix Technologies Ltd.
> > Version: 2004Q3
> > Release Date: 10/12/2005
> >
> > The other, where I first had lost tick problems, says:
> >
> > 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev a2)
> > 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller
> > (rev a3) 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA
> > Controller (rev a3)
> >
> > BIOS Information
> > Vendor: Phoenix Technologies Ltd.
> > Version: 2004Q3
> > Release Date: 06/07/2005
> >
> >
> > Thanks,
> >
> > Bill
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-27 22:00 ` Matt Heler
@ 2006-03-30 1:23 ` Alexander Samad
0 siblings, 0 replies; 14+ messages in thread
From: Alexander Samad @ 2006-03-30 1:23 UTC (permalink / raw)
To: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 4101 bytes --]
Hi
I was wondering if the patch for sata_nv to hanlde the lost interrupts
has been submitted for inclussion in the kernel.
I have sort of been following this thread, were it seems that the
interrupt problem has been fixed, but they were also going to fix the
adma problem as well.
I did a quick check of 2.6.16 changelog and see no mention for sata_nv.
I seem to suffere from this issue quite often - doing dvd burns across a
network.....
Thanks
On Mon, Mar 27, 2006 at 05:00:53PM -0500, Matt Heler wrote:
> I forgot to include this.. but here's my bios version
>
> Vendor: Phoenix Technologies, LTD
> Version: 6.00 PG
> Release Date: 06/23/2005
>
> On Monday 27 March 2006 4:46 pm, Matt Heler wrote:
> > Hey Bill,
> >
> > I spoke abit to soon though regarding that stability.. After booting up X
> > and running Azerues and kde, my system stalled and locked. However it
> > lasted much longer then Jeff's version.
> >
> > I'm running the DFI Lanparty with an Athlon 4400. I also have the following
> > setup
> >
> > 2x300gb seagate drives 7200.8 in a raid0 format with ext3
> > 2x400gb seagate drives 7200.9 again in a raid0 format with ext3
> >
> > and lspci gives me the following ::
> >
> > 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev a2)
> > 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller (rev
> > a3) 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller
> > (rev a3)
> >
> > On Monday 27 March 2006 11:08 am, Bill Rugolsky Jr. wrote:
> > > On Sun, Mar 26, 2006 at 08:14:35PM -0500, Matt Heler wrote:
> > > > Using Bill's original patch I was able to boot up perfectly with adma
> > > > support enabled on my workstation. Even after several stress tests (
> > > > tar -cf /dev/null . , and dd if=/dev/sda of=/dev/null ), everything
> > > > seems to be a-ok. However when I tried the sata_nv.c file that you sent
> > > > to Bill, I kept on getting hardlocks, and thus was unable to stress
> > > > test your version.
> > > >
> > > > Also for note, I heve not received any of the timeout problems reported
> > > > by Bill. Nor have I had any latency problems with adma enabled.
> > >
> > > Matt,
> > >
> > > Nice to see some value falling out of this sata_nv thread. Did you see
> > > latency problems before enabling ADMA?
> > >
> > > Would you provide some specifics on your setup?
> > >
> > > Which motherboard, #CPUs, BIOS revision, kernel, MD/LVM2/fs?
> > >
> > > On two of my Tyan S2895 machines, including the one that I'm using for
> > > testing, lspci says:
> > >
> > > 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev f2)
> > > 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller
> > > (rev f3) 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA
> > > Controller (rev f3)
> > >
> > > and dmidecode says:
> > >
> > > BIOS Information
> > > Vendor: Phoenix Technologies Ltd.
> > > Version: 2004Q3
> > > Release Date: 10/12/2005
> > >
> > > The other, where I first had lost tick problems, says:
> > >
> > > 00:06.0 IDE interface: nVidia Corporation CK804 IDE (rev a2)
> > > 00:07.0 IDE interface: nVidia Corporation CK804 Serial ATA Controller
> > > (rev a3) 00:08.0 IDE interface: nVidia Corporation CK804 Serial ATA
> > > Controller (rev a3)
> > >
> > > BIOS Information
> > > Vendor: Phoenix Technologies Ltd.
> > > Version: 2004Q3
> > > Release Date: 06/07/2005
> > >
> > >
> > > Thanks,
> > >
> > > Bill
> >
> > -
> > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> > Please read the FAQ at http://www.tux.org/lkml/
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 191 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-03-27 16:08 ` Bill Rugolsky Jr.
2006-03-27 21:46 ` Matt Heler
@ 2006-06-07 22:09 ` Dan Carpenter
2006-06-09 15:32 ` Bill Rugolsky Jr.
1 sibling, 1 reply; 14+ messages in thread
From: Dan Carpenter @ 2006-06-07 22:09 UTC (permalink / raw)
To: Bill Rugolsky Jr., Matt Heler, Jeff Garzik, linux-kernel,
linux-ide@vger.kernel.org
Bill Rugolsky Jr. <bill@rugolsky.com> wrote:
> Here is an incomplete attempt to get the sata_nv ADMA code working.
>
> I made another pass through my merge of the sata_nv driver to fix and
> clean up a few things. I've only made the minimal changes necessary
> to get it into a testable state; methinks it could do with a lot of
> refactoring and cleanup, instead of all the "if (host->...host_type == ADMA)" tests.
>
Did you ever figure this out?
It looks like you're mixing two completely seperate bugs. The ata
timeouts and the lost ticks messages.
> BIOS Information
> Vendor: Phoenix Technologies Ltd.
> Version: 2004Q3
> Release Date: 10/12/2005
>
This is causing the sata failures:
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=190856
Downgrade to match your other system (version 1.01). That will fix
the ata timeouts at least.
I'm still interested in the lost ticks messages though...
regards,
dan carpenter
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH][INCOMPLETE] sata_nv: merge ADMA support
2006-06-07 22:09 ` Dan Carpenter
@ 2006-06-09 15:32 ` Bill Rugolsky Jr.
0 siblings, 0 replies; 14+ messages in thread
From: Bill Rugolsky Jr. @ 2006-06-09 15:32 UTC (permalink / raw)
To: Dan Carpenter
Cc: Matt Heler, Jeff Garzik, linux-kernel, linux-ide@vger.kernel.org
On Wed, Jun 07, 2006 at 03:09:31PM -0700, Dan Carpenter wrote:
> Bill Rugolsky Jr. <bill@rugolsky.com> wrote:
> >Here is an incomplete attempt to get the sata_nv ADMA code working.
> >
> >I made another pass through my merge of the sata_nv driver to fix and
> >clean up a few things. I've only made the minimal changes necessary
> >to get it into a testable state; methinks it could do with a lot of
> >refactoring and cleanup, instead of all the "if (host->...host_type ==
> >ADMA)" tests.
> >
>
> Did you ever figure this out?
>
> It looks like you're mixing two completely seperate bugs. The ata
> timeouts and the lost ticks messages.
>
> >BIOS Information
> > Vendor: Phoenix Technologies Ltd.
> > Version: 2004Q3
> > Release Date: 10/12/2005
> >
>
> This is causing the sata failures:
> https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=190856
> Downgrade to match your other system (version 1.01). That will fix
> the ata timeouts at least.
Interesting; I still have one such machine set aside for testing purposes,
so I can give that a try; in my limited testing, ADMA didn't exhibit the
same long latencies.
I'm just rolling up a 2.6.17 kernel patchset, so I'll pull in Jeff's
sata_nv branch and give it a try; the patch queue is long though, so
it will probably be sometime next week.
> I'm still interested in the lost ticks messages though...
My workaround has been to just use John Stultz's new timekeeping
code (actually the variant in Thomas Gleixner's hrtimers rollup --
http://tglx.de/projects/hrtimers/). Our PostgreSQL servers now maintain
sub-millisecond NTP offsets, whereas previously they would drift 100+ms away,
lose sync, and require an NTP restart every day or so.
John wrote the other day:
With the current code in -mm I can run a test app that disables
interrupts for 2 seconds at a time over and over and I'm still keeping
synched w/ an NTP server within 30 microseconds.
The patches in -mm are set to be merged in 2.6.18-rc1, so I have no qualms
about including them in my internal 2.6.17 patchset.
Kudos to John, Thomas, Ingo, and others who have worked on the new
timekeeping code. This is yet another area where work on the -rt kernel
benefits everyone in a measurable way.
-Bill
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2006-06-09 15:32 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-03-17 23:23 [PATCH][INCOMPLETE] sata_nv: merge ADMA support Bill Rugolsky Jr.
2006-03-18 0:56 ` Jeff Garzik
2006-03-18 8:06 ` Bill Rugolsky Jr.
2006-03-18 8:56 ` Jeff Garzik
2006-03-19 23:23 ` Bill Rugolsky Jr.
2006-03-21 1:28 ` Jeff Garzik
2006-03-21 12:48 ` Bill Rugolsky Jr.
2006-03-27 1:14 ` Matt Heler
2006-03-27 16:08 ` Bill Rugolsky Jr.
2006-03-27 21:46 ` Matt Heler
2006-03-27 22:00 ` Matt Heler
2006-03-30 1:23 ` Alexander Samad
2006-06-07 22:09 ` Dan Carpenter
2006-06-09 15:32 ` Bill Rugolsky Jr.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox