1 /*
2 ppc6lnx.c (c) 2001 Micro Solutions Inc.
3 Released under the terms of the GNU General Public license
4
5 ppc6lnx.c is a par of the protocol driver for the Micro Solutions
6 "BACKPACK" parallel port IDE adapter
7 (Works on Series 6 drives)
8
9 */
10
11 //***************************************************************************
12
13 // PPC 6 Code in C sanitized for LINUX
14 // Original x86 ASM by Ron, Converted to C by Clive
15
16 //***************************************************************************
17
18
19 #define port_stb 1
20 #define port_afd 2
21 #define cmd_stb port_afd
22 #define port_init 4
23 #define data_stb port_init
24 #define port_sel 8
25 #define port_int 16
26 #define port_dir 0x20
27
28 #define ECR_EPP 0x80
29 #define ECR_BI 0x20
30
31 //***************************************************************************
32
33 // 60772 Commands
34
35 #define ACCESS_REG 0x00
36 #define ACCESS_PORT 0x40
37
38 #define ACCESS_READ 0x00
39 #define ACCESS_WRITE 0x20
40
41 // 60772 Command Prefix
42
43 #define CMD_PREFIX_SET 0xe0 // Special command that modifies the next command's operation
44 #define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits
45 #define PREFIX_IO16 0x01 // perform 16-bit wide I/O
46 #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write
47 #define PREFIX_BLK 0x08 // enable block transfer mode
48
49 // 60772 Registers
50
51 #define REG_STATUS 0x00 // status register
52 #define STATUS_IRQA 0x01 // Peripheral IRQA line
53 #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit
54 #define REG_VERSION 0x01 // PPC version register (read)
55 #define REG_HWCFG 0x02 // Hardware Config register
56 #define REG_RAMSIZE 0x03 // Size of RAM Buffer
57 #define RAMSIZE_128K 0x02
58 #define REG_EEPROM 0x06 // EEPROM control register
59 #define EEPROM_SK 0x01 // eeprom SK bit
60 #define EEPROM_DI 0x02 // eeprom DI bit
61 #define EEPROM_CS 0x04 // eeprom CS bit
62 #define EEPROM_EN 0x08 // eeprom output enable
63 #define REG_BLKSIZE 0x08 // Block transfer len (24 bit)
64
65 //***************************************************************************
66
67 typedef struct ppc_storage {
68 u16 lpt_addr; // LPT base address
69 u8 ppc_id;
70 u8 mode; // operating mode
71 // 0 = PPC Uni SW
72 // 1 = PPC Uni FW
73 // 2 = PPC Bi SW
74 // 3 = PPC Bi FW
75 // 4 = EPP Byte
76 // 5 = EPP Word
77 // 6 = EPP Dword
78 u8 ppc_flags;
79 u8 org_data; // original LPT data port contents
80 u8 org_ctrl; // original LPT control port contents
81 u8 cur_ctrl; // current control port contents
82 } Interface;
83
84 //***************************************************************************
85
86 // ppc_flags
87
88 #define fifo_wait 0x10
89
90 //***************************************************************************
91
92 // DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES
93
94 #define PPCMODE_UNI_SW 0
95 #define PPCMODE_UNI_FW 1
96 #define PPCMODE_BI_SW 2
97 #define PPCMODE_BI_FW 3
98 #define PPCMODE_EPP_BYTE 4
99 #define PPCMODE_EPP_WORD 5
100 #define PPCMODE_EPP_DWORD 6
101
102 //***************************************************************************
103
104 static int ppc6_select(Interface *ppc);
105 static void ppc6_deselect(Interface *ppc);
106 static void ppc6_send_cmd(Interface *ppc, u8 cmd);
107 static void ppc6_wr_data_byte(Interface *ppc, u8 data);
108 static u8 ppc6_rd_data_byte(Interface *ppc);
109 static u8 ppc6_rd_port(Interface *ppc, u8 port);
110 static void ppc6_wr_port(Interface *ppc, u8 port, u8 data);
111 static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count);
112 static void ppc6_wait_for_fifo(Interface *ppc);
113 static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count);
114 static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
115 static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
116 static void ppc6_wr_extout(Interface *ppc, u8 regdata);
117 static int ppc6_open(Interface *ppc);
118 static void ppc6_close(Interface *ppc);
119
120 //***************************************************************************
121
ppc6_select(Interface * ppc)122 static int ppc6_select(Interface *ppc)
123 {
124 u8 i, j, k;
125
126 i = inb(ppc->lpt_addr + 1);
127
128 if (i & 1)
129 outb(i, ppc->lpt_addr + 1);
130
131 ppc->org_data = inb(ppc->lpt_addr);
132
133 ppc->org_ctrl = inb(ppc->lpt_addr + 2) & 0x5F; // readback ctrl
134
135 ppc->cur_ctrl = ppc->org_ctrl;
136
137 ppc->cur_ctrl |= port_sel;
138
139 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
140
141 if (ppc->org_data == 'b')
142 outb('x', ppc->lpt_addr);
143
144 outb('b', ppc->lpt_addr);
145 outb('p', ppc->lpt_addr);
146 outb(ppc->ppc_id, ppc->lpt_addr);
147 outb(~ppc->ppc_id,ppc->lpt_addr);
148
149 ppc->cur_ctrl &= ~port_sel;
150
151 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
152
153 ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init;
154
155 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
156
157 i = ppc->mode & 0x0C;
158
159 if (i == 0)
160 i = (ppc->mode & 2) | 1;
161
162 outb(i, ppc->lpt_addr);
163
164 ppc->cur_ctrl |= port_sel;
165
166 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
167
168 // DELAY
169
170 ppc->cur_ctrl |= port_afd;
171
172 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
173
174 j = ((i & 0x08) << 4) | ((i & 0x07) << 3);
175
176 k = inb(ppc->lpt_addr + 1) & 0xB8;
177
178 if (j == k)
179 {
180 ppc->cur_ctrl &= ~port_afd;
181
182 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
183
184 k = (inb(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8;
185
186 if (j == k)
187 {
188 if (i & 4) // EPP
189 ppc->cur_ctrl &= ~(port_sel | port_init);
190 else // PPC/ECP
191 ppc->cur_ctrl &= ~port_sel;
192
193 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
194
195 return(1);
196 }
197 }
198
199 outb(ppc->org_ctrl, ppc->lpt_addr + 2);
200
201 outb(ppc->org_data, ppc->lpt_addr);
202
203 return(0); // FAIL
204 }
205
206 //***************************************************************************
207
ppc6_deselect(Interface * ppc)208 static void ppc6_deselect(Interface *ppc)
209 {
210 if (ppc->mode & 4) // EPP
211 ppc->cur_ctrl |= port_init;
212 else // PPC/ECP
213 ppc->cur_ctrl |= port_sel;
214
215 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
216
217 outb(ppc->org_data, ppc->lpt_addr);
218
219 outb((ppc->org_ctrl | port_sel), ppc->lpt_addr + 2);
220
221 outb(ppc->org_ctrl, ppc->lpt_addr + 2);
222 }
223
224 //***************************************************************************
225
ppc6_send_cmd(Interface * ppc,u8 cmd)226 static void ppc6_send_cmd(Interface *ppc, u8 cmd)
227 {
228 switch(ppc->mode)
229 {
230 case PPCMODE_UNI_SW :
231 case PPCMODE_UNI_FW :
232 case PPCMODE_BI_SW :
233 case PPCMODE_BI_FW :
234 {
235 outb(cmd, ppc->lpt_addr);
236
237 ppc->cur_ctrl ^= cmd_stb;
238
239 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
240
241 break;
242 }
243
244 case PPCMODE_EPP_BYTE :
245 case PPCMODE_EPP_WORD :
246 case PPCMODE_EPP_DWORD :
247 {
248 outb(cmd, ppc->lpt_addr + 3);
249
250 break;
251 }
252 }
253 }
254
255 //***************************************************************************
256
ppc6_wr_data_byte(Interface * ppc,u8 data)257 static void ppc6_wr_data_byte(Interface *ppc, u8 data)
258 {
259 switch(ppc->mode)
260 {
261 case PPCMODE_UNI_SW :
262 case PPCMODE_UNI_FW :
263 case PPCMODE_BI_SW :
264 case PPCMODE_BI_FW :
265 {
266 outb(data, ppc->lpt_addr);
267
268 ppc->cur_ctrl ^= data_stb;
269
270 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
271
272 break;
273 }
274
275 case PPCMODE_EPP_BYTE :
276 case PPCMODE_EPP_WORD :
277 case PPCMODE_EPP_DWORD :
278 {
279 outb(data, ppc->lpt_addr + 4);
280
281 break;
282 }
283 }
284 }
285
286 //***************************************************************************
287
ppc6_rd_data_byte(Interface * ppc)288 static u8 ppc6_rd_data_byte(Interface *ppc)
289 {
290 u8 data = 0;
291
292 switch(ppc->mode)
293 {
294 case PPCMODE_UNI_SW :
295 case PPCMODE_UNI_FW :
296 {
297 ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
298
299 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
300
301 // DELAY
302
303 data = inb(ppc->lpt_addr + 1);
304
305 data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);
306
307 ppc->cur_ctrl |= port_stb;
308
309 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
310
311 // DELAY
312
313 data |= inb(ppc->lpt_addr + 1) & 0xB8;
314
315 break;
316 }
317
318 case PPCMODE_BI_SW :
319 case PPCMODE_BI_FW :
320 {
321 ppc->cur_ctrl |= port_dir;
322
323 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
324
325 ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb;
326
327 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
328
329 data = inb(ppc->lpt_addr);
330
331 ppc->cur_ctrl &= ~port_stb;
332
333 outb(ppc->cur_ctrl,ppc->lpt_addr + 2);
334
335 ppc->cur_ctrl &= ~port_dir;
336
337 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
338
339 break;
340 }
341
342 case PPCMODE_EPP_BYTE :
343 case PPCMODE_EPP_WORD :
344 case PPCMODE_EPP_DWORD :
345 {
346 outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);
347
348 data = inb(ppc->lpt_addr + 4);
349
350 outb(ppc->cur_ctrl,ppc->lpt_addr + 2);
351
352 break;
353 }
354 }
355
356 return(data);
357 }
358
359 //***************************************************************************
360
ppc6_rd_port(Interface * ppc,u8 port)361 static u8 ppc6_rd_port(Interface *ppc, u8 port)
362 {
363 ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_READ));
364
365 return(ppc6_rd_data_byte(ppc));
366 }
367
368 //***************************************************************************
369
ppc6_wr_port(Interface * ppc,u8 port,u8 data)370 static void ppc6_wr_port(Interface *ppc, u8 port, u8 data)
371 {
372 ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_WRITE));
373
374 ppc6_wr_data_byte(ppc, data);
375 }
376
377 //***************************************************************************
378
ppc6_rd_data_blk(Interface * ppc,u8 * data,long count)379 static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count)
380 {
381 switch(ppc->mode)
382 {
383 case PPCMODE_UNI_SW :
384 case PPCMODE_UNI_FW :
385 {
386 while(count)
387 {
388 u8 d;
389
390 ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
391
392 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
393
394 // DELAY
395
396 d = inb(ppc->lpt_addr + 1);
397
398 d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);
399
400 ppc->cur_ctrl |= port_stb;
401
402 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
403
404 // DELAY
405
406 d |= inb(ppc->lpt_addr + 1) & 0xB8;
407
408 *data++ = d;
409 count--;
410 }
411
412 break;
413 }
414
415 case PPCMODE_BI_SW :
416 case PPCMODE_BI_FW :
417 {
418 ppc->cur_ctrl |= port_dir;
419
420 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
421
422 ppc->cur_ctrl |= port_stb;
423
424 while(count)
425 {
426 ppc->cur_ctrl ^= data_stb;
427
428 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
429
430 *data++ = inb(ppc->lpt_addr);
431 count--;
432 }
433
434 ppc->cur_ctrl &= ~port_stb;
435
436 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
437
438 ppc->cur_ctrl &= ~port_dir;
439
440 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
441
442 break;
443 }
444
445 case PPCMODE_EPP_BYTE :
446 {
447 outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
448
449 // DELAY
450
451 while(count)
452 {
453 *data++ = inb(ppc->lpt_addr + 4);
454 count--;
455 }
456
457 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
458
459 break;
460 }
461
462 case PPCMODE_EPP_WORD :
463 {
464 outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
465
466 // DELAY
467
468 while(count > 1)
469 {
470 *((u16 *)data) = inw(ppc->lpt_addr + 4);
471 data += 2;
472 count -= 2;
473 }
474
475 while(count)
476 {
477 *data++ = inb(ppc->lpt_addr + 4);
478 count--;
479 }
480
481 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
482
483 break;
484 }
485
486 case PPCMODE_EPP_DWORD :
487 {
488 outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);
489
490 // DELAY
491
492 while(count > 3)
493 {
494 *((u32 *)data) = inl(ppc->lpt_addr + 4);
495 data += 4;
496 count -= 4;
497 }
498
499 while(count)
500 {
501 *data++ = inb(ppc->lpt_addr + 4);
502 count--;
503 }
504
505 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
506
507 break;
508 }
509 }
510
511 }
512
513 //***************************************************************************
514
ppc6_wait_for_fifo(Interface * ppc)515 static void ppc6_wait_for_fifo(Interface *ppc)
516 {
517 int i;
518
519 if (ppc->ppc_flags & fifo_wait)
520 {
521 for(i=0; i<20; i++)
522 inb(ppc->lpt_addr + 1);
523 }
524 }
525
526 //***************************************************************************
527
ppc6_wr_data_blk(Interface * ppc,u8 * data,long count)528 static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count)
529 {
530 switch(ppc->mode)
531 {
532 case PPCMODE_UNI_SW :
533 case PPCMODE_BI_SW :
534 {
535 while(count--)
536 {
537 outb(*data++, ppc->lpt_addr);
538
539 ppc->cur_ctrl ^= data_stb;
540
541 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
542 }
543
544 break;
545 }
546
547 case PPCMODE_UNI_FW :
548 case PPCMODE_BI_FW :
549 {
550 u8 this, last;
551
552 ppc6_send_cmd(ppc,(CMD_PREFIX_SET | PREFIX_FASTWR));
553
554 ppc->cur_ctrl |= port_stb;
555
556 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
557
558 last = *data;
559
560 outb(last, ppc->lpt_addr);
561
562 while(count)
563 {
564 this = *data++;
565 count--;
566
567 if (this == last)
568 {
569 ppc->cur_ctrl ^= data_stb;
570
571 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
572 }
573 else
574 {
575 outb(this, ppc->lpt_addr);
576
577 last = this;
578 }
579 }
580
581 ppc->cur_ctrl &= ~port_stb;
582
583 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
584
585 ppc6_send_cmd(ppc,(CMD_PREFIX_RESET | PREFIX_FASTWR));
586
587 break;
588 }
589
590 case PPCMODE_EPP_BYTE :
591 {
592 while(count)
593 {
594 outb(*data++,ppc->lpt_addr + 4);
595 count--;
596 }
597
598 ppc6_wait_for_fifo(ppc);
599
600 break;
601 }
602
603 case PPCMODE_EPP_WORD :
604 {
605 while(count > 1)
606 {
607 outw(*((u16 *)data),ppc->lpt_addr + 4);
608 data += 2;
609 count -= 2;
610 }
611
612 while(count)
613 {
614 outb(*data++,ppc->lpt_addr + 4);
615 count--;
616 }
617
618 ppc6_wait_for_fifo(ppc);
619
620 break;
621 }
622
623 case PPCMODE_EPP_DWORD :
624 {
625 while(count > 3)
626 {
627 outl(*((u32 *)data),ppc->lpt_addr + 4);
628 data += 4;
629 count -= 4;
630 }
631
632 while(count)
633 {
634 outb(*data++,ppc->lpt_addr + 4);
635 count--;
636 }
637
638 ppc6_wait_for_fifo(ppc);
639
640 break;
641 }
642 }
643 }
644
645 //***************************************************************************
646
ppc6_rd_port16_blk(Interface * ppc,u8 port,u8 * data,long length)647 static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
648 {
649 length = length << 1;
650
651 ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
652 ppc6_wr_data_byte(ppc,(u8)length);
653 ppc6_wr_data_byte(ppc,(u8)(length >> 8));
654 ppc6_wr_data_byte(ppc,0);
655
656 ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));
657
658 ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_READ));
659
660 ppc6_rd_data_blk(ppc, data, length);
661
662 ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
663 }
664
665 //***************************************************************************
666
ppc6_wr_port16_blk(Interface * ppc,u8 port,u8 * data,long length)667 static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
668 {
669 length = length << 1;
670
671 ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
672 ppc6_wr_data_byte(ppc,(u8)length);
673 ppc6_wr_data_byte(ppc,(u8)(length >> 8));
674 ppc6_wr_data_byte(ppc,0);
675
676 ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));
677
678 ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_WRITE));
679
680 ppc6_wr_data_blk(ppc, data, length);
681
682 ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
683 }
684
685 //***************************************************************************
686
ppc6_wr_extout(Interface * ppc,u8 regdata)687 static void ppc6_wr_extout(Interface *ppc, u8 regdata)
688 {
689 ppc6_send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE));
690
691 ppc6_wr_data_byte(ppc, (u8)((regdata & 0x03) << 6));
692 }
693
694 //***************************************************************************
695
ppc6_open(Interface * ppc)696 static int ppc6_open(Interface *ppc)
697 {
698 int ret;
699
700 ret = ppc6_select(ppc);
701
702 if (ret == 0)
703 return(ret);
704
705 ppc->ppc_flags &= ~fifo_wait;
706
707 ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE));
708 ppc6_wr_data_byte(ppc, RAMSIZE_128K);
709
710 ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_READ | REG_VERSION));
711
712 if ((ppc6_rd_data_byte(ppc) & 0x3F) == 0x0C)
713 ppc->ppc_flags |= fifo_wait;
714
715 return(ret);
716 }
717
718 //***************************************************************************
719
ppc6_close(Interface * ppc)720 static void ppc6_close(Interface *ppc)
721 {
722 ppc6_deselect(ppc);
723 }
724
725 //***************************************************************************
726
727