[Fwd: [Patch] sata_via.c: add support for VT8251, fix the internalchips issue and]

From: Jeff Garzik
Date: Fri Aug 22 2008 - 02:39:57 EST


This was signed-off-by and meant for the public kernel, so I am forwarding this to the lists.

Jeff



--- Begin Message --- Dear Jeff,

There are some big changes for sata_via.c, please refer to [Summary] for more detail.
Any question, please feel free to contact me. Thanks.

BRs,
Joseph Chan

====================
Summary:
a. Add board ID for VT8251.
-That is the VT8251 has 2 SATA channels and each channel supports Master/Slave slot,
the standard SATA operations in libata suppots only Master slot.
b. Patch <svia_scr_read> and <svia_scr_write> functions for VT8251
- The location of SATA register on VT8251 is in PCI configuration space, not in PCI BAR.
c. Add function <via_ata_tf_load>
- Fix the internal bug of via chipsets, which will reset the device register after changing the IEN bit in CTL register
d. Add function <via_std_set_pio_mode>, <via_std_set_dma_mode> and <via_std_cable_detect>
- VIA's some PATA_SATA controllers (sucu as VT6421, CX700 and VX800), and the configure spaces are different
for those controllers. To avoid writing several similar functions for these chips, these 3 functions are used.
e. Add function <via_std_sata_prereset> and <via_std_sata_comreset>
- Since some VIA SATA controllers support Master/Slave mode, but the standard hardreset in libata does not
support slave slot, so we implement the hardreset during prereset process and then do softreset.
f. Redefine sata_ops
- Libata does not do softreset for SATA controller if hardreset is available, to ensure the slave slot could be detected,
we skip standard hardrest in libata. As a result, we could not inherit the ops from ata_bmdma_port_ops, which
has a default hardreset.

Signed-off-by: Josepch Chan <josephchan@xxxxxxxxxx>

--- linux-2.6.26/drivers/ata/sata_via.c 2008-07-14 15:46:22.000000000 +0800
+++ linux-2.6.26/drivers/ata/sata_via.c 2008-07-19 03:19:29.000000000 +0800
@@ -46,9 +46,15 @@
#define DRV_NAME "sata_via"
#define DRV_VERSION "2.3"

+/*
+ * vt8251 is different from other sata controllers of VIA.
+ * It has two channels, each channel has both Master and
+ * Slave slot
+ */
enum board_ids_enum {
vt6420,
vt6421,
+ vt8251,
};

enum {
@@ -71,19 +77,25 @@
static int svia_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val);
static int svia_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val);
static void svia_noop_freeze(struct ata_port *ap);
-static int vt6420_prereset(struct ata_link *link, unsigned long deadline);
-static int vt6421_pata_cable_detect(struct ata_port *ap);
-static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev);
-static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev);
+
+static void via_ata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf);
+
+static void via_std_set_pio_mode(struct ata_port *ap, struct ata_device *adev);
+static void via_std_set_dma_mode(struct ata_port *ap, struct ata_device *adev);
+static int via_std_cable_detect(struct ata_port *ap);
+
+static int via_std_sata_prereset(struct ata_link *link,
+ unsigned long deadline);
+static int via_std_sata_comreset(struct ata_port *ap, int isMaster);

static const struct pci_device_id svia_pci_tbl[] = {
+ { PCI_VDEVICE(VIA, 0x3149), vt6420 }, /* Two sata channels(Master) */
{ PCI_VDEVICE(VIA, 0x5337), vt6420 },
- { PCI_VDEVICE(VIA, 0x0591), vt6420 },
- { PCI_VDEVICE(VIA, 0x3149), vt6420 },
- { PCI_VDEVICE(VIA, 0x3249), vt6421 },
- { PCI_VDEVICE(VIA, 0x5287), vt6420 },
{ PCI_VDEVICE(VIA, 0x5372), vt6420 },
+ { PCI_VDEVICE(VIA, 0x0591), vt6420 }, /* Two sata channels(Master) */
{ PCI_VDEVICE(VIA, 0x7372), vt6420 },
+ { PCI_VDEVICE(VIA, 0x3249), vt6421 }, /* 2 sata chnls, 1 pata chnl*/
+ { PCI_VDEVICE(VIA, 0x5287), vt8251 }, /* 2 sata chnls(Master/Slave) */

{ } /* terminate list */
};
@@ -103,31 +115,73 @@
ATA_BMDMA_SHT(DRV_NAME),
};

-static struct ata_port_operations vt6420_sata_ops = {
- .inherits = &ata_bmdma_port_ops,
+/*
+ * The opertion redefined by VIA to implement sata functions.
+ * Since some via sata controllers support Master/Slave mode,
+ * but the standard hardreset in libata could not work well
+ * for slave slot, so we implement the hardreset during
+ * prereset, and then do softreset. It should be mentioned
+ * that libata does not do softreset for sata controller if
+ * hardreset is availuable, to ensure the slave slot could
+ * be detected, we skip standard hardrest in libata. That is
+ * why we have to redefine the sata operations of sata
+ */
+static struct ata_port_operations via_std_sata_ops = {
+ .inherits = &ata_base_port_ops,
+
+ .qc_prep = ata_sff_qc_prep,
+ .qc_issue = ata_sff_qc_issue,
+ .qc_fill_rtf = ata_sff_qc_fill_rtf,
+
+ .thaw = ata_sff_thaw,
+ .softreset = ata_sff_softreset,
+ .postreset = ata_sff_postreset,
+ .error_handler = ata_sff_error_handler,
+ .post_internal_cmd = ata_sff_post_internal_cmd,
+
+ .sff_dev_select = ata_sff_dev_select,
+ .sff_check_status = ata_sff_check_status,
+ .sff_tf_read = ata_sff_tf_read,
+ .sff_exec_command = ata_sff_exec_command,
+ .sff_data_xfer = ata_sff_data_xfer,
+ .sff_irq_on = ata_sff_irq_on,
+ .sff_irq_clear = ata_sff_irq_clear,
+
+ .port_start = ata_sff_port_start,
+
+ .mode_filter = ata_bmdma_mode_filter,
+
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+
+ .sff_tf_load = via_ata_tf_load,
+
+ .prereset = via_std_sata_prereset,
.freeze = svia_noop_freeze,
- .prereset = vt6420_prereset,
-};

-static struct ata_port_operations vt6421_pata_ops = {
- .inherits = &ata_bmdma_port_ops,
- .cable_detect = vt6421_pata_cable_detect,
- .set_piomode = vt6421_set_pio_mode,
- .set_dmamode = vt6421_set_dma_mode,
+ .scr_read = svia_scr_read,
+ .scr_write = svia_scr_write,
};

-static struct ata_port_operations vt6421_sata_ops = {
+static struct ata_port_operations via_std_pata_ops = {
.inherits = &ata_bmdma_port_ops,
- .scr_read = svia_scr_read,
- .scr_write = svia_scr_write,
+
+ .cable_detect = via_std_cable_detect,
+ .set_piomode = via_std_set_pio_mode,
+ .set_dmamode = via_std_set_dma_mode,
+
+ .sff_tf_load = via_ata_tf_load,
};

-static const struct ata_port_info vt6420_port_info = {
+
+static struct ata_port_info vt6420_port_info = {
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
.pio_mask = 0x1f,
.mwdma_mask = 0x07,
.udma_mask = ATA_UDMA6,
- .port_ops = &vt6420_sata_ops,
+ .port_ops = &via_std_sata_ops,
};

static struct ata_port_info vt6421_sport_info = {
@@ -135,7 +189,7 @@
.pio_mask = 0x1f,
.mwdma_mask = 0x07,
.udma_mask = ATA_UDMA6,
- .port_ops = &vt6421_sata_ops,
+ .port_ops = &via_std_sata_ops,
};

static struct ata_port_info vt6421_pport_info = {
@@ -143,7 +197,20 @@
.pio_mask = 0x1f,
.mwdma_mask = 0,
.udma_mask = ATA_UDMA6,
- .port_ops = &vt6421_pata_ops,
+ .port_ops = &via_std_pata_ops,
+};
+
+/**
+ * VT8251 has two sata channels, which are both Master/Slave mode. This is different
+ * from the implementation in libata, which supports only master mode
+ */
+static struct ata_port_info vt8251_port_info = {
+ .flags = ATA_FLAG_SATA | ATA_FLAG_SLAVE_POSS |
+ ATA_FLAG_NO_LEGACY,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x07,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &via_std_sata_ops,
};

MODULE_AUTHOR("Jeff Garzik");
@@ -152,22 +219,235 @@
MODULE_DEVICE_TABLE(pci, svia_pci_tbl);
MODULE_VERSION(DRV_VERSION);

+/**
+ * For controller 0x8251 and ox0581/0x5324, the sata controller registers do not
+ * locate in the PCI BARs, instead, they are in the PCI configure space.
+ */
static int svia_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val)
{
- if (sc_reg > SCR_CONTROL)
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 config_space_value;
+ u8 byte_temp;
+ u32 scr_value;
+ u32 dword_temp;
+ s8 shift;
+ u8 slot;
+
+ unsigned int scr_reg = sc_reg;
+
+ if (scr_reg > SCR_CONTROL) {
+ VPRINTK("invalid scr[%d] address!\n", scr_reg);
return -EINVAL;
- *val = ioread32(ap->ioaddr.scr_addr + (4 * sc_reg));
- return 0;
+ }
+
+
+ if (!(pdev->device == 0x5287 || pdev->device == 0x0581 ||
+ pdev->device == 0x5324))
+ *val = ioread32(ap->ioaddr.scr_addr + (4 * scr_reg));
+ else {
+ config_space_value = 0;
+ scr_value = 0;
+ shift = 0;
+
+ /**
+ * Since these chips do not use iomap to get the address of scr,
+ * we use these register to store the slot index
+ */
+ slot = (u32)ap->ioaddr.scr_addr & 0x03;
+
+ if (scr_reg == SCR_STATUS) {
+ pci_read_config_byte(pdev, (0xA0 + (slot & 0x03)),
+ &config_space_value);
+
+ /* Set the DET field, bit0 and 1 of the config byte */
+ scr_value |= (config_space_value & 0x03);
+
+ /* Set the SPD field, bit4 of the configure byte */
+ if (config_space_value & (1<<4))
+ scr_value |= (u8)(0x02 << 4);
+ else
+ scr_value |= (u8)(0x01 << 4);
+
+ /* Set the IPM field, bit2 and 3 of the config byte */
+ byte_temp = (config_space_value >> 2) & 0x03;
+ shift = byte_temp - 1;
+ if (shift < 0)
+ shift = 0;
+
+ dword_temp = (byte_temp + 0x01) << shift;
+ scr_value |= ((dword_temp & 0x0f) << 8);
+ } else if (scr_reg == SCR_ERROR) {
+ if (pdev->device == 0x5287)
+ pci_read_config_dword(pdev,
+ (0xB0 + (slot & 0x03) * 4), &scr_value);
+ else
+ pci_read_config_dword(pdev,
+ (0xA8 + (slot & 0x03) * 4), &scr_value);
+ } else if (scr_reg == SCR_CONTROL) {
+ pci_read_config_byte(pdev,
+ (0xA4 + (slot & 0x03)), &config_space_value);
+
+ /* Read the DET field, bit0 and bit1 */
+ byte_temp = config_space_value & 0x03;
+ scr_value |= (((byte_temp & 0x02) << 1) |
+ (byte_temp & 0x01));
+
+ /* Read the SPD field, bit4 */
+ scr_value &= ~(u32)0xF0;
+
+ /* Read the IPM field, bit2 and bit3 */
+ dword_temp = (config_space_value >> 2) & 0x03;
+ scr_value |= (dword_temp << 8);
+ }
+
+ *val = scr_value;
+ }
+
+ return 0;
}

+
+
static int svia_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val)
{
- if (sc_reg > SCR_CONTROL)
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 config_space_value;
+ u8 byte_temp;
+ u8 slot;
+
+ unsigned int scr_reg = sc_reg;
+
+ if (scr_reg > SCR_CONTROL) {
+ VPRINTK("invalid scr[%d] address!\n", scr_reg);
return -EINVAL;
- iowrite32(val, ap->ioaddr.scr_addr + (4 * sc_reg));
+ }
+
+ if (scr_reg == SCR_STATUS) {
+ VPRINTK("scr_status can not be write!\n");
+ return -EINVAL;
+ }
+
+ if (!(pdev->device == 0x5287 || pdev->device == 0x0581 ||
+ pdev->device == 0x5324))
+ iowrite32(val, ap->ioaddr.scr_addr + (4 * scr_reg));
+ else {
+ byte_temp = 0;
+ config_space_value = 0;
+
+ /* To get the slot index */
+ slot = (u32) ap->ioaddr.scr_addr & 0x03;
+
+ if (scr_reg == SCR_ERROR) {
+ if (pdev->device == 0x5287)
+ pci_write_config_dword(pdev,
+ (0xB0 + (slot & 0x03) * 4), val);
+ else
+ pci_write_config_dword(pdev,
+ (0xA8 + (slot & 0x03) * 4), val);
+ } else if (scr_reg == SCR_CONTROL) {
+ /* Set the DET field */
+ byte_temp = (((u8)(val & 0x04)) >> 1) |
+ ((u8)(val & 0x01));
+ config_space_value |= (byte_temp & 0x0F);
+
+ /* Set the IPM field */
+ byte_temp = (val >> 8) & 0x03;
+ config_space_value |= (byte_temp << 2);
+ pci_write_config_byte(pdev, (0xA4 + (slot & 0x03)),
+ config_space_value);
+ }
+ }
+
return 0;
}

+/**
+ * via_ata_sff_tf_load - send taskfile registers to host controller
+ * @ap: Port to which output is sent
+ * @tf: ATA taskfile register set
+ *
+ * Outputs ATA taskfile to standard ATA host controller.
+ *
+ * Note: This is to fix the internal bug of via chipsets,
+ * which will reset the device register after changing the IEN bit
+ * on ctl register
+ */
+static void via_ata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+ if (tf->ctl != ap->last_ctl) {
+ iowrite8(tf->ctl, ioaddr->ctl_addr);
+ iowrite8(tf->device, ioaddr->device_addr);
+ ap->last_ctl = tf->ctl;
+ ata_wait_idle(ap);
+ }
+
+ if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+ iowrite8(tf->hob_feature, ioaddr->feature_addr);
+ iowrite8(tf->hob_nsect, ioaddr->nsect_addr);
+ iowrite8(tf->hob_lbal, ioaddr->lbal_addr);
+ iowrite8(tf->hob_lbam, ioaddr->lbam_addr);
+ iowrite8(tf->hob_lbah, ioaddr->lbah_addr);
+ VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+ tf->hob_feature,
+ tf->hob_nsect,
+ tf->hob_lbal,
+ tf->hob_lbam,
+ tf->hob_lbah);
+ }
+
+ if (is_addr) {
+ iowrite8(tf->feature, ioaddr->feature_addr);
+ iowrite8(tf->nsect, ioaddr->nsect_addr);
+ iowrite8(tf->lbal, ioaddr->lbal_addr);
+ iowrite8(tf->lbam, ioaddr->lbam_addr);
+ iowrite8(tf->lbah, ioaddr->lbah_addr);
+ VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
+ tf->feature,
+ tf->nsect,
+ tf->lbal,
+ tf->lbam,
+ tf->lbah);
+ }
+
+ if (tf->flags & ATA_TFLAG_DEVICE) {
+ iowrite8(tf->device, ioaddr->device_addr);
+ VPRINTK("device 0x%X\n", tf->device);
+ }
+
+ ata_wait_idle(ap);
+}
+
+static int via_std_cable_detect(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u8 tmp;
+ int cfg_addr;
+
+ if (ap->port_no == 2) {
+ if (pdev->device == 0x3249) {
+ cfg_addr = 0xB3;
+ pci_read_config_byte(pdev, cfg_addr, &tmp);
+ if (tmp & 0x10)
+ return ATA_CBL_PATA40;
+ return ATA_CBL_PATA80;
+ }
+ } else if (ap->port_no == 1) {
+ if ((pdev->device == 0x0581) || (pdev->device == 0x5324)) {
+ cfg_addr = 0x50;
+ pci_read_config_byte(pdev, cfg_addr, &tmp);
+ if (tmp & 0x10) /* 40pin cable */
+ return ATA_CBL_PATA40;
+ else /* 80pin cable */
+ return ATA_CBL_PATA80;
+ }
+ }
+
+ return ATA_CBL_SATA;
+}
+
static void svia_noop_freeze(struct ata_port *ap)
{
/* Some VIA controllers choke if ATA_NIEN is manipulated in
@@ -177,19 +457,118 @@
ata_sff_irq_clear(ap);
}

+ /**
+ * via_std_sata_comreset - comreset for via sata controller
+ * @ap: target ATA port
+ * @isMaster: whether the input port is master slot
+ *
+ * Some VIA controllers does not use PCI BAR to map the sata
+ * controller registers, besides, some via data controllers
+ * have both master and slave slots. As a result, standard
+ * operations in libata could not work well
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep)
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+static int via_std_sata_comreset(struct ata_port *ap, int isMaster)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ unsigned long timeout = jiffies + (HZ * 5);
+ u32 sstatus, scontrol, serror;
+ int online = 0;
+
+ int slot;
+ int slot_start = ap->port_no;
+ unsigned int dev = 0;
+
+ unsigned int cbl_tmp = via_std_cable_detect(ap);
+ unsigned long flag_tmp = ap->flags;
+
+ if (ap->flags & ATA_FLAG_SLAVE_POSS)
+ slot_start = slot_start << 1;
+
+ ap->cbl = ATA_CBL_SATA;
+ ap->flags |= ATA_FLAG_SATA;
+
+ {
+ slot = (isMaster ? 0 : 1) + slot_start;
+ if (pdev->device == 0x5287 || pdev->device == 0x0581 ||
+ pdev->device == 0x5324)
+ ap->ioaddr.scr_addr = (void __iomem *)slot;
+
+ dev = (ap->flags & ATA_FLAG_SLAVE_POSS) ? (slot&0x01) : 0;
+ ap->ops->sff_dev_select(ap, dev);
+
+ DPRINTK("Comreset SATA Device %X PORT : %d, SLOT : %s\n", \
+ pdev->device, ap->port_no, \
+ (dev == 0) ? "<Master>" : "<Slave>");
+
+ ap->ops->scr_read(ap, SCR_CONTROL, &scontrol);
+ scontrol = (scontrol & 0x0f0) | 0x301;
+ ap->ops->scr_write(ap, SCR_CONTROL, scontrol);
+ ap->ops->scr_read(ap, SCR_CONTROL, &scontrol);
+
+ msleep(2);
+ scontrol = (scontrol & 0x0f0) | 0x300;
+ ap->ops->scr_write(ap, SCR_CONTROL, scontrol);
+ ap->ops->scr_read(ap, SCR_CONTROL, &scontrol); /* flush */
+
+ /* wait for phy to become ready, if necessary */
+ do {
+ msleep(200);
+ ap->ops->scr_read(ap, SCR_STATUS, &sstatus);
+ if ((sstatus & 0xf) != 1)
+ break;
+ } while (time_before(jiffies, timeout));
+
+ /* open code sata_print_link_status() */
+ ap->ops->scr_read(ap, SCR_STATUS, &sstatus);
+ ap->ops->scr_read(ap, SCR_CONTROL, &scontrol);
+ ap->ops->scr_read(ap, SCR_ERROR, &serror);
+ ap->ops->scr_write(ap, SCR_ERROR, serror);
+
+ ata_port_printk(ap, KERN_INFO,
+ "SATA link %s (SStatus %X SControl %X)\n",
+ online ? "up" : "down", sstatus, scontrol);
+
+ /* SStatus is read one more time */
+ ap->ops->scr_read(ap, SCR_STATUS, &sstatus);
+
+ online = (sstatus & 0xf) == 0x3;
+ if (!online) {
+ /* tell EH to bail */
+ DPRINTK("Comreset %X PORT : %d, SLOT : %s failed\n", \
+ pdev->device, ap->port_no, \
+ (dev == 0) ? "<Master>" : "<Slave>");
+
+ ap->cbl = cbl_tmp;
+ ap->flags = flag_tmp;
+ return -ENODEV;
+ }
+ DPRINTK("Comreset %X PORT : %d, SLOT : %s succeeded.\n", \
+ pdev->device, ap->port_no, \
+ (dev == 0) ? "<Master>" : "<Slave>");
+
+ }
+
+ ap->cbl = cbl_tmp;
+ ap->flags = flag_tmp;
+
+
+ return 0;
+}
+
+
/**
- * vt6420_prereset - prereset for vt6420
+ * via_std_sata_prereset - prereset for via sata controllers
* @link: target ATA link
* @deadline: deadline jiffies for the operation
*
- * 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.
+ * Standard error handling will retry several times if reset
+ * fails, this makes users cannot bear it.
*
* LOCKING:
* Kernel thread context (may sleep)
@@ -197,79 +576,146 @@
* RETURNS:
* 0 on success, -errno otherwise.
*/
-static int vt6420_prereset(struct ata_link *link, unsigned long deadline)
+static int via_std_sata_prereset(struct ata_link *link, unsigned long deadline)
{
struct ata_port *ap = link->ap;
- struct ata_eh_context *ehc = &ap->link.eh_context;
- unsigned long timeout = jiffies + (HZ * 5);
- u32 sstatus, scontrol;
- int online;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ int devmask = 0;
+ int slot_cnt = 0;
+ int slot;
+ int rc;
+ u8 status;

/* don't do any SCR stuff if we're not loading */
if (!(ap->pflags & ATA_PFLAG_LOADING))
goto skip_scr;

- /* Resume phy. This is the old SATA resume sequence */
- svia_scr_write(ap, SCR_CONTROL, 0x300);
- svia_scr_read(ap, SCR_CONTROL, &scontrol); /* flush */
-
- /* wait for phy to become ready, if necessary */
- do {
- msleep(200);
- svia_scr_read(ap, SCR_STATUS, &sstatus);
- if ((sstatus & 0xf) != 1)
- break;
- } while (time_before(jiffies, timeout));
-
- /* open code sata_print_link_status() */
- svia_scr_read(ap, SCR_STATUS, &sstatus);
- svia_scr_read(ap, SCR_CONTROL, &scontrol);
-
- 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, &sstatus);
-
- if (!online) {
- /* tell EH to bail */
- ehc->i.action &= ~ATA_EH_RESET;
- return 0;
+ slot_cnt = (ap->flags & ATA_FLAG_SLAVE_POSS) ? 2 : 1;
+ for (slot = 0; slot < slot_cnt; ++slot) {
+ rc = via_std_sata_comreset(ap, (slot%2 == 0) ? 1 : 0);
+ if (!rc)
+ devmask |= (1<<slot);
+
+ rc = ata_sff_wait_ready(link, deadline);
+ status = link->ap->ops->sff_check_status(link->ap);
+ DPRINTK("Prereset %X for PORT%d status %x: \n", \
+ pdev->device, ap->port_no, status);
+ if (rc != 0) {
+ DPRINTK("Prereset %X for PORT%d failed\n", \
+ pdev->device, ap->port_no);
+ devmask &= ~(1<<slot);
+ }
+ }
+
+ /*
+ * To fix the online check in libata. The softreset in libata will check
+ * whether the Master slot will be online for an ata link, so we should
+ * reset the master port again if the slave slot does not have a device
+ * attached
+ */
+ if ((slot_cnt == 2) && (devmask == 0x01))
+ via_std_sata_comreset(ap, 1);
+ else if (devmask == 0) {
+ DPRINTK("Prereset%X for PORT%d failed: (No device)\n", \
+ pdev->device, ap->port_no);
+ return -ENODEV;
}

- skip_scr:
+skip_scr:
/* wait for !BSY */
- ata_sff_wait_ready(link, deadline);
+ rc = ata_sff_wait_ready(link, deadline);
+ if (rc)
+ DPRINTK("Prereset %X for PORT%d failed\n", \
+ pdev->device, ap->port_no);

- return 0;
+ return rc;
}

-static int vt6421_pata_cable_detect(struct ata_port *ap)
+
+static void via_std_set_pio_mode(struct ata_port *ap, struct ata_device *adev)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 tmp;

- pci_read_config_byte(pdev, PATA_UDMA_TIMING, &tmp);
- if (tmp & 0x10)
- return ATA_CBL_PATA40;
- return ATA_CBL_PATA80;
-}
+ u8 cfg_byte;
+ int cfg_addr;
+ u16 tmp16;
+
+ tmp16 = pdev->device;
+
+ if ((tmp16 == 0x3249) && (ap->port_no == 2))/* PATA channel in VT6421 */
+ cfg_addr = 0xAB;
+ else if ((tmp16 == 0x0581 || tmp16 == 0x5324) &&
+ (ap->port_no == 1))/* PATA channel in VT8353 */
+ cfg_addr = 0x49;
+ else
+ return;

-static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev)
-{
- struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- static const u8 pio_bits[] = { 0xA8, 0x65, 0x65, 0x31, 0x20 };
- pci_write_config_byte(pdev, PATA_PIO_TIMING, pio_bits[adev->pio_mode - XFER_PIO_0]);
+ switch (adev->pio_mode & 0x07) {
+ case 0:
+ cfg_byte = 0xa8;
+ break;
+ case 1:
+ cfg_byte = 0x65;
+ break;
+ case 2:
+ cfg_byte = 0x65;
+ break;
+ case 3:
+ cfg_byte = 0x31;
+ break;
+ case 4:
+ cfg_byte = 0x20;
+ break;
+ default:
+ cfg_byte = 0x20;
+ }
+
+ pci_write_config_byte(pdev, cfg_addr, cfg_byte);
}

-static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev)
+static void via_std_set_dma_mode(struct ata_port *ap, struct ata_device *adev)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- static const u8 udma_bits[] = { 0xEE, 0xE8, 0xE6, 0xE4, 0xE2, 0xE1, 0xE0, 0xE0 };
- pci_write_config_byte(pdev, PATA_UDMA_TIMING, udma_bits[adev->dma_mode - XFER_UDMA_0]);
+ u8 cfg_byte;
+ int cfg_addr;
+ u16 tmp16;
+
+ tmp16 = pdev->device;
+
+ if ((tmp16 == 0x3249) && (ap->port_no == 2))
+ cfg_addr = 0xB3; /* PATA channel in VT6421 */
+ else if ((tmp16 == 0x0581) && (ap->port_no == 1))
+ cfg_addr = 0x50; /* PATA channel in VT8353 */
+ else
+ return;
+
+ switch (adev->dma_mode & 0x07) {
+ case 0:
+ cfg_byte = 0xee;
+ break;
+ case 1:
+ cfg_byte = 0xe8;
+ break;
+ case 2:
+ cfg_byte = 0xe6;
+ break;
+ case 3:
+ cfg_byte = 0xe4;
+ break;
+ case 4:
+ cfg_byte = 0xe2;
+ break;
+ case 5:
+ cfg_byte = 0xe1;
+ break;
+ case 6:
+ cfg_byte = 0xe0;
+ break;
+ default:
+ cfg_byte = 0xe0;
+ }
+
+ pci_write_config_byte(pdev, cfg_addr, cfg_byte);
}

static const unsigned int svia_bar_sizes[] = {
@@ -292,7 +738,7 @@

static void vt6421_init_addrs(struct ata_port *ap)
{
- void __iomem * const * iomap = ap->host->iomap;
+ void __iomem * const *iomap = ap->host->iomap;
void __iomem *reg_addr = iomap[ap->port_no];
void __iomem *bmdma_addr = iomap[4] + (ap->port_no * 8);
struct ata_ioports *ioaddr = &ap->ioaddr;
@@ -367,6 +813,26 @@
return 0;
}

+static int vt8251_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
+{
+ const struct ata_port_info *ppi[] = {&vt8251_port_info, NULL};
+ struct ata_host *host;
+ int rc;
+
+ rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+ if (rc)
+ return rc;
+ *r_host = host;
+
+ rc = pcim_iomap_regions(pdev, 1 << 5, DRV_NAME);
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev, "failed to iomap PCI BAR 5\n");
+ return rc;
+ }
+
+ return 0;
+}
+
static void svia_configure(struct pci_dev *pdev)
{
u8 tmp8;
@@ -424,8 +890,11 @@

if (board_id == vt6420)
bar_sizes = &svia_bar_sizes[0];
- else
+ else if (board_id == vt6421)
bar_sizes = &vt6421_bar_sizes[0];
+ else
+ bar_sizes = &svia_bar_sizes[0];
+

for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++)
if ((pci_resource_start(pdev, i) == 0) ||
@@ -440,8 +909,11 @@

if (board_id == vt6420)
rc = vt6420_prepare_host(pdev, &host);
- else
+ else if (board_id == vt6421)
rc = vt6421_prepare_host(pdev, &host);
+ else
+ rc = vt8251_prepare_host(pdev, &host);
+
if (rc)
return rc;


--- End Message ---