linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
@ 2005-07-28  1:36 Tejun Heo
  2005-07-28 20:27 ` Jeff Garzik
  0 siblings, 1 reply; 13+ messages in thread
From: Tejun Heo @ 2005-07-28  1:36 UTC (permalink / raw)
  To: Jeff Garzik, linux-ide

 Hello, Jeff.

 This is rewritten sil24 driver against v2.6.13-rc3.  It seems to work
and am currently running stress test on it (random raw read of
concurrency 4, repeatitive mount/copy/checksup/unmount).  I'll keep
running stress test for at least 12 hours and let you know if
something goes wrong.  I've also tested basic error handling and it
seems to work.

 Thanks.

Signed-off-by: Tejun Heo <htejun@gmail.com>

diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -499,6 +499,14 @@ config SCSI_SATA_SIL
 
 	  If unsure, say N.
 
+config SCSI_SATA_SIL24
+	tristate "Silicon Image 3124/3132 SATA support"
+	depends on SCSI_SATA && PCI && EXPERIMENTAL
+	help
+	  This option enables support for Silicon Image 3124/3132 Serial ATA.
+
+	  If unsure, say N.
+
 config SCSI_SATA_SIS
 	tristate "SiS 964/180 SATA support"
 	depends on SCSI_SATA && PCI && EXPERIMENTAL
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -126,6 +126,7 @@ obj-$(CONFIG_SCSI_ATA_PIIX)	+= libata.o 
 obj-$(CONFIG_SCSI_SATA_PROMISE)	+= libata.o sata_promise.o
 obj-$(CONFIG_SCSI_SATA_QSTOR)	+= libata.o sata_qstor.o
 obj-$(CONFIG_SCSI_SATA_SIL)	+= libata.o sata_sil.o
+obj-$(CONFIG_SCSI_SATA_SIL24)	+= libata.o sata_sil24.o
 obj-$(CONFIG_SCSI_SATA_VIA)	+= libata.o sata_via.o
 obj-$(CONFIG_SCSI_SATA_VITESSE)	+= libata.o sata_vsc.o
 obj-$(CONFIG_SCSI_SATA_SIS)	+= libata.o sata_sis.o
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
new file mode 100644
--- /dev/null
+++ b/drivers/scsi/sata_sil24.c
@@ -0,0 +1,786 @@
+/*
+ * sata_sil24.c - Driver for Silicon Image 3124/3132 SATA-2 controllers
+ *
+ * Copyright 2005  Tejun Heo
+ *
+ * Based on preview driver from Silicon Image.
+ *
+ * NOTE: No NCQ/ATAPI support yet.  The preview driver didn't support
+ * NCQ nor ATAPI, and, unfortunately, I couldn't find out how to make
+ * those work.  Enabling those shouldn't be difficult.  Basic
+ * structure is all there (in libata-dev tree).  If you have any
+ * information about this hardware, please contact me or linux-ide.
+ * Info is needed on...
+ *
+ * - How to issue tagged commands and turn on sactive on issue accordingly.
+ * - Where to put an ATAPI command and how to tell the device to send it.
+ * - How to enable/use 64bit.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <scsi/scsi_host.h>
+#include "scsi.h"
+#include <linux/libata.h>
+#include <asm/io.h>
+
+#define DRV_NAME	"sata_sil24"
+#define DRV_VERSION	"0.20"	/* Silicon Image's preview driver was 0.10 */
+
+#define NR_PORTS	4
+
+/*
+ * Port request block (PRB) 32 bytes
+ */
+struct sil24_prb {
+	u16	ctrl;
+	u16	prot;
+	u32	rx_cnt;
+	u8	fis[6 * 4];
+};
+
+/*
+ * Scatter gather entry (SGE) 16 bytes
+ */
+struct sil24_sge {
+	u64	addr;
+	u32	cnt;
+	u32	flags;
+};
+
+/*
+ * Port multiplier
+ */
+struct sil24_port_multiplier {
+	u32	diag;
+	u32	sactive;
+};
+
+enum {
+	/*
+	 * Global controller registers (128 bytes @ BAR0)
+	 */
+		/* 32 bit regs */
+	HOST_SLOT_STAT		= 0x00, /* 32 bit slot stat * 4 */
+	HOST_CTRL		= 0x40,
+	HOST_IRQ_STAT		= 0x44,
+	HOST_PHY_CFG		= 0x48,
+	HOST_BIST_CTRL		= 0x50,
+	HOST_BIST_PTRN		= 0x54,
+	HOST_BIST_STAT		= 0x58,
+	HOST_MEM_BIST_STAT	= 0x5c,
+	HOST_FLASH_CMD		= 0x70,
+		/* 8 bit regs */
+	HOST_FLASH_DATA		= 0x74,
+	HOST_TRANSITION_DETECT	= 0x75,
+	HOST_GPIO_CTRL		= 0x76,
+	HOST_I2C_ADDR		= 0x78, /* 32 bit */
+	HOST_I2C_DATA		= 0x7c,
+	HOST_I2C_XFER_CNT	= 0x7e,
+	HOST_I2C_CTRL		= 0x7f,
+
+	/* HOST_SLOT_STAT bits */
+	HOST_SSTAT_ATTN		= (1 << 31),
+
+	/*
+	 * Port registers
+	 * (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2)
+	 */
+	PORT_REGS_SIZE		= 0x2000,
+	PORT_PRB		= 0x0000, /* (32 bytes PRB + 16 bytes SGEs * 6) * 31 (3968 bytes) */
+		/* TF is overlayed w/ PRB regs in the preview driver,
+		 * but it doesn't seem to work. */
+	PORT_TF			= 0x0000,
+
+	PORT_PM			= 0x0f80, /* 8 bytes PM * 16 (128 bytes) */
+		/* 32 bit regs */
+	PORT_CTRL_STAT		= 0x1000, /* write:ctrl, read:stat */
+	PORT_CTRL_CLR		= 0x1004,
+	PORT_IRQ_STAT		= 0x1008,
+	PORT_IRQ_ENABLE_SET	= 0x1010,
+	PORT_IRQ_ENABLE_CLR	= 0x1014,
+	PORT_ACTIVATE_UPPER_ADDR= 0x101c,
+	PORT_EXEC_FIFO		= 0x1020,
+	PORT_CMD_ERR		= 0x1024,
+	PORT_FIS_CFG		= 0x1028,
+	PORT_FIFO_THRES		= 0x102c,
+		/* 16 bit regs */
+	PORT_DECODE_ERR_CNT	= 0x1040,
+	PORT_DECODE_ERR_THRESH	= 0x1042,
+	PORT_CRC_ERR_CNT	= 0x1044,
+	PORT_CRC_ERR_THRESH	= 0x1046,
+	PORT_HSHK_ERR_CNT	= 0x1048,
+	PORT_HSHK_ERR_THRESH	= 0x104a,
+		/* 32 bit regs */
+	PORT_PHY_CFG		= 0x1050,
+	PORT_SLOT_STAT		= 0x1800,
+	PORT_CMD_ACTIVATE	= 0x1c00, /* 64 bit cmd activate * 31 (248 bytes) */
+	PORT_EXEC_DIAG		= 0x1e00, /* 32bit exec diag * 16 (64 bytes, 0-10 used on 3124) */
+	PORT_PSD_DIAG		= 0x1e40, /* 32bit psd diag * 16 (64 bytes, 0-8 used on 3124) */
+	PORT_SCONTROL		= 0x1f00,
+	PORT_SSTATUS		= 0x1f04,
+	PORT_SERROR		= 0x1f08,
+	PORT_SACTIVE		= 0x1f0c,
+
+	/* PORT_CTRL_STAT bits */
+	PORT_CS_PORT_RST	= (1 << 0), /* port reset */
+	PORT_CS_DEV_RST		= (1 << 1), /* device reset */
+	PORT_CS_INIT		= (1 << 2), /* port initialize */
+	PORT_CS_IRQ_WOC		= (1 << 3), /* interrupt write one to clear */
+	PORT_CS_RESUME		= (1 << 4), /* port resume */
+	PORT_CS_32BIT_ACTV	= (1 << 5), /* 32-bit activation */
+	PORT_CS_PM_EN		= (1 << 6), /* port multiplier enable */
+	PORT_CS_RDY		= (1 << 7), /* port ready to accept commands */
+
+	/* PORT_IRQ_STAT/ENABLE_SET/CLR */
+	/* bits[11:0] are masked */
+	PORT_IRQ_COMPLETE	= (1 << 0), /* command(s) completed */
+	PORT_IRQ_ERROR		= (1 << 1), /* command execution error */
+	PORT_IRQ_PORTRDY_CHG	= (1 << 2), /* port ready change */
+	PORT_IRQ_PWR_CHG	= (1 << 3), /* power management change */
+	PORT_IRQ_PHYRDY_CHG	= (1 << 4), /* PHY ready change */
+	PORT_IRQ_COMWAKE	= (1 << 5), /* COMWAKE received */
+	PORT_IRQ_UNK_FIS	= (1 << 6), /* Unknown FIS received */
+	PORT_IRQ_SDB_FIS	= (1 << 11), /* SDB FIS received */
+
+	/* bits[27:16] are unmasked (raw) */
+	PORT_IRQ_RAW_SHIFT	= 16,
+	PORT_IRQ_MASKED_MASK	= 0x7ff,
+	PORT_IRQ_RAW_MASK	= (0x7ff << PORT_IRQ_RAW_SHIFT),
+
+	/* ENABLE_SET/CLR specific, intr steering - 2 bit field */
+	PORT_IRQ_STEER_SHIFT	= 30,
+	PORT_IRQ_STEER_MASK	= (3 << PORT_IRQ_STEER_SHIFT),
+
+	/* PORT_CMD_ERR constants */
+	PORT_CERR_DEV		= 1, /* Error bit in D2H Register FIS */
+	PORT_CERR_SDB		= 2, /* Error bit in SDB FIS */
+	PORT_CERR_DATA		= 3, /* Error in data FIS not detected by dev */
+	PORT_CERR_SEND		= 4, /* Initial cmd FIS transmission failure */
+	PORT_CERR_INCONSISTENT	= 5, /* Protocol mismatch */
+	PORT_CERR_DIRECTION	= 6, /* Data direction mismatch */
+	PORT_CERR_UNDERRUN	= 7, /* Ran out of SGEs while writing */
+	PORT_CERR_OVERRUN	= 8, /* Ran out of SGEs while reading */
+	PORT_CERR_PKT_PROT	= 11, /* DIR invalid in 1st PIO setup of ATAPI */
+	PORT_CERR_SGT_BOUNDARY	= 16, /* PLD ecode 00 - SGT not on qword boundary */
+	PORT_CERR_SGT_TGTABRT	= 17, /* PLD ecode 01 - target abort */
+	PORT_CERR_SGT_MSTABRT	= 18, /* PLD ecode 10 - master abort */
+	PORT_CERR_SGT_PCIPERR	= 19, /* PLD ecode 11 - PCI parity err while fetching SGT */
+	PORT_CERR_CMD_BOUNDARY	= 24, /* ctrl[15:13] 001 - PRB not on qword boundary */
+	PORT_CERR_CMD_TGTABRT	= 25, /* ctrl[15:13] 010 - target abort */
+	PORT_CERR_CMD_MSTABRT	= 26, /* ctrl[15:13] 100 - master abort */
+	PORT_CERR_CMD_PCIPERR	= 27, /* ctrl[15:13] 110 - PCI parity err while fetching PRB */
+	PORT_CERR_XFR_UNDEF	= 32, /* PSD ecode 00 - undefined */
+	PORT_CERR_XFR_TGTABRT	= 33, /* PSD ecode 01 - target abort */
+	PORT_CERR_XFR_MSGABRT	= 34, /* PSD ecode 10 - master abort */
+	PORT_CERR_XFR_PCIPERR	= 35, /* PSD ecode 11 - PCI prity err during transfer */
+	PORT_CERR_SENDSERVICE	= 36, /* FIS received whiel sending service */
+
+	/*
+	 * Other constants
+	 */
+	SGE_TRM			= (1 << 31), /* Last SGE in chain */
+	PRB_SOFT_RST		= (1 << 7),  /* Soft reset request (ign BSY?) */
+
+	/* board id */
+	BID_SIL3124		= 0,
+	BID_SIL3132		= 1,
+
+	IRQ_STAT_4PORTS		= 0xf,
+};
+
+struct sil24_cmd_block {
+	struct sil24_prb prb;
+	struct sil24_sge sge[LIBATA_MAX_PRD];
+};
+
+/*
+ * ap->private_data
+ *
+ * The preview driver always returned 0 for status.  We emulate it
+ * here from the previous interrupt.
+ */
+struct sil24_port_priv {
+	void *port;
+	struct sil24_cmd_block *cmd_block;	/* 32 cmd blocks */
+	dma_addr_t cmd_block_dma;		/* DMA base addr for them */
+};
+
+/* ap->host_set->private_data */
+struct sil24_host_priv {
+	void *host_base;	/* global controller control (128 bytes @BAR0) */
+	void *port_base;	/* port registers (4 * 8192 bytes @BAR2) */
+};
+
+static u8 sil24_check_status(struct ata_port *ap);
+static u8 sil24_check_err(struct ata_port *ap);
+static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg);
+static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val);
+static void sil24_phy_reset(struct ata_port *ap);
+static void sil24_qc_prep(struct ata_queued_cmd *qc);
+static int sil24_qc_issue(struct ata_queued_cmd *qc);
+static void sil24_irq_clear(struct ata_port *ap);
+static void sil24_eng_timeout(struct ata_port *ap);
+static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static int sil24_port_start(struct ata_port *ap);
+static void sil24_port_stop(struct ata_port *ap);
+static void sil24_host_stop(struct ata_host_set *host_set);
+static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
+
+static struct pci_device_id sil24_pci_tbl[] = {
+	{ 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 },
+	{ 0x1095, 0x3132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3132 },
+};
+
+static struct pci_driver sil24_pci_driver = {
+	.name			= DRV_NAME,
+	.id_table		= sil24_pci_tbl,
+	.probe			= sil24_init_one,
+	.remove			= ata_pci_remove_one, /* safe? */
+};
+
+static Scsi_Host_Template sil24_sht = {
+	.module			= THIS_MODULE,
+	.name			= DRV_NAME,
+	.ioctl			= ata_scsi_ioctl,
+	.queuecommand		= ata_scsi_queuecmd,
+	.eh_strategy_handler	= ata_scsi_error,
+	.can_queue		= ATA_DEF_QUEUE,
+	.this_id		= ATA_SHT_THIS_ID,
+	.sg_tablesize		= LIBATA_MAX_PRD,
+	.max_sectors		= ATA_MAX_SECTORS,
+	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
+	.emulated		= ATA_SHT_EMULATED,
+	.use_clustering		= ATA_SHT_USE_CLUSTERING,
+	.proc_name		= DRV_NAME,
+	.dma_boundary		= ATA_DMA_BOUNDARY,
+	.slave_configure	= ata_scsi_slave_config,
+	.bios_param		= ata_std_bios_param,
+	.ordered_flush		= 1, /* NCQ not supported yet */
+};
+
+static struct ata_port_operations sil24_ops = {
+	.port_disable		= ata_port_disable,
+
+	.check_status		= sil24_check_status,
+	.check_altstatus	= sil24_check_status,
+	.check_err		= sil24_check_err,
+	.dev_select		= ata_noop_dev_select,
+
+	.phy_reset		= sil24_phy_reset,
+
+	.qc_prep		= sil24_qc_prep,
+	.qc_issue		= sil24_qc_issue,
+
+	.eng_timeout		= sil24_eng_timeout,
+
+	.irq_handler		= sil24_interrupt,
+	.irq_clear		= sil24_irq_clear,
+
+	.scr_read		= sil24_scr_read,
+	.scr_write		= sil24_scr_write,
+
+	.port_start		= sil24_port_start,
+	.port_stop		= sil24_port_stop,
+	.host_stop		= sil24_host_stop,
+};
+
+static struct ata_port_info sil24_port_info[] = {
+	/* sil_3124 */
+	{
+		.sht		= &sil24_sht,
+		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+				  ATA_FLAG_PIO_DMA,
+		.pio_mask	= 0x1f,			/* pio0-4 */
+		.mwdma_mask	= 0x07,			/* mwdma0-2 */
+		.udma_mask	= 0x3f,			/* udma0-5 */
+		.port_ops	= &sil24_ops,
+	},
+	/* sil_3132 */ 
+	{
+		.sht		= &sil24_sht,
+		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+				  ATA_FLAG_PIO_DMA,
+		.pio_mask	= 0x1f,			/* pio0-4 */
+		.mwdma_mask	= 0x07,			/* mwdma0-2 */
+		.udma_mask	= 0x3f,			/* udma0-5 */
+		.port_ops	= &sil24_ops,
+	},
+};
+
+static u8 sil24_check_status(struct ata_port *ap)
+{
+	return ATA_DRDY;
+}
+
+static u8 sil24_check_err(struct ata_port *ap)
+{
+	return 0;
+}
+
+static int sil24_scr_map[] = {
+	[SCR_CONTROL]	= 0,
+	[SCR_STATUS]	= 1,
+	[SCR_ERROR]	= 2,
+	[SCR_ACTIVE]	= 3,
+};
+
+static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg)
+{
+	void *scr_addr = (void *)ap->ioaddr.scr_addr;
+	if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
+		void *addr;
+		addr = scr_addr + sil24_scr_map[sc_reg] * 4;
+		return readl(scr_addr + sil24_scr_map[sc_reg] * 4);
+	}
+	return 0xffffffffU;
+}
+
+static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val)
+{
+	void *scr_addr = (void *)ap->ioaddr.scr_addr;
+	if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
+		void *addr;
+		addr = scr_addr + sil24_scr_map[sc_reg] * 4;
+		writel(val, scr_addr + sil24_scr_map[sc_reg] * 4);
+	}
+}
+
+static void sil24_phy_reset(struct ata_port *ap)
+{
+	__sata_phy_reset(ap);
+	/*
+	 * No ATAPI yet.  Just unconditionally indicate ATA device.
+	 * If ATAPI device is attached, it will fail ATA_CMD_ID_ATA
+	 * and libata core will ignore the device.
+	 */
+	if (!(ap->flags & ATA_FLAG_PORT_DISABLED))
+		ap->device[0].class = ATA_DEV_ATA;
+}
+
+static inline void sil24_fill_sg(struct ata_queued_cmd *qc,
+				 struct sil24_cmd_block *cb)
+{
+	struct scatterlist *sg = qc->sg;
+	struct sil24_sge *sge = cb->sge;
+	unsigned i;
+
+	for (i = 0; i < qc->n_elem; i++, sg++, sge++) {
+		sge->addr = cpu_to_le64(sg_dma_address(sg));
+		sge->cnt = cpu_to_le32(sg_dma_len(sg));
+		sge->flags = 0;
+		sge->flags = i < qc->n_elem - 1 ? 0 : cpu_to_le32(SGE_TRM);
+	}
+}
+
+static void sil24_qc_prep(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct sil24_port_priv *pp = ap->private_data;
+	struct sil24_cmd_block *cb = pp->cmd_block + qc->tag;
+	struct sil24_prb *prb = &cb->prb;
+
+	switch (qc->tf.protocol) {
+	case ATA_PROT_PIO:
+	case ATA_PROT_DMA:
+	case ATA_PROT_NODATA:
+		break;
+	default:
+		/* ATAPI isn't supported yet */
+		BUG();
+	}
+
+	ata_tf_to_fis(&qc->tf, prb->fis, 0);
+
+	if (qc->flags & ATA_QCFLAG_DMAMAP)
+		sil24_fill_sg(qc, cb);
+}
+
+static int sil24_qc_issue(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct sil24_port_priv *pp = ap->private_data;
+	dma_addr_t paddr = pp->cmd_block_dma + qc->tag * sizeof(*pp->cmd_block);
+
+	writel((u32)paddr, pp->port + PORT_CMD_ACTIVATE);
+	return 0;
+}
+
+static void sil24_irq_clear(struct ata_port *ap)
+{
+	/* unused */
+}
+
+static void sil24_reset_controller(struct ata_port *ap)
+{
+	struct sil24_port_priv *pp = ap->private_data;
+	void *port = pp->port;
+	int cnt;
+	u32 tmp;
+
+	printk(KERN_NOTICE DRV_NAME
+	       " ata%u: resetting controller...\n", ap->id);
+
+	/* Reset controller state.  Is this correct? */
+	writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
+	readl(port + PORT_CTRL_STAT);	/* sync */
+
+	/* Max ~100ms */
+	for (cnt = 0; cnt < 1000; cnt++) {
+		udelay(100);
+		tmp = readl(port + PORT_CTRL_STAT);
+		if (!(tmp & PORT_CS_DEV_RST))
+			break;
+	}
+	if (tmp & PORT_CS_DEV_RST)
+		printk(KERN_ERR DRV_NAME
+		       " ata%u: failed to reset controller\n", ap->id);
+}
+
+static void sil24_eng_timeout(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc;
+
+	qc = ata_qc_from_tag(ap, ap->active_tag);
+	if (!qc) {
+		printk(KERN_ERR "ata%u: BUG: tiemout without command\n",
+		       ap->id);
+		return;
+	}
+
+	/*
+	 * hack alert!  We cannot use the supplied completion
+	 * function from inside the ->eh_strategy_handler() thread.
+	 * libata is the only user of ->eh_strategy_handler() in
+	 * any kernel, so the default scsi_done() assumes it is
+	 * not being called from the SCSI EH.
+	 */
+	printk(KERN_ERR "ata%u: command timeout\n", ap->id);
+	qc->scsidone = scsi_finish_command;
+	ata_qc_complete(qc, ATA_ERR);
+
+	sil24_reset_controller(ap);
+}
+
+static inline void sil24_host_intr(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
+	struct sil24_port_priv *pp = ap->private_data;
+	void *port = pp->port;
+	u32 slot_stat;
+
+	slot_stat = readl(port + PORT_SLOT_STAT);
+	if (!(slot_stat & HOST_SSTAT_ATTN)) {
+		if (qc)
+			ata_qc_complete(qc, 0);
+	} else {
+		u32 irq_stat, cmd_err, sstatus, serror;
+
+		irq_stat = readl(port + PORT_IRQ_STAT);
+		cmd_err = readl(port + PORT_CMD_ERR);
+		sstatus = readl(port + PORT_SSTATUS);
+		serror = readl(port + PORT_SERROR);
+
+		/* Clear IRQ/errors */
+		writel(irq_stat, port + PORT_IRQ_STAT);
+		if (cmd_err)
+			writel(cmd_err, port + PORT_CMD_ERR);
+		if (serror)
+			writel(serror, port + PORT_SERROR);
+
+		printk(KERN_ERR DRV_NAME " ata%u: error interrupt on port%d\n"
+		       "  stat=0x%x irq=0x%x cmd_err=%d sstatus=0x%x serror=0x%x\n",
+		       ap->id, ap->port_no, slot_stat, irq_stat, cmd_err, sstatus, serror);
+
+		if (qc)
+			ata_qc_complete(qc, ATA_ERR);
+
+		sil24_reset_controller(ap);
+	}
+}
+
+static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+{
+	struct ata_host_set *host_set = dev_instance;
+	struct sil24_host_priv *hpriv = host_set->private_data;
+	unsigned handled = 0;
+	u32 status;
+	int i;
+
+	status = readl(hpriv->host_base + HOST_IRQ_STAT);
+
+	if (!(status & IRQ_STAT_4PORTS))
+		goto out;
+
+	spin_lock(&host_set->lock);
+
+	for (i = 0; i < host_set->n_ports; i++)
+		if (status & (1 << i)) {
+			struct ata_port *ap = host_set->ports[i];
+			if (ap && !(ap->flags & ATA_FLAG_PORT_DISABLED))
+				sil24_host_intr(host_set->ports[i]);
+			else {
+				u32 tmp;
+				printk(KERN_WARNING DRV_NAME
+				       ": spurious interrupt from port %d\n", i);
+				tmp = readl(hpriv->host_base + HOST_CTRL);
+				tmp &= ~(1 << i);
+				writel(tmp, hpriv->host_base + HOST_CTRL);
+			}
+			handled++;
+		}
+
+	spin_unlock(&host_set->lock);
+ out:
+	return IRQ_RETVAL(handled);
+}
+
+static int sil24_port_start(struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	struct sil24_host_priv *hpriv = ap->host_set->private_data;
+	struct sil24_port_priv *pp;
+	struct sil24_cmd_block *cb;
+	size_t cb_size = sizeof(*cb);
+	dma_addr_t cb_dma;
+
+	pp = kmalloc(sizeof(*pp), GFP_KERNEL);
+	if (!pp)
+		return -ENOMEM;
+	memset(pp, 0, sizeof(*pp));
+
+	cb = dma_alloc_coherent(dev, cb_size, &cb_dma, GFP_KERNEL);
+	if (!cb) {
+		kfree(pp);
+		return -ENOMEM;
+	}
+	memset(cb, 0, cb_size);
+
+	pp->port = hpriv->port_base + ap->port_no * PORT_REGS_SIZE;
+	pp->cmd_block = cb;
+	pp->cmd_block_dma = cb_dma;
+
+	ap->private_data = pp;
+
+	return 0;
+}
+
+static void sil24_port_stop(struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	struct sil24_port_priv *pp = ap->private_data;
+	size_t cb_size = sizeof(*pp->cmd_block);
+
+	dma_free_coherent(dev, cb_size, pp->cmd_block, pp->cmd_block_dma);
+	kfree(pp);
+}
+
+static void sil24_host_stop(struct ata_host_set *host_set)
+{
+	struct sil24_host_priv *hpriv = host_set->private_data;
+
+	iounmap(hpriv->host_base);
+	iounmap(hpriv->port_base);
+	kfree(hpriv);
+}
+
+static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	static int printed_version = 0;
+	unsigned int board_id = (unsigned int)ent->driver_data;
+	struct ata_probe_ent *probe_ent = NULL;
+	struct sil24_host_priv *hpriv = NULL;
+	void *host_base = NULL, *port_base = NULL;
+	int i, rc;
+
+	if (!printed_version++)
+		printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+	rc = pci_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	rc = pci_request_regions(pdev, DRV_NAME);
+	if (rc)
+		goto out_disable;
+
+	rc = -ENOMEM;
+	/* ioremap mmio registers */
+	host_base = ioremap(pci_resource_start(pdev, 0),
+			    pci_resource_len(pdev, 0));
+	if (!host_base)
+		goto out_free;
+	port_base = ioremap(pci_resource_start(pdev, 2),
+			    pci_resource_len(pdev, 2));
+	if (!port_base)
+		goto out_free;
+
+	/* allocate & init probe_ent and hpriv */
+	probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+	if (!probe_ent)
+		goto out_free;
+
+	hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
+	if (!hpriv)
+		goto out_free;
+
+	memset(probe_ent, 0, sizeof(*probe_ent));
+	probe_ent->dev = pci_dev_to_dev(pdev);
+	INIT_LIST_HEAD(&probe_ent->node);
+
+	probe_ent->sht		= sil24_port_info[board_id].sht;
+	probe_ent->host_flags	= sil24_port_info[board_id].host_flags;
+	probe_ent->pio_mask	= sil24_port_info[board_id].pio_mask;
+	probe_ent->udma_mask	= sil24_port_info[board_id].udma_mask;
+	probe_ent->port_ops	= sil24_port_info[board_id].port_ops;
+	probe_ent->n_ports	= (board_id == BID_SIL3124) ? 4 : 2;
+
+	probe_ent->irq = pdev->irq;
+	probe_ent->irq_flags = SA_SHIRQ;
+	probe_ent->mmio_base = port_base;
+	probe_ent->private_data = hpriv;
+
+	memset(hpriv, 0, sizeof(*hpriv));
+	hpriv->host_base = host_base;
+	hpriv->port_base = port_base;
+
+	/*
+	 * Configure the device
+	 */
+	/*
+	 * FIXME: This device is certainly 64-bit capable.  We just
+	 * don't know how to use it.  After fixing 32bit activation in
+	 * this function, enable 64bit masks here.
+	 */
+	rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+	if (rc) {
+		printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n",
+		       pci_name(pdev));
+		goto out_free;
+	}
+	rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+	if (rc) {
+		printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable failed\n",
+		       pci_name(pdev));
+		goto out_free;
+	}
+
+	/* GPIO off */
+	writel(0, host_base + HOST_FLASH_CMD);
+
+	/* Mask interrupts during initialization */
+	writel(0, host_base + HOST_CTRL);
+
+	for (i = 0; i < probe_ent->n_ports; i++) {
+		void *port = port_base + i * PORT_REGS_SIZE;
+		unsigned long portu = (unsigned long)port;
+		u32 tmp;
+		int cnt;
+
+		probe_ent->port[i].cmd_addr = portu + PORT_TF;
+		probe_ent->port[i].ctl_addr = portu + PORT_TF + 0xa;
+		probe_ent->port[i].altstatus_addr = portu + PORT_TF + 0xa;
+		probe_ent->port[i].scr_addr = portu + PORT_SCONTROL;
+
+		ata_std_ports(&probe_ent->port[i]);
+
+		/* Initial PHY setting */
+		writel(0x20c, port + PORT_PHY_CFG);
+
+		/* Clear port RST */
+		tmp = readl(port + PORT_CTRL_STAT);
+		if (tmp & PORT_CS_PORT_RST) {
+			writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
+			readl(port + PORT_CTRL_STAT);	/* sync */
+			for (cnt = 0; cnt < 10; cnt++) {
+				msleep(10);
+				tmp = readl(port + PORT_CTRL_STAT);
+				if (!(tmp & PORT_CS_PORT_RST))
+					break;
+			}
+			if (tmp & PORT_CS_PORT_RST)
+				printk(KERN_ERR DRV_NAME
+				       "(%s): failed to clear port RST\n",
+				       pci_name(pdev));
+		}
+
+		/* Zero error counters. */
+		writel(0x8000, port + PORT_DECODE_ERR_THRESH);
+		writel(0x8000, port + PORT_CRC_ERR_THRESH);
+		writel(0x8000, port + PORT_HSHK_ERR_THRESH);
+		writel(0x0000, port + PORT_DECODE_ERR_CNT);
+		writel(0x0000, port + PORT_CRC_ERR_CNT);
+		writel(0x0000, port + PORT_HSHK_ERR_CNT);
+
+		/* FIXME: 32bit activation? */
+		writel(0, port + PORT_ACTIVATE_UPPER_ADDR);
+		writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_STAT);
+
+		/* Configure interrupts */
+		writel(0xffff, port + PORT_IRQ_ENABLE_CLR);
+		writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | PORT_IRQ_SDB_FIS,
+		       port + PORT_IRQ_ENABLE_SET);
+
+		/* Clear interrupts */
+		writel(0x0fff0fff, port + PORT_IRQ_STAT);
+		writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
+	}
+
+	/* Turn on interrupts */
+	writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
+
+	pci_set_master(pdev);
+
+	ata_device_add(probe_ent);
+
+	kfree(probe_ent);
+	return 0;
+
+ out_free:
+	if (host_base)
+		iounmap(host_base);
+	if (port_base)
+		iounmap(port_base);
+	kfree(probe_ent);
+	kfree(hpriv);
+	pci_release_regions(pdev);
+ out_disable:
+	pci_disable_device(pdev);
+	return rc;
+}
+
+static int __init sil24_init(void)
+{
+	return pci_module_init(&sil24_pci_driver);
+}
+
+static void __exit sil24_exit(void)
+{
+	pci_unregister_driver(&sil24_pci_driver);
+}
+
+MODULE_AUTHOR("Tejun Heo");
+MODULE_DESCRIPTION("Silicon Image 3124/3132 SATA low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, sil24_pci_tbl);
+
+module_init(sil24_init);
+module_exit(sil24_exit);

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-07-28  1:36 [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver Tejun Heo
@ 2005-07-28 20:27 ` Jeff Garzik
  2005-07-30  8:17   ` Tejun Heo
  0 siblings, 1 reply; 13+ messages in thread
From: Jeff Garzik @ 2005-07-28 20:27 UTC (permalink / raw)
  To: Tejun Heo; +Cc: linux-ide, Linux Kernel, Carlos Pardo

Tejun Heo wrote:
>  Hello, Jeff.
> 
>  This is rewritten sil24 driver against v2.6.13-rc3.  It seems to work
> and am currently running stress test on it (random raw read of
> concurrency 4, repeatitive mount/copy/checksup/unmount).  I'll keep
> running stress test for at least 12 hours and let you know if
> something goes wrong.  I've also tested basic error handling and it
> seems to work.

I've merged this into the 'sil24' branch of libata-dev.git, and moved 
the original driver from Silicon Image into the 'sil24-original' branch.

So, please submit an incremental patch for any future changes to this 
driver.

Comments below, need a few fixups before pushing upstream, most likely.

Also, a question:  do you have hardware docs?


> diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
> --- a/drivers/scsi/Kconfig
> +++ b/drivers/scsi/Kconfig
> @@ -499,6 +499,14 @@ config SCSI_SATA_SIL
>  
>  	  If unsure, say N.
>  
> +config SCSI_SATA_SIL24
> +	tristate "Silicon Image 3124/3132 SATA support"
> +	depends on SCSI_SATA && PCI && EXPERIMENTAL
> +	help
> +	  This option enables support for Silicon Image 3124/3132 Serial ATA.
> +
> +	  If unsure, say N.
> +
>  config SCSI_SATA_SIS
>  	tristate "SiS 964/180 SATA support"
>  	depends on SCSI_SATA && PCI && EXPERIMENTAL
> diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
> --- a/drivers/scsi/Makefile
> +++ b/drivers/scsi/Makefile
> @@ -126,6 +126,7 @@ obj-$(CONFIG_SCSI_ATA_PIIX)	+= libata.o 
>  obj-$(CONFIG_SCSI_SATA_PROMISE)	+= libata.o sata_promise.o
>  obj-$(CONFIG_SCSI_SATA_QSTOR)	+= libata.o sata_qstor.o
>  obj-$(CONFIG_SCSI_SATA_SIL)	+= libata.o sata_sil.o
> +obj-$(CONFIG_SCSI_SATA_SIL24)	+= libata.o sata_sil24.o
>  obj-$(CONFIG_SCSI_SATA_VIA)	+= libata.o sata_via.o
>  obj-$(CONFIG_SCSI_SATA_VITESSE)	+= libata.o sata_vsc.o
>  obj-$(CONFIG_SCSI_SATA_SIS)	+= libata.o sata_sis.o
> diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/scsi/sata_sil24.c
> @@ -0,0 +1,786 @@
> +/*
> + * sata_sil24.c - Driver for Silicon Image 3124/3132 SATA-2 controllers
> + *
> + * Copyright 2005  Tejun Heo
> + *
> + * Based on preview driver from Silicon Image.
> + *
> + * NOTE: No NCQ/ATAPI support yet.  The preview driver didn't support
> + * NCQ nor ATAPI, and, unfortunately, I couldn't find out how to make
> + * those work.  Enabling those shouldn't be difficult.  Basic
> + * structure is all there (in libata-dev tree).  If you have any
> + * information about this hardware, please contact me or linux-ide.
> + * Info is needed on...
> + *
> + * - How to issue tagged commands and turn on sactive on issue accordingly.
> + * - Where to put an ATAPI command and how to tell the device to send it.
> + * - How to enable/use 64bit.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2, or (at your option) any
> + * later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/blkdev.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/dma-mapping.h>
> +#include <scsi/scsi_host.h>
> +#include "scsi.h"
> +#include <linux/libata.h>
> +#include <asm/io.h>
> +
> +#define DRV_NAME	"sata_sil24"
> +#define DRV_VERSION	"0.20"	/* Silicon Image's preview driver was 0.10 */
> +
> +#define NR_PORTS	4
> +
> +/*
> + * Port request block (PRB) 32 bytes
> + */
> +struct sil24_prb {
> +	u16	ctrl;
> +	u16	prot;
> +	u32	rx_cnt;
> +	u8	fis[6 * 4];
> +};
> +
> +/*
> + * Scatter gather entry (SGE) 16 bytes
> + */
> +struct sil24_sge {
> +	u64	addr;
> +	u32	cnt;
> +	u32	flags;
> +};
> +
> +/*
> + * Port multiplier
> + */
> +struct sil24_port_multiplier {
> +	u32	diag;
> +	u32	sactive;
> +};

I know this is unlikely, but just asking...  you didn't test this with a 
port multiplier, did you?


> +enum {
> +	/*
> +	 * Global controller registers (128 bytes @ BAR0)
> +	 */
> +		/* 32 bit regs */
> +	HOST_SLOT_STAT		= 0x00, /* 32 bit slot stat * 4 */
> +	HOST_CTRL		= 0x40,
> +	HOST_IRQ_STAT		= 0x44,
> +	HOST_PHY_CFG		= 0x48,
> +	HOST_BIST_CTRL		= 0x50,
> +	HOST_BIST_PTRN		= 0x54,
> +	HOST_BIST_STAT		= 0x58,
> +	HOST_MEM_BIST_STAT	= 0x5c,
> +	HOST_FLASH_CMD		= 0x70,
> +		/* 8 bit regs */
> +	HOST_FLASH_DATA		= 0x74,
> +	HOST_TRANSITION_DETECT	= 0x75,
> +	HOST_GPIO_CTRL		= 0x76,
> +	HOST_I2C_ADDR		= 0x78, /* 32 bit */
> +	HOST_I2C_DATA		= 0x7c,
> +	HOST_I2C_XFER_CNT	= 0x7e,
> +	HOST_I2C_CTRL		= 0x7f,
> +
> +	/* HOST_SLOT_STAT bits */
> +	HOST_SSTAT_ATTN		= (1 << 31),
> +
> +	/*
> +	 * Port registers
> +	 * (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2)
> +	 */
> +	PORT_REGS_SIZE		= 0x2000,
> +	PORT_PRB		= 0x0000, /* (32 bytes PRB + 16 bytes SGEs * 6) * 31 (3968 bytes) */
> +		/* TF is overlayed w/ PRB regs in the preview driver,
> +		 * but it doesn't seem to work. */
> +	PORT_TF			= 0x0000,
> +
> +	PORT_PM			= 0x0f80, /* 8 bytes PM * 16 (128 bytes) */
> +		/* 32 bit regs */
> +	PORT_CTRL_STAT		= 0x1000, /* write:ctrl, read:stat */
> +	PORT_CTRL_CLR		= 0x1004,
> +	PORT_IRQ_STAT		= 0x1008,
> +	PORT_IRQ_ENABLE_SET	= 0x1010,
> +	PORT_IRQ_ENABLE_CLR	= 0x1014,
> +	PORT_ACTIVATE_UPPER_ADDR= 0x101c,
> +	PORT_EXEC_FIFO		= 0x1020,
> +	PORT_CMD_ERR		= 0x1024,
> +	PORT_FIS_CFG		= 0x1028,
> +	PORT_FIFO_THRES		= 0x102c,
> +		/* 16 bit regs */
> +	PORT_DECODE_ERR_CNT	= 0x1040,
> +	PORT_DECODE_ERR_THRESH	= 0x1042,
> +	PORT_CRC_ERR_CNT	= 0x1044,
> +	PORT_CRC_ERR_THRESH	= 0x1046,
> +	PORT_HSHK_ERR_CNT	= 0x1048,
> +	PORT_HSHK_ERR_THRESH	= 0x104a,
> +		/* 32 bit regs */
> +	PORT_PHY_CFG		= 0x1050,
> +	PORT_SLOT_STAT		= 0x1800,
> +	PORT_CMD_ACTIVATE	= 0x1c00, /* 64 bit cmd activate * 31 (248 bytes) */
> +	PORT_EXEC_DIAG		= 0x1e00, /* 32bit exec diag * 16 (64 bytes, 0-10 used on 3124) */
> +	PORT_PSD_DIAG		= 0x1e40, /* 32bit psd diag * 16 (64 bytes, 0-8 used on 3124) */
> +	PORT_SCONTROL		= 0x1f00,
> +	PORT_SSTATUS		= 0x1f04,
> +	PORT_SERROR		= 0x1f08,
> +	PORT_SACTIVE		= 0x1f0c,
> +
> +	/* PORT_CTRL_STAT bits */
> +	PORT_CS_PORT_RST	= (1 << 0), /* port reset */
> +	PORT_CS_DEV_RST		= (1 << 1), /* device reset */
> +	PORT_CS_INIT		= (1 << 2), /* port initialize */
> +	PORT_CS_IRQ_WOC		= (1 << 3), /* interrupt write one to clear */
> +	PORT_CS_RESUME		= (1 << 4), /* port resume */
> +	PORT_CS_32BIT_ACTV	= (1 << 5), /* 32-bit activation */
> +	PORT_CS_PM_EN		= (1 << 6), /* port multiplier enable */
> +	PORT_CS_RDY		= (1 << 7), /* port ready to accept commands */
> +
> +	/* PORT_IRQ_STAT/ENABLE_SET/CLR */
> +	/* bits[11:0] are masked */
> +	PORT_IRQ_COMPLETE	= (1 << 0), /* command(s) completed */
> +	PORT_IRQ_ERROR		= (1 << 1), /* command execution error */
> +	PORT_IRQ_PORTRDY_CHG	= (1 << 2), /* port ready change */
> +	PORT_IRQ_PWR_CHG	= (1 << 3), /* power management change */
> +	PORT_IRQ_PHYRDY_CHG	= (1 << 4), /* PHY ready change */
> +	PORT_IRQ_COMWAKE	= (1 << 5), /* COMWAKE received */
> +	PORT_IRQ_UNK_FIS	= (1 << 6), /* Unknown FIS received */
> +	PORT_IRQ_SDB_FIS	= (1 << 11), /* SDB FIS received */
> +
> +	/* bits[27:16] are unmasked (raw) */
> +	PORT_IRQ_RAW_SHIFT	= 16,
> +	PORT_IRQ_MASKED_MASK	= 0x7ff,
> +	PORT_IRQ_RAW_MASK	= (0x7ff << PORT_IRQ_RAW_SHIFT),
> +
> +	/* ENABLE_SET/CLR specific, intr steering - 2 bit field */
> +	PORT_IRQ_STEER_SHIFT	= 30,
> +	PORT_IRQ_STEER_MASK	= (3 << PORT_IRQ_STEER_SHIFT),
> +
> +	/* PORT_CMD_ERR constants */
> +	PORT_CERR_DEV		= 1, /* Error bit in D2H Register FIS */
> +	PORT_CERR_SDB		= 2, /* Error bit in SDB FIS */
> +	PORT_CERR_DATA		= 3, /* Error in data FIS not detected by dev */
> +	PORT_CERR_SEND		= 4, /* Initial cmd FIS transmission failure */
> +	PORT_CERR_INCONSISTENT	= 5, /* Protocol mismatch */
> +	PORT_CERR_DIRECTION	= 6, /* Data direction mismatch */
> +	PORT_CERR_UNDERRUN	= 7, /* Ran out of SGEs while writing */
> +	PORT_CERR_OVERRUN	= 8, /* Ran out of SGEs while reading */
> +	PORT_CERR_PKT_PROT	= 11, /* DIR invalid in 1st PIO setup of ATAPI */
> +	PORT_CERR_SGT_BOUNDARY	= 16, /* PLD ecode 00 - SGT not on qword boundary */
> +	PORT_CERR_SGT_TGTABRT	= 17, /* PLD ecode 01 - target abort */
> +	PORT_CERR_SGT_MSTABRT	= 18, /* PLD ecode 10 - master abort */
> +	PORT_CERR_SGT_PCIPERR	= 19, /* PLD ecode 11 - PCI parity err while fetching SGT */
> +	PORT_CERR_CMD_BOUNDARY	= 24, /* ctrl[15:13] 001 - PRB not on qword boundary */
> +	PORT_CERR_CMD_TGTABRT	= 25, /* ctrl[15:13] 010 - target abort */
> +	PORT_CERR_CMD_MSTABRT	= 26, /* ctrl[15:13] 100 - master abort */
> +	PORT_CERR_CMD_PCIPERR	= 27, /* ctrl[15:13] 110 - PCI parity err while fetching PRB */
> +	PORT_CERR_XFR_UNDEF	= 32, /* PSD ecode 00 - undefined */
> +	PORT_CERR_XFR_TGTABRT	= 33, /* PSD ecode 01 - target abort */
> +	PORT_CERR_XFR_MSGABRT	= 34, /* PSD ecode 10 - master abort */
> +	PORT_CERR_XFR_PCIPERR	= 35, /* PSD ecode 11 - PCI prity err during transfer */
> +	PORT_CERR_SENDSERVICE	= 36, /* FIS received whiel sending service */
> +
> +	/*
> +	 * Other constants
> +	 */
> +	SGE_TRM			= (1 << 31), /* Last SGE in chain */
> +	PRB_SOFT_RST		= (1 << 7),  /* Soft reset request (ign BSY?) */
> +
> +	/* board id */
> +	BID_SIL3124		= 0,
> +	BID_SIL3132		= 1,
> +
> +	IRQ_STAT_4PORTS		= 0xf,
> +};
> +
> +struct sil24_cmd_block {
> +	struct sil24_prb prb;
> +	struct sil24_sge sge[LIBATA_MAX_PRD];
> +};
> +
> +/*
> + * ap->private_data
> + *
> + * The preview driver always returned 0 for status.  We emulate it
> + * here from the previous interrupt.
> + */
> +struct sil24_port_priv {
> +	void *port;
> +	struct sil24_cmd_block *cmd_block;	/* 32 cmd blocks */
> +	dma_addr_t cmd_block_dma;		/* DMA base addr for them */
> +};
> +
> +/* ap->host_set->private_data */
> +struct sil24_host_priv {
> +	void *host_base;	/* global controller control (128 bytes @BAR0) */
> +	void *port_base;	/* port registers (4 * 8192 bytes @BAR2) */
> +};
> +
> +static u8 sil24_check_status(struct ata_port *ap);
> +static u8 sil24_check_err(struct ata_port *ap);
> +static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg);
> +static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val);
> +static void sil24_phy_reset(struct ata_port *ap);
> +static void sil24_qc_prep(struct ata_queued_cmd *qc);
> +static int sil24_qc_issue(struct ata_queued_cmd *qc);
> +static void sil24_irq_clear(struct ata_port *ap);
> +static void sil24_eng_timeout(struct ata_port *ap);
> +static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
> +static int sil24_port_start(struct ata_port *ap);
> +static void sil24_port_stop(struct ata_port *ap);
> +static void sil24_host_stop(struct ata_host_set *host_set);
> +static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
> +
> +static struct pci_device_id sil24_pci_tbl[] = {
> +	{ 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 },
> +	{ 0x1095, 0x3132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3132 },
> +};
> +
> +static struct pci_driver sil24_pci_driver = {
> +	.name			= DRV_NAME,
> +	.id_table		= sil24_pci_tbl,
> +	.probe			= sil24_init_one,
> +	.remove			= ata_pci_remove_one, /* safe? */
> +};
> +
> +static Scsi_Host_Template sil24_sht = {
> +	.module			= THIS_MODULE,
> +	.name			= DRV_NAME,
> +	.ioctl			= ata_scsi_ioctl,
> +	.queuecommand		= ata_scsi_queuecmd,
> +	.eh_strategy_handler	= ata_scsi_error,
> +	.can_queue		= ATA_DEF_QUEUE,
> +	.this_id		= ATA_SHT_THIS_ID,
> +	.sg_tablesize		= LIBATA_MAX_PRD,
> +	.max_sectors		= ATA_MAX_SECTORS,
> +	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
> +	.emulated		= ATA_SHT_EMULATED,
> +	.use_clustering		= ATA_SHT_USE_CLUSTERING,
> +	.proc_name		= DRV_NAME,
> +	.dma_boundary		= ATA_DMA_BOUNDARY,
> +	.slave_configure	= ata_scsi_slave_config,
> +	.bios_param		= ata_std_bios_param,
> +	.ordered_flush		= 1, /* NCQ not supported yet */
> +};
> +
> +static struct ata_port_operations sil24_ops = {
> +	.port_disable		= ata_port_disable,
> +
> +	.check_status		= sil24_check_status,
> +	.check_altstatus	= sil24_check_status,
> +	.check_err		= sil24_check_err,
> +	.dev_select		= ata_noop_dev_select,
> +
> +	.phy_reset		= sil24_phy_reset,
> +
> +	.qc_prep		= sil24_qc_prep,
> +	.qc_issue		= sil24_qc_issue,
> +
> +	.eng_timeout		= sil24_eng_timeout,
> +
> +	.irq_handler		= sil24_interrupt,
> +	.irq_clear		= sil24_irq_clear,
> +
> +	.scr_read		= sil24_scr_read,
> +	.scr_write		= sil24_scr_write,
> +
> +	.port_start		= sil24_port_start,
> +	.port_stop		= sil24_port_stop,
> +	.host_stop		= sil24_host_stop,
> +};
> +
> +static struct ata_port_info sil24_port_info[] = {
> +	/* sil_3124 */
> +	{
> +		.sht		= &sil24_sht,
> +		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
> +				  ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
> +				  ATA_FLAG_PIO_DMA,
> +		.pio_mask	= 0x1f,			/* pio0-4 */
> +		.mwdma_mask	= 0x07,			/* mwdma0-2 */
> +		.udma_mask	= 0x3f,			/* udma0-5 */
> +		.port_ops	= &sil24_ops,
> +	},
> +	/* sil_3132 */ 
> +	{
> +		.sht		= &sil24_sht,
> +		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
> +				  ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
> +				  ATA_FLAG_PIO_DMA,
> +		.pio_mask	= 0x1f,			/* pio0-4 */
> +		.mwdma_mask	= 0x07,			/* mwdma0-2 */
> +		.udma_mask	= 0x3f,			/* udma0-5 */
> +		.port_ops	= &sil24_ops,
> +	},
> +};
> +
> +static u8 sil24_check_status(struct ata_port *ap)
> +{
> +	return ATA_DRDY;
> +}
> +
> +static u8 sil24_check_err(struct ata_port *ap)
> +{
> +	return 0;
> +}

For these two functions, we need to grab the values for these registers 
from the D2H Register FIS.  These should not be constant values, they 
are used in probing.


> +static int sil24_scr_map[] = {
> +	[SCR_CONTROL]	= 0,
> +	[SCR_STATUS]	= 1,
> +	[SCR_ERROR]	= 2,
> +	[SCR_ACTIVE]	= 3,
> +};
> +
> +static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg)
> +{
> +	void *scr_addr = (void *)ap->ioaddr.scr_addr;
> +	if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
> +		void *addr;
> +		addr = scr_addr + sil24_scr_map[sc_reg] * 4;
> +		return readl(scr_addr + sil24_scr_map[sc_reg] * 4);
> +	}
> +	return 0xffffffffU;
> +}
> +
> +static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val)
> +{
> +	void *scr_addr = (void *)ap->ioaddr.scr_addr;
> +	if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
> +		void *addr;
> +		addr = scr_addr + sil24_scr_map[sc_reg] * 4;
> +		writel(val, scr_addr + sil24_scr_map[sc_reg] * 4);
> +	}
> +}
> +
> +static void sil24_phy_reset(struct ata_port *ap)
> +{
> +	__sata_phy_reset(ap);
> +	/*
> +	 * No ATAPI yet.  Just unconditionally indicate ATA device.
> +	 * If ATAPI device is attached, it will fail ATA_CMD_ID_ATA
> +	 * and libata core will ignore the device.
> +	 */
> +	if (!(ap->flags & ATA_FLAG_PORT_DISABLED))
> +		ap->device[0].class = ATA_DEV_ATA;
> +}

We need to use the standard probing code to figure this out. 
ata_dev_classify(), etc.


> +static inline void sil24_fill_sg(struct ata_queued_cmd *qc,
> +				 struct sil24_cmd_block *cb)
> +{
> +	struct scatterlist *sg = qc->sg;
> +	struct sil24_sge *sge = cb->sge;
> +	unsigned i;
> +
> +	for (i = 0; i < qc->n_elem; i++, sg++, sge++) {
> +		sge->addr = cpu_to_le64(sg_dma_address(sg));
> +		sge->cnt = cpu_to_le32(sg_dma_len(sg));
> +		sge->flags = 0;
> +		sge->flags = i < qc->n_elem - 1 ? 0 : cpu_to_le32(SGE_TRM);
> +	}
> +}
> +
> +static void sil24_qc_prep(struct ata_queued_cmd *qc)
> +{
> +	struct ata_port *ap = qc->ap;
> +	struct sil24_port_priv *pp = ap->private_data;
> +	struct sil24_cmd_block *cb = pp->cmd_block + qc->tag;
> +	struct sil24_prb *prb = &cb->prb;
> +
> +	switch (qc->tf.protocol) {
> +	case ATA_PROT_PIO:
> +	case ATA_PROT_DMA:
> +	case ATA_PROT_NODATA:
> +		break;
> +	default:
> +		/* ATAPI isn't supported yet */
> +		BUG();
> +	}
> +
> +	ata_tf_to_fis(&qc->tf, prb->fis, 0);
> +
> +	if (qc->flags & ATA_QCFLAG_DMAMAP)
> +		sil24_fill_sg(qc, cb);
> +}
> +
> +static int sil24_qc_issue(struct ata_queued_cmd *qc)
> +{
> +	struct ata_port *ap = qc->ap;
> +	struct sil24_port_priv *pp = ap->private_data;
> +	dma_addr_t paddr = pp->cmd_block_dma + qc->tag * sizeof(*pp->cmd_block);
> +
> +	writel((u32)paddr, pp->port + PORT_CMD_ACTIVATE);
> +	return 0;
> +}
> +
> +static void sil24_irq_clear(struct ata_port *ap)
> +{
> +	/* unused */
> +}

we need to fill this in.


> +static void sil24_reset_controller(struct ata_port *ap)
> +{
> +	struct sil24_port_priv *pp = ap->private_data;
> +	void *port = pp->port;
> +	int cnt;
> +	u32 tmp;
> +
> +	printk(KERN_NOTICE DRV_NAME
> +	       " ata%u: resetting controller...\n", ap->id);
> +
> +	/* Reset controller state.  Is this correct? */
> +	writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
> +	readl(port + PORT_CTRL_STAT);	/* sync */
> +
> +	/* Max ~100ms */
> +	for (cnt = 0; cnt < 1000; cnt++) {
> +		udelay(100);
> +		tmp = readl(port + PORT_CTRL_STAT);
> +		if (!(tmp & PORT_CS_DEV_RST))
> +			break;
> +	}

Use mdelay.  We should be able to sleep here?

Either way, we want to avoid long delays like this.


> +	if (tmp & PORT_CS_DEV_RST)
> +		printk(KERN_ERR DRV_NAME
> +		       " ata%u: failed to reset controller\n", ap->id);
> +}
> +
> +static void sil24_eng_timeout(struct ata_port *ap)
> +{
> +	struct ata_queued_cmd *qc;
> +
> +	qc = ata_qc_from_tag(ap, ap->active_tag);
> +	if (!qc) {
> +		printk(KERN_ERR "ata%u: BUG: tiemout without command\n",
> +		       ap->id);
> +		return;
> +	}
> +
> +	/*
> +	 * hack alert!  We cannot use the supplied completion
> +	 * function from inside the ->eh_strategy_handler() thread.
> +	 * libata is the only user of ->eh_strategy_handler() in
> +	 * any kernel, so the default scsi_done() assumes it is
> +	 * not being called from the SCSI EH.
> +	 */
> +	printk(KERN_ERR "ata%u: command timeout\n", ap->id);
> +	qc->scsidone = scsi_finish_command;
> +	ata_qc_complete(qc, ATA_ERR);
> +
> +	sil24_reset_controller(ap);
> +}
> +
> +static inline void sil24_host_intr(struct ata_port *ap)
> +{
> +	struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
> +	struct sil24_port_priv *pp = ap->private_data;
> +	void *port = pp->port;
> +	u32 slot_stat;
> +
> +	slot_stat = readl(port + PORT_SLOT_STAT);
> +	if (!(slot_stat & HOST_SSTAT_ATTN)) {
> +		if (qc)
> +			ata_qc_complete(qc, 0);
> +	} else {
> +		u32 irq_stat, cmd_err, sstatus, serror;
> +
> +		irq_stat = readl(port + PORT_IRQ_STAT);
> +		cmd_err = readl(port + PORT_CMD_ERR);
> +		sstatus = readl(port + PORT_SSTATUS);
> +		serror = readl(port + PORT_SERROR);
> +
> +		/* Clear IRQ/errors */
> +		writel(irq_stat, port + PORT_IRQ_STAT);
> +		if (cmd_err)
> +			writel(cmd_err, port + PORT_CMD_ERR);
> +		if (serror)
> +			writel(serror, port + PORT_SERROR);
> +
> +		printk(KERN_ERR DRV_NAME " ata%u: error interrupt on port%d\n"
> +		       "  stat=0x%x irq=0x%x cmd_err=%d sstatus=0x%x serror=0x%x\n",
> +		       ap->id, ap->port_no, slot_stat, irq_stat, cmd_err, sstatus, serror);
> +
> +		if (qc)
> +			ata_qc_complete(qc, ATA_ERR);
> +
> +		sil24_reset_controller(ap);
> +	}
> +}

Two comments:
1) The "OK" test for command completion seems quite fragile.  I think 
the code may be -assuming- a command is completed.  I'll have to check 
the docs to see if HOST_IRQ_STAT + no-other-events truly guarantees an 
indication of command completion.

2) Error handling should be moved out-of-line, in a separate function.


> +static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
> +{
> +	struct ata_host_set *host_set = dev_instance;
> +	struct sil24_host_priv *hpriv = host_set->private_data;
> +	unsigned handled = 0;
> +	u32 status;
> +	int i;
> +
> +	status = readl(hpriv->host_base + HOST_IRQ_STAT);
> +
> +	if (!(status & IRQ_STAT_4PORTS))
> +		goto out;

also check for status==0xffffffff to indicate PCI bus fault, or hardware 
unplug.


> +	spin_lock(&host_set->lock);
> +
> +	for (i = 0; i < host_set->n_ports; i++)
> +		if (status & (1 << i)) {
> +			struct ata_port *ap = host_set->ports[i];
> +			if (ap && !(ap->flags & ATA_FLAG_PORT_DISABLED))
> +				sil24_host_intr(host_set->ports[i]);
> +			else {
> +				u32 tmp;
> +				printk(KERN_WARNING DRV_NAME
> +				       ": spurious interrupt from port %d\n", i);
> +				tmp = readl(hpriv->host_base + HOST_CTRL);
> +				tmp &= ~(1 << i);
> +				writel(tmp, hpriv->host_base + HOST_CTRL);

add a comment describing what these three lines of code do


> +			}
> +			handled++;
> +		}
> +
> +	spin_unlock(&host_set->lock);
> + out:
> +	return IRQ_RETVAL(handled);
> +}
> +
> +static int sil24_port_start(struct ata_port *ap)
> +{
> +	struct device *dev = ap->host_set->dev;
> +	struct sil24_host_priv *hpriv = ap->host_set->private_data;
> +	struct sil24_port_priv *pp;
> +	struct sil24_cmd_block *cb;
> +	size_t cb_size = sizeof(*cb);
> +	dma_addr_t cb_dma;
> +
> +	pp = kmalloc(sizeof(*pp), GFP_KERNEL);
> +	if (!pp)
> +		return -ENOMEM;
> +	memset(pp, 0, sizeof(*pp));
> +
> +	cb = dma_alloc_coherent(dev, cb_size, &cb_dma, GFP_KERNEL);
> +	if (!cb) {
> +		kfree(pp);
> +		return -ENOMEM;
> +	}
> +	memset(cb, 0, cb_size);
> +
> +	pp->port = hpriv->port_base + ap->port_no * PORT_REGS_SIZE;
> +	pp->cmd_block = cb;
> +	pp->cmd_block_dma = cb_dma;
> +
> +	ap->private_data = pp;
> +
> +	return 0;
> +}
> +
> +static void sil24_port_stop(struct ata_port *ap)
> +{
> +	struct device *dev = ap->host_set->dev;
> +	struct sil24_port_priv *pp = ap->private_data;
> +	size_t cb_size = sizeof(*pp->cmd_block);
> +
> +	dma_free_coherent(dev, cb_size, pp->cmd_block, pp->cmd_block_dma);
> +	kfree(pp);
> +}
> +
> +static void sil24_host_stop(struct ata_host_set *host_set)
> +{
> +	struct sil24_host_priv *hpriv = host_set->private_data;
> +
> +	iounmap(hpriv->host_base);
> +	iounmap(hpriv->port_base);
> +	kfree(hpriv);
> +}
> +
> +static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> +{
> +	static int printed_version = 0;
> +	unsigned int board_id = (unsigned int)ent->driver_data;
> +	struct ata_probe_ent *probe_ent = NULL;
> +	struct sil24_host_priv *hpriv = NULL;
> +	void *host_base = NULL, *port_base = NULL;
> +	int i, rc;
> +
> +	if (!printed_version++)
> +		printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
> +
> +	rc = pci_enable_device(pdev);
> +	if (rc)
> +		return rc;
> +
> +	rc = pci_request_regions(pdev, DRV_NAME);
> +	if (rc)
> +		goto out_disable;
> +
> +	rc = -ENOMEM;
> +	/* ioremap mmio registers */
> +	host_base = ioremap(pci_resource_start(pdev, 0),
> +			    pci_resource_len(pdev, 0));
> +	if (!host_base)
> +		goto out_free;
> +	port_base = ioremap(pci_resource_start(pdev, 2),
> +			    pci_resource_len(pdev, 2));
> +	if (!port_base)
> +		goto out_free;
> +	/* allocate & init probe_ent and hpriv */
> +	probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
> +	if (!probe_ent)
> +		goto out_free;
> +
> +	hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
> +	if (!hpriv)
> +		goto out_free;
> +
> +	memset(probe_ent, 0, sizeof(*probe_ent));
> +	probe_ent->dev = pci_dev_to_dev(pdev);
> +	INIT_LIST_HEAD(&probe_ent->node);
> +
> +	probe_ent->sht		= sil24_port_info[board_id].sht;
> +	probe_ent->host_flags	= sil24_port_info[board_id].host_flags;
> +	probe_ent->pio_mask	= sil24_port_info[board_id].pio_mask;
> +	probe_ent->udma_mask	= sil24_port_info[board_id].udma_mask;
> +	probe_ent->port_ops	= sil24_port_info[board_id].port_ops;
> +	probe_ent->n_ports	= (board_id == BID_SIL3124) ? 4 : 2;
> +
> +	probe_ent->irq = pdev->irq;
> +	probe_ent->irq_flags = SA_SHIRQ;
> +	probe_ent->mmio_base = port_base;
> +	probe_ent->private_data = hpriv;
> +
> +	memset(hpriv, 0, sizeof(*hpriv));
> +	hpriv->host_base = host_base;
> +	hpriv->port_base = port_base;
> +
> +	/*
> +	 * Configure the device
> +	 */
> +	/*
> +	 * FIXME: This device is certainly 64-bit capable.  We just
> +	 * don't know how to use it.  After fixing 32bit activation in
> +	 * this function, enable 64bit masks here.
> +	 */

elaboration?


> +	rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
> +	if (rc) {
> +		printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n",
> +		       pci_name(pdev));
> +		goto out_free;
> +	}
> +	rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
> +	if (rc) {
> +		printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable failed\n",
> +		       pci_name(pdev));
> +		goto out_free;
> +	}
> +
> +	/* GPIO off */
> +	writel(0, host_base + HOST_FLASH_CMD);
> +
> +	/* Mask interrupts during initialization */
> +	writel(0, host_base + HOST_CTRL);

add a readl() to flush this write out to the PCI bus ("PCI posting")


> +	for (i = 0; i < probe_ent->n_ports; i++) {
> +		void *port = port_base + i * PORT_REGS_SIZE;
> +		unsigned long portu = (unsigned long)port;
> +		u32 tmp;
> +		int cnt;
> +
> +		probe_ent->port[i].cmd_addr = portu + PORT_TF;
> +		probe_ent->port[i].ctl_addr = portu + PORT_TF + 0xa;
> +		probe_ent->port[i].altstatus_addr = portu + PORT_TF + 0xa;
> +		probe_ent->port[i].scr_addr = portu + PORT_SCONTROL;
> +
> +		ata_std_ports(&probe_ent->port[i]);
> +
> +		/* Initial PHY setting */
> +		writel(0x20c, port + PORT_PHY_CFG);
> +
> +		/* Clear port RST */
> +		tmp = readl(port + PORT_CTRL_STAT);
> +		if (tmp & PORT_CS_PORT_RST) {
> +			writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
> +			readl(port + PORT_CTRL_STAT);	/* sync */
> +			for (cnt = 0; cnt < 10; cnt++) {
> +				msleep(10);
> +				tmp = readl(port + PORT_CTRL_STAT);
> +				if (!(tmp & PORT_CS_PORT_RST))
> +					break;
> +			}
> +			if (tmp & PORT_CS_PORT_RST)
> +				printk(KERN_ERR DRV_NAME
> +				       "(%s): failed to clear port RST\n",
> +				       pci_name(pdev));
> +		}

this is already done in sata_phy_reset()?


> +		/* Zero error counters. */
> +		writel(0x8000, port + PORT_DECODE_ERR_THRESH);
> +		writel(0x8000, port + PORT_CRC_ERR_THRESH);
> +		writel(0x8000, port + PORT_HSHK_ERR_THRESH);
> +		writel(0x0000, port + PORT_DECODE_ERR_CNT);
> +		writel(0x0000, port + PORT_CRC_ERR_CNT);
> +		writel(0x0000, port + PORT_HSHK_ERR_CNT);
> +
> +		/* FIXME: 32bit activation? */
> +		writel(0, port + PORT_ACTIVATE_UPPER_ADDR);
> +		writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_STAT);
> +
> +		/* Configure interrupts */
> +		writel(0xffff, port + PORT_IRQ_ENABLE_CLR);
> +		writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | PORT_IRQ_SDB_FIS,
> +		       port + PORT_IRQ_ENABLE_SET);
> +
> +		/* Clear interrupts */
> +		writel(0x0fff0fff, port + PORT_IRQ_STAT);
> +		writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
> +	}
> +
> +	/* Turn on interrupts */
> +	writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
> +
> +	pci_set_master(pdev);
> +
> +	ata_device_add(probe_ent);

as with other drivers, add FIXME comment, indicating that the return 
value should be checked, and if ata_device_add() failed.


> +	kfree(probe_ent);
> +	return 0;
> +
> + out_free:
> +	if (host_base)
> +		iounmap(host_base);
> +	if (port_base)
> +		iounmap(port_base);
> +	kfree(probe_ent);
> +	kfree(hpriv);
> +	pci_release_regions(pdev);
> + out_disable:
> +	pci_disable_device(pdev);
> +	return rc;
> +}
> +
> +static int __init sil24_init(void)
> +{
> +	return pci_module_init(&sil24_pci_driver);
> +}
> +
> +static void __exit sil24_exit(void)
> +{
> +	pci_unregister_driver(&sil24_pci_driver);
> +}
> +
> +MODULE_AUTHOR("Tejun Heo");
> +MODULE_DESCRIPTION("Silicon Image 3124/3132 SATA low-level driver");
> +MODULE_LICENSE("GPL");
> +MODULE_DEVICE_TABLE(pci, sil24_pci_tbl);
> +
> +module_init(sil24_init);
> +module_exit(sil24_exit);
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-07-28 20:27 ` Jeff Garzik
@ 2005-07-30  8:17   ` Tejun Heo
  2005-08-02 22:56     ` Edward Falk
  0 siblings, 1 reply; 13+ messages in thread
