mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-23 13:41:27 +00:00
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@187 d57e44dd-8a1f-0410-8b47-8ef2f437770f
3549 lines
118 KiB
C
3549 lines
118 KiB
C
/* $Id: isp_pci.c,v 1.126 2007/06/01 17:19:34 mjacob Exp $ */
|
|
/*
|
|
* Copyright (c) 1997-2007 by Matthew Jacob
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of the
|
|
* the GNU Public License ("GPL") with platforms where the prevalant license
|
|
* is the GNU Public License:
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*
|
|
* Matthew Jacob
|
|
* Feral Software
|
|
* 421 Laurel Avenue
|
|
* Menlo Park, CA 94025
|
|
* USA
|
|
*
|
|
* gplbsd at feral com
|
|
*/
|
|
/*
|
|
* Qlogic ISP Host Adapter PCI specific probe and attach routines
|
|
*/
|
|
#include "isp_linux.h"
|
|
static int isp_pci_mapmem = 0xffffffff;
|
|
#if defined(__sparc__)
|
|
#undef ioremap_nocache
|
|
#define ioremap_nocache ioremap
|
|
#endif
|
|
static int isplinux_pci_init(struct Scsi_Host *);
|
|
static uint32_t isp_pci_rd_reg(ispsoftc_t *, int);
|
|
static void isp_pci_wr_reg(ispsoftc_t *, int, uint32_t);
|
|
#if !(defined(ISP_DISABLE_1080_SUPPORT) && defined(ISP_DISABLE_12160_SUPPORT))
|
|
static uint32_t isp_pci_rd_reg_1080(ispsoftc_t *, int);
|
|
static void isp_pci_wr_reg_1080(ispsoftc_t *, int, uint32_t);
|
|
#endif
|
|
#if !(defined(ISP_DISABLE_1020_SUPPORT) && defined(ISP_DISABLE_1080_SUPPORT) && defined(ISP_DISABLE_12160_SUPPORT) && \
|
|
defined(ISP_DISABLE_2100_SUPPORT) && defined(ISP_DISABLE_2200_SUPPORT))
|
|
static int isp_pci_rd_isr(ispsoftc_t *, uint32_t *, uint16_t *, uint16_t *);
|
|
#endif
|
|
#ifndef ISP_DISABLE_2300_SUPPORT
|
|
static int isp_pci_rd_isr_2300(ispsoftc_t *, uint32_t *, uint16_t *, uint16_t *);
|
|
#endif
|
|
#ifndef ISP_DISABLE_2400_SUPPORT
|
|
static uint32_t isp_pci_rd_reg_2400(ispsoftc_t *, int);
|
|
static void isp_pci_wr_reg_2400(ispsoftc_t *, int, uint32_t);
|
|
static int isp_pci_rd_isr_2400(ispsoftc_t *, uint32_t *, uint16_t *, uint16_t *);
|
|
static int isp_pci_2400_dmasetup(ispsoftc_t *, XS_T *, ispreq_t *, uint32_t *, uint32_t);
|
|
#endif
|
|
static int isp_pci_mbxdma(ispsoftc_t *);
|
|
static int isp_pci_dmasetup(ispsoftc_t *, XS_T *, ispreq_t *, uint32_t *, uint32_t);
|
|
static void isp_pci_dmateardown(ispsoftc_t *, XS_T *, uint32_t);
|
|
|
|
#define FOURG_SEG(x) (((u64) (x)) & 0xffffffff00000000ULL)
|
|
#define SAME_4G(addr, cnt) (FOURG_SEG(addr) == FOURG_SEG(addr + cnt - 1))
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
|
|
#define ISP_IRQ_FLAGS SA_INTERRUPT | SA_SHIRQ
|
|
#else
|
|
#define ISP_IRQ_FLAGS IRQF_SHARED
|
|
#endif
|
|
|
|
#ifdef ISP_DAC_SUPPORTED
|
|
#define ISP_A64 1
|
|
#define HIWD(x) ((x) >> 32)
|
|
#define IS_HIGH_ISP_ADDR(addr) ((u64) addr > ((u64) 0xffffffffLL))
|
|
#else
|
|
#define ISP_A64 0
|
|
#define HIWD(x) 0
|
|
#define IS_HIGH_ISP_ADDR(addr) 0
|
|
#endif
|
|
#define LOWD(x) x
|
|
|
|
static void isp_pci_reset0(ispsoftc_t *);
|
|
static void isp_pci_reset1(ispsoftc_t *);
|
|
static void isp_pci_dumpregs(ispsoftc_t *, const char *);
|
|
static int isplinux_pci_exclude(struct pci_dev *);
|
|
|
|
|
|
#ifndef ISP_DISABLE_1020_SUPPORT
|
|
#include "asm_1040.h"
|
|
#define ISP_1040_RISC_CODE (void *) isp_1040_risc_code
|
|
#else
|
|
#define ISP_1040_RISC_CODE NULL
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_1080_SUPPORT
|
|
#include "asm_1080.h"
|
|
#define ISP_1080_RISC_CODE (void *) isp_1080_risc_code
|
|
#else
|
|
#define ISP_1080_RISC_CODE NULL
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_12160_SUPPORT
|
|
#include "asm_12160.h"
|
|
#define ISP_12160_RISC_CODE (void *) isp_12160_risc_code
|
|
#else
|
|
#define ISP_12160_RISC_CODE NULL
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2100_SUPPORT
|
|
#include "asm_2100.h"
|
|
#define ISP_2100_RISC_CODE (void *) isp_2100_risc_code
|
|
#else
|
|
#define ISP_2100_RISC_CODE NULL
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2200_SUPPORT
|
|
#include "asm_2200.h"
|
|
#define ISP_2200_RISC_CODE (void *) isp_2200_risc_code
|
|
#else
|
|
#define ISP_2200_RISC_CODE NULL
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2300_SUPPORT
|
|
#include "asm_2300.h"
|
|
#define ISP_2300_RISC_CODE (void *) isp_2300_risc_code
|
|
#else
|
|
#define ISP_2300_RISC_CODE NULL
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2300_SUPPORT
|
|
#include "asm_2322.h"
|
|
#define ISP_2322_RISC_CODE (void *) isp_2322_risc_code
|
|
#else
|
|
#define ISP_2322_RISC_CODE NULL
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2400_SUPPORT
|
|
#include "asm_2400.h"
|
|
#define ISP_2400_RISC_CODE (void *) isp_2400_risc_code
|
|
#else
|
|
#define ISP_2400_RISC_CODE NULL
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_1020_SUPPORT
|
|
static struct ispmdvec mdvec = {
|
|
isp_pci_rd_isr,
|
|
isp_pci_rd_reg,
|
|
isp_pci_wr_reg,
|
|
isp_pci_mbxdma,
|
|
isp_pci_dmasetup,
|
|
isp_pci_dmateardown,
|
|
isp_pci_reset0,
|
|
isp_pci_reset1,
|
|
isp_pci_dumpregs,
|
|
ISP_1040_RISC_CODE,
|
|
BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64
|
|
};
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_1080_SUPPORT
|
|
static struct ispmdvec mdvec_1080 = {
|
|
isp_pci_rd_isr,
|
|
isp_pci_rd_reg_1080,
|
|
isp_pci_wr_reg_1080,
|
|
isp_pci_mbxdma,
|
|
isp_pci_dmasetup,
|
|
isp_pci_dmateardown,
|
|
isp_pci_reset0,
|
|
isp_pci_reset1,
|
|
isp_pci_dumpregs,
|
|
ISP_1080_RISC_CODE,
|
|
BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_128
|
|
};
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_12160_SUPPORT
|
|
static struct ispmdvec mdvec_12160 = {
|
|
isp_pci_rd_isr,
|
|
isp_pci_rd_reg_1080,
|
|
isp_pci_wr_reg_1080,
|
|
isp_pci_mbxdma,
|
|
isp_pci_dmasetup,
|
|
isp_pci_dmateardown,
|
|
isp_pci_reset0,
|
|
isp_pci_reset1,
|
|
isp_pci_dumpregs,
|
|
ISP_12160_RISC_CODE,
|
|
BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_128
|
|
};
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2100_SUPPORT
|
|
static struct ispmdvec mdvec_2100 = {
|
|
isp_pci_rd_isr,
|
|
isp_pci_rd_reg,
|
|
isp_pci_wr_reg,
|
|
isp_pci_mbxdma,
|
|
isp_pci_dmasetup,
|
|
isp_pci_dmateardown,
|
|
isp_pci_reset0,
|
|
isp_pci_reset1,
|
|
isp_pci_dumpregs,
|
|
ISP_2100_RISC_CODE
|
|
};
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2200_SUPPORT
|
|
static struct ispmdvec mdvec_2200 = {
|
|
isp_pci_rd_isr,
|
|
isp_pci_rd_reg,
|
|
isp_pci_wr_reg,
|
|
isp_pci_mbxdma,
|
|
isp_pci_dmasetup,
|
|
isp_pci_dmateardown,
|
|
isp_pci_reset0,
|
|
isp_pci_reset1,
|
|
isp_pci_dumpregs,
|
|
ISP_2200_RISC_CODE
|
|
};
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2300_SUPPORT
|
|
static struct ispmdvec mdvec_2300 = {
|
|
isp_pci_rd_isr_2300,
|
|
isp_pci_rd_reg,
|
|
isp_pci_wr_reg,
|
|
isp_pci_mbxdma,
|
|
isp_pci_dmasetup,
|
|
isp_pci_dmateardown,
|
|
isp_pci_reset0,
|
|
isp_pci_reset1,
|
|
isp_pci_dumpregs,
|
|
ISP_2300_RISC_CODE
|
|
};
|
|
static struct ispmdvec mdvec_2322 = {
|
|
isp_pci_rd_isr_2300,
|
|
isp_pci_rd_reg,
|
|
isp_pci_wr_reg,
|
|
isp_pci_mbxdma,
|
|
isp_pci_dmasetup,
|
|
isp_pci_dmateardown,
|
|
isp_pci_reset0,
|
|
isp_pci_reset1,
|
|
isp_pci_dumpregs,
|
|
ISP_2322_RISC_CODE
|
|
};
|
|
#endif
|
|
#ifndef ISP_DISABLE_2400_SUPPORT
|
|
static struct ispmdvec mdvec_2400 = {
|
|
isp_pci_rd_isr_2400,
|
|
isp_pci_rd_reg_2400,
|
|
isp_pci_wr_reg_2400,
|
|
isp_pci_mbxdma,
|
|
isp_pci_2400_dmasetup,
|
|
isp_pci_dmateardown,
|
|
isp_pci_reset0,
|
|
isp_pci_reset1,
|
|
NULL,
|
|
ISP_2400_RISC_CODE
|
|
};
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP1020
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP1020
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP1080
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP10160
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP12160
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP1240
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP1280
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2100
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2200
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2300
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2312
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2322
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2422
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP2422 0x2422
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2432
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP2432 0x2432
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP6312
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP6312 0x6312
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_QLOGIC_ISP6322
|
|
#define PCI_DEVICE_ID_QLOGIC_ISP6322 0x6322
|
|
#endif
|
|
|
|
#define PCI_DFLT_LTNCY 0x40
|
|
#define PCI_DFLT_LNSZ 0x10
|
|
#define PCI_CMD_ISP (PCI_COMMAND_MASTER|PCI_COMMAND_INVALIDATE|PCI_COMMAND_PARITY|PCI_COMMAND_SERR)
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
|
|
static struct device_driver isp_pci_device_driver;
|
|
#endif
|
|
|
|
/*
|
|
* Encapsulating softc... Order of elements is important. The tag
|
|
* pci_isp must come first because of multiple structure punning
|
|
* (Scsi_Host * == struct isp_pcisoftc * == struct ispsofct *).
|
|
*/
|
|
struct isp_pcisoftc {
|
|
ispsoftc_t pci_isp;
|
|
struct pci_dev * pci_dev;
|
|
vm_offset_t port; /* I/O port address */
|
|
vm_offset_t paddr; /* Physical Memory Address */
|
|
void * vaddr; /* Mapped Memory Address */
|
|
vm_offset_t voff;
|
|
vm_offset_t poff[_NREG_BLKS];
|
|
union pstore params;
|
|
};
|
|
|
|
/*
|
|
* Gratefully borrowed from Gerard Roudier's sym53c8xx driver
|
|
*/
|
|
static __inline void *
|
|
map_pci_mem(struct isp_pcisoftc *isp_pci, u_long size)
|
|
{
|
|
unsigned long page_base;
|
|
unsigned long map_size;
|
|
u8 *page_remapped;
|
|
|
|
page_base = isp_pci->paddr & PAGE_MASK;
|
|
isp_pci->voff = isp_pci->paddr - page_base;
|
|
map_size = roundup(isp_pci->voff + size, PAGE_SIZE);
|
|
page_remapped = ioremap_nocache(page_base, map_size);
|
|
if (page_remapped) {
|
|
page_remapped += isp_pci->voff;
|
|
}
|
|
return (page_remapped);
|
|
}
|
|
|
|
static __inline
|
|
void unmap_pci_mem(struct isp_pcisoftc *isp_pci, unsigned long size)
|
|
{
|
|
if (isp_pci->vaddr) {
|
|
u8 *p = isp_pci->vaddr;
|
|
p += isp_pci->voff;
|
|
iounmap(p);
|
|
}
|
|
}
|
|
|
|
static __inline int
|
|
map_isp_mem(struct isp_pcisoftc *isp_pci, u_short cmd, vm_offset_t mem_base)
|
|
{
|
|
if (cmd & PCI_COMMAND_MEMORY) {
|
|
isp_pci->paddr = mem_base;
|
|
isp_pci->paddr &= PCI_BASE_ADDRESS_MEM_MASK;
|
|
isp_pci->vaddr = map_pci_mem(isp_pci, 0xff);
|
|
return (isp_pci->vaddr != (void *) 0);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static __inline int
|
|
map_isp_io(struct isp_pcisoftc *isp_pci, u_short cmd, vm_offset_t io_base)
|
|
{
|
|
if ((cmd & PCI_COMMAND_IO) && (io_base & 3) == 1) {
|
|
isp_pci->port = io_base & PCI_BASE_ADDRESS_IO_MASK;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
if (check_region(isp_pci->port, 0xff)) {
|
|
return (0);
|
|
}
|
|
#endif
|
|
request_region(isp_pci->port, 0xff, "isp");
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#define ISEARCH(x) (pcidev = pci_find_device(PCI_VENDOR_ID_QLOGIC, x, pcidev)) != NULL
|
|
|
|
static struct isp_pcisoftc *
|
|
isplinux_pci_addhost(Scsi_Host_Template *tmpt, struct pci_dev *pcidev)
|
|
{
|
|
struct Scsi_Host *host;
|
|
ispsoftc_t *isp;
|
|
struct isp_pcisoftc *pci_isp;
|
|
int i;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
|
|
|
|
host = scsi_host_alloc(tmpt, sizeof(struct isp_pcisoftc));
|
|
if (host == NULL) {
|
|
return (NULL);
|
|
}
|
|
pci_isp = (struct isp_pcisoftc *) host->hostdata;
|
|
pci_set_drvdata(pcidev, pci_isp);
|
|
pci_isp->pci_dev = pcidev;
|
|
isp = (ispsoftc_t *) pci_isp;
|
|
isp->isp_host = host;
|
|
isp->isp_osinfo.storep = &pci_isp->params;
|
|
isp->isp_osinfo.device = pcidev;
|
|
|
|
host->unique_id = isp_unit_seed;
|
|
if (isplinux_pci_init(host)) {
|
|
scsi_host_put(host);
|
|
return (NULL);
|
|
}
|
|
|
|
pcidev->dev.driver = &isp_pci_device_driver;
|
|
if (scsi_add_host(host, &pcidev->dev)) {
|
|
scsi_host_put(host);
|
|
return (NULL);
|
|
}
|
|
#else
|
|
host = scsi_register(tmpt, sizeof(struct isp_pcisoftc));
|
|
if (host == NULL) {
|
|
printk("isplinux_pci_addhost: scsi_register failed\n");
|
|
return (NULL);
|
|
}
|
|
pci_isp = (struct isp_pcisoftc *) host->hostdata;
|
|
if (pci_isp == NULL) {
|
|
scsi_unregister(host);
|
|
printk("isplinux_pci_addhost: cannot get softc out of scsi_register\n");
|
|
return (NULL);
|
|
}
|
|
pci_isp->pci_dev = pcidev;
|
|
isp = (ispsoftc_t *) pci_isp;
|
|
isp->isp_host = host;
|
|
isp->isp_osinfo.storep = &pci_isp->params;
|
|
isp->isp_osinfo.device = pcidev;
|
|
|
|
if (isplinux_pci_init(host)) {
|
|
scsi_unregister(host);
|
|
return (NULL);
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
|
|
scsi_set_pci_device(host, pci_isp->pci_dev);
|
|
#endif
|
|
#endif
|
|
for (i = 0; i < MAX_ISP; i++) {
|
|
if (isplist[i] == NULL) {
|
|
isplist[i] = isp;
|
|
break;
|
|
}
|
|
}
|
|
return (pci_isp);
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
#include <linux/reboot.h>
|
|
static int
|
|
isp_notify_reboot(struct notifier_block *ispnb, unsigned long Event, void *b)
|
|
{
|
|
unsigned long flags;
|
|
ispsoftc_t *isp;
|
|
int i;
|
|
|
|
switch (Event) {
|
|
case SYS_RESTART:
|
|
case SYS_HALT:
|
|
case SYS_POWER_OFF:
|
|
break;
|
|
default:
|
|
return (NOTIFY_DONE);
|
|
}
|
|
for (i = 0; i < MAX_ISP; i++) {
|
|
isp = isplist[i];
|
|
if (isp == NULL) {
|
|
continue;
|
|
}
|
|
ISP_LOCKU_SOFTC(isp);
|
|
isp_shutdown(isp);
|
|
ISP_UNLKU_SOFTC(isp);
|
|
}
|
|
return (NOTIFY_OK);
|
|
}
|
|
static struct notifier_block isp_notifier = {
|
|
notifier_call: isp_notify_reboot,
|
|
next: NULL,
|
|
priority: 0
|
|
};
|
|
#endif
|
|
|
|
static int isp_nfound = 0;
|
|
int
|
|
isplinux_pci_detect(Scsi_Host_Template *tmpt)
|
|
{
|
|
static const char *fmt =
|
|
KERN_INFO "ISP SCSI and Fibre Channel Host Adapter Driver\n"
|
|
KERN_INFO " Linux Platform Version %d.%d\n"
|
|
KERN_INFO " Common Core Code Version %d.%d\n"
|
|
KERN_INFO " Built on %s, %s\n";
|
|
struct isp_pcisoftc *pci_isp;
|
|
struct pci_dev *pcidev;
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
if (pci_present() == 0) {
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
printk(fmt, ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR, ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR, __DATE__ , __TIME__ );
|
|
|
|
#ifndef ISP_DISABLE_1020_SUPPORT
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP1020)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_1080_SUPPORT
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP1240)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP1080)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP1280)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_12160_SUPPORT
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP10160)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP12160)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2100_SUPPORT
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP2100)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2200_SUPPORT
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP2200)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2300_SUPPORT
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP2300)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP2312)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP2322)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP6312)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP6322)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
#endif
|
|
#ifndef ISP_DISABLE_2400_SUPPORT
|
|
pcidev = NULL;
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP2422)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
while(ISEARCH(PCI_DEVICE_ID_QLOGIC_ISP2432)) {
|
|
if (isplinux_pci_exclude(pcidev)) {
|
|
continue;
|
|
}
|
|
pci_isp = isplinux_pci_addhost(tmpt, pcidev);
|
|
if (pci_isp) {
|
|
isp_nfound++;
|
|
}
|
|
}
|
|
#endif
|
|
/*
|
|
* Don't do reboot notifier stuff for 2.5.X yet
|
|
*/
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
if (isp_nfound) {
|
|
register_reboot_notifier(&isp_notifier);
|
|
}
|
|
#endif
|
|
return (isp_nfound);
|
|
}
|
|
|
|
void
|
|
isplinux_pci_release(struct Scsi_Host *host)
|
|
{
|
|
ispsoftc_t *isp = (ispsoftc_t *) host->hostdata;
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) host->hostdata;
|
|
int i;
|
|
|
|
pci_disable_device(pcs->pci_dev);
|
|
free_irq(host->irq, pcs);
|
|
if (pcs->vaddr != 0) {
|
|
unmap_pci_mem(pcs, 0xff);
|
|
pcs->vaddr = 0;
|
|
} else if (pcs->port) {
|
|
release_region(pcs->port, 0xff);
|
|
pcs->port = 0;
|
|
}
|
|
if (isp->isp_rquest) {
|
|
pci_free_consistent(pcs->pci_dev, RQUEST_QUEUE_LEN(isp) * QENTRY_LEN, isp->isp_rquest, isp->isp_rquest_dma);
|
|
isp->isp_rquest = NULL;
|
|
}
|
|
if (isp->isp_xflist) {
|
|
isp_kfree(isp->isp_xflist, isp->isp_osinfo.mcorig * sizeof (XS_T **));
|
|
isp->isp_xflist = NULL;
|
|
}
|
|
#ifdef ISP_TARGET_MODE
|
|
if (isp->isp_tgtlist) {
|
|
isp_kfree(isp->isp_tgtlist, isp->isp_osinfo.mcorig * sizeof (void **));
|
|
isp->isp_tgtlist = NULL;
|
|
}
|
|
#endif
|
|
if (isp->isp_result) {
|
|
pci_free_consistent(pcs->pci_dev, RESULT_QUEUE_LEN(isp) * QENTRY_LEN, isp->isp_result, isp->isp_result_dma);
|
|
isp->isp_result = NULL;
|
|
}
|
|
if (IS_FC(isp)) {
|
|
for (i = 0; i < isp->isp_nchan; i++) {
|
|
fcparam *fcp = FCPARAM(isp, i);
|
|
if (fcp->isp_scratch) {
|
|
pci_free_consistent(pcs->pci_dev, ISP_FC_SCRLEN, fcp->isp_scratch, fcp->isp_scdma);
|
|
fcp->isp_scratch = NULL;
|
|
}
|
|
}
|
|
}
|
|
pci_release_regions(pcs->pci_dev);
|
|
|
|
/*
|
|
* Pull ourselves off the global list
|
|
*/
|
|
for (i = 0; i < MAX_ISP; i++) {
|
|
if (isplist[i] == isp) {
|
|
isplist[i] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
if (--isp_nfound <= 0) {
|
|
unregister_reboot_notifier(&isp_notifier);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
isplinux_pci_init(struct Scsi_Host *host)
|
|
{
|
|
static char *nomap = "cannot map either memory or I/O space";
|
|
unsigned long io_base, mem_base;
|
|
unsigned int irq, pci_cmd_isp = PCI_CMD_ISP;
|
|
struct isp_pcisoftc *isp_pci;
|
|
u_char rev, lnsz, timer;
|
|
u_short vid, did, cmd;
|
|
char loc[32];
|
|
ispsoftc_t *isp;
|
|
int dowrite = 0;
|
|
|
|
isp_pci = (struct isp_pcisoftc *) host->hostdata;
|
|
isp = (ispsoftc_t *) isp_pci;
|
|
|
|
if (pci_request_regions(isp_pci->pci_dev, "isp")) {
|
|
return (1);
|
|
}
|
|
|
|
sprintf(loc, "isp@<PCI%d,Slot%d,Func%d>", isp_pci->pci_dev->bus->number, PCI_SLOT(isp_pci->pci_dev->devfn), PCI_FUNC(isp_pci->pci_dev->devfn));
|
|
if (PRDW(isp_pci, PCI_COMMAND, &cmd) || PRDB(isp_pci, PCI_CACHE_LINE_SIZE, &lnsz) || PRDB(isp_pci, PCI_LATENCY_TIMER, &timer) || PRDB(isp_pci, PCI_CLASS_REVISION, &rev)) {
|
|
printk("%s: error reading PCI configuration\n", loc);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
vid = isp_pci->pci_dev->vendor;
|
|
did = isp_pci->pci_dev->device;
|
|
|
|
io_base = pci_resource_start(isp_pci->pci_dev, 0);
|
|
if (pci_resource_flags(isp_pci->pci_dev, 0) & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
|
irq = 2;
|
|
} else {
|
|
irq = 1;
|
|
}
|
|
mem_base = pci_resource_start(isp_pci->pci_dev, irq);
|
|
if (pci_resource_flags(isp_pci->pci_dev, irq) & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
|
#if BITS_PER_LONG == 64
|
|
mem_base |= pci_resource_start(isp_pci->pci_dev, irq+1) << 32;
|
|
#else
|
|
isp_pci_mapmem &= ~(1 << isp->isp_unit);
|
|
#endif
|
|
}
|
|
|
|
if (vid != PCI_VENDOR_ID_QLOGIC) {
|
|
printk("%s: 0x%04x is not QLogic's PCI Vendor ID\n", loc, vid);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
|
|
isp_pci->poff[BIU_BLOCK >> _BLK_REG_SHFT] = BIU_REGS_OFF;
|
|
isp_pci->poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS_OFF;
|
|
isp_pci->poff[SXP_BLOCK >> _BLK_REG_SHFT] = PCI_SXP_REGS_OFF;
|
|
isp_pci->poff[RISC_BLOCK >> _BLK_REG_SHFT] = PCI_RISC_REGS_OFF;
|
|
isp_pci->poff[DMA_BLOCK >> _BLK_REG_SHFT] = DMA_REGS_OFF;
|
|
isp->isp_nchan = 1;
|
|
switch (did) {
|
|
case PCI_DEVICE_ID_QLOGIC_ISP1020:
|
|
break;
|
|
case PCI_DEVICE_ID_QLOGIC_ISP1240:
|
|
case PCI_DEVICE_ID_QLOGIC_ISP1280:
|
|
case PCI_DEVICE_ID_QLOGIC_ISP12160:
|
|
isp->isp_nchan = 2;
|
|
/* FALLTHROUGH */
|
|
case PCI_DEVICE_ID_QLOGIC_ISP1080:
|
|
case PCI_DEVICE_ID_QLOGIC_ISP10160:
|
|
isp_pci->poff[DMA_BLOCK >> _BLK_REG_SHFT] = ISP1080_DMA_REGS_OFF;
|
|
break;
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2200:
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2100:
|
|
isp_pci->poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS2100_OFF;
|
|
break;
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2300:
|
|
pci_cmd_isp &= ~PCI_COMMAND_INVALIDATE; /* per errata */
|
|
isp_pci->poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS2300_OFF;
|
|
break;
|
|
case PCI_DEVICE_ID_QLOGIC_ISP6312:
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2312:
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2322:
|
|
isp->isp_port = PCI_FUNC(isp_pci->pci_dev->devfn);
|
|
isp_pci->poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS2300_OFF;
|
|
break;
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2422:
|
|
case PCI_DEVICE_ID_QLOGIC_ISP2432:
|
|
isp->isp_port = PCI_FUNC(isp_pci->pci_dev->devfn);
|
|
isp_pci->poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS2400_OFF;
|
|
break;
|
|
default:
|
|
printk("%s: Device ID 0x%04x is not a known Qlogic Device\n", loc, did);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Bump unit seed- we're here, whether we complete the attachment or not.
|
|
*/
|
|
isp->isp_unit = isp_unit_seed++;
|
|
sprintf(isp->isp_name, "isp%d", isp->isp_unit);
|
|
|
|
isp->isp_osinfo.device_id = ((isp_pci->pci_dev->bus->number) << 16) | (PCI_SLOT(isp_pci->pci_dev->devfn) << 8) | (PCI_FUNC(isp_pci->pci_dev->devfn));
|
|
|
|
if (isp_disable & (1 << isp->isp_unit)) {
|
|
printk("%s: disabled at user request\n", loc);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
|
|
if (pci_enable_device(isp_pci->pci_dev)) {
|
|
printk("%s: fails to be PCI_ENABLEd\n", loc);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
|
|
(void) PRDW(isp_pci, PCI_COMMAND, &cmd);
|
|
|
|
if ((cmd & PCI_CMD_ISP) != pci_cmd_isp) {
|
|
if (isp_debug & ISP_LOGINFO) {
|
|
printk("%s: rewriting command register from 0x%x to 0x%x\n", loc, cmd, (cmd & ~PCI_CMD_ISP) | pci_cmd_isp);
|
|
}
|
|
cmd &= ~PCI_CMD_ISP;
|
|
cmd |= pci_cmd_isp;
|
|
dowrite = 1;
|
|
}
|
|
|
|
/* PCI Rev 2.3 changes */
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP6312 || did == PCI_DEVICE_ID_QLOGIC_ISP2322) {
|
|
if (cmd & PCI_COMMAND_INTX_DISABLE) {
|
|
cmd &= ~PCI_COMMAND_INTX_DISABLE;
|
|
dowrite = 1;
|
|
}
|
|
}
|
|
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP2422 || did == PCI_DEVICE_ID_QLOGIC_ISP2432) {
|
|
|
|
int reg;
|
|
|
|
cmd &= ~PCI_COMMAND_INTX_DISABLE;
|
|
dowrite = 1;
|
|
|
|
/*
|
|
* Is this a PCI-X card? If so, set max read byte count.
|
|
*/
|
|
reg = pci_find_capability(isp_pci->pci_dev, PCI_CAP_ID_PCIX);
|
|
if (reg) {
|
|
uint16_t pxcmd;
|
|
|
|
reg += 0x2;
|
|
PRDW(isp_pci, reg, &pxcmd);
|
|
pxcmd &= ~PCI_X_CMD_MAX_READ;
|
|
pxcmd |= 0x8;
|
|
PWRW(isp_pci, reg, pxcmd);
|
|
}
|
|
|
|
/*
|
|
* Is this a PCI Express card? If so, set max read byte count.
|
|
*/
|
|
reg = pci_find_capability(isp_pci->pci_dev, PCI_CAP_ID_EXP);
|
|
if (reg) {
|
|
uint16_t pectl;
|
|
|
|
reg += 0x8;
|
|
PRDW(isp_pci, reg, &pectl);
|
|
pectl &= ~0x7000;
|
|
pectl |= 0x4000;
|
|
PWRW(isp_pci, reg, pectl);
|
|
}
|
|
}
|
|
|
|
if (dowrite) {
|
|
PWRW(isp_pci, PCI_COMMAND, cmd);
|
|
}
|
|
|
|
if (lnsz != PCI_DFLT_LNSZ) {
|
|
if (isp_debug & ISP_LOGINFO) {
|
|
printk("%s: rewriting cache line size from 0x%x to 0x%x\n", loc, lnsz, PCI_DFLT_LNSZ);
|
|
}
|
|
lnsz = PCI_DFLT_LNSZ;
|
|
PWRB(isp_pci, PCI_CACHE_LINE_SIZE, lnsz);
|
|
}
|
|
|
|
#ifdef __sparc__
|
|
if (PRDB(isp_pci, PCI_MIN_GNT, &rev)) {
|
|
printk("%s: unable to read min grant\n", loc);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
if (rev) {
|
|
rev = (rev << 3) & 0xff;
|
|
}
|
|
if (rev == 0) {
|
|
rev = 64;
|
|
}
|
|
if (isp_debug & ISP_LOGINFO) {
|
|
printk("%s: rewriting latency timer from 0x%x to 0x%x\n", loc, timer, rev);
|
|
}
|
|
PWRB(isp_pci, PCI_LATENCY_TIMER, rev);
|
|
#else
|
|
if (timer < PCI_DFLT_LTNCY) {
|
|
if (isp_debug & ISP_LOGINFO) {
|
|
printk("%s: rewriting latency timer from 0x%x to 0x%x\n", loc, timer, PCI_DFLT_LTNCY);
|
|
}
|
|
timer = PCI_DFLT_LTNCY;
|
|
PWRB(isp_pci, PCI_LATENCY_TIMER, timer);
|
|
}
|
|
#endif
|
|
|
|
if ((cmd & (PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) == 0) {
|
|
#ifdef __powerpc__
|
|
if (io_base == 0 && mem_base == 0) {
|
|
printk("%s: you lose- no register access defined\n", loc);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
if (io_base) {
|
|
cmd |= PCI_COMMAND_IO;
|
|
}
|
|
if (mem_base) {
|
|
cmd |= PCI_COMMAND_MEMORY;
|
|
}
|
|
PWRW(isp_pci, PCI_COMMAND, cmd);
|
|
#else
|
|
printk("%s: you lose- no register access defined\n", loc);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Disable the ROM.
|
|
*/
|
|
PWRL(isp_pci, PCI_ROM_ADDRESS, 0);
|
|
|
|
/*
|
|
* Set up stuff...
|
|
*/
|
|
isp_pci->port = 0;
|
|
isp_pci->vaddr = NULL;
|
|
|
|
/*
|
|
* If we prefer to map memory space over I/O, try that first.
|
|
*/
|
|
if (isp_pci_mapmem & (1 << isp->isp_unit)) {
|
|
if (map_isp_mem(isp_pci, cmd, mem_base) == 0) {
|
|
if (map_isp_io(isp_pci, cmd, io_base) == 0) {
|
|
printk("%s: %s\n", loc, nomap);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
}
|
|
} else {
|
|
if (map_isp_io(isp_pci, cmd, io_base) == 0) {
|
|
if (map_isp_mem(isp_pci, cmd, mem_base) == 0) {
|
|
printk("%s: %s\n", loc, nomap);
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
}
|
|
}
|
|
if (isp_pci->vaddr) {
|
|
if (isp_debug & ISP_LOGCONFIG) {
|
|
printk("%s: mapped memory 0x%lx at %p\n", loc, isp_pci->paddr, isp_pci->vaddr);
|
|
}
|
|
host->io_port = isp_pci->paddr;
|
|
} else {
|
|
if (isp_debug & ISP_LOGCONFIG) {
|
|
printk("%s: mapped I/O space at 0x%lx\n", loc, isp_pci->port);
|
|
}
|
|
host->io_port = isp_pci->port;
|
|
}
|
|
host->irq = 0;
|
|
isp_pci->pci_isp.isp_revision = rev;
|
|
#ifndef ISP_DISABLE_1020_SUPPORT
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP1020) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_SCSI_UNKNOWN;
|
|
}
|
|
#endif
|
|
#ifndef ISP_DISABLE_1080_SUPPORT
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP1080) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_1080;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_SCSI_1080;
|
|
}
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP1240) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_1080;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_SCSI_1240;
|
|
host->max_channel = 1;
|
|
}
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP1280) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_1080;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_SCSI_1280;
|
|
host->max_channel = 1;
|
|
}
|
|
#endif
|
|
#ifndef ISP_DISABLE_12160_SUPPORT
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP10160) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_12160;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_SCSI_12160;
|
|
}
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP12160) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_12160;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_SCSI_12160;
|
|
host->max_channel = 1;
|
|
}
|
|
#endif
|
|
#ifndef ISP_DISABLE_2100_SUPPORT
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP2100) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_2100;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_FC_2100;
|
|
}
|
|
#endif
|
|
#ifndef ISP_DISABLE_2200_SUPPORT
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP2200) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_2200;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_FC_2200;
|
|
}
|
|
#endif
|
|
#ifndef ISP_DISABLE_2300_SUPPORT
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP2300) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_2300;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_FC_2300;
|
|
}
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP2312) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_2300;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_FC_2312;
|
|
}
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP2322) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_2322;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_FC_2322;
|
|
}
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP6312) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_2300;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_FC_2312;
|
|
}
|
|
|
|
if (IS_23XX(isp)) {
|
|
/*
|
|
* Can't tell if the ROM will hang on 'ABOUT FIRMWARE' command
|
|
*/
|
|
isp->isp_touched = 1;
|
|
}
|
|
#endif
|
|
#ifndef ISP_DISABLE_2400_SUPPORT
|
|
if (did == PCI_DEVICE_ID_QLOGIC_ISP2422 || did == PCI_DEVICE_ID_QLOGIC_ISP2432) {
|
|
isp_pci->pci_isp.isp_mdvec = &mdvec_2400;
|
|
isp_pci->pci_isp.isp_type = ISP_HA_FC_2400;
|
|
}
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
host->select_queue_depths = isplinux_sqd;
|
|
#endif
|
|
isp->isp_param = &isp_pci->params;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18)
|
|
/*
|
|
* All PCI QLogic cards really can do full 32 bit PCI transactions,
|
|
* at least. But the older cards (1020s) have a 24 bit segment limit
|
|
* where the dma address can't cross a 24 bit boundary. Until we get
|
|
* have segment aware midlayer code, we'll set the DMA mask as if
|
|
* we only could do 24 bit I/O for those cards.
|
|
*
|
|
* We can turn on highmem_io for all of them as we use the PCI dma mapping
|
|
* API.
|
|
*
|
|
* We use our synthetic ISP_A64 define here because this allows us to
|
|
* remove code we wouldn't want to try and use if we don't have
|
|
* CONFIG_HIGHMEM64G defined.
|
|
*/
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
host->highmem_io = 1;
|
|
#endif
|
|
|
|
if (isp->isp_type < ISP_HA_SCSI_1240) {
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
host->highmem_io = 0;
|
|
#endif
|
|
if (pci_set_dma_mask(isp_pci->pci_dev, (u64)0x00ffffff)) {
|
|
printk("%s: cannot set 24 bit dma mask\n", loc);
|
|
goto bad;
|
|
}
|
|
} else if (ISP_A64) {
|
|
if (pci_set_dma_mask(isp_pci->pci_dev, (u64) 0xffffffffffffffffULL)) {
|
|
if (pci_set_dma_mask(isp_pci->pci_dev, (u64) 0xffffffff)) {
|
|
printk("%s: cannot set 32 bit dma mask\n", loc);
|
|
goto bad;
|
|
}
|
|
} else {
|
|
if (isp_debug & ISP_LOGCONFIG) {
|
|
printk("%s: enabling 64 bit DMA\n", loc);
|
|
}
|
|
}
|
|
} else {
|
|
if (pci_set_dma_mask(isp_pci->pci_dev, (u64)0xffffffff)) {
|
|
printk("%s: cannot set 32 bit dma mask\n", loc);
|
|
goto bad;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (isplinux_common_init(isp)) {
|
|
printk("%s: isplinux_common_init failed\n", loc);
|
|
goto bad;
|
|
}
|
|
|
|
if (IS_FC(isp)) {
|
|
host->max_cmd_len = 16;
|
|
} else {
|
|
host->max_cmd_len = 12;
|
|
}
|
|
|
|
irq = isp_pci->pci_dev->irq;
|
|
if (request_irq(irq, isplinux_intr, ISP_IRQ_FLAGS, isp->isp_name, isp_pci)) {
|
|
printk("%s: could not snag irq %u (0x%x)\n", loc, irq, irq);
|
|
goto bad;
|
|
}
|
|
host->irq = irq;
|
|
|
|
return (0);
|
|
bad:
|
|
if (isp_pci->vaddr != 0) {
|
|
unmap_pci_mem(isp_pci, 0xff);
|
|
isp_pci->vaddr = 0;
|
|
} else {
|
|
release_region(isp_pci->port, 0xff);
|
|
isp_pci->port = 0;
|
|
}
|
|
pci_release_regions(isp_pci->pci_dev);
|
|
return (1);
|
|
}
|
|
|
|
static __inline uint32_t
|
|
ispregrd(struct isp_pcisoftc *pcs, vm_offset_t offset)
|
|
{
|
|
uint32_t rv;
|
|
if (pcs->vaddr) {
|
|
u8 *addr = pcs->vaddr;
|
|
rv = readw(addr+offset);
|
|
} else {
|
|
offset += pcs->port;
|
|
rv = inw(offset);
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
static __inline void
|
|
ispregwr(struct isp_pcisoftc *pcs, vm_offset_t offset, uint32_t val)
|
|
{
|
|
if (pcs->vaddr) {
|
|
u8 *addr = pcs->vaddr;
|
|
writew(val, addr+offset);
|
|
} else {
|
|
offset += pcs->port;
|
|
outw(val, offset);
|
|
}
|
|
}
|
|
|
|
static __inline int
|
|
isp_pci_rd_debounced(struct isp_pcisoftc *pcs, vm_offset_t off, uint16_t *rp)
|
|
{
|
|
uint16_t val0, val1;
|
|
int i = 0;
|
|
do {
|
|
val0 = ispregrd(pcs, off);
|
|
val1 = ispregrd(pcs, off);
|
|
} while (val0 != val1 && ++i < 1000);
|
|
if (val0 != val1) {
|
|
return (1);
|
|
}
|
|
*rp = val0;
|
|
return (0);
|
|
}
|
|
|
|
#define IspVirt2Off(a, x) ((a)->poff[((x) & _BLK_REG_MASK) >> _BLK_REG_SHFT] + ((x) & 0xff))
|
|
|
|
#if !(defined(ISP_DISABLE_1020_SUPPORT) && defined(ISP_DISABLE_1080_SUPPORT) && defined(ISP_DISABLE_12160_SUPPORT) && defined(ISP_DISABLE_2100_SUPPORT) && defined(ISP_DISABLE_2200_SUPPORT))
|
|
static int
|
|
isp_pci_rd_isr(ispsoftc_t *isp, uint32_t *isrp, uint16_t *semap, uint16_t *mbp)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
uint16_t isr, sema;
|
|
|
|
if (IS_2100(isp)) {
|
|
if (isp_pci_rd_debounced(pcs, IspVirt2Off(pcs, BIU_ISR), &isr)) {
|
|
return (0);
|
|
}
|
|
if (isp_pci_rd_debounced(pcs, IspVirt2Off(pcs, BIU_SEMA), &sema)) {
|
|
return (0);
|
|
}
|
|
} else {
|
|
isr = ispregrd(pcs, IspVirt2Off(pcs, BIU_ISR));
|
|
sema = ispregrd(pcs, IspVirt2Off(pcs, BIU_SEMA));
|
|
}
|
|
isp_prt(isp, ISP_LOGDEBUG3, "ISR 0x%x SEMA 0x%x", isr, sema);
|
|
isr &= INT_PENDING_MASK(isp);
|
|
sema &= BIU_SEMA_LOCK;
|
|
if (isr == 0 && sema == 0) {
|
|
return (0);
|
|
}
|
|
*isrp = isr;
|
|
if ((*semap = sema) != 0) {
|
|
if (IS_2100(isp)) {
|
|
if (isp_pci_rd_debounced(pcs, IspVirt2Off(pcs, OUTMAILBOX0), mbp)) {
|
|
return (0);
|
|
}
|
|
} else {
|
|
*mbp = ispregrd(pcs, IspVirt2Off(pcs, OUTMAILBOX0));
|
|
}
|
|
}
|
|
return (1);
|
|
}
|
|
#endif
|
|
|
|
#if !(defined(ISP_DISABLE_2300_SUPPORT) && defined(ISP_DISABLE_2400_SUPPORT))
|
|
static __inline uint32_t
|
|
ispregrd32(struct isp_pcisoftc *pcs, vm_offset_t offset)
|
|
{
|
|
uint32_t rv;
|
|
if (pcs->vaddr) {
|
|
u8 *addr = pcs->vaddr;
|
|
rv = readl(addr+offset);
|
|
} else {
|
|
offset += pcs->port;
|
|
rv = inl(offset);
|
|
}
|
|
return (rv);
|
|
}
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2300_SUPPORT
|
|
static int
|
|
isp_pci_rd_isr_2300(ispsoftc_t *isp, uint32_t *isrp, uint16_t *semap, uint16_t *mbox0p)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
uint32_t hccr;
|
|
uint32_t r2hisr;
|
|
|
|
if ((ispregrd(pcs, IspVirt2Off(pcs, BIU_ISR)) & BIU2100_ISR_RISC_INT) == 0) {
|
|
*isrp = 0;
|
|
return (0);
|
|
}
|
|
|
|
r2hisr = ispregrd32(pcs, IspVirt2Off(pcs, BIU_R2HSTSLO));
|
|
isp_prt(isp, ISP_LOGDEBUG3, "RISC2HOST ISR 0x%x", r2hisr);
|
|
if ((r2hisr & BIU_R2HST_INTR) == 0) {
|
|
*isrp = 0;
|
|
return (0);
|
|
}
|
|
switch (r2hisr & BIU_R2HST_ISTAT_MASK) {
|
|
case ISPR2HST_ROM_MBX_OK:
|
|
case ISPR2HST_ROM_MBX_FAIL:
|
|
case ISPR2HST_MBX_OK:
|
|
case ISPR2HST_MBX_FAIL:
|
|
case ISPR2HST_ASYNC_EVENT:
|
|
*isrp = r2hisr & 0xffff;
|
|
*mbox0p = (r2hisr >> 16);
|
|
*semap = 1;
|
|
return (1);
|
|
case ISPR2HST_RIO_16:
|
|
*isrp = r2hisr & 0xffff;
|
|
*mbox0p = ASYNC_RIO1;
|
|
*semap = 1;
|
|
return (1);
|
|
case ISPR2HST_FPOST:
|
|
*isrp = r2hisr & 0xffff;
|
|
*mbox0p = ASYNC_CMD_CMPLT;
|
|
*semap = 1;
|
|
return (1);
|
|
case ISPR2HST_FPOST_CTIO:
|
|
*isrp = r2hisr & 0xffff;
|
|
*mbox0p = ASYNC_CTIO_DONE;
|
|
*semap = 1;
|
|
return (1);
|
|
case ISPR2HST_RSPQ_UPDATE:
|
|
*isrp = r2hisr & 0xffff;
|
|
*mbox0p = 0;
|
|
*semap = 0;
|
|
return (1);
|
|
default:
|
|
hccr = ISP_READ(isp, HCCR);
|
|
if (hccr & HCCR_PAUSE) {
|
|
ISP_WRITE(isp, HCCR, HCCR_RESET);
|
|
isp_prt(isp, ISP_LOGERR, "RISC paused at interrupt (%x->%x)", hccr, ISP_READ(isp, HCCR));
|
|
} else {
|
|
isp_prt(isp, ISP_LOGERR, "unknown interrerupt 0x%x", r2hisr);
|
|
}
|
|
return (0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef ISP_DISABLE_2400_SUPPORT
|
|
static __inline void
|
|
ispregwr32(struct isp_pcisoftc *pcs, vm_offset_t offset, uint32_t val)
|
|
{
|
|
if (pcs->vaddr) {
|
|
u8 *addr = pcs->vaddr;
|
|
writel(val, addr+offset);
|
|
} else {
|
|
offset += pcs->port;
|
|
outl(val, offset);
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
isp_pci_rd_reg_2400(ispsoftc_t *isp, int regoff)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
uint32_t rv;
|
|
int block = regoff & _BLK_REG_MASK;
|
|
|
|
switch (block) {
|
|
case BIU_BLOCK:
|
|
break;
|
|
case MBOX_BLOCK:
|
|
return (ispregrd(pcs, IspVirt2Off(pcs, regoff)));
|
|
case SXP_BLOCK:
|
|
isp_prt(isp, ISP_LOGWARN, "SXP_BLOCK read at 0x%x", regoff);
|
|
return (0xffffffff);
|
|
case RISC_BLOCK:
|
|
isp_prt(isp, ISP_LOGWARN, "RISC_BLOCK read at 0x%x", regoff);
|
|
return (0xffffffff);
|
|
case DMA_BLOCK:
|
|
isp_prt(isp, ISP_LOGWARN, "DMA_BLOCK read at 0x%x", regoff);
|
|
return (0xffffffff);
|
|
default:
|
|
isp_prt(isp, ISP_LOGWARN, "unknown block read at 0x%x", regoff);
|
|
return (0xffffffff);
|
|
}
|
|
|
|
|
|
switch (regoff) {
|
|
case BIU2400_FLASH_ADDR:
|
|
case BIU2400_FLASH_DATA:
|
|
case BIU2400_ICR:
|
|
case BIU2400_ISR:
|
|
case BIU2400_CSR:
|
|
case BIU2400_REQINP:
|
|
case BIU2400_REQOUTP:
|
|
case BIU2400_RSPINP:
|
|
case BIU2400_RSPOUTP:
|
|
case BIU2400_PRI_RQINP:
|
|
case BIU2400_PRI_RSPINP:
|
|
case BIU2400_ATIO_RSPINP:
|
|
case BIU2400_ATIO_REQINP:
|
|
case BIU2400_HCCR:
|
|
case BIU2400_GPIOD:
|
|
case BIU2400_GPIOE:
|
|
case BIU2400_HSEMA:
|
|
rv = ispregrd32(pcs, IspVirt2Off(pcs, regoff));
|
|
break;
|
|
case BIU2400_R2HSTSLO:
|
|
rv = ispregrd32(pcs, IspVirt2Off(pcs, regoff));
|
|
break;
|
|
case BIU2400_R2HSTSHI:
|
|
rv = ispregrd32(pcs, IspVirt2Off(pcs, regoff)) >> 16;
|
|
break;
|
|
default:
|
|
isp_prt(isp, ISP_LOGERR, "isp_pci_rd_reg_2400: unknown offset %x", regoff);
|
|
rv = 0xffffffff;
|
|
break;
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
static void
|
|
isp_pci_wr_reg_2400(ispsoftc_t *isp, int regoff, uint32_t val)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
int block = regoff & _BLK_REG_MASK;
|
|
volatile int junk;
|
|
|
|
switch (block) {
|
|
case BIU_BLOCK:
|
|
break;
|
|
case MBOX_BLOCK:
|
|
ispregwr(pcs, IspVirt2Off(pcs, regoff), val);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, regoff));
|
|
return;
|
|
case SXP_BLOCK:
|
|
isp_prt(isp, ISP_LOGWARN, "SXP_BLOCK write at 0x%x", regoff);
|
|
return;
|
|
case RISC_BLOCK:
|
|
isp_prt(isp, ISP_LOGWARN, "RISC_BLOCK write at 0x%x", regoff);
|
|
return;
|
|
case DMA_BLOCK:
|
|
isp_prt(isp, ISP_LOGWARN, "DMA_BLOCK write at 0x%x", regoff);
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (regoff) {
|
|
case BIU2400_FLASH_ADDR:
|
|
case BIU2400_FLASH_DATA:
|
|
case BIU2400_ICR:
|
|
case BIU2400_ISR:
|
|
case BIU2400_CSR:
|
|
case BIU2400_REQINP:
|
|
case BIU2400_REQOUTP:
|
|
case BIU2400_RSPINP:
|
|
case BIU2400_RSPOUTP:
|
|
case BIU2400_PRI_RQINP:
|
|
case BIU2400_PRI_RSPINP:
|
|
case BIU2400_ATIO_RSPINP:
|
|
case BIU2400_ATIO_REQINP:
|
|
case BIU2400_HCCR:
|
|
case BIU2400_GPIOD:
|
|
case BIU2400_GPIOE:
|
|
case BIU2400_HSEMA:
|
|
ispregwr32(pcs, IspVirt2Off(pcs, regoff), val);
|
|
junk = ispregrd32(pcs, IspVirt2Off(pcs, regoff));
|
|
break;
|
|
default:
|
|
isp_prt(isp, ISP_LOGERR, "isp_pci_wr_reg_2400: bad offset 0x%x", regoff);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
isp_pci_rd_isr_2400(ispsoftc_t *isp, uint32_t *isrp, uint16_t *semap, uint16_t *mbox0p)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
uint32_t r2hisr;
|
|
volatile int junk;
|
|
|
|
r2hisr = ispregrd32(pcs, IspVirt2Off(pcs, BIU2400_R2HSTSLO));
|
|
isp_prt(isp, ISP_LOGDEBUG3, "RISC2HOST ISR 0x%x", r2hisr);
|
|
if ((r2hisr & BIU2400_R2HST_INTR) == 0) {
|
|
*isrp = 0;
|
|
return (0);
|
|
}
|
|
switch (r2hisr & BIU2400_R2HST_ISTAT_MASK) {
|
|
case ISP2400R2HST_ROM_MBX_OK:
|
|
case ISP2400R2HST_ROM_MBX_FAIL:
|
|
case ISP2400R2HST_MBX_OK:
|
|
case ISP2400R2HST_MBX_FAIL:
|
|
case ISP2400R2HST_ASYNC_EVENT:
|
|
*isrp = r2hisr & 0xffff;
|
|
*mbox0p = (r2hisr >> 16);
|
|
*semap = 1;
|
|
return (1);
|
|
case ISP2400R2HST_RSPQ_UPDATE:
|
|
case ISP2400R2HST_ATIO_RSPQ_UPDATE:
|
|
case ISP2400R2HST_ATIO_RQST_UPDATE:
|
|
*isrp = r2hisr & 0xffff;
|
|
*mbox0p = 0;
|
|
*semap = 0;
|
|
return (1);
|
|
default:
|
|
ispregwr32(pcs, IspVirt2Off(pcs, BIU2400_HCCR), HCCR_2400_CMD_CLEAR_RISC_INT);
|
|
junk = ispregrd32(pcs, IspVirt2Off(pcs, BIU2400_HCCR));
|
|
isp_prt(isp, ISP_LOGERR, "unknown interrupt 0x%x", r2hisr);
|
|
return (0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static uint32_t
|
|
isp_pci_rd_reg(ispsoftc_t *isp, int regoff)
|
|
{
|
|
uint32_t rv, oldconf = 0;
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
volatile int junk;
|
|
|
|
if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) {
|
|
/*
|
|
* We will assume that someone has paused the RISC processor.
|
|
*/
|
|
oldconf = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), oldconf | BIU_PCI_CONF1_SXP);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
}
|
|
rv = ispregrd(pcs, IspVirt2Off(pcs, regoff));
|
|
if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) {
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), oldconf);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
static void
|
|
isp_pci_wr_reg(ispsoftc_t *isp, int regoff, uint32_t val)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
uint32_t oldconf = 0;
|
|
volatile int junk;
|
|
|
|
if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) {
|
|
/*
|
|
* We will assume that someone has paused the RISC processor.
|
|
*/
|
|
oldconf = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), oldconf | BIU_PCI_CONF1_SXP);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
}
|
|
ispregwr(pcs, IspVirt2Off(pcs, regoff), val);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, regoff));
|
|
if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) {
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), oldconf);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
}
|
|
}
|
|
|
|
#if !(defined(ISP_DISABLE_1080_SUPPORT) && defined(ISP_DISABLE_12160_SUPPORT))
|
|
static uint32_t
|
|
isp_pci_rd_reg_1080(ispsoftc_t *isp, int regoff)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
uint32_t rv, oldconf = 0;
|
|
volatile int junk;
|
|
|
|
if ((regoff & _BLK_REG_MASK) == SXP_BLOCK || (regoff & _BLK_REG_MASK) == (SXP_BLOCK|SXP_BANK1_SELECT)) {
|
|
uint32_t tmpconf;
|
|
/*
|
|
* We will assume that someone has paused the RISC processor.
|
|
*/
|
|
oldconf = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
tmpconf = oldconf & ~BIU_PCI1080_CONF1_DMA;
|
|
if (IS_1280(isp)) {
|
|
if (regoff & SXP_BANK1_SELECT) {
|
|
tmpconf |= BIU_PCI1080_CONF1_SXP0;
|
|
} else {
|
|
tmpconf |= BIU_PCI1080_CONF1_SXP1;
|
|
}
|
|
} else {
|
|
tmpconf |= BIU_PCI1080_CONF1_SXP0;
|
|
}
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), tmpconf);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
} else if ((regoff & _BLK_REG_MASK) == DMA_BLOCK) {
|
|
oldconf = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), oldconf | BIU_PCI1080_CONF1_DMA);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
}
|
|
rv = ispregrd(pcs, IspVirt2Off(pcs, regoff));
|
|
if (oldconf) {
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), oldconf);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
static void
|
|
isp_pci_wr_reg_1080(ispsoftc_t *isp, int regoff, uint32_t val)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
uint32_t oldconf = 0;
|
|
volatile int junk;
|
|
|
|
if ((regoff & _BLK_REG_MASK) == SXP_BLOCK || (regoff & _BLK_REG_MASK) == (SXP_BLOCK|SXP_BANK1_SELECT)) {
|
|
uint32_t tmpconf;
|
|
/*
|
|
* We will assume that someone has paused the RISC processor.
|
|
*/
|
|
oldconf = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
tmpconf = oldconf & ~BIU_PCI1080_CONF1_DMA;
|
|
if (IS_1280(isp)) {
|
|
if (regoff & SXP_BANK1_SELECT) {
|
|
tmpconf |= BIU_PCI1080_CONF1_SXP0;
|
|
} else {
|
|
tmpconf |= BIU_PCI1080_CONF1_SXP1;
|
|
}
|
|
} else {
|
|
tmpconf |= BIU_PCI1080_CONF1_SXP0;
|
|
}
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), tmpconf);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
} else if ((regoff & _BLK_REG_MASK) == DMA_BLOCK) {
|
|
oldconf = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), oldconf | BIU_PCI1080_CONF1_DMA);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
}
|
|
ispregwr(pcs, IspVirt2Off(pcs, regoff), val);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, regoff));
|
|
if (oldconf) {
|
|
ispregwr(pcs, IspVirt2Off(pcs, BIU_CONF1), oldconf);
|
|
junk = ispregrd(pcs, IspVirt2Off(pcs, BIU_CONF1));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* We enter with the IRQs disabled.
|
|
*
|
|
* This makes 2.6 unhappy when we try to allocate memory.
|
|
*
|
|
* The only time we need to allocate memory is when we're
|
|
* setting things up, and in that case the chip isn't really
|
|
* quite active yet.
|
|
*/
|
|
static int
|
|
isp_pci_mbxdma(ispsoftc_t *isp)
|
|
{
|
|
fcparam *fcp;
|
|
int i;
|
|
struct isp_pcisoftc *pcs;
|
|
|
|
if (isp->isp_xflist) {
|
|
return (0);
|
|
}
|
|
isp->isp_osinfo.mcorig = isp->isp_maxcmds;
|
|
|
|
pcs = (struct isp_pcisoftc *) isp;
|
|
|
|
ISP_DROP_LK_SOFTC(isp);
|
|
if (isp->isp_xflist == NULL) {
|
|
size_t amt = isp->isp_osinfo.mcorig * sizeof (XS_T **);
|
|
isp->isp_xflist = isp_kzalloc(amt, GFP_KERNEL);
|
|
if (isp->isp_xflist == NULL) {
|
|
isp_prt(isp, ISP_LOGERR, "unable to allocate xflist array");
|
|
goto bad;
|
|
}
|
|
}
|
|
#ifdef ISP_TARGET_MODE
|
|
if (isp->isp_tgtlist == NULL) {
|
|
size_t amt = isp->isp_osinfo.mcorig * sizeof (void **);
|
|
isp->isp_tgtlist = isp_kzalloc(amt, GFP_KERNEL);
|
|
if (isp->isp_tgtlist == NULL) {
|
|
isp_prt(isp, ISP_LOGERR, "unable to allocate tgtlist array");
|
|
goto bad;
|
|
}
|
|
}
|
|
if (IS_24XX(isp) && isp->isp_atioq == NULL) {
|
|
dma_addr_t busaddr;
|
|
isp->isp_atioq = pci_alloc_consistent(pcs->pci_dev, RESULT_QUEUE_LEN(isp) * QENTRY_LEN, &busaddr);
|
|
if (isp->isp_atioq == NULL) {
|
|
isp_prt(isp, ISP_LOGERR, "unable to allocate atio queue");
|
|
goto bad;
|
|
}
|
|
isp->isp_atioq_dma = busaddr;
|
|
if (isp->isp_atioq_dma & 0x3f) {
|
|
isp_prt(isp, ISP_LOGERR, "ATIO Queue not on 64 byte boundary");
|
|
goto bad;
|
|
}
|
|
MEMZERO(isp->isp_atioq, ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp)));
|
|
}
|
|
#endif
|
|
if (isp->isp_rquest == NULL) {
|
|
dma_addr_t busaddr;
|
|
isp->isp_rquest = pci_alloc_consistent(pcs->pci_dev, RQUEST_QUEUE_LEN(isp) * QENTRY_LEN, &busaddr);
|
|
if (isp->isp_rquest == NULL) {
|
|
isp_prt(isp, ISP_LOGERR, "unable to allocate request queue");
|
|
goto bad;
|
|
}
|
|
isp->isp_rquest_dma = busaddr;
|
|
if (isp->isp_rquest_dma & 0x3f) {
|
|
isp_prt(isp, ISP_LOGERR, "Request Queue not on 64 byte boundary");
|
|
goto bad;
|
|
}
|
|
MEMZERO(isp->isp_rquest, ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)));
|
|
}
|
|
|
|
if (isp->isp_result == NULL) {
|
|
dma_addr_t busaddr;
|
|
isp->isp_result = pci_alloc_consistent(pcs->pci_dev, RESULT_QUEUE_LEN(isp) * QENTRY_LEN, &busaddr);
|
|
if (isp->isp_result == NULL) {
|
|
isp_prt(isp, ISP_LOGERR, "unable to allocate result queue");
|
|
goto bad;
|
|
}
|
|
isp->isp_result_dma = busaddr;
|
|
if (isp->isp_rquest_dma & 0x3f) {
|
|
isp_prt(isp, ISP_LOGERR, "Result Queue not on 64 byte boundary");
|
|
goto bad;
|
|
}
|
|
MEMZERO(isp->isp_result, ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp)));
|
|
}
|
|
|
|
if (IS_FC(isp)) {
|
|
for (i = 0; i < isp->isp_nchan; i++) {
|
|
fcp = FCPARAM(isp, i);
|
|
if (fcp->isp_scratch == NULL) {
|
|
dma_addr_t busaddr;
|
|
fcp->isp_scratch = pci_alloc_consistent(pcs->pci_dev, ISP_FC_SCRLEN, &busaddr);
|
|
if (fcp->isp_scratch == NULL) {
|
|
isp_prt(isp, ISP_LOGERR, "unable to allocate scratch space");
|
|
goto bad;
|
|
}
|
|
fcp->isp_scdma = busaddr;
|
|
MEMZERO(fcp->isp_scratch, ISP_FC_SCRLEN);
|
|
if (fcp->isp_scdma & 0x7) {
|
|
isp_prt(isp, ISP_LOGERR, "scratch space not 8 byte aligned");
|
|
goto bad;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ISP_IGET_LK_SOFTC(isp);
|
|
return (0);
|
|
|
|
bad:
|
|
if (isp->isp_xflist) {
|
|
isp_kfree(isp->isp_xflist, isp->isp_osinfo.mcorig * sizeof (XS_T **));
|
|
isp->isp_xflist = NULL;
|
|
}
|
|
#ifdef ISP_TARGET_MODE
|
|
if (isp->isp_tgtlist) {
|
|
isp_kfree(isp->isp_tgtlist, isp->isp_osinfo.mcorig * sizeof (void **));
|
|
isp->isp_tgtlist = NULL;
|
|
}
|
|
if (isp->isp_atioq) {
|
|
pci_free_consistent(pcs->pci_dev, RESULT_QUEUE_LEN(isp) * QENTRY_LEN, isp->isp_atioq, isp->isp_atioq_dma);
|
|
isp->isp_atioq = NULL;
|
|
isp->isp_atioq_dma = 0;
|
|
}
|
|
#endif
|
|
if (isp->isp_rquest) {
|
|
pci_free_consistent(pcs->pci_dev, RQUEST_QUEUE_LEN(isp) * QENTRY_LEN, isp->isp_rquest, isp->isp_rquest_dma);
|
|
isp->isp_rquest = NULL;
|
|
isp->isp_rquest_dma = 0;
|
|
}
|
|
if (isp->isp_result) {
|
|
pci_free_consistent(pcs->pci_dev, RESULT_QUEUE_LEN(isp) * QENTRY_LEN, isp->isp_result, isp->isp_result_dma);
|
|
isp->isp_result = NULL;
|
|
isp->isp_result_dma = 0;
|
|
}
|
|
if (IS_FC(isp)) {
|
|
for (i = 0; i < isp->isp_nchan; i++) {
|
|
fcp = FCPARAM(isp, i);
|
|
if (fcp->isp_scratch) {
|
|
pci_free_consistent(pcs->pci_dev, ISP_FC_SCRLEN, fcp->isp_scratch, fcp->isp_scdma);
|
|
fcp->isp_scratch = NULL;
|
|
fcp->isp_scdma = 0;
|
|
}
|
|
}
|
|
}
|
|
ISP_IGET_LK_SOFTC(isp);
|
|
return (1);
|
|
}
|
|
|
|
#ifdef ISP_TARGET_MODE
|
|
static int tdma_mk(ispsoftc_t *, tmd_cmd_t *, ct_entry_t *, uint32_t *, uint32_t);
|
|
static int tdma_mkfc(ispsoftc_t *, tmd_cmd_t *, ct2_entry_t *, uint32_t *, uint32_t);
|
|
|
|
#define ALLOW_SYNTHETIC_CTIO 1
|
|
#ifndef ALLOW_SYNTHETIC_CTIO
|
|
#define cto2 0
|
|
#endif
|
|
|
|
#define STATUS_WITH_DATA 1
|
|
|
|
/*
|
|
* We need to handle DMA for target mode differently from initiator mode.
|
|
*
|
|
* DMA mapping and construction and submission of CTIO Request Entries
|
|
* and rendevous for completion are very tightly coupled because we start
|
|
* out by knowing (per platform) how much data we have to move, but we
|
|
* don't know, up front, how many DMA mapping segments will have to be used
|
|
* cover that data, so we don't know how many CTIO and Continuation Request
|
|
* Entries we will end up using. Further, for performance reasons we may want
|
|
* to (on the last CTIO for Fibre Channel), send status too (if all went well).
|
|
*
|
|
* The standard vector still goes through isp_pci_dmasetup, but the callback
|
|
* for the DMA mapping routines comes here instead with a pointer to a
|
|
* partially filled in already allocated request queue entry.
|
|
*/
|
|
|
|
static int
|
|
tdma_mk(ispsoftc_t *isp, tmd_cmd_t *tmd, ct_entry_t *cto, uint32_t *nxtip, uint32_t optr)
|
|
{
|
|
static const char ctx[] = "CTIO[%x] lun %d for iid%d flgs 0x%x sts 0x%x ssts 0x%x res %u %s";
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
struct scatterlist *sg;
|
|
ct_entry_t *qe;
|
|
uint8_t scsi_status;
|
|
uint32_t curi, nxti, handle;
|
|
uint32_t sflags;
|
|
int32_t resid;
|
|
int nth_ctio, nctios, send_status, nseg, new_seg_cnt;
|
|
|
|
curi = isp->isp_reqidx;
|
|
qe = (ct_entry_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx);
|
|
|
|
cto->ct_xfrlen = 0;
|
|
cto->ct_seg_count = 0;
|
|
cto->ct_header.rqs_entry_count = 1;
|
|
MEMZERO(cto->ct_dataseg, sizeof (cto->ct_dataseg));
|
|
|
|
if (tmd->cd_xfrlen == 0) {
|
|
ISP_TDQE(isp, "tdma_mk[no data]", curi, cto);
|
|
isp_prt(isp, ISP_LOGTDEBUG1, ctx, cto->ct_fwhandle, L0LUN_TO_FLATLUN(tmd->cd_lun), (int) cto->ct_iid, cto->ct_flags, cto->ct_status,
|
|
cto->ct_scsi_status, cto->ct_resid, "<END>");
|
|
isp_put_ctio(isp, cto, qe);
|
|
return (CMD_QUEUED);
|
|
}
|
|
|
|
if (tmd->cd_xfrlen <= 1024) {
|
|
nseg = 0;
|
|
} else if (tmd->cd_xfrlen <= 4096) {
|
|
nseg = 1;
|
|
} else if (tmd->cd_xfrlen <= 32768) {
|
|
nseg = 2;
|
|
} else if (tmd->cd_xfrlen <= 65536) {
|
|
nseg = 3;
|
|
} else if (tmd->cd_xfrlen <= 131372) {
|
|
nseg = 4;
|
|
} else if (tmd->cd_xfrlen <= 262144) {
|
|
nseg = 5;
|
|
} else if (tmd->cd_xfrlen <= 524288) {
|
|
nseg = 6;
|
|
} else {
|
|
nseg = 7;
|
|
}
|
|
isp->isp_osinfo.bins[nseg]++;
|
|
|
|
sg = tmd->cd_data;
|
|
nseg = 0;
|
|
resid = (int32_t) tmd->cd_xfrlen;
|
|
while (resid > 0) {
|
|
if (sg->length == 0) {
|
|
isp_prt(isp, ISP_LOGWARN, "%s: zero length segment #%d for tag %llx\n", __FUNCTION__, nseg, tmd->cd_tagval);
|
|
cto->ct_resid = -EINVAL;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
nseg++;
|
|
resid -= sg->length;
|
|
sg++;
|
|
}
|
|
sg = tmd->cd_data;
|
|
|
|
new_seg_cnt = pci_map_sg(pcs->pci_dev, sg, nseg, (cto->ct_flags & CT_DATA_IN)? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
|
|
if (new_seg_cnt == 0) {
|
|
isp_prt(isp, ISP_LOGWARN, "%s: unable to dma map request", __FUNCTION__);
|
|
cto->ct_resid = -ENOMEM;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
tmd->cd_nseg = new_seg_cnt;
|
|
|
|
nctios = nseg / ISP_RQDSEG;
|
|
if (nseg % ISP_RQDSEG) {
|
|
nctios++;
|
|
}
|
|
|
|
/*
|
|
* Save handle, and potentially any SCSI status, which
|
|
* we'll reinsert on the last CTIO we're going to send.
|
|
*/
|
|
handle = cto->ct_syshandle;
|
|
cto->ct_syshandle = 0;
|
|
cto->ct_header.rqs_seqno = 0;
|
|
send_status = (cto->ct_flags & CT_SENDSTATUS) != 0;
|
|
|
|
if (send_status) {
|
|
sflags = cto->ct_flags & (CT_SENDSTATUS | CT_CCINCR);
|
|
cto->ct_flags &= ~(CT_SENDSTATUS|CT_CCINCR);
|
|
/*
|
|
* Preserve residual.
|
|
*/
|
|
resid = cto->ct_resid;
|
|
|
|
/*
|
|
* Save actual SCSI status.
|
|
*/
|
|
scsi_status = cto->ct_scsi_status;
|
|
|
|
#ifndef STATUS_WITH_DATA
|
|
sflags |= CT_NO_DATA;
|
|
/*
|
|
* We can't do a status at the same time as a data CTIO, so
|
|
* we need to synthesize an extra CTIO at this level.
|
|
*/
|
|
nctios++;
|
|
#endif
|
|
} else {
|
|
sflags = scsi_status = resid = 0;
|
|
}
|
|
|
|
cto->ct_resid = 0;
|
|
cto->ct_scsi_status = 0;
|
|
|
|
nxti = *nxtip;
|
|
|
|
for (nth_ctio = 0; nth_ctio < nctios; nth_ctio++) {
|
|
int seglim;
|
|
|
|
seglim = nseg;
|
|
if (seglim) {
|
|
int seg;
|
|
|
|
if (seglim > ISP_RQDSEG)
|
|
seglim = ISP_RQDSEG;
|
|
|
|
for (seg = 0; seg < seglim; seg++, nseg--) {
|
|
XS_DMA_ADDR_T addr = sg_dma_address(sg);
|
|
|
|
/*
|
|
* We could actually do the work to support this,
|
|
* but it's extra code to write and test with things
|
|
* pretty unlikely to ever be used.
|
|
*/
|
|
if (ISP_A64 && IS_HIGH_ISP_ADDR(addr)) {
|
|
isp_prt(isp, ISP_LOGERR, "%s: 64 bit tgt mode not supported", __FUNCTION__);
|
|
cto->ct_resid = -EFAULT;
|
|
pci_unmap_sg(pcs->pci_dev, tmd->cd_data, nseg, (cto->ct_flags & CT_DATA_IN)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE);
|
|
return (CMD_COMPLETE);
|
|
}
|
|
/*
|
|
* Unlike normal initiator commands, we don't do any swizzling here.
|
|
*/
|
|
cto->ct_dataseg[seg].ds_base = LOWD(addr);
|
|
cto->ct_dataseg[seg].ds_count = (uint32_t) sg_dma_len(sg);
|
|
cto->ct_xfrlen += sg_dma_len(sg);
|
|
sg++;
|
|
}
|
|
cto->ct_seg_count = seg;
|
|
} else {
|
|
/*
|
|
* This case should only happen when we're
|
|
* sending an extra CTIO with final status.
|
|
*/
|
|
if (send_status == 0) {
|
|
isp_prt(isp, ISP_LOGERR, "%s: ran out of segments, no status to send", __FUNCTION__);
|
|
return (CMD_EAGAIN);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* At this point, the fields ct_lun, ct_iid, ct_tagval, ct_tagtype, and
|
|
* ct_timeout have been carried over unchanged from what our caller had
|
|
* set.
|
|
*
|
|
* The dataseg fields and the seg_count fields we just got through
|
|
* setting. The data direction we've preserved all along and only
|
|
* clear it if we're now sending status.
|
|
*/
|
|
if (nth_ctio == nctios - 1) {
|
|
/*
|
|
* We're the last in a sequence of CTIOs, so mark this
|
|
* CTIO and save the handle to the command such that when
|
|
* this CTIO completes we can free dma resources and
|
|
* do whatever else we need to do to finish the rest
|
|
* of the command.
|
|
*/
|
|
cto->ct_syshandle = handle;
|
|
cto->ct_header.rqs_seqno = 1;
|
|
|
|
if (send_status) {
|
|
cto->ct_scsi_status = scsi_status;
|
|
cto->ct_flags |= sflags;
|
|
cto->ct_resid = resid;
|
|
}
|
|
if (send_status) {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, ctx, cto->ct_fwhandle, L0LUN_TO_FLATLUN(tmd->cd_lun), (int) cto->ct_iid, cto->ct_flags,
|
|
cto->ct_status, cto->ct_scsi_status, cto->ct_resid, "<END>");
|
|
} else {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, ctx, cto->ct_fwhandle, L0LUN_TO_FLATLUN(tmd->cd_lun), (int) cto->ct_iid, cto->ct_flags,
|
|
cto->ct_status, cto->ct_scsi_status, cto->ct_resid, "<MID>");
|
|
}
|
|
isp_put_ctio(isp, cto, qe);
|
|
ISP_TDQE(isp, "last tdma_mk", curi, cto);
|
|
if (nctios > 1) {
|
|
MEMORYBARRIER(isp, SYNC_REQUEST, curi, QENTRY_LEN);
|
|
}
|
|
} else {
|
|
ct_entry_t *oqe = qe;
|
|
|
|
/*
|
|
* Make sure handle fields are clean
|
|
*/
|
|
cto->ct_syshandle = 0;
|
|
cto->ct_header.rqs_seqno = 0;
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "CTIO[%x] lun%d for ID%d ct_flags 0x%x", cto->ct_fwhandle, L0LUN_TO_FLATLUN(tmd->cd_lun), (int) cto->ct_iid, cto->ct_flags);
|
|
|
|
/*
|
|
* Get a new CTIO
|
|
*/
|
|
qe = (ct_entry_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti);
|
|
nxti = ISP_NXT_QENTRY(nxti, RQUEST_QUEUE_LEN(isp));
|
|
if (nxti == optr) {
|
|
isp_prt(isp, ISP_LOGERR, "%s: request queue overflow", __FUNCTION__);
|
|
return (CMD_EAGAIN);
|
|
}
|
|
|
|
/*
|
|
* Now that we're done with the old CTIO,
|
|
* flush it out to the request queue.
|
|
*/
|
|
ISP_TDQE(isp, "tdma_mk", curi, cto);
|
|
isp_put_ctio(isp, cto, oqe);
|
|
if (nth_ctio != 0) {
|
|
MEMORYBARRIER(isp, SYNC_REQUEST, curi, QENTRY_LEN);
|
|
}
|
|
curi = ISP_NXT_QENTRY(curi, RQUEST_QUEUE_LEN(isp));
|
|
|
|
/*
|
|
* Reset some fields in the CTIO so we can reuse
|
|
* for the next one we'll flush to the request
|
|
* queue.
|
|
*/
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO;
|
|
cto->ct_header.rqs_entry_count = 1;
|
|
cto->ct_header.rqs_flags = 0;
|
|
cto->ct_status = 0;
|
|
cto->ct_scsi_status = 0;
|
|
cto->ct_xfrlen = 0;
|
|
cto->ct_resid = 0;
|
|
cto->ct_seg_count = 0;
|
|
MEMZERO(cto->ct_dataseg, sizeof (cto->ct_dataseg));
|
|
}
|
|
}
|
|
*nxtip = nxti;
|
|
isp_prt(isp, ISP_LOGTDEBUG2, "[%llx]: map %d segments at %p for handle 0x%x", tmd->cd_tagval, new_seg_cnt, tmd->cd_data, cto->ct_syshandle);
|
|
return (CMD_QUEUED);
|
|
}
|
|
|
|
/*
|
|
* We're passed a pointer to a prototype ct2_entry_t.
|
|
*
|
|
* If it doesn't contain any data movement, it has to be for sending status,
|
|
* possibly with Sense Data as well, so we send a single CTIO2. This should
|
|
* be a Mode 1 CTIO2, and it's up to the caller to set up the Sense Data
|
|
* and flags appropriately.
|
|
*
|
|
* If it does contain data movement, it may *also* be for sending status
|
|
* (possibly with Sense Data also). It's possible to describe to the firmware
|
|
* what we want in one CTIO2. However, under some conditions it is not,
|
|
* so we must also send a *second* CTIO2 after the first one.
|
|
*
|
|
* If the data to be sent is in segments that exceeds that which we can
|
|
* fit into a CTIO2 (likely, as there's only room for 3 segments), we
|
|
* utilize normal continuation entries, which get pushed after the
|
|
* first CTIO2, and possibly are followed by a final CTIO2.
|
|
*
|
|
* In any case, it's up to the caller to send us a Mode 0 CTIO2 describing
|
|
* the data to be moved (if any) and the appropriate flags indicating
|
|
* status. We'll clear and set as appropriate. We'll also check to see
|
|
* whether Sense Data is attempting to be sent and retrieve it as appropriate.
|
|
*
|
|
* In all cases the caller should not assume that the prototype CTIO2
|
|
* has been left unchanged.
|
|
*/
|
|
#ifndef ISP_DISABLE_2400_SUPPORT
|
|
static int tdma_mk_2400(ispsoftc_t *, tmd_cmd_t *, ct7_entry_t *, uint32_t *, uint32_t);
|
|
static int
|
|
tdma_mk_2400(ispsoftc_t *isp, tmd_cmd_t *tmd, ct7_entry_t *cto, uint32_t *nxtip, uint32_t optr)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
static const char ctx[] = "CTIO7[%x] lun %d for nphdl %x flgs 0x%x ssts 0x%x res %d %s";
|
|
XS_DMA_ADDR_T addr, last_synthetic_addr;
|
|
struct scatterlist *sg;
|
|
void *qe;
|
|
uint32_t swd, curi, nxti;
|
|
uint32_t bc, last_synthetic_count;
|
|
long xfcnt; /* must be signed */
|
|
int nseg, seg, ovseg, seglim, new_seg_cnt;
|
|
#ifdef ALLOW_SYNTHETIC_CTIO
|
|
ct7_entry_t *cto2 = NULL, ct2;
|
|
#endif
|
|
|
|
nxti = *nxtip;
|
|
curi = isp->isp_reqidx;
|
|
qe = ISP_QUEUE_ENTRY(isp->isp_rquest, curi);
|
|
|
|
/*
|
|
* Handle commands that transfer no data right away.
|
|
*/
|
|
if (tmd->cd_xfrlen == 0) {
|
|
cto->ct_header.rqs_entry_count = 1;
|
|
cto->ct_header.rqs_seqno = 1;
|
|
|
|
/* ct_syshandle contains the synchronization handle set by caller */
|
|
cto->ct_flags |= CT7_NO_DATA;
|
|
if (cto->ct_resid > 0) {
|
|
cto->ct_scsi_status |= CT2_DATA_UNDER; /* XXX : should be in isp_stds.h */
|
|
}
|
|
isp_prt(isp, ISP_LOGTDEBUG1, ctx, cto->ct_rxid, L0LUN_TO_FLATLUN(tmd->cd_lun), cto->ct_nphdl, cto->ct_flags, cto->ct_scsi_status, cto->ct_resid, "<END>");
|
|
isp_put_ctio7(isp, cto, qe);
|
|
ISP_TDQE(isp, "tdma_mk_2400[no data]", curi, qe);
|
|
return (CMD_QUEUED);
|
|
}
|
|
|
|
if ((cto->ct_flags & CT7_FLAG_MMASK) != CT7_FLAG_MODE0) {
|
|
isp_prt(isp, ISP_LOGERR, "%s: a data CTIO7 without MODE0 set (0x%x)", __FUNCTION__, cto->ct_flags);
|
|
cto->ct_resid = -EINVAL;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
|
|
if (tmd->cd_xfrlen <= 1024) {
|
|
nseg = 0;
|
|
} else if (tmd->cd_xfrlen <= 4096) {
|
|
nseg = 1;
|
|
} else if (tmd->cd_xfrlen <= 32768) {
|
|
nseg = 2;
|
|
} else if (tmd->cd_xfrlen <= 65536) {
|
|
nseg = 3;
|
|
} else if (tmd->cd_xfrlen <= 131372) {
|
|
nseg = 4;
|
|
} else if (tmd->cd_xfrlen <= 262144) {
|
|
nseg = 5;
|
|
} else if (tmd->cd_xfrlen <= 524288) {
|
|
nseg = 6;
|
|
} else {
|
|
nseg = 7;
|
|
}
|
|
isp->isp_osinfo.bins[nseg]++;
|
|
|
|
/*
|
|
* First, count and map all S/G segments
|
|
*
|
|
* The byte counter has to be signed because
|
|
* we can have descriptors that are, in fact,
|
|
* longer than our data transfer count.
|
|
*/
|
|
sg = tmd->cd_data;
|
|
nseg = 0;
|
|
xfcnt = tmd->cd_xfrlen;
|
|
while (xfcnt > 0) {
|
|
if (sg->length == 0) {
|
|
isp_prt(isp, ISP_LOGWARN, "%s: zero length segment #%d for tag %llx\n", __FUNCTION__, nseg, tmd->cd_tagval);
|
|
cto->ct_resid = -EINVAL;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
nseg++;
|
|
xfcnt -= sg->length;
|
|
sg++;
|
|
}
|
|
sg = tmd->cd_data;
|
|
new_seg_cnt = pci_map_sg(pcs->pci_dev, sg, nseg, (cto->ct_flags & CT2_DATA_IN)? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
if (new_seg_cnt == 0) {
|
|
isp_prt(isp, ISP_LOGWARN, "%s: unable to dma map request", __FUNCTION__);
|
|
cto->ct_resid = -ENOMEM;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
tmd->cd_nseg = new_seg_cnt;
|
|
|
|
/*
|
|
* Second, figure out whether we'll need to send a separate status CTIO.
|
|
*/
|
|
swd = cto->ct_scsi_status;
|
|
|
|
if ((cto->ct_flags & CT7_SENDSTATUS) && ((swd & 0xf) || cto->ct_resid)) {
|
|
#ifdef ALLOW_SYNTHETIC_CTIO
|
|
cto2 = &ct2;
|
|
/*
|
|
* Copy over CTIO2
|
|
*/
|
|
MEMCPY(cto2, cto, sizeof (ct7_entry_t));
|
|
|
|
/*
|
|
* Clear fields from first CTIO2 that now need to be cleared
|
|
*/
|
|
cto->ct_flags &= ~CT7_SENDSTATUS;
|
|
cto->ct_resid = 0;
|
|
cto->ct_syshandle = 0;
|
|
cto->ct_scsi_status = 0;
|
|
|
|
/*
|
|
* Reset fields in the second CTIO2 as appropriate.
|
|
*/
|
|
cto2->ct_flags &= ~(CT7_FLAG_MMASK|CT7_DATAMASK);
|
|
cto2->ct_flags |= CT7_NO_DATA|CT7_NO_DATA|CT7_FLAG_MODE1;
|
|
cto2->ct_seg_count = 0;
|
|
MEMZERO(&cto2->rsp, sizeof (cto2->rsp));
|
|
cto2->ct_scsi_status = swd;
|
|
if ((swd & 0xf) == SCSI_CHECK && (tmd->cd_hflags & CDFH_SNSVALID)) {
|
|
cto2->rsp.m1.ct_resplen = min(TMD_SENSELEN, MAXRESPLEN_24XX);
|
|
MEMCPY(cto2->rsp.m1.ct_resp, tmd->cd_sense, cto2->rsp.m1.ct_resplen);
|
|
}
|
|
#else
|
|
cto->ct_flags &= ~CT7_SENDSTATUS;
|
|
cto->ct_resid = 0;
|
|
cto->ct_scsi_status = 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Third, fill in the data segments in the first CTIO2 itself.
|
|
* This is also a good place to set the relative offset.
|
|
*/
|
|
xfcnt = tmd->cd_xfrlen;
|
|
|
|
/*
|
|
* cd_resid was already decremented by cd_xfrlen in isp_target_start_ctio
|
|
*
|
|
* We're taking the total amount for the command and backing it off for
|
|
* the amounts already known to have transferred. That should get us the
|
|
* relative offset to start at for this transfer.
|
|
*/
|
|
cto->rsp.m0.reloff = tmd->cd_totlen - (tmd->cd_resid + tmd->cd_xfrlen);
|
|
|
|
seglim = 1;
|
|
|
|
last_synthetic_count = 0;
|
|
last_synthetic_addr = 0;
|
|
cto->ct_seg_count = 1;
|
|
|
|
for (seg = 0; seg < cto->ct_seg_count; seg++) {
|
|
bc = min(sg_dma_len(sg), xfcnt);
|
|
addr = sg_dma_address(sg);
|
|
#ifdef ISP_DAC_SUPPORTED
|
|
cto->rsp.m0.ds.ds_base = LOWD(addr);
|
|
cto->rsp.m0.ds.ds_basehi = HIWD(addr);
|
|
if (!SAME_4G(addr, bc)) {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "seg0[%d]%x%08x:%u (TRUNC'd)", seg, (uint32_t) HIWD(addr), (uint32_t)LOWD(addr), bc);
|
|
cto->rsp.m0.ds.ds_count = (unsigned int) (FOURG_SEG(addr + bc) - addr);
|
|
addr += cto->rsp.m0.ds.ds_count;
|
|
bc -= cto->rsp.m0.ds.ds_count;
|
|
last_synthetic_count = bc;
|
|
last_synthetic_addr = addr;
|
|
} else {
|
|
cto->rsp.m0.ds.ds_count = bc;
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: seg0[%d]%lx%08lx:%u", __FUNCTION__, seg,
|
|
(unsigned long)cto->rsp.m0.ds.ds_basehi, (unsigned long)cto->rsp.m0.ds.ds_base, bc);
|
|
}
|
|
#else
|
|
cto->rsp.m0.ds.ds_base = addr;
|
|
cto->rsp.m0.ds.ds_basehi = 0;
|
|
cto->rsp.m0.ds.ds_count = bc;
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: seg0[%d]%lx:%u", __FUNCTION__, seg, (unsigned long) cto->rsp.m0.ds.ds_base, bc);
|
|
#endif
|
|
cto->rsp.m0.ct_xfrlen += bc;
|
|
xfcnt -= bc;
|
|
sg++;
|
|
}
|
|
|
|
|
|
if (seg == nseg && last_synthetic_count == 0) {
|
|
goto mbxsync;
|
|
}
|
|
|
|
/*
|
|
* Now do any continuation segments that are required.
|
|
*/
|
|
do {
|
|
int lim;
|
|
uint32_t curip;
|
|
ispcontreq_t local, *crq = &local, *qep;
|
|
|
|
curip = nxti;
|
|
qep = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, curip);
|
|
nxti = ISP_NXT_QENTRY((curip), RQUEST_QUEUE_LEN(isp));
|
|
if (nxti == optr) {
|
|
pci_unmap_sg(pcs->pci_dev, tmd->cd_data, nseg, (cto->ct_flags & CT2_DATA_IN)? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: out of space for continuations (%d of %d segs done)", __FUNCTION__, cto->ct_seg_count, nseg);
|
|
return (CMD_EAGAIN);
|
|
}
|
|
cto->ct_header.rqs_entry_count++;
|
|
MEMZERO((void *)crq, sizeof (*crq));
|
|
crq->req_header.rqs_entry_count = 1;
|
|
crq->req_header.rqs_entry_type = RQSTYPE_A64_CONT;
|
|
lim = ISP_CDSEG64;
|
|
|
|
for (ovseg = 0; (seg < nseg || last_synthetic_count) && ovseg < lim; seg++, ovseg++, sg++) {
|
|
ispcontreq64_t *xrq;
|
|
if (last_synthetic_count) {
|
|
addr = last_synthetic_addr;
|
|
bc = last_synthetic_count;
|
|
last_synthetic_count = 0;
|
|
sg--;
|
|
seg--;
|
|
} else {
|
|
addr = sg_dma_address(sg);
|
|
bc = min(sg_dma_len(sg), xfcnt);
|
|
}
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: seg%d[%d]%llx:%u", __FUNCTION__, cto->ct_header.rqs_entry_count-1, ovseg, (unsigned long long) addr, bc);
|
|
|
|
cto->ct_seg_count++;
|
|
cto->rsp.m0.ct_xfrlen += bc;
|
|
|
|
xrq = (ispcontreq64_t *) crq;
|
|
xrq->req_dataseg[ovseg].ds_count = bc;
|
|
xrq->req_dataseg[ovseg].ds_base = LOWD(addr);
|
|
xrq->req_dataseg[ovseg].ds_basehi = HIWD(addr);
|
|
/*
|
|
* Make sure we don't cross a 4GB boundary.
|
|
*/
|
|
if (!SAME_4G(addr, bc)) {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "seg%d[%d]%llx:%u (TRUNC'd)", cto->ct_header.rqs_entry_count-1, ovseg, (long long)addr, bc);
|
|
xrq->req_dataseg[ovseg].ds_count = (unsigned int) (FOURG_SEG(addr + bc) - addr);
|
|
addr += xrq->req_dataseg[ovseg].ds_count;
|
|
bc -= xrq->req_dataseg[ovseg].ds_count;
|
|
xfcnt -= xrq->req_dataseg[ovseg].ds_count;
|
|
/*
|
|
* Do we have space to split it here?
|
|
*/
|
|
if (ovseg == lim - 1) {
|
|
last_synthetic_count = bc;
|
|
last_synthetic_addr = addr;
|
|
cto->ct_seg_count++;
|
|
} else {
|
|
ovseg++;
|
|
xrq->req_dataseg[ovseg].ds_count = bc;
|
|
xrq->req_dataseg[ovseg].ds_base = LOWD(addr);
|
|
xrq->req_dataseg[ovseg].ds_basehi = HIWD(addr);
|
|
}
|
|
}
|
|
}
|
|
ISP_TDQE(isp, "tdma_mk_2400 cont", curip, crq);
|
|
MEMORYBARRIER(isp, SYNC_REQUEST, curip, QENTRY_LEN);
|
|
if (crq->req_header.rqs_entry_type == RQSTYPE_A64_CONT) {
|
|
isp_put_cont64_req(isp, (ispcontreq64_t *)crq, (ispcontreq64_t *)qep);
|
|
} else {
|
|
isp_put_cont_req(isp, crq, qep);
|
|
}
|
|
} while (seg < nseg || last_synthetic_count);
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG2, "[%llx]: map %d segments at %p for handle 0x%x", tmd->cd_tagval, new_seg_cnt, tmd->cd_data, cto->ct_syshandle);
|
|
|
|
mbxsync:
|
|
|
|
#ifdef ALLOW_SYNTHETIC_CTIO
|
|
/*
|
|
* If we have a final CTIO2, allocate and push *that*
|
|
* onto the request queue.
|
|
*/
|
|
if (cto2) {
|
|
qe = (ct7_entry_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti);
|
|
curi = nxti;
|
|
nxti = ISP_NXT_QENTRY(curi, RQUEST_QUEUE_LEN(isp));
|
|
if (nxti == optr) {
|
|
pci_unmap_sg(pcs->pci_dev, tmd->cd_data, nseg, (cto->ct_flags & CT7_DATA_IN)? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: request queue overflow", __FUNCTION__);
|
|
cto->ct_resid = -EAGAIN;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
MEMORYBARRIER(isp, SYNC_REQUEST, curi, QENTRY_LEN);
|
|
isp_put_ctio7(isp, cto2, (ct7_entry_t *)qe);
|
|
ISP_TDQE(isp, "tdma_mk_2400:final", curi, cto2);
|
|
}
|
|
#endif
|
|
qe = ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx);
|
|
isp_put_ctio7(isp, cto, qe);
|
|
if (cto->ct_flags & CT2_FASTPOST) {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "[%x] fastpost (0x%x) with entry count %d", cto->ct_rxid, tmd->cd_cdb[0], cto->ct_header.rqs_entry_count);
|
|
}
|
|
ISP_TDQE(isp, "tdma_mk_2400", isp->isp_reqidx, cto);
|
|
*nxtip = nxti;
|
|
return (CMD_QUEUED);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
tdma_mkfc(ispsoftc_t *isp, tmd_cmd_t *tmd, ct2_entry_t *cto, uint32_t *nxtip, uint32_t optr)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
static const char ctx[] = "CTIO2[%x] lun %d for iid %d flgs 0x%x sts 0x%x ssts 0x%x res %d %s";
|
|
XS_DMA_ADDR_T addr, last_synthetic_addr;
|
|
struct scatterlist *sg;
|
|
void *qe;
|
|
uint32_t swd, curi, nxti;
|
|
uint32_t bc, last_synthetic_count;
|
|
long xfcnt; /* must be signed */
|
|
int nseg, seg, ovseg, seglim, new_seg_cnt;
|
|
#ifdef ALLOW_SYNTHETIC_CTIO
|
|
ct2_entry_t *cto2 = NULL, ct2;
|
|
#endif
|
|
|
|
nxti = *nxtip;
|
|
curi = isp->isp_reqidx;
|
|
qe = ISP_QUEUE_ENTRY(isp->isp_rquest, curi);
|
|
if (cto->ct_flags & CT2_FASTPOST) {
|
|
if ((tmd->cd_hflags & (CDFH_STSVALID|CDFH_SNSVALID)) != CDFH_STSVALID) {
|
|
cto->ct_flags &= ~CT2_FASTPOST;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle commands that transfer no data right away.
|
|
*/
|
|
if (tmd->cd_xfrlen == 0) {
|
|
if ((cto->ct_flags & CT2_FLAG_MMASK) != CT2_FLAG_MODE1) {
|
|
isp_prt(isp, ISP_LOGERR, "%s: a status CTIO2 without MODE1 set (0x%x)", __FUNCTION__, cto->ct_flags);
|
|
cto->ct_resid = -EINVAL;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
cto->ct_header.rqs_entry_count = 1;
|
|
cto->ct_header.rqs_seqno = 1;
|
|
|
|
/* ct_syshandle contains the synchronization handle set by caller */
|
|
/*
|
|
* We preserve ct_lun, ct_iid, ct_rxid. We set the data movement
|
|
* flags to NO DATA and clear relative offset flags. We preserve
|
|
* ct_resid and the response area. We assume that if there is
|
|
* associated sense data that it has been appropriately set by
|
|
* the caller.
|
|
*/
|
|
cto->ct_flags |= CT2_NO_DATA;
|
|
if (cto->ct_resid > 0) {
|
|
cto->rsp.m1.ct_scsi_status |= CT2_DATA_UNDER;
|
|
cto->ct_flags &= ~CT2_FASTPOST;
|
|
} else if (cto->ct_resid < 0) {
|
|
cto->rsp.m1.ct_scsi_status |= CT2_DATA_OVER;
|
|
cto->ct_flags &= ~CT2_FASTPOST;
|
|
}
|
|
cto->ct_seg_count = 0;
|
|
cto->ct_reloff = 0;
|
|
isp_prt(isp, ISP_LOGTDEBUG1, ctx, cto->ct_rxid, L0LUN_TO_FLATLUN(tmd->cd_lun), cto->ct_iid, cto->ct_flags, cto->ct_status, cto->rsp.m1.ct_scsi_status,
|
|
cto->ct_resid, "<END>");
|
|
isp_put_ctio2(isp, cto, qe);
|
|
if (cto->ct_flags & CT2_FASTPOST) {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "[%x] nodata (0x%x)", cto->ct_rxid,
|
|
tmd->cd_cdb[0]);
|
|
}
|
|
ISP_TDQE(isp, "tdma_mkfc[no data]", curi, qe);
|
|
return (CMD_QUEUED);
|
|
}
|
|
|
|
if ((cto->ct_flags & CT2_FLAG_MMASK) != CT2_FLAG_MODE0) {
|
|
isp_prt(isp, ISP_LOGERR, "%s: a data CTIO2 without MODE0 set (0x%x)", __FUNCTION__, cto->ct_flags);
|
|
cto->ct_resid = -EINVAL;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
|
|
if (tmd->cd_xfrlen <= 1024) {
|
|
nseg = 0;
|
|
} else if (tmd->cd_xfrlen <= 4096) {
|
|
nseg = 1;
|
|
} else if (tmd->cd_xfrlen <= 32768) {
|
|
nseg = 2;
|
|
} else if (tmd->cd_xfrlen <= 65536) {
|
|
nseg = 3;
|
|
} else if (tmd->cd_xfrlen <= 131372) {
|
|
nseg = 4;
|
|
} else if (tmd->cd_xfrlen <= 262144) {
|
|
nseg = 5;
|
|
} else if (tmd->cd_xfrlen <= 524288) {
|
|
nseg = 6;
|
|
} else {
|
|
nseg = 7;
|
|
}
|
|
isp->isp_osinfo.bins[nseg]++;
|
|
|
|
|
|
/*
|
|
* First, count and map all S/G segments
|
|
*
|
|
* The byte counter has to be signed because
|
|
* we can have descriptors that are, in fact,
|
|
* longer than our data transfer count.
|
|
*/
|
|
sg = tmd->cd_data;
|
|
nseg = 0;
|
|
xfcnt = tmd->cd_xfrlen;
|
|
while (xfcnt > 0) {
|
|
if (sg->length == 0) {
|
|
isp_prt(isp, ISP_LOGWARN, "%s: zero length segment #%d for tag %llx\n", __FUNCTION__, nseg, tmd->cd_tagval);
|
|
cto->ct_resid = -EINVAL;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
nseg++;
|
|
xfcnt -= sg->length;
|
|
sg++;
|
|
}
|
|
sg = tmd->cd_data;
|
|
new_seg_cnt = pci_map_sg(pcs->pci_dev, sg, nseg, (cto->ct_flags & CT2_DATA_IN)? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
if (new_seg_cnt == 0) {
|
|
isp_prt(isp, ISP_LOGWARN, "%s: unable to dma map request", __FUNCTION__);
|
|
cto->ct_resid = -ENOMEM;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
tmd->cd_nseg = new_seg_cnt;
|
|
|
|
/*
|
|
* Second, figure out whether we'll need to send a separate status CTIO.
|
|
*/
|
|
swd = cto->rsp.m0.ct_scsi_status;
|
|
|
|
if ((cto->ct_flags & CT2_SENDSTATUS) && ((swd & 0xf) || cto->ct_resid)) {
|
|
#ifdef ALLOW_SYNTHETIC_CTIO
|
|
cto2 = &ct2;
|
|
/*
|
|
* Copy over CTIO2
|
|
*/
|
|
MEMCPY(cto2, cto, sizeof (ct2_entry_t));
|
|
|
|
/*
|
|
* Clear fields from first CTIO2 that now need to be cleared
|
|
*/
|
|
cto->ct_flags &= ~(CT2_SENDSTATUS|CT2_CCINCR|CT2_FASTPOST);
|
|
cto->ct_resid = 0;
|
|
cto->ct_syshandle = 0;
|
|
cto->rsp.m0.ct_scsi_status = 0;
|
|
|
|
/*
|
|
* Reset fields in the second CTIO2 as appropriate.
|
|
*/
|
|
cto2->ct_flags &= ~(CT2_FLAG_MMASK|CT2_DATAMASK|CT2_FASTPOST);
|
|
cto2->ct_flags |= CT2_NO_DATA|CT2_NO_DATA|CT2_FLAG_MODE1;
|
|
cto2->ct_seg_count = 0;
|
|
cto2->ct_reloff = 0;
|
|
MEMZERO(&cto2->rsp, sizeof (cto2->rsp));
|
|
cto2->rsp.m1.ct_scsi_status = swd;
|
|
if ((swd & 0xf) == SCSI_CHECK && (swd & CT2_SNSLEN_VALID)) {
|
|
cto2->rsp.m1.ct_senselen = min(TMD_SENSELEN, MAXRESPLEN);
|
|
MEMCPY(cto2->rsp.m1.ct_resp, tmd->cd_sense, cto2->rsp.m1.ct_senselen);
|
|
cto2->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID;
|
|
}
|
|
#else
|
|
cto->ct_flags &= ~(CT2_SENDSTATUS|CT2_CCINCR|CT2_FASTPOST);
|
|
cto->ct_resid = 0;
|
|
cto->rsp.m0.ct_scsi_status = 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Third, fill in the data segments in the first CTIO2 itself.
|
|
* This is also a good place to set the relative offset.
|
|
*/
|
|
xfcnt = tmd->cd_xfrlen;
|
|
|
|
/*
|
|
* cd_resid was already decremented by cd_xfrlen in isp_target_start_ctio
|
|
*
|
|
* We're taking the total amount for the command and backing it off for
|
|
* the amounts already known to have transferred. That should get us the
|
|
* relative offset to start at for this transfer.
|
|
*/
|
|
cto->ct_reloff = tmd->cd_totlen - (tmd->cd_resid + tmd->cd_xfrlen);
|
|
|
|
/*
|
|
* This is a good place to return to if we need to redo this with
|
|
* 64 bit PCI addressing. We really want to use 32 bit addressing
|
|
* if we can because it's a lot more efficient.
|
|
*/
|
|
if (IS_2322(isp)) {
|
|
seglim = ISP_RQDSEG_T3;
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO3;
|
|
if (cto2) {
|
|
cto2->ct_header.rqs_entry_type = RQSTYPE_CTIO3;
|
|
}
|
|
} else {
|
|
seglim = ISP_RQDSEG_T2;
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
|
|
if (cto2) {
|
|
cto2->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
|
|
}
|
|
}
|
|
|
|
again:
|
|
last_synthetic_count = 0;
|
|
last_synthetic_addr = 0;
|
|
cto->ct_seg_count = min(nseg, seglim);
|
|
|
|
for (seg = 0; seg < cto->ct_seg_count; seg++) {
|
|
bc = min(sg_dma_len(sg), xfcnt);
|
|
addr = sg_dma_address(sg);
|
|
#ifdef ISP_DAC_SUPPORTED
|
|
if (seglim == ISP_RQDSEG_T2) {
|
|
if (IS_HIGH_ISP_ADDR(addr)) {
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO3;
|
|
if (cto2) {
|
|
cto2->ct_header.rqs_entry_type = RQSTYPE_CTIO3;
|
|
}
|
|
xfcnt = tmd->cd_xfrlen;
|
|
cto->rsp.m0.ct_xfrlen = 0;
|
|
sg = tmd->cd_data;
|
|
seglim = ISP_RQDSEG_T3;
|
|
isp_prt(isp, ISP_LOGTDEBUG2, "%s: found hi page", __FUNCTION__);
|
|
goto again;
|
|
}
|
|
cto->rsp.m0.u.ct_dataseg[seg].ds_base = LOWD(addr);
|
|
cto->rsp.m0.u.ct_dataseg[seg].ds_count = bc;
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: seg0[%d]%x:%u", __FUNCTION__, seg, cto->rsp.m0.u.ct_dataseg[seg].ds_base, bc);
|
|
} else {
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_base = LOWD(addr);
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_basehi = HIWD(addr);
|
|
if (!SAME_4G(addr, bc)) {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "seg0[%d]%x%08x:%u (TRUNC'd)", seg, (uint32_t) HIWD(addr), (uint32_t)LOWD(addr), bc);
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_count = (unsigned int) (FOURG_SEG(addr + bc) - addr);
|
|
addr += cto->rsp.m0.u.ct_dataseg64[seg].ds_count;
|
|
bc -= cto->rsp.m0.u.ct_dataseg64[seg].ds_count;
|
|
/*
|
|
* Do we have space to split it here?
|
|
*/
|
|
if (seg == seglim - 1) {
|
|
last_synthetic_count = bc;
|
|
last_synthetic_addr = addr;
|
|
} else {
|
|
cto->ct_seg_count++;
|
|
seg++;
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_count = bc;
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_base = LOWD(addr);
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_basehi = HIWD(addr);
|
|
isp_prt(isp, ISP_LOGALL, "%s: seg0[%d]%lx%08lx:%u", __FUNCTION__, seg,
|
|
(unsigned long)cto->rsp.m0.u.ct_dataseg64[seg].ds_basehi, (unsigned long)cto->rsp.m0.u.ct_dataseg64[seg].ds_base, bc);
|
|
}
|
|
} else {
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_count = bc;
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: seg0[%d]%lx%08lx:%u", __FUNCTION__, seg,
|
|
(unsigned long)cto->rsp.m0.u.ct_dataseg64[seg].ds_basehi, (unsigned long)cto->rsp.m0.u.ct_dataseg64[seg].ds_base, bc);
|
|
}
|
|
}
|
|
#else
|
|
if (seglim == ISP_RQDSEG_T2) {
|
|
cto->rsp.m0.u.ct_dataseg[seg].ds_base = addr;
|
|
cto->rsp.m0.u.ct_dataseg[seg].ds_count = bc;
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: seg0[%d]%x:%u", __FUNCTION__, seg, cto->rsp.m0.u.ct_dataseg[seg].ds_base, bc);
|
|
} else {
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_base = addr;
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_basehi = 0;
|
|
cto->rsp.m0.u.ct_dataseg64[seg].ds_count = bc;
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: seg0[%d]%lx:%u", __FUNCTION__, seg, (unsigned long) cto->rsp.m0.u.ct_dataseg64[seg].ds_base, bc);
|
|
}
|
|
#endif
|
|
cto->rsp.m0.ct_xfrlen += bc;
|
|
xfcnt -= bc;
|
|
sg++;
|
|
}
|
|
|
|
|
|
if (seg == nseg && last_synthetic_count == 0) {
|
|
goto mbxsync;
|
|
}
|
|
|
|
/*
|
|
* Now do any continuation segments that are required.
|
|
*/
|
|
do {
|
|
int lim;
|
|
uint32_t curip;
|
|
ispcontreq_t local, *crq = &local, *qep;
|
|
|
|
curip = nxti;
|
|
qep = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, curip);
|
|
nxti = ISP_NXT_QENTRY((curip), RQUEST_QUEUE_LEN(isp));
|
|
if (nxti == optr) {
|
|
pci_unmap_sg(pcs->pci_dev, tmd->cd_data, nseg, (cto->ct_flags & CT2_DATA_IN)? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: out of space for continuations (%d of %d segs done)", __FUNCTION__, cto->ct_seg_count, nseg);
|
|
return (CMD_EAGAIN);
|
|
}
|
|
cto->ct_header.rqs_entry_count++;
|
|
MEMZERO((void *)crq, sizeof (*crq));
|
|
crq->req_header.rqs_entry_count = 1;
|
|
if (cto->ct_header.rqs_entry_type == RQSTYPE_CTIO3) {
|
|
crq->req_header.rqs_entry_type = RQSTYPE_A64_CONT;
|
|
lim = ISP_CDSEG64;
|
|
} else {
|
|
crq->req_header.rqs_entry_type = RQSTYPE_DATASEG;
|
|
lim = ISP_CDSEG;
|
|
}
|
|
|
|
for (ovseg = 0; (seg < nseg || last_synthetic_count) && ovseg < lim; seg++, ovseg++, sg++) {
|
|
if (last_synthetic_count) {
|
|
addr = last_synthetic_addr;
|
|
bc = last_synthetic_count;
|
|
last_synthetic_count = 0;
|
|
sg--;
|
|
seg--;
|
|
} else {
|
|
addr = sg_dma_address(sg);
|
|
bc = min(sg_dma_len(sg), xfcnt);
|
|
}
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: seg%d[%d]%llx:%u", __FUNCTION__, cto->ct_header.rqs_entry_count-1, ovseg, (unsigned long long) addr, bc);
|
|
|
|
cto->ct_seg_count++;
|
|
cto->rsp.m0.ct_xfrlen += bc;
|
|
|
|
if (crq->req_header.rqs_entry_type == RQSTYPE_A64_CONT) {
|
|
ispcontreq64_t *xrq = (ispcontreq64_t *) crq;
|
|
xrq->req_dataseg[ovseg].ds_count = bc;
|
|
xrq->req_dataseg[ovseg].ds_base = LOWD(addr);
|
|
xrq->req_dataseg[ovseg].ds_basehi = HIWD(addr);
|
|
/*
|
|
* Make sure we don't cross a 4GB boundary.
|
|
*/
|
|
if (!SAME_4G(addr, bc)) {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "seg%d[%d]%llx:%u (TRUNC'd)", cto->ct_header.rqs_entry_count-1, ovseg, (long long)addr, bc);
|
|
xrq->req_dataseg[ovseg].ds_count = (unsigned int) (FOURG_SEG(addr + bc) - addr);
|
|
addr += xrq->req_dataseg[ovseg].ds_count;
|
|
bc -= xrq->req_dataseg[ovseg].ds_count;
|
|
xfcnt -= xrq->req_dataseg[ovseg].ds_count;
|
|
/*
|
|
* Do we have space to split it here?
|
|
*/
|
|
if (ovseg == lim - 1) {
|
|
last_synthetic_count = bc;
|
|
last_synthetic_addr = addr;
|
|
cto->ct_seg_count++;
|
|
} else {
|
|
ovseg++;
|
|
xrq->req_dataseg[ovseg].ds_count = bc;
|
|
xrq->req_dataseg[ovseg].ds_base = LOWD(addr);
|
|
xrq->req_dataseg[ovseg].ds_basehi = HIWD(addr);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
/*
|
|
* We get here if we're a 32 bit continuation entry.
|
|
* We also check for being over 32 bits with our PCI
|
|
* address. If we are, we set ourselves up to do 64
|
|
* bit addressing and start the whole mapping process
|
|
* all over again- we apparently can't really mix types
|
|
*/
|
|
if (ISP_A64 && IS_HIGH_ISP_ADDR(addr)) {
|
|
nxti = *nxtip;
|
|
cto->ct_header.rqs_entry_count = 1;
|
|
xfcnt = tmd->cd_xfrlen;
|
|
cto->ct_header.rqs_entry_type = RQSTYPE_CTIO3;
|
|
if (cto2) {
|
|
cto2->ct_header.rqs_entry_type = RQSTYPE_CTIO3;
|
|
}
|
|
cto->rsp.m0.ct_xfrlen = 0;
|
|
sg = tmd->cd_data;
|
|
seglim = ISP_RQDSEG_T3;
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "%s: found hi page in continuation, restarting", __FUNCTION__);
|
|
goto again;
|
|
}
|
|
crq->req_dataseg[ovseg].ds_count = bc;
|
|
crq->req_dataseg[ovseg].ds_base = addr;
|
|
xfcnt -= bc;
|
|
}
|
|
|
|
ISP_TDQE(isp, "tdma_mkfc cont", curip, crq);
|
|
MEMORYBARRIER(isp, SYNC_REQUEST, curip, QENTRY_LEN);
|
|
if (crq->req_header.rqs_entry_type == RQSTYPE_A64_CONT) {
|
|
isp_put_cont64_req(isp, (ispcontreq64_t *)crq, (ispcontreq64_t *)qep);
|
|
} else {
|
|
isp_put_cont_req(isp, crq, qep);
|
|
}
|
|
} while (seg < nseg || last_synthetic_count);
|
|
|
|
isp_prt(isp, ISP_LOGTDEBUG2, "[%llx]: map %d segments at %p for handle 0x%x", tmd->cd_tagval, new_seg_cnt, tmd->cd_data, cto->ct_syshandle);
|
|
|
|
mbxsync:
|
|
|
|
#ifdef ALLOW_SYNTHETIC_CTIO
|
|
/*
|
|
* If we have a final CTIO2, allocate and push *that*
|
|
* onto the request queue.
|
|
*/
|
|
if (cto2) {
|
|
qe = (ct2_entry_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti);
|
|
curi = nxti;
|
|
nxti = ISP_NXT_QENTRY(curi, RQUEST_QUEUE_LEN(isp));
|
|
if (nxti == optr) {
|
|
pci_unmap_sg(pcs->pci_dev, tmd->cd_data, nseg, (cto->ct_flags & CT2_DATA_IN)? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
isp_prt(isp, ISP_LOGTDEBUG0, "%s: request queue overflow", __FUNCTION__);
|
|
cto->ct_resid = -EAGAIN;
|
|
return (CMD_COMPLETE);
|
|
}
|
|
MEMORYBARRIER(isp, SYNC_REQUEST, curi, QENTRY_LEN);
|
|
isp_put_ctio2(isp, cto2, (ct2_entry_t *)qe);
|
|
ISP_TDQE(isp, "tdma_mkfc:final", curi, cto2);
|
|
}
|
|
#endif
|
|
qe = ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx);
|
|
isp_put_ctio2(isp, cto, qe);
|
|
if (cto->ct_flags & CT2_FASTPOST) {
|
|
isp_prt(isp, ISP_LOGTDEBUG1, "[%x] fastpost (0x%x) with entry count %d", cto->ct_rxid, tmd->cd_cdb[0], cto->ct_header.rqs_entry_count);
|
|
}
|
|
ISP_TDQE(isp, "tdma_mkfc", isp->isp_reqidx, cto);
|
|
*nxtip = nxti;
|
|
return (CMD_QUEUED);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
isp_pci_dmasetup(ispsoftc_t *isp, Scsi_Cmnd *Cmnd, ispreq_t *rq, uint32_t *nxi, uint32_t optr)
|
|
{
|
|
struct scatterlist *sg, *savesg;
|
|
XS_DMA_ADDR_T one_shot_addr, last_synthetic_addr;
|
|
unsigned int one_shot_length, last_synthetic_count;
|
|
int segcnt, seg, ovseg, seglim;
|
|
void *h;
|
|
uint32_t nxti;
|
|
|
|
#ifdef ISP_TARGET_MODE
|
|
if (rq->req_header.rqs_entry_type == RQSTYPE_CTIO || rq->req_header.rqs_entry_type == RQSTYPE_CTIO2 ||
|
|
rq->req_header.rqs_entry_type == RQSTYPE_CTIO3) {
|
|
int s;
|
|
if (IS_FC(isp)) {
|
|
s = tdma_mkfc(isp, (tmd_cmd_t *)Cmnd, (ct2_entry_t *)rq, nxi, optr);
|
|
} else {
|
|
s = tdma_mk(isp, (tmd_cmd_t *)Cmnd, (ct_entry_t *)rq, nxi, optr);
|
|
}
|
|
return (s);
|
|
}
|
|
#endif
|
|
|
|
nxti = *nxi;
|
|
h = (void *) ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx);
|
|
|
|
if (Cmnd->sc_data_direction == SCSI_DATA_NONE || Cmnd->request_bufflen == 0) {
|
|
rq->req_seg_count = 1;
|
|
goto mbxsync;
|
|
}
|
|
|
|
if (Cmnd->request_bufflen <= 1024) {
|
|
seg = 0;
|
|
} else if (Cmnd->request_bufflen <= 4096) {
|
|
seg = 1;
|
|
} else if (Cmnd->request_bufflen <= 32768) {
|
|
seg = 2;
|
|
} else if (Cmnd->request_bufflen <= 65536) {
|
|
seg = 3;
|
|
} else if (Cmnd->request_bufflen <= 131372) {
|
|
seg = 4;
|
|
} else if (Cmnd->request_bufflen <= 262144) {
|
|
seg = 5;
|
|
} else if (Cmnd->request_bufflen <= 524288) {
|
|
seg = 6;
|
|
} else {
|
|
seg = 7;
|
|
}
|
|
isp->isp_osinfo.bins[seg]++;
|
|
|
|
if (IS_FC(isp)) {
|
|
seglim = ISP_RQDSEG_T2;
|
|
((ispreqt2_t *)rq)->req_totalcnt = Cmnd->request_bufflen;
|
|
if (Cmnd->sc_data_direction == SCSI_DATA_WRITE) {
|
|
((ispreqt2_t *)rq)->req_flags |= REQFLAG_DATA_OUT;
|
|
} else if (Cmnd->sc_data_direction == SCSI_DATA_READ) {
|
|
((ispreqt2_t *)rq)->req_flags |= REQFLAG_DATA_IN;
|
|
} else {
|
|
isp_prt(isp, ISP_LOGERR, "%s: unkown data direction (%x) for %d byte request (opcode 0x%x)", __FUNCTION__,
|
|
Cmnd->sc_data_direction, Cmnd->request_bufflen, Cmnd->cmnd[0]);
|
|
XS_SETERR(Cmnd, HBA_BOTCH);
|
|
return (CMD_COMPLETE);
|
|
}
|
|
} else {
|
|
if (Cmnd->cmd_len > 12) {
|
|
seglim = 0;
|
|
} else {
|
|
seglim = ISP_RQDSEG;
|
|
}
|
|
if (Cmnd->sc_data_direction == SCSI_DATA_WRITE) {
|
|
rq->req_flags |= REQFLAG_DATA_OUT;
|
|
} else if (Cmnd->sc_data_direction == SCSI_DATA_READ) {
|
|
rq->req_flags |= REQFLAG_DATA_IN;
|
|
} else {
|
|
isp_prt(isp, ISP_LOGERR, "%s: unkown data direction (%x) for %d byte request (opcode 0x%x)", __FUNCTION__,
|
|
Cmnd->sc_data_direction, Cmnd->request_bufflen, Cmnd->cmnd[0]);
|
|
XS_SETERR(Cmnd, HBA_BOTCH);
|
|
return (CMD_COMPLETE);
|
|
}
|
|
}
|
|
|
|
one_shot_addr = (XS_DMA_ADDR_T) 0;
|
|
one_shot_length = 0;
|
|
if ((segcnt = Cmnd->use_sg) == 0) {
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
segcnt = 1;
|
|
sg = NULL;
|
|
one_shot_length = Cmnd->request_bufflen;
|
|
one_shot_addr = pci_map_single(pcs->pci_dev, Cmnd->request_buffer, Cmnd->request_bufflen, scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
|
|
QLA_HANDLE(Cmnd) = (DMA_HTYPE_T) one_shot_addr;
|
|
} else {
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
sg = (struct scatterlist *) Cmnd->request_buffer;
|
|
segcnt = pci_map_sg(pcs->pci_dev, sg, Cmnd->use_sg, scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
|
|
}
|
|
if (segcnt == 0) {
|
|
isp_prt(isp, ISP_LOGWARN, "%s: unable to dma map request", __FUNCTION__);
|
|
XS_SETERR(Cmnd, HBA_BOTCH);
|
|
return (CMD_EAGAIN);
|
|
}
|
|
savesg = sg;
|
|
|
|
again:
|
|
last_synthetic_count = 0;
|
|
last_synthetic_addr = 0;
|
|
for (seg = 0, rq->req_seg_count = 0; seg < segcnt && rq->req_seg_count < seglim; seg++, rq->req_seg_count++) {
|
|
XS_DMA_ADDR_T addr;
|
|
unsigned int length;
|
|
|
|
if (sg) {
|
|
length = sg_dma_len(sg);
|
|
addr = sg_dma_address(sg);
|
|
sg++;
|
|
} else {
|
|
length = one_shot_length;
|
|
addr = one_shot_addr;
|
|
}
|
|
|
|
if (ISP_A64 && IS_HIGH_ISP_ADDR(addr)) {
|
|
if (IS_FC(isp)) {
|
|
if (rq->req_header.rqs_entry_type != RQSTYPE_T3RQS) {
|
|
rq->req_header.rqs_entry_type = RQSTYPE_T3RQS;
|
|
seglim = ISP_RQDSEG_T3;
|
|
sg = savesg;
|
|
goto again;
|
|
}
|
|
} else {
|
|
if (rq->req_header.rqs_entry_type != RQSTYPE_A64) {
|
|
rq->req_header.rqs_entry_type = RQSTYPE_A64;
|
|
seglim = ISP_RQDSEG_A64;
|
|
sg = savesg;
|
|
goto again;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ISP_A64 && rq->req_header.rqs_entry_type == RQSTYPE_T3RQS) {
|
|
ispreqt3_t *rq3 = (ispreqt3_t *)rq;
|
|
rq3->req_dataseg[rq3->req_seg_count].ds_count = length;
|
|
rq3->req_dataseg[rq3->req_seg_count].ds_base = LOWD(addr);
|
|
rq3->req_dataseg[rq3->req_seg_count].ds_basehi = HIWD(addr);
|
|
/*
|
|
* Make sure we don't cross a 4GB boundary.
|
|
*/
|
|
if (!SAME_4G(addr, length)) {
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg0[%d]%08x%08x:%u (TRUNC'd)", rq->req_seg_count, (uint32_t)HIWD(addr), (uint32_t)LOWD(addr), length);
|
|
rq3->req_dataseg[rq3->req_seg_count].ds_count = (unsigned int) (FOURG_SEG(addr + length) - addr);
|
|
addr += rq3->req_dataseg[rq3->req_seg_count].ds_count;
|
|
length -= rq3->req_dataseg[rq3->req_seg_count].ds_count;
|
|
/*
|
|
* Do we have space to split it here?
|
|
*/
|
|
if (rq3->req_seg_count == seglim - 1) {
|
|
last_synthetic_count = length;
|
|
last_synthetic_addr = addr;
|
|
} else {
|
|
rq3->req_seg_count++;
|
|
rq3->req_dataseg[rq3->req_seg_count].ds_count = length;
|
|
rq3->req_dataseg[rq3->req_seg_count].ds_base = LOWD(addr);
|
|
rq3->req_dataseg[rq3->req_seg_count].ds_basehi = HIWD(addr);
|
|
}
|
|
}
|
|
} else if (ISP_A64 && rq->req_header.rqs_entry_type == RQSTYPE_A64) {
|
|
ispreq64_t *rq6 = (ispreq64_t *)rq;
|
|
rq6->req_dataseg[rq6->req_seg_count].ds_count = length;
|
|
rq6->req_dataseg[rq6->req_seg_count].ds_base = LOWD(addr);
|
|
rq6->req_dataseg[rq6->req_seg_count].ds_basehi = HIWD(addr);
|
|
/*
|
|
* Make sure we don't cross a 4GB boundary.
|
|
*/
|
|
if (!SAME_4G(addr, length)) {
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg0[%d]%llx:%u (TRUNC'd)", rq->req_seg_count, (long long)addr, length);
|
|
rq6->req_dataseg[rq6->req_seg_count].ds_count = (unsigned int) (FOURG_SEG(addr + length) - addr);
|
|
addr += rq6->req_dataseg[rq6->req_seg_count].ds_count;
|
|
length -= rq6->req_dataseg[rq6->req_seg_count].ds_count;
|
|
/*
|
|
* Do we have space to split it here?
|
|
*/
|
|
if (rq6->req_seg_count == seglim - 1) {
|
|
last_synthetic_count = length;
|
|
last_synthetic_addr = LOWD(addr);
|
|
} else {
|
|
rq6->req_seg_count++;
|
|
rq6->req_dataseg[rq6->req_seg_count].ds_count = length;
|
|
rq6->req_dataseg[rq6->req_seg_count].ds_base = LOWD(addr);
|
|
rq6->req_dataseg[rq6->req_seg_count].ds_basehi = HIWD(addr);
|
|
}
|
|
}
|
|
} else if (rq->req_header.rqs_entry_type == RQSTYPE_T2RQS) {
|
|
ispreqt2_t *rq2 = (ispreqt2_t *)rq;
|
|
rq2->req_dataseg[rq2->req_seg_count].ds_count = length;
|
|
rq2->req_dataseg[rq2->req_seg_count].ds_base = addr;
|
|
} else {
|
|
rq->req_dataseg[rq->req_seg_count].ds_count = length;
|
|
rq->req_dataseg[rq->req_seg_count].ds_base = addr;
|
|
}
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg0[%d]%llx:%u", rq->req_seg_count, (long long)addr, length);
|
|
}
|
|
|
|
if (sg == NULL || (seg == segcnt && last_synthetic_count == 0)) {
|
|
goto mbxsync;
|
|
}
|
|
|
|
do {
|
|
int lim;
|
|
uint32_t curip;
|
|
ispcontreq_t local, *crq = &local, *qep;
|
|
|
|
curip = nxti;
|
|
qep = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, curip);
|
|
nxti = ISP_NXT_QENTRY((curip), RQUEST_QUEUE_LEN(isp));
|
|
if (nxti == optr) {
|
|
isp_pci_dmateardown(isp, Cmnd, 0);
|
|
isp_prt(isp, ISP_LOGDEBUG0, "%s: out of space for continuations (%d of %d done)", __FUNCTION__, seg, segcnt);
|
|
XS_SETERR(Cmnd, HBA_BOTCH);
|
|
return (CMD_EAGAIN);
|
|
}
|
|
rq->req_header.rqs_entry_count++;
|
|
MEMZERO((void *)crq, sizeof (*crq));
|
|
crq->req_header.rqs_entry_count = 1;
|
|
if (rq->req_header.rqs_entry_type == RQSTYPE_T3RQS || rq->req_header.rqs_entry_type == RQSTYPE_A64) {
|
|
crq->req_header.rqs_entry_type = RQSTYPE_A64_CONT;
|
|
lim = ISP_CDSEG64;
|
|
} else {
|
|
crq->req_header.rqs_entry_type = RQSTYPE_DATASEG;
|
|
lim = ISP_CDSEG;
|
|
}
|
|
|
|
for (ovseg = 0; (seg < segcnt || last_synthetic_count) && ovseg < lim; rq->req_seg_count++, seg++, ovseg++, sg++) {
|
|
XS_DMA_ADDR_T addr;
|
|
unsigned int length;
|
|
|
|
if (last_synthetic_count) {
|
|
addr = last_synthetic_addr;
|
|
length = last_synthetic_count;
|
|
last_synthetic_count = 0;
|
|
sg--;
|
|
seg--;
|
|
} else {
|
|
addr = sg_dma_address(sg);
|
|
length = sg_dma_len(sg);
|
|
}
|
|
|
|
if (length == 0) {
|
|
panic("zero length s-g element at line %d", __LINE__);
|
|
}
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg%d[%d]%llx:%u", rq->req_header.rqs_entry_count-1, ovseg, (unsigned long long) addr, length);
|
|
|
|
if (crq->req_header.rqs_entry_type == RQSTYPE_A64_CONT) {
|
|
ispcontreq64_t *xrq = (ispcontreq64_t *) crq;
|
|
xrq->req_dataseg[ovseg].ds_count = length;
|
|
xrq->req_dataseg[ovseg].ds_base = LOWD(addr);
|
|
xrq->req_dataseg[ovseg].ds_basehi = HIWD(addr);
|
|
/*
|
|
* Make sure we don't cross a 4GB boundary.
|
|
*/
|
|
if (!SAME_4G(addr, length)) {
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg%d[%d]%llx:%u (TRUNC'd)", rq->req_header.rqs_entry_count-1, ovseg, (long long)addr, length);
|
|
xrq->req_dataseg[ovseg].ds_count = (unsigned int) (FOURG_SEG(addr + length) - addr);
|
|
addr += xrq->req_dataseg[ovseg].ds_count;
|
|
length -= xrq->req_dataseg[ovseg].ds_count;
|
|
/*
|
|
* Do we have space to split it here?
|
|
*/
|
|
if (ovseg == lim - 1) {
|
|
last_synthetic_count = length;
|
|
last_synthetic_addr = addr;
|
|
} else {
|
|
ovseg++;
|
|
xrq->req_dataseg[ovseg].ds_count = length;
|
|
xrq->req_dataseg[ovseg].ds_base = LOWD(addr);
|
|
xrq->req_dataseg[ovseg].ds_basehi = HIWD(addr);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
/*
|
|
* We get here if we're a 32 bit continuation entry.
|
|
* We also check for being over 32 bits with our PCI
|
|
* address. If we are, we set ourselves up to do 64
|
|
* bit addressing and start the whole mapping process
|
|
* all over again- we apparently can't really mix types
|
|
*/
|
|
if (ISP_A64 && IS_HIGH_ISP_ADDR(addr)) {
|
|
if (IS_FC(isp)) {
|
|
rq->req_header.rqs_entry_type = RQSTYPE_T3RQS;
|
|
seglim = ISP_RQDSEG_T3;
|
|
} else {
|
|
rq->req_header.rqs_entry_type = RQSTYPE_A64;
|
|
seglim = ISP_RQDSEG_A64;
|
|
}
|
|
sg = savesg;
|
|
nxti = *nxi;
|
|
rq->req_header.rqs_entry_count = 1;
|
|
goto again;
|
|
}
|
|
crq->req_dataseg[ovseg].ds_count = length;
|
|
crq->req_dataseg[ovseg].ds_base = addr;
|
|
}
|
|
if (isp->isp_dblev & ISP_LOGDEBUG1) {
|
|
isp_print_qentry(isp, "tdma_mkfc: continuation", curip, crq);
|
|
}
|
|
MEMORYBARRIER(isp, SYNC_REQUEST, curip, QENTRY_LEN);
|
|
if (crq->req_header.rqs_entry_type == RQSTYPE_A64_CONT) {
|
|
isp_put_cont64_req(isp, (ispcontreq64_t *)crq, (ispcontreq64_t *)qep);
|
|
} else {
|
|
isp_put_cont_req(isp, crq, qep);
|
|
}
|
|
} while (seg < segcnt || last_synthetic_count);
|
|
mbxsync:
|
|
if (isp->isp_dblev & ISP_LOGDEBUG1) {
|
|
isp_print_qentry(isp, "isp_pci_dmasetup", isp->isp_reqidx, rq);
|
|
}
|
|
|
|
if (rq->req_header.rqs_entry_type == RQSTYPE_T3RQS) {
|
|
if (ISP_CAP_2KLOGIN(isp))
|
|
isp_put_request_t3e(isp, (ispreqt3e_t *) rq, (ispreqt3e_t *) h);
|
|
else
|
|
isp_put_request_t3(isp, (ispreqt3_t *) rq, (ispreqt3_t *) h);
|
|
} else if (rq->req_header.rqs_entry_type == RQSTYPE_T2RQS) {
|
|
if (ISP_CAP_2KLOGIN(isp))
|
|
isp_put_request_t2e(isp, (ispreqt2e_t *) rq, (ispreqt2e_t *) h);
|
|
else
|
|
isp_put_request_t2(isp, (ispreqt2_t *) rq, (ispreqt2_t *) h);
|
|
} else {
|
|
isp_put_request(isp, (ispreq_t *) rq, (ispreq_t *) h);
|
|
}
|
|
*nxi = nxti;
|
|
return (CMD_QUEUED);
|
|
}
|
|
|
|
#ifndef ISP_DISABLE_2400_SUPPORT
|
|
static int
|
|
isp_pci_2400_dmasetup(ispsoftc_t *isp, Scsi_Cmnd *Cmnd, ispreq_t *orig_rq, uint32_t *nxi, uint32_t optr)
|
|
{
|
|
struct scatterlist *sg, *savesg;
|
|
ispreqt7_t *rq;
|
|
XS_DMA_ADDR_T addr, one_shot_addr, last_synthetic_addr;
|
|
unsigned int one_shot_length, last_synthetic_count, length;
|
|
int segcnt, seg, ovseg;
|
|
void *h;
|
|
uint32_t nxti;
|
|
|
|
#ifdef ISP_TARGET_MODE
|
|
if (orig_rq->req_header.rqs_entry_type == RQSTYPE_CTIO7) {
|
|
return tdma_mk_2400(isp, (tmd_cmd_t *)Cmnd, (ct7_entry_t *)orig_rq, nxi, optr);
|
|
}
|
|
#endif
|
|
rq = (ispreqt7_t *) orig_rq;
|
|
nxti = *nxi;
|
|
h = (void *) ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx);
|
|
|
|
if (Cmnd->sc_data_direction == SCSI_DATA_NONE || Cmnd->request_bufflen == 0) {
|
|
rq->req_seg_count = 0;
|
|
goto mbxsync;
|
|
}
|
|
|
|
if (Cmnd->request_bufflen <= 1024) {
|
|
seg = 0;
|
|
} else if (Cmnd->request_bufflen <= 4096) {
|
|
seg = 1;
|
|
} else if (Cmnd->request_bufflen <= 32768) {
|
|
seg = 2;
|
|
} else if (Cmnd->request_bufflen <= 65536) {
|
|
seg = 3;
|
|
} else if (Cmnd->request_bufflen <= 131372) {
|
|
seg = 4;
|
|
} else if (Cmnd->request_bufflen <= 262144) {
|
|
seg = 5;
|
|
} else if (Cmnd->request_bufflen <= 524288) {
|
|
seg = 6;
|
|
} else {
|
|
seg = 7;
|
|
}
|
|
isp->isp_osinfo.bins[seg]++;
|
|
|
|
rq->req_dl = Cmnd->request_bufflen;
|
|
rq->req_seg_count = 1;
|
|
if (Cmnd->sc_data_direction == SCSI_DATA_WRITE) {
|
|
rq->req_alen_datadir = FCP_CMND_DATA_WRITE;
|
|
} else if (Cmnd->sc_data_direction == SCSI_DATA_READ) {
|
|
rq->req_alen_datadir = FCP_CMND_DATA_READ;
|
|
} else {
|
|
isp_prt(isp, ISP_LOGERR, "unknown data direction (%x) for %d byte request (opcode 0x%x)",
|
|
Cmnd->sc_data_direction, Cmnd->request_bufflen, Cmnd->cmnd[0]);
|
|
XS_SETERR(Cmnd, HBA_BOTCH);
|
|
return (CMD_COMPLETE);
|
|
}
|
|
|
|
one_shot_addr = (XS_DMA_ADDR_T) 0;
|
|
one_shot_length = 0;
|
|
if ((segcnt = Cmnd->use_sg) == 0) {
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
segcnt = 1;
|
|
sg = NULL;
|
|
one_shot_length = Cmnd->request_bufflen;
|
|
one_shot_addr = pci_map_single(pcs->pci_dev, Cmnd->request_buffer, Cmnd->request_bufflen, scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
|
|
QLA_HANDLE(Cmnd) = (DMA_HTYPE_T) one_shot_addr;
|
|
} else {
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
sg = (struct scatterlist *) Cmnd->request_buffer;
|
|
segcnt = pci_map_sg(pcs->pci_dev, sg, Cmnd->use_sg, scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
|
|
}
|
|
|
|
if (segcnt == 0) {
|
|
isp_prt(isp, ISP_LOGWARN, "unable to dma map request");
|
|
XS_SETERR(Cmnd, HBA_BOTCH);
|
|
return (CMD_EAGAIN);
|
|
}
|
|
|
|
savesg = sg;
|
|
|
|
last_synthetic_count = 0;
|
|
last_synthetic_addr = 0;
|
|
|
|
if (sg) {
|
|
length = sg_dma_len(sg);
|
|
addr = sg_dma_address(sg);
|
|
sg++;
|
|
} else {
|
|
length = one_shot_length;
|
|
addr = one_shot_addr;
|
|
}
|
|
seg = 1;
|
|
|
|
rq->req_dataseg.ds_base = LOWD(addr);
|
|
rq->req_dataseg.ds_basehi = HIWD(addr);
|
|
rq->req_dataseg.ds_count = length;
|
|
|
|
/*
|
|
* Make sure we don't cross a 4GB boundary.
|
|
*/
|
|
if (!SAME_4G(addr, length)) {
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg0[%d]0x%016llx:%u (TRUNC'd)", rq->req_seg_count, (unsigned long long) addr, length);
|
|
rq->req_dataseg.ds_count = (unsigned int) (FOURG_SEG(addr + length) - addr);
|
|
addr += rq->req_dataseg.ds_count;
|
|
length -= rq->req_dataseg.ds_count;
|
|
last_synthetic_count = length;
|
|
last_synthetic_addr = addr;
|
|
}
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg0[%d]0x%016llx:%u", rq->req_seg_count, (unsigned long long) addr, length);
|
|
|
|
if (sg == NULL || (seg == segcnt && last_synthetic_count == 0)) {
|
|
goto mbxsync;
|
|
}
|
|
|
|
do {
|
|
int lim;
|
|
uint32_t curip;
|
|
ispcontreq64_t local, *xrq = &local, *qep;
|
|
|
|
curip = nxti;
|
|
qep = (ispcontreq64_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, curip);
|
|
nxti = ISP_NXT_QENTRY((curip), RQUEST_QUEUE_LEN(isp));
|
|
if (nxti == optr) {
|
|
isp_pci_dmateardown(isp, Cmnd, 0);
|
|
isp_prt(isp, ISP_LOGWARN, "out of space for continuations (did %d of %d segments)", seg, segcnt);
|
|
XS_SETERR(Cmnd, HBA_BOTCH);
|
|
return (CMD_EAGAIN);
|
|
}
|
|
rq->req_header.rqs_entry_count++;
|
|
MEMZERO((void *)xrq, sizeof (*xrq));
|
|
xrq->req_header.rqs_entry_count = 1;
|
|
xrq->req_header.rqs_entry_type = RQSTYPE_A64_CONT;
|
|
lim = ISP_CDSEG64;
|
|
|
|
for (ovseg = 0; (seg < segcnt || last_synthetic_count) && ovseg < lim; rq->req_seg_count++, seg++, ovseg++, sg++) {
|
|
XS_DMA_ADDR_T addr;
|
|
unsigned int length;
|
|
|
|
if (last_synthetic_count) {
|
|
addr = last_synthetic_addr;
|
|
length = last_synthetic_count;
|
|
last_synthetic_count = 0;
|
|
sg--;
|
|
seg--;
|
|
} else {
|
|
addr = sg_dma_address(sg);
|
|
length = sg_dma_len(sg);
|
|
}
|
|
|
|
if (length == 0) {
|
|
panic("zero length s-g element at line %d", __LINE__);
|
|
}
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg%d[%d]0x%016llx:%u", rq->req_header.rqs_entry_count-1, ovseg, (unsigned long long) addr, length);
|
|
|
|
xrq->req_dataseg[ovseg].ds_count = length;
|
|
xrq->req_dataseg[ovseg].ds_base = LOWD(addr);
|
|
xrq->req_dataseg[ovseg].ds_basehi = HIWD(addr);
|
|
/*
|
|
* Make sure we don't cross a 4GB boundary.
|
|
*/
|
|
if (!SAME_4G(addr, length)) {
|
|
isp_prt(isp, ISP_LOGDEBUG1, "seg%d[%d]%llx:%u (TRUNC'd)", rq->req_header.rqs_entry_count-1, ovseg, (unsigned long long)addr, length);
|
|
xrq->req_dataseg[ovseg].ds_count = (unsigned int) (FOURG_SEG(addr + length) - addr);
|
|
addr += xrq->req_dataseg[ovseg].ds_count;
|
|
length -= xrq->req_dataseg[ovseg].ds_count;
|
|
/*
|
|
* Do we have space to split it here?
|
|
*/
|
|
if (ovseg == lim - 1) {
|
|
last_synthetic_count = length;
|
|
last_synthetic_addr = addr;
|
|
} else {
|
|
ovseg++;
|
|
xrq->req_dataseg[ovseg].ds_count = length;
|
|
xrq->req_dataseg[ovseg].ds_base = LOWD(addr);
|
|
xrq->req_dataseg[ovseg].ds_basehi = HIWD(addr);
|
|
}
|
|
}
|
|
}
|
|
if (isp->isp_dblev & ISP_LOGDEBUG1) {
|
|
isp_print_qentry(isp, "isp_pci_2400_dmasetup continuation", curip, xrq);
|
|
}
|
|
MEMORYBARRIER(isp, SYNC_REQUEST, curip, QENTRY_LEN);
|
|
isp_put_cont64_req(isp, xrq, qep);
|
|
} while (seg < segcnt || last_synthetic_count);
|
|
mbxsync:
|
|
if (isp->isp_dblev & ISP_LOGDEBUG1) {
|
|
isp_print_qentry(isp, "isp_pci_2400_dmasetup", isp->isp_reqidx, rq);
|
|
}
|
|
isp_put_request_t7(isp, rq, (ispreqt7_t *) h);
|
|
*nxi = nxti;
|
|
return (CMD_QUEUED);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
isp_pci_dmateardown(ispsoftc_t *isp, Scsi_Cmnd *Cmnd, uint32_t handle)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp;
|
|
#ifdef ISP_TARGET_MODE
|
|
/*
|
|
* The argument passed may not be a Cmnd pointer- this is the
|
|
* safest way to keep the two w/o redoing our internal apis.
|
|
*/
|
|
if (IS_TARGET_HANDLE(handle)) {
|
|
tmd_cmd_t *tmd = (tmd_cmd_t *) Cmnd;
|
|
int nseg = tmd? tmd->cd_nseg : 0;
|
|
if (nseg && tmd->cd_data) {
|
|
isp_prt(isp, ISP_LOGTDEBUG2, "[%llx]: pci_unmap %d segments at %p for handle 0x%x", tmd->cd_tagval, nseg, tmd->cd_data, handle);
|
|
pci_unmap_sg(pcs->pci_dev, tmd->cd_data, nseg, (tmd->cd_hflags & CDFH_DATA_IN)? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
|
}
|
|
} else
|
|
#endif
|
|
if (Cmnd->sc_data_direction != SCSI_DATA_NONE) {
|
|
if (Cmnd->use_sg) {
|
|
pci_unmap_sg(pcs->pci_dev, (struct scatterlist *)Cmnd->request_buffer,
|
|
Cmnd->use_sg, scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
|
|
} else if (Cmnd->request_bufflen) {
|
|
XS_DMA_ADDR_T dhandle = (XS_DMA_ADDR_T) QLA_HANDLE(Cmnd);
|
|
pci_unmap_single(pcs->pci_dev, dhandle, Cmnd->request_bufflen,
|
|
scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
isp_pci_reset0(ispsoftc_t *isp)
|
|
{
|
|
ISP_DISABLE_INTS(isp);
|
|
isp->mbintsok = 0;
|
|
isp->intsok = 0;
|
|
}
|
|
|
|
static void
|
|
isp_pci_reset1(ispsoftc_t *isp)
|
|
{
|
|
if (!IS_24XX(isp)) {
|
|
isp_pci_wr_reg(isp, HCCR, PCI_HCCR_CMD_BIOS);
|
|
}
|
|
ISP_ENABLE_INTS(isp);
|
|
isp->intsok = 1;
|
|
isp->mbintsok = 1;
|
|
}
|
|
|
|
static void
|
|
isp_pci_dumpregs(ispsoftc_t *isp, const char *msg)
|
|
{
|
|
struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
|
|
uint16_t csr;
|
|
|
|
pci_read_config_word(pcs->pci_dev, PCI_COMMAND, &csr);
|
|
printk("%s: ", isp->isp_name);
|
|
if (msg) {
|
|
printk("%s\n", msg);
|
|
}
|
|
if (IS_SCSI(isp)) {
|
|
printk(" biu_conf1=%x", ISP_READ(isp, BIU_CONF1));
|
|
} else {
|
|
printk(" biu_csr=%x", ISP_READ(isp, BIU2100_CSR));
|
|
}
|
|
printk(" biu_icr=%x biu_isr=%x biu_sema=%x ", ISP_READ(isp, BIU_ICR), ISP_READ(isp, BIU_ISR), ISP_READ(isp, BIU_SEMA));
|
|
printk("risc_hccr=%x\n", ISP_READ(isp, HCCR));
|
|
if (IS_SCSI(isp)) {
|
|
ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
|
|
printk(" cdma_conf=%x cdma_sts=%x cdma_fifostat=%x\n", ISP_READ(isp, CDMA_CONF), ISP_READ(isp, CDMA_STATUS), ISP_READ(isp, CDMA_FIFO_STS));
|
|
printk(" ddma_conf=%x ddma_sts=%x ddma_fifostat=%x\n", ISP_READ(isp, DDMA_CONF), ISP_READ(isp, DDMA_STATUS), ISP_READ(isp, DDMA_FIFO_STS));
|
|
printk(" sxp_int=%x sxp_gross=%x sxp(scsi_ctrl)=%x\n", ISP_READ(isp, SXP_INTERRUPT), ISP_READ(isp, SXP_GROSS_ERR), ISP_READ(isp, SXP_PINS_CTRL));
|
|
ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE);
|
|
}
|
|
printk(" mbox regs: %x %x %x %x %x\n",
|
|
ISP_READ(isp, OUTMAILBOX0), ISP_READ(isp, OUTMAILBOX1),
|
|
ISP_READ(isp, OUTMAILBOX2), ISP_READ(isp, OUTMAILBOX3),
|
|
ISP_READ(isp, OUTMAILBOX4));
|
|
printk(" PCI Status Command/Status=%x\n", csr);
|
|
}
|
|
|
|
static char *isp_pci_exclude = NULL;
|
|
static char *isp_pci_include = NULL;
|
|
|
|
static int
|
|
isplinux_pci_exclude(struct pci_dev *dev)
|
|
{
|
|
int checking_for_inclusion;
|
|
char *wrk;
|
|
|
|
if (isp_pci_include && *isp_pci_include) {
|
|
checking_for_inclusion = 1;
|
|
wrk = isp_pci_include;
|
|
} else {
|
|
checking_for_inclusion = 0;
|
|
wrk = isp_pci_exclude;
|
|
}
|
|
while (wrk && *wrk) {
|
|
unsigned int id;
|
|
char *commatok, *p, *q;
|
|
|
|
commatok = strchr(wrk, ',');
|
|
if (commatok) {
|
|
*commatok = 0;
|
|
}
|
|
if (strncmp(wrk, "0x", 2) == 0) {
|
|
q = wrk + 2;
|
|
} else {
|
|
q = wrk;
|
|
}
|
|
id = simple_strtoul(q, &p, 16);
|
|
if (commatok) {
|
|
*commatok = ',';
|
|
}
|
|
if (p != q) {
|
|
/*
|
|
* We have a device id. See if it matches the current device.
|
|
*/
|
|
unsigned int exid = ((dev->bus->number) << 16) | (PCI_SLOT(dev->devfn) << 8) | (PCI_FUNC(dev->devfn));
|
|
if (id == exid) {
|
|
if (checking_for_inclusion) {
|
|
return (0);
|
|
} else {
|
|
printk(KERN_INFO "isp@<%d,%d,%d>: excluding device\n", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
|
|
return (1);
|
|
}
|
|
}
|
|
}
|
|
if (commatok) {
|
|
wrk = commatok+1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* We didn't find this device on our list and we were checking
|
|
* the list of devices to *include*, so don't attach this device.
|
|
* Otherwise, we can attach this device.
|
|
*/
|
|
if (checking_for_inclusion) {
|
|
printk(KERN_INFO "isp@<%d,%d,%d>: excluding device\n", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
|
|
return (1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
#ifdef MODULE
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
MODULE_PARM(isp_pci_mapmem, "i");
|
|
MODULE_PARM(isp_pci_exclude, "s");
|
|
MODULE_PARM(isp_pci_include, "s");
|
|
#else
|
|
module_param(isp_pci_mapmem, int, 0);
|
|
module_param(isp_pci_exclude, charp, 0);
|
|
module_param(isp_pci_include, charp, 0);
|
|
#endif
|
|
#else
|
|
static int __init isp_exclude(char *str)
|
|
{
|
|
isp_pci_exclude = str;
|
|
return 0;
|
|
}
|
|
__setup("isp_pci_exclude=", isp_exclude);
|
|
|
|
static int __init isp_include(char *str)
|
|
{
|
|
isp_pci_include = str;
|
|
return 0;
|
|
}
|
|
__setup("isp_pci_include=", isp_include);
|
|
|
|
#endif
|
|
/*
|
|
* vim:ts=4:sw=4:expandtab
|
|
*/
|