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

From: Straub, Michael
Date: Thu Jun 22 2006 - 09:17:54 EST


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 <michael.straub@xxxxxxxxxxx>

---
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 <michael.straub@xxxxxxxxxxx>
+ */
+
+/*
+ * 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/