All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jeff Garzik <jeff@garzik.org>
To: Linux IDE mailing list <linux-ide@vger.kernel.org>
Cc: LKML <linux-kernel@vger.kernel.org>
Subject: [Fwd: [Patch] sata_via.c: add support for VT8251, fix the internal chips issue and]
Date: Fri, 22 Aug 2008 02:39:38 -0400	[thread overview]
Message-ID: <48AE5F2A.5060803@garzik.org> (raw)

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

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

	Jeff




[-- Attachment #2: [Patch] sata_via.c: add support for VT8251, fix the internal chips issue and.eml --]
[-- Type: message/rfc822, Size: 35421 bytes --]

From: <JosephChan@via.com.tw>
To: <jeff@garzik.org>
Cc: <alan@lxorguk.ukuu.org.uk>, <akpm@osdl.org>
Subject: [Patch] sata_via.c: add support for VT8251, fix the internal chips issue and
Date: Mon, 21 Jul 2008 15:38:38 +0800
Message-ID: <C80EF34A3D2E494DBAF9AC29C7AE4EB8076EC3C0@exchtp03.taipei.via.com.tw>

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@via.com.tw>

--- 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;
 

                 reply	other threads:[~2008-08-22  6:39 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=48AE5F2A.5060803@garzik.org \
    --to=jeff@garzik.org \
    --cc=linux-ide@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.