diff options
Diffstat (limited to 'drivers/block/paride/epat.c')
-rw-r--r-- | drivers/block/paride/epat.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/drivers/block/paride/epat.c b/drivers/block/paride/epat.c new file mode 100644 index 000000000000..55d1c0a1fb90 --- /dev/null +++ b/drivers/block/paride/epat.c | |||
@@ -0,0 +1,340 @@ | |||
1 | /* | ||
2 | epat.c (c) 1997-8 Grant R. Guenther <grant@torque.net> | ||
3 | Under the terms of the GNU General Public License. | ||
4 | |||
5 | This is the low level protocol driver for the EPAT parallel | ||
6 | to IDE adapter from Shuttle Technologies. This adapter is | ||
7 | used in many popular parallel port disk products such as the | ||
8 | SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk. | ||
9 | |||
10 | */ | ||
11 | |||
12 | /* Changes: | ||
13 | |||
14 | 1.01 GRG 1998.05.06 init_proto, release_proto | ||
15 | 1.02 Joshua b. Jore CPP(renamed), epat_connect, epat_disconnect | ||
16 | |||
17 | */ | ||
18 | |||
19 | #define EPAT_VERSION "1.02" | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/wait.h> | ||
27 | #include <asm/io.h> | ||
28 | |||
29 | #include "paride.h" | ||
30 | |||
31 | #define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) | ||
32 | #define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) | ||
33 | |||
34 | static int epatc8; | ||
35 | |||
36 | module_param(epatc8, int, 0); | ||
37 | MODULE_PARM_DESC(epatc8, "support for the Shuttle EP1284 chip, " | ||
38 | "used in any recent Imation SuperDisk (LS-120) drive."); | ||
39 | |||
40 | /* cont = 0 IDE register file | ||
41 | cont = 1 IDE control registers | ||
42 | cont = 2 internal EPAT registers | ||
43 | */ | ||
44 | |||
45 | static int cont_map[3] = { 0x18, 0x10, 0 }; | ||
46 | |||
47 | static void epat_write_regr( PIA *pi, int cont, int regr, int val) | ||
48 | |||
49 | { int r; | ||
50 | |||
51 | r = regr + cont_map[cont]; | ||
52 | |||
53 | switch (pi->mode) { | ||
54 | |||
55 | case 0: | ||
56 | case 1: | ||
57 | case 2: w0(0x60+r); w2(1); w0(val); w2(4); | ||
58 | break; | ||
59 | |||
60 | case 3: | ||
61 | case 4: | ||
62 | case 5: w3(0x40+r); w4(val); | ||
63 | break; | ||
64 | |||
65 | } | ||
66 | } | ||
67 | |||
68 | static int epat_read_regr( PIA *pi, int cont, int regr ) | ||
69 | |||
70 | { int a, b, r; | ||
71 | |||
72 | r = regr + cont_map[cont]; | ||
73 | |||
74 | switch (pi->mode) { | ||
75 | |||
76 | case 0: w0(r); w2(1); w2(3); | ||
77 | a = r1(); w2(4); b = r1(); | ||
78 | return j44(a,b); | ||
79 | |||
80 | case 1: w0(0x40+r); w2(1); w2(4); | ||
81 | a = r1(); b = r2(); w0(0xff); | ||
82 | return j53(a,b); | ||
83 | |||
84 | case 2: w0(0x20+r); w2(1); w2(0x25); | ||
85 | a = r0(); w2(4); | ||
86 | return a; | ||
87 | |||
88 | case 3: | ||
89 | case 4: | ||
90 | case 5: w3(r); w2(0x24); a = r4(); w2(4); | ||
91 | return a; | ||
92 | |||
93 | } | ||
94 | return -1; /* never gets here */ | ||
95 | } | ||
96 | |||
97 | static void epat_read_block( PIA *pi, char * buf, int count ) | ||
98 | |||
99 | { int k, ph, a, b; | ||
100 | |||
101 | switch (pi->mode) { | ||
102 | |||
103 | case 0: w0(7); w2(1); w2(3); w0(0xff); | ||
104 | ph = 0; | ||
105 | for(k=0;k<count;k++) { | ||
106 | if (k == count-1) w0(0xfd); | ||
107 | w2(6+ph); a = r1(); | ||
108 | if (a & 8) b = a; | ||
109 | else { w2(4+ph); b = r1(); } | ||
110 | buf[k] = j44(a,b); | ||
111 | ph = 1 - ph; | ||
112 | } | ||
113 | w0(0); w2(4); | ||
114 | break; | ||
115 | |||
116 | case 1: w0(0x47); w2(1); w2(5); w0(0xff); | ||
117 | ph = 0; | ||
118 | for(k=0;k<count;k++) { | ||
119 | if (k == count-1) w0(0xfd); | ||
120 | w2(4+ph); | ||
121 | a = r1(); b = r2(); | ||
122 | buf[k] = j53(a,b); | ||
123 | ph = 1 - ph; | ||
124 | } | ||
125 | w0(0); w2(4); | ||
126 | break; | ||
127 | |||
128 | case 2: w0(0x27); w2(1); w2(0x25); w0(0); | ||
129 | ph = 0; | ||
130 | for(k=0;k<count-1;k++) { | ||
131 | w2(0x24+ph); | ||
132 | buf[k] = r0(); | ||
133 | ph = 1 - ph; | ||
134 | } | ||
135 | w2(0x26); w2(0x27); buf[count-1] = r0(); | ||
136 | w2(0x25); w2(4); | ||
137 | break; | ||
138 | |||
139 | case 3: w3(0x80); w2(0x24); | ||
140 | for(k=0;k<count-1;k++) buf[k] = r4(); | ||
141 | w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); | ||
142 | w2(4); | ||
143 | break; | ||
144 | |||
145 | case 4: w3(0x80); w2(0x24); | ||
146 | for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w(); | ||
147 | buf[count-2] = r4(); | ||
148 | w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); | ||
149 | w2(4); | ||
150 | break; | ||
151 | |||
152 | case 5: w3(0x80); w2(0x24); | ||
153 | for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l(); | ||
154 | for(k=count-4;k<count-1;k++) buf[k] = r4(); | ||
155 | w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4(); | ||
156 | w2(4); | ||
157 | break; | ||
158 | |||
159 | } | ||
160 | } | ||
161 | |||
162 | static void epat_write_block( PIA *pi, char * buf, int count ) | ||
163 | |||
164 | { int ph, k; | ||
165 | |||
166 | switch (pi->mode) { | ||
167 | |||
168 | case 0: | ||
169 | case 1: | ||
170 | case 2: w0(0x67); w2(1); w2(5); | ||
171 | ph = 0; | ||
172 | for(k=0;k<count;k++) { | ||
173 | w0(buf[k]); | ||
174 | w2(4+ph); | ||
175 | ph = 1 - ph; | ||
176 | } | ||
177 | w2(7); w2(4); | ||
178 | break; | ||
179 | |||
180 | case 3: w3(0xc0); | ||
181 | for(k=0;k<count;k++) w4(buf[k]); | ||
182 | w2(4); | ||
183 | break; | ||
184 | |||
185 | case 4: w3(0xc0); | ||
186 | for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]); | ||
187 | w2(4); | ||
188 | break; | ||
189 | |||
190 | case 5: w3(0xc0); | ||
191 | for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]); | ||
192 | w2(4); | ||
193 | break; | ||
194 | |||
195 | } | ||
196 | } | ||
197 | |||
198 | /* these macros access the EPAT registers in native addressing */ | ||
199 | |||
200 | #define WR(r,v) epat_write_regr(pi,2,r,v) | ||
201 | #define RR(r) (epat_read_regr(pi,2,r)) | ||
202 | |||
203 | /* and these access the IDE task file */ | ||
204 | |||
205 | #define WRi(r,v) epat_write_regr(pi,0,r,v) | ||
206 | #define RRi(r) (epat_read_regr(pi,0,r)) | ||
207 | |||
208 | /* FIXME: the CPP stuff should be fixed to handle multiple EPATs on a chain */ | ||
209 | |||
210 | #define CPP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ | ||
211 | w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff); | ||
212 | |||
213 | static void epat_connect ( PIA *pi ) | ||
214 | |||
215 | { pi->saved_r0 = r0(); | ||
216 | pi->saved_r2 = r2(); | ||
217 | |||
218 | /* Initialize the chip */ | ||
219 | CPP(0); | ||
220 | |||
221 | if (epatc8) { | ||
222 | CPP(0x40);CPP(0xe0); | ||
223 | w0(0);w2(1);w2(4); | ||
224 | WR(0x8,0x12);WR(0xc,0x14);WR(0x12,0x10); | ||
225 | WR(0xe,0xf);WR(0xf,4); | ||
226 | /* WR(0xe,0xa);WR(0xf,4); */ | ||
227 | WR(0xe,0xd);WR(0xf,0); | ||
228 | /* CPP(0x30); */ | ||
229 | } | ||
230 | |||
231 | /* Connect to the chip */ | ||
232 | CPP(0xe0); | ||
233 | w0(0);w2(1);w2(4); /* Idle into SPP */ | ||
234 | if (pi->mode >= 3) { | ||
235 | w0(0);w2(1);w2(4);w2(0xc); | ||
236 | /* Request EPP */ | ||
237 | w0(0x40);w2(6);w2(7);w2(4);w2(0xc);w2(4); | ||
238 | } | ||
239 | |||
240 | if (!epatc8) { | ||
241 | WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | static void epat_disconnect (PIA *pi) | ||
246 | { CPP(0x30); | ||
247 | w0(pi->saved_r0); | ||
248 | w2(pi->saved_r2); | ||
249 | } | ||
250 | |||
251 | static int epat_test_proto( PIA *pi, char * scratch, int verbose ) | ||
252 | |||
253 | { int k, j, f, cc; | ||
254 | int e[2] = {0,0}; | ||
255 | |||
256 | epat_connect(pi); | ||
257 | cc = RR(0xd); | ||
258 | epat_disconnect(pi); | ||
259 | |||
260 | epat_connect(pi); | ||
261 | for (j=0;j<2;j++) { | ||
262 | WRi(6,0xa0+j*0x10); | ||
263 | for (k=0;k<256;k++) { | ||
264 | WRi(2,k^0xaa); | ||
265 | WRi(3,k^0x55); | ||
266 | if (RRi(2) != (k^0xaa)) e[j]++; | ||
267 | } | ||
268 | } | ||
269 | epat_disconnect(pi); | ||
270 | |||
271 | f = 0; | ||
272 | epat_connect(pi); | ||
273 | WR(0x13,1); WR(0x13,0); WR(0xa,0x11); | ||
274 | epat_read_block(pi,scratch,512); | ||
275 | |||
276 | for (k=0;k<256;k++) { | ||
277 | if ((scratch[2*k] & 0xff) != k) f++; | ||
278 | if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++; | ||
279 | } | ||
280 | epat_disconnect(pi); | ||
281 | |||
282 | if (verbose) { | ||
283 | printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n", | ||
284 | pi->device,pi->port,pi->mode,cc,e[0],e[1],f); | ||
285 | } | ||
286 | |||
287 | return (e[0] && e[1]) || f; | ||
288 | } | ||
289 | |||
290 | static void epat_log_adapter( PIA *pi, char * scratch, int verbose ) | ||
291 | |||
292 | { int ver; | ||
293 | char *mode_string[6] = | ||
294 | {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"}; | ||
295 | |||
296 | epat_connect(pi); | ||
297 | WR(0xa,0x38); /* read the version code */ | ||
298 | ver = RR(0xb); | ||
299 | epat_disconnect(pi); | ||
300 | |||
301 | printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ", | ||
302 | pi->device,EPAT_VERSION,ver,pi->port); | ||
303 | printk("mode %d (%s), delay %d\n",pi->mode, | ||
304 | mode_string[pi->mode],pi->delay); | ||
305 | |||
306 | } | ||
307 | |||
308 | static struct pi_protocol epat = { | ||
309 | .owner = THIS_MODULE, | ||
310 | .name = "epat", | ||
311 | .max_mode = 6, | ||
312 | .epp_first = 3, | ||
313 | .default_delay = 1, | ||
314 | .max_units = 1, | ||
315 | .write_regr = epat_write_regr, | ||
316 | .read_regr = epat_read_regr, | ||
317 | .write_block = epat_write_block, | ||
318 | .read_block = epat_read_block, | ||
319 | .connect = epat_connect, | ||
320 | .disconnect = epat_disconnect, | ||
321 | .test_proto = epat_test_proto, | ||
322 | .log_adapter = epat_log_adapter, | ||
323 | }; | ||
324 | |||
325 | static int __init epat_init(void) | ||
326 | { | ||
327 | #ifdef CONFIG_PARIDE_EPATC8 | ||
328 | epatc8 = 1; | ||
329 | #endif | ||
330 | return pi_register(&epat)-1; | ||
331 | } | ||
332 | |||
333 | static void __exit epat_exit(void) | ||
334 | { | ||
335 | pi_unregister(&epat); | ||
336 | } | ||
337 | |||
338 | MODULE_LICENSE("GPL"); | ||
339 | module_init(epat_init) | ||
340 | module_exit(epat_exit) | ||