X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Fsym53c8xx_comm.h;h=6408020752cb8dadc32a86f97c4fcf5a748a986b;hb=9e1bf581d67d87a1d7fc0ea500729e3a03643a26;hp=7ca14db3475481aa3a2b462d92319f998c4dc655;hpb=8d40237c730b8be87c1b80a5d96b9c603fefa829;p=linux-2.6.git diff --git a/drivers/scsi/sym53c8xx_comm.h b/drivers/scsi/sym53c8xx_comm.h index 7ca14db34..640802075 100644 --- a/drivers/scsi/sym53c8xx_comm.h +++ b/drivers/scsi/sym53c8xx_comm.h @@ -54,6 +54,57 @@ ******************************************************************************* */ +/* +** 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 @@ -86,22 +137,170 @@ static int ncr_debug = SCSI_NCR_DEBUG_FLAGS; #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 #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. @@ -120,6 +319,8 @@ static inline struct list_head *ncr_list_pop(struct list_head *head) **========================================================== */ +#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 */ @@ -155,6 +356,10 @@ 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); +#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; @@ -180,7 +385,7 @@ static void *___m_alloc(m_pool_s *mp, int size) 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; @@ -229,7 +434,7 @@ static void ___m_free(m_pool_s *mp, void *ptr, int size) while (1) { #ifdef MEMO_FREE_UNUSED if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) { - mp->freep(mp, a); + M_FREEP(a); break; } #endif @@ -250,8 +455,6 @@ static void ___m_free(m_pool_s *mp, void *ptr, int size) } } -static spinlock_t ncr53c8xx_lock = SPIN_LOCK_UNLOCKED; - static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags) { void *p; @@ -262,7 +465,7 @@ static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags) 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); @@ -288,7 +491,7 @@ static void __m_free(m_pool_s *mp, void *ptr, int size, char *name) 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; @@ -296,7 +499,7 @@ static m_addr_t ___mp0_getp(m_pool_s *mp) static void ___mp0_freep(m_pool_s *mp, m_addr_t m) { - free_pages(m, MEMO_PAGE_ORDER); + FreePages(m); --mp->nump; } @@ -366,7 +569,7 @@ 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)); + bzero(mp, sizeof(*mp)); mp->bush = bush; mp->getp = ___dma_getp; mp->freep = ___dma_freep; @@ -394,7 +597,7 @@ static void *__m_calloc_dma(m_bush_t bush, int size, char *name) 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); @@ -402,7 +605,7 @@ static void *__m_calloc_dma(m_bush_t bush, int size, char *name) 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; } @@ -412,13 +615,13 @@ static void __m_free_dma(m_bush_t bush, void *m, int size, char *name) 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) @@ -429,14 +632,14 @@ 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; } @@ -455,59 +658,100 @@ static m_addr_t __vtobus(m_bush_t bush, void *m) #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. @@ -530,6 +774,58 @@ static struct ncr_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