📄 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-2005 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 <linux/slab.h>#include <asm/param.h> /* for timeouts in units of HZ */#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 struct sym_ccb *sym_alloc_ccb(struct sym_hcb *np);static struct sym_ccb *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, struct sym_ccb *cp);static void sym_complete_ok (struct sym_hcb *np, struct sym_ccb *cp);static int sym_compute_residual(struct sym_hcb *np, struct sym_ccb *cp);/* * Print a buffer in hexadecimal format with a ".\n" at end. */static void sym_printl_hex(u_char *p, int n){ while (n-- > 0) printf (" %x", *p++); printf (".\n");}/* * 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(struct sym_ccb *cp, char *label, u_char *msg){ sym_print_addr(cp->cmd, "%s: ", label); sym_show_msg(msg); printf(".\n");}static void sym_print_nego_msg(struct sym_hcb *np, int target, char *label, u_char *msg){ struct sym_tcb *tp = &np->target[target]; dev_info(&tp->starget->dev, "%s: ", label); sym_show_msg(msg); printf(".\n");}/* * Print something that tells about extended errors. */void sym_print_xerr(struct scsi_cmnd *cmd, int x_status){ if (x_status & XE_PARITY_ERR) { sym_print_addr(cmd, "unrecovered SCSI parity error.\n"); } if (x_status & XE_EXTRA_DATA) { sym_print_addr(cmd, "extraneous data discarded.\n"); } if (x_status & XE_BAD_PHASE) { sym_print_addr(cmd, "illegal scsi phase (4/5).\n"); } if (x_status & XE_SODL_UNRUN) { sym_print_addr(cmd, "ODD transfer in DATA OUT phase.\n"); } if (x_status & XE_SWIDE_OVRUN) { sym_print_addr(cmd, "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(np, nc_istat, SRST); INB(np, nc_mbox1); udelay(10); OUTB(np, nc_istat, 0); INB(np, nc_mbox1); 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(np, nc_istat1) & SCRUN)) goto do_chip_reset; OUTB(np, nc_istat, CABRT); for (i = 100000 ; i ; --i) { istat = INB(np, nc_istat); if (istat & SIP) { INW(np, nc_sist); } else if (istat & DIP) { if (INB(np, nc_dstat) & ABRT) break; } udelay(5); } OUTB(np, 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){ 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(np, nc_sien, RST); /* * Enable Tolerant, reset IRQD if present and * properly set IRQ mode, prior to resetting the bus. */ OUTB(np, nc_stest3, TE); OUTB(np, nc_dcntl, (np->rv_dcntl & IRQM)); OUTB(np, nc_scntl1, CRST); INB(np, nc_mbox1); 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(np, nc_sstat0); term = ((term & 2) << 7) + ((term & 1) << 17); /* rst sdp0 */ term |= ((INB(np, nc_sstat2) & 0x01) << 26) | /* sdp1 */ ((INW(np, nc_sbdl) & 0xff) << 9) | /* d7-0 */ ((INW(np, nc_sbdl) & 0xff00) << 10) | /* d15-8 */ INB(np, 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(np, 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(np, nc_scntl3, scntl3); return; } if (sym_verbose >= 2) printf ("%s: enabling clock multiplier\n", sym_name(np)); OUTB(np, 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(np, nc_stest4) & LCKFRQ) && --i > 0) udelay(20); if (!i) printf("%s: the chip cannot lock the frequency\n", sym_name(np)); } else { INB(np, nc_mbox1); udelay(50+10); } OUTB(np, nc_stest3, HSC); /* Halt the scsi clock */ OUTB(np, nc_scntl3, scntl3); OUTB(np, nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */ OUTB(np, 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(np, nc_sien, 0); /* mask all scsi interrupts */ INW(np, nc_sist); /* clear pending scsi interrupt */ OUTB(np, nc_dien, 0); /* mask all dma interrupts */ INW(np, 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(np, nc_sien, GEN); OUTB(np, nc_istat1, SIRQD); } OUTB(np, nc_scntl3, 4); /* set pre-scaler to divide by 3 */ OUTB(np, nc_stime1, 0); /* disable general purpose timer */ OUTB(np, nc_stime1, gen); /* set to nominal delay of 1<<gen * 125us */ while (!(INW(np, nc_sist) & GEN) && ms++ < 100000) udelay(1000/4); /* count in 1/4 of ms */ OUTB(np, nc_stime1, 0); /* disable general purpose timer */ /* * Undo C1010-33 specific settings. */ if (np->features & FE_C10) { OUTW(np, nc_sien, 0); OUTB(np, 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(np, 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; 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(np, 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(np, nc_stest1, SCLK); /* Use the PCI clock as SCSI clock */ f = sym_getfreq(np); OUTB(np, nc_stest1, 0); } np->pciclk_khz = f; return f;}/* * SYMBIOS chip clock divisor table. * * Divisors are multiplied by 10,000,000 in order to make * calculations more simple. */#define _5M 5000000static u32 div_10M[] = {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};/* * Get clock factor and sync divisor for a given * synchronous factor period. */static int sym_getsync(struct sym_hcb *np, u_char dt, u_char sfac, u_char *divp, u_char *fakp){ u32 clk = np->clock_khz; /* SCSI clock frequency in kHz */ int div = np->clock_divn; /* Number of divisors supported */ u32 fak; /* Sync factor in sxfer */ u32 per; /* Period in tenths of ns */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -