Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/block/paride/ppc6lnx.c
15112 views
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
727
728