1 /*
2 	friq.c	(c) 1998    Grant R. Guenther <grant@torque.net>
3 		            Under the terms of the GNU General Public License
4 
5 	friq.c is a low-level protocol driver for the Freecom "IQ"
6 	parallel port IDE adapter.   Early versions of this adapter
7 	use the 'frpw' protocol.
8 
9 	Freecom uses this adapter in a battery powered external
10 	CD-ROM drive.  It is also used in LS-120 drives by
11 	Maxell and Panasonic, and other devices.
12 
13 	The battery powered drive requires software support to
14 	control the power to the drive.  This module enables the
15 	drive power when the high level driver (pcd) is loaded
16 	and disables it when the module is unloaded.  Note, if
17 	the friq module is built in to the kernel, the power
18 	will never be switched off, so other means should be
19 	used to conserve battery power.
20 
21 */
22 
23 /* Changes:
24 
25 	1.01	GRG 1998.12.20	 Added support for soft power switch
26 */
27 
28 #define	FRIQ_VERSION	"1.01"
29 
30 #include <linux/module.h>
31 #include <linux/init.h>
32 #include <linux/delay.h>
33 #include <linux/kernel.h>
34 #include <linux/types.h>
35 #include <linux/wait.h>
36 #include <asm/io.h>
37 
38 #include <linux/pata_parport.h>
39 
40 #define CMD(x)		w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\
41 			w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x);
42 
43 #define j44(l,h)	(((l>>4)&0x0f)|(h&0xf0))
44 
45 /* cont = 0 - access the IDE register file
46    cont = 1 - access the IDE command set
47 */
48 
49 static int  cont_map[2] = { 0x08, 0x10 };
50 
friq_read_regr(PIA * pi,int cont,int regr)51 static int friq_read_regr( PIA *pi, int cont, int regr )
52 
53 {	int	h,l,r;
54 
55 	r = regr + cont_map[cont];
56 
57 	CMD(r);
58 	w2(6); l = r1();
59 	w2(4); h = r1();
60 	w2(4);
61 
62 	return j44(l,h);
63 
64 }
65 
friq_write_regr(PIA * pi,int cont,int regr,int val)66 static void friq_write_regr( PIA *pi, int cont, int regr, int val)
67 
68 {	int r;
69 
70         r = regr + cont_map[cont];
71 
72 	CMD(r);
73 	w0(val);
74 	w2(5);w2(7);w2(5);w2(4);
75 }
76 
friq_read_block_int(PIA * pi,char * buf,int count,int regr)77 static void friq_read_block_int( PIA *pi, char * buf, int count, int regr )
78 
79 {       int     h, l, k, ph;
80 
81         switch(pi->mode) {
82 
83         case 0: CMD(regr);
84                 for (k=0;k<count;k++) {
85                         w2(6); l = r1();
86                         w2(4); h = r1();
87                         buf[k] = j44(l,h);
88                 }
89                 w2(4);
90                 break;
91 
92         case 1: ph = 2;
93                 CMD(regr+0xc0);
94                 w0(0xff);
95                 for (k=0;k<count;k++) {
96                         w2(0xa4 + ph);
97                         buf[k] = r0();
98                         ph = 2 - ph;
99                 }
100                 w2(0xac); w2(0xa4); w2(4);
101                 break;
102 
103 	case 2: CMD(regr+0x80);
104 		for (k=0;k<count-2;k++) buf[k] = r4();
105 		w2(0xac); w2(0xa4);
106 		buf[count-2] = r4();
107 		buf[count-1] = r4();
108 		w2(4);
109 		break;
110 
111 	case 3: CMD(regr+0x80);
112                 for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
113                 w2(0xac); w2(0xa4);
114                 buf[count-2] = r4();
115                 buf[count-1] = r4();
116                 w2(4);
117                 break;
118 
119 	case 4: CMD(regr+0x80);
120                 for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
121                 buf[count-4] = r4();
122                 buf[count-3] = r4();
123                 w2(0xac); w2(0xa4);
124                 buf[count-2] = r4();
125                 buf[count-1] = r4();
126                 w2(4);
127                 break;
128 
129         }
130 }
131 
friq_read_block(PIA * pi,char * buf,int count)132 static void friq_read_block( PIA *pi, char * buf, int count)
133 
134 {	friq_read_block_int(pi,buf,count,0x08);
135 }
136 
friq_write_block(PIA * pi,char * buf,int count)137 static void friq_write_block( PIA *pi, char * buf, int count )
138 
139 {	int	k;
140 
141 	switch(pi->mode) {
142 
143 	case 0:
144 	case 1: CMD(8); w2(5);
145         	for (k=0;k<count;k++) {
146 			w0(buf[k]);
147 			w2(7);w2(5);
148 		}
149 		w2(4);
150 		break;
151 
152 	case 2: CMD(0xc8); w2(5);
153 		for (k=0;k<count;k++) w4(buf[k]);
154 		w2(4);
155 		break;
156 
157         case 3: CMD(0xc8); w2(5);
158                 for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
159                 w2(4);
160                 break;
161 
162         case 4: CMD(0xc8); w2(5);
163                 for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
164                 w2(4);
165                 break;
166 	}
167 }
168 
friq_connect(PIA * pi)169 static void friq_connect ( PIA *pi  )
170 
171 {       pi->saved_r0 = r0();
172         pi->saved_r2 = r2();
173 	w2(4);
174 }
175 
friq_disconnect(PIA * pi)176 static void friq_disconnect ( PIA *pi )
177 
178 {       CMD(0x20);
179 	w0(pi->saved_r0);
180         w2(pi->saved_r2);
181 }
182 
friq_test_proto(PIA * pi,char * scratch,int verbose)183 static int friq_test_proto( PIA *pi, char * scratch, int verbose )
184 
185 {       int     j, k, r;
186 	int	e[2] = {0,0};
187 
188 	pi->saved_r0 = r0();
189 	w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */
190 	udelay(500);
191 	w0(pi->saved_r0);
192 
193 	friq_connect(pi);
194 	for (j=0;j<2;j++) {
195                 friq_write_regr(pi,0,6,0xa0+j*0x10);
196                 for (k=0;k<256;k++) {
197                         friq_write_regr(pi,0,2,k^0xaa);
198                         friq_write_regr(pi,0,3,k^0x55);
199                         if (friq_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
200                         }
201                 }
202 	friq_disconnect(pi);
203 
204 	friq_connect(pi);
205         friq_read_block_int(pi,scratch,512,0x10);
206         r = 0;
207         for (k=0;k<128;k++) if (scratch[k] != k) r++;
208 	friq_disconnect(pi);
209 
210         if (verbose)  {
211             printk("%s: friq: port 0x%x, mode %d, test=(%d,%d,%d)\n",
212                    pi->device,pi->port,pi->mode,e[0],e[1],r);
213         }
214 
215         return (r || (e[0] && e[1]));
216 }
217 
218 
friq_log_adapter(PIA * pi,char * scratch,int verbose)219 static void friq_log_adapter( PIA *pi, char * scratch, int verbose )
220 
221 {       char    *mode_string[6] = {"4-bit","8-bit",
222 				   "EPP-8","EPP-16","EPP-32"};
223 
224         printk("%s: friq %s, Freecom IQ ASIC-2 adapter at 0x%x, ", pi->device,
225 		FRIQ_VERSION,pi->port);
226         printk("mode %d (%s), delay %d\n",pi->mode,
227 		mode_string[pi->mode],pi->delay);
228 
229 	pi->private = 1;
230 	friq_connect(pi);
231 	CMD(0x9e);  		/* disable sleep timer */
232 	friq_disconnect(pi);
233 
234 }
235 
friq_release_proto(PIA * pi)236 static void friq_release_proto( PIA *pi)
237 {
238 	if (pi->private) {		/* turn off the power */
239 		friq_connect(pi);
240 		CMD(0x1d); CMD(0x1e);
241 		friq_disconnect(pi);
242 		pi->private = 0;
243 	}
244 }
245 
246 static struct pi_protocol friq = {
247 	.owner		= THIS_MODULE,
248 	.name		= "friq",
249 	.max_mode	= 5,
250 	.epp_first	= 2,
251 	.default_delay	= 1,
252 	.max_units	= 1,
253 	.write_regr	= friq_write_regr,
254 	.read_regr	= friq_read_regr,
255 	.write_block	= friq_write_block,
256 	.read_block	= friq_read_block,
257 	.connect	= friq_connect,
258 	.disconnect	= friq_disconnect,
259 	.test_proto	= friq_test_proto,
260 	.log_adapter	= friq_log_adapter,
261 	.release_proto	= friq_release_proto,
262 };
263 
friq_init(void)264 static int __init friq_init(void)
265 {
266 	return paride_register(&friq);
267 }
268 
friq_exit(void)269 static void __exit friq_exit(void)
270 {
271 	paride_unregister(&friq);
272 }
273 
274 MODULE_LICENSE("GPL");
275 module_init(friq_init)
276 module_exit(friq_exit)
277