From: Tejun Heo @ 2005-07-30  8:17 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: linux-ide, Linux Kernel, Carlos Pardo

 Hello, Jeff.

 I'll answer to your comments in this mail (and several questions,
too) and will soon post patches fixing things in separate mails.

On Thu, Jul 28, 2005 at 04:27:37PM -0400, Jeff Garzik wrote:
> Tejun Heo wrote:
> > Hello, Jeff.
> >
> > This is rewritten sil24 driver against v2.6.13-rc3.  It seems to work
> >and am currently running stress test on it (random raw read of
> >concurrency 4, repeatitive mount/copy/checksup/unmount).  I'll keep
> >running stress test for at least 12 hours and let you know if
> >something goes wrong.  I've also tested basic error handling and it
> >seems to work.
> 
> I've merged this into the 'sil24' branch of libata-dev.git, and moved 
> the original driver from Silicon Image into the 'sil24-original' branch.
> 
> So, please submit an incremental patch for any future changes to this 
> driver.
> 
> Comments below, need a few fixups before pushing upstream, most likely.
> 
> Also, a question:  do you have hardware docs?
> 

 Unfortunately, no.  I've written this driver soley based on the SII's
preview driver.  It would be great if I can obtain the hardware docs,
so that I can do things more properly and try NCQ and ATAPI.

