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
48static int  cont_map[2] = { 0x08, 0x10 };
49
50static 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
65static 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
76static 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
131static void friq_read_block( PIA *pi, char * buf, int count)
132
133{	friq_read_block_int(pi,buf,count,0x08);
134}
135
136static 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
168static void friq_connect ( PIA *pi  )
169
170{       pi->saved_r0 = r0();
171        pi->saved_r2 = r2();
172	w2(4);
173}
174
175static void friq_disconnect ( PIA *pi )
176
177{       CMD(0x20);
178	w0(pi->saved_r0);
179        w2(pi->saved_r2);
180}
181
182static 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
218static 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
235static void friq_init_proto( PIA *pi)
236
237{       MOD_INC_USE_COUNT;
238	pi->private = 0;
239}
240
241static 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
253struct 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
271int     init_module(void)
272
273{       return pi_register( &friq ) - 1;
274}
275
276void    cleanup_module(void)
277
278{       pi_unregister( &friq );
279}
280
281#endif
282
283/* end of friq.c */
284MODULE_LICENSE("GPL");
285