Merge branch 'upstream-fixes' into upstream
authorJeff Garzik <jeff@garzik.org>
Thu, 24 Aug 2006 06:51:38 +0000 (02:51 -0400)
committerJeff Garzik <jeff@garzik.org>
Thu, 24 Aug 2006 06:51:38 +0000 (02:51 -0400)
1  2 
drivers/ata/ata_piix.c
drivers/ata/sata_via.c

index 6846b56,0000000..0ca4c3b
mode 100644,000000..100644
--- /dev/null
@@@ -1,990 -1,0 +1,1010 @@@
-               .host_flags     = ATA_FLAG_SATA | PIIX_FLAG_CHECKINTR,
 +/*
 + *    ata_piix.c - Intel PATA/SATA controllers
 + *
 + *    Maintained by:  Jeff Garzik <jgarzik@pobox.com>
 + *                        Please ALWAYS copy linux-ide@vger.kernel.org
 + *                on emails.
 + *
 + *
 + *    Copyright 2003-2005 Red Hat Inc
 + *    Copyright 2003-2005 Jeff Garzik
 + *
 + *
 + *    Copyright header from piix.c:
 + *
 + *  Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
 + *  Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
 + *  Copyright (C) 2003 Red Hat Inc <alan@redhat.com>
 + *
 + *
 + *  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.
 + *
 + *  You should have received a copy of the GNU General Public License
 + *  along with this program; see the file COPYING.  If not, write to
 + *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 + *
 + *
 + *  libata documentation is available via 'make {ps|pdf}docs',
 + *  as Documentation/DocBook/libata.*
 + *
 + *  Hardware documentation available at http://developer.intel.com/
 + *
 + * Documentation
 + *    Publically available from Intel web site. Errata documentation
 + * is also publically available. As an aide to anyone hacking on this
 + * driver the list of errata that are relevant is below.going back to
 + * PIIX4. Older device documentation is now a bit tricky to find.
 + *
 + * The chipsets all follow very much the same design. The orginal Triton
 + * series chipsets do _not_ support independant device timings, but this
 + * is fixed in Triton II. With the odd mobile exception the chips then
 + * change little except in gaining more modes until SATA arrives. This
 + * driver supports only the chips with independant timing (that is those
 + * with SITRE and the 0x44 timing register). See pata_oldpiix and pata_mpiix
 + * for the early chip drivers.
 + *
 + * Errata of note:
 + *
 + * Unfixable
 + *    PIIX4    errata #9      - Only on ultra obscure hw
 + *    ICH3     errata #13     - Not observed to affect real hw
 + *                              by Intel
 + *
 + * Things we must deal with
 + *    PIIX4   errata #10      - BM IDE hang with non UDMA
 + *                              (must stop/start dma to recover)
 + *    440MX   errata #15      - As PIIX4 errata #10
 + *    PIIX4   errata #15      - Must not read control registers
 + *                              during a PIO transfer
 + *    440MX   errata #13      - As PIIX4 errata #15
 + *    ICH2    errata #21      - DMA mode 0 doesn't work right
 + *    ICH0/1  errata #55      - As ICH2 errata #21
 + *    ICH2    spec c #9       - Extra operations needed to handle
 + *                              drive hotswap [NOT YET SUPPORTED]
 + *    ICH2    spec c #20      - IDE PRD must not cross a 64K boundary
 + *                              and must be dword aligned
 + *    ICH2    spec c #24      - UDMA mode 4,5 t85/86 should be 6ns not 3.3
 + *
 + * Should have been BIOS fixed:
 + *    450NX:  errata #19      - DMA hangs on old 450NX
 + *    450NX:  errata #20      - DMA hangs on old 450NX
 + *    450NX:  errata #25      - Corruption with DMA on old 450NX
 + *    ICH3    errata #15      - IDE deadlock under high load
 + *                              (BIOS must set dev 31 fn 0 bit 23)
 + *    ICH3    errata #18      - Don't use native mode
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/pci.h>
 +#include <linux/init.h>
 +#include <linux/blkdev.h>
 +#include <linux/delay.h>
 +#include <linux/device.h>
 +#include <scsi/scsi_host.h>
 +#include <linux/libata.h>
 +
 +#define DRV_NAME      "ata_piix"
 +#define DRV_VERSION   "2.00"
 +
 +enum {
 +      PIIX_IOCFG              = 0x54, /* IDE I/O configuration register */
 +      ICH5_PMR                = 0x90, /* port mapping register */
 +      ICH5_PCS                = 0x92, /* port control and status */
 +      PIIX_SCC                = 0x0A, /* sub-class code register */
 +
 +      PIIX_FLAG_IGNORE_PCS    = (1 << 25), /* ignore PCS present bits */
 +      PIIX_FLAG_SCR           = (1 << 26), /* SCR available */
 +      PIIX_FLAG_AHCI          = (1 << 27), /* AHCI possible */
 +      PIIX_FLAG_CHECKINTR     = (1 << 28), /* make sure PCI INTx enabled */
 +
 +      /* combined mode.  if set, PATA is channel 0.
 +       * if clear, PATA is channel 1.
 +       */
 +      PIIX_PORT_ENABLED       = (1 << 0),
 +      PIIX_PORT_PRESENT       = (1 << 4),
 +
 +      PIIX_80C_PRI            = (1 << 5) | (1 << 4),
 +      PIIX_80C_SEC            = (1 << 7) | (1 << 6),
 +
 +      /* controller IDs */
 +      piix4_pata              = 0,
 +      ich5_pata               = 1,
 +      ich5_sata               = 2,
 +      esb_sata                = 3,
 +      ich6_sata               = 4,
 +      ich6_sata_ahci          = 5,
 +      ich6m_sata_ahci         = 6,
 +      ich8_sata_ahci          = 7,
 +
 +      /* constants for mapping table */
 +      P0                      = 0,  /* port 0 */
 +      P1                      = 1,  /* port 1 */
 +      P2                      = 2,  /* port 2 */
 +      P3                      = 3,  /* port 3 */
 +      IDE                     = -1, /* IDE */
 +      NA                      = -2, /* not avaliable */
 +      RV                      = -3, /* reserved */
 +
 +      PIIX_AHCI_DEVICE        = 6,
 +};
 +
 +struct piix_map_db {
 +      const u32 mask;
 +      const u16 port_enable;
 +      const int present_shift;
 +      const int map[][4];
 +};
 +
 +struct piix_host_priv {
 +      const int *map;
 +      const struct piix_map_db *map_db;
 +};
 +
 +static int piix_init_one (struct pci_dev *pdev,
 +                                  const struct pci_device_id *ent);
 +static void piix_host_stop(struct ata_host_set *host_set);
 +static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev);
 +static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev);
 +static void piix_pata_error_handler(struct ata_port *ap);
 +static void piix_sata_error_handler(struct ata_port *ap);
 +
 +static unsigned int in_module_init = 1;
 +
 +static const struct pci_device_id piix_pci_tbl[] = {
 +#ifdef ATA_ENABLE_PATA
 +      { 0x8086, 0x7111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata },
 +      { 0x8086, 0x24db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata },
 +      { 0x8086, 0x25a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata },
 +      { 0x8086, 0x27df, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata },
 +#endif
 +
 +      /* NOTE: The following PCI ids must be kept in sync with the
 +       * list in drivers/pci/quirks.c.
 +       */
 +
 +      /* 82801EB (ICH5) */
 +      { 0x8086, 0x24d1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
 +      /* 82801EB (ICH5) */
 +      { 0x8086, 0x24df, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
 +      /* 6300ESB (ICH5 variant with broken PCS present bits) */
 +      { 0x8086, 0x25a3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, esb_sata },
 +      /* 6300ESB pretending RAID */
 +      { 0x8086, 0x25b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, esb_sata },
 +      /* 82801FB/FW (ICH6/ICH6W) */
 +      { 0x8086, 0x2651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata },
 +      /* 82801FR/FRW (ICH6R/ICH6RW) */
 +      { 0x8086, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci },
 +      /* 82801FBM ICH6M (ICH6R with only port 0 and 2 implemented) */
 +      { 0x8086, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6m_sata_ahci },
 +      /* 82801GB/GR/GH (ICH7, identical to ICH6) */
 +      { 0x8086, 0x27c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci },
 +      /* 2801GBM/GHM (ICH7M, identical to ICH6M) */
 +      { 0x8086, 0x27c4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6m_sata_ahci },
 +      /* Enterprise Southbridge 2 (where's the datasheet?) */
 +      { 0x8086, 0x2680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_ahci },
 +      /* SATA Controller 1 IDE (ICH8, no datasheet yet) */
 +      { 0x8086, 0x2820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
 +      /* SATA Controller 2 IDE (ICH8, ditto) */
 +      { 0x8086, 0x2825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
 +      /* Mobile SATA Controller IDE (ICH8M, ditto) */
 +      { 0x8086, 0x2828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
 +
 +      { }     /* terminate list */
 +};
 +
 +static struct pci_driver piix_pci_driver = {
 +      .name                   = DRV_NAME,
 +      .id_table               = piix_pci_tbl,
 +      .probe                  = piix_init_one,
 +      .remove                 = ata_pci_remove_one,
 +      .suspend                = ata_pci_device_suspend,
 +      .resume                 = ata_pci_device_resume,
 +};
 +
 +static struct scsi_host_template piix_sht = {
 +      .module                 = THIS_MODULE,
 +      .name                   = DRV_NAME,
 +      .ioctl                  = ata_scsi_ioctl,
 +      .queuecommand           = ata_scsi_queuecmd,
 +      .can_queue              = ATA_DEF_QUEUE,
 +      .this_id                = ATA_SHT_THIS_ID,
 +      .sg_tablesize           = LIBATA_MAX_PRD,
 +      .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,
 +      .slave_destroy          = ata_scsi_slave_destroy,
 +      .bios_param             = ata_std_bios_param,
 +      .resume                 = ata_scsi_device_resume,
 +      .suspend                = ata_scsi_device_suspend,
 +};
 +
 +static const struct ata_port_operations piix_pata_ops = {
 +      .port_disable           = ata_port_disable,
 +      .set_piomode            = piix_set_piomode,
 +      .set_dmamode            = piix_set_dmamode,
 +      .mode_filter            = ata_pci_default_filter,
 +
 +      .tf_load                = ata_tf_load,
 +      .tf_read                = ata_tf_read,
 +      .check_status           = ata_check_status,
 +      .exec_command           = ata_exec_command,
 +      .dev_select             = ata_std_dev_select,
 +
 +      .bmdma_setup            = ata_bmdma_setup,
 +      .bmdma_start            = ata_bmdma_start,
 +      .bmdma_stop             = ata_bmdma_stop,
 +      .bmdma_status           = ata_bmdma_status,
 +      .qc_prep                = ata_qc_prep,
 +      .qc_issue               = ata_qc_issue_prot,
 +      .data_xfer              = ata_pio_data_xfer,
 +
 +      .freeze                 = ata_bmdma_freeze,
 +      .thaw                   = ata_bmdma_thaw,
 +      .error_handler          = piix_pata_error_handler,
 +      .post_internal_cmd      = ata_bmdma_post_internal_cmd,
 +
 +      .irq_handler            = ata_interrupt,
 +      .irq_clear              = ata_bmdma_irq_clear,
 +
 +      .port_start             = ata_port_start,
 +      .port_stop              = ata_port_stop,
 +      .host_stop              = piix_host_stop,
 +};
 +
 +static const struct ata_port_operations piix_sata_ops = {
 +      .port_disable           = ata_port_disable,
 +
 +      .tf_load                = ata_tf_load,
 +      .tf_read                = ata_tf_read,
 +      .check_status           = ata_check_status,
 +      .exec_command           = ata_exec_command,
 +      .dev_select             = ata_std_dev_select,
 +
 +      .bmdma_setup            = ata_bmdma_setup,
 +      .bmdma_start            = ata_bmdma_start,
 +      .bmdma_stop             = ata_bmdma_stop,
 +      .bmdma_status           = ata_bmdma_status,
 +      .qc_prep                = ata_qc_prep,
 +      .qc_issue               = ata_qc_issue_prot,
 +      .data_xfer              = ata_pio_data_xfer,
 +
 +      .freeze                 = ata_bmdma_freeze,
 +      .thaw                   = ata_bmdma_thaw,
 +      .error_handler          = piix_sata_error_handler,
 +      .post_internal_cmd      = ata_bmdma_post_internal_cmd,
 +
 +      .irq_handler            = ata_interrupt,
 +      .irq_clear              = ata_bmdma_irq_clear,
 +
 +      .port_start             = ata_port_start,
 +      .port_stop              = ata_port_stop,
 +      .host_stop              = piix_host_stop,
 +};
 +
 +static const struct piix_map_db ich5_map_db = {
 +      .mask = 0x7,
 +      .port_enable = 0x3,
 +      .present_shift = 4,
 +      .map = {
 +              /* PM   PS   SM   SS       MAP  */
 +              {  P0,  NA,  P1,  NA }, /* 000b */
 +              {  P1,  NA,  P0,  NA }, /* 001b */
 +              {  RV,  RV,  RV,  RV },
 +              {  RV,  RV,  RV,  RV },
 +              {  P0,  P1, IDE, IDE }, /* 100b */
 +              {  P1,  P0, IDE, IDE }, /* 101b */
 +              { IDE, IDE,  P0,  P1 }, /* 110b */
 +              { IDE, IDE,  P1,  P0 }, /* 111b */
 +      },
 +};
 +
 +static const struct piix_map_db ich6_map_db = {
 +      .mask = 0x3,
 +      .port_enable = 0xf,
 +      .present_shift = 4,
 +      .map = {
 +              /* PM   PS   SM   SS       MAP */
 +              {  P0,  P2,  P1,  P3 }, /* 00b */
 +              { IDE, IDE,  P1,  P3 }, /* 01b */
 +              {  P0,  P2, IDE, IDE }, /* 10b */
 +              {  RV,  RV,  RV,  RV },
 +      },
 +};
 +
 +static const struct piix_map_db ich6m_map_db = {
 +      .mask = 0x3,
 +      .port_enable = 0x5,
 +      .present_shift = 4,
 +      .map = {
 +              /* PM   PS   SM   SS       MAP */
 +              {  P0,  P2,  RV,  RV }, /* 00b */
 +              {  RV,  RV,  RV,  RV },
 +              {  P0,  P2, IDE, IDE }, /* 10b */
 +              {  RV,  RV,  RV,  RV },
 +      },
 +};
 +
 +static const struct piix_map_db ich8_map_db = {
 +      .mask = 0x3,
 +      .port_enable = 0x3,
 +      .present_shift = 8,
 +      .map = {
 +              /* PM   PS   SM   SS       MAP */
 +              {  P0,  NA,  P1,  NA }, /* 00b (hardwired) */
 +              {  RV,  RV,  RV,  RV },
 +              {  RV,  RV,  RV,  RV }, /* 10b (never) */
 +              {  RV,  RV,  RV,  RV },
 +      },
 +};
 +
 +static const struct piix_map_db *piix_map_db_table[] = {
 +      [ich5_sata]             = &ich5_map_db,
 +      [esb_sata]              = &ich5_map_db,
 +      [ich6_sata]             = &ich6_map_db,
 +      [ich6_sata_ahci]        = &ich6_map_db,
 +      [ich6m_sata_ahci]       = &ich6m_map_db,
 +      [ich8_sata_ahci]        = &ich8_map_db,
 +};
 +
 +static struct ata_port_info piix_port_info[] = {
 +      /* piix4_pata */
 +      {
 +              .sht            = &piix_sht,
 +              .host_flags     = ATA_FLAG_SLAVE_POSS,
 +              .pio_mask       = 0x1f, /* pio0-4 */
 +#if 0
 +              .mwdma_mask     = 0x06, /* mwdma1-2 */
 +#else
 +              .mwdma_mask     = 0x00, /* mwdma broken */
 +#endif
 +              .udma_mask      = ATA_UDMA_MASK_40C,
 +              .port_ops       = &piix_pata_ops,
 +      },
 +
 +      /* ich5_pata */
 +      {
 +              .sht            = &piix_sht,
 +              .host_flags     = ATA_FLAG_SLAVE_POSS | PIIX_FLAG_CHECKINTR,
 +              .pio_mask       = 0x1f, /* pio0-4 */
 +#if 0
 +              .mwdma_mask     = 0x06, /* mwdma1-2 */
 +#else
 +              .mwdma_mask     = 0x00, /* mwdma broken */
 +#endif
 +              .udma_mask      = 0x3f, /* udma0-5 */
 +              .port_ops       = &piix_pata_ops,
 +      },
 +
 +      /* ich5_sata */
 +      {
 +              .sht            = &piix_sht,
-               piix_init_pcs(pdev, piix_map_db_table[ent->driver_data]);
++              .host_flags     = ATA_FLAG_SATA | PIIX_FLAG_CHECKINTR |
++                                PIIX_FLAG_IGNORE_PCS,
 +              .pio_mask       = 0x1f, /* pio0-4 */
 +              .mwdma_mask     = 0x07, /* mwdma0-2 */
 +              .udma_mask      = 0x7f, /* udma0-6 */
 +              .port_ops       = &piix_sata_ops,
 +      },
 +
 +      /* i6300esb_sata */
 +      {
 +              .sht            = &piix_sht,
 +              .host_flags     = ATA_FLAG_SATA |
 +                                PIIX_FLAG_CHECKINTR | PIIX_FLAG_IGNORE_PCS,
 +              .pio_mask       = 0x1f, /* pio0-4 */
 +              .mwdma_mask     = 0x07, /* mwdma0-2 */
 +              .udma_mask      = 0x7f, /* udma0-6 */
 +              .port_ops       = &piix_sata_ops,
 +      },
 +
 +      /* ich6_sata */
 +      {
 +              .sht            = &piix_sht,
 +              .host_flags     = ATA_FLAG_SATA |
 +                                PIIX_FLAG_CHECKINTR | PIIX_FLAG_SCR,
 +              .pio_mask       = 0x1f, /* pio0-4 */
 +              .mwdma_mask     = 0x07, /* mwdma0-2 */
 +              .udma_mask      = 0x7f, /* udma0-6 */
 +              .port_ops       = &piix_sata_ops,
 +      },
 +
 +      /* ich6_sata_ahci */
 +      {
 +              .sht            = &piix_sht,
 +              .host_flags     = ATA_FLAG_SATA |
 +                                PIIX_FLAG_CHECKINTR | PIIX_FLAG_SCR |
 +                                PIIX_FLAG_AHCI,
 +              .pio_mask       = 0x1f, /* pio0-4 */
 +              .mwdma_mask     = 0x07, /* mwdma0-2 */
 +              .udma_mask      = 0x7f, /* udma0-6 */
 +              .port_ops       = &piix_sata_ops,
 +      },
 +
 +      /* ich6m_sata_ahci */
 +      {
 +              .sht            = &piix_sht,
 +              .host_flags     = ATA_FLAG_SATA |
 +                                PIIX_FLAG_CHECKINTR | PIIX_FLAG_SCR |
 +                                PIIX_FLAG_AHCI,
 +              .pio_mask       = 0x1f, /* pio0-4 */
 +              .mwdma_mask     = 0x07, /* mwdma0-2 */
 +              .udma_mask      = 0x7f, /* udma0-6 */
 +              .port_ops       = &piix_sata_ops,
 +      },
 +
 +      /* ich8_sata_ahci */
 +      {
 +              .sht            = &piix_sht,
 +              .host_flags     = ATA_FLAG_SATA |
 +                                PIIX_FLAG_CHECKINTR | PIIX_FLAG_SCR |
 +                                PIIX_FLAG_AHCI,
 +              .pio_mask       = 0x1f, /* pio0-4 */
 +              .mwdma_mask     = 0x07, /* mwdma0-2 */
 +              .udma_mask      = 0x7f, /* udma0-6 */
 +              .port_ops       = &piix_sata_ops,
 +      },
 +};
 +
 +static struct pci_bits piix_enable_bits[] = {
 +      { 0x41U, 1U, 0x80UL, 0x80UL },  /* port 0 */
 +      { 0x43U, 1U, 0x80UL, 0x80UL },  /* port 1 */
 +};
 +
 +MODULE_AUTHOR("Andre Hedrick, Alan Cox, Andrzej Krzysztofowicz, Jeff Garzik");
 +MODULE_DESCRIPTION("SCSI low-level driver for Intel PIIX/ICH ATA controllers");
 +MODULE_LICENSE("GPL");
 +MODULE_DEVICE_TABLE(pci, piix_pci_tbl);
 +MODULE_VERSION(DRV_VERSION);
 +
++static int force_pcs = 0;
++module_param(force_pcs, int, 0444);
++MODULE_PARM_DESC(force_pcs, "force honoring or ignoring PCS to work around "
++               "device mis-detection (0=default, 1=ignore PCS, 2=honor PCS)");
++
 +/**
 + *    piix_pata_cbl_detect - Probe host controller cable detect info
 + *    @ap: Port for which cable detect info is desired
 + *
 + *    Read 80c cable indicator from ATA PCI device's PCI config
 + *    register.  This register is normally set by firmware (BIOS).
 + *
 + *    LOCKING:
 + *    None (inherited from caller).
 + */
 +static void piix_pata_cbl_detect(struct ata_port *ap)
 +{
 +      struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
 +      u8 tmp, mask;
 +
 +      /* no 80c support in host controller? */
 +      if ((ap->udma_mask & ~ATA_UDMA_MASK_40C) == 0)
 +              goto cbl40;
 +
 +      /* check BIOS cable detect results */
 +      mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC;
 +      pci_read_config_byte(pdev, PIIX_IOCFG, &tmp);
 +      if ((tmp & mask) == 0)
 +              goto cbl40;
 +
 +      ap->cbl = ATA_CBL_PATA80;
 +      return;
 +
 +cbl40:
 +      ap->cbl = ATA_CBL_PATA40;
 +      ap->udma_mask &= ATA_UDMA_MASK_40C;
 +}
 +
 +/**
 + *    piix_pata_prereset - prereset for PATA host controller
 + *    @ap: Target port
 + *
 + *    Prereset including cable detection.
 + *
 + *    LOCKING:
 + *    None (inherited from caller).
 + */
 +static int piix_pata_prereset(struct ata_port *ap)
 +{
 +      struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
 +
 +      if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) {
 +              ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n");
 +              ap->eh_context.i.action &= ~ATA_EH_RESET_MASK;
 +              return 0;
 +      }
 +
 +      piix_pata_cbl_detect(ap);
 +
 +      return ata_std_prereset(ap);
 +}
 +
 +static void piix_pata_error_handler(struct ata_port *ap)
 +{
 +      ata_bmdma_drive_eh(ap, piix_pata_prereset, ata_std_softreset, NULL,
 +                         ata_std_postreset);
 +}
 +
 +/**
 + *    piix_sata_present_mask - determine present mask for SATA host controller
 + *    @ap: Target port
 + *
 + *    Reads SATA PCI device's PCI config register Port Configuration
 + *    and Status (PCS) to determine port and device availability.
 + *
 + *    LOCKING:
 + *    None (inherited from caller).
 + *
 + *    RETURNS:
 + *    determined present_mask
 + */
 +static unsigned int piix_sata_present_mask(struct ata_port *ap)
 +{
 +      struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
 +      struct piix_host_priv *hpriv = ap->host_set->private_data;
 +      const unsigned int *map = hpriv->map;
 +      int base = 2 * ap->port_no;
 +      unsigned int present_mask = 0;
 +      int port, i;
 +      u16 pcs;
 +
 +      pci_read_config_word(pdev, ICH5_PCS, &pcs);
 +      DPRINTK("ata%u: ENTER, pcs=0x%x base=%d\n", ap->id, pcs, base);
 +
 +      for (i = 0; i < 2; i++) {
 +              port = map[base + i];
 +              if (port < 0)
 +                      continue;
 +              if ((ap->flags & PIIX_FLAG_IGNORE_PCS) ||
 +                  (pcs & 1 << (hpriv->map_db->present_shift + port)))
 +                      present_mask |= 1 << i;
 +      }
 +
 +      DPRINTK("ata%u: LEAVE, pcs=0x%x present_mask=0x%x\n",
 +              ap->id, pcs, present_mask);
 +
 +      return present_mask;
 +}
 +
 +/**
 + *    piix_sata_softreset - reset SATA host port via ATA SRST
 + *    @ap: port to reset
 + *    @classes: resulting classes of attached devices
 + *
 + *    Reset SATA host port via ATA SRST.  On controllers with
 + *    reliable PCS present bits, the bits are used to determine
 + *    device presence.
 + *
 + *    LOCKING:
 + *    Kernel thread context (may sleep)
 + *
 + *    RETURNS:
 + *    0 on success, -errno otherwise.
 + */
 +static int piix_sata_softreset(struct ata_port *ap, unsigned int *classes)
 +{
 +      unsigned int present_mask;
 +      int i, rc;
 +
 +      present_mask = piix_sata_present_mask(ap);
 +
 +      rc = ata_std_softreset(ap, classes);
 +      if (rc)
 +              return rc;
 +
 +      for (i = 0; i < ATA_MAX_DEVICES; i++) {
 +              if (!(present_mask & (1 << i)))
 +                      classes[i] = ATA_DEV_NONE;
 +      }
 +
 +      return 0;
 +}
 +
 +static void piix_sata_error_handler(struct ata_port *ap)
 +{
 +      ata_bmdma_drive_eh(ap, ata_std_prereset, piix_sata_softreset, NULL,
 +                         ata_std_postreset);
 +}
 +
 +/**
 + *    piix_set_piomode - Initialize host controller PATA PIO timings
 + *    @ap: Port whose timings we are configuring
 + *    @adev: um
 + *
 + *    Set PIO mode for device, in host controller PCI config space.
 + *
 + *    LOCKING:
 + *    None (inherited from caller).
 + */
 +
 +static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev)
 +{
 +      unsigned int pio        = adev->pio_mode - XFER_PIO_0;
 +      struct pci_dev *dev     = to_pci_dev(ap->host_set->dev);
 +      unsigned int is_slave   = (adev->devno != 0);
 +      unsigned int master_port= ap->port_no ? 0x42 : 0x40;
 +      unsigned int slave_port = 0x44;
 +      u16 master_data;
 +      u8 slave_data;
 +
 +      static const     /* ISP  RTC */
 +      u8 timings[][2] = { { 0, 0 },
 +                          { 0, 0 },
 +                          { 1, 0 },
 +                          { 2, 1 },
 +                          { 2, 3 }, };
 +
 +      pci_read_config_word(dev, master_port, &master_data);
 +      if (is_slave) {
 +              master_data |= 0x4000;
 +              /* enable PPE, IE and TIME */
 +              master_data |= 0x0070;
 +              pci_read_config_byte(dev, slave_port, &slave_data);
 +              slave_data &= (ap->port_no ? 0x0f : 0xf0);
 +              slave_data |=
 +                      (timings[pio][0] << 2) |
 +                      (timings[pio][1] << (ap->port_no ? 4 : 0));
 +      } else {
 +              master_data &= 0xccf8;
 +              /* enable PPE, IE and TIME */
 +              master_data |= 0x0007;
 +              master_data |=
 +                      (timings[pio][0] << 12) |
 +                      (timings[pio][1] << 8);
 +      }
 +      pci_write_config_word(dev, master_port, master_data);
 +      if (is_slave)
 +              pci_write_config_byte(dev, slave_port, slave_data);
 +}
 +
 +/**
 + *    piix_set_dmamode - Initialize host controller PATA PIO timings
 + *    @ap: Port whose timings we are configuring
 + *    @adev: um
 + *    @udma: udma mode, 0 - 6
 + *
 + *    Set UDMA mode for device, in host controller PCI config space.
 + *
 + *    LOCKING:
 + *    None (inherited from caller).
 + */
 +
 +static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev)
 +{
 +      unsigned int udma       = adev->dma_mode; /* FIXME: MWDMA too */
 +      struct pci_dev *dev     = to_pci_dev(ap->host_set->dev);
 +      u8 maslave              = ap->port_no ? 0x42 : 0x40;
 +      u8 speed                = udma;
 +      unsigned int drive_dn   = (ap->port_no ? 2 : 0) + adev->devno;
 +      int a_speed             = 3 << (drive_dn * 4);
 +      int u_flag              = 1 << drive_dn;
 +      int v_flag              = 0x01 << drive_dn;
 +      int w_flag              = 0x10 << drive_dn;
 +      int u_speed             = 0;
 +      int                     sitre;
 +      u16                     reg4042, reg4a;
 +      u8                      reg48, reg54, reg55;
 +
 +      pci_read_config_word(dev, maslave, &reg4042);
 +      DPRINTK("reg4042 = 0x%04x\n", reg4042);
 +      sitre = (reg4042 & 0x4000) ? 1 : 0;
 +      pci_read_config_byte(dev, 0x48, &reg48);
 +      pci_read_config_word(dev, 0x4a, &reg4a);
 +      pci_read_config_byte(dev, 0x54, &reg54);
 +      pci_read_config_byte(dev, 0x55, &reg55);
 +
 +      switch(speed) {
 +              case XFER_UDMA_4:
 +              case XFER_UDMA_2:       u_speed = 2 << (drive_dn * 4); break;
 +              case XFER_UDMA_6:
 +              case XFER_UDMA_5:
 +              case XFER_UDMA_3:
 +              case XFER_UDMA_1:       u_speed = 1 << (drive_dn * 4); break;
 +              case XFER_UDMA_0:       u_speed = 0 << (drive_dn * 4); break;
 +              case XFER_MW_DMA_2:
 +              case XFER_MW_DMA_1:     break;
 +              default:
 +                      BUG();
 +                      return;
 +      }
 +
 +      if (speed >= XFER_UDMA_0) {
 +              if (!(reg48 & u_flag))
 +                      pci_write_config_byte(dev, 0x48, reg48 | u_flag);
 +              if (speed == XFER_UDMA_5) {
 +                      pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag);
 +              } else {
 +                      pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
 +              }
 +              if ((reg4a & a_speed) != u_speed)
 +                      pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed);
 +              if (speed > XFER_UDMA_2) {
 +                      if (!(reg54 & v_flag))
 +                              pci_write_config_byte(dev, 0x54, reg54 | v_flag);
 +              } else
 +                      pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
 +      } else {
 +              if (reg48 & u_flag)
 +                      pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
 +              if (reg4a & a_speed)
 +                      pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
 +              if (reg54 & v_flag)
 +                      pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
 +              if (reg55 & w_flag)
 +                      pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
 +      }
 +}
 +
 +#define AHCI_PCI_BAR 5
 +#define AHCI_GLOBAL_CTL 0x04
 +#define AHCI_ENABLE (1 << 31)
 +static int piix_disable_ahci(struct pci_dev *pdev)
 +{
 +      void __iomem *mmio;
 +      u32 tmp;
 +      int rc = 0;
 +
 +      /* BUG: pci_enable_device has not yet been called.  This
 +       * works because this device is usually set up by BIOS.
 +       */
 +
 +      if (!pci_resource_start(pdev, AHCI_PCI_BAR) ||
 +          !pci_resource_len(pdev, AHCI_PCI_BAR))
 +              return 0;
 +
 +      mmio = pci_iomap(pdev, AHCI_PCI_BAR, 64);
 +      if (!mmio)
 +              return -ENOMEM;
 +
 +      tmp = readl(mmio + AHCI_GLOBAL_CTL);
 +      if (tmp & AHCI_ENABLE) {
 +              tmp &= ~AHCI_ENABLE;
 +              writel(tmp, mmio + AHCI_GLOBAL_CTL);
 +
 +              tmp = readl(mmio + AHCI_GLOBAL_CTL);
 +              if (tmp & AHCI_ENABLE)
 +                      rc = -EIO;
 +      }
 +
 +      pci_iounmap(pdev, mmio);
 +      return rc;
 +}
 +
 +/**
 + *    piix_check_450nx_errata -       Check for problem 450NX setup
 + *    @ata_dev: the PCI device to check
 + *
 + *    Check for the present of 450NX errata #19 and errata #25. If
 + *    they are found return an error code so we can turn off DMA
 + */
 +
 +static int __devinit piix_check_450nx_errata(struct pci_dev *ata_dev)
 +{
 +      struct pci_dev *pdev = NULL;
 +      u16 cfg;
 +      u8 rev;
 +      int no_piix_dma = 0;
 +
 +      while((pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, pdev)) != NULL)
 +      {
 +              /* Look for 450NX PXB. Check for problem configurations
 +                 A PCI quirk checks bit 6 already */
 +              pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
 +              pci_read_config_word(pdev, 0x41, &cfg);
 +              /* Only on the original revision: IDE DMA can hang */
 +              if (rev == 0x00)
 +                      no_piix_dma = 1;
 +              /* On all revisions below 5 PXB bus lock must be disabled for IDE */
 +              else if (cfg & (1<<14) && rev < 5)
 +                      no_piix_dma = 2;
 +      }
 +      if (no_piix_dma)
 +              dev_printk(KERN_WARNING, &ata_dev->dev, "450NX errata present, disabling IDE DMA.\n");
 +      if (no_piix_dma == 2)
 +              dev_printk(KERN_WARNING, &ata_dev->dev, "A BIOS update may resolve this.\n");
 +      return no_piix_dma;
 +}
 +
 +static void __devinit piix_init_pcs(struct pci_dev *pdev,
++                                  struct ata_port_info *pinfo,
 +                                  const struct piix_map_db *map_db)
 +{
 +      u16 pcs, new_pcs;
 +
 +      pci_read_config_word(pdev, ICH5_PCS, &pcs);
 +
 +      new_pcs = pcs | map_db->port_enable;
 +
 +      if (new_pcs != pcs) {
 +              DPRINTK("updating PCS from 0x%x to 0x%x\n", pcs, new_pcs);
 +              pci_write_config_word(pdev, ICH5_PCS, new_pcs);
 +              msleep(150);
 +      }
++
++      if (force_pcs == 1) {
++              dev_printk(KERN_INFO, &pdev->dev,
++                         "force ignoring PCS (0x%x)\n", new_pcs);
++              pinfo[0].host_flags |= PIIX_FLAG_IGNORE_PCS;
++              pinfo[1].host_flags |= PIIX_FLAG_IGNORE_PCS;
++      } else if (force_pcs == 2) {
++              dev_printk(KERN_INFO, &pdev->dev,
++                         "force honoring PCS (0x%x)\n", new_pcs);
++              pinfo[0].host_flags &= ~PIIX_FLAG_IGNORE_PCS;
++              pinfo[1].host_flags &= ~PIIX_FLAG_IGNORE_PCS;
++      }
 +}
 +
 +static void __devinit piix_init_sata_map(struct pci_dev *pdev,
 +                                       struct ata_port_info *pinfo,
 +                                       const struct piix_map_db *map_db)
 +{
 +      struct piix_host_priv *hpriv = pinfo[0].private_data;
 +      const unsigned int *map;
 +      int i, invalid_map = 0;
 +      u8 map_value;
 +
 +      pci_read_config_byte(pdev, ICH5_PMR, &map_value);
 +
 +      map = map_db->map[map_value & map_db->mask];
 +
 +      dev_printk(KERN_INFO, &pdev->dev, "MAP [");
 +      for (i = 0; i < 4; i++) {
 +              switch (map[i]) {
 +              case RV:
 +                      invalid_map = 1;
 +                      printk(" XX");
 +                      break;
 +
 +              case NA:
 +                      printk(" --");
 +                      break;
 +
 +              case IDE:
 +                      WARN_ON((i & 1) || map[i + 1] != IDE);
 +                      pinfo[i / 2] = piix_port_info[ich5_pata];
 +                      pinfo[i / 2].private_data = hpriv;
 +                      i++;
 +                      printk(" IDE IDE");
 +                      break;
 +
 +              default:
 +                      printk(" P%d", map[i]);
 +                      if (i & 1)
 +                              pinfo[i / 2].host_flags |= ATA_FLAG_SLAVE_POSS;
 +                      break;
 +              }
 +      }
 +      printk(" ]\n");
 +
 +      if (invalid_map)
 +              dev_printk(KERN_ERR, &pdev->dev,
 +                         "invalid MAP value %u\n", map_value);
 +
 +      hpriv->map = map;
 +      hpriv->map_db = map_db;
 +}
 +
 +/**
 + *    piix_init_one - Register PIIX ATA PCI device with kernel services
 + *    @pdev: PCI device to register
 + *    @ent: Entry in piix_pci_tbl matching with @pdev
 + *
 + *    Called from kernel PCI layer.  We probe for combined mode (sigh),
 + *    and then hand over control to libata, for it to do the rest.
 + *
 + *    LOCKING:
 + *    Inherited from PCI layer (may sleep).
 + *
 + *    RETURNS:
 + *    Zero on success, or -ERRNO value.
 + */
 +
 +static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 +{
 +      static int printed_version;
 +      struct ata_port_info port_info[2];
 +      struct ata_port_info *ppinfo[2] = { &port_info[0], &port_info[1] };
 +      struct piix_host_priv *hpriv;
 +      unsigned long host_flags;
 +
 +      if (!printed_version++)
 +              dev_printk(KERN_DEBUG, &pdev->dev,
 +                         "version " DRV_VERSION "\n");
 +
 +      /* no hotplugging support (FIXME) */
 +      if (!in_module_init)
 +              return -ENODEV;
 +
 +      hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL);
 +      if (!hpriv)
 +              return -ENOMEM;
 +
 +      port_info[0] = piix_port_info[ent->driver_data];
 +      port_info[1] = piix_port_info[ent->driver_data];
 +      port_info[0].private_data = hpriv;
 +      port_info[1].private_data = hpriv;
 +
 +      host_flags = port_info[0].host_flags;
 +
 +      if (host_flags & PIIX_FLAG_AHCI) {
 +              u8 tmp;
 +              pci_read_config_byte(pdev, PIIX_SCC, &tmp);
 +              if (tmp == PIIX_AHCI_DEVICE) {
 +                      int rc = piix_disable_ahci(pdev);
 +                      if (rc)
 +                              return rc;
 +              }
 +      }
 +
 +      /* Initialize SATA map */
 +      if (host_flags & ATA_FLAG_SATA) {
 +              piix_init_sata_map(pdev, port_info,
 +                                 piix_map_db_table[ent->driver_data]);
++              piix_init_pcs(pdev, port_info,
++                            piix_map_db_table[ent->driver_data]);
 +      }
 +
 +      /* On ICH5, some BIOSen disable the interrupt using the
 +       * PCI_COMMAND_INTX_DISABLE bit added in PCI 2.3.
 +       * On ICH6, this bit has the same effect, but only when
 +       * MSI is disabled (and it is disabled, as we don't use
 +       * message-signalled interrupts currently).
 +       */
 +      if (host_flags & PIIX_FLAG_CHECKINTR)
 +              pci_intx(pdev, 1);
 +
 +      if (piix_check_450nx_errata(pdev)) {
 +              /* This writes into the master table but it does not
 +                 really matter for this errata as we will apply it to
 +                 all the PIIX devices on the board */
 +              port_info[0].mwdma_mask = 0;
 +              port_info[0].udma_mask = 0;
 +              port_info[1].mwdma_mask = 0;
 +              port_info[1].udma_mask = 0;
 +      }
 +      return ata_pci_init_one(pdev, ppinfo, 2);
 +}
 +
 +static void piix_host_stop(struct ata_host_set *host_set)
 +{
 +      struct piix_host_priv *hpriv = host_set->private_data;
 +
 +      ata_host_stop(host_set);
 +
 +      kfree(hpriv);
 +}
 +
 +static int __init piix_init(void)
 +{
 +      int rc;
 +
 +      DPRINTK("pci_register_driver\n");
 +      rc = pci_register_driver(&piix_pci_driver);
 +      if (rc)
 +              return rc;
 +
 +      in_module_init = 0;
 +
 +      DPRINTK("done\n");
 +      return 0;
 +}
 +
 +static void __exit piix_exit(void)
 +{
 +      pci_unregister_driver(&piix_pci_driver);
 +}
 +
 +module_init(piix_init);
 +module_exit(piix_exit);
 +
index 6529189,0000000..a0699a1
mode 100644,000000..100644
--- /dev/null
@@@ -1,395 -1,0 +1,502 @@@
- static const struct ata_port_operations svia_sata_ops = {
 +/*
 + *  sata_via.c - VIA Serial ATA controllers
 + *
 + *  Maintained by:  Jeff Garzik <jgarzik@pobox.com>
 + *               Please ALWAYS copy linux-ide@vger.kernel.org
 +                 on emails.
 + *
 + *  Copyright 2003-2004 Red Hat, Inc.  All rights reserved.
 + *  Copyright 2003-2004 Jeff Garzik
 + *
 + *
 + *  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.
 + *
 + *  You should have received a copy of the GNU General Public License
 + *  along with this program; see the file COPYING.  If not, write to
 + *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 + *
 + *
 + *  libata documentation is available via 'make {ps|pdf}docs',
 + *  as Documentation/DocBook/libata.*
 + *
 + *  Hardware documentation available under NDA.
 + *
 + *
 + *  To-do list:
 + *  - VT6421 PATA support
 + *
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/pci.h>
 +#include <linux/init.h>
 +#include <linux/blkdev.h>
 +#include <linux/delay.h>
 +#include <linux/device.h>
 +#include <scsi/scsi_host.h>
 +#include <linux/libata.h>
 +#include <asm/io.h>
 +
 +#define DRV_NAME      "sata_via"
 +#define DRV_VERSION   "2.0"
 +
 +enum board_ids_enum {
 +      vt6420,
 +      vt6421,
 +};
 +
 +enum {
 +      SATA_CHAN_ENAB          = 0x40, /* SATA channel enable */
 +      SATA_INT_GATE           = 0x41, /* SATA interrupt gating */
 +      SATA_NATIVE_MODE        = 0x42, /* Native mode enable */
 +      SATA_PATA_SHARING       = 0x49, /* PATA/SATA sharing func ctrl */
 +
 +      PORT0                   = (1 << 1),
 +      PORT1                   = (1 << 0),
 +      ALL_PORTS               = PORT0 | PORT1,
 +      N_PORTS                 = 2,
 +
 +      NATIVE_MODE_ALL         = (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4),
 +
 +      SATA_EXT_PHY            = (1 << 6), /* 0==use PATA, 1==ext phy */
 +      SATA_2DEV               = (1 << 5), /* SATA is master/slave */
 +};
 +
 +static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
 +static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg);
 +static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
++static void vt6420_error_handler(struct ata_port *ap);
 +
 +static const struct pci_device_id svia_pci_tbl[] = {
 +      { 0x1106, 0x0591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 },
 +      { 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 },
 +      { 0x1106, 0x3249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6421 },
 +
 +      { }     /* terminate list */
 +};
 +
 +static struct pci_driver svia_pci_driver = {
 +      .name                   = DRV_NAME,
 +      .id_table               = svia_pci_tbl,
 +      .probe                  = svia_init_one,
 +      .remove                 = ata_pci_remove_one,
 +};
 +
 +static struct scsi_host_template svia_sht = {
 +      .module                 = THIS_MODULE,
 +      .name                   = DRV_NAME,
 +      .ioctl                  = ata_scsi_ioctl,
 +      .queuecommand           = ata_scsi_queuecmd,
 +      .can_queue              = ATA_DEF_QUEUE,
 +      .this_id                = ATA_SHT_THIS_ID,
 +      .sg_tablesize           = LIBATA_MAX_PRD,
 +      .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,
 +      .slave_destroy          = ata_scsi_slave_destroy,
 +      .bios_param             = ata_std_bios_param,
 +};
 +
- static struct ata_port_info svia_port_info = {
++static const struct ata_port_operations vt6420_sata_ops = {
++      .port_disable           = ata_port_disable,
++
++      .tf_load                = ata_tf_load,
++      .tf_read                = ata_tf_read,
++      .check_status           = ata_check_status,
++      .exec_command           = ata_exec_command,
++      .dev_select             = ata_std_dev_select,
++
++      .bmdma_setup            = ata_bmdma_setup,
++      .bmdma_start            = ata_bmdma_start,
++      .bmdma_stop             = ata_bmdma_stop,
++      .bmdma_status           = ata_bmdma_status,
++
++      .qc_prep                = ata_qc_prep,
++      .qc_issue               = ata_qc_issue_prot,
++      .data_xfer              = ata_pio_data_xfer,
++
++      .freeze                 = ata_bmdma_freeze,
++      .thaw                   = ata_bmdma_thaw,
++      .error_handler          = vt6420_error_handler,
++      .post_internal_cmd      = ata_bmdma_post_internal_cmd,
++
++      .irq_handler            = ata_interrupt,
++      .irq_clear              = ata_bmdma_irq_clear,
++
++      .port_start             = ata_port_start,
++      .port_stop              = ata_port_stop,
++      .host_stop              = ata_host_stop,
++};
++
++static const struct ata_port_operations vt6421_sata_ops = {
 +      .port_disable           = ata_port_disable,
 +
 +      .tf_load                = ata_tf_load,
 +      .tf_read                = ata_tf_read,
 +      .check_status           = ata_check_status,
 +      .exec_command           = ata_exec_command,
 +      .dev_select             = ata_std_dev_select,
 +
 +      .bmdma_setup            = ata_bmdma_setup,
 +      .bmdma_start            = ata_bmdma_start,
 +      .bmdma_stop             = ata_bmdma_stop,
 +      .bmdma_status           = ata_bmdma_status,
 +
 +      .qc_prep                = ata_qc_prep,
 +      .qc_issue               = ata_qc_issue_prot,
 +      .data_xfer              = ata_pio_data_xfer,
 +
 +      .freeze                 = ata_bmdma_freeze,
 +      .thaw                   = ata_bmdma_thaw,
 +      .error_handler          = ata_bmdma_error_handler,
 +      .post_internal_cmd      = ata_bmdma_post_internal_cmd,
 +
 +      .irq_handler            = ata_interrupt,
 +      .irq_clear              = ata_bmdma_irq_clear,
 +
 +      .scr_read               = svia_scr_read,
 +      .scr_write              = svia_scr_write,
 +
 +      .port_start             = ata_port_start,
 +      .port_stop              = ata_port_stop,
 +      .host_stop              = ata_host_stop,
 +};
 +
-       .port_ops       = &svia_sata_ops,
++static struct ata_port_info vt6420_port_info = {
 +      .sht            = &svia_sht,
 +      .host_flags     = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
 +      .pio_mask       = 0x1f,
 +      .mwdma_mask     = 0x07,
 +      .udma_mask      = 0x7f,
-       struct ata_port_info *ppi = &svia_port_info;
++      .port_ops       = &vt6420_sata_ops,
 +};
 +
 +MODULE_AUTHOR("Jeff Garzik");
 +MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers");
 +MODULE_LICENSE("GPL");
 +MODULE_DEVICE_TABLE(pci, svia_pci_tbl);
 +MODULE_VERSION(DRV_VERSION);
 +
 +static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg)
 +{
 +      if (sc_reg > SCR_CONTROL)
 +              return 0xffffffffU;
 +      return inl(ap->ioaddr.scr_addr + (4 * sc_reg));
 +}
 +
 +static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
 +{
 +      if (sc_reg > SCR_CONTROL)
 +              return;
 +      outl(val, ap->ioaddr.scr_addr + (4 * sc_reg));
 +}
 +
++/**
++ *    vt6420_prereset - prereset for vt6420
++ *    @ap: target ATA port
++ *
++ *    SCR registers on vt6420 are pieces of shit and may hang the
++ *    whole machine completely if accessed with the wrong timing.
++ *    To avoid such catastrophe, vt6420 doesn't provide generic SCR
++ *    access operations, but uses SStatus and SControl only during
++ *    boot probing in controlled way.
++ *
++ *    As the old (pre EH update) probing code is proven to work, we
++ *    strictly follow the access pattern.
++ *
++ *    LOCKING:
++ *    Kernel thread context (may sleep)
++ *
++ *    RETURNS:
++ *    0 on success, -errno otherwise.
++ */
++static int vt6420_prereset(struct ata_port *ap)
++{
++      struct ata_eh_context *ehc = &ap->eh_context;
++      unsigned long timeout = jiffies + (HZ * 5);
++      u32 sstatus, scontrol;
++      int online;
++
++      /* don't do any SCR stuff if we're not loading */
++      if (!ATA_PFLAG_LOADING)
++              goto skip_scr;
++
++      /* Resume phy.  This is the old resume sequence from
++       * __sata_phy_reset().
++       */
++      svia_scr_write(ap, SCR_CONTROL, 0x300);
++      svia_scr_read(ap, SCR_CONTROL); /* flush */
++
++      /* wait for phy to become ready, if necessary */
++      do {
++              msleep(200);
++              if ((svia_scr_read(ap, SCR_STATUS) & 0xf) != 1)
++                      break;
++      } while (time_before(jiffies, timeout));
++
++      /* open code sata_print_link_status() */
++      sstatus = svia_scr_read(ap, SCR_STATUS);
++      scontrol = svia_scr_read(ap, SCR_CONTROL);
++
++      online = (sstatus & 0xf) == 0x3;
++
++      ata_port_printk(ap, KERN_INFO,
++                      "SATA link %s 1.5 Gbps (SStatus %X SControl %X)\n",
++                      online ? "up" : "down", sstatus, scontrol);
++
++      /* SStatus is read one more time */
++      svia_scr_read(ap, SCR_STATUS);
++
++      if (!online) {
++              /* tell EH to bail */
++              ehc->i.action &= ~ATA_EH_RESET_MASK;
++              return 0;
++      }
++
++ skip_scr:
++      /* wait for !BSY */
++      ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
++
++      return 0;
++}
++
++static void vt6420_error_handler(struct ata_port *ap)
++{
++      return ata_bmdma_drive_eh(ap, vt6420_prereset, ata_std_softreset,
++                                NULL, ata_std_postreset);
++}
++
 +static const unsigned int svia_bar_sizes[] = {
 +      8, 4, 8, 4, 16, 256
 +};
 +
 +static const unsigned int vt6421_bar_sizes[] = {
 +      16, 16, 16, 16, 32, 128
 +};
 +
 +static unsigned long svia_scr_addr(unsigned long addr, unsigned int port)
 +{
 +      return addr + (port * 128);
 +}
 +
 +static unsigned long vt6421_scr_addr(unsigned long addr, unsigned int port)
 +{
 +      return addr + (port * 64);
 +}
 +
 +static void vt6421_init_addrs(struct ata_probe_ent *probe_ent,
 +                            struct pci_dev *pdev,
 +                            unsigned int port)
 +{
 +      unsigned long reg_addr = pci_resource_start(pdev, port);
 +      unsigned long bmdma_addr = pci_resource_start(pdev, 4) + (port * 8);
 +      unsigned long scr_addr;
 +
 +      probe_ent->port[port].cmd_addr = reg_addr;
 +      probe_ent->port[port].altstatus_addr =
 +      probe_ent->port[port].ctl_addr = (reg_addr + 8) | ATA_PCI_CTL_OFS;
 +      probe_ent->port[port].bmdma_addr = bmdma_addr;
 +
 +      scr_addr = vt6421_scr_addr(pci_resource_start(pdev, 5), port);
 +      probe_ent->port[port].scr_addr = scr_addr;
 +
 +      ata_std_ports(&probe_ent->port[port]);
 +}
 +
 +static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev)
 +{
 +      struct ata_probe_ent *probe_ent;
-       probe_ent->port_ops     = &svia_sata_ops;
++      struct ata_port_info *ppi = &vt6420_port_info;
 +
 +      probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
 +      if (!probe_ent)
 +              return NULL;
 +
 +      probe_ent->port[0].scr_addr =
 +              svia_scr_addr(pci_resource_start(pdev, 5), 0);
 +      probe_ent->port[1].scr_addr =
 +              svia_scr_addr(pci_resource_start(pdev, 5), 1);
 +
 +      return probe_ent;
 +}
 +
 +static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev)
 +{
 +      struct ata_probe_ent *probe_ent;
 +      unsigned int i;
 +
 +      probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
 +      if (!probe_ent)
 +              return NULL;
 +
 +      memset(probe_ent, 0, sizeof(*probe_ent));
 +      probe_ent->dev = pci_dev_to_dev(pdev);
 +      INIT_LIST_HEAD(&probe_ent->node);
 +
 +      probe_ent->sht          = &svia_sht;
 +      probe_ent->host_flags   = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY;
++      probe_ent->port_ops     = &vt6421_sata_ops;
 +      probe_ent->n_ports      = N_PORTS;
 +      probe_ent->irq          = pdev->irq;
 +      probe_ent->irq_flags    = IRQF_SHARED;
 +      probe_ent->pio_mask     = 0x1f;
 +      probe_ent->mwdma_mask   = 0x07;
 +      probe_ent->udma_mask    = 0x7f;
 +
 +      for (i = 0; i < N_PORTS; i++)
 +              vt6421_init_addrs(probe_ent, pdev, i);
 +
 +      return probe_ent;
 +}
 +
 +static void svia_configure(struct pci_dev *pdev)
 +{
 +      u8 tmp8;
 +
 +      pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8);
 +      dev_printk(KERN_INFO, &pdev->dev, "routed to hard irq line %d\n",
 +             (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
 +
 +      /* make sure SATA channels are enabled */
 +      pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8);
 +      if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
 +              dev_printk(KERN_DEBUG, &pdev->dev,
 +                         "enabling SATA channels (0x%x)\n",
 +                         (int) tmp8);
 +              tmp8 |= ALL_PORTS;
 +              pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8);
 +      }
 +
 +      /* make sure interrupts for each channel sent to us */
 +      pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8);
 +      if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
 +              dev_printk(KERN_DEBUG, &pdev->dev,
 +                         "enabling SATA channel interrupts (0x%x)\n",
 +                         (int) tmp8);
 +              tmp8 |= ALL_PORTS;
 +              pci_write_config_byte(pdev, SATA_INT_GATE, tmp8);
 +      }
 +
 +      /* make sure native mode is enabled */
 +      pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8);
 +      if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) {
 +              dev_printk(KERN_DEBUG, &pdev->dev,
 +                         "enabling SATA channel native mode (0x%x)\n",
 +                         (int) tmp8);
 +              tmp8 |= NATIVE_MODE_ALL;
 +              pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
 +      }
 +}
 +
 +static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 +{
 +      static int printed_version;
 +      unsigned int i;
 +      int rc;
 +      struct ata_probe_ent *probe_ent;
 +      int board_id = (int) ent->driver_data;
 +      const int *bar_sizes;
 +      int pci_dev_busy = 0;
 +      u8 tmp8;
 +
 +      if (!printed_version++)
 +              dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 +
 +      rc = pci_enable_device(pdev);
 +      if (rc)
 +              return rc;
 +
 +      rc = pci_request_regions(pdev, DRV_NAME);
 +      if (rc) {
 +              pci_dev_busy = 1;
 +              goto err_out;
 +      }
 +
 +      if (board_id == vt6420) {
 +              pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8);
 +              if (tmp8 & SATA_2DEV) {
 +                      dev_printk(KERN_ERR, &pdev->dev,
 +                                 "SATA master/slave not supported (0x%x)\n",
 +                                 (int) tmp8);
 +                      rc = -EIO;
 +                      goto err_out_regions;
 +              }
 +
 +              bar_sizes = &svia_bar_sizes[0];
 +      } else {
 +              bar_sizes = &vt6421_bar_sizes[0];
 +      }
 +
 +      for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++)
 +              if ((pci_resource_start(pdev, i) == 0) ||
 +                  (pci_resource_len(pdev, i) < bar_sizes[i])) {
 +                      dev_printk(KERN_ERR, &pdev->dev,
 +                              "invalid PCI BAR %u (sz 0x%llx, val 0x%llx)\n",
 +                              i,
 +                              (unsigned long long)pci_resource_start(pdev, i),
 +                              (unsigned long long)pci_resource_len(pdev, i));
 +                      rc = -ENODEV;
 +                      goto err_out_regions;
 +              }
 +
 +      rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
 +      if (rc)
 +              goto err_out_regions;
 +      rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
 +      if (rc)
 +              goto err_out_regions;
 +
 +      if (board_id == vt6420)
 +              probe_ent = vt6420_init_probe_ent(pdev);
 +      else
 +              probe_ent = vt6421_init_probe_ent(pdev);
 +
 +      if (!probe_ent) {
 +              dev_printk(KERN_ERR, &pdev->dev, "out of memory\n");
 +              rc = -ENOMEM;
 +              goto err_out_regions;
 +      }
 +
 +      svia_configure(pdev);
 +
 +      pci_set_master(pdev);
 +
 +      /* FIXME: check ata_device_add return value */
 +      ata_device_add(probe_ent);
 +      kfree(probe_ent);
 +
 +      return 0;
 +
 +err_out_regions:
 +      pci_release_regions(pdev);
 +err_out:
 +      if (!pci_dev_busy)
 +              pci_disable_device(pdev);
 +      return rc;
 +}
 +
 +static int __init svia_init(void)
 +{
 +      return pci_register_driver(&svia_pci_driver);
 +}
 +
 +static void __exit svia_exit(void)
 +{
 +      pci_unregister_driver(&svia_pci_driver);
 +}
 +
 +module_init(svia_init);
 +module_exit(svia_exit);
 +