299 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 port6 * IDE adapter.7 *8 * Some applications of this adapter may require a "printer" reset prior to9 * 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 file28 * cont = 1 - access the IDE command set29 */30static int cont_map[2] = { 0x08, 0x10 };31 32static 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 47static 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 56static 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 126static void frpw_read_block(struct pi_adapter *pi, char *buf, int count)127{128 frpw_read_block_int(pi, buf, count, 0x08);129}130 131static 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 170static void frpw_connect(struct pi_adapter *pi)171{172 pi->saved_r0 = r0();173 pi->saved_r2 = r2();174 w2(4);175}176 177static 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 distinguish186 * between the Xilinx and ASIC implementations of the Freecom adapter.187 * returns chip_type: 0 = Xilinx, 1 = ASIC188 */189static int frpw_test_pnp(struct pi_adapter *pi)190{191 int olddelay, a, b;192 193#ifdef FRPW_HARD_RESET194 w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */195 mdelay(1500);196#endif197 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 */219static 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 267static 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 278static 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 295MODULE_LICENSE("GPL");296MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");297MODULE_DESCRIPTION("Freecom Power parallel port IDE adapter protocol driver");298module_pata_parport_driver(frpw);299