18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * pata_cmd64x.c 	- CMD64x PATA for new ATA layer
48c2ecf20Sopenharmony_ci *			  (C) 2005 Red Hat Inc
58c2ecf20Sopenharmony_ci *			  Alan Cox <alan@lxorguk.ukuu.org.uk>
68c2ecf20Sopenharmony_ci *			  (C) 2009-2010 Bartlomiej Zolnierkiewicz
78c2ecf20Sopenharmony_ci *			  (C) 2012 MontaVista Software, LLC <source@mvista.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based upon
108c2ecf20Sopenharmony_ci * linux/drivers/ide/pci/cmd64x.c		Version 1.30	Sept 10, 2002
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
138c2ecf20Sopenharmony_ci *           Note, this driver is not used at all on other systems because
148c2ecf20Sopenharmony_ci *           there the "BIOS" has done all of the following already.
158c2ecf20Sopenharmony_ci *           Due to massive hardware bugs, UltraDMA is only supported
168c2ecf20Sopenharmony_ci *           on the 646U2 and not on the 646U.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Copyright (C) 1998		Eddie C. Dost  (ecd@skynet.be)
198c2ecf20Sopenharmony_ci * Copyright (C) 1998		David S. Miller (davem@redhat.com)
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * Copyright (C) 1999-2002	Andre Hedrick <andre@linux-ide.org>
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * TODO
248c2ecf20Sopenharmony_ci *	Testing work
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/kernel.h>
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/pci.h>
308c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
318c2ecf20Sopenharmony_ci#include <linux/delay.h>
328c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
338c2ecf20Sopenharmony_ci#include <linux/libata.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define DRV_NAME "pata_cmd64x"
368c2ecf20Sopenharmony_ci#define DRV_VERSION "0.2.18"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * CMD64x specific registers definition.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cienum {
438c2ecf20Sopenharmony_ci	CFR 		= 0x50,
448c2ecf20Sopenharmony_ci		CFR_INTR_CH0  = 0x04,
458c2ecf20Sopenharmony_ci	CNTRL		= 0x51,
468c2ecf20Sopenharmony_ci		CNTRL_CH0     = 0x04,
478c2ecf20Sopenharmony_ci		CNTRL_CH1     = 0x08,
488c2ecf20Sopenharmony_ci	CMDTIM 		= 0x52,
498c2ecf20Sopenharmony_ci	ARTTIM0 	= 0x53,
508c2ecf20Sopenharmony_ci	DRWTIM0 	= 0x54,
518c2ecf20Sopenharmony_ci	ARTTIM1 	= 0x55,
528c2ecf20Sopenharmony_ci	DRWTIM1 	= 0x56,
538c2ecf20Sopenharmony_ci	ARTTIM23 	= 0x57,
548c2ecf20Sopenharmony_ci		ARTTIM23_DIS_RA2  = 0x04,
558c2ecf20Sopenharmony_ci		ARTTIM23_DIS_RA3  = 0x08,
568c2ecf20Sopenharmony_ci		ARTTIM23_INTR_CH1 = 0x10,
578c2ecf20Sopenharmony_ci	DRWTIM2 	= 0x58,
588c2ecf20Sopenharmony_ci	BRST 		= 0x59,
598c2ecf20Sopenharmony_ci	DRWTIM3 	= 0x5b,
608c2ecf20Sopenharmony_ci	BMIDECR0	= 0x70,
618c2ecf20Sopenharmony_ci	MRDMODE		= 0x71,
628c2ecf20Sopenharmony_ci		MRDMODE_INTR_CH0 = 0x04,
638c2ecf20Sopenharmony_ci		MRDMODE_INTR_CH1 = 0x08,
648c2ecf20Sopenharmony_ci	BMIDESR0	= 0x72,
658c2ecf20Sopenharmony_ci	UDIDETCR0	= 0x73,
668c2ecf20Sopenharmony_ci	DTPR0		= 0x74,
678c2ecf20Sopenharmony_ci	BMIDECR1	= 0x78,
688c2ecf20Sopenharmony_ci	BMIDECSR	= 0x79,
698c2ecf20Sopenharmony_ci	UDIDETCR1	= 0x7B,
708c2ecf20Sopenharmony_ci	DTPR1		= 0x7C
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int cmd648_cable_detect(struct ata_port *ap)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
768c2ecf20Sopenharmony_ci	u8 r;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* Check cable detect bits */
798c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, BMIDECSR, &r);
808c2ecf20Sopenharmony_ci	if (r & (1 << ap->port_no))
818c2ecf20Sopenharmony_ci		return ATA_CBL_PATA80;
828c2ecf20Sopenharmony_ci	return ATA_CBL_PATA40;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/**
868c2ecf20Sopenharmony_ci *	cmd64x_set_timing	-	set PIO and MWDMA timing
878c2ecf20Sopenharmony_ci *	@ap: ATA interface
888c2ecf20Sopenharmony_ci *	@adev: ATA device
898c2ecf20Sopenharmony_ci *	@mode: mode
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci *	Called to do the PIO and MWDMA mode setup.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void cmd64x_set_timing(struct ata_port *ap, struct ata_device *adev, u8 mode)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
978c2ecf20Sopenharmony_ci	struct ata_timing t;
988c2ecf20Sopenharmony_ci	const unsigned long T = 1000000 / 33;
998c2ecf20Sopenharmony_ci	const u8 setup_data[] = { 0x40, 0x40, 0x40, 0x80, 0x00 };
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	u8 reg;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/* Port layout is not logical so use a table */
1048c2ecf20Sopenharmony_ci	const u8 arttim_port[2][2] = {
1058c2ecf20Sopenharmony_ci		{ ARTTIM0, ARTTIM1 },
1068c2ecf20Sopenharmony_ci		{ ARTTIM23, ARTTIM23 }
1078c2ecf20Sopenharmony_ci	};
1088c2ecf20Sopenharmony_ci	const u8 drwtim_port[2][2] = {
1098c2ecf20Sopenharmony_ci		{ DRWTIM0, DRWTIM1 },
1108c2ecf20Sopenharmony_ci		{ DRWTIM2, DRWTIM3 }
1118c2ecf20Sopenharmony_ci	};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	int arttim = arttim_port[ap->port_no][adev->devno];
1148c2ecf20Sopenharmony_ci	int drwtim = drwtim_port[ap->port_no][adev->devno];
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* ata_timing_compute is smart and will produce timings for MWDMA
1178c2ecf20Sopenharmony_ci	   that don't violate the drives PIO capabilities. */
1188c2ecf20Sopenharmony_ci	if (ata_timing_compute(adev, mode, &t, T, 0) < 0) {
1198c2ecf20Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": mode computation failed.\n");
1208c2ecf20Sopenharmony_ci		return;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	if (ap->port_no) {
1238c2ecf20Sopenharmony_ci		/* Slave has shared address setup */
1248c2ecf20Sopenharmony_ci		struct ata_device *pair = ata_dev_pair(adev);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		if (pair) {
1278c2ecf20Sopenharmony_ci			struct ata_timing tp;
1288c2ecf20Sopenharmony_ci			ata_timing_compute(pair, pair->pio_mode, &tp, T, 0);
1298c2ecf20Sopenharmony_ci			ata_timing_merge(&t, &tp, &t, ATA_TIMING_SETUP);
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	printk(KERN_DEBUG DRV_NAME ": active %d recovery %d setup %d.\n",
1348c2ecf20Sopenharmony_ci		t.active, t.recover, t.setup);
1358c2ecf20Sopenharmony_ci	if (t.recover > 16) {
1368c2ecf20Sopenharmony_ci		t.active += t.recover - 16;
1378c2ecf20Sopenharmony_ci		t.recover = 16;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci	if (t.active > 16)
1408c2ecf20Sopenharmony_ci		t.active = 16;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* Now convert the clocks into values we can actually stuff into
1438c2ecf20Sopenharmony_ci	   the chip */
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (t.recover == 16)
1468c2ecf20Sopenharmony_ci		t.recover = 0;
1478c2ecf20Sopenharmony_ci	else if (t.recover > 1)
1488c2ecf20Sopenharmony_ci		t.recover--;
1498c2ecf20Sopenharmony_ci	else
1508c2ecf20Sopenharmony_ci		t.recover = 15;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (t.setup > 4)
1538c2ecf20Sopenharmony_ci		t.setup = 0xC0;
1548c2ecf20Sopenharmony_ci	else
1558c2ecf20Sopenharmony_ci		t.setup = setup_data[t.setup];
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	t.active &= 0x0F;	/* 0 = 16 */
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* Load setup timing */
1608c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, arttim, &reg);
1618c2ecf20Sopenharmony_ci	reg &= 0x3F;
1628c2ecf20Sopenharmony_ci	reg |= t.setup;
1638c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, arttim, reg);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Load active/recovery */
1668c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, drwtim, (t.active << 4) | t.recover);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/**
1708c2ecf20Sopenharmony_ci *	cmd64x_set_piomode	-	set initial PIO mode data
1718c2ecf20Sopenharmony_ci *	@ap: ATA interface
1728c2ecf20Sopenharmony_ci *	@adev: ATA device
1738c2ecf20Sopenharmony_ci *
1748c2ecf20Sopenharmony_ci *	Used when configuring the devices ot set the PIO timings. All the
1758c2ecf20Sopenharmony_ci *	actual work is done by the PIO/MWDMA setting helper
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic void cmd64x_set_piomode(struct ata_port *ap, struct ata_device *adev)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	cmd64x_set_timing(ap, adev, adev->pio_mode);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/**
1848c2ecf20Sopenharmony_ci *	cmd64x_set_dmamode	-	set initial DMA mode data
1858c2ecf20Sopenharmony_ci *	@ap: ATA interface
1868c2ecf20Sopenharmony_ci *	@adev: ATA device
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci *	Called to do the DMA mode setup.
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void cmd64x_set_dmamode(struct ata_port *ap, struct ata_device *adev)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	static const u8 udma_data[] = {
1948c2ecf20Sopenharmony_ci		0x30, 0x20, 0x10, 0x20, 0x10, 0x00
1958c2ecf20Sopenharmony_ci	};
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
1988c2ecf20Sopenharmony_ci	u8 regU, regD;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	int pciU = UDIDETCR0 + 8 * ap->port_no;
2018c2ecf20Sopenharmony_ci	int pciD = BMIDESR0 + 8 * ap->port_no;
2028c2ecf20Sopenharmony_ci	int shift = 2 * adev->devno;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, pciD, &regD);
2058c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, pciU, &regU);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* DMA bits off */
2088c2ecf20Sopenharmony_ci	regD &= ~(0x20 << adev->devno);
2098c2ecf20Sopenharmony_ci	/* DMA control bits */
2108c2ecf20Sopenharmony_ci	regU &= ~(0x30 << shift);
2118c2ecf20Sopenharmony_ci	/* DMA timing bits */
2128c2ecf20Sopenharmony_ci	regU &= ~(0x05 << adev->devno);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (adev->dma_mode >= XFER_UDMA_0) {
2158c2ecf20Sopenharmony_ci		/* Merge the timing value */
2168c2ecf20Sopenharmony_ci		regU |= udma_data[adev->dma_mode - XFER_UDMA_0] << shift;
2178c2ecf20Sopenharmony_ci		/* Merge the control bits */
2188c2ecf20Sopenharmony_ci		regU |= 1 << adev->devno; /* UDMA on */
2198c2ecf20Sopenharmony_ci		if (adev->dma_mode > XFER_UDMA_2) /* 15nS timing */
2208c2ecf20Sopenharmony_ci			regU |= 4 << adev->devno;
2218c2ecf20Sopenharmony_ci	} else {
2228c2ecf20Sopenharmony_ci		regU &= ~ (1 << adev->devno);	/* UDMA off */
2238c2ecf20Sopenharmony_ci		cmd64x_set_timing(ap, adev, adev->dma_mode);
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	regD |= 0x20 << adev->devno;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, pciU, regU);
2298c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, pciD, regD);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/**
2338c2ecf20Sopenharmony_ci *	cmd64x_sff_irq_check	-	check IDE interrupt
2348c2ecf20Sopenharmony_ci *	@ap: ATA interface
2358c2ecf20Sopenharmony_ci *
2368c2ecf20Sopenharmony_ci *	Check IDE interrupt in CFR/ARTTIM23 registers.
2378c2ecf20Sopenharmony_ci */
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic bool cmd64x_sff_irq_check(struct ata_port *ap)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
2428c2ecf20Sopenharmony_ci	int irq_mask = ap->port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0;
2438c2ecf20Sopenharmony_ci	int irq_reg  = ap->port_no ? ARTTIM23 : CFR;
2448c2ecf20Sopenharmony_ci	u8 irq_stat;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* NOTE: reading the register should clear the interrupt */
2478c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, irq_reg, &irq_stat);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return irq_stat & irq_mask;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/**
2538c2ecf20Sopenharmony_ci *	cmd64x_sff_irq_clear	-	clear IDE interrupt
2548c2ecf20Sopenharmony_ci *	@ap: ATA interface
2558c2ecf20Sopenharmony_ci *
2568c2ecf20Sopenharmony_ci *	Clear IDE interrupt in CFR/ARTTIM23 and DMA status registers.
2578c2ecf20Sopenharmony_ci */
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void cmd64x_sff_irq_clear(struct ata_port *ap)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
2628c2ecf20Sopenharmony_ci	int irq_reg = ap->port_no ? ARTTIM23 : CFR;
2638c2ecf20Sopenharmony_ci	u8 irq_stat;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	ata_bmdma_irq_clear(ap);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* Reading the register should be enough to clear the interrupt */
2688c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, irq_reg, &irq_stat);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/**
2728c2ecf20Sopenharmony_ci *	cmd648_sff_irq_check	-	check IDE interrupt
2738c2ecf20Sopenharmony_ci *	@ap: ATA interface
2748c2ecf20Sopenharmony_ci *
2758c2ecf20Sopenharmony_ci *	Check IDE interrupt in MRDMODE register.
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic bool cmd648_sff_irq_check(struct ata_port *ap)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
2818c2ecf20Sopenharmony_ci	unsigned long base = pci_resource_start(pdev, 4);
2828c2ecf20Sopenharmony_ci	int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0;
2838c2ecf20Sopenharmony_ci	u8 mrdmode = inb(base + 1);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return mrdmode & irq_mask;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/**
2898c2ecf20Sopenharmony_ci *	cmd648_sff_irq_clear	-	clear IDE interrupt
2908c2ecf20Sopenharmony_ci *	@ap: ATA interface
2918c2ecf20Sopenharmony_ci *
2928c2ecf20Sopenharmony_ci *	Clear IDE interrupt in MRDMODE and DMA status registers.
2938c2ecf20Sopenharmony_ci */
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic void cmd648_sff_irq_clear(struct ata_port *ap)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
2988c2ecf20Sopenharmony_ci	unsigned long base = pci_resource_start(pdev, 4);
2998c2ecf20Sopenharmony_ci	int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0;
3008c2ecf20Sopenharmony_ci	u8 mrdmode;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	ata_bmdma_irq_clear(ap);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* Clear this port's interrupt bit (leaving the other port alone) */
3058c2ecf20Sopenharmony_ci	mrdmode  = inb(base + 1);
3068c2ecf20Sopenharmony_ci	mrdmode &= ~(MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1);
3078c2ecf20Sopenharmony_ci	outb(mrdmode | irq_mask, base + 1);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci/**
3118c2ecf20Sopenharmony_ci *	cmd646r1_bmdma_stop	-	DMA stop callback
3128c2ecf20Sopenharmony_ci *	@qc: Command in progress
3138c2ecf20Sopenharmony_ci *
3148c2ecf20Sopenharmony_ci *	Stub for now while investigating the r1 quirk in the old driver.
3158c2ecf20Sopenharmony_ci */
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void cmd646r1_bmdma_stop(struct ata_queued_cmd *qc)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	ata_bmdma_stop(qc);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic struct scsi_host_template cmd64x_sht = {
3238c2ecf20Sopenharmony_ci	ATA_BMDMA_SHT(DRV_NAME),
3248c2ecf20Sopenharmony_ci};
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic const struct ata_port_operations cmd64x_base_ops = {
3278c2ecf20Sopenharmony_ci	.inherits	= &ata_bmdma_port_ops,
3288c2ecf20Sopenharmony_ci	.set_piomode	= cmd64x_set_piomode,
3298c2ecf20Sopenharmony_ci	.set_dmamode	= cmd64x_set_dmamode,
3308c2ecf20Sopenharmony_ci};
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic struct ata_port_operations cmd64x_port_ops = {
3338c2ecf20Sopenharmony_ci	.inherits	= &cmd64x_base_ops,
3348c2ecf20Sopenharmony_ci	.sff_irq_check	= cmd64x_sff_irq_check,
3358c2ecf20Sopenharmony_ci	.sff_irq_clear	= cmd64x_sff_irq_clear,
3368c2ecf20Sopenharmony_ci	.cable_detect	= ata_cable_40wire,
3378c2ecf20Sopenharmony_ci};
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic struct ata_port_operations cmd646r1_port_ops = {
3408c2ecf20Sopenharmony_ci	.inherits	= &cmd64x_base_ops,
3418c2ecf20Sopenharmony_ci	.sff_irq_check	= cmd64x_sff_irq_check,
3428c2ecf20Sopenharmony_ci	.sff_irq_clear	= cmd64x_sff_irq_clear,
3438c2ecf20Sopenharmony_ci	.bmdma_stop	= cmd646r1_bmdma_stop,
3448c2ecf20Sopenharmony_ci	.cable_detect	= ata_cable_40wire,
3458c2ecf20Sopenharmony_ci};
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic struct ata_port_operations cmd646r3_port_ops = {
3488c2ecf20Sopenharmony_ci	.inherits	= &cmd64x_base_ops,
3498c2ecf20Sopenharmony_ci	.sff_irq_check	= cmd648_sff_irq_check,
3508c2ecf20Sopenharmony_ci	.sff_irq_clear	= cmd648_sff_irq_clear,
3518c2ecf20Sopenharmony_ci	.cable_detect	= ata_cable_40wire,
3528c2ecf20Sopenharmony_ci};
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic struct ata_port_operations cmd648_port_ops = {
3558c2ecf20Sopenharmony_ci	.inherits	= &cmd64x_base_ops,
3568c2ecf20Sopenharmony_ci	.sff_irq_check	= cmd648_sff_irq_check,
3578c2ecf20Sopenharmony_ci	.sff_irq_clear	= cmd648_sff_irq_clear,
3588c2ecf20Sopenharmony_ci	.cable_detect	= cmd648_cable_detect,
3598c2ecf20Sopenharmony_ci};
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void cmd64x_fixup(struct pci_dev *pdev)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	u8 mrdmode;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
3668c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, MRDMODE, &mrdmode);
3678c2ecf20Sopenharmony_ci	mrdmode &= ~0x30;	/* IRQ set up */
3688c2ecf20Sopenharmony_ci	mrdmode |= 0x02;	/* Memory read line enable */
3698c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, MRDMODE, mrdmode);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* PPC specific fixup copied from old driver */
3728c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC
3738c2ecf20Sopenharmony_ci	pci_write_config_byte(pdev, UDIDETCR0, 0xF0);
3748c2ecf20Sopenharmony_ci#endif
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	static const struct ata_port_info cmd_info[7] = {
3808c2ecf20Sopenharmony_ci		{	/* CMD 643 - no UDMA */
3818c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
3828c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
3838c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
3848c2ecf20Sopenharmony_ci			.port_ops = &cmd64x_port_ops
3858c2ecf20Sopenharmony_ci		},
3868c2ecf20Sopenharmony_ci		{	/* CMD 646 with broken UDMA */
3878c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
3888c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
3898c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
3908c2ecf20Sopenharmony_ci			.port_ops = &cmd64x_port_ops
3918c2ecf20Sopenharmony_ci		},
3928c2ecf20Sopenharmony_ci		{	/* CMD 646U with broken UDMA */
3938c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
3948c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
3958c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
3968c2ecf20Sopenharmony_ci			.port_ops = &cmd646r3_port_ops
3978c2ecf20Sopenharmony_ci		},
3988c2ecf20Sopenharmony_ci		{	/* CMD 646U2 with working UDMA */
3998c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
4008c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
4018c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
4028c2ecf20Sopenharmony_ci			.udma_mask = ATA_UDMA2,
4038c2ecf20Sopenharmony_ci			.port_ops = &cmd646r3_port_ops
4048c2ecf20Sopenharmony_ci		},
4058c2ecf20Sopenharmony_ci		{	/* CMD 646 rev 1  */
4068c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
4078c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
4088c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
4098c2ecf20Sopenharmony_ci			.port_ops = &cmd646r1_port_ops
4108c2ecf20Sopenharmony_ci		},
4118c2ecf20Sopenharmony_ci		{	/* CMD 648 */
4128c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
4138c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
4148c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
4158c2ecf20Sopenharmony_ci			.udma_mask = ATA_UDMA4,
4168c2ecf20Sopenharmony_ci			.port_ops = &cmd648_port_ops
4178c2ecf20Sopenharmony_ci		},
4188c2ecf20Sopenharmony_ci		{	/* CMD 649 */
4198c2ecf20Sopenharmony_ci			.flags = ATA_FLAG_SLAVE_POSS,
4208c2ecf20Sopenharmony_ci			.pio_mask = ATA_PIO4,
4218c2ecf20Sopenharmony_ci			.mwdma_mask = ATA_MWDMA2,
4228c2ecf20Sopenharmony_ci			.udma_mask = ATA_UDMA5,
4238c2ecf20Sopenharmony_ci			.port_ops = &cmd648_port_ops
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci	};
4268c2ecf20Sopenharmony_ci	const struct ata_port_info *ppi[] = {
4278c2ecf20Sopenharmony_ci		&cmd_info[id->driver_data],
4288c2ecf20Sopenharmony_ci		&cmd_info[id->driver_data],
4298c2ecf20Sopenharmony_ci		NULL
4308c2ecf20Sopenharmony_ci	};
4318c2ecf20Sopenharmony_ci	u8 reg;
4328c2ecf20Sopenharmony_ci	int rc;
4338c2ecf20Sopenharmony_ci	struct pci_dev *bridge = pdev->bus->self;
4348c2ecf20Sopenharmony_ci	/* mobility split bridges don't report enabled ports correctly */
4358c2ecf20Sopenharmony_ci	int port_ok = !(bridge && bridge->vendor ==
4368c2ecf20Sopenharmony_ci			PCI_VENDOR_ID_MOBILITY_ELECTRONICS);
4378c2ecf20Sopenharmony_ci	/* all (with exceptions below) apart from 643 have CNTRL_CH0 bit */
4388c2ecf20Sopenharmony_ci	int cntrl_ch0_ok = (id->driver_data != 0);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	rc = pcim_enable_device(pdev);
4418c2ecf20Sopenharmony_ci	if (rc)
4428c2ecf20Sopenharmony_ci		return rc;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (id->driver_data == 0)	/* 643 */
4458c2ecf20Sopenharmony_ci		ata_pci_bmdma_clear_simplex(pdev);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (pdev->device == PCI_DEVICE_ID_CMD_646)
4488c2ecf20Sopenharmony_ci		switch (pdev->revision) {
4498c2ecf20Sopenharmony_ci		/* UDMA works since rev 5 */
4508c2ecf20Sopenharmony_ci		default:
4518c2ecf20Sopenharmony_ci			ppi[0] = &cmd_info[3];
4528c2ecf20Sopenharmony_ci			ppi[1] = &cmd_info[3];
4538c2ecf20Sopenharmony_ci			break;
4548c2ecf20Sopenharmony_ci		/* Interrupts in MRDMODE since rev 3 */
4558c2ecf20Sopenharmony_ci		case 3:
4568c2ecf20Sopenharmony_ci		case 4:
4578c2ecf20Sopenharmony_ci			ppi[0] = &cmd_info[2];
4588c2ecf20Sopenharmony_ci			ppi[1] = &cmd_info[2];
4598c2ecf20Sopenharmony_ci			break;
4608c2ecf20Sopenharmony_ci		/* Rev 1 with other problems? */
4618c2ecf20Sopenharmony_ci		case 1:
4628c2ecf20Sopenharmony_ci			ppi[0] = &cmd_info[4];
4638c2ecf20Sopenharmony_ci			ppi[1] = &cmd_info[4];
4648c2ecf20Sopenharmony_ci			fallthrough;
4658c2ecf20Sopenharmony_ci		/* Early revs have no CNTRL_CH0 */
4668c2ecf20Sopenharmony_ci		case 2:
4678c2ecf20Sopenharmony_ci		case 0:
4688c2ecf20Sopenharmony_ci			cntrl_ch0_ok = 0;
4698c2ecf20Sopenharmony_ci			break;
4708c2ecf20Sopenharmony_ci		}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	cmd64x_fixup(pdev);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* check for enabled ports */
4758c2ecf20Sopenharmony_ci	pci_read_config_byte(pdev, CNTRL, &reg);
4768c2ecf20Sopenharmony_ci	if (!port_ok)
4778c2ecf20Sopenharmony_ci		dev_notice(&pdev->dev, "Mobility Bridge detected, ignoring CNTRL port enable/disable\n");
4788c2ecf20Sopenharmony_ci	if (port_ok && cntrl_ch0_ok && !(reg & CNTRL_CH0)) {
4798c2ecf20Sopenharmony_ci		dev_notice(&pdev->dev, "Primary port is disabled\n");
4808c2ecf20Sopenharmony_ci		ppi[0] = &ata_dummy_port_info;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci	if (port_ok && !(reg & CNTRL_CH1)) {
4848c2ecf20Sopenharmony_ci		dev_notice(&pdev->dev, "Secondary port is disabled\n");
4858c2ecf20Sopenharmony_ci		ppi[1] = &ata_dummy_port_info;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return ata_pci_bmdma_init_one(pdev, ppi, &cmd64x_sht, NULL, 0);
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
4928c2ecf20Sopenharmony_cistatic int cmd64x_reinit_one(struct pci_dev *pdev)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct ata_host *host = pci_get_drvdata(pdev);
4958c2ecf20Sopenharmony_ci	int rc;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	rc = ata_pci_device_do_resume(pdev);
4988c2ecf20Sopenharmony_ci	if (rc)
4998c2ecf20Sopenharmony_ci		return rc;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	cmd64x_fixup(pdev);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	ata_host_resume(host);
5048c2ecf20Sopenharmony_ci	return 0;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci#endif
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic const struct pci_device_id cmd64x[] = {
5098c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 },
5108c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 },
5118c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 5 },
5128c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 6 },
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	{ },
5158c2ecf20Sopenharmony_ci};
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic struct pci_driver cmd64x_pci_driver = {
5188c2ecf20Sopenharmony_ci	.name 		= DRV_NAME,
5198c2ecf20Sopenharmony_ci	.id_table	= cmd64x,
5208c2ecf20Sopenharmony_ci	.probe 		= cmd64x_init_one,
5218c2ecf20Sopenharmony_ci	.remove		= ata_pci_remove_one,
5228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
5238c2ecf20Sopenharmony_ci	.suspend	= ata_pci_device_suspend,
5248c2ecf20Sopenharmony_ci	.resume		= cmd64x_reinit_one,
5258c2ecf20Sopenharmony_ci#endif
5268c2ecf20Sopenharmony_ci};
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cimodule_pci_driver(cmd64x_pci_driver);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alan Cox");
5318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("low-level driver for CMD64x series PATA controllers");
5328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cmd64x);
5348c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
535