pata_serverworks.c

来自「linux 内核源代码」· C语言 代码 · 共 601 行 · 第 1/2 页

C
601
字号
/* * pata_serverworks.c 	- Serverworks PATA for new ATA layer *			  (C) 2005 Red Hat Inc *			  Alan Cox <alan@redhat.com> * * based upon * * serverworks.c * * Copyright (C) 1998-2000 Michel Aubry * Copyright (C) 1998-2000 Andrzej Krzysztofowicz * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> * Portions copyright (c) 2001 Sun Microsystems * * * RCC/ServerWorks IDE driver for Linux * *   OSB4: `Open South Bridge' IDE Interface (fn 1) *         supports UDMA mode 2 (33 MB/s) * *   CSB5: `Champion South Bridge' IDE Interface (fn 1) *         all revisions support UDMA mode 4 (66 MB/s) *         revision A2.0 and up support UDMA mode 5 (100 MB/s) * *         *** The CSB5 does not provide ANY register *** *         *** to detect 80-conductor cable presence. *** * *   CSB6: `Champion South Bridge' IDE Interface (optional: third channel) * * Documentation: *	Available under NDA only. Errata info very hard to get. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <scsi/scsi_host.h>#include <linux/libata.h>#define DRV_NAME "pata_serverworks"#define DRV_VERSION "0.4.2"#define SVWKS_CSB5_REVISION_NEW	0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */#define SVWKS_CSB6_REVISION	0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) *//* Seagate Barracuda ATA IV Family drives in UDMA mode 5 * can overrun their FIFOs when used with the CSB5 */static const char *csb_bad_ata100[] = {	"ST320011A",	"ST340016A",	"ST360021A",	"ST380021A",	NULL};/** *	dell_cable	-	Dell serverworks cable detection *	@ap: ATA port to do cable detect * *	Dell hide the 40/80 pin select for their interfaces in the top two *	bits of the subsystem ID. */static int dell_cable(struct ata_port *ap) {	struct pci_dev *pdev = to_pci_dev(ap->host->dev);	if (pdev->subsystem_device & (1 << (ap->port_no + 14)))		return ATA_CBL_PATA80;	return ATA_CBL_PATA40;}/** *	sun_cable	-	Sun Cobalt 'Alpine' cable detection *	@ap: ATA port to do cable select * *	Cobalt CSB5 IDE hides the 40/80pin in the top two bits of the *	subsystem ID the same as dell. We could use one function but we may *	need to extend the Dell one in future */static int sun_cable(struct ata_port *ap) {	struct pci_dev *pdev = to_pci_dev(ap->host->dev);	if (pdev->subsystem_device & (1 << (ap->port_no + 14)))		return ATA_CBL_PATA80;	return ATA_CBL_PATA40;}/** *	osb4_cable	-	OSB4 cable detect *	@ap: ATA port to check * *	The OSB4 isn't UDMA66 capable so this is easy */static int osb4_cable(struct ata_port *ap) {	return ATA_CBL_PATA40;}/** *	csb4_cable	-	CSB5/6 cable detect *	@ap: ATA port to check * *	Serverworks default arrangement is to use the drive side detection *	only. */static int csb_cable(struct ata_port *ap) {	return ATA_CBL_PATA80;}struct sv_cable_table {	int device;	int subvendor;	int (*cable_detect)(struct ata_port *ap);};/* *	Note that we don't copy the old serverworks code because the old *	code contains obvious mistakes */static struct sv_cable_table cable_detect[] = {	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, dell_cable },	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, dell_cable },	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN,  sun_cable },	{ PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, osb4_cable },	{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, csb_cable },	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, csb_cable },	{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, csb_cable },	{ PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, csb_cable },	{ }};/** *	serverworks_cable_detect	-	cable detection *	@ap: ATA port *	@deadline: deadline jiffies for the operation * *	Perform cable detection according to the device and subvendor *	identifications */static int serverworks_cable_detect(struct ata_port *ap){	struct pci_dev *pdev = to_pci_dev(ap->host->dev);	struct sv_cable_table *cb = cable_detect;	while(cb->device) {		if (cb->device == pdev->device &&		    (cb->subvendor == pdev->subsystem_vendor ||		      cb->subvendor == PCI_ANY_ID)) {			return cb->cable_detect(ap);		}		cb++;	}	BUG();	return -1;	/* kill compiler warning */}/** *	serverworks_is_csb	-	Check for CSB or OSB *	@pdev: PCI device to check * *	Returns true if the device being checked is known to be a CSB *	series device. */static u8 serverworks_is_csb(struct pci_dev *pdev){	switch (pdev->device) {		case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:		case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:		case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:		case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:			return 1;		default:			break;	}	return 0;}/** *	serverworks_osb4_filter	-	mode selection filter *	@adev: ATA device *	@mask: Mask of proposed modes * *	Filter the offered modes for the device to apply controller *	specific rules. OSB4 requires no UDMA for disks due to a FIFO *	bug we hit. */static unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask){	if (adev->class == ATA_DEV_ATA)		mask &= ~ATA_MASK_UDMA;	return ata_pci_default_filter(adev, mask);}/** *	serverworks_csb_filter	-	mode selection filter *	@adev: ATA device *	@mask: Mask of proposed modes * *	Check the blacklist and disable UDMA5 if matched */static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask){	const char *p;	char model_num[ATA_ID_PROD_LEN + 1];	int i;	/* Disk, UDMA */	if (adev->class != ATA_DEV_ATA)		return ata_pci_default_filter(adev, mask);	/* Actually do need to check */	ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num));	for (i = 0; (p = csb_bad_ata100[i]) != NULL; i++) {		if (!strcmp(p, model_num))			mask &= ~(0x1F << ATA_SHIFT_UDMA);	}	return ata_pci_default_filter(adev, mask);}/** *	serverworks_set_piomode	-	set initial PIO mode data *	@ap: ATA interface *	@adev: ATA device * *	Program the OSB4/CSB5 timing registers for PIO. The PIO register *	load is done as a simple lookup. */static void serverworks_set_piomode(struct ata_port *ap, struct ata_device *adev){	static const u8 pio_mode[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };	int offset = 1 + (2 * ap->port_no) - adev->devno;	int devbits = (2 * ap->port_no + adev->devno) * 4;	u16 csb5_pio;	struct pci_dev *pdev = to_pci_dev(ap->host->dev);	int pio = adev->pio_mode - XFER_PIO_0;	pci_write_config_byte(pdev, 0x40 + offset, pio_mode[pio]);	/* The OSB4 just requires the timing but the CSB series want the	   mode number as well */	if (serverworks_is_csb(pdev)) {		pci_read_config_word(pdev, 0x4A, &csb5_pio);		csb5_pio &= ~(0x0F << devbits);		pci_write_config_byte(pdev, 0x4A, csb5_pio | (pio << devbits));	}}/** *	serverworks_set_dmamode	-	set initial DMA mode data *	@ap: ATA interface *	@adev: ATA device * *	Program the MWDMA/UDMA modes for the serverworks OSB4/CSB5 *	chipset. The MWDMA mode values are pulled from a lookup table *	while the chipset uses mode number for UDMA. */static void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev){	static const u8 dma_mode[] = { 0x77, 0x21, 0x20 };	int offset = 1 + 2 * ap->port_no - adev->devno;	int devbits = 2 * ap->port_no + adev->devno;	u8 ultra;	u8 ultra_cfg;	struct pci_dev *pdev = to_pci_dev(ap->host->dev);	pci_read_config_byte(pdev, 0x54, &ultra_cfg);	pci_read_config_byte(pdev, 0x56 + ap->port_no, &ultra);	ultra &= ~(0x0F << (adev->devno * 4));	if (adev->dma_mode >= XFER_UDMA_0) {		pci_write_config_byte(pdev, 0x44 + offset,  0x20);		ultra |= (adev->dma_mode - XFER_UDMA_0)					<< (adev->devno * 4);		ultra_cfg |=  (1 << devbits);	} else {		pci_write_config_byte(pdev, 0x44 + offset,			dma_mode[adev->dma_mode - XFER_MW_DMA_0]);		ultra_cfg &= ~(1 << devbits);	}	pci_write_config_byte(pdev, 0x56 + ap->port_no, ultra);	pci_write_config_byte(pdev, 0x54, ultra_cfg);}static struct scsi_host_template serverworks_sht = {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?