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

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/drivers/acorn/scsi/eesox.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 READ/WRITE version
  17.  * added DMA support and hardware definitions
  18.  *   14-03-1998 RMK Updated DMA support
  19.  * Added terminator control
  20.  *   15-04-1998 RMK Only do PIO if FAS216 will allow it.
  21.  *   27-06-1998 RMK Changed asm/delay.h to linux/delay.h
  22.  *   02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new
  23.  * error handling code.
  24.  */
  25. #include <linux/module.h>
  26. #include <linux/blk.h>
  27. #include <linux/kernel.h>
  28. #include <linux/string.h>
  29. #include <linux/ioport.h>
  30. #include <linux/sched.h>
  31. #include <linux/proc_fs.h>
  32. #include <linux/unistd.h>
  33. #include <linux/stat.h>
  34. #include <linux/delay.h>
  35. #include <linux/pci.h>
  36. #include <linux/init.h>
  37. #include <asm/io.h>
  38. #include <asm/irq.h>
  39. #include <asm/dma.h>
  40. #include <asm/ecard.h>
  41. #include <asm/pgtable.h>
  42. #include "../../scsi/sd.h"
  43. #include "../../scsi/hosts.h"
  44. #include "fas216.h"
  45. #include <scsi/scsicam.h>
  46. /* Configuration */
  47. #define EESOX_XTALFREQ 40
  48. #define EESOX_ASYNC_PERIOD 200
  49. #define EESOX_SYNC_DEPTH 7
  50. /*
  51.  * List of devices that the driver will recognise
  52.  */
  53. #define EESOXSCSI_LIST { MANU_EESOX, PROD_EESOX_SCSI2 }
  54. #define EESOX_FAS216_OFFSET 0xc00
  55. #define EESOX_FAS216_SHIFT 3
  56. #define EESOX_STATUS 0xa00
  57. #define EESOX_STAT_INTR 0x01
  58. #define EESOX_STAT_DMA 0x02
  59. #define EESOX_CONTROL 0xa00
  60. #define EESOX_INTR_ENABLE 0x04
  61. #define EESOX_TERM_ENABLE 0x02
  62. #define EESOX_RESET 0x01
  63. #define EESOX_DMA_OFFSET 0xe00
  64. /*
  65.  * Version
  66.  */
  67. #define VER_MAJOR 0
  68. #define VER_MINOR 0
  69. #define VER_PATCH 3
  70. static struct expansion_card *ecs[MAX_ECARDS];
  71. /*
  72.  * Use term=0,1,0,0,0 to turn terminators on/off
  73.  */
  74. static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
  75. #define NR_SG 256
  76. struct control {
  77. unsigned int io_port;
  78. unsigned int control;
  79. };
  80. typedef struct {
  81. FAS216_Info info;
  82. struct control control;
  83. unsigned int dmaarea; /* Pseudo DMA area */
  84. struct scatterlist sg[NR_SG]; /* Scatter DMA list */
  85. } EESOXScsi_Info;
  86. /* Prototype: void eesoxscsi_irqenable(ec, irqnr)
  87.  * Purpose  : Enable interrupts on EESOX SCSI card
  88.  * Params   : ec    - expansion card structure
  89.  *          : irqnr - interrupt number
  90.  */
  91. static void
  92. eesoxscsi_irqenable(struct expansion_card *ec, int irqnr)
  93. {
  94. struct control *control = (struct control *)ec->irq_data;
  95. control->control |= EESOX_INTR_ENABLE;
  96. outb(control->control, control->io_port);
  97. }
  98. /* Prototype: void eesoxscsi_irqdisable(ec, irqnr)
  99.  * Purpose  : Disable interrupts on EESOX SCSI card
  100.  * Params   : ec    - expansion card structure
  101.  *          : irqnr - interrupt number
  102.  */
  103. static void
  104. eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr)
  105. {
  106. struct control *control = (struct control *)ec->irq_data;
  107. control->control &= ~EESOX_INTR_ENABLE;
  108. outb(control->control, control->io_port);
  109. }
  110. static const expansioncard_ops_t eesoxscsi_ops = {
  111. eesoxscsi_irqenable,
  112. eesoxscsi_irqdisable,
  113. NULL,
  114. NULL,
  115. NULL,
  116. NULL
  117. };
  118. /* Prototype: void eesoxscsi_terminator_ctl(*host, on_off)
  119.  * Purpose  : Turn the EESOX SCSI terminators on or off
  120.  * Params   : host   - card to turn on/off
  121.  *          : on_off - !0 to turn on, 0 to turn off
  122.  */
  123. static void
  124. eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
  125. {
  126. EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
  127. unsigned long flags;
  128. save_flags_cli(flags);
  129. if (on_off)
  130. info->control.control |= EESOX_TERM_ENABLE;
  131. else
  132. info->control.control &= ~EESOX_TERM_ENABLE;
  133. restore_flags(flags);
  134. outb(info->control.control, info->control.io_port);
  135. }
  136. /* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs)
  137.  * Purpose  : handle interrupts from EESOX SCSI card
  138.  * Params   : irq    - interrupt number
  139.  *       dev_id - user-defined (Scsi_Host structure)
  140.  *       regs   - processor registers at interrupt
  141.  */
  142. static void
  143. eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
  144. {
  145. struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
  146. fas216_intr(host);
  147. }
  148. /* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type)
  149.  * Purpose  : initialises DMA/PIO
  150.  * Params   : host      - host
  151.  *       SCpnt     - command
  152.  *       direction - DMA on to/off of card
  153.  *       min_type  - minimum DMA support that we must have for this transfer
  154.  * Returns  : type of transfer to be performed
  155.  */
  156. static fasdmatype_t
  157. eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
  158.        fasdmadir_t direction, fasdmatype_t min_type)
  159. {
  160. EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
  161. int dmach = host->dma_channel;
  162. if (dmach != NO_DMA &&
  163.     (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
  164. int bufs = SCp->buffers_residual;
  165. int pci_dir, dma_dir;
  166. if (bufs)
  167. memcpy(info->sg + 1, SCp->buffer + 1,
  168. sizeof(struct scatterlist) * bufs);
  169. info->sg[0].address = SCp->ptr;
  170. info->sg[0].page    = NULL;
  171. info->sg[0].length  = SCp->this_residual;
  172. if (direction == DMA_OUT)
  173. pci_dir = PCI_DMA_TODEVICE,
  174. dma_dir = DMA_MODE_WRITE;
  175. else
  176. pci_dir = PCI_DMA_FROMDEVICE,
  177. dma_dir = DMA_MODE_READ;
  178. pci_map_sg(NULL, info->sg, bufs + 1, pci_dir);
  179. disable_dma(dmach);
  180. set_dma_sg(dmach, info->sg, bufs + 1);
  181. set_dma_mode(dmach, dma_dir);
  182. enable_dma(dmach);
  183. return fasdma_real_all;
  184. }
  185. /*
  186.  * We don't do DMA, we only do slow PIO
  187.  *
  188.  * Some day, we will do Pseudo DMA
  189.  */
  190. return fasdma_pseudo;
  191. }
  192. static void
  193. eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
  194.      fasdmadir_t dir, int transfer_size)
  195. {
  196. EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
  197. unsigned int status;
  198. unsigned int length = SCp->this_residual;
  199. union {
  200. unsigned char *c;
  201. unsigned short *s;
  202. unsigned long *l;
  203. } buffer;
  204. buffer.c = SCp->ptr;
  205. status = inb(host->io_port + EESOX_STATUS);
  206. if (dir == DMA_IN) {
  207. while (length > 8) {
  208. if (status & EESOX_STAT_DMA) {
  209. unsigned long l1, l2;
  210. l1 = inw(info->dmaarea);
  211. l1 |= inw(info->dmaarea) << 16;
  212. l2 = inw(info->dmaarea);
  213. l2 |= inw(info->dmaarea) << 16;
  214. *buffer.l++ = l1;
  215. *buffer.l++ = l2;
  216. length -= 8;
  217. } else if (status & EESOX_STAT_INTR)
  218. goto end;
  219. status = inb(host->io_port + EESOX_STATUS);
  220. }
  221. while (length > 1) {
  222. if (status & EESOX_STAT_DMA) {
  223. *buffer.s++ = inw(info->dmaarea);
  224. length -= 2;
  225. } else if (status & EESOX_STAT_INTR)
  226. goto end;
  227. status = inb(host->io_port + EESOX_STATUS);
  228. }
  229. while (length > 0) {
  230. if (status & EESOX_STAT_DMA) {
  231. *buffer.c++ = inw(info->dmaarea);
  232. length -= 1;
  233. } else if (status & EESOX_STAT_INTR)
  234. goto end;
  235. status = inb(host->io_port + EESOX_STATUS);
  236. }
  237. } else {
  238. while (length > 8) {
  239. if (status & EESOX_STAT_DMA) {
  240. unsigned long l1, l2;
  241. l1 = *buffer.l++;
  242. l2 = *buffer.l++;
  243. outw(l1, info->dmaarea);
  244. outw(l1 >> 16, info->dmaarea);
  245. outw(l2, info->dmaarea);
  246. outw(l2 >> 16, info->dmaarea);
  247. length -= 8;
  248. } else if (status & EESOX_STAT_INTR)
  249. goto end;
  250. status = inb(host->io_port + EESOX_STATUS);
  251. }
  252. while (length > 1) {
  253. if (status & EESOX_STAT_DMA) {
  254. outw(*buffer.s++, info->dmaarea);
  255. length -= 2;
  256. } else if (status & EESOX_STAT_INTR)
  257. goto end;
  258. status = inb(host->io_port + EESOX_STATUS);
  259. }
  260. while (length > 0) {
  261. if (status & EESOX_STAT_DMA) {
  262. outw(*buffer.c++, info->dmaarea);
  263. length -= 1;
  264. } else if (status & EESOX_STAT_INTR)
  265. goto end;
  266. status = inb(host->io_port + EESOX_STATUS);
  267. }
  268. }
  269. end:
  270. }
  271. /* Prototype: int eesoxscsi_dma_stop(host, SCpnt)
  272.  * Purpose  : stops DMA/PIO
  273.  * Params   : host  - host
  274.  *       SCpnt - command
  275.  */
  276. static void
  277. eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
  278. {
  279. if (host->dma_channel != NO_DMA)
  280. disable_dma(host->dma_channel);
  281. }
  282. /* Prototype: int eesoxscsi_detect(Scsi_Host_Template * tpnt)
  283.  * Purpose  : initialises EESOX SCSI driver
  284.  * Params   : tpnt - template for this SCSI adapter
  285.  * Returns  : >0 if host found, 0 otherwise.
  286.  */
  287. int
  288. eesoxscsi_detect(Scsi_Host_Template *tpnt)
  289. {
  290. static const card_ids eesoxscsi_cids[] =
  291. { EESOXSCSI_LIST, { 0xffff, 0xffff} };
  292. int count = 0;
  293. struct Scsi_Host *host;
  294.   
  295. tpnt->proc_name = "eesox";
  296. memset(ecs, 0, sizeof (ecs));
  297. ecard_startfind();
  298. while(1) {
  299.      EESOXScsi_Info *info;
  300. ecs[count] = ecard_find(0, eesoxscsi_cids);
  301. if (!ecs[count])
  302. break;
  303. ecard_claim(ecs[count]);
  304. host = scsi_register(tpnt, sizeof (EESOXScsi_Info));
  305. if (!host) {
  306. ecard_release(ecs[count]);
  307. break;
  308. }
  309. host->io_port = ecard_address(ecs[count], ECARD_IOC, ECARD_FAST);
  310. host->irq = ecs[count]->irq;
  311. host->dma_channel = ecs[count]->dma;
  312. info = (EESOXScsi_Info *)host->hostdata;
  313. info->control.io_port = host->io_port + EESOX_CONTROL;
  314. info->control.control = term[count] ? EESOX_TERM_ENABLE : 0;
  315. outb(info->control.control, info->control.io_port);
  316. ecs[count]->irqaddr = (unsigned char *)
  317.     ioaddr(host->io_port + EESOX_STATUS);
  318. ecs[count]->irqmask = EESOX_STAT_INTR;
  319. ecs[count]->irq_data = &info->control;
  320. ecs[count]->ops = (expansioncard_ops_t *)&eesoxscsi_ops;
  321. info->info.scsi.io_port = host->io_port + EESOX_FAS216_OFFSET;
  322. info->info.scsi.io_shift = EESOX_FAS216_SHIFT;
  323. info->info.scsi.irq = host->irq;
  324. info->info.ifcfg.clockrate = EESOX_XTALFREQ;
  325. info->info.ifcfg.select_timeout = 255;
  326. info->info.ifcfg.asyncperiod = EESOX_ASYNC_PERIOD;
  327. info->info.ifcfg.sync_max_depth = EESOX_SYNC_DEPTH;
  328. info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
  329. info->info.ifcfg.disconnect_ok = 1;
  330. info->info.ifcfg.wide_max_size = 0;
  331. info->info.dma.setup = eesoxscsi_dma_setup;
  332. info->info.dma.pseudo = eesoxscsi_dma_pseudo;
  333. info->info.dma.stop = eesoxscsi_dma_stop;
  334. info->dmaarea = host->io_port + EESOX_DMA_OFFSET;
  335. request_region(host->io_port + EESOX_FAS216_OFFSET,
  336. 16 << EESOX_FAS216_SHIFT, "eesox2-fas");
  337. if (host->irq != NO_IRQ &&
  338.     request_irq(host->irq, eesoxscsi_intr,
  339. SA_INTERRUPT, "eesox", host)) {
  340. printk("scsi%d: IRQ%d not free, interrupts disabledn",
  341.        host->host_no, host->irq);
  342. host->irq = NO_IRQ;
  343. }
  344. if (host->dma_channel != NO_DMA &&
  345.     request_dma(host->dma_channel, "eesox")) {
  346. printk("scsi%d: DMA%d not free, DMA disabledn",
  347.        host->host_no, host->dma_channel);
  348. host->dma_channel = NO_DMA;
  349. }
  350. fas216_init(host);
  351. ++count;
  352. }
  353. return count;
  354. }
  355. /* Prototype: int eesoxscsi_release(struct Scsi_Host * host)
  356.  * Purpose  : releases all resources used by this adapter
  357.  * Params   : host - driver host structure to return info for.
  358.  */
  359. int eesoxscsi_release(struct Scsi_Host *host)
  360. {
  361. int i;
  362. fas216_release(host);
  363. if (host->irq != NO_IRQ)
  364. free_irq(host->irq, host);
  365. if (host->dma_channel != NO_DMA)
  366. free_dma(host->dma_channel);
  367. release_region(host->io_port + EESOX_FAS216_OFFSET, 16 << EESOX_FAS216_SHIFT);
  368. for (i = 0; i < MAX_ECARDS; i++)
  369. if (ecs[i] &&
  370.     host->io_port == ecard_address(ecs[i], ECARD_IOC, ECARD_FAST))
  371. ecard_release(ecs[i]);
  372. return 0;
  373. }
  374. /* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host)
  375.  * Purpose  : returns a descriptive string about this interface,
  376.  * Params   : host - driver host structure to return info for.
  377.  * Returns  : pointer to a static buffer containing null terminated string.
  378.  */
  379. const char *eesoxscsi_info(struct Scsi_Host *host)
  380. {
  381. EESOXScsi_Info *info = (EESOXScsi_Info *)host->hostdata;
  382. static char string[100], *p;
  383. p = string;
  384. p += sprintf(p, "%s ", host->hostt->name);
  385. p += fas216_info(&info->info, p);
  386. p += sprintf(p, "v%d.%d.%d terminators o%s",
  387.      VER_MAJOR, VER_MINOR, VER_PATCH,
  388.      info->control.control & EESOX_TERM_ENABLE ? "n" : "ff");
  389. return string;
  390. }
  391. /* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
  392.  * Purpose  : Set a driver specific function
  393.  * Params   : host   - host to setup
  394.  *          : buffer - buffer containing string describing operation
  395.  *          : length - length of string
  396.  * Returns  : -EINVAL, or 0
  397.  */
  398. static int
  399. eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
  400. {
  401. int ret = length;
  402. if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) {
  403. buffer += 9;
  404. length -= 9;
  405. if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
  406. if (buffer[5] == '1')
  407. eesoxscsi_terminator_ctl(host, 1);
  408. else if (buffer[5] == '0')
  409. eesoxscsi_terminator_ctl(host, 0);
  410. else
  411. ret = -EINVAL;
  412. } else
  413. ret = -EINVAL;
  414. } else
  415. ret = -EINVAL;
  416. return ret;
  417. }
  418. /* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
  419.  *       int length, int host_no, int inout)
  420.  * Purpose  : Return information about the driver to a user process accessing
  421.  *       the /proc filesystem.
  422.  * Params   : buffer - a buffer to write information to
  423.  *       start  - a pointer into this buffer set by this routine to the start
  424.  *        of the required information.
  425.  *       offset - offset into information that we have read upto.
  426.  *       length - length of buffer
  427.  *       host_no - host number to return information for
  428.  *       inout  - 0 for reading, 1 for writing.
  429.  * Returns  : length of data written to buffer.
  430.  */
  431. int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
  432.     int length, int host_no, int inout)
  433. {
  434. int pos, begin;
  435. struct Scsi_Host *host = scsi_hostlist;
  436. EESOXScsi_Info *info;
  437. Scsi_Device *scd;
  438. while (host) {
  439. if (host->host_no == host_no)
  440. break;
  441. host = host->next;
  442. }
  443. if (!host)
  444. return 0;
  445. if (inout == 1)
  446. return eesoxscsi_set_proc_info(host, buffer, length);
  447. info = (EESOXScsi_Info *)host->hostdata;
  448. begin = 0;
  449. pos = sprintf(buffer,
  450. "EESOX SCSI driver version %d.%d.%dn",
  451. VER_MAJOR, VER_MINOR, VER_PATCH);
  452. pos += fas216_print_host(&info->info, buffer + pos);
  453. pos += sprintf(buffer + pos, "Term    : o%sn",
  454. info->control.control & EESOX_TERM_ENABLE ? "n" : "ff");
  455. pos += fas216_print_stats(&info->info, buffer + pos);
  456. pos += sprintf (buffer+pos, "nAttached devices:n");
  457. for (scd = host->host_queue; scd; scd = scd->next) {
  458. int len;
  459. proc_print_scsidevice (scd, buffer, &len, pos);
  460. pos += len;
  461. pos += sprintf (buffer+pos, "Extensions: ");
  462. if (scd->tagged_supported)
  463. pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
  464. scd->tagged_queue ? "en" : "dis",
  465. scd->current_tag);
  466. pos += sprintf (buffer+pos, "n");
  467. if (pos + begin < offset) {
  468. begin += pos;
  469. pos = 0;
  470. }
  471. }
  472. *start = buffer + (offset - begin);
  473. pos -= offset - begin;
  474. if (pos > length)
  475. pos = length;
  476. return pos;
  477. }
  478. static Scsi_Host_Template eesox_template = {
  479. module: THIS_MODULE,
  480. proc_info: eesoxscsi_proc_info,
  481. name: "EESOX SCSI",
  482. detect: eesoxscsi_detect,
  483. release: eesoxscsi_release,
  484. info: eesoxscsi_info,
  485. bios_param: scsicam_bios_param,
  486. can_queue: 1,
  487. this_id: 7,
  488. sg_tablesize: SG_ALL,
  489. cmd_per_lun: 1,
  490. use_clustering: DISABLE_CLUSTERING,
  491. command: fas216_command,
  492. queuecommand: fas216_queue_command,
  493. eh_host_reset_handler: fas216_eh_host_reset,
  494. eh_bus_reset_handler: fas216_eh_bus_reset,
  495. eh_device_reset_handler: fas216_eh_device_reset,
  496. eh_abort_handler: fas216_eh_abort,
  497. use_new_eh_code: 1
  498. };
  499. static int __init eesox_init(void)
  500. {
  501. scsi_register_module(MODULE_SCSI_HA, &eesox_template);
  502. if (eesox_template.present)
  503. return 0;
  504. scsi_unregister_module(MODULE_SCSI_HA, &eesox_template);
  505. return -ENODEV;
  506. }
  507. static void __exit eesox_exit(void)
  508. {
  509. scsi_unregister_module(MODULE_SCSI_HA, &eesox_template);
  510. }
  511. module_init(eesox_init);
  512. module_exit(eesox_exit);
  513. MODULE_AUTHOR("Russell King");
  514. MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines");
  515. MODULE_PARM(term, "1-8i");
  516. MODULE_PARM_DESC(term, "SCSI bus termination");
  517. MODULE_LICENSE("GPL");
  518. EXPORT_NO_SYMBOLS;