friq.c (6365B)
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 49static int cont_map[2] = { 0x08, 0x10 }; 50 51static 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 66static 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 77static 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 132static void friq_read_block( PIA *pi, char * buf, int count) 133 134{ friq_read_block_int(pi,buf,count,0x08); 135} 136 137static 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 169static void friq_connect ( PIA *pi ) 170 171{ pi->saved_r0 = r0(); 172 pi->saved_r2 = r2(); 173 w2(4); 174} 175 176static void friq_disconnect ( PIA *pi ) 177 178{ CMD(0x20); 179 w0(pi->saved_r0); 180 w2(pi->saved_r2); 181} 182 183static 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 219static 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 236static 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 246static 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 264static int __init friq_init(void) 265{ 266 return paride_register(&friq); 267} 268 269static void __exit friq_exit(void) 270{ 271 paride_unregister(&friq); 272} 273 274MODULE_LICENSE("GPL"); 275module_init(friq_init) 276module_exit(friq_exit)