1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2669a5db4SJeff Garzik /* 3a0fcdc02SJeff Garzik * pata_serverworks.c - Serverworks PATA for new ATA layer 4669a5db4SJeff Garzik * (C) 2005 Red Hat Inc 58490377aSBartlomiej Zolnierkiewicz * (C) 2010 Bartlomiej Zolnierkiewicz 6669a5db4SJeff Garzik * 7669a5db4SJeff Garzik * based upon 8669a5db4SJeff Garzik * 9669a5db4SJeff Garzik * serverworks.c 10669a5db4SJeff Garzik * 11669a5db4SJeff Garzik * Copyright (C) 1998-2000 Michel Aubry 12669a5db4SJeff Garzik * Copyright (C) 1998-2000 Andrzej Krzysztofowicz 13669a5db4SJeff Garzik * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> 14669a5db4SJeff Garzik * Portions copyright (c) 2001 Sun Microsystems 15669a5db4SJeff Garzik * 16669a5db4SJeff Garzik * 17669a5db4SJeff Garzik * RCC/ServerWorks IDE driver for Linux 18669a5db4SJeff Garzik * 19669a5db4SJeff Garzik * OSB4: `Open South Bridge' IDE Interface (fn 1) 20669a5db4SJeff Garzik * supports UDMA mode 2 (33 MB/s) 21669a5db4SJeff Garzik * 22669a5db4SJeff Garzik * CSB5: `Champion South Bridge' IDE Interface (fn 1) 23669a5db4SJeff Garzik * all revisions support UDMA mode 4 (66 MB/s) 24669a5db4SJeff Garzik * revision A2.0 and up support UDMA mode 5 (100 MB/s) 25669a5db4SJeff Garzik * 26669a5db4SJeff Garzik * *** The CSB5 does not provide ANY register *** 27669a5db4SJeff Garzik * *** to detect 80-conductor cable presence. *** 28669a5db4SJeff Garzik * 29669a5db4SJeff Garzik * CSB6: `Champion South Bridge' IDE Interface (optional: third channel) 30669a5db4SJeff Garzik * 31669a5db4SJeff Garzik * Documentation: 32669a5db4SJeff Garzik * Available under NDA only. Errata info very hard to get. 33669a5db4SJeff Garzik */ 34669a5db4SJeff Garzik 35669a5db4SJeff Garzik #include <linux/kernel.h> 36669a5db4SJeff Garzik #include <linux/module.h> 37669a5db4SJeff Garzik #include <linux/pci.h> 38669a5db4SJeff Garzik #include <linux/blkdev.h> 39669a5db4SJeff Garzik #include <linux/delay.h> 40669a5db4SJeff Garzik #include <scsi/scsi_host.h> 41669a5db4SJeff Garzik #include <linux/libata.h> 42669a5db4SJeff Garzik 43669a5db4SJeff Garzik #define DRV_NAME "pata_serverworks" 440f069788SAlan Cox #define DRV_VERSION "0.4.3" 45669a5db4SJeff Garzik 46669a5db4SJeff Garzik #define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ 47669a5db4SJeff Garzik #define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */ 48669a5db4SJeff Garzik 49669a5db4SJeff Garzik /* Seagate Barracuda ATA IV Family drives in UDMA mode 5 50669a5db4SJeff Garzik * can overrun their FIFOs when used with the CSB5 */ 51669a5db4SJeff Garzik 52669a5db4SJeff Garzik static const char *csb_bad_ata100[] = { 53669a5db4SJeff Garzik "ST320011A", 54669a5db4SJeff Garzik "ST340016A", 55669a5db4SJeff Garzik "ST360021A", 56669a5db4SJeff Garzik "ST380021A", 57669a5db4SJeff Garzik NULL 58669a5db4SJeff Garzik }; 59669a5db4SJeff Garzik 60669a5db4SJeff Garzik /** 61e69a70d9SBartlomiej Zolnierkiewicz * oem_cable - Dell/Sun serverworks cable detection 62669a5db4SJeff Garzik * @ap: ATA port to do cable detect 63669a5db4SJeff Garzik * 64e69a70d9SBartlomiej Zolnierkiewicz * Dell PowerEdge and Sun Cobalt 'Alpine' hide the 40/80 pin select 65e69a70d9SBartlomiej Zolnierkiewicz * for their interfaces in the top two bits of the subsystem ID. 66669a5db4SJeff Garzik */ 67669a5db4SJeff Garzik 68e69a70d9SBartlomiej Zolnierkiewicz static int oem_cable(struct ata_port *ap) 695860a554SBartlomiej Zolnierkiewicz { 70669a5db4SJeff Garzik struct pci_dev *pdev = to_pci_dev(ap->host->dev); 71669a5db4SJeff Garzik 72669a5db4SJeff Garzik if (pdev->subsystem_device & (1 << (ap->port_no + 14))) 73669a5db4SJeff Garzik return ATA_CBL_PATA80; 74669a5db4SJeff Garzik return ATA_CBL_PATA40; 75669a5db4SJeff Garzik } 76669a5db4SJeff Garzik 77669a5db4SJeff Garzik struct sv_cable_table { 78669a5db4SJeff Garzik int device; 79669a5db4SJeff Garzik int subvendor; 80669a5db4SJeff Garzik int (*cable_detect)(struct ata_port *ap); 81669a5db4SJeff Garzik }; 82669a5db4SJeff Garzik 83669a5db4SJeff Garzik static struct sv_cable_table cable_detect[] = { 84e69a70d9SBartlomiej Zolnierkiewicz { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, oem_cable }, 85e69a70d9SBartlomiej Zolnierkiewicz { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, oem_cable }, 86e69a70d9SBartlomiej Zolnierkiewicz { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN, oem_cable }, 875860a554SBartlomiej Zolnierkiewicz { PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, ata_cable_40wire }, 885860a554SBartlomiej Zolnierkiewicz { PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, ata_cable_unknown }, 895860a554SBartlomiej Zolnierkiewicz { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, ata_cable_unknown }, 905860a554SBartlomiej Zolnierkiewicz { PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, ata_cable_unknown }, 915860a554SBartlomiej Zolnierkiewicz { PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, ata_cable_unknown }, 92669a5db4SJeff Garzik { } 93669a5db4SJeff Garzik }; 94669a5db4SJeff Garzik 95669a5db4SJeff Garzik /** 96a0fcdc02SJeff Garzik * serverworks_cable_detect - cable detection 97669a5db4SJeff Garzik * @ap: ATA port 98669a5db4SJeff Garzik * 99669a5db4SJeff Garzik * Perform cable detection according to the device and subvendor 100669a5db4SJeff Garzik * identifications 101669a5db4SJeff Garzik */ 102669a5db4SJeff Garzik 103d4b2bab4STejun Heo static int serverworks_cable_detect(struct ata_port *ap) 104d4b2bab4STejun Heo { 105669a5db4SJeff Garzik struct pci_dev *pdev = to_pci_dev(ap->host->dev); 106669a5db4SJeff Garzik struct sv_cable_table *cb = cable_detect; 107669a5db4SJeff Garzik 108669a5db4SJeff Garzik while(cb->device) { 109669a5db4SJeff Garzik if (cb->device == pdev->device && 110669a5db4SJeff Garzik (cb->subvendor == pdev->subsystem_vendor || 111669a5db4SJeff Garzik cb->subvendor == PCI_ANY_ID)) { 112a0fcdc02SJeff Garzik return cb->cable_detect(ap); 113669a5db4SJeff Garzik } 114669a5db4SJeff Garzik cb++; 115669a5db4SJeff Garzik } 116669a5db4SJeff Garzik 117669a5db4SJeff Garzik BUG(); 118669a5db4SJeff Garzik return -1; /* kill compiler warning */ 119669a5db4SJeff Garzik } 120669a5db4SJeff Garzik 121669a5db4SJeff Garzik /** 122669a5db4SJeff Garzik * serverworks_is_csb - Check for CSB or OSB 123669a5db4SJeff Garzik * @pdev: PCI device to check 124669a5db4SJeff Garzik * 125669a5db4SJeff Garzik * Returns true if the device being checked is known to be a CSB 126669a5db4SJeff Garzik * series device. 127669a5db4SJeff Garzik */ 128669a5db4SJeff Garzik 129669a5db4SJeff Garzik static u8 serverworks_is_csb(struct pci_dev *pdev) 130669a5db4SJeff Garzik { 131669a5db4SJeff Garzik switch (pdev->device) { 132669a5db4SJeff Garzik case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: 133669a5db4SJeff Garzik case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: 134669a5db4SJeff Garzik case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: 135669a5db4SJeff Garzik case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: 136669a5db4SJeff Garzik return 1; 137669a5db4SJeff Garzik default: 138669a5db4SJeff Garzik break; 139669a5db4SJeff Garzik } 140669a5db4SJeff Garzik return 0; 141669a5db4SJeff Garzik } 142669a5db4SJeff Garzik 143669a5db4SJeff Garzik /** 144669a5db4SJeff Garzik * serverworks_osb4_filter - mode selection filter 145669a5db4SJeff Garzik * @adev: ATA device 146a76b62caSAlan Cox * @mask: Mask of proposed modes 147669a5db4SJeff Garzik * 148669a5db4SJeff Garzik * Filter the offered modes for the device to apply controller 149669a5db4SJeff Garzik * specific rules. OSB4 requires no UDMA for disks due to a FIFO 150669a5db4SJeff Garzik * bug we hit. 151669a5db4SJeff Garzik */ 152669a5db4SJeff Garzik 153a76b62caSAlan Cox static unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask) 154669a5db4SJeff Garzik { 155669a5db4SJeff Garzik if (adev->class == ATA_DEV_ATA) 156669a5db4SJeff Garzik mask &= ~ATA_MASK_UDMA; 157c7087652STejun Heo return mask; 158669a5db4SJeff Garzik } 159669a5db4SJeff Garzik 160669a5db4SJeff Garzik 161669a5db4SJeff Garzik /** 162669a5db4SJeff Garzik * serverworks_csb_filter - mode selection filter 163669a5db4SJeff Garzik * @adev: ATA device 164a76b62caSAlan Cox * @mask: Mask of proposed modes 165669a5db4SJeff Garzik * 166669a5db4SJeff Garzik * Check the blacklist and disable UDMA5 if matched 167669a5db4SJeff Garzik */ 168669a5db4SJeff Garzik 169a76b62caSAlan Cox static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask) 170669a5db4SJeff Garzik { 171669a5db4SJeff Garzik const char *p; 1728bfa79fcSTejun Heo char model_num[ATA_ID_PROD_LEN + 1]; 1738bfa79fcSTejun Heo int i; 174669a5db4SJeff Garzik 175669a5db4SJeff Garzik /* Disk, UDMA */ 176669a5db4SJeff Garzik if (adev->class != ATA_DEV_ATA) 177c7087652STejun Heo return mask; 178669a5db4SJeff Garzik 179669a5db4SJeff Garzik /* Actually do need to check */ 1808bfa79fcSTejun Heo ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); 181669a5db4SJeff Garzik 182669a5db4SJeff Garzik for (i = 0; (p = csb_bad_ata100[i]) != NULL; i++) { 1838bfa79fcSTejun Heo if (!strcmp(p, model_num)) 1846ddd6861SAlan Cox mask &= ~(0xE0 << ATA_SHIFT_UDMA); 185669a5db4SJeff Garzik } 186c7087652STejun Heo return mask; 187669a5db4SJeff Garzik } 188669a5db4SJeff Garzik 189669a5db4SJeff Garzik /** 190669a5db4SJeff Garzik * serverworks_set_piomode - set initial PIO mode data 191669a5db4SJeff Garzik * @ap: ATA interface 192669a5db4SJeff Garzik * @adev: ATA device 193669a5db4SJeff Garzik * 194669a5db4SJeff Garzik * Program the OSB4/CSB5 timing registers for PIO. The PIO register 195669a5db4SJeff Garzik * load is done as a simple lookup. 196669a5db4SJeff Garzik */ 197669a5db4SJeff Garzik static void serverworks_set_piomode(struct ata_port *ap, struct ata_device *adev) 198669a5db4SJeff Garzik { 199669a5db4SJeff Garzik static const u8 pio_mode[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 }; 2000f069788SAlan Cox int offset = 1 + 2 * ap->port_no - adev->devno; 201669a5db4SJeff Garzik int devbits = (2 * ap->port_no + adev->devno) * 4; 202669a5db4SJeff Garzik u16 csb5_pio; 203669a5db4SJeff Garzik struct pci_dev *pdev = to_pci_dev(ap->host->dev); 204669a5db4SJeff Garzik int pio = adev->pio_mode - XFER_PIO_0; 205669a5db4SJeff Garzik 206669a5db4SJeff Garzik pci_write_config_byte(pdev, 0x40 + offset, pio_mode[pio]); 207669a5db4SJeff Garzik 208669a5db4SJeff Garzik /* The OSB4 just requires the timing but the CSB series want the 209669a5db4SJeff Garzik mode number as well */ 210669a5db4SJeff Garzik if (serverworks_is_csb(pdev)) { 211669a5db4SJeff Garzik pci_read_config_word(pdev, 0x4A, &csb5_pio); 212669a5db4SJeff Garzik csb5_pio &= ~(0x0F << devbits); 2138490377aSBartlomiej Zolnierkiewicz pci_write_config_word(pdev, 0x4A, csb5_pio | (pio << devbits)); 214669a5db4SJeff Garzik } 215669a5db4SJeff Garzik } 216669a5db4SJeff Garzik 217669a5db4SJeff Garzik /** 218669a5db4SJeff Garzik * serverworks_set_dmamode - set initial DMA mode data 219669a5db4SJeff Garzik * @ap: ATA interface 220669a5db4SJeff Garzik * @adev: ATA device 221669a5db4SJeff Garzik * 222669a5db4SJeff Garzik * Program the MWDMA/UDMA modes for the serverworks OSB4/CSB5 223669a5db4SJeff Garzik * chipset. The MWDMA mode values are pulled from a lookup table 224669a5db4SJeff Garzik * while the chipset uses mode number for UDMA. 225669a5db4SJeff Garzik */ 226669a5db4SJeff Garzik 227669a5db4SJeff Garzik static void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev) 228669a5db4SJeff Garzik { 229669a5db4SJeff Garzik static const u8 dma_mode[] = { 0x77, 0x21, 0x20 }; 230669a5db4SJeff Garzik int offset = 1 + 2 * ap->port_no - adev->devno; 23136beb823SAlan Cox int devbits = 2 * ap->port_no + adev->devno; 232669a5db4SJeff Garzik u8 ultra; 233669a5db4SJeff Garzik u8 ultra_cfg; 234669a5db4SJeff Garzik struct pci_dev *pdev = to_pci_dev(ap->host->dev); 235669a5db4SJeff Garzik 236669a5db4SJeff Garzik pci_read_config_byte(pdev, 0x54, &ultra_cfg); 23736beb823SAlan Cox pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra); 23836beb823SAlan Cox ultra &= ~(0x0F << (adev->devno * 4)); 239669a5db4SJeff Garzik 240669a5db4SJeff Garzik if (adev->dma_mode >= XFER_UDMA_0) { 241669a5db4SJeff Garzik pci_write_config_byte(pdev, 0x44 + offset, 0x20); 242669a5db4SJeff Garzik 243669a5db4SJeff Garzik ultra |= (adev->dma_mode - XFER_UDMA_0) 24436beb823SAlan Cox << (adev->devno * 4); 245669a5db4SJeff Garzik ultra_cfg |= (1 << devbits); 246669a5db4SJeff Garzik } else { 247669a5db4SJeff Garzik pci_write_config_byte(pdev, 0x44 + offset, 248669a5db4SJeff Garzik dma_mode[adev->dma_mode - XFER_MW_DMA_0]); 249669a5db4SJeff Garzik ultra_cfg &= ~(1 << devbits); 250669a5db4SJeff Garzik } 25136beb823SAlan Cox pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra); 252669a5db4SJeff Garzik pci_write_config_byte(pdev, 0x54, ultra_cfg); 253669a5db4SJeff Garzik } 254669a5db4SJeff Garzik 25537017ac6SScott Carter static struct scsi_host_template serverworks_osb4_sht = { 25637017ac6SScott Carter ATA_BMDMA_SHT(DRV_NAME), 25737017ac6SScott Carter .sg_tablesize = LIBATA_DUMB_MAX_PRD, 25837017ac6SScott Carter }; 25937017ac6SScott Carter 26037017ac6SScott Carter static struct scsi_host_template serverworks_csb_sht = { 26168d1d07bSTejun Heo ATA_BMDMA_SHT(DRV_NAME), 262669a5db4SJeff Garzik }; 263669a5db4SJeff Garzik 264669a5db4SJeff Garzik static struct ata_port_operations serverworks_osb4_port_ops = { 265029cfd6bSTejun Heo .inherits = &ata_bmdma_port_ops, 26637017ac6SScott Carter .qc_prep = ata_bmdma_dumb_qc_prep, 267029cfd6bSTejun Heo .cable_detect = serverworks_cable_detect, 268029cfd6bSTejun Heo .mode_filter = serverworks_osb4_filter, 269669a5db4SJeff Garzik .set_piomode = serverworks_set_piomode, 270669a5db4SJeff Garzik .set_dmamode = serverworks_set_dmamode, 271669a5db4SJeff Garzik }; 272669a5db4SJeff Garzik 273669a5db4SJeff Garzik static struct ata_port_operations serverworks_csb_port_ops = { 274029cfd6bSTejun Heo .inherits = &serverworks_osb4_port_ops, 27537017ac6SScott Carter .qc_prep = ata_bmdma_qc_prep, 276669a5db4SJeff Garzik .mode_filter = serverworks_csb_filter, 277669a5db4SJeff Garzik }; 278669a5db4SJeff Garzik 279669a5db4SJeff Garzik static int serverworks_fixup_osb4(struct pci_dev *pdev) 280669a5db4SJeff Garzik { 281669a5db4SJeff Garzik u32 reg; 282669a5db4SJeff Garzik struct pci_dev *isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 283669a5db4SJeff Garzik PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); 284669a5db4SJeff Garzik if (isa_dev) { 285669a5db4SJeff Garzik pci_read_config_dword(isa_dev, 0x64, ®); 286669a5db4SJeff Garzik reg &= ~0x00002000; /* disable 600ns interrupt mask */ 287669a5db4SJeff Garzik if (!(reg & 0x00004000)) 288669a5db4SJeff Garzik printk(KERN_DEBUG DRV_NAME ": UDMA not BIOS enabled.\n"); 289669a5db4SJeff Garzik reg |= 0x00004000; /* enable UDMA/33 support */ 290669a5db4SJeff Garzik pci_write_config_dword(isa_dev, 0x64, reg); 291669a5db4SJeff Garzik pci_dev_put(isa_dev); 292669a5db4SJeff Garzik return 0; 293669a5db4SJeff Garzik } 294cfcf9ee2SBartlomiej Zolnierkiewicz printk(KERN_WARNING DRV_NAME ": Unable to find bridge.\n"); 295669a5db4SJeff Garzik return -ENODEV; 296669a5db4SJeff Garzik } 297669a5db4SJeff Garzik 298669a5db4SJeff Garzik static int serverworks_fixup_csb(struct pci_dev *pdev) 299669a5db4SJeff Garzik { 300669a5db4SJeff Garzik u8 btr; 301669a5db4SJeff Garzik 302669a5db4SJeff Garzik /* Third Channel Test */ 303669a5db4SJeff Garzik if (!(PCI_FUNC(pdev->devfn) & 1)) { 304669a5db4SJeff Garzik struct pci_dev * findev = NULL; 305669a5db4SJeff Garzik u32 reg4c = 0; 306669a5db4SJeff Garzik findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 307669a5db4SJeff Garzik PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL); 308669a5db4SJeff Garzik if (findev) { 309669a5db4SJeff Garzik pci_read_config_dword(findev, 0x4C, ®4c); 310669a5db4SJeff Garzik reg4c &= ~0x000007FF; 311669a5db4SJeff Garzik reg4c |= 0x00000040; 312669a5db4SJeff Garzik reg4c |= 0x00000020; 313669a5db4SJeff Garzik pci_write_config_dword(findev, 0x4C, reg4c); 314669a5db4SJeff Garzik pci_dev_put(findev); 315669a5db4SJeff Garzik } 316669a5db4SJeff Garzik } else { 317669a5db4SJeff Garzik struct pci_dev * findev = NULL; 318669a5db4SJeff Garzik u8 reg41 = 0; 319669a5db4SJeff Garzik 320669a5db4SJeff Garzik findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS, 321669a5db4SJeff Garzik PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL); 322669a5db4SJeff Garzik if (findev) { 323669a5db4SJeff Garzik pci_read_config_byte(findev, 0x41, ®41); 324669a5db4SJeff Garzik reg41 &= ~0x40; 325669a5db4SJeff Garzik pci_write_config_byte(findev, 0x41, reg41); 326669a5db4SJeff Garzik pci_dev_put(findev); 327669a5db4SJeff Garzik } 328669a5db4SJeff Garzik } 329669a5db4SJeff Garzik /* setup the UDMA Control register 330669a5db4SJeff Garzik * 331669a5db4SJeff Garzik * 1. clear bit 6 to enable DMA 332669a5db4SJeff Garzik * 2. enable DMA modes with bits 0-1 333669a5db4SJeff Garzik * 00 : legacy 334669a5db4SJeff Garzik * 01 : udma2 335669a5db4SJeff Garzik * 10 : udma2/udma4 336669a5db4SJeff Garzik * 11 : udma2/udma4/udma5 337669a5db4SJeff Garzik */ 338669a5db4SJeff Garzik pci_read_config_byte(pdev, 0x5A, &btr); 339669a5db4SJeff Garzik btr &= ~0x40; 340669a5db4SJeff Garzik if (!(PCI_FUNC(pdev->devfn) & 1)) 341669a5db4SJeff Garzik btr |= 0x2; 342669a5db4SJeff Garzik else 34344c10138SAuke Kok btr |= (pdev->revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2; 344669a5db4SJeff Garzik pci_write_config_byte(pdev, 0x5A, btr); 345669a5db4SJeff Garzik 346669a5db4SJeff Garzik return btr; 347669a5db4SJeff Garzik } 348669a5db4SJeff Garzik 349669a5db4SJeff Garzik static void serverworks_fixup_ht1000(struct pci_dev *pdev) 350669a5db4SJeff Garzik { 351669a5db4SJeff Garzik u8 btr; 352669a5db4SJeff Garzik /* Setup HT1000 SouthBridge Controller - Single Channel Only */ 353669a5db4SJeff Garzik pci_read_config_byte(pdev, 0x5A, &btr); 354669a5db4SJeff Garzik btr &= ~0x40; 355669a5db4SJeff Garzik btr |= 0x3; 356669a5db4SJeff Garzik pci_write_config_byte(pdev, 0x5A, btr); 357669a5db4SJeff Garzik } 358669a5db4SJeff Garzik 359d912be2fSBartlomiej Zolnierkiewicz static int serverworks_fixup(struct pci_dev *pdev) 360d912be2fSBartlomiej Zolnierkiewicz { 361d912be2fSBartlomiej Zolnierkiewicz int rc = 0; 362d912be2fSBartlomiej Zolnierkiewicz 363d912be2fSBartlomiej Zolnierkiewicz /* Force master latency timer to 64 PCI clocks */ 364d912be2fSBartlomiej Zolnierkiewicz pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40); 365d912be2fSBartlomiej Zolnierkiewicz 366d912be2fSBartlomiej Zolnierkiewicz switch (pdev->device) { 367d912be2fSBartlomiej Zolnierkiewicz case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE: 368d912be2fSBartlomiej Zolnierkiewicz rc = serverworks_fixup_osb4(pdev); 369d912be2fSBartlomiej Zolnierkiewicz break; 370d912be2fSBartlomiej Zolnierkiewicz case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: 371d912be2fSBartlomiej Zolnierkiewicz ata_pci_bmdma_clear_simplex(pdev); 372d912be2fSBartlomiej Zolnierkiewicz /* fall through */ 373d912be2fSBartlomiej Zolnierkiewicz case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: 374d912be2fSBartlomiej Zolnierkiewicz case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2: 375d912be2fSBartlomiej Zolnierkiewicz rc = serverworks_fixup_csb(pdev); 376d912be2fSBartlomiej Zolnierkiewicz break; 377d912be2fSBartlomiej Zolnierkiewicz case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: 378d912be2fSBartlomiej Zolnierkiewicz serverworks_fixup_ht1000(pdev); 379d912be2fSBartlomiej Zolnierkiewicz break; 380d912be2fSBartlomiej Zolnierkiewicz } 381d912be2fSBartlomiej Zolnierkiewicz 382d912be2fSBartlomiej Zolnierkiewicz return rc; 383d912be2fSBartlomiej Zolnierkiewicz } 384669a5db4SJeff Garzik 385669a5db4SJeff Garzik static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id *id) 386669a5db4SJeff Garzik { 3871626aeb8STejun Heo static const struct ata_port_info info[4] = { 388669a5db4SJeff Garzik { /* OSB4 */ 3891d2808fdSJeff Garzik .flags = ATA_FLAG_SLAVE_POSS, 39014bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 39114bdef98SErik Inge Bolsø .mwdma_mask = ATA_MWDMA2, 39214bdef98SErik Inge Bolsø .udma_mask = ATA_UDMA2, 393669a5db4SJeff Garzik .port_ops = &serverworks_osb4_port_ops 394669a5db4SJeff Garzik }, { /* OSB4 no UDMA */ 3951d2808fdSJeff Garzik .flags = ATA_FLAG_SLAVE_POSS, 39614bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 39714bdef98SErik Inge Bolsø .mwdma_mask = ATA_MWDMA2, 39814bdef98SErik Inge Bolsø /* No UDMA */ 399669a5db4SJeff Garzik .port_ops = &serverworks_osb4_port_ops 400669a5db4SJeff Garzik }, { /* CSB5 */ 4011d2808fdSJeff Garzik .flags = ATA_FLAG_SLAVE_POSS, 40214bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 40314bdef98SErik Inge Bolsø .mwdma_mask = ATA_MWDMA2, 404bf6263a8SJeff Garzik .udma_mask = ATA_UDMA4, 405669a5db4SJeff Garzik .port_ops = &serverworks_csb_port_ops 406669a5db4SJeff Garzik }, { /* CSB5 - later revisions*/ 4071d2808fdSJeff Garzik .flags = ATA_FLAG_SLAVE_POSS, 40814bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 40914bdef98SErik Inge Bolsø .mwdma_mask = ATA_MWDMA2, 410bf6263a8SJeff Garzik .udma_mask = ATA_UDMA5, 411669a5db4SJeff Garzik .port_ops = &serverworks_csb_port_ops 412669a5db4SJeff Garzik } 413669a5db4SJeff Garzik }; 4141626aeb8STejun Heo const struct ata_port_info *ppi[] = { &info[id->driver_data], NULL }; 41537017ac6SScott Carter struct scsi_host_template *sht = &serverworks_csb_sht; 416f08048e9STejun Heo int rc; 417f08048e9STejun Heo 418f08048e9STejun Heo rc = pcim_enable_device(pdev); 419f08048e9STejun Heo if (rc) 420f08048e9STejun Heo return rc; 421669a5db4SJeff Garzik 422d912be2fSBartlomiej Zolnierkiewicz rc = serverworks_fixup(pdev); 423669a5db4SJeff Garzik 424669a5db4SJeff Garzik /* OSB4 : South Bridge and IDE */ 425669a5db4SJeff Garzik if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { 426669a5db4SJeff Garzik /* Select non UDMA capable OSB4 if we can't do fixups */ 427d912be2fSBartlomiej Zolnierkiewicz if (rc < 0) 4281626aeb8STejun Heo ppi[0] = &info[1]; 42937017ac6SScott Carter sht = &serverworks_osb4_sht; 430669a5db4SJeff Garzik } 431669a5db4SJeff Garzik /* setup CSB5/CSB6 : South Bridge and IDE option RAID */ 432669a5db4SJeff Garzik else if ((pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) || 433669a5db4SJeff Garzik (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || 434669a5db4SJeff Garzik (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) { 435669a5db4SJeff Garzik 436669a5db4SJeff Garzik /* If the returned btr is the newer revision then 437669a5db4SJeff Garzik select the right info block */ 438d912be2fSBartlomiej Zolnierkiewicz if (rc == 3) 4391626aeb8STejun Heo ppi[0] = &info[3]; 440669a5db4SJeff Garzik 441669a5db4SJeff Garzik /* Is this the 3rd channel CSB6 IDE ? */ 442669a5db4SJeff Garzik if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2) 4431626aeb8STejun Heo ppi[1] = &ata_dummy_port_info; 444669a5db4SJeff Garzik } 445669a5db4SJeff Garzik 44637017ac6SScott Carter return ata_pci_bmdma_init_one(pdev, ppi, sht, NULL, 0); 447669a5db4SJeff Garzik } 448669a5db4SJeff Garzik 44958eb8cd5SBartlomiej Zolnierkiewicz #ifdef CONFIG_PM_SLEEP 45038e0d56eSAlan static int serverworks_reinit_one(struct pci_dev *pdev) 45138e0d56eSAlan { 4520a86e1c8SJingoo Han struct ata_host *host = pci_get_drvdata(pdev); 453f08048e9STejun Heo int rc; 454f08048e9STejun Heo 455f08048e9STejun Heo rc = ata_pci_device_do_resume(pdev); 456f08048e9STejun Heo if (rc) 457f08048e9STejun Heo return rc; 458f08048e9STejun Heo 459d912be2fSBartlomiej Zolnierkiewicz (void)serverworks_fixup(pdev); 460f08048e9STejun Heo 461f08048e9STejun Heo ata_host_resume(host); 462f08048e9STejun Heo return 0; 46338e0d56eSAlan } 464438ac6d5STejun Heo #endif 46538e0d56eSAlan 4662d2744fcSJeff Garzik static const struct pci_device_id serverworks[] = { 4672d2744fcSJeff Garzik { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, 4682d2744fcSJeff Garzik { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2}, 4692d2744fcSJeff Garzik { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2}, 4702d2744fcSJeff Garzik { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2}, 4712d2744fcSJeff Garzik { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2}, 4722d2744fcSJeff Garzik 4732d2744fcSJeff Garzik { }, 474669a5db4SJeff Garzik }; 475669a5db4SJeff Garzik 476669a5db4SJeff Garzik static struct pci_driver serverworks_pci_driver = { 477669a5db4SJeff Garzik .name = DRV_NAME, 478669a5db4SJeff Garzik .id_table = serverworks, 479669a5db4SJeff Garzik .probe = serverworks_init_one, 48038e0d56eSAlan .remove = ata_pci_remove_one, 48158eb8cd5SBartlomiej Zolnierkiewicz #ifdef CONFIG_PM_SLEEP 48238e0d56eSAlan .suspend = ata_pci_device_suspend, 48338e0d56eSAlan .resume = serverworks_reinit_one, 484438ac6d5STejun Heo #endif 485669a5db4SJeff Garzik }; 486669a5db4SJeff Garzik 4872fc75da0SAxel Lin module_pci_driver(serverworks_pci_driver); 488669a5db4SJeff Garzik 489669a5db4SJeff Garzik MODULE_AUTHOR("Alan Cox"); 490669a5db4SJeff Garzik MODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6"); 491669a5db4SJeff Garzik MODULE_LICENSE("GPL"); 492669a5db4SJeff Garzik MODULE_DEVICE_TABLE(pci, serverworks); 493669a5db4SJeff Garzik MODULE_VERSION(DRV_VERSION); 494