#include <linux/module.h>
MODULE_AUTHOR("Deanna Bonds, with _lots_ of help from Mark Salyzyn");
MODULE_DESCRIPTION("Adaptec I2O RAID Driver");
#include <linux/ioctl.h> /* For SCSI-Passthrough */
#include <linux/uaccess.h>
#include <linux/stat.h>
#include <linux/slab.h> /* for kmalloc() */
#include <linux/pci.h> /* for PCI support */
#include <linux/proc_fs.h>
#include <linux/blkdev.h>
#include <linux/delay.h> /* for udelay */
#include <linux/interrupt.h>
#include <linux/kernel.h> /* for printk */
#include <linux/sched.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/mutex.h>
#include <asm/processor.h> /* for boot_cpu_data */
#include <asm/pgtable.h>
#include <asm/io.h> /* for virt_to_bus, etc. */
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include "dpt/dptsig.h"
#include "dpti.h"
static DEFINE_MUTEX(adpt_mutex);
static dpt_sig_S DPTI_sig = {
{'d', 'P', 't', 'S', 'i', 'G'}, SIG_VERSION,
#ifdef __i386__
PROC_INTEL, PROC_386 | PROC_486 | PROC_PENTIUM | PROC_SEXIUM,
#elif defined(__ia64__)
PROC_INTEL, PROC_IA64,
#elif defined(__sparc__)
PROC_ULTRASPARC, PROC_ULTRASPARC,
#elif defined(__alpha__)
PROC_ALPHA, PROC_ALPHA,
#else
(-1),(-1),
#endif
FT_HBADRVR, 0, OEM_DPT, OS_LINUX, CAP_OVERLAP, DEV_ALL,
ADF_ALL_SC5, 0, 0, DPT_VERSION, DPT_REVISION, DPT_SUBREVISION,
DPT_MONTH, DPT_DAY, DPT_YEAR, "Adaptec Linux I2O RAID Driver"
};
static DEFINE_MUTEX(adpt_configuration_lock);
static struct i2o_sys_tbl *sys_tbl;
static dma_addr_t sys_tbl_pa;
static int sys_tbl_ind;
static int sys_tbl_len;
static adpt_hba* hba_chain = NULL;
static int hba_count = 0;
static struct class *adpt_sysfs_class;
static long adpt_unlocked_ioctl(struct file *, unsigned int, unsigned long);
#ifdef CONFIG_COMPAT
static long compat_adpt_ioctl(struct file *, unsigned int, unsigned long);
#endif
static const struct file_operations adpt_fops = {
.unlocked_ioctl = adpt_unlocked_ioctl,
.open = adpt_open,
.release = adpt_close,
#ifdef CONFIG_COMPAT
.compat_ioctl = compat_adpt_ioctl,
#endif
.llseek = noop_llseek,
};
struct adpt_i2o_post_wait_data
{
int status;
u32 id;
adpt_wait_queue_head_t *wq;
struct adpt_i2o_post_wait_data *next;
};
static struct adpt_i2o_post_wait_data *adpt_post_wait_queue = NULL;
static u32 adpt_post_wait_id = 0;
static DEFINE_SPINLOCK(adpt_post_wait_lock);
static inline int dpt_dma64(adpt_hba *pHba)
{
return (sizeof(dma_addr_t) > 4 && (pHba)->dma64);
}
static inline u32 dma_high(dma_addr_t addr)
{
return upper_32_bits(addr);
}
static inline u32 dma_low(dma_addr_t addr)
{
return (u32)addr;
}
static u8 adpt_read_blink_led(adpt_hba* host)
{
if (host->FwDebugBLEDflag_P) {
if( readb(host->FwDebugBLEDflag_P) == 0xbc ){
return readb(host->FwDebugBLEDvalue_P);
}
}
return 0;
}
#ifdef MODULE
static struct pci_device_id dptids[] = {
{ PCI_DPT_VENDOR_ID, PCI_DPT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
{ PCI_DPT_VENDOR_ID, PCI_DPT_RAPTOR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
{ 0, }
};
#endif
MODULE_DEVICE_TABLE(pci,dptids);
static int adpt_detect(struct scsi_host_template* sht)
{
struct pci_dev *pDev = NULL;
adpt_hba *pHba;
adpt_hba *next;
PINFO("Detecting Adaptec I2O RAID controllers...\n");
while ((pDev = pci_get_device( PCI_DPT_VENDOR_ID, PCI_ANY_ID, pDev))) {
if(pDev->device == PCI_DPT_DEVICE_ID ||
pDev->device == PCI_DPT_RAPTOR_DEVICE_ID){
if(adpt_install_hba(sht, pDev) ){
PERROR("Could not Init an I2O RAID device\n");
PERROR("Will not try to detect others.\n");
return hba_count-1;
}
pci_dev_get(pDev);
}
}
for (pHba = hba_chain; pHba; pHba = next) {
next = pHba->next;
if (adpt_i2o_activate_hba(pHba) < 0) {
adpt_i2o_delete_hba(pHba);
}
}
rebuild_sys_tab:
if (hba_chain == NULL)
return 0;
if (adpt_i2o_build_sys_table() < 0) {
adpt_i2o_sys_shutdown();
return 0;
}
PDEBUG("HBA's in HOLD state\n");
for (pHba = hba_chain; pHba; pHba = pHba->next) {
if (adpt_i2o_online_hba(pHba) < 0) {
adpt_i2o_delete_hba(pHba);
goto rebuild_sys_tab;
}
}
PDEBUG("HBA's in OPERATIONAL state\n");
printk("dpti: If you have a lot of devices this could take a few minutes.\n");
for (pHba = hba_chain; pHba; pHba = next) {
next = pHba->next;
printk(KERN_INFO"%s: Reading the hardware resource table.\n", pHba->name);
if (adpt_i2o_lct_get(pHba) < 0){
adpt_i2o_delete_hba(pHba);
continue;
}
if (adpt_i2o_parse_lct(pHba) < 0){
adpt_i2o_delete_hba(pHba);
continue;
}
adpt_inquiry(pHba);
}
adpt_sysfs_class = class_create(THIS_MODULE, "dpt_i2o");
if (IS_ERR(adpt_sysfs_class)) {
printk(KERN_WARNING"dpti: unable to create dpt_i2o class\n");
adpt_sysfs_class = NULL;
}
for (pHba = hba_chain; pHba; pHba = next) {
next = pHba->next;
if (adpt_scsi_host_alloc(pHba, sht) < 0){
adpt_i2o_delete_hba(pHba);
continue;
}
pHba->initialized = TRUE;
pHba->state &= ~DPTI_STATE_RESET;
if (adpt_sysfs_class) {
struct device *dev = device_create(adpt_sysfs_class,
NULL, MKDEV(DPTI_I2O_MAJOR, pHba->unit), NULL,
"dpti%d", pHba->unit);
if (IS_ERR(dev)) {
printk(KERN_WARNING"dpti%d: unable to "
"create device in dpt_i2o class\n",
pHba->unit);
}
}
}
if (hba_count && register_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER, &adpt_fops)) {
adpt_i2o_sys_shutdown();
return 0;
}
return hba_count;
}
static void adpt_release(adpt_hba *pHba)
{
struct Scsi_Host *shost = pHba->host;
scsi_remove_host(shost);
adpt_i2o_delete_hba(pHba);
scsi_host_put(shost);
}
static void adpt_inquiry(adpt_hba* pHba)
{
u32 msg[17];
u32 *mptr;
u32 *lenptr;
int direction;
int scsidir;
u32 len;
u32 reqlen;
u8* buf;
dma_addr_t addr;
u8 scb[16];
s32 rcode;
memset(msg, 0, sizeof(msg));
buf = dma_alloc_coherent(&pHba->pDev->dev, 80, &addr, GFP_KERNEL);
if(!buf){
printk(KERN_ERR"%s: Could not allocate buffer\n",pHba->name);
return;
}
memset((void*)buf, 0, 36);
len = 36;
direction = 0x00000000;
scsidir =0x40000000;
if (dpt_dma64(pHba))
reqlen = 17;
else
reqlen = 14;
msg[0] = reqlen<<16 | SGL_OFFSET_12;
msg[1] = (0xff<<24|HOST_TID<<12|ADAPTER_TID);
msg[2] = 0;
msg[3] = 0;
msg[4] = I2O_CMD_SCSI_EXEC|DPT_ORGANIZATION_ID<<16;
msg[5] = ADAPTER_TID | 1<<16 ;
msg[6] = scsidir|0x20a00000| 6 ;
mptr=msg+7;
memset(scb, 0, sizeof(scb));
scb[0] = INQUIRY;
scb[1] = 0;
scb[2] = 0;
scb[3] = 0;
scb[4] = 36;
scb[5] = 0;
memcpy(mptr, scb, sizeof(scb));
mptr+=4;
lenptr=mptr++;
*lenptr = len;
if (dpt_dma64(pHba)) {
*mptr++ = (0x7C<<24)+(2<<16)+0x02;
*mptr++ = 1 << PAGE_SHIFT;
*mptr++ = 0xD0000000|direction|len;
*mptr++ = dma_low(addr);
*mptr++ = dma_high(addr);
} else {
*mptr++ = 0xD0000000|direction|len;
*mptr++ = addr;
}
rcode = adpt_i2o_post_wait(pHba, msg, reqlen<<2, 120);
if (rcode != 0) {
sprintf(pHba->detail, "Adaptec I2O RAID");
printk(KERN_INFO "%s: Inquiry Error (%d)\n",pHba->name,rcode);
if (rcode != -ETIME && rcode != -EINTR)
dma_free_coherent(&pHba->pDev->dev, 80, buf, addr);
} else {
memset(pHba->detail, 0, sizeof(pHba->detail));
memcpy(&(pHba->detail), "Vendor: Adaptec ", 16);
memcpy(&(pHba->detail[16]), " Model: ", 8);
memcpy(&(pHba->detail[24]), (u8*) &buf[16], 16);
memcpy(&(pHba->detail[40]), " FW: ", 4);
memcpy(&(pHba->detail[44]), (u8*) &buf[32], 4);
pHba->detail[48] = '\0';
dma_free_coherent(&pHba->pDev->dev, 80, buf, addr);
}
adpt_i2o_status_get(pHba);
return ;
}
static int adpt_slave_configure(struct scsi_device * device)
{
struct Scsi_Host *host = device->host;
adpt_hba* pHba;
pHba = (adpt_hba *) host->hostdata[0];
if (host->can_queue && device->tagged_supported) {
scsi_change_queue_depth(device,
host->can_queue - 1);
}
return 0;
}
static int adpt_queue_lck(struct scsi_cmnd * cmd, void (*done) (struct scsi_cmnd *))
{
adpt_hba* pHba = NULL;
struct adpt_device* pDev = NULL;
cmd->scsi_done = done;
if ((cmd->cmnd[0] == REQUEST_SENSE) && (cmd->sense_buffer[0] != 0)) {
cmd->result = (DID_OK << 16);
cmd->scsi_done(cmd);
return 0;
}
pHba = (adpt_hba*)cmd->device->host->hostdata[0];
if (!pHba) {
return FAILED;
}
rmb();
if ((pHba->state) & DPTI_STATE_RESET)
return SCSI_MLQUEUE_HOST_BUSY;
if((pDev = (struct adpt_device*) (cmd->device->hostdata)) == NULL) {
if ((pDev = adpt_find_device(pHba, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun)) == NULL) {
cmd->result = (DID_NO_CONNECT << 16);
cmd->scsi_done(cmd);
return 0;
}
cmd->device->hostdata = pDev;
}
pDev->pScsi_dev = cmd->device;
if (pDev->state & DPTI_DEV_RESET ) {
return FAILED;
}
return adpt_scsi_to_i2o(pHba, cmd, pDev);
}
static DEF_SCSI_QCMD(adpt_queue)
static int adpt_bios_param(struct scsi_device *sdev, struct block_device *dev,
sector_t capacity, int geom[])
{
int heads=-1;
int sectors=-1;
int cylinders=-1;
if (capacity < 0x2000 ) {
heads = 18;
sectors = 2;
}
else if (capacity < 0x20000) {
heads = 64;
sectors = 32;
}
else if (capacity < 0x40000) {
heads = 65;
sectors = 63;
}
else if (capacity < 0x80000) {
heads = 128;
sectors = 63;
}
else {
heads = 255;
sectors = 63;
}
cylinders = sector_div(capacity, heads * sectors);
if(sdev->type == 5) {
heads = 252;
sectors = 63;
cylinders = 1111;
}
geom[0] = heads;
geom[1] = sectors;
geom[2] = cylinders;
PDEBUG("adpt_bios_param: exit\n");
return 0;
}
static const char *adpt_info(struct Scsi_Host *host)
{
adpt_hba* pHba;
pHba = (adpt_hba *) host->hostdata[0];
return (char *) (pHba->detail);
}
static int adpt_show_info(struct seq_file *m, struct Scsi_Host *host)
{
struct adpt_device* d;
int id;
int chan;
adpt_hba* pHba;
int unit;
mutex_lock(&adpt_configuration_lock);
for (pHba = hba_chain; pHba; pHba = pHba->next) {
if (pHba->host == host) {
break;
}
}
mutex_unlock(&adpt_configuration_lock);
if (pHba == NULL) {
return 0;
}
host = pHba->host;
seq_printf(m, "Adaptec I2O RAID Driver Version: %s\n\n", DPT_I2O_VERSION);
seq_printf(m, "%s\n", pHba->detail);
seq_printf(m, "SCSI Host=scsi%d Control Node=/dev/%s irq=%d\n",
pHba->host->host_no, pHba->name, host->irq);
seq_printf(m, "\tpost fifo size = %d\n\treply fifo size = %d\n\tsg table size = %d\n\n",
host->can_queue, (int) pHba->reply_fifo_size , host->sg_tablesize);
seq_puts(m, "Devices:\n");
for(chan = 0; chan < MAX_CHANNEL; chan++) {
for(id = 0; id < MAX_ID; id++) {
d = pHba->channel[chan].device[id];
while(d) {
seq_printf(m,"\t%-24.24s", d->pScsi_dev->vendor);
seq_printf(m," Rev: %-8.8s\n", d->pScsi_dev->rev);
unit = d->pI2o_dev->lct_data.tid;
seq_printf(m, "\tTID=%d, (Channel=%d, Target=%d, Lun=%llu) (%s)\n\n",
unit, (int)d->scsi_channel, (int)d->scsi_id, d->scsi_lun,
scsi_device_online(d->pScsi_dev)? "online":"offline");
d = d->next_lun;
}
}
}
return 0;
}
static u32 adpt_ioctl_to_context(adpt_hba * pHba, void *reply)
{
#if BITS_PER_LONG == 32
return (u32)(unsigned long)reply;
#else
ulong flags = 0;
u32 nr, i;
spin_lock_irqsave(pHba->host->host_lock, flags);
nr = ARRAY_SIZE(pHba->ioctl_reply_context);
for (i = 0; i < nr; i++) {
if (pHba->ioctl_reply_context[i] == NULL) {
pHba->ioctl_reply_context[i] = reply;
break;
}
}
spin_unlock_irqrestore(pHba->host->host_lock, flags);
if (i >= nr) {
printk(KERN_WARNING"%s: Too many outstanding "
"ioctl commands\n", pHba->name);
return (u32)-1;
}
return i;
#endif
}
static void *adpt_ioctl_from_context(adpt_hba *pHba, u32 context)
{
#if BITS_PER_LONG == 32
return (void *)(unsigned long)context;
#else
void *p = pHba->ioctl_reply_context[context];
pHba->ioctl_reply_context[context] = NULL;
return p;
#endif
}
static int adpt_abort(struct scsi_cmnd * cmd)
{
adpt_hba* pHba = NULL;
struct adpt_device* dptdevice;
u32 msg[5];
int rcode;
pHba = (adpt_hba*) cmd->device->host->hostdata[0];
printk(KERN_INFO"%s: Trying to Abort\n",pHba->name);
if ((dptdevice = (void*) (cmd->device->hostdata)) == NULL) {
printk(KERN_ERR "%s: Unable to abort: No device in cmnd\n",pHba->name);
return FAILED;
}
memset(msg, 0, sizeof(msg));
msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|dptdevice->tid;
msg[2] = 0;
msg[3]= 0;
msg[4] = cmd->request->tag + 1;
if (pHba->host)
spin_lock_irq(pHba->host->host_lock);
rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER);
if (pHba->host)
spin_unlock_irq(pHba->host->host_lock);
if (rcode != 0) {
if(rcode == -EOPNOTSUPP ){
printk(KERN_INFO"%s: Abort cmd not supported\n",pHba->name);
return FAILED;
}
printk(KERN_INFO"%s: Abort failed.\n",pHba->name);
return FAILED;
}
printk(KERN_INFO"%s: Abort complete.\n",pHba->name);
return SUCCESS;
}
#define I2O_DEVICE_RESET 0x27
static int adpt_device_reset(struct scsi_cmnd* cmd)
{
adpt_hba* pHba;
u32 msg[4];
u32 rcode;
int old_state;
struct adpt_device* d = cmd->device->hostdata;
pHba = (void*) cmd->device->host->hostdata[0];
printk(KERN_INFO"%s: Trying to reset device\n",pHba->name);
if (!d) {
printk(KERN_INFO"%s: Reset Device: Device Not found\n",pHba->name);
return FAILED;
}
memset(msg, 0, sizeof(msg));
msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
msg[1] = (I2O_DEVICE_RESET<<24|HOST_TID<<12|d->tid);
msg[2] = 0;
msg[3] = 0;
if (pHba->host)
spin_lock_irq(pHba->host->host_lock);
old_state = d->state;
d->state |= DPTI_DEV_RESET;
rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER);
d->state = old_state;
if (pHba->host)
spin_unlock_irq(pHba->host->host_lock);
if (rcode != 0) {
if(rcode == -EOPNOTSUPP ){
printk(KERN_INFO"%s: Device reset not supported\n",pHba->name);
return FAILED;
}
printk(KERN_INFO"%s: Device reset failed\n",pHba->name);
return FAILED;
} else {
printk(KERN_INFO"%s: Device reset successful\n",pHba->name);
return SUCCESS;
}
}
#define I2O_HBA_BUS_RESET 0x87
static int adpt_bus_reset(struct scsi_cmnd* cmd)
{
adpt_hba* pHba;
u32 msg[4];
u32 rcode;
pHba = (adpt_hba*)cmd->device->host->hostdata[0];
memset(msg, 0, sizeof(msg));
printk(KERN_WARNING"%s: Bus reset: SCSI Bus %d: tid: %d\n",pHba->name, cmd->device->channel,pHba->channel[cmd->device->channel].tid );
msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
msg[1] = (I2O_HBA_BUS_RESET<<24|HOST_TID<<12|pHba->channel[cmd->device->channel].tid);
msg[2] = 0;
msg[3] = 0;
if (pHba->host)
spin_lock_irq(pHba->host->host_lock);
rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER);
if (pHba->host)
spin_unlock_irq(pHba->host->host_lock);
if (rcode != 0) {
printk(KERN_WARNING"%s: Bus reset failed.\n",pHba->name);
return FAILED;
} else {
printk(KERN_WARNING"%s: Bus reset success.\n",pHba->name);
return SUCCESS;
}
}
static int __adpt_reset(struct scsi_cmnd* cmd)
{
adpt_hba* pHba;
int rcode;
char name[32];
pHba = (adpt_hba*)cmd->device->host->hostdata[0];
strncpy(name, pHba->name, sizeof(name));
printk(KERN_WARNING"%s: Hba Reset: scsi id %d: tid: %d\n", name, cmd->device->channel, pHba->channel[cmd->device->channel].tid);
rcode = adpt_hba_reset(pHba);
if(rcode == 0){
printk(KERN_WARNING"%s: HBA reset complete\n", name);
return SUCCESS;
} else {
printk(KERN_WARNING"%s: HBA reset failed (%x)\n", name, rcode);
return FAILED;
}
}
static int adpt_reset(struct scsi_cmnd* cmd)
{
int rc;
spin_lock_irq(cmd->device->host->host_lock);
rc = __adpt_reset(cmd);
spin_unlock_irq(cmd->device->host->host_lock);
return rc;
}
static int adpt_hba_reset(adpt_hba* pHba)
{
int rcode;
pHba->state |= DPTI_STATE_RESET;
if ((rcode=adpt_i2o_activate_hba(pHba)) < 0) {
printk(KERN_ERR "%s: Could not activate\n", pHba->name);
adpt_i2o_delete_hba(pHba);
return rcode;
}
if ((rcode=adpt_i2o_build_sys_table()) < 0) {
adpt_i2o_delete_hba(pHba);
return rcode;
}
PDEBUG("%s: in HOLD state\n",pHba->name);
if ((rcode=adpt_i2o_online_hba(pHba)) < 0) {
adpt_i2o_delete_hba(pHba);
return rcode;
}
PDEBUG("%s: in OPERATIONAL state\n",pHba->name);
if ((rcode=adpt_i2o_lct_get(pHba)) < 0){
adpt_i2o_delete_hba(pHba);
return rcode;
}
if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0){
adpt_i2o_delete_hba(pHba);
return rcode;
}
pHba->state &= ~DPTI_STATE_RESET;
adpt_fail_posted_scbs(pHba);
return 0;
}
static void adpt_i2o_sys_shutdown(void)
{
adpt_hba *pHba, *pNext;
struct adpt_i2o_post_wait_data *p1, *old;
printk(KERN_INFO "Shutting down Adaptec I2O controllers.\n");
printk(KERN_INFO " This could take a few minutes if there are many devices attached\n");
for (pHba = hba_chain; pHba; pHba = pNext) {
pNext = pHba->next;
adpt_i2o_delete_hba(pHba);
}
for(p1 = adpt_post_wait_queue; p1;) {
old = p1;
p1 = p1->next;
kfree(old);
}
adpt_post_wait_queue = NULL;
printk(KERN_INFO "Adaptec I2O controllers down.\n");
}
static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev)
{
adpt_hba* pHba = NULL;
adpt_hba* p = NULL;
ulong base_addr0_phys = 0;
ulong base_addr1_phys = 0;
u32 hba_map0_area_size = 0;
u32 hba_map1_area_size = 0;
void __iomem *base_addr_virt = NULL;
void __iomem *msg_addr_virt = NULL;
int dma64 = 0;
int raptorFlag = FALSE;
if(pci_enable_device(pDev)) {
return -EINVAL;
}
if (pci_request_regions(pDev, "dpt_i2o")) {
PERROR("dpti: adpt_config_hba: pci request region failed\n");
return -EINVAL;
}
pci_set_master(pDev);
if (sizeof(dma_addr_t) > 4 &&
dma_get_required_mask(&pDev->dev) > DMA_BIT_MASK(32) &&
dma_set_mask(&pDev->dev, DMA_BIT_MASK(64)) == 0)
dma64 = 1;
if (!dma64 && dma_set_mask(&pDev->dev, DMA_BIT_MASK(32)) != 0)
return -EINVAL;
dma_set_coherent_mask(&pDev->dev, DMA_BIT_MASK(32));
base_addr0_phys = pci_resource_start(pDev,0);
hba_map0_area_size = pci_resource_len(pDev,0);
if(pDev->device == PCI_DPT_DEVICE_ID){
if(pDev->subsystem_device >=0xc032 && pDev->subsystem_device <= 0xc03b){
hba_map0_area_size = 0x400000;
} else {
if(hba_map0_area_size > 0x100000 ){
hba_map0_area_size = 0x100000;
}
}
} else {
base_addr1_phys = pci_resource_start(pDev,1);
hba_map1_area_size = pci_resource_len(pDev,1);
raptorFlag = TRUE;
}
#if BITS_PER_LONG == 64
if (raptorFlag == TRUE) {
if (hba_map0_area_size > 128)
hba_map0_area_size = 128;
if (hba_map1_area_size > 524288)
hba_map1_area_size = 524288;
} else {
if (hba_map0_area_size > 524288)
hba_map0_area_size = 524288;
}
#endif
base_addr_virt = ioremap(base_addr0_phys,hba_map0_area_size);
if (!base_addr_virt) {
pci_release_regions(pDev);
PERROR("dpti: adpt_config_hba: io remap failed\n");
return -EINVAL;
}
if(raptorFlag == TRUE) {
msg_addr_virt = ioremap(base_addr1_phys, hba_map1_area_size );
if (!msg_addr_virt) {
PERROR("dpti: adpt_config_hba: io remap failed on BAR1\n");
iounmap(base_addr_virt);
pci_release_regions(pDev);
return -EINVAL;
}
} else {
msg_addr_virt = base_addr_virt;
}
pHba = kzalloc(sizeof(adpt_hba), GFP_KERNEL);
if (!pHba) {
if (msg_addr_virt != base_addr_virt)
iounmap(msg_addr_virt);
iounmap(base_addr_virt);
pci_release_regions(pDev);
return -ENOMEM;
}
mutex_lock(&adpt_configuration_lock);
if(hba_chain != NULL){
for(p = hba_chain; p->next; p = p->next);
p->next = pHba;
} else {
hba_chain = pHba;
}
pHba->next = NULL;
pHba->unit = hba_count;
sprintf(pHba->name, "dpti%d", hba_count);
hba_count++;
mutex_unlock(&adpt_configuration_lock);
pHba->pDev = pDev;
pHba->base_addr_phys = base_addr0_phys;
pHba->base_addr_virt = base_addr_virt;
pHba->msg_addr_virt = msg_addr_virt;
pHba->irq_mask = base_addr_virt+0x30;
pHba->post_port = base_addr_virt+0x40;
pHba->reply_port = base_addr_virt+0x44;
pHba->hrt = NULL;
pHba->lct = NULL;
pHba->lct_size = 0;
pHba->status_block = NULL;
pHba->post_count = 0;
pHba->state = DPTI_STATE_RESET;
pHba->pDev = pDev;
pHba->devices = NULL;
pHba->dma64 = dma64;
spin_lock_init(&pHba->state_lock);
spin_lock_init(&adpt_post_wait_lock);
if(raptorFlag == 0){
printk(KERN_INFO "Adaptec I2O RAID controller"
" %d at %p size=%x irq=%d%s\n",
hba_count-1, base_addr_virt,
hba_map0_area_size, pDev->irq,
dma64 ? " (64-bit DMA)" : "");
} else {
printk(KERN_INFO"Adaptec I2O RAID controller %d irq=%d%s\n",
hba_count-1, pDev->irq,
dma64 ? " (64-bit DMA)" : "");
printk(KERN_INFO" BAR0 %p - size= %x\n",base_addr_virt,hba_map0_area_size);
printk(KERN_INFO" BAR1 %p - size= %x\n",msg_addr_virt,hba_map1_area_size);
}
if (request_irq (pDev->irq, adpt_isr, IRQF_SHARED, pHba->name, pHba)) {
printk(KERN_ERR"%s: Couldn't register IRQ %d\n", pHba->name, pDev->irq);
adpt_i2o_delete_hba(pHba);
return -EINVAL;
}
return 0;
}
static void adpt_i2o_delete_hba(adpt_hba* pHba)
{
adpt_hba* p1;
adpt_hba* p2;
struct i2o_device* d;
struct i2o_device* next;
int i;
int j;
struct adpt_device* pDev;
struct adpt_device* pNext;
mutex_lock(&adpt_configuration_lock);
if(pHba->host){
free_irq(pHba->host->irq, pHba);
}
p2 = NULL;
for( p1 = hba_chain; p1; p2 = p1,p1=p1->next){
if(p1 == pHba) {
if(p2) {
p2->next = p1->next;
} else {
hba_chain = p1->next;
}
break;
}
}
hba_count--;
mutex_unlock(&adpt_configuration_lock);
iounmap(pHba->base_addr_virt);
pci_release_regions(pHba->pDev);
if(pHba->msg_addr_virt != pHba->base_addr_virt){
iounmap(pHba->msg_addr_virt);
}
if(pHba->FwDebugBuffer_P)
iounmap(pHba->FwDebugBuffer_P);
if(pHba->hrt) {
dma_free_coherent(&pHba->pDev->dev,
pHba->hrt->num_entries * pHba->hrt->entry_len << 2,
pHba->hrt, pHba->hrt_pa);
}
if(pHba->lct) {
dma_free_coherent(&pHba->pDev->dev, pHba->lct_size,
pHba->lct, pHba->lct_pa);
}
if(pHba->status_block) {
dma_free_coherent(&pHba->pDev->dev, sizeof(i2o_status_block),
pHba->status_block, pHba->status_block_pa);
}
if(pHba->reply_pool) {
dma_free_coherent(&pHba->pDev->dev,
pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4,
pHba->reply_pool, pHba->reply_pool_pa);
}
for(d = pHba->devices; d ; d = next){
next = d->next;
kfree(d);
}
for(i = 0 ; i < pHba->top_scsi_channel ; i++){
for(j = 0; j < MAX_ID; j++){
if(pHba->channel[i].device[j] != NULL){
for(pDev = pHba->channel[i].device[j]; pDev; pDev = pNext){
pNext = pDev->next_lun;
kfree(pDev);
}
}
}
}
pci_dev_put(pHba->pDev);
if (adpt_sysfs_class)
device_destroy(adpt_sysfs_class,
MKDEV(DPTI_I2O_MAJOR, pHba->unit));
kfree(pHba);
if(hba_count <= 0){
unregister_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER);
if (adpt_sysfs_class) {
class_destroy(adpt_sysfs_class);
adpt_sysfs_class = NULL;
}
}
}
static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u64 lun)
{
struct adpt_device* d;
if(chan < 0 || chan >= MAX_CHANNEL)
return NULL;
d = pHba->channel[chan].device[id];
if(!d || d->tid == 0) {
return NULL;
}
if(d->scsi_lun == lun){
return d;
}
for(d=d->next_lun ; d ; d = d->next_lun){
if(d->scsi_lun == lun){
return d;
}
}
return NULL;
}
static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout)
{
ADPT_DECLARE_WAIT_QUEUE_HEAD(adpt_wq_i2o_post);
int status = 0;
ulong flags = 0;
struct adpt_i2o_post_wait_data *p1, *p2;
struct adpt_i2o_post_wait_data *wait_data =
kmalloc(sizeof(struct adpt_i2o_post_wait_data), GFP_ATOMIC);
DECLARE_WAITQUEUE(wait, current);
if (!wait_data)
return -ENOMEM;
spin_lock_irqsave(&adpt_post_wait_lock, flags);
wait_data->next = adpt_post_wait_queue;
adpt_post_wait_queue = wait_data;
adpt_post_wait_id++;
adpt_post_wait_id &= 0x7fff;
wait_data->id = adpt_post_wait_id;
spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
wait_data->wq = &adpt_wq_i2o_post;
wait_data->status = -ETIMEDOUT;
add_wait_queue(&adpt_wq_i2o_post, &wait);
msg[2] |= 0x80000000 | ((u32)wait_data->id);
timeout *= HZ;
if((status = adpt_i2o_post_this(pHba, msg, len)) == 0){
set_current_state(TASK_INTERRUPTIBLE);
if(pHba->host)
spin_unlock_irq(pHba->host->host_lock);
if (!timeout)
schedule();
else{
timeout = schedule_timeout(timeout);
if (timeout == 0) {
status = -ETIME;
}
}
if(pHba->host)
spin_lock_irq(pHba->host->host_lock);
}
remove_wait_queue(&adpt_wq_i2o_post, &wait);
if(status == -ETIMEDOUT){
printk(KERN_INFO"dpti%d: POST WAIT TIMEOUT\n",pHba->unit);
return status;
}
p2 = NULL;
spin_lock_irqsave(&adpt_post_wait_lock, flags);
for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p1->next) {
if(p1 == wait_data) {
if(p1->status == I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION ) {
status = -EOPNOTSUPP;
}
if(p2) {
p2->next = p1->next;
} else {
adpt_post_wait_queue = p1->next;
}
break;
}
}
spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
kfree(wait_data);
return status;
}
static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len)
{
u32 m = EMPTY_QUEUE;
u32 __iomem *msg;
ulong timeout = jiffies + 30*HZ;
do {
rmb();
m = readl(pHba->post_port);
if (m != EMPTY_QUEUE) {
break;
}
if(time_after(jiffies,timeout)){
printk(KERN_WARNING"dpti%d: Timeout waiting for message frame!\n", pHba->unit);
return -ETIMEDOUT;
}
schedule_timeout_uninterruptible(1);
} while(m == EMPTY_QUEUE);
msg = pHba->msg_addr_virt + m;
memcpy_toio(msg, data, len);
wmb();
writel(m, pHba->post_port);
wmb();
return 0;
}
static void adpt_i2o_post_wait_complete(u32 context, int status)
{
struct adpt_i2o_post_wait_data *p1 = NULL;
context &= 0x7fff;
spin_lock(&adpt_post_wait_lock);
for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) {
if(p1->id == context) {
p1->status = status;
spin_unlock(&adpt_post_wait_lock);
wake_up_interruptible(p1->wq);
return;
}
}
spin_unlock(&adpt_post_wait_lock);
printk(KERN_DEBUG"dpti: Could Not find task %d in wait queue\n",context);
printk(KERN_DEBUG" Tasks in wait queue:\n");
for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) {
printk(KERN_DEBUG" %d\n",p1->id);
}
return;
}
static s32 adpt_i2o_reset_hba(adpt_hba* pHba)
{
u32 msg[8];
u8* status;
dma_addr_t addr;
u32 m = EMPTY_QUEUE ;
ulong timeout = jiffies + (TMOUT_IOPRESET*HZ);
if(pHba->initialized == FALSE) {
timeout = jiffies + (25*HZ);
} else {
adpt_i2o_quiesce_hba(pHba);
}
do {
rmb();
m = readl(pHba->post_port);
if (m != EMPTY_QUEUE) {
break;
}
if(time_after(jiffies,timeout)){
printk(KERN_WARNING"Timeout waiting for message!\n");
return -ETIMEDOUT;
}
schedule_timeout_uninterruptible(1);
} while (m == EMPTY_QUEUE);
status = dma_alloc_coherent(&pHba->pDev->dev, 4, &addr, GFP_KERNEL);
if(status == NULL) {
adpt_send_nop(pHba, m);
printk(KERN_ERR"IOP reset failed - no free memory.\n");
return -ENOMEM;
}
memset(status,0,4);
msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0;
msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID;
msg[2]=0;
msg[3]=0;
msg[4]=0;
msg[5]=0;
msg[6]=dma_low(addr);
msg[7]=dma_high(addr);
memcpy_toio(pHba->msg_addr_virt+m, msg, sizeof(msg));
wmb();
writel(m, pHba->post_port);
wmb();
while(*status == 0){
if(time_after(jiffies,timeout)){
printk(KERN_WARNING"%s: IOP Reset Timeout\n",pHba->name);
return -ETIMEDOUT;
}
rmb();
schedule_timeout_uninterruptible(1);
}
if(*status == 0x01 ) {
PDEBUG("%s: Reset in progress...\n", pHba->name);
do {
rmb();
m = readl(pHba->post_port);
if (m != EMPTY_QUEUE) {
break;
}
if(time_after(jiffies,timeout)){
printk(KERN_ERR "%s:Timeout waiting for IOP Reset.\n",pHba->name);
return -ETIMEDOUT;
}
schedule_timeout_uninterruptible(1);
} while (m == EMPTY_QUEUE);
adpt_send_nop(pHba, m);
}
adpt_i2o_status_get(pHba);
if(*status == 0x02 ||
pHba->status_block->iop_state != ADAPTER_STATE_RESET) {
printk(KERN_WARNING"%s: Reset reject, trying to clear\n",
pHba->name);
} else {
PDEBUG("%s: Reset completed.\n", pHba->name);
}
dma_free_coherent(&pHba->pDev->dev, 4, status, addr);
#ifdef UARTDELAY
adpt_delay(20000);
#endif
return 0;
}
static int adpt_i2o_parse_lct(adpt_hba* pHba)
{
int i;
int max;
int tid;
struct i2o_device *d;
i2o_lct *lct = pHba->lct;
u8 bus_no = 0;
s16 scsi_id;
u64 scsi_lun;
u32 buf[10];
struct adpt_device* pDev;
if (lct == NULL) {
printk(KERN_ERR "%s: LCT is empty???\n",pHba->name);
return -1;
}
max = lct->table_size;
max -= 3;
max /= 9;
for(i=0;i<max;i++) {
if( lct->lct_entry[i].user_tid != 0xfff){
if( lct->lct_entry[i].class_id != I2O_CLASS_RANDOM_BLOCK_STORAGE &&
lct->lct_entry[i].class_id != I2O_CLASS_SCSI_PERIPHERAL &&
lct->lct_entry[i].class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
continue;
}
tid = lct->lct_entry[i].tid;
if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) {
continue;
}
bus_no = buf[0]>>16;
scsi_id = buf[1];
scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]);
if(bus_no >= MAX_CHANNEL) {
printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no);
continue;
}
if (scsi_id >= MAX_ID){
printk(KERN_WARNING"%s: SCSI ID %d out of range \n", pHba->name, bus_no);
continue;
}
if(bus_no > pHba->top_scsi_channel){
pHba->top_scsi_channel = bus_no;
}
if(scsi_id > pHba->top_scsi_id){
pHba->top_scsi_id = scsi_id;
}
if(scsi_lun > pHba->top_scsi_lun){
pHba->top_scsi_lun = scsi_lun;
}
continue;
}
d = kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
if(d==NULL)
{
printk(KERN_CRIT"%s: Out of memory for I2O device data.\n",pHba->name);
return -ENOMEM;
}
d->controller = pHba;
d->next = NULL;
memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
d->flags = 0;
tid = d->lct_data.tid;
adpt_i2o_report_hba_unit(pHba, d);
adpt_i2o_install_device(pHba, d);
}
bus_no = 0;
for(d = pHba->devices; d ; d = d->next) {
if(d->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT ||
d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PORT){
tid = d->lct_data.tid;
if(bus_no > pHba->top_scsi_channel){
pHba->top_scsi_channel = bus_no;
}
pHba->channel[bus_no].type = d->lct_data.class_id;
pHba->channel[bus_no].tid = tid;
if(adpt_i2o_query_scalar(pHba, tid, 0x0200, -1, buf, 28)>=0)
{
pHba->channel[bus_no].scsi_id = buf[1];
PDEBUG("Bus %d - SCSI ID %d.\n", bus_no, buf[1]);
}
bus_no++;
if(bus_no >= MAX_CHANNEL) {
printk(KERN_WARNING"%s: Channel number %d out of range - LCT\n", pHba->name, bus_no);
break;
}
}
}
for(d = pHba->devices; d ; d = d->next) {
if(d->lct_data.class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE ||
d->lct_data.class_id == I2O_CLASS_SCSI_PERIPHERAL ||
d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
tid = d->lct_data.tid;
scsi_id = -1;
if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)>=0) {
bus_no = buf[0]>>16;
scsi_id = buf[1];
scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]);
if(bus_no >= MAX_CHANNEL) {
continue;
}
if (scsi_id >= MAX_ID) {
continue;
}
if( pHba->channel[bus_no].device[scsi_id] == NULL){
pDev = kzalloc(sizeof(struct adpt_device),GFP_KERNEL);
if(pDev == NULL) {
return -ENOMEM;
}
pHba->channel[bus_no].device[scsi_id] = pDev;
} else {
for( pDev = pHba->channel[bus_no].device[scsi_id];
pDev->next_lun; pDev = pDev->next_lun){
}
pDev->next_lun = kzalloc(sizeof(struct adpt_device),GFP_KERNEL);
if(pDev->next_lun == NULL) {
return -ENOMEM;
}
pDev = pDev->next_lun;
}
pDev->tid = tid;
pDev->scsi_channel = bus_no;
pDev->scsi_id = scsi_id;
pDev->scsi_lun = scsi_lun;
pDev->pI2o_dev = d;
d->owner = pDev;
pDev->type = (buf[0])&0xff;
pDev->flags = (buf[0]>>8)&0xff;
if(scsi_id > pHba->top_scsi_id){
pHba->top_scsi_id = scsi_id;
}
if(scsi_lun > pHba->top_scsi_lun){
pHba->top_scsi_lun = scsi_lun;
}
}
if(scsi_id == -1){
printk(KERN_WARNING"Could not find SCSI ID for %s\n",
d->lct_data.identity_tag);
}
}
}
return 0;
}
static int adpt_i2o_install_device(adpt_hba* pHba, struct i2o_device *d)
{
mutex_lock(&adpt_configuration_lock);
d->controller=pHba;
d->owner=NULL;
d->next=pHba->devices;
d->prev=NULL;
if (pHba->devices != NULL){
pHba->devices->prev=d;
}
pHba->devices=d;
*d->dev_name = 0;
mutex_unlock(&adpt_configuration_lock);
return 0;
}
static int adpt_open(struct inode *inode, struct file *file)
{
int minor;
adpt_hba* pHba;
mutex_lock(&adpt_mutex);
minor = iminor(inode);
if (minor >= hba_count) {
mutex_unlock(&adpt_mutex);
return -ENXIO;
}
mutex_lock(&adpt_configuration_lock);
for (pHba = hba_chain; pHba; pHba = pHba->next) {
if (pHba->unit == minor) {
break;
}
}
if (pHba == NULL) {
mutex_unlock(&adpt_configuration_lock);
mutex_unlock(&adpt_mutex);
return -ENXIO;
}
pHba->in_use = 1;
mutex_unlock(&adpt_configuration_lock);
mutex_unlock(&adpt_mutex);
return 0;
}
static int adpt_close(struct inode *inode, struct file *file)
{
int minor;
adpt_hba* pHba;
minor = iminor(inode);
if (minor >= hba_count) {
return -ENXIO;
}
mutex_lock(&adpt_configuration_lock);
for (pHba = hba_chain; pHba; pHba = pHba->next) {
if (pHba->unit == minor) {
break;
}
}
mutex_unlock(&adpt_configuration_lock);
if (pHba == NULL) {
return -ENXIO;
}
pHba->in_use = 0;
return 0;
}
static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg)
{
u32 msg[MAX_MESSAGE_SIZE];
u32* reply = NULL;
u32 size = 0;
u32 reply_size = 0;
u32 __user *user_msg = arg;
u32 __user * user_reply = NULL;
void **sg_list = NULL;
u32 sg_offset = 0;
u32 sg_count = 0;
int sg_index = 0;
u32 i = 0;
u32 rcode = 0;
void *p = NULL;
dma_addr_t addr;
ulong flags = 0;
memset(&msg, 0, MAX_MESSAGE_SIZE*4);
if(get_user(size, &user_msg[0])){
return -EFAULT;
}
size = size>>16;
user_reply = &user_msg[size];
if(size > MAX_MESSAGE_SIZE){
return -EFAULT;
}
size *= 4;
if(copy_from_user(msg, user_msg, size)) {
return -EFAULT;
}
get_user(reply_size, &user_reply[0]);
reply_size = reply_size>>16;
if(reply_size > REPLY_FRAME_SIZE){
reply_size = REPLY_FRAME_SIZE;
}
reply_size *= 4;
reply = kzalloc(REPLY_FRAME_SIZE*4, GFP_KERNEL);
if(reply == NULL) {
printk(KERN_WARNING"%s: Could not allocate reply buffer\n",pHba->name);
return -ENOMEM;
}
sg_offset = (msg[0]>>4)&0xf;
msg[2] = 0x40000000;
msg[3] = adpt_ioctl_to_context(pHba, reply);
if (msg[3] == (u32)-1) {
rcode = -EBUSY;
goto free;
}
sg_list = kcalloc(pHba->sg_tablesize, sizeof(*sg_list), GFP_KERNEL);
if (!sg_list) {
rcode = -ENOMEM;
goto free;
}
if(sg_offset) {
struct sg_simple_element *sg = (struct sg_simple_element*) (msg+sg_offset);
sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
if (sg_count > pHba->sg_tablesize){
printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", pHba->name,sg_count);
rcode = -EINVAL;
goto free;
}
for(i = 0; i < sg_count; i++) {
int sg_size;
if (!(sg[i].flag_count & 0x10000000 )) {
printk(KERN_DEBUG"%s:Bad SG element %d - not simple (%x)\n",pHba->name,i, sg[i].flag_count);
rcode = -EINVAL;
goto cleanup;
}
sg_size = sg[i].flag_count & 0xffffff;
p = dma_alloc_coherent(&pHba->pDev->dev, sg_size, &addr, GFP_KERNEL);
if(!p) {
printk(KERN_DEBUG"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
pHba->name,sg_size,i,sg_count);
rcode = -ENOMEM;
goto cleanup;
}
sg_list[sg_index++] = p;
if(sg[i].flag_count & 0x04000000 ) {
if (copy_from_user(p,(void __user *)(ulong)sg[i].addr_bus, sg_size)) {
printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",pHba->name,i);
rcode = -EFAULT;
goto cleanup;
}
}
sg[i].addr_bus = addr;
}
}
do {
if (pHba->host) {
scsi_block_requests(pHba->host);
spin_lock_irqsave(pHba->host->host_lock, flags);
}
rcode = adpt_i2o_post_wait(pHba, msg, size, FOREVER);
if (rcode != 0)
printk("adpt_i2o_passthru: post wait failed %d %p\n",
rcode, reply);
if (pHba->host) {
spin_unlock_irqrestore(pHba->host->host_lock, flags);
scsi_unblock_requests(pHba->host);
}
} while (rcode == -ETIMEDOUT);
if(rcode){
goto cleanup;
}
if(sg_offset) {
u32 j;
struct sg_simple_element* sg;
int sg_size;
memset(&msg, 0, MAX_MESSAGE_SIZE*4);
if(get_user(size, &user_msg[0])){
rcode = -EFAULT;
goto cleanup;
}
size = size>>16;
size *= 4;
if (size > MAX_MESSAGE_SIZE) {
rcode = -EINVAL;
goto cleanup;
}
if (copy_from_user (msg, user_msg, size)) {
rcode = -EFAULT;
goto cleanup;
}
sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
sg = (struct sg_simple_element*)(msg + sg_offset);
for (j = 0; j < sg_count; j++) {
if(! (sg[j].flag_count & 0x4000000 )) {
sg_size = sg[j].flag_count & 0xffffff;
if (copy_to_user((void __user *)(ulong)sg[j].addr_bus,sg_list[j], sg_size)) {
printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",pHba->name, sg_list[j], sg[j].addr_bus);
rcode = -EFAULT;
goto cleanup;
}
}
}
}
if (reply_size) {
if(copy_from_user(reply+2, user_msg+2, sizeof(u32)*2)) {
printk(KERN_WARNING"%s: Could not copy message context FROM user\n",pHba->name);
rcode = -EFAULT;
}
if(copy_to_user(user_reply, reply, reply_size)) {
printk(KERN_WARNING"%s: Could not copy reply TO user\n",pHba->name);
rcode = -EFAULT;
}
}
cleanup:
if (rcode != -ETIME && rcode != -EINTR) {
struct sg_simple_element *sg =
(struct sg_simple_element*) (msg +sg_offset);
while(sg_index) {
if(sg_list[--sg_index]) {
dma_free_coherent(&pHba->pDev->dev,
sg[sg_index].flag_count & 0xffffff,
sg_list[sg_index],
sg[sg_index].addr_bus);
}
}
}
free:
kfree(sg_list);
kfree(reply);
return rcode;
}
#if defined __ia64__
static void adpt_ia64_info(sysInfo_S* si)
{
si->processorType = PROC_IA64;
}
#endif
#if defined __sparc__
static void adpt_sparc_info(sysInfo_S* si)
{
si->processorType = PROC_ULTRASPARC;
}
#endif
#if defined __alpha__
static void adpt_alpha_info(sysInfo_S* si)
{
si->processorType = PROC_ALPHA;
}
#endif
#if defined __i386__
#include <uapi/asm/vm86.h>
static void adpt_i386_info(sysInfo_S* si)
{
switch (boot_cpu_data.x86) {
case CPU_386:
si->processorType = PROC_386;
break;
case CPU_486:
si->processorType = PROC_486;
break;
case CPU_586:
si->processorType = PROC_PENTIUM;
break;
default:
si->processorType = PROC_PENTIUM;
break;
}
}
#endif
static int adpt_system_info(void __user *buffer)
{
sysInfo_S si;
memset(&si, 0, sizeof(si));
si.osType = OS_LINUX;
si.osMajorVersion = 0;
si.osMinorVersion = 0;
si.osRevision = 0;
si.busType = SI_PCI_BUS;
si.processorFamily = DPTI_sig.dsProcessorFamily;
#if defined __i386__
adpt_i386_info(&si);
#elif defined (__ia64__)
adpt_ia64_info(&si);
#elif defined(__sparc__)
adpt_sparc_info(&si);
#elif defined (__alpha__)
adpt_alpha_info(&si);
#else
si.processorType = 0xff ;
#endif
if (copy_to_user(buffer, &si, sizeof(si))){
printk(KERN_WARNING"dpti: Could not copy buffer TO user\n");
return -EFAULT;
}
return 0;
}
static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
{
int minor;
int error = 0;
adpt_hba* pHba;
ulong flags = 0;
void __user *argp = (void __user *)arg;
minor = iminor(inode);
if (minor >= DPTI_MAX_HBA){
return -ENXIO;
}
mutex_lock(&adpt_configuration_lock);
for (pHba = hba_chain; pHba; pHba = pHba->next) {
if (pHba->unit == minor) {
break;
}
}
mutex_unlock(&adpt_configuration_lock);
if(pHba == NULL){
return -ENXIO;
}
while((volatile u32) pHba->state & DPTI_STATE_RESET )
schedule_timeout_uninterruptible(2);
switch (cmd) {
case DPT_SIGNATURE:
if (copy_to_user(argp, &DPTI_sig, sizeof(DPTI_sig))) {
return -EFAULT;
}
break;
case I2OUSRCMD:
return adpt_i2o_passthru(pHba, argp);
case DPT_CTRLINFO:{
drvrHBAinfo_S HbaInfo;
#define FLG_OSD_PCI_VALID 0x0001
#define FLG_OSD_DMA 0x0002
#define FLG_OSD_I2O 0x0004
memset(&HbaInfo, 0, sizeof(HbaInfo));
HbaInfo.drvrHBAnum = pHba->unit;
HbaInfo.baseAddr = (ulong) pHba->base_addr_phys;
HbaInfo.blinkState = adpt_read_blink_led(pHba);
HbaInfo.pciBusNum = pHba->pDev->bus->number;
HbaInfo.pciDeviceNum=PCI_SLOT(pHba->pDev->devfn);
HbaInfo.Interrupt = pHba->pDev->irq;
HbaInfo.hbaFlags = FLG_OSD_PCI_VALID | FLG_OSD_DMA | FLG_OSD_I2O;
if(copy_to_user(argp, &HbaInfo, sizeof(HbaInfo))){
printk(KERN_WARNING"%s: Could not copy HbaInfo TO user\n",pHba->name);
return -EFAULT;
}
break;
}
case DPT_SYSINFO:
return adpt_system_info(argp);
case DPT_BLINKLED:{
u32 value;
value = (u32)adpt_read_blink_led(pHba);
if (copy_to_user(argp, &value, sizeof(value))) {
return -EFAULT;
}
break;
}
case I2ORESETCMD: {
struct Scsi_Host *shost = pHba->host;
if (shost)
spin_lock_irqsave(shost->host_lock, flags);
adpt_hba_reset(pHba);
if (shost)
spin_unlock_irqrestore(shost->host_lock, flags);
break;
}
case I2ORESCANCMD:
adpt_rescan(pHba);
break;
default:
return -EINVAL;
}
return error;
}
static long adpt_unlocked_ioctl(struct file *file, uint cmd, ulong arg)
{
struct inode *inode;
long ret;
inode = file_inode(file);
mutex_lock(&adpt_mutex);
ret = adpt_ioctl(inode, file, cmd, arg);
mutex_unlock(&adpt_mutex);
return ret;
}
#ifdef CONFIG_COMPAT
static long compat_adpt_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct inode *inode;
long ret;
inode = file_inode(file);
mutex_lock(&adpt_mutex);
switch(cmd) {
case DPT_SIGNATURE:
case I2OUSRCMD:
case DPT_CTRLINFO:
case DPT_SYSINFO:
case DPT_BLINKLED:
case I2ORESETCMD:
case I2ORESCANCMD:
case (DPT_TARGET_BUSY & 0xFFFF):
case DPT_TARGET_BUSY:
ret = adpt_ioctl(inode, file, cmd, arg);
break;
default:
ret = -ENOIOCTLCMD;
}
mutex_unlock(&adpt_mutex);
return ret;
}
#endif
static irqreturn_t adpt_isr(int irq, void *dev_id)
{
struct scsi_cmnd* cmd;
adpt_hba* pHba = dev_id;
u32 m;
void __iomem *reply;
u32 status=0;
u32 context;
ulong flags = 0;
int handled = 0;
if (pHba == NULL){
printk(KERN_WARNING"adpt_isr: NULL dev_id\n");
return IRQ_NONE;
}
if(pHba->host)
spin_lock_irqsave(pHba->host->host_lock, flags);
while( readl(pHba->irq_mask) & I2O_INTERRUPT_PENDING_B) {
m = readl(pHba->reply_port);
if(m == EMPTY_QUEUE){
rmb();
m = readl(pHba->reply_port);
if(m == EMPTY_QUEUE){
printk(KERN_ERR"dpti: Could not get reply frame\n");
goto out;
}
}
if (pHba->reply_pool_pa <= m &&
m < pHba->reply_pool_pa +
(pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4)) {
reply = (u8 *)pHba->reply_pool +
(m - pHba->reply_pool_pa);
} else {
printk(KERN_ERR "dpti: reply frame not from pool\n");
reply = (u8 *)bus_to_virt(m);
}
if (readl(reply) & MSG_FAIL) {
u32 old_m = readl(reply+28);
void __iomem *msg;
u32 old_context;
PDEBUG("%s: Failed message\n",pHba->name);
if(old_m >= 0x100000){
printk(KERN_ERR"%s: Bad preserved MFA (%x)- dropping frame\n",pHba->name,old_m);
writel(m,pHba->reply_port);
continue;
}
msg = pHba->msg_addr_virt + old_m;
old_context = readl(msg+12);
writel(old_context, reply+12);
adpt_send_nop(pHba, old_m);
}
context = readl(reply+8);
if(context & 0x40000000){
void *p = adpt_ioctl_from_context(pHba, readl(reply+12));
if( p != NULL) {
memcpy_fromio(p, reply, REPLY_FRAME_SIZE * 4);
}
}
if(context & 0x80000000){
status = readl(reply+16);
if(status >> 24){
status &= 0xffff;
} else {
status = I2O_POST_WAIT_OK;
}
if(!(context & 0x40000000)) {
cmd = scsi_host_find_tag(pHba->host,
readl(reply + 12) - 1);
if(cmd != NULL) {
printk(KERN_WARNING"%s: Apparent SCSI cmd in Post Wait Context - cmd=%p context=%x\n", pHba->name, cmd, context);
}
}
adpt_i2o_post_wait_complete(context, status);
} else {
cmd = scsi_host_find_tag(pHba->host,
readl(reply + 12) - 1);
if(cmd != NULL){
scsi_dma_unmap(cmd);
adpt_i2o_to_scsi(reply, cmd);
}
}
writel(m, pHba->reply_port);
wmb();
rmb();
}
handled = 1;
out: if(pHba->host)
spin_unlock_irqrestore(pHba->host->host_lock, flags);
return IRQ_RETVAL(handled);
}
static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_device* d)
{
int i;
u32 msg[MAX_MESSAGE_SIZE];
u32* mptr;
u32* lptr;
u32 *lenptr;
int direction;
int scsidir;
int nseg;
u32 len;
u32 reqlen;
s32 rcode;
dma_addr_t addr;
memset(msg, 0 , sizeof(msg));
len = scsi_bufflen(cmd);
direction = 0x00000000;
scsidir = 0x00000000;
if(len) {
switch(cmd->sc_data_direction){
case DMA_FROM_DEVICE:
scsidir =0x40000000;
break;
case DMA_TO_DEVICE:
direction=0x04000000;
scsidir =0x80000000;
break;
case DMA_NONE:
break;
case DMA_BIDIRECTIONAL:
scsidir =0x40000000;
break;
default:
printk(KERN_WARNING"%s: scsi opcode 0x%x not supported.\n",
pHba->name, cmd->cmnd[0]);
cmd->result = (DID_OK <<16) | (INITIATOR_ERROR << 8);
cmd->scsi_done(cmd);
return 0;
}
}
msg[1] = ((0xff<<24)|(HOST_TID<<12)|d->tid);
msg[2] = 0;
msg[3] = cmd->request->tag + 1;
msg[4] = I2O_CMD_SCSI_EXEC|(DPT_ORGANIZATION_ID<<16);
msg[5] = d->tid;
msg[6] = scsidir|0x20a00000|cmd->cmd_len;
mptr=msg+7;
memset(mptr, 0, 16);
memcpy(mptr, cmd->cmnd, cmd->cmd_len);
mptr+=4;
lenptr=mptr++;
if (dpt_dma64(pHba)) {
reqlen = 16;
*mptr++ = (0x7C<<24)+(2<<16)+0x02;
*mptr++ = 1 << PAGE_SHIFT;
} else {
reqlen = 14;
}
nseg = scsi_dma_map(cmd);
BUG_ON(nseg < 0);
if (nseg) {
struct scatterlist *sg;
len = 0;
scsi_for_each_sg(cmd, sg, nseg, i) {
lptr = mptr;
*mptr++ = direction|0x10000000|sg_dma_len(sg);
len+=sg_dma_len(sg);
addr = sg_dma_address(sg);
*mptr++ = dma_low(addr);
if (dpt_dma64(pHba))
*mptr++ = dma_high(addr);
if (i == nseg - 1)
*lptr = direction|0xD0000000|sg_dma_len(sg);
}
reqlen = mptr - msg;
*lenptr = len;
if(cmd->underflow && len != cmd->underflow){
printk(KERN_WARNING"Cmd len %08X Cmd underflow %08X\n",
len, cmd->underflow);
}
} else {
*lenptr = len = 0;
reqlen = 12;
}
msg[0] = reqlen<<16 | ((reqlen > 12) ? SGL_OFFSET_12 : SGL_OFFSET_0);
rcode = adpt_i2o_post_this(pHba, msg, reqlen<<2);
if (rcode == 0) {
return 0;
}
return rcode;
}
static s32 adpt_scsi_host_alloc(adpt_hba* pHba, struct scsi_host_template *sht)
{
struct Scsi_Host *host;
host = scsi_host_alloc(sht, sizeof(adpt_hba*));
if (host == NULL) {
printk("%s: scsi_host_alloc returned NULL\n", pHba->name);
return -1;
}
host->hostdata[0] = (unsigned long)pHba;
pHba->host = host;
host->irq = pHba->pDev->irq;
host->io_port = 0;
host->n_io_port = 0;
host->max_id = 16;
host->max_lun = 256;
host->max_channel = pHba->top_scsi_channel + 1;
host->cmd_per_lun = 1;
host->unique_id = (u32)sys_tbl_pa + pHba->unit;
host->sg_tablesize = pHba->sg_tablesize;
host->can_queue = pHba->post_fifo_size;
host->use_cmd_list = 1;
return 0;
}
static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd)
{
adpt_hba* pHba;
u32 hba_status;
u32 dev_status;
u32 reply_flags = readl(reply) & 0xff00;
u16 detailed_status = readl(reply+16) &0xffff;
dev_status = (detailed_status & 0xff);
hba_status = detailed_status >> 8;
scsi_set_resid(cmd, scsi_bufflen(cmd) - readl(reply+20));
pHba = (adpt_hba*) cmd->device->host->hostdata[0];
cmd->sense_buffer[0] = '\0';
if(!(reply_flags & MSG_FAIL)) {
switch(detailed_status & I2O_SCSI_DSC_MASK) {
case I2O_SCSI_DSC_SUCCESS:
cmd->result = (DID_OK << 16);
if (readl(reply+20) < cmd->underflow) {
cmd->result = (DID_ERROR <<16);
printk(KERN_WARNING"%s: SCSI CMD underflow\n",pHba->name);
}
break;
case I2O_SCSI_DSC_REQUEST_ABORTED:
cmd->result = (DID_ABORT << 16);
break;
case I2O_SCSI_DSC_PATH_INVALID:
case I2O_SCSI_DSC_DEVICE_NOT_PRESENT:
case I2O_SCSI_DSC_SELECTION_TIMEOUT:
case I2O_SCSI_DSC_COMMAND_TIMEOUT:
case I2O_SCSI_DSC_NO_ADAPTER:
case I2O_SCSI_DSC_RESOURCE_UNAVAILABLE:
printk(KERN_WARNING"%s: SCSI Timeout-Device (%d,%d,%llu) hba status=0x%x, dev status=0x%x, cmd=0x%x\n",
pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]);
cmd->result = (DID_TIME_OUT << 16);
break;
case I2O_SCSI_DSC_ADAPTER_BUSY:
case I2O_SCSI_DSC_BUS_BUSY:
cmd->result = (DID_BUS_BUSY << 16);
break;
case I2O_SCSI_DSC_SCSI_BUS_RESET:
case I2O_SCSI_DSC_BDR_MESSAGE_SENT:
cmd->result = (DID_RESET << 16);
break;
case I2O_SCSI_DSC_PARITY_ERROR_FAILURE:
printk(KERN_WARNING"%s: SCSI CMD parity error\n",pHba->name);
cmd->result = (DID_PARITY << 16);
break;
case I2O_SCSI_DSC_UNABLE_TO_ABORT:
case I2O_SCSI_DSC_COMPLETE_WITH_ERROR:
case I2O_SCSI_DSC_UNABLE_TO_TERMINATE:
case I2O_SCSI_DSC_MR_MESSAGE_RECEIVED:
case I2O_SCSI_DSC_AUTOSENSE_FAILED:
case I2O_SCSI_DSC_DATA_OVERRUN:
case I2O_SCSI_DSC_UNEXPECTED_BUS_FREE:
case I2O_SCSI_DSC_SEQUENCE_FAILURE:
case I2O_SCSI_DSC_REQUEST_LENGTH_ERROR:
case I2O_SCSI_DSC_PROVIDE_FAILURE:
case I2O_SCSI_DSC_REQUEST_TERMINATED:
case I2O_SCSI_DSC_IDE_MESSAGE_SENT:
case I2O_SCSI_DSC_UNACKNOWLEDGED_EVENT:
case I2O_SCSI_DSC_MESSAGE_RECEIVED:
case I2O_SCSI_DSC_INVALID_CDB:
case I2O_SCSI_DSC_LUN_INVALID:
case I2O_SCSI_DSC_SCSI_TID_INVALID:
case I2O_SCSI_DSC_FUNCTION_UNAVAILABLE:
case I2O_SCSI_DSC_NO_NEXUS:
case I2O_SCSI_DSC_CDB_RECEIVED:
case I2O_SCSI_DSC_LUN_ALREADY_ENABLED:
case I2O_SCSI_DSC_QUEUE_FROZEN:
case I2O_SCSI_DSC_REQUEST_INVALID:
default:
printk(KERN_WARNING"%s: SCSI error %0x-Device(%d,%d,%llu) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n",
pHba->name, detailed_status & I2O_SCSI_DSC_MASK, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun,
hba_status, dev_status, cmd->cmnd[0]);
cmd->result = (DID_ERROR << 16);
break;
}
if (dev_status == SAM_STAT_CHECK_CONDITION) {
u32 len = min(SCSI_SENSE_BUFFERSIZE, 40);
memcpy_fromio(cmd->sense_buffer, (reply+28) , len);
if(cmd->sense_buffer[0] == 0x70 &&
cmd->sense_buffer[2] == DATA_PROTECT ){
cmd->result = (DID_TIME_OUT << 16);
printk(KERN_WARNING"%s: SCSI Data Protect-Device (%d,%d,%llu) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n",
pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun,
hba_status, dev_status, cmd->cmnd[0]);
}
}
} else {
cmd->result = (DID_TIME_OUT << 16);
printk(KERN_WARNING"%s: I2O MSG_FAIL - Device (%d,%d,%llu) tid=%d, cmd=0x%x\n",
pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun,
((struct adpt_device*)(cmd->device->hostdata))->tid, cmd->cmnd[0]);
}
cmd->result |= (dev_status);
if(cmd->scsi_done != NULL){
cmd->scsi_done(cmd);
}
return cmd->result;
}
static s32 adpt_rescan(adpt_hba* pHba)
{
s32 rcode;
ulong flags = 0;
if(pHba->host)
spin_lock_irqsave(pHba->host->host_lock, flags);
if ((rcode=adpt_i2o_lct_get(pHba)) < 0)
goto out;
if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0)
goto out;
rcode = 0;
out: if(pHba->host)
spin_unlock_irqrestore(pHba->host->host_lock, flags);
return rcode;
}
static s32 adpt_i2o_reparse_lct(adpt_hba* pHba)
{
int i;
int max;
int tid;
struct i2o_device *d;
i2o_lct *lct = pHba->lct;
u8 bus_no = 0;
s16 scsi_id;
u64 scsi_lun;
u32 buf[10];
struct adpt_device* pDev = NULL;
struct i2o_device* pI2o_dev = NULL;
if (lct == NULL) {
printk(KERN_ERR "%s: LCT is empty???\n",pHba->name);
return -1;
}
max = lct->table_size;
max -= 3;
max /= 9;
for (d = pHba->devices; d; d = d->next) {
pDev =(struct adpt_device*) d->owner;
if(!pDev){
continue;
}
pDev->state |= DPTI_DEV_UNSCANNED;
}
printk(KERN_INFO "%s: LCT has %d entries.\n", pHba->name,max);
for(i=0;i<max;i++) {
if( lct->lct_entry[i].user_tid != 0xfff){
continue;
}
if( lct->lct_entry[i].class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE ||
lct->lct_entry[i].class_id == I2O_CLASS_SCSI_PERIPHERAL ||
lct->lct_entry[i].class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
tid = lct->lct_entry[i].tid;
if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) {
printk(KERN_ERR"%s: Could not query device\n",pHba->name);
continue;
}
bus_no = buf[0]>>16;
if (bus_no >= MAX_CHANNEL) {
printk(KERN_WARNING
"%s: Channel number %d out of range\n",
pHba->name, bus_no);
continue;
}
scsi_id = buf[1];
scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]);
pDev = pHba->channel[bus_no].device[scsi_id];
while(pDev) {
if(pDev->scsi_lun == scsi_lun) {
break;
}
pDev = pDev->next_lun;
}
if(!pDev ) {
d = kmalloc(sizeof(struct i2o_device),
GFP_ATOMIC);
if(d==NULL)
{
printk(KERN_CRIT "Out of memory for I2O device data.\n");
return -ENOMEM;
}
d->controller = pHba;
d->next = NULL;
memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
d->flags = 0;
adpt_i2o_report_hba_unit(pHba, d);
adpt_i2o_install_device(pHba, d);
pDev = pHba->channel[bus_no].device[scsi_id];
if( pDev == NULL){
pDev =
kzalloc(sizeof(struct adpt_device),
GFP_ATOMIC);
if(pDev == NULL) {
return -ENOMEM;
}
pHba->channel[bus_no].device[scsi_id] = pDev;
} else {
while (pDev->next_lun) {
pDev = pDev->next_lun;
}
pDev = pDev->next_lun =
kzalloc(sizeof(struct adpt_device),
GFP_ATOMIC);
if(pDev == NULL) {
return -ENOMEM;
}
}
pDev->tid = d->lct_data.tid;
pDev->scsi_channel = bus_no;
pDev->scsi_id = scsi_id;
pDev->scsi_lun = scsi_lun;
pDev->pI2o_dev = d;
d->owner = pDev;
pDev->type = (buf[0])&0xff;
pDev->flags = (buf[0]>>8)&0xff;
if(scsi_id > pHba->top_scsi_id){
pHba->top_scsi_id = scsi_id;
}
if(scsi_lun > pHba->top_scsi_lun){
pHba->top_scsi_lun = scsi_lun;
}
continue;
}
while(pDev) {
if(pDev->scsi_lun == scsi_lun) {
if(!scsi_device_online(pDev->pScsi_dev)) {
printk(KERN_WARNING"%s: Setting device (%d,%d,%llu) back online\n",
pHba->name,bus_no,scsi_id,scsi_lun);
if (pDev->pScsi_dev) {
scsi_device_set_state(pDev->pScsi_dev, SDEV_RUNNING);
}
}
d = pDev->pI2o_dev;
if(d->lct_data.tid != tid) {
pDev->tid = tid;
memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
if (pDev->pScsi_dev) {
pDev->pScsi_dev->changed = TRUE;
pDev->pScsi_dev->removable = TRUE;
}
}
pDev->state = DPTI_DEV_ONLINE;
break;
}
pDev = pDev->next_lun;
}
}
}
for (pI2o_dev = pHba->devices; pI2o_dev; pI2o_dev = pI2o_dev->next) {
pDev =(struct adpt_device*) pI2o_dev->owner;
if(!pDev){
continue;
}
if (pDev->state & DPTI_DEV_UNSCANNED){
pDev->state = DPTI_DEV_OFFLINE;
printk(KERN_WARNING"%s: Device (%d,%d,%llu) offline\n",pHba->name,pDev->scsi_channel,pDev->scsi_id,pDev->scsi_lun);
if (pDev->pScsi_dev) {
scsi_device_set_state(pDev->pScsi_dev, SDEV_OFFLINE);
}
}
}
return 0;
}
static void adpt_fail_posted_scbs(adpt_hba* pHba)
{
struct scsi_cmnd* cmd = NULL;
struct scsi_device* d = NULL;
shost_for_each_device(d, pHba->host) {
unsigned long flags;
spin_lock_irqsave(&d->list_lock, flags);
list_for_each_entry(cmd, &d->cmd_list, list) {
cmd->result = (DID_OK << 16) | (QUEUE_FULL <<1);
cmd->scsi_done(cmd);
}
spin_unlock_irqrestore(&d->list_lock, flags);
}
}
static int adpt_i2o_activate_hba(adpt_hba* pHba)
{
int rcode;
if(pHba->initialized ) {
if (adpt_i2o_status_get(pHba) < 0) {
if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
return rcode;
}
if (adpt_i2o_status_get(pHba) < 0) {
printk(KERN_INFO "HBA not responding.\n");
return -1;
}
}
if(pHba->status_block->iop_state == ADAPTER_STATE_FAULTED) {
printk(KERN_CRIT "%s: hardware fault\n", pHba->name);
return -1;
}
if (pHba->status_block->iop_state == ADAPTER_STATE_READY ||
pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL ||
pHba->status_block->iop_state == ADAPTER_STATE_HOLD ||
pHba->status_block->iop_state == ADAPTER_STATE_FAILED) {
adpt_i2o_reset_hba(pHba);
if (adpt_i2o_status_get(pHba) < 0 || pHba->status_block->iop_state != ADAPTER_STATE_RESET) {
printk(KERN_ERR "%s: Failed to initialize.\n", pHba->name);
return -1;
}
}
} else {
if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
return rcode;
}
}
if (adpt_i2o_init_outbound_q(pHba) < 0) {
return -1;
}
if (adpt_i2o_hrt_get(pHba) < 0) {
return -1;
}
return 0;
}
static int adpt_i2o_online_hba(adpt_hba* pHba)
{
if (adpt_i2o_systab_send(pHba) < 0)
return -1;
if (adpt_i2o_enable_hba(pHba) < 0)
return -1;
return 0;
}
static s32 adpt_send_nop(adpt_hba*pHba,u32 m)
{
u32 __iomem *msg;
ulong timeout = jiffies + 5*HZ;
while(m == EMPTY_QUEUE){
rmb();
m = readl(pHba->post_port);
if(m != EMPTY_QUEUE){
break;
}
if(time_after(jiffies,timeout)){
printk(KERN_ERR "%s: Timeout waiting for message frame!\n",pHba->name);
return 2;
}
schedule_timeout_uninterruptible(1);
}
msg = (u32 __iomem *)(pHba->msg_addr_virt + m);
writel( THREE_WORD_MSG_SIZE | SGL_OFFSET_0,&msg[0]);
writel( I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0,&msg[1]);
writel( 0,&msg[2]);
wmb();
writel(m, pHba->post_port);
wmb();
return 0;
}
static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba)
{
u8 *status;
dma_addr_t addr;
u32 __iomem *msg = NULL;
int i;
ulong timeout = jiffies + TMOUT_INITOUTBOUND*HZ;
u32 m;
do {
rmb();
m = readl(pHba->post_port);
if (m != EMPTY_QUEUE) {
break;
}
if(time_after(jiffies,timeout)){
printk(KERN_WARNING"%s: Timeout waiting for message frame\n",pHba->name);
return -ETIMEDOUT;
}
schedule_timeout_uninterruptible(1);
} while(m == EMPTY_QUEUE);
msg=(u32 __iomem *)(pHba->msg_addr_virt+m);
status = dma_alloc_coherent(&pHba->pDev->dev, 4, &addr, GFP_KERNEL);
if (!status) {
adpt_send_nop(pHba, m);
printk(KERN_WARNING"%s: IOP reset failed - no free memory.\n",
pHba->name);
return -ENOMEM;
}
memset(status, 0, 4);
writel(EIGHT_WORD_MSG_SIZE| SGL_OFFSET_6, &msg[0]);
writel(I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID, &msg[1]);
writel(0, &msg[2]);
writel(0x0106, &msg[3]);
writel(4096, &msg[4]);
writel((REPLY_FRAME_SIZE)<<16|0x80, &msg[5]);
writel(0xD0000004, &msg[6]);
writel((u32)addr, &msg[7]);
writel(m, pHba->post_port);
wmb();
do {
if (*status) {
if (*status != 0x01 ) {
break;
}
}
rmb();
if(time_after(jiffies,timeout)){
printk(KERN_WARNING"%s: Timeout Initializing\n",pHba->name);
return -ETIMEDOUT;
}
schedule_timeout_uninterruptible(1);
} while (1);
if(*status != 0x04 ) {
dma_free_coherent(&pHba->pDev->dev, 4, status, addr);
return -2;
}
dma_free_coherent(&pHba->pDev->dev, 4, status, addr);
if(pHba->reply_pool != NULL) {
dma_free_coherent(&pHba->pDev->dev,
pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4,
pHba->reply_pool, pHba->reply_pool_pa);
}
pHba->reply_pool = dma_alloc_coherent(&pHba->pDev->dev,
pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4,
&pHba->reply_pool_pa, GFP_KERNEL);
if (!pHba->reply_pool) {
printk(KERN_ERR "%s: Could not allocate reply pool\n", pHba->name);
return -ENOMEM;
}
memset(pHba->reply_pool, 0 , pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4);
for(i = 0; i < pHba->reply_fifo_size; i++) {
writel(pHba->reply_pool_pa + (i * REPLY_FRAME_SIZE * 4),
pHba->reply_port);
wmb();
}
adpt_i2o_status_get(pHba);
return 0;
}
static s32 adpt_i2o_status_get(adpt_hba* pHba)
{
ulong timeout;
u32 m;
u32 __iomem *msg;
u8 *status_block=NULL;
if(pHba->status_block == NULL) {
pHba->status_block = dma_alloc_coherent(&pHba->pDev->dev,
sizeof(i2o_status_block),
&pHba->status_block_pa, GFP_KERNEL);
if(pHba->status_block == NULL) {
printk(KERN_ERR
"dpti%d: Get Status Block failed; Out of memory. \n",
pHba->unit);
return -ENOMEM;
}
}
memset(pHba->status_block, 0, sizeof(i2o_status_block));
status_block = (u8*)(pHba->status_block);
timeout = jiffies+TMOUT_GETSTATUS*HZ;
do {
rmb();
m = readl(pHba->post_port);
if (m != EMPTY_QUEUE) {
break;
}
if(time_after(jiffies,timeout)){
printk(KERN_ERR "%s: Timeout waiting for message !\n",
pHba->name);
return -ETIMEDOUT;
}
schedule_timeout_uninterruptible(1);
} while(m==EMPTY_QUEUE);
msg=(u32 __iomem *)(pHba->msg_addr_virt+m);
writel(NINE_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]);
writel(I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID, &msg[1]);
writel(1, &msg[2]);
writel(0, &msg[3]);
writel(0, &msg[4]);
writel(0, &msg[5]);
writel( dma_low(pHba->status_block_pa), &msg[6]);
writel( dma_high(pHba->status_block_pa), &msg[7]);
writel(sizeof(i2o_status_block), &msg[8]);
writel(m, pHba->post_port);
wmb();
while(status_block[87]!=0xff){
if(time_after(jiffies,timeout)){
printk(KERN_ERR"dpti%d: Get status timeout.\n",
pHba->unit);
return -ETIMEDOUT;
}
rmb();
schedule_timeout_uninterruptible(1);
}
pHba->post_fifo_size = pHba->status_block->max_inbound_frames;
if (pHba->post_fifo_size > MAX_TO_IOP_MESSAGES) {
pHba->post_fifo_size = MAX_TO_IOP_MESSAGES;
}
pHba->reply_fifo_size = pHba->status_block->max_outbound_frames;
if (pHba->reply_fifo_size > MAX_FROM_IOP_MESSAGES) {
pHba->reply_fifo_size = MAX_FROM_IOP_MESSAGES;
}
if (dpt_dma64(pHba)) {
pHba->sg_tablesize
= ((pHba->status_block->inbound_frame_size * 4
- 14 * sizeof(u32))
/ (sizeof(struct sg_simple_element) + sizeof(u32)));
} else {
pHba->sg_tablesize
= ((pHba->status_block->inbound_frame_size * 4
- 12 * sizeof(u32))
/ sizeof(struct sg_simple_element));
}
if (pHba->sg_tablesize > SG_LIST_ELEMENTS) {
pHba->sg_tablesize = SG_LIST_ELEMENTS;
}
#ifdef DEBUG
printk("dpti%d: State = ",pHba->unit);
switch(pHba->status_block->iop_state) {
case 0x01:
printk("INIT\n");
break;
case 0x02:
printk("RESET\n");
break;
case 0x04:
printk("HOLD\n");
break;
case 0x05:
printk("READY\n");
break;
case 0x08:
printk("OPERATIONAL\n");
break;
case 0x10:
printk("FAILED\n");
break;
case 0x11:
printk("FAULTED\n");
break;
default:
printk("%x (unknown!!)\n",pHba->status_block->iop_state);
}
#endif
return 0;
}
static int adpt_i2o_lct_get(adpt_hba* pHba)
{
u32 msg[8];
int ret;
u32 buf[16];
if ((pHba->lct_size == 0) || (pHba->lct == NULL)){
pHba->lct_size = pHba->status_block->expected_lct_size;
}
do {
if (pHba->lct == NULL) {
pHba->lct = dma_alloc_coherent(&pHba->pDev->dev,
pHba->lct_size, &pHba->lct_pa,
GFP_ATOMIC);
if(pHba->lct == NULL) {
printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n",
pHba->name);
return -ENOMEM;
}
}
memset(pHba->lct, 0, pHba->lct_size);
msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6;
msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
msg[2] = 0;
msg[3] = 0;
msg[4] = 0xFFFFFFFF;
msg[5] = 0x00000000;
msg[6] = 0xD0000000|pHba->lct_size;
msg[7] = (u32)pHba->lct_pa;
if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 360))) {
printk(KERN_ERR "%s: LCT Get failed (status=%#10x.\n",
pHba->name, ret);
printk(KERN_ERR"Adaptec: Error Reading Hardware.\n");
return ret;
}
if ((pHba->lct->table_size << 2) > pHba->lct_size) {
pHba->lct_size = pHba->lct->table_size << 2;
dma_free_coherent(&pHba->pDev->dev, pHba->lct_size,
pHba->lct, pHba->lct_pa);
pHba->lct = NULL;
}
} while (pHba->lct == NULL);
PDEBUG("%s: Hardware resource table read.\n", pHba->name);
if(adpt_i2o_query_scalar(pHba, 0 , 0x8000, -1, buf, sizeof(buf))>=0) {
pHba->FwDebugBufferSize = buf[1];
pHba->FwDebugBuffer_P = ioremap(pHba->base_addr_phys + buf[0],
pHba->FwDebugBufferSize);
if (pHba->FwDebugBuffer_P) {
pHba->FwDebugFlags_P = pHba->FwDebugBuffer_P +
FW_DEBUG_FLAGS_OFFSET;
pHba->FwDebugBLEDvalue_P = pHba->FwDebugBuffer_P +
FW_DEBUG_BLED_OFFSET;
pHba->FwDebugBLEDflag_P = pHba->FwDebugBLEDvalue_P + 1;
pHba->FwDebugStrLength_P = pHba->FwDebugBuffer_P +
FW_DEBUG_STR_LENGTH_OFFSET;
pHba->FwDebugBuffer_P += buf[2];
pHba->FwDebugFlags = 0;
}
}
return 0;
}
static int adpt_i2o_build_sys_table(void)
{
adpt_hba* pHba = hba_chain;
int count = 0;
if (sys_tbl)
dma_free_coherent(&pHba->pDev->dev, sys_tbl_len,
sys_tbl, sys_tbl_pa);
sys_tbl_len = sizeof(struct i2o_sys_tbl) +
(hba_count) * sizeof(struct i2o_sys_tbl_entry);
sys_tbl = dma_alloc_coherent(&pHba->pDev->dev,
sys_tbl_len, &sys_tbl_pa, GFP_KERNEL);
if (!sys_tbl) {
printk(KERN_WARNING "SysTab Set failed. Out of memory.\n");
return -ENOMEM;
}
memset(sys_tbl, 0, sys_tbl_len);
sys_tbl->num_entries = hba_count;
sys_tbl->version = I2OVERSION;
sys_tbl->change_ind = sys_tbl_ind++;
for(pHba = hba_chain; pHba; pHba = pHba->next) {
u64 addr;
if (adpt_i2o_status_get(pHba)) {
sys_tbl->num_entries--;
continue;
}
sys_tbl->iops[count].org_id = pHba->status_block->org_id;
sys_tbl->iops[count].iop_id = pHba->unit + 2;
sys_tbl->iops[count].seg_num = 0;
sys_tbl->iops[count].i2o_version = pHba->status_block->i2o_version;
sys_tbl->iops[count].iop_state = pHba->status_block->iop_state;
sys_tbl->iops[count].msg_type = pHba->status_block->msg_type;
sys_tbl->iops[count].frame_size = pHba->status_block->inbound_frame_size;
sys_tbl->iops[count].last_changed = sys_tbl_ind - 1;
sys_tbl->iops[count].iop_capabilities = pHba->status_block->iop_capabilities;
addr = pHba->base_addr_phys + 0x40;
sys_tbl->iops[count].inbound_low = dma_low(addr);
sys_tbl->iops[count].inbound_high = dma_high(addr);
count++;
}
#ifdef DEBUG
{
u32 *table = (u32*)sys_tbl;
printk(KERN_DEBUG"sys_tbl_len=%d in 32bit words\n",(sys_tbl_len >>2));
for(count = 0; count < (sys_tbl_len >>2); count++) {
printk(KERN_INFO "sys_tbl[%d] = %0#10x\n",
count, table[count]);
}
}
#endif
return 0;
}
static void adpt_i2o_report_hba_unit(adpt_hba* pHba, struct i2o_device *d)
{
char buf[64];
int unit = d->lct_data.tid;
printk(KERN_INFO "TID %3.3d ", unit);
if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 3, buf, 16)>=0)
{
buf[16]=0;
printk(" Vendor: %-12.12s", buf);
}
if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 4, buf, 16)>=0)
{
buf[16]=0;
printk(" Device: %-12.12s", buf);
}
if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 6, buf, 8)>=0)
{
buf[8]=0;
printk(" Rev: %-12.12s\n", buf);
}
#ifdef DEBUG
printk(KERN_INFO "\tClass: %.21s\n", adpt_i2o_get_class_name(d->lct_data.class_id));
printk(KERN_INFO "\tSubclass: 0x%04X\n", d->lct_data.sub_class);
printk(KERN_INFO "\tFlags: ");
if(d->lct_data.device_flags&(1<<0))
printk("C");
if(d->lct_data.device_flags&(1<<1))
printk("U");
if(!(d->lct_data.device_flags&(1<<4)))
printk("P");
if(!(d->lct_data.device_flags&(1<<5)))
printk("M");
printk("\n");
#endif
}
#ifdef DEBUG
static const char *adpt_i2o_get_class_name(int class)
{
int idx = 16;
static char *i2o_class_name[] = {
"Executive",
"Device Driver Module",
"Block Device",
"Tape Device",
"LAN Interface",
"WAN Interface",
"Fibre Channel Port",
"Fibre Channel Device",
"SCSI Device",
"ATE Port",
"ATE Device",
"Floppy Controller",
"Floppy Device",
"Secondary Bus Port",
"Peer Transport Agent",
"Peer Transport",
"Unknown"
};
switch(class&0xFFF) {
case I2O_CLASS_EXECUTIVE:
idx = 0; break;
case I2O_CLASS_DDM:
idx = 1; break;
case I2O_CLASS_RANDOM_BLOCK_STORAGE:
idx = 2; break;
case I2O_CLASS_SEQUENTIAL_STORAGE:
idx = 3; break;
case I2O_CLASS_LAN:
idx = 4; break;
case I2O_CLASS_WAN:
idx = 5; break;
case I2O_CLASS_FIBRE_CHANNEL_PORT:
idx = 6; break;
case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL:
idx = 7; break;
case I2O_CLASS_SCSI_PERIPHERAL:
idx = 8; break;
case I2O_CLASS_ATE_PORT:
idx = 9; break;
case I2O_CLASS_ATE_PERIPHERAL:
idx = 10; break;
case I2O_CLASS_FLOPPY_CONTROLLER:
idx = 11; break;
case I2O_CLASS_FLOPPY_DEVICE:
idx = 12; break;
case I2O_CLASS_BUS_ADAPTER_PORT:
idx = 13; break;
case I2O_CLASS_PEER_TRANSPORT_AGENT:
idx = 14; break;
case I2O_CLASS_PEER_TRANSPORT:
idx = 15; break;
}
return i2o_class_name[idx];
}
#endif
static s32 adpt_i2o_hrt_get(adpt_hba* pHba)
{
u32 msg[6];
int ret, size = sizeof(i2o_hrt);
do {
if (pHba->hrt == NULL) {
pHba->hrt = dma_alloc_coherent(&pHba->pDev->dev,
size, &pHba->hrt_pa, GFP_KERNEL);
if (pHba->hrt == NULL) {
printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", pHba->name);
return -ENOMEM;
}
}
msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
msg[2]= 0;
msg[3]= 0;
msg[4]= (0xD0000000 | size);
msg[5]= (u32)pHba->hrt_pa;
if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg),20))) {
printk(KERN_ERR "%s: Unable to get HRT (status=%#10x)\n", pHba->name, ret);
return ret;
}
if (pHba->hrt->num_entries * pHba->hrt->entry_len << 2 > size) {
int newsize = pHba->hrt->num_entries * pHba->hrt->entry_len << 2;
dma_free_coherent(&pHba->pDev->dev, size,
pHba->hrt, pHba->hrt_pa);
size = newsize;
pHba->hrt = NULL;
}
} while(pHba->hrt == NULL);
return 0;
}
static int adpt_i2o_query_scalar(adpt_hba* pHba, int tid,
int group, int field, void *buf, int buflen)
{
u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field };
u8 *opblk_va;
dma_addr_t opblk_pa;
u8 *resblk_va;
dma_addr_t resblk_pa;
int size;
resblk_va = dma_alloc_coherent(&pHba->pDev->dev,
sizeof(u8) * (8 + buflen), &resblk_pa, GFP_KERNEL);
if (resblk_va == NULL) {
printk(KERN_CRIT "%s: query scalar failed; Out of memory.\n", pHba->name);
return -ENOMEM;
}
opblk_va = dma_alloc_coherent(&pHba->pDev->dev,
sizeof(opblk), &opblk_pa, GFP_KERNEL);
if (opblk_va == NULL) {
dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen),
resblk_va, resblk_pa);
printk(KERN_CRIT "%s: query operation failed; Out of memory.\n",
pHba->name);
return -ENOMEM;
}
if (field == -1)
opblk[4] = -1;
memcpy(opblk_va, opblk, sizeof(opblk));
size = adpt_i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, pHba, tid,
opblk_va, opblk_pa, sizeof(opblk),
resblk_va, resblk_pa, sizeof(u8)*(8+buflen));
dma_free_coherent(&pHba->pDev->dev, sizeof(opblk), opblk_va, opblk_pa);
if (size == -ETIME) {
dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen),
resblk_va, resblk_pa);
printk(KERN_WARNING "%s: issue params failed; Timed out.\n", pHba->name);
return -ETIME;
} else if (size == -EINTR) {
dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen),
resblk_va, resblk_pa);
printk(KERN_WARNING "%s: issue params failed; Interrupted.\n", pHba->name);
return -EINTR;
}
memcpy(buf, resblk_va+8, buflen);
dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen),
resblk_va, resblk_pa);
if (size < 0)
return size;
return buflen;
}
static int adpt_i2o_issue_params(int cmd, adpt_hba* pHba, int tid,
void *opblk_va, dma_addr_t opblk_pa, int oplen,
void *resblk_va, dma_addr_t resblk_pa, int reslen)
{
u32 msg[9];
u32 *res = (u32 *)resblk_va;
int wait_status;
msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5;
msg[1] = cmd << 24 | HOST_TID << 12 | tid;
msg[2] = 0;
msg[3] = 0;
msg[4] = 0;
msg[5] = 0x54000000 | oplen;
msg[6] = (u32)opblk_pa;
msg[7] = 0xD0000000 | reslen;
msg[8] = (u32)resblk_pa;
if ((wait_status = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 20))) {
printk("adpt_i2o_issue_params: post_wait failed (%p)\n", resblk_va);
return wait_status;
}
if (res[1]&0x00FF0000) {
printk(KERN_WARNING "%s: %s - Error:\n ErrorInfoSize = 0x%02x, "
"BlockStatus = 0x%02x, BlockSize = 0x%04x\n",
pHba->name,
(cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET"
: "PARAMS_GET",
res[1]>>24, (res[1]>>16)&0xFF, res[1]&0xFFFF);
return -((res[1] >> 16) & 0xFF);
}
return 4 + ((res[1] & 0x0000FFFF) << 2);
}
static s32 adpt_i2o_quiesce_hba(adpt_hba* pHba)
{
u32 msg[4];
int ret;
adpt_i2o_status_get(pHba);
if((pHba->status_block->iop_state != ADAPTER_STATE_READY) &&
(pHba->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)){
return 0;
}
msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID;
msg[2] = 0;
msg[3] = 0;
if((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) {
printk(KERN_INFO"dpti%d: Unable to quiesce (status=%#x).\n",
pHba->unit, -ret);
} else {
printk(KERN_INFO"dpti%d: Quiesced.\n",pHba->unit);
}
adpt_i2o_status_get(pHba);
return ret;
}
static int adpt_i2o_enable_hba(adpt_hba* pHba)
{
u32 msg[4];
int ret;
adpt_i2o_status_get(pHba);
if(!pHba->status_block){
return -ENOMEM;
}
if(pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL)
return 0;
if(pHba->status_block->iop_state != ADAPTER_STATE_READY)
return -EINVAL;
msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID;
msg[2]= 0;
msg[3]= 0;
if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) {
printk(KERN_WARNING"%s: Could not enable (status=%#10x).\n",
pHba->name, ret);
} else {
PDEBUG("%s: Enabled.\n", pHba->name);
}
adpt_i2o_status_get(pHba);
return ret;
}
static int adpt_i2o_systab_send(adpt_hba* pHba)
{
u32 msg[12];
int ret;
msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6;
msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID;
msg[2] = 0;
msg[3] = 0;
msg[4] = (0<<16) | ((pHba->unit+2) << 12);
msg[5] = 0;
msg[6] = 0x54000000 | sys_tbl_len;
msg[7] = (u32)sys_tbl_pa;
msg[8] = 0x54000000 | 0;
msg[9] = 0;
msg[10] = 0xD4000000 | 0;
msg[11] = 0;
if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 120))) {
printk(KERN_INFO "%s: Unable to set SysTab (status=%#10x).\n",
pHba->name, ret);
}
#ifdef DEBUG
else {
PINFO("%s: SysTab set.\n", pHba->name);
}
#endif
return ret;
}
#ifdef UARTDELAY
static static void adpt_delay(int millisec)
{
int i;
for (i = 0; i < millisec; i++) {
udelay(1000);
}
}
#endif
static struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.name = "dpt_i2o",
.proc_name = "dpt_i2o",
.show_info = adpt_show_info,
.info = adpt_info,
.queuecommand = adpt_queue,
.eh_abort_handler = adpt_abort,
.eh_device_reset_handler = adpt_device_reset,
.eh_bus_reset_handler = adpt_bus_reset,
.eh_host_reset_handler = adpt_reset,
.bios_param = adpt_bios_param,
.slave_configure = adpt_slave_configure,
.can_queue = MAX_TO_IOP_MESSAGES,
.this_id = 7,
};
static int __init adpt_init(void)
{
int error;
adpt_hba *pHba, *next;
printk("Loading Adaptec I2O RAID: Version " DPT_I2O_VERSION "\n");
error = adpt_detect(&driver_template);
if (error < 0)
return error;
if (hba_chain == NULL)
return -ENODEV;
for (pHba = hba_chain; pHba; pHba = pHba->next) {
error = scsi_add_host(pHba->host, &pHba->pDev->dev);
if (error)
goto fail;
scsi_scan_host(pHba->host);
}
return 0;
fail:
for (pHba = hba_chain; pHba; pHba = next) {
next = pHba->next;
scsi_remove_host(pHba->host);
}
return error;
}
static void __exit adpt_exit(void)
{
adpt_hba *pHba, *next;
for (pHba = hba_chain; pHba; pHba = next) {
next = pHba->next;
adpt_release(pHba);
}
}
module_init(adpt_init);
module_exit(adpt_exit);
MODULE_LICENSE("GPL"