Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[RFC][PATCH 2.6.13] Marvell SATA support (PIO mode)

11 views
Skip to first unread message

Brett Russ

unread,
Aug 30, 2005, 2:37:12 PM8/30/05
to Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org
This is the first public release of my libata compatible low level driver for
the Marvell SATA family. Currently it successfully runs in PIO mode on a 6081
chip. EDMA support is in the works and should be done shortly. Review,
testing (especially on other flavors of Marvell), comments welcome.

Thank you,
BR


Index: linux-2.6.13/drivers/scsi/sata_mv.c
===================================================================
--- /dev/null
+++ linux-2.6.13/drivers/scsi/sata_mv.c
@@ -0,0 +1,825 @@
+/*
+ * sata_mv.c - Marvell SATA support
+ *
+ * Copyright 2005: EMC Corporation, all rights reserved.
+ *
+ * Please ALWAYS copy linu...@vger.kernel.org on emails.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#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/sched.h>
+#include <linux/dma-mapping.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+
+#define DRV_NAME "sata_mv"
+#define DRV_VERSION "0.10"
+
+enum {
+ /* BAR's are enumerated in terms of pci_resource_start() terms */
+ MV_PRIMARY_BAR = 0, /* offset 0x10: memory space */
+ MV_IO_BAR = 2, /* offset 0x18: IO space */
+ MV_MISC_BAR = 3, /* offset 0x1c: FLASH, NVRAM, SRAM */
+
+ MV_MAJOR_REG_AREA_SZ = 0x10000, /* 64KB */
+ MV_MINOR_REG_AREA_SZ = 0x2000, /* 8KB */
+
+ MV_PCI_REG_BASE = 0,
+ MV_IRQ_COAL_REG_BASE = 0x18000, /* 6xxx part only */
+ MV_SATAHC0_REG_BASE = 0x20000,
+
+ MV_PCI_REG_SZ = MV_MAJOR_REG_AREA_SZ,
+ MV_SATAHC_REG_SZ = MV_MAJOR_REG_AREA_SZ,
+ MV_SATAHC_ARBTR_REG_SZ = MV_MINOR_REG_AREA_SZ, /* arbiter */
+ MV_PORT_REG_SZ = MV_MINOR_REG_AREA_SZ,
+
+ MV_Q_CT = 32,
+ MV_CRQB_SZ = 32,
+ MV_CRPB_SZ = 8,
+
+ MV_DMA_BOUNDARY = 0xffffffffU,
+ SATAHC_MASK = (~(MV_SATAHC_REG_SZ - 1)),
+
+ MV_PORTS_PER_HC = 4,
+ /* == (port / MV_PORTS_PER_HC) to determine HC from 0-7 port */
+ MV_PORT_HC_SHIFT = 2,
+ /* == (port % MV_PORTS_PER_HC) to determine port from 0-7 port */
+ MV_PORT_MASK = 3,
+
+ MV_FLAG_DUAL_HC = (1 << 30), /* two SATA Host Controllers */
+ MV_FLAG_IRQ_COALESCE = (1 << 29), /* IRQ coalescing capability */
+ MV_FLAG_BDMA = (1 << 28), /* Basic DMA */
+
+ chip_504x = 0,
+ chip_508x = 1,
+ chip_604x = 2,
+ chip_608x = 3,
+
+ /* PCI interface registers */
+
+ PCI_MAIN_CMD_STS_OFS = 0xd30,
+ STOP_PCI_MASTER = (1 << 2),
+ PCI_MASTER_EMPTY = (1 << 3),
+ GLOB_SFT_RST = (1 << 4),
+
+ PCI_IRQ_CAUSE = 0x1d58,
+ PCI_IRQ_MASK = 0x1d5c,
+ PCI_UNMASK_ALL_IRQS = 0x7fffff, /* bits 22-0 */
+
+ HC_MAIN_IRQ_CAUSE = 0x1d60,
+ PORT0_ERR = (1 << 0), /* shift by port # */
+ PORT0_DONE = (1 << 1), /* shift by port # */
+ HC0_IRQ_PEND = 0x1ff, /* bits 0-8 = HC0's ports */
+ HC_SHIFT = 9, /* bits 9-17 = HC1's ports */
+ PCI_ERR = (1 << 18),
+ TRAN_LO_DONE = (1 << 19), /* 6xxx: IRQ coalescing */
+ TRAN_HI_DONE = (1 << 20), /* 6xxx: IRQ coalescing */
+ PORTS_0_7_COAL_DONE = (1 << 21), /* 6xxx: IRQ coalescing */
+ GPIO_INT = (1 << 22),
+ SELF_INT = (1 << 23),
+ TWSI_INT = (1 << 24),
+ HC_MAIN_RSVD = (0x7f << 25), /* bits 31-25 */
+ HC_MAIN_IRQ_MASK = 0x1d64,
+ HC_MAIN_MASKED_IRQS = (TRAN_LO_DONE | TRAN_HI_DONE |
+ PORTS_0_7_COAL_DONE | GPIO_INT | TWSI_INT |
+ HC_MAIN_RSVD),
+
+ /* SATAHC registers */
+ HC_CFG_OFS = 0,
+
+ HC_IRQ_CAUSE_OFS = 0x14,
+ CRBP_DMA_DONE = (1 << 0), /* shift by port # */
+ HC_IRQ_COAL = (1 << 4), /* IRQ coalescing */
+ DEV_IRQ = (1 << 8), /* shift by port # */
+
+ /* Shadow block registers */
+ SHD_PIO_DATA_OFS = 0x100,
+ SHD_FEA_ERR_OFS = 0x104,
+ SHD_SECT_CNT_OFS = 0x108,
+ SHD_LBA_L_OFS = 0x10C,
+ SHD_LBA_M_OFS = 0x110,
+ SHD_LBA_H_OFS = 0x114,
+ SHD_DEV_HD_OFS = 0x118,
+ SHD_CMD_STA_OFS = 0x11C,
+ SHD_CTL_AST_OFS = 0x120,
+
+ /* SATA registers */
+ SATA_STATUS_OFS = 0x300, /* ctrl, err regs follow status */
+ SATA_ACTIVE_OFS = 0x350,
+
+ /* Port registers */
+ EDMA_CFG_OFS = 0,
+
+ EDMA_ERR_IRQ_CAUSE_OFS = 0x8,
+ EDMA_ERR_IRQ_MASK_OFS = 0xc,
+ EDMA_ERR_D_PAR = (1 << 0),
+ EDMA_ERR_PRD_PAR = (1 << 1),
+ EDMA_ERR_DEV = (1 << 2),
+ EDMA_ERR_DEV_DCON = (1 << 3),
+ EDMA_ERR_DEV_CON = (1 << 4),
+ EDMA_ERR_SERR = (1 << 5),
+ EDMA_ERR_SELF_DIS = (1 << 7),
+ EDMA_ERR_BIST_ASYNC = (1 << 8),
+ EDMA_ERR_CRBQ_PAR = (1 << 9),
+ EDMA_ERR_CRPB_PAR = (1 << 10),
+ EDMA_ERR_INTRL_PAR = (1 << 11),
+ EDMA_ERR_IORDY = (1 << 12),
+ EDMA_ERR_LNK_CTRL_RX = (0xf << 13),
+ EDMA_ERR_LNK_CTRL_RX_2 = (1 << 15),
+ EDMA_ERR_LNK_DATA_RX = (0xf << 17),
+ EDMA_ERR_LNK_CTRL_TX = (0x1f << 21),
+ EDMA_ERR_LNK_DATA_TX = (0x1f << 26),
+ EDMA_ERR_TRANS_PROTO = (1 << 31),
+ EDMA_ERR_FATAL = (EDMA_ERR_D_PAR | EDMA_ERR_PRD_PAR |
+ EDMA_ERR_DEV_DCON | EDMA_ERR_CRBQ_PAR |
+ EDMA_ERR_CRPB_PAR | EDMA_ERR_INTRL_PAR |
+ EDMA_ERR_IORDY | EDMA_ERR_LNK_CTRL_RX_2 |
+ EDMA_ERR_LNK_DATA_RX |
+ EDMA_ERR_LNK_DATA_TX |
+ EDMA_ERR_TRANS_PROTO),
+
+ EDMA_CMD_OFS = 0x28,
+ EDMA_EN = (1 << 0),
+ EDMA_DS = (1 << 1),
+ ATA_RST = (1 << 2),
+
+ /* BDMA is 6xxx part only */
+ BDMA_CMD_OFS = 0x224,
+ BDMA_START = (1 << 0),
+
+ MV_UNDEF = 0,
+};
+
+struct mv_port_priv {
+
+};
+
+struct mv_host_priv {
+
+};
+
+static void mv_irq_clear(struct ata_port *ap);
+static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in);
+static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val);
+static void mv_phy_reset(struct ata_port *ap);
+static int mv_master_reset(void __iomem *mmio_base);
+static irqreturn_t mv_interrupt(int irq, void *dev_instance,
+ struct pt_regs *regs);
+static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
+
+static Scsi_Host_Template mv_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 = MV_UNDEF,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = MV_UNDEF,
+ .proc_name = DRV_NAME,
+ .dma_boundary = MV_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations mv_ops = {
+ .port_disable = ata_port_disable,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .phy_reset = mv_phy_reset,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .eng_timeout = ata_eng_timeout,
+
+ .irq_handler = mv_interrupt,
+ .irq_clear = mv_irq_clear,
+
+ .scr_read = mv_scr_read,
+ .scr_write = mv_scr_write,
+
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+ .host_stop = ata_host_stop,
+};
+
+static struct ata_port_info mv_port_info[] = {
+ { /* chip_504x */
+ .sht = &mv_sht,
+ .host_flags = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO),
+ .pio_mask = 0x1f, /* pio4-0 */
+ .udma_mask = 0, /* 0x7f (udma6-0 disabled for now) */
+ .port_ops = &mv_ops,
+ },
+ { /* chip_508x */
+ .sht = &mv_sht,
+ .host_flags = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+ MV_FLAG_DUAL_HC),
+ .pio_mask = 0x1f, /* pio4-0 */
+ .udma_mask = 0, /* 0x7f (udma6-0 disabled for now) */
+ .port_ops = &mv_ops,
+ },
+ { /* chip_604x */
+ .sht = &mv_sht,
+ .host_flags = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+ MV_FLAG_IRQ_COALESCE | MV_FLAG_BDMA),
+ .pio_mask = 0x1f, /* pio4-0 */
+ .udma_mask = 0, /* 0x7f (udma6-0 disabled for now) */
+ .port_ops = &mv_ops,
+ },
+ { /* chip_608x */
+ .sht = &mv_sht,
+ .host_flags = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+ MV_FLAG_IRQ_COALESCE | MV_FLAG_DUAL_HC |
+ MV_FLAG_BDMA),
+ .pio_mask = 0x1f, /* pio4-0 */
+ .udma_mask = 0, /* 0x7f (udma6-0 disabled for now) */
+ .port_ops = &mv_ops,
+ },
+};
+
+static struct pci_device_id mv_pci_tbl[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_508x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x},
+
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x},
+ {} /* terminate list */
+};
+
+static struct pci_driver mv_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = mv_pci_tbl,
+ .probe = mv_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+/*
+ * Functions
+ */
+
+static inline void writelfl(unsigned long data, void __iomem *addr)
+{
+ writel(data, addr);
+ (void) readl(addr); /* flush */
+}
+
+static inline void __iomem *mv_port_addr_to_hc_base(void __iomem *port_mmio)
+{
+ return ((void __iomem *)((unsigned long)port_mmio &
+ (unsigned long)SATAHC_MASK));
+}
+
+static inline void __iomem *mv_hc_base(void __iomem *base, unsigned int hc)
+{
+ return (base + MV_SATAHC0_REG_BASE + (hc * MV_SATAHC_REG_SZ));
+}
+
+static inline void __iomem *mv_port_base(void __iomem *base, unsigned int port)
+{
+ return (mv_hc_base(base, port >> MV_PORT_HC_SHIFT) +
+ MV_SATAHC_ARBTR_REG_SZ +
+ ((port & MV_PORT_MASK) * MV_PORT_REG_SZ));
+}
+
+static inline void __iomem *mv_ap_base(struct ata_port *ap)
+{
+ return (mv_port_base(ap->host_set->mmio_base, ap->port_no));
+}
+
+static inline int mv_get_hc_count(unsigned long flags)
+{
+ return ((flags & MV_FLAG_DUAL_HC) ? 2 : 1);
+}
+
+static inline int mv_is_edma_active(struct ata_port *ap)
+{
+ void __iomem *port_mmio = mv_ap_base(ap);
+ return (EDMA_EN & readl(port_mmio + EDMA_CMD_OFS));
+}
+
+static inline int mv_port_bdma_capable(struct ata_port *ap)
+{
+ return (ap->flags & MV_FLAG_BDMA);
+}
+
+static void mv_irq_clear(struct ata_port *ap)
+{
+ return;
+}
+
+static unsigned int mv_scr_offset(unsigned int sc_reg_in)
+{
+ unsigned int ofs;
+
+ switch (sc_reg_in) {
+ case SCR_STATUS:
+ case SCR_CONTROL:
+ case SCR_ERROR:
+ ofs = SATA_STATUS_OFS + (sc_reg_in * sizeof(u32));
+ break;
+ case SCR_ACTIVE:
+ ofs = SATA_ACTIVE_OFS; /* active is not with the others */
+ break;
+ default:
+ ofs = 0xffffffffU;
+ break;
+ }
+ return (ofs);
+}
+
+static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in)
+{
+ unsigned int ofs = mv_scr_offset(sc_reg_in);
+
+ if (0xffffffffU != ofs) {
+ return (readl(mv_ap_base(ap) + ofs));
+ } else {
+ return ((u32) ofs);
+ }
+}
+
+static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val)
+{
+ unsigned int ofs = mv_scr_offset(sc_reg_in);
+
+ if (0xffffffffU != ofs) {
+ writelfl(val, mv_ap_base(ap) + ofs);
+ }
+}
+
+static int mv_master_reset(void __iomem *mmio_base)
+{
+ void __iomem *reg = mmio_base + PCI_MAIN_CMD_STS_OFS;
+ int i, rc = 0;
+ u32 t;
+
+ VPRINTK("ENTER\n");
+
+ /* Following procedure defined in PCI "main command and status
+ * register" table.
+ */
+ t = readl(reg);
+ writel(t | STOP_PCI_MASTER, reg);
+
+ for (i = 0; i < 100; i++) {
+ msleep(10);
+ t = readl(reg);
+ if (PCI_MASTER_EMPTY & t) {
+ break;
+ }
+ }
+ if (!(PCI_MASTER_EMPTY & t)) {
+ printk(KERN_ERR DRV_NAME "PCI master won't flush\n");
+ rc = 1; /* broken HW? */
+ goto done;
+ }
+
+ /* set reset */
+ i = 5;
+ do {
+ writel(t | GLOB_SFT_RST, reg);
+ t = readl(reg);
+ udelay(1);
+ } while (!(GLOB_SFT_RST & t) && (i-- > 0));
+
+ if (!(GLOB_SFT_RST & t)) {
+ printk(KERN_ERR DRV_NAME "can't set global reset\n");
+ rc = 1; /* broken HW? */
+ goto done;
+ }
+
+ /* clear reset */
+ i = 5;
+ do {
+ writel(t & ~GLOB_SFT_RST, reg);
+ t = readl(reg);
+ udelay(1);
+ } while ((GLOB_SFT_RST & t) && (i-- > 0));
+
+ if (GLOB_SFT_RST & t) {
+ printk(KERN_ERR DRV_NAME "can't clear global reset\n");
+ rc = 1; /* broken HW? */
+ }
+
+ done:
+ VPRINTK("EXIT, rc = %i\n", rc);
+ return (rc);
+}
+
+static void mv_err_intr(struct ata_port *ap)
+{
+ void __iomem *port_mmio;
+ u32 edma_err_cause, serr = 0;
+
+ /* bug here b/c we got an err int on a port we don't know about,
+ * so there's no way to clear it
+ */
+ BUG_ON(NULL == ap);
+ port_mmio = mv_ap_base(ap);
+
+ edma_err_cause = readl(port_mmio + EDMA_ERR_IRQ_CAUSE_OFS);
+
+ if (EDMA_ERR_SERR & edma_err_cause) {
+ serr = scr_read(ap, SCR_ERROR);
+ scr_write_flush(ap, SCR_ERROR, serr);
+ }
+ DPRINTK("port %u error; EDMA err cause: 0x%08x SERR: 0x%08x\n",
+ ap->port_no, edma_err_cause, serr);
+
+ /* Clear EDMA now that SERR cleanup done */
+ writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS);
+
+ /* check for fatal here and recover if needed */
+ if (EDMA_ERR_FATAL & edma_err_cause) {
+ mv_phy_reset(ap);
+ }
+}
+
+/* Handle any outstanding interrupts in a single SATAHC
+ */
+static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
+ unsigned int hc)
+{
+ void __iomem *mmio = host_set->mmio_base;
+ void __iomem *hc_mmio = mv_hc_base(mmio, hc);
+ struct ata_port *ap;
+ struct ata_queued_cmd *qc;
+ u32 hc_irq_cause;
+ int shift, port, port0, hard_port;
+ u8 ata_status;
+
+ if (hc == 0) {
+ port0 = 0;
+ } else {
+ port0 = MV_PORTS_PER_HC;
+ }
+
+ /* we'll need the HC success int register in most cases */
+ hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS);
+ if (hc_irq_cause) {
+ writelfl(0, hc_mmio + HC_IRQ_CAUSE_OFS);
+ }
+
+ VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n",
+ hc,relevant,hc_irq_cause);
+
+ for (port = port0; port < port0 + MV_PORTS_PER_HC; port++) {
+ ap = host_set->ports[port];
+ hard_port = port & MV_PORT_MASK; /* range 0-3 */
+ ata_status = 0xffU;
+
+ if (((CRBP_DMA_DONE | DEV_IRQ) << hard_port) & hc_irq_cause) {
+ BUG_ON(NULL == ap);
+ /* rcv'd new resp, basic DMA complete, or ATA IRQ */
+ /* This is needed to clear the ATA INTRQ.
+ * FIXME: don't read the status reg in EDMA mode!
+ */
+ ata_status = readb((void __iomem *)
+ ap->ioaddr.status_addr);
+ }
+
+ shift = port * 2;
+ if (port >= MV_PORTS_PER_HC) {
+ shift++; /* skip bit 8 in the HC Main IRQ reg */
+ }
+ if ((PORT0_ERR << shift) & relevant) {
+ mv_err_intr(ap);
+ /* FIXME: smart to OR in ATA_ERR? */
+ ata_status = readb((void __iomem *)
+ ap->ioaddr.status_addr) | ATA_ERR;
+ }
+
+ if (ap &&
+ (NULL != (qc = ata_qc_from_tag(ap, ap->active_tag)))) {
+ VPRINTK("port %u IRQ found for qc, ata_status 0x%x\n",
+ port,ata_status);
+ BUG_ON(0xffU == ata_status);
+ /* mark qc status appropriately */
+ ata_qc_complete(qc, ata_status);
+ }
+ }
+ VPRINTK("EXIT\n");
+}
+
+static irqreturn_t mv_interrupt(int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct ata_host_set *host_set = dev_instance;
+ unsigned int hc, handled = 0, n_hcs;
+ void __iomem *mmio;
+ u32 irq_stat;
+
+ mmio = host_set->mmio_base;
+ irq_stat = readl(mmio + HC_MAIN_IRQ_CAUSE);
+ n_hcs = mv_get_hc_count(host_set->ports[0]->flags);
+
+ /* check the cases where we either have nothing pending or have read
+ * a bogus register value which can indicate HW removal or PCI fault
+ */
+ if (!irq_stat || (0xffffffffU == irq_stat)) {
+ return IRQ_NONE;
+ }
+
+ spin_lock(&host_set->lock);
+
+ for (hc = 0; hc < n_hcs; hc++) {
+ u32 relevant = irq_stat & (HC0_IRQ_PEND << (hc * HC_SHIFT));
+ if (relevant) {
+ mv_host_intr(host_set, relevant, hc);
+ handled = 1;
+ }
+ }
+ if (PCI_ERR & irq_stat) {
+ /* FIXME: these are all masked by default, but still need
+ * to recover from them properly.
+ */
+ }
+
+ spin_unlock(&host_set->lock);
+
+ return (IRQ_RETVAL(handled));
+}
+
+static void mv_phy_reset(struct ata_port *ap)
+{
+ void __iomem *port_mmio = mv_ap_base(ap);
+ struct ata_taskfile tf;
+ struct ata_device *dev = &ap->device[0];
+ u32 edma = 0, bdma;
+
+ VPRINTK("ENTER, port %u, mmio 0x%p\n", ap->port_no, port_mmio);
+
+ edma = readl(port_mmio + EDMA_CMD_OFS);
+ if (EDMA_EN & edma) {
+ /* disable EDMA if active */
+ edma &= ~EDMA_EN;
+ writelfl(edma | EDMA_DS, port_mmio + EDMA_CMD_OFS);
+ udelay(1);
+ } else if (mv_port_bdma_capable(ap) &&
+ (bdma = readl(port_mmio + BDMA_CMD_OFS)) & BDMA_START) {
+ /* disable BDMA if active */
+ writelfl(bdma & ~BDMA_START, port_mmio + BDMA_CMD_OFS);
+ }
+
+ writelfl(edma | ATA_RST, port_mmio + EDMA_CMD_OFS);
+ udelay(25); /* allow reset propagation */
+
+ /* Spec never mentions clearing the bit. Marvell's driver does
+ * clear the bit, however.
+ */
+ writelfl(edma & ~ATA_RST, port_mmio + EDMA_CMD_OFS);
+
+ VPRINTK("Done. Now calling __sata_phy_reset()\n");
+
+ /* proceed to init communications via the scr_control reg */
+ __sata_phy_reset(ap);
+
+ if (ap->flags & ATA_FLAG_PORT_DISABLED) {
+ VPRINTK("Port disabled pre-sig. Exiting.\n");
+ return;
+ }
+
+ tf.lbah = readb((void __iomem *) ap->ioaddr.lbah_addr);
+ tf.lbam = readb((void __iomem *) ap->ioaddr.lbam_addr);
+ tf.lbal = readb((void __iomem *) ap->ioaddr.lbal_addr);
+ tf.nsect = readb((void __iomem *) ap->ioaddr.nsect_addr);
+
+ dev->class = ata_dev_classify(&tf);
+ if (!ata_dev_present(dev)) {
+ VPRINTK("Port disabled post-sig: No device present.\n");
+ ata_port_disable(ap);
+ }
+ VPRINTK("EXIT\n");
+}
+
+static void mv_port_init(struct ata_ioports *port, unsigned long base)
+{
+ /* PIO related setup */
+ port->data_addr = base + SHD_PIO_DATA_OFS;
+ port->error_addr = port->feature_addr = base + SHD_FEA_ERR_OFS;
+ port->nsect_addr = base + SHD_SECT_CNT_OFS;
+ port->lbal_addr = base + SHD_LBA_L_OFS;
+ port->lbam_addr = base + SHD_LBA_M_OFS;
+ port->lbah_addr = base + SHD_LBA_H_OFS;
+ port->device_addr = base + SHD_DEV_HD_OFS;
+ port->status_addr = port->command_addr = base + SHD_CMD_STA_OFS;
+ port->altstatus_addr = port->ctl_addr = base + SHD_CTL_AST_OFS;
+ /* unused */
+ port->cmd_addr = port->bmdma_addr = port->scr_addr = 0;
+
+ /* unmask all EDMA error interrupts */
+ writel(~0, (void __iomem *)base + EDMA_ERR_IRQ_MASK_OFS);
+
+ VPRINTK("EDMA cfg=0x%08x EDMA IRQ err cause/mask=0x%08x/0x%08x\n",
+ readl((void __iomem *)base+EDMA_CFG_OFS),
+ readl((void __iomem *)base+EDMA_ERR_IRQ_CAUSE_OFS),
+ readl((void __iomem *)base+EDMA_ERR_IRQ_MASK_OFS));
+}
+
+static int mv_host_init(struct ata_probe_ent *probe_ent)
+{
+ int rc = 0, n_hc, port, hc;
+ void __iomem *mmio = probe_ent->mmio_base;
+ void __iomem *port_mmio;
+
+ if (mv_master_reset(probe_ent->mmio_base)) {
+ rc = 1;
+ goto done;
+ }
+
+ n_hc = mv_get_hc_count(probe_ent->host_flags);
+ probe_ent->n_ports = MV_PORTS_PER_HC * n_hc;
+
+ for (port = 0; port < probe_ent->n_ports; port++) {
+ port_mmio = mv_port_base(mmio, port);
+ mv_port_init(&probe_ent->port[port], (unsigned long)port_mmio);
+ }
+
+ for (hc = 0; hc < n_hc; hc++) {
+ VPRINTK("HC%i: HC config=0x%08x HC IRQ cause=0x%08x\n", hc,
+ readl(mv_hc_base(mmio, hc) + HC_CFG_OFS),
+ readl(mv_hc_base(mmio, hc) + HC_IRQ_CAUSE_OFS));
+ }
+
+ writel(~HC_MAIN_MASKED_IRQS, mmio + HC_MAIN_IRQ_MASK);
+ writel(PCI_UNMASK_ALL_IRQS, mmio + PCI_IRQ_MASK);
+
+ VPRINTK("HC MAIN IRQ cause/mask=0x%08x/0x%08x "
+ "PCI int cause/mask=0x%08x/0x%08x\n",
+ readl(mmio+HC_MAIN_IRQ_CAUSE),
+ readl(mmio+HC_MAIN_IRQ_MASK),
+ readl(mmio+PCI_IRQ_CAUSE),
+ readl(mmio+PCI_IRQ_MASK));
+
+ done:
+ return (rc);
+}
+
+static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version = 0;
+ struct ata_probe_ent *probe_ent = NULL;
+ struct mv_host_priv *hpriv;
+ unsigned int board_idx = (unsigned int)ent->driver_data;
+ void __iomem *mmio_base;
+ int pci_dev_busy = 0;
+ int rc;
+
+ if (!printed_version++) {
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+ }
+
+ VPRINTK("ENTER for PCI Bus:Slot.Func=%u:%u.%u\n", pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ return (rc);
+ }
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ pci_intx(pdev, 1);
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (probe_ent == NULL) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ INIT_LIST_HEAD(&probe_ent->node);
+
+ mmio_base = ioremap_nocache(pci_resource_start(pdev, MV_PRIMARY_BAR),
+ pci_resource_len(pdev, MV_PRIMARY_BAR));
+ if (mmio_base == NULL) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+
+ hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
+ if (!hpriv) {
+ rc = -ENOMEM;
+ goto err_out_iounmap;
+ }
+ memset(hpriv, 0, sizeof(*hpriv));
+
+ probe_ent->sht = mv_port_info[board_idx].sht;
+ probe_ent->host_flags = mv_port_info[board_idx].host_flags;
+ probe_ent->pio_mask = mv_port_info[board_idx].pio_mask;
+ probe_ent->udma_mask = mv_port_info[board_idx].udma_mask;
+ probe_ent->port_ops = mv_port_info[board_idx].port_ops;
+
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->mmio_base = mmio_base;
+ probe_ent->private_data = hpriv;
+
+ /* initialize adapter */
+ rc = mv_host_init(probe_ent);
+ if (rc) {
+ goto err_out_hpriv;
+ }
+/* mv_print_info(probe_ent); */
+
+ {
+ int b, w;
+ u32 dw[4]; /* hold a line of 16b */
+ VPRINTK("PCI config space:\n");
+ for (b = 0; b < 0x40; ) {
+ for (w = 0; w < 4; w++) {
+ (void) pci_read_config_dword(pdev,b,&dw[w]);
+ b += sizeof(*dw);
+ }
+ VPRINTK("%08x %08x %08x %08x\n",
+ dw[0],dw[1],dw[2],dw[3]);
+ }
+ }
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return (0);
+
+ err_out_hpriv:
+ kfree(hpriv);
+ err_out_iounmap:
+ iounmap(mmio_base);
+ err_out_free_ent:
+ kfree(probe_ent);
+ err_out_regions:
+ pci_release_regions(pdev);
+ err_out:
+ if (!pci_dev_busy) {
+ pci_disable_device(pdev);
+ }
+
+ return (rc);
+}
+
+static int __init mv_init(void)
+{
+ return (pci_module_init(&mv_pci_driver));
+}
+
+static void __exit mv_exit(void)
+{
+ pci_unregister_driver(&mv_pci_driver);
+}
+
+MODULE_AUTHOR("Brett Russ");
+MODULE_DESCRIPTION("SCSI low-level driver for Marvell SATA controllers");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, mv_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(mv_init);
+module_exit(mv_exit);
Index: linux-2.6.13/drivers/scsi/Kconfig
===================================================================
--- linux-2.6.13.orig/drivers/scsi/Kconfig
+++ linux-2.6.13/drivers/scsi/Kconfig
@@ -459,6 +459,15 @@ config SCSI_ATA_PIIX

