18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pata_serverworks.c - Serverworks PATA for new ATA layer 48c2ecf20Sopenharmony_ci * (C) 2005 Red Hat Inc 58c2ecf20Sopenharmony_ci * (C) 2010 Bartlomiej Zolnierkiewicz 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * based upon 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * serverworks.c 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 1998-2000 Michel Aubry 128c2ecf20Sopenharmony_ci * Copyright (C) 1998-2000 Andrzej Krzysztofowicz 138c2ecf20Sopenharmony_ci * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> 148c2ecf20Sopenharmony_ci * Portions copyright (c) 2001 Sun Microsystems 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * RCC/ServerWorks IDE driver for Linux 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * OSB4: `Open South Bridge' IDE Interface (fn 1) 208c2ecf20Sopenharmony_ci * supports UDMA mode 2 (33 MB/s) 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * CSB5: `Champion South Bridge' IDE Interface (fn 1) 238c2ecf20Sopenharmony_ci * all revisions support UDMA mode 4 (66 MB/s) 248c2ecf20Sopenharmony_ci * revision A2.0 and up support UDMA mode 5 (100 MB/s) 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * *** The CSB5 does not provide ANY register *** 278c2ecf20Sopenharmony_ci * *** to detect 80-conductor cable presence. *** 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * CSB6: `Champion South Bridge' IDE Interface (optional: third channel) 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Documentation: 328c2ecf20Sopenharmony_ci * Available under NDA only. Errata info very hard to get. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/kernel.h> 368c2ecf20Sopenharmony_ci#include <linux/module.h> 378c2ecf20Sopenharmony_ci#include <linux/pci.h> 388c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 398c2ecf20Sopenharmony_ci#include <linux/delay.h> 408c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 418c2ecf20Sopenharmony_ci#include <linux/libata.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define DRV_NAME "pata_serverworks" 448c2ecf20Sopenharmony_ci#define DRV_VERSION "0.4.3" 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ 478c2ecf20Sopenharmony_ci#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Seagate Barracuda ATA IV Family drives in UDMA mode 5 508c2ecf20Sopenharmony_ci * can overrun their FIFOs when used with the CSB5 */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic const char *csb_bad_ata100[] = { 538c2ecf20Sopenharmony_ci "ST320011A", 548c2ecf20Sopenharmony_ci "ST340016A", 558c2ecf20Sopenharmony_ci "ST360021A", 568c2ecf20Sopenharmony_ci "ST380021A", 578c2ecf20Sopenharmony_ci NULL 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/** 618c2ecf20Sopenharmony_ci * oem_cable - Dell/Sun serverworks cable detection 628c2ecf20Sopenharmony_ci * @ap: ATA port to do cable detect 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Dell PowerEdge and Sun Cobalt 'Alpine' hide the 40/80 pin select 658c2ecf20Sopenharmony_ci * for their interfaces in the top two bits of the subsystem ID. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int oem_cable(struct ata_port *ap) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (pdev->subsystem_device & (1 << (ap->port_no + 14))) 738c2ecf20Sopenharmony_ci return ATA_CBL_PATA80; 748c2ecf20Sopenharmony_ci return ATA_CBL_PATA40; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistruct sv_cable_table { 788c2ecf20Sopenharmony_ci int device; 798c2ecf20Sopenharmony_ci int subvendor; 808c2ecf20Sopenharmony_ci int (*cable_detect)(struct ata_port *ap); 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic struct sv_cable_table cable_detect[] = { 848c2ecf20Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, oem_cable }, 858c2ecf20Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, oem_cable }, 868c2ecf20Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN, oem_cable }, 878c2ecf20Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, ata_cable_40wire }, 888c2ecf20Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, ata_cable_unknown }, 898c2ecf20Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, ata_cable_unknown }, 908c2ecf20Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, ata_cable_unknown }, 918c2ecf20Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, ata_cable_unknown }, 928c2ecf20Sopenharmony_ci { } 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/** 968c2ecf20Sopenharmony_ci * serverworks_cable_detect - cable detection 978c2ecf20Sopenharmony_ci * @ap: ATA port 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * Perform cable detection according to the device and subvendor 1008c2ecf20Sopenharmony_ci * identifications 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int serverworks_cable_detect(struct ata_port *ap) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 1068c2ecf20Sopenharmony_ci struct sv_cable_table *cb = cable_detect; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci while(cb->device) { 1098c2ecf20Sopenharmony_ci if (cb->device == pdev->device && 1108c2ecf20Sopenharmony_ci (cb->subvendor == pdev->subsystem_vendor || 1118c2ecf20Sopenharmony_ci cb->subvendor == PCI_ANY_ID)) { 1128c2ecf20Sopenharmony_ci return cb->cable_detect(ap); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci cb++; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci BUG(); 1188c2ecf20Sopenharmony_ci return -1; /* kill compiler warning */ 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * serverworks_is_csb - Check for CSB or OSB 1238c2ecf20Sopenharmony_ci * @pdev: PCI device to check 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * Returns true if the device being checked is known to be a CSB 1268c2ecf20Sopenharmony_ci * series device. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic u8 serverworks_is_csb(struct pci_dev *pdev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci switch (pdev->device) { 1328c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: 1338c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: 1348c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: 1358c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: 1368c2ecf20Sopenharmony_ci return 1; 1378c2ecf20Sopenharmony_ci default: 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/** 1448c2ecf20Sopenharmony_ci * serverworks_osb4_filter - mode selection filter 1458c2ecf20Sopenharmony_ci * @adev: ATA device 1468c2ecf20Sopenharmony_ci * @mask: Mask of proposed modes 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * Filter the offered modes for the device to apply controller 1498c2ecf20Sopenharmony_ci * specific rules. OSB4 requires no UDMA for disks due to a FIFO 1508c2ecf20Sopenharmony_ci * bug we hit. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci if (adev->class == ATA_DEV_ATA) 1568c2ecf20Sopenharmony_ci mask &= ~ATA_MASK_UDMA; 1578c2ecf20Sopenharmony_ci return mask; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/** 1628c2ecf20Sopenharmony_ci * serverworks_csb_filter - mode selection filter 1638c2ecf20Sopenharmony_ci * @adev: ATA device 1648c2ecf20Sopenharmony_ci * @mask: Mask of proposed modes 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * Check the blacklist and disable UDMA5 if matched 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci const char *p; 1728c2ecf20Sopenharmony_ci char model_num[ATA_ID_PROD_LEN + 1]; 1738c2ecf20Sopenharmony_ci int i; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Disk, UDMA */ 1768c2ecf20Sopenharmony_ci if (adev->class != ATA_DEV_ATA) 1778c2ecf20Sopenharmony_ci return mask; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Actually do need to check */ 1808c2ecf20Sopenharmony_ci ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (i = 0; (p = csb_bad_ata100[i]) != NULL; i++) { 1838c2ecf20Sopenharmony_ci if (!strcmp(p, model_num)) 1848c2ecf20Sopenharmony_ci mask &= ~(0xE0 << ATA_SHIFT_UDMA); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci return mask; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/** 1908c2ecf20Sopenharmony_ci * serverworks_set_piomode - set initial PIO mode data 1918c2ecf20Sopenharmony_ci * @ap: ATA interface 1928c2ecf20Sopenharmony_ci * @adev: ATA device 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Program the OSB4/CSB5 timing registers for PIO. The PIO register 1958c2ecf20Sopenharmony_ci * load is done as a simple lookup. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistatic void serverworks_set_piomode(struct ata_port *ap, struct ata_device *adev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci static const u8 pio_mode[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 }; 2008c2ecf20Sopenharmony_ci int offset = 1 + 2 * ap->port_no - adev->devno; 2018c2ecf20Sopenharmony_ci int devbits = (2 * ap->port_no + adev->devno) * 4; 2028c2ecf20Sopenharmony_ci u16 csb5_pio; 2038c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 2048c2ecf20Sopenharmony_ci int pio = adev->pio_mode - XFER_PIO_0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x40 + offset, pio_mode[pio]); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* The OSB4 just requires the timing but the CSB series want the 2098c2ecf20Sopenharmony_ci mode number as well */ 2108c2ecf20Sopenharmony_ci if (serverworks_is_csb(pdev)) { 2118c2ecf20Sopenharmony_ci pci_read_config_word(pdev, 0x4A, &csb5_pio); 2128c2ecf20Sopenharmony_ci csb5_pio &= ~(0x0F << devbits); 2138c2ecf20Sopenharmony_ci pci_write_config_word(pdev, 0x4A, csb5_pio | (pio << devbits)); 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/** 2188c2ecf20Sopenharmony_ci * serverworks_set_dmamode - set initial DMA mode data 2198c2ecf20Sopenharmony_ci * @ap: ATA interface 2208c2ecf20Sopenharmony_ci * @adev: ATA device 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * Program the MWDMA/UDMA modes for the serverworks OSB4/CSB5 2238c2ecf20Sopenharmony_ci * chipset. The MWDMA mode values are pulled from a lookup table 2248c2ecf20Sopenharmony_ci * while the chipset uses mode number for UDMA. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci static const u8 dma_mode[] = { 0x77, 0x21, 0x20 }; 2308c2ecf20Sopenharmony_ci int offset = 1 + 2 * ap->port_no - adev->devno; 2318c2ecf20Sopenharmony_ci int devbits = 2 * ap->port_no + adev->devno; 2328c2ecf20Sopenharmony_ci u8 ultra; 2338c2ecf20Sopenharmony_ci u8 ultra_cfg; 2348c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, 0x54, &ultra_cfg); 2378c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra); 2388c2ecf20Sopenharmony_ci ultra &= ~(0x0F << (adev->devno * 4)); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (adev->dma_mode >= XFER_UDMA_0) { 2418c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x44 + offset, 0x20); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ultra |= (adev->dma_mode - XFER_UDMA_0) 2448c2ecf20Sopenharmony_ci << (adev->devno * 4); 2458c2ecf20Sopenharmony_ci ultra_cfg |= (1 << devbits); 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x44 + offset, 2488c2ecf20Sopenharmony_ci dma_mode[adev->dma_mode - XFER_MW_DMA_0]); 2498c2ecf20Sopenharmony_ci ultra_cfg &= ~(1 << devbits); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra); 2528c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x54, ultra_cfg); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic struct scsi_host_template serverworks_osb4_sht = { 2568c2ecf20Sopenharmony_ci ATA_BMDMA_SHT(DRV_NAME), 2578c2ecf20Sopenharmony_ci .sg_tablesize = LIBATA_DUMB_MAX_PRD, 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic struct scsi_host_template serverworks_csb_sht = { 2618c2ecf20Sopenharmony_ci ATA_BMDMA_SHT(DRV_NAME), 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic struct ata_port_operations serverworks_osb4_port_ops = { 2658c2ecf20Sopenharmony_ci .inherits = &ata_bmdma_port_ops, 2668c2ecf20Sopenharmony_ci .qc_prep = ata_bmdma_dumb_qc_prep, 2678c2ecf20Sopenharmony_ci .cable_detect = serverworks_cable_detect, 2688c2ecf20Sopenharmony_ci .mode_filter = serverworks_osb4_filter, 2698c2ecf20Sopenharmony_ci .set_piomode = serverworks_set_piomode, 2708c2ecf20Sopenharmony_ci .set_dmamode = serverworks_set_dmamode, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic struct ata_port_operations serverworks_csb_port_ops = { 2748c2ecf20Sopenharmony_ci .inherits = &serverworks_osb4_port_ops, 2758c2ecf20Sopenharmony_ci .qc_prep = ata_bmdma_qc_prep, 2768c2ecf20Sopenharmony_ci .mode_filter = serverworks_csb_filter, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int serverworks_fixup_osb4(struct pci_dev *pdev) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci u32 reg; 2828c2ecf20Sopenharmony_ci struct pci_dev *isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 2838c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); 2848c2ecf20Sopenharmony_ci if (isa_dev) { 2858c2ecf20Sopenharmony_ci pci_read_config_dword(isa_dev, 0x64, ®); 2868c2ecf20Sopenharmony_ci reg &= ~0x00002000; /* disable 600ns interrupt mask */ 2878c2ecf20Sopenharmony_ci if (!(reg & 0x00004000)) 2888c2ecf20Sopenharmony_ci printk(KERN_DEBUG DRV_NAME ": UDMA not BIOS enabled.\n"); 2898c2ecf20Sopenharmony_ci reg |= 0x00004000; /* enable UDMA/33 support */ 2908c2ecf20Sopenharmony_ci pci_write_config_dword(isa_dev, 0x64, reg); 2918c2ecf20Sopenharmony_ci pci_dev_put(isa_dev); 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci printk(KERN_WARNING DRV_NAME ": Unable to find bridge.\n"); 2958c2ecf20Sopenharmony_ci return -ENODEV; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int serverworks_fixup_csb(struct pci_dev *pdev) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci u8 btr; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Third Channel Test */ 3038c2ecf20Sopenharmony_ci if (!(PCI_FUNC(pdev->devfn) & 1)) { 3048c2ecf20Sopenharmony_ci struct pci_dev * findev = NULL; 3058c2ecf20Sopenharmony_ci u32 reg4c = 0; 3068c2ecf20Sopenharmony_ci findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 3078c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL); 3088c2ecf20Sopenharmony_ci if (findev) { 3098c2ecf20Sopenharmony_ci pci_read_config_dword(findev, 0x4C, ®4c); 3108c2ecf20Sopenharmony_ci reg4c &= ~0x000007FF; 3118c2ecf20Sopenharmony_ci reg4c |= 0x00000040; 3128c2ecf20Sopenharmony_ci reg4c |= 0x00000020; 3138c2ecf20Sopenharmony_ci pci_write_config_dword(findev, 0x4C, reg4c); 3148c2ecf20Sopenharmony_ci pci_dev_put(findev); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } else { 3178c2ecf20Sopenharmony_ci struct pci_dev * findev = NULL; 3188c2ecf20Sopenharmony_ci u8 reg41 = 0; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 3218c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL); 3228c2ecf20Sopenharmony_ci if (findev) { 3238c2ecf20Sopenharmony_ci pci_read_config_byte(findev, 0x41, ®41); 3248c2ecf20Sopenharmony_ci reg41 &= ~0x40; 3258c2ecf20Sopenharmony_ci pci_write_config_byte(findev, 0x41, reg41); 3268c2ecf20Sopenharmony_ci pci_dev_put(findev); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci /* setup the UDMA Control register 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci * 1. clear bit 6 to enable DMA 3328c2ecf20Sopenharmony_ci * 2. enable DMA modes with bits 0-1 3338c2ecf20Sopenharmony_ci * 00 : legacy 3348c2ecf20Sopenharmony_ci * 01 : udma2 3358c2ecf20Sopenharmony_ci * 10 : udma2/udma4 3368c2ecf20Sopenharmony_ci * 11 : udma2/udma4/udma5 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, 0x5A, &btr); 3398c2ecf20Sopenharmony_ci btr &= ~0x40; 3408c2ecf20Sopenharmony_ci if (!(PCI_FUNC(pdev->devfn) & 1)) 3418c2ecf20Sopenharmony_ci btr |= 0x2; 3428c2ecf20Sopenharmony_ci else 3438c2ecf20Sopenharmony_ci btr |= (pdev->revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2; 3448c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x5A, btr); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return btr; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void serverworks_fixup_ht1000(struct pci_dev *pdev) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci u8 btr; 3528c2ecf20Sopenharmony_ci /* Setup HT1000 SouthBridge Controller - Single Channel Only */ 3538c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, 0x5A, &btr); 3548c2ecf20Sopenharmony_ci btr &= ~0x40; 3558c2ecf20Sopenharmony_ci btr |= 0x3; 3568c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, 0x5A, btr); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int serverworks_fixup(struct pci_dev *pdev) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci int rc = 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Force master latency timer to 64 PCI clocks */ 3648c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci switch (pdev->device) { 3678c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE: 3688c2ecf20Sopenharmony_ci rc = serverworks_fixup_osb4(pdev); 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: 3718c2ecf20Sopenharmony_ci ata_pci_bmdma_clear_simplex(pdev); 3728c2ecf20Sopenharmony_ci fallthrough; 3738c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: 3748c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: 3758c2ecf20Sopenharmony_ci rc = serverworks_fixup_csb(pdev); 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: 3788c2ecf20Sopenharmony_ci serverworks_fixup_ht1000(pdev); 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return rc; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id *id) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci static const struct ata_port_info info[4] = { 3888c2ecf20Sopenharmony_ci { /* OSB4 */ 3898c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 3908c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 3918c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 3928c2ecf20Sopenharmony_ci .udma_mask = ATA_UDMA2, 3938c2ecf20Sopenharmony_ci .port_ops = &serverworks_osb4_port_ops 3948c2ecf20Sopenharmony_ci }, { /* OSB4 no UDMA */ 3958c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 3968c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 3978c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 3988c2ecf20Sopenharmony_ci /* No UDMA */ 3998c2ecf20Sopenharmony_ci .port_ops = &serverworks_osb4_port_ops 4008c2ecf20Sopenharmony_ci }, { /* CSB5 */ 4018c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 4028c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 4038c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 4048c2ecf20Sopenharmony_ci .udma_mask = ATA_UDMA4, 4058c2ecf20Sopenharmony_ci .port_ops = &serverworks_csb_port_ops 4068c2ecf20Sopenharmony_ci }, { /* CSB5 - later revisions*/ 4078c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 4088c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 4098c2ecf20Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 4108c2ecf20Sopenharmony_ci .udma_mask = ATA_UDMA5, 4118c2ecf20Sopenharmony_ci .port_ops = &serverworks_csb_port_ops 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci }; 4148c2ecf20Sopenharmony_ci const struct ata_port_info *ppi[] = { &info[id->driver_data], NULL }; 4158c2ecf20Sopenharmony_ci struct scsi_host_template *sht = &serverworks_csb_sht; 4168c2ecf20Sopenharmony_ci int rc; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci rc = pcim_enable_device(pdev); 4198c2ecf20Sopenharmony_ci if (rc) 4208c2ecf20Sopenharmony_ci return rc; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci rc = serverworks_fixup(pdev); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* OSB4 : South Bridge and IDE */ 4258c2ecf20Sopenharmony_ci if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { 4268c2ecf20Sopenharmony_ci /* Select non UDMA capable OSB4 if we can't do fixups */ 4278c2ecf20Sopenharmony_ci if (rc < 0) 4288c2ecf20Sopenharmony_ci ppi[0] = &info[1]; 4298c2ecf20Sopenharmony_ci sht = &serverworks_osb4_sht; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci /* setup CSB5/CSB6 : South Bridge and IDE option RAID */ 4328c2ecf20Sopenharmony_ci else if ((pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) || 4338c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || 4348c2ecf20Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) { 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* If the returned btr is the newer revision then 4378c2ecf20Sopenharmony_ci select the right info block */ 4388c2ecf20Sopenharmony_ci if (rc == 3) 4398c2ecf20Sopenharmony_ci ppi[0] = &info[3]; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Is this the 3rd channel CSB6 IDE ? */ 4428c2ecf20Sopenharmony_ci if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2) 4438c2ecf20Sopenharmony_ci ppi[1] = &ata_dummy_port_info; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return ata_pci_bmdma_init_one(pdev, ppi, sht, NULL, 0); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4508c2ecf20Sopenharmony_cistatic int serverworks_reinit_one(struct pci_dev *pdev) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct ata_host *host = pci_get_drvdata(pdev); 4538c2ecf20Sopenharmony_ci int rc; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci rc = ata_pci_device_do_resume(pdev); 4568c2ecf20Sopenharmony_ci if (rc) 4578c2ecf20Sopenharmony_ci return rc; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci (void)serverworks_fixup(pdev); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ata_host_resume(host); 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci#endif 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic const struct pci_device_id serverworks[] = { 4678c2ecf20Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, 4688c2ecf20Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2}, 4698c2ecf20Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2}, 4708c2ecf20Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2}, 4718c2ecf20Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2}, 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci { }, 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic struct pci_driver serverworks_pci_driver = { 4778c2ecf20Sopenharmony_ci .name = DRV_NAME, 4788c2ecf20Sopenharmony_ci .id_table = serverworks, 4798c2ecf20Sopenharmony_ci .probe = serverworks_init_one, 4808c2ecf20Sopenharmony_ci .remove = ata_pci_remove_one, 4818c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4828c2ecf20Sopenharmony_ci .suspend = ata_pci_device_suspend, 4838c2ecf20Sopenharmony_ci .resume = serverworks_reinit_one, 4848c2ecf20Sopenharmony_ci#endif 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cimodule_pci_driver(serverworks_pci_driver); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alan Cox"); 4908c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6"); 4918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, serverworks); 4938c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 494