18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci frpw.c (c) 1996-8 Grant R. Guenther <grant@torque.net> 38c2ecf20Sopenharmony_ci Under the terms of the GNU General Public License 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci frpw.c is a low-level protocol driver for the Freecom "Power" 68c2ecf20Sopenharmony_ci parallel port IDE adapter. 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci Some applications of this adapter may require a "printer" reset 98c2ecf20Sopenharmony_ci prior to loading the driver. This can be done by loading and 108c2ecf20Sopenharmony_ci unloading the "lp" driver, or it can be done by this driver 118c2ecf20Sopenharmony_ci if you define FRPW_HARD_RESET. The latter is not recommended 128c2ecf20Sopenharmony_ci as it may upset devices on other ports. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci*/ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* Changes: 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci 1.01 GRG 1998.05.06 init_proto, release_proto 198c2ecf20Sopenharmony_ci fix chip detect 208c2ecf20Sopenharmony_ci added EPP-16 and EPP-32 218c2ecf20Sopenharmony_ci 1.02 GRG 1998.09.23 added hard reset to initialisation process 228c2ecf20Sopenharmony_ci 1.03 GRG 1998.12.14 made hard reset conditional 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci*/ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define FRPW_VERSION "1.03" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/init.h> 308c2ecf20Sopenharmony_ci#include <linux/delay.h> 318c2ecf20Sopenharmony_ci#include <linux/kernel.h> 328c2ecf20Sopenharmony_ci#include <linux/types.h> 338c2ecf20Sopenharmony_ci#include <linux/wait.h> 348c2ecf20Sopenharmony_ci#include <asm/io.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "paride.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4); 398c2ecf20Sopenharmony_ci#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0)) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* cont = 0 - access the IDE register file 428c2ecf20Sopenharmony_ci cont = 1 - access the IDE command set 438c2ecf20Sopenharmony_ci*/ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int cont_map[2] = { 0x08, 0x10 }; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int frpw_read_regr( PIA *pi, int cont, int regr ) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci{ int h,l,r; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci r = regr + cont_map[cont]; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci w2(4); 548c2ecf20Sopenharmony_ci w0(r); cec4; 558c2ecf20Sopenharmony_ci w2(6); l = r1(); 568c2ecf20Sopenharmony_ci w2(4); h = r1(); 578c2ecf20Sopenharmony_ci w2(4); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return j44(l,h); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void frpw_write_regr( PIA *pi, int cont, int regr, int val) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci{ int r; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci r = regr + cont_map[cont]; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci w2(4); w0(r); cec4; 708c2ecf20Sopenharmony_ci w0(val); 718c2ecf20Sopenharmony_ci w2(5);w2(7);w2(5);w2(4); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void frpw_read_block_int( PIA *pi, char * buf, int count, int regr ) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci{ int h, l, k, ph; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci switch(pi->mode) { 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci case 0: w2(4); w0(regr); cec4; 818c2ecf20Sopenharmony_ci for (k=0;k<count;k++) { 828c2ecf20Sopenharmony_ci w2(6); l = r1(); 838c2ecf20Sopenharmony_ci w2(4); h = r1(); 848c2ecf20Sopenharmony_ci buf[k] = j44(l,h); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci w2(4); 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci case 1: ph = 2; 908c2ecf20Sopenharmony_ci w2(4); w0(regr + 0xc0); cec4; 918c2ecf20Sopenharmony_ci w0(0xff); 928c2ecf20Sopenharmony_ci for (k=0;k<count;k++) { 938c2ecf20Sopenharmony_ci w2(0xa4 + ph); 948c2ecf20Sopenharmony_ci buf[k] = r0(); 958c2ecf20Sopenharmony_ci ph = 2 - ph; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); w2(4); 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci case 2: w2(4); w0(regr + 0x80); cec4; 1018c2ecf20Sopenharmony_ci for (k=0;k<count;k++) buf[k] = r4(); 1028c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); 1038c2ecf20Sopenharmony_ci w2(4); 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci case 3: w2(4); w0(regr + 0x80); cec4; 1078c2ecf20Sopenharmony_ci for (k=0;k<count-2;k++) buf[k] = r4(); 1088c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); 1098c2ecf20Sopenharmony_ci buf[count-2] = r4(); 1108c2ecf20Sopenharmony_ci buf[count-1] = r4(); 1118c2ecf20Sopenharmony_ci w2(4); 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci case 4: w2(4); w0(regr + 0x80); cec4; 1158c2ecf20Sopenharmony_ci for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w(); 1168c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); 1178c2ecf20Sopenharmony_ci buf[count-2] = r4(); 1188c2ecf20Sopenharmony_ci buf[count-1] = r4(); 1198c2ecf20Sopenharmony_ci w2(4); 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci case 5: w2(4); w0(regr + 0x80); cec4; 1238c2ecf20Sopenharmony_ci for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l(); 1248c2ecf20Sopenharmony_ci buf[count-4] = r4(); 1258c2ecf20Sopenharmony_ci buf[count-3] = r4(); 1268c2ecf20Sopenharmony_ci w2(0xac); w2(0xa4); 1278c2ecf20Sopenharmony_ci buf[count-2] = r4(); 1288c2ecf20Sopenharmony_ci buf[count-1] = r4(); 1298c2ecf20Sopenharmony_ci w2(4); 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void frpw_read_block( PIA *pi, char * buf, int count) 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci{ frpw_read_block_int(pi,buf,count,0x08); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void frpw_write_block( PIA *pi, char * buf, int count ) 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci{ int k; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci switch(pi->mode) { 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci case 0: 1478c2ecf20Sopenharmony_ci case 1: 1488c2ecf20Sopenharmony_ci case 2: w2(4); w0(8); cec4; w2(5); 1498c2ecf20Sopenharmony_ci for (k=0;k<count;k++) { 1508c2ecf20Sopenharmony_ci w0(buf[k]); 1518c2ecf20Sopenharmony_ci w2(7);w2(5); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci w2(4); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci case 3: w2(4); w0(0xc8); cec4; w2(5); 1578c2ecf20Sopenharmony_ci for (k=0;k<count;k++) w4(buf[k]); 1588c2ecf20Sopenharmony_ci w2(4); 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci case 4: w2(4); w0(0xc8); cec4; w2(5); 1628c2ecf20Sopenharmony_ci for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); 1638c2ecf20Sopenharmony_ci w2(4); 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci case 5: w2(4); w0(0xc8); cec4; w2(5); 1678c2ecf20Sopenharmony_ci for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); 1688c2ecf20Sopenharmony_ci w2(4); 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void frpw_connect ( PIA *pi ) 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci{ pi->saved_r0 = r0(); 1768c2ecf20Sopenharmony_ci pi->saved_r2 = r2(); 1778c2ecf20Sopenharmony_ci w2(4); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void frpw_disconnect ( PIA *pi ) 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci{ w2(4); w0(0x20); cec4; 1838c2ecf20Sopenharmony_ci w0(pi->saved_r0); 1848c2ecf20Sopenharmony_ci w2(pi->saved_r2); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* Stub logic to see if PNP string is available - used to distinguish 1888c2ecf20Sopenharmony_ci between the Xilinx and ASIC implementations of the Freecom adapter. 1898c2ecf20Sopenharmony_ci*/ 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int frpw_test_pnp ( PIA *pi ) 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* returns chip_type: 0 = Xilinx, 1 = ASIC */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci{ int olddelay, a, b; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#ifdef FRPW_HARD_RESET 1988c2ecf20Sopenharmony_ci w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */ 1998c2ecf20Sopenharmony_ci mdelay(1500); 2008c2ecf20Sopenharmony_ci#endif 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci olddelay = pi->delay; 2038c2ecf20Sopenharmony_ci pi->delay = 10; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci pi->saved_r0 = r0(); 2068c2ecf20Sopenharmony_ci pi->saved_r2 = r2(); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci w2(4); w0(4); w2(6); w2(7); 2098c2ecf20Sopenharmony_ci a = r1() & 0xff; w2(4); b = r1() & 0xff; 2108c2ecf20Sopenharmony_ci w2(0xc); w2(0xe); w2(4); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci pi->delay = olddelay; 2138c2ecf20Sopenharmony_ci w0(pi->saved_r0); 2148c2ecf20Sopenharmony_ci w2(pi->saved_r2); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return ((~a&0x40) && (b&0x40)); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* We use the pi->private to remember the result of the PNP test. 2208c2ecf20Sopenharmony_ci To make this work, private = port*2 + chip. Yes, I know it's 2218c2ecf20Sopenharmony_ci a hack :-( 2228c2ecf20Sopenharmony_ci*/ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int frpw_test_proto( PIA *pi, char * scratch, int verbose ) 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci{ int j, k, r; 2278c2ecf20Sopenharmony_ci int e[2] = {0,0}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if ((pi->private>>1) != pi->port) 2308c2ecf20Sopenharmony_ci pi->private = frpw_test_pnp(pi) + 2*pi->port; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (((pi->private%2) == 0) && (pi->mode > 2)) { 2338c2ecf20Sopenharmony_ci if (verbose) 2348c2ecf20Sopenharmony_ci printk("%s: frpw: Xilinx does not support mode %d\n", 2358c2ecf20Sopenharmony_ci pi->device, pi->mode); 2368c2ecf20Sopenharmony_ci return 1; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (((pi->private%2) == 1) && (pi->mode == 2)) { 2408c2ecf20Sopenharmony_ci if (verbose) 2418c2ecf20Sopenharmony_ci printk("%s: frpw: ASIC does not support mode 2\n", 2428c2ecf20Sopenharmony_ci pi->device); 2438c2ecf20Sopenharmony_ci return 1; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci frpw_connect(pi); 2478c2ecf20Sopenharmony_ci for (j=0;j<2;j++) { 2488c2ecf20Sopenharmony_ci frpw_write_regr(pi,0,6,0xa0+j*0x10); 2498c2ecf20Sopenharmony_ci for (k=0;k<256;k++) { 2508c2ecf20Sopenharmony_ci frpw_write_regr(pi,0,2,k^0xaa); 2518c2ecf20Sopenharmony_ci frpw_write_regr(pi,0,3,k^0x55); 2528c2ecf20Sopenharmony_ci if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci frpw_disconnect(pi); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci frpw_connect(pi); 2588c2ecf20Sopenharmony_ci frpw_read_block_int(pi,scratch,512,0x10); 2598c2ecf20Sopenharmony_ci r = 0; 2608c2ecf20Sopenharmony_ci for (k=0;k<128;k++) if (scratch[k] != k) r++; 2618c2ecf20Sopenharmony_ci frpw_disconnect(pi); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (verbose) { 2648c2ecf20Sopenharmony_ci printk("%s: frpw: port 0x%x, chip %ld, mode %d, test=(%d,%d,%d)\n", 2658c2ecf20Sopenharmony_ci pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return (r || (e[0] && e[1])); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void frpw_log_adapter( PIA *pi, char * scratch, int verbose ) 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci{ char *mode_string[6] = {"4-bit","8-bit","EPP", 2758c2ecf20Sopenharmony_ci "EPP-8","EPP-16","EPP-32"}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device, 2788c2ecf20Sopenharmony_ci FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port); 2798c2ecf20Sopenharmony_ci printk("mode %d (%s), delay %d\n",pi->mode, 2808c2ecf20Sopenharmony_ci mode_string[pi->mode],pi->delay); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic struct pi_protocol frpw = { 2858c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2868c2ecf20Sopenharmony_ci .name = "frpw", 2878c2ecf20Sopenharmony_ci .max_mode = 6, 2888c2ecf20Sopenharmony_ci .epp_first = 2, 2898c2ecf20Sopenharmony_ci .default_delay = 2, 2908c2ecf20Sopenharmony_ci .max_units = 1, 2918c2ecf20Sopenharmony_ci .write_regr = frpw_write_regr, 2928c2ecf20Sopenharmony_ci .read_regr = frpw_read_regr, 2938c2ecf20Sopenharmony_ci .write_block = frpw_write_block, 2948c2ecf20Sopenharmony_ci .read_block = frpw_read_block, 2958c2ecf20Sopenharmony_ci .connect = frpw_connect, 2968c2ecf20Sopenharmony_ci .disconnect = frpw_disconnect, 2978c2ecf20Sopenharmony_ci .test_proto = frpw_test_proto, 2988c2ecf20Sopenharmony_ci .log_adapter = frpw_log_adapter, 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int __init frpw_init(void) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci return paride_register(&frpw); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void __exit frpw_exit(void) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci paride_unregister(&frpw); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3128c2ecf20Sopenharmony_cimodule_init(frpw_init) 3138c2ecf20Sopenharmony_cimodule_exit(frpw_exit) 314