*******************************************************************************
*/
+/*
+** This file contains definitions and code that the
+** sym53c8xx and ncr53c8xx drivers should share.
+** The sharing will be achieved in a further version
+** of the driver bundle. For now, only the ncr53c8xx
+** driver includes this file.
+*/
+
+/*==========================================================
+**
+** Hmmm... What complex some PCI-HOST bridges actually
+** are, despite the fact that the PCI specifications
+** are looking so smart and simple! ;-)
+**
+**==========================================================
+*/
+
+/*==========================================================
+**
+** Miscallaneous defines.
+**
+**==========================================================
+*/
+
+#define u_char unsigned char
+#define u_long unsigned long
+
+#ifndef bzero
+#define bzero(d, n) memset((d), 0, (n))
+#endif
+
+/*==========================================================
+**
+** assert ()
+**
+**==========================================================
+**
+** modified copy from 386bsd:/usr/include/sys/assert.h
+**
+**----------------------------------------------------------
+*/
+
+#define assert(expression) { \
+ if (!(expression)) { \
+ (void)panic( \
+ "assertion \"%s\" failed: file \"%s\", line %d\n", \
+ #expression, \
+ __FILE__, __LINE__); \
+ } \
+}
+
/*==========================================================
**
** Debugging tags
#define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
#endif
-static inline struct list_head *ncr_list_pop(struct list_head *head)
+/*==========================================================
+**
+** A la VMS/CAM-3 queue management.
+** Implemented from linux list management.
+**
+**==========================================================
+*/
+
+typedef struct xpt_quehead {
+ struct xpt_quehead *flink; /* Forward pointer */
+ struct xpt_quehead *blink; /* Backward pointer */
+} XPT_QUEHEAD;
+
+#define xpt_que_init(ptr) do { \
+ (ptr)->flink = (ptr); (ptr)->blink = (ptr); \
+} while (0)
+
+static inline void __xpt_que_add(struct xpt_quehead * new,
+ struct xpt_quehead * blink,
+ struct xpt_quehead * flink)
{
- if (!list_empty(head)) {
- struct list_head *elem = head->next;
+ flink->blink = new;
+ new->flink = flink;
+ new->blink = blink;
+ blink->flink = new;
+}
+
+static inline void __xpt_que_del(struct xpt_quehead * blink,
+ struct xpt_quehead * flink)
+{
+ flink->blink = blink;
+ blink->flink = flink;
+}
- list_del(elem);
- return elem;
+static inline int xpt_que_empty(struct xpt_quehead *head)
+{
+ return head->flink == head;
+}
+
+static inline void xpt_que_splice(struct xpt_quehead *list,
+ struct xpt_quehead *head)
+{
+ struct xpt_quehead *first = list->flink;
+
+ if (first != list) {
+ struct xpt_quehead *last = list->blink;
+ struct xpt_quehead *at = head->flink;
+
+ first->blink = head;
+ head->flink = first;
+
+ last->flink = at;
+ at->blink = last;
}
+}
+
+#define xpt_que_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+
+#define xpt_insque(new, pos) __xpt_que_add(new, pos, (pos)->flink)
+
+#define xpt_remque(el) __xpt_que_del((el)->blink, (el)->flink)
- return NULL;
+#define xpt_insque_head(new, head) __xpt_que_add(new, head, (head)->flink)
+
+static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
+{
+ struct xpt_quehead *elem = head->flink;
+
+ if (elem != head)
+ __xpt_que_del(head, elem->flink);
+ else
+ elem = NULL;
+ return elem;
}
+#define xpt_insque_tail(new, head) __xpt_que_add(new, (head)->blink, head)
+
+static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
+{
+ struct xpt_quehead *elem = head->blink;
+
+ if (elem != head)
+ __xpt_que_del(elem->blink, head);
+ else
+ elem = 0;
+ return elem;
+}
+
+
+/*==========================================================
+**
+** 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.: by controller).
+**
+**==========================================================
+*/
+
+spinlock_t DRIVER_SMP_LOCK = SPIN_LOCK_UNLOCKED;
+#define NCR_LOCK_DRIVER(flags) spin_lock_irqsave(&DRIVER_SMP_LOCK, flags)
+#define NCR_UNLOCK_DRIVER(flags) \
+ spin_unlock_irqrestore(&DRIVER_SMP_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(host, flags) \
+ spin_lock_irqsave((host)->host_lock, flags)
+#define NCR_UNLOCK_SCSI_DONE(host, flags) \
+ spin_unlock_irqrestore(((host)->host_lock), flags)
+
+/*==========================================================
+**
+** Memory mapped IO
+**
+** Since linux-2.1, we must use ioremap() to map the io
+** memory space and iounmap() to unmap it. This 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.
+**
+**==========================================================
+*/
+
#ifdef __sparc__
#include <asm/irq.h>
#endif
+#define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
+
+/*==========================================================
+**
+** 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.
+**
+**==========================================================
+*/
+
+#define UDELAY udelay
+#define MDELAY mdelay
+
/*==========================================================
**
** Simple power of two buddy-like allocator.
**==========================================================
*/
+#define __GetFreePages(flags, order) __get_free_pages(flags, order)
+
#define MEMO_SHIFT 4 /* 16 bytes minimum memory chunk */
#if PAGE_SIZE >= 8192
#define MEMO_PAGE_ORDER 0 /* 1 PAGE maximum */
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;
j = i;
while (!h[j].next) {
if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
- h[j].next = (m_link_s *)mp->getp(mp);
+ h[j].next = (m_link_s *) M_GETP();
if (h[j].next)
h[j].next->next = NULL;
break;
while (1) {
#ifdef MEMO_FREE_UNUSED
if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
- mp->freep(mp, a);
+ M_FREEP(a);
break;
}
#endif
}
}
-static spinlock_t ncr53c8xx_lock = SPIN_LOCK_UNLOCKED;
-
static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags)
{
void *p;
printk ("new %-10s[%4d] @%p.\n", name, size, p);
if (p)
- memset(p, 0, size);
+ bzero(p, size);
else if (uflags & MEMO_WARN)
printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size);
static m_addr_t ___mp0_getp(m_pool_s *mp)
{
- m_addr_t m = __get_free_pages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER);
+ m_addr_t m = GetPages();
if (m)
++mp->nump;
return m;
static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
{
- free_pages(m, MEMO_PAGE_ORDER);
+ FreePages(m);
--mp->nump;
}
m_pool_s *mp;
mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL");
if (mp) {
- memset(mp, 0, sizeof(*mp));
+ bzero(mp, sizeof(*mp));
mp->bush = bush;
mp->getp = ___dma_getp;
mp->freep = ___dma_freep;
struct m_pool *mp;
void *m = NULL;
- spin_lock_irqsave(&ncr53c8xx_lock, flags);
+ NCR_LOCK_DRIVER(flags);
mp = ___get_dma_pool(bush);
if (!mp)
mp = ___cre_dma_pool(bush);
m = __m_calloc(mp, size, name);
if (mp && !mp->nump)
___del_dma_pool(mp);
- spin_unlock_irqrestore(&ncr53c8xx_lock, flags);
+ NCR_UNLOCK_DRIVER(flags);
return m;
}
u_long flags;
struct m_pool *mp;
- spin_lock_irqsave(&ncr53c8xx_lock, flags);
+ NCR_LOCK_DRIVER(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);
+ NCR_UNLOCK_DRIVER(flags);
}
static m_addr_t __vtobus(m_bush_t bush, void *m)
m_vtob_s *vp = NULL;
m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK;
- spin_lock_irqsave(&ncr53c8xx_lock, flags);
+ NCR_LOCK_DRIVER(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);
+ NCR_UNLOCK_DRIVER(flags);
return vp ? vp->baddr + (((m_addr_t) m) - a) : 0;
}
#define __data_mapped SCp.phase
#define __data_mapping SCp.have_data_in
-static void __unmap_scsi_data(struct device *dev, struct scsi_cmnd *cmd)
+static void __unmap_scsi_data(struct device *dev, Scsi_Cmnd *cmd)
{
+ enum dma_data_direction dma_dir =
+ (enum dma_data_direction)scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
switch(cmd->__data_mapped) {
case 2:
- dma_unmap_sg(dev, cmd->buffer, cmd->use_sg,
- cmd->sc_data_direction);
+ dma_unmap_sg(dev, cmd->buffer, cmd->use_sg, dma_dir);
break;
case 1:
dma_unmap_single(dev, cmd->__data_mapping,
- cmd->request_bufflen,
- cmd->sc_data_direction);
+ cmd->request_bufflen, dma_dir);
break;
}
cmd->__data_mapped = 0;
}
-static u_long __map_scsi_single_data(struct device *dev, struct scsi_cmnd *cmd)
+static u_long __map_scsi_single_data(struct device *dev, Scsi_Cmnd *cmd)
{
dma_addr_t mapping;
+ enum dma_data_direction dma_dir =
+ (enum dma_data_direction)scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
if (cmd->request_bufflen == 0)
return 0;
mapping = dma_map_single(dev, cmd->request_buffer,
- cmd->request_bufflen,
- cmd->sc_data_direction);
+ cmd->request_bufflen, dma_dir);
cmd->__data_mapped = 1;
cmd->__data_mapping = mapping;
return mapping;
}
-static int __map_scsi_sg_data(struct device *dev, struct scsi_cmnd *cmd)
+static int __map_scsi_sg_data(struct device *dev, Scsi_Cmnd *cmd)
{
int use_sg;
+ enum dma_data_direction dma_dir =
+ (enum dma_data_direction)scsi_to_pci_dma_dir(cmd->sc_data_direction);
if (cmd->use_sg == 0)
return 0;
- use_sg = dma_map_sg(dev, cmd->buffer, cmd->use_sg,
- cmd->sc_data_direction);
+ use_sg = dma_map_sg(dev, cmd->buffer, cmd->use_sg, dma_dir);
cmd->__data_mapped = 2;
cmd->__data_mapping = use_sg;
return use_sg;
}
+static void __sync_scsi_data_for_cpu(struct device *dev, Scsi_Cmnd *cmd)
+{
+ enum dma_data_direction dma_dir =
+ (enum dma_data_direction)scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ switch(cmd->__data_mapped) {
+ case 2:
+ dma_sync_sg_for_cpu(dev, cmd->buffer, cmd->use_sg, dma_dir);
+ break;
+ case 1:
+ dma_sync_single_for_cpu(dev, cmd->__data_mapping,
+ cmd->request_bufflen, dma_dir);
+ break;
+ }
+}
+
+static void __sync_scsi_data_for_device(struct device *dev, Scsi_Cmnd *cmd)
+{
+ enum dma_data_direction dma_dir =
+ (enum dma_data_direction)scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+ switch(cmd->__data_mapped) {
+ case 2:
+ dma_sync_sg_for_device(dev, cmd->buffer, cmd->use_sg, dma_dir);
+ break;
+ case 1:
+ dma_sync_single_for_device(dev, cmd->__data_mapping,
+ cmd->request_bufflen, dma_dir);
+ break;
+ }
+}
+
+#define scsi_sg_dma_address(sc) sg_dma_address(sc)
+#define scsi_sg_dma_len(sc) sg_dma_len(sc)
+
#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)
#define sync_scsi_data_for_cpu(np, cmd) __sync_scsi_data_for_cpu(np->dev, cmd)
#define sync_scsi_data_for_device(np, cmd) __sync_scsi_data_for_device(np->dev, cmd)
+#define scsi_data_direction(cmd) (cmd->sc_data_direction)
+
/*==========================================================
**
** Driver setup.
#define bootverbose (np->verbose)
+/*===================================================================
+**
+** Utility routines that protperly return data through /proc FS.
+**
+**===================================================================
+*/
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
+struct info_str
+{
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->length)
+ len = info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer + info->pos, data, len);
+ info->pos += len;
+ }
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[81];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+#endif
+
/*===================================================================
**
** Driver setup from the boot command line