> 
> >diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
> >--- a/drivers/scsi/Kconfig
> >+++ b/drivers/scsi/Kconfig
> >@@ -499,6 +499,14 @@ config SCSI_SATA_SIL
> > 
> > 	  If unsure, say N.
> > 
> >+config SCSI_SATA_SIL24
> >+	tristate "Silicon Image 3124/3132 SATA support"
> >+	depends on SCSI_SATA && PCI && EXPERIMENTAL
> >+	help
> >+	  This option enables support for Silicon Image 3124/3132 Serial ATA.
> >+
> >+	  If unsure, say N.
> >+
> > config SCSI_SATA_SIS
> > 	tristate "SiS 964/180 SATA support"
> > 	depends on SCSI_SATA && PCI && EXPERIMENTAL
> >diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
> >--- a/drivers/scsi/Makefile
> >+++ b/drivers/scsi/Makefile
> >@@ -126,6 +126,7 @@ obj-$(CONFIG_SCSI_ATA_PIIX)	+= libata.o 
> > obj-$(CONFIG_SCSI_SATA_PROMISE)	+= libata.o sata_promise.o
> > obj-$(CONFIG_SCSI_SATA_QSTOR)	+= libata.o sata_qstor.o
> > obj-$(CONFIG_SCSI_SATA_SIL)	+= libata.o sata_sil.o
> >+obj-$(CONFIG_SCSI_SATA_SIL24)	+= libata.o sata_sil24.o
> > obj-$(CONFIG_SCSI_SATA_VIA)	+= libata.o sata_via.o
> > obj-$(CONFIG_SCSI_SATA_VITESSE)	+= libata.o sata_vsc.o
> > obj-$(CONFIG_SCSI_SATA_SIS)	+= libata.o sata_sis.o
> >diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
> >new file mode 100644
> >--- /dev/null
> >+++ b/drivers/scsi/sata_sil24.c
> >@@ -0,0 +1,786 @@
> >+/*
> >+ * sata_sil24.c - Driver for Silicon Image 3124/3132 SATA-2 controllers
> >+ *
> >+ * Copyright 2005  Tejun Heo
> >+ *
> >+ * Based on preview driver from Silicon Image.
> >+ *
> >+ * NOTE: No NCQ/ATAPI support yet.  The preview driver didn't support
> >+ * NCQ nor ATAPI, and, unfortunately, I couldn't find out how to make
> >+ * those work.  Enabling those shouldn't be difficult.  Basic
> >+ * structure is all there (in libata-dev tree).  If you have any
> >+ * information about this hardware, please contact me or linux-ide.
> >+ * Info is needed on...
> >+ *
> >+ * - How to issue tagged commands and turn on sactive on issue 
> >accordingly.
> >+ * - Where to put an ATAPI command and how to tell the device to send it.
> >+ * - How to enable/use 64bit.
> >+ *
> >+ * This program is free software; you can redistribute it and/or modify it
> >+ * under the terms of the GNU General Public License as published by the
> >+ * Free Software Foundation; either version 2, or (at your option) any
> >+ * later version.
> >+ *
> >+ * This program is distributed in the hope that it will be useful, but
> >+ * WITHOUT ANY WARRANTY; without even the implied warranty of
> >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >+ * General Public License for more details.
> >+ *
> >+ */
> >+
> >+#include <linux/kernel.h>
> >+#include <linux/module.h>
> >+#include <linux/pci.h>
> >+#include <linux/blkdev.h>
> >+#include <linux/delay.h>
> >+#include <linux/interrupt.h>
> >+#include <linux/dma-mapping.h>
> >+#include <scsi/scsi_host.h>
> >+#include "scsi.h"
> >+#include <linux/libata.h>
> >+#include <asm/io.h>
> >+
> >+#define DRV_NAME	"sata_sil24"
> >+#define DRV_VERSION	"0.20"	/* Silicon Image's preview driver was 0.10 */
> >+
> >+#define NR_PORTS	4
> >+
> >+/*
> >+ * Port request block (PRB) 32 bytes
> >+ */
> >+struct sil24_prb {
> >+	u16	ctrl;
> >+	u16	prot;
> >+	u32	rx_cnt;
> >+	u8	fis[6 * 4];
> >+};
> >+
> >+/*
> >+ * Scatter gather entry (SGE) 16 bytes
> >+ */
> >+struct sil24_sge {
> >+	u64	addr;
> >+	u32	cnt;
> >+	u32	flags;
> >+};
> >+
> >+/*
> >+ * Port multiplier
> >+ */
> >+struct sil24_port_multiplier {
> >+	u32	diag;
> >+	u32	sactive;
> >+};
> 
> I know this is unlikely, but just asking...  you didn't test this with a 
> port multiplier, did you?
> 

 No, the structure is there just for completness (the preview driver
had it).  If I can get the docs and access to a port multiplier, I'm
willing to implement PM support though.  Is any multiplier on sale?  I
cannot find any on online shops over here.

> 
> >+enum {
> >+	/*
> >+	 * Global controller registers (128 bytes @ BAR0)
> >+	 */
> >+		/* 32 bit regs */
> >+	HOST_SLOT_STAT		= 0x00, /* 32 bit slot stat * 4 */
> >+	HOST_CTRL		= 0x40,
> >+	HOST_IRQ_STAT		= 0x44,
> >+	HOST_PHY_CFG		= 0x48,
> >+	HOST_BIST_CTRL		= 0x50,
> >+	HOST_BIST_PTRN		= 0x54,
> >+	HOST_BIST_STAT		= 0x58,
> >+	HOST_MEM_BIST_STAT	= 0x5c,
> >+	HOST_FLASH_CMD		= 0x70,
> >+		/* 8 bit regs */
> >+	HOST_FLASH_DATA		= 0x74,
> >+	HOST_TRANSITION_DETECT	= 0x75,
> >+	HOST_GPIO_CTRL		= 0x76,
> >+	HOST_I2C_ADDR		= 0x78, /* 32 bit */
> >+	HOST_I2C_DATA		= 0x7c,
> >+	HOST_I2C_XFER_CNT	= 0x7e,
> >+	HOST_I2C_CTRL		= 0x7f,
> >+
> >+	/* HOST_SLOT_STAT bits */
> >+	HOST_SSTAT_ATTN		= (1 << 31),
> >+
> >+	/*
> >+	 * Port registers
> >+	 * (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2)
> >+	 */
> >+	PORT_REGS_SIZE		= 0x2000,
> >+	PORT_PRB		= 0x0000, /* (32 bytes PRB + 16 bytes SGEs * 
> >6) * 31 (3968 bytes) */
> >+		/* TF is overlayed w/ PRB regs in the preview driver,
> >+		 * but it doesn't seem to work. */
> >+	PORT_TF			= 0x0000,
> >+
> >+	PORT_PM			= 0x0f80, /* 8 bytes PM * 16 (128 bytes) */
> >+		/* 32 bit regs */
> >+	PORT_CTRL_STAT		= 0x1000, /* write:ctrl, read:stat */
> >+	PORT_CTRL_CLR		= 0x1004,
> >+	PORT_IRQ_STAT		= 0x1008,
> >+	PORT_IRQ_ENABLE_SET	= 0x1010,
> >+	PORT_IRQ_ENABLE_CLR	= 0x1014,
> >+	PORT_ACTIVATE_UPPER_ADDR= 0x101c,
> >+	PORT_EXEC_FIFO		= 0x1020,
> >+	PORT_CMD_ERR		= 0x1024,
> >+	PORT_FIS_CFG		= 0x1028,
> >+	PORT_FIFO_THRES		= 0x102c,
> >+		/* 16 bit regs */
> >+	PORT_DECODE_ERR_CNT	= 0x1040,
> >+	PORT_DECODE_ERR_THRESH	= 0x1042,
> >+	PORT_CRC_ERR_CNT	= 0x1044,
> >+	PORT_CRC_ERR_THRESH	= 0x1046,
> >+	PORT_HSHK_ERR_CNT	= 0x1048,
> >+	PORT_HSHK_ERR_THRESH	= 0x104a,
> >+		/* 32 bit regs */
> >+	PORT_PHY_CFG		= 0x1050,
> >+	PORT_SLOT_STAT		= 0x1800,
> >+	PORT_CMD_ACTIVATE	= 0x1c00, /* 64 bit cmd activate * 31 (248 
> >bytes) */
> >+	PORT_EXEC_DIAG		= 0x1e00, /* 32bit exec diag * 16 (64 bytes, 
> >0-10 used on 3124) */
> >+	PORT_PSD_DIAG		= 0x1e40, /* 32bit psd diag * 16 (64 bytes, 
> >0-8 used on 3124) */
> >+	PORT_SCONTROL		= 0x1f00,
> >+	PORT_SSTATUS		= 0x1f04,
> >+	PORT_SERROR		= 0x1f08,
> >+	PORT_SACTIVE		= 0x1f0c,
> >+
> >+	/* PORT_CTRL_STAT bits */
> >+	PORT_CS_PORT_RST	= (1 << 0), /* port reset */
> >+	PORT_CS_DEV_RST		= (1 << 1), /* device reset */
> >+	PORT_CS_INIT		= (1 << 2), /* port initialize */
> >+	PORT_CS_IRQ_WOC		= (1 << 3), /* interrupt write one to clear 
> >*/
> >+	PORT_CS_RESUME		= (1 << 4), /* port resume */
> >+	PORT_CS_32BIT_ACTV	= (1 << 5), /* 32-bit activation */
> >+	PORT_CS_PM_EN		= (1 << 6), /* port multiplier enable */
> >+	PORT_CS_RDY		= (1 << 7), /* port ready to accept commands 
> >*/
> >+
> >+	/* PORT_IRQ_STAT/ENABLE_SET/CLR */
> >+	/* bits[11:0] are masked */
> >+	PORT_IRQ_COMPLETE	= (1 << 0), /* command(s) completed */
> >+	PORT_IRQ_ERROR		= (1 << 1), /* command execution error */
> >+	PORT_IRQ_PORTRDY_CHG	= (1 << 2), /* port ready change */
> >+	PORT_IRQ_PWR_CHG	= (1 << 3), /* power management change */
> >+	PORT_IRQ_PHYRDY_CHG	= (1 << 4), /* PHY ready change */
> >+	PORT_IRQ_COMWAKE	= (1 << 5), /* COMWAKE received */
> >+	PORT_IRQ_UNK_FIS	= (1 << 6), /* Unknown FIS received */
> >+	PORT_IRQ_SDB_FIS	= (1 << 11), /* SDB FIS received */
> >+
> >+	/* bits[27:16] are unmasked (raw) */
> >+	PORT_IRQ_RAW_SHIFT	= 16,
> >+	PORT_IRQ_MASKED_MASK	= 0x7ff,
> >+	PORT_IRQ_RAW_MASK	= (0x7ff << PORT_IRQ_RAW_SHIFT),
> >+
> >+	/* ENABLE_SET/CLR specific, intr steering - 2 bit field */
> >+	PORT_IRQ_STEER_SHIFT	= 30,
> >+	PORT_IRQ_STEER_MASK	= (3 << PORT_IRQ_STEER_SHIFT),
> >+
> >+	/* PORT_CMD_ERR constants */
> >+	PORT_CERR_DEV		= 1, /* Error bit in D2H Register FIS */
> >+	PORT_CERR_SDB		= 2, /* Error bit in SDB FIS */
> >+	PORT_CERR_DATA		= 3, /* Error in data FIS not detected by 
> >dev */
> >+	PORT_CERR_SEND		= 4, /* Initial cmd FIS transmission failure 
> >*/
> >+	PORT_CERR_INCONSISTENT	= 5, /* Protocol mismatch */
> >+	PORT_CERR_DIRECTION	= 6, /* Data direction mismatch */
> >+	PORT_CERR_UNDERRUN	= 7, /* Ran out of SGEs while writing */
> >+	PORT_CERR_OVERRUN	= 8, /* Ran out of SGEs while reading */
> >+	PORT_CERR_PKT_PROT	= 11, /* DIR invalid in 1st PIO setup of 
> >ATAPI */
> >+	PORT_CERR_SGT_BOUNDARY	= 16, /* PLD ecode 00 - SGT not on qword 
> >boundary */
> >+	PORT_CERR_SGT_TGTABRT	= 17, /* PLD ecode 01 - target abort */
> >+	PORT_CERR_SGT_MSTABRT	= 18, /* PLD ecode 10 - master abort */
> >+	PORT_CERR_SGT_PCIPERR	= 19, /* PLD ecode 11 - PCI parity err while 
> >fetching SGT */
> >+	PORT_CERR_CMD_BOUNDARY	= 24, /* ctrl[15:13] 001 - PRB not on qword 
> >boundary */
> >+	PORT_CERR_CMD_TGTABRT	= 25, /* ctrl[15:13] 010 - target abort */
> >+	PORT_CERR_CMD_MSTABRT	= 26, /* ctrl[15:13] 100 - master abort */
> >+	PORT_CERR_CMD_PCIPERR	= 27, /* ctrl[15:13] 110 - PCI parity err 
> >while fetching PRB */
> >+	PORT_CERR_XFR_UNDEF	= 32, /* PSD ecode 00 - undefined */
> >+	PORT_CERR_XFR_TGTABRT	= 33, /* PSD ecode 01 - target abort */
> >+	PORT_CERR_XFR_MSGABRT	= 34, /* PSD ecode 10 - master abort */
> >+	PORT_CERR_XFR_PCIPERR	= 35, /* PSD ecode 11 - PCI prity err during 
> >transfer */
> >+	PORT_CERR_SENDSERVICE	= 36, /* FIS received whiel sending service 
> >*/
> >+
> >+	/*
> >+	 * Other constants
> >+	 */
> >+	SGE_TRM			= (1 << 31), /* Last SGE in chain */
> >+	PRB_SOFT_RST		= (1 << 7),  /* Soft reset request (ign 
> >BSY?) */
> >+
> >+	/* board id */
> >+	BID_SIL3124		= 0,
> >+	BID_SIL3132		= 1,
> >+
> >+	IRQ_STAT_4PORTS		= 0xf,
> >+};
> >+
> >+struct sil24_cmd_block {
> >+	struct sil24_prb prb;
> >+	struct sil24_sge sge[LIBATA_MAX_PRD];
> >+};
> >+
> >+/*
> >+ * ap->private_data
> >+ *
> >+ * The preview driver always returned 0 for status.  We emulate it
> >+ * here from the previous interrupt.
> >+ */
> >+struct sil24_port_priv {
> >+	void *port;
> >+	struct sil24_cmd_block *cmd_block;	/* 32 cmd blocks */
> >+	dma_addr_t cmd_block_dma;		/* DMA base addr for them */
> >+};
> >+
> >+/* ap->host_set->private_data */
> >+struct sil24_host_priv {
> >+	void *host_base;	/* global controller control (128 bytes 
> >@BAR0) */
> >+	void *port_base;	/* port registers (4 * 8192 bytes @BAR2) */
> >+};
> >+
> >+static u8 sil24_check_status(struct ata_port *ap);
> >+static u8 sil24_check_err(struct ata_port *ap);
> >+static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg);
> >+static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 
> >val);
> >+static void sil24_phy_reset(struct ata_port *ap);
> >+static void sil24_qc_prep(struct ata_queued_cmd *qc);
> >+static int sil24_qc_issue(struct ata_queued_cmd *qc);
> >+static void sil24_irq_clear(struct ata_port *ap);
> >+static void sil24_eng_timeout(struct ata_port *ap);
> >+static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct 
> >pt_regs *regs);
> >+static int sil24_port_start(struct ata_port *ap);
> >+static void sil24_port_stop(struct ata_port *ap);
> >+static void sil24_host_stop(struct ata_host_set *host_set);
> >+static int sil24_init_one(struct pci_dev *pdev, const struct 
> >pci_device_id *ent);
> >+
> >+static struct pci_device_id sil24_pci_tbl[] = {
> >+	{ 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 },
> >+	{ 0x1095, 0x3132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3132 },
> >+};
> >+
> >+static struct pci_driver sil24_pci_driver = {
> >+	.name			= DRV_NAME,
> >+	.id_table		= sil24_pci_tbl,
> >+	.probe			= sil24_init_one,
> >+	.remove			= ata_pci_remove_one, /* safe? */
> >+};
> >+
> >+static Scsi_Host_Template sil24_sht = {
> >+	.module			= THIS_MODULE,
> >+	.name			= DRV_NAME,
> >+	.ioctl			= ata_scsi_ioctl,
> >+	.queuecommand		= ata_scsi_queuecmd,
> >+	.eh_strategy_handler	= ata_scsi_error,
> >+	.can_queue		= ATA_DEF_QUEUE,
> >+	.this_id		= ATA_SHT_THIS_ID,
> >+	.sg_tablesize		= LIBATA_MAX_PRD,
> >+	.max_sectors		= ATA_MAX_SECTORS,
> >+	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
> >+	.emulated		= ATA_SHT_EMULATED,
> >+	.use_clustering		= ATA_SHT_USE_CLUSTERING,
> >+	.proc_name		= DRV_NAME,
> >+	.dma_boundary		= ATA_DMA_BOUNDARY,
> >+	.slave_configure	= ata_scsi_slave_config,
> >+	.bios_param		= ata_std_bios_param,
> >+	.ordered_flush		= 1, /* NCQ not supported yet */
> >+};
> >+
> >+static struct ata_port_operations sil24_ops = {
> >+	.port_disable		= ata_port_disable,
> >+
> >+	.check_status		= sil24_check_status,
> >+	.check_altstatus	= sil24_check_status,
> >+	.check_err		= sil24_check_err,
> >+	.dev_select		= ata_noop_dev_select,
> >+
> >+	.phy_reset		= sil24_phy_reset,
> >+
> >+	.qc_prep		= sil24_qc_prep,
> >+	.qc_issue		= sil24_qc_issue,
> >+
> >+	.eng_timeout		= sil24_eng_timeout,
> >+
> >+	.irq_handler		= sil24_interrupt,
> >+	.irq_clear		= sil24_irq_clear,
> >+
> >+	.scr_read		= sil24_scr_read,
> >+	.scr_write		= sil24_scr_write,
> >+
> >+	.port_start		= sil24_port_start,
> >+	.port_stop		= sil24_port_stop,
> >+	.host_stop		= sil24_host_stop,
> >+};
> >+
> >+static struct ata_port_info sil24_port_info[] = {
> >+	/* sil_3124 */
> >+	{
> >+		.sht		= &sil24_sht,
> >+		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
> >+				  ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
> >+				  ATA_FLAG_PIO_DMA,
> >+		.pio_mask	= 0x1f,			/* pio0-4 */
> >+		.mwdma_mask	= 0x07,			/* mwdma0-2 */
> >+		.udma_mask	= 0x3f,			/* udma0-5 */
> >+		.port_ops	= &sil24_ops,
> >+	},
> >+	/* sil_3132 */ 
> >+	{
> >+		.sht		= &sil24_sht,
> >+		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
> >+				  ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
> >+				  ATA_FLAG_PIO_DMA,
> >+		.pio_mask	= 0x1f,			/* pio0-4 */
> >+		.mwdma_mask	= 0x07,			/* mwdma0-2 */
> >+		.udma_mask	= 0x3f,			/* udma0-5 */
> >+		.port_ops	= &sil24_ops,
> >+	},
> >+};
> >+
> >+static u8 sil24_check_status(struct ata_port *ap)
> >+{
> >+	return ATA_DRDY;
> >+}
> >+
> >+static u8 sil24_check_err(struct ata_port *ap)
> >+{
> >+	return 0;
> >+}
> 
> For these two functions, we need to grab the values for these registers 
> from the D2H Register FIS.  These should not be constant values, they 
> are used in probing.
> 

 Sadly I don't know where the values are.  The original driver seems
to assume that taskfile registers are overlayed with PORT_PRB, but
they are not.  Values are bogus there.  Again, in need of hardware
docs here.

 The original rewritten sil24 driver against NCQ helpers had simple
status register emulation using normal/error completion interrupt.  I
don't know why I stripped that while converting the driver over
vanilla libata (sorry).  I'll restore it.  It's not good, but it
correctly indicates error on error.

> 
> >+static int sil24_scr_map[] = {
> >+	[SCR_CONTROL]	= 0,
> >+	[SCR_STATUS]	= 1,
> >+	[SCR_ERROR]	= 2,
> >+	[SCR_ACTIVE]	= 3,
> >+};
> >+
> >+static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg)
> >+{
> >+	void *scr_addr = (void *)ap->ioaddr.scr_addr;
> >+	if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
> >+		void *addr;
> >+		addr = scr_addr + sil24_scr_map[sc_reg] * 4;
> >+		return readl(scr_addr + sil24_scr_map[sc_reg] * 4);
> >+	}
> >+	return 0xffffffffU;
> >+}
> >+
> >+static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val)
> >+{
> >+	void *scr_addr = (void *)ap->ioaddr.scr_addr;
> >+	if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
> >+		void *addr;
> >+		addr = scr_addr + sil24_scr_map[sc_reg] * 4;
> >+		writel(val, scr_addr + sil24_scr_map[sc_reg] * 4);
> >+	}
> >+}
> >+
> >+static void sil24_phy_reset(struct ata_port *ap)
> >+{
> >+	__sata_phy_reset(ap);
> >+	/*
> >+	 * No ATAPI yet.  Just unconditionally indicate ATA device.
> >+	 * If ATAPI device is attached, it will fail ATA_CMD_ID_ATA
> >+	 * and libata core will ignore the device.
> >+	 */
> >+	if (!(ap->flags & ATA_FLAG_PORT_DISABLED))
> >+		ap->device[0].class = ATA_DEV_ATA;
> >+}
> 
> We need to use the standard probing code to figure this out. 
> ata_dev_classify(), etc.
> 

 Again, the problem here is that I don't know how to access the
signature values after reset.  The preview driver didn't do that and
maybe that's why they duplicated the whole reset-probe procedure.  The
above code always forces ATA_DEV_ATA signature after reset which makes
ata_dev_identify() always use ATA_CMD_ID_ATA which will be failed by
the drive (with soon-to-be-posted status fix... :-).  So, it's a bit
hacky but achieves ATA-only support with very few lines.

 Maybe the proper thing here would be...

* get information on how to access signature information and set class
  to ATA_DEV_NONE if ATAPI.  (Or, even better, add proper ATAPI support)
* if signature info is inaccessible, we can implement ATA_FLAG_NOSIG
  flag and let libata layer figure out what's attached using both
  ATA_CMD_ID_ATA and ATA_CMD_ID_ATAPI.

> 
> >+static inline void sil24_fill_sg(struct ata_queued_cmd *qc,
> >+				 struct sil24_cmd_block *cb)
> >+{
> >+	struct scatterlist *sg = qc->sg;
> >+	struct sil24_sge *sge = cb->sge;
> >+	unsigned i;
> >+
> >+	for (i = 0; i < qc->n_elem; i++, sg++, sge++) {
> >+		sge->addr = cpu_to_le64(sg_dma_address(sg));
> >+		sge->cnt = cpu_to_le32(sg_dma_len(sg));
> >+		sge->flags = 0;
> >+		sge->flags = i < qc->n_elem - 1 ? 0 : cpu_to_le32(SGE_TRM);
> >+	}
> >+}
> >+
> >+static void sil24_qc_prep(struct ata_queued_cmd *qc)
> >+{
> >+	struct ata_port *ap = qc->ap;
> >+	struct sil24_port_priv *pp = ap->private_data;
> >+	struct sil24_cmd_block *cb = pp->cmd_block + qc->tag;
> >+	struct sil24_prb *prb = &cb->prb;
> >+
> >+	switch (qc->tf.protocol) {
> >+	case ATA_PROT_PIO:
> >+	case ATA_PROT_DMA:
> >+	case ATA_PROT_NODATA:
> >+		break;
> >+	default:
> >+		/* ATAPI isn't supported yet */
> >+		BUG();
> >+	}
> >+
> >+	ata_tf_to_fis(&qc->tf, prb->fis, 0);
> >+
> >+	if (qc->flags & ATA_QCFLAG_DMAMAP)
> >+		sil24_fill_sg(qc, cb);
> >+}
> >+
> >+static int sil24_qc_issue(struct ata_queued_cmd *qc)
> >+{
> >+	struct ata_port *ap = qc->ap;
> >+	struct sil24_port_priv *pp = ap->private_data;
> >+	dma_addr_t paddr = pp->cmd_block_dma + qc->tag * 
> >sizeof(*pp->cmd_block);
> >+
> >+	writel((u32)paddr, pp->port + PORT_CMD_ACTIVATE);
> >+	return 0;
> >+}
> >+
> >+static void sil24_irq_clear(struct ata_port *ap)
> >+{
> >+	/* unused */
> >+}
> 
> we need to fill this in.
> 

 AFAIK, this function will be called only from ata_device_add().  As
we call that function after resetting all status (including
interrupts), this callback isn't really useful in this case, I think.

> 
> >+static void sil24_reset_controller(struct ata_port *ap)
> >+{
> >+	struct sil24_port_priv *pp = ap->private_data;
> >+	void *port = pp->port;
> >+	int cnt;
> >+	u32 tmp;
> >+
> >+	printk(KERN_NOTICE DRV_NAME
> >+	       " ata%u: resetting controller...\n", ap->id);
> >+
> >+	/* Reset controller state.  Is this correct? */
> >+	writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
> >+	readl(port + PORT_CTRL_STAT);	/* sync */
> >+
> >+	/* Max ~100ms */
> >+	for (cnt = 0; cnt < 1000; cnt++) {
> >+		udelay(100);
> >+		tmp = readl(port + PORT_CTRL_STAT);
> >+		if (!(tmp & PORT_CS_DEV_RST))
> >+			break;
> >+	}
> 
> Use mdelay.  We should be able to sleep here?
> 
> Either way, we want to avoid long delays like this.
> 

With NCQ-helpers changes, all errors are handled in EH thread (NCQ
device or not), so original rewritten driver uses msleep, but here we
cannot as sil24_host_intr calls this function on error (I know it's
bad... but AHCI does the same way currently).  And the reset procedure
is what I've found by trial & error, so I don't know what the upper
time limit is.  I can change it to cnt < 100, msleep(1) or whatever
else, as it's an arbitrary value anyway.  Again, more information
needed.

> 
> >+	if (tmp & PORT_CS_DEV_RST)
> >+		printk(KERN_ERR DRV_NAME
> >+		       " ata%u: failed to reset controller\n", ap->id);
> >+}
> >+
> >+static void sil24_eng_timeout(struct ata_port *ap)
> >+{
> >+	struct ata_queued_cmd *qc;
> >+
> >+	qc = ata_qc_from_tag(ap, ap->active_tag);
> >+	if (!qc) {
> >+		printk(KERN_ERR "ata%u: BUG: tiemout without command\n",
> >+		       ap->id);
> >+		return;
> >+	}
> >+
> >+	/*
> >+	 * hack alert!  We cannot use the supplied completion
> >+	 * function from inside the ->eh_strategy_handler() thread.
> >+	 * libata is the only user of ->eh_strategy_handler() in
> >+	 * any kernel, so the default scsi_done() assumes it is
> >+	 * not being called from the SCSI EH.
> >+	 */
> >+	printk(KERN_ERR "ata%u: command timeout\n", ap->id);
> >+	qc->scsidone = scsi_finish_command;
> >+	ata_qc_complete(qc, ATA_ERR);
> >+
> >+	sil24_reset_controller(ap);
> >+}
> >+
> >+static inline void sil24_host_intr(struct ata_port *ap)
> >+{
> >+	struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
> >+	struct sil24_port_priv *pp = ap->private_data;
> >+	void *port = pp->port;
> >+	u32 slot_stat;
> >+
> >+	slot_stat = readl(port + PORT_SLOT_STAT);
> >+	if (!(slot_stat & HOST_SSTAT_ATTN)) {
> >+		if (qc)
> >+			ata_qc_complete(qc, 0);
> >+	} else {
> >+		u32 irq_stat, cmd_err, sstatus, serror;
> >+
> >+		irq_stat = readl(port + PORT_IRQ_STAT);
> >+		cmd_err = readl(port + PORT_CMD_ERR);
> >+		sstatus = readl(port + PORT_SSTATUS);
> >+		serror = readl(port + PORT_SERROR);
> >+
> >+		/* Clear IRQ/errors */
> >+		writel(irq_stat, port + PORT_IRQ_STAT);
> >+		if (cmd_err)
> >+			writel(cmd_err, port + PORT_CMD_ERR);
> >+		if (serror)
> >+			writel(serror, port + PORT_SERROR);
> >+
> >+		printk(KERN_ERR DRV_NAME " ata%u: error interrupt on 
> >port%d\n"
> >+		       "  stat=0x%x irq=0x%x cmd_err=%d sstatus=0x%x 
> >serror=0x%x\n",
> >+		       ap->id, ap->port_no, slot_stat, irq_stat, cmd_err, 
> >sstatus, serror);
> >+
> >+		if (qc)
> >+			ata_qc_complete(qc, ATA_ERR);
> >+
> >+		sil24_reset_controller(ap);
> >+	}
> >+}
> 
> Two comments:
> 1) The "OK" test for command completion seems quite fragile.  I think 
> the code may be -assuming- a command is completed.  I'll have to check 
> the docs to see if HOST_IRQ_STAT + no-other-events truly guarantees an 
> indication of command completion.
> 

 It's from the preview driver.  DOCS, PLEASE. :-)

> 2) Error handling should be moved out-of-line, in a separate function.

 Sure.

> 
> >+static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct 
> >pt_regs *regs)
> >+{
> >+	struct ata_host_set *host_set = dev_instance;
> >+	struct sil24_host_priv *hpriv = host_set->private_data;
> >+	unsigned handled = 0;
> >+	u32 status;
> >+	int i;
> >+
> >+	status = readl(hpriv->host_base + HOST_IRQ_STAT);
> >+
> >+	if (!(status & IRQ_STAT_4PORTS))
> >+		goto out;
> 
> also check for status==0xffffffff to indicate PCI bus fault, or hardware 
> unplug.
> 

 Okay.  Hmmm... Out of curiosity, are we supposed to do this kind of
test in all drivers?  Can you please point me to information regarding
this?

> 
> >+	spin_lock(&host_set->lock);
> >+
> >+	for (i = 0; i < host_set->n_ports; i++)
> >+		if (status & (1 << i)) {
> >+			struct ata_port *ap = host_set->ports[i];
> >+			if (ap && !(ap->flags & ATA_FLAG_PORT_DISABLED))
> >+				sil24_host_intr(host_set->ports[i]);
> >+			else {
> >+				u32 tmp;
> >+				printk(KERN_WARNING DRV_NAME
> >+				       ": spurious interrupt from port 
> >%d\n", i);
> >+				tmp = readl(hpriv->host_base + HOST_CTRL);
> >+				tmp &= ~(1 << i);
> >+				writel(tmp, hpriv->host_base + HOST_CTRL);
> 
> add a comment describing what these three lines of code do
> 

 The code disables interrupt for the port which has generated spurious
interrupt.  The original code just printed a message and told IRQ
subsystem that it isn't ours, possibly causing "nobody cared" error.
So, this piece of code is what I've added.  I think I'll enable IRQs
just for enabled ports and remove the disabling code here.

> 
> >+			}
> >+			handled++;
> >+		}
> >+
> >+	spin_unlock(&host_set->lock);
> >+ out:
> >+	return IRQ_RETVAL(handled);
> >+}
> >+
> >+static int sil24_port_start(struct ata_port *ap)
> >+{
> >+	struct device *dev = ap->host_set->dev;
> >+	struct sil24_host_priv *hpriv = ap->host_set->private_data;
> >+	struct sil24_port_priv *pp;
> >+	struct sil24_cmd_block *cb;
> >+	size_t cb_size = sizeof(*cb);
> >+	dma_addr_t cb_dma;
> >+
> >+	pp = kmalloc(sizeof(*pp), GFP_KERNEL);
> >+	if (!pp)
> >+		return -ENOMEM;
> >+	memset(pp, 0, sizeof(*pp));
> >+
> >+	cb = dma_alloc_coherent(dev, cb_size, &cb_dma, GFP_KERNEL);
> >+	if (!cb) {
> >+		kfree(pp);
> >+		return -ENOMEM;
> >+	}
> >+	memset(cb, 0, cb_size);
> >+
> >+	pp->port = hpriv->port_base + ap->port_no * PORT_REGS_SIZE;
> >+	pp->cmd_block = cb;
> >+	pp->cmd_block_dma = cb_dma;
> >+
> >+	ap->private_data = pp;
> >+
> >+	return 0;
> >+}
> >+
> >+static void sil24_port_stop(struct ata_port *ap)
> >+{
> >+	struct device *dev = ap->host_set->dev;
> >+	struct sil24_port_priv *pp = ap->private_data;
> >+	size_t cb_size = sizeof(*pp->cmd_block);
> >+
> >+	dma_free_coherent(dev, cb_size, pp->cmd_block, pp->cmd_block_dma);
> >+	kfree(pp);
> >+}
> >+
> >+static void sil24_host_stop(struct ata_host_set *host_set)
> >+{
> >+	struct sil24_host_priv *hpriv = host_set->private_data;
> >+
> >+	iounmap(hpriv->host_base);
> >+	iounmap(hpriv->port_base);
> >+	kfree(hpriv);
> >+}
> >+
> >+static int sil24_init_one(struct pci_dev *pdev, const struct 
> >pci_device_id *ent)
> >+{
> >+	static int printed_version = 0;
> >+	unsigned int board_id = (unsigned int)ent->driver_data;
> >+	struct ata_probe_ent *probe_ent = NULL;
> >+	struct sil24_host_priv *hpriv = NULL;
> >+	void *host_base = NULL, *port_base = NULL;
> >+	int i, rc;
> >+
> >+	if (!printed_version++)
> >+		printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
> >+
> >+	rc = pci_enable_device(pdev);
> >+	if (rc)
> >+		return rc;
> >+
> >+	rc = pci_request_regions(pdev, DRV_NAME);
> >+	if (rc)
> >+		goto out_disable;
> >+
> >+	rc = -ENOMEM;
> >+	/* ioremap mmio registers */
> >+	host_base = ioremap(pci_resource_start(pdev, 0),
> >+			    pci_resource_len(pdev, 0));
> >+	if (!host_base)
> >+		goto out_free;
> >+	port_base = ioremap(pci_resource_start(pdev, 2),
> >+			    pci_resource_len(pdev, 2));
> >+	if (!port_base)
> >+		goto out_free;
> >+	/* allocate & init probe_ent and hpriv */
> >+	probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
> >+	if (!probe_ent)
> >+		goto out_free;
> >+
> >+	hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
> >+	if (!hpriv)
> >+		goto out_free;
> >+
> >+	memset(probe_ent, 0, sizeof(*probe_ent));
> >+	probe_ent->dev = pci_dev_to_dev(pdev);
> >+	INIT_LIST_HEAD(&probe_ent->node);
> >+
> >+	probe_ent->sht		= sil24_port_info[board_id].sht;
> >+	probe_ent->host_flags	= sil24_port_info[board_id].host_flags;
> >+	probe_ent->pio_mask	= sil24_port_info[board_id].pio_mask;
> >+	probe_ent->udma_mask	= sil24_port_info[board_id].udma_mask;
> >+	probe_ent->port_ops	= sil24_port_info[board_id].port_ops;
> >+	probe_ent->n_ports	= (board_id == BID_SIL3124) ? 4 : 2;
> >+
> >+	probe_ent->irq = pdev->irq;
> >+	probe_ent->irq_flags = SA_SHIRQ;
> >+	probe_ent->mmio_base = port_base;
> >+	probe_ent->private_data = hpriv;
> >+
> >+	memset(hpriv, 0, sizeof(*hpriv));
> >+	hpriv->host_base = host_base;
> >+	hpriv->port_base = port_base;
> >+
> >+	/*
> >+	 * Configure the device
> >+	 */
> >+	/*
> >+	 * FIXME: This device is certainly 64-bit capable.  We just
> >+	 * don't know how to use it.  After fixing 32bit activation in
> >+	 * this function, enable 64bit masks here.
> >+	 */
> 
> elaboration?
> 

 In the preview driver, while initializing port, PORT_CSR_M_32BIT_ACTV
is written to CtrlSetStatus.  I don't know what it really means and
have no means of testing it as I don't have any machine with more than
4gigs of memory.  So, I wasn't really sure if that 32 bit applies only
to consistent memory (maybe it's 32bit activation address?) or also to
DMA buffers.  That's why I just used 32bit masks for both of them.
Again, with docs, this should be easy to fix.

> 
> >+	rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
> >+	if (rc) {
> >+		printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n",
> >+		       pci_name(pdev));
> >+		goto out_free;
> >+	}
> >+	rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
> >+	if (rc) {
> >+		printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable 
> >failed\n",
> >+		       pci_name(pdev));
> >+		goto out_free;
> >+	}
> >+
> >+	/* GPIO off */
> >+	writel(0, host_base + HOST_FLASH_CMD);
> >+
> >+	/* Mask interrupts during initialization */
> >+	writel(0, host_base + HOST_CTRL);
> 
> add a readl() to flush this write out to the PCI bus ("PCI posting")
> 

 Sure.  And, out of curiosity, isn't sync unnecessary unless we're
gonna perform some kind of timed waiting following it?  We don't have
any timing requirement after above interrupt masking, do we?

> 
> >+	for (i = 0; i < probe_ent->n_ports; i++) {
> >+		void *port = port_base + i * PORT_REGS_SIZE;
> >+		unsigned long portu = (unsigned long)port;
> >+		u32 tmp;
> >+		int cnt;
> >+
> >+		probe_ent->port[i].cmd_addr = portu + PORT_TF;
> >+		probe_ent->port[i].ctl_addr = portu + PORT_TF + 0xa;
> >+		probe_ent->port[i].altstatus_addr = portu + PORT_TF + 0xa;
> >+		probe_ent->port[i].scr_addr = portu + PORT_SCONTROL;
> >+
> >+		ata_std_ports(&probe_ent->port[i]);
> >+
> >+		/* Initial PHY setting */
> >+		writel(0x20c, port + PORT_PHY_CFG);
> >+
> >+		/* Clear port RST */
> >+		tmp = readl(port + PORT_CTRL_STAT);
> >+		if (tmp & PORT_CS_PORT_RST) {
> >+			writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
> >+			readl(port + PORT_CTRL_STAT);	/* sync */
> >+			for (cnt = 0; cnt < 10; cnt++) {
> >+				msleep(10);
> >+				tmp = readl(port + PORT_CTRL_STAT);
> >+				if (!(tmp & PORT_CS_PORT_RST))
> >+					break;
> >+			}
> >+			if (tmp & PORT_CS_PORT_RST)
> >+				printk(KERN_ERR DRV_NAME
> >+				       "(%s): failed to clear port RST\n",
> >+				       pci_name(pdev));
> >+		}
> 
> this is already done in sata_phy_reset()?
> 

 I don't know what PORT_RST does here.  This is what the preview
driver does, so...  If it's the same thing as phy reset using SATA
SCR, we can remove it.  Again, docs needed. 

> 
> >+		/* Zero error counters. */
> >+		writel(0x8000, port + PORT_DECODE_ERR_THRESH);
> >+		writel(0x8000, port + PORT_CRC_ERR_THRESH);
> >+		writel(0x8000, port + PORT_HSHK_ERR_THRESH);
> >+		writel(0x0000, port + PORT_DECODE_ERR_CNT);
> >+		writel(0x0000, port + PORT_CRC_ERR_CNT);
> >+		writel(0x0000, port + PORT_HSHK_ERR_CNT);
> >+
> >+		/* FIXME: 32bit activation? */
> >+		writel(0, port + PORT_ACTIVATE_UPPER_ADDR);
> >+		writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_STAT);
> >+
> >+		/* Configure interrupts */
> >+		writel(0xffff, port + PORT_IRQ_ENABLE_CLR);
> >+		writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | PORT_IRQ_SDB_FIS,
> >+		       port + PORT_IRQ_ENABLE_SET);
> >+
> >+		/* Clear interrupts */
> >+		writel(0x0fff0fff, port + PORT_IRQ_STAT);
> >+		writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
> >+	}
> >+
> >+	/* Turn on interrupts */
> >+	writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
> >+
> >+	pci_set_master(pdev);
> >+
> >+	ata_device_add(probe_ent);
> 
> as with other drivers, add FIXME comment, indicating that the return 
> value should be checked, and if ata_device_add() failed.
> 

 Sure.

> 
> >+	kfree(probe_ent);
> >+	return 0;
> >+
> >+ out_free:
> >+	if (host_base)
> >+		iounmap(host_base);
> >+	if (port_base)
> >+		iounmap(port_base);
> >+	kfree(probe_ent);
> >+	kfree(hpriv);
> >+	pci_release_regions(pdev);
> >+ out_disable:
> >+	pci_disable_device(pdev);
> >+	return rc;
> >+}
> >+
> >+static int __init sil24_init(void)
> >+{
> >+	return pci_module_init(&sil24_pci_driver);
> >+}
> >+
> >+static void __exit sil24_exit(void)
> >+{
> >+	pci_unregister_driver(&sil24_pci_driver);
> >+}
> >+
> >+MODULE_AUTHOR("Tejun Heo");
> >+MODULE_DESCRIPTION("Silicon Image 3124/3132 SATA low-level driver");
> >+MODULE_LICENSE("GPL");
> >+MODULE_DEVICE_TABLE(pci, sil24_pci_tbl);
> >+
> >+module_init(sil24_init);
> >+module_exit(sil24_exit);
> >

-- 
tejun

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-07-30  8:17   ` Tejun Heo
@ 2005-08-02 22:56     ` Edward Falk
  2005-08-03  4:09       ` Jeff Garzik
  2005-08-04  8:51       ` Tejun Heo
  0 siblings, 2 replies; 13+ messages in thread
From: Edward Falk @ 2005-08-02 22:56 UTC (permalink / raw)
  To: Tejun Heo; +Cc: linux-ide, Linux Kernel, Carlos Pardo, Jeff Garzik

Hi Tejun; I'm the guy at Google working on SATA drivers (port 
multipliers right now).  As soon as I can (next week perhaps, I'll start 
looking at the driver you wrote.  From what I can see, it looks quite good.



>>>+
>>>+static u8 sil24_check_status(struct ata_port *ap)
>>>+{
>>>+	return ATA_DRDY;
>>>+}
>>>+
>>>+static u8 sil24_check_err(struct ata_port *ap)
>>>+{
>>>+	return 0;
>>>+}
>>
>>For these two functions, we need to grab the values for these registers 
>>from the D2H Register FIS.  These should not be constant values, they 
>>are used in probing.
>>
> 
>  Sadly I don't know where the values are.  The original driver seems
> to assume that taskfile registers are overlayed with PORT_PRB, but
> they are not.  Values are bogus there.  Again, in need of hardware
> docs here.

The original driver is broken.  Taskfile registers have to be read back 
from the returned FIS block after a command completion.

Hmmmm, perhaps libata should provide an ata_fis_to_tf() function that 
examines a FIS block, confirms that it's a device-to-host type FIS, and 
read the taskfile registers back out.



> 
>  The original rewritten sil24 driver against NCQ helpers had simple
> status register emulation using normal/error completion interrupt.  I
> don't know why I stripped that while converting the driver over
> vanilla libata (sorry).  I'll restore it.  It's not good, but it
> correctly indicates error on error.

It's still better than what we have.



>>>+static void sil24_phy_reset(struct ata_port *ap)
>>>+{
>>>+	__sata_phy_reset(ap);
>>>+	/*
>>>+	 * No ATAPI yet.  Just unconditionally indicate ATA device.
>>>+	 * If ATAPI device is attached, it will fail ATA_CMD_ID_ATA
>>>+	 * and libata core will ignore the device.
>>>+	 */
>>>+	if (!(ap->flags & ATA_FLAG_PORT_DISABLED))
>>>+		ap->device[0].class = ATA_DEV_ATA;
>>>+}
>>
>>We need to use the standard probing code to figure this out. 
>>ata_dev_classify(), etc.
>>
> 
> 
>  Again, the problem here is that I don't know how to access the
> signature values after reset.

Again, you would need to fetch them from the returned FIS structure. 
Here's a code fragment derived from SiI's issue_soft_reset() function:

	struct Port_Registers *port_base = yadayada;
	struct sil_port_priv *pp = ap->private_data;
	struct Port_Registers *PR;	/* in memory */
	dma_addr_t paddr = pp->pc_dma; /* physical address base */

	PR = (struct Port_Registers *) (&pp->pc->mregs);
	port_base = yadayada;
	slot = 0;
	slotp = &PR->Slot[slot];
	memset(&slotp->Prb, 0, sizeof(slotp->Prb));
	slotp->Prb.Control = 0x80;		/* soft reset */
	slotp->Prb.FisType == 0;
	writel(paddr, &port_base->CmdActivate[slot].s.ActiveLow);
	if (!sil_wait_for_completion(port_base)) {
		/* timeout or error */
		return ATA_DEV_NONE;
	} else {
		/* Examine slot for taskfile registers */
		slotp = port_base->Slot[slot];
		if (slotp->Prb.FisType != 0x34 &&
		    slotp->Prb.FisType != 0x5F) {
			/* WTF?  Wrong FIS Type */
			return ATA_DEV_NONE;
		}
		if (slotp->Prb.CylLow == 0 &&
		    slotp->Prb.CylHigh == 0)
			return ATA_DEV_ATA;
		if (slotp->Prb.CylLow == 0x14 &&
		    slotp->Prb.CylHigh == 0xEB)
			return ATA_DEV_ATAPI;
		if (slotp->Prb.CylLow == 0x69 &&
		    slotp->Prb.CylHigh == 0x96)
			return ATA_DEV_PORT_MULTIPLIER;
		printk(KERN_WARN "unknown ATA device signature %x.%x\n",
			slotp->Prb.CylLow, slotp->Prb.CylHigh);
		return ATA_DEV_NONE;
	}
	


>>>+static void sil24_irq_clear(struct ata_port *ap)
>>>+{
>>>+	/* unused */
>>>+}
>>
>>we need to fill this in.

I think this will work (adapted from sil_interrupt():

static void sil_irq_clear(struct ata_port *ap)
{
         struct sil_port_priv *pp = ap->private_data;
         struct Port_Registers *port_base = pp->pregs;
	unsigned long port_int;

	port_int  = readl((void *)&port_base->IntStatus);
	writel(port_int, &port_base->IntStatus);
}

I'm assuming that this entry point is expected to clear all interrupts, no?



>>>+	/* Max ~100ms */
>>>+	for (cnt = 0; cnt < 1000; cnt++) {
>>>+		udelay(100);
>>>+		tmp = readl(port + PORT_CTRL_STAT);
>>>+		if (!(tmp & PORT_CS_DEV_RST))
>>>+			break;
>>>+	}
>>
>>Use mdelay.  We should be able to sleep here?
>>
>>Either way, we want to avoid long delays like this.

Does mdelay() sleep?  I thought it just called udelay().

At any rate, I think 100ms is only the worst-case delay.

Is it safe to call msleep() at boot time?




>>>+	/* GPIO off */
>>>+	writel(0, host_base + HOST_FLASH_CMD);
>>>+
>>>+	/* Mask interrupts during initialization */
>>>+	writel(0, host_base + HOST_CTRL);
>>
>>add a readl() to flush this write out to the PCI bus ("PCI posting")
>>
> 
> 
>  Sure.  And, out of curiosity, isn't sync unnecessary unless we're
> gonna perform some kind of timed waiting following it?  We don't have
> any timing requirement after above interrupt masking, do we?


I think we're ok here; the code reads PORT_CTRL_STAT a few lines down; 
that will flush the write.  I don't think there's a race condition involved.





OK, a couple of questions of my own:

Any documentation on NCQ helpers or other related kernel code?

What's the proper way to implement very long delays.  I want to 
implement staggered disk spinup in my port multiplier code.  Would 
mdelay(5000) be terribly anti-social?  I'm guessing yes, but then, how 
do I ensure that no process tries to access the disk until I've spun it up?

Port multiplier spec says that the GSCR[2] register indicates how many 
ports the port multiplier supports.  On my 5-port device, this register 
reads back as six.  Are they counting the control port, or is this 
device defective?

What causes a disk to spin up?  Is it the COMRESET?  Soft reset?  In 
either case, it sounds like I need to spin up a drive just to detect it. 
  Not optimal.

	-ed falk

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-08-02 22:56     ` Edward Falk
@ 2005-08-03  4:09       ` Jeff Garzik
  2005-08-03 14:28         ` Tejun Heo
  2005-08-04  8:51       ` Tejun Heo
  1 sibling, 1 reply; 13+ messages in thread
From: Jeff Garzik @ 2005-08-03  4:09 UTC (permalink / raw)
  To: Edward Falk; +Cc: Tejun Heo, linux-ide, Linux Kernel, Carlos Pardo

Edward Falk wrote:
> Hi Tejun; I'm the guy at Google working on SATA drivers (port 
> multipliers right now).  As soon as I can (next week perhaps, I'll start 
> looking at the driver you wrote.  From what I can see, it looks quite good.
> 
> 
> 
>>>> +
>>>> +static u8 sil24_check_status(struct ata_port *ap)
>>>> +{
>>>> +    return ATA_DRDY;
>>>> +}
>>>> +
>>>> +static u8 sil24_check_err(struct ata_port *ap)
>>>> +{
>>>> +    return 0;
>>>> +}
>>>
>>>
>>> For these two functions, we need to grab the values for these 
>>> registers from the D2H Register FIS.  These should not be constant 
>>> values, they are used in probing.
>>>
>>
>>  Sadly I don't know where the values are.  The original driver seems
>> to assume that taskfile registers are overlayed with PORT_PRB, but
>> they are not.  Values are bogus there.  Again, in need of hardware
>> docs here.
> 
> 
> The original driver is broken.  Taskfile registers have to be read back 
> from the returned FIS block after a command completion.

Correct.


> Hmmmm, perhaps libata should provide an ata_fis_to_tf() function that 
> examines a FIS block, confirms that it's a device-to-host type FIS, and 
> read the taskfile registers back out.

Such as ata_tf_from_fis() in libata-core.c? :)


>>  The original rewritten sil24 driver against NCQ helpers had simple
>> status register emulation using normal/error completion interrupt.  I
>> don't know why I stripped that while converting the driver over
>> vanilla libata (sorry).  I'll restore it.  It's not good, but it
>> correctly indicates error on error.
> 
> 
> It's still better than what we have.
> 
> 
> 
>>>> +static void sil24_phy_reset(struct ata_port *ap)
>>>> +{
>>>> +    __sata_phy_reset(ap);
>>>> +    /*
>>>> +     * No ATAPI yet.  Just unconditionally indicate ATA device.
>>>> +     * If ATAPI device is attached, it will fail ATA_CMD_ID_ATA
>>>> +     * and libata core will ignore the device.
>>>> +     */
>>>> +    if (!(ap->flags & ATA_FLAG_PORT_DISABLED))
>>>> +        ap->device[0].class = ATA_DEV_ATA;
>>>> +}
>>>
>>>
>>> We need to use the standard probing code to figure this out. 
>>> ata_dev_classify(), etc.
>>>
>>
>>
>>  Again, the problem here is that I don't know how to access the
>> signature values after reset.
> 
> 
> Again, you would need to fetch them from the returned FIS structure. 

Correct.


>>>> +static void sil24_irq_clear(struct ata_port *ap)
>>>> +{
>>>> +    /* unused */
>>>> +}
>>>
>>>
>>> we need to fill this in.
> 
> 
> I think this will work (adapted from sil_interrupt():
> 
> static void sil_irq_clear(struct ata_port *ap)
> {
>         struct sil_port_priv *pp = ap->private_data;
>         struct Port_Registers *port_base = pp->pregs;
>     unsigned long port_int;
> 
>     port_int  = readl((void *)&port_base->IntStatus);
>     writel(port_int, &port_base->IntStatus);
> }
> 
> I'm assuming that this entry point is expected to clear all interrupts, no?

Correct.


>>>> +    /* Max ~100ms */
>>>> +    for (cnt = 0; cnt < 1000; cnt++) {
>>>> +        udelay(100);
>>>> +        tmp = readl(port + PORT_CTRL_STAT);
>>>> +        if (!(tmp & PORT_CS_DEV_RST))
>>>> +            break;
>>>> +    }
>>>
>>>
>>> Use mdelay.  We should be able to sleep here?
>>>
>>> Either way, we want to avoid long delays like this.
> 
> 
> Does mdelay() sleep?  I thought it just called udelay().

mdelay() does not sleep.


> At any rate, I think 100ms is only the worst-case delay.
> 
> Is it safe to call msleep() at boot time?

Yes.


>>>> +    /* GPIO off */
>>>> +    writel(0, host_base + HOST_FLASH_CMD);
>>>> +
>>>> +    /* Mask interrupts during initialization */
>>>> +    writel(0, host_base + HOST_CTRL);
>>>
>>>
>>> add a readl() to flush this write out to the PCI bus ("PCI posting")
>>>
>>
>>
>>  Sure.  And, out of curiosity, isn't sync unnecessary unless we're
>> gonna perform some kind of timed waiting following it?  We don't have
>> any timing requirement after above interrupt masking, do we?
> 
> 
> 
> I think we're ok here; the code reads PORT_CTRL_STAT a few lines down; 
> that will flush the write.  I don't think there's a race condition 
> involved.

No race condition.  Typically there is often a mistaken assumption that

	writel(...)
	udelay(...)

will accomplish the desired effect.  Due to posted writes, the write may 
be posted to the PCI device after some delay, such that, the udelay() 
and the posted write execute concurrently, skewing the desired timing 
effect.


> OK, a couple of questions of my own:
> 
> Any documentation on NCQ helpers or other related kernel code?
> 
> What's the proper way to implement very long delays.  I want to 
> implement staggered disk spinup in my port multiplier code.  Would 
> mdelay(5000) be terribly anti-social?  I'm guessing yes, but then, how 

Yes :)  msleep(5000) is far less anti-social, but still yucky...


> do I ensure that no process tries to access the disk until I've spun it up?

The probe code is typically serialized, to prevent problems like this. 
Specifically, you'll have to be aware of when libata adds devices to the 
SCSI layer.


> Port multiplier spec says that the GSCR[2] register indicates how many 
> ports the port multiplier supports.  On my 5-port device, this register 
> reads back as six.  Are they counting the control port, or is this 
> device defective?

Good question!  ;-)


> What causes a disk to spin up?  Is it the COMRESET?  Soft reset?  In 
> either case, it sounds like I need to spin up a drive just to detect it. 
>  Not optimal.

