/drivers/scsi/pm8001/pm8001_sas.c
C | 1155 lines | 889 code | 81 blank | 185 comment | 147 complexity | 82cf4db51db89147250b1bddacdafc66 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
- /*
- * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
- *
- * Copyright (c) 2008-2009 USI Co., Ltd.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- *
- */
- #include <linux/slab.h>
- #include "pm8001_sas.h"
- /**
- * pm8001_find_tag - from sas task to find out tag that belongs to this task
- * @task: the task sent to the LLDD
- * @tag: the found tag associated with the task
- */
- static int pm8001_find_tag(struct sas_task *task, u32 *tag)
- {
- if (task->lldd_task) {
- struct pm8001_ccb_info *ccb;
- ccb = task->lldd_task;
- *tag = ccb->ccb_tag;
- return 1;
- }
- return 0;
- }
- /**
- * pm8001_tag_clear - clear the tags bitmap
- * @pm8001_ha: our hba struct
- * @tag: the found tag associated with the task
- */
- static void pm8001_tag_clear(struct pm8001_hba_info *pm8001_ha, u32 tag)
- {
- void *bitmap = pm8001_ha->tags;
- clear_bit(tag, bitmap);
- }
- static void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag)
- {
- pm8001_tag_clear(pm8001_ha, tag);
- }
- static void pm8001_tag_set(struct pm8001_hba_info *pm8001_ha, u32 tag)
- {
- void *bitmap = pm8001_ha->tags;
- set_bit(tag, bitmap);
- }
- /**
- * pm8001_tag_alloc - allocate a empty tag for task used.
- * @pm8001_ha: our hba struct
- * @tag_out: the found empty tag .
- */
- inline int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out)
- {
- unsigned int index, tag;
- void *bitmap = pm8001_ha->tags;
- index = find_first_zero_bit(bitmap, pm8001_ha->tags_num);
- tag = index;
- if (tag >= pm8001_ha->tags_num)
- return -SAS_QUEUE_FULL;
- pm8001_tag_set(pm8001_ha, tag);
- *tag_out = tag;
- return 0;
- }
- void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha)
- {
- int i;
- for (i = 0; i < pm8001_ha->tags_num; ++i)
- pm8001_tag_clear(pm8001_ha, i);
- }
- /**
- * pm8001_mem_alloc - allocate memory for pm8001.
- * @pdev: pci device.
- * @virt_addr: the allocated virtual address
- * @pphys_addr_hi: the physical address high byte address.
- * @pphys_addr_lo: the physical address low byte address.
- * @mem_size: memory size.
- */
- int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
- dma_addr_t *pphys_addr, u32 *pphys_addr_hi,
- u32 *pphys_addr_lo, u32 mem_size, u32 align)
- {
- caddr_t mem_virt_alloc;
- dma_addr_t mem_dma_handle;
- u64 phys_align;
- u64 align_offset = 0;
- if (align)
- align_offset = (dma_addr_t)align - 1;
- mem_virt_alloc =
- pci_alloc_consistent(pdev, mem_size + align, &mem_dma_handle);
- if (!mem_virt_alloc) {
- pm8001_printk("memory allocation error\n");
- return -1;
- }
- memset((void *)mem_virt_alloc, 0, mem_size+align);
- *pphys_addr = mem_dma_handle;
- phys_align = (*pphys_addr + align_offset) & ~align_offset;
- *virt_addr = (void *)mem_virt_alloc + phys_align - *pphys_addr;
- *pphys_addr_hi = upper_32_bits(phys_align);
- *pphys_addr_lo = lower_32_bits(phys_align);
- return 0;
- }
- /**
- * pm8001_find_ha_by_dev - from domain device which come from sas layer to
- * find out our hba struct.
- * @dev: the domain device which from sas layer.
- */
- static
- struct pm8001_hba_info *pm8001_find_ha_by_dev(struct domain_device *dev)
- {
- struct sas_ha_struct *sha = dev->port->ha;
- struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
- return pm8001_ha;
- }
- /**
- * pm8001_phy_control - this function should be registered to
- * sas_domain_function_template to provide libsas used, note: this is just
- * control the HBA phy rather than other expander phy if you want control
- * other phy, you should use SMP command.
- * @sas_phy: which phy in HBA phys.
- * @func: the operation.
- * @funcdata: always NULL.
- */
- int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
- void *funcdata)
- {
- int rc = 0, phy_id = sas_phy->id;
- struct pm8001_hba_info *pm8001_ha = NULL;
- struct sas_phy_linkrates *rates;
- DECLARE_COMPLETION_ONSTACK(completion);
- pm8001_ha = sas_phy->ha->lldd_ha;
- pm8001_ha->phy[phy_id].enable_completion = &completion;
- switch (func) {
- case PHY_FUNC_SET_LINK_RATE:
- rates = funcdata;
- if (rates->minimum_linkrate) {
- pm8001_ha->phy[phy_id].minimum_linkrate =
- rates->minimum_linkrate;
- }
- if (rates->maximum_linkrate) {
- pm8001_ha->phy[phy_id].maximum_linkrate =
- rates->maximum_linkrate;
- }
- if (pm8001_ha->phy[phy_id].phy_state == 0) {
- PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
- wait_for_completion(&completion);
- }
- PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
- PHY_LINK_RESET);
- break;
- case PHY_FUNC_HARD_RESET:
- if (pm8001_ha->phy[phy_id].phy_state == 0) {
- PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
- wait_for_completion(&completion);
- }
- PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
- PHY_HARD_RESET);
- break;
- case PHY_FUNC_LINK_RESET:
- if (pm8001_ha->phy[phy_id].phy_state == 0) {
- PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
- wait_for_completion(&completion);
- }
- PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
- PHY_LINK_RESET);
- break;
- case PHY_FUNC_RELEASE_SPINUP_HOLD:
- PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
- PHY_LINK_RESET);
- break;
- case PHY_FUNC_DISABLE:
- PM8001_CHIP_DISP->phy_stop_req(pm8001_ha, phy_id);
- break;
- default:
- rc = -EOPNOTSUPP;
- }
- msleep(300);
- return rc;
- }
- int pm8001_slave_alloc(struct scsi_device *scsi_dev)
- {
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
- if (dev_is_sata(dev)) {
- /* We don't need to rescan targets
- * if REPORT_LUNS request is failed
- */
- if (scsi_dev->lun > 0)
- return -ENXIO;
- scsi_dev->tagged_supported = 1;
- }
- return sas_slave_alloc(scsi_dev);
- }
- /**
- * pm8001_scan_start - we should enable all HBA phys by sending the phy_start
- * command to HBA.
- * @shost: the scsi host data.
- */
- void pm8001_scan_start(struct Scsi_Host *shost)
- {
- int i;
- struct pm8001_hba_info *pm8001_ha;
- struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
- pm8001_ha = sha->lldd_ha;
- PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha);
- for (i = 0; i < pm8001_ha->chip->n_phy; ++i)
- PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i);
- }