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