| /****************************************************************************** |
| ** 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; |
| } |