/* 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 */
wait_queue_head_t *waiting;
struct Scsi_Host *host;
struct list_head list;
} ppa_struct;
#include "ppa.h"
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)
dev->cur_cmd->SCp.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