If unsure, say N.

+config SCSI_SATA_MV
+ tristate "Marvell SATA support"
+ depends on SCSI_SATA && PCI && EXPERIMENTAL
+ help
+ This option enables support for the Marvell Serial ATA family.
+ Currently supports 88SX[56]0[48][01] chips.
+
+ If unsure, say N.
+
config SCSI_SATA_NV
tristate "NVIDIA SATA support"
depends on SCSI_SATA && PCI && EXPERIMENTAL
Index: linux-2.6.13/drivers/scsi/Makefile
===================================================================
--- linux-2.6.13.orig/drivers/scsi/Makefile
+++ linux-2.6.13/drivers/scsi/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_SCSI_SATA_SIS) += libata.o
obj-$(CONFIG_SCSI_SATA_SX4) += libata.o sata_sx4.o
obj-$(CONFIG_SCSI_SATA_NV) += libata.o sata_nv.o
obj-$(CONFIG_SCSI_SATA_ULI) += libata.o sata_uli.o
+obj-$(CONFIG_SCSI_SATA_MV) += libata.o sata_mv.o

obj-$(CONFIG_ARM) += arm/

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Jeff Garzik

unread,
Aug 30, 2005, 4:48:55 PM8/30/05
to Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org
Brett Russ wrote:
> This is the first public release of my libata compatible low level driver for
> the Marvell SATA family. Currently it successfully runs in PIO mode on a 6081
> chip. EDMA support is in the works and should be done shortly. Review,
> testing (especially on other flavors of Marvell), comments welcome.

