13957df61SMartin K. Petersen /* 23957df61SMartin K. Petersen * pata_cs5536.c - CS5536 PATA for new ATA layer 33957df61SMartin K. Petersen * (C) 2007 Martin K. Petersen <mkp@mkp.net> 43957df61SMartin K. Petersen * 53957df61SMartin K. Petersen * This program is free software; you can redistribute it and/or modify 63957df61SMartin K. Petersen * it under the terms of the GNU General Public License version 2 as 73957df61SMartin K. Petersen * published by the Free Software Foundation. 83957df61SMartin K. Petersen * 93957df61SMartin K. Petersen * This program is distributed in the hope that it will be useful, 103957df61SMartin K. Petersen * but WITHOUT ANY WARRANTY; without even the implied warranty of 113957df61SMartin K. Petersen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 123957df61SMartin K. Petersen * GNU General Public License for more details. 133957df61SMartin K. Petersen * 143957df61SMartin K. Petersen * You should have received a copy of the GNU General Public License 153957df61SMartin K. Petersen * along with this program; if not, write to the Free Software 163957df61SMartin K. Petersen * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 173957df61SMartin K. Petersen * 183957df61SMartin K. Petersen * Documentation: 193957df61SMartin K. Petersen * Available from AMD web site. 203957df61SMartin K. Petersen * 213957df61SMartin K. Petersen * The IDE timing registers for the CS5536 live in the Geode Machine 223957df61SMartin K. Petersen * Specific Register file and not PCI config space. Most BIOSes 233957df61SMartin K. Petersen * virtualize the PCI registers so the chip looks like a standard IDE 243957df61SMartin K. Petersen * controller. Unfortunately not all implementations get this right. 253957df61SMartin K. Petersen * In particular some have problems with unaligned accesses to the 263957df61SMartin K. Petersen * virtualized PCI registers. This driver always does full dword 273957df61SMartin K. Petersen * writes to work around the issue. Also, in case of a bad BIOS this 283957df61SMartin K. Petersen * driver can be loaded with the "msr=1" parameter which forces using 293957df61SMartin K. Petersen * the Machine Specific Registers to configure the device. 303957df61SMartin K. Petersen */ 313957df61SMartin K. Petersen 323957df61SMartin K. Petersen #include <linux/kernel.h> 333957df61SMartin K. Petersen #include <linux/module.h> 343957df61SMartin K. Petersen #include <linux/pci.h> 353957df61SMartin K. Petersen #include <linux/init.h> 363957df61SMartin K. Petersen #include <linux/blkdev.h> 373957df61SMartin K. Petersen #include <linux/delay.h> 383957df61SMartin K. Petersen #include <linux/libata.h> 393957df61SMartin K. Petersen #include <scsi/scsi_host.h> 403957df61SMartin K. Petersen #include <asm/msr.h> 413957df61SMartin K. Petersen 423957df61SMartin K. Petersen #define DRV_NAME "pata_cs5536" 43b6966a61SMartin K. Petersen #define DRV_VERSION "0.0.7" 443957df61SMartin K. Petersen 453957df61SMartin K. Petersen enum { 463957df61SMartin K. Petersen CFG = 0, 473957df61SMartin K. Petersen DTC = 1, 483957df61SMartin K. Petersen CAST = 2, 493957df61SMartin K. Petersen ETC = 3, 503957df61SMartin K. Petersen 513957df61SMartin K. Petersen MSR_IDE_BASE = 0x51300000, 523957df61SMartin K. Petersen MSR_IDE_CFG = (MSR_IDE_BASE + 0x10), 533957df61SMartin K. Petersen MSR_IDE_DTC = (MSR_IDE_BASE + 0x12), 543957df61SMartin K. Petersen MSR_IDE_CAST = (MSR_IDE_BASE + 0x13), 553957df61SMartin K. Petersen MSR_IDE_ETC = (MSR_IDE_BASE + 0x14), 563957df61SMartin K. Petersen 573957df61SMartin K. Petersen PCI_IDE_CFG = 0x40, 583957df61SMartin K. Petersen PCI_IDE_DTC = 0x48, 593957df61SMartin K. Petersen PCI_IDE_CAST = 0x4c, 603957df61SMartin K. Petersen PCI_IDE_ETC = 0x50, 613957df61SMartin K. Petersen 623957df61SMartin K. Petersen IDE_CFG_CHANEN = 0x2, 633957df61SMartin K. Petersen IDE_CFG_CABLE = 0x10000, 643957df61SMartin K. Petersen 653957df61SMartin K. Petersen IDE_D0_SHIFT = 24, 663957df61SMartin K. Petersen IDE_D1_SHIFT = 16, 673957df61SMartin K. Petersen IDE_DRV_MASK = 0xff, 683957df61SMartin K. Petersen 693957df61SMartin K. Petersen IDE_CAST_D0_SHIFT = 6, 703957df61SMartin K. Petersen IDE_CAST_D1_SHIFT = 4, 713957df61SMartin K. Petersen IDE_CAST_DRV_MASK = 0x3, 723957df61SMartin K. Petersen IDE_CAST_CMD_MASK = 0xff, 733957df61SMartin K. Petersen IDE_CAST_CMD_SHIFT = 24, 743957df61SMartin K. Petersen 753957df61SMartin K. Petersen IDE_ETC_NODMA = 0x03, 763957df61SMartin K. Petersen }; 773957df61SMartin K. Petersen 783957df61SMartin K. Petersen static int use_msr; 793957df61SMartin K. Petersen 803957df61SMartin K. Petersen static const u32 msr_reg[4] = { 813957df61SMartin K. Petersen MSR_IDE_CFG, MSR_IDE_DTC, MSR_IDE_CAST, MSR_IDE_ETC, 823957df61SMartin K. Petersen }; 833957df61SMartin K. Petersen 843957df61SMartin K. Petersen static const u8 pci_reg[4] = { 853957df61SMartin K. Petersen PCI_IDE_CFG, PCI_IDE_DTC, PCI_IDE_CAST, PCI_IDE_ETC, 863957df61SMartin K. Petersen }; 873957df61SMartin K. Petersen 882072fb55SHarvey Harrison static inline int cs5536_read(struct pci_dev *pdev, int reg, u32 *val) 893957df61SMartin K. Petersen { 903957df61SMartin K. Petersen if (unlikely(use_msr)) { 913957df61SMartin K. Petersen u32 dummy; 923957df61SMartin K. Petersen 933957df61SMartin K. Petersen rdmsr(msr_reg[reg], *val, dummy); 943957df61SMartin K. Petersen return 0; 953957df61SMartin K. Petersen } 963957df61SMartin K. Petersen 973957df61SMartin K. Petersen return pci_read_config_dword(pdev, pci_reg[reg], val); 983957df61SMartin K. Petersen } 993957df61SMartin K. Petersen 1003957df61SMartin K. Petersen static inline int cs5536_write(struct pci_dev *pdev, int reg, int val) 1013957df61SMartin K. Petersen { 1023957df61SMartin K. Petersen if (unlikely(use_msr)) { 1033957df61SMartin K. Petersen wrmsr(msr_reg[reg], val, 0); 1043957df61SMartin K. Petersen return 0; 1053957df61SMartin K. Petersen } 1063957df61SMartin K. Petersen 1073957df61SMartin K. Petersen return pci_write_config_dword(pdev, pci_reg[reg], val); 1083957df61SMartin K. Petersen } 1093957df61SMartin K. Petersen 1103957df61SMartin K. Petersen /** 1113957df61SMartin K. Petersen * cs5536_cable_detect - detect cable type 1123957df61SMartin K. Petersen * @ap: Port to detect on 1133957df61SMartin K. Petersen * 1143957df61SMartin K. Petersen * Perform cable detection for ATA66 capable cable. Return a libata 1153957df61SMartin K. Petersen * cable type. 1163957df61SMartin K. Petersen */ 1173957df61SMartin K. Petersen 1183957df61SMartin K. Petersen static int cs5536_cable_detect(struct ata_port *ap) 1193957df61SMartin K. Petersen { 1203957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1213957df61SMartin K. Petersen u32 cfg; 1223957df61SMartin K. Petersen 1233957df61SMartin K. Petersen cs5536_read(pdev, CFG, &cfg); 1243957df61SMartin K. Petersen 1253957df61SMartin K. Petersen if (cfg & (IDE_CFG_CABLE << ap->port_no)) 1263957df61SMartin K. Petersen return ATA_CBL_PATA80; 1273957df61SMartin K. Petersen else 1283957df61SMartin K. Petersen return ATA_CBL_PATA40; 1293957df61SMartin K. Petersen } 1303957df61SMartin K. Petersen 1313957df61SMartin K. Petersen /** 1323957df61SMartin K. Petersen * cs5536_set_piomode - PIO setup 1333957df61SMartin K. Petersen * @ap: ATA interface 1343957df61SMartin K. Petersen * @adev: device on the interface 1353957df61SMartin K. Petersen */ 1363957df61SMartin K. Petersen 1373957df61SMartin K. Petersen static void cs5536_set_piomode(struct ata_port *ap, struct ata_device *adev) 1383957df61SMartin K. Petersen { 1393957df61SMartin K. Petersen static const u8 drv_timings[5] = { 1403957df61SMartin K. Petersen 0x98, 0x55, 0x32, 0x21, 0x20, 1413957df61SMartin K. Petersen }; 1423957df61SMartin K. Petersen 1433957df61SMartin K. Petersen static const u8 addr_timings[5] = { 1443957df61SMartin K. Petersen 0x2, 0x1, 0x0, 0x0, 0x0, 1453957df61SMartin K. Petersen }; 1463957df61SMartin K. Petersen 1473957df61SMartin K. Petersen static const u8 cmd_timings[5] = { 1483957df61SMartin K. Petersen 0x99, 0x92, 0x90, 0x22, 0x20, 1493957df61SMartin K. Petersen }; 1503957df61SMartin K. Petersen 1513957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1523957df61SMartin K. Petersen struct ata_device *pair = ata_dev_pair(adev); 1533957df61SMartin K. Petersen int mode = adev->pio_mode - XFER_PIO_0; 1543957df61SMartin K. Petersen int cmdmode = mode; 155b6966a61SMartin K. Petersen int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; 156b6966a61SMartin K. Petersen int cshift = adev->devno ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT; 1573957df61SMartin K. Petersen u32 dtc, cast, etc; 1583957df61SMartin K. Petersen 1593957df61SMartin K. Petersen if (pair) 1603957df61SMartin K. Petersen cmdmode = min(mode, pair->pio_mode - XFER_PIO_0); 1613957df61SMartin K. Petersen 1623957df61SMartin K. Petersen cs5536_read(pdev, DTC, &dtc); 1633957df61SMartin K. Petersen cs5536_read(pdev, CAST, &cast); 1643957df61SMartin K. Petersen cs5536_read(pdev, ETC, &etc); 1653957df61SMartin K. Petersen 1663957df61SMartin K. Petersen dtc &= ~(IDE_DRV_MASK << dshift); 1673957df61SMartin K. Petersen dtc |= drv_timings[mode] << dshift; 1683957df61SMartin K. Petersen 1693957df61SMartin K. Petersen cast &= ~(IDE_CAST_DRV_MASK << cshift); 1703957df61SMartin K. Petersen cast |= addr_timings[mode] << cshift; 1713957df61SMartin K. Petersen 1723957df61SMartin K. Petersen cast &= ~(IDE_CAST_CMD_MASK << IDE_CAST_CMD_SHIFT); 1733957df61SMartin K. Petersen cast |= cmd_timings[cmdmode] << IDE_CAST_CMD_SHIFT; 1743957df61SMartin K. Petersen 1753957df61SMartin K. Petersen etc &= ~(IDE_DRV_MASK << dshift); 1763957df61SMartin K. Petersen etc |= IDE_ETC_NODMA << dshift; 1773957df61SMartin K. Petersen 1783957df61SMartin K. Petersen cs5536_write(pdev, DTC, dtc); 1793957df61SMartin K. Petersen cs5536_write(pdev, CAST, cast); 1803957df61SMartin K. Petersen cs5536_write(pdev, ETC, etc); 1813957df61SMartin K. Petersen } 1823957df61SMartin K. Petersen 1833957df61SMartin K. Petersen /** 1843957df61SMartin K. Petersen * cs5536_set_dmamode - DMA timing setup 1853957df61SMartin K. Petersen * @ap: ATA interface 1863957df61SMartin K. Petersen * @adev: Device being configured 1873957df61SMartin K. Petersen * 1883957df61SMartin K. Petersen */ 1893957df61SMartin K. Petersen 1903957df61SMartin K. Petersen static void cs5536_set_dmamode(struct ata_port *ap, struct ata_device *adev) 1913957df61SMartin K. Petersen { 1923957df61SMartin K. Petersen static const u8 udma_timings[6] = { 1933957df61SMartin K. Petersen 0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6, 1943957df61SMartin K. Petersen }; 1953957df61SMartin K. Petersen 1963957df61SMartin K. Petersen static const u8 mwdma_timings[3] = { 1973957df61SMartin K. Petersen 0x67, 0x21, 0x20, 1983957df61SMartin K. Petersen }; 1993957df61SMartin K. Petersen 2003957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 2013957df61SMartin K. Petersen u32 dtc, etc; 2023957df61SMartin K. Petersen int mode = adev->dma_mode; 203b6966a61SMartin K. Petersen int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; 2043957df61SMartin K. Petersen 2053957df61SMartin K. Petersen if (mode >= XFER_UDMA_0) { 2063957df61SMartin K. Petersen cs5536_read(pdev, ETC, &etc); 2073957df61SMartin K. Petersen 2083957df61SMartin K. Petersen etc &= ~(IDE_DRV_MASK << dshift); 2093957df61SMartin K. Petersen etc |= udma_timings[mode - XFER_UDMA_0] << dshift; 2103957df61SMartin K. Petersen 2113957df61SMartin K. Petersen cs5536_write(pdev, ETC, etc); 2123957df61SMartin K. Petersen } else { /* MWDMA */ 2133957df61SMartin K. Petersen cs5536_read(pdev, DTC, &dtc); 2143957df61SMartin K. Petersen 2153957df61SMartin K. Petersen dtc &= ~(IDE_DRV_MASK << dshift); 21680f6fd38SBartlomiej Zolnierkiewicz dtc |= mwdma_timings[mode - XFER_MW_DMA_0] << dshift; 2173957df61SMartin K. Petersen 2183957df61SMartin K. Petersen cs5536_write(pdev, DTC, dtc); 2193957df61SMartin K. Petersen } 2203957df61SMartin K. Petersen } 2213957df61SMartin K. Petersen 2223957df61SMartin K. Petersen static struct scsi_host_template cs5536_sht = { 22368d1d07bSTejun Heo ATA_BMDMA_SHT(DRV_NAME), 2243957df61SMartin K. Petersen }; 2253957df61SMartin K. Petersen 2263957df61SMartin K. Petersen static struct ata_port_operations cs5536_port_ops = { 227ba3a221cSKrzysztof Halasa .inherits = &ata_bmdma32_port_ops, 228029cfd6bSTejun Heo .cable_detect = cs5536_cable_detect, 2293957df61SMartin K. Petersen .set_piomode = cs5536_set_piomode, 2303957df61SMartin K. Petersen .set_dmamode = cs5536_set_dmamode, 2313957df61SMartin K. Petersen }; 2323957df61SMartin K. Petersen 2333957df61SMartin K. Petersen /** 2343957df61SMartin K. Petersen * cs5536_init_one 2353957df61SMartin K. Petersen * @dev: PCI device 2363957df61SMartin K. Petersen * @id: Entry in match table 2373957df61SMartin K. Petersen * 2383957df61SMartin K. Petersen */ 2393957df61SMartin K. Petersen 2403957df61SMartin K. Petersen static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id) 2413957df61SMartin K. Petersen { 2423957df61SMartin K. Petersen static const struct ata_port_info info = { 2433957df61SMartin K. Petersen .flags = ATA_FLAG_SLAVE_POSS, 24414bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 24514bdef98SErik Inge Bolsø .mwdma_mask = ATA_MWDMA2, 2463957df61SMartin K. Petersen .udma_mask = ATA_UDMA5, 2473957df61SMartin K. Petersen .port_ops = &cs5536_port_ops, 2483957df61SMartin K. Petersen }; 2493957df61SMartin K. Petersen 2503957df61SMartin K. Petersen const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info }; 2513957df61SMartin K. Petersen u32 cfg; 2523957df61SMartin K. Petersen 2533957df61SMartin K. Petersen if (use_msr) 2543957df61SMartin K. Petersen printk(KERN_ERR DRV_NAME ": Using MSR regs instead of PCI\n"); 2553957df61SMartin K. Petersen 2563957df61SMartin K. Petersen cs5536_read(dev, CFG, &cfg); 2573957df61SMartin K. Petersen 2583957df61SMartin K. Petersen if ((cfg & IDE_CFG_CHANEN) == 0) { 2593957df61SMartin K. Petersen printk(KERN_ERR DRV_NAME ": disabled by BIOS\n"); 2603957df61SMartin K. Petersen return -ENODEV; 2613957df61SMartin K. Petersen } 2623957df61SMartin K. Petersen 2631c5afdf7STejun Heo return ata_pci_bmdma_init_one(dev, ppi, &cs5536_sht, NULL, 0); 2643957df61SMartin K. Petersen } 2653957df61SMartin K. Petersen 2663957df61SMartin K. Petersen static const struct pci_device_id cs5536[] = { 2673957df61SMartin K. Petersen { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), }, 2683957df61SMartin K. Petersen { }, 2693957df61SMartin K. Petersen }; 2703957df61SMartin K. Petersen 2713957df61SMartin K. Petersen static struct pci_driver cs5536_pci_driver = { 2723957df61SMartin K. Petersen .name = DRV_NAME, 2733957df61SMartin K. Petersen .id_table = cs5536, 2743957df61SMartin K. Petersen .probe = cs5536_init_one, 2753957df61SMartin K. Petersen .remove = ata_pci_remove_one, 2763957df61SMartin K. Petersen #ifdef CONFIG_PM 2773957df61SMartin K. Petersen .suspend = ata_pci_device_suspend, 2783957df61SMartin K. Petersen .resume = ata_pci_device_resume, 2793957df61SMartin K. Petersen #endif 2803957df61SMartin K. Petersen }; 2813957df61SMartin K. Petersen 2823957df61SMartin K. Petersen static int __init cs5536_init(void) 2833957df61SMartin K. Petersen { 2843957df61SMartin K. Petersen return pci_register_driver(&cs5536_pci_driver); 2853957df61SMartin K. Petersen } 2863957df61SMartin K. Petersen 2873957df61SMartin K. Petersen static void __exit cs5536_exit(void) 2883957df61SMartin K. Petersen { 2893957df61SMartin K. Petersen pci_unregister_driver(&cs5536_pci_driver); 2903957df61SMartin K. Petersen } 2913957df61SMartin K. Petersen 2923957df61SMartin K. Petersen MODULE_AUTHOR("Martin K. Petersen"); 2933957df61SMartin K. Petersen MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller"); 2943957df61SMartin K. Petersen MODULE_LICENSE("GPL"); 2953957df61SMartin K. Petersen MODULE_DEVICE_TABLE(pci, cs5536); 2963957df61SMartin K. Petersen MODULE_VERSION(DRV_VERSION); 2973957df61SMartin K. Petersen module_param_named(msr, use_msr, int, 0644); 2983957df61SMartin K. Petersen MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)"); 2993957df61SMartin K. Petersen 3003957df61SMartin K. Petersen module_init(cs5536_init); 3013957df61SMartin K. Petersen module_exit(cs5536_exit); 302