[PATCH] ahci: vt8251 support

From: Geoff Rivell
Date: Tue Jan 24 2006 - 07:21:55 EST


Modified the Via patch for VT8251 sata support in the kernel.

I am hoping the patch is clean enough for kernel inclusion. It has had
limited testing by the viaarena.com forum people.

--
...."Have you mooed today?"...
--- ahci.c.orig 2006-01-24 11:39:14.000000000 +0000
+++ ahci.c 2006-01-24 11:39:23.000000000 +0000
@@ -70,6 +70,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 */
@@ -138,6 +139,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 */
@@ -196,6 +198,10 @@
static u8 ahci_check_status(struct ata_port *ap);
static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
static void ahci_remove_one (struct pci_dev *pdev);
+static int via_ahci_qc_issue(struct ata_queued_cmd *qc);
+static void via_ahci_phy_reset(struct ata_port *ap);
+static int via_ahci_softreset(struct ata_port *ap);
+

static struct scsi_host_template ahci_sht = {
.module = THIS_MODULE,
@@ -243,6 +249,33 @@
.port_stop = ahci_port_stop,
};

+static struct ata_port_operations via_ahci_ops = {
+ .port_disable = ata_port_disable,
+
+ .check_status = ahci_check_status,
+ .check_altstatus = ahci_check_status,
+ .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,
+
+ .phy_reset = via_ahci_phy_reset,
+ .qc_issue = via_ahci_qc_issue,
+ .port_stop = ahci_port_stop,
+};
+
static struct ata_port_info ahci_port_info[] = {
/* board_ahci */
{
@@ -254,6 +287,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 const struct pci_device_id ahci_pci_tbl[] = {
@@ -277,6 +320,8 @@
board_ahci }, /* ESB2 */
{ PCI_VENDOR_ID_INTEL, 0x27c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
board_ahci }, /* ICH7-M DH */
+ { PCI_VENDOR_ID_VIA, 0x3349, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_via_ahci }, /* VT8251*/
{ } /* terminate list */
};

@@ -1100,6 +1145,113 @@
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,j,via_opts[]={0x00000505,0x00000005};
+ u8 *fisbuf,via_fisbuf[]={0x04,0x00};
+
+ VPRINTK("ENTER\n");
+
+ writel(0x00000000, port_mmio + PORT_IRQ_MASK); /*disable interrupt */
+ readl (port_mmio + PORT_IRQ_MASK); /* flush */
+
+ /* prepare the software-reset commands */
+
+ memset(pp->cmd_slot, 0, 32);
+
+ for (j = 0; j < 2; j++) {
+ /* prepare the command header */
+ pp->cmd_slot[0].opts = via_opts[j];
+ 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] = via_fisbuf[j];
+
+ /* 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 void via_ahci_phy_reset(struct ata_port *ap)
+{
+ void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ u32 tmp,i;
+ u8 tmp_status;
+
+ /*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;
+ }
+ }
+
+ ahci_phy_reset(ap);
+}
+
+static int via_ahci_qc_issue(struct ata_queued_cmd *qc)
+{
+
+ 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;
+ }
+
+ ahci_qc_issue(qc);
+
+ return 0;
+}
+
+
static void ahci_remove_one (struct pci_dev *pdev)
{
struct device *dev = pci_dev_to_dev(pdev);