Yesterday, I send this patch once but seems that it was block by the list ,so I send it once again. Any reviews and comments will be appreciated. Jack Wang >From 0512262fa4b09dbf246d22e35cfd4585039b5336 Mon Sep 17 00:00:00 2001 From: Jack Wang <jack_wang@xxxxxxxxx> Date: Wed, 1 Jul 2009 19:21:09 +0800 Subject: [PATCH 5/5] Add pm8001 SAS/SATA HBA driver Signed-off-by: Jack Wang <jack_wang@xxxxxxxxx> Signed-off-by: Lindar Liu <lindar_liu@xxxxxxxxx> Signed-off-by: Tom Peng <tom_peng@xxxxxxxxx> --- drivers/scsi/pm8001/Kconfig | 48 + drivers/scsi/pm8001/Makefile | 47 + drivers/scsi/pm8001/pm8001_chips.h | 91 + drivers/scsi/pm8001/pm8001_ctl.c | 415 ++++ drivers/scsi/pm8001/pm8001_ctl.h | 93 + drivers/scsi/pm8001/pm8001_defs.h | 114 ++ drivers/scsi/pm8001/pm8001_hwi.c | 3896 ++++++++++++++++++++++++++++++++++++ drivers/scsi/pm8001/pm8001_hwi.h | 981 +++++++++ drivers/scsi/pm8001/pm8001_init.c | 737 +++++++ drivers/scsi/pm8001/pm8001_sas.c | 1090 ++++++++++ drivers/scsi/pm8001/pm8001_sas.h | 458 +++++ 11 files changed, 7970 insertions(+), 0 deletions(-) create mode 100644 drivers/scsi/pm8001/Kconfig create mode 100644 drivers/scsi/pm8001/Makefile create mode 100644 drivers/scsi/pm8001/pm8001_chips.h create mode 100644 drivers/scsi/pm8001/pm8001_ctl.c create mode 100644 drivers/scsi/pm8001/pm8001_ctl.h create mode 100644 drivers/scsi/pm8001/pm8001_defs.h create mode 100644 drivers/scsi/pm8001/pm8001_hwi.c create mode 100644 drivers/scsi/pm8001/pm8001_hwi.h create mode 100644 drivers/scsi/pm8001/pm8001_init.c create mode 100644 drivers/scsi/pm8001/pm8001_sas.c create mode 100644 drivers/scsi/pm8001/pm8001_sas.h diff --git a/drivers/scsi/pm8001/Kconfig b/drivers/scsi/pm8001/Kconfig new file mode 100644 index 0000000..4db6021 --- /dev/null +++ b/drivers/scsi/pm8001/Kconfig @@ -0,0 +1,48 @@ +# +# Kernel configuration file for the PM8001 SAS/SATA 8x6G based HBA driver +# +# Copyright (C) 2008-2009 USI Co., Ltd. +# Copyright (C) 2009 Jack Wang <jack_wang@xxxxxxxxx> + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# NO WARRANTY +# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT +# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is +# solely responsible for determining the appropriateness of using and +# distributing the Program and assumes all risks associated with its +# exercise of rights under this Agreement, including but not limited to +# the risks and costs of program errors, damage to or loss of data, +# programs or equipment, and unavailability or interruption of operations. + +# DISCLAIMER OF LIABILITY +# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + + config SCSI_PM8001 + tristate "PMC-Sierra SPC 8001 SAS/SATA Based Host Adapter driver" + depends on PCI && SCSI + select SCSI_SAS_LIBSAS + help + This driver supports PMC-Sierra PCIE SAS/SATA 8x6G SPC 8001 chip + based host adapters. \ No newline at end of file diff --git a/drivers/scsi/pm8001/Makefile b/drivers/scsi/pm8001/Makefile new file mode 100644 index 0000000..6e668ae --- /dev/null +++ b/drivers/scsi/pm8001/Makefile @@ -0,0 +1,47 @@ +# +# Kernel configuration file for the PM8001 SAS/SATA 8x6G based HBA driver +# +# Copyright (C) 2008-2009 USI Co., Ltd. +# Copyright (C) 2009 Jack Wang <jack_wang@xxxxxxxxx> + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# NO WARRANTY +# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT +# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is +# solely responsible for determining the appropriateness of using and +# distributing the Program and assumes all risks associated with its +# exercise of rights under this Agreement, including but not limited to +# the risks and costs of program errors, damage to or loss of data, +# programs or equipment, and unavailability or interruption of operations. + +# DISCLAIMER OF LIABILITY +# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + + +obj-$(CONFIG_SCSI_PM8001) += pm8001.o +pm8001-y += pm8001_init.o \ + pm8001_sas.o \ + pm8001_ctl.o \ + pm8001_hwi.o \ No newline at end of file diff --git a/drivers/scsi/pm8001/pm8001_chips.h b/drivers/scsi/pm8001/pm8001_chips.h new file mode 100644 index 0000000..fc72f4e --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_chips.h @@ -0,0 +1,91 @@ +/* + * 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. + * + */ + + +#ifndef _PM8001_CHIPS_H_ +#define _PM8001_CHIPS_H_ + +#define READ_LE_32(ADDR32, DMA_ADDR, OFFSET) \ + (*((u32 *)ADDR32)) = (*((u32 *)(((u8 *)DMA_ADDR) + (OFFSET)))) + +#define WRITE_LE_32(DMA_ADDR, OFFSET, VALUE32) \ + (*((u32 *)(((u8 *)DMA_ADDR)+(OFFSET)))) = (u32)(VALUE32) + +#define READ_LE_16(ADDR16, DMA_ADDR, OFFSET) \ + (*((u16 *)ADDR16)) = (*((u16 *)(((u8 *)DMA_ADDR) + (OFFSET)))) + +static inline u32 pm8001_cr32(struct pm8001_hba_info *pm8001_ha, u32 bar, + u32 addr) +{ + return readl(pm8001_ha->io_mem[bar].memvirtaddr + addr); +} + +static inline void pm8001_cw32(struct pm8001_hba_info *pm8001_ha, u32 bar, + u32 addr, u32 val) +{ + writel(val, pm8001_ha->io_mem[bar].memvirtaddr + addr); +} +static inline u32 pm8001_mr32(dma_addr_t addr, u32 offset) +{ + return readl((void *)addr + offset); +} +static inline void pm8001_mw32(dma_addr_t addr, u32 offset, u32 val) +{ + writel(val, (void *)addr + offset); +} +static inline u32 get_pci_bar_index(u32 pcibar) +{ + switch (pcibar) { + case 0x10: + case 0x14: + return 0; + case 0x18: + case 0x1C: + return 1; + case 0x20: + return 2; + case 0x24: + return 3; + default: + return 0; + } +} + +#endif /* _PM8001_CHIPS_H_ */ diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c new file mode 100644 index 0000000..c6ff7b0 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -0,0 +1,415 @@ +/* + * 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/miscdevice.h> +#include <linux/poll.h> +#include "pm8001_sas.h" +#include "pm8001_ctl.h" + +static struct fasync_struct *async_queue; + +/** + * pm8001_ctl_fasync - + * @fd - + * @filep - + * @mode - + * + * Called when application request fasyn callback handler. + */ +static int pm8001_ctl_fasync(int fd, struct file *filep, int mode) +{ + return fasync_helper(fd, filep, mode, &async_queue); +} + +/** + * pm8001_ctl_release - + * @inode - + * @filep - + * + * Called when application releases the fasyn callback handler. + */ +static int pm8001_ctl_release(struct inode *inode, struct file *filep) +{ + return fasync_helper(-1, filep, 0, &async_queue); +} + + +/** + * pm8001_ctl_verify_adapter - validates chip_number passed from application + * @ppm8001_ha: per adapter object + * @chip_number: chip id. + * + * Return (-1) means error, else chip_number. + */ +static int +pm8001_ctl_verify_adapter(int chip_number, struct pm8001_hba_info **ppm8001_ha) +{ + struct pm8001_hba_info *pm8001_ha; + printk(KERN_INFO "chip_number = %d \n", chip_number); + + list_for_each_entry(pm8001_ha, &hba_list, list) { + printk(KERN_INFO "pm8001_ha->id = %x \n", pm8001_ha->id); + if (pm8001_ha->id != chip_number) + continue; + *ppm8001_ha = pm8001_ha; + return chip_number; + } + *ppm8001_ha = NULL; + return -1; +} + +/** + * pm8001_ctl_ioctl_main - main ioctl entry point + * @file - (struct file) + * @cmd - ioctl opcode + * @arg - + */ +static long +pm8001_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg) +{ + long ret = 0; + u16 arg_length; + struct pm8001_ioctl_payload __user *payload = (void __user *) arg; + struct pm8001_ioctl_payload *ioctl_payload ; + DECLARE_COMPLETION_ONSTACK(completion); + struct pm8001_hba_info *pm8001_ha = NULL; + arg_length = sizeof(struct pm8001_ioctl_payload) + payload->length; + ioctl_payload = kmalloc(arg_length, GFP_KERNEL); + + if (copy_from_user(ioctl_payload, payload, arg_length)) { + pm8001_printk("copy data from user space @ %p failed\n", + payload); + return -EFAULT; + } + + if (pm8001_ctl_verify_adapter(ioctl_payload->id, &pm8001_ha) == -1 || + !pm8001_ha) { + pm8001_printk("find the chip fail \n"); + return -ENODEV; + } + + pm8001_ha->nvmd_completion = &completion; + switch (cmd) { + case IOCTL_GET_EVENT_LOG1: + PM8001_IOCTL_DBG(pm8001_ha, + pm8001_printk("cmd:IOCTL_GET_EVENT_LOG1 \n")); + ioctl_payload->status = IOCTL_STATUS_OK; + if ((4096 + 32) < ioctl_payload->offset) { + ioctl_payload->status = IOCTL_STATUS_NO_MORE_DATA; + ioctl_payload->length = 0; + ret = IOCTL_CALL_FAIL; + } + memcpy(ioctl_payload->func_specific, + pm8001_ha->memoryMap.region[AAP1].virt_ptr + + ioctl_payload->offset, ioctl_payload->length); + if (copy_to_user((void *)arg, (void *)ioctl_payload, + arg_length)) { + pm8001_printk("copy to user ERROR\n"); + ret = -EFAULT; + } + kfree(ioctl_payload); + return ret; + case IOCTL_GET_EVENT_LOG2: + PM8001_IOCTL_DBG(pm8001_ha, + pm8001_printk(" cmd: IOCTL_GET_EVENT_LOG2 \n")); + ioctl_payload->status = IOCTL_STATUS_OK; + if ((4096+32) < ioctl_payload->offset) { + ioctl_payload->status = IOCTL_STATUS_NO_MORE_DATA; + ioctl_payload->length = 0; + ret = IOCTL_CALL_FAIL; + } + memcpy(ioctl_payload->func_specific, + (u8 *)pm8001_ha->memoryMap.region[IOP].virt_ptr, + ioctl_payload->length); + if (copy_to_user((void *)arg, + (void *)ioctl_payload, arg_length)) { + pm8001_printk("copy to user ERROR\n"); + ret = -EFAULT; + } + kfree(ioctl_payload); + return ret; + case IOCTL_FW_CONTROL: + PM8001_IOCTL_DBG(pm8001_ha, + pm8001_printk(" cmd: IOCTL_FW_CONTROL \n")); + ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, + 0, ioctl_payload); + break; + case IOCTL_NVMD_GET: + PM8001_IOCTL_DBG(pm8001_ha, + pm8001_printk(" cmd: IOCTL_NVMD_GET \n")); + ret = PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, + 0, ioctl_payload); + break; + case IOCTL_NVMD_SET: + PM8001_IOCTL_DBG(pm8001_ha, + pm8001_printk(" cmd: IOCTL_NVMD_SET \n")); + ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, + 0, ioctl_payload); + break; + default: + return IOCTL_CALL_INVALID_CODE; + } + wait_for_completion(&completion); + if (copy_to_user((void *)arg, (void *)ioctl_payload, arg_length)) { + printk(KERN_INFO "copy to user ERROR\n"); + ret = -EFAULT; + } + kfree(ioctl_payload); + return ret; +} + +/** + * pm8001_ctl_ioctl - main ioctl entry point (unlocked) + * @file - (struct file) + * @cmd - ioctl opcode + * @arg - + */ +static long +pm8001_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret; + lock_kernel(); + ret = pm8001_ctl_ioctl_main(file, cmd, (void __user *)arg); + unlock_kernel(); + return ret; +} + +/* scsi host attributes */ + +/** + * pm8001_ctl_mpi_interface_rev_show - MPI interface revision number + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_mpi_interface_rev_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%d\n", + pm8001_ha->cfg_data.interface_rev); +} +static +DEVICE_ATTR(interface_rev, S_IRUGO, pm8001_ctl_mpi_interface_rev_show, NULL); + +/** + * pm8001_ctl_version_fw_show - firmware version + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_version_fw_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n", + (pm8001_ha->cfg_data.firmware_rev & 0xFF000000) >> 24, + (pm8001_ha->cfg_data.firmware_rev & 0x00FF0000) >> 16, + (pm8001_ha->cfg_data.firmware_rev & 0x0000FF00) >> 8, + pm8001_ha->cfg_data.firmware_rev & 0x000000FF); +} +static DEVICE_ATTR(version_fw, S_IRUGO, pm8001_ctl_version_fw_show, NULL); +/** + * pm8001_ctl_max_out_io_show - max outstanding io supported + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_max_out_io_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%d\n", + pm8001_ha->cfg_data.max_out_io); +} +static DEVICE_ATTR(max_out_io, S_IRUGO, pm8001_ctl_max_out_io_show, NULL); +/** + * pm8001_ctl_max_devices_show - max devices support + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_max_devices_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%04d\n", + (pm8001_ha->cfg_data.max_sgl & 0xFFFF0000)>>16); +} +static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL); +/** + * pm8001_ctl_max_sg_list_show - max sg list supported iff not 0.0 for no + * hardware limitation + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_max_sg_list_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%04d\n", + pm8001_ha->cfg_data.max_sgl & 0x0000FFFF); +} +static DEVICE_ATTR(max_sg_list, S_IRUGO, pm8001_ctl_max_sg_list_show, NULL); + +/** + * pm8001_ctl_sas_address_show - sas address + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is the controller sas address + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + return snprintf(buf, PAGE_SIZE, "0x%016llx\n", + (unsigned long long)*(u32 *)pm8001_ha->sas_addr); +} +static DEVICE_ATTR(host_sas_address, S_IRUGO, + pm8001_ctl_host_sas_address_show, NULL); + +/** + * pm8001_ctl_logging_level_show - logging level + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read/write' shost attribute. + */ +static ssize_t pm8001_ctl_logging_level_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return snprintf(buf, PAGE_SIZE, "%08xh\n", pm8001_ha->logging_level); +} +static ssize_t pm8001_ctl_logging_level_store(struct device *cdev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + int val = 0; + + if (sscanf(buf, "%x", &val) != 1) + return -EINVAL; + + pm8001_ha->logging_level = val; + PM8001_IOCTL_DBG(pm8001_ha, pm8001_printk("logging_level=%08xh\n", + pm8001_ha->logging_level)); + return strlen(buf); +} + +static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, + pm8001_ctl_logging_level_show, pm8001_ctl_logging_level_store); + +struct device_attribute *pm8001_host_attrs[] = { + &dev_attr_interface_rev, + &dev_attr_version_fw, + &dev_attr_max_out_io, + &dev_attr_max_devices, + &dev_attr_max_sg_list, + &dev_attr_logging_level, + &dev_attr_host_sas_address, + NULL, +}; + + +static const struct file_operations ctl_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = pm8001_ctl_ioctl, + .release = pm8001_ctl_release, + .fasync = pm8001_ctl_fasync, +}; + +static struct miscdevice ctl_dev = { + .minor = PM8001_MINOR, + .name = PM8001_DEV_NAME, + .fops = &ctl_fops, +}; + +/** + * pm8001_ctl_init - main entry point for ctl. + */ +void +pm8001_ctl_init(void) +{ + async_queue = NULL; + if (misc_register(&ctl_dev) < 0) + printk(KERN_ERR "%s can't register misc device [minor=%d]\n", + PM8001_DEV_NAME, PM8001_MINOR); + +} + +/** + * pm8001_ctl_exit - exit point for ctl + */ +void +pm8001_ctl_exit(void) +{ + misc_deregister(&ctl_dev); +} diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h new file mode 100644 index 0000000..5815016 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_ctl.h @@ -0,0 +1,93 @@ + /* + * 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. + * + */ + +#ifndef PM8001_CTL_H_INCLUDED +#define PM8001_CTL_H_INCLUDED + +#ifdef __KERNEL__ +#include <linux/miscdevice.h> +#endif + +#define PM8001_DEV_NAME "pm8001ctl" +#define PM8001_MAGIC_NUMBER 'S' +#define PM8001_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */ + +/** + * IOCTL opcodes + */ +#define IOCTL_FW_CONTROL _IOWR(PM8001_MAGIC_NUMBER, 81, int) +#define IOCTL_NVMD_GET _IOWR(PM8001_MAGIC_NUMBER, 82, int) +#define IOCTL_NVMD_SET _IOWR(PM8001_MAGIC_NUMBER, 83, int) +#define IOCTL_GET_EVENT_LOG1 _IOWR(PM8001_MAGIC_NUMBER, 84, int) +#define IOCTL_GET_EVENT_LOG2 _IOWR(PM8001_MAGIC_NUMBER, 85, int) +#define IOCTL_GET_CORE_DUMP _IOWR(PM8001_MAGIC_NUMBER, 86, int) +#define IOCTL_GET_VPD_INFO _IOWR(PM8001_MAGIC_NUMBER, 87, int) + +#define PM8001_MINOR 254 + +/* + * IOCTL Status Codes + */ +#define IOCTL_STATUS_OK 0x00 +#define IOCTL_STATUS_MORE_DATA 0x01 +#define IOCTL_STATUS_NO_MORE_DATA 0x02 +#define IOCTL_STATUS_INVALID_CODE 0x03 + +/* + * IOCTL Return Codes + */ +#define IOCTL_CALL_SUCCESS 0x00 +#define IOCTL_CALL_FAIL 0x01 +#define IOCTL_CALL_PENDING 0x02 +#define IOCTL_CALL_INVALID_CODE 0x03 + +struct pm8001_ioctl_payload { + u32 signature; + u16 major_function; + u16 minor_function; + u16 length; + u16 status; + u16 offset; + u16 id; + u8 func_specific[1]; +}; + +#endif /* PM8001_CTL_H_INCLUDED */ + diff --git a/drivers/scsi/pm8001/pm8001_defs.h b/drivers/scsi/pm8001/pm8001_defs.h new file mode 100644 index 0000000..e400718 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_defs.h @@ -0,0 +1,114 @@ +/* + * 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. + * + */ + +#ifndef _PM8001_DEFS_H_ +#define _PM8001_DEFS_H_ + +#define PCI_VENDOR_ID_PMC_Sierra 0x11f8 +#define PCI_DEVICE_ID_8001 0x8001 +enum chip_flavors { + chip_8001, +}; +#define USI_MAX_MEMCNT 7 +#define PM8001_MAX_DMA_SG 128 + +#define PHY_SPEED_60 0x04 +#define PHY_SPEED_30 0x02 +#define PHY_SPEED_15 0x01 + +#define DATA_DIR_BYRECIPIENT 4 /* UNSPECIFIED */ +#define DATA_DIR_OUT 2 /* OUTBOUND */ +#define DATA_DIR_IN 1 /* INBOUND */ +#define DATA_DIR_NONE 0 /* NO TRANSFER */ +#if (BITS_PER_LONG == 64) +#define LOW_32_BITS(addr) (u32)(addr & 0xffffffff) +#define HIGH_32_BITS(addr) (u32)((addr >> 32) & 0xffffffff) +#else +#define LOW_32_BITS(addr) (u32)addr +#define HIGH_32_BITS(addr) 0 +#endif +enum port_type { + PORT_TYPE_SAS = (1L << 1), + PORT_TYPE_SATA = (1L << 0), +}; + +/* driver compile-time configuration */ +enum driver_configuration { + PM8001_MAX_CCB = 512, /* max ccbs supported */ + PM8001_MAX_INB_NUM = 1, + PM8001_MAX_OUTB_NUM = 1, + PM8001_CAN_QUEUE = 512, /* SCSI Queue depth */ +}; + +/* unchangeable hardware details */ +enum hardware_details { + PM8001_MAX_PHYS = 8, /* max. possible phys */ + PM8001_MAX_PORTS = 8, /* max. possible ports */ + PM8001_MAX_DEVICES = 1024, /* max supported device */ +}; +#define AAP1 0x00 +#define IOP 0x01 +#define CI 0x02 +#define PI 0x03 +#define IB 0x04 +#define OB 0x05 +#define NVMD 0x06 + + /*error code*/ +#define MPI_IO_STATUS_SUCCESS 0 +#define MPI_IO_STATUS_BUSY 1 +#define MPI_IO_STATUS_FAIL -1 +/** + * Phy Control constants + */ +#define PHY_LINK_RESET 0x01 +#define PHY_HARD_RESET 0x02 +#define PHY_GET_ERROR_COUNTS 0x03 +#define PHY_CLEAR_ERROR_COUNTS 0x04 +#define PHY_NOTIFY_ENABLE_SPINUP 0x10 +#define PHY_BROADCAST_ASYNCH_EVENT 0x12 +#define PHY_COMINIT_OOB 0x20 + +enum pm8001_hba_info_flags { + PM8001F_INIT_TIME = (1U << 0), + PM8001F_RUN_TIME = (1U << 1), +}; + +#endif diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c new file mode 100644 index 0000000..0b21470 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -0,0 +1,3896 @@ +/* + * 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 "pm8001_sas.h" + #include "pm8001_hwi.h" + #include "pm8001_chips.h" + #include "pm8001_ctl.h" + +/** + * read_main_config_table - read the configure table and update it to hba + * structure. + * @pm8001_ha: our hba card information + */ +static void read_main_config_table(struct pm8001_hba_info *pm8001_ha) +{ + dma_addr_t address = pm8001_ha->cfg_addr; + pm8001_ha->cfg_data.signature = pm8001_mr32(address, 0x00); + pm8001_ha->cfg_data.interface_rev = pm8001_mr32(address, 0x04); + pm8001_ha->cfg_data.firmware_rev = pm8001_mr32(address, 0x08); + pm8001_ha->cfg_data.max_out_io = pm8001_mr32(address, 0x0C); + pm8001_ha->cfg_data.max_sgl = pm8001_mr32(address, 0x10); + pm8001_ha->cfg_data.ctrl_cap_flag = pm8001_mr32(address, 0x14); + pm8001_ha->cfg_data.gst_offset = pm8001_mr32(address, 0x18); + pm8001_ha->cfg_data.inbound_queue_offset = pm8001_mr32(address, 0x1C); + pm8001_ha->cfg_data.outbound_queue_offset = pm8001_mr32(address, 0x20); + pm8001_ha->cfg_data.hda_mode_flag = + pm8001_mr32(address, MAIN_HDA_FLAGS_OFFSET); + + /* read analog Setting offset from the configuration table */ + pm8001_ha->cfg_data.anolog_setup_table_offset = + pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET); + + /* read Error Dump Offset and Length */ + pm8001_ha->cfg_data.fatal_err_dump_offset0 = + pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_OFFSET); + pm8001_ha->cfg_data.fatal_err_dump_length0 = + pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_LENGTH); + pm8001_ha->cfg_data.fatal_err_dump_offset1 = + pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_OFFSET); + pm8001_ha->cfg_data.fatal_err_dump_length1 = + pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_LENGTH); +} + + /** + * read_general_status_table - read the general status table and update it to + * hba structure. + * @pm8001_ha: our hba card information + */ +static void read_general_status_table(struct pm8001_hba_info *pm8001_ha) +{ + dma_addr_t address = pm8001_ha->gst_addr; + pm8001_ha->gst_data.gst_len_mpistate = pm8001_mr32(address, 0x00); + pm8001_ha->gst_data.iq_freeze_state0 = pm8001_mr32(address, 0x04); + pm8001_ha->gst_data.iq_freeze_state1 = pm8001_mr32(address, 0x08); + pm8001_ha->gst_data.msgu_tcnt = pm8001_mr32(address, 0x0C); + pm8001_ha->gst_data.iop_tcnt = pm8001_mr32(address, 0x10); + pm8001_ha->gst_data.reserved = pm8001_mr32(address, 0x14); + pm8001_ha->gst_data.phy_state[0] = pm8001_mr32(address, 0x18); + pm8001_ha->gst_data.phy_state[1] = pm8001_mr32(address, 0x1C); + pm8001_ha->gst_data.phy_state[2] = pm8001_mr32(address, 0x20); + pm8001_ha->gst_data.phy_state[3] = pm8001_mr32(address, 0x24); + pm8001_ha->gst_data.phy_state[4] = pm8001_mr32(address, 0x28); + pm8001_ha->gst_data.phy_state[5] = pm8001_mr32(address, 0x2C); + pm8001_ha->gst_data.phy_state[6] = pm8001_mr32(address, 0x30); + pm8001_ha->gst_data.phy_state[7] = pm8001_mr32(address, 0x34); + pm8001_ha->gst_data.reserved1 = pm8001_mr32(address, 0x38); + pm8001_ha->gst_data.reserved2 = pm8001_mr32(address, 0x3C); + pm8001_ha->gst_data.reserved3 = pm8001_mr32(address, 0x40); + pm8001_ha->gst_data.recover_err_info[0] = pm8001_mr32(address, 0x44); + pm8001_ha->gst_data.recover_err_info[1] = pm8001_mr32(address, 0x48); + pm8001_ha->gst_data.recover_err_info[2] = pm8001_mr32(address, 0x4C); + pm8001_ha->gst_data.recover_err_info[3] = pm8001_mr32(address, 0x50); + pm8001_ha->gst_data.recover_err_info[4] = pm8001_mr32(address, 0x54); + pm8001_ha->gst_data.recover_err_info[5] = pm8001_mr32(address, 0x58); + pm8001_ha->gst_data.recover_err_info[6] = pm8001_mr32(address, 0x5C); + pm8001_ha->gst_data.recover_err_info[7] = pm8001_mr32(address, 0x60); +} + + /** + * read_inbound_queue_table - read the inbound queue table and update it to hba + * structure. + * @pm8001_ha: our hba card information + */ +static void read_inbound_queue_table(struct pm8001_hba_info *pm8001_ha) +{ + int inbQ_num = 1; + u32 offset; + int i; + dma_addr_t address = pm8001_ha->inb_addr; + for (i = 0; i < inbQ_num; i++) { + offset = i * 0x24; + pm8001_ha->inb_data[i].pi_pci_bar = + get_pci_bar_index(pm8001_mr32(address, (offset + 0x14))); + pm8001_ha->inb_data[i].pi_offset = + pm8001_mr32(address, (offset + 0x18)); + } +} + + /** + * read_outbound_queue_table - read the outbound queue table and update it to + * hba structure. + * @pm8001_ha: our hba card information + */ +static void read_outbound_queue_table(struct pm8001_hba_info *pm8001_ha) +{ + int outbQ_num = 1; + u32 offset; + int i; + dma_addr_t address = pm8001_ha->outb_addr; + for (i = 0; i < outbQ_num; i++) { + offset = i * 0x24; + pm8001_ha->outb_data[i].ci_pci_bar = + get_pci_bar_index(pm8001_mr32(address, (offset + 0x14))); + pm8001_ha->outb_data[i].ci_offset = + pm8001_mr32(address, (offset + 0x18)); + } +} + + /** + * init_default_table_values - init the default table. + * @pm8001_ha: our hba card information + */ +static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) +{ + int qn = 1; + int i; + u32 offsetib, offsetob; + dma_addr_t addressib = pm8001_ha->inb_addr; + dma_addr_t addressob = pm8001_ha->outb_addr; + + pm8001_ha->cfg_data.inbound_q_nppd_hppd = 0; + pm8001_ha->cfg_data.outbound_hw_event_pid0_3 = 0; + pm8001_ha->cfg_data.outbound_hw_event_pid4_7 = 0; + pm8001_ha->cfg_data.outbound_ncq_event_pid0_3 = 0; + pm8001_ha->cfg_data.outbound_ncq_event_pid4_7 = 0; + pm8001_ha->cfg_data.outbound_tgt_ITNexus_event_pid0_3 = 0; + pm8001_ha->cfg_data.outbound_tgt_ITNexus_event_pid4_7 = 0; + pm8001_ha->cfg_data.outbound_tgt_ssp_event_pid0_3 = 0; + pm8001_ha->cfg_data.outbound_tgt_ssp_event_pid4_7 = 0; + pm8001_ha->cfg_data.outbound_tgt_smp_event_pid0_3 = 0; + pm8001_ha->cfg_data.outbound_tgt_smp_event_pid4_7 = 0; + + pm8001_ha->cfg_data.upper_event_log_addr = + pm8001_ha->memoryMap.region[AAP1].phys_addr_hi; + pm8001_ha->cfg_data.lower_event_log_addr = + pm8001_ha->memoryMap.region[AAP1].phys_addr_lo; + pm8001_ha->cfg_data.event_log_size = 128*1024; + pm8001_ha->cfg_data.event_log_option = 0x03; + pm8001_ha->cfg_data.upper_iop_event_log_addr = + pm8001_ha->memoryMap.region[IOP].phys_addr_hi; + pm8001_ha->cfg_data.lower_iop_event_log_addr = + pm8001_ha->memoryMap.region[IOP].phys_addr_lo; + pm8001_ha->cfg_data.iop_event_log_size = 128*1024; + pm8001_ha->cfg_data.iop_event_log_option = 0x03; + pm8001_ha->cfg_data.fatal_err_interrupt = 0x01; + for (i = 0; i < qn; i++) { + pm8001_ha->inb_data[i].element_pri_size_cnt = + 0x00000100 | (0x00000040 << 16) | (0x00<<30); + pm8001_ha->inb_data[i].upper_base_addr = + pm8001_ha->memoryMap.region[IB].phys_addr_hi; + pm8001_ha->inb_data[i].lower_base_addr = + pm8001_ha->memoryMap.region[IB].phys_addr_lo; + pm8001_ha->inb_data[i].base_virt = + (u8 *)pm8001_ha->memoryMap.region[IB].virt_ptr; + pm8001_ha->inb_data[i].total_length = + pm8001_ha->memoryMap.region[IB].total_len; + pm8001_ha->inb_data[i].ci_upper_base_addr = + pm8001_ha->memoryMap.region[CI].phys_addr_hi; + pm8001_ha->inb_data[i].ci_lower_base_addr = + pm8001_ha->memoryMap.region[CI].phys_addr_lo; + pm8001_ha->inb_data[i].ci_virt = + (u8 *)pm8001_ha->memoryMap.region[CI].virt_ptr; + offsetib = i * 0x20; + pm8001_ha->inb_data[i].pi_pci_bar = + get_pci_bar_index(pm8001_mr32(addressib, + (offsetib + 0x14))); + pm8001_ha->inb_data[i].pi_offset = + pm8001_mr32(addressib, (offsetib + 0x18)); + pm8001_ha->inb_data[i].producer_idx = 0; + pm8001_ha->inb_data[i].consumer_index = 0; + } + for (i = 0; i < qn; i++) { + pm8001_ha->outb_data[i].element_size_cnt = + 256 | (64 << 16) | (1<<30); + pm8001_ha->outb_data[i].upper_base_addr = + pm8001_ha->memoryMap.region[OB].phys_addr_hi; + pm8001_ha->outb_data[i].lower_base_addr = + pm8001_ha->memoryMap.region[OB].phys_addr_lo; + pm8001_ha->outb_data[i].base_virt = + (u8 *)pm8001_ha->memoryMap.region[OB].virt_ptr; + pm8001_ha->outb_data[i].total_length = + pm8001_ha->memoryMap.region[OB].total_len; + pm8001_ha->outb_data[i].pi_upper_base_addr = + pm8001_ha->memoryMap.region[PI].phys_addr_hi; + pm8001_ha->outb_data[i].pi_lower_base_addr = + pm8001_ha->memoryMap.region[PI].phys_addr_lo; + pm8001_ha->outb_data[i].interrup_vec_cnt_delay = + 0 | (0 << 16) | (0 << 24); + pm8001_ha->outb_data[i].pi_virt = + (u8 *)pm8001_ha->memoryMap.region[PI].virt_ptr; + offsetob = i * 0x24; + pm8001_ha->outb_data[i].ci_pci_bar = + get_pci_bar_index(pm8001_mr32(addressob, + offsetob + 0x14)); + pm8001_ha->outb_data[i].ci_offset = + pm8001_mr32(addressob, (offsetob + 0x18)); + pm8001_ha->outb_data[i].consumer_idx = 0; + pm8001_ha->outb_data[i].producer_index = 0; + } +} + + /** + * update_main_config_table - update the main default table to the HBA. + * @pm8001_ha: our hba card information + */ +static void update_main_config_table(struct pm8001_hba_info *pm8001_ha) +{ + dma_addr_t address = pm8001_ha->cfg_addr; + pm8001_mw32(address, 0x24, pm8001_ha->cfg_data.inbound_q_nppd_hppd); + pm8001_mw32(address, 0x28, + pm8001_ha->cfg_data.outbound_hw_event_pid0_3); + pm8001_mw32(address, 0x2C, + pm8001_ha->cfg_data.outbound_hw_event_pid4_7); + pm8001_mw32(address, 0x30, + pm8001_ha->cfg_data.outbound_ncq_event_pid0_3); + pm8001_mw32(address, 0x34, + pm8001_ha->cfg_data.outbound_ncq_event_pid4_7); + pm8001_mw32(address, 0x38, + pm8001_ha->cfg_data.outbound_tgt_ITNexus_event_pid0_3); + pm8001_mw32(address, 0x3C, + pm8001_ha->cfg_data.outbound_tgt_ITNexus_event_pid4_7); + pm8001_mw32(address, 0x40, + pm8001_ha->cfg_data.outbound_tgt_ssp_event_pid0_3); + pm8001_mw32(address, 0x44, + pm8001_ha->cfg_data.outbound_tgt_ssp_event_pid4_7); + pm8001_mw32(address, 0x48, + pm8001_ha->cfg_data.outbound_tgt_smp_event_pid0_3); + pm8001_mw32(address, 0x4C, + pm8001_ha->cfg_data.outbound_tgt_smp_event_pid4_7); + pm8001_mw32(address, 0x50, + pm8001_ha->cfg_data.upper_event_log_addr); + pm8001_mw32(address, 0x54, pm8001_ha->cfg_data.lower_event_log_addr); + pm8001_mw32(address, 0x58, pm8001_ha->cfg_data.event_log_size); + pm8001_mw32(address, 0x5C, pm8001_ha->cfg_data.event_log_option); + pm8001_mw32(address, 0x60, + pm8001_ha->cfg_data.upper_iop_event_log_addr); + pm8001_mw32(address, 0x64, + pm8001_ha->cfg_data.lower_iop_event_log_addr); + pm8001_mw32(address, 0x68, pm8001_ha->cfg_data.iop_event_log_size); + pm8001_mw32(address, 0x6C, pm8001_ha->cfg_data.iop_event_log_option); + pm8001_mw32(address, 0x70, pm8001_ha->cfg_data.fatal_err_interrupt); +} + + /** + * update_inbound_queue_table - update the inbound queue table to the HBA. + * @pm8001_ha: our hba card information + */ +static void +update_inbound_queue_table(struct pm8001_hba_info *pm8001_ha, int number) +{ + dma_addr_t address = pm8001_ha->inb_addr; + u16 offset = number * 0x20; + pm8001_mw32(address, offset + 0x00, + pm8001_ha->inb_data[number].element_pri_size_cnt); + pm8001_mw32(address, offset + 0x04, + pm8001_ha->inb_data[number].upper_base_addr); + pm8001_mw32(address, offset + 0x08, + pm8001_ha->inb_data[number].lower_base_addr); + pm8001_mw32(address, offset + 0x0C, + pm8001_ha->inb_data[number].ci_upper_base_addr); + pm8001_mw32(address, offset + 0x10, + pm8001_ha->inb_data[number].ci_lower_base_addr); +} + + /** + * update_outbound_queue_table - update the outbound queue table to the HBA. + * @pm8001_ha: our hba card information + */ +static void +update_outbound_queue_table(struct pm8001_hba_info *pm8001_ha, int number) +{ + dma_addr_t address = pm8001_ha->outb_addr; + u16 offset = number * 0x24; + pm8001_mw32(address, offset + 0x00, + pm8001_ha->outb_data[number].element_size_cnt); + pm8001_mw32(address, offset + 0x04, + pm8001_ha->outb_data[number].upper_base_addr); + pm8001_mw32(address, offset + 0x08, + pm8001_ha->outb_data[number].lower_base_addr); + pm8001_mw32(address, offset + 0x0C, + pm8001_ha->outb_data[number].pi_upper_base_addr); + pm8001_mw32(address, offset + 0x10, + pm8001_ha->outb_data[number].pi_lower_base_addr); + pm8001_mw32(address, offset + 0x1C, + pm8001_ha->outb_data[number].interrup_vec_cnt_delay); +} + +/** + * mpi_init_check - notify firmware update finished and check initialization + * status. + * @pm8001_ha: our hba card information + */ +static int mpi_init_check(struct pm8001_hba_info *pm8001_ha) +{ + u32 max_wait_time, max_wait_count; + u32 value; + u32 gst_len_mpistate; + /* Write bit0=1 to Inbound DoorBell Register to tell the SPC FW the + table is updated */ + pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_UPDATE); + /* wait until Inbound DoorBell Clear Register toggled */ + max_wait_time = 1 * 1000 * 1000;/* 1 sec */ + max_wait_count = max_wait_time/10; + do { + udelay(10); + value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET); + value &= SPC_MSGU_CFG_TABLE_UPDATE; + } while ((value != 0) && (--max_wait_count)); + + if (!max_wait_count) + return -1; + /* check the MPI-State for initialization */ + gst_len_mpistate = + pm8001_mr32(pm8001_ha->gst_addr, GST_GSTLEN_MPIS_OFFSET); + if (GST_MPI_STATE_INIT != (gst_len_mpistate & GST_MPI_STATE_MASK)) + return -1; + /* check MPI Initialization error */ + gst_len_mpistate = gst_len_mpistate >> 16; + if (0x0000 != gst_len_mpistate) + return -1; + return 0; +} + +/** + * check_fw_ready - The LLDD check if the FW is ready, if not,wait as long + * as 1 seccond and return error. + * @pm8001_ha: our hba card information + */ +static int check_fw_ready(struct pm8001_hba_info *pm8001_ha) +{ + u32 value, value1; + u32 max_wait_time, max_wait_count; + /* check error state */ + value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); + /* check AAP error */ + if (SCRATCH_PAD1_ERR == (value & SCRATCH_PAD_STATE_MASK)) { + /* error state */ + value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0); + return -1; + } + + /* check IOP error */ + if (SCRATCH_PAD2_ERR == (value1 & SCRATCH_PAD_STATE_MASK)) { + /* error state */ + value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3); + return -1; + } + + /* bit 4-31 of scratch pad1 should be zeros if it is not + in error state*/ + if (value & SCRATCH_PAD1_STATE_MASK) { + /* error case */ + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0); + return -1; + } + + /* bit 2, 4-31 of scratch pad2 should be zeros if it is not + in error state */ + if (value1 & SCRATCH_PAD2_STATE_MASK) { + /* error case */ + return -1; + } + + max_wait_time = 1 * 1000 * 1000;/* 1 sec timeout */ + max_wait_count = max_wait_time/10; + + /* wait until scratch pad 1 and 2 registers in ready state */ + do { + mdelay(10); + value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) + & SCRATCH_PAD1_RDY; + value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) + & SCRATCH_PAD2_RDY; + if ((--max_wait_count) == 0) + break; + } while ((value != SCRATCH_PAD1_RDY) || (value1 != SCRATCH_PAD2_RDY)); + + if (!max_wait_count) + return -1; + return 0; +} + +/* Initialize pci space address eg: mpi offset */ +static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha) +{ + dma_addr_t cfg_addr; + u32 value; + u32 offset; + u32 pcibar; + u32 pcilogic; + + value = pm8001_cr32(pm8001_ha, 0, 0x44); + offset = value & 0x03FFFFFF; + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Scratchpad 0 Offset: %x \n", offset)); + pcilogic = (value & 0xFC000000) >> 26; + pcibar = get_pci_bar_index(pcilogic); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Scratchpad 0 PCI BAR: %d \n", pcibar)); + pm8001_ha->cfg_addr = cfg_addr = + (dma_addr_t)pm8001_ha->io_mem[pcibar].memvirtaddr + offset; + pm8001_ha->gst_addr = + cfg_addr + pm8001_cr32(pm8001_ha, pcibar, (u32)offset + 0x18); + pm8001_ha->inb_addr = + cfg_addr + pm8001_cr32(pm8001_ha, pcibar, (u32)offset + 0x1C); + pm8001_ha->outb_addr = + cfg_addr + pm8001_cr32(pm8001_ha, pcibar, (u32)offset + 0x20); +} + +/** + * pm8001_chip_init - the main init function that initialize whole PM8001 chip. + * @pm8001_ha: our hba card information + */ +static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha) +{ + /* check the firmware status */ + if (-1 == check_fw_ready(pm8001_ha)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Firmware is not ready!\n")); + return -EBUSY; + } + + /* Initialize pci space address eg: mpi offset */ + init_pci_device_addresses(pm8001_ha); + init_default_table_values(pm8001_ha); + read_main_config_table(pm8001_ha); + read_general_status_table(pm8001_ha); + read_inbound_queue_table(pm8001_ha); + read_outbound_queue_table(pm8001_ha); + /* update main config table ,inbound table and outbound table */ + update_main_config_table(pm8001_ha); + update_inbound_queue_table(pm8001_ha, 0); + update_outbound_queue_table(pm8001_ha, 0); + + /* notify firmware update finished and check initialization status */ + if (0 == mpi_init_check(pm8001_ha)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("MPI initialize successful!\n")); + } else + return -EBUSY; + /*This register is a 16-bit timer with a resolution of 1us. This is the + timer used for interrupt delay/coalescing in the PCIe Application Layer. + Zero is not a valid value.A value of 1 in the register will cause the + interrupts to be normal. A value greater than 1 will cause coalescing + delays.*/ + pm8001_cw32(pm8001_ha, 1, 0x0033c0, 0x1); + pm8001_cw32(pm8001_ha, 1, 0x0033c4, 0); + return 0; +} + +/** + *Function to do BAR shifting function is called to shift BAR base address + * + * @pm8001_ha handles for this instance of SAS/SATA hardware + * @shiftValue shifting value + * + * return success or fail + */ +static u32 bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue) +{ + u32 regVal; + u32 max_wait_time; + u32 max_wait_count; + + /* program the inbound AXI translation Lower Address */ + pm8001_cw32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW, shiftValue); + + /* confirm the setting is written */ + max_wait_time = 1 * 1000 * 1000; /* 1 sec */ + max_wait_count = max_wait_time; + do { + mdelay(1); + regVal = pm8001_cr32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW); + } while ((regVal != shiftValue) && (--max_wait_count)); + + if (!max_wait_count) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW" + " = 0x%x\n", regVal)); + return -1; + } + return 0; +} +static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha) +{ + u32 max_wait_time, max_wait_count; + u32 value; + u32 gst_len_mpistate; + /* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the + table is stop */ + pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_RESET); + + /* wait until Inbound DoorBell Clear Register toggled */ + max_wait_time = 1 * 1000 * 1000;/* 1 sec */ + max_wait_count = max_wait_time/10; + do { + udelay(20); + value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET); + value &= SPC_MSGU_CFG_TABLE_RESET; + } while ((value != 0) && (--max_wait_count)); + + if (!max_wait_count) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TIMEOUT:IBDB value/=0x%x\n", value)); + return -1; + } + + /* check the MPI-State for termination in progress */ + /* wait until Inbound DoorBell Clear Register toggled */ + max_wait_time = 1 * 1000 * 1000; /* 1 sec */ + max_wait_count = max_wait_time/10; + do { + udelay(20); + gst_len_mpistate = pm8001_mr32(pm8001_ha->gst_addr, + GST_GSTLEN_MPIS_OFFSET); + if (GST_MPI_STATE_UNINIT == + (gst_len_mpistate & GST_MPI_STATE_MASK)) + break; + } while (--max_wait_count); + if (!max_wait_count) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk(" TIME OUT MPI State = 0x%x\n", + gst_len_mpistate & GST_MPI_STATE_MASK)); + return -1; + } + return 0; +} + +/** +* soft_reset_ready_check - Function to check FW is ready for soft reset. +* @pm8001_ha: our hba card information +*/ +static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha) +{ + u32 regVal, regVal1, regVal2; + if (mpi_uninit_check(pm8001_ha) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("MPI state is not ready\n")); + return -1; + } + /* read the scratch pad 2 register bit 2 */ + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) + & SCRATCH_PAD2_FWRDY_RST; + if (regVal == SCRATCH_PAD2_FWRDY_RST) { + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Firmware is ready for reset .\n")); + } else { + /* Trigger NMI twice via RB6 */ + if (-1 == bar4_shift(pm8001_ha, RB6_ACCESS_REG)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + RB6_ACCESS_REG)); + return -1; + } + pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, + RB6_MAGIC_NUMBER_RST); + pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, RB6_MAGIC_NUMBER_RST); + /* wait for 100 ms */ + mdelay(100); + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) & + SCRATCH_PAD2_FWRDY_RST; + if (regVal != SCRATCH_PAD2_FWRDY_RST) { + regVal1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + regVal2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC TIMEOUT:MSGU_SCRATCH_PAD1" + "=0x%x,MSGU_SCRATCH_PAD2=0x%x\n", + regVal1, regVal2)); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC SCRATCH_PAD0 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0))); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC SCRATCH_PAD3 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3))); + return -1; + } + } + return 0; +} + +/** +* pm8001_chip_soft_rst - soft reset the PM8001 chip, so that the clear all +* the FW register status to the originated status. +* @pm8001_ha: our hba card information +* @signature: signature in host scratch pad0 register. +*/ +static int +pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature) +{ + u32 regVal, toggleVal; + u32 max_wait_time; + u32 max_wait_count; + u32 regVal1, regVal2, regVal3; + + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("SPC soft_reset : start\n")); + + /* step1: Check FW is ready for soft reset */ + if (soft_reset_ready_check(pm8001_ha) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC soft_reset : FW is not ready\n")); + return -1; + } + + /* step 2: clear NMI status register on AAP1 and IOP, write the same + value to clear */ + /* map 0x60000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + MBIC_AAP1_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("MBIC(A) - NMI Enable VPE0 (IOP): = 0x%x\n", + regVal)); + pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP, 0x0); + /* map 0x70000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + MBIC_IOP_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("MBIC(A) - NMI Enable VPE0 (AAP1): = 0x%x\n", + regVal)); + pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1, 0x0); + + regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCIE -Event Interrupt Enable Register:= 0x%x\n", + regVal)); + pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE, 0x0); + + regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCIE - Event Interrupt Register: = 0x%x\n", + regVal)); + pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT, regVal); + + regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCIE -Error Interrupt Enable Register:= 0x%x\n", + regVal)); + pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE, 0x0); + + regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCIE - Error Interrupt Register: = 0x%x\n", + regVal)); + pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT, regVal); + + /* read the scratch pad 1 register bit 2 */ + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) + & SCRATCH_PAD1_RST; + toggleVal = regVal ^ SCRATCH_PAD1_RST; + + /* set signature in host scratch pad0 register to tell SPC that the + host performs the soft reset */ + pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0, signature); + + /* read required registers for confirmming */ + /* map 0x0700000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + GSM_ADDR_BASE)); + return -1; + } + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x0(0x00007b88)-GSM Configuration and" + " Reset = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET))); + + /* step 3: host read GSM Configuration and Reset register */ + regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET); + /* Put those bits to low */ + /* GSM XCBI offset = 0x70 0000 + 0x00 Bit 13 COM_SLV_SW_RSTB 1 + 0x00 Bit 12 QSSP_SW_RSTB 1 + 0x00 Bit 11 RAAE_SW_RSTB 1 + 0x00 Bit 9 RB_1_SW_RSTB 1 + 0x00 Bit 8 SM_SW_RSTB 1 + */ + regVal &= ~(0x00003b00); + /* host write GSM Configuration and Reset register */ + pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x0 (0x00007b88 ==> 0x00004088) - GSM " + "Configuration and Reset is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET))); + + /* step 4: */ + /* disable GSM - Read Address Parity Check */ + regVal1 = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700038 - Read Address Parity Check " + "Enable = 0x%x\n", regVal1)); + pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, 0x0); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable" + "is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK))); + + /* disable GSM - Write Address Parity Check */ + regVal2 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700040 - Write Address Parity Check" + " Enable = 0x%x\n", regVal2)); + pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, 0x0); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700040 - Write Address Parity Check " + "Enable is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK))); + + /* disable GSM - Write Data Parity Check */ + regVal3 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x300048 - Write Data Parity Check" + " Enable = 0x%x\n", regVal3); + pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, 0x0)); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x300048 - Write Data Parity Check Enable" + "is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK))); + + /* step 5: delay 10 usec */ + udelay(10); + + /* Step 6: Reset the IOP and AAP1 */ + /* map 0x00000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + SPC_TOP_LEVEL_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Top Register before resetting IOP/AAP1" + ":= 0x%x\n", regVal)); + regVal &= ~(SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS | + SPC_REG_RESET_PCS_SPBC); + pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); + + /* step 7: Reset the BDMA/OSSP */ + regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Top Register before resetting BDMA/OSSP" + ": = 0x%x\n", regVal)); + regVal &= ~(SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP); + pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); + + /* step 8: delay 10 usec */ + udelay(10); + + /* step 9: bring the BDMA and OSSP out of reset */ + regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Top Register before bringing up BDMA/OSSP" + ":= 0x%x\n", regVal)); + regVal |= (SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP); + pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); + + /* step 10: delay 10 usec */ + udelay(10); + + /* step 11: reads and sets the GSM Configuration and Reset Register */ + /* map 0x0700000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + GSM_ADDR_BASE)); + return -1; + } + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x0 (0x00007b88)-GSM Configuration and " + "Reset = 0x%x\n", pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET))); + regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET); + /* Put those bits to high */ + /* GSM XCBI offset = 0x70 0000 + 0x00 Bit 13 COM_SLV_SW_RSTB 1 + 0x00 Bit 12 QSSP_SW_RSTB 1 + 0x00 Bit 11 RAAE_SW_RSTB 1 + 0x00 Bit 9 RB_1_SW_RSTB 1 + 0x00 Bit 8 SM_SW_RSTB 1 + */ + regVal |= (GSM_CONFIG_RESET_VALUE); + pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM (0x00004088 ==> 0x00007b88) - GSM" + " Configuration and Reset is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET))); + + /* step 12: Restore GSM - Read Address Parity Check */ + regVal = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK); + /* just for debugging */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable" + " = 0x%x\n", regVal)); + pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, regVal1); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700038 - Read Address Parity" + " Check Enable is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK))); + /* Restore GSM - Write Address Parity Check */ + regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK); + pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, regVal2); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700040 - Write Address Parity Check" + " Enable is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK))); + /* Restore GSM - Write Data Parity Check */ + regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK); + pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, regVal3); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("GSM 0x700048 - Write Data Parity Check Enable" + "is set to = 0x%x\n", + pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK))); + + /* step 13: bring the IOP and AAP1 out of reset */ + /* map 0x00000 to BAR4(0x20), BAR2(win) */ + if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC Shift Bar4 to 0x%x failed\n", + SPC_TOP_LEVEL_ADDR_BASE)); + return -1; + } + regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET); + regVal |= (SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS | + SPC_REG_RESET_PCS_SPBC); + pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal); + + /* step 14: delay 10 usec - Normal Mode */ + udelay(10); + /* check Soft Reset Normal mode or Soft Reset HDA mode */ + if (signature == SPC_SOFT_RESET_SIGNATURE) { + /* step 15 (Normal Mode): wait until scratch pad1 register + bit 2 toggled */ + max_wait_time = 2 * 1000 * 1000;/* 2 sec */ + max_wait_count = max_wait_time/10; + do { + udelay(10); + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) & + SCRATCH_PAD1_RST; + } while ((regVal != toggleVal) && (--max_wait_count)); + + if (!max_wait_count) { + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC TIMEOUT:ToggleVal 0x%x," + "MSGU_SCRATCH_PAD1 = 0x%x\n", + toggleVal, regVal)); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC SCRATCH_PAD0 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0))); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC SCRATCH_PAD2 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2))); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("SPC SCRATCH_PAD3 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3))); + return -1; + } + + /* step 16 (Normal) - Clear ODMR and ODCR */ + pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL); + pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL); + + /* step 17 (Normal Mode): wait for the FW and IOP to get + ready - 1 sec timeout */ + /* Wait for the SPC Configuration Table to be ready */ + if (check_fw_ready(pm8001_ha) == -1) { + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1); + /* return error if MPI Configuration Table not ready */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SPC FW not ready SCRATCH_PAD1" + " = 0x%x\n", regVal)); + regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2); + /* return error if MPI Configuration Table not ready */ + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SPC FW not ready SCRATCH_PAD2" + " = 0x%x\n", regVal)); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SPC SCRATCH_PAD0 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0))); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SPC SCRATCH_PAD3 value = 0x%x\n", + pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3))); + return -1; + } + } + + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("SPC soft reset Complete\n")); + return 0; +} + +/** +* pm8001_chip_iounmap - unmap the virtual address to HBA high register address +* which maped when initilized. +* @pm8001_ha: our hba card information +*/ +static void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha) +{ + s8 bar, logical = 0; + for (bar = 0; bar < 6; bar++) { + /* + ** logical BARs for SPC: + ** bar 0 and 1 - logical BAR0 + ** bar 2 and 3 - logical BAR1 + ** bar4 - logical BAR2 + ** bar5 - logical BAR3 + ** Skip the appropriate assignments: + */ + if ((bar == 1) || (bar == 3)) + continue; + if (pm8001_ha->io_mem[logical].memvirtaddr) { + iounmap(pm8001_ha->io_mem[logical].memvirtaddr); + logical++; + } + } +} + +/** +* pm8001_chip_interrupt_enable - enable PM8001 chip interrupt for outbound +* message. +* @pm8001_ha: our hba card information +*/ +static void +pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha) +{ + pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL); +} + + /** + * pm8001_chip_interrupt_disable- disable PM8001 chip interrupt for outbound + * message. + * @pm8001_ha: our hba card information + */ +static void +pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha) +{ + pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_MASK_ALL); +} + +/** +* mpi_msg_free_get- get the free message buffer for transfer inbound queue +* message. +* @circularQ: the inbound queue we want to transfer to HBA. +* @messageSize: the message size of this transfer, normally it is 64 bytes +* @messagePtr: the pointer to message. +*/ +static u32 mpi_msg_free_get(struct inbound_queue_table *circularQ, + u16 messageSize, void **messagePtr) +{ + u32 offset; + struct mpi_msg_hdr *msgHeader; + u8 bcCount = 1; /* only support single buffer */ + + /* Checks is the requested message size can be allocated in this queue*/ + if (messageSize > 64) { + *messagePtr = NULL; + return -1; + } + + /* Stores the new consumer index */ + READ_LE_32(&circularQ->consumer_index, circularQ->ci_virt, 0); + if (((circularQ->producer_idx + bcCount) % 256) == + circularQ->consumer_index) { + *messagePtr = NULL; + return -1; + } + /* get memory IOMB buffer address */ + offset = circularQ->producer_idx * 64; + /* increment to next bcCount element */ + circularQ->producer_idx = (circularQ->producer_idx + bcCount) % 256; + /* Adds that distance to the base of the region virtual address plus + the message header size*/ + msgHeader = (struct mpi_msg_hdr *)(((u8 *)(circularQ->base_virt)) + + offset); + *messagePtr = ((void *)msgHeader) + sizeof(struct mpi_msg_hdr); + return 0; +} + +/** + * mpi_build_cmd- build the message queue for transfer, update the PI to FW + * to tell the fw to get this message from IOMB. + * @circularQ: the inbound queue we want to transfer to HBA. + * @pm8001_ha: our hba card information + * @opCode: the operation code represent differnt command which LLDD and fw all + * recognized. + * @payload: the head is the general information while payload is different + * from each operation command. + */ +static u32 mpi_build_cmd(struct pm8001_hba_info *pm8001_ha, + struct inbound_queue_table *circularQ, u32 opCode, u32 *payload) +{ + u32 Header = 0, hpriority = 0, bc = 1, category = 0x02; + u32 responseQueue = 0; + void *pMessage; + u32 payoffset, cpayload; + + if (mpi_msg_free_get(circularQ, 64, &pMessage) < 0) { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("mpi_msg_free_get no free mpibuffer \n")); + return -1; + } + + /*Copy to the payload*/ + memcpy(pMessage, payload, (64 - sizeof(struct mpi_msg_hdr))); + + /*Build the header*/ + Header = ((1 << 31) | (hpriority << 30) | ((bc & 0x1f) << 24) + | ((responseQueue & 0x3F) << 16) + | ((category & 0xF) << 12) | (opCode & 0xFFF)); + + WRITE_LE_32((pMessage - 4), 0, Header); + /*This is for debug,print the payload information*/ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("payload:\n")); + for (payoffset = 0; payoffset < 60; payoffset += 4) { + READ_LE_32(&cpayload, pMessage, payoffset); + PM8001_IO_DBG(pm8001_ha, pm8001_printk(" %x ", cpayload)); + } + PM8001_IO_DBG(pm8001_ha, pm8001_printk(" \n ")); + /*Update the PI to the firmware*/ + pm8001_cw32(pm8001_ha, circularQ->pi_pci_bar, + circularQ->pi_offset, circularQ->producer_idx); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("after PI= %d CI= %d \n", circularQ->producer_idx, + circularQ->consumer_index)); + return 0; +} + +static u32 mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, + struct outbound_queue_table *circularQ, u8 bc) +{ + /* free the circular queue buffer elements associated with the message*/ + circularQ->consumer_idx = (circularQ->consumer_idx + bc) % 256; + /* update the CI of outbound queue */ + pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar, circularQ->ci_offset, + circularQ->consumer_idx); + /* Update the producer index from SPC*/ + READ_LE_32((u32 *)(&(circularQ->producer_index)), + circularQ->pi_virt, 0); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" CI=%d PI=%d\n", circularQ->consumer_idx, + circularQ->producer_index)); + return 0; +} + +/** + * mpi_msg_consume- get the MPI message from outbound queue message table. + * @circularQ: the outbound queue table. + * @messagePtr1: the message contents of this outbound message. + * @pBC: the message size. + */ +static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha, + struct outbound_queue_table *circularQ, void **messagePtr1, u8 *pBC) +{ + struct mpi_msg_hdr *msgHeader; + u32 msgHeader_tmp; + do { + /* If there are not-yet-delivered messages ... */ + if (circularQ->producer_index != circularQ->consumer_idx) { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("process an IOMB\n")); + /*Get the pointer to the circular queue buffer element*/ + msgHeader = (struct mpi_msg_hdr *) + ((u8 *)(circularQ->base_virt)+ + circularQ->consumer_idx * 64); + /* read header */ + READ_LE_32(&msgHeader_tmp, msgHeader, 0); + if (0 != (msgHeader_tmp & 0x80000000)) { + if (OPC_OUB_SKIP_ENTRY != + (msgHeader_tmp & 0xfff)) { + *messagePtr1 = + ((u8 *)msgHeader) + + sizeof(struct mpi_msg_hdr); + *pBC = (u8)((msgHeader_tmp >> 24) & + 0x1f); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("mpi_msg_consume" + ": CI=%d PI=%d msgHeader=%p\n", + circularQ->consumer_idx, + circularQ->producer_index, + (void *)msgHeader)); + return MPI_IO_STATUS_SUCCESS; + } else { + /* free the circular queue buffer + elements associated with the message*/ + circularQ->consumer_idx = + (circularQ->consumer_idx + + ((msgHeader_tmp >> 24) & 0x1f)) % 256; + /* update the CI of outbound queue */ + pm8001_cw32(pm8001_ha, + circularQ->ci_pci_bar, + circularQ->ci_offset, + circularQ->consumer_idx); + /* Update the producer index from SPC */ + READ_LE_32((u32 *) + (&(circularQ->producer_index)), + circularQ->pi_virt, 0); + } + } else + return MPI_IO_STATUS_FAIL; + } + } while (circularQ->producer_index != circularQ->consumer_idx); + /* while we don't have any more not-yet-delivered message */ + /* report empty */ + return MPI_IO_STATUS_BUSY; +} + +/** + * mpi_ssp_completion- this function deal with the event that FW response to + * the SSP request. When FW has completed a ssp request for example a IO + * request, after it has filled the SG data with the data, it will trigger this + * event represent that he has finished the job,please check the coresponding + * buffer. So we will tell the caller who maybe waiting the result + * to tell him that the task has been finished. + * @pm8001_ha: our hba card information + * @piomb: the message contents of this outbound message. + */ +static int +mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb) +{ + struct sas_task *t; + struct pm8001_ccb_info *ccb; + unsigned long flags; + u32 status; + u32 param; + struct ssp_completion_resp *psspPayload; + struct task_status_struct *ts; + struct ssp_response_iu *iu; + struct pm8001_device *pm8001_dev; + psspPayload = (struct ssp_completion_resp *)((u8*)piomb + 4); + status = psspPayload->status; + ccb = &pm8001_ha->ccb_info[psspPayload->tag]; + pm8001_dev = ccb->device; + param = psspPayload->param; + + PM8001_IO_DBG(pm8001_ha, pm8001_printk("OPC_OUB_SSP_COMP\n")); + t = ccb->task; + ts = &t->task_status; + if (t == NULL) + return -1; + if (status) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("sas IO status 0x%x\n", status)); + + switch (status) { + case IO_SUCCESS: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS" + ",param = %d \n", param)); + if (param == 0) { + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; + } else { + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_PROTO_RESPONSE; + ts->residual = param; + iu = &psspPayload->ssp_resp_iu; + sas_ssp_task_response(pm8001_ha->dev, t, iu); + } + if (pm8001_dev) + pm8001_dev->runing_req--; + break; + case IO_ABORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ABORTED IOMB Tag \n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + break; + case IO_UNDERFLOW: + /* SSP Completion with error */ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW" + ",param = %d \n", param)); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_UNDERRUN; + ts->residual = param; + if (pm8001_dev) + pm8001_dev->runing_req--; + break; + case IO_NO_DEVICE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_NO_DEVICE\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_PHY_DOWN; + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_INTERRUPTED; + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_EPROTO; + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_CONT0; + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_BAD_DEST; + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_" + "NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_CONN_RATE; + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_WRONG_DEST; + break; + case IO_XFER_ERROR_NAK_RECEIVED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_ACK_NAK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_DMA: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_DMA\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_TO; + break; + case IO_XFER_ERROR_OFFSET_MISMATCH: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_PORT_IN_RESET: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_PORT_IN_RESET\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_DS_NON_OPERATIONAL: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_NON_OPERATIONAL\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&t->task_state_lock, flags); + break; + case IO_DS_IN_RECOVERY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_IN_RECOVERY\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_TM_TAG_NOT_FOUND: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_TM_TAG_NOT_FOUND\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_SSP_EXT_IU_ZERO_LEN_ERROR: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_SSP_EXT_IU_ZERO_LEN_ERROR\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", status)); + /* not allowed case. Therefore, return failed status */ + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + } + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("scsi_satus = %x \n ", + psspPayload->ssp_resp_iu.status)); + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&t->task_state_lock, flags); + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with" + " io_status 0x%x resp 0x%x " + "stat 0x%x but aborted by upper layer!\n", + t, status, ts->resp, ts->stat)); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, psspPayload->tag); + } else { + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, psspPayload->tag); + mb();/* locked operation in order to force CPU ordering */ + t->task_done(t); + } + return 0; +} + +/*See the comments for mpi_ssp_completion */ +static int mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb) +{ + struct sas_task *t; + struct pm8001_ccb_info *ccb; + struct ssp_event_resp *psspPayload = + (struct ssp_event_resp *)((u8 *)piomb + 4); + u32 event = psspPayload->event; + u32 tag = psspPayload->tag ; + u32 port_id = psspPayload->port_id; + u32 dev_id = psspPayload->device_id; + + ccb = &pm8001_ha->ccb_info[psspPayload->tag]; + t = ccb->task; + if (t == NULL) + return -1; + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("port_id = %x,device_id = %x\n", + port_id, dev_id)); + switch (event) { + case IO_OVERFLOW: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n");) + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT" + "_SUPPORTED\n")); + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT" + "_SUPPORTED\n")); + break; + case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY\n")); + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + break; + case IO_XFER_ERROR_NAK_RECEIVED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n")); + break; + case IO_XFER_ERROR_ACK_NAK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n")); + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + break; + case IO_XFER_ERROR_UNEXPECTED_PHASE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n")); + break; + case IO_XFER_ERROR_XFER_RDY_OVERRUN: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n")); + break; + case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n")); + break; + case IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT\n")); + break; + case IO_XFER_ERROR_OFFSET_MISMATCH: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n")); + break; + case IO_XFER_ERROR_XFER_ZERO_DATA_LEN: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n")); + break; + case IO_XFER_CMD_FRAME_ISSUED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" IO_XFER_CMD_FRAME_ISSUED\n")); + break; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", event)); + /* not allowed case. Therefore, return failed status */ + break; + } + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + return 0; +} + +/*See the comments for mpi_ssp_completion */ +static int +mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct sas_task *t; + struct pm8001_ccb_info *ccb; + unsigned long flags; + u32 param; + u32 status; + struct sata_completion_resp *psataPayload; + struct task_status_struct *ts; + struct ata_task_resp *resp ; + u32 *sata_resp; + struct pm8001_device *pm8001_dev; + + psataPayload = (struct sata_completion_resp *)((u8 *)piomb + 4); + status = psataPayload->status; + + ccb = &pm8001_ha->ccb_info[psataPayload->tag]; + param = psataPayload->param; + t = ccb->task; + ts = &t->task_status; + pm8001_dev = ccb->device; + if (t == NULL) + return -1; + if (status) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("sata IO status 0x%x\n", status)); + switch (status) { + case IO_SUCCESS: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n")); + if (param == 0) { + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; + } else { + u8 len; + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_PROTO_RESPONSE; + ts->residual = param; + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("SAS_PROTO_RESPONSE len = %d\n", + param)); + sata_resp = &psataPayload->sata_resp[0]; + resp = (struct ata_task_resp *)ts->buf; + if (t->ata_task.dma_xfer == 0 && + t->data_dir == PCI_DMA_FROMDEVICE) { + len = sizeof(struct pio_setup_fis); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("PIO read len = %d\n", len)); + } else if (t->ata_task.use_ncq) { + len = sizeof(struct set_dev_bits_fis); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("FPDMA len = %d\n", len)); + } else { + len = sizeof(struct dev_to_host_fis); + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("other len = %d\n", len)); + } + if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) { + resp->frame_len = len; + memcpy(&resp->ending_fis[0], sata_resp, len); + ts->buf_valid_size = sizeof(*resp); + } else + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("response to large \n")); + } + if (pm8001_dev) + pm8001_dev->runing_req--; + break; + case IO_ABORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ABORTED IOMB Tag \n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + if (pm8001_dev) + pm8001_dev->runing_req--; + break; + /* following cases are to do cases */ + case IO_UNDERFLOW: + /* SATA Completion with error */ + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_UNDERFLOW param = %d\n", param)); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_UNDERRUN; + ts->residual = param; + if (pm8001_dev) + pm8001_dev->runing_req--; + break; + case IO_NO_DEVICE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_NO_DEVICE\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_PHY_DOWN; + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_INTERRUPTED; + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT" + "_SUPPORTED\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_EPROTO; + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_CONT0; + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&t->task_state_lock, flags); + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_BAD_DEST; + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_" + "NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_CONN_RATE; + break; + case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_STP_RESOURCES" + "_BUSY\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&t->task_state_lock, flags); + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_WRONG_DEST; + break; + case IO_XFER_ERROR_NAK_RECEIVED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_ACK_NAK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_NAK_R_ERR; + break; + case IO_XFER_ERROR_DMA: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_DMA\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + break; + case IO_XFER_ERROR_SATA_LINK_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_SATA_LINK_TIMEOUT\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_XFER_ERROR_REJECTED_NCQ_MODE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_TO; + break; + case IO_PORT_IN_RESET: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_PORT_IN_RESET\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_DS_NON_OPERATIONAL: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_NON_OPERATIONAL\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&t->task_state_lock, flags); + break; + case IO_DS_IN_RECOVERY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" IO_DS_IN_RECOVERY\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_DS_IN_ERROR: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_IN_ERROR\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&t->task_state_lock, flags); + break; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", status)); + /* not allowed case. Therefore, return failed status */ + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + } + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&t->task_state_lock, flags); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("task 0x%p done with io_status 0x%x" + " resp 0x%x stat 0x%x but aborted by upper layer!\n", + t, status, ts->resp, ts->stat)); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, psataPayload->tag); + } else { + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, psataPayload->tag); + mb();/* locked operation in order to force CPU ordering */ + t->task_done(t); + } + return 0; +} + +/*See the comments for mpi_ssp_completion */ +static int mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb) +{ + struct sas_task *t; + struct pm8001_ccb_info *ccb; + struct sata_event_resp *psataPayload = + (struct sata_event_resp *)((u8 *)piomb + 4); + u32 event = psataPayload->event; + u32 tag = psataPayload->tag; + u32 port_id = psataPayload->port_id; + u32 dev_id = psataPayload->device_id; + + ccb = &pm8001_ha->ccb_info[psataPayload->tag]; + t = ccb->task; + if (t == NULL) + return -1; + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("port_id = %x,device_id = %x\n", + port_id, dev_id)); + switch (event) { + case IO_OVERFLOW: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n")); + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n")); + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT" + "_SUPPORTED\n")); + break; + case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_STP_RESOURCES" + "_BUSY\n")); + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + break; + case IO_XFER_ERROR_NAK_RECEIVED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n")); + break; + case IO_XFER_ERROR_PEER_ABORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PEER_ABORTED\n")); + break; + case IO_XFER_ERROR_REJECTED_NCQ_MODE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n")); + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + break; + case IO_XFER_ERROR_UNEXPECTED_PHASE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n")); + break; + case IO_XFER_ERROR_XFER_RDY_OVERRUN: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n")); + break; + case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n")); + break; + case IO_XFER_ERROR_OFFSET_MISMATCH: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n")); + break; + case IO_XFER_ERROR_XFER_ZERO_DATA_LEN: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n")); + break; + case IO_XFER_CMD_FRAME_ISSUED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_CMD_FRAME_ISSUED\n")); + break; + case IO_XFER_PIO_SETUP_ERROR: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_PIO_SETUP_ERROR\n")); + break; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", event)); + /* not allowed case. Therefore, return failed status */ + break; + } + pm8001_ccb_task_free(pm8001_ha, t, ccb, tag); + return 0; +} + +/*See the comments for mpi_ssp_completion */ +static int +mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 param; + struct sas_task *t; + struct pm8001_ccb_info *ccb; + unsigned long flags; + u32 status ; + struct smp_completion_resp *psmpPayload; + struct task_status_struct *ts; + struct pm8001_device *pm8001_dev; + + psmpPayload = (struct smp_completion_resp *)((u8 *)piomb + 4); + status = psmpPayload->status; + + ccb = &pm8001_ha->ccb_info[psmpPayload->tag]; + param = psmpPayload->param; + t = ccb->task; + ts = &t->task_status; + pm8001_dev = ccb->device; + if (t == NULL) + return -1; + if (status) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("smp IO status 0x%x\n", status)); + + switch (status) { + case IO_SUCCESS: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; +if (pm8001_dev) + pm8001_dev->runing_req--; + break; + case IO_ABORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ABORTED IOMB\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_ABORTED_TASK; + if (pm8001_dev) + pm8001_dev->runing_req--; + break; + case IO_OVERFLOW: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_DATA_OVERRUN; + ts->residual = 0; + if (pm8001_dev) + pm8001_dev->runing_req--; + break; + case IO_NO_DEVICE: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_NO_DEVICE\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_PHY_DOWN; + break; + case IO_ERROR_HW_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ERROR_HW_TIMEOUT\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAM_BUSY; + break; + case IO_XFER_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_BREAK\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAM_BUSY; + break; + case IO_XFER_ERROR_PHY_NOT_READY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAM_BUSY; + break; + case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_ZONE_VIOLATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BREAK: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_CONT0; + break; + case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_UNKNOWN; + break; + case IO_OPEN_CNX_ERROR_BAD_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_BAD_DEST; + break; + case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_" + "NOT_SUPPORTED\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_CONN_RATE; + break; + case IO_OPEN_CNX_ERROR_WRONG_DESTINATION: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_WRONG_DEST; + break; + case IO_XFER_ERROR_RX_FRAME: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_RX_FRAME\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + break; + case IO_XFER_OPEN_RETRY_TIMEOUT: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_TO; + break; + case IO_ERROR_INTERNAL_SMP_RESOURCE: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_ERROR_INTERNAL_SMP_RESOURCE\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_QUEUE_FULL; + break; + case IO_PORT_IN_RESET: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_PORT_IN_RESET\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_DS_NON_OPERATIONAL: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_NON_OPERATIONAL\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&t->task_state_lock, flags); + break; + case IO_DS_IN_RECOVERY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_DS_IN_RECOVERY\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n")); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; + default: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("Unknown status 0x%x\n", status)); + ts->resp = SAS_TASK_UNDELIVERED; + ts->stat = SAS_DEV_NO_RESPONSE; + /* not allowed case. Therefore, return failed status */ + break; + } + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) { + spin_unlock_irqrestore(&t->task_state_lock, flags); + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with" + " io_status 0x%x resp 0x%x " + "stat 0x%x but aborted by upper layer!\n", + t, status, ts->resp, ts->stat)); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, psmpPayload->tag); + } else { + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, psmpPayload->tag); + mb();/* locked operation in order to force CPU ordering */ + t->task_done(t); + } + return 0; +} + +static void +mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct set_dev_state_resp *pPayload = + (struct set_dev_state_resp *)((u8 *)piomb + 4); + struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[pPayload->tag]; + struct pm8001_device *pm8001_dev = ccb->device; + u32 status = pPayload->status; + u32 device_id = pPayload->device_id; + u8 pds = pPayload->pds_nds | PDS_BITS; + u8 nds = pPayload->pds_nds | NDS_BITS; + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("Set device id = 0x%x state " + "from 0x%x to 0x%x status = 0x%x!\n", + device_id, pds, nds, status)); + complete(pm8001_dev->setds_completion); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, pPayload->tag); +} + +static void +mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct get_nvm_data_resp *pPayload = + (struct get_nvm_data_resp *)((u8 *)piomb + 4); + struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[pPayload->tag]; + u32 dlen_status = pPayload->dlen_status; + complete(pm8001_ha->nvmd_completion); + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set nvm data complete!\n")); + if ((dlen_status & NVMD_STAT) != 0) { + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Set nvm data error!\n")); + return; + } + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, pPayload->tag); +} + +static void +mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct fw_control_ex *fw_control_context; + struct get_nvm_data_resp *pPayload = + (struct get_nvm_data_resp *)((u8*)piomb + 4); + struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[pPayload->tag]; + u32 dlen_status = pPayload->dlen_status; + u32 ir_tds_bn_dps_das_nvm = pPayload->ir_tda_bn_dps_das_nvm ; + void *virt_addr; + fw_control_context = ccb->fw_control_context; + + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Get nvm data complete!\n")); + if ((dlen_status & NVMD_STAT) != 0) { + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Get nvm data error!\n")); + complete(pm8001_ha->nvmd_completion); + return; + } + + if (ir_tds_bn_dps_das_nvm & IPMode) { + /* indirect mode - IR bit set */ + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Get NVMD success, IR=1\n")); + if ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == TWI_DEVICE) { + if (ir_tds_bn_dps_das_nvm == 0x80a80200) { + virt_addr = + pm8001_ha->memoryMap.region[NVMD].virt_ptr; + memcpy(pm8001_ha->sas_addr, + ((u8 *)virt_addr + 4), + SAS_ADDR_SIZE); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Get SAS address successfully" + " from VPD!\n")); + } + } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == C_SEEPROM) + || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == VPD_FLASH) || + ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == EXPAN_ROM)) { + ; + } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == AAP1_RDUMP) + || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == IOP_RDUMP)) { + ; + } else { + /* Should not be happened*/ + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("(IR=1)Wrong Device type 0x%x\n", + ir_tds_bn_dps_das_nvm)); + } + } else /* direct mode */{ + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Get NVMD success, IR=0, dataLen=%d\n", + (dlen_status & NVMD_LEN) >> 24)); + } + memcpy((void *)(fw_control_context->usrAddr), + (void *)(pm8001_ha->memoryMap.region[NVMD].virt_ptr), + fw_control_context->len); + complete(pm8001_ha->nvmd_completion); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, pPayload->tag); +} + +static int mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct local_phy_ctl_resp *pPayload = + (struct local_phy_ctl_resp *)((u8 *)piomb + 4); + u32 status = pPayload->status; + u32 phy_id = pPayload->phyop_phyid & ID_BITS; + u32 phy_op = pPayload->phyop_phyid & OP_BITS; + if (status != 0) { + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("%x phy execute %x phy op failed! \n", + phy_id, phy_op)); + } else + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("%x phy execute %x phy op success! \n", + phy_id, phy_op)); + return 0; +} + +/** + * pm8001_bytes_dmaed - one of the interface function communication with the + * sas layer(libsas).when HBA driver received the identify done event or + * initial FIS received event(for SATA), it will invoke this function to notify + * the sas layer that the sas toplogy has formed, please discover the the whole + * sas domain, while receive a broadcast(change) primitive just tell the sas + * layer to discover the changed domain rather than the whole domain. + * @pm8001_ha: our hba card information + * @i: which phy that received the event. + */ +static void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i) +{ + struct pm8001_phy *phy = &pm8001_ha->phy[i]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + struct sas_ha_struct *sas_ha; + if (!phy->phy_attached) + return; + + sas_ha = pm8001_ha->sas; + if (sas_phy->phy) { + struct sas_phy *sphy = sas_phy->phy; + sphy->negotiated_linkrate = sas_phy->linkrate; + sphy->minimum_linkrate = phy->minimum_linkrate; + sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + sphy->maximum_linkrate = phy->maximum_linkrate; + sphy->maximum_linkrate_hw = phy->maximum_linkrate; + } + + if (phy->phy_type & PORT_TYPE_SAS) { + struct sas_identify_frame *id; + id = (struct sas_identify_frame *)phy->frame_rcvd; + id->dev_type = phy->identify.device_type; + id->initiator_bits = SAS_PROTOCOL_ALL; + id->target_bits = phy->identify.target_port_protocols; + } else if (phy->phy_type & PORT_TYPE_SATA) { + /*Nothing*/ + } + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("phy %d byte dmaded.\n", i)); + + sas_phy->frame_rcvd_size = phy->frame_rcvd_size; + pm8001_ha->sas->notify_port_event(sas_phy, PORTE_BYTES_DMAED); +} + +/* Get the link rate speed */ +static void get_lrate_mode(struct pm8001_phy *phy, u8 link_rate) +{ + struct sas_phy *sas_phy = phy->sas_phy.phy; + + switch (link_rate) { + case PHY_SPEED_60: + phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; + break; + case PHY_SPEED_30: + phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + case PHY_SPEED_15: + phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + } + sas_phy->negotiated_linkrate = phy->sas_phy.linkrate; + sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_6_0_GBPS; + sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + sas_phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS; + sas_phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; +} + +/** + * asd_get_attached_sas_addr -- extract/generate attached SAS address + * phy: pointer to asd_phy + * sas_addr: pointer to buffer where the SAS address is to be written + * + * This function extracts the SAS address from an IDENTIFY frame + * received. If OOB is SATA, then a SAS address is generated from the + * HA tables. + * + * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame + * buffer. + */ +static void pm8001_get_attached_sas_addr(struct pm8001_phy *phy, + u8 *sas_addr) +{ + if (phy->sas_phy.frame_rcvd[0] == 0x34 + && phy->sas_phy.oob_mode == SATA_OOB_MODE) { + struct pm8001_hba_info *pm8001_ha = phy->sas_phy.ha->lldd_ha; + /* FIS device-to-host */ + u64 addr = be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr); + addr += phy->sas_phy.id; + *(__be64 *)sas_addr = cpu_to_be64(addr); + } else { + struct sas_identify_frame *idframe = + (void *) phy->sas_phy.frame_rcvd; + memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE); + } +} + +/** + * pm8001_hw_event_ack_req- For PM8001,some events need to acknowage to FW. + * @pm8001_ha: our hba card information + * @Qnum: the outbound queue message number. + * @SEA: source of event to ack + * @ port_id: port id. + * @phyId: phy id. + * @param0: parameter 0. + * @param1: parameter 1. + */ +static void pm8001_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha, + u32 Qnum, u32 SEA, u32 port_id, u32 phyId, u32 param0, u32 param1) +{ + struct hw_event_ack_req payload; + u32 opc = OPC_INB_SAS_HW_EVENT_ACK; + + struct inbound_queue_table *circularQ; + + memset((u8 *)&payload, 0, sizeof(payload)); + circularQ = &pm8001_ha->inb_data[Qnum]; + payload.tag = 1; + payload.sea_phyid_portid = ((SEA & 0xFFFF) << 8) | + ((phyId & 0x0F) << 4) | (port_id & 0x0F); + payload.param0 = param0; + payload.param1 = param1; + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&payload); +} + +static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha, + u32 num, u32 phyId, u32 phy_op); + +/** + * hw_event_sas_phy_up -FW tells me a SAS phy up event. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static void +hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct hw_event_resp *pPayload = + (struct hw_event_resp *)((u8 *)piomb + 4); + u8 link_rate = + (u8)((pPayload->lr_evt_status_phyid_portid & 0xF0000000) >> 28); + u8 phy_id = + (u8)((pPayload->lr_evt_status_phyid_portid & 0x000000F0) >> 4); + struct sas_ha_struct *sas_ha = pm8001_ha->sas; + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + unsigned long flags; + u8 deviceType = pPayload->sas_identify.dev_type; + + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_SAS_PHY_UP \n")); + + switch (deviceType) { + case SAS_PHY_UNUSED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("device type no device.\n")); + break; + case SAS_END_DEVICE: + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("end device.\n")); + pm8001_chip_phy_ctl_req(pm8001_ha, 0, phy_id, + PHY_NOTIFY_ENABLE_SPINUP); + get_lrate_mode(phy, link_rate); + break; + case SAS_EDGE_EXPANDER_DEVICE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("expander device.\n")); + get_lrate_mode(phy, link_rate); + break; + case SAS_FANOUT_EXPANDER_DEVICE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("fanout expander device.\n")); + get_lrate_mode(phy, link_rate); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("unkown device type(%x)\n", deviceType)); + break; + } + phy->phy_type |= PORT_TYPE_SAS; + phy->identify.device_type = deviceType; + phy->phy_attached = 1; + if (phy->identify.device_type == SAS_END_DEV) + phy->identify.target_port_protocols = SAS_PROTOCOL_SSP; + else if (phy->identify.device_type != NO_DEVICE) + phy->identify.target_port_protocols = SAS_PROTOCOL_SMP; + phy->sas_phy.oob_mode = SAS_OOB_MODE; + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE); + spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags); + memcpy(phy->frame_rcvd, &pPayload->sas_identify, + sizeof(struct sas_identify_frame)-4); + phy->frame_rcvd_size = sizeof(struct sas_identify_frame) - 4; + pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr); + spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags); + if (pm8001_ha->flags == PM8001F_RUN_TIME) + mdelay(200);/*delay a moment to wait disk to spinup*/ + pm8001_bytes_dmaed(pm8001_ha, phy_id); +} + +/** + * hw_event_sata_phy_up -FW tells me a SATA phy up event. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static void +hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct hw_event_resp *pPayload = + (struct hw_event_resp *)((u8 *)piomb + 4); + u8 link_rate = + (u8)((pPayload->lr_evt_status_phyid_portid & 0xF0000000) >> 28); + u8 phy_id = + (u8)((pPayload->lr_evt_status_phyid_portid & 0x000000F0) >> 4); + struct sas_ha_struct *sas_ha = pm8001_ha->sas; + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + unsigned long flags; + get_lrate_mode(phy, link_rate); + phy->phy_type |= PORT_TYPE_SATA; + phy->phy_attached = 1; + phy->sas_phy.oob_mode = SATA_OOB_MODE; + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE); + spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags); + memcpy(phy->frame_rcvd, ((u8 *)&pPayload->sata_fis), + sizeof(struct dev_to_host_fis)); + phy->frame_rcvd_size = sizeof(struct dev_to_host_fis); + phy->identify.target_port_protocols = SAS_PROTOCOL_SATA; + phy->identify.device_type = SATA_DEV; + pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr); + spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags); + pm8001_bytes_dmaed(pm8001_ha, phy_id); +} + +/** + * hw_event_phy_down -The hw event is phy down, we should notify the libsas that + * the phy loss of signal before call this function. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static void +hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct hw_event_resp *pPayload = + (struct hw_event_resp *)((u8 *)piomb + 4); + u8 port_id = (u8)(pPayload->lr_evt_status_phyid_portid & 0x0000000F); + u8 phy_id = + (u8)((pPayload->lr_evt_status_phyid_portid & 0x000000F0) >> 4); + u8 portstate = (u8)(pPayload->npip_portstate & 0x0000000F); + + switch (portstate) { + case PORT_VALID: + break; + case PORT_INVALID: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" PortInvalid portID %d \n", port_id)); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" Last phy Down and port invalid\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN, + port_id, phy_id, 0, 0); + break; + case PORT_IN_RESET: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" PortInReset portID %d \n", port_id)); + break; + case PORT_NOT_ESTABLISHED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" phy Down and PORT_NOT_ESTABLISHED\n")); + break; + case PORT_LOSTCOMM: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" phy Down and PORT_LOSTCOMM\n")); + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" Last phy Down and port invalid\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN, + port_id, phy_id, 0, 0); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" phy Down and(default) = %x\n", + portstate)); + break; + + } +} + +/** + * mpi_reg_resp -The hw event is register device ID response, when sas layer find + * a device it will notify LLDD, then the driver register the domain device to + * FW, this event is the return device ID which the FW has assigned, from now, + * inter-communication with FW is no longer using the SAS address, use device ID + * which FW assigned. + * the phy loss of signal before call this function. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 status; + u32 device_id; + u32 htag; + struct pm8001_ccb_info *ccb; + struct pm8001_device *pm8001_dev; + struct dev_reg_resp *registerRespPayload = + (struct dev_reg_resp *)((u8*)piomb + 4); + + htag = registerRespPayload->tag; + ccb = &pm8001_ha->ccb_info[registerRespPayload->tag]; + pm8001_dev = ccb->device; + status = registerRespPayload->status; + device_id = registerRespPayload->device_id; + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" register device is status = %d\n", status)); + switch (status) { + case DEVREG_SUCCESS: + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("DEVREG_SUCCESS\n")); + pm8001_dev->device_id = device_id; + break; + case DEVREG_FAILURE_OUT_OF_RESOURCE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_OUT_OF_RESOURCE\n")); + break; + case DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED\n")); + break; + case DEVREG_FAILURE_INVALID_PHY_ID: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_INVALID_PHY_ID\n")); + break; + case DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED\n")); + break; + case DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE\n")); + break; + case DEVREG_FAILURE_PORT_NOT_VALID_STATE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_PORT_NOT_VALID_STATE\n")); + break; + case DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID\n")); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_UNSORPORTED\n")); + break; + } + complete(pm8001_dev->dcompletion); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, htag); + return 0; +} + +static int mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 status; + u32 device_id; + struct dev_reg_resp *registerRespPayload = + (struct dev_reg_resp *)((u8 *)piomb + 4); + + status = registerRespPayload->status; + device_id = registerRespPayload->device_id; + if (status != 0) + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" deregister device failed ,status = %x" + ",device_id = %x\n", status, device_id)); + return 0; +} + +static int +mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 status; + struct fw_control_ex fw_control_context; + struct fw_flash_Update_resp *ppayload = + (struct fw_flash_Update_resp *)((u8 *)piomb + 4); + u32 tag = ppayload->tag; + struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag]; + status = ppayload->status; + memcpy(&fw_control_context, + ccb->fw_control_context, + sizeof(fw_control_context)); + switch (status) { + case FLASH_UPDATE_COMPLETE_PENDING_REBOOT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_COMPLETE_PENDING_REBOOT\n")); + break; + case FLASH_UPDATE_IN_PROGRESS: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_IN_PROGRESS\n")); + break; + case FLASH_UPDATE_HDR_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_HDR_ERR\n")); + break; + case FLASH_UPDATE_OFFSET_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_OFFSET_ERR\n")); + break; + case FLASH_UPDATE_CRC_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_CRC_ERR\n")); + break; + case FLASH_UPDATE_LENGTH_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_LENGTH_ERR\n")); + break; + case FLASH_UPDATE_HW_ERR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_HW_ERR\n")); + break; + case FLASH_UPDATE_DNLD_NOT_SUPPORTED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_DNLD_NOT_SUPPORTED\n")); + break; + case FLASH_UPDATE_DISABLED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(": FLASH_UPDATE_DISABLED\n")); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("No matched status = %d\n", status)); + break; + } + ccb->fw_control_context->fw_control->retcode = status; + pci_free_consistent(pm8001_ha->pdev, + fw_control_context.len, + fw_control_context.virtAddr, + fw_control_context.phys_addr); + complete(pm8001_ha->nvmd_completion); + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, tag); + return 0; +} + +static int +mpi_general_event(struct pm8001_hba_info *pm8001_ha , void *piomb) +{ + u32 status; + int i; + struct general_event_resp *pPayload = + (struct general_event_resp *)((u8 *)piomb + 4); + status = pPayload->status; + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk(" status = 0x%x\n", status)); + for (i = 0; i < GENERAL_EVENT_PAYLOAD; i++) + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("inb_IOMB_payload[0x%x] 0x%x, \n", i, + pPayload->inb_IOMB_payload[i])); + return 0; +} + +static int +mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + struct sas_task *t; + struct pm8001_ccb_info *ccb; + unsigned long flags; + u32 status ; + u32 tag, scp; + struct task_status_struct *ts; + + struct task_abort_resp *pPayload = + (struct task_abort_resp *)((u8 *)piomb + 4); + ccb = &pm8001_ha->ccb_info[pPayload->tag]; + t = ccb->task; + ts = &t->task_status; + + if (t == NULL) + return -1; + + status = pPayload->status; + tag = pPayload->tag; + scp = pPayload->scp; + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" status = 0x%x\n", status)); + if (status != 0) + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("task abort failed tag = 0x%x," + " scp= 0x%x\n", tag, scp)); + switch (status) { + case IO_SUCCESS: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAM_GOOD; + break; + case IO_NOT_VALID: + PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_NOT_VALID\n")); + ts->resp = TMF_RESP_FUNC_FAILED; + break; + } + spin_lock_irqsave(&t->task_state_lock, flags); + t->task_state_flags &= ~SAS_TASK_STATE_PENDING; + t->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + t->task_state_flags |= SAS_TASK_STATE_DONE; + spin_unlock_irqrestore(&t->task_state_lock, flags); + pm8001_ccb_task_free(pm8001_ha, t, ccb, pPayload->tag); + mb(); + t->task_done(t); + return 0; +} +/** + * mpi_hw_event -The hw event has come. + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb) +{ + unsigned long flags; + struct hw_event_resp *pPayload = + (struct hw_event_resp *)((u8 *)piomb + 4); + u8 port_id = (u8)(pPayload->lr_evt_status_phyid_portid & 0x0000000F); + u8 phy_id = + (u8)((pPayload->lr_evt_status_phyid_portid & 0x000000F0) >> 4); + u16 eventType = + (u16)((pPayload->lr_evt_status_phyid_portid & 0x00FFFF00) >> 8); + u8 status = + (u8)((pPayload->lr_evt_status_phyid_portid & 0x0F000000) >> 24); + struct sas_ha_struct *sas_ha = pm8001_ha->sas; + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; + + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("outbound queue HW event & event type : ")); + switch (eventType) { + case HW_EVENT_PHY_START_STATUS: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_START_STATUS" + " status = %x\n", status)); + if (status == 0) { + phy->phy_state = 1; + if (pm8001_ha->flags == PM8001F_RUN_TIME) + complete(phy->enable_completion); + } + break; + case HW_EVENT_SAS_PHY_UP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_START_STATUS \n")); + hw_event_sas_phy_up(pm8001_ha, piomb); + break; + case HW_EVENT_SATA_PHY_UP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_SATA_PHY_UP \n")); + hw_event_sata_phy_up(pm8001_ha, piomb); + break; + case HW_EVENT_PHY_STOP_STATUS: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_STOP_STATUS " + "status = %x\n", status)); + if (status == 0) + phy->phy_state = 0; + break; + case HW_EVENT_SATA_SPINUP_HOLD: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_SATA_SPINUP_HOLD \n")); + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD); + break; + case HW_EVENT_PHY_DOWN: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_DOWN \n")); + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL); + phy->phy_attached = 0; + phy->phy_state = 0; + hw_event_phy_down(pm8001_ha, piomb); + break; + case HW_EVENT_PORT_INVALID: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_INVALID\n")); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + /* the broadcast change primitive received, tell the LIBSAS this event + to revalidate the sas TOP*/ + case HW_EVENT_BROADCAST_CHANGE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_BROADCAST_CHANGE\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_BROADCAST_CHANGE, + port_id, phy_id, 1, 0); + spin_lock_irqsave(&sas_phy->sas_prim_lock, flags); + sas_phy->sas_prim = HW_EVENT_BROADCAST_CHANGE; + spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags); + sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); + break; + case HW_EVENT_PHY_ERROR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PHY_ERROR\n")); + sas_phy_disconnected(&phy->sas_phy); + phy->phy_attached = 0; + sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR); + break; + case HW_EVENT_BROADCAST_EXP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_BROADCAST_EXP\n")); + spin_lock_irqsave(&sas_phy->sas_prim_lock, flags); + sas_phy->sas_prim = HW_EVENT_BROADCAST_EXP; + spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags); + sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); + break; + case HW_EVENT_LINK_ERR_INVALID_DWORD: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_LINK_ERR_DISPARITY_ERROR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_DISPARITY_ERROR\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_DISPARITY_ERROR, + port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_LINK_ERR_CODE_VIOLATION: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_CODE_VIOLATION\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_CODE_VIOLATION, + port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH, + port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_MALFUNCTION: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_MALFUNCTION\n")); + break; + case HW_EVENT_BROADCAST_SES: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_BROADCAST_SES\n")); + spin_lock_irqsave(&sas_phy->sas_prim_lock, flags); + sas_phy->sas_prim = HW_EVENT_BROADCAST_SES; + spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags); + sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); + break; + case HW_EVENT_INBOUND_CRC_ERROR: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_INBOUND_CRC_ERROR\n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_INBOUND_CRC_ERROR, + port_id, phy_id, 0, 0); + break; + case HW_EVENT_HARD_RESET_RECEIVED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_HARD_RESET_RECEIVED\n")); + sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET); + break; + case HW_EVENT_ID_FRAME_TIMEOUT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_ID_FRAME_TIMEOUT\n")); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_LINK_ERR_PHY_RESET_FAILED: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_LINK_ERR_PHY_RESET_FAILED \n")); + pm8001_hw_event_ack_req(pm8001_ha, 0, + HW_EVENT_LINK_ERR_PHY_RESET_FAILED, + port_id, phy_id, 0, 0); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_PORT_RESET_TIMER_TMO: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_RESET_TIMER_TMO \n")); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_PORT_RECOVERY_TIMER_TMO: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_RECOVERY_TIMER_TMO \n")); + sas_phy_disconnected(sas_phy); + phy->phy_attached = 0; + sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + break; + case HW_EVENT_PORT_RECOVER: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_RECOVER \n")); + break; + case HW_EVENT_PORT_RESET_COMPLETE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("HW_EVENT_PORT_RESET_COMPLETE \n")); + break; + case EVENT_BROADCAST_ASYNCH_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("EVENT_BROADCAST_ASYNCH_EVENT\n")); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Unknown event type = %x\n", eventType)); + break; + } + return 0; +} + +/** + * process_one_iomb - process one outbound Queue memory block + * + * @pm8001_ha: our hba card information + * @piomb: IO message buffer + */ +static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb) +{ + u32 pHeader = (u32)*(u32 *)piomb; + u8 opc = (u8)(pHeader & 0xFFF); + + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("process_one_iomb:\n")); + + switch (opc) { + case OPC_OUB_ECHO: + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("OPC_OUB_ECHO \n")); + break; + case OPC_OUB_HW_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_HW_EVENT \n")); + mpi_hw_event(pm8001_ha, piomb); + break; + case OPC_OUB_SSP_COMP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SSP_COMP \n")); + mpi_ssp_completion(pm8001_ha, piomb); + break; + case OPC_OUB_SMP_COMP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SMP_COMP \n")); + mpi_smp_completion(pm8001_ha, piomb); + break; + case OPC_OUB_LOCAL_PHY_CNTRL: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_LOCAL_PHY_CNTRL\n")); + mpi_local_phy_ctl(pm8001_ha, piomb); + break; + case OPC_OUB_DEV_REGIST: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_DEV_REGIST \n")); + mpi_reg_resp(pm8001_ha, piomb); + break; + case OPC_OUB_DEREG_DEV: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("unresgister the deviece \n")); + mpi_dereg_resp(pm8001_ha, piomb); + break; + case OPC_OUB_GET_DEV_HANDLE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GET_DEV_HANDLE \n")); + break; + case OPC_OUB_SATA_COMP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SATA_COMP \n")); + mpi_sata_completion(pm8001_ha, piomb); + break; + case OPC_OUB_SATA_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SATA_EVENT \n")); + mpi_sata_event(pm8001_ha, piomb); + break; + case OPC_OUB_SSP_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SSP_EVENT\n")); + mpi_ssp_event(pm8001_ha, piomb); + break; + case OPC_OUB_DEV_HANDLE_ARRIV: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_DEV_HANDLE_ARRIV\n")); + /*This is for target*/ + break; + case OPC_OUB_SSP_RECV_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SSP_RECV_EVENT\n")); + /*This is for target*/ + break; + case OPC_OUB_DEV_INFO: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_DEV_INFO\n")); + break; + case OPC_OUB_FW_FLASH_UPDATE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_FW_FLASH_UPDATE\n")); + mpi_fw_flash_update_resp(pm8001_ha, piomb); + break; + case OPC_OUB_GPIO_RESPONSE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GPIO_RESPONSE\n")); + break; + case OPC_OUB_GPIO_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GPIO_EVENT\n")); + break; + case OPC_OUB_GENERAL_EVENT: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GENERAL_EVENT\n")); + mpi_general_event(pm8001_ha, piomb); + break; + case OPC_OUB_SSP_ABORT_RSP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SSP_ABORT_RSP\n")); + mpi_task_abort_resp(pm8001_ha, piomb); + break; + case OPC_OUB_SATA_ABORT_RSP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SATA_ABORT_RSP\n")); + mpi_task_abort_resp(pm8001_ha, piomb); + break; + case OPC_OUB_SAS_DIAG_MODE_START_END: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SAS_DIAG_MODE_START_END\n")); + break; + case OPC_OUB_SAS_DIAG_EXECUTE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SAS_DIAG_EXECUTE\n")); + break; + case OPC_OUB_GET_TIME_STAMP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GET_TIME_STAMP\n")); + break; + case OPC_OUB_SAS_HW_EVENT_ACK: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SAS_HW_EVENT_ACK\n")); + break; + case OPC_OUB_PORT_CONTROL: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_PORT_CONTROL\n")); + break; + case OPC_OUB_SMP_ABORT_RSP: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SMP_ABORT_RSP\n")); + mpi_task_abort_resp(pm8001_ha, piomb); + break; + case OPC_OUB_GET_NVMD_DATA: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GET_NVMD_DATA\n")); + mpi_get_nvmd_resp(pm8001_ha, piomb); + break; + case OPC_OUB_SET_NVMD_DATA: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SET_NVMD_DATA\n")); + mpi_set_nvmd_resp(pm8001_ha, piomb); + break; + case OPC_OUB_DEVICE_HANDLE_REMOVAL: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_DEVICE_HANDLE_REMOVAL\n")); + break; + case OPC_OUB_SET_DEVICE_STATE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SET_DEVICE_STATE\n")); + mpi_set_dev_state_resp(pm8001_ha, piomb); + break; + case OPC_OUB_GET_DEVICE_STATE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_GET_DEVICE_STATE\n")); + break; + case OPC_OUB_SET_DEV_INFO: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SET_DEV_INFO\n")); + break; + case OPC_OUB_SAS_RE_INITIALIZE: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("OPC_OUB_SAS_RE_INITIALIZE\n")); + break; + default: + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Unknown outbound Queue IOMB OPC = %x\n", + opc)); + break; + } +} + +static int process_oq(struct pm8001_hba_info *pm8001_ha, u32 num) +{ + struct outbound_queue_table *circularQ; + void *pMsg1 = NULL; + u8 bc = 0; + u32 ret = MPI_IO_STATUS_FAIL, processedMsgCount = 0; + + circularQ = &pm8001_ha->outb_data[num]; + do { + ret = mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc); + if (MPI_IO_STATUS_SUCCESS == ret) { + /* process the outbound message */ + process_one_iomb(pm8001_ha, (void *)((u8 *)pMsg1 - 4)); + /* free the message from the outbound circular buffer */ + mpi_msg_free_set(pm8001_ha, circularQ, bc); + processedMsgCount++; + } + if (MPI_IO_STATUS_BUSY == ret) { + /* Update the producer index from SPC */ + READ_LE_32((u32 *)(&(circularQ->producer_index)), + circularQ->pi_virt, 0); + if (circularQ->producer_index == + circularQ->consumer_idx) + /* OQ is empty */ + break; + } + } while (100 > processedMsgCount);/*end message processing if hit the + count*/ + return ret; +} + +/* PCI_DMA_... to our direction translation. */ +static const u8 data_dir_flags[] = { + [PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT,/* UNSPECIFIED */ + [PCI_DMA_TODEVICE] = DATA_DIR_OUT,/* OUTBOUND */ + [PCI_DMA_FROMDEVICE] = DATA_DIR_IN,/* INBOUND */ + [PCI_DMA_NONE] = DATA_DIR_NONE,/* NO TRANSFER */ +}; +static void +pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd) +{ + int i; + struct scatterlist *sg; + struct pm8001_prd *buf_prd = prd; + + for_each_sg(scatter, sg, nr, i) { + buf_prd->addr = cpu_to_le64(sg_dma_address(sg)); + buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg)); + buf_prd->im_len.e = 0; + buf_prd++; + } +} + +static void build_smp_cmd(u32 deviceID, u32 hTag, struct smp_req *psmp_cmd) +{ + psmp_cmd->tag = hTag; + psmp_cmd->device_id = deviceID; + psmp_cmd->len_ip_ir = 1|(1 << 1); +} + +/** + * pm8001_chip_smp_req - send a SMP task, note SMP command is formed in + * sas layer + * @pm8001_ha: our hba card information. + * @ccb: the ccb information this request used. + */ +static int pm8001_chip_smp_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + int elem, rc; + struct sas_task *task = ccb->task; + struct domain_device *dev = task->dev; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct scatterlist *sg_req, *sg_resp; + u32 req_len, resp_len; + struct smp_req smp_cmd; + u32 opc; + struct inbound_queue_table *circularQ; + + PM8001_IO_DBG(pm8001_ha, pm8001_printk(" pm8001_chip_smp_req\n")); + memset(&smp_cmd, 0, sizeof(smp_cmd)); + /* + * DMA-map SMP request, response buffers + */ + sg_req = &task->smp_task.smp_req; + elem = dma_map_sg(pm8001_ha->dev, sg_req, 1, PCI_DMA_TODEVICE); + if (!elem) + return -ENOMEM; + req_len = sg_dma_len(sg_req); + + sg_resp = &task->smp_task.smp_resp; + elem = dma_map_sg(pm8001_ha->dev, sg_resp, 1, PCI_DMA_FROMDEVICE); + if (!elem) { + rc = -ENOMEM; + goto err_out; + } + resp_len = sg_dma_len(sg_resp); + /* must be in dwords */ + if ((req_len & 0x3) || (resp_len & 0x3)) { + rc = -EINVAL; + goto err_out_2; + } + + opc = OPC_INB_SMP_REQUEST; + circularQ = &pm8001_ha->inb_data[0]; + smp_cmd.tag = ccb->ccb_tag; + smp_cmd.long_smp_req.long_req_addr = + cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req)); + smp_cmd.long_smp_req.long_req_size = + cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4); + smp_cmd.long_smp_req.long_resp_addr = + cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp)); + smp_cmd.long_smp_req.long_resp_size = + cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4); + build_smp_cmd(pm8001_dev->device_id, smp_cmd.tag, &smp_cmd); + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&smp_cmd); + return 0; + +err_out_2: + dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_resp, 1, + PCI_DMA_FROMDEVICE); +err_out: + dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_req, 1, + PCI_DMA_TODEVICE); + return rc; +} + +/** + * pm8001_chip_ssp_io_req - send a SSP task, note ssp command is formed in + * upper layer such as scsi core. + * @pm8001_ha: our hba card information. + * @ccb: the ccb information this request used. + */ +static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + struct sas_task *task = ccb->task; + struct domain_device *dev = task->dev; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct ssp_ini_io_start_req ssp_cmd; + u32 tag = ccb->ccb_tag; + u64 phys_addr; + struct inbound_queue_table *circularQ; + u32 opc = OPC_INB_SSPINIIOSTART; + memset(&ssp_cmd, 0, sizeof(ssp_cmd)); + memcpy(ssp_cmd.ssp_iu.lun, task->ssp_task.LUN, 8); + ssp_cmd.dir_m_tlr = data_dir_flags[task->data_dir] << 8 | 0x03; + ssp_cmd.data_len = task->total_xfer_len; + ssp_cmd.device_id = pm8001_dev->device_id; + ssp_cmd.tag = tag; + if (task->ssp_task.enable_first_burst) + ssp_cmd.ssp_iu.efb_prio_attr |= 0x80; + ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_prio << 3); + ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7); + memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cdb, 16); + circularQ = &pm8001_ha->inb_data[0]; + + /* fill in PRD (scatter/gather) table, if any */ + if (task->num_scatter > 1) { + pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd); + phys_addr = virt_to_bus((void *)(ccb->buf_prd)); + ssp_cmd.addr_low = LOW_32_BITS(phys_addr); + ssp_cmd.addr_high = HIGH_32_BITS(phys_addr); + ssp_cmd.esgl = 1<<31; + } else if (task->num_scatter == 1) { + u64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter)); + ssp_cmd.addr_low = LOW_32_BITS(dma_addr); + ssp_cmd.addr_high = HIGH_32_BITS(dma_addr); + ssp_cmd.len = task->total_xfer_len; + ssp_cmd.esgl = 0; + } else if (task->num_scatter == 0) { + ssp_cmd.addr_low = 0; + ssp_cmd.addr_high = 0; + ssp_cmd.len = task->total_xfer_len; + ssp_cmd.esgl = 0; + } + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&ssp_cmd); + return 0; +} + +static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + struct sas_task *task = ccb->task; + struct domain_device *dev = task->dev; + struct pm8001_device *pm8001_ha_dev = dev->lldd_dev; + u32 tag = ccb->ccb_tag; + struct sata_start_req sata_cmd; + u32 hdr_tag, ncg_tag = 0; + u64 phys_addr; + u32 ATAP = 0x0; + u32 dir; + struct inbound_queue_table *circularQ; + u32 opc = OPC_INB_SATA_HOST_OPSTART; + memset(&sata_cmd, 0, sizeof(sata_cmd)); + circularQ = &pm8001_ha->inb_data[0]; + if (task->data_dir == PCI_DMA_NONE) { + ATAP = 0x04; /* no data*/ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("no data \n")); + } else if (likely(!task->ata_task.device_control_reg_update)) { + if (task->ata_task.dma_xfer) { + ATAP = 0x06; /* DMA */ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("DMA \n")); + } else { + ATAP = 0x05; /* PIO*/ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("PIO \n")); + } + if (task->ata_task.use_ncq && + dev->sata_dev.command_set != ATAPI_COMMAND_SET) { + ATAP = 0x07; /* FPDMA */ + PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA \n")); + } + } + if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag)) + ncg_tag = hdr_tag; + dir = data_dir_flags[task->data_dir] << 8; + sata_cmd.tag = tag; + sata_cmd.device_id = pm8001_ha_dev->device_id; + sata_cmd.data_len = task->total_xfer_len; + sata_cmd.ncqtag_atap_dir_m = + ((ncg_tag & 0xff)<<16)|((ATAP & 0x3f) << 10) | dir; + sata_cmd.sata_fis = task->ata_task.fis; + if (likely(!task->ata_task.device_control_reg_update)) + sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */ + sata_cmd.sata_fis.flags &= 0xF0;/* PM_PORT field shall be 0 */ + /* fill in PRD (scatter/gather) table, if any */ + if (task->num_scatter > 1) { + pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd); + phys_addr = virt_to_bus((void *)(ccb->buf_prd)); + sata_cmd.addr_low = LOW_32_BITS(phys_addr); + sata_cmd.addr_high = HIGH_32_BITS(phys_addr); + sata_cmd.esgl = 1 << 31; + } else if (task->num_scatter == 1) { + u64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter)); + sata_cmd.addr_low = LOW_32_BITS(dma_addr); + sata_cmd.addr_high = HIGH_32_BITS(dma_addr); + sata_cmd.len = task->total_xfer_len; + sata_cmd.esgl = 0; + } else if (task->num_scatter == 0) { + sata_cmd.addr_low = 0; + sata_cmd.addr_high = 0; + sata_cmd.len = task->total_xfer_len; + sata_cmd.esgl = 0; + } + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&sata_cmd); + return 0; +} + +/** + * pm8001_chip_phy_start_req - start phy via PHY_START COMMAND + * @pm8001_ha: our hba card information. + * @num: the inbound queue number + * @phy_id: the phy id which we wanted to start up. + */ +static int +pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u32 num, + u8 phy_id) +{ + struct phy_start_req payload; + struct inbound_queue_table *circularQ; + u32 tag = 0x01; + u32 opcode = OPC_INB_PHYSTART; + circularQ = &pm8001_ha->inb_data[num]; + memset(&payload, 0, sizeof(payload)); + payload.tag = tag; + /* + ** [0:7] PHY Identifier + ** [8:11] link rate 1.5G, 3G, 6G + ** [12:13] link mode 01b SAS mode; 10b SATA mode; 11b both + ** [14] 0b disable spin up hold; 1b enable spin up hold + */ + payload.ase_sh_lm_slr_phyid = SPINHOLD_DISABLE | LINKMODE_AUTO | + LINKRATE_15 | LINKRATE_30 | LINKRATE_60 | phy_id; + payload.sas_identify.dev_type = SAS_END_DEV; + payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL; + memcpy(payload.sas_identify.sas_addr, + pm8001_ha->sas_addr, SAS_ADDR_SIZE); + payload.sas_identify.phy_id = phy_id; + mpi_build_cmd(pm8001_ha, circularQ, opcode, (u32 *)&payload); + return 0; +} + +/** + * pm8001_chip_phy_stop_req - start phy via PHY_STOP COMMAND + * @pm8001_ha: our hba card information. + * @num: the inbound queue number + * @phy_id: the phy id which we wanted to start up. + */ +static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha, + u32 num, u8 phy_id) +{ + struct phy_stop_req payload; + struct inbound_queue_table *circularQ; + u32 tag = 0x01; + u32 opcode = OPC_INB_PHYSTOP; + circularQ = &pm8001_ha->inb_data[num]; + memset(&payload, 0, sizeof(payload)); + payload.tag = tag; + payload.phy_id = phy_id; + mpi_build_cmd(pm8001_ha, circularQ, opcode, (u32 *)&payload); + return 0; +} + +/** + * see comments on mpi_reg_resp. + */ +static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha, + u32 num, struct pm8001_device *pm8001_dev, u32 flag) +{ + struct reg_dev_req payload; + u32 opc; + u32 stp_sspsmp_sata = 0x4; + struct inbound_queue_table *circularQ; + u32 linkrate, phy_id; + u32 rc, tag = 0xdeadbeef; + struct pm8001_ccb_info *ccb; + u8 retryFlag = 0x1; + u16 firstBurstSize = 0; + u16 ITNT = 2000; + struct domain_device *dev = pm8001_dev->sas_device; + struct domain_device *parent_dev = dev->parent; + + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("send_reg_dev_req \n")); + circularQ = &pm8001_ha->inb_data[num]; + + memset(&payload, 0, sizeof(payload)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->device = pm8001_dev; + ccb->ccb_tag = tag; + payload.tag = tag; + if (flag == 1) + stp_sspsmp_sata = 0x02; /*direct attached sata */ + else { + if (pm8001_dev->dev_type == SATA_DEV) + stp_sspsmp_sata = 0x00; /* stp*/ + else if (pm8001_dev->dev_type == SAS_END_DEV || + pm8001_dev->dev_type == EDGE_DEV || + pm8001_dev->dev_type == FANOUT_DEV) + stp_sspsmp_sata = 0x01; /*ssp or smp*/ + } + if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) + phy_id = parent_dev->ex_dev.ex_phy->phy_id; + else + phy_id = pm8001_dev->attached_phy; + opc = OPC_INB_REG_DEV; + linkrate = pm8001_dev->sas_device->linkrate; + payload.phyid_portid = ((pm8001_dev->sas_device->port->id) & 0x0F) | + ((phy_id & 0x0F) << 4); + payload.dtype_dlr_retry = (retryFlag & 0x01) | + ((linkrate & 0x0F) * 0x1000000) | + ((stp_sspsmp_sata & 0x03) * 0x10000000); + payload.firstburstsize_ITNexustimeout = + ITNT | (firstBurstSize * 0x10000); + memcpy(&payload.sas_addr_hi, pm8001_dev->sas_device->sas_addr, + SAS_ADDR_SIZE); + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&payload); + return 0; +} + +/** + * see comments on mpi_reg_resp. + */ +static int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha, + u32 num, u32 device_id) +{ + struct dereg_dev_req payload; + u32 opc = OPC_INB_DEREG_DEV_HANDLE; + struct inbound_queue_table *circularQ; + + circularQ = &pm8001_ha->inb_data[num]; + memset((u8 *)&payload, 0, sizeof(payload)); + payload.tag = 1; + payload.device_id = device_id; + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("unregister device device_id = %d\n", device_id)); + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&payload); + return 0; +} + +/** + * pm8001_chip_phy_ctl_req - the lower lever device driver(LLDD) provided + * function to support the phy operation, note this phy is local phy, + * the remote phy control is with SMP command. + * @pm8001_ha: our hba card information. + * @num: the inbound queue number + * @phy_id: the phy id which we wanted to operate + * @phy_op: + */ +static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha, + u32 num, u32 phyId, u32 phy_op) +{ + struct local_phy_ctl_req payload; + struct inbound_queue_table *circularQ; + u32 opc = OPC_INB_LOCAL_PHY_CONTROL; + memset((u8 *)&payload, 0, sizeof(payload)); + circularQ = &pm8001_ha->inb_data[num]; + payload.tag = 1; + payload.phyop_phyid = ((phy_op & 0xff) << 8) | (phyId & 0x0F); + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&payload); + return 0; +} + +/** + * pm8001_chip_isr - PM8001 isr handler. + * @pm8001_ha: our hba card information. + * @irq: irq number. + * @stat: stat. + */ +static irqreturn_t +pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha, int irq) +{ + u32 retVal; + u32 num = 0; + irq = irq ;/*to fix the gcc compiler noisy*/ + retVal = pm8001_cr32(pm8001_ha, num, MSGU_ODR); + if (retVal) { + /* clear interrupt bit */ + pm8001_cw32(pm8001_ha, num, MSGU_ODCR, retVal); + /* mask interrupt */ + pm8001_cw32(pm8001_ha, num, MSGU_ODMR, retVal); + } else + return IRQ_NONE; + process_oq(pm8001_ha, num); + pm8001_cw32(pm8001_ha, num, MSGU_ODMR, 0); + return IRQ_HANDLED; +} + +static int send_task_abort(struct pm8001_hba_info *pm8001_ha, u32 opc, + u32 dev_id, u8 flag, u32 task_tag, u32 cmd_tag) +{ + struct task_abort_req task_abort; + struct inbound_queue_table *circularQ; + + circularQ = &pm8001_ha->inb_data[0]; + memset(&task_abort, 0, sizeof(task_abort)); + if (ABORT_SINGLE == (flag & ABORT_MASK)) { + task_abort.abort_all = 0; + task_abort.device_id = dev_id; + task_abort.tag_to_abort = task_tag; + task_abort.tag = cmd_tag; + } else if (ABORT_ALL == (flag & ABORT_MASK)) { + task_abort.abort_all = 1; + task_abort.device_id = dev_id; + task_abort.tag = cmd_tag; + } + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&task_abort); + return 0; +} + +/** + * pm8001_chip_abort_task - SAS abort task when error or exception happened. + * @task: the task we wanted to aborted. + * @flag: the abort flag. + */ +static int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, u32 cmd_tag) +{ + u32 opc, device_id; + int rc = TMF_RESP_FUNC_FAILED; + PM8001_IO_DBG(pm8001_ha, pm8001_printk("Abort tag[%x]", task_tag)); + if (pm8001_dev->dev_type == SAS_END_DEV) + opc = OPC_INB_SSP_ABORT; + else if (pm8001_dev->dev_type == SATA_DEV) + opc = OPC_INB_SATA_ABORT; + else + opc = OPC_INB_SMP_ABORT;/* SMP */ + device_id = pm8001_dev->device_id; + rc = send_task_abort(pm8001_ha, opc, device_id, flag, + task_tag, cmd_tag); + if (rc != TMF_RESP_FUNC_COMPLETE) + PM8001_IO_DBG(pm8001_ha, pm8001_printk("rc= %d\n", rc)); + return rc; +} + +/** + * pm8001_chip_ssp_tm_req - if the ssp command is a TMF, built the task + * managment command. + * @pm8001_ha: our hba card information. + * @ccb: the ccb information. + * @tmf: task management function. + */ +static int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf) +{ + struct sas_task *task = ccb->task; + struct domain_device *dev = task->dev; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + u32 opc = OPC_INB_SSPINITMSTART; + struct inbound_queue_table *circularQ; + struct ssp_ini_tm_start_req sspTMCmd; + + memset(&sspTMCmd, 0, sizeof(sspTMCmd)); + sspTMCmd.device_id = pm8001_dev->device_id; + sspTMCmd.relate_tag = tmf->tag_of_task_to_be_managed; + sspTMCmd.tmf = tmf->tmf; + sspTMCmd.ds_ads_m = 1 << 2; + memcpy(sspTMCmd.lun, task->ssp_task.LUN, 8); + sspTMCmd.tag = ccb->ccb_tag; + circularQ = &pm8001_ha->inb_data[0]; + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&sspTMCmd); + return 0; +} + +static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha, + u32 num, void *payload) +{ + u32 opc = OPC_INB_GET_NVMD_DATA; + u32 nvmd_type; + u32 rc; + u32 tag; + struct pm8001_ccb_info *ccb; + struct inbound_queue_table *circularQ; + struct get_nvm_data_req nvmd_req; + struct fw_control_ex *fw_control_context; + struct pm8001_ioctl_payload *ioctl_payload = payload; + + nvmd_type = ioctl_payload->minor_function; + fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL); + fw_control_context->usrAddr = (u8 *)&ioctl_payload->func_specific[0]; + fw_control_context->len = ioctl_payload->length; + circularQ = &pm8001_ha->inb_data[num]; + memset(&nvmd_req, 0, sizeof(nvmd_req)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->ccb_tag = tag; + ccb->fw_control_context = fw_control_context; + nvmd_req.tag = tag; + + switch (nvmd_type) { + case TWI_DEVICE: { + u32 twi_addr, twi_page_size; + twi_addr = 0xa8; + twi_page_size = 2; + + nvmd_req.len_ir_vpdd = IPMode | twi_addr << 16 | + twi_page_size << 8 | TWI_DEVICE; + nvmd_req.resp_len = ioctl_payload->length; + nvmd_req.resp_addr_hi = + pm8001_ha->memoryMap.region[NVMD].phys_addr_hi; + nvmd_req.resp_addr_lo = + pm8001_ha->memoryMap.region[NVMD].phys_addr_lo; + break; + } + case C_SEEPROM: { + nvmd_req.len_ir_vpdd = IPMode | C_SEEPROM; + nvmd_req.resp_len = ioctl_payload->length; + nvmd_req.resp_addr_hi = + pm8001_ha->memoryMap.region[NVMD].phys_addr_hi; + nvmd_req.resp_addr_lo = + pm8001_ha->memoryMap.region[NVMD].phys_addr_lo; + break; + } + case VPD_FLASH: { + nvmd_req.len_ir_vpdd = IPMode | VPD_FLASH; + nvmd_req.resp_len = ioctl_payload->length; + nvmd_req.resp_addr_hi = + pm8001_ha->memoryMap.region[NVMD].phys_addr_hi; + nvmd_req.resp_addr_lo = + pm8001_ha->memoryMap.region[NVMD].phys_addr_lo; + break; + } + case EXPAN_ROM: { + nvmd_req.len_ir_vpdd = IPMode | EXPAN_ROM; + nvmd_req.resp_len = ioctl_payload->length; + nvmd_req.resp_addr_hi = + pm8001_ha->memoryMap.region[NVMD].phys_addr_hi; + nvmd_req.resp_addr_lo = + pm8001_ha->memoryMap.region[NVMD].phys_addr_lo; + break; + } + default: + break; + } + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&nvmd_req); + return 0; +} + +static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha, + u32 num, void *payload) +{ + u32 opc = OPC_INB_SET_NVMD_DATA; + u32 nvmd_type; + u32 rc; + u32 tag; + struct pm8001_ccb_info *ccb; + struct inbound_queue_table *circularQ; + struct set_nvm_data_req nvmd_req; + struct fw_control_ex *fw_control_context; + struct pm8001_ioctl_payload *ioctl_payload = payload; + + nvmd_type = ioctl_payload->minor_function; + fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL); + circularQ = &pm8001_ha->inb_data[num]; + memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr, + ioctl_payload->func_specific, + ioctl_payload->length); + memset(&nvmd_req, 0, sizeof(nvmd_req)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->fw_control_context = fw_control_context; + ccb->ccb_tag = tag; + nvmd_req.tag = tag; + switch (nvmd_type) { + case TWI_DEVICE: { + u32 twi_addr, twi_page_size; + twi_addr = 0xa8; + twi_page_size = 2; + nvmd_req.reserved[0] = 0xFEDCBA98; + nvmd_req.len_ir_vpdd = IPMode | twi_addr << 16 | + twi_page_size << 8 | TWI_DEVICE; + nvmd_req.resp_len = ioctl_payload->length; + nvmd_req.resp_addr_hi = + pm8001_ha->memoryMap.region[NVMD].phys_addr_hi; + nvmd_req.resp_addr_lo = + pm8001_ha->memoryMap.region[NVMD].phys_addr_lo; + break; + } + case C_SEEPROM: + nvmd_req.len_ir_vpdd = IPMode | C_SEEPROM; + nvmd_req.resp_len = ioctl_payload->length; + nvmd_req.reserved[0] = 0xFEDCBA98; + nvmd_req.resp_addr_hi = + pm8001_ha->memoryMap.region[NVMD].phys_addr_hi; + nvmd_req.resp_addr_lo = + pm8001_ha->memoryMap.region[NVMD].phys_addr_lo; + break; + case VPD_FLASH: + nvmd_req.len_ir_vpdd = IPMode | VPD_FLASH; + nvmd_req.resp_len = ioctl_payload->length; + nvmd_req.reserved[0] = 0xFEDCBA98; + nvmd_req.resp_addr_hi = + pm8001_ha->memoryMap.region[NVMD].phys_addr_hi; + nvmd_req.resp_addr_lo = + pm8001_ha->memoryMap.region[NVMD].phys_addr_lo; + break; + case EXPAN_ROM: + nvmd_req.len_ir_vpdd = IPMode | EXPAN_ROM; + nvmd_req.resp_len = ioctl_payload->length; + nvmd_req.reserved[0] = 0xFEDCBA98; + nvmd_req.resp_addr_hi = + pm8001_ha->memoryMap.region[NVMD].phys_addr_hi; + nvmd_req.resp_addr_lo = + pm8001_ha->memoryMap.region[NVMD].phys_addr_lo; + break; + default: + break; + } + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&nvmd_req); + return 0; +} + +/** + * pm8001_chip_fw_flash_update_build - the LLDD provided + * function to support the firmware update operation + * @pm8001_ha: our hba card information. + * @num: the inbound queue number + * @fw_flash_updata_info: firmware flash update param + */ +static int +pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha, + u32 num, void *fw_flash_updata_info, u32 tag) +{ + struct fw_flash_Update_req payload; + struct fw_flash_updata_info *info; + struct inbound_queue_table *circularQ; + u32 opc = OPC_INB_FW_FLASH_UPDATE; + + memset((u8 *)&payload, 0, sizeof(struct fw_flash_Update_req)); + circularQ = &pm8001_ha->inb_data[num]; + info = fw_flash_updata_info; + payload.tag = tag; + payload.cur_image_len = info->cur_image_len; + payload.cur_image_offset = info->cur_image_offset; + payload.total_image_len = info->total_image_len; + payload.len = info->sgl.im_len.len ; + payload.sgl_addr_lo = LOW_32_BITS(info->sgl.addr); + payload.sgl_addr_hi = HIGH_32_BITS(info->sgl.addr); + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&payload); + return 0; +} + +static int +pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha, + u32 num, void *payload) +{ + struct fw_flash_updata_info flash_update_info; + struct fw_control_info *fw_control; + struct fw_control_ex *fw_control_context; + u32 rc; + u32 tag; + struct pm8001_ccb_info *ccb; + void *buffer = NULL; + dma_addr_t phys_addr; + u32 phys_addr_hi; + u32 phys_addr_lo; + struct pm8001_ioctl_payload *ioctl_payload = payload; + + fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL); + fw_control = (struct fw_control_info *)&ioctl_payload->func_specific[0]; + if (fw_control->len != 0) { + if (pm8001_mem_alloc(pm8001_ha->pdev, + (void **)&buffer, + &phys_addr, + &phys_addr_hi, + &phys_addr_lo, + fw_control->len) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Mem alloc failure\n")); + return -ENOMEM; + } + } + memset((void *)buffer, 0, fw_control->len); + memcpy((void *)buffer, fw_control->buffer, fw_control->len); + flash_update_info.sgl.addr = phys_addr; + flash_update_info.sgl.im_len.len = fw_control->len; + flash_update_info.sgl.im_len.e = 0; + flash_update_info.cur_image_offset = fw_control->offset; + flash_update_info.cur_image_len = fw_control->len; + flash_update_info.total_image_len = fw_control->size; + fw_control_context->fw_control = fw_control; + fw_control_context->virtAddr = buffer; + fw_control_context->len = fw_control->len; + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->fw_control_context = fw_control_context; + ccb->ccb_tag = tag; + pm8001_chip_fw_flash_update_build(pm8001_ha, num, + &flash_update_info, tag); + return 0; +} + +static int +pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u32 num, u32 state) +{ + struct set_dev_state_req payload; + struct inbound_queue_table *circularQ; + struct pm8001_ccb_info *ccb; + u32 rc; + u32 tag; + u32 opc = OPC_INB_SET_DEVICE_STATE; + memset((u8 *)&payload, 0, sizeof(payload)); + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + return -1; + ccb = &pm8001_ha->ccb_info[tag]; + ccb->ccb_tag = tag; + ccb->device = pm8001_dev; + circularQ = &pm8001_ha->inb_data[num]; + payload.tag = tag; + payload.device_id = pm8001_dev->device_id; + payload.nds = state; + mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&payload); + return 0; + +} + +const struct pm8001_dispatch pm8001_8001_dispatch = { + .name = "pmc8001", + .chip_init = pm8001_chip_init, + .chip_soft_rst = pm8001_chip_soft_rst, + .chip_iounmap = pm8001_chip_iounmap, + .isr = pm8001_chip_isr, + .interrupt_enable = pm8001_chip_interrupt_enable, + .interrupt_disable = pm8001_chip_interrupt_disable, + .make_prd = pm8001_chip_make_sg, + .smp_req = pm8001_chip_smp_req, + .ssp_io_req = pm8001_chip_ssp_io_req, + .sata_req = pm8001_chip_sata_req, + .phy_start_req = pm8001_chip_phy_start_req, + .phy_stop_req = pm8001_chip_phy_stop_req, + .reg_dev_req = pm8001_chip_reg_dev_req, + .dereg_dev_req = pm8001_chip_dereg_dev_req, + .phy_ctl_req = pm8001_chip_phy_ctl_req, + .task_abort = pm8001_chip_abort_task, + .ssp_tm_req = pm8001_chip_ssp_tm_req, + .get_nvmd_req = pm8001_chip_get_nvmd_req, + .set_nvmd_req = pm8001_chip_set_nvmd_req, + .fw_flash_update_req = pm8001_chip_fw_flash_update_req, + .set_dev_state_req = pm8001_chip_set_dev_state_req, +}; + diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h new file mode 100644 index 0000000..f2757f4 --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_hwi.h @@ -0,0 +1,981 @@ +/* + * 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. + * + */ +#ifndef _PMC8001_REG_H_ +#define _PMC8001_REG_H_ + +#include <linux/types.h> +#include <scsi/libsas.h> + + +/* for Request Opcode of IOMB */ +#define OPC_INB_ECHO 1 /* 0x000 */ +#define OPC_INB_PHYSTART 4 /* 0x004 */ +#define OPC_INB_PHYSTOP 5 /* 0x005 */ +#define OPC_INB_SSPINIIOSTART 6 /* 0x006 */ +#define OPC_INB_SSPINITMSTART 7 /* 0x007 */ +#define OPC_INB_SSPINIEXTIOSTART 8 /* 0x008 */ +#define OPC_INB_DEV_HANDLE_ACCEPT 9 /* 0x009 */ +#define OPC_INB_SSPTGTIOSTART 10 /* 0x00A */ +#define OPC_INB_SSPTGTRSPSTART 11 /* 0x00B */ +#define OPC_INB_SSPINIEDCIOSTART 12 /* 0x00C */ +#define OPC_INB_SSPINIEXTEDCIOSTART 13 /* 0x00D */ +#define OPC_INB_SSPTGTEDCIOSTART 14 /* 0x00E */ +#define OPC_INB_SSP_ABORT 15 /* 0x00F */ +#define OPC_INB_DEREG_DEV_HANDLE 16 /* 0x010 */ +#define OPC_INB_GET_DEV_HANDLE 17 /* 0x011 */ +#define OPC_INB_SMP_REQUEST 18 /* 0x012 */ +/* SMP_RESPONSE is removed */ +#define OPC_INB_SMP_RESPONSE 19 /* 0x013 */ +#define OPC_INB_SMP_ABORT 20 /* 0x014 */ +#define OPC_INB_REG_DEV 22 /* 0x016 */ +#define OPC_INB_SATA_HOST_OPSTART 23 /* 0x017 */ +#define OPC_INB_SATA_ABORT 24 /* 0x018 */ +#define OPC_INB_LOCAL_PHY_CONTROL 25 /* 0x019 */ +#define OPC_INB_GET_DEV_INFO 26 /* 0x01A */ +#define OPC_INB_FW_FLASH_UPDATE 32 /* 0x020 */ +#define OPC_INB_GPIO 34 /* 0x022 */ +#define OPC_INB_SAS_DIAG_MODE_START_END 35 /* 0x023 */ +#define OPC_INB_SAS_DIAG_EXECUTE 36 /* 0x024 */ +#define OPC_INB_SAS_HW_EVENT_ACK 37 /* 0x025 */ +#define OPC_INB_GET_TIME_STAMP 38 /* 0x026 */ +#define OPC_INB_PORT_CONTROL 39 /* 0x027 */ +#define OPC_INB_GET_NVMD_DATA 40 /* 0x028 */ +#define OPC_INB_SET_NVMD_DATA 41 /* 0x029 */ +#define OPC_INB_SET_DEVICE_STATE 42 /* 0x02A */ +#define OPC_INB_GET_DEVICE_STATE 43 /* 0x02B */ +#define OPC_INB_SET_DEV_INFO 44 /* 0x02C */ +#define OPC_INB_SAS_RE_INITIALIZE 45 /* 0x02D */ + +/* for Response Opcode of IOMB */ +#define OPC_OUB_ECHO 1 /* 0x001 */ +#define OPC_OUB_HW_EVENT 4 /* 0x004 */ +#define OPC_OUB_SSP_COMP 5 /* 0x005 */ +#define OPC_OUB_SMP_COMP 6 /* 0x006 */ +#define OPC_OUB_LOCAL_PHY_CNTRL 7 /* 0x007 */ +#define OPC_OUB_DEV_REGIST 10 /* 0x00A */ +#define OPC_OUB_DEREG_DEV 11 /* 0x00B */ +#define OPC_OUB_GET_DEV_HANDLE 12 /* 0x00C */ +#define OPC_OUB_SATA_COMP 13 /* 0x00D */ +#define OPC_OUB_SATA_EVENT 14 /* 0x00E */ +#define OPC_OUB_SSP_EVENT 15 /* 0x00F */ +#define OPC_OUB_DEV_HANDLE_ARRIV 16 /* 0x010 */ +/* SMP_RECEIVED Notification is removed */ +#define OPC_OUB_SMP_RECV_EVENT 17 /* 0x011 */ +#define OPC_OUB_SSP_RECV_EVENT 18 /* 0x012 */ +#define OPC_OUB_DEV_INFO 19 /* 0x013 */ +#define OPC_OUB_FW_FLASH_UPDATE 20 /* 0x014 */ +#define OPC_OUB_GPIO_RESPONSE 22 /* 0x016 */ +#define OPC_OUB_GPIO_EVENT 23 /* 0x017 */ +#define OPC_OUB_GENERAL_EVENT 24 /* 0x018 */ +#define OPC_OUB_SSP_ABORT_RSP 26 /* 0x01A */ +#define OPC_OUB_SATA_ABORT_RSP 27 /* 0x01B */ +#define OPC_OUB_SAS_DIAG_MODE_START_END 28 /* 0x01C */ +#define OPC_OUB_SAS_DIAG_EXECUTE 29 /* 0x01D */ +#define OPC_OUB_GET_TIME_STAMP 30 /* 0x01E */ +#define OPC_OUB_SAS_HW_EVENT_ACK 31 /* 0x01F */ +#define OPC_OUB_PORT_CONTROL 32 /* 0x020 */ +#define OPC_OUB_SKIP_ENTRY 33 /* 0x021 */ +#define OPC_OUB_SMP_ABORT_RSP 34 /* 0x022 */ +#define OPC_OUB_GET_NVMD_DATA 35 /* 0x023 */ +#define OPC_OUB_SET_NVMD_DATA 36 /* 0x024 */ +#define OPC_OUB_DEVICE_HANDLE_REMOVAL 37 /* 0x025 */ +#define OPC_OUB_SET_DEVICE_STATE 38 /* 0x026 */ +#define OPC_OUB_GET_DEVICE_STATE 39 /* 0x027 */ +#define OPC_OUB_SET_DEV_INFO 40 /* 0x028 */ +#define OPC_OUB_SAS_RE_INITIALIZE 41 /* 0x029 */ + +/* for phy start*/ +#define SPINHOLD_DISABLE (0x00 << 14) +#define SPINHOLD_ENABLE (0x01 << 14) +#define LINKMODE_SAS (0x01 << 12) +#define LINKMODE_DSATA (0x02 << 12) +#define LINKMODE_AUTO (0x03 << 12) +#define LINKRATE_15 (0x01 << 8) +#define LINKRATE_30 (0x02 << 8) +#define LINKRATE_60 (0x04 << 8) + +struct mpi_msg_hdr{ + u32 header; /* Bits [11:0] - Message operation code */ + /* Bits [15:12] - Message Category */ + /* Bits [21:16] - Outboundqueue ID for the + operation completion message */ + /* Bits [23:22] - Reserved */ + /* Bits [28:24] - Buffer Count, indicates how + many buffer are allocated for the massage */ + /* Bits [30:29] - Reserved */ + /* Bits [31] - Message Valid bit */ +}; + +/* + * brief the data structure of PHY Start Command + * use to describe enable the phy (64 bytes) + */ +struct phy_start_req { + u32 tag; + u32 ase_sh_lm_slr_phyid; + struct sas_identify_frame sas_identify; + u32 reserved[5]; +}; + +/* + * brief the data structure of PHY Start Command + * use to disable the phy (64 bytes) + */ +struct phy_stop_req { + u32 tag; + u32 phy_id; + u32 reserved[13]; +}; + +/* set device bits fis - device to host */ +struct set_dev_bits_fis { + u8 fis_type; /* 0xA1*/ + u8 n_i_pmport; + /* b7 : n Bit. Notification bit. If set device needs attention. */ + /* b6 : i Bit. Interrupt Bit */ + /* b5-b4: reserved2 */ + /* b3-b0: PM Port */ + u8 status; + u8 error; + u32 _r_a; +} __attribute__ ((packed)); +/* PIO setup FIS - device to host */ +struct pio_setup_fis { + u8 fis_type; /* 0x5f */ + u8 i_d_pmPort; + /* b7 : reserved */ + /* b6 : i bit. Interrupt bit */ + /* b5 : d bit. data transfer direction. set to 1 for device to host + xfer */ + /* b4 : reserved */ + /* b3-b0: PM Port */ + u8 status; + u8 error; + u8 lbal; + u8 lbam; + u8 lbah; + u8 device; + u8 lbal_exp; + u8 lbam_exp; + u8 lbah_exp; + u8 _r_a; + u8 sector_count; + u8 sector_count_exp; + u8 _r_b; + u8 e_status; + u8 _r_c[2]; + u8 transfer_count; +} __attribute__ ((packed)); + +/* + * brief the data structure of SATA Completion Response + * use to discribe the sata task response (64 bytes) + */ +struct sata_completion_resp { + u32 tag; + u32 status; + u32 param; + u32 sata_resp[12]; +}; + +/* + * brief the data structure of SAS HW Event Notification + * use to alert the host about the hardware event(64 bytes) + */ +struct hw_event_resp { + u32 lr_evt_status_phyid_portid; + u32 evt_param; + u32 npip_portstate; + struct sas_identify_frame sas_identify; + struct dev_to_host_fis sata_fis; +}; + +/* + * brief the data structure of REGISTER DEVICE Command + * use to describe MPI REGISTER DEVICE Command (64 bytes) + */ + +struct reg_dev_req { + u32 tag; + u32 phyid_portid; + u32 dtype_dlr_retry; + u32 firstburstsize_ITNexustimeout; + u32 sas_addr_hi; + u32 sas_addr_low; + u32 upper_device_id; + u32 reserved[8]; +}; + +/* + * brief the data structure of DEREGISTER DEVICE Command + * use to request spc to remove all internal resources associated + * with the device id (64 bytes) + */ + +struct dereg_dev_req { + u32 tag; + u32 device_id; + u32 reserved[13]; +} ; + +/* + * brief the data structure of DEVICE_REGISTRATION Response + * use to notify the completion of the device registration (64 bytes) + */ + +struct dev_reg_resp { + u32 tag; + u32 status; + u32 device_id; + u32 reserved[12]; +}; + +/* + * brief the data structure of Local PHY Control Command + * use to issue PHY CONTROL to local phy (64 bytes) + */ +struct local_phy_ctl_req { + u32 tag; + u32 phyop_phyid; + u32 reserved1[13]; +}; + +/** + * brief the data structure of Local Phy Control Response + * use to describe MPI Local Phy Control Response (64 bytes) + */ +struct local_phy_ctl_resp { + u32 tag; + u32 phyop_phyid; + u32 status; + u32 reserved[12]; +} ; + +#define OP_BITS 0x0000FF00 +#define ID_BITS 0x0000000F + +/* + * brief the data structure of PORT Control Command + * use to control port properties (64 bytes) + */ + +struct port_ctl_req { + u32 tag; + u32 portop_portid; + u32 param0; + u32 param1; + u32 reserved1[11]; +}; + +/* + * brief the data structure of HW Event Ack Command + * use to acknowledge receive HW event (64 bytes) + */ + +struct hw_event_ack_req { + u32 tag; + u32 sea_phyid_portid; + u32 param0; + u32 param1; + u32 reserved1[11]; +}; + +/* + * brief the data structure of SSP Completion Response + * use to indicate a SSP Completion (n bytes) + */ +struct ssp_completion_resp { + u32 tag; + u32 status; + u32 param; + u32 ssptag_rescv_rescpad; + struct ssp_response_iu ssp_resp_iu; + u32 residual_count; +}; + +#define SSP_RESCV_BIT 0x00010000 + +/* + * brief the data structure of SATA EVNET esponse + * use to indicate a SATA Completion (64 bytes) + */ + +struct sata_event_resp { + u32 tag; + u32 event; + u32 port_id; + u32 device_id; + u32 reserved[11]; +} ; +/* + * brief the data structure of SSP EVNET esponse + * use to indicate a SSP Completion (64 bytes) + */ + +struct ssp_event_resp { + u32 tag; + u32 event; + u32 port_id; + u32 device_id; + u32 reserved[11]; +}; +/** + * brief the data structure of General Event Notification Response + * use to describe MPI General Event Notification Response (64 bytes) + */ +struct general_event_resp { + u32 status; + u32 inb_IOMB_payload[14]; +}; + +#define GENERAL_EVENT_PAYLOAD 14 +#define OPCODE_BITS 0x00000fff + +/* + * brief the data structure of SMP Request Command + * use to describe MPI SMP REQUEST Command (64 bytes) + */ +struct smp_req { + u32 tag; + u32 device_id; + u32 len_ip_ir; + /* Bits [0] - Indirect response */ + /* Bits [1] - Indirect Payload */ + /* Bits [15:2] - Reserved */ + /* Bits [23:16] - direct payload Len */ + /* Bits [31:24] - Reserved */ + u8 smp_req16[16]; + union { + u8 smp_req[32]; + struct { + __le64 long_req_addr;/* sg dma address, LE */ + __le32 long_req_size;/* LE */ + u32 _r_a; + __le64 long_resp_addr;/* sg dma address, LE */ + __le32 long_resp_size;/* LE */ + u32 _r_b; + } long_smp_req;/* sequencer extension */ + }; +} ; +/* + * brief the data structure of SMP Completion Response + * use to describe MPI SMP Completion Response (64 bytes) + */ +struct smp_completion_resp { + u32 tag; + u32 status; + u32 param; + u32 _r_a[12]; +}; +/* + *brief the data structure of SSP SMP SATA Abort Command + * use to describe MPI SSP SMP & SATA Abort Command (64 bytes) + */ +struct task_abort_req { + u32 tag; + u32 device_id; + u32 tag_to_abort; + u32 abort_all; + u32 reserved[11]; +}; +/* These flags used for SSP SMP & SATA Abort */ +#define ABORT_MASK 0x3 +#define ABORT_SINGLE 0x0 +#define ABORT_ALL 0x1 + +/** + * brief the data structure of SSP SATA SMP Abort Response + * use to describe SSP SMP & SATA Abort Response ( 64 bytes) + */ +struct task_abort_resp { + u32 tag; + u32 status; + u32 scp; + u32 reserved[12]; +}; + +/** + * brief the data structure of SAS Diagnostic Start/End Command + * use to describe MPI SAS Diagnostic Start/End Command (64 bytes) + */ +struct sas_diag_start_end_req { + u32 tag; + u32 operation_phyid; + u32 reserved[13]; +}; + +/** + * brief the data structure of SAS Diagnostic Execute Command + * use to describe MPI SAS Diagnostic Execute Command (64 bytes) + */ +struct sas_diag_execute_req{ + u32 tag; + u32 cmdtype_cmddesc_phyid; + u32 pat1_pat2; + u32 threshold; + u32 codepat_errmsk; + u32 pmon; + u32 pERF1CTL; + u32 reserved[8]; +}; + +#define SAS_DIAG_PARAM_BYTES 24 + +/* + * brief the data structure of Set Device State Command + * use to describe MPI Set Device State Command (64 bytes) + */ +struct set_dev_state_req { + u32 tag; + u32 device_id; + u32 nds; + u32 reserved[12]; +}; + +/* + * brief the data structure of SATA Start Command + * use to describe MPI SATA IO Start Command (64 bytes) + */ + +struct sata_start_req { + u32 tag; + u32 device_id; + u32 data_len; + u32 ncqtag_atap_dir_m; + struct host_to_dev_fis sata_fis; + u32 reserved1; + u32 reserved2; + u32 addr_low; + u32 addr_high; + u32 len; + u32 esgl; +} ; +/** + * brief the data structure of SSP INI TM Start Command + * use to describe MPI SSP INI TM Start Command (64 bytes) + */ +struct ssp_ini_tm_start_req { + u32 tag; + u32 device_id; + u32 relate_tag; + u32 tmf; + u8 lun[8]; + u32 ds_ads_m; + u32 reserved[8]; +} ; + +struct ssp_info_unit { + u8 lun[8];/* SCSI Logical Unit Number */ + u8 reserved1;/* reserved */ + u8 efb_prio_attr; + /* B7 : enabledFirstBurst */ + /* B6-3 : taskPriority */ + /* B2-0 : taskAttribute */ + u8 reserved2; /* reserved */ + u8 additional_cdb_len; + /* B7-2 : additional_cdb_len */ + /* B1-0 : reserved */ + u8 cdb[16];/* The SCSI CDB up to 16 bytes length */ +} ; + +/** + * brief the data structure of SSP INI IO Start Command + * use to describe MPI SSP INI IO Start Command (64 bytes) + */ +struct ssp_ini_io_start_req { + u32 tag; + u32 device_id; + u32 data_len; + u32 dir_m_tlr; + struct ssp_info_unit ssp_iu; + u32 addr_low; + u32 addr_high; + u32 len; + u32 esgl; +}; + +/** + * brief the data structure of Firmware download + * use to describe MPI FW DOWNLOAD Command (64 bytes) + */ +struct fw_flash_Update_req { + u32 tag; + u32 cur_image_offset; + u32 cur_image_len; + u32 total_image_len; + u32 reserved0[7]; + u32 sgl_addr_lo; + u32 sgl_addr_hi; + u32 len; + u32 ext_reserved; +}; + +#define FWFLASH_IOMB_RESERVED_LEN 0x07 +/** + * brief the data structure of FW_FLASH_UPDATE Response + * use to describe MPI FW_FLASH_UPDATE Response (64 bytes) + * + */ +struct fw_flash_Update_resp { + dma_addr_t tag; + u32 status; + u32 reserved[13]; +}; + +/** + * FW Flash Update status values + */ +#define FLASH_UPDATE_COMPLETE_PENDING_REBOOT 0x00 +#define FLASH_UPDATE_IN_PROGRESS 0x01 +#define FLASH_UPDATE_HDR_ERR 0x02 +#define FLASH_UPDATE_OFFSET_ERR 0x03 +#define FLASH_UPDATE_CRC_ERR 0x04 +#define FLASH_UPDATE_LENGTH_ERR 0x05 +#define FLASH_UPDATE_HW_ERR 0x06 +#define FLASH_UPDATE_DNLD_NOT_SUPPORTED 0x10 +#define FLASH_UPDATE_DISABLED 0x11 + +/** + * brief the data structure of Get NVM Data Command + * use to get data from NVM in HBA(64 bytes) + */ +struct get_nvm_data_req { + u32 tag; + u32 len_ir_vpdd; + u32 vpd_offset; + u32 reserved[8]; + u32 resp_addr_lo; + u32 resp_addr_hi; + u32 resp_len; + u32 reserved1; +}; + +struct set_nvm_data_req { + u32 tag; + u32 len_ir_vpdd; + u32 vpd_offset; + u32 reserved[8]; + u32 resp_addr_lo; + u32 resp_addr_hi; + u32 resp_len; + u32 reserved1; +}; + +#define TWI_DEVICE 0x0 +#define C_SEEPROM 0x1 +#define VPD_FLASH 0x4 +#define AAP1_RDUMP 0x5 +#define IOP_RDUMP 0x6 +#define EXPAN_ROM 0x7 + +#define IPMode 0x80000000 +#define NVMD_TYPE 0x0000000F +#define NVMD_STAT 0x0000FFFF +#define NVMD_LEN 0xFF000000 +/** + * brief the data structure of Get NVMD Data Response + * use to describe MPI Get NVMD Data Response (64 bytes) + */ +struct get_nvm_data_resp { + u32 tag; + u32 ir_tda_bn_dps_das_nvm; + u32 dlen_status; + u32 nvm_data[12]; +}; + +/** + * brief the data structure of SAS Diagnostic Start/End Response + * use to describe MPI SAS Diagnostic Start/End Response (64 bytes) + * + */ +struct sas_diag_start_end_resp { + u32 tag; + u32 status; + u32 reserved[13]; +}; + +/** + * brief the data structure of SAS Diagnostic Execute Response + * use to describe MPI SAS Diagnostic Execute Response (64 bytes) + * + */ +struct sas_diag_execute_resp { + u32 tag; + u32 cmdtype_cmddesc_phyid; + u32 Status; + u32 ReportData; + u32 reserved[11]; +} ; + +/** + * brief the data structure of Set Device State Response + * use to describe MPI Set Device State Response (64 bytes) + * + */ +struct set_dev_state_resp { + u32 tag; + u32 status; + u32 device_id; + u32 pds_nds; + u32 reserved[11]; +}; + +#define NDS_BITS 0x0F +#define PDS_BITS 0xF0 + +/* + * HW Events type + */ + +#define HW_EVENT_RESET_START 0x01 +#define HW_EVENT_CHIP_RESET_COMPLETE 0x02 +#define HW_EVENT_PHY_STOP_STATUS 0x03 +#define HW_EVENT_SAS_PHY_UP 0x04 +#define HW_EVENT_SATA_PHY_UP 0x05 +#define HW_EVENT_SATA_SPINUP_HOLD 0x06 +#define HW_EVENT_PHY_DOWN 0x07 +#define HW_EVENT_PORT_INVALID 0x08 +#define HW_EVENT_BROADCAST_CHANGE 0x09 +#define HW_EVENT_PHY_ERROR 0x0A +#define HW_EVENT_BROADCAST_SES 0x0B +#define HW_EVENT_INBOUND_CRC_ERROR 0x0C +#define HW_EVENT_HARD_RESET_RECEIVED 0x0D +#define HW_EVENT_MALFUNCTION 0x0E +#define HW_EVENT_ID_FRAME_TIMEOUT 0x0F +#define HW_EVENT_BROADCAST_EXP 0x10 +#define HW_EVENT_PHY_START_STATUS 0x11 +#define HW_EVENT_LINK_ERR_INVALID_DWORD 0x12 +#define HW_EVENT_LINK_ERR_DISPARITY_ERROR 0x13 +#define HW_EVENT_LINK_ERR_CODE_VIOLATION 0x14 +#define HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH 0x15 +#define HW_EVENT_LINK_ERR_PHY_RESET_FAILED 0x16 +#define HW_EVENT_PORT_RECOVERY_TIMER_TMO 0x17 +#define HW_EVENT_PORT_RECOVER 0x18 +#define HW_EVENT_PORT_RESET_TIMER_TMO 0x19 +#define HW_EVENT_PORT_RESET_COMPLETE 0x20 +#define EVENT_BROADCAST_ASYNCH_EVENT 0x21 + +/* port state */ +#define PORT_NOT_ESTABLISHED 0x00 +#define PORT_VALID 0x01 +#define PORT_LOSTCOMM 0x02 +#define PORT_IN_RESET 0x04 +#define PORT_INVALID 0x08 + +/* + * SSP/SMP/SATA IO Completion Status values + */ + +#define IO_SUCCESS 0x00 +#define IO_ABORTED 0x01 +#define IO_OVERFLOW 0x02 +#define IO_UNDERFLOW 0x03 +#define IO_FAILED 0x04 +#define IO_ABORT_RESET 0x05 +#define IO_NOT_VALID 0x06 +#define IO_NO_DEVICE 0x07 +#define IO_ILLEGAL_PARAMETER 0x08 +#define IO_LINK_FAILURE 0x09 +#define IO_PROG_ERROR 0x0A +#define IO_EDC_IN_ERROR 0x0B +#define IO_EDC_OUT_ERROR 0x0C +#define IO_ERROR_HW_TIMEOUT 0x0D +#define IO_XFER_ERROR_BREAK 0x0E +#define IO_XFER_ERROR_PHY_NOT_READY 0x0F +#define IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED 0x10 +#define IO_OPEN_CNX_ERROR_ZONE_VIOLATION 0x11 +#define IO_OPEN_CNX_ERROR_BREAK 0x12 +#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS 0x13 +#define IO_OPEN_CNX_ERROR_BAD_DESTINATION 0x14 +#define IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED 0x15 +#define IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY 0x16 +#define IO_OPEN_CNX_ERROR_WRONG_DESTINATION 0x17 +#define IO_OPEN_CNX_ERROR_UNKNOWN_ERROR 0x18 +#define IO_XFER_ERROR_NAK_RECEIVED 0x19 +#define IO_XFER_ERROR_ACK_NAK_TIMEOUT 0x1A +#define IO_XFER_ERROR_PEER_ABORTED 0x1B +#define IO_XFER_ERROR_RX_FRAME 0x1C +#define IO_XFER_ERROR_DMA 0x1D +#define IO_XFER_ERROR_CREDIT_TIMEOUT 0x1E +#define IO_XFER_ERROR_SATA_LINK_TIMEOUT 0x1F +#define IO_XFER_ERROR_SATA 0x20 +#define IO_XFER_ERROR_ABORTED_DUE_TO_SRST 0x22 +#define IO_XFER_ERROR_REJECTED_NCQ_MODE 0x21 +#define IO_XFER_ERROR_ABORTED_NCQ_MODE 0x23 +#define IO_XFER_OPEN_RETRY_TIMEOUT 0x24 +#define IO_XFER_SMP_RESP_CONNECTION_ERROR 0x25 +#define IO_XFER_ERROR_UNEXPECTED_PHASE 0x26 +#define IO_XFER_ERROR_XFER_RDY_OVERRUN 0x27 +#define IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED 0x28 + +#define IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT 0x30 +#define IO_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NAK 0x31 +#define IO_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK 0x32 + +#define IO_XFER_ERROR_OFFSET_MISMATCH 0x34 +#define IO_XFER_ERROR_XFER_ZERO_DATA_LEN 0x35 +#define IO_XFER_CMD_FRAME_ISSUED 0x36 +#define IO_ERROR_INTERNAL_SMP_RESOURCE 0x37 +#define IO_PORT_IN_RESET 0x38 +#define IO_DS_NON_OPERATIONAL 0x39 +#define IO_DS_IN_RECOVERY 0x3A +#define IO_TM_TAG_NOT_FOUND 0x3B +#define IO_XFER_PIO_SETUP_ERROR 0x3C +#define IO_SSP_EXT_IU_ZERO_LEN_ERROR 0x3D +#define IO_DS_IN_ERROR 0x3E +#define IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY 0x3F +#define IO_ABORT_IN_PROGRESS 0x40 +#define IO_ABORT_DELAYED 0x41 +#define IO_INVALID_LENGTH 0x42 + +/* WARNING: This error code must always be the last number. + * If you add error code, modify this code also + * It is used as an index + */ +#define IO_ERROR_UNKNOWN_GENERIC 0x43 + +/* MSGU CONFIGURATION TABLE*/ + +#define SPC_MSGU_CFG_TABLE_UPDATE 0x01/* Inbound doorbell bit0 */ +#define SPC_MSGU_CFG_TABLE_RESET 0x02/* Inbound doorbell bit1 */ +#define SPC_MSGU_CFG_TABLE_FREEZE 0x04/* Inbound doorbell bit2 */ +#define SPC_MSGU_CFG_TABLE_UNFREEZE 0x08/* Inbound doorbell bit4 */ +#define MSGU_IBDB_SET 0x04 +#define MSGU_HOST_INT_STATUS 0x08 +#define MSGU_HOST_INT_MASK 0x0C +#define MSGU_IOPIB_INT_STATUS 0x18 +#define MSGU_IOPIB_INT_MASK 0x1C +#define MSGU_IBDB_CLEAR 0x20/* RevB - Host not use */ +#define MSGU_MSGU_CONTROL 0x24 +#define MSGU_ODR 0x3C/* RevB */ +#define MSGU_ODCR 0x40/* RevB */ +#define MSGU_SCRATCH_PAD_0 0x44 +#define MSGU_SCRATCH_PAD_1 0x48 +#define MSGU_SCRATCH_PAD_2 0x4C +#define MSGU_SCRATCH_PAD_3 0x50 +#define MSGU_HOST_SCRATCH_PAD_0 0x54 +#define MSGU_HOST_SCRATCH_PAD_1 0x58 +#define MSGU_HOST_SCRATCH_PAD_2 0x5C +#define MSGU_HOST_SCRATCH_PAD_3 0x60 +#define MSGU_HOST_SCRATCH_PAD_4 0x64 +#define MSGU_HOST_SCRATCH_PAD_5 0x68 +#define MSGU_HOST_SCRATCH_PAD_6 0x6C +#define MSGU_HOST_SCRATCH_PAD_7 0x70 +#define MSGU_ODMR 0x74/* RevB */ + +/* bit definition for ODMR register */ +#define ODMR_MASK_ALL 0xFFFFFFFF/* mask all + interrupt vector */ +#define ODMR_CLEAR_ALL 0/* clear all + interrupt vector */ + /* bit definition for ODCR register */ +#define ODCR_CLEAR_ALL 0xFFFFFFFF /* mask all + interrupt vector*/ + + +/* state definition for Scratch Pad1 register */ +#define SCRATCH_PAD1_POR 0x00 /* power on reset state */ +#define SCRATCH_PAD1_SFR 0x01 /* soft reset state */ +#define SCRATCH_PAD1_ERR 0x02 /* error state */ +#define SCRATCH_PAD1_RDY 0x03 /* ready state */ +#define SCRATCH_PAD1_RST 0x04 /* soft reset toggle flag */ +#define SCRATCH_PAD1_AAP1RDY_RST 0x08 /* AAP1 ready for soft reset */ +#define SCRATCH_PAD1_STATE_MASK 0xFFFFFFF0 /* ScratchPad1 + Mask, bit1-0 State, bit2 Soft Reset, bit3 FW RDY for Soft Reset */ +#define SCRATCH_PAD1_RESERVED 0x000003F8 /* Scratch Pad1 + Reserved bit 3 to 9 */ + + /* state definition for Scratch Pad2 register */ +#define SCRATCH_PAD2_POR 0x00 /* power on state */ +#define SCRATCH_PAD2_SFR 0x01 /* soft reset state */ +#define SCRATCH_PAD2_ERR 0x02 /* error state */ +#define SCRATCH_PAD2_RDY 0x03 /* ready state */ +#define SCRATCH_PAD2_FWRDY_RST 0x04 /* FW ready for soft reset flag*/ +#define SCRATCH_PAD2_IOPRDY_RST 0x08 /* IOP ready for soft reset */ +#define SCRATCH_PAD2_STATE_MASK 0xFFFFFFF4 /* ScratchPad 2 + Mask, bit1-0 State */ +#define SCRATCH_PAD2_RESERVED 0x000003FC /* Scratch Pad1 + Reserved bit 2 to 9 */ + +#define SCRATCH_PAD_ERROR_MASK 0xFFFFFC00 /* Error mask bits */ +#define SCRATCH_PAD_STATE_MASK 0x00000003 /* State Mask bits */ + +/* main configuration offset - byte offset */ +#define MAIN_SIGNATURE_OFFSET 0x00/* DWORD 0x00 */ +#define MAIN_INTERFACE_REVISION 0x04/* DWORD 0x01 */ +#define MAIN_FW_REVISION 0x08/* DWORD 0x02 */ +#define MAIN_MAX_OUTSTANDING_IO_OFFSET 0x0C/* DWORD 0x03 */ +#define MAIN_MAX_SGL_OFFSET 0x10/* DWORD 0x04 */ +#define MAIN_CNTRL_CAP_OFFSET 0x14/* DWORD 0x05 */ +#define MAIN_GST_OFFSET 0x18/* DWORD 0x06 */ +#define MAIN_IBQ_OFFSET 0x1C/* DWORD 0x07 */ +#define MAIN_OBQ_OFFSET 0x20/* DWORD 0x08 */ +#define MAIN_IQNPPD_HPPD_OFFSET 0x24/* DWORD 0x09 */ +#define MAIN_OB_HW_EVENT_PID03_OFFSET 0x28/* DWORD 0x0A */ +#define MAIN_OB_HW_EVENT_PID47_OFFSET 0x2C/* DWORD 0x0B */ +#define MAIN_OB_NCQ_EVENT_PID03_OFFSET 0x30/* DWORD 0x0C */ +#define MAIN_OB_NCQ_EVENT_PID47_OFFSET 0x34/* DWORD 0x0D */ +#define MAIN_TITNX_EVENT_PID03_OFFSET 0x38/* DWORD 0x0E */ +#define MAIN_TITNX_EVENT_PID47_OFFSET 0x3C/* DWORD 0x0F */ +#define MAIN_OB_SSP_EVENT_PID03_OFFSET 0x40/* DWORD 0x10 */ +#define MAIN_OB_SSP_EVENT_PID47_OFFSET 0x44/* DWORD 0x11 */ +#define MAIN_OB_SMP_EVENT_PID03_OFFSET 0x48/* DWORD 0x12 */ +#define MAIN_OB_SMP_EVENT_PID47_OFFSET 0x4C/* DWORD 0x13 */ +#define MAIN_EVENT_LOG_ADDR_HI 0x50/* DWORD 0x14 */ +#define MAIN_EVENT_LOG_ADDR_LO 0x54/* DWORD 0x15 */ +#define MAIN_EVENT_LOG_BUFF_SIZE 0x58/* DWORD 0x16 */ +#define MAIN_EVENT_LOG_OPTION 0x5C/* DWORD 0x17 */ +#define MAIN_IOP_EVENT_LOG_ADDR_HI 0x60/* DWORD 0x18 */ +#define MAIN_IOP_EVENT_LOG_ADDR_LO 0x64/* DWORD 0x19 */ +#define MAIN_IOP_EVENT_LOG_BUFF_SIZE 0x68/* DWORD 0x1A */ +#define MAIN_IOP_EVENT_LOG_OPTION 0x6C/* DWORD 0x1B */ +#define MAIN_FATAL_ERROR_INTERRUPT 0x70/* DWORD 0x1C */ +#define MAIN_FATAL_ERROR_RDUMP0_OFFSET 0x74/* DWORD 0x1D */ +#define MAIN_FATAL_ERROR_RDUMP0_LENGTH 0x78/* DWORD 0x1E */ +#define MAIN_FATAL_ERROR_RDUMP1_OFFSET 0x7C/* DWORD 0x1F */ +#define MAIN_FATAL_ERROR_RDUMP1_LENGTH 0x80/* DWORD 0x20 */ +#define MAIN_HDA_FLAGS_OFFSET 0x84/* DWORD 0x21 */ +#define MAIN_ANALOG_SETUP_OFFSET 0x88/* DWORD 0x22 */ + +/* Gereral Status Table offset - byte offset */ +#define GST_GSTLEN_MPIS_OFFSET 0x00 +#define GST_IQ_FREEZE_STATE0_OFFSET 0x04 +#define GST_IQ_FREEZE_STATE1_OFFSET 0x08 +#define GST_MSGUTCNT_OFFSET 0x0C +#define GST_IOPTCNT_OFFSET 0x10 +#define GST_PHYSTATE_OFFSET 0x18 +#define GST_PHYSTATE0_OFFSET 0x18 +#define GST_PHYSTATE1_OFFSET 0x1C +#define GST_PHYSTATE2_OFFSET 0x20 +#define GST_PHYSTATE3_OFFSET 0x24 +#define GST_PHYSTATE4_OFFSET 0x28 +#define GST_PHYSTATE5_OFFSET 0x2C +#define GST_PHYSTATE6_OFFSET 0x30 +#define GST_PHYSTATE7_OFFSET 0x34 +#define GST_RERRINFO_OFFSET 0x44 + +/* General Status Table - MPI state */ +#define GST_MPI_STATE_UNINIT 0x00 +#define GST_MPI_STATE_INIT 0x01 +#define GST_MPI_STATE_TERMINATION 0x02 +#define GST_MPI_STATE_ERROR 0x03 +#define GST_MPI_STATE_MASK 0x07 + +#define MBIC_NMI_ENABLE_VPE0_IOP 0x000418 +#define MBIC_NMI_ENABLE_VPE0_AAP1 0x000418 +/* PCIE registers - BAR2(0x18), BAR1(win) 0x010000 */ +#define PCIE_EVENT_INTERRUPT_ENABLE 0x003040 +#define PCIE_EVENT_INTERRUPT 0x003044 +#define PCIE_ERROR_INTERRUPT_ENABLE 0x003048 +#define PCIE_ERROR_INTERRUPT 0x00304C +/* signature defintion for host scratch pad0 register */ +#define SPC_SOFT_RESET_SIGNATURE 0x252acbcd +/* Signature for Soft Reset */ + +/* SPC Reset register - BAR4(0x20), BAR2(win) (need dynamic mapping) */ +#define SPC_REG_RESET 0x000000/* reset register */ + +/* bit difination for SPC_RESET register */ +#define SPC_REG_RESET_OSSP 0x00000001 +#define SPC_REG_RESET_RAAE 0x00000002 +#define SPC_REG_RESET_PCS_SPBC 0x00000004 +#define SPC_REG_RESET_PCS_IOP_SS 0x00000008 +#define SPC_REG_RESET_PCS_AAP1_SS 0x00000010 +#define SPC_REG_RESET_PCS_AAP2_SS 0x00000020 +#define SPC_REG_RESET_PCS_LM 0x00000040 +#define SPC_REG_RESET_PCS 0x00000080 +#define SPC_REG_RESET_GSM 0x00000100 +#define SPC_REG_RESET_DDR2 0x00010000 +#define SPC_REG_RESET_BDMA_CORE 0x00020000 +#define SPC_REG_RESET_BDMA_SXCBI 0x00040000 +#define SPC_REG_RESET_PCIE_AL_SXCBI 0x00080000 +#define SPC_REG_RESET_PCIE_PWR 0x00100000 +#define SPC_REG_RESET_PCIE_SFT 0x00200000 +#define SPC_REG_RESET_PCS_SXCBI 0x00400000 +#define SPC_REG_RESET_LMS_SXCBI 0x00800000 +#define SPC_REG_RESET_PMIC_SXCBI 0x01000000 +#define SPC_REG_RESET_PMIC_CORE 0x02000000 +#define SPC_REG_RESET_PCIE_PC_SXCBI 0x04000000 +#define SPC_REG_RESET_DEVICE 0x80000000 + +/* registers for BAR Shifting - BAR2(0x18), BAR1(win) */ +#define SPC_IBW_AXI_TRANSLATION_LOW 0x003258 + +#define MBIC_AAP1_ADDR_BASE 0x060000 +#define MBIC_IOP_ADDR_BASE 0x070000 +#define GSM_ADDR_BASE 0x0700000 +/* Dynamic map through Bar4 - 0x00700000 */ +#define GSM_CONFIG_RESET 0x00000000 +#define RAM_ECC_DB_ERR 0x00000018 +#define GSM_READ_ADDR_PARITY_INDIC 0x00000058 +#define GSM_WRITE_ADDR_PARITY_INDIC 0x00000060 +#define GSM_WRITE_DATA_PARITY_INDIC 0x00000068 +#define GSM_READ_ADDR_PARITY_CHECK 0x00000038 +#define GSM_WRITE_ADDR_PARITY_CHECK 0x00000040 +#define GSM_WRITE_DATA_PARITY_CHECK 0x00000048 + +#define RB6_ACCESS_REG 0x6A0000 +#define HDAC_EXEC_CMD 0x0002 +#define HDA_C_PA 0xcb +#define HDA_SEQ_ID_BITS 0x00ff0000 +#define HDA_GSM_OFFSET_BITS 0x00FFFFFF +#define MBIC_AAP1_ADDR_BASE 0x060000 +#define MBIC_IOP_ADDR_BASE 0x070000 +#define GSM_ADDR_BASE 0x0700000 +#define SPC_TOP_LEVEL_ADDR_BASE 0x000000 +#define GSM_CONFIG_RESET_VALUE 0x00003b00 + +/* RB6 offset */ +#define SPC_RB6_OFFSET 0x80C0 +/* Magic number of soft reset for RB6 */ +#define RB6_MAGIC_NUMBER_RST 0x1234 + +/* Device Register status */ +#define DEVREG_SUCCESS 0x00 +#define DEVREG_FAILURE_OUT_OF_RESOURCE 0x01 +#define DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED 0x02 +#define DEVREG_FAILURE_INVALID_PHY_ID 0x03 +#define DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED 0x04 +#define DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE 0x05 +#define DEVREG_FAILURE_PORT_NOT_VALID_STATE 0x06 +#define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID 0x07 + +#endif + diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c new file mode 100644 index 0000000..03cd9eb --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -0,0 +1,737 @@ +/* + * 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 "pm8001_sas.h" + +static struct scsi_transport_template *pm8001_stt; + +static const struct pm8001_chip_info pm8001_chips[] = { + [chip_8001] = { 8, &pm8001_8001_dispatch,}, +}; +static int pm8001_id; + +LIST_HEAD(hba_list); + +/** + * The main structure which LLDD must register for scsi core. + */ +static struct scsi_host_template pm8001_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .queuecommand = sas_queuecommand, + .target_alloc = sas_target_alloc, + .slave_configure = pm8001_slave_configure, + .slave_destroy = sas_slave_destroy, + .scan_finished = pm8001_scan_finished, + .scan_start = pm8001_scan_start, + .change_queue_depth = sas_change_queue_depth, + .change_queue_type = sas_change_queue_type, + .bios_param = sas_bios_param, + .can_queue = 1, + .cmd_per_lun = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .max_sectors = SCSI_DEFAULT_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = sas_eh_device_reset_handler, + .eh_bus_reset_handler = sas_eh_bus_reset_handler, + .slave_alloc = pm8001_slave_alloc, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, + .shost_attrs = pm8001_host_attrs, +}; + +/** + * Sas layer call this function to execute specific task. + */ +static struct sas_domain_function_template pm8001_transport_ops = { + .lldd_dev_found = pm8001_dev_found, + .lldd_dev_gone = pm8001_dev_gone, + + .lldd_execute_task = pm8001_queue_command, + .lldd_control_phy = pm8001_phy_control, + + .lldd_abort_task = pm8001_abort_task, + .lldd_abort_task_set = pm8001_abort_task_set, + .lldd_clear_aca = pm8001_clear_aca, + .lldd_clear_task_set = pm8001_clear_task_set, + .lldd_I_T_nexus_reset = pm8001_I_T_nexus_reset, + .lldd_lu_reset = pm8001_lu_reset, + .lldd_query_task = pm8001_query_task, +}; + +/** + *pm8001_phy_init - initiate our adapter phys + *@pm8001_ha: our hba structure. + *@phy_id: phy id. + */ +static void __devinit pm8001_phy_init(struct pm8001_hba_info *pm8001_ha, + int phy_id) +{ + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + phy->phy_state = 0; + phy->pm8001_ha = pm8001_ha; + init_timer(&phy->timer); + sas_phy->enabled = (phy_id < pm8001_ha->chip->n_phy) ? 1 : 0; + sas_phy->class = SAS; + sas_phy->iproto = SAS_PROTOCOL_ALL; + sas_phy->tproto = 0; + sas_phy->type = PHY_TYPE_PHYSICAL; + sas_phy->role = PHY_ROLE_INITIATOR; + sas_phy->oob_mode = OOB_NOT_CONNECTED; + sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; + sas_phy->id = phy_id; + sas_phy->sas_addr = &pm8001_ha->sas_addr[0]; + sas_phy->frame_rcvd = &phy->frame_rcvd[0]; + sas_phy->ha = (struct sas_ha_struct *)pm8001_ha->shost->hostdata; + sas_phy->lldd_phy = phy; +} + +/** + *pm8001_free - free hba + *@pm8001_ha: our hba structure. + * + */ +static void pm8001_free(struct pm8001_hba_info *pm8001_ha) +{ + int i; + + if (!pm8001_ha) + return; + + for (i = 0; i < USI_MAX_MEMCNT; i++) { + if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) { + pci_free_consistent(pm8001_ha->pdev, + pm8001_ha->memoryMap.region[i].element_size, + pm8001_ha->memoryMap.region[i].virt_ptr, + pm8001_ha->memoryMap.region[i].phys_addr); + } + } + PM8001_CHIP_DISP->chip_iounmap(pm8001_ha); + if (pm8001_ha->shost) + scsi_host_put(pm8001_ha->shost); + kfree(pm8001_ha); +} + +#ifdef PM8001_USE_TASKLET +static void pm8001_tasklet(unsigned long opaque) +{ + struct pm8001_hba_info *pm8001_ha; + pm8001_ha = (struct pm8001_hba_info *)opaque;; + if (unlikely(!pm8001_ha)) + BUG_ON(1); + PM8001_CHIP_DISP->isr(pm8001_ha, pm8001_ha->irq); +} +#endif + + /** + * pm8001_interrupt - when HBA originate a interrupt,we should invoke this + * dispatcher to handle each case. + * @irq: irq number. + * @opaque: the passed general host adapter struct + */ +static irqreturn_t pm8001_interrupt(int irq, void *opaque) +{ + struct pm8001_hba_info *pm8001_ha; + irqreturn_t ret = IRQ_HANDLED; + struct sas_ha_struct *sha = opaque; + pm8001_ha = sha->lldd_ha; + if (unlikely(!pm8001_ha)) + return IRQ_NONE; +#ifdef PM8001_USE_TASKLET + tasklet_schedule(&pm8001_ha->tasklet); +#else + spin_lock(&pm8001_ha->lock); + ret = PM8001_CHIP_DISP->isr(pm8001_ha, irq); + spin_unlock(&pm8001_ha->lock); +#endif + return ret; +} + +/** + * pm8001_alloc - initiate our hba structure and 6 DMAs area. + * @pm8001_ha:our hba structure. + * + */ +static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha) +{ + int i; + spin_lock_init(&pm8001_ha->lock); + for (i = 0; i < pm8001_ha->chip->n_phy; i++) + pm8001_phy_init(pm8001_ha, i); + + for (i = 0; i < PM8001_MAX_DEVICES; i++) { + pm8001_ha->devices[i].dev_type = NO_DEVICE; + pm8001_ha->devices[i].id = i; + pm8001_ha->devices[i].device_id = PM8001_MAX_DEVICES; + pm8001_ha->devices[i].runing_req = 0; + } + /* MPI Memory region 1 for AAP Event Log for fw */ + pm8001_ha->memoryMap.region[AAP1].num_elements = 1; + pm8001_ha->memoryMap.region[AAP1].element_size = 128*1024; + pm8001_ha->memoryMap.region[AAP1].total_len = 128*1024; + + /* MPI Memory region 2 for IOP Event Log for fw */ + pm8001_ha->memoryMap.region[IOP].num_elements = 1; + pm8001_ha->memoryMap.region[IOP].element_size = 128*1024; + pm8001_ha->memoryMap.region[IOP].total_len = 128*1024; + + /* MPI Memory region 3 for consumer Index of inbound queues */ + pm8001_ha->memoryMap.region[CI].num_elements = 1; + pm8001_ha->memoryMap.region[CI].element_size = 4; + pm8001_ha->memoryMap.region[CI].total_len = 4; + + /* MPI Memory region 4 for producer Index of outbound queues */ + pm8001_ha->memoryMap.region[PI].num_elements = 1; + pm8001_ha->memoryMap.region[PI].element_size = 4; + pm8001_ha->memoryMap.region[PI].total_len = 4; + + /* MPI Memory region 5 inbound queues */ + pm8001_ha->memoryMap.region[IB].num_elements = 256; + pm8001_ha->memoryMap.region[IB].element_size = 64; + pm8001_ha->memoryMap.region[IB].total_len = 256*64; + + /* MPI Memory region 6 inbound queues */ + pm8001_ha->memoryMap.region[OB].num_elements = 256; + pm8001_ha->memoryMap.region[OB].element_size = 64; + pm8001_ha->memoryMap.region[OB].total_len = 256*64; + + /* Memory region write DMA*/ + pm8001_ha->memoryMap.region[NVMD].num_elements = 1; + pm8001_ha->memoryMap.region[NVMD].element_size = 4096; + pm8001_ha->memoryMap.region[NVMD].total_len = 4096; + + for (i = 0; i < USI_MAX_MEMCNT; i++) { + if (pm8001_mem_alloc(pm8001_ha->pdev, + &pm8001_ha->memoryMap.region[i].virt_ptr, + &pm8001_ha->memoryMap.region[i].phys_addr, + &pm8001_ha->memoryMap.region[i].phys_addr_hi, + &pm8001_ha->memoryMap.region[i].phys_addr_lo, + pm8001_ha->memoryMap.region[i].total_len) != 0) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Mem allocate failure %d \n", i)); + goto err_out; + } + } + for (i = 0; i < PM8001_MAX_CCB; i++) + ++pm8001_ha->tags_num; + pm8001_ha->flags = PM8001F_INIT_TIME; + /* Initialize tags */ + pm8001_tag_init(pm8001_ha); + return 0; +err_out: + return 1; +} + +/** + * pm8001_ioremap - remap the pci high physical address to kernal virtual + * address so that we can access them. + * @pm8001_ha:our hba structure. + */ + +static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha) +{ + u32 bar; + u32 logicalBar = 0; + struct pci_dev *pdev; + + pdev = pm8001_ha->pdev; + /* map pci mem (PMC pci base 0-3)*/ + for (bar = 0; bar < 6; bar++) { + /* + ** logical BARs for SPC: + ** bar 0 and 1 - logical BAR0 + ** bar 2 and 3 - logical BAR1 + ** bar4 - logical BAR2 + ** bar5 - logical BAR3 + ** Skip the appropriate assignments: + */ + if ((bar == 1) || (bar == 3)) + continue; + if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { + pm8001_ha->io_mem[logicalBar].membase = + pci_resource_start(pdev, bar); + pm8001_ha->io_mem[logicalBar].membase &= + (u32)PCI_BASE_ADDRESS_MEM_MASK; + pm8001_ha->io_mem[logicalBar].memsize = + pci_resource_len(pdev, bar); + pm8001_ha->io_mem[logicalBar].memvirtaddr = + ioremap(pm8001_ha->io_mem[logicalBar].membase, + pm8001_ha->io_mem[logicalBar].memsize); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PCI: bar %d,logicalBar %d " + "virt_addr=%lx,len=%d\n", bar, logicalBar, + (unsigned long) + pm8001_ha->io_mem[logicalBar].memvirtaddr, + pm8001_ha->io_mem[logicalBar].memsize)); + } else { + pm8001_ha->io_mem[logicalBar].membase = 0; + pm8001_ha->io_mem[logicalBar].memsize = 0; + pm8001_ha->io_mem[logicalBar].memvirtaddr = 0; + } + logicalBar++; + } + return 0; +} + +/** + * pm8001_pci_alloc - initialize our ha card structure, note it is a field in + * sas_ha_struct which is also a field of sturct scsi_host. + * @pdev: pci device. + * @ent: ent + * @shost: scsi host struct which has been initialized before. + */ +static struct pm8001_hba_info *__devinit +pm8001_pci_alloc(struct pci_dev *pdev, u32 chip_id, struct Scsi_Host *shost) +{ + struct pm8001_hba_info *pm8001_ha; + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + pm8001_ha = sha->lldd_ha; + if (!pm8001_ha) + return NULL; + + pm8001_ha->pdev = pdev; + pm8001_ha->dev = &pdev->dev; + pm8001_ha->chip_id = chip_id; + pm8001_ha->chip = &pm8001_chips[pm8001_ha->chip_id]; + pm8001_ha->irq = pdev->irq; + pm8001_ha->sas = sha; + pm8001_ha->shost = shost; + pm8001_ha->id = pm8001_id++; + pm8001_ha->logging_level = 0x1; + sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id); +#ifdef PM8001_USE_TASKLET + tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, + (unsigned long)pm8001_ha); +#endif + pm8001_ioremap(pm8001_ha); + if (!pm8001_alloc(pm8001_ha)) + return pm8001_ha; + pm8001_free(pm8001_ha); + return NULL; +} + +/** + * pci_go_44 - pm8001 specified, its DMA is 44 bit rather than 64 bit + * @pdev: pci device. + */ +static int pci_go_44(struct pci_dev *pdev) +{ + int rc; + + if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(44))) { + rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(44)); + if (rc) { + rc = pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(32)); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "44-bit DMA enable failed\n"); + return rc; + } + } + } else { + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + return rc; + } + rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + return rc; + } + } + return rc; +} + +/** + * pm8001_prep_sas_ha_init - allocate sas_phy and sas_port struct in general + * hba struct,allocate our special hba struct in general hba struct. + * @shost: scsi host which has been allocated outside. + * @chip_info: our ha struct. + */ +static int __devinit pm8001_prep_sas_ha_init(struct Scsi_Host * shost, + const struct pm8001_chip_info *chip_info) +{ + int phy_nr, port_nr; + struct asd_sas_phy **arr_phy; + struct asd_sas_port **arr_port; + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + phy_nr = chip_info->n_phy; + port_nr = phy_nr; + memset(sha, 0x00, sizeof(*sha)); + arr_phy = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL); + if (!arr_phy) + goto exit; + arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL); + if (!arr_port) + goto exit_free2; + + sha->sas_phy = arr_phy; + sha->sas_port = arr_port; + sha->lldd_ha = kzalloc(sizeof(struct pm8001_hba_info), GFP_KERNEL); + if (!sha->lldd_ha) + goto exit_free1; + + shost->transportt = pm8001_stt; + shost->max_id = PM8001_MAX_DEVICES; + shost->max_lun = 8; + shost->max_channel = 0; + shost->unique_id = pm8001_id; + shost->max_cmd_len = 16; + shost->can_queue = PM8001_CAN_QUEUE; + shost->cmd_per_lun = 32; + return 0; +exit_free1: + kfree(arr_port); +exit_free2: + kfree(arr_phy); +exit: + return -1; +} + +/** + * pm8001_post_sas_ha_init - initialize general hba struct which is defined + * in libsas + * @shost: scsi host which has been allocated outside + * @chip_info: our ha struct. + */ +static void __devinit pm8001_post_sas_ha_init(struct Scsi_Host *shost, + const struct pm8001_chip_info *chip_info) +{ + int i = 0; + struct pm8001_hba_info *pm8001_ha; + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + pm8001_ha = sha->lldd_ha; + for (i = 0; i < chip_info->n_phy; i++) { + sha->sas_phy[i] = &pm8001_ha->phy[i].sas_phy; + sha->sas_port[i] = &pm8001_ha->port[i].sas_port; + } + sha->sas_ha_name = DRV_NAME; + sha->dev = pm8001_ha->dev; + + sha->lldd_module = THIS_MODULE; + sha->sas_addr = &pm8001_ha->sas_addr[0]; + sha->num_phys = chip_info->n_phy; + sha->lldd_max_execute_num = 1; + sha->lldd_queue_size = PM8001_CAN_QUEUE; + sha->core.shost = shost; +} + +/** + * pm8001_init_sas_add - initialize sas address, currently we just set the + * fixed SAS address to our HBA,for manufacture, it should read from the EEPROM + * @chip_info: our ha struct. + */ +static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) +{ + u8 i; +#ifdef PM8001_READ_VPD + DECLARE_COMPLETION_ONSTACK(completion); + pm8001_ha->nvmd_completion = &completion; + PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, 0, 0); + wait_for_completion(&completion); + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + memcpy(&pm8001_ha->phy[i].dev_sas_addr, pm8001_ha->sas_addr, + SAS_ADDR_SIZE); + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("phy %d sas_addr = %x \n", i, + (u64)pm8001_ha->phy[i].dev_sas_addr)); + } +#else + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + pm8001_ha->phy[i].dev_sas_addr = 0x500e004010000000ULL; + pm8001_ha->phy[i].dev_sas_addr = + cpu_to_be64((u64) + (*(u64 *)&pm8001_ha->phy[i].dev_sas_addr)); + } + memcpy(pm8001_ha->sas_addr, &pm8001_ha->phy[0].dev_sas_addr, + SAS_ADDR_SIZE); +#endif +} + +#ifdef PM8001_USE_MSIX +/** + * pm8001_setup_msix - enable MSI-X interrupt + * @chip_info: our ha struct. + * @irq_handler: irq_handler + */ +static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha, + irq_handler_t irq_handler) +{ + u32 i = 0, j = 0; + u32 number_of_intr = 16; + int flag = 0; + u32 max_entry; + int rc; + max_entry = sizeof(pm8001_ha->msix_entries) / + sizeof(pm8001_ha->msix_entries[0]); + flag |= IRQF_DISABLED; + for (i = 0; i < max_entry ; i++) + pm8001_ha->msix_entries[i].entry = i; + rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries, + number_of_intr); + pm8001_ha->number_of_intr = number_of_intr; + if (!rc) { + for (i = 0; i < number_of_intr; i++) { + if (request_irq(pm8001_ha->msix_entries[i].vector, + irq_handler, flag, DRV_NAME, + SHOST_TO_SAS_HA(pm8001_ha->shost))) { + for (j = 0; j < i; j++) + free_irq( + pm8001_ha->msix_entries[j].vector, + SHOST_TO_SAS_HA(pm8001_ha->shost)); + pci_disable_msix(pm8001_ha->pdev); + break; + } + } + } + return rc; +} +#endif + +/** + * pm8001_request_irq - register interrupt + * @chip_info: our ha struct. + */ +static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) +{ + struct pci_dev *pdev; + irq_handler_t irq_handler = pm8001_interrupt; + u32 rc; + + pdev = pm8001_ha->pdev; + +#ifdef PM8001_USE_MSIX + if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) + return pm8001_setup_msix(pm8001_ha, irq_handler); + else + goto intx; +#endif + +intx: + /* + ** intialize the INT-X interrupt + */ + rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, + SHOST_TO_SAS_HA(pm8001_ha->shost)); + return rc; +} + +/** + * pm8001_pci_probe - This function is the main initialization function, + * when register a new pci driver it is invoked, all struct an hardware + * initilization should be done here, also, register interrupt + * @pdev: pci device which kernel has been prepared for. + * @ ent: ent + */ +static int __devinit pm8001_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + unsigned int rc; + struct pm8001_hba_info *pm8001_ha; + struct Scsi_Host *shost = NULL; + const struct pm8001_chip_info *chip; + + dev_printk(KERN_INFO, &pdev->dev, + "pm8001: driver version %s\n", DRV_VERSION); + rc = pci_enable_device(pdev); + if (rc) + goto err_out_enable; + pci_set_master(pdev); + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out_disable; + rc = pci_go_44(pdev); + if (rc) + goto err_out_regions; + shost = scsi_host_alloc(&pm8001_sht, sizeof(void *)); + if (!shost) { + rc = -ENOMEM; + goto err_out_regions; + } + chip = &pm8001_chips[ent->driver_data]; + SHOST_TO_SAS_HA(shost) = + kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL); + if (!SHOST_TO_SAS_HA(shost)) { + rc = -ENOMEM; + goto err_out_free_host; + } + + rc = pm8001_prep_sas_ha_init(shost, chip); + if (rc) { + rc = -ENOMEM; + goto err_out_free; + } + pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost)); + pm8001_ha = pm8001_pci_alloc(pdev, chip_8001, shost); + if (!pm8001_ha) { + rc = -ENOMEM; + goto err_out_free; + } + list_add_tail(&pm8001_ha->list, &hba_list); + rc = PM8001_CHIP_DISP->chip_init(pm8001_ha); + if (rc) + goto err_out_ha_free; + + rc = scsi_add_host(shost, &pdev->dev); + if (rc) + goto err_out_ha_free; + rc = pm8001_request_irq(pm8001_ha); + if (rc) + goto err_out_shost; + + PM8001_CHIP_DISP->interrupt_enable(pm8001_ha); + pm8001_init_sas_add(pm8001_ha); + pm8001_post_sas_ha_init(shost, chip); + rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); + if (rc) + goto err_out_shost; + scsi_scan_host(pm8001_ha->shost); + return 0; + +err_out_shost: + scsi_remove_host(pm8001_ha->shost); +err_out_ha_free: + pm8001_free(pm8001_ha); +err_out_free: + kfree(SHOST_TO_SAS_HA(shost)); +err_out_free_host: + kfree(shost); +err_out_regions: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_enable: + return rc; +} +static void __devexit pm8001_pci_remove(struct pci_dev *pdev) +{ + struct sas_ha_struct *sha = pci_get_drvdata(pdev); + struct pm8001_hba_info *pm8001_ha = NULL; + int i; + pm8001_ha = sha->lldd_ha; + /*scsi_block_requests(pm8001_ha->shost);*/ + /*flush_scheduled_work();*/ + pci_set_drvdata(pdev, NULL); + sas_unregister_ha(sha); + sas_remove_host(pm8001_ha->shost); + list_del(&pm8001_ha->list); + scsi_remove_host(pm8001_ha->shost); + PM8001_CHIP_DISP->interrupt_disable(pm8001_ha); + PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); +#ifdef PM8001_USE_MSIX + for (i = 0; i < pm8001_ha->number_of_intr; i++) + synchronize_irq(pm8001_ha->msix_entries[i].vector); + for (i = 0; i < pm8001_ha->number_of_intr; i++) + free_irq(pm8001_ha->msix_entries[i].vector, sha); + pci_disable_msix(pdev); +#else + free_irq(pm8001_ha->irq, sha); +#endif +#ifdef PM8001_USE_TASKLET + tasklet_kill(&pm8001_ha->tasklet); +#endif + pm8001_free(pm8001_ha); + kfree(sha->sas_phy); + kfree(sha->sas_port); + kfree(sha); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static struct pci_device_id __devinitdata pm8001_pci_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_PMC_Sierra, PCI_DEVICE_ID_8001), + .driver_data = chip_8001 + }, + { } /* terminate list */ +}; + +static struct pci_driver pm8001_pci_driver = { + .name = DRV_NAME, + .id_table = pm8001_pci_table, + .probe = pm8001_pci_probe, + .remove = __devexit_p(pm8001_pci_remove), +}; + +/** + * pm8001_init - initialize scsi transport template + */ +static int __init pm8001_init(void) +{ + int rc; + pm8001_id = 0; + pm8001_stt = sas_domain_attach_transport(&pm8001_transport_ops); + if (!pm8001_stt) + return -ENOMEM; + pm8001_ctl_init(); + rc = pci_register_driver(&pm8001_pci_driver); + if (rc) + goto err_out; + return 0; +err_out: + sas_release_transport(pm8001_stt); + return rc; +} + +static void __exit pm8001_exit(void) +{ + pci_unregister_driver(&pm8001_pci_driver); + sas_release_transport(pm8001_stt); + pm8001_ctl_exit(); +} + +module_init(pm8001_init); +module_exit(pm8001_exit); + +MODULE_AUTHOR("Jack Wang <jack_wang@xxxxxxxxx>"); +MODULE_DESCRIPTION("PMC-Sierra PM8001 SAS/SATA controller driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, pm8001_pci_table); + diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c new file mode 100644 index 0000000..ae2b19c --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -0,0 +1,1090 @@ +/* + * 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 "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 = (void *)&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 = (void *)&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 = (void *)&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 memery 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: memery size. + */ +int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr, + u32 *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo, u32 mem_size) +{ + caddr_t mem_virt_alloc; + dma_addr_t mem_dma_handle; + mem_virt_alloc = pci_alloc_consistent(pdev, mem_size, &mem_dma_handle); + if (!mem_virt_alloc) { + pm8001_printk("memory allocation error\n"); + return -1; + } + memset((void *)mem_virt_alloc, 0, mem_size); + *virt_addr = (void *)(mem_virt_alloc); + *pphys_addr = mem_dma_handle; + *pphys_addr_hi = HIGH_32_BITS(mem_dma_handle); + *pphys_addr_lo = LOW_32_BITS(mem_dma_handle); + 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, 0, phy_id); + wait_for_completion(&completion); + } + PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, 0, + 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, 0, phy_id); + wait_for_completion(&completion); + } + PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, 0, + 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, 0, phy_id); + wait_for_completion(&completion); + } + PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, 0, + phy_id, PHY_LINK_RESET); + break; + case PHY_FUNC_RELEASE_SPINUP_HOLD: + PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, 0, + phy_id, PHY_LINK_RESET); + break; + case PHY_FUNC_DISABLE: + PM8001_CHIP_DISP->phy_stop_req(pm8001_ha, 0, 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 - this is for scsi core invoking, for our device, first + * of all, we should enable all HBA phys by send 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; + for (i = 0; i < pm8001_ha->chip->n_phy; ++i) + PM8001_CHIP_DISP->phy_start_req(pm8001_ha, 0, i); +} + +int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time) +{ + /* give the phy enabling interrupt event time to come in (1s + * is empirically about all it takes) */ + if (time < HZ) + return 0; + /* Wait for discovery to finish */ + scsi_flush_work(shost); + return 1; +} + +/** + * pm8001_task_prep_smp - the dispatcher function, prepare data for smp task + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to smp task + */ +static int pm8001_task_prep_smp(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + return PM8001_CHIP_DISP->smp_req(pm8001_ha, ccb); +} + +u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag) +{ + struct ata_queued_cmd *qc = task->uldd_task; + if (qc) { + if (qc->tf.command == ATA_CMD_FPDMA_WRITE || + qc->tf.command == ATA_CMD_FPDMA_READ) { + *tag = qc->tag; + return 1; + } + } + return 0; +} + +/** + * pm8001_task_prep_ata - the dispatcher function, prepare data for sata task + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to sata task + */ +static int pm8001_task_prep_ata(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + return PM8001_CHIP_DISP->sata_req(pm8001_ha, ccb); +} + +/** + * pm8001_task_prep_ssp_tm - the dispatcher function, prepare task management data + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to TM + * @tmf: the task management IU + */ +static int pm8001_task_prep_ssp_tm(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf) +{ + return PM8001_CHIP_DISP->ssp_tm_req(pm8001_ha, ccb, tmf); +} + +/** + * pm8001_task_prep_ssp - the dispatcher function,prepare ssp data for ssp task + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to ssp task + */ +static int pm8001_task_prep_ssp(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb) +{ + return PM8001_CHIP_DISP->ssp_io_req(pm8001_ha, ccb); +} +int pm8001_slave_configure(struct scsi_device *sdev) +{ + struct domain_device *dev = sdev_to_domain_dev(sdev); + int ret = sas_slave_configure(sdev); + if (ret) + return ret; + if (dev_is_sata(dev)) { + #ifdef PM8001_DISABLE_NCQ + struct ata_port *ap = dev->sata_dev.ap; + struct ata_device *adev = ap->link.device; + adev->flags |= ATA_DFLAG_NCQ_OFF; + scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1); + #endif + } + return 0; +} +/** + * pm8001_task_exec -execute the task which come from upper level, send the + * command or data to DMA area and then increase CI,for queuecommand(ssp), + * it is from upper layer and for smp command,it is from libsas, + * for ata command it is from libata. + * @task: the task to be execute. + * @num: if can_queue great than 1, the task can be queued up. for SMP task, + * we always execute one one time. + * @gfp_flags: gfp_flags. + * @is tmf: if it is task management task. + * @tmf: the task management IU + */ +#define DEV_IS_GONE(pm8001_dev) \ + ((!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE))) +static int pm8001_task_exec(struct sas_task *task, const int num, + gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf) +{ + struct domain_device *dev = task->dev; + struct pm8001_hba_info *pm8001_ha; + struct pm8001_device *pm8001_dev; + struct sas_task *t = task; + struct pm8001_ccb_info *ccb; + u32 tag = 0xdeadbeef, rc, n_elem = 0; + u32 n = num; + unsigned long flags = 0; + + if (!dev->port) { + struct task_status_struct *tsm = &t->task_status; + tsm->resp = SAS_TASK_UNDELIVERED; + tsm->stat = SAS_PHY_DOWN; + t->task_done(t); + return 0; + } + pm8001_ha = pm8001_find_ha_by_dev(task->dev); + PM8001_IO_DBG(pm8001_ha, pm8001_printk("pm8001_task_exec device \n ")); + spin_lock_irqsave(&pm8001_ha->lock, flags); + do { + dev = t->dev; + pm8001_dev = dev->lldd_dev; + if (DEV_IS_GONE(pm8001_dev)) { + if (pm8001_dev) { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("device %d not ready.\n", + pm8001_dev->device_id)); + } else { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("device %016llx not " + "ready.\n", SAS_ADDR(dev->sas_addr))); + } + rc = SAS_PHY_DOWN; + goto out_done; + } + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + goto err_out; + ccb = &pm8001_ha->ccb_info[tag]; + + if (!sas_protocol_ata(t->task_proto)) { + if (t->num_scatter) { + n_elem = dma_map_sg(pm8001_ha->dev, + t->scatter, + t->num_scatter, + t->data_dir); + if (!n_elem) { + rc = -ENOMEM; + goto err_out; + } + } + } else { + n_elem = t->num_scatter; + } + + t->lldd_task = NULL; + ccb->n_elem = n_elem; + ccb->ccb_tag = tag; + ccb->task = t; + ccb->n_elem = n_elem; + switch (t->task_proto) { + case SAS_PROTOCOL_SMP: + rc = pm8001_task_prep_smp(pm8001_ha, ccb); + break; + case SAS_PROTOCOL_SSP: + if (is_tmf) + rc = pm8001_task_prep_ssp_tm(pm8001_ha, + ccb, tmf); + else + rc = pm8001_task_prep_ssp(pm8001_ha, ccb); + break; + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: + rc = pm8001_task_prep_ata(pm8001_ha, ccb); + break; + default: + dev_printk(KERN_ERR, pm8001_ha->dev, + "unknown sas_task proto: 0x%x\n", + t->task_proto); + rc = -EINVAL; + break; + } + + if (rc) { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("rc is %x\n", rc)); + goto err_out_tag; + } + t->lldd_task = ccb; + /* TODO: select normal or high priority */ + spin_lock(&t->task_state_lock); + t->task_state_flags |= SAS_TASK_AT_INITIATOR; + spin_unlock(&t->task_state_lock); + pm8001_dev->runing_req++; + if (n > 1) + t = list_entry(t->list.next, struct sas_task, list); + } while (--n); + rc = 0; + goto out_done; + +err_out_tag: + pm8001_tag_free(pm8001_ha, tag); +err_out: + dev_printk(KERN_ERR, pm8001_ha->dev, "pm8001 exec failed[%d]!\n", rc); + if (!sas_protocol_ata(t->task_proto)) + if (n_elem) + dma_unmap_sg(pm8001_ha->dev, t->scatter, n_elem, + t->data_dir); +out_done: + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return rc; +} + +/** + * pm8001_queue_command - register for upper layer used, all IO commands sent + * to HBA are from this interface. + * @task: the task to be execute. + * @num: if can_queue great than 1, the task can be queued up. for SMP task, + * we always execute one one time + * @gfp_flags: gfp_flags + */ +int pm8001_queue_command(struct sas_task *task, const int num, + gfp_t gfp_flags) +{ + return pm8001_task_exec(task, num, gfp_flags, 0, NULL); +} + +void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx) +{ + pm8001_tag_clear(pm8001_ha, ccb_idx); +} + +/** + * pm8001_ccb_task_free - free the sg for ssp and smp command, free the ccb. + * @pm8001_ha: our hba card information + * @ccb: the ccb which attached to ssp task + * @task: the task to be free. + * @ccb_idx: ccb index. + */ +void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha, + struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx) +{ + if (!ccb->task) + return; + if (!sas_protocol_ata(task->task_proto)) + if (ccb->n_elem) + dma_unmap_sg(pm8001_ha->dev, task->scatter, + ccb->n_elem, task->data_dir); + + switch (task->task_proto) { + case SAS_PROTOCOL_SMP: + dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_resp, 1, + PCI_DMA_FROMDEVICE); + dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_req, 1, + PCI_DMA_TODEVICE); + break; + + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SSP: + default: + /* do nothing */ + break; + } + task->lldd_task = NULL; + ccb->task = NULL; + ccb->ccb_tag = 0xFFFFFFFF; + pm8001_ccb_free(pm8001_ha, ccb_idx); +} + + /** + * pm8001_alloc_dev - find the empty pm8001_device structure, allocate and + * return it for use. + * @pm8001_ha: our hba card information + */ +struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha) +{ + u32 dev; + for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) { + if (pm8001_ha->devices[dev].dev_type == NO_DEVICE) { + pm8001_ha->devices[dev].id = dev; + return &pm8001_ha->devices[dev]; + } + } + if (dev == PM8001_MAX_DEVICES) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("max support %d devices, ignore ..\n", + PM8001_MAX_DEVICES)); + } + return NULL; +} + +static void pm8001_free_dev(struct pm8001_device *pm8001_dev) +{ + u32 id = pm8001_dev->id; + memset(pm8001_dev, 0, sizeof(*pm8001_dev)); + pm8001_dev->id = id; + pm8001_dev->dev_type = NO_DEVICE; + pm8001_dev->device_id = PM8001_MAX_DEVICES; + pm8001_dev->sas_device = NULL; +} + +/** + * pm8001_dev_found_notify - when libsas find a sas domain device, it should + * tell the LLDD that device is found, and then LLDD register this device to + * HBA FW by the command "OPC_INB_REG_DEV", after that the HBA will assign + * a device ID(according to device's sas address) and returned it to LLDD.from + * now on, we communicate with HBA FW with the device ID which HBA assigned + * rather than sas address. it is the neccessary step for our HBA but it is + * the optional for other HBA driver. + * @dev: the device structure which sas layer used. + */ +static int pm8001_dev_found_notify(struct domain_device *dev) +{ + unsigned long flags = 0; + int res = 0; + struct pm8001_hba_info *pm8001_ha = NULL; + struct domain_device *parent_dev = dev->parent; + struct pm8001_device *pm8001_device; + DECLARE_COMPLETION_ONSTACK(completion); + u32 flag = 0; + pm8001_ha = pm8001_find_ha_by_dev(dev); + spin_lock_irqsave(&pm8001_ha->lock, flags); + + pm8001_device = pm8001_alloc_dev(pm8001_ha); + pm8001_device->sas_device = dev; + if (!pm8001_device) { + res = -1; + goto found_out; + } + dev->lldd_dev = (void *)pm8001_device; + pm8001_device->dev_type = dev->dev_type; + pm8001_device->dcompletion = &completion; + if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) { + int phy_id; + struct ex_phy *phy; + for (phy_id = 0; phy_id < parent_dev->ex_dev.num_phys; + phy_id++) { + phy = &parent_dev->ex_dev.ex_phy[phy_id]; + if (SAS_ADDR(phy->attached_sas_addr) + == SAS_ADDR(dev->sas_addr)) { + pm8001_device->attached_phy = phy_id; + break; + } + } + if (phy_id == parent_dev->ex_dev.num_phys) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Error: no attached dev:%016llx" + " at ex:%016llx.\n", SAS_ADDR(dev->sas_addr), + SAS_ADDR(parent_dev->sas_addr))); + res = -1; + } + } else { + if (dev->dev_type == SATA_DEV) { + pm8001_device->attached_phy = + dev->rphy->identify.phy_identifier; + flag = 1; /* directly sata*/ + } + } /*register this device to HBA*/ + PM8001_DISC_DBG(pm8001_ha, pm8001_printk("Found device \n")); + PM8001_CHIP_DISP->reg_dev_req(pm8001_ha, 0, pm8001_device, flag); + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + wait_for_completion(&completion); + pm8001_ha->flags = PM8001F_RUN_TIME ; + return 0; +found_out: + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + return res; +} + +int pm8001_dev_found(struct domain_device *dev) +{ + return pm8001_dev_found_notify(dev); +} + + + +/** + * pm8001_alloc_task - allocate a task structure for TMF + */ +static struct sas_task *pm8001_alloc_task(void) +{ + struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL); + if (task) { + INIT_LIST_HEAD(&task->list); + spin_lock_init(&task->task_state_lock); + task->task_state_flags = SAS_TASK_STATE_PENDING; + init_timer(&task->timer); + init_completion(&task->completion); + } + return task; +} + +static void pm8001_free_task(struct sas_task *task) +{ + if (task) { + BUG_ON(!list_empty(&task->list)); + kfree(task); + } +} + +static void pm8001_task_done(struct sas_task *task) +{ + if (!del_timer(&task->timer)) + return; + complete(&task->completion); +} + +static void pm8001_tmf_timedout(unsigned long data) +{ + struct sas_task *task = (struct sas_task *)data; + + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + complete(&task->completion); +} + +#define PM8001_TASK_TIMEOUT 20 +/** + * pm8001_exec_internal_tmf_task - when errors or exception happened, we may + * want to do something, for example abort issued task which result in this + * execption, this is by calling this function, note it is also with the task + * execute interface. + * @dev: the wanted device. + * @tmf: which task management wanted to be take. + * @para_len: para_len. + * @parameter: ssp task parameter. + */ +static int pm8001_exec_internal_tmf_task(struct domain_device *dev, + void *parameter, u32 para_len, struct pm8001_tmf_task *tmf) +{ + int res, retry; + struct sas_task *task = NULL; + struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); + + for (retry = 0; retry < 3; retry++) { + task = pm8001_alloc_task(); + if (!task) + return -ENOMEM; + + task->dev = dev; + task->task_proto = dev->tproto; + memcpy(&task->ssp_task, parameter, para_len); + task->task_done = pm8001_task_done; + task->timer.data = (unsigned long)task; + task->timer.function = pm8001_tmf_timedout; + task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ; + add_timer(&task->timer); + + res = pm8001_task_exec(task, 1, GFP_KERNEL, 1, tmf); + + if (res) { + del_timer(&task->timer); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Executing internal task " + "failed\n")); + goto ex_err; + } + wait_for_completion(&task->completion); + res = -TMF_RESP_FUNC_FAILED; + /* Even TMF timed out, return direct. */ + if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TMF task[%x]timeout.\n", + tmf->tmf)); + goto ex_err; + } + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAM_GOOD) { + res = TMF_RESP_FUNC_COMPLETE; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_UNDERRUN) { + /* no error, but return the number of bytes of + * underrun */ + res = task->task_status.residual; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_OVERRUN) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Blocked task error.\n")); + res = -EMSGSIZE; + break; + } else { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" Task to dev %016llx response: 0x%x" + "status 0x%x\n", + SAS_ADDR(dev->sas_addr), + task->task_status.resp, + task->task_status.stat)); + pm8001_free_task(task); + task = NULL; + } + } +ex_err: + BUG_ON(retry == 3 && task != NULL); + if (task != NULL) + pm8001_free_task(task); + return res; +} + +static int +pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, struct domain_device *dev, u32 flag, + u32 task_tag) +{ + int res, retry; + u32 rc, ccb_tag; + struct pm8001_ccb_info *ccb; + struct sas_task *task = NULL; + + for (retry = 0; retry < 3; retry++) { + task = pm8001_alloc_task(); + if (!task) + return -ENOMEM; + + task->dev = dev; + task->task_proto = dev->tproto; + task->task_done = pm8001_task_done; + task->timer.data = (unsigned long)task; + task->timer.function = pm8001_tmf_timedout; + task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ; + add_timer(&task->timer); + + rc = pm8001_tag_alloc(pm8001_ha, &ccb_tag); + if (rc) + return rc; + ccb = &pm8001_ha->ccb_info[ccb_tag]; + ccb->device = pm8001_dev; + ccb->ccb_tag = ccb_tag; + ccb->task = task; + + res = PM8001_CHIP_DISP->task_abort(pm8001_ha, + pm8001_dev, flag, task_tag, ccb_tag); + + if (res) { + del_timer(&task->timer); + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Executing internal task " + "failed\n")); + goto ex_err; + } + wait_for_completion(&task->completion); + res = TMF_RESP_FUNC_FAILED; + /* Even TMF timed out, return direct. */ + if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("TMF task timeout.\n")); + goto ex_err; + } + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAM_GOOD) { + res = TMF_RESP_FUNC_COMPLETE; + break; + + } else { + PM8001_IO_DBG(pm8001_ha, + pm8001_printk(" Task to dev %016llx response: " + "0x%x status 0x%x\n", + SAS_ADDR(dev->sas_addr), + task->task_status.resp, + task->task_status.stat)); + pm8001_free_task(task); + task = NULL; + } + } +ex_err: + BUG_ON(retry == 3 && task != NULL); + if (task != NULL) + pm8001_free_task(task); + return res; +} + +/** + * pm8001_dev_gone_notify - see the comments for "pm8001_dev_found_notify" + * @dev: the device structure which sas layer used. + */ +static void pm8001_dev_gone_notify(struct domain_device *dev) +{ + unsigned long flags = 0; + u32 tag; + struct pm8001_hba_info *pm8001_ha; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + u32 device_id = pm8001_dev->device_id; + pm8001_ha = pm8001_find_ha_by_dev(dev); + spin_lock_irqsave(&pm8001_ha->lock, flags); + pm8001_tag_alloc(pm8001_ha, &tag); + if (pm8001_dev) { + PM8001_DISC_DBG(pm8001_ha, + pm8001_printk("found dev[%d:%x] is gone.\n", + pm8001_dev->device_id, pm8001_dev->dev_type)); + if (pm8001_dev->runing_req) { + spin_unlock_irqrestore(&pm8001_ha->lock, flags); + pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , + dev, 1, 0); + spin_lock_irqsave(&pm8001_ha->lock, flags); + } + PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, 0, device_id); + pm8001_free_dev(pm8001_dev); + } else { + PM8001_DISC_DBG(pm8001_ha, + pm8001_printk("Found dev has gone.\n")); + } + dev->lldd_dev = NULL; + spin_unlock_irqrestore(&pm8001_ha->lock, flags); +} + +void pm8001_dev_gone(struct domain_device *dev) +{ + pm8001_dev_gone_notify(dev); +} + +static int pm8001_issue_ssp_tmf(struct domain_device *dev, + u8 *lun, struct pm8001_tmf_task *tmf) +{ + struct sas_ssp_task ssp_task; + if (!(dev->tproto & SAS_PROTOCOL_SSP)) + return TMF_RESP_FUNC_ESUPP; + + strncpy((u8 *)&ssp_task.LUN, lun, 8); + return pm8001_exec_internal_tmf_task(dev, &ssp_task, sizeof(ssp_task), + tmf); +} + +/** + * Standard mandates link reset for ATA (type 0) and hard reset for + * SSP (type 1) , only for RECOVERY + */ +int pm8001_I_T_nexus_reset(struct domain_device *dev) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); + struct sas_phy *phy = sas_find_local_phy(dev); + if (dev_is_sata(dev)) { + struct sas_phy *phy = sas_find_local_phy(dev); + DECLARE_COMPLETION_ONSTACK(completion_setstate); + rc = sas_phy_reset(phy, 1); + msleep(2000); + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , + dev, 1, 0); + pm8001_dev->setds_completion = &completion_setstate; + rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, + pm8001_dev, 0, 0x01); + wait_for_completion(&completion_setstate); + } else{ + rc = sas_phy_reset(phy, 1); + msleep(2000); + } + PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n", + pm8001_dev->device_id, rc)); + return rc; +} + + + +/* mandatory SAM-3, the task reset the specified LUN*/ +int pm8001_lu_reset(struct domain_device *dev, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_tmf_task tmf_task; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); + if (dev_is_sata(dev)) { + struct sas_phy *phy = sas_find_local_phy(dev); + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , + dev, 1, 0); + rc = sas_phy_reset(phy, 1); + rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, + pm8001_dev, 0, 0x01); + msleep(2000); + } else { + tmf_task.tmf = TMF_LU_RESET; + rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task); + } + /* If failed, fall-through I_T_Nexus reset */ + PM8001_EH_DBG(pm8001_ha, pm8001_printk("for device[%x]:rc=%d\n", + pm8001_dev->device_id, rc)); + return rc; +} + +/* optional SAM-3 */ +int pm8001_query_task(struct sas_task *task) +{ + u32 tag = 0xdeadbeef; + int i = 0; + struct scsi_lun lun; + struct pm8001_tmf_task tmf_task; + int rc = TMF_RESP_FUNC_FAILED; + + if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) { + struct scsi_cmnd *cmnd = task->uldd_task; + struct domain_device *dev = task->dev; + struct pm8001_hba_info *pm8001_ha = + pm8001_find_ha_by_dev(dev); + + int_to_scsilun(cmnd->device->lun, &lun); + rc = pm8001_find_tag(task, &tag); + if (rc == 0) { + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + PM8001_EH_DBG(pm8001_ha, pm8001_printk("Query:[")); + for (i = 0; i < 16; i++) + printk(KERN_INFO "%02x ", cmnd->cmnd[i]); + printk(KERN_INFO "]\n"); + tmf_task.tmf = TMF_QUERY_TASK; + tmf_task.tag_of_task_to_be_managed = tag; + + rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task); + switch (rc) { + /* The task is still in Lun, release it then */ + case TMF_RESP_FUNC_SUCC: + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("The task is still in Lun \n")); + /* The task is not in Lun or failed, reset the phy */ + case TMF_RESP_FUNC_FAILED: + case TMF_RESP_FUNC_COMPLETE: + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("The task is not in Lun or failed," + " reset the phy \n")); + break; + } + } + pm8001_printk(":rc= %d\n", rc); + return rc; +} + +/* mandatory SAM-3, still need free task/ccb info, abord the specified task */ +int pm8001_abort_task(struct sas_task *task) +{ + unsigned long flags; + u32 tag = 0xdeadbeef; + u32 device_id; + struct domain_device *dev ; + struct pm8001_hba_info *pm8001_ha = NULL; + struct pm8001_ccb_info *ccb; + struct scsi_lun lun; + struct pm8001_device *pm8001_dev; + struct pm8001_tmf_task tmf_task; + int rc = TMF_RESP_FUNC_FAILED; + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + rc = TMF_RESP_FUNC_COMPLETE; + goto out; + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) { + struct scsi_cmnd *cmnd = task->uldd_task; + dev = task->dev; + ccb = task->lldd_task; + pm8001_dev = dev->lldd_dev; + pm8001_ha = pm8001_find_ha_by_dev(dev); + int_to_scsilun(cmnd->device->lun, &lun); + rc = pm8001_find_tag(task, &tag); + if (rc == 0) { + printk(KERN_INFO "No such tag in %s\n", __func__); + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + device_id = pm8001_dev->device_id; + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("abort io to device_id = %d\n", device_id)); + tmf_task.tmf = TMF_ABORT_TASK; + tmf_task.tag_of_task_to_be_managed = tag; + rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task); + if (rc) + goto out; + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev, + pm8001_dev->sas_device, 0, tag); + } else if (task->task_proto & SAS_PROTOCOL_SATA || + task->task_proto & SAS_PROTOCOL_STP) { + dev = task->dev; + pm8001_dev = dev->lldd_dev; + pm8001_ha = pm8001_find_ha_by_dev(dev); + rc = pm8001_find_tag(task, &tag); + if (rc == 0) { + printk(KERN_INFO "No such tag in %s\n", __func__); + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev, + pm8001_dev->sas_device, 0, tag); + } else { + /* SMP */ + dev = task->dev; + pm8001_dev = dev->lldd_dev; + pm8001_ha = pm8001_find_ha_by_dev(dev); + rc = pm8001_find_tag(task, &tag); + if (rc == 0) { + printk(KERN_INFO "No such tag in %s\n", __func__); + rc = TMF_RESP_FUNC_FAILED; + return rc; + } + rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev, + pm8001_dev->sas_device, 0, tag); + + } +out: + if (rc != TMF_RESP_FUNC_COMPLETE) + pm8001_printk("rc= %d\n", rc); + return rc; +} + +int pm8001_abort_task_set(struct domain_device *dev, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_tmf_task tmf_task; + + tmf_task.tmf = TMF_ABORT_TASK_SET; + rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task); + return rc; +} + +int pm8001_clear_aca(struct domain_device *dev, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_tmf_task tmf_task; + + tmf_task.tmf = TMF_CLEAR_ACA; + rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task); + + return rc; +} + +int pm8001_clear_task_set(struct domain_device *dev, u8 *lun) +{ + int rc = TMF_RESP_FUNC_FAILED; + struct pm8001_tmf_task tmf_task; + struct pm8001_device *pm8001_dev = dev->lldd_dev; + struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); + + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("I_T_L_Q clear task set[%x]\n", + pm8001_dev->device_id)); + tmf_task.tmf = TMF_CLEAR_TASK_SET; + rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task); + return rc; +} diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h new file mode 100644 index 0000000..a74260b --- /dev/null +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -0,0 +1,458 @@ +/* + * 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. + * + */ + +#ifndef _PM8001_SAS_H_ +#define _PM8001_SAS_H_ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/ctype.h> +#include <linux/dma-mapping.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <scsi/libsas.h> +#include <scsi/scsi_tcq.h> +#include <scsi/sas_ata.h> + +#include "pm8001_defs.h" + +#define DRV_NAME "pm8001" +#define DRV_VERSION "0.1.30" +#define PM8001_FAIL_LOGGING 0x01 /* libsas EH function logging */ +#define PM8001_INIT_LOGGING 0x02 /* driver init logging */ +#define PM8001_DISC_LOGGING 0x04 /* discovery layer logging */ +#define PM8001_IO_LOGGING 0x08 /* I/O path logging */ +#define PM8001_EH_LOGGING 0x10 /* Error message logging */ +#define PM8001_IOCTL_LOGGING 0x20 /* IOCTL message logging */ +#define PM8001_MSG_LOGGING 0x40 /* misc message logging */ +#define pm8001_printk(format, arg...) printk(KERN_INFO "%s %d:" format,\ + __func__, __LINE__, ## arg) +#define PM8001_CHECK_LOGGING(HBA, LEVEL, CMD) \ +do { \ + if (unlikely(HBA->logging_level & LEVEL)) \ + do { \ + CMD; \ + } while (0); \ +} while (0); + +#define PM8001_EH_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_EH_LOGGING, CMD) + +#define PM8001_INIT_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_INIT_LOGGING, CMD) + +#define PM8001_DISC_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_DISC_LOGGING, CMD) + +#define PM8001_IO_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_IO_LOGGING, CMD) + +#define PM8001_FAIL_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_FAIL_LOGGING, CMD) + +#define PM8001_IOCTL_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_IOCTL_LOGGING, CMD) + +#define PM8001_MSG_DBG(HBA, CMD) \ + PM8001_CHECK_LOGGING(HBA, PM8001_MSG_LOGGING, CMD) + + +#define PM8001_USE_TASKLET +#define PM8001_USE_MSIX +#define PM8001_CONFIG_PM + + +#define DEV_IS_EXPANDER(type) ((type == EDGE_DEV) || (type == FANOUT_DEV)) +#define bit(n) ((u32)1 << n) +#define for_each_phy(__lseq_mask, __mc, __lseq, __rest) \ + for ((__mc) = (__lseq_mask), (__lseq) = 0; \ + (__mc) != 0 && __rest; \ + (++__lseq), (__mc) >>= 1) + +#define PM8001_NAME_LENGTH 32/* generic length of strings */ +extern struct list_head hba_list; +extern const struct pm8001_dispatch pm8001_8001_dispatch; + +struct pm8001_hba_info; +struct pm8001_ccb_info; +struct pm8001_device; +struct pm8001_tmf_task; +struct pm8001_dispatch { + char *name; + int (*chip_init)(struct pm8001_hba_info *pm8001_ha); + int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha, u32 signature); + int (*chip_ioremap)(struct pm8001_hba_info *pm8001_ha); + void (*chip_iounmap)(struct pm8001_hba_info *pm8001_ha); + irqreturn_t (*isr)(struct pm8001_hba_info *pm8001_ha, int irq); + void (*interrupt_enable)(struct pm8001_hba_info *pm8001_ha); + void (*interrupt_disable)(struct pm8001_hba_info *pm8001_ha); + void (*make_prd)(struct scatterlist *scatter, int nr, void *prd); + int (*smp_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb); + int (*ssp_io_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb); + int (*sata_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb); + int (*phy_start_req)(struct pm8001_hba_info *pm8001_ha, u32 num, + u8 phy_id); + int (*phy_stop_req)(struct pm8001_hba_info *pm8001_ha, u32 num, + u8 phy_id); + int (*reg_dev_req)(struct pm8001_hba_info *pm8001_ha, u32 num, + struct pm8001_device *pm8001_dev, u32 flag); + int (*dereg_dev_req)(struct pm8001_hba_info *pm8001_ha, + u32 num, u32 device_id); + int (*phy_ctl_req)(struct pm8001_hba_info *pm8001_ha, + u32 num, u32 phyId, u32 phy_op); + int (*task_abort)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, + u32 cmd_tag); + int (*ssp_tm_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf); + int (*get_nvmd_req)(struct pm8001_hba_info *pm8001_ha, u32 num, + void *payload); + int (*set_nvmd_req)(struct pm8001_hba_info *pm8001_ha, u32 num, + void *payload); + int (*fw_flash_update_req)(struct pm8001_hba_info *pm8001_ha, + u32 num, void *payload); + int (*set_dev_state_req)(struct pm8001_hba_info *pm8001_ha, + struct pm8001_device *pm8001_dev, u32 num, u32 state); + int (*sas_diag_start_end_req)(struct pm8001_hba_info *pm8001_ha, + u32 num, u32 state); + int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha, + u32 num, u32 state); +}; + +struct pm8001_chip_info { + u32 n_phy; + const struct pm8001_dispatch *dispatch; +}; +#define PM8001_CHIP_DISP (pm8001_ha->chip->dispatch) + +struct pm8001_port { + struct asd_sas_port sas_port; +}; + +struct pm8001_phy { + struct pm8001_hba_info *pm8001_ha; + struct pm8001_port *port; + struct asd_sas_phy sas_phy; + struct sas_identify identify; + struct scsi_device *sdev; + struct timer_list timer; + u64 dev_sas_addr; + u32 phy_type; + struct completion *enable_completion; + u32 frame_rcvd_size; + u8 frame_rcvd[32]; + u8 phy_attached; + u8 phy_state; + enum sas_linkrate minimum_linkrate; + enum sas_linkrate maximum_linkrate; +}; + +struct pm8001_device { + enum sas_dev_type dev_type; + struct domain_device *sas_device; + u32 attached_phy; + u32 id; + struct completion *dcompletion; + struct completion *setds_completion; + u32 device_id; + u32 runing_req; +}; + +struct pm8001_prd_imt { + __le32 len; + __le32 e; +}; + +struct pm8001_prd { + __le64 addr; /* 64-bit buffer address */ + struct pm8001_prd_imt im_len; /* 64-bit length */ +} __attribute__ ((packed)); +/* + * CCB(Command Control Block) + */ +struct pm8001_ccb_info { + struct list_head entry; + struct sas_task *task; + u32 n_elem; + u32 ccb_tag; + struct pm8001_device *device; + struct pm8001_prd buf_prd[PM8001_MAX_DMA_SG]; + struct fw_control_ex *fw_control_context; +}; + +struct mpi_mem { + /* Virtual pointer to the memory region */ + void *virt_ptr; + dma_addr_t phys_addr; + /* Upper 32 bits of physical address */ + u32 phys_addr_hi; + /* Lower 32 bits of physical address*/ + u32 phys_addr_lo; + /* Total length in bytes allocated */ + u32 total_len; + /* Number of elements */ + u32 num_elements; + /* Size in bytes of an element */ + u32 element_size; +}; + +struct mpi_mem_req { + /* The number of element in the mpiMemory array */ + u32 count; + /* The array of structures that define memroy regions*/ + struct mpi_mem region[USI_MAX_MEMCNT]; +}; + +struct main_cfg_table { + u32 signature; + u32 interface_rev; + u32 firmware_rev; + u32 max_out_io; + u32 max_sgl; + u32 ctrl_cap_flag; + u32 gst_offset; + u32 inbound_queue_offset; + u32 outbound_queue_offset; + u32 inbound_q_nppd_hppd; + u32 outbound_hw_event_pid0_3; + u32 outbound_hw_event_pid4_7; + u32 outbound_ncq_event_pid0_3; + u32 outbound_ncq_event_pid4_7; + u32 outbound_tgt_ITNexus_event_pid0_3; + u32 outbound_tgt_ITNexus_event_pid4_7; + u32 outbound_tgt_ssp_event_pid0_3; + u32 outbound_tgt_ssp_event_pid4_7; + u32 outbound_tgt_smp_event_pid0_3; + u32 outbound_tgt_smp_event_pid4_7; + u32 upper_event_log_addr; + u32 lower_event_log_addr; + u32 event_log_size; + u32 event_log_option; + u32 upper_iop_event_log_addr; + u32 lower_iop_event_log_addr; + u32 iop_event_log_size; + u32 iop_event_log_option; + u32 fatal_err_interrupt; + u32 fatal_err_dump_offset0; + u32 fatal_err_dump_length0; + u32 fatal_err_dump_offset1; + u32 fatal_err_dump_length1; + u32 hda_mode_flag; + u32 anolog_setup_table_offset; +}; +struct general_status_table { + u32 gst_len_mpistate; + u32 iq_freeze_state0; + u32 iq_freeze_state1; + u32 msgu_tcnt; + u32 iop_tcnt; + u32 reserved; + u32 phy_state[8]; + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 recover_err_info[8]; +}; +struct inbound_queue_table { + u32 element_pri_size_cnt; + u32 upper_base_addr; + u32 lower_base_addr; + u32 ci_upper_base_addr; + u32 ci_lower_base_addr; + u32 pi_pci_bar; + u32 pi_offset; + u32 total_length; + void *base_virt; + void *ci_virt; + u32 reserved; + u32 consumer_index; + u32 producer_idx; +}; +struct outbound_queue_table { + u32 element_size_cnt; + u32 upper_base_addr; + u32 lower_base_addr; + void *base_virt; + u32 pi_upper_base_addr; + u32 pi_lower_base_addr; + u32 ci_pci_bar; + u32 ci_offset; + u32 total_length; + void *pi_virt; + u32 interrup_vec_cnt_delay; + u32 dinterrup_to_pci_offset; + u32 producer_index; + u32 consumer_idx; +}; +struct pm8001_hba_memspace { + void __iomem *memvirtaddr; + u64 membase; + u32 memsize; +}; +struct pm8001_hba_info { + char name[PM8001_NAME_LENGTH]; + struct list_head list; + unsigned long flags; + spinlock_t lock;/* host-wide lock */ + struct pci_dev *pdev;/* our device */ + struct device *dev; + struct pm8001_hba_memspace io_mem[6]; + struct mpi_mem_req memoryMap; + dma_addr_t mur_addr;/*Message Unit Register Base Address*/ + dma_addr_t cfg_addr;/*Main Configuration Table Address*/ + dma_addr_t gst_addr;/*General Status Table Address*/ + dma_addr_t inb_addr;/*Inbound Queue Configuration Table + Addess*/ + dma_addr_t outb_addr;/*Outbound Queue Configuration Table + Address*/ + struct main_cfg_table cfg_data; + struct general_status_table gst_data; + struct inbound_queue_table inb_data[PM8001_MAX_INB_NUM]; + struct outbound_queue_table outb_data[PM8001_MAX_OUTB_NUM]; + u8 sas_addr[SAS_ADDR_SIZE]; + struct sas_ha_struct *sas;/* SCSI/SAS glue */ + struct Scsi_Host *shost; + u32 chip_id; + const struct pm8001_chip_info *chip; + struct completion *nvmd_completion; + int tags_num; + u8 tags[PM8001_MAX_DEVICES]; + struct pm8001_phy phy[PM8001_MAX_PHYS]; + struct pm8001_port port[PM8001_MAX_PHYS]; + u32 id; + u32 irq; + struct pm8001_device devices[PM8001_MAX_DEVICES]; + struct pm8001_ccb_info ccb_info[PM8001_MAX_DEVICES]; +#ifdef PM8001_USE_MSIX + struct msix_entry msix_entries[16];/*for msi-x interrupt*/ + int number_of_intr;/*will be used in remove()*/ +#endif +#ifdef PM8001_USE_TASKLET + struct tasklet_struct tasklet; +#endif + u32 logging_level; +}; + +/* define task management IU */ +struct pm8001_tmf_task { + u8 tmf; + u32 tag_of_task_to_be_managed; +}; + +/** + * brief param structure for firmware flash update. + */ +struct fw_flash_updata_info { + u32 cur_image_offset; + u32 cur_image_len; + u32 total_image_len; + struct pm8001_prd sgl; +}; + +struct fw_control_info { + u32 retcode;/*ret code (status)*/ + u32 phase;/*ret code phase*/ + u32 phaseCmplt;/*percent complete for the current + update phase */ + u32 version;/*Hex encoded firmware version number*/ + u32 offset;/*Used for downloading firmware */ + u32 len; /*len of buffer*/ + u32 size;/* Used in OS VPD and Trace get size + operations.*/ + u32 reserved;/* padding required for 64 bit + alignment */ + u8 buffer[1];/* Start of buffer */ +}; +struct fw_control_ex { + struct fw_control_info *fw_control; + void *buffer;/* keep buffer pointer to be + freed when the responce comes*/ + void *virtAddr;/* keep virtual address of the data */ + void *usrAddr;/* keep virtual address of the + user data */ + dma_addr_t phys_addr; + u32 len; /* len of buffer */ + void *payload; /* pointer to IOCTL Payload */ + u8 inProgress;/*if 1 - the IOCTL request is in + progress */ + void *param1; + void *param2; + void *param3; +}; + +/******************** function prototype *********************/ +int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out); +void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha); +u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag); +void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx); +void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha, + struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx); +int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, + void *funcdata); +int pm8001_slave_alloc(struct scsi_device *scsi_dev); +int pm8001_slave_configure(struct scsi_device *sdev); +void pm8001_scan_start(struct Scsi_Host *shost); +int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time); +int pm8001_queue_command(struct sas_task *task, const int num, + gfp_t gfp_flags); +int pm8001_abort_task(struct sas_task *task); +int pm8001_abort_task_set(struct domain_device *dev, u8 *lun); +int pm8001_clear_aca(struct domain_device *dev, u8 *lun); +int pm8001_clear_task_set(struct domain_device *dev, u8 *lun); +int pm8001_dev_found(struct domain_device *dev); +void pm8001_dev_gone(struct domain_device *dev); +int pm8001_lu_reset(struct domain_device *dev, u8 *lun); +int pm8001_I_T_nexus_reset(struct domain_device *dev); +int pm8001_query_task(struct sas_task *task); +int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr, + u32 *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo, u32 mem_size); + +/* ctl shared API */ +extern struct device_attribute *pm8001_host_attrs[]; +void pm8001_ctl_init(void); +void pm8001_ctl_exit(void); +#endif + -- -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html