| /******************************************************************************* |
| 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 - mvLinuxIalLib.c |
| * |
| * DESCRIPTION: |
| * implementation for linux IAL lib functions. |
| * |
| * DEPENDENCIES: |
| * mvLinuxIalLib.h |
| * mvLinuxIalHt.h |
| * |
| * |
| *******************************************************************************/ |
| |
| /* includes */ |
| #include <linux/version.h> |
| #include <linux/kernel.h> |
| #include <linux/autoconf.h> |
| #include <linux/types.h> |
| #include <linux/slab.h> |
| #include <linux/pci.h> |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) |
| #include <linux/workqueue.h> |
| #endif |
| |
| #include "mvLinuxIalLib.h" |
| #include "mvIALCommon.h" |
| |
| #ifndef scsi_to_pci_dma_dir |
| #define scsi_to_pci_dma_dir(scsi_dir) ((int)(scsi_dir)) |
| #endif |
| |
| |
| /* Connect / disconnect timers. */ |
| /* Note that the disconnect timer should be smaller than the SCSI */ |
| /* subsystem timer. */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) |
| struct rescan_wrapper |
| { |
| struct work_struct work; |
| IAL_ADAPTER_T *pAdapter; |
| MV_U8 channelIndex; |
| MV_U16 targetsToRemove; |
| MV_U16 targetsToAdd; |
| }; |
| #endif |
| |
| |
| static void *mv_ial_lib_prd_allocate(IAL_HOST_T *pHost); |
| |
| static int mv_ial_lib_add_buffer_to_prd_table(MV_SATA_ADAPTER *pMvSataAdapter, |
| MV_SATA_EDMA_PRD_ENTRY *pPRD_table, |
| int table_size, |
| int *count, dma_addr_t buf_addr, |
| unsigned int buf_len, int isEOT); |
| |
| void release_ata_mem(struct mv_comp_info * pInfo); |
| |
| dma_addr_t inline pci64_map_page(struct pci_dev *hwdev, void* address, |
| size_t size, int direction) |
| |
| { |
| dma_addr_t mm = pci_map_page(hwdev, virt_to_page(address), |
| ((unsigned long)address & ~PAGE_MASK), |
| size, direction); |
| return mm; |
| } |
| |
| |
| void inline pci64_unmap_page(struct pci_dev *hwdev, dma_addr_t address, |
| size_t size, int direction) |
| { |
| pci_unmap_page(hwdev, address, size, direction); |
| } |
| |
| |
| int mv_ial_lib_prd_init(IAL_HOST_T *pHost) |
| { |
| MV_U8 i; |
| MV_U32 boolSize = MV_SATA_SW_QUEUE_SIZE; |
| |
| if ((pHost->pAdapter->mvSataAdapter.sataAdapterGeneration >= MV_SATA_GEN_IIE)&& |
| (pHost->pAdapter->mvSataAdapter.pciConfigDeviceId != MV_SATA_DEVICE_ID_6082)) |
| { |
| boolSize = MV_SATA_GEN2E_SW_QUEUE_SIZE; |
| } |
| /* |
| * Allocate PRD Pool - |
| * Since the driver supports 64 SG table, then each PRD table can go upto |
| * 1KByte (64 entries * 16byte) |
| */ |
| for (i = 0 ; i < boolSize; i++) |
| { |
| pHost->prdPool[i] = kmalloc ((MV_EDMA_PRD_ENTRY_SIZE * MV_PRD_TABLE_SIZE * 2) |
| + 16, |
| GFP_KERNEL); |
| if (pHost->prdPool[i] == NULL) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d %d]: Could not allocate PRD pool\n", |
| pHost->pAdapter->mvSataAdapter.adapterId, pHost->channelIndex); |
| return -1; |
| } |
| pHost->prdPoolAligned[i] = (void *)(((ulong)(pHost->prdPool[i]) + 15 ) & ~0xf); |
| } |
| |
| pHost->freePRDsNum = boolSize; |
| |
| return 0; |
| } |
| |
| static void *mv_ial_lib_prd_allocate(IAL_HOST_T *pHost) |
| { |
| return pHost->prdPoolAligned[--pHost->freePRDsNum]; |
| } |
| |
| int mv_ial_lib_prd_free(IAL_HOST_T *pHost, int size, dma_addr_t dmaPtr, |
| void *cpuPtr) |
| { |
| pci64_unmap_page(pHost->pAdapter->pcidev, |
| dmaPtr, |
| size * MV_EDMA_PRD_ENTRY_SIZE, |
| PCI_DMA_TODEVICE); |
| |
| pHost->prdPoolAligned[pHost->freePRDsNum++] = cpuPtr; |
| return 0; |
| } |
| |
| |
| int mv_ial_lib_prd_destroy(IAL_HOST_T *pHost) |
| { |
| MV_U8 temp; |
| MV_U32 boolSize = MV_SATA_SW_QUEUE_SIZE; |
| |
| if ((pHost->pAdapter->mvSataAdapter.sataAdapterGeneration >= MV_SATA_GEN_IIE)&& |
| (pHost->pAdapter->mvSataAdapter.pciConfigDeviceId != MV_SATA_DEVICE_ID_6082)) |
| { |
| boolSize = MV_SATA_GEN2E_SW_QUEUE_SIZE; |
| } |
| |
| for (temp = 0; temp < boolSize; temp++) |
| { |
| if (pHost->prdPool[temp] != NULL) |
| { |
| kfree (pHost->prdPool[temp]); |
| } |
| } |
| return 0; |
| } |
| |
| |
| /******************************************************************************* |
| * Name: mv_ial_lib_add_done_queue |
| * |
| * Description: Add scsi_cmnd to done list. Caller must take care of |
| * adapter_lock locking. |
| * |
| * Parameters: pAdapter - Adapter data structure |
| * scsi_cmnd - SCSI command data sturcture |
| * |
| ******************************************************************************/ |
| void mv_ial_lib_add_done_queue (struct IALAdapter *pAdapter, |
| MV_U8 channel, |
| struct scsi_cmnd *scsi_cmnd) |
| { |
| /* Put new command in the tail of the queue and make it point to NULL */ |
| ((struct mv_comp_info *)(&scsi_cmnd->SCp))->next_done = NULL; |
| |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Adding command @ %p to done queue, " |
| "channel %d\n",scsi_cmnd, channel); |
| if ((channel >= MV_SATA_CHANNELS_NUM) || (pAdapter->host[channel] == NULL)) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_FATAL_ERROR, "Adding command @ %p to " |
| " invalid channel (%d)\n",scsi_cmnd, channel); |
| } |
| if (pAdapter->host[channel]->scsi_cmnd_done_head == NULL) |
| { |
| /* First command to the queue */ |
| pAdapter->host[channel]->scsi_cmnd_done_head = scsi_cmnd; |
| pAdapter->host[channel]->scsi_cmnd_done_tail = scsi_cmnd; |
| } |
| else |
| { |
| /* We already have commands in the queue ; put this command in the tail */ |
| ((struct mv_comp_info *)(&pAdapter->host[channel]->scsi_cmnd_done_tail->SCp))->next_done = scsi_cmnd; |
| pAdapter->host[channel]->scsi_cmnd_done_tail = scsi_cmnd; |
| } |
| } |
| |
| /******************************************************************************* |
| * Name: mv_ial_lib_get_first_cmnd |
| * |
| * Description: Gets first scsi_cmnd from a chain of scsi commands to be |
| * completed, then sets NULL to head and tail. |
| * Caller must take care of adapter_lock locking. |
| * |
| * Parameters: pAdapter - Adapter data structure |
| * |
| * Return Value: Pointer to first scsi command in chain |
| ******************************************************************************/ |
| struct scsi_cmnd * mv_ial_lib_get_first_cmnd (struct IALAdapter *pAdapter, MV_U8 channel) |
| { |
| if (pAdapter->host[channel] != NULL) |
| { |
| struct scsi_cmnd *cmnd = pAdapter->host[channel]->scsi_cmnd_done_head; |
| pAdapter->host[channel]->scsi_cmnd_done_head = NULL; |
| pAdapter->host[channel]->scsi_cmnd_done_tail = NULL; |
| return cmnd; |
| } |
| return NULL; |
| } |
| |
| /******************************************************************************* |
| * Name: mv_ial_lib_do_done |
| * |
| * Description: Calls scsi_done of chain of scsi commands. |
| * Note that adapter_lock can be locked or unlocked, but |
| * caller must take care that io_request_lock is locked. |
| * |
| * Parameters: cmnd - First command in scsi commands chain |
| * |
| ******************************************************************************/ |
| void mv_ial_lib_do_done (struct scsi_cmnd *cmnd) |
| { |
| /* Call done function for all commands in queue */ |
| while (cmnd) |
| { |
| struct scsi_cmnd *temp; |
| temp = ((struct mv_comp_info *)(&cmnd->SCp))->next_done; |
| |
| if (cmnd->scsi_done == NULL) |
| { |
| return; |
| } |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Calling done to command @ %p " |
| "scsi_done = %p\n",cmnd, cmnd->scsi_done); |
| cmnd->scsi_done(cmnd); |
| cmnd = temp; |
| } |
| } |
| |
| |
| /******************************************************************************* |
| * Name: mv_ial_lib_free_channel |
| * |
| * Description: free allocated queues for the given channel |
| * |
| * Parameters: pMvSataAdapter - pointer to the adapter controler this |
| * channel connected to. |
| * channelNum - channel number. |
| * |
| ******************************************************************************/ |
| void mv_ial_lib_free_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum) |
| { |
| MV_SATA_CHANNEL *pMvSataChannel; |
| |
| if (channelNum >= pAdapter->mvSataAdapter.numberOfChannels) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: Bad channelNum=%d\n", |
| pAdapter->mvSataAdapter.adapterId, channelNum); |
| return; |
| } |
| |
| pMvSataChannel = pAdapter->mvSataAdapter.sataChannel[channelNum]; |
| kfree(pMvSataChannel); |
| pAdapter->mvSataAdapter.sataChannel[channelNum] = NULL; |
| return; |
| } |
| /**************************************************************** |
| * Name: mv_ial_lib_init_channel |
| * |
| * Description: allocate request and response queues for the EDMA of the |
| * given channel and sets other fields. |
| * |
| * Parameters: |
| * pAdapter - pointer to the emulated adapter data structure |
| * channelNum - channel number. |
| * Return: 0 on success, otherwise on failure |
| ****************************************************************/ |
| int mv_ial_lib_init_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum) |
| { |
| MV_SATA_CHANNEL *pMvSataChannel; |
| dma_addr_t req_dma_addr; |
| dma_addr_t rsp_dma_addr; |
| |
| if (channelNum >= pAdapter->mvSataAdapter.numberOfChannels) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: Bad channelNum=%d", |
| pAdapter->mvSataAdapter.adapterId, channelNum); |
| return -1; |
| } |
| |
| pMvSataChannel = (MV_SATA_CHANNEL *)kmalloc(sizeof(MV_SATA_CHANNEL), |
| GFP_ATOMIC); |
| if (pMvSataChannel == NULL) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: out of memory\n", |
| pAdapter->mvSataAdapter.adapterId); |
| return -1; |
| } |
| pAdapter->mvSataAdapter.sataChannel[channelNum] = pMvSataChannel; |
| pMvSataChannel->channelNumber = channelNum; |
| |
| pMvSataChannel->requestQueue = (struct mvDmaRequestQueueEntry *) |
| (pAdapter->requestsArrayBaseAlignedAddr + |
| (channelNum * pAdapter->requestQueueSize)); |
| req_dma_addr = pAdapter->requestsArrayBaseDmaAlignedAddr + |
| (channelNum * pAdapter->requestQueueSize); |
| |
| /* check the 1K alignment of the request queue*/ |
| if (((u64)req_dma_addr) & 0x3ffULL) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: request queue allocated isn't 1 K aligned," |
| " dma_addr=%x.%x channel=%d\n", pAdapter->mvSataAdapter.adapterId, |
| (unsigned int)pci64_dma_hi32(req_dma_addr), |
| (unsigned int)pci64_dma_lo32(req_dma_addr), |
| channelNum); |
| return -1; |
| } |
| pMvSataChannel->requestQueuePciLowAddress = pci64_dma_lo32(req_dma_addr); |
| pMvSataChannel->requestQueuePciHiAddress = pci64_dma_hi32(req_dma_addr); |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: request queue allocated: 0x%p\n", |
| pAdapter->mvSataAdapter.adapterId, channelNum, |
| pMvSataChannel->requestQueue); |
| pMvSataChannel->responseQueue = (struct mvDmaResponseQueueEntry *) |
| (pAdapter->responsesArrayBaseAlignedAddr + |
| (channelNum * pAdapter->responseQueueSize)); |
| rsp_dma_addr = pAdapter->responsesArrayBaseDmaAlignedAddr + |
| (channelNum * pAdapter->responseQueueSize); |
| |
| /* check the 256 alignment of the response queue*/ |
| if (((u64)rsp_dma_addr) & 0xff) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d,%d]: response queue allocated isn't 256 byte " |
| "aligned, dma_addr=%x.%x\n", |
| pAdapter->mvSataAdapter.adapterId, (unsigned int)pci64_dma_hi32(rsp_dma_addr), |
| (unsigned int)pci64_dma_lo32(rsp_dma_addr), channelNum); |
| return -1; |
| } |
| pMvSataChannel->responseQueuePciLowAddress = pci64_dma_lo32(rsp_dma_addr); |
| pMvSataChannel->responseQueuePciHiAddress = pci64_dma_hi32(rsp_dma_addr); |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: response queue allocated: 0x%p\n", |
| pAdapter->mvSataAdapter.adapterId, channelNum, |
| pMvSataChannel->responseQueue); |
| return 0; |
| } |
| |
| /**************************************************************** |
| * Name: mv_ial_lib_int_handler |
| * |
| * Description: Interrupt handler. |
| * |
| * Parameters: irq - Hardware IRQ number.assume that different cards will have |
| * different IRQ's TBD |
| * dev_id - points to the mvxxxxxxDeviceStruct that generated |
| * the interrupt |
| * regs - |
| * |
| * |
| ****************************************************************/ |
| irqreturn_t mv_ial_lib_int_handler (int irq, void *dev_id ) |
| { |
| IAL_ADAPTER_T *pAdapter; |
| unsigned long flags; |
| int handled = 0; |
| struct scsi_cmnd *cmnds_done_list = NULL; |
| pAdapter = (IAL_ADAPTER_T *)dev_id; |
| |
| /* |
| * Acquire the adapter spinlock. Meantime all completed commands will be added |
| * to done queue. |
| */ |
| spin_lock_irqsave(&pAdapter->adapter_lock, flags); |
| |
| if (mvSataInterruptServiceRoutine(&pAdapter->mvSataAdapter) == MV_TRUE) |
| { |
| handled = 1; |
| pAdapter->procNumOfInterrupts ++; |
| mvSataScsiPostIntService(pAdapter->ataScsiAdapterExt); |
| } |
| /* Unlock adapter lock */ |
| spin_unlock_irqrestore(&pAdapter->adapter_lock, flags); |
| /* Check if there are commands in the done queue to be completed */ |
| if (handled == 1) |
| { |
| MV_U8 i; |
| |
| for (i = 0; i < pAdapter->maxHosts; i++) |
| { |
| spin_lock_irqsave(&pAdapter->adapter_lock, flags); |
| cmnds_done_list = mv_ial_lib_get_first_cmnd(pAdapter, i); |
| spin_unlock_irqrestore(&pAdapter->adapter_lock, flags); |
| if (cmnds_done_list) |
| { |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) |
| spin_lock_irqsave(&io_request_lock, flags); |
| #else |
| spin_lock_irqsave(pAdapter->host[i]->scsihost->host_lock, flags); |
| #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); |
| #else |
| spin_unlock_irqrestore(pAdapter->host[i]->scsihost->host_lock, flags); |
| #endif |
| } |
| } |
| } |
| return IRQ_RETVAL(handled); |
| } |
| |
| /**************************************************************** |
| * Name: mv_ial_lib_add_buffer_to_prd_table |
| * |
| * Description: insert one buffer into number of entries in the PRD table, |
| * keeping 64KB boundaries |
| * |
| * Parameters: pPRD_table: pointer to the PRD table. |
| * table_size: number of entries in the PRD table. |
| * count: index of the next entry to add, should be updated by this |
| * function |
| * buf_addr,buf_len: the dma address and the size of the buffer to add. |
| * isEOT: 1 if this is the last entry |
| * Returns: 0 on success, otherwise onfailure. |
| * |
| ****************************************************************/ |
| |
| static int mv_ial_lib_add_buffer_to_prd_table(MV_SATA_ADAPTER *pMvSataAdapter, |
| MV_SATA_EDMA_PRD_ENTRY *pPRD_table, |
| int table_size, int *count, |
| dma_addr_t buf_addr, |
| unsigned int buf_len, |
| int isEOT) |
| { |
| unsigned int entry = *count; |
| u64 xcount = 0; |
| |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG,"insert to PRD table, count=%d, buf_addr=%x, buf_len=%x\n", |
| *count,(unsigned int) buf_addr, buf_len); |
| |
| |
| |
| /* |
| The buffer is splitted in case then either the buffer size exceeds 64 KB |
| or 2 high address bits of all data in the buffer are not identical |
| */ |
| while (buf_len) |
| { |
| if (entry >= table_size) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,"PRD table too small (entry %d, table_size %d\n", |
| entry, table_size); |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,"[%d] insert to PRD table, count=%d," |
| " buf_addr=%x, buf_len=%x\n", |
| pMvSataAdapter->adapterId, |
| *count,(unsigned int) buf_addr, buf_len); |
| return -1; |
| } |
| else |
| { |
| u64 bcount = buf_len; |
| /*buffer size exceeds 64K*/ |
| if (bcount > 0x10000) |
| bcount = 0x10000; |
| if (buf_addr & 0x1) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, " PRD entry low address is not 1 bit aligned\n"); |
| return -1; |
| } |
| #if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G) |
| /*Split the buffer if 2 high address bits of all |
| data in the buffer are not the same*/ |
| if ((buf_addr | 0xFFFFFFFF) != |
| ((buf_addr + bcount - 1) | 0xFFFFFFFF)) |
| { |
| bcount = 0x100000000ULL - (buf_addr & 0xFFFFFFFF); |
| } |
| #endif |
| if(((buf_addr & MRVL_SATA_BOUNDARY_MASK) + bcount) > |
| MRVL_SATA_BUFF_BOUNDARY) |
| { |
| bcount = MRVL_SATA_BUFF_BOUNDARY - |
| (buf_addr & MRVL_SATA_BOUNDARY_MASK); |
| } |
| /*In case then buffer size is 64K |
| PRD entry byte count is set to zero*/ |
| xcount = bcount & 0xffff; |
| pPRD_table[entry].lowBaseAddr = |
| cpu_to_le32(pci64_dma_lo32(buf_addr)); |
| pPRD_table[entry].highBaseAddr = |
| cpu_to_le32(pci64_dma_hi32(buf_addr)); |
| pPRD_table[entry].byteCount = cpu_to_le16(xcount); |
| pPRD_table[entry].reserved = 0; |
| /* enable snoop on data buffers */ |
| pPRD_table[entry].flags = 0;/*cpu_to_le16(MV_EDMA_PRD_NO_SNOOP_FLAG);*/ |
| if (xcount & 0x1) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR," PRD entry byte count is not 1 bit aligned\n"); |
| return -1; |
| } |
| buf_addr += bcount; |
| buf_len -= bcount; |
| entry++; |
| } |
| } |
| |
| if (entry) |
| { |
| if (isEOT)/* enable snoop on data buffers */ |
| pPRD_table[entry-1].flags = cpu_to_le16(MV_EDMA_PRD_EOT_FLAG /*| |
| MV_EDMA_PRD_NO_SNOOP_FLAG*/); |
| |
| *count = entry; |
| return 0; |
| } |
| |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "insert zero entries to PRD table \n"); |
| return -1; |
| } |
| |
| /* map to pci */ |
| int mv_ial_lib_generate_prd(MV_SATA_ADAPTER *pMvSataAdapter, struct scsi_cmnd *SCpnt, |
| struct mv_comp_info *completion_info) |
| { |
| IAL_ADAPTER_T *pAdapter = MV_IAL_ADAPTER(SCpnt->device->host); |
| IAL_HOST_T *pHost = HOSTDATA(SCpnt->device->host); |
| MV_SATA_EDMA_PRD_ENTRY *pPRD_table = NULL; |
| dma_addr_t PRD_dma_address = 0; |
| dma_addr_t busaddr = 0; |
| unsigned int prd_size = 0; |
| struct scatterlist *sg; |
| unsigned int prd_count; |
| MV_SATA_DEVICE_TYPE deviceType = pAdapter->ataScsiAdapterExt->ataDriveData[pHost->channelIndex][SCpnt->device->id].identifyInfo.deviceType; |
| /*should be removed*/ |
| #ifndef MV_SUPPORT_1MBYTE_IOS |
| if (scsi_bufflen(SCpnt) > (SCpnt->device->host->max_sectors << 9)) |
| { |
| printk("ERROR: request length exceeds the maximum alowed value, %x %x\n", |
| pMvSataAdapter->pciConfigDeviceId, |
| pMvSataAdapter->pciConfigRevisionId); |
| } |
| #endif |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| if (SCpnt->use_sg) |
| { |
| unsigned int sg_count; |
| unsigned int i; |
| sg = (struct scatterlist *) SCpnt->request_buffer; |
| |
| sg_count = pci64_map_sg(pAdapter->pcidev, sg, |
| SCpnt->use_sg, |
| scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); |
| |
| if (sg_count != SCpnt->use_sg) |
| printk("WARNING sg_count(%d) != SCpnt->use_sg(%d)\n", |
| (unsigned int)sg_count, SCpnt->use_sg); |
| #else |
| { |
| unsigned int sg_count; |
| unsigned int i; |
| |
| sg = scsi_sglist(SCpnt); |
| sg_count = scsi_dma_map(SCpnt); |
| if (sg_count < 0) { |
| dev_err(&pAdapter->pcidev->dev, "pci_map_sg failed!\n"); |
| return -1; |
| } |
| |
| #endif |
| if ((sg_count == 1) && (pAdapter->mvSataAdapter.sataAdapterGeneration >= |
| MV_SATA_GEN_IIE) && (sg_dma_len(sg) <= 0x10000) && |
| (deviceType != MV_SATA_DEVICE_TYPE_ATAPI_DEVICE)) |
| { |
| completion_info->pSALBlock->singleDataRegion = MV_TRUE; |
| completion_info->cpu_PRDpnt = NULL; |
| completion_info->dma_PRDpnt = 0; |
| completion_info->allocated_entries = 0; |
| completion_info->single_buff_busaddr = 0; |
| PRD_dma_address = sg_dma_address(sg); |
| completion_info->pSALBlock->PRDTableLowPhyAddress = pci64_dma_lo32(PRD_dma_address); |
| completion_info->pSALBlock->PRDTableHighPhyAddress = pci64_dma_hi32(PRD_dma_address); |
| completion_info->pSALBlock->byteCount = sg_dma_len(sg); |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Use single data region" |
| " buffer, size=%d\n", |
| completion_info->pSALBlock->byteCount); |
| |
| return 0; |
| } |
| prd_size = MV_PRD_TABLE_SIZE; |
| pPRD_table = (MV_SATA_EDMA_PRD_ENTRY*)mv_ial_lib_prd_allocate(pHost); |
| if (pPRD_table == NULL) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "Failed to allocate PRD table, requested size=%d\n" |
| , prd_size); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| pci64_unmap_sg(pAdapter->pcidev, sg, SCpnt->use_sg, |
| scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); |
| #else |
| scsi_dma_unmap(SCpnt); |
| #endif |
| return -1; |
| } |
| prd_count=0; |
| for (i=0; (i < sg_count) && (sg_dma_len(sg)); i++, sg++) |
| { |
| int isEOT; |
| |
| isEOT =((i+1 < sg_count) && (sg_dma_len(&sg[1]))) ? 0 : 1; |
| |
| if (mv_ial_lib_add_buffer_to_prd_table(pMvSataAdapter, |
| pPRD_table, |
| prd_size, |
| &prd_count, |
| sg_dma_address(sg), |
| sg_dma_len(sg), |
| isEOT)) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR," in building PRD table from scatterlist, " |
| "prd_size=%d, prd_count=%d\n", prd_size, |
| prd_count); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| pci64_unmap_sg(pAdapter->pcidev, |
| (struct scatterlist *)SCpnt->request_buffer, |
| SCpnt->use_sg, |
| scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); |
| #else |
| scsi_dma_unmap(SCpnt); |
| #endif |
| |
| return -1; |
| } |
| } |
| PRD_dma_address = pci64_map_page(pAdapter->pcidev, |
| pPRD_table, |
| MV_EDMA_PRD_ENTRY_SIZE * (prd_size), |
| PCI_DMA_TODEVICE); |
| } |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| else if (scsi_bufflen(SCpnt) && SCpnt->sc_data_direction != PCI_DMA_NONE) |
| { |
| if ((pAdapter->mvSataAdapter.sataAdapterGeneration >= MV_SATA_GEN_IIE) |
| && (scsi_bufflen(SCpnt) <= 0x10000) && |
| (deviceType != MV_SATA_DEVICE_TYPE_ATAPI_DEVICE)) |
| { |
| completion_info->pSALBlock->singleDataRegion = MV_TRUE; |
| completion_info->cpu_PRDpnt = NULL; |
| completion_info->dma_PRDpnt = 0; |
| completion_info->allocated_entries = 0; |
| busaddr = pci64_map_page(pAdapter->pcidev, SCpnt->request_buffer, |
| scsi_bufflen(SCpnt), |
| scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); |
| completion_info->single_buff_busaddr = busaddr; |
| completion_info->pSALBlock->PRDTableLowPhyAddress = pci64_dma_lo32(busaddr); |
| completion_info->pSALBlock->PRDTableHighPhyAddress = pci64_dma_hi32(busaddr); |
| completion_info->pSALBlock->byteCount = scsi_bufflen(SCpnt); |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Use single data region" |
| " buffer, size=%d\n", |
| completion_info->pSALBlock->byteCount); |
| return 0; |
| } |
| prd_size = MV_PRD_TABLE_SIZE; |
| pPRD_table = (MV_SATA_EDMA_PRD_ENTRY*)mv_ial_lib_prd_allocate(pHost); |
| if (pPRD_table == NULL) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "Failed to allocate PRD table, requested size=%d\n", |
| prd_size); |
| return -1; |
| } |
| |
| busaddr = pci64_map_page(pAdapter->pcidev, SCpnt->request_buffer, |
| scsi_bufflen(SCpnt), |
| scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); |
| prd_count = 0; |
| if (mv_ial_lib_add_buffer_to_prd_table(pMvSataAdapter, |
| pPRD_table, |
| prd_size, |
| &prd_count, busaddr, |
| scsi_bufflen(SCpnt), 1)) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, " in building PRD table from buffer\n"); |
| pci64_unmap_page(pAdapter->pcidev, busaddr, scsi_bufflen(SCpnt), |
| scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); |
| return -1; |
| } |
| PRD_dma_address = pci64_map_page(pAdapter->pcidev, |
| pPRD_table, |
| MV_EDMA_PRD_ENTRY_SIZE* (prd_size), |
| PCI_DMA_TODEVICE); |
| } |
| #endif |
| completion_info->cpu_PRDpnt = pPRD_table; |
| completion_info->dma_PRDpnt = PRD_dma_address; |
| completion_info->allocated_entries = prd_size; |
| completion_info->single_buff_busaddr = busaddr; |
| completion_info->pSALBlock->PRDTableLowPhyAddress = pci64_dma_lo32(PRD_dma_address); |
| completion_info->pSALBlock->PRDTableHighPhyAddress = pci64_dma_hi32(PRD_dma_address); |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "PRD table allocated %p\n", pPRD_table); |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "PRD table allocated (dma) %x \n", PRD_dma_address); |
| return 0; |
| } |
| |
| |
| /**************************************************************** |
| * Name: mv_ial_block_requests |
| * |
| * Description: Blocks request from SCSI mid layer while channel |
| * initialization is in progress |
| * |
| * Parameters: pAdapter, pointer to the IAL adapter data structure. |
| * channelIndex, channel number |
| * |
| * Returns: None. |
| * |
| ****************************************************************/ |
| |
| void mv_ial_block_requests(struct IALAdapter *pAdapter, MV_U8 channelIndex) |
| { |
| if (MV_TRUE == pAdapter->host[channelIndex]->hostBlocked) |
| { |
| return; |
| } |
| |
| if ((pAdapter->ialCommonExt.channelState[channelIndex] != CHANNEL_READY) && |
| (pAdapter->ialCommonExt.channelState[channelIndex] != CHANNEL_NOT_CONNECTED)) |
| { |
| pAdapter->host[channelIndex]->hostBlocked = MV_TRUE; |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: blocking SCSI host.\n", |
| pAdapter->mvSataAdapter.adapterId, channelIndex); |
| scsi_block_requests(pAdapter->host[channelIndex]->scsihost); |
| } |
| else |
| { |
| pAdapter->host[channelIndex]->hostBlocked = MV_FALSE; |
| } |
| } |
| |
| /**************************************************************** |
| * Name: mv_ial_unblock_requests |
| * |
| * Description: Unblocks request from SCSI mid layer for non connected |
| * channels or channels whose initialization is finished |
| * |
| * Parameters: pAdapter - pointer to the IAL adapter data structure. |
| * channelIndex - channel number |
| * |
| * Returns: None. |
| * |
| ****************************************************************/ |
| static void mv_ial_unblock_requests(struct IALAdapter *pAdapter, MV_U8 channelIndex) |
| { |
| if ((CHANNEL_NOT_CONNECTED == pAdapter->ialCommonExt.channelState[channelIndex]) || |
| (CHANNEL_READY == pAdapter->ialCommonExt.channelState[channelIndex])) |
| { |
| pAdapter->host[channelIndex]->hostBlocked = MV_FALSE; |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: unblocking SCSI host.\n", |
| pAdapter->mvSataAdapter.adapterId, channelIndex); |
| scsi_unblock_requests(pAdapter->host[channelIndex]->scsihost); |
| } |
| } |
| |
| |
| |
| |
| /**************************************************************** |
| * Name: mv_ial_lib_event_notify |
| * |
| * Description: this function called by the low level to notify a certain event |
| * |
| * Parameters: pMvSataAdapter, pointer to the Device data structure. |
| * |
| * Returns: MV_TRUE on success, MV_FALSE on failure. |
| * |
| ****************************************************************/ |
| MV_BOOLEAN mv_ial_lib_event_notify(MV_SATA_ADAPTER *pMvSataAdapter, MV_EVENT_TYPE eventType, |
| MV_U32 param1, MV_U32 param2) |
| { |
| IAL_ADAPTER_T *pAdapter = pMvSataAdapter->IALData; |
| MV_U8 channel = param2; |
| |
| switch (eventType) |
| { |
| case MV_EVENT_TYPE_SATA_CABLE: |
| { |
| |
| |
| if (param1 == MV_SATA_CABLE_EVENT_CONNECT) |
| { |
| |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: device connected event received\n", |
| pMvSataAdapter->adapterId, channel); |
| mvRestartChannel(&pAdapter->ialCommonExt, channel, |
| pAdapter->ataScsiAdapterExt, MV_FALSE); |
| mv_ial_block_requests(pAdapter, channel); |
| } |
| else if (param1 == MV_SATA_CABLE_EVENT_DISCONNECT) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: device disconnected event received \n", |
| pMvSataAdapter->adapterId, channel); |
| if (mvSataIsStorageDeviceConnected(pMvSataAdapter, channel, NULL) == |
| MV_FALSE) |
| { |
| mvStopChannel(&pAdapter->ialCommonExt, channel, |
| pAdapter->ataScsiAdapterExt); |
| } |
| else |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d,%d]: device disconnected event ignored.\n", |
| pMvSataAdapter->adapterId, channel); |
| } |
| |
| } |
| else if (param1 == MV_SATA_CABLE_EVENT_PM_HOT_PLUG) |
| { |
| mvPMHotPlugDetected(&pAdapter->ialCommonExt, channel, |
| pAdapter->ataScsiAdapterExt); |
| mv_ial_block_requests(pAdapter, channel); |
| } |
| else |
| { |
| |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "illegal value for param1(%d) at " |
| "connect/disconect event, host=%d\n", param1, |
| pMvSataAdapter->adapterId ); |
| } |
| } |
| break; |
| case MV_EVENT_TYPE_ADAPTER_ERROR: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "DEVICE error event received, pci cause " |
| "reg=%x, don't know how to handle this\n", param1); |
| return MV_TRUE; |
| case MV_EVENT_TYPE_SATA_ERROR: |
| switch (param1) |
| { |
| case MV_SATA_RECOVERABLE_COMMUNICATION_ERROR: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, |
| " [%d %d] sata recoverable error occured\n", |
| pMvSataAdapter->adapterId, channel); |
| break; |
| case MV_SATA_UNRECOVERABLE_COMMUNICATION_ERROR: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, |
| " [%d %d] sata unrecoverable error occured, restart channel\n", |
| pMvSataAdapter->adapterId, channel); |
| mvSataChannelHardReset(pMvSataAdapter, channel); |
| mvRestartChannel(&pAdapter->ialCommonExt, channel, |
| pAdapter->ataScsiAdapterExt, MV_TRUE); |
| mv_ial_block_requests(pAdapter, channel); |
| break; |
| case MV_SATA_DEVICE_ERROR: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, |
| " [%d %d] device error occured\n", |
| pMvSataAdapter->adapterId, channel); |
| break; |
| } |
| break; |
| default: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, " adapter %d unknown event %d" |
| " param1= %x param2 = %x\n", pMvSataAdapter->adapterId, |
| eventType - MV_EVENT_TYPE_ADAPTER_ERROR, param1, param2); |
| return MV_FALSE; |
| |
| }/*switch*/ |
| return MV_TRUE; |
| } |
| |
| MV_BOOLEAN IALConfigQueuingMode(MV_SATA_ADAPTER *pSataAdapter, |
| MV_U8 channelIndex, |
| MV_EDMA_MODE mode, |
| MV_SATA_SWITCHING_MODE switchingMode, |
| MV_BOOLEAN use128Entries) |
| |
| { |
| IAL_ADAPTER_T *pAdapter = pSataAdapter->IALData; |
| pAdapter->host[channelIndex]->mode = mode; |
| pAdapter->host[channelIndex]->switchingMode = switchingMode; |
| pAdapter->host[channelIndex]->use128Entries = use128Entries; |
| |
| if (mvSataConfigEdmaMode(pSataAdapter, channelIndex, |
| mode, 31) == MV_FALSE) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d %d]: mvSataConfigEdmaMode failed\n", |
| pSataAdapter->adapterId, channelIndex); |
| return -1; |
| } |
| return MV_TRUE; |
| } |
| |
| MV_BOOLEAN IALInitChannel(MV_SATA_ADAPTER *pSataAdapter, MV_U8 channelIndex) |
| { |
| if (mv_ial_lib_init_channel(pSataAdapter->IALData, channelIndex) == 0) |
| { |
| return MV_TRUE; |
| } |
| return MV_FALSE; |
| } |
| void IALReleaseChannel(MV_SATA_ADAPTER *pSataAdapter, MV_U8 channelIndex) |
| { |
| mv_ial_lib_free_channel(pSataAdapter->IALData, channelIndex); |
| } |
| |
| void IALChannelCommandsQueueFlushed(MV_SATA_ADAPTER *pSataAdapter, MV_U8 channelIndex) |
| { |
| |
| } |
| |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) |
| |
| static void channel_rescan(struct work_struct *work) |
| { |
| struct rescan_wrapper* rescan = container_of(work, struct rescan_wrapper, work); |
| struct Scsi_Host *host; |
| struct scsi_device *sdev = NULL; |
| MV_U16 target; |
| if (rescan->pAdapter->host[rescan->channelIndex] == NULL) |
| { |
| kfree(rescan); |
| return; |
| } |
| host = rescan->pAdapter->host[rescan->channelIndex]->scsihost; |
| down(&rescan->pAdapter->rescan_mutex); |
| if (atomic_read(&rescan->pAdapter->stopped) > 0) |
| { |
| up(&rescan->pAdapter->rescan_mutex); |
| kfree(rescan); |
| return; |
| } |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d %d] channel_rescan(): " |
| "targets to add 0x%X, targets to remove 0x%X\n", |
| rescan->pAdapter->mvSataAdapter.adapterId, |
| rescan->channelIndex, |
| rescan->targetsToAdd, |
| rescan->targetsToRemove); |
| |
| for (target = 0; (rescan->targetsToRemove != 0) && (target < host->max_id); target++) |
| { |
| if (rescan->targetsToRemove & (1 << target)) |
| { |
| sdev = scsi_device_lookup(host, 0, target, 0); |
| if (sdev != NULL) |
| { |
| /*scsi_device_cancel(sdev, 0);*/ |
| scsi_remove_device(sdev); |
| scsi_device_put(sdev); |
| } |
| else |
| { |
| mvLogMsg(MV_IAL_LOG_ID, |
| MV_DEBUG_ERROR, |
| "[%d %d %d] failed to deattach scsi device\n", |
| rescan->pAdapter->mvSataAdapter.adapterId, |
| rescan->channelIndex, |
| target); |
| } |
| } |
| } |
| sdev = NULL; |
| for (target = 0; (rescan->targetsToAdd != 0) && (target < host->max_id); target++) |
| { |
| if (rescan->targetsToAdd & (1 << target)) |
| { |
| int error = scsi_add_device(host, 0, target, 0); |
| if (error) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, |
| MV_DEBUG_ERROR, |
| "[%d %d %d] Error adding scsi device\n", |
| rescan->pAdapter->mvSataAdapter.adapterId, |
| rescan->channelIndex, |
| target); |
| } |
| } |
| } |
| up(&rescan->pAdapter->rescan_mutex); |
| kfree(rescan); |
| } |
| #endif |
| |
| MV_BOOLEAN IALBusChangeNotify(MV_SATA_ADAPTER *pSataAdapter, |
| MV_U8 channelIndex) |
| { |
| return MV_TRUE; |
| } |
| |
| MV_BOOLEAN IALBusChangeNotifyEx(MV_SATA_ADAPTER *pSataAdapter, |
| MV_U8 channelIndex, |
| MV_U16 targetsToRemove, |
| MV_U16 targetsToAdd) |
| { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) |
| IAL_ADAPTER_T *pAdapter = pSataAdapter->IALData; |
| if (0 == atomic_read(&pAdapter->stopped)) |
| { |
| struct rescan_wrapper* rescan = |
| kmalloc(sizeof(struct rescan_wrapper), GFP_ATOMIC); |
| if (rescan != NULL) |
| { |
| INIT_WORK(&rescan->work, channel_rescan); |
| rescan->pAdapter = pAdapter; |
| rescan->channelIndex = channelIndex; |
| rescan->targetsToRemove = targetsToRemove; |
| rescan->targetsToAdd = targetsToAdd; |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, |
| "[%d %d] Rescan bus: remove 0x%X, add 0x%X.\n", |
| pSataAdapter->adapterId, |
| channelIndex, targetsToRemove, targetsToAdd); |
| if (schedule_work(&rescan->work) == 0) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, |
| "[%d %d] Rescan bus: schedule_work() failed.\n", |
| pSataAdapter->adapterId, |
| channelIndex); |
| kfree(rescan); |
| } |
| } |
| else |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, |
| "[%d %d] Rescan bus: memory allocation error.\n", |
| pSataAdapter->adapterId, |
| channelIndex); |
| } |
| } |
| #endif |
| return MV_TRUE; |
| } |
| |
| |
| void asyncStartTimerFunction(unsigned long data) |
| { |
| IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)data; |
| unsigned long flags; |
| struct scsi_cmnd *cmnds_done_list = NULL; |
| MV_U8 i; |
| |
| spin_lock_irqsave(&pAdapter->adapter_lock, flags); |
| if (pAdapter->stopAsyncTimer == MV_FALSE) |
| { |
| mvIALTimerCallback(&pAdapter->ialCommonExt, |
| pAdapter->ataScsiAdapterExt); |
| for (i = 0; i < pAdapter->maxHosts; i++) |
| { |
| if (MV_TRUE == pAdapter->host[i]->hostBlocked) |
| { |
| spin_unlock_irqrestore(&pAdapter->adapter_lock, flags); |
| mv_ial_unblock_requests(pAdapter, i); |
| spin_lock_irqsave(&pAdapter->adapter_lock, flags); |
| } |
| } |
| pAdapter->asyncStartTimer.expires = jiffies + MV_LINUX_ASYNC_TIMER_PERIOD; |
| add_timer (&pAdapter->asyncStartTimer); |
| } |
| else |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d]: Async timer stopped\n", |
| pAdapter->mvSataAdapter.adapterId); |
| } |
| spin_unlock_irqrestore(&pAdapter->adapter_lock, flags); |
| /* Check if there are commands in the done queue to be completed */ |
| for (i = 0; i < pAdapter->maxHosts; i++) |
| { |
| spin_lock_irqsave(&pAdapter->adapter_lock, flags); |
| cmnds_done_list = mv_ial_lib_get_first_cmnd(pAdapter, i); |
| spin_unlock_irqrestore(&pAdapter->adapter_lock, flags); |
| if (cmnds_done_list) |
| { |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) |
| spin_lock_irqsave(&io_request_lock, flags); |
| #else |
| spin_lock_irqsave(pAdapter->host[i]->scsihost->host_lock, flags); |
| #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); |
| #else |
| spin_unlock_irqrestore(pAdapter->host[i]->scsihost->host_lock, flags); |
| #endif |
| } |
| } |
| } |
| |
| /**************************************************************** |
| * Name: release_ata_mem |
| * |
| * Description: free memory allocated to the PRD table |
| * unmap the data buffers of the scsi command |
| * free completion_info data structure. |
| * Parameters: pInfo: pointer to the data structure returned by the |
| * completion call back function to identify the origial command |
| * |
| ****************************************************************/ |
| void release_ata_mem(struct mv_comp_info * pInfo) |
| { |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| IAL_ADAPTER_T *pAdapter = MV_IAL_ADAPTER(pInfo->SCpnt->device->host); |
| #endif |
| IAL_HOST_T *pHost = HOSTDATA(pInfo->SCpnt->device->host); |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) |
| if(pInfo->kmap_buffer) |
| { |
| if( pInfo->SCpnt->sc_data_direction == DMA_FROM_DEVICE) |
| { |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) |
| |
| struct scatterlist *sg; |
| MV_U8* pBuffer; |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| sg = (struct scatterlist *) pInfo->SCpnt->request_buffer; |
| #else |
| sg = scsi_sglist(pInfo->SCpnt); |
| #endif |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "SCpnt %p: copy data from temp" |
| " buffer to command buffer, length %d \n", pInfo->SCpnt, sg->length); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| pBuffer = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; |
| #else |
| pBuffer = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; |
| #endif |
| memcpy(pBuffer, pInfo->pSALBlock->pDataBuffer , sg->length); |
| kunmap_atomic(pBuffer - sg->offset, KM_IRQ0); |
| |
| #else |
| scsi_sg_copy_from_buffer(pInfo->SCpnt, pInfo->pSALBlock->pDataBuffer, |
| scsi_bufflen(pInfo->SCpnt)); |
| |
| #endif |
| |
| } |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "SCpnt %p: free temp data " |
| "buffer\n", pInfo->SCpnt); |
| kfree(pInfo->pSALBlock->pDataBuffer); |
| } |
| #endif |
| if (pInfo->cpu_PRDpnt) |
| { |
| mv_ial_lib_prd_free(pHost, |
| pInfo->allocated_entries, |
| pInfo->dma_PRDpnt, |
| pInfo->cpu_PRDpnt); |
| } |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| if( pInfo->cpu_PRDpnt || (pInfo->pSALBlock->singleDataRegion == MV_TRUE)) |
| { |
| if (pInfo->SCpnt->use_sg) |
| { |
| pci64_unmap_sg(pAdapter->pcidev, |
| (struct scatterlist *)pInfo->SCpnt->request_buffer, |
| pInfo->SCpnt->use_sg, |
| scsi_to_pci_dma_dir(pInfo->SCpnt->sc_data_direction)); |
| } |
| else |
| { |
| pci64_unmap_page(pAdapter->pcidev, |
| pInfo->single_buff_busaddr, |
| scsi_bufflen(pInfo->SCpnt), |
| scsi_to_pci_dma_dir(pInfo->SCpnt->sc_data_direction)); |
| } |
| } |
| #else |
| scsi_dma_unmap(pInfo->SCpnt); |
| #endif |
| |
| pInfo->cpu_PRDpnt = NULL; |
| kfree(pInfo->pSALBlock); |
| } |
| |
| int mv_ial_lib_allocate_edma_queues(IAL_ADAPTER_T *pAdapter) |
| { |
| ulong *tmp; |
| ulong requests_array_size; |
| ulong responses_array_size; |
| |
| if ((pAdapter->mvSataAdapter.sataAdapterGeneration >= MV_SATA_GEN_IIE) && |
| (pAdapter->mvSataAdapter.pciConfigDeviceId != MV_SATA_DEVICE_ID_6082)) |
| { |
| pAdapter->requestQueueSize = MV_EDMA_GEN2E_REQUEST_QUEUE_SIZE; |
| pAdapter->responseQueueSize = MV_EDMA_GEN2E_RESPONSE_QUEUE_SIZE; |
| } |
| else |
| { |
| pAdapter->requestQueueSize = MV_EDMA_REQUEST_QUEUE_SIZE; |
| pAdapter->responseQueueSize = MV_EDMA_RESPONSE_QUEUE_SIZE; |
| } |
| |
| requests_array_size = (pAdapter->maxHosts + 1) * (pAdapter->requestQueueSize); |
| responses_array_size =(pAdapter->maxHosts + 1) * (pAdapter->responseQueueSize); |
| |
| pAdapter->requestsArrayBaseAddr = |
| pci_alloc_consistent(pAdapter->pcidev, |
| requests_array_size, |
| &(pAdapter->requestsArrayBaseDmaAddr)); |
| if (pAdapter->requestsArrayBaseAddr == NULL) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: Failed to allocate memory for EDMA request" |
| " queues\n", pAdapter->mvSataAdapter.adapterId); |
| return -1; |
| } |
| pAdapter->requestsArrayBaseAlignedAddr = pAdapter->requestsArrayBaseAddr + |
| pAdapter->requestQueueSize; |
| tmp = (ulong*)&pAdapter->requestsArrayBaseAlignedAddr; |
| *tmp &= ~((ulong)pAdapter->requestQueueSize - 1); |
| |
| pAdapter->requestsArrayBaseDmaAlignedAddr = |
| pAdapter->requestsArrayBaseDmaAddr + pAdapter->requestQueueSize; |
| pAdapter->requestsArrayBaseDmaAlignedAddr &= |
| ~(pAdapter->requestQueueSize - 1); |
| |
| if ((pAdapter->requestsArrayBaseDmaAlignedAddr - pAdapter->requestsArrayBaseDmaAddr) != |
| (pAdapter->requestsArrayBaseAlignedAddr - pAdapter->requestsArrayBaseAddr)) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: Error in Request Queues Alignment\n", |
| pAdapter->mvSataAdapter.adapterId |
| ); |
| pci_free_consistent(pAdapter->pcidev, requests_array_size, |
| pAdapter->requestsArrayBaseAddr, |
| pAdapter->requestsArrayBaseDmaAddr); |
| return -1; |
| } |
| /* response queues */ |
| pAdapter->responsesArrayBaseAddr = |
| pci_alloc_consistent(pAdapter->pcidev, |
| responses_array_size, |
| &(pAdapter->responsesArrayBaseDmaAddr)); |
| if (pAdapter->responsesArrayBaseAddr == NULL) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: Failed to allocate memory for EDMA response" |
| " queues\n", pAdapter->mvSataAdapter.adapterId); |
| pci_free_consistent(pAdapter->pcidev, requests_array_size, |
| pAdapter->requestsArrayBaseAddr, |
| pAdapter->requestsArrayBaseDmaAddr); |
| return -1; |
| } |
| pAdapter->responsesArrayBaseAlignedAddr = pAdapter->responsesArrayBaseAddr |
| + pAdapter->responseQueueSize; |
| tmp = (ulong*)&pAdapter->responsesArrayBaseAlignedAddr; |
| *tmp &= ~((ulong)pAdapter->responseQueueSize - 1); |
| |
| pAdapter->responsesArrayBaseDmaAlignedAddr = |
| pAdapter->responsesArrayBaseDmaAddr + pAdapter->responseQueueSize; |
| pAdapter->responsesArrayBaseDmaAlignedAddr &= |
| ~(pAdapter->responseQueueSize - 1); |
| |
| |
| if ((pAdapter->responsesArrayBaseDmaAlignedAddr - pAdapter->responsesArrayBaseDmaAddr) != |
| (pAdapter->responsesArrayBaseAlignedAddr - pAdapter->responsesArrayBaseAddr)) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "[%d]: Error in Response Quueues Alignment\n", |
| pAdapter->mvSataAdapter.adapterId); |
| pci_free_consistent(pAdapter->pcidev, responses_array_size, |
| pAdapter->responsesArrayBaseAddr, |
| pAdapter->responsesArrayBaseDmaAddr); |
| pci_free_consistent(pAdapter->pcidev, requests_array_size, |
| pAdapter->requestsArrayBaseAddr, |
| pAdapter->requestsArrayBaseDmaAddr); |
| return -1; |
| } |
| return 0; |
| } |
| |
| void mv_ial_lib_free_edma_queues(IAL_ADAPTER_T *pAdapter) |
| { |
| pci_free_consistent(pAdapter->pcidev, |
| (pAdapter->maxHosts + 1) * (pAdapter->responseQueueSize), |
| pAdapter->responsesArrayBaseAddr, |
| pAdapter->responsesArrayBaseDmaAddr); |
| pci_free_consistent(pAdapter->pcidev, |
| (pAdapter->maxHosts + 1) * (pAdapter->requestQueueSize), |
| pAdapter->requestsArrayBaseAddr, |
| pAdapter->requestsArrayBaseDmaAddr); |
| } |
| |
| MV_BOOLEAN IALCompletion(struct mvSataAdapter *pSataAdapter, |
| MV_SATA_SCSI_CMD_BLOCK *pCmdBlock) |
| { |
| struct scsi_cmnd *SCpnt = (struct scsi_cmnd *)pCmdBlock->IALData; |
| struct mv_comp_info *pInfo = ( struct mv_comp_info *) &(SCpnt->SCp); |
| MV_U8 host_status; |
| |
| |
| switch (pCmdBlock->ScsiCommandCompletion) |
| { |
| case MV_SCSI_COMPLETION_SUCCESS: |
| host_status = DID_OK; |
| break; |
| case MV_SCSI_COMPLETION_BAD_SCSI_COMMAND: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with BAD_SCSI_COMMAND\n"); |
| host_status = DID_OK; |
| break; |
| case MV_SCSI_COMPLETION_ATA_FAILED: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with ATA FAILED\n"); |
| host_status = DID_OK; |
| break; |
| case MV_SCSI_COMPLETION_UA_RESET: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with UA BUS" |
| " RESET\n"); |
| scsi_report_bus_reset(pInfo->SCpnt->device->host, pInfo->SCpnt->device->channel); |
| host_status = DID_OK; |
| break; |
| case MV_SCSI_COMPLETION_UA_PARAMS_CHANGED: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with UA " |
| "PARAMETERS CHANGED\n"); |
| scsi_report_bus_reset(pInfo->SCpnt->device->host, pInfo->SCpnt->device->channel); |
| host_status = DID_OK; |
| break; |
| |
| case MV_SCSI_COMPLETION_QUEUE_FULL: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with QUEUE FULL\n"); |
| /* flushed from ial common*/ |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) |
| if (pCmdBlock->ScsiStatus == MV_SCSI_STATUS_BUSY) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Host status: DID_RESET\n"); |
| host_status = DID_RESET; |
| |
| pInfo->SCpnt->flags |= IS_RESETTING; |
| } |
| else |
| #endif |
| { |
| host_status = DID_OK; |
| } |
| break; |
| |
| case MV_SCSI_COMPLETION_ABORTED: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with ABORTED\n"); |
| host_status = DID_ERROR; |
| break; |
| |
| case MV_SCSI_COMPLETION_OVERRUN: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with OVERRUN\n"); |
| if (pInfo->SCpnt->underflow > pCmdBlock->dataTransfered) |
| { |
| host_status = DID_ERROR; |
| } |
| else |
| { |
| host_status = DID_OK; |
| } |
| break; |
| case MV_SCSI_COMPLETION_UNDERRUN: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with UNDERRUN\n"); |
| host_status = DID_ERROR; |
| break; |
| |
| case MV_SCSI_COMPLETION_PARITY_ERROR: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR, "Scsi command completed with PARITY ERROR\n"); |
| host_status = DID_PARITY; |
| break; |
| case MV_SCSI_COMPLETION_INVALID_BUS: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with INVALID BUS\n"); |
| host_status = DID_BAD_TARGET; |
| break; |
| case MV_SCSI_COMPLETION_NO_DEVICE: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with NO DEVICE\n"); |
| host_status = DID_NO_CONNECT; |
| break; |
| case MV_SCSI_COMPLETION_INVALID_STATUS: |
| case MV_SCSI_COMPLETION_BAD_SCB: |
| case MV_SCSI_COMPLETION_NOT_READY: |
| case MV_SCSI_COMPLETION_DISCONNECT: |
| case MV_SCSI_COMPLETION_BUS_RESET: |
| case MV_SCSI_COMPLETION_BUSY: |
| default: |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG_ERROR,"Bad Scsi completion status %d\n", |
| pCmdBlock->ScsiCommandCompletion); |
| host_status = DID_ERROR; |
| |
| } |
| if ((pCmdBlock->senseDataLength == 0) && (pCmdBlock->senseBufferLength)) |
| { |
| pCmdBlock->pSenseBuffer[0] = 0; |
| } |
| pInfo->SCpnt->result = host_status << 16 | (pCmdBlock->ScsiStatus & 0x3f); |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) |
| pInfo->SCpnt->resid = pCmdBlock->dataBufferLength - pCmdBlock->dataTransfered; |
| #else |
| scsi_set_resid(pInfo->SCpnt, pCmdBlock->dataBufferLength - pCmdBlock->dataTransfered); |
| #endif |
| if(pCmdBlock->dataBufferLength - pCmdBlock->dataTransfered) |
| { |
| mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "Scsi command completed with resid 0x%x\n",pCmdBlock->dataBufferLength - pCmdBlock->dataTransfered); |
| } |
| |
| { |
| MV_U8 channelIndex = pCmdBlock->bus; |
| release_ata_mem(pInfo); |
| mv_ial_lib_add_done_queue (pSataAdapter->IALData, channelIndex, SCpnt); |
| } |
| return MV_TRUE; |
| } |