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 *) ¶m->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