1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * (c) 2001 Micro Solutions Inc. |
4 | * |
5 | * backpack.c is a low-level protocol driver for the Micro Solutions |
6 | * "BACKPACK" parallel port IDE adapter (works on Series 6 drives). |
7 | * |
8 | * Written by: Ken Hahn (linux-dev@micro-solutions.com) |
9 | * Clive Turvey (linux-dev@micro-solutions.com) |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/init.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/types.h> |
16 | #include <linux/parport.h> |
17 | #include "pata_parport.h" |
18 | |
19 | /* 60772 Commands */ |
20 | #define ACCESS_REG 0x00 |
21 | #define ACCESS_PORT 0x40 |
22 | |
23 | #define ACCESS_READ 0x00 |
24 | #define ACCESS_WRITE 0x20 |
25 | |
26 | /* 60772 Command Prefix */ |
27 | #define CMD_PREFIX_SET 0xe0 // Special command that modifies next command's operation |
28 | #define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits |
29 | #define PREFIX_IO16 0x01 // perform 16-bit wide I/O |
30 | #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write |
31 | #define PREFIX_BLK 0x08 // enable block transfer mode |
32 | |
33 | /* 60772 Registers */ |
34 | #define REG_STATUS 0x00 // status register |
35 | #define STATUS_IRQA 0x01 // Peripheral IRQA line |
36 | #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit |
37 | #define REG_VERSION 0x01 // PPC version register (read) |
38 | #define REG_HWCFG 0x02 // Hardware Config register |
39 | #define REG_RAMSIZE 0x03 // Size of RAM Buffer |
40 | #define RAMSIZE_128K 0x02 |
41 | #define REG_EEPROM 0x06 // EEPROM control register |
42 | #define EEPROM_SK 0x01 // eeprom SK bit |
43 | #define EEPROM_DI 0x02 // eeprom DI bit |
44 | #define EEPROM_CS 0x04 // eeprom CS bit |
45 | #define EEPROM_EN 0x08 // eeprom output enable |
46 | #define REG_BLKSIZE 0x08 // Block transfer len (24 bit) |
47 | |
48 | /* flags */ |
49 | #define fifo_wait 0x10 |
50 | |
51 | /* DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES */ |
52 | #define PPCMODE_UNI_SW 0 |
53 | #define PPCMODE_UNI_FW 1 |
54 | #define PPCMODE_BI_SW 2 |
55 | #define PPCMODE_BI_FW 3 |
56 | #define PPCMODE_EPP_BYTE 4 |
57 | #define PPCMODE_EPP_WORD 5 |
58 | #define PPCMODE_EPP_DWORD 6 |
59 | |
60 | static int mode_map[] = { PPCMODE_UNI_FW, PPCMODE_BI_FW, PPCMODE_EPP_BYTE, |
61 | PPCMODE_EPP_WORD, PPCMODE_EPP_DWORD }; |
62 | |
63 | static void bpck6_send_cmd(struct pi_adapter *pi, u8 cmd) |
64 | { |
65 | switch (mode_map[pi->mode]) { |
66 | case PPCMODE_UNI_SW: |
67 | case PPCMODE_UNI_FW: |
68 | case PPCMODE_BI_SW: |
69 | case PPCMODE_BI_FW: |
70 | parport_write_data(pi->pardev->port, cmd); |
71 | parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_AUTOFD); |
72 | break; |
73 | case PPCMODE_EPP_BYTE: |
74 | case PPCMODE_EPP_WORD: |
75 | case PPCMODE_EPP_DWORD: |
76 | pi->pardev->port->ops->epp_write_addr(pi->pardev->port, &cmd, 1, 0); |
77 | break; |
78 | } |
79 | } |
80 | |
81 | static u8 bpck6_rd_data_byte(struct pi_adapter *pi) |
82 | { |
83 | u8 data = 0; |
84 | |
85 | switch (mode_map[pi->mode]) { |
86 | case PPCMODE_UNI_SW: |
87 | case PPCMODE_UNI_FW: |
88 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, |
89 | PARPORT_CONTROL_INIT); |
90 | data = parport_read_status(pi->pardev->port); |
91 | data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3); |
92 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, |
93 | PARPORT_CONTROL_STROBE); |
94 | data |= parport_read_status(pi->pardev->port) & 0xB8; |
95 | break; |
96 | case PPCMODE_BI_SW: |
97 | case PPCMODE_BI_FW: |
98 | parport_data_reverse(pi->pardev->port); |
99 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, |
100 | PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT); |
101 | data = parport_read_data(pi->pardev->port); |
102 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, 0); |
103 | parport_data_forward(pi->pardev->port); |
104 | break; |
105 | case PPCMODE_EPP_BYTE: |
106 | case PPCMODE_EPP_WORD: |
107 | case PPCMODE_EPP_DWORD: |
108 | pi->pardev->port->ops->epp_read_data(pi->pardev->port, &data, 1, 0); |
109 | break; |
110 | } |
111 | |
112 | return data; |
113 | } |
114 | |
115 | static void bpck6_wr_data_byte(struct pi_adapter *pi, u8 data) |
116 | { |
117 | switch (mode_map[pi->mode]) { |
118 | case PPCMODE_UNI_SW: |
119 | case PPCMODE_UNI_FW: |
120 | case PPCMODE_BI_SW: |
121 | case PPCMODE_BI_FW: |
122 | parport_write_data(pi->pardev->port, data); |
123 | parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_INIT); |
124 | break; |
125 | case PPCMODE_EPP_BYTE: |
126 | case PPCMODE_EPP_WORD: |
127 | case PPCMODE_EPP_DWORD: |
128 | pi->pardev->port->ops->epp_write_data(pi->pardev->port, &data, 1, 0); |
129 | break; |
130 | } |
131 | } |
132 | |
133 | static int bpck6_read_regr(struct pi_adapter *pi, int cont, int reg) |
134 | { |
135 | u8 port = cont ? reg | 8 : reg; |
136 | |
137 | bpck6_send_cmd(pi, cmd: port | ACCESS_PORT | ACCESS_READ); |
138 | return bpck6_rd_data_byte(pi); |
139 | } |
140 | |
141 | static void bpck6_write_regr(struct pi_adapter *pi, int cont, int reg, int val) |
142 | { |
143 | u8 port = cont ? reg | 8 : reg; |
144 | |
145 | bpck6_send_cmd(pi, cmd: port | ACCESS_PORT | ACCESS_WRITE); |
146 | bpck6_wr_data_byte(pi, data: val); |
147 | } |
148 | |
149 | static void bpck6_wait_for_fifo(struct pi_adapter *pi) |
150 | { |
151 | int i; |
152 | |
153 | if (pi->private & fifo_wait) { |
154 | for (i = 0; i < 20; i++) |
155 | parport_read_status(pi->pardev->port); |
156 | } |
157 | } |
158 | |
159 | static void bpck6_write_block(struct pi_adapter *pi, char *buf, int len) |
160 | { |
161 | u8 this, last; |
162 | |
163 | bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE); |
164 | bpck6_wr_data_byte(pi, data: (u8)len); |
165 | bpck6_wr_data_byte(pi, data: (u8)(len >> 8)); |
166 | bpck6_wr_data_byte(pi, data: 0); |
167 | |
168 | bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK); |
169 | bpck6_send_cmd(pi, cmd: ATA_REG_DATA | ACCESS_PORT | ACCESS_WRITE); |
170 | |
171 | switch (mode_map[pi->mode]) { |
172 | case PPCMODE_UNI_SW: |
173 | case PPCMODE_BI_SW: |
174 | while (len--) { |
175 | parport_write_data(pi->pardev->port, *buf++); |
176 | parport_frob_control(pi->pardev->port, 0, |
177 | PARPORT_CONTROL_INIT); |
178 | } |
179 | break; |
180 | case PPCMODE_UNI_FW: |
181 | case PPCMODE_BI_FW: |
182 | bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_FASTWR); |
183 | |
184 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, |
185 | PARPORT_CONTROL_STROBE); |
186 | |
187 | last = *buf; |
188 | |
189 | parport_write_data(pi->pardev->port, last); |
190 | |
191 | while (len) { |
192 | this = *buf++; |
193 | len--; |
194 | |
195 | if (this == last) { |
196 | parport_frob_control(pi->pardev->port, 0, |
197 | PARPORT_CONTROL_INIT); |
198 | } else { |
199 | parport_write_data(pi->pardev->port, this); |
200 | last = this; |
201 | } |
202 | } |
203 | |
204 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, |
205 | 0); |
206 | bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_FASTWR); |
207 | break; |
208 | case PPCMODE_EPP_BYTE: |
209 | pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf, |
210 | len, PARPORT_EPP_FAST_8); |
211 | bpck6_wait_for_fifo(pi); |
212 | break; |
213 | case PPCMODE_EPP_WORD: |
214 | pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf, |
215 | len, PARPORT_EPP_FAST_16); |
216 | bpck6_wait_for_fifo(pi); |
217 | break; |
218 | case PPCMODE_EPP_DWORD: |
219 | pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf, |
220 | len, PARPORT_EPP_FAST_32); |
221 | bpck6_wait_for_fifo(pi); |
222 | break; |
223 | } |
224 | |
225 | bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK); |
226 | } |
227 | |
228 | static void bpck6_read_block(struct pi_adapter *pi, char *buf, int len) |
229 | { |
230 | bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE); |
231 | bpck6_wr_data_byte(pi, data: (u8)len); |
232 | bpck6_wr_data_byte(pi, data: (u8)(len >> 8)); |
233 | bpck6_wr_data_byte(pi, data: 0); |
234 | |
235 | bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK); |
236 | bpck6_send_cmd(pi, cmd: ATA_REG_DATA | ACCESS_PORT | ACCESS_READ); |
237 | |
238 | switch (mode_map[pi->mode]) { |
239 | case PPCMODE_UNI_SW: |
240 | case PPCMODE_UNI_FW: |
241 | while (len) { |
242 | u8 d; |
243 | |
244 | parport_frob_control(pi->pardev->port, |
245 | PARPORT_CONTROL_STROBE, |
246 | PARPORT_CONTROL_INIT); /* DATA STROBE */ |
247 | d = parport_read_status(pi->pardev->port); |
248 | d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3); |
249 | parport_frob_control(pi->pardev->port, |
250 | PARPORT_CONTROL_STROBE, |
251 | PARPORT_CONTROL_STROBE); |
252 | d |= parport_read_status(pi->pardev->port) & 0xB8; |
253 | *buf++ = d; |
254 | len--; |
255 | } |
256 | break; |
257 | case PPCMODE_BI_SW: |
258 | case PPCMODE_BI_FW: |
259 | parport_data_reverse(pi->pardev->port); |
260 | while (len) { |
261 | parport_frob_control(pi->pardev->port, |
262 | PARPORT_CONTROL_STROBE, |
263 | PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT); |
264 | *buf++ = parport_read_data(pi->pardev->port); |
265 | len--; |
266 | } |
267 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, |
268 | 0); |
269 | parport_data_forward(pi->pardev->port); |
270 | break; |
271 | case PPCMODE_EPP_BYTE: |
272 | pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len, |
273 | PARPORT_EPP_FAST_8); |
274 | break; |
275 | case PPCMODE_EPP_WORD: |
276 | pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len, |
277 | PARPORT_EPP_FAST_16); |
278 | break; |
279 | case PPCMODE_EPP_DWORD: |
280 | pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len, |
281 | PARPORT_EPP_FAST_32); |
282 | break; |
283 | } |
284 | |
285 | bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK); |
286 | } |
287 | |
288 | static int bpck6_open(struct pi_adapter *pi) |
289 | { |
290 | u8 i, j, k; |
291 | |
292 | pi->saved_r0 = parport_read_data(pi->pardev->port); |
293 | pi->saved_r2 = parport_read_control(pi->pardev->port) & 0x5F; |
294 | |
295 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, |
296 | PARPORT_CONTROL_SELECT); |
297 | if (pi->saved_r0 == 'b') |
298 | parport_write_data(pi->pardev->port, 'x'); |
299 | parport_write_data(pi->pardev->port, 'b'); |
300 | parport_write_data(pi->pardev->port, 'p'); |
301 | parport_write_data(pi->pardev->port, pi->unit); |
302 | parport_write_data(pi->pardev->port, ~pi->unit); |
303 | |
304 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0); |
305 | parport_write_control(pi->pardev->port, PARPORT_CONTROL_INIT); |
306 | |
307 | i = mode_map[pi->mode] & 0x0C; |
308 | if (i == 0) |
309 | i = (mode_map[pi->mode] & 2) | 1; |
310 | parport_write_data(pi->pardev->port, i); |
311 | |
312 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, |
313 | PARPORT_CONTROL_SELECT); |
314 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, |
315 | PARPORT_CONTROL_AUTOFD); |
316 | |
317 | j = ((i & 0x08) << 4) | ((i & 0x07) << 3); |
318 | k = parport_read_status(pi->pardev->port) & 0xB8; |
319 | if (j != k) |
320 | goto fail; |
321 | |
322 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, 0); |
323 | k = (parport_read_status(pi->pardev->port) & 0xB8) ^ 0xB8; |
324 | if (j != k) |
325 | goto fail; |
326 | |
327 | if (i & 4) { |
328 | /* EPP */ |
329 | parport_frob_control(pi->pardev->port, |
330 | PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, 0); |
331 | } else { |
332 | /* PPC/ECP */ |
333 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0); |
334 | } |
335 | |
336 | pi->private = 0; |
337 | |
338 | bpck6_send_cmd(pi, ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE); |
339 | bpck6_wr_data_byte(pi, RAMSIZE_128K); |
340 | |
341 | bpck6_send_cmd(pi, ACCESS_REG | ACCESS_READ | REG_VERSION); |
342 | if ((bpck6_rd_data_byte(pi) & 0x3F) == 0x0C) |
343 | pi->private |= fifo_wait; |
344 | |
345 | return 1; |
346 | |
347 | fail: |
348 | parport_write_control(pi->pardev->port, pi->saved_r2); |
349 | parport_write_data(pi->pardev->port, pi->saved_r0); |
350 | |
351 | return 0; |
352 | } |
353 | |
354 | static void bpck6_deselect(struct pi_adapter *pi) |
355 | { |
356 | if (mode_map[pi->mode] & 4) { |
357 | /* EPP */ |
358 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_INIT, |
359 | PARPORT_CONTROL_INIT); |
360 | } else { |
361 | /* PPC/ECP */ |
362 | parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, |
363 | PARPORT_CONTROL_SELECT); |
364 | } |
365 | |
366 | parport_write_data(pi->pardev->port, pi->saved_r0); |
367 | parport_write_control(pi->pardev->port, |
368 | pi->saved_r2 | PARPORT_CONTROL_SELECT); |
369 | parport_write_control(pi->pardev->port, pi->saved_r2); |
370 | } |
371 | |
372 | static void bpck6_wr_extout(struct pi_adapter *pi, u8 regdata) |
373 | { |
374 | bpck6_send_cmd(pi, REG_VERSION | ACCESS_REG | ACCESS_WRITE); |
375 | bpck6_wr_data_byte(pi, data: (u8)((regdata & 0x03) << 6)); |
376 | } |
377 | |
378 | static void bpck6_connect(struct pi_adapter *pi) |
379 | { |
380 | dev_dbg(&pi->dev, "connect\n" ); |
381 | |
382 | bpck6_open(pi); |
383 | bpck6_wr_extout(pi, regdata: 0x3); |
384 | } |
385 | |
386 | static void bpck6_disconnect(struct pi_adapter *pi) |
387 | { |
388 | dev_dbg(&pi->dev, "disconnect\n" ); |
389 | bpck6_wr_extout(pi, regdata: 0x0); |
390 | bpck6_deselect(pi); |
391 | } |
392 | |
393 | /* check for 8-bit port */ |
394 | static int bpck6_test_port(struct pi_adapter *pi) |
395 | { |
396 | dev_dbg(&pi->dev, "PARPORT indicates modes=%x for lp=0x%lx\n" , |
397 | pi->pardev->port->modes, pi->pardev->port->base); |
398 | |
399 | /* look at the parport device to see what modes we can use */ |
400 | if (pi->pardev->port->modes & PARPORT_MODE_EPP) |
401 | return 5; /* Can do EPP */ |
402 | if (pi->pardev->port->modes & PARPORT_MODE_TRISTATE) |
403 | return 2; |
404 | return 1; /* Just flat SPP */ |
405 | } |
406 | |
407 | static int bpck6_probe_unit(struct pi_adapter *pi) |
408 | { |
409 | int out, saved_mode; |
410 | |
411 | dev_dbg(&pi->dev, "PROBE UNIT %x on port:%x\n" , pi->unit, pi->port); |
412 | |
413 | saved_mode = pi->mode; |
414 | /*LOWER DOWN TO UNIDIRECTIONAL*/ |
415 | pi->mode = 0; |
416 | |
417 | out = bpck6_open(pi); |
418 | |
419 | dev_dbg(&pi->dev, "ppc_open returned %2x\n" , out); |
420 | |
421 | if (out) { |
422 | bpck6_deselect(pi); |
423 | dev_dbg(&pi->dev, "leaving probe\n" ); |
424 | pi->mode = saved_mode; |
425 | return 1; |
426 | } |
427 | |
428 | dev_dbg(&pi->dev, "Failed open\n" ); |
429 | pi->mode = saved_mode; |
430 | |
431 | return 0; |
432 | } |
433 | |
434 | static void bpck6_log_adapter(struct pi_adapter *pi) |
435 | { |
436 | char *mode_string[5] = { "4-bit" , "8-bit" , "EPP-8" , "EPP-16" , "EPP-32" }; |
437 | |
438 | dev_info(&pi->dev, |
439 | "Micro Solutions BACKPACK Drive unit %d at 0x%x, mode:%d (%s), delay %d\n" , |
440 | pi->unit, pi->port, pi->mode, mode_string[pi->mode], pi->delay); |
441 | } |
442 | |
443 | static struct pi_protocol bpck6 = { |
444 | .owner = THIS_MODULE, |
445 | .name = "bpck6" , |
446 | .max_mode = 5, |
447 | .epp_first = 2, /* 2-5 use epp (need 8 ports) */ |
448 | .max_units = 255, |
449 | .write_regr = bpck6_write_regr, |
450 | .read_regr = bpck6_read_regr, |
451 | .write_block = bpck6_write_block, |
452 | .read_block = bpck6_read_block, |
453 | .connect = bpck6_connect, |
454 | .disconnect = bpck6_disconnect, |
455 | .test_port = bpck6_test_port, |
456 | .probe_unit = bpck6_probe_unit, |
457 | .log_adapter = bpck6_log_adapter, |
458 | }; |
459 | |
460 | MODULE_LICENSE("GPL" ); |
461 | MODULE_AUTHOR("Micro Solutions Inc." ); |
462 | MODULE_DESCRIPTION("Micro Solutions BACKPACK parallel port IDE adapter " |
463 | "(version 6 drives) protocol driver" ); |
464 | module_pata_parport_driver(bpck6); |
465 | |