18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci friq.c (c) 1998 Grant R. Guenther <grant@torque.net> 38c2ecf20Sopenharmony_ci Under the terms of the GNU General Public License 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci friq.c is a low-level protocol driver for the Freecom "IQ" 68c2ecf20Sopenharmony_ci parallel port IDE adapter. Early versions of this adapter 78c2ecf20Sopenharmony_ci use the 'frpw' protocol. 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci Freecom uses this adapter in a battery powered external 108c2ecf20Sopenharmony_ci CD-ROM drive. It is also used in LS-120 drives by 118c2ecf20Sopenharmony_ci Maxell and Panasonic, and other devices. 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci The battery powered drive requires software support to 148c2ecf20Sopenharmony_ci control the power to the drive. This module enables the 158c2ecf20Sopenharmony_ci drive power when the high level driver (pcd) is loaded 168c2ecf20Sopenharmony_ci and disables it when the module is unloaded. Note, if 178c2ecf20Sopenharmony_ci the friq module is built in to the kernel, the power 188c2ecf20Sopenharmony_ci will never be switched off, so other means should be 198c2ecf20Sopenharmony_ci used to conserve battery power. 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci*/ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Changes: 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 1.01 GRG 1998.12.20 Added support for soft power switch 268c2ecf20Sopenharmony_ci*/ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define FRIQ_VERSION "1.01" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/init.h> 328c2ecf20Sopenharmony_ci#include <linux/delay.h> 338c2ecf20Sopenharmony_ci#include <linux/kernel.h> 348c2ecf20Sopenharmony_ci#include <linux/types.h> 358c2ecf20Sopenharmony_ci#include <linux/wait.h> 368c2ecf20Sopenharmony_ci#include <asm/io.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "paride.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define CMD(x) w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\ 418c2ecf20Sopenharmony_ci w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0)) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* cont = 0 - access the IDE register file 468c2ecf20Sopenharmony_ci cont = 1 - access the IDE command set 478c2ecf20Sopenharmony_ci*/ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int cont_map[2] = { 0x08, 0x10 }; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int friq_read_regr( PIA *pi, int cont, int regr ) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci{ int h,l,r; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci r = regr + cont_map[cont]; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci CMD(r); 588c2ecf20Sopenharmony_ci w2(6); l = r1(); 598c2ecf20Sopenharmony_ci w2(4); h = r1(); 608c2ecf20Sopenharmony_ci w2(4); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return j44(l,h); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void friq_write_regr( PIA *pi, int cont, int regr, int val) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci{ int r; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci r = regr + cont_map[cont]; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci CMD(r); 738c2ecf20Sopenharmony_ci w0(val); 748c2ecf20Sopenharmony_ci w2(5);w2(7);w2(5);w2(4); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void friq_read_block_int( PIA *pi, char * buf, int count, int regr ) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci{ int h, l, k, ph; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci switch(pi->mode) { 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci case 0: CMD(regr); 848c2ecf20Sopenharmony_ci for (k=0;k<count;k++) { 858c2ecf20Sopenharmony_ci w2(6); l = r1(); 868c2ecf20Sopenharmony_ci w2(4); h = r1(); 878c2ecf20Sopenharmony_ci buf[k] = j44(l,h); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci w2(4); 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci case 1: ph = 2; 938c2ecf20Sopenharmony_ci CMD(regr+0xc0); 948c2ecf20Sopenharmony_ci w0(0xff); 958c2ecf20Sopenharmony_ci for (k=0;k<count;k++) { 968c2ecf20Sopenharmony_ci w2(0xa4 + ph); 978c2ecf20Sopenharmony_ci buf[k] = r0(); 988c2ecf20Sopenharmony_ci ph = 2 - ph; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); w2(4); 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci case 2: CMD(regr+0x80); 1048c2ecf20Sopenharmony_ci for (k=0;k<count-2;k++) buf[k] = r4(); 1058c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); 1068c2ecf20Sopenharmony_ci buf[count-2] = r4(); 1078c2ecf20Sopenharmony_ci buf[count-1] = r4(); 1088c2ecf20Sopenharmony_ci w2(4); 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci case 3: CMD(regr+0x80); 1128c2ecf20Sopenharmony_ci for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w(); 1138c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); 1148c2ecf20Sopenharmony_ci buf[count-2] = r4(); 1158c2ecf20Sopenharmony_ci buf[count-1] = r4(); 1168c2ecf20Sopenharmony_ci w2(4); 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci case 4: CMD(regr+0x80); 1208c2ecf20Sopenharmony_ci for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l(); 1218c2ecf20Sopenharmony_ci buf[count-4] = r4(); 1228c2ecf20Sopenharmony_ci buf[count-3] = r4(); 1238c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); 1248c2ecf20Sopenharmony_ci buf[count-2] = r4(); 1258c2ecf20Sopenharmony_ci buf[count-1] = r4(); 1268c2ecf20Sopenharmony_ci w2(4); 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void friq_read_block( PIA *pi, char * buf, int count) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci{ friq_read_block_int(pi,buf,count,0x08); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void friq_write_block( PIA *pi, char * buf, int count ) 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci{ int k; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci switch(pi->mode) { 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci case 0: 1448c2ecf20Sopenharmony_ci case 1: CMD(8); w2(5); 1458c2ecf20Sopenharmony_ci for (k=0;k<count;k++) { 1468c2ecf20Sopenharmony_ci w0(buf[k]); 1478c2ecf20Sopenharmony_ci w2(7);w2(5); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci w2(4); 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci case 2: CMD(0xc8); w2(5); 1538c2ecf20Sopenharmony_ci for (k=0;k<count;k++) w4(buf[k]); 1548c2ecf20Sopenharmony_ci w2(4); 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci case 3: CMD(0xc8); w2(5); 1588c2ecf20Sopenharmony_ci for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); 1598c2ecf20Sopenharmony_ci w2(4); 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci case 4: CMD(0xc8); w2(5); 1638c2ecf20Sopenharmony_ci for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); 1648c2ecf20Sopenharmony_ci w2(4); 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void friq_connect ( PIA *pi ) 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci{ pi->saved_r0 = r0(); 1728c2ecf20Sopenharmony_ci pi->saved_r2 = r2(); 1738c2ecf20Sopenharmony_ci w2(4); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void friq_disconnect ( PIA *pi ) 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci{ CMD(0x20); 1798c2ecf20Sopenharmony_ci w0(pi->saved_r0); 1808c2ecf20Sopenharmony_ci w2(pi->saved_r2); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int friq_test_proto( PIA *pi, char * scratch, int verbose ) 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci{ int j, k, r; 1868c2ecf20Sopenharmony_ci int e[2] = {0,0}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci pi->saved_r0 = r0(); 1898c2ecf20Sopenharmony_ci w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */ 1908c2ecf20Sopenharmony_ci udelay(500); 1918c2ecf20Sopenharmony_ci w0(pi->saved_r0); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci friq_connect(pi); 1948c2ecf20Sopenharmony_ci for (j=0;j<2;j++) { 1958c2ecf20Sopenharmony_ci friq_write_regr(pi,0,6,0xa0+j*0x10); 1968c2ecf20Sopenharmony_ci for (k=0;k<256;k++) { 1978c2ecf20Sopenharmony_ci friq_write_regr(pi,0,2,k^0xaa); 1988c2ecf20Sopenharmony_ci friq_write_regr(pi,0,3,k^0x55); 1998c2ecf20Sopenharmony_ci if (friq_read_regr(pi,0,2) != (k^0xaa)) e[j]++; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci friq_disconnect(pi); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci friq_connect(pi); 2058c2ecf20Sopenharmony_ci friq_read_block_int(pi,scratch,512,0x10); 2068c2ecf20Sopenharmony_ci r = 0; 2078c2ecf20Sopenharmony_ci for (k=0;k<128;k++) if (scratch[k] != k) r++; 2088c2ecf20Sopenharmony_ci friq_disconnect(pi); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (verbose) { 2118c2ecf20Sopenharmony_ci printk("%s: friq: port 0x%x, mode %d, test=(%d,%d,%d)\n", 2128c2ecf20Sopenharmony_ci pi->device,pi->port,pi->mode,e[0],e[1],r); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return (r || (e[0] && e[1])); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void friq_log_adapter( PIA *pi, char * scratch, int verbose ) 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci{ char *mode_string[6] = {"4-bit","8-bit", 2228c2ecf20Sopenharmony_ci "EPP-8","EPP-16","EPP-32"}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci printk("%s: friq %s, Freecom IQ ASIC-2 adapter at 0x%x, ", pi->device, 2258c2ecf20Sopenharmony_ci FRIQ_VERSION,pi->port); 2268c2ecf20Sopenharmony_ci printk("mode %d (%s), delay %d\n",pi->mode, 2278c2ecf20Sopenharmony_ci mode_string[pi->mode],pi->delay); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci pi->private = 1; 2308c2ecf20Sopenharmony_ci friq_connect(pi); 2318c2ecf20Sopenharmony_ci CMD(0x9e); /* disable sleep timer */ 2328c2ecf20Sopenharmony_ci friq_disconnect(pi); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void friq_release_proto( PIA *pi) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci if (pi->private) { /* turn off the power */ 2398c2ecf20Sopenharmony_ci friq_connect(pi); 2408c2ecf20Sopenharmony_ci CMD(0x1d); CMD(0x1e); 2418c2ecf20Sopenharmony_ci friq_disconnect(pi); 2428c2ecf20Sopenharmony_ci pi->private = 0; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct pi_protocol friq = { 2478c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2488c2ecf20Sopenharmony_ci .name = "friq", 2498c2ecf20Sopenharmony_ci .max_mode = 5, 2508c2ecf20Sopenharmony_ci .epp_first = 2, 2518c2ecf20Sopenharmony_ci .default_delay = 1, 2528c2ecf20Sopenharmony_ci .max_units = 1, 2538c2ecf20Sopenharmony_ci .write_regr = friq_write_regr, 2548c2ecf20Sopenharmony_ci .read_regr = friq_read_regr, 2558c2ecf20Sopenharmony_ci .write_block = friq_write_block, 2568c2ecf20Sopenharmony_ci .read_block = friq_read_block, 2578c2ecf20Sopenharmony_ci .connect = friq_connect, 2588c2ecf20Sopenharmony_ci .disconnect = friq_disconnect, 2598c2ecf20Sopenharmony_ci .test_proto = friq_test_proto, 2608c2ecf20Sopenharmony_ci .log_adapter = friq_log_adapter, 2618c2ecf20Sopenharmony_ci .release_proto = friq_release_proto, 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int __init friq_init(void) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci return paride_register(&friq); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void __exit friq_exit(void) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci paride_unregister(&friq); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2758c2ecf20Sopenharmony_cimodule_init(friq_init) 2768c2ecf20Sopenharmony_cimodule_exit(friq_exit) 277