162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* imm.c -- low level driver for the IOMEGA MatchMaker 362306a36Sopenharmony_ci * parallel port SCSI host adapter. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (The IMM is the embedded controller in the ZIP Plus drive.) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * My unofficial company acronym list is 21 pages long: 862306a36Sopenharmony_ci * FLA: Four letter acronym with built in facility for 962306a36Sopenharmony_ci * future expansion to five letters. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/blkdev.h> 1662306a36Sopenharmony_ci#include <linux/parport.h> 1762306a36Sopenharmony_ci#include <linux/workqueue.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <asm/io.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <scsi/scsi.h> 2362306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 2462306a36Sopenharmony_ci#include <scsi/scsi_device.h> 2562306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* The following #define is to avoid a clash with hosts.c */ 2862306a36Sopenharmony_ci#define IMM_PROBE_SPP 0x0001 2962306a36Sopenharmony_ci#define IMM_PROBE_PS2 0x0002 3062306a36Sopenharmony_ci#define IMM_PROBE_ECR 0x0010 3162306a36Sopenharmony_ci#define IMM_PROBE_EPP17 0x0100 3262306a36Sopenharmony_ci#define IMM_PROBE_EPP19 0x0200 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_citypedef struct { 3662306a36Sopenharmony_ci struct pardevice *dev; /* Parport device entry */ 3762306a36Sopenharmony_ci int base; /* Actual port address */ 3862306a36Sopenharmony_ci int base_hi; /* Hi Base address for ECP-ISA chipset */ 3962306a36Sopenharmony_ci int mode; /* Transfer mode */ 4062306a36Sopenharmony_ci struct scsi_cmnd *cur_cmd; /* Current queued command */ 4162306a36Sopenharmony_ci struct delayed_work imm_tq; /* Polling interrupt stuff */ 4262306a36Sopenharmony_ci unsigned long jstart; /* Jiffies at start */ 4362306a36Sopenharmony_ci unsigned failed:1; /* Failure flag */ 4462306a36Sopenharmony_ci unsigned dp:1; /* Data phase present */ 4562306a36Sopenharmony_ci unsigned rd:1; /* Read data in data phase */ 4662306a36Sopenharmony_ci unsigned wanted:1; /* Parport sharing busy flag */ 4762306a36Sopenharmony_ci unsigned int dev_no; /* Device number */ 4862306a36Sopenharmony_ci wait_queue_head_t *waiting; 4962306a36Sopenharmony_ci struct Scsi_Host *host; 5062306a36Sopenharmony_ci struct list_head list; 5162306a36Sopenharmony_ci} imm_struct; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void imm_reset_pulse(unsigned int base); 5462306a36Sopenharmony_cistatic int device_check(imm_struct *dev); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#include "imm.h" 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic inline imm_struct *imm_dev(struct Scsi_Host *host) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return *(imm_struct **)&host->hostdata; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(arbitration_lock); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void got_it(imm_struct *dev) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci dev->base = dev->dev->port->base; 6862306a36Sopenharmony_ci if (dev->cur_cmd) 6962306a36Sopenharmony_ci imm_scsi_pointer(dev->cur_cmd)->phase = 1; 7062306a36Sopenharmony_ci else 7162306a36Sopenharmony_ci wake_up(dev->waiting); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void imm_wakeup(void *ref) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci imm_struct *dev = (imm_struct *) ref; 7762306a36Sopenharmony_ci unsigned long flags; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci spin_lock_irqsave(&arbitration_lock, flags); 8062306a36Sopenharmony_ci if (dev->wanted) { 8162306a36Sopenharmony_ci if (parport_claim(dev->dev) == 0) { 8262306a36Sopenharmony_ci got_it(dev); 8362306a36Sopenharmony_ci dev->wanted = 0; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci spin_unlock_irqrestore(&arbitration_lock, flags); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int imm_pb_claim(imm_struct *dev) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci unsigned long flags; 9262306a36Sopenharmony_ci int res = 1; 9362306a36Sopenharmony_ci spin_lock_irqsave(&arbitration_lock, flags); 9462306a36Sopenharmony_ci if (parport_claim(dev->dev) == 0) { 9562306a36Sopenharmony_ci got_it(dev); 9662306a36Sopenharmony_ci res = 0; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci dev->wanted = res; 9962306a36Sopenharmony_ci spin_unlock_irqrestore(&arbitration_lock, flags); 10062306a36Sopenharmony_ci return res; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void imm_pb_dismiss(imm_struct *dev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci unsigned long flags; 10662306a36Sopenharmony_ci int wanted; 10762306a36Sopenharmony_ci spin_lock_irqsave(&arbitration_lock, flags); 10862306a36Sopenharmony_ci wanted = dev->wanted; 10962306a36Sopenharmony_ci dev->wanted = 0; 11062306a36Sopenharmony_ci spin_unlock_irqrestore(&arbitration_lock, flags); 11162306a36Sopenharmony_ci if (!wanted) 11262306a36Sopenharmony_ci parport_release(dev->dev); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic inline void imm_pb_release(imm_struct *dev) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci parport_release(dev->dev); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* This is to give the imm driver a way to modify the timings (and other 12162306a36Sopenharmony_ci * parameters) by writing to the /proc/scsi/imm/0 file. 12262306a36Sopenharmony_ci * Very simple method really... (Too simple, no error checking :( ) 12362306a36Sopenharmony_ci * Reason: Kernel hackers HATE having to unload and reload modules for 12462306a36Sopenharmony_ci * testing... 12562306a36Sopenharmony_ci * Also gives a method to use a script to obtain optimum timings (TODO) 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic int imm_write_info(struct Scsi_Host *host, char *buffer, int length) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci imm_struct *dev = imm_dev(host); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) { 13262306a36Sopenharmony_ci dev->mode = simple_strtoul(buffer + 5, NULL, 0); 13362306a36Sopenharmony_ci return length; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci printk("imm /proc: invalid variable\n"); 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int imm_show_info(struct seq_file *m, struct Scsi_Host *host) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci imm_struct *dev = imm_dev(host); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci seq_printf(m, "Version : %s\n", IMM_VERSION); 14462306a36Sopenharmony_ci seq_printf(m, "Parport : %s\n", dev->dev->port->name); 14562306a36Sopenharmony_ci seq_printf(m, "Mode : %s\n", IMM_MODE_STRING[dev->mode]); 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#if IMM_DEBUG > 0 15062306a36Sopenharmony_ci#define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\ 15162306a36Sopenharmony_ci y, __func__, __LINE__); imm_fail_func(x,y); 15262306a36Sopenharmony_cistatic inline void 15362306a36Sopenharmony_ciimm_fail_func(imm_struct *dev, int error_code) 15462306a36Sopenharmony_ci#else 15562306a36Sopenharmony_cistatic inline void 15662306a36Sopenharmony_ciimm_fail(imm_struct *dev, int error_code) 15762306a36Sopenharmony_ci#endif 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci /* If we fail a device then we trash status / message bytes */ 16062306a36Sopenharmony_ci if (dev->cur_cmd) { 16162306a36Sopenharmony_ci dev->cur_cmd->result = error_code << 16; 16262306a36Sopenharmony_ci dev->failed = 1; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* 16762306a36Sopenharmony_ci * Wait for the high bit to be set. 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * In principle, this could be tied to an interrupt, but the adapter 17062306a36Sopenharmony_ci * doesn't appear to be designed to support interrupts. We spin on 17162306a36Sopenharmony_ci * the 0x80 ready bit. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic unsigned char imm_wait(imm_struct *dev) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int k; 17662306a36Sopenharmony_ci unsigned short ppb = dev->base; 17762306a36Sopenharmony_ci unsigned char r; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci k = IMM_SPIN_TMO; 18262306a36Sopenharmony_ci do { 18362306a36Sopenharmony_ci r = r_str(ppb); 18462306a36Sopenharmony_ci k--; 18562306a36Sopenharmony_ci udelay(1); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci while (!(r & 0x80) && (k)); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * STR register (LPT base+1) to SCSI mapping: 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * STR imm imm 19362306a36Sopenharmony_ci * =================================== 19462306a36Sopenharmony_ci * 0x80 S_REQ S_REQ 19562306a36Sopenharmony_ci * 0x40 !S_BSY (????) 19662306a36Sopenharmony_ci * 0x20 !S_CD !S_CD 19762306a36Sopenharmony_ci * 0x10 !S_IO !S_IO 19862306a36Sopenharmony_ci * 0x08 (????) !S_BSY 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * imm imm meaning 20162306a36Sopenharmony_ci * ================================== 20262306a36Sopenharmony_ci * 0xf0 0xb8 Bit mask 20362306a36Sopenharmony_ci * 0xc0 0x88 ZIP wants more data 20462306a36Sopenharmony_ci * 0xd0 0x98 ZIP wants to send more data 20562306a36Sopenharmony_ci * 0xe0 0xa8 ZIP is expecting SCSI command data 20662306a36Sopenharmony_ci * 0xf0 0xb8 end of transfer, ZIP is sending status 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci w_ctr(ppb, 0x04); 20962306a36Sopenharmony_ci if (k) 21062306a36Sopenharmony_ci return (r & 0xb8); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Counter expired - Time out occurred */ 21362306a36Sopenharmony_ci imm_fail(dev, DID_TIME_OUT); 21462306a36Sopenharmony_ci printk("imm timeout in imm_wait\n"); 21562306a36Sopenharmony_ci return 0; /* command timed out */ 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int imm_negotiate(imm_struct * tmp) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * The following is supposedly the IEEE 1284-1994 negotiate 22262306a36Sopenharmony_ci * sequence. I have yet to obtain a copy of the above standard 22362306a36Sopenharmony_ci * so this is a bit of a guess... 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * A fair chunk of this is based on the Linux parport implementation 22662306a36Sopenharmony_ci * of IEEE 1284. 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * Return 0 if data available 22962306a36Sopenharmony_ci * 1 if no data available 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci unsigned short base = tmp->base; 23362306a36Sopenharmony_ci unsigned char a, mode; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci switch (tmp->mode) { 23662306a36Sopenharmony_ci case IMM_NIBBLE: 23762306a36Sopenharmony_ci mode = 0x00; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci case IMM_PS2: 24062306a36Sopenharmony_ci mode = 0x01; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci default: 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci w_ctr(base, 0x04); 24762306a36Sopenharmony_ci udelay(5); 24862306a36Sopenharmony_ci w_dtr(base, mode); 24962306a36Sopenharmony_ci udelay(100); 25062306a36Sopenharmony_ci w_ctr(base, 0x06); 25162306a36Sopenharmony_ci udelay(5); 25262306a36Sopenharmony_ci a = (r_str(base) & 0x20) ? 0 : 1; 25362306a36Sopenharmony_ci udelay(5); 25462306a36Sopenharmony_ci w_ctr(base, 0x07); 25562306a36Sopenharmony_ci udelay(5); 25662306a36Sopenharmony_ci w_ctr(base, 0x06); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (a) { 25962306a36Sopenharmony_ci printk 26062306a36Sopenharmony_ci ("IMM: IEEE1284 negotiate indicates no data available.\n"); 26162306a36Sopenharmony_ci imm_fail(tmp, DID_ERROR); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci return a; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* 26762306a36Sopenharmony_ci * Clear EPP timeout bit. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_cistatic inline void epp_reset(unsigned short ppb) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci int i; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci i = r_str(ppb); 27462306a36Sopenharmony_ci w_str(ppb, i); 27562306a36Sopenharmony_ci w_str(ppb, i & 0xfe); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* 27962306a36Sopenharmony_ci * Wait for empty ECP fifo (if we are in ECP fifo mode only) 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistatic inline void ecp_sync(imm_struct *dev) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci int i, ppb_hi = dev->base_hi; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (ppb_hi == 0) 28662306a36Sopenharmony_ci return; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if ((r_ecr(ppb_hi) & 0xe0) == 0x60) { /* mode 011 == ECP fifo mode */ 28962306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 29062306a36Sopenharmony_ci if (r_ecr(ppb_hi) & 0x01) 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci udelay(5); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci printk("imm: ECP sync failed as data still present in FIFO.\n"); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int imm_byte_out(unsigned short base, const char *buffer, int len) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci int i; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci w_ctr(base, 0x4); /* apparently a sane mode */ 30362306a36Sopenharmony_ci for (i = len >> 1; i; i--) { 30462306a36Sopenharmony_ci w_dtr(base, *buffer++); 30562306a36Sopenharmony_ci w_ctr(base, 0x5); /* Drop STROBE low */ 30662306a36Sopenharmony_ci w_dtr(base, *buffer++); 30762306a36Sopenharmony_ci w_ctr(base, 0x0); /* STROBE high + INIT low */ 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci w_ctr(base, 0x4); /* apparently a sane mode */ 31062306a36Sopenharmony_ci return 1; /* All went well - we hope! */ 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int imm_nibble_in(unsigned short base, char *buffer, int len) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci unsigned char l; 31662306a36Sopenharmony_ci int i; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* 31962306a36Sopenharmony_ci * The following is based on documented timing signals 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci w_ctr(base, 0x4); 32262306a36Sopenharmony_ci for (i = len; i; i--) { 32362306a36Sopenharmony_ci w_ctr(base, 0x6); 32462306a36Sopenharmony_ci l = (r_str(base) & 0xf0) >> 4; 32562306a36Sopenharmony_ci w_ctr(base, 0x5); 32662306a36Sopenharmony_ci *buffer++ = (r_str(base) & 0xf0) | l; 32762306a36Sopenharmony_ci w_ctr(base, 0x4); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci return 1; /* All went well - we hope! */ 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int imm_byte_in(unsigned short base, char *buffer, int len) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int i; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * The following is based on documented timing signals 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci w_ctr(base, 0x4); 34062306a36Sopenharmony_ci for (i = len; i; i--) { 34162306a36Sopenharmony_ci w_ctr(base, 0x26); 34262306a36Sopenharmony_ci *buffer++ = r_dtr(base); 34362306a36Sopenharmony_ci w_ctr(base, 0x25); 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci return 1; /* All went well - we hope! */ 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic int imm_out(imm_struct *dev, char *buffer, int len) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci unsigned short ppb = dev->base; 35162306a36Sopenharmony_ci int r = imm_wait(dev); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * Make sure that: 35562306a36Sopenharmony_ci * a) the SCSI bus is BUSY (device still listening) 35662306a36Sopenharmony_ci * b) the device is listening 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci if ((r & 0x18) != 0x08) { 35962306a36Sopenharmony_ci imm_fail(dev, DID_ERROR); 36062306a36Sopenharmony_ci printk("IMM: returned SCSI status %2x\n", r); 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci switch (dev->mode) { 36462306a36Sopenharmony_ci case IMM_EPP_32: 36562306a36Sopenharmony_ci case IMM_EPP_16: 36662306a36Sopenharmony_ci case IMM_EPP_8: 36762306a36Sopenharmony_ci epp_reset(ppb); 36862306a36Sopenharmony_ci w_ctr(ppb, 0x4); 36962306a36Sopenharmony_ci#ifdef CONFIG_SCSI_IZIP_EPP16 37062306a36Sopenharmony_ci if (!(((long) buffer | len) & 0x01)) 37162306a36Sopenharmony_ci outsw(ppb + 4, buffer, len >> 1); 37262306a36Sopenharmony_ci#else 37362306a36Sopenharmony_ci if (!(((long) buffer | len) & 0x03)) 37462306a36Sopenharmony_ci outsl(ppb + 4, buffer, len >> 2); 37562306a36Sopenharmony_ci#endif 37662306a36Sopenharmony_ci else 37762306a36Sopenharmony_ci outsb(ppb + 4, buffer, len); 37862306a36Sopenharmony_ci w_ctr(ppb, 0xc); 37962306a36Sopenharmony_ci r = !(r_str(ppb) & 0x01); 38062306a36Sopenharmony_ci w_ctr(ppb, 0xc); 38162306a36Sopenharmony_ci ecp_sync(dev); 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci case IMM_NIBBLE: 38562306a36Sopenharmony_ci case IMM_PS2: 38662306a36Sopenharmony_ci /* 8 bit output, with a loop */ 38762306a36Sopenharmony_ci r = imm_byte_out(ppb, buffer, len); 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci default: 39162306a36Sopenharmony_ci printk("IMM: bug in imm_out()\n"); 39262306a36Sopenharmony_ci r = 0; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci return r; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int imm_in(imm_struct *dev, char *buffer, int len) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci unsigned short ppb = dev->base; 40062306a36Sopenharmony_ci int r = imm_wait(dev); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * Make sure that: 40462306a36Sopenharmony_ci * a) the SCSI bus is BUSY (device still listening) 40562306a36Sopenharmony_ci * b) the device is sending data 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci if ((r & 0x18) != 0x18) { 40862306a36Sopenharmony_ci imm_fail(dev, DID_ERROR); 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci switch (dev->mode) { 41262306a36Sopenharmony_ci case IMM_NIBBLE: 41362306a36Sopenharmony_ci /* 4 bit input, with a loop */ 41462306a36Sopenharmony_ci r = imm_nibble_in(ppb, buffer, len); 41562306a36Sopenharmony_ci w_ctr(ppb, 0xc); 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci case IMM_PS2: 41962306a36Sopenharmony_ci /* 8 bit input, with a loop */ 42062306a36Sopenharmony_ci r = imm_byte_in(ppb, buffer, len); 42162306a36Sopenharmony_ci w_ctr(ppb, 0xc); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci case IMM_EPP_32: 42562306a36Sopenharmony_ci case IMM_EPP_16: 42662306a36Sopenharmony_ci case IMM_EPP_8: 42762306a36Sopenharmony_ci epp_reset(ppb); 42862306a36Sopenharmony_ci w_ctr(ppb, 0x24); 42962306a36Sopenharmony_ci#ifdef CONFIG_SCSI_IZIP_EPP16 43062306a36Sopenharmony_ci if (!(((long) buffer | len) & 0x01)) 43162306a36Sopenharmony_ci insw(ppb + 4, buffer, len >> 1); 43262306a36Sopenharmony_ci#else 43362306a36Sopenharmony_ci if (!(((long) buffer | len) & 0x03)) 43462306a36Sopenharmony_ci insl(ppb + 4, buffer, len >> 2); 43562306a36Sopenharmony_ci#endif 43662306a36Sopenharmony_ci else 43762306a36Sopenharmony_ci insb(ppb + 4, buffer, len); 43862306a36Sopenharmony_ci w_ctr(ppb, 0x2c); 43962306a36Sopenharmony_ci r = !(r_str(ppb) & 0x01); 44062306a36Sopenharmony_ci w_ctr(ppb, 0x2c); 44162306a36Sopenharmony_ci ecp_sync(dev); 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci default: 44562306a36Sopenharmony_ci printk("IMM: bug in imm_ins()\n"); 44662306a36Sopenharmony_ci r = 0; 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci return r; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int imm_cpp(unsigned short ppb, unsigned char b) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * Comments on udelay values refer to the 45662306a36Sopenharmony_ci * Command Packet Protocol (CPP) timing diagram. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci unsigned char s1, s2, s3; 46062306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 46162306a36Sopenharmony_ci udelay(2); /* 1 usec - infinite */ 46262306a36Sopenharmony_ci w_dtr(ppb, 0xaa); 46362306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 46462306a36Sopenharmony_ci w_dtr(ppb, 0x55); 46562306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 46662306a36Sopenharmony_ci w_dtr(ppb, 0x00); 46762306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 46862306a36Sopenharmony_ci w_dtr(ppb, 0xff); 46962306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 47062306a36Sopenharmony_ci s1 = r_str(ppb) & 0xb8; 47162306a36Sopenharmony_ci w_dtr(ppb, 0x87); 47262306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 47362306a36Sopenharmony_ci s2 = r_str(ppb) & 0xb8; 47462306a36Sopenharmony_ci w_dtr(ppb, 0x78); 47562306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 47662306a36Sopenharmony_ci s3 = r_str(ppb) & 0x38; 47762306a36Sopenharmony_ci /* 47862306a36Sopenharmony_ci * Values for b are: 47962306a36Sopenharmony_ci * 0000 00aa Assign address aa to current device 48062306a36Sopenharmony_ci * 0010 00aa Select device aa in EPP Winbond mode 48162306a36Sopenharmony_ci * 0010 10aa Select device aa in EPP mode 48262306a36Sopenharmony_ci * 0011 xxxx Deselect all devices 48362306a36Sopenharmony_ci * 0110 00aa Test device aa 48462306a36Sopenharmony_ci * 1101 00aa Select device aa in ECP mode 48562306a36Sopenharmony_ci * 1110 00aa Select device aa in Compatible mode 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci w_dtr(ppb, b); 48862306a36Sopenharmony_ci udelay(2); /* 1 usec - infinite */ 48962306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 49062306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 49162306a36Sopenharmony_ci w_ctr(ppb, 0x0d); 49262306a36Sopenharmony_ci udelay(2); /* 1 usec - infinite */ 49362306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 49462306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 49562306a36Sopenharmony_ci w_dtr(ppb, 0xff); 49662306a36Sopenharmony_ci udelay(10); /* 7 usec - infinite */ 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* 49962306a36Sopenharmony_ci * The following table is electrical pin values. 50062306a36Sopenharmony_ci * (BSY is inverted at the CTR register) 50162306a36Sopenharmony_ci * 50262306a36Sopenharmony_ci * BSY ACK POut SEL Fault 50362306a36Sopenharmony_ci * S1 0 X 1 1 1 50462306a36Sopenharmony_ci * S2 1 X 0 1 1 50562306a36Sopenharmony_ci * S3 L X 1 1 S 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * L => Last device in chain 50862306a36Sopenharmony_ci * S => Selected 50962306a36Sopenharmony_ci * 51062306a36Sopenharmony_ci * Observered values for S1,S2,S3 are: 51162306a36Sopenharmony_ci * Disconnect => f8/58/78 51262306a36Sopenharmony_ci * Connect => f8/58/70 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x30)) 51562306a36Sopenharmony_ci return 1; /* Connected */ 51662306a36Sopenharmony_ci if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x38)) 51762306a36Sopenharmony_ci return 0; /* Disconnected */ 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return -1; /* No device present */ 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic inline int imm_connect(imm_struct *dev, int flag) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci unsigned short ppb = dev->base; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci imm_cpp(ppb, 0xe0); /* Select device 0 in compatible mode */ 52762306a36Sopenharmony_ci imm_cpp(ppb, 0x30); /* Disconnect all devices */ 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if ((dev->mode == IMM_EPP_8) || 53062306a36Sopenharmony_ci (dev->mode == IMM_EPP_16) || 53162306a36Sopenharmony_ci (dev->mode == IMM_EPP_32)) 53262306a36Sopenharmony_ci return imm_cpp(ppb, 0x28); /* Select device 0 in EPP mode */ 53362306a36Sopenharmony_ci return imm_cpp(ppb, 0xe0); /* Select device 0 in compatible mode */ 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic void imm_disconnect(imm_struct *dev) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci imm_cpp(dev->base, 0x30); /* Disconnect all devices */ 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int imm_select(imm_struct *dev, int target) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci int k; 54462306a36Sopenharmony_ci unsigned short ppb = dev->base; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* 54762306a36Sopenharmony_ci * Firstly we want to make sure there is nothing 54862306a36Sopenharmony_ci * holding onto the SCSI bus. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci w_ctr(ppb, 0xc); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci k = IMM_SELECT_TMO; 55362306a36Sopenharmony_ci do { 55462306a36Sopenharmony_ci k--; 55562306a36Sopenharmony_ci } while ((r_str(ppb) & 0x08) && (k)); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (!k) 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * Now assert the SCSI ID (HOST and TARGET) on the data bus 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci w_ctr(ppb, 0x4); 56462306a36Sopenharmony_ci w_dtr(ppb, 0x80 | (1 << target)); 56562306a36Sopenharmony_ci udelay(1); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* 56862306a36Sopenharmony_ci * Deassert SELIN first followed by STROBE 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci w_ctr(ppb, 0xc); 57162306a36Sopenharmony_ci w_ctr(ppb, 0xd); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* 57462306a36Sopenharmony_ci * ACK should drop low while SELIN is deasserted. 57562306a36Sopenharmony_ci * FAULT should drop low when the SCSI device latches the bus. 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci k = IMM_SELECT_TMO; 57862306a36Sopenharmony_ci do { 57962306a36Sopenharmony_ci k--; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci while (!(r_str(ppb) & 0x08) && (k)); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* 58462306a36Sopenharmony_ci * Place the interface back into a sane state (status mode) 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ci w_ctr(ppb, 0xc); 58762306a36Sopenharmony_ci return (k) ? 1 : 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int imm_init(imm_struct *dev) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci if (imm_connect(dev, 0) != 1) 59362306a36Sopenharmony_ci return -EIO; 59462306a36Sopenharmony_ci imm_reset_pulse(dev->base); 59562306a36Sopenharmony_ci mdelay(1); /* Delay to allow devices to settle */ 59662306a36Sopenharmony_ci imm_disconnect(dev); 59762306a36Sopenharmony_ci mdelay(1); /* Another delay to allow devices to settle */ 59862306a36Sopenharmony_ci return device_check(dev); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic inline int imm_send_command(struct scsi_cmnd *cmd) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci imm_struct *dev = imm_dev(cmd->device->host); 60462306a36Sopenharmony_ci int k; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* NOTE: IMM uses byte pairs */ 60762306a36Sopenharmony_ci for (k = 0; k < cmd->cmd_len; k += 2) 60862306a36Sopenharmony_ci if (!imm_out(dev, &cmd->cmnd[k], 2)) 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci return 1; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/* 61462306a36Sopenharmony_ci * The bulk flag enables some optimisations in the data transfer loops, 61562306a36Sopenharmony_ci * it should be true for any command that transfers data in integral 61662306a36Sopenharmony_ci * numbers of sectors. 61762306a36Sopenharmony_ci * 61862306a36Sopenharmony_ci * The driver appears to remain stable if we speed up the parallel port 61962306a36Sopenharmony_ci * i/o in this function, but not elsewhere. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cistatic int imm_completion(struct scsi_cmnd *const cmd) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci /* Return codes: 62462306a36Sopenharmony_ci * -1 Error 62562306a36Sopenharmony_ci * 0 Told to schedule 62662306a36Sopenharmony_ci * 1 Finished data transfer 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci struct scsi_pointer *scsi_pointer = imm_scsi_pointer(cmd); 62962306a36Sopenharmony_ci imm_struct *dev = imm_dev(cmd->device->host); 63062306a36Sopenharmony_ci unsigned short ppb = dev->base; 63162306a36Sopenharmony_ci unsigned long start_jiffies = jiffies; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci unsigned char r, v; 63462306a36Sopenharmony_ci int fast, bulk, status; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci v = cmd->cmnd[0]; 63762306a36Sopenharmony_ci bulk = ((v == READ_6) || 63862306a36Sopenharmony_ci (v == READ_10) || (v == WRITE_6) || (v == WRITE_10)); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * We only get here if the drive is ready to comunicate, 64262306a36Sopenharmony_ci * hence no need for a full imm_wait. 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 64562306a36Sopenharmony_ci r = (r_str(ppb) & 0xb8); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * while (device is not ready to send status byte) 64962306a36Sopenharmony_ci * loop; 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci while (r != (unsigned char) 0xb8) { 65262306a36Sopenharmony_ci /* 65362306a36Sopenharmony_ci * If we have been running for more than a full timer tick 65462306a36Sopenharmony_ci * then take a rest. 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_ci if (time_after(jiffies, start_jiffies + 1)) 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * FAIL if: 66162306a36Sopenharmony_ci * a) Drive status is screwy (!ready && !present) 66262306a36Sopenharmony_ci * b) Drive is requesting/sending more data than expected 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci if ((r & 0x88) != 0x88 || scsi_pointer->this_residual <= 0) { 66562306a36Sopenharmony_ci imm_fail(dev, DID_ERROR); 66662306a36Sopenharmony_ci return -1; /* ERROR_RETURN */ 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci /* determine if we should use burst I/O */ 66962306a36Sopenharmony_ci if (dev->rd == 0) { 67062306a36Sopenharmony_ci fast = bulk && scsi_pointer->this_residual >= 67162306a36Sopenharmony_ci IMM_BURST_SIZE ? IMM_BURST_SIZE : 2; 67262306a36Sopenharmony_ci status = imm_out(dev, scsi_pointer->ptr, fast); 67362306a36Sopenharmony_ci } else { 67462306a36Sopenharmony_ci fast = bulk && scsi_pointer->this_residual >= 67562306a36Sopenharmony_ci IMM_BURST_SIZE ? IMM_BURST_SIZE : 1; 67662306a36Sopenharmony_ci status = imm_in(dev, scsi_pointer->ptr, fast); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci scsi_pointer->ptr += fast; 68062306a36Sopenharmony_ci scsi_pointer->this_residual -= fast; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (!status) { 68362306a36Sopenharmony_ci imm_fail(dev, DID_BUS_BUSY); 68462306a36Sopenharmony_ci return -1; /* ERROR_RETURN */ 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci if (scsi_pointer->buffer && !scsi_pointer->this_residual) { 68762306a36Sopenharmony_ci /* if scatter/gather, advance to the next segment */ 68862306a36Sopenharmony_ci if (scsi_pointer->buffers_residual--) { 68962306a36Sopenharmony_ci scsi_pointer->buffer = 69062306a36Sopenharmony_ci sg_next(scsi_pointer->buffer); 69162306a36Sopenharmony_ci scsi_pointer->this_residual = 69262306a36Sopenharmony_ci scsi_pointer->buffer->length; 69362306a36Sopenharmony_ci scsi_pointer->ptr = sg_virt(scsi_pointer->buffer); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* 69662306a36Sopenharmony_ci * Make sure that we transfer even number of bytes 69762306a36Sopenharmony_ci * otherwise it makes imm_byte_out() messy. 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_ci if (scsi_pointer->this_residual & 0x01) 70062306a36Sopenharmony_ci scsi_pointer->this_residual++; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci /* Now check to see if the drive is ready to comunicate */ 70462306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 70562306a36Sopenharmony_ci r = (r_str(ppb) & 0xb8); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* If not, drop back down to the scheduler and wait a timer tick */ 70862306a36Sopenharmony_ci if (!(r & 0x80)) 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci return 1; /* FINISH_RETURN */ 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci/* 71562306a36Sopenharmony_ci * Since the IMM itself doesn't generate interrupts, we use 71662306a36Sopenharmony_ci * the scheduler's task queue to generate a stream of call-backs and 71762306a36Sopenharmony_ci * complete the request when the drive is ready. 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_cistatic void imm_interrupt(struct work_struct *work) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci imm_struct *dev = container_of(work, imm_struct, imm_tq.work); 72262306a36Sopenharmony_ci struct scsi_cmnd *cmd = dev->cur_cmd; 72362306a36Sopenharmony_ci struct Scsi_Host *host = cmd->device->host; 72462306a36Sopenharmony_ci unsigned long flags; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (imm_engine(dev, cmd)) { 72762306a36Sopenharmony_ci schedule_delayed_work(&dev->imm_tq, 1); 72862306a36Sopenharmony_ci return; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci /* Command must of completed hence it is safe to let go... */ 73162306a36Sopenharmony_ci#if IMM_DEBUG > 0 73262306a36Sopenharmony_ci switch ((cmd->result >> 16) & 0xff) { 73362306a36Sopenharmony_ci case DID_OK: 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci case DID_NO_CONNECT: 73662306a36Sopenharmony_ci printk("imm: no device at SCSI ID %i\n", cmd->device->id); 73762306a36Sopenharmony_ci break; 73862306a36Sopenharmony_ci case DID_BUS_BUSY: 73962306a36Sopenharmony_ci printk("imm: BUS BUSY - EPP timeout detected\n"); 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci case DID_TIME_OUT: 74262306a36Sopenharmony_ci printk("imm: unknown timeout\n"); 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case DID_ABORT: 74562306a36Sopenharmony_ci printk("imm: told to abort\n"); 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case DID_PARITY: 74862306a36Sopenharmony_ci printk("imm: parity error (???)\n"); 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci case DID_ERROR: 75162306a36Sopenharmony_ci printk("imm: internal driver error\n"); 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci case DID_RESET: 75462306a36Sopenharmony_ci printk("imm: told to reset device\n"); 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci case DID_BAD_INTR: 75762306a36Sopenharmony_ci printk("imm: bad interrupt (???)\n"); 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci default: 76062306a36Sopenharmony_ci printk("imm: bad return code (%02x)\n", 76162306a36Sopenharmony_ci (cmd->result >> 16) & 0xff); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci#endif 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (imm_scsi_pointer(cmd)->phase > 1) 76662306a36Sopenharmony_ci imm_disconnect(dev); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci imm_pb_dismiss(dev); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci spin_lock_irqsave(host->host_lock, flags); 77162306a36Sopenharmony_ci dev->cur_cmd = NULL; 77262306a36Sopenharmony_ci scsi_done(cmd); 77362306a36Sopenharmony_ci spin_unlock_irqrestore(host->host_lock, flags); 77462306a36Sopenharmony_ci return; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int imm_engine(imm_struct *dev, struct scsi_cmnd *const cmd) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct scsi_pointer *scsi_pointer = imm_scsi_pointer(cmd); 78062306a36Sopenharmony_ci unsigned short ppb = dev->base; 78162306a36Sopenharmony_ci unsigned char l = 0, h = 0; 78262306a36Sopenharmony_ci int retv, x; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* First check for any errors that may have occurred 78562306a36Sopenharmony_ci * Here we check for internal errors 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_ci if (dev->failed) 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci switch (scsi_pointer->phase) { 79162306a36Sopenharmony_ci case 0: /* Phase 0 - Waiting for parport */ 79262306a36Sopenharmony_ci if (time_after(jiffies, dev->jstart + HZ)) { 79362306a36Sopenharmony_ci /* 79462306a36Sopenharmony_ci * We waited more than a second 79562306a36Sopenharmony_ci * for parport to call us 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci imm_fail(dev, DID_BUS_BUSY); 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci return 1; /* wait until imm_wakeup claims parport */ 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci case 1: /* Phase 1 - Connected */ 80362306a36Sopenharmony_ci imm_connect(dev, CONNECT_EPP_MAYBE); 80462306a36Sopenharmony_ci scsi_pointer->phase++; 80562306a36Sopenharmony_ci fallthrough; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci case 2: /* Phase 2 - We are now talking to the scsi bus */ 80862306a36Sopenharmony_ci if (!imm_select(dev, scmd_id(cmd))) { 80962306a36Sopenharmony_ci imm_fail(dev, DID_NO_CONNECT); 81062306a36Sopenharmony_ci return 0; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci scsi_pointer->phase++; 81362306a36Sopenharmony_ci fallthrough; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci case 3: /* Phase 3 - Ready to accept a command */ 81662306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 81762306a36Sopenharmony_ci if (!(r_str(ppb) & 0x80)) 81862306a36Sopenharmony_ci return 1; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (!imm_send_command(cmd)) 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci scsi_pointer->phase++; 82362306a36Sopenharmony_ci fallthrough; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci case 4: /* Phase 4 - Setup scatter/gather buffers */ 82662306a36Sopenharmony_ci if (scsi_bufflen(cmd)) { 82762306a36Sopenharmony_ci scsi_pointer->buffer = scsi_sglist(cmd); 82862306a36Sopenharmony_ci scsi_pointer->this_residual = scsi_pointer->buffer->length; 82962306a36Sopenharmony_ci scsi_pointer->ptr = sg_virt(scsi_pointer->buffer); 83062306a36Sopenharmony_ci } else { 83162306a36Sopenharmony_ci scsi_pointer->buffer = NULL; 83262306a36Sopenharmony_ci scsi_pointer->this_residual = 0; 83362306a36Sopenharmony_ci scsi_pointer->ptr = NULL; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci scsi_pointer->buffers_residual = scsi_sg_count(cmd) - 1; 83662306a36Sopenharmony_ci scsi_pointer->phase++; 83762306a36Sopenharmony_ci if (scsi_pointer->this_residual & 0x01) 83862306a36Sopenharmony_ci scsi_pointer->this_residual++; 83962306a36Sopenharmony_ci fallthrough; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci case 5: /* Phase 5 - Pre-Data transfer stage */ 84262306a36Sopenharmony_ci /* Spin lock for BUSY */ 84362306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 84462306a36Sopenharmony_ci if (!(r_str(ppb) & 0x80)) 84562306a36Sopenharmony_ci return 1; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* Require negotiation for read requests */ 84862306a36Sopenharmony_ci x = (r_str(ppb) & 0xb8); 84962306a36Sopenharmony_ci dev->rd = (x & 0x10) ? 1 : 0; 85062306a36Sopenharmony_ci dev->dp = (x & 0x20) ? 0 : 1; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if ((dev->dp) && (dev->rd)) 85362306a36Sopenharmony_ci if (imm_negotiate(dev)) 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci scsi_pointer->phase++; 85662306a36Sopenharmony_ci fallthrough; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci case 6: /* Phase 6 - Data transfer stage */ 85962306a36Sopenharmony_ci /* Spin lock for BUSY */ 86062306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 86162306a36Sopenharmony_ci if (!(r_str(ppb) & 0x80)) 86262306a36Sopenharmony_ci return 1; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (dev->dp) { 86562306a36Sopenharmony_ci retv = imm_completion(cmd); 86662306a36Sopenharmony_ci if (retv == -1) 86762306a36Sopenharmony_ci return 0; 86862306a36Sopenharmony_ci if (retv == 0) 86962306a36Sopenharmony_ci return 1; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci scsi_pointer->phase++; 87262306a36Sopenharmony_ci fallthrough; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci case 7: /* Phase 7 - Post data transfer stage */ 87562306a36Sopenharmony_ci if ((dev->dp) && (dev->rd)) { 87662306a36Sopenharmony_ci if ((dev->mode == IMM_NIBBLE) || (dev->mode == IMM_PS2)) { 87762306a36Sopenharmony_ci w_ctr(ppb, 0x4); 87862306a36Sopenharmony_ci w_ctr(ppb, 0xc); 87962306a36Sopenharmony_ci w_ctr(ppb, 0xe); 88062306a36Sopenharmony_ci w_ctr(ppb, 0x4); 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci scsi_pointer->phase++; 88462306a36Sopenharmony_ci fallthrough; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci case 8: /* Phase 8 - Read status/message */ 88762306a36Sopenharmony_ci /* Check for data overrun */ 88862306a36Sopenharmony_ci if (imm_wait(dev) != (unsigned char) 0xb8) { 88962306a36Sopenharmony_ci imm_fail(dev, DID_ERROR); 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci if (imm_negotiate(dev)) 89362306a36Sopenharmony_ci return 0; 89462306a36Sopenharmony_ci if (imm_in(dev, &l, 1)) { /* read status byte */ 89562306a36Sopenharmony_ci /* Check for optional message byte */ 89662306a36Sopenharmony_ci if (imm_wait(dev) == (unsigned char) 0xb8) 89762306a36Sopenharmony_ci imm_in(dev, &h, 1); 89862306a36Sopenharmony_ci cmd->result = (DID_OK << 16) | (l & STATUS_MASK); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci if ((dev->mode == IMM_NIBBLE) || (dev->mode == IMM_PS2)) { 90162306a36Sopenharmony_ci w_ctr(ppb, 0x4); 90262306a36Sopenharmony_ci w_ctr(ppb, 0xc); 90362306a36Sopenharmony_ci w_ctr(ppb, 0xe); 90462306a36Sopenharmony_ci w_ctr(ppb, 0x4); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci return 0; /* Finished */ 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci default: 90962306a36Sopenharmony_ci printk("imm: Invalid scsi phase\n"); 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci return 0; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic int imm_queuecommand_lck(struct scsi_cmnd *cmd) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci imm_struct *dev = imm_dev(cmd->device->host); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (dev->cur_cmd) { 91962306a36Sopenharmony_ci printk("IMM: bug in imm_queuecommand\n"); 92062306a36Sopenharmony_ci return 0; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci dev->failed = 0; 92362306a36Sopenharmony_ci dev->jstart = jiffies; 92462306a36Sopenharmony_ci dev->cur_cmd = cmd; 92562306a36Sopenharmony_ci cmd->result = DID_ERROR << 16; /* default return code */ 92662306a36Sopenharmony_ci imm_scsi_pointer(cmd)->phase = 0; /* bus free */ 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci schedule_delayed_work(&dev->imm_tq, 0); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci imm_pb_claim(dev); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci return 0; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic DEF_SCSI_QCMD(imm_queuecommand) 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci/* 93862306a36Sopenharmony_ci * Apparently the disk->capacity attribute is off by 1 sector 93962306a36Sopenharmony_ci * for all disk drives. We add the one here, but it should really 94062306a36Sopenharmony_ci * be done in sd.c. Even if it gets fixed there, this will still 94162306a36Sopenharmony_ci * work. 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_cistatic int imm_biosparam(struct scsi_device *sdev, struct block_device *dev, 94462306a36Sopenharmony_ci sector_t capacity, int ip[]) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci ip[0] = 0x40; 94762306a36Sopenharmony_ci ip[1] = 0x20; 94862306a36Sopenharmony_ci ip[2] = ((unsigned long) capacity + 1) / (ip[0] * ip[1]); 94962306a36Sopenharmony_ci if (ip[2] > 1024) { 95062306a36Sopenharmony_ci ip[0] = 0xff; 95162306a36Sopenharmony_ci ip[1] = 0x3f; 95262306a36Sopenharmony_ci ip[2] = ((unsigned long) capacity + 1) / (ip[0] * ip[1]); 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int imm_abort(struct scsi_cmnd *cmd) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci imm_struct *dev = imm_dev(cmd->device->host); 96062306a36Sopenharmony_ci /* 96162306a36Sopenharmony_ci * There is no method for aborting commands since Iomega 96262306a36Sopenharmony_ci * have tied the SCSI_MESSAGE line high in the interface 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci switch (imm_scsi_pointer(cmd)->phase) { 96662306a36Sopenharmony_ci case 0: /* Do not have access to parport */ 96762306a36Sopenharmony_ci case 1: /* Have not connected to interface */ 96862306a36Sopenharmony_ci dev->cur_cmd = NULL; /* Forget the problem */ 96962306a36Sopenharmony_ci return SUCCESS; 97062306a36Sopenharmony_ci default: /* SCSI command sent, can not abort */ 97162306a36Sopenharmony_ci return FAILED; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic void imm_reset_pulse(unsigned int base) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci w_ctr(base, 0x04); 97862306a36Sopenharmony_ci w_dtr(base, 0x40); 97962306a36Sopenharmony_ci udelay(1); 98062306a36Sopenharmony_ci w_ctr(base, 0x0c); 98162306a36Sopenharmony_ci w_ctr(base, 0x0d); 98262306a36Sopenharmony_ci udelay(50); 98362306a36Sopenharmony_ci w_ctr(base, 0x0c); 98462306a36Sopenharmony_ci w_ctr(base, 0x04); 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic int imm_reset(struct scsi_cmnd *cmd) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci imm_struct *dev = imm_dev(cmd->device->host); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (imm_scsi_pointer(cmd)->phase) 99262306a36Sopenharmony_ci imm_disconnect(dev); 99362306a36Sopenharmony_ci dev->cur_cmd = NULL; /* Forget the problem */ 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci imm_connect(dev, CONNECT_NORMAL); 99662306a36Sopenharmony_ci imm_reset_pulse(dev->base); 99762306a36Sopenharmony_ci mdelay(1); /* device settle delay */ 99862306a36Sopenharmony_ci imm_disconnect(dev); 99962306a36Sopenharmony_ci mdelay(1); /* device settle delay */ 100062306a36Sopenharmony_ci return SUCCESS; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic int device_check(imm_struct *dev) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci /* This routine looks for a device and then attempts to use EPP 100662306a36Sopenharmony_ci to send a command. If all goes as planned then EPP is available. */ 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci static char cmd[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 100962306a36Sopenharmony_ci int loop, old_mode, status, k, ppb = dev->base; 101062306a36Sopenharmony_ci unsigned char l; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci old_mode = dev->mode; 101362306a36Sopenharmony_ci for (loop = 0; loop < 8; loop++) { 101462306a36Sopenharmony_ci /* Attempt to use EPP for Test Unit Ready */ 101562306a36Sopenharmony_ci if ((ppb & 0x0007) == 0x0000) 101662306a36Sopenharmony_ci dev->mode = IMM_EPP_32; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci second_pass: 101962306a36Sopenharmony_ci imm_connect(dev, CONNECT_EPP_MAYBE); 102062306a36Sopenharmony_ci /* Select SCSI device */ 102162306a36Sopenharmony_ci if (!imm_select(dev, loop)) { 102262306a36Sopenharmony_ci imm_disconnect(dev); 102362306a36Sopenharmony_ci continue; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci printk("imm: Found device at ID %i, Attempting to use %s\n", 102662306a36Sopenharmony_ci loop, IMM_MODE_STRING[dev->mode]); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* Send SCSI command */ 102962306a36Sopenharmony_ci status = 1; 103062306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 103162306a36Sopenharmony_ci for (l = 0; (l < 3) && (status); l++) 103262306a36Sopenharmony_ci status = imm_out(dev, &cmd[l << 1], 2); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (!status) { 103562306a36Sopenharmony_ci imm_disconnect(dev); 103662306a36Sopenharmony_ci imm_connect(dev, CONNECT_EPP_MAYBE); 103762306a36Sopenharmony_ci imm_reset_pulse(dev->base); 103862306a36Sopenharmony_ci udelay(1000); 103962306a36Sopenharmony_ci imm_disconnect(dev); 104062306a36Sopenharmony_ci udelay(1000); 104162306a36Sopenharmony_ci if (dev->mode == IMM_EPP_32) { 104262306a36Sopenharmony_ci dev->mode = old_mode; 104362306a36Sopenharmony_ci goto second_pass; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci printk("imm: Unable to establish communication\n"); 104662306a36Sopenharmony_ci return -EIO; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci k = 1000000; /* 1 Second */ 105162306a36Sopenharmony_ci do { 105262306a36Sopenharmony_ci l = r_str(ppb); 105362306a36Sopenharmony_ci k--; 105462306a36Sopenharmony_ci udelay(1); 105562306a36Sopenharmony_ci } while (!(l & 0x80) && (k)); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci l &= 0xb8; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (l != 0xb8) { 106062306a36Sopenharmony_ci imm_disconnect(dev); 106162306a36Sopenharmony_ci imm_connect(dev, CONNECT_EPP_MAYBE); 106262306a36Sopenharmony_ci imm_reset_pulse(dev->base); 106362306a36Sopenharmony_ci udelay(1000); 106462306a36Sopenharmony_ci imm_disconnect(dev); 106562306a36Sopenharmony_ci udelay(1000); 106662306a36Sopenharmony_ci if (dev->mode == IMM_EPP_32) { 106762306a36Sopenharmony_ci dev->mode = old_mode; 106862306a36Sopenharmony_ci goto second_pass; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci printk 107162306a36Sopenharmony_ci ("imm: Unable to establish communication\n"); 107262306a36Sopenharmony_ci return -EIO; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci imm_disconnect(dev); 107562306a36Sopenharmony_ci printk 107662306a36Sopenharmony_ci ("imm: Communication established at 0x%x with ID %i using %s\n", 107762306a36Sopenharmony_ci ppb, loop, IMM_MODE_STRING[dev->mode]); 107862306a36Sopenharmony_ci imm_connect(dev, CONNECT_EPP_MAYBE); 107962306a36Sopenharmony_ci imm_reset_pulse(dev->base); 108062306a36Sopenharmony_ci udelay(1000); 108162306a36Sopenharmony_ci imm_disconnect(dev); 108262306a36Sopenharmony_ci udelay(1000); 108362306a36Sopenharmony_ci return 0; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci printk("imm: No devices found\n"); 108662306a36Sopenharmony_ci return -ENODEV; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci/* 109062306a36Sopenharmony_ci * imm cannot deal with highmem, so this causes all IO pages for this host 109162306a36Sopenharmony_ci * to reside in low memory (hence mapped) 109262306a36Sopenharmony_ci */ 109362306a36Sopenharmony_cistatic int imm_adjust_queue(struct scsi_device *device) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci blk_queue_bounce_limit(device->request_queue, BLK_BOUNCE_HIGH); 109662306a36Sopenharmony_ci return 0; 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic const struct scsi_host_template imm_template = { 110062306a36Sopenharmony_ci .module = THIS_MODULE, 110162306a36Sopenharmony_ci .proc_name = "imm", 110262306a36Sopenharmony_ci .show_info = imm_show_info, 110362306a36Sopenharmony_ci .write_info = imm_write_info, 110462306a36Sopenharmony_ci .name = "Iomega VPI2 (imm) interface", 110562306a36Sopenharmony_ci .queuecommand = imm_queuecommand, 110662306a36Sopenharmony_ci .eh_abort_handler = imm_abort, 110762306a36Sopenharmony_ci .eh_host_reset_handler = imm_reset, 110862306a36Sopenharmony_ci .bios_param = imm_biosparam, 110962306a36Sopenharmony_ci .this_id = 7, 111062306a36Sopenharmony_ci .sg_tablesize = SG_ALL, 111162306a36Sopenharmony_ci .can_queue = 1, 111262306a36Sopenharmony_ci .slave_alloc = imm_adjust_queue, 111362306a36Sopenharmony_ci .cmd_size = sizeof(struct scsi_pointer), 111462306a36Sopenharmony_ci}; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci/*************************************************************************** 111762306a36Sopenharmony_ci * Parallel port probing routines * 111862306a36Sopenharmony_ci ***************************************************************************/ 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic LIST_HEAD(imm_hosts); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci/* 112362306a36Sopenharmony_ci * Finds the first available device number that can be alloted to the 112462306a36Sopenharmony_ci * new imm device and returns the address of the previous node so that 112562306a36Sopenharmony_ci * we can add to the tail and have a list in the ascending order. 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic inline imm_struct *find_parent(void) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci imm_struct *dev, *par = NULL; 113162306a36Sopenharmony_ci unsigned int cnt = 0; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (list_empty(&imm_hosts)) 113462306a36Sopenharmony_ci return NULL; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci list_for_each_entry(dev, &imm_hosts, list) { 113762306a36Sopenharmony_ci if (dev->dev_no != cnt) 113862306a36Sopenharmony_ci return par; 113962306a36Sopenharmony_ci cnt++; 114062306a36Sopenharmony_ci par = dev; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci return par; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic int __imm_attach(struct parport *pb) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci struct Scsi_Host *host; 114962306a36Sopenharmony_ci imm_struct *dev, *temp; 115062306a36Sopenharmony_ci DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waiting); 115162306a36Sopenharmony_ci DEFINE_WAIT(wait); 115262306a36Sopenharmony_ci int ports; 115362306a36Sopenharmony_ci int modes, ppb; 115462306a36Sopenharmony_ci int err = -ENOMEM; 115562306a36Sopenharmony_ci struct pardev_cb imm_cb; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci init_waitqueue_head(&waiting); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci dev = kzalloc(sizeof(imm_struct), GFP_KERNEL); 116062306a36Sopenharmony_ci if (!dev) 116162306a36Sopenharmony_ci return -ENOMEM; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci dev->base = -1; 116562306a36Sopenharmony_ci dev->mode = IMM_AUTODETECT; 116662306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->list); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci temp = find_parent(); 116962306a36Sopenharmony_ci if (temp) 117062306a36Sopenharmony_ci dev->dev_no = temp->dev_no + 1; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci memset(&imm_cb, 0, sizeof(imm_cb)); 117362306a36Sopenharmony_ci imm_cb.private = dev; 117462306a36Sopenharmony_ci imm_cb.wakeup = imm_wakeup; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci dev->dev = parport_register_dev_model(pb, "imm", &imm_cb, dev->dev_no); 117762306a36Sopenharmony_ci if (!dev->dev) 117862306a36Sopenharmony_ci goto out; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* Claim the bus so it remembers what we do to the control 118262306a36Sopenharmony_ci * registers. [ CTR and ECP ] 118362306a36Sopenharmony_ci */ 118462306a36Sopenharmony_ci err = -EBUSY; 118562306a36Sopenharmony_ci dev->waiting = &waiting; 118662306a36Sopenharmony_ci prepare_to_wait(&waiting, &wait, TASK_UNINTERRUPTIBLE); 118762306a36Sopenharmony_ci if (imm_pb_claim(dev)) 118862306a36Sopenharmony_ci schedule_timeout(3 * HZ); 118962306a36Sopenharmony_ci if (dev->wanted) { 119062306a36Sopenharmony_ci printk(KERN_ERR "imm%d: failed to claim parport because " 119162306a36Sopenharmony_ci "a pardevice is owning the port for too long " 119262306a36Sopenharmony_ci "time!\n", pb->number); 119362306a36Sopenharmony_ci imm_pb_dismiss(dev); 119462306a36Sopenharmony_ci dev->waiting = NULL; 119562306a36Sopenharmony_ci finish_wait(&waiting, &wait); 119662306a36Sopenharmony_ci goto out1; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci dev->waiting = NULL; 119962306a36Sopenharmony_ci finish_wait(&waiting, &wait); 120062306a36Sopenharmony_ci ppb = dev->base = dev->dev->port->base; 120162306a36Sopenharmony_ci dev->base_hi = dev->dev->port->base_hi; 120262306a36Sopenharmony_ci w_ctr(ppb, 0x0c); 120362306a36Sopenharmony_ci modes = dev->dev->port->modes; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* Mode detection works up the chain of speed 120662306a36Sopenharmony_ci * This avoids a nasty if-then-else-if-... tree 120762306a36Sopenharmony_ci */ 120862306a36Sopenharmony_ci dev->mode = IMM_NIBBLE; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (modes & PARPORT_MODE_TRISTATE) 121162306a36Sopenharmony_ci dev->mode = IMM_PS2; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* Done configuration */ 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci err = imm_init(dev); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci imm_pb_release(dev); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci if (err) 122062306a36Sopenharmony_ci goto out1; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci /* now the glue ... */ 122362306a36Sopenharmony_ci if (dev->mode == IMM_NIBBLE || dev->mode == IMM_PS2) 122462306a36Sopenharmony_ci ports = 3; 122562306a36Sopenharmony_ci else 122662306a36Sopenharmony_ci ports = 8; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci INIT_DELAYED_WORK(&dev->imm_tq, imm_interrupt); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci err = -ENOMEM; 123162306a36Sopenharmony_ci host = scsi_host_alloc(&imm_template, sizeof(imm_struct *)); 123262306a36Sopenharmony_ci if (!host) 123362306a36Sopenharmony_ci goto out1; 123462306a36Sopenharmony_ci host->io_port = pb->base; 123562306a36Sopenharmony_ci host->n_io_port = ports; 123662306a36Sopenharmony_ci host->dma_channel = -1; 123762306a36Sopenharmony_ci host->unique_id = pb->number; 123862306a36Sopenharmony_ci *(imm_struct **)&host->hostdata = dev; 123962306a36Sopenharmony_ci dev->host = host; 124062306a36Sopenharmony_ci if (!temp) 124162306a36Sopenharmony_ci list_add_tail(&dev->list, &imm_hosts); 124262306a36Sopenharmony_ci else 124362306a36Sopenharmony_ci list_add_tail(&dev->list, &temp->list); 124462306a36Sopenharmony_ci err = scsi_add_host(host, NULL); 124562306a36Sopenharmony_ci if (err) 124662306a36Sopenharmony_ci goto out2; 124762306a36Sopenharmony_ci scsi_scan_host(host); 124862306a36Sopenharmony_ci return 0; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ciout2: 125162306a36Sopenharmony_ci list_del_init(&dev->list); 125262306a36Sopenharmony_ci scsi_host_put(host); 125362306a36Sopenharmony_ciout1: 125462306a36Sopenharmony_ci parport_unregister_device(dev->dev); 125562306a36Sopenharmony_ciout: 125662306a36Sopenharmony_ci kfree(dev); 125762306a36Sopenharmony_ci return err; 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic void imm_attach(struct parport *pb) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci __imm_attach(pb); 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_cistatic void imm_detach(struct parport *pb) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci imm_struct *dev; 126862306a36Sopenharmony_ci list_for_each_entry(dev, &imm_hosts, list) { 126962306a36Sopenharmony_ci if (dev->dev->port == pb) { 127062306a36Sopenharmony_ci list_del_init(&dev->list); 127162306a36Sopenharmony_ci scsi_remove_host(dev->host); 127262306a36Sopenharmony_ci scsi_host_put(dev->host); 127362306a36Sopenharmony_ci parport_unregister_device(dev->dev); 127462306a36Sopenharmony_ci kfree(dev); 127562306a36Sopenharmony_ci break; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic struct parport_driver imm_driver = { 128162306a36Sopenharmony_ci .name = "imm", 128262306a36Sopenharmony_ci .match_port = imm_attach, 128362306a36Sopenharmony_ci .detach = imm_detach, 128462306a36Sopenharmony_ci .devmodel = true, 128562306a36Sopenharmony_ci}; 128662306a36Sopenharmony_cimodule_parport_driver(imm_driver); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1289