Pull novell-bugzilla-156426 into release branch
[linux-2.6] / drivers / block / paride / ppc6lnx.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
720 static void ppc6_close(Interface *ppc)
721 {
722         ppc6_deselect(ppc);
723 }
724
725 //***************************************************************************
726