* [PATCH] Add Marvell 6141 PATA support to AHCI driver
@ 2007-05-27 3:09 Jeff Garzik
2007-05-27 12:18 ` Alan Cox
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Jeff Garzik @ 2007-05-27 3:09 UTC (permalink / raw)
To: linux-ide; +Cc: LKML, Alan Cox
Here is a patch against 2.6.22-rc3 that adds support for both the PATA
and SATA portions of the Marvell AHCI-like chip.
The architecture for PATA is quite nice, mimicing AHCI very closely.
Basic port scanning, interrupt handling, freezing and thawing is the
same, and uses the same register offets.
This is completely untested code. But it looks like it should work :)
Engineering questions I need to bounce back to Marvell:
* do I need to worry about PIO/UDMA timing? I don't see any registers
or other knobs dealing with timing. Maybe the controller snoops?
* it appears that I don't have to worry about device selection, that
the controller will handle this for me. But I want to make sure.
* figure out controller commands. these are poorly documented.
controller commands are how one directly accesses the PATA device's
command and control registers, and are necessary to do things like
SRST. Currently the driver only does hard reset.
This is checked into the 'mv-ahci-pata' branch of
git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
drivers/ata/ahci.c | 496 +++++++++++++++++++++++++++++++++++++++++++++++-----
include/linux/ata.h | 1
2 files changed, 453 insertions(+), 44 deletions(-)
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 7baeaff..5bbca8f 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -81,6 +81,7 @@ enum {
board_ahci_vt8251 = 2,
board_ahci_ign_iferr = 3,
board_ahci_sb600 = 4,
+ board_ahci_mv = 5,
/* global controller registers */
HOST_CAP = 0x00, /* host capabilities */
@@ -171,6 +172,7 @@ enum {
AHCI_FLAG_HONOR_PI = (1 << 26), /* honor PORTS_IMPL */
AHCI_FLAG_IGN_SERR_INTERNAL = (1 << 27), /* ignore SERR_INTERNAL */
AHCI_FLAG_32BIT_ONLY = (1 << 28), /* force 32bit */
+ AHCI_FLAG_MV_PATA = (1 << 29), /* PATA port */
AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
@@ -178,6 +180,46 @@ enum {
ATA_FLAG_ACPI_SATA,
};
+enum mvp_specific_registers {
+ MVP_D0_TF0 = 0x20, /* dev0 taskfile part 1 */
+ MVP_D0_TF1 = 0x24, /* dev0 taskfile part 2 */
+ MVP_D0_TF2 = 0x28, /* dev0 taskfile part 3 */
+ MVP_D1_TF0 = 0x2C, /* dev1 taskfile part 1 */
+ MVP_D1_TF1 = 0x30, /* dev1 taskfile part 2 */
+ MVP_D1_TF2 = 0x3C, /* dev1 taskfile part 3 */
+};
+
+enum mvp_specific_bits {
+ /* PORT_CMD register */
+ MVP_CMD_HW_RST = (1 << 3), /* Generate H_RESET_N to
+ * attached devices.
+ * Self de-asserting.
+ */
+
+ /* PORT_IRQ_[STAT,MASK] registers */
+ MVP_IRQ_D0_INTRQ = (1 << 0), /* dev0 INTRQ line */
+ MVP_IRQ_D0_ERR = (1 << 1), /* ERR in dev0 Status reg */
+ MVP_IRQ_D1_INTRQ = (1 << 2), /* dev1 INTRQ line */
+ MVP_IRQ_D1_ERR = (1 << 3), /* ERR in dev1 Status reg */
+ MVP_IRQ_FREEZE = 0,
+ MVP_IRQ_ERROR = MVP_IRQ_FREEZE |
+ MVP_IRQ_D0_ERR | MVP_IRQ_D1_ERR,
+ DEF_MVP_IRQ = MVP_IRQ_ERROR |
+ MVP_IRQ_D0_INTRQ | MVP_IRQ_D1_INTRQ,
+
+ MVP_CMD_DEV1 = (1 << 15), /* dev0 == 0, dev1 == 1 */
+ MVP_CMD_ATAPI_RST = (1 << 14), /* cmd==ATA_CMD_DEV_RESET */
+ MVP_CMD_EDD = (1 << 13), /* cmd==ATA_CMD_EDD */
+ MVP_CMD_LBA48 = (1 << 12), /* is an LBA48 cmd */
+ MVP_CMD_PIO_SECT = (1 << 11), /* PIO single/mult sector cmd */
+ MVP_CMD_NODATA = (1 << 10), /* is a non-data cmd */
+ MVP_CMD_READ = (1 << 9), /* data-in (dev->host) */
+ MVP_CMD_DMA = (1 << 8), /* ATA or ATAPI DMA */
+ MVP_CMD_ATAPI = (1 << 7), /* is a PACKET cmd */
+ MVP_CMD_TCQ = (1 << 6), /* is a legacy TCQ cmd */
+ MVP_CMD_CONTROLLER = (1 << 5), /* PATA controller command */
+};
+
struct ahci_cmd_hdr {
u32 opts;
u32 status;
@@ -211,6 +253,8 @@ struct ahci_port_priv {
unsigned int ncq_saw_d2h:1;
unsigned int ncq_saw_dmas:1;
unsigned int ncq_saw_sdb:1;
+
+ int mvp_selected;
};
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
@@ -228,13 +272,22 @@ static void ahci_thaw(struct ata_port *ap);
static void ahci_error_handler(struct ata_port *ap);
static void ahci_vt8251_error_handler(struct ata_port *ap);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
+static int ahci_port_resume(struct ata_port *ap);
+static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl);
+static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
+ u32 opts);
#ifdef CONFIG_PM
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
-static int ahci_port_resume(struct ata_port *ap);
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
static int ahci_pci_device_resume(struct pci_dev *pdev);
#endif
+static void mvp_dev_select(struct ata_port *ap, unsigned int device);
+static u8 mvp_check_status(struct ata_port *ap);
+static void mvp_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
+static void mvp_qc_prep(struct ata_queued_cmd *qc);
+static void mvp_error_handler(struct ata_port *ap);
+
static struct scsi_host_template ahci_sht = {
.module = THIS_MODULE,
.name = DRV_NAME,
@@ -322,6 +375,36 @@ static const struct ata_port_operations ahci_vt8251_ops = {
.port_stop = ahci_port_stop,
};
+static const struct ata_port_operations mvp_ops = {
+ .port_disable = ata_port_disable,
+
+ .check_status = mvp_check_status,
+ .check_altstatus = mvp_check_status,
+ .dev_select = mvp_dev_select,
+
+ .tf_read = mvp_tf_read,
+
+ .qc_prep = mvp_qc_prep,
+ .qc_issue = ahci_qc_issue,
+
+ .irq_clear = ahci_irq_clear,
+ .irq_on = ata_dummy_irq_on,
+ .irq_ack = ata_dummy_irq_ack,
+
+ .freeze = ahci_freeze,
+ .thaw = ahci_thaw,
+
+ .error_handler = mvp_error_handler,
+
+#ifdef CONFIG_PM
+ .port_suspend = ahci_port_suspend,
+ .port_resume = ahci_port_resume,
+#endif
+
+ .port_start = ahci_port_start,
+ .port_stop = ahci_port_stop,
+};
+
static const struct ata_port_info ahci_port_info[] = {
/* board_ahci */
{
@@ -361,6 +444,16 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = 0x7f, /* udma0-6 ; FIXME */
.port_ops = &ahci_ops,
},
+ /* board_ahci_mv */
+ {
+ .sht = &ahci_sht,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
+ ATA_FLAG_SKIP_D2H_BSY | AHCI_FLAG_HONOR_PI,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
+ .port_ops = &ahci_ops,
+ },
};
static const struct pci_device_id ahci_pci_tbl[] = {
@@ -432,6 +525,9 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 966 */
{ PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */
+ /* Marvell */
+ { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */
+
/* Generic, PCI class code for AHCI */
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci },
@@ -457,11 +553,17 @@ static inline int ahci_nr_ports(u32 cap)
return (cap & 0x1f) + 1;
}
-static inline void __iomem *ahci_port_base(struct ata_port *ap)
+static inline void __iomem *__ahci_port_base(struct ata_host *host,
+ unsigned int port_no)
{
- void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
+ void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
- return mmio + 0x100 + (ap->port_no * 0x80);
+ return mmio + 0x100 + (port_no * 0x80);
+}
+
+static inline void __iomem *ahci_port_base(struct ata_port *ap)
+{
+ return __ahci_port_base(ap->host, ap->port_no);
}
/**
@@ -716,7 +818,7 @@ static void ahci_power_down(struct ata_port *ap)
}
#endif
-static void ahci_init_port(struct ata_port *ap)
+static void ahci_start_port(struct ata_port *ap)
{
/* enable FIS reception */
ahci_start_fis_rx(ap);
@@ -737,7 +839,8 @@ static int ahci_deinit_port(struct ata_port *ap, const char **emsg)
}
/* disable FIS reception */
- rc = ahci_stop_fis_rx(ap);
+ if (!(ap->flags & AHCI_FLAG_MV_PATA))
+ rc = ahci_stop_fis_rx(ap);
if (rc) {
*emsg = "failed stop FIS RX";
return rc;
@@ -790,39 +893,322 @@ static int ahci_reset_controller(struct ata_host *host)
return 0;
}
+static void mvp_dev_select(struct ata_port *ap, unsigned int device)
+{
+ struct ahci_port_priv *pp = ap->private_data;
+
+ pp->mvp_selected = device;
+}
+
+/* WARNING: the following doesn't clear the interrupt, it's
+ * a read-only view of the device's ATA shadow registers
+ */
+static u8 mvp_check_status(struct ata_port *ap)
+{
+ void __iomem *mmio = ahci_port_base(ap);
+ struct ahci_port_priv *pp = ap->private_data;
+
+ if (pp->mvp_selected == 0)
+ mmio += MVP_D0_TF0;
+ else
+ mmio += MVP_D1_TF0;
+
+ return readl(mmio); /* lower 8 bits are Status */
+}
+
+/* WARNING: the following doesn't clear the interrupt, it's
+ * a read-only view of the device's ATA shadow registers
+ */
+static void mvp_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ void __iomem *mmio = ahci_port_base(ap);
+ struct ahci_port_priv *pp = ap->private_data;
+ u32 val;
+
+ if (pp->mvp_selected == 0)
+ val = readl(mmio + MVP_D0_TF0);
+ else
+ val = readl(mmio + MVP_D1_TF0);
+ tf->feature = val >> 24;
+ tf->device = val >> 8;
+ tf->command = val;
+
+ if (pp->mvp_selected == 0)
+ val = readl(mmio + MVP_D0_TF1);
+ else
+ val = readl(mmio + MVP_D1_TF1);
+ tf->nsect = val >> 24;
+ tf->hob_nsect = val >> 16;
+ tf->lbal = val >> 8;
+ tf->hob_lbal = val;
+
+ if (pp->mvp_selected == 0)
+ val = readl(mmio + MVP_D0_TF2);
+ else
+ val = readl(mmio + MVP_D1_TF2);
+ tf->lbam = val >> 24;
+ tf->hob_lbam = val >> 16;
+ tf->lbah = val >> 8;
+ tf->hob_lbah = val;
+}
+
+static void mvp_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ahci_port_priv *pp = ap->private_data;
+ int is_atapi = is_atapi_taskfile(&qc->tf);
+ void *cmd_tbl;
+ u32 *cmd_block;
+ u32 opts;
+ unsigned int n_elem;
+
+ /*
+ * Fill in command table information. First, the header,
+ * a SATA Register - Host to Device command FIS.
+ */
+ cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ;
+
+ /* FIXME: this hardcodes use of the ATA CDB format,
+ * excluding the "controller command" feature that
+ * we will need to use for direct PATA signal twiddling
+ */
+ cmd_block = cmd_tbl;
+ cmd_block[0] =
+ ((u32)qc->tf.feature) |
+ (((u32)qc->tf.hob_feature) << 8) |
+ (((u32)qc->tf.nsect) << 16) |
+ (((u32)qc->tf.hob_nsect) << 24);
+ cmd_block[1] =
+ ((u32)qc->tf.lbal) |
+ (((u32)qc->tf.hob_lbal) << 8) |
+ (((u32)qc->tf.lbam) << 16) |
+ (((u32)qc->tf.hob_lbam) << 24);
+ cmd_block[2] =
+ ((u32)qc->tf.command) |
+ (((u32)qc->tf.device) << 8) |
+ (((u32)qc->tf.lbah) << 16) |
+ (((u32)qc->tf.hob_lbah) << 24);
+
+ if (is_atapi) {
+ memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32);
+ memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len);
+ }
+
+ n_elem = 0;
+ if (qc->flags & ATA_QCFLAG_DMAMAP)
+ n_elem = ahci_fill_sg(qc, cmd_tbl);
+
+ /*
+ * Fill in command slot information.
+ */
+ opts = n_elem << 16;
+ if (qc->dev->devno == 1)
+ opts |= MVP_CMD_DEV1;
+ if (qc->tf.command == ATA_CMD_DEV_RESET)
+ opts |= MVP_CMD_ATAPI_RST;
+ if (qc->tf.command == ATA_CMD_EDD)
+ opts |= MVP_CMD_EDD;
+ if (qc->tf.flags & ATA_TFLAG_LBA48)
+ opts |= MVP_CMD_LBA48;
+ if (qc->tf.protocol == ATA_PROT_PIO)
+ opts |= MVP_CMD_PIO_SECT;
+ if ((qc->tf.protocol == ATA_PROT_NODATA) ||
+ (qc->tf.protocol == ATA_PROT_ATAPI_NODATA))
+ opts |= MVP_CMD_NODATA;
+ if (!(qc->tf.flags & ATA_TFLAG_WRITE))
+ opts |= MVP_CMD_READ;
+ if ((qc->tf.protocol == ATA_PROT_DMA) ||
+ (qc->tf.protocol == ATA_PROT_ATAPI_DMA))
+ opts |= MVP_CMD_DMA;
+ if (is_atapi)
+ opts |= MVP_CMD_ATAPI;
+ opts |= (qc->dev->multi_count & 0x1f);
+
+ ahci_fill_cmd_slot(pp, qc->tag, opts);
+}
+
+static void mvp_error_intr(struct ata_port *ap, u32 irq_stat)
+{
+ struct ata_queued_cmd *qc;
+ struct ata_eh_info *ehi = &ap->eh_info;
+ unsigned int err_mask = 0, action = 0;
+
+ ata_ehi_clear_desc(ehi);
+
+ /* analyze @irq_stat */
+ ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
+
+ if (irq_stat & (MVP_IRQ_D0_ERR | MVP_IRQ_D1_ERR))
+ err_mask |= AC_ERR_DEV;
+
+ ehi->action |= action;
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (qc)
+ qc->err_mask |= err_mask;
+ else
+ ehi->err_mask |= err_mask;
+
+ if (irq_stat & MVP_IRQ_FREEZE)
+ ata_port_freeze(ap);
+ else
+ ata_port_abort(ap);
+}
+
+static void mvp_port_intr(struct ata_port *ap)
+{
+ void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+ u32 status, qc_active;
+ int rc;
+
+ status = readl(port_mmio + PORT_IRQ_STAT);
+ writel(status, port_mmio + PORT_IRQ_STAT);
+
+ if (unlikely(status & MVP_IRQ_ERROR)) {
+ mvp_error_intr(ap, status);
+ return;
+ }
+
+ qc_active = readl(port_mmio + PORT_CMD_ISSUE);
+
+ rc = ata_qc_complete_multiple(ap, qc_active, NULL);
+ if (rc > 0)
+ return;
+ if (rc < 0) {
+ struct ata_eh_info *ehi = &ap->eh_info;
+ ehi->err_mask |= AC_ERR_HSM;
+ ehi->action |= ATA_EH_SOFTRESET;
+ ata_port_freeze(ap);
+ return;
+ }
+
+ /* if we get here... spurious interrupt? */
+}
+
+static int mvp_hardreset(struct ata_port *ap, unsigned int *class,
+ unsigned long deadline)
+{
+ void __iomem *port_mmio = ap->ioaddr.cmd_addr;
+ u32 tmp;
+ int rc;
+
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp |= MVP_CMD_HW_RST;
+ writel(tmp, port_mmio + PORT_CMD);
+
+ msleep(150); /* paranoia... guessing that it might take
+ * a tiny bit of time for MVP_CMD_HW_RST
+ * to appear asserted in the register?
+ */
+
+ tmp = ata_wait_register(port_mmio + PORT_CMD,
+ MVP_CMD_HW_RST, MVP_CMD_HW_RST, 2, 500);
+ if (tmp & MVP_CMD_HW_RST) {
+ ata_port_printk(ap, KERN_ERR,
+ "hard reset timed out (errno=%d)\n", -EIO);
+ return -EIO;
+ }
+
+ rc = ata_wait_ready(ap, deadline);
+ if (rc) {
+ ata_port_printk(ap, KERN_ERR,
+ "hard reset failed (errno=%d)\n", rc);
+ return rc;
+ }
+
+ *class = ata_dev_try_classify(ap, 0, NULL);
+
+ return 0;
+}
+
+static void mvp_error_handler(struct ata_port *ap)
+{
+ if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
+ /* restart engine */
+ ahci_stop_engine(ap);
+ ahci_start_engine(ap);
+ }
+
+ /* perform recovery */
+ ata_do_eh(ap, ata_std_prereset, NULL, mvp_hardreset, ata_std_postreset);
+}
+
+static void mvp_port_init(struct pci_dev *pdev, struct ata_port *ap,
+ int port_no, void __iomem *mmio,
+ void __iomem *port_mmio)
+{
+ int rc;
+ u32 tmp;
+ const char *emsg = NULL;
+
+ /* make sure port is not active */
+ rc = ahci_deinit_port(ap, &emsg);
+ if (rc)
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "%s (%d)\n", emsg, rc);
+
+ /* select the IRQ events we're interested in */
+ writel(DEF_MVP_IRQ, port_mmio + PORT_IRQ_MASK);
+
+ /* clear port IRQ */
+ tmp = readl(port_mmio + PORT_IRQ_STAT);
+ VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
+ if (tmp)
+ writel(tmp, port_mmio + PORT_IRQ_STAT);
+
+ writel(1 << port_no, mmio + HOST_IRQ_STAT);
+}
+
+static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap,
+ int port_no, void __iomem *mmio,
+ void __iomem *port_mmio)
+{
+ int rc;
+ u32 tmp;
+ const char *emsg = NULL;
+
+ /* make sure port is not active */
+ rc = ahci_deinit_port(ap, &emsg);
+ if (rc)
+ dev_printk(KERN_WARNING, &pdev->dev,
+ "%s (%d)\n", emsg, rc);
+
+ /* clear SError */
+ tmp = readl(port_mmio + PORT_SCR_ERR);
+ VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
+ writel(tmp, port_mmio + PORT_SCR_ERR);
+
+ /* select the IRQ events we're interested in */
+ writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
+
+ /* clear port IRQ */
+ tmp = readl(port_mmio + PORT_IRQ_STAT);
+ VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
+ if (tmp)
+ writel(tmp, port_mmio + PORT_IRQ_STAT);
+
+ writel(1 << port_no, mmio + HOST_IRQ_STAT);
+}
+
static void ahci_init_controller(struct ata_host *host)
{
struct pci_dev *pdev = to_pci_dev(host->dev);
void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
- int i, rc;
+ void __iomem *port_mmio;
+ int i;
u32 tmp;
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
- void __iomem *port_mmio = ahci_port_base(ap);
- const char *emsg = NULL;
+ port_mmio = ahci_port_base(ap);
if (ata_port_is_dummy(ap))
continue;
- /* make sure port is not active */
- rc = ahci_deinit_port(ap, &emsg);
- if (rc)
- dev_printk(KERN_WARNING, &pdev->dev,
- "%s (%d)\n", emsg, rc);
-
- /* clear SError */
- tmp = readl(port_mmio + PORT_SCR_ERR);
- VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
- writel(tmp, port_mmio + PORT_SCR_ERR);
-
- /* clear port IRQ */
- tmp = readl(port_mmio + PORT_IRQ_STAT);
- VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
- if (tmp)
- writel(tmp, port_mmio + PORT_IRQ_STAT);
-
- writel(1 << i, mmio + HOST_IRQ_STAT);
+ if (ap->flags & AHCI_FLAG_MV_PATA)
+ mvp_port_init(pdev, ap, i, mmio, port_mmio);
+ else
+ ahci_port_init(pdev, ap, i, mmio, port_mmio);
}
tmp = readl(mmio + HOST_CTL);
@@ -1208,7 +1594,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
ata_port_abort(ap);
}
-static void ahci_host_intr(struct ata_port *ap)
+static void ahci_port_intr(struct ata_port *ap)
{
void __iomem *port_mmio = ap->ioaddr.cmd_addr;
struct ata_eh_info *ehi = &ap->eh_info;
@@ -1334,7 +1720,10 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
ap = host->ports[i];
if (ap) {
- ahci_host_intr(ap);
+ if (ap->flags & AHCI_FLAG_MV_PATA)
+ mvp_port_intr(ap);
+ else
+ ahci_port_intr(ap);
VPRINTK("port %u\n", i);
} else {
VPRINTK("port %u (no irq)\n", i);
@@ -1442,7 +1831,7 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
ahci_power_down(ap);
else {
ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc);
- ahci_init_port(ap);
+ ahci_start_port(ap);
}
return rc;
@@ -1451,7 +1840,7 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
static int ahci_port_resume(struct ata_port *ap)
{
ahci_power_up(ap);
- ahci_init_port(ap);
+ ahci_start_port(ap);
return 0;
}
@@ -1549,13 +1938,8 @@ static int ahci_port_start(struct ata_port *ap)
ap->private_data = pp;
- /* power up port */
- ahci_power_up(ap);
-
- /* initialize port */
- ahci_init_port(ap);
-
- return 0;
+ /* engage engines, captain */
+ return ahci_port_resume(ap);
}
static void ahci_port_stop(struct ata_port *ap)
@@ -1680,7 +2064,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
struct device *dev = &pdev->dev;
struct ahci_host_priv *hpriv;
struct ata_host *host;
- int i, rc;
+ int i, rc, mvp_port;
VPRINTK("ENTER\n");
@@ -1720,15 +2104,39 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
host->iomap = pcim_iomap_table(pdev);
host->private_data = hpriv;
+ switch(ent->driver_data) {
+ case board_ahci_mv:
+ mvp_port = 4; /* count starts from zero */
+ break;
+
+ default:
+ mvp_port = -1;
+ break;
+ }
+
for (i = 0; i < host->n_ports; i++) {
- if (hpriv->port_map & (1 << i)) {
- struct ata_port *ap = host->ports[i];
- void __iomem *port_mmio = ahci_port_base(ap);
+ struct ata_port *ap = host->ports[i];
+ void __iomem *port_mmio = ahci_port_base(ap);
+ /* switch to Marvell PATA port operations, flags */
+ if ((i == mvp_port) && (hpriv->port_map & (1 << i))) {
+ ap->ioaddr.cmd_addr = port_mmio;
+
+ ap->flags &= ~(ATA_FLAG_NCQ | ATA_FLAG_SKIP_D2H_BSY |
+ ATA_FLAG_ACPI_SATA | ATA_FLAG_SATA);
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+ ap->ops = &mvp_ops;
+ }
+
+ /* standard SATA port setup */
+ else if (hpriv->port_map & (1 << i)) {
ap->ioaddr.cmd_addr = port_mmio;
ap->ioaddr.scr_addr = port_mmio + PORT_SCR;
- } else
- host->ports[i]->ops = &ata_dummy_port_ops;
+ }
+
+ /* disabled/not-implemented port */
+ else
+ ap->ops = &ata_dummy_port_ops;
}
/* initialize adapter */
diff --git a/include/linux/ata.h b/include/linux/ata.h
index edb31bf..2e3542a 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -126,6 +126,7 @@ enum {
ATA_REG_IRQ = ATA_REG_NSECT,
/* ATA device commands */
+ ATA_CMD_DEV_RESET = 0x08, /* ATAPI device reset */
ATA_CMD_CHK_POWER = 0xE5, /* check power mode */
ATA_CMD_STANDBY = 0xE2, /* place in standby power mode */
ATA_CMD_IDLE = 0xE3, /* place in idle power mode */
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] Add Marvell 6141 PATA support to AHCI driver
2007-05-27 3:09 [PATCH] Add Marvell 6141 PATA support to AHCI driver Jeff Garzik
@ 2007-05-27 12:18 ` Alan Cox
2007-05-28 19:24 ` George T. Joseph (development)
2007-06-03 22:58 ` [PATCH] libata: CONFIG_PM=n compile fix Olof Johansson
2 siblings, 0 replies; 7+ messages in thread
From: Alan Cox @ 2007-05-27 12:18 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linux-ide, LKML
> Engineering questions I need to bounce back to Marvell:
> * do I need to worry about PIO/UDMA timing? I don't see any registers
> or other knobs dealing with timing. Maybe the controller snoops?
In non-AHCI mode it certainly seems to do the snooping for you
Acked-by: Alan Cox <alan@redhat.com>
Alan
^ permalink raw reply [flat|nested] 7+ messages in thread
* RE: [PATCH] Add Marvell 6141 PATA support to AHCI driver
2007-05-27 3:09 [PATCH] Add Marvell 6141 PATA support to AHCI driver Jeff Garzik
2007-05-27 12:18 ` Alan Cox
@ 2007-05-28 19:24 ` George T. Joseph (development)
2007-07-07 15:29 ` Jeff Garzik
2007-06-03 22:58 ` [PATCH] libata: CONFIG_PM=n compile fix Olof Johansson
2 siblings, 1 reply; 7+ messages in thread
From: George T. Joseph (development) @ 2007-05-28 19:24 UTC (permalink / raw)
To: Jeff Garzik, linux-ide
Jeff,
Any news from Marvell on the msi and ncq issues?
Environment:
Intel 975XBX2 w/ Marvell 6145 with BIOS at 1.1.0.34
(the pata port is n/c on this board)
2x Seagate 7200.10 320g
2x WD WD2500KS 250g
Drives and cables work fine on the ICH7.
With MSI enabled...
ACPI: PCI Interrupt 0000:03:00.0[A] -> GSI 16 (level, low) -> IRQ 16
ahci 0000:03:00.0: AHCI 0001.0000 32 slots 5 ports 3 Gbps 0x1f impl SATA
mode
ahci 0000:03:00.0: flags: 64bit ncq stag pmp slum part
PCI: Setting latency timer of device 0000:03:00.0 to 64
scsi4 : ahci
scsi5 : ahci
scsi6 : ahci
scsi7 : ahci
scsi8 : ahci
ata5: SATA max UDMA/133 cmd 0xf8822100 ctl 0x00000000 bmdma 0x00000000
irq 0
ata6: SATA max UDMA/133 cmd 0xf8822180 ctl 0x00000000 bmdma 0x00000000
irq 0
ata7: SATA max UDMA/133 cmd 0xf8822200 ctl 0x00000000 bmdma 0x00000000
irq 0
ata8: SATA max UDMA/133 cmd 0xf8822280 ctl 0x00000000 bmdma 0x00000000
irq 0
ata9: PATA max UDMA/133 cmd 0xf8822300 ctl 0x00000000 bmdma 0x00000000
irq 0
ata5: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
irq 16: nobody cared (try booting with the "irqpoll" option)
[<c01036df>] show_trace_log_lvl+0x1a/0x2f
[<c01041c9>] show_trace+0x12/0x14
[<c0104250>] dump_stack+0x16/0x18
[<c0141bc0>] __report_bad_irq+0x39/0x79
[<c0141da0>] note_interrupt+0x1a0/0x1dc
[<c0142348>] handle_fasteoi_irq+0x91/0xb6
[<c0104928>] do_IRQ+0xb7/0xe3
=======================
handlers:
[<c02713e6>] (usb_hcd_irq+0x0/0x52)
Disabling IRQ #16
ata5.00: qc timeout (cmd 0xec)
ata5.00: failed to IDENTIFY (I/O error, err_mask=0x4)
ata5: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
ATA: abnormal status 0x58 on port 0x00000000
ATA: abnormal status 0x58 on port 0x00000000
ata5.00: qc timeout (cmd 0xec)
ata5.00: failed to IDENTIFY (I/O error, err_mask=0x4)
ata5.00: limiting speed to UDMA7:PIO5
ata5: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
ATA: abnormal status 0x58 on port 0x00000000
ATA: abnormal status 0x58 on port 0x00000000
ata5.00: qc timeout (cmd 0xec)
ata5.00: failed to IDENTIFY (I/O error, err_mask=0x4)
ata5: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
<repeated for each drive connected>
With MSI disabled and NCQ enabled (a few minutes into a load test)...
ata6.00: exception Emask 0x0 SAct 0x7 SErr 0x0 action 0x2 frozen
ata6.00: cmd 60/40:00:80:94:69/00:00:00:00:00/40 tag 0 cdb 0x0 data
32768 in
res 40/00:40:f8:fd:88/00:00:00:00:00/40 Emask 0x4 (timeout)
ata6.00: cmd 60/40:08:c0:94:69/00:00:00:00:00/40 tag 1 cdb 0x0 data
32768 in
res 40/00:00:00:00:00/00:00:00:00:00/00 Emask 0x4 (timeout)
ata6.00: cmd 60/80:10:00:95:69/00:00:00:00:00/40 tag 2 cdb 0x0 data
65536 in
res 40/00:00:00:00:00/00:00:00:00:00/00 Emask 0x4 (timeout)
ata6: soft resetting port
ata6: softreset failed (1st FIS failed)
ata6: reset failed (errno=-5), retrying in 10 secs
ata6: hard resetting port
ata6: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
ata6.00: qc timeout (cmd 0xec)
ata6.00: failed to IDENTIFY (I/O error, err_mask=0x4)
ata6.00: revalidation failed (errno=-5)
ata6: failed to recover some devices, retrying in 5 secs
ata6: hard resetting port
ata6: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
ata6.00: qc timeout (cmd 0xec)
ata6.00: failed to IDENTIFY (I/O error, err_mask=0x4)
ata6.00: revalidation failed (errno=-5)
ata6.00: limiting speed to UDMA/133:PIO3
ata6: failed to recover some devices, retrying in 5 secs
ata6: hard resetting port
ata6: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
ata6.00: qc timeout (cmd 0xec)
ata6.00: failed to IDENTIFY (I/O error, err_mask=0x4)
ata6.00: revalidation failed (errno=-5)
ata6.00: disabled
ata6: EH complete
> -----Original Message-----
> From: linux-ide-owner@vger.kernel.org
> [mailto:linux-ide-owner@vger.kernel.org] On Behalf Of Jeff Garzik
> Sent: Saturday, May 26, 2007 9:10 PM
> To: linux-ide@vger.kernel.org
> Cc: LKML; Alan Cox
> Subject: [PATCH] Add Marvell 6141 PATA support to AHCI driver
>
>
> Here is a patch against 2.6.22-rc3 that adds support for both the PATA
> and SATA portions of the Marvell AHCI-like chip.
>
> The architecture for PATA is quite nice, mimicing AHCI very closely.
> Basic port scanning, interrupt handling, freezing and thawing is the
> same, and uses the same register offets.
>
> This is completely untested code. But it looks like it should work :)
>
> Engineering questions I need to bounce back to Marvell:
> * do I need to worry about PIO/UDMA timing? I don't see any registers
> or other knobs dealing with timing. Maybe the controller snoops?
> * it appears that I don't have to worry about device selection, that
> the controller will handle this for me. But I want to make sure.
> * figure out controller commands. these are poorly documented.
> controller commands are how one directly accesses the PATA device's
> command and control registers, and are necessary to do things like
> SRST. Currently the driver only does hard reset.
>
> This is checked into the 'mv-ahci-pata' branch of
> git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH] libata: CONFIG_PM=n compile fix
2007-05-27 3:09 [PATCH] Add Marvell 6141 PATA support to AHCI driver Jeff Garzik
2007-05-27 12:18 ` Alan Cox
2007-05-28 19:24 ` George T. Joseph (development)
@ 2007-06-03 22:58 ` Olof Johansson
2007-06-04 1:38 ` Nigel Cunningham
2007-07-03 14:16 ` Jeff Garzik
2 siblings, 2 replies; 7+ messages in thread
From: Olof Johansson @ 2007-06-03 22:58 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linux-ide, LKML, Alan Cox
CONFIG_PM=n compile fix.
Signed-off-by: Olof Johansson <olof@lixom.net>
---
Hi,
On Sat, May 26, 2007 at 11:09:54PM -0400, Jeff Garzik wrote:
> This is checked into the 'mv-ahci-pata' branch of
> git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
Didn't build for me without this change, since I had CONFIG_PM=n.
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 21df81e..07d5a95 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1818,6 +1836,14 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
}
}
+static int ahci_port_resume(struct ata_port *ap)
+{
+ ahci_power_up(ap);
+ ahci_start_port(ap);
+
+ return 0;
+}
+
#ifdef CONFIG_PM
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
{
@@ -1835,14 +1861,6 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
return rc;
}
-static int ahci_port_resume(struct ata_port *ap)
-{
- ahci_power_up(ap);
- ahci_start_port(ap);
-
- return 0;
-}
-
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
struct ata_host *host = dev_get_drvdata(&pdev->dev);
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] libata: CONFIG_PM=n compile fix
2007-06-03 22:58 ` [PATCH] libata: CONFIG_PM=n compile fix Olof Johansson
@ 2007-06-04 1:38 ` Nigel Cunningham
2007-07-03 14:16 ` Jeff Garzik
1 sibling, 0 replies; 7+ messages in thread
From: Nigel Cunningham @ 2007-06-04 1:38 UTC (permalink / raw)
To: Olof Johansson; +Cc: Jeff Garzik, linux-ide, LKML, Alan Cox
[-- Attachment #1: Type: text/plain, Size: 1494 bytes --]
Hi.
On Sun, 2007-06-03 at 17:58 -0500, Olof Johansson wrote:
> CONFIG_PM=n compile fix.
>
> Signed-off-by: Olof Johansson <olof@lixom.net>
>
> ---
>
> Hi,
>
> On Sat, May 26, 2007 at 11:09:54PM -0400, Jeff Garzik wrote:
>
> > This is checked into the 'mv-ahci-pata' branch of
> > git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
>
> Didn't build for me without this change, since I had CONFIG_PM=n.
>
>
> diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
> index 21df81e..07d5a95 100644
> --- a/drivers/ata/ahci.c
> +++ b/drivers/ata/ahci.c
> @@ -1818,6 +1836,14 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
> }
> }
>
> +static int ahci_port_resume(struct ata_port *ap)
> +{
> + ahci_power_up(ap);
> + ahci_start_port(ap);
> +
> + return 0;
> +}
> +
> #ifdef CONFIG_PM
> static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
> {
> @@ -1835,14 +1861,6 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
> return rc;
> }
>
> -static int ahci_port_resume(struct ata_port *ap)
> -{
> - ahci_power_up(ap);
> - ahci_start_port(ap);
> -
> - return 0;
> -}
> -
> static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
> {
> struct ata_host *host = dev_get_drvdata(&pdev->dev);
At first glance, this looked wrong to me, but I see Jeff is using
ahci_port_resume from ahci_port_start, so it is right.
Regards,
Nigel
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] libata: CONFIG_PM=n compile fix
2007-06-03 22:58 ` [PATCH] libata: CONFIG_PM=n compile fix Olof Johansson
2007-06-04 1:38 ` Nigel Cunningham
@ 2007-07-03 14:16 ` Jeff Garzik
1 sibling, 0 replies; 7+ messages in thread
From: Jeff Garzik @ 2007-07-03 14:16 UTC (permalink / raw)
To: Olof Johansson; +Cc: linux-ide, LKML, Alan Cox
Olof Johansson wrote:
> CONFIG_PM=n compile fix.
>
> Signed-off-by: Olof Johansson <olof@lixom.net>
>
> ---
>
> Hi,
>
> On Sat, May 26, 2007 at 11:09:54PM -0400, Jeff Garzik wrote:
>
>> This is checked into the 'mv-ahci-pata' branch of
>> git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
>
> Didn't build for me without this change, since I had CONFIG_PM=n.
applied
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] Add Marvell 6141 PATA support to AHCI driver
2007-05-28 19:24 ` George T. Joseph (development)
@ 2007-07-07 15:29 ` Jeff Garzik
0 siblings, 0 replies; 7+ messages in thread
From: Jeff Garzik @ 2007-07-07 15:29 UTC (permalink / raw)
To: George T. Joseph (development); +Cc: linux-ide
George T. Joseph (development) wrote:
> Jeff,
>
> Any news from Marvell on the msi and ncq issues?
Only terse "NCQ works for us" and "we don't turn on PCI MSI ourselves"
answers... :)
So my current plan is to disable NCQ and guarantee PCI MSI is not
enabled for Marvell AHCI-like devices, and push SATA support upstream.
Marvell AHCI-like PATA support (branch mv-ahci-pata) will take a little
bit longer, since it definitely needs a fix to its "PATA controller
command" code.
Jeff
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2007-07-07 15:29 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-05-27 3:09 [PATCH] Add Marvell 6141 PATA support to AHCI driver Jeff Garzik
2007-05-27 12:18 ` Alan Cox
2007-05-28 19:24 ` George T. Joseph (development)
2007-07-07 15:29 ` Jeff Garzik
2007-06-03 22:58 ` [PATCH] libata: CONFIG_PM=n compile fix Olof Johansson
2007-06-04 1:38 ` Nigel Cunningham
2007-07-03 14:16 ` Jeff Garzik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).