📄 sym_hipd.c
字号:
/* * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family * of PCI-SCSI IO processors. * * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr> * Copyright (c) 2003-2004 Matthew Wilcox <matthew@wil.cx> * * This driver is derived from the Linux sym53c8xx driver. * Copyright (C) 1998-2000 Gerard Roudier * * The sym53c8xx driver is derived from the ncr53c8xx driver that had been * a port of the FreeBSD ncr driver to Linux-1.2.13. * * The original ncr driver has been written for 386bsd and FreeBSD by * Wolfgang Stanglmeier <wolf@cologne.de> * Stefan Esser <se@mi.Uni-Koeln.de> * Copyright (C) 1994 Wolfgang Stanglmeier * * Other major contributions: * * NVRAM detection and reading. * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk> * *----------------------------------------------------------------------------- * * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "sym_glue.h"#include "sym_nvram.h"#if 0#define SYM_DEBUG_GENERIC_SUPPORT#endif/* * Needed function prototypes. */static void sym_int_ma (struct sym_hcb *np);static void sym_int_sir (struct sym_hcb *np);static ccb_p sym_alloc_ccb(struct sym_hcb *np);static ccb_p sym_ccb_from_dsa(struct sym_hcb *np, u32 dsa);static void sym_alloc_lcb_tags (struct sym_hcb *np, u_char tn, u_char ln);static void sym_complete_error (struct sym_hcb *np, ccb_p cp);static void sym_complete_ok (struct sym_hcb *np, ccb_p cp);static int sym_compute_residual(struct sym_hcb *np, ccb_p cp);/* * Returns the name of this driver. */char *sym_driver_name(void){ return SYM_DRIVER_NAME;}/* * Print a buffer in hexadecimal format. */static void sym_printb_hex (u_char *p, int n){ while (n-- > 0) printf (" %x", *p++);}/* * Same with a label at beginning and .\n at end. */static void sym_printl_hex (char *label, u_char *p, int n){ printf ("%s", label); sym_printb_hex (p, n); printf (".\n");}/* * Print something which allows to retrieve the controler type, * unit, target, lun concerned by a kernel message. */static void sym_print_target (struct sym_hcb *np, int target){ printf ("%s:%d:", sym_name(np), target);}static void sym_print_lun(struct sym_hcb *np, int target, int lun){ printf ("%s:%d:%d:", sym_name(np), target, lun);}/* * Print out the content of a SCSI message. */static int sym_show_msg (u_char * msg){ u_char i; printf ("%x",*msg); if (*msg==M_EXTENDED) { for (i=1;i<8;i++) { if (i-1>msg[1]) break; printf ("-%x",msg[i]); }; return (i+1); } else if ((*msg & 0xf0) == 0x20) { printf ("-%x",msg[1]); return (2); }; return (1);}static void sym_print_msg (ccb_p cp, char *label, u_char *msg){ PRINT_ADDR(cp); if (label) printf ("%s: ", label); (void) sym_show_msg (msg); printf (".\n");}static void sym_print_nego_msg (struct sym_hcb *np, int target, char *label, u_char *msg){ PRINT_TARGET(np, target); if (label) printf ("%s: ", label); (void) sym_show_msg (msg); printf (".\n");}/* * Print something that tells about extended errors. */void sym_print_xerr(ccb_p cp, int x_status){ if (x_status & XE_PARITY_ERR) { PRINT_ADDR(cp); printf ("unrecovered SCSI parity error.\n"); } if (x_status & XE_EXTRA_DATA) { PRINT_ADDR(cp); printf ("extraneous data discarded.\n"); } if (x_status & XE_BAD_PHASE) { PRINT_ADDR(cp); printf ("illegal scsi phase (4/5).\n"); } if (x_status & XE_SODL_UNRUN) { PRINT_ADDR(cp); printf ("ODD transfer in DATA OUT phase.\n"); } if (x_status & XE_SWIDE_OVRUN) { PRINT_ADDR(cp); printf ("ODD transfer in DATA IN phase.\n"); }}/* * Return a string for SCSI BUS mode. */static char *sym_scsi_bus_mode(int mode){ switch(mode) { case SMODE_HVD: return "HVD"; case SMODE_SE: return "SE"; case SMODE_LVD: return "LVD"; } return "??";}/* * Soft reset the chip. * * Raising SRST when the chip is running may cause * problems on dual function chips (see below). * On the other hand, LVD devices need some delay * to settle and report actual BUS mode in STEST4. */static void sym_chip_reset (struct sym_hcb *np){ OUTB (nc_istat, SRST); UDELAY (10); OUTB (nc_istat, 0); UDELAY(2000); /* For BUS MODE to settle */}/* * Really soft reset the chip.:) * * Some 896 and 876 chip revisions may hang-up if we set * the SRST (soft reset) bit at the wrong time when SCRIPTS * are running. * So, we need to abort the current operation prior to * soft resetting the chip. */static void sym_soft_reset (struct sym_hcb *np){ u_char istat = 0; int i; if (!(np->features & FE_ISTAT1) || !(INB (nc_istat1) & SCRUN)) goto do_chip_reset; OUTB (nc_istat, CABRT); for (i = 100000 ; i ; --i) { istat = INB (nc_istat); if (istat & SIP) { INW (nc_sist); } else if (istat & DIP) { if (INB (nc_dstat) & ABRT) break; } UDELAY(5); } OUTB (nc_istat, 0); if (!i) printf("%s: unable to abort current chip operation, " "ISTAT=0x%02x.\n", sym_name(np), istat);do_chip_reset: sym_chip_reset (np);}/* * Start reset process. * * The interrupt handler will reinitialize the chip. */static void sym_start_reset(struct sym_hcb *np){ (void) sym_reset_scsi_bus(np, 1);} int sym_reset_scsi_bus(struct sym_hcb *np, int enab_int){ u32 term; int retv = 0; sym_soft_reset(np); /* Soft reset the chip */ if (enab_int) OUTW (nc_sien, RST); /* * Enable Tolerant, reset IRQD if present and * properly set IRQ mode, prior to resetting the bus. */ OUTB (nc_stest3, TE); OUTB (nc_dcntl, (np->rv_dcntl & IRQM)); OUTB (nc_scntl1, CRST); UDELAY (200); if (!SYM_SETUP_SCSI_BUS_CHECK) goto out; /* * Check for no terminators or SCSI bus shorts to ground. * Read SCSI data bus, data parity bits and control signals. * We are expecting RESET to be TRUE and other signals to be * FALSE. */ term = INB(nc_sstat0); term = ((term & 2) << 7) + ((term & 1) << 17); /* rst sdp0 */ term |= ((INB(nc_sstat2) & 0x01) << 26) | /* sdp1 */ ((INW(nc_sbdl) & 0xff) << 9) | /* d7-0 */ ((INW(nc_sbdl) & 0xff00) << 10) | /* d15-8 */ INB(nc_sbcl); /* req ack bsy sel atn msg cd io */ if (!np->maxwide) term &= 0x3ffff; if (term != (2<<7)) { printf("%s: suspicious SCSI data while resetting the BUS.\n", sym_name(np)); printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = " "0x%lx, expecting 0x%lx\n", sym_name(np), (np->features & FE_WIDE) ? "dp1,d15-8," : "", (u_long)term, (u_long)(2<<7)); if (SYM_SETUP_SCSI_BUS_CHECK == 1) retv = 1; }out: OUTB (nc_scntl1, 0); return retv;}/* * Select SCSI clock frequency */static void sym_selectclock(struct sym_hcb *np, u_char scntl3){ /* * If multiplier not present or not selected, leave here. */ if (np->multiplier <= 1) { OUTB(nc_scntl3, scntl3); return; } if (sym_verbose >= 2) printf ("%s: enabling clock multiplier\n", sym_name(np)); OUTB(nc_stest1, DBLEN); /* Enable clock multiplier */ /* * Wait for the LCKFRQ bit to be set if supported by the chip. * Otherwise wait 50 micro-seconds (at least). */ if (np->features & FE_LCKFRQ) { int i = 20; while (!(INB(nc_stest4) & LCKFRQ) && --i > 0) UDELAY (20); if (!i) printf("%s: the chip cannot lock the frequency\n", sym_name(np)); } else UDELAY ((50+10)); OUTB(nc_stest3, HSC); /* Halt the scsi clock */ OUTB(nc_scntl3, scntl3); OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */ OUTB(nc_stest3, 0x00); /* Restart scsi clock */}/* * Determine the chip's clock frequency. * * This is essential for the negotiation of the synchronous * transfer rate. * * Note: we have to return the correct value. * THERE IS NO SAFE DEFAULT VALUE. * * Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock. * 53C860 and 53C875 rev. 1 support fast20 transfers but * do not have a clock doubler and so are provided with a * 80 MHz clock. All other fast20 boards incorporate a doubler * and so should be delivered with a 40 MHz clock. * The recent fast40 chips (895/896/895A/1010) use a 40 Mhz base * clock and provide a clock quadrupler (160 Mhz). *//* * calculate SCSI clock frequency (in KHz) */static unsigned getfreq (struct sym_hcb *np, int gen){ unsigned int ms = 0; unsigned int f; /* * Measure GEN timer delay in order * to calculate SCSI clock frequency * * This code will never execute too * many loop iterations (if DELAY is * reasonably correct). It could get * too low a delay (too high a freq.) * if the CPU is slow executing the * loop for some reason (an NMI, for * example). For this reason we will * if multiple measurements are to be * performed trust the higher delay * (lower frequency returned). */ OUTW (nc_sien , 0); /* mask all scsi interrupts */ (void) INW (nc_sist); /* clear pending scsi interrupt */ OUTB (nc_dien , 0); /* mask all dma interrupts */ (void) INW (nc_sist); /* another one, just to be sure :) */ /* * The C1010-33 core does not report GEN in SIST, * if this interrupt is masked in SIEN. * I don't know yet if the C1010-66 behaves the same way. */ if (np->features & FE_C10) { OUTW (nc_sien, GEN); OUTB (nc_istat1, SIRQD); } OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */ OUTB (nc_stime1, 0); /* disable general purpose timer */ OUTB (nc_stime1, gen); /* set to nominal delay of 1<<gen * 125us */ while (!(INW(nc_sist) & GEN) && ms++ < 100000) UDELAY (1000/4);/* count in 1/4 of ms */ OUTB (nc_stime1, 0); /* disable general purpose timer */ /* * Undo C1010-33 specific settings. */ if (np->features & FE_C10) { OUTW (nc_sien, 0); OUTB (nc_istat1, 0); } /* * set prescaler to divide by whatever 0 means * 0 ought to choose divide by 2, but appears * to set divide by 3.5 mode in my 53c810 ... */ OUTB (nc_scntl3, 0); /* * adjust for prescaler, and convert into KHz */ f = ms ? ((1 << gen) * (4340*4)) / ms : 0; /* * The C1010-33 result is biased by a factor * of 2/3 compared to earlier chips. */ if (np->features & FE_C10) f = (f * 2) / 3; if (sym_verbose >= 2) printf ("%s: Delay (GEN=%d): %u msec, %u KHz\n", sym_name(np), gen, ms/4, f); return f;}static unsigned sym_getfreq (struct sym_hcb *np){ u_int f1, f2; int gen = 8; (void) getfreq (np, gen); /* throw away first result */ f1 = getfreq (np, gen); f2 = getfreq (np, gen); if (f1 > f2) f1 = f2; /* trust lower result */ return f1;}/* * Get/probe chip SCSI clock frequency */static void sym_getclock (struct sym_hcb *np, int mult){ unsigned char scntl3 = np->sv_scntl3; unsigned char stest1 = np->sv_stest1; unsigned f1; np->multiplier = 1; f1 = 40000; /* * True with 875/895/896/895A with clock multiplier selected */ if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) { if (sym_verbose >= 2) printf ("%s: clock multiplier found\n", sym_name(np)); np->multiplier = mult; } /* * If multiplier not found or scntl3 not 7,5,3, * reset chip and get frequency from general purpose timer. * Otherwise trust scntl3 BIOS setting. */ if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) { OUTB (nc_stest1, 0); /* make sure doubler is OFF */ f1 = sym_getfreq (np); if (sym_verbose) printf ("%s: chip clock is %uKHz\n", sym_name(np), f1); if (f1 < 45000) f1 = 40000; else if (f1 < 55000) f1 = 50000; else f1 = 80000; if (f1 < 80000 && mult > 1) { if (sym_verbose >= 2) printf ("%s: clock multiplier assumed\n", sym_name(np)); np->multiplier = mult; } } else { if ((scntl3 & 7) == 3) f1 = 40000; else if ((scntl3 & 7) == 5) f1 = 80000; else f1 = 160000; f1 /= np->multiplier; } /* * Compute controller synchronous parameters. */ f1 *= np->multiplier; np->clock_khz = f1;}/* * Get/probe PCI clock frequency */static int sym_getpciclock (struct sym_hcb *np){ int f = 0; /* * For now, we only need to know about the actual * PCI BUS clock frequency for C1010-66 chips. */#if 1 if (np->features & FE_66MHZ) {#else if (1) {#endif OUTB (nc_stest1, SCLK); /* Use the PCI clock as SCSI clock */ f = (int) sym_getfreq (np); OUTB (nc_stest1, 0); } np->pciclk_khz = f; return f;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -