sym_glue.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,464 行 · 第 1/4 页

C
2,464
字号
	sym_driver_safe_setup __initdata = SYM_LINUX_DRIVER_SAFE_SETUP;#ifdef	MODULEchar *sym53c8xx;	/* command line passed by insmod */MODULE_PARM(sym53c8xx, "s");#endif#define OPT_MAX_TAG		1#define OPT_BURST_ORDER		2#define OPT_SCSI_LED		3#define OPT_SCSI_DIFF		4#define OPT_IRQ_MODE		5#define OPT_SCSI_BUS_CHECK	6#define	OPT_HOST_ID		7#define OPT_REVERSE_PROBE	8#define OPT_VERBOSE		9#define OPT_DEBUG		10#define OPT_SETTLE_DELAY	11#define OPT_USE_NVRAM		12#define OPT_EXCLUDE		13#define OPT_SAFE_SETUP		14static char setup_token[] __initdata =	"tags:"		"burst:"	"led:"		"diff:"	"irqm:"		"buschk:"	"hostid:"	"revprob:"	"verb:"		"debug:"	"settle:"	"nvram:"	"excl:"		"safe:"	;#ifdef MODULE#define	ARG_SEP	' '#else#define	ARG_SEP	','#endifstatic int __init get_setup_token(char *p){	char *cur = setup_token;	char *pc;	int i = 0;	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {		++pc;		++i;		if (!strncmp(p, cur, pc - cur))			return i;		cur = pc;	}	return 0;}#endif	/* SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT */int __init sym53c8xx_setup(char *str){#ifdef	SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT	char *cur = str;	char *pc, *pv;	unsigned long val;	unsigned int i,  c;	int xi = 0;	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {		char *pe;		val = 0;		pv = pc;		c = *++pv;		if	(c == 'n')			val = 0;		else if	(c == 'y')			val = 1;		else			val = (int) simple_strtoul(pv, &pe, 0);		switch (get_setup_token(cur)) {		case OPT_MAX_TAG:			sym_driver_setup.max_tag = val;			if (!(pe && *pe == '/'))				break;			i = 0;			while (*pe && *pe != ARG_SEP && 				i < sizeof(sym_driver_setup.tag_ctrl)-1) {				sym_driver_setup.tag_ctrl[i++] = *pe++;			}			sym_driver_setup.tag_ctrl[i] = '\0';			break;		case OPT_SAFE_SETUP:			memcpy(&sym_driver_setup, &sym_driver_safe_setup,				sizeof(sym_driver_setup));			break;		case OPT_EXCLUDE:			if (xi < 8)				sym_driver_setup.excludes[xi++] = val;			break;#define __SIMPLE_OPTION(NAME, name) \		case OPT_ ## NAME :		\			sym_driver_setup.name = val;\			break;		__SIMPLE_OPTION(BURST_ORDER, burst_order)		__SIMPLE_OPTION(SCSI_LED, scsi_led)		__SIMPLE_OPTION(SCSI_DIFF, scsi_diff)		__SIMPLE_OPTION(IRQ_MODE, irq_mode)		__SIMPLE_OPTION(SCSI_BUS_CHECK, scsi_bus_check)		__SIMPLE_OPTION(HOST_ID, host_id)		__SIMPLE_OPTION(REVERSE_PROBE, reverse_probe)		__SIMPLE_OPTION(VERBOSE, verbose)		__SIMPLE_OPTION(DEBUG, debug)		__SIMPLE_OPTION(SETTLE_DELAY, settle_delay)		__SIMPLE_OPTION(USE_NVRAM, use_nvram)#undef __SIMPLE_OPTION		default:			printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);			break;		}		if ((cur = strchr(cur, ARG_SEP)) != NULL)			++cur;	}#endif	/* SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT */	return 1;}#ifndef MODULE__setup("sym53c8xx=", sym53c8xx_setup);#endif/* *  Read and check the PCI configuration for any detected NCR  *  boards and save data for attaching after all boards have  *  been detected. */static int __devinitsym53c8xx_pci_init(struct pci_dev *pdev, struct sym_device *device){	struct sym_pci_chip *chip;	u_long base, base_2; 	u_long base_c, base_2_c, io_port; 	int i;	u_short device_id, status_reg;	u_char revision;	/* Choose some short name for this device */	sprintf(device->s.inst_name, "sym.%d.%d.%d", pdev->bus->number,			PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));	device_id = pdev->device;	io_port = pdev->resource[0].start;	base_c = pdev->resource[1].start;	i = pci_get_base_address(pdev, 1, &base);	base_2_c = pdev->resource[i].start;	pci_get_base_address(pdev, i, &base_2);	base	&= PCI_BASE_ADDRESS_MEM_MASK;	base_2	&= PCI_BASE_ADDRESS_MEM_MASK;	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);	/*	 *  If user excluded this chip, do not initialize it.	 */	if (io_port) {		for (i = 0 ; i < 8 ; i++) {			if (sym_driver_setup.excludes[i] == io_port)				return -1;		}	}	/*	 *  Check if the chip is supported.	 */	chip = sym_lookup_pci_chip_table(device_id, revision);	if (!chip) {		printf_info("%s: device not supported\n", sym_name(device));		return -1;	}	/*	 *  Check if the chip has been assigned resources we need.	 *  XXX: can this still happen with Linux 2.6's PCI layer?	 */#ifdef SYM_CONF_IOMAPPED	if (!io_port) {		printf_info("%s: IO base address disabled.\n",			    sym_name(device));		return -1;	}#else	if (!base) {		printf_info("%s: MMIO base address disabled.\n",			    sym_name(device));		return -1;	}#endif	/*	 *  Ignore Symbios chips controlled by various RAID controllers.	 *  These controllers set value 0x52414944 at RAM end - 16.	 */#if defined(__i386__)	if (base_2_c) {		unsigned int ram_size, ram_val;		void *ram_ptr;		if (chip->features & FE_RAM8K)			ram_size = 8192;		else			ram_size = 4096;		ram_ptr = ioremap(base_2_c, ram_size);		if (ram_ptr) {			ram_val = readl_raw(ram_ptr + ram_size - 16);			iounmap(ram_ptr);			if (ram_val == 0x52414944) {				printf_info("%s: not initializing, "					    "driven by RAID controller.\n",					    sym_name(device));				return -1;			}		}	}#endif /* i386 and PCI MEMORY accessible */	/*	 *  Copy the chip description to our device structure, 	 *  so we can make it match the actual device and options.	 */	memcpy(&device->chip, chip, sizeof(device->chip));	device->chip.revision_id = revision;	/*	 *  Some features are required to be enabled in order to 	 *  work around some chip problems. :) ;)	 *  (ITEM 12 of a DEL about the 896 I haven't yet).	 *  We must ensure the chip will use WRITE AND INVALIDATE.	 *  The revision number limit is for now arbitrary.	 */	if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision < 0x4) {		chip->features	|= (FE_WRIE | FE_CLSE);	}	/* If the chip can do Memory Write Invalidate, enable it */	if (chip->features & FE_WRIE) {		if (pci_set_mwi(pdev))			return -1;	}	/*	 *  Work around for errant bit in 895A. The 66Mhz	 *  capable bit is set erroneously. Clear this bit.	 *  (Item 1 DEL 533)	 *	 *  Make sure Config space and Features agree.	 *	 *  Recall: writes are not normal to status register -	 *  write a 1 to clear and a 0 to leave unchanged.	 *  Can only reset bits.	 */	pci_read_config_word(pdev, PCI_STATUS, &status_reg);	if (chip->features & FE_66MHZ) {		if (!(status_reg & PCI_STATUS_66MHZ))			chip->features &= ~FE_66MHZ;	} else {		if (status_reg & PCI_STATUS_66MHZ) {			status_reg = PCI_STATUS_66MHZ;			pci_write_config_word(pdev, PCI_STATUS, status_reg);			pci_read_config_word(pdev, PCI_STATUS, &status_reg);		}	} 	/*	 *  Initialise device structure with items required by sym_attach.	 */	device->pdev		= pdev;	device->s.base		= base;	device->s.base_2	= base_2;	device->s.base_c	= base_c;	device->s.base_2_c	= base_2_c;	device->s.io_port	= io_port;	device->s.irq		= pdev->irq;	return 0;}/* * The NCR PQS and PDS cards are constructed as a DEC bridge * behind which sits a proprietary NCR memory controller and * either four or two 53c875s as separate devices.  We can tell * if an 875 is part of a PQS/PDS or not since if it is, it will * be on the same bus as the memory controller.  In its usual * mode of operation, the 875s are slaved to the memory * controller for all transfers.  To operate with the Linux * driver, the memory controller is disabled and the 875s * freed to function independently.  The only wrinkle is that * the preset SCSI ID (which may be zero) must be read in from * a special configuration space register of the 875. */void sym_config_pqs(struct pci_dev *pdev, struct sym_device *sym_dev){	int slot;	for (slot = 0; slot < 256; slot++) {		u8 tmp;		struct pci_dev *memc = pci_get_slot(pdev->bus, slot);		if (!memc || memc->vendor != 0x101a || memc->device == 0x0009) {			pci_dev_put(memc);			continue;		}		/*		 * We set these bits in the memory controller once per 875.		 * This isn't a problem in practice.		 */		/* bit 1: allow individual 875 configuration */		pci_read_config_byte(memc, 0x44, &tmp);		tmp |= 0x2;		pci_write_config_byte(memc, 0x44, tmp);		/* bit 2: drive individual 875 interrupts to the bus */		pci_read_config_byte(memc, 0x45, &tmp);		tmp |= 0x4;		pci_write_config_byte(memc, 0x45, tmp);		pci_read_config_byte(pdev, 0x84, &tmp);		sym_dev->host_id = tmp;		pci_dev_put(memc);		break;	}}/* *  Called before unloading the module. *  Detach the host. *  We have to free resources and halt the NCR chip. */static int sym_detach(struct sym_hcb *np){	printk("%s: detaching ...\n", sym_name(np));	del_timer_sync(&np->s.timer);	/*	 * Reset NCR chip.	 * We should use sym_soft_reset(), but we don't want to do 	 * so, since we may not be safe if interrupts occur.	 */	printk("%s: resetting chip\n", sym_name(np));	OUTB (nc_istat, SRST);	UDELAY (10);	OUTB (nc_istat, 0);	sym_free_resources(np);	return 1;}MODULE_LICENSE("Dual BSD/GPL");MODULE_VERSION(SYM_VERSION);/* * Driver host template. */static struct scsi_host_template sym2_template = {	.module			= THIS_MODULE,	.name			= "sym53c8xx",	.info			= sym53c8xx_info, 	.queuecommand		= sym53c8xx_queue_command,	.slave_configure	= sym53c8xx_slave_configure,	.eh_abort_handler	= sym53c8xx_eh_abort_handler,	.eh_device_reset_handler = sym53c8xx_eh_device_reset_handler,	.eh_bus_reset_handler	= sym53c8xx_eh_bus_reset_handler,	.eh_host_reset_handler	= sym53c8xx_eh_host_reset_handler,	.this_id		= 7,	.use_clustering		= DISABLE_CLUSTERING,#ifdef SYM_LINUX_PROC_INFO_SUPPORT	.proc_info		= sym53c8xx_proc_info,	.proc_name		= NAME53C8XX,#endif};static int attach_count;static int __devinit sym2_probe(struct pci_dev *pdev,				const struct pci_device_id *ent){	struct sym_device sym_dev;	struct sym_nvram nvram;	struct Scsi_Host *instance;	memset(&sym_dev, 0, sizeof(sym_dev));	memset(&nvram, 0, sizeof(nvram));	if (pci_enable_device(pdev))		return -ENODEV;	pci_set_master(pdev);	if (pci_request_regions(pdev, NAME53C8XX))		goto disable;	sym_dev.host_id = SYM_SETUP_HOST_ID;	if (sym53c8xx_pci_init(pdev, &sym_dev))		goto free;	sym_config_pqs(pdev, &sym_dev);	sym_get_nvram(&sym_dev, &nvram);	instance = sym_attach(&sym2_template, attach_count, &sym_dev);	if (!instance)		goto free;	if (scsi_add_host(instance, &pdev->dev))		goto detach;	scsi_scan_host(instance);	attach_count++;	return 0; detach:	sym_detach(pci_get_drvdata(pdev)); free:	pci_release_regions(pdev); disable:	pci_disable_device(pdev);	return -ENODEV;}static void __devexit sym2_remove(struct pci_dev *pdev){	struct sym_hcb *np = pci_get_drvdata(pdev);	struct Scsi_Host *host = np->s.host;	scsi_remove_host(host);	scsi_host_put(host);	sym_detach(np);	pci_release_regions(pdev);	pci_disable_device(pdev);	attach_count--;}static void sym2_get_offset(struct scsi_device *sdev){	struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb;	struct sym_tcb *tp = &np->target[sdev->id];	spi_offset(sdev) = tp->tinfo.curr.offset;}static void sym2_set_offset(struct scsi_device *sdev, int offset){	struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb;	struct sym_tcb *tp = &np->target[sdev->id];	tp->tinfo.goal.offset = offset;}static void sym2_get_period(struct scsi_device *sdev){	struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb;	struct sym_tcb *tp = &np->target[sdev->id];	spi_period(sdev) = tp->tinfo.curr.period;}static void sym2_set_period(struct scsi_device *sdev, int period){	struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb;	struct sym_tcb *tp = &np->target[sdev->id];	/* have to have DT for these transfers */	if (period <= np->minsync)		tp->tinfo.goal.options |= PPR_OPT_DT;	tp->tinfo.goal.period = period;}static void sym2_get_width(struct scsi_device *sdev){	struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb;	struct sym_tcb *tp = &np->target[sdev->id];	spi_width(sdev) = tp->tinfo.curr.width ? 1 : 0;}static void sym2_set_width(struct scsi_device *sdev, int width){	struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb;	struct sym_tcb *tp = &np->target[sdev->id];	/* It is illegal to have DT set on narrow transfers */	if (width == 0)		tp->tinfo.goal.options &= ~PPR_OPT_DT;	tp->tinfo.goal.width = width;}static void sym2_get_dt(struct scsi_device *sdev){	struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb;	struct sym_tcb *tp = &np->target[sdev->id];	spi_dt(sdev) = (tp->tinfo.curr.options & PPR_OPT_DT) ? 1 : 0;}static void sym2_set_dt(struct scsi_device *sdev, int dt){	struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb;	struct sym_tcb *tp = &np->target[sdev->id];	if (dt)		tp->tinfo.goal.options |= PPR_OPT_DT;	else		tp->tinfo.goal.options &= ~PPR_OPT_DT;}	static struct spi_function_template sym2_transport_functions = {	.set_offset	= sym2_set_offset,	.get_offset	= sym2_get_offset,	.show_offset	= 1,	.set_period	= sym2_set_period,	.get_period	= sym2_get_period,	.show_period	= 1,	.set_width	= sym2_set_width,	.get_width	= sym2_get_width,	.show_width	= 1,	.get_dt		= sym2_get_dt,	.set_dt		= sym2_set_dt,	.show_dt	= 1,};static struct pci_device_id sym2_id_table[] __devinitdata = {	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C810,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C820,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C825,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C815,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C810AP,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C860,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1510,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C896,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C895,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C885,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C875,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C1510,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C895A,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C875A,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1010_33,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1010_66,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C875J,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ 0, }};MODULE_DEVICE_TABLE(pci, sym2_id_table);static struct pci_driver sym2_driver = {	.name		= NAME53C8XX,	.id_table	= sym2_id_table,	.probe		= sym2_probe,	.remove		= __devexit_p(sym2_remove),};static int __init sym2_init(void){	sym2_transport_template = spi_attach_transport(&sym2_transport_functions);	if (!sym2_transport_template)		return -ENODEV;	pci_register_driver(&sym2_driver);	return 0;}static void __exit sym2_exit(void){	pci_unregister_driver(&sym2_driver);	spi_release_transport(sym2_transport_template);}module_init(sym2_init);module_exit(sym2_exit);

⌨️ 快捷键说明

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