793 lines
18 KiB
C
793 lines
18 KiB
C
|
/******************************************************************************
|
||
|
** High Performance device driver for the Symbios 53C896 controller.
|
||
|
**
|
||
|
** Copyright (C) 1998-2001 Gerard Roudier <groudier@free.fr>
|
||
|
**
|
||
|
** This driver also supports all the Symbios 53C8XX controller family,
|
||
|
** except 53C810 revisions < 16, 53C825 revisions < 16 and all
|
||
|
** revisions of 53C815 controllers.
|
||
|
**
|
||
|
** This driver is based on the Linux port of the FreeBSD ncr driver.
|
||
|
**
|
||
|
** Copyright (C) 1994 Wolfgang Stanglmeier
|
||
|
**
|
||
|
**-----------------------------------------------------------------------------
|
||
|
**
|
||
|
** 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.
|
||
|
**
|
||
|
**-----------------------------------------------------------------------------
|
||
|
**
|
||
|
** The Linux port of the FreeBSD ncr driver has been achieved in
|
||
|
** november 1995 by:
|
||
|
**
|
||
|
** Gerard Roudier <groudier@free.fr>
|
||
|
**
|
||
|
** Being given that this driver originates from the FreeBSD version, and
|
||
|
** in order to keep synergy on both, any suggested enhancements and corrections
|
||
|
** received on Linux are automatically a potential candidate for the FreeBSD
|
||
|
** version.
|
||
|
**
|
||
|
** The original driver has been written for 386bsd and FreeBSD by
|
||
|
** Wolfgang Stanglmeier <wolf@cologne.de>
|
||
|
** Stefan Esser <se@mi.Uni-Koeln.de>
|
||
|
**
|
||
|
**-----------------------------------------------------------------------------
|
||
|
**
|
||
|
** Major contributions:
|
||
|
** --------------------
|
||
|
**
|
||
|
** NVRAM detection and reading.
|
||
|
** Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
|
||
|
**
|
||
|
*******************************************************************************
|
||
|
*/
|
||
|
|
||
|
/*==========================================================
|
||
|
**
|
||
|
** Debugging tags
|
||
|
**
|
||
|
**==========================================================
|
||
|
*/
|
||
|
|
||
|
#define DEBUG_ALLOC (0x0001)
|
||
|
#define DEBUG_PHASE (0x0002)
|
||
|
#define DEBUG_QUEUE (0x0008)
|
||
|
#define DEBUG_RESULT (0x0010)
|
||
|
#define DEBUG_POINTER (0x0020)
|
||
|
#define DEBUG_SCRIPT (0x0040)
|
||
|
#define DEBUG_TINY (0x0080)
|
||
|
#define DEBUG_TIMING (0x0100)
|
||
|
#define DEBUG_NEGO (0x0200)
|
||
|
#define DEBUG_TAGS (0x0400)
|
||
|
#define DEBUG_SCATTER (0x0800)
|
||
|
#define DEBUG_IC (0x1000)
|
||
|
|
||
|
/*
|
||
|
** Enable/Disable debug messages.
|
||
|
** Can be changed at runtime too.
|
||
|
*/
|
||
|
|
||
|
#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
|
||
|
static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
|
||
|
#define DEBUG_FLAGS ncr_debug
|
||
|
#else
|
||
|
#define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
|
||
|
#endif
|
||
|
|
||
|
static inline struct list_head *ncr_list_pop(struct list_head *head)
|
||
|
{
|
||
|
if (!list_empty(head)) {
|
||
|
struct list_head *elem = head->next;
|
||
|
|
||
|
list_del(elem);
|
||
|
return elem;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#ifdef __sparc__
|
||
|
#include <asm/irq.h>
|
||
|
#endif
|
||
|
|
||
|
/*==========================================================
|
||
|
**
|
||
|
** Simple power of two buddy-like allocator.
|
||
|
**
|
||
|
** This simple code is not intended to be fast, but to
|
||
|
** provide power of 2 aligned memory allocations.
|
||
|
** Since the SCRIPTS processor only supplies 8 bit
|
||
|
** arithmetic, this allocator allows simple and fast
|
||
|
** address calculations from the SCRIPTS code.
|
||
|
** In addition, cache line alignment is guaranteed for
|
||
|
** power of 2 cache line size.
|
||
|
** Enhanced in linux-2.3.44 to provide a memory pool
|
||
|
** per pcidev to support dynamic dma mapping. (I would
|
||
|
** have preferred a real bus astraction, btw).
|
||
|
**
|
||
|
**==========================================================
|
||
|
*/
|
||
|
|
||
|
#define MEMO_SHIFT 4 /* 16 bytes minimum memory chunk */
|
||
|
#if PAGE_SIZE >= 8192
|
||
|
#define MEMO_PAGE_ORDER 0 /* 1 PAGE maximum */
|
||
|
#else
|
||
|
#define MEMO_PAGE_ORDER 1 /* 2 PAGES maximum */
|
||
|
#endif
|
||
|
#define MEMO_FREE_UNUSED /* Free unused pages immediately */
|
||
|
#define MEMO_WARN 1
|
||
|
#define MEMO_GFP_FLAGS GFP_ATOMIC
|
||
|
#define MEMO_CLUSTER_SHIFT (PAGE_SHIFT+MEMO_PAGE_ORDER)
|
||
|
#define MEMO_CLUSTER_SIZE (1UL << MEMO_CLUSTER_SHIFT)
|
||
|
#define MEMO_CLUSTER_MASK (MEMO_CLUSTER_SIZE-1)
|
||
|
|
||
|
typedef u_long m_addr_t; /* Enough bits to bit-hack addresses */
|
||
|
typedef struct device *m_bush_t; /* Something that addresses DMAable */
|
||
|
|
||
|
typedef struct m_link { /* Link between free memory chunks */
|
||
|
struct m_link *next;
|
||
|
} m_link_s;
|
||
|
|
||
|
typedef struct m_vtob { /* Virtual to Bus address translation */
|
||
|
struct m_vtob *next;
|
||
|
m_addr_t vaddr;
|
||
|
m_addr_t baddr;
|
||
|
} m_vtob_s;
|
||
|
#define VTOB_HASH_SHIFT 5
|
||
|
#define VTOB_HASH_SIZE (1UL << VTOB_HASH_SHIFT)
|
||
|
#define VTOB_HASH_MASK (VTOB_HASH_SIZE-1)
|
||
|
#define VTOB_HASH_CODE(m) \
|
||
|
((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK)
|
||
|
|
||
|
typedef struct m_pool { /* Memory pool of a given kind */
|
||
|
m_bush_t bush;
|
||
|
m_addr_t (*getp)(struct m_pool *);
|
||
|
void (*freep)(struct m_pool *, m_addr_t);
|
||
|
int nump;
|
||
|
m_vtob_s *(vtob[VTOB_HASH_SIZE]);
|
||
|
struct m_pool *next;
|
||
|
struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1];
|
||
|
} m_pool_s;
|
||
|
|
||
|
static void *___m_alloc(m_pool_s *mp, int size)
|
||
|
{
|
||
|
int i = 0;
|
||
|
int s = (1 << MEMO_SHIFT);
|
||
|
int j;
|
||
|
m_addr_t a;
|
||
|
m_link_s *h = mp->h;
|
||
|
|
||
|
if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
|
||
|
return NULL;
|
||
|
|
||
|
while (size > s) {
|
||
|
s <<= 1;
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
j = i;
|
||
|
while (!h[j].next) {
|
||
|
if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
|
||
|
h[j].next = (m_link_s *)mp->getp(mp);
|
||
|
if (h[j].next)
|
||
|
h[j].next->next = NULL;
|
||
|
break;
|
||
|
}
|
||
|
++j;
|
||
|
s <<= 1;
|
||
|
}
|
||
|
a = (m_addr_t) h[j].next;
|
||
|
if (a) {
|
||
|
h[j].next = h[j].next->next;
|
||
|
while (j > i) {
|
||
|
j -= 1;
|
||
|
s >>= 1;
|
||
|
h[j].next = (m_link_s *) (a+s);
|
||
|
h[j].next->next = NULL;
|
||
|
}
|
||
|
}
|
||
|
#ifdef DEBUG
|
||
|
printk("___m_alloc(%d) = %p\n", size, (void *) a);
|
||
|
#endif
|
||
|
return (void *) a;
|
||
|
}
|
||
|
|
||
|
static void ___m_free(m_pool_s *mp, void *ptr, int size)
|
||
|
{
|
||
|
int i = 0;
|
||
|
int s = (1 << MEMO_SHIFT);
|
||
|
m_link_s *q;
|
||
|
m_addr_t a, b;
|
||
|
m_link_s *h = mp->h;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
printk("___m_free(%p, %d)\n", ptr, size);
|
||
|
#endif
|
||
|
|
||
|
if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
|
||
|
return;
|
||
|
|
||
|
while (size > s) {
|
||
|
s <<= 1;
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
a = (m_addr_t) ptr;
|
||
|
|
||
|
while (1) {
|
||
|
#ifdef MEMO_FREE_UNUSED
|
||
|
if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
|
||
|
mp->freep(mp, a);
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
b = a ^ s;
|
||
|
q = &h[i];
|
||
|
while (q->next && q->next != (m_link_s *) b) {
|
||
|
q = q->next;
|
||
|
}
|
||
|
if (!q->next) {
|
||
|
((m_link_s *) a)->next = h[i].next;
|
||
|
h[i].next = (m_link_s *) a;
|
||
|
break;
|
||
|
}
|
||
|
q->next = q->next->next;
|
||
|
a = a & b;
|
||
|
s <<= 1;
|
||
|
++i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static DEFINE_SPINLOCK(ncr53c8xx_lock);
|
||
|
|
||
|
static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags)
|
||
|
{
|
||
|
void *p;
|
||
|
|
||
|
p = ___m_alloc(mp, size);
|
||
|
|
||
|
if (DEBUG_FLAGS & DEBUG_ALLOC)
|
||
|
printk ("new %-10s[%4d] @%p.\n", name, size, p);
|
||
|
|
||
|
if (p)
|
||
|
memset(p, 0, size);
|
||
|
else if (uflags & MEMO_WARN)
|
||
|
printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size);
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
#define __m_calloc(mp, s, n) __m_calloc2(mp, s, n, MEMO_WARN)
|
||
|
|
||
|
static void __m_free(m_pool_s *mp, void *ptr, int size, char *name)
|
||
|
{
|
||
|
if (DEBUG_FLAGS & DEBUG_ALLOC)
|
||
|
printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
|
||
|
|
||
|
___m_free(mp, ptr, size);
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* With pci bus iommu support, we use a default pool of unmapped memory
|
||
|
* for memory we donnot need to DMA from/to and one pool per pcidev for
|
||
|
* memory accessed by the PCI chip. `mp0' is the default not DMAable pool.
|
||
|
*/
|
||
|
|
||
|
static m_addr_t ___mp0_getp(m_pool_s *mp)
|
||
|
{
|
||
|
m_addr_t m = __get_free_pages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER);
|
||
|
if (m)
|
||
|
++mp->nump;
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
|
||
|
{
|
||
|
free_pages(m, MEMO_PAGE_ORDER);
|
||
|
--mp->nump;
|
||
|
}
|
||
|
|
||
|
static m_pool_s mp0 = {NULL, ___mp0_getp, ___mp0_freep};
|
||
|
|
||
|
/*
|
||
|
* DMAable pools.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* With pci bus iommu support, we maintain one pool per pcidev and a
|
||
|
* hashed reverse table for virtual to bus physical address translations.
|
||
|
*/
|
||
|
static m_addr_t ___dma_getp(m_pool_s *mp)
|
||
|
{
|
||
|
m_addr_t vp;
|
||
|
m_vtob_s *vbp;
|
||
|
|
||
|
vbp = __m_calloc(&mp0, sizeof(*vbp), "VTOB");
|
||
|
if (vbp) {
|
||
|
dma_addr_t daddr;
|
||
|
vp = (m_addr_t) dma_alloc_coherent(mp->bush,
|
||
|
PAGE_SIZE<<MEMO_PAGE_ORDER,
|
||
|
&daddr, GFP_ATOMIC);
|
||
|
if (vp) {
|
||
|
int hc = VTOB_HASH_CODE(vp);
|
||
|
vbp->vaddr = vp;
|
||
|
vbp->baddr = daddr;
|
||
|
vbp->next = mp->vtob[hc];
|
||
|
mp->vtob[hc] = vbp;
|
||
|
++mp->nump;
|
||
|
return vp;
|
||
|
}
|
||
|
}
|
||
|
if (vbp)
|
||
|
__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void ___dma_freep(m_pool_s *mp, m_addr_t m)
|
||
|
{
|
||
|
m_vtob_s **vbpp, *vbp;
|
||
|
int hc = VTOB_HASH_CODE(m);
|
||
|
|
||
|
vbpp = &mp->vtob[hc];
|
||
|
while (*vbpp && (*vbpp)->vaddr != m)
|
||
|
vbpp = &(*vbpp)->next;
|
||
|
if (*vbpp) {
|
||
|
vbp = *vbpp;
|
||
|
*vbpp = (*vbpp)->next;
|
||
|
dma_free_coherent(mp->bush, PAGE_SIZE<<MEMO_PAGE_ORDER,
|
||
|
(void *)vbp->vaddr, (dma_addr_t)vbp->baddr);
|
||
|
__m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
|
||
|
--mp->nump;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static inline m_pool_s *___get_dma_pool(m_bush_t bush)
|
||
|
{
|
||
|
m_pool_s *mp;
|
||
|
for (mp = mp0.next; mp && mp->bush != bush; mp = mp->next);
|
||
|
return mp;
|
||
|
}
|
||
|
|
||
|
static m_pool_s *___cre_dma_pool(m_bush_t bush)
|
||
|
{
|
||
|
m_pool_s *mp;
|
||
|
mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL");
|
||
|
if (mp) {
|
||
|
memset(mp, 0, sizeof(*mp));
|
||
|
mp->bush = bush;
|
||
|
mp->getp = ___dma_getp;
|
||
|
mp->freep = ___dma_freep;
|
||
|
mp->next = mp0.next;
|
||
|
mp0.next = mp;
|
||
|
}
|
||
|
return mp;
|
||
|
}
|
||
|
|
||
|
static void ___del_dma_pool(m_pool_s *p)
|
||
|
{
|
||
|
struct m_pool **pp = &mp0.next;
|
||
|
|
||
|
while (*pp && *pp != p)
|
||
|
pp = &(*pp)->next;
|
||
|
if (*pp) {
|
||
|
*pp = (*pp)->next;
|
||
|
__m_free(&mp0, p, sizeof(*p), "MPOOL");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void *__m_calloc_dma(m_bush_t bush, int size, char *name)
|
||
|
{
|
||
|
u_long flags;
|
||
|
struct m_pool *mp;
|
||
|
void *m = NULL;
|
||
|
|
||
|
spin_lock_irqsave(&ncr53c8xx_lock, flags);
|
||
|
mp = ___get_dma_pool(bush);
|
||
|
if (!mp)
|
||
|
mp = ___cre_dma_pool(bush);
|
||
|
if (mp)
|
||
|
m = __m_calloc(mp, size, name);
|
||
|
if (mp && !mp->nump)
|
||
|
___del_dma_pool(mp);
|
||
|
spin_unlock_irqrestore(&ncr53c8xx_lock, flags);
|
||
|
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
static void __m_free_dma(m_bush_t bush, void *m, int size, char *name)
|
||
|
{
|
||
|
u_long flags;
|
||
|
struct m_pool *mp;
|
||
|
|
||
|
spin_lock_irqsave(&ncr53c8xx_lock, flags);
|
||
|
mp = ___get_dma_pool(bush);
|
||
|
if (mp)
|
||
|
__m_free(mp, m, size, name);
|
||
|
if (mp && !mp->nump)
|
||
|
___del_dma_pool(mp);
|
||
|
spin_unlock_irqrestore(&ncr53c8xx_lock, flags);
|
||
|
}
|
||
|
|
||
|
static m_addr_t __vtobus(m_bush_t bush, void *m)
|
||
|
{
|
||
|
u_long flags;
|
||
|
m_pool_s *mp;
|
||
|
int hc = VTOB_HASH_CODE(m);
|
||
|
m_vtob_s *vp = NULL;
|
||
|
m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK;
|
||
|
|
||
|
spin_lock_irqsave(&ncr53c8xx_lock, flags);
|
||
|
mp = ___get_dma_pool(bush);
|
||
|
if (mp) {
|
||
|
vp = mp->vtob[hc];
|
||
|
while (vp && (m_addr_t) vp->vaddr != a)
|
||
|
vp = vp->next;
|
||
|
}
|
||
|
spin_unlock_irqrestore(&ncr53c8xx_lock, flags);
|
||
|
return vp ? vp->baddr + (((m_addr_t) m) - a) : 0;
|
||
|
}
|
||
|
|
||
|
#define _m_calloc_dma(np, s, n) __m_calloc_dma(np->dev, s, n)
|
||
|
#define _m_free_dma(np, p, s, n) __m_free_dma(np->dev, p, s, n)
|
||
|
#define m_calloc_dma(s, n) _m_calloc_dma(np, s, n)
|
||
|
#define m_free_dma(p, s, n) _m_free_dma(np, p, s, n)
|
||
|
#define _vtobus(np, p) __vtobus(np->dev, p)
|
||
|
#define vtobus(p) _vtobus(np, p)
|
||
|
|
||
|
/*
|
||
|
* Deal with DMA mapping/unmapping.
|
||
|
*/
|
||
|
|
||
|
/* To keep track of the dma mapping (sg/single) that has been set */
|
||
|
#define __data_mapped SCp.phase
|
||
|
#define __data_mapping SCp.have_data_in
|
||
|
|
||
|
static void __unmap_scsi_data(struct device *dev, struct scsi_cmnd *cmd)
|
||
|
{
|
||
|
switch(cmd->__data_mapped) {
|
||
|
case 2:
|
||
|
dma_unmap_sg(dev, cmd->buffer, cmd->use_sg,
|
||
|
cmd->sc_data_direction);
|
||
|
break;
|
||
|
case 1:
|
||
|
dma_unmap_single(dev, cmd->__data_mapping,
|
||
|
cmd->request_bufflen,
|
||
|
cmd->sc_data_direction);
|
||
|
break;
|
||
|
}
|
||
|
cmd->__data_mapped = 0;
|
||
|
}
|
||
|
|
||
|
static u_long __map_scsi_single_data(struct device *dev, struct scsi_cmnd *cmd)
|
||
|
{
|
||
|
dma_addr_t mapping;
|
||
|
|
||
|
if (cmd->request_bufflen == 0)
|
||
|
return 0;
|
||
|
|
||
|
mapping = dma_map_single(dev, cmd->request_buffer,
|
||
|
cmd->request_bufflen,
|
||
|
cmd->sc_data_direction);
|
||
|
cmd->__data_mapped = 1;
|
||
|
cmd->__data_mapping = mapping;
|
||
|
|
||
|
return mapping;
|
||
|
}
|
||
|
|
||
|
static int __map_scsi_sg_data(struct device *dev, struct scsi_cmnd *cmd)
|
||
|
{
|
||
|
int use_sg;
|
||
|
|
||
|
if (cmd->use_sg == 0)
|
||
|
return 0;
|
||
|
|
||
|
use_sg = dma_map_sg(dev, cmd->buffer, cmd->use_sg,
|
||
|
cmd->sc_data_direction);
|
||
|
cmd->__data_mapped = 2;
|
||
|
cmd->__data_mapping = use_sg;
|
||
|
|
||
|
return use_sg;
|
||
|
}
|
||
|
|
||
|
#define unmap_scsi_data(np, cmd) __unmap_scsi_data(np->dev, cmd)
|
||
|
#define map_scsi_single_data(np, cmd) __map_scsi_single_data(np->dev, cmd)
|
||
|
#define map_scsi_sg_data(np, cmd) __map_scsi_sg_data(np->dev, cmd)
|
||
|
|
||
|
/*==========================================================
|
||
|
**
|
||
|
** Driver setup.
|
||
|
**
|
||
|
** This structure is initialized from linux config
|
||
|
** options. It can be overridden at boot-up by the boot
|
||
|
** command line.
|
||
|
**
|
||
|
**==========================================================
|
||
|
*/
|
||
|
static struct ncr_driver_setup
|
||
|
driver_setup = SCSI_NCR_DRIVER_SETUP;
|
||
|
|
||
|
#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
|
||
|
static struct ncr_driver_setup
|
||
|
driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
|
||
|
#endif
|
||
|
|
||
|
#define initverbose (driver_setup.verbose)
|
||
|
#define bootverbose (np->verbose)
|
||
|
|
||
|
|
||
|
/*===================================================================
|
||
|
**
|
||
|
** Driver setup from the boot command line
|
||
|
**
|
||
|
**===================================================================
|
||
|
*/
|
||
|
|
||
|
#ifdef MODULE
|
||
|
#define ARG_SEP ' '
|
||
|
#else
|
||
|
#define ARG_SEP ','
|
||
|
#endif
|
||
|
|
||
|
#define OPT_TAGS 1
|
||
|
#define OPT_MASTER_PARITY 2
|
||
|
#define OPT_SCSI_PARITY 3
|
||
|
#define OPT_DISCONNECTION 4
|
||
|
#define OPT_SPECIAL_FEATURES 5
|
||
|
#define OPT_UNUSED_1 6
|
||
|
#define OPT_FORCE_SYNC_NEGO 7
|
||
|
#define OPT_REVERSE_PROBE 8
|
||
|
#define OPT_DEFAULT_SYNC 9
|
||
|
#define OPT_VERBOSE 10
|
||
|
#define OPT_DEBUG 11
|
||
|
#define OPT_BURST_MAX 12
|
||
|
#define OPT_LED_PIN 13
|
||
|
#define OPT_MAX_WIDE 14
|
||
|
#define OPT_SETTLE_DELAY 15
|
||
|
#define OPT_DIFF_SUPPORT 16
|
||
|
#define OPT_IRQM 17
|
||
|
#define OPT_PCI_FIX_UP 18
|
||
|
#define OPT_BUS_CHECK 19
|
||
|
#define OPT_OPTIMIZE 20
|
||
|
#define OPT_RECOVERY 21
|
||
|
#define OPT_SAFE_SETUP 22
|
||
|
#define OPT_USE_NVRAM 23
|
||
|
#define OPT_EXCLUDE 24
|
||
|
#define OPT_HOST_ID 25
|
||
|
|
||
|
#ifdef SCSI_NCR_IARB_SUPPORT
|
||
|
#define OPT_IARB 26
|
||
|
#endif
|
||
|
|
||
|
static char setup_token[] __initdata =
|
||
|
"tags:" "mpar:"
|
||
|
"spar:" "disc:"
|
||
|
"specf:" "ultra:"
|
||
|
"fsn:" "revprob:"
|
||
|
"sync:" "verb:"
|
||
|
"debug:" "burst:"
|
||
|
"led:" "wide:"
|
||
|
"settle:" "diff:"
|
||
|
"irqm:" "pcifix:"
|
||
|
"buschk:" "optim:"
|
||
|
"recovery:"
|
||
|
"safe:" "nvram:"
|
||
|
"excl:" "hostid:"
|
||
|
#ifdef SCSI_NCR_IARB_SUPPORT
|
||
|
"iarb:"
|
||
|
#endif
|
||
|
; /* DONNOT REMOVE THIS ';' */
|
||
|
|
||
|
#ifdef MODULE
|
||
|
#define ARG_SEP ' '
|
||
|
#else
|
||
|
#define ARG_SEP ','
|
||
|
#endif
|
||
|
|
||
|
static int __init get_setup_token(char *p)
|
||
|
{
|
||
|
char *cur = setup_token;
|
||
|
char *pc;
|
||
|
int i = 0;
|
||
|
|
||
|
while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
|
||
|
++pc;
|
||
|
++i;
|
||
|
if (!strncmp(p, cur, pc - cur))
|
||
|
return i;
|
||
|
cur = pc;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int __init sym53c8xx__setup(char *str)
|
||
|
{
|
||
|
#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
|
||
|
char *cur = str;
|
||
|
char *pc, *pv;
|
||
|
int i, val, c;
|
||
|
int xi = 0;
|
||
|
|
||
|
while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
|
||
|
char *pe;
|
||
|
|
||
|
val = 0;
|
||
|
pv = pc;
|
||
|
c = *++pv;
|
||
|
|
||
|
if (c == 'n')
|
||
|
val = 0;
|
||
|
else if (c == 'y')
|
||
|
val = 1;
|
||
|
else
|
||
|
val = (int) simple_strtoul(pv, &pe, 0);
|
||
|
|
||
|
switch (get_setup_token(cur)) {
|
||
|
case OPT_TAGS:
|
||
|
driver_setup.default_tags = val;
|
||
|
if (pe && *pe == '/') {
|
||
|
i = 0;
|
||
|
while (*pe && *pe != ARG_SEP &&
|
||
|
i < sizeof(driver_setup.tag_ctrl)-1) {
|
||
|
driver_setup.tag_ctrl[i++] = *pe++;
|
||
|
}
|
||
|
driver_setup.tag_ctrl[i] = '\0';
|
||
|
}
|
||
|
break;
|
||
|
case OPT_MASTER_PARITY:
|
||
|
driver_setup.master_parity = val;
|
||
|
break;
|
||
|
case OPT_SCSI_PARITY:
|
||
|
driver_setup.scsi_parity = val;
|
||
|
break;
|
||
|
case OPT_DISCONNECTION:
|
||
|
driver_setup.disconnection = val;
|
||
|
break;
|
||
|
case OPT_SPECIAL_FEATURES:
|
||
|
driver_setup.special_features = val;
|
||
|
break;
|
||
|
case OPT_FORCE_SYNC_NEGO:
|
||
|
driver_setup.force_sync_nego = val;
|
||
|
break;
|
||
|
case OPT_REVERSE_PROBE:
|
||
|
driver_setup.reverse_probe = val;
|
||
|
break;
|
||
|
case OPT_DEFAULT_SYNC:
|
||
|
driver_setup.default_sync = val;
|
||
|
break;
|
||
|
case OPT_VERBOSE:
|
||
|
driver_setup.verbose = val;
|
||
|
break;
|
||
|
case OPT_DEBUG:
|
||
|
driver_setup.debug = val;
|
||
|
break;
|
||
|
case OPT_BURST_MAX:
|
||
|
driver_setup.burst_max = val;
|
||
|
break;
|
||
|
case OPT_LED_PIN:
|
||
|
driver_setup.led_pin = val;
|
||
|
break;
|
||
|
case OPT_MAX_WIDE:
|
||
|
driver_setup.max_wide = val? 1:0;
|
||
|
break;
|
||
|
case OPT_SETTLE_DELAY:
|
||
|
driver_setup.settle_delay = val;
|
||
|
break;
|
||
|
case OPT_DIFF_SUPPORT:
|
||
|
driver_setup.diff_support = val;
|
||
|
break;
|
||
|
case OPT_IRQM:
|
||
|
driver_setup.irqm = val;
|
||
|
break;
|
||
|
case OPT_PCI_FIX_UP:
|
||
|
driver_setup.pci_fix_up = val;
|
||
|
break;
|
||
|
case OPT_BUS_CHECK:
|
||
|
driver_setup.bus_check = val;
|
||
|
break;
|
||
|
case OPT_OPTIMIZE:
|
||
|
driver_setup.optimize = val;
|
||
|
break;
|
||
|
case OPT_RECOVERY:
|
||
|
driver_setup.recovery = val;
|
||
|
break;
|
||
|
case OPT_USE_NVRAM:
|
||
|
driver_setup.use_nvram = val;
|
||
|
break;
|
||
|
case OPT_SAFE_SETUP:
|
||
|
memcpy(&driver_setup, &driver_safe_setup,
|
||
|
sizeof(driver_setup));
|
||
|
break;
|
||
|
case OPT_EXCLUDE:
|
||
|
if (xi < SCSI_NCR_MAX_EXCLUDES)
|
||
|
driver_setup.excludes[xi++] = val;
|
||
|
break;
|
||
|
case OPT_HOST_ID:
|
||
|
driver_setup.host_id = val;
|
||
|
break;
|
||
|
#ifdef SCSI_NCR_IARB_SUPPORT
|
||
|
case OPT_IARB:
|
||
|
driver_setup.iarb = val;
|
||
|
break;
|
||
|
#endif
|
||
|
default:
|
||
|
printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((cur = strchr(cur, ARG_SEP)) != NULL)
|
||
|
++cur;
|
||
|
}
|
||
|
#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*===================================================================
|
||
|
**
|
||
|
** Get device queue depth from boot command line.
|
||
|
**
|
||
|
**===================================================================
|
||
|
*/
|
||
|
#define DEF_DEPTH (driver_setup.default_tags)
|
||
|
#define ALL_TARGETS -2
|
||
|
#define NO_TARGET -1
|
||
|
#define ALL_LUNS -2
|
||
|
#define NO_LUN -1
|
||
|
|
||
|
static int device_queue_depth(int unit, int target, int lun)
|
||
|
{
|
||
|
int c, h, t, u, v;
|
||
|
char *p = driver_setup.tag_ctrl;
|
||
|
char *ep;
|
||
|
|
||
|
h = -1;
|
||
|
t = NO_TARGET;
|
||
|
u = NO_LUN;
|
||
|
while ((c = *p++) != 0) {
|
||
|
v = simple_strtoul(p, &ep, 0);
|
||
|
switch(c) {
|
||
|
case '/':
|
||
|
++h;
|
||
|
t = ALL_TARGETS;
|
||
|
u = ALL_LUNS;
|
||
|
break;
|
||
|
case 't':
|
||
|
if (t != target)
|
||
|
t = (target == v) ? v : NO_TARGET;
|
||
|
u = ALL_LUNS;
|
||
|
break;
|
||
|
case 'u':
|
||
|
if (u != lun)
|
||
|
u = (lun == v) ? v : NO_LUN;
|
||
|
break;
|
||
|
case 'q':
|
||
|
if (h == unit &&
|
||
|
(t == ALL_TARGETS || t == target) &&
|
||
|
(u == ALL_LUNS || u == lun))
|
||
|
return v;
|
||
|
break;
|
||
|
case '-':
|
||
|
t = ALL_TARGETS;
|
||
|
u = ALL_LUNS;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
p = ep;
|
||
|
}
|
||
|
return DEF_DEPTH;
|
||
|
}
|