18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci	frpw.c	(c) 1996-8  Grant R. Guenther <grant@torque.net>
38c2ecf20Sopenharmony_ci		            Under the terms of the GNU General Public License
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci	frpw.c is a low-level protocol driver for the Freecom "Power"
68c2ecf20Sopenharmony_ci	parallel port IDE adapter.
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci	Some applications of this adapter may require a "printer" reset
98c2ecf20Sopenharmony_ci	prior to loading the driver.  This can be done by loading and
108c2ecf20Sopenharmony_ci	unloading the "lp" driver, or it can be done by this driver
118c2ecf20Sopenharmony_ci	if you define FRPW_HARD_RESET.  The latter is not recommended
128c2ecf20Sopenharmony_ci	as it may upset devices on other ports.
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci*/
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* Changes:
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci        1.01    GRG 1998.05.06 init_proto, release_proto
198c2ecf20Sopenharmony_ci			       fix chip detect
208c2ecf20Sopenharmony_ci			       added EPP-16 and EPP-32
218c2ecf20Sopenharmony_ci	1.02    GRG 1998.09.23 added hard reset to initialisation process
228c2ecf20Sopenharmony_ci	1.03    GRG 1998.12.14 made hard reset conditional
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci*/
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define	FRPW_VERSION	"1.03"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/init.h>
308c2ecf20Sopenharmony_ci#include <linux/delay.h>
318c2ecf20Sopenharmony_ci#include <linux/kernel.h>
328c2ecf20Sopenharmony_ci#include <linux/types.h>
338c2ecf20Sopenharmony_ci#include <linux/wait.h>
348c2ecf20Sopenharmony_ci#include <asm/io.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include "paride.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define cec4		w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
398c2ecf20Sopenharmony_ci#define j44(l,h)	(((l>>4)&0x0f)|(h&0xf0))
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* cont = 0 - access the IDE register file
428c2ecf20Sopenharmony_ci   cont = 1 - access the IDE command set
438c2ecf20Sopenharmony_ci*/
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int  cont_map[2] = { 0x08, 0x10 };
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int frpw_read_regr( PIA *pi, int cont, int regr )
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci{	int	h,l,r;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	r = regr + cont_map[cont];
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	w2(4);
548c2ecf20Sopenharmony_ci	w0(r); cec4;
558c2ecf20Sopenharmony_ci	w2(6); l = r1();
568c2ecf20Sopenharmony_ci	w2(4); h = r1();
578c2ecf20Sopenharmony_ci	w2(4);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return j44(l,h);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void frpw_write_regr( PIA *pi, int cont, int regr, int val)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci{	int r;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci        r = regr + cont_map[cont];
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	w2(4); w0(r); cec4;
708c2ecf20Sopenharmony_ci	w0(val);
718c2ecf20Sopenharmony_ci	w2(5);w2(7);w2(5);w2(4);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void frpw_read_block_int( PIA *pi, char * buf, int count, int regr )
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci{       int     h, l, k, ph;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci        switch(pi->mode) {
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci        case 0: w2(4); w0(regr); cec4;
818c2ecf20Sopenharmony_ci                for (k=0;k<count;k++) {
828c2ecf20Sopenharmony_ci                        w2(6); l = r1();
838c2ecf20Sopenharmony_ci                        w2(4); h = r1();
848c2ecf20Sopenharmony_ci                        buf[k] = j44(l,h);
858c2ecf20Sopenharmony_ci                }
868c2ecf20Sopenharmony_ci                w2(4);
878c2ecf20Sopenharmony_ci                break;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci        case 1: ph = 2;
908c2ecf20Sopenharmony_ci                w2(4); w0(regr + 0xc0); cec4;
918c2ecf20Sopenharmony_ci                w0(0xff);
928c2ecf20Sopenharmony_ci                for (k=0;k<count;k++) {
938c2ecf20Sopenharmony_ci                        w2(0xa4 + ph);
948c2ecf20Sopenharmony_ci                        buf[k] = r0();
958c2ecf20Sopenharmony_ci                        ph = 2 - ph;
968c2ecf20Sopenharmony_ci                }
978c2ecf20Sopenharmony_ci                w2(0xac); w2(0xa4); w2(4);
988c2ecf20Sopenharmony_ci                break;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci        case 2: w2(4); w0(regr + 0x80); cec4;
1018c2ecf20Sopenharmony_ci                for (k=0;k<count;k++) buf[k] = r4();
1028c2ecf20Sopenharmony_ci                w2(0xac); w2(0xa4);
1038c2ecf20Sopenharmony_ci                w2(4);
1048c2ecf20Sopenharmony_ci                break;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	case 3: w2(4); w0(regr + 0x80); cec4;
1078c2ecf20Sopenharmony_ci		for (k=0;k<count-2;k++) buf[k] = r4();
1088c2ecf20Sopenharmony_ci		w2(0xac); w2(0xa4);
1098c2ecf20Sopenharmony_ci		buf[count-2] = r4();
1108c2ecf20Sopenharmony_ci		buf[count-1] = r4();
1118c2ecf20Sopenharmony_ci		w2(4);
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	case 4: w2(4); w0(regr + 0x80); cec4;
1158c2ecf20Sopenharmony_ci                for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
1168c2ecf20Sopenharmony_ci                w2(0xac); w2(0xa4);
1178c2ecf20Sopenharmony_ci                buf[count-2] = r4();
1188c2ecf20Sopenharmony_ci                buf[count-1] = r4();
1198c2ecf20Sopenharmony_ci                w2(4);
1208c2ecf20Sopenharmony_ci                break;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	case 5: w2(4); w0(regr + 0x80); cec4;
1238c2ecf20Sopenharmony_ci                for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
1248c2ecf20Sopenharmony_ci                buf[count-4] = r4();
1258c2ecf20Sopenharmony_ci                buf[count-3] = r4();
1268c2ecf20Sopenharmony_ci                w2(0xac); w2(0xa4);
1278c2ecf20Sopenharmony_ci                buf[count-2] = r4();
1288c2ecf20Sopenharmony_ci                buf[count-1] = r4();
1298c2ecf20Sopenharmony_ci                w2(4);
1308c2ecf20Sopenharmony_ci                break;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci        }
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void frpw_read_block( PIA *pi, char * buf, int count)
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci{	frpw_read_block_int(pi,buf,count,0x08);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void frpw_write_block( PIA *pi, char * buf, int count )
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci{	int	k;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	switch(pi->mode) {
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	case 0:
1478c2ecf20Sopenharmony_ci	case 1:
1488c2ecf20Sopenharmony_ci	case 2: w2(4); w0(8); cec4; w2(5);
1498c2ecf20Sopenharmony_ci        	for (k=0;k<count;k++) {
1508c2ecf20Sopenharmony_ci			w0(buf[k]);
1518c2ecf20Sopenharmony_ci			w2(7);w2(5);
1528c2ecf20Sopenharmony_ci		}
1538c2ecf20Sopenharmony_ci		w2(4);
1548c2ecf20Sopenharmony_ci		break;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	case 3: w2(4); w0(0xc8); cec4; w2(5);
1578c2ecf20Sopenharmony_ci		for (k=0;k<count;k++) w4(buf[k]);
1588c2ecf20Sopenharmony_ci		w2(4);
1598c2ecf20Sopenharmony_ci		break;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci        case 4: w2(4); w0(0xc8); cec4; w2(5);
1628c2ecf20Sopenharmony_ci                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
1638c2ecf20Sopenharmony_ci                w2(4);
1648c2ecf20Sopenharmony_ci                break;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci        case 5: w2(4); w0(0xc8); cec4; w2(5);
1678c2ecf20Sopenharmony_ci                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
1688c2ecf20Sopenharmony_ci                w2(4);
1698c2ecf20Sopenharmony_ci                break;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void frpw_connect ( PIA *pi  )
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci{       pi->saved_r0 = r0();
1768c2ecf20Sopenharmony_ci        pi->saved_r2 = r2();
1778c2ecf20Sopenharmony_ci	w2(4);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void frpw_disconnect ( PIA *pi )
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci{       w2(4); w0(0x20); cec4;
1838c2ecf20Sopenharmony_ci	w0(pi->saved_r0);
1848c2ecf20Sopenharmony_ci        w2(pi->saved_r2);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/* Stub logic to see if PNP string is available - used to distinguish
1888c2ecf20Sopenharmony_ci   between the Xilinx and ASIC implementations of the Freecom adapter.
1898c2ecf20Sopenharmony_ci*/
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int frpw_test_pnp ( PIA *pi )
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/*  returns chip_type:   0 = Xilinx, 1 = ASIC   */
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci{	int olddelay, a, b;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#ifdef FRPW_HARD_RESET
1988c2ecf20Sopenharmony_ci        w0(0); w2(8); udelay(50); w2(0xc);   /* parallel bus reset */
1998c2ecf20Sopenharmony_ci        mdelay(1500);
2008c2ecf20Sopenharmony_ci#endif
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	olddelay = pi->delay;
2038c2ecf20Sopenharmony_ci	pi->delay = 10;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	pi->saved_r0 = r0();
2068c2ecf20Sopenharmony_ci        pi->saved_r2 = r2();
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	w2(4); w0(4); w2(6); w2(7);
2098c2ecf20Sopenharmony_ci	a = r1() & 0xff; w2(4); b = r1() & 0xff;
2108c2ecf20Sopenharmony_ci	w2(0xc); w2(0xe); w2(4);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	pi->delay = olddelay;
2138c2ecf20Sopenharmony_ci        w0(pi->saved_r0);
2148c2ecf20Sopenharmony_ci        w2(pi->saved_r2);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return ((~a&0x40) && (b&0x40));
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/* We use the pi->private to remember the result of the PNP test.
2208c2ecf20Sopenharmony_ci   To make this work, private = port*2 + chip.  Yes, I know it's
2218c2ecf20Sopenharmony_ci   a hack :-(
2228c2ecf20Sopenharmony_ci*/
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int frpw_test_proto( PIA *pi, char * scratch, int verbose )
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci{       int     j, k, r;
2278c2ecf20Sopenharmony_ci	int	e[2] = {0,0};
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if ((pi->private>>1) != pi->port)
2308c2ecf20Sopenharmony_ci	   pi->private = frpw_test_pnp(pi) + 2*pi->port;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (((pi->private%2) == 0) && (pi->mode > 2)) {
2338c2ecf20Sopenharmony_ci	   if (verbose)
2348c2ecf20Sopenharmony_ci		printk("%s: frpw: Xilinx does not support mode %d\n",
2358c2ecf20Sopenharmony_ci			pi->device, pi->mode);
2368c2ecf20Sopenharmony_ci	   return 1;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (((pi->private%2) == 1) && (pi->mode == 2)) {
2408c2ecf20Sopenharmony_ci	   if (verbose)
2418c2ecf20Sopenharmony_ci		printk("%s: frpw: ASIC does not support mode 2\n",
2428c2ecf20Sopenharmony_ci			pi->device);
2438c2ecf20Sopenharmony_ci	   return 1;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	frpw_connect(pi);
2478c2ecf20Sopenharmony_ci	for (j=0;j<2;j++) {
2488c2ecf20Sopenharmony_ci                frpw_write_regr(pi,0,6,0xa0+j*0x10);
2498c2ecf20Sopenharmony_ci                for (k=0;k<256;k++) {
2508c2ecf20Sopenharmony_ci                        frpw_write_regr(pi,0,2,k^0xaa);
2518c2ecf20Sopenharmony_ci                        frpw_write_regr(pi,0,3,k^0x55);
2528c2ecf20Sopenharmony_ci                        if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
2538c2ecf20Sopenharmony_ci                        }
2548c2ecf20Sopenharmony_ci                }
2558c2ecf20Sopenharmony_ci	frpw_disconnect(pi);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	frpw_connect(pi);
2588c2ecf20Sopenharmony_ci        frpw_read_block_int(pi,scratch,512,0x10);
2598c2ecf20Sopenharmony_ci        r = 0;
2608c2ecf20Sopenharmony_ci        for (k=0;k<128;k++) if (scratch[k] != k) r++;
2618c2ecf20Sopenharmony_ci	frpw_disconnect(pi);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci        if (verbose)  {
2648c2ecf20Sopenharmony_ci            printk("%s: frpw: port 0x%x, chip %ld, mode %d, test=(%d,%d,%d)\n",
2658c2ecf20Sopenharmony_ci                   pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r);
2668c2ecf20Sopenharmony_ci        }
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci        return (r || (e[0] && e[1]));
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic void frpw_log_adapter( PIA *pi, char * scratch, int verbose )
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci{       char    *mode_string[6] = {"4-bit","8-bit","EPP",
2758c2ecf20Sopenharmony_ci				   "EPP-8","EPP-16","EPP-32"};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci        printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device,
2788c2ecf20Sopenharmony_ci		FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port);
2798c2ecf20Sopenharmony_ci        printk("mode %d (%s), delay %d\n",pi->mode,
2808c2ecf20Sopenharmony_ci		mode_string[pi->mode],pi->delay);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic struct pi_protocol frpw = {
2858c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2868c2ecf20Sopenharmony_ci	.name		= "frpw",
2878c2ecf20Sopenharmony_ci	.max_mode	= 6,
2888c2ecf20Sopenharmony_ci	.epp_first	= 2,
2898c2ecf20Sopenharmony_ci	.default_delay	= 2,
2908c2ecf20Sopenharmony_ci	.max_units	= 1,
2918c2ecf20Sopenharmony_ci	.write_regr	= frpw_write_regr,
2928c2ecf20Sopenharmony_ci	.read_regr	= frpw_read_regr,
2938c2ecf20Sopenharmony_ci	.write_block	= frpw_write_block,
2948c2ecf20Sopenharmony_ci	.read_block	= frpw_read_block,
2958c2ecf20Sopenharmony_ci	.connect	= frpw_connect,
2968c2ecf20Sopenharmony_ci	.disconnect	= frpw_disconnect,
2978c2ecf20Sopenharmony_ci	.test_proto	= frpw_test_proto,
2988c2ecf20Sopenharmony_ci	.log_adapter	= frpw_log_adapter,
2998c2ecf20Sopenharmony_ci};
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int __init frpw_init(void)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	return paride_register(&frpw);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic void __exit frpw_exit(void)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	paride_unregister(&frpw);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3128c2ecf20Sopenharmony_cimodule_init(frpw_init)
3138c2ecf20Sopenharmony_cimodule_exit(frpw_exit)
314