blob: 05b7f755499b1dcad1c90cb353d9758299ac15f8 [file] [log] [blame]
/*
* Oak Generic NCR5380 driver
*
* Copyright 1995-2002, Russell King
*/
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <asm/ecard.h>
#include <asm/io.h>
#include <scsi/scsi_host.h>
#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
#define NCR5380_read(reg) readb(hostdata->io + ((reg) << 2))
#define NCR5380_write(reg, value) writeb(value, hostdata->io + ((reg) << 2))
#define NCR5380_dma_xfer_len NCR5380_dma_xfer_none
#define NCR5380_dma_recv_setup oakscsi_pread
#define NCR5380_dma_send_setup oakscsi_pwrite
#define NCR5380_dma_residual NCR5380_dma_residual_none
#define NCR5380_queue_command oakscsi_queue_command
#define NCR5380_info oakscsi_info
#define NCR5380_implementation_fields /* none */
#include "../NCR5380.h"
#undef START_DMA_INITIATOR_RECEIVE_REG
#define START_DMA_INITIATOR_RECEIVE_REG (128 + 7)
#define STAT ((128 + 16) << 2)
#define DATA ((128 + 8) << 2)
static inline int oakscsi_pwrite(struct NCR5380_hostdata *hostdata,
unsigned char *addr, int len)
{
u8 __iomem *base = hostdata->io;
printk("writing %p len %d\n",addr, len);
while(1)
{
int status;
while (((status = readw(base + STAT)) & 0x100)==0);
}
return 0;
}
static inline int oakscsi_pread(struct NCR5380_hostdata *hostdata,
unsigned char *addr, int len)
{
u8 __iomem *base = hostdata->io;
printk("reading %p len %d\n", addr, len);
while(len > 0)
{
unsigned int status, timeout;
unsigned long b;
timeout = 0x01FFFFFF;
while (((status = readw(base + STAT)) & 0x100)==0)
{
timeout--;
if(status & 0x200 || !timeout)
{
printk("status = %08X\n", status);
return -1;
}
}
if(len >= 128)
{
readsw(base + DATA, addr, 128);
addr += 128;
len -= 128;
}
else
{
b = (unsigned long) readw(base + DATA);
*addr ++ = b;
len -= 1;
if(len)
*addr ++ = b>>8;
len -= 1;
}
}
return 0;
}
#undef STAT
#undef DATA
#include "../NCR5380.c"
static struct scsi_host_template oakscsi_template = {
.module = THIS_MODULE,
.name = "Oak 16-bit SCSI",
.info = oakscsi_info,
.queuecommand = oakscsi_queue_command,
.eh_abort_handler = NCR5380_abort,
.eh_host_reset_handler = NCR5380_host_reset,
.can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
.cmd_per_lun = 2,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "oakscsi",
.cmd_size = NCR5380_CMD_SIZE,
.max_sectors = 128,
};
static int oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct Scsi_Host *host;
int ret = -ENOMEM;
ret = ecard_request_resources(ec);
if (ret)
goto out;
host = scsi_host_alloc(&oakscsi_template, sizeof(struct NCR5380_hostdata));
if (!host) {
ret = -ENOMEM;
goto release;
}
priv(host)->io = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
ecard_resource_len(ec, ECARD_RES_MEMC));
if (!priv(host)->io) {
ret = -ENOMEM;
goto unreg;
}
host->irq = NO_IRQ;
ret = NCR5380_init(host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP);
if (ret)
goto out_unmap;
NCR5380_maybe_reset_bus(host);
ret = scsi_add_host(host, &ec->dev);
if (ret)
goto out_exit;
scsi_scan_host(host);
goto out;
out_exit:
NCR5380_exit(host);
out_unmap:
iounmap(priv(host)->io);
unreg:
scsi_host_put(host);
release:
ecard_release_resources(ec);
out:
return ret;
}
static void oakscsi_remove(struct expansion_card *ec)
{
struct Scsi_Host *host = ecard_get_drvdata(ec);
void __iomem *base = priv(host)->io;
ecard_set_drvdata(ec, NULL);
scsi_remove_host(host);
NCR5380_exit(host);
scsi_host_put(host);
iounmap(base);
ecard_release_resources(ec);
}
static const struct ecard_id oakscsi_cids[] = {
{ MANU_OAK, PROD_OAK_SCSI },
{ 0xffff, 0xffff }
};
static struct ecard_driver oakscsi_driver = {
.probe = oakscsi_probe,
.remove = oakscsi_remove,
.id_table = oakscsi_cids,
.drv = {
.name = "oakscsi",
},
};
static int __init oakscsi_init(void)
{
return ecard_register_driver(&oakscsi_driver);
}
static void __exit oakscsi_exit(void)
{
ecard_remove_driver(&oakscsi_driver);
}
module_init(oakscsi_init);
module_exit(oakscsi_exit);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("Oak SCSI driver");
MODULE_LICENSE("GPL");