18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * pata_serverworks.c 	- Serverworks PATA for new ATA layer
48c2ecf20Sopenharmony_ci *			  (C) 2005 Red Hat Inc
58c2ecf20Sopenharmony_ci *			  (C) 2010 Bartlomiej Zolnierkiewicz
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * based upon
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * serverworks.c
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright (C) 1998-2000 Michel Aubry
128c2ecf20Sopenharmony_ci * Copyright (C) 1998-2000 Andrzej Krzysztofowicz
138c2ecf20Sopenharmony_ci * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
148c2ecf20Sopenharmony_ci * Portions copyright (c) 2001 Sun Microsystems
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * RCC/ServerWorks IDE driver for Linux
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *   OSB4: `Open South Bridge' IDE Interface (fn 1)
208c2ecf20Sopenharmony_ci *         supports UDMA mode 2 (33 MB/s)
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci *   CSB5: `Champion South Bridge' IDE Interface (fn 1)
238c2ecf20Sopenharmony_ci *         all revisions support UDMA mode 4 (66 MB/s)
248c2ecf20Sopenharmony_ci *         revision A2.0 and up support UDMA mode 5 (100 MB/s)
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci *         *** The CSB5 does not provide ANY register ***
278c2ecf20Sopenharmony_ci *         *** to detect 80-conductor cable presence. ***
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci *   CSB6: `Champion South Bridge' IDE Interface (optional: third channel)
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Documentation:
328c2ecf20Sopenharmony_ci *	Available under NDA only. Errata info very hard to get.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <linux/kernel.h>
368c2ecf20Sopenharmony_ci#include <linux/module.h>
378c2ecf20Sopenharmony_ci#include <linux/pci.h>
388c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
398c2ecf20Sopenharmony_ci#include <linux/delay.h>
408c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
418c2ecf20Sopenharmony_ci#include <linux/libata.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define DRV_NAME "pata_serverworks"
448c2ecf20Sopenharmony_ci#define DRV_VERSION "0.4.3"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define SVWKS_CSB5_REVISION_NEW	0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
478c2ecf20Sopenharmony_ci#define SVWKS_CSB6_REVISION	0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Seagate Barracuda ATA IV Family drives in UDMA mode 5
508c2ecf20Sopenharmony_ci * can overrun their FIFOs when used with the CSB5 */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const char *csb_bad_ata100[] = {
538c2ecf20Sopenharmony_ci	"ST320011A",
548c2ecf20Sopenharmony_ci	"ST340016A",
558c2ecf20Sopenharmony_ci	"ST360021A",
568c2ecf20Sopenharmony_ci	"ST380021A",
578c2ecf20Sopenharmony_ci	NULL
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/**
618c2ecf20Sopenharmony_ci *	oem_cable	-	Dell/Sun serverworks cable detection
628c2ecf20Sopenharmony_ci *	@ap: ATA port to do cable detect
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci *	Dell PowerEdge and Sun Cobalt 'Alpine' hide the 40/80 pin select
658c2ecf20Sopenharmony_ci *	for their interfaces in the top two bits of the subsystem ID.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int oem_cable(struct ata_port *ap)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (pdev->subsystem_device & (1 << (ap->port_no + 14)))
738c2ecf20Sopenharmony_ci		return ATA_CBL_PATA80;
748c2ecf20Sopenharmony_ci	return ATA_CBL_PATA40;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistruct sv_cable_table {
788c2ecf20Sopenharmony_ci	int device;
798c2ecf20Sopenharmony_ci	int subvendor;
808c2ecf20Sopenharmony_ci	int (*cable_detect)(struct ata_port *ap);
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic struct sv_cable_table cable_detect[] = {
848c2ecf20Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE,   PCI_VENDOR_ID_DELL, oem_cable },
858c2ecf20Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE,   PCI_VENDOR_ID_DELL, oem_cable },
868c2ecf20Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE,   PCI_VENDOR_ID_SUN,  oem_cable },
878c2ecf20Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_OSB4IDE,   PCI_ANY_ID, ata_cable_40wire  },
888c2ecf20Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE,   PCI_ANY_ID, ata_cable_unknown },
898c2ecf20Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE,   PCI_ANY_ID, ata_cable_unknown },
908c2ecf20Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2,  PCI_ANY_ID, ata_cable_unknown },
918c2ecf20Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, ata_cable_unknown },
928c2ecf20Sopenharmony_ci	{ }
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/**
968c2ecf20Sopenharmony_ci *	serverworks_cable_detect	-	cable detection
978c2ecf20Sopenharmony_ci *	@ap: ATA port
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci *	Perform cable detection according to the device and subvendor
1008c2ecf20Sopenharmony_ci *	identifications
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int serverworks_cable_detect(struct ata_port *ap)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
1068c2ecf20Sopenharmony_ci	struct sv_cable_table *cb = cable_detect;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	while(cb->device) {
1098c2ecf20Sopenharmony_ci		if (cb->device == pdev->device &&
1108c2ecf20Sopenharmony_ci		    (cb->subvendor == pdev->subsystem_vendor ||
1118c2ecf20Sopenharmony_ci		      cb->subvendor == PCI_ANY_ID)) {
1128c2ecf20Sopenharmony_ci			return cb->cable_detect(ap);
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci		cb++;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	BUG();
1188c2ecf20Sopenharmony_ci	return -1;	/* kill compiler warning */
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/**
1228c2ecf20Sopenharmony_ci *	serverworks_is_csb	-	Check for CSB or OSB
1238c2ecf20Sopenharmony_ci *	@pdev: PCI device to check
1248c2ecf20Sopenharmony_ci *
1258c2ecf20Sopenharmony_ci *	Returns true if the device being checked is known to be a CSB
1268c2ecf20Sopenharmony_ci *	series device.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic u8 serverworks_is_csb(struct pci_dev *pdev)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	switch (pdev->device) {
1328c2ecf20Sopenharmony_ci		case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
1338c2ecf20Sopenharmony_ci		case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:
1348c2ecf20Sopenharmony_ci		case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:
1358c2ecf20Sopenharmony_ci		case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:
1368c2ecf20Sopenharmony_ci			return 1;
1378c2ecf20Sopenharmony_ci		default:
1388c2ecf20Sopenharmony_ci			break;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/**
1448c2ecf20Sopenharmony_ci *	serverworks_osb4_filter	-	mode selection filter
1458c2ecf20Sopenharmony_ci *	@adev: ATA device
1468c2ecf20Sopenharmony_ci *	@mask: Mask of proposed modes
1478c2ecf20Sopenharmony_ci *
1488c2ecf20Sopenharmony_ci *	Filter the offered modes for the device to apply controller
1498c2ecf20Sopenharmony_ci *	specific rules. OSB4 requires no UDMA for disks due to a FIFO
1508c2ecf20Sopenharmony_ci *	bug we hit.
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	if (adev->class == ATA_DEV_ATA)
1568c2ecf20Sopenharmony_ci		mask &= ~ATA_MASK_UDMA;
1578c2ecf20Sopenharmony_ci	return mask;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/**
1628c2ecf20Sopenharmony_ci *	serverworks_csb_filter	-	mode selection filter
1638c2ecf20Sopenharmony_ci *	@adev: ATA device
1648c2ecf20Sopenharmony_ci *	@mask: Mask of proposed modes
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci *	Check the blacklist and disable UDMA5 if matched
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	const char *p;
1728c2ecf20Sopenharmony_ci	char model_num[ATA_ID_PROD_LEN + 1];
1738c2ecf20Sopenharmony_ci	int i;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Disk, UDMA */
1768c2ecf20Sopenharmony_ci	if (adev->class != ATA_DEV_ATA)
1778c2ecf20Sopenharmony_ci		return mask;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Actually do need to check */
1808c2ecf20Sopenharmony_ci	ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num));
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	for (i = 0; (p = csb_bad_ata100[i]) != NULL; i++) {
1838c2ecf20Sopenharmony_ci		if (!strcmp(p, model_num))
1848c2ecf20Sopenharmony_ci			mask &= ~(0xE0 << ATA_SHIFT_UDMA);
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	return mask;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/**
1908c2ecf20Sopenharmony_ci *	serverworks_set_piomode	-	set initial PIO mode data
1918c2ecf20Sopenharmony_ci *	@ap: ATA interface
1928c2ecf20Sopenharmony_ci *	@adev: ATA device
1938c2ecf20Sopenharmony_ci *
1948c2ecf20Sopenharmony_ci *	Program the OSB4/CSB5 timing registers for PIO. The PIO register
1958c2ecf20Sopenharmony_ci *	load is done as a simple lookup.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_cistatic void serverworks_set_piomode(struct ata_port *ap, struct ata_device *adev)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	static const u8 pio_mode[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };
2008c2ecf20Sopenharmony_ci	int offset = 1 + 2 * ap->port_no - adev->devno;
2018c2ecf20Sopenharmony_ci	int devbits = (2 * ap->port_no + adev->devno) * 4;
2028c2ecf20Sopenharmony_ci	u16 csb5_pio;
2038c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
2048c2ecf20Sopenharmony_ci	int pio = adev->pio_mode - XFER_PIO_0;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, 0x40 + offset, pio_mode[pio]);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* The OSB4 just requires the timing but the CSB series want the
2098c2ecf20Sopenharmony_ci	   mode number as well */
2108c2ecf20Sopenharmony_ci	if (serverworks_is_csb(pdev)) {
2118c2ecf20Sopenharmony_ci		pci_read_config_word(pdev, 0x4A, &csb5_pio);
2128c2ecf20Sopenharmony_ci		csb5_pio &= ~(0x0F << devbits);
2138c2ecf20Sopenharmony_ci		pci_write_config_word(pdev, 0x4A, csb5_pio | (pio << devbits));
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/**
2188c2ecf20Sopenharmony_ci *	serverworks_set_dmamode	-	set initial DMA mode data
2198c2ecf20Sopenharmony_ci *	@ap: ATA interface
2208c2ecf20Sopenharmony_ci *	@adev: ATA device
2218c2ecf20Sopenharmony_ci *
2228c2ecf20Sopenharmony_ci *	Program the MWDMA/UDMA modes for the serverworks OSB4/CSB5
2238c2ecf20Sopenharmony_ci *	chipset. The MWDMA mode values are pulled from a lookup table
2248c2ecf20Sopenharmony_ci *	while the chipset uses mode number for UDMA.
2258c2ecf20Sopenharmony_ci */
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	static const u8 dma_mode[] = { 0x77, 0x21, 0x20 };
2308c2ecf20Sopenharmony_ci	int offset = 1 + 2 * ap->port_no - adev->devno;
2318c2ecf20Sopenharmony_ci	int devbits = 2 * ap->port_no + adev->devno;
2328c2ecf20Sopenharmony_ci	u8 ultra;
2338c2ecf20Sopenharmony_ci	u8 ultra_cfg;
2348c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, 0x54, &ultra_cfg);
2378c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra);
2388c2ecf20Sopenharmony_ci	ultra &= ~(0x0F << (adev->devno * 4));
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (adev->dma_mode >= XFER_UDMA_0) {
2418c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0x44 + offset,  0x20);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		ultra |= (adev->dma_mode - XFER_UDMA_0)
2448c2ecf20Sopenharmony_ci					<< (adev->devno * 4);
2458c2ecf20Sopenharmony_ci		ultra_cfg |=  (1 << devbits);
2468c2ecf20Sopenharmony_ci	} else {
2478c2ecf20Sopenharmony_ci		pci_write_config_byte(pdev, 0x44 + offset,
2488c2ecf20Sopenharmony_ci			dma_mode[adev->dma_mode - XFER_MW_DMA_0]);
2498c2ecf20Sopenharmony_ci		ultra_cfg &= ~(1 << devbits);
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra);
2528c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, 0x54, ultra_cfg);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic struct scsi_host_template serverworks_osb4_sht = {
2568c2ecf20Sopenharmony_ci	ATA_BMDMA_SHT(DRV_NAME),
2578c2ecf20Sopenharmony_ci	.sg_tablesize	= LIBATA_DUMB_MAX_PRD,
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic struct scsi_host_template serverworks_csb_sht = {
2618c2ecf20Sopenharmony_ci	ATA_BMDMA_SHT(DRV_NAME),
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic struct ata_port_operations serverworks_osb4_port_ops = {
2658c2ecf20Sopenharmony_ci	.inherits	= &ata_bmdma_port_ops,
2668c2ecf20Sopenharmony_ci	.qc_prep	= ata_bmdma_dumb_qc_prep,
2678c2ecf20Sopenharmony_ci	.cable_detect	= serverworks_cable_detect,
2688c2ecf20Sopenharmony_ci	.mode_filter	= serverworks_osb4_filter,
2698c2ecf20Sopenharmony_ci	.set_piomode	= serverworks_set_piomode,
2708c2ecf20Sopenharmony_ci	.set_dmamode	= serverworks_set_dmamode,
2718c2ecf20Sopenharmony_ci};
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic struct ata_port_operations serverworks_csb_port_ops = {
2748c2ecf20Sopenharmony_ci	.inherits	= &serverworks_osb4_port_ops,
2758c2ecf20Sopenharmony_ci	.qc_prep	= ata_bmdma_qc_prep,
2768c2ecf20Sopenharmony_ci	.mode_filter	= serverworks_csb_filter,
2778c2ecf20Sopenharmony_ci};
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int serverworks_fixup_osb4(struct pci_dev *pdev)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	u32 reg;
2828c2ecf20Sopenharmony_ci	struct pci_dev *isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
2838c2ecf20Sopenharmony_ci		  PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL);
2848c2ecf20Sopenharmony_ci	if (isa_dev) {
2858c2ecf20Sopenharmony_ci		pci_read_config_dword(isa_dev, 0x64, &reg);
2868c2ecf20Sopenharmony_ci		reg &= ~0x00002000; /* disable 600ns interrupt mask */
2878c2ecf20Sopenharmony_ci		if (!(reg & 0x00004000))
2888c2ecf20Sopenharmony_ci			printk(KERN_DEBUG DRV_NAME ": UDMA not BIOS enabled.\n");
2898c2ecf20Sopenharmony_ci		reg |=  0x00004000; /* enable UDMA/33 support */
2908c2ecf20Sopenharmony_ci		pci_write_config_dword(isa_dev, 0x64, reg);
2918c2ecf20Sopenharmony_ci		pci_dev_put(isa_dev);
2928c2ecf20Sopenharmony_ci		return 0;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	printk(KERN_WARNING DRV_NAME ": Unable to find bridge.\n");
2958c2ecf20Sopenharmony_ci	return -ENODEV;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int serverworks_fixup_csb(struct pci_dev *pdev)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	u8 btr;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Third Channel Test */
3038c2ecf20Sopenharmony_ci	if (!(PCI_FUNC(pdev->devfn) & 1)) {
3048c2ecf20Sopenharmony_ci		struct pci_dev * findev = NULL;
3058c2ecf20Sopenharmony_ci		u32 reg4c = 0;
3068c2ecf20Sopenharmony_ci		findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
3078c2ecf20Sopenharmony_ci			PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL);
3088c2ecf20Sopenharmony_ci		if (findev) {
3098c2ecf20Sopenharmony_ci			pci_read_config_dword(findev, 0x4C, &reg4c);
3108c2ecf20Sopenharmony_ci			reg4c &= ~0x000007FF;
3118c2ecf20Sopenharmony_ci			reg4c |=  0x00000040;
3128c2ecf20Sopenharmony_ci			reg4c |=  0x00000020;
3138c2ecf20Sopenharmony_ci			pci_write_config_dword(findev, 0x4C, reg4c);
3148c2ecf20Sopenharmony_ci			pci_dev_put(findev);
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci	} else {
3178c2ecf20Sopenharmony_ci		struct pci_dev * findev = NULL;
3188c2ecf20Sopenharmony_ci		u8 reg41 = 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
3218c2ecf20Sopenharmony_ci				PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL);
3228c2ecf20Sopenharmony_ci		if (findev) {
3238c2ecf20Sopenharmony_ci			pci_read_config_byte(findev, 0x41, &reg41);
3248c2ecf20Sopenharmony_ci			reg41 &= ~0x40;
3258c2ecf20Sopenharmony_ci			pci_write_config_byte(findev, 0x41, reg41);
3268c2ecf20Sopenharmony_ci			pci_dev_put(findev);
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	/* setup the UDMA Control register
3308c2ecf20Sopenharmony_ci	 *
3318c2ecf20Sopenharmony_ci	 * 1. clear bit 6 to enable DMA
3328c2ecf20Sopenharmony_ci	 * 2. enable DMA modes with bits 0-1
3338c2ecf20Sopenharmony_ci	 * 	00 : legacy
3348c2ecf20Sopenharmony_ci	 * 	01 : udma2
3358c2ecf20Sopenharmony_ci	 * 	10 : udma2/udma4
3368c2ecf20Sopenharmony_ci	 * 	11 : udma2/udma4/udma5
3378c2ecf20Sopenharmony_ci	 */
3388c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, 0x5A, &btr);
3398c2ecf20Sopenharmony_ci	btr &= ~0x40;
3408c2ecf20Sopenharmony_ci	if (!(PCI_FUNC(pdev->devfn) & 1))
3418c2ecf20Sopenharmony_ci		btr |= 0x2;
3428c2ecf20Sopenharmony_ci	else
3438c2ecf20Sopenharmony_ci		btr |= (pdev->revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2;
3448c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, 0x5A, btr);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return btr;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic void serverworks_fixup_ht1000(struct pci_dev *pdev)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	u8 btr;
3528c2ecf20Sopenharmony_ci	/* Setup HT1000 SouthBridge Controller - Single Channel Only */
3538c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, 0x5A, &btr);
3548c2ecf20Sopenharmony_ci	btr &= ~0x40;
3558c2ecf20Sopenharmony_ci	btr |= 0x3;
3568c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, 0x5A, btr);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic int serverworks_fixup(struct pci_dev *pdev)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	int rc = 0;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* Force master latency timer to 64 PCI clocks */
3648c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	switch (pdev->device) {
3678c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE:
3688c2ecf20Sopenharmony_ci		rc = serverworks_fixup_osb4(pdev);
3698c2ecf20Sopenharmony_ci		break;
3708c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
3718c2ecf20Sopenharmony_ci		ata_pci_bmdma_clear_simplex(pdev);
3728c2ecf20Sopenharmony_ci		fallthrough;
3738c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:
3748c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:
3758c2ecf20Sopenharmony_ci		rc = serverworks_fixup_csb(pdev);
3768c2ecf20Sopenharmony_ci		break;
3778c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:
3788c2ecf20Sopenharmony_ci		serverworks_fixup_ht1000(pdev);
3798c2ecf20Sopenharmony_ci		break;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return rc;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	static const struct ata_port_info info[4] = {
3888c2ecf20Sopenharmony_ci		{ /* OSB4 */
3898c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
3908c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
3918c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
3928c2ecf20Sopenharmony_ci			.udma_mask = ATA_UDMA2,
3938c2ecf20Sopenharmony_ci			.port_ops = &serverworks_osb4_port_ops
3948c2ecf20Sopenharmony_ci		}, { /* OSB4 no UDMA */
3958c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
3968c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
3978c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
3988c2ecf20Sopenharmony_ci			/* No UDMA */
3998c2ecf20Sopenharmony_ci			.port_ops = &serverworks_osb4_port_ops
4008c2ecf20Sopenharmony_ci		}, { /* CSB5 */
4018c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
4028c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
4038c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
4048c2ecf20Sopenharmony_ci			.udma_mask = ATA_UDMA4,
4058c2ecf20Sopenharmony_ci			.port_ops = &serverworks_csb_port_ops
4068c2ecf20Sopenharmony_ci		}, { /* CSB5 - later revisions*/
4078c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
4088c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
4098c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
4108c2ecf20Sopenharmony_ci			.udma_mask = ATA_UDMA5,
4118c2ecf20Sopenharmony_ci			.port_ops = &serverworks_csb_port_ops
4128c2ecf20Sopenharmony_ci		}
4138c2ecf20Sopenharmony_ci	};
4148c2ecf20Sopenharmony_ci	const struct ata_port_info *ppi[] = { &info[id->driver_data], NULL };
4158c2ecf20Sopenharmony_ci	struct scsi_host_template *sht = &serverworks_csb_sht;
4168c2ecf20Sopenharmony_ci	int rc;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	rc = pcim_enable_device(pdev);
4198c2ecf20Sopenharmony_ci	if (rc)
4208c2ecf20Sopenharmony_ci		return rc;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	rc = serverworks_fixup(pdev);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* OSB4 : South Bridge and IDE */
4258c2ecf20Sopenharmony_ci	if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
4268c2ecf20Sopenharmony_ci		/* Select non UDMA capable OSB4 if we can't do fixups */
4278c2ecf20Sopenharmony_ci		if (rc < 0)
4288c2ecf20Sopenharmony_ci			ppi[0] = &info[1];
4298c2ecf20Sopenharmony_ci		sht = &serverworks_osb4_sht;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci	/* setup CSB5/CSB6 : South Bridge and IDE option RAID */
4328c2ecf20Sopenharmony_ci	else if ((pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) ||
4338c2ecf20Sopenharmony_ci		 (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
4348c2ecf20Sopenharmony_ci		 (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) {
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		 /* If the returned btr is the newer revision then
4378c2ecf20Sopenharmony_ci		    select the right info block */
4388c2ecf20Sopenharmony_ci		 if (rc == 3)
4398c2ecf20Sopenharmony_ci		 	ppi[0] = &info[3];
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		/* Is this the 3rd channel CSB6 IDE ? */
4428c2ecf20Sopenharmony_ci		if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)
4438c2ecf20Sopenharmony_ci			ppi[1] = &ata_dummy_port_info;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	return ata_pci_bmdma_init_one(pdev, ppi, sht, NULL, 0);
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
4508c2ecf20Sopenharmony_cistatic int serverworks_reinit_one(struct pci_dev *pdev)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct ata_host *host = pci_get_drvdata(pdev);
4538c2ecf20Sopenharmony_ci	int rc;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	rc = ata_pci_device_do_resume(pdev);
4568c2ecf20Sopenharmony_ci	if (rc)
4578c2ecf20Sopenharmony_ci		return rc;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	(void)serverworks_fixup(pdev);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	ata_host_resume(host);
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci#endif
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic const struct pci_device_id serverworks[] = {
4678c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0},
4688c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2},
4698c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2},
4708c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2},
4718c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2},
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	{ },
4748c2ecf20Sopenharmony_ci};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic struct pci_driver serverworks_pci_driver = {
4778c2ecf20Sopenharmony_ci	.name 		= DRV_NAME,
4788c2ecf20Sopenharmony_ci	.id_table	= serverworks,
4798c2ecf20Sopenharmony_ci	.probe 		= serverworks_init_one,
4808c2ecf20Sopenharmony_ci	.remove		= ata_pci_remove_one,
4818c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
4828c2ecf20Sopenharmony_ci	.suspend	= ata_pci_device_suspend,
4838c2ecf20Sopenharmony_ci	.resume		= serverworks_reinit_one,
4848c2ecf20Sopenharmony_ci#endif
4858c2ecf20Sopenharmony_ci};
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cimodule_pci_driver(serverworks_pci_driver);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alan Cox");
4908c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6");
4918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, serverworks);
4938c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
494