18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci on26.c (c) 1997-8 Grant R. Guenther <grant@torque.net> 38c2ecf20Sopenharmony_ci Under the terms of the GNU General Public License. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci on26.c is a low-level protocol driver for the 68c2ecf20Sopenharmony_ci OnSpec 90c26 parallel to IDE adapter chip. 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci*/ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* Changes: 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci 1.01 GRG 1998.05.06 init_proto, release_proto 138c2ecf20Sopenharmony_ci 1.02 GRG 1998.09.23 updates for the -E rev chip 148c2ecf20Sopenharmony_ci 1.03 GRG 1998.12.14 fix for slave drives 158c2ecf20Sopenharmony_ci 1.04 GRG 1998.12.20 yet another bug fix 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci*/ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define ON26_VERSION "1.04" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/kernel.h> 258c2ecf20Sopenharmony_ci#include <linux/types.h> 268c2ecf20Sopenharmony_ci#include <linux/wait.h> 278c2ecf20Sopenharmony_ci#include <asm/io.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "paride.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* mode codes: 0 nybble reads, 8-bit writes 328c2ecf20Sopenharmony_ci 1 8-bit reads and writes 338c2ecf20Sopenharmony_ci 2 8-bit EPP mode 348c2ecf20Sopenharmony_ci 3 EPP-16 358c2ecf20Sopenharmony_ci 4 EPP-32 368c2ecf20Sopenharmony_ci*/ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define P1 w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); 418c2ecf20Sopenharmony_ci#define P2 w2(5);w2(7);w2(5);w2(4); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* cont = 0 - access the IDE register file 448c2ecf20Sopenharmony_ci cont = 1 - access the IDE command set 458c2ecf20Sopenharmony_ci*/ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int on26_read_regr( PIA *pi, int cont, int regr ) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci{ int a, b, r; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci r = (regr<<2) + 1 + cont; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci switch (pi->mode) { 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci case 0: w0(1); P1; w0(r); P2; w0(0); P1; 568c2ecf20Sopenharmony_ci w2(6); a = r1(); w2(4); 578c2ecf20Sopenharmony_ci w2(6); b = r1(); w2(4); 588c2ecf20Sopenharmony_ci w2(6); w2(4); w2(6); w2(4); 598c2ecf20Sopenharmony_ci return j44(a,b); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci case 1: w0(1); P1; w0(r); P2; w0(0); P1; 628c2ecf20Sopenharmony_ci w2(0x26); a = r0(); w2(4); w2(0x26); w2(4); 638c2ecf20Sopenharmony_ci return a; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci case 2: 668c2ecf20Sopenharmony_ci case 3: 678c2ecf20Sopenharmony_ci case 4: w3(1); w3(1); w2(5); w4(r); w2(4); 688c2ecf20Sopenharmony_ci w3(0); w3(0); w2(0x24); a = r4(); w2(4); 698c2ecf20Sopenharmony_ci w2(0x24); (void)r4(); w2(4); 708c2ecf20Sopenharmony_ci return a; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci return -1; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void on26_write_regr( PIA *pi, int cont, int regr, int val ) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci{ int r; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci r = (regr<<2) + 1 + cont; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci switch (pi->mode) { 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci case 0: 858c2ecf20Sopenharmony_ci case 1: w0(1); P1; w0(r); P2; w0(0); P1; 868c2ecf20Sopenharmony_ci w0(val); P2; w0(val); P2; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci case 2: 908c2ecf20Sopenharmony_ci case 3: 918c2ecf20Sopenharmony_ci case 4: w3(1); w3(1); w2(5); w4(r); w2(4); 928c2ecf20Sopenharmony_ci w3(0); w3(0); 938c2ecf20Sopenharmony_ci w2(5); w4(val); w2(4); 948c2ecf20Sopenharmony_ci w2(5); w4(val); w2(4); 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define CCP(x) w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ 1008c2ecf20Sopenharmony_ci w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void on26_connect ( PIA *pi ) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci{ int x; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci pi->saved_r0 = r0(); 1078c2ecf20Sopenharmony_ci pi->saved_r2 = r2(); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci CCP(0x20); 1108c2ecf20Sopenharmony_ci x = 8; if (pi->mode) x = 9; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci w0(2); P1; w0(8); P2; 1138c2ecf20Sopenharmony_ci w0(2); P1; w0(x); P2; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void on26_disconnect ( PIA *pi ) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci{ if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); } 1198c2ecf20Sopenharmony_ci else { w0(4); P1; w0(4); P1; } 1208c2ecf20Sopenharmony_ci CCP(0x30); 1218c2ecf20Sopenharmony_ci w0(pi->saved_r0); 1228c2ecf20Sopenharmony_ci w2(pi->saved_r2); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define RESET_WAIT 200 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int on26_test_port( PIA *pi) /* hard reset */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci{ int i, m, d, x=0, y=0; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci pi->saved_r0 = r0(); 1328c2ecf20Sopenharmony_ci pi->saved_r2 = r2(); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci d = pi->delay; 1358c2ecf20Sopenharmony_ci m = pi->mode; 1368c2ecf20Sopenharmony_ci pi->delay = 5; 1378c2ecf20Sopenharmony_ci pi->mode = 0; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci w2(0xc); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci CCP(0x30); CCP(0); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff); 1448c2ecf20Sopenharmony_ci i = ((r1() & 0xf0) << 4); w0(0x87); 1458c2ecf20Sopenharmony_ci i |= (r1() & 0xf0); w0(0x78); 1468c2ecf20Sopenharmony_ci w0(0x20);w2(4);w2(5); 1478c2ecf20Sopenharmony_ci i |= ((r1() & 0xf0) >> 4); 1488c2ecf20Sopenharmony_ci w2(4);w0(0xff); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (i == 0xb5f) { 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci w0(2); P1; w0(0); P2; 1538c2ecf20Sopenharmony_ci w0(3); P1; w0(0); P2; 1548c2ecf20Sopenharmony_ci w0(2); P1; w0(8); P2; udelay(100); 1558c2ecf20Sopenharmony_ci w0(2); P1; w0(0xa); P2; udelay(100); 1568c2ecf20Sopenharmony_ci w0(2); P1; w0(8); P2; udelay(1000); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci on26_write_regr(pi,0,6,0xa0); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci for (i=0;i<RESET_WAIT;i++) { 1618c2ecf20Sopenharmony_ci on26_write_regr(pi,0,6,0xa0); 1628c2ecf20Sopenharmony_ci x = on26_read_regr(pi,0,7); 1638c2ecf20Sopenharmony_ci on26_write_regr(pi,0,6,0xb0); 1648c2ecf20Sopenharmony_ci y = on26_read_regr(pi,0,7); 1658c2ecf20Sopenharmony_ci if (!((x&0x80)||(y&0x80))) break; 1668c2ecf20Sopenharmony_ci mdelay(100); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (i == RESET_WAIT) 1708c2ecf20Sopenharmony_ci printk("on26: Device reset failed (%x,%x)\n",x,y); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci w0(4); P1; w0(4); P1; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci CCP(0x30); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci pi->delay = d; 1788c2ecf20Sopenharmony_ci pi->mode = m; 1798c2ecf20Sopenharmony_ci w0(pi->saved_r0); 1808c2ecf20Sopenharmony_ci w2(pi->saved_r2); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 5; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void on26_read_block( PIA *pi, char * buf, int count ) 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci{ int k, a, b; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci switch (pi->mode) { 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1; 1938c2ecf20Sopenharmony_ci udelay(10); 1948c2ecf20Sopenharmony_ci for (k=0;k<count;k++) { 1958c2ecf20Sopenharmony_ci w2(6); a = r1(); 1968c2ecf20Sopenharmony_ci w2(4); b = r1(); 1978c2ecf20Sopenharmony_ci buf[k] = j44(a,b); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci w0(2); P1; w0(8); P2; 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1; 2038c2ecf20Sopenharmony_ci udelay(10); 2048c2ecf20Sopenharmony_ci for (k=0;k<count/2;k++) { 2058c2ecf20Sopenharmony_ci w2(0x26); buf[2*k] = r0(); 2068c2ecf20Sopenharmony_ci w2(0x24); buf[2*k+1] = r0(); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci w0(2); P1; w0(9); P2; 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci case 2: w3(1); w3(1); w2(5); w4(1); w2(4); 2128c2ecf20Sopenharmony_ci w3(0); w3(0); w2(0x24); 2138c2ecf20Sopenharmony_ci udelay(10); 2148c2ecf20Sopenharmony_ci for (k=0;k<count;k++) buf[k] = r4(); 2158c2ecf20Sopenharmony_ci w2(4); 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci case 3: w3(1); w3(1); w2(5); w4(1); w2(4); 2198c2ecf20Sopenharmony_ci w3(0); w3(0); w2(0x24); 2208c2ecf20Sopenharmony_ci udelay(10); 2218c2ecf20Sopenharmony_ci for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w(); 2228c2ecf20Sopenharmony_ci w2(4); 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci case 4: w3(1); w3(1); w2(5); w4(1); w2(4); 2268c2ecf20Sopenharmony_ci w3(0); w3(0); w2(0x24); 2278c2ecf20Sopenharmony_ci udelay(10); 2288c2ecf20Sopenharmony_ci for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l(); 2298c2ecf20Sopenharmony_ci w2(4); 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void on26_write_block( PIA *pi, char * buf, int count ) 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci{ int k; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci switch (pi->mode) { 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci case 0: 2428c2ecf20Sopenharmony_ci case 1: w0(1); P1; w0(1); P2; 2438c2ecf20Sopenharmony_ci w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1; 2448c2ecf20Sopenharmony_ci udelay(10); 2458c2ecf20Sopenharmony_ci for (k=0;k<count/2;k++) { 2468c2ecf20Sopenharmony_ci w2(5); w0(buf[2*k]); 2478c2ecf20Sopenharmony_ci w2(7); w0(buf[2*k+1]); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci w2(5); w2(4); 2508c2ecf20Sopenharmony_ci w0(2); P1; w0(8+pi->mode); P2; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci case 2: w3(1); w3(1); w2(5); w4(1); w2(4); 2548c2ecf20Sopenharmony_ci w3(0); w3(0); w2(0xc5); 2558c2ecf20Sopenharmony_ci udelay(10); 2568c2ecf20Sopenharmony_ci for (k=0;k<count;k++) w4(buf[k]); 2578c2ecf20Sopenharmony_ci w2(0xc4); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci case 3: w3(1); w3(1); w2(5); w4(1); w2(4); 2618c2ecf20Sopenharmony_ci w3(0); w3(0); w2(0xc5); 2628c2ecf20Sopenharmony_ci udelay(10); 2638c2ecf20Sopenharmony_ci for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]); 2648c2ecf20Sopenharmony_ci w2(0xc4); 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci case 4: w3(1); w3(1); w2(5); w4(1); w2(4); 2688c2ecf20Sopenharmony_ci w3(0); w3(0); w2(0xc5); 2698c2ecf20Sopenharmony_ci udelay(10); 2708c2ecf20Sopenharmony_ci for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]); 2718c2ecf20Sopenharmony_ci w2(0xc4); 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic void on26_log_adapter( PIA *pi, char * scratch, int verbose ) 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci{ char *mode_string[5] = {"4-bit","8-bit","EPP-8", 2818c2ecf20Sopenharmony_ci "EPP-16","EPP-32"}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ", 2848c2ecf20Sopenharmony_ci pi->device,ON26_VERSION,pi->port); 2858c2ecf20Sopenharmony_ci printk("mode %d (%s), delay %d\n",pi->mode, 2868c2ecf20Sopenharmony_ci mode_string[pi->mode],pi->delay); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic struct pi_protocol on26 = { 2918c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2928c2ecf20Sopenharmony_ci .name = "on26", 2938c2ecf20Sopenharmony_ci .max_mode = 5, 2948c2ecf20Sopenharmony_ci .epp_first = 2, 2958c2ecf20Sopenharmony_ci .default_delay = 1, 2968c2ecf20Sopenharmony_ci .max_units = 1, 2978c2ecf20Sopenharmony_ci .write_regr = on26_write_regr, 2988c2ecf20Sopenharmony_ci .read_regr = on26_read_regr, 2998c2ecf20Sopenharmony_ci .write_block = on26_write_block, 3008c2ecf20Sopenharmony_ci .read_block = on26_read_block, 3018c2ecf20Sopenharmony_ci .connect = on26_connect, 3028c2ecf20Sopenharmony_ci .disconnect = on26_disconnect, 3038c2ecf20Sopenharmony_ci .test_port = on26_test_port, 3048c2ecf20Sopenharmony_ci .log_adapter = on26_log_adapter, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int __init on26_init(void) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci return paride_register(&on26); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic void __exit on26_exit(void) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci paride_unregister(&on26); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3188c2ecf20Sopenharmony_cimodule_init(on26_init) 3198c2ecf20Sopenharmony_cimodule_exit(on26_exit) 320