162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * pata_serverworks.c 	- Serverworks PATA for new ATA layer
462306a36Sopenharmony_ci *			  (C) 2005 Red Hat Inc
562306a36Sopenharmony_ci *			  (C) 2010 Bartlomiej Zolnierkiewicz
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * based upon
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * serverworks.c
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright (C) 1998-2000 Michel Aubry
1262306a36Sopenharmony_ci * Copyright (C) 1998-2000 Andrzej Krzysztofowicz
1362306a36Sopenharmony_ci * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
1462306a36Sopenharmony_ci * Portions copyright (c) 2001 Sun Microsystems
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * RCC/ServerWorks IDE driver for Linux
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *   OSB4: `Open South Bridge' IDE Interface (fn 1)
2062306a36Sopenharmony_ci *         supports UDMA mode 2 (33 MB/s)
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *   CSB5: `Champion South Bridge' IDE Interface (fn 1)
2362306a36Sopenharmony_ci *         all revisions support UDMA mode 4 (66 MB/s)
2462306a36Sopenharmony_ci *         revision A2.0 and up support UDMA mode 5 (100 MB/s)
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci *         *** The CSB5 does not provide ANY register ***
2762306a36Sopenharmony_ci *         *** to detect 80-conductor cable presence. ***
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci *   CSB6: `Champion South Bridge' IDE Interface (optional: third channel)
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * Documentation:
3262306a36Sopenharmony_ci *	Available under NDA only. Errata info very hard to get.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/kernel.h>
3662306a36Sopenharmony_ci#include <linux/module.h>
3762306a36Sopenharmony_ci#include <linux/pci.h>
3862306a36Sopenharmony_ci#include <linux/blkdev.h>
3962306a36Sopenharmony_ci#include <linux/delay.h>
4062306a36Sopenharmony_ci#include <scsi/scsi_host.h>
4162306a36Sopenharmony_ci#include <linux/libata.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define DRV_NAME "pata_serverworks"
4462306a36Sopenharmony_ci#define DRV_VERSION "0.4.3"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define SVWKS_CSB5_REVISION_NEW	0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
4762306a36Sopenharmony_ci#define SVWKS_CSB6_REVISION	0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Seagate Barracuda ATA IV Family drives in UDMA mode 5
5062306a36Sopenharmony_ci * can overrun their FIFOs when used with the CSB5 */
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic const char *csb_bad_ata100[] = {
5362306a36Sopenharmony_ci	"ST320011A",
5462306a36Sopenharmony_ci	"ST340016A",
5562306a36Sopenharmony_ci	"ST360021A",
5662306a36Sopenharmony_ci	"ST380021A",
5762306a36Sopenharmony_ci	NULL
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/**
6162306a36Sopenharmony_ci *	oem_cable	-	Dell/Sun serverworks cable detection
6262306a36Sopenharmony_ci *	@ap: ATA port to do cable detect
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci *	Dell PowerEdge and Sun Cobalt 'Alpine' hide the 40/80 pin select
6562306a36Sopenharmony_ci *	for their interfaces in the top two bits of the subsystem ID.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int oem_cable(struct ata_port *ap)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (pdev->subsystem_device & (1 << (ap->port_no + 14)))
7362306a36Sopenharmony_ci		return ATA_CBL_PATA80;
7462306a36Sopenharmony_ci	return ATA_CBL_PATA40;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistruct sv_cable_table {
7862306a36Sopenharmony_ci	int device;
7962306a36Sopenharmony_ci	int subvendor;
8062306a36Sopenharmony_ci	int (*cable_detect)(struct ata_port *ap);
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct sv_cable_table cable_detect[] = {
8462306a36Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE,   PCI_VENDOR_ID_DELL, oem_cable },
8562306a36Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE,   PCI_VENDOR_ID_DELL, oem_cable },
8662306a36Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE,   PCI_VENDOR_ID_SUN,  oem_cable },
8762306a36Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_OSB4IDE,   PCI_ANY_ID, ata_cable_40wire  },
8862306a36Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE,   PCI_ANY_ID, ata_cable_unknown },
8962306a36Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE,   PCI_ANY_ID, ata_cable_unknown },
9062306a36Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2,  PCI_ANY_ID, ata_cable_unknown },
9162306a36Sopenharmony_ci	{ PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, ata_cable_unknown },
9262306a36Sopenharmony_ci	{ }
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/**
9662306a36Sopenharmony_ci *	serverworks_cable_detect	-	cable detection
9762306a36Sopenharmony_ci *	@ap: ATA port
9862306a36Sopenharmony_ci *
9962306a36Sopenharmony_ci *	Perform cable detection according to the device and subvendor
10062306a36Sopenharmony_ci *	identifications
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int serverworks_cable_detect(struct ata_port *ap)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
10662306a36Sopenharmony_ci	struct sv_cable_table *cb = cable_detect;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	while(cb->device) {
10962306a36Sopenharmony_ci		if (cb->device == pdev->device &&
11062306a36Sopenharmony_ci		    (cb->subvendor == pdev->subsystem_vendor ||
11162306a36Sopenharmony_ci		      cb->subvendor == PCI_ANY_ID)) {
11262306a36Sopenharmony_ci			return cb->cable_detect(ap);
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci		cb++;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	BUG();
11862306a36Sopenharmony_ci	return -1;	/* kill compiler warning */
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/**
12262306a36Sopenharmony_ci *	serverworks_is_csb	-	Check for CSB or OSB
12362306a36Sopenharmony_ci *	@pdev: PCI device to check
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci *	Returns true if the device being checked is known to be a CSB
12662306a36Sopenharmony_ci *	series device.
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic u8 serverworks_is_csb(struct pci_dev *pdev)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	switch (pdev->device) {
13262306a36Sopenharmony_ci		case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
13362306a36Sopenharmony_ci		case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:
13462306a36Sopenharmony_ci		case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:
13562306a36Sopenharmony_ci		case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:
13662306a36Sopenharmony_ci			return 1;
13762306a36Sopenharmony_ci		default:
13862306a36Sopenharmony_ci			break;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/**
14462306a36Sopenharmony_ci *	serverworks_osb4_filter	-	mode selection filter
14562306a36Sopenharmony_ci *	@adev: ATA device
14662306a36Sopenharmony_ci *	@mask: Mask of proposed modes
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci *	Filter the offered modes for the device to apply controller
14962306a36Sopenharmony_ci *	specific rules. OSB4 requires no UDMA for disks due to a FIFO
15062306a36Sopenharmony_ci *	bug we hit.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic unsigned int serverworks_osb4_filter(struct ata_device *adev, unsigned int mask)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	if (adev->class == ATA_DEV_ATA)
15662306a36Sopenharmony_ci		mask &= ~ATA_MASK_UDMA;
15762306a36Sopenharmony_ci	return mask;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/**
16262306a36Sopenharmony_ci *	serverworks_csb_filter	-	mode selection filter
16362306a36Sopenharmony_ci *	@adev: ATA device
16462306a36Sopenharmony_ci *	@mask: Mask of proposed modes
16562306a36Sopenharmony_ci *
16662306a36Sopenharmony_ci *	Check the blacklist and disable UDMA5 if matched
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic unsigned int serverworks_csb_filter(struct ata_device *adev, unsigned int mask)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	const char *p;
17262306a36Sopenharmony_ci	char model_num[ATA_ID_PROD_LEN + 1];
17362306a36Sopenharmony_ci	int i;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* Disk, UDMA */
17662306a36Sopenharmony_ci	if (adev->class != ATA_DEV_ATA)
17762306a36Sopenharmony_ci		return mask;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Actually do need to check */
18062306a36Sopenharmony_ci	ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num));
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	for (i = 0; (p = csb_bad_ata100[i]) != NULL; i++) {
18362306a36Sopenharmony_ci		if (!strcmp(p, model_num))
18462306a36Sopenharmony_ci			mask &= ~(0xE0 << ATA_SHIFT_UDMA);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	return mask;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/**
19062306a36Sopenharmony_ci *	serverworks_set_piomode	-	set initial PIO mode data
19162306a36Sopenharmony_ci *	@ap: ATA interface
19262306a36Sopenharmony_ci *	@adev: ATA device
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci *	Program the OSB4/CSB5 timing registers for PIO. The PIO register
19562306a36Sopenharmony_ci *	load is done as a simple lookup.
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_cistatic void serverworks_set_piomode(struct ata_port *ap, struct ata_device *adev)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	static const u8 pio_mode[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };
20062306a36Sopenharmony_ci	int offset = 1 + 2 * ap->port_no - adev->devno;
20162306a36Sopenharmony_ci	int devbits = (2 * ap->port_no + adev->devno) * 4;
20262306a36Sopenharmony_ci	u16 csb5_pio;
20362306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
20462306a36Sopenharmony_ci	int pio = adev->pio_mode - XFER_PIO_0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	pci_write_config_byte(pdev, 0x40 + offset, pio_mode[pio]);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* The OSB4 just requires the timing but the CSB series want the
20962306a36Sopenharmony_ci	   mode number as well */
21062306a36Sopenharmony_ci	if (serverworks_is_csb(pdev)) {
21162306a36Sopenharmony_ci		pci_read_config_word(pdev, 0x4A, &csb5_pio);
21262306a36Sopenharmony_ci		csb5_pio &= ~(0x0F << devbits);
21362306a36Sopenharmony_ci		pci_write_config_word(pdev, 0x4A, csb5_pio | (pio << devbits));
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/**
21862306a36Sopenharmony_ci *	serverworks_set_dmamode	-	set initial DMA mode data
21962306a36Sopenharmony_ci *	@ap: ATA interface
22062306a36Sopenharmony_ci *	@adev: ATA device
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci *	Program the MWDMA/UDMA modes for the serverworks OSB4/CSB5
22362306a36Sopenharmony_ci *	chipset. The MWDMA mode values are pulled from a lookup table
22462306a36Sopenharmony_ci *	while the chipset uses mode number for UDMA.
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	static const u8 dma_mode[] = { 0x77, 0x21, 0x20 };
23062306a36Sopenharmony_ci	int offset = 1 + 2 * ap->port_no - adev->devno;
23162306a36Sopenharmony_ci	int devbits = 2 * ap->port_no + adev->devno;
23262306a36Sopenharmony_ci	u8 ultra;
23362306a36Sopenharmony_ci	u8 ultra_cfg;
23462306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	pci_read_config_byte(pdev, 0x54, &ultra_cfg);
23762306a36Sopenharmony_ci	pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra);
23862306a36Sopenharmony_ci	ultra &= ~(0x0F << (adev->devno * 4));
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (adev->dma_mode >= XFER_UDMA_0) {
24162306a36Sopenharmony_ci		pci_write_config_byte(pdev, 0x44 + offset,  0x20);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		ultra |= (adev->dma_mode - XFER_UDMA_0)
24462306a36Sopenharmony_ci					<< (adev->devno * 4);
24562306a36Sopenharmony_ci		ultra_cfg |=  (1 << devbits);
24662306a36Sopenharmony_ci	} else {
24762306a36Sopenharmony_ci		pci_write_config_byte(pdev, 0x44 + offset,
24862306a36Sopenharmony_ci			dma_mode[adev->dma_mode - XFER_MW_DMA_0]);
24962306a36Sopenharmony_ci		ultra_cfg &= ~(1 << devbits);
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci	pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra);
25262306a36Sopenharmony_ci	pci_write_config_byte(pdev, 0x54, ultra_cfg);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic const struct scsi_host_template serverworks_osb4_sht = {
25662306a36Sopenharmony_ci	ATA_BASE_SHT(DRV_NAME),
25762306a36Sopenharmony_ci	.sg_tablesize	= LIBATA_DUMB_MAX_PRD,
25862306a36Sopenharmony_ci	.dma_boundary	= ATA_DMA_BOUNDARY,
25962306a36Sopenharmony_ci};
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic const struct scsi_host_template serverworks_csb_sht = {
26262306a36Sopenharmony_ci	ATA_BMDMA_SHT(DRV_NAME),
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic struct ata_port_operations serverworks_osb4_port_ops = {
26662306a36Sopenharmony_ci	.inherits	= &ata_bmdma_port_ops,
26762306a36Sopenharmony_ci	.qc_prep	= ata_bmdma_dumb_qc_prep,
26862306a36Sopenharmony_ci	.cable_detect	= serverworks_cable_detect,
26962306a36Sopenharmony_ci	.mode_filter	= serverworks_osb4_filter,
27062306a36Sopenharmony_ci	.set_piomode	= serverworks_set_piomode,
27162306a36Sopenharmony_ci	.set_dmamode	= serverworks_set_dmamode,
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic struct ata_port_operations serverworks_csb_port_ops = {
27562306a36Sopenharmony_ci	.inherits	= &serverworks_osb4_port_ops,
27662306a36Sopenharmony_ci	.qc_prep	= ata_bmdma_qc_prep,
27762306a36Sopenharmony_ci	.mode_filter	= serverworks_csb_filter,
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int serverworks_fixup_osb4(struct pci_dev *pdev)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	u32 reg;
28362306a36Sopenharmony_ci	struct pci_dev *isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
28462306a36Sopenharmony_ci		  PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL);
28562306a36Sopenharmony_ci	if (isa_dev) {
28662306a36Sopenharmony_ci		pci_read_config_dword(isa_dev, 0x64, &reg);
28762306a36Sopenharmony_ci		reg &= ~0x00002000; /* disable 600ns interrupt mask */
28862306a36Sopenharmony_ci		if (!(reg & 0x00004000))
28962306a36Sopenharmony_ci			dev_info(&pdev->dev, "UDMA not BIOS enabled.\n");
29062306a36Sopenharmony_ci		reg |=  0x00004000; /* enable UDMA/33 support */
29162306a36Sopenharmony_ci		pci_write_config_dword(isa_dev, 0x64, reg);
29262306a36Sopenharmony_ci		pci_dev_put(isa_dev);
29362306a36Sopenharmony_ci		return 0;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci	dev_warn(&pdev->dev, "Unable to find bridge.\n");
29662306a36Sopenharmony_ci	return -ENODEV;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int serverworks_fixup_csb(struct pci_dev *pdev)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	u8 btr;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* Third Channel Test */
30462306a36Sopenharmony_ci	if (!(PCI_FUNC(pdev->devfn) & 1)) {
30562306a36Sopenharmony_ci		struct pci_dev * findev = NULL;
30662306a36Sopenharmony_ci		u32 reg4c = 0;
30762306a36Sopenharmony_ci		findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
30862306a36Sopenharmony_ci			PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL);
30962306a36Sopenharmony_ci		if (findev) {
31062306a36Sopenharmony_ci			pci_read_config_dword(findev, 0x4C, &reg4c);
31162306a36Sopenharmony_ci			reg4c &= ~0x000007FF;
31262306a36Sopenharmony_ci			reg4c |=  0x00000040;
31362306a36Sopenharmony_ci			reg4c |=  0x00000020;
31462306a36Sopenharmony_ci			pci_write_config_dword(findev, 0x4C, reg4c);
31562306a36Sopenharmony_ci			pci_dev_put(findev);
31662306a36Sopenharmony_ci		}
31762306a36Sopenharmony_ci	} else {
31862306a36Sopenharmony_ci		struct pci_dev * findev = NULL;
31962306a36Sopenharmony_ci		u8 reg41 = 0;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci		findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
32262306a36Sopenharmony_ci				PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL);
32362306a36Sopenharmony_ci		if (findev) {
32462306a36Sopenharmony_ci			pci_read_config_byte(findev, 0x41, &reg41);
32562306a36Sopenharmony_ci			reg41 &= ~0x40;
32662306a36Sopenharmony_ci			pci_write_config_byte(findev, 0x41, reg41);
32762306a36Sopenharmony_ci			pci_dev_put(findev);
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	/* setup the UDMA Control register
33162306a36Sopenharmony_ci	 *
33262306a36Sopenharmony_ci	 * 1. clear bit 6 to enable DMA
33362306a36Sopenharmony_ci	 * 2. enable DMA modes with bits 0-1
33462306a36Sopenharmony_ci	 * 	00 : legacy
33562306a36Sopenharmony_ci	 * 	01 : udma2
33662306a36Sopenharmony_ci	 * 	10 : udma2/udma4
33762306a36Sopenharmony_ci	 * 	11 : udma2/udma4/udma5
33862306a36Sopenharmony_ci	 */
33962306a36Sopenharmony_ci	pci_read_config_byte(pdev, 0x5A, &btr);
34062306a36Sopenharmony_ci	btr &= ~0x40;
34162306a36Sopenharmony_ci	if (!(PCI_FUNC(pdev->devfn) & 1))
34262306a36Sopenharmony_ci		btr |= 0x2;
34362306a36Sopenharmony_ci	else
34462306a36Sopenharmony_ci		btr |= (pdev->revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2;
34562306a36Sopenharmony_ci	pci_write_config_byte(pdev, 0x5A, btr);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return btr;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic void serverworks_fixup_ht1000(struct pci_dev *pdev)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	u8 btr;
35362306a36Sopenharmony_ci	/* Setup HT1000 SouthBridge Controller - Single Channel Only */
35462306a36Sopenharmony_ci	pci_read_config_byte(pdev, 0x5A, &btr);
35562306a36Sopenharmony_ci	btr &= ~0x40;
35662306a36Sopenharmony_ci	btr |= 0x3;
35762306a36Sopenharmony_ci	pci_write_config_byte(pdev, 0x5A, btr);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int serverworks_fixup(struct pci_dev *pdev)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	int rc = 0;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Force master latency timer to 64 PCI clocks */
36562306a36Sopenharmony_ci	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	switch (pdev->device) {
36862306a36Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE:
36962306a36Sopenharmony_ci		rc = serverworks_fixup_osb4(pdev);
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
37262306a36Sopenharmony_ci		ata_pci_bmdma_clear_simplex(pdev);
37362306a36Sopenharmony_ci		fallthrough;
37462306a36Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:
37562306a36Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:
37662306a36Sopenharmony_ci		rc = serverworks_fixup_csb(pdev);
37762306a36Sopenharmony_ci		break;
37862306a36Sopenharmony_ci	case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:
37962306a36Sopenharmony_ci		serverworks_fixup_ht1000(pdev);
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return rc;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	static const struct ata_port_info info[4] = {
38962306a36Sopenharmony_ci		{ /* OSB4 */
39062306a36Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
39162306a36Sopenharmony_ci			.pio_mask = ATA_PIO4,
39262306a36Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
39362306a36Sopenharmony_ci			.udma_mask = ATA_UDMA2,
39462306a36Sopenharmony_ci			.port_ops = &serverworks_osb4_port_ops
39562306a36Sopenharmony_ci		}, { /* OSB4 no UDMA */
39662306a36Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
39762306a36Sopenharmony_ci			.pio_mask = ATA_PIO4,
39862306a36Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
39962306a36Sopenharmony_ci			/* No UDMA */
40062306a36Sopenharmony_ci			.port_ops = &serverworks_osb4_port_ops
40162306a36Sopenharmony_ci		}, { /* CSB5 */
40262306a36Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
40362306a36Sopenharmony_ci			.pio_mask = ATA_PIO4,
40462306a36Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
40562306a36Sopenharmony_ci			.udma_mask = ATA_UDMA4,
40662306a36Sopenharmony_ci			.port_ops = &serverworks_csb_port_ops
40762306a36Sopenharmony_ci		}, { /* CSB5 - later revisions*/
40862306a36Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
40962306a36Sopenharmony_ci			.pio_mask = ATA_PIO4,
41062306a36Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
41162306a36Sopenharmony_ci			.udma_mask = ATA_UDMA5,
41262306a36Sopenharmony_ci			.port_ops = &serverworks_csb_port_ops
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci	};
41562306a36Sopenharmony_ci	const struct ata_port_info *ppi[] = { &info[id->driver_data], NULL };
41662306a36Sopenharmony_ci	const struct scsi_host_template *sht = &serverworks_csb_sht;
41762306a36Sopenharmony_ci	int rc;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	rc = pcim_enable_device(pdev);
42062306a36Sopenharmony_ci	if (rc)
42162306a36Sopenharmony_ci		return rc;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	rc = serverworks_fixup(pdev);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* OSB4 : South Bridge and IDE */
42662306a36Sopenharmony_ci	if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
42762306a36Sopenharmony_ci		/* Select non UDMA capable OSB4 if we can't do fixups */
42862306a36Sopenharmony_ci		if (rc < 0)
42962306a36Sopenharmony_ci			ppi[0] = &info[1];
43062306a36Sopenharmony_ci		sht = &serverworks_osb4_sht;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci	/* setup CSB5/CSB6 : South Bridge and IDE option RAID */
43362306a36Sopenharmony_ci	else if ((pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) ||
43462306a36Sopenharmony_ci		 (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
43562306a36Sopenharmony_ci		 (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) {
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		 /* If the returned btr is the newer revision then
43862306a36Sopenharmony_ci		    select the right info block */
43962306a36Sopenharmony_ci		 if (rc == 3)
44062306a36Sopenharmony_ci		 	ppi[0] = &info[3];
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		/* Is this the 3rd channel CSB6 IDE ? */
44362306a36Sopenharmony_ci		if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)
44462306a36Sopenharmony_ci			ppi[1] = &ata_dummy_port_info;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return ata_pci_bmdma_init_one(pdev, ppi, sht, NULL, 0);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
45162306a36Sopenharmony_cistatic int serverworks_reinit_one(struct pci_dev *pdev)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct ata_host *host = pci_get_drvdata(pdev);
45462306a36Sopenharmony_ci	int rc;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	rc = ata_pci_device_do_resume(pdev);
45762306a36Sopenharmony_ci	if (rc)
45862306a36Sopenharmony_ci		return rc;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	(void)serverworks_fixup(pdev);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	ata_host_resume(host);
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci#endif
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic const struct pci_device_id serverworks[] = {
46862306a36Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0},
46962306a36Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2},
47062306a36Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2},
47162306a36Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2},
47262306a36Sopenharmony_ci	{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2},
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	{ },
47562306a36Sopenharmony_ci};
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic struct pci_driver serverworks_pci_driver = {
47862306a36Sopenharmony_ci	.name 		= DRV_NAME,
47962306a36Sopenharmony_ci	.id_table	= serverworks,
48062306a36Sopenharmony_ci	.probe 		= serverworks_init_one,
48162306a36Sopenharmony_ci	.remove		= ata_pci_remove_one,
48262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
48362306a36Sopenharmony_ci	.suspend	= ata_pci_device_suspend,
48462306a36Sopenharmony_ci	.resume		= serverworks_reinit_one,
48562306a36Sopenharmony_ci#endif
48662306a36Sopenharmony_ci};
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cimodule_pci_driver(serverworks_pci_driver);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ciMODULE_AUTHOR("Alan Cox");
49162306a36Sopenharmony_ciMODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6");
49262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
49362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, serverworks);
49462306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
495