📄 sym53c8xx.c
字号:
**** This wrapper allows to get rid of old kernel PCI interface ** and still allows to preserve linux-2.0 compatibilty.** In fact, it is mostly an incomplete emulation of the new ** PCI code for pre-2.2 kernels. When kernel-2.0 support ** will be dropped, we will just have to remove most of this ** code.*/#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)typedef struct pci_dev *pcidev_t;#define PCIDEV_NULL (0)#define PciBusNumber(d) (d)->bus->number#define PciDeviceFn(d) (d)->devfn#define PciVendorId(d) (d)->vendor#define PciDeviceId(d) (d)->device#define PciIrqLine(d) (d)->irq#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)static int __init pci_get_base_address(struct pci_dev *pdev, int index, u_long *base){ *base = pdev->resource[index].start; if ((pdev->resource[index].flags & 0x7) == 0x4) ++index; return ++index;}#elsestatic int __init pci_get_base_address(struct pci_dev *pdev, int index, u_long *base){ *base = pdev->base_address[index++]; if ((*base & 0x7) == 0x4) {#if BITS_PER_LONG > 32 *base |= (((u_long)pdev->base_address[index]) << 32);#endif ++index; } return index;}#endif#else /* Incomplete emulation of current PCI code for pre-2.2 kernels */typedef unsigned int pcidev_t;#define PCIDEV_NULL (~0u)#define PciBusNumber(d) ((d)>>8)#define PciDeviceFn(d) ((d)&0xff)#define __PciDev(busn, devfn) (((busn)<<8)+(devfn))#define pci_present pcibios_present#define pci_read_config_byte(d, w, v) \ pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)#define pci_read_config_word(d, w, v) \ pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)#define pci_read_config_dword(d, w, v) \ pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)#define pci_write_config_byte(d, w, v) \ pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)#define pci_write_config_word(d, w, v) \ pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)#define pci_write_config_dword(d, w, v) \ pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)static pcidev_t __initpci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev){ static unsigned short pci_index; int retv; unsigned char bus_number, device_fn; if (prev == PCIDEV_NULL) pci_index = 0; else ++pci_index; retv = pcibios_find_device (vendor, device, pci_index, &bus_number, &device_fn); return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);}static u_short __init PciVendorId(pcidev_t dev){ u_short vendor_id; pci_read_config_word(dev, PCI_VENDOR_ID, &vendor_id); return vendor_id;}static u_short __init PciDeviceId(pcidev_t dev){ u_short device_id; pci_read_config_word(dev, PCI_DEVICE_ID, &device_id); return device_id;}static u_int __init PciIrqLine(pcidev_t dev){ u_char irq; pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); return irq;}static int __init pci_get_base_address(pcidev_t dev, int offset, u_long *base){ u_int32 tmp; pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp); *base = tmp; offset += sizeof(u_int32); if ((tmp & 0x7) == 0x4) {#if BITS_PER_LONG > 32 pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp); *base |= (((u_long)tmp) << 32);#endif offset += sizeof(u_int32); } return offset;}#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) *//*==========================================================**** 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_IC (0x0800)/*** 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#endif/*** SMP threading.**** Assuming that SMP systems are generally high end systems and may ** use several SCSI adapters, we are using one lock per controller ** instead of some global one. For the moment (linux-2.1.95), driver's ** entry points are called with the 'io_request_lock' lock held, so:** - We are uselessly loosing a couple of micro-seconds to lock the ** controller data structure.** - But the driver is not broken by design for SMP and so can be ** more resistant to bugs or bad changes in the IO sub-system code.** - A small advantage could be that the interrupt code is grained as ** wished (e.g.: threaded by controller).*/#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)spinlock_t sym53c8xx_lock = SPIN_LOCK_UNLOCKED;#define NCR_LOCK_DRIVER(flags) spin_lock_irqsave(&sym53c8xx_lock, flags)#define NCR_UNLOCK_DRIVER(flags) spin_unlock_irqrestore(&sym53c8xx_lock,flags)#define NCR_INIT_LOCK_NCB(np) spin_lock_init(&np->smp_lock);#define NCR_LOCK_NCB(np, flags) spin_lock_irqsave(&np->smp_lock, flags)#define NCR_UNLOCK_NCB(np, flags) spin_unlock_irqrestore(&np->smp_lock, flags)#define NCR_LOCK_SCSI_DONE(np, flags) \ spin_lock_irqsave(&io_request_lock, flags)#define NCR_UNLOCK_SCSI_DONE(np, flags) \ spin_unlock_irqrestore(&io_request_lock, flags)#else#define NCR_LOCK_DRIVER(flags) do { save_flags(flags); cli(); } while (0)#define NCR_UNLOCK_DRIVER(flags) do { restore_flags(flags); } while (0)#define NCR_INIT_LOCK_NCB(np) do { } while (0)#define NCR_LOCK_NCB(np, flags) do { save_flags(flags); cli(); } while (0)#define NCR_UNLOCK_NCB(np, flags) do { restore_flags(flags); } while (0)#define NCR_LOCK_SCSI_DONE(np, flags) do {;} while (0)#define NCR_UNLOCK_SCSI_DONE(np, flags) do {;} while (0)#endif/*** 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.*/#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)#define ioremap vremap#define iounmap vfree#endif#ifdef __sparc__# include <asm/irq.h># define pcivtobus(p) bus_dvma_to_mem(p)# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))#elif defined(__alpha__)# define pcivtobus(p) ((p) & 0xfffffffful)# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))#elif defined(CONFIG_PPC)# define pcivtobus(p) phys_to_bus(p)# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))#else /* others */# define pcivtobus(p) (p)# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))#endif#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTEDstatic u_long __init 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; u_long page_remapped = (u_long) ioremap(page_base, page_offs+size); return page_remapped? (page_remapped + page_offs) : 0UL;}static void __init unmap_pci_mem(u_long vaddr, u_long size){ if (vaddr) iounmap((void *) (vaddr & PAGE_MASK));}#endif /* not def SCSI_NCR_PCI_MEM_NOT_SUPPORTED *//*** Insert a delay in micro-seconds and milli-seconds.** -------------------------------------------------** Under Linux, udelay() is restricted to delay < 1 milli-second.** In fact, it generally works for up to 1 second delay.** Since 2.1.105, the mdelay() function is provided for delays ** in milli-seconds.** Under 2.0 kernels, udelay() is an inline function that is very ** inaccurate on Pentium processors.*/#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)#define UDELAY udelay#define MDELAY mdelay#elsestatic void UDELAY(long us) { udelay(us); }static void MDELAY(long ms) { while (ms--) UDELAY(1000); }#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).*/#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)#define __GetFreePages(flags, order) __get_free_pages(flags, order)#else#define __GetFreePages(flags, order) __get_free_pages(flags, order, 0)#endif#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 pcidev_t m_bush_t; /* Something that addresses DMAable */typedef struct m_link { /* Link between free memory chunks */ struct m_link *next;} m_link_s;#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPINGtypedef 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)#endiftypedef struct m_pool { /* Memory pool of a given kind */#ifdef SCSI_NCR_DYNAMIC_DMA_MAPPING m_bush_t bush; m_addr_t (*getp)(struct m_pool *); void (*freep)(struct m_pool *, m_addr_t);#define M_GETP() mp->getp(mp)#define M_FREEP(p) mp->freep(mp, p)#define GetPages() __GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)#define FreePages(p) free_pages(p, MEMO_PAGE_ORDER) int nump; m_vtob_s *(vtob[VTOB_HASH_SIZE]); struct m_pool *next;#else#define M_GETP() __GetFreePages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER)#define M_FREEP(p) free_pages(p, MEMO_PAGE_ORDER)#endif /* SCSI_NCR_DYNAMIC_DMA_MAPPING */ 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 0; 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 *) M_GETP(); if (h[j].next) h[j].next->next = 0; 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 = 0; } }#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)) { M_FREEP(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 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) bzero(p, 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. */#ifndef SCSI_NCR_DYNAMIC_DMA_MAPPINGstatic m_pool_s mp0;#elsestatic m_addr_t ___mp0_getp(m_pool_s *mp){ m_addr_t m = GetPages(); if (m) ++mp->nump; return m;}static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -