linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] AHCI SATA vendor update from VIA
@ 2005-12-12  4:09 Jeff Garzik
  2006-03-15 16:17 ` Sergey Vlasov
  0 siblings, 1 reply; 10+ messages in thread
From: Jeff Garzik @ 2005-12-12  4:09 UTC (permalink / raw)
  To: linux-ide@vger.kernel.org; +Cc: Linux Kernel

[-- Attachment #1: Type: text/plain, Size: 255 bytes --]

I received this ahci.c patch from VIA, and pass it on for review, 
comment, and testing.

This patch won't go in as-is, because it does too much special casing. 
But until we get around to that, people with VIA controllers probably 
want this...

	Jeff



[-- Attachment #2: ahci.c.patch --]
[-- Type: text/plain, Size: 8994 bytes --]

--- ahci.c.orig	2005-11-21 14:24:48.000000000 +0800
+++ ahci.c	2005-11-21 14:25:35.000000000 +0800
@@ -59,6 +59,7 @@
 	RX_FIS_D2H_REG		= 0x40,	/* offset of D2H Register FIS data */
 
 	board_ahci		= 0,
+	board_via_ahci		= 1,
 
 	/* global controller registers */
 	HOST_CAP		= 0x00, /* host capabilities */
@@ -126,6 +127,7 @@
 	PORT_CMD_LIST_ON	= (1 << 15), /* cmd list DMA engine running */
 	PORT_CMD_FIS_ON		= (1 << 14), /* FIS DMA engine running */
 	PORT_CMD_FIS_RX		= (1 << 4), /* Enable FIS receive DMA engine */
+	PORT_CMD_CLO		= (1 << 3), /* CLO */
 	PORT_CMD_POWER_ON	= (1 << 2), /* Power up device */
 	PORT_CMD_SPIN_UP	= (1 << 1), /* Spin up device */
 	PORT_CMD_START		= (1 << 0), /* Enable port DMA engine */
@@ -183,6 +185,14 @@
 static u8 ahci_check_err(struct ata_port *ap);
 static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
 
+static int via_ahci_qc_issue(struct ata_queued_cmd *qc);
+static void via_ahci_phy_reset(struct ata_port *ap);
+static void via_ahci_port_stop(struct ata_port *ap);
+static int via_ahci_softreset(struct ata_port *ap);
+static unsigned int via_ata_busy_sleep (struct ata_port *ap,
+				    unsigned long tmout_pat,
+			    	    unsigned long tmout);
+
 static Scsi_Host_Template ahci_sht = {
 	.module			= THIS_MODULE,
 	.name			= DRV_NAME,
@@ -231,6 +241,35 @@
 	.host_stop		= ahci_host_stop,
 };
 
+static struct ata_port_operations via_ahci_ops = {
+	.port_disable		= ata_port_disable,
+
+	.check_status		= ahci_check_status,
+	.check_altstatus	= ahci_check_status,
+	.check_err		= ahci_check_err,
+	.dev_select		= ata_noop_dev_select,
+
+	.tf_read		= ahci_tf_read,
+
+
+	.qc_prep		= ahci_qc_prep,
+
+	.eng_timeout		= ahci_eng_timeout,
+
+	.irq_handler		= ahci_interrupt,
+	.irq_clear		= ahci_irq_clear,
+
+	.scr_read		= ahci_scr_read,
+	.scr_write		= ahci_scr_write,
+
+	.port_start		= ahci_port_start,
+	.host_stop		= ahci_host_stop,
+
+	.phy_reset		= via_ahci_phy_reset,
+	.qc_issue		= via_ahci_qc_issue,
+	.port_stop		= via_ahci_port_stop,
+};
+
 static struct ata_port_info ahci_port_info[] = {
 	/* board_ahci */
 	{
@@ -242,6 +281,16 @@
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &ahci_ops,
 	},
+	/* board_via_ahci */
+	{
+		.sht		= &ahci_sht,
+		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+				  ATA_FLAG_SRST | ATA_FLAG_MMIO |
+				  ATA_FLAG_PIO_DMA,
+		.pio_mask	= 0x03, /* pio3-4 */
+		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
+		.port_ops	= &via_ahci_ops,
+	},
 };
 
 static struct pci_device_id ahci_pci_tbl[] = {
@@ -263,6 +312,8 @@
 	  board_ahci }, /* ESB2 */
 	{ PCI_VENDOR_ID_INTEL, 0x2683, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 	  board_ahci }, /* ESB2 */
+	{ PCI_VENDOR_ID_VIA, 0x3349, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+	  board_via_ahci }, /* VT8251*/
 	{ }	/* terminate list */
 };
 
@@ -1049,6 +1100,244 @@
 	return rc;
 }
 
+/* START: patch code for VIA VT8251 ahci controller */
+
+static int via_ahci_softreset(struct ata_port *ap)
+{
+	void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+	struct ahci_port_priv *pp = ap->private_data;
+	u32 tmp,i;
+	u8 *fisbuf;
+
+	VPRINTK("ENTER\n");
+
+	writel(0x00000000, port_mmio + PORT_IRQ_MASK);  /*disable interrupt */
+	readl (port_mmio + PORT_IRQ_MASK);  /* flush */
+
+	/* prepare the software-reset commands */
+
+	/* prepare first command header */
+	memset(pp->cmd_slot, 0, 32);
+	pp->cmd_slot[0].opts = 0x00000505;
+	pp->cmd_slot[0].status = 0;
+	pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff);
+	pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16);
+
+	/* prepare CMDFIS struct */
+	fisbuf = pp->cmd_tbl;
+	memset(fisbuf, 0, 64);
+	fisbuf[0] = 0x27;
+	fisbuf[7] = 0xa0;
+	fisbuf[15] = 0x04;
+	
+	/* trigger the commands */
+	writel(0x1, port_mmio + PORT_CMD_ISSUE);
+	readl (port_mmio + PORT_CMD_ISSUE); /* flush */
+
+	udelay(20);
+    /* wait command complete */
+	for (i = 0; i < 2000; i++) {
+		tmp = readl(port_mmio + PORT_CMD_ISSUE);
+		if ((tmp & 1) == 0) break;
+		msleep(20);
+	}
+
+	if (i == 2000) {
+		printk(KERN_WARNING "TimeOut for Wait Command complete\n");
+		return 1;
+	}
+
+	/* prepare second command header */
+	pp->cmd_slot[0].opts = 0x00000005;
+	pp->cmd_slot[0].status = 0;
+	pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff);
+	pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16);
+
+	fisbuf = pp->cmd_tbl;
+	memset(fisbuf, 0, 64);
+	fisbuf[0] = 0x27;
+	fisbuf[7] = 0xa0;
+	fisbuf[15] = 0x00;
+
+	/* trigger the commands */
+	writel(0x1, port_mmio + PORT_CMD_ISSUE);
+	readl (port_mmio + PORT_CMD_ISSUE); /* flush */
+
+	udelay(20);
+    /* wait command complete */
+	for (i = 0; i < 2000; i++) {
+		tmp = readl(port_mmio + PORT_CMD_ISSUE);
+		if ((tmp & 1) == 0) break;
+		msleep(20);
+	}
+
+	if (i == 2000) {
+		printk(KERN_WARNING "TimeOut for Wait Command complete\n");
+		return 1;
+	}
+
+	// enable port interrupt
+	writel(0xffffffff, port_mmio + PORT_IRQ_MASK);
+	readl (port_mmio + PORT_IRQ_MASK);  /* flush */
+
+	return 0;
+}
+
+static unsigned int via_ata_busy_sleep (struct ata_port *ap,
+				    unsigned long tmout_pat,
+			    	    unsigned long tmout)
+{
+	unsigned long timer_start, timeout;
+	u8 status;
+
+	status = ata_busy_wait(ap, ATA_BUSY, 300);
+	timer_start = jiffies;
+	timeout = timer_start + tmout_pat;
+	while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) {
+		msleep(50);
+		status = ata_busy_wait(ap, ATA_BUSY, 3);
+	}
+
+	if (status & ATA_BUSY)
+		printk(KERN_WARNING "ata%u is slow to respond, "
+		       "please be patient\n", ap->id);
+
+	timeout = timer_start + tmout;
+	while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) {
+		msleep(50);
+		status = ata_chk_status(ap);
+	}
+
+	if (status & ATA_BUSY) {
+		printk(KERN_ERR "ata%u failed to respond (%lu secs)\n",
+		       ap->id, tmout / HZ);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void via_ahci_phy_reset(struct ata_port *ap)
+{
+	void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+	struct ata_taskfile tf;
+	struct ata_device *dev = &ap->device[0];
+	u32 tmp;
+
+	u32 sstatus,i;
+	unsigned long timeout = jiffies + (HZ * 5);
+	u8 tmp_status;
+	
+	if (ap->flags & ATA_FLAG_SATA_RESET) {
+		/* issue phy wake/reset */
+		scr_write_flush(ap, SCR_CONTROL, 0x301);
+		udelay(400);			/* FIXME: a guess */
+	}
+	scr_write_flush(ap, SCR_CONTROL, 0x300); /* phy wake/clear reset */
+
+	/* wait for phy to become ready, if necessary */
+	do {
+		msleep(200);
+		sstatus = scr_read(ap, SCR_STATUS);
+		if ((sstatus & 0xf) != 1)
+			break;
+	} while (time_before(jiffies, timeout));
+
+	/* TODO: phy layer with polling, timeouts, etc. */
+	if (sata_dev_present(ap))
+		ata_port_probe(ap);
+	else {
+		sstatus = scr_read(ap, SCR_STATUS);
+		printk(KERN_INFO "ata%u: no device found (phy stat %08x)\n",
+		       ap->id, sstatus);
+		ata_port_disable(ap);
+	}
+
+	if (ap->flags & ATA_FLAG_PORT_DISABLED)
+		return;
+
+	/*Fix the VIA busy bug by a software reset*/
+	for (i = 0; i < 100; i++) {
+		tmp_status = ap->ops->check_status(ap);
+		if ((tmp_status & ATA_BUSY) == 0) break;
+		msleep(10);
+	}
+
+	if ((tmp_status & ATA_BUSY)) {
+		DPRINTK("Busy after CommReset, do softreset...\n"); 
+		/*set the PxCMD.CLO bit to clear BUSY and DRQ, to make the reset command sent out*/
+		tmp = readl(port_mmio + PORT_CMD);
+		tmp |= PORT_CMD_CLO;
+		writel(tmp, port_mmio + PORT_CMD);
+		readl(port_mmio + PORT_CMD); /* flush */
+
+		if (via_ahci_softreset(ap)) {
+			printk(KERN_WARNING "softreset failed\n");
+			return;
+		}
+	}
+
+	if (via_ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) {
+		ata_port_disable(ap);
+		return;
+	}
+
+	ap->cbl = ATA_CBL_SATA;
+
+	if (ap->flags & ATA_FLAG_PORT_DISABLED)
+		return;
+
+	tmp = readl(port_mmio + PORT_SIG);
+	tf.lbah		= (tmp >> 24)	& 0xff;
+	tf.lbam		= (tmp >> 16)	& 0xff;
+	tf.lbal		= (tmp >> 8)	& 0xff;
+	tf.nsect	= (tmp)		& 0xff;
+
+	dev->class = ata_dev_classify(&tf);
+	if (!ata_dev_present(dev))
+		ata_port_disable(ap);
+
+}
+
+static int via_ahci_qc_issue(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	void *port_mmio = (void *) ap->ioaddr.cmd_addr;
+
+    if (qc &&
+        qc->tf.command == ATA_CMD_SET_FEATURES &&
+        qc->tf.feature == SETFEATURES_XFER) {
+        /* skip set xfer mode process */
+
+		ata_qc_complete(qc, 0);
+		qc = NULL;
+        return 0;
+    }
+
+	writel(1, port_mmio + PORT_CMD_ISSUE);
+	readl(port_mmio + PORT_CMD_ISSUE);	/* flush */
+
+	return 0;
+}
+
+static void via_ahci_port_stop(struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	struct ahci_port_priv *pp = ap->private_data;
+
+	/* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
+	 * this is slightly incorrect.
+	 */
+	msleep(500);
+
+	ap->private_data = NULL;
+	dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ,
+			  pp->cmd_slot, pp->cmd_slot_dma);
+	kfree(pp);
+	ata_port_stop(ap);
+}
+
+/* END: patch code for VIA VT8251 ahci controller */
 
 static int __init ahci_init(void)
 {

^ permalink raw reply	[flat|nested] 10+ messages in thread
* re: [PATCH] AHCI SATA vendor update from VIA
@ 2006-03-16  6:07 Ph. Marek
  2006-03-17 12:08 ` Ph. Marek
  0 siblings, 1 reply; 10+ messages in thread
