patch-2.4.15 linux/drivers/scsi/sym53c8xx_2/sym_fw.c

Next file: linux/drivers/scsi/sym53c8xx_2/sym_fw.h
Previous file: linux/drivers/scsi/sym53c8xx_2/sym_defs.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.14/linux/drivers/scsi/sym53c8xx_2/sym_fw.c linux/drivers/scsi/sym53c8xx_2/sym_fw.c
@@ -0,0 +1,617 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * 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>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef __FreeBSD__
+#include <dev/sym/sym_glue.h>
+#else
+#include "sym_glue.h"
+#endif
+
+/*
+ *  Macros used for all firmwares.
+ */
+#define	SYM_GEN_A(s, label)	((short) offsetof(s, label)),
+#define	SYM_GEN_B(s, label)	((short) offsetof(s, label)),
+#define	SYM_GEN_Z(s, label)	((short) offsetof(s, label)),
+#define	PADDR_A(label)		SYM_GEN_PADDR_A(struct SYM_FWA_SCR, label)
+#define	PADDR_B(label)		SYM_GEN_PADDR_B(struct SYM_FWB_SCR, label)
+
+
+#if	SYM_CONF_GENERIC_SUPPORT
+/*
+ *  Allocate firmware #1 script area.
+ */
+#define	SYM_FWA_SCR		sym_fw1a_scr
+#define	SYM_FWB_SCR		sym_fw1b_scr
+#define	SYM_FWZ_SCR		sym_fw1z_scr
+#ifdef __FreeBSD__
+#include <dev/sym/sym_fw1.h>
+#else
+#include "sym_fw1.h"
+#endif
+static struct sym_fwa_ofs sym_fw1a_ofs = {
+	SYM_GEN_FW_A(struct SYM_FWA_SCR)
+};
+static struct sym_fwb_ofs sym_fw1b_ofs = {
+	SYM_GEN_FW_B(struct SYM_FWB_SCR)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+	SYM_GEN_B(struct SYM_FWB_SCR, data_io)
+#endif
+};
+static struct sym_fwz_ofs sym_fw1z_ofs = {
+	SYM_GEN_FW_Z(struct SYM_FWZ_SCR)
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+	SYM_GEN_Z(struct SYM_FWZ_SCR, start_ram)
+#endif
+};
+#undef	SYM_FWA_SCR
+#undef	SYM_FWB_SCR
+#undef	SYM_FWZ_SCR
+#endif	/* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ *  Allocate firmware #2 script area.
+ */
+#define	SYM_FWA_SCR		sym_fw2a_scr
+#define	SYM_FWB_SCR		sym_fw2b_scr
+#define	SYM_FWZ_SCR		sym_fw2z_scr
+#ifdef __FreeBSD__
+#include <dev/sym/sym_fw2.h>
+#else
+#include "sym_fw2.h"
+#endif
+static struct sym_fwa_ofs sym_fw2a_ofs = {
+	SYM_GEN_FW_A(struct SYM_FWA_SCR)
+};
+static struct sym_fwb_ofs sym_fw2b_ofs = {
+	SYM_GEN_FW_B(struct SYM_FWB_SCR)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+	SYM_GEN_B(struct SYM_FWB_SCR, data_io)
+#endif
+	SYM_GEN_B(struct SYM_FWB_SCR, start64)
+	SYM_GEN_B(struct SYM_FWB_SCR, pm_handle)
+};
+static struct sym_fwz_ofs sym_fw2z_ofs = {
+	SYM_GEN_FW_Z(struct SYM_FWZ_SCR)
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+	SYM_GEN_Z(struct SYM_FWZ_SCR, start_ram)
+	SYM_GEN_Z(struct SYM_FWZ_SCR, start_ram64)
+#endif
+};
+#undef	SYM_FWA_SCR
+#undef	SYM_FWB_SCR
+#undef	SYM_FWZ_SCR
+
+#undef	SYM_GEN_A
+#undef	SYM_GEN_B
+#undef	SYM_GEN_Z
+#undef	PADDR_A
+#undef	PADDR_B
+
+#if	SYM_CONF_GENERIC_SUPPORT
+/*
+ *  Patch routine for firmware #1.
+ */
+static void
+sym_fw1_patch(hcb_p np)
+{
+	struct sym_fw1a_scr *scripta0;
+	struct sym_fw1b_scr *scriptb0;
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+	struct sym_fw1z_scr *scriptz0 = 
+		(struct sym_fw1z_scr *) np->scriptz0;
+#endif
+
+	scripta0 = (struct sym_fw1a_scr *) np->scripta0;
+	scriptb0 = (struct sym_fw1b_scr *) np->scriptb0;
+
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+	/*
+	 *  Set up BUS physical address of SCRIPTS that is to 
+	 *  be copied to on-chip RAM by the SCRIPTS processor.
+	 */
+	scriptz0->scripta0_ba[0]	= cpu_to_scr(vtobus(scripta0));
+#endif
+
+	/*
+	 *  Remove LED support if not needed.
+	 */
+	if (!(np->features & FE_LED0)) {
+		scripta0->idle[0]	= cpu_to_scr(SCR_NO_OP);
+		scripta0->reselected[0]	= cpu_to_scr(SCR_NO_OP);
+		scripta0->start[0]	= cpu_to_scr(SCR_NO_OP);
+	}
+
+#ifdef SYM_CONF_IARB_SUPPORT
+	/*
+	 *    If user does not want to use IMMEDIATE ARBITRATION
+	 *    when we are reselected while attempting to arbitrate,
+	 *    patch the SCRIPTS accordingly with a SCRIPT NO_OP.
+	 */
+	if (!SYM_CONF_SET_IARB_ON_ARB_LOST)
+		scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);
+#endif
+	/*
+	 *  Patch some data in SCRIPTS.
+	 *  - start and done queue initial bus address.
+	 *  - target bus address table bus address.
+	 */
+	scriptb0->startpos[0]	= cpu_to_scr(np->squeue_ba);
+	scriptb0->done_pos[0]	= cpu_to_scr(np->dqueue_ba);
+	scriptb0->targtbl[0]	= cpu_to_scr(np->targtbl_ba);
+}
+#endif	/* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ *  Patch routine for firmware #2.
+ */
+static void
+sym_fw2_patch(hcb_p np)
+{
+	struct sym_fw2a_scr *scripta0;
+	struct sym_fw2b_scr *scriptb0;
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+	struct sym_fw2z_scr *scriptz0 = 
+		(struct sym_fw2z_scr *) np->scriptz0;
+#endif
+
+	scripta0 = (struct sym_fw2a_scr *) np->scripta0;
+	scriptb0 = (struct sym_fw2b_scr *) np->scriptb0;
+
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+	/*
+	 *  Set up BUS physical address of SCRIPTS that is to 
+	 *  be copied to on-chip RAM by the SCRIPTS processor.
+	 */
+	scriptz0->scripta0_ba64[0]	= /* Nothing is missing here */
+	scriptz0->scripta0_ba[0]	= cpu_to_scr(vtobus(scripta0));
+	scriptz0->scriptb0_ba64[0]	= cpu_to_scr(vtobus(scriptb0));
+	scriptz0->ram_seg64[0]		= np->scr_ram_seg;
+#endif
+
+	/*
+	 *  Remove LED support if not needed.
+	 */
+	if (!(np->features & FE_LED0)) {
+		scripta0->idle[0]	= cpu_to_scr(SCR_NO_OP);
+		scripta0->reselected[0]	= cpu_to_scr(SCR_NO_OP);
+		scripta0->start[0]	= cpu_to_scr(SCR_NO_OP);
+	}
+
+#if   SYM_CONF_DMA_ADDRESSING_MODE == 2
+	/*
+	 *  Remove useless 64 bit DMA specific SCRIPTS, 
+	 *  when this feature is not available.
+	 */
+	if (!np->use_dac) {
+		scripta0->is_dmap_dirty[0] = cpu_to_scr(SCR_NO_OP);
+		scripta0->is_dmap_dirty[1] = 0;
+		scripta0->is_dmap_dirty[2] = cpu_to_scr(SCR_NO_OP);
+		scripta0->is_dmap_dirty[3] = 0;
+	}
+#endif
+
+#ifdef SYM_CONF_IARB_SUPPORT
+	/*
+	 *    If user does not want to use IMMEDIATE ARBITRATION
+	 *    when we are reselected while attempting to arbitrate,
+	 *    patch the SCRIPTS accordingly with a SCRIPT NO_OP.
+	 */
+	if (!SYM_CONF_SET_IARB_ON_ARB_LOST)
+		scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);
+#endif
+	/*
+	 *  Patch some variable in SCRIPTS.
+	 *  - start and done queue initial bus address.
+	 *  - target bus address table bus address.
+	 */
+	scriptb0->startpos[0]	= cpu_to_scr(np->squeue_ba);
+	scriptb0->done_pos[0]	= cpu_to_scr(np->dqueue_ba);
+	scriptb0->targtbl[0]	= cpu_to_scr(np->targtbl_ba);
+
+	/*
+	 *  Remove the load of SCNTL4 on reselection if not a C10.
+	 */
+	if (!(np->features & FE_C10)) {
+		scripta0->resel_scntl4[0] = cpu_to_scr(SCR_NO_OP);
+		scripta0->resel_scntl4[1] = cpu_to_scr(0);
+	}
+
+	/*
+	 *  Remove a couple of work-arounds specific to C1010 if 
+	 *  they are not desirable. See `sym_fw2.h' for more details.
+	 */
+	if (!(np->device_id == PCI_ID_LSI53C1010_2 &&
+	      np->revision_id < 0x1 &&
+	      np->pciclk_khz < 60000)) {
+		scripta0->datao_phase[0] = cpu_to_scr(SCR_NO_OP);
+		scripta0->datao_phase[1] = cpu_to_scr(0);
+	}
+	if (!(np->device_id == PCI_ID_LSI53C1010 &&
+	      /* np->revision_id < 0xff */ 1)) {
+		scripta0->sel_done[0] = cpu_to_scr(SCR_NO_OP);
+		scripta0->sel_done[1] = cpu_to_scr(0);
+	}
+
+	/*
+	 *  Patch some other variables in SCRIPTS.
+	 *  These ones are loaded by the SCRIPTS processor.
+	 */
+	scriptb0->pm0_data_addr[0] =
+		cpu_to_scr(np->scripta_ba + 
+			   offsetof(struct sym_fw2a_scr, pm0_data));
+	scriptb0->pm1_data_addr[0] =
+		cpu_to_scr(np->scripta_ba + 
+			   offsetof(struct sym_fw2a_scr, pm1_data));
+}
+
+/*
+ *  Fill the data area in scripts.
+ *  To be done for all firmwares.
+ */
+static void
+sym_fw_fill_data (u32 *in, u32 *out)
+{
+	int	i;
+
+	for (i = 0; i < SYM_CONF_MAX_SG; i++) {
+		*in++  = SCR_CHMOV_TBL ^ SCR_DATA_IN;
+		*in++  = offsetof (struct sym_dsb, data[i]);
+		*out++ = SCR_CHMOV_TBL ^ SCR_DATA_OUT;
+		*out++ = offsetof (struct sym_dsb, data[i]);
+	}
+}
+
+/*
+ *  Setup useful script bus addresses.
+ *  To be done for all firmwares.
+ */
+static void 
+sym_fw_setup_bus_addresses(hcb_p np, struct sym_fw *fw)
+{
+	u32 *pa;
+	u_short *po;
+	int i;
+
+	/*
+	 *  Build the bus address table for script A 
+	 *  from the script A offset table.
+	 */
+	po = (u_short *) fw->a_ofs;
+	pa = (u32 *) &np->fwa_bas;
+	for (i = 0 ; i < sizeof(np->fwa_bas)/sizeof(u32) ; i++)
+		pa[i] = np->scripta_ba + po[i];
+
+	/*
+	 *  Same for script B.
+	 */
+	po = (u_short *) fw->b_ofs;
+	pa = (u32 *) &np->fwb_bas;
+	for (i = 0 ; i < sizeof(np->fwb_bas)/sizeof(u32) ; i++)
+		pa[i] = np->scriptb_ba + po[i];
+
+	/*
+	 *  Same for script Z.
+	 */
+	po = (u_short *) fw->z_ofs;
+	pa = (u32 *) &np->fwz_bas;
+	for (i = 0 ; i < sizeof(np->fwz_bas)/sizeof(u32) ; i++)
+		pa[i] = np->scriptz_ba + po[i];
+}
+
+#if	SYM_CONF_GENERIC_SUPPORT
+/*
+ *  Setup routine for firmware #1.
+ */
+static void 
+sym_fw1_setup(hcb_p np, struct sym_fw *fw)
+{
+	struct sym_fw1a_scr *scripta0;
+	struct sym_fw1b_scr *scriptb0;
+
+	scripta0 = (struct sym_fw1a_scr *) np->scripta0;
+	scriptb0 = (struct sym_fw1b_scr *) np->scriptb0;
+
+	/*
+	 *  Fill variable parts in scripts.
+	 */
+	sym_fw_fill_data(scripta0->data_in, scripta0->data_out);
+
+	/*
+	 *  Setup bus addresses used from the C code..
+	 */
+	sym_fw_setup_bus_addresses(np, fw);
+}
+#endif	/* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ *  Setup routine for firmware #2.
+ */
+static void 
+sym_fw2_setup(hcb_p np, struct sym_fw *fw)
+{
+	struct sym_fw2a_scr *scripta0;
+	struct sym_fw2b_scr *scriptb0;
+
+	scripta0 = (struct sym_fw2a_scr *) np->scripta0;
+	scriptb0 = (struct sym_fw2b_scr *) np->scriptb0;
+
+	/*
+	 *  Fill variable parts in scripts.
+	 */
+	sym_fw_fill_data(scripta0->data_in, scripta0->data_out);
+
+	/*
+	 *  Setup bus addresses used from the C code..
+	 */
+	sym_fw_setup_bus_addresses(np, fw);
+}
+
+/*
+ *  Allocate firmware descriptors.
+ */
+#if	SYM_CONF_GENERIC_SUPPORT
+static struct sym_fw sym_fw1 = SYM_FW_ENTRY(sym_fw1, "NCR-generic");
+#endif	/* SYM_CONF_GENERIC_SUPPORT */
+static struct sym_fw sym_fw2 = SYM_FW_ENTRY(sym_fw2, "LOAD/STORE-based");
+
+/*
+ *  Find the most appropriate firmware for a chip.
+ */
+struct sym_fw * 
+sym_find_firmware(struct sym_pci_chip *chip)
+{
+	if (chip->features & FE_LDSTR)
+		return &sym_fw2;
+#if	SYM_CONF_GENERIC_SUPPORT
+	else if (!(chip->features & (FE_PFEN|FE_NOPM|FE_DAC)))
+		return &sym_fw1;
+#endif
+	else
+		return 0;
+}
+
+/*
+ *  Bind a script to physical addresses.
+ */
+void sym_fw_bind_script (hcb_p np, u32 *start, int len)
+{
+	u32 opcode, new, old, tmp1, tmp2;
+	u32 *end, *cur;
+	int relocs;
+
+	cur = start;
+	end = start + len/4;
+
+	while (cur < end) {
+
+		opcode = *cur;
+
+		/*
+		 *  If we forget to change the length
+		 *  in scripts, a field will be
+		 *  padded with 0. This is an illegal
+		 *  command.
+		 */
+		if (opcode == 0) {
+			printf ("%s: ERROR0 IN SCRIPT at %d.\n",
+				sym_name(np), (int) (cur-start));
+			MDELAY (10000);
+			++cur;
+			continue;
+		};
+
+		/*
+		 *  We use the bogus value 0xf00ff00f ;-)
+		 *  to reserve data area in SCRIPTS.
+		 */
+		if (opcode == SCR_DATA_ZERO) {
+			*cur++ = 0;
+			continue;
+		}
+
+		if (DEBUG_FLAGS & DEBUG_SCRIPT)
+			printf ("%d:  <%x>\n", (int) (cur-start),
+				(unsigned)opcode);
+
+		/*
+		 *  We don't have to decode ALL commands
+		 */
+		switch (opcode >> 28) {
+		case 0xf:
+			/*
+			 *  LOAD / STORE DSA relative, don't relocate.
+			 */
+			relocs = 0;
+			break;
+		case 0xe:
+			/*
+			 *  LOAD / STORE absolute.
+			 */
+			relocs = 1;
+			break;
+		case 0xc:
+			/*
+			 *  COPY has TWO arguments.
+			 */
+			relocs = 2;
+			tmp1 = cur[1];
+			tmp2 = cur[2];
+			if ((tmp1 ^ tmp2) & 3) {
+				printf ("%s: ERROR1 IN SCRIPT at %d.\n",
+					sym_name(np), (int) (cur-start));
+				MDELAY (10000);
+			}
+			/*
+			 *  If PREFETCH feature not enabled, remove 
+			 *  the NO FLUSH bit if present.
+			 */
+			if ((opcode & SCR_NO_FLUSH) &&
+			    !(np->features & FE_PFEN)) {
+				opcode = (opcode & ~SCR_NO_FLUSH);
+			}
+			break;
+		case 0x0:
+			/*
+			 *  MOVE/CHMOV (absolute address)
+			 */
+			if (!(np->features & FE_WIDE))
+				opcode = (opcode | OPC_MOVE);
+			relocs = 1;
+			break;
+		case 0x1:
+			/*
+			 *  MOVE/CHMOV (table indirect)
+			 */
+			if (!(np->features & FE_WIDE))
+				opcode = (opcode | OPC_MOVE);
+			relocs = 0;
+			break;
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+		case 0x2:
+			/*
+			 *  MOVE/CHMOV in target role (absolute address)
+			 */
+			opcode &= ~0x20000000;
+			if (!(np->features & FE_WIDE))
+				opcode = (opcode & ~OPC_TCHMOVE);
+			relocs = 1;
+			break;
+		case 0x3:
+			/*
+			 *  MOVE/CHMOV in target role (table indirect)
+			 */
+			opcode &= ~0x20000000;
+			if (!(np->features & FE_WIDE))
+				opcode = (opcode & ~OPC_TCHMOVE);
+			relocs = 0;
+			break;
+#endif
+		case 0x8:
+			/*
+			 *  JUMP / CALL
+			 *  dont't relocate if relative :-)
+			 */
+			if (opcode & 0x00800000)
+				relocs = 0;
+			else if ((opcode & 0xf8400000) == 0x80400000)/*JUMP64*/
+				relocs = 2;
+			else
+				relocs = 1;
+			break;
+		case 0x4:
+		case 0x5:
+		case 0x6:
+		case 0x7:
+			relocs = 1;
+			break;
+		default:
+			relocs = 0;
+			break;
+		};
+
+		/*
+		 *  Scriptify:) the opcode.
+		 */
+		*cur++ = cpu_to_scr(opcode);
+
+		/*
+		 *  If no relocation, assume 1 argument 
+		 *  and just scriptize:) it.
+		 */
+		if (!relocs) {
+			*cur = cpu_to_scr(*cur);
+			++cur;
+			continue;
+		}
+
+		/*
+		 *  Otherwise performs all needed relocations.
+		 */
+		while (relocs--) {
+			old = *cur;
+
+			switch (old & RELOC_MASK) {
+			case RELOC_REGISTER:
+				new = (old & ~RELOC_MASK) + np->mmio_ba;
+				break;
+			case RELOC_LABEL_A:
+				new = (old & ~RELOC_MASK) + np->scripta_ba;
+				break;
+			case RELOC_LABEL_B:
+				new = (old & ~RELOC_MASK) + np->scriptb_ba;
+				break;
+			case RELOC_SOFTC:
+				new = (old & ~RELOC_MASK) + np->hcb_ba;
+				break;
+			case 0:
+				/*
+				 *  Don't relocate a 0 address.
+				 *  They are mostly used for patched or 
+				 *  script self-modified areas.
+				 */
+				if (old == 0) {
+					new = old;
+					break;
+				}
+				/* fall through */
+			default:
+				new = 0;
+				panic("sym_fw_bind_script: "
+				      "weird relocation %x\n", old);
+				break;
+			}
+
+			*cur++ = cpu_to_scr(new);
+		}
+	};
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)