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