From: Ph. Marek @ 2006-03-16  6:07 UTC (permalink / raw)
  To: jgarzik; +Cc: linux-ide, linux-kernel

Hello Jeff,


I'd like to confirm that the patch in your email from 2005-12-11 does indeed 
work for my vt8251 on an K8M800 motherboard.

It didn't apply cleanly to 2.6.15; I had to fix two rejects, and then comment 
out the two lines with
	.host_stop =
and
	.check_err =
to make it compile.


I'd like to ask you if that could have negative effects, and when the driver 
(part) will be included in the standard kernel.


Thank you for this patch. I'm looking forward to your answer.


Regards,

Phil

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

end of thread, other threads:[~2006-03-23  0:58 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-12-12  4:09 [PATCH] AHCI SATA vendor update from VIA Jeff Garzik
2006-03-15 16:17 ` Sergey Vlasov
2006-03-21  1:53   ` Jeff Garzik
2006-03-21 11:19     ` Tejun Heo
2006-03-21 16:39       ` Jeff Garzik
2006-03-22 12:07         ` [PATCH] ahci: add softreset Tejun Heo
2006-03-23  0:58           ` Jeff Garzik
  -- strict thread matches above, loose matches on Subject: below --
2006-03-16  6:07 [PATCH] AHCI SATA vendor update from VIA Ph. Marek
2006-03-17 12:08 ` Ph. Marek
2006-03-17 13:03   ` Geoff Rivell

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).