* [RFC][PATCH 8/13] Equinox SST driver: driver init and shutdown
@ 2006-06-22 13:19 Straub, Michael
2006-06-22 13:41 ` Jiri Slaby
0 siblings, 1 reply; 2+ messages in thread
From: Straub, Michael @ 2006-06-22 13:19 UTC (permalink / raw)
To: linux-kernel
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@avocent.com>
---
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@avocent.com>
+ */
+
+/*
+ * 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);
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [RFC][PATCH 8/13] Equinox SST driver: driver init and shutdown
2006-06-22 13:19 [RFC][PATCH 8/13] Equinox SST driver: driver init and shutdown Straub, Michael
@ 2006-06-22 13:41 ` Jiri Slaby
0 siblings, 0 replies; 2+ messages in thread
From: Jiri Slaby @ 2006-06-22 13:41 UTC (permalink / raw)
To: Straub, Michael; +Cc: linux-kernel
Straub, Michael napsal(a):
> 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@avocent.com>
>
> ---
> 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@avocent.com>
> + */
> +
> +/*
> + * driver "main" - discovers and initializes all SST boards
> + */
> +
> +char eqnx_version[] = "Equinox / Avocent SuperSerial Technology Device
> Driver";
static?
> +
> +#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
why ifdefs?
> +
> +#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;
static?
> +
> +/* 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;
what is the cast for?
> +
> +/* 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
nope
> +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}
> +};
use PCI_DEVICE()
> +
> +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
nope
> +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 (a) {
if (b) {
}
}
->
if (a && b) {
}
> + }
> +
> + 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);
not cast
> + if (eqnPCIcfg == (char *)NULL) {
cast
> + printk(KERN_ERR "eqnx_init: Failed eqnPCIcfg allocate of
> "
> + "size %d\n", pcicfg_size);
> + return (-ENOMEM);
no (), use goto and return at the end of function
> + }
> +
> + memset(eqnPCIcfg, 0, pcicfg_size);
kzalloc
> + 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) {
cast
> + 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)
static? _exit?
> +{
> + 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);
5x cast?
> +
> + 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);
We are not *bsd. omit (), everywhere.
> +}
> +
> +/*
> + * 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))) {
Probably needs pci probing.
> + 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);
regards,
--
Jiri Slaby www.fi.muni.cz/~xslaby
\_.-^-._ jirislaby@gmail.com _.-^-._/
B67499670407CE62ACC8 22A032CC55C339D47A7E
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2006-06-22 13:41 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-06-22 13:19 [RFC][PATCH 8/13] Equinox SST driver: driver init and shutdown Straub, Michael
2006-06-22 13:41 ` Jiri Slaby
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox