162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Marvell UMI driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011 Marvell. <jyli@marvell.com>
662306a36Sopenharmony_ci*/
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/moduleparam.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/list.h>
1562306a36Sopenharmony_ci#include <linux/spinlock.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/ktime.h>
1962306a36Sopenharmony_ci#include <linux/blkdev.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <scsi/scsi.h>
2262306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
2362306a36Sopenharmony_ci#include <scsi/scsi_device.h>
2462306a36Sopenharmony_ci#include <scsi/scsi_host.h>
2562306a36Sopenharmony_ci#include <scsi/scsi_transport.h>
2662306a36Sopenharmony_ci#include <scsi/scsi_eh.h>
2762306a36Sopenharmony_ci#include <linux/uaccess.h>
2862306a36Sopenharmony_ci#include <linux/kthread.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "mvumi.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3362306a36Sopenharmony_ciMODULE_AUTHOR("jyli@marvell.com");
3462306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell UMI Driver");
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic const struct pci_device_id mvumi_pci_table[] = {
3762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, PCI_DEVICE_ID_MARVELL_MV9143) },
3862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, PCI_DEVICE_ID_MARVELL_MV9580) },
3962306a36Sopenharmony_ci	{ 0 }
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mvumi_pci_table);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void tag_init(struct mvumi_tag *st, unsigned short size)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	unsigned short i;
4762306a36Sopenharmony_ci	BUG_ON(size != st->size);
4862306a36Sopenharmony_ci	st->top = size;
4962306a36Sopenharmony_ci	for (i = 0; i < size; i++)
5062306a36Sopenharmony_ci		st->stack[i] = size - 1 - i;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic unsigned short tag_get_one(struct mvumi_hba *mhba, struct mvumi_tag *st)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	BUG_ON(st->top <= 0);
5662306a36Sopenharmony_ci	return st->stack[--st->top];
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void tag_release_one(struct mvumi_hba *mhba, struct mvumi_tag *st,
6062306a36Sopenharmony_ci							unsigned short tag)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	BUG_ON(st->top >= st->size);
6362306a36Sopenharmony_ci	st->stack[st->top++] = tag;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic bool tag_is_empty(struct mvumi_tag *st)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	if (st->top == 0)
6962306a36Sopenharmony_ci		return true;
7062306a36Sopenharmony_ci	else
7162306a36Sopenharmony_ci		return false;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void mvumi_unmap_pci_addr(struct pci_dev *dev, void **addr_array)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	for (i = 0; i < MAX_BASE_ADDRESS; i++)
7962306a36Sopenharmony_ci		if ((pci_resource_flags(dev, i) & IORESOURCE_MEM) &&
8062306a36Sopenharmony_ci								addr_array[i])
8162306a36Sopenharmony_ci			pci_iounmap(dev, addr_array[i]);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int mvumi_map_pci_addr(struct pci_dev *dev, void **addr_array)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	int i;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	for (i = 0; i < MAX_BASE_ADDRESS; i++) {
8962306a36Sopenharmony_ci		if (pci_resource_flags(dev, i) & IORESOURCE_MEM) {
9062306a36Sopenharmony_ci			addr_array[i] = pci_iomap(dev, i, 0);
9162306a36Sopenharmony_ci			if (!addr_array[i]) {
9262306a36Sopenharmony_ci				dev_err(&dev->dev, "failed to map Bar[%d]\n",
9362306a36Sopenharmony_ci									i);
9462306a36Sopenharmony_ci				mvumi_unmap_pci_addr(dev, addr_array);
9562306a36Sopenharmony_ci				return -ENOMEM;
9662306a36Sopenharmony_ci			}
9762306a36Sopenharmony_ci		} else
9862306a36Sopenharmony_ci			addr_array[i] = NULL;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		dev_dbg(&dev->dev, "Bar %d : %p.\n", i, addr_array[i]);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic struct mvumi_res *mvumi_alloc_mem_resource(struct mvumi_hba *mhba,
10762306a36Sopenharmony_ci				enum resource_type type, unsigned int size)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct mvumi_res *res = kzalloc(sizeof(*res), GFP_ATOMIC);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (!res) {
11262306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
11362306a36Sopenharmony_ci			"Failed to allocate memory for resource manager.\n");
11462306a36Sopenharmony_ci		return NULL;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	switch (type) {
11862306a36Sopenharmony_ci	case RESOURCE_CACHED_MEMORY:
11962306a36Sopenharmony_ci		res->virt_addr = kzalloc(size, GFP_ATOMIC);
12062306a36Sopenharmony_ci		if (!res->virt_addr) {
12162306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
12262306a36Sopenharmony_ci				"unable to allocate memory,size = %d.\n", size);
12362306a36Sopenharmony_ci			kfree(res);
12462306a36Sopenharmony_ci			return NULL;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci		break;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	case RESOURCE_UNCACHED_MEMORY:
12962306a36Sopenharmony_ci		size = round_up(size, 8);
13062306a36Sopenharmony_ci		res->virt_addr = dma_alloc_coherent(&mhba->pdev->dev, size,
13162306a36Sopenharmony_ci						    &res->bus_addr,
13262306a36Sopenharmony_ci						    GFP_KERNEL);
13362306a36Sopenharmony_ci		if (!res->virt_addr) {
13462306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
13562306a36Sopenharmony_ci					"unable to allocate consistent mem,"
13662306a36Sopenharmony_ci							"size = %d.\n", size);
13762306a36Sopenharmony_ci			kfree(res);
13862306a36Sopenharmony_ci			return NULL;
13962306a36Sopenharmony_ci		}
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	default:
14362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "unknown resource type %d.\n", type);
14462306a36Sopenharmony_ci		kfree(res);
14562306a36Sopenharmony_ci		return NULL;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	res->type = type;
14962306a36Sopenharmony_ci	res->size = size;
15062306a36Sopenharmony_ci	INIT_LIST_HEAD(&res->entry);
15162306a36Sopenharmony_ci	list_add_tail(&res->entry, &mhba->res_list);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return res;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic void mvumi_release_mem_resource(struct mvumi_hba *mhba)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct mvumi_res *res, *tmp;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	list_for_each_entry_safe(res, tmp, &mhba->res_list, entry) {
16162306a36Sopenharmony_ci		switch (res->type) {
16262306a36Sopenharmony_ci		case RESOURCE_UNCACHED_MEMORY:
16362306a36Sopenharmony_ci			dma_free_coherent(&mhba->pdev->dev, res->size,
16462306a36Sopenharmony_ci						res->virt_addr, res->bus_addr);
16562306a36Sopenharmony_ci			break;
16662306a36Sopenharmony_ci		case RESOURCE_CACHED_MEMORY:
16762306a36Sopenharmony_ci			kfree(res->virt_addr);
16862306a36Sopenharmony_ci			break;
16962306a36Sopenharmony_ci		default:
17062306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
17162306a36Sopenharmony_ci				"unknown resource type %d\n", res->type);
17262306a36Sopenharmony_ci			break;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci		list_del(&res->entry);
17562306a36Sopenharmony_ci		kfree(res);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci	mhba->fw_flag &= ~MVUMI_FW_ALLOC;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/**
18162306a36Sopenharmony_ci * mvumi_make_sgl -	Prepares  SGL
18262306a36Sopenharmony_ci * @mhba:		Adapter soft state
18362306a36Sopenharmony_ci * @scmd:		SCSI command from the mid-layer
18462306a36Sopenharmony_ci * @sgl_p:		SGL to be filled in
18562306a36Sopenharmony_ci * @sg_count:		return the number of SG elements
18662306a36Sopenharmony_ci *
18762306a36Sopenharmony_ci * If successful, this function returns 0. otherwise, it returns -1.
18862306a36Sopenharmony_ci */
18962306a36Sopenharmony_cistatic int mvumi_make_sgl(struct mvumi_hba *mhba, struct scsi_cmnd *scmd,
19062306a36Sopenharmony_ci					void *sgl_p, unsigned char *sg_count)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct scatterlist *sg;
19362306a36Sopenharmony_ci	struct mvumi_sgl *m_sg = (struct mvumi_sgl *) sgl_p;
19462306a36Sopenharmony_ci	unsigned int i;
19562306a36Sopenharmony_ci	unsigned int sgnum = scsi_sg_count(scmd);
19662306a36Sopenharmony_ci	dma_addr_t busaddr;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	*sg_count = dma_map_sg(&mhba->pdev->dev, scsi_sglist(scmd), sgnum,
19962306a36Sopenharmony_ci			       scmd->sc_data_direction);
20062306a36Sopenharmony_ci	if (*sg_count > mhba->max_sge) {
20162306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
20262306a36Sopenharmony_ci			"sg count[0x%x] is bigger than max sg[0x%x].\n",
20362306a36Sopenharmony_ci			*sg_count, mhba->max_sge);
20462306a36Sopenharmony_ci		dma_unmap_sg(&mhba->pdev->dev, scsi_sglist(scmd), sgnum,
20562306a36Sopenharmony_ci			     scmd->sc_data_direction);
20662306a36Sopenharmony_ci		return -1;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	scsi_for_each_sg(scmd, sg, *sg_count, i) {
20962306a36Sopenharmony_ci		busaddr = sg_dma_address(sg);
21062306a36Sopenharmony_ci		m_sg->baseaddr_l = cpu_to_le32(lower_32_bits(busaddr));
21162306a36Sopenharmony_ci		m_sg->baseaddr_h = cpu_to_le32(upper_32_bits(busaddr));
21262306a36Sopenharmony_ci		m_sg->flags = 0;
21362306a36Sopenharmony_ci		sgd_setsz(mhba, m_sg, cpu_to_le32(sg_dma_len(sg)));
21462306a36Sopenharmony_ci		if ((i + 1) == *sg_count)
21562306a36Sopenharmony_ci			m_sg->flags |= 1U << mhba->eot_flag;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		sgd_inc(mhba, m_sg);
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int mvumi_internal_cmd_sgl(struct mvumi_hba *mhba, struct mvumi_cmd *cmd,
22462306a36Sopenharmony_ci							unsigned int size)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct mvumi_sgl *m_sg;
22762306a36Sopenharmony_ci	void *virt_addr;
22862306a36Sopenharmony_ci	dma_addr_t phy_addr;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (size == 0)
23162306a36Sopenharmony_ci		return 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	virt_addr = dma_alloc_coherent(&mhba->pdev->dev, size, &phy_addr,
23462306a36Sopenharmony_ci				       GFP_KERNEL);
23562306a36Sopenharmony_ci	if (!virt_addr)
23662306a36Sopenharmony_ci		return -1;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	m_sg = (struct mvumi_sgl *) &cmd->frame->payload[0];
23962306a36Sopenharmony_ci	cmd->frame->sg_counts = 1;
24062306a36Sopenharmony_ci	cmd->data_buf = virt_addr;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	m_sg->baseaddr_l = cpu_to_le32(lower_32_bits(phy_addr));
24362306a36Sopenharmony_ci	m_sg->baseaddr_h = cpu_to_le32(upper_32_bits(phy_addr));
24462306a36Sopenharmony_ci	m_sg->flags = 1U << mhba->eot_flag;
24562306a36Sopenharmony_ci	sgd_setsz(mhba, m_sg, cpu_to_le32(size));
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic struct mvumi_cmd *mvumi_create_internal_cmd(struct mvumi_hba *mhba,
25162306a36Sopenharmony_ci				unsigned int buf_size)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct mvumi_cmd *cmd;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
25662306a36Sopenharmony_ci	if (!cmd) {
25762306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "failed to create a internal cmd\n");
25862306a36Sopenharmony_ci		return NULL;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci	INIT_LIST_HEAD(&cmd->queue_pointer);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	cmd->frame = dma_alloc_coherent(&mhba->pdev->dev, mhba->ib_max_size,
26362306a36Sopenharmony_ci			&cmd->frame_phys, GFP_KERNEL);
26462306a36Sopenharmony_ci	if (!cmd->frame) {
26562306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "failed to allocate memory for FW"
26662306a36Sopenharmony_ci			" frame,size = %d.\n", mhba->ib_max_size);
26762306a36Sopenharmony_ci		kfree(cmd);
26862306a36Sopenharmony_ci		return NULL;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (buf_size) {
27262306a36Sopenharmony_ci		if (mvumi_internal_cmd_sgl(mhba, cmd, buf_size)) {
27362306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev, "failed to allocate memory"
27462306a36Sopenharmony_ci						" for internal frame\n");
27562306a36Sopenharmony_ci			dma_free_coherent(&mhba->pdev->dev, mhba->ib_max_size,
27662306a36Sopenharmony_ci					cmd->frame, cmd->frame_phys);
27762306a36Sopenharmony_ci			kfree(cmd);
27862306a36Sopenharmony_ci			return NULL;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci	} else
28162306a36Sopenharmony_ci		cmd->frame->sg_counts = 0;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return cmd;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void mvumi_delete_internal_cmd(struct mvumi_hba *mhba,
28762306a36Sopenharmony_ci						struct mvumi_cmd *cmd)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct mvumi_sgl *m_sg;
29062306a36Sopenharmony_ci	unsigned int size;
29162306a36Sopenharmony_ci	dma_addr_t phy_addr;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (cmd && cmd->frame) {
29462306a36Sopenharmony_ci		if (cmd->frame->sg_counts) {
29562306a36Sopenharmony_ci			m_sg = (struct mvumi_sgl *) &cmd->frame->payload[0];
29662306a36Sopenharmony_ci			sgd_getsz(mhba, m_sg, size);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci			phy_addr = (dma_addr_t) m_sg->baseaddr_l |
29962306a36Sopenharmony_ci				(dma_addr_t) ((m_sg->baseaddr_h << 16) << 16);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci			dma_free_coherent(&mhba->pdev->dev, size, cmd->data_buf,
30262306a36Sopenharmony_ci								phy_addr);
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci		dma_free_coherent(&mhba->pdev->dev, mhba->ib_max_size,
30562306a36Sopenharmony_ci				cmd->frame, cmd->frame_phys);
30662306a36Sopenharmony_ci		kfree(cmd);
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/**
31162306a36Sopenharmony_ci * mvumi_get_cmd -	Get a command from the free pool
31262306a36Sopenharmony_ci * @mhba:		Adapter soft state
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * Returns a free command from the pool
31562306a36Sopenharmony_ci */
31662306a36Sopenharmony_cistatic struct mvumi_cmd *mvumi_get_cmd(struct mvumi_hba *mhba)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct mvumi_cmd *cmd = NULL;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (likely(!list_empty(&mhba->cmd_pool))) {
32162306a36Sopenharmony_ci		cmd = list_entry((&mhba->cmd_pool)->next,
32262306a36Sopenharmony_ci				struct mvumi_cmd, queue_pointer);
32362306a36Sopenharmony_ci		list_del_init(&cmd->queue_pointer);
32462306a36Sopenharmony_ci	} else
32562306a36Sopenharmony_ci		dev_warn(&mhba->pdev->dev, "command pool is empty!\n");
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return cmd;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/**
33162306a36Sopenharmony_ci * mvumi_return_cmd -	Return a cmd to free command pool
33262306a36Sopenharmony_ci * @mhba:		Adapter soft state
33362306a36Sopenharmony_ci * @cmd:		Command packet to be returned to free command pool
33462306a36Sopenharmony_ci */
33562306a36Sopenharmony_cistatic inline void mvumi_return_cmd(struct mvumi_hba *mhba,
33662306a36Sopenharmony_ci						struct mvumi_cmd *cmd)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	cmd->scmd = NULL;
33962306a36Sopenharmony_ci	list_add_tail(&cmd->queue_pointer, &mhba->cmd_pool);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/**
34362306a36Sopenharmony_ci * mvumi_free_cmds -	Free all the cmds in the free cmd pool
34462306a36Sopenharmony_ci * @mhba:		Adapter soft state
34562306a36Sopenharmony_ci */
34662306a36Sopenharmony_cistatic void mvumi_free_cmds(struct mvumi_hba *mhba)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct mvumi_cmd *cmd;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	while (!list_empty(&mhba->cmd_pool)) {
35162306a36Sopenharmony_ci		cmd = list_first_entry(&mhba->cmd_pool, struct mvumi_cmd,
35262306a36Sopenharmony_ci							queue_pointer);
35362306a36Sopenharmony_ci		list_del(&cmd->queue_pointer);
35462306a36Sopenharmony_ci		if (!(mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC))
35562306a36Sopenharmony_ci			kfree(cmd->frame);
35662306a36Sopenharmony_ci		kfree(cmd);
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/**
36162306a36Sopenharmony_ci * mvumi_alloc_cmds -	Allocates the command packets
36262306a36Sopenharmony_ci * @mhba:		Adapter soft state
36362306a36Sopenharmony_ci *
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic int mvumi_alloc_cmds(struct mvumi_hba *mhba)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	int i;
36862306a36Sopenharmony_ci	struct mvumi_cmd *cmd;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	for (i = 0; i < mhba->max_io; i++) {
37162306a36Sopenharmony_ci		cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
37262306a36Sopenharmony_ci		if (!cmd)
37362306a36Sopenharmony_ci			goto err_exit;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		INIT_LIST_HEAD(&cmd->queue_pointer);
37662306a36Sopenharmony_ci		list_add_tail(&cmd->queue_pointer, &mhba->cmd_pool);
37762306a36Sopenharmony_ci		if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) {
37862306a36Sopenharmony_ci			cmd->frame = mhba->ib_frame + i * mhba->ib_max_size;
37962306a36Sopenharmony_ci			cmd->frame_phys = mhba->ib_frame_phys
38062306a36Sopenharmony_ci						+ i * mhba->ib_max_size;
38162306a36Sopenharmony_ci		} else
38262306a36Sopenharmony_ci			cmd->frame = kzalloc(mhba->ib_max_size, GFP_KERNEL);
38362306a36Sopenharmony_ci		if (!cmd->frame)
38462306a36Sopenharmony_ci			goto err_exit;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci	return 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cierr_exit:
38962306a36Sopenharmony_ci	dev_err(&mhba->pdev->dev,
39062306a36Sopenharmony_ci			"failed to allocate memory for cmd[0x%x].\n", i);
39162306a36Sopenharmony_ci	while (!list_empty(&mhba->cmd_pool)) {
39262306a36Sopenharmony_ci		cmd = list_first_entry(&mhba->cmd_pool, struct mvumi_cmd,
39362306a36Sopenharmony_ci						queue_pointer);
39462306a36Sopenharmony_ci		list_del(&cmd->queue_pointer);
39562306a36Sopenharmony_ci		if (!(mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC))
39662306a36Sopenharmony_ci			kfree(cmd->frame);
39762306a36Sopenharmony_ci		kfree(cmd);
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci	return -ENOMEM;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic unsigned int mvumi_check_ib_list_9143(struct mvumi_hba *mhba)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	unsigned int ib_rp_reg;
40562306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	ib_rp_reg = ioread32(mhba->regs->inb_read_pointer);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (unlikely(((ib_rp_reg & regs->cl_slot_num_mask) ==
41062306a36Sopenharmony_ci			(mhba->ib_cur_slot & regs->cl_slot_num_mask)) &&
41162306a36Sopenharmony_ci			((ib_rp_reg & regs->cl_pointer_toggle)
41262306a36Sopenharmony_ci			 != (mhba->ib_cur_slot & regs->cl_pointer_toggle)))) {
41362306a36Sopenharmony_ci		dev_warn(&mhba->pdev->dev, "no free slot to use.\n");
41462306a36Sopenharmony_ci		return 0;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	if (atomic_read(&mhba->fw_outstanding) >= mhba->max_io) {
41762306a36Sopenharmony_ci		dev_warn(&mhba->pdev->dev, "firmware io overflow.\n");
41862306a36Sopenharmony_ci		return 0;
41962306a36Sopenharmony_ci	} else {
42062306a36Sopenharmony_ci		return mhba->max_io - atomic_read(&mhba->fw_outstanding);
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic unsigned int mvumi_check_ib_list_9580(struct mvumi_hba *mhba)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	unsigned int count;
42762306a36Sopenharmony_ci	if (atomic_read(&mhba->fw_outstanding) >= (mhba->max_io - 1))
42862306a36Sopenharmony_ci		return 0;
42962306a36Sopenharmony_ci	count = ioread32(mhba->ib_shadow);
43062306a36Sopenharmony_ci	if (count == 0xffff)
43162306a36Sopenharmony_ci		return 0;
43262306a36Sopenharmony_ci	return count;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void mvumi_get_ib_list_entry(struct mvumi_hba *mhba, void **ib_entry)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	unsigned int cur_ib_entry;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	cur_ib_entry = mhba->ib_cur_slot & mhba->regs->cl_slot_num_mask;
44062306a36Sopenharmony_ci	cur_ib_entry++;
44162306a36Sopenharmony_ci	if (cur_ib_entry >= mhba->list_num_io) {
44262306a36Sopenharmony_ci		cur_ib_entry -= mhba->list_num_io;
44362306a36Sopenharmony_ci		mhba->ib_cur_slot ^= mhba->regs->cl_pointer_toggle;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci	mhba->ib_cur_slot &= ~mhba->regs->cl_slot_num_mask;
44662306a36Sopenharmony_ci	mhba->ib_cur_slot |= (cur_ib_entry & mhba->regs->cl_slot_num_mask);
44762306a36Sopenharmony_ci	if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) {
44862306a36Sopenharmony_ci		*ib_entry = mhba->ib_list + cur_ib_entry *
44962306a36Sopenharmony_ci				sizeof(struct mvumi_dyn_list_entry);
45062306a36Sopenharmony_ci	} else {
45162306a36Sopenharmony_ci		*ib_entry = mhba->ib_list + cur_ib_entry * mhba->ib_max_size;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci	atomic_inc(&mhba->fw_outstanding);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void mvumi_send_ib_list_entry(struct mvumi_hba *mhba)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	iowrite32(0xffff, mhba->ib_shadow);
45962306a36Sopenharmony_ci	iowrite32(mhba->ib_cur_slot, mhba->regs->inb_write_pointer);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic char mvumi_check_ob_frame(struct mvumi_hba *mhba,
46362306a36Sopenharmony_ci		unsigned int cur_obf, struct mvumi_rsp_frame *p_outb_frame)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	unsigned short tag, request_id;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	udelay(1);
46862306a36Sopenharmony_ci	p_outb_frame = mhba->ob_list + cur_obf * mhba->ob_max_size;
46962306a36Sopenharmony_ci	request_id = p_outb_frame->request_id;
47062306a36Sopenharmony_ci	tag = p_outb_frame->tag;
47162306a36Sopenharmony_ci	if (tag > mhba->tag_pool.size) {
47262306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "ob frame data error\n");
47362306a36Sopenharmony_ci		return -1;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci	if (mhba->tag_cmd[tag] == NULL) {
47662306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "tag[0x%x] with NO command\n", tag);
47762306a36Sopenharmony_ci		return -1;
47862306a36Sopenharmony_ci	} else if (mhba->tag_cmd[tag]->request_id != request_id &&
47962306a36Sopenharmony_ci						mhba->request_id_enabled) {
48062306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev, "request ID from FW:0x%x,"
48162306a36Sopenharmony_ci					"cmd request ID:0x%x\n", request_id,
48262306a36Sopenharmony_ci					mhba->tag_cmd[tag]->request_id);
48362306a36Sopenharmony_ci			return -1;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int mvumi_check_ob_list_9143(struct mvumi_hba *mhba,
49062306a36Sopenharmony_ci			unsigned int *cur_obf, unsigned int *assign_obf_end)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	unsigned int ob_write, ob_write_shadow;
49362306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	do {
49662306a36Sopenharmony_ci		ob_write = ioread32(regs->outb_copy_pointer);
49762306a36Sopenharmony_ci		ob_write_shadow = ioread32(mhba->ob_shadow);
49862306a36Sopenharmony_ci	} while ((ob_write & regs->cl_slot_num_mask) != ob_write_shadow);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	*cur_obf = mhba->ob_cur_slot & mhba->regs->cl_slot_num_mask;
50162306a36Sopenharmony_ci	*assign_obf_end = ob_write & mhba->regs->cl_slot_num_mask;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if ((ob_write & regs->cl_pointer_toggle) !=
50462306a36Sopenharmony_ci			(mhba->ob_cur_slot & regs->cl_pointer_toggle)) {
50562306a36Sopenharmony_ci		*assign_obf_end += mhba->list_num_io;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci	return 0;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int mvumi_check_ob_list_9580(struct mvumi_hba *mhba,
51162306a36Sopenharmony_ci			unsigned int *cur_obf, unsigned int *assign_obf_end)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	unsigned int ob_write;
51462306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	ob_write = ioread32(regs->outb_read_pointer);
51762306a36Sopenharmony_ci	ob_write = ioread32(regs->outb_copy_pointer);
51862306a36Sopenharmony_ci	*cur_obf = mhba->ob_cur_slot & mhba->regs->cl_slot_num_mask;
51962306a36Sopenharmony_ci	*assign_obf_end = ob_write & mhba->regs->cl_slot_num_mask;
52062306a36Sopenharmony_ci	if (*assign_obf_end < *cur_obf)
52162306a36Sopenharmony_ci		*assign_obf_end += mhba->list_num_io;
52262306a36Sopenharmony_ci	else if (*assign_obf_end == *cur_obf)
52362306a36Sopenharmony_ci		return -1;
52462306a36Sopenharmony_ci	return 0;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic void mvumi_receive_ob_list_entry(struct mvumi_hba *mhba)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	unsigned int cur_obf, assign_obf_end, i;
53062306a36Sopenharmony_ci	struct mvumi_ob_data *ob_data;
53162306a36Sopenharmony_ci	struct mvumi_rsp_frame *p_outb_frame;
53262306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (mhba->instancet->check_ob_list(mhba, &cur_obf, &assign_obf_end))
53562306a36Sopenharmony_ci		return;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	for (i = (assign_obf_end - cur_obf); i != 0; i--) {
53862306a36Sopenharmony_ci		cur_obf++;
53962306a36Sopenharmony_ci		if (cur_obf >= mhba->list_num_io) {
54062306a36Sopenharmony_ci			cur_obf -= mhba->list_num_io;
54162306a36Sopenharmony_ci			mhba->ob_cur_slot ^= regs->cl_pointer_toggle;
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		p_outb_frame = mhba->ob_list + cur_obf * mhba->ob_max_size;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		/* Copy pointer may point to entry in outbound list
54762306a36Sopenharmony_ci		*  before entry has valid data
54862306a36Sopenharmony_ci		*/
54962306a36Sopenharmony_ci		if (unlikely(p_outb_frame->tag > mhba->tag_pool.size ||
55062306a36Sopenharmony_ci			mhba->tag_cmd[p_outb_frame->tag] == NULL ||
55162306a36Sopenharmony_ci			p_outb_frame->request_id !=
55262306a36Sopenharmony_ci				mhba->tag_cmd[p_outb_frame->tag]->request_id))
55362306a36Sopenharmony_ci			if (mvumi_check_ob_frame(mhba, cur_obf, p_outb_frame))
55462306a36Sopenharmony_ci				continue;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		if (!list_empty(&mhba->ob_data_list)) {
55762306a36Sopenharmony_ci			ob_data = (struct mvumi_ob_data *)
55862306a36Sopenharmony_ci				list_first_entry(&mhba->ob_data_list,
55962306a36Sopenharmony_ci					struct mvumi_ob_data, list);
56062306a36Sopenharmony_ci			list_del_init(&ob_data->list);
56162306a36Sopenharmony_ci		} else {
56262306a36Sopenharmony_ci			ob_data = NULL;
56362306a36Sopenharmony_ci			if (cur_obf == 0) {
56462306a36Sopenharmony_ci				cur_obf = mhba->list_num_io - 1;
56562306a36Sopenharmony_ci				mhba->ob_cur_slot ^= regs->cl_pointer_toggle;
56662306a36Sopenharmony_ci			} else
56762306a36Sopenharmony_ci				cur_obf -= 1;
56862306a36Sopenharmony_ci			break;
56962306a36Sopenharmony_ci		}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		memcpy(ob_data->data, p_outb_frame, mhba->ob_max_size);
57262306a36Sopenharmony_ci		p_outb_frame->tag = 0xff;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		list_add_tail(&ob_data->list, &mhba->free_ob_list);
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci	mhba->ob_cur_slot &= ~regs->cl_slot_num_mask;
57762306a36Sopenharmony_ci	mhba->ob_cur_slot |= (cur_obf & regs->cl_slot_num_mask);
57862306a36Sopenharmony_ci	iowrite32(mhba->ob_cur_slot, regs->outb_read_pointer);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic void mvumi_reset(struct mvumi_hba *mhba)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	iowrite32(0, regs->enpointa_mask_reg);
58662306a36Sopenharmony_ci	if (ioread32(regs->arm_to_pciea_msg1) != HANDSHAKE_DONESTATE)
58762306a36Sopenharmony_ci		return;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	iowrite32(DRBL_SOFT_RESET, regs->pciea_to_arm_drbl_reg);
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic unsigned char mvumi_start(struct mvumi_hba *mhba);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic int mvumi_wait_for_outstanding(struct mvumi_hba *mhba)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	mhba->fw_state = FW_STATE_ABORT;
59762306a36Sopenharmony_ci	mvumi_reset(mhba);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (mvumi_start(mhba))
60062306a36Sopenharmony_ci		return FAILED;
60162306a36Sopenharmony_ci	else
60262306a36Sopenharmony_ci		return SUCCESS;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic int mvumi_wait_for_fw(struct mvumi_hba *mhba)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
60862306a36Sopenharmony_ci	u32 tmp;
60962306a36Sopenharmony_ci	unsigned long before;
61062306a36Sopenharmony_ci	before = jiffies;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	iowrite32(0, regs->enpointa_mask_reg);
61362306a36Sopenharmony_ci	tmp = ioread32(regs->arm_to_pciea_msg1);
61462306a36Sopenharmony_ci	while (tmp != HANDSHAKE_READYSTATE) {
61562306a36Sopenharmony_ci		iowrite32(DRBL_MU_RESET, regs->pciea_to_arm_drbl_reg);
61662306a36Sopenharmony_ci		if (time_after(jiffies, before + FW_MAX_DELAY * HZ)) {
61762306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
61862306a36Sopenharmony_ci				"FW reset failed [0x%x].\n", tmp);
61962306a36Sopenharmony_ci			return FAILED;
62062306a36Sopenharmony_ci		}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		msleep(500);
62362306a36Sopenharmony_ci		rmb();
62462306a36Sopenharmony_ci		tmp = ioread32(regs->arm_to_pciea_msg1);
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return SUCCESS;
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic void mvumi_backup_bar_addr(struct mvumi_hba *mhba)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	unsigned char i;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	for (i = 0; i < MAX_BASE_ADDRESS; i++) {
63562306a36Sopenharmony_ci		pci_read_config_dword(mhba->pdev, 0x10 + i * 4,
63662306a36Sopenharmony_ci						&mhba->pci_base[i]);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic void mvumi_restore_bar_addr(struct mvumi_hba *mhba)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	unsigned char i;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	for (i = 0; i < MAX_BASE_ADDRESS; i++) {
64562306a36Sopenharmony_ci		if (mhba->pci_base[i])
64662306a36Sopenharmony_ci			pci_write_config_dword(mhba->pdev, 0x10 + i * 4,
64762306a36Sopenharmony_ci						mhba->pci_base[i]);
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic int mvumi_pci_set_master(struct pci_dev *pdev)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	int ret = 0;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	pci_set_master(pdev);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (IS_DMA64) {
65862306a36Sopenharmony_ci		if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)))
65962306a36Sopenharmony_ci			ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
66062306a36Sopenharmony_ci	} else
66162306a36Sopenharmony_ci		ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return ret;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int mvumi_reset_host_9580(struct mvumi_hba *mhba)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	mhba->fw_state = FW_STATE_ABORT;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	iowrite32(0, mhba->regs->reset_enable);
67162306a36Sopenharmony_ci	iowrite32(0xf, mhba->regs->reset_request);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	iowrite32(0x10, mhba->regs->reset_enable);
67462306a36Sopenharmony_ci	iowrite32(0x10, mhba->regs->reset_request);
67562306a36Sopenharmony_ci	msleep(100);
67662306a36Sopenharmony_ci	pci_disable_device(mhba->pdev);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (pci_enable_device(mhba->pdev)) {
67962306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "enable device failed\n");
68062306a36Sopenharmony_ci		return FAILED;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci	if (mvumi_pci_set_master(mhba->pdev)) {
68362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "set master failed\n");
68462306a36Sopenharmony_ci		return FAILED;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci	mvumi_restore_bar_addr(mhba);
68762306a36Sopenharmony_ci	if (mvumi_wait_for_fw(mhba) == FAILED)
68862306a36Sopenharmony_ci		return FAILED;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return mvumi_wait_for_outstanding(mhba);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int mvumi_reset_host_9143(struct mvumi_hba *mhba)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	return mvumi_wait_for_outstanding(mhba);
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int mvumi_host_reset(struct scsi_cmnd *scmd)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct mvumi_hba *mhba;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	mhba = (struct mvumi_hba *) scmd->device->host->hostdata;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	scmd_printk(KERN_NOTICE, scmd, "RESET -%u cmd=%x retries=%x\n",
70562306a36Sopenharmony_ci			scsi_cmd_to_rq(scmd)->tag, scmd->cmnd[0], scmd->retries);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return mhba->instancet->reset_host(mhba);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int mvumi_issue_blocked_cmd(struct mvumi_hba *mhba,
71162306a36Sopenharmony_ci						struct mvumi_cmd *cmd)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	unsigned long flags;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	cmd->cmd_status = REQ_STATUS_PENDING;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (atomic_read(&cmd->sync_cmd)) {
71862306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
71962306a36Sopenharmony_ci			"last blocked cmd not finished, sync_cmd = %d\n",
72062306a36Sopenharmony_ci						atomic_read(&cmd->sync_cmd));
72162306a36Sopenharmony_ci		BUG_ON(1);
72262306a36Sopenharmony_ci		return -1;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci	atomic_inc(&cmd->sync_cmd);
72562306a36Sopenharmony_ci	spin_lock_irqsave(mhba->shost->host_lock, flags);
72662306a36Sopenharmony_ci	mhba->instancet->fire_cmd(mhba, cmd);
72762306a36Sopenharmony_ci	spin_unlock_irqrestore(mhba->shost->host_lock, flags);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	wait_event_timeout(mhba->int_cmd_wait_q,
73062306a36Sopenharmony_ci		(cmd->cmd_status != REQ_STATUS_PENDING),
73162306a36Sopenharmony_ci		MVUMI_INTERNAL_CMD_WAIT_TIME * HZ);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/* command timeout */
73462306a36Sopenharmony_ci	if (atomic_read(&cmd->sync_cmd)) {
73562306a36Sopenharmony_ci		spin_lock_irqsave(mhba->shost->host_lock, flags);
73662306a36Sopenharmony_ci		atomic_dec(&cmd->sync_cmd);
73762306a36Sopenharmony_ci		if (mhba->tag_cmd[cmd->frame->tag]) {
73862306a36Sopenharmony_ci			mhba->tag_cmd[cmd->frame->tag] = NULL;
73962306a36Sopenharmony_ci			dev_warn(&mhba->pdev->dev, "TIMEOUT:release tag [%d]\n",
74062306a36Sopenharmony_ci							cmd->frame->tag);
74162306a36Sopenharmony_ci			tag_release_one(mhba, &mhba->tag_pool, cmd->frame->tag);
74262306a36Sopenharmony_ci		}
74362306a36Sopenharmony_ci		if (!list_empty(&cmd->queue_pointer)) {
74462306a36Sopenharmony_ci			dev_warn(&mhba->pdev->dev,
74562306a36Sopenharmony_ci				"TIMEOUT:A internal command doesn't send!\n");
74662306a36Sopenharmony_ci			list_del_init(&cmd->queue_pointer);
74762306a36Sopenharmony_ci		} else
74862306a36Sopenharmony_ci			atomic_dec(&mhba->fw_outstanding);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		spin_unlock_irqrestore(mhba->shost->host_lock, flags);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci	return 0;
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic void mvumi_release_fw(struct mvumi_hba *mhba)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	mvumi_free_cmds(mhba);
75862306a36Sopenharmony_ci	mvumi_release_mem_resource(mhba);
75962306a36Sopenharmony_ci	mvumi_unmap_pci_addr(mhba->pdev, mhba->base_addr);
76062306a36Sopenharmony_ci	dma_free_coherent(&mhba->pdev->dev, HSP_MAX_SIZE,
76162306a36Sopenharmony_ci		mhba->handshake_page, mhba->handshake_page_phys);
76262306a36Sopenharmony_ci	kfree(mhba->regs);
76362306a36Sopenharmony_ci	pci_release_regions(mhba->pdev);
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_cistatic unsigned char mvumi_flush_cache(struct mvumi_hba *mhba)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	struct mvumi_cmd *cmd;
76962306a36Sopenharmony_ci	struct mvumi_msg_frame *frame;
77062306a36Sopenharmony_ci	unsigned char device_id, retry = 0;
77162306a36Sopenharmony_ci	unsigned char bitcount = sizeof(unsigned char) * 8;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	for (device_id = 0; device_id < mhba->max_target_id; device_id++) {
77462306a36Sopenharmony_ci		if (!(mhba->target_map[device_id / bitcount] &
77562306a36Sopenharmony_ci				(1 << (device_id % bitcount))))
77662306a36Sopenharmony_ci			continue;
77762306a36Sopenharmony_ciget_cmd:	cmd = mvumi_create_internal_cmd(mhba, 0);
77862306a36Sopenharmony_ci		if (!cmd) {
77962306a36Sopenharmony_ci			if (retry++ >= 5) {
78062306a36Sopenharmony_ci				dev_err(&mhba->pdev->dev, "failed to get memory"
78162306a36Sopenharmony_ci					" for internal flush cache cmd for "
78262306a36Sopenharmony_ci					"device %d", device_id);
78362306a36Sopenharmony_ci				retry = 0;
78462306a36Sopenharmony_ci				continue;
78562306a36Sopenharmony_ci			} else
78662306a36Sopenharmony_ci				goto get_cmd;
78762306a36Sopenharmony_ci		}
78862306a36Sopenharmony_ci		cmd->scmd = NULL;
78962306a36Sopenharmony_ci		cmd->cmd_status = REQ_STATUS_PENDING;
79062306a36Sopenharmony_ci		atomic_set(&cmd->sync_cmd, 0);
79162306a36Sopenharmony_ci		frame = cmd->frame;
79262306a36Sopenharmony_ci		frame->req_function = CL_FUN_SCSI_CMD;
79362306a36Sopenharmony_ci		frame->device_id = device_id;
79462306a36Sopenharmony_ci		frame->cmd_flag = CMD_FLAG_NON_DATA;
79562306a36Sopenharmony_ci		frame->data_transfer_length = 0;
79662306a36Sopenharmony_ci		frame->cdb_length = MAX_COMMAND_SIZE;
79762306a36Sopenharmony_ci		memset(frame->cdb, 0, MAX_COMMAND_SIZE);
79862306a36Sopenharmony_ci		frame->cdb[0] = SCSI_CMD_MARVELL_SPECIFIC;
79962306a36Sopenharmony_ci		frame->cdb[1] = CDB_CORE_MODULE;
80062306a36Sopenharmony_ci		frame->cdb[2] = CDB_CORE_SHUTDOWN;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci		mvumi_issue_blocked_cmd(mhba, cmd);
80362306a36Sopenharmony_ci		if (cmd->cmd_status != SAM_STAT_GOOD) {
80462306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
80562306a36Sopenharmony_ci				"device %d flush cache failed, status=0x%x.\n",
80662306a36Sopenharmony_ci				device_id, cmd->cmd_status);
80762306a36Sopenharmony_ci		}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci		mvumi_delete_internal_cmd(mhba, cmd);
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci	return 0;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic unsigned char
81562306a36Sopenharmony_cimvumi_calculate_checksum(struct mvumi_hs_header *p_header,
81662306a36Sopenharmony_ci							unsigned short len)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	unsigned char *ptr;
81962306a36Sopenharmony_ci	unsigned char ret = 0, i;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	ptr = (unsigned char *) p_header->frame_content;
82262306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
82362306a36Sopenharmony_ci		ret ^= *ptr;
82462306a36Sopenharmony_ci		ptr++;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	return ret;
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cistatic void mvumi_hs_build_page(struct mvumi_hba *mhba,
83162306a36Sopenharmony_ci				struct mvumi_hs_header *hs_header)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct mvumi_hs_page2 *hs_page2;
83462306a36Sopenharmony_ci	struct mvumi_hs_page4 *hs_page4;
83562306a36Sopenharmony_ci	struct mvumi_hs_page3 *hs_page3;
83662306a36Sopenharmony_ci	u64 time;
83762306a36Sopenharmony_ci	u64 local_time;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	switch (hs_header->page_code) {
84062306a36Sopenharmony_ci	case HS_PAGE_HOST_INFO:
84162306a36Sopenharmony_ci		hs_page2 = (struct mvumi_hs_page2 *) hs_header;
84262306a36Sopenharmony_ci		hs_header->frame_length = sizeof(*hs_page2) - 4;
84362306a36Sopenharmony_ci		memset(hs_header->frame_content, 0, hs_header->frame_length);
84462306a36Sopenharmony_ci		hs_page2->host_type = 3; /* 3 mean linux*/
84562306a36Sopenharmony_ci		if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC)
84662306a36Sopenharmony_ci			hs_page2->host_cap = 0x08;/* host dynamic source mode */
84762306a36Sopenharmony_ci		hs_page2->host_ver.ver_major = VER_MAJOR;
84862306a36Sopenharmony_ci		hs_page2->host_ver.ver_minor = VER_MINOR;
84962306a36Sopenharmony_ci		hs_page2->host_ver.ver_oem = VER_OEM;
85062306a36Sopenharmony_ci		hs_page2->host_ver.ver_build = VER_BUILD;
85162306a36Sopenharmony_ci		hs_page2->system_io_bus = 0;
85262306a36Sopenharmony_ci		hs_page2->slot_number = 0;
85362306a36Sopenharmony_ci		hs_page2->intr_level = 0;
85462306a36Sopenharmony_ci		hs_page2->intr_vector = 0;
85562306a36Sopenharmony_ci		time = ktime_get_real_seconds();
85662306a36Sopenharmony_ci		local_time = (time - (sys_tz.tz_minuteswest * 60));
85762306a36Sopenharmony_ci		hs_page2->seconds_since1970 = local_time;
85862306a36Sopenharmony_ci		hs_header->checksum = mvumi_calculate_checksum(hs_header,
85962306a36Sopenharmony_ci						hs_header->frame_length);
86062306a36Sopenharmony_ci		break;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	case HS_PAGE_FIRM_CTL:
86362306a36Sopenharmony_ci		hs_page3 = (struct mvumi_hs_page3 *) hs_header;
86462306a36Sopenharmony_ci		hs_header->frame_length = sizeof(*hs_page3) - 4;
86562306a36Sopenharmony_ci		memset(hs_header->frame_content, 0, hs_header->frame_length);
86662306a36Sopenharmony_ci		hs_header->checksum = mvumi_calculate_checksum(hs_header,
86762306a36Sopenharmony_ci						hs_header->frame_length);
86862306a36Sopenharmony_ci		break;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	case HS_PAGE_CL_INFO:
87162306a36Sopenharmony_ci		hs_page4 = (struct mvumi_hs_page4 *) hs_header;
87262306a36Sopenharmony_ci		hs_header->frame_length = sizeof(*hs_page4) - 4;
87362306a36Sopenharmony_ci		memset(hs_header->frame_content, 0, hs_header->frame_length);
87462306a36Sopenharmony_ci		hs_page4->ib_baseaddr_l = lower_32_bits(mhba->ib_list_phys);
87562306a36Sopenharmony_ci		hs_page4->ib_baseaddr_h = upper_32_bits(mhba->ib_list_phys);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci		hs_page4->ob_baseaddr_l = lower_32_bits(mhba->ob_list_phys);
87862306a36Sopenharmony_ci		hs_page4->ob_baseaddr_h = upper_32_bits(mhba->ob_list_phys);
87962306a36Sopenharmony_ci		hs_page4->ib_entry_size = mhba->ib_max_size_setting;
88062306a36Sopenharmony_ci		hs_page4->ob_entry_size = mhba->ob_max_size_setting;
88162306a36Sopenharmony_ci		if (mhba->hba_capability
88262306a36Sopenharmony_ci			& HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF) {
88362306a36Sopenharmony_ci			hs_page4->ob_depth = find_first_bit((unsigned long *)
88462306a36Sopenharmony_ci							    &mhba->list_num_io,
88562306a36Sopenharmony_ci							    BITS_PER_LONG);
88662306a36Sopenharmony_ci			hs_page4->ib_depth = find_first_bit((unsigned long *)
88762306a36Sopenharmony_ci							    &mhba->list_num_io,
88862306a36Sopenharmony_ci							    BITS_PER_LONG);
88962306a36Sopenharmony_ci		} else {
89062306a36Sopenharmony_ci			hs_page4->ob_depth = (u8) mhba->list_num_io;
89162306a36Sopenharmony_ci			hs_page4->ib_depth = (u8) mhba->list_num_io;
89262306a36Sopenharmony_ci		}
89362306a36Sopenharmony_ci		hs_header->checksum = mvumi_calculate_checksum(hs_header,
89462306a36Sopenharmony_ci						hs_header->frame_length);
89562306a36Sopenharmony_ci		break;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	default:
89862306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "cannot build page, code[0x%x]\n",
89962306a36Sopenharmony_ci			hs_header->page_code);
90062306a36Sopenharmony_ci		break;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci/**
90562306a36Sopenharmony_ci * mvumi_init_data -	Initialize requested date for FW
90662306a36Sopenharmony_ci * @mhba:			Adapter soft state
90762306a36Sopenharmony_ci */
90862306a36Sopenharmony_cistatic int mvumi_init_data(struct mvumi_hba *mhba)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	struct mvumi_ob_data *ob_pool;
91162306a36Sopenharmony_ci	struct mvumi_res *res_mgnt;
91262306a36Sopenharmony_ci	unsigned int tmp_size, offset, i;
91362306a36Sopenharmony_ci	void *virmem, *v;
91462306a36Sopenharmony_ci	dma_addr_t p;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if (mhba->fw_flag & MVUMI_FW_ALLOC)
91762306a36Sopenharmony_ci		return 0;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	tmp_size = mhba->ib_max_size * mhba->max_io;
92062306a36Sopenharmony_ci	if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC)
92162306a36Sopenharmony_ci		tmp_size += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	tmp_size += 128 + mhba->ob_max_size * mhba->max_io;
92462306a36Sopenharmony_ci	tmp_size += 8 + sizeof(u32)*2 + 16;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	res_mgnt = mvumi_alloc_mem_resource(mhba,
92762306a36Sopenharmony_ci					RESOURCE_UNCACHED_MEMORY, tmp_size);
92862306a36Sopenharmony_ci	if (!res_mgnt) {
92962306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
93062306a36Sopenharmony_ci			"failed to allocate memory for inbound list\n");
93162306a36Sopenharmony_ci		goto fail_alloc_dma_buf;
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	p = res_mgnt->bus_addr;
93562306a36Sopenharmony_ci	v = res_mgnt->virt_addr;
93662306a36Sopenharmony_ci	/* ib_list */
93762306a36Sopenharmony_ci	offset = round_up(p, 128) - p;
93862306a36Sopenharmony_ci	p += offset;
93962306a36Sopenharmony_ci	v += offset;
94062306a36Sopenharmony_ci	mhba->ib_list = v;
94162306a36Sopenharmony_ci	mhba->ib_list_phys = p;
94262306a36Sopenharmony_ci	if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) {
94362306a36Sopenharmony_ci		v += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io;
94462306a36Sopenharmony_ci		p += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io;
94562306a36Sopenharmony_ci		mhba->ib_frame = v;
94662306a36Sopenharmony_ci		mhba->ib_frame_phys = p;
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci	v += mhba->ib_max_size * mhba->max_io;
94962306a36Sopenharmony_ci	p += mhba->ib_max_size * mhba->max_io;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	/* ib shadow */
95262306a36Sopenharmony_ci	offset = round_up(p, 8) - p;
95362306a36Sopenharmony_ci	p += offset;
95462306a36Sopenharmony_ci	v += offset;
95562306a36Sopenharmony_ci	mhba->ib_shadow = v;
95662306a36Sopenharmony_ci	mhba->ib_shadow_phys = p;
95762306a36Sopenharmony_ci	p += sizeof(u32)*2;
95862306a36Sopenharmony_ci	v += sizeof(u32)*2;
95962306a36Sopenharmony_ci	/* ob shadow */
96062306a36Sopenharmony_ci	if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) {
96162306a36Sopenharmony_ci		offset = round_up(p, 8) - p;
96262306a36Sopenharmony_ci		p += offset;
96362306a36Sopenharmony_ci		v += offset;
96462306a36Sopenharmony_ci		mhba->ob_shadow = v;
96562306a36Sopenharmony_ci		mhba->ob_shadow_phys = p;
96662306a36Sopenharmony_ci		p += 8;
96762306a36Sopenharmony_ci		v += 8;
96862306a36Sopenharmony_ci	} else {
96962306a36Sopenharmony_ci		offset = round_up(p, 4) - p;
97062306a36Sopenharmony_ci		p += offset;
97162306a36Sopenharmony_ci		v += offset;
97262306a36Sopenharmony_ci		mhba->ob_shadow = v;
97362306a36Sopenharmony_ci		mhba->ob_shadow_phys = p;
97462306a36Sopenharmony_ci		p += 4;
97562306a36Sopenharmony_ci		v += 4;
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/* ob list */
97962306a36Sopenharmony_ci	offset = round_up(p, 128) - p;
98062306a36Sopenharmony_ci	p += offset;
98162306a36Sopenharmony_ci	v += offset;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	mhba->ob_list = v;
98462306a36Sopenharmony_ci	mhba->ob_list_phys = p;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	/* ob data pool */
98762306a36Sopenharmony_ci	tmp_size = mhba->max_io * (mhba->ob_max_size + sizeof(*ob_pool));
98862306a36Sopenharmony_ci	tmp_size = round_up(tmp_size, 8);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	res_mgnt = mvumi_alloc_mem_resource(mhba,
99162306a36Sopenharmony_ci				RESOURCE_CACHED_MEMORY, tmp_size);
99262306a36Sopenharmony_ci	if (!res_mgnt) {
99362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
99462306a36Sopenharmony_ci			"failed to allocate memory for outbound data buffer\n");
99562306a36Sopenharmony_ci		goto fail_alloc_dma_buf;
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci	virmem = res_mgnt->virt_addr;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	for (i = mhba->max_io; i != 0; i--) {
100062306a36Sopenharmony_ci		ob_pool = (struct mvumi_ob_data *) virmem;
100162306a36Sopenharmony_ci		list_add_tail(&ob_pool->list, &mhba->ob_data_list);
100262306a36Sopenharmony_ci		virmem += mhba->ob_max_size + sizeof(*ob_pool);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	tmp_size = sizeof(unsigned short) * mhba->max_io +
100662306a36Sopenharmony_ci				sizeof(struct mvumi_cmd *) * mhba->max_io;
100762306a36Sopenharmony_ci	tmp_size += round_up(mhba->max_target_id, sizeof(unsigned char) * 8) /
100862306a36Sopenharmony_ci						(sizeof(unsigned char) * 8);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	res_mgnt = mvumi_alloc_mem_resource(mhba,
101162306a36Sopenharmony_ci				RESOURCE_CACHED_MEMORY, tmp_size);
101262306a36Sopenharmony_ci	if (!res_mgnt) {
101362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
101462306a36Sopenharmony_ci			"failed to allocate memory for tag and target map\n");
101562306a36Sopenharmony_ci		goto fail_alloc_dma_buf;
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	virmem = res_mgnt->virt_addr;
101962306a36Sopenharmony_ci	mhba->tag_pool.stack = virmem;
102062306a36Sopenharmony_ci	mhba->tag_pool.size = mhba->max_io;
102162306a36Sopenharmony_ci	tag_init(&mhba->tag_pool, mhba->max_io);
102262306a36Sopenharmony_ci	virmem += sizeof(unsigned short) * mhba->max_io;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	mhba->tag_cmd = virmem;
102562306a36Sopenharmony_ci	virmem += sizeof(struct mvumi_cmd *) * mhba->max_io;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	mhba->target_map = virmem;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	mhba->fw_flag |= MVUMI_FW_ALLOC;
103062306a36Sopenharmony_ci	return 0;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cifail_alloc_dma_buf:
103362306a36Sopenharmony_ci	mvumi_release_mem_resource(mhba);
103462306a36Sopenharmony_ci	return -1;
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic int mvumi_hs_process_page(struct mvumi_hba *mhba,
103862306a36Sopenharmony_ci				struct mvumi_hs_header *hs_header)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct mvumi_hs_page1 *hs_page1;
104162306a36Sopenharmony_ci	unsigned char page_checksum;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	page_checksum = mvumi_calculate_checksum(hs_header,
104462306a36Sopenharmony_ci						hs_header->frame_length);
104562306a36Sopenharmony_ci	if (page_checksum != hs_header->checksum) {
104662306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "checksum error\n");
104762306a36Sopenharmony_ci		return -1;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	switch (hs_header->page_code) {
105162306a36Sopenharmony_ci	case HS_PAGE_FIRM_CAP:
105262306a36Sopenharmony_ci		hs_page1 = (struct mvumi_hs_page1 *) hs_header;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci		mhba->max_io = hs_page1->max_io_support;
105562306a36Sopenharmony_ci		mhba->list_num_io = hs_page1->cl_inout_list_depth;
105662306a36Sopenharmony_ci		mhba->max_transfer_size = hs_page1->max_transfer_size;
105762306a36Sopenharmony_ci		mhba->max_target_id = hs_page1->max_devices_support;
105862306a36Sopenharmony_ci		mhba->hba_capability = hs_page1->capability;
105962306a36Sopenharmony_ci		mhba->ib_max_size_setting = hs_page1->cl_in_max_entry_size;
106062306a36Sopenharmony_ci		mhba->ib_max_size = (1 << hs_page1->cl_in_max_entry_size) << 2;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		mhba->ob_max_size_setting = hs_page1->cl_out_max_entry_size;
106362306a36Sopenharmony_ci		mhba->ob_max_size = (1 << hs_page1->cl_out_max_entry_size) << 2;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci		dev_dbg(&mhba->pdev->dev, "FW version:%d\n",
106662306a36Sopenharmony_ci						hs_page1->fw_ver.ver_build);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci		if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG)
106962306a36Sopenharmony_ci			mhba->eot_flag = 22;
107062306a36Sopenharmony_ci		else
107162306a36Sopenharmony_ci			mhba->eot_flag = 27;
107262306a36Sopenharmony_ci		if (mhba->hba_capability & HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF)
107362306a36Sopenharmony_ci			mhba->list_num_io = 1 << hs_page1->cl_inout_list_depth;
107462306a36Sopenharmony_ci		break;
107562306a36Sopenharmony_ci	default:
107662306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "handshake: page code error\n");
107762306a36Sopenharmony_ci		return -1;
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci	return 0;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci/**
108362306a36Sopenharmony_ci * mvumi_handshake -	Move the FW to READY state
108462306a36Sopenharmony_ci * @mhba:				Adapter soft state
108562306a36Sopenharmony_ci *
108662306a36Sopenharmony_ci * During the initialization, FW passes can potentially be in any one of
108762306a36Sopenharmony_ci * several possible states. If the FW in operational, waiting-for-handshake
108862306a36Sopenharmony_ci * states, driver must take steps to bring it to ready state. Otherwise, it
108962306a36Sopenharmony_ci * has to wait for the ready state.
109062306a36Sopenharmony_ci */
109162306a36Sopenharmony_cistatic int mvumi_handshake(struct mvumi_hba *mhba)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	unsigned int hs_state, tmp, hs_fun;
109462306a36Sopenharmony_ci	struct mvumi_hs_header *hs_header;
109562306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (mhba->fw_state == FW_STATE_STARTING)
109862306a36Sopenharmony_ci		hs_state = HS_S_START;
109962306a36Sopenharmony_ci	else {
110062306a36Sopenharmony_ci		tmp = ioread32(regs->arm_to_pciea_msg0);
110162306a36Sopenharmony_ci		hs_state = HS_GET_STATE(tmp);
110262306a36Sopenharmony_ci		dev_dbg(&mhba->pdev->dev, "handshake state[0x%x].\n", hs_state);
110362306a36Sopenharmony_ci		if (HS_GET_STATUS(tmp) != HS_STATUS_OK) {
110462306a36Sopenharmony_ci			mhba->fw_state = FW_STATE_STARTING;
110562306a36Sopenharmony_ci			return -1;
110662306a36Sopenharmony_ci		}
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	hs_fun = 0;
111062306a36Sopenharmony_ci	switch (hs_state) {
111162306a36Sopenharmony_ci	case HS_S_START:
111262306a36Sopenharmony_ci		mhba->fw_state = FW_STATE_HANDSHAKING;
111362306a36Sopenharmony_ci		HS_SET_STATUS(hs_fun, HS_STATUS_OK);
111462306a36Sopenharmony_ci		HS_SET_STATE(hs_fun, HS_S_RESET);
111562306a36Sopenharmony_ci		iowrite32(HANDSHAKE_SIGNATURE, regs->pciea_to_arm_msg1);
111662306a36Sopenharmony_ci		iowrite32(hs_fun, regs->pciea_to_arm_msg0);
111762306a36Sopenharmony_ci		iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg);
111862306a36Sopenharmony_ci		break;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	case HS_S_RESET:
112162306a36Sopenharmony_ci		iowrite32(lower_32_bits(mhba->handshake_page_phys),
112262306a36Sopenharmony_ci					regs->pciea_to_arm_msg1);
112362306a36Sopenharmony_ci		iowrite32(upper_32_bits(mhba->handshake_page_phys),
112462306a36Sopenharmony_ci					regs->arm_to_pciea_msg1);
112562306a36Sopenharmony_ci		HS_SET_STATUS(hs_fun, HS_STATUS_OK);
112662306a36Sopenharmony_ci		HS_SET_STATE(hs_fun, HS_S_PAGE_ADDR);
112762306a36Sopenharmony_ci		iowrite32(hs_fun, regs->pciea_to_arm_msg0);
112862306a36Sopenharmony_ci		iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg);
112962306a36Sopenharmony_ci		break;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	case HS_S_PAGE_ADDR:
113262306a36Sopenharmony_ci	case HS_S_QUERY_PAGE:
113362306a36Sopenharmony_ci	case HS_S_SEND_PAGE:
113462306a36Sopenharmony_ci		hs_header = (struct mvumi_hs_header *) mhba->handshake_page;
113562306a36Sopenharmony_ci		if (hs_header->page_code == HS_PAGE_FIRM_CAP) {
113662306a36Sopenharmony_ci			mhba->hba_total_pages =
113762306a36Sopenharmony_ci			((struct mvumi_hs_page1 *) hs_header)->total_pages;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci			if (mhba->hba_total_pages == 0)
114062306a36Sopenharmony_ci				mhba->hba_total_pages = HS_PAGE_TOTAL-1;
114162306a36Sopenharmony_ci		}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci		if (hs_state == HS_S_QUERY_PAGE) {
114462306a36Sopenharmony_ci			if (mvumi_hs_process_page(mhba, hs_header)) {
114562306a36Sopenharmony_ci				HS_SET_STATE(hs_fun, HS_S_ABORT);
114662306a36Sopenharmony_ci				return -1;
114762306a36Sopenharmony_ci			}
114862306a36Sopenharmony_ci			if (mvumi_init_data(mhba)) {
114962306a36Sopenharmony_ci				HS_SET_STATE(hs_fun, HS_S_ABORT);
115062306a36Sopenharmony_ci				return -1;
115162306a36Sopenharmony_ci			}
115262306a36Sopenharmony_ci		} else if (hs_state == HS_S_PAGE_ADDR) {
115362306a36Sopenharmony_ci			hs_header->page_code = 0;
115462306a36Sopenharmony_ci			mhba->hba_total_pages = HS_PAGE_TOTAL-1;
115562306a36Sopenharmony_ci		}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		if ((hs_header->page_code + 1) <= mhba->hba_total_pages) {
115862306a36Sopenharmony_ci			hs_header->page_code++;
115962306a36Sopenharmony_ci			if (hs_header->page_code != HS_PAGE_FIRM_CAP) {
116062306a36Sopenharmony_ci				mvumi_hs_build_page(mhba, hs_header);
116162306a36Sopenharmony_ci				HS_SET_STATE(hs_fun, HS_S_SEND_PAGE);
116262306a36Sopenharmony_ci			} else
116362306a36Sopenharmony_ci				HS_SET_STATE(hs_fun, HS_S_QUERY_PAGE);
116462306a36Sopenharmony_ci		} else
116562306a36Sopenharmony_ci			HS_SET_STATE(hs_fun, HS_S_END);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci		HS_SET_STATUS(hs_fun, HS_STATUS_OK);
116862306a36Sopenharmony_ci		iowrite32(hs_fun, regs->pciea_to_arm_msg0);
116962306a36Sopenharmony_ci		iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg);
117062306a36Sopenharmony_ci		break;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	case HS_S_END:
117362306a36Sopenharmony_ci		/* Set communication list ISR */
117462306a36Sopenharmony_ci		tmp = ioread32(regs->enpointa_mask_reg);
117562306a36Sopenharmony_ci		tmp |= regs->int_comaout | regs->int_comaerr;
117662306a36Sopenharmony_ci		iowrite32(tmp, regs->enpointa_mask_reg);
117762306a36Sopenharmony_ci		iowrite32(mhba->list_num_io, mhba->ib_shadow);
117862306a36Sopenharmony_ci		/* Set InBound List Available count shadow */
117962306a36Sopenharmony_ci		iowrite32(lower_32_bits(mhba->ib_shadow_phys),
118062306a36Sopenharmony_ci					regs->inb_aval_count_basel);
118162306a36Sopenharmony_ci		iowrite32(upper_32_bits(mhba->ib_shadow_phys),
118262306a36Sopenharmony_ci					regs->inb_aval_count_baseh);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143) {
118562306a36Sopenharmony_ci			/* Set OutBound List Available count shadow */
118662306a36Sopenharmony_ci			iowrite32((mhba->list_num_io-1) |
118762306a36Sopenharmony_ci							regs->cl_pointer_toggle,
118862306a36Sopenharmony_ci							mhba->ob_shadow);
118962306a36Sopenharmony_ci			iowrite32(lower_32_bits(mhba->ob_shadow_phys),
119062306a36Sopenharmony_ci							regs->outb_copy_basel);
119162306a36Sopenharmony_ci			iowrite32(upper_32_bits(mhba->ob_shadow_phys),
119262306a36Sopenharmony_ci							regs->outb_copy_baseh);
119362306a36Sopenharmony_ci		}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci		mhba->ib_cur_slot = (mhba->list_num_io - 1) |
119662306a36Sopenharmony_ci							regs->cl_pointer_toggle;
119762306a36Sopenharmony_ci		mhba->ob_cur_slot = (mhba->list_num_io - 1) |
119862306a36Sopenharmony_ci							regs->cl_pointer_toggle;
119962306a36Sopenharmony_ci		mhba->fw_state = FW_STATE_STARTED;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		break;
120262306a36Sopenharmony_ci	default:
120362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "unknown handshake state [0x%x].\n",
120462306a36Sopenharmony_ci								hs_state);
120562306a36Sopenharmony_ci		return -1;
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci	return 0;
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic unsigned char mvumi_handshake_event(struct mvumi_hba *mhba)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	unsigned int isr_status;
121362306a36Sopenharmony_ci	unsigned long before;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	before = jiffies;
121662306a36Sopenharmony_ci	mvumi_handshake(mhba);
121762306a36Sopenharmony_ci	do {
121862306a36Sopenharmony_ci		isr_status = mhba->instancet->read_fw_status_reg(mhba);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		if (mhba->fw_state == FW_STATE_STARTED)
122162306a36Sopenharmony_ci			return 0;
122262306a36Sopenharmony_ci		if (time_after(jiffies, before + FW_MAX_DELAY * HZ)) {
122362306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
122462306a36Sopenharmony_ci				"no handshake response at state 0x%x.\n",
122562306a36Sopenharmony_ci				  mhba->fw_state);
122662306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
122762306a36Sopenharmony_ci				"isr : global=0x%x,status=0x%x.\n",
122862306a36Sopenharmony_ci					mhba->global_isr, isr_status);
122962306a36Sopenharmony_ci			return -1;
123062306a36Sopenharmony_ci		}
123162306a36Sopenharmony_ci		rmb();
123262306a36Sopenharmony_ci		usleep_range(1000, 2000);
123362306a36Sopenharmony_ci	} while (!(isr_status & DRBL_HANDSHAKE_ISR));
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	return 0;
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_cistatic unsigned char mvumi_check_handshake(struct mvumi_hba *mhba)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	unsigned int tmp;
124162306a36Sopenharmony_ci	unsigned long before;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	before = jiffies;
124462306a36Sopenharmony_ci	tmp = ioread32(mhba->regs->arm_to_pciea_msg1);
124562306a36Sopenharmony_ci	while ((tmp != HANDSHAKE_READYSTATE) && (tmp != HANDSHAKE_DONESTATE)) {
124662306a36Sopenharmony_ci		if (tmp != HANDSHAKE_READYSTATE)
124762306a36Sopenharmony_ci			iowrite32(DRBL_MU_RESET,
124862306a36Sopenharmony_ci					mhba->regs->pciea_to_arm_drbl_reg);
124962306a36Sopenharmony_ci		if (time_after(jiffies, before + FW_MAX_DELAY * HZ)) {
125062306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
125162306a36Sopenharmony_ci				"invalid signature [0x%x].\n", tmp);
125262306a36Sopenharmony_ci			return -1;
125362306a36Sopenharmony_ci		}
125462306a36Sopenharmony_ci		usleep_range(1000, 2000);
125562306a36Sopenharmony_ci		rmb();
125662306a36Sopenharmony_ci		tmp = ioread32(mhba->regs->arm_to_pciea_msg1);
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	mhba->fw_state = FW_STATE_STARTING;
126062306a36Sopenharmony_ci	dev_dbg(&mhba->pdev->dev, "start firmware handshake...\n");
126162306a36Sopenharmony_ci	do {
126262306a36Sopenharmony_ci		if (mvumi_handshake_event(mhba)) {
126362306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev,
126462306a36Sopenharmony_ci					"handshake failed at state 0x%x.\n",
126562306a36Sopenharmony_ci						mhba->fw_state);
126662306a36Sopenharmony_ci			return -1;
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci	} while (mhba->fw_state != FW_STATE_STARTED);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	dev_dbg(&mhba->pdev->dev, "firmware handshake done\n");
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	return 0;
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic unsigned char mvumi_start(struct mvumi_hba *mhba)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	unsigned int tmp;
127862306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	/* clear Door bell */
128162306a36Sopenharmony_ci	tmp = ioread32(regs->arm_to_pciea_drbl_reg);
128262306a36Sopenharmony_ci	iowrite32(tmp, regs->arm_to_pciea_drbl_reg);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	iowrite32(regs->int_drbl_int_mask, regs->arm_to_pciea_mask_reg);
128562306a36Sopenharmony_ci	tmp = ioread32(regs->enpointa_mask_reg) | regs->int_dl_cpu2pciea;
128662306a36Sopenharmony_ci	iowrite32(tmp, regs->enpointa_mask_reg);
128762306a36Sopenharmony_ci	msleep(100);
128862306a36Sopenharmony_ci	if (mvumi_check_handshake(mhba))
128962306a36Sopenharmony_ci		return -1;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	return 0;
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci/**
129562306a36Sopenharmony_ci * mvumi_complete_cmd -	Completes a command
129662306a36Sopenharmony_ci * @mhba:			Adapter soft state
129762306a36Sopenharmony_ci * @cmd:			Command to be completed
129862306a36Sopenharmony_ci * @ob_frame:			Command response
129962306a36Sopenharmony_ci */
130062306a36Sopenharmony_cistatic void mvumi_complete_cmd(struct mvumi_hba *mhba, struct mvumi_cmd *cmd,
130162306a36Sopenharmony_ci					struct mvumi_rsp_frame *ob_frame)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	struct scsi_cmnd *scmd = cmd->scmd;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	mvumi_priv(cmd->scmd)->cmd_priv = NULL;
130662306a36Sopenharmony_ci	scmd->result = ob_frame->req_status;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	switch (ob_frame->req_status) {
130962306a36Sopenharmony_ci	case SAM_STAT_GOOD:
131062306a36Sopenharmony_ci		scmd->result |= DID_OK << 16;
131162306a36Sopenharmony_ci		break;
131262306a36Sopenharmony_ci	case SAM_STAT_BUSY:
131362306a36Sopenharmony_ci		scmd->result |= DID_BUS_BUSY << 16;
131462306a36Sopenharmony_ci		break;
131562306a36Sopenharmony_ci	case SAM_STAT_CHECK_CONDITION:
131662306a36Sopenharmony_ci		scmd->result |= (DID_OK << 16);
131762306a36Sopenharmony_ci		if (ob_frame->rsp_flag & CL_RSP_FLAG_SENSEDATA) {
131862306a36Sopenharmony_ci			memcpy(cmd->scmd->sense_buffer, ob_frame->payload,
131962306a36Sopenharmony_ci				sizeof(struct mvumi_sense_data));
132062306a36Sopenharmony_ci		}
132162306a36Sopenharmony_ci		break;
132262306a36Sopenharmony_ci	default:
132362306a36Sopenharmony_ci		scmd->result |= (DID_ABORT << 16);
132462306a36Sopenharmony_ci		break;
132562306a36Sopenharmony_ci	}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	if (scsi_bufflen(scmd))
132862306a36Sopenharmony_ci		dma_unmap_sg(&mhba->pdev->dev, scsi_sglist(scmd),
132962306a36Sopenharmony_ci			     scsi_sg_count(scmd),
133062306a36Sopenharmony_ci			     scmd->sc_data_direction);
133162306a36Sopenharmony_ci	scsi_done(scmd);
133262306a36Sopenharmony_ci	mvumi_return_cmd(mhba, cmd);
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cistatic void mvumi_complete_internal_cmd(struct mvumi_hba *mhba,
133662306a36Sopenharmony_ci						struct mvumi_cmd *cmd,
133762306a36Sopenharmony_ci					struct mvumi_rsp_frame *ob_frame)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	if (atomic_read(&cmd->sync_cmd)) {
134062306a36Sopenharmony_ci		cmd->cmd_status = ob_frame->req_status;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci		if ((ob_frame->req_status == SAM_STAT_CHECK_CONDITION) &&
134362306a36Sopenharmony_ci				(ob_frame->rsp_flag & CL_RSP_FLAG_SENSEDATA) &&
134462306a36Sopenharmony_ci				cmd->data_buf) {
134562306a36Sopenharmony_ci			memcpy(cmd->data_buf, ob_frame->payload,
134662306a36Sopenharmony_ci					sizeof(struct mvumi_sense_data));
134762306a36Sopenharmony_ci		}
134862306a36Sopenharmony_ci		atomic_dec(&cmd->sync_cmd);
134962306a36Sopenharmony_ci		wake_up(&mhba->int_cmd_wait_q);
135062306a36Sopenharmony_ci	}
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_cistatic void mvumi_show_event(struct mvumi_hba *mhba,
135462306a36Sopenharmony_ci			struct mvumi_driver_event *ptr)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	unsigned int i;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	dev_warn(&mhba->pdev->dev,
135962306a36Sopenharmony_ci		"Event[0x%x] id[0x%x] severity[0x%x] device id[0x%x]\n",
136062306a36Sopenharmony_ci		ptr->sequence_no, ptr->event_id, ptr->severity, ptr->device_id);
136162306a36Sopenharmony_ci	if (ptr->param_count) {
136262306a36Sopenharmony_ci		printk(KERN_WARNING "Event param(len 0x%x): ",
136362306a36Sopenharmony_ci						ptr->param_count);
136462306a36Sopenharmony_ci		for (i = 0; i < ptr->param_count; i++)
136562306a36Sopenharmony_ci			printk(KERN_WARNING "0x%x ", ptr->params[i]);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci		printk(KERN_WARNING "\n");
136862306a36Sopenharmony_ci	}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	if (ptr->sense_data_length) {
137162306a36Sopenharmony_ci		printk(KERN_WARNING "Event sense data(len 0x%x): ",
137262306a36Sopenharmony_ci						ptr->sense_data_length);
137362306a36Sopenharmony_ci		for (i = 0; i < ptr->sense_data_length; i++)
137462306a36Sopenharmony_ci			printk(KERN_WARNING "0x%x ", ptr->sense_data[i]);
137562306a36Sopenharmony_ci		printk(KERN_WARNING "\n");
137662306a36Sopenharmony_ci	}
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic int mvumi_handle_hotplug(struct mvumi_hba *mhba, u16 devid, int status)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci	struct scsi_device *sdev;
138262306a36Sopenharmony_ci	int ret = -1;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	if (status == DEVICE_OFFLINE) {
138562306a36Sopenharmony_ci		sdev = scsi_device_lookup(mhba->shost, 0, devid, 0);
138662306a36Sopenharmony_ci		if (sdev) {
138762306a36Sopenharmony_ci			dev_dbg(&mhba->pdev->dev, "remove disk %d-%d-%d.\n", 0,
138862306a36Sopenharmony_ci								sdev->id, 0);
138962306a36Sopenharmony_ci			scsi_remove_device(sdev);
139062306a36Sopenharmony_ci			scsi_device_put(sdev);
139162306a36Sopenharmony_ci			ret = 0;
139262306a36Sopenharmony_ci		} else
139362306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev, " no disk[%d] to remove\n",
139462306a36Sopenharmony_ci									devid);
139562306a36Sopenharmony_ci	} else if (status == DEVICE_ONLINE) {
139662306a36Sopenharmony_ci		sdev = scsi_device_lookup(mhba->shost, 0, devid, 0);
139762306a36Sopenharmony_ci		if (!sdev) {
139862306a36Sopenharmony_ci			scsi_add_device(mhba->shost, 0, devid, 0);
139962306a36Sopenharmony_ci			dev_dbg(&mhba->pdev->dev, " add disk %d-%d-%d.\n", 0,
140062306a36Sopenharmony_ci								devid, 0);
140162306a36Sopenharmony_ci			ret = 0;
140262306a36Sopenharmony_ci		} else {
140362306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev, " don't add disk %d-%d-%d.\n",
140462306a36Sopenharmony_ci								0, devid, 0);
140562306a36Sopenharmony_ci			scsi_device_put(sdev);
140662306a36Sopenharmony_ci		}
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci	return ret;
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_cistatic u64 mvumi_inquiry(struct mvumi_hba *mhba,
141262306a36Sopenharmony_ci	unsigned int id, struct mvumi_cmd *cmd)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	struct mvumi_msg_frame *frame;
141562306a36Sopenharmony_ci	u64 wwid = 0;
141662306a36Sopenharmony_ci	int cmd_alloc = 0;
141762306a36Sopenharmony_ci	int data_buf_len = 64;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	if (!cmd) {
142062306a36Sopenharmony_ci		cmd = mvumi_create_internal_cmd(mhba, data_buf_len);
142162306a36Sopenharmony_ci		if (cmd)
142262306a36Sopenharmony_ci			cmd_alloc = 1;
142362306a36Sopenharmony_ci		else
142462306a36Sopenharmony_ci			return 0;
142562306a36Sopenharmony_ci	} else {
142662306a36Sopenharmony_ci		memset(cmd->data_buf, 0, data_buf_len);
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci	cmd->scmd = NULL;
142962306a36Sopenharmony_ci	cmd->cmd_status = REQ_STATUS_PENDING;
143062306a36Sopenharmony_ci	atomic_set(&cmd->sync_cmd, 0);
143162306a36Sopenharmony_ci	frame = cmd->frame;
143262306a36Sopenharmony_ci	frame->device_id = (u16) id;
143362306a36Sopenharmony_ci	frame->cmd_flag = CMD_FLAG_DATA_IN;
143462306a36Sopenharmony_ci	frame->req_function = CL_FUN_SCSI_CMD;
143562306a36Sopenharmony_ci	frame->cdb_length = 6;
143662306a36Sopenharmony_ci	frame->data_transfer_length = MVUMI_INQUIRY_LENGTH;
143762306a36Sopenharmony_ci	memset(frame->cdb, 0, frame->cdb_length);
143862306a36Sopenharmony_ci	frame->cdb[0] = INQUIRY;
143962306a36Sopenharmony_ci	frame->cdb[4] = frame->data_transfer_length;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	mvumi_issue_blocked_cmd(mhba, cmd);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	if (cmd->cmd_status == SAM_STAT_GOOD) {
144462306a36Sopenharmony_ci		if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143)
144562306a36Sopenharmony_ci			wwid = id + 1;
144662306a36Sopenharmony_ci		else
144762306a36Sopenharmony_ci			memcpy((void *)&wwid,
144862306a36Sopenharmony_ci			       (cmd->data_buf + MVUMI_INQUIRY_UUID_OFF),
144962306a36Sopenharmony_ci			       MVUMI_INQUIRY_UUID_LEN);
145062306a36Sopenharmony_ci		dev_dbg(&mhba->pdev->dev,
145162306a36Sopenharmony_ci			"inquiry device(0:%d:0) wwid(%llx)\n", id, wwid);
145262306a36Sopenharmony_ci	} else {
145362306a36Sopenharmony_ci		wwid = 0;
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci	if (cmd_alloc)
145662306a36Sopenharmony_ci		mvumi_delete_internal_cmd(mhba, cmd);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	return wwid;
145962306a36Sopenharmony_ci}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_cistatic void mvumi_detach_devices(struct mvumi_hba *mhba)
146262306a36Sopenharmony_ci{
146362306a36Sopenharmony_ci	struct mvumi_device *mv_dev = NULL , *dev_next;
146462306a36Sopenharmony_ci	struct scsi_device *sdev = NULL;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	mutex_lock(&mhba->device_lock);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	/* detach Hard Disk */
146962306a36Sopenharmony_ci	list_for_each_entry_safe(mv_dev, dev_next,
147062306a36Sopenharmony_ci		&mhba->shost_dev_list, list) {
147162306a36Sopenharmony_ci		mvumi_handle_hotplug(mhba, mv_dev->id, DEVICE_OFFLINE);
147262306a36Sopenharmony_ci		list_del_init(&mv_dev->list);
147362306a36Sopenharmony_ci		dev_dbg(&mhba->pdev->dev, "release device(0:%d:0) wwid(%llx)\n",
147462306a36Sopenharmony_ci			mv_dev->id, mv_dev->wwid);
147562306a36Sopenharmony_ci		kfree(mv_dev);
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci	list_for_each_entry_safe(mv_dev, dev_next, &mhba->mhba_dev_list, list) {
147862306a36Sopenharmony_ci		list_del_init(&mv_dev->list);
147962306a36Sopenharmony_ci		dev_dbg(&mhba->pdev->dev, "release device(0:%d:0) wwid(%llx)\n",
148062306a36Sopenharmony_ci			mv_dev->id, mv_dev->wwid);
148162306a36Sopenharmony_ci		kfree(mv_dev);
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	/* detach virtual device */
148562306a36Sopenharmony_ci	if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580)
148662306a36Sopenharmony_ci		sdev = scsi_device_lookup(mhba->shost, 0,
148762306a36Sopenharmony_ci						mhba->max_target_id - 1, 0);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	if (sdev) {
149062306a36Sopenharmony_ci		scsi_remove_device(sdev);
149162306a36Sopenharmony_ci		scsi_device_put(sdev);
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	mutex_unlock(&mhba->device_lock);
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_cistatic void mvumi_rescan_devices(struct mvumi_hba *mhba, int id)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	struct scsi_device *sdev;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	sdev = scsi_device_lookup(mhba->shost, 0, id, 0);
150262306a36Sopenharmony_ci	if (sdev) {
150362306a36Sopenharmony_ci		scsi_rescan_device(sdev);
150462306a36Sopenharmony_ci		scsi_device_put(sdev);
150562306a36Sopenharmony_ci	}
150662306a36Sopenharmony_ci}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_cistatic int mvumi_match_devices(struct mvumi_hba *mhba, int id, u64 wwid)
150962306a36Sopenharmony_ci{
151062306a36Sopenharmony_ci	struct mvumi_device *mv_dev = NULL;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	list_for_each_entry(mv_dev, &mhba->shost_dev_list, list) {
151362306a36Sopenharmony_ci		if (mv_dev->wwid == wwid) {
151462306a36Sopenharmony_ci			if (mv_dev->id != id) {
151562306a36Sopenharmony_ci				dev_err(&mhba->pdev->dev,
151662306a36Sopenharmony_ci					"%s has same wwid[%llx] ,"
151762306a36Sopenharmony_ci					" but different id[%d %d]\n",
151862306a36Sopenharmony_ci					__func__, mv_dev->wwid, mv_dev->id, id);
151962306a36Sopenharmony_ci				return -1;
152062306a36Sopenharmony_ci			} else {
152162306a36Sopenharmony_ci				if (mhba->pdev->device ==
152262306a36Sopenharmony_ci						PCI_DEVICE_ID_MARVELL_MV9143)
152362306a36Sopenharmony_ci					mvumi_rescan_devices(mhba, id);
152462306a36Sopenharmony_ci				return 1;
152562306a36Sopenharmony_ci			}
152662306a36Sopenharmony_ci		}
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci	return 0;
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cistatic void mvumi_remove_devices(struct mvumi_hba *mhba, int id)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	struct mvumi_device *mv_dev = NULL, *dev_next;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	list_for_each_entry_safe(mv_dev, dev_next,
153662306a36Sopenharmony_ci				&mhba->shost_dev_list, list) {
153762306a36Sopenharmony_ci		if (mv_dev->id == id) {
153862306a36Sopenharmony_ci			dev_dbg(&mhba->pdev->dev,
153962306a36Sopenharmony_ci				"detach device(0:%d:0) wwid(%llx) from HOST\n",
154062306a36Sopenharmony_ci				mv_dev->id, mv_dev->wwid);
154162306a36Sopenharmony_ci			mvumi_handle_hotplug(mhba, mv_dev->id, DEVICE_OFFLINE);
154262306a36Sopenharmony_ci			list_del_init(&mv_dev->list);
154362306a36Sopenharmony_ci			kfree(mv_dev);
154462306a36Sopenharmony_ci		}
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cistatic int mvumi_probe_devices(struct mvumi_hba *mhba)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	int id, maxid;
155162306a36Sopenharmony_ci	u64 wwid = 0;
155262306a36Sopenharmony_ci	struct mvumi_device *mv_dev = NULL;
155362306a36Sopenharmony_ci	struct mvumi_cmd *cmd = NULL;
155462306a36Sopenharmony_ci	int found = 0;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	cmd = mvumi_create_internal_cmd(mhba, 64);
155762306a36Sopenharmony_ci	if (!cmd)
155862306a36Sopenharmony_ci		return -1;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143)
156162306a36Sopenharmony_ci		maxid = mhba->max_target_id;
156262306a36Sopenharmony_ci	else
156362306a36Sopenharmony_ci		maxid = mhba->max_target_id - 1;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	for (id = 0; id < maxid; id++) {
156662306a36Sopenharmony_ci		wwid = mvumi_inquiry(mhba, id, cmd);
156762306a36Sopenharmony_ci		if (!wwid) {
156862306a36Sopenharmony_ci			/* device no response, remove it */
156962306a36Sopenharmony_ci			mvumi_remove_devices(mhba, id);
157062306a36Sopenharmony_ci		} else {
157162306a36Sopenharmony_ci			/* device response, add it */
157262306a36Sopenharmony_ci			found = mvumi_match_devices(mhba, id, wwid);
157362306a36Sopenharmony_ci			if (!found) {
157462306a36Sopenharmony_ci				mvumi_remove_devices(mhba, id);
157562306a36Sopenharmony_ci				mv_dev = kzalloc(sizeof(struct mvumi_device),
157662306a36Sopenharmony_ci								GFP_KERNEL);
157762306a36Sopenharmony_ci				if (!mv_dev) {
157862306a36Sopenharmony_ci					dev_err(&mhba->pdev->dev,
157962306a36Sopenharmony_ci						"%s alloc mv_dev failed\n",
158062306a36Sopenharmony_ci						__func__);
158162306a36Sopenharmony_ci					continue;
158262306a36Sopenharmony_ci				}
158362306a36Sopenharmony_ci				mv_dev->id = id;
158462306a36Sopenharmony_ci				mv_dev->wwid = wwid;
158562306a36Sopenharmony_ci				mv_dev->sdev = NULL;
158662306a36Sopenharmony_ci				INIT_LIST_HEAD(&mv_dev->list);
158762306a36Sopenharmony_ci				list_add_tail(&mv_dev->list,
158862306a36Sopenharmony_ci					      &mhba->mhba_dev_list);
158962306a36Sopenharmony_ci				dev_dbg(&mhba->pdev->dev,
159062306a36Sopenharmony_ci					"probe a new device(0:%d:0)"
159162306a36Sopenharmony_ci					" wwid(%llx)\n", id, mv_dev->wwid);
159262306a36Sopenharmony_ci			} else if (found == -1)
159362306a36Sopenharmony_ci				return -1;
159462306a36Sopenharmony_ci			else
159562306a36Sopenharmony_ci				continue;
159662306a36Sopenharmony_ci		}
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	if (cmd)
160062306a36Sopenharmony_ci		mvumi_delete_internal_cmd(mhba, cmd);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	return 0;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int mvumi_rescan_bus(void *data)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	int ret = 0;
160862306a36Sopenharmony_ci	struct mvumi_hba *mhba = (struct mvumi_hba *) data;
160962306a36Sopenharmony_ci	struct mvumi_device *mv_dev = NULL , *dev_next;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	while (!kthread_should_stop()) {
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
161462306a36Sopenharmony_ci		if (!atomic_read(&mhba->pnp_count))
161562306a36Sopenharmony_ci			schedule();
161662306a36Sopenharmony_ci		msleep(1000);
161762306a36Sopenharmony_ci		atomic_set(&mhba->pnp_count, 0);
161862306a36Sopenharmony_ci		__set_current_state(TASK_RUNNING);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci		mutex_lock(&mhba->device_lock);
162162306a36Sopenharmony_ci		ret = mvumi_probe_devices(mhba);
162262306a36Sopenharmony_ci		if (!ret) {
162362306a36Sopenharmony_ci			list_for_each_entry_safe(mv_dev, dev_next,
162462306a36Sopenharmony_ci						 &mhba->mhba_dev_list, list) {
162562306a36Sopenharmony_ci				if (mvumi_handle_hotplug(mhba, mv_dev->id,
162662306a36Sopenharmony_ci							 DEVICE_ONLINE)) {
162762306a36Sopenharmony_ci					dev_err(&mhba->pdev->dev,
162862306a36Sopenharmony_ci						"%s add device(0:%d:0) failed"
162962306a36Sopenharmony_ci						"wwid(%llx) has exist\n",
163062306a36Sopenharmony_ci						__func__,
163162306a36Sopenharmony_ci						mv_dev->id, mv_dev->wwid);
163262306a36Sopenharmony_ci					list_del_init(&mv_dev->list);
163362306a36Sopenharmony_ci					kfree(mv_dev);
163462306a36Sopenharmony_ci				} else {
163562306a36Sopenharmony_ci					list_move_tail(&mv_dev->list,
163662306a36Sopenharmony_ci						       &mhba->shost_dev_list);
163762306a36Sopenharmony_ci				}
163862306a36Sopenharmony_ci			}
163962306a36Sopenharmony_ci		}
164062306a36Sopenharmony_ci		mutex_unlock(&mhba->device_lock);
164162306a36Sopenharmony_ci	}
164262306a36Sopenharmony_ci	return 0;
164362306a36Sopenharmony_ci}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_cistatic void mvumi_proc_msg(struct mvumi_hba *mhba,
164662306a36Sopenharmony_ci					struct mvumi_hotplug_event *param)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	u16 size = param->size;
164962306a36Sopenharmony_ci	const unsigned long *ar_bitmap;
165062306a36Sopenharmony_ci	const unsigned long *re_bitmap;
165162306a36Sopenharmony_ci	int index;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	if (mhba->fw_flag & MVUMI_FW_ATTACH) {
165462306a36Sopenharmony_ci		index = -1;
165562306a36Sopenharmony_ci		ar_bitmap = (const unsigned long *) param->bitmap;
165662306a36Sopenharmony_ci		re_bitmap = (const unsigned long *) &param->bitmap[size >> 3];
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci		mutex_lock(&mhba->sas_discovery_mutex);
165962306a36Sopenharmony_ci		do {
166062306a36Sopenharmony_ci			index = find_next_zero_bit(ar_bitmap, size, index + 1);
166162306a36Sopenharmony_ci			if (index >= size)
166262306a36Sopenharmony_ci				break;
166362306a36Sopenharmony_ci			mvumi_handle_hotplug(mhba, index, DEVICE_ONLINE);
166462306a36Sopenharmony_ci		} while (1);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci		index = -1;
166762306a36Sopenharmony_ci		do {
166862306a36Sopenharmony_ci			index = find_next_zero_bit(re_bitmap, size, index + 1);
166962306a36Sopenharmony_ci			if (index >= size)
167062306a36Sopenharmony_ci				break;
167162306a36Sopenharmony_ci			mvumi_handle_hotplug(mhba, index, DEVICE_OFFLINE);
167262306a36Sopenharmony_ci		} while (1);
167362306a36Sopenharmony_ci		mutex_unlock(&mhba->sas_discovery_mutex);
167462306a36Sopenharmony_ci	}
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_cistatic void mvumi_notification(struct mvumi_hba *mhba, u8 msg, void *buffer)
167862306a36Sopenharmony_ci{
167962306a36Sopenharmony_ci	if (msg == APICDB1_EVENT_GETEVENT) {
168062306a36Sopenharmony_ci		int i, count;
168162306a36Sopenharmony_ci		struct mvumi_driver_event *param = NULL;
168262306a36Sopenharmony_ci		struct mvumi_event_req *er = buffer;
168362306a36Sopenharmony_ci		count = er->count;
168462306a36Sopenharmony_ci		if (count > MAX_EVENTS_RETURNED) {
168562306a36Sopenharmony_ci			dev_err(&mhba->pdev->dev, "event count[0x%x] is bigger"
168662306a36Sopenharmony_ci					" than max event count[0x%x].\n",
168762306a36Sopenharmony_ci					count, MAX_EVENTS_RETURNED);
168862306a36Sopenharmony_ci			return;
168962306a36Sopenharmony_ci		}
169062306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
169162306a36Sopenharmony_ci			param = &er->events[i];
169262306a36Sopenharmony_ci			mvumi_show_event(mhba, param);
169362306a36Sopenharmony_ci		}
169462306a36Sopenharmony_ci	} else if (msg == APICDB1_HOST_GETEVENT) {
169562306a36Sopenharmony_ci		mvumi_proc_msg(mhba, buffer);
169662306a36Sopenharmony_ci	}
169762306a36Sopenharmony_ci}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_cistatic int mvumi_get_event(struct mvumi_hba *mhba, unsigned char msg)
170062306a36Sopenharmony_ci{
170162306a36Sopenharmony_ci	struct mvumi_cmd *cmd;
170262306a36Sopenharmony_ci	struct mvumi_msg_frame *frame;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	cmd = mvumi_create_internal_cmd(mhba, 512);
170562306a36Sopenharmony_ci	if (!cmd)
170662306a36Sopenharmony_ci		return -1;
170762306a36Sopenharmony_ci	cmd->scmd = NULL;
170862306a36Sopenharmony_ci	cmd->cmd_status = REQ_STATUS_PENDING;
170962306a36Sopenharmony_ci	atomic_set(&cmd->sync_cmd, 0);
171062306a36Sopenharmony_ci	frame = cmd->frame;
171162306a36Sopenharmony_ci	frame->device_id = 0;
171262306a36Sopenharmony_ci	frame->cmd_flag = CMD_FLAG_DATA_IN;
171362306a36Sopenharmony_ci	frame->req_function = CL_FUN_SCSI_CMD;
171462306a36Sopenharmony_ci	frame->cdb_length = MAX_COMMAND_SIZE;
171562306a36Sopenharmony_ci	frame->data_transfer_length = sizeof(struct mvumi_event_req);
171662306a36Sopenharmony_ci	memset(frame->cdb, 0, MAX_COMMAND_SIZE);
171762306a36Sopenharmony_ci	frame->cdb[0] = APICDB0_EVENT;
171862306a36Sopenharmony_ci	frame->cdb[1] = msg;
171962306a36Sopenharmony_ci	mvumi_issue_blocked_cmd(mhba, cmd);
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (cmd->cmd_status != SAM_STAT_GOOD)
172262306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "get event failed, status=0x%x.\n",
172362306a36Sopenharmony_ci							cmd->cmd_status);
172462306a36Sopenharmony_ci	else
172562306a36Sopenharmony_ci		mvumi_notification(mhba, cmd->frame->cdb[1], cmd->data_buf);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	mvumi_delete_internal_cmd(mhba, cmd);
172862306a36Sopenharmony_ci	return 0;
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_cistatic void mvumi_scan_events(struct work_struct *work)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci	struct mvumi_events_wq *mu_ev =
173462306a36Sopenharmony_ci		container_of(work, struct mvumi_events_wq, work_q);
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	mvumi_get_event(mu_ev->mhba, mu_ev->event);
173762306a36Sopenharmony_ci	kfree(mu_ev);
173862306a36Sopenharmony_ci}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_cistatic void mvumi_launch_events(struct mvumi_hba *mhba, u32 isr_status)
174162306a36Sopenharmony_ci{
174262306a36Sopenharmony_ci	struct mvumi_events_wq *mu_ev;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	while (isr_status & (DRBL_BUS_CHANGE | DRBL_EVENT_NOTIFY)) {
174562306a36Sopenharmony_ci		if (isr_status & DRBL_BUS_CHANGE) {
174662306a36Sopenharmony_ci			atomic_inc(&mhba->pnp_count);
174762306a36Sopenharmony_ci			wake_up_process(mhba->dm_thread);
174862306a36Sopenharmony_ci			isr_status &= ~(DRBL_BUS_CHANGE);
174962306a36Sopenharmony_ci			continue;
175062306a36Sopenharmony_ci		}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci		mu_ev = kzalloc(sizeof(*mu_ev), GFP_ATOMIC);
175362306a36Sopenharmony_ci		if (mu_ev) {
175462306a36Sopenharmony_ci			INIT_WORK(&mu_ev->work_q, mvumi_scan_events);
175562306a36Sopenharmony_ci			mu_ev->mhba = mhba;
175662306a36Sopenharmony_ci			mu_ev->event = APICDB1_EVENT_GETEVENT;
175762306a36Sopenharmony_ci			isr_status &= ~(DRBL_EVENT_NOTIFY);
175862306a36Sopenharmony_ci			mu_ev->param = NULL;
175962306a36Sopenharmony_ci			schedule_work(&mu_ev->work_q);
176062306a36Sopenharmony_ci		}
176162306a36Sopenharmony_ci	}
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_cistatic void mvumi_handle_clob(struct mvumi_hba *mhba)
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci	struct mvumi_rsp_frame *ob_frame;
176762306a36Sopenharmony_ci	struct mvumi_cmd *cmd;
176862306a36Sopenharmony_ci	struct mvumi_ob_data *pool;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	while (!list_empty(&mhba->free_ob_list)) {
177162306a36Sopenharmony_ci		pool = list_first_entry(&mhba->free_ob_list,
177262306a36Sopenharmony_ci						struct mvumi_ob_data, list);
177362306a36Sopenharmony_ci		list_del_init(&pool->list);
177462306a36Sopenharmony_ci		list_add_tail(&pool->list, &mhba->ob_data_list);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci		ob_frame = (struct mvumi_rsp_frame *) &pool->data[0];
177762306a36Sopenharmony_ci		cmd = mhba->tag_cmd[ob_frame->tag];
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci		atomic_dec(&mhba->fw_outstanding);
178062306a36Sopenharmony_ci		mhba->tag_cmd[ob_frame->tag] = NULL;
178162306a36Sopenharmony_ci		tag_release_one(mhba, &mhba->tag_pool, ob_frame->tag);
178262306a36Sopenharmony_ci		if (cmd->scmd)
178362306a36Sopenharmony_ci			mvumi_complete_cmd(mhba, cmd, ob_frame);
178462306a36Sopenharmony_ci		else
178562306a36Sopenharmony_ci			mvumi_complete_internal_cmd(mhba, cmd, ob_frame);
178662306a36Sopenharmony_ci	}
178762306a36Sopenharmony_ci	mhba->instancet->fire_cmd(mhba, NULL);
178862306a36Sopenharmony_ci}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_cistatic irqreturn_t mvumi_isr_handler(int irq, void *devp)
179162306a36Sopenharmony_ci{
179262306a36Sopenharmony_ci	struct mvumi_hba *mhba = (struct mvumi_hba *) devp;
179362306a36Sopenharmony_ci	unsigned long flags;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	spin_lock_irqsave(mhba->shost->host_lock, flags);
179662306a36Sopenharmony_ci	if (unlikely(mhba->instancet->clear_intr(mhba) || !mhba->global_isr)) {
179762306a36Sopenharmony_ci		spin_unlock_irqrestore(mhba->shost->host_lock, flags);
179862306a36Sopenharmony_ci		return IRQ_NONE;
179962306a36Sopenharmony_ci	}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	if (mhba->global_isr & mhba->regs->int_dl_cpu2pciea) {
180262306a36Sopenharmony_ci		if (mhba->isr_status & (DRBL_BUS_CHANGE | DRBL_EVENT_NOTIFY))
180362306a36Sopenharmony_ci			mvumi_launch_events(mhba, mhba->isr_status);
180462306a36Sopenharmony_ci		if (mhba->isr_status & DRBL_HANDSHAKE_ISR) {
180562306a36Sopenharmony_ci			dev_warn(&mhba->pdev->dev, "enter handshake again!\n");
180662306a36Sopenharmony_ci			mvumi_handshake(mhba);
180762306a36Sopenharmony_ci		}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	if (mhba->global_isr & mhba->regs->int_comaout)
181262306a36Sopenharmony_ci		mvumi_receive_ob_list_entry(mhba);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	mhba->global_isr = 0;
181562306a36Sopenharmony_ci	mhba->isr_status = 0;
181662306a36Sopenharmony_ci	if (mhba->fw_state == FW_STATE_STARTED)
181762306a36Sopenharmony_ci		mvumi_handle_clob(mhba);
181862306a36Sopenharmony_ci	spin_unlock_irqrestore(mhba->shost->host_lock, flags);
181962306a36Sopenharmony_ci	return IRQ_HANDLED;
182062306a36Sopenharmony_ci}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_cistatic enum mvumi_qc_result mvumi_send_command(struct mvumi_hba *mhba,
182362306a36Sopenharmony_ci						struct mvumi_cmd *cmd)
182462306a36Sopenharmony_ci{
182562306a36Sopenharmony_ci	void *ib_entry;
182662306a36Sopenharmony_ci	struct mvumi_msg_frame *ib_frame;
182762306a36Sopenharmony_ci	unsigned int frame_len;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	ib_frame = cmd->frame;
183062306a36Sopenharmony_ci	if (unlikely(mhba->fw_state != FW_STATE_STARTED)) {
183162306a36Sopenharmony_ci		dev_dbg(&mhba->pdev->dev, "firmware not ready.\n");
183262306a36Sopenharmony_ci		return MV_QUEUE_COMMAND_RESULT_NO_RESOURCE;
183362306a36Sopenharmony_ci	}
183462306a36Sopenharmony_ci	if (tag_is_empty(&mhba->tag_pool)) {
183562306a36Sopenharmony_ci		dev_dbg(&mhba->pdev->dev, "no free tag.\n");
183662306a36Sopenharmony_ci		return MV_QUEUE_COMMAND_RESULT_NO_RESOURCE;
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci	mvumi_get_ib_list_entry(mhba, &ib_entry);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	cmd->frame->tag = tag_get_one(mhba, &mhba->tag_pool);
184162306a36Sopenharmony_ci	cmd->frame->request_id = mhba->io_seq++;
184262306a36Sopenharmony_ci	cmd->request_id = cmd->frame->request_id;
184362306a36Sopenharmony_ci	mhba->tag_cmd[cmd->frame->tag] = cmd;
184462306a36Sopenharmony_ci	frame_len = sizeof(*ib_frame) +
184562306a36Sopenharmony_ci				ib_frame->sg_counts * sizeof(struct mvumi_sgl);
184662306a36Sopenharmony_ci	if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) {
184762306a36Sopenharmony_ci		struct mvumi_dyn_list_entry *dle;
184862306a36Sopenharmony_ci		dle = ib_entry;
184962306a36Sopenharmony_ci		dle->src_low_addr =
185062306a36Sopenharmony_ci			cpu_to_le32(lower_32_bits(cmd->frame_phys));
185162306a36Sopenharmony_ci		dle->src_high_addr =
185262306a36Sopenharmony_ci			cpu_to_le32(upper_32_bits(cmd->frame_phys));
185362306a36Sopenharmony_ci		dle->if_length = (frame_len >> 2) & 0xFFF;
185462306a36Sopenharmony_ci	} else {
185562306a36Sopenharmony_ci		memcpy(ib_entry, ib_frame, frame_len);
185662306a36Sopenharmony_ci	}
185762306a36Sopenharmony_ci	return MV_QUEUE_COMMAND_RESULT_SENT;
185862306a36Sopenharmony_ci}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_cistatic void mvumi_fire_cmd(struct mvumi_hba *mhba, struct mvumi_cmd *cmd)
186162306a36Sopenharmony_ci{
186262306a36Sopenharmony_ci	unsigned short num_of_cl_sent = 0;
186362306a36Sopenharmony_ci	unsigned int count;
186462306a36Sopenharmony_ci	enum mvumi_qc_result result;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	if (cmd)
186762306a36Sopenharmony_ci		list_add_tail(&cmd->queue_pointer, &mhba->waiting_req_list);
186862306a36Sopenharmony_ci	count = mhba->instancet->check_ib_list(mhba);
186962306a36Sopenharmony_ci	if (list_empty(&mhba->waiting_req_list) || !count)
187062306a36Sopenharmony_ci		return;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	do {
187362306a36Sopenharmony_ci		cmd = list_first_entry(&mhba->waiting_req_list,
187462306a36Sopenharmony_ci				       struct mvumi_cmd, queue_pointer);
187562306a36Sopenharmony_ci		list_del_init(&cmd->queue_pointer);
187662306a36Sopenharmony_ci		result = mvumi_send_command(mhba, cmd);
187762306a36Sopenharmony_ci		switch (result) {
187862306a36Sopenharmony_ci		case MV_QUEUE_COMMAND_RESULT_SENT:
187962306a36Sopenharmony_ci			num_of_cl_sent++;
188062306a36Sopenharmony_ci			break;
188162306a36Sopenharmony_ci		case MV_QUEUE_COMMAND_RESULT_NO_RESOURCE:
188262306a36Sopenharmony_ci			list_add(&cmd->queue_pointer, &mhba->waiting_req_list);
188362306a36Sopenharmony_ci			if (num_of_cl_sent > 0)
188462306a36Sopenharmony_ci				mvumi_send_ib_list_entry(mhba);
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci			return;
188762306a36Sopenharmony_ci		}
188862306a36Sopenharmony_ci	} while (!list_empty(&mhba->waiting_req_list) && count--);
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	if (num_of_cl_sent > 0)
189162306a36Sopenharmony_ci		mvumi_send_ib_list_entry(mhba);
189262306a36Sopenharmony_ci}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci/**
189562306a36Sopenharmony_ci * mvumi_enable_intr -	Enables interrupts
189662306a36Sopenharmony_ci * @mhba:		Adapter soft state
189762306a36Sopenharmony_ci */
189862306a36Sopenharmony_cistatic void mvumi_enable_intr(struct mvumi_hba *mhba)
189962306a36Sopenharmony_ci{
190062306a36Sopenharmony_ci	unsigned int mask;
190162306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	iowrite32(regs->int_drbl_int_mask, regs->arm_to_pciea_mask_reg);
190462306a36Sopenharmony_ci	mask = ioread32(regs->enpointa_mask_reg);
190562306a36Sopenharmony_ci	mask |= regs->int_dl_cpu2pciea | regs->int_comaout | regs->int_comaerr;
190662306a36Sopenharmony_ci	iowrite32(mask, regs->enpointa_mask_reg);
190762306a36Sopenharmony_ci}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci/**
191062306a36Sopenharmony_ci * mvumi_disable_intr -Disables interrupt
191162306a36Sopenharmony_ci * @mhba:		Adapter soft state
191262306a36Sopenharmony_ci */
191362306a36Sopenharmony_cistatic void mvumi_disable_intr(struct mvumi_hba *mhba)
191462306a36Sopenharmony_ci{
191562306a36Sopenharmony_ci	unsigned int mask;
191662306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	iowrite32(0, regs->arm_to_pciea_mask_reg);
191962306a36Sopenharmony_ci	mask = ioread32(regs->enpointa_mask_reg);
192062306a36Sopenharmony_ci	mask &= ~(regs->int_dl_cpu2pciea | regs->int_comaout |
192162306a36Sopenharmony_ci							regs->int_comaerr);
192262306a36Sopenharmony_ci	iowrite32(mask, regs->enpointa_mask_reg);
192362306a36Sopenharmony_ci}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_cistatic int mvumi_clear_intr(void *extend)
192662306a36Sopenharmony_ci{
192762306a36Sopenharmony_ci	struct mvumi_hba *mhba = (struct mvumi_hba *) extend;
192862306a36Sopenharmony_ci	unsigned int status, isr_status = 0, tmp = 0;
192962306a36Sopenharmony_ci	struct mvumi_hw_regs *regs = mhba->regs;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	status = ioread32(regs->main_int_cause_reg);
193262306a36Sopenharmony_ci	if (!(status & regs->int_mu) || status == 0xFFFFFFFF)
193362306a36Sopenharmony_ci		return 1;
193462306a36Sopenharmony_ci	if (unlikely(status & regs->int_comaerr)) {
193562306a36Sopenharmony_ci		tmp = ioread32(regs->outb_isr_cause);
193662306a36Sopenharmony_ci		if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) {
193762306a36Sopenharmony_ci			if (tmp & regs->clic_out_err) {
193862306a36Sopenharmony_ci				iowrite32(tmp & regs->clic_out_err,
193962306a36Sopenharmony_ci							regs->outb_isr_cause);
194062306a36Sopenharmony_ci			}
194162306a36Sopenharmony_ci		} else {
194262306a36Sopenharmony_ci			if (tmp & (regs->clic_in_err | regs->clic_out_err))
194362306a36Sopenharmony_ci				iowrite32(tmp & (regs->clic_in_err |
194462306a36Sopenharmony_ci						regs->clic_out_err),
194562306a36Sopenharmony_ci						regs->outb_isr_cause);
194662306a36Sopenharmony_ci		}
194762306a36Sopenharmony_ci		status ^= mhba->regs->int_comaerr;
194862306a36Sopenharmony_ci		/* inbound or outbound parity error, command will timeout */
194962306a36Sopenharmony_ci	}
195062306a36Sopenharmony_ci	if (status & regs->int_comaout) {
195162306a36Sopenharmony_ci		tmp = ioread32(regs->outb_isr_cause);
195262306a36Sopenharmony_ci		if (tmp & regs->clic_irq)
195362306a36Sopenharmony_ci			iowrite32(tmp & regs->clic_irq, regs->outb_isr_cause);
195462306a36Sopenharmony_ci	}
195562306a36Sopenharmony_ci	if (status & regs->int_dl_cpu2pciea) {
195662306a36Sopenharmony_ci		isr_status = ioread32(regs->arm_to_pciea_drbl_reg);
195762306a36Sopenharmony_ci		if (isr_status)
195862306a36Sopenharmony_ci			iowrite32(isr_status, regs->arm_to_pciea_drbl_reg);
195962306a36Sopenharmony_ci	}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	mhba->global_isr = status;
196262306a36Sopenharmony_ci	mhba->isr_status = isr_status;
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	return 0;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci/**
196862306a36Sopenharmony_ci * mvumi_read_fw_status_reg - returns the current FW status value
196962306a36Sopenharmony_ci * @mhba:		Adapter soft state
197062306a36Sopenharmony_ci */
197162306a36Sopenharmony_cistatic unsigned int mvumi_read_fw_status_reg(struct mvumi_hba *mhba)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	unsigned int status;
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	status = ioread32(mhba->regs->arm_to_pciea_drbl_reg);
197662306a36Sopenharmony_ci	if (status)
197762306a36Sopenharmony_ci		iowrite32(status, mhba->regs->arm_to_pciea_drbl_reg);
197862306a36Sopenharmony_ci	return status;
197962306a36Sopenharmony_ci}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_cistatic struct mvumi_instance_template mvumi_instance_9143 = {
198262306a36Sopenharmony_ci	.fire_cmd = mvumi_fire_cmd,
198362306a36Sopenharmony_ci	.enable_intr = mvumi_enable_intr,
198462306a36Sopenharmony_ci	.disable_intr = mvumi_disable_intr,
198562306a36Sopenharmony_ci	.clear_intr = mvumi_clear_intr,
198662306a36Sopenharmony_ci	.read_fw_status_reg = mvumi_read_fw_status_reg,
198762306a36Sopenharmony_ci	.check_ib_list = mvumi_check_ib_list_9143,
198862306a36Sopenharmony_ci	.check_ob_list = mvumi_check_ob_list_9143,
198962306a36Sopenharmony_ci	.reset_host = mvumi_reset_host_9143,
199062306a36Sopenharmony_ci};
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_cistatic struct mvumi_instance_template mvumi_instance_9580 = {
199362306a36Sopenharmony_ci	.fire_cmd = mvumi_fire_cmd,
199462306a36Sopenharmony_ci	.enable_intr = mvumi_enable_intr,
199562306a36Sopenharmony_ci	.disable_intr = mvumi_disable_intr,
199662306a36Sopenharmony_ci	.clear_intr = mvumi_clear_intr,
199762306a36Sopenharmony_ci	.read_fw_status_reg = mvumi_read_fw_status_reg,
199862306a36Sopenharmony_ci	.check_ib_list = mvumi_check_ib_list_9580,
199962306a36Sopenharmony_ci	.check_ob_list = mvumi_check_ob_list_9580,
200062306a36Sopenharmony_ci	.reset_host = mvumi_reset_host_9580,
200162306a36Sopenharmony_ci};
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_cistatic int mvumi_slave_configure(struct scsi_device *sdev)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	struct mvumi_hba *mhba;
200662306a36Sopenharmony_ci	unsigned char bitcount = sizeof(unsigned char) * 8;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	mhba = (struct mvumi_hba *) sdev->host->hostdata;
200962306a36Sopenharmony_ci	if (sdev->id >= mhba->max_target_id)
201062306a36Sopenharmony_ci		return -EINVAL;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	mhba->target_map[sdev->id / bitcount] |= (1 << (sdev->id % bitcount));
201362306a36Sopenharmony_ci	return 0;
201462306a36Sopenharmony_ci}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci/**
201762306a36Sopenharmony_ci * mvumi_build_frame -	Prepares a direct cdb (DCDB) command
201862306a36Sopenharmony_ci * @mhba:		Adapter soft state
201962306a36Sopenharmony_ci * @scmd:		SCSI command
202062306a36Sopenharmony_ci * @cmd:		Command to be prepared in
202162306a36Sopenharmony_ci *
202262306a36Sopenharmony_ci * This function prepares CDB commands. These are typcially pass-through
202362306a36Sopenharmony_ci * commands to the devices.
202462306a36Sopenharmony_ci */
202562306a36Sopenharmony_cistatic unsigned char mvumi_build_frame(struct mvumi_hba *mhba,
202662306a36Sopenharmony_ci				struct scsi_cmnd *scmd, struct mvumi_cmd *cmd)
202762306a36Sopenharmony_ci{
202862306a36Sopenharmony_ci	struct mvumi_msg_frame *pframe;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	cmd->scmd = scmd;
203162306a36Sopenharmony_ci	cmd->cmd_status = REQ_STATUS_PENDING;
203262306a36Sopenharmony_ci	pframe = cmd->frame;
203362306a36Sopenharmony_ci	pframe->device_id = ((unsigned short) scmd->device->id) |
203462306a36Sopenharmony_ci				(((unsigned short) scmd->device->lun) << 8);
203562306a36Sopenharmony_ci	pframe->cmd_flag = 0;
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	switch (scmd->sc_data_direction) {
203862306a36Sopenharmony_ci	case DMA_NONE:
203962306a36Sopenharmony_ci		pframe->cmd_flag |= CMD_FLAG_NON_DATA;
204062306a36Sopenharmony_ci		break;
204162306a36Sopenharmony_ci	case DMA_FROM_DEVICE:
204262306a36Sopenharmony_ci		pframe->cmd_flag |= CMD_FLAG_DATA_IN;
204362306a36Sopenharmony_ci		break;
204462306a36Sopenharmony_ci	case DMA_TO_DEVICE:
204562306a36Sopenharmony_ci		pframe->cmd_flag |= CMD_FLAG_DATA_OUT;
204662306a36Sopenharmony_ci		break;
204762306a36Sopenharmony_ci	case DMA_BIDIRECTIONAL:
204862306a36Sopenharmony_ci	default:
204962306a36Sopenharmony_ci		dev_warn(&mhba->pdev->dev, "unexpected data direction[%d] "
205062306a36Sopenharmony_ci			"cmd[0x%x]\n", scmd->sc_data_direction, scmd->cmnd[0]);
205162306a36Sopenharmony_ci		goto error;
205262306a36Sopenharmony_ci	}
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	pframe->cdb_length = scmd->cmd_len;
205562306a36Sopenharmony_ci	memcpy(pframe->cdb, scmd->cmnd, pframe->cdb_length);
205662306a36Sopenharmony_ci	pframe->req_function = CL_FUN_SCSI_CMD;
205762306a36Sopenharmony_ci	if (scsi_bufflen(scmd)) {
205862306a36Sopenharmony_ci		if (mvumi_make_sgl(mhba, scmd, &pframe->payload[0],
205962306a36Sopenharmony_ci			&pframe->sg_counts))
206062306a36Sopenharmony_ci			goto error;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci		pframe->data_transfer_length = scsi_bufflen(scmd);
206362306a36Sopenharmony_ci	} else {
206462306a36Sopenharmony_ci		pframe->sg_counts = 0;
206562306a36Sopenharmony_ci		pframe->data_transfer_length = 0;
206662306a36Sopenharmony_ci	}
206762306a36Sopenharmony_ci	return 0;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_cierror:
207062306a36Sopenharmony_ci	scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, 0x24, 0);
207162306a36Sopenharmony_ci	return -1;
207262306a36Sopenharmony_ci}
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci/**
207562306a36Sopenharmony_ci * mvumi_queue_command -	Queue entry point
207662306a36Sopenharmony_ci * @shost:			Scsi host to queue command on
207762306a36Sopenharmony_ci * @scmd:			SCSI command to be queued
207862306a36Sopenharmony_ci */
207962306a36Sopenharmony_cistatic int mvumi_queue_command(struct Scsi_Host *shost,
208062306a36Sopenharmony_ci					struct scsi_cmnd *scmd)
208162306a36Sopenharmony_ci{
208262306a36Sopenharmony_ci	struct mvumi_cmd *cmd;
208362306a36Sopenharmony_ci	struct mvumi_hba *mhba;
208462306a36Sopenharmony_ci	unsigned long irq_flags;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, irq_flags);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	mhba = (struct mvumi_hba *) shost->hostdata;
208962306a36Sopenharmony_ci	scmd->result = 0;
209062306a36Sopenharmony_ci	cmd = mvumi_get_cmd(mhba);
209162306a36Sopenharmony_ci	if (unlikely(!cmd)) {
209262306a36Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, irq_flags);
209362306a36Sopenharmony_ci		return SCSI_MLQUEUE_HOST_BUSY;
209462306a36Sopenharmony_ci	}
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	if (unlikely(mvumi_build_frame(mhba, scmd, cmd)))
209762306a36Sopenharmony_ci		goto out_return_cmd;
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	cmd->scmd = scmd;
210062306a36Sopenharmony_ci	mvumi_priv(scmd)->cmd_priv = cmd;
210162306a36Sopenharmony_ci	mhba->instancet->fire_cmd(mhba, cmd);
210262306a36Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, irq_flags);
210362306a36Sopenharmony_ci	return 0;
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ciout_return_cmd:
210662306a36Sopenharmony_ci	mvumi_return_cmd(mhba, cmd);
210762306a36Sopenharmony_ci	scsi_done(scmd);
210862306a36Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, irq_flags);
210962306a36Sopenharmony_ci	return 0;
211062306a36Sopenharmony_ci}
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_cistatic enum scsi_timeout_action mvumi_timed_out(struct scsi_cmnd *scmd)
211362306a36Sopenharmony_ci{
211462306a36Sopenharmony_ci	struct mvumi_cmd *cmd = mvumi_priv(scmd)->cmd_priv;
211562306a36Sopenharmony_ci	struct Scsi_Host *host = scmd->device->host;
211662306a36Sopenharmony_ci	struct mvumi_hba *mhba = shost_priv(host);
211762306a36Sopenharmony_ci	unsigned long flags;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	spin_lock_irqsave(mhba->shost->host_lock, flags);
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	if (mhba->tag_cmd[cmd->frame->tag]) {
212262306a36Sopenharmony_ci		mhba->tag_cmd[cmd->frame->tag] = NULL;
212362306a36Sopenharmony_ci		tag_release_one(mhba, &mhba->tag_pool, cmd->frame->tag);
212462306a36Sopenharmony_ci	}
212562306a36Sopenharmony_ci	if (!list_empty(&cmd->queue_pointer))
212662306a36Sopenharmony_ci		list_del_init(&cmd->queue_pointer);
212762306a36Sopenharmony_ci	else
212862306a36Sopenharmony_ci		atomic_dec(&mhba->fw_outstanding);
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	scmd->result = (DID_ABORT << 16);
213162306a36Sopenharmony_ci	mvumi_priv(scmd)->cmd_priv = NULL;
213262306a36Sopenharmony_ci	if (scsi_bufflen(scmd)) {
213362306a36Sopenharmony_ci		dma_unmap_sg(&mhba->pdev->dev, scsi_sglist(scmd),
213462306a36Sopenharmony_ci			     scsi_sg_count(scmd),
213562306a36Sopenharmony_ci			     scmd->sc_data_direction);
213662306a36Sopenharmony_ci	}
213762306a36Sopenharmony_ci	mvumi_return_cmd(mhba, cmd);
213862306a36Sopenharmony_ci	spin_unlock_irqrestore(mhba->shost->host_lock, flags);
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci	return SCSI_EH_NOT_HANDLED;
214162306a36Sopenharmony_ci}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_cistatic int
214462306a36Sopenharmony_cimvumi_bios_param(struct scsi_device *sdev, struct block_device *bdev,
214562306a36Sopenharmony_ci			sector_t capacity, int geom[])
214662306a36Sopenharmony_ci{
214762306a36Sopenharmony_ci	int heads, sectors;
214862306a36Sopenharmony_ci	sector_t cylinders;
214962306a36Sopenharmony_ci	unsigned long tmp;
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	heads = 64;
215262306a36Sopenharmony_ci	sectors = 32;
215362306a36Sopenharmony_ci	tmp = heads * sectors;
215462306a36Sopenharmony_ci	cylinders = capacity;
215562306a36Sopenharmony_ci	sector_div(cylinders, tmp);
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	if (capacity >= 0x200000) {
215862306a36Sopenharmony_ci		heads = 255;
215962306a36Sopenharmony_ci		sectors = 63;
216062306a36Sopenharmony_ci		tmp = heads * sectors;
216162306a36Sopenharmony_ci		cylinders = capacity;
216262306a36Sopenharmony_ci		sector_div(cylinders, tmp);
216362306a36Sopenharmony_ci	}
216462306a36Sopenharmony_ci	geom[0] = heads;
216562306a36Sopenharmony_ci	geom[1] = sectors;
216662306a36Sopenharmony_ci	geom[2] = cylinders;
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	return 0;
216962306a36Sopenharmony_ci}
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_cistatic const struct scsi_host_template mvumi_template = {
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	.module = THIS_MODULE,
217462306a36Sopenharmony_ci	.name = "Marvell Storage Controller",
217562306a36Sopenharmony_ci	.slave_configure = mvumi_slave_configure,
217662306a36Sopenharmony_ci	.queuecommand = mvumi_queue_command,
217762306a36Sopenharmony_ci	.eh_timed_out = mvumi_timed_out,
217862306a36Sopenharmony_ci	.eh_host_reset_handler = mvumi_host_reset,
217962306a36Sopenharmony_ci	.bios_param = mvumi_bios_param,
218062306a36Sopenharmony_ci	.dma_boundary = PAGE_SIZE - 1,
218162306a36Sopenharmony_ci	.this_id = -1,
218262306a36Sopenharmony_ci	.cmd_size = sizeof(struct mvumi_cmd_priv),
218362306a36Sopenharmony_ci};
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_cistatic int mvumi_cfg_hw_reg(struct mvumi_hba *mhba)
218662306a36Sopenharmony_ci{
218762306a36Sopenharmony_ci	void *base = NULL;
218862306a36Sopenharmony_ci	struct mvumi_hw_regs *regs;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	switch (mhba->pdev->device) {
219162306a36Sopenharmony_ci	case PCI_DEVICE_ID_MARVELL_MV9143:
219262306a36Sopenharmony_ci		mhba->mmio = mhba->base_addr[0];
219362306a36Sopenharmony_ci		base = mhba->mmio;
219462306a36Sopenharmony_ci		if (!mhba->regs) {
219562306a36Sopenharmony_ci			mhba->regs = kzalloc(sizeof(*regs), GFP_KERNEL);
219662306a36Sopenharmony_ci			if (mhba->regs == NULL)
219762306a36Sopenharmony_ci				return -ENOMEM;
219862306a36Sopenharmony_ci		}
219962306a36Sopenharmony_ci		regs = mhba->regs;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci		/* For Arm */
220262306a36Sopenharmony_ci		regs->ctrl_sts_reg          = base + 0x20104;
220362306a36Sopenharmony_ci		regs->rstoutn_mask_reg      = base + 0x20108;
220462306a36Sopenharmony_ci		regs->sys_soft_rst_reg      = base + 0x2010C;
220562306a36Sopenharmony_ci		regs->main_int_cause_reg    = base + 0x20200;
220662306a36Sopenharmony_ci		regs->enpointa_mask_reg     = base + 0x2020C;
220762306a36Sopenharmony_ci		regs->rstoutn_en_reg        = base + 0xF1400;
220862306a36Sopenharmony_ci		/* For Doorbell */
220962306a36Sopenharmony_ci		regs->pciea_to_arm_drbl_reg = base + 0x20400;
221062306a36Sopenharmony_ci		regs->arm_to_pciea_drbl_reg = base + 0x20408;
221162306a36Sopenharmony_ci		regs->arm_to_pciea_mask_reg = base + 0x2040C;
221262306a36Sopenharmony_ci		regs->pciea_to_arm_msg0     = base + 0x20430;
221362306a36Sopenharmony_ci		regs->pciea_to_arm_msg1     = base + 0x20434;
221462306a36Sopenharmony_ci		regs->arm_to_pciea_msg0     = base + 0x20438;
221562306a36Sopenharmony_ci		regs->arm_to_pciea_msg1     = base + 0x2043C;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci		/* For Message Unit */
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci		regs->inb_aval_count_basel  = base + 0x508;
222062306a36Sopenharmony_ci		regs->inb_aval_count_baseh  = base + 0x50C;
222162306a36Sopenharmony_ci		regs->inb_write_pointer     = base + 0x518;
222262306a36Sopenharmony_ci		regs->inb_read_pointer      = base + 0x51C;
222362306a36Sopenharmony_ci		regs->outb_coal_cfg         = base + 0x568;
222462306a36Sopenharmony_ci		regs->outb_copy_basel       = base + 0x5B0;
222562306a36Sopenharmony_ci		regs->outb_copy_baseh       = base + 0x5B4;
222662306a36Sopenharmony_ci		regs->outb_copy_pointer     = base + 0x544;
222762306a36Sopenharmony_ci		regs->outb_read_pointer     = base + 0x548;
222862306a36Sopenharmony_ci		regs->outb_isr_cause        = base + 0x560;
222962306a36Sopenharmony_ci		regs->outb_coal_cfg         = base + 0x568;
223062306a36Sopenharmony_ci		/* Bit setting for HW */
223162306a36Sopenharmony_ci		regs->int_comaout           = 1 << 8;
223262306a36Sopenharmony_ci		regs->int_comaerr           = 1 << 6;
223362306a36Sopenharmony_ci		regs->int_dl_cpu2pciea      = 1 << 1;
223462306a36Sopenharmony_ci		regs->cl_pointer_toggle     = 1 << 12;
223562306a36Sopenharmony_ci		regs->clic_irq              = 1 << 1;
223662306a36Sopenharmony_ci		regs->clic_in_err           = 1 << 8;
223762306a36Sopenharmony_ci		regs->clic_out_err          = 1 << 12;
223862306a36Sopenharmony_ci		regs->cl_slot_num_mask      = 0xFFF;
223962306a36Sopenharmony_ci		regs->int_drbl_int_mask     = 0x3FFFFFFF;
224062306a36Sopenharmony_ci		regs->int_mu = regs->int_dl_cpu2pciea | regs->int_comaout |
224162306a36Sopenharmony_ci							regs->int_comaerr;
224262306a36Sopenharmony_ci		break;
224362306a36Sopenharmony_ci	case PCI_DEVICE_ID_MARVELL_MV9580:
224462306a36Sopenharmony_ci		mhba->mmio = mhba->base_addr[2];
224562306a36Sopenharmony_ci		base = mhba->mmio;
224662306a36Sopenharmony_ci		if (!mhba->regs) {
224762306a36Sopenharmony_ci			mhba->regs = kzalloc(sizeof(*regs), GFP_KERNEL);
224862306a36Sopenharmony_ci			if (mhba->regs == NULL)
224962306a36Sopenharmony_ci				return -ENOMEM;
225062306a36Sopenharmony_ci		}
225162306a36Sopenharmony_ci		regs = mhba->regs;
225262306a36Sopenharmony_ci		/* For Arm */
225362306a36Sopenharmony_ci		regs->ctrl_sts_reg          = base + 0x20104;
225462306a36Sopenharmony_ci		regs->rstoutn_mask_reg      = base + 0x1010C;
225562306a36Sopenharmony_ci		regs->sys_soft_rst_reg      = base + 0x10108;
225662306a36Sopenharmony_ci		regs->main_int_cause_reg    = base + 0x10200;
225762306a36Sopenharmony_ci		regs->enpointa_mask_reg     = base + 0x1020C;
225862306a36Sopenharmony_ci		regs->rstoutn_en_reg        = base + 0xF1400;
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci		/* For Doorbell */
226162306a36Sopenharmony_ci		regs->pciea_to_arm_drbl_reg = base + 0x10460;
226262306a36Sopenharmony_ci		regs->arm_to_pciea_drbl_reg = base + 0x10480;
226362306a36Sopenharmony_ci		regs->arm_to_pciea_mask_reg = base + 0x10484;
226462306a36Sopenharmony_ci		regs->pciea_to_arm_msg0     = base + 0x10400;
226562306a36Sopenharmony_ci		regs->pciea_to_arm_msg1     = base + 0x10404;
226662306a36Sopenharmony_ci		regs->arm_to_pciea_msg0     = base + 0x10420;
226762306a36Sopenharmony_ci		regs->arm_to_pciea_msg1     = base + 0x10424;
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci		/* For reset*/
227062306a36Sopenharmony_ci		regs->reset_request         = base + 0x10108;
227162306a36Sopenharmony_ci		regs->reset_enable          = base + 0x1010c;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci		/* For Message Unit */
227462306a36Sopenharmony_ci		regs->inb_aval_count_basel  = base + 0x4008;
227562306a36Sopenharmony_ci		regs->inb_aval_count_baseh  = base + 0x400C;
227662306a36Sopenharmony_ci		regs->inb_write_pointer     = base + 0x4018;
227762306a36Sopenharmony_ci		regs->inb_read_pointer      = base + 0x401C;
227862306a36Sopenharmony_ci		regs->outb_copy_basel       = base + 0x4058;
227962306a36Sopenharmony_ci		regs->outb_copy_baseh       = base + 0x405C;
228062306a36Sopenharmony_ci		regs->outb_copy_pointer     = base + 0x406C;
228162306a36Sopenharmony_ci		regs->outb_read_pointer     = base + 0x4070;
228262306a36Sopenharmony_ci		regs->outb_coal_cfg         = base + 0x4080;
228362306a36Sopenharmony_ci		regs->outb_isr_cause        = base + 0x4088;
228462306a36Sopenharmony_ci		/* Bit setting for HW */
228562306a36Sopenharmony_ci		regs->int_comaout           = 1 << 4;
228662306a36Sopenharmony_ci		regs->int_dl_cpu2pciea      = 1 << 12;
228762306a36Sopenharmony_ci		regs->int_comaerr           = 1 << 29;
228862306a36Sopenharmony_ci		regs->cl_pointer_toggle     = 1 << 14;
228962306a36Sopenharmony_ci		regs->cl_slot_num_mask      = 0x3FFF;
229062306a36Sopenharmony_ci		regs->clic_irq              = 1 << 0;
229162306a36Sopenharmony_ci		regs->clic_out_err          = 1 << 1;
229262306a36Sopenharmony_ci		regs->int_drbl_int_mask     = 0x3FFFFFFF;
229362306a36Sopenharmony_ci		regs->int_mu = regs->int_dl_cpu2pciea | regs->int_comaout;
229462306a36Sopenharmony_ci		break;
229562306a36Sopenharmony_ci	default:
229662306a36Sopenharmony_ci		return -1;
229762306a36Sopenharmony_ci	}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	return 0;
230062306a36Sopenharmony_ci}
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci/**
230362306a36Sopenharmony_ci * mvumi_init_fw -	Initializes the FW
230462306a36Sopenharmony_ci * @mhba:		Adapter soft state
230562306a36Sopenharmony_ci *
230662306a36Sopenharmony_ci * This is the main function for initializing firmware.
230762306a36Sopenharmony_ci */
230862306a36Sopenharmony_cistatic int mvumi_init_fw(struct mvumi_hba *mhba)
230962306a36Sopenharmony_ci{
231062306a36Sopenharmony_ci	int ret = 0;
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	if (pci_request_regions(mhba->pdev, MV_DRIVER_NAME)) {
231362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "IO memory region busy!\n");
231462306a36Sopenharmony_ci		return -EBUSY;
231562306a36Sopenharmony_ci	}
231662306a36Sopenharmony_ci	ret = mvumi_map_pci_addr(mhba->pdev, mhba->base_addr);
231762306a36Sopenharmony_ci	if (ret)
231862306a36Sopenharmony_ci		goto fail_ioremap;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	switch (mhba->pdev->device) {
232162306a36Sopenharmony_ci	case PCI_DEVICE_ID_MARVELL_MV9143:
232262306a36Sopenharmony_ci		mhba->instancet = &mvumi_instance_9143;
232362306a36Sopenharmony_ci		mhba->io_seq = 0;
232462306a36Sopenharmony_ci		mhba->max_sge = MVUMI_MAX_SG_ENTRY;
232562306a36Sopenharmony_ci		mhba->request_id_enabled = 1;
232662306a36Sopenharmony_ci		break;
232762306a36Sopenharmony_ci	case PCI_DEVICE_ID_MARVELL_MV9580:
232862306a36Sopenharmony_ci		mhba->instancet = &mvumi_instance_9580;
232962306a36Sopenharmony_ci		mhba->io_seq = 0;
233062306a36Sopenharmony_ci		mhba->max_sge = MVUMI_MAX_SG_ENTRY;
233162306a36Sopenharmony_ci		break;
233262306a36Sopenharmony_ci	default:
233362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "device 0x%x not supported!\n",
233462306a36Sopenharmony_ci							mhba->pdev->device);
233562306a36Sopenharmony_ci		mhba->instancet = NULL;
233662306a36Sopenharmony_ci		ret = -EINVAL;
233762306a36Sopenharmony_ci		goto fail_alloc_mem;
233862306a36Sopenharmony_ci	}
233962306a36Sopenharmony_ci	dev_dbg(&mhba->pdev->dev, "device id : %04X is found.\n",
234062306a36Sopenharmony_ci							mhba->pdev->device);
234162306a36Sopenharmony_ci	ret = mvumi_cfg_hw_reg(mhba);
234262306a36Sopenharmony_ci	if (ret) {
234362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
234462306a36Sopenharmony_ci			"failed to allocate memory for reg\n");
234562306a36Sopenharmony_ci		ret = -ENOMEM;
234662306a36Sopenharmony_ci		goto fail_alloc_mem;
234762306a36Sopenharmony_ci	}
234862306a36Sopenharmony_ci	mhba->handshake_page = dma_alloc_coherent(&mhba->pdev->dev,
234962306a36Sopenharmony_ci			HSP_MAX_SIZE, &mhba->handshake_page_phys, GFP_KERNEL);
235062306a36Sopenharmony_ci	if (!mhba->handshake_page) {
235162306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
235262306a36Sopenharmony_ci			"failed to allocate memory for handshake\n");
235362306a36Sopenharmony_ci		ret = -ENOMEM;
235462306a36Sopenharmony_ci		goto fail_alloc_page;
235562306a36Sopenharmony_ci	}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	if (mvumi_start(mhba)) {
235862306a36Sopenharmony_ci		ret = -EINVAL;
235962306a36Sopenharmony_ci		goto fail_ready_state;
236062306a36Sopenharmony_ci	}
236162306a36Sopenharmony_ci	ret = mvumi_alloc_cmds(mhba);
236262306a36Sopenharmony_ci	if (ret)
236362306a36Sopenharmony_ci		goto fail_ready_state;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	return 0;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_cifail_ready_state:
236862306a36Sopenharmony_ci	mvumi_release_mem_resource(mhba);
236962306a36Sopenharmony_ci	dma_free_coherent(&mhba->pdev->dev, HSP_MAX_SIZE,
237062306a36Sopenharmony_ci		mhba->handshake_page, mhba->handshake_page_phys);
237162306a36Sopenharmony_cifail_alloc_page:
237262306a36Sopenharmony_ci	kfree(mhba->regs);
237362306a36Sopenharmony_cifail_alloc_mem:
237462306a36Sopenharmony_ci	mvumi_unmap_pci_addr(mhba->pdev, mhba->base_addr);
237562306a36Sopenharmony_cifail_ioremap:
237662306a36Sopenharmony_ci	pci_release_regions(mhba->pdev);
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	return ret;
237962306a36Sopenharmony_ci}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci/**
238262306a36Sopenharmony_ci * mvumi_io_attach -	Attaches this driver to SCSI mid-layer
238362306a36Sopenharmony_ci * @mhba:		Adapter soft state
238462306a36Sopenharmony_ci */
238562306a36Sopenharmony_cistatic int mvumi_io_attach(struct mvumi_hba *mhba)
238662306a36Sopenharmony_ci{
238762306a36Sopenharmony_ci	struct Scsi_Host *host = mhba->shost;
238862306a36Sopenharmony_ci	struct scsi_device *sdev = NULL;
238962306a36Sopenharmony_ci	int ret;
239062306a36Sopenharmony_ci	unsigned int max_sg = (mhba->ib_max_size -
239162306a36Sopenharmony_ci		sizeof(struct mvumi_msg_frame)) / sizeof(struct mvumi_sgl);
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	host->irq = mhba->pdev->irq;
239462306a36Sopenharmony_ci	host->unique_id = mhba->unique_id;
239562306a36Sopenharmony_ci	host->can_queue = (mhba->max_io - 1) ? (mhba->max_io - 1) : 1;
239662306a36Sopenharmony_ci	host->sg_tablesize = mhba->max_sge > max_sg ? max_sg : mhba->max_sge;
239762306a36Sopenharmony_ci	host->max_sectors = mhba->max_transfer_size / 512;
239862306a36Sopenharmony_ci	host->cmd_per_lun = (mhba->max_io - 1) ? (mhba->max_io - 1) : 1;
239962306a36Sopenharmony_ci	host->max_id = mhba->max_target_id;
240062306a36Sopenharmony_ci	host->max_cmd_len = MAX_COMMAND_SIZE;
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	ret = scsi_add_host(host, &mhba->pdev->dev);
240362306a36Sopenharmony_ci	if (ret) {
240462306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "scsi_add_host failed\n");
240562306a36Sopenharmony_ci		return ret;
240662306a36Sopenharmony_ci	}
240762306a36Sopenharmony_ci	mhba->fw_flag |= MVUMI_FW_ATTACH;
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	mutex_lock(&mhba->sas_discovery_mutex);
241062306a36Sopenharmony_ci	if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580)
241162306a36Sopenharmony_ci		ret = scsi_add_device(host, 0, mhba->max_target_id - 1, 0);
241262306a36Sopenharmony_ci	else
241362306a36Sopenharmony_ci		ret = 0;
241462306a36Sopenharmony_ci	if (ret) {
241562306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev, "add virtual device failed\n");
241662306a36Sopenharmony_ci		mutex_unlock(&mhba->sas_discovery_mutex);
241762306a36Sopenharmony_ci		goto fail_add_device;
241862306a36Sopenharmony_ci	}
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	mhba->dm_thread = kthread_create(mvumi_rescan_bus,
242162306a36Sopenharmony_ci						mhba, "mvumi_scanthread");
242262306a36Sopenharmony_ci	if (IS_ERR(mhba->dm_thread)) {
242362306a36Sopenharmony_ci		dev_err(&mhba->pdev->dev,
242462306a36Sopenharmony_ci			"failed to create device scan thread\n");
242562306a36Sopenharmony_ci		ret = PTR_ERR(mhba->dm_thread);
242662306a36Sopenharmony_ci		mutex_unlock(&mhba->sas_discovery_mutex);
242762306a36Sopenharmony_ci		goto fail_create_thread;
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci	atomic_set(&mhba->pnp_count, 1);
243062306a36Sopenharmony_ci	wake_up_process(mhba->dm_thread);
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	mutex_unlock(&mhba->sas_discovery_mutex);
243362306a36Sopenharmony_ci	return 0;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_cifail_create_thread:
243662306a36Sopenharmony_ci	if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580)
243762306a36Sopenharmony_ci		sdev = scsi_device_lookup(mhba->shost, 0,
243862306a36Sopenharmony_ci						mhba->max_target_id - 1, 0);
243962306a36Sopenharmony_ci	if (sdev) {
244062306a36Sopenharmony_ci		scsi_remove_device(sdev);
244162306a36Sopenharmony_ci		scsi_device_put(sdev);
244262306a36Sopenharmony_ci	}
244362306a36Sopenharmony_cifail_add_device:
244462306a36Sopenharmony_ci	scsi_remove_host(mhba->shost);
244562306a36Sopenharmony_ci	return ret;
244662306a36Sopenharmony_ci}
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci/**
244962306a36Sopenharmony_ci * mvumi_probe_one -	PCI hotplug entry point
245062306a36Sopenharmony_ci * @pdev:		PCI device structure
245162306a36Sopenharmony_ci * @id:			PCI ids of supported hotplugged adapter
245262306a36Sopenharmony_ci */
245362306a36Sopenharmony_cistatic int mvumi_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
245462306a36Sopenharmony_ci{
245562306a36Sopenharmony_ci	struct Scsi_Host *host;
245662306a36Sopenharmony_ci	struct mvumi_hba *mhba;
245762306a36Sopenharmony_ci	int ret;
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	dev_dbg(&pdev->dev, " %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
246062306a36Sopenharmony_ci			pdev->vendor, pdev->device, pdev->subsystem_vendor,
246162306a36Sopenharmony_ci			pdev->subsystem_device);
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	ret = pci_enable_device(pdev);
246462306a36Sopenharmony_ci	if (ret)
246562306a36Sopenharmony_ci		return ret;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci	ret = mvumi_pci_set_master(pdev);
246862306a36Sopenharmony_ci	if (ret)
246962306a36Sopenharmony_ci		goto fail_set_dma_mask;
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	host = scsi_host_alloc(&mvumi_template, sizeof(*mhba));
247262306a36Sopenharmony_ci	if (!host) {
247362306a36Sopenharmony_ci		dev_err(&pdev->dev, "scsi_host_alloc failed\n");
247462306a36Sopenharmony_ci		ret = -ENOMEM;
247562306a36Sopenharmony_ci		goto fail_alloc_instance;
247662306a36Sopenharmony_ci	}
247762306a36Sopenharmony_ci	mhba = shost_priv(host);
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhba->cmd_pool);
248062306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhba->ob_data_list);
248162306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhba->free_ob_list);
248262306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhba->res_list);
248362306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhba->waiting_req_list);
248462306a36Sopenharmony_ci	mutex_init(&mhba->device_lock);
248562306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhba->mhba_dev_list);
248662306a36Sopenharmony_ci	INIT_LIST_HEAD(&mhba->shost_dev_list);
248762306a36Sopenharmony_ci	atomic_set(&mhba->fw_outstanding, 0);
248862306a36Sopenharmony_ci	init_waitqueue_head(&mhba->int_cmd_wait_q);
248962306a36Sopenharmony_ci	mutex_init(&mhba->sas_discovery_mutex);
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	mhba->pdev = pdev;
249262306a36Sopenharmony_ci	mhba->shost = host;
249362306a36Sopenharmony_ci	mhba->unique_id = pci_dev_id(pdev);
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	ret = mvumi_init_fw(mhba);
249662306a36Sopenharmony_ci	if (ret)
249762306a36Sopenharmony_ci		goto fail_init_fw;
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	ret = request_irq(mhba->pdev->irq, mvumi_isr_handler, IRQF_SHARED,
250062306a36Sopenharmony_ci				"mvumi", mhba);
250162306a36Sopenharmony_ci	if (ret) {
250262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register IRQ\n");
250362306a36Sopenharmony_ci		goto fail_init_irq;
250462306a36Sopenharmony_ci	}
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	mhba->instancet->enable_intr(mhba);
250762306a36Sopenharmony_ci	pci_set_drvdata(pdev, mhba);
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci	ret = mvumi_io_attach(mhba);
251062306a36Sopenharmony_ci	if (ret)
251162306a36Sopenharmony_ci		goto fail_io_attach;
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	mvumi_backup_bar_addr(mhba);
251462306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "probe mvumi driver successfully.\n");
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	return 0;
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_cifail_io_attach:
251962306a36Sopenharmony_ci	mhba->instancet->disable_intr(mhba);
252062306a36Sopenharmony_ci	free_irq(mhba->pdev->irq, mhba);
252162306a36Sopenharmony_cifail_init_irq:
252262306a36Sopenharmony_ci	mvumi_release_fw(mhba);
252362306a36Sopenharmony_cifail_init_fw:
252462306a36Sopenharmony_ci	scsi_host_put(host);
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_cifail_alloc_instance:
252762306a36Sopenharmony_cifail_set_dma_mask:
252862306a36Sopenharmony_ci	pci_disable_device(pdev);
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	return ret;
253162306a36Sopenharmony_ci}
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_cistatic void mvumi_detach_one(struct pci_dev *pdev)
253462306a36Sopenharmony_ci{
253562306a36Sopenharmony_ci	struct Scsi_Host *host;
253662306a36Sopenharmony_ci	struct mvumi_hba *mhba;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	mhba = pci_get_drvdata(pdev);
253962306a36Sopenharmony_ci	if (mhba->dm_thread) {
254062306a36Sopenharmony_ci		kthread_stop(mhba->dm_thread);
254162306a36Sopenharmony_ci		mhba->dm_thread = NULL;
254262306a36Sopenharmony_ci	}
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	mvumi_detach_devices(mhba);
254562306a36Sopenharmony_ci	host = mhba->shost;
254662306a36Sopenharmony_ci	scsi_remove_host(mhba->shost);
254762306a36Sopenharmony_ci	mvumi_flush_cache(mhba);
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci	mhba->instancet->disable_intr(mhba);
255062306a36Sopenharmony_ci	free_irq(mhba->pdev->irq, mhba);
255162306a36Sopenharmony_ci	mvumi_release_fw(mhba);
255262306a36Sopenharmony_ci	scsi_host_put(host);
255362306a36Sopenharmony_ci	pci_disable_device(pdev);
255462306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "driver is removed!\n");
255562306a36Sopenharmony_ci}
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci/**
255862306a36Sopenharmony_ci * mvumi_shutdown -	Shutdown entry point
255962306a36Sopenharmony_ci * @pdev:		PCI device structure
256062306a36Sopenharmony_ci */
256162306a36Sopenharmony_cistatic void mvumi_shutdown(struct pci_dev *pdev)
256262306a36Sopenharmony_ci{
256362306a36Sopenharmony_ci	struct mvumi_hba *mhba = pci_get_drvdata(pdev);
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	mvumi_flush_cache(mhba);
256662306a36Sopenharmony_ci}
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_cistatic int __maybe_unused mvumi_suspend(struct device *dev)
256962306a36Sopenharmony_ci{
257062306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
257162306a36Sopenharmony_ci	struct mvumi_hba *mhba = pci_get_drvdata(pdev);
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	mvumi_flush_cache(mhba);
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	mhba->instancet->disable_intr(mhba);
257662306a36Sopenharmony_ci	mvumi_unmap_pci_addr(pdev, mhba->base_addr);
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	return 0;
257962306a36Sopenharmony_ci}
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_cistatic int __maybe_unused mvumi_resume(struct device *dev)
258262306a36Sopenharmony_ci{
258362306a36Sopenharmony_ci	int ret;
258462306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
258562306a36Sopenharmony_ci	struct mvumi_hba *mhba = pci_get_drvdata(pdev);
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci	ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
258862306a36Sopenharmony_ci	if (ret)
258962306a36Sopenharmony_ci		goto fail;
259062306a36Sopenharmony_ci	ret = mvumi_map_pci_addr(mhba->pdev, mhba->base_addr);
259162306a36Sopenharmony_ci	if (ret)
259262306a36Sopenharmony_ci		goto release_regions;
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci	if (mvumi_cfg_hw_reg(mhba)) {
259562306a36Sopenharmony_ci		ret = -EINVAL;
259662306a36Sopenharmony_ci		goto unmap_pci_addr;
259762306a36Sopenharmony_ci	}
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	mhba->mmio = mhba->base_addr[0];
260062306a36Sopenharmony_ci	mvumi_reset(mhba);
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	if (mvumi_start(mhba)) {
260362306a36Sopenharmony_ci		ret = -EINVAL;
260462306a36Sopenharmony_ci		goto unmap_pci_addr;
260562306a36Sopenharmony_ci	}
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	mhba->instancet->enable_intr(mhba);
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	return 0;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ciunmap_pci_addr:
261262306a36Sopenharmony_ci	mvumi_unmap_pci_addr(pdev, mhba->base_addr);
261362306a36Sopenharmony_cirelease_regions:
261462306a36Sopenharmony_ci	pci_release_regions(pdev);
261562306a36Sopenharmony_cifail:
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	return ret;
261862306a36Sopenharmony_ci}
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mvumi_pm_ops, mvumi_suspend, mvumi_resume);
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_cistatic struct pci_driver mvumi_pci_driver = {
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	.name = MV_DRIVER_NAME,
262562306a36Sopenharmony_ci	.id_table = mvumi_pci_table,
262662306a36Sopenharmony_ci	.probe = mvumi_probe_one,
262762306a36Sopenharmony_ci	.remove = mvumi_detach_one,
262862306a36Sopenharmony_ci	.shutdown = mvumi_shutdown,
262962306a36Sopenharmony_ci	.driver.pm = &mvumi_pm_ops,
263062306a36Sopenharmony_ci};
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_cimodule_pci_driver(mvumi_pci_driver);
2633