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