Even though its only PIO, if you feel this is stable, I would like to
get it into upstream soon-ish.

Current version looks OK to me.

Jeff

Jiri Slaby

unread,
Aug 31, 2005, 6:39:00 AM8/31/05
to Brett Russ, Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org
Brett Russ napsal(a):

>This is the first public release of my libata compatible low level driver for
>the Marvell SATA family. Currently it successfully runs in PIO mode on a 6081
>chip. EDMA support is in the works and should be done shortly. Review,
>testing (especially on other flavors of Marvell), comments welcome.
>
>

[snip]

>+static struct pci_device_id mv_pci_tbl[] = {
>+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x},
>+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x},
>+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_508x},
>+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x},
>+
>+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x},
>+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x},
>+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x},
>+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x},
>+ {} /* terminate list */
>+};

> <http://localhost/lxr/ident?i=MODULE_DEVICE_TABLE>
>
MODULE_DEVICE_TABLE(pci,
<http://localhost/lxr/ident?i=MODULE_DEVICE_TABLE>mv_pci_tbl); here for
hotplug

>+
>+static struct pci_driver mv_pci_driver = {
>+ .name = DRV_NAME,
>+ .id_table = mv_pci_tbl,
>+ .probe = mv_init_one,
>+ .remove = ata_pci_remove_one,
>+};
>
>

[snip]

--
Jiri Slaby www.fi.muni.cz/~xslaby
~\-/~ jiri...@gmail.com ~\-/~
241B347EC88228DE51EE A49C4A73A25004CB2A10

Brett Russ

unread,
Aug 31, 2005, 7:43:09 AM8/31/05
to Jiri Slaby, Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org
Jiri Slaby wrote:
>> +static struct pci_device_id mv_pci_tbl[] = {
>> + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x},
>> + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x},
>> + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_508x},
>> + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x},
>> +
>> + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x},
>> + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x},
>> + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x},
>> + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x},
>> + {} /* terminate list */
>> +};
>> <http://localhost/lxr/ident?i=MODULE_DEVICE_TABLE>
>>
> MODULE_DEVICE_TABLE(pci,
> <http://localhost/lxr/ident?i=MODULE_DEVICE_TABLE>mv_pci_tbl); here for
> hotplug

Thanks Jiri,

All the MODULE_ stuff is at the bottom of the file, including
MODULE_DEVICE_TABLE. What inserted those URLs?

BR

Brett Russ

unread,
Sep 1, 2005, 10:31:42 AM9/1/05
to Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org
Some (non-functional) cleanup modifications since the version 0.10
driver I sent out 2005-08-30. Also adding signed-off-by for Jeff's
upstream push. This is my libata compatible low level driver for

the Marvell SATA family. Currently it successfully runs in PIO mode
on a 6081 chip. EDMA support is in the works and should be done
shortly. Review, testing (especially on other flavors of Marvell),
comments welcome.

Thank you,
BR

Signed-off-by: Brett Russ <ru...@emc.com>


Index: linux-2.6.13/drivers/scsi/sata_mv.c
===================================================================
--- /dev/null
+++ linux-2.6.13/drivers/scsi/sata_mv.c

@@ -0,0 +1,826 @@

+#define DRV_VERSION "0.11"

+ /* Host Flags */


+ MV_FLAG_DUAL_HC = (1 << 30), /* two SATA Host Controllers */
+ MV_FLAG_IRQ_COALESCE = (1 << 29), /* IRQ coalescing capability */
+ MV_FLAG_BDMA = (1 << 28), /* Basic DMA */
+
+ chip_504x = 0,
+ chip_508x = 1,
+ chip_604x = 2,
+ chip_608x = 3,
+
+ /* PCI interface registers */
+
+ PCI_MAIN_CMD_STS_OFS = 0xd30,
+ STOP_PCI_MASTER = (1 << 2),
+ PCI_MASTER_EMPTY = (1 << 3),
+ GLOB_SFT_RST = (1 << 4),
+

+ PCI_IRQ_CAUSE_OFS = 0x1d58,
+ PCI_IRQ_MASK_OFS = 0x1d5c,


+ PCI_UNMASK_ALL_IRQS = 0x7fffff, /* bits 22-0 */
+

+ HC_MAIN_IRQ_CAUSE_OFS = 0x1d60,
+ HC_MAIN_IRQ_MASK_OFS = 0x1d64,


+ PORT0_ERR = (1 << 0), /* shift by port # */
+ PORT0_DONE = (1 << 1), /* shift by port # */
+ HC0_IRQ_PEND = 0x1ff, /* bits 0-8 = HC0's ports */
+ HC_SHIFT = 9, /* bits 9-17 = HC1's ports */
+ PCI_ERR = (1 << 18),
+ TRAN_LO_DONE = (1 << 19), /* 6xxx: IRQ coalescing */
+ TRAN_HI_DONE = (1 << 20), /* 6xxx: IRQ coalescing */
+ PORTS_0_7_COAL_DONE = (1 << 21), /* 6xxx: IRQ coalescing */
+ GPIO_INT = (1 << 22),
+ SELF_INT = (1 << 23),
+ TWSI_INT = (1 << 24),
+ HC_MAIN_RSVD = (0x7f << 25), /* bits 31-25 */

+ .name = DRV_NAME,

+static struct pci_device_id mv_pci_tbl[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_508x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x},
+
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x},
+ {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x},
+ {} /* terminate list */
+};

+
+static struct pci_driver mv_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = mv_pci_tbl,
+ .probe = mv_init_one,
+ .remove = ata_pci_remove_one,
+};

+ irq_stat = readl(mmio + HC_MAIN_IRQ_CAUSE_OFS);

+ writel(~HC_MAIN_MASKED_IRQS, mmio + HC_MAIN_IRQ_MASK_OFS);
+ writel(PCI_UNMASK_ALL_IRQS, mmio + PCI_IRQ_MASK_OFS);


+
+ VPRINTK("HC MAIN IRQ cause/mask=0x%08x/0x%08x "
+ "PCI int cause/mask=0x%08x/0x%08x\n",

+ readl(mmio + HC_MAIN_IRQ_CAUSE_OFS),
+ readl(mmio + HC_MAIN_IRQ_MASK_OFS),
+ readl(mmio + PCI_IRQ_CAUSE_OFS),
+ readl(mmio + PCI_IRQ_MASK_OFS));

Christoph Hellwig

unread,
Sep 1, 2005, 10:41:56 AM9/1/05
to Brett Russ, Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org
> +#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/sched.h>
> +#include <linux/dma-mapping.h>
> +#include "scsi.h"

pleaese don't include "scsi.h" in new drivers. It will go away soon.
Use the <scsi/*.h> headers and get rid of usage of obsolete constucts
in your driver.

> +static inline void writelfl(unsigned long data, void __iomem *addr)
> +{
> + writel(data, addr);
> + (void) readl(addr); /* flush */

no need for the (void) case.

> +static void mv_irq_clear(struct ata_port *ap)
> +{
> + return;
> +}

no need for the return

> + return (ofs);

please remove the braces around the return value

> + if (ap &&
> + (NULL != (qc = ata_qc_from_tag(ap, ap->active_tag)))) {
> + VPRINTK("port %u IRQ found for qc, ata_status 0x%x\n",
> + port,ata_status);
> + BUG_ON(0xffU == ata_status);
> + /* mark qc status appropriately */
> + ata_qc_complete(qc, ata_status);
> + }

the formatting looks rather odd. What about;

if (ap) {
qc = ata_qc_from_tag(ap, ap->active_tag);
if (qc) {


VPRINTK("port %u IRQ found for qc, "
"ata_status 0x%x\n",

port, ata_status);
BUG_ON(0xffU == ata_status);


/* mark qc status appropriately */

ata_qc_complete(qc, ata_status);
}
}

> + err_out_hpriv:

rather odd placement of the goto labels. If you look at kernel code it's
always either not indented at all, or indented a single space.

Jeff Garzik

