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