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
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