unread,
Sep 1, 2005, 3:49:44 PM9/1/05
to Christoph Hellwig, Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org
Christoph Hellwig wrote:
>>+#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/sched.h>
>>+#include <linux/dma-mapping.h>
>>+#include "scsi.h"
>
>
> pleaese don't include "scsi.h" in new drivers. It will go away soon.
> Use the <scsi/*.h> headers and get rid of usage of obsolete constucts
> in your driver.


It stays until the rest of the libata drivers lose the include.

After ATAPI support is done, I can stop 2.4.x support, and this and
several other compat-isms will go away.

Jeff

Christoph Hellwig

unread,
Sep 1, 2005, 3:59:06 PM9/1/05
to Jeff Garzik, Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org
On Thu, Sep 01, 2005 at 03:48:51PM -0400, Jeff Garzik wrote:
> Christoph Hellwig wrote:
> >>+#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/sched.h>
> >>+#include <linux/dma-mapping.h>
> >>+#include "scsi.h"
> >
> >
> >pleaese don't include "scsi.h" in new drivers. It will go away soon.
> >Use the <scsi/*.h> headers and get rid of usage of obsolete constucts
> >in your driver.
>
>
> It stays until the rest of the libata drivers lose the include.
>
> After ATAPI support is done, I can stop 2.4.x support, and this and
> several other compat-isms will go away.

NACK. Jeff, I accept that you don't want to convert old drivers yet,
but this is not acceptable for new drivers. We don't allow it for any
new scsi LLDDs, and that includes libata drivers.

Jeff Garzik

unread,
Sep 1, 2005, 4:04:09 PM9/1/05
to Christoph Hellwig, Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org
Christoph Hellwig wrote:
> On Thu, Sep 01, 2005 at 03:48:51PM -0400, Jeff Garzik wrote:
>
>>Christoph Hellwig wrote:
>>
>>>>+#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/sched.h>
>>>>+#include <linux/dma-mapping.h>
>>>>+#include "scsi.h"
>>>
>>>
>>>pleaese don't include "scsi.h" in new drivers. It will go away soon.
>>>Use the <scsi/*.h> headers and get rid of usage of obsolete constucts
>>>in your driver.
>>
>>
>>It stays until the rest of the libata drivers lose the include.
>>
>>After ATAPI support is done, I can stop 2.4.x support, and this and
>>several other compat-isms will go away.
>
>
> NACK. Jeff, I accept that you don't want to convert old drivers yet,
> but this is not acceptable for new drivers. We don't allow it for any
> new scsi LLDDs, and that includes libata drivers.

Sorry, you don't get to NAK that change, since it affects 2.4.x
maintenance of this new driver.

As I said, the include does away simultaneously for all libata drivers.

Jeff

Jeff Garzik

unread,
Sep 1, 2005, 4:05:50 PM9/1/05
to Christoph Hellwig, Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org
Jeff Garzik wrote:
> As I said, the include does away simultaneously for all libata drivers.

s/does/goes/

Christoph Hellwig

unread,
Sep 1, 2005, 4:06:12 PM9/1/05
to Jeff Garzik, Christoph Hellwig, Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org

Stop that crap now please. Adding "scsi.h" includes is _not_ allowed
for new drivers, period. There's no exceptions, not even for
Jeff "I'm part of the calal" Garzik.

Jeff Garzik

unread,
Sep 1, 2005, 4:28:50 PM9/1/05
to Christoph Hellwig, Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org
Christoph Hellwig wrote:
> Stop that crap now please. Adding "scsi.h" includes is _not_ allowed
> for new drivers, period. There's no exceptions, not even for
> Jeff "I'm part of the calal" Garzik.

There are solid technical reasons (a) why libata drivers include scsi.h,
and (b) why all libata drivers look similar. It -impedes- maintenance
to have one libata driver different from all the others, and this is
what you are suggesting.

Your suggestion causes nothing but additional work, for zero gain: as I
have explained, all the scsi.h includes will go away at the same time.
Such as sweep would catch all libata drivers, including sata_mv.

Until you're willing to step up and help with 2.4.x maintenance, you're
just being an impediment for non-technical reasons. If you want to do
that, join politics and become a politician. I have real work to do.

Jeff

Brett Russ

unread,
Sep 1, 2005, 6:29:10 PM9/1/05
to Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org, Christoph Hellwig
More (non-functional) style modifications since the version 0.11
driver I sent out earlier today. Removed most parens around return
value, corrected indentation of labels, improved comment for flush,
removed naked return, and cleanup ata_qc_from_tag() call block.

This is my libata compatible low level driver for the Marvell SATA
family. Currently it successfully runs in PIO mode on a 6081 chip.
EDMA support is in the works and should be done shortly. Review,
testing (especially on other flavors of Marvell), comments welcome.

Thank you,
BR

Signed-off-by: Brett Russ <ru...@emc.com>


Index: linux-2.6.13/drivers/scsi/sata_mv.c
===================================================================
--- /dev/null
+++ linux-2.6.13/drivers/scsi/sata_mv.c

@@ -0,0 +1,827 @@


+/*
+ * sata_mv.c - Marvell SATA support
+ *
+ * Copyright 2005: EMC Corporation, all rights reserved.
+ *
+ * Please ALWAYS copy linu...@vger.kernel.org on emails.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+

+#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/sched.h>
+#include <linux/dma-mapping.h>
+#include "scsi.h"

+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+
+#define DRV_NAME "sata_mv"

+#define DRV_VERSION "0.12"

+static inline void writelfl(unsigned long data, void __iomem *addr)
+{
+ writel(data, addr);

+ (void) readl(addr); /* flush to avoid PCI posted write */


+}
+
+static inline void __iomem *mv_port_addr_to_hc_base(void __iomem *port_mmio)
+{
+ return ((void __iomem *)((unsigned long)port_mmio &
+ (unsigned long)SATAHC_MASK));
+}
+
+static inline void __iomem *mv_hc_base(void __iomem *base, unsigned int hc)
+{
+ return (base + MV_SATAHC0_REG_BASE + (hc * MV_SATAHC_REG_SZ));
+}
+
+static inline void __iomem *mv_port_base(void __iomem *base, unsigned int port)
+{
+ return (mv_hc_base(base, port >> MV_PORT_HC_SHIFT) +
+ MV_SATAHC_ARBTR_REG_SZ +
+ ((port & MV_PORT_MASK) * MV_PORT_REG_SZ));
+}
+

+static inline void __iomem *mv_ap_base(struct ata_port *ap)
+{
+ return mv_port_base(ap->host_set->mmio_base, ap->port_no);


+}
+
+static inline int mv_get_hc_count(unsigned long flags)
+{
+ return ((flags & MV_FLAG_DUAL_HC) ? 2 : 1);
+}
+
+static inline int mv_is_edma_active(struct ata_port *ap)
+{
+ void __iomem *port_mmio = mv_ap_base(ap);
+ return (EDMA_EN & readl(port_mmio + EDMA_CMD_OFS));
+}
+

+static inline int mv_port_bdma_capable(struct ata_port *ap)
+{


+ return (ap->flags & MV_FLAG_BDMA);
+}
+

+static void mv_irq_clear(struct ata_port *ap)
+{
+}

