145051539SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23957df61SMartin K. Petersen /* 33957df61SMartin K. Petersen * pata_cs5536.c - CS5536 PATA for new ATA layer 43957df61SMartin K. Petersen * (C) 2007 Martin K. Petersen <mkp@mkp.net> 5d63e94a4SBartlomiej Zolnierkiewicz * (C) 2011 Bartlomiej Zolnierkiewicz 63957df61SMartin K. Petersen * 73957df61SMartin K. Petersen * Documentation: 83957df61SMartin K. Petersen * Available from AMD web site. 93957df61SMartin K. Petersen * 103957df61SMartin K. Petersen * The IDE timing registers for the CS5536 live in the Geode Machine 113957df61SMartin K. Petersen * Specific Register file and not PCI config space. Most BIOSes 123957df61SMartin K. Petersen * virtualize the PCI registers so the chip looks like a standard IDE 133957df61SMartin K. Petersen * controller. Unfortunately not all implementations get this right. 143957df61SMartin K. Petersen * In particular some have problems with unaligned accesses to the 153957df61SMartin K. Petersen * virtualized PCI registers. This driver always does full dword 163957df61SMartin K. Petersen * writes to work around the issue. Also, in case of a bad BIOS this 173957df61SMartin K. Petersen * driver can be loaded with the "msr=1" parameter which forces using 183957df61SMartin K. Petersen * the Machine Specific Registers to configure the device. 193957df61SMartin K. Petersen */ 203957df61SMartin K. Petersen 213957df61SMartin K. Petersen #include <linux/kernel.h> 223957df61SMartin K. Petersen #include <linux/module.h> 233957df61SMartin K. Petersen #include <linux/pci.h> 243957df61SMartin K. Petersen #include <linux/blkdev.h> 253957df61SMartin K. Petersen #include <linux/delay.h> 263957df61SMartin K. Petersen #include <linux/libata.h> 273957df61SMartin K. Petersen #include <scsi/scsi_host.h> 28abf8f2b8SChristian Gmeiner #include <linux/dmi.h> 299272dcc2SWu Zhangjin 309272dcc2SWu Zhangjin #ifdef CONFIG_X86_32 313957df61SMartin K. Petersen #include <asm/msr.h> 329272dcc2SWu Zhangjin static int use_msr; 339272dcc2SWu Zhangjin module_param_named(msr, use_msr, int, 0644); 349272dcc2SWu Zhangjin MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)"); 359272dcc2SWu Zhangjin #else 36ff5dd32bSJeff Garzik #undef rdmsr /* avoid accidental MSR usage on, e.g. x86-64 */ 37ff5dd32bSJeff Garzik #undef wrmsr 389272dcc2SWu Zhangjin #define rdmsr(x, y, z) do { } while (0) 399272dcc2SWu Zhangjin #define wrmsr(x, y, z) do { } while (0) 409272dcc2SWu Zhangjin #define use_msr 0 419272dcc2SWu Zhangjin #endif 423957df61SMartin K. Petersen 433957df61SMartin K. Petersen #define DRV_NAME "pata_cs5536" 449272dcc2SWu Zhangjin #define DRV_VERSION "0.0.8" 453957df61SMartin K. Petersen 463957df61SMartin K. Petersen enum { 47d63e94a4SBartlomiej Zolnierkiewicz MSR_IDE_CFG = 0x51300010, 483957df61SMartin K. Petersen PCI_IDE_CFG = 0x40, 493957df61SMartin K. Petersen 50d63e94a4SBartlomiej Zolnierkiewicz CFG = 0, 51d63e94a4SBartlomiej Zolnierkiewicz DTC = 2, 52d63e94a4SBartlomiej Zolnierkiewicz CAST = 3, 53d63e94a4SBartlomiej Zolnierkiewicz ETC = 4, 54d63e94a4SBartlomiej Zolnierkiewicz 55d63e94a4SBartlomiej Zolnierkiewicz IDE_CFG_CHANEN = (1 << 1), 56d63e94a4SBartlomiej Zolnierkiewicz IDE_CFG_CABLE = (1 << 17) | (1 << 16), 573957df61SMartin K. Petersen 583957df61SMartin K. Petersen IDE_D0_SHIFT = 24, 593957df61SMartin K. Petersen IDE_D1_SHIFT = 16, 603957df61SMartin K. Petersen IDE_DRV_MASK = 0xff, 613957df61SMartin K. Petersen 623957df61SMartin K. Petersen IDE_CAST_D0_SHIFT = 6, 633957df61SMartin K. Petersen IDE_CAST_D1_SHIFT = 4, 643957df61SMartin K. Petersen IDE_CAST_DRV_MASK = 0x3, 653957df61SMartin K. Petersen IDE_CAST_CMD_MASK = 0xff, 663957df61SMartin K. Petersen IDE_CAST_CMD_SHIFT = 24, 673957df61SMartin K. Petersen 68d63e94a4SBartlomiej Zolnierkiewicz IDE_ETC_UDMA_MASK = 0xc0, 693957df61SMartin K. Petersen }; 703957df61SMartin K. Petersen 71abf8f2b8SChristian Gmeiner /* Some Bachmann OT200 devices have a non working UDMA support due a 72abf8f2b8SChristian Gmeiner * missing resistor. 73abf8f2b8SChristian Gmeiner */ 74abf8f2b8SChristian Gmeiner static const struct dmi_system_id udma_quirk_dmi_table[] = { 75abf8f2b8SChristian Gmeiner { 76abf8f2b8SChristian Gmeiner .ident = "Bachmann electronic OT200", 77abf8f2b8SChristian Gmeiner .matches = { 78abf8f2b8SChristian Gmeiner DMI_MATCH(DMI_SYS_VENDOR, "Bachmann electronic"), 79abf8f2b8SChristian Gmeiner DMI_MATCH(DMI_PRODUCT_NAME, "OT200"), 80abf8f2b8SChristian Gmeiner DMI_MATCH(DMI_PRODUCT_VERSION, "1") 81abf8f2b8SChristian Gmeiner }, 82abf8f2b8SChristian Gmeiner }, 83abf8f2b8SChristian Gmeiner { } 84abf8f2b8SChristian Gmeiner }; 85abf8f2b8SChristian Gmeiner 86d63e94a4SBartlomiej Zolnierkiewicz static int cs5536_read(struct pci_dev *pdev, int reg, u32 *val) 873957df61SMartin K. Petersen { 883957df61SMartin K. Petersen if (unlikely(use_msr)) { 899272dcc2SWu Zhangjin u32 dummy __maybe_unused; 903957df61SMartin K. Petersen 91d63e94a4SBartlomiej Zolnierkiewicz rdmsr(MSR_IDE_CFG + reg, *val, dummy); 923957df61SMartin K. Petersen return 0; 933957df61SMartin K. Petersen } 943957df61SMartin K. Petersen 95d63e94a4SBartlomiej Zolnierkiewicz return pci_read_config_dword(pdev, PCI_IDE_CFG + reg * 4, val); 963957df61SMartin K. Petersen } 973957df61SMartin K. Petersen 98d63e94a4SBartlomiej Zolnierkiewicz static int cs5536_write(struct pci_dev *pdev, int reg, int val) 993957df61SMartin K. Petersen { 1003957df61SMartin K. Petersen if (unlikely(use_msr)) { 101d63e94a4SBartlomiej Zolnierkiewicz wrmsr(MSR_IDE_CFG + reg, val, 0); 1023957df61SMartin K. Petersen return 0; 1033957df61SMartin K. Petersen } 1043957df61SMartin K. Petersen 105d63e94a4SBartlomiej Zolnierkiewicz return pci_write_config_dword(pdev, PCI_IDE_CFG + reg * 4, val); 106d63e94a4SBartlomiej Zolnierkiewicz } 107d63e94a4SBartlomiej Zolnierkiewicz 108d63e94a4SBartlomiej Zolnierkiewicz static void cs5536_program_dtc(struct ata_device *adev, u8 tim) 109d63e94a4SBartlomiej Zolnierkiewicz { 110d63e94a4SBartlomiej Zolnierkiewicz struct pci_dev *pdev = to_pci_dev(adev->link->ap->host->dev); 111d63e94a4SBartlomiej Zolnierkiewicz int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; 112d63e94a4SBartlomiej Zolnierkiewicz u32 dtc; 113d63e94a4SBartlomiej Zolnierkiewicz 114d63e94a4SBartlomiej Zolnierkiewicz cs5536_read(pdev, DTC, &dtc); 115d63e94a4SBartlomiej Zolnierkiewicz dtc &= ~(IDE_DRV_MASK << dshift); 116d63e94a4SBartlomiej Zolnierkiewicz dtc |= tim << dshift; 117d63e94a4SBartlomiej Zolnierkiewicz cs5536_write(pdev, DTC, dtc); 1183957df61SMartin K. Petersen } 1193957df61SMartin K. Petersen 1203957df61SMartin K. Petersen /** 1213957df61SMartin K. Petersen * cs5536_cable_detect - detect cable type 1223957df61SMartin K. Petersen * @ap: Port to detect on 1233957df61SMartin K. Petersen * 124d63e94a4SBartlomiej Zolnierkiewicz * Perform cable detection for ATA66 capable cable. 125d63e94a4SBartlomiej Zolnierkiewicz * 126d63e94a4SBartlomiej Zolnierkiewicz * Returns a cable type. 1273957df61SMartin K. Petersen */ 1283957df61SMartin K. Petersen 1293957df61SMartin K. Petersen static int cs5536_cable_detect(struct ata_port *ap) 1303957df61SMartin K. Petersen { 1313957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1323957df61SMartin K. Petersen u32 cfg; 1333957df61SMartin K. Petersen 1343957df61SMartin K. Petersen cs5536_read(pdev, CFG, &cfg); 1353957df61SMartin K. Petersen 136d63e94a4SBartlomiej Zolnierkiewicz if (cfg & IDE_CFG_CABLE) 1373957df61SMartin K. Petersen return ATA_CBL_PATA80; 1383957df61SMartin K. Petersen else 1393957df61SMartin K. Petersen return ATA_CBL_PATA40; 1403957df61SMartin K. Petersen } 1413957df61SMartin K. Petersen 1423957df61SMartin K. Petersen /** 1433957df61SMartin K. Petersen * cs5536_set_piomode - PIO setup 1443957df61SMartin K. Petersen * @ap: ATA interface 1453957df61SMartin K. Petersen * @adev: device on the interface 1463957df61SMartin K. Petersen */ 1473957df61SMartin K. Petersen 1483957df61SMartin K. Petersen static void cs5536_set_piomode(struct ata_port *ap, struct ata_device *adev) 1493957df61SMartin K. Petersen { 1503957df61SMartin K. Petersen static const u8 drv_timings[5] = { 1513957df61SMartin K. Petersen 0x98, 0x55, 0x32, 0x21, 0x20, 1523957df61SMartin K. Petersen }; 1533957df61SMartin K. Petersen 1543957df61SMartin K. Petersen static const u8 addr_timings[5] = { 1553957df61SMartin K. Petersen 0x2, 0x1, 0x0, 0x0, 0x0, 1563957df61SMartin K. Petersen }; 1573957df61SMartin K. Petersen 1583957df61SMartin K. Petersen static const u8 cmd_timings[5] = { 1593957df61SMartin K. Petersen 0x99, 0x92, 0x90, 0x22, 0x20, 1603957df61SMartin K. Petersen }; 1613957df61SMartin K. Petersen 1623957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1633957df61SMartin K. Petersen struct ata_device *pair = ata_dev_pair(adev); 1643957df61SMartin K. Petersen int mode = adev->pio_mode - XFER_PIO_0; 1653957df61SMartin K. Petersen int cmdmode = mode; 166b6966a61SMartin K. Petersen int cshift = adev->devno ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT; 167d63e94a4SBartlomiej Zolnierkiewicz u32 cast; 1683957df61SMartin K. Petersen 1693957df61SMartin K. Petersen if (pair) 1703957df61SMartin K. Petersen cmdmode = min(mode, pair->pio_mode - XFER_PIO_0); 1713957df61SMartin K. Petersen 172d63e94a4SBartlomiej Zolnierkiewicz cs5536_program_dtc(adev, drv_timings[mode]); 1733957df61SMartin K. Petersen 174d63e94a4SBartlomiej Zolnierkiewicz cs5536_read(pdev, CAST, &cast); 1753957df61SMartin K. Petersen 1763957df61SMartin K. Petersen cast &= ~(IDE_CAST_DRV_MASK << cshift); 1773957df61SMartin K. Petersen cast |= addr_timings[mode] << cshift; 1783957df61SMartin K. Petersen 1793957df61SMartin K. Petersen cast &= ~(IDE_CAST_CMD_MASK << IDE_CAST_CMD_SHIFT); 1803957df61SMartin K. Petersen cast |= cmd_timings[cmdmode] << IDE_CAST_CMD_SHIFT; 1813957df61SMartin K. Petersen 1823957df61SMartin K. Petersen cs5536_write(pdev, CAST, cast); 1833957df61SMartin K. Petersen } 1843957df61SMartin K. Petersen 1853957df61SMartin K. Petersen /** 1863957df61SMartin K. Petersen * cs5536_set_dmamode - DMA timing setup 1873957df61SMartin K. Petersen * @ap: ATA interface 1883957df61SMartin K. Petersen * @adev: Device being configured 1893957df61SMartin K. Petersen * 1903957df61SMartin K. Petersen */ 1913957df61SMartin K. Petersen 1923957df61SMartin K. Petersen static void cs5536_set_dmamode(struct ata_port *ap, struct ata_device *adev) 1933957df61SMartin K. Petersen { 1943957df61SMartin K. Petersen static const u8 udma_timings[6] = { 1953957df61SMartin K. Petersen 0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6, 1963957df61SMartin K. Petersen }; 1973957df61SMartin K. Petersen 1983957df61SMartin K. Petersen static const u8 mwdma_timings[3] = { 1993957df61SMartin K. Petersen 0x67, 0x21, 0x20, 2003957df61SMartin K. Petersen }; 2013957df61SMartin K. Petersen 2023957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 203d63e94a4SBartlomiej Zolnierkiewicz u32 etc; 2043957df61SMartin K. Petersen int mode = adev->dma_mode; 205b6966a61SMartin K. Petersen int dshift = adev->devno ? IDE_D1_SHIFT : IDE_D0_SHIFT; 2063957df61SMartin K. Petersen 2073957df61SMartin K. Petersen cs5536_read(pdev, ETC, &etc); 2083957df61SMartin K. Petersen 209d63e94a4SBartlomiej Zolnierkiewicz if (mode >= XFER_UDMA_0) { 2103957df61SMartin K. Petersen etc &= ~(IDE_DRV_MASK << dshift); 2113957df61SMartin K. Petersen etc |= udma_timings[mode - XFER_UDMA_0] << dshift; 212d63e94a4SBartlomiej Zolnierkiewicz } else { /* MWDMA */ 213d63e94a4SBartlomiej Zolnierkiewicz etc &= ~(IDE_ETC_UDMA_MASK << dshift); 214d63e94a4SBartlomiej Zolnierkiewicz cs5536_program_dtc(adev, mwdma_timings[mode - XFER_MW_DMA_0]); 215d63e94a4SBartlomiej Zolnierkiewicz } 2163957df61SMartin K. Petersen 2173957df61SMartin K. Petersen cs5536_write(pdev, ETC, etc); 2183957df61SMartin K. Petersen } 2193957df61SMartin K. Petersen 2203957df61SMartin K. Petersen static struct scsi_host_template cs5536_sht = { 22168d1d07bSTejun Heo ATA_BMDMA_SHT(DRV_NAME), 2223957df61SMartin K. Petersen }; 2233957df61SMartin K. Petersen 2243957df61SMartin K. Petersen static struct ata_port_operations cs5536_port_ops = { 225ba3a221cSKrzysztof Halasa .inherits = &ata_bmdma32_port_ops, 226029cfd6bSTejun Heo .cable_detect = cs5536_cable_detect, 2273957df61SMartin K. Petersen .set_piomode = cs5536_set_piomode, 2283957df61SMartin K. Petersen .set_dmamode = cs5536_set_dmamode, 2293957df61SMartin K. Petersen }; 2303957df61SMartin K. Petersen 2313957df61SMartin K. Petersen /** 2323957df61SMartin K. Petersen * cs5536_init_one 2333957df61SMartin K. Petersen * @dev: PCI device 2343957df61SMartin K. Petersen * @id: Entry in match table 2353957df61SMartin K. Petersen * 2363957df61SMartin K. Petersen */ 2373957df61SMartin K. Petersen 2383957df61SMartin K. Petersen static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id) 2393957df61SMartin K. Petersen { 2403957df61SMartin K. Petersen static const struct ata_port_info info = { 2413957df61SMartin K. Petersen .flags = ATA_FLAG_SLAVE_POSS, 24214bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 24314bdef98SErik Inge Bolsø .mwdma_mask = ATA_MWDMA2, 2443957df61SMartin K. Petersen .udma_mask = ATA_UDMA5, 2453957df61SMartin K. Petersen .port_ops = &cs5536_port_ops, 2463957df61SMartin K. Petersen }; 2473957df61SMartin K. Petersen 248abf8f2b8SChristian Gmeiner static const struct ata_port_info no_udma_info = { 249abf8f2b8SChristian Gmeiner .flags = ATA_FLAG_SLAVE_POSS, 250abf8f2b8SChristian Gmeiner .pio_mask = ATA_PIO4, 251abf8f2b8SChristian Gmeiner .port_ops = &cs5536_port_ops, 252abf8f2b8SChristian Gmeiner }; 253abf8f2b8SChristian Gmeiner 254abf8f2b8SChristian Gmeiner 255abf8f2b8SChristian Gmeiner const struct ata_port_info *ppi[2]; 2563957df61SMartin K. Petersen u32 cfg; 2573957df61SMartin K. Petersen 258abf8f2b8SChristian Gmeiner if (dmi_check_system(udma_quirk_dmi_table)) 259abf8f2b8SChristian Gmeiner ppi[0] = &no_udma_info; 260abf8f2b8SChristian Gmeiner else 261abf8f2b8SChristian Gmeiner ppi[0] = &info; 262abf8f2b8SChristian Gmeiner 263abf8f2b8SChristian Gmeiner ppi[1] = &ata_dummy_port_info; 264abf8f2b8SChristian Gmeiner 2653957df61SMartin K. Petersen if (use_msr) 2663957df61SMartin K. Petersen printk(KERN_ERR DRV_NAME ": Using MSR regs instead of PCI\n"); 2673957df61SMartin K. Petersen 2683957df61SMartin K. Petersen cs5536_read(dev, CFG, &cfg); 2693957df61SMartin K. Petersen 2703957df61SMartin K. Petersen if ((cfg & IDE_CFG_CHANEN) == 0) { 2713957df61SMartin K. Petersen printk(KERN_ERR DRV_NAME ": disabled by BIOS\n"); 2723957df61SMartin K. Petersen return -ENODEV; 2733957df61SMartin K. Petersen } 2743957df61SMartin K. Petersen 2751c5afdf7STejun Heo return ata_pci_bmdma_init_one(dev, ppi, &cs5536_sht, NULL, 0); 2763957df61SMartin K. Petersen } 2773957df61SMartin K. Petersen 2783957df61SMartin K. Petersen static const struct pci_device_id cs5536[] = { 2793957df61SMartin K. Petersen { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), }, 280591b6bb6SAndrey Korolyov { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_DEV_IDE), }, 2813957df61SMartin K. Petersen { }, 2823957df61SMartin K. Petersen }; 2833957df61SMartin K. Petersen 2843957df61SMartin K. Petersen static struct pci_driver cs5536_pci_driver = { 2853957df61SMartin K. Petersen .name = DRV_NAME, 2863957df61SMartin K. Petersen .id_table = cs5536, 2873957df61SMartin K. Petersen .probe = cs5536_init_one, 2883957df61SMartin K. Petersen .remove = ata_pci_remove_one, 28958eb8cd5SBartlomiej Zolnierkiewicz #ifdef CONFIG_PM_SLEEP 2903957df61SMartin K. Petersen .suspend = ata_pci_device_suspend, 2913957df61SMartin K. Petersen .resume = ata_pci_device_resume, 2923957df61SMartin K. Petersen #endif 2933957df61SMartin K. Petersen }; 2943957df61SMartin K. Petersen 2952fc75da0SAxel Lin module_pci_driver(cs5536_pci_driver); 2963957df61SMartin K. Petersen 2973957df61SMartin K. Petersen MODULE_AUTHOR("Martin K. Petersen"); 2983957df61SMartin K. Petersen MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller"); 2993957df61SMartin K. Petersen MODULE_LICENSE("GPL"); 3003957df61SMartin K. Petersen MODULE_DEVICE_TABLE(pci, cs5536); 3013957df61SMartin K. Petersen MODULE_VERSION(DRV_VERSION); 302