📄 ncr53c8xx.c
字号:
/******************************************************************************** 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@club-internet.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).**********************************************************************************//*** 30 January 1998, version 2.5f.1**** Supported SCSI-II features:** Synchronous negotiation** Wide negotiation (depends on the NCR Chip)** Enable disconnection** Tagged command queuing** Parity checking** Etc...**** Supported NCR chips:** 53C810 (8 bits, Fast SCSI-2, no rom BIOS) ** 53C815 (8 bits, Fast SCSI-2, on board rom BIOS)** 53C820 (Wide, Fast SCSI-2, no rom BIOS)** 53C825 (Wide, Fast SCSI-2, on board rom BIOS)** 53C860 (8 bits, Fast 20, no rom BIOS)** 53C875 (Wide, Fast 20, on board rom BIOS)** 53C895 (Wide, Fast 40, on board rom BIOS)**** Other features:** Memory mapped IO (linux-1.3.X and above only)** Module** Shared IRQ (since linux-1.3.72)*/#define SCSI_NCR_DEBUG_FLAGS (0) #define NCR_GETCC_WITHMSG/*==========================================================**** Include files****==========================================================*/#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))#ifdef MODULE#include <linux/module.h>#endif#include <asm/dma.h>#include <asm/io.h>#include <asm/system.h>#include <linux/delay.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/bios32.h>#include <linux/pci.h>#include <linux/string.h>#include <linux/malloc.h>#include <linux/mm.h>#include <linux/ioport.h>#include <linux/time.h>#include <linux/timer.h>#include <linux/stat.h>#include <linux/version.h>#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)#include <linux/blk.h>#else#include "../block/blk.h"#endif#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)#include <linux/init.h>#else#ifndef __initdata#define __initdata#endif#ifndef __initfunc#define __initfunc(__arginit) __arginit#endif#endif#include "scsi.h"#include "hosts.h"#include "constants.h"#include "sd.h"#include <linux/types.h>/*** Define the BSD style u_int32 type*/typedef u32 u_int32;#include "ncr53c8xx.h"/*==========================================================**** Configuration and Debugging****==========================================================*//*** SCSI address of this device.** The boot routines should have set it.** If not, use this.*/#ifndef SCSI_NCR_MYADDR#define SCSI_NCR_MYADDR (7)#endif/*** The maximum number of tags per logic unit.** Used only for disk devices that support tags.*/#ifndef SCSI_NCR_MAX_TAGS#define SCSI_NCR_MAX_TAGS (4)#endif/*** Number of targets supported by the driver.** n permits target numbers 0..n-1.** Default is 7, meaning targets #0..#6.** #7 .. is myself.*/#ifdef SCSI_NCR_MAX_TARGET#define MAX_TARGET (SCSI_NCR_MAX_TARGET)#else#define MAX_TARGET (16)#endif/*** Number of logic units supported by the driver.** n enables logic unit numbers 0..n-1.** The common SCSI devices require only** one lun, so take 1 as the default.*/#ifdef SCSI_NCR_MAX_LUN#define MAX_LUN SCSI_NCR_MAX_LUN#else#define MAX_LUN (1)#endif/*** Asynchronous pre-scaler (ns). Shall be 40*/ #ifndef SCSI_NCR_MIN_ASYNC#define SCSI_NCR_MIN_ASYNC (40)#endif/*** The maximum number of jobs scheduled for starting.** There should be one slot per target, and one slot** for each tag of each target in use.** The calculation below is actually quite silly ...*/#ifdef SCSI_NCR_CAN_QUEUE#define MAX_START (SCSI_NCR_CAN_QUEUE + 4)#else#define MAX_START (MAX_TARGET + 7 * SCSI_NCR_MAX_TAGS)#endif/*** The maximum number of segments a transfer is split into.*/#define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)/*** Io mapped or memory mapped.*/#if defined(SCSI_NCR_IOMAPPED)#define NCR_IOMAPPED#endif/*** other*/#define NCR_SNOOP_TIMEOUT (1000000)/*==========================================================**** Defines for Linux.**** Linux and Bsd kernel functions are quite different.** These defines allow a minimum change of the original** code.****==========================================================*/ /* ** Obvious definitions */#define printf printk#define u_char unsigned char#define u_short unsigned short#define u_int unsigned int#define u_long unsigned longtypedef u_long vm_offset_t;typedef int vm_size_t;#define bcopy(s, d, n) memcpy((d), (s), (n))#define bzero(d, n) memset((d), 0, (n))#ifndef offsetof#define offsetof(t, m) ((size_t) (&((t *)0)->m))#endif/*** Address translation**** On Linux 1.3.X, virt_to_bus() must be used to translate** virtual memory addresses of the kernel data segment into** IO bus adresses.** On i386 architecture, IO bus addresses match the physical** addresses. But on other architectures they can be different.** In the original Bsd driver, vtophys() is called to translate** data addresses to IO bus addresses. In order to minimize** change, I decide to define vtophys() as virt_to_bus().*/#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)#define vtophys(p) virt_to_bus(p)/*** Memory mapped IO**** Since linux-2.1, we must use ioremap() to map the io memory space.** iounmap() to unmap it. That allows portability.** Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater ** than the highest physical memory address to kernel virtual pages with ** vremap() / vfree(). That was not portable but worked with i386 ** architecture.*/#ifndef NCR_IOMAPPED__initfunc(static vm_offset_t remap_pci_mem(u_long base, u_long size)){ u_long page_base = ((u_long) base) & PAGE_MASK; u_long page_offs = ((u_long) base) - page_base;#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) u_long page_remapped = (u_long) ioremap(page_base, page_offs+size);#else u_long page_remapped = (u_long) vremap(page_base, page_offs+size);#endif return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL);}__initfunc(static void unmap_pci_mem(vm_offset_t vaddr, u_long size)){ if (vaddr)#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) iounmap((void *) (vaddr & PAGE_MASK));#else vfree((void *) (vaddr & PAGE_MASK));#endif}#endif /* !NCR_IOMAPPED */#else /* linux-1.2.13 *//*** Linux 1.2.X assumes that addresses (virtual, physical, bus)** are the same.**** I have not found how to do MMIO. It seems that only processes can** map high physical pages to virtual (Xservers can do MMIO).*/#define vtophys(p) ((u_long) (p))#endif/*** Insert a delay in micro-seconds.*/static void DELAY(long us){ for (;us>1000;us-=1000) udelay(1000); if (us) udelay(us);}/*** Internal data structure allocation.**** Linux scsi memory poor pool is adjusted for the need of** middle-level scsi driver.** We allocate our control blocks in the kernel memory pool** to avoid scsi pool shortage.** I notice that kmalloc() returns NULL during host attach under** Linux 1.2.13. But this ncr driver is reliable enough to** accomodate with this joke.**** kmalloc() only ensure 8 bytes boundary alignment.** The NCR need better alignment for cache line bursting.** The global header is moved betewen the NCB and CCBs and need ** origin and destination addresses to have same lower four bits.**** We use 32 boundary alignment for NCB and CCBs and offset multiple ** of 32 for global header fields. That's too much but at least enough.*/#define ALIGN_SIZE(shift) (1UL << shift)#define ALIGN_MASK(shift) (~(ALIGN_SIZE(shift)-1))#define NCB_ALIGN_SHIFT 5#define CCB_ALIGN_SHIFT 5#define LCB_ALIGN_SHIFT 5#define SCR_ALIGN_SHIFT 5#define NCB_ALIGN_SIZE ALIGN_SIZE(NCB_ALIGN_SHIFT)#define NCB_ALIGN_MASK ALIGN_MASK(NCB_ALIGN_SHIFT)#define CCB_ALIGN_SIZE ALIGN_SIZE(CCB_ALIGN_SHIFT)#define CCB_ALIGN_MASK ALIGN_MASK(CCB_ALIGN_SHIFT)#define SCR_ALIGN_SIZE ALIGN_SIZE(SCR_ALIGN_SHIFT)#define SCR_ALIGN_MASK ALIGN_MASK(SCR_ALIGN_SHIFT)static void *m_alloc(int size, int a_shift){ u_long addr; void *ptr; u_long a_size, a_mask; if (a_shift < 3) a_shift = 3; a_size = ALIGN_SIZE(a_shift); a_mask = ALIGN_MASK(a_shift); ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC); if (ptr) { addr = (((u_long) ptr) + a_size) & a_mask; *((void **) (addr - sizeof(void *))) = ptr; ptr = (void *) addr; } return ptr;}#ifdef MODULEstatic void m_free(void *ptr, int size){ u_long addr; if (ptr) { addr = (u_long) ptr; ptr = *((void **) (addr - sizeof(void *))); kfree(ptr); }}#endif/*** Transfer direction**** Low-level scsi drivers under Linux do not receive the expected ** data transfer direction from upper scsi drivers.** The driver will only check actual data direction for common ** scsi opcodes. Other ones may cause problem, since they may ** depend on device type or be vendor specific.** I would prefer to never trust the device for data direction, ** but that is not possible.**** The original driver requires the expected direction to be known.** The Linux version of the driver has been enhanced in order to ** be able to transfer data in the direction choosen by the target. */#define XferNone 0#define XferIn 1#define XferOut 2#define XferBoth 3static int guess_xfer_direction(int opcode);/*** Head of list of NCR boards**** For kernel version < 1.3.70, host is retrieved by its irq level.** For later kernels, the internal host control block address ** (struct ncb) is used as device id parameter of the irq stuff.*/static struct Scsi_Host *first_host = NULL;static Scsi_Host_Template *the_template = NULL; /*** /proc directory entry and proc_info function*/#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)struct proc_dir_entry proc_scsi_ncr53c8xx = { PROC_SCSI_NCR53C8XX, 9, "ncr53c8xx", S_IFDIR | S_IRUGO | S_IXUGO, 2};# ifdef SCSI_NCR_PROC_INFO_SUPPORTint ncr53c8xx_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int func);# endif#endif/*** Table of target capabilities.**** This bitmap is anded with the byte 7 of inquiry data on completion of** INQUIRY command.** The driver never see zeroed bits and will ignore the corresponding** capabilities of the target.*/static struct { unsigned char and_map[MAX_TARGET];} target_capabilities[SCSI_NCR_MAX_HOST] = { NCR53C8XX_TARGET_CAPABILITIES };/*** Driver setup.**** This structure is initialized from linux config options.** It can be overridden at boot-up by the boot command line.*/struct ncr_driver_setup { unsigned master_parity : 1; unsigned scsi_parity : 1; unsigned disconnection : 1; unsigned special_features : 2; unsigned ultra_scsi : 2; unsigned force_sync_nego: 1; unsigned reverse_probe: 1; unsigned pci_fix_up: 4; u_char use_nvram; u_char verbose; u_char default_tags; u_short default_sync; u_short debug; u_char burst_max; u_char led_pin; u_char max_wide; u_char settle_delay; u_char diff_support; u_char irqm; u_char bus_check;};static struct ncr_driver_setup driver_setup = SCSI_NCR_DRIVER_SETUP;#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORTstatic struct ncr_driver_setup driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;#ifdef MODULEchar *ncr53c8xx = 0; /* command line passed by insmod */#endif#endif/*** Other Linux definitions*/#define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist);#endif#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);#elsestatic void ncr53c8xx_intr(int irq, struct pt_regs * regs);#endifstatic void ncr53c8xx_timeout(unsigned long np);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -