18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2017 Hannes Reinecke, SUSE Linux GmbH <hare@suse.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on the original DAC960 driver, 88c2ecf20Sopenharmony_ci * Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com> 98c2ecf20Sopenharmony_ci * Portions Copyright 2002 by Mylex (An IBM Business Unit) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/raid_class.h> 198c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 208c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 218c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 228c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 238c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 248c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h> 258c2ecf20Sopenharmony_ci#include "myrb.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic struct raid_template *myrb_raid_template; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void myrb_monitor(struct work_struct *work); 308c2ecf20Sopenharmony_cistatic inline void myrb_translate_devstate(void *DeviceState); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic inline int myrb_logical_channel(struct Scsi_Host *shost) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci return shost->max_channel - 1; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct myrb_devstate_name_entry { 388c2ecf20Sopenharmony_ci enum myrb_devstate state; 398c2ecf20Sopenharmony_ci const char *name; 408c2ecf20Sopenharmony_ci} myrb_devstate_name_list[] = { 418c2ecf20Sopenharmony_ci { MYRB_DEVICE_DEAD, "Dead" }, 428c2ecf20Sopenharmony_ci { MYRB_DEVICE_WO, "WriteOnly" }, 438c2ecf20Sopenharmony_ci { MYRB_DEVICE_ONLINE, "Online" }, 448c2ecf20Sopenharmony_ci { MYRB_DEVICE_CRITICAL, "Critical" }, 458c2ecf20Sopenharmony_ci { MYRB_DEVICE_STANDBY, "Standby" }, 468c2ecf20Sopenharmony_ci { MYRB_DEVICE_OFFLINE, "Offline" }, 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic const char *myrb_devstate_name(enum myrb_devstate state) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct myrb_devstate_name_entry *entry = myrb_devstate_name_list; 528c2ecf20Sopenharmony_ci int i; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(myrb_devstate_name_list); i++) { 558c2ecf20Sopenharmony_ci if (entry[i].state == state) 568c2ecf20Sopenharmony_ci return entry[i].name; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci return "Unknown"; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic struct myrb_raidlevel_name_entry { 628c2ecf20Sopenharmony_ci enum myrb_raidlevel level; 638c2ecf20Sopenharmony_ci const char *name; 648c2ecf20Sopenharmony_ci} myrb_raidlevel_name_list[] = { 658c2ecf20Sopenharmony_ci { MYRB_RAID_LEVEL0, "RAID0" }, 668c2ecf20Sopenharmony_ci { MYRB_RAID_LEVEL1, "RAID1" }, 678c2ecf20Sopenharmony_ci { MYRB_RAID_LEVEL3, "RAID3" }, 688c2ecf20Sopenharmony_ci { MYRB_RAID_LEVEL5, "RAID5" }, 698c2ecf20Sopenharmony_ci { MYRB_RAID_LEVEL6, "RAID6" }, 708c2ecf20Sopenharmony_ci { MYRB_RAID_JBOD, "JBOD" }, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const char *myrb_raidlevel_name(enum myrb_raidlevel level) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct myrb_raidlevel_name_entry *entry = myrb_raidlevel_name_list; 768c2ecf20Sopenharmony_ci int i; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(myrb_raidlevel_name_list); i++) { 798c2ecf20Sopenharmony_ci if (entry[i].level == level) 808c2ecf20Sopenharmony_ci return entry[i].name; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci return NULL; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/** 868c2ecf20Sopenharmony_ci * myrb_create_mempools - allocates auxiliary data structures 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Return: true on success, false otherwise. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_cistatic bool myrb_create_mempools(struct pci_dev *pdev, struct myrb_hba *cb) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci size_t elem_size, elem_align; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci elem_align = sizeof(struct myrb_sge); 958c2ecf20Sopenharmony_ci elem_size = cb->host->sg_tablesize * elem_align; 968c2ecf20Sopenharmony_ci cb->sg_pool = dma_pool_create("myrb_sg", &pdev->dev, 978c2ecf20Sopenharmony_ci elem_size, elem_align, 0); 988c2ecf20Sopenharmony_ci if (cb->sg_pool == NULL) { 998c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 1008c2ecf20Sopenharmony_ci "Failed to allocate SG pool\n"); 1018c2ecf20Sopenharmony_ci return false; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci cb->dcdb_pool = dma_pool_create("myrb_dcdb", &pdev->dev, 1058c2ecf20Sopenharmony_ci sizeof(struct myrb_dcdb), 1068c2ecf20Sopenharmony_ci sizeof(unsigned int), 0); 1078c2ecf20Sopenharmony_ci if (!cb->dcdb_pool) { 1088c2ecf20Sopenharmony_ci dma_pool_destroy(cb->sg_pool); 1098c2ecf20Sopenharmony_ci cb->sg_pool = NULL; 1108c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 1118c2ecf20Sopenharmony_ci "Failed to allocate DCDB pool\n"); 1128c2ecf20Sopenharmony_ci return false; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci snprintf(cb->work_q_name, sizeof(cb->work_q_name), 1168c2ecf20Sopenharmony_ci "myrb_wq_%d", cb->host->host_no); 1178c2ecf20Sopenharmony_ci cb->work_q = create_singlethread_workqueue(cb->work_q_name); 1188c2ecf20Sopenharmony_ci if (!cb->work_q) { 1198c2ecf20Sopenharmony_ci dma_pool_destroy(cb->dcdb_pool); 1208c2ecf20Sopenharmony_ci cb->dcdb_pool = NULL; 1218c2ecf20Sopenharmony_ci dma_pool_destroy(cb->sg_pool); 1228c2ecf20Sopenharmony_ci cb->sg_pool = NULL; 1238c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 1248c2ecf20Sopenharmony_ci "Failed to create workqueue\n"); 1258c2ecf20Sopenharmony_ci return false; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Initialize the Monitoring Timer. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&cb->monitor_work, myrb_monitor); 1328c2ecf20Sopenharmony_ci queue_delayed_work(cb->work_q, &cb->monitor_work, 1); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return true; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/** 1388c2ecf20Sopenharmony_ci * myrb_destroy_mempools - tears down the memory pools for the controller 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistatic void myrb_destroy_mempools(struct myrb_hba *cb) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&cb->monitor_work); 1438c2ecf20Sopenharmony_ci destroy_workqueue(cb->work_q); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci dma_pool_destroy(cb->sg_pool); 1468c2ecf20Sopenharmony_ci dma_pool_destroy(cb->dcdb_pool); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/** 1508c2ecf20Sopenharmony_ci * myrb_reset_cmd - reset command block 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_cistatic inline void myrb_reset_cmd(struct myrb_cmdblk *cmd_blk) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci memset(mbox, 0, sizeof(union myrb_cmd_mbox)); 1578c2ecf20Sopenharmony_ci cmd_blk->status = 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/** 1618c2ecf20Sopenharmony_ci * myrb_qcmd - queues command block for execution 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic void myrb_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci void __iomem *base = cb->io_base; 1668c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 1678c2ecf20Sopenharmony_ci union myrb_cmd_mbox *next_mbox = cb->next_cmd_mbox; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci cb->write_cmd_mbox(next_mbox, mbox); 1708c2ecf20Sopenharmony_ci if (cb->prev_cmd_mbox1->words[0] == 0 || 1718c2ecf20Sopenharmony_ci cb->prev_cmd_mbox2->words[0] == 0) 1728c2ecf20Sopenharmony_ci cb->get_cmd_mbox(base); 1738c2ecf20Sopenharmony_ci cb->prev_cmd_mbox2 = cb->prev_cmd_mbox1; 1748c2ecf20Sopenharmony_ci cb->prev_cmd_mbox1 = next_mbox; 1758c2ecf20Sopenharmony_ci if (++next_mbox > cb->last_cmd_mbox) 1768c2ecf20Sopenharmony_ci next_mbox = cb->first_cmd_mbox; 1778c2ecf20Sopenharmony_ci cb->next_cmd_mbox = next_mbox; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/** 1818c2ecf20Sopenharmony_ci * myrb_exec_cmd - executes command block and waits for completion. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * Return: command status 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic unsigned short myrb_exec_cmd(struct myrb_hba *cb, 1868c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(cmpl); 1898c2ecf20Sopenharmony_ci unsigned long flags; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci cmd_blk->completion = &cmpl; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 1948c2ecf20Sopenharmony_ci cb->qcmd(cb, cmd_blk); 1958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci WARN_ON(in_interrupt()); 1988c2ecf20Sopenharmony_ci wait_for_completion(&cmpl); 1998c2ecf20Sopenharmony_ci return cmd_blk->status; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * myrb_exec_type3 - executes a type 3 command and waits for completion. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * Return: command status 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_cistatic unsigned short myrb_exec_type3(struct myrb_hba *cb, 2088c2ecf20Sopenharmony_ci enum myrb_cmd_opcode op, dma_addr_t addr) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; 2118c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 2128c2ecf20Sopenharmony_ci unsigned short status; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 2158c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 2168c2ecf20Sopenharmony_ci mbox->type3.id = MYRB_DCMD_TAG; 2178c2ecf20Sopenharmony_ci mbox->type3.opcode = op; 2188c2ecf20Sopenharmony_ci mbox->type3.addr = addr; 2198c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 2208c2ecf20Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 2218c2ecf20Sopenharmony_ci return status; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * myrb_exec_type3D - executes a type 3D command and waits for completion. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * Return: command status 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic unsigned short myrb_exec_type3D(struct myrb_hba *cb, 2308c2ecf20Sopenharmony_ci enum myrb_cmd_opcode op, struct scsi_device *sdev, 2318c2ecf20Sopenharmony_ci struct myrb_pdev_state *pdev_info) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; 2348c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 2358c2ecf20Sopenharmony_ci unsigned short status; 2368c2ecf20Sopenharmony_ci dma_addr_t pdev_info_addr; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci pdev_info_addr = dma_map_single(&cb->pdev->dev, pdev_info, 2398c2ecf20Sopenharmony_ci sizeof(struct myrb_pdev_state), 2408c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2418c2ecf20Sopenharmony_ci if (dma_mapping_error(&cb->pdev->dev, pdev_info_addr)) 2428c2ecf20Sopenharmony_ci return MYRB_STATUS_SUBSYS_FAILED; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 2458c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 2468c2ecf20Sopenharmony_ci mbox->type3D.id = MYRB_DCMD_TAG; 2478c2ecf20Sopenharmony_ci mbox->type3D.opcode = op; 2488c2ecf20Sopenharmony_ci mbox->type3D.channel = sdev->channel; 2498c2ecf20Sopenharmony_ci mbox->type3D.target = sdev->id; 2508c2ecf20Sopenharmony_ci mbox->type3D.addr = pdev_info_addr; 2518c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 2528c2ecf20Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 2538c2ecf20Sopenharmony_ci dma_unmap_single(&cb->pdev->dev, pdev_info_addr, 2548c2ecf20Sopenharmony_ci sizeof(struct myrb_pdev_state), DMA_FROM_DEVICE); 2558c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS && 2568c2ecf20Sopenharmony_ci mbox->type3D.opcode == MYRB_CMD_GET_DEVICE_STATE_OLD) 2578c2ecf20Sopenharmony_ci myrb_translate_devstate(pdev_info); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return status; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic char *myrb_event_msg[] = { 2638c2ecf20Sopenharmony_ci "killed because write recovery failed", 2648c2ecf20Sopenharmony_ci "killed because of SCSI bus reset failure", 2658c2ecf20Sopenharmony_ci "killed because of double check condition", 2668c2ecf20Sopenharmony_ci "killed because it was removed", 2678c2ecf20Sopenharmony_ci "killed because of gross error on SCSI chip", 2688c2ecf20Sopenharmony_ci "killed because of bad tag returned from drive", 2698c2ecf20Sopenharmony_ci "killed because of timeout on SCSI command", 2708c2ecf20Sopenharmony_ci "killed because of reset SCSI command issued from system", 2718c2ecf20Sopenharmony_ci "killed because busy or parity error count exceeded limit", 2728c2ecf20Sopenharmony_ci "killed because of 'kill drive' command from system", 2738c2ecf20Sopenharmony_ci "killed because of selection timeout", 2748c2ecf20Sopenharmony_ci "killed due to SCSI phase sequence error", 2758c2ecf20Sopenharmony_ci "killed due to unknown status", 2768c2ecf20Sopenharmony_ci}; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/** 2798c2ecf20Sopenharmony_ci * myrb_get_event - get event log from HBA 2808c2ecf20Sopenharmony_ci * @cb: pointer to the hba structure 2818c2ecf20Sopenharmony_ci * @event: number of the event 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * Execute a type 3E command and logs the event message 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_cistatic void myrb_get_event(struct myrb_hba *cb, unsigned int event) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 2888c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 2898c2ecf20Sopenharmony_ci struct myrb_log_entry *ev_buf; 2908c2ecf20Sopenharmony_ci dma_addr_t ev_addr; 2918c2ecf20Sopenharmony_ci unsigned short status; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ev_buf = dma_alloc_coherent(&cb->pdev->dev, 2948c2ecf20Sopenharmony_ci sizeof(struct myrb_log_entry), 2958c2ecf20Sopenharmony_ci &ev_addr, GFP_KERNEL); 2968c2ecf20Sopenharmony_ci if (!ev_buf) 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 3008c2ecf20Sopenharmony_ci mbox->type3E.id = MYRB_MCMD_TAG; 3018c2ecf20Sopenharmony_ci mbox->type3E.opcode = MYRB_CMD_EVENT_LOG_OPERATION; 3028c2ecf20Sopenharmony_ci mbox->type3E.optype = DAC960_V1_GetEventLogEntry; 3038c2ecf20Sopenharmony_ci mbox->type3E.opqual = 1; 3048c2ecf20Sopenharmony_ci mbox->type3E.ev_seq = event; 3058c2ecf20Sopenharmony_ci mbox->type3E.addr = ev_addr; 3068c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 3078c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) 3088c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 3098c2ecf20Sopenharmony_ci "Failed to get event log %d, status %04x\n", 3108c2ecf20Sopenharmony_ci event, status); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci else if (ev_buf->seq_num == event) { 3138c2ecf20Sopenharmony_ci struct scsi_sense_hdr sshdr; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci memset(&sshdr, 0, sizeof(sshdr)); 3168c2ecf20Sopenharmony_ci scsi_normalize_sense(ev_buf->sense, 32, &sshdr); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (sshdr.sense_key == VENDOR_SPECIFIC && 3198c2ecf20Sopenharmony_ci sshdr.asc == 0x80 && 3208c2ecf20Sopenharmony_ci sshdr.ascq < ARRAY_SIZE(myrb_event_msg)) 3218c2ecf20Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 3228c2ecf20Sopenharmony_ci "Physical drive %d:%d: %s\n", 3238c2ecf20Sopenharmony_ci ev_buf->channel, ev_buf->target, 3248c2ecf20Sopenharmony_ci myrb_event_msg[sshdr.ascq]); 3258c2ecf20Sopenharmony_ci else 3268c2ecf20Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 3278c2ecf20Sopenharmony_ci "Physical drive %d:%d: Sense: %X/%02X/%02X\n", 3288c2ecf20Sopenharmony_ci ev_buf->channel, ev_buf->target, 3298c2ecf20Sopenharmony_ci sshdr.sense_key, sshdr.asc, sshdr.ascq); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_log_entry), 3338c2ecf20Sopenharmony_ci ev_buf, ev_addr); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/** 3378c2ecf20Sopenharmony_ci * myrb_get_errtable - retrieves the error table from the controller 3388c2ecf20Sopenharmony_ci * 3398c2ecf20Sopenharmony_ci * Executes a type 3 command and logs the error table from the controller. 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_cistatic void myrb_get_errtable(struct myrb_hba *cb) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 3448c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 3458c2ecf20Sopenharmony_ci unsigned short status; 3468c2ecf20Sopenharmony_ci struct myrb_error_entry old_table[MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS]; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci memcpy(&old_table, cb->err_table, sizeof(old_table)); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 3518c2ecf20Sopenharmony_ci mbox->type3.id = MYRB_MCMD_TAG; 3528c2ecf20Sopenharmony_ci mbox->type3.opcode = MYRB_CMD_GET_ERROR_TABLE; 3538c2ecf20Sopenharmony_ci mbox->type3.addr = cb->err_table_addr; 3548c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 3558c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 3568c2ecf20Sopenharmony_ci struct myrb_error_entry *table = cb->err_table; 3578c2ecf20Sopenharmony_ci struct myrb_error_entry *new, *old; 3588c2ecf20Sopenharmony_ci size_t err_table_offset; 3598c2ecf20Sopenharmony_ci struct scsi_device *sdev; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci shost_for_each_device(sdev, cb->host) { 3628c2ecf20Sopenharmony_ci if (sdev->channel >= myrb_logical_channel(cb->host)) 3638c2ecf20Sopenharmony_ci continue; 3648c2ecf20Sopenharmony_ci err_table_offset = sdev->channel * MYRB_MAX_TARGETS 3658c2ecf20Sopenharmony_ci + sdev->id; 3668c2ecf20Sopenharmony_ci new = table + err_table_offset; 3678c2ecf20Sopenharmony_ci old = &old_table[err_table_offset]; 3688c2ecf20Sopenharmony_ci if (new->parity_err == old->parity_err && 3698c2ecf20Sopenharmony_ci new->soft_err == old->soft_err && 3708c2ecf20Sopenharmony_ci new->hard_err == old->hard_err && 3718c2ecf20Sopenharmony_ci new->misc_err == old->misc_err) 3728c2ecf20Sopenharmony_ci continue; 3738c2ecf20Sopenharmony_ci sdev_printk(KERN_CRIT, sdev, 3748c2ecf20Sopenharmony_ci "Errors: Parity = %d, Soft = %d, Hard = %d, Misc = %d\n", 3758c2ecf20Sopenharmony_ci new->parity_err, new->soft_err, 3768c2ecf20Sopenharmony_ci new->hard_err, new->misc_err); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/** 3828c2ecf20Sopenharmony_ci * myrb_get_ldev_info - retrieves the logical device table from the controller 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * Executes a type 3 command and updates the logical device table. 3858c2ecf20Sopenharmony_ci * 3868c2ecf20Sopenharmony_ci * Return: command status 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_cistatic unsigned short myrb_get_ldev_info(struct myrb_hba *cb) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci unsigned short status; 3918c2ecf20Sopenharmony_ci int ldev_num, ldev_cnt = cb->enquiry->ldev_count; 3928c2ecf20Sopenharmony_ci struct Scsi_Host *shost = cb->host; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_GET_LDEV_INFO, 3958c2ecf20Sopenharmony_ci cb->ldev_info_addr); 3968c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) 3978c2ecf20Sopenharmony_ci return status; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci for (ldev_num = 0; ldev_num < ldev_cnt; ldev_num++) { 4008c2ecf20Sopenharmony_ci struct myrb_ldev_info *old = NULL; 4018c2ecf20Sopenharmony_ci struct myrb_ldev_info *new = cb->ldev_info_buf + ldev_num; 4028c2ecf20Sopenharmony_ci struct scsi_device *sdev; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci sdev = scsi_device_lookup(shost, myrb_logical_channel(shost), 4058c2ecf20Sopenharmony_ci ldev_num, 0); 4068c2ecf20Sopenharmony_ci if (!sdev) { 4078c2ecf20Sopenharmony_ci if (new->state == MYRB_DEVICE_OFFLINE) 4088c2ecf20Sopenharmony_ci continue; 4098c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, shost, 4108c2ecf20Sopenharmony_ci "Adding Logical Drive %d in state %s\n", 4118c2ecf20Sopenharmony_ci ldev_num, myrb_devstate_name(new->state)); 4128c2ecf20Sopenharmony_ci scsi_add_device(shost, myrb_logical_channel(shost), 4138c2ecf20Sopenharmony_ci ldev_num, 0); 4148c2ecf20Sopenharmony_ci continue; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci old = sdev->hostdata; 4178c2ecf20Sopenharmony_ci if (new->state != old->state) 4188c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, shost, 4198c2ecf20Sopenharmony_ci "Logical Drive %d is now %s\n", 4208c2ecf20Sopenharmony_ci ldev_num, myrb_devstate_name(new->state)); 4218c2ecf20Sopenharmony_ci if (new->wb_enabled != old->wb_enabled) 4228c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 4238c2ecf20Sopenharmony_ci "Logical Drive is now WRITE %s\n", 4248c2ecf20Sopenharmony_ci (new->wb_enabled ? "BACK" : "THRU")); 4258c2ecf20Sopenharmony_ci memcpy(old, new, sizeof(*new)); 4268c2ecf20Sopenharmony_ci scsi_device_put(sdev); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci return status; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/** 4328c2ecf20Sopenharmony_ci * myrb_get_rbld_progress - get rebuild progress information 4338c2ecf20Sopenharmony_ci * 4348c2ecf20Sopenharmony_ci * Executes a type 3 command and returns the rebuild progress 4358c2ecf20Sopenharmony_ci * information. 4368c2ecf20Sopenharmony_ci * 4378c2ecf20Sopenharmony_ci * Return: command status 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_cistatic unsigned short myrb_get_rbld_progress(struct myrb_hba *cb, 4408c2ecf20Sopenharmony_ci struct myrb_rbld_progress *rbld) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 4438c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 4448c2ecf20Sopenharmony_ci struct myrb_rbld_progress *rbld_buf; 4458c2ecf20Sopenharmony_ci dma_addr_t rbld_addr; 4468c2ecf20Sopenharmony_ci unsigned short status; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci rbld_buf = dma_alloc_coherent(&cb->pdev->dev, 4498c2ecf20Sopenharmony_ci sizeof(struct myrb_rbld_progress), 4508c2ecf20Sopenharmony_ci &rbld_addr, GFP_KERNEL); 4518c2ecf20Sopenharmony_ci if (!rbld_buf) 4528c2ecf20Sopenharmony_ci return MYRB_STATUS_RBLD_NOT_CHECKED; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 4558c2ecf20Sopenharmony_ci mbox->type3.id = MYRB_MCMD_TAG; 4568c2ecf20Sopenharmony_ci mbox->type3.opcode = MYRB_CMD_GET_REBUILD_PROGRESS; 4578c2ecf20Sopenharmony_ci mbox->type3.addr = rbld_addr; 4588c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 4598c2ecf20Sopenharmony_ci if (rbld) 4608c2ecf20Sopenharmony_ci memcpy(rbld, rbld_buf, sizeof(struct myrb_rbld_progress)); 4618c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_rbld_progress), 4628c2ecf20Sopenharmony_ci rbld_buf, rbld_addr); 4638c2ecf20Sopenharmony_ci return status; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/** 4678c2ecf20Sopenharmony_ci * myrb_update_rbld_progress - updates the rebuild status 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * Updates the rebuild status for the attached logical devices. 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_cistatic void myrb_update_rbld_progress(struct myrb_hba *cb) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct myrb_rbld_progress rbld_buf; 4758c2ecf20Sopenharmony_ci unsigned short status; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci status = myrb_get_rbld_progress(cb, &rbld_buf); 4788c2ecf20Sopenharmony_ci if (status == MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS && 4798c2ecf20Sopenharmony_ci cb->last_rbld_status == MYRB_STATUS_SUCCESS) 4808c2ecf20Sopenharmony_ci status = MYRB_STATUS_RBLD_SUCCESS; 4818c2ecf20Sopenharmony_ci if (status != MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS) { 4828c2ecf20Sopenharmony_ci unsigned int blocks_done = 4838c2ecf20Sopenharmony_ci rbld_buf.ldev_size - rbld_buf.blocks_left; 4848c2ecf20Sopenharmony_ci struct scsi_device *sdev; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci sdev = scsi_device_lookup(cb->host, 4878c2ecf20Sopenharmony_ci myrb_logical_channel(cb->host), 4888c2ecf20Sopenharmony_ci rbld_buf.ldev_num, 0); 4898c2ecf20Sopenharmony_ci if (!sdev) 4908c2ecf20Sopenharmony_ci return; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci switch (status) { 4938c2ecf20Sopenharmony_ci case MYRB_STATUS_SUCCESS: 4948c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 4958c2ecf20Sopenharmony_ci "Rebuild in Progress, %d%% completed\n", 4968c2ecf20Sopenharmony_ci (100 * (blocks_done >> 7)) 4978c2ecf20Sopenharmony_ci / (rbld_buf.ldev_size >> 7)); 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_FAILED_LDEV_FAILURE: 5008c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 5018c2ecf20Sopenharmony_ci "Rebuild Failed due to Logical Drive Failure\n"); 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_FAILED_BADBLOCKS: 5048c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 5058c2ecf20Sopenharmony_ci "Rebuild Failed due to Bad Blocks on Other Drives\n"); 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_FAILED_NEW_DRIVE_FAILED: 5088c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 5098c2ecf20Sopenharmony_ci "Rebuild Failed due to Failure of Drive Being Rebuilt\n"); 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_SUCCESS: 5128c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 5138c2ecf20Sopenharmony_ci "Rebuild Completed Successfully\n"); 5148c2ecf20Sopenharmony_ci break; 5158c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_SUCCESS_TERMINATED: 5168c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 5178c2ecf20Sopenharmony_ci "Rebuild Successfully Terminated\n"); 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci default: 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci scsi_device_put(sdev); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci cb->last_rbld_status = status; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/** 5288c2ecf20Sopenharmony_ci * myrb_get_cc_progress - retrieve the rebuild status 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * Execute a type 3 Command and fetch the rebuild / consistency check 5318c2ecf20Sopenharmony_ci * status. 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_cistatic void myrb_get_cc_progress(struct myrb_hba *cb) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 5368c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 5378c2ecf20Sopenharmony_ci struct myrb_rbld_progress *rbld_buf; 5388c2ecf20Sopenharmony_ci dma_addr_t rbld_addr; 5398c2ecf20Sopenharmony_ci unsigned short status; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci rbld_buf = dma_alloc_coherent(&cb->pdev->dev, 5428c2ecf20Sopenharmony_ci sizeof(struct myrb_rbld_progress), 5438c2ecf20Sopenharmony_ci &rbld_addr, GFP_KERNEL); 5448c2ecf20Sopenharmony_ci if (!rbld_buf) { 5458c2ecf20Sopenharmony_ci cb->need_cc_status = true; 5468c2ecf20Sopenharmony_ci return; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 5498c2ecf20Sopenharmony_ci mbox->type3.id = MYRB_MCMD_TAG; 5508c2ecf20Sopenharmony_ci mbox->type3.opcode = MYRB_CMD_REBUILD_STAT; 5518c2ecf20Sopenharmony_ci mbox->type3.addr = rbld_addr; 5528c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 5538c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 5548c2ecf20Sopenharmony_ci unsigned int ldev_num = rbld_buf->ldev_num; 5558c2ecf20Sopenharmony_ci unsigned int ldev_size = rbld_buf->ldev_size; 5568c2ecf20Sopenharmony_ci unsigned int blocks_done = 5578c2ecf20Sopenharmony_ci ldev_size - rbld_buf->blocks_left; 5588c2ecf20Sopenharmony_ci struct scsi_device *sdev; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci sdev = scsi_device_lookup(cb->host, 5618c2ecf20Sopenharmony_ci myrb_logical_channel(cb->host), 5628c2ecf20Sopenharmony_ci ldev_num, 0); 5638c2ecf20Sopenharmony_ci if (sdev) { 5648c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 5658c2ecf20Sopenharmony_ci "Consistency Check in Progress: %d%% completed\n", 5668c2ecf20Sopenharmony_ci (100 * (blocks_done >> 7)) 5678c2ecf20Sopenharmony_ci / (ldev_size >> 7)); 5688c2ecf20Sopenharmony_ci scsi_device_put(sdev); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_rbld_progress), 5728c2ecf20Sopenharmony_ci rbld_buf, rbld_addr); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci/** 5768c2ecf20Sopenharmony_ci * myrb_bgi_control - updates background initialisation status 5778c2ecf20Sopenharmony_ci * 5788c2ecf20Sopenharmony_ci * Executes a type 3B command and updates the background initialisation status 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_cistatic void myrb_bgi_control(struct myrb_hba *cb) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->mcmd_blk; 5838c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 5848c2ecf20Sopenharmony_ci struct myrb_bgi_status *bgi, *last_bgi; 5858c2ecf20Sopenharmony_ci dma_addr_t bgi_addr; 5868c2ecf20Sopenharmony_ci struct scsi_device *sdev = NULL; 5878c2ecf20Sopenharmony_ci unsigned short status; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci bgi = dma_alloc_coherent(&cb->pdev->dev, sizeof(struct myrb_bgi_status), 5908c2ecf20Sopenharmony_ci &bgi_addr, GFP_KERNEL); 5918c2ecf20Sopenharmony_ci if (!bgi) { 5928c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 5938c2ecf20Sopenharmony_ci "Failed to allocate bgi memory\n"); 5948c2ecf20Sopenharmony_ci return; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 5978c2ecf20Sopenharmony_ci mbox->type3B.id = MYRB_DCMD_TAG; 5988c2ecf20Sopenharmony_ci mbox->type3B.opcode = MYRB_CMD_BGI_CONTROL; 5998c2ecf20Sopenharmony_ci mbox->type3B.optype = 0x20; 6008c2ecf20Sopenharmony_ci mbox->type3B.addr = bgi_addr; 6018c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 6028c2ecf20Sopenharmony_ci last_bgi = &cb->bgi_status; 6038c2ecf20Sopenharmony_ci sdev = scsi_device_lookup(cb->host, 6048c2ecf20Sopenharmony_ci myrb_logical_channel(cb->host), 6058c2ecf20Sopenharmony_ci bgi->ldev_num, 0); 6068c2ecf20Sopenharmony_ci switch (status) { 6078c2ecf20Sopenharmony_ci case MYRB_STATUS_SUCCESS: 6088c2ecf20Sopenharmony_ci switch (bgi->status) { 6098c2ecf20Sopenharmony_ci case MYRB_BGI_INVALID: 6108c2ecf20Sopenharmony_ci break; 6118c2ecf20Sopenharmony_ci case MYRB_BGI_STARTED: 6128c2ecf20Sopenharmony_ci if (!sdev) 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 6158c2ecf20Sopenharmony_ci "Background Initialization Started\n"); 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case MYRB_BGI_INPROGRESS: 6188c2ecf20Sopenharmony_ci if (!sdev) 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci if (bgi->blocks_done == last_bgi->blocks_done && 6218c2ecf20Sopenharmony_ci bgi->ldev_num == last_bgi->ldev_num) 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 6248c2ecf20Sopenharmony_ci "Background Initialization in Progress: %d%% completed\n", 6258c2ecf20Sopenharmony_ci (100 * (bgi->blocks_done >> 7)) 6268c2ecf20Sopenharmony_ci / (bgi->ldev_size >> 7)); 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci case MYRB_BGI_SUSPENDED: 6298c2ecf20Sopenharmony_ci if (!sdev) 6308c2ecf20Sopenharmony_ci break; 6318c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 6328c2ecf20Sopenharmony_ci "Background Initialization Suspended\n"); 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci case MYRB_BGI_CANCELLED: 6358c2ecf20Sopenharmony_ci if (!sdev) 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 6388c2ecf20Sopenharmony_ci "Background Initialization Cancelled\n"); 6398c2ecf20Sopenharmony_ci break; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci memcpy(&cb->bgi_status, bgi, sizeof(struct myrb_bgi_status)); 6428c2ecf20Sopenharmony_ci break; 6438c2ecf20Sopenharmony_ci case MYRB_STATUS_BGI_SUCCESS: 6448c2ecf20Sopenharmony_ci if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS) 6458c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 6468c2ecf20Sopenharmony_ci "Background Initialization Completed Successfully\n"); 6478c2ecf20Sopenharmony_ci cb->bgi_status.status = MYRB_BGI_INVALID; 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci case MYRB_STATUS_BGI_ABORTED: 6508c2ecf20Sopenharmony_ci if (sdev && cb->bgi_status.status == MYRB_BGI_INPROGRESS) 6518c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 6528c2ecf20Sopenharmony_ci "Background Initialization Aborted\n"); 6538c2ecf20Sopenharmony_ci fallthrough; 6548c2ecf20Sopenharmony_ci case MYRB_STATUS_NO_BGI_INPROGRESS: 6558c2ecf20Sopenharmony_ci cb->bgi_status.status = MYRB_BGI_INVALID; 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci if (sdev) 6598c2ecf20Sopenharmony_ci scsi_device_put(sdev); 6608c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_bgi_status), 6618c2ecf20Sopenharmony_ci bgi, bgi_addr); 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/** 6658c2ecf20Sopenharmony_ci * myrb_hba_enquiry - updates the controller status 6668c2ecf20Sopenharmony_ci * 6678c2ecf20Sopenharmony_ci * Executes a DAC_V1_Enquiry command and updates the controller status. 6688c2ecf20Sopenharmony_ci * 6698c2ecf20Sopenharmony_ci * Return: command status 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_cistatic unsigned short myrb_hba_enquiry(struct myrb_hba *cb) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct myrb_enquiry old, *new; 6748c2ecf20Sopenharmony_ci unsigned short status; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci memcpy(&old, cb->enquiry, sizeof(struct myrb_enquiry)); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY, cb->enquiry_addr); 6798c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) 6808c2ecf20Sopenharmony_ci return status; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci new = cb->enquiry; 6838c2ecf20Sopenharmony_ci if (new->ldev_count > old.ldev_count) { 6848c2ecf20Sopenharmony_ci int ldev_num = old.ldev_count - 1; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci while (++ldev_num < new->ldev_count) 6878c2ecf20Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 6888c2ecf20Sopenharmony_ci "Logical Drive %d Now Exists\n", 6898c2ecf20Sopenharmony_ci ldev_num); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci if (new->ldev_count < old.ldev_count) { 6928c2ecf20Sopenharmony_ci int ldev_num = new->ldev_count - 1; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci while (++ldev_num < old.ldev_count) 6958c2ecf20Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 6968c2ecf20Sopenharmony_ci "Logical Drive %d No Longer Exists\n", 6978c2ecf20Sopenharmony_ci ldev_num); 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci if (new->status.deferred != old.status.deferred) 7008c2ecf20Sopenharmony_ci shost_printk(KERN_CRIT, cb->host, 7018c2ecf20Sopenharmony_ci "Deferred Write Error Flag is now %s\n", 7028c2ecf20Sopenharmony_ci (new->status.deferred ? "TRUE" : "FALSE")); 7038c2ecf20Sopenharmony_ci if (new->ev_seq != old.ev_seq) { 7048c2ecf20Sopenharmony_ci cb->new_ev_seq = new->ev_seq; 7058c2ecf20Sopenharmony_ci cb->need_err_info = true; 7068c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 7078c2ecf20Sopenharmony_ci "Event log %d/%d (%d/%d) available\n", 7088c2ecf20Sopenharmony_ci cb->old_ev_seq, cb->new_ev_seq, 7098c2ecf20Sopenharmony_ci old.ev_seq, new->ev_seq); 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci if ((new->ldev_critical > 0 && 7128c2ecf20Sopenharmony_ci new->ldev_critical != old.ldev_critical) || 7138c2ecf20Sopenharmony_ci (new->ldev_offline > 0 && 7148c2ecf20Sopenharmony_ci new->ldev_offline != old.ldev_offline) || 7158c2ecf20Sopenharmony_ci (new->ldev_count != old.ldev_count)) { 7168c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 7178c2ecf20Sopenharmony_ci "Logical drive count changed (%d/%d/%d)\n", 7188c2ecf20Sopenharmony_ci new->ldev_critical, 7198c2ecf20Sopenharmony_ci new->ldev_offline, 7208c2ecf20Sopenharmony_ci new->ldev_count); 7218c2ecf20Sopenharmony_ci cb->need_ldev_info = true; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci if (new->pdev_dead > 0 || 7248c2ecf20Sopenharmony_ci new->pdev_dead != old.pdev_dead || 7258c2ecf20Sopenharmony_ci time_after_eq(jiffies, cb->secondary_monitor_time 7268c2ecf20Sopenharmony_ci + MYRB_SECONDARY_MONITOR_INTERVAL)) { 7278c2ecf20Sopenharmony_ci cb->need_bgi_status = cb->bgi_status_supported; 7288c2ecf20Sopenharmony_ci cb->secondary_monitor_time = jiffies; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci if (new->rbld == MYRB_STDBY_RBLD_IN_PROGRESS || 7318c2ecf20Sopenharmony_ci new->rbld == MYRB_BG_RBLD_IN_PROGRESS || 7328c2ecf20Sopenharmony_ci old.rbld == MYRB_STDBY_RBLD_IN_PROGRESS || 7338c2ecf20Sopenharmony_ci old.rbld == MYRB_BG_RBLD_IN_PROGRESS) { 7348c2ecf20Sopenharmony_ci cb->need_rbld = true; 7358c2ecf20Sopenharmony_ci cb->rbld_first = (new->ldev_critical < old.ldev_critical); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci if (old.rbld == MYRB_BG_CHECK_IN_PROGRESS) 7388c2ecf20Sopenharmony_ci switch (new->rbld) { 7398c2ecf20Sopenharmony_ci case MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS: 7408c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 7418c2ecf20Sopenharmony_ci "Consistency Check Completed Successfully\n"); 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci case MYRB_STDBY_RBLD_IN_PROGRESS: 7448c2ecf20Sopenharmony_ci case MYRB_BG_RBLD_IN_PROGRESS: 7458c2ecf20Sopenharmony_ci break; 7468c2ecf20Sopenharmony_ci case MYRB_BG_CHECK_IN_PROGRESS: 7478c2ecf20Sopenharmony_ci cb->need_cc_status = true; 7488c2ecf20Sopenharmony_ci break; 7498c2ecf20Sopenharmony_ci case MYRB_STDBY_RBLD_COMPLETED_WITH_ERROR: 7508c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 7518c2ecf20Sopenharmony_ci "Consistency Check Completed with Error\n"); 7528c2ecf20Sopenharmony_ci break; 7538c2ecf20Sopenharmony_ci case MYRB_BG_RBLD_OR_CHECK_FAILED_DRIVE_FAILED: 7548c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 7558c2ecf20Sopenharmony_ci "Consistency Check Failed - Physical Device Failed\n"); 7568c2ecf20Sopenharmony_ci break; 7578c2ecf20Sopenharmony_ci case MYRB_BG_RBLD_OR_CHECK_FAILED_LDEV_FAILED: 7588c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 7598c2ecf20Sopenharmony_ci "Consistency Check Failed - Logical Drive Failed\n"); 7608c2ecf20Sopenharmony_ci break; 7618c2ecf20Sopenharmony_ci case MYRB_BG_RBLD_OR_CHECK_FAILED_OTHER: 7628c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 7638c2ecf20Sopenharmony_ci "Consistency Check Failed - Other Causes\n"); 7648c2ecf20Sopenharmony_ci break; 7658c2ecf20Sopenharmony_ci case MYRB_BG_RBLD_OR_CHECK_SUCCESS_TERMINATED: 7668c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 7678c2ecf20Sopenharmony_ci "Consistency Check Successfully Terminated\n"); 7688c2ecf20Sopenharmony_ci break; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci else if (new->rbld == MYRB_BG_CHECK_IN_PROGRESS) 7718c2ecf20Sopenharmony_ci cb->need_cc_status = true; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci return MYRB_STATUS_SUCCESS; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci/** 7778c2ecf20Sopenharmony_ci * myrb_set_pdev_state - sets the device state for a physical device 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * Return: command status 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistatic unsigned short myrb_set_pdev_state(struct myrb_hba *cb, 7828c2ecf20Sopenharmony_ci struct scsi_device *sdev, enum myrb_devstate state) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = &cb->dcmd_blk; 7858c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 7868c2ecf20Sopenharmony_ci unsigned short status; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 7898c2ecf20Sopenharmony_ci mbox->type3D.opcode = MYRB_CMD_START_DEVICE; 7908c2ecf20Sopenharmony_ci mbox->type3D.id = MYRB_DCMD_TAG; 7918c2ecf20Sopenharmony_ci mbox->type3D.channel = sdev->channel; 7928c2ecf20Sopenharmony_ci mbox->type3D.target = sdev->id; 7938c2ecf20Sopenharmony_ci mbox->type3D.state = state & 0x1F; 7948c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 7958c2ecf20Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci return status; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/** 8018c2ecf20Sopenharmony_ci * myrb_enable_mmio - enables the Memory Mailbox Interface 8028c2ecf20Sopenharmony_ci * 8038c2ecf20Sopenharmony_ci * PD and P controller types have no memory mailbox, but still need the 8048c2ecf20Sopenharmony_ci * other dma mapped memory. 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * Return: true on success, false otherwise. 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_cistatic bool myrb_enable_mmio(struct myrb_hba *cb, mbox_mmio_init_t mmio_init_fn) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci void __iomem *base = cb->io_base; 8118c2ecf20Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 8128c2ecf20Sopenharmony_ci size_t err_table_size; 8138c2ecf20Sopenharmony_ci size_t ldev_info_size; 8148c2ecf20Sopenharmony_ci union myrb_cmd_mbox *cmd_mbox_mem; 8158c2ecf20Sopenharmony_ci struct myrb_stat_mbox *stat_mbox_mem; 8168c2ecf20Sopenharmony_ci union myrb_cmd_mbox mbox; 8178c2ecf20Sopenharmony_ci unsigned short status; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci memset(&mbox, 0, sizeof(union myrb_cmd_mbox)); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { 8228c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DMA mask out of range\n"); 8238c2ecf20Sopenharmony_ci return false; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci cb->enquiry = dma_alloc_coherent(&pdev->dev, 8278c2ecf20Sopenharmony_ci sizeof(struct myrb_enquiry), 8288c2ecf20Sopenharmony_ci &cb->enquiry_addr, GFP_KERNEL); 8298c2ecf20Sopenharmony_ci if (!cb->enquiry) 8308c2ecf20Sopenharmony_ci return false; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci err_table_size = sizeof(struct myrb_error_entry) * 8338c2ecf20Sopenharmony_ci MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS; 8348c2ecf20Sopenharmony_ci cb->err_table = dma_alloc_coherent(&pdev->dev, err_table_size, 8358c2ecf20Sopenharmony_ci &cb->err_table_addr, GFP_KERNEL); 8368c2ecf20Sopenharmony_ci if (!cb->err_table) 8378c2ecf20Sopenharmony_ci return false; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci ldev_info_size = sizeof(struct myrb_ldev_info) * MYRB_MAX_LDEVS; 8408c2ecf20Sopenharmony_ci cb->ldev_info_buf = dma_alloc_coherent(&pdev->dev, ldev_info_size, 8418c2ecf20Sopenharmony_ci &cb->ldev_info_addr, GFP_KERNEL); 8428c2ecf20Sopenharmony_ci if (!cb->ldev_info_buf) 8438c2ecf20Sopenharmony_ci return false; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* 8468c2ecf20Sopenharmony_ci * Skip mailbox initialisation for PD and P Controllers 8478c2ecf20Sopenharmony_ci */ 8488c2ecf20Sopenharmony_ci if (!mmio_init_fn) 8498c2ecf20Sopenharmony_ci return true; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* These are the base addresses for the command memory mailbox array */ 8528c2ecf20Sopenharmony_ci cb->cmd_mbox_size = MYRB_CMD_MBOX_COUNT * sizeof(union myrb_cmd_mbox); 8538c2ecf20Sopenharmony_ci cb->first_cmd_mbox = dma_alloc_coherent(&pdev->dev, 8548c2ecf20Sopenharmony_ci cb->cmd_mbox_size, 8558c2ecf20Sopenharmony_ci &cb->cmd_mbox_addr, 8568c2ecf20Sopenharmony_ci GFP_KERNEL); 8578c2ecf20Sopenharmony_ci if (!cb->first_cmd_mbox) 8588c2ecf20Sopenharmony_ci return false; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci cmd_mbox_mem = cb->first_cmd_mbox; 8618c2ecf20Sopenharmony_ci cmd_mbox_mem += MYRB_CMD_MBOX_COUNT - 1; 8628c2ecf20Sopenharmony_ci cb->last_cmd_mbox = cmd_mbox_mem; 8638c2ecf20Sopenharmony_ci cb->next_cmd_mbox = cb->first_cmd_mbox; 8648c2ecf20Sopenharmony_ci cb->prev_cmd_mbox1 = cb->last_cmd_mbox; 8658c2ecf20Sopenharmony_ci cb->prev_cmd_mbox2 = cb->last_cmd_mbox - 1; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* These are the base addresses for the status memory mailbox array */ 8688c2ecf20Sopenharmony_ci cb->stat_mbox_size = MYRB_STAT_MBOX_COUNT * 8698c2ecf20Sopenharmony_ci sizeof(struct myrb_stat_mbox); 8708c2ecf20Sopenharmony_ci cb->first_stat_mbox = dma_alloc_coherent(&pdev->dev, 8718c2ecf20Sopenharmony_ci cb->stat_mbox_size, 8728c2ecf20Sopenharmony_ci &cb->stat_mbox_addr, 8738c2ecf20Sopenharmony_ci GFP_KERNEL); 8748c2ecf20Sopenharmony_ci if (!cb->first_stat_mbox) 8758c2ecf20Sopenharmony_ci return false; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci stat_mbox_mem = cb->first_stat_mbox; 8788c2ecf20Sopenharmony_ci stat_mbox_mem += MYRB_STAT_MBOX_COUNT - 1; 8798c2ecf20Sopenharmony_ci cb->last_stat_mbox = stat_mbox_mem; 8808c2ecf20Sopenharmony_ci cb->next_stat_mbox = cb->first_stat_mbox; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* Enable the Memory Mailbox Interface. */ 8838c2ecf20Sopenharmony_ci cb->dual_mode_interface = true; 8848c2ecf20Sopenharmony_ci mbox.typeX.opcode = 0x2B; 8858c2ecf20Sopenharmony_ci mbox.typeX.id = 0; 8868c2ecf20Sopenharmony_ci mbox.typeX.opcode2 = 0x14; 8878c2ecf20Sopenharmony_ci mbox.typeX.cmd_mbox_addr = cb->cmd_mbox_addr; 8888c2ecf20Sopenharmony_ci mbox.typeX.stat_mbox_addr = cb->stat_mbox_addr; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci status = mmio_init_fn(pdev, base, &mbox); 8918c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 8928c2ecf20Sopenharmony_ci cb->dual_mode_interface = false; 8938c2ecf20Sopenharmony_ci mbox.typeX.opcode2 = 0x10; 8948c2ecf20Sopenharmony_ci status = mmio_init_fn(pdev, base, &mbox); 8958c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 8968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 8978c2ecf20Sopenharmony_ci "Failed to enable mailbox, statux %02X\n", 8988c2ecf20Sopenharmony_ci status); 8998c2ecf20Sopenharmony_ci return false; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci return true; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci/** 9068c2ecf20Sopenharmony_ci * myrb_get_hba_config - reads the configuration information 9078c2ecf20Sopenharmony_ci * 9088c2ecf20Sopenharmony_ci * Reads the configuration information from the controller and 9098c2ecf20Sopenharmony_ci * initializes the controller structure. 9108c2ecf20Sopenharmony_ci * 9118c2ecf20Sopenharmony_ci * Return: 0 on success, errno otherwise 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_cistatic int myrb_get_hba_config(struct myrb_hba *cb) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci struct myrb_enquiry2 *enquiry2; 9168c2ecf20Sopenharmony_ci dma_addr_t enquiry2_addr; 9178c2ecf20Sopenharmony_ci struct myrb_config2 *config2; 9188c2ecf20Sopenharmony_ci dma_addr_t config2_addr; 9198c2ecf20Sopenharmony_ci struct Scsi_Host *shost = cb->host; 9208c2ecf20Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 9218c2ecf20Sopenharmony_ci int pchan_max = 0, pchan_cur = 0; 9228c2ecf20Sopenharmony_ci unsigned short status; 9238c2ecf20Sopenharmony_ci int ret = -ENODEV, memsize = 0; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci enquiry2 = dma_alloc_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), 9268c2ecf20Sopenharmony_ci &enquiry2_addr, GFP_KERNEL); 9278c2ecf20Sopenharmony_ci if (!enquiry2) { 9288c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 9298c2ecf20Sopenharmony_ci "Failed to allocate V1 enquiry2 memory\n"); 9308c2ecf20Sopenharmony_ci return -ENOMEM; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci config2 = dma_alloc_coherent(&pdev->dev, sizeof(struct myrb_config2), 9338c2ecf20Sopenharmony_ci &config2_addr, GFP_KERNEL); 9348c2ecf20Sopenharmony_ci if (!config2) { 9358c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, cb->host, 9368c2ecf20Sopenharmony_ci "Failed to allocate V1 config2 memory\n"); 9378c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), 9388c2ecf20Sopenharmony_ci enquiry2, enquiry2_addr); 9398c2ecf20Sopenharmony_ci return -ENOMEM; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci mutex_lock(&cb->dma_mutex); 9428c2ecf20Sopenharmony_ci status = myrb_hba_enquiry(cb); 9438c2ecf20Sopenharmony_ci mutex_unlock(&cb->dma_mutex); 9448c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 9458c2ecf20Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 9468c2ecf20Sopenharmony_ci "Failed it issue V1 Enquiry\n"); 9478c2ecf20Sopenharmony_ci goto out_free; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_ENQUIRY2, enquiry2_addr); 9518c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 9528c2ecf20Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 9538c2ecf20Sopenharmony_ci "Failed to issue V1 Enquiry2\n"); 9548c2ecf20Sopenharmony_ci goto out_free; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_READ_CONFIG2, config2_addr); 9588c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 9598c2ecf20Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 9608c2ecf20Sopenharmony_ci "Failed to issue ReadConfig2\n"); 9618c2ecf20Sopenharmony_ci goto out_free; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci status = myrb_get_ldev_info(cb); 9658c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 9668c2ecf20Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 9678c2ecf20Sopenharmony_ci "Failed to get logical drive information\n"); 9688c2ecf20Sopenharmony_ci goto out_free; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* 9728c2ecf20Sopenharmony_ci * Initialize the Controller Model Name and Full Model Name fields. 9738c2ecf20Sopenharmony_ci */ 9748c2ecf20Sopenharmony_ci switch (enquiry2->hw.sub_model) { 9758c2ecf20Sopenharmony_ci case DAC960_V1_P_PD_PU: 9768c2ecf20Sopenharmony_ci if (enquiry2->scsi_cap.bus_speed == MYRB_SCSI_SPEED_ULTRA) 9778c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PU"); 9788c2ecf20Sopenharmony_ci else 9798c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PD"); 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci case DAC960_V1_PL: 9828c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PL"); 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci case DAC960_V1_PG: 9858c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PG"); 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci case DAC960_V1_PJ: 9888c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PJ"); 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci case DAC960_V1_PR: 9918c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PR"); 9928c2ecf20Sopenharmony_ci break; 9938c2ecf20Sopenharmony_ci case DAC960_V1_PT: 9948c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PT"); 9958c2ecf20Sopenharmony_ci break; 9968c2ecf20Sopenharmony_ci case DAC960_V1_PTL0: 9978c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PTL0"); 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci case DAC960_V1_PRL: 10008c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PRL"); 10018c2ecf20Sopenharmony_ci break; 10028c2ecf20Sopenharmony_ci case DAC960_V1_PTL1: 10038c2ecf20Sopenharmony_ci strcpy(cb->model_name, "DAC960PTL1"); 10048c2ecf20Sopenharmony_ci break; 10058c2ecf20Sopenharmony_ci case DAC960_V1_1164P: 10068c2ecf20Sopenharmony_ci strcpy(cb->model_name, "eXtremeRAID 1100"); 10078c2ecf20Sopenharmony_ci break; 10088c2ecf20Sopenharmony_ci default: 10098c2ecf20Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 10108c2ecf20Sopenharmony_ci "Unknown Model %X\n", 10118c2ecf20Sopenharmony_ci enquiry2->hw.sub_model); 10128c2ecf20Sopenharmony_ci goto out; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci /* 10158c2ecf20Sopenharmony_ci * Initialize the Controller Firmware Version field and verify that it 10168c2ecf20Sopenharmony_ci * is a supported firmware version. 10178c2ecf20Sopenharmony_ci * The supported firmware versions are: 10188c2ecf20Sopenharmony_ci * 10198c2ecf20Sopenharmony_ci * DAC1164P 5.06 and above 10208c2ecf20Sopenharmony_ci * DAC960PTL/PRL/PJ/PG 4.06 and above 10218c2ecf20Sopenharmony_ci * DAC960PU/PD/PL 3.51 and above 10228c2ecf20Sopenharmony_ci * DAC960PU/PD/PL/P 2.73 and above 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_ci#if defined(CONFIG_ALPHA) 10258c2ecf20Sopenharmony_ci /* 10268c2ecf20Sopenharmony_ci * DEC Alpha machines were often equipped with DAC960 cards that were 10278c2ecf20Sopenharmony_ci * OEMed from Mylex, and had their own custom firmware. Version 2.70, 10288c2ecf20Sopenharmony_ci * the last custom FW revision to be released by DEC for these older 10298c2ecf20Sopenharmony_ci * controllers, appears to work quite well with this driver. 10308c2ecf20Sopenharmony_ci * 10318c2ecf20Sopenharmony_ci * Cards tested successfully were several versions each of the PD and 10328c2ecf20Sopenharmony_ci * PU, called by DEC the KZPSC and KZPAC, respectively, and having 10338c2ecf20Sopenharmony_ci * the Manufacturer Numbers (from Mylex), usually on a sticker on the 10348c2ecf20Sopenharmony_ci * back of the board, of: 10358c2ecf20Sopenharmony_ci * 10368c2ecf20Sopenharmony_ci * KZPSC: D040347 (1-channel) or D040348 (2-channel) 10378c2ecf20Sopenharmony_ci * or D040349 (3-channel) 10388c2ecf20Sopenharmony_ci * KZPAC: D040395 (1-channel) or D040396 (2-channel) 10398c2ecf20Sopenharmony_ci * or D040397 (3-channel) 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_ci# define FIRMWARE_27X "2.70" 10428c2ecf20Sopenharmony_ci#else 10438c2ecf20Sopenharmony_ci# define FIRMWARE_27X "2.73" 10448c2ecf20Sopenharmony_ci#endif 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (enquiry2->fw.major_version == 0) { 10478c2ecf20Sopenharmony_ci enquiry2->fw.major_version = cb->enquiry->fw_major_version; 10488c2ecf20Sopenharmony_ci enquiry2->fw.minor_version = cb->enquiry->fw_minor_version; 10498c2ecf20Sopenharmony_ci enquiry2->fw.firmware_type = '0'; 10508c2ecf20Sopenharmony_ci enquiry2->fw.turn_id = 0; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci snprintf(cb->fw_version, sizeof(cb->fw_version), 10538c2ecf20Sopenharmony_ci "%u.%02u-%c-%02u", 10548c2ecf20Sopenharmony_ci enquiry2->fw.major_version, 10558c2ecf20Sopenharmony_ci enquiry2->fw.minor_version, 10568c2ecf20Sopenharmony_ci enquiry2->fw.firmware_type, 10578c2ecf20Sopenharmony_ci enquiry2->fw.turn_id); 10588c2ecf20Sopenharmony_ci if (!((enquiry2->fw.major_version == 5 && 10598c2ecf20Sopenharmony_ci enquiry2->fw.minor_version >= 6) || 10608c2ecf20Sopenharmony_ci (enquiry2->fw.major_version == 4 && 10618c2ecf20Sopenharmony_ci enquiry2->fw.minor_version >= 6) || 10628c2ecf20Sopenharmony_ci (enquiry2->fw.major_version == 3 && 10638c2ecf20Sopenharmony_ci enquiry2->fw.minor_version >= 51) || 10648c2ecf20Sopenharmony_ci (enquiry2->fw.major_version == 2 && 10658c2ecf20Sopenharmony_ci strcmp(cb->fw_version, FIRMWARE_27X) >= 0))) { 10668c2ecf20Sopenharmony_ci shost_printk(KERN_WARNING, cb->host, 10678c2ecf20Sopenharmony_ci "Firmware Version '%s' unsupported\n", 10688c2ecf20Sopenharmony_ci cb->fw_version); 10698c2ecf20Sopenharmony_ci goto out; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci /* 10728c2ecf20Sopenharmony_ci * Initialize the Channels, Targets, Memory Size, and SAF-TE 10738c2ecf20Sopenharmony_ci * Enclosure Management Enabled fields. 10748c2ecf20Sopenharmony_ci */ 10758c2ecf20Sopenharmony_ci switch (enquiry2->hw.model) { 10768c2ecf20Sopenharmony_ci case MYRB_5_CHANNEL_BOARD: 10778c2ecf20Sopenharmony_ci pchan_max = 5; 10788c2ecf20Sopenharmony_ci break; 10798c2ecf20Sopenharmony_ci case MYRB_3_CHANNEL_BOARD: 10808c2ecf20Sopenharmony_ci case MYRB_3_CHANNEL_ASIC_DAC: 10818c2ecf20Sopenharmony_ci pchan_max = 3; 10828c2ecf20Sopenharmony_ci break; 10838c2ecf20Sopenharmony_ci case MYRB_2_CHANNEL_BOARD: 10848c2ecf20Sopenharmony_ci pchan_max = 2; 10858c2ecf20Sopenharmony_ci break; 10868c2ecf20Sopenharmony_ci default: 10878c2ecf20Sopenharmony_ci pchan_max = enquiry2->cfg_chan; 10888c2ecf20Sopenharmony_ci break; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci pchan_cur = enquiry2->cur_chan; 10918c2ecf20Sopenharmony_ci if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_32BIT) 10928c2ecf20Sopenharmony_ci cb->bus_width = 32; 10938c2ecf20Sopenharmony_ci else if (enquiry2->scsi_cap.bus_width == MYRB_WIDTH_WIDE_16BIT) 10948c2ecf20Sopenharmony_ci cb->bus_width = 16; 10958c2ecf20Sopenharmony_ci else 10968c2ecf20Sopenharmony_ci cb->bus_width = 8; 10978c2ecf20Sopenharmony_ci cb->ldev_block_size = enquiry2->ldev_block_size; 10988c2ecf20Sopenharmony_ci shost->max_channel = pchan_cur; 10998c2ecf20Sopenharmony_ci shost->max_id = enquiry2->max_targets; 11008c2ecf20Sopenharmony_ci memsize = enquiry2->mem_size >> 20; 11018c2ecf20Sopenharmony_ci cb->safte_enabled = (enquiry2->fault_mgmt == MYRB_FAULT_SAFTE); 11028c2ecf20Sopenharmony_ci /* 11038c2ecf20Sopenharmony_ci * Initialize the Controller Queue Depth, Driver Queue Depth, 11048c2ecf20Sopenharmony_ci * Logical Drive Count, Maximum Blocks per Command, Controller 11058c2ecf20Sopenharmony_ci * Scatter/Gather Limit, and Driver Scatter/Gather Limit. 11068c2ecf20Sopenharmony_ci * The Driver Queue Depth must be at most one less than the 11078c2ecf20Sopenharmony_ci * Controller Queue Depth to allow for an automatic drive 11088c2ecf20Sopenharmony_ci * rebuild operation. 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_ci shost->can_queue = cb->enquiry->max_tcq; 11118c2ecf20Sopenharmony_ci if (shost->can_queue < 3) 11128c2ecf20Sopenharmony_ci shost->can_queue = enquiry2->max_cmds; 11138c2ecf20Sopenharmony_ci if (shost->can_queue < 3) 11148c2ecf20Sopenharmony_ci /* Play safe and disable TCQ */ 11158c2ecf20Sopenharmony_ci shost->can_queue = 1; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci if (shost->can_queue > MYRB_CMD_MBOX_COUNT - 2) 11188c2ecf20Sopenharmony_ci shost->can_queue = MYRB_CMD_MBOX_COUNT - 2; 11198c2ecf20Sopenharmony_ci shost->max_sectors = enquiry2->max_sectors; 11208c2ecf20Sopenharmony_ci shost->sg_tablesize = enquiry2->max_sge; 11218c2ecf20Sopenharmony_ci if (shost->sg_tablesize > MYRB_SCATTER_GATHER_LIMIT) 11228c2ecf20Sopenharmony_ci shost->sg_tablesize = MYRB_SCATTER_GATHER_LIMIT; 11238c2ecf20Sopenharmony_ci /* 11248c2ecf20Sopenharmony_ci * Initialize the Stripe Size, Segment Size, and Geometry Translation. 11258c2ecf20Sopenharmony_ci */ 11268c2ecf20Sopenharmony_ci cb->stripe_size = config2->blocks_per_stripe * config2->block_factor 11278c2ecf20Sopenharmony_ci >> (10 - MYRB_BLKSIZE_BITS); 11288c2ecf20Sopenharmony_ci cb->segment_size = config2->blocks_per_cacheline * config2->block_factor 11298c2ecf20Sopenharmony_ci >> (10 - MYRB_BLKSIZE_BITS); 11308c2ecf20Sopenharmony_ci /* Assume 255/63 translation */ 11318c2ecf20Sopenharmony_ci cb->ldev_geom_heads = 255; 11328c2ecf20Sopenharmony_ci cb->ldev_geom_sectors = 63; 11338c2ecf20Sopenharmony_ci if (config2->drive_geometry) { 11348c2ecf20Sopenharmony_ci cb->ldev_geom_heads = 128; 11358c2ecf20Sopenharmony_ci cb->ldev_geom_sectors = 32; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci /* 11398c2ecf20Sopenharmony_ci * Initialize the Background Initialization Status. 11408c2ecf20Sopenharmony_ci */ 11418c2ecf20Sopenharmony_ci if ((cb->fw_version[0] == '4' && 11428c2ecf20Sopenharmony_ci strcmp(cb->fw_version, "4.08") >= 0) || 11438c2ecf20Sopenharmony_ci (cb->fw_version[0] == '5' && 11448c2ecf20Sopenharmony_ci strcmp(cb->fw_version, "5.08") >= 0)) { 11458c2ecf20Sopenharmony_ci cb->bgi_status_supported = true; 11468c2ecf20Sopenharmony_ci myrb_bgi_control(cb); 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci cb->last_rbld_status = MYRB_NO_STDBY_RBLD_OR_CHECK_IN_PROGRESS; 11498c2ecf20Sopenharmony_ci ret = 0; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ciout: 11528c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11538c2ecf20Sopenharmony_ci "Configuring %s PCI RAID Controller\n", cb->model_name); 11548c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11558c2ecf20Sopenharmony_ci " Firmware Version: %s, Memory Size: %dMB\n", 11568c2ecf20Sopenharmony_ci cb->fw_version, memsize); 11578c2ecf20Sopenharmony_ci if (cb->io_addr == 0) 11588c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11598c2ecf20Sopenharmony_ci " I/O Address: n/a, PCI Address: 0x%lX, IRQ Channel: %d\n", 11608c2ecf20Sopenharmony_ci (unsigned long)cb->pci_addr, cb->irq); 11618c2ecf20Sopenharmony_ci else 11628c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11638c2ecf20Sopenharmony_ci " I/O Address: 0x%lX, PCI Address: 0x%lX, IRQ Channel: %d\n", 11648c2ecf20Sopenharmony_ci (unsigned long)cb->io_addr, (unsigned long)cb->pci_addr, 11658c2ecf20Sopenharmony_ci cb->irq); 11668c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11678c2ecf20Sopenharmony_ci " Controller Queue Depth: %d, Maximum Blocks per Command: %d\n", 11688c2ecf20Sopenharmony_ci cb->host->can_queue, cb->host->max_sectors); 11698c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11708c2ecf20Sopenharmony_ci " Driver Queue Depth: %d, Scatter/Gather Limit: %d of %d Segments\n", 11718c2ecf20Sopenharmony_ci cb->host->can_queue, cb->host->sg_tablesize, 11728c2ecf20Sopenharmony_ci MYRB_SCATTER_GATHER_LIMIT); 11738c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11748c2ecf20Sopenharmony_ci " Stripe Size: %dKB, Segment Size: %dKB, BIOS Geometry: %d/%d%s\n", 11758c2ecf20Sopenharmony_ci cb->stripe_size, cb->segment_size, 11768c2ecf20Sopenharmony_ci cb->ldev_geom_heads, cb->ldev_geom_sectors, 11778c2ecf20Sopenharmony_ci cb->safte_enabled ? 11788c2ecf20Sopenharmony_ci " SAF-TE Enclosure Management Enabled" : ""); 11798c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11808c2ecf20Sopenharmony_ci " Physical: %d/%d channels %d/%d/%d devices\n", 11818c2ecf20Sopenharmony_ci pchan_cur, pchan_max, 0, cb->enquiry->pdev_dead, 11828c2ecf20Sopenharmony_ci cb->host->max_id); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, cb->host, 11858c2ecf20Sopenharmony_ci " Logical: 1/1 channels, %d/%d disks\n", 11868c2ecf20Sopenharmony_ci cb->enquiry->ldev_count, MYRB_MAX_LDEVS); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ciout_free: 11898c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct myrb_enquiry2), 11908c2ecf20Sopenharmony_ci enquiry2, enquiry2_addr); 11918c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct myrb_config2), 11928c2ecf20Sopenharmony_ci config2, config2_addr); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci return ret; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci/** 11988c2ecf20Sopenharmony_ci * myrb_unmap - unmaps controller structures 11998c2ecf20Sopenharmony_ci */ 12008c2ecf20Sopenharmony_cistatic void myrb_unmap(struct myrb_hba *cb) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci if (cb->ldev_info_buf) { 12038c2ecf20Sopenharmony_ci size_t ldev_info_size = sizeof(struct myrb_ldev_info) * 12048c2ecf20Sopenharmony_ci MYRB_MAX_LDEVS; 12058c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, ldev_info_size, 12068c2ecf20Sopenharmony_ci cb->ldev_info_buf, cb->ldev_info_addr); 12078c2ecf20Sopenharmony_ci cb->ldev_info_buf = NULL; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci if (cb->err_table) { 12108c2ecf20Sopenharmony_ci size_t err_table_size = sizeof(struct myrb_error_entry) * 12118c2ecf20Sopenharmony_ci MYRB_MAX_CHANNELS * MYRB_MAX_TARGETS; 12128c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, err_table_size, 12138c2ecf20Sopenharmony_ci cb->err_table, cb->err_table_addr); 12148c2ecf20Sopenharmony_ci cb->err_table = NULL; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci if (cb->enquiry) { 12178c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, sizeof(struct myrb_enquiry), 12188c2ecf20Sopenharmony_ci cb->enquiry, cb->enquiry_addr); 12198c2ecf20Sopenharmony_ci cb->enquiry = NULL; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci if (cb->first_stat_mbox) { 12228c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, cb->stat_mbox_size, 12238c2ecf20Sopenharmony_ci cb->first_stat_mbox, cb->stat_mbox_addr); 12248c2ecf20Sopenharmony_ci cb->first_stat_mbox = NULL; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci if (cb->first_cmd_mbox) { 12278c2ecf20Sopenharmony_ci dma_free_coherent(&cb->pdev->dev, cb->cmd_mbox_size, 12288c2ecf20Sopenharmony_ci cb->first_cmd_mbox, cb->cmd_mbox_addr); 12298c2ecf20Sopenharmony_ci cb->first_cmd_mbox = NULL; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci} 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci/** 12348c2ecf20Sopenharmony_ci * myrb_cleanup - cleanup controller structures 12358c2ecf20Sopenharmony_ci */ 12368c2ecf20Sopenharmony_cistatic void myrb_cleanup(struct myrb_hba *cb) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci /* Free the memory mailbox, status, and related structures */ 12418c2ecf20Sopenharmony_ci myrb_unmap(cb); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci if (cb->mmio_base) { 12448c2ecf20Sopenharmony_ci if (cb->disable_intr) 12458c2ecf20Sopenharmony_ci cb->disable_intr(cb->io_base); 12468c2ecf20Sopenharmony_ci iounmap(cb->mmio_base); 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci if (cb->irq) 12498c2ecf20Sopenharmony_ci free_irq(cb->irq, cb); 12508c2ecf20Sopenharmony_ci if (cb->io_addr) 12518c2ecf20Sopenharmony_ci release_region(cb->io_addr, 0x80); 12528c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 12538c2ecf20Sopenharmony_ci pci_disable_device(pdev); 12548c2ecf20Sopenharmony_ci scsi_host_put(cb->host); 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic int myrb_host_reset(struct scsi_cmnd *scmd) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci struct Scsi_Host *shost = scmd->device->host; 12608c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci cb->reset(cb->io_base); 12638c2ecf20Sopenharmony_ci return SUCCESS; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic int myrb_pthru_queuecommand(struct Scsi_Host *shost, 12678c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 12708c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd); 12718c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 12728c2ecf20Sopenharmony_ci struct myrb_dcdb *dcdb; 12738c2ecf20Sopenharmony_ci dma_addr_t dcdb_addr; 12748c2ecf20Sopenharmony_ci struct scsi_device *sdev = scmd->device; 12758c2ecf20Sopenharmony_ci struct scatterlist *sgl; 12768c2ecf20Sopenharmony_ci unsigned long flags; 12778c2ecf20Sopenharmony_ci int nsge; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 12808c2ecf20Sopenharmony_ci dcdb = dma_pool_alloc(cb->dcdb_pool, GFP_ATOMIC, &dcdb_addr); 12818c2ecf20Sopenharmony_ci if (!dcdb) 12828c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 12838c2ecf20Sopenharmony_ci nsge = scsi_dma_map(scmd); 12848c2ecf20Sopenharmony_ci if (nsge > 1) { 12858c2ecf20Sopenharmony_ci dma_pool_free(cb->dcdb_pool, dcdb, dcdb_addr); 12868c2ecf20Sopenharmony_ci scmd->result = (DID_ERROR << 16); 12878c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 12888c2ecf20Sopenharmony_ci return 0; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci mbox->type3.opcode = MYRB_CMD_DCDB; 12928c2ecf20Sopenharmony_ci mbox->type3.id = scmd->request->tag + 3; 12938c2ecf20Sopenharmony_ci mbox->type3.addr = dcdb_addr; 12948c2ecf20Sopenharmony_ci dcdb->channel = sdev->channel; 12958c2ecf20Sopenharmony_ci dcdb->target = sdev->id; 12968c2ecf20Sopenharmony_ci switch (scmd->sc_data_direction) { 12978c2ecf20Sopenharmony_ci case DMA_NONE: 12988c2ecf20Sopenharmony_ci dcdb->data_xfer = MYRB_DCDB_XFER_NONE; 12998c2ecf20Sopenharmony_ci break; 13008c2ecf20Sopenharmony_ci case DMA_TO_DEVICE: 13018c2ecf20Sopenharmony_ci dcdb->data_xfer = MYRB_DCDB_XFER_SYSTEM_TO_DEVICE; 13028c2ecf20Sopenharmony_ci break; 13038c2ecf20Sopenharmony_ci case DMA_FROM_DEVICE: 13048c2ecf20Sopenharmony_ci dcdb->data_xfer = MYRB_DCDB_XFER_DEVICE_TO_SYSTEM; 13058c2ecf20Sopenharmony_ci break; 13068c2ecf20Sopenharmony_ci default: 13078c2ecf20Sopenharmony_ci dcdb->data_xfer = MYRB_DCDB_XFER_ILLEGAL; 13088c2ecf20Sopenharmony_ci break; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci dcdb->early_status = false; 13118c2ecf20Sopenharmony_ci if (scmd->request->timeout <= 10) 13128c2ecf20Sopenharmony_ci dcdb->timeout = MYRB_DCDB_TMO_10_SECS; 13138c2ecf20Sopenharmony_ci else if (scmd->request->timeout <= 60) 13148c2ecf20Sopenharmony_ci dcdb->timeout = MYRB_DCDB_TMO_60_SECS; 13158c2ecf20Sopenharmony_ci else if (scmd->request->timeout <= 600) 13168c2ecf20Sopenharmony_ci dcdb->timeout = MYRB_DCDB_TMO_10_MINS; 13178c2ecf20Sopenharmony_ci else 13188c2ecf20Sopenharmony_ci dcdb->timeout = MYRB_DCDB_TMO_24_HRS; 13198c2ecf20Sopenharmony_ci dcdb->no_autosense = false; 13208c2ecf20Sopenharmony_ci dcdb->allow_disconnect = true; 13218c2ecf20Sopenharmony_ci sgl = scsi_sglist(scmd); 13228c2ecf20Sopenharmony_ci dcdb->dma_addr = sg_dma_address(sgl); 13238c2ecf20Sopenharmony_ci if (sg_dma_len(sgl) > USHRT_MAX) { 13248c2ecf20Sopenharmony_ci dcdb->xfer_len_lo = sg_dma_len(sgl) & 0xffff; 13258c2ecf20Sopenharmony_ci dcdb->xfer_len_hi4 = sg_dma_len(sgl) >> 16; 13268c2ecf20Sopenharmony_ci } else { 13278c2ecf20Sopenharmony_ci dcdb->xfer_len_lo = sg_dma_len(sgl); 13288c2ecf20Sopenharmony_ci dcdb->xfer_len_hi4 = 0; 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci dcdb->cdb_len = scmd->cmd_len; 13318c2ecf20Sopenharmony_ci dcdb->sense_len = sizeof(dcdb->sense); 13328c2ecf20Sopenharmony_ci memcpy(&dcdb->cdb, scmd->cmnd, scmd->cmd_len); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 13358c2ecf20Sopenharmony_ci cb->qcmd(cb, cmd_blk); 13368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 13378c2ecf20Sopenharmony_ci return 0; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic void myrb_inquiry(struct myrb_hba *cb, 13418c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd) 13428c2ecf20Sopenharmony_ci{ 13438c2ecf20Sopenharmony_ci unsigned char inq[36] = { 13448c2ecf20Sopenharmony_ci 0x00, 0x00, 0x03, 0x02, 0x20, 0x00, 0x01, 0x00, 13458c2ecf20Sopenharmony_ci 0x4d, 0x59, 0x4c, 0x45, 0x58, 0x20, 0x20, 0x20, 13468c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 13478c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 13488c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 13498c2ecf20Sopenharmony_ci }; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci if (cb->bus_width > 16) 13528c2ecf20Sopenharmony_ci inq[7] |= 1 << 6; 13538c2ecf20Sopenharmony_ci if (cb->bus_width > 8) 13548c2ecf20Sopenharmony_ci inq[7] |= 1 << 5; 13558c2ecf20Sopenharmony_ci memcpy(&inq[16], cb->model_name, 16); 13568c2ecf20Sopenharmony_ci memcpy(&inq[32], cb->fw_version, 1); 13578c2ecf20Sopenharmony_ci memcpy(&inq[33], &cb->fw_version[2], 2); 13588c2ecf20Sopenharmony_ci memcpy(&inq[35], &cb->fw_version[7], 1); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci scsi_sg_copy_from_buffer(scmd, (void *)inq, 36); 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic void 13648c2ecf20Sopenharmony_cimyrb_mode_sense(struct myrb_hba *cb, struct scsi_cmnd *scmd, 13658c2ecf20Sopenharmony_ci struct myrb_ldev_info *ldev_info) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci unsigned char modes[32], *mode_pg; 13688c2ecf20Sopenharmony_ci bool dbd; 13698c2ecf20Sopenharmony_ci size_t mode_len; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci dbd = (scmd->cmnd[1] & 0x08) == 0x08; 13728c2ecf20Sopenharmony_ci if (dbd) { 13738c2ecf20Sopenharmony_ci mode_len = 24; 13748c2ecf20Sopenharmony_ci mode_pg = &modes[4]; 13758c2ecf20Sopenharmony_ci } else { 13768c2ecf20Sopenharmony_ci mode_len = 32; 13778c2ecf20Sopenharmony_ci mode_pg = &modes[12]; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci memset(modes, 0, sizeof(modes)); 13808c2ecf20Sopenharmony_ci modes[0] = mode_len - 1; 13818c2ecf20Sopenharmony_ci if (!dbd) { 13828c2ecf20Sopenharmony_ci unsigned char *block_desc = &modes[4]; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci modes[3] = 8; 13858c2ecf20Sopenharmony_ci put_unaligned_be32(ldev_info->size, &block_desc[0]); 13868c2ecf20Sopenharmony_ci put_unaligned_be32(cb->ldev_block_size, &block_desc[5]); 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci mode_pg[0] = 0x08; 13898c2ecf20Sopenharmony_ci mode_pg[1] = 0x12; 13908c2ecf20Sopenharmony_ci if (ldev_info->wb_enabled) 13918c2ecf20Sopenharmony_ci mode_pg[2] |= 0x04; 13928c2ecf20Sopenharmony_ci if (cb->segment_size) { 13938c2ecf20Sopenharmony_ci mode_pg[2] |= 0x08; 13948c2ecf20Sopenharmony_ci put_unaligned_be16(cb->segment_size, &mode_pg[14]); 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci scsi_sg_copy_from_buffer(scmd, modes, mode_len); 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_cistatic void myrb_request_sense(struct myrb_hba *cb, 14018c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 14048c2ecf20Sopenharmony_ci NO_SENSE, 0, 0); 14058c2ecf20Sopenharmony_ci scsi_sg_copy_from_buffer(scmd, scmd->sense_buffer, 14068c2ecf20Sopenharmony_ci SCSI_SENSE_BUFFERSIZE); 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic void myrb_read_capacity(struct myrb_hba *cb, struct scsi_cmnd *scmd, 14108c2ecf20Sopenharmony_ci struct myrb_ldev_info *ldev_info) 14118c2ecf20Sopenharmony_ci{ 14128c2ecf20Sopenharmony_ci unsigned char data[8]; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, 14158c2ecf20Sopenharmony_ci "Capacity %u, blocksize %u\n", 14168c2ecf20Sopenharmony_ci ldev_info->size, cb->ldev_block_size); 14178c2ecf20Sopenharmony_ci put_unaligned_be32(ldev_info->size - 1, &data[0]); 14188c2ecf20Sopenharmony_ci put_unaligned_be32(cb->ldev_block_size, &data[4]); 14198c2ecf20Sopenharmony_ci scsi_sg_copy_from_buffer(scmd, data, 8); 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic int myrb_ldev_queuecommand(struct Scsi_Host *shost, 14238c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 14268c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = scsi_cmd_priv(scmd); 14278c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 14288c2ecf20Sopenharmony_ci struct myrb_ldev_info *ldev_info; 14298c2ecf20Sopenharmony_ci struct scsi_device *sdev = scmd->device; 14308c2ecf20Sopenharmony_ci struct scatterlist *sgl; 14318c2ecf20Sopenharmony_ci unsigned long flags; 14328c2ecf20Sopenharmony_ci u64 lba; 14338c2ecf20Sopenharmony_ci u32 block_cnt; 14348c2ecf20Sopenharmony_ci int nsge; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci ldev_info = sdev->hostdata; 14378c2ecf20Sopenharmony_ci if (ldev_info->state != MYRB_DEVICE_ONLINE && 14388c2ecf20Sopenharmony_ci ldev_info->state != MYRB_DEVICE_WO) { 14398c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, "ldev %u in state %x, skip\n", 14408c2ecf20Sopenharmony_ci sdev->id, ldev_info ? ldev_info->state : 0xff); 14418c2ecf20Sopenharmony_ci scmd->result = (DID_BAD_TARGET << 16); 14428c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 14438c2ecf20Sopenharmony_ci return 0; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci switch (scmd->cmnd[0]) { 14468c2ecf20Sopenharmony_ci case TEST_UNIT_READY: 14478c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16); 14488c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 14498c2ecf20Sopenharmony_ci return 0; 14508c2ecf20Sopenharmony_ci case INQUIRY: 14518c2ecf20Sopenharmony_ci if (scmd->cmnd[1] & 1) { 14528c2ecf20Sopenharmony_ci /* Illegal request, invalid field in CDB */ 14538c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 14548c2ecf20Sopenharmony_ci ILLEGAL_REQUEST, 0x24, 0); 14558c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | 14568c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 14578c2ecf20Sopenharmony_ci } else { 14588c2ecf20Sopenharmony_ci myrb_inquiry(cb, scmd); 14598c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16); 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 14628c2ecf20Sopenharmony_ci return 0; 14638c2ecf20Sopenharmony_ci case SYNCHRONIZE_CACHE: 14648c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16); 14658c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 14668c2ecf20Sopenharmony_ci return 0; 14678c2ecf20Sopenharmony_ci case MODE_SENSE: 14688c2ecf20Sopenharmony_ci if ((scmd->cmnd[2] & 0x3F) != 0x3F && 14698c2ecf20Sopenharmony_ci (scmd->cmnd[2] & 0x3F) != 0x08) { 14708c2ecf20Sopenharmony_ci /* Illegal request, invalid field in CDB */ 14718c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 14728c2ecf20Sopenharmony_ci ILLEGAL_REQUEST, 0x24, 0); 14738c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | 14748c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 14758c2ecf20Sopenharmony_ci } else { 14768c2ecf20Sopenharmony_ci myrb_mode_sense(cb, scmd, ldev_info); 14778c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16); 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 14808c2ecf20Sopenharmony_ci return 0; 14818c2ecf20Sopenharmony_ci case READ_CAPACITY: 14828c2ecf20Sopenharmony_ci if ((scmd->cmnd[1] & 1) || 14838c2ecf20Sopenharmony_ci (scmd->cmnd[8] & 1)) { 14848c2ecf20Sopenharmony_ci /* Illegal request, invalid field in CDB */ 14858c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 14868c2ecf20Sopenharmony_ci ILLEGAL_REQUEST, 0x24, 0); 14878c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | 14888c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 14898c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 14908c2ecf20Sopenharmony_ci return 0; 14918c2ecf20Sopenharmony_ci } 14928c2ecf20Sopenharmony_ci lba = get_unaligned_be32(&scmd->cmnd[2]); 14938c2ecf20Sopenharmony_ci if (lba) { 14948c2ecf20Sopenharmony_ci /* Illegal request, invalid field in CDB */ 14958c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 14968c2ecf20Sopenharmony_ci ILLEGAL_REQUEST, 0x24, 0); 14978c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | 14988c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 14998c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 15008c2ecf20Sopenharmony_ci return 0; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci myrb_read_capacity(cb, scmd, ldev_info); 15038c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 15048c2ecf20Sopenharmony_ci return 0; 15058c2ecf20Sopenharmony_ci case REQUEST_SENSE: 15068c2ecf20Sopenharmony_ci myrb_request_sense(cb, scmd); 15078c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16); 15088c2ecf20Sopenharmony_ci return 0; 15098c2ecf20Sopenharmony_ci case SEND_DIAGNOSTIC: 15108c2ecf20Sopenharmony_ci if (scmd->cmnd[1] != 0x04) { 15118c2ecf20Sopenharmony_ci /* Illegal request, invalid field in CDB */ 15128c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 15138c2ecf20Sopenharmony_ci ILLEGAL_REQUEST, 0x24, 0); 15148c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | 15158c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 15168c2ecf20Sopenharmony_ci } else { 15178c2ecf20Sopenharmony_ci /* Assume good status */ 15188c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16); 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 15218c2ecf20Sopenharmony_ci return 0; 15228c2ecf20Sopenharmony_ci case READ_6: 15238c2ecf20Sopenharmony_ci if (ldev_info->state == MYRB_DEVICE_WO) { 15248c2ecf20Sopenharmony_ci /* Data protect, attempt to read invalid data */ 15258c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 15268c2ecf20Sopenharmony_ci DATA_PROTECT, 0x21, 0x06); 15278c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | 15288c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 15298c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 15308c2ecf20Sopenharmony_ci return 0; 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci fallthrough; 15338c2ecf20Sopenharmony_ci case WRITE_6: 15348c2ecf20Sopenharmony_ci lba = (((scmd->cmnd[1] & 0x1F) << 16) | 15358c2ecf20Sopenharmony_ci (scmd->cmnd[2] << 8) | 15368c2ecf20Sopenharmony_ci scmd->cmnd[3]); 15378c2ecf20Sopenharmony_ci block_cnt = scmd->cmnd[4]; 15388c2ecf20Sopenharmony_ci break; 15398c2ecf20Sopenharmony_ci case READ_10: 15408c2ecf20Sopenharmony_ci if (ldev_info->state == MYRB_DEVICE_WO) { 15418c2ecf20Sopenharmony_ci /* Data protect, attempt to read invalid data */ 15428c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 15438c2ecf20Sopenharmony_ci DATA_PROTECT, 0x21, 0x06); 15448c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | 15458c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 15468c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 15478c2ecf20Sopenharmony_ci return 0; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci fallthrough; 15508c2ecf20Sopenharmony_ci case WRITE_10: 15518c2ecf20Sopenharmony_ci case VERIFY: /* 0x2F */ 15528c2ecf20Sopenharmony_ci case WRITE_VERIFY: /* 0x2E */ 15538c2ecf20Sopenharmony_ci lba = get_unaligned_be32(&scmd->cmnd[2]); 15548c2ecf20Sopenharmony_ci block_cnt = get_unaligned_be16(&scmd->cmnd[7]); 15558c2ecf20Sopenharmony_ci break; 15568c2ecf20Sopenharmony_ci case READ_12: 15578c2ecf20Sopenharmony_ci if (ldev_info->state == MYRB_DEVICE_WO) { 15588c2ecf20Sopenharmony_ci /* Data protect, attempt to read invalid data */ 15598c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 15608c2ecf20Sopenharmony_ci DATA_PROTECT, 0x21, 0x06); 15618c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | 15628c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 15638c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 15648c2ecf20Sopenharmony_ci return 0; 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci fallthrough; 15678c2ecf20Sopenharmony_ci case WRITE_12: 15688c2ecf20Sopenharmony_ci case VERIFY_12: /* 0xAF */ 15698c2ecf20Sopenharmony_ci case WRITE_VERIFY_12: /* 0xAE */ 15708c2ecf20Sopenharmony_ci lba = get_unaligned_be32(&scmd->cmnd[2]); 15718c2ecf20Sopenharmony_ci block_cnt = get_unaligned_be32(&scmd->cmnd[6]); 15728c2ecf20Sopenharmony_ci break; 15738c2ecf20Sopenharmony_ci default: 15748c2ecf20Sopenharmony_ci /* Illegal request, invalid opcode */ 15758c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 15768c2ecf20Sopenharmony_ci ILLEGAL_REQUEST, 0x20, 0); 15778c2ecf20Sopenharmony_ci scmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; 15788c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 15798c2ecf20Sopenharmony_ci return 0; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 15838c2ecf20Sopenharmony_ci mbox->type5.id = scmd->request->tag + 3; 15848c2ecf20Sopenharmony_ci if (scmd->sc_data_direction == DMA_NONE) 15858c2ecf20Sopenharmony_ci goto submit; 15868c2ecf20Sopenharmony_ci nsge = scsi_dma_map(scmd); 15878c2ecf20Sopenharmony_ci if (nsge == 1) { 15888c2ecf20Sopenharmony_ci sgl = scsi_sglist(scmd); 15898c2ecf20Sopenharmony_ci if (scmd->sc_data_direction == DMA_FROM_DEVICE) 15908c2ecf20Sopenharmony_ci mbox->type5.opcode = MYRB_CMD_READ; 15918c2ecf20Sopenharmony_ci else 15928c2ecf20Sopenharmony_ci mbox->type5.opcode = MYRB_CMD_WRITE; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci mbox->type5.ld.xfer_len = block_cnt; 15958c2ecf20Sopenharmony_ci mbox->type5.ld.ldev_num = sdev->id; 15968c2ecf20Sopenharmony_ci mbox->type5.lba = lba; 15978c2ecf20Sopenharmony_ci mbox->type5.addr = (u32)sg_dma_address(sgl); 15988c2ecf20Sopenharmony_ci } else { 15998c2ecf20Sopenharmony_ci struct myrb_sge *hw_sgl; 16008c2ecf20Sopenharmony_ci dma_addr_t hw_sgl_addr; 16018c2ecf20Sopenharmony_ci int i; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci hw_sgl = dma_pool_alloc(cb->sg_pool, GFP_ATOMIC, &hw_sgl_addr); 16048c2ecf20Sopenharmony_ci if (!hw_sgl) 16058c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci cmd_blk->sgl = hw_sgl; 16088c2ecf20Sopenharmony_ci cmd_blk->sgl_addr = hw_sgl_addr; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (scmd->sc_data_direction == DMA_FROM_DEVICE) 16118c2ecf20Sopenharmony_ci mbox->type5.opcode = MYRB_CMD_READ_SG; 16128c2ecf20Sopenharmony_ci else 16138c2ecf20Sopenharmony_ci mbox->type5.opcode = MYRB_CMD_WRITE_SG; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci mbox->type5.ld.xfer_len = block_cnt; 16168c2ecf20Sopenharmony_ci mbox->type5.ld.ldev_num = sdev->id; 16178c2ecf20Sopenharmony_ci mbox->type5.lba = lba; 16188c2ecf20Sopenharmony_ci mbox->type5.addr = hw_sgl_addr; 16198c2ecf20Sopenharmony_ci mbox->type5.sg_count = nsge; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci scsi_for_each_sg(scmd, sgl, nsge, i) { 16228c2ecf20Sopenharmony_ci hw_sgl->sge_addr = (u32)sg_dma_address(sgl); 16238c2ecf20Sopenharmony_ci hw_sgl->sge_count = (u32)sg_dma_len(sgl); 16248c2ecf20Sopenharmony_ci hw_sgl++; 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci } 16278c2ecf20Sopenharmony_cisubmit: 16288c2ecf20Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 16298c2ecf20Sopenharmony_ci cb->qcmd(cb, cmd_blk); 16308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci return 0; 16338c2ecf20Sopenharmony_ci} 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_cistatic int myrb_queuecommand(struct Scsi_Host *shost, 16368c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci struct scsi_device *sdev = scmd->device; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci if (sdev->channel > myrb_logical_channel(shost)) { 16418c2ecf20Sopenharmony_ci scmd->result = (DID_BAD_TARGET << 16); 16428c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 16438c2ecf20Sopenharmony_ci return 0; 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci if (sdev->channel == myrb_logical_channel(shost)) 16468c2ecf20Sopenharmony_ci return myrb_ldev_queuecommand(shost, scmd); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci return myrb_pthru_queuecommand(shost, scmd); 16498c2ecf20Sopenharmony_ci} 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic int myrb_ldev_slave_alloc(struct scsi_device *sdev) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 16548c2ecf20Sopenharmony_ci struct myrb_ldev_info *ldev_info; 16558c2ecf20Sopenharmony_ci unsigned short ldev_num = sdev->id; 16568c2ecf20Sopenharmony_ci enum raid_level level; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci ldev_info = cb->ldev_info_buf + ldev_num; 16598c2ecf20Sopenharmony_ci if (!ldev_info) 16608c2ecf20Sopenharmony_ci return -ENXIO; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci sdev->hostdata = kzalloc(sizeof(*ldev_info), GFP_KERNEL); 16638c2ecf20Sopenharmony_ci if (!sdev->hostdata) 16648c2ecf20Sopenharmony_ci return -ENOMEM; 16658c2ecf20Sopenharmony_ci dev_dbg(&sdev->sdev_gendev, 16668c2ecf20Sopenharmony_ci "slave alloc ldev %d state %x\n", 16678c2ecf20Sopenharmony_ci ldev_num, ldev_info->state); 16688c2ecf20Sopenharmony_ci memcpy(sdev->hostdata, ldev_info, 16698c2ecf20Sopenharmony_ci sizeof(*ldev_info)); 16708c2ecf20Sopenharmony_ci switch (ldev_info->raid_level) { 16718c2ecf20Sopenharmony_ci case MYRB_RAID_LEVEL0: 16728c2ecf20Sopenharmony_ci level = RAID_LEVEL_LINEAR; 16738c2ecf20Sopenharmony_ci break; 16748c2ecf20Sopenharmony_ci case MYRB_RAID_LEVEL1: 16758c2ecf20Sopenharmony_ci level = RAID_LEVEL_1; 16768c2ecf20Sopenharmony_ci break; 16778c2ecf20Sopenharmony_ci case MYRB_RAID_LEVEL3: 16788c2ecf20Sopenharmony_ci level = RAID_LEVEL_3; 16798c2ecf20Sopenharmony_ci break; 16808c2ecf20Sopenharmony_ci case MYRB_RAID_LEVEL5: 16818c2ecf20Sopenharmony_ci level = RAID_LEVEL_5; 16828c2ecf20Sopenharmony_ci break; 16838c2ecf20Sopenharmony_ci case MYRB_RAID_LEVEL6: 16848c2ecf20Sopenharmony_ci level = RAID_LEVEL_6; 16858c2ecf20Sopenharmony_ci break; 16868c2ecf20Sopenharmony_ci case MYRB_RAID_JBOD: 16878c2ecf20Sopenharmony_ci level = RAID_LEVEL_JBOD; 16888c2ecf20Sopenharmony_ci break; 16898c2ecf20Sopenharmony_ci default: 16908c2ecf20Sopenharmony_ci level = RAID_LEVEL_UNKNOWN; 16918c2ecf20Sopenharmony_ci break; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci raid_set_level(myrb_raid_template, &sdev->sdev_gendev, level); 16948c2ecf20Sopenharmony_ci return 0; 16958c2ecf20Sopenharmony_ci} 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_cistatic int myrb_pdev_slave_alloc(struct scsi_device *sdev) 16988c2ecf20Sopenharmony_ci{ 16998c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 17008c2ecf20Sopenharmony_ci struct myrb_pdev_state *pdev_info; 17018c2ecf20Sopenharmony_ci unsigned short status; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci if (sdev->id > MYRB_MAX_TARGETS) 17048c2ecf20Sopenharmony_ci return -ENXIO; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci pdev_info = kzalloc(sizeof(*pdev_info), GFP_KERNEL|GFP_DMA); 17078c2ecf20Sopenharmony_ci if (!pdev_info) 17088c2ecf20Sopenharmony_ci return -ENOMEM; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE, 17118c2ecf20Sopenharmony_ci sdev, pdev_info); 17128c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 17138c2ecf20Sopenharmony_ci dev_dbg(&sdev->sdev_gendev, 17148c2ecf20Sopenharmony_ci "Failed to get device state, status %x\n", 17158c2ecf20Sopenharmony_ci status); 17168c2ecf20Sopenharmony_ci kfree(pdev_info); 17178c2ecf20Sopenharmony_ci return -ENXIO; 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci if (!pdev_info->present) { 17208c2ecf20Sopenharmony_ci dev_dbg(&sdev->sdev_gendev, 17218c2ecf20Sopenharmony_ci "device not present, skip\n"); 17228c2ecf20Sopenharmony_ci kfree(pdev_info); 17238c2ecf20Sopenharmony_ci return -ENXIO; 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci dev_dbg(&sdev->sdev_gendev, 17268c2ecf20Sopenharmony_ci "slave alloc pdev %d:%d state %x\n", 17278c2ecf20Sopenharmony_ci sdev->channel, sdev->id, pdev_info->state); 17288c2ecf20Sopenharmony_ci sdev->hostdata = pdev_info; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci return 0; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic int myrb_slave_alloc(struct scsi_device *sdev) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci if (sdev->channel > myrb_logical_channel(sdev->host)) 17368c2ecf20Sopenharmony_ci return -ENXIO; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci if (sdev->lun > 0) 17398c2ecf20Sopenharmony_ci return -ENXIO; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci if (sdev->channel == myrb_logical_channel(sdev->host)) 17428c2ecf20Sopenharmony_ci return myrb_ldev_slave_alloc(sdev); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci return myrb_pdev_slave_alloc(sdev); 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic int myrb_slave_configure(struct scsi_device *sdev) 17488c2ecf20Sopenharmony_ci{ 17498c2ecf20Sopenharmony_ci struct myrb_ldev_info *ldev_info; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci if (sdev->channel > myrb_logical_channel(sdev->host)) 17528c2ecf20Sopenharmony_ci return -ENXIO; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host)) { 17558c2ecf20Sopenharmony_ci sdev->no_uld_attach = 1; 17568c2ecf20Sopenharmony_ci return 0; 17578c2ecf20Sopenharmony_ci } 17588c2ecf20Sopenharmony_ci if (sdev->lun != 0) 17598c2ecf20Sopenharmony_ci return -ENXIO; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci ldev_info = sdev->hostdata; 17628c2ecf20Sopenharmony_ci if (!ldev_info) 17638c2ecf20Sopenharmony_ci return -ENXIO; 17648c2ecf20Sopenharmony_ci if (ldev_info->state != MYRB_DEVICE_ONLINE) 17658c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 17668c2ecf20Sopenharmony_ci "Logical drive is %s\n", 17678c2ecf20Sopenharmony_ci myrb_devstate_name(ldev_info->state)); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci sdev->tagged_supported = 1; 17708c2ecf20Sopenharmony_ci return 0; 17718c2ecf20Sopenharmony_ci} 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cistatic void myrb_slave_destroy(struct scsi_device *sdev) 17748c2ecf20Sopenharmony_ci{ 17758c2ecf20Sopenharmony_ci kfree(sdev->hostdata); 17768c2ecf20Sopenharmony_ci} 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_cistatic int myrb_biosparam(struct scsi_device *sdev, struct block_device *bdev, 17798c2ecf20Sopenharmony_ci sector_t capacity, int geom[]) 17808c2ecf20Sopenharmony_ci{ 17818c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci geom[0] = cb->ldev_geom_heads; 17848c2ecf20Sopenharmony_ci geom[1] = cb->ldev_geom_sectors; 17858c2ecf20Sopenharmony_ci geom[2] = sector_div(capacity, geom[0] * geom[1]); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci return 0; 17888c2ecf20Sopenharmony_ci} 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_cistatic ssize_t raid_state_show(struct device *dev, 17918c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 17928c2ecf20Sopenharmony_ci{ 17938c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 17948c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 17958c2ecf20Sopenharmony_ci int ret; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci if (!sdev->hostdata) 17988c2ecf20Sopenharmony_ci return snprintf(buf, 16, "Unknown\n"); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (sdev->channel == myrb_logical_channel(sdev->host)) { 18018c2ecf20Sopenharmony_ci struct myrb_ldev_info *ldev_info = sdev->hostdata; 18028c2ecf20Sopenharmony_ci const char *name; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci name = myrb_devstate_name(ldev_info->state); 18058c2ecf20Sopenharmony_ci if (name) 18068c2ecf20Sopenharmony_ci ret = snprintf(buf, 32, "%s\n", name); 18078c2ecf20Sopenharmony_ci else 18088c2ecf20Sopenharmony_ci ret = snprintf(buf, 32, "Invalid (%02X)\n", 18098c2ecf20Sopenharmony_ci ldev_info->state); 18108c2ecf20Sopenharmony_ci } else { 18118c2ecf20Sopenharmony_ci struct myrb_pdev_state *pdev_info = sdev->hostdata; 18128c2ecf20Sopenharmony_ci unsigned short status; 18138c2ecf20Sopenharmony_ci const char *name; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci status = myrb_exec_type3D(cb, MYRB_CMD_GET_DEVICE_STATE, 18168c2ecf20Sopenharmony_ci sdev, pdev_info); 18178c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) 18188c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 18198c2ecf20Sopenharmony_ci "Failed to get device state, status %x\n", 18208c2ecf20Sopenharmony_ci status); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci if (!pdev_info->present) 18238c2ecf20Sopenharmony_ci name = "Removed"; 18248c2ecf20Sopenharmony_ci else 18258c2ecf20Sopenharmony_ci name = myrb_devstate_name(pdev_info->state); 18268c2ecf20Sopenharmony_ci if (name) 18278c2ecf20Sopenharmony_ci ret = snprintf(buf, 32, "%s\n", name); 18288c2ecf20Sopenharmony_ci else 18298c2ecf20Sopenharmony_ci ret = snprintf(buf, 32, "Invalid (%02X)\n", 18308c2ecf20Sopenharmony_ci pdev_info->state); 18318c2ecf20Sopenharmony_ci } 18328c2ecf20Sopenharmony_ci return ret; 18338c2ecf20Sopenharmony_ci} 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_cistatic ssize_t raid_state_store(struct device *dev, 18368c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 18378c2ecf20Sopenharmony_ci{ 18388c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 18398c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 18408c2ecf20Sopenharmony_ci struct myrb_pdev_state *pdev_info; 18418c2ecf20Sopenharmony_ci enum myrb_devstate new_state; 18428c2ecf20Sopenharmony_ci unsigned short status; 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci if (!strncmp(buf, "kill", 4) || 18458c2ecf20Sopenharmony_ci !strncmp(buf, "offline", 7)) 18468c2ecf20Sopenharmony_ci new_state = MYRB_DEVICE_DEAD; 18478c2ecf20Sopenharmony_ci else if (!strncmp(buf, "online", 6)) 18488c2ecf20Sopenharmony_ci new_state = MYRB_DEVICE_ONLINE; 18498c2ecf20Sopenharmony_ci else if (!strncmp(buf, "standby", 7)) 18508c2ecf20Sopenharmony_ci new_state = MYRB_DEVICE_STANDBY; 18518c2ecf20Sopenharmony_ci else 18528c2ecf20Sopenharmony_ci return -EINVAL; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci pdev_info = sdev->hostdata; 18558c2ecf20Sopenharmony_ci if (!pdev_info) { 18568c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 18578c2ecf20Sopenharmony_ci "Failed - no physical device information\n"); 18588c2ecf20Sopenharmony_ci return -ENXIO; 18598c2ecf20Sopenharmony_ci } 18608c2ecf20Sopenharmony_ci if (!pdev_info->present) { 18618c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 18628c2ecf20Sopenharmony_ci "Failed - device not present\n"); 18638c2ecf20Sopenharmony_ci return -ENXIO; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci if (pdev_info->state == new_state) 18678c2ecf20Sopenharmony_ci return count; 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci status = myrb_set_pdev_state(cb, sdev, new_state); 18708c2ecf20Sopenharmony_ci switch (status) { 18718c2ecf20Sopenharmony_ci case MYRB_STATUS_SUCCESS: 18728c2ecf20Sopenharmony_ci break; 18738c2ecf20Sopenharmony_ci case MYRB_STATUS_START_DEVICE_FAILED: 18748c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 18758c2ecf20Sopenharmony_ci "Failed - Unable to Start Device\n"); 18768c2ecf20Sopenharmony_ci count = -EAGAIN; 18778c2ecf20Sopenharmony_ci break; 18788c2ecf20Sopenharmony_ci case MYRB_STATUS_NO_DEVICE: 18798c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 18808c2ecf20Sopenharmony_ci "Failed - No Device at Address\n"); 18818c2ecf20Sopenharmony_ci count = -ENODEV; 18828c2ecf20Sopenharmony_ci break; 18838c2ecf20Sopenharmony_ci case MYRB_STATUS_INVALID_CHANNEL_OR_TARGET: 18848c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 18858c2ecf20Sopenharmony_ci "Failed - Invalid Channel or Target or Modifier\n"); 18868c2ecf20Sopenharmony_ci count = -EINVAL; 18878c2ecf20Sopenharmony_ci break; 18888c2ecf20Sopenharmony_ci case MYRB_STATUS_CHANNEL_BUSY: 18898c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 18908c2ecf20Sopenharmony_ci "Failed - Channel Busy\n"); 18918c2ecf20Sopenharmony_ci count = -EBUSY; 18928c2ecf20Sopenharmony_ci break; 18938c2ecf20Sopenharmony_ci default: 18948c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 18958c2ecf20Sopenharmony_ci "Failed - Unexpected Status %04X\n", status); 18968c2ecf20Sopenharmony_ci count = -EIO; 18978c2ecf20Sopenharmony_ci break; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci return count; 19008c2ecf20Sopenharmony_ci} 19018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(raid_state); 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_cistatic ssize_t raid_level_show(struct device *dev, 19048c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 19058c2ecf20Sopenharmony_ci{ 19068c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci if (sdev->channel == myrb_logical_channel(sdev->host)) { 19098c2ecf20Sopenharmony_ci struct myrb_ldev_info *ldev_info = sdev->hostdata; 19108c2ecf20Sopenharmony_ci const char *name; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci if (!ldev_info) 19138c2ecf20Sopenharmony_ci return -ENXIO; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci name = myrb_raidlevel_name(ldev_info->raid_level); 19168c2ecf20Sopenharmony_ci if (!name) 19178c2ecf20Sopenharmony_ci return snprintf(buf, 32, "Invalid (%02X)\n", 19188c2ecf20Sopenharmony_ci ldev_info->state); 19198c2ecf20Sopenharmony_ci return snprintf(buf, 32, "%s\n", name); 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci return snprintf(buf, 32, "Physical Drive\n"); 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(raid_level); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_cistatic ssize_t rebuild_show(struct device *dev, 19268c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 19278c2ecf20Sopenharmony_ci{ 19288c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 19298c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 19308c2ecf20Sopenharmony_ci struct myrb_rbld_progress rbld_buf; 19318c2ecf20Sopenharmony_ci unsigned char status; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host)) 19348c2ecf20Sopenharmony_ci return snprintf(buf, 32, "physical device - not rebuilding\n"); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci status = myrb_get_rbld_progress(cb, &rbld_buf); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci if (rbld_buf.ldev_num != sdev->id || 19398c2ecf20Sopenharmony_ci status != MYRB_STATUS_SUCCESS) 19408c2ecf20Sopenharmony_ci return snprintf(buf, 32, "not rebuilding\n"); 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci return snprintf(buf, 32, "rebuilding block %u of %u\n", 19438c2ecf20Sopenharmony_ci rbld_buf.ldev_size - rbld_buf.blocks_left, 19448c2ecf20Sopenharmony_ci rbld_buf.ldev_size); 19458c2ecf20Sopenharmony_ci} 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_cistatic ssize_t rebuild_store(struct device *dev, 19488c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 19498c2ecf20Sopenharmony_ci{ 19508c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 19518c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 19528c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk; 19538c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox; 19548c2ecf20Sopenharmony_ci unsigned short status; 19558c2ecf20Sopenharmony_ci int rc, start; 19568c2ecf20Sopenharmony_ci const char *msg; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci rc = kstrtoint(buf, 0, &start); 19598c2ecf20Sopenharmony_ci if (rc) 19608c2ecf20Sopenharmony_ci return rc; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci if (sdev->channel >= myrb_logical_channel(sdev->host)) 19638c2ecf20Sopenharmony_ci return -ENXIO; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci status = myrb_get_rbld_progress(cb, NULL); 19668c2ecf20Sopenharmony_ci if (start) { 19678c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 19688c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 19698c2ecf20Sopenharmony_ci "Rebuild Not Initiated; already in progress\n"); 19708c2ecf20Sopenharmony_ci return -EALREADY; 19718c2ecf20Sopenharmony_ci } 19728c2ecf20Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 19738c2ecf20Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 19748c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 19758c2ecf20Sopenharmony_ci mbox = &cmd_blk->mbox; 19768c2ecf20Sopenharmony_ci mbox->type3D.opcode = MYRB_CMD_REBUILD_ASYNC; 19778c2ecf20Sopenharmony_ci mbox->type3D.id = MYRB_DCMD_TAG; 19788c2ecf20Sopenharmony_ci mbox->type3D.channel = sdev->channel; 19798c2ecf20Sopenharmony_ci mbox->type3D.target = sdev->id; 19808c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 19818c2ecf20Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 19828c2ecf20Sopenharmony_ci } else { 19838c2ecf20Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 19848c2ecf20Sopenharmony_ci unsigned char *rate; 19858c2ecf20Sopenharmony_ci dma_addr_t rate_addr; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci if (status != MYRB_STATUS_SUCCESS) { 19888c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 19898c2ecf20Sopenharmony_ci "Rebuild Not Cancelled; not in progress\n"); 19908c2ecf20Sopenharmony_ci return 0; 19918c2ecf20Sopenharmony_ci } 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci rate = dma_alloc_coherent(&pdev->dev, sizeof(char), 19948c2ecf20Sopenharmony_ci &rate_addr, GFP_KERNEL); 19958c2ecf20Sopenharmony_ci if (rate == NULL) { 19968c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 19978c2ecf20Sopenharmony_ci "Cancellation of Rebuild Failed - Out of Memory\n"); 19988c2ecf20Sopenharmony_ci return -ENOMEM; 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 20018c2ecf20Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 20028c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 20038c2ecf20Sopenharmony_ci mbox = &cmd_blk->mbox; 20048c2ecf20Sopenharmony_ci mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL; 20058c2ecf20Sopenharmony_ci mbox->type3R.id = MYRB_DCMD_TAG; 20068c2ecf20Sopenharmony_ci mbox->type3R.rbld_rate = 0xFF; 20078c2ecf20Sopenharmony_ci mbox->type3R.addr = rate_addr; 20088c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 20098c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(char), rate, rate_addr); 20108c2ecf20Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 20138c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Rebuild %s\n", 20148c2ecf20Sopenharmony_ci start ? "Initiated" : "Cancelled"); 20158c2ecf20Sopenharmony_ci return count; 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci if (!start) { 20188c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 20198c2ecf20Sopenharmony_ci "Rebuild Not Cancelled, status 0x%x\n", 20208c2ecf20Sopenharmony_ci status); 20218c2ecf20Sopenharmony_ci return -EIO; 20228c2ecf20Sopenharmony_ci } 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci switch (status) { 20258c2ecf20Sopenharmony_ci case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE: 20268c2ecf20Sopenharmony_ci msg = "Attempt to Rebuild Online or Unresponsive Drive"; 20278c2ecf20Sopenharmony_ci break; 20288c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_NEW_DISK_FAILED: 20298c2ecf20Sopenharmony_ci msg = "New Disk Failed During Rebuild"; 20308c2ecf20Sopenharmony_ci break; 20318c2ecf20Sopenharmony_ci case MYRB_STATUS_INVALID_ADDRESS: 20328c2ecf20Sopenharmony_ci msg = "Invalid Device Address"; 20338c2ecf20Sopenharmony_ci break; 20348c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS: 20358c2ecf20Sopenharmony_ci msg = "Already in Progress"; 20368c2ecf20Sopenharmony_ci break; 20378c2ecf20Sopenharmony_ci default: 20388c2ecf20Sopenharmony_ci msg = NULL; 20398c2ecf20Sopenharmony_ci break; 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci if (msg) 20428c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 20438c2ecf20Sopenharmony_ci "Rebuild Failed - %s\n", msg); 20448c2ecf20Sopenharmony_ci else 20458c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 20468c2ecf20Sopenharmony_ci "Rebuild Failed, status 0x%x\n", status); 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci return -EIO; 20498c2ecf20Sopenharmony_ci} 20508c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(rebuild); 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cistatic ssize_t consistency_check_store(struct device *dev, 20538c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 20548c2ecf20Sopenharmony_ci{ 20558c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 20568c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 20578c2ecf20Sopenharmony_ci struct myrb_rbld_progress rbld_buf; 20588c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk; 20598c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox; 20608c2ecf20Sopenharmony_ci unsigned short ldev_num = 0xFFFF; 20618c2ecf20Sopenharmony_ci unsigned short status; 20628c2ecf20Sopenharmony_ci int rc, start; 20638c2ecf20Sopenharmony_ci const char *msg; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci rc = kstrtoint(buf, 0, &start); 20668c2ecf20Sopenharmony_ci if (rc) 20678c2ecf20Sopenharmony_ci return rc; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host)) 20708c2ecf20Sopenharmony_ci return -ENXIO; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci status = myrb_get_rbld_progress(cb, &rbld_buf); 20738c2ecf20Sopenharmony_ci if (start) { 20748c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 20758c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 20768c2ecf20Sopenharmony_ci "Check Consistency Not Initiated; already in progress\n"); 20778c2ecf20Sopenharmony_ci return -EALREADY; 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 20808c2ecf20Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 20818c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 20828c2ecf20Sopenharmony_ci mbox = &cmd_blk->mbox; 20838c2ecf20Sopenharmony_ci mbox->type3C.opcode = MYRB_CMD_CHECK_CONSISTENCY_ASYNC; 20848c2ecf20Sopenharmony_ci mbox->type3C.id = MYRB_DCMD_TAG; 20858c2ecf20Sopenharmony_ci mbox->type3C.ldev_num = sdev->id; 20868c2ecf20Sopenharmony_ci mbox->type3C.auto_restore = true; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 20898c2ecf20Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 20908c2ecf20Sopenharmony_ci } else { 20918c2ecf20Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 20928c2ecf20Sopenharmony_ci unsigned char *rate; 20938c2ecf20Sopenharmony_ci dma_addr_t rate_addr; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci if (ldev_num != sdev->id) { 20968c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 20978c2ecf20Sopenharmony_ci "Check Consistency Not Cancelled; not in progress\n"); 20988c2ecf20Sopenharmony_ci return 0; 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci rate = dma_alloc_coherent(&pdev->dev, sizeof(char), 21018c2ecf20Sopenharmony_ci &rate_addr, GFP_KERNEL); 21028c2ecf20Sopenharmony_ci if (rate == NULL) { 21038c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 21048c2ecf20Sopenharmony_ci "Cancellation of Check Consistency Failed - Out of Memory\n"); 21058c2ecf20Sopenharmony_ci return -ENOMEM; 21068c2ecf20Sopenharmony_ci } 21078c2ecf20Sopenharmony_ci mutex_lock(&cb->dcmd_mutex); 21088c2ecf20Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 21098c2ecf20Sopenharmony_ci myrb_reset_cmd(cmd_blk); 21108c2ecf20Sopenharmony_ci mbox = &cmd_blk->mbox; 21118c2ecf20Sopenharmony_ci mbox->type3R.opcode = MYRB_CMD_REBUILD_CONTROL; 21128c2ecf20Sopenharmony_ci mbox->type3R.id = MYRB_DCMD_TAG; 21138c2ecf20Sopenharmony_ci mbox->type3R.rbld_rate = 0xFF; 21148c2ecf20Sopenharmony_ci mbox->type3R.addr = rate_addr; 21158c2ecf20Sopenharmony_ci status = myrb_exec_cmd(cb, cmd_blk); 21168c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(char), rate, rate_addr); 21178c2ecf20Sopenharmony_ci mutex_unlock(&cb->dcmd_mutex); 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 21208c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Check Consistency %s\n", 21218c2ecf20Sopenharmony_ci start ? "Initiated" : "Cancelled"); 21228c2ecf20Sopenharmony_ci return count; 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci if (!start) { 21258c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 21268c2ecf20Sopenharmony_ci "Check Consistency Not Cancelled, status 0x%x\n", 21278c2ecf20Sopenharmony_ci status); 21288c2ecf20Sopenharmony_ci return -EIO; 21298c2ecf20Sopenharmony_ci } 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci switch (status) { 21328c2ecf20Sopenharmony_ci case MYRB_STATUS_ATTEMPT_TO_RBLD_ONLINE_DRIVE: 21338c2ecf20Sopenharmony_ci msg = "Dependent Physical Device is DEAD"; 21348c2ecf20Sopenharmony_ci break; 21358c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_NEW_DISK_FAILED: 21368c2ecf20Sopenharmony_ci msg = "New Disk Failed During Rebuild"; 21378c2ecf20Sopenharmony_ci break; 21388c2ecf20Sopenharmony_ci case MYRB_STATUS_INVALID_ADDRESS: 21398c2ecf20Sopenharmony_ci msg = "Invalid or Nonredundant Logical Drive"; 21408c2ecf20Sopenharmony_ci break; 21418c2ecf20Sopenharmony_ci case MYRB_STATUS_RBLD_OR_CHECK_INPROGRESS: 21428c2ecf20Sopenharmony_ci msg = "Already in Progress"; 21438c2ecf20Sopenharmony_ci break; 21448c2ecf20Sopenharmony_ci default: 21458c2ecf20Sopenharmony_ci msg = NULL; 21468c2ecf20Sopenharmony_ci break; 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci if (msg) 21498c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 21508c2ecf20Sopenharmony_ci "Check Consistency Failed - %s\n", msg); 21518c2ecf20Sopenharmony_ci else 21528c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 21538c2ecf20Sopenharmony_ci "Check Consistency Failed, status 0x%x\n", status); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci return -EIO; 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic ssize_t consistency_check_show(struct device *dev, 21598c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 21608c2ecf20Sopenharmony_ci{ 21618c2ecf20Sopenharmony_ci return rebuild_show(dev, attr, buf); 21628c2ecf20Sopenharmony_ci} 21638c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(consistency_check); 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_cistatic ssize_t ctlr_num_show(struct device *dev, 21668c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 21678c2ecf20Sopenharmony_ci{ 21688c2ecf20Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 21698c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci return snprintf(buf, 20, "%u\n", cb->ctlr_num); 21728c2ecf20Sopenharmony_ci} 21738c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(ctlr_num); 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_cistatic ssize_t firmware_show(struct device *dev, 21768c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 21778c2ecf20Sopenharmony_ci{ 21788c2ecf20Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 21798c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci return snprintf(buf, 16, "%s\n", cb->fw_version); 21828c2ecf20Sopenharmony_ci} 21838c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(firmware); 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_cistatic ssize_t model_show(struct device *dev, 21868c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 21878c2ecf20Sopenharmony_ci{ 21888c2ecf20Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 21898c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci return snprintf(buf, 16, "%s\n", cb->model_name); 21928c2ecf20Sopenharmony_ci} 21938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(model); 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_cistatic ssize_t flush_cache_store(struct device *dev, 21968c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 21978c2ecf20Sopenharmony_ci{ 21988c2ecf20Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 21998c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(shost); 22008c2ecf20Sopenharmony_ci unsigned short status; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci status = myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0); 22038c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 22048c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, shost, 22058c2ecf20Sopenharmony_ci "Cache Flush Completed\n"); 22068c2ecf20Sopenharmony_ci return count; 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, shost, 22098c2ecf20Sopenharmony_ci "Cache Flush Failed, status %x\n", status); 22108c2ecf20Sopenharmony_ci return -EIO; 22118c2ecf20Sopenharmony_ci} 22128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(flush_cache); 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_cistatic struct device_attribute *myrb_sdev_attrs[] = { 22158c2ecf20Sopenharmony_ci &dev_attr_rebuild, 22168c2ecf20Sopenharmony_ci &dev_attr_consistency_check, 22178c2ecf20Sopenharmony_ci &dev_attr_raid_state, 22188c2ecf20Sopenharmony_ci &dev_attr_raid_level, 22198c2ecf20Sopenharmony_ci NULL, 22208c2ecf20Sopenharmony_ci}; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_cistatic struct device_attribute *myrb_shost_attrs[] = { 22238c2ecf20Sopenharmony_ci &dev_attr_ctlr_num, 22248c2ecf20Sopenharmony_ci &dev_attr_model, 22258c2ecf20Sopenharmony_ci &dev_attr_firmware, 22268c2ecf20Sopenharmony_ci &dev_attr_flush_cache, 22278c2ecf20Sopenharmony_ci NULL, 22288c2ecf20Sopenharmony_ci}; 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_cistatic struct scsi_host_template myrb_template = { 22318c2ecf20Sopenharmony_ci .module = THIS_MODULE, 22328c2ecf20Sopenharmony_ci .name = "DAC960", 22338c2ecf20Sopenharmony_ci .proc_name = "myrb", 22348c2ecf20Sopenharmony_ci .queuecommand = myrb_queuecommand, 22358c2ecf20Sopenharmony_ci .eh_host_reset_handler = myrb_host_reset, 22368c2ecf20Sopenharmony_ci .slave_alloc = myrb_slave_alloc, 22378c2ecf20Sopenharmony_ci .slave_configure = myrb_slave_configure, 22388c2ecf20Sopenharmony_ci .slave_destroy = myrb_slave_destroy, 22398c2ecf20Sopenharmony_ci .bios_param = myrb_biosparam, 22408c2ecf20Sopenharmony_ci .cmd_size = sizeof(struct myrb_cmdblk), 22418c2ecf20Sopenharmony_ci .shost_attrs = myrb_shost_attrs, 22428c2ecf20Sopenharmony_ci .sdev_attrs = myrb_sdev_attrs, 22438c2ecf20Sopenharmony_ci .this_id = -1, 22448c2ecf20Sopenharmony_ci}; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci/** 22478c2ecf20Sopenharmony_ci * myrb_is_raid - return boolean indicating device is raid volume 22488c2ecf20Sopenharmony_ci * @dev the device struct object 22498c2ecf20Sopenharmony_ci */ 22508c2ecf20Sopenharmony_cistatic int myrb_is_raid(struct device *dev) 22518c2ecf20Sopenharmony_ci{ 22528c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci return sdev->channel == myrb_logical_channel(sdev->host); 22558c2ecf20Sopenharmony_ci} 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci/** 22588c2ecf20Sopenharmony_ci * myrb_get_resync - get raid volume resync percent complete 22598c2ecf20Sopenharmony_ci * @dev the device struct object 22608c2ecf20Sopenharmony_ci */ 22618c2ecf20Sopenharmony_cistatic void myrb_get_resync(struct device *dev) 22628c2ecf20Sopenharmony_ci{ 22638c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 22648c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 22658c2ecf20Sopenharmony_ci struct myrb_rbld_progress rbld_buf; 22668c2ecf20Sopenharmony_ci unsigned int percent_complete = 0; 22678c2ecf20Sopenharmony_ci unsigned short status; 22688c2ecf20Sopenharmony_ci unsigned int ldev_size = 0, remaining = 0; 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host)) 22718c2ecf20Sopenharmony_ci return; 22728c2ecf20Sopenharmony_ci status = myrb_get_rbld_progress(cb, &rbld_buf); 22738c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) { 22748c2ecf20Sopenharmony_ci if (rbld_buf.ldev_num == sdev->id) { 22758c2ecf20Sopenharmony_ci ldev_size = rbld_buf.ldev_size; 22768c2ecf20Sopenharmony_ci remaining = rbld_buf.blocks_left; 22778c2ecf20Sopenharmony_ci } 22788c2ecf20Sopenharmony_ci } 22798c2ecf20Sopenharmony_ci if (remaining && ldev_size) 22808c2ecf20Sopenharmony_ci percent_complete = (ldev_size - remaining) * 100 / ldev_size; 22818c2ecf20Sopenharmony_ci raid_set_resync(myrb_raid_template, dev, percent_complete); 22828c2ecf20Sopenharmony_ci} 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci/** 22858c2ecf20Sopenharmony_ci * myrb_get_state - get raid volume status 22868c2ecf20Sopenharmony_ci * @dev the device struct object 22878c2ecf20Sopenharmony_ci */ 22888c2ecf20Sopenharmony_cistatic void myrb_get_state(struct device *dev) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 22918c2ecf20Sopenharmony_ci struct myrb_hba *cb = shost_priv(sdev->host); 22928c2ecf20Sopenharmony_ci struct myrb_ldev_info *ldev_info = sdev->hostdata; 22938c2ecf20Sopenharmony_ci enum raid_state state = RAID_STATE_UNKNOWN; 22948c2ecf20Sopenharmony_ci unsigned short status; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci if (sdev->channel < myrb_logical_channel(sdev->host) || !ldev_info) 22978c2ecf20Sopenharmony_ci state = RAID_STATE_UNKNOWN; 22988c2ecf20Sopenharmony_ci else { 22998c2ecf20Sopenharmony_ci status = myrb_get_rbld_progress(cb, NULL); 23008c2ecf20Sopenharmony_ci if (status == MYRB_STATUS_SUCCESS) 23018c2ecf20Sopenharmony_ci state = RAID_STATE_RESYNCING; 23028c2ecf20Sopenharmony_ci else { 23038c2ecf20Sopenharmony_ci switch (ldev_info->state) { 23048c2ecf20Sopenharmony_ci case MYRB_DEVICE_ONLINE: 23058c2ecf20Sopenharmony_ci state = RAID_STATE_ACTIVE; 23068c2ecf20Sopenharmony_ci break; 23078c2ecf20Sopenharmony_ci case MYRB_DEVICE_WO: 23088c2ecf20Sopenharmony_ci case MYRB_DEVICE_CRITICAL: 23098c2ecf20Sopenharmony_ci state = RAID_STATE_DEGRADED; 23108c2ecf20Sopenharmony_ci break; 23118c2ecf20Sopenharmony_ci default: 23128c2ecf20Sopenharmony_ci state = RAID_STATE_OFFLINE; 23138c2ecf20Sopenharmony_ci } 23148c2ecf20Sopenharmony_ci } 23158c2ecf20Sopenharmony_ci } 23168c2ecf20Sopenharmony_ci raid_set_state(myrb_raid_template, dev, state); 23178c2ecf20Sopenharmony_ci} 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_cistatic struct raid_function_template myrb_raid_functions = { 23208c2ecf20Sopenharmony_ci .cookie = &myrb_template, 23218c2ecf20Sopenharmony_ci .is_raid = myrb_is_raid, 23228c2ecf20Sopenharmony_ci .get_resync = myrb_get_resync, 23238c2ecf20Sopenharmony_ci .get_state = myrb_get_state, 23248c2ecf20Sopenharmony_ci}; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_cistatic void myrb_handle_scsi(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk, 23278c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd) 23288c2ecf20Sopenharmony_ci{ 23298c2ecf20Sopenharmony_ci unsigned short status; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (!cmd_blk) 23328c2ecf20Sopenharmony_ci return; 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci scsi_dma_unmap(scmd); 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci if (cmd_blk->dcdb) { 23378c2ecf20Sopenharmony_ci memcpy(scmd->sense_buffer, &cmd_blk->dcdb->sense, 64); 23388c2ecf20Sopenharmony_ci dma_pool_free(cb->dcdb_pool, cmd_blk->dcdb, 23398c2ecf20Sopenharmony_ci cmd_blk->dcdb_addr); 23408c2ecf20Sopenharmony_ci cmd_blk->dcdb = NULL; 23418c2ecf20Sopenharmony_ci } 23428c2ecf20Sopenharmony_ci if (cmd_blk->sgl) { 23438c2ecf20Sopenharmony_ci dma_pool_free(cb->sg_pool, cmd_blk->sgl, cmd_blk->sgl_addr); 23448c2ecf20Sopenharmony_ci cmd_blk->sgl = NULL; 23458c2ecf20Sopenharmony_ci cmd_blk->sgl_addr = 0; 23468c2ecf20Sopenharmony_ci } 23478c2ecf20Sopenharmony_ci status = cmd_blk->status; 23488c2ecf20Sopenharmony_ci switch (status) { 23498c2ecf20Sopenharmony_ci case MYRB_STATUS_SUCCESS: 23508c2ecf20Sopenharmony_ci case MYRB_STATUS_DEVICE_BUSY: 23518c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16) | status; 23528c2ecf20Sopenharmony_ci break; 23538c2ecf20Sopenharmony_ci case MYRB_STATUS_BAD_DATA: 23548c2ecf20Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, 23558c2ecf20Sopenharmony_ci "Bad Data Encountered\n"); 23568c2ecf20Sopenharmony_ci if (scmd->sc_data_direction == DMA_FROM_DEVICE) 23578c2ecf20Sopenharmony_ci /* Unrecovered read error */ 23588c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 23598c2ecf20Sopenharmony_ci MEDIUM_ERROR, 0x11, 0); 23608c2ecf20Sopenharmony_ci else 23618c2ecf20Sopenharmony_ci /* Write error */ 23628c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 23638c2ecf20Sopenharmony_ci MEDIUM_ERROR, 0x0C, 0); 23648c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION; 23658c2ecf20Sopenharmony_ci break; 23668c2ecf20Sopenharmony_ci case MYRB_STATUS_IRRECOVERABLE_DATA_ERROR: 23678c2ecf20Sopenharmony_ci scmd_printk(KERN_ERR, scmd, "Irrecoverable Data Error\n"); 23688c2ecf20Sopenharmony_ci if (scmd->sc_data_direction == DMA_FROM_DEVICE) 23698c2ecf20Sopenharmony_ci /* Unrecovered read error, auto-reallocation failed */ 23708c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 23718c2ecf20Sopenharmony_ci MEDIUM_ERROR, 0x11, 0x04); 23728c2ecf20Sopenharmony_ci else 23738c2ecf20Sopenharmony_ci /* Write error, auto-reallocation failed */ 23748c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 23758c2ecf20Sopenharmony_ci MEDIUM_ERROR, 0x0C, 0x02); 23768c2ecf20Sopenharmony_ci scmd->result = (DID_OK << 16) | SAM_STAT_CHECK_CONDITION; 23778c2ecf20Sopenharmony_ci break; 23788c2ecf20Sopenharmony_ci case MYRB_STATUS_LDRV_NONEXISTENT_OR_OFFLINE: 23798c2ecf20Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, 23808c2ecf20Sopenharmony_ci "Logical Drive Nonexistent or Offline"); 23818c2ecf20Sopenharmony_ci scmd->result = (DID_BAD_TARGET << 16); 23828c2ecf20Sopenharmony_ci break; 23838c2ecf20Sopenharmony_ci case MYRB_STATUS_ACCESS_BEYOND_END_OF_LDRV: 23848c2ecf20Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, 23858c2ecf20Sopenharmony_ci "Attempt to Access Beyond End of Logical Drive"); 23868c2ecf20Sopenharmony_ci /* Logical block address out of range */ 23878c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, scmd->sense_buffer, 23888c2ecf20Sopenharmony_ci NOT_READY, 0x21, 0); 23898c2ecf20Sopenharmony_ci break; 23908c2ecf20Sopenharmony_ci case MYRB_STATUS_DEVICE_NONRESPONSIVE: 23918c2ecf20Sopenharmony_ci dev_dbg(&scmd->device->sdev_gendev, "Device nonresponsive\n"); 23928c2ecf20Sopenharmony_ci scmd->result = (DID_BAD_TARGET << 16); 23938c2ecf20Sopenharmony_ci break; 23948c2ecf20Sopenharmony_ci default: 23958c2ecf20Sopenharmony_ci scmd_printk(KERN_ERR, scmd, 23968c2ecf20Sopenharmony_ci "Unexpected Error Status %04X", status); 23978c2ecf20Sopenharmony_ci scmd->result = (DID_ERROR << 16); 23988c2ecf20Sopenharmony_ci break; 23998c2ecf20Sopenharmony_ci } 24008c2ecf20Sopenharmony_ci scmd->scsi_done(scmd); 24018c2ecf20Sopenharmony_ci} 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_cistatic void myrb_handle_cmdblk(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) 24048c2ecf20Sopenharmony_ci{ 24058c2ecf20Sopenharmony_ci if (!cmd_blk) 24068c2ecf20Sopenharmony_ci return; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci if (cmd_blk->completion) { 24098c2ecf20Sopenharmony_ci complete(cmd_blk->completion); 24108c2ecf20Sopenharmony_ci cmd_blk->completion = NULL; 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci} 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_cistatic void myrb_monitor(struct work_struct *work) 24158c2ecf20Sopenharmony_ci{ 24168c2ecf20Sopenharmony_ci struct myrb_hba *cb = container_of(work, 24178c2ecf20Sopenharmony_ci struct myrb_hba, monitor_work.work); 24188c2ecf20Sopenharmony_ci struct Scsi_Host *shost = cb->host; 24198c2ecf20Sopenharmony_ci unsigned long interval = MYRB_PRIMARY_MONITOR_INTERVAL; 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, "monitor tick\n"); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci if (cb->new_ev_seq > cb->old_ev_seq) { 24248c2ecf20Sopenharmony_ci int event = cb->old_ev_seq; 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, 24278c2ecf20Sopenharmony_ci "get event log no %d/%d\n", 24288c2ecf20Sopenharmony_ci cb->new_ev_seq, event); 24298c2ecf20Sopenharmony_ci myrb_get_event(cb, event); 24308c2ecf20Sopenharmony_ci cb->old_ev_seq = event + 1; 24318c2ecf20Sopenharmony_ci interval = 10; 24328c2ecf20Sopenharmony_ci } else if (cb->need_err_info) { 24338c2ecf20Sopenharmony_ci cb->need_err_info = false; 24348c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, "get error table\n"); 24358c2ecf20Sopenharmony_ci myrb_get_errtable(cb); 24368c2ecf20Sopenharmony_ci interval = 10; 24378c2ecf20Sopenharmony_ci } else if (cb->need_rbld && cb->rbld_first) { 24388c2ecf20Sopenharmony_ci cb->need_rbld = false; 24398c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, 24408c2ecf20Sopenharmony_ci "get rebuild progress\n"); 24418c2ecf20Sopenharmony_ci myrb_update_rbld_progress(cb); 24428c2ecf20Sopenharmony_ci interval = 10; 24438c2ecf20Sopenharmony_ci } else if (cb->need_ldev_info) { 24448c2ecf20Sopenharmony_ci cb->need_ldev_info = false; 24458c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, 24468c2ecf20Sopenharmony_ci "get logical drive info\n"); 24478c2ecf20Sopenharmony_ci myrb_get_ldev_info(cb); 24488c2ecf20Sopenharmony_ci interval = 10; 24498c2ecf20Sopenharmony_ci } else if (cb->need_rbld) { 24508c2ecf20Sopenharmony_ci cb->need_rbld = false; 24518c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, 24528c2ecf20Sopenharmony_ci "get rebuild progress\n"); 24538c2ecf20Sopenharmony_ci myrb_update_rbld_progress(cb); 24548c2ecf20Sopenharmony_ci interval = 10; 24558c2ecf20Sopenharmony_ci } else if (cb->need_cc_status) { 24568c2ecf20Sopenharmony_ci cb->need_cc_status = false; 24578c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, 24588c2ecf20Sopenharmony_ci "get consistency check progress\n"); 24598c2ecf20Sopenharmony_ci myrb_get_cc_progress(cb); 24608c2ecf20Sopenharmony_ci interval = 10; 24618c2ecf20Sopenharmony_ci } else if (cb->need_bgi_status) { 24628c2ecf20Sopenharmony_ci cb->need_bgi_status = false; 24638c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, "get background init status\n"); 24648c2ecf20Sopenharmony_ci myrb_bgi_control(cb); 24658c2ecf20Sopenharmony_ci interval = 10; 24668c2ecf20Sopenharmony_ci } else { 24678c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, "new enquiry\n"); 24688c2ecf20Sopenharmony_ci mutex_lock(&cb->dma_mutex); 24698c2ecf20Sopenharmony_ci myrb_hba_enquiry(cb); 24708c2ecf20Sopenharmony_ci mutex_unlock(&cb->dma_mutex); 24718c2ecf20Sopenharmony_ci if ((cb->new_ev_seq - cb->old_ev_seq > 0) || 24728c2ecf20Sopenharmony_ci cb->need_err_info || cb->need_rbld || 24738c2ecf20Sopenharmony_ci cb->need_ldev_info || cb->need_cc_status || 24748c2ecf20Sopenharmony_ci cb->need_bgi_status) { 24758c2ecf20Sopenharmony_ci dev_dbg(&shost->shost_gendev, 24768c2ecf20Sopenharmony_ci "reschedule monitor\n"); 24778c2ecf20Sopenharmony_ci interval = 0; 24788c2ecf20Sopenharmony_ci } 24798c2ecf20Sopenharmony_ci } 24808c2ecf20Sopenharmony_ci if (interval > 1) 24818c2ecf20Sopenharmony_ci cb->primary_monitor_time = jiffies; 24828c2ecf20Sopenharmony_ci queue_delayed_work(cb->work_q, &cb->monitor_work, interval); 24838c2ecf20Sopenharmony_ci} 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci/** 24868c2ecf20Sopenharmony_ci * myrb_err_status - reports controller BIOS messages 24878c2ecf20Sopenharmony_ci * 24888c2ecf20Sopenharmony_ci * Controller BIOS messages are passed through the Error Status Register 24898c2ecf20Sopenharmony_ci * when the driver performs the BIOS handshaking. 24908c2ecf20Sopenharmony_ci * 24918c2ecf20Sopenharmony_ci * Return: true for fatal errors and false otherwise. 24928c2ecf20Sopenharmony_ci */ 24938c2ecf20Sopenharmony_cistatic bool myrb_err_status(struct myrb_hba *cb, unsigned char error, 24948c2ecf20Sopenharmony_ci unsigned char parm0, unsigned char parm1) 24958c2ecf20Sopenharmony_ci{ 24968c2ecf20Sopenharmony_ci struct pci_dev *pdev = cb->pdev; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci switch (error) { 24998c2ecf20Sopenharmony_ci case 0x00: 25008c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 25018c2ecf20Sopenharmony_ci "Physical Device %d:%d Not Responding\n", 25028c2ecf20Sopenharmony_ci parm1, parm0); 25038c2ecf20Sopenharmony_ci break; 25048c2ecf20Sopenharmony_ci case 0x08: 25058c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Spinning Up Drives\n"); 25068c2ecf20Sopenharmony_ci break; 25078c2ecf20Sopenharmony_ci case 0x30: 25088c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Configuration Checksum Error\n"); 25098c2ecf20Sopenharmony_ci break; 25108c2ecf20Sopenharmony_ci case 0x60: 25118c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Mirror Race Recovery Failed\n"); 25128c2ecf20Sopenharmony_ci break; 25138c2ecf20Sopenharmony_ci case 0x70: 25148c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Mirror Race Recovery In Progress\n"); 25158c2ecf20Sopenharmony_ci break; 25168c2ecf20Sopenharmony_ci case 0x90: 25178c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Physical Device %d:%d COD Mismatch\n", 25188c2ecf20Sopenharmony_ci parm1, parm0); 25198c2ecf20Sopenharmony_ci break; 25208c2ecf20Sopenharmony_ci case 0xA0: 25218c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Logical Drive Installation Aborted\n"); 25228c2ecf20Sopenharmony_ci break; 25238c2ecf20Sopenharmony_ci case 0xB0: 25248c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "Mirror Race On A Critical Logical Drive\n"); 25258c2ecf20Sopenharmony_ci break; 25268c2ecf20Sopenharmony_ci case 0xD0: 25278c2ecf20Sopenharmony_ci dev_notice(&pdev->dev, "New Controller Configuration Found\n"); 25288c2ecf20Sopenharmony_ci break; 25298c2ecf20Sopenharmony_ci case 0xF0: 25308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Fatal Memory Parity Error\n"); 25318c2ecf20Sopenharmony_ci return true; 25328c2ecf20Sopenharmony_ci default: 25338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unknown Initialization Error %02X\n", 25348c2ecf20Sopenharmony_ci error); 25358c2ecf20Sopenharmony_ci return true; 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci return false; 25388c2ecf20Sopenharmony_ci} 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci/* 25418c2ecf20Sopenharmony_ci * Hardware-specific functions 25428c2ecf20Sopenharmony_ci */ 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci/* 25458c2ecf20Sopenharmony_ci * DAC960 LA Series Controllers 25468c2ecf20Sopenharmony_ci */ 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_cistatic inline void DAC960_LA_hw_mbox_new_cmd(void __iomem *base) 25498c2ecf20Sopenharmony_ci{ 25508c2ecf20Sopenharmony_ci writeb(DAC960_LA_IDB_HWMBOX_NEW_CMD, base + DAC960_LA_IDB_OFFSET); 25518c2ecf20Sopenharmony_ci} 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_cistatic inline void DAC960_LA_ack_hw_mbox_status(void __iomem *base) 25548c2ecf20Sopenharmony_ci{ 25558c2ecf20Sopenharmony_ci writeb(DAC960_LA_IDB_HWMBOX_ACK_STS, base + DAC960_LA_IDB_OFFSET); 25568c2ecf20Sopenharmony_ci} 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_cistatic inline void DAC960_LA_gen_intr(void __iomem *base) 25598c2ecf20Sopenharmony_ci{ 25608c2ecf20Sopenharmony_ci writeb(DAC960_LA_IDB_GEN_IRQ, base + DAC960_LA_IDB_OFFSET); 25618c2ecf20Sopenharmony_ci} 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_cistatic inline void DAC960_LA_reset_ctrl(void __iomem *base) 25648c2ecf20Sopenharmony_ci{ 25658c2ecf20Sopenharmony_ci writeb(DAC960_LA_IDB_CTRL_RESET, base + DAC960_LA_IDB_OFFSET); 25668c2ecf20Sopenharmony_ci} 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_cistatic inline void DAC960_LA_mem_mbox_new_cmd(void __iomem *base) 25698c2ecf20Sopenharmony_ci{ 25708c2ecf20Sopenharmony_ci writeb(DAC960_LA_IDB_MMBOX_NEW_CMD, base + DAC960_LA_IDB_OFFSET); 25718c2ecf20Sopenharmony_ci} 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_cistatic inline bool DAC960_LA_hw_mbox_is_full(void __iomem *base) 25748c2ecf20Sopenharmony_ci{ 25758c2ecf20Sopenharmony_ci unsigned char idb = readb(base + DAC960_LA_IDB_OFFSET); 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci return !(idb & DAC960_LA_IDB_HWMBOX_EMPTY); 25788c2ecf20Sopenharmony_ci} 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_cistatic inline bool DAC960_LA_init_in_progress(void __iomem *base) 25818c2ecf20Sopenharmony_ci{ 25828c2ecf20Sopenharmony_ci unsigned char idb = readb(base + DAC960_LA_IDB_OFFSET); 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci return !(idb & DAC960_LA_IDB_INIT_DONE); 25858c2ecf20Sopenharmony_ci} 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_cistatic inline void DAC960_LA_ack_hw_mbox_intr(void __iomem *base) 25888c2ecf20Sopenharmony_ci{ 25898c2ecf20Sopenharmony_ci writeb(DAC960_LA_ODB_HWMBOX_ACK_IRQ, base + DAC960_LA_ODB_OFFSET); 25908c2ecf20Sopenharmony_ci} 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_cistatic inline void DAC960_LA_ack_mem_mbox_intr(void __iomem *base) 25938c2ecf20Sopenharmony_ci{ 25948c2ecf20Sopenharmony_ci writeb(DAC960_LA_ODB_MMBOX_ACK_IRQ, base + DAC960_LA_ODB_OFFSET); 25958c2ecf20Sopenharmony_ci} 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_cistatic inline void DAC960_LA_ack_intr(void __iomem *base) 25988c2ecf20Sopenharmony_ci{ 25998c2ecf20Sopenharmony_ci writeb(DAC960_LA_ODB_HWMBOX_ACK_IRQ | DAC960_LA_ODB_MMBOX_ACK_IRQ, 26008c2ecf20Sopenharmony_ci base + DAC960_LA_ODB_OFFSET); 26018c2ecf20Sopenharmony_ci} 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_cistatic inline bool DAC960_LA_hw_mbox_status_available(void __iomem *base) 26048c2ecf20Sopenharmony_ci{ 26058c2ecf20Sopenharmony_ci unsigned char odb = readb(base + DAC960_LA_ODB_OFFSET); 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci return odb & DAC960_LA_ODB_HWMBOX_STS_AVAIL; 26088c2ecf20Sopenharmony_ci} 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_cistatic inline bool DAC960_LA_mem_mbox_status_available(void __iomem *base) 26118c2ecf20Sopenharmony_ci{ 26128c2ecf20Sopenharmony_ci unsigned char odb = readb(base + DAC960_LA_ODB_OFFSET); 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci return odb & DAC960_LA_ODB_MMBOX_STS_AVAIL; 26158c2ecf20Sopenharmony_ci} 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_cistatic inline void DAC960_LA_enable_intr(void __iomem *base) 26188c2ecf20Sopenharmony_ci{ 26198c2ecf20Sopenharmony_ci unsigned char odb = 0xFF; 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci odb &= ~DAC960_LA_IRQMASK_DISABLE_IRQ; 26228c2ecf20Sopenharmony_ci writeb(odb, base + DAC960_LA_IRQMASK_OFFSET); 26238c2ecf20Sopenharmony_ci} 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_cistatic inline void DAC960_LA_disable_intr(void __iomem *base) 26268c2ecf20Sopenharmony_ci{ 26278c2ecf20Sopenharmony_ci unsigned char odb = 0xFF; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci odb |= DAC960_LA_IRQMASK_DISABLE_IRQ; 26308c2ecf20Sopenharmony_ci writeb(odb, base + DAC960_LA_IRQMASK_OFFSET); 26318c2ecf20Sopenharmony_ci} 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_cistatic inline bool DAC960_LA_intr_enabled(void __iomem *base) 26348c2ecf20Sopenharmony_ci{ 26358c2ecf20Sopenharmony_ci unsigned char imask = readb(base + DAC960_LA_IRQMASK_OFFSET); 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci return !(imask & DAC960_LA_IRQMASK_DISABLE_IRQ); 26388c2ecf20Sopenharmony_ci} 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_cistatic inline void DAC960_LA_write_cmd_mbox(union myrb_cmd_mbox *mem_mbox, 26418c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox) 26428c2ecf20Sopenharmony_ci{ 26438c2ecf20Sopenharmony_ci mem_mbox->words[1] = mbox->words[1]; 26448c2ecf20Sopenharmony_ci mem_mbox->words[2] = mbox->words[2]; 26458c2ecf20Sopenharmony_ci mem_mbox->words[3] = mbox->words[3]; 26468c2ecf20Sopenharmony_ci /* Memory barrier to prevent reordering */ 26478c2ecf20Sopenharmony_ci wmb(); 26488c2ecf20Sopenharmony_ci mem_mbox->words[0] = mbox->words[0]; 26498c2ecf20Sopenharmony_ci /* Memory barrier to force PCI access */ 26508c2ecf20Sopenharmony_ci mb(); 26518c2ecf20Sopenharmony_ci} 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_cistatic inline void DAC960_LA_write_hw_mbox(void __iomem *base, 26548c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox) 26558c2ecf20Sopenharmony_ci{ 26568c2ecf20Sopenharmony_ci writel(mbox->words[0], base + DAC960_LA_CMDOP_OFFSET); 26578c2ecf20Sopenharmony_ci writel(mbox->words[1], base + DAC960_LA_MBOX4_OFFSET); 26588c2ecf20Sopenharmony_ci writel(mbox->words[2], base + DAC960_LA_MBOX8_OFFSET); 26598c2ecf20Sopenharmony_ci writeb(mbox->bytes[12], base + DAC960_LA_MBOX12_OFFSET); 26608c2ecf20Sopenharmony_ci} 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_cistatic inline unsigned char DAC960_LA_read_status_cmd_ident(void __iomem *base) 26638c2ecf20Sopenharmony_ci{ 26648c2ecf20Sopenharmony_ci return readb(base + DAC960_LA_STSID_OFFSET); 26658c2ecf20Sopenharmony_ci} 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_cistatic inline unsigned short DAC960_LA_read_status(void __iomem *base) 26688c2ecf20Sopenharmony_ci{ 26698c2ecf20Sopenharmony_ci return readw(base + DAC960_LA_STS_OFFSET); 26708c2ecf20Sopenharmony_ci} 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_cistatic inline bool 26738c2ecf20Sopenharmony_ciDAC960_LA_read_error_status(void __iomem *base, unsigned char *error, 26748c2ecf20Sopenharmony_ci unsigned char *param0, unsigned char *param1) 26758c2ecf20Sopenharmony_ci{ 26768c2ecf20Sopenharmony_ci unsigned char errsts = readb(base + DAC960_LA_ERRSTS_OFFSET); 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci if (!(errsts & DAC960_LA_ERRSTS_PENDING)) 26798c2ecf20Sopenharmony_ci return false; 26808c2ecf20Sopenharmony_ci errsts &= ~DAC960_LA_ERRSTS_PENDING; 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci *error = errsts; 26838c2ecf20Sopenharmony_ci *param0 = readb(base + DAC960_LA_CMDOP_OFFSET); 26848c2ecf20Sopenharmony_ci *param1 = readb(base + DAC960_LA_CMDID_OFFSET); 26858c2ecf20Sopenharmony_ci writeb(0xFF, base + DAC960_LA_ERRSTS_OFFSET); 26868c2ecf20Sopenharmony_ci return true; 26878c2ecf20Sopenharmony_ci} 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_cistatic inline unsigned short 26908c2ecf20Sopenharmony_ciDAC960_LA_mbox_init(struct pci_dev *pdev, void __iomem *base, 26918c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox) 26928c2ecf20Sopenharmony_ci{ 26938c2ecf20Sopenharmony_ci unsigned short status; 26948c2ecf20Sopenharmony_ci int timeout = 0; 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci while (timeout < MYRB_MAILBOX_TIMEOUT) { 26978c2ecf20Sopenharmony_ci if (!DAC960_LA_hw_mbox_is_full(base)) 26988c2ecf20Sopenharmony_ci break; 26998c2ecf20Sopenharmony_ci udelay(10); 27008c2ecf20Sopenharmony_ci timeout++; 27018c2ecf20Sopenharmony_ci } 27028c2ecf20Sopenharmony_ci if (DAC960_LA_hw_mbox_is_full(base)) { 27038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 27048c2ecf20Sopenharmony_ci "Timeout waiting for empty mailbox\n"); 27058c2ecf20Sopenharmony_ci return MYRB_STATUS_SUBSYS_TIMEOUT; 27068c2ecf20Sopenharmony_ci } 27078c2ecf20Sopenharmony_ci DAC960_LA_write_hw_mbox(base, mbox); 27088c2ecf20Sopenharmony_ci DAC960_LA_hw_mbox_new_cmd(base); 27098c2ecf20Sopenharmony_ci timeout = 0; 27108c2ecf20Sopenharmony_ci while (timeout < MYRB_MAILBOX_TIMEOUT) { 27118c2ecf20Sopenharmony_ci if (DAC960_LA_hw_mbox_status_available(base)) 27128c2ecf20Sopenharmony_ci break; 27138c2ecf20Sopenharmony_ci udelay(10); 27148c2ecf20Sopenharmony_ci timeout++; 27158c2ecf20Sopenharmony_ci } 27168c2ecf20Sopenharmony_ci if (!DAC960_LA_hw_mbox_status_available(base)) { 27178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Timeout waiting for mailbox status\n"); 27188c2ecf20Sopenharmony_ci return MYRB_STATUS_SUBSYS_TIMEOUT; 27198c2ecf20Sopenharmony_ci } 27208c2ecf20Sopenharmony_ci status = DAC960_LA_read_status(base); 27218c2ecf20Sopenharmony_ci DAC960_LA_ack_hw_mbox_intr(base); 27228c2ecf20Sopenharmony_ci DAC960_LA_ack_hw_mbox_status(base); 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci return status; 27258c2ecf20Sopenharmony_ci} 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_cistatic int DAC960_LA_hw_init(struct pci_dev *pdev, 27288c2ecf20Sopenharmony_ci struct myrb_hba *cb, void __iomem *base) 27298c2ecf20Sopenharmony_ci{ 27308c2ecf20Sopenharmony_ci int timeout = 0; 27318c2ecf20Sopenharmony_ci unsigned char error, parm0, parm1; 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci DAC960_LA_disable_intr(base); 27348c2ecf20Sopenharmony_ci DAC960_LA_ack_hw_mbox_status(base); 27358c2ecf20Sopenharmony_ci udelay(1000); 27368c2ecf20Sopenharmony_ci while (DAC960_LA_init_in_progress(base) && 27378c2ecf20Sopenharmony_ci timeout < MYRB_MAILBOX_TIMEOUT) { 27388c2ecf20Sopenharmony_ci if (DAC960_LA_read_error_status(base, &error, 27398c2ecf20Sopenharmony_ci &parm0, &parm1) && 27408c2ecf20Sopenharmony_ci myrb_err_status(cb, error, parm0, parm1)) 27418c2ecf20Sopenharmony_ci return -ENODEV; 27428c2ecf20Sopenharmony_ci udelay(10); 27438c2ecf20Sopenharmony_ci timeout++; 27448c2ecf20Sopenharmony_ci } 27458c2ecf20Sopenharmony_ci if (timeout == MYRB_MAILBOX_TIMEOUT) { 27468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 27478c2ecf20Sopenharmony_ci "Timeout waiting for Controller Initialisation\n"); 27488c2ecf20Sopenharmony_ci return -ETIMEDOUT; 27498c2ecf20Sopenharmony_ci } 27508c2ecf20Sopenharmony_ci if (!myrb_enable_mmio(cb, DAC960_LA_mbox_init)) { 27518c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 27528c2ecf20Sopenharmony_ci "Unable to Enable Memory Mailbox Interface\n"); 27538c2ecf20Sopenharmony_ci DAC960_LA_reset_ctrl(base); 27548c2ecf20Sopenharmony_ci return -ENODEV; 27558c2ecf20Sopenharmony_ci } 27568c2ecf20Sopenharmony_ci DAC960_LA_enable_intr(base); 27578c2ecf20Sopenharmony_ci cb->qcmd = myrb_qcmd; 27588c2ecf20Sopenharmony_ci cb->write_cmd_mbox = DAC960_LA_write_cmd_mbox; 27598c2ecf20Sopenharmony_ci if (cb->dual_mode_interface) 27608c2ecf20Sopenharmony_ci cb->get_cmd_mbox = DAC960_LA_mem_mbox_new_cmd; 27618c2ecf20Sopenharmony_ci else 27628c2ecf20Sopenharmony_ci cb->get_cmd_mbox = DAC960_LA_hw_mbox_new_cmd; 27638c2ecf20Sopenharmony_ci cb->disable_intr = DAC960_LA_disable_intr; 27648c2ecf20Sopenharmony_ci cb->reset = DAC960_LA_reset_ctrl; 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci return 0; 27678c2ecf20Sopenharmony_ci} 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_cistatic irqreturn_t DAC960_LA_intr_handler(int irq, void *arg) 27708c2ecf20Sopenharmony_ci{ 27718c2ecf20Sopenharmony_ci struct myrb_hba *cb = arg; 27728c2ecf20Sopenharmony_ci void __iomem *base = cb->io_base; 27738c2ecf20Sopenharmony_ci struct myrb_stat_mbox *next_stat_mbox; 27748c2ecf20Sopenharmony_ci unsigned long flags; 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 27778c2ecf20Sopenharmony_ci DAC960_LA_ack_intr(base); 27788c2ecf20Sopenharmony_ci next_stat_mbox = cb->next_stat_mbox; 27798c2ecf20Sopenharmony_ci while (next_stat_mbox->valid) { 27808c2ecf20Sopenharmony_ci unsigned char id = next_stat_mbox->id; 27818c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd = NULL; 27828c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = NULL; 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci if (id == MYRB_DCMD_TAG) 27858c2ecf20Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 27868c2ecf20Sopenharmony_ci else if (id == MYRB_MCMD_TAG) 27878c2ecf20Sopenharmony_ci cmd_blk = &cb->mcmd_blk; 27888c2ecf20Sopenharmony_ci else { 27898c2ecf20Sopenharmony_ci scmd = scsi_host_find_tag(cb->host, id - 3); 27908c2ecf20Sopenharmony_ci if (scmd) 27918c2ecf20Sopenharmony_ci cmd_blk = scsi_cmd_priv(scmd); 27928c2ecf20Sopenharmony_ci } 27938c2ecf20Sopenharmony_ci if (cmd_blk) 27948c2ecf20Sopenharmony_ci cmd_blk->status = next_stat_mbox->status; 27958c2ecf20Sopenharmony_ci else 27968c2ecf20Sopenharmony_ci dev_err(&cb->pdev->dev, 27978c2ecf20Sopenharmony_ci "Unhandled command completion %d\n", id); 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci memset(next_stat_mbox, 0, sizeof(struct myrb_stat_mbox)); 28008c2ecf20Sopenharmony_ci if (++next_stat_mbox > cb->last_stat_mbox) 28018c2ecf20Sopenharmony_ci next_stat_mbox = cb->first_stat_mbox; 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci if (cmd_blk) { 28048c2ecf20Sopenharmony_ci if (id < 3) 28058c2ecf20Sopenharmony_ci myrb_handle_cmdblk(cb, cmd_blk); 28068c2ecf20Sopenharmony_ci else 28078c2ecf20Sopenharmony_ci myrb_handle_scsi(cb, cmd_blk, scmd); 28088c2ecf20Sopenharmony_ci } 28098c2ecf20Sopenharmony_ci } 28108c2ecf20Sopenharmony_ci cb->next_stat_mbox = next_stat_mbox; 28118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 28128c2ecf20Sopenharmony_ci return IRQ_HANDLED; 28138c2ecf20Sopenharmony_ci} 28148c2ecf20Sopenharmony_ci 28158c2ecf20Sopenharmony_cistruct myrb_privdata DAC960_LA_privdata = { 28168c2ecf20Sopenharmony_ci .hw_init = DAC960_LA_hw_init, 28178c2ecf20Sopenharmony_ci .irq_handler = DAC960_LA_intr_handler, 28188c2ecf20Sopenharmony_ci .mmio_size = DAC960_LA_mmio_size, 28198c2ecf20Sopenharmony_ci}; 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci/* 28228c2ecf20Sopenharmony_ci * DAC960 PG Series Controllers 28238c2ecf20Sopenharmony_ci */ 28248c2ecf20Sopenharmony_cistatic inline void DAC960_PG_hw_mbox_new_cmd(void __iomem *base) 28258c2ecf20Sopenharmony_ci{ 28268c2ecf20Sopenharmony_ci writel(DAC960_PG_IDB_HWMBOX_NEW_CMD, base + DAC960_PG_IDB_OFFSET); 28278c2ecf20Sopenharmony_ci} 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_cistatic inline void DAC960_PG_ack_hw_mbox_status(void __iomem *base) 28308c2ecf20Sopenharmony_ci{ 28318c2ecf20Sopenharmony_ci writel(DAC960_PG_IDB_HWMBOX_ACK_STS, base + DAC960_PG_IDB_OFFSET); 28328c2ecf20Sopenharmony_ci} 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_cistatic inline void DAC960_PG_gen_intr(void __iomem *base) 28358c2ecf20Sopenharmony_ci{ 28368c2ecf20Sopenharmony_ci writel(DAC960_PG_IDB_GEN_IRQ, base + DAC960_PG_IDB_OFFSET); 28378c2ecf20Sopenharmony_ci} 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_cistatic inline void DAC960_PG_reset_ctrl(void __iomem *base) 28408c2ecf20Sopenharmony_ci{ 28418c2ecf20Sopenharmony_ci writel(DAC960_PG_IDB_CTRL_RESET, base + DAC960_PG_IDB_OFFSET); 28428c2ecf20Sopenharmony_ci} 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_cistatic inline void DAC960_PG_mem_mbox_new_cmd(void __iomem *base) 28458c2ecf20Sopenharmony_ci{ 28468c2ecf20Sopenharmony_ci writel(DAC960_PG_IDB_MMBOX_NEW_CMD, base + DAC960_PG_IDB_OFFSET); 28478c2ecf20Sopenharmony_ci} 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_cistatic inline bool DAC960_PG_hw_mbox_is_full(void __iomem *base) 28508c2ecf20Sopenharmony_ci{ 28518c2ecf20Sopenharmony_ci unsigned char idb = readl(base + DAC960_PG_IDB_OFFSET); 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci return idb & DAC960_PG_IDB_HWMBOX_FULL; 28548c2ecf20Sopenharmony_ci} 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_cistatic inline bool DAC960_PG_init_in_progress(void __iomem *base) 28578c2ecf20Sopenharmony_ci{ 28588c2ecf20Sopenharmony_ci unsigned char idb = readl(base + DAC960_PG_IDB_OFFSET); 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci return idb & DAC960_PG_IDB_INIT_IN_PROGRESS; 28618c2ecf20Sopenharmony_ci} 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_cistatic inline void DAC960_PG_ack_hw_mbox_intr(void __iomem *base) 28648c2ecf20Sopenharmony_ci{ 28658c2ecf20Sopenharmony_ci writel(DAC960_PG_ODB_HWMBOX_ACK_IRQ, base + DAC960_PG_ODB_OFFSET); 28668c2ecf20Sopenharmony_ci} 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_cistatic inline void DAC960_PG_ack_mem_mbox_intr(void __iomem *base) 28698c2ecf20Sopenharmony_ci{ 28708c2ecf20Sopenharmony_ci writel(DAC960_PG_ODB_MMBOX_ACK_IRQ, base + DAC960_PG_ODB_OFFSET); 28718c2ecf20Sopenharmony_ci} 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_cistatic inline void DAC960_PG_ack_intr(void __iomem *base) 28748c2ecf20Sopenharmony_ci{ 28758c2ecf20Sopenharmony_ci writel(DAC960_PG_ODB_HWMBOX_ACK_IRQ | DAC960_PG_ODB_MMBOX_ACK_IRQ, 28768c2ecf20Sopenharmony_ci base + DAC960_PG_ODB_OFFSET); 28778c2ecf20Sopenharmony_ci} 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_cistatic inline bool DAC960_PG_hw_mbox_status_available(void __iomem *base) 28808c2ecf20Sopenharmony_ci{ 28818c2ecf20Sopenharmony_ci unsigned char odb = readl(base + DAC960_PG_ODB_OFFSET); 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci return odb & DAC960_PG_ODB_HWMBOX_STS_AVAIL; 28848c2ecf20Sopenharmony_ci} 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_cistatic inline bool DAC960_PG_mem_mbox_status_available(void __iomem *base) 28878c2ecf20Sopenharmony_ci{ 28888c2ecf20Sopenharmony_ci unsigned char odb = readl(base + DAC960_PG_ODB_OFFSET); 28898c2ecf20Sopenharmony_ci 28908c2ecf20Sopenharmony_ci return odb & DAC960_PG_ODB_MMBOX_STS_AVAIL; 28918c2ecf20Sopenharmony_ci} 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_cistatic inline void DAC960_PG_enable_intr(void __iomem *base) 28948c2ecf20Sopenharmony_ci{ 28958c2ecf20Sopenharmony_ci unsigned int imask = (unsigned int)-1; 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci imask &= ~DAC960_PG_IRQMASK_DISABLE_IRQ; 28988c2ecf20Sopenharmony_ci writel(imask, base + DAC960_PG_IRQMASK_OFFSET); 28998c2ecf20Sopenharmony_ci} 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_cistatic inline void DAC960_PG_disable_intr(void __iomem *base) 29028c2ecf20Sopenharmony_ci{ 29038c2ecf20Sopenharmony_ci unsigned int imask = (unsigned int)-1; 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci writel(imask, base + DAC960_PG_IRQMASK_OFFSET); 29068c2ecf20Sopenharmony_ci} 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_cistatic inline bool DAC960_PG_intr_enabled(void __iomem *base) 29098c2ecf20Sopenharmony_ci{ 29108c2ecf20Sopenharmony_ci unsigned int imask = readl(base + DAC960_PG_IRQMASK_OFFSET); 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci return !(imask & DAC960_PG_IRQMASK_DISABLE_IRQ); 29138c2ecf20Sopenharmony_ci} 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_cistatic inline void DAC960_PG_write_cmd_mbox(union myrb_cmd_mbox *mem_mbox, 29168c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox) 29178c2ecf20Sopenharmony_ci{ 29188c2ecf20Sopenharmony_ci mem_mbox->words[1] = mbox->words[1]; 29198c2ecf20Sopenharmony_ci mem_mbox->words[2] = mbox->words[2]; 29208c2ecf20Sopenharmony_ci mem_mbox->words[3] = mbox->words[3]; 29218c2ecf20Sopenharmony_ci /* Memory barrier to prevent reordering */ 29228c2ecf20Sopenharmony_ci wmb(); 29238c2ecf20Sopenharmony_ci mem_mbox->words[0] = mbox->words[0]; 29248c2ecf20Sopenharmony_ci /* Memory barrier to force PCI access */ 29258c2ecf20Sopenharmony_ci mb(); 29268c2ecf20Sopenharmony_ci} 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_cistatic inline void DAC960_PG_write_hw_mbox(void __iomem *base, 29298c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox) 29308c2ecf20Sopenharmony_ci{ 29318c2ecf20Sopenharmony_ci writel(mbox->words[0], base + DAC960_PG_CMDOP_OFFSET); 29328c2ecf20Sopenharmony_ci writel(mbox->words[1], base + DAC960_PG_MBOX4_OFFSET); 29338c2ecf20Sopenharmony_ci writel(mbox->words[2], base + DAC960_PG_MBOX8_OFFSET); 29348c2ecf20Sopenharmony_ci writeb(mbox->bytes[12], base + DAC960_PG_MBOX12_OFFSET); 29358c2ecf20Sopenharmony_ci} 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_cistatic inline unsigned char 29388c2ecf20Sopenharmony_ciDAC960_PG_read_status_cmd_ident(void __iomem *base) 29398c2ecf20Sopenharmony_ci{ 29408c2ecf20Sopenharmony_ci return readb(base + DAC960_PG_STSID_OFFSET); 29418c2ecf20Sopenharmony_ci} 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_cistatic inline unsigned short 29448c2ecf20Sopenharmony_ciDAC960_PG_read_status(void __iomem *base) 29458c2ecf20Sopenharmony_ci{ 29468c2ecf20Sopenharmony_ci return readw(base + DAC960_PG_STS_OFFSET); 29478c2ecf20Sopenharmony_ci} 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_cistatic inline bool 29508c2ecf20Sopenharmony_ciDAC960_PG_read_error_status(void __iomem *base, unsigned char *error, 29518c2ecf20Sopenharmony_ci unsigned char *param0, unsigned char *param1) 29528c2ecf20Sopenharmony_ci{ 29538c2ecf20Sopenharmony_ci unsigned char errsts = readb(base + DAC960_PG_ERRSTS_OFFSET); 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci if (!(errsts & DAC960_PG_ERRSTS_PENDING)) 29568c2ecf20Sopenharmony_ci return false; 29578c2ecf20Sopenharmony_ci errsts &= ~DAC960_PG_ERRSTS_PENDING; 29588c2ecf20Sopenharmony_ci *error = errsts; 29598c2ecf20Sopenharmony_ci *param0 = readb(base + DAC960_PG_CMDOP_OFFSET); 29608c2ecf20Sopenharmony_ci *param1 = readb(base + DAC960_PG_CMDID_OFFSET); 29618c2ecf20Sopenharmony_ci writeb(0, base + DAC960_PG_ERRSTS_OFFSET); 29628c2ecf20Sopenharmony_ci return true; 29638c2ecf20Sopenharmony_ci} 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_cistatic inline unsigned short 29668c2ecf20Sopenharmony_ciDAC960_PG_mbox_init(struct pci_dev *pdev, void __iomem *base, 29678c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox) 29688c2ecf20Sopenharmony_ci{ 29698c2ecf20Sopenharmony_ci unsigned short status; 29708c2ecf20Sopenharmony_ci int timeout = 0; 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_ci while (timeout < MYRB_MAILBOX_TIMEOUT) { 29738c2ecf20Sopenharmony_ci if (!DAC960_PG_hw_mbox_is_full(base)) 29748c2ecf20Sopenharmony_ci break; 29758c2ecf20Sopenharmony_ci udelay(10); 29768c2ecf20Sopenharmony_ci timeout++; 29778c2ecf20Sopenharmony_ci } 29788c2ecf20Sopenharmony_ci if (DAC960_PG_hw_mbox_is_full(base)) { 29798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 29808c2ecf20Sopenharmony_ci "Timeout waiting for empty mailbox\n"); 29818c2ecf20Sopenharmony_ci return MYRB_STATUS_SUBSYS_TIMEOUT; 29828c2ecf20Sopenharmony_ci } 29838c2ecf20Sopenharmony_ci DAC960_PG_write_hw_mbox(base, mbox); 29848c2ecf20Sopenharmony_ci DAC960_PG_hw_mbox_new_cmd(base); 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_ci timeout = 0; 29878c2ecf20Sopenharmony_ci while (timeout < MYRB_MAILBOX_TIMEOUT) { 29888c2ecf20Sopenharmony_ci if (DAC960_PG_hw_mbox_status_available(base)) 29898c2ecf20Sopenharmony_ci break; 29908c2ecf20Sopenharmony_ci udelay(10); 29918c2ecf20Sopenharmony_ci timeout++; 29928c2ecf20Sopenharmony_ci } 29938c2ecf20Sopenharmony_ci if (!DAC960_PG_hw_mbox_status_available(base)) { 29948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 29958c2ecf20Sopenharmony_ci "Timeout waiting for mailbox status\n"); 29968c2ecf20Sopenharmony_ci return MYRB_STATUS_SUBSYS_TIMEOUT; 29978c2ecf20Sopenharmony_ci } 29988c2ecf20Sopenharmony_ci status = DAC960_PG_read_status(base); 29998c2ecf20Sopenharmony_ci DAC960_PG_ack_hw_mbox_intr(base); 30008c2ecf20Sopenharmony_ci DAC960_PG_ack_hw_mbox_status(base); 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci return status; 30038c2ecf20Sopenharmony_ci} 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_cistatic int DAC960_PG_hw_init(struct pci_dev *pdev, 30068c2ecf20Sopenharmony_ci struct myrb_hba *cb, void __iomem *base) 30078c2ecf20Sopenharmony_ci{ 30088c2ecf20Sopenharmony_ci int timeout = 0; 30098c2ecf20Sopenharmony_ci unsigned char error, parm0, parm1; 30108c2ecf20Sopenharmony_ci 30118c2ecf20Sopenharmony_ci DAC960_PG_disable_intr(base); 30128c2ecf20Sopenharmony_ci DAC960_PG_ack_hw_mbox_status(base); 30138c2ecf20Sopenharmony_ci udelay(1000); 30148c2ecf20Sopenharmony_ci while (DAC960_PG_init_in_progress(base) && 30158c2ecf20Sopenharmony_ci timeout < MYRB_MAILBOX_TIMEOUT) { 30168c2ecf20Sopenharmony_ci if (DAC960_PG_read_error_status(base, &error, 30178c2ecf20Sopenharmony_ci &parm0, &parm1) && 30188c2ecf20Sopenharmony_ci myrb_err_status(cb, error, parm0, parm1)) 30198c2ecf20Sopenharmony_ci return -EIO; 30208c2ecf20Sopenharmony_ci udelay(10); 30218c2ecf20Sopenharmony_ci timeout++; 30228c2ecf20Sopenharmony_ci } 30238c2ecf20Sopenharmony_ci if (timeout == MYRB_MAILBOX_TIMEOUT) { 30248c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 30258c2ecf20Sopenharmony_ci "Timeout waiting for Controller Initialisation\n"); 30268c2ecf20Sopenharmony_ci return -ETIMEDOUT; 30278c2ecf20Sopenharmony_ci } 30288c2ecf20Sopenharmony_ci if (!myrb_enable_mmio(cb, DAC960_PG_mbox_init)) { 30298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 30308c2ecf20Sopenharmony_ci "Unable to Enable Memory Mailbox Interface\n"); 30318c2ecf20Sopenharmony_ci DAC960_PG_reset_ctrl(base); 30328c2ecf20Sopenharmony_ci return -ENODEV; 30338c2ecf20Sopenharmony_ci } 30348c2ecf20Sopenharmony_ci DAC960_PG_enable_intr(base); 30358c2ecf20Sopenharmony_ci cb->qcmd = myrb_qcmd; 30368c2ecf20Sopenharmony_ci cb->write_cmd_mbox = DAC960_PG_write_cmd_mbox; 30378c2ecf20Sopenharmony_ci if (cb->dual_mode_interface) 30388c2ecf20Sopenharmony_ci cb->get_cmd_mbox = DAC960_PG_mem_mbox_new_cmd; 30398c2ecf20Sopenharmony_ci else 30408c2ecf20Sopenharmony_ci cb->get_cmd_mbox = DAC960_PG_hw_mbox_new_cmd; 30418c2ecf20Sopenharmony_ci cb->disable_intr = DAC960_PG_disable_intr; 30428c2ecf20Sopenharmony_ci cb->reset = DAC960_PG_reset_ctrl; 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci return 0; 30458c2ecf20Sopenharmony_ci} 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_cistatic irqreturn_t DAC960_PG_intr_handler(int irq, void *arg) 30488c2ecf20Sopenharmony_ci{ 30498c2ecf20Sopenharmony_ci struct myrb_hba *cb = arg; 30508c2ecf20Sopenharmony_ci void __iomem *base = cb->io_base; 30518c2ecf20Sopenharmony_ci struct myrb_stat_mbox *next_stat_mbox; 30528c2ecf20Sopenharmony_ci unsigned long flags; 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 30558c2ecf20Sopenharmony_ci DAC960_PG_ack_intr(base); 30568c2ecf20Sopenharmony_ci next_stat_mbox = cb->next_stat_mbox; 30578c2ecf20Sopenharmony_ci while (next_stat_mbox->valid) { 30588c2ecf20Sopenharmony_ci unsigned char id = next_stat_mbox->id; 30598c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd = NULL; 30608c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = NULL; 30618c2ecf20Sopenharmony_ci 30628c2ecf20Sopenharmony_ci if (id == MYRB_DCMD_TAG) 30638c2ecf20Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 30648c2ecf20Sopenharmony_ci else if (id == MYRB_MCMD_TAG) 30658c2ecf20Sopenharmony_ci cmd_blk = &cb->mcmd_blk; 30668c2ecf20Sopenharmony_ci else { 30678c2ecf20Sopenharmony_ci scmd = scsi_host_find_tag(cb->host, id - 3); 30688c2ecf20Sopenharmony_ci if (scmd) 30698c2ecf20Sopenharmony_ci cmd_blk = scsi_cmd_priv(scmd); 30708c2ecf20Sopenharmony_ci } 30718c2ecf20Sopenharmony_ci if (cmd_blk) 30728c2ecf20Sopenharmony_ci cmd_blk->status = next_stat_mbox->status; 30738c2ecf20Sopenharmony_ci else 30748c2ecf20Sopenharmony_ci dev_err(&cb->pdev->dev, 30758c2ecf20Sopenharmony_ci "Unhandled command completion %d\n", id); 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci memset(next_stat_mbox, 0, sizeof(struct myrb_stat_mbox)); 30788c2ecf20Sopenharmony_ci if (++next_stat_mbox > cb->last_stat_mbox) 30798c2ecf20Sopenharmony_ci next_stat_mbox = cb->first_stat_mbox; 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci if (id < 3) 30828c2ecf20Sopenharmony_ci myrb_handle_cmdblk(cb, cmd_blk); 30838c2ecf20Sopenharmony_ci else 30848c2ecf20Sopenharmony_ci myrb_handle_scsi(cb, cmd_blk, scmd); 30858c2ecf20Sopenharmony_ci } 30868c2ecf20Sopenharmony_ci cb->next_stat_mbox = next_stat_mbox; 30878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 30888c2ecf20Sopenharmony_ci return IRQ_HANDLED; 30898c2ecf20Sopenharmony_ci} 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_cistruct myrb_privdata DAC960_PG_privdata = { 30928c2ecf20Sopenharmony_ci .hw_init = DAC960_PG_hw_init, 30938c2ecf20Sopenharmony_ci .irq_handler = DAC960_PG_intr_handler, 30948c2ecf20Sopenharmony_ci .mmio_size = DAC960_PG_mmio_size, 30958c2ecf20Sopenharmony_ci}; 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci/* 30998c2ecf20Sopenharmony_ci * DAC960 PD Series Controllers 31008c2ecf20Sopenharmony_ci */ 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_cistatic inline void DAC960_PD_hw_mbox_new_cmd(void __iomem *base) 31038c2ecf20Sopenharmony_ci{ 31048c2ecf20Sopenharmony_ci writeb(DAC960_PD_IDB_HWMBOX_NEW_CMD, base + DAC960_PD_IDB_OFFSET); 31058c2ecf20Sopenharmony_ci} 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_cistatic inline void DAC960_PD_ack_hw_mbox_status(void __iomem *base) 31088c2ecf20Sopenharmony_ci{ 31098c2ecf20Sopenharmony_ci writeb(DAC960_PD_IDB_HWMBOX_ACK_STS, base + DAC960_PD_IDB_OFFSET); 31108c2ecf20Sopenharmony_ci} 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_cistatic inline void DAC960_PD_gen_intr(void __iomem *base) 31138c2ecf20Sopenharmony_ci{ 31148c2ecf20Sopenharmony_ci writeb(DAC960_PD_IDB_GEN_IRQ, base + DAC960_PD_IDB_OFFSET); 31158c2ecf20Sopenharmony_ci} 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_cistatic inline void DAC960_PD_reset_ctrl(void __iomem *base) 31188c2ecf20Sopenharmony_ci{ 31198c2ecf20Sopenharmony_ci writeb(DAC960_PD_IDB_CTRL_RESET, base + DAC960_PD_IDB_OFFSET); 31208c2ecf20Sopenharmony_ci} 31218c2ecf20Sopenharmony_ci 31228c2ecf20Sopenharmony_cistatic inline bool DAC960_PD_hw_mbox_is_full(void __iomem *base) 31238c2ecf20Sopenharmony_ci{ 31248c2ecf20Sopenharmony_ci unsigned char idb = readb(base + DAC960_PD_IDB_OFFSET); 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci return idb & DAC960_PD_IDB_HWMBOX_FULL; 31278c2ecf20Sopenharmony_ci} 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_cistatic inline bool DAC960_PD_init_in_progress(void __iomem *base) 31308c2ecf20Sopenharmony_ci{ 31318c2ecf20Sopenharmony_ci unsigned char idb = readb(base + DAC960_PD_IDB_OFFSET); 31328c2ecf20Sopenharmony_ci 31338c2ecf20Sopenharmony_ci return idb & DAC960_PD_IDB_INIT_IN_PROGRESS; 31348c2ecf20Sopenharmony_ci} 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_cistatic inline void DAC960_PD_ack_intr(void __iomem *base) 31378c2ecf20Sopenharmony_ci{ 31388c2ecf20Sopenharmony_ci writeb(DAC960_PD_ODB_HWMBOX_ACK_IRQ, base + DAC960_PD_ODB_OFFSET); 31398c2ecf20Sopenharmony_ci} 31408c2ecf20Sopenharmony_ci 31418c2ecf20Sopenharmony_cistatic inline bool DAC960_PD_hw_mbox_status_available(void __iomem *base) 31428c2ecf20Sopenharmony_ci{ 31438c2ecf20Sopenharmony_ci unsigned char odb = readb(base + DAC960_PD_ODB_OFFSET); 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci return odb & DAC960_PD_ODB_HWMBOX_STS_AVAIL; 31468c2ecf20Sopenharmony_ci} 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_cistatic inline void DAC960_PD_enable_intr(void __iomem *base) 31498c2ecf20Sopenharmony_ci{ 31508c2ecf20Sopenharmony_ci writeb(DAC960_PD_IRQMASK_ENABLE_IRQ, base + DAC960_PD_IRQEN_OFFSET); 31518c2ecf20Sopenharmony_ci} 31528c2ecf20Sopenharmony_ci 31538c2ecf20Sopenharmony_cistatic inline void DAC960_PD_disable_intr(void __iomem *base) 31548c2ecf20Sopenharmony_ci{ 31558c2ecf20Sopenharmony_ci writeb(0, base + DAC960_PD_IRQEN_OFFSET); 31568c2ecf20Sopenharmony_ci} 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_cistatic inline bool DAC960_PD_intr_enabled(void __iomem *base) 31598c2ecf20Sopenharmony_ci{ 31608c2ecf20Sopenharmony_ci unsigned char imask = readb(base + DAC960_PD_IRQEN_OFFSET); 31618c2ecf20Sopenharmony_ci 31628c2ecf20Sopenharmony_ci return imask & DAC960_PD_IRQMASK_ENABLE_IRQ; 31638c2ecf20Sopenharmony_ci} 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_cistatic inline void DAC960_PD_write_cmd_mbox(void __iomem *base, 31668c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox) 31678c2ecf20Sopenharmony_ci{ 31688c2ecf20Sopenharmony_ci writel(mbox->words[0], base + DAC960_PD_CMDOP_OFFSET); 31698c2ecf20Sopenharmony_ci writel(mbox->words[1], base + DAC960_PD_MBOX4_OFFSET); 31708c2ecf20Sopenharmony_ci writel(mbox->words[2], base + DAC960_PD_MBOX8_OFFSET); 31718c2ecf20Sopenharmony_ci writeb(mbox->bytes[12], base + DAC960_PD_MBOX12_OFFSET); 31728c2ecf20Sopenharmony_ci} 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_cistatic inline unsigned char 31758c2ecf20Sopenharmony_ciDAC960_PD_read_status_cmd_ident(void __iomem *base) 31768c2ecf20Sopenharmony_ci{ 31778c2ecf20Sopenharmony_ci return readb(base + DAC960_PD_STSID_OFFSET); 31788c2ecf20Sopenharmony_ci} 31798c2ecf20Sopenharmony_ci 31808c2ecf20Sopenharmony_cistatic inline unsigned short 31818c2ecf20Sopenharmony_ciDAC960_PD_read_status(void __iomem *base) 31828c2ecf20Sopenharmony_ci{ 31838c2ecf20Sopenharmony_ci return readw(base + DAC960_PD_STS_OFFSET); 31848c2ecf20Sopenharmony_ci} 31858c2ecf20Sopenharmony_ci 31868c2ecf20Sopenharmony_cistatic inline bool 31878c2ecf20Sopenharmony_ciDAC960_PD_read_error_status(void __iomem *base, unsigned char *error, 31888c2ecf20Sopenharmony_ci unsigned char *param0, unsigned char *param1) 31898c2ecf20Sopenharmony_ci{ 31908c2ecf20Sopenharmony_ci unsigned char errsts = readb(base + DAC960_PD_ERRSTS_OFFSET); 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_ci if (!(errsts & DAC960_PD_ERRSTS_PENDING)) 31938c2ecf20Sopenharmony_ci return false; 31948c2ecf20Sopenharmony_ci errsts &= ~DAC960_PD_ERRSTS_PENDING; 31958c2ecf20Sopenharmony_ci *error = errsts; 31968c2ecf20Sopenharmony_ci *param0 = readb(base + DAC960_PD_CMDOP_OFFSET); 31978c2ecf20Sopenharmony_ci *param1 = readb(base + DAC960_PD_CMDID_OFFSET); 31988c2ecf20Sopenharmony_ci writeb(0, base + DAC960_PD_ERRSTS_OFFSET); 31998c2ecf20Sopenharmony_ci return true; 32008c2ecf20Sopenharmony_ci} 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_cistatic void DAC960_PD_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) 32038c2ecf20Sopenharmony_ci{ 32048c2ecf20Sopenharmony_ci void __iomem *base = cb->io_base; 32058c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 32068c2ecf20Sopenharmony_ci 32078c2ecf20Sopenharmony_ci while (DAC960_PD_hw_mbox_is_full(base)) 32088c2ecf20Sopenharmony_ci udelay(1); 32098c2ecf20Sopenharmony_ci DAC960_PD_write_cmd_mbox(base, mbox); 32108c2ecf20Sopenharmony_ci DAC960_PD_hw_mbox_new_cmd(base); 32118c2ecf20Sopenharmony_ci} 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_cistatic int DAC960_PD_hw_init(struct pci_dev *pdev, 32148c2ecf20Sopenharmony_ci struct myrb_hba *cb, void __iomem *base) 32158c2ecf20Sopenharmony_ci{ 32168c2ecf20Sopenharmony_ci int timeout = 0; 32178c2ecf20Sopenharmony_ci unsigned char error, parm0, parm1; 32188c2ecf20Sopenharmony_ci 32198c2ecf20Sopenharmony_ci if (!request_region(cb->io_addr, 0x80, "myrb")) { 32208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IO port 0x%lx busy\n", 32218c2ecf20Sopenharmony_ci (unsigned long)cb->io_addr); 32228c2ecf20Sopenharmony_ci return -EBUSY; 32238c2ecf20Sopenharmony_ci } 32248c2ecf20Sopenharmony_ci DAC960_PD_disable_intr(base); 32258c2ecf20Sopenharmony_ci DAC960_PD_ack_hw_mbox_status(base); 32268c2ecf20Sopenharmony_ci udelay(1000); 32278c2ecf20Sopenharmony_ci while (DAC960_PD_init_in_progress(base) && 32288c2ecf20Sopenharmony_ci timeout < MYRB_MAILBOX_TIMEOUT) { 32298c2ecf20Sopenharmony_ci if (DAC960_PD_read_error_status(base, &error, 32308c2ecf20Sopenharmony_ci &parm0, &parm1) && 32318c2ecf20Sopenharmony_ci myrb_err_status(cb, error, parm0, parm1)) 32328c2ecf20Sopenharmony_ci return -EIO; 32338c2ecf20Sopenharmony_ci udelay(10); 32348c2ecf20Sopenharmony_ci timeout++; 32358c2ecf20Sopenharmony_ci } 32368c2ecf20Sopenharmony_ci if (timeout == MYRB_MAILBOX_TIMEOUT) { 32378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 32388c2ecf20Sopenharmony_ci "Timeout waiting for Controller Initialisation\n"); 32398c2ecf20Sopenharmony_ci return -ETIMEDOUT; 32408c2ecf20Sopenharmony_ci } 32418c2ecf20Sopenharmony_ci if (!myrb_enable_mmio(cb, NULL)) { 32428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 32438c2ecf20Sopenharmony_ci "Unable to Enable Memory Mailbox Interface\n"); 32448c2ecf20Sopenharmony_ci DAC960_PD_reset_ctrl(base); 32458c2ecf20Sopenharmony_ci return -ENODEV; 32468c2ecf20Sopenharmony_ci } 32478c2ecf20Sopenharmony_ci DAC960_PD_enable_intr(base); 32488c2ecf20Sopenharmony_ci cb->qcmd = DAC960_PD_qcmd; 32498c2ecf20Sopenharmony_ci cb->disable_intr = DAC960_PD_disable_intr; 32508c2ecf20Sopenharmony_ci cb->reset = DAC960_PD_reset_ctrl; 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_ci return 0; 32538c2ecf20Sopenharmony_ci} 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_cistatic irqreturn_t DAC960_PD_intr_handler(int irq, void *arg) 32568c2ecf20Sopenharmony_ci{ 32578c2ecf20Sopenharmony_ci struct myrb_hba *cb = arg; 32588c2ecf20Sopenharmony_ci void __iomem *base = cb->io_base; 32598c2ecf20Sopenharmony_ci unsigned long flags; 32608c2ecf20Sopenharmony_ci 32618c2ecf20Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 32628c2ecf20Sopenharmony_ci while (DAC960_PD_hw_mbox_status_available(base)) { 32638c2ecf20Sopenharmony_ci unsigned char id = DAC960_PD_read_status_cmd_ident(base); 32648c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd = NULL; 32658c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = NULL; 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci if (id == MYRB_DCMD_TAG) 32688c2ecf20Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 32698c2ecf20Sopenharmony_ci else if (id == MYRB_MCMD_TAG) 32708c2ecf20Sopenharmony_ci cmd_blk = &cb->mcmd_blk; 32718c2ecf20Sopenharmony_ci else { 32728c2ecf20Sopenharmony_ci scmd = scsi_host_find_tag(cb->host, id - 3); 32738c2ecf20Sopenharmony_ci if (scmd) 32748c2ecf20Sopenharmony_ci cmd_blk = scsi_cmd_priv(scmd); 32758c2ecf20Sopenharmony_ci } 32768c2ecf20Sopenharmony_ci if (cmd_blk) 32778c2ecf20Sopenharmony_ci cmd_blk->status = DAC960_PD_read_status(base); 32788c2ecf20Sopenharmony_ci else 32798c2ecf20Sopenharmony_ci dev_err(&cb->pdev->dev, 32808c2ecf20Sopenharmony_ci "Unhandled command completion %d\n", id); 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci DAC960_PD_ack_intr(base); 32838c2ecf20Sopenharmony_ci DAC960_PD_ack_hw_mbox_status(base); 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci if (id < 3) 32868c2ecf20Sopenharmony_ci myrb_handle_cmdblk(cb, cmd_blk); 32878c2ecf20Sopenharmony_ci else 32888c2ecf20Sopenharmony_ci myrb_handle_scsi(cb, cmd_blk, scmd); 32898c2ecf20Sopenharmony_ci } 32908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 32918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 32928c2ecf20Sopenharmony_ci} 32938c2ecf20Sopenharmony_ci 32948c2ecf20Sopenharmony_cistruct myrb_privdata DAC960_PD_privdata = { 32958c2ecf20Sopenharmony_ci .hw_init = DAC960_PD_hw_init, 32968c2ecf20Sopenharmony_ci .irq_handler = DAC960_PD_intr_handler, 32978c2ecf20Sopenharmony_ci .mmio_size = DAC960_PD_mmio_size, 32988c2ecf20Sopenharmony_ci}; 32998c2ecf20Sopenharmony_ci 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_ci/* 33028c2ecf20Sopenharmony_ci * DAC960 P Series Controllers 33038c2ecf20Sopenharmony_ci * 33048c2ecf20Sopenharmony_ci * Similar to the DAC960 PD Series Controllers, but some commands have 33058c2ecf20Sopenharmony_ci * to be translated. 33068c2ecf20Sopenharmony_ci */ 33078c2ecf20Sopenharmony_ci 33088c2ecf20Sopenharmony_cistatic inline void myrb_translate_enquiry(void *enq) 33098c2ecf20Sopenharmony_ci{ 33108c2ecf20Sopenharmony_ci memcpy(enq + 132, enq + 36, 64); 33118c2ecf20Sopenharmony_ci memset(enq + 36, 0, 96); 33128c2ecf20Sopenharmony_ci} 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_cistatic inline void myrb_translate_devstate(void *state) 33158c2ecf20Sopenharmony_ci{ 33168c2ecf20Sopenharmony_ci memcpy(state + 2, state + 3, 1); 33178c2ecf20Sopenharmony_ci memmove(state + 4, state + 5, 2); 33188c2ecf20Sopenharmony_ci memmove(state + 6, state + 8, 4); 33198c2ecf20Sopenharmony_ci} 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_cistatic inline void myrb_translate_to_rw_command(struct myrb_cmdblk *cmd_blk) 33228c2ecf20Sopenharmony_ci{ 33238c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 33248c2ecf20Sopenharmony_ci int ldev_num = mbox->type5.ld.ldev_num; 33258c2ecf20Sopenharmony_ci 33268c2ecf20Sopenharmony_ci mbox->bytes[3] &= 0x7; 33278c2ecf20Sopenharmony_ci mbox->bytes[3] |= mbox->bytes[7] << 6; 33288c2ecf20Sopenharmony_ci mbox->bytes[7] = ldev_num; 33298c2ecf20Sopenharmony_ci} 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_cistatic inline void myrb_translate_from_rw_command(struct myrb_cmdblk *cmd_blk) 33328c2ecf20Sopenharmony_ci{ 33338c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 33348c2ecf20Sopenharmony_ci int ldev_num = mbox->bytes[7]; 33358c2ecf20Sopenharmony_ci 33368c2ecf20Sopenharmony_ci mbox->bytes[7] = mbox->bytes[3] >> 6; 33378c2ecf20Sopenharmony_ci mbox->bytes[3] &= 0x7; 33388c2ecf20Sopenharmony_ci mbox->bytes[3] |= ldev_num << 3; 33398c2ecf20Sopenharmony_ci} 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_cistatic void DAC960_P_qcmd(struct myrb_hba *cb, struct myrb_cmdblk *cmd_blk) 33428c2ecf20Sopenharmony_ci{ 33438c2ecf20Sopenharmony_ci void __iomem *base = cb->io_base; 33448c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox = &cmd_blk->mbox; 33458c2ecf20Sopenharmony_ci 33468c2ecf20Sopenharmony_ci switch (mbox->common.opcode) { 33478c2ecf20Sopenharmony_ci case MYRB_CMD_ENQUIRY: 33488c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_ENQUIRY_OLD; 33498c2ecf20Sopenharmony_ci break; 33508c2ecf20Sopenharmony_ci case MYRB_CMD_GET_DEVICE_STATE: 33518c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_GET_DEVICE_STATE_OLD; 33528c2ecf20Sopenharmony_ci break; 33538c2ecf20Sopenharmony_ci case MYRB_CMD_READ: 33548c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_READ_OLD; 33558c2ecf20Sopenharmony_ci myrb_translate_to_rw_command(cmd_blk); 33568c2ecf20Sopenharmony_ci break; 33578c2ecf20Sopenharmony_ci case MYRB_CMD_WRITE: 33588c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_WRITE_OLD; 33598c2ecf20Sopenharmony_ci myrb_translate_to_rw_command(cmd_blk); 33608c2ecf20Sopenharmony_ci break; 33618c2ecf20Sopenharmony_ci case MYRB_CMD_READ_SG: 33628c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_READ_SG_OLD; 33638c2ecf20Sopenharmony_ci myrb_translate_to_rw_command(cmd_blk); 33648c2ecf20Sopenharmony_ci break; 33658c2ecf20Sopenharmony_ci case MYRB_CMD_WRITE_SG: 33668c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_WRITE_SG_OLD; 33678c2ecf20Sopenharmony_ci myrb_translate_to_rw_command(cmd_blk); 33688c2ecf20Sopenharmony_ci break; 33698c2ecf20Sopenharmony_ci default: 33708c2ecf20Sopenharmony_ci break; 33718c2ecf20Sopenharmony_ci } 33728c2ecf20Sopenharmony_ci while (DAC960_PD_hw_mbox_is_full(base)) 33738c2ecf20Sopenharmony_ci udelay(1); 33748c2ecf20Sopenharmony_ci DAC960_PD_write_cmd_mbox(base, mbox); 33758c2ecf20Sopenharmony_ci DAC960_PD_hw_mbox_new_cmd(base); 33768c2ecf20Sopenharmony_ci} 33778c2ecf20Sopenharmony_ci 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_cistatic int DAC960_P_hw_init(struct pci_dev *pdev, 33808c2ecf20Sopenharmony_ci struct myrb_hba *cb, void __iomem *base) 33818c2ecf20Sopenharmony_ci{ 33828c2ecf20Sopenharmony_ci int timeout = 0; 33838c2ecf20Sopenharmony_ci unsigned char error, parm0, parm1; 33848c2ecf20Sopenharmony_ci 33858c2ecf20Sopenharmony_ci if (!request_region(cb->io_addr, 0x80, "myrb")) { 33868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IO port 0x%lx busy\n", 33878c2ecf20Sopenharmony_ci (unsigned long)cb->io_addr); 33888c2ecf20Sopenharmony_ci return -EBUSY; 33898c2ecf20Sopenharmony_ci } 33908c2ecf20Sopenharmony_ci DAC960_PD_disable_intr(base); 33918c2ecf20Sopenharmony_ci DAC960_PD_ack_hw_mbox_status(base); 33928c2ecf20Sopenharmony_ci udelay(1000); 33938c2ecf20Sopenharmony_ci while (DAC960_PD_init_in_progress(base) && 33948c2ecf20Sopenharmony_ci timeout < MYRB_MAILBOX_TIMEOUT) { 33958c2ecf20Sopenharmony_ci if (DAC960_PD_read_error_status(base, &error, 33968c2ecf20Sopenharmony_ci &parm0, &parm1) && 33978c2ecf20Sopenharmony_ci myrb_err_status(cb, error, parm0, parm1)) 33988c2ecf20Sopenharmony_ci return -EAGAIN; 33998c2ecf20Sopenharmony_ci udelay(10); 34008c2ecf20Sopenharmony_ci timeout++; 34018c2ecf20Sopenharmony_ci } 34028c2ecf20Sopenharmony_ci if (timeout == MYRB_MAILBOX_TIMEOUT) { 34038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 34048c2ecf20Sopenharmony_ci "Timeout waiting for Controller Initialisation\n"); 34058c2ecf20Sopenharmony_ci return -ETIMEDOUT; 34068c2ecf20Sopenharmony_ci } 34078c2ecf20Sopenharmony_ci if (!myrb_enable_mmio(cb, NULL)) { 34088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 34098c2ecf20Sopenharmony_ci "Unable to allocate DMA mapped memory\n"); 34108c2ecf20Sopenharmony_ci DAC960_PD_reset_ctrl(base); 34118c2ecf20Sopenharmony_ci return -ETIMEDOUT; 34128c2ecf20Sopenharmony_ci } 34138c2ecf20Sopenharmony_ci DAC960_PD_enable_intr(base); 34148c2ecf20Sopenharmony_ci cb->qcmd = DAC960_P_qcmd; 34158c2ecf20Sopenharmony_ci cb->disable_intr = DAC960_PD_disable_intr; 34168c2ecf20Sopenharmony_ci cb->reset = DAC960_PD_reset_ctrl; 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ci return 0; 34198c2ecf20Sopenharmony_ci} 34208c2ecf20Sopenharmony_ci 34218c2ecf20Sopenharmony_cistatic irqreturn_t DAC960_P_intr_handler(int irq, void *arg) 34228c2ecf20Sopenharmony_ci{ 34238c2ecf20Sopenharmony_ci struct myrb_hba *cb = arg; 34248c2ecf20Sopenharmony_ci void __iomem *base = cb->io_base; 34258c2ecf20Sopenharmony_ci unsigned long flags; 34268c2ecf20Sopenharmony_ci 34278c2ecf20Sopenharmony_ci spin_lock_irqsave(&cb->queue_lock, flags); 34288c2ecf20Sopenharmony_ci while (DAC960_PD_hw_mbox_status_available(base)) { 34298c2ecf20Sopenharmony_ci unsigned char id = DAC960_PD_read_status_cmd_ident(base); 34308c2ecf20Sopenharmony_ci struct scsi_cmnd *scmd = NULL; 34318c2ecf20Sopenharmony_ci struct myrb_cmdblk *cmd_blk = NULL; 34328c2ecf20Sopenharmony_ci union myrb_cmd_mbox *mbox; 34338c2ecf20Sopenharmony_ci enum myrb_cmd_opcode op; 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_ci if (id == MYRB_DCMD_TAG) 34378c2ecf20Sopenharmony_ci cmd_blk = &cb->dcmd_blk; 34388c2ecf20Sopenharmony_ci else if (id == MYRB_MCMD_TAG) 34398c2ecf20Sopenharmony_ci cmd_blk = &cb->mcmd_blk; 34408c2ecf20Sopenharmony_ci else { 34418c2ecf20Sopenharmony_ci scmd = scsi_host_find_tag(cb->host, id - 3); 34428c2ecf20Sopenharmony_ci if (scmd) 34438c2ecf20Sopenharmony_ci cmd_blk = scsi_cmd_priv(scmd); 34448c2ecf20Sopenharmony_ci } 34458c2ecf20Sopenharmony_ci if (cmd_blk) 34468c2ecf20Sopenharmony_ci cmd_blk->status = DAC960_PD_read_status(base); 34478c2ecf20Sopenharmony_ci else 34488c2ecf20Sopenharmony_ci dev_err(&cb->pdev->dev, 34498c2ecf20Sopenharmony_ci "Unhandled command completion %d\n", id); 34508c2ecf20Sopenharmony_ci 34518c2ecf20Sopenharmony_ci DAC960_PD_ack_intr(base); 34528c2ecf20Sopenharmony_ci DAC960_PD_ack_hw_mbox_status(base); 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_ci if (!cmd_blk) 34558c2ecf20Sopenharmony_ci continue; 34568c2ecf20Sopenharmony_ci 34578c2ecf20Sopenharmony_ci mbox = &cmd_blk->mbox; 34588c2ecf20Sopenharmony_ci op = mbox->common.opcode; 34598c2ecf20Sopenharmony_ci switch (op) { 34608c2ecf20Sopenharmony_ci case MYRB_CMD_ENQUIRY_OLD: 34618c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_ENQUIRY; 34628c2ecf20Sopenharmony_ci myrb_translate_enquiry(cb->enquiry); 34638c2ecf20Sopenharmony_ci break; 34648c2ecf20Sopenharmony_ci case MYRB_CMD_READ_OLD: 34658c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_READ; 34668c2ecf20Sopenharmony_ci myrb_translate_from_rw_command(cmd_blk); 34678c2ecf20Sopenharmony_ci break; 34688c2ecf20Sopenharmony_ci case MYRB_CMD_WRITE_OLD: 34698c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_WRITE; 34708c2ecf20Sopenharmony_ci myrb_translate_from_rw_command(cmd_blk); 34718c2ecf20Sopenharmony_ci break; 34728c2ecf20Sopenharmony_ci case MYRB_CMD_READ_SG_OLD: 34738c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_READ_SG; 34748c2ecf20Sopenharmony_ci myrb_translate_from_rw_command(cmd_blk); 34758c2ecf20Sopenharmony_ci break; 34768c2ecf20Sopenharmony_ci case MYRB_CMD_WRITE_SG_OLD: 34778c2ecf20Sopenharmony_ci mbox->common.opcode = MYRB_CMD_WRITE_SG; 34788c2ecf20Sopenharmony_ci myrb_translate_from_rw_command(cmd_blk); 34798c2ecf20Sopenharmony_ci break; 34808c2ecf20Sopenharmony_ci default: 34818c2ecf20Sopenharmony_ci break; 34828c2ecf20Sopenharmony_ci } 34838c2ecf20Sopenharmony_ci if (id < 3) 34848c2ecf20Sopenharmony_ci myrb_handle_cmdblk(cb, cmd_blk); 34858c2ecf20Sopenharmony_ci else 34868c2ecf20Sopenharmony_ci myrb_handle_scsi(cb, cmd_blk, scmd); 34878c2ecf20Sopenharmony_ci } 34888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cb->queue_lock, flags); 34898c2ecf20Sopenharmony_ci return IRQ_HANDLED; 34908c2ecf20Sopenharmony_ci} 34918c2ecf20Sopenharmony_ci 34928c2ecf20Sopenharmony_cistruct myrb_privdata DAC960_P_privdata = { 34938c2ecf20Sopenharmony_ci .hw_init = DAC960_P_hw_init, 34948c2ecf20Sopenharmony_ci .irq_handler = DAC960_P_intr_handler, 34958c2ecf20Sopenharmony_ci .mmio_size = DAC960_PD_mmio_size, 34968c2ecf20Sopenharmony_ci}; 34978c2ecf20Sopenharmony_ci 34988c2ecf20Sopenharmony_cistatic struct myrb_hba *myrb_detect(struct pci_dev *pdev, 34998c2ecf20Sopenharmony_ci const struct pci_device_id *entry) 35008c2ecf20Sopenharmony_ci{ 35018c2ecf20Sopenharmony_ci struct myrb_privdata *privdata = 35028c2ecf20Sopenharmony_ci (struct myrb_privdata *)entry->driver_data; 35038c2ecf20Sopenharmony_ci irq_handler_t irq_handler = privdata->irq_handler; 35048c2ecf20Sopenharmony_ci unsigned int mmio_size = privdata->mmio_size; 35058c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 35068c2ecf20Sopenharmony_ci struct myrb_hba *cb = NULL; 35078c2ecf20Sopenharmony_ci 35088c2ecf20Sopenharmony_ci shost = scsi_host_alloc(&myrb_template, sizeof(struct myrb_hba)); 35098c2ecf20Sopenharmony_ci if (!shost) { 35108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to allocate Controller\n"); 35118c2ecf20Sopenharmony_ci return NULL; 35128c2ecf20Sopenharmony_ci } 35138c2ecf20Sopenharmony_ci shost->max_cmd_len = 12; 35148c2ecf20Sopenharmony_ci shost->max_lun = 256; 35158c2ecf20Sopenharmony_ci cb = shost_priv(shost); 35168c2ecf20Sopenharmony_ci mutex_init(&cb->dcmd_mutex); 35178c2ecf20Sopenharmony_ci mutex_init(&cb->dma_mutex); 35188c2ecf20Sopenharmony_ci cb->pdev = pdev; 35198c2ecf20Sopenharmony_ci cb->host = shost; 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) { 35228c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable PCI device\n"); 35238c2ecf20Sopenharmony_ci scsi_host_put(shost); 35248c2ecf20Sopenharmony_ci return NULL; 35258c2ecf20Sopenharmony_ci } 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_ci if (privdata->hw_init == DAC960_PD_hw_init || 35288c2ecf20Sopenharmony_ci privdata->hw_init == DAC960_P_hw_init) { 35298c2ecf20Sopenharmony_ci cb->io_addr = pci_resource_start(pdev, 0); 35308c2ecf20Sopenharmony_ci cb->pci_addr = pci_resource_start(pdev, 1); 35318c2ecf20Sopenharmony_ci } else 35328c2ecf20Sopenharmony_ci cb->pci_addr = pci_resource_start(pdev, 0); 35338c2ecf20Sopenharmony_ci 35348c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, cb); 35358c2ecf20Sopenharmony_ci spin_lock_init(&cb->queue_lock); 35368c2ecf20Sopenharmony_ci if (mmio_size < PAGE_SIZE) 35378c2ecf20Sopenharmony_ci mmio_size = PAGE_SIZE; 35388c2ecf20Sopenharmony_ci cb->mmio_base = ioremap(cb->pci_addr & PAGE_MASK, mmio_size); 35398c2ecf20Sopenharmony_ci if (cb->mmio_base == NULL) { 35408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 35418c2ecf20Sopenharmony_ci "Unable to map Controller Register Window\n"); 35428c2ecf20Sopenharmony_ci goto failure; 35438c2ecf20Sopenharmony_ci } 35448c2ecf20Sopenharmony_ci 35458c2ecf20Sopenharmony_ci cb->io_base = cb->mmio_base + (cb->pci_addr & ~PAGE_MASK); 35468c2ecf20Sopenharmony_ci if (privdata->hw_init(pdev, cb, cb->io_base)) 35478c2ecf20Sopenharmony_ci goto failure; 35488c2ecf20Sopenharmony_ci 35498c2ecf20Sopenharmony_ci if (request_irq(pdev->irq, irq_handler, IRQF_SHARED, "myrb", cb) < 0) { 35508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 35518c2ecf20Sopenharmony_ci "Unable to acquire IRQ Channel %d\n", pdev->irq); 35528c2ecf20Sopenharmony_ci goto failure; 35538c2ecf20Sopenharmony_ci } 35548c2ecf20Sopenharmony_ci cb->irq = pdev->irq; 35558c2ecf20Sopenharmony_ci return cb; 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_cifailure: 35588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 35598c2ecf20Sopenharmony_ci "Failed to initialize Controller\n"); 35608c2ecf20Sopenharmony_ci myrb_cleanup(cb); 35618c2ecf20Sopenharmony_ci return NULL; 35628c2ecf20Sopenharmony_ci} 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_cistatic int myrb_probe(struct pci_dev *dev, const struct pci_device_id *entry) 35658c2ecf20Sopenharmony_ci{ 35668c2ecf20Sopenharmony_ci struct myrb_hba *cb; 35678c2ecf20Sopenharmony_ci int ret; 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci cb = myrb_detect(dev, entry); 35708c2ecf20Sopenharmony_ci if (!cb) 35718c2ecf20Sopenharmony_ci return -ENODEV; 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci ret = myrb_get_hba_config(cb); 35748c2ecf20Sopenharmony_ci if (ret < 0) { 35758c2ecf20Sopenharmony_ci myrb_cleanup(cb); 35768c2ecf20Sopenharmony_ci return ret; 35778c2ecf20Sopenharmony_ci } 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci if (!myrb_create_mempools(dev, cb)) { 35808c2ecf20Sopenharmony_ci ret = -ENOMEM; 35818c2ecf20Sopenharmony_ci goto failed; 35828c2ecf20Sopenharmony_ci } 35838c2ecf20Sopenharmony_ci 35848c2ecf20Sopenharmony_ci ret = scsi_add_host(cb->host, &dev->dev); 35858c2ecf20Sopenharmony_ci if (ret) { 35868c2ecf20Sopenharmony_ci dev_err(&dev->dev, "scsi_add_host failed with %d\n", ret); 35878c2ecf20Sopenharmony_ci myrb_destroy_mempools(cb); 35888c2ecf20Sopenharmony_ci goto failed; 35898c2ecf20Sopenharmony_ci } 35908c2ecf20Sopenharmony_ci scsi_scan_host(cb->host); 35918c2ecf20Sopenharmony_ci return 0; 35928c2ecf20Sopenharmony_cifailed: 35938c2ecf20Sopenharmony_ci myrb_cleanup(cb); 35948c2ecf20Sopenharmony_ci return ret; 35958c2ecf20Sopenharmony_ci} 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci 35988c2ecf20Sopenharmony_cistatic void myrb_remove(struct pci_dev *pdev) 35998c2ecf20Sopenharmony_ci{ 36008c2ecf20Sopenharmony_ci struct myrb_hba *cb = pci_get_drvdata(pdev); 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci shost_printk(KERN_NOTICE, cb->host, "Flushing Cache..."); 36038c2ecf20Sopenharmony_ci myrb_exec_type3(cb, MYRB_CMD_FLUSH, 0); 36048c2ecf20Sopenharmony_ci myrb_cleanup(cb); 36058c2ecf20Sopenharmony_ci myrb_destroy_mempools(cb); 36068c2ecf20Sopenharmony_ci} 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci 36098c2ecf20Sopenharmony_cistatic const struct pci_device_id myrb_id_table[] = { 36108c2ecf20Sopenharmony_ci { 36118c2ecf20Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_DEC, 36128c2ecf20Sopenharmony_ci PCI_DEVICE_ID_DEC_21285, 36138c2ecf20Sopenharmony_ci PCI_VENDOR_ID_MYLEX, 36148c2ecf20Sopenharmony_ci PCI_DEVICE_ID_MYLEX_DAC960_LA), 36158c2ecf20Sopenharmony_ci .driver_data = (unsigned long) &DAC960_LA_privdata, 36168c2ecf20Sopenharmony_ci }, 36178c2ecf20Sopenharmony_ci { 36188c2ecf20Sopenharmony_ci PCI_DEVICE_DATA(MYLEX, DAC960_PG, &DAC960_PG_privdata), 36198c2ecf20Sopenharmony_ci }, 36208c2ecf20Sopenharmony_ci { 36218c2ecf20Sopenharmony_ci PCI_DEVICE_DATA(MYLEX, DAC960_PD, &DAC960_PD_privdata), 36228c2ecf20Sopenharmony_ci }, 36238c2ecf20Sopenharmony_ci { 36248c2ecf20Sopenharmony_ci PCI_DEVICE_DATA(MYLEX, DAC960_P, &DAC960_P_privdata), 36258c2ecf20Sopenharmony_ci }, 36268c2ecf20Sopenharmony_ci {0, }, 36278c2ecf20Sopenharmony_ci}; 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, myrb_id_table); 36308c2ecf20Sopenharmony_ci 36318c2ecf20Sopenharmony_cistatic struct pci_driver myrb_pci_driver = { 36328c2ecf20Sopenharmony_ci .name = "myrb", 36338c2ecf20Sopenharmony_ci .id_table = myrb_id_table, 36348c2ecf20Sopenharmony_ci .probe = myrb_probe, 36358c2ecf20Sopenharmony_ci .remove = myrb_remove, 36368c2ecf20Sopenharmony_ci}; 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_cistatic int __init myrb_init_module(void) 36398c2ecf20Sopenharmony_ci{ 36408c2ecf20Sopenharmony_ci int ret; 36418c2ecf20Sopenharmony_ci 36428c2ecf20Sopenharmony_ci myrb_raid_template = raid_class_attach(&myrb_raid_functions); 36438c2ecf20Sopenharmony_ci if (!myrb_raid_template) 36448c2ecf20Sopenharmony_ci return -ENODEV; 36458c2ecf20Sopenharmony_ci 36468c2ecf20Sopenharmony_ci ret = pci_register_driver(&myrb_pci_driver); 36478c2ecf20Sopenharmony_ci if (ret) 36488c2ecf20Sopenharmony_ci raid_class_release(myrb_raid_template); 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci return ret; 36518c2ecf20Sopenharmony_ci} 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_cistatic void __exit myrb_cleanup_module(void) 36548c2ecf20Sopenharmony_ci{ 36558c2ecf20Sopenharmony_ci pci_unregister_driver(&myrb_pci_driver); 36568c2ecf20Sopenharmony_ci raid_class_release(myrb_raid_template); 36578c2ecf20Sopenharmony_ci} 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_cimodule_init(myrb_init_module); 36608c2ecf20Sopenharmony_cimodule_exit(myrb_cleanup_module); 36618c2ecf20Sopenharmony_ci 36628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mylex DAC960/AcceleRAID/eXtremeRAID driver (Block interface)"); 36638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hannes Reinecke <hare@suse.com>"); 36648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3665