powertec.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:14k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/drivers/acorn/scsi/powertec.c
  3.  *
  4.  *  Copyright (C) 1997-2000 Russell King
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License version 2 as
  8.  * published by the Free Software Foundation.
  9.  *
  10.  * This driver is based on experimentation.  Hence, it may have made
  11.  * assumptions about the particular card that I have available, and
  12.  * may not be reliable!
  13.  *
  14.  * Changelog:
  15.  *  01-10-1997 RMK Created, READONLY version.
  16.  *  15-02-1998 RMK Added DMA support and hardware definitions.
  17.  *  15-04-1998 RMK Only do PIO if FAS216 will allow it.
  18.  *  02-05-1998 RMK Moved DMA sg list into per-interface structure.
  19.  *  27-06-1998 RMK Changed asm/delay.h to linux/delay.h
  20.  *  02-04-2000 RMK Updated for new error handling code.
  21.  */
  22. #include <linux/module.h>
  23. #include <linux/blk.h>
  24. #include <linux/kernel.h>
  25. #include <linux/string.h>
  26. #include <linux/ioport.h>
  27. #include <linux/sched.h>
  28. #include <linux/proc_fs.h>
  29. #include <linux/unistd.h>
  30. #include <linux/stat.h>
  31. #include <linux/delay.h>
  32. #include <linux/pci.h>
  33. #include <linux/init.h>
  34. #include <asm/dma.h>
  35. #include <asm/ecard.h>
  36. #include <asm/io.h>
  37. #include <asm/irq.h>
  38. #include <asm/pgtable.h>
  39. #include "../../scsi/sd.h"
  40. #include "../../scsi/hosts.h"
  41. #include "fas216.h"
  42. #include <scsi/scsicam.h>
  43. /* Configuration */
  44. #define POWERTEC_XTALFREQ 40
  45. #define POWERTEC_ASYNC_PERIOD 200
  46. #define POWERTEC_SYNC_DEPTH 7
  47. /*
  48.  * List of devices that the driver will recognise
  49.  */
  50. #define POWERTECSCSI_LIST { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI }
  51. #define POWERTEC_FAS216_OFFSET 0xc00
  52. #define POWERTEC_FAS216_SHIFT 4
  53. #define POWERTEC_INTR_STATUS 0x800
  54. #define POWERTEC_INTR_BIT 0x80
  55. #define POWERTEC_RESET_CONTROL 0x406
  56. #define POWERTEC_RESET_BIT 1
  57. #define POWERTEC_TERM_CONTROL 0x806
  58. #define POWERTEC_TERM_ENABLE 1
  59. #define POWERTEC_INTR_CONTROL 0x407
  60. #define POWERTEC_INTR_ENABLE 1
  61. #define POWERTEC_INTR_DISABLE 0
  62. /*
  63.  * Version
  64.  */
  65. #define VER_MAJOR 0
  66. #define VER_MINOR 0
  67. #define VER_PATCH 5
  68. static struct expansion_card *ecs[MAX_ECARDS];
  69. /*
  70.  * Use term=0,1,0,0,0 to turn terminators on/off
  71.  */
  72. static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
  73. #define NR_SG 256
  74. typedef struct {
  75. FAS216_Info info;
  76. struct {
  77. unsigned int term_port;
  78. unsigned int terms;
  79. } control;
  80. /* other info... */
  81. struct scatterlist sg[NR_SG]; /* Scatter DMA list */
  82. } PowerTecScsi_Info;
  83. /* Prototype: void powertecscsi_irqenable(ec, irqnr)
  84.  * Purpose  : Enable interrupts on Powertec SCSI card
  85.  * Params   : ec    - expansion card structure
  86.  *          : irqnr - interrupt number
  87.  */
  88. static void
  89. powertecscsi_irqenable(struct expansion_card *ec, int irqnr)
  90. {
  91. unsigned int port = (unsigned int)ec->irq_data;
  92. outb(POWERTEC_INTR_ENABLE, port);
  93. }
  94. /* Prototype: void powertecscsi_irqdisable(ec, irqnr)
  95.  * Purpose  : Disable interrupts on Powertec SCSI card
  96.  * Params   : ec    - expansion card structure
  97.  *          : irqnr - interrupt number
  98.  */
  99. static void
  100. powertecscsi_irqdisable(struct expansion_card *ec, int irqnr)
  101. {
  102. unsigned int port = (unsigned int)ec->irq_data;
  103. outb(POWERTEC_INTR_DISABLE, port);
  104. }
  105. static const expansioncard_ops_t powertecscsi_ops = {
  106. powertecscsi_irqenable,
  107. powertecscsi_irqdisable,
  108. NULL,
  109. NULL,
  110. NULL,
  111. NULL
  112. };
  113. /* Prototype: void powertecscsi_terminator_ctl(host, on_off)
  114.  * Purpose  : Turn the Powertec SCSI terminators on or off
  115.  * Params   : host   - card to turn on/off
  116.  *          : on_off - !0 to turn on, 0 to turn off
  117.  */
  118. static void
  119. powertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
  120. {
  121. PowerTecScsi_Info *info = (PowerTecScsi_Info *)host->hostdata;
  122. if (on_off)
  123. info->control.terms = POWERTEC_TERM_ENABLE;
  124. else
  125. info->control.terms = 0;
  126. outb(info->control.terms, info->control.term_port);
  127. }
  128. /* Prototype: void powertecscsi_intr(irq, *dev_id, *regs)
  129.  * Purpose  : handle interrupts from Powertec SCSI card
  130.  * Params   : irq    - interrupt number
  131.  *       dev_id - user-defined (Scsi_Host structure)
  132.  *       regs   - processor registers at interrupt
  133.  */
  134. static void
  135. powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
  136. {
  137. struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
  138. fas216_intr(host);
  139. }
  140. /* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type)
  141.  * Purpose  : initialises DMA/PIO
  142.  * Params   : host      - host
  143.  *       SCpnt     - command
  144.  *       direction - DMA on to/off of card
  145.  *       min_type  - minimum DMA support that we must have for this transfer
  146.  * Returns  : type of transfer to be performed
  147.  */
  148. static fasdmatype_t
  149. powertecscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
  150.        fasdmadir_t direction, fasdmatype_t min_type)
  151. {
  152. PowerTecScsi_Info *info = (PowerTecScsi_Info *)host->hostdata;
  153. int dmach = host->dma_channel;
  154. if (dmach != NO_DMA &&
  155.     (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
  156. int bufs = SCp->buffers_residual;
  157. int pci_dir, dma_dir;
  158. if (bufs)
  159. memcpy(info->sg + 1, SCp->buffer + 1,
  160. sizeof(struct scatterlist) * bufs);
  161. info->sg[0].address = SCp->ptr;
  162. info->sg[0].page    = NULL;
  163. info->sg[0].length  = SCp->this_residual;
  164. if (direction == DMA_OUT)
  165. pci_dir = PCI_DMA_TODEVICE,
  166. dma_dir = DMA_MODE_WRITE;
  167. else
  168. pci_dir = PCI_DMA_FROMDEVICE,
  169. dma_dir = DMA_MODE_READ;
  170. pci_map_sg(NULL, info->sg, bufs + 1, pci_dir);
  171. disable_dma(dmach);
  172. set_dma_sg(dmach, info->sg, bufs + 1);
  173. set_dma_mode(dmach, dma_dir);
  174. enable_dma(dmach);
  175. return fasdma_real_all;
  176. }
  177. /*
  178.  * If we're not doing DMA,
  179.  *  we'll do slow PIO
  180.  */
  181. return fasdma_pio;
  182. }
  183. /* Prototype: int powertecscsi_dma_stop(host, SCpnt)
  184.  * Purpose  : stops DMA/PIO
  185.  * Params   : host  - host
  186.  *       SCpnt - command
  187.  */
  188. static void
  189. powertecscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
  190. {
  191. if (host->dma_channel != NO_DMA)
  192. disable_dma(host->dma_channel);
  193. }
  194. /* Prototype: int powertecscsi_detect(Scsi_Host_Template * tpnt)
  195.  * Purpose  : initialises PowerTec SCSI driver
  196.  * Params   : tpnt - template for this SCSI adapter
  197.  * Returns  : >0 if host found, 0 otherwise.
  198.  */
  199. int
  200. powertecscsi_detect(Scsi_Host_Template *tpnt)
  201. {
  202. static const card_ids powertecscsi_cids[] =
  203. { POWERTECSCSI_LIST, { 0xffff, 0xffff} };
  204. int count = 0;
  205. struct Scsi_Host *host;
  206.   
  207. tpnt->proc_name = "powertec";
  208. memset(ecs, 0, sizeof (ecs));
  209. ecard_startfind();
  210. while (1) {
  211.      PowerTecScsi_Info *info;
  212. ecs[count] = ecard_find(0, powertecscsi_cids);
  213. if (!ecs[count])
  214. break;
  215. ecard_claim(ecs[count]);
  216. host = scsi_register(tpnt, sizeof (PowerTecScsi_Info));
  217. if (!host) {
  218. ecard_release(ecs[count]);
  219. break;
  220. }
  221. host->io_port = ecard_address(ecs[count], ECARD_IOC, ECARD_FAST);
  222. host->irq = ecs[count]->irq;
  223. host->dma_channel = ecs[count]->dma;
  224. info = (PowerTecScsi_Info *)host->hostdata;
  225. if (host->dma_channel != NO_DMA)
  226. set_dma_speed(host->dma_channel, 180);
  227. info->control.term_port = host->io_port + POWERTEC_TERM_CONTROL;
  228. info->control.terms = term[count] ? POWERTEC_TERM_ENABLE : 0;
  229. powertecscsi_terminator_ctl(host, info->control.terms);
  230. info->info.scsi.io_port =
  231. host->io_port + POWERTEC_FAS216_OFFSET;
  232. info->info.scsi.io_shift= POWERTEC_FAS216_SHIFT;
  233. info->info.scsi.irq = host->irq;
  234. info->info.ifcfg.clockrate = POWERTEC_XTALFREQ;
  235. info->info.ifcfg.select_timeout = 255;
  236. info->info.ifcfg.asyncperiod = POWERTEC_ASYNC_PERIOD;
  237. info->info.ifcfg.sync_max_depth = POWERTEC_SYNC_DEPTH;
  238. info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
  239. info->info.ifcfg.disconnect_ok = 1;
  240. info->info.ifcfg.wide_max_size = 0;
  241. info->info.dma.setup = powertecscsi_dma_setup;
  242. info->info.dma.pseudo = NULL;
  243. info->info.dma.stop = powertecscsi_dma_stop;
  244. ecs[count]->irqaddr = (unsigned char *)
  245.     ioaddr(host->io_port + POWERTEC_INTR_STATUS);
  246. ecs[count]->irqmask = POWERTEC_INTR_BIT;
  247. ecs[count]->irq_data = (void *)
  248.     (host->io_port + POWERTEC_INTR_CONTROL);
  249. ecs[count]->ops = (expansioncard_ops_t *)&powertecscsi_ops;
  250. request_region(host->io_port + POWERTEC_FAS216_OFFSET,
  251.        16 << POWERTEC_FAS216_SHIFT, "powertec2-fas");
  252. if (host->irq != NO_IRQ &&
  253.     request_irq(host->irq, powertecscsi_intr,
  254. SA_INTERRUPT, "powertec", host)) {
  255. printk("scsi%d: IRQ%d not free, interrupts disabledn",
  256.        host->host_no, host->irq);
  257. host->irq = NO_IRQ;
  258. info->info.scsi.irq = NO_IRQ;
  259. }
  260. if (host->dma_channel != NO_DMA &&
  261.     request_dma(host->dma_channel, "powertec")) {
  262. printk("scsi%d: DMA%d not free, DMA disabledn",
  263.        host->host_no, host->dma_channel);
  264. host->dma_channel = NO_DMA;
  265. }
  266. fas216_init(host);
  267. ++count;
  268. }
  269. return count;
  270. }
  271. /* Prototype: int powertecscsi_release(struct Scsi_Host * host)
  272.  * Purpose  : releases all resources used by this adapter
  273.  * Params   : host - driver host structure to return info for.
  274.  */
  275. int powertecscsi_release(struct Scsi_Host *host)
  276. {
  277. int i;
  278. fas216_release(host);
  279. if (host->irq != NO_IRQ)
  280. free_irq(host->irq, host);
  281. if (host->dma_channel != NO_DMA)
  282. free_dma(host->dma_channel);
  283. release_region(host->io_port + POWERTEC_FAS216_OFFSET,
  284.        16 << POWERTEC_FAS216_SHIFT);
  285. for (i = 0; i < MAX_ECARDS; i++)
  286. if (ecs[i] &&
  287.     host->io_port == ecard_address(ecs[i], ECARD_IOC, ECARD_FAST))
  288. ecard_release(ecs[i]);
  289. return 0;
  290. }
  291. /* Prototype: const char *powertecscsi_info(struct Scsi_Host * host)
  292.  * Purpose  : returns a descriptive string about this interface,
  293.  * Params   : host - driver host structure to return info for.
  294.  * Returns  : pointer to a static buffer containing null terminated string.
  295.  */
  296. const char *powertecscsi_info(struct Scsi_Host *host)
  297. {
  298. PowerTecScsi_Info *info = (PowerTecScsi_Info *)host->hostdata;
  299. static char string[100], *p;
  300. p = string;
  301. p += sprintf(p, "%s ", host->hostt->name);
  302. p += fas216_info(&info->info, p);
  303. p += sprintf(p, "v%d.%d.%d terminators o%s",
  304.      VER_MAJOR, VER_MINOR, VER_PATCH,
  305.      info->control.terms ? "n" : "ff");
  306. return string;
  307. }
  308. /* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
  309.  * Purpose  : Set a driver specific function
  310.  * Params   : host   - host to setup
  311.  *          : buffer - buffer containing string describing operation
  312.  *          : length - length of string
  313.  * Returns  : -EINVAL, or 0
  314.  */
  315. static int
  316. powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
  317. {
  318. int ret = length;
  319. if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) {
  320. buffer += 12;
  321. length -= 12;
  322. if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
  323. if (buffer[5] == '1')
  324. powertecscsi_terminator_ctl(host, 1);
  325. else if (buffer[5] == '0')
  326. powertecscsi_terminator_ctl(host, 0);
  327. else
  328. ret = -EINVAL;
  329. } else
  330. ret = -EINVAL;
  331. } else
  332. ret = -EINVAL;
  333. return ret;
  334. }
  335. /* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
  336.  * int length, int host_no, int inout)
  337.  * Purpose  : Return information about the driver to a user process accessing
  338.  *       the /proc filesystem.
  339.  * Params   : buffer  - a buffer to write information to
  340.  *       start   - a pointer into this buffer set by this routine to the start
  341.  *         of the required information.
  342.  *       offset  - offset into information that we have read upto.
  343.  *       length  - length of buffer
  344.  *       host_no - host number to return information for
  345.  *       inout   - 0 for reading, 1 for writing.
  346.  * Returns  : length of data written to buffer.
  347.  */
  348. int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
  349.     int length, int host_no, int inout)
  350. {
  351. int pos, begin;
  352. struct Scsi_Host *host = scsi_hostlist;
  353. PowerTecScsi_Info *info;
  354. Scsi_Device *scd;
  355. while (host) {
  356. if (host->host_no == host_no)
  357. break;
  358. host = host->next;
  359. }
  360. if (!host)
  361. return 0;
  362. if (inout == 1)
  363. return powertecscsi_set_proc_info(host, buffer, length);
  364. info = (PowerTecScsi_Info *)host->hostdata;
  365. begin = 0;
  366. pos = sprintf(buffer,
  367. "PowerTec SCSI driver version %d.%d.%dn",
  368. VER_MAJOR, VER_MINOR, VER_PATCH);
  369. pos += fas216_print_host(&info->info, buffer + pos);
  370. pos += sprintf(buffer + pos, "Term    : o%sn",
  371. info->control.terms ? "n" : "ff");
  372. pos += fas216_print_stats(&info->info, buffer + pos);
  373. pos += sprintf(buffer+pos, "nAttached devices:n");
  374. for (scd = host->host_queue; scd; scd = scd->next) {
  375. pos += fas216_print_device(&info->info, scd, buffer + pos);
  376. if (pos + begin < offset) {
  377. begin += pos;
  378. pos = 0;
  379. }
  380. if (pos + begin > offset + length)
  381. break;
  382. }
  383. *start = buffer + (offset - begin);
  384. pos -= offset - begin;
  385. if (pos > length)
  386. pos = length;
  387. return pos;
  388. }
  389. static Scsi_Host_Template powertecscsi_template = {
  390. module: THIS_MODULE,
  391. proc_info: powertecscsi_proc_info,
  392. name: "PowerTec SCSI",
  393. detect: powertecscsi_detect,
  394. release: powertecscsi_release,
  395. info: powertecscsi_info,
  396. bios_param: scsicam_bios_param,
  397. can_queue: 1,
  398. this_id: 7,
  399. sg_tablesize: SG_ALL,
  400. cmd_per_lun: 1,
  401. use_clustering: ENABLE_CLUSTERING,
  402. command: fas216_command,
  403. queuecommand: fas216_queue_command,
  404. eh_host_reset_handler: fas216_eh_host_reset,
  405. eh_bus_reset_handler: fas216_eh_bus_reset,
  406. eh_device_reset_handler: fas216_eh_device_reset,
  407. eh_abort_handler: fas216_eh_abort,
  408. use_new_eh_code: 1
  409. };
  410. static int __init powertecscsi_init(void)
  411. {
  412. scsi_register_module(MODULE_SCSI_HA, &powertecscsi_template);
  413. if (powertecscsi_template.present)
  414. return 0;
  415. scsi_unregister_module(MODULE_SCSI_HA, &powertecscsi_template);
  416. return -ENODEV;
  417. }
  418. static void __exit powertecscsi_exit(void)
  419. {
  420. scsi_unregister_module(MODULE_SCSI_HA, &powertecscsi_template);
  421. }
  422. module_init(powertecscsi_init);
  423. module_exit(powertecscsi_exit);
  424. MODULE_AUTHOR("Russell King");
  425. MODULE_DESCRIPTION("Powertec SCSI driver");
  426. MODULE_PARM(term, "1-8i");
  427. MODULE_PARM_DESC(term, "SCSI bus termination");
  428. MODULE_LICENSE("GPL");
  429. EXPORT_NO_SYMBOLS;