ncr53c8xx.c
来自「linux 内核源代码」· C语言 代码 · 共 2,376 行 · 第 1/5 页
C
2,376 行
/******************************************************************************** Device driver for the PCI-SCSI NCR538XX controller family.**** 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.****-----------------------------------------------------------------------------**** This driver has been ported to Linux from the FreeBSD NCR53C8XX driver** and is currently maintained 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>**** And has been ported to NetBSD by** Charles M. Hannum <mycroft@gnu.ai.mit.edu>****-----------------------------------------------------------------------------**** Brief history**** December 10 1995 by Gerard Roudier:** Initial port to Linux.**** June 23 1996 by Gerard Roudier:** Support for 64 bits architectures (Alpha).**** November 30 1996 by Gerard Roudier:** Support for Fast-20 scsi.** Support for large DMA fifo and 128 dwords bursting.**** February 27 1997 by Gerard Roudier:** Support for Fast-40 scsi.** Support for on-Board RAM.**** May 3 1997 by Gerard Roudier:** Full support for scsi scripts instructions pre-fetching.**** May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:** Support for NvRAM detection and reading.**** August 18 1997 by Cort <cort@cs.nmt.edu>:** Support for Power/PC (Big Endian).**** June 20 1998 by Gerard Roudier** Support for up to 64 tags per lun.** O(1) everywhere (C and SCRIPTS) for normal cases.** Low PCI traffic for command handling when on-chip RAM is present.** Aggressive SCSI SCRIPTS optimizations.**** 2005 by Matthew Wilcox and James Bottomley** PCI-ectomy. This driver now supports only the 720 chip (see the** NCR_Q720 and zalon drivers for the bus probe logic).**********************************************************************************//*** Supported SCSI-II features:** Synchronous negotiation** Wide negotiation (depends on the NCR Chip)** Enable disconnection** Tagged command queuing** Parity checking** Etc...**** Supported NCR/SYMBIOS chips:** 53C720 (Wide, Fast SCSI-2, intfly problems)*//* Name and version of the driver */#define SCSI_NCR_DRIVER_NAME "ncr53c8xx-3.4.3g"#define SCSI_NCR_DEBUG_FLAGS (0)#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/spinlock.h>#include <linux/stat.h>#include <linux/string.h>#include <linux/time.h>#include <linux/timer.h>#include <linux/types.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/system.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_dbg.h>#include <scsi/scsi_device.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_spi.h>#include "ncr53c8xx.h"#define NAME53C8XX "ncr53c8xx"/*==========================================================**** 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_SUPPORTstatic int ncr_debug = SCSI_NCR_DEBUG_FLAGS; #define DEBUG_FLAGS ncr_debug#else #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS#endifstatic 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;}/*==========================================================**** 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 abstraction, 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);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?