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> 4d63e94a4SBartlomiej Zolnierkiewicz * (C) 2011 Bartlomiej Zolnierkiewicz 53957df61SMartin K. Petersen * 63957df61SMartin K. Petersen * This program is free software; you can redistribute it and/or modify 73957df61SMartin K. Petersen * it under the terms of the GNU General Public License version 2 as 83957df61SMartin K. Petersen * published by the Free Software Foundation. 93957df61SMartin K. Petersen * 103957df61SMartin K. Petersen * This program is distributed in the hope that it will be useful, 113957df61SMartin K. Petersen * but WITHOUT ANY WARRANTY; without even the implied warranty of 123957df61SMartin K. Petersen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 133957df61SMartin K. Petersen * GNU General Public License for more details. 143957df61SMartin K. Petersen * 153957df61SMartin K. Petersen * You should have received a copy of the GNU General Public License 163957df61SMartin K. Petersen * along with this program; if not, write to the Free Software 173957df61SMartin K. Petersen * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 183957df61SMartin K. Petersen * 193957df61SMartin K. Petersen * Documentation: 203957df61SMartin K. Petersen * Available from AMD web site. 213957df61SMartin K. Petersen * 223957df61SMartin K. Petersen * The IDE timing registers for the CS5536 live in the Geode Machine 233957df61SMartin K. Petersen * Specific Register file and not PCI config space. Most BIOSes 243957df61SMartin K. Petersen * virtualize the PCI registers so the chip looks like a standard IDE 253957df61SMartin K. Petersen * controller. Unfortunately not all implementations get this right. 263957df61SMartin K. Petersen * In particular some have problems with unaligned accesses to the 273957df61SMartin K. Petersen * virtualized PCI registers. This driver always does full dword 283957df61SMartin K. Petersen * writes to work around the issue. Also, in case of a bad BIOS this 293957df61SMartin K. Petersen * driver can be loaded with the "msr=1" parameter which forces using 303957df61SMartin K. Petersen * the Machine Specific Registers to configure the device. 313957df61SMartin K. Petersen */ 323957df61SMartin K. Petersen 333957df61SMartin K. Petersen #include <linux/kernel.h> 343957df61SMartin K. Petersen #include <linux/module.h> 353957df61SMartin K. Petersen #include <linux/pci.h> 363957df61SMartin K. Petersen #include <linux/init.h> 373957df61SMartin K. Petersen #include <linux/blkdev.h> 383957df61SMartin K. Petersen #include <linux/delay.h> 393957df61SMartin K. Petersen #include <linux/libata.h> 403957df61SMartin K. Petersen #include <scsi/scsi_host.h> 419272dcc2SWu Zhangjin 429272dcc2SWu Zhangjin #ifdef CONFIG_X86_32 433957df61SMartin K. Petersen #include <asm/msr.h> 449272dcc2SWu Zhangjin static int use_msr; 459272dcc2SWu Zhangjin module_param_named(msr, use_msr, int, 0644); 469272dcc2SWu Zhangjin MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)"); 479272dcc2SWu Zhangjin #else 48ff5dd32bSJeff Garzik #undef rdmsr /* avoid accidental MSR usage on, e.g. x86-64 */ 49ff5dd32bSJeff Garzik #undef wrmsr 509272dcc2SWu Zhangjin #define rdmsr(x, y, z) do { } while (0) 519272dcc2SWu Zhangjin #define wrmsr(x, y, z) do { } while (0) 529272dcc2SWu Zhangjin #define use_msr 0 539272dcc2SWu Zhangjin #endif 543957df61SMartin K. Petersen 553957df61SMartin K. Petersen #define DRV_NAME "pata_cs5536" 569272dcc2SWu Zhangjin #define DRV_VERSION "0.0.8" 573957df61SMartin K. Petersen 583957df61SMartin K. Petersen enum { 59d63e94a4SBartlomiej Zolnierkiewicz MSR_IDE_CFG = 0x51300010, 603957df61SMartin K. Petersen PCI_IDE_CFG = 0x40, 613957df61SMartin K. Petersen 62d63e94a4SBartlomiej Zolnierkiewicz CFG = 0, 63d63e94a4SBartlomiej Zolnierkiewicz DTC = 2, 64d63e94a4SBartlomiej Zolnierkiewicz CAST = 3, 65d63e94a4SBartlomiej Zolnierkiewicz ETC = 4, 66d63e94a4SBartlomiej Zolnierkiewicz 67d63e94a4SBartlomiej Zolnierkiewicz IDE_CFG_CHANEN = (1 << 1), 68d63e94a4SBartlomiej Zolnierkiewicz IDE_CFG_CABLE = (1 << 17) | (1 << 16), 693957df61SMartin K. Petersen 703957df61SMartin K. Petersen IDE_D0_SHIFT = 24, 713957df61SMartin K. Petersen IDE_D1_SHIFT = 16, 723957df61SMartin K. Petersen IDE_DRV_MASK = 0xff, 733957df61SMartin K. Petersen 743957df61SMartin K. Petersen IDE_CAST_D0_SHIFT = 6, 753957df61SMartin K. Petersen IDE_CAST_D1_SHIFT = 4, 763957df61SMartin K. Petersen IDE_CAST_DRV_MASK = 0x3, 773957df61SMartin K. Petersen IDE_CAST_CMD_MASK = 0xff, 783957df61SMartin K. Petersen IDE_CAST_CMD_SHIFT = 24, 793957df61SMartin K. Petersen 80d63e94a4SBartlomiej Zolnierkiewicz IDE_ETC_UDMA_MASK = 0xc0, 813957df61SMartin K. Petersen }; 823957df61SMartin K. Petersen 83d63e94a4SBartlomiej Zolnierkiewicz static int cs5536_read(struct pci_dev *pdev, int reg, u32 *val) 843957df61SMartin K. Petersen { 853957df61SMartin K. Petersen if (unlikely(use_msr)) { 869272dcc2SWu Zhangjin u32 dummy __maybe_unused; 873957df61SMartin K. Petersen 88d63e94a4SBartlomiej Zolnierkiewicz rdmsr(MSR_IDE_CFG + reg, *val, dummy); 893957df61SMartin K. Petersen return 0; 903957df61SMartin K. Petersen } 913957df61SMartin K. Petersen 92d63e94a4SBartlomiej Zolnierkiewicz return pci_read_config_dword(pdev, PCI_IDE_CFG + reg * 4, val); 933957df61SMartin K. Petersen } 943957df61SMartin K. Petersen 95d63e94a4SBartlomiej Zolnierkiewicz static int cs5536_write(struct pci_dev *pdev, int reg, int val) 963957df61SMartin K. Petersen { 973957df61SMartin K. Petersen if (unlikely(use_msr)) { 98d63e94a4SBartlomiej Zolnierkiewicz wrmsr(MSR_IDE_CFG + reg, val, 0); 993957df61SMartin K. Petersen return 0; 1003957df61SMartin K. Petersen } 1013957df61SMartin K. Petersen 102d63e94a4SBartlomiej Zolnierkiewicz return pci_write_config_dword(pdev, PCI_IDE_CFG + reg * 4, val); 103d63e94a4SBartlomiej Zolnierkiewicz } 104d63e94a4SBartlomiej Zolnierkiewicz 105d63e94a4SBartlomiej Zolnierkiewicz static void cs5536_program_dtc(struct ata_device *adev, u8 tim) 106d63e94a4SBartlomiej Zolnierkiewicz { 107d63e94a4SBartlomiej Zolnierkiewicz struct pci_dev *pdev = to_pci_dev(adev->link->ap->host->dev); 108d63e94a4SBartlomiej Zolnierkiewicz int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; 109d63e94a4SBartlomiej Zolnierkiewicz u32 dtc; 110d63e94a4SBartlomiej Zolnierkiewicz 111d63e94a4SBartlomiej Zolnierkiewicz cs5536_read(pdev, DTC, &dtc); 112d63e94a4SBartlomiej Zolnierkiewicz dtc &= ~(IDE_DRV_MASK << dshift); 113d63e94a4SBartlomiej Zolnierkiewicz dtc |= tim << dshift; 114d63e94a4SBartlomiej Zolnierkiewicz cs5536_write(pdev, DTC, dtc); 1153957df61SMartin K. Petersen } 1163957df61SMartin K. Petersen 1173957df61SMartin K. Petersen /** 1183957df61SMartin K. Petersen * cs5536_cable_detect - detect cable type 1193957df61SMartin K. Petersen * @ap: Port to detect on 1203957df61SMartin K. Petersen * 121d63e94a4SBartlomiej Zolnierkiewicz * Perform cable detection for ATA66 capable cable. 122d63e94a4SBartlomiej Zolnierkiewicz * 123d63e94a4SBartlomiej Zolnierkiewicz * Returns a cable type. 1243957df61SMartin K. Petersen */ 1253957df61SMartin K. Petersen 1263957df61SMartin K. Petersen static int cs5536_cable_detect(struct ata_port *ap) 1273957df61SMartin K. Petersen { 1283957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1293957df61SMartin K. Petersen u32 cfg; 1303957df61SMartin K. Petersen 1313957df61SMartin K. Petersen cs5536_read(pdev, CFG, &cfg); 1323957df61SMartin K. Petersen 133d63e94a4SBartlomiej Zolnierkiewicz if (cfg & IDE_CFG_CABLE) 1343957df61SMartin K. Petersen return ATA_CBL_PATA80; 1353957df61SMartin K. Petersen else 1363957df61SMartin K. Petersen return ATA_CBL_PATA40; 1373957df61SMartin K. Petersen } 1383957df61SMartin K. Petersen 1393957df61SMartin K. Petersen /** 1403957df61SMartin K. Petersen * cs5536_set_piomode - PIO setup 1413957df61SMartin K. Petersen * @ap: ATA interface 1423957df61SMartin K. Petersen * @adev: device on the interface 1433957df61SMartin K. Petersen */ 1443957df61SMartin K. Petersen 1453957df61SMartin K. Petersen static void cs5536_set_piomode(struct ata_port *ap, struct ata_device *adev) 1463957df61SMartin K. Petersen { 1473957df61SMartin K. Petersen static const u8 drv_timings[5] = { 1483957df61SMartin K. Petersen 0x98, 0x55, 0x32, 0x21, 0x20, 1493957df61SMartin K. Petersen }; 1503957df61SMartin K. Petersen 1513957df61SMartin K. Petersen static const u8 addr_timings[5] = { 1523957df61SMartin K. Petersen 0x2, 0x1, 0x0, 0x0, 0x0, 1533957df61SMartin K. Petersen }; 1543957df61SMartin K. Petersen 1553957df61SMartin K. Petersen static const u8 cmd_timings[5] = { 1563957df61SMartin K. Petersen 0x99, 0x92, 0x90, 0x22, 0x20, 1573957df61SMartin K. Petersen }; 1583957df61SMartin K. Petersen 1593957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1603957df61SMartin K. Petersen struct ata_device *pair = ata_dev_pair(adev); 1613957df61SMartin K. Petersen int mode = adev->pio_mode - XFER_PIO_0; 1623957df61SMartin K. Petersen int cmdmode = mode; 163b6966a61SMartin K. Petersen int cshift = adev->devno ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT; 164d63e94a4SBartlomiej Zolnierkiewicz u32 cast; 1653957df61SMartin K. Petersen 1663957df61SMartin K. Petersen if (pair) 1673957df61SMartin K. Petersen cmdmode = min(mode, pair->pio_mode - XFER_PIO_0); 1683957df61SMartin K. Petersen 169d63e94a4SBartlomiej Zolnierkiewicz cs5536_program_dtc(adev, drv_timings[mode]); 1703957df61SMartin K. Petersen 171d63e94a4SBartlomiej Zolnierkiewicz cs5536_read(pdev, CAST, &cast); 1723957df61SMartin K. Petersen 1733957df61SMartin K. Petersen cast &= ~(IDE_CAST_DRV_MASK << cshift); 1743957df61SMartin K. Petersen cast |= addr_timings[mode] << cshift; 1753957df61SMartin K. Petersen 1763957df61SMartin K. Petersen cast &= ~(IDE_CAST_CMD_MASK << IDE_CAST_CMD_SHIFT); 1773957df61SMartin K. Petersen cast |= cmd_timings[cmdmode] << IDE_CAST_CMD_SHIFT; 1783957df61SMartin K. Petersen 1793957df61SMartin K. Petersen cs5536_write(pdev, CAST, cast); 1803957df61SMartin K. Petersen } 1813957df61SMartin K. Petersen 1823957df61SMartin K. Petersen /** 1833957df61SMartin K. Petersen * cs5536_set_dmamode - DMA timing setup 1843957df61SMartin K. Petersen * @ap: ATA interface 1853957df61SMartin K. Petersen * @adev: Device being configured 1863957df61SMartin K. Petersen * 1873957df61SMartin K. Petersen */ 1883957df61SMartin K. Petersen 1893957df61SMartin K. Petersen static void cs5536_set_dmamode(struct ata_port *ap, struct ata_device *adev) 1903957df61SMartin K. Petersen { 1913957df61SMartin K. Petersen static const u8 udma_timings[6] = { 1923957df61SMartin K. Petersen 0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6, 1933957df61SMartin K. Petersen }; 1943957df61SMartin K. Petersen 1953957df61SMartin K. Petersen static const u8 mwdma_timings[3] = { 1963957df61SMartin K. Petersen 0x67, 0x21, 0x20, 1973957df61SMartin K. Petersen }; 1983957df61SMartin K. Petersen 1993957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 200d63e94a4SBartlomiej Zolnierkiewicz u32 etc; 2013957df61SMartin K. Petersen int mode = adev->dma_mode; 202b6966a61SMartin K. Petersen int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; 2033957df61SMartin K. Petersen 2043957df61SMartin K. Petersen cs5536_read(pdev, ETC, &etc); 2053957df61SMartin K. Petersen 206d63e94a4SBartlomiej Zolnierkiewicz if (mode >= XFER_UDMA_0) { 2073957df61SMartin K. Petersen etc &= ~(IDE_DRV_MASK << dshift); 2083957df61SMartin K. Petersen etc |= udma_timings[mode - XFER_UDMA_0] << dshift; 209d63e94a4SBartlomiej Zolnierkiewicz } else { /* MWDMA */ 210d63e94a4SBartlomiej Zolnierkiewicz etc &= ~(IDE_ETC_UDMA_MASK << dshift); 211d63e94a4SBartlomiej Zolnierkiewicz cs5536_program_dtc(adev, mwdma_timings[mode - XFER_MW_DMA_0]); 212d63e94a4SBartlomiej Zolnierkiewicz } 2133957df61SMartin K. Petersen 2143957df61SMartin K. Petersen cs5536_write(pdev, ETC, etc); 2153957df61SMartin K. Petersen } 2163957df61SMartin K. Petersen 2173957df61SMartin K. Petersen static struct scsi_host_template cs5536_sht = { 21868d1d07bSTejun Heo ATA_BMDMA_SHT(DRV_NAME), 2193957df61SMartin K. Petersen }; 2203957df61SMartin K. Petersen 2213957df61SMartin K. Petersen static struct ata_port_operations cs5536_port_ops = { 222ba3a221cSKrzysztof Halasa .inherits = &ata_bmdma32_port_ops, 223029cfd6bSTejun Heo .cable_detect = cs5536_cable_detect, 2243957df61SMartin K. Petersen .set_piomode = cs5536_set_piomode, 2253957df61SMartin K. Petersen .set_dmamode = cs5536_set_dmamode, 2263957df61SMartin K. Petersen }; 2273957df61SMartin K. Petersen 2283957df61SMartin K. Petersen /** 2293957df61SMartin K. Petersen * cs5536_init_one 2303957df61SMartin K. Petersen * @dev: PCI device 2313957df61SMartin K. Petersen * @id: Entry in match table 2323957df61SMartin K. Petersen * 2333957df61SMartin K. Petersen */ 2343957df61SMartin K. Petersen 2353957df61SMartin K. Petersen static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id) 2363957df61SMartin K. Petersen { 2373957df61SMartin K. Petersen static const struct ata_port_info info = { 2383957df61SMartin K. Petersen .flags = ATA_FLAG_SLAVE_POSS, 23914bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 24014bdef98SErik Inge Bolsø .mwdma_mask = ATA_MWDMA2, 2413957df61SMartin K. Petersen .udma_mask = ATA_UDMA5, 2423957df61SMartin K. Petersen .port_ops = &cs5536_port_ops, 2433957df61SMartin K. Petersen }; 2443957df61SMartin K. Petersen 2453957df61SMartin K. Petersen const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info }; 2463957df61SMartin K. Petersen u32 cfg; 2473957df61SMartin K. Petersen 2483957df61SMartin K. Petersen if (use_msr) 2493957df61SMartin K. Petersen printk(KERN_ERR DRV_NAME ": Using MSR regs instead of PCI\n"); 2503957df61SMartin K. Petersen 2513957df61SMartin K. Petersen cs5536_read(dev, CFG, &cfg); 2523957df61SMartin K. Petersen 2533957df61SMartin K. Petersen if ((cfg & IDE_CFG_CHANEN) == 0) { 2543957df61SMartin K. Petersen printk(KERN_ERR DRV_NAME ": disabled by BIOS\n"); 2553957df61SMartin K. Petersen return -ENODEV; 2563957df61SMartin K. Petersen } 2573957df61SMartin K. Petersen 2581c5afdf7STejun Heo return ata_pci_bmdma_init_one(dev, ppi, &cs5536_sht, NULL, 0); 2593957df61SMartin K. Petersen } 2603957df61SMartin K. Petersen 2613957df61SMartin K. Petersen static const struct pci_device_id cs5536[] = { 2623957df61SMartin K. Petersen { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), }, 2633957df61SMartin K. Petersen { }, 2643957df61SMartin K. Petersen }; 2653957df61SMartin K. Petersen 2663957df61SMartin K. Petersen static struct pci_driver cs5536_pci_driver = { 2673957df61SMartin K. Petersen .name = DRV_NAME, 2683957df61SMartin K. Petersen .id_table = cs5536, 2693957df61SMartin K. Petersen .probe = cs5536_init_one, 2703957df61SMartin K. Petersen .remove = ata_pci_remove_one, 2713957df61SMartin K. Petersen #ifdef CONFIG_PM 2723957df61SMartin K. Petersen .suspend = ata_pci_device_suspend, 2733957df61SMartin K. Petersen .resume = ata_pci_device_resume, 2743957df61SMartin K. Petersen #endif 2753957df61SMartin K. Petersen }; 2763957df61SMartin K. Petersen 2772fc75da0SAxel Lin module_pci_driver(cs5536_pci_driver); 2783957df61SMartin K. Petersen 2793957df61SMartin K. Petersen MODULE_AUTHOR("Martin K. Petersen"); 2803957df61SMartin K. Petersen MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller"); 2813957df61SMartin K. Petersen MODULE_LICENSE("GPL"); 2823957df61SMartin K. Petersen MODULE_DEVICE_TABLE(pci, cs5536); 2833957df61SMartin K. Petersen MODULE_VERSION(DRV_VERSION); 284