Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de)
- Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu)
- (Some codes are stolen from proposed v4l2 videodev.c
- of Bill Dirks <dirks@rendition.com>)
+ Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu)
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
static unsigned char saa_status(int, struct planb *);
static void saa_set(unsigned char, unsigned char, struct planb *);
static void saa_init_regs(struct planb *);
-static void * rvmalloc(unsigned long);
-static void rvfree(void *, unsigned long);
-static unsigned long vmalloc_to_bus(void *);
-static unsigned long vmalloc_to_phys(void *);
-static int fbuffer_alloc(struct planb *);
+static int grabbuf_alloc(struct planb *);
static int vgrab(struct planb *, struct video_mmap *);
static void add_clip(struct planb *, struct video_clip *);
static void fill_cmd_buff(struct planb *);
/* Memory management functions */
/*******************************/
-static void * rvmalloc(unsigned long size)
+static int grabbuf_alloc(struct planb *pb)
{
- void *mem, *memptr;
- unsigned long page;
-
- mem=vmalloc(size);
- if (mem)
- {
- memset(mem, 0, size); /* Clear the ram out, leave no junk */
- memptr = mem;
- while (size > 0)
- {
- page = vmalloc_to_phys(memptr);
- mem_map_reserve(MAP_NR(phys_to_virt(page)));
- memptr+=PAGE_SIZE;
- size-=PAGE_SIZE;
- }
- }
- return mem;
-}
+ int i, npage;
-static void rvfree(void * mem, unsigned long size)
-{
- void *memptr;
- unsigned long page;
-
- if (mem)
- {
- memptr = mem;
- while (size > 0)
- {
- page = vmalloc_to_phys(memptr);
- mem_map_unreserve(MAP_NR(phys_to_virt(page)));
- memptr += PAGE_SIZE;
- size-=PAGE_SIZE;
- }
- vfree(mem);
+ npage = MAX_GBUFFERS * ((PLANB_MAX_FBUF / PAGE_SIZE + 1)
+#ifndef PLANB_GSCANLINE
+ + MAX_LNUM
+#endif /* PLANB_GSCANLINE */
+ );
+ if ((pb->rawbuf = (unsigned char**) kmalloc (npage
+ * sizeof(unsigned long), GFP_KERNEL)) == 0)
+ return -ENOMEM;
+ for (i = 0; i < npage; i++) {
+ pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL
+ |GFP_DMA, 0);
+ if (!pb->rawbuf[i])
+ break;
+ set_bit(PG_reserved, &mem_map[MAP_NR(pb->rawbuf[i])].flags);
}
-}
-
-/* Useful for using vmalloc()ed memory as DMA target */
-static unsigned long vmalloc_to_bus(void *virt)
-{
- pgd_t *pgd;
- pmd_t *pmd;
- pte_t *pte;
- unsigned long a = (unsigned long)virt;
-
- if (pgd_none(*(pgd = pgd_offset(current->mm, a))) ||
- pmd_none(*(pmd = pmd_offset(pgd, a))) ||
- pte_none(*(pte = pte_offset(pmd, a))))
- return 0;
- return virt_to_bus((void *)pte_page(*pte))
- + (a & (PAGE_SIZE - 1));
-}
-
-static unsigned long vmalloc_to_phys(void *virt) {
- return virt_to_phys(bus_to_virt(vmalloc_to_bus(virt)));
-}
-
-/*
- * Create the giant waste of buffer space we need for now
- * until we get DMA to user space sorted out (probably 2.3.x)
- *
- * We only create this as and when someone uses mmap
- */
-
-static int fbuffer_alloc(struct planb *pb)
-{
- if(!pb->fbuffer)
- pb->fbuffer=(unsigned char *) rvmalloc(MAX_GBUFFERS
- * PLANB_MAX_FBUF);
- else
- printk(KERN_ERR "PlanB: Double alloc of fbuffer!\n");
- if(!pb->fbuffer)
+ if (i-- < npage) {
+ printk(KERN_DEBUG "PlanB: init_grab: grab buffer not allocated\n");
+ for (; i > 0; i--) {
+ clear_bit(PG_reserved,
+ &mem_map[MAP_NR(pb->rawbuf[i])].flags);
+ free_pages((unsigned long)pb->rawbuf[i], 0);
+ }
+ kfree(pb->rawbuf);
return -ENOBUFS;
+ }
+ pb->rawbuf_size = npage;
return 0;
}
+ PLANB_DUMMY);
pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS);
- pb->fbuffer = (unsigned char *)rvmalloc(MAX_GBUFFERS * PLANB_MAX_FBUF);
- if (!pb->fbuffer) {
- kfree(pb->priv_space);
- return -ENOMEM;
- }
+ pb->rawbuf = NULL;
+ pb->rawbuf_size = 0;
pb->grabbing = 0;
for (i = 0; i < MAX_GBUFFERS; i++) {
pb->frame_stat[i] = GBUFFER_UNUSED;
#ifndef PLANB_GSCANLINE
pb->lsize[i] = 0;
pb->lnum[i] = 0;
- pb->l_fr_addr[i]=(unsigned char *)rvmalloc(PAGE_SIZE*MAX_LNUM);
- if (!pb->l_fr_addr[i]) {
- int j;
- kfree(pb->priv_space);
- rvfree((void *)pb->fbuffer, MAX_GBUFFERS
- * PLANB_MAX_FBUF);
- for(j = 0; j < i; j++)
- rvfree((void *)pb->l_fr_addr[j], PAGE_SIZE
- * MAX_LNUM);
- return -ENOMEM;
- }
#endif /* PLANB_GSCANLINE */
}
pb->gcount = 0;
pb->suspend = 0;
pb->last_fr = -999;
pb->prev_last_fr = -999;
- return 0;
+
+ /* Reset DMA controllers */
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+
+ return 0;
}
static void planb_prepare_close(struct planb *pb)
{
-#ifndef PLANB_GSCANLINE
int i;
-#endif
/* make sure the dma's are idle */
planb_dbdma_stop(&pb->planb_base->ch2);
pb->priv_space = 0;
pb->cmd_buff_inited = 0;
}
- if(pb->fbuffer)
- rvfree((void *)pb->fbuffer, MAX_GBUFFERS*PLANB_MAX_FBUF);
- pb->fbuffer = NULL;
-#ifndef PLANB_GSCANLINE
- for(i = 0; i < MAX_GBUFFERS; i++) {
- if(pb->l_fr_addr[i])
- rvfree((void *)pb->l_fr_addr[i], PAGE_SIZE * MAX_LNUM);
- pb->l_fr_addr[i] = NULL;
+ if(pb->rawbuf) {
+ for (i = 0; i < pb->rawbuf_size; i++) {
+ clear_bit(PG_reserved,
+ &mem_map[MAP_NR(pb->rawbuf[i])].flags);
+ free_pages((unsigned long)pb->rawbuf[i], 0);
+ }
+ kfree(pb->rawbuf);
}
-#endif /* PLANB_GSCANLINE */
+ pb->rawbuf = NULL;
}
/*****************************/
0,
0,
};
-#define PLANB_PALETTE_MAX 15
-#define SWAP4(x) (((x>>24) & 0x000000ff) |\
- ((x>>8) & 0x0000ff00) |\
- ((x<<8) & 0x00ff0000) |\
- ((x<<24) & 0xff000000))
+#define PLANB_PALETTE_MAX 15
static inline int overlay_is_active(struct planb *pb)
{
unsigned int fr = mp->frame;
unsigned int format;
- if(pb->fbuffer==NULL) {
- if(fbuffer_alloc(pb))
- return -ENOBUFS;
+ if(pb->rawbuf==NULL) {
+ int err;
+ if((err=grabbuf_alloc(pb)))
+ return err;
}
IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing,
return -EINVAL;
planb_lock(pb);
- pb->gbuffer[fr] = (unsigned char *)(pb->fbuffer + PLANB_MAX_FBUF * fr);
if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] ||
format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) {
-#ifdef PLANB_GSCANLINE
int i;
-#else
+#ifndef PLANB_GSCANLINE
unsigned int osize = pb->gwidth[fr] * pb->gheight[fr]
* pb->gfmt[fr];
unsigned int nsize = mp->width * mp->height * format;
#ifndef PLANB_GSCANLINE
if(pb->gnorm_switch[fr])
nsize = 0;
- if(nsize < osize)
- memset((void *)(pb->gbuffer[fr] + nsize), 0,
- osize - nsize);
- memset((void *)pb->l_fr_addr[fr], 0, PAGE_SIZE * pb->lnum[fr]);
+ if (nsize < osize) {
+ for(i = pb->gbuf_idx[fr]; osize > 0; i++) {
+ memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
+ osize -= PAGE_SIZE;
+ }
+ }
+ for(i = pb->l_fr_addr_idx[fr]; i < pb->l_fr_addr_idx[fr]
+ + pb->lnum[fr]; i++)
+ memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
#else
/* XXX TODO */
/*
unsigned long base;
#endif
unsigned long jump;
- unsigned char *vaddr;
+ int pagei;
volatile struct dbdma_cmd *c1;
volatile struct dbdma_cmd *jump_addr;
/* even field data: */
- vaddr = pb->gbuffer[fr];
+ pagei = pb->gbuf_idx[fr];
#ifdef PLANB_GSCANLINE
for (i = 0; i < nlines; i += stepsize) {
tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
- vmalloc_to_bus(vaddr + i * scanline), jump);
+ virt_to_bus(pb->rawbuf[pagei
+ + i * scanline / PAGE_SIZE]), jump);
}
#else
i = 0;
do {
int j;
- base = vmalloc_to_bus((void*)vaddr);
+ base = virt_to_bus(pb->rawbuf[pagei]);
nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base
+ count * nlpp * stepsize + leftover1, jump);
} else {
- pb->l_to_addr[fr][pb->lnum[fr]] = vaddr + count * nlpp
- * stepsize + leftover1;
+ pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
+ + count * nlpp * stepsize + leftover1;
+ pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
+ pb->l_to_next_size[fr][pb->lnum[fr]] = count - lov0;
tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
- vmalloc_to_bus(pb->l_fr_addr[fr] + PAGE_SIZE
- * pb->lnum[fr]), jump);
+ virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
+ + pb->lnum[fr]]), jump);
if(++pb->lnum[fr] > MAX_LNUM)
pb->lnum[fr]--;
}
i += stepsize;
}
}
- vaddr += PAGE_SIZE;
+ pagei++;
} while(i < nlines);
tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
c1 = jump_addr;
#ifdef PLANB_GSCANLINE
for (i = 1; i < nlines; i += stepsize) {
tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
- vmalloc_to_bus(vaddr + i * scanline), jump);
+ virt_to_bus(pb->rawbuf[pagei
+ + i * scanline / PAGE_SIZE]), jump);
}
#else
i = 1;
leftover1 = 0;
- vaddr = pb->gbuffer[fr];
+ pagei = pb->gbuf_idx[fr];
if(nlines <= 1)
goto skip;
do {
int j;
- base = vmalloc_to_bus((void*)vaddr);
+ base = virt_to_bus(pb->rawbuf[pagei]);
nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
if(leftover1 >= count) {
tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
leftover1 = 0;
else {
if(lov0 > count) {
- pb->l_to_addr[fr][pb->lnum[fr]] = vaddr + count
- * (nlpp * stepsize + 1) + leftover1;
+ pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
+ + count * (nlpp * stepsize + 1) + leftover1;
+ pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
+ pb->l_to_next_size[fr][pb->lnum[fr]] = count * stepsize
+ - lov0;
tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
- vmalloc_to_bus(pb->l_fr_addr[fr] + PAGE_SIZE
- * pb->lnum[fr]), jump);
+ virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
+ + pb->lnum[fr]]), jump);
if(++pb->lnum[fr] > MAX_LNUM)
pb->lnum[fr]--;
i += stepsize;
leftover1 = count * stepsize - lov0;
}
}
- vaddr += PAGE_SIZE;
+ pagei++;
} while(i < nlines);
skip:
tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
cmd_tab_data_end:
tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat),
- (fr << 2) | PLANB_FRM_IRQ | PLANB_GEN_IRQ);
+ (fr << 9) | PLANB_FRM_IRQ | PLANB_GEN_IRQ);
/* stop it */
tab_cmd_dbdma(c1, DBDMA_STOP, 0);
IDEBUG("PlanB: planb_irq()\n");
/* get/clear interrupt status bits */
+ eieio();
stat = in_le32(&pb->planb_base->intr_stat);
astat = stat & pb->intr_mask;
- out_le32(&pb->planb_base->intr_stat, PLANB_IRQ_CMD_MASK
+ out_le32(&pb->planb_base->intr_stat, PLANB_FRM_IRQ
& ~astat & stat & ~PLANB_GEN_IRQ);
+ IDEBUG("PlanB: stat = %X, astat = %X\n", stat, astat);
if(astat & PLANB_FRM_IRQ) {
- unsigned int fr = stat >> 2;
+ unsigned int fr = stat >> 9;
#ifndef PLANB_GSCANLINE
int i;
#endif
#ifndef PLANB_GSCANLINE
IDEBUG("PlanB: %d * %d bytes are being copied over\n",
pb->lnum[fr], pb->lsize[fr]);
- for(i = 0; i < pb->lnum[fr]; i++)
- memcpy(pb->l_to_addr[fr][i], pb->l_fr_addr[fr]
- + PAGE_SIZE * i, pb->lsize[fr]);
+ for(i = 0; i < pb->lnum[fr]; i++) {
+ int first = pb->lsize[fr] - pb->l_to_next_size[fr][i];
+
+ memcpy(pb->l_to_addr[fr][i],
+ pb->rawbuf[pb->l_fr_addr_idx[fr] + i],
+ first);
+ memcpy(pb->rawbuf[pb->l_to_next_idx[fr][i]],
+ pb->rawbuf[pb->l_fr_addr_idx[fr] + i] + first,
+ pb->l_to_next_size[fr][i]);
+ }
#endif
pb->frame_stat[fr] = GBUFFER_DONE;
pb->grabbing--;
IDEBUG("PlanB: waiting for grab"
" done (%d)\n", i);
interruptible_sleep_on(&pb->capq);
+ if(signal_pending(current))
+ return -EINTR;
goto chk_grab;
case GBUFFER_DONE:
pb->frame_stat[i] = GBUFFER_UNUSED;
return 0;
}
-/*
- * This maps the vmalloced and reserved fbuffer to user space.
- *
- * FIXME:
- * - PAGE_READONLY should suffice!?
- * - remap_page_range is kind of inefficient for page by page remapping.
- * But e.g. pte_alloc() does not work in modules ... :-(
- */
-
static int planb_mmap(struct video_device *dev, const char *adr, unsigned long size)
{
- struct planb *pb=(struct planb *)dev;
- unsigned long start=(unsigned long) adr;
- unsigned long page;
- void *pos;
+ int i;
+ struct planb *pb = (struct planb *)dev;
+ unsigned long start = (unsigned long)adr;
- if (size>MAX_GBUFFERS*PLANB_MAX_FBUF)
+ if (size > MAX_GBUFFERS * PLANB_MAX_FBUF)
return -EINVAL;
- if (!pb->fbuffer)
- {
- if(fbuffer_alloc(pb))
- return -EINVAL;
+ if (!pb->rawbuf) {
+ int err;
+ if((err=grabbuf_alloc(pb)))
+ return err;
}
- pos = (void *)pb->fbuffer;
- while (size > 0)
- {
- page = vmalloc_to_phys(pos);
- if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
- start+=PAGE_SIZE;
- pos+=PAGE_SIZE;
- size-=PAGE_SIZE;
+ for (i = 0; i < pb->rawbuf_size; i++) {
+ if (remap_page_range(start, virt_to_phys((void *)pb->rawbuf[i]),
+ PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ if (size <= PAGE_SIZE)
+ break;
+ size -= PAGE_SIZE;
}
return 0;
}
{
unsigned char saa_rev;
int i, result;
+ unsigned long flags;
memset ((void *) &pb->win, 0, sizeof (struct planb_window));
/* Simple sanity check */
/* clear interrupt mask */
pb->intr_mask = PLANB_CLR_IRQ;
+ save_flags(flags); cli();
result = request_irq(pb->irq, planb_irq, 0, "PlanB", (void *)pb);
- if (result==-EINVAL) {
- printk(KERN_ERR "PlanB: Bad irq number (%d) or handler\n",
- (int)pb->irq);
- return result;
- }
- if (result==-EBUSY) {
- printk(KERN_ERR "PlanB: I don't know why, but IRQ %d busy\n",
- (int)pb->irq);
- return result;
- }
- if (result < 0)
- return result;
+ if (result < 0) {
+ if (result==-EINVAL)
+ printk(KERN_ERR "PlanB: Bad irq number (%d) "
+ "or handler\n", (int)pb->irq);
+ else if (result==-EBUSY)
+ printk(KERN_ERR "PlanB: I don't know why, "
+ "but IRQ %d is busy\n", (int)pb->irq);
+ restore_flags(flags);
+ return result;
+ }
+ disable_irq(pb->irq);
+ restore_flags(flags);
/* Now add the template and register the device unit. */
memcpy(&pb->video_dev,&planb_template,sizeof(planb_template));
pb->frame_stat=NULL;
pb->capq=NULL;
for(i=0; i<MAX_GBUFFERS; i++) {
- pb->gbuffer[i]=NULL;
+ pb->gbuf_idx[i] = PLANB_MAX_FBUF * i / PAGE_SIZE;
pb->gwidth[i]=0;
pb->gheight[i]=0;
pb->gfmt[i]=0;
pb->cap_cmd[i]=NULL;
#ifndef PLANB_GSCANLINE
- pb->l_fr_addr[i]=NULL;
+ pb->l_fr_addr_idx[i] = MAX_GBUFFERS * (PLANB_MAX_FBUF
+ / PAGE_SIZE + 1) + MAX_LNUM * i;
pb->lsize[i] = 0;
pb->lnum[i] = 0;
#endif
}
- pb->fbuffer=NULL;
+ pb->rawbuf=NULL;
pb->grabbing=0;
- /* clear interrupts */
+ /* enable interrupts */
out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
- /* set interrupt mask */
pb->intr_mask = PLANB_FRM_IRQ;
+ enable_irq(pb->irq);
if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER)<0)
return -1;
1.02.00.014 - Fix bug in tw_findcards() where AEN code could be lost.
Increase timeout in tw_aen_drain_queue() to 30 seconds.
1.02.00.015 - Re-write raw command post with data ioctl method.
+ 1.02.00.016 - Modified pci parity error handling/clearing from config space
+ during initialization.
+ 1.02.00.017 - Better handling of request sense opcode and sense information
+ for failed commands. Add tw_decode_sense().
+ Replace all mdelay()'s with scsi_sleep().
+ 1.02.00.018 - Revert mdelay's and scsi_sleep's, this caused problems on
+ some SMP systems.
+ 1.02.00.019 - Bump cmd_per_lun in SHT to 255 for better jbod performance.
+ Improve handling of errors in tw_interrupt().
+ Add handling/clearing of controller queue error.
+ Better alignment checking in tw_allocate_memory().
+ Cleanup tw_initialize_device_extension().
+ Empty stale responses before draining aen queue.
+ Fix tw_scsi_eh_abort() to not reset on every io abort.
+ Set can_queue in SHT to 255 to prevent hang from AEN.
*/
#include <linux/module.h>
};
/* Globals */
-char *tw_driver_version="1.02.00.015";
+char *tw_driver_version="1.02.00.019";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0;
}
tw_clear_attention_interrupt(tw_dev);
+ /* Empty response queue */
+ tw_empty_response_que(tw_dev);
+
/* Initialize command packet */
if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet virtual address.\n");
status_reg_value = inl(status_reg_addr);
if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
+ tw_decode_bits(tw_dev, status_reg_value, 0);
return 1;
}
if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
if (command_packet->status != 0) {
if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
/* Bad response */
- dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
- tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+ tw_decode_sense(tw_dev, request_id, 0);
return 1;
} else {
/* We know this is a 3w-1x00, and doesn't support aen's */
status_reg_value = inl(status_reg_addr);
if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
+ tw_decode_bits(tw_dev, status_reg_value, 1);
return 1;
}
if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
return 1;
}
- if ((u32)virt_addr % TW_ALIGNMENT) {
+ if ((u32)virt_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
kfree(virt_addr);
- printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
+ printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
return 1;
}
switch(which) {
status_reg_addr = tw_dev->registers.status_reg_addr;
status_reg_value = inl(status_reg_addr);
- if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value))
+ if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) {
+ tw_decode_bits(tw_dev, status_reg_value, 0);
return 1;
+ }
return 0;
} /* End tw_check_errors() */
+/* This function will clear all interrupts on the controller */
+void tw_clear_all_interrupts(TW_Device_Extension *tw_dev)
+{
+ u32 control_reg_addr, control_reg_value;
+
+ control_reg_addr = tw_dev->registers.control_reg_addr;
+ control_reg_value = TW_STATUS_VALID_INTERRUPT;
+ outl(control_reg_value, control_reg_addr);
+} /* End tw_clear_all_interrupts() */
+
/* This function will clear the attention interrupt */
void tw_clear_attention_interrupt(TW_Device_Extension *tw_dev)
{
}
} /* End tw_copy_mem_info() */
-/* This function will print readable messages from statsu register errors */
-void tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
+/* This function will print readable messages from status register errors */
+int tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value, int print_host)
{
+ char host[16];
+
dprintk(KERN_WARNING "3w-xxxx: tw_decode_bits()\n");
+
+ if (print_host)
+ sprintf(host, " scsi%d:", tw_dev->host->host_no);
+ else
+ host[0] = '\0';
+
switch (status_reg_value & TW_STATUS_UNEXPECTED_BITS) {
case TW_STATUS_PCI_PARITY_ERROR:
- printk(KERN_WARNING "3w-xxxx: PCI Parity Error: Reseat card, move card, or buggy device on the bus.\n");
+ printk(KERN_WARNING "3w-xxxx:%s PCI Parity Error: clearing.\n", host);
outl(TW_CONTROL_CLEAR_PARITY_ERROR, tw_dev->registers.control_reg_addr);
- pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PARITY_ERRORS);
break;
case TW_STATUS_MICROCONTROLLER_ERROR:
- printk(KERN_WARNING "3w-xxxx: Microcontroller Error.\n");
- break;
+ if (tw_dev->reset_print == 0) {
+ printk(KERN_WARNING "3w-xxxx:%s Microcontroller Error: clearing.\n", host);
+ tw_dev->reset_print = 1;
+ }
+ return 1;
case TW_STATUS_PCI_ABORT:
- printk(KERN_WARNING "3w-xxxx: PCI Abort: clearing.\n");
+ printk(KERN_WARNING "3w-xxxx:%s PCI Abort: clearing.\n", host);
outl(TW_CONTROL_CLEAR_PCI_ABORT, tw_dev->registers.control_reg_addr);
pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
break;
+ case TW_STATUS_QUEUE_ERROR:
+ printk(KERN_WARNING "3w-xxxx:%s Controller Queue Error: clearing.\n", host);
+ outl(TW_CONTROL_CLEAR_QUEUE_ERROR, tw_dev->registers.control_reg_addr);
+ break;
+ case TW_STATUS_SBUF_WRITE_ERROR:
+ printk(KERN_WARNING "3w-xxxx:%s SBUF Write Error: clearing.\n", host);
+ outl(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, tw_dev->registers.control_reg_addr);
+ break;
}
+
+ return 0;
} /* End tw_decode_bits() */
-/* This function will print readable messages from flags and status values */
-void tw_decode_error(TW_Device_Extension *tw_dev, unsigned char status, unsigned char flags, unsigned char unit)
+/* This function will return valid sense buffer information for failed cmds */
+int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense)
{
- dprintk(KERN_WARNING "3w-xxxx: tw_decode_error()\n");
- switch (status) {
- case 0xc7:
- switch (flags) {
- case 0x1b:
- printk(KERN_WARNING "3w-xxxx: scsi%d: Drive timeout on unit %d, check drive and drive cables.\n", tw_dev->host->host_no, unit);
- break;
- case 0x51:
- printk(KERN_WARNING "3w-xxxx: scsi%d: Unrecoverable drive error on unit %d, check/replace cabling, or possible bad drive.\n", tw_dev->host->host_no, unit);
- break;
- default:
- printk(KERN_WARNING "3w-xxxx: scsi%d: Controller error: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, status, flags, unit);
+ int i;
+ TW_Command *command;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_decode_sense()\n");
+ command = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Command failed: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, command->status, command->flags, command->byte3.unit);
+
+ /* Attempt to return intelligent sense information */
+ if (fill_sense) {
+ if ((command->status == 0xc7) || (command->status == 0xcb)) {
+ for (i=0;i<(sizeof(tw_sense_table)/sizeof(tw_sense_table[0]));i++) {
+ if (command->flags == tw_sense_table[i][0]) {
+
+ /* Valid bit and 'current errors' */
+ tw_dev->srb[request_id]->sense_buffer[0] = (0x1 << 7 | 0x70);
+
+ /* Sense key */
+ tw_dev->srb[request_id]->sense_buffer[2] = tw_sense_table[i][1];
+
+ /* Additional sense length */
+ tw_dev->srb[request_id]->sense_buffer[7] = 0xa; /* 10 bytes */
+
+ /* Additional sense code */
+ tw_dev->srb[request_id]->sense_buffer[12] = tw_sense_table[i][2];
+
+ /* Additional sense code qualifier */
+ tw_dev->srb[request_id]->sense_buffer[13] = tw_sense_table[i][3];
+
+ tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ return TW_ISR_DONT_RESULT; /* Special case for isr to not over-write result */
+ }
+ }
}
- break;
- default:
- printk(KERN_WARNING "3w-xxxx: scsi%d: Controller error: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, status, flags, unit);
+
+ /* If no table match, error so we get a reset */
+ return 1;
}
-} /* End tw_decode_error() */
+
+ return 0;
+} /* End tw_decode_sense() */
/* This function will disable interrupts on the controller */
void tw_disable_interrupts(TW_Device_Extension *tw_dev)
} /* End tw_disable_interrupts() */
/* This function will empty the response que */
-int tw_empty_response_que(TW_Device_Extension *tw_dev)
+void tw_empty_response_que(TW_Device_Extension *tw_dev)
{
u32 status_reg_addr, status_reg_value;
u32 response_que_addr, response_que_value;
status_reg_value = inl(status_reg_addr);
- if (tw_check_bits(status_reg_value)) {
- dprintk(KERN_WARNING "3w-xxxx: tw_empty_response_queue(): Unexpected bits 1.\n");
- tw_decode_bits(tw_dev, status_reg_value);
- return 1;
- }
-
while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
response_que_value = inl(response_que_addr);
status_reg_value = inl(status_reg_addr);
- if (tw_check_bits(status_reg_value)) {
- dprintk(KERN_WARNING "3w-xxxx: tw_empty_response_queue(): Unexpected bits 2.\n");
- tw_decode_bits(tw_dev, status_reg_value);
- return 1;
- }
}
- return 0;
} /* End tw_empty_response_que() */
/* This function will enable interrupts on the controller */
}
memset(tw_dev, 0, sizeof(TW_Device_Extension));
+ /* Save pci_dev struct to device extension */
+ tw_dev->tw_pci_dev = tw_pci_dev;
+
error = tw_initialize_device_extension(tw_dev);
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards);
tw_dev->registers.status_reg_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x4);
tw_dev->registers.command_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x8);
tw_dev->registers.response_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0xC);
- /* Save pci_dev struct to device extension */
- tw_dev->tw_pci_dev = tw_pci_dev;
/* Check for errors and clear them */
status_reg_value = inl(tw_dev->registers.status_reg_addr);
if (TW_STATUS_ERRORS(status_reg_value))
- tw_decode_bits(tw_dev, status_reg_value);
+ tw_decode_bits(tw_dev, status_reg_value, 0);
/* Poll status register for 60 secs for 'Controller Ready' flag */
if (tw_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY, 60)) {
while (tries < TW_MAX_RESET_TRIES) {
/* Do soft reset */
tw_soft_reset(tw_dev);
-
+
error = tw_aen_drain_queue(tw_dev);
if (error) {
printk(KERN_WARNING "3w-xxxx: AEN drain failed for card %d.\n", numcards);
continue;
}
- /* Empty the response queue */
- error = tw_empty_response_que(tw_dev);
- if (error) {
- printk(KERN_WARNING "3w-xxxx: Couldn't empty response queue, retrying for card %d.\n", numcards);
- tries++;
- continue;
- }
-
/* Now the controller is in a good state */
break;
}
request_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME);
error = tw_initialize_units(tw_dev);
if (error) {
- printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize units for card %d.\n", numcards);
+ printk(KERN_WARNING "3w-xxxx: No valid units for card %d.\n", numcards);
release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
tw_free_device_extension(tw_dev);
kfree(tw_dev);
tw_dev->free_tail = TW_MAX_BOUNCEBUF - 1;
tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1;
} else {
- tw_host->cmd_per_lun = (TW_Q_LENGTH-1)/tw_dev->num_units;
+ /* Use SHT cmd_per_lun here */
tw_dev->free_head = TW_Q_START;
tw_dev->free_tail = TW_Q_LENGTH - 1;
tw_dev->free_wrap = TW_Q_LENGTH - 1;
status_reg_value = inl(status_reg_addr);
if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
+ tw_decode_bits(tw_dev, status_reg_value, 0);
return 1;
}
if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
}
if (command_packet->status != 0) {
/* bad response */
- dprintk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
- tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+ tw_decode_sense(tw_dev, request_id, 0);
return 1;
}
break; /* Response was okay, so we exit */
/* This function will initialize the fields of a device extension */
int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
{
- int i, imax;
+ int i;
dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n");
- imax = TW_Q_LENGTH;
- for (i=0; i<imax; i++) {
+ for (i=0; i<TW_Q_LENGTH;i++) {
/* Initialize command packet buffers */
tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 0);
if (tw_dev->command_packet_virtual_address[i] == NULL) {
tw_dev->free_queue[i] = i;
tw_dev->state[i] = TW_S_INITIAL;
- tw_dev->ioctl_size[i] = 0;
- tw_dev->aen_queue[i] = 0;
- tw_dev->ioctl_data[i] = NULL;
- }
-
- for (i=0;i<TW_MAX_UNITS;i++) {
- tw_dev->is_unit_present[i] = 0;
- tw_dev->is_raid_five[i] = 0;
}
- tw_dev->num_units = 0;
- tw_dev->num_aborts = 0;
- tw_dev->num_resets = 0;
- tw_dev->posted_request_count = 0;
- tw_dev->max_posted_request_count = 0;
- tw_dev->max_sgl_entries = 0;
- tw_dev->sgl_entries = 0;
- tw_dev->host = NULL;
tw_dev->pending_head = TW_Q_START;
tw_dev->pending_tail = TW_Q_START;
- tw_dev->aen_head = 0;
- tw_dev->aen_tail = 0;
- tw_dev->sector_count = 0;
- tw_dev->max_sector_count = 0;
- tw_dev->aen_count = 0;
- tw_dev->num_raid_five = 0;
spin_lock_init(&tw_dev->tw_lock);
+
return 0;
} /* End tw_initialize_device_extension() */
status_reg_value = inl(status_reg_addr);
if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
+ tw_decode_bits(tw_dev, status_reg_value, 0);
return 1;
}
if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
}
if (command_packet->status != 0) {
/* bad response */
- dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
- tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+ tw_decode_sense(tw_dev, request_id, 0);
return 1;
}
found = 1;
status_reg_value = inl(status_reg_addr);
if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
+ tw_decode_bits(tw_dev, status_reg_value, 0);
return 1;
}
if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
}
if (command_packet->status != 0) {
/* bad response */
- dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
- tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+ tw_decode_sense(tw_dev, request_id, 0);
return 1;
}
found = 1;
u32 response_que_addr;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
TW_Response_Queue response_que;
- int error = 0;
- int do_response_interrupt=0;
- int do_attention_interrupt=0;
- int do_host_interrupt=0;
- int do_command_interrupt=0;
+ int error = 0, retval = 0;
unsigned long flags = 0;
TW_Command *command_packet;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt()\n");
+
+ /* See if we are already running on another processor */
+ if (test_and_set_bit(TW_IN_INTR, &tw_dev->flags))
+ return;
+
+ /* Get the block layer lock for io completions */
spin_lock_irqsave(&io_request_lock, flags);
+ /* See if the interrupt matches this instance */
if (tw_dev->tw_pci_dev->irq == irq) {
+
+ /* Make sure io isn't queueing */
spin_lock(&tw_dev->tw_lock);
- dprintk(KERN_WARNING "3w-xxxx: tw_interrupt()\n");
/* Read the registers */
status_reg_addr = tw_dev->registers.status_reg_addr;
response_que_addr = tw_dev->registers.response_que_addr;
status_reg_value = inl(status_reg_addr);
- /* Check which interrupt */
- if (status_reg_value & TW_STATUS_HOST_INTERRUPT)
- do_host_interrupt=1;
- if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT)
- do_attention_interrupt=1;
- if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT)
- do_command_interrupt=1;
- if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT)
- do_response_interrupt=1;
+ /* Check if this is our interrupt, otherwise bail */
+ if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT))
+ goto tw_interrupt_bail;
+
+ /* Check controller for errors */
+ if (tw_check_bits(status_reg_value)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
+ if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
+ tw_clear_all_interrupts(tw_dev);
+ goto tw_interrupt_bail;
+ }
+ }
/* Handle host interrupt */
- if (do_host_interrupt) {
+ if (status_reg_value & TW_STATUS_HOST_INTERRUPT) {
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received host interrupt.\n");
tw_clear_host_interrupt(tw_dev);
}
/* Handle attention interrupt */
- if (do_attention_interrupt) {
+ if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) {
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received attention interrupt.\n");
- dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Clearing attention interrupt.\n");
tw_clear_attention_interrupt(tw_dev);
tw_state_request_start(tw_dev, &request_id);
error = tw_aen_read_queue(tw_dev, request_id);
}
/* Handle command interrupt */
- if (do_command_interrupt) {
+ if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
/* Drain as many pending commands as we can */
while (tw_dev->pending_request_count > 0) {
request_id = tw_dev->pending_queue[tw_dev->pending_head];
}
tw_dev->pending_request_count--;
} else {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Error posting pending commands.\n", tw_dev->host->host_no);
break;
}
}
}
/* Handle response interrupt */
- if (do_response_interrupt) {
+ if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
/* Drain the response queue from the board */
while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+ /* Read response queue register */
response_que.value = inl(response_que_addr);
request_id = response_que.u.response_id;
command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
error = 0;
+
+ /* Check for bad response */
if (command_packet->status != 0) {
- dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x, unit = 0x%x.\n", command_packet->status, command_packet->flags, command_packet->byte3.unit);
- tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
- error = 1;
+ /* If internal commands, don't error, don't fill sense */
+ if (tw_dev->srb[request_id] == 0) {
+ tw_decode_sense(tw_dev, request_id, 0);
+ } else {
+ error = tw_decode_sense(tw_dev, request_id, 1);
+ }
}
+
+ /* Check for correct state */
if (tw_dev->state[request_id] != TW_S_POSTED) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", tw_dev->host->host_no, request_id, command_packet->byte0.opcode);
error = 1;
}
- if (TW_STATUS_ERRORS(status_reg_value)) {
- tw_decode_bits(tw_dev, status_reg_value);
- error = 1;
- }
+
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
- /* Check for internal command */
+
+ /* Check for internal command completion */
if (tw_dev->srb[request_id] == 0) {
dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n");
- error = tw_aen_complete(tw_dev, request_id);
- if (error) {
+ retval = tw_aen_complete(tw_dev, request_id);
+ if (retval) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no);
}
- status_reg_value = inl(status_reg_addr);
- if (tw_check_bits(status_reg_value)) {
- dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
- }
- } else {
+ } else {
switch (tw_dev->srb[request_id]->cmnd[0]) {
case READ_10:
case READ_6:
error = tw_ioctl_complete(tw_dev, request_id);
break;
default:
- printk(KERN_WARNING "3w-xxxx: scsi%d: Unknown scsi opcode: 0x%x.\n", tw_dev->host->host_no, tw_dev->srb[request_id]->cmnd[0]);
- tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
- }
- if (error == 1) {
- /* Tell scsi layer there was an error */
- dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
- tw_dev->srb[request_id]->result = (DID_RESET << 16);
+ printk(KERN_WARNING "3w-xxxx: case slip in tw_interrupt()\n");
+ error = 1;
}
+
+ /* If no error command was a success */
if (error == 0) {
- /* Tell scsi layer command was a success */
tw_dev->srb[request_id]->result = (DID_OK << 16);
}
- if (error != 2) {
+
+ /* If error, command failed */
+ if (error == 1) {
+ tw_dev->srb[request_id]->result = (DID_RESET << 16);
+ }
+
+ /* Now complete the io */
+ if ((error != TW_ISR_DONT_COMPLETE)) {
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
tw_dev->posted_request_count--;
tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
}
- status_reg_value = inl(status_reg_addr);
- if (tw_check_bits(status_reg_value)) {
- dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
+ }
+
+ /* Check for valid status after each drain */
+ status_reg_value = inl(status_reg_addr);
+ if (tw_check_bits(status_reg_value)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
+ if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
+ tw_clear_all_interrupts(tw_dev);
+ goto tw_interrupt_bail;
}
}
}
}
+tw_interrupt_bail:
spin_unlock(&tw_dev->tw_lock);
- }
+ } else
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt() called for wrong instance.\n");
+
spin_unlock_irqrestore(&io_request_lock, flags);
+ clear_bit(TW_IN_INTR, &tw_dev->flags);
} /* End tw_interrupt() */
/* This function handles ioctls from userspace to the driver */
break;
case TW_CMD_PACKET_WITH_DATA:
dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): caught TW_CMD_PACKET_WITH_DATA.\n");
- return 2; /* Special case for isr to not complete io */
+ return TW_ISR_DONT_COMPLETE; /* Special case for isr to not complete io */
default:
memset(buff, 0, tw_dev->srb[request_id]->request_bufflen);
param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
do_gettimeofday(&before);
status_reg_value = inl(status_reg_addr);
+ if (tw_check_bits(status_reg_value)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Unexpected bits.\n");
+ tw_decode_bits(tw_dev, status_reg_value, 0);
+ }
+
while ((status_reg_value & flag) != flag) {
status_reg_value = inl(status_reg_addr);
+
+ if (tw_check_bits(status_reg_value)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Unexpected bits.\n");
+ tw_decode_bits(tw_dev, status_reg_value, 0);
+ }
+
do_gettimeofday(&timeout);
if (before.tv_sec + seconds < timeout.tv_sec) {
dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Flag 0x%x not found.\n", flag);
return 1;
}
- mdelay(1);
+ mdelay(5);
}
return 0;
} /* End tw_poll_status() */
if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_post_command_packet(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
+ tw_decode_bits(tw_dev, status_reg_value, 1);
}
if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
tw_dev->pending_request_count = 0;
tw_dev->pending_head = TW_Q_START;
tw_dev->pending_tail = TW_Q_START;
+ tw_dev->reset_print = 0;
return 0;
} /* End tw_reset_device_extension() */
continue;
}
- /* Empty the response queue again */
- error = tw_empty_response_que(tw_dev);
- if (error) {
- printk(KERN_WARNING "3w-xxxx: scsi%d: Couldn't empty response queue, retrying.\n", tw_dev->host->host_no);
- tries++;
- continue;
- }
-
/* Now the controller is in a good state */
break;
}
return (FAILED);
}
- /* We have to let AEN requests through before the reset */
- spin_unlock_irq(&io_request_lock);
- mdelay(TW_AEN_WAIT_TIME);
- spin_lock_irq(&io_request_lock);
-
spin_lock(&tw_dev->tw_lock);
tw_dev->num_aborts++;
spin_unlock(&tw_dev->tw_lock);
return (SUCCESS);
}
+ if (tw_dev->state[i] == TW_S_POSTED) {
+ /* If the command has already been posted, we have to reset the card */
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, (u32)SCpnt);
+ /* We have to let AEN requests through before the reset */
+ spin_unlock(&tw_dev->tw_lock);
+ spin_unlock_irq(&io_request_lock);
+ mdelay(TW_AEN_WAIT_TIME);
+ spin_lock_irq(&io_request_lock);
+ spin_lock(&tw_dev->tw_lock);
+
+ if (tw_reset_device_extension(tw_dev)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Reset failed for card %d.\n", tw_dev->host->host_no);
+ spin_unlock(&tw_dev->tw_lock);
+ return (FAILED);
+ }
+ }
}
}
- /* If the command has already been posted, we have to reset the card */
- printk(KERN_WARNING "3w-xxxx: scsi%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, (u32)SCpnt);
- if (tw_reset_device_extension(tw_dev)) {
- dprintk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Reset failed for card %d.\n", tw_dev->host->host_no);
- spin_unlock(&tw_dev->tw_lock);
- return (FAILED);
- }
spin_unlock(&tw_dev->tw_lock);
-
return (SUCCESS);
} /* End tw_scsi_eh_abort() */
}
command_packet->byte0.sgl_offset = 3;
- command_packet->size = 5;
+ command_packet->size = 3;
command_packet->request_id = request_id;
command_packet->byte3.unit = srb->target;
command_packet->byte3.host_id = 0;
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n");
command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer);
command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
+ command_packet->size+=2;
}
/* Do this if we have multiple sg list entries */
command_packet->byte8.io.sgl[i].length = sglist[i].length;
command_packet->size+=2;
}
- if (tw_dev->srb[request_id]->use_sg >= 1)
- command_packet->size-=2;
}
} else {
/* Do this if there are no sg list entries for raid 5 */
memcpy(tw_dev->bounce_buffer[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen);
command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]);
command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
+ command_packet->size+=2;
}
/* Do this if we have multiple sg list entries for raid 5 */
{
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
- /* For now we just zero the sense buffer */
+ /* For now we just zero the request buffer */
memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
status_reg_value = inl(status_reg_addr);
if (tw_check_bits(status_reg_value)) {
dprintk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected bits.\n");
- tw_decode_bits(tw_dev, status_reg_value);
+ tw_decode_bits(tw_dev, status_reg_value, 1);
return 1;
}
if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
}
if (command_packet->status != 0) {
/* bad response */
- dprintk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
- tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+ tw_decode_sense(tw_dev, request_id, 0);
return 1;
}
break; /* Response was okay, so we exit */
"Verify failed: Port #", // 0x02A
"Verify complete: Unit #", // 0x02B
"Overwrote bad sector during rebuild: Port #", //0x2C
- "Encountered bad sector during rebuild: Port #" //0x2D
+ "Encountered bad sector during rebuild: Port #", //0x2D
+ "Replacement drive is too small: Port #" //0x2E
};
-#define TW_AEN_STRING_MAX 0x02E
+#define TW_AEN_STRING_MAX 0x02F
+
+/*
+ Sense key lookup table
+ Format: ESDC/flags,SenseKey,AdditionalSenseCode,AdditionalSenseCodeQualifier
+*/
+static unsigned char tw_sense_table[][4] =
+{
+ /* Codes for newer firmware */
+ // ATA Error SCSI Error
+ {0x01, 0x03, 0x13, 0x00}, // Address mark not found Address mark not found for data field
+ {0x04, 0x0b, 0x00, 0x00}, // Aborted command Aborted command
+ {0x10, 0x0b, 0x14, 0x00}, // ID not found Recorded entity not found
+ {0x40, 0x03, 0x11, 0x00}, // Uncorrectable ECC error Unrecovered read error
+ {0x61, 0x04, 0x00, 0x00}, // Device fault Hardware error
+ {0x84, 0x0b, 0x47, 0x00}, // Data CRC error SCSI parity error
+ {0xd0, 0x0b, 0x00, 0x00}, // Device busy Aborted command
+ {0xd1, 0x0b, 0x00, 0x00}, // Device busy Aborted command
+
+ /* Codes for older firmware */
+ // 3ware Error SCSI Error
+ {0x09, 0x0b, 0x00, 0x00}, // Unrecovered disk error Aborted command
+ {0x37, 0x0b, 0x04, 0x00}, // Unit offline Logical unit not ready
+ {0x51, 0x0b, 0x00, 0x00} // Unspecified Aborted command
+};
/* Control register bit definitions */
#define TW_CONTROL_CLEAR_HOST_INTERRUPT 0x00080000
#define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040
#define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020
#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000
+#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000
#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000
+#define TW_CONTROL_CLEAR_SBUF_WRITE_ERROR 0x00000008
/* Status register bit definitions */
#define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000
#define TW_STATUS_ALL_INTERRUPTS 0x000F0000
#define TW_STATUS_CLEARABLE_BITS 0x00D00000
#define TW_STATUS_EXPECTED_BITS 0x00002000
-#define TW_STATUS_UNEXPECTED_BITS 0x00F80000
+#define TW_STATUS_UNEXPECTED_BITS 0x00F00008
+#define TW_STATUS_SBUF_WRITE_ERROR 0x00000008
+#define TW_STATUS_VALID_INTERRUPT 0x00DF0008
/* RESPONSE QUEUE BIT DEFINITIONS */
#define TW_RESPONSE_ID_MASK 0x00000FF0
#define TW_AEN_SBUF_FAIL 0x0024
/* Misc defines */
-#define TW_ALIGNMENT 0x200 /* 16 D-WORDS */
+#define TW_ALIGNMENT_6000 64 /* 64 bytes */
+#define TW_ALIGNMENT_7000 4 /* 4 bytes */
#define TW_MAX_UNITS 16
#define TW_COMMAND_ALIGNMENT_MASK 0x1ff
#define TW_INIT_MESSAGE_CREDITS 0x100
#define TW_MAX_PCI_BUSES 255
#define TW_MAX_RESET_TRIES 3
#define TW_UNIT_INFORMATION_TABLE_BASE 0x300
-#define TW_MAX_CMDS_PER_LUN (TW_Q_LENGTH-2)/TW_MAX_UNITS
+#define TW_MAX_CMDS_PER_LUN 255
#define TW_BLOCK_SIZE 0x200 /* 512-byte blocks */
#define TW_IOCTL 0x80
#define TW_MAX_AEN_TRIES 100
#define TW_MAX_SECTORS 128
#define TW_AEN_WAIT_TIME 1000
#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
+#define TW_ISR_DONT_COMPLETE 2
+#define TW_ISR_DONT_RESULT 3
/* Macros */
#define TW_STATUS_ERRORS(x) \
#ifdef TW_DEBUG
#define dprintk(msg...) printk(msg)
#else
-#define dprintk(msg...) do { } while(0);
+#define dprintk(msg...) do { } while(0)
#endif
extern struct proc_dir_entry tw_scsi_proc_entry;
unsigned short aen_queue[TW_Q_LENGTH];
unsigned char aen_head;
unsigned char aen_tail;
- u32 flags;
+ volatile long flags;
char *ioctl_data[TW_Q_LENGTH];
+ int reset_print;
} TW_Device_Extension;
/* Function prototypes */
int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which);
int tw_check_bits(u32 status_reg_value);
int tw_check_errors(TW_Device_Extension *tw_dev);
+void tw_clear_all_interrupts(TW_Device_Extension *tw_dev);
void tw_clear_attention_interrupt(TW_Device_Extension *tw_dev);
void tw_clear_host_interrupt(TW_Device_Extension *tw_dev);
-void tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value);
-void tw_decode_error(TW_Device_Extension *tw_dev, unsigned char status, unsigned char flags, unsigned char unit);
+int tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value, int print_host);
+int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense);
void tw_disable_interrupts(TW_Device_Extension *tw_dev);
-int tw_empty_response_que(TW_Device_Extension *tw_dev);
+void tw_empty_response_que(TW_Device_Extension *tw_dev);
void tw_enable_interrupts(TW_Device_Extension *tw_dev);
void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev);
int tw_findcards(Scsi_Host_Template *tw_host);
reset : NULL, \
slave_attach : NULL, \
bios_param : tw_scsi_biosparam, \
- can_queue : TW_Q_LENGTH, \
+ can_queue : TW_Q_LENGTH-1, \
this_id: -1, \
sg_tablesize : TW_MAX_SGL_LENGTH, \
cmd_per_lun: TW_MAX_CMDS_PER_LUN, \