162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2017 Hannes Reinecke, SUSE Linux GmbH <hare@suse.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on the original DAC960 driver, 862306a36Sopenharmony_ci * Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com> 962306a36Sopenharmony_ci * Portions Copyright 2002 by Mylex (An IBM Business Unit) 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/pci.h> 1862306a36Sopenharmony_ci#include <linux/raid_class.h> 1962306a36Sopenharmony_ci#include <asm/unaligned.h> 2062306a36Sopenharmony_ci#include <scsi/scsi.h> 2162306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2262306a36Sopenharmony_ci#include <scsi/scsi_device.h> 2362306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 2462306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 2562306a36Sopenharmony_ci#include "myrb.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct raid_template *myrb_raid_template; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void myrb_monitor(struct work_struct *work); 3062306a36Sopenharmony_cistatic inline void myrb_translate_devstate(void *DeviceState); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic inline int myrb_logical_channel(struct Scsi_Host *shost) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci return shost->max_channel - 1; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct myrb_devstate_name_entry { 3862306a36Sopenharmony_ci enum myrb_devstate state; 3962306a36Sopenharmony_ci const char *name; 4062306a36Sopenharmony_ci} myrb_devstate_name_list[] = { 4162306a36Sopenharmony_ci { MYRB_DEVICE_DEAD, "Dead" }, 4262306a36Sopenharmony_ci { MYRB_DEVICE_WO, "WriteOnly" }, 4362306a36Sopenharmony_ci { MYRB_DEVICE_ONLINE, "Online" }, 4462306a36Sopenharmony_ci { MYRB_DEVICE_CRITICAL, "Critical" }, 4562306a36Sopenharmony_ci { MYRB_DEVICE_STANDBY, "Standby" }, 4662306a36Sopenharmony_ci { MYRB_DEVICE_OFFLINE, "Offline" }, 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const char *myrb_devstate_name(enum myrb_devstate state) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct myrb_devstate_name_entry *entry = myrb_devstate_name_list; 5262306a36Sopenharmony_ci int i; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(myrb_devstate_name_list); i++) { 5562306a36Sopenharmony_ci if (entry[i].state == state) 5662306a36Sopenharmony_ci return entry[i].name; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci return "Unknown"; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct myrb_raidlevel_name_entry { 6262306a36Sopenharmony_ci enum myrb_raidlevel level; 6362306a36Sopenharmony_ci const char *name; 6462306a36Sopenharmony_ci} myrb_raidlevel_name_list[] = { 6562306a36Sopenharmony_ci { MYRB_RAID_LEVEL0, "RAID0" }, 6662306a36Sopenharmony_ci { MYRB_RAID_LEVEL1, "RAID1" }, 6762306a36Sopenharmony_ci { MYRB_RAID_LEVEL3, "RAID3" }, 6862306a36Sopenharmony_ci { MYRB_RAID_LEVEL5, "RAID5" }, 6962306a36Sopenharmony_ci { MYRB_RAID_LEVEL6, "RAID6" }, 7062306a36Sopenharmony_ci { MYRB_RAID_JBOD, "JBOD" }, 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const char *myrb_raidlevel_name(enum myrb_raidlevel level) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct myrb_raidlevel_name_entry *entry = myrb_raidlevel_name_list; 7662306a36Sopenharmony_ci int i; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(myrb_raidlevel_name_list); i++) { 7962306a36Sopenharmony_ci if (entry[i].level == level) 8062306a36Sopenharmony_ci return entry[i].name; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci return NULL; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * myrb_create_mempools - allocates auxiliary data structures 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * Return: true on success, false otherwise. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic bool myrb_create_mempools(struct pci_dev *pdev, struct myrb_hba *cb) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci size_t elem_size, elem_align; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci elem_align = sizeof(struct myrb_sge); 9562306a36Sopenharmony_ci elem_size = cb->host->sg_tablesize * elem_align; 9662306a36Sopenharmony_ci cb->sg_pool = dma_pool_create("myrb_sg", &pdev->dev, 9762306a36Sopenharmony_ci elem_size, elem_align, 0); 9862306a36Sopenharmony_ci if (cb->sg_pool == NULL) { 9962306a36Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 10062306a36Sopenharmony_ci "Failed to allocate SG pool\n"); 10162306a36Sopenharmony_ci return false; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci cb->dcdb_pool = dma_pool_create("myrb_dcdb", &pdev->dev, 10562306a36Sopenharmony_ci sizeof(struct myrb_dcdb), 10662306a36Sopenharmony_ci sizeof(unsigned int), 0); 10762306a36Sopenharmony_ci if (!cb->dcdb_pool) { 10862306a36Sopenharmony_ci dma_pool_destroy(cb->sg_pool); 10962306a36Sopenharmony_ci cb->sg_pool = NULL; 11062306a36Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 11162306a36Sopenharmony_ci "Failed to allocate DCDB pool\n"); 11262306a36Sopenharmony_ci return false; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci snprintf(cb->work_q_name, sizeof(cb->work_q_name), 11662306a36Sopenharmony_ci "myrb_wq_%d", cb->host->host_no); 11762306a36Sopenharmony_ci cb->work_q = create_singlethread_workqueue(cb->work_q_name); 11862306a36Sopenharmony_ci if (!cb->work_q) { 11962306a36Sopenharmony_ci dma_pool_destroy(cb->dcdb_pool); 12062306a36Sopenharmony_ci cb->dcdb_pool = NULL; 12162306a36Sopenharmony_ci dma_pool_destroy(cb->sg_pool); 12262306a36Sopenharmony_ci cb->sg_pool = NULL; 12362306a36Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 12462306a36Sopenharmony_ci "Failed to create workqueue\n"); 12562306a36Sopenharmony_ci return false; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * Initialize the Monitoring Timer. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci INIT_DELAYED_WORK(&cb->monitor_work, myrb_monitor); 13262306a36Sopenharmony_ci queue_delayed_work(cb->work_q, &cb->monitor_work, 1); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return true; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * myrb_destroy_mempools - tears down the memory pools for the controller 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic void myrb_destroy_mempools(struct myrb_hba *cb) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci cancel_delayed_work_sync(&cb->monitor_work); 14362306a36Sopenharmony_ci destroy_workqueue(cb->work_q); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci dma_pool_destroy(cb->sg_pool); 14662306a36Sopenharmony_ci dma_pool_destroy(cb->dcdb_pool); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * myrb_reset_cmd - reset command block 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic inline void myrb_reset_cmd(struct myrb_cmdblk *cmd_blk) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci memset(mbox, 0, sizeof(union myrb_cmd_mbox)); 15762306a36Sopenharmony_ci cmd_blk->status = 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* 16162306a36Sopenharmony_ci * myrb_qcmd - queues command block for execution 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cistatic void myrb_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci void __iomem *base = cb->io_base; 16662306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 16762306a36Sopenharmony_ci union myrb_cmd_mbox *next_mbox = cb->next_cmd_mbox; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci cb->write_cmd_mbox(next_mbox, mbox); 17062306a36Sopenharmony_ci if (cb->prev_cmd_mbox1->words[0] == 0 || 17162306a36Sopenharmony_ci cb->prev_cmd_mbox2->words[0] == 0) 17262306a36Sopenharmony_ci cb->get_cmd_mbox(base); 17362306a36Sopenharmony_ci cb->prev_cmd_mbox2 = cb->prev_cmd_mbox1; 17462306a36Sopenharmony_ci cb->prev_cmd_mbox1 = next_mbox; 17562306a36Sopenharmony_ci if (++next_mbox > cb->last_cmd_mbox) 17662306a36Sopenharmony_ci next_mbox = cb->first_cmd_mbox; 17762306a36Sopenharmony_ci cb->next_cmd_mbox = next_mbox; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* 18162306a36Sopenharmony_ci * myrb_exec_cmd - executes command block and waits for completion. 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * Return: command status 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistatic unsigned short myrb_exec_cmd(struct myrb_hba *cb, 18662306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(cmpl); 18962306a36Sopenharmony_ci unsigned long flags; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci cmd_blk->completion = &cmpl; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 19462306a36Sopenharmony_ci cb->qcmd(cb, cmd_blk); 19562306a36Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci wait_for_completion(&cmpl); 19862306a36Sopenharmony_ci return cmd_blk->status; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* 20262306a36Sopenharmony_ci * myrb_exec_type3 - executes a type 3 command and waits for completion. 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * Return: command status 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic unsigned short myrb_exec_type3(struct myrb_hba *cb, 20762306a36Sopenharmony_ci enum myrb_cmd_opcode op, dma_addr_t addr) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; 21062306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 21162306a36Sopenharmony_ci unsigned short status; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 21462306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 21562306a36Sopenharmony_ci mbox->type3.id = MYRB_DCMD_TAG; 21662306a36Sopenharmony_ci mbox->type3.opcode = op; 21762306a36Sopenharmony_ci mbox->type3.addr = addr; 21862306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 21962306a36Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 22062306a36Sopenharmony_ci return status; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/* 22462306a36Sopenharmony_ci * myrb_exec_type3D - executes a type 3D command and waits for completion. 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * Return: command status 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic unsigned short myrb_exec_type3D(struct myrb_hba *cb, 22962306a36Sopenharmony_ci enum myrb_cmd_opcode op, struct scsi_device *sdev, 23062306a36Sopenharmony_ci struct myrb_pdev_state *pdev_info) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; 23362306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 23462306a36Sopenharmony_ci unsigned short status; 23562306a36Sopenharmony_ci dma_addr_t pdev_info_addr; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci pdev_info_addr = dma_map_single(&cb->pdev->dev, pdev_info, 23862306a36Sopenharmony_ci sizeof(struct myrb_pdev_state), 23962306a36Sopenharmony_ci DMA_FROM_DEVICE); 24062306a36Sopenharmony_ci if (dma_mapping_error(&cb->pdev->dev, pdev_info_addr)) 24162306a36Sopenharmony_ci return MYRB_STATUS_SUBSYS_FAILED; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 24462306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 24562306a36Sopenharmony_ci mbox->type3D.id = MYRB_DCMD_TAG; 24662306a36Sopenharmony_ci mbox->type3D.opcode = op; 24762306a36Sopenharmony_ci mbox->type3D.channel = sdev->channel; 24862306a36Sopenharmony_ci mbox->type3D.target = sdev->id; 24962306a36Sopenharmony_ci mbox->type3D.addr = pdev_info_addr; 25062306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 25162306a36Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 25262306a36Sopenharmony_ci dma_unmap_single(&cb->pdev->dev, pdev_info_addr, 25362306a36Sopenharmony_ci sizeof(struct myrb_pdev_state), DMA_FROM_DEVICE); 25462306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS && 25562306a36Sopenharmony_ci mbox->type3D.opcode == MYRB_CMD_GET_DEVICE_STATE_OLD) 25662306a36Sopenharmony_ci myrb_translate_devstate(pdev_info); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return status; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic char *myrb_event_msg[] = { 26262306a36Sopenharmony_ci "killed because write recovery failed", 26362306a36Sopenharmony_ci "killed because of SCSI bus reset failure", 26462306a36Sopenharmony_ci "killed because of double check condition", 26562306a36Sopenharmony_ci "killed because it was removed", 26662306a36Sopenharmony_ci "killed because of gross error on SCSI chip", 26762306a36Sopenharmony_ci "killed because of bad tag returned from drive", 26862306a36Sopenharmony_ci "killed because of timeout on SCSI command", 26962306a36Sopenharmony_ci "killed because of reset SCSI command issued from system", 27062306a36Sopenharmony_ci "killed because busy or parity error count exceeded limit", 27162306a36Sopenharmony_ci "killed because of 'kill drive' command from system", 27262306a36Sopenharmony_ci "killed because of selection timeout", 27362306a36Sopenharmony_ci "killed due to SCSI phase sequence error", 27462306a36Sopenharmony_ci "killed due to unknown status", 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/** 27862306a36Sopenharmony_ci * myrb_get_event - get event log from HBA 27962306a36Sopenharmony_ci * @cb: pointer to the hba structure 28062306a36Sopenharmony_ci * @event: number of the event 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * Execute a type 3E command and logs the event message 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic void myrb_get_event(struct myrb_hba *cb, unsigned int event) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 28762306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 28862306a36Sopenharmony_ci struct myrb_log_entry *ev_buf; 28962306a36Sopenharmony_ci dma_addr_t ev_addr; 29062306a36Sopenharmony_ci unsigned short status; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ev_buf = dma_alloc_coherent(&cb->pdev->dev, 29362306a36Sopenharmony_ci sizeof(struct myrb_log_entry), 29462306a36Sopenharmony_ci &ev_addr, GFP_KERNEL); 29562306a36Sopenharmony_ci if (!ev_buf) 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 29962306a36Sopenharmony_ci mbox->type3E.id = MYRB_MCMD_TAG; 30062306a36Sopenharmony_ci mbox->type3E.opcode = MYRB_CMD_EVENT_LOG_OPERATION; 30162306a36Sopenharmony_ci mbox->type3E.optype = DAC960_V1_GetEventLogEntry; 30262306a36Sopenharmony_ci mbox->type3E.opqual = 1; 30362306a36Sopenharmony_ci mbox->type3E.ev_seq = event; 30462306a36Sopenharmony_ci mbox->type3E.addr = ev_addr; 30562306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 30662306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) 30762306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 30862306a36Sopenharmony_ci "Failed to get event log %d, status %04x\n", 30962306a36Sopenharmony_ci event, status); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci else if (ev_buf->seq_num == event) { 31262306a36Sopenharmony_ci struct scsi_sense_hdr sshdr; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci memset(&sshdr, 0, sizeof(sshdr)); 31562306a36Sopenharmony_ci scsi_normalize_sense(ev_buf->sense, 32, &sshdr); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (sshdr.sense_key == VENDOR_SPECIFIC && 31862306a36Sopenharmony_ci sshdr.asc == 0x80 && 31962306a36Sopenharmony_ci sshdr.ascq < ARRAY_SIZE(myrb_event_msg)) 32062306a36Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 32162306a36Sopenharmony_ci "Physical drive %d:%d: %s\n", 32262306a36Sopenharmony_ci ev_buf->channel, ev_buf->target, 32362306a36Sopenharmony_ci myrb_event_msg[sshdr.ascq]); 32462306a36Sopenharmony_ci else 32562306a36Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 32662306a36Sopenharmony_ci "Physical drive %d:%d: Sense: %X/%02X/%02X\n", 32762306a36Sopenharmony_ci ev_buf->channel, ev_buf->target, 32862306a36Sopenharmony_ci sshdr.sense_key, sshdr.asc, sshdr.ascq); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_log_entry), 33262306a36Sopenharmony_ci ev_buf, ev_addr); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* 33662306a36Sopenharmony_ci * myrb_get_errtable - retrieves the error table from the controller 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * Executes a type 3 command and logs the error table from the controller. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_cistatic void myrb_get_errtable(struct myrb_hba *cb) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 34362306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 34462306a36Sopenharmony_ci unsigned short status; 34562306a36Sopenharmony_ci struct myrb_error_entry old_table[MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS]; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci memcpy(&old_table, cb->err_table, sizeof(old_table)); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 35062306a36Sopenharmony_ci mbox->type3.id = MYRB_MCMD_TAG; 35162306a36Sopenharmony_ci mbox->type3.opcode = MYRB_CMD_GET_ERROR_TABLE; 35262306a36Sopenharmony_ci mbox->type3.addr = cb->err_table_addr; 35362306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 35462306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 35562306a36Sopenharmony_ci struct myrb_error_entry *table = cb->err_table; 35662306a36Sopenharmony_ci struct myrb_error_entry *new, *old; 35762306a36Sopenharmony_ci size_t err_table_offset; 35862306a36Sopenharmony_ci struct scsi_device *sdev; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci shost_for_each_device(sdev, cb->host) { 36162306a36Sopenharmony_ci if (sdev->channel >= myrb_logical_channel(cb->host)) 36262306a36Sopenharmony_ci continue; 36362306a36Sopenharmony_ci err_table_offset = sdev->channel * MYRB_MAX_TARGETS 36462306a36Sopenharmony_ci + sdev->id; 36562306a36Sopenharmony_ci new = table + err_table_offset; 36662306a36Sopenharmony_ci old = &old_table[err_table_offset]; 36762306a36Sopenharmony_ci if (new->parity_err == old->parity_err && 36862306a36Sopenharmony_ci new->soft_err == old->soft_err && 36962306a36Sopenharmony_ci new->hard_err == old->hard_err && 37062306a36Sopenharmony_ci new->misc_err == old->misc_err) 37162306a36Sopenharmony_ci continue; 37262306a36Sopenharmony_ci sdev_printk(KERN_CRIT, sdev, 37362306a36Sopenharmony_ci "Errors: Parity = %d, Soft = %d, Hard = %d, Misc = %d\n", 37462306a36Sopenharmony_ci new->parity_err, new->soft_err, 37562306a36Sopenharmony_ci new->hard_err, new->misc_err); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* 38162306a36Sopenharmony_ci * myrb_get_ldev_info - retrieves the logical device table from the controller 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * Executes a type 3 command and updates the logical device table. 38462306a36Sopenharmony_ci * 38562306a36Sopenharmony_ci * Return: command status 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_cistatic unsigned short myrb_get_ldev_info(struct myrb_hba *cb) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci unsigned short status; 39062306a36Sopenharmony_ci int ldev_num, ldev_cnt = cb->enquiry->ldev_count; 39162306a36Sopenharmony_ci struct Scsi_Host *shost = cb->host; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_GET_LDEV_INFO, 39462306a36Sopenharmony_ci cb->ldev_info_addr); 39562306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) 39662306a36Sopenharmony_ci return status; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci for (ldev_num = 0; ldev_num < ldev_cnt; ldev_num++) { 39962306a36Sopenharmony_ci struct myrb_ldev_info *old = NULL; 40062306a36Sopenharmony_ci struct myrb_ldev_info *new = cb->ldev_info_buf + ldev_num; 40162306a36Sopenharmony_ci struct scsi_device *sdev; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci sdev = scsi_device_lookup(shost, myrb_logical_channel(shost), 40462306a36Sopenharmony_ci ldev_num, 0); 40562306a36Sopenharmony_ci if (!sdev) { 40662306a36Sopenharmony_ci if (new->state == MYRB_DEVICE_OFFLINE) 40762306a36Sopenharmony_ci continue; 40862306a36Sopenharmony_ci shost_printk(KERN_INFO, shost, 40962306a36Sopenharmony_ci "Adding Logical Drive %d in state %s\n", 41062306a36Sopenharmony_ci ldev_num, myrb_devstate_name(new->state)); 41162306a36Sopenharmony_ci scsi_add_device(shost, myrb_logical_channel(shost), 41262306a36Sopenharmony_ci ldev_num, 0); 41362306a36Sopenharmony_ci continue; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci old = sdev->hostdata; 41662306a36Sopenharmony_ci if (new->state != old->state) 41762306a36Sopenharmony_ci shost_printk(KERN_INFO, shost, 41862306a36Sopenharmony_ci "Logical Drive %d is now %s\n", 41962306a36Sopenharmony_ci ldev_num, myrb_devstate_name(new->state)); 42062306a36Sopenharmony_ci if (new->wb_enabled != old->wb_enabled) 42162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 42262306a36Sopenharmony_ci "Logical Drive is now WRITE %s\n", 42362306a36Sopenharmony_ci (new->wb_enabled ? "BACK" : "THRU")); 42462306a36Sopenharmony_ci memcpy(old, new, sizeof(*new)); 42562306a36Sopenharmony_ci scsi_device_put(sdev); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci return status; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/* 43162306a36Sopenharmony_ci * myrb_get_rbld_progress - get rebuild progress information 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * Executes a type 3 command and returns the rebuild progress 43462306a36Sopenharmony_ci * information. 43562306a36Sopenharmony_ci * 43662306a36Sopenharmony_ci * Return: command status 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_cistatic unsigned short myrb_get_rbld_progress(struct myrb_hba *cb, 43962306a36Sopenharmony_ci struct myrb_rbld_progress *rbld) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 44262306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 44362306a36Sopenharmony_ci struct myrb_rbld_progress *rbld_buf; 44462306a36Sopenharmony_ci dma_addr_t rbld_addr; 44562306a36Sopenharmony_ci unsigned short status; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci rbld_buf = dma_alloc_coherent(&cb->pdev->dev, 44862306a36Sopenharmony_ci sizeof(struct myrb_rbld_progress), 44962306a36Sopenharmony_ci &rbld_addr, GFP_KERNEL); 45062306a36Sopenharmony_ci if (!rbld_buf) 45162306a36Sopenharmony_ci return MYRB_STATUS_RBLD_NOT_CHECKED; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 45462306a36Sopenharmony_ci mbox->type3.id = MYRB_MCMD_TAG; 45562306a36Sopenharmony_ci mbox->type3.opcode = MYRB_CMD_GET_REBUILD_PROGRESS; 45662306a36Sopenharmony_ci mbox->type3.addr = rbld_addr; 45762306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 45862306a36Sopenharmony_ci if (rbld) 45962306a36Sopenharmony_ci memcpy(rbld, rbld_buf, sizeof(struct myrb_rbld_progress)); 46062306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_rbld_progress), 46162306a36Sopenharmony_ci rbld_buf, rbld_addr); 46262306a36Sopenharmony_ci return status; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/* 46662306a36Sopenharmony_ci * myrb_update_rbld_progress - updates the rebuild status 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * Updates the rebuild status for the attached logical devices. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_cistatic void myrb_update_rbld_progress(struct myrb_hba *cb) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct myrb_rbld_progress rbld_buf; 47362306a36Sopenharmony_ci unsigned short status; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci status = myrb_get_rbld_progress(cb, &rbld_buf); 47662306a36Sopenharmony_ci if (status == MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS && 47762306a36Sopenharmony_ci cb->last_rbld_status == MYRB_STATUS_SUCCESS) 47862306a36Sopenharmony_ci status = MYRB_STATUS_RBLD_SUCCESS; 47962306a36Sopenharmony_ci if (status != MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS) { 48062306a36Sopenharmony_ci unsigned int blocks_done = 48162306a36Sopenharmony_ci rbld_buf.ldev_size - rbld_buf.blocks_left; 48262306a36Sopenharmony_ci struct scsi_device *sdev; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci sdev = scsi_device_lookup(cb->host, 48562306a36Sopenharmony_ci myrb_logical_channel(cb->host), 48662306a36Sopenharmony_ci rbld_buf.ldev_num, 0); 48762306a36Sopenharmony_ci if (!sdev) 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci switch (status) { 49162306a36Sopenharmony_ci case MYRB_STATUS_SUCCESS: 49262306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 49362306a36Sopenharmony_ci "Rebuild in Progress, %d%% completed\n", 49462306a36Sopenharmony_ci (100 * (blocks_done >> 7)) 49562306a36Sopenharmony_ci / (rbld_buf.ldev_size >> 7)); 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case MYRB_STATUS_RBLD_FAILED_LDEV_FAILURE: 49862306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 49962306a36Sopenharmony_ci "Rebuild Failed due to Logical Drive Failure\n"); 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci case MYRB_STATUS_RBLD_FAILED_BADBLOCKS: 50262306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 50362306a36Sopenharmony_ci "Rebuild Failed due to Bad Blocks on Other Drives\n"); 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci case MYRB_STATUS_RBLD_FAILED_NEW_DRIVE_FAILED: 50662306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 50762306a36Sopenharmony_ci "Rebuild Failed due to Failure of Drive Being Rebuilt\n"); 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci case MYRB_STATUS_RBLD_SUCCESS: 51062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 51162306a36Sopenharmony_ci "Rebuild Completed Successfully\n"); 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci case MYRB_STATUS_RBLD_SUCCESS_TERMINATED: 51462306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 51562306a36Sopenharmony_ci "Rebuild Successfully Terminated\n"); 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci default: 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci scsi_device_put(sdev); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci cb->last_rbld_status = status; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci/* 52662306a36Sopenharmony_ci * myrb_get_cc_progress - retrieve the rebuild status 52762306a36Sopenharmony_ci * 52862306a36Sopenharmony_ci * Execute a type 3 Command and fetch the rebuild / consistency check 52962306a36Sopenharmony_ci * status. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic void myrb_get_cc_progress(struct myrb_hba *cb) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 53462306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 53562306a36Sopenharmony_ci struct myrb_rbld_progress *rbld_buf; 53662306a36Sopenharmony_ci dma_addr_t rbld_addr; 53762306a36Sopenharmony_ci unsigned short status; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci rbld_buf = dma_alloc_coherent(&cb->pdev->dev, 54062306a36Sopenharmony_ci sizeof(struct myrb_rbld_progress), 54162306a36Sopenharmony_ci &rbld_addr, GFP_KERNEL); 54262306a36Sopenharmony_ci if (!rbld_buf) { 54362306a36Sopenharmony_ci cb->need_cc_status = true; 54462306a36Sopenharmony_ci return; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 54762306a36Sopenharmony_ci mbox->type3.id = MYRB_MCMD_TAG; 54862306a36Sopenharmony_ci mbox->type3.opcode = MYRB_CMD_REBUILD_STAT; 54962306a36Sopenharmony_ci mbox->type3.addr = rbld_addr; 55062306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 55162306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 55262306a36Sopenharmony_ci unsigned int ldev_num = rbld_buf->ldev_num; 55362306a36Sopenharmony_ci unsigned int ldev_size = rbld_buf->ldev_size; 55462306a36Sopenharmony_ci unsigned int blocks_done = 55562306a36Sopenharmony_ci ldev_size - rbld_buf->blocks_left; 55662306a36Sopenharmony_ci struct scsi_device *sdev; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci sdev = scsi_device_lookup(cb->host, 55962306a36Sopenharmony_ci myrb_logical_channel(cb->host), 56062306a36Sopenharmony_ci ldev_num, 0); 56162306a36Sopenharmony_ci if (sdev) { 56262306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 56362306a36Sopenharmony_ci "Consistency Check in Progress: %d%% completed\n", 56462306a36Sopenharmony_ci (100 * (blocks_done >> 7)) 56562306a36Sopenharmony_ci / (ldev_size >> 7)); 56662306a36Sopenharmony_ci scsi_device_put(sdev); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_rbld_progress), 57062306a36Sopenharmony_ci rbld_buf, rbld_addr); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* 57462306a36Sopenharmony_ci * myrb_bgi_control - updates background initialisation status 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * Executes a type 3B command and updates the background initialisation status 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_cistatic void myrb_bgi_control(struct myrb_hba *cb) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 58162306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 58262306a36Sopenharmony_ci struct myrb_bgi_status *bgi, *last_bgi; 58362306a36Sopenharmony_ci dma_addr_t bgi_addr; 58462306a36Sopenharmony_ci struct scsi_device *sdev = NULL; 58562306a36Sopenharmony_ci unsigned short status; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci bgi = dma_alloc_coherent(&cb->pdev->dev, sizeof(struct myrb_bgi_status), 58862306a36Sopenharmony_ci &bgi_addr, GFP_KERNEL); 58962306a36Sopenharmony_ci if (!bgi) { 59062306a36Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 59162306a36Sopenharmony_ci "Failed to allocate bgi memory\n"); 59262306a36Sopenharmony_ci return; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 59562306a36Sopenharmony_ci mbox->type3B.id = MYRB_DCMD_TAG; 59662306a36Sopenharmony_ci mbox->type3B.opcode = MYRB_CMD_BGI_CONTROL; 59762306a36Sopenharmony_ci mbox->type3B.optype = 0x20; 59862306a36Sopenharmony_ci mbox->type3B.addr = bgi_addr; 59962306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 60062306a36Sopenharmony_ci last_bgi = &cb->bgi_status; 60162306a36Sopenharmony_ci sdev = scsi_device_lookup(cb->host, 60262306a36Sopenharmony_ci myrb_logical_channel(cb->host), 60362306a36Sopenharmony_ci bgi->ldev_num, 0); 60462306a36Sopenharmony_ci switch (status) { 60562306a36Sopenharmony_ci case MYRB_STATUS_SUCCESS: 60662306a36Sopenharmony_ci switch (bgi->status) { 60762306a36Sopenharmony_ci case MYRB_BGI_INVALID: 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci case MYRB_BGI_STARTED: 61062306a36Sopenharmony_ci if (!sdev) 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 61362306a36Sopenharmony_ci "Background Initialization Started\n"); 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci case MYRB_BGI_INPROGRESS: 61662306a36Sopenharmony_ci if (!sdev) 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci if (bgi->blocks_done == last_bgi->blocks_done && 61962306a36Sopenharmony_ci bgi->ldev_num == last_bgi->ldev_num) 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 62262306a36Sopenharmony_ci "Background Initialization in Progress: %d%% completed\n", 62362306a36Sopenharmony_ci (100 * (bgi->blocks_done >> 7)) 62462306a36Sopenharmony_ci / (bgi->ldev_size >> 7)); 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case MYRB_BGI_SUSPENDED: 62762306a36Sopenharmony_ci if (!sdev) 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 63062306a36Sopenharmony_ci "Background Initialization Suspended\n"); 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci case MYRB_BGI_CANCELLED: 63362306a36Sopenharmony_ci if (!sdev) 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 63662306a36Sopenharmony_ci "Background Initialization Cancelled\n"); 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci memcpy(&cb->bgi_status, bgi, sizeof(struct myrb_bgi_status)); 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case MYRB_STATUS_BGI_SUCCESS: 64262306a36Sopenharmony_ci if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS) 64362306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 64462306a36Sopenharmony_ci "Background Initialization Completed Successfully\n"); 64562306a36Sopenharmony_ci cb->bgi_status.status = MYRB_BGI_INVALID; 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci case MYRB_STATUS_BGI_ABORTED: 64862306a36Sopenharmony_ci if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS) 64962306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 65062306a36Sopenharmony_ci "Background Initialization Aborted\n"); 65162306a36Sopenharmony_ci fallthrough; 65262306a36Sopenharmony_ci case MYRB_STATUS_NO_BGI_INPROGRESS: 65362306a36Sopenharmony_ci cb->bgi_status.status = MYRB_BGI_INVALID; 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci if (sdev) 65762306a36Sopenharmony_ci scsi_device_put(sdev); 65862306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_bgi_status), 65962306a36Sopenharmony_ci bgi, bgi_addr); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci/* 66362306a36Sopenharmony_ci * myrb_hba_enquiry - updates the controller status 66462306a36Sopenharmony_ci * 66562306a36Sopenharmony_ci * Executes a DAC_V1_Enquiry command and updates the controller status. 66662306a36Sopenharmony_ci * 66762306a36Sopenharmony_ci * Return: command status 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_cistatic unsigned short myrb_hba_enquiry(struct myrb_hba *cb) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct myrb_enquiry old, *new; 67262306a36Sopenharmony_ci unsigned short status; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci memcpy(&old, cb->enquiry, sizeof(struct myrb_enquiry)); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY, cb->enquiry_addr); 67762306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) 67862306a36Sopenharmony_ci return status; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci new = cb->enquiry; 68162306a36Sopenharmony_ci if (new->ldev_count > old.ldev_count) { 68262306a36Sopenharmony_ci int ldev_num = old.ldev_count - 1; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci while (++ldev_num < new->ldev_count) 68562306a36Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 68662306a36Sopenharmony_ci "Logical Drive %d Now Exists\n", 68762306a36Sopenharmony_ci ldev_num); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci if (new->ldev_count < old.ldev_count) { 69062306a36Sopenharmony_ci int ldev_num = new->ldev_count - 1; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci while (++ldev_num < old.ldev_count) 69362306a36Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 69462306a36Sopenharmony_ci "Logical Drive %d No Longer Exists\n", 69562306a36Sopenharmony_ci ldev_num); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci if (new->status.deferred != old.status.deferred) 69862306a36Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 69962306a36Sopenharmony_ci "Deferred Write Error Flag is now %s\n", 70062306a36Sopenharmony_ci (new->status.deferred ? "TRUE" : "FALSE")); 70162306a36Sopenharmony_ci if (new->ev_seq != old.ev_seq) { 70262306a36Sopenharmony_ci cb->new_ev_seq = new->ev_seq; 70362306a36Sopenharmony_ci cb->need_err_info = true; 70462306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 70562306a36Sopenharmony_ci "Event log %d/%d (%d/%d) available\n", 70662306a36Sopenharmony_ci cb->old_ev_seq, cb->new_ev_seq, 70762306a36Sopenharmony_ci old.ev_seq, new->ev_seq); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci if ((new->ldev_critical > 0 && 71062306a36Sopenharmony_ci new->ldev_critical != old.ldev_critical) || 71162306a36Sopenharmony_ci (new->ldev_offline > 0 && 71262306a36Sopenharmony_ci new->ldev_offline != old.ldev_offline) || 71362306a36Sopenharmony_ci (new->ldev_count != old.ldev_count)) { 71462306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 71562306a36Sopenharmony_ci "Logical drive count changed (%d/%d/%d)\n", 71662306a36Sopenharmony_ci new->ldev_critical, 71762306a36Sopenharmony_ci new->ldev_offline, 71862306a36Sopenharmony_ci new->ldev_count); 71962306a36Sopenharmony_ci cb->need_ldev_info = true; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci if (new->pdev_dead > 0 || 72262306a36Sopenharmony_ci new->pdev_dead != old.pdev_dead || 72362306a36Sopenharmony_ci time_after_eq(jiffies, cb->secondary_monitor_time 72462306a36Sopenharmony_ci + MYRB_SECONDARY_MONITOR_INTERVAL)) { 72562306a36Sopenharmony_ci cb->need_bgi_status = cb->bgi_status_supported; 72662306a36Sopenharmony_ci cb->secondary_monitor_time = jiffies; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci if (new->rbld == MYRB_STDBY_RBLD_IN_PROGRESS || 72962306a36Sopenharmony_ci new->rbld == MYRB_BG_RBLD_IN_PROGRESS || 73062306a36Sopenharmony_ci old.rbld == MYRB_STDBY_RBLD_IN_PROGRESS || 73162306a36Sopenharmony_ci old.rbld == MYRB_BG_RBLD_IN_PROGRESS) { 73262306a36Sopenharmony_ci cb->need_rbld = true; 73362306a36Sopenharmony_ci cb->rbld_first = (new->ldev_critical < old.ldev_critical); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci if (old.rbld == MYRB_BG_CHECK_IN_PROGRESS) 73662306a36Sopenharmony_ci switch (new->rbld) { 73762306a36Sopenharmony_ci case MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS: 73862306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 73962306a36Sopenharmony_ci "Consistency Check Completed Successfully\n"); 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci case MYRB_STDBY_RBLD_IN_PROGRESS: 74262306a36Sopenharmony_ci case MYRB_BG_RBLD_IN_PROGRESS: 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case MYRB_BG_CHECK_IN_PROGRESS: 74562306a36Sopenharmony_ci cb->need_cc_status = true; 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case MYRB_STDBY_RBLD_COMPLETED_WITH_ERROR: 74862306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 74962306a36Sopenharmony_ci "Consistency Check Completed with Error\n"); 75062306a36Sopenharmony_ci break; 75162306a36Sopenharmony_ci case MYRB_BG_RBLD_OR_CHECK_FAILED_DRIVE_FAILED: 75262306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 75362306a36Sopenharmony_ci "Consistency Check Failed - Physical Device Failed\n"); 75462306a36Sopenharmony_ci break; 75562306a36Sopenharmony_ci case MYRB_BG_RBLD_OR_CHECK_FAILED_LDEV_FAILED: 75662306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 75762306a36Sopenharmony_ci "Consistency Check Failed - Logical Drive Failed\n"); 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci case MYRB_BG_RBLD_OR_CHECK_FAILED_OTHER: 76062306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 76162306a36Sopenharmony_ci "Consistency Check Failed - Other Causes\n"); 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci case MYRB_BG_RBLD_OR_CHECK_SUCCESS_TERMINATED: 76462306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 76562306a36Sopenharmony_ci "Consistency Check Successfully Terminated\n"); 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci else if (new->rbld == MYRB_BG_CHECK_IN_PROGRESS) 76962306a36Sopenharmony_ci cb->need_cc_status = true; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return MYRB_STATUS_SUCCESS; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/* 77562306a36Sopenharmony_ci * myrb_set_pdev_state - sets the device state for a physical device 77662306a36Sopenharmony_ci * 77762306a36Sopenharmony_ci * Return: command status 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_cistatic unsigned short myrb_set_pdev_state(struct myrb_hba *cb, 78062306a36Sopenharmony_ci struct scsi_device *sdev, enum myrb_devstate state) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; 78362306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 78462306a36Sopenharmony_ci unsigned short status; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 78762306a36Sopenharmony_ci mbox->type3D.opcode = MYRB_CMD_START_DEVICE; 78862306a36Sopenharmony_ci mbox->type3D.id = MYRB_DCMD_TAG; 78962306a36Sopenharmony_ci mbox->type3D.channel = sdev->channel; 79062306a36Sopenharmony_ci mbox->type3D.target = sdev->id; 79162306a36Sopenharmony_ci mbox->type3D.state = state & 0x1F; 79262306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 79362306a36Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return status; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci/* 79962306a36Sopenharmony_ci * myrb_enable_mmio - enables the Memory Mailbox Interface 80062306a36Sopenharmony_ci * 80162306a36Sopenharmony_ci * PD and P controller types have no memory mailbox, but still need the 80262306a36Sopenharmony_ci * other dma mapped memory. 80362306a36Sopenharmony_ci * 80462306a36Sopenharmony_ci * Return: true on success, false otherwise. 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_cistatic bool myrb_enable_mmio(struct myrb_hba *cb, mbox_mmio_init_t mmio_init_fn) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci void __iomem *base = cb->io_base; 80962306a36Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 81062306a36Sopenharmony_ci size_t err_table_size; 81162306a36Sopenharmony_ci size_t ldev_info_size; 81262306a36Sopenharmony_ci union myrb_cmd_mbox *cmd_mbox_mem; 81362306a36Sopenharmony_ci struct myrb_stat_mbox *stat_mbox_mem; 81462306a36Sopenharmony_ci union myrb_cmd_mbox mbox; 81562306a36Sopenharmony_ci unsigned short status; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci memset(&mbox, 0, sizeof(union myrb_cmd_mbox)); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { 82062306a36Sopenharmony_ci dev_err(&pdev->dev, "DMA mask out of range\n"); 82162306a36Sopenharmony_ci return false; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci cb->enquiry = dma_alloc_coherent(&pdev->dev, 82562306a36Sopenharmony_ci sizeof(struct myrb_enquiry), 82662306a36Sopenharmony_ci &cb->enquiry_addr, GFP_KERNEL); 82762306a36Sopenharmony_ci if (!cb->enquiry) 82862306a36Sopenharmony_ci return false; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci err_table_size = sizeof(struct myrb_error_entry) * 83162306a36Sopenharmony_ci MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS; 83262306a36Sopenharmony_ci cb->err_table = dma_alloc_coherent(&pdev->dev, err_table_size, 83362306a36Sopenharmony_ci &cb->err_table_addr, GFP_KERNEL); 83462306a36Sopenharmony_ci if (!cb->err_table) 83562306a36Sopenharmony_ci return false; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci ldev_info_size = sizeof(struct myrb_ldev_info) * MYRB_MAX_LDEVS; 83862306a36Sopenharmony_ci cb->ldev_info_buf = dma_alloc_coherent(&pdev->dev, ldev_info_size, 83962306a36Sopenharmony_ci &cb->ldev_info_addr, GFP_KERNEL); 84062306a36Sopenharmony_ci if (!cb->ldev_info_buf) 84162306a36Sopenharmony_ci return false; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* 84462306a36Sopenharmony_ci * Skip mailbox initialisation for PD and P Controllers 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_ci if (!mmio_init_fn) 84762306a36Sopenharmony_ci return true; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* These are the base addresses for the command memory mailbox array */ 85062306a36Sopenharmony_ci cb->cmd_mbox_size = MYRB_CMD_MBOX_COUNT * sizeof(union myrb_cmd_mbox); 85162306a36Sopenharmony_ci cb->first_cmd_mbox = dma_alloc_coherent(&pdev->dev, 85262306a36Sopenharmony_ci cb->cmd_mbox_size, 85362306a36Sopenharmony_ci &cb->cmd_mbox_addr, 85462306a36Sopenharmony_ci GFP_KERNEL); 85562306a36Sopenharmony_ci if (!cb->first_cmd_mbox) 85662306a36Sopenharmony_ci return false; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci cmd_mbox_mem = cb->first_cmd_mbox; 85962306a36Sopenharmony_ci cmd_mbox_mem += MYRB_CMD_MBOX_COUNT - 1; 86062306a36Sopenharmony_ci cb->last_cmd_mbox = cmd_mbox_mem; 86162306a36Sopenharmony_ci cb->next_cmd_mbox = cb->first_cmd_mbox; 86262306a36Sopenharmony_ci cb->prev_cmd_mbox1 = cb->last_cmd_mbox; 86362306a36Sopenharmony_ci cb->prev_cmd_mbox2 = cb->last_cmd_mbox - 1; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* These are the base addresses for the status memory mailbox array */ 86662306a36Sopenharmony_ci cb->stat_mbox_size = MYRB_STAT_MBOX_COUNT * 86762306a36Sopenharmony_ci sizeof(struct myrb_stat_mbox); 86862306a36Sopenharmony_ci cb->first_stat_mbox = dma_alloc_coherent(&pdev->dev, 86962306a36Sopenharmony_ci cb->stat_mbox_size, 87062306a36Sopenharmony_ci &cb->stat_mbox_addr, 87162306a36Sopenharmony_ci GFP_KERNEL); 87262306a36Sopenharmony_ci if (!cb->first_stat_mbox) 87362306a36Sopenharmony_ci return false; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci stat_mbox_mem = cb->first_stat_mbox; 87662306a36Sopenharmony_ci stat_mbox_mem += MYRB_STAT_MBOX_COUNT - 1; 87762306a36Sopenharmony_ci cb->last_stat_mbox = stat_mbox_mem; 87862306a36Sopenharmony_ci cb->next_stat_mbox = cb->first_stat_mbox; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* Enable the Memory Mailbox Interface. */ 88162306a36Sopenharmony_ci cb->dual_mode_interface = true; 88262306a36Sopenharmony_ci mbox.typeX.opcode = 0x2B; 88362306a36Sopenharmony_ci mbox.typeX.id = 0; 88462306a36Sopenharmony_ci mbox.typeX.opcode2 = 0x14; 88562306a36Sopenharmony_ci mbox.typeX.cmd_mbox_addr = cb->cmd_mbox_addr; 88662306a36Sopenharmony_ci mbox.typeX.stat_mbox_addr = cb->stat_mbox_addr; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci status = mmio_init_fn(pdev, base, &mbox); 88962306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 89062306a36Sopenharmony_ci cb->dual_mode_interface = false; 89162306a36Sopenharmony_ci mbox.typeX.opcode2 = 0x10; 89262306a36Sopenharmony_ci status = mmio_init_fn(pdev, base, &mbox); 89362306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 89462306a36Sopenharmony_ci dev_err(&pdev->dev, 89562306a36Sopenharmony_ci "Failed to enable mailbox, statux %02X\n", 89662306a36Sopenharmony_ci status); 89762306a36Sopenharmony_ci return false; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci return true; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci/* 90462306a36Sopenharmony_ci * myrb_get_hba_config - reads the configuration information 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * Reads the configuration information from the controller and 90762306a36Sopenharmony_ci * initializes the controller structure. 90862306a36Sopenharmony_ci * 90962306a36Sopenharmony_ci * Return: 0 on success, errno otherwise 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_cistatic int myrb_get_hba_config(struct myrb_hba *cb) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct myrb_enquiry2 *enquiry2; 91462306a36Sopenharmony_ci dma_addr_t enquiry2_addr; 91562306a36Sopenharmony_ci struct myrb_config2 *config2; 91662306a36Sopenharmony_ci dma_addr_t config2_addr; 91762306a36Sopenharmony_ci struct Scsi_Host *shost = cb->host; 91862306a36Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 91962306a36Sopenharmony_ci int pchan_max = 0, pchan_cur = 0; 92062306a36Sopenharmony_ci unsigned short status; 92162306a36Sopenharmony_ci int ret = -ENODEV, memsize = 0; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci enquiry2 = dma_alloc_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), 92462306a36Sopenharmony_ci &enquiry2_addr, GFP_KERNEL); 92562306a36Sopenharmony_ci if (!enquiry2) { 92662306a36Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 92762306a36Sopenharmony_ci "Failed to allocate V1 enquiry2 memory\n"); 92862306a36Sopenharmony_ci return -ENOMEM; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci config2 = dma_alloc_coherent(&pdev->dev, sizeof(struct myrb_config2), 93162306a36Sopenharmony_ci &config2_addr, GFP_KERNEL); 93262306a36Sopenharmony_ci if (!config2) { 93362306a36Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 93462306a36Sopenharmony_ci "Failed to allocate V1 config2 memory\n"); 93562306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), 93662306a36Sopenharmony_ci enquiry2, enquiry2_addr); 93762306a36Sopenharmony_ci return -ENOMEM; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci mutex_lock(&cb->dma_mutex); 94062306a36Sopenharmony_ci status = myrb_hba_enquiry(cb); 94162306a36Sopenharmony_ci mutex_unlock(&cb->dma_mutex); 94262306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 94362306a36Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 94462306a36Sopenharmony_ci "Failed it issue V1 Enquiry\n"); 94562306a36Sopenharmony_ci goto out_free; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY2, enquiry2_addr); 94962306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 95062306a36Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 95162306a36Sopenharmony_ci "Failed to issue V1 Enquiry2\n"); 95262306a36Sopenharmony_ci goto out_free; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_READ_CONFIG2, config2_addr); 95662306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 95762306a36Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 95862306a36Sopenharmony_ci "Failed to issue ReadConfig2\n"); 95962306a36Sopenharmony_ci goto out_free; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci status = myrb_get_ldev_info(cb); 96362306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 96462306a36Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 96562306a36Sopenharmony_ci "Failed to get logical drive information\n"); 96662306a36Sopenharmony_ci goto out_free; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* 97062306a36Sopenharmony_ci * Initialize the Controller Model Name and Full Model Name fields. 97162306a36Sopenharmony_ci */ 97262306a36Sopenharmony_ci switch (enquiry2->hw.sub_model) { 97362306a36Sopenharmony_ci case DAC960_V1_P_PD_PU: 97462306a36Sopenharmony_ci if (enquiry2->scsi_cap.bus_speed == MYRB_SCSI_SPEED_ULTRA) 97562306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PU"); 97662306a36Sopenharmony_ci else 97762306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PD"); 97862306a36Sopenharmony_ci break; 97962306a36Sopenharmony_ci case DAC960_V1_PL: 98062306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PL"); 98162306a36Sopenharmony_ci break; 98262306a36Sopenharmony_ci case DAC960_V1_PG: 98362306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PG"); 98462306a36Sopenharmony_ci break; 98562306a36Sopenharmony_ci case DAC960_V1_PJ: 98662306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PJ"); 98762306a36Sopenharmony_ci break; 98862306a36Sopenharmony_ci case DAC960_V1_PR: 98962306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PR"); 99062306a36Sopenharmony_ci break; 99162306a36Sopenharmony_ci case DAC960_V1_PT: 99262306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PT"); 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci case DAC960_V1_PTL0: 99562306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PTL0"); 99662306a36Sopenharmony_ci break; 99762306a36Sopenharmony_ci case DAC960_V1_PRL: 99862306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PRL"); 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci case DAC960_V1_PTL1: 100162306a36Sopenharmony_ci strcpy(cb->model_name, "DAC960PTL1"); 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci case DAC960_V1_1164P: 100462306a36Sopenharmony_ci strcpy(cb->model_name, "eXtremeRAID 1100"); 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci default: 100762306a36Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 100862306a36Sopenharmony_ci "Unknown Model %X\n", 100962306a36Sopenharmony_ci enquiry2->hw.sub_model); 101062306a36Sopenharmony_ci goto out; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci /* 101362306a36Sopenharmony_ci * Initialize the Controller Firmware Version field and verify that it 101462306a36Sopenharmony_ci * is a supported firmware version. 101562306a36Sopenharmony_ci * The supported firmware versions are: 101662306a36Sopenharmony_ci * 101762306a36Sopenharmony_ci * DAC1164P 5.06 and above 101862306a36Sopenharmony_ci * DAC960PTL/PRL/PJ/PG 4.06 and above 101962306a36Sopenharmony_ci * DAC960PU/PD/PL 3.51 and above 102062306a36Sopenharmony_ci * DAC960PU/PD/PL/P 2.73 and above 102162306a36Sopenharmony_ci */ 102262306a36Sopenharmony_ci#if defined(CONFIG_ALPHA) 102362306a36Sopenharmony_ci /* 102462306a36Sopenharmony_ci * DEC Alpha machines were often equipped with DAC960 cards that were 102562306a36Sopenharmony_ci * OEMed from Mylex, and had their own custom firmware. Version 2.70, 102662306a36Sopenharmony_ci * the last custom FW revision to be released by DEC for these older 102762306a36Sopenharmony_ci * controllers, appears to work quite well with this driver. 102862306a36Sopenharmony_ci * 102962306a36Sopenharmony_ci * Cards tested successfully were several versions each of the PD and 103062306a36Sopenharmony_ci * PU, called by DEC the KZPSC and KZPAC, respectively, and having 103162306a36Sopenharmony_ci * the Manufacturer Numbers (from Mylex), usually on a sticker on the 103262306a36Sopenharmony_ci * back of the board, of: 103362306a36Sopenharmony_ci * 103462306a36Sopenharmony_ci * KZPSC: D040347 (1-channel) or D040348 (2-channel) 103562306a36Sopenharmony_ci * or D040349 (3-channel) 103662306a36Sopenharmony_ci * KZPAC: D040395 (1-channel) or D040396 (2-channel) 103762306a36Sopenharmony_ci * or D040397 (3-channel) 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci# define FIRMWARE_27X "2.70" 104062306a36Sopenharmony_ci#else 104162306a36Sopenharmony_ci# define FIRMWARE_27X "2.73" 104262306a36Sopenharmony_ci#endif 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (enquiry2->fw.major_version == 0) { 104562306a36Sopenharmony_ci enquiry2->fw.major_version = cb->enquiry->fw_major_version; 104662306a36Sopenharmony_ci enquiry2->fw.minor_version = cb->enquiry->fw_minor_version; 104762306a36Sopenharmony_ci enquiry2->fw.firmware_type = '0'; 104862306a36Sopenharmony_ci enquiry2->fw.turn_id = 0; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci snprintf(cb->fw_version, sizeof(cb->fw_version), 105162306a36Sopenharmony_ci "%u.%02u-%c-%02u", 105262306a36Sopenharmony_ci enquiry2->fw.major_version, 105362306a36Sopenharmony_ci enquiry2->fw.minor_version, 105462306a36Sopenharmony_ci enquiry2->fw.firmware_type, 105562306a36Sopenharmony_ci enquiry2->fw.turn_id); 105662306a36Sopenharmony_ci if (!((enquiry2->fw.major_version == 5 && 105762306a36Sopenharmony_ci enquiry2->fw.minor_version >= 6) || 105862306a36Sopenharmony_ci (enquiry2->fw.major_version == 4 && 105962306a36Sopenharmony_ci enquiry2->fw.minor_version >= 6) || 106062306a36Sopenharmony_ci (enquiry2->fw.major_version == 3 && 106162306a36Sopenharmony_ci enquiry2->fw.minor_version >= 51) || 106262306a36Sopenharmony_ci (enquiry2->fw.major_version == 2 && 106362306a36Sopenharmony_ci strcmp(cb->fw_version, FIRMWARE_27X) >= 0))) { 106462306a36Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 106562306a36Sopenharmony_ci "Firmware Version '%s' unsupported\n", 106662306a36Sopenharmony_ci cb->fw_version); 106762306a36Sopenharmony_ci goto out; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci /* 107062306a36Sopenharmony_ci * Initialize the Channels, Targets, Memory Size, and SAF-TE 107162306a36Sopenharmony_ci * Enclosure Management Enabled fields. 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ci switch (enquiry2->hw.model) { 107462306a36Sopenharmony_ci case MYRB_5_CHANNEL_BOARD: 107562306a36Sopenharmony_ci pchan_max = 5; 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci case MYRB_3_CHANNEL_BOARD: 107862306a36Sopenharmony_ci case MYRB_3_CHANNEL_ASIC_DAC: 107962306a36Sopenharmony_ci pchan_max = 3; 108062306a36Sopenharmony_ci break; 108162306a36Sopenharmony_ci case MYRB_2_CHANNEL_BOARD: 108262306a36Sopenharmony_ci pchan_max = 2; 108362306a36Sopenharmony_ci break; 108462306a36Sopenharmony_ci default: 108562306a36Sopenharmony_ci pchan_max = enquiry2->cfg_chan; 108662306a36Sopenharmony_ci break; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci pchan_cur = enquiry2->cur_chan; 108962306a36Sopenharmony_ci if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_32BIT) 109062306a36Sopenharmony_ci cb->bus_width = 32; 109162306a36Sopenharmony_ci else if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_16BIT) 109262306a36Sopenharmony_ci cb->bus_width = 16; 109362306a36Sopenharmony_ci else 109462306a36Sopenharmony_ci cb->bus_width = 8; 109562306a36Sopenharmony_ci cb->ldev_block_size = enquiry2->ldev_block_size; 109662306a36Sopenharmony_ci shost->max_channel = pchan_cur; 109762306a36Sopenharmony_ci shost->max_id = enquiry2->max_targets; 109862306a36Sopenharmony_ci memsize = enquiry2->mem_size >> 20; 109962306a36Sopenharmony_ci cb->safte_enabled = (enquiry2->fault_mgmt == MYRB_FAULT_SAFTE); 110062306a36Sopenharmony_ci /* 110162306a36Sopenharmony_ci * Initialize the Controller Queue Depth, Driver Queue Depth, 110262306a36Sopenharmony_ci * Logical Drive Count, Maximum Blocks per Command, Controller 110362306a36Sopenharmony_ci * Scatter/Gather Limit, and Driver Scatter/Gather Limit. 110462306a36Sopenharmony_ci * The Driver Queue Depth must be at most one less than the 110562306a36Sopenharmony_ci * Controller Queue Depth to allow for an automatic drive 110662306a36Sopenharmony_ci * rebuild operation. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci shost->can_queue = cb->enquiry->max_tcq; 110962306a36Sopenharmony_ci if (shost->can_queue < 3) 111062306a36Sopenharmony_ci shost->can_queue = enquiry2->max_cmds; 111162306a36Sopenharmony_ci if (shost->can_queue < 3) 111262306a36Sopenharmony_ci /* Play safe and disable TCQ */ 111362306a36Sopenharmony_ci shost->can_queue = 1; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (shost->can_queue > MYRB_CMD_MBOX_COUNT - 2) 111662306a36Sopenharmony_ci shost->can_queue = MYRB_CMD_MBOX_COUNT - 2; 111762306a36Sopenharmony_ci shost->max_sectors = enquiry2->max_sectors; 111862306a36Sopenharmony_ci shost->sg_tablesize = enquiry2->max_sge; 111962306a36Sopenharmony_ci if (shost->sg_tablesize > MYRB_SCATTER_GATHER_LIMIT) 112062306a36Sopenharmony_ci shost->sg_tablesize = MYRB_SCATTER_GATHER_LIMIT; 112162306a36Sopenharmony_ci /* 112262306a36Sopenharmony_ci * Initialize the Stripe Size, Segment Size, and Geometry Translation. 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_ci cb->stripe_size = config2->blocks_per_stripe * config2->block_factor 112562306a36Sopenharmony_ci >> (10 - MYRB_BLKSIZE_BITS); 112662306a36Sopenharmony_ci cb->segment_size = config2->blocks_per_cacheline * config2->block_factor 112762306a36Sopenharmony_ci >> (10 - MYRB_BLKSIZE_BITS); 112862306a36Sopenharmony_ci /* Assume 255/63 translation */ 112962306a36Sopenharmony_ci cb->ldev_geom_heads = 255; 113062306a36Sopenharmony_ci cb->ldev_geom_sectors = 63; 113162306a36Sopenharmony_ci if (config2->drive_geometry) { 113262306a36Sopenharmony_ci cb->ldev_geom_heads = 128; 113362306a36Sopenharmony_ci cb->ldev_geom_sectors = 32; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* 113762306a36Sopenharmony_ci * Initialize the Background Initialization Status. 113862306a36Sopenharmony_ci */ 113962306a36Sopenharmony_ci if ((cb->fw_version[0] == '4' && 114062306a36Sopenharmony_ci strcmp(cb->fw_version, "4.08") >= 0) || 114162306a36Sopenharmony_ci (cb->fw_version[0] == '5' && 114262306a36Sopenharmony_ci strcmp(cb->fw_version, "5.08") >= 0)) { 114362306a36Sopenharmony_ci cb->bgi_status_supported = true; 114462306a36Sopenharmony_ci myrb_bgi_control(cb); 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci cb->last_rbld_status = MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS; 114762306a36Sopenharmony_ci ret = 0; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ciout: 115062306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 115162306a36Sopenharmony_ci "Configuring %s PCI RAID Controller\n", cb->model_name); 115262306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 115362306a36Sopenharmony_ci " Firmware Version: %s, Memory Size: %dMB\n", 115462306a36Sopenharmony_ci cb->fw_version, memsize); 115562306a36Sopenharmony_ci if (cb->io_addr == 0) 115662306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 115762306a36Sopenharmony_ci " I/O Address: n/a, PCI Address: 0x%lX, IRQ Channel: %d\n", 115862306a36Sopenharmony_ci (unsigned long)cb->pci_addr, cb->irq); 115962306a36Sopenharmony_ci else 116062306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 116162306a36Sopenharmony_ci " I/O Address: 0x%lX, PCI Address: 0x%lX, IRQ Channel: %d\n", 116262306a36Sopenharmony_ci (unsigned long)cb->io_addr, (unsigned long)cb->pci_addr, 116362306a36Sopenharmony_ci cb->irq); 116462306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 116562306a36Sopenharmony_ci " Controller Queue Depth: %d, Maximum Blocks per Command: %d\n", 116662306a36Sopenharmony_ci cb->host->can_queue, cb->host->max_sectors); 116762306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 116862306a36Sopenharmony_ci " Driver Queue Depth: %d, Scatter/Gather Limit: %d of %d Segments\n", 116962306a36Sopenharmony_ci cb->host->can_queue, cb->host->sg_tablesize, 117062306a36Sopenharmony_ci MYRB_SCATTER_GATHER_LIMIT); 117162306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 117262306a36Sopenharmony_ci " Stripe Size: %dKB, Segment Size: %dKB, BIOS Geometry: %d/%d%s\n", 117362306a36Sopenharmony_ci cb->stripe_size, cb->segment_size, 117462306a36Sopenharmony_ci cb->ldev_geom_heads, cb->ldev_geom_sectors, 117562306a36Sopenharmony_ci cb->safte_enabled ? 117662306a36Sopenharmony_ci " SAF-TE Enclosure Management Enabled" : ""); 117762306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 117862306a36Sopenharmony_ci " Physical: %d/%d channels %d/%d/%d devices\n", 117962306a36Sopenharmony_ci pchan_cur, pchan_max, 0, cb->enquiry->pdev_dead, 118062306a36Sopenharmony_ci cb->host->max_id); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 118362306a36Sopenharmony_ci " Logical: 1/1 channels, %d/%d disks\n", 118462306a36Sopenharmony_ci cb->enquiry->ldev_count, MYRB_MAX_LDEVS); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ciout_free: 118762306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), 118862306a36Sopenharmony_ci enquiry2, enquiry2_addr); 118962306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct myrb_config2), 119062306a36Sopenharmony_ci config2, config2_addr); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci return ret; 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci/* 119662306a36Sopenharmony_ci * myrb_unmap - unmaps controller structures 119762306a36Sopenharmony_ci */ 119862306a36Sopenharmony_cistatic void myrb_unmap(struct myrb_hba *cb) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci if (cb->ldev_info_buf) { 120162306a36Sopenharmony_ci size_t ldev_info_size = sizeof(struct myrb_ldev_info) * 120262306a36Sopenharmony_ci MYRB_MAX_LDEVS; 120362306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, ldev_info_size, 120462306a36Sopenharmony_ci cb->ldev_info_buf, cb->ldev_info_addr); 120562306a36Sopenharmony_ci cb->ldev_info_buf = NULL; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci if (cb->err_table) { 120862306a36Sopenharmony_ci size_t err_table_size = sizeof(struct myrb_error_entry) * 120962306a36Sopenharmony_ci MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS; 121062306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, err_table_size, 121162306a36Sopenharmony_ci cb->err_table, cb->err_table_addr); 121262306a36Sopenharmony_ci cb->err_table = NULL; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci if (cb->enquiry) { 121562306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_enquiry), 121662306a36Sopenharmony_ci cb->enquiry, cb->enquiry_addr); 121762306a36Sopenharmony_ci cb->enquiry = NULL; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci if (cb->first_stat_mbox) { 122062306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, cb->stat_mbox_size, 122162306a36Sopenharmony_ci cb->first_stat_mbox, cb->stat_mbox_addr); 122262306a36Sopenharmony_ci cb->first_stat_mbox = NULL; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci if (cb->first_cmd_mbox) { 122562306a36Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, cb->cmd_mbox_size, 122662306a36Sopenharmony_ci cb->first_cmd_mbox, cb->cmd_mbox_addr); 122762306a36Sopenharmony_ci cb->first_cmd_mbox = NULL; 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci/* 123262306a36Sopenharmony_ci * myrb_cleanup - cleanup controller structures 123362306a36Sopenharmony_ci */ 123462306a36Sopenharmony_cistatic void myrb_cleanup(struct myrb_hba *cb) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* Free the memory mailbox, status, and related structures */ 123962306a36Sopenharmony_ci myrb_unmap(cb); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci if (cb->mmio_base) { 124262306a36Sopenharmony_ci if (cb->disable_intr) 124362306a36Sopenharmony_ci cb->disable_intr(cb->io_base); 124462306a36Sopenharmony_ci iounmap(cb->mmio_base); 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci if (cb->irq) 124762306a36Sopenharmony_ci free_irq(cb->irq, cb); 124862306a36Sopenharmony_ci if (cb->io_addr) 124962306a36Sopenharmony_ci release_region(cb->io_addr, 0x80); 125062306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 125162306a36Sopenharmony_ci pci_disable_device(pdev); 125262306a36Sopenharmony_ci scsi_host_put(cb->host); 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic int myrb_host_reset(struct scsi_cmnd *scmd) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci struct Scsi_Host *shost = scmd->device->host; 125862306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci cb->reset(cb->io_base); 126162306a36Sopenharmony_ci return SUCCESS; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cistatic int myrb_pthru_queuecommand(struct Scsi_Host *shost, 126562306a36Sopenharmony_ci struct scsi_cmnd *scmd) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct request *rq = scsi_cmd_to_rq(scmd); 126862306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 126962306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd); 127062306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 127162306a36Sopenharmony_ci struct myrb_dcdb *dcdb; 127262306a36Sopenharmony_ci dma_addr_t dcdb_addr; 127362306a36Sopenharmony_ci struct scsi_device *sdev = scmd->device; 127462306a36Sopenharmony_ci struct scatterlist *sgl; 127562306a36Sopenharmony_ci unsigned long flags; 127662306a36Sopenharmony_ci int nsge; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 127962306a36Sopenharmony_ci dcdb = dma_pool_alloc(cb->dcdb_pool, GFP_ATOMIC, &dcdb_addr); 128062306a36Sopenharmony_ci if (!dcdb) 128162306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 128262306a36Sopenharmony_ci nsge = scsi_dma_map(scmd); 128362306a36Sopenharmony_ci if (nsge > 1) { 128462306a36Sopenharmony_ci dma_pool_free(cb->dcdb_pool, dcdb, dcdb_addr); 128562306a36Sopenharmony_ci scmd->result = (DID_ERROR << 16); 128662306a36Sopenharmony_ci scsi_done(scmd); 128762306a36Sopenharmony_ci return 0; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci mbox->type3.opcode = MYRB_CMD_DCDB; 129162306a36Sopenharmony_ci mbox->type3.id = rq->tag + 3; 129262306a36Sopenharmony_ci mbox->type3.addr = dcdb_addr; 129362306a36Sopenharmony_ci dcdb->channel = sdev->channel; 129462306a36Sopenharmony_ci dcdb->target = sdev->id; 129562306a36Sopenharmony_ci switch (scmd->sc_data_direction) { 129662306a36Sopenharmony_ci case DMA_NONE: 129762306a36Sopenharmony_ci dcdb->data_xfer = MYRB_DCDB_XFER_NONE; 129862306a36Sopenharmony_ci break; 129962306a36Sopenharmony_ci case DMA_TO_DEVICE: 130062306a36Sopenharmony_ci dcdb->data_xfer = MYRB_DCDB_XFER_SYSTEM_TO_DEVICE; 130162306a36Sopenharmony_ci break; 130262306a36Sopenharmony_ci case DMA_FROM_DEVICE: 130362306a36Sopenharmony_ci dcdb->data_xfer = MYRB_DCDB_XFER_DEVICE_TO_SYSTEM; 130462306a36Sopenharmony_ci break; 130562306a36Sopenharmony_ci default: 130662306a36Sopenharmony_ci dcdb->data_xfer = MYRB_DCDB_XFER_ILLEGAL; 130762306a36Sopenharmony_ci break; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci dcdb->early_status = false; 131062306a36Sopenharmony_ci if (rq->timeout <= 10) 131162306a36Sopenharmony_ci dcdb->timeout = MYRB_DCDB_TMO_10_SECS; 131262306a36Sopenharmony_ci else if (rq->timeout <= 60) 131362306a36Sopenharmony_ci dcdb->timeout = MYRB_DCDB_TMO_60_SECS; 131462306a36Sopenharmony_ci else if (rq->timeout <= 600) 131562306a36Sopenharmony_ci dcdb->timeout = MYRB_DCDB_TMO_10_MINS; 131662306a36Sopenharmony_ci else 131762306a36Sopenharmony_ci dcdb->timeout = MYRB_DCDB_TMO_24_HRS; 131862306a36Sopenharmony_ci dcdb->no_autosense = false; 131962306a36Sopenharmony_ci dcdb->allow_disconnect = true; 132062306a36Sopenharmony_ci sgl = scsi_sglist(scmd); 132162306a36Sopenharmony_ci dcdb->dma_addr = sg_dma_address(sgl); 132262306a36Sopenharmony_ci if (sg_dma_len(sgl) > USHRT_MAX) { 132362306a36Sopenharmony_ci dcdb->xfer_len_lo = sg_dma_len(sgl) & 0xffff; 132462306a36Sopenharmony_ci dcdb->xfer_len_hi4 = sg_dma_len(sgl) >> 16; 132562306a36Sopenharmony_ci } else { 132662306a36Sopenharmony_ci dcdb->xfer_len_lo = sg_dma_len(sgl); 132762306a36Sopenharmony_ci dcdb->xfer_len_hi4 = 0; 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci dcdb->cdb_len = scmd->cmd_len; 133062306a36Sopenharmony_ci dcdb->sense_len = sizeof(dcdb->sense); 133162306a36Sopenharmony_ci memcpy(&dcdb->cdb, scmd->cmnd, scmd->cmd_len); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 133462306a36Sopenharmony_ci cb->qcmd(cb, cmd_blk); 133562306a36Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 133662306a36Sopenharmony_ci return 0; 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_cistatic void myrb_inquiry(struct myrb_hba *cb, 134062306a36Sopenharmony_ci struct scsi_cmnd *scmd) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci unsigned char inq[36] = { 134362306a36Sopenharmony_ci 0x00, 0x00, 0x03, 0x02, 0x20, 0x00, 0x01, 0x00, 134462306a36Sopenharmony_ci 0x4d, 0x59, 0x4c, 0x45, 0x58, 0x20, 0x20, 0x20, 134562306a36Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 134662306a36Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 134762306a36Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 134862306a36Sopenharmony_ci }; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (cb->bus_width > 16) 135162306a36Sopenharmony_ci inq[7] |= 1 << 6; 135262306a36Sopenharmony_ci if (cb->bus_width > 8) 135362306a36Sopenharmony_ci inq[7] |= 1 << 5; 135462306a36Sopenharmony_ci memcpy(&inq[16], cb->model_name, 16); 135562306a36Sopenharmony_ci memcpy(&inq[32], cb->fw_version, 1); 135662306a36Sopenharmony_ci memcpy(&inq[33], &cb->fw_version[2], 2); 135762306a36Sopenharmony_ci memcpy(&inq[35], &cb->fw_version[7], 1); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci scsi_sg_copy_from_buffer(scmd, (void *)inq, 36); 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cistatic void 136362306a36Sopenharmony_cimyrb_mode_sense(struct myrb_hba *cb, struct scsi_cmnd *scmd, 136462306a36Sopenharmony_ci struct myrb_ldev_info *ldev_info) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci unsigned char modes[32], *mode_pg; 136762306a36Sopenharmony_ci bool dbd; 136862306a36Sopenharmony_ci size_t mode_len; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci dbd = (scmd->cmnd[1] & 0x08) == 0x08; 137162306a36Sopenharmony_ci if (dbd) { 137262306a36Sopenharmony_ci mode_len = 24; 137362306a36Sopenharmony_ci mode_pg = &modes[4]; 137462306a36Sopenharmony_ci } else { 137562306a36Sopenharmony_ci mode_len = 32; 137662306a36Sopenharmony_ci mode_pg = &modes[12]; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci memset(modes, 0, sizeof(modes)); 137962306a36Sopenharmony_ci modes[0] = mode_len - 1; 138062306a36Sopenharmony_ci if (!dbd) { 138162306a36Sopenharmony_ci unsigned char *block_desc = &modes[4]; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci modes[3] = 8; 138462306a36Sopenharmony_ci put_unaligned_be32(ldev_info->size, &block_desc[0]); 138562306a36Sopenharmony_ci put_unaligned_be32(cb->ldev_block_size, &block_desc[5]); 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci mode_pg[0] = 0x08; 138862306a36Sopenharmony_ci mode_pg[1] = 0x12; 138962306a36Sopenharmony_ci if (ldev_info->wb_enabled) 139062306a36Sopenharmony_ci mode_pg[2] |= 0x04; 139162306a36Sopenharmony_ci if (cb->segment_size) { 139262306a36Sopenharmony_ci mode_pg[2] |= 0x08; 139362306a36Sopenharmony_ci put_unaligned_be16(cb->segment_size, &mode_pg[14]); 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci scsi_sg_copy_from_buffer(scmd, modes, mode_len); 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic void myrb_request_sense(struct myrb_hba *cb, 140062306a36Sopenharmony_ci struct scsi_cmnd *scmd) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci scsi_build_sense(scmd, 0, NO_SENSE, 0, 0); 140362306a36Sopenharmony_ci scsi_sg_copy_from_buffer(scmd, scmd->sense_buffer, 140462306a36Sopenharmony_ci SCSI_SENSE_BUFFERSIZE); 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic void myrb_read_capacity(struct myrb_hba *cb, struct scsi_cmnd *scmd, 140862306a36Sopenharmony_ci struct myrb_ldev_info *ldev_info) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci unsigned char data[8]; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, 141362306a36Sopenharmony_ci "Capacity %u, blocksize %u\n", 141462306a36Sopenharmony_ci ldev_info->size, cb->ldev_block_size); 141562306a36Sopenharmony_ci put_unaligned_be32(ldev_info->size - 1, &data[0]); 141662306a36Sopenharmony_ci put_unaligned_be32(cb->ldev_block_size, &data[4]); 141762306a36Sopenharmony_ci scsi_sg_copy_from_buffer(scmd, data, 8); 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic int myrb_ldev_queuecommand(struct Scsi_Host *shost, 142162306a36Sopenharmony_ci struct scsi_cmnd *scmd) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 142462306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd); 142562306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 142662306a36Sopenharmony_ci struct myrb_ldev_info *ldev_info; 142762306a36Sopenharmony_ci struct scsi_device *sdev = scmd->device; 142862306a36Sopenharmony_ci struct scatterlist *sgl; 142962306a36Sopenharmony_ci unsigned long flags; 143062306a36Sopenharmony_ci u64 lba; 143162306a36Sopenharmony_ci u32 block_cnt; 143262306a36Sopenharmony_ci int nsge; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci ldev_info = sdev->hostdata; 143562306a36Sopenharmony_ci if (ldev_info->state != MYRB_DEVICE_ONLINE && 143662306a36Sopenharmony_ci ldev_info->state != MYRB_DEVICE_WO) { 143762306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, "ldev %u in state %x, skip\n", 143862306a36Sopenharmony_ci sdev->id, ldev_info ? ldev_info->state : 0xff); 143962306a36Sopenharmony_ci scmd->result = (DID_BAD_TARGET << 16); 144062306a36Sopenharmony_ci scsi_done(scmd); 144162306a36Sopenharmony_ci return 0; 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci switch (scmd->cmnd[0]) { 144462306a36Sopenharmony_ci case TEST_UNIT_READY: 144562306a36Sopenharmony_ci scmd->result = (DID_OK << 16); 144662306a36Sopenharmony_ci scsi_done(scmd); 144762306a36Sopenharmony_ci return 0; 144862306a36Sopenharmony_ci case INQUIRY: 144962306a36Sopenharmony_ci if (scmd->cmnd[1] & 1) { 145062306a36Sopenharmony_ci /* Illegal request, invalid field in CDB */ 145162306a36Sopenharmony_ci scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); 145262306a36Sopenharmony_ci } else { 145362306a36Sopenharmony_ci myrb_inquiry(cb, scmd); 145462306a36Sopenharmony_ci scmd->result = (DID_OK << 16); 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci scsi_done(scmd); 145762306a36Sopenharmony_ci return 0; 145862306a36Sopenharmony_ci case SYNCHRONIZE_CACHE: 145962306a36Sopenharmony_ci scmd->result = (DID_OK << 16); 146062306a36Sopenharmony_ci scsi_done(scmd); 146162306a36Sopenharmony_ci return 0; 146262306a36Sopenharmony_ci case MODE_SENSE: 146362306a36Sopenharmony_ci if ((scmd->cmnd[2] & 0x3F) != 0x3F && 146462306a36Sopenharmony_ci (scmd->cmnd[2] & 0x3F) != 0x08) { 146562306a36Sopenharmony_ci /* Illegal request, invalid field in CDB */ 146662306a36Sopenharmony_ci scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); 146762306a36Sopenharmony_ci } else { 146862306a36Sopenharmony_ci myrb_mode_sense(cb, scmd, ldev_info); 146962306a36Sopenharmony_ci scmd->result = (DID_OK << 16); 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci scsi_done(scmd); 147262306a36Sopenharmony_ci return 0; 147362306a36Sopenharmony_ci case READ_CAPACITY: 147462306a36Sopenharmony_ci if ((scmd->cmnd[1] & 1) || 147562306a36Sopenharmony_ci (scmd->cmnd[8] & 1)) { 147662306a36Sopenharmony_ci /* Illegal request, invalid field in CDB */ 147762306a36Sopenharmony_ci scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); 147862306a36Sopenharmony_ci scsi_done(scmd); 147962306a36Sopenharmony_ci return 0; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci lba = get_unaligned_be32(&scmd->cmnd[2]); 148262306a36Sopenharmony_ci if (lba) { 148362306a36Sopenharmony_ci /* Illegal request, invalid field in CDB */ 148462306a36Sopenharmony_ci scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); 148562306a36Sopenharmony_ci scsi_done(scmd); 148662306a36Sopenharmony_ci return 0; 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci myrb_read_capacity(cb, scmd, ldev_info); 148962306a36Sopenharmony_ci scsi_done(scmd); 149062306a36Sopenharmony_ci return 0; 149162306a36Sopenharmony_ci case REQUEST_SENSE: 149262306a36Sopenharmony_ci myrb_request_sense(cb, scmd); 149362306a36Sopenharmony_ci scmd->result = (DID_OK << 16); 149462306a36Sopenharmony_ci return 0; 149562306a36Sopenharmony_ci case SEND_DIAGNOSTIC: 149662306a36Sopenharmony_ci if (scmd->cmnd[1] != 0x04) { 149762306a36Sopenharmony_ci /* Illegal request, invalid field in CDB */ 149862306a36Sopenharmony_ci scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0); 149962306a36Sopenharmony_ci } else { 150062306a36Sopenharmony_ci /* Assume good status */ 150162306a36Sopenharmony_ci scmd->result = (DID_OK << 16); 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci scsi_done(scmd); 150462306a36Sopenharmony_ci return 0; 150562306a36Sopenharmony_ci case READ_6: 150662306a36Sopenharmony_ci if (ldev_info->state == MYRB_DEVICE_WO) { 150762306a36Sopenharmony_ci /* Data protect, attempt to read invalid data */ 150862306a36Sopenharmony_ci scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06); 150962306a36Sopenharmony_ci scsi_done(scmd); 151062306a36Sopenharmony_ci return 0; 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci fallthrough; 151362306a36Sopenharmony_ci case WRITE_6: 151462306a36Sopenharmony_ci lba = (((scmd->cmnd[1] & 0x1F) << 16) | 151562306a36Sopenharmony_ci (scmd->cmnd[2] << 8) | 151662306a36Sopenharmony_ci scmd->cmnd[3]); 151762306a36Sopenharmony_ci block_cnt = scmd->cmnd[4]; 151862306a36Sopenharmony_ci break; 151962306a36Sopenharmony_ci case READ_10: 152062306a36Sopenharmony_ci if (ldev_info->state == MYRB_DEVICE_WO) { 152162306a36Sopenharmony_ci /* Data protect, attempt to read invalid data */ 152262306a36Sopenharmony_ci scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06); 152362306a36Sopenharmony_ci scsi_done(scmd); 152462306a36Sopenharmony_ci return 0; 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci fallthrough; 152762306a36Sopenharmony_ci case WRITE_10: 152862306a36Sopenharmony_ci case VERIFY: /* 0x2F */ 152962306a36Sopenharmony_ci case WRITE_VERIFY: /* 0x2E */ 153062306a36Sopenharmony_ci lba = get_unaligned_be32(&scmd->cmnd[2]); 153162306a36Sopenharmony_ci block_cnt = get_unaligned_be16(&scmd->cmnd[7]); 153262306a36Sopenharmony_ci break; 153362306a36Sopenharmony_ci case READ_12: 153462306a36Sopenharmony_ci if (ldev_info->state == MYRB_DEVICE_WO) { 153562306a36Sopenharmony_ci /* Data protect, attempt to read invalid data */ 153662306a36Sopenharmony_ci scsi_build_sense(scmd, 0, DATA_PROTECT, 0x21, 0x06); 153762306a36Sopenharmony_ci scsi_done(scmd); 153862306a36Sopenharmony_ci return 0; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci fallthrough; 154162306a36Sopenharmony_ci case WRITE_12: 154262306a36Sopenharmony_ci case VERIFY_12: /* 0xAF */ 154362306a36Sopenharmony_ci case WRITE_VERIFY_12: /* 0xAE */ 154462306a36Sopenharmony_ci lba = get_unaligned_be32(&scmd->cmnd[2]); 154562306a36Sopenharmony_ci block_cnt = get_unaligned_be32(&scmd->cmnd[6]); 154662306a36Sopenharmony_ci break; 154762306a36Sopenharmony_ci default: 154862306a36Sopenharmony_ci /* Illegal request, invalid opcode */ 154962306a36Sopenharmony_ci scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x20, 0); 155062306a36Sopenharmony_ci scsi_done(scmd); 155162306a36Sopenharmony_ci return 0; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 155562306a36Sopenharmony_ci mbox->type5.id = scsi_cmd_to_rq(scmd)->tag + 3; 155662306a36Sopenharmony_ci if (scmd->sc_data_direction == DMA_NONE) 155762306a36Sopenharmony_ci goto submit; 155862306a36Sopenharmony_ci nsge = scsi_dma_map(scmd); 155962306a36Sopenharmony_ci if (nsge == 1) { 156062306a36Sopenharmony_ci sgl = scsi_sglist(scmd); 156162306a36Sopenharmony_ci if (scmd->sc_data_direction == DMA_FROM_DEVICE) 156262306a36Sopenharmony_ci mbox->type5.opcode = MYRB_CMD_READ; 156362306a36Sopenharmony_ci else 156462306a36Sopenharmony_ci mbox->type5.opcode = MYRB_CMD_WRITE; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci mbox->type5.ld.xfer_len = block_cnt; 156762306a36Sopenharmony_ci mbox->type5.ld.ldev_num = sdev->id; 156862306a36Sopenharmony_ci mbox->type5.lba = lba; 156962306a36Sopenharmony_ci mbox->type5.addr = (u32)sg_dma_address(sgl); 157062306a36Sopenharmony_ci } else { 157162306a36Sopenharmony_ci struct myrb_sge *hw_sgl; 157262306a36Sopenharmony_ci dma_addr_t hw_sgl_addr; 157362306a36Sopenharmony_ci int i; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci hw_sgl = dma_pool_alloc(cb->sg_pool, GFP_ATOMIC, &hw_sgl_addr); 157662306a36Sopenharmony_ci if (!hw_sgl) 157762306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci cmd_blk->sgl = hw_sgl; 158062306a36Sopenharmony_ci cmd_blk->sgl_addr = hw_sgl_addr; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (scmd->sc_data_direction == DMA_FROM_DEVICE) 158362306a36Sopenharmony_ci mbox->type5.opcode = MYRB_CMD_READ_SG; 158462306a36Sopenharmony_ci else 158562306a36Sopenharmony_ci mbox->type5.opcode = MYRB_CMD_WRITE_SG; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci mbox->type5.ld.xfer_len = block_cnt; 158862306a36Sopenharmony_ci mbox->type5.ld.ldev_num = sdev->id; 158962306a36Sopenharmony_ci mbox->type5.lba = lba; 159062306a36Sopenharmony_ci mbox->type5.addr = hw_sgl_addr; 159162306a36Sopenharmony_ci mbox->type5.sg_count = nsge; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci scsi_for_each_sg(scmd, sgl, nsge, i) { 159462306a36Sopenharmony_ci hw_sgl->sge_addr = (u32)sg_dma_address(sgl); 159562306a36Sopenharmony_ci hw_sgl->sge_count = (u32)sg_dma_len(sgl); 159662306a36Sopenharmony_ci hw_sgl++; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_cisubmit: 160062306a36Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 160162306a36Sopenharmony_ci cb->qcmd(cb, cmd_blk); 160262306a36Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci return 0; 160562306a36Sopenharmony_ci} 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_cistatic int myrb_queuecommand(struct Scsi_Host *shost, 160862306a36Sopenharmony_ci struct scsi_cmnd *scmd) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci struct scsi_device *sdev = scmd->device; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (sdev->channel > myrb_logical_channel(shost)) { 161362306a36Sopenharmony_ci scmd->result = (DID_BAD_TARGET << 16); 161462306a36Sopenharmony_ci scsi_done(scmd); 161562306a36Sopenharmony_ci return 0; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci if (sdev->channel == myrb_logical_channel(shost)) 161862306a36Sopenharmony_ci return myrb_ldev_queuecommand(shost, scmd); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci return myrb_pthru_queuecommand(shost, scmd); 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cistatic int myrb_ldev_slave_alloc(struct scsi_device *sdev) 162462306a36Sopenharmony_ci{ 162562306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 162662306a36Sopenharmony_ci struct myrb_ldev_info *ldev_info; 162762306a36Sopenharmony_ci unsigned short ldev_num = sdev->id; 162862306a36Sopenharmony_ci enum raid_level level; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci ldev_info = cb->ldev_info_buf + ldev_num; 163162306a36Sopenharmony_ci if (!ldev_info) 163262306a36Sopenharmony_ci return -ENXIO; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci sdev->hostdata = kzalloc(sizeof(*ldev_info), GFP_KERNEL); 163562306a36Sopenharmony_ci if (!sdev->hostdata) 163662306a36Sopenharmony_ci return -ENOMEM; 163762306a36Sopenharmony_ci dev_dbg(&sdev->sdev_gendev, 163862306a36Sopenharmony_ci "slave alloc ldev %d state %x\n", 163962306a36Sopenharmony_ci ldev_num, ldev_info->state); 164062306a36Sopenharmony_ci memcpy(sdev->hostdata, ldev_info, 164162306a36Sopenharmony_ci sizeof(*ldev_info)); 164262306a36Sopenharmony_ci switch (ldev_info->raid_level) { 164362306a36Sopenharmony_ci case MYRB_RAID_LEVEL0: 164462306a36Sopenharmony_ci level = RAID_LEVEL_LINEAR; 164562306a36Sopenharmony_ci break; 164662306a36Sopenharmony_ci case MYRB_RAID_LEVEL1: 164762306a36Sopenharmony_ci level = RAID_LEVEL_1; 164862306a36Sopenharmony_ci break; 164962306a36Sopenharmony_ci case MYRB_RAID_LEVEL3: 165062306a36Sopenharmony_ci level = RAID_LEVEL_3; 165162306a36Sopenharmony_ci break; 165262306a36Sopenharmony_ci case MYRB_RAID_LEVEL5: 165362306a36Sopenharmony_ci level = RAID_LEVEL_5; 165462306a36Sopenharmony_ci break; 165562306a36Sopenharmony_ci case MYRB_RAID_LEVEL6: 165662306a36Sopenharmony_ci level = RAID_LEVEL_6; 165762306a36Sopenharmony_ci break; 165862306a36Sopenharmony_ci case MYRB_RAID_JBOD: 165962306a36Sopenharmony_ci level = RAID_LEVEL_JBOD; 166062306a36Sopenharmony_ci break; 166162306a36Sopenharmony_ci default: 166262306a36Sopenharmony_ci level = RAID_LEVEL_UNKNOWN; 166362306a36Sopenharmony_ci break; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci raid_set_level(myrb_raid_template, &sdev->sdev_gendev, level); 166662306a36Sopenharmony_ci return 0; 166762306a36Sopenharmony_ci} 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cistatic int myrb_pdev_slave_alloc(struct scsi_device *sdev) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 167262306a36Sopenharmony_ci struct myrb_pdev_state *pdev_info; 167362306a36Sopenharmony_ci unsigned short status; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if (sdev->id > MYRB_MAX_TARGETS) 167662306a36Sopenharmony_ci return -ENXIO; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci pdev_info = kzalloc(sizeof(*pdev_info), GFP_KERNEL); 167962306a36Sopenharmony_ci if (!pdev_info) 168062306a36Sopenharmony_ci return -ENOMEM; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE, 168362306a36Sopenharmony_ci sdev, pdev_info); 168462306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 168562306a36Sopenharmony_ci dev_dbg(&sdev->sdev_gendev, 168662306a36Sopenharmony_ci "Failed to get device state, status %x\n", 168762306a36Sopenharmony_ci status); 168862306a36Sopenharmony_ci kfree(pdev_info); 168962306a36Sopenharmony_ci return -ENXIO; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci if (!pdev_info->present) { 169262306a36Sopenharmony_ci dev_dbg(&sdev->sdev_gendev, 169362306a36Sopenharmony_ci "device not present, skip\n"); 169462306a36Sopenharmony_ci kfree(pdev_info); 169562306a36Sopenharmony_ci return -ENXIO; 169662306a36Sopenharmony_ci } 169762306a36Sopenharmony_ci dev_dbg(&sdev->sdev_gendev, 169862306a36Sopenharmony_ci "slave alloc pdev %d:%d state %x\n", 169962306a36Sopenharmony_ci sdev->channel, sdev->id, pdev_info->state); 170062306a36Sopenharmony_ci sdev->hostdata = pdev_info; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci return 0; 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_cistatic int myrb_slave_alloc(struct scsi_device *sdev) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci if (sdev->channel > myrb_logical_channel(sdev->host)) 170862306a36Sopenharmony_ci return -ENXIO; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci if (sdev->lun > 0) 171162306a36Sopenharmony_ci return -ENXIO; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (sdev->channel == myrb_logical_channel(sdev->host)) 171462306a36Sopenharmony_ci return myrb_ldev_slave_alloc(sdev); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci return myrb_pdev_slave_alloc(sdev); 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic int myrb_slave_configure(struct scsi_device *sdev) 172062306a36Sopenharmony_ci{ 172162306a36Sopenharmony_ci struct myrb_ldev_info *ldev_info; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (sdev->channel > myrb_logical_channel(sdev->host)) 172462306a36Sopenharmony_ci return -ENXIO; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host)) { 172762306a36Sopenharmony_ci sdev->no_uld_attach = 1; 172862306a36Sopenharmony_ci return 0; 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci if (sdev->lun != 0) 173162306a36Sopenharmony_ci return -ENXIO; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci ldev_info = sdev->hostdata; 173462306a36Sopenharmony_ci if (!ldev_info) 173562306a36Sopenharmony_ci return -ENXIO; 173662306a36Sopenharmony_ci if (ldev_info->state != MYRB_DEVICE_ONLINE) 173762306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 173862306a36Sopenharmony_ci "Logical drive is %s\n", 173962306a36Sopenharmony_ci myrb_devstate_name(ldev_info->state)); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci sdev->tagged_supported = 1; 174262306a36Sopenharmony_ci return 0; 174362306a36Sopenharmony_ci} 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_cistatic void myrb_slave_destroy(struct scsi_device *sdev) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci kfree(sdev->hostdata); 174862306a36Sopenharmony_ci} 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cistatic int myrb_biosparam(struct scsi_device *sdev, struct block_device *bdev, 175162306a36Sopenharmony_ci sector_t capacity, int geom[]) 175262306a36Sopenharmony_ci{ 175362306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci geom[0] = cb->ldev_geom_heads; 175662306a36Sopenharmony_ci geom[1] = cb->ldev_geom_sectors; 175762306a36Sopenharmony_ci geom[2] = sector_div(capacity, geom[0] * geom[1]); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci return 0; 176062306a36Sopenharmony_ci} 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_cistatic ssize_t raid_state_show(struct device *dev, 176362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 176662306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 176762306a36Sopenharmony_ci int ret; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (!sdev->hostdata) 177062306a36Sopenharmony_ci return snprintf(buf, 16, "Unknown\n"); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (sdev->channel == myrb_logical_channel(sdev->host)) { 177362306a36Sopenharmony_ci struct myrb_ldev_info *ldev_info = sdev->hostdata; 177462306a36Sopenharmony_ci const char *name; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci name = myrb_devstate_name(ldev_info->state); 177762306a36Sopenharmony_ci if (name) 177862306a36Sopenharmony_ci ret = snprintf(buf, 32, "%s\n", name); 177962306a36Sopenharmony_ci else 178062306a36Sopenharmony_ci ret = snprintf(buf, 32, "Invalid (%02X)\n", 178162306a36Sopenharmony_ci ldev_info->state); 178262306a36Sopenharmony_ci } else { 178362306a36Sopenharmony_ci struct myrb_pdev_state *pdev_info = sdev->hostdata; 178462306a36Sopenharmony_ci unsigned short status; 178562306a36Sopenharmony_ci const char *name; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE, 178862306a36Sopenharmony_ci sdev, pdev_info); 178962306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) 179062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 179162306a36Sopenharmony_ci "Failed to get device state, status %x\n", 179262306a36Sopenharmony_ci status); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci if (!pdev_info->present) 179562306a36Sopenharmony_ci name = "Removed"; 179662306a36Sopenharmony_ci else 179762306a36Sopenharmony_ci name = myrb_devstate_name(pdev_info->state); 179862306a36Sopenharmony_ci if (name) 179962306a36Sopenharmony_ci ret = snprintf(buf, 32, "%s\n", name); 180062306a36Sopenharmony_ci else 180162306a36Sopenharmony_ci ret = snprintf(buf, 32, "Invalid (%02X)\n", 180262306a36Sopenharmony_ci pdev_info->state); 180362306a36Sopenharmony_ci } 180462306a36Sopenharmony_ci return ret; 180562306a36Sopenharmony_ci} 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_cistatic ssize_t raid_state_store(struct device *dev, 180862306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 180962306a36Sopenharmony_ci{ 181062306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 181162306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 181262306a36Sopenharmony_ci struct myrb_pdev_state *pdev_info; 181362306a36Sopenharmony_ci enum myrb_devstate new_state; 181462306a36Sopenharmony_ci unsigned short status; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci if (!strncmp(buf, "kill", 4) || 181762306a36Sopenharmony_ci !strncmp(buf, "offline", 7)) 181862306a36Sopenharmony_ci new_state = MYRB_DEVICE_DEAD; 181962306a36Sopenharmony_ci else if (!strncmp(buf, "online", 6)) 182062306a36Sopenharmony_ci new_state = MYRB_DEVICE_ONLINE; 182162306a36Sopenharmony_ci else if (!strncmp(buf, "standby", 7)) 182262306a36Sopenharmony_ci new_state = MYRB_DEVICE_STANDBY; 182362306a36Sopenharmony_ci else 182462306a36Sopenharmony_ci return -EINVAL; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci pdev_info = sdev->hostdata; 182762306a36Sopenharmony_ci if (!pdev_info) { 182862306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 182962306a36Sopenharmony_ci "Failed - no physical device information\n"); 183062306a36Sopenharmony_ci return -ENXIO; 183162306a36Sopenharmony_ci } 183262306a36Sopenharmony_ci if (!pdev_info->present) { 183362306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 183462306a36Sopenharmony_ci "Failed - device not present\n"); 183562306a36Sopenharmony_ci return -ENXIO; 183662306a36Sopenharmony_ci } 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci if (pdev_info->state == new_state) 183962306a36Sopenharmony_ci return count; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci status = myrb_set_pdev_state(cb, sdev, new_state); 184262306a36Sopenharmony_ci switch (status) { 184362306a36Sopenharmony_ci case MYRB_STATUS_SUCCESS: 184462306a36Sopenharmony_ci break; 184562306a36Sopenharmony_ci case MYRB_STATUS_START_DEVICE_FAILED: 184662306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 184762306a36Sopenharmony_ci "Failed - Unable to Start Device\n"); 184862306a36Sopenharmony_ci count = -EAGAIN; 184962306a36Sopenharmony_ci break; 185062306a36Sopenharmony_ci case MYRB_STATUS_NO_DEVICE: 185162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 185262306a36Sopenharmony_ci "Failed - No Device at Address\n"); 185362306a36Sopenharmony_ci count = -ENODEV; 185462306a36Sopenharmony_ci break; 185562306a36Sopenharmony_ci case MYRB_STATUS_INVALID_CHANNEL_OR_TARGET: 185662306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 185762306a36Sopenharmony_ci "Failed - Invalid Channel or Target or Modifier\n"); 185862306a36Sopenharmony_ci count = -EINVAL; 185962306a36Sopenharmony_ci break; 186062306a36Sopenharmony_ci case MYRB_STATUS_CHANNEL_BUSY: 186162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 186262306a36Sopenharmony_ci "Failed - Channel Busy\n"); 186362306a36Sopenharmony_ci count = -EBUSY; 186462306a36Sopenharmony_ci break; 186562306a36Sopenharmony_ci default: 186662306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 186762306a36Sopenharmony_ci "Failed - Unexpected Status %04X\n", status); 186862306a36Sopenharmony_ci count = -EIO; 186962306a36Sopenharmony_ci break; 187062306a36Sopenharmony_ci } 187162306a36Sopenharmony_ci return count; 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(raid_state); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_cistatic ssize_t raid_level_show(struct device *dev, 187662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 187762306a36Sopenharmony_ci{ 187862306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci if (sdev->channel == myrb_logical_channel(sdev->host)) { 188162306a36Sopenharmony_ci struct myrb_ldev_info *ldev_info = sdev->hostdata; 188262306a36Sopenharmony_ci const char *name; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci if (!ldev_info) 188562306a36Sopenharmony_ci return -ENXIO; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci name = myrb_raidlevel_name(ldev_info->raid_level); 188862306a36Sopenharmony_ci if (!name) 188962306a36Sopenharmony_ci return snprintf(buf, 32, "Invalid (%02X)\n", 189062306a36Sopenharmony_ci ldev_info->state); 189162306a36Sopenharmony_ci return snprintf(buf, 32, "%s\n", name); 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci return snprintf(buf, 32, "Physical Drive\n"); 189462306a36Sopenharmony_ci} 189562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(raid_level); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_cistatic ssize_t rebuild_show(struct device *dev, 189862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 189962306a36Sopenharmony_ci{ 190062306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 190162306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 190262306a36Sopenharmony_ci struct myrb_rbld_progress rbld_buf; 190362306a36Sopenharmony_ci unsigned char status; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host)) 190662306a36Sopenharmony_ci return snprintf(buf, 32, "physical device - not rebuilding\n"); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci status = myrb_get_rbld_progress(cb, &rbld_buf); 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci if (rbld_buf.ldev_num != sdev->id || 191162306a36Sopenharmony_ci status != MYRB_STATUS_SUCCESS) 191262306a36Sopenharmony_ci return snprintf(buf, 32, "not rebuilding\n"); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci return snprintf(buf, 32, "rebuilding block %u of %u\n", 191562306a36Sopenharmony_ci rbld_buf.ldev_size - rbld_buf.blocks_left, 191662306a36Sopenharmony_ci rbld_buf.ldev_size); 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic ssize_t rebuild_store(struct device *dev, 192062306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 192362306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 192462306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk; 192562306a36Sopenharmony_ci union myrb_cmd_mbox *mbox; 192662306a36Sopenharmony_ci unsigned short status; 192762306a36Sopenharmony_ci int rc, start; 192862306a36Sopenharmony_ci const char *msg; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci rc = kstrtoint(buf, 0, &start); 193162306a36Sopenharmony_ci if (rc) 193262306a36Sopenharmony_ci return rc; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci if (sdev->channel >= myrb_logical_channel(sdev->host)) 193562306a36Sopenharmony_ci return -ENXIO; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci status = myrb_get_rbld_progress(cb, NULL); 193862306a36Sopenharmony_ci if (start) { 193962306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 194062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 194162306a36Sopenharmony_ci "Rebuild Not Initiated; already in progress\n"); 194262306a36Sopenharmony_ci return -EALREADY; 194362306a36Sopenharmony_ci } 194462306a36Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 194562306a36Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 194662306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 194762306a36Sopenharmony_ci mbox = &cmd_blk->mbox; 194862306a36Sopenharmony_ci mbox->type3D.opcode = MYRB_CMD_REBUILD_ASYNC; 194962306a36Sopenharmony_ci mbox->type3D.id = MYRB_DCMD_TAG; 195062306a36Sopenharmony_ci mbox->type3D.channel = sdev->channel; 195162306a36Sopenharmony_ci mbox->type3D.target = sdev->id; 195262306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 195362306a36Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 195462306a36Sopenharmony_ci } else { 195562306a36Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 195662306a36Sopenharmony_ci unsigned char *rate; 195762306a36Sopenharmony_ci dma_addr_t rate_addr; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 196062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 196162306a36Sopenharmony_ci "Rebuild Not Cancelled; not in progress\n"); 196262306a36Sopenharmony_ci return 0; 196362306a36Sopenharmony_ci } 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci rate = dma_alloc_coherent(&pdev->dev, sizeof(char), 196662306a36Sopenharmony_ci &rate_addr, GFP_KERNEL); 196762306a36Sopenharmony_ci if (rate == NULL) { 196862306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 196962306a36Sopenharmony_ci "Cancellation of Rebuild Failed - Out of Memory\n"); 197062306a36Sopenharmony_ci return -ENOMEM; 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 197362306a36Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 197462306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 197562306a36Sopenharmony_ci mbox = &cmd_blk->mbox; 197662306a36Sopenharmony_ci mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL; 197762306a36Sopenharmony_ci mbox->type3R.id = MYRB_DCMD_TAG; 197862306a36Sopenharmony_ci mbox->type3R.rbld_rate = 0xFF; 197962306a36Sopenharmony_ci mbox->type3R.addr = rate_addr; 198062306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 198162306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(char), rate, rate_addr); 198262306a36Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 198362306a36Sopenharmony_ci } 198462306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 198562306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Rebuild %s\n", 198662306a36Sopenharmony_ci start ? "Initiated" : "Cancelled"); 198762306a36Sopenharmony_ci return count; 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci if (!start) { 199062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 199162306a36Sopenharmony_ci "Rebuild Not Cancelled, status 0x%x\n", 199262306a36Sopenharmony_ci status); 199362306a36Sopenharmony_ci return -EIO; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci switch (status) { 199762306a36Sopenharmony_ci case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE: 199862306a36Sopenharmony_ci msg = "Attempt to Rebuild Online or Unresponsive Drive"; 199962306a36Sopenharmony_ci break; 200062306a36Sopenharmony_ci case MYRB_STATUS_RBLD_NEW_DISK_FAILED: 200162306a36Sopenharmony_ci msg = "New Disk Failed During Rebuild"; 200262306a36Sopenharmony_ci break; 200362306a36Sopenharmony_ci case MYRB_STATUS_INVALID_ADDRESS: 200462306a36Sopenharmony_ci msg = "Invalid Device Address"; 200562306a36Sopenharmony_ci break; 200662306a36Sopenharmony_ci case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS: 200762306a36Sopenharmony_ci msg = "Already in Progress"; 200862306a36Sopenharmony_ci break; 200962306a36Sopenharmony_ci default: 201062306a36Sopenharmony_ci msg = NULL; 201162306a36Sopenharmony_ci break; 201262306a36Sopenharmony_ci } 201362306a36Sopenharmony_ci if (msg) 201462306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 201562306a36Sopenharmony_ci "Rebuild Failed - %s\n", msg); 201662306a36Sopenharmony_ci else 201762306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 201862306a36Sopenharmony_ci "Rebuild Failed, status 0x%x\n", status); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci return -EIO; 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(rebuild); 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_cistatic ssize_t consistency_check_store(struct device *dev, 202562306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 202662306a36Sopenharmony_ci{ 202762306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 202862306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 202962306a36Sopenharmony_ci struct myrb_rbld_progress rbld_buf; 203062306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk; 203162306a36Sopenharmony_ci union myrb_cmd_mbox *mbox; 203262306a36Sopenharmony_ci unsigned short ldev_num = 0xFFFF; 203362306a36Sopenharmony_ci unsigned short status; 203462306a36Sopenharmony_ci int rc, start; 203562306a36Sopenharmony_ci const char *msg; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci rc = kstrtoint(buf, 0, &start); 203862306a36Sopenharmony_ci if (rc) 203962306a36Sopenharmony_ci return rc; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host)) 204262306a36Sopenharmony_ci return -ENXIO; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci status = myrb_get_rbld_progress(cb, &rbld_buf); 204562306a36Sopenharmony_ci if (start) { 204662306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 204762306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 204862306a36Sopenharmony_ci "Check Consistency Not Initiated; already in progress\n"); 204962306a36Sopenharmony_ci return -EALREADY; 205062306a36Sopenharmony_ci } 205162306a36Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 205262306a36Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 205362306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 205462306a36Sopenharmony_ci mbox = &cmd_blk->mbox; 205562306a36Sopenharmony_ci mbox->type3C.opcode = MYRB_CMD_CHECK_CONSISTENCY_ASYNC; 205662306a36Sopenharmony_ci mbox->type3C.id = MYRB_DCMD_TAG; 205762306a36Sopenharmony_ci mbox->type3C.ldev_num = sdev->id; 205862306a36Sopenharmony_ci mbox->type3C.auto_restore = true; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 206162306a36Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 206262306a36Sopenharmony_ci } else { 206362306a36Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 206462306a36Sopenharmony_ci unsigned char *rate; 206562306a36Sopenharmony_ci dma_addr_t rate_addr; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (ldev_num != sdev->id) { 206862306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 206962306a36Sopenharmony_ci "Check Consistency Not Cancelled; not in progress\n"); 207062306a36Sopenharmony_ci return 0; 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci rate = dma_alloc_coherent(&pdev->dev, sizeof(char), 207362306a36Sopenharmony_ci &rate_addr, GFP_KERNEL); 207462306a36Sopenharmony_ci if (rate == NULL) { 207562306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 207662306a36Sopenharmony_ci "Cancellation of Check Consistency Failed - Out of Memory\n"); 207762306a36Sopenharmony_ci return -ENOMEM; 207862306a36Sopenharmony_ci } 207962306a36Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 208062306a36Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 208162306a36Sopenharmony_ci myrb_reset_cmd(cmd_blk); 208262306a36Sopenharmony_ci mbox = &cmd_blk->mbox; 208362306a36Sopenharmony_ci mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL; 208462306a36Sopenharmony_ci mbox->type3R.id = MYRB_DCMD_TAG; 208562306a36Sopenharmony_ci mbox->type3R.rbld_rate = 0xFF; 208662306a36Sopenharmony_ci mbox->type3R.addr = rate_addr; 208762306a36Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 208862306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(char), rate, rate_addr); 208962306a36Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 209262306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Check Consistency %s\n", 209362306a36Sopenharmony_ci start ? "Initiated" : "Cancelled"); 209462306a36Sopenharmony_ci return count; 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci if (!start) { 209762306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 209862306a36Sopenharmony_ci "Check Consistency Not Cancelled, status 0x%x\n", 209962306a36Sopenharmony_ci status); 210062306a36Sopenharmony_ci return -EIO; 210162306a36Sopenharmony_ci } 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci switch (status) { 210462306a36Sopenharmony_ci case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE: 210562306a36Sopenharmony_ci msg = "Dependent Physical Device is DEAD"; 210662306a36Sopenharmony_ci break; 210762306a36Sopenharmony_ci case MYRB_STATUS_RBLD_NEW_DISK_FAILED: 210862306a36Sopenharmony_ci msg = "New Disk Failed During Rebuild"; 210962306a36Sopenharmony_ci break; 211062306a36Sopenharmony_ci case MYRB_STATUS_INVALID_ADDRESS: 211162306a36Sopenharmony_ci msg = "Invalid or Nonredundant Logical Drive"; 211262306a36Sopenharmony_ci break; 211362306a36Sopenharmony_ci case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS: 211462306a36Sopenharmony_ci msg = "Already in Progress"; 211562306a36Sopenharmony_ci break; 211662306a36Sopenharmony_ci default: 211762306a36Sopenharmony_ci msg = NULL; 211862306a36Sopenharmony_ci break; 211962306a36Sopenharmony_ci } 212062306a36Sopenharmony_ci if (msg) 212162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 212262306a36Sopenharmony_ci "Check Consistency Failed - %s\n", msg); 212362306a36Sopenharmony_ci else 212462306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 212562306a36Sopenharmony_ci "Check Consistency Failed, status 0x%x\n", status); 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci return -EIO; 212862306a36Sopenharmony_ci} 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_cistatic ssize_t consistency_check_show(struct device *dev, 213162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 213262306a36Sopenharmony_ci{ 213362306a36Sopenharmony_ci return rebuild_show(dev, attr, buf); 213462306a36Sopenharmony_ci} 213562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(consistency_check); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_cistatic ssize_t ctlr_num_show(struct device *dev, 213862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 213962306a36Sopenharmony_ci{ 214062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 214162306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci return snprintf(buf, 20, "%u\n", cb->ctlr_num); 214462306a36Sopenharmony_ci} 214562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ctlr_num); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_cistatic ssize_t firmware_show(struct device *dev, 214862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 214962306a36Sopenharmony_ci{ 215062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 215162306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci return snprintf(buf, 16, "%s\n", cb->fw_version); 215462306a36Sopenharmony_ci} 215562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(firmware); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_cistatic ssize_t model_show(struct device *dev, 215862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 215962306a36Sopenharmony_ci{ 216062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 216162306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci return snprintf(buf, 16, "%s\n", cb->model_name); 216462306a36Sopenharmony_ci} 216562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(model); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_cistatic ssize_t flush_cache_store(struct device *dev, 216862306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 217162306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 217262306a36Sopenharmony_ci unsigned short status; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0); 217562306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 217662306a36Sopenharmony_ci shost_printk(KERN_INFO, shost, 217762306a36Sopenharmony_ci "Cache Flush Completed\n"); 217862306a36Sopenharmony_ci return count; 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci shost_printk(KERN_INFO, shost, 218162306a36Sopenharmony_ci "Cache Flush Failed, status %x\n", status); 218262306a36Sopenharmony_ci return -EIO; 218362306a36Sopenharmony_ci} 218462306a36Sopenharmony_cistatic DEVICE_ATTR_WO(flush_cache); 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_cistatic struct attribute *myrb_sdev_attrs[] = { 218762306a36Sopenharmony_ci &dev_attr_rebuild.attr, 218862306a36Sopenharmony_ci &dev_attr_consistency_check.attr, 218962306a36Sopenharmony_ci &dev_attr_raid_state.attr, 219062306a36Sopenharmony_ci &dev_attr_raid_level.attr, 219162306a36Sopenharmony_ci NULL, 219262306a36Sopenharmony_ci}; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ciATTRIBUTE_GROUPS(myrb_sdev); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_cistatic struct attribute *myrb_shost_attrs[] = { 219762306a36Sopenharmony_ci &dev_attr_ctlr_num.attr, 219862306a36Sopenharmony_ci &dev_attr_model.attr, 219962306a36Sopenharmony_ci &dev_attr_firmware.attr, 220062306a36Sopenharmony_ci &dev_attr_flush_cache.attr, 220162306a36Sopenharmony_ci NULL, 220262306a36Sopenharmony_ci}; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ciATTRIBUTE_GROUPS(myrb_shost); 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_cistatic const struct scsi_host_template myrb_template = { 220762306a36Sopenharmony_ci .module = THIS_MODULE, 220862306a36Sopenharmony_ci .name = "DAC960", 220962306a36Sopenharmony_ci .proc_name = "myrb", 221062306a36Sopenharmony_ci .queuecommand = myrb_queuecommand, 221162306a36Sopenharmony_ci .eh_host_reset_handler = myrb_host_reset, 221262306a36Sopenharmony_ci .slave_alloc = myrb_slave_alloc, 221362306a36Sopenharmony_ci .slave_configure = myrb_slave_configure, 221462306a36Sopenharmony_ci .slave_destroy = myrb_slave_destroy, 221562306a36Sopenharmony_ci .bios_param = myrb_biosparam, 221662306a36Sopenharmony_ci .cmd_size = sizeof(struct myrb_cmdblk), 221762306a36Sopenharmony_ci .shost_groups = myrb_shost_groups, 221862306a36Sopenharmony_ci .sdev_groups = myrb_sdev_groups, 221962306a36Sopenharmony_ci .this_id = -1, 222062306a36Sopenharmony_ci}; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci/** 222362306a36Sopenharmony_ci * myrb_is_raid - return boolean indicating device is raid volume 222462306a36Sopenharmony_ci * @dev: the device struct object 222562306a36Sopenharmony_ci */ 222662306a36Sopenharmony_cistatic int myrb_is_raid(struct device *dev) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci return sdev->channel == myrb_logical_channel(sdev->host); 223162306a36Sopenharmony_ci} 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci/** 223462306a36Sopenharmony_ci * myrb_get_resync - get raid volume resync percent complete 223562306a36Sopenharmony_ci * @dev: the device struct object 223662306a36Sopenharmony_ci */ 223762306a36Sopenharmony_cistatic void myrb_get_resync(struct device *dev) 223862306a36Sopenharmony_ci{ 223962306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 224062306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 224162306a36Sopenharmony_ci struct myrb_rbld_progress rbld_buf; 224262306a36Sopenharmony_ci unsigned int percent_complete = 0; 224362306a36Sopenharmony_ci unsigned short status; 224462306a36Sopenharmony_ci unsigned int ldev_size = 0, remaining = 0; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host)) 224762306a36Sopenharmony_ci return; 224862306a36Sopenharmony_ci status = myrb_get_rbld_progress(cb, &rbld_buf); 224962306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 225062306a36Sopenharmony_ci if (rbld_buf.ldev_num == sdev->id) { 225162306a36Sopenharmony_ci ldev_size = rbld_buf.ldev_size; 225262306a36Sopenharmony_ci remaining = rbld_buf.blocks_left; 225362306a36Sopenharmony_ci } 225462306a36Sopenharmony_ci } 225562306a36Sopenharmony_ci if (remaining && ldev_size) 225662306a36Sopenharmony_ci percent_complete = (ldev_size - remaining) * 100 / ldev_size; 225762306a36Sopenharmony_ci raid_set_resync(myrb_raid_template, dev, percent_complete); 225862306a36Sopenharmony_ci} 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci/** 226162306a36Sopenharmony_ci * myrb_get_state - get raid volume status 226262306a36Sopenharmony_ci * @dev: the device struct object 226362306a36Sopenharmony_ci */ 226462306a36Sopenharmony_cistatic void myrb_get_state(struct device *dev) 226562306a36Sopenharmony_ci{ 226662306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 226762306a36Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 226862306a36Sopenharmony_ci struct myrb_ldev_info *ldev_info = sdev->hostdata; 226962306a36Sopenharmony_ci enum raid_state state = RAID_STATE_UNKNOWN; 227062306a36Sopenharmony_ci unsigned short status; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host) || !ldev_info) 227362306a36Sopenharmony_ci state = RAID_STATE_UNKNOWN; 227462306a36Sopenharmony_ci else { 227562306a36Sopenharmony_ci status = myrb_get_rbld_progress(cb, NULL); 227662306a36Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) 227762306a36Sopenharmony_ci state = RAID_STATE_RESYNCING; 227862306a36Sopenharmony_ci else { 227962306a36Sopenharmony_ci switch (ldev_info->state) { 228062306a36Sopenharmony_ci case MYRB_DEVICE_ONLINE: 228162306a36Sopenharmony_ci state = RAID_STATE_ACTIVE; 228262306a36Sopenharmony_ci break; 228362306a36Sopenharmony_ci case MYRB_DEVICE_WO: 228462306a36Sopenharmony_ci case MYRB_DEVICE_CRITICAL: 228562306a36Sopenharmony_ci state = RAID_STATE_DEGRADED; 228662306a36Sopenharmony_ci break; 228762306a36Sopenharmony_ci default: 228862306a36Sopenharmony_ci state = RAID_STATE_OFFLINE; 228962306a36Sopenharmony_ci } 229062306a36Sopenharmony_ci } 229162306a36Sopenharmony_ci } 229262306a36Sopenharmony_ci raid_set_state(myrb_raid_template, dev, state); 229362306a36Sopenharmony_ci} 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_cistatic struct raid_function_template myrb_raid_functions = { 229662306a36Sopenharmony_ci .cookie = &myrb_template, 229762306a36Sopenharmony_ci .is_raid = myrb_is_raid, 229862306a36Sopenharmony_ci .get_resync = myrb_get_resync, 229962306a36Sopenharmony_ci .get_state = myrb_get_state, 230062306a36Sopenharmony_ci}; 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_cistatic void myrb_handle_scsi(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk, 230362306a36Sopenharmony_ci struct scsi_cmnd *scmd) 230462306a36Sopenharmony_ci{ 230562306a36Sopenharmony_ci unsigned short status; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci if (!cmd_blk) 230862306a36Sopenharmony_ci return; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci scsi_dma_unmap(scmd); 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci if (cmd_blk->dcdb) { 231362306a36Sopenharmony_ci memcpy(scmd->sense_buffer, &cmd_blk->dcdb->sense, 64); 231462306a36Sopenharmony_ci dma_pool_free(cb->dcdb_pool, cmd_blk->dcdb, 231562306a36Sopenharmony_ci cmd_blk->dcdb_addr); 231662306a36Sopenharmony_ci cmd_blk->dcdb = NULL; 231762306a36Sopenharmony_ci } 231862306a36Sopenharmony_ci if (cmd_blk->sgl) { 231962306a36Sopenharmony_ci dma_pool_free(cb->sg_pool, cmd_blk->sgl, cmd_blk->sgl_addr); 232062306a36Sopenharmony_ci cmd_blk->sgl = NULL; 232162306a36Sopenharmony_ci cmd_blk->sgl_addr = 0; 232262306a36Sopenharmony_ci } 232362306a36Sopenharmony_ci status = cmd_blk->status; 232462306a36Sopenharmony_ci switch (status) { 232562306a36Sopenharmony_ci case MYRB_STATUS_SUCCESS: 232662306a36Sopenharmony_ci case MYRB_STATUS_DEVICE_BUSY: 232762306a36Sopenharmony_ci scmd->result = (DID_OK << 16) | status; 232862306a36Sopenharmony_ci break; 232962306a36Sopenharmony_ci case MYRB_STATUS_BAD_DATA: 233062306a36Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, 233162306a36Sopenharmony_ci "Bad Data Encountered\n"); 233262306a36Sopenharmony_ci if (scmd->sc_data_direction == DMA_FROM_DEVICE) 233362306a36Sopenharmony_ci /* Unrecovered read error */ 233462306a36Sopenharmony_ci scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x11, 0); 233562306a36Sopenharmony_ci else 233662306a36Sopenharmony_ci /* Write error */ 233762306a36Sopenharmony_ci scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x0C, 0); 233862306a36Sopenharmony_ci break; 233962306a36Sopenharmony_ci case MYRB_STATUS_IRRECOVERABLE_DATA_ERROR: 234062306a36Sopenharmony_ci scmd_printk(KERN_ERR, scmd, "Irrecoverable Data Error\n"); 234162306a36Sopenharmony_ci if (scmd->sc_data_direction == DMA_FROM_DEVICE) 234262306a36Sopenharmony_ci /* Unrecovered read error, auto-reallocation failed */ 234362306a36Sopenharmony_ci scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x11, 0x04); 234462306a36Sopenharmony_ci else 234562306a36Sopenharmony_ci /* Write error, auto-reallocation failed */ 234662306a36Sopenharmony_ci scsi_build_sense(scmd, 0, MEDIUM_ERROR, 0x0C, 0x02); 234762306a36Sopenharmony_ci break; 234862306a36Sopenharmony_ci case MYRB_STATUS_LDRV_NONEXISTENT_OR_OFFLINE: 234962306a36Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, 235062306a36Sopenharmony_ci "Logical Drive Nonexistent or Offline"); 235162306a36Sopenharmony_ci scmd->result = (DID_BAD_TARGET << 16); 235262306a36Sopenharmony_ci break; 235362306a36Sopenharmony_ci case MYRB_STATUS_ACCESS_BEYOND_END_OF_LDRV: 235462306a36Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, 235562306a36Sopenharmony_ci "Attempt to Access Beyond End of Logical Drive"); 235662306a36Sopenharmony_ci /* Logical block address out of range */ 235762306a36Sopenharmony_ci scsi_build_sense(scmd, 0, NOT_READY, 0x21, 0); 235862306a36Sopenharmony_ci break; 235962306a36Sopenharmony_ci case MYRB_STATUS_DEVICE_NONRESPONSIVE: 236062306a36Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, "Device nonresponsive\n"); 236162306a36Sopenharmony_ci scmd->result = (DID_BAD_TARGET << 16); 236262306a36Sopenharmony_ci break; 236362306a36Sopenharmony_ci default: 236462306a36Sopenharmony_ci scmd_printk(KERN_ERR, scmd, 236562306a36Sopenharmony_ci "Unexpected Error Status %04X", status); 236662306a36Sopenharmony_ci scmd->result = (DID_ERROR << 16); 236762306a36Sopenharmony_ci break; 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci scsi_done(scmd); 237062306a36Sopenharmony_ci} 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_cistatic void myrb_handle_cmdblk(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) 237362306a36Sopenharmony_ci{ 237462306a36Sopenharmony_ci if (!cmd_blk) 237562306a36Sopenharmony_ci return; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci if (cmd_blk->completion) { 237862306a36Sopenharmony_ci complete(cmd_blk->completion); 237962306a36Sopenharmony_ci cmd_blk->completion = NULL; 238062306a36Sopenharmony_ci } 238162306a36Sopenharmony_ci} 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_cistatic void myrb_monitor(struct work_struct *work) 238462306a36Sopenharmony_ci{ 238562306a36Sopenharmony_ci struct myrb_hba *cb = container_of(work, 238662306a36Sopenharmony_ci struct myrb_hba, monitor_work.work); 238762306a36Sopenharmony_ci struct Scsi_Host *shost = cb->host; 238862306a36Sopenharmony_ci unsigned long interval = MYRB_PRIMARY_MONITOR_INTERVAL; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, "monitor tick\n"); 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci if (cb->new_ev_seq > cb->old_ev_seq) { 239362306a36Sopenharmony_ci int event = cb->old_ev_seq; 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, 239662306a36Sopenharmony_ci "get event log no %d/%d\n", 239762306a36Sopenharmony_ci cb->new_ev_seq, event); 239862306a36Sopenharmony_ci myrb_get_event(cb, event); 239962306a36Sopenharmony_ci cb->old_ev_seq = event + 1; 240062306a36Sopenharmony_ci interval = 10; 240162306a36Sopenharmony_ci } else if (cb->need_err_info) { 240262306a36Sopenharmony_ci cb->need_err_info = false; 240362306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, "get error table\n"); 240462306a36Sopenharmony_ci myrb_get_errtable(cb); 240562306a36Sopenharmony_ci interval = 10; 240662306a36Sopenharmony_ci } else if (cb->need_rbld && cb->rbld_first) { 240762306a36Sopenharmony_ci cb->need_rbld = false; 240862306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, 240962306a36Sopenharmony_ci "get rebuild progress\n"); 241062306a36Sopenharmony_ci myrb_update_rbld_progress(cb); 241162306a36Sopenharmony_ci interval = 10; 241262306a36Sopenharmony_ci } else if (cb->need_ldev_info) { 241362306a36Sopenharmony_ci cb->need_ldev_info = false; 241462306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, 241562306a36Sopenharmony_ci "get logical drive info\n"); 241662306a36Sopenharmony_ci myrb_get_ldev_info(cb); 241762306a36Sopenharmony_ci interval = 10; 241862306a36Sopenharmony_ci } else if (cb->need_rbld) { 241962306a36Sopenharmony_ci cb->need_rbld = false; 242062306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, 242162306a36Sopenharmony_ci "get rebuild progress\n"); 242262306a36Sopenharmony_ci myrb_update_rbld_progress(cb); 242362306a36Sopenharmony_ci interval = 10; 242462306a36Sopenharmony_ci } else if (cb->need_cc_status) { 242562306a36Sopenharmony_ci cb->need_cc_status = false; 242662306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, 242762306a36Sopenharmony_ci "get consistency check progress\n"); 242862306a36Sopenharmony_ci myrb_get_cc_progress(cb); 242962306a36Sopenharmony_ci interval = 10; 243062306a36Sopenharmony_ci } else if (cb->need_bgi_status) { 243162306a36Sopenharmony_ci cb->need_bgi_status = false; 243262306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, "get background init status\n"); 243362306a36Sopenharmony_ci myrb_bgi_control(cb); 243462306a36Sopenharmony_ci interval = 10; 243562306a36Sopenharmony_ci } else { 243662306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, "new enquiry\n"); 243762306a36Sopenharmony_ci mutex_lock(&cb->dma_mutex); 243862306a36Sopenharmony_ci myrb_hba_enquiry(cb); 243962306a36Sopenharmony_ci mutex_unlock(&cb->dma_mutex); 244062306a36Sopenharmony_ci if ((cb->new_ev_seq - cb->old_ev_seq > 0) || 244162306a36Sopenharmony_ci cb->need_err_info || cb->need_rbld || 244262306a36Sopenharmony_ci cb->need_ldev_info || cb->need_cc_status || 244362306a36Sopenharmony_ci cb->need_bgi_status) { 244462306a36Sopenharmony_ci dev_dbg(&shost->shost_gendev, 244562306a36Sopenharmony_ci "reschedule monitor\n"); 244662306a36Sopenharmony_ci interval = 0; 244762306a36Sopenharmony_ci } 244862306a36Sopenharmony_ci } 244962306a36Sopenharmony_ci if (interval > 1) 245062306a36Sopenharmony_ci cb->primary_monitor_time = jiffies; 245162306a36Sopenharmony_ci queue_delayed_work(cb->work_q, &cb->monitor_work, interval); 245262306a36Sopenharmony_ci} 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci/* 245562306a36Sopenharmony_ci * myrb_err_status - reports controller BIOS messages 245662306a36Sopenharmony_ci * 245762306a36Sopenharmony_ci * Controller BIOS messages are passed through the Error Status Register 245862306a36Sopenharmony_ci * when the driver performs the BIOS handshaking. 245962306a36Sopenharmony_ci * 246062306a36Sopenharmony_ci * Return: true for fatal errors and false otherwise. 246162306a36Sopenharmony_ci */ 246262306a36Sopenharmony_cistatic bool myrb_err_status(struct myrb_hba *cb, unsigned char error, 246362306a36Sopenharmony_ci unsigned char parm0, unsigned char parm1) 246462306a36Sopenharmony_ci{ 246562306a36Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci switch (error) { 246862306a36Sopenharmony_ci case 0x00: 246962306a36Sopenharmony_ci dev_info(&pdev->dev, 247062306a36Sopenharmony_ci "Physical Device %d:%d Not Responding\n", 247162306a36Sopenharmony_ci parm1, parm0); 247262306a36Sopenharmony_ci break; 247362306a36Sopenharmony_ci case 0x08: 247462306a36Sopenharmony_ci dev_notice(&pdev->dev, "Spinning Up Drives\n"); 247562306a36Sopenharmony_ci break; 247662306a36Sopenharmony_ci case 0x30: 247762306a36Sopenharmony_ci dev_notice(&pdev->dev, "Configuration Checksum Error\n"); 247862306a36Sopenharmony_ci break; 247962306a36Sopenharmony_ci case 0x60: 248062306a36Sopenharmony_ci dev_notice(&pdev->dev, "Mirror Race Recovery Failed\n"); 248162306a36Sopenharmony_ci break; 248262306a36Sopenharmony_ci case 0x70: 248362306a36Sopenharmony_ci dev_notice(&pdev->dev, "Mirror Race Recovery In Progress\n"); 248462306a36Sopenharmony_ci break; 248562306a36Sopenharmony_ci case 0x90: 248662306a36Sopenharmony_ci dev_notice(&pdev->dev, "Physical Device %d:%d COD Mismatch\n", 248762306a36Sopenharmony_ci parm1, parm0); 248862306a36Sopenharmony_ci break; 248962306a36Sopenharmony_ci case 0xA0: 249062306a36Sopenharmony_ci dev_notice(&pdev->dev, "Logical Drive Installation Aborted\n"); 249162306a36Sopenharmony_ci break; 249262306a36Sopenharmony_ci case 0xB0: 249362306a36Sopenharmony_ci dev_notice(&pdev->dev, "Mirror Race On A Critical Logical Drive\n"); 249462306a36Sopenharmony_ci break; 249562306a36Sopenharmony_ci case 0xD0: 249662306a36Sopenharmony_ci dev_notice(&pdev->dev, "New Controller Configuration Found\n"); 249762306a36Sopenharmony_ci break; 249862306a36Sopenharmony_ci case 0xF0: 249962306a36Sopenharmony_ci dev_err(&pdev->dev, "Fatal Memory Parity Error\n"); 250062306a36Sopenharmony_ci return true; 250162306a36Sopenharmony_ci default: 250262306a36Sopenharmony_ci dev_err(&pdev->dev, "Unknown Initialization Error %02X\n", 250362306a36Sopenharmony_ci error); 250462306a36Sopenharmony_ci return true; 250562306a36Sopenharmony_ci } 250662306a36Sopenharmony_ci return false; 250762306a36Sopenharmony_ci} 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci/* 251062306a36Sopenharmony_ci * Hardware-specific functions 251162306a36Sopenharmony_ci */ 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci/* 251462306a36Sopenharmony_ci * DAC960 LA Series Controllers 251562306a36Sopenharmony_ci */ 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_cistatic inline void DAC960_LA_hw_mbox_new_cmd(void __iomem *base) 251862306a36Sopenharmony_ci{ 251962306a36Sopenharmony_ci writeb(DAC960_LA_IDB_HWMBOX_NEW_CMD, base + DAC960_LA_IDB_OFFSET); 252062306a36Sopenharmony_ci} 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_cistatic inline void DAC960_LA_ack_hw_mbox_status(void __iomem *base) 252362306a36Sopenharmony_ci{ 252462306a36Sopenharmony_ci writeb(DAC960_LA_IDB_HWMBOX_ACK_STS, base + DAC960_LA_IDB_OFFSET); 252562306a36Sopenharmony_ci} 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_cistatic inline void DAC960_LA_reset_ctrl(void __iomem *base) 252862306a36Sopenharmony_ci{ 252962306a36Sopenharmony_ci writeb(DAC960_LA_IDB_CTRL_RESET, base + DAC960_LA_IDB_OFFSET); 253062306a36Sopenharmony_ci} 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_cistatic inline void DAC960_LA_mem_mbox_new_cmd(void __iomem *base) 253362306a36Sopenharmony_ci{ 253462306a36Sopenharmony_ci writeb(DAC960_LA_IDB_MMBOX_NEW_CMD, base + DAC960_LA_IDB_OFFSET); 253562306a36Sopenharmony_ci} 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_cistatic inline bool DAC960_LA_hw_mbox_is_full(void __iomem *base) 253862306a36Sopenharmony_ci{ 253962306a36Sopenharmony_ci unsigned char idb = readb(base + DAC960_LA_IDB_OFFSET); 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci return !(idb & DAC960_LA_IDB_HWMBOX_EMPTY); 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_cistatic inline bool DAC960_LA_init_in_progress(void __iomem *base) 254562306a36Sopenharmony_ci{ 254662306a36Sopenharmony_ci unsigned char idb = readb(base + DAC960_LA_IDB_OFFSET); 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci return !(idb & DAC960_LA_IDB_INIT_DONE); 254962306a36Sopenharmony_ci} 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_cistatic inline void DAC960_LA_ack_hw_mbox_intr(void __iomem *base) 255262306a36Sopenharmony_ci{ 255362306a36Sopenharmony_ci writeb(DAC960_LA_ODB_HWMBOX_ACK_IRQ, base + DAC960_LA_ODB_OFFSET); 255462306a36Sopenharmony_ci} 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_cistatic inline void DAC960_LA_ack_intr(void __iomem *base) 255762306a36Sopenharmony_ci{ 255862306a36Sopenharmony_ci writeb(DAC960_LA_ODB_HWMBOX_ACK_IRQ | DAC960_LA_ODB_MMBOX_ACK_IRQ, 255962306a36Sopenharmony_ci base + DAC960_LA_ODB_OFFSET); 256062306a36Sopenharmony_ci} 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_cistatic inline bool DAC960_LA_hw_mbox_status_available(void __iomem *base) 256362306a36Sopenharmony_ci{ 256462306a36Sopenharmony_ci unsigned char odb = readb(base + DAC960_LA_ODB_OFFSET); 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci return odb & DAC960_LA_ODB_HWMBOX_STS_AVAIL; 256762306a36Sopenharmony_ci} 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_cistatic inline void DAC960_LA_enable_intr(void __iomem *base) 257062306a36Sopenharmony_ci{ 257162306a36Sopenharmony_ci unsigned char odb = 0xFF; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci odb &= ~DAC960_LA_IRQMASK_DISABLE_IRQ; 257462306a36Sopenharmony_ci writeb(odb, base + DAC960_LA_IRQMASK_OFFSET); 257562306a36Sopenharmony_ci} 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_cistatic inline void DAC960_LA_disable_intr(void __iomem *base) 257862306a36Sopenharmony_ci{ 257962306a36Sopenharmony_ci unsigned char odb = 0xFF; 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci odb |= DAC960_LA_IRQMASK_DISABLE_IRQ; 258262306a36Sopenharmony_ci writeb(odb, base + DAC960_LA_IRQMASK_OFFSET); 258362306a36Sopenharmony_ci} 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_cistatic inline void DAC960_LA_write_cmd_mbox(union myrb_cmd_mbox *mem_mbox, 258662306a36Sopenharmony_ci union myrb_cmd_mbox *mbox) 258762306a36Sopenharmony_ci{ 258862306a36Sopenharmony_ci mem_mbox->words[1] = mbox->words[1]; 258962306a36Sopenharmony_ci mem_mbox->words[2] = mbox->words[2]; 259062306a36Sopenharmony_ci mem_mbox->words[3] = mbox->words[3]; 259162306a36Sopenharmony_ci /* Memory barrier to prevent reordering */ 259262306a36Sopenharmony_ci wmb(); 259362306a36Sopenharmony_ci mem_mbox->words[0] = mbox->words[0]; 259462306a36Sopenharmony_ci /* Memory barrier to force PCI access */ 259562306a36Sopenharmony_ci mb(); 259662306a36Sopenharmony_ci} 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_cistatic inline void DAC960_LA_write_hw_mbox(void __iomem *base, 259962306a36Sopenharmony_ci union myrb_cmd_mbox *mbox) 260062306a36Sopenharmony_ci{ 260162306a36Sopenharmony_ci writel(mbox->words[0], base + DAC960_LA_CMDOP_OFFSET); 260262306a36Sopenharmony_ci writel(mbox->words[1], base + DAC960_LA_MBOX4_OFFSET); 260362306a36Sopenharmony_ci writel(mbox->words[2], base + DAC960_LA_MBOX8_OFFSET); 260462306a36Sopenharmony_ci writeb(mbox->bytes[12], base + DAC960_LA_MBOX12_OFFSET); 260562306a36Sopenharmony_ci} 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_cistatic inline unsigned short DAC960_LA_read_status(void __iomem *base) 260862306a36Sopenharmony_ci{ 260962306a36Sopenharmony_ci return readw(base + DAC960_LA_STS_OFFSET); 261062306a36Sopenharmony_ci} 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_cistatic inline bool 261362306a36Sopenharmony_ciDAC960_LA_read_error_status(void __iomem *base, unsigned char *error, 261462306a36Sopenharmony_ci unsigned char *param0, unsigned char *param1) 261562306a36Sopenharmony_ci{ 261662306a36Sopenharmony_ci unsigned char errsts = readb(base + DAC960_LA_ERRSTS_OFFSET); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci if (!(errsts & DAC960_LA_ERRSTS_PENDING)) 261962306a36Sopenharmony_ci return false; 262062306a36Sopenharmony_ci errsts &= ~DAC960_LA_ERRSTS_PENDING; 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci *error = errsts; 262362306a36Sopenharmony_ci *param0 = readb(base + DAC960_LA_CMDOP_OFFSET); 262462306a36Sopenharmony_ci *param1 = readb(base + DAC960_LA_CMDID_OFFSET); 262562306a36Sopenharmony_ci writeb(0xFF, base + DAC960_LA_ERRSTS_OFFSET); 262662306a36Sopenharmony_ci return true; 262762306a36Sopenharmony_ci} 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_cistatic inline unsigned short 263062306a36Sopenharmony_ciDAC960_LA_mbox_init(struct pci_dev *pdev, void __iomem *base, 263162306a36Sopenharmony_ci union myrb_cmd_mbox *mbox) 263262306a36Sopenharmony_ci{ 263362306a36Sopenharmony_ci unsigned short status; 263462306a36Sopenharmony_ci int timeout = 0; 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_ci while (timeout < MYRB_MAILBOX_TIMEOUT) { 263762306a36Sopenharmony_ci if (!DAC960_LA_hw_mbox_is_full(base)) 263862306a36Sopenharmony_ci break; 263962306a36Sopenharmony_ci udelay(10); 264062306a36Sopenharmony_ci timeout++; 264162306a36Sopenharmony_ci } 264262306a36Sopenharmony_ci if (DAC960_LA_hw_mbox_is_full(base)) { 264362306a36Sopenharmony_ci dev_err(&pdev->dev, 264462306a36Sopenharmony_ci "Timeout waiting for empty mailbox\n"); 264562306a36Sopenharmony_ci return MYRB_STATUS_SUBSYS_TIMEOUT; 264662306a36Sopenharmony_ci } 264762306a36Sopenharmony_ci DAC960_LA_write_hw_mbox(base, mbox); 264862306a36Sopenharmony_ci DAC960_LA_hw_mbox_new_cmd(base); 264962306a36Sopenharmony_ci timeout = 0; 265062306a36Sopenharmony_ci while (timeout < MYRB_MAILBOX_TIMEOUT) { 265162306a36Sopenharmony_ci if (DAC960_LA_hw_mbox_status_available(base)) 265262306a36Sopenharmony_ci break; 265362306a36Sopenharmony_ci udelay(10); 265462306a36Sopenharmony_ci timeout++; 265562306a36Sopenharmony_ci } 265662306a36Sopenharmony_ci if (!DAC960_LA_hw_mbox_status_available(base)) { 265762306a36Sopenharmony_ci dev_err(&pdev->dev, "Timeout waiting for mailbox status\n"); 265862306a36Sopenharmony_ci return MYRB_STATUS_SUBSYS_TIMEOUT; 265962306a36Sopenharmony_ci } 266062306a36Sopenharmony_ci status = DAC960_LA_read_status(base); 266162306a36Sopenharmony_ci DAC960_LA_ack_hw_mbox_intr(base); 266262306a36Sopenharmony_ci DAC960_LA_ack_hw_mbox_status(base); 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci return status; 266562306a36Sopenharmony_ci} 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_cistatic int DAC960_LA_hw_init(struct pci_dev *pdev, 266862306a36Sopenharmony_ci struct myrb_hba *cb, void __iomem *base) 266962306a36Sopenharmony_ci{ 267062306a36Sopenharmony_ci int timeout = 0; 267162306a36Sopenharmony_ci unsigned char error, parm0, parm1; 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_ci DAC960_LA_disable_intr(base); 267462306a36Sopenharmony_ci DAC960_LA_ack_hw_mbox_status(base); 267562306a36Sopenharmony_ci udelay(1000); 267662306a36Sopenharmony_ci while (DAC960_LA_init_in_progress(base) && 267762306a36Sopenharmony_ci timeout < MYRB_MAILBOX_TIMEOUT) { 267862306a36Sopenharmony_ci if (DAC960_LA_read_error_status(base, &error, 267962306a36Sopenharmony_ci &parm0, &parm1) && 268062306a36Sopenharmony_ci myrb_err_status(cb, error, parm0, parm1)) 268162306a36Sopenharmony_ci return -ENODEV; 268262306a36Sopenharmony_ci udelay(10); 268362306a36Sopenharmony_ci timeout++; 268462306a36Sopenharmony_ci } 268562306a36Sopenharmony_ci if (timeout == MYRB_MAILBOX_TIMEOUT) { 268662306a36Sopenharmony_ci dev_err(&pdev->dev, 268762306a36Sopenharmony_ci "Timeout waiting for Controller Initialisation\n"); 268862306a36Sopenharmony_ci return -ETIMEDOUT; 268962306a36Sopenharmony_ci } 269062306a36Sopenharmony_ci if (!myrb_enable_mmio(cb, DAC960_LA_mbox_init)) { 269162306a36Sopenharmony_ci dev_err(&pdev->dev, 269262306a36Sopenharmony_ci "Unable to Enable Memory Mailbox Interface\n"); 269362306a36Sopenharmony_ci DAC960_LA_reset_ctrl(base); 269462306a36Sopenharmony_ci return -ENODEV; 269562306a36Sopenharmony_ci } 269662306a36Sopenharmony_ci DAC960_LA_enable_intr(base); 269762306a36Sopenharmony_ci cb->qcmd = myrb_qcmd; 269862306a36Sopenharmony_ci cb->write_cmd_mbox = DAC960_LA_write_cmd_mbox; 269962306a36Sopenharmony_ci if (cb->dual_mode_interface) 270062306a36Sopenharmony_ci cb->get_cmd_mbox = DAC960_LA_mem_mbox_new_cmd; 270162306a36Sopenharmony_ci else 270262306a36Sopenharmony_ci cb->get_cmd_mbox = DAC960_LA_hw_mbox_new_cmd; 270362306a36Sopenharmony_ci cb->disable_intr = DAC960_LA_disable_intr; 270462306a36Sopenharmony_ci cb->reset = DAC960_LA_reset_ctrl; 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci return 0; 270762306a36Sopenharmony_ci} 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_cistatic irqreturn_t DAC960_LA_intr_handler(int irq, void *arg) 271062306a36Sopenharmony_ci{ 271162306a36Sopenharmony_ci struct myrb_hba *cb = arg; 271262306a36Sopenharmony_ci void __iomem *base = cb->io_base; 271362306a36Sopenharmony_ci struct myrb_stat_mbox *next_stat_mbox; 271462306a36Sopenharmony_ci unsigned long flags; 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 271762306a36Sopenharmony_ci DAC960_LA_ack_intr(base); 271862306a36Sopenharmony_ci next_stat_mbox = cb->next_stat_mbox; 271962306a36Sopenharmony_ci while (next_stat_mbox->valid) { 272062306a36Sopenharmony_ci unsigned char id = next_stat_mbox->id; 272162306a36Sopenharmony_ci struct scsi_cmnd *scmd = NULL; 272262306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = NULL; 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci if (id == MYRB_DCMD_TAG) 272562306a36Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 272662306a36Sopenharmony_ci else if (id == MYRB_MCMD_TAG) 272762306a36Sopenharmony_ci cmd_blk = &cb->mcmd_blk; 272862306a36Sopenharmony_ci else { 272962306a36Sopenharmony_ci scmd = scsi_host_find_tag(cb->host, id - 3); 273062306a36Sopenharmony_ci if (scmd) 273162306a36Sopenharmony_ci cmd_blk = scsi_cmd_priv(scmd); 273262306a36Sopenharmony_ci } 273362306a36Sopenharmony_ci if (cmd_blk) 273462306a36Sopenharmony_ci cmd_blk->status = next_stat_mbox->status; 273562306a36Sopenharmony_ci else 273662306a36Sopenharmony_ci dev_err(&cb->pdev->dev, 273762306a36Sopenharmony_ci "Unhandled command completion %d\n", id); 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci memset(next_stat_mbox, 0, sizeof(struct myrb_stat_mbox)); 274062306a36Sopenharmony_ci if (++next_stat_mbox > cb->last_stat_mbox) 274162306a36Sopenharmony_ci next_stat_mbox = cb->first_stat_mbox; 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci if (cmd_blk) { 274462306a36Sopenharmony_ci if (id < 3) 274562306a36Sopenharmony_ci myrb_handle_cmdblk(cb, cmd_blk); 274662306a36Sopenharmony_ci else 274762306a36Sopenharmony_ci myrb_handle_scsi(cb, cmd_blk, scmd); 274862306a36Sopenharmony_ci } 274962306a36Sopenharmony_ci } 275062306a36Sopenharmony_ci cb->next_stat_mbox = next_stat_mbox; 275162306a36Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 275262306a36Sopenharmony_ci return IRQ_HANDLED; 275362306a36Sopenharmony_ci} 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_cistatic struct myrb_privdata DAC960_LA_privdata = { 275662306a36Sopenharmony_ci .hw_init = DAC960_LA_hw_init, 275762306a36Sopenharmony_ci .irq_handler = DAC960_LA_intr_handler, 275862306a36Sopenharmony_ci .mmio_size = DAC960_LA_mmio_size, 275962306a36Sopenharmony_ci}; 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci/* 276262306a36Sopenharmony_ci * DAC960 PG Series Controllers 276362306a36Sopenharmony_ci */ 276462306a36Sopenharmony_cistatic inline void DAC960_PG_hw_mbox_new_cmd(void __iomem *base) 276562306a36Sopenharmony_ci{ 276662306a36Sopenharmony_ci writel(DAC960_PG_IDB_HWMBOX_NEW_CMD, base + DAC960_PG_IDB_OFFSET); 276762306a36Sopenharmony_ci} 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_cistatic inline void DAC960_PG_ack_hw_mbox_status(void __iomem *base) 277062306a36Sopenharmony_ci{ 277162306a36Sopenharmony_ci writel(DAC960_PG_IDB_HWMBOX_ACK_STS, base + DAC960_PG_IDB_OFFSET); 277262306a36Sopenharmony_ci} 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_cistatic inline void DAC960_PG_reset_ctrl(void __iomem *base) 277562306a36Sopenharmony_ci{ 277662306a36Sopenharmony_ci writel(DAC960_PG_IDB_CTRL_RESET, base + DAC960_PG_IDB_OFFSET); 277762306a36Sopenharmony_ci} 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_cistatic inline void DAC960_PG_mem_mbox_new_cmd(void __iomem *base) 278062306a36Sopenharmony_ci{ 278162306a36Sopenharmony_ci writel(DAC960_PG_IDB_MMBOX_NEW_CMD, base + DAC960_PG_IDB_OFFSET); 278262306a36Sopenharmony_ci} 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_cistatic inline bool DAC960_PG_hw_mbox_is_full(void __iomem *base) 278562306a36Sopenharmony_ci{ 278662306a36Sopenharmony_ci unsigned char idb = readl(base + DAC960_PG_IDB_OFFSET); 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci return idb & DAC960_PG_IDB_HWMBOX_FULL; 278962306a36Sopenharmony_ci} 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_cistatic inline bool DAC960_PG_init_in_progress(void __iomem *base) 279262306a36Sopenharmony_ci{ 279362306a36Sopenharmony_ci unsigned char idb = readl(base + DAC960_PG_IDB_OFFSET); 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci return idb & DAC960_PG_IDB_INIT_IN_PROGRESS; 279662306a36Sopenharmony_ci} 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_cistatic inline void DAC960_PG_ack_hw_mbox_intr(void __iomem *base) 279962306a36Sopenharmony_ci{ 280062306a36Sopenharmony_ci writel(DAC960_PG_ODB_HWMBOX_ACK_IRQ, base + DAC960_PG_ODB_OFFSET); 280162306a36Sopenharmony_ci} 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_cistatic inline void DAC960_PG_ack_intr(void __iomem *base) 280462306a36Sopenharmony_ci{ 280562306a36Sopenharmony_ci writel(DAC960_PG_ODB_HWMBOX_ACK_IRQ | DAC960_PG_ODB_MMBOX_ACK_IRQ, 280662306a36Sopenharmony_ci base + DAC960_PG_ODB_OFFSET); 280762306a36Sopenharmony_ci} 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_cistatic inline bool DAC960_PG_hw_mbox_status_available(void __iomem *base) 281062306a36Sopenharmony_ci{ 281162306a36Sopenharmony_ci unsigned char odb = readl(base + DAC960_PG_ODB_OFFSET); 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_ci return odb & DAC960_PG_ODB_HWMBOX_STS_AVAIL; 281462306a36Sopenharmony_ci} 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_cistatic inline void DAC960_PG_enable_intr(void __iomem *base) 281762306a36Sopenharmony_ci{ 281862306a36Sopenharmony_ci unsigned int imask = (unsigned int)-1; 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci imask &= ~DAC960_PG_IRQMASK_DISABLE_IRQ; 282162306a36Sopenharmony_ci writel(imask, base + DAC960_PG_IRQMASK_OFFSET); 282262306a36Sopenharmony_ci} 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_cistatic inline void DAC960_PG_disable_intr(void __iomem *base) 282562306a36Sopenharmony_ci{ 282662306a36Sopenharmony_ci unsigned int imask = (unsigned int)-1; 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci writel(imask, base + DAC960_PG_IRQMASK_OFFSET); 282962306a36Sopenharmony_ci} 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_cistatic inline void DAC960_PG_write_cmd_mbox(union myrb_cmd_mbox *mem_mbox, 283262306a36Sopenharmony_ci union myrb_cmd_mbox *mbox) 283362306a36Sopenharmony_ci{ 283462306a36Sopenharmony_ci mem_mbox->words[1] = mbox->words[1]; 283562306a36Sopenharmony_ci mem_mbox->words[2] = mbox->words[2]; 283662306a36Sopenharmony_ci mem_mbox->words[3] = mbox->words[3]; 283762306a36Sopenharmony_ci /* Memory barrier to prevent reordering */ 283862306a36Sopenharmony_ci wmb(); 283962306a36Sopenharmony_ci mem_mbox->words[0] = mbox->words[0]; 284062306a36Sopenharmony_ci /* Memory barrier to force PCI access */ 284162306a36Sopenharmony_ci mb(); 284262306a36Sopenharmony_ci} 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_cistatic inline void DAC960_PG_write_hw_mbox(void __iomem *base, 284562306a36Sopenharmony_ci union myrb_cmd_mbox *mbox) 284662306a36Sopenharmony_ci{ 284762306a36Sopenharmony_ci writel(mbox->words[0], base + DAC960_PG_CMDOP_OFFSET); 284862306a36Sopenharmony_ci writel(mbox->words[1], base + DAC960_PG_MBOX4_OFFSET); 284962306a36Sopenharmony_ci writel(mbox->words[2], base + DAC960_PG_MBOX8_OFFSET); 285062306a36Sopenharmony_ci writeb(mbox->bytes[12], base + DAC960_PG_MBOX12_OFFSET); 285162306a36Sopenharmony_ci} 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_cistatic inline unsigned short 285462306a36Sopenharmony_ciDAC960_PG_read_status(void __iomem *base) 285562306a36Sopenharmony_ci{ 285662306a36Sopenharmony_ci return readw(base + DAC960_PG_STS_OFFSET); 285762306a36Sopenharmony_ci} 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_cistatic inline bool 286062306a36Sopenharmony_ciDAC960_PG_read_error_status(void __iomem *base, unsigned char *error, 286162306a36Sopenharmony_ci unsigned char *param0, unsigned char *param1) 286262306a36Sopenharmony_ci{ 286362306a36Sopenharmony_ci unsigned char errsts = readb(base + DAC960_PG_ERRSTS_OFFSET); 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci if (!(errsts & DAC960_PG_ERRSTS_PENDING)) 286662306a36Sopenharmony_ci return false; 286762306a36Sopenharmony_ci errsts &= ~DAC960_PG_ERRSTS_PENDING; 286862306a36Sopenharmony_ci *error = errsts; 286962306a36Sopenharmony_ci *param0 = readb(base + DAC960_PG_CMDOP_OFFSET); 287062306a36Sopenharmony_ci *param1 = readb(base + DAC960_PG_CMDID_OFFSET); 287162306a36Sopenharmony_ci writeb(0, base + DAC960_PG_ERRSTS_OFFSET); 287262306a36Sopenharmony_ci return true; 287362306a36Sopenharmony_ci} 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_cistatic inline unsigned short 287662306a36Sopenharmony_ciDAC960_PG_mbox_init(struct pci_dev *pdev, void __iomem *base, 287762306a36Sopenharmony_ci union myrb_cmd_mbox *mbox) 287862306a36Sopenharmony_ci{ 287962306a36Sopenharmony_ci unsigned short status; 288062306a36Sopenharmony_ci int timeout = 0; 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci while (timeout < MYRB_MAILBOX_TIMEOUT) { 288362306a36Sopenharmony_ci if (!DAC960_PG_hw_mbox_is_full(base)) 288462306a36Sopenharmony_ci break; 288562306a36Sopenharmony_ci udelay(10); 288662306a36Sopenharmony_ci timeout++; 288762306a36Sopenharmony_ci } 288862306a36Sopenharmony_ci if (DAC960_PG_hw_mbox_is_full(base)) { 288962306a36Sopenharmony_ci dev_err(&pdev->dev, 289062306a36Sopenharmony_ci "Timeout waiting for empty mailbox\n"); 289162306a36Sopenharmony_ci return MYRB_STATUS_SUBSYS_TIMEOUT; 289262306a36Sopenharmony_ci } 289362306a36Sopenharmony_ci DAC960_PG_write_hw_mbox(base, mbox); 289462306a36Sopenharmony_ci DAC960_PG_hw_mbox_new_cmd(base); 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci timeout = 0; 289762306a36Sopenharmony_ci while (timeout < MYRB_MAILBOX_TIMEOUT) { 289862306a36Sopenharmony_ci if (DAC960_PG_hw_mbox_status_available(base)) 289962306a36Sopenharmony_ci break; 290062306a36Sopenharmony_ci udelay(10); 290162306a36Sopenharmony_ci timeout++; 290262306a36Sopenharmony_ci } 290362306a36Sopenharmony_ci if (!DAC960_PG_hw_mbox_status_available(base)) { 290462306a36Sopenharmony_ci dev_err(&pdev->dev, 290562306a36Sopenharmony_ci "Timeout waiting for mailbox status\n"); 290662306a36Sopenharmony_ci return MYRB_STATUS_SUBSYS_TIMEOUT; 290762306a36Sopenharmony_ci } 290862306a36Sopenharmony_ci status = DAC960_PG_read_status(base); 290962306a36Sopenharmony_ci DAC960_PG_ack_hw_mbox_intr(base); 291062306a36Sopenharmony_ci DAC960_PG_ack_hw_mbox_status(base); 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci return status; 291362306a36Sopenharmony_ci} 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_cistatic int DAC960_PG_hw_init(struct pci_dev *pdev, 291662306a36Sopenharmony_ci struct myrb_hba *cb, void __iomem *base) 291762306a36Sopenharmony_ci{ 291862306a36Sopenharmony_ci int timeout = 0; 291962306a36Sopenharmony_ci unsigned char error, parm0, parm1; 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci DAC960_PG_disable_intr(base); 292262306a36Sopenharmony_ci DAC960_PG_ack_hw_mbox_status(base); 292362306a36Sopenharmony_ci udelay(1000); 292462306a36Sopenharmony_ci while (DAC960_PG_init_in_progress(base) && 292562306a36Sopenharmony_ci timeout < MYRB_MAILBOX_TIMEOUT) { 292662306a36Sopenharmony_ci if (DAC960_PG_read_error_status(base, &error, 292762306a36Sopenharmony_ci &parm0, &parm1) && 292862306a36Sopenharmony_ci myrb_err_status(cb, error, parm0, parm1)) 292962306a36Sopenharmony_ci return -EIO; 293062306a36Sopenharmony_ci udelay(10); 293162306a36Sopenharmony_ci timeout++; 293262306a36Sopenharmony_ci } 293362306a36Sopenharmony_ci if (timeout == MYRB_MAILBOX_TIMEOUT) { 293462306a36Sopenharmony_ci dev_err(&pdev->dev, 293562306a36Sopenharmony_ci "Timeout waiting for Controller Initialisation\n"); 293662306a36Sopenharmony_ci return -ETIMEDOUT; 293762306a36Sopenharmony_ci } 293862306a36Sopenharmony_ci if (!myrb_enable_mmio(cb, DAC960_PG_mbox_init)) { 293962306a36Sopenharmony_ci dev_err(&pdev->dev, 294062306a36Sopenharmony_ci "Unable to Enable Memory Mailbox Interface\n"); 294162306a36Sopenharmony_ci DAC960_PG_reset_ctrl(base); 294262306a36Sopenharmony_ci return -ENODEV; 294362306a36Sopenharmony_ci } 294462306a36Sopenharmony_ci DAC960_PG_enable_intr(base); 294562306a36Sopenharmony_ci cb->qcmd = myrb_qcmd; 294662306a36Sopenharmony_ci cb->write_cmd_mbox = DAC960_PG_write_cmd_mbox; 294762306a36Sopenharmony_ci if (cb->dual_mode_interface) 294862306a36Sopenharmony_ci cb->get_cmd_mbox = DAC960_PG_mem_mbox_new_cmd; 294962306a36Sopenharmony_ci else 295062306a36Sopenharmony_ci cb->get_cmd_mbox = DAC960_PG_hw_mbox_new_cmd; 295162306a36Sopenharmony_ci cb->disable_intr = DAC960_PG_disable_intr; 295262306a36Sopenharmony_ci cb->reset = DAC960_PG_reset_ctrl; 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci return 0; 295562306a36Sopenharmony_ci} 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_cistatic irqreturn_t DAC960_PG_intr_handler(int irq, void *arg) 295862306a36Sopenharmony_ci{ 295962306a36Sopenharmony_ci struct myrb_hba *cb = arg; 296062306a36Sopenharmony_ci void __iomem *base = cb->io_base; 296162306a36Sopenharmony_ci struct myrb_stat_mbox *next_stat_mbox; 296262306a36Sopenharmony_ci unsigned long flags; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 296562306a36Sopenharmony_ci DAC960_PG_ack_intr(base); 296662306a36Sopenharmony_ci next_stat_mbox = cb->next_stat_mbox; 296762306a36Sopenharmony_ci while (next_stat_mbox->valid) { 296862306a36Sopenharmony_ci unsigned char id = next_stat_mbox->id; 296962306a36Sopenharmony_ci struct scsi_cmnd *scmd = NULL; 297062306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = NULL; 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci if (id == MYRB_DCMD_TAG) 297362306a36Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 297462306a36Sopenharmony_ci else if (id == MYRB_MCMD_TAG) 297562306a36Sopenharmony_ci cmd_blk = &cb->mcmd_blk; 297662306a36Sopenharmony_ci else { 297762306a36Sopenharmony_ci scmd = scsi_host_find_tag(cb->host, id - 3); 297862306a36Sopenharmony_ci if (scmd) 297962306a36Sopenharmony_ci cmd_blk = scsi_cmd_priv(scmd); 298062306a36Sopenharmony_ci } 298162306a36Sopenharmony_ci if (cmd_blk) 298262306a36Sopenharmony_ci cmd_blk->status = next_stat_mbox->status; 298362306a36Sopenharmony_ci else 298462306a36Sopenharmony_ci dev_err(&cb->pdev->dev, 298562306a36Sopenharmony_ci "Unhandled command completion %d\n", id); 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci memset(next_stat_mbox, 0, sizeof(struct myrb_stat_mbox)); 298862306a36Sopenharmony_ci if (++next_stat_mbox > cb->last_stat_mbox) 298962306a36Sopenharmony_ci next_stat_mbox = cb->first_stat_mbox; 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci if (id < 3) 299262306a36Sopenharmony_ci myrb_handle_cmdblk(cb, cmd_blk); 299362306a36Sopenharmony_ci else 299462306a36Sopenharmony_ci myrb_handle_scsi(cb, cmd_blk, scmd); 299562306a36Sopenharmony_ci } 299662306a36Sopenharmony_ci cb->next_stat_mbox = next_stat_mbox; 299762306a36Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 299862306a36Sopenharmony_ci return IRQ_HANDLED; 299962306a36Sopenharmony_ci} 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_cistatic struct myrb_privdata DAC960_PG_privdata = { 300262306a36Sopenharmony_ci .hw_init = DAC960_PG_hw_init, 300362306a36Sopenharmony_ci .irq_handler = DAC960_PG_intr_handler, 300462306a36Sopenharmony_ci .mmio_size = DAC960_PG_mmio_size, 300562306a36Sopenharmony_ci}; 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci/* 300962306a36Sopenharmony_ci * DAC960 PD Series Controllers 301062306a36Sopenharmony_ci */ 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_cistatic inline void DAC960_PD_hw_mbox_new_cmd(void __iomem *base) 301362306a36Sopenharmony_ci{ 301462306a36Sopenharmony_ci writeb(DAC960_PD_IDB_HWMBOX_NEW_CMD, base + DAC960_PD_IDB_OFFSET); 301562306a36Sopenharmony_ci} 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_cistatic inline void DAC960_PD_ack_hw_mbox_status(void __iomem *base) 301862306a36Sopenharmony_ci{ 301962306a36Sopenharmony_ci writeb(DAC960_PD_IDB_HWMBOX_ACK_STS, base + DAC960_PD_IDB_OFFSET); 302062306a36Sopenharmony_ci} 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_cistatic inline void DAC960_PD_reset_ctrl(void __iomem *base) 302362306a36Sopenharmony_ci{ 302462306a36Sopenharmony_ci writeb(DAC960_PD_IDB_CTRL_RESET, base + DAC960_PD_IDB_OFFSET); 302562306a36Sopenharmony_ci} 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_cistatic inline bool DAC960_PD_hw_mbox_is_full(void __iomem *base) 302862306a36Sopenharmony_ci{ 302962306a36Sopenharmony_ci unsigned char idb = readb(base + DAC960_PD_IDB_OFFSET); 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci return idb & DAC960_PD_IDB_HWMBOX_FULL; 303262306a36Sopenharmony_ci} 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_cistatic inline bool DAC960_PD_init_in_progress(void __iomem *base) 303562306a36Sopenharmony_ci{ 303662306a36Sopenharmony_ci unsigned char idb = readb(base + DAC960_PD_IDB_OFFSET); 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci return idb & DAC960_PD_IDB_INIT_IN_PROGRESS; 303962306a36Sopenharmony_ci} 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_cistatic inline void DAC960_PD_ack_intr(void __iomem *base) 304262306a36Sopenharmony_ci{ 304362306a36Sopenharmony_ci writeb(DAC960_PD_ODB_HWMBOX_ACK_IRQ, base + DAC960_PD_ODB_OFFSET); 304462306a36Sopenharmony_ci} 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_cistatic inline bool DAC960_PD_hw_mbox_status_available(void __iomem *base) 304762306a36Sopenharmony_ci{ 304862306a36Sopenharmony_ci unsigned char odb = readb(base + DAC960_PD_ODB_OFFSET); 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci return odb & DAC960_PD_ODB_HWMBOX_STS_AVAIL; 305162306a36Sopenharmony_ci} 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_cistatic inline void DAC960_PD_enable_intr(void __iomem *base) 305462306a36Sopenharmony_ci{ 305562306a36Sopenharmony_ci writeb(DAC960_PD_IRQMASK_ENABLE_IRQ, base + DAC960_PD_IRQEN_OFFSET); 305662306a36Sopenharmony_ci} 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_cistatic inline void DAC960_PD_disable_intr(void __iomem *base) 305962306a36Sopenharmony_ci{ 306062306a36Sopenharmony_ci writeb(0, base + DAC960_PD_IRQEN_OFFSET); 306162306a36Sopenharmony_ci} 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_cistatic inline void DAC960_PD_write_cmd_mbox(void __iomem *base, 306462306a36Sopenharmony_ci union myrb_cmd_mbox *mbox) 306562306a36Sopenharmony_ci{ 306662306a36Sopenharmony_ci writel(mbox->words[0], base + DAC960_PD_CMDOP_OFFSET); 306762306a36Sopenharmony_ci writel(mbox->words[1], base + DAC960_PD_MBOX4_OFFSET); 306862306a36Sopenharmony_ci writel(mbox->words[2], base + DAC960_PD_MBOX8_OFFSET); 306962306a36Sopenharmony_ci writeb(mbox->bytes[12], base + DAC960_PD_MBOX12_OFFSET); 307062306a36Sopenharmony_ci} 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_cistatic inline unsigned char 307362306a36Sopenharmony_ciDAC960_PD_read_status_cmd_ident(void __iomem *base) 307462306a36Sopenharmony_ci{ 307562306a36Sopenharmony_ci return readb(base + DAC960_PD_STSID_OFFSET); 307662306a36Sopenharmony_ci} 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_cistatic inline unsigned short 307962306a36Sopenharmony_ciDAC960_PD_read_status(void __iomem *base) 308062306a36Sopenharmony_ci{ 308162306a36Sopenharmony_ci return readw(base + DAC960_PD_STS_OFFSET); 308262306a36Sopenharmony_ci} 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_cistatic inline bool 308562306a36Sopenharmony_ciDAC960_PD_read_error_status(void __iomem *base, unsigned char *error, 308662306a36Sopenharmony_ci unsigned char *param0, unsigned char *param1) 308762306a36Sopenharmony_ci{ 308862306a36Sopenharmony_ci unsigned char errsts = readb(base + DAC960_PD_ERRSTS_OFFSET); 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci if (!(errsts & DAC960_PD_ERRSTS_PENDING)) 309162306a36Sopenharmony_ci return false; 309262306a36Sopenharmony_ci errsts &= ~DAC960_PD_ERRSTS_PENDING; 309362306a36Sopenharmony_ci *error = errsts; 309462306a36Sopenharmony_ci *param0 = readb(base + DAC960_PD_CMDOP_OFFSET); 309562306a36Sopenharmony_ci *param1 = readb(base + DAC960_PD_CMDID_OFFSET); 309662306a36Sopenharmony_ci writeb(0, base + DAC960_PD_ERRSTS_OFFSET); 309762306a36Sopenharmony_ci return true; 309862306a36Sopenharmony_ci} 309962306a36Sopenharmony_ci 310062306a36Sopenharmony_cistatic void DAC960_PD_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) 310162306a36Sopenharmony_ci{ 310262306a36Sopenharmony_ci void __iomem *base = cb->io_base; 310362306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci while (DAC960_PD_hw_mbox_is_full(base)) 310662306a36Sopenharmony_ci udelay(1); 310762306a36Sopenharmony_ci DAC960_PD_write_cmd_mbox(base, mbox); 310862306a36Sopenharmony_ci DAC960_PD_hw_mbox_new_cmd(base); 310962306a36Sopenharmony_ci} 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_cistatic int DAC960_PD_hw_init(struct pci_dev *pdev, 311262306a36Sopenharmony_ci struct myrb_hba *cb, void __iomem *base) 311362306a36Sopenharmony_ci{ 311462306a36Sopenharmony_ci int timeout = 0; 311562306a36Sopenharmony_ci unsigned char error, parm0, parm1; 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci if (!request_region(cb->io_addr, 0x80, "myrb")) { 311862306a36Sopenharmony_ci dev_err(&pdev->dev, "IO port 0x%lx busy\n", 311962306a36Sopenharmony_ci (unsigned long)cb->io_addr); 312062306a36Sopenharmony_ci return -EBUSY; 312162306a36Sopenharmony_ci } 312262306a36Sopenharmony_ci DAC960_PD_disable_intr(base); 312362306a36Sopenharmony_ci DAC960_PD_ack_hw_mbox_status(base); 312462306a36Sopenharmony_ci udelay(1000); 312562306a36Sopenharmony_ci while (DAC960_PD_init_in_progress(base) && 312662306a36Sopenharmony_ci timeout < MYRB_MAILBOX_TIMEOUT) { 312762306a36Sopenharmony_ci if (DAC960_PD_read_error_status(base, &error, 312862306a36Sopenharmony_ci &parm0, &parm1) && 312962306a36Sopenharmony_ci myrb_err_status(cb, error, parm0, parm1)) 313062306a36Sopenharmony_ci return -EIO; 313162306a36Sopenharmony_ci udelay(10); 313262306a36Sopenharmony_ci timeout++; 313362306a36Sopenharmony_ci } 313462306a36Sopenharmony_ci if (timeout == MYRB_MAILBOX_TIMEOUT) { 313562306a36Sopenharmony_ci dev_err(&pdev->dev, 313662306a36Sopenharmony_ci "Timeout waiting for Controller Initialisation\n"); 313762306a36Sopenharmony_ci return -ETIMEDOUT; 313862306a36Sopenharmony_ci } 313962306a36Sopenharmony_ci if (!myrb_enable_mmio(cb, NULL)) { 314062306a36Sopenharmony_ci dev_err(&pdev->dev, 314162306a36Sopenharmony_ci "Unable to Enable Memory Mailbox Interface\n"); 314262306a36Sopenharmony_ci DAC960_PD_reset_ctrl(base); 314362306a36Sopenharmony_ci return -ENODEV; 314462306a36Sopenharmony_ci } 314562306a36Sopenharmony_ci DAC960_PD_enable_intr(base); 314662306a36Sopenharmony_ci cb->qcmd = DAC960_PD_qcmd; 314762306a36Sopenharmony_ci cb->disable_intr = DAC960_PD_disable_intr; 314862306a36Sopenharmony_ci cb->reset = DAC960_PD_reset_ctrl; 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci return 0; 315162306a36Sopenharmony_ci} 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_cistatic irqreturn_t DAC960_PD_intr_handler(int irq, void *arg) 315462306a36Sopenharmony_ci{ 315562306a36Sopenharmony_ci struct myrb_hba *cb = arg; 315662306a36Sopenharmony_ci void __iomem *base = cb->io_base; 315762306a36Sopenharmony_ci unsigned long flags; 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 316062306a36Sopenharmony_ci while (DAC960_PD_hw_mbox_status_available(base)) { 316162306a36Sopenharmony_ci unsigned char id = DAC960_PD_read_status_cmd_ident(base); 316262306a36Sopenharmony_ci struct scsi_cmnd *scmd = NULL; 316362306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = NULL; 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci if (id == MYRB_DCMD_TAG) 316662306a36Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 316762306a36Sopenharmony_ci else if (id == MYRB_MCMD_TAG) 316862306a36Sopenharmony_ci cmd_blk = &cb->mcmd_blk; 316962306a36Sopenharmony_ci else { 317062306a36Sopenharmony_ci scmd = scsi_host_find_tag(cb->host, id - 3); 317162306a36Sopenharmony_ci if (scmd) 317262306a36Sopenharmony_ci cmd_blk = scsi_cmd_priv(scmd); 317362306a36Sopenharmony_ci } 317462306a36Sopenharmony_ci if (cmd_blk) 317562306a36Sopenharmony_ci cmd_blk->status = DAC960_PD_read_status(base); 317662306a36Sopenharmony_ci else 317762306a36Sopenharmony_ci dev_err(&cb->pdev->dev, 317862306a36Sopenharmony_ci "Unhandled command completion %d\n", id); 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_ci DAC960_PD_ack_intr(base); 318162306a36Sopenharmony_ci DAC960_PD_ack_hw_mbox_status(base); 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci if (id < 3) 318462306a36Sopenharmony_ci myrb_handle_cmdblk(cb, cmd_blk); 318562306a36Sopenharmony_ci else 318662306a36Sopenharmony_ci myrb_handle_scsi(cb, cmd_blk, scmd); 318762306a36Sopenharmony_ci } 318862306a36Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 318962306a36Sopenharmony_ci return IRQ_HANDLED; 319062306a36Sopenharmony_ci} 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_cistatic struct myrb_privdata DAC960_PD_privdata = { 319362306a36Sopenharmony_ci .hw_init = DAC960_PD_hw_init, 319462306a36Sopenharmony_ci .irq_handler = DAC960_PD_intr_handler, 319562306a36Sopenharmony_ci .mmio_size = DAC960_PD_mmio_size, 319662306a36Sopenharmony_ci}; 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci/* 320062306a36Sopenharmony_ci * DAC960 P Series Controllers 320162306a36Sopenharmony_ci * 320262306a36Sopenharmony_ci * Similar to the DAC960 PD Series Controllers, but some commands have 320362306a36Sopenharmony_ci * to be translated. 320462306a36Sopenharmony_ci */ 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_cistatic inline void myrb_translate_enquiry(void *enq) 320762306a36Sopenharmony_ci{ 320862306a36Sopenharmony_ci memcpy(enq + 132, enq + 36, 64); 320962306a36Sopenharmony_ci memset(enq + 36, 0, 96); 321062306a36Sopenharmony_ci} 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_cistatic inline void myrb_translate_devstate(void *state) 321362306a36Sopenharmony_ci{ 321462306a36Sopenharmony_ci memcpy(state + 2, state + 3, 1); 321562306a36Sopenharmony_ci memmove(state + 4, state + 5, 2); 321662306a36Sopenharmony_ci memmove(state + 6, state + 8, 4); 321762306a36Sopenharmony_ci} 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_cistatic inline void myrb_translate_to_rw_command(struct myrb_cmdblk *cmd_blk) 322062306a36Sopenharmony_ci{ 322162306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 322262306a36Sopenharmony_ci int ldev_num = mbox->type5.ld.ldev_num; 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci mbox->bytes[3] &= 0x7; 322562306a36Sopenharmony_ci mbox->bytes[3] |= mbox->bytes[7] << 6; 322662306a36Sopenharmony_ci mbox->bytes[7] = ldev_num; 322762306a36Sopenharmony_ci} 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_cistatic inline void myrb_translate_from_rw_command(struct myrb_cmdblk *cmd_blk) 323062306a36Sopenharmony_ci{ 323162306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 323262306a36Sopenharmony_ci int ldev_num = mbox->bytes[7]; 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci mbox->bytes[7] = mbox->bytes[3] >> 6; 323562306a36Sopenharmony_ci mbox->bytes[3] &= 0x7; 323662306a36Sopenharmony_ci mbox->bytes[3] |= ldev_num << 3; 323762306a36Sopenharmony_ci} 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_cistatic void DAC960_P_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) 324062306a36Sopenharmony_ci{ 324162306a36Sopenharmony_ci void __iomem *base = cb->io_base; 324262306a36Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci switch (mbox->common.opcode) { 324562306a36Sopenharmony_ci case MYRB_CMD_ENQUIRY: 324662306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_ENQUIRY_OLD; 324762306a36Sopenharmony_ci break; 324862306a36Sopenharmony_ci case MYRB_CMD_GET_DEVICE_STATE: 324962306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_GET_DEVICE_STATE_OLD; 325062306a36Sopenharmony_ci break; 325162306a36Sopenharmony_ci case MYRB_CMD_READ: 325262306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_READ_OLD; 325362306a36Sopenharmony_ci myrb_translate_to_rw_command(cmd_blk); 325462306a36Sopenharmony_ci break; 325562306a36Sopenharmony_ci case MYRB_CMD_WRITE: 325662306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_WRITE_OLD; 325762306a36Sopenharmony_ci myrb_translate_to_rw_command(cmd_blk); 325862306a36Sopenharmony_ci break; 325962306a36Sopenharmony_ci case MYRB_CMD_READ_SG: 326062306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_READ_SG_OLD; 326162306a36Sopenharmony_ci myrb_translate_to_rw_command(cmd_blk); 326262306a36Sopenharmony_ci break; 326362306a36Sopenharmony_ci case MYRB_CMD_WRITE_SG: 326462306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_WRITE_SG_OLD; 326562306a36Sopenharmony_ci myrb_translate_to_rw_command(cmd_blk); 326662306a36Sopenharmony_ci break; 326762306a36Sopenharmony_ci default: 326862306a36Sopenharmony_ci break; 326962306a36Sopenharmony_ci } 327062306a36Sopenharmony_ci while (DAC960_PD_hw_mbox_is_full(base)) 327162306a36Sopenharmony_ci udelay(1); 327262306a36Sopenharmony_ci DAC960_PD_write_cmd_mbox(base, mbox); 327362306a36Sopenharmony_ci DAC960_PD_hw_mbox_new_cmd(base); 327462306a36Sopenharmony_ci} 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_cistatic int DAC960_P_hw_init(struct pci_dev *pdev, 327862306a36Sopenharmony_ci struct myrb_hba *cb, void __iomem *base) 327962306a36Sopenharmony_ci{ 328062306a36Sopenharmony_ci int timeout = 0; 328162306a36Sopenharmony_ci unsigned char error, parm0, parm1; 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci if (!request_region(cb->io_addr, 0x80, "myrb")) { 328462306a36Sopenharmony_ci dev_err(&pdev->dev, "IO port 0x%lx busy\n", 328562306a36Sopenharmony_ci (unsigned long)cb->io_addr); 328662306a36Sopenharmony_ci return -EBUSY; 328762306a36Sopenharmony_ci } 328862306a36Sopenharmony_ci DAC960_PD_disable_intr(base); 328962306a36Sopenharmony_ci DAC960_PD_ack_hw_mbox_status(base); 329062306a36Sopenharmony_ci udelay(1000); 329162306a36Sopenharmony_ci while (DAC960_PD_init_in_progress(base) && 329262306a36Sopenharmony_ci timeout < MYRB_MAILBOX_TIMEOUT) { 329362306a36Sopenharmony_ci if (DAC960_PD_read_error_status(base, &error, 329462306a36Sopenharmony_ci &parm0, &parm1) && 329562306a36Sopenharmony_ci myrb_err_status(cb, error, parm0, parm1)) 329662306a36Sopenharmony_ci return -EAGAIN; 329762306a36Sopenharmony_ci udelay(10); 329862306a36Sopenharmony_ci timeout++; 329962306a36Sopenharmony_ci } 330062306a36Sopenharmony_ci if (timeout == MYRB_MAILBOX_TIMEOUT) { 330162306a36Sopenharmony_ci dev_err(&pdev->dev, 330262306a36Sopenharmony_ci "Timeout waiting for Controller Initialisation\n"); 330362306a36Sopenharmony_ci return -ETIMEDOUT; 330462306a36Sopenharmony_ci } 330562306a36Sopenharmony_ci if (!myrb_enable_mmio(cb, NULL)) { 330662306a36Sopenharmony_ci dev_err(&pdev->dev, 330762306a36Sopenharmony_ci "Unable to allocate DMA mapped memory\n"); 330862306a36Sopenharmony_ci DAC960_PD_reset_ctrl(base); 330962306a36Sopenharmony_ci return -ETIMEDOUT; 331062306a36Sopenharmony_ci } 331162306a36Sopenharmony_ci DAC960_PD_enable_intr(base); 331262306a36Sopenharmony_ci cb->qcmd = DAC960_P_qcmd; 331362306a36Sopenharmony_ci cb->disable_intr = DAC960_PD_disable_intr; 331462306a36Sopenharmony_ci cb->reset = DAC960_PD_reset_ctrl; 331562306a36Sopenharmony_ci 331662306a36Sopenharmony_ci return 0; 331762306a36Sopenharmony_ci} 331862306a36Sopenharmony_ci 331962306a36Sopenharmony_cistatic irqreturn_t DAC960_P_intr_handler(int irq, void *arg) 332062306a36Sopenharmony_ci{ 332162306a36Sopenharmony_ci struct myrb_hba *cb = arg; 332262306a36Sopenharmony_ci void __iomem *base = cb->io_base; 332362306a36Sopenharmony_ci unsigned long flags; 332462306a36Sopenharmony_ci 332562306a36Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 332662306a36Sopenharmony_ci while (DAC960_PD_hw_mbox_status_available(base)) { 332762306a36Sopenharmony_ci unsigned char id = DAC960_PD_read_status_cmd_ident(base); 332862306a36Sopenharmony_ci struct scsi_cmnd *scmd = NULL; 332962306a36Sopenharmony_ci struct myrb_cmdblk *cmd_blk = NULL; 333062306a36Sopenharmony_ci union myrb_cmd_mbox *mbox; 333162306a36Sopenharmony_ci enum myrb_cmd_opcode op; 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci 333462306a36Sopenharmony_ci if (id == MYRB_DCMD_TAG) 333562306a36Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 333662306a36Sopenharmony_ci else if (id == MYRB_MCMD_TAG) 333762306a36Sopenharmony_ci cmd_blk = &cb->mcmd_blk; 333862306a36Sopenharmony_ci else { 333962306a36Sopenharmony_ci scmd = scsi_host_find_tag(cb->host, id - 3); 334062306a36Sopenharmony_ci if (scmd) 334162306a36Sopenharmony_ci cmd_blk = scsi_cmd_priv(scmd); 334262306a36Sopenharmony_ci } 334362306a36Sopenharmony_ci if (cmd_blk) 334462306a36Sopenharmony_ci cmd_blk->status = DAC960_PD_read_status(base); 334562306a36Sopenharmony_ci else 334662306a36Sopenharmony_ci dev_err(&cb->pdev->dev, 334762306a36Sopenharmony_ci "Unhandled command completion %d\n", id); 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci DAC960_PD_ack_intr(base); 335062306a36Sopenharmony_ci DAC960_PD_ack_hw_mbox_status(base); 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci if (!cmd_blk) 335362306a36Sopenharmony_ci continue; 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci mbox = &cmd_blk->mbox; 335662306a36Sopenharmony_ci op = mbox->common.opcode; 335762306a36Sopenharmony_ci switch (op) { 335862306a36Sopenharmony_ci case MYRB_CMD_ENQUIRY_OLD: 335962306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_ENQUIRY; 336062306a36Sopenharmony_ci myrb_translate_enquiry(cb->enquiry); 336162306a36Sopenharmony_ci break; 336262306a36Sopenharmony_ci case MYRB_CMD_READ_OLD: 336362306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_READ; 336462306a36Sopenharmony_ci myrb_translate_from_rw_command(cmd_blk); 336562306a36Sopenharmony_ci break; 336662306a36Sopenharmony_ci case MYRB_CMD_WRITE_OLD: 336762306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_WRITE; 336862306a36Sopenharmony_ci myrb_translate_from_rw_command(cmd_blk); 336962306a36Sopenharmony_ci break; 337062306a36Sopenharmony_ci case MYRB_CMD_READ_SG_OLD: 337162306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_READ_SG; 337262306a36Sopenharmony_ci myrb_translate_from_rw_command(cmd_blk); 337362306a36Sopenharmony_ci break; 337462306a36Sopenharmony_ci case MYRB_CMD_WRITE_SG_OLD: 337562306a36Sopenharmony_ci mbox->common.opcode = MYRB_CMD_WRITE_SG; 337662306a36Sopenharmony_ci myrb_translate_from_rw_command(cmd_blk); 337762306a36Sopenharmony_ci break; 337862306a36Sopenharmony_ci default: 337962306a36Sopenharmony_ci break; 338062306a36Sopenharmony_ci } 338162306a36Sopenharmony_ci if (id < 3) 338262306a36Sopenharmony_ci myrb_handle_cmdblk(cb, cmd_blk); 338362306a36Sopenharmony_ci else 338462306a36Sopenharmony_ci myrb_handle_scsi(cb, cmd_blk, scmd); 338562306a36Sopenharmony_ci } 338662306a36Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 338762306a36Sopenharmony_ci return IRQ_HANDLED; 338862306a36Sopenharmony_ci} 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_cistatic struct myrb_privdata DAC960_P_privdata = { 339162306a36Sopenharmony_ci .hw_init = DAC960_P_hw_init, 339262306a36Sopenharmony_ci .irq_handler = DAC960_P_intr_handler, 339362306a36Sopenharmony_ci .mmio_size = DAC960_PD_mmio_size, 339462306a36Sopenharmony_ci}; 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_cistatic struct myrb_hba *myrb_detect(struct pci_dev *pdev, 339762306a36Sopenharmony_ci const struct pci_device_id *entry) 339862306a36Sopenharmony_ci{ 339962306a36Sopenharmony_ci struct myrb_privdata *privdata = 340062306a36Sopenharmony_ci (struct myrb_privdata *)entry->driver_data; 340162306a36Sopenharmony_ci irq_handler_t irq_handler = privdata->irq_handler; 340262306a36Sopenharmony_ci unsigned int mmio_size = privdata->mmio_size; 340362306a36Sopenharmony_ci struct Scsi_Host *shost; 340462306a36Sopenharmony_ci struct myrb_hba *cb = NULL; 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci shost = scsi_host_alloc(&myrb_template, sizeof(struct myrb_hba)); 340762306a36Sopenharmony_ci if (!shost) { 340862306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to allocate Controller\n"); 340962306a36Sopenharmony_ci return NULL; 341062306a36Sopenharmony_ci } 341162306a36Sopenharmony_ci shost->max_cmd_len = 12; 341262306a36Sopenharmony_ci shost->max_lun = 256; 341362306a36Sopenharmony_ci cb = shost_priv(shost); 341462306a36Sopenharmony_ci mutex_init(&cb->dcmd_mutex); 341562306a36Sopenharmony_ci mutex_init(&cb->dma_mutex); 341662306a36Sopenharmony_ci cb->pdev = pdev; 341762306a36Sopenharmony_ci cb->host = shost; 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci if (pci_enable_device(pdev)) { 342062306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable PCI device\n"); 342162306a36Sopenharmony_ci scsi_host_put(shost); 342262306a36Sopenharmony_ci return NULL; 342362306a36Sopenharmony_ci } 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci if (privdata->hw_init == DAC960_PD_hw_init || 342662306a36Sopenharmony_ci privdata->hw_init == DAC960_P_hw_init) { 342762306a36Sopenharmony_ci cb->io_addr = pci_resource_start(pdev, 0); 342862306a36Sopenharmony_ci cb->pci_addr = pci_resource_start(pdev, 1); 342962306a36Sopenharmony_ci } else 343062306a36Sopenharmony_ci cb->pci_addr = pci_resource_start(pdev, 0); 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci pci_set_drvdata(pdev, cb); 343362306a36Sopenharmony_ci spin_lock_init(&cb->queue_lock); 343462306a36Sopenharmony_ci if (mmio_size < PAGE_SIZE) 343562306a36Sopenharmony_ci mmio_size = PAGE_SIZE; 343662306a36Sopenharmony_ci cb->mmio_base = ioremap(cb->pci_addr & PAGE_MASK, mmio_size); 343762306a36Sopenharmony_ci if (cb->mmio_base == NULL) { 343862306a36Sopenharmony_ci dev_err(&pdev->dev, 343962306a36Sopenharmony_ci "Unable to map Controller Register Window\n"); 344062306a36Sopenharmony_ci goto failure; 344162306a36Sopenharmony_ci } 344262306a36Sopenharmony_ci 344362306a36Sopenharmony_ci cb->io_base = cb->mmio_base + (cb->pci_addr & ~PAGE_MASK); 344462306a36Sopenharmony_ci if (privdata->hw_init(pdev, cb, cb->io_base)) 344562306a36Sopenharmony_ci goto failure; 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci if (request_irq(pdev->irq, irq_handler, IRQF_SHARED, "myrb", cb) < 0) { 344862306a36Sopenharmony_ci dev_err(&pdev->dev, 344962306a36Sopenharmony_ci "Unable to acquire IRQ Channel %d\n", pdev->irq); 345062306a36Sopenharmony_ci goto failure; 345162306a36Sopenharmony_ci } 345262306a36Sopenharmony_ci cb->irq = pdev->irq; 345362306a36Sopenharmony_ci return cb; 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_cifailure: 345662306a36Sopenharmony_ci dev_err(&pdev->dev, 345762306a36Sopenharmony_ci "Failed to initialize Controller\n"); 345862306a36Sopenharmony_ci myrb_cleanup(cb); 345962306a36Sopenharmony_ci return NULL; 346062306a36Sopenharmony_ci} 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_cistatic int myrb_probe(struct pci_dev *dev, const struct pci_device_id *entry) 346362306a36Sopenharmony_ci{ 346462306a36Sopenharmony_ci struct myrb_hba *cb; 346562306a36Sopenharmony_ci int ret; 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_ci cb = myrb_detect(dev, entry); 346862306a36Sopenharmony_ci if (!cb) 346962306a36Sopenharmony_ci return -ENODEV; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci ret = myrb_get_hba_config(cb); 347262306a36Sopenharmony_ci if (ret < 0) { 347362306a36Sopenharmony_ci myrb_cleanup(cb); 347462306a36Sopenharmony_ci return ret; 347562306a36Sopenharmony_ci } 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci if (!myrb_create_mempools(dev, cb)) { 347862306a36Sopenharmony_ci ret = -ENOMEM; 347962306a36Sopenharmony_ci goto failed; 348062306a36Sopenharmony_ci } 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci ret = scsi_add_host(cb->host, &dev->dev); 348362306a36Sopenharmony_ci if (ret) { 348462306a36Sopenharmony_ci dev_err(&dev->dev, "scsi_add_host failed with %d\n", ret); 348562306a36Sopenharmony_ci myrb_destroy_mempools(cb); 348662306a36Sopenharmony_ci goto failed; 348762306a36Sopenharmony_ci } 348862306a36Sopenharmony_ci scsi_scan_host(cb->host); 348962306a36Sopenharmony_ci return 0; 349062306a36Sopenharmony_cifailed: 349162306a36Sopenharmony_ci myrb_cleanup(cb); 349262306a36Sopenharmony_ci return ret; 349362306a36Sopenharmony_ci} 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci 349662306a36Sopenharmony_cistatic void myrb_remove(struct pci_dev *pdev) 349762306a36Sopenharmony_ci{ 349862306a36Sopenharmony_ci struct myrb_hba *cb = pci_get_drvdata(pdev); 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci shost_printk(KERN_NOTICE, cb->host, "Flushing Cache..."); 350162306a36Sopenharmony_ci myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0); 350262306a36Sopenharmony_ci myrb_cleanup(cb); 350362306a36Sopenharmony_ci myrb_destroy_mempools(cb); 350462306a36Sopenharmony_ci} 350562306a36Sopenharmony_ci 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_cistatic const struct pci_device_id myrb_id_table[] = { 350862306a36Sopenharmony_ci { 350962306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_DEC, 351062306a36Sopenharmony_ci PCI_DEVICE_ID_DEC_21285, 351162306a36Sopenharmony_ci PCI_VENDOR_ID_MYLEX, 351262306a36Sopenharmony_ci PCI_DEVICE_ID_MYLEX_DAC960_LA), 351362306a36Sopenharmony_ci .driver_data = (unsigned long) &DAC960_LA_privdata, 351462306a36Sopenharmony_ci }, 351562306a36Sopenharmony_ci { 351662306a36Sopenharmony_ci PCI_DEVICE_DATA(MYLEX, DAC960_PG, &DAC960_PG_privdata), 351762306a36Sopenharmony_ci }, 351862306a36Sopenharmony_ci { 351962306a36Sopenharmony_ci PCI_DEVICE_DATA(MYLEX, DAC960_PD, &DAC960_PD_privdata), 352062306a36Sopenharmony_ci }, 352162306a36Sopenharmony_ci { 352262306a36Sopenharmony_ci PCI_DEVICE_DATA(MYLEX, DAC960_P, &DAC960_P_privdata), 352362306a36Sopenharmony_ci }, 352462306a36Sopenharmony_ci {0, }, 352562306a36Sopenharmony_ci}; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, myrb_id_table); 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_cistatic struct pci_driver myrb_pci_driver = { 353062306a36Sopenharmony_ci .name = "myrb", 353162306a36Sopenharmony_ci .id_table = myrb_id_table, 353262306a36Sopenharmony_ci .probe = myrb_probe, 353362306a36Sopenharmony_ci .remove = myrb_remove, 353462306a36Sopenharmony_ci}; 353562306a36Sopenharmony_ci 353662306a36Sopenharmony_cistatic int __init myrb_init_module(void) 353762306a36Sopenharmony_ci{ 353862306a36Sopenharmony_ci int ret; 353962306a36Sopenharmony_ci 354062306a36Sopenharmony_ci myrb_raid_template = raid_class_attach(&myrb_raid_functions); 354162306a36Sopenharmony_ci if (!myrb_raid_template) 354262306a36Sopenharmony_ci return -ENODEV; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci ret = pci_register_driver(&myrb_pci_driver); 354562306a36Sopenharmony_ci if (ret) 354662306a36Sopenharmony_ci raid_class_release(myrb_raid_template); 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci return ret; 354962306a36Sopenharmony_ci} 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_cistatic void __exit myrb_cleanup_module(void) 355262306a36Sopenharmony_ci{ 355362306a36Sopenharmony_ci pci_unregister_driver(&myrb_pci_driver); 355462306a36Sopenharmony_ci raid_class_release(myrb_raid_template); 355562306a36Sopenharmony_ci} 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_cimodule_init(myrb_init_module); 355862306a36Sopenharmony_cimodule_exit(myrb_cleanup_module); 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ciMODULE_DESCRIPTION("Mylex DAC960/AcceleRAID/eXtremeRAID driver (Block interface)"); 356162306a36Sopenharmony_ciMODULE_AUTHOR("Hannes Reinecke <hare@suse.com>"); 356262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3563