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