xref: /kernel/linux/linux-5.10/drivers/ide/cs5530.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2000			Andre Hedrick <andre@linux-ide.org>
38c2ecf20Sopenharmony_ci * Copyright (C) 2000			Mark Lord <mlord@pobox.com>
48c2ecf20Sopenharmony_ci * Copyright (C) 2007			Bartlomiej Zolnierkiewicz
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * May be copied or modified under the terms of the GNU General Public License
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Development of this chipset driver was funded
98c2ecf20Sopenharmony_ci * by the nice folks at National Semiconductor.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Documentation:
128c2ecf20Sopenharmony_ci *	CS5530 documentation available from National Semiconductor.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/types.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/ide.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/io.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define DRV_NAME "cs5530"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * Here are the standard PIO mode 0-4 timings for each "format".
288c2ecf20Sopenharmony_ci * Format-0 uses fast data reg timings, with slower command reg timings.
298c2ecf20Sopenharmony_ci * Format-1 uses fast timings for all registers, but won't work with all drives.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic unsigned int cs5530_pio_timings[2][5] = {
328c2ecf20Sopenharmony_ci	{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010},
338c2ecf20Sopenharmony_ci	{0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * After chip reset, the PIO timings are set to 0x0000e132, which is not valid.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci#define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132)
408c2ecf20Sopenharmony_ci#define CS5530_BASEREG(hwif)	(((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20))
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/**
438c2ecf20Sopenharmony_ci *	cs5530_set_pio_mode	-	set host controller for PIO mode
448c2ecf20Sopenharmony_ci *	@hwif: port
458c2ecf20Sopenharmony_ci *	@drive: drive
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci *	Handles setting of PIO mode for the chipset.
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci *	The init_hwif_cs5530() routine guarantees that all drives
508c2ecf20Sopenharmony_ci *	will have valid default PIO timings set up before we get here.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void cs5530_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned long basereg = CS5530_BASEREG(hwif);
568c2ecf20Sopenharmony_ci	unsigned int format = (inl(basereg + 4) >> 31) & 1;
578c2ecf20Sopenharmony_ci	const u8 pio = drive->pio_mode - XFER_PIO_0;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	outl(cs5530_pio_timings[format][pio], basereg + ((drive->dn & 1)<<3));
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/**
638c2ecf20Sopenharmony_ci *	cs5530_udma_filter	-	UDMA filter
648c2ecf20Sopenharmony_ci *	@drive: drive
658c2ecf20Sopenharmony_ci *
668c2ecf20Sopenharmony_ci *	cs5530_udma_filter() does UDMA mask filtering for the given drive
678c2ecf20Sopenharmony_ci *	taking into the consideration capabilities of the mate device.
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci *	The CS5530 specifies that two drives sharing a cable cannot mix
708c2ecf20Sopenharmony_ci *	UDMA/MDMA.  It has to be one or the other, for the pair, though
718c2ecf20Sopenharmony_ci *	different timings can still be chosen for each drive.  We could
728c2ecf20Sopenharmony_ci *	set the appropriate timing bits on the fly, but that might be
738c2ecf20Sopenharmony_ci *	a bit confusing.  So, for now we statically handle this requirement
748c2ecf20Sopenharmony_ci *	by looking at our mate drive to see what it is capable of, before
758c2ecf20Sopenharmony_ci *	choosing a mode for our own drive.
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci *	Note: This relies on the fact we never fail from UDMA to MWDMA2
788c2ecf20Sopenharmony_ci *	but instead drop to PIO.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic u8 cs5530_udma_filter(ide_drive_t *drive)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	ide_hwif_t *hwif = drive->hwif;
848c2ecf20Sopenharmony_ci	ide_drive_t *mate = ide_get_pair_dev(drive);
858c2ecf20Sopenharmony_ci	u16 *mateid;
868c2ecf20Sopenharmony_ci	u8 mask = hwif->ultra_mask;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (mate == NULL)
898c2ecf20Sopenharmony_ci		goto out;
908c2ecf20Sopenharmony_ci	mateid = mate->id;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) {
938c2ecf20Sopenharmony_ci		if ((mateid[ATA_ID_FIELD_VALID] & 4) &&
948c2ecf20Sopenharmony_ci		    (mateid[ATA_ID_UDMA_MODES] & 7))
958c2ecf20Sopenharmony_ci			goto out;
968c2ecf20Sopenharmony_ci		if (mateid[ATA_ID_MWDMA_MODES] & 7)
978c2ecf20Sopenharmony_ci			mask = 0;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ciout:
1008c2ecf20Sopenharmony_ci	return mask;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void cs5530_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	unsigned long basereg;
1068c2ecf20Sopenharmony_ci	unsigned int reg, timings = 0;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	switch (drive->dma_mode) {
1098c2ecf20Sopenharmony_ci		case XFER_UDMA_0:	timings = 0x00921250; break;
1108c2ecf20Sopenharmony_ci		case XFER_UDMA_1:	timings = 0x00911140; break;
1118c2ecf20Sopenharmony_ci		case XFER_UDMA_2:	timings = 0x00911030; break;
1128c2ecf20Sopenharmony_ci		case XFER_MW_DMA_0:	timings = 0x00077771; break;
1138c2ecf20Sopenharmony_ci		case XFER_MW_DMA_1:	timings = 0x00012121; break;
1148c2ecf20Sopenharmony_ci		case XFER_MW_DMA_2:	timings = 0x00002020; break;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci	basereg = CS5530_BASEREG(hwif);
1178c2ecf20Sopenharmony_ci	reg = inl(basereg + 4);			/* get drive0 config register */
1188c2ecf20Sopenharmony_ci	timings |= reg & 0x80000000;		/* preserve PIO format bit */
1198c2ecf20Sopenharmony_ci	if ((drive-> dn & 1) == 0) {		/* are we configuring drive0? */
1208c2ecf20Sopenharmony_ci		outl(timings, basereg + 4);	/* write drive0 config register */
1218c2ecf20Sopenharmony_ci	} else {
1228c2ecf20Sopenharmony_ci		if (timings & 0x00100000)
1238c2ecf20Sopenharmony_ci			reg |=  0x00100000;	/* enable UDMA timings for both drives */
1248c2ecf20Sopenharmony_ci		else
1258c2ecf20Sopenharmony_ci			reg &= ~0x00100000;	/* disable UDMA timings for both drives */
1268c2ecf20Sopenharmony_ci		outl(reg, basereg + 4);		/* write drive0 config register */
1278c2ecf20Sopenharmony_ci		outl(timings, basereg + 12);	/* write drive1 config register */
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/**
1328c2ecf20Sopenharmony_ci *	init_chipset_5530	-	set up 5530 bridge
1338c2ecf20Sopenharmony_ci *	@dev: PCI device
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci *	Initialize the cs5530 bridge for reliable IDE DMA operation.
1368c2ecf20Sopenharmony_ci */
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int init_chipset_cs5530(struct pci_dev *dev)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (pci_resource_start(dev, 4) == 0)
1438c2ecf20Sopenharmony_ci		return -EFAULT;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	dev = NULL;
1468c2ecf20Sopenharmony_ci	while ((dev = pci_get_device(PCI_VENDOR_ID_CYRIX, PCI_ANY_ID, dev)) != NULL) {
1478c2ecf20Sopenharmony_ci		switch (dev->device) {
1488c2ecf20Sopenharmony_ci			case PCI_DEVICE_ID_CYRIX_PCI_MASTER:
1498c2ecf20Sopenharmony_ci				master_0 = pci_dev_get(dev);
1508c2ecf20Sopenharmony_ci				break;
1518c2ecf20Sopenharmony_ci			case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
1528c2ecf20Sopenharmony_ci				cs5530_0 = pci_dev_get(dev);
1538c2ecf20Sopenharmony_ci				break;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci	if (!master_0) {
1578c2ecf20Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": unable to locate PCI MASTER function\n");
1588c2ecf20Sopenharmony_ci		goto out;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	if (!cs5530_0) {
1618c2ecf20Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": unable to locate CS5530 LEGACY function\n");
1628c2ecf20Sopenharmony_ci		goto out;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/*
1668c2ecf20Sopenharmony_ci	 * Enable BusMaster and MemoryWriteAndInvalidate for the cs5530:
1678c2ecf20Sopenharmony_ci	 * -->  OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530
1688c2ecf20Sopenharmony_ci	 */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	pci_set_master(cs5530_0);
1718c2ecf20Sopenharmony_ci	pci_try_set_mwi(cs5530_0);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/*
1748c2ecf20Sopenharmony_ci	 * Set PCI CacheLineSize to 16-bytes:
1758c2ecf20Sopenharmony_ci	 * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530
1768c2ecf20Sopenharmony_ci	 */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/*
1818c2ecf20Sopenharmony_ci	 * Disable trapping of UDMA register accesses (Win98 hack):
1828c2ecf20Sopenharmony_ci	 * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530
1838c2ecf20Sopenharmony_ci	 */
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	pci_write_config_word(cs5530_0, 0xd0, 0x5006);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/*
1888c2ecf20Sopenharmony_ci	 * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus:
1898c2ecf20Sopenharmony_ci	 * The other settings are what is necessary to get the register
1908c2ecf20Sopenharmony_ci	 * into a sane state for IDE DMA operation.
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	pci_write_config_byte(master_0, 0x40, 0x1e);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/*
1968c2ecf20Sopenharmony_ci	 * Set max PCI burst size (16-bytes seems to work best):
1978c2ecf20Sopenharmony_ci	 *	   16bytes: set bit-1 at 0x41 (reg value of 0x16)
1988c2ecf20Sopenharmony_ci	 *	all others: clear bit-1 at 0x41, and do:
1998c2ecf20Sopenharmony_ci	 *	  128bytes: OR 0x00 at 0x41
2008c2ecf20Sopenharmony_ci	 *	  256bytes: OR 0x04 at 0x41
2018c2ecf20Sopenharmony_ci	 *	  512bytes: OR 0x08 at 0x41
2028c2ecf20Sopenharmony_ci	 *	 1024bytes: OR 0x0c at 0x41
2038c2ecf20Sopenharmony_ci	 */
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	pci_write_config_byte(master_0, 0x41, 0x14);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/*
2088c2ecf20Sopenharmony_ci	 * These settings are necessary to get the chip
2098c2ecf20Sopenharmony_ci	 * into a sane state for IDE DMA operation.
2108c2ecf20Sopenharmony_ci	 */
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	pci_write_config_byte(master_0, 0x42, 0x00);
2138c2ecf20Sopenharmony_ci	pci_write_config_byte(master_0, 0x43, 0xc1);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ciout:
2168c2ecf20Sopenharmony_ci	pci_dev_put(master_0);
2178c2ecf20Sopenharmony_ci	pci_dev_put(cs5530_0);
2188c2ecf20Sopenharmony_ci	return 0;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/**
2228c2ecf20Sopenharmony_ci *	init_hwif_cs5530	-	initialise an IDE channel
2238c2ecf20Sopenharmony_ci *	@hwif: IDE to initialize
2248c2ecf20Sopenharmony_ci *
2258c2ecf20Sopenharmony_ci *	This gets invoked by the IDE driver once for each channel. It
2268c2ecf20Sopenharmony_ci *	performs channel-specific pre-initialization before drive probing.
2278c2ecf20Sopenharmony_ci */
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void init_hwif_cs5530 (ide_hwif_t *hwif)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	unsigned long basereg;
2328c2ecf20Sopenharmony_ci	u32 d0_timings;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	basereg = CS5530_BASEREG(hwif);
2358c2ecf20Sopenharmony_ci	d0_timings = inl(basereg + 0);
2368c2ecf20Sopenharmony_ci	if (CS5530_BAD_PIO(d0_timings))
2378c2ecf20Sopenharmony_ci		outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 0);
2388c2ecf20Sopenharmony_ci	if (CS5530_BAD_PIO(inl(basereg + 8)))
2398c2ecf20Sopenharmony_ci		outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 8);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic const struct ide_port_ops cs5530_port_ops = {
2438c2ecf20Sopenharmony_ci	.set_pio_mode		= cs5530_set_pio_mode,
2448c2ecf20Sopenharmony_ci	.set_dma_mode		= cs5530_set_dma_mode,
2458c2ecf20Sopenharmony_ci	.udma_filter		= cs5530_udma_filter,
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic const struct ide_port_info cs5530_chipset = {
2498c2ecf20Sopenharmony_ci	.name		= DRV_NAME,
2508c2ecf20Sopenharmony_ci	.init_chipset	= init_chipset_cs5530,
2518c2ecf20Sopenharmony_ci	.init_hwif	= init_hwif_cs5530,
2528c2ecf20Sopenharmony_ci	.port_ops	= &cs5530_port_ops,
2538c2ecf20Sopenharmony_ci	.host_flags	= IDE_HFLAG_SERIALIZE |
2548c2ecf20Sopenharmony_ci			  IDE_HFLAG_POST_SET_MODE,
2558c2ecf20Sopenharmony_ci	.pio_mask	= ATA_PIO4,
2568c2ecf20Sopenharmony_ci	.mwdma_mask	= ATA_MWDMA2,
2578c2ecf20Sopenharmony_ci	.udma_mask	= ATA_UDMA2,
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	return ide_pci_init_one(dev, &cs5530_chipset, NULL);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic const struct pci_device_id cs5530_pci_tbl[] = {
2668c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), 0 },
2678c2ecf20Sopenharmony_ci	{ 0, },
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cs5530_pci_tbl);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic struct pci_driver cs5530_pci_driver = {
2728c2ecf20Sopenharmony_ci	.name		= "CS5530 IDE",
2738c2ecf20Sopenharmony_ci	.id_table	= cs5530_pci_tbl,
2748c2ecf20Sopenharmony_ci	.probe		= cs5530_init_one,
2758c2ecf20Sopenharmony_ci	.remove		= ide_pci_remove,
2768c2ecf20Sopenharmony_ci	.suspend	= ide_pci_suspend,
2778c2ecf20Sopenharmony_ci	.resume		= ide_pci_resume,
2788c2ecf20Sopenharmony_ci};
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int __init cs5530_ide_init(void)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	return ide_pci_register_driver(&cs5530_pci_driver);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void __exit cs5530_ide_exit(void)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	pci_unregister_driver(&cs5530_pci_driver);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cimodule_init(cs5530_ide_init);
2918c2ecf20Sopenharmony_cimodule_exit(cs5530_ide_exit);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Lord");
2948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PCI driver module for Cyrix/NS 5530 IDE");
2958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
296