+
+static unsigned int mv_scr_offset(unsigned int sc_reg_in)
+{
+ unsigned int ofs;
+
+ switch (sc_reg_in) {
+ case SCR_STATUS:
+ case SCR_CONTROL:
+ case SCR_ERROR:
+ ofs = SATA_STATUS_OFS + (sc_reg_in * sizeof(u32));
+ break;
+ case SCR_ACTIVE:
+ ofs = SATA_ACTIVE_OFS; /* active is not with the others */
+ break;
+ default:
+ ofs = 0xffffffffU;
+ break;
+ }

+ return ofs;


+}
+
+static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in)
+{
+ unsigned int ofs = mv_scr_offset(sc_reg_in);
+
+ if (0xffffffffU != ofs) {

+ return readl(mv_ap_base(ap) + ofs);
+ } else {
+ return (u32) ofs;

+ return rc;

+ if (ap) {
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (NULL != qc) {


+ VPRINTK("port %u IRQ found for qc, "

+ "ata_status 0x%x\n", port,ata_status);


+ BUG_ON(0xffU == ata_status);
+ /* mark qc status appropriately */
+ ata_qc_complete(qc, ata_status);
+ }

+ return IRQ_RETVAL(handled);

+ return rc;


+}
+
+static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version = 0;
+ struct ata_probe_ent *probe_ent = NULL;
+ struct mv_host_priv *hpriv;
+ unsigned int board_idx = (unsigned int)ent->driver_data;
+ void __iomem *mmio_base;
+ int pci_dev_busy = 0;
+ int rc;
+
+ if (!printed_version++) {
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+ }
+
+ VPRINTK("ENTER for PCI Bus:Slot.Func=%u:%u.%u\n", pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+
+ rc = pci_enable_device(pdev);
+ if (rc) {

+ return rc;

+ return 0;


+
+ err_out_hpriv:
+ kfree(hpriv);
+ err_out_iounmap:
+ iounmap(mmio_base);
+ err_out_free_ent:
+ kfree(probe_ent);
+ err_out_regions:
+ pci_release_regions(pdev);
+ err_out:
+ if (!pci_dev_busy) {
+ pci_disable_device(pdev);
+ }
+

+ return rc;


+}
+
+static int __init mv_init(void)
+{

+ return pci_module_init(&mv_pci_driver);

Brett Russ

unread,
Sep 1, 2005, 9:33:44 PM9/1/05
to Brett Russ, Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org, Christoph Hellwig
Brett Russ wrote:
> This is my libata compatible low level driver for the Marvell SATA
> family. Currently it successfully runs in PIO mode on a 6081 chip.
> EDMA support is in the works and should be done shortly. Review,
> testing (especially on other flavors of Marvell), comments welcome.

Note that this patch depends on the PCI INTx patch I submitted earlier:

http://lkml.org/lkml/2005/8/15/165

Sorry for the delayed notice,
BR

Bogdan Costescu

unread,
Sep 2, 2005, 1:18:35 PM9/2/05
to Brett Russ, Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org, Christoph Hellwig
On Thu, 1 Sep 2005, Brett Russ wrote:

> This is my libata compatible low level driver for the Marvell SATA
> family.

First of all, thanks! I've been waiting for such a driver to appear.

I somehow did not manage to get a working 2.6.13 kernel to boot and,
as I didn't have too much time to see why, I compiled this driver
against the RHEL 4 Update 2 beta kernel (2.6.9-16, but which is itself
compiled by me from source RPM, so Jeff won't be able to match
addresses with the real RHEL binary RPM), which has libata v1.11, as
opposed to v1.12 in kernel 2.6.13 - I had to add the pci_intx function
(from ahci.c in kernel 2.6.13) and to remove the last line
(.ordered_flush = 1) from mv_sht initialization. All tests are with
the UP kernel.

The hardware is an Asus PSCHSR-A board with Adaptec AIC8110, but which
appears on the PCI bus (supposedly a PCI-X one) as:

02:08.0 SCSI storage controller: Marvell Technology Group Ltd. MV88SX5041 4-port SATA I PCI-X Controller (rev 03)

The driver starts with:

ACPI: PCI interrupt 0000:02:08.0[A] -> GSI 9 (level, low) -> IRQ 9
sata_mvPCI master won't flush

which means init failed, but the driver is still loaded at this point.
Afterwards, when I try to run rmmod, I get:

Unable to handle kernel NULL pointer dereference at virtual address 00000024
printing eip:
f89738b9
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: sata_mv(U) sd_mod libata scsi_mod nfsd exportfs lockd sunrpc dm_mod button battery ac uhci_hcd ehci_hcd e1000 ext3 jbd
CPU: 0
EIP: 0060:[<f89738b9>] Not tainted VLI
EFLAGS: 00010246 (2.6.9-16.EL)
EIP is at ata_pci_remove_one+0x12/0xb3 [libata]
eax: f7f6ac44 ebx: 00000000 ecx: f8855488 edx: f89738a7
esi: f8855424 edi: 00000000 ebp: f7f6ac00 esp: f6f96f14
ds: 007b es: 007b ss: 0068
Process rmmod (pid: 2628, threadinfo=f6f96000 task=f733f770)
Stack: f7f6ac44 f7f6ac00 f8855424 f8855424 f6f96000 c01ec1df f7f6ac44 c024a46f
f8855424 f8855488 c024a491 c0368c40 c0368c8c c024a821 f885542c f8855424
00000000 c024abe7 f8855400 c035e5c0 c01ec39c f8855500 c013b6f5 00000000
Call Trace:
[<c01ec1df>] pci_device_remove+0x16/0x28
[<c024a46f>] device_release_driver+0x3c/0x46
[<c024a491>] driver_detach+0x18/0x1f
[<c024a821>] bus_remove_driver+0x48/0x75
[<c024abe7>] driver_unregister+0xc/0x31
[<c01ec39c>] pci_unregister_driver+0xb/0x13
[<c013b6f5>] sys_delete_module+0x132/0x179
[<c015a699>] unmap_vma_list+0xe/0x17
[<c015aa48>] do_munmap+0x1c8/0x1d2
[<c011a779>] do_page_fault+0x0/0x4dc
[<c030fa37>] syscall_call+0x7/0xb
Code: 87 c7 83 3c 24 00 74 07 89 e8 e8 59 72 87 c7 89 f0 83 c4 54 5b 5e 5f 5d c3 55 89 c5 8d 40 44 57 31 ff 56 53 52 89 04 24 8b 58 74 <3b> 7b 24 73 0e 8b 74 bb 30 47 8b 06 e8 55 ef fa ff eb ed 8b 43
<0>Fatal exception: panic in 5 seconds

This init error seems to be connected to the PCI BusMaster state:
after booting, but before loading the driver, 'lspci -vv' shows
BusMaster+, but after loading the driver which complains, the state is
BusMaster-. This is with the latest BIOS (1007A) available for this
board, so "update BIOS" is not an option...

If I comment out the call to mv_master_reset(), the driver seems to
initialize fine (no disks attached at this point):

sata_mv version 0.10
ACPI: PCI interrupt 0000:02:08.0[A] -> GSI 9 (level, low) -> IRQ 9
ata1: SATA max PIO4 cmd 0x0 ctl 0xF8A22120 bmdma 0x0 irq 9
ata2: SATA max PIO4 cmd 0x0 ctl 0xF8A24120 bmdma 0x0 irq 9
ata3: SATA max PIO4 cmd 0x0 ctl 0xF8A26120 bmdma 0x0 irq 9
ata4: SATA max PIO4 cmd 0x0 ctl 0xF8A28120 bmdma 0x0 irq 9
ata1: no device found (phy stat 00000000)
scsi0 : sata_mv
ata2: no device found (phy stat 00000000)
scsi1 : sata_mv
ata3: no device found (phy stat 00000000)
scsi2 : sata_mv
ata4: no device found (phy stat 00000000)
scsi3 : sata_mv
blk_queue_max_hw_segments: set to minimum 1

(the last line is actually repeated several times). Afterwards, rmmod
doesn't produce any oops.

If I connect a disk in this state (driver unloaded) = device hotplug,
I get immediately:

irq 9: nobody cared! (screaming interrupt?)
irq 9: Please try booting with acpi=off and report a bug
[<c0107e8f>] __report_bad_irq+0x3a/0x77
[<c010834d>] note_interrupt+0x191/0x1b7
[<c0108852>] do_IRQ+0x209/0x2bf
[<c030fb7c>] common_interrupt+0x18/0x20
[<c02b007b>] skb_copy_and_csum_bits+0x25d/0x281
[<c0126774>] __do_softirq+0x2c/0x79
[<c0109338>] do_softirq+0x46/0x4d
=======================
[<c01088fc>] do_IRQ+0x2b3/0x2bf
[<c030fb7c>] common_interrupt+0x18/0x20
[<c010403b>] default_idle+0x23/0x26
[<c010408c>] cpu_idle+0x1f/0x34
[<c03b86b9>] start_kernel+0x214/0x216
handlers:
[<c01fc0c3>] (acpi_irq+0x0/0x14)
Disabling IRQ #9

so I guess that the controller generates some interrupt whenever a
device is connected and the kernel doesn't know what to do with this
interrupt - so the interrupt generation by the controller probably has
to be disabled on rmmod.

Booting with apci=off and loading the same driver (without the
mv_master_reset() call) with one disk connected, I get:

PCI: Found IRQ 5 for device 0000:02:08.0
PCI: Sharing IRQ 5 with 0000:00:1d.0
IRQ routing conflict for 0000:02:08.0, have irq 9, want irq 5
ata1: SATA max PIO4 cmd 0x0 ctl 0xF8A22120 bmdma 0x0 irq 9
ata2: SATA max PIO4 cmd 0x0 ctl 0xF8A24120 bmdma 0x0 irq 9
ata3: SATA max PIO4 cmd 0x0 ctl 0xF8A26120 bmdma 0x0 irq 9
ata4: SATA max PIO4 cmd 0x0 ctl 0xF8A28120 bmdma 0x0 irq 9
Badness in __sata_phy_reset at drivers/scsi/libata-core.c:1413
[<f88e8f0c>] __sata_phy_reset+0x75/0x12e [libata]
[<f883f62f>] mv_phy_reset+0xbf/0x11e [sata_mv]
[<c0250f16>] end_that_request_last+0x6c/0x7e
[<f883f3bf>] mv_host_intr+0xd6/0x142 [sata_mv]
[<f883f500>] mv_interrupt+0xd5/0x145 [sata_mv]
[<c0107e2b>] handle_IRQ_event+0x25/0x4f
[<c01087d3>] do_IRQ+0x18a/0x2bf
=======================
[<c030fb7c>] common_interrupt+0x18/0x20
[<f883f618>] mv_phy_reset+0xa8/0x11e [sata_mv]
[<c01091d8>] setup_irq+0x179/0x181
[<f883f42b>] mv_interrupt+0x0/0x145 [sata_mv]
[<f88e8e25>] ata_bus_probe+0xe/0x7b [libata]
[<f88eb34d>] ata_device_add+0x186/0x202 [libata]
[<f883f97a>] mv_init_one+0x197/0x1d5 [sata_mv]
[<c01ec15d>] pci_device_probe_static+0x2a/0x3d
[<c01ec18b>] __pci_device_probe+0x1b/0x2c
[<c01ec1b7>] pci_device_probe+0x1b/0x2d
[<c024a33b>] bus_match+0x27/0x45
[<c024a404>] driver_attach+0x37/0x66
[<c024a7b9>] bus_add_driver+0x77/0x97
[<c024abd4>] driver_register+0x51/0x58
[<c01ec375>] pci_register_driver+0x85/0xa1
[<f881a00a>] mv_init+0xa/0x15 [sata_mv]
[<c013d5a3>] sys_init_module+0x1f1/0x2d9
[<c030fa37>] syscall_call+0x7/0xb
bad: scheduling while atomic!
[<c030d515>] schedule+0x2d/0x552
[<c0107e2b>] handle_IRQ_event+0x25/0x4f
[<c030e40e>] schedule_timeout+0xf1/0x10c
[<c012ad7e>] process_timeout+0x0/0x5
[<f883f082>] mv_scr_read+0xf/0x54 [sata_mv]
[<c012b498>] msleep+0x4e/0x54
[<f88e8f3f>] __sata_phy_reset+0xa8/0x12e [libata]
[<f883f62f>] mv_phy_reset+0xbf/0x11e [sata_mv]
[<c0250f16>] end_that_request_last+0x6c/0x7e
[<f883f3bf>] mv_host_intr+0xd6/0x142 [sata_mv]
[<f883f500>] mv_interrupt+0xd5/0x145 [sata_mv]
[<c0107e2b>] handle_IRQ_event+0x25/0x4f
[<c01087d3>] do_IRQ+0x18a/0x2bf
=======================
[<c030fb7c>] common_interrupt+0x18/0x20
[<f883f618>] mv_phy_reset+0xa8/0x11e [sata_mv]
[<c01091d8>] setup_irq+0x179/0x181
[<f883f42b>] mv_interrupt+0x0/0x145 [sata_mv]
[<f88e8e25>] ata_bus_probe+0xe/0x7b [libata]
[<f88eb34d>] ata_device_add+0x186/0x202 [libata]
[<f883f97a>] mv_init_one+0x197/0x1d5 [sata_mv]
[<c01ec15d>] pci_device_probe_static+0x2a/0x3d
[<c01ec18b>] __pci_device_probe+0x1b/0x2c
[<c01ec1b7>] pci_device_probe+0x1b/0x2d
[<c024a33b>] bus_match+0x27/0x45
[<c024a404>] driver_attach+0x37/0x66
[<c024a7b9>] bus_add_driver+0x77/0x97
[<c024abd4>] driver_register+0x51/0x58
[<c01ec375>] pci_register_driver+0x85/0xa1
[<f881a00a>] mv_init+0xa/0x15 [sata_mv]
[<c013d5a3>] sys_init_module+0x1f1/0x2d9
[<c030fa37>] syscall_call+0x7/0xb


I don't know how much of the problem comes from BIOS/ACPI and how much
from the combination of this driver with the RHEL kernel and my
hacking. But I don't know how to proceed further, so I'm waiting for
some hints or patches :-)

Side note: I was able some time ago to use this controller with the
mv_sata driver 3.40, also with a RHEL kernel, without any fiddling
with ACPI.

--
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan....@IWR.Uni-Heidelberg.De

Ray Lee

unread,
Sep 2, 2005, 1:55:52 PM9/2/05
to Brett Russ, Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org, Christoph Hellwig
On 9/1/05, Brett Russ <ru...@emc.com> wrote:
> More (non-functional) style modifications since the version 0.11
> driver I sent out earlier today. Removed most parens around return
> value,

return is not a function call; you can safely remove them all.

> + return ((void __iomem *)((unsigned long)port_mmio &
> + (unsigned long)SATAHC_MASK));

> + return (base + MV_SATAHC0_REG_BASE + (hc * MV_SATAHC_REG_SZ));

> + return (mv_hc_base(base, port >> MV_PORT_HC_SHIFT) +
> + MV_SATAHC_ARBTR_REG_SZ +
> + ((port & MV_PORT_MASK) * MV_PORT_REG_SZ));

> + return ((flags & MV_FLAG_DUAL_HC) ? 2 : 1);

> + return (EDMA_EN & readl(port_mmio + EDMA_CMD_OFS));

> + return (ap->flags & MV_FLAG_BDMA);

Ray

Tyler

unread,
Sep 3, 2005, 10:09:34 AM9/3/05
to Brett Russ, Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org
Brett Russ wrote:

>Some (non-functional) cleanup modifications since the version 0.10
>driver I sent out 2005-08-30. Also adding signed-off-by for Jeff's
>upstream push. This is my libata compatible low level driver for
>the Marvell SATA family. Currently it successfully runs in PIO mode
>on a 6081 chip. EDMA support is in the works and should be done
>shortly. Review, testing (especially on other flavors of Marvell),
>comments welcome.
>
>Thank you,
>BR
>
>Signed-off-by: Brett Russ <ru...@emc.com>
>
>
>

[snip..]

Please find attached patches that add the Adaptec 1420SA controller to
the PCI ID list in the driver, and a small note in the kernel config
option to state so. This is untested as of currently, if anyone has a
1420SA to try, that would be great.. The one I had access to is now
gone to a remote location out of reach for testing. I read in one post
I found with google, that the 1420SA uses a 6541 chip instead of a 6041,
but I am not able to verify this, and also don't know if it may still
work as a 6041. The card is still a Sata2, PCI-X card, with 4 ports,
the same as the 6041 based cards. This patch may or may not be
useful.. The card comes with a manufacturer ID of 9005 according to a
linux PCI-ID list, which is a secondary id of Adaptec's known as
ADAPTEC2, and an actual PCI Id of 0241.

Signed-off-by: Tyler Guthrie <p...@dtbb.net>


--- linux-2.6.13.orig/drivers/scsi/Kconfig 2005-09-03
06:42:20.000000000 -0700
+++ linux-2.6.13drivers/scsi/Kconfig 2005-09-03 06:44:29.000000000 -0700
@@ -466,6 +466,8 @@


This option enables support for the Marvell Serial ATA family.

Currently supports 88SX[56]0[48][01] chips.

+ Also Including Adaptec 1420SA Card (using marvell chip pci-id
0x0241).


+
If unsure, say N.

config SCSI_SATA_NV

--- linux-2.6.13.orig/drivers/scsi/sata_mv.c 2005-09-03
06:40:07.000000000 -0700
+++ linux-2.6.13/drivers/scsi/sata_mv.c 2005-09-03
06:39:47.000000000 -0700
@@ -286,6 +286,7 @@


{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x},

{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x},

{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x},

+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x0241), 0, 0, chip_604x},
{} /* terminate list */
};

