cumana_2.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:16k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

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