diff options
author | peter fuerst <post@pfrst.de> | 2007-02-12 09:20:15 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2007-02-16 10:22:11 -0500 |
commit | a5d8421b2f03e46f02cc02066b186fdbc0f590a6 (patch) | |
tree | fe517aae7d78c85af15c3a23810044544a576e52 /drivers/scsi | |
parent | 7de970e11fb832a56c897276967fb0e49f59b313 (diff) |
[SCSI] wd33c93: Fast SCSI with WD33C93B
Attached are patches, which help to utilize more of the WD33C93B SCSI
controller's capabilities.
1) Added/changed all the necessary code to enable Burst Mode DMA. Only
Single Byte DMA was used before.
2) Added/changed all the necessary code to enable Fast-10 SCSI transfers.
3) The original driver inadvertently used a transfer period of 1000-800ns
(the lowest possible transfer rate) for asynchronous data transfers,
instead of the (configurable) default period intended for this purpose,
if the target responded to a SDTR not with a Reject-message, but with
a zero-SDTR. This issue was fixed.
Moreover, in case of a Reject the driver used the default-period's
initialization-value instead of its (maybe smaller) current value. The
missing assignment was added.
4) The driver's commandline- and proc-file-interface was augmented to
handle the new options properly.
The WD33C93 manual, found at
http://www.datasheet.in/datasheet-html/W/D/3/WD33C93B_WesternDigital.pdf.html,
was very helpful.
Signed-off-by: peter fuerst <post@pfrst.de>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/wd33c93.c | 321 | ||||
-rw-r--r-- | drivers/scsi/wd33c93.h | 6 |
2 files changed, 248 insertions, 79 deletions
diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 835751600e93..fa4e08e508ad 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c | |||
@@ -69,6 +69,11 @@ | |||
69 | * Added support for pre -A chips, which don't have advanced features | 69 | * Added support for pre -A chips, which don't have advanced features |
70 | * and will generate CSR_RESEL rather than CSR_RESEL_AM. | 70 | * and will generate CSR_RESEL rather than CSR_RESEL_AM. |
71 | * Richard Hirst <richard@sleepie.demon.co.uk> August 2000 | 71 | * Richard Hirst <richard@sleepie.demon.co.uk> August 2000 |
72 | * | ||
73 | * Added support for Burst Mode DMA and Fast SCSI. Enabled the use of | ||
74 | * default_sx_per for asynchronous data transfers. Added adjustment | ||
75 | * of transfer periods in sx_table to the actual input-clock. | ||
76 | * peter fuerst <post@pfrst.de> February 2007 | ||
72 | */ | 77 | */ |
73 | 78 | ||
74 | #include <linux/module.h> | 79 | #include <linux/module.h> |
@@ -86,9 +91,11 @@ | |||
86 | 91 | ||
87 | #include "wd33c93.h" | 92 | #include "wd33c93.h" |
88 | 93 | ||
94 | #define optimum_sx_per(hostdata) (hostdata)->sx_table[1].period_ns | ||
95 | |||
89 | 96 | ||
90 | #define WD33C93_VERSION "1.26" | 97 | #define WD33C93_VERSION "1.26++" |
91 | #define WD33C93_DATE "22/Feb/2003" | 98 | #define WD33C93_DATE "10/Feb/2007" |
92 | 99 | ||
93 | MODULE_AUTHOR("John Shifflett"); | 100 | MODULE_AUTHOR("John Shifflett"); |
94 | MODULE_DESCRIPTION("Generic WD33C93 SCSI driver"); | 101 | MODULE_DESCRIPTION("Generic WD33C93 SCSI driver"); |
@@ -122,6 +129,13 @@ MODULE_LICENSE("GPL"); | |||
122 | * defines in wd33c93.h | 129 | * defines in wd33c93.h |
123 | * - clock:x -x = clock input in MHz for WD33c93 chip. Normal values | 130 | * - clock:x -x = clock input in MHz for WD33c93 chip. Normal values |
124 | * would be from 8 through 20. Default is 8. | 131 | * would be from 8 through 20. Default is 8. |
132 | * - burst:x -x = 1 to use Burst Mode (or Demand-Mode) DMA, x = 0 to use | ||
133 | * Single Byte DMA, which is the default. Argument is | ||
134 | * optional - if not present, same as "burst:1". | ||
135 | * - fast:x -x = 1 to enable Fast SCSI, which is only effective with | ||
136 | * input-clock divisor 4 (WD33C93_FS_16_20), x = 0 to disable | ||
137 | * it, which is the default. Argument is optional - if not | ||
138 | * present, same as "fast:1". | ||
125 | * - next -No argument. Used to separate blocks of keywords when | 139 | * - next -No argument. Used to separate blocks of keywords when |
126 | * there's more than one host adapter in the system. | 140 | * there's more than one host adapter in the system. |
127 | * | 141 | * |
@@ -148,7 +162,7 @@ MODULE_LICENSE("GPL"); | |||
148 | */ | 162 | */ |
149 | 163 | ||
150 | /* Normally, no defaults are specified */ | 164 | /* Normally, no defaults are specified */ |
151 | static char *setup_args[] = { "", "", "", "", "", "", "", "", "" }; | 165 | static char *setup_args[] = { "", "", "", "", "", "", "", "", "", "" }; |
152 | 166 | ||
153 | static char *setup_strings; | 167 | static char *setup_strings; |
154 | module_param(setup_strings, charp, 0); | 168 | module_param(setup_strings, charp, 0); |
@@ -298,20 +312,8 @@ read_1_byte(const wd33c93_regs regs) | |||
298 | return x; | 312 | return x; |
299 | } | 313 | } |
300 | 314 | ||
301 | static struct sx_period sx_table[] = { | ||
302 | {1, 0x20}, | ||
303 | {252, 0x20}, | ||
304 | {376, 0x30}, | ||
305 | {500, 0x40}, | ||
306 | {624, 0x50}, | ||
307 | {752, 0x60}, | ||
308 | {876, 0x70}, | ||
309 | {1000, 0x00}, | ||
310 | {0, 0} | ||
311 | }; | ||
312 | |||
313 | static int | 315 | static int |
314 | round_period(unsigned int period) | 316 | round_period(unsigned int period, const struct sx_period *sx_table) |
315 | { | 317 | { |
316 | int x; | 318 | int x; |
317 | 319 | ||
@@ -324,17 +326,49 @@ round_period(unsigned int period) | |||
324 | return 7; | 326 | return 7; |
325 | } | 327 | } |
326 | 328 | ||
329 | /* | ||
330 | * Calculate Synchronous Transfer Register value from SDTR code. | ||
331 | */ | ||
327 | static uchar | 332 | static uchar |
328 | calc_sync_xfer(unsigned int period, unsigned int offset) | 333 | calc_sync_xfer(unsigned int period, unsigned int offset, unsigned int fast, |
334 | const struct sx_period *sx_table) | ||
329 | { | 335 | { |
336 | /* When doing Fast SCSI synchronous data transfers, the corresponding | ||
337 | * value in 'sx_table' is two times the actually used transfer period. | ||
338 | */ | ||
330 | uchar result; | 339 | uchar result; |
331 | 340 | ||
341 | if (offset && fast) { | ||
342 | fast = STR_FSS; | ||
343 | period *= 2; | ||
344 | } else { | ||
345 | fast = 0; | ||
346 | } | ||
332 | period *= 4; /* convert SDTR code to ns */ | 347 | period *= 4; /* convert SDTR code to ns */ |
333 | result = sx_table[round_period(period)].reg_value; | 348 | result = sx_table[round_period(period,sx_table)].reg_value; |
334 | result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF; | 349 | result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF; |
350 | result |= fast; | ||
335 | return result; | 351 | return result; |
336 | } | 352 | } |
337 | 353 | ||
354 | /* | ||
355 | * Calculate SDTR code bytes [3],[4] from period and offset. | ||
356 | */ | ||
357 | static inline void | ||
358 | calc_sync_msg(unsigned int period, unsigned int offset, unsigned int fast, | ||
359 | uchar msg[2]) | ||
360 | { | ||
361 | /* 'period' is a "normal"-mode value, like the ones in 'sx_table'. The | ||
362 | * actually used transfer period for Fast SCSI synchronous data | ||
363 | * transfers is half that value. | ||
364 | */ | ||
365 | period /= 4; | ||
366 | if (offset && fast) | ||
367 | period /= 2; | ||
368 | msg[0] = period; | ||
369 | msg[1] = offset; | ||
370 | } | ||
371 | |||
338 | int | 372 | int |
339 | wd33c93_queuecommand(struct scsi_cmnd *cmd, | 373 | wd33c93_queuecommand(struct scsi_cmnd *cmd, |
340 | void (*done)(struct scsi_cmnd *)) | 374 | void (*done)(struct scsi_cmnd *)) |
@@ -632,7 +666,7 @@ wd33c93_execute(struct Scsi_Host *instance) | |||
632 | write_wd33c93_count(regs, | 666 | write_wd33c93_count(regs, |
633 | cmd->SCp.this_residual); | 667 | cmd->SCp.this_residual); |
634 | write_wd33c93(regs, WD_CONTROL, | 668 | write_wd33c93(regs, WD_CONTROL, |
635 | CTRL_IDI | CTRL_EDI | CTRL_DMA); | 669 | CTRL_IDI | CTRL_EDI | hostdata->dma_mode); |
636 | hostdata->dma = D_DMA_RUNNING; | 670 | hostdata->dma = D_DMA_RUNNING; |
637 | } | 671 | } |
638 | } else | 672 | } else |
@@ -712,6 +746,8 @@ transfer_bytes(const wd33c93_regs regs, struct scsi_cmnd *cmd, | |||
712 | cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) + | 746 | cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) + |
713 | cmd->SCp.buffer->offset; | 747 | cmd->SCp.buffer->offset; |
714 | } | 748 | } |
749 | if (!cmd->SCp.this_residual) /* avoid bogus setups */ | ||
750 | return; | ||
715 | 751 | ||
716 | write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, | 752 | write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, |
717 | hostdata->sync_xfer[cmd->device->id]); | 753 | hostdata->sync_xfer[cmd->device->id]); |
@@ -744,7 +780,7 @@ transfer_bytes(const wd33c93_regs regs, struct scsi_cmnd *cmd, | |||
744 | #ifdef PROC_STATISTICS | 780 | #ifdef PROC_STATISTICS |
745 | hostdata->dma_cnt++; | 781 | hostdata->dma_cnt++; |
746 | #endif | 782 | #endif |
747 | write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA); | 783 | write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | hostdata->dma_mode); |
748 | write_wd33c93_count(regs, cmd->SCp.this_residual); | 784 | write_wd33c93_count(regs, cmd->SCp.this_residual); |
749 | 785 | ||
750 | if ((hostdata->level2 >= L2_DATA) || | 786 | if ((hostdata->level2 >= L2_DATA) || |
@@ -862,9 +898,6 @@ wd33c93_intr(struct Scsi_Host *instance) | |||
862 | hostdata->outgoing_msg[0] |= 0x40; | 898 | hostdata->outgoing_msg[0] |= 0x40; |
863 | 899 | ||
864 | if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) { | 900 | if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) { |
865 | #ifdef SYNC_DEBUG | ||
866 | printk(" sending SDTR "); | ||
867 | #endif | ||
868 | 901 | ||
869 | hostdata->sync_stat[cmd->device->id] = SS_WAITING; | 902 | hostdata->sync_stat[cmd->device->id] = SS_WAITING; |
870 | 903 | ||
@@ -878,14 +911,20 @@ wd33c93_intr(struct Scsi_Host *instance) | |||
878 | hostdata->outgoing_msg[2] = 3; | 911 | hostdata->outgoing_msg[2] = 3; |
879 | hostdata->outgoing_msg[3] = EXTENDED_SDTR; | 912 | hostdata->outgoing_msg[3] = EXTENDED_SDTR; |
880 | if (hostdata->no_sync & (1 << cmd->device->id)) { | 913 | if (hostdata->no_sync & (1 << cmd->device->id)) { |
881 | hostdata->outgoing_msg[4] = | 914 | calc_sync_msg(hostdata->default_sx_per, 0, |
882 | hostdata->default_sx_per / 4; | 915 | 0, hostdata->outgoing_msg + 4); |
883 | hostdata->outgoing_msg[5] = 0; | ||
884 | } else { | 916 | } else { |
885 | hostdata->outgoing_msg[4] = OPTIMUM_SX_PER / 4; | 917 | calc_sync_msg(optimum_sx_per(hostdata), |
886 | hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF; | 918 | OPTIMUM_SX_OFF, |
919 | hostdata->fast, | ||
920 | hostdata->outgoing_msg + 4); | ||
887 | } | 921 | } |
888 | hostdata->outgoing_len = 6; | 922 | hostdata->outgoing_len = 6; |
923 | #ifdef SYNC_DEBUG | ||
924 | ucp = hostdata->outgoing_msg + 1; | ||
925 | printk(" sending SDTR %02x03%02x%02x%02x ", | ||
926 | ucp[0], ucp[2], ucp[3], ucp[4]); | ||
927 | #endif | ||
889 | } else | 928 | } else |
890 | hostdata->outgoing_len = 1; | 929 | hostdata->outgoing_len = 1; |
891 | 930 | ||
@@ -1001,8 +1040,13 @@ wd33c93_intr(struct Scsi_Host *instance) | |||
1001 | #ifdef SYNC_DEBUG | 1040 | #ifdef SYNC_DEBUG |
1002 | printk("-REJ-"); | 1041 | printk("-REJ-"); |
1003 | #endif | 1042 | #endif |
1004 | if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) | 1043 | if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) { |
1005 | hostdata->sync_stat[cmd->device->id] = SS_SET; | 1044 | hostdata->sync_stat[cmd->device->id] = SS_SET; |
1045 | /* we want default_sx_per, not DEFAULT_SX_PER */ | ||
1046 | hostdata->sync_xfer[cmd->device->id] = | ||
1047 | calc_sync_xfer(hostdata->default_sx_per | ||
1048 | / 4, 0, 0, hostdata->sx_table); | ||
1049 | } | ||
1006 | write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); | 1050 | write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); |
1007 | hostdata->state = S_CONNECTED; | 1051 | hostdata->state = S_CONNECTED; |
1008 | break; | 1052 | break; |
@@ -1022,7 +1066,10 @@ wd33c93_intr(struct Scsi_Host *instance) | |||
1022 | 1066 | ||
1023 | switch (ucp[2]) { /* what's the EXTENDED code? */ | 1067 | switch (ucp[2]) { /* what's the EXTENDED code? */ |
1024 | case EXTENDED_SDTR: | 1068 | case EXTENDED_SDTR: |
1025 | id = calc_sync_xfer(ucp[3], ucp[4]); | 1069 | /* default to default async period */ |
1070 | id = calc_sync_xfer(hostdata-> | ||
1071 | default_sx_per / 4, 0, | ||
1072 | 0, hostdata->sx_table); | ||
1026 | if (hostdata->sync_stat[cmd->device->id] != | 1073 | if (hostdata->sync_stat[cmd->device->id] != |
1027 | SS_WAITING) { | 1074 | SS_WAITING) { |
1028 | 1075 | ||
@@ -1041,20 +1088,22 @@ wd33c93_intr(struct Scsi_Host *instance) | |||
1041 | hostdata->outgoing_msg[1] = 3; | 1088 | hostdata->outgoing_msg[1] = 3; |
1042 | hostdata->outgoing_msg[2] = | 1089 | hostdata->outgoing_msg[2] = |
1043 | EXTENDED_SDTR; | 1090 | EXTENDED_SDTR; |
1044 | hostdata->outgoing_msg[3] = | 1091 | calc_sync_msg(hostdata-> |
1045 | hostdata->default_sx_per / | 1092 | default_sx_per, 0, |
1046 | 4; | 1093 | 0, hostdata->outgoing_msg + 3); |
1047 | hostdata->outgoing_msg[4] = 0; | ||
1048 | hostdata->outgoing_len = 5; | 1094 | hostdata->outgoing_len = 5; |
1049 | hostdata->sync_xfer[cmd->device->id] = | ||
1050 | calc_sync_xfer(hostdata-> | ||
1051 | default_sx_per | ||
1052 | / 4, 0); | ||
1053 | } else { | 1095 | } else { |
1054 | hostdata->sync_xfer[cmd->device->id] = id; | 1096 | if (ucp[4]) /* well, sync transfer */ |
1097 | id = calc_sync_xfer(ucp[3], ucp[4], | ||
1098 | hostdata->fast, | ||
1099 | hostdata->sx_table); | ||
1100 | else if (ucp[3]) /* very unlikely... */ | ||
1101 | id = calc_sync_xfer(ucp[3], ucp[4], | ||
1102 | 0, hostdata->sx_table); | ||
1055 | } | 1103 | } |
1104 | hostdata->sync_xfer[cmd->device->id] = id; | ||
1056 | #ifdef SYNC_DEBUG | 1105 | #ifdef SYNC_DEBUG |
1057 | printk("sync_xfer=%02x", | 1106 | printk(" sync_xfer=%02x\n", |
1058 | hostdata->sync_xfer[cmd->device->id]); | 1107 | hostdata->sync_xfer[cmd->device->id]); |
1059 | #endif | 1108 | #endif |
1060 | hostdata->sync_stat[cmd->device->id] = | 1109 | hostdata->sync_stat[cmd->device->id] = |
@@ -1486,7 +1535,7 @@ reset_wd33c93(struct Scsi_Host *instance) | |||
1486 | write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); | 1535 | write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); |
1487 | write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, | 1536 | write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, |
1488 | calc_sync_xfer(hostdata->default_sx_per / 4, | 1537 | calc_sync_xfer(hostdata->default_sx_per / 4, |
1489 | DEFAULT_SX_OFF)); | 1538 | DEFAULT_SX_OFF, 0, hostdata->sx_table)); |
1490 | write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET); | 1539 | write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET); |
1491 | 1540 | ||
1492 | 1541 | ||
@@ -1512,6 +1561,9 @@ reset_wd33c93(struct Scsi_Host *instance) | |||
1512 | } else | 1561 | } else |
1513 | hostdata->chip = C_UNKNOWN_CHIP; | 1562 | hostdata->chip = C_UNKNOWN_CHIP; |
1514 | 1563 | ||
1564 | if (hostdata->chip != C_WD33C93B) /* Fast SCSI unavailable */ | ||
1565 | hostdata->fast = 0; | ||
1566 | |||
1515 | write_wd33c93(regs, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE); | 1567 | write_wd33c93(regs, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE); |
1516 | write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); | 1568 | write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); |
1517 | } | 1569 | } |
@@ -1533,7 +1585,8 @@ wd33c93_host_reset(struct scsi_cmnd * SCpnt) | |||
1533 | for (i = 0; i < 8; i++) { | 1585 | for (i = 0; i < 8; i++) { |
1534 | hostdata->busy[i] = 0; | 1586 | hostdata->busy[i] = 0; |
1535 | hostdata->sync_xfer[i] = | 1587 | hostdata->sync_xfer[i] = |
1536 | calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF); | 1588 | calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, |
1589 | 0, hostdata->sx_table); | ||
1537 | hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */ | 1590 | hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */ |
1538 | } | 1591 | } |
1539 | hostdata->input_Q = NULL; | 1592 | hostdata->input_Q = NULL; |
@@ -1782,6 +1835,98 @@ check_setup_args(char *key, int *flags, int *val, char *buf) | |||
1782 | return ++x; | 1835 | return ++x; |
1783 | } | 1836 | } |
1784 | 1837 | ||
1838 | /* | ||
1839 | * Calculate internal data-transfer-clock cycle from input-clock | ||
1840 | * frequency (/MHz) and fill 'sx_table'. | ||
1841 | * | ||
1842 | * The original driver used to rely on a fixed sx_table, containing periods | ||
1843 | * for (only) the lower limits of the respective input-clock-frequency ranges | ||
1844 | * (8-10/12-15/16-20 MHz). Although it seems, that no problems ocurred with | ||
1845 | * this setting so far, it might be desirable to adjust the transfer periods | ||
1846 | * closer to the really attached, possibly 25% higher, input-clock, since | ||
1847 | * - the wd33c93 may really use a significant shorter period, than it has | ||
1848 | * negotiated (eg. thrashing the target, which expects 4/8MHz, with 5/10MHz | ||
1849 | * instead). | ||
1850 | * - the wd33c93 may ask the target for a lower transfer rate, than the target | ||
1851 | * is capable of (eg. negotiating for an assumed minimum of 252ns instead of | ||
1852 | * possible 200ns, which indeed shows up in tests as an approx. 10% lower | ||
1853 | * transfer rate). | ||
1854 | */ | ||
1855 | static inline unsigned int | ||
1856 | round_4(unsigned int x) | ||
1857 | { | ||
1858 | switch (x & 3) { | ||
1859 | case 1: --x; | ||
1860 | break; | ||
1861 | case 2: ++x; | ||
1862 | case 3: ++x; | ||
1863 | } | ||
1864 | return x; | ||
1865 | } | ||
1866 | |||
1867 | static void | ||
1868 | calc_sx_table(unsigned int mhz, struct sx_period sx_table[9]) | ||
1869 | { | ||
1870 | unsigned int d, i; | ||
1871 | if (mhz < 11) | ||
1872 | d = 2; /* divisor for 8-10 MHz input-clock */ | ||
1873 | else if (mhz < 16) | ||
1874 | d = 3; /* divisor for 12-15 MHz input-clock */ | ||
1875 | else | ||
1876 | d = 4; /* divisor for 16-20 MHz input-clock */ | ||
1877 | |||
1878 | d = (100000 * d) / 2 / mhz; /* 100 x DTCC / nanosec */ | ||
1879 | |||
1880 | sx_table[0].period_ns = 1; | ||
1881 | sx_table[0].reg_value = 0x20; | ||
1882 | for (i = 1; i < 8; i++) { | ||
1883 | sx_table[i].period_ns = round_4((i+1)*d / 100); | ||
1884 | sx_table[i].reg_value = (i+1)*0x10; | ||
1885 | } | ||
1886 | sx_table[7].reg_value = 0; | ||
1887 | sx_table[8].period_ns = 0; | ||
1888 | sx_table[8].reg_value = 0; | ||
1889 | } | ||
1890 | |||
1891 | /* | ||
1892 | * check and, maybe, map an init- or "clock:"- argument. | ||
1893 | */ | ||
1894 | static uchar | ||
1895 | set_clk_freq(int freq, int *mhz) | ||
1896 | { | ||
1897 | int x = freq; | ||
1898 | if (WD33C93_FS_8_10 == freq) | ||
1899 | freq = 8; | ||
1900 | else if (WD33C93_FS_12_15 == freq) | ||
1901 | freq = 12; | ||
1902 | else if (WD33C93_FS_16_20 == freq) | ||
1903 | freq = 16; | ||
1904 | else if (freq > 7 && freq < 11) | ||
1905 | x = WD33C93_FS_8_10; | ||
1906 | else if (freq > 11 && freq < 16) | ||
1907 | x = WD33C93_FS_12_15; | ||
1908 | else if (freq > 15 && freq < 21) | ||
1909 | x = WD33C93_FS_16_20; | ||
1910 | else { | ||
1911 | /* Hmm, wouldn't it be safer to assume highest freq here? */ | ||
1912 | x = WD33C93_FS_8_10; | ||
1913 | freq = 8; | ||
1914 | } | ||
1915 | *mhz = freq; | ||
1916 | return x; | ||
1917 | } | ||
1918 | |||
1919 | /* | ||
1920 | * to be used with the resync: fast: ... options | ||
1921 | */ | ||
1922 | static inline void set_resync ( struct WD33C93_hostdata *hd, int mask ) | ||
1923 | { | ||
1924 | int i; | ||
1925 | for (i = 0; i < 8; i++) | ||
1926 | if (mask & (1 << i)) | ||
1927 | hd->sync_stat[i] = SS_UNSET; | ||
1928 | } | ||
1929 | |||
1785 | void | 1930 | void |
1786 | wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, | 1931 | wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, |
1787 | dma_setup_t setup, dma_stop_t stop, int clock_freq) | 1932 | dma_setup_t setup, dma_stop_t stop, int clock_freq) |
@@ -1798,7 +1943,8 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, | |||
1798 | hostdata = (struct WD33C93_hostdata *) instance->hostdata; | 1943 | hostdata = (struct WD33C93_hostdata *) instance->hostdata; |
1799 | 1944 | ||
1800 | hostdata->regs = regs; | 1945 | hostdata->regs = regs; |
1801 | hostdata->clock_freq = clock_freq; | 1946 | hostdata->clock_freq = set_clk_freq(clock_freq, &i); |
1947 | calc_sx_table(i, hostdata->sx_table); | ||
1802 | hostdata->dma_setup = setup; | 1948 | hostdata->dma_setup = setup; |
1803 | hostdata->dma_stop = stop; | 1949 | hostdata->dma_stop = stop; |
1804 | hostdata->dma_bounce_buffer = NULL; | 1950 | hostdata->dma_bounce_buffer = NULL; |
@@ -1806,7 +1952,8 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, | |||
1806 | for (i = 0; i < 8; i++) { | 1952 | for (i = 0; i < 8; i++) { |
1807 | hostdata->busy[i] = 0; | 1953 | hostdata->busy[i] = 0; |
1808 | hostdata->sync_xfer[i] = | 1954 | hostdata->sync_xfer[i] = |
1809 | calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF); | 1955 | calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, |
1956 | 0, hostdata->sx_table); | ||
1810 | hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */ | 1957 | hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */ |
1811 | #ifdef PROC_STATISTICS | 1958 | #ifdef PROC_STATISTICS |
1812 | hostdata->cmd_cnt[i] = 0; | 1959 | hostdata->cmd_cnt[i] = 0; |
@@ -1828,6 +1975,8 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, | |||
1828 | hostdata->default_sx_per = DEFAULT_SX_PER; | 1975 | hostdata->default_sx_per = DEFAULT_SX_PER; |
1829 | hostdata->no_sync = 0xff; /* sync defaults to off */ | 1976 | hostdata->no_sync = 0xff; /* sync defaults to off */ |
1830 | hostdata->no_dma = 0; /* default is DMA enabled */ | 1977 | hostdata->no_dma = 0; /* default is DMA enabled */ |
1978 | hostdata->fast = 0; /* default is Fast SCSI transfers disabled */ | ||
1979 | hostdata->dma_mode = CTRL_DMA; /* default is Single Byte DMA */ | ||
1831 | 1980 | ||
1832 | #ifdef PROC_INTERFACE | 1981 | #ifdef PROC_INTERFACE |
1833 | hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS | | 1982 | hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS | |
@@ -1839,6 +1988,11 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, | |||
1839 | #endif | 1988 | #endif |
1840 | #endif | 1989 | #endif |
1841 | 1990 | ||
1991 | if (check_setup_args("clock", &flags, &val, buf)) { | ||
1992 | hostdata->clock_freq = set_clk_freq(val, &val); | ||
1993 | calc_sx_table(val, hostdata->sx_table); | ||
1994 | } | ||
1995 | |||
1842 | if (check_setup_args("nosync", &flags, &val, buf)) | 1996 | if (check_setup_args("nosync", &flags, &val, buf)) |
1843 | hostdata->no_sync = val; | 1997 | hostdata->no_sync = val; |
1844 | 1998 | ||
@@ -1847,7 +2001,8 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, | |||
1847 | 2001 | ||
1848 | if (check_setup_args("period", &flags, &val, buf)) | 2002 | if (check_setup_args("period", &flags, &val, buf)) |
1849 | hostdata->default_sx_per = | 2003 | hostdata->default_sx_per = |
1850 | sx_table[round_period((unsigned int) val)].period_ns; | 2004 | hostdata->sx_table[round_period((unsigned int) val, |
2005 | hostdata->sx_table)].period_ns; | ||
1851 | 2006 | ||
1852 | if (check_setup_args("disconnect", &flags, &val, buf)) { | 2007 | if (check_setup_args("disconnect", &flags, &val, buf)) { |
1853 | if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) | 2008 | if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) |
@@ -1862,17 +2017,12 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, | |||
1862 | if (check_setup_args("debug", &flags, &val, buf)) | 2017 | if (check_setup_args("debug", &flags, &val, buf)) |
1863 | hostdata->args = val & DB_MASK; | 2018 | hostdata->args = val & DB_MASK; |
1864 | 2019 | ||
1865 | if (check_setup_args("clock", &flags, &val, buf)) { | 2020 | if (check_setup_args("burst", &flags, &val, buf)) |
1866 | if (val > 7 && val < 11) | 2021 | hostdata->dma_mode = val ? CTRL_BURST:CTRL_DMA; |
1867 | val = WD33C93_FS_8_10; | 2022 | |
1868 | else if (val > 11 && val < 16) | 2023 | if (WD33C93_FS_16_20 == hostdata->clock_freq /* divisor 4 */ |
1869 | val = WD33C93_FS_12_15; | 2024 | && check_setup_args("fast", &flags, &val, buf)) |
1870 | else if (val > 15 && val < 21) | 2025 | hostdata->fast = !!val; |
1871 | val = WD33C93_FS_16_20; | ||
1872 | else | ||
1873 | val = WD33C93_FS_8_10; | ||
1874 | hostdata->clock_freq = val; | ||
1875 | } | ||
1876 | 2026 | ||
1877 | if ((i = check_setup_args("next", &flags, &val, buf))) { | 2027 | if ((i = check_setup_args("next", &flags, &val, buf))) { |
1878 | while (i) | 2028 | while (i) |
@@ -1917,53 +2067,65 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off | |||
1917 | char tbuf[128]; | 2067 | char tbuf[128]; |
1918 | struct WD33C93_hostdata *hd; | 2068 | struct WD33C93_hostdata *hd; |
1919 | struct scsi_cmnd *cmd; | 2069 | struct scsi_cmnd *cmd; |
1920 | int x, i; | 2070 | int x; |
1921 | static int stop = 0; | 2071 | static int stop = 0; |
1922 | 2072 | ||
1923 | hd = (struct WD33C93_hostdata *) instance->hostdata; | 2073 | hd = (struct WD33C93_hostdata *) instance->hostdata; |
1924 | 2074 | ||
1925 | /* If 'in' is TRUE we need to _read_ the proc file. We accept the following | 2075 | /* If 'in' is TRUE we need to _read_ the proc file. We accept the following |
1926 | * keywords (same format as command-line, but only ONE per read): | 2076 | * keywords (same format as command-line, but arguments are not optional): |
1927 | * debug | 2077 | * debug |
1928 | * disconnect | 2078 | * disconnect |
1929 | * period | 2079 | * period |
1930 | * resync | 2080 | * resync |
1931 | * proc | 2081 | * proc |
1932 | * nodma | 2082 | * nodma |
2083 | * level2 | ||
2084 | * burst | ||
2085 | * fast | ||
2086 | * nosync | ||
1933 | */ | 2087 | */ |
1934 | 2088 | ||
1935 | if (in) { | 2089 | if (in) { |
1936 | buf[len] = '\0'; | 2090 | buf[len] = '\0'; |
1937 | bp = buf; | 2091 | for (bp = buf; *bp; ) { |
2092 | while (',' == *bp || ' ' == *bp) | ||
2093 | ++bp; | ||
1938 | if (!strncmp(bp, "debug:", 6)) { | 2094 | if (!strncmp(bp, "debug:", 6)) { |
1939 | bp += 6; | 2095 | hd->args = simple_strtoul(bp+6, &bp, 0) & DB_MASK; |
1940 | hd->args = simple_strtoul(bp, NULL, 0) & DB_MASK; | ||
1941 | } else if (!strncmp(bp, "disconnect:", 11)) { | 2096 | } else if (!strncmp(bp, "disconnect:", 11)) { |
1942 | bp += 11; | 2097 | x = simple_strtoul(bp+11, &bp, 0); |
1943 | x = simple_strtoul(bp, NULL, 0); | ||
1944 | if (x < DIS_NEVER || x > DIS_ALWAYS) | 2098 | if (x < DIS_NEVER || x > DIS_ALWAYS) |
1945 | x = DIS_ADAPTIVE; | 2099 | x = DIS_ADAPTIVE; |
1946 | hd->disconnect = x; | 2100 | hd->disconnect = x; |
1947 | } else if (!strncmp(bp, "period:", 7)) { | 2101 | } else if (!strncmp(bp, "period:", 7)) { |
1948 | bp += 7; | 2102 | x = simple_strtoul(bp+7, &bp, 0); |
1949 | x = simple_strtoul(bp, NULL, 0); | ||
1950 | hd->default_sx_per = | 2103 | hd->default_sx_per = |
1951 | sx_table[round_period((unsigned int) x)].period_ns; | 2104 | hd->sx_table[round_period((unsigned int) x, |
2105 | hd->sx_table)].period_ns; | ||
1952 | } else if (!strncmp(bp, "resync:", 7)) { | 2106 | } else if (!strncmp(bp, "resync:", 7)) { |
1953 | bp += 7; | 2107 | set_resync(hd, (int)simple_strtoul(bp+7, &bp, 0)); |
1954 | x = simple_strtoul(bp, NULL, 0); | ||
1955 | for (i = 0; i < 7; i++) | ||
1956 | if (x & (1 << i)) | ||
1957 | hd->sync_stat[i] = SS_UNSET; | ||
1958 | } else if (!strncmp(bp, "proc:", 5)) { | 2108 | } else if (!strncmp(bp, "proc:", 5)) { |
1959 | bp += 5; | 2109 | hd->proc = simple_strtoul(bp+5, &bp, 0); |
1960 | hd->proc = simple_strtoul(bp, NULL, 0); | ||
1961 | } else if (!strncmp(bp, "nodma:", 6)) { | 2110 | } else if (!strncmp(bp, "nodma:", 6)) { |
1962 | bp += 6; | 2111 | hd->no_dma = simple_strtoul(bp+6, &bp, 0); |
1963 | hd->no_dma = simple_strtoul(bp, NULL, 0); | ||
1964 | } else if (!strncmp(bp, "level2:", 7)) { | 2112 | } else if (!strncmp(bp, "level2:", 7)) { |
1965 | bp += 7; | 2113 | hd->level2 = simple_strtoul(bp+7, &bp, 0); |
1966 | hd->level2 = simple_strtoul(bp, NULL, 0); | 2114 | } else if (!strncmp(bp, "burst:", 6)) { |
2115 | hd->dma_mode = | ||
2116 | simple_strtol(bp+6, &bp, 0) ? CTRL_BURST:CTRL_DMA; | ||
2117 | } else if (!strncmp(bp, "fast:", 5)) { | ||
2118 | x = !!simple_strtol(bp+5, &bp, 0); | ||
2119 | if (x != hd->fast) | ||
2120 | set_resync(hd, 0xff); | ||
2121 | hd->fast = x; | ||
2122 | } else if (!strncmp(bp, "nosync:", 7)) { | ||
2123 | x = simple_strtoul(bp+7, &bp, 0); | ||
2124 | set_resync(hd, x ^ hd->no_sync); | ||
2125 | hd->no_sync = x; | ||
2126 | } else { | ||
2127 | break; /* unknown keyword,syntax-error,... */ | ||
2128 | } | ||
1967 | } | 2129 | } |
1968 | return len; | 2130 | return len; |
1969 | } | 2131 | } |
@@ -1977,8 +2139,9 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off | |||
1977 | strcat(bp, tbuf); | 2139 | strcat(bp, tbuf); |
1978 | } | 2140 | } |
1979 | if (hd->proc & PR_INFO) { | 2141 | if (hd->proc & PR_INFO) { |
1980 | sprintf(tbuf, "\nclock_freq=%02x no_sync=%02x no_dma=%d", | 2142 | sprintf(tbuf, "\nclock_freq=%02x no_sync=%02x no_dma=%d" |
1981 | hd->clock_freq, hd->no_sync, hd->no_dma); | 2143 | " dma_mode=%02x fast=%d", |
2144 | hd->clock_freq, hd->no_sync, hd->no_dma, hd->dma_mode, hd->fast); | ||
1982 | strcat(bp, tbuf); | 2145 | strcat(bp, tbuf); |
1983 | strcat(bp, "\nsync_xfer[] = "); | 2146 | strcat(bp, "\nsync_xfer[] = "); |
1984 | for (x = 0; x < 7; x++) { | 2147 | for (x = 0; x < 7; x++) { |
diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h index edcb0365cf0c..61ffb860dacc 100644 --- a/drivers/scsi/wd33c93.h +++ b/drivers/scsi/wd33c93.h | |||
@@ -155,6 +155,9 @@ | |||
155 | #define WD33C93_FS_12_15 OWNID_FS_12 | 155 | #define WD33C93_FS_12_15 OWNID_FS_12 |
156 | #define WD33C93_FS_16_20 OWNID_FS_16 | 156 | #define WD33C93_FS_16_20 OWNID_FS_16 |
157 | 157 | ||
158 | /* pass input-clock explicitely. accepted mhz values are 8-10,12-20 */ | ||
159 | #define WD33C93_FS_MHZ(mhz) (mhz) | ||
160 | |||
158 | /* Control register */ | 161 | /* Control register */ |
159 | #define CTRL_HSP 0x01 | 162 | #define CTRL_HSP 0x01 |
160 | #define CTRL_HA 0x02 | 163 | #define CTRL_HA 0x02 |
@@ -253,6 +256,9 @@ struct WD33C93_hostdata { | |||
253 | uchar sync_stat[8]; /* status of sync negotiation per target */ | 256 | uchar sync_stat[8]; /* status of sync negotiation per target */ |
254 | uchar no_sync; /* bitmask: don't do sync on these targets */ | 257 | uchar no_sync; /* bitmask: don't do sync on these targets */ |
255 | uchar no_dma; /* set this flag to disable DMA */ | 258 | uchar no_dma; /* set this flag to disable DMA */ |
259 | uchar dma_mode; /* DMA Burst Mode or Single Byte DMA */ | ||
260 | uchar fast; /* set this flag to enable Fast SCSI */ | ||
261 | struct sx_period sx_table[9]; /* transfer periods for actual DTC-setting */ | ||
256 | #ifdef PROC_INTERFACE | 262 | #ifdef PROC_INTERFACE |
257 | uchar proc; /* bitmask: what's in proc output */ | 263 | uchar proc; /* bitmask: what's in proc output */ |
258 | #ifdef PROC_STATISTICS | 264 | #ifdef PROC_STATISTICS |