/* imm.c -- low level driver for the IOMEGA MatchMaker
* parallel port SCSI host adapter.
*
*
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/parport.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
/* The following #define is to avoid a clash with hosts.c */
#define IMM_PROBE_SPP 0x0001
#define IMM_PROBE_PS2 0x0002
#define IMM_PROBE_ECR 0x0010
#define IMM_PROBE_EPP17 0x0100
#define IMM_PROBE_EPP19 0x0200
typedef struct {
struct pardevice *dev; /* Parport device entry */
int base; /* Actual port address */
int base_hi; /* Hi Base address for ECP-ISA chipset */
int mode; /* Transfer mode */
struct scsi_cmnd *cur_cmd; /* Current queued command */
struct delayed_work imm_tq; /* Polling interrupt stuff */
unsigned long jstart; /* Jiffies at start */
unsigned failed:1; /* Failure flag */
unsigned dp:1; /* Data phase present */
unsigned rd:1; /* Read data in data phase */
unsigned wanted:1; /* Parport sharing busy flag */
wait_queue_head_t *waiting;
struct Scsi_Host *host;
struct list_head list;
} imm_struct;
static void imm_reset_pulse(unsigned int base);
static int device_check(imm_struct *dev);
#include "imm.h"
static inline imm_struct *imm_dev(struct Scsi_Host *host)
{
return *(imm_struct **)&host->hostdata;
}
static DEFINE_SPINLOCK(arbitration_lock);
static void got_it(imm_struct *dev)
{
dev->base = dev->dev->port->base;
if (dev->cur_cmd)
dev->cur_cmd->SCp.phase = 1;
else
wake_up(dev->waiting);
}
static void imm_wakeup(void *ref)
{
imm_struct *dev = (imm_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);
}
static int imm_pb_claim(imm_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 imm_pb_dismiss(imm_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 imm_pb_release(imm_struct *dev)
{
parport_release(dev->dev);
}
/* This is to give the imm driver a way to modify the timings (and other
* parameters) by writing to the /proc/scsi/imm/0 file.
* Very simple method really... (Too 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 imm_proc_write(imm_struct *dev, char *buffer, int length)
{
unsigned long x;
if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {
x = simple_strtoul(buffer + 5, NULL, 0);
dev->mode = x;
return length;
}
printk("imm /proc: invalid variable\n");
return (-EINVAL);
}
static int imm_proc_info(struct Scsi_Host *host, char *buffer, char **start,
off_t offset, int length, int inout)
{
imm_struct *dev = imm_dev(host);
int len = 0;
if (inout)
return imm_proc_write(dev, buffer, length);
len += sprintf(buffer + len, "Version : %s\n", IMM_VERSION);
len +=
sprintf(buffer + len, "Parport : %s\n",
dev->dev->port->name);
len +=
sprintf(buffer + len, "Mode : %s\n",
IMM_MODE_STRING[dev->mode]);
/* Request for beyond end of buffer */
if (offset > len)
return 0;
*start = buffer + offset;
len -= offset;
if (len > length)
len = length;
return len;
}
#if IMM_DEBUG > 0
#define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\
y, __func__, __LINE__); imm_fail_func(x,y);
static inline void
imm_fail_func(imm_struct *dev, int error_code)
#else
static inline void
imm_fail(imm_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 imm_wait(imm_struct *dev)
{
int k;
unsigned short ppb = dev->base;
unsigned char r;
w_ctr(ppb, 0x0c);
k = IMM_SPIN_TMO;
do {
r = r_str(ppb);
k--;
udelay(1);
}
while (!(r & 0x80) && (k));
/*
* STR register (LPT base+1) to SCSI mapping:
*
* STR imm imm
* ===================================
* 0x80 S_REQ S_REQ
* 0x40 !S_BSY (????)
* 0x20 !S_CD !S_CD
* 0x10 !S_IO !S_IO
* 0x08 (????) !S_BSY
*
* imm imm meaning
* ==================================
* 0xf0 0xb8 Bit mask
* 0xc0 0x88 ZIP wants more data
* 0xd0 0x98 ZIP wants to send more data
* 0xe0 0xa8 ZIP is expecting SCSI command data
* 0xf0 0xb8 end of transfer, ZIP is sending status
*/
w_ctr(ppb, 0x04);
if (k)
return (r & 0xb8);
/* Counter expired - Time out occurred */
imm_fail(dev, DID_TIME_OUT);
printk("imm timeout in imm_wait\n");
return 0; /* command timed out */
}
static int imm_negotiate(imm_struct * tmp)
{
/*
* The following is supposedly the IEEE 1284-1994 negotiate
* sequence. I have yet to obtain a copy of the above standard
* so this is a bit of a guess...
*
* A fair chunk of this is based on the Linux parport implementation
* of IEEE 1284.
*
* Return 0 if data available
* 1 if no data available
*/
unsigned short base = tmp->base;
unsigned char a, mode;
switch (tmp->mode) {
case IMM_NIBBLE:
mode = 0x00;
break;
case IMM_PS2:
mode = 0x01;
break;
default:
return 0;
}
w_ctr(base, 0x04);
udelay(5);
w_dtr(base, mode);
udelay(100);
w_ctr(base, 0x06);
udelay(5);
a = (r_str(base) & 0x20) ? 0 : 1;
udelay(5);
w_ctr(base, 0x07);
udelay(5);
w_ctr(base, 0x06);
if (a) {
printk
("IMM: IEEE1284 negotiate indicates no data available.\n");
imm_fail(tmp, DID_ERROR);
}
return a;
}
/*
* 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(imm_struct *dev)
{
int i, ppb_hi = dev->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("imm: ECP sync failed as data still present in FIFO.\n");
}
}
static int imm_byte_out(unsigned short base, const char *buffer, int len)
{
int i;
w_ctr(base, 0x4); /* apparently a sane mode */
for (i = len >> 1; i; i--) {
w_dtr(base, *buffer++);
w_ctr(base, 0x5); /* Drop STROBE low */
w_dtr(base, *buffer++);
w_ctr(base, 0x0); /* STROBE high + INIT low */
}
w_ctr(base, 0x4); /* apparently a sane mode */
return 1; /* All went well - we hope! */
}
static int imm_nibble_in(unsigned short base, char *buffer, int len)
{
unsigned char l;
int i;
/*
* The following is based on documented timing signals
*/
w_ctr(base, 0x4);
for (i = len; i; i--) {
w_ctr(base, 0x6);
l = (r_str(base) & 0xf0) >> 4;
w_ctr(base, 0x5);
*buffer++ = (r_str(base) & 0xf0) | l;
w_ctr(base, 0x4);
}
return 1; /* All went well - we hope! */
}
static int imm_byte_in(unsigned short base, char *buffer, int len)
{
int i;
/*
* The following is based on documented timing