18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pata_cmd64x.c - CMD64x PATA for new ATA layer 48c2ecf20Sopenharmony_ci * (C) 2005 Red Hat Inc 58c2ecf20Sopenharmony_ci * Alan Cox <alan@lxorguk.ukuu.org.uk> 68c2ecf20Sopenharmony_ci * (C) 2009-2010 Bartlomiej Zolnierkiewicz 78c2ecf20Sopenharmony_ci * (C) 2012 MontaVista Software, LLC <source@mvista.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based upon 108c2ecf20Sopenharmony_ci * linux/drivers/ide/pci/cmd64x.c Version 1.30 Sept 10, 2002 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines. 138c2ecf20Sopenharmony_ci * Note, this driver is not used at all on other systems because 148c2ecf20Sopenharmony_ci * there the "BIOS" has done all of the following already. 158c2ecf20Sopenharmony_ci * Due to massive hardware bugs, UltraDMA is only supported 168c2ecf20Sopenharmony_ci * on the 646U2 and not on the 646U. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) 198c2ecf20Sopenharmony_ci * Copyright (C) 1998 David S. Miller (davem@redhat.com) 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Copyright (C) 1999-2002 Andre Hedrick <andre@linux-ide.org> 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * TODO 248c2ecf20Sopenharmony_ci * Testing work 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/pci.h> 308c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 318c2ecf20Sopenharmony_ci#include <linux/delay.h> 328c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 338c2ecf20Sopenharmony_ci#include <linux/libata.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define DRV_NAME "pata_cmd64x" 368c2ecf20Sopenharmony_ci#define DRV_VERSION "0.2.18" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * CMD64x specific registers definition. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cienum { 438c2ecf20Sopenharmony_ci CFR = 0x50, 448c2ecf20Sopenharmony_ci CFR_INTR_CH0 = 0x04, 458c2ecf20Sopenharmony_ci CNTRL = 0x51, 468c2ecf20Sopenharmony_ci CNTRL_CH0 = 0x04, 478c2ecf20Sopenharmony_ci CNTRL_CH1 = 0x08, 488c2ecf20Sopenharmony_ci CMDTIM = 0x52, 498c2ecf20Sopenharmony_ci ARTTIM0 = 0x53, 508c2ecf20Sopenharmony_ci DRWTIM0 = 0x54, 518c2ecf20Sopenharmony_ci ARTTIM1 = 0x55, 528c2ecf20Sopenharmony_ci DRWTIM1 = 0x56, 538c2ecf20Sopenharmony_ci ARTTIM23 = 0x57, 548c2ecf20Sopenharmony_ci ARTTIM23_DIS_RA2 = 0x04, 558c2ecf20Sopenharmony_ci ARTTIM23_DIS_RA3 = 0x08, 568c2ecf20Sopenharmony_ci ARTTIM23_INTR_CH1 = 0x10, 578c2ecf20Sopenharmony_ci DRWTIM2 = 0x58, 588c2ecf20Sopenharmony_ci BRST = 0x59, 598c2ecf20Sopenharmony_ci DRWTIM3 = 0x5b, 608c2ecf20Sopenharmony_ci BMIDECR0 = 0x70, 618c2ecf20Sopenharmony_ci MRDMODE = 0x71, 628c2ecf20Sopenharmony_ci MRDMODE_INTR_CH0 = 0x04, 638c2ecf20Sopenharmony_ci MRDMODE_INTR_CH1 = 0x08, 648c2ecf20Sopenharmony_ci BMIDESR0 = 0x72, 658c2ecf20Sopenharmony_ci UDIDETCR0 = 0x73, 668c2ecf20Sopenharmony_ci DTPR0 = 0x74, 678c2ecf20Sopenharmony_ci BMIDECR1 = 0x78, 688c2ecf20Sopenharmony_ci BMIDECSR = 0x79, 698c2ecf20Sopenharmony_ci UDIDETCR1 = 0x7B, 708c2ecf20Sopenharmony_ci DTPR1 = 0x7C 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int cmd648_cable_detect(struct ata_port *ap) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 768c2ecf20Sopenharmony_ci u8 r; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* Check cable detect bits */ 798c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, BMIDECSR, &r); 808c2ecf20Sopenharmony_ci if (r & (1 << ap->port_no)) 818c2ecf20Sopenharmony_ci return ATA_CBL_PATA80; 828c2ecf20Sopenharmony_ci return ATA_CBL_PATA40; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/** 868c2ecf20Sopenharmony_ci * cmd64x_set_timing - set PIO and MWDMA timing 878c2ecf20Sopenharmony_ci * @ap: ATA interface 888c2ecf20Sopenharmony_ci * @adev: ATA device 898c2ecf20Sopenharmony_ci * @mode: mode 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Called to do the PIO and MWDMA mode setup. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void cmd64x_set_timing(struct ata_port *ap, struct ata_device *adev, u8 mode) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 978c2ecf20Sopenharmony_ci struct ata_timing t; 988c2ecf20Sopenharmony_ci const unsigned long T = 1000000 / 33; 998c2ecf20Sopenharmony_ci const u8 setup_data[] = { 0x40, 0x40, 0x40, 0x80, 0x00 }; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci u8 reg; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Port layout is not logical so use a table */ 1048c2ecf20Sopenharmony_ci const u8 arttim_port[2][2] = { 1058c2ecf20Sopenharmony_ci { ARTTIM0, ARTTIM1 }, 1068c2ecf20Sopenharmony_ci { ARTTIM23, ARTTIM23 } 1078c2ecf20Sopenharmony_ci }; 1088c2ecf20Sopenharmony_ci const u8 drwtim_port[2][2] = { 1098c2ecf20Sopenharmony_ci { DRWTIM0, DRWTIM1 }, 1108c2ecf20Sopenharmony_ci { DRWTIM2, DRWTIM3 } 1118c2ecf20Sopenharmony_ci }; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci int arttim = arttim_port[ap->port_no][adev->devno]; 1148c2ecf20Sopenharmony_ci int drwtim = drwtim_port[ap->port_no][adev->devno]; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* ata_timing_compute is smart and will produce timings for MWDMA 1178c2ecf20Sopenharmony_ci that don't violate the drives PIO capabilities. */ 1188c2ecf20Sopenharmony_ci if (ata_timing_compute(adev, mode, &t, T, 0) < 0) { 1198c2ecf20Sopenharmony_ci printk(KERN_ERR DRV_NAME ": mode computation failed.\n"); 1208c2ecf20Sopenharmony_ci return; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci if (ap->port_no) { 1238c2ecf20Sopenharmony_ci /* Slave has shared address setup */ 1248c2ecf20Sopenharmony_ci struct ata_device *pair = ata_dev_pair(adev); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (pair) { 1278c2ecf20Sopenharmony_ci struct ata_timing tp; 1288c2ecf20Sopenharmony_ci ata_timing_compute(pair, pair->pio_mode, &tp, T, 0); 1298c2ecf20Sopenharmony_ci ata_timing_merge(&t, &tp, &t, ATA_TIMING_SETUP); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci printk(KERN_DEBUG DRV_NAME ": active %d recovery %d setup %d.\n", 1348c2ecf20Sopenharmony_ci t.active, t.recover, t.setup); 1358c2ecf20Sopenharmony_ci if (t.recover > 16) { 1368c2ecf20Sopenharmony_ci t.active += t.recover - 16; 1378c2ecf20Sopenharmony_ci t.recover = 16; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci if (t.active > 16) 1408c2ecf20Sopenharmony_ci t.active = 16; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Now convert the clocks into values we can actually stuff into 1438c2ecf20Sopenharmony_ci the chip */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (t.recover == 16) 1468c2ecf20Sopenharmony_ci t.recover = 0; 1478c2ecf20Sopenharmony_ci else if (t.recover > 1) 1488c2ecf20Sopenharmony_ci t.recover--; 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci t.recover = 15; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (t.setup > 4) 1538c2ecf20Sopenharmony_ci t.setup = 0xC0; 1548c2ecf20Sopenharmony_ci else 1558c2ecf20Sopenharmony_ci t.setup = setup_data[t.setup]; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci t.active &= 0x0F; /* 0 = 16 */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Load setup timing */ 1608c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, arttim, ®); 1618c2ecf20Sopenharmony_ci reg &= 0x3F; 1628c2ecf20Sopenharmony_ci reg |= t.setup; 1638c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, arttim, reg); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Load active/recovery */ 1668c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, drwtim, (t.active << 4) | t.recover); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/** 1708c2ecf20Sopenharmony_ci * cmd64x_set_piomode - set initial PIO mode data 1718c2ecf20Sopenharmony_ci * @ap: ATA interface 1728c2ecf20Sopenharmony_ci * @adev: ATA device 1738c2ecf20Sopenharmony_ci * 1748c2ecf20Sopenharmony_ci * Used when configuring the devices ot set the PIO timings. All the 1758c2ecf20Sopenharmony_ci * actual work is done by the PIO/MWDMA setting helper 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void cmd64x_set_piomode(struct ata_port *ap, struct ata_device *adev) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci cmd64x_set_timing(ap, adev, adev->pio_mode); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/** 1848c2ecf20Sopenharmony_ci * cmd64x_set_dmamode - set initial DMA mode data 1858c2ecf20Sopenharmony_ci * @ap: ATA interface 1868c2ecf20Sopenharmony_ci * @adev: ATA device 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * Called to do the DMA mode setup. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void cmd64x_set_dmamode(struct ata_port *ap, struct ata_device *adev) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci static const u8 udma_data[] = { 1948c2ecf20Sopenharmony_ci 0x30, 0x20, 0x10, 0x20, 0x10, 0x00 1958c2ecf20Sopenharmony_ci }; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1988c2ecf20Sopenharmony_ci u8 regU, regD; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci int pciU = UDIDETCR0 + 8 * ap->port_no; 2018c2ecf20Sopenharmony_ci int pciD = BMIDESR0 + 8 * ap->port_no; 2028c2ecf20Sopenharmony_ci int shift = 2 * adev->devno; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, pciD, ®D); 2058c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, pciU, ®U); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* DMA bits off */ 2088c2ecf20Sopenharmony_ci regD &= ~(0x20 << adev->devno); 2098c2ecf20Sopenharmony_ci /* DMA control bits */ 2108c2ecf20Sopenharmony_ci regU &= ~(0x30 << shift); 2118c2ecf20Sopenharmony_ci /* DMA timing bits */ 2128c2ecf20Sopenharmony_ci regU &= ~(0x05 << adev->devno); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (adev->dma_mode >= XFER_UDMA_0) { 2158c2ecf20Sopenharmony_ci /* Merge the timing value */ 2168c2ecf20Sopenharmony_ci regU |= udma_data[adev->dma_mode - XFER_UDMA_0] << shift; 2178c2ecf20Sopenharmony_ci /* Merge the control bits */ 2188c2ecf20Sopenharmony_ci regU |= 1 << adev->devno; /* UDMA on */ 2198c2ecf20Sopenharmony_ci if (adev->dma_mode > XFER_UDMA_2) /* 15nS timing */ 2208c2ecf20Sopenharmony_ci regU |= 4 << adev->devno; 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci regU &= ~ (1 << adev->devno); /* UDMA off */ 2238c2ecf20Sopenharmony_ci cmd64x_set_timing(ap, adev, adev->dma_mode); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci regD |= 0x20 << adev->devno; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, pciU, regU); 2298c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, pciD, regD); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/** 2338c2ecf20Sopenharmony_ci * cmd64x_sff_irq_check - check IDE interrupt 2348c2ecf20Sopenharmony_ci * @ap: ATA interface 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * Check IDE interrupt in CFR/ARTTIM23 registers. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic bool cmd64x_sff_irq_check(struct ata_port *ap) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 2428c2ecf20Sopenharmony_ci int irq_mask = ap->port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; 2438c2ecf20Sopenharmony_ci int irq_reg = ap->port_no ? ARTTIM23 : CFR; 2448c2ecf20Sopenharmony_ci u8 irq_stat; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* NOTE: reading the register should clear the interrupt */ 2478c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, irq_reg, &irq_stat); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return irq_stat & irq_mask; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/** 2538c2ecf20Sopenharmony_ci * cmd64x_sff_irq_clear - clear IDE interrupt 2548c2ecf20Sopenharmony_ci * @ap: ATA interface 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * Clear IDE interrupt in CFR/ARTTIM23 and DMA status registers. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void cmd64x_sff_irq_clear(struct ata_port *ap) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 2628c2ecf20Sopenharmony_ci int irq_reg = ap->port_no ? ARTTIM23 : CFR; 2638c2ecf20Sopenharmony_ci u8 irq_stat; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ata_bmdma_irq_clear(ap); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Reading the register should be enough to clear the interrupt */ 2688c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, irq_reg, &irq_stat); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/** 2728c2ecf20Sopenharmony_ci * cmd648_sff_irq_check - check IDE interrupt 2738c2ecf20Sopenharmony_ci * @ap: ATA interface 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Check IDE interrupt in MRDMODE register. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic bool cmd648_sff_irq_check(struct ata_port *ap) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 2818c2ecf20Sopenharmony_ci unsigned long base = pci_resource_start(pdev, 4); 2828c2ecf20Sopenharmony_ci int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; 2838c2ecf20Sopenharmony_ci u8 mrdmode = inb(base + 1); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return mrdmode & irq_mask; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * cmd648_sff_irq_clear - clear IDE interrupt 2908c2ecf20Sopenharmony_ci * @ap: ATA interface 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * Clear IDE interrupt in MRDMODE and DMA status registers. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic void cmd648_sff_irq_clear(struct ata_port *ap) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 2988c2ecf20Sopenharmony_ci unsigned long base = pci_resource_start(pdev, 4); 2998c2ecf20Sopenharmony_ci int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; 3008c2ecf20Sopenharmony_ci u8 mrdmode; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ata_bmdma_irq_clear(ap); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Clear this port's interrupt bit (leaving the other port alone) */ 3058c2ecf20Sopenharmony_ci mrdmode = inb(base + 1); 3068c2ecf20Sopenharmony_ci mrdmode &= ~(MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1); 3078c2ecf20Sopenharmony_ci outb(mrdmode | irq_mask, base + 1); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/** 3118c2ecf20Sopenharmony_ci * cmd646r1_bmdma_stop - DMA stop callback 3128c2ecf20Sopenharmony_ci * @qc: Command in progress 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * Stub for now while investigating the r1 quirk in the old driver. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void cmd646r1_bmdma_stop(struct ata_queued_cmd *qc) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci ata_bmdma_stop(qc); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic struct scsi_host_template cmd64x_sht = { 3238c2ecf20Sopenharmony_ci ATA_BMDMA_SHT(DRV_NAME), 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic const struct ata_port_operations cmd64x_base_ops = { 3278c2ecf20Sopenharmony_ci .inherits = &ata_bmdma_port_ops, 3288c2ecf20Sopenharmony_ci .set_piomode = cmd64x_set_piomode, 3298c2ecf20Sopenharmony_ci .set_dmamode = cmd64x_set_dmamode, 3308c2ecf20Sopenharmony_ci}; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic struct ata_port_operations cmd64x_port_ops = { 3338c2ecf20Sopenharmony_ci .inherits = &cmd64x_base_ops, 3348c2ecf20Sopenharmony_ci .sff_irq_check = cmd64x_sff_irq_check, 3358c2ecf20Sopenharmony_ci .sff_irq_clear = cmd64x_sff_irq_clear, 3368c2ecf20Sopenharmony_ci .cable_detect = ata_cable_40wire, 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic struct ata_port_operations cmd646r1_port_ops = { 3408c2ecf20Sopenharmony_ci .inherits = &cmd64x_base_ops, 3418c2ecf20Sopenharmony_ci .sff_irq_check = cmd64x_sff_irq_check, 3428c2ecf20Sopenharmony_ci .sff_irq_clear = cmd64x_sff_irq_clear, 3438c2ecf20Sopenharmony_ci .bmdma_stop = cmd646r1_bmdma_stop, 3448c2ecf20Sopenharmony_ci .cable_detect = ata_cable_40wire, 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic struct ata_port_operations cmd646r3_port_ops = { 3488c2ecf20Sopenharmony_ci .inherits = &cmd64x_base_ops, 3498c2ecf20Sopenharmony_ci .sff_irq_check = cmd648_sff_irq_check, 3508c2ecf20Sopenharmony_ci .sff_irq_clear = cmd648_sff_irq_clear, 3518c2ecf20Sopenharmony_ci .cable_detect = ata_cable_40wire, 3528c2ecf20Sopenharmony_ci}; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic struct ata_port_operations cmd648_port_ops = { 3558c2ecf20Sopenharmony_ci .inherits = &cmd64x_base_ops, 3568c2ecf20Sopenharmony_ci .sff_irq_check = cmd648_sff_irq_check, 3578c2ecf20Sopenharmony_ci .sff_irq_clear = cmd648_sff_irq_clear, 3588c2ecf20Sopenharmony_ci .cable_detect = cmd648_cable_detect, 3598c2ecf20Sopenharmony_ci}; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void cmd64x_fixup(struct pci_dev *pdev) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci u8 mrdmode; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); 3668c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, MRDMODE, &mrdmode); 3678c2ecf20Sopenharmony_ci mrdmode &= ~0x30; /* IRQ set up */ 3688c2ecf20Sopenharmony_ci mrdmode |= 0x02; /* Memory read line enable */ 3698c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, MRDMODE, mrdmode); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* PPC specific fixup copied from old driver */ 3728c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 3738c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, UDIDETCR0, 0xF0); 3748c2ecf20Sopenharmony_ci#endif 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci static const struct ata_port_info cmd_info[7] = { 3808c2ecf20Sopenharmony_ci { /* CMD 643 - no UDMA */ 3818c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 3828c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 3838c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 3848c2ecf20Sopenharmony_ci .port_ops = &cmd64x_port_ops 3858c2ecf20Sopenharmony_ci }, 3868c2ecf20Sopenharmony_ci { /* CMD 646 with broken UDMA */ 3878c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 3888c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 3898c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 3908c2ecf20Sopenharmony_ci .port_ops = &cmd64x_port_ops 3918c2ecf20Sopenharmony_ci }, 3928c2ecf20Sopenharmony_ci { /* CMD 646U with broken UDMA */ 3938c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 3948c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 3958c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 3968c2ecf20Sopenharmony_ci .port_ops = &cmd646r3_port_ops 3978c2ecf20Sopenharmony_ci }, 3988c2ecf20Sopenharmony_ci { /* CMD 646U2 with working UDMA */ 3998c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 4008c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 4018c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 4028c2ecf20Sopenharmony_ci .udma_mask = ATA_UDMA2, 4038c2ecf20Sopenharmony_ci .port_ops = &cmd646r3_port_ops 4048c2ecf20Sopenharmony_ci }, 4058c2ecf20Sopenharmony_ci { /* CMD 646 rev 1 */ 4068c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 4078c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 4088c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 4098c2ecf20Sopenharmony_ci .port_ops = &cmd646r1_port_ops 4108c2ecf20Sopenharmony_ci }, 4118c2ecf20Sopenharmony_ci { /* CMD 648 */ 4128c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 4138c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 4148c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 4158c2ecf20Sopenharmony_ci .udma_mask = ATA_UDMA4, 4168c2ecf20Sopenharmony_ci .port_ops = &cmd648_port_ops 4178c2ecf20Sopenharmony_ci }, 4188c2ecf20Sopenharmony_ci { /* CMD 649 */ 4198c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 4208c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 4218c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 4228c2ecf20Sopenharmony_ci .udma_mask = ATA_UDMA5, 4238c2ecf20Sopenharmony_ci .port_ops = &cmd648_port_ops 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci }; 4268c2ecf20Sopenharmony_ci const struct ata_port_info *ppi[] = { 4278c2ecf20Sopenharmony_ci &cmd_info[id->driver_data], 4288c2ecf20Sopenharmony_ci &cmd_info[id->driver_data], 4298c2ecf20Sopenharmony_ci NULL 4308c2ecf20Sopenharmony_ci }; 4318c2ecf20Sopenharmony_ci u8 reg; 4328c2ecf20Sopenharmony_ci int rc; 4338c2ecf20Sopenharmony_ci struct pci_dev *bridge = pdev->bus->self; 4348c2ecf20Sopenharmony_ci /* mobility split bridges don't report enabled ports correctly */ 4358c2ecf20Sopenharmony_ci int port_ok = !(bridge && bridge->vendor == 4368c2ecf20Sopenharmony_ci PCI_VENDOR_ID_MOBILITY_ELECTRONICS); 4378c2ecf20Sopenharmony_ci /* all (with exceptions below) apart from 643 have CNTRL_CH0 bit */ 4388c2ecf20Sopenharmony_ci int cntrl_ch0_ok = (id->driver_data != 0); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci rc = pcim_enable_device(pdev); 4418c2ecf20Sopenharmony_ci if (rc) 4428c2ecf20Sopenharmony_ci return rc; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (id->driver_data == 0) /* 643 */ 4458c2ecf20Sopenharmony_ci ata_pci_bmdma_clear_simplex(pdev); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (pdev->device == PCI_DEVICE_ID_CMD_646) 4488c2ecf20Sopenharmony_ci switch (pdev->revision) { 4498c2ecf20Sopenharmony_ci /* UDMA works since rev 5 */ 4508c2ecf20Sopenharmony_ci default: 4518c2ecf20Sopenharmony_ci ppi[0] = &cmd_info[3]; 4528c2ecf20Sopenharmony_ci ppi[1] = &cmd_info[3]; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci /* Interrupts in MRDMODE since rev 3 */ 4558c2ecf20Sopenharmony_ci case 3: 4568c2ecf20Sopenharmony_ci case 4: 4578c2ecf20Sopenharmony_ci ppi[0] = &cmd_info[2]; 4588c2ecf20Sopenharmony_ci ppi[1] = &cmd_info[2]; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci /* Rev 1 with other problems? */ 4618c2ecf20Sopenharmony_ci case 1: 4628c2ecf20Sopenharmony_ci ppi[0] = &cmd_info[4]; 4638c2ecf20Sopenharmony_ci ppi[1] = &cmd_info[4]; 4648c2ecf20Sopenharmony_ci fallthrough; 4658c2ecf20Sopenharmony_ci /* Early revs have no CNTRL_CH0 */ 4668c2ecf20Sopenharmony_ci case 2: 4678c2ecf20Sopenharmony_ci case 0: 4688c2ecf20Sopenharmony_ci cntrl_ch0_ok = 0; 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci cmd64x_fixup(pdev); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* check for enabled ports */ 4758c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, CNTRL, ®); 4768c2ecf20Sopenharmony_ci if (!port_ok) 4778c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Mobility Bridge detected, ignoring CNTRL port enable/disable\n"); 4788c2ecf20Sopenharmony_ci if (port_ok && cntrl_ch0_ok && !(reg & CNTRL_CH0)) { 4798c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Primary port is disabled\n"); 4808c2ecf20Sopenharmony_ci ppi[0] = &ata_dummy_port_info; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci if (port_ok && !(reg & CNTRL_CH1)) { 4848c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Secondary port is disabled\n"); 4858c2ecf20Sopenharmony_ci ppi[1] = &ata_dummy_port_info; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return ata_pci_bmdma_init_one(pdev, ppi, &cmd64x_sht, NULL, 0); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4928c2ecf20Sopenharmony_cistatic int cmd64x_reinit_one(struct pci_dev *pdev) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct ata_host *host = pci_get_drvdata(pdev); 4958c2ecf20Sopenharmony_ci int rc; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci rc = ata_pci_device_do_resume(pdev); 4988c2ecf20Sopenharmony_ci if (rc) 4998c2ecf20Sopenharmony_ci return rc; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci cmd64x_fixup(pdev); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ata_host_resume(host); 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci#endif 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic const struct pci_device_id cmd64x[] = { 5098c2ecf20Sopenharmony_ci { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 }, 5108c2ecf20Sopenharmony_ci { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 }, 5118c2ecf20Sopenharmony_ci { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 5 }, 5128c2ecf20Sopenharmony_ci { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 6 }, 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci { }, 5158c2ecf20Sopenharmony_ci}; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic struct pci_driver cmd64x_pci_driver = { 5188c2ecf20Sopenharmony_ci .name = DRV_NAME, 5198c2ecf20Sopenharmony_ci .id_table = cmd64x, 5208c2ecf20Sopenharmony_ci .probe = cmd64x_init_one, 5218c2ecf20Sopenharmony_ci .remove = ata_pci_remove_one, 5228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5238c2ecf20Sopenharmony_ci .suspend = ata_pci_device_suspend, 5248c2ecf20Sopenharmony_ci .resume = cmd64x_reinit_one, 5258c2ecf20Sopenharmony_ci#endif 5268c2ecf20Sopenharmony_ci}; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cimodule_pci_driver(cmd64x_pci_driver); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alan Cox"); 5318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("low-level driver for CMD64x series PATA controllers"); 5328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cmd64x); 5348c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 535