#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi_tcq.h>
#include "../qlogicfas408.h"
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include <pcmcia/ciscode.h>
#define INT_TYPE 0
static char qlogic_name[] = "qlogic_cs";
static struct scsi_host_template qlogicfas_driver_template = {
.module = THIS_MODULE,
.name = qlogic_name,
.proc_name = qlogic_name,
.info = qlogicfas408_info,
.queuecommand = qlogicfas408_queuecommand,
.eh_abort_handler = qlogicfas408_abort,
.eh_host_reset_handler = qlogicfas408_host_reset,
.bios_param = qlogicfas408_biosparam,
.can_queue = 1,
.this_id = -1,
.sg_tablesize = SG_ALL,
.dma_boundary = PAGE_SIZE - 1,
};
typedef struct scsi_info_t {
struct pcmcia_device *p_dev;
struct Scsi_Host *host;
unsigned short manf_id;
} scsi_info_t;
static void qlogic_release(struct pcmcia_device *link);
static void qlogic_detach(struct pcmcia_device *p_dev);
static int qlogic_config(struct pcmcia_device * link);
static struct Scsi_Host *qlogic_detect(struct scsi_host_template *host,
struct pcmcia_device *link, int qbase, int qlirq)
{
int qltyp;
int qinitid;
struct Scsi_Host *shost;
struct qlogicfas408_priv *priv;
qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
qinitid = host->this_id;
if (qinitid < 0)
qinitid = 7;
qlogicfas408_setup(qbase, qinitid, INT_TYPE);
host->name = qlogic_name;
shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
if (!shost)
goto err;
shost->io_port = qbase;
shost->n_io_port = 16;
shost->dma_channel = -1;
if (qlirq != -1)
shost->irq = qlirq;
priv = get_priv_by_host(shost);
priv->qlirq = qlirq;
priv->qbase = qbase;
priv->qinitid = qinitid;
priv->shost = shost;
priv->int_type = INT_TYPE;
if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
goto free_scsi_host;
sprintf(priv->qinfo,
"Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
qltyp, qbase, qlirq, QL_TURBO_PDMA);
if (scsi_add_host(shost, NULL))
goto free_interrupt;
scsi_scan_host(shost);
return shost;
free_interrupt:
free_irq(qlirq, shost);
free_scsi_host:
scsi_host_put(shost);
err:
return NULL;
}
static int qlogic_probe(struct pcmcia_device *link)
{
scsi_info_t *info;
dev_dbg(&link->dev, "qlogic_attach()\n");
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->p_dev = link;
link->priv = info;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
link->config_regs = PRESENT_OPTION;
return qlogic_config(link);
}
static void qlogic_detach(struct pcmcia_device *link)
{
dev_dbg(&link->dev, "qlogic_detach\n");
qlogic_release(link);
kfree(link->priv);
}
static int qlogic_config_check(struct pcmcia_device *p_dev, void *priv_data)
{
p_dev->io_lines = 10;
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
if (p_dev->resource[0]->start == 0)
return -ENODEV;
return pcmcia_request_io(p_dev);
}
static int qlogic_config(struct pcmcia_device * link)
{
scsi_info_t *info = link->priv;
int ret;
struct Scsi_Host *host;
dev_dbg(&link->dev, "qlogic_config\n");
ret = pcmcia_loop_config(link, qlogic_config_check, NULL);
if (ret)
goto failed;
if (!link->irq)
goto failed;
ret = pcmcia_enable_device(link);
if (ret)
goto failed;
if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
outb(0xb4, link->resource[0]->start + 0xd);
outb(0x24, link->resource[0]->start + 0x9);
outb(0x04, link->resource[0]->start + 0xd);
}
if (resource_size(link->resource[0]) == 32)
host = qlogic_detect(&qlogicfas_driver_template, link,
link->resource[0]->start + 16, link->irq);
else
host = qlogic_detect(&qlogicfas_driver_template, link,
link->resource[0]->start, link->irq);
if (!host) {
printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
goto failed;
}
info->host = host;
return 0;
failed:
pcmcia_disable_device(link);
return -ENODEV;
}
static void qlogic_release(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
dev_dbg(&link->dev, "qlogic_release\n");
scsi_remove_host(info->host);
free_irq(link->irq, info->host);
pcmcia_disable_device(link);
scsi_host_put(info->host);
}
static int qlogic_resume(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
int ret;
ret = pcmcia_enable_device(link);
if (ret)
return ret;
if ((info->manf_id == MANFID_MACNICA) ||
(info->manf_id == MANFID_PIONEER) ||
(info->manf_id == 0x0098)) {
outb(0x80, link->resource[0]->start + 0xd);
outb(0x24, link->resource[0]->start + 0x9);
outb(0x04, link->resource[0]->start + 0xd);
}
qlogicfas408_host_reset(NULL);
return 0;
}
static const struct pcmcia_device_id qlogic_ids[] = {
PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6),
PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751),
PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d),
PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79),
PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a),
PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7),
PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54),
PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec),
PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735),
PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8),
PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f),
PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1),
PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe),
PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0),
PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, qlogic_ids);
static struct pcmcia_driver qlogic_cs_driver = {
.owner = THIS_MODULE,
.name = "qlogic_cs",
.probe = qlogic_probe,
.remove = qlogic_detach,
.id_table = qlogic_ids,
.resume = qlogic_resume,
};
MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
MODULE_LICENSE("GPL");
module_pcmcia_driver