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