Tyler.

Jeff Garzik

unread,
Sep 3, 2005, 1:30:30 PM9/3/05
to Tyler, Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org

I would prefer to have it tested, before accepting this patch...

Jeff

Jeff Garzik

unread,
Sep 7, 2005, 1:56:50 AM9/7/05
to Brett Russ, linu...@vger.kernel.org, linux-...@vger.kernel.org, Christoph Hellwig
applied

Brett Russ

unread,
Sep 7, 2005, 10:41:04 AM9/7/05
to Jeff Garzik, linux-...@vger.kernel.org, linu...@vger.kernel.org, h...@infradead.org
Jeff Garzik wrote:
> applied
>

There are some issues with this. One of which I fixed and the other is
a bit confusing. The one I fixed concerned the 5xxx chips not
supporting the master reset functionality. The other problem has been
reported by 2 people so far. I have a stack trace from each of them:

Stack 1: (from http://article.gmane.org/gmane.linux.ide/5280)

Stack 2: (from off list email)


scheduling while atomic: klogd/0x00010000/1572
[<c0343524>] schedule+0xab4/0xbf0
[<c01200bf>] scheduler_tick+0x15f/0x380
[<c012dbe0>] lock_timer_base+0x20/0x50
[<c012dcb8>] __mod_timer+0xa8/0xd0
[<c0343eee>] schedule_timeout+0x4e/0xc0
[<c012e790>] process_timeout+0x0/0x10
[<c012ebb0>] msleep+0x30/0x40
[<f89d914a>] __sata_phy_reset+0x4a/0x120 [libata]
[<c0114b72>] delay_pmtmr+0x12/0x20
[<f899051a>] mv_phy_reset+0x7a/0x140 [sata_mv]
[<c02857f2>] ide_end_request+0x92/0xb0
[<f899035e>] mv_host_intr+0xce/0x120 [sata_mv]
[<f8990457>] mv_interrupt+0xa7/0xf0 [sata_mv]
[<c0149fe3>] handle_IRQ_event+0x33/0x70
[<c014a0f9>] __do_IRQ+0xd9/0x150
[<c0106847>] do_IRQ+0x57/0xa0
=======================
[<c0104c4a>] common_interrupt+0x1a/0x20
[<c013007b>] __group_send_sig_info+0x2b/0xd0
[<c01247f8>] do_syslog+0xe8/0x3e0
[<c0104cd8>] apic_timer_interrupt+0x1c/0x24
[<c013a0c0>] autoremove_wake_function+0x0/0x50
[<c013a0c0>] autoremove_wake_function+0x0/0x50
[<c01a8090>] kmsg_read+0x0/0x50
[<c016c368>] vfs_read+0xb8/0x170
[<c016c701>] sys_read+0x41/0x70
[<c01041bb>] sysenter_past_esp+0x54/0x75

So it looks like mv_phy_reset() is getting called from interrupt level,
and it calls __sata_phy_reset() which sleeps.

I only call mv_phy_reset() as part of fatal error interrupt cleanup.
The chip does take an "error" interrupt upon drive connection but that's
not fatal. Either way, mv_phy_reset() is called from mv_err_intr()
which doesn't appear in either of the stack dumps above.

Possible solutions:
-change __sata_phy_reset() to do a mdelay rather than msleep?
-do the phy_reset part of error recovery after returning from interrupt
handler?

Thoughts?
BR

Bogdan Costescu

unread,
Sep 7, 2005, 12:34:14 PM9/7/05
to Brett Russ, Jeff Garzik, linux-...@vger.kernel.org, linu...@vger.kernel.org, h...@infradead.org
On Wed, 7 Sep 2005, Brett Russ wrote:

> The one I fixed concerned the 5xxx chips not supporting the master
> reset functionality.

Please note that in my case the failure to do master reset is followed
by the module still being loaded and oops-ing at rmmod, which means
that the error path in the probe routine is wrong and some resources
remain allocated.

> IRQ routing conflict for 0000:02:08.0, have irq 9, want irq 5

In the mean time, I have discovered that booting the SMP kernel (the
mainboard is UP, but both it and the CPU support HyperThreading) takes
care of this: the controller gets some high numbered IRQ, like 209 or
so, not shared. This is not exactly related to this driver, but might
help others that want to use the on-board controller on this MB.

> The chip does take an "error" interrupt upon drive connection but
> that's not fatal.

In my situation this happened with the drive already connected, so is
it possible that such interrupt is (also) generated when probing for
drives ?

Here is some more output, this time with debugging on (cut-and-pasted
from serial console, maybe with some extra newlines):

- without any drive connected, I also get problems now:

mv_init_one: ENTER for PCI Bus:Slot.Func=2:8.0
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000018/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_host_init: HC0: HC config=0x11dcf013 HC IRQ cause=0x00000000
mv_host_init: HC MAIN IRQ cause/mask=0x00000001/0x0007ffff PCI int
cause/mask=0x00000000/6mv_init_one: PCI config space:
mv_init_one: 504111ab 02b00007 01000003 00002008
mv_init_one: f5000004 00000000 00000000 00000000
mv_init_one: 00000000 00000000 00000000 81241043
mv_init_one: 00000000 00000040 00000000 00000109
mv_host_intr: ENTER, hc0 relevant=0x00000001 HC IRQ cause=0x00000000
mv_phy_reset: ENTER, port 0, mmio 0xf8a22000
mv_phy_reset: Done. Now calling __sata_phy_reset()
mv_err_intr: port 0 error; EDMA err cause: 0x00000018 SERR: 0x00000000
mv_phy_reset: ENTER, port 0, mmio 0xf8a22000
mv_phy_reset: Done. Now calling __sata_phy_reset()


Badness in __sata_phy_reset at drivers/scsi/libata-core.c:1413

[<f8961dc8>] __sata_phy_reset+0x75/0x12e [libata]
[<f887566e>] mv_phy_reset+0xe8/0x175 [sata_mv]
[<f8875449>] mv_host_intr+0xf1/0x187 [sata_mv]
[<f887555e>] mv_interrupt+0x7f/0xa7 [sata_mv]
[<c010745e>] handle_IRQ_event+0x25/0x4f
[<c01079be>] do_IRQ+0x11c/0x1ae
=======================
[<c02d1c88>] common_interrupt+0x18/0x20
bad: scheduling while atomic!
[<c02ced41>] schedule+0x2d/0x87a
[<c011e53e>] scheduler_tick+0x3ce/0x3e5
[<c0122bba>] profile_hook+0x1b/0x26
[<c0129741>] __mod_timer+0x101/0x10b
[<c02cfa90>] schedule_timeout+0xd3/0xee
[<c0129feb>] process_timeout+0x0/0x5
[<f8875082>] mv_scr_read+0xf/0x54 [sata_mv]
[<c012a562>] msleep+0x4f/0x55
[<f8961dfb>] __sata_phy_reset+0xa8/0x12e [libata]
[<f887566e>] mv_phy_reset+0xe8/0x175 [sata_mv]
[<f8875449>] mv_host_intr+0xf1/0x187 [sata_mv]
[<f887555e>] mv_interrupt+0x7f/0xa7 [sata_mv]
[<c010745e>] handle_IRQ_event+0x25/0x4f
[<c01079be>] do_IRQ+0x11c/0x1ae
=======================
[<c02d1c88>] common_interrupt+0x18/0x20

which kills the network, the system is still responsive to keyboard -
but doesn't want to reboot, needs the button push. This however seems
to be random, as I can also get a successful start:

mv_init_one: ENTER for PCI Bus:Slot.Func=2:8.0
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_host_init: HC0: HC config=0x11dcf013 HC IRQ cause=0x00000000
mv_host_init: HC MAIN IRQ cause/mask=0x00000000/0x0007ffff PCI int
cause/mask=0x00000000/6mv_init_one: PCI config space:
mv_init_one: 504111ab 02b00007 01000003 00002008
mv_init_one: f5000004 00000000 00000000 00000000
mv_init_one: 00000000 00000000 00000000 81241043
mv_init_one: 00000000 00000040 00000000 00000109
mv_phy_reset: ENTER, port 0, mmio 0xf8a22000
mv_phy_reset: Done. Now calling __sata_phy_reset()
mv_phy_reset: Port disabled pre-sig. Exiting.
mv_phy_reset: ENTER, port 1, mmio 0xf8a24000
mv_phy_reset: Done. Now calling __sata_phy_reset()
mv_phy_reset: Port disabled pre-sig. Exiting.
mv_phy_reset: ENTER, port 2, mmio 0xf8a26000
mv_phy_reset: Done. Now calling __sata_phy_reset()
mv_phy_reset: Port disabled pre-sig. Exiting.
mv_phy_reset: ENTER, port 3, mmio 0xf8a28000
mv_phy_reset: Done. Now calling __sata_phy_reset()
mv_phy_reset: Port disabled pre-sig. Exiting.

- with a drive connected to what on the MB is labeled as the first
channel for this controller (which IIRC is also recognized as the
first channel by the mv_sata driver):

mv_init_one: ENTER for PCI Bus:Slot.Func=2:8.0
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_port_init: EDMA cfg=0x0000011f EDMA IRQ err
cause/mask=0x00000000/0x00001f7f
mv_host_init: HC0: HC config=0x11dcf013 HC IRQ cause=0x00000111
mv_host_init: HC MAIN IRQ cause/mask=0x00000102/0x0007ffff PCI int
cause/mask=0x00000000/6mv_init_one: PCI config space:
mv_init_one: 504111ab 02b00007 01000003 00002008
mv_init_one: f5000004 00000000 00000000 00000000
mv_init_one: 00000000 00000000 00000000 81241043
mv_init_one: 00000000 00000040 00000000 00000109
mv_host_intr: ENTER, hc0 relevant=0x00000102 HC IRQ cause=0x00000111
mv_host_intr: EXIT
mv_phy_reset: ENTER, port 0, mmio 0xf8a22000
mv_host_intr: ENTER, hc0 relevant=0x00000001 HC IRQ cause=0x00000000
mv_err_intr: port 0 error; EDMA err cause: 0x00000008 SERR: 0x00000000
mv_phy_reset: ENTER, port 0, mmio 0xf8a22000
mv_phy_reset: Done. Now calling __sata_phy_reset()


Badness in __sata_phy_reset at drivers/scsi/libata-core.c:1413

[<f8961dc8>] __sata_phy_reset+0x75/0x12e [libata]
[<f887566e>] mv_phy_reset+0xe8/0x175 [sata_mv]
[<f8875449>] mv_host_intr+0xf1/0x187 [sata_mv]
[<f887555e>] mv_interrupt+0x7f/0xa7 [sata_mv]
[<c010745e>] handle_IRQ_event+0x25/0x4f
[<c01079be>] do_IRQ+0x11c/0x1ae
=======================
[<c02d1c88>] common_interrupt+0x18/0x20
[<c012007b>] dup_task_struct+0x71/0xc0
[<c0111714>] delay_tsc+0x9/0x13
[<c01c18d9>] __delay+0x9/0xa
[<f8875652>] mv_phy_reset+0xcc/0x175 [sata_mv]
[<c0107e9e>] setup_irq+0xae/0xb7
[<f88754df>] mv_interrupt+0x0/0xa7 [sata_mv]
[<f8961ce1>] ata_bus_probe+0xe/0x7b [libata]
[<f8963fd4>] ata_device_add+0x16c/0x1e8 [libata]
[<f8875abf>] mv_init_one+0x1f8/0x235 [sata_mv]
[<c01c744d>] pci_device_probe_static+0x2a/0x3d
[<c01c747b>] __pci_device_probe+0x1b/0x2c
[<c01c74a7>] pci_device_probe+0x1b/0x2d
[<c021d734>] bus_match+0x27/0x45
[<c021d7fd>] driver_attach+0x37/0x66
[<c021dbbb>] bus_add_driver+0x78/0x99
[<c01c764e>] pci_register_driver+0x6e/0x8a
[<f881b00a>] mv_init+0xa/0x15 [sata_mv]
[<c0137c05>] sys_init_module+0x116/0x238
[<c02d12cb>] syscall_call+0x7/0xb
bad: scheduling while atomic!
[<c02ced41>] schedule+0x2d/0x87a
[<c011e1ae>] scheduler_tick+0x3e/0x3e5
[<c0122bba>] profile_hook+0x1b/0x26
[<c0129741>] __mod_timer+0x101/0x10b
[<c02cfa90>] schedule_timeout+0xd3/0xee
[<c0129feb>] process_timeout+0x0/0x5
[<f8875082>] mv_scr_read+0xf/0x54 [sata_mv]
[<c012a562>] msleep+0x4f/0x55
[<f8961dfb>] __sata_phy_reset+0xa8/0x12e [libata]
[<f887566e>] mv_phy_reset+0xe8/0x175 [sata_mv]
[<f8875449>] mv_host_intr+0xf1/0x187 [sata_mv]
[<f887555e>] mv_interrupt+0x7f/0xa7 [sata_mv]
[<c010745e>] handle_IRQ_event+0x25/0x4f
[<c01079be>] do_IRQ+0x11c/0x1ae
=======================
[<c02d1c88>] common_interrupt+0x18/0x20
[<c012007b>] dup_task_struct+0x71/0xc0
[<c0111714>] delay_tsc+0x9/0x13
[<c01c18d9>] __delay+0x9/0xa
[<f8875652>] mv_phy_reset+0xcc/0x175 [sata_mv]
[<c0107e9e>] setup_irq+0xae/0xb7
[<f88754df>] mv_interrupt+0x0/0xa7 [sata_mv]
[<f8961ce1>] ata_bus_probe+0xe/0x7b [libata]
[<f8963fd4>] ata_device_add+0x16c/0x1e8 [libata]
[<f8875abf>] mv_init_one+0x1f8/0x235 [sata_mv]
[<c01c744d>] pci_device_probe_static+0x2a/0x3d
[<c01c747b>] __pci_device_probe+0x1b/0x2c
[<c01c74a7>] pci_device_probe+0x1b/0x2d
[<c021d734>] bus_match+0x27/0x45
[<c021d7fd>] driver_attach+0x37/0x66
[<c021dbbb>] bus_add_driver+0x78/0x99
[<c01c764e>] pci_register_driver+0x6e/0x8a
[<f881b00a>] mv_init+0xa/0x15 [sata_mv]
[<c0137c05>] sys_init_module+0x116/0x238
[<c02d12cb>] syscall_call+0x7/0xb

after which the system is dead.

> Either way, mv_phy_reset() is called from mv_err_intr() which
> doesn't appear in either of the stack dumps above.

It appears now, both in the debug messages and on the stack (but not
in the success case).

> -do the phy_reset part of error recovery after returning from
> interrupt handler?

I might be completely off here, but what the 3c59x network driver does
for the case where a MII link is used is to start a timer which checks
the state of the link, async from the init routine, which is very
similar in my understanding to your intention expressed above. This
scheme works fine for network...

Please note that I also exposed another problem in my previous
message: after 'rmmod sata_mv', the controller can still generate
interrupts, f.e. when a drive is attached.

--
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan....@IWR.Uni-Heidelberg.De

-

Tyler

unread,
Sep 27, 2005, 1:37:24 AM9/27/05
to Brett Russ, Jeff Garzik, linu...@vger.kernel.org, linux-...@vger.kernel.org
> To unsubscribe from this list: send the line "unsubscribe linux-ide" in

> the body of a message to majo...@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
-------- Original Message --------
From: - Sun Sep 25 07:30:35 2005
Return-Path: <dek...@pandora.be>
Delivered-To: 1-...@dtbb.net
(Postfix) with ESMTP id 0C44938138 for <p...@dtbb.net>; Sun, 25 Sep
2005 16:25:31 +0200 (CEST)
Message-ID: <4336B356...@pandora.be>
Date: Sun, 25 Sep 2005 16:25:26 +0200
From: Jeroen <dek...@pandora.be>
To: p...@dtbb.net
Subject: Re: [PATCH 2.6.13] libata: Marvell SATA support (PIO mode)
X-Antivirus: AVG for E-mail 7.0.344 [267.11.6]

Hi,

I have just tested you patch and i can confirm that is work with the
adaptec 1420SA. If u would like any further information feel free to
contact me.

thanks

Jeroen

--
No virus found in this outgoing message.
Checked by AVG Anti-Virus.
Version: 7.0.344 / Virus Database: 267.11.7/112 - Release Date: 9/26/2005

0 new messages