It depends.  COMRESET or power management can do it.  This stuff is 
documented in the SATA docs.  This is more on the device end than the 
host end, but in general you must be aware of
* host controller power state(s)
* SATA bus power state(s)
* SATA device power state(s)

	Jeff



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-08-03  4:09       ` Jeff Garzik
@ 2005-08-03 14:28         ` Tejun Heo
  2005-08-04  2:20           ` Tejun Heo
  0 siblings, 1 reply; 13+ messages in thread
From: Tejun Heo @ 2005-08-03 14:28 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Edward Falk, linux-ide, Linux Kernel, Carlos Pardo

 Greetings, Edward and Jeff.

 Jeff, as my previous patchset against sil24 driver hasn't been
accepted yet, and it seems that most of them can be done better with
info from Edward, please ignore the patchset.  I'll post a new
patchset tomorrow (hopefully).  Sorry about the hassle.

On Wed, Aug 03, 2005 at 12:09:05AM -0400, Jeff Garzik wrote:
> Edward Falk wrote:
> >Hi Tejun; I'm the guy at Google working on SATA drivers (port 
> >multipliers right now). 

 Great.

> > As soon as I can (next week perhaps, I'll start 
> >looking at the driver you wrote.  From what I can see, it looks quite good.
> >

 Thanks a lot.  I'll try to make my driver saner by then.

> >
> >
> >>>>+
> >>>>+static u8 sil24_check_status(struct ata_port *ap)
> >>>>+{
> >>>>+    return ATA_DRDY;
> >>>>+}
> >>>>+
> >>>>+static u8 sil24_check_err(struct ata_port *ap)
> >>>>+{
> >>>>+    return 0;
> >>>>+}
> >>>
> >>>
> >>>For these two functions, we need to grab the values for these 
> >>>registers from the D2H Register FIS.  These should not be constant 
> >>>values, they are used in probing.
> >>>
> >>
> >> Sadly I don't know where the values are.  The original driver seems
> >>to assume that taskfile registers are overlayed with PORT_PRB, but
> >>they are not.  Values are bogus there.  Again, in need of hardware
> >>docs here.
> >
> >
> >The original driver is broken.  Taskfile registers have to be read back 
> >from the returned FIS block after a command completion.
> 
> Correct.
> 

 Ahh.. Thanks a lot.  I thought PRB was used just for command issues.
I'll fix the driver ASAP.

> 
> >Hmmmm, perhaps libata should provide an ata_fis_to_tf() function that 
> >examines a FIS block, confirms that it's a device-to-host type FIS, and 
> >read the taskfile registers back out.
> 
> Such as ata_tf_from_fis() in libata-core.c? :)
> 
> 
> >> The original rewritten sil24 driver against NCQ helpers had simple
> >>status register emulation using normal/error completion interrupt.  I
> >>don't know why I stripped that while converting the driver over
> >>vanilla libata (sorry).  I'll restore it.  It's not good, but it
> >>correctly indicates error on error.
> >
> >
> >It's still better than what we have.
> >

 Hmmm.. Now that I know how to access the D2H FIS, I can properly
emulate status and error registers from interrupt handler.  I'll
submit a patch soon.

> >
> >
> >>>>+static void sil24_phy_reset(struct ata_port *ap)
> >>>>+{
> >>>>+    __sata_phy_reset(ap);
> >>>>+    /*
> >>>>+     * No ATAPI yet.  Just unconditionally indicate ATA device.
> >>>>+     * If ATAPI device is attached, it will fail ATA_CMD_ID_ATA
> >>>>+     * and libata core will ignore the device.
> >>>>+     */
> >>>>+    if (!(ap->flags & ATA_FLAG_PORT_DISABLED))
> >>>>+        ap->device[0].class = ATA_DEV_ATA;
> >>>>+}
> >>>
> >>>
> >>>We need to use the standard probing code to figure this out. 
> >>>ata_dev_classify(), etc.
> >>>
> >>
> >>
> >> Again, the problem here is that I don't know how to access the
> >>signature values after reset.
> >
> >
> >Again, you would need to fetch them from the returned FIS structure. 
> 
> Correct.
> 

 Again, thanks.  ;-)

> 
> >>>>+static void sil24_irq_clear(struct ata_port *ap)
> >>>>+{
> >>>>+    /* unused */
> >>>>+}
> >>>
> >>>
> >>>we need to fill this in.
> >
> >
> >I think this will work (adapted from sil_interrupt():
> >
> >static void sil_irq_clear(struct ata_port *ap)
> >{
> >        struct sil_port_priv *pp = ap->private_data;
> >        struct Port_Registers *port_base = pp->pregs;
> >    unsigned long port_int;
> >
> >    port_int  = readl((void *)&port_base->IntStatus);
> >    writel(port_int, &port_base->IntStatus);
> >}
> >
> >I'm assuming that this entry point is expected to clear all interrupts, no?
> 
> Correct.
> 

 I'll verify with the error register clearing part of the original
driver and submit a patch.

> 
> >>>>+    /* Max ~100ms */
> >>>>+    for (cnt = 0; cnt < 1000; cnt++) {
> >>>>+        udelay(100);
> >>>>+        tmp = readl(port + PORT_CTRL_STAT);
> >>>>+        if (!(tmp & PORT_CS_DEV_RST))
> >>>>+            break;
> >>>>+    }
> >>>
> >>>
> >>>Use mdelay.  We should be able to sleep here?
> >>>
> >>>Either way, we want to avoid long delays like this.
> >
> >
> >Does mdelay() sleep?  I thought it just called udelay().
> 
> mdelay() does not sleep.
> 
> 
> >At any rate, I think 100ms is only the worst-case delay.
> >
> >Is it safe to call msleep() at boot time?
> 
> Yes.
> 
> 
> >>>>+    /* GPIO off */
> >>>>+    writel(0, host_base + HOST_FLASH_CMD);
> >>>>+
> >>>>+    /* Mask interrupts during initialization */
> >>>>+    writel(0, host_base + HOST_CTRL);
> >>>
> >>>
> >>>add a readl() to flush this write out to the PCI bus ("PCI posting")
> >>>
> >>
> >>
> >> Sure.  And, out of curiosity, isn't sync unnecessary unless we're
> >>gonna perform some kind of timed waiting following it?  We don't have
> >>any timing requirement after above interrupt masking, do we?
> >
> >
> >
> >I think we're ok here; the code reads PORT_CTRL_STAT a few lines down; 
> >that will flush the write.  I don't think there's a race condition 
> >involved.
> 
> No race condition.  Typically there is often a mistaken assumption that
> 
> 	writel(...)
> 	udelay(...)
> 
> will accomplish the desired effect.  Due to posted writes, the write may 
> be posted to the PCI device after some delay, such that, the udelay() 
> and the posted write execute concurrently, skewing the desired timing 
> effect.
> 

 So... we don't really need flushing here, right?  No delay
requirement here.

> 
> >OK, a couple of questions of my own:
> >
> >Any documentation on NCQ helpers or other related kernel code?
> >

 Not yet.  I'm waiting for Jeff's comment before converting all
drivers and possibly writing up a document.  If Jeff doesn't like it,
I'll have to tear it down and do it some other way, so you better not
invest too much into the helpers yet.  However, the usage is really
simple, just take a look at interrupt and error hanndlers of ahci.c
driver.  Other NCQ enabled drivers shouldn't be very different.

> >What's the proper way to implement very long delays.  I want to 
> >implement staggered disk spinup in my port multiplier code.  Would 
> >mdelay(5000) be terribly anti-social?  I'm guessing yes, but then, how 
> 
> Yes :)  msleep(5000) is far less anti-social, but still yucky...
> 

 Heh... but w/ staggered spin-up, I don't think we have many other
choices.  Maybe it would be nice if we can do multi-threaded device
driver initialization with checkpoints to order necessary stuff...
BTW, doesn't the BIOS spin up drives as in SCSI HBAs?

> 
> >do I ensure that no process tries to access the disk until I've spun it up?
> 
> The probe code is typically serialized, to prevent problems like this. 
> Specifically, you'll have to be aware of when libata adds devices to the 
> SCSI layer.
> 
> 
> >Port multiplier spec says that the GSCR[2] register indicates how many 
> >ports the port multiplier supports.  On my 5-port device, this register 
> >reads back as six.  Are they counting the control port, or is this 
> >device defective?
> 
> Good question!  ;-)
> 

 Hmmm... that field is 4-bit wide, so, if the count includes the
control port, the actual maximum number of supported ports would be 14
instead of 15 as specified in the spec.  I guess the device is faulty.
And my spec (rev 1.1) says GSCR[2] 3-0: Number of exposed device
fan-out ports.  That's pretty clear, I think.

> 
> >What causes a disk to spin up?  Is it the COMRESET?  Soft reset?  In 
> >either case, it sounds like I need to spin up a drive just to detect it. 
> > Not optimal.
> 
> It depends.  COMRESET or power management can do it.  This stuff is 
> documented in the SATA docs.  This is more on the device end than the 
> host end, but in general you must be aware of
> * host controller power state(s)
> * SATA bus power state(s)
> * SATA device power state(s)
> 
> 	Jeff
> 

 Thanks, Edward & Jeff.

-- 
tejun

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-08-03 14:28         ` Tejun Heo
@ 2005-08-04  2:20           ` Tejun Heo
  2005-08-04 18:46             ` Edward Falk
  0 siblings, 1 reply; 13+ messages in thread
From: Tejun Heo @ 2005-08-04  2:20 UTC (permalink / raw)
  To: Edward Falk; +Cc: Jeff Garzik, linux-ide, Linux Kernel, Carlos Pardo

 Hello, Edward.

 One more question.

> > >
> > >I think this will work (adapted from sil_interrupt():
> > >
> > >static void sil_irq_clear(struct ata_port *ap)
> > >{
> > >        struct sil_port_priv *pp = ap->private_data;
> > >        struct Port_Registers *port_base = pp->pregs;
> > >    unsigned long port_int;
> > >
> > >    port_int  = readl((void *)&port_base->IntStatus);
> > >    writel(port_int, &port_base->IntStatus);
> > >}
> > >
> > >I'm assuming that this entry point is expected to clear all interrupts, no?
> > 
> > Correct.
> > 
> 
>  I'll verify with the error register clearing part of the original
> driver and submit a patch.
> 

 Command completion interrupt is automatcally cleared by reading
PORT_SLOT_STAT register (SlotStatus in the original driver), and error
registers should be manually cleared by writing to PORT_IRQ_STAT
(IntStatus).

 I agree that above code should clear both.  Just wanna verify.  Have
you tested it and/or do you have any information confirming this?  If
we don't have any further info, I think we should read PORT_SLOT_STAT
before clearing PORT_IRQ_STAT to be on the safe side.

 Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-08-02 22:56     ` Edward Falk
  2005-08-03  4:09       ` Jeff Garzik
@ 2005-08-04  8:51       ` Tejun Heo
  2005-08-04 18:51         ` Edward Falk
  1 sibling, 1 reply; 13+ messages in thread
From: Tejun Heo @ 2005-08-04  8:51 UTC (permalink / raw)
  To: Edward Falk; +Cc: linux-ide, Linux Kernel, Carlos Pardo, Jeff Garzik

 Hi, again, Edward.

 Bad news...

On Tue, Aug 02, 2005 at 03:56:05PM -0700, Edward Falk wrote:
> 
> Again, you would need to fetch them from the returned FIS structure. 
> Here's a code fragment derived from SiI's issue_soft_reset() function:
> 
> 	struct Port_Registers *port_base = yadayada;
> 	struct sil_port_priv *pp = ap->private_data;
> 	struct Port_Registers *PR;	/* in memory */
> 	dma_addr_t paddr = pp->pc_dma; /* physical address base */
> 
> 	PR = (struct Port_Registers *) (&pp->pc->mregs);
> 	port_base = yadayada;
> 	slot = 0;
> 	slotp = &PR->Slot[slot];
> 	memset(&slotp->Prb, 0, sizeof(slotp->Prb));
> 	slotp->Prb.Control = 0x80;		/* soft reset */
> 	slotp->Prb.FisType == 0;
> 	writel(paddr, &port_base->CmdActivate[slot].s.ActiveLow);
> 	if (!sil_wait_for_completion(port_base)) {
> 		/* timeout or error */
> 		return ATA_DEV_NONE;
> 	} else {
> 		/* Examine slot for taskfile registers */
> 		slotp = port_base->Slot[slot];
> 		if (slotp->Prb.FisType != 0x34 &&
> 		    slotp->Prb.FisType != 0x5F) {
> 			/* WTF?  Wrong FIS Type */
> 			return ATA_DEV_NONE;
> 		}
> 		if (slotp->Prb.CylLow == 0 &&
> 		    slotp->Prb.CylHigh == 0)
> 			return ATA_DEV_ATA;
> 		if (slotp->Prb.CylLow == 0x14 &&
> 		    slotp->Prb.CylHigh == 0xEB)
> 			return ATA_DEV_ATAPI;
> 		if (slotp->Prb.CylLow == 0x69 &&
> 		    slotp->Prb.CylHigh == 0x96)
> 			return ATA_DEV_PORT_MULTIPLIER;
> 		printk(KERN_WARN "unknown ATA device signature %x.%x\n",
> 			slotp->Prb.CylLow, slotp->Prb.CylHigh);
> 		return ATA_DEV_NONE;
> 	}

 Reading FIS off the cotroller's PRB doesn't seem to work.  I've added
the following code to the preview sil24 driver after completing soft
reset.

	writel((u32) paddr, &port_base->CmdActivate[0].s.ActiveLow);
	if (!sil_wait_for_completion(port_base)) {
		struct Port_Request_Block *prb = &port_base->Slot[0].Prb;
		printk("POSTRESET c:p=%04x:%04x rc=%08x\n"
		       "fis=%08x:%08x:%08x:%08x\n",
		       prb->Control, prb->Protocol, prb->ReceiveCount,
		       prb->AsDword_1, prb->AsDword_2,
		       prb->AsDword_3, prb->AsDword_4);
		return ATA_DEV_ATA; 
	} else	{
		 printk("sata_sil24: Port Not Ready status=%x\n",
				port_base->CtrlSetStatus);
		return ATA_DEV_NONE;
	}

 And, this is what I get.

sata_sil24: Issuing soft reset to phys=1f4f2000
POSTRESET c:p=0080:0000 rc=00000000
fis=01500000:00000001:00000000:00000001

 Which, apparantly, isn't a valid D2H FIS.

 Here are some more dumps of contoller's PRB from my sil24 driver.
PRE ISSUE is the values of PRB regs before issueing a command.  CMD is
the command to issue in the consistent memory.  POST ISSUE and POST
INTR are respectively after issueing a command and processing a
interrupt.

** POST RESET
c:p=ffff:ffff rc=00000200
fis=0050fc90:e0000000:00000000:00000000
** PRE ISSUE
c:p=ffff:ffff rc=00000200
fis=0050fc90:e0000000:00000000:00000000
** CMD
c:p=0000:0000 rc=00000000
fis=00ec8027:a0000000:00000000:08000000
** POST ISSUE
c:p=ffff:ffff rc=00000000
fis=1f57d000:00000000:1f57d000:00000000
** POST INTERRUPT
c:p=0200:0000 rc=00000200
fis=0050d000:a0000000:1f000000:000000ff
ata6: dev 0 ATA, max UDMA7, 312581808 sectors: lba48

 Is there something more to do to get received FIS from PRB?

 Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-08-04  2:20           ` Tejun Heo
@ 2005-08-04 18:46             ` Edward Falk
  0 siblings, 0 replies; 13+ messages in thread
From: Edward Falk @ 2005-08-04 18:46 UTC (permalink / raw)
  To: Tejun Heo; +Cc: Jeff Garzik, linux-ide, Linux Kernel, Carlos Pardo


>  I agree that above code should clear both.  Just wanna verify.  Have
> you tested it and/or do you have any information confirming this?  If
> we don't have any further info, I think we should read PORT_SLOT_STAT
> before clearing PORT_IRQ_STAT to be on the safe side.

I've implemented the clear_irq() function to clear all interrupts as you 
said, but haven't had time to test this thoroughly.  Ultimately, we 
don't know if it works until we experience real errors and see how the 
system responds.

I don't think it matters in which order you do things, as long as you 
allow for the fact that reading PORT_SLOT_STAT will clear the command 
completion interrupt flag.

	-ed

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-08-04  8:51       ` Tejun Heo
@ 2005-08-04 18:51         ` Edward Falk
  2005-08-05  2:30           ` Tejun Heo
  0 siblings, 1 reply; 13+ messages in thread
From: Edward Falk @ 2005-08-04 18:51 UTC (permalink / raw)
  To: Tejun Heo; +Cc: linux-ide

Tejun Heo wrote:
>  Hi, again, Edward.
> 
>  Bad news...
> 
> On Tue, Aug 02, 2005 at 03:56:05PM -0700, Edward Falk wrote:
> 
>>Again, you would need to fetch them from the returned FIS structure. 
>>Here's a code fragment derived from SiI's issue_soft_reset() function:

Yeah, well, that's what I get for sending code fragments I haven't 
tested.  Stand by, I'll tinker with my own version of the driver and 
send you an improved fragment.

	-ed

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-08-04 18:51         ` Edward Falk
@ 2005-08-05  2:30           ` Tejun Heo
  0 siblings, 0 replies; 13+ messages in thread
From: Tejun Heo @ 2005-08-05  2:30 UTC (permalink / raw)
  To: Edward Falk; +Cc: linux-ide

Edward Falk wrote:
> Tejun Heo wrote:
> 
>>  Hi, again, Edward.
>>
>>  Bad news...
>>
>> On Tue, Aug 02, 2005 at 03:56:05PM -0700, Edward Falk wrote:
>>
>>> Again, you would need to fetch them from the returned FIS structure. 
>>> Here's a code fragment derived from SiI's issue_soft_reset() function:
> 
> 
> Yeah, well, that's what I get for sending code fragments I haven't 
> tested.  Stand by, I'll tinker with my own version of the driver and 
> send you an improved fragment.
> 
>     -ed

  One more thing I've found out.  They actually look like valid FIS 
values except for the FIS type.  I missed that as I was looking too hard 
at the FIS type value.

POSTRESET c:p=0080:0000 rc=00000000
fis=01500000:00000001:00000000:00000001
       ^    ^        ^                 ^
      DRDY  |      sect num        sect cnt
        FIS type 0?

  The following is from new driver w/ ATAPI device after SATA phy reset. 
(not softreset)

** POST RESET
c:p=0080:0000 rc=00000000
fis=01000027:00eb1401:00000000:00000001
            ^   ^^^^^^                 ^
       type 27? |----- valid sig ------|

  Also, after command completion, the status/error registers seem to 
carry the correct values.  The following is after issuing 
ATA_DEV_IDENTIFY to a ATAPI device.

** POST INTERRUPT
c:p=ffff:ffff rc=00000000
fis=0451e000:a0eb1400:1f000000:00000003
      ^ ^   ^
   ABRT |  FIS type 00 -_-;;
     DRDY/ERR

  So, it almost seems like I can just ignore the FIS type value. 
However, after a SATA phy reset, a hard disk returns the following.

** POST RESET
c:p=ffff:ffff rc=00000200
fis=0050fc90:e0000000:00000000:00000000

  This doesn't contain valid signature....

  I'll wait for further comment from you.  Thanks a lot.  ;-)

-- 
tejun

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
@ 2005-08-08 17:31 Carlos Pardo
  2005-08-08 17:37 ` Jeff Garzik
  0 siblings, 1 reply; 13+ messages in thread
From: Carlos Pardo @ 2005-08-08 17:31 UTC (permalink / raw)
  To: Tejun Heo, Edward Falk; +Cc: linux-ide, Linux Kernel, Jeff Garzik, Raymond Liu

Jeff, et al -

One question. Do the open source driver support pass-through commands ? if so, how ? If you do not support it, are you planning to implement pass-through commands sometime in the future ?

Carlos


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver
  2005-08-08 17:31 Carlos Pardo
@ 2005-08-08 17:37 ` Jeff Garzik
  0 siblings, 0 replies; 13+ messages in thread
From: Jeff Garzik @ 2005-08-08 17:37 UTC (permalink / raw)
  To: Carlos Pardo; +Cc: Tejun Heo, Edward Falk, linux-ide, Linux Kernel, Raymond Liu

On Mon, Aug 08, 2005 at 10:31:45AM -0700, Carlos Pardo wrote:
> One question. Do the open source driver support pass-through commands ? if so, how ? If you do not support it, are you planning to implement pass-through commands sometime in the future ?

I don't know about sata_sil24 in particular.  In general, there is a
patch in the "libata-dev.git" repository that supports ATA passthru, and
this is scheduled to be merged into the main 2.6.x kernel as soon as
some bugs are fixed.

sata_sil24 definitely needs to support this ATA passthru feature.

As implemented, each SATA driver is simply a generic ATA command engine,
for any command.  struct ata_taskfile (linux/ata.h) has a field that
indicates the command protocol used for command delivery (pio, dma,
no-data, etc.).  Each driver should execute ATA commands based on the
specified ATA protocol.

	Jeff




^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2005-08-08 17:37 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-07-28  1:36 [PATCH linux-2.6.13-rc3] SATA: rewritten sil24 driver Tejun Heo
2005-07-28 20:27 ` Jeff Garzik
2005-07-30  8:17   ` Tejun Heo
2005-08-02 22:56     ` Edward Falk
2005-08-03  4:09       ` Jeff Garzik
2005-08-03 14:28         ` Tejun Heo
2005-08-04  2:20           ` Tejun Heo
2005-08-04 18:46             ` Edward Falk
2005-08-04  8:51       ` Tejun Heo
2005-08-04 18:51         ` Edward Falk
2005-08-05  2:30           ` Tejun Heo
  -- strict thread matches above, loose matches on Subject: below --
2005-08-08 17:31 Carlos Pardo
2005-08-08 17:37 ` 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).