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