1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * (c) 1996-1998 Grant R. Guenther <grant@torque.net>
4 *
5 * bpck.c is a low-level protocol driver for the MicroSolutions
6 * "backpack" parallel port IDE adapter.
7 */
8
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/delay.h>
12#include <linux/kernel.h>
13#include <linux/types.h>
14#include <linux/wait.h>
15#include <asm/io.h>
16#include "pata_parport.h"
17
18#undef r2
19#undef w2
20#undef PC
21
22#define PC pi->private
23#define r2() (PC=(in_p(2) & 0xff))
24#define w2(byte) {out_p(2,byte); PC = byte;}
25#define t2(pat) {PC ^= pat; out_p(2,PC);}
26#define e2() {PC &= 0xfe; out_p(2,PC);}
27#define o2() {PC |= 1; out_p(2,PC);}
28
29#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
30
31/*
32 * cont = 0 - access the IDE register file
33 * cont = 1 - access the IDE command set
34 * cont = 2 - use internal bpck register addressing
35 */
36static int cont_map[3] = { 0x40, 0x48, 0 };
37
38static int bpck_read_regr(struct pi_adapter *pi, int cont, int regr)
39{
40 int r, l, h;
41
42 r = regr + cont_map[cont];
43
44 switch (pi->mode) {
45 case 0:
46 w0(r & 0xf); w0(r); t2(2); t2(4);
47 l = r1();
48 t2(4);
49 h = r1();
50 return j44(l, h);
51 case 1:
52 w0(r & 0xf); w0(r); t2(2);
53 e2(); t2(0x20);
54 t2(4); h = r0();
55 t2(1); t2(0x20);
56 return h;
57 case 2:
58 case 3:
59 case 4:
60 w0(r); w2(9); w2(0); w2(0x20);
61 h = r4();
62 w2(0);
63 return h;
64
65 }
66 return -1;
67}
68
69static void bpck_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
70{
71 int r;
72
73 r = regr + cont_map[cont];
74
75 switch (pi->mode) {
76 case 0:
77 case 1: w0(r);
78 t2(2);
79 w0(val);
80 o2(); t2(4); t2(1);
81 break;
82 case 2:
83 case 3:
84 case 4: w0(r); w2(9); w2(0);
85 w0(val); w2(1); w2(3); w2(0);
86 break;
87
88 }
89}
90
91/* These macros access the bpck registers in native addressing */
92
93#define WR(r,v) bpck_write_regr(pi,2,r,v)
94#define RR(r) (bpck_read_regr(pi,2,r))
95
96static void bpck_write_block(struct pi_adapter *pi, char *buf, int count)
97{
98 int i;
99
100 switch (pi->mode) {
101
102 case 0:
103 WR(4, 0x40);
104 w0(0x40); t2(2); t2(1);
105 for (i = 0; i < count; i++) {
106 w0(buf[i]);
107 t2(4);
108 }
109 WR(4, 0);
110 break;
111
112 case 1:
113 WR(4, 0x50);
114 w0(0x40); t2(2); t2(1);
115 for (i = 0; i < count; i++) {
116 w0(buf[i]);
117 t2(4);
118 }
119 WR(4, 0x10);
120 break;
121
122 case 2:
123 WR(4, 0x48);
124 w0(0x40); w2(9); w2(0); w2(1);
125 for (i = 0; i < count; i++)
126 w4(buf[i]);
127 w2(0);
128 WR(4, 8);
129 break;
130
131 case 3:
132 WR(4, 0x48);
133 w0(0x40); w2(9); w2(0); w2(1);
134 for (i = 0; i < count / 2; i++)
135 w4w(((u16 *)buf)[i]);
136 w2(0);
137 WR(4, 8);
138 break;
139
140 case 4:
141 WR(4, 0x48);
142 w0(0x40); w2(9); w2(0); w2(1);
143 for (i = 0; i < count / 4; i++)
144 w4l(((u32 *)buf)[i]);
145 w2(0);
146 WR(4, 8);
147 break;
148 }
149}
150
151static void bpck_read_block(struct pi_adapter *pi, char *buf, int count)
152{
153 int i, l, h;
154
155 switch (pi->mode) {
156
157 case 0:
158 WR(4, 0x40);
159 w0(0x40); t2(2);
160 for (i = 0; i < count; i++) {
161 t2(4); l = r1();
162 t2(4); h = r1();
163 buf[i] = j44(l, h);
164 }
165 WR(4, 0);
166 break;
167
168 case 1:
169 WR(4, 0x50);
170 w0(0x40); t2(2); t2(0x20);
171 for (i = 0; i < count; i++) {
172 t2(4);
173 buf[i] = r0();
174 }
175 t2(1); t2(0x20);
176 WR(4, 0x10);
177 break;
178
179 case 2:
180 WR(4, 0x48);
181 w0(0x40); w2(9); w2(0); w2(0x20);
182 for (i = 0; i < count; i++)
183 buf[i] = r4();
184 w2(0);
185 WR(4, 8);
186 break;
187
188 case 3:
189 WR(4, 0x48);
190 w0(0x40); w2(9); w2(0); w2(0x20);
191 for (i = 0; i < count / 2; i++)
192 ((u16 *)buf)[i] = r4w();
193 w2(0);
194 WR(4, 8);
195 break;
196
197 case 4:
198 WR(4, 0x48);
199 w0(0x40); w2(9); w2(0); w2(0x20);
200 for (i = 0; i < count / 4; i++)
201 ((u32 *)buf)[i] = r4l();
202 w2(0);
203 WR(4, 8);
204 break;
205
206 }
207}
208
209static int bpck_probe_unit(struct pi_adapter *pi)
210{
211 int o1, o0, f7, id;
212 int t, s;
213
214 id = pi->unit;
215 s = 0;
216 w2(4); w2(0xe); r2(); t2(2);
217 o1 = r1()&0xf8;
218 o0 = r0();
219 w0(255-id); w2(4); w0(id);
220 t2(8); t2(8); t2(8);
221 t2(2); t = r1()&0xf8;
222 f7 = ((id % 8) == 7);
223 if ((f7) || (t != o1)) {
224 t2(2);
225 s = r1() & 0xf8;
226 }
227 if ((t == o1) && ((!f7) || (s == o1))) {
228 w2(0x4c); w0(o0);
229 return 0;
230 }
231 t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
232 return 1;
233}
234
235static void bpck_connect(struct pi_adapter *pi)
236{
237 pi->saved_r0 = r0();
238 w0(0xff-pi->unit); w2(4); w0(pi->unit);
239 t2(8); t2(8); t2(8);
240 t2(2); t2(2);
241
242 switch (pi->mode) {
243 case 0:
244 t2(8); WR(4, 0);
245 break;
246 case 1:
247 t2(8); WR(4, 0x10);
248 break;
249 case 2:
250 case 3:
251 case 4:
252 w2(0); WR(4, 8);
253 break;
254 }
255
256 WR(5,8);
257
258 /*
259 * Possibly wrong, purpose unknown (fiddle with ESS logic ???)
260 * if (pi->devtype == PI_PCD) {
261 */
262 WR(0x46, 0x10);
263 WR(0x4c, 0x38);
264 WR(0x4d, 0x88);
265 WR(0x46, 0xa0);
266 WR(0x41, 0);
267 WR(0x4e, 8);
268 /* } */
269}
270
271static void bpck_disconnect(struct pi_adapter *pi)
272{
273 w0(0);
274 if (pi->mode >= 2) {
275 w2(9); w2(0);
276 } else {
277 t2(2);
278 }
279 w2(0x4c); w0(pi->saved_r0);
280}
281
282static void bpck_force_spp(struct pi_adapter *pi)
283{
284 /* This fakes the EPP protocol to turn off EPP ... */
285 pi->saved_r0 = r0();
286 w0(0xff-pi->unit); w2(4); w0(pi->unit);
287 t2(8); t2(8); t2(8);
288 t2(2); t2(2);
289
290 w2(0);
291 w0(4); w2(9); w2(0);
292 w0(0); w2(1); w2(3); w2(0);
293 w0(0); w2(9); w2(0);
294 w2(0x4c); w0(pi->saved_r0);
295}
296
297#define TEST_LEN 16
298
299static int bpck_test_proto(struct pi_adapter *pi)
300{
301 int i, e, l, h, om;
302 char buf[TEST_LEN];
303
304 bpck_force_spp(pi);
305
306 switch (pi->mode) {
307
308 case 0:
309 bpck_connect(pi);
310 WR(0x13, 0x7f);
311 w0(0x13); t2(2);
312 for (i = 0; i < TEST_LEN; i++) {
313 t2(4); l = r1();
314 t2(4); h = r1();
315 buf[i] = j44(l, h);
316 }
317 bpck_disconnect(pi);
318 break;
319
320 case 1:
321 bpck_connect(pi);
322 WR(0x13, 0x7f);
323 w0(0x13); t2(2); t2(0x20);
324 for (i = 0; i < TEST_LEN; i++) {
325 t2(4);
326 buf[i] = r0();
327 }
328 t2(1); t2(0x20);
329 bpck_disconnect(pi);
330 break;
331
332 case 2:
333 case 3:
334 case 4:
335 om = pi->mode;
336 pi->mode = 0;
337 bpck_connect(pi);
338 WR(7, 3);
339 WR(4, 8);
340 bpck_disconnect(pi);
341
342 pi->mode = om;
343 bpck_connect(pi);
344 w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
345
346 switch (pi->mode) {
347 case 2:
348 for (i = 0; i < TEST_LEN; i++)
349 buf[i] = r4();
350 break;
351 case 3:
352 for (i = 0; i < TEST_LEN / 2; i++)
353 ((u16 *)buf)[i] = r4w();
354 break;
355 case 4:
356 for (i = 0; i < TEST_LEN / 4; i++)
357 ((u32 *)buf)[i] = r4l();
358 break;
359 }
360
361 w2(0);
362 WR(7, 0);
363 bpck_disconnect(pi);
364 break;
365
366 }
367
368 dev_dbg(&pi->dev, "bpck: 0x%x unit %d mode %d: ",
369 pi->port, pi->unit, pi->mode);
370 print_hex_dump_debug("bpck: ", DUMP_PREFIX_NONE, TEST_LEN, 1, buf,
371 TEST_LEN, false);
372
373 e = 0;
374 for (i = 0; i < TEST_LEN; i++) {
375 if (buf[i] != i + 1)
376 e++;
377 }
378
379 return e;
380}
381
382static void bpck_read_eeprom(struct pi_adapter *pi, char *buf)
383{
384 int i, j, k, p, v, f, om, od;
385
386 bpck_force_spp(pi);
387
388 om = pi->mode; od = pi->delay;
389 pi->mode = 0; pi->delay = 6;
390
391 bpck_connect(pi);
392
393 WR(4, 0);
394 for (i = 0; i < 64; i++) {
395 WR(6, 8);
396 WR(6, 0xc);
397 p = 0x100;
398 for (k = 0; k < 9; k++) {
399 f = (((i + 0x180) & p) != 0) * 2;
400 WR(6, f + 0xc);
401 WR(6, f + 0xd);
402 WR(6, f + 0xc);
403 p = (p >> 1);
404 }
405 for (j = 0; j < 2; j++) {
406 v = 0;
407 for (k = 0; k < 8; k++) {
408 WR(6, 0xc);
409 WR(6, 0xd);
410 WR(6, 0xc);
411 f = RR(0);
412 v = 2 * v + (f == 0x84);
413 }
414 buf[2 * i + 1 - j] = v;
415 }
416 }
417 WR(6, 8);
418 WR(6, 0);
419 WR(5, 8);
420
421 bpck_disconnect(pi);
422
423 if (om >= 2) {
424 bpck_connect(pi);
425 WR(7, 3);
426 WR(4, 8);
427 bpck_disconnect(pi);
428 }
429
430 pi->mode = om; pi->delay = od;
431}
432
433static int bpck_test_port(struct pi_adapter *pi)
434{
435 int i, r, m;
436
437 /* Check for 8-bit port */
438 w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
439 m = -1;
440 if (r == i)
441 m = 2;
442 if (r == (255-i))
443 m = 0;
444
445 w2(0xc);
446 i = r0();
447 w0(255-i);
448 r = r0();
449 w0(i);
450 if (r != (255-i))
451 m = -1;
452
453 if (m == 0) {
454 w2(6);
455 w2(0xc);
456 r = r0();
457 w0(0xaa);
458 w0(r);
459 w0(0xaa);
460 }
461 if (m == 2) {
462 w2(0x26);
463 w2(0xc);
464 }
465
466 if (m == -1)
467 return 0;
468
469 return 5;
470}
471
472static void bpck_log_adapter(struct pi_adapter *pi)
473{
474 char *mode_str[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
475 char scratch[128];
476
477 bpck_read_eeprom(pi,buf: scratch);
478 print_hex_dump_bytes("bpck EEPROM: ", DUMP_PREFIX_NONE, scratch, 128);
479 dev_info(&pi->dev,
480 "backpack %8.8s unit %d at 0x%x, mode %d (%s), delay %d\n",
481 &scratch[110], pi->unit, pi->port, pi->mode,
482 mode_str[pi->mode], pi->delay);
483}
484
485static struct pi_protocol bpck = {
486 .owner = THIS_MODULE,
487 .name = "bpck",
488 .max_mode = 5,
489 .epp_first = 2,
490 .default_delay = 4,
491 .max_units = 255,
492 .write_regr = bpck_write_regr,
493 .read_regr = bpck_read_regr,
494 .write_block = bpck_write_block,
495 .read_block = bpck_read_block,
496 .connect = bpck_connect,
497 .disconnect = bpck_disconnect,
498 .test_port = bpck_test_port,
499 .probe_unit = bpck_probe_unit,
500 .test_proto = bpck_test_proto,
501 .log_adapter = bpck_log_adapter,
502};
503
504MODULE_LICENSE("GPL");
505MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
506MODULE_DESCRIPTION("MicroSolutions BACKPACK parallel port IDE adapter protocol driver");
507module_pata_parport_driver(bpck);
508

source code of linux/drivers/ata/pata_parport/bpck.c