1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * (c) 1996-1998 Grant R. Guenther <grant@torque.net> 4 * 5 * frpw.c is a low-level protocol driver for the Freecom "Power" parallel port 6 * IDE adapter. 7 * 8 * Some applications of this adapter may require a "printer" reset prior to 9 * loading the driver. This can be done by loading and unloading the "lp" 10 * driver, or it can be done by this driver if you define FRPW_HARD_RESET. 11 * The latter is not recommended as it may upset devices on other ports. 12 */ 13 14 #include <linux/module.h> 15 #include <linux/init.h> 16 #include <linux/delay.h> 17 #include <linux/kernel.h> 18 #include <linux/types.h> 19 #include <linux/wait.h> 20 #include <asm/io.h> 21 #include "pata_parport.h" 22 23 #define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4); 24 #define j44(l,h) (((l>>4)&0x0f)|(h&0xf0)) 25 26 /* 27 * cont = 0 - access the IDE register file 28 * cont = 1 - access the IDE command set 29 */ 30 static int cont_map[2] = { 0x08, 0x10 }; 31 32 static int frpw_read_regr(struct pi_adapter *pi, int cont, int regr) 33 { 34 int h, l, r; 35 36 r = regr + cont_map[cont]; 37 38 w2(4); 39 w0(r); cec4; 40 w2(6); l = r1(); 41 w2(4); h = r1(); 42 w2(4); 43 44 return j44(l, h); 45 } 46 47 static void frpw_write_regr(struct pi_adapter *pi, int cont, int regr, int val) 48 { 49 int r = regr + cont_map[cont]; 50 51 w2(4); w0(r); cec4; 52 w0(val); 53 w2(5); w2(7); w2(5); w2(4); 54 } 55 56 static void frpw_read_block_int(struct pi_adapter *pi, char *buf, int count, 57 int regr) 58 { 59 int h, l, k, ph; 60 61 switch (pi->mode) { 62 case 0: 63 w2(4); w0(regr); cec4; 64 for (k = 0; k < count; k++) { 65 w2(6); l = r1(); 66 w2(4); h = r1(); 67 buf[k] = j44(l, h); 68 } 69 w2(4); 70 break; 71 72 case 1: 73 ph = 2; 74 w2(4); w0(regr + 0xc0); cec4; 75 w0(0xff); 76 for (k = 0; k < count; k++) { 77 w2(0xa4 + ph); 78 buf[k] = r0(); 79 ph = 2 - ph; 80 } 81 w2(0xac); w2(0xa4); w2(4); 82 break; 83 84 case 2: 85 w2(4); w0(regr + 0x80); cec4; 86 for (k = 0; k < count; k++) 87 buf[k] = r4(); 88 w2(0xac); w2(0xa4); 89 w2(4); 90 break; 91 92 case 3: 93 w2(4); w0(regr + 0x80); cec4; 94 for (k = 0; k < count - 2; k++) 95 buf[k] = r4(); 96 w2(0xac); w2(0xa4); 97 buf[count - 2] = r4(); 98 buf[count - 1] = r4(); 99 w2(4); 100 break; 101 102 case 4: 103 w2(4); w0(regr + 0x80); cec4; 104 for (k = 0; k < count / 2 - 1; k++) 105 ((u16 *)buf)[k] = r4w(); 106 w2(0xac); w2(0xa4); 107 buf[count - 2] = r4(); 108 buf[count - 1] = r4(); 109 w2(4); 110 break; 111 112 case 5: 113 w2(4); w0(regr + 0x80); cec4; 114 for (k = 0; k < count / 4 - 1; k++) 115 ((u32 *)buf)[k] = r4l(); 116 buf[count - 4] = r4(); 117 buf[count - 3] = r4(); 118 w2(0xac); w2(0xa4); 119 buf[count - 2] = r4(); 120 buf[count - 1] = r4(); 121 w2(4); 122 break; 123 } 124 } 125 126 static void frpw_read_block(struct pi_adapter *pi, char *buf, int count) 127 { 128 frpw_read_block_int(pi, buf, count, 0x08); 129 } 130 131 static void frpw_write_block(struct pi_adapter *pi, char *buf, int count) 132 { 133 int k; 134 135 switch (pi->mode) { 136 case 0: 137 case 1: 138 case 2: 139 w2(4); w0(8); cec4; w2(5); 140 for (k = 0; k < count; k++) { 141 w0(buf[k]); 142 w2(7); w2(5); 143 } 144 w2(4); 145 break; 146 147 case 3: 148 w2(4); w0(0xc8); cec4; w2(5); 149 for (k = 0; k < count; k++) 150 w4(buf[k]); 151 w2(4); 152 break; 153 154 case 4: 155 w2(4); w0(0xc8); cec4; w2(5); 156 for (k = 0; k < count / 2; k++) 157 w4w(((u16 *)buf)[k]); 158 w2(4); 159 break; 160 161 case 5: 162 w2(4); w0(0xc8); cec4; w2(5); 163 for (k = 0; k < count / 4; k++) 164 w4l(((u32 *)buf)[k]); 165 w2(4); 166 break; 167 } 168 } 169 170 static void frpw_connect(struct pi_adapter *pi) 171 { 172 pi->saved_r0 = r0(); 173 pi->saved_r2 = r2(); 174 w2(4); 175 } 176 177 static void frpw_disconnect(struct pi_adapter *pi) 178 { 179 w2(4); w0(0x20); cec4; 180 w0(pi->saved_r0); 181 w2(pi->saved_r2); 182 } 183 184 /* 185 * Stub logic to see if PNP string is available - used to distinguish 186 * between the Xilinx and ASIC implementations of the Freecom adapter. 187 * returns chip_type: 0 = Xilinx, 1 = ASIC 188 */ 189 static int frpw_test_pnp(struct pi_adapter *pi) 190 { 191 int olddelay, a, b; 192 193 #ifdef FRPW_HARD_RESET 194 w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */ 195 mdelay(1500); 196 #endif 197 198 olddelay = pi->delay; 199 pi->delay = 10; 200 201 pi->saved_r0 = r0(); 202 pi->saved_r2 = r2(); 203 204 w2(4); w0(4); w2(6); w2(7); 205 a = r1() & 0xff; w2(4); b = r1() & 0xff; 206 w2(0xc); w2(0xe); w2(4); 207 208 pi->delay = olddelay; 209 w0(pi->saved_r0); 210 w2(pi->saved_r2); 211 212 return ((~a & 0x40) && (b & 0x40)); 213 } 214 215 /* 216 * We use the pi->private to remember the result of the PNP test. 217 * To make this work, private = port*2 + chip. Yes, I know it's a hack :-( 218 */ 219 static int frpw_test_proto(struct pi_adapter *pi) 220 { 221 int j, k, r; 222 int e[2] = { 0, 0 }; 223 char scratch[512]; 224 225 if ((pi->private >> 1) != pi->port) 226 pi->private = frpw_test_pnp(pi) + 2*pi->port; 227 228 if (((pi->private & 0x1) == 0) && (pi->mode > 2)) { 229 dev_dbg(&pi->dev, 230 "frpw: Xilinx does not support mode %d\n", pi->mode); 231 return 1; 232 } 233 234 if (((pi->private & 0x1) == 1) && (pi->mode == 2)) { 235 dev_dbg(&pi->dev, "frpw: ASIC does not support mode 2\n"); 236 return 1; 237 } 238 239 frpw_connect(pi); 240 for (j = 0; j < 2; j++) { 241 frpw_write_regr(pi, 0, 6, 0xa0 + j * 0x10); 242 for (k = 0; k < 256; k++) { 243 frpw_write_regr(pi, 0, 2, k ^ 0xaa); 244 frpw_write_regr(pi, 0, 3, k ^ 0x55); 245 if (frpw_read_regr(pi, 0, 2) != (k ^ 0xaa)) 246 e[j]++; 247 } 248 } 249 frpw_disconnect(pi); 250 251 frpw_connect(pi); 252 frpw_read_block_int(pi, scratch, 512, 0x10); 253 r = 0; 254 for (k = 0; k < 128; k++) { 255 if (scratch[k] != k) 256 r++; 257 } 258 frpw_disconnect(pi); 259 260 dev_dbg(&pi->dev, 261 "frpw: port 0x%x, chip %ld, mode %d, test=(%d,%d,%d)\n", 262 pi->port, (pi->private%2), pi->mode, e[0], e[1], r); 263 264 return r || (e[0] && e[1]); 265 } 266 267 static void frpw_log_adapter(struct pi_adapter *pi) 268 269 { 270 char *mode[6] = { "4-bit", "8-bit", "EPP", "EPP-8", "EPP-16", "EPP-32"}; 271 272 dev_info(&pi->dev, 273 "Freecom (%s) adapter at 0x%x, mode %d (%s), delay %d\n", 274 ((pi->private & 0x1) == 0) ? "Xilinx" : "ASIC", 275 pi->port, pi->mode, mode[pi->mode], pi->delay); 276 } 277 278 static struct pi_protocol frpw = { 279 .owner = THIS_MODULE, 280 .name = "frpw", 281 .max_mode = 6, 282 .epp_first = 2, 283 .default_delay = 2, 284 .max_units = 1, 285 .write_regr = frpw_write_regr, 286 .read_regr = frpw_read_regr, 287 .write_block = frpw_write_block, 288 .read_block = frpw_read_block, 289 .connect = frpw_connect, 290 .disconnect = frpw_disconnect, 291 .test_proto = frpw_test_proto, 292 .log_adapter = frpw_log_adapter, 293 }; 294 295 MODULE_LICENSE("GPL"); 296 MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>"); 297 MODULE_DESCRIPTION("Freecom Power parallel port IDE adapter protocol driver"); 298 module_pata_parport_driver(frpw); 299