Merge branch 'linus' into stackprotector
[linux-2.6] / drivers / block / paride / friq.c
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 "paride.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
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
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
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
132 static void friq_read_block( PIA *pi, char * buf, int count)
133
134 {       friq_read_block_int(pi,buf,count,0x08);
135 }
136
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
169 static void friq_connect ( PIA *pi  )
170
171 {       pi->saved_r0 = r0();
172         pi->saved_r2 = r2();
173         w2(4);
174 }
175
176 static void friq_disconnect ( PIA *pi )
177
178 {       CMD(0x20);
179         w0(pi->saved_r0);
180         w2(pi->saved_r2);
181
182
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
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
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
264 static int __init friq_init(void)
265 {
266         return paride_register(&friq);
267 }
268
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)