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/delay.h>
32 #include <linux/kernel.h>
33 #include <linux/types.h>
34 #include <linux/wait.h>
35 #include <asm/io.h>
36 
37 #include "paride.h"
38 
39 #define CMD(x)		w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\
40 			w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x);
41 
42 #define j44(l,h)	(((l>>4)&0x0f)|(h&0xf0))
43 
44 /* cont = 0 - access the IDE register file
45    cont = 1 - access the IDE command set
46 */
47 
48 static int  cont_map[2] = { 0x08, 0x10 };
49 
friq_read_regr(PIA * pi,int cont,int regr)50 static int friq_read_regr( PIA *pi, int cont, int regr )
51 
52 {	int	h,l,r;
53 
54 	r = regr + cont_map[cont];
55 
56 	CMD(r);
57 	w2(6); l = r1();
58 	w2(4); h = r1();
59 	w2(4);
60 
61 	return j44(l,h);
62 
63 }
64 
friq_write_regr(PIA * pi,int cont,int regr,int val)65 static void friq_write_regr( PIA *pi, int cont, int regr, int val)
66 
67 {	int r;
68 
69         r = regr + cont_map[cont];
70 
71 	CMD(r);
72 	w0(val);
73 	w2(5);w2(7);w2(5);w2(4);
74 }
75 
friq_read_block_int(PIA * pi,char * buf,int count,int regr)76 static void friq_read_block_int( PIA *pi, char * buf, int count, int regr )
77 
78 {       int     h, l, k, ph;
79 
80         switch(pi->mode) {
81 
82         case 0: CMD(regr);
83                 for (k=0;k<count;k++) {
84                         w2(6); l = r1();
85                         w2(4); h = r1();
86                         buf[k] = j44(l,h);
87                 }
88                 w2(4);
89                 break;
90 
91         case 1: ph = 2;
92                 CMD(regr+0xc0);
93                 w0(0xff);
94                 for (k=0;k<count;k++) {
95                         w2(0xa4 + ph);
96                         buf[k] = r0();
97                         ph = 2 - ph;
98                 }
99                 w2(0xac); w2(0xa4); w2(4);
100                 break;
101 
102 	case 2: CMD(regr+0x80);
103 		for (k=0;k<count-2;k++) buf[k] = r4();
104 		w2(0xac); w2(0xa4);
105 		buf[count-2] = r4();
106 		buf[count-1] = r4();
107 		w2(4);
108 		break;
109 
110 	case 3: CMD(regr+0x80);
111                 for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
112                 w2(0xac); w2(0xa4);
113                 buf[count-2] = r4();
114                 buf[count-1] = r4();
115                 w2(4);
116                 break;
117 
118 	case 4: CMD(regr+0x80);
119                 for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
120                 buf[count-4] = r4();
121                 buf[count-3] = r4();
122                 w2(0xac); w2(0xa4);
123                 buf[count-2] = r4();
124                 buf[count-1] = r4();
125                 w2(4);
126                 break;
127 
128         }
129 }
130 
friq_read_block(PIA * pi,char * buf,int count)131 static void friq_read_block( PIA *pi, char * buf, int count)
132 
133 {	friq_read_block_int(pi,buf,count,0x08);
134 }
135 
friq_write_block(PIA * pi,char * buf,int count)136 static void friq_write_block( PIA *pi, char * buf, int count )
137 
138 {	int	k;
139 
140 	switch(pi->mode) {
141 
142 	case 0:
143 	case 1: CMD(8); w2(5);
144         	for (k=0;k<count;k++) {
145 			w0(buf[k]);
146 			w2(7);w2(5);
147 		}
148 		w2(4);
149 		break;
150 
151 	case 2: CMD(0xc8); w2(5);
152 		for (k=0;k<count;k++) w4(buf[k]);
153 		w2(4);
154 		break;
155 
156         case 3: CMD(0xc8); w2(5);
157                 for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
158                 w2(4);
159                 break;
160 
161         case 4: CMD(0xc8); w2(5);
162                 for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
163                 w2(4);
164                 break;
165 	}
166 }
167 
friq_connect(PIA * pi)168 static void friq_connect ( PIA *pi  )
169 
170 {       pi->saved_r0 = r0();
171         pi->saved_r2 = r2();
172 	w2(4);
173 }
174 
friq_disconnect(PIA * pi)175 static void friq_disconnect ( PIA *pi )
176 
177 {       CMD(0x20);
178 	w0(pi->saved_r0);
179         w2(pi->saved_r2);
180 }
181 
friq_test_proto(PIA * pi,char * scratch,int verbose)182 static int friq_test_proto( PIA *pi, char * scratch, int verbose )
183 
184 {       int     j, k, r;
185 	int	e[2] = {0,0};
186 
187 	pi->saved_r0 = r0();
188 	w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */
189 	udelay(500);
190 	w0(pi->saved_r0);
191 
192 	friq_connect(pi);
193 	for (j=0;j<2;j++) {
194                 friq_write_regr(pi,0,6,0xa0+j*0x10);
195                 for (k=0;k<256;k++) {
196                         friq_write_regr(pi,0,2,k^0xaa);
197                         friq_write_regr(pi,0,3,k^0x55);
198                         if (friq_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
199                         }
200                 }
201 	friq_disconnect(pi);
202 
203 	friq_connect(pi);
204         friq_read_block_int(pi,scratch,512,0x10);
205         r = 0;
206         for (k=0;k<128;k++) if (scratch[k] != k) r++;
207 	friq_disconnect(pi);
208 
209         if (verbose)  {
210             printk("%s: friq: port 0x%x, mode %d, test=(%d,%d,%d)\n",
211                    pi->device,pi->port,pi->mode,e[0],e[1],r);
212         }
213 
214         return (r || (e[0] && e[1]));
215 }
216 
217 
friq_log_adapter(PIA * pi,char * scratch,int verbose)218 static void friq_log_adapter( PIA *pi, char * scratch, int verbose )
219 
220 {       char    *mode_string[6] = {"4-bit","8-bit",
221 				   "EPP-8","EPP-16","EPP-32"};
222 
223         printk("%s: friq %s, Freecom IQ ASIC-2 adapter at 0x%x, ", pi->device,
224 		FRIQ_VERSION,pi->port);
225         printk("mode %d (%s), delay %d\n",pi->mode,
226 		mode_string[pi->mode],pi->delay);
227 
228 	pi->private = 1;
229 	friq_connect(pi);
230 	CMD(0x9e);  		/* disable sleep timer */
231 	friq_disconnect(pi);
232 
233 }
234 
friq_init_proto(PIA * pi)235 static void friq_init_proto( PIA *pi)
236 
237 {       MOD_INC_USE_COUNT;
238 	pi->private = 0;
239 }
240 
friq_release_proto(PIA * pi)241 static void friq_release_proto( PIA *pi)
242 
243 {       if (pi->private) {		/* turn off the power */
244 		friq_connect(pi);
245 		CMD(0x1d); CMD(0x1e);
246 		friq_disconnect(pi);
247 		pi->private = 0;
248 	}
249 
250 	MOD_DEC_USE_COUNT;
251 }
252 
253 struct pi_protocol friq = {"friq",0,5,2,1,1,
254                            friq_write_regr,
255                            friq_read_regr,
256                            friq_write_block,
257                            friq_read_block,
258                            friq_connect,
259                            friq_disconnect,
260                            0,
261                            0,
262                            friq_test_proto,
263                            friq_log_adapter,
264                            friq_init_proto,
265                            friq_release_proto
266                           };
267 
268 
269 #ifdef MODULE
270 
init_module(void)271 int     init_module(void)
272 
273 {       return pi_register( &friq ) - 1;
274 }
275 
cleanup_module(void)276 void    cleanup_module(void)
277 
278 {       pi_unregister( &friq );
279 }
280 
281 #endif
282 
283 /* end of friq.c */
284 MODULE_LICENSE("GPL");
285