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" 4380f6fd38SBartlomiej Zolnierkiewicz #define DRV_VERSION "0.0.6" 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 883957df61SMartin K. Petersen static inline int cs5536_read(struct pci_dev *pdev, int reg, int *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 * @deadline: deadline jiffies for the operation 1143957df61SMartin K. Petersen * 1153957df61SMartin K. Petersen * Perform cable detection for ATA66 capable cable. Return a libata 1163957df61SMartin K. Petersen * cable type. 1173957df61SMartin K. Petersen */ 1183957df61SMartin K. Petersen 1193957df61SMartin K. Petersen static int cs5536_cable_detect(struct ata_port *ap) 1203957df61SMartin K. Petersen { 1213957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1223957df61SMartin K. Petersen u32 cfg; 1233957df61SMartin K. Petersen 1243957df61SMartin K. Petersen cs5536_read(pdev, CFG, &cfg); 1253957df61SMartin K. Petersen 1263957df61SMartin K. Petersen if (cfg & (IDE_CFG_CABLE << ap->port_no)) 1273957df61SMartin K. Petersen return ATA_CBL_PATA80; 1283957df61SMartin K. Petersen else 1293957df61SMartin K. Petersen return ATA_CBL_PATA40; 1303957df61SMartin K. Petersen } 1313957df61SMartin K. Petersen 1323957df61SMartin K. Petersen /** 1333957df61SMartin K. Petersen * cs5536_set_piomode - PIO setup 1343957df61SMartin K. Petersen * @ap: ATA interface 1353957df61SMartin K. Petersen * @adev: device on the interface 1363957df61SMartin K. Petersen */ 1373957df61SMartin K. Petersen 1383957df61SMartin K. Petersen static void cs5536_set_piomode(struct ata_port *ap, struct ata_device *adev) 1393957df61SMartin K. Petersen { 1403957df61SMartin K. Petersen static const u8 drv_timings[5] = { 1413957df61SMartin K. Petersen 0x98, 0x55, 0x32, 0x21, 0x20, 1423957df61SMartin K. Petersen }; 1433957df61SMartin K. Petersen 1443957df61SMartin K. Petersen static const u8 addr_timings[5] = { 1453957df61SMartin K. Petersen 0x2, 0x1, 0x0, 0x0, 0x0, 1463957df61SMartin K. Petersen }; 1473957df61SMartin K. Petersen 1483957df61SMartin K. Petersen static const u8 cmd_timings[5] = { 1493957df61SMartin K. Petersen 0x99, 0x92, 0x90, 0x22, 0x20, 1503957df61SMartin K. Petersen }; 1513957df61SMartin K. Petersen 1523957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1533957df61SMartin K. Petersen struct ata_device *pair = ata_dev_pair(adev); 1543957df61SMartin K. Petersen int mode = adev->pio_mode - XFER_PIO_0; 1553957df61SMartin K. Petersen int cmdmode = mode; 1563957df61SMartin K. Petersen int dshift = ap->port_no ? IDE_D1_SHIFT : IDE_D0_SHIFT; 1573957df61SMartin K. Petersen int cshift = ap->port_no ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT; 1583957df61SMartin K. Petersen u32 dtc, cast, etc; 1593957df61SMartin K. Petersen 1603957df61SMartin K. Petersen if (pair) 1613957df61SMartin K. Petersen cmdmode = min(mode, pair->pio_mode - XFER_PIO_0); 1623957df61SMartin K. Petersen 1633957df61SMartin K. Petersen cs5536_read(pdev, DTC, &dtc); 1643957df61SMartin K. Petersen cs5536_read(pdev, CAST, &cast); 1653957df61SMartin K. Petersen cs5536_read(pdev, ETC, &etc); 1663957df61SMartin K. Petersen 1673957df61SMartin K. Petersen dtc &= ~(IDE_DRV_MASK << dshift); 1683957df61SMartin K. Petersen dtc |= drv_timings[mode] << dshift; 1693957df61SMartin K. Petersen 1703957df61SMartin K. Petersen cast &= ~(IDE_CAST_DRV_MASK << cshift); 1713957df61SMartin K. Petersen cast |= addr_timings[mode] << cshift; 1723957df61SMartin K. Petersen 1733957df61SMartin K. Petersen cast &= ~(IDE_CAST_CMD_MASK << IDE_CAST_CMD_SHIFT); 1743957df61SMartin K. Petersen cast |= cmd_timings[cmdmode] << IDE_CAST_CMD_SHIFT; 1753957df61SMartin K. Petersen 1763957df61SMartin K. Petersen etc &= ~(IDE_DRV_MASK << dshift); 1773957df61SMartin K. Petersen etc |= IDE_ETC_NODMA << dshift; 1783957df61SMartin K. Petersen 1793957df61SMartin K. Petersen cs5536_write(pdev, DTC, dtc); 1803957df61SMartin K. Petersen cs5536_write(pdev, CAST, cast); 1813957df61SMartin K. Petersen cs5536_write(pdev, ETC, etc); 1823957df61SMartin K. Petersen } 1833957df61SMartin K. Petersen 1843957df61SMartin K. Petersen /** 1853957df61SMartin K. Petersen * cs5536_set_dmamode - DMA timing setup 1863957df61SMartin K. Petersen * @ap: ATA interface 1873957df61SMartin K. Petersen * @adev: Device being configured 1883957df61SMartin K. Petersen * 1893957df61SMartin K. Petersen */ 1903957df61SMartin K. Petersen 1913957df61SMartin K. Petersen static void cs5536_set_dmamode(struct ata_port *ap, struct ata_device *adev) 1923957df61SMartin K. Petersen { 1933957df61SMartin K. Petersen static const u8 udma_timings[6] = { 1943957df61SMartin K. Petersen 0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6, 1953957df61SMartin K. Petersen }; 1963957df61SMartin K. Petersen 1973957df61SMartin K. Petersen static const u8 mwdma_timings[3] = { 1983957df61SMartin K. Petersen 0x67, 0x21, 0x20, 1993957df61SMartin K. Petersen }; 2003957df61SMartin K. Petersen 2013957df61SMartin K. Petersen struct pci_dev *pdev = to_pci_dev(ap->host->dev); 2023957df61SMartin K. Petersen u32 dtc, etc; 2033957df61SMartin K. Petersen int mode = adev->dma_mode; 2043957df61SMartin K. Petersen int dshift = ap->port_no ? IDE_D1_SHIFT : IDE_D0_SHIFT; 2053957df61SMartin K. Petersen 2063957df61SMartin K. Petersen if (mode >= XFER_UDMA_0) { 2073957df61SMartin K. Petersen cs5536_read(pdev, ETC, &etc); 2083957df61SMartin K. Petersen 2093957df61SMartin K. Petersen etc &= ~(IDE_DRV_MASK << dshift); 2103957df61SMartin K. Petersen etc |= udma_timings[mode - XFER_UDMA_0] << dshift; 2113957df61SMartin K. Petersen 2123957df61SMartin K. Petersen cs5536_write(pdev, ETC, etc); 2133957df61SMartin K. Petersen } else { /* MWDMA */ 2143957df61SMartin K. Petersen cs5536_read(pdev, DTC, &dtc); 2153957df61SMartin K. Petersen 2163957df61SMartin K. Petersen dtc &= ~(IDE_DRV_MASK << dshift); 21780f6fd38SBartlomiej Zolnierkiewicz dtc |= mwdma_timings[mode - XFER_MW_DMA_0] << dshift; 2183957df61SMartin K. Petersen 2193957df61SMartin K. Petersen cs5536_write(pdev, DTC, dtc); 2203957df61SMartin K. Petersen } 2213957df61SMartin K. Petersen } 2223957df61SMartin K. Petersen 2233957df61SMartin K. Petersen static struct scsi_host_template cs5536_sht = { 2243957df61SMartin K. Petersen .module = THIS_MODULE, 2253957df61SMartin K. Petersen .name = DRV_NAME, 2263957df61SMartin K. Petersen .ioctl = ata_scsi_ioctl, 2273957df61SMartin K. Petersen .queuecommand = ata_scsi_queuecmd, 2283957df61SMartin K. Petersen .can_queue = ATA_DEF_QUEUE, 2293957df61SMartin K. Petersen .this_id = ATA_SHT_THIS_ID, 2303957df61SMartin K. Petersen .sg_tablesize = LIBATA_MAX_PRD, 2313957df61SMartin K. Petersen .cmd_per_lun = ATA_SHT_CMD_PER_LUN, 2323957df61SMartin K. Petersen .emulated = ATA_SHT_EMULATED, 2333957df61SMartin K. Petersen .use_clustering = ATA_SHT_USE_CLUSTERING, 2343957df61SMartin K. Petersen .proc_name = DRV_NAME, 2353957df61SMartin K. Petersen .dma_boundary = ATA_DMA_BOUNDARY, 2363957df61SMartin K. Petersen .slave_configure = ata_scsi_slave_config, 2373957df61SMartin K. Petersen .slave_destroy = ata_scsi_slave_destroy, 2383957df61SMartin K. Petersen .bios_param = ata_std_bios_param, 2393957df61SMartin K. Petersen }; 2403957df61SMartin K. Petersen 2413957df61SMartin K. Petersen static struct ata_port_operations cs5536_port_ops = { 2423957df61SMartin K. Petersen .set_piomode = cs5536_set_piomode, 2433957df61SMartin K. Petersen .set_dmamode = cs5536_set_dmamode, 2443957df61SMartin K. Petersen .mode_filter = ata_pci_default_filter, 2453957df61SMartin K. Petersen 2463957df61SMartin K. Petersen .tf_load = ata_tf_load, 2473957df61SMartin K. Petersen .tf_read = ata_tf_read, 2483957df61SMartin K. Petersen .check_status = ata_check_status, 2493957df61SMartin K. Petersen .exec_command = ata_exec_command, 2503957df61SMartin K. Petersen .dev_select = ata_std_dev_select, 2513957df61SMartin K. Petersen 2523957df61SMartin K. Petersen .freeze = ata_bmdma_freeze, 2533957df61SMartin K. Petersen .thaw = ata_bmdma_thaw, 2543957df61SMartin K. Petersen .error_handler = ata_bmdma_error_handler, 2553957df61SMartin K. Petersen .post_internal_cmd = ata_bmdma_post_internal_cmd, 2563957df61SMartin K. Petersen .cable_detect = cs5536_cable_detect, 2573957df61SMartin K. Petersen 2583957df61SMartin K. Petersen .bmdma_setup = ata_bmdma_setup, 2593957df61SMartin K. Petersen .bmdma_start = ata_bmdma_start, 2603957df61SMartin K. Petersen .bmdma_stop = ata_bmdma_stop, 2613957df61SMartin K. Petersen .bmdma_status = ata_bmdma_status, 2623957df61SMartin K. Petersen 2633957df61SMartin K. Petersen .qc_prep = ata_qc_prep, 2643957df61SMartin K. Petersen .qc_issue = ata_qc_issue_prot, 2653957df61SMartin K. Petersen 2663957df61SMartin K. Petersen .data_xfer = ata_data_xfer, 2673957df61SMartin K. Petersen 2683957df61SMartin K. Petersen .irq_handler = ata_interrupt, 2693957df61SMartin K. Petersen .irq_clear = ata_bmdma_irq_clear, 2703957df61SMartin K. Petersen .irq_on = ata_irq_on, 2713957df61SMartin K. Petersen 2723957df61SMartin K. Petersen .port_start = ata_port_start, 2733957df61SMartin K. Petersen }; 2743957df61SMartin K. Petersen 2753957df61SMartin K. Petersen /** 2763957df61SMartin K. Petersen * cs5536_init_one 2773957df61SMartin K. Petersen * @dev: PCI device 2783957df61SMartin K. Petersen * @id: Entry in match table 2793957df61SMartin K. Petersen * 2803957df61SMartin K. Petersen */ 2813957df61SMartin K. Petersen 2823957df61SMartin K. Petersen static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id) 2833957df61SMartin K. Petersen { 2843957df61SMartin K. Petersen static const struct ata_port_info info = { 2853957df61SMartin K. Petersen .sht = &cs5536_sht, 2863957df61SMartin K. Petersen .flags = ATA_FLAG_SLAVE_POSS, 2873957df61SMartin K. Petersen .pio_mask = 0x1f, 2883957df61SMartin K. Petersen .mwdma_mask = 0x07, 2893957df61SMartin K. Petersen .udma_mask = ATA_UDMA5, 2903957df61SMartin K. Petersen .port_ops = &cs5536_port_ops, 2913957df61SMartin K. Petersen }; 2923957df61SMartin K. Petersen 2933957df61SMartin K. Petersen const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info }; 2943957df61SMartin K. Petersen u32 cfg; 2953957df61SMartin K. Petersen 2963957df61SMartin K. Petersen if (use_msr) 2973957df61SMartin K. Petersen printk(KERN_ERR DRV_NAME ": Using MSR regs instead of PCI\n"); 2983957df61SMartin K. Petersen 2993957df61SMartin K. Petersen cs5536_read(dev, CFG, &cfg); 3003957df61SMartin K. Petersen 3013957df61SMartin K. Petersen if ((cfg & IDE_CFG_CHANEN) == 0) { 3023957df61SMartin K. Petersen printk(KERN_ERR DRV_NAME ": disabled by BIOS\n"); 3033957df61SMartin K. Petersen return -ENODEV; 3043957df61SMartin K. Petersen } 3053957df61SMartin K. Petersen 3063957df61SMartin K. Petersen return ata_pci_init_one(dev, ppi); 3073957df61SMartin K. Petersen } 3083957df61SMartin K. Petersen 3093957df61SMartin K. Petersen static const struct pci_device_id cs5536[] = { 3103957df61SMartin K. Petersen { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), }, 3113957df61SMartin K. Petersen { }, 3123957df61SMartin K. Petersen }; 3133957df61SMartin K. Petersen 3143957df61SMartin K. Petersen static struct pci_driver cs5536_pci_driver = { 3153957df61SMartin K. Petersen .name = DRV_NAME, 3163957df61SMartin K. Petersen .id_table = cs5536, 3173957df61SMartin K. Petersen .probe = cs5536_init_one, 3183957df61SMartin K. Petersen .remove = ata_pci_remove_one, 3193957df61SMartin K. Petersen #ifdef CONFIG_PM 3203957df61SMartin K. Petersen .suspend = ata_pci_device_suspend, 3213957df61SMartin K. Petersen .resume = ata_pci_device_resume, 3223957df61SMartin K. Petersen #endif 3233957df61SMartin K. Petersen }; 3243957df61SMartin K. Petersen 3253957df61SMartin K. Petersen static int __init cs5536_init(void) 3263957df61SMartin K. Petersen { 3273957df61SMartin K. Petersen return pci_register_driver(&cs5536_pci_driver); 3283957df61SMartin K. Petersen } 3293957df61SMartin K. Petersen 3303957df61SMartin K. Petersen static void __exit cs5536_exit(void) 3313957df61SMartin K. Petersen { 3323957df61SMartin K. Petersen pci_unregister_driver(&cs5536_pci_driver); 3333957df61SMartin K. Petersen } 3343957df61SMartin K. Petersen 3353957df61SMartin K. Petersen MODULE_AUTHOR("Martin K. Petersen"); 3363957df61SMartin K. Petersen MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller"); 3373957df61SMartin K. Petersen MODULE_LICENSE("GPL"); 3383957df61SMartin K. Petersen MODULE_DEVICE_TABLE(pci, cs5536); 3393957df61SMartin K. Petersen MODULE_VERSION(DRV_VERSION); 3403957df61SMartin K. Petersen module_param_named(msr, use_msr, int, 0644); 3413957df61SMartin K. Petersen MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)"); 3423957df61SMartin K. Petersen 3433957df61SMartin K. Petersen module_init(cs5536_init); 3443957df61SMartin K. Petersen module_exit(cs5536_exit); 345