[RFC][PATCH 8/13] Equinox SST driver: driver init and shutdown

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Adds Equinox multi-port serial (SST) driver.

Part 8: new source file: drivers/char/eqnx/sst.c.  Driver "main".  Does
the
initialization and cleanup for the driver.  Verifies and initializes any
discovered SST boards.

Major number and TTY devices names have been lanana assigned.

Signed-off-by: Mike Straub <[email protected]>

---
 sst.c | 1626
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1626 insertions(+)

diff -Naurp -X dontdiff linux-2.6.17/drivers/char/eqnx/sst.c
linux-2.6.17.eqnx/drivers/char/eqnx/sst.c
--- linux-2.6.17/drivers/char/eqnx/sst.c	1969-12-31
19:00:00.000000000 -0500
+++ linux-2.6.17.eqnx/drivers/char/eqnx/sst.c	2006-06-20
09:50:08.000000000 -0400
@@ -0,0 +1,1626 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * This driver supports the PCI models of the Equinox / Avocent SST
boards
+ * using SSP-4 and SSP-64 ASIC technology
+ * Boards supported:
+ * SSP-4P
+ * SSP-8P
+ * SSP-16P
+ * SSP-64P
+ * SSP-128P
+ *
+ * Currently maintained by mike straub <[email protected]>
+ */
+
+/*
+ * driver "main" - discovers and initializes all SST boards
+ */
+
+char eqnx_version[] = "Equinox / Avocent SuperSerial Technology Device
Driver";
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#ifdef CONFIG_MODVERSIONS
+#define MODVERSIONS	1
+#endif
+
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#ifdef  MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+#include <linux/vermagic.h>
+#include <linux/compiler.h>
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+static const struct modversion_info ____versions[]
+    __attribute__ ((section("__versions"))) = {
+};
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/list.h>
+#include <linux/isapnp.h>
+#include <asm/io.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/kdev_t.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/device.h>
+#include <linux/pci_ids.h>
+
+#include "eqnx_def.h"
+#include "eqnx.h"
+#include "icp.h"
+
+/**********************************************************************
***
+ *
+ * global variables and structures
+ *
+
************************************************************************
*/
+
+/* maximum number of boards, MAXSSP may be redefined */
+static int maxbrd = MAXSSP;
+
+/* number of boards and ICPs found */
+int eqnx_nssps = 0;
+int eqnx_nicps = 0;
+
+/* adapter structures - one for each board */
+struct mpdev eqnx_dev[MAXSSP];
+
+/* channel structures - one for each channel */
+struct mpchan *eqnx_chan;
+
+/* tty driver and termios structs */
+static struct tty_driver *eqnx_driver;
+
+/* default termios */
+static struct termios eqnx_deftermios = {
+	.c_iflag = 0,
+	.c_oflag = 0,
+	.c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+	.c_lflag = 0,
+	.c_cc = INIT_C_CC
+};
+
+/* major numbers */
+static int din_num;
+
+/*
+ * per-channel hardware queue information
+ * index 0 for SSP64 boards, index 1 for SSP4 boards
+ */
+static struct hwq_struct sst_hwq[] = {
+	{HWQ4SIZE, HWQ4HIWAT, HWQ4LOWAT, HWQ4RXWRAP, HWQ4TXWRAP,
HWQ4CMDSIZE},
+	{HWQ1SIZE, HWQ1HIWAT, HWQ1LOWAT, HWQ1RXWRAP, HWQ1TXWRAP,
HWQ1CMDSIZE},
+};
+
+/* semaphores and timers */
+struct timer_list eqnx_timer;
+
+/* local buffer for copying output characters. Used in eqnx_put_char */
+char *eqnx_txcookbuf = (char *)NULL;
+
+/* initial - unknown board defintion */
+static struct brdtab_t unknown_board = {
+	NOID, NOID, 0, 1, 0, 0, "Unknown"
+};
+
+/* board definition tables */
+static struct brdtab_t board_table[] = {
+	{0x8, 0x8, SSP64, 1, 64, POLL40, "SST-64P"},
+	{0x10, 0x10, SSP64, 2, 128, POLL40, "SST-128P"},
+	{0x14, 0x88, SSP4, 2, 8, RJ, "SST-8P/RJ"},
+	{0x14, 0x90, SSP4, 2, 8, RJ, "SST-8P/RJ"},
+	{0x68, 0x8, SSP64, 1, 64, POLL40, "SST-64P (HP)"},
+	{0x70, 0x10, SSP64, 2, 128, POLL40, "SST-128P (HP)"},
+	{0x88, 0x88, SSP4, 1, 4, 0, "SST-4P"},
+	{0x8C, 0x88, SSP4, 1, 4, RJ, "SST-4P/RJ"},
+	{0x90, 0x90, SSP4, 2, 8, 0, "SST-8P"},
+	{0x94, 0x90, SSP4, 2, 8, RJ, "SST-8P/RJ"},
+	{0x98, 0x98, SSP4, 2, 8, MM, "SST-MM8P"},
+	{0x9C, 0x98, SSP4, 1, 4, MM, "SST-MM4P"},
+	{0xA0, 0x88, SSP4, 1, 8, 0, "SST-4C 8"},
+	{0xA4, 0x88, SSP4, 1, 4, 0, "SST-4C 4"},
+	{0xAC, 0x88, SSP4, 1, 4, 0, "SST-4C 0"},
+	{0xB0, 0x90, SSP4, 2, 8, 0, "SST-8C 8"},
+	{0xB4, 0x90, SSP4, 2, 4, 0, "SST-8C 4"},
+	{0xB8, 0x88, SSP4, 1, 4, LP, "SST-4P/LP"},
+	{0xBC, 0x90, SSP4, 2, 8, 0, "SST-8C 0"},
+	{0xC0, 0x80, SSP4, 4, 16, DB25_PAN, "SST-16P CP16-DB"},
+	{0xC4, 0x80, SSP4, 4, 16, RJ_PAN, "SST-16P CP16-RJ"},
+	{0xC8, 0x80, SSP4, 4, 16, NOPANEL, "SST-16P No panel"},
+	{0xD0, 0x80, SSP4, 4, 16, DB9_PAN, "SST-16P CP16-DB9"},
+	{0xD4, 0x80, SSP4, 2, 8, 0, "SST-8P-DB"},
+	{0xEC, 0x88, SSP4, 1, 4, 0, "SST-4P,PWR"},
+	{0xF0, 0x90, SSP4, 2, 8, 0, "SST-8P (HP)"},
+	{0xF4, 0x90, SSP4, 2, 8, 0, "SST-8P,PWR"},
+	{0xFC, 0x88, SSP4, 1, 4, LP, "SST-4P/ULP"},
+};
+
+#ifdef	MODULE
+static struct pci_device_id eqnx_pcibrds[] = {
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST64P, PCI_ANY_ID,
PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST128P, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PRJ, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PRJ2, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST64PHP, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST128PHP, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST4P, PCI_ANY_ID,
PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST4PRJ, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8P, PCI_ANY_ID,
PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PRJ3, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SSTMM8P, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SSTMM4P, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST4PC8, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST4PC4, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST4PC0, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PC8, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PC4, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST4PLP, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PC0, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST16PDB, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST16PRJ, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST16PNP, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST16PDB9, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PDB, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST4PPWR, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PHP, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST8PPWR, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{PCI_VENDOR_ID_EQNX, PCI_DEVICE_ID_EQNX_SST4PULP, PCI_ANY_ID,
+	 PCI_ANY_ID},
+	{0}
+};
+
+MODULE_DEVICE_TABLE(pci, eqnx_pcibrds);
+#endif
+
+/* total number of entries */
+static int brdtab_entries = sizeof board_table / sizeof(struct
brdtab_t);
+
+static char *eqnPCIcfg;
+
+/**********************************************************************
***
+ *
+ * miscellaneous definitions
+ *
+
************************************************************************
*/
+
+/* 16K register space for each ICP */
+#define	HWREGSLEN	0x4000
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
+
+/**********************************************************************
***
+ *
+ * module declarations
+ *
+
************************************************************************
*/
+
+#ifdef MODULE
+MODULE_AUTHOR("Mike Straub - Avocent Corporation");
+MODULE_DESCRIPTION("Equinox/Avocent SST Driver");
+MODULE_LICENSE("GPL");
+#endif
+
+/**********************************************************************
***
+ *
+ * function declarations
+ *
+
************************************************************************
*/
+
+static __init void brd_mem_cfg(struct mpdev *mpd);
+static int eqnx_pcifindbrds(struct pci_cfg *);
+
+static __init int mem_zero(struct mpdev *mpd, unsigned short *ramw,
+			   int testlen);
+static __init int mem_test(struct mpdev *mpd, int icp);
+static __init int mem_test_dram(struct mpdev *mpd, u16 * ramw, int
testlen);
+static __init int mem_test_pram(struct mpdev *mpd, u16 * ramw, int
testlen);
+static __init int mem_test_tag(struct mpdev *mpd, u8 * ramb, int
testlen);
+
+static __init int register_eqnx(void);
+
+extern void eqnx_chnl_sync(struct mpchan *mpc);
+extern void sstpoll(unsigned long arg);
+extern int eqnx_open(struct tty_struct *, struct file *);
+extern void eqnx_close(struct tty_struct *, struct file *);
+extern int eqnx_write(struct tty_struct *, const unsigned char *, int);
+extern void eqnx_put_char(struct tty_struct *, unsigned char);
+extern void eqnx_flush_chars(struct tty_struct *);
+extern int eqnx_write_room(struct tty_struct *);
+extern int eqnx_chars_in_buffer(struct tty_struct *);
+extern int eqnx_chars_in_buffer(struct tty_struct *);
+extern void eqnx_throttle(struct tty_struct *);
+extern void eqnx_unthrottle(struct tty_struct *);
+extern void eqnx_flush_buffer(struct tty_struct *);
+extern void eqnx_stop(struct tty_struct *);
+extern void eqnx_start(struct tty_struct *);
+extern void eqnx_hangup(struct tty_struct *);
+extern void eqnx_set_termios(struct tty_struct *, struct termios *);
+extern int eqnx_tiocmget(struct tty_struct *, struct file *);
+extern int eqnx_tiocmset(struct tty_struct *, struct file *, unsigned
int,
+			 unsigned int);
+extern int eqnx_ioctl(struct tty_struct *, struct file *, unsigned int,
+		      unsigned long);
+
+extern void eqnx_create_sysfs(struct device *);
+extern void eqnx_remove_sysfs(struct device *);
+extern void eqnx_create_tty_sysfs(struct class_device *);
+extern void eqnx_remove_tty_sysfs(struct class_device *);
+
+/**********************************************************************
***
+ *
+ * tty interface struct
+ *
+
************************************************************************
*/
+static struct tty_operations eqnx_ops = {
+	.open = eqnx_open,
+	.close = eqnx_close,
+	.write = eqnx_write,
+	.put_char = eqnx_put_char,
+	.flush_chars = eqnx_flush_chars,
+	.write_room = eqnx_write_room,
+	.chars_in_buffer = eqnx_chars_in_buffer,
+	.throttle = eqnx_throttle,
+	.unthrottle = eqnx_unthrottle,
+	.flush_buffer = eqnx_flush_buffer,
+	.stop = eqnx_stop,
+	.start = eqnx_start,
+	.hangup = eqnx_hangup,
+	.set_termios = eqnx_set_termios,
+	.ioctl = eqnx_ioctl,
+	.tiocmget = eqnx_tiocmget,
+	.tiocmset = eqnx_tiocmset,
+};
+
+/*
+ * SSTMINOR(maj, min)
+ * return channel index using major and minor numbers
+ *
+ * maj	= major number
+ * min	= minor number
+ */
+int SSTMINOR(unsigned int maj, unsigned int min)
+{
+	if (maj != din_num)
+		return (-1);
+
+	return (min);
+}
+
+/*
+ * find_board_def(id)
+ *
+ *	return pointer to board definition entry for the board
+ *	with the specified full (16-bit) id.
+ *
+ *	returns NULL if not found.
+ */
+static struct brdtab_t *find_board_def(unsigned short id)
+{
+	int i;
+	struct brdtab_t *brdtab_ptr = NULL;
+	unsigned char primary_id = NOID, secondary_id = NOID;
+
+	primary_id = id & 0xFC;
+	secondary_id = (id & 0xFF00) >> 8;
+
+	/*
+	 * mask off rev-id bits, special case
+	 * chrysler board
+	 */
+	if ((primary_id != 0x14) && (secondary_id != 0x88) &&
+	    (primary_id < 0x80))
+		primary_id &= 0xF8;
+
+	/*
+	 * Now search the board table for a matching entry
+	 */
+	for (i = 0; i < brdtab_entries; i++) {
+		brdtab_ptr = &board_table[i];
+
+		if (brdtab_ptr->primary_id == primary_id) {
+			if ((brdtab_ptr->secondary_id == secondary_id)
||
+			    (brdtab_ptr->secondary_id == NOID) ||
+			    (secondary_id == NOID)) {
+				break;
+			}
+		}
+	}
+
+	if (i >= brdtab_entries)
+		brdtab_ptr = NULL;
+
+	return brdtab_ptr;
+}
+
+/*
+ * eqnx_init(kmem_start)
+ *
+ * initialization routine
+ */
+static int __init eqnx_init(void)
+{
+	struct mpdev *mpd;
+	struct mpchan *mpc;
+	volatile struct icp_in_struct *icpi;
+	volatile struct icp_out_struct *icpo;
+	volatile union global_regs_u *icpg = NULL;
+	volatile struct cout_que_struct *icpq;
+	struct icp_struct *icp;
+	volatile struct hwq_struct *hwq;
+	mpaddr_t mp;
+	int i, j, k, lmx, ii, jj, addr, duplicate, di, addr1, nextmin;
+	u8 cchnl;
+	volatile unsigned char *chnl_ptr;
+	u16 no_cache, no_icp, nxt_dma, attn_ena, status, cntrl_sig;
+	struct pci_cfg *cfgp;
+	unsigned short numboards = 0;
+	void *base_addr;
+	unsigned char config, rev_id, c_code;
+	unsigned long flags;
+	int ssp_channels = 0, lmx_factor, pcicfg_size;
+	volatile u8 *bus_ctrl_p;
+
+	printk(KERN_INFO "Loading %s Version %s\n", eqnx_version,
VERSNUM);
+
+	/* initialize board structs */
+	for (ii = 0; ii < maxbrd; ii++) {
+		eqnx_dev[ii].mpd_alive = 0;
+		spin_lock_init(&eqnx_dev[ii].mpd_lock);
+	}
+
+	eqnx_nssps = 0;
+	eqnx_nicps = 0;
+	nextmin = 0;
+
+	pcicfg_size = sizeof(struct pci_cfg) * maxbrd;
+	eqnPCIcfg = (char *)kmalloc(pcicfg_size, GFP_KERNEL);
+	if (eqnPCIcfg == (char *)NULL) {
+		printk(KERN_ERR "eqnx_init: Failed eqnPCIcfg allocate of
"
+		       "size %d\n", pcicfg_size);
+		return (-ENOMEM);
+	}
+
+	memset(eqnPCIcfg, 0, pcicfg_size);
+	cfgp = (struct pci_cfg *)&eqnPCIcfg[0];
+	numboards = eqnx_pcifindbrds(cfgp);
+
+	if (numboards) {
+		for (i = 0; i < numboards; i++, cfgp++) {
+			mpd = &eqnx_dev[eqnx_nssps];
+
+			mpd->mpd_pdev = cfgp->pdev;
+			mpd->mpd_board_def =
find_board_def(mpd->mpd_pdev->
+							    device);
+
+			if (mpd->mpd_board_def == NULL)
+				mpd->mpd_board_def = &unknown_board;
+
+			mpd->dev = &mpd->mpd_pdev->dev;
+			eqnx_create_sysfs(mpd->dev);
+			dev_info(mpd->dev, "eqnx SST brd: id %4.4x
(%s)\n",
+				 mpd->mpd_pdev->device,
+				 mpd->mpd_board_def->name);
+
+			config = cfgp->command;
+			rev_id = cfgp->rev_id;
+			base_addr =
+			    (void *)((unsigned long)cfgp->base_addr_reg0
&
+				     PCI_BASE_ADDR_MASK);
+			/*
+			 * This code checks for duplicate boards.
+			 * This happens because some brain-dead
+			 * PCI controllers "see" multiple busses
+			 * when there aren't multiple busses.
+			 * So, they see the same bus multiple times,
+			 * reporting our board on all the busses.
+			 */
+			duplicate = 0;
+			for (di = 0; di < i; di++) {
+				struct mpdev *mpd = &eqnx_dev[di];
+
+				if (base_addr == mpd->mpd_pmem) {
+
+					duplicate = 1;
+					break;
+				}
+			}
+			if (duplicate)
+				continue;
+			/* Make sure base address was really configured
*/
+			if (!base_addr) {
+				dev_err(mpd->dev, "PCI Base Addr not "
+					"configured.\n");
+				continue;
+			}
+
+			/* 
+			 * Look at the Memory Space Control bit to
+			 * see if this has been configured.
+			 */
+			if (!(config & PCI_MSC))
+				continue;
+
+			/* Make sure we don't find more boards than the
max. */
+			if (eqnx_nssps >= maxbrd) {
+				mpd->mpd_cnfg_state = CNFG_STATE_FAIL;
+				mpd->mpd_cnfg_fail = CNFG_FAIL_MAXBRD;
+				continue;
+			} else
+				mpd->mpd_cnfg_state = CNFG_STATE_OK;
+
+			/* Mark board as being alive */
+			mpd->mpd_alive = true;
+
+			/* revision */
+			mpd->mpd_rev = rev_id;
+
+			mpd->mpd_nicps =
mpd->mpd_board_def->number_of_asics;
+			eqnx_nicps += mpd->mpd_nicps;
+			mpd->mpd_lmx_index = 0;
+
+			/* Save the physical address */
+			mpd->mpd_pmem = base_addr;
+
+			/* Get the memory size */
+			if (mpd->mpd_board_def->number_of_ports >= 64)
+				mpd->mpd_addrsz = FLAT128_MEM_LEN;
+			else
+				mpd->mpd_addrsz = FLAT64K_MEM_LEN;
+
+			if (mpd->mpd_board_def->number_of_asics == 4)
+				/* SSP4 SST-16P */
+				mpd->mpd_memsz = mpd->mpd_addrsz;
+			else	/* DRAM memory size is one-half memory
size */
+				mpd->mpd_memsz = mpd->mpd_addrsz / 2;
+
+			/* PCI memory width is always 32 */
+			mpd->mpd_mem_width = 32;
+
+			mpd->mpd_nchan =
mpd->mpd_board_def->number_of_ports;
+			mpd->mpd_minor = nextmin;
+			nextmin += MAXCHNL_BRD;
+
+			eqnx_nssps++;
+		}
+	}
+
+	if (!eqnx_nssps) {
+		printk(KERN_INFO "eqnx_init: No SST boards found.\n");
+		return 0;
+	}
+
+	eqnx_chan = (struct mpchan *)vmalloc(sizeof(struct mpchan) *
+					     eqnx_nssps * MAXCHNL_BRD);
+	if (eqnx_chan == (struct mpchan *)NULL) {
+		printk(KERN_ERR "eqnx_init: Failed eqnx_chan allocate of
"
+		       "size %d\n",
+		       (sizeof(struct mpchan) * eqnx_nssps *
MAXCHNL_BRD));
+		return (-ENOMEM);
+	}
+	memset(eqnx_chan, 0, (sizeof(struct mpchan) * eqnx_nssps *
+			      MAXCHNL_BRD));
+	mpc = eqnx_chan;
+	for (k = 0; k < eqnx_nssps; k++) {
+		/* map Adapter memory */
+		mpd = &eqnx_dev[k];
+		/* skip board if dead */
+		if (mpd->mpd_alive == 0)
+			continue;
+		mpd->mpd_mpc = (struct mpchan *)&eqnx_chan[k *
MAXCHNL_BRD];
+
+		mpd->mpd_mem = ioremap((unsigned long)mpd->mpd_pmem,
+				       mpd->mpd_addrsz);
+		if (!mpd->mpd_mem) {
+			dev_err(mpd->dev, "eqnx_init: Memory map failed
"
+				"for board %d\n", k + 1);
+			/* Driver init failed */
+			mpd->mpd_cnfg_state = CNFG_STATE_FAIL;
+			mpd->mpd_cnfg_fail = CNFG_FAIL_MEMORY;
+			mpd->mpd_alive = false;
+			continue;
+		}
+		brd_mem_cfg(mpd);
+		hwq = mpd->mpd_hwq;
+
+		if (mpd->mpd_board_def->asic == SSP64)
+			/* SSP-64 */
+			mpd->mpd_sspchan = MAXCHNL_ICP;
+		else
+			/* SSP-4 */
+			mpd->mpd_sspchan = 4;
+
+		/* loop through each ICP */
+		for (j = 0; j < (int)mpd->mpd_nicps; j++) {
+			int mux;
+
+			/* reset ICP to known state */
+			icp = &mpd->icp[j];
+			icp->icp_minor_start = mpd->mpd_minor +
+			    (j * mpd->mpd_sspchan);
+			if (mpd->mpd_board_def->asic == SSP64) {
+				/* SSP-64 */
+				icpg = (volatile union global_regs_u *)
+				    ((unsigned long)icp->icp_regs_start
+
+				     0x2000);
+				icpg->ssp.gicp_attn = 0;
+				icpg->ssp.gicp_initiate = 0;
+				bus_ctrl_p =
&(icpg->ssp.gicp_bus_cntrl);
+
+				/*
+				 * set up Global Bus Control register
with
+				 * the CPU bus width (32 bit bus) and
the
+				 * 40 bit DRAM bit
+				 */
+				if (!j)
+					*bus_ctrl_p =
+					    (STERNG | DRAM_40 |
CPU_BUS_32);
+				else
+					*bus_ctrl_p = (DRAM_40 |
CPU_BUS_32);
+			} else {
+				/* SSP-4 */
+				volatile union global_regs_u *icp_glo =
+				    (volatile union global_regs_u *)
+				    ((unsigned long)icp->icp_regs_start
+
+				     0x400);
+				volatile struct icp_out_struct *icp_cout
=
+				    (volatile struct icp_out_struct *)
+				    ((unsigned long)icp->icp_regs_start
+
+				     0x200);
+				volatile struct icp_in_struct *icp_cin =
+				    (volatile struct icp_in_struct *)
+				    (icp->icp_regs_start);
+
+				icp_glo->ssp4.on_line = 0;
+				/* lock input and output */
+				icp_cin->cin_locks = 0xFF;
+				icp_cout->cout_lck_cntrl = 0x77;
+				/* Set Bus Control = 16 bits */
+				bus_ctrl_p = &(icp_glo->ssp4.bus_cntrl);
+				*bus_ctrl_p = (BUS_CNTRL_16 |
BUS_CNTRL_WR);
+
+				ssp_channels = 4;
+
+				for (ii = 0; ii < ssp_channels; ii++) {
+					((volatile struct icp_in_struct
*)
+					 ((char *)icp_cin +
ii))->cin_locks =
+					    0xFF;
+					((volatile struct icp_out_struct
*)
+					 ((char *)icp_cout +
+					  ii))->cout_lck_cntrl = 0x77;
+					SSTWR16(((volatile struct
+						  icp_out_struct *)
+						 ((char *)icp_cout +
+						  ii))->cout_cntrl_sig,
0x0F);
+				}
+				icp_glo->ssp4.bus_cntrl = BUS_CNTRL_16;
+				icp_glo->ssp4.on_line = 0;
+			}
+
+			if ((ii = mem_test(mpd, j))) {
+				dev_err(mpd->dev, "eqnx_init: Memory
test "
+					"failed for SST board %d ICP
%d\n",
+					k + 1, j + 1);
+				mpd->mpd_cnfg_state = CNFG_STATE_FAIL;
+				mpd->mpd_cnfg_fail = CNFG_FAIL_MEM_FAIL;
+				mpd->mpd_alive = false;
+				continue;
+			}
+
+			/* verify that pram is not cached */
+			if (mpd->mpd_board_def->asic == SSP64) {
+				/* SSP-64 board */
+				icpg = (volatile union global_regs_u *)
+				    ((unsigned long)icp->icp_regs_start
+
+				     0x2000);
+				/* verify that pram is not cached */
+				cchnl = icpg->ssp.gicp_chan;
+				chnl_ptr = &(icpg->ssp.gicp_chan);
+			} else {
+				/* SSP-4 board */
+				struct icp_struct *icp;
+				volatile union global_regs_u *icp_glo;
+				icp = &mpd->icp[j];
+				icp_glo = (volatile union global_regs_u
*)
+				    ((unsigned long)icp->icp_regs_start
+
+				     0x400);
+				cchnl = icp_glo->ssp4.chan_ctr;
+				chnl_ptr = &(icp_glo->ssp4.chan_ctr);
+			}
+			no_cache = false;
+			for (ii = 0; ii < 0x100000; ii++) {
+				if (*chnl_ptr != cchnl) {
+					no_cache = true;
+					break;
+				}
+			}
+			if (!no_cache) {
+				dev_err(mpd->dev, "eqnx_init: PRAM
memory "
+					"appears to be cached %lx.\n",
+					(unsigned long)mpd->mpd_mem);
+				/* Driver init failed */
+				mpd->mpd_cnfg_state = CNFG_STATE_FAIL;
+				mpd->mpd_cnfg_fail =
CNFG_FAIL_PRAM_FAIL;
+				mpd->mpd_alive = false;
+				continue;
+			}
+
+			lmx = 0;
+			mp = (mpaddr_t) mpd->icp[j].icp_regs_start;
+
+			if (mpd->mpd_board_def->asic == SSP64) {
+				if (mpd->mpd_board_def->number_of_asics
> 0)
+					/* Number per ICP */
+					ssp_channels = 64;
+				else
+					ssp_channels = 0;
+			}
+
+			if (mpd->mpd_board_def->asic != SSP64) {
+				/* SSP-4 */
+				struct icp_struct *icp;
+				volatile union global_regs_u *icp_glo;
+				icp = &mpd->icp[j];
+				icp_glo = (volatile union global_regs_u
*)
+				    ((unsigned long)icp->icp_regs_start
+
+				     0x400);
+				cchnl = icp_glo->ssp4.chan_ctr;
+				chnl_ptr = &(icp_glo->ssp4.chan_ctr);
+				for (ii = 0; ii < 0x10000; ii++)
+					if (*chnl_ptr != cchnl)
+						break;
+				icp_glo->ssp4.bus_cntrl = 0xCD;
+				cchnl = icp_glo->ssp4.chan_ctr;
+				for (ii = 0; ii < 0x10000; ii++)
+					if (*chnl_ptr != cchnl)
+						break;
+			}
+
+			/* mux control sigs on SST-16P */
+			mux = 0;
+			if ((mpd->mpd_board_def->asic != SSP64) &&
+			    (mpd->mpd_board_def->number_of_ports == 16))
+				mux = 1;
+
+			/* loop through each channel */
+			for (i = 0; i < ssp_channels; i++) {
+				/*
+				 * setup mpc virtual addresses for cpu
access 
+				 * of icp
+				 */
+				jj = mpd->mpd_minor + i +
+				    (j * mpd->mpd_sspchan);
+				mpc = &eqnx_chan[jj];
+				mpc->mpc_brdno = k;
+				mpc->mpc_chan = i;
+				mpc->mpc_icpi =
+				    (volatile struct icp_in_struct *)
+				    &mp->mp_icpi[i];
+				if (mpd->mpd_board_def->asic == SSP64)
+					mpc->mpc_icpo =
+					    (volatile struct
icp_out_struct *)
+					    &mp->mp_icpo[i];
+				else
+					mpc->mpc_icpo =
+					    (volatile struct
icp_out_struct *)
+					    ((unsigned char
*)(mpc->mpc_icpi) +
+					     0x200);
+				mpc->mpc_mpd = mpd;
+				mpc->mpc_icp = (struct icp_struct *)
+				    &mpd->icp[j];
+				mpc->mpc_icpno = j;
+
+				if ((mpc->normaltermios = (struct
termios *)
+				     vmalloc(sizeof(struct termios))) ==
+				    (struct termios *)NULL) {
+					dev_err(mpd->dev, "eqnx_int:
Failed "
+						"normaltermios alloc of
"
+						"size %d\n",
+						sizeof(struct termios));
+					return (-ENOMEM);
+				}
+				memset(mpc->normaltermios, 0,
+				       sizeof(struct termios));
+				*mpc->normaltermios = eqnx_deftermios;
+				mpc->closing_wait = CLSTIMEO;
+				mpc->close_delay = EQNX_CLOSEDELAY;
+
+				/* initialize each of the wait queues */
+				mpc->open_wait_wait = 0;
+				init_waitqueue_head(&mpc->open_wait);
+				init_waitqueue_head(&mpc->close_wait);
+				init_waitqueue_head(&mpc->raw_wait);
+
+				mpc->mpc_input = 0;
+				mpc->mpc_output = 0;
+				mpc->mpc_cin_events = 0;
+				mpc->mpc_cout_events = 0;
+				mpc->mpc_cin_ena = 0;
+				mpc->mpc_cout_ena = 0;
+
+				mpc->mpc_parity_err_cnt = 0;
+				mpc->mpc_framing_err_cnt = 0;
+				mpc->mpc_break_cnt = 0;
+
+				icpi = mpc->mpc_icpi;
+				icpo = mpc->mpc_icpo;
+				if (mpd->mpd_board_def->asic == SSP64) {
+					/* SSP-64 */
+					icpg = (volatile union
global_regs_u *)
+					    icpo;
+					lmx_factor = 16;
+				} else {
+					icpg = (volatile union
global_regs_u *)
+					    ((unsigned long)icpi +
0x400);
+					lmx_factor = 4;
+				}
+				icpq = &icpo->cout_q0;
+				if (i == (0 * lmx_factor)) {
+					lmx = 0;
+					mpc->mpc_icp->lmx[lmx].lmx_mpc =
mpc;
+					mpc->mpc_icp->lmx[lmx].lmx_wait
= -1;
+				}
+
+				if (i == (1 * lmx_factor)) {
+					lmx = 1;
+					mpc->mpc_icp->lmx[lmx].lmx_mpc =
mpc;
+					mpc->mpc_icp->lmx[lmx].lmx_wait
= -1;
+				}
+
+				if (i == (2 * lmx_factor)) {
+					lmx = 2;
+					mpc->mpc_icp->lmx[lmx].lmx_mpc =
mpc;
+					mpc->mpc_icp->lmx[lmx].lmx_wait
= -1;
+				}
+
+				if (i == (3 * lmx_factor)) {
+					lmx = 3;
+					mpc->mpc_icp->lmx[lmx].lmx_mpc =
mpc;
+					mpc->mpc_icp->lmx[lmx].lmx_wait
= -1;
+				}
+				mpc->mpc_lmxno = lmx;
+
+				/* icp input - assign queues, etc. */
+				/* setup input hardware registers */
+				icpi->cin_locks = 0xff;
+				icpo->cout_lck_cntrl = 0xff;
+				spin_lock_irqsave(&mpd->mpd_lock,
flags);
+				eqnx_chnl_sync(mpc);
+				spin_unlock_irqrestore(&mpd->mpd_lock,
flags);
+				if (mpd->mpd_board_def->asic == SSP64) {
+					/* SSP64 */
+					addr = (i + j * MAXCHNL_ICP) * 2
*
+					    hwq->hwq_size;
+					icpi->cin_dma_hi = (addr >> 16);
+					nxt_dma = addr & 0xffff;
+				} else {
+					/* SSP4 */
+					addr = i * 0x400;
+					nxt_dma = addr & 0xffff;
+				}
+				SSTWR16(icpi->cin_bank_a.bank_nxt_dma,
nxt_dma);
+				SSTWR16(icpi->cin_bank_b.bank_nxt_dma,
nxt_dma);
+				SSTWR16(icpi->cin_tail_ptr_a, nxt_dma);
+				SSTWR16(icpi->cin_tail_ptr_b, nxt_dma);
+
+				/* setup mpc values for input registers
*/
+				mpc->mpc_rxq.q_begin = addr & 0xffff;
+				mpc->mpc_rxq.q_ptr = addr & 0xffff;
+				mpc->mpc_rxbase = 0;
+				mpc->mpc_rxpg = 0;
+				mpc->mpc_tgpg = 0;
+				mpc->mpc_rxq.q_end = (addr & 0xffff) +
+				    (hwq->hwq_size - 1);
+				mpc->mpc_rxq.q_size = hwq->hwq_size;
+				if (mpd->mpd_board_def->asic == SSP64) {
+					/* SSP64 */
+					mpc->mpc_tags = addr >> 4;
+					mpc->mpc_rxq.q_addr = (char *)
+					    (icp->icp_dram_start +
+					     ((i * 2 * hwq->hwq_size) &
+					      0xffff0000));
+				} else {
+					/* SSP4 */
+					mpc->mpc_tags = 0;
+					mpc->mpc_rxq.q_addr = (char *)
+					    (icp->icp_dram_start +
+					     ((i * hwq->hwq_size) &
+					      0xffff0000));
+				}
+
+				/* setup additional icp input registers
*/
+				SSTWR16(icpi->cin_overload_lvl,
hwq->hwq_hiwat);
+				icpi->cin_susp_output_lmx =
(LMX_NOT_CONN |
+
LMX_OFF_LINE);
+				icpi->cin_susp_output_sig = 0x00;
+				icpi->cin_q_cntrl = hwq->hwq_rxwrap |
+				    EN_IXOFF_SVC;
+				SSTWR16(icpi->cin_min_char_lvl, 1);
+				icpi->cin_iband_flow_cntrl = 0;
+				/* unlock input */
+				/* Start with Bank B locked */
+				icpi->cin_locks = 0x10 | LOCK_B;
+
+				/* icp output - assign data & cmd queues
*/
+				/* setup queue 0 only for each channel
*/
+				/* use circular output data queue */
+				/* - no session registers */
+
+				status = SSTRD16(icpo->cout_status);
+				if (mpd->mpd_board_def->asic == SSP64) {
+					/* SSP-64 */
+					addr = ((i + j * MAXCHNL_ICP) *
2 + 1) *
+					    hwq->hwq_size;
+					icpq->q_data_ptr_u = (addr >>
16);
+					SSTWR16(icpq->q_data_ptr_l,
+						(addr & 0xffff));
+					icpq->q_data_q_type =
+					    EN_CIRC_Q | EN_TX_LOW |
+					    EN_TX_EMPTY |
hwq->hwq_txwrap;
+					/* permanent send data state */
+					icpq->q_block_count =
+					    hwq->hwq_lowat / 64;
+					/* lowat mark in 64-byte blocks
*/
+					status |= (i << 10);
+				} else {
+					/* SSP-4 */
+					addr = i * 0x400;
+
SSTWR16(icpo->cout_q0.q_data_ptr_l,
+						(addr & 0xFFFF));
+					icpo->cout_q0.q_data_q_type =
+					    EN_CIRC_Q | EN_TX_LOW |
+					    EN_TX_EMPTY |
hwq->hwq_txwrap;
+					icpo->cout_q0.q_block_count =
+					    hwq->hwq_lowat / 64;
+					status |= ((i * 4) << 8);
+				}
+				SSTWR16(icpo->cout_status, status);
+
+				if (mpd->mpd_board_def->asic == SSP64) {
+					/* SSP-64 */
+					/* circular, size, cause empty &
*/
+					/* lowat events */
+					icpq->q_out_state =
CMDQ_CONT_SND |
+					    hwq->hwq_cmdsize;
+					icpo->cout_ses_cntrl_a = SCR_EN;
+					addr1 = addr;
+				} else {
+					/* SSP-4 */
+					icpo->cout_q0.q_out_state =
CMDQ_SND;
+					icpo->cout_ses_cntrl_a = SCR_EN;
+					addr1 = addr + 0x1000;
+				}
+
+				mpc->mpc_txq.q_begin = addr1 & 0xffff;
+				mpc->mpc_txq.q_ptr = addr1 & 0xffff;
+				mpc->mpc_txbase = 0;
+				mpc->mpc_txpg = 0;
+				mpc->mpc_txq.q_end =
+				    (addr1 & 0xffff) + (hwq->hwq_size -
1);
+				mpc->mpc_txq.q_size = hwq->hwq_size;
+				if (mpd->mpd_board_def->asic == SSP64)
+					/* SSP-64 */
+					mpc->mpc_txq.q_addr =
+					    (char *)(icp->icp_dram_start
+
+						     (((i * 2 + 1) *
+						       hwq->hwq_size) &
+						      0xffff0000));
+				else
+					/* SSP-4 */
+					mpc->mpc_txq.q_addr =
+					    (char *)(icp->icp_dram_start
+
+						     ((i *
hwq->hwq_size) &
+						      0xFFFF0000));
+
+				/* cmd queue */
+				if (mpd->mpd_board_def->asic == SSP64)
+					addr = (MAXCHNL_ICP + i +
+						(j * MAXCHNL_ICP)) *
+					    hwq->hwq_size / 4;
+				else
+					addr = i * 0x400;
+				icpq->q_cmnd_ptr_u = (addr >> 16);
+				SSTWR16(icpq->q_cmnd_ptr_l, (addr &
0xffff));
+
+				/* output timer - setup for 10 ms
prescale */
+				icpo->cout_tim_scale = 2;
+				/* place ms count here */
+				icpo->cout_tim_reg = 0;
+
+				/* misc. output registers */
+				cntrl_sig =
SSTRD16(icpo->cout_cntrl_sig);
+				cntrl_sig &= ~0xff;
+				if (mux)
+					/* mux control signals */
+					cntrl_sig |= 0x44;
+				SSTWR16(icpo->cout_cntrl_sig,
cntrl_sig);
+
+				/* unlock output */
+				icpo->cout_lck_cntrl = 0x02;
+				icpo->cout_cpu_req ^= 0x04;
+				/* force send data state */
+			}
+
+			/* check sanity of ICP */
+			/* "ring clock failure" should be on since ring
*/
+			/* clock is off */
+			if (mpd->mpd_board_def->asic == SSP64) {
+				/* SSP64 */
+				no_icp = !(icpg->ssp.gicp_attn &
RNG_FAIL);
+				if (!no_icp) {
+					mpc =
&eqnx_chan[icp->icp_minor_start];
+					/* setup mpc structs for each
chan */
+					for (ii = 0; ii < ssp_channels;
ii++,
+					     mpc++) {
+						/* don't enable
lmx_cond_chng */
+						/* until ring found */
+						icpi = mpc->mpc_icpi;
+						icpo = mpc->mpc_icpo;
+						/* allow software attn
*/
+						attn_ena =
EN_REG_UPDT_EV;
+						if (!(ii % 16))
+							attn_ena |=
ENA_LMX_CNG;
+
SSTWR16(icpi->cin_attn_ena,
+							attn_ena);
+						/* start with Bank B
locked */
+						icpi->cin_locks =
LOCK_B;
+						/* allow software attn
*/
+
SSTWR16(icpo->cout_attn_enbl,
+							EN_REG_UPDT_EV);
+						icpo->cout_lck_cntrl ^=
+						    (LCK_EVT_A |
LCK_EVT_B);
+					}
+
+					/* setup global interval timer
for 1 */
+					/* second pulse */
+					/* 0.1 seconds before decrement
*/
+					icpg->ssp.gicp_tmr_size = 192;
+					/* 10 decrements before pulse */
+					icpg->ssp.gicp_tmr_count = 10;
+					icpg->ssp.gicp_bus_cntrl |=
GTIMER_EN;
+
+					/* disable watchdog */
+					icpg->ssp.gicp_watchdog = 0;
+
+					/* enable global pram writes,
dma */
+					/* and ring clock */
+					icpg->ssp.gicp_initiate =
+					    (RNG_CLK_ON | ICP_PRAM_WR |
+					     DMA_EN | DISABLE_ATTN_CLR);
+					mpd->icp[j].icp_rng_state =
RNG_BAD;
+					/* set ring bad */
+					mpd->icp[j].icp_rng_last = 0x4;
+				} else {
+					dev_err(mpd->dev, "eqnx_init:
ICP %d "
+						"not detected for board
with "
+						"I/O address %d\n",
+						j + 1, k + 1);
+					mpd->mpd_cnfg_state =
CNFG_STATE_FAIL;
+					mpd->mpd_cnfg_fail =
CNFG_FAIL_ICP_FAIL;
+					mpd->mpd_alive = false;
+					continue;
+				}
+			} else {
+				/* SSP-4 */
+				volatile union global_regs_u *icp_glo;
+				icp_glo = (volatile union global_regs_u
*)
+				    ((unsigned long)icp->icp_regs_start
+
+				     0x400);
+				mpc = &eqnx_chan[icp->icp_minor_start];
+				for (jj = 0; jj < ssp_channels; jj++,
mpc++) {
+					icpi = mpc->mpc_icpi;
+					icpo = mpc->mpc_icpo;
+					icpi->cin_q_cntrl |= 0x80;
+					/* allow software attention */
+					SSTWR16(icpi->cin_attn_ena,
+						EN_REG_UPDT_EV);
+					icpi->cin_locks = LOCK_B;
+					/* 0.1 seconds before decrement
*/
+					icpi->cin_tmr_preset_sz = 0x00;
+					/* 10 decrements before pulse */
+					icpi->cin_tmr_preset_count =
0x00;
+					/* allow software attention */
+					SSTWR16(icpo->cout_attn_enbl,
0x8000);
+					icpo->cout_lck_cntrl = LOCK_B;
+				}
+				/* setup global interval timer for 
+				   a 1 second pulse */
+				/* enable it */
+				icp_glo->ssp4.bus_cntrl |= (BUS_CNTRL_16
|
+							    BUS_CNTRL_WR
|
+
BUS_CNTRL_DMA);
+
+				mpc = &eqnx_chan[icp->icp_minor_start];
+				/* fake ldv record */
+				icp->lmx[0].lmx_active = DEV_GOOD;
+				icp->lmx[0].lmx_mpc = mpc;
+				icp->lmx[0].lmx_id = 0;
+				icp->lmx[1].lmx_active = DEV_BAD;
+				icp->lmx[2].lmx_active = DEV_BAD;
+				icp->lmx[3].lmx_active = DEV_BAD;
+				icp->lmx[0].lmx_chan = 4;
+				icp->lmx[0].lmx_speed = 3;
+
+				icp->lmx[0].lmx_bridge = false;
+				icp->lmx[0].lmx_rmt_active = 0;
+				icp->lmx[0].lmx_good_count = 0;
+				icp->lmx[0].lmx_wait = -1;
+				/* fake ring state flags */
+				icp->icp_rng_state = RNG_GOOD;
+				/* set "ring bad" last pass */
+				icp->icp_rng_last = 0x04;
+				icp_glo->ssp4.on_line = 0x01;
+			}
+		}
+
+		/* Store Multimodem country code and print to log */
+		if (mpd->mpd_board_def->flags & MM) {
+			c_code = readb(mpd->mpd_mem +
MM_COUNTRY_CODE_REG)
+				& 0x7F;
+			mpd->mpd_ccode = c_code;
+			dev_info(mpd->dev, "eqnx_init: Multimodem board
"
+				 "country code ID is %X\n", c_code);
+		}
+	}
+
+	mpd = eqnx_dev;
+
+	printk(KERN_INFO "eqnx_init: Driver Enabled for %d board%s.\n",
+	       eqnx_nssps, (eqnx_nssps > 1) ? "s" : "");
+
+	/*
+	 * Allocate temporary write buffer.
+	 */
+	eqnx_txcookbuf = (char *)kmalloc(XMIT_BUF_SIZE, GFP_KERNEL);
+	if (eqnx_txcookbuf == (char *)NULL)
+		printk(KERN_ERR "eqnx_init: failed write buffer allocate
"
+		       "(size=%d)\n", XMIT_BUF_SIZE);
+
+	/* Initialize the tty_driver structure */
+	if (register_eqnx() != 0)
+		return (-ENODEV);
+
+	init_timer(&eqnx_timer);
+	eqnx_timer.expires = jiffies + MPTIMEO;
+	eqnx_timer.data = 0;
+	eqnx_timer.function = &sstpoll;
+	add_timer(&eqnx_timer);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "eqnx_init: registered EQNX driver\n");
+#endif
+	return 0;
+}
+
+/*
+ * eqnx_cleanup()
+ * cleanup on module unload
+ */
+void eqnx_cleanup(void)
+{
+	struct mpdev *mpd;
+	volatile union global_regs_u *icpg;
+	struct icp_struct *icp;
+	unsigned long flags;
+	int i, n = 0, k, numchans, base;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "eqnx_cleanup: trying to unregister
eqnx_driver\n");
+#endif
+
+	if (timer_pending(&eqnx_timer))
+		del_timer(&eqnx_timer);
+
+	/* for each board */
+	for (k = 0; k < eqnx_nssps; k++) {
+		mpd = &eqnx_dev[k];
+
+		eqnx_remove_sysfs(mpd->dev);
+		if (mpd->mpd_alive == 0)
+			continue;
+		else
+			mpd->mpd_alive = 0;
+
+		numchans = mpd->mpd_nicps * mpd->mpd_sspchan;
+		base = k * MAXCHNL_BRD;
+
+		for (i = 0; i < numchans; i++) {
+			eqnx_remove_tty_sysfs(eqnx_chan[base + i].cdev);
+			tty_unregister_device(eqnx_driver, base + i);
+		}
+
+		spin_lock_irqsave(&mpd->mpd_lock, flags);
+		if (mpd->mpd_board_def->asic == SSP64) {
+			/* SSP64 */
+			/* for each ICP */
+			for (i = 0; i < (int)mpd->mpd_nicps; i++) {
+				icp = &mpd->icp[i];
+				icpg = (volatile union global_regs_u *)
+				    ((unsigned long)icp->icp_regs_start
+				     + 0x2000);
+#ifdef	DEBUG
+				printk(KERN_DEBUG "eqnx_cleanup: turn
off ring "
+				       "clock, PRAM and DMA\n");
+#endif
+				icpg->ssp.gicp_initiate &=
+				    ~(RNG_CLK_ON | ICP_PRAM_WR | DMA_EN
|
+				      DISABLE_ATTN_CLR);
+				icpg->ssp.gicp_attn = 0;
+				icpg->ssp.gicp_initiate = 0;
+				icpg->ssp.gicp_watchdog = 0;
+			}
+		}
+		spin_unlock_irqrestore(&mpd->mpd_lock, flags);
+		iounmap(mpd->mpd_mem);
+	}
+
+	if (eqnx_chan != (struct mpchan *)NULL)
+		vfree((void *)eqnx_chan);
+	if (eqnPCIcfg != (char *)NULL)
+		kfree((void *)eqnPCIcfg);
+	if (eqnx_txcookbuf != (char *)NULL)
+		kfree(eqnx_txcookbuf);
+
+	n = tty_unregister_driver(eqnx_driver);
+
+	if (n) {
+		printk(KERN_WARNING "eqnx cleanup: Failed to unregister
"
+		       "tty driver, return = %d\n", n);
+		return;
+	}
+
+	put_tty_driver(eqnx_driver);
+
+}
+
+/*
+ * brd_mem_cfg(mpd)
+ * setup pointers to buffer, tag and cmd memory in icp structs.
+ *
+ * mpd	= board structure
+ */
+static __init void brd_mem_cfg(struct mpdev *mpd)
+{
+	int i;
+
+	/* for each ICP */
+	for (i = 0; i < (int)mpd->mpd_nicps; i++) {
+		if (mpd->mpd_board_def->asic == SSP64) {
+			/* SSP64 */
+			mpd->icp[i].icp_regs_start = (mpaddr_t)
+			    ((unsigned long)mpd->mpd_mem + (i *
0x4000));
+			/* Special case PCI 64 port boards */
+			if ((mpd->mpd_pdev->device & 0xF8) == 0x08)
+				mpd->icp[i].icp_dram_start = (void *)
+				    ((unsigned long)mpd->mpd_mem +
+				     mpd->mpd_memsz / 2);
+			else
+				mpd->icp[i].icp_dram_start = (void *)
+				    ((unsigned long)mpd->mpd_mem +
+				     mpd->mpd_memsz + (i *
mpd->mpd_memsz / 2));
+			mpd->icp[i].icp_tags_start = (void *)
+			    ((unsigned long)mpd->mpd_mem + 0x40000 +
+			     (i * 0x20000));
+			mpd->icp[i].icp_cmds_start = (void *)
+			    ((unsigned long)mpd->mpd_mem + 0x40000 +
+			     (i * 0x20000));
+			mpd->mpd_hwq = &sst_hwq[0];
+		} else {
+			/* SSP4 */
+			mpd->icp[i].icp_regs_start = (mpaddr_t)
+			    ((unsigned long)mpd->mpd_mem +
+			     (i * sizeof(struct ssp4_addr_space_s)));
+			mpd->icp[i].icp_dram_start = (void *)
+			    ((unsigned long)mpd->mpd_mem +
+			     (i * sizeof(struct ssp4_addr_space_s)) +
0x1000);
+			mpd->icp[i].icp_tags_start = (void *)
+			    ((unsigned long)mpd->mpd_mem + 0x3000 +
+			     (i * sizeof(struct ssp4_addr_space_s)));
+			/* cmds doesn't exist, so point to tags */
+			mpd->icp[i].icp_cmds_start =
mpd->icp[i].icp_tags_start;
+			mpd->mpd_hwq = &sst_hwq[1];
+		}
+	}
+}
+
+/*
+ * mem_test_pram(mpd, ramw, testlen)
+ * verify processor ram memory (ICP registers) is valid.
+ *
+ * mpd	= board structure
+ * ramw	= beginning of ram buffer
+ * testlen = length of ram buffer
+ */
+static __init int mem_test_pram(struct mpdev *mpd, u16 * ramw, int
testlen)
+{
+	int ram_ok = 0, ii, jj;
+
+	/* test input and output registers */
+	for (ii = 0; ii < testlen; ii += 2, ramw++) {
+		if (mpd->mpd_board_def->asic == SSP64) {
+			/* SSP64 */
+			jj = ii & 0x7f;
+			/* protect sensitive registers */
+			if ((ii >= HWREGSLEN / 2) &&
+			    ((jj >= 0x18 && jj < 0x20) ||
+			     (jj >= 0x38 && jj < 0x40) ||
+			     (jj >= 0x58 && jj < 0x60) ||
+			     (jj >= 0x78 && jj < 0x80)))
+				continue;
+		}
+
+		*ramw = 0x55aa;
+		ram_ok = true;
+		if (*ramw != 0x55aa) {
+			ram_ok = false;
+			break;
+		}
+		*ramw = 0xaa55;
+		if (*ramw != 0xaa55) {
+			ram_ok = false;
+			break;
+		}
+	}
+
+	if (ram_ok)
+		return (0);
+	return (ii);
+}
+
+/*
+ * mem_test_dram(mpd, ramw, testlen)
+ * verify on-board memory is valid.
+ *
+ * mpd	= board structure
+ * ramw	= beginning of ram buffer
+ * testlen = length of ram buffer
+ */
+static __init int mem_test_dram(struct mpdev *mpd, u16 * ramw, int
testlen)
+{
+	int ram_ok, ii;
+	u8 ram_pg;
+
+	ram_ok = true;
+	ram_pg = 0;
+	for (ii = 0; ii < testlen; ii += 2, ramw++) {
+		*ramw = 0x55aa;
+		if (*ramw != 0x55aa) {
+			ram_ok = false;
+			break;
+		}
+		*ramw = 0xaa55;
+		if (*ramw != 0xaa55) {
+			ram_ok = false;
+			break;
+		}
+	}
+
+	if (ram_ok)
+		return (0);
+	return (ii);
+}
+
+/*
+ * mem_test_tag(mpd, ramb, testlen)
+ * verify on-board tag memory is valid.
+ *
+ * mpd	= board structure
+ * ramb	= beginning of ram buffer
+ * testlen = length of ram buffer
+ */
+static __init int mem_test_tag(struct mpdev *mpd, u8 * ramb, int
testlen)
+{
+	int ram_ok, ii;
+	u8 ram_pg;
+	volatile u8 *ramb2;
+
+	ramb2 = ramb + 1;
+	ram_ok = true;
+	ram_pg = 0;
+	for (ii = 0; ii < testlen; ii += 2, ramb += 2, ramb2 += 2) {
+		*ramb = 0x55;
+		*ramb2 = 0xaa;
+		if (*ramb != 0x55 || *ramb2 != 0xaa) {
+			ram_ok = false;
+			break;
+		}
+	}
+
+	if (ram_ok)
+		return (0);
+	return (ii);
+}
+
+/*
+ * mem_zero(mpd, ramw, testlen)
+ * zero processor ram memory.
+ *
+ * mpd	= board structure
+ * ramw	= beginning of ram buffer
+ * testlen = length of ram buffer
+ */
+static __init int mem_zero(struct mpdev *mpd, unsigned short *ramw, int
testlen)
+{
+	int ii, jj;
+
+	for (ii = 0; ii < testlen; ii += 2, ramw++) {
+		if (mpd->mpd_board_def->asic == SSP64) {
+			/* SSP-64 */
+			jj = ii & 0x7f;
+			/* protect sensitive registers */
+			if ((ii >= HWREGSLEN / 2) &&
+			    ((jj >= 0x18 && jj < 0x20) ||
+			     (jj >= 0x38 && jj < 0x40) ||
+			     (jj >= 0x58 && jj < 0x60) ||
+			     (jj >= 0x78 && jj < 0x80)))
+				continue;
+		} else {
+			/* SSP-4 */
+			jj = ii & 0x7f;
+			/* protect sensitive registers */
+			if ((jj == 0x02) || (jj == 0x64) || (jj ==
0x72))
+				continue;
+		}
+		*ramw = 0;
+	}
+
+	return (0);
+}
+
+/*
+ * mem_test(mpd, icp)
+ * full memory test
+ *
+ * mpd	= board structure
+ * icp	= ICP index
+ */
+static __init int mem_test(struct mpdev *mpd, int icp)
+{
+	int ram_index, err = 0, testlen;
+	u8 *ramb;
+	u16 *ramw;
+
+	/* test pram  - 16-bit test */
+	ramw = (u16 *) (mpd->icp[icp].icp_regs_start);
+	if (mpd->mpd_board_def->asic == SSP64) {
+		/* SSP-64 */
+		testlen = HWREGSLEN;
+		ram_index = mem_test_pram(mpd, ramw, testlen);
+	} else {
+		/* SSP-4 */
+		testlen = 0x200;
+		ram_index = mem_test_pram(mpd, ramw, testlen);
+		if (!ram_index) {
+			ramw = (u16 *) ((unsigned char *)
+					(mpd->icp[icp].icp_regs_start) +
0x200);
+			ram_index = mem_test_pram(mpd, ramw, testlen);
+		}
+	}
+
+	if (ram_index) {
+		dev_err(mpd->dev, "eqnx_init: PRAM memory test failure,
"
+			"index=%d\n", ram_index);
+		err = 1;
+	}
+
+	/* test dram - word test */
+	if (mpd->mpd_board_def->asic == SSP64) {
+		/* SSP-64 */
+		ramw = (u16 *) (mpd->icp[0].icp_dram_start);
+		testlen = mpd->mpd_memsz;
+		ram_index = mem_test_dram(mpd, ramw, testlen);
+	} else {
+		/* SSP-4 */
+		testlen = 0x1000;
+
+		/* test input buff */
+		ramw = (u16 *) (mpd->icp[icp].icp_dram_start);
+		ram_index = mem_test_dram(mpd, ramw, testlen);
+
+		if (!ram_index) {
+			/* test output buff */
+			ramw = (u16 *) ((unsigned long)
+					mpd->icp[icp].icp_dram_start +
0x1000);
+			ram_index = mem_test_dram(mpd, ramw, testlen);
+		}
+	}
+
+	if (ram_index) {
+		dev_err(mpd->dev, "eqnx_init: DRAM memory test failure,
"
+			"index=%d\n", ram_index);
+		err |= 2;
+	}
+
+	/* test tag dram - requires BYTE accesses! */
+	ramb = (u8 *) (mpd->icp[0].icp_tags_start);
+	if (mpd->mpd_board_def->asic == SSP64)
+		/* SSP-64 */
+		testlen = mpd->mpd_memsz / 4;
+	else
+		testlen = 0x400;
+	ram_index = mem_test_tag(mpd, ramb, testlen);
+	if (ram_index) {
+		dev_err(mpd->dev, "eqnx_init: DRAM tags memory test
failure, "
+			"index=%d\n", ram_index);
+		err |= 4;
+	}
+
+	/* zero pram */
+	ramw = (u16 *) (mpd->icp[icp].icp_regs_start);
+	if (mpd->mpd_board_def->asic == SSP64)
+		/* SSP-64 */
+		testlen = HWREGSLEN;
+	else
+		/* SSP-4 */
+		testlen = 0x400;
+	mem_zero(mpd, ramw, testlen);
+
+	return (err);
+}
+
+/*
+ * register_eqnx_devs(driver)
+ * Register eqnx devices.
+ */
+static __init void register_eqnx_devs(struct tty_driver *driver)
+{
+	int i, numchans, base, brd;
+	struct class_device *classp;
+
+	for (brd = 0; brd < eqnx_nssps; brd++) {
+		numchans = eqnx_dev[brd].mpd_nicps *
eqnx_dev[brd].mpd_sspchan;
+		base = brd * MAXCHNL_BRD;
+		for (i = 0; i < numchans; i++) {
+			classp = tty_register_device(driver, base + i,
NULL);
+			eqnx_chan[base + i].cdev = classp;
+			eqnx_create_tty_sysfs(classp);
+		}
+	}
+}
+
+/*
+ * register_eqnx
+ * Register eqnx driver with tty driver.
+ */
+static __init int register_eqnx(void)
+{
+	int i;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "eqnx: registering the driver\n");
+#endif
+	eqnx_driver = alloc_tty_driver(eqnx_nssps * MAXCHNL_BRD);
+	if (!eqnx_driver) {
+		printk(KERN_ERR "eqnx_init: Failed alloc_tty_driver\n");
+		return (-1);
+	}
+
+	eqnx_driver->owner = THIS_MODULE;
+	eqnx_driver->driver_name = "Equinox_SST";
+	eqnx_driver->name = "ttyEQ";
+	eqnx_driver->devfs_name = "tts/EQ";
+	eqnx_driver->major = EQNX_MAJOR;
+	eqnx_driver->minor_start = 0;
+	eqnx_driver->minor_num = eqnx_nssps * MAXCHNL_BRD;
+	eqnx_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	eqnx_driver->subtype = SERIAL_TYPE_NORMAL;
+	eqnx_driver->init_termios = eqnx_deftermios;
+	eqnx_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+	tty_set_operations(eqnx_driver, &eqnx_ops);
+
+	if (tty_register_driver(eqnx_driver)) {
+		printk(KERN_ERR "eqnx_init: failed to register
device\n");
+		put_tty_driver(eqnx_driver);
+		return (-1);
+	}
+
+	din_num = eqnx_driver->major;
+	if (din_num <= 0) {
+		printk(KERN_ERR "eqnx_init: failed to register
device\n");
+		put_tty_driver(eqnx_driver);
+		return (-1);
+	}
+
+	for (i = 0; i < eqnx_nssps; i++) {
+		eqnx_dev[i].mpd_major = din_num;
+		eqnx_dev[i].mpd_minor_start = i * MAXCHNL_BRD;
+	}
+
+	register_eqnx_devs(eqnx_driver);
+
+	return (0);
+}
+
+/*
+ * eqnx_pcifindbrds(cfg)
+ *
+ * locate all PCI SST boards
+ *
+ * return: cfg with board information
+ * return: number of boards found
+ */
+static __init int eqnx_pcifindbrds(struct pci_cfg *cfg)
+{
+	struct pci_dev *dev = NULL;
+	int i, brd_index = 0;
+	u16 devid;
+
+	for (i = 0; i < brdtab_entries && brd_index < maxbrd; i++) {
+		devid = (board_table[i].secondary_id << 8) & 0xff00;
+		devid |= board_table[i].primary_id;
+
+		while ((dev = pci_find_device(PCI_VENDOR_ID_EQNX, devid,
dev))) {
+			if (pci_enable_device(dev))
+				continue;
+			pci_read_config_byte(dev, PCI_REVISION_ID,
+					     &cfg->rev_id);
+			pci_read_config_word(dev, PCI_COMMAND,
&cfg->command);
+			cfg->base_addr_reg0 =
+			    (void *)pci_resource_start(dev, 0);
+			cfg->pdev = dev;
+			brd_index++;
+			cfg++;
+		}
+	}
+
+	return (brd_index);
+}
+
+module_init(eqnx_init);
+module_exit(eqnx_cleanup);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux