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 + -
显示快捷键?