1*bd91cbfaSDamien Le Moal // SPDX-License-Identifier: GPL-2.0-or-later 272f2b0b2SOndrej Zary /* 3*bd91cbfaSDamien Le Moal * (c) 1998 Grant R. Guenther <grant@torque.net> 4*bd91cbfaSDamien Le Moal * 5*bd91cbfaSDamien Le Moal * friq.c is a low-level protocol driver for the Freecom "IQ" 6*bd91cbfaSDamien Le Moal * parallel port IDE adapter. Early versions of this adapter 7*bd91cbfaSDamien Le Moal * use the 'frpw' protocol. 8*bd91cbfaSDamien Le Moal * 9*bd91cbfaSDamien Le Moal * Freecom uses this adapter in a battery powered external 10*bd91cbfaSDamien Le Moal * CD-ROM drive. It is also used in LS-120 drives by 11*bd91cbfaSDamien Le Moal * Maxell and Panasonic, and other devices. 12*bd91cbfaSDamien Le Moal * 13*bd91cbfaSDamien Le Moal * The battery powered drive requires software support to 14*bd91cbfaSDamien Le Moal * control the power to the drive. This module enables the 15*bd91cbfaSDamien Le Moal * drive power when the high level driver (pcd) is loaded 16*bd91cbfaSDamien Le Moal * and disables it when the module is unloaded. Note, if 17*bd91cbfaSDamien Le Moal * the friq module is built in to the kernel, the power 18*bd91cbfaSDamien Le Moal * will never be switched off, so other means should be 19*bd91cbfaSDamien Le Moal * used to conserve battery power. 2072f2b0b2SOndrej Zary */ 2172f2b0b2SOndrej Zary 2272f2b0b2SOndrej Zary #include <linux/module.h> 2372f2b0b2SOndrej Zary #include <linux/init.h> 2472f2b0b2SOndrej Zary #include <linux/delay.h> 2572f2b0b2SOndrej Zary #include <linux/kernel.h> 2672f2b0b2SOndrej Zary #include <linux/types.h> 2772f2b0b2SOndrej Zary #include <linux/wait.h> 2872f2b0b2SOndrej Zary #include <asm/io.h> 29fe027ff9SOndrej Zary #include "pata_parport.h" 3072f2b0b2SOndrej Zary 31*bd91cbfaSDamien Le Moal #define CMD(x) \ 32*bd91cbfaSDamien Le Moal do { \ 33*bd91cbfaSDamien Le Moal w2(4); w0(0xff); w0(0xff); w0(0x73); w0(0x73); \ 34*bd91cbfaSDamien Le Moal w0(0xc9); w0(0xc9); w0(0x26); \ 35*bd91cbfaSDamien Le Moal w0(0x26); w0(x); w0(x); \ 36*bd91cbfaSDamien Le Moal } while (0) 3772f2b0b2SOndrej Zary 3872f2b0b2SOndrej Zary #define j44(l, h) (((l >> 4) & 0x0f) | (h & 0xf0)) 3972f2b0b2SOndrej Zary 40*bd91cbfaSDamien Le Moal /* 41*bd91cbfaSDamien Le Moal * cont = 0 - access the IDE register file 42*bd91cbfaSDamien Le Moal * cont = 1 - access the IDE command set 4372f2b0b2SOndrej Zary */ 4472f2b0b2SOndrej Zary static int cont_map[2] = { 0x08, 0x10 }; 4572f2b0b2SOndrej Zary 46882ff0caSOndrej Zary static int friq_read_regr(struct pi_adapter *pi, int cont, int regr) 47*bd91cbfaSDamien Le Moal { 48*bd91cbfaSDamien Le Moal int h, l, r; 4972f2b0b2SOndrej Zary 5072f2b0b2SOndrej Zary r = regr + cont_map[cont]; 5172f2b0b2SOndrej Zary 5272f2b0b2SOndrej Zary CMD(r); 5372f2b0b2SOndrej Zary w2(6); l = r1(); 5472f2b0b2SOndrej Zary w2(4); h = r1(); 5572f2b0b2SOndrej Zary w2(4); 5672f2b0b2SOndrej Zary 5772f2b0b2SOndrej Zary return j44(l, h); 5872f2b0b2SOndrej Zary } 5972f2b0b2SOndrej Zary 60882ff0caSOndrej Zary static void friq_write_regr(struct pi_adapter *pi, int cont, int regr, int val) 61*bd91cbfaSDamien Le Moal { 62*bd91cbfaSDamien Le Moal int r = regr + cont_map[cont]; 6372f2b0b2SOndrej Zary 6472f2b0b2SOndrej Zary CMD(r); 6572f2b0b2SOndrej Zary w0(val); 6672f2b0b2SOndrej Zary w2(5); w2(7); w2(5); w2(4); 6772f2b0b2SOndrej Zary } 6872f2b0b2SOndrej Zary 69882ff0caSOndrej Zary static void friq_read_block_int(struct pi_adapter *pi, char *buf, int count, int regr) 70*bd91cbfaSDamien Le Moal { 71*bd91cbfaSDamien Le Moal int h, l, k, ph; 7272f2b0b2SOndrej Zary 7372f2b0b2SOndrej Zary switch (pi->mode) { 74*bd91cbfaSDamien Le Moal case 0: 75*bd91cbfaSDamien Le Moal CMD(regr); 7672f2b0b2SOndrej Zary for (k = 0; k < count; k++) { 7772f2b0b2SOndrej Zary w2(6); l = r1(); 7872f2b0b2SOndrej Zary w2(4); h = r1(); 7972f2b0b2SOndrej Zary buf[k] = j44(l, h); 8072f2b0b2SOndrej Zary } 8172f2b0b2SOndrej Zary w2(4); 8272f2b0b2SOndrej Zary break; 83*bd91cbfaSDamien Le Moal case 1: 84*bd91cbfaSDamien Le Moal ph = 2; 8572f2b0b2SOndrej Zary CMD(regr + 0xc0); 8672f2b0b2SOndrej Zary w0(0xff); 8772f2b0b2SOndrej Zary for (k = 0; k < count; k++) { 8872f2b0b2SOndrej Zary w2(0xa4 + ph); 8972f2b0b2SOndrej Zary buf[k] = r0(); 9072f2b0b2SOndrej Zary ph = 2 - ph; 9172f2b0b2SOndrej Zary } 9272f2b0b2SOndrej Zary w2(0xac); w2(0xa4); w2(4); 9372f2b0b2SOndrej Zary break; 94*bd91cbfaSDamien Le Moal case 2: 95*bd91cbfaSDamien Le Moal CMD(regr + 0x80); 96*bd91cbfaSDamien Le Moal for (k = 0; k < count - 2; k++) 97*bd91cbfaSDamien Le Moal buf[k] = r4(); 9872f2b0b2SOndrej Zary w2(0xac); w2(0xa4); 9972f2b0b2SOndrej Zary buf[count - 2] = r4(); 10072f2b0b2SOndrej Zary buf[count - 1] = r4(); 10172f2b0b2SOndrej Zary w2(4); 10272f2b0b2SOndrej Zary break; 103*bd91cbfaSDamien Le Moal case 3: 104*bd91cbfaSDamien Le Moal CMD(regr + 0x80); 105*bd91cbfaSDamien Le Moal for (k = 0; k < count / 2 - 1; k++) 106*bd91cbfaSDamien Le Moal ((u16 *)buf)[k] = r4w(); 10772f2b0b2SOndrej Zary w2(0xac); w2(0xa4); 10872f2b0b2SOndrej Zary buf[count - 2] = r4(); 10972f2b0b2SOndrej Zary buf[count - 1] = r4(); 11072f2b0b2SOndrej Zary w2(4); 11172f2b0b2SOndrej Zary break; 112*bd91cbfaSDamien Le Moal case 4: 113*bd91cbfaSDamien Le Moal CMD(regr + 0x80); 114*bd91cbfaSDamien Le Moal for (k = 0; k < count / 4 - 1; k++) 115*bd91cbfaSDamien Le Moal ((u32 *)buf)[k] = r4l(); 11672f2b0b2SOndrej Zary buf[count - 4] = r4(); 11772f2b0b2SOndrej Zary buf[count - 3] = r4(); 11872f2b0b2SOndrej Zary w2(0xac); w2(0xa4); 11972f2b0b2SOndrej Zary buf[count - 2] = r4(); 12072f2b0b2SOndrej Zary buf[count - 1] = r4(); 12172f2b0b2SOndrej Zary w2(4); 12272f2b0b2SOndrej Zary break; 12372f2b0b2SOndrej Zary } 12472f2b0b2SOndrej Zary } 12572f2b0b2SOndrej Zary 126882ff0caSOndrej Zary static void friq_read_block(struct pi_adapter *pi, char *buf, int count) 127*bd91cbfaSDamien Le Moal { 128*bd91cbfaSDamien Le Moal friq_read_block_int(pi, buf, count, 0x08); 12972f2b0b2SOndrej Zary } 13072f2b0b2SOndrej Zary 131882ff0caSOndrej Zary static void friq_write_block(struct pi_adapter *pi, char *buf, int count) 132*bd91cbfaSDamien Le Moal { 133*bd91cbfaSDamien Le Moal int k; 13472f2b0b2SOndrej Zary 13572f2b0b2SOndrej Zary switch (pi->mode) { 13672f2b0b2SOndrej Zary case 0: 137*bd91cbfaSDamien Le Moal case 1: 138*bd91cbfaSDamien Le Moal CMD(8); w2(5); 13972f2b0b2SOndrej Zary for (k = 0; k < count; k++) { 14072f2b0b2SOndrej Zary w0(buf[k]); 14172f2b0b2SOndrej Zary w2(7); w2(5); 14272f2b0b2SOndrej Zary } 14372f2b0b2SOndrej Zary w2(4); 14472f2b0b2SOndrej Zary break; 145*bd91cbfaSDamien Le Moal case 2: 146*bd91cbfaSDamien Le Moal CMD(0xc8); w2(5); 147*bd91cbfaSDamien Le Moal for (k = 0; k < count; k++) 148*bd91cbfaSDamien Le Moal w4(buf[k]); 14972f2b0b2SOndrej Zary w2(4); 15072f2b0b2SOndrej Zary break; 151*bd91cbfaSDamien Le Moal case 3: 152*bd91cbfaSDamien Le Moal CMD(0xc8); w2(5); 153*bd91cbfaSDamien Le Moal for (k = 0; k < count / 2; k++) 154*bd91cbfaSDamien Le Moal w4w(((u16 *)buf)[k]); 15572f2b0b2SOndrej Zary w2(4); 15672f2b0b2SOndrej Zary break; 157*bd91cbfaSDamien Le Moal case 4: 158*bd91cbfaSDamien Le Moal CMD(0xc8); w2(5); 159*bd91cbfaSDamien Le Moal for (k = 0; k < count / 4; k++) 160*bd91cbfaSDamien Le Moal w4l(((u32 *)buf)[k]); 16172f2b0b2SOndrej Zary w2(4); 16272f2b0b2SOndrej Zary break; 16372f2b0b2SOndrej Zary } 16472f2b0b2SOndrej Zary } 16572f2b0b2SOndrej Zary 166882ff0caSOndrej Zary static void friq_connect(struct pi_adapter *pi) 167*bd91cbfaSDamien Le Moal { 168*bd91cbfaSDamien Le Moal pi->saved_r0 = r0(); 16972f2b0b2SOndrej Zary pi->saved_r2 = r2(); 17072f2b0b2SOndrej Zary w2(4); 17172f2b0b2SOndrej Zary } 17272f2b0b2SOndrej Zary 173882ff0caSOndrej Zary static void friq_disconnect(struct pi_adapter *pi) 174*bd91cbfaSDamien Le Moal { 175*bd91cbfaSDamien Le Moal CMD(0x20); 17672f2b0b2SOndrej Zary w0(pi->saved_r0); 17772f2b0b2SOndrej Zary w2(pi->saved_r2); 17872f2b0b2SOndrej Zary } 17972f2b0b2SOndrej Zary 180b42251a8SOndrej Zary static int friq_test_proto(struct pi_adapter *pi) 181*bd91cbfaSDamien Le Moal { 182*bd91cbfaSDamien Le Moal int j, k, r; 18372f2b0b2SOndrej Zary int e[2] = { 0, 0 }; 184b42251a8SOndrej Zary char scratch[512]; 18572f2b0b2SOndrej Zary 18672f2b0b2SOndrej Zary pi->saved_r0 = r0(); 18772f2b0b2SOndrej Zary w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */ 18872f2b0b2SOndrej Zary udelay(500); 18972f2b0b2SOndrej Zary w0(pi->saved_r0); 19072f2b0b2SOndrej Zary 19172f2b0b2SOndrej Zary friq_connect(pi); 19272f2b0b2SOndrej Zary for (j = 0; j < 2; j++) { 19372f2b0b2SOndrej Zary friq_write_regr(pi, 0, 6, 0xa0 + j * 0x10); 19472f2b0b2SOndrej Zary for (k = 0; k < 256; k++) { 19572f2b0b2SOndrej Zary friq_write_regr(pi, 0, 2, k ^ 0xaa); 19672f2b0b2SOndrej Zary friq_write_regr(pi, 0, 3, k ^ 0x55); 197*bd91cbfaSDamien Le Moal if (friq_read_regr(pi, 0, 2) != (k ^ 0xaa)) 198*bd91cbfaSDamien Le Moal e[j]++; 19972f2b0b2SOndrej Zary } 20072f2b0b2SOndrej Zary } 20172f2b0b2SOndrej Zary friq_disconnect(pi); 20272f2b0b2SOndrej Zary 20372f2b0b2SOndrej Zary friq_connect(pi); 20472f2b0b2SOndrej Zary friq_read_block_int(pi, scratch, 512, 0x10); 20572f2b0b2SOndrej Zary r = 0; 206*bd91cbfaSDamien Le Moal for (k = 0; k < 128; k++) { 207*bd91cbfaSDamien Le Moal if (scratch[k] != k) 208*bd91cbfaSDamien Le Moal r++; 209*bd91cbfaSDamien Le Moal } 21072f2b0b2SOndrej Zary friq_disconnect(pi); 21172f2b0b2SOndrej Zary 212*bd91cbfaSDamien Le Moal dev_dbg(&pi->dev, 213*bd91cbfaSDamien Le Moal "friq: port 0x%x, mode %d, test=(%d,%d,%d)\n", 214a4f2ff92SOndrej Zary pi->port, pi->mode, e[0], e[1], r); 21572f2b0b2SOndrej Zary 216*bd91cbfaSDamien Le Moal return r || (e[0] && e[1]); 21772f2b0b2SOndrej Zary } 21872f2b0b2SOndrej Zary 2195b77db9cSOndrej Zary static void friq_log_adapter(struct pi_adapter *pi) 220*bd91cbfaSDamien Le Moal { 221*bd91cbfaSDamien Le Moal char *mode_string[6] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32"}; 22272f2b0b2SOndrej Zary 223*bd91cbfaSDamien Le Moal dev_info(&pi->dev, 224*bd91cbfaSDamien Le Moal "Freecom IQ ASIC-2 adapter at 0x%x, mode %d (%s), delay %d\n", 225426eb3c5SOndrej Zary pi->port, pi->mode, mode_string[pi->mode], pi->delay); 22672f2b0b2SOndrej Zary 22772f2b0b2SOndrej Zary pi->private = 1; 22872f2b0b2SOndrej Zary friq_connect(pi); 22972f2b0b2SOndrej Zary CMD(0x9e); /* disable sleep timer */ 23072f2b0b2SOndrej Zary friq_disconnect(pi); 23172f2b0b2SOndrej Zary } 23272f2b0b2SOndrej Zary 233882ff0caSOndrej Zary static void friq_release_proto(struct pi_adapter *pi) 23472f2b0b2SOndrej Zary { 23572f2b0b2SOndrej Zary if (pi->private) { /* turn off the power */ 23672f2b0b2SOndrej Zary friq_connect(pi); 23772f2b0b2SOndrej Zary CMD(0x1d); CMD(0x1e); 23872f2b0b2SOndrej Zary friq_disconnect(pi); 23972f2b0b2SOndrej Zary pi->private = 0; 24072f2b0b2SOndrej Zary } 24172f2b0b2SOndrej Zary } 24272f2b0b2SOndrej Zary 24372f2b0b2SOndrej Zary static struct pi_protocol friq = { 24472f2b0b2SOndrej Zary .owner = THIS_MODULE, 24572f2b0b2SOndrej Zary .name = "friq", 24672f2b0b2SOndrej Zary .max_mode = 5, 24772f2b0b2SOndrej Zary .epp_first = 2, 24872f2b0b2SOndrej Zary .default_delay = 1, 24972f2b0b2SOndrej Zary .max_units = 1, 25072f2b0b2SOndrej Zary .write_regr = friq_write_regr, 25172f2b0b2SOndrej Zary .read_regr = friq_read_regr, 25272f2b0b2SOndrej Zary .write_block = friq_write_block, 25372f2b0b2SOndrej Zary .read_block = friq_read_block, 25472f2b0b2SOndrej Zary .connect = friq_connect, 25572f2b0b2SOndrej Zary .disconnect = friq_disconnect, 25672f2b0b2SOndrej Zary .test_proto = friq_test_proto, 25772f2b0b2SOndrej Zary .log_adapter = friq_log_adapter, 25872f2b0b2SOndrej Zary .release_proto = friq_release_proto, 25972f2b0b2SOndrej Zary }; 26072f2b0b2SOndrej Zary 26172f2b0b2SOndrej Zary MODULE_LICENSE("GPL"); 2622c08ec0fSOndrej Zary module_pata_parport_driver(friq); 263