lkml.org 
[lkml]   [2005]   [Dec]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] AHCI SATA vendor update from VIA
    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


    --- 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)
    {
    \
     
     \ /
      Last update: 2005-12-12 05:12    [W:0.050 / U:0.084 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site