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 + -
显示快捷键?