// SPDX-License-Identifier: GPL-2.0-only
/* imm.c -- low level driver for the IOMEGA MatchMaker
* parallel port SCSI host adapter.
*
* (The IMM is the embedded controller in the ZIP Plus drive.)
*
* My unofficial company acronym list is 21 pages long:
* FLA: Four letter acronym with built in facility for
* future expansion to five letters.
*/
#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 */
unsigned int dev_no; /* Device number */
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, bool autodetect);
#include "imm.h"
static unsigned int mode = IMM_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 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)
imm_scsi_pointer(dev->cur_cmd)->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) {
if (parport_claim(dev->dev) == 0) {
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 <