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