📄 atari_scsi.c
字号:
/* * atari_scsi.c -- Device dependent functions for the Atari generic SCSI port * * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> * * Loosely based on the work of Robert De Vries' team and added: * - working real DMA * - Falcon support (untested yet!) ++bjoern fixed and now it works * - lots of extensions and bug fixes. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * *//**************************************************************************//* *//* Notes for Falcon SCSI: *//* ---------------------- *//* *//* Since the Falcon SCSI uses the ST-DMA chip, that is shared among *//* several device drivers, locking and unlocking the access to this *//* chip is required. But locking is not possible from an interrupt, *//* since it puts the process to sleep if the lock is not available. *//* This prevents "late" locking of the DMA chip, i.e. locking it just *//* before using it, since in case of disconnection-reconnection *//* commands, the DMA is started from the reselection interrupt. *//* *//* Two possible schemes for ST-DMA-locking would be: *//* 1) The lock is taken for each command separately and disconnecting *//* is forbidden (i.e. can_queue = 1). *//* 2) The DMA chip is locked when the first command comes in and *//* released when the last command is finished and all queues are *//* empty. *//* The first alternative would result in bad performance, since the *//* interleaving of commands would not be used. The second is unfair to *//* other drivers using the ST-DMA, because the queues will seldom be *//* totally empty if there is a lot of disk traffic. *//* *//* For this reasons I decided to employ a more elaborate scheme: *//* - First, we give up the lock every time we can (for fairness), this *//* means every time a command finishes and there are no other commands *//* on the disconnected queue. *//* - If there are others waiting to lock the DMA chip, we stop *//* issuing commands, i.e. moving them onto the issue queue. *//* Because of that, the disconnected queue will run empty in a *//* while. Instead we go to sleep on a 'fairness_queue'. *//* - If the lock is released, all processes waiting on the fairness *//* queue will be woken. The first of them tries to re-lock the DMA, *//* the others wait for the first to finish this task. After that, *//* they can all run on and do their commands... *//* This sounds complicated (and it is it :-(), but it seems to be a *//* good compromise between fairness and performance: As long as no one *//* else wants to work with the ST-DMA chip, SCSI can go along as *//* usual. If now someone else comes, this behaviour is changed to a *//* "fairness mode": just already initiated commands are finished and *//* then the lock is released. The other one waiting will probably win *//* the race for locking the DMA, since it was waiting for longer. And *//* after it has finished, SCSI can go ahead again. Finally: I hope I *//* have not produced any deadlock possibilities! *//* *//**************************************************************************/#include <linux/config.h>#include <linux/module.h>#define NDEBUG (0)#define NDEBUG_ABORT 0x800000#define NDEBUG_TAGS 0x1000000#define NDEBUG_MERGING 0x2000000#define AUTOSENSE/* For the Atari version, use only polled IO or REAL_DMA */#define REAL_DMA/* Support tagged queuing? (on devices that are able to... :-) */#define SUPPORT_TAGS#define MAX_TAGS 32#include <linux/types.h>#include <linux/stddef.h>#include <linux/ctype.h>#include <linux/delay.h>#include <linux/mm.h>#include <linux/blk.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/nvram.h>#include <asm/setup.h>#include <asm/atarihw.h>#include <asm/atariints.h>#include <asm/page.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/traps.h>#include <asm/bitops.h>#include "scsi.h"#include "hosts.h"#include "atari_scsi.h"#include "NCR5380.h"#include "constants.h"#include <asm/atari_stdma.h>#include <asm/atari_stram.h>#include <asm/io.h>#include <linux/stat.h>#define IS_A_TT() ATARIHW_PRESENT(TT_SCSI)#define SCSI_DMA_WRITE_P(elt,val) \ do { \ unsigned long v = val; \ tt_scsi_dma.elt##_lo = v & 0xff; \ v >>= 8; \ tt_scsi_dma.elt##_lmd = v & 0xff; \ v >>= 8; \ tt_scsi_dma.elt##_hmd = v & 0xff; \ v >>= 8; \ tt_scsi_dma.elt##_hi = v & 0xff; \ } while(0)#define SCSI_DMA_READ_P(elt) \ (((((((unsigned long)tt_scsi_dma.elt##_hi << 8) | \ (unsigned long)tt_scsi_dma.elt##_hmd) << 8) | \ (unsigned long)tt_scsi_dma.elt##_lmd) << 8) | \ (unsigned long)tt_scsi_dma.elt##_lo)static inline void SCSI_DMA_SETADR(unsigned long adr){ st_dma.dma_lo = (unsigned char)adr; MFPDELAY(); adr >>= 8; st_dma.dma_md = (unsigned char)adr; MFPDELAY(); adr >>= 8; st_dma.dma_hi = (unsigned char)adr; MFPDELAY();}static inline unsigned long SCSI_DMA_GETADR(void){ unsigned long adr; adr = st_dma.dma_lo; MFPDELAY(); adr |= (st_dma.dma_md & 0xff) << 8; MFPDELAY(); adr |= (st_dma.dma_hi & 0xff) << 16; MFPDELAY(); return adr;}static inline void ENABLE_IRQ(void){ if (IS_A_TT()) atari_enable_irq(IRQ_TT_MFP_SCSI); else atari_enable_irq(IRQ_MFP_FSCSI);}static inline void DISABLE_IRQ(void){ if (IS_A_TT()) atari_disable_irq(IRQ_TT_MFP_SCSI); else atari_disable_irq(IRQ_MFP_FSCSI);}#define HOSTDATA_DMALEN (((struct NCR5380_hostdata *) \ (atari_scsi_host->hostdata))->dma_len)/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms, * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more * need ten times the standard value... */#ifndef CONFIG_ATARI_SCSI_TOSHIBA_DELAY#define AFTER_RESET_DELAY (HZ/2)#else#define AFTER_RESET_DELAY (5*HZ/2)#endif/***************************** Prototypes *****************************/#ifdef REAL_DMAstatic int scsi_dma_is_ignored_buserr( unsigned char dma_stat );static void atari_scsi_fetch_restbytes( void );static long atari_scsi_dma_residual( struct Scsi_Host *instance );static int falcon_classify_cmd( Scsi_Cmnd *cmd );static unsigned long atari_dma_xfer_len( unsigned long wanted_len, Scsi_Cmnd *cmd, int write_flag );#endifstatic void scsi_tt_intr( int irq, void *dummy, struct pt_regs *fp);static void scsi_falcon_intr( int irq, void *dummy, struct pt_regs *fp);static void falcon_release_lock_if_possible( struct NCR5380_hostdata * hostdata );static void falcon_get_lock( void );#ifdef CONFIG_ATARI_SCSI_RESET_BOOTstatic void atari_scsi_reset_boot( void );#endifstatic unsigned char atari_scsi_tt_reg_read( unsigned char reg );static void atari_scsi_tt_reg_write( unsigned char reg, unsigned char value);static unsigned char atari_scsi_falcon_reg_read( unsigned char reg );static void atari_scsi_falcon_reg_write( unsigned char reg, unsigned char value );/************************* End of Prototypes **************************/static struct Scsi_Host *atari_scsi_host = NULL;static unsigned char (*atari_scsi_reg_read)( unsigned char reg );static void (*atari_scsi_reg_write)( unsigned char reg, unsigned char value );#ifdef REAL_DMAstatic unsigned long atari_dma_residual, atari_dma_startaddr;static short atari_dma_active;/* pointer to the dribble buffer */static char *atari_dma_buffer = NULL;/* precalculated physical address of the dribble buffer */static unsigned long atari_dma_phys_buffer;/* != 0 tells the Falcon int handler to copy data from the dribble buffer */static char *atari_dma_orig_addr;/* size of the dribble buffer; 4k seems enough, since the Falcon cannot use * scatter-gather anyway, so most transfers are 1024 byte only. In the rare * cases where requests to physical contiguous buffers have been merged, this * request is <= 4k (one page). So I don't think we have to split transfers * just due to this buffer size... */#define STRAM_BUFFER_SIZE (4096)/* mask for address bits that can't be used with the ST-DMA */static unsigned long atari_dma_stram_mask;#define STRAM_ADDR(a) (((a) & atari_dma_stram_mask) == 0)/* number of bytes to cut from a transfer to handle NCR overruns */static int atari_read_overruns = 0;#endifstatic int setup_can_queue = -1;MODULE_PARM(setup_can_queue, "i");static int setup_cmd_per_lun = -1;MODULE_PARM(setup_cmd_per_lun, "i");static int setup_sg_tablesize = -1;MODULE_PARM(setup_sg_tablesize, "i");#ifdef SUPPORT_TAGSstatic int setup_use_tagged_queuing = -1;MODULE_PARM(setup_use_tagged_queuing, "i");#endifstatic int setup_hostid = -1;MODULE_PARM(setup_hostid, "i");#if defined(CONFIG_TT_DMA_EMUL)#include "atari_dma_emul.c"#endif#if defined(REAL_DMA)static int scsi_dma_is_ignored_buserr( unsigned char dma_stat ){ int i; unsigned long addr = SCSI_DMA_READ_P( dma_addr ), end_addr; if (dma_stat & 0x01) { /* A bus error happens when DMA-ing from the last page of a * physical memory chunk (DMA prefetch!), but that doesn't hurt. * Check for this case: */ for( i = 0; i < m68k_num_memory; ++i ) { end_addr = m68k_memory[i].addr + m68k_memory[i].size; if (end_addr <= addr && addr <= end_addr + 4) return( 1 ); } } return( 0 );}#if 0/* Dead code... wasn't called anyway :-) and causes some trouble, because at * end-of-DMA, both SCSI ints are triggered simultaneously, so the NCR int has * to clear the DMA int pending bit before it allows other level 6 interrupts. */static void scsi_dma_buserr (int irq, void *dummy, struct pt_regs *fp){ unsigned char dma_stat = tt_scsi_dma.dma_ctrl; /* Don't do anything if a NCR interrupt is pending. Probably it's just * masked... */ if (atari_irq_pending( IRQ_TT_MFP_SCSI )) return; printk("Bad SCSI DMA interrupt! dma_addr=0x%08lx dma_stat=%02x dma_cnt=%08lx\n", SCSI_DMA_READ_P(dma_addr), dma_stat, SCSI_DMA_READ_P(dma_cnt)); if (dma_stat & 0x80) { if (!scsi_dma_is_ignored_buserr( dma_stat )) printk( "SCSI DMA bus error -- bad DMA programming!\n" ); } else { /* Under normal circumstances we never should get to this point, * since both interrupts are triggered simultaneously and the 5380 * int has higher priority. When this irq is handled, that DMA * interrupt is cleared. So a warning message is printed here. */ printk( "SCSI DMA intr ?? -- this shouldn't happen!\n" ); }}#endif#endifstatic void scsi_tt_intr (int irq, void *dummy, struct pt_regs *fp){#ifdef REAL_DMA int dma_stat; dma_stat = tt_scsi_dma.dma_ctrl; INT_PRINTK("scsi%d: NCR5380 interrupt, DMA status = %02x\n", atari_scsi_host->host_no, dma_stat & 0xff); /* Look if it was the DMA that has interrupted: First possibility * is that a bus error occurred... */ if (dma_stat & 0x80) { if (!scsi_dma_is_ignored_buserr( dma_stat )) { printk(KERN_ERR "SCSI DMA caused bus error near 0x%08lx\n", SCSI_DMA_READ_P(dma_addr)); printk(KERN_CRIT "SCSI DMA bus error -- bad DMA programming!"); } } /* If the DMA is active but not finished, we have the case * that some other 5380 interrupt occurred within the DMA transfer. * This means we have residual bytes, if the desired end address * is not yet reached. Maybe we have to fetch some bytes from the * rest data register, too. The residual must be calculated from * the address pointer, not the counter register, because only the * addr reg counts bytes not yet written and pending in the rest * data reg! */ if ((dma_stat & 0x02) && !(dma_stat & 0x40)) { atari_dma_residual = HOSTDATA_DMALEN - (SCSI_DMA_READ_P( dma_addr ) - atari_dma_startaddr); DMA_PRINTK("SCSI DMA: There are %ld residual bytes.\n", atari_dma_residual); if ((signed int)atari_dma_residual < 0) atari_dma_residual = 0; if ((dma_stat & 1) == 0) { /* After read operations, we maybe have to transport some rest bytes */ atari_scsi_fetch_restbytes(); } else { /* There seems to be a nasty bug in some SCSI-DMA/NCR combinations: If a target disconnects while a write operation is going on, the address register of the DMA may be a few bytes farer than it actually read. This is probably due to DMA prefetching and a delay between DMA and NCR. Experiments showed that the dma_addr is 9 bytes to high, but this could vary. The problem is, that the residual is thus calculated wrong and the next transfer will start behind where it should. So we round up the residual to the next multiple of a sector size, if it isn't already a multiple and the originally expected transfer size was. The latter condition is there to ensure that the correction is taken only for "real" data transfers and not for, e.g., the parameters of some other command. These shouldn't disconnect anyway.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -