162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pata_serverworks.c - Serverworks PATA for new ATA layer 462306a36Sopenharmony_ci * (C) 2005 Red Hat Inc 562306a36Sopenharmony_ci * (C) 2010 Bartlomiej Zolnierkiewicz 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * based upon 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * serverworks.c 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright (C) 1998-2000 Michel Aubry 1262306a36Sopenharmony_ci * Copyright (C) 1998-2000 Andrzej Krzysztofowicz 1362306a36Sopenharmony_ci * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> 1462306a36Sopenharmony_ci * Portions copyright (c) 2001 Sun Microsystems 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * RCC/ServerWorks IDE driver for Linux 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * OSB4: `Open South Bridge' IDE Interface (fn 1) 2062306a36Sopenharmony_ci * supports UDMA mode 2 (33 MB/s) 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * CSB5: `Champion South Bridge' IDE Interface (fn 1) 2362306a36Sopenharmony_ci * all revisions support UDMA mode 4 (66 MB/s) 2462306a36Sopenharmony_ci * revision A2.0 and up support UDMA mode 5 (100 MB/s) 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * *** The CSB5 does not provide ANY register *** 2762306a36Sopenharmony_ci * *** to detect 80-conductor cable presence. *** 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * CSB6: `Champion South Bridge' IDE Interface (optional: third channel) 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Documentation: 3262306a36Sopenharmony_ci * Available under NDA only. Errata info very hard to get. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/kernel.h> 3662306a36Sopenharmony_ci#include <linux/module.h> 3762306a36Sopenharmony_ci#include <linux/pci.h> 3862306a36Sopenharmony_ci#include <linux/blkdev.h> 3962306a36Sopenharmony_ci#include <linux/delay.h> 4062306a36Sopenharmony_ci#include <scsi/scsi_host.h> 4162306a36Sopenharmony_ci#include <linux/libata.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define DRV_NAME "pata_serverworks" 4462306a36Sopenharmony_ci#define DRV_VERSION "0.4.3" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ 4762306a36Sopenharmony_ci#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Seagate Barracuda ATA IV Family drives in UDMA mode 5 5062306a36Sopenharmony_ci * can overrun their FIFOs when used with the CSB5 */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic const char *csb_bad_ata100[] = { 5362306a36Sopenharmony_ci "ST320011A", 5462306a36Sopenharmony_ci "ST340016A", 5562306a36Sopenharmony_ci "ST360021A", 5662306a36Sopenharmony_ci "ST380021A", 5762306a36Sopenharmony_ci NULL 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/** 6162306a36Sopenharmony_ci * oem_cable - Dell/Sun serverworks cable detection 6262306a36Sopenharmony_ci * @ap: ATA port to do cable detect 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * Dell PowerEdge and Sun Cobalt 'Alpine' hide the 40/80 pin select 6562306a36Sopenharmony_ci * for their interfaces in the top two bits of the subsystem ID. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int oem_cable(struct ata_port *ap) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (pdev->subsystem_device & (1 << (ap->port_no + 14))) 7362306a36Sopenharmony_ci return ATA_CBL_PATA80; 7462306a36Sopenharmony_ci return ATA_CBL_PATA40; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct sv_cable_table { 7862306a36Sopenharmony_ci int device; 7962306a36Sopenharmony_ci int subvendor; 8062306a36Sopenharmony_ci int (*cable_detect)(struct ata_port *ap); 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic struct sv_cable_table cable_detect[] = { 8462306a36Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, oem_cable }, 8562306a36Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, oem_cable }, 8662306a36Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN, oem_cable }, 8762306a36Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, ata_cable_40wire }, 8862306a36Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, ata_cable_unknown }, 8962306a36Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, ata_cable_unknown }, 9062306a36Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, ata_cable_unknown }, 9162306a36Sopenharmony_ci { PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, ata_cable_unknown }, 9262306a36Sopenharmony_ci { } 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/** 9662306a36Sopenharmony_ci * serverworks_cable_detect - cable detection 9762306a36Sopenharmony_ci * @ap: ATA port 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Perform cable detection according to the device and subvendor 10062306a36Sopenharmony_ci * identifications 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int serverworks_cable_detect(struct ata_port *ap) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 10662306a36Sopenharmony_ci struct sv_cable_table *cb = cable_detect; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci while(cb->device) { 10962306a36Sopenharmony_ci if (cb->device == pdev->device && 11062306a36Sopenharmony_ci (cb->subvendor == pdev->subsystem_vendor || 11162306a36Sopenharmony_ci cb->subvendor == PCI_ANY_ID)) { 11262306a36Sopenharmony_ci return cb->cable_detect(ap); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci cb++; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci BUG(); 11862306a36Sopenharmony_ci return -1; /* kill compiler warning */ 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/** 12262306a36Sopenharmony_ci * serverworks_is_csb - Check for CSB or OSB 12362306a36Sopenharmony_ci * @pdev: PCI device to check 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Returns true if the device being checked is known to be a CSB 12662306a36Sopenharmony_ci * series device. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic u8 serverworks_is_csb(struct pci_dev *pdev) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci switch (pdev->device) { 13262306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: 13362306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: 13462306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: 13562306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: 13662306a36Sopenharmony_ci return 1; 13762306a36Sopenharmony_ci default: 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/** 14462306a36Sopenharmony_ci * serverworks_osb4_filter - mode selection filter 14562306a36Sopenharmony_ci * @adev: ATA device 14662306a36Sopenharmony_ci * @mask: Mask of proposed modes 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * Filter the offered modes for the device to apply controller 14962306a36Sopenharmony_ci * specific rules. OSB4 requires no UDMA for disks due to a FIFO 15062306a36Sopenharmony_ci * bug we hit. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic unsigned int serverworks_osb4_filter(struct ata_device *adev, unsigned int mask) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci if (adev->class == ATA_DEV_ATA) 15662306a36Sopenharmony_ci mask &= ~ATA_MASK_UDMA; 15762306a36Sopenharmony_ci return mask; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * serverworks_csb_filter - mode selection filter 16362306a36Sopenharmony_ci * @adev: ATA device 16462306a36Sopenharmony_ci * @mask: Mask of proposed modes 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Check the blacklist and disable UDMA5 if matched 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic unsigned int serverworks_csb_filter(struct ata_device *adev, unsigned int mask) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci const char *p; 17262306a36Sopenharmony_ci char model_num[ATA_ID_PROD_LEN + 1]; 17362306a36Sopenharmony_ci int i; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Disk, UDMA */ 17662306a36Sopenharmony_ci if (adev->class != ATA_DEV_ATA) 17762306a36Sopenharmony_ci return mask; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Actually do need to check */ 18062306a36Sopenharmony_ci ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for (i = 0; (p = csb_bad_ata100[i]) != NULL; i++) { 18362306a36Sopenharmony_ci if (!strcmp(p, model_num)) 18462306a36Sopenharmony_ci mask &= ~(0xE0 << ATA_SHIFT_UDMA); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci return mask; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/** 19062306a36Sopenharmony_ci * serverworks_set_piomode - set initial PIO mode data 19162306a36Sopenharmony_ci * @ap: ATA interface 19262306a36Sopenharmony_ci * @adev: ATA device 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Program the OSB4/CSB5 timing registers for PIO. The PIO register 19562306a36Sopenharmony_ci * load is done as a simple lookup. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic void serverworks_set_piomode(struct ata_port *ap, struct ata_device *adev) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci static const u8 pio_mode[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 }; 20062306a36Sopenharmony_ci int offset = 1 + 2 * ap->port_no - adev->devno; 20162306a36Sopenharmony_ci int devbits = (2 * ap->port_no + adev->devno) * 4; 20262306a36Sopenharmony_ci u16 csb5_pio; 20362306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 20462306a36Sopenharmony_ci int pio = adev->pio_mode - XFER_PIO_0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci pci_write_config_byte(pdev, 0x40 + offset, pio_mode[pio]); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* The OSB4 just requires the timing but the CSB series want the 20962306a36Sopenharmony_ci mode number as well */ 21062306a36Sopenharmony_ci if (serverworks_is_csb(pdev)) { 21162306a36Sopenharmony_ci pci_read_config_word(pdev, 0x4A, &csb5_pio); 21262306a36Sopenharmony_ci csb5_pio &= ~(0x0F << devbits); 21362306a36Sopenharmony_ci pci_write_config_word(pdev, 0x4A, csb5_pio | (pio << devbits)); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/** 21862306a36Sopenharmony_ci * serverworks_set_dmamode - set initial DMA mode data 21962306a36Sopenharmony_ci * @ap: ATA interface 22062306a36Sopenharmony_ci * @adev: ATA device 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * Program the MWDMA/UDMA modes for the serverworks OSB4/CSB5 22362306a36Sopenharmony_ci * chipset. The MWDMA mode values are pulled from a lookup table 22462306a36Sopenharmony_ci * while the chipset uses mode number for UDMA. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci static const u8 dma_mode[] = { 0x77, 0x21, 0x20 }; 23062306a36Sopenharmony_ci int offset = 1 + 2 * ap->port_no - adev->devno; 23162306a36Sopenharmony_ci int devbits = 2 * ap->port_no + adev->devno; 23262306a36Sopenharmony_ci u8 ultra; 23362306a36Sopenharmony_ci u8 ultra_cfg; 23462306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ap->host->dev); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci pci_read_config_byte(pdev, 0x54, &ultra_cfg); 23762306a36Sopenharmony_ci pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra); 23862306a36Sopenharmony_ci ultra &= ~(0x0F << (adev->devno * 4)); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (adev->dma_mode >= XFER_UDMA_0) { 24162306a36Sopenharmony_ci pci_write_config_byte(pdev, 0x44 + offset, 0x20); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ultra |= (adev->dma_mode - XFER_UDMA_0) 24462306a36Sopenharmony_ci << (adev->devno * 4); 24562306a36Sopenharmony_ci ultra_cfg |= (1 << devbits); 24662306a36Sopenharmony_ci } else { 24762306a36Sopenharmony_ci pci_write_config_byte(pdev, 0x44 + offset, 24862306a36Sopenharmony_ci dma_mode[adev->dma_mode - XFER_MW_DMA_0]); 24962306a36Sopenharmony_ci ultra_cfg &= ~(1 << devbits); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra); 25262306a36Sopenharmony_ci pci_write_config_byte(pdev, 0x54, ultra_cfg); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic const struct scsi_host_template serverworks_osb4_sht = { 25662306a36Sopenharmony_ci ATA_BASE_SHT(DRV_NAME), 25762306a36Sopenharmony_ci .sg_tablesize = LIBATA_DUMB_MAX_PRD, 25862306a36Sopenharmony_ci .dma_boundary = ATA_DMA_BOUNDARY, 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic const struct scsi_host_template serverworks_csb_sht = { 26262306a36Sopenharmony_ci ATA_BMDMA_SHT(DRV_NAME), 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic struct ata_port_operations serverworks_osb4_port_ops = { 26662306a36Sopenharmony_ci .inherits = &ata_bmdma_port_ops, 26762306a36Sopenharmony_ci .qc_prep = ata_bmdma_dumb_qc_prep, 26862306a36Sopenharmony_ci .cable_detect = serverworks_cable_detect, 26962306a36Sopenharmony_ci .mode_filter = serverworks_osb4_filter, 27062306a36Sopenharmony_ci .set_piomode = serverworks_set_piomode, 27162306a36Sopenharmony_ci .set_dmamode = serverworks_set_dmamode, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic struct ata_port_operations serverworks_csb_port_ops = { 27562306a36Sopenharmony_ci .inherits = &serverworks_osb4_port_ops, 27662306a36Sopenharmony_ci .qc_prep = ata_bmdma_qc_prep, 27762306a36Sopenharmony_ci .mode_filter = serverworks_csb_filter, 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int serverworks_fixup_osb4(struct pci_dev *pdev) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci u32 reg; 28362306a36Sopenharmony_ci struct pci_dev *isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 28462306a36Sopenharmony_ci PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); 28562306a36Sopenharmony_ci if (isa_dev) { 28662306a36Sopenharmony_ci pci_read_config_dword(isa_dev, 0x64, ®); 28762306a36Sopenharmony_ci reg &= ~0x00002000; /* disable 600ns interrupt mask */ 28862306a36Sopenharmony_ci if (!(reg & 0x00004000)) 28962306a36Sopenharmony_ci dev_info(&pdev->dev, "UDMA not BIOS enabled.\n"); 29062306a36Sopenharmony_ci reg |= 0x00004000; /* enable UDMA/33 support */ 29162306a36Sopenharmony_ci pci_write_config_dword(isa_dev, 0x64, reg); 29262306a36Sopenharmony_ci pci_dev_put(isa_dev); 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci dev_warn(&pdev->dev, "Unable to find bridge.\n"); 29662306a36Sopenharmony_ci return -ENODEV; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int serverworks_fixup_csb(struct pci_dev *pdev) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci u8 btr; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Third Channel Test */ 30462306a36Sopenharmony_ci if (!(PCI_FUNC(pdev->devfn) & 1)) { 30562306a36Sopenharmony_ci struct pci_dev * findev = NULL; 30662306a36Sopenharmony_ci u32 reg4c = 0; 30762306a36Sopenharmony_ci findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 30862306a36Sopenharmony_ci PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL); 30962306a36Sopenharmony_ci if (findev) { 31062306a36Sopenharmony_ci pci_read_config_dword(findev, 0x4C, ®4c); 31162306a36Sopenharmony_ci reg4c &= ~0x000007FF; 31262306a36Sopenharmony_ci reg4c |= 0x00000040; 31362306a36Sopenharmony_ci reg4c |= 0x00000020; 31462306a36Sopenharmony_ci pci_write_config_dword(findev, 0x4C, reg4c); 31562306a36Sopenharmony_ci pci_dev_put(findev); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci struct pci_dev * findev = NULL; 31962306a36Sopenharmony_ci u8 reg41 = 0; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 32262306a36Sopenharmony_ci PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL); 32362306a36Sopenharmony_ci if (findev) { 32462306a36Sopenharmony_ci pci_read_config_byte(findev, 0x41, ®41); 32562306a36Sopenharmony_ci reg41 &= ~0x40; 32662306a36Sopenharmony_ci pci_write_config_byte(findev, 0x41, reg41); 32762306a36Sopenharmony_ci pci_dev_put(findev); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci /* setup the UDMA Control register 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * 1. clear bit 6 to enable DMA 33362306a36Sopenharmony_ci * 2. enable DMA modes with bits 0-1 33462306a36Sopenharmony_ci * 00 : legacy 33562306a36Sopenharmony_ci * 01 : udma2 33662306a36Sopenharmony_ci * 10 : udma2/udma4 33762306a36Sopenharmony_ci * 11 : udma2/udma4/udma5 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci pci_read_config_byte(pdev, 0x5A, &btr); 34062306a36Sopenharmony_ci btr &= ~0x40; 34162306a36Sopenharmony_ci if (!(PCI_FUNC(pdev->devfn) & 1)) 34262306a36Sopenharmony_ci btr |= 0x2; 34362306a36Sopenharmony_ci else 34462306a36Sopenharmony_ci btr |= (pdev->revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2; 34562306a36Sopenharmony_ci pci_write_config_byte(pdev, 0x5A, btr); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return btr; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void serverworks_fixup_ht1000(struct pci_dev *pdev) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci u8 btr; 35362306a36Sopenharmony_ci /* Setup HT1000 SouthBridge Controller - Single Channel Only */ 35462306a36Sopenharmony_ci pci_read_config_byte(pdev, 0x5A, &btr); 35562306a36Sopenharmony_ci btr &= ~0x40; 35662306a36Sopenharmony_ci btr |= 0x3; 35762306a36Sopenharmony_ci pci_write_config_byte(pdev, 0x5A, btr); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int serverworks_fixup(struct pci_dev *pdev) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci int rc = 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Force master latency timer to 64 PCI clocks */ 36562306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci switch (pdev->device) { 36862306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE: 36962306a36Sopenharmony_ci rc = serverworks_fixup_osb4(pdev); 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: 37262306a36Sopenharmony_ci ata_pci_bmdma_clear_simplex(pdev); 37362306a36Sopenharmony_ci fallthrough; 37462306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: 37562306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: 37662306a36Sopenharmony_ci rc = serverworks_fixup_csb(pdev); 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: 37962306a36Sopenharmony_ci serverworks_fixup_ht1000(pdev); 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return rc; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id *id) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci static const struct ata_port_info info[4] = { 38962306a36Sopenharmony_ci { /* OSB4 */ 39062306a36Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 39162306a36Sopenharmony_ci .pio_mask = ATA_PIO4, 39262306a36Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 39362306a36Sopenharmony_ci .udma_mask = ATA_UDMA2, 39462306a36Sopenharmony_ci .port_ops = &serverworks_osb4_port_ops 39562306a36Sopenharmony_ci }, { /* OSB4 no UDMA */ 39662306a36Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 39762306a36Sopenharmony_ci .pio_mask = ATA_PIO4, 39862306a36Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 39962306a36Sopenharmony_ci /* No UDMA */ 40062306a36Sopenharmony_ci .port_ops = &serverworks_osb4_port_ops 40162306a36Sopenharmony_ci }, { /* CSB5 */ 40262306a36Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 40362306a36Sopenharmony_ci .pio_mask = ATA_PIO4, 40462306a36Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 40562306a36Sopenharmony_ci .udma_mask = ATA_UDMA4, 40662306a36Sopenharmony_ci .port_ops = &serverworks_csb_port_ops 40762306a36Sopenharmony_ci }, { /* CSB5 - later revisions*/ 40862306a36Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS, 40962306a36Sopenharmony_ci .pio_mask = ATA_PIO4, 41062306a36Sopenharmony_ci .mwdma_mask = ATA_MWDMA2, 41162306a36Sopenharmony_ci .udma_mask = ATA_UDMA5, 41262306a36Sopenharmony_ci .port_ops = &serverworks_csb_port_ops 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci }; 41562306a36Sopenharmony_ci const struct ata_port_info *ppi[] = { &info[id->driver_data], NULL }; 41662306a36Sopenharmony_ci const struct scsi_host_template *sht = &serverworks_csb_sht; 41762306a36Sopenharmony_ci int rc; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci rc = pcim_enable_device(pdev); 42062306a36Sopenharmony_ci if (rc) 42162306a36Sopenharmony_ci return rc; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci rc = serverworks_fixup(pdev); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* OSB4 : South Bridge and IDE */ 42662306a36Sopenharmony_ci if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { 42762306a36Sopenharmony_ci /* Select non UDMA capable OSB4 if we can't do fixups */ 42862306a36Sopenharmony_ci if (rc < 0) 42962306a36Sopenharmony_ci ppi[0] = &info[1]; 43062306a36Sopenharmony_ci sht = &serverworks_osb4_sht; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci /* setup CSB5/CSB6 : South Bridge and IDE option RAID */ 43362306a36Sopenharmony_ci else if ((pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) || 43462306a36Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || 43562306a36Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) { 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* If the returned btr is the newer revision then 43862306a36Sopenharmony_ci select the right info block */ 43962306a36Sopenharmony_ci if (rc == 3) 44062306a36Sopenharmony_ci ppi[0] = &info[3]; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Is this the 3rd channel CSB6 IDE ? */ 44362306a36Sopenharmony_ci if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2) 44462306a36Sopenharmony_ci ppi[1] = &ata_dummy_port_info; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return ata_pci_bmdma_init_one(pdev, ppi, sht, NULL, 0); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 45162306a36Sopenharmony_cistatic int serverworks_reinit_one(struct pci_dev *pdev) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct ata_host *host = pci_get_drvdata(pdev); 45462306a36Sopenharmony_ci int rc; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci rc = ata_pci_device_do_resume(pdev); 45762306a36Sopenharmony_ci if (rc) 45862306a36Sopenharmony_ci return rc; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci (void)serverworks_fixup(pdev); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ata_host_resume(host); 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci#endif 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic const struct pci_device_id serverworks[] = { 46862306a36Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, 46962306a36Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2}, 47062306a36Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2}, 47162306a36Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2}, 47262306a36Sopenharmony_ci { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2}, 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci { }, 47562306a36Sopenharmony_ci}; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic struct pci_driver serverworks_pci_driver = { 47862306a36Sopenharmony_ci .name = DRV_NAME, 47962306a36Sopenharmony_ci .id_table = serverworks, 48062306a36Sopenharmony_ci .probe = serverworks_init_one, 48162306a36Sopenharmony_ci .remove = ata_pci_remove_one, 48262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 48362306a36Sopenharmony_ci .suspend = ata_pci_device_suspend, 48462306a36Sopenharmony_ci .resume = serverworks_reinit_one, 48562306a36Sopenharmony_ci#endif 48662306a36Sopenharmony_ci}; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cimodule_pci_driver(serverworks_pci_driver); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciMODULE_AUTHOR("Alan Cox"); 49162306a36Sopenharmony_ciMODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6"); 49262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 49362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, serverworks); 49462306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 495