blob: 5e5521d3b1dde99d41792835c6d7fa01cce1f982 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
67typedef 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
104static int ppc6_select(Interface *ppc);
105static void ppc6_deselect(Interface *ppc);
106static void ppc6_send_cmd(Interface *ppc, u8 cmd);
107static void ppc6_wr_data_byte(Interface *ppc, u8 data);
108static u8 ppc6_rd_data_byte(Interface *ppc);
109static u8 ppc6_rd_port(Interface *ppc, u8 port);
110static void ppc6_wr_port(Interface *ppc, u8 port, u8 data);
111static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count);
112static void ppc6_wait_for_fifo(Interface *ppc);
113static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count);
114static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
115static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
116static void ppc6_wr_extout(Interface *ppc, u8 regdata);
117static int ppc6_open(Interface *ppc);
118static void ppc6_close(Interface *ppc);
119
120//***************************************************************************
121
122static 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
208static 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
226static 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
257static 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
288static 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
361static 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
370static 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
379static 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
515static 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
528static 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
647static 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
667static 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
687static 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
696static 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
720static void ppc6_close(Interface *ppc)
721{
722 ppc6_deselect(ppc);
723}
724
725//***************************************************************************
726