blob: 770c2bd7c41f686a6dfc2f2292e829589fb07835 [file] [log] [blame]
/*******************************************************************************
Copyright (C) Marvell International Ltd. and its affiliates
This software file (the "File") is owned and distributed by Marvell
International Ltd. and/or its affiliates ("Marvell") under the following
alternative licensing terms. Once you have made an election to distribute the
File under one of the following license alternatives, please (i) delete this
introductory statement regarding license alternatives, (ii) delete the two
license alternatives that you have not elected to use and (iii) preserve the
Marvell copyright notice above.
********************************************************************************
Marvell GPL License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File in accordance with the terms and conditions of the General
Public License Version 2, June 1991 (the "GPL License"), a copy of which is
available along with the File in the license.txt file or by writing to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED. The GPL License provides additional details about this warranty
disclaimer.
*******************************************************************************/
/*******************************************************************************
* file_name - mvLinuxIalHt.c
*
* DESCRIPTION: implementation for Linux IAL.
*
* DEPENDENCIES:
* mvLinuxIalHt.h
* mvLinuxIalLib.h
* Linux Os header files
* Core driver header files
*
*
*******************************************************************************/
/* includes */
#ifndef LINUX_VERSION_CODE
#include <linux/version.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
#error "This driver works only with kernel 2.4.0 or higher!"
#endif
#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)))
#error "This driver does not support kernel 2.5!"
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/hdreg.h>
#ifdef CONFIG_MV_INCLUDE_INTEG_SATA
#include "ctrlEnv/mvCtrlEnvLib.h"
#include "mvSysSataApi.h"
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#include <linux/blk.h>
#include "scsi.h"
#include "hosts.h"
#else
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#endif
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/io.h>
#include "mvLinuxIalHt.h"
#include "mvRegs.h"
#include "mvIALCommon.h"
#include "mvLinuxIalSmart.h"
extern Scsi_Host_Template driver_template;
static void mv_ial_init_log(void);
static char mv_ial_proc_version[]="Version_1_1";
extern void release_ata_mem(struct mv_comp_info * pInfo);
extern MV_BOOLEAN IALCompletion(struct mvSataAdapter *pSataAdapter,
MV_SATA_SCSI_CMD_BLOCK *pCmdBlock);
static struct pci_device_id mvSata_pci_table[] =
{
{MV_SATA_VENDOR_ID, MV_SATA_DEVICE_ID_5080, PCI_ANY_ID, PCI_ANY_ID, 0, 0},
{MV_SATA_VENDOR_ID, MV_SATA_DEVICE_ID_5081, PCI_ANY_ID, PCI_ANY_ID, 0, 0},
{MV_SATA_VENDOR_ID, MV_SATA_DEVICE_ID_5040, PCI_ANY_ID, PCI_ANY_ID, 0, 0},
{MV_SATA_VENDOR_ID, MV_SATA_DEVICE_ID_5041, PCI_ANY_ID, PCI_ANY_ID, 0, 0},
{MV_SATA_VENDOR_ID, MV_SATA_DEVICE_ID_6081, PCI_ANY_ID, PCI_ANY_ID, 0, 0},
{MV_SATA_VENDOR_ID, MV_SATA_DEVICE_ID_6041, PCI_ANY_ID, PCI_ANY_ID, 0, 0},
{MV_SATA_VENDOR_ID, MV_SATA_DEVICE_ID_6042, PCI_ANY_ID, PCI_ANY_ID, 0, 0},
{MV_SATA_VENDOR_ID, MV_SATA_DEVICE_ID_7042, PCI_ANY_ID, PCI_ANY_ID, 0, 0},
{0,}
};
int adapterId = 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#ifndef __devexit_p
#define __devexit_p(x) x
#endif
static void mv_ial_ht_select_queue_depths (struct Scsi_Host* pHost,
struct scsi_device* pDevs);
static inline struct Scsi_Host *scsi_host_alloc(Scsi_Host_Template *t, size_t s)
{
return scsi_register(t, s);
}
static inline void scsi_host_put(struct Scsi_Host *h)
{
scsi_unregister(h);
}
#define scsi_scan_host(x...)
#define scsi_remove_host(x...)
#else
static int mv_ial_ht_slave_configure (struct scsi_device* pDevs);
static int __devinit mv_ial_probe_device(struct pci_dev *pci_dev, const struct pci_device_id *ent);
static void __devexit mv_ial_remove_device(struct pci_dev *pci_dev);
MODULE_DEVICE_TABLE(pci, mvSata_pci_table);
static char mv_hot_plug_name[] = "mvSata";
static struct pci_driver mv_ial_pci_driver =
{
.name = mv_hot_plug_name,
.id_table = mvSata_pci_table,
.probe = mv_ial_probe_device,
.remove = __devexit_p(mv_ial_remove_device),
};
#ifdef CONFIG_MV_INCLUDE_INTEG_SATA
static int __devinit mv_ial_init_soc_sata(void);
#endif
IAL_ADAPTER_T *pSocAdapter = NULL;
static int __init mv_ial_init(void)
{
mv_ial_init_log();
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "mvSata init.\n");
driver_template.module = THIS_MODULE;
#ifdef CONFIG_MV_INCLUDE_INTEG_SATA
if (MV_FALSE == mvCtrlPwrClckGet(SATA_UNIT_ID, 0))
{
printk("\nWarning Sata is Powered Off\n");
}
else
{
printk("Integrated Sata device found\n");
mv_ial_init_soc_sata();
}
#endif
return (int)pci_register_driver(&mv_ial_pci_driver);
}
static void __exit mv_ial_exit(void)
{
#ifdef CONFIG_MV_INCLUDE_INTEG_SATA
mv_ial_remove_device(NULL);
#endif
pci_unregister_driver(&mv_ial_pci_driver);
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "mvSata exit.\n");
}
module_init(mv_ial_init);
module_exit(mv_ial_exit);
#endif
static void mv_ial_init_log(void)
{
#ifdef MV_LOGGER
char *szModules[] = {"Core Driver",
"SAL",
"Common IAL",
"Linux IAL"
};
#if defined (MV_LOG_DEBUG)
mvLogRegisterModule(MV_CORE_DRIVER_LOG_ID, MV_DEBUG_ENABLE_ALL,
szModules[MV_CORE_DRIVER_LOG_ID]);
mvLogRegisterModule(MV_SAL_LOG_ID, MV_DEBUG_ENABLE_ALL,
szModules[MV_SAL_LOG_ID]);
mvLogRegisterModule(MV_IAL_COMMON_LOG_ID, MV_DEBUG_ENABLE_ALL,
szModules[MV_IAL_COMMON_LOG_ID]);
mvLogRegisterModule(MV_IAL_LOG_ID, MV_DEBUG_ENABLE_ALL,
szModules[MV_IAL_LOG_ID]);
#elif defined (MV_LOG_ERROR)
mvLogRegisterModule(MV_CORE_DRIVER_LOG_ID, MV_DEBUG_FATAL_ERROR | MV_DEBUG_ERROR |
MV_DEBUG_INFO,
szModules[MV_CORE_DRIVER_LOG_ID]);
mvLogRegisterModule(MV_SAL_LOG_ID, MV_DEBUG_FATAL_ERROR | MV_DEBUG_ERROR |
MV_DEBUG_INFO,
szModules[MV_SAL_LOG_ID]);
mvLogRegisterModule(MV_IAL_COMMON_LOG_ID, MV_DEBUG_FATAL_ERROR | MV_DEBUG_ERROR |
MV_DEBUG_INFO,
szModules[MV_IAL_COMMON_LOG_ID]);
mvLogRegisterModule(MV_IAL_LOG_ID, MV_DEBUG_FATAL_ERROR | MV_DEBUG_ERROR |
MV_DEBUG_INFO,
szModules[MV_IAL_LOG_ID]);
#endif
#endif
}
/****************************************************************
* Name: set_device_regs
*
* Description: initialize the device registers.
*
* Parameters: pMvSataAdapter, pointer to the Device data structure.
* pcidev, pointer to the pci device data structure.
*
* Returns: =0 ->success, < 0 ->failure.
*
****************************************************************/
static int set_device_regs(MV_SATA_ADAPTER *pMvSataAdapter,
struct pci_dev *pcidev)
{
pMvSataAdapter->intCoalThre[0]= MV_IAL_HT_SACOALT_DEFAULT;
pMvSataAdapter->intCoalThre[1]= MV_IAL_HT_SACOALT_DEFAULT;
pMvSataAdapter->intTimeThre[0] = MV_IAL_HT_SAITMTH_DEFAULT;
pMvSataAdapter->intTimeThre[1] = MV_IAL_HT_SAITMTH_DEFAULT;
pMvSataAdapter->pciCommand = MV_PCI_COMMAND_REG_DEFAULT;
pMvSataAdapter->pciSerrMask = MV_PCI_SERR_MASK_REG_ENABLE_ALL;
pMvSataAdapter->pciInterruptMask = MV_PCI_INTERRUPT_MASK_REG_ENABLE_ALL;
pMvSataAdapter->mvSataEventNotify = mv_ial_lib_event_notify;
return 0;
}
static int mv_ial_get_num_of_ports(const struct pci_device_id *id)
{
switch(id->device)
{
case MV_SATA_DEVICE_ID_5080:
case MV_SATA_DEVICE_ID_5081:
case MV_SATA_DEVICE_ID_6081:
return 8;
case MV_SATA_DEVICE_ID_5040:
case MV_SATA_DEVICE_ID_5041:
case MV_SATA_DEVICE_ID_6041:
case MV_SATA_DEVICE_ID_6042:
case MV_SATA_DEVICE_ID_7042:
return 4;
default:
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_FATAL_ERROR,
"getMaxNumberOfPorts() Unknown device ID.\n");
return 0;
}
}
static void mv_ial_free_scsi_hosts(IAL_ADAPTER_T *pAdapter, MV_BOOLEAN freeAdapter)
{
int i;
for (i = 0; i < pAdapter->maxHosts; i++)
{
if (pAdapter->host[i] != NULL)
{
mv_ial_lib_prd_destroy(pAdapter->host[i]);
scsi_host_put(pAdapter->host[i]->scsihost);
pAdapter->host[i] = NULL;
}
}
pAdapter->activeHosts = 0;
if (MV_TRUE == freeAdapter)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG,
"[%d] freeing Adapter data structure.\n", pAdapter->mvSataAdapter.adapterId);
kfree(pAdapter);
}
}
static int __devinit mv_ial_probe_device(struct pci_dev *pcidev,
const struct pci_device_id *id)
{
MV_SATA_ADAPTER *pMvSataAdapter;
IAL_ADAPTER_T *pAdapter;
MV_U8 i;
pci_set_drvdata(pcidev, NULL);
if (pci_enable_device(pcidev))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,
"pci_enable_device() failed\n");
return -ENODEV;
}
pci_set_master(pcidev);
if (0 == pci_set_dma_mask(pcidev, 0xffffffffffffffffULL))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG,"using 64-bit DMA.\n");
}
else if (0 == pci_set_dma_mask(pcidev, 0xffffffffUL))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "using 32-bit DMA.\n");
}
else
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "DMA 32-bit not supported"
" in the system\n");
pci_disable_device(pcidev);
return -ENODEV;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
if (pci_request_regions(pcidev, mv_hot_plug_name) != 0)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "pci_request_regions() failed\n");
pci_disable_device(pcidev);
return -ENOMEM;
}
#endif
pAdapter = (IAL_ADAPTER_T*)kmalloc(sizeof(IAL_ADAPTER_T), GFP_ATOMIC);
if (pAdapter == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "IAL Adapter allocation failed\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
memset(pAdapter, 0, sizeof(IAL_ADAPTER_T));
pAdapter->activeHosts = 0;
pAdapter->maxHosts = mv_ial_get_num_of_ports(id);
if (pAdapter->maxHosts == 0)
{
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "mv_ial_get_num_of_ports() failed\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
for (i = 0; i < pAdapter->maxHosts; i++)
{
struct Scsi_Host *pshost = scsi_host_alloc(&driver_template, sizeof(IAL_HOST_T));
if (pshost == NULL)
{
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "Scsi_Host allocation failed\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
pAdapter->host[i] = HOSTDATA(pshost);
memset(pAdapter->host[i], 0, sizeof(IAL_HOST_T));
pAdapter->host[i]->scsihost = pshost;
pAdapter->host[i]->pAdapter = pAdapter;
pAdapter->host[i]->channelIndex = (MV_U8)i;
pAdapter->activeHosts |= (1 << i);
}
pAdapter->pcidev = pcidev;
pMvSataAdapter = &(pAdapter->mvSataAdapter);
pMvSataAdapter->IALData = pAdapter;
spin_lock_init (&pAdapter->adapter_lock);
for (i = 0; i < pAdapter->maxHosts; i++)
{
pAdapter->host[i]->scsi_cmnd_done_head = NULL;
pAdapter->host[i]->scsi_cmnd_done_tail = NULL;
}
pAdapter->host[0]->scsihost->base = pci_resource_start(pcidev, 0);
for (i = 1; i < pAdapter->maxHosts; i++)
{
if (pAdapter->host[i] != NULL)
pAdapter->host[i]->scsihost->base = pAdapter->host[0]->scsihost->base;
}
pMvSataAdapter->adapterIoBaseAddress =
(MV_BUS_ADDR_T)ioremap(pAdapter->host[0]->scsihost->base,
pci_resource_len(pcidev, 0));
if (!pMvSataAdapter->adapterIoBaseAddress)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "Failed to remap memory io spcae\n");
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
else
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "io base address 0x%08lx\n",
(ulong)pMvSataAdapter->adapterIoBaseAddress);
}
pMvSataAdapter->adapterId = adapterId++;
/* get the revision ID */
if (pci_read_config_byte(pcidev, PCI_REVISION_ID, &pAdapter->rev_id))
{
printk(KERN_WARNING "mvSata: Failed to get revision id.\n");
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
pMvSataAdapter->pciConfigRevisionId = pAdapter->rev_id;
pMvSataAdapter->pciConfigDeviceId = id->device;
if (set_device_regs(pMvSataAdapter, pcidev))
{
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
/*Do not allow hotplug handler to work*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
init_MUTEX(&pAdapter->rescan_mutex);
atomic_set(&pAdapter->stopped, 1);
#endif
if (mvSataInitAdapter(pMvSataAdapter) == MV_FALSE)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: core failed to initialize the adapter\n",
pMvSataAdapter->adapterId);
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
if (mv_ial_lib_allocate_edma_queues(pAdapter))
{
mvLogMsg(MV_IAL_LOG_ID,MV_DEBUG_ERROR,
"Failed to allocate memory for EDMA queues\n");
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
for (i = 0; i < pAdapter->maxHosts; i++)
{
if ((pAdapter->activeHosts & (1 << i)) == 0)
{
continue;
}
if (mv_ial_lib_prd_init(pAdapter->host[i]))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,
"Failed to init PRD memory manager - host %d\n", i);
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
}
pAdapter->ataScsiAdapterExt = (MV_SAL_ADAPTER_EXTENSION*)kmalloc(sizeof(MV_SAL_ADAPTER_EXTENSION),
GFP_ATOMIC);
if (pAdapter->ataScsiAdapterExt == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,"[%d]: out of memory, failed to allocate MV_SAL_ADAPTER_EXTENSION\n",
pAdapter->mvSataAdapter.adapterId);
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
mvSataScsiInitAdapterExt(pAdapter->ataScsiAdapterExt,
pMvSataAdapter);
/* let SAL report only the BUS RESET UA event*/
pAdapter->ataScsiAdapterExt->UAMask = MV_BIT0;
/* enable device interrupts even if no storage devices connected now*/
#ifdef MV_SUPPORT_MSI
{
int err;
if ((err = pci_enable_msi(pcidev)))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: Unable to allocate MSI interrupt Error: %d\n",
pMvSataAdapter->adapterId, err);
}
}
#endif
if (request_irq(pcidev->irq, mv_ial_lib_int_handler,
(IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED), "mvSata",
pAdapter) < 0)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: unable to allocate IRQ for controler\n",
pMvSataAdapter->adapterId);
#ifdef MV_SUPPORT_MSI
pci_disable_msi(pAdapter->pcidev);
#endif
kfree(pAdapter->ataScsiAdapterExt);
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
for (i = 0; i < pAdapter->maxHosts; i++)
{
if ((pAdapter->activeHosts & (1 << i)) == 0)
{
continue;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
scsi_set_device(pAdapter->host[i]->scsihost, &pcidev->dev);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
scsi_set_pci_device(pAdapter->host[i]->scsihost, pcidev);
#endif
pAdapter->host[i]->scsihost->irq = pcidev->irq;
/* each SATA channel will emulate scsi host !!!*/
if (pMvSataAdapter->sataAdapterGeneration == MV_SATA_GEN_I)
{
pAdapter->host[i]->scsihost->max_id = 1;
}
else
{
pAdapter->host[i]->scsihost->max_id = MV_SATA_PM_MAX_PORTS;
}
pAdapter->host[i]->scsihost->max_lun = 1;
pAdapter->host[i]->scsihost->max_channel = 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
pAdapter->host[i]->scsihost->select_queue_depths = mv_ial_ht_select_queue_depths;
#endif
}
if (MV_FALSE == mvAdapterStartInitialization(pMvSataAdapter,
&pAdapter->ialCommonExt,
pAdapter->ataScsiAdapterExt))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: mvAdapterStartInitialization"
" Failed\n", pMvSataAdapter->adapterId);
free_irq (pcidev->irq, pMvSataAdapter);
#ifdef MV_SUPPORT_MSI
pci_disable_msi(pAdapter->pcidev);
#endif
kfree(pAdapter->ataScsiAdapterExt);
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
pci_release_regions(pcidev);
#endif
pci_disable_device(pcidev);
return -ENOMEM;
}
pci_set_drvdata(pcidev, pAdapter);
for (i = 0; i < pAdapter->maxHosts; i++)
{
if ((pAdapter->activeHosts & (1 << i)) == 0)
{
continue;
}
mv_ial_block_requests(pAdapter, i);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
if (scsi_add_host(pAdapter->host[i]->scsihost, &pcidev->dev) != 0)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: scsi_add_host() failed.\n"
, pMvSataAdapter->adapterId);
free_irq (pcidev->irq, pMvSataAdapter);
#ifdef MV_SUPPORT_MSI
pci_disable_msi(pAdapter->pcidev);
#endif
kfree(pAdapter->ataScsiAdapterExt);
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
pci_release_regions(pcidev);
pci_disable_device(pcidev);
return -ENODEV;
}
#endif
}
pAdapter->stopAsyncTimer = MV_FALSE;
init_timer(&pAdapter->asyncStartTimer);
pAdapter->asyncStartTimer.data = (unsigned long)pAdapter;
pAdapter->asyncStartTimer.function = asyncStartTimerFunction;
pAdapter->asyncStartTimer.expires = jiffies + MV_LINUX_ASYNC_TIMER_PERIOD;
add_timer (&pAdapter->asyncStartTimer);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
for (i = 0; i < pAdapter->maxHosts; i++)
{
if ((pAdapter->activeHosts & (1 << i)) != 0)
{
scsi_scan_host(pAdapter->host[i]->scsihost);
}
}
/*Enable hotplug handler*/
atomic_set(&pAdapter->stopped, 0);
#endif
return 0;
}
#ifdef CONFIG_MV_INCLUDE_INTEG_SATA
static int __devinit mv_ial_init_soc_sata(void)
{
MV_SATA_ADAPTER *pMvSataAdapter;
IAL_ADAPTER_T *pAdapter;
MV_U8 i;
#if defined(CONFIG_MV78200) || defined(CONFIG_MV632X)
if (MV_FALSE == mvSocUnitIsMappedToThisCpu(SATA))
{
printk(KERN_INFO"Integrated SATA is not mapped to this CPU\n");
return -ENODEV;
}
#endif
// mvSataWinInit();
pAdapter = (IAL_ADAPTER_T*)kmalloc(sizeof(IAL_ADAPTER_T), GFP_ATOMIC);
if (pAdapter == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "IAL Adapter allocation failed\n");
return -ENOMEM;
}
pSocAdapter = pAdapter;
memset(pAdapter, 0, sizeof(IAL_ADAPTER_T));
pAdapter->activeHosts = 0;
if(MV_5182_DEV_ID == mvCtrlModelGet())
pAdapter->maxHosts = MV_SATA_5182_PORT_NUM;
else if(MV_5082_DEV_ID == mvCtrlModelGet())
pAdapter->maxHosts = MV_SATA_5082_PORT_NUM;
else if(MV_6082_DEV_ID == mvCtrlModelGet())
pAdapter->maxHosts = MV_SATA_6082_PORT_NUM;
#ifdef MV88F6281
else if(MV_6281_DEV_ID == mvCtrlModelGet())
pAdapter->maxHosts = MV_SATA_6281_PORT_NUM;
else if(MV_6192_DEV_ID == mvCtrlModelGet())
pAdapter->maxHosts = MV_SATA_6192_PORT_NUM;
else if(MV_6190_DEV_ID == mvCtrlModelGet())
pAdapter->maxHosts = MV_SATA_6190_PORT_NUM;
else if(MV_6282_DEV_ID == mvCtrlModelGet())
pAdapter->maxHosts = MV_SATA_6282_PORT_NUM;
#endif
else if ((mvCtrlModelGet() == MV_78100_DEV_ID) ||
(mvCtrlModelGet() == MV_78200_DEV_ID) ||
(mvCtrlModelGet() == MV_78XX0_DEV_ID))
pAdapter->maxHosts = MV_SATA_78XX0_PORT_NUM;
else if (mvCtrlModelGet() == MV_76100_DEV_ID)
pAdapter->maxHosts = MV_SATA_76100_PORT_NUM;
else if (mvCtrlModelGet() == MV_6323_DEV_ID)
pAdapter->maxHosts = MV_SATA_6323_PORT_NUM;
else if ((mvCtrlModelGet() == MV_6510_DEV_ID) ||
(mvCtrlModelGet() == MV_6530_DEV_ID) ||
(mvCtrlModelGet() == MV_6550_DEV_ID) ||
(mvCtrlModelGet() == MV_6560_DEV_ID))
pAdapter->maxHosts = MV_SATA_65XX_PORT_NUM;
for (i = 0; i < pAdapter->maxHosts; i++)
{
if (MV_FALSE == mvCtrlPwrClckGet(SATA_UNIT_ID, (MV_U32)i))
{
printk("Warning: SATA %d is powered off\n", i);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENOMEM;
}
}
for (i = 0; i < pAdapter->maxHosts; i++)
{
struct Scsi_Host *pshost = scsi_host_alloc(&driver_template, sizeof(IAL_HOST_T));
if (pshost == NULL)
{
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "Scsi_Host allocation failed\n");
return -ENOMEM;
}
pAdapter->host[i] = HOSTDATA(pshost);
memset(pAdapter->host[i], 0, sizeof(IAL_HOST_T));
pAdapter->host[i]->scsihost = pshost;
pAdapter->host[i]->pAdapter = pAdapter;
pAdapter->host[i]->channelIndex = (MV_U8)i;
pAdapter->activeHosts |= (1 << i);
}
pAdapter->pcidev = NULL;
pMvSataAdapter = &(pAdapter->mvSataAdapter);
pMvSataAdapter->IALData = pAdapter;
spin_lock_init (&pAdapter->adapter_lock);
for (i = 0; i < pAdapter->maxHosts; i++)
{
pAdapter->host[i]->scsi_cmnd_done_head = NULL;
pAdapter->host[i]->scsi_cmnd_done_tail = NULL;
}
pAdapter->host[0]->scsihost->base = 0/*pci_resource_start(pcidev, 0)*/;
for (i = 1; i < pAdapter->maxHosts; i++)
{
if (pAdapter->host[i] != NULL)
pAdapter->host[i]->scsihost->base = pAdapter->host[0]->scsihost->base;
}
pMvSataAdapter->adapterIoBaseAddress = (MV_BUS_ADDR_T)(INTER_REGS_BASE + MV_SATA_REGS_OFFSET -
0x20000);
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "io base address 0x%08lx\n",
(ulong)pMvSataAdapter->adapterIoBaseAddress);
pMvSataAdapter->adapterId = adapterId++;
/* get the revision ID */
pMvSataAdapter->pciConfigRevisionId = 0;
pMvSataAdapter->pciConfigDeviceId = mvCtrlModelGet();
if (set_device_regs(pMvSataAdapter, NULL))
{
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENOMEM;
}
/*Do not allow hotplug handler to work*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
init_MUTEX(&pAdapter->rescan_mutex);
atomic_set(&pAdapter->stopped, 1);
#endif
if (mvSataInitAdapter(pMvSataAdapter) == MV_FALSE)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,
"[%d]: core failed to initialize the adapter\n",
pMvSataAdapter->adapterId);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENOMEM;
}
if (mv_ial_lib_allocate_edma_queues(pAdapter))
{
mvLogMsg(MV_IAL_LOG_ID,MV_DEBUG_ERROR,
"Failed to allocate memory for EDMA queues\n");
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENOMEM;
}
for (i = 0; i < pAdapter->maxHosts; i++)
{
if ((pAdapter->activeHosts & (1 << i)) == 0)
{
continue;
}
if (mv_ial_lib_prd_init(pAdapter->host[i]))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,
"Failed to init PRD memory manager - host %d\n", i);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENOMEM;
}
}
pAdapter->ataScsiAdapterExt = (MV_SAL_ADAPTER_EXTENSION*)kmalloc(sizeof(MV_SAL_ADAPTER_EXTENSION),
GFP_ATOMIC);
if (pAdapter->ataScsiAdapterExt == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,"[%d]: out of memory, failed to allocate MV_SAL_ADAPTER_EXTENSION\n",
pAdapter->mvSataAdapter.adapterId);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENOMEM;
}
mvSataScsiInitAdapterExt(pAdapter->ataScsiAdapterExt,
pMvSataAdapter);
/* let SAL report only the BUS RESET UA event*/
pAdapter->ataScsiAdapterExt->UAMask = MV_BIT0;
/* enable device interrupts even if no storage devices connected now*/
if (request_irq(SATA_IRQ_NUM, mv_ial_lib_int_handler,
(IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED), "mvSata",
pAdapter) < 0)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: unable to allocate IRQ for controler\n",
pMvSataAdapter->adapterId);
kfree(pAdapter->ataScsiAdapterExt);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENOMEM;
}
for (i = 0; i < pAdapter->maxHosts; i++)
{
if ((pAdapter->activeHosts & (1 << i)) == 0)
{
continue;
}
pAdapter->host[i]->scsihost->irq = SATA_IRQ_NUM;
pAdapter->host[i]->scsihost->max_id = MV_SATA_PM_MAX_PORTS;
pAdapter->host[i]->scsihost->max_lun = 1;
pAdapter->host[i]->scsihost->max_channel = 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
pAdapter->host[i]->scsihost->select_queue_depths = mv_ial_ht_select_queue_depths;
#endif
}
if (MV_FALSE == mvAdapterStartInitialization(pMvSataAdapter,
&pAdapter->ialCommonExt,
pAdapter->ataScsiAdapterExt))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: mvAdapterStartInitialization"
" Failed\n", pMvSataAdapter->adapterId);
free_irq (SATA_IRQ_NUM, pMvSataAdapter);
kfree(pAdapter->ataScsiAdapterExt);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENOMEM;
}
for (i = 0; i < pAdapter->maxHosts; i++)
{
if ((pAdapter->activeHosts & (1 << i)) == 0)
{
continue;
}
mv_ial_block_requests(pAdapter, i);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
if (scsi_add_host(pAdapter->host[i]->scsihost, NULL) != 0)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: scsi_add_host() failed.\n"
, pMvSataAdapter->adapterId);
free_irq (SATA_IRQ_NUM , pMvSataAdapter);
kfree(pAdapter->ataScsiAdapterExt);
mv_ial_lib_free_edma_queues(pAdapter);
mv_ial_free_scsi_hosts(pAdapter, MV_TRUE);
return -ENODEV;
}
#endif
}
pAdapter->stopAsyncTimer = MV_FALSE;
init_timer(&pAdapter->asyncStartTimer);
pAdapter->asyncStartTimer.data = (unsigned long)pAdapter;
pAdapter->asyncStartTimer.function = asyncStartTimerFunction;
pAdapter->asyncStartTimer.expires = jiffies + MV_LINUX_ASYNC_TIMER_PERIOD;
add_timer (&pAdapter->asyncStartTimer);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
for (i = 0; i < pAdapter->maxHosts; i++)
{
if ((pAdapter->activeHosts & (1 << i)) != 0)
{
scsi_scan_host(pAdapter->host[i]->scsihost);
}
}
/*Enable hotplug handler*/
atomic_set(&pAdapter->stopped, 0);
#endif
return 0;
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
/****************************************************************
* Name: mv_ial_ht_detect
*
* Description: Detect and initialize our boards.
*
* Parameters: tpnt - Pointer to SCSI host template structure.
*
* Returns: Number of adapters installed.
*
****************************************************************/
int mv_ial_ht_detect (Scsi_Host_Template *tpnt)
{
int num_hosts=0;
struct pci_dev *pcidev = NULL;
int index;
struct pci_device_id *id = &mvSata_pci_table[0];
mv_ial_init_log();
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
if (!pci_present())
{
printk ("mvSata: PCI BIOS not present\n");
return 0;
}
#endif
if (sizeof(struct mv_comp_info) > sizeof(Scsi_Pointer))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "WARNING mv_comp_info must be "
"re-defined - its too big");
return -1;
}
index = 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_unlock_irq (&io_request_lock);
#endif
while (1)
{
if (id[index].device == 0)
{
break;
}
pcidev = NULL;
while ((pcidev = pci_find_device (MV_SATA_VENDOR_ID,
id[index].device, pcidev)) != NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "PCI device found, DeviceId 0x%x "
"BAR0=%lx\n",
id[index].device, pci_resource_start(pcidev,0));
if (mv_ial_probe_device(pcidev, &id[index]) == 0)
{
IAL_ADAPTER_T *pAdapter = pci_get_drvdata(pcidev);
num_hosts += pAdapter->maxHosts;
}
}
index ++;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq (&io_request_lock);
#endif
return num_hosts;
}
#endif
/****************************************************************
* Name: mv_ial_ht_release
*
* Description: release scsi host
*
* Parameters: SCpnt - Pointer to SCSI host structure.
*
* Returns: 0 on success, otherwise of failure.
*
****************************************************************/
int mv_ial_ht_release (struct Scsi_Host *pHost)
{
IAL_ADAPTER_T *pAdapter = MV_IAL_ADAPTER (pHost);
MV_U8 channel;
MV_SATA_ADAPTER * pMvSataAdapter = &pAdapter->mvSataAdapter;
unsigned long lock_flags;
struct scsi_cmnd *cmnds_done_list = NULL;
IAL_HOST_T *ial_host = HOSTDATA(pHost);
channel = ial_host->channelIndex;
pAdapter->activeHosts &= ~ (1 << channel);
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, ": release host %d\n", pHost->host_no);
spin_lock_irqsave (&pAdapter->adapter_lock, lock_flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
if (pAdapter->stopAsyncTimer != MV_TRUE)
{
/* Delete any pending timers */
pAdapter->stopAsyncTimer = MV_TRUE;
del_timer_sync(&pAdapter->asyncStartTimer);
}
#endif
if (pMvSataAdapter->sataChannel[channel])
{
mvSataDisableChannelDma(pMvSataAdapter, channel);
mvSataFlushDmaQueue(pMvSataAdapter, channel,
MV_FLUSH_TYPE_CALLBACK);
mv_ial_lib_free_channel(pAdapter, channel);
}
/* Check if there are commands in the done queue to be completed */
cmnds_done_list = mv_ial_lib_get_first_cmnd (pAdapter, channel);
if (cmnds_done_list)
{
unsigned long flags_io_request_lock;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irqsave(&io_request_lock, flags_io_request_lock);
#else
spin_lock_irqsave(ial_host->scsihost->host_lock, flags_io_request_lock);
#endif
mv_ial_lib_do_done(cmnds_done_list);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_unlock_irqrestore(&io_request_lock, flags_io_request_lock);
#else
spin_unlock_irqrestore(ial_host->scsihost->host_lock, flags_io_request_lock);
#endif
}
if (0 == pAdapter->activeHosts)
{
mvSataShutdownAdapter(pMvSataAdapter);
}
pAdapter->host[channel] = NULL;
mv_ial_lib_prd_destroy(ial_host);
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
scsi_remove_host(pHost);
scsi_host_put(pHost);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
if (0 == pAdapter->activeHosts)
{
struct pci_dev *dev = pAdapter->pcidev;
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG,
"[%d] freeing Adapter resources.\n", pAdapter->mvSataAdapter.adapterId);
free_irq (pAdapter->pcidev->irq, pMvSataAdapter);
#ifdef MV_SUPPORT_MSI
pci_disable_msi(pAdapter->pcidev);
#endif
kfree(pAdapter->ataScsiAdapterExt);
iounmap(pMvSataAdapter->adapterIoBaseAddress);
mv_ial_lib_free_edma_queues(pAdapter);
kfree(pAdapter);
pci_disable_device(dev);
}
#endif
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
static void __devexit mv_ial_remove_device(struct pci_dev *pdev)
{
IAL_ADAPTER_T *pAdapter = (pdev != NULL) ? pci_get_drvdata(pdev) : pSocAdapter;
int numhosts;
int i;
unsigned long lock_flags;
if (pAdapter == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_FATAL_ERROR,
"mv_ial_remove_device() No valid Adapter IAL structure found.\n");
return;
}
numhosts = pAdapter->maxHosts;
/*flush hotplug rescan worker*/
atomic_inc(&pAdapter->stopped);
flush_scheduled_work();
/* Delete any pending timers */
spin_lock_irqsave (&pAdapter->adapter_lock, lock_flags);
pAdapter->stopAsyncTimer = MV_TRUE;
del_timer_sync(&pAdapter->asyncStartTimer);
spin_unlock_irqrestore(&pAdapter->adapter_lock, lock_flags);
for (i = 0; i < numhosts; i++)
{
if (pAdapter->host[i] != NULL)
{
mv_ial_ht_release (pAdapter->host[i]->scsihost);
}
}
if (pdev != NULL) /* pci device */
{
free_irq (pAdapter->pcidev->irq, &pAdapter->mvSataAdapter);
#ifdef MV_SUPPORT_MSI
pci_disable_msi(pAdapter->pcidev);
#endif
kfree(pAdapter->ataScsiAdapterExt);
iounmap(pAdapter->mvSataAdapter.adapterIoBaseAddress);
mv_ial_lib_free_edma_queues(pAdapter);
kfree(pAdapter);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
#ifdef CONFIG_MV_INCLUDE_INTEG_SATA
else /* Soc sata*/
{
free_irq (SATA_IRQ_NUM, &pAdapter->mvSataAdapter);
kfree(pAdapter->ataScsiAdapterExt);
mv_ial_lib_free_edma_queues(pAdapter);
kfree(pAdapter);
}
#endif
}
#endif
/****************************************************************
* Name: mv_ial_ht_ata_cmd
*
* Description: handles mv_sata ata IOCTL special drive command (HDIO_DRIVE_CMD)
*
* Parameters: scsidev - Device to which we are issuing command
* arg - User provided data for issuing command
*
* Returns: 0 on success, otherwise of failure.
*
****************************************************************/
static int mv_ial_ht_ata_cmd(struct scsi_device *scsidev, void __user *arg)
{
int rc = 0;
u8 scsi_cmd[MAX_COMMAND_SIZE];
u8 args[4], *argbuf = NULL, *sensebuf = NULL;
int argsize = 0;
enum dma_data_direction data_dir;
int cmd_result;
if (arg == NULL)
return -EINVAL;
if (copy_from_user(args, arg, sizeof(args)))
return -EFAULT;
sensebuf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO);
if (!sensebuf)
return -ENOMEM;
memset(scsi_cmd, 0, sizeof(scsi_cmd));
if (args[3]) {
argsize = SECTOR_SIZE * args[3];
argbuf = kmalloc(argsize, GFP_KERNEL);
if (argbuf == NULL) {
rc = -ENOMEM;
goto error;
}
scsi_cmd[1] = (4 << 1); /* PIO Data-in */
scsi_cmd[2] = 0x0e; /* no off.line or cc, read from dev,
block count in sector count field */
data_dir = DMA_FROM_DEVICE;
} else {
scsi_cmd[1] = (3 << 1); /* Non-data */
scsi_cmd[2] = 0x20; /* cc but no off.line or data xfer */
data_dir = DMA_NONE;
}
scsi_cmd[0] = ATA_16;
scsi_cmd[4] = args[2];
if (args[0] == WIN_SMART) { /* hack -- ide driver does this too... */
scsi_cmd[6] = args[3];
scsi_cmd[8] = args[1];
scsi_cmd[10] = 0x4f;
scsi_cmd[12] = 0xc2;
} else {
scsi_cmd[6] = args[1];
}
scsi_cmd[14] = args[0];
/* Good values for timeout and retries? Values below
from scsi_ioctl_send_command() for default case... */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize,
sensebuf, (10*HZ), 5, 0);
#else
cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize,
sensebuf, (10*HZ), 5, 0, NULL);
#endif
if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */
u8 *desc = sensebuf + 8;
cmd_result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */
/* If we set cc then ATA pass-through will cause a
* check condition even if no error. Filter that. */
if (cmd_result & SAM_STAT_CHECK_CONDITION) {
struct scsi_sense_hdr sshdr;
scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE,
&sshdr);
if (sshdr.sense_key==0 &&
sshdr.asc==0 && sshdr.ascq==0)
cmd_result &= ~SAM_STAT_CHECK_CONDITION;
}
/* Send userspace a few ATA registers (same as drivers/ide) */
if (sensebuf[0] == 0x72 && /* format is "descriptor" */
desc[0] == 0x09 ) { /* code is "ATA Descriptor" */
args[0] = desc[13]; /* status */
args[1] = desc[3]; /* error */
args[2] = desc[5]; /* sector count (0:7) */
if (copy_to_user(arg, args, sizeof(args)))
rc = -EFAULT;
}
}
if (cmd_result) {
rc = -EIO;
goto error;
}
if ((argbuf) && copy_to_user(arg + sizeof(args), argbuf, argsize))
rc = -EFAULT;
error:
if (sensebuf) kfree(sensebuf);
if (argbuf) kfree(argbuf);
return rc;
}
/****************************************************************
* Name: mv_ial_ht_ioctl
*
* Description: mv_sata scsi ioctl
*
* Parameters: scsidev - Device to which we are issuing command
* cmd - ioctl command
* arg - User provided data for issuing command
*
* Returns: 0 on success, otherwise of failure.
*
****************************************************************/
int mv_ial_ht_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg)
{
int rc = -ENOTTY;
/* No idea how this happens.... */
if (!scsidev)
return -ENXIO;
if (arg == NULL)
return -EINVAL;
switch (cmd) {
case HDIO_DRIVE_CMD:
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
return -EACCES;
rc = mv_ial_ht_ata_cmd(scsidev, arg);
break;
default:
rc = -ENOTTY;
}
return rc;
}
/****************************************************************
* Name: mv_ial_ht_queuecommand
*
* Description: Process a queued command from the SCSI manager.
*
* Parameters: SCpnt - Pointer to SCSI command structure.
* done - Pointer to done function to call.
*
* Returns: Status code.
*
****************************************************************/
int mv_ial_ht_queuecommand (struct scsi_cmnd * SCpnt, void (*done) (struct scsi_cmnd *))
{
IAL_ADAPTER_T *pAdapter = MV_IAL_ADAPTER(SCpnt->device->host);
MV_SATA_ADAPTER *pMvSataAdapter;
IAL_HOST_T *pHost = HOSTDATA(SCpnt->device->host);
MV_U8 channel = pHost->channelIndex;
int build_prd_table = 0;
unchar *cmd = (unchar *) SCpnt->cmnd;
struct mv_comp_info *completion_info;
unsigned long lock_flags;
struct scsi_cmnd *cmnds_done_list = NULL;
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, " :queuecommand host=%d, bus=%d, channel=%d\n",
SCpnt->device->host->host_no,
SCpnt->device->channel,
channel);
if (done == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, ": in queuecommand, done function can't be NULL\n");
return 0;
}
if ((pAdapter == NULL) || (channel >= MV_SATA_CHANNELS_NUM)||
(pAdapter->host[channel] == NULL))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_FATAL_ERROR,": in queuecommand, "
"command queued for released host!!\n");
SCpnt->result = DID_NO_CONNECT << 16;
done(SCpnt);
return 0;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_unlock_irq (&io_request_lock);
#else
spin_unlock_irq(pHost->scsihost->host_lock);
#endif
spin_lock_irqsave (&pAdapter->adapter_lock, lock_flags);
if (SCpnt->retries > 0)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,": retry command host=%d, bus=%d"
" SCpnt = %p\n", SCpnt->device->host->host_no, channel, SCpnt);
}
if (MV_TRUE == pAdapter->host[channel]->hostBlocked)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,": command received for "
"blocked host=%d, bus=%d, channel=%d, SCpnt = %p\n",
SCpnt->device->host->host_no,
SCpnt->device->channel,
channel, SCpnt);
#if 0
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq (&io_request_lock);
#else
spin_lock_irq(pHost->scsihost->host_lock);
#endif
return SCSI_MLQUEUE_HOST_BUSY;
#endif
}
pMvSataAdapter = &pAdapter->mvSataAdapter;
SCpnt->result = DID_ERROR << 16;
SCpnt->scsi_done = done;
completion_info = ( struct mv_comp_info *) &(SCpnt->SCp);
completion_info->pSALBlock =
(MV_SATA_SCSI_CMD_BLOCK *) kmalloc(sizeof(MV_SATA_SCSI_CMD_BLOCK),
GFP_ATOMIC);
if (completion_info->pSALBlock == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "in queuecommand: Failed to allocate SAL Block\n");
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
done(SCpnt);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq (&io_request_lock);
#else
spin_lock_irq(pHost->scsihost->host_lock);
#endif
return -1;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
completion_info->kmap_buffer = 0;
#endif
/* prepare the SAL Block paramters*/
if ((*cmd == READ_6) || (*cmd == READ_10) || (*cmd == READ_16) ||
(*cmd == WRITE_6) || (*cmd == WRITE_10) || (*cmd == WRITE_16))
{
build_prd_table = 1;
}
else if((pAdapter->ataScsiAdapterExt->ataDriveData[channel][SCpnt->device->id].identifyInfo.deviceType == MV_SATA_DEVICE_TYPE_ATAPI_DEVICE) && use_sg(SCpnt))
{
/*
for the 60x1 devices don't use DMA for control commands as the BMDMA will
not write date to DRAM in case on underrun.
*/
if(!(pAdapter->mvSataAdapter.sataAdapterGeneration == MV_SATA_GEN_II)){
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG,
"in queuecommand: PRD for non data command for ATAPI device\n");
build_prd_table = 1;
}
}
if((pAdapter->ataScsiAdapterExt->ataDriveData[channel][SCpnt->device->id].identifyInfo.deviceType == MV_SATA_DEVICE_TYPE_ATAPI_DEVICE))
{
BUG_ON(((unsigned int)SCpnt->cmnd) & 0x1);
}
completion_info->pSALBlock->singleDataRegion = MV_FALSE;
/* prepare the SAL Block paramters*/
if(build_prd_table)
{
if(pAdapter->ataScsiAdapterExt->ataDriveData[channel][SCpnt->device->id].identifyInfo.deviceType == MV_SATA_DEVICE_TYPE_ATAPI_DEVICE)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "in queuecommand: Data command for ATAPI device\n");
}
else
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "in queuecommand: Data command for ATA device\n");
}
if (mv_ial_lib_generate_prd(pMvSataAdapter, SCpnt, completion_info))
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "in queuecommand: illegal requested buffer\n");
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
done(SCpnt);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq (&io_request_lock);
#else
spin_lock_irq(pHost->scsihost->host_lock);
#endif
return -1;
}
completion_info->pSALBlock->pDataBuffer = NULL;
}
else
{
completion_info->cpu_PRDpnt = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
if (use_sg(SCpnt))
{
completion_info->kmap_buffer = 1;
}
#else
completion_info->pSALBlock->pDataBuffer = SCpnt->request_buffer;
#endif
}
completion_info->SCpnt = SCpnt;
completion_info->pSALBlock->bus = channel;
completion_info->pSALBlock->target = SCpnt->device->id;
completion_info->pSALBlock->lun = SCpnt->device->lun;
completion_info->pSALBlock->pSalAdapterExtension = pAdapter->ataScsiAdapterExt;
completion_info->pSALBlock->pIalAdapterExtension = &pAdapter->ialCommonExt;
completion_info->pSALBlock->completionCallBack = IALCompletion;
completion_info->pSALBlock->IALData = SCpnt;
completion_info->pSALBlock->dataBufferLength = scsi_bufflen(SCpnt);
completion_info->pSALBlock->pSenseBuffer = SCpnt->sense_buffer;
completion_info->pSALBlock->ScsiCdb = SCpnt->cmnd;
completion_info->pSALBlock->ScsiCdbLength = SCpnt->cmd_len;
completion_info->pSALBlock->senseBufferLength = SCSI_SENSE_BUFFERSIZE;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
if (completion_info->kmap_buffer)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
struct scatterlist *sg;
sg = (struct scatterlist *) SCpnt->request_buffer;
#endif
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "SCpnt %p, cmd %x need to use"
" temp data buffer.lengh %d \n", SCpnt, *cmd , scsi_bufflen(SCpnt));
completion_info->pSALBlock->pDataBuffer = kmalloc(scsi_bufflen(SCpnt), GFP_ATOMIC);
if (completion_info->pSALBlock->pDataBuffer == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "in queuecommand: Failed to allocate temp buffer for kmap\n");
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
done(SCpnt);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq (&io_request_lock);
#else
spin_lock_irq(pHost->scsihost->host_lock);
#endif
return -1;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
completion_info->pSALBlock->dataBufferLength = sg->length;
#else
completion_info->pSALBlock->dataBufferLength = scsi_bufflen(SCpnt);
#endif
if( SCpnt->sc_data_direction == DMA_TO_DEVICE)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
struct scatterlist *sg;
MV_U8* pBuffer;
sg = (struct scatterlist *) SCpnt->request_buffer;
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "SCpnt %p, cmd %x kmap temp data buffer and copy data.lengh %d \n", SCpnt, *cmd ,sg->length);
pBuffer = kmap_atomic(sg->page, KM_USER0) + sg->offset;
memcpy(completion_info->pSALBlock->pDataBuffer, pBuffer , sg->length);
kunmap_atomic(pBuffer - sg->offset, KM_USER0);
#else
scsi_sg_copy_to_buffer(SCpnt, completion_info->pSALBlock->pDataBuffer,
scsi_bufflen(SCpnt));
#endif
}
}
#endif
switch(SCpnt->sc_data_direction)
{
case DMA_FROM_DEVICE:
completion_info->pSALBlock->dataDirection = MV_SCSI_COMMAND_DATA_DIRECTION_IN;
break;
case DMA_TO_DEVICE:
completion_info->pSALBlock->dataDirection = MV_SCSI_COMMAND_DATA_DIRECTION_OUT;
break;
default:
completion_info->pSALBlock->dataDirection = MV_SCSI_COMMAND_DATA_DIRECTION_NON;
}
if (*cmd != SCSI_OPCODE_MVSATA_SMART)
{
mvExecuteScsiCommand(completion_info->pSALBlock, MV_TRUE);
}
else
{
mvScsiAtaSendSmartCommand(pMvSataAdapter, completion_info->pSALBlock);
}
/*
* Check if there is valid commands to be completed. This is usually
* an immediate completed commands such as INQUIRY etc...
*/
cmnds_done_list = mv_ial_lib_get_first_cmnd(pAdapter, channel);
spin_unlock_irqrestore(&pAdapter->adapter_lock, lock_flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq (&io_request_lock);
#else
spin_lock_irq(pHost->scsihost->host_lock);
#endif
if (cmnds_done_list)
{
mv_ial_lib_do_done(cmnds_done_list);
}
return 0;
}
/****************************************************************
* Name: mv_ial_ht_bus_reset
*
* Description: reset given devise, all pending commands will be aborted
* with status DID_RESET.
*
* Parameters: SCpnt - Pointer to SCSI command structure.
*
* Returns: Status code.
*
****************************************************************/
int mv_ial_ht_bus_reset (struct scsi_cmnd *SCpnt)
{
IAL_ADAPTER_T *pAdapter = MV_IAL_ADAPTER(SCpnt->device->host);
MV_SATA_ADAPTER *pMvSataAdapter = &pAdapter->mvSataAdapter;
IAL_HOST_T *pHost = HOSTDATA(SCpnt->device->host);
MV_U8 channel = pHost->channelIndex;
unsigned long lock_flags;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_unlock_irq (&io_request_lock);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
spin_unlock_irq(pHost->scsihost->host_lock);
#endif
spin_lock_irqsave (&pAdapter->adapter_lock, lock_flags);
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "Bus Reset: host=%d, channel=%d, target=%d\n",
SCpnt->device->host->host_no, SCpnt->device->channel, SCpnt->device->id);
if (pMvSataAdapter->sataChannel[channel] == NULL)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "trying to reset disabled channel, host=%d, channel=%d\n",
SCpnt->device->host->host_no, channel);
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq(&io_request_lock);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
spin_lock_irq(pHost->scsihost->host_lock);
#endif
return FAILED;
}
mvSataDisableChannelDma(pMvSataAdapter, channel);
/* Flush pending commands */
mvSataFlushDmaQueue (pMvSataAdapter, channel, MV_FLUSH_TYPE_CALLBACK);
/* Hardware reset channel */
mvSataChannelHardReset(pMvSataAdapter, channel);
if (pMvSataAdapter->sataChannel[channel])
{
mvRestartChannel(&pAdapter->ialCommonExt, channel,
pAdapter->ataScsiAdapterExt, MV_TRUE);
mv_ial_block_requests(pAdapter, channel);
}
/* don't call scsi done for the commands on this channel*/
mv_ial_lib_get_first_cmnd(pAdapter, channel);
spin_unlock_irqrestore(&pAdapter->adapter_lock, lock_flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq(&io_request_lock);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
spin_lock_irq(pHost->scsihost->host_lock);
#endif
return SUCCESS;
}
static MV_VOID mvAta2HostString(IN MV_U16 *source,
OUT MV_U16 *target,
IN MV_U32 wordsCount
)
{
MV_U32 i;
for (i=0 ; i < wordsCount; i++)
{
target[i] = MV_LE16_TO_CPU(target[i]);
}
}
/****************************************************************
* Name: mv_ial_ht_proc_info
*
* Description: /proc file
*
* Parameters:
*
* Returns:
*
****************************************************************/
int mv_ial_ht_proc_info(struct Scsi_Host *pshost,
char *buffer, char **start, off_t offset,
int length, int inout)
{
int len = 0, temp, pmPort;
IAL_ADAPTER_T *pAdapter;
MV_SATA_ADAPTER *pMvSataAdapter;
IAL_HOST_T *pHost = HOSTDATA(pshost);
unsigned long lock_flags;
pAdapter = MV_IAL_ADAPTER(pshost);
pMvSataAdapter = &pAdapter->mvSataAdapter;
temp = pHost->channelIndex;
spin_lock_irqsave (&pAdapter->adapter_lock, lock_flags);
if (inout == 1)
{ /* Writing to file */
/* The format is 'int_coal <sata unit> <coal_threshold> <timeout>' */
int i;
/* Check signature 'int_coal' at start of buffer */
if (!strncmp (buffer, "int_coal", strlen ("int_coal")))
{
int sata_unit;
u32 time_thre, coal_thre;
i = sscanf (buffer + strlen ("int_coal"), "%d %d %d\n",
&sata_unit, &coal_thre, &time_thre);
if (i == 3)
{ /* Three matched inputs */
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d]: Modifying interrupt coalescing of unit %d to %d threshold and %d timer\n",pMvSataAdapter->adapterId, sata_unit, coal_thre, time_thre);
mvSataSetIntCoalParams (pMvSataAdapter, sata_unit, coal_thre, time_thre);
}
else
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d]: Error in interrupt coalescing parameters\n",
pMvSataAdapter->adapterId);
}
}
/* Check signature 'sata_phy_shutdown' at start of buffer */
else if (!strncmp (buffer, "sata_phy_shutdown", strlen ("sata_phy_shutdown")))
{
int sata_phy;
i = sscanf (buffer + strlen ("sata_phy_shutdown"), "%d\n", &sata_phy);
if (i == 1)
{ /* Three matched inputs */
if (mvSataIsStorageDeviceConnected (pMvSataAdapter, sata_phy, NULL) == MV_TRUE)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: Warning - shutting down a phy that is connected to a storage device\n", pMvSataAdapter->adapterId, sata_phy);
}
if (mvSataChannelPhyShutdown (pMvSataAdapter, sata_phy) == MV_TRUE)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: Shutting down SATA phy\n", pMvSataAdapter->adapterId, sata_phy);
}
}
else
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d]: Error in shutting down SATA phy parameters\n",
pMvSataAdapter->adapterId);
}
}
else if (!strncmp (buffer, "sata_phy_powerup", strlen ("sata_phy_powerup")))
{
int sata_phy;
i = sscanf (buffer + strlen ("sata_phy_powerup"), "%d\n", &sata_phy);
if (i == 1)
{ /* Three matched inputs */
if (mvSataChannelPhyPowerOn (pMvSataAdapter, sata_phy) == MV_TRUE)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: Turning on SATA phy\n", pMvSataAdapter->adapterId, sata_phy);
}
}
else
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG,"[%d]: Error in powering up SATA phy parameters\n",
pMvSataAdapter->adapterId);
}
}
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
return length;
}
else
{ /* Reading from file */
int i;
/*
* Write to the file the time stamp which is difference between last
* jiffies and current one. Next to it write the HZ parameter which
* indicates how many time jiffies parameter is incremented in a
* second.
*/
len += snprintf (buffer + len,length - len, "%s\n", mv_ial_proc_version);
if (len >= length)
{
goto out;
}
len += snprintf (buffer + len,length - len, "\nTimeStamp :\n%ld\t%d\n",
jiffies, HZ);
if (len >= length)
{
goto out;
}
/* Write the number of interrupts this adapter generated within the
* sampling time.
*/
len += snprintf (buffer + len,length - len, "\nNumber of interrupts generated by the adapter is : \n%d\n",
pAdapter->procNumOfInterrupts);
if (len >= length)
{
goto out;
}
if (pAdapter->pcidev)
{
len += snprintf (buffer + len, length - len, "\nPCI location: Bus %d, Slot %d\n",
pAdapter->pcidev->bus->number,
PCI_SLOT(pAdapter->pcidev->devfn));
if (len >= length)
{
goto out;
}
len += snprintf (buffer + len, length - len, "DeviceID: %x, Rev %x,"
" adapterId %d, channel %d \n",
pAdapter->mvSataAdapter.pciConfigDeviceId,
pAdapter->mvSataAdapter.pciConfigRevisionId,
pAdapter->mvSataAdapter.adapterId,
pHost->channelIndex);
if (len >= length)
{
goto out;
}
}
else /*integrated sata*/
{
len += snprintf (buffer + len, length - len, "\nIntegrated Sata adapterId %d, "
"channel %d\n",pAdapter->mvSataAdapter.adapterId,
pHost->channelIndex);
if (len >= length)
{
goto out;
}
}
if (pMvSataAdapter->sataChannel[temp])
{
if (pMvSataAdapter->sataChannel[temp]->deviceType == MV_SATA_DEVICE_TYPE_PM)
{
len += snprintf (buffer + len, length - len,
"Port Multiplier connected, switching mode: %s\n",
(pMvSataAdapter->sataChannel[temp]->FBSEnabled == MV_TRUE) ?
"FBS":"CBS");
if (len >= length)
{
goto out;
}
}
}
/*
* Check if channel connected.
* If not connected write -1 on a line
* If connected, write a line that has -
* 1.. Adapter number
* 2.. SCSI channel number (equivalent to SATA channel number).
* 3.. ID
* 4.. LUN (always 0)
* 5.. vendor name
* 6.. number of outstanding commands accumulated
* 7.. total sampling of outstanding commands
* 8.. total sectors transferred
* 9.. flag if queued / non-queued (1/0)
* 10. flag if LBA 48 or not (1/0)
* 11. flag if the storage device can be removed or not
* (1 means can't be removed / 0 can be removed).
*/
len += snprintf (buffer + len,length - len,"\n%s\t%s\t%s\t%s\t%s\t%s\t%s\t\t%s\t%s\n",
"Adapter", "Channel", "Id", "LUN", "TO", "TS", "Vendor",
"Mode", "LBA48");
if (len >= length)
{
goto out;
}
if ((len + 100) >= length)
{
goto out;
}
for (i = 0 ; i < 80 ; i++)
buffer [len + i] = '-';
len += i;
len += snprintf (buffer + len,length - len, "\n");
if (len >= length)
{
goto out;
}
if (pMvSataAdapter->sataChannel[temp])
{
for (pmPort = 0; pmPort < MV_SATA_PM_MAX_PORTS; pmPort++)
{
if (pmPort > 0 &&
(pMvSataAdapter->sataChannel[temp]->deviceType != MV_SATA_DEVICE_TYPE_PM))
{
break;
}
if (pAdapter->ataScsiAdapterExt->ataDriveData[temp][pmPort].driveReady == MV_FALSE)
{
continue;
}
len += snprintf (buffer + len,length - len, "%d\t%d\t%d\t%d\t%u\t%u\t",
pAdapter->mvSataAdapter.adapterId, temp, pmPort, 0,
pAdapter->ataScsiAdapterExt->ataDriveData[temp][pmPort].stats.totalIOs,
pAdapter->ataScsiAdapterExt->ataDriveData[temp][pmPort].stats.totalSectorsTransferred);
if (len >= length)
{
goto out;
}
/*
* Copy first 10 characters of the vendor name from the IDENTIFY
* DEVICE ATA command result buffer
*/
if ((len+10) >= length)
{
goto out;
}
memcpy (buffer+len,
pAdapter->ataScsiAdapterExt->ataDriveData[temp][pmPort].identifyInfo.model, 10);
mvAta2HostString((MV_U16 *)(buffer+len), (MV_U16 *)(buffer+len), 5);
/*
* Clean spaces in vendor name and swap odd and even characters.
* The swap is due to the format of the IDENTIFY DEVICE command
*/
for (i=0 ; i<10 ; i+=2)
{
char ch = buffer[len + i];
buffer[len + i] = buffer[len+1 + i];
buffer[len+1 + i] = ch;
if (buffer[len + i] == ' ')
{
buffer[len + i + 1] = ' ';
break;
}
if (buffer[len+1 + i] == ' ')
{
break;
}
}
if ((len + 10) >= length)
{
goto out;
}
for (; i < 10; i++)
{
buffer[len + i] = ' ';
}
len += 10;
len += snprintf (buffer + len,length - len, "\t%s \t%d\n",
(pMvSataAdapter->sataChannel[temp]->queuedDMA == MV_EDMA_MODE_QUEUED) ?
"TCQ" : (pMvSataAdapter->sataChannel[temp]->queuedDMA == MV_EDMA_MODE_NATIVE_QUEUING) ?
"NCQ":"Normal",
(pAdapter->ataScsiAdapterExt->ataDriveData[temp][pmPort].identifyInfo.LBA48Supported == MV_TRUE) ? 1 : 0);
if (len >= length)
{
goto out;
}
}
}
if ((!pMvSataAdapter->sataChannel[temp]) &&
(mvSataIsStorageDeviceConnected (pMvSataAdapter, temp, NULL) == MV_TRUE))
len += snprintf (buffer + len,length - len, "Storage device connected to channel %d is malfunction\n", temp);
if (len >= length)
{
goto out;
}
len += snprintf (buffer + len,length - len,"\n\n\nTO - Total Outstanding commands accumulated\n");
if (len >= length)
{
goto out;
}
len += snprintf (buffer + len,length - len,"TSA - Total number of IOs accumulated\n");
if (len >= length)
{
goto out;
}
len += snprintf (buffer + len,length - len,"TS - Total number of sectors transferred (both read/write)\n");
if (len >= length)
{
goto out;
}
len += snprintf (buffer + len,length - len,"Mode - EDMA mode (TCQ|NCQ|Normal)\n");
if (len >= length)
{
goto out;
}
len += snprintf (buffer + len,length - len,"LBA48 - Large Block Address 48 feature set enabled\n");
if (len >= length)
{
goto out;
}
}
out:
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
return(len);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
/****************************************************************
* Name: mv_ial_ht_proc_info (kernel version < 2.6)
*
* Description: /proc file
*
* Parameters:
*
* Returns:
*
****************************************************************/
int mv_ial_ht_proc_info24(char *buffer, char **start, off_t offset,
int length, int inode, int inout)
{
struct Scsi_Host *pshost = 0;
for (pshost = scsi_hostlist; pshost; pshost = pshost->next)
{
if (pshost->host_no == inode)
{
return mv_ial_ht_proc_info(pshost, buffer, start,
offset,length, inout);
}
}
return -EINVAL;
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
static int mv_ial_ht_slave_configure (struct scsi_device* pDevs)
{
IAL_HOST_T *pHost = HOSTDATA (pDevs->host);
struct Scsi_Host* scsiHost = pDevs->host;
struct scsi_device* pDevice = NULL;
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d]: slave configure\n",
pHost->pAdapter->mvSataAdapter.adapterId);
if (pHost->use128Entries == MV_TRUE)
{
pHost->scsihost->can_queue = MV_SATA_GEN2E_SW_QUEUE_SIZE;
}
else
{
pHost->scsihost->can_queue = MV_SATA_SW_QUEUE_SIZE;
}
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d %d]: adjust host[channel] queue depth"
" to %d\n", pHost->pAdapter->mvSataAdapter.adapterId, pHost->channelIndex,
pHost->scsihost->can_queue);
shost_for_each_device(pDevice, scsiHost)
{
int deviceQDepth = 2;
if(pHost->pAdapter->ataScsiAdapterExt->ataDriveData[pHost->channelIndex][pDevice->id].identifyInfo.deviceType == MV_SATA_DEVICE_TYPE_ATAPI_DEVICE)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d %d %d]: ATAPI device found\n",
pHost->pAdapter->mvSataAdapter.adapterId, pHost->channelIndex,
pDevice->id);
pDevice->use_10_for_rw = 1;
pDevice->use_10_for_ms = 1;
scsi_adjust_queue_depth(pDevice, 0, 1);
// pHost->scsihost->max_cmd_len = 12;
blk_queue_max_sectors(pDevice->request_queue, 256);
}
else
{
if (pHost->mode != MV_EDMA_MODE_NOT_QUEUED)
{
deviceQDepth = 31;
if (pHost->scsihost->can_queue >= 32)
{
deviceQDepth = 32;
}
}
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d %d %d]: adjust device queue "
"depth to %d\n", pHost->pAdapter->mvSataAdapter.adapterId,
pDevice->channel, pDevice->id, deviceQDepth);
scsi_adjust_queue_depth(pDevice, MSG_SIMPLE_TAG, deviceQDepth);
#ifdef MV_SUPPORT_1MBYTE_IOS
if(pHost->pAdapter->ataScsiAdapterExt->ataDriveData[pHost->channelIndex][pDevice->id].identifyInfo.LBA48Supported == MV_TRUE)
{
blk_queue_max_sectors(pDevice->request_queue, 2048);
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d %d %d]: set device max sectors to 2048 \n",
pHost->pAdapter->mvSataAdapter.adapterId,
pHost->channelIndex, pDevice->id);
}
#endif
}
}
scsiHost->max_cmd_len = 16;
return 0;
}
#else
static void mv_ial_ht_select_queue_depths (struct Scsi_Host* pHost,
struct scsi_device* pDevs)
{
IAL_HOST_T *ial_host = HOSTDATA (pHost);
struct scsi_device* pDevice;
if (ial_host != NULL)
{
/* linux 2.4 queue depth is not tunable, so we set the device queue */
/* to the max value (MV_SATA_SW_QUEUE_SIZE), and limit the number queued */
/* commands using the cmd_per_lun */
/* set can_queue to the max number of queued commands per host (sata */
/* channel). This may casue startvation if PortMultiplier is connected*/
pHost->cmd_per_lun = 31;
if (ial_host->mode != MV_EDMA_MODE_NOT_QUEUED)
{
if (ial_host->use128Entries == MV_TRUE)
{
pHost->can_queue = MV_SATA_GEN2E_SW_QUEUE_SIZE;
pHost->cmd_per_lun = 32;
}
else
{
pHost->can_queue = MV_SATA_SW_QUEUE_SIZE;
}
}
else
{
pHost->can_queue = MV_DEFAULT_QUEUE_DEPTH;
}
/*always allocate the max number of commands */
for (pDevice = pDevs; pDevice; pDevice = pDevice->next)
{
if (pDevice->host == pHost)
{
pDevice->queue_depth = MV_SATA_SW_QUEUE_SIZE;
}
}
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d %d]: adjust queue depth to %d\n",
ial_host->pAdapter->mvSataAdapter.adapterId,
ial_host->channelIndex,
pHost->can_queue);
}
}
#endif
int mv_ial_ht_abort(struct scsi_cmnd *SCpnt)
{
IAL_ADAPTER_T *pAdapter;
IAL_HOST_T *pHost;
MV_SATA_ADAPTER *pMvSataAdapter;
MV_U8 channel;
unsigned long lock_flags;
struct scsi_cmnd *cmnds_done_list = NULL;
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "abort command %p\n", SCpnt);
if (SCpnt == NULL)
{
return FAILED;
}
pHost = HOSTDATA(SCpnt->device->host);
channel = pHost->channelIndex;
pAdapter = MV_IAL_ADAPTER(SCpnt->device->host);
pMvSataAdapter = &pAdapter->mvSataAdapter;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
if (SCpnt->serial_number != SCpnt->serial_number_at_timeout)
{
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d %d %d]: abort failed, "
"serial number mismatch\n",SCpnt->device->host->host_no,
channel, SCpnt->device->id);
return FAILED;
}
spin_unlock_irq (&io_request_lock);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
spin_unlock_irq (pHost->scsihost->host_lock);
#endif
spin_lock_irqsave (&pAdapter->adapter_lock, lock_flags);
mvRestartChannel(&pAdapter->ialCommonExt, channel,
pAdapter->ataScsiAdapterExt, MV_TRUE);
mv_ial_block_requests(pAdapter, channel);
cmnds_done_list = mv_ial_lib_get_first_cmnd(pAdapter, channel);
spin_unlock_irqrestore (&pAdapter->adapter_lock, lock_flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
spin_lock_irq(&io_request_lock);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
spin_lock_irq (pHost->scsihost->host_lock);
#endif
if (cmnds_done_list)
{
scsi_report_bus_reset(SCpnt->device->host, SCpnt->device->channel);
mv_ial_lib_do_done(cmnds_done_list);
return SUCCESS;
}
mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d %d %d]: command abort failed\n",
SCpnt->device->host->host_no, SCpnt->device->channel, SCpnt->device->id);
return FAILED;
}
Scsi_Host_Template driver_template = mvSata;
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Marvell Serial ATA PCI-X Adapter");
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#include "scsi_module.c"
#endif