1157 lines · 26.5 KB · C
RawDownload
/* ppa.c   --  low level driver for the IOMEGA PPA3  * parallel port SCSI host adapter. *  * (The PPA3 is the embedded controller in the ZIP drive.) *  * (c) 1995,1996 Grant R. Guenther, grant@torque.net, * under the terms of the GNU General Public License. *  */ #include <linux/init.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/blkdev.h>#include <linux/parport.h>#include <linux/workqueue.h>#include <linux/delay.h>#include <linux/jiffies.h>#include <asm/io.h> #include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>  static void ppa_reset_pulse(unsigned int base); typedef struct {	struct pardevice *dev;	/* Parport device entry         */	int base;		/* Actual port address          */	int mode;		/* Transfer mode                */	struct scsi_cmnd *cur_cmd;	/* Current queued command       */	struct delayed_work ppa_tq;	/* Polling interrupt stuff       */	unsigned long jstart;	/* Jiffies at start             */	unsigned long recon_tmo;	/* How many usecs to wait for reconnection (6th bit) */	unsigned int failed:1;	/* Failure flag                 */	unsigned wanted:1;	/* Parport sharing busy flag    */	unsigned int dev_no;	/* Device number		*/	wait_queue_head_t *waiting;	struct Scsi_Host *host;	struct list_head list;} ppa_struct; #include  "ppa.h" static unsigned int mode = PPA_AUTODETECT;module_param(mode, uint, 0644);MODULE_PARM_DESC(mode, "Transfer mode (0 = Autodetect, 1 = SPP 4-bit, "	"2 = SPP 8-bit, 3 = EPP 8-bit, 4 = EPP 16-bit, 5 = EPP 32-bit"); static struct scsi_pointer *ppa_scsi_pointer(struct scsi_cmnd *cmd){	return scsi_cmd_priv(cmd);} static inline ppa_struct *ppa_dev(struct Scsi_Host *host){	return *(ppa_struct **)&host->hostdata;} static DEFINE_SPINLOCK(arbitration_lock); static void got_it(ppa_struct *dev){	dev->base = dev->dev->port->base;	if (dev->cur_cmd)		ppa_scsi_pointer(dev->cur_cmd)->phase = 1;	else		wake_up(dev->waiting);} static void ppa_wakeup(void *ref){	ppa_struct *dev = (ppa_struct *) ref;	unsigned long flags; 	spin_lock_irqsave(&arbitration_lock, flags);	if (dev->wanted) {		parport_claim(dev->dev);		got_it(dev);		dev->wanted = 0;	}	spin_unlock_irqrestore(&arbitration_lock, flags);	return;} static int ppa_pb_claim(ppa_struct *dev){	unsigned long flags;	int res = 1;	spin_lock_irqsave(&arbitration_lock, flags);	if (parport_claim(dev->dev) == 0) {		got_it(dev);		res = 0;	}	dev->wanted = res;	spin_unlock_irqrestore(&arbitration_lock, flags);	return res;} static void ppa_pb_dismiss(ppa_struct *dev){	unsigned long flags;	int wanted;	spin_lock_irqsave(&arbitration_lock, flags);	wanted = dev->wanted;	dev->wanted = 0;	spin_unlock_irqrestore(&arbitration_lock, flags);	if (!wanted)		parport_release(dev->dev);} static inline void ppa_pb_release(ppa_struct *dev){	parport_release(dev->dev);} /* * Start of Chipset kludges */ /* This is to give the ppa driver a way to modify the timings (and other * parameters) by writing to the /proc/scsi/ppa/0 file. * Very simple method really... (To simple, no error checking :( ) * Reason: Kernel hackers HATE having to unload and reload modules for * testing... * Also gives a method to use a script to obtain optimum timings (TODO) */ static inline int ppa_write_info(struct Scsi_Host *host, char *buffer, int length){	ppa_struct *dev = ppa_dev(host);	unsigned long x; 	if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {		x = simple_strtoul(buffer + 5, NULL, 0);		dev->mode = x;		return length;	}	if ((length > 10) && (strncmp(buffer, "recon_tmo=", 10) == 0)) {		x = simple_strtoul(buffer + 10, NULL, 0);		dev->recon_tmo = x;		printk(KERN_INFO "ppa: recon_tmo set to %ld\n", x);		return length;	}	printk(KERN_WARNING "ppa /proc: invalid variable\n");	return -EINVAL;} static int ppa_show_info(struct seq_file *m, struct Scsi_Host *host){	ppa_struct *dev = ppa_dev(host); 	seq_printf(m, "Version : %s\n", PPA_VERSION);	seq_printf(m, "Parport : %s\n", dev->dev->port->name);	seq_printf(m, "Mode    : %s\n", PPA_MODE_STRING[dev->mode]);#if PPA_DEBUG > 0	seq_printf(m, "recon_tmo : %lu\n", dev->recon_tmo);#endif	return 0;} static int device_check(ppa_struct *dev, bool autodetect); #if PPA_DEBUG > 0#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\	   y, __func__, __LINE__); ppa_fail_func(x,y);static inline void ppa_fail_func(ppa_struct *dev, int error_code)#elsestatic inline void ppa_fail(ppa_struct *dev, int error_code)#endif{	/* If we fail a device then we trash status / message bytes */	if (dev->cur_cmd) {		dev->cur_cmd->result = error_code << 16;		dev->failed = 1;	}} /* * Wait for the high bit to be set. *  * In principle, this could be tied to an interrupt, but the adapter * doesn't appear to be designed to support interrupts.  We spin on * the 0x80 ready bit.  */static unsigned char ppa_wait(ppa_struct *dev){	int k;	unsigned short ppb = dev->base;	unsigned char r; 	k = PPA_SPIN_TMO;	/* Wait for bit 6 and 7 - PJC */	for (r = r_str(ppb); ((r & 0xc0) != 0xc0) && (k); k--) {		udelay(1);		r = r_str(ppb);	} 	/*	 * return some status information.	 * Semantics: 0xc0 = ZIP wants more data	 *            0xd0 = ZIP wants to send more data	 *            0xe0 = ZIP is expecting SCSI command data	 *            0xf0 = end of transfer, ZIP is sending status	 */	if (k)		return (r & 0xf0); 	/* Counter expired - Time out occurred */	ppa_fail(dev, DID_TIME_OUT);	printk(KERN_WARNING "ppa timeout in ppa_wait\n");	return 0;		/* command timed out */} /* * Clear EPP Timeout Bit  */static inline void epp_reset(unsigned short ppb){	int i; 	i = r_str(ppb);	w_str(ppb, i);	w_str(ppb, i & 0xfe);} /*  * Wait for empty ECP fifo (if we are in ECP fifo mode only) */static inline void ecp_sync(ppa_struct *dev){	int i, ppb_hi = dev->dev->port->base_hi; 	if (ppb_hi == 0)		return; 	if ((r_ecr(ppb_hi) & 0xe0) == 0x60) {	/* mode 011 == ECP fifo mode */		for (i = 0; i < 100; i++) {			if (r_ecr(ppb_hi) & 0x01)				return;			udelay(5);		}		printk(KERN_WARNING "ppa: ECP sync failed as data still present in FIFO.\n");	}} static int ppa_byte_out(unsigned short base, const char *buffer, int len){	int i; 	for (i = len; i; i--) {		w_dtr(base, *buffer++);		w_ctr(base, 0xe);		w_ctr(base, 0xc);	}	return 1;		/* All went well - we hope! */} static int ppa_byte_in(unsigned short base, char *buffer, int len){	int i; 	for (i = len; i; i--) {		*buffer++ = r_dtr(base);		w_ctr(base, 0x27);		w_ctr(base, 0x25);	}	return 1;		/* All went well - we hope! */} static int ppa_nibble_in(unsigned short base, char *buffer, int len){	for (; len; len--) {		unsigned char h; 		w_ctr(base, 0x4);		h = r_str(base) & 0xf0;		w_ctr(base, 0x6);		*buffer++ = h | ((r_str(base) & 0xf0) >> 4);	}	return 1;		/* All went well - we hope! */} static int ppa_out(ppa_struct *dev, char *buffer, int len){	int r;	unsigned short ppb = dev->base; 	r = ppa_wait(dev); 	if ((r & 0x50) != 0x40) {		ppa_fail(dev, DID_ERROR);		return 0;	}	switch (dev->mode) {	case PPA_NIBBLE:	case PPA_PS2:		/* 8 bit output, with a loop */		r = ppa_byte_out(ppb, buffer, len);		break; 	case PPA_EPP_32:	case PPA_EPP_16:	case PPA_EPP_8:		epp_reset(ppb);		w_ctr(ppb, 0x4);		if (dev->mode == PPA_EPP_32 && !(((long) buffer | len) & 0x03))			outsl(ppb + 4, buffer, len >> 2);		else if (dev->mode == PPA_EPP_16 && !(((long) buffer | len) & 0x01))			outsw(ppb + 4, buffer, len >> 1);		else			outsb(ppb + 4, buffer, len);		w_ctr(ppb, 0xc);		r = !(r_str(ppb) & 0x01);		w_ctr(ppb, 0xc);		ecp_sync(dev);		break; 	default:		printk(KERN_ERR "PPA: bug in ppa_out()\n");		r = 0;	}	return r;} static int ppa_in(ppa_struct *dev, char *buffer, int len){	int r;	unsigned short ppb = dev->base; 	r = ppa_wait(dev); 	if ((r & 0x50) != 0x50) {		ppa_fail(dev, DID_ERROR);		return 0;	}	switch (dev->mode) {	case PPA_NIBBLE:		/* 4 bit input, with a loop */		r = ppa_nibble_in(ppb, buffer, len);		w_ctr(ppb, 0xc);		break; 	case PPA_PS2:		/* 8 bit input, with a loop */		w_ctr(ppb, 0x25);		r = ppa_byte_in(ppb, buffer, len);		w_ctr(ppb, 0x4);		w_ctr(ppb, 0xc);		break; 	case PPA_EPP_32:	case PPA_EPP_16:	case PPA_EPP_8:		epp_reset(ppb);		w_ctr(ppb, 0x24);		if (dev->mode == PPA_EPP_32 && !(((long) buffer | len) & 0x03))			insl(ppb + 4, buffer, len >> 2);		else if (dev->mode == PPA_EPP_16 && !(((long) buffer | len) & 0x01))			insw(ppb + 4, buffer, len >> 1);		else			insb(ppb + 4, buffer, len);		w_ctr(ppb, 0x2c);		r = !(r_str(ppb) & 0x01);		w_ctr(ppb, 0x2c);		ecp_sync(dev);		break; 	default:		printk(KERN_ERR "PPA: bug in ppa_ins()\n");		r = 0;		break;	}	return r;} /* end of ppa_io.h */static inline void ppa_d_pulse(unsigned short ppb, unsigned char b){	w_dtr(ppb, b);	w_ctr(ppb, 0xc);	w_ctr(ppb, 0xe);	w_ctr(ppb, 0xc);	w_ctr(ppb, 0x4);	w_ctr(ppb, 0xc);} static void ppa_disconnect(ppa_struct *dev){	unsigned short ppb = dev->base; 	ppa_d_pulse(ppb, 0);	ppa_d_pulse(ppb, 0x3c);	ppa_d_pulse(ppb, 0x20);	ppa_d_pulse(ppb, 0xf);} static inline void ppa_c_pulse(unsigned short ppb, unsigned char b){	w_dtr(ppb, b);	w_ctr(ppb, 0x4);	w_ctr(ppb, 0x6);	w_ctr(ppb, 0x4);	w_ctr(ppb, 0xc);} static inline void ppa_connect(ppa_struct *dev, int flag){	unsigned short ppb = dev->base; 	ppa_c_pulse(ppb, 0);	ppa_c_pulse(ppb, 0x3c);	ppa_c_pulse(ppb, 0x20);	if ((flag == CONNECT_EPP_MAYBE) && IN_EPP_MODE(dev->mode))		ppa_c_pulse(ppb, 0xcf);	else		ppa_c_pulse(ppb, 0x8f);} static int ppa_select(ppa_struct *dev, int target){	int k;	unsigned short ppb = dev->base; 	/*	 * Bit 6 (0x40) is the device selected bit.	 * First we must wait till the current device goes off line...	 */	k = PPA_SELECT_TMO;	do {		k--;		udelay(1);	} while ((r_str(ppb) & 0x40) && (k));	if (!k)		return 0; 	w_dtr(ppb, (1 << target));	w_ctr(ppb, 0xe);	w_ctr(ppb, 0xc);	w_dtr(ppb, 0x80);	/* This is NOT the initator */	w_ctr(ppb, 0x8); 	k = PPA_SELECT_TMO;	do {		k--;		udelay(1);	}	while (!(r_str(ppb) & 0x40) && (k));	if (!k)		return 0; 	return 1;} /*  * This is based on a trace of what the Iomega DOS 'guest' driver does. * I've tried several different kinds of parallel ports with guest and * coded this to react in the same ways that it does. *  * The return value from this function is just a hint about where the * handshaking failed. *  */static int ppa_init(ppa_struct *dev){	int retv;	unsigned short ppb = dev->base;	bool autodetect = dev->mode == PPA_AUTODETECT; 	if (autodetect) {		int modes = dev->dev->port->modes;		int ppb_hi = dev->dev->port->base_hi; 		/* Mode detection works up the chain of speed		 * This avoids a nasty if-then-else-if-... tree		 */		dev->mode = PPA_NIBBLE; 		if (modes & PARPORT_MODE_TRISTATE)			dev->mode = PPA_PS2; 		if (modes & PARPORT_MODE_ECP) {			w_ecr(ppb_hi, 0x20);			dev->mode = PPA_PS2;		}		if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP))			w_ecr(ppb_hi, 0x80);	} 	ppa_disconnect(dev);	ppa_connect(dev, CONNECT_NORMAL); 	retv = 2;		/* Failed */ 	w_ctr(ppb, 0xe);	if ((r_str(ppb) & 0x08) == 0x08)		retv--;

Lines 1–500 of 1157.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t