summaryrefslogtreecommitdiff
path: root/drivers/atm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/atm')
-rw-r--r--drivers/atm/.gitignore5
-rw-r--r--drivers/atm/Kconfig401
-rw-r--r--drivers/atm/Makefile35
-rw-r--r--drivers/atm/adummy.c202
-rw-r--r--drivers/atm/ambassador.c2422
-rw-r--r--drivers/atm/ambassador.h663
-rw-r--r--drivers/atm/atmtcp.c492
-rw-r--r--drivers/atm/eni.c2340
-rw-r--r--drivers/atm/eni.h135
-rw-r--r--drivers/atm/firestream.c2065
-rw-r--r--drivers/atm/firestream.h517
-rw-r--r--drivers/atm/fore200e.c3178
-rw-r--r--drivers/atm/fore200e.h979
-rw-r--r--drivers/atm/he.c2869
-rw-r--r--drivers/atm/he.h845
-rw-r--r--drivers/atm/horizon.c2914
-rw-r--r--drivers/atm/horizon.h507
-rw-r--r--drivers/atm/idt77105.c378
-rw-r--r--drivers/atm/idt77105.h91
-rw-r--r--drivers/atm/idt77252.c3804
-rw-r--r--drivers/atm/idt77252.h813
-rw-r--r--drivers/atm/idt77252_tables.h780
-rw-r--r--drivers/atm/iphase.c3298
-rw-r--r--drivers/atm/iphase.h1453
-rw-r--r--drivers/atm/lanai.c2608
-rw-r--r--drivers/atm/midway.h265
-rw-r--r--drivers/atm/nicstar.c2747
-rw-r--r--drivers/atm/nicstar.h758
-rw-r--r--drivers/atm/nicstarmac.c248
-rw-r--r--drivers/atm/nicstarmac.copyright61
-rw-r--r--drivers/atm/solos-attrlist.c82
-rw-r--r--drivers/atm/solos-pci.c1493
-rw-r--r--drivers/atm/suni.c392
-rw-r--r--drivers/atm/suni.h241
-rw-r--r--drivers/atm/tonga.h20
-rw-r--r--drivers/atm/uPD98401.h292
-rw-r--r--drivers/atm/uPD98402.c265
-rw-r--r--drivers/atm/uPD98402.h106
-rw-r--r--drivers/atm/zatm.c1662
-rw-r--r--drivers/atm/zatm.h103
-rw-r--r--drivers/atm/zeprom.h34
41 files changed, 42563 insertions, 0 deletions
diff --git a/drivers/atm/.gitignore b/drivers/atm/.gitignore
new file mode 100644
index 000000000..58bcddaae
--- /dev/null
+++ b/drivers/atm/.gitignore
@@ -0,0 +1,5 @@
+# Ignore generated files
+fore200e_mkfirm
+fore200e_pca_fw.c
+/*(DEBLOBBED)*/
+/*(DEBLOBBED)*/
diff --git a/drivers/atm/Kconfig b/drivers/atm/Kconfig
new file mode 100644
index 000000000..31c60101a
--- /dev/null
+++ b/drivers/atm/Kconfig
@@ -0,0 +1,401 @@
+#
+# ATM device configuration
+#
+
+menuconfig ATM_DRIVERS
+ bool "ATM drivers"
+ depends on NETDEVICES && ATM
+ default y
+ ---help---
+ Say Y here to get to see options for Asynchronous Transfer Mode
+ device drivers. This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if ATM_DRIVERS && NETDEVICES && ATM
+
+config ATM_DUMMY
+ tristate "Dummy ATM driver"
+ help
+ Dummy ATM driver. Useful for proxy signalling, testing,
+ and development. If unsure, say N.
+
+config ATM_TCP
+ tristate "ATM over TCP"
+ depends on INET
+ help
+ ATM over TCP driver. Useful mainly for development and for
+ experiments. If unsure, say N.
+
+config ATM_LANAI
+ tristate "Efficient Networks Speedstream 3010"
+ depends on PCI && ATM
+ help
+ Supports ATM cards based on the Efficient Networks "Lanai"
+ chipset such as the Speedstream 3010 and the ENI-25p. The
+ Speedstream 3060 is currently not supported since we don't
+ have the code to drive the on-board Alcatel DSL chipset (yet).
+
+config ATM_ENI
+ tristate "Efficient Networks ENI155P"
+ depends on PCI
+ ---help---
+ Driver for the Efficient Networks ENI155p series and SMC ATM
+ Power155 155 Mbps ATM adapters. Both, the versions with 512KB and
+ 2MB on-board RAM (Efficient calls them "C" and "S", respectively),
+ and the FPGA and the ASIC Tonga versions of the board are supported.
+ The driver works with MMF (-MF or ...F) and UTP-5 (-U5 or ...D)
+ adapters.
+
+ To compile this driver as a module, choose M here: the module will
+ be called eni.
+
+config ATM_ENI_DEBUG
+ bool "Enable extended debugging"
+ depends on ATM_ENI
+ help
+ Extended debugging records various events and displays that list
+ when an inconsistency is detected. This mechanism is faster than
+ generally using printks, but still has some impact on performance.
+ Note that extended debugging may create certain race conditions
+ itself. Enable this ONLY if you suspect problems with the driver.
+
+config ATM_ENI_TUNE_BURST
+ bool "Fine-tune burst settings"
+ depends on ATM_ENI
+ ---help---
+ In order to obtain good throughput, the ENI NIC can transfer
+ multiple words of data per PCI bus access cycle. Such a multi-word
+ transfer is called a burst.
+
+ The default settings for the burst sizes are suitable for most PCI
+ chipsets. However, in some cases, large bursts may overrun buffers
+ in the PCI chipset and cause data corruption. In such cases, large
+ bursts must be disabled and only (slower) small bursts can be used.
+ The burst sizes can be set independently in the send (TX) and
+ receive (RX) direction.
+
+ Note that enabling many different burst sizes in the same direction
+ may increase the cost of setting up a transfer such that the
+ resulting throughput is lower than when using only the largest
+ available burst size.
+
+ Also, sometimes larger bursts lead to lower throughput, e.g. on an
+ Intel 440FX board, a drop from 135 Mbps to 103 Mbps was observed
+ when going from 8W to 16W bursts.
+
+config ATM_ENI_BURST_TX_16W
+ bool "Enable 16W TX bursts (discouraged)"
+ depends on ATM_ENI_TUNE_BURST
+ help
+ Burst sixteen words at once in the send direction. This may work
+ with recent PCI chipsets, but is known to fail with older chipsets.
+
+config ATM_ENI_BURST_TX_8W
+ bool "Enable 8W TX bursts (recommended)"
+ depends on ATM_ENI_TUNE_BURST
+ help
+ Burst eight words at once in the send direction. This is the default
+ setting.
+
+config ATM_ENI_BURST_TX_4W
+ bool "Enable 4W TX bursts (optional)"
+ depends on ATM_ENI_TUNE_BURST
+ help
+ Burst four words at once in the send direction. You may want to try
+ this if you have disabled 8W bursts. Enabling 4W if 8W is also set
+ may or may not improve throughput.
+
+config ATM_ENI_BURST_TX_2W
+ bool "Enable 2W TX bursts (optional)"
+ depends on ATM_ENI_TUNE_BURST
+ help
+ Burst two words at once in the send direction. You may want to try
+ this if you have disabled 4W and 8W bursts. Enabling 2W if 4W or 8W
+ are also set may or may not improve throughput.
+
+config ATM_ENI_BURST_RX_16W
+ bool "Enable 16W RX bursts (discouraged)"
+ depends on ATM_ENI_TUNE_BURST
+ help
+ Burst sixteen words at once in the receive direction. This may work
+ with recent PCI chipsets, but is known to fail with older chipsets.
+
+config ATM_ENI_BURST_RX_8W
+ bool "Enable 8W RX bursts (discouraged)"
+ depends on ATM_ENI_TUNE_BURST
+ help
+ Burst eight words at once in the receive direction. This may work
+ with recent PCI chipsets, but is known to fail with older chipsets,
+ such as the Intel Neptune series.
+
+config ATM_ENI_BURST_RX_4W
+ bool "Enable 4W RX bursts (recommended)"
+ depends on ATM_ENI_TUNE_BURST
+ help
+ Burst four words at once in the receive direction. This is the
+ default setting. Enabling 4W if 8W is also set may or may not
+ improve throughput.
+
+config ATM_ENI_BURST_RX_2W
+ bool "Enable 2W RX bursts (optional)"
+ depends on ATM_ENI_TUNE_BURST
+ help
+ Burst two words at once in the receive direction. You may want to
+ try this if you have disabled 4W and 8W bursts. Enabling 2W if 4W or
+ 8W are also set may or may not improve throughput.
+
+config ATM_FIRESTREAM
+ tristate "Fujitsu FireStream (FS50/FS155) "
+ depends on PCI && VIRT_TO_BUS
+ help
+ Driver for the Fujitsu FireStream 155 (MB86697) and
+ FireStream 50 (MB86695) ATM PCI chips.
+
+ To compile this driver as a module, choose M here: the module will
+ be called firestream.
+
+config ATM_ZATM
+ tristate "ZeitNet ZN1221/ZN1225"
+ depends on PCI && VIRT_TO_BUS
+ help
+ Driver for the ZeitNet ZN1221 (MMF) and ZN1225 (UTP-5) 155 Mbps ATM
+ adapters.
+
+ To compile this driver as a module, choose M here: the module will
+ be called zatm.
+
+config ATM_ZATM_DEBUG
+ bool "Enable extended debugging"
+ depends on ATM_ZATM
+ help
+ Extended debugging records various events and displays that list
+ when an inconsistency is detected. This mechanism is faster than
+ generally using printks, but still has some impact on performance.
+ Note that extended debugging may create certain race conditions
+ itself. Enable this ONLY if you suspect problems with the driver.
+
+config ATM_NICSTAR
+ tristate "IDT 77201 (NICStAR) (ForeRunnerLE)"
+ depends on PCI
+ help
+ The NICStAR chipset family is used in a large number of ATM NICs for
+ 25 and for 155 Mbps, including IDT cards and the Fore ForeRunnerLE
+ series. Say Y if you have one of those.
+
+ To compile this driver as a module, choose M here: the module will
+ be called nicstar.
+
+config ATM_NICSTAR_USE_SUNI
+ bool "Use suni PHY driver (155Mbps)"
+ depends on ATM_NICSTAR
+ help
+ Support for the S-UNI and compatible PHYsical layer chips. These are
+ found in most 155Mbps NICStAR based ATM cards, namely in the
+ ForeRunner LE155 cards. This driver provides detection of cable~
+ removal and reinsertion and provides some statistics. This driver
+ doesn't have removal capability when compiled as a module, so if you
+ need that capability don't include S-UNI support (it's not needed to
+ make the card work).
+
+config ATM_NICSTAR_USE_IDT77105
+ bool "Use IDT77015 PHY driver (25Mbps)"
+ depends on ATM_NICSTAR
+ help
+ Support for the PHYsical layer chip in ForeRunner LE25 cards. In
+ addition to cable removal/reinsertion detection, this driver allows
+ you to control the loopback mode of the chip via a dedicated IOCTL.
+ This driver is required for proper handling of temporary carrier
+ loss, so if you have a 25Mbps NICStAR based ATM card you must say Y.
+
+config ATM_IDT77252
+ tristate "IDT 77252 (NICStAR II)"
+ depends on PCI
+ help
+ Driver for the IDT 77252 ATM PCI chips.
+
+ To compile this driver as a module, choose M here: the module will
+ be called idt77252.
+
+config ATM_IDT77252_DEBUG
+ bool "Enable debugging messages"
+ depends on ATM_IDT77252
+ help
+ Somewhat useful debugging messages are available. The choice of
+ messages is controlled by a bitmap. This may be specified as a
+ module argument. See the file <file:drivers/atm/idt77252.h> for
+ the meanings of the bits in the mask.
+
+ When active, these messages can have a significant impact on the
+ speed of the driver, and the size of your syslog files! When
+ inactive, they will have only a modest impact on performance.
+
+config ATM_IDT77252_RCV_ALL
+ bool "Receive ALL cells in raw queue"
+ depends on ATM_IDT77252
+ help
+ Enable receiving of all cells on the ATM link, that do not match
+ an open connection in the raw cell queue of the driver. Useful
+ for debugging or special applications only, so the safe answer is N.
+
+config ATM_IDT77252_USE_SUNI
+ bool
+ depends on ATM_IDT77252
+ default y
+
+config ATM_AMBASSADOR
+ tristate "Madge Ambassador (Collage PCI 155 Server)"
+ depends on PCI && VIRT_TO_BUS
+ select BITREVERSE
+ help
+ This is a driver for ATMizer based ATM card produced by Madge
+ Networks Ltd. Say Y (or M to compile as a module named ambassador)
+ here if you have one of these cards.
+
+config ATM_AMBASSADOR_DEBUG
+ bool "Enable debugging messages"
+ depends on ATM_AMBASSADOR
+ ---help---
+ Somewhat useful debugging messages are available. The choice of
+ messages is controlled by a bitmap. This may be specified as a
+ module argument (kernel command line argument as well?), changed
+ dynamically using an ioctl (not yet) or changed by sending the
+ string "Dxxxx" to VCI 1023 (where x is a hex digit). See the file
+ <file:drivers/atm/ambassador.h> for the meanings of the bits in the
+ mask.
+
+ When active, these messages can have a significant impact on the
+ speed of the driver, and the size of your syslog files! When
+ inactive, they will have only a modest impact on performance.
+
+config ATM_HORIZON
+ tristate "Madge Horizon [Ultra] (Collage PCI 25 and Collage PCI 155 Client)"
+ depends on PCI && VIRT_TO_BUS
+ help
+ This is a driver for the Horizon chipset ATM adapter cards once
+ produced by Madge Networks Ltd. Say Y (or M to compile as a module
+ named horizon) here if you have one of these cards.
+
+config ATM_HORIZON_DEBUG
+ bool "Enable debugging messages"
+ depends on ATM_HORIZON
+ ---help---
+ Somewhat useful debugging messages are available. The choice of
+ messages is controlled by a bitmap. This may be specified as a
+ module argument (kernel command line argument as well?), changed
+ dynamically using an ioctl (not yet) or changed by sending the
+ string "Dxxxx" to VCI 1023 (where x is a hex digit). See the file
+ <file:drivers/atm/horizon.h> for the meanings of the bits in the
+ mask.
+
+ When active, these messages can have a significant impact on the
+ speed of the driver, and the size of your syslog files! When
+ inactive, they will have only a modest impact on performance.
+
+config ATM_IA
+ tristate "Interphase ATM PCI x575/x525/x531"
+ depends on PCI
+ ---help---
+ This is a driver for the Interphase (i)ChipSAR adapter cards
+ which include a variety of variants in term of the size of the
+ control memory (128K-1KVC, 512K-4KVC), the size of the packet
+ memory (128K, 512K, 1M), and the PHY type (Single/Multi mode OC3,
+ UTP155, UTP25, DS3 and E3). Go to:
+ <http://www.iphase.com/>
+ for more info about the cards. Say Y (or M to compile as a module
+ named iphase) here if you have one of these cards.
+
+ See the file <file:Documentation/networking/iphase.txt> for further
+ details.
+
+config ATM_IA_DEBUG
+ bool "Enable debugging messages"
+ depends on ATM_IA
+ ---help---
+ Somewhat useful debugging messages are available. The choice of
+ messages is controlled by a bitmap. This may be specified as a
+ module argument (kernel command line argument as well?), changed
+ dynamically using an ioctl (Get the debug utility, iadbg, from
+ <ftp://ftp.iphase.com/pub/atm/pci/>).
+
+ See the file <file:drivers/atm/iphase.h> for the meanings of the
+ bits in the mask.
+
+ When active, these messages can have a significant impact on the
+ speed of the driver, and the size of your syslog files! When
+ inactive, they will have only a modest impact on performance.
+
+config ATM_FORE200E
+ tristate "FORE Systems 200E-series"
+ depends on (PCI || SBUS)
+ select FW_LOADER
+ ---help---
+ This is a driver for the FORE Systems 200E-series ATM adapter
+ cards. It simultaneously supports PCA-200E and SBA-200E models
+ on PCI and SBUS hosts. Say Y (or M to compile as a module
+ named fore_200e) here if you have one of these ATM adapters.
+
+ See the file <file:Documentation/networking/fore200e.txt> for
+ further details.
+
+config ATM_FORE200E_USE_TASKLET
+ bool "Defer interrupt work to a tasklet"
+ depends on ATM_FORE200E
+ default n
+ help
+ This defers work to be done by the interrupt handler to a
+ tasklet instead of handling everything at interrupt time. This
+ may improve the responsive of the host.
+
+config ATM_FORE200E_TX_RETRY
+ int "Maximum number of tx retries"
+ depends on ATM_FORE200E
+ default "16"
+ ---help---
+ Specifies the number of times the driver attempts to transmit
+ a message before giving up, if the transmit queue of the ATM card
+ is transiently saturated.
+
+ Saturation of the transmit queue may occur only under extreme
+ conditions, e.g. when a fast host continuously submits very small
+ frames (<64 bytes) or raw AAL0 cells (48 bytes) to the ATM adapter.
+
+ Note that under common conditions, it is unlikely that you encounter
+ a saturation of the transmit queue, so the retry mechanism never
+ comes into play.
+
+config ATM_FORE200E_DEBUG
+ int "Debugging level (0-3)"
+ depends on ATM_FORE200E
+ default "0"
+ help
+ Specifies the level of debugging messages issued by the driver.
+ The verbosity of the driver increases with the value of this
+ parameter.
+
+ When active, these messages can have a significant impact on
+ the performances of the driver, and the size of your syslog files!
+ Keep the debugging level to 0 during normal operations.
+
+config ATM_HE
+ tristate "ForeRunner HE Series"
+ depends on PCI
+ help
+ This is a driver for the Marconi ForeRunner HE-series ATM adapter
+ cards. It simultaneously supports the 155 and 622 versions.
+
+config ATM_HE_USE_SUNI
+ bool "Use S/UNI PHY driver"
+ depends on ATM_HE
+ help
+ Support for the S/UNI-Ultra and S/UNI-622 found in the ForeRunner
+ HE cards. This driver provides carrier detection some statistics.
+
+config ATM_SOLOS
+ tristate "Solos ADSL2+ PCI Multiport card driver"
+ depends on PCI
+ select FW_LOADER
+ help
+ Support for the Solos multiport ADSL2+ card.
+
+endif # ATM
diff --git a/drivers/atm/Makefile b/drivers/atm/Makefile
new file mode 100644
index 000000000..c6c9ee9f5
--- /dev/null
+++ b/drivers/atm/Makefile
@@ -0,0 +1,35 @@
+#
+# Makefile for the Linux network (ATM) device drivers.
+#
+
+fore_200e-y := fore200e.o
+
+obj-$(CONFIG_ATM_ZATM) += zatm.o uPD98402.o
+obj-$(CONFIG_ATM_NICSTAR) += nicstar.o
+obj-$(CONFIG_ATM_AMBASSADOR) += ambassador.o
+obj-$(CONFIG_ATM_HORIZON) += horizon.o
+obj-$(CONFIG_ATM_IA) += iphase.o suni.o
+obj-$(CONFIG_ATM_FORE200E) += fore_200e.o
+obj-$(CONFIG_ATM_ENI) += eni.o suni.o
+obj-$(CONFIG_ATM_IDT77252) += idt77252.o
+obj-$(CONFIG_ATM_SOLOS) += solos-pci.o
+
+ifeq ($(CONFIG_ATM_NICSTAR_USE_SUNI),y)
+ obj-$(CONFIG_ATM_NICSTAR) += suni.o
+endif
+ifeq ($(CONFIG_ATM_NICSTAR_USE_IDT77105),y)
+ obj-$(CONFIG_ATM_NICSTAR) += idt77105.o
+endif
+ifeq ($(CONFIG_ATM_IDT77252_USE_SUNI),y)
+ obj-$(CONFIG_ATM_IDT77252) += suni.o
+endif
+
+obj-$(CONFIG_ATM_DUMMY) += adummy.o
+obj-$(CONFIG_ATM_TCP) += atmtcp.o
+obj-$(CONFIG_ATM_FIRESTREAM) += firestream.o
+obj-$(CONFIG_ATM_LANAI) += lanai.o
+
+obj-$(CONFIG_ATM_HE) += he.o
+ifeq ($(CONFIG_ATM_HE_USE_SUNI),y)
+ obj-$(CONFIG_ATM_HE) += suni.o
+endif
diff --git a/drivers/atm/adummy.c b/drivers/atm/adummy.c
new file mode 100644
index 000000000..f9b983ae6
--- /dev/null
+++ b/drivers/atm/adummy.c
@@ -0,0 +1,202 @@
+/*
+ * adummy.c: a dummy ATM driver
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#include <linux/atmdev.h>
+#include <linux/atm.h>
+#include <linux/sonet.h>
+
+/* version definition */
+
+#define DRV_VERSION "1.0"
+
+#define DEV_LABEL "adummy"
+
+#define ADUMMY_DEV(dev) ((struct adummy_dev *) (dev)->dev_data)
+
+struct adummy_dev {
+ struct atm_dev *atm_dev;
+
+ struct list_head entry;
+};
+
+/* globals */
+
+static LIST_HEAD(adummy_devs);
+
+static ssize_t __set_signal(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct atm_dev *atm_dev = container_of(dev, struct atm_dev, class_dev);
+ int signal;
+
+ if (sscanf(buf, "%d", &signal) == 1) {
+
+ if (signal < ATM_PHY_SIG_LOST || signal > ATM_PHY_SIG_FOUND)
+ signal = ATM_PHY_SIG_UNKNOWN;
+
+ atm_dev_signal_change(atm_dev, signal);
+ return 1;
+ }
+ return -EINVAL;
+}
+
+static ssize_t __show_signal(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atm_dev *atm_dev = container_of(dev, struct atm_dev, class_dev);
+ return sprintf(buf, "%d\n", atm_dev->signal);
+}
+static DEVICE_ATTR(signal, 0644, __show_signal, __set_signal);
+
+static struct attribute *adummy_attrs[] = {
+ &dev_attr_signal.attr,
+ NULL
+};
+
+static struct attribute_group adummy_group_attrs = {
+ .name = NULL, /* We want them in dev's root folder */
+ .attrs = adummy_attrs
+};
+
+static int __init
+adummy_start(struct atm_dev *dev)
+{
+ dev->ci_range.vpi_bits = 4;
+ dev->ci_range.vci_bits = 12;
+
+ return 0;
+}
+
+static int
+adummy_open(struct atm_vcc *vcc)
+{
+ short vpi = vcc->vpi;
+ int vci = vcc->vci;
+
+ if (vci == ATM_VCI_UNSPEC || vpi == ATM_VPI_UNSPEC)
+ return 0;
+
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+ set_bit(ATM_VF_READY, &vcc->flags);
+
+ return 0;
+}
+
+static void
+adummy_close(struct atm_vcc *vcc)
+{
+ clear_bit(ATM_VF_READY, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+}
+
+static int
+adummy_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ atomic_inc(&vcc->stats->tx);
+
+ return 0;
+}
+
+static int
+adummy_proc_read(struct atm_dev *dev, loff_t *pos, char *page)
+{
+ int left = *pos;
+
+ if (!left--)
+ return sprintf(page, "version %s\n", DRV_VERSION);
+
+ return 0;
+}
+
+static struct atmdev_ops adummy_ops =
+{
+ .open = adummy_open,
+ .close = adummy_close,
+ .send = adummy_send,
+ .proc_read = adummy_proc_read,
+ .owner = THIS_MODULE
+};
+
+static int __init adummy_init(void)
+{
+ struct atm_dev *atm_dev;
+ struct adummy_dev *adummy_dev;
+ int err = 0;
+
+ printk(KERN_ERR "adummy: version %s\n", DRV_VERSION);
+
+ adummy_dev = kzalloc(sizeof(struct adummy_dev),
+ GFP_KERNEL);
+ if (!adummy_dev) {
+ printk(KERN_ERR DEV_LABEL ": kzalloc() failed\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ atm_dev = atm_dev_register(DEV_LABEL, NULL, &adummy_ops, -1, NULL);
+ if (!atm_dev) {
+ printk(KERN_ERR DEV_LABEL ": atm_dev_register() failed\n");
+ err = -ENODEV;
+ goto out_kfree;
+ }
+
+ adummy_dev->atm_dev = atm_dev;
+ atm_dev->dev_data = adummy_dev;
+
+ if (sysfs_create_group(&atm_dev->class_dev.kobj, &adummy_group_attrs))
+ dev_err(&atm_dev->class_dev, "Could not register attrs for adummy\n");
+
+ if (adummy_start(atm_dev)) {
+ printk(KERN_ERR DEV_LABEL ": adummy_start() failed\n");
+ err = -ENODEV;
+ goto out_unregister;
+ }
+
+ list_add(&adummy_dev->entry, &adummy_devs);
+out:
+ return err;
+
+out_unregister:
+ atm_dev_deregister(atm_dev);
+out_kfree:
+ kfree(adummy_dev);
+ goto out;
+}
+
+static void __exit adummy_cleanup(void)
+{
+ struct adummy_dev *adummy_dev, *next;
+
+ list_for_each_entry_safe(adummy_dev, next, &adummy_devs, entry) {
+ atm_dev_deregister(adummy_dev->atm_dev);
+ kfree(adummy_dev);
+ }
+}
+
+module_init(adummy_init);
+module_exit(adummy_cleanup);
+
+MODULE_AUTHOR("chas williams <chas@cmf.nrl.navy.mil>");
+MODULE_DESCRIPTION("dummy ATM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
new file mode 100644
index 000000000..cd3f62eb1
--- /dev/null
+++ b/drivers/atm/ambassador.c
@@ -0,0 +1,2422 @@
+/*
+ Madge Ambassador ATM Adapter driver.
+ Copyright (C) 1995-1999 Madge Networks Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian
+ system and in the file COPYING in the Linux kernel source.
+*/
+
+/* * dedicated to the memory of Graham Gordon 1971-1998 * */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/atmdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/poison.h>
+#include <linux/bitrev.h>
+#include <linux/mutex.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/slab.h>
+
+#include <linux/atomic.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include "ambassador.h"
+
+#define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"
+#define description_string "Madge ATM Ambassador driver"
+#define version_string "1.2.4"
+
+static inline void __init show_version (void) {
+ printk ("%s version %s\n", description_string, version_string);
+}
+
+/*
+
+ Theory of Operation
+
+ I Hardware, detection, initialisation and shutdown.
+
+ 1. Supported Hardware
+
+ This driver is for the PCI ATMizer-based Ambassador card (except
+ very early versions). It is not suitable for the similar EISA "TR7"
+ card. Commercially, both cards are known as Collage Server ATM
+ adapters.
+
+ The loader supports image transfer to the card, image start and few
+ other miscellaneous commands.
+
+ Only AAL5 is supported with vpi = 0 and vci in the range 0 to 1023.
+
+ The cards are big-endian.
+
+ 2. Detection
+
+ Standard PCI stuff, the early cards are detected and rejected.
+
+ 3. Initialisation
+
+ The cards are reset and the self-test results are checked. The
+ microcode image is then transferred and started. This waits for a
+ pointer to a descriptor containing details of the host-based queues
+ and buffers and various parameters etc. Once they are processed
+ normal operations may begin. The BIA is read using a microcode
+ command.
+
+ 4. Shutdown
+
+ This may be accomplished either by a card reset or via the microcode
+ shutdown command. Further investigation required.
+
+ 5. Persistent state
+
+ The card reset does not affect PCI configuration (good) or the
+ contents of several other "shared run-time registers" (bad) which
+ include doorbell and interrupt control as well as EEPROM and PCI
+ control. The driver must be careful when modifying these registers
+ not to touch bits it does not use and to undo any changes at exit.
+
+ II Driver software
+
+ 0. Generalities
+
+ The adapter is quite intelligent (fast) and has a simple interface
+ (few features). VPI is always zero, 1024 VCIs are supported. There
+ is limited cell rate support. UBR channels can be capped and ABR
+ (explicit rate, but not EFCI) is supported. There is no CBR or VBR
+ support.
+
+ 1. Driver <-> Adapter Communication
+
+ Apart from the basic loader commands, the driver communicates
+ through three entities: the command queue (CQ), the transmit queue
+ pair (TXQ) and the receive queue pairs (RXQ). These three entities
+ are set up by the host and passed to the microcode just after it has
+ been started.
+
+ All queues are host-based circular queues. They are contiguous and
+ (due to hardware limitations) have some restrictions as to their
+ locations in (bus) memory. They are of the "full means the same as
+ empty so don't do that" variety since the adapter uses pointers
+ internally.
+
+ The queue pairs work as follows: one queue is for supply to the
+ adapter, items in it are pending and are owned by the adapter; the
+ other is the queue for return from the adapter, items in it have
+ been dealt with by the adapter. The host adds items to the supply
+ (TX descriptors and free RX buffer descriptors) and removes items
+ from the return (TX and RX completions). The adapter deals with out
+ of order completions.
+
+ Interrupts (card to host) and the doorbell (host to card) are used
+ for signalling.
+
+ 1. CQ
+
+ This is to communicate "open VC", "close VC", "get stats" etc. to
+ the adapter. At most one command is retired every millisecond by the
+ card. There is no out of order completion or notification. The
+ driver needs to check the return code of the command, waiting as
+ appropriate.
+
+ 2. TXQ
+
+ TX supply items are of variable length (scatter gather support) and
+ so the queue items are (more or less) pointers to the real thing.
+ Each TX supply item contains a unique, host-supplied handle (the skb
+ bus address seems most sensible as this works for Alphas as well,
+ there is no need to do any endian conversions on the handles).
+
+ TX return items consist of just the handles above.
+
+ 3. RXQ (up to 4 of these with different lengths and buffer sizes)
+
+ RX supply items consist of a unique, host-supplied handle (the skb
+ bus address again) and a pointer to the buffer data area.
+
+ RX return items consist of the handle above, the VC, length and a
+ status word. This just screams "oh so easy" doesn't it?
+
+ Note on RX pool sizes:
+
+ Each pool should have enough buffers to handle a back-to-back stream
+ of minimum sized frames on a single VC. For example:
+
+ frame spacing = 3us (about right)
+
+ delay = IRQ lat + RX handling + RX buffer replenish = 20 (us) (a guess)
+
+ min number of buffers for one VC = 1 + delay/spacing (buffers)
+
+ delay/spacing = latency = (20+2)/3 = 7 (buffers) (rounding up)
+
+ The 20us delay assumes that there is no need to sleep; if we need to
+ sleep to get buffers we are going to drop frames anyway.
+
+ In fact, each pool should have enough buffers to support the
+ simultaneous reassembly of a separate frame on each VC and cope with
+ the case in which frames complete in round robin cell fashion on
+ each VC.
+
+ Only one frame can complete at each cell arrival, so if "n" VCs are
+ open, the worst case is to have them all complete frames together
+ followed by all starting new frames together.
+
+ desired number of buffers = n + delay/spacing
+
+ These are the extreme requirements, however, they are "n+k" for some
+ "k" so we have only the constant to choose. This is the argument
+ rx_lats which current defaults to 7.
+
+ Actually, "n ? n+k : 0" is better and this is what is implemented,
+ subject to the limit given by the pool size.
+
+ 4. Driver locking
+
+ Simple spinlocks are used around the TX and RX queue mechanisms.
+ Anyone with a faster, working method is welcome to implement it.
+
+ The adapter command queue is protected with a spinlock. We always
+ wait for commands to complete.
+
+ A more complex form of locking is used around parts of the VC open
+ and close functions. There are three reasons for a lock: 1. we need
+ to do atomic rate reservation and release (not used yet), 2. Opening
+ sometimes involves two adapter commands which must not be separated
+ by another command on the same VC, 3. the changes to RX pool size
+ must be atomic. The lock needs to work over context switches, so we
+ use a semaphore.
+
+ III Hardware Features and Microcode Bugs
+
+ 1. Byte Ordering
+
+ *%^"$&%^$*&^"$(%^$#&^%$(&#%$*(&^#%!"!"!*!
+
+ 2. Memory access
+
+ All structures that are not accessed using DMA must be 4-byte
+ aligned (not a problem) and must not cross 4MB boundaries.
+
+ There is a DMA memory hole at E0000000-E00000FF (groan).
+
+ TX fragments (DMA read) must not cross 4MB boundaries (would be 16MB
+ but for a hardware bug).
+
+ RX buffers (DMA write) must not cross 16MB boundaries and must
+ include spare trailing bytes up to the next 4-byte boundary; they
+ will be written with rubbish.
+
+ The PLX likes to prefetch; if reading up to 4 u32 past the end of
+ each TX fragment is not a problem, then TX can be made to go a
+ little faster by passing a flag at init that disables a prefetch
+ workaround. We do not pass this flag. (new microcode only)
+
+ Now we:
+ . Note that alloc_skb rounds up size to a 16byte boundary.
+ . Ensure all areas do not traverse 4MB boundaries.
+ . Ensure all areas do not start at a E00000xx bus address.
+ (I cannot be certain, but this may always hold with Linux)
+ . Make all failures cause a loud message.
+ . Discard non-conforming SKBs (causes TX failure or RX fill delay).
+ . Discard non-conforming TX fragment descriptors (the TX fails).
+ In the future we could:
+ . Allow RX areas that traverse 4MB (but not 16MB) boundaries.
+ . Segment TX areas into some/more fragments, when necessary.
+ . Relax checks for non-DMA items (ignore hole).
+ . Give scatter-gather (iovec) requirements using ???. (?)
+
+ 3. VC close is broken (only for new microcode)
+
+ The VC close adapter microcode command fails to do anything if any
+ frames have been received on the VC but none have been transmitted.
+ Frames continue to be reassembled and passed (with IRQ) to the
+ driver.
+
+ IV To Do List
+
+ . Fix bugs!
+
+ . Timer code may be broken.
+
+ . Deal with buggy VC close (somehow) in microcode 12.
+
+ . Handle interrupted and/or non-blocking writes - is this a job for
+ the protocol layer?
+
+ . Add code to break up TX fragments when they span 4MB boundaries.
+
+ . Add SUNI phy layer (need to know where SUNI lives on card).
+
+ . Implement a tx_alloc fn to (a) satisfy TX alignment etc. and (b)
+ leave extra headroom space for Ambassador TX descriptors.
+
+ . Understand these elements of struct atm_vcc: recvq (proto?),
+ sleep, callback, listenq, backlog_quota, reply and user_back.
+
+ . Adjust TX/RX skb allocation to favour IP with LANE/CLIP (configurable).
+
+ . Impose a TX-pending limit (2?) on each VC, help avoid TX q overflow.
+
+ . Decide whether RX buffer recycling is or can be made completely safe;
+ turn it back on. It looks like Werner is going to axe this.
+
+ . Implement QoS changes on open VCs (involves extracting parts of VC open
+ and close into separate functions and using them to make changes).
+
+ . Hack on command queue so that someone can issue multiple commands and wait
+ on the last one (OR only "no-op" or "wait" commands are waited for).
+
+ . Eliminate need for while-schedule around do_command.
+
+*/
+
+static void do_housekeeping (unsigned long arg);
+/********** globals **********/
+
+static unsigned short debug = 0;
+static unsigned int cmds = 8;
+static unsigned int txs = 32;
+static unsigned int rxs[NUM_RX_POOLS] = { 64, 64, 64, 64 };
+static unsigned int rxs_bs[NUM_RX_POOLS] = { 4080, 12240, 36720, 65535 };
+static unsigned int rx_lats = 7;
+static unsigned char pci_lat = 0;
+
+static const unsigned long onegigmask = -1 << 30;
+
+/********** access to adapter **********/
+
+static inline void wr_plain (const amb_dev * dev, size_t addr, u32 data) {
+ PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x", addr, data);
+#ifdef AMB_MMIO
+ dev->membase[addr / sizeof(u32)] = data;
+#else
+ outl (data, dev->iobase + addr);
+#endif
+}
+
+static inline u32 rd_plain (const amb_dev * dev, size_t addr) {
+#ifdef AMB_MMIO
+ u32 data = dev->membase[addr / sizeof(u32)];
+#else
+ u32 data = inl (dev->iobase + addr);
+#endif
+ PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x", addr, data);
+ return data;
+}
+
+static inline void wr_mem (const amb_dev * dev, size_t addr, u32 data) {
+ __be32 be = cpu_to_be32 (data);
+ PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x b[%08x]", addr, data, be);
+#ifdef AMB_MMIO
+ dev->membase[addr / sizeof(u32)] = be;
+#else
+ outl (be, dev->iobase + addr);
+#endif
+}
+
+static inline u32 rd_mem (const amb_dev * dev, size_t addr) {
+#ifdef AMB_MMIO
+ __be32 be = dev->membase[addr / sizeof(u32)];
+#else
+ __be32 be = inl (dev->iobase + addr);
+#endif
+ u32 data = be32_to_cpu (be);
+ PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x b[%08x]", addr, data, be);
+ return data;
+}
+
+/********** dump routines **********/
+
+static inline void dump_registers (const amb_dev * dev) {
+#ifdef DEBUG_AMBASSADOR
+ if (debug & DBG_REGS) {
+ size_t i;
+ PRINTD (DBG_REGS, "reading PLX control: ");
+ for (i = 0x00; i < 0x30; i += sizeof(u32))
+ rd_mem (dev, i);
+ PRINTD (DBG_REGS, "reading mailboxes: ");
+ for (i = 0x40; i < 0x60; i += sizeof(u32))
+ rd_mem (dev, i);
+ PRINTD (DBG_REGS, "reading doorb irqev irqen reset:");
+ for (i = 0x60; i < 0x70; i += sizeof(u32))
+ rd_mem (dev, i);
+ }
+#else
+ (void) dev;
+#endif
+ return;
+}
+
+static inline void dump_loader_block (volatile loader_block * lb) {
+#ifdef DEBUG_AMBASSADOR
+ unsigned int i;
+ PRINTDB (DBG_LOAD, "lb @ %p; res: %d, cmd: %d, pay:",
+ lb, be32_to_cpu (lb->result), be32_to_cpu (lb->command));
+ for (i = 0; i < MAX_COMMAND_DATA; ++i)
+ PRINTDM (DBG_LOAD, " %08x", be32_to_cpu (lb->payload.data[i]));
+ PRINTDE (DBG_LOAD, ", vld: %08x", be32_to_cpu (lb->valid));
+#else
+ (void) lb;
+#endif
+ return;
+}
+
+static inline void dump_command (command * cmd) {
+#ifdef DEBUG_AMBASSADOR
+ unsigned int i;
+ PRINTDB (DBG_CMD, "cmd @ %p, req: %08x, pars:",
+ cmd, /*be32_to_cpu*/ (cmd->request));
+ for (i = 0; i < 3; ++i)
+ PRINTDM (DBG_CMD, " %08x", /*be32_to_cpu*/ (cmd->args.par[i]));
+ PRINTDE (DBG_CMD, "");
+#else
+ (void) cmd;
+#endif
+ return;
+}
+
+static inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) {
+#ifdef DEBUG_AMBASSADOR
+ unsigned int i;
+ unsigned char * data = skb->data;
+ PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc);
+ for (i=0; i<skb->len && i < 256;i++)
+ PRINTDM (DBG_DATA, "%02x ", data[i]);
+ PRINTDE (DBG_DATA,"");
+#else
+ (void) prefix;
+ (void) vc;
+ (void) skb;
+#endif
+ return;
+}
+
+/********** check memory areas for use by Ambassador **********/
+
+/* see limitations under Hardware Features */
+
+static int check_area (void * start, size_t length) {
+ // assumes length > 0
+ const u32 fourmegmask = -1 << 22;
+ const u32 twofivesixmask = -1 << 8;
+ const u32 starthole = 0xE0000000;
+ u32 startaddress = virt_to_bus (start);
+ u32 lastaddress = startaddress+length-1;
+ if ((startaddress ^ lastaddress) & fourmegmask ||
+ (startaddress & twofivesixmask) == starthole) {
+ PRINTK (KERN_ERR, "check_area failure: [%x,%x] - mail maintainer!",
+ startaddress, lastaddress);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/********** free an skb (as per ATM device driver documentation) **********/
+
+static void amb_kfree_skb (struct sk_buff * skb) {
+ if (ATM_SKB(skb)->vcc->pop) {
+ ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
+ } else {
+ dev_kfree_skb_any (skb);
+ }
+}
+
+/********** TX completion **********/
+
+static void tx_complete (amb_dev * dev, tx_out * tx) {
+ tx_simple * tx_descr = bus_to_virt (tx->handle);
+ struct sk_buff * skb = tx_descr->skb;
+
+ PRINTD (DBG_FLOW|DBG_TX, "tx_complete %p %p", dev, tx);
+
+ // VC layer stats
+ atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
+
+ // free the descriptor
+ kfree (tx_descr);
+
+ // free the skb
+ amb_kfree_skb (skb);
+
+ dev->stats.tx_ok++;
+ return;
+}
+
+/********** RX completion **********/
+
+static void rx_complete (amb_dev * dev, rx_out * rx) {
+ struct sk_buff * skb = bus_to_virt (rx->handle);
+ u16 vc = be16_to_cpu (rx->vc);
+ // unused: u16 lec_id = be16_to_cpu (rx->lec_id);
+ u16 status = be16_to_cpu (rx->status);
+ u16 rx_len = be16_to_cpu (rx->length);
+
+ PRINTD (DBG_FLOW|DBG_RX, "rx_complete %p %p (len=%hu)", dev, rx, rx_len);
+
+ // XXX move this in and add to VC stats ???
+ if (!status) {
+ struct atm_vcc * atm_vcc = dev->rxer[vc];
+ dev->stats.rx.ok++;
+
+ if (atm_vcc) {
+
+ if (rx_len <= atm_vcc->qos.rxtp.max_sdu) {
+
+ if (atm_charge (atm_vcc, skb->truesize)) {
+
+ // prepare socket buffer
+ ATM_SKB(skb)->vcc = atm_vcc;
+ skb_put (skb, rx_len);
+
+ dump_skb ("<<<", vc, skb);
+
+ // VC layer stats
+ atomic_inc(&atm_vcc->stats->rx);
+ __net_timestamp(skb);
+ // end of our responsibility
+ atm_vcc->push (atm_vcc, skb);
+ return;
+
+ } else {
+ // someone fix this (message), please!
+ PRINTD (DBG_INFO|DBG_RX, "dropped thanks to atm_charge (vc %hu, truesize %u)", vc, skb->truesize);
+ // drop stats incremented in atm_charge
+ }
+
+ } else {
+ PRINTK (KERN_INFO, "dropped over-size frame");
+ // should we count this?
+ atomic_inc(&atm_vcc->stats->rx_drop);
+ }
+
+ } else {
+ PRINTD (DBG_WARN|DBG_RX, "got frame but RX closed for channel %hu", vc);
+ // this is an adapter bug, only in new version of microcode
+ }
+
+ } else {
+ dev->stats.rx.error++;
+ if (status & CRC_ERR)
+ dev->stats.rx.badcrc++;
+ if (status & LEN_ERR)
+ dev->stats.rx.toolong++;
+ if (status & ABORT_ERR)
+ dev->stats.rx.aborted++;
+ if (status & UNUSED_ERR)
+ dev->stats.rx.unused++;
+ }
+
+ dev_kfree_skb_any (skb);
+ return;
+}
+
+/*
+
+ Note on queue handling.
+
+ Here "give" and "take" refer to queue entries and a queue (pair)
+ rather than frames to or from the host or adapter. Empty frame
+ buffers are given to the RX queue pair and returned unused or
+ containing RX frames. TX frames (well, pointers to TX fragment
+ lists) are given to the TX queue pair, completions are returned.
+
+*/
+
+/********** command queue **********/
+
+// I really don't like this, but it's the best I can do at the moment
+
+// also, the callers are responsible for byte order as the microcode
+// sometimes does 16-bit accesses (yuk yuk yuk)
+
+static int command_do (amb_dev * dev, command * cmd) {
+ amb_cq * cq = &dev->cq;
+ volatile amb_cq_ptrs * ptrs = &cq->ptrs;
+ command * my_slot;
+
+ PRINTD (DBG_FLOW|DBG_CMD, "command_do %p", dev);
+
+ if (test_bit (dead, &dev->flags))
+ return 0;
+
+ spin_lock (&cq->lock);
+
+ // if not full...
+ if (cq->pending < cq->maximum) {
+ // remember my slot for later
+ my_slot = ptrs->in;
+ PRINTD (DBG_CMD, "command in slot %p", my_slot);
+
+ dump_command (cmd);
+
+ // copy command in
+ *ptrs->in = *cmd;
+ cq->pending++;
+ ptrs->in = NEXTQ (ptrs->in, ptrs->start, ptrs->limit);
+
+ // mail the command
+ wr_mem (dev, offsetof(amb_mem, mb.adapter.cmd_address), virt_to_bus (ptrs->in));
+
+ if (cq->pending > cq->high)
+ cq->high = cq->pending;
+ spin_unlock (&cq->lock);
+
+ // these comments were in a while-loop before, msleep removes the loop
+ // go to sleep
+ // PRINTD (DBG_CMD, "wait: sleeping %lu for command", timeout);
+ msleep(cq->pending);
+
+ // wait for my slot to be reached (all waiters are here or above, until...)
+ while (ptrs->out != my_slot) {
+ PRINTD (DBG_CMD, "wait: command slot (now at %p)", ptrs->out);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ }
+
+ // wait on my slot (... one gets to its slot, and... )
+ while (ptrs->out->request != cpu_to_be32 (SRB_COMPLETE)) {
+ PRINTD (DBG_CMD, "wait: command slot completion");
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ }
+
+ PRINTD (DBG_CMD, "command complete");
+ // update queue (... moves the queue along to the next slot)
+ spin_lock (&cq->lock);
+ cq->pending--;
+ // copy command out
+ *cmd = *ptrs->out;
+ ptrs->out = NEXTQ (ptrs->out, ptrs->start, ptrs->limit);
+ spin_unlock (&cq->lock);
+
+ return 0;
+ } else {
+ cq->filled++;
+ spin_unlock (&cq->lock);
+ return -EAGAIN;
+ }
+
+}
+
+/********** TX queue pair **********/
+
+static int tx_give (amb_dev * dev, tx_in * tx) {
+ amb_txq * txq = &dev->txq;
+ unsigned long flags;
+
+ PRINTD (DBG_FLOW|DBG_TX, "tx_give %p", dev);
+
+ if (test_bit (dead, &dev->flags))
+ return 0;
+
+ spin_lock_irqsave (&txq->lock, flags);
+
+ if (txq->pending < txq->maximum) {
+ PRINTD (DBG_TX, "TX in slot %p", txq->in.ptr);
+
+ *txq->in.ptr = *tx;
+ txq->pending++;
+ txq->in.ptr = NEXTQ (txq->in.ptr, txq->in.start, txq->in.limit);
+ // hand over the TX and ring the bell
+ wr_mem (dev, offsetof(amb_mem, mb.adapter.tx_address), virt_to_bus (txq->in.ptr));
+ wr_mem (dev, offsetof(amb_mem, doorbell), TX_FRAME);
+
+ if (txq->pending > txq->high)
+ txq->high = txq->pending;
+ spin_unlock_irqrestore (&txq->lock, flags);
+ return 0;
+ } else {
+ txq->filled++;
+ spin_unlock_irqrestore (&txq->lock, flags);
+ return -EAGAIN;
+ }
+}
+
+static int tx_take (amb_dev * dev) {
+ amb_txq * txq = &dev->txq;
+ unsigned long flags;
+
+ PRINTD (DBG_FLOW|DBG_TX, "tx_take %p", dev);
+
+ spin_lock_irqsave (&txq->lock, flags);
+
+ if (txq->pending && txq->out.ptr->handle) {
+ // deal with TX completion
+ tx_complete (dev, txq->out.ptr);
+ // mark unused again
+ txq->out.ptr->handle = 0;
+ // remove item
+ txq->pending--;
+ txq->out.ptr = NEXTQ (txq->out.ptr, txq->out.start, txq->out.limit);
+
+ spin_unlock_irqrestore (&txq->lock, flags);
+ return 0;
+ } else {
+
+ spin_unlock_irqrestore (&txq->lock, flags);
+ return -1;
+ }
+}
+
+/********** RX queue pairs **********/
+
+static int rx_give (amb_dev * dev, rx_in * rx, unsigned char pool) {
+ amb_rxq * rxq = &dev->rxq[pool];
+ unsigned long flags;
+
+ PRINTD (DBG_FLOW|DBG_RX, "rx_give %p[%hu]", dev, pool);
+
+ spin_lock_irqsave (&rxq->lock, flags);
+
+ if (rxq->pending < rxq->maximum) {
+ PRINTD (DBG_RX, "RX in slot %p", rxq->in.ptr);
+
+ *rxq->in.ptr = *rx;
+ rxq->pending++;
+ rxq->in.ptr = NEXTQ (rxq->in.ptr, rxq->in.start, rxq->in.limit);
+ // hand over the RX buffer
+ wr_mem (dev, offsetof(amb_mem, mb.adapter.rx_address[pool]), virt_to_bus (rxq->in.ptr));
+
+ spin_unlock_irqrestore (&rxq->lock, flags);
+ return 0;
+ } else {
+ spin_unlock_irqrestore (&rxq->lock, flags);
+ return -1;
+ }
+}
+
+static int rx_take (amb_dev * dev, unsigned char pool) {
+ amb_rxq * rxq = &dev->rxq[pool];
+ unsigned long flags;
+
+ PRINTD (DBG_FLOW|DBG_RX, "rx_take %p[%hu]", dev, pool);
+
+ spin_lock_irqsave (&rxq->lock, flags);
+
+ if (rxq->pending && (rxq->out.ptr->status || rxq->out.ptr->length)) {
+ // deal with RX completion
+ rx_complete (dev, rxq->out.ptr);
+ // mark unused again
+ rxq->out.ptr->status = 0;
+ rxq->out.ptr->length = 0;
+ // remove item
+ rxq->pending--;
+ rxq->out.ptr = NEXTQ (rxq->out.ptr, rxq->out.start, rxq->out.limit);
+
+ if (rxq->pending < rxq->low)
+ rxq->low = rxq->pending;
+ spin_unlock_irqrestore (&rxq->lock, flags);
+ return 0;
+ } else {
+ if (!rxq->pending && rxq->buffers_wanted)
+ rxq->emptied++;
+ spin_unlock_irqrestore (&rxq->lock, flags);
+ return -1;
+ }
+}
+
+/********** RX Pool handling **********/
+
+/* pre: buffers_wanted = 0, post: pending = 0 */
+static void drain_rx_pool (amb_dev * dev, unsigned char pool) {
+ amb_rxq * rxq = &dev->rxq[pool];
+
+ PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pool %p %hu", dev, pool);
+
+ if (test_bit (dead, &dev->flags))
+ return;
+
+ /* we are not quite like the fill pool routines as we cannot just
+ remove one buffer, we have to remove all of them, but we might as
+ well pretend... */
+ if (rxq->pending > rxq->buffers_wanted) {
+ command cmd;
+ cmd.request = cpu_to_be32 (SRB_FLUSH_BUFFER_Q);
+ cmd.args.flush.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT);
+ while (command_do (dev, &cmd))
+ schedule();
+ /* the pool may also be emptied via the interrupt handler */
+ while (rxq->pending > rxq->buffers_wanted)
+ if (rx_take (dev, pool))
+ schedule();
+ }
+
+ return;
+}
+
+static void drain_rx_pools (amb_dev * dev) {
+ unsigned char pool;
+
+ PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pools %p", dev);
+
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool)
+ drain_rx_pool (dev, pool);
+}
+
+static void fill_rx_pool (amb_dev * dev, unsigned char pool,
+ gfp_t priority)
+{
+ rx_in rx;
+ amb_rxq * rxq;
+
+ PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pool %p %hu %x", dev, pool, priority);
+
+ if (test_bit (dead, &dev->flags))
+ return;
+
+ rxq = &dev->rxq[pool];
+ while (rxq->pending < rxq->maximum && rxq->pending < rxq->buffers_wanted) {
+
+ struct sk_buff * skb = alloc_skb (rxq->buffer_size, priority);
+ if (!skb) {
+ PRINTD (DBG_SKB|DBG_POOL, "failed to allocate skb for RX pool %hu", pool);
+ return;
+ }
+ if (check_area (skb->data, skb->truesize)) {
+ dev_kfree_skb_any (skb);
+ return;
+ }
+ // cast needed as there is no %? for pointer differences
+ PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li",
+ skb, skb->head, (long) skb_end_offset(skb));
+ rx.handle = virt_to_bus (skb);
+ rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
+ if (rx_give (dev, &rx, pool))
+ dev_kfree_skb_any (skb);
+
+ }
+
+ return;
+}
+
+// top up all RX pools
+static void fill_rx_pools (amb_dev * dev) {
+ unsigned char pool;
+
+ PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pools %p", dev);
+
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool)
+ fill_rx_pool (dev, pool, GFP_ATOMIC);
+
+ return;
+}
+
+/********** enable host interrupts **********/
+
+static void interrupts_on (amb_dev * dev) {
+ wr_plain (dev, offsetof(amb_mem, interrupt_control),
+ rd_plain (dev, offsetof(amb_mem, interrupt_control))
+ | AMB_INTERRUPT_BITS);
+}
+
+/********** disable host interrupts **********/
+
+static void interrupts_off (amb_dev * dev) {
+ wr_plain (dev, offsetof(amb_mem, interrupt_control),
+ rd_plain (dev, offsetof(amb_mem, interrupt_control))
+ &~ AMB_INTERRUPT_BITS);
+}
+
+/********** interrupt handling **********/
+
+static irqreturn_t interrupt_handler(int irq, void *dev_id) {
+ amb_dev * dev = dev_id;
+
+ PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id);
+
+ {
+ u32 interrupt = rd_plain (dev, offsetof(amb_mem, interrupt));
+
+ // for us or someone else sharing the same interrupt
+ if (!interrupt) {
+ PRINTD (DBG_IRQ, "irq not for me: %d", irq);
+ return IRQ_NONE;
+ }
+
+ // definitely for us
+ PRINTD (DBG_IRQ, "FYI: interrupt was %08x", interrupt);
+ wr_plain (dev, offsetof(amb_mem, interrupt), -1);
+ }
+
+ {
+ unsigned int irq_work = 0;
+ unsigned char pool;
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool)
+ while (!rx_take (dev, pool))
+ ++irq_work;
+ while (!tx_take (dev))
+ ++irq_work;
+
+ if (irq_work) {
+ fill_rx_pools (dev);
+
+ PRINTD (DBG_IRQ, "work done: %u", irq_work);
+ } else {
+ PRINTD (DBG_IRQ|DBG_WARN, "no work done");
+ }
+ }
+
+ PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id);
+ return IRQ_HANDLED;
+}
+
+/********** make rate (not quite as much fun as Horizon) **********/
+
+static int make_rate (unsigned int rate, rounding r,
+ u16 * bits, unsigned int * actual) {
+ unsigned char exp = -1; // hush gcc
+ unsigned int man = -1; // hush gcc
+
+ PRINTD (DBG_FLOW|DBG_QOS, "make_rate %u", rate);
+
+ // rates in cells per second, ITU format (nasty 16-bit floating-point)
+ // given 5-bit e and 9-bit m:
+ // rate = EITHER (1+m/2^9)*2^e OR 0
+ // bits = EITHER 1<<14 | e<<9 | m OR 0
+ // (bit 15 is "reserved", bit 14 "non-zero")
+ // smallest rate is 0 (special representation)
+ // largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1)
+ // smallest non-zero rate is (1+0/512)*2^0 = 1 (> 0)
+ // simple algorithm:
+ // find position of top bit, this gives e
+ // remove top bit and shift (rounding if feeling clever) by 9-e
+
+ // ucode bug: please don't set bit 14! so 0 rate not representable
+
+ if (rate > 0xffc00000U) {
+ // larger than largest representable rate
+
+ if (r == round_up) {
+ return -EINVAL;
+ } else {
+ exp = 31;
+ man = 511;
+ }
+
+ } else if (rate) {
+ // representable rate
+
+ exp = 31;
+ man = rate;
+
+ // invariant: rate = man*2^(exp-31)
+ while (!(man & (1<<31))) {
+ exp = exp - 1;
+ man = man<<1;
+ }
+
+ // man has top bit set
+ // rate = (2^31+(man-2^31))*2^(exp-31)
+ // rate = (1+(man-2^31)/2^31)*2^exp
+ man = man<<1;
+ man &= 0xffffffffU; // a nop on 32-bit systems
+ // rate = (1+man/2^32)*2^exp
+
+ // exp is in the range 0 to 31, man is in the range 0 to 2^32-1
+ // time to lose significance... we want m in the range 0 to 2^9-1
+ // rounding presents a minor problem... we first decide which way
+ // we are rounding (based on given rounding direction and possibly
+ // the bits of the mantissa that are to be discarded).
+
+ switch (r) {
+ case round_down: {
+ // just truncate
+ man = man>>(32-9);
+ break;
+ }
+ case round_up: {
+ // check all bits that we are discarding
+ if (man & (~0U>>9)) {
+ man = (man>>(32-9)) + 1;
+ if (man == (1<<9)) {
+ // no need to check for round up outside of range
+ man = 0;
+ exp += 1;
+ }
+ } else {
+ man = (man>>(32-9));
+ }
+ break;
+ }
+ case round_nearest: {
+ // check msb that we are discarding
+ if (man & (1<<(32-9-1))) {
+ man = (man>>(32-9)) + 1;
+ if (man == (1<<9)) {
+ // no need to check for round up outside of range
+ man = 0;
+ exp += 1;
+ }
+ } else {
+ man = (man>>(32-9));
+ }
+ break;
+ }
+ }
+
+ } else {
+ // zero rate - not representable
+
+ if (r == round_down) {
+ return -EINVAL;
+ } else {
+ exp = 0;
+ man = 0;
+ }
+
+ }
+
+ PRINTD (DBG_QOS, "rate: man=%u, exp=%hu", man, exp);
+
+ if (bits)
+ *bits = /* (1<<14) | */ (exp<<9) | man;
+
+ if (actual)
+ *actual = (exp >= 9)
+ ? (1 << exp) + (man << (exp-9))
+ : (1 << exp) + ((man + (1<<(9-exp-1))) >> (9-exp));
+
+ return 0;
+}
+
+/********** Linux ATM Operations **********/
+
+// some are not yet implemented while others do not make sense for
+// this device
+
+/********** Open a VC **********/
+
+static int amb_open (struct atm_vcc * atm_vcc)
+{
+ int error;
+
+ struct atm_qos * qos;
+ struct atm_trafprm * txtp;
+ struct atm_trafprm * rxtp;
+ u16 tx_rate_bits = -1; // hush gcc
+ u16 tx_vc_bits = -1; // hush gcc
+ u16 tx_frame_bits = -1; // hush gcc
+
+ amb_dev * dev = AMB_DEV(atm_vcc->dev);
+ amb_vcc * vcc;
+ unsigned char pool = -1; // hush gcc
+ short vpi = atm_vcc->vpi;
+ int vci = atm_vcc->vci;
+
+ PRINTD (DBG_FLOW|DBG_VCC, "amb_open %x %x", vpi, vci);
+
+#ifdef ATM_VPI_UNSPEC
+ // UNSPEC is deprecated, remove this code eventually
+ if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) {
+ PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)");
+ return -EINVAL;
+ }
+#endif
+
+ if (!(0 <= vpi && vpi < (1<<NUM_VPI_BITS) &&
+ 0 <= vci && vci < (1<<NUM_VCI_BITS))) {
+ PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci);
+ return -EINVAL;
+ }
+
+ qos = &atm_vcc->qos;
+
+ if (qos->aal != ATM_AAL5) {
+ PRINTD (DBG_QOS, "AAL not supported");
+ return -EINVAL;
+ }
+
+ // traffic parameters
+
+ PRINTD (DBG_QOS, "TX:");
+ txtp = &qos->txtp;
+ if (txtp->traffic_class != ATM_NONE) {
+ switch (txtp->traffic_class) {
+ case ATM_UBR: {
+ // we take "the PCR" as a rate-cap
+ int pcr = atm_pcr_goal (txtp);
+ if (!pcr) {
+ // no rate cap
+ tx_rate_bits = 0;
+ tx_vc_bits = TX_UBR;
+ tx_frame_bits = TX_FRAME_NOTCAP;
+ } else {
+ rounding r;
+ if (pcr < 0) {
+ r = round_down;
+ pcr = -pcr;
+ } else {
+ r = round_up;
+ }
+ error = make_rate (pcr, r, &tx_rate_bits, NULL);
+ if (error)
+ return error;
+ tx_vc_bits = TX_UBR_CAPPED;
+ tx_frame_bits = TX_FRAME_CAPPED;
+ }
+ break;
+ }
+#if 0
+ case ATM_ABR: {
+ pcr = atm_pcr_goal (txtp);
+ PRINTD (DBG_QOS, "pcr goal = %d", pcr);
+ break;
+ }
+#endif
+ default: {
+ // PRINTD (DBG_QOS, "request for non-UBR/ABR denied");
+ PRINTD (DBG_QOS, "request for non-UBR denied");
+ return -EINVAL;
+ }
+ }
+ PRINTD (DBG_QOS, "tx_rate_bits=%hx, tx_vc_bits=%hx",
+ tx_rate_bits, tx_vc_bits);
+ }
+
+ PRINTD (DBG_QOS, "RX:");
+ rxtp = &qos->rxtp;
+ if (rxtp->traffic_class == ATM_NONE) {
+ // do nothing
+ } else {
+ // choose an RX pool (arranged in increasing size)
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool)
+ if ((unsigned int) rxtp->max_sdu <= dev->rxq[pool].buffer_size) {
+ PRINTD (DBG_VCC|DBG_QOS|DBG_POOL, "chose pool %hu (max_sdu %u <= %u)",
+ pool, rxtp->max_sdu, dev->rxq[pool].buffer_size);
+ break;
+ }
+ if (pool == NUM_RX_POOLS) {
+ PRINTD (DBG_WARN|DBG_VCC|DBG_QOS|DBG_POOL,
+ "no pool suitable for VC (RX max_sdu %d is too large)",
+ rxtp->max_sdu);
+ return -EINVAL;
+ }
+
+ switch (rxtp->traffic_class) {
+ case ATM_UBR: {
+ break;
+ }
+#if 0
+ case ATM_ABR: {
+ pcr = atm_pcr_goal (rxtp);
+ PRINTD (DBG_QOS, "pcr goal = %d", pcr);
+ break;
+ }
+#endif
+ default: {
+ // PRINTD (DBG_QOS, "request for non-UBR/ABR denied");
+ PRINTD (DBG_QOS, "request for non-UBR denied");
+ return -EINVAL;
+ }
+ }
+ }
+
+ // get space for our vcc stuff
+ vcc = kmalloc (sizeof(amb_vcc), GFP_KERNEL);
+ if (!vcc) {
+ PRINTK (KERN_ERR, "out of memory!");
+ return -ENOMEM;
+ }
+ atm_vcc->dev_data = (void *) vcc;
+
+ // no failures beyond this point
+
+ // we are not really "immediately before allocating the connection
+ // identifier in hardware", but it will just have to do!
+ set_bit(ATM_VF_ADDR,&atm_vcc->flags);
+
+ if (txtp->traffic_class != ATM_NONE) {
+ command cmd;
+
+ vcc->tx_frame_bits = tx_frame_bits;
+
+ mutex_lock(&dev->vcc_sf);
+ if (dev->rxer[vci]) {
+ // RXer on the channel already, just modify rate...
+ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE);
+ cmd.args.modify_rate.vc = cpu_to_be32 (vci); // vpi 0
+ cmd.args.modify_rate.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT);
+ while (command_do (dev, &cmd))
+ schedule();
+ // ... and TX flags, preserving the RX pool
+ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
+ cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0
+ cmd.args.modify_flags.flags = cpu_to_be32
+ ( (AMB_VCC(dev->rxer[vci])->rx_info.pool << SRB_POOL_SHIFT)
+ | (tx_vc_bits << SRB_FLAGS_SHIFT) );
+ while (command_do (dev, &cmd))
+ schedule();
+ } else {
+ // no RXer on the channel, just open (with pool zero)
+ cmd.request = cpu_to_be32 (SRB_OPEN_VC);
+ cmd.args.open.vc = cpu_to_be32 (vci); // vpi 0
+ cmd.args.open.flags = cpu_to_be32 (tx_vc_bits << SRB_FLAGS_SHIFT);
+ cmd.args.open.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT);
+ while (command_do (dev, &cmd))
+ schedule();
+ }
+ dev->txer[vci].tx_present = 1;
+ mutex_unlock(&dev->vcc_sf);
+ }
+
+ if (rxtp->traffic_class != ATM_NONE) {
+ command cmd;
+
+ vcc->rx_info.pool = pool;
+
+ mutex_lock(&dev->vcc_sf);
+ /* grow RX buffer pool */
+ if (!dev->rxq[pool].buffers_wanted)
+ dev->rxq[pool].buffers_wanted = rx_lats;
+ dev->rxq[pool].buffers_wanted += 1;
+ fill_rx_pool (dev, pool, GFP_KERNEL);
+
+ if (dev->txer[vci].tx_present) {
+ // TXer on the channel already
+ // switch (from pool zero) to this pool, preserving the TX bits
+ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
+ cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0
+ cmd.args.modify_flags.flags = cpu_to_be32
+ ( (pool << SRB_POOL_SHIFT)
+ | (dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT) );
+ } else {
+ // no TXer on the channel, open the VC (with no rate info)
+ cmd.request = cpu_to_be32 (SRB_OPEN_VC);
+ cmd.args.open.vc = cpu_to_be32 (vci); // vpi 0
+ cmd.args.open.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT);
+ cmd.args.open.rate = cpu_to_be32 (0);
+ }
+ while (command_do (dev, &cmd))
+ schedule();
+ // this link allows RX frames through
+ dev->rxer[vci] = atm_vcc;
+ mutex_unlock(&dev->vcc_sf);
+ }
+
+ // indicate readiness
+ set_bit(ATM_VF_READY,&atm_vcc->flags);
+
+ return 0;
+}
+
+/********** Close a VC **********/
+
+static void amb_close (struct atm_vcc * atm_vcc) {
+ amb_dev * dev = AMB_DEV (atm_vcc->dev);
+ amb_vcc * vcc = AMB_VCC (atm_vcc);
+ u16 vci = atm_vcc->vci;
+
+ PRINTD (DBG_VCC|DBG_FLOW, "amb_close");
+
+ // indicate unreadiness
+ clear_bit(ATM_VF_READY,&atm_vcc->flags);
+
+ // disable TXing
+ if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
+ command cmd;
+
+ mutex_lock(&dev->vcc_sf);
+ if (dev->rxer[vci]) {
+ // RXer still on the channel, just modify rate... XXX not really needed
+ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE);
+ cmd.args.modify_rate.vc = cpu_to_be32 (vci); // vpi 0
+ cmd.args.modify_rate.rate = cpu_to_be32 (0);
+ // ... and clear TX rate flags (XXX to stop RM cell output?), preserving RX pool
+ } else {
+ // no RXer on the channel, close channel
+ cmd.request = cpu_to_be32 (SRB_CLOSE_VC);
+ cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0
+ }
+ dev->txer[vci].tx_present = 0;
+ while (command_do (dev, &cmd))
+ schedule();
+ mutex_unlock(&dev->vcc_sf);
+ }
+
+ // disable RXing
+ if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ command cmd;
+
+ // this is (the?) one reason why we need the amb_vcc struct
+ unsigned char pool = vcc->rx_info.pool;
+
+ mutex_lock(&dev->vcc_sf);
+ if (dev->txer[vci].tx_present) {
+ // TXer still on the channel, just go to pool zero XXX not really needed
+ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
+ cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0
+ cmd.args.modify_flags.flags = cpu_to_be32
+ (dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT);
+ } else {
+ // no TXer on the channel, close the VC
+ cmd.request = cpu_to_be32 (SRB_CLOSE_VC);
+ cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0
+ }
+ // forget the rxer - no more skbs will be pushed
+ if (atm_vcc != dev->rxer[vci])
+ PRINTK (KERN_ERR, "%s vcc=%p rxer[vci]=%p",
+ "arghhh! we're going to die!",
+ vcc, dev->rxer[vci]);
+ dev->rxer[vci] = NULL;
+ while (command_do (dev, &cmd))
+ schedule();
+
+ /* shrink RX buffer pool */
+ dev->rxq[pool].buffers_wanted -= 1;
+ if (dev->rxq[pool].buffers_wanted == rx_lats) {
+ dev->rxq[pool].buffers_wanted = 0;
+ drain_rx_pool (dev, pool);
+ }
+ mutex_unlock(&dev->vcc_sf);
+ }
+
+ // free our structure
+ kfree (vcc);
+
+ // say the VPI/VCI is free again
+ clear_bit(ATM_VF_ADDR,&atm_vcc->flags);
+
+ return;
+}
+
+/********** Send **********/
+
+static int amb_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
+ amb_dev * dev = AMB_DEV(atm_vcc->dev);
+ amb_vcc * vcc = AMB_VCC(atm_vcc);
+ u16 vc = atm_vcc->vci;
+ unsigned int tx_len = skb->len;
+ unsigned char * tx_data = skb->data;
+ tx_simple * tx_descr;
+ tx_in tx;
+
+ if (test_bit (dead, &dev->flags))
+ return -EIO;
+
+ PRINTD (DBG_FLOW|DBG_TX, "amb_send vc %x data %p len %u",
+ vc, tx_data, tx_len);
+
+ dump_skb (">>>", vc, skb);
+
+ if (!dev->txer[vc].tx_present) {
+ PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", vc);
+ return -EBADFD;
+ }
+
+ // this is a driver private field so we have to set it ourselves,
+ // despite the fact that we are _required_ to use it to check for a
+ // pop function
+ ATM_SKB(skb)->vcc = atm_vcc;
+
+ if (skb->len > (size_t) atm_vcc->qos.txtp.max_sdu) {
+ PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping...");
+ return -EIO;
+ }
+
+ if (check_area (skb->data, skb->len)) {
+ atomic_inc(&atm_vcc->stats->tx_err);
+ return -ENOMEM; // ?
+ }
+
+ // allocate memory for fragments
+ tx_descr = kmalloc (sizeof(tx_simple), GFP_KERNEL);
+ if (!tx_descr) {
+ PRINTK (KERN_ERR, "could not allocate TX descriptor");
+ return -ENOMEM;
+ }
+ if (check_area (tx_descr, sizeof(tx_simple))) {
+ kfree (tx_descr);
+ return -ENOMEM;
+ }
+ PRINTD (DBG_TX, "fragment list allocated at %p", tx_descr);
+
+ tx_descr->skb = skb;
+
+ tx_descr->tx_frag.bytes = cpu_to_be32 (tx_len);
+ tx_descr->tx_frag.address = cpu_to_be32 (virt_to_bus (tx_data));
+
+ tx_descr->tx_frag_end.handle = virt_to_bus (tx_descr);
+ tx_descr->tx_frag_end.vc = 0;
+ tx_descr->tx_frag_end.next_descriptor_length = 0;
+ tx_descr->tx_frag_end.next_descriptor = 0;
+#ifdef AMB_NEW_MICROCODE
+ tx_descr->tx_frag_end.cpcs_uu = 0;
+ tx_descr->tx_frag_end.cpi = 0;
+ tx_descr->tx_frag_end.pad = 0;
+#endif
+
+ tx.vc = cpu_to_be16 (vcc->tx_frame_bits | vc);
+ tx.tx_descr_length = cpu_to_be16 (sizeof(tx_frag)+sizeof(tx_frag_end));
+ tx.tx_descr_addr = cpu_to_be32 (virt_to_bus (&tx_descr->tx_frag));
+
+ while (tx_give (dev, &tx))
+ schedule();
+ return 0;
+}
+
+/********** Change QoS on a VC **********/
+
+// int amb_change_qos (struct atm_vcc * atm_vcc, struct atm_qos * qos, int flags);
+
+/********** Free RX Socket Buffer **********/
+
+#if 0
+static void amb_free_rx_skb (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
+ amb_dev * dev = AMB_DEV (atm_vcc->dev);
+ amb_vcc * vcc = AMB_VCC (atm_vcc);
+ unsigned char pool = vcc->rx_info.pool;
+ rx_in rx;
+
+ // This may be unsafe for various reasons that I cannot really guess
+ // at. However, I note that the ATM layer calls kfree_skb rather
+ // than dev_kfree_skb at this point so we are least covered as far
+ // as buffer locking goes. There may be bugs if pcap clones RX skbs.
+
+ PRINTD (DBG_FLOW|DBG_SKB, "amb_rx_free skb %p (atm_vcc %p, vcc %p)",
+ skb, atm_vcc, vcc);
+
+ rx.handle = virt_to_bus (skb);
+ rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
+
+ skb->data = skb->head;
+ skb_reset_tail_pointer(skb);
+ skb->len = 0;
+
+ if (!rx_give (dev, &rx, pool)) {
+ // success
+ PRINTD (DBG_SKB|DBG_POOL, "recycled skb for pool %hu", pool);
+ return;
+ }
+
+ // just do what the ATM layer would have done
+ dev_kfree_skb_any (skb);
+
+ return;
+}
+#endif
+
+/********** Proc File Output **********/
+
+static int amb_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) {
+ amb_dev * dev = AMB_DEV (atm_dev);
+ int left = *pos;
+ unsigned char pool;
+
+ PRINTD (DBG_FLOW, "amb_proc_read");
+
+ /* more diagnostics here? */
+
+ if (!left--) {
+ amb_stats * s = &dev->stats;
+ return sprintf (page,
+ "frames: TX OK %lu, RX OK %lu, RX bad %lu "
+ "(CRC %lu, long %lu, aborted %lu, unused %lu).\n",
+ s->tx_ok, s->rx.ok, s->rx.error,
+ s->rx.badcrc, s->rx.toolong,
+ s->rx.aborted, s->rx.unused);
+ }
+
+ if (!left--) {
+ amb_cq * c = &dev->cq;
+ return sprintf (page, "cmd queue [cur/hi/max]: %u/%u/%u. ",
+ c->pending, c->high, c->maximum);
+ }
+
+ if (!left--) {
+ amb_txq * t = &dev->txq;
+ return sprintf (page, "TX queue [cur/max high full]: %u/%u %u %u.\n",
+ t->pending, t->maximum, t->high, t->filled);
+ }
+
+ if (!left--) {
+ unsigned int count = sprintf (page, "RX queues [cur/max/req low empty]:");
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
+ amb_rxq * r = &dev->rxq[pool];
+ count += sprintf (page+count, " %u/%u/%u %u %u",
+ r->pending, r->maximum, r->buffers_wanted, r->low, r->emptied);
+ }
+ count += sprintf (page+count, ".\n");
+ return count;
+ }
+
+ if (!left--) {
+ unsigned int count = sprintf (page, "RX buffer sizes:");
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
+ amb_rxq * r = &dev->rxq[pool];
+ count += sprintf (page+count, " %u", r->buffer_size);
+ }
+ count += sprintf (page+count, ".\n");
+ return count;
+ }
+
+#if 0
+ if (!left--) {
+ // suni block etc?
+ }
+#endif
+
+ return 0;
+}
+
+/********** Operation Structure **********/
+
+static const struct atmdev_ops amb_ops = {
+ .open = amb_open,
+ .close = amb_close,
+ .send = amb_send,
+ .proc_read = amb_proc_read,
+ .owner = THIS_MODULE,
+};
+
+/********** housekeeping **********/
+static void do_housekeeping (unsigned long arg) {
+ amb_dev * dev = (amb_dev *) arg;
+
+ // could collect device-specific (not driver/atm-linux) stats here
+
+ // last resort refill once every ten seconds
+ fill_rx_pools (dev);
+ mod_timer(&dev->housekeeping, jiffies + 10*HZ);
+
+ return;
+}
+
+/********** creation of communication queues **********/
+
+static int create_queues(amb_dev *dev, unsigned int cmds, unsigned int txs,
+ unsigned int *rxs, unsigned int *rx_buffer_sizes)
+{
+ unsigned char pool;
+ size_t total = 0;
+ void * memory;
+ void * limit;
+
+ PRINTD (DBG_FLOW, "create_queues %p", dev);
+
+ total += cmds * sizeof(command);
+
+ total += txs * (sizeof(tx_in) + sizeof(tx_out));
+
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool)
+ total += rxs[pool] * (sizeof(rx_in) + sizeof(rx_out));
+
+ memory = kmalloc (total, GFP_KERNEL);
+ if (!memory) {
+ PRINTK (KERN_ERR, "could not allocate queues");
+ return -ENOMEM;
+ }
+ if (check_area (memory, total)) {
+ PRINTK (KERN_ERR, "queues allocated in nasty area");
+ kfree (memory);
+ return -ENOMEM;
+ }
+
+ limit = memory + total;
+ PRINTD (DBG_INIT, "queues from %p to %p", memory, limit);
+
+ PRINTD (DBG_CMD, "command queue at %p", memory);
+
+ {
+ command * cmd = memory;
+ amb_cq * cq = &dev->cq;
+
+ cq->pending = 0;
+ cq->high = 0;
+ cq->maximum = cmds - 1;
+
+ cq->ptrs.start = cmd;
+ cq->ptrs.in = cmd;
+ cq->ptrs.out = cmd;
+ cq->ptrs.limit = cmd + cmds;
+
+ memory = cq->ptrs.limit;
+ }
+
+ PRINTD (DBG_TX, "TX queue pair at %p", memory);
+
+ {
+ tx_in * in = memory;
+ tx_out * out;
+ amb_txq * txq = &dev->txq;
+
+ txq->pending = 0;
+ txq->high = 0;
+ txq->filled = 0;
+ txq->maximum = txs - 1;
+
+ txq->in.start = in;
+ txq->in.ptr = in;
+ txq->in.limit = in + txs;
+
+ memory = txq->in.limit;
+ out = memory;
+
+ txq->out.start = out;
+ txq->out.ptr = out;
+ txq->out.limit = out + txs;
+
+ memory = txq->out.limit;
+ }
+
+ PRINTD (DBG_RX, "RX queue pairs at %p", memory);
+
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
+ rx_in * in = memory;
+ rx_out * out;
+ amb_rxq * rxq = &dev->rxq[pool];
+
+ rxq->buffer_size = rx_buffer_sizes[pool];
+ rxq->buffers_wanted = 0;
+
+ rxq->pending = 0;
+ rxq->low = rxs[pool] - 1;
+ rxq->emptied = 0;
+ rxq->maximum = rxs[pool] - 1;
+
+ rxq->in.start = in;
+ rxq->in.ptr = in;
+ rxq->in.limit = in + rxs[pool];
+
+ memory = rxq->in.limit;
+ out = memory;
+
+ rxq->out.start = out;
+ rxq->out.ptr = out;
+ rxq->out.limit = out + rxs[pool];
+
+ memory = rxq->out.limit;
+ }
+
+ if (memory == limit) {
+ return 0;
+ } else {
+ PRINTK (KERN_ERR, "bad queue alloc %p != %p (tell maintainer)", memory, limit);
+ kfree (limit - total);
+ return -ENOMEM;
+ }
+
+}
+
+/********** destruction of communication queues **********/
+
+static void destroy_queues (amb_dev * dev) {
+ // all queues assumed empty
+ void * memory = dev->cq.ptrs.start;
+ // includes txq.in, txq.out, rxq[].in and rxq[].out
+
+ PRINTD (DBG_FLOW, "destroy_queues %p", dev);
+
+ PRINTD (DBG_INIT, "freeing queues at %p", memory);
+ kfree (memory);
+
+ return;
+}
+
+/********** basic loader commands and error handling **********/
+// centisecond timeouts - guessing away here
+static unsigned int command_timeouts [] = {
+ [host_memory_test] = 15,
+ [read_adapter_memory] = 2,
+ [write_adapter_memory] = 2,
+ [adapter_start] = 50,
+ [get_version_number] = 10,
+ [interrupt_host] = 1,
+ [flash_erase_sector] = 1,
+ [adap_download_block] = 1,
+ [adap_erase_flash] = 1,
+ [adap_run_in_iram] = 1,
+ [adap_end_download] = 1
+};
+
+
+static unsigned int command_successes [] = {
+ [host_memory_test] = COMMAND_PASSED_TEST,
+ [read_adapter_memory] = COMMAND_READ_DATA_OK,
+ [write_adapter_memory] = COMMAND_WRITE_DATA_OK,
+ [adapter_start] = COMMAND_COMPLETE,
+ [get_version_number] = COMMAND_COMPLETE,
+ [interrupt_host] = COMMAND_COMPLETE,
+ [flash_erase_sector] = COMMAND_COMPLETE,
+ [adap_download_block] = COMMAND_COMPLETE,
+ [adap_erase_flash] = COMMAND_COMPLETE,
+ [adap_run_in_iram] = COMMAND_COMPLETE,
+ [adap_end_download] = COMMAND_COMPLETE
+};
+
+static int decode_loader_result (loader_command cmd, u32 result)
+{
+ int res;
+ const char *msg;
+
+ if (result == command_successes[cmd])
+ return 0;
+
+ switch (result) {
+ case BAD_COMMAND:
+ res = -EINVAL;
+ msg = "bad command";
+ break;
+ case COMMAND_IN_PROGRESS:
+ res = -ETIMEDOUT;
+ msg = "command in progress";
+ break;
+ case COMMAND_PASSED_TEST:
+ res = 0;
+ msg = "command passed test";
+ break;
+ case COMMAND_FAILED_TEST:
+ res = -EIO;
+ msg = "command failed test";
+ break;
+ case COMMAND_READ_DATA_OK:
+ res = 0;
+ msg = "command read data ok";
+ break;
+ case COMMAND_READ_BAD_ADDRESS:
+ res = -EINVAL;
+ msg = "command read bad address";
+ break;
+ case COMMAND_WRITE_DATA_OK:
+ res = 0;
+ msg = "command write data ok";
+ break;
+ case COMMAND_WRITE_BAD_ADDRESS:
+ res = -EINVAL;
+ msg = "command write bad address";
+ break;
+ case COMMAND_WRITE_FLASH_FAILURE:
+ res = -EIO;
+ msg = "command write flash failure";
+ break;
+ case COMMAND_COMPLETE:
+ res = 0;
+ msg = "command complete";
+ break;
+ case COMMAND_FLASH_ERASE_FAILURE:
+ res = -EIO;
+ msg = "command flash erase failure";
+ break;
+ case COMMAND_WRITE_BAD_DATA:
+ res = -EINVAL;
+ msg = "command write bad data";
+ break;
+ default:
+ res = -EINVAL;
+ msg = "unknown error";
+ PRINTD (DBG_LOAD|DBG_ERR,
+ "decode_loader_result got %d=%x !",
+ result, result);
+ break;
+ }
+
+ PRINTK (KERN_ERR, "%s", msg);
+ return res;
+}
+
+static int do_loader_command(volatile loader_block *lb, const amb_dev *dev,
+ loader_command cmd)
+{
+
+ unsigned long timeout;
+
+ PRINTD (DBG_FLOW|DBG_LOAD, "do_loader_command");
+
+ /* do a command
+
+ Set the return value to zero, set the command type and set the
+ valid entry to the right magic value. The payload is already
+ correctly byte-ordered so we leave it alone. Hit the doorbell
+ with the bus address of this structure.
+
+ */
+
+ lb->result = 0;
+ lb->command = cpu_to_be32 (cmd);
+ lb->valid = cpu_to_be32 (DMA_VALID);
+ // dump_registers (dev);
+ // dump_loader_block (lb);
+ wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (lb) & ~onegigmask);
+
+ timeout = command_timeouts[cmd] * 10;
+
+ while (!lb->result || lb->result == cpu_to_be32 (COMMAND_IN_PROGRESS))
+ if (timeout) {
+ timeout = msleep_interruptible(timeout);
+ } else {
+ PRINTD (DBG_LOAD|DBG_ERR, "command %d timed out", cmd);
+ dump_registers (dev);
+ dump_loader_block (lb);
+ return -ETIMEDOUT;
+ }
+
+ if (cmd == adapter_start) {
+ // wait for start command to acknowledge...
+ timeout = 100;
+ while (rd_plain (dev, offsetof(amb_mem, doorbell)))
+ if (timeout) {
+ timeout = msleep_interruptible(timeout);
+ } else {
+ PRINTD (DBG_LOAD|DBG_ERR, "start command did not clear doorbell, res=%08x",
+ be32_to_cpu (lb->result));
+ dump_registers (dev);
+ return -ETIMEDOUT;
+ }
+ return 0;
+ } else {
+ return decode_loader_result (cmd, be32_to_cpu (lb->result));
+ }
+
+}
+
+/* loader: determine loader version */
+
+static int get_loader_version(loader_block *lb, const amb_dev *dev,
+ u32 *version)
+{
+ int res;
+
+ PRINTD (DBG_FLOW|DBG_LOAD, "get_loader_version");
+
+ res = do_loader_command (lb, dev, get_version_number);
+ if (res)
+ return res;
+ if (version)
+ *version = be32_to_cpu (lb->payload.version);
+ return 0;
+}
+
+/* loader: write memory data blocks */
+
+static int loader_write(loader_block *lb, const amb_dev *dev,
+ const struct ihex_binrec *rec)
+{
+ transfer_block * tb = &lb->payload.transfer;
+
+ PRINTD (DBG_FLOW|DBG_LOAD, "loader_write");
+
+ tb->address = rec->addr;
+ tb->count = cpu_to_be32(be16_to_cpu(rec->len) / 4);
+ memcpy(tb->data, rec->data, be16_to_cpu(rec->len));
+ return do_loader_command (lb, dev, write_adapter_memory);
+}
+
+/* loader: verify memory data blocks */
+
+static int loader_verify(loader_block *lb, const amb_dev *dev,
+ const struct ihex_binrec *rec)
+{
+ transfer_block * tb = &lb->payload.transfer;
+ int res;
+
+ PRINTD (DBG_FLOW|DBG_LOAD, "loader_verify");
+
+ tb->address = rec->addr;
+ tb->count = cpu_to_be32(be16_to_cpu(rec->len) / 4);
+ res = do_loader_command (lb, dev, read_adapter_memory);
+ if (!res && memcmp(tb->data, rec->data, be16_to_cpu(rec->len)))
+ res = -EINVAL;
+ return res;
+}
+
+/* loader: start microcode */
+
+static int loader_start(loader_block *lb, const amb_dev *dev, u32 address)
+{
+ PRINTD (DBG_FLOW|DBG_LOAD, "loader_start");
+
+ lb->payload.start = cpu_to_be32 (address);
+ return do_loader_command (lb, dev, adapter_start);
+}
+
+/********** reset card **********/
+
+static inline void sf (const char * msg)
+{
+ PRINTK (KERN_ERR, "self-test failed: %s", msg);
+}
+
+static int amb_reset (amb_dev * dev, int diags) {
+ u32 word;
+
+ PRINTD (DBG_FLOW|DBG_LOAD, "amb_reset");
+
+ word = rd_plain (dev, offsetof(amb_mem, reset_control));
+ // put card into reset state
+ wr_plain (dev, offsetof(amb_mem, reset_control), word | AMB_RESET_BITS);
+ // wait a short while
+ udelay (10);
+#if 1
+ // put card into known good state
+ wr_plain (dev, offsetof(amb_mem, interrupt_control), AMB_DOORBELL_BITS);
+ // clear all interrupts just in case
+ wr_plain (dev, offsetof(amb_mem, interrupt), -1);
+#endif
+ // clear self-test done flag
+ wr_plain (dev, offsetof(amb_mem, mb.loader.ready), 0);
+ // take card out of reset state
+ wr_plain (dev, offsetof(amb_mem, reset_control), word &~ AMB_RESET_BITS);
+
+ if (diags) {
+ unsigned long timeout;
+ // 4.2 second wait
+ msleep(4200);
+ // half second time-out
+ timeout = 500;
+ while (!rd_plain (dev, offsetof(amb_mem, mb.loader.ready)))
+ if (timeout) {
+ timeout = msleep_interruptible(timeout);
+ } else {
+ PRINTD (DBG_LOAD|DBG_ERR, "reset timed out");
+ return -ETIMEDOUT;
+ }
+
+ // get results of self-test
+ // XXX double check byte-order
+ word = rd_mem (dev, offsetof(amb_mem, mb.loader.result));
+ if (word & SELF_TEST_FAILURE) {
+ if (word & GPINT_TST_FAILURE)
+ sf ("interrupt");
+ if (word & SUNI_DATA_PATTERN_FAILURE)
+ sf ("SUNI data pattern");
+ if (word & SUNI_DATA_BITS_FAILURE)
+ sf ("SUNI data bits");
+ if (word & SUNI_UTOPIA_FAILURE)
+ sf ("SUNI UTOPIA interface");
+ if (word & SUNI_FIFO_FAILURE)
+ sf ("SUNI cell buffer FIFO");
+ if (word & SRAM_FAILURE)
+ sf ("bad SRAM");
+ // better return value?
+ return -EIO;
+ }
+
+ }
+ return 0;
+}
+
+/********** transfer and start the microcode **********/
+
+static int ucode_init(loader_block *lb, amb_dev *dev)
+{
+ const struct firmware *fw;
+ unsigned long start_address;
+ const struct ihex_binrec *rec;
+ const char *errmsg = NULL;
+ int res;
+
+ res = reject_firmware(&fw, "/*(DEBLOBBED)*/", &dev->pci_dev->dev);
+ if (res) {
+ PRINTK (KERN_ERR, "Cannot load microcode data");
+ return res;
+ }
+
+ /* First record contains just the start address */
+ rec = (const struct ihex_binrec *)fw->data;
+ if (be16_to_cpu(rec->len) != sizeof(__be32) || be32_to_cpu(rec->addr)) {
+ errmsg = "no start record";
+ goto fail;
+ }
+ start_address = be32_to_cpup((__be32 *)rec->data);
+
+ rec = ihex_next_binrec(rec);
+
+ PRINTD (DBG_FLOW|DBG_LOAD, "ucode_init");
+
+ while (rec) {
+ PRINTD (DBG_LOAD, "starting region (%x, %u)", be32_to_cpu(rec->addr),
+ be16_to_cpu(rec->len));
+ if (be16_to_cpu(rec->len) > 4 * MAX_TRANSFER_DATA) {
+ errmsg = "record too long";
+ goto fail;
+ }
+ if (be16_to_cpu(rec->len) & 3) {
+ errmsg = "odd number of bytes";
+ goto fail;
+ }
+ res = loader_write(lb, dev, rec);
+ if (res)
+ break;
+
+ res = loader_verify(lb, dev, rec);
+ if (res)
+ break;
+ rec = ihex_next_binrec(rec);
+ }
+ release_firmware(fw);
+ if (!res)
+ res = loader_start(lb, dev, start_address);
+
+ return res;
+fail:
+ release_firmware(fw);
+ PRINTK(KERN_ERR, "Bad microcode data (%s)", errmsg);
+ return -EINVAL;
+}
+
+/********** give adapter parameters **********/
+
+static inline __be32 bus_addr(void * addr) {
+ return cpu_to_be32 (virt_to_bus (addr));
+}
+
+static int amb_talk(amb_dev *dev)
+{
+ adap_talk_block a;
+ unsigned char pool;
+ unsigned long timeout;
+
+ PRINTD (DBG_FLOW, "amb_talk %p", dev);
+
+ a.command_start = bus_addr (dev->cq.ptrs.start);
+ a.command_end = bus_addr (dev->cq.ptrs.limit);
+ a.tx_start = bus_addr (dev->txq.in.start);
+ a.tx_end = bus_addr (dev->txq.in.limit);
+ a.txcom_start = bus_addr (dev->txq.out.start);
+ a.txcom_end = bus_addr (dev->txq.out.limit);
+
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
+ // the other "a" items are set up by the adapter
+ a.rec_struct[pool].buffer_start = bus_addr (dev->rxq[pool].in.start);
+ a.rec_struct[pool].buffer_end = bus_addr (dev->rxq[pool].in.limit);
+ a.rec_struct[pool].rx_start = bus_addr (dev->rxq[pool].out.start);
+ a.rec_struct[pool].rx_end = bus_addr (dev->rxq[pool].out.limit);
+ a.rec_struct[pool].buffer_size = cpu_to_be32 (dev->rxq[pool].buffer_size);
+ }
+
+#ifdef AMB_NEW_MICROCODE
+ // disable fast PLX prefetching
+ a.init_flags = 0;
+#endif
+
+ // pass the structure
+ wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (&a));
+
+ // 2.2 second wait (must not touch doorbell during 2 second DMA test)
+ msleep(2200);
+ // give the adapter another half second?
+ timeout = 500;
+ while (rd_plain (dev, offsetof(amb_mem, doorbell)))
+ if (timeout) {
+ timeout = msleep_interruptible(timeout);
+ } else {
+ PRINTD (DBG_INIT|DBG_ERR, "adapter init timed out");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+// get microcode version
+static void amb_ucode_version(amb_dev *dev)
+{
+ u32 major;
+ u32 minor;
+ command cmd;
+ cmd.request = cpu_to_be32 (SRB_GET_VERSION);
+ while (command_do (dev, &cmd)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ }
+ major = be32_to_cpu (cmd.args.version.major);
+ minor = be32_to_cpu (cmd.args.version.minor);
+ PRINTK (KERN_INFO, "microcode version is %u.%u", major, minor);
+}
+
+// get end station address
+static void amb_esi(amb_dev *dev, u8 *esi)
+{
+ u32 lower4;
+ u16 upper2;
+ command cmd;
+
+ cmd.request = cpu_to_be32 (SRB_GET_BIA);
+ while (command_do (dev, &cmd)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ }
+ lower4 = be32_to_cpu (cmd.args.bia.lower4);
+ upper2 = be32_to_cpu (cmd.args.bia.upper2);
+ PRINTD (DBG_LOAD, "BIA: lower4: %08x, upper2 %04x", lower4, upper2);
+
+ if (esi) {
+ unsigned int i;
+
+ PRINTDB (DBG_INIT, "ESI:");
+ for (i = 0; i < ESI_LEN; ++i) {
+ if (i < 4)
+ esi[i] = bitrev8(lower4>>(8*i));
+ else
+ esi[i] = bitrev8(upper2>>(8*(i-4)));
+ PRINTDM (DBG_INIT, " %02x", esi[i]);
+ }
+
+ PRINTDE (DBG_INIT, "");
+ }
+
+ return;
+}
+
+static void fixup_plx_window (amb_dev *dev, loader_block *lb)
+{
+ // fix up the PLX-mapped window base address to match the block
+ unsigned long blb;
+ u32 mapreg;
+ blb = virt_to_bus(lb);
+ // the kernel stack had better not ever cross a 1Gb boundary!
+ mapreg = rd_plain (dev, offsetof(amb_mem, stuff[10]));
+ mapreg &= ~onegigmask;
+ mapreg |= blb & onegigmask;
+ wr_plain (dev, offsetof(amb_mem, stuff[10]), mapreg);
+ return;
+}
+
+static int amb_init(amb_dev *dev)
+{
+ loader_block lb;
+
+ u32 version;
+
+ if (amb_reset (dev, 1)) {
+ PRINTK (KERN_ERR, "card reset failed!");
+ } else {
+ fixup_plx_window (dev, &lb);
+
+ if (get_loader_version (&lb, dev, &version)) {
+ PRINTK (KERN_INFO, "failed to get loader version");
+ } else {
+ PRINTK (KERN_INFO, "loader version is %08x", version);
+
+ if (ucode_init (&lb, dev)) {
+ PRINTK (KERN_ERR, "microcode failure");
+ } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) {
+ PRINTK (KERN_ERR, "failed to get memory for queues");
+ } else {
+
+ if (amb_talk (dev)) {
+ PRINTK (KERN_ERR, "adapter did not accept queues");
+ } else {
+
+ amb_ucode_version (dev);
+ return 0;
+
+ } /* amb_talk */
+
+ destroy_queues (dev);
+ } /* create_queues, ucode_init */
+
+ amb_reset (dev, 0);
+ } /* get_loader_version */
+
+ } /* amb_reset */
+
+ return -EINVAL;
+}
+
+static void setup_dev(amb_dev *dev, struct pci_dev *pci_dev)
+{
+ unsigned char pool;
+
+ // set up known dev items straight away
+ dev->pci_dev = pci_dev;
+ pci_set_drvdata(pci_dev, dev);
+
+ dev->iobase = pci_resource_start (pci_dev, 1);
+ dev->irq = pci_dev->irq;
+ dev->membase = bus_to_virt(pci_resource_start(pci_dev, 0));
+
+ // flags (currently only dead)
+ dev->flags = 0;
+
+ // Allocate cell rates (fibre)
+ // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
+ // to be really pedantic, this should be ATM_OC3c_PCR
+ dev->tx_avail = ATM_OC3_PCR;
+ dev->rx_avail = ATM_OC3_PCR;
+
+ // semaphore for txer/rxer modifications - we cannot use a
+ // spinlock as the critical region needs to switch processes
+ mutex_init(&dev->vcc_sf);
+ // queue manipulation spinlocks; we want atomic reads and
+ // writes to the queue descriptors (handles IRQ and SMP)
+ // consider replacing "int pending" -> "atomic_t available"
+ // => problem related to who gets to move queue pointers
+ spin_lock_init (&dev->cq.lock);
+ spin_lock_init (&dev->txq.lock);
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool)
+ spin_lock_init (&dev->rxq[pool].lock);
+}
+
+static void setup_pci_dev(struct pci_dev *pci_dev)
+{
+ unsigned char lat;
+
+ // enable bus master accesses
+ pci_set_master(pci_dev);
+
+ // frobnicate latency (upwards, usually)
+ pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat);
+
+ if (!pci_lat)
+ pci_lat = (lat < MIN_PCI_LATENCY) ? MIN_PCI_LATENCY : lat;
+
+ if (lat != pci_lat) {
+ PRINTK (KERN_INFO, "Changing PCI latency timer from %hu to %hu",
+ lat, pci_lat);
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat);
+ }
+}
+
+static int amb_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_ent)
+{
+ amb_dev * dev;
+ int err;
+ unsigned int irq;
+
+ err = pci_enable_device(pci_dev);
+ if (err < 0) {
+ PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card");
+ goto out;
+ }
+
+ // read resources from PCI configuration space
+ irq = pci_dev->irq;
+
+ if (pci_dev->device == PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD) {
+ PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card");
+ err = -EINVAL;
+ goto out_disable;
+ }
+
+ PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at"
+ " IO %llx, IRQ %u, MEM %p",
+ (unsigned long long)pci_resource_start(pci_dev, 1),
+ irq, bus_to_virt(pci_resource_start(pci_dev, 0)));
+
+ // check IO region
+ err = pci_request_region(pci_dev, 1, DEV_LABEL);
+ if (err < 0) {
+ PRINTK (KERN_ERR, "IO range already in use!");
+ goto out_disable;
+ }
+
+ dev = kzalloc(sizeof(amb_dev), GFP_KERNEL);
+ if (!dev) {
+ PRINTK (KERN_ERR, "out of memory!");
+ err = -ENOMEM;
+ goto out_release;
+ }
+
+ setup_dev(dev, pci_dev);
+
+ err = amb_init(dev);
+ if (err < 0) {
+ PRINTK (KERN_ERR, "adapter initialisation failure");
+ goto out_free;
+ }
+
+ setup_pci_dev(pci_dev);
+
+ // grab (but share) IRQ and install handler
+ err = request_irq(irq, interrupt_handler, IRQF_SHARED, DEV_LABEL, dev);
+ if (err < 0) {
+ PRINTK (KERN_ERR, "request IRQ failed!");
+ goto out_reset;
+ }
+
+ dev->atm_dev = atm_dev_register (DEV_LABEL, &pci_dev->dev, &amb_ops, -1,
+ NULL);
+ if (!dev->atm_dev) {
+ PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
+ err = -EINVAL;
+ goto out_free_irq;
+ }
+
+ PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
+ dev->atm_dev->number, dev, dev->atm_dev);
+ dev->atm_dev->dev_data = (void *) dev;
+
+ // register our address
+ amb_esi (dev, dev->atm_dev->esi);
+
+ // 0 bits for vpi, 10 bits for vci
+ dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS;
+ dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS;
+
+ init_timer(&dev->housekeeping);
+ dev->housekeeping.function = do_housekeeping;
+ dev->housekeeping.data = (unsigned long) dev;
+ mod_timer(&dev->housekeeping, jiffies);
+
+ // enable host interrupts
+ interrupts_on (dev);
+
+out:
+ return err;
+
+out_free_irq:
+ free_irq(irq, dev);
+out_reset:
+ amb_reset(dev, 0);
+out_free:
+ kfree(dev);
+out_release:
+ pci_release_region(pci_dev, 1);
+out_disable:
+ pci_disable_device(pci_dev);
+ goto out;
+}
+
+
+static void amb_remove_one(struct pci_dev *pci_dev)
+{
+ struct amb_dev *dev;
+
+ dev = pci_get_drvdata(pci_dev);
+
+ PRINTD(DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
+ del_timer_sync(&dev->housekeeping);
+ // the drain should not be necessary
+ drain_rx_pools(dev);
+ interrupts_off(dev);
+ amb_reset(dev, 0);
+ free_irq(dev->irq, dev);
+ pci_disable_device(pci_dev);
+ destroy_queues(dev);
+ atm_dev_deregister(dev->atm_dev);
+ kfree(dev);
+ pci_release_region(pci_dev, 1);
+}
+
+static void __init amb_check_args (void) {
+ unsigned char pool;
+ unsigned int max_rx_size;
+
+#ifdef DEBUG_AMBASSADOR
+ PRINTK (KERN_NOTICE, "debug bitmap is %hx", debug &= DBG_MASK);
+#else
+ if (debug)
+ PRINTK (KERN_NOTICE, "no debugging support");
+#endif
+
+ if (cmds < MIN_QUEUE_SIZE)
+ PRINTK (KERN_NOTICE, "cmds has been raised to %u",
+ cmds = MIN_QUEUE_SIZE);
+
+ if (txs < MIN_QUEUE_SIZE)
+ PRINTK (KERN_NOTICE, "txs has been raised to %u",
+ txs = MIN_QUEUE_SIZE);
+
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool)
+ if (rxs[pool] < MIN_QUEUE_SIZE)
+ PRINTK (KERN_NOTICE, "rxs[%hu] has been raised to %u",
+ pool, rxs[pool] = MIN_QUEUE_SIZE);
+
+ // buffers sizes should be greater than zero and strictly increasing
+ max_rx_size = 0;
+ for (pool = 0; pool < NUM_RX_POOLS; ++pool)
+ if (rxs_bs[pool] <= max_rx_size)
+ PRINTK (KERN_NOTICE, "useless pool (rxs_bs[%hu] = %u)",
+ pool, rxs_bs[pool]);
+ else
+ max_rx_size = rxs_bs[pool];
+
+ if (rx_lats < MIN_RX_BUFFERS)
+ PRINTK (KERN_NOTICE, "rx_lats has been raised to %u",
+ rx_lats = MIN_RX_BUFFERS);
+
+ return;
+}
+
+/********** module stuff **********/
+
+MODULE_AUTHOR(maintainer_string);
+MODULE_DESCRIPTION(description_string);
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
+module_param(debug, ushort, 0644);
+module_param(cmds, uint, 0);
+module_param(txs, uint, 0);
+module_param_array(rxs, uint, NULL, 0);
+module_param_array(rxs_bs, uint, NULL, 0);
+module_param(rx_lats, uint, 0);
+module_param(pci_lat, byte, 0);
+MODULE_PARM_DESC(debug, "debug bitmap, see .h file");
+MODULE_PARM_DESC(cmds, "number of command queue entries");
+MODULE_PARM_DESC(txs, "number of TX queue entries");
+MODULE_PARM_DESC(rxs, "number of RX queue entries [" __MODULE_STRING(NUM_RX_POOLS) "]");
+MODULE_PARM_DESC(rxs_bs, "size of RX buffers [" __MODULE_STRING(NUM_RX_POOLS) "]");
+MODULE_PARM_DESC(rx_lats, "number of extra buffers to cope with RX latencies");
+MODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles");
+
+/********** module entry **********/
+
+static struct pci_device_id amb_pci_tbl[] = {
+ { PCI_VDEVICE(MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR), 0 },
+ { PCI_VDEVICE(MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD), 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, amb_pci_tbl);
+
+static struct pci_driver amb_driver = {
+ .name = "amb",
+ .probe = amb_probe,
+ .remove = amb_remove_one,
+ .id_table = amb_pci_tbl,
+};
+
+static int __init amb_module_init (void)
+{
+ PRINTD (DBG_FLOW|DBG_INIT, "init_module");
+
+ // sanity check - cast needed as printk does not support %Zu
+ if (sizeof(amb_mem) != 4*16 + 4*12) {
+ PRINTK (KERN_ERR, "Fix amb_mem (is %lu words).",
+ (unsigned long) sizeof(amb_mem));
+ return -ENOMEM;
+ }
+
+ show_version();
+
+ amb_check_args();
+
+ // get the juice
+ return pci_register_driver(&amb_driver);
+}
+
+/********** module exit **********/
+
+static void __exit amb_module_exit (void)
+{
+ PRINTD (DBG_FLOW|DBG_INIT, "cleanup_module");
+
+ pci_unregister_driver(&amb_driver);
+}
+
+module_init(amb_module_init);
+module_exit(amb_module_exit);
diff --git a/drivers/atm/ambassador.h b/drivers/atm/ambassador.h
new file mode 100644
index 000000000..aa9710556
--- /dev/null
+++ b/drivers/atm/ambassador.h
@@ -0,0 +1,663 @@
+/*
+ Madge Ambassador ATM Adapter driver.
+ Copyright (C) 1995-1999 Madge Networks Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian
+ system and in the file COPYING in the Linux kernel source.
+*/
+
+#ifndef AMBASSADOR_H
+#define AMBASSADOR_H
+
+
+#ifdef CONFIG_ATM_AMBASSADOR_DEBUG
+#define DEBUG_AMBASSADOR
+#endif
+
+#define DEV_LABEL "amb"
+
+#ifndef PCI_VENDOR_ID_MADGE
+#define PCI_VENDOR_ID_MADGE 0x10B6
+#endif
+#ifndef PCI_VENDOR_ID_MADGE_AMBASSADOR
+#define PCI_DEVICE_ID_MADGE_AMBASSADOR 0x1001
+#endif
+#ifndef PCI_VENDOR_ID_MADGE_AMBASSADOR_BAD
+#define PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD 0x1002
+#endif
+
+// diagnostic output
+
+#define PRINTK(severity,format,args...) \
+ printk(severity DEV_LABEL ": " format "\n" , ## args)
+
+#ifdef DEBUG_AMBASSADOR
+
+#define DBG_ERR 0x0001
+#define DBG_WARN 0x0002
+#define DBG_INFO 0x0004
+#define DBG_INIT 0x0008
+#define DBG_LOAD 0x0010
+#define DBG_VCC 0x0020
+#define DBG_QOS 0x0040
+#define DBG_CMD 0x0080
+#define DBG_TX 0x0100
+#define DBG_RX 0x0200
+#define DBG_SKB 0x0400
+#define DBG_POOL 0x0800
+#define DBG_IRQ 0x1000
+#define DBG_FLOW 0x2000
+#define DBG_REGS 0x4000
+#define DBG_DATA 0x8000
+#define DBG_MASK 0xffff
+
+/* the ## prevents the annoying double expansion of the macro arguments */
+/* KERN_INFO is used since KERN_DEBUG often does not make it to the console */
+#define PRINTDB(bits,format,args...) \
+ ( (debug & (bits)) ? printk (KERN_INFO DEV_LABEL ": " format , ## args) : 1 )
+#define PRINTDM(bits,format,args...) \
+ ( (debug & (bits)) ? printk (format , ## args) : 1 )
+#define PRINTDE(bits,format,args...) \
+ ( (debug & (bits)) ? printk (format "\n" , ## args) : 1 )
+#define PRINTD(bits,format,args...) \
+ ( (debug & (bits)) ? printk (KERN_INFO DEV_LABEL ": " format "\n" , ## args) : 1 )
+
+#else
+
+#define PRINTD(bits,format,args...)
+#define PRINTDB(bits,format,args...)
+#define PRINTDM(bits,format,args...)
+#define PRINTDE(bits,format,args...)
+
+#endif
+
+#define PRINTDD(bits,format,args...)
+#define PRINTDDB(sec,fmt,args...)
+#define PRINTDDM(sec,fmt,args...)
+#define PRINTDDE(sec,fmt,args...)
+
+// tunable values (?)
+
+/* MUST be powers of two -- why ? */
+#define COM_Q_ENTRIES 8
+#define TX_Q_ENTRIES 32
+#define RX_Q_ENTRIES 64
+
+// fixed values
+
+// guessing
+#define AMB_EXTENT 0x80
+
+// Minimum allowed size for an Ambassador queue
+#define MIN_QUEUE_SIZE 2
+
+// Ambassador microcode allows 1 to 4 pools, we use 4 (simpler)
+#define NUM_RX_POOLS 4
+
+// minimum RX buffers required to cope with replenishing delay
+#define MIN_RX_BUFFERS 1
+
+// minimum PCI latency we will tolerate (32 IS TOO SMALL)
+#define MIN_PCI_LATENCY 64 // 255
+
+// VCs supported by card (VPI always 0)
+#define NUM_VPI_BITS 0
+#define NUM_VCI_BITS 10
+#define NUM_VCS 1024
+
+/* The status field bits defined so far. */
+#define RX_ERR 0x8000 // always present if there is an error (hmm)
+#define CRC_ERR 0x4000 // AAL5 CRC error
+#define LEN_ERR 0x2000 // overlength frame
+#define ABORT_ERR 0x1000 // zero length field in received frame
+#define UNUSED_ERR 0x0800 // buffer returned unused
+
+// Adaptor commands
+
+#define SRB_OPEN_VC 0
+/* par_0: dwordswap(VC_number) */
+/* par_1: dwordswap(flags<<16) or wordswap(flags)*/
+/* flags: */
+
+/* LANE: 0x0004 */
+/* NOT_UBR: 0x0008 */
+/* ABR: 0x0010 */
+
+/* RxPool0: 0x0000 */
+/* RxPool1: 0x0020 */
+/* RxPool2: 0x0040 */
+/* RxPool3: 0x0060 */
+
+/* par_2: dwordswap(fp_rate<<16) or wordswap(fp_rate) */
+
+#define SRB_CLOSE_VC 1
+/* par_0: dwordswap(VC_number) */
+
+#define SRB_GET_BIA 2
+/* returns */
+/* par_0: dwordswap(half BIA) */
+/* par_1: dwordswap(half BIA) */
+
+#define SRB_GET_SUNI_STATS 3
+/* par_0: dwordswap(physical_host_address) */
+
+#define SRB_SET_BITS_8 4
+#define SRB_SET_BITS_16 5
+#define SRB_SET_BITS_32 6
+#define SRB_CLEAR_BITS_8 7
+#define SRB_CLEAR_BITS_16 8
+#define SRB_CLEAR_BITS_32 9
+/* par_0: dwordswap(ATMizer address) */
+/* par_1: dwordswap(mask) */
+
+#define SRB_SET_8 10
+#define SRB_SET_16 11
+#define SRB_SET_32 12
+/* par_0: dwordswap(ATMizer address) */
+/* par_1: dwordswap(data) */
+
+#define SRB_GET_32 13
+/* par_0: dwordswap(ATMizer address) */
+/* returns */
+/* par_1: dwordswap(ATMizer data) */
+
+#define SRB_GET_VERSION 14
+/* returns */
+/* par_0: dwordswap(Major Version) */
+/* par_1: dwordswap(Minor Version) */
+
+#define SRB_FLUSH_BUFFER_Q 15
+/* Only flags to define which buffer pool; all others must be zero */
+/* par_0: dwordswap(flags<<16) or wordswap(flags)*/
+
+#define SRB_GET_DMA_SPEEDS 16
+/* returns */
+/* par_0: dwordswap(Read speed (bytes/sec)) */
+/* par_1: dwordswap(Write speed (bytes/sec)) */
+
+#define SRB_MODIFY_VC_RATE 17
+/* par_0: dwordswap(VC_number) */
+/* par_1: dwordswap(fp_rate<<16) or wordswap(fp_rate) */
+
+#define SRB_MODIFY_VC_FLAGS 18
+/* par_0: dwordswap(VC_number) */
+/* par_1: dwordswap(flags<<16) or wordswap(flags)*/
+
+/* flags: */
+
+/* LANE: 0x0004 */
+/* NOT_UBR: 0x0008 */
+/* ABR: 0x0010 */
+
+/* RxPool0: 0x0000 */
+/* RxPool1: 0x0020 */
+/* RxPool2: 0x0040 */
+/* RxPool3: 0x0060 */
+
+#define SRB_RATE_SHIFT 16
+#define SRB_POOL_SHIFT (SRB_FLAGS_SHIFT+5)
+#define SRB_FLAGS_SHIFT 16
+
+#define SRB_STOP_TASKING 19
+#define SRB_START_TASKING 20
+#define SRB_SHUT_DOWN 21
+#define MAX_SRB 21
+
+#define SRB_COMPLETE 0xffffffff
+
+#define TX_FRAME 0x80000000
+
+// number of types of SRB MUST be a power of two -- why?
+#define NUM_OF_SRB 32
+
+// number of bits of period info for rate
+#define MAX_RATE_BITS 6
+
+#define TX_UBR 0x0000
+#define TX_UBR_CAPPED 0x0008
+#define TX_ABR 0x0018
+#define TX_FRAME_NOTCAP 0x0000
+#define TX_FRAME_CAPPED 0x8000
+
+#define FP_155_RATE 0x24b1
+#define FP_25_RATE 0x1f9d
+
+/* #define VERSION_NUMBER 0x01000000 // initial release */
+/* #define VERSION_NUMBER 0x01010000 // fixed startup probs PLX MB0 not cleared */
+/* #define VERSION_NUMBER 0x01020000 // changed SUNI reset timings; allowed r/w onchip */
+
+/* #define VERSION_NUMBER 0x01030000 // clear local doorbell int reg on reset */
+/* #define VERSION_NUMBER 0x01040000 // PLX bug work around version PLUS */
+/* remove race conditions on basic interface */
+/* indicate to the host that diagnostics */
+/* have finished; if failed, how and what */
+/* failed */
+/* fix host memory test to fix PLX bug */
+/* allow flash upgrade and BIA upgrade directly */
+/* */
+#define VERSION_NUMBER 0x01050025 /* Jason's first hacked version. */
+/* Change in download algorithm */
+
+#define DMA_VALID 0xb728e149 /* completely random */
+
+#define FLASH_BASE 0xa0c00000
+#define FLASH_SIZE 0x00020000 /* 128K */
+#define BIA_BASE (FLASH_BASE+0x0001c000) /* Flash Sector 7 */
+#define BIA_ADDRESS ((void *)0xa0c1c000)
+#define PLX_BASE 0xe0000000
+
+typedef enum {
+ host_memory_test = 1,
+ read_adapter_memory,
+ write_adapter_memory,
+ adapter_start,
+ get_version_number,
+ interrupt_host,
+ flash_erase_sector,
+ adap_download_block = 0x20,
+ adap_erase_flash,
+ adap_run_in_iram,
+ adap_end_download
+} loader_command;
+
+#define BAD_COMMAND (-1)
+#define COMMAND_IN_PROGRESS 1
+#define COMMAND_PASSED_TEST 2
+#define COMMAND_FAILED_TEST 3
+#define COMMAND_READ_DATA_OK 4
+#define COMMAND_READ_BAD_ADDRESS 5
+#define COMMAND_WRITE_DATA_OK 6
+#define COMMAND_WRITE_BAD_ADDRESS 7
+#define COMMAND_WRITE_FLASH_FAILURE 8
+#define COMMAND_COMPLETE 9
+#define COMMAND_FLASH_ERASE_FAILURE 10
+#define COMMAND_WRITE_BAD_DATA 11
+
+/* bit fields for mailbox[0] return values */
+
+#define GPINT_TST_FAILURE 0x00000001
+#define SUNI_DATA_PATTERN_FAILURE 0x00000002
+#define SUNI_DATA_BITS_FAILURE 0x00000004
+#define SUNI_UTOPIA_FAILURE 0x00000008
+#define SUNI_FIFO_FAILURE 0x00000010
+#define SRAM_FAILURE 0x00000020
+#define SELF_TEST_FAILURE 0x0000003f
+
+/* mailbox[1] = 0 in progress, -1 on completion */
+/* mailbox[2] = current test 00 00 test(8 bit) phase(8 bit) */
+/* mailbox[3] = last failure, 00 00 test(8 bit) phase(8 bit) */
+/* mailbox[4],mailbox[5],mailbox[6] random failure values */
+
+/* PLX/etc. memory map including command structure */
+
+/* These registers may also be memory mapped in PCI memory */
+
+#define UNUSED_LOADER_MAILBOXES 6
+
+typedef struct {
+ u32 stuff[16];
+ union {
+ struct {
+ u32 result;
+ u32 ready;
+ u32 stuff[UNUSED_LOADER_MAILBOXES];
+ } loader;
+ struct {
+ u32 cmd_address;
+ u32 tx_address;
+ u32 rx_address[NUM_RX_POOLS];
+ u32 gen_counter;
+ u32 spare;
+ } adapter;
+ } mb;
+ u32 doorbell;
+ u32 interrupt;
+ u32 interrupt_control;
+ u32 reset_control;
+} amb_mem;
+
+/* RESET bit, IRQ (card to host) and doorbell (host to card) enable bits */
+#define AMB_RESET_BITS 0x40000000
+#define AMB_INTERRUPT_BITS 0x00000300
+#define AMB_DOORBELL_BITS 0x00030000
+
+/* loader commands */
+
+#define MAX_COMMAND_DATA 13
+#define MAX_TRANSFER_DATA 11
+
+typedef struct {
+ __be32 address;
+ __be32 count;
+ __be32 data[MAX_TRANSFER_DATA];
+} transfer_block;
+
+typedef struct {
+ __be32 result;
+ __be32 command;
+ union {
+ transfer_block transfer;
+ __be32 version;
+ __be32 start;
+ __be32 data[MAX_COMMAND_DATA];
+ } payload;
+ __be32 valid;
+} loader_block;
+
+/* command queue */
+
+/* Again all data are BIG ENDIAN */
+
+typedef struct {
+ union {
+ struct {
+ __be32 vc;
+ __be32 flags;
+ __be32 rate;
+ } open;
+ struct {
+ __be32 vc;
+ __be32 rate;
+ } modify_rate;
+ struct {
+ __be32 vc;
+ __be32 flags;
+ } modify_flags;
+ struct {
+ __be32 vc;
+ } close;
+ struct {
+ __be32 lower4;
+ __be32 upper2;
+ } bia;
+ struct {
+ __be32 address;
+ } suni;
+ struct {
+ __be32 major;
+ __be32 minor;
+ } version;
+ struct {
+ __be32 read;
+ __be32 write;
+ } speed;
+ struct {
+ __be32 flags;
+ } flush;
+ struct {
+ __be32 address;
+ __be32 data;
+ } memory;
+ __be32 par[3];
+ } args;
+ __be32 request;
+} command;
+
+/* transmit queues and associated structures */
+
+/* The hosts transmit structure. All BIG ENDIAN; host address
+ restricted to first 1GByte, but address passed to the card must
+ have the top MS bit or'ed in. -- check this */
+
+/* TX is described by 1+ tx_frags followed by a tx_frag_end */
+
+typedef struct {
+ __be32 bytes;
+ __be32 address;
+} tx_frag;
+
+/* apart from handle the fields here are for the adapter to play with
+ and should be set to zero */
+
+typedef struct {
+ u32 handle;
+ u16 vc;
+ u16 next_descriptor_length;
+ u32 next_descriptor;
+#ifdef AMB_NEW_MICROCODE
+ u8 cpcs_uu;
+ u8 cpi;
+ u16 pad;
+#endif
+} tx_frag_end;
+
+typedef struct {
+ tx_frag tx_frag;
+ tx_frag_end tx_frag_end;
+ struct sk_buff * skb;
+} tx_simple;
+
+#if 0
+typedef union {
+ tx_frag fragment;
+ tx_frag_end end_of_list;
+} tx_descr;
+#endif
+
+/* this "points" to the sequence of fragments and trailer */
+
+typedef struct {
+ __be16 vc;
+ __be16 tx_descr_length;
+ __be32 tx_descr_addr;
+} tx_in;
+
+/* handle is the handle from tx_in */
+
+typedef struct {
+ u32 handle;
+} tx_out;
+
+/* receive frame structure */
+
+/* All BIG ENDIAN; handle is as passed from host; length is zero for
+ aborted frames, and frames with errors. Header is actually VC
+ number, lec-id is NOT yet supported. */
+
+typedef struct {
+ u32 handle;
+ __be16 vc;
+ __be16 lec_id; // unused
+ __be16 status;
+ __be16 length;
+} rx_out;
+
+/* buffer supply structure */
+
+typedef struct {
+ u32 handle;
+ __be32 host_address;
+} rx_in;
+
+/* This first structure is the area in host memory where the adapter
+ writes its pointer values. These pointer values are BIG ENDIAN and
+ reside in the same 4MB 'page' as this structure. The host gives the
+ adapter the address of this block by sending a doorbell interrupt
+ to the adapter after downloading the code and setting it going. The
+ addresses have the top 10 bits set to 1010000010b -- really?
+
+ The host must initialise these before handing the block to the
+ adapter. */
+
+typedef struct {
+ __be32 command_start; /* SRB commands completions */
+ __be32 command_end; /* SRB commands completions */
+ __be32 tx_start;
+ __be32 tx_end;
+ __be32 txcom_start; /* tx completions */
+ __be32 txcom_end; /* tx completions */
+ struct {
+ __be32 buffer_start;
+ __be32 buffer_end;
+ u32 buffer_q_get;
+ u32 buffer_q_end;
+ u32 buffer_aptr;
+ __be32 rx_start; /* rx completions */
+ __be32 rx_end;
+ u32 rx_ptr;
+ __be32 buffer_size; /* size of host buffer */
+ } rec_struct[NUM_RX_POOLS];
+#ifdef AMB_NEW_MICROCODE
+ u16 init_flags;
+ u16 talk_block_spare;
+#endif
+} adap_talk_block;
+
+/* This structure must be kept in line with the vcr image in sarmain.h
+
+ This is the structure in the host filled in by the adapter by
+ GET_SUNI_STATS */
+
+typedef struct {
+ u8 racp_chcs;
+ u8 racp_uhcs;
+ u16 spare;
+ u32 racp_rcell;
+ u32 tacp_tcell;
+ u32 flags;
+ u32 dropped_cells;
+ u32 dropped_frames;
+} suni_stats;
+
+typedef enum {
+ dead
+} amb_flags;
+
+#define NEXTQ(current,start,limit) \
+ ( (current)+1 < (limit) ? (current)+1 : (start) )
+
+typedef struct {
+ command * start;
+ command * in;
+ command * out;
+ command * limit;
+} amb_cq_ptrs;
+
+typedef struct {
+ spinlock_t lock;
+ unsigned int pending;
+ unsigned int high;
+ unsigned int filled;
+ unsigned int maximum; // size - 1 (q implementation)
+ amb_cq_ptrs ptrs;
+} amb_cq;
+
+typedef struct {
+ spinlock_t lock;
+ unsigned int pending;
+ unsigned int high;
+ unsigned int filled;
+ unsigned int maximum; // size - 1 (q implementation)
+ struct {
+ tx_in * start;
+ tx_in * ptr;
+ tx_in * limit;
+ } in;
+ struct {
+ tx_out * start;
+ tx_out * ptr;
+ tx_out * limit;
+ } out;
+} amb_txq;
+
+typedef struct {
+ spinlock_t lock;
+ unsigned int pending;
+ unsigned int low;
+ unsigned int emptied;
+ unsigned int maximum; // size - 1 (q implementation)
+ struct {
+ rx_in * start;
+ rx_in * ptr;
+ rx_in * limit;
+ } in;
+ struct {
+ rx_out * start;
+ rx_out * ptr;
+ rx_out * limit;
+ } out;
+ unsigned int buffers_wanted;
+ unsigned int buffer_size;
+} amb_rxq;
+
+typedef struct {
+ unsigned long tx_ok;
+ struct {
+ unsigned long ok;
+ unsigned long error;
+ unsigned long badcrc;
+ unsigned long toolong;
+ unsigned long aborted;
+ unsigned long unused;
+ } rx;
+} amb_stats;
+
+// a single struct pointed to by atm_vcc->dev_data
+
+typedef struct {
+ u8 tx_vc_bits:7;
+ u8 tx_present:1;
+} amb_tx_info;
+
+typedef struct {
+ unsigned char pool;
+} amb_rx_info;
+
+typedef struct {
+ amb_rx_info rx_info;
+ u16 tx_frame_bits;
+ unsigned int tx_rate;
+ unsigned int rx_rate;
+} amb_vcc;
+
+struct amb_dev {
+ u8 irq;
+ unsigned long flags;
+ u32 iobase;
+ u32 * membase;
+
+ amb_cq cq;
+ amb_txq txq;
+ amb_rxq rxq[NUM_RX_POOLS];
+
+ struct mutex vcc_sf;
+ amb_tx_info txer[NUM_VCS];
+ struct atm_vcc * rxer[NUM_VCS];
+ unsigned int tx_avail;
+ unsigned int rx_avail;
+
+ amb_stats stats;
+
+ struct atm_dev * atm_dev;
+ struct pci_dev * pci_dev;
+ struct timer_list housekeeping;
+};
+
+typedef struct amb_dev amb_dev;
+
+#define AMB_DEV(atm_dev) ((amb_dev *) (atm_dev)->dev_data)
+#define AMB_VCC(atm_vcc) ((amb_vcc *) (atm_vcc)->dev_data)
+
+/* rate rounding */
+
+typedef enum {
+ round_up,
+ round_down,
+ round_nearest
+} rounding;
+
+#endif
diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c
new file mode 100644
index 000000000..480fa6ffb
--- /dev/null
+++ b/drivers/atm/atmtcp.c
@@ -0,0 +1,492 @@
+/* drivers/atm/atmtcp.c - ATM over TCP "device" driver */
+
+/* Written 1997-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/atmdev.h>
+#include <linux/atm_tcp.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/atomic.h>
+
+
+extern int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */
+
+
+#define PRIV(dev) ((struct atmtcp_dev_data *) ((dev)->dev_data))
+
+
+struct atmtcp_dev_data {
+ struct atm_vcc *vcc; /* control VCC; NULL if detached */
+ int persist; /* non-zero if persistent */
+};
+
+
+#define DEV_LABEL "atmtcp"
+
+#define MAX_VPI_BITS 8 /* simplifies life */
+#define MAX_VCI_BITS 16
+
+
+/*
+ * Hairy code ahead: the control VCC may be closed while we're still
+ * waiting for an answer, so we need to re-validate out_vcc every once
+ * in a while.
+ */
+
+
+static int atmtcp_send_control(struct atm_vcc *vcc,int type,
+ const struct atmtcp_control *msg,int flag)
+{
+ DECLARE_WAITQUEUE(wait,current);
+ struct atm_vcc *out_vcc;
+ struct sk_buff *skb;
+ struct atmtcp_control *new_msg;
+ int old_test;
+ int error = 0;
+
+ out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL;
+ if (!out_vcc) return -EUNATCH;
+ skb = alloc_skb(sizeof(*msg),GFP_KERNEL);
+ if (!skb) return -ENOMEM;
+ mb();
+ out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL;
+ if (!out_vcc) {
+ dev_kfree_skb(skb);
+ return -EUNATCH;
+ }
+ atm_force_charge(out_vcc,skb->truesize);
+ new_msg = (struct atmtcp_control *) skb_put(skb,sizeof(*new_msg));
+ *new_msg = *msg;
+ new_msg->hdr.length = ATMTCP_HDR_MAGIC;
+ new_msg->type = type;
+ memset(&new_msg->vcc,0,sizeof(atm_kptr_t));
+ *(struct atm_vcc **) &new_msg->vcc = vcc;
+ old_test = test_bit(flag,&vcc->flags);
+ out_vcc->push(out_vcc,skb);
+ add_wait_queue(sk_sleep(sk_atm(vcc)), &wait);
+ while (test_bit(flag,&vcc->flags) == old_test) {
+ mb();
+ out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL;
+ if (!out_vcc) {
+ error = -EUNATCH;
+ break;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk_sleep(sk_atm(vcc)), &wait);
+ return error;
+}
+
+
+static int atmtcp_recv_control(const struct atmtcp_control *msg)
+{
+ struct atm_vcc *vcc = *(struct atm_vcc **) &msg->vcc;
+
+ vcc->vpi = msg->addr.sap_addr.vpi;
+ vcc->vci = msg->addr.sap_addr.vci;
+ vcc->qos = msg->qos;
+ sk_atm(vcc)->sk_err = -msg->result;
+ switch (msg->type) {
+ case ATMTCP_CTRL_OPEN:
+ change_bit(ATM_VF_READY,&vcc->flags);
+ break;
+ case ATMTCP_CTRL_CLOSE:
+ change_bit(ATM_VF_ADDR,&vcc->flags);
+ break;
+ default:
+ printk(KERN_ERR "atmtcp_recv_control: unknown type %d\n",
+ msg->type);
+ return -EINVAL;
+ }
+ wake_up(sk_sleep(sk_atm(vcc)));
+ return 0;
+}
+
+
+static void atmtcp_v_dev_close(struct atm_dev *dev)
+{
+ /* Nothing.... Isn't this simple :-) -- REW */
+}
+
+
+static int atmtcp_v_open(struct atm_vcc *vcc)
+{
+ struct atmtcp_control msg;
+ int error;
+ short vpi = vcc->vpi;
+ int vci = vcc->vci;
+
+ memset(&msg,0,sizeof(msg));
+ msg.addr.sap_family = AF_ATMPVC;
+ msg.hdr.vpi = htons(vpi);
+ msg.addr.sap_addr.vpi = vpi;
+ msg.hdr.vci = htons(vci);
+ msg.addr.sap_addr.vci = vci;
+ if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) return 0;
+ msg.type = ATMTCP_CTRL_OPEN;
+ msg.qos = vcc->qos;
+ set_bit(ATM_VF_ADDR,&vcc->flags);
+ clear_bit(ATM_VF_READY,&vcc->flags); /* just in case ... */
+ error = atmtcp_send_control(vcc,ATMTCP_CTRL_OPEN,&msg,ATM_VF_READY);
+ if (error) return error;
+ return -sk_atm(vcc)->sk_err;
+}
+
+
+static void atmtcp_v_close(struct atm_vcc *vcc)
+{
+ struct atmtcp_control msg;
+
+ memset(&msg,0,sizeof(msg));
+ msg.addr.sap_family = AF_ATMPVC;
+ msg.addr.sap_addr.vpi = vcc->vpi;
+ msg.addr.sap_addr.vci = vcc->vci;
+ clear_bit(ATM_VF_READY,&vcc->flags);
+ (void) atmtcp_send_control(vcc,ATMTCP_CTRL_CLOSE,&msg,ATM_VF_ADDR);
+}
+
+
+static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
+{
+ struct atm_cirange ci;
+ struct atm_vcc *vcc;
+ struct sock *s;
+ int i;
+
+ if (cmd != ATM_SETCIRANGE) return -ENOIOCTLCMD;
+ if (copy_from_user(&ci, arg,sizeof(ci))) return -EFAULT;
+ if (ci.vpi_bits == ATM_CI_MAX) ci.vpi_bits = MAX_VPI_BITS;
+ if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS;
+ if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 ||
+ ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0) return -EINVAL;
+ read_lock(&vcc_sklist_lock);
+ for(i = 0; i < VCC_HTABLE_SIZE; ++i) {
+ struct hlist_head *head = &vcc_hash[i];
+
+ sk_for_each(s, head) {
+ vcc = atm_sk(s);
+ if (vcc->dev != dev)
+ continue;
+ if ((vcc->vpi >> ci.vpi_bits) ||
+ (vcc->vci >> ci.vci_bits)) {
+ read_unlock(&vcc_sklist_lock);
+ return -EBUSY;
+ }
+ }
+ }
+ read_unlock(&vcc_sklist_lock);
+ dev->ci_range = ci;
+ return 0;
+}
+
+
+static int atmtcp_v_send(struct atm_vcc *vcc,struct sk_buff *skb)
+{
+ struct atmtcp_dev_data *dev_data;
+ struct atm_vcc *out_vcc=NULL; /* Initializer quietens GCC warning */
+ struct sk_buff *new_skb;
+ struct atmtcp_hdr *hdr;
+ int size;
+
+ if (vcc->qos.txtp.traffic_class == ATM_NONE) {
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+ dev_data = PRIV(vcc->dev);
+ if (dev_data) out_vcc = dev_data->vcc;
+ if (!dev_data || !out_vcc) {
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb(skb);
+ if (dev_data) return 0;
+ atomic_inc(&vcc->stats->tx_err);
+ return -ENOLINK;
+ }
+ size = skb->len+sizeof(struct atmtcp_hdr);
+ new_skb = atm_alloc_charge(out_vcc,size,GFP_ATOMIC);
+ if (!new_skb) {
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb(skb);
+ atomic_inc(&vcc->stats->tx_err);
+ return -ENOBUFS;
+ }
+ hdr = (void *) skb_put(new_skb,sizeof(struct atmtcp_hdr));
+ hdr->vpi = htons(vcc->vpi);
+ hdr->vci = htons(vcc->vci);
+ hdr->length = htonl(skb->len);
+ skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len);
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb(skb);
+ out_vcc->push(out_vcc,new_skb);
+ atomic_inc(&vcc->stats->tx);
+ atomic_inc(&out_vcc->stats->rx);
+ return 0;
+}
+
+
+static int atmtcp_v_proc(struct atm_dev *dev,loff_t *pos,char *page)
+{
+ struct atmtcp_dev_data *dev_data = PRIV(dev);
+
+ if (*pos) return 0;
+ if (!dev_data->persist) return sprintf(page,"ephemeral\n");
+ return sprintf(page,"persistent, %sconnected\n",
+ dev_data->vcc ? "" : "dis");
+}
+
+
+static void atmtcp_c_close(struct atm_vcc *vcc)
+{
+ struct atm_dev *atmtcp_dev;
+ struct atmtcp_dev_data *dev_data;
+
+ atmtcp_dev = (struct atm_dev *) vcc->dev_data;
+ dev_data = PRIV(atmtcp_dev);
+ dev_data->vcc = NULL;
+ if (dev_data->persist) return;
+ atmtcp_dev->dev_data = NULL;
+ kfree(dev_data);
+ atm_dev_deregister(atmtcp_dev);
+ vcc->dev_data = NULL;
+ module_put(THIS_MODULE);
+}
+
+
+static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci)
+{
+ struct hlist_head *head;
+ struct atm_vcc *vcc;
+ struct sock *s;
+
+ head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)];
+
+ sk_for_each(s, head) {
+ vcc = atm_sk(s);
+ if (vcc->dev == dev &&
+ vcc->vci == vci && vcc->vpi == vpi &&
+ vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ return vcc;
+ }
+ }
+ return NULL;
+}
+
+
+static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb)
+{
+ struct atm_dev *dev;
+ struct atmtcp_hdr *hdr;
+ struct atm_vcc *out_vcc;
+ struct sk_buff *new_skb;
+ int result = 0;
+
+ if (!skb->len) return 0;
+ dev = vcc->dev_data;
+ hdr = (struct atmtcp_hdr *) skb->data;
+ if (hdr->length == ATMTCP_HDR_MAGIC) {
+ result = atmtcp_recv_control(
+ (struct atmtcp_control *) skb->data);
+ goto done;
+ }
+ read_lock(&vcc_sklist_lock);
+ out_vcc = find_vcc(dev, ntohs(hdr->vpi), ntohs(hdr->vci));
+ read_unlock(&vcc_sklist_lock);
+ if (!out_vcc) {
+ result = -EUNATCH;
+ atomic_inc(&vcc->stats->tx_err);
+ goto done;
+ }
+ skb_pull(skb,sizeof(struct atmtcp_hdr));
+ new_skb = atm_alloc_charge(out_vcc,skb->len,GFP_KERNEL);
+ if (!new_skb) {
+ result = -ENOBUFS;
+ goto done;
+ }
+ __net_timestamp(new_skb);
+ skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len);
+ out_vcc->push(out_vcc,new_skb);
+ atomic_inc(&vcc->stats->tx);
+ atomic_inc(&out_vcc->stats->rx);
+done:
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb(skb);
+ return result;
+}
+
+
+/*
+ * Device operations for the virtual ATM devices created by ATMTCP.
+ */
+
+
+static struct atmdev_ops atmtcp_v_dev_ops = {
+ .dev_close = atmtcp_v_dev_close,
+ .open = atmtcp_v_open,
+ .close = atmtcp_v_close,
+ .ioctl = atmtcp_v_ioctl,
+ .send = atmtcp_v_send,
+ .proc_read = atmtcp_v_proc,
+ .owner = THIS_MODULE
+};
+
+
+/*
+ * Device operations for the ATMTCP control device.
+ */
+
+
+static struct atmdev_ops atmtcp_c_dev_ops = {
+ .close = atmtcp_c_close,
+ .send = atmtcp_c_send
+};
+
+
+static struct atm_dev atmtcp_control_dev = {
+ .ops = &atmtcp_c_dev_ops,
+ .type = "atmtcp",
+ .number = 999,
+ .lock = __SPIN_LOCK_UNLOCKED(atmtcp_control_dev.lock)
+};
+
+
+static int atmtcp_create(int itf,int persist,struct atm_dev **result)
+{
+ struct atmtcp_dev_data *dev_data;
+ struct atm_dev *dev;
+
+ dev_data = kmalloc(sizeof(*dev_data),GFP_KERNEL);
+ if (!dev_data)
+ return -ENOMEM;
+
+ dev = atm_dev_register(DEV_LABEL,NULL,&atmtcp_v_dev_ops,itf,NULL);
+ if (!dev) {
+ kfree(dev_data);
+ return itf == -1 ? -ENOMEM : -EBUSY;
+ }
+ dev->ci_range.vpi_bits = MAX_VPI_BITS;
+ dev->ci_range.vci_bits = MAX_VCI_BITS;
+ dev->dev_data = dev_data;
+ PRIV(dev)->vcc = NULL;
+ PRIV(dev)->persist = persist;
+ if (result) *result = dev;
+ return 0;
+}
+
+
+static int atmtcp_attach(struct atm_vcc *vcc,int itf)
+{
+ struct atm_dev *dev;
+
+ dev = NULL;
+ if (itf != -1) dev = atm_dev_lookup(itf);
+ if (dev) {
+ if (dev->ops != &atmtcp_v_dev_ops) {
+ atm_dev_put(dev);
+ return -EMEDIUMTYPE;
+ }
+ if (PRIV(dev)->vcc) {
+ atm_dev_put(dev);
+ return -EBUSY;
+ }
+ }
+ else {
+ int error;
+
+ error = atmtcp_create(itf,0,&dev);
+ if (error) return error;
+ }
+ PRIV(dev)->vcc = vcc;
+ vcc->dev = &atmtcp_control_dev;
+ vcc_insert_socket(sk_atm(vcc));
+ set_bit(ATM_VF_META,&vcc->flags);
+ set_bit(ATM_VF_READY,&vcc->flags);
+ vcc->dev_data = dev;
+ (void) atm_init_aal5(vcc); /* @@@ losing AAL in transit ... */
+ vcc->stats = &atmtcp_control_dev.stats.aal5;
+ return dev->number;
+}
+
+
+static int atmtcp_create_persistent(int itf)
+{
+ return atmtcp_create(itf,1,NULL);
+}
+
+
+static int atmtcp_remove_persistent(int itf)
+{
+ struct atm_dev *dev;
+ struct atmtcp_dev_data *dev_data;
+
+ dev = atm_dev_lookup(itf);
+ if (!dev) return -ENODEV;
+ if (dev->ops != &atmtcp_v_dev_ops) {
+ atm_dev_put(dev);
+ return -EMEDIUMTYPE;
+ }
+ dev_data = PRIV(dev);
+ if (!dev_data->persist) return 0;
+ dev_data->persist = 0;
+ if (PRIV(dev)->vcc) return 0;
+ kfree(dev_data);
+ atm_dev_put(dev);
+ atm_dev_deregister(dev);
+ return 0;
+}
+
+static int atmtcp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct atm_vcc *vcc = ATM_SD(sock);
+
+ if (cmd != SIOCSIFATMTCP && cmd != ATMTCP_CREATE && cmd != ATMTCP_REMOVE)
+ return -ENOIOCTLCMD;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+ case SIOCSIFATMTCP:
+ err = atmtcp_attach(vcc, (int) arg);
+ if (err >= 0) {
+ sock->state = SS_CONNECTED;
+ __module_get(THIS_MODULE);
+ }
+ break;
+ case ATMTCP_CREATE:
+ err = atmtcp_create_persistent((int) arg);
+ break;
+ case ATMTCP_REMOVE:
+ err = atmtcp_remove_persistent((int) arg);
+ break;
+ }
+ return err;
+}
+
+static struct atm_ioctl atmtcp_ioctl_ops = {
+ .owner = THIS_MODULE,
+ .ioctl = atmtcp_ioctl,
+};
+
+static __init int atmtcp_init(void)
+{
+ register_atm_ioctl(&atmtcp_ioctl_ops);
+ return 0;
+}
+
+
+static void __exit atmtcp_exit(void)
+{
+ deregister_atm_ioctl(&atmtcp_ioctl_ops);
+}
+
+MODULE_LICENSE("GPL");
+module_init(atmtcp_init);
+module_exit(atmtcp_exit);
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
new file mode 100644
index 000000000..6339efd32
--- /dev/null
+++ b/drivers/atm/eni.c
@@ -0,0 +1,2340 @@
+/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/skbuff.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/uio.h>
+#include <linux/init.h>
+#include <linux/atm_eni.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+#include <asm/uaccess.h>
+#include <asm/string.h>
+#include <asm/byteorder.h>
+
+#include "tonga.h"
+#include "midway.h"
+#include "suni.h"
+#include "eni.h"
+
+#if !defined(__i386__) && !defined(__x86_64__)
+#ifndef ioremap_nocache
+#define ioremap_nocache(X,Y) ioremap(X,Y)
+#endif
+#endif
+
+/*
+ * TODO:
+ *
+ * Show stoppers
+ * none
+ *
+ * Minor
+ * - OAM support
+ * - fix bugs listed below
+ */
+
+/*
+ * KNOWN BUGS:
+ *
+ * - may run into JK-JK bug and deadlock
+ * - should allocate UBR channel first
+ * - buffer space allocation algorithm is stupid
+ * (RX: should be maxSDU+maxdelay*rate
+ * TX: should be maxSDU+min(maxSDU,maxdelay*rate) )
+ * - doesn't support OAM cells
+ * - eni_put_free may hang if not putting memory fragments that _complete_
+ * 2^n block (never happens in real life, though)
+ */
+
+
+#if 0
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+
+#ifndef CONFIG_ATM_ENI_TUNE_BURST
+#define CONFIG_ATM_ENI_BURST_TX_8W
+#define CONFIG_ATM_ENI_BURST_RX_4W
+#endif
+
+
+#ifndef CONFIG_ATM_ENI_DEBUG
+
+
+#define NULLCHECK(x)
+
+#define EVENT(s,a,b)
+
+
+static void event_dump(void)
+{
+}
+
+
+#else
+
+
+/*
+ * NULL pointer checking
+ */
+
+#define NULLCHECK(x) \
+ if ((unsigned long) (x) < 0x30) \
+ printk(KERN_CRIT #x "==0x%lx\n",(unsigned long) (x))
+
+/*
+ * Very extensive activity logging. Greatly improves bug detection speed but
+ * costs a few Mbps if enabled.
+ */
+
+#define EV 64
+
+static const char *ev[EV];
+static unsigned long ev_a[EV],ev_b[EV];
+static int ec = 0;
+
+
+static void EVENT(const char *s,unsigned long a,unsigned long b)
+{
+ ev[ec] = s;
+ ev_a[ec] = a;
+ ev_b[ec] = b;
+ ec = (ec+1) % EV;
+}
+
+
+static void event_dump(void)
+{
+ int n,i;
+
+ for (n = 0; n < EV; n++) {
+ i = (ec+n) % EV;
+ printk(KERN_NOTICE);
+ printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
+ }
+}
+
+
+#endif /* CONFIG_ATM_ENI_DEBUG */
+
+
+/*
+ * NExx must not be equal at end
+ * EExx may be equal at end
+ * xxPJOK verify validity of pointer jumps
+ * xxPMOK operating on a circular buffer of "c" words
+ */
+
+#define NEPJOK(a0,a1,b) \
+ ((a0) < (a1) ? (b) <= (a0) || (b) > (a1) : (b) <= (a0) && (b) > (a1))
+#define EEPJOK(a0,a1,b) \
+ ((a0) < (a1) ? (b) < (a0) || (b) >= (a1) : (b) < (a0) && (b) >= (a1))
+#define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b)
+#define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b)
+
+
+static int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0,
+ backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0,
+ putting = 0;
+
+static struct atm_dev *eni_boards = NULL;
+
+/* Read/write registers on card */
+#define eni_in(r) readl(eni_dev->reg+(r)*4)
+#define eni_out(v,r) writel((v),eni_dev->reg+(r)*4)
+
+
+/*-------------------------------- utilities --------------------------------*/
+
+
+static void dump_mem(struct eni_dev *eni_dev)
+{
+ int i;
+
+ for (i = 0; i < eni_dev->free_len; i++)
+ printk(KERN_DEBUG " %d: %p %d\n",i,
+ eni_dev->free_list[i].start,
+ 1 << eni_dev->free_list[i].order);
+}
+
+
+static void dump(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+
+ int i;
+
+ eni_dev = ENI_DEV(dev);
+ printk(KERN_NOTICE "Free memory\n");
+ dump_mem(eni_dev);
+ printk(KERN_NOTICE "TX buffers\n");
+ for (i = 0; i < NR_CHAN; i++)
+ if (eni_dev->tx[i].send)
+ printk(KERN_NOTICE " TX %d @ %p: %ld\n",i,
+ eni_dev->tx[i].send,eni_dev->tx[i].words*4);
+ printk(KERN_NOTICE "RX buffers\n");
+ for (i = 0; i < 1024; i++)
+ if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx)
+ printk(KERN_NOTICE " RX %d @ %p: %ld\n",i,
+ ENI_VCC(eni_dev->rx_map[i])->recv,
+ ENI_VCC(eni_dev->rx_map[i])->words*4);
+ printk(KERN_NOTICE "----\n");
+}
+
+
+static void eni_put_free(struct eni_dev *eni_dev, void __iomem *start,
+ unsigned long size)
+{
+ struct eni_free *list;
+ int len,order;
+
+ DPRINTK("init 0x%lx+%ld(0x%lx)\n",start,size,size);
+ start += eni_dev->base_diff;
+ list = eni_dev->free_list;
+ len = eni_dev->free_len;
+ while (size) {
+ if (len >= eni_dev->free_list_size) {
+ printk(KERN_CRIT "eni_put_free overflow (%p,%ld)\n",
+ start,size);
+ break;
+ }
+ for (order = 0; !(((unsigned long)start | size) & (1 << order)); order++);
+ if (MID_MIN_BUF_SIZE > (1 << order)) {
+ printk(KERN_CRIT "eni_put_free: order %d too small\n",
+ order);
+ break;
+ }
+ list[len].start = (void __iomem *) start;
+ list[len].order = order;
+ len++;
+ start += 1 << order;
+ size -= 1 << order;
+ }
+ eni_dev->free_len = len;
+ /*dump_mem(eni_dev);*/
+}
+
+
+static void __iomem *eni_alloc_mem(struct eni_dev *eni_dev, unsigned long *size)
+{
+ struct eni_free *list;
+ void __iomem *start;
+ int len,i,order,best_order,index;
+
+ list = eni_dev->free_list;
+ len = eni_dev->free_len;
+ if (*size < MID_MIN_BUF_SIZE) *size = MID_MIN_BUF_SIZE;
+ if (*size > MID_MAX_BUF_SIZE) return NULL;
+ for (order = 0; (1 << order) < *size; order++);
+ DPRINTK("trying: %ld->%d\n",*size,order);
+ best_order = 65; /* we don't have more than 2^64 of anything ... */
+ index = 0; /* silence GCC */
+ for (i = 0; i < len; i++)
+ if (list[i].order == order) {
+ best_order = order;
+ index = i;
+ break;
+ }
+ else if (best_order > list[i].order && list[i].order > order) {
+ best_order = list[i].order;
+ index = i;
+ }
+ if (best_order == 65) return NULL;
+ start = list[index].start-eni_dev->base_diff;
+ list[index] = list[--len];
+ eni_dev->free_len = len;
+ *size = 1 << order;
+ eni_put_free(eni_dev,start+*size,(1 << best_order)-*size);
+ DPRINTK("%ld bytes (order %d) at 0x%lx\n",*size,order,start);
+ memset_io(start,0,*size); /* never leak data */
+ /*dump_mem(eni_dev);*/
+ return start;
+}
+
+
+static void eni_free_mem(struct eni_dev *eni_dev, void __iomem *start,
+ unsigned long size)
+{
+ struct eni_free *list;
+ int len,i,order;
+
+ start += eni_dev->base_diff;
+ list = eni_dev->free_list;
+ len = eni_dev->free_len;
+ for (order = -1; size; order++) size >>= 1;
+ DPRINTK("eni_free_mem: %p+0x%lx (order %d)\n",start,size,order);
+ for (i = 0; i < len; i++)
+ if (((unsigned long) list[i].start) == ((unsigned long)start^(1 << order)) &&
+ list[i].order == order) {
+ DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i,
+ list[i].start,start,1 << order,list[i].order,order);
+ list[i] = list[--len];
+ start = (void __iomem *) ((unsigned long) start & ~(unsigned long) (1 << order));
+ order++;
+ i = -1;
+ continue;
+ }
+ if (len >= eni_dev->free_list_size) {
+ printk(KERN_ALERT "eni_free_mem overflow (%p,%d)\n",start,
+ order);
+ return;
+ }
+ list[len].start = start;
+ list[len].order = order;
+ eni_dev->free_len = len+1;
+ /*dump_mem(eni_dev);*/
+}
+
+
+/*----------------------------------- RX ------------------------------------*/
+
+
+#define ENI_VCC_NOS ((struct atm_vcc *) 1)
+
+
+static void rx_ident_err(struct atm_vcc *vcc)
+{
+ struct atm_dev *dev;
+ struct eni_dev *eni_dev;
+ struct eni_vcc *eni_vcc;
+
+ dev = vcc->dev;
+ eni_dev = ENI_DEV(dev);
+ /* immediately halt adapter */
+ eni_out(eni_in(MID_MC_S) &
+ ~(MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE),MID_MC_S);
+ /* dump useful information */
+ eni_vcc = ENI_VCC(vcc);
+ printk(KERN_ALERT DEV_LABEL "(itf %d): driver error - RX ident "
+ "mismatch\n",dev->number);
+ printk(KERN_ALERT " VCI %d, rxing %d, words %ld\n",vcc->vci,
+ eni_vcc->rxing,eni_vcc->words);
+ printk(KERN_ALERT " host descr 0x%lx, rx pos 0x%lx, descr value "
+ "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos,
+ (unsigned) readl(eni_vcc->recv+eni_vcc->descr*4));
+ printk(KERN_ALERT " last %p, servicing %d\n",eni_vcc->last,
+ eni_vcc->servicing);
+ EVENT("---dump ends here---\n",0,0);
+ printk(KERN_NOTICE "---recent events---\n");
+ event_dump();
+ ENI_DEV(dev)->fast = NULL; /* really stop it */
+ ENI_DEV(dev)->slow = NULL;
+ skb_queue_head_init(&ENI_DEV(dev)->rx_queue);
+}
+
+
+static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb,
+ unsigned long skip,unsigned long size,unsigned long eff)
+{
+ struct eni_dev *eni_dev;
+ struct eni_vcc *eni_vcc;
+ u32 dma_rd,dma_wr;
+ u32 dma[RX_DMA_BUF*2];
+ dma_addr_t paddr;
+ unsigned long here;
+ int i,j;
+
+ eni_dev = ENI_DEV(vcc->dev);
+ eni_vcc = ENI_VCC(vcc);
+ paddr = 0; /* GCC, shut up */
+ if (skb) {
+ paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr))
+ goto dma_map_error;
+ ENI_PRV_PADDR(skb) = paddr;
+ if (paddr & 3)
+ printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has "
+ "mis-aligned RX data (0x%lx)\n",vcc->dev->number,
+ vcc->vci,(unsigned long) paddr);
+ ENI_PRV_SIZE(skb) = size+skip;
+ /* PDU plus descriptor */
+ ATM_SKB(skb)->vcc = vcc;
+ }
+ j = 0;
+ if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */
+ here = (eni_vcc->descr+skip) & (eni_vcc->words-1);
+ dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci
+ << MID_DMA_VCI_SHIFT) | MID_DT_JK;
+ j++;
+ }
+ here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1);
+ if (!eff) size += skip;
+ else {
+ unsigned long words;
+
+ if (!size) {
+ DPRINTK("strange things happen ...\n");
+ EVENT("strange things happen ... (skip=%ld,eff=%ld)\n",
+ size,eff);
+ }
+ words = eff;
+ if (paddr & 15) {
+ unsigned long init;
+
+ init = 4-((paddr & 15) >> 2);
+ if (init > words) init = words;
+ dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
+ (vcc->vci << MID_DMA_VCI_SHIFT);
+ dma[j++] = paddr;
+ paddr += init << 2;
+ words -= init;
+ }
+#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */
+ if (words & ~15) {
+ dma[j++] = MID_DT_16W | ((words >> 4) <<
+ MID_DMA_COUNT_SHIFT) | (vcc->vci <<
+ MID_DMA_VCI_SHIFT);
+ dma[j++] = paddr;
+ paddr += (words & ~15) << 2;
+ words &= 15;
+ }
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */
+ if (words & ~7) {
+ dma[j++] = MID_DT_8W | ((words >> 3) <<
+ MID_DMA_COUNT_SHIFT) | (vcc->vci <<
+ MID_DMA_VCI_SHIFT);
+ dma[j++] = paddr;
+ paddr += (words & ~7) << 2;
+ words &= 7;
+ }
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */
+ if (words & ~3) {
+ dma[j++] = MID_DT_4W | ((words >> 2) <<
+ MID_DMA_COUNT_SHIFT) | (vcc->vci <<
+ MID_DMA_VCI_SHIFT);
+ dma[j++] = paddr;
+ paddr += (words & ~3) << 2;
+ words &= 3;
+ }
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */
+ if (words & ~1) {
+ dma[j++] = MID_DT_2W | ((words >> 1) <<
+ MID_DMA_COUNT_SHIFT) | (vcc->vci <<
+ MID_DMA_VCI_SHIFT);
+ dma[j++] = paddr;
+ paddr += (words & ~1) << 2;
+ words &= 1;
+ }
+#endif
+ if (words) {
+ dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT)
+ | (vcc->vci << MID_DMA_VCI_SHIFT);
+ dma[j++] = paddr;
+ }
+ }
+ if (size != eff) {
+ dma[j++] = (here << MID_DMA_COUNT_SHIFT) |
+ (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK;
+ j++;
+ }
+ if (!j || j > 2*RX_DMA_BUF) {
+ printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n");
+ goto trouble;
+ }
+ dma[j-2] |= MID_DMA_END;
+ j = j >> 1;
+ dma_wr = eni_in(MID_DMA_WR_RX);
+ dma_rd = eni_in(MID_DMA_RD_RX);
+ /*
+ * Can I move the dma_wr pointer by 2j+1 positions without overwriting
+ * data that hasn't been read (position of dma_rd) yet ?
+ */
+ if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */
+ printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n",
+ vcc->dev->number);
+ goto trouble;
+ }
+ for (i = 0; i < j; i++) {
+ writel(dma[i*2],eni_dev->rx_dma+dma_wr*8);
+ writel(dma[i*2+1],eni_dev->rx_dma+dma_wr*8+4);
+ dma_wr = (dma_wr+1) & (NR_DMA_RX-1);
+ }
+ if (skb) {
+ ENI_PRV_POS(skb) = eni_vcc->descr+size+1;
+ skb_queue_tail(&eni_dev->rx_queue,skb);
+ eni_vcc->last = skb;
+rx_enqueued++;
+ }
+ eni_vcc->descr = here;
+ eni_out(dma_wr,MID_DMA_WR_RX);
+ return 0;
+
+trouble:
+ if (paddr)
+ dma_unmap_single(&eni_dev->pci_dev->dev,paddr,skb->len,
+ DMA_FROM_DEVICE);
+dma_map_error:
+ if (skb) dev_kfree_skb_irq(skb);
+ return -1;
+}
+
+
+static void discard(struct atm_vcc *vcc,unsigned long size)
+{
+ struct eni_vcc *eni_vcc;
+
+ eni_vcc = ENI_VCC(vcc);
+ EVENT("discard (size=%ld)\n",size,0);
+ while (do_rx_dma(vcc,NULL,1,size,0)) EVENT("BUSY LOOP",0,0);
+ /* could do a full fallback, but that might be more expensive */
+ if (eni_vcc->rxing) ENI_PRV_POS(eni_vcc->last) += size+1;
+ else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1);
+}
+
+
+/*
+ * TODO: should check whether direct copies (without DMA setup, dequeuing on
+ * interrupt, etc.) aren't much faster for AAL0
+ */
+
+static int rx_aal0(struct atm_vcc *vcc)
+{
+ struct eni_vcc *eni_vcc;
+ unsigned long descr;
+ unsigned long length;
+ struct sk_buff *skb;
+
+ DPRINTK(">rx_aal0\n");
+ eni_vcc = ENI_VCC(vcc);
+ descr = readl(eni_vcc->recv+eni_vcc->descr*4);
+ if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) {
+ rx_ident_err(vcc);
+ return 1;
+ }
+ if (descr & MID_RED_T) {
+ DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
+ vcc->dev->number);
+ length = 0;
+ atomic_inc(&vcc->stats->rx_err);
+ }
+ else {
+ length = ATM_CELL_SIZE-1; /* no HEC */
+ }
+ skb = length ? atm_alloc_charge(vcc,length,GFP_ATOMIC) : NULL;
+ if (!skb) {
+ discard(vcc,length >> 2);
+ return 0;
+ }
+ skb_put(skb,length);
+ skb->tstamp = eni_vcc->timestamp;
+ DPRINTK("got len %ld\n",length);
+ if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1;
+ eni_vcc->rxing++;
+ return 0;
+}
+
+
+static int rx_aal5(struct atm_vcc *vcc)
+{
+ struct eni_vcc *eni_vcc;
+ unsigned long descr;
+ unsigned long size,eff,length;
+ struct sk_buff *skb;
+
+ EVENT("rx_aal5\n",0,0);
+ DPRINTK(">rx_aal5\n");
+ eni_vcc = ENI_VCC(vcc);
+ descr = readl(eni_vcc->recv+eni_vcc->descr*4);
+ if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) {
+ rx_ident_err(vcc);
+ return 1;
+ }
+ if (descr & (MID_RED_T | MID_RED_CRC_ERR)) {
+ if (descr & MID_RED_T) {
+ EVENT("empty cell (descr=0x%lx)\n",descr,0);
+ DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
+ vcc->dev->number);
+ size = 0;
+ }
+ else {
+ static unsigned long silence = 0;
+
+ if (time_after(jiffies, silence) || silence == 0) {
+ printk(KERN_WARNING DEV_LABEL "(itf %d): "
+ "discarding PDU(s) with CRC error\n",
+ vcc->dev->number);
+ silence = (jiffies+2*HZ)|1;
+ }
+ size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
+ EVENT("CRC error (descr=0x%lx,size=%ld)\n",descr,
+ size);
+ }
+ eff = length = 0;
+ atomic_inc(&vcc->stats->rx_err);
+ }
+ else {
+ size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
+ DPRINTK("size=%ld\n",size);
+ length = readl(eni_vcc->recv+(((eni_vcc->descr+size-1) &
+ (eni_vcc->words-1)))*4) & 0xffff;
+ /* -trailer(2)+header(1) */
+ if (length && length <= (size << 2)-8 && length <=
+ ATM_MAX_AAL5_PDU) eff = (length+3) >> 2;
+ else { /* ^ trailer length (8) */
+ EVENT("bad PDU (descr=0x08%lx,length=%ld)\n",descr,
+ length);
+ printk(KERN_ERR DEV_LABEL "(itf %d): bad AAL5 PDU "
+ "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n",
+ vcc->dev->number,vcc->vci,length,size << 2,descr);
+ length = eff = 0;
+ atomic_inc(&vcc->stats->rx_err);
+ }
+ }
+ skb = eff ? atm_alloc_charge(vcc,eff << 2,GFP_ATOMIC) : NULL;
+ if (!skb) {
+ discard(vcc,size);
+ return 0;
+ }
+ skb_put(skb,length);
+ DPRINTK("got len %ld\n",length);
+ if (do_rx_dma(vcc,skb,1,size,eff)) return 1;
+ eni_vcc->rxing++;
+ return 0;
+}
+
+
+static inline int rx_vcc(struct atm_vcc *vcc)
+{
+ void __iomem *vci_dsc;
+ unsigned long tmp;
+ struct eni_vcc *eni_vcc;
+
+ eni_vcc = ENI_VCC(vcc);
+ vci_dsc = ENI_DEV(vcc->dev)->vci+vcc->vci*16;
+ EVENT("rx_vcc(1)\n",0,0);
+ while (eni_vcc->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) >>
+ MID_VCI_DESCR_SHIFT)) {
+ EVENT("rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx)\n",
+ eni_vcc->descr,tmp);
+ DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr,
+ (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >>
+ MID_VCI_DESCR_SHIFT));
+ if (ENI_VCC(vcc)->rx(vcc)) return 1;
+ }
+ /* clear IN_SERVICE flag */
+ writel(readl(vci_dsc) & ~MID_VCI_IN_SERVICE,vci_dsc);
+ /*
+ * If new data has arrived between evaluating the while condition and
+ * clearing IN_SERVICE, we wouldn't be notified until additional data
+ * follows. So we have to loop again to be sure.
+ */
+ EVENT("rx_vcc(3)\n",0,0);
+ while (ENI_VCC(vcc)->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR)
+ >> MID_VCI_DESCR_SHIFT)) {
+ EVENT("rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx)\n",
+ eni_vcc->descr,tmp);
+ DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr,
+ (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >>
+ MID_VCI_DESCR_SHIFT));
+ if (ENI_VCC(vcc)->rx(vcc)) return 1;
+ }
+ return 0;
+}
+
+
+static void poll_rx(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+ struct atm_vcc *curr;
+
+ eni_dev = ENI_DEV(dev);
+ while ((curr = eni_dev->fast)) {
+ EVENT("poll_rx.fast\n",0,0);
+ if (rx_vcc(curr)) return;
+ eni_dev->fast = ENI_VCC(curr)->next;
+ ENI_VCC(curr)->next = ENI_VCC_NOS;
+ barrier();
+ ENI_VCC(curr)->servicing--;
+ }
+ while ((curr = eni_dev->slow)) {
+ EVENT("poll_rx.slow\n",0,0);
+ if (rx_vcc(curr)) return;
+ eni_dev->slow = ENI_VCC(curr)->next;
+ ENI_VCC(curr)->next = ENI_VCC_NOS;
+ barrier();
+ ENI_VCC(curr)->servicing--;
+ }
+}
+
+
+static void get_service(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+ struct atm_vcc *vcc;
+ unsigned long vci;
+
+ DPRINTK(">get_service\n");
+ eni_dev = ENI_DEV(dev);
+ while (eni_in(MID_SERV_WRITE) != eni_dev->serv_read) {
+ vci = readl(eni_dev->service+eni_dev->serv_read*4);
+ eni_dev->serv_read = (eni_dev->serv_read+1) & (NR_SERVICE-1);
+ vcc = eni_dev->rx_map[vci & 1023];
+ if (!vcc) {
+ printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %ld not "
+ "found\n",dev->number,vci);
+ continue; /* nasty but we try to go on anyway */
+ /* @@@ nope, doesn't work */
+ }
+ EVENT("getting from service\n",0,0);
+ if (ENI_VCC(vcc)->next != ENI_VCC_NOS) {
+ EVENT("double service\n",0,0);
+ DPRINTK("Grr, servicing VCC %ld twice\n",vci);
+ continue;
+ }
+ ENI_VCC(vcc)->timestamp = ktime_get_real();
+ ENI_VCC(vcc)->next = NULL;
+ if (vcc->qos.rxtp.traffic_class == ATM_CBR) {
+ if (eni_dev->fast)
+ ENI_VCC(eni_dev->last_fast)->next = vcc;
+ else eni_dev->fast = vcc;
+ eni_dev->last_fast = vcc;
+ }
+ else {
+ if (eni_dev->slow)
+ ENI_VCC(eni_dev->last_slow)->next = vcc;
+ else eni_dev->slow = vcc;
+ eni_dev->last_slow = vcc;
+ }
+putting++;
+ ENI_VCC(vcc)->servicing++;
+ }
+}
+
+
+static void dequeue_rx(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+ struct eni_vcc *eni_vcc;
+ struct atm_vcc *vcc;
+ struct sk_buff *skb;
+ void __iomem *vci_dsc;
+ int first;
+
+ eni_dev = ENI_DEV(dev);
+ first = 1;
+ while (1) {
+ skb = skb_dequeue(&eni_dev->rx_queue);
+ if (!skb) {
+ if (first) {
+ DPRINTK(DEV_LABEL "(itf %d): RX but not "
+ "rxing\n",dev->number);
+ EVENT("nothing to dequeue\n",0,0);
+ }
+ break;
+ }
+ EVENT("dequeued (size=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb),
+ ENI_PRV_POS(skb));
+rx_dequeued++;
+ vcc = ATM_SKB(skb)->vcc;
+ eni_vcc = ENI_VCC(vcc);
+ first = 0;
+ vci_dsc = eni_dev->vci+vcc->vci*16;
+ if (!EEPMOK(eni_vcc->rx_pos,ENI_PRV_SIZE(skb),
+ (readl(vci_dsc+4) & MID_VCI_READ) >> MID_VCI_READ_SHIFT,
+ eni_vcc->words)) {
+ EVENT("requeuing\n",0,0);
+ skb_queue_head(&eni_dev->rx_queue,skb);
+ break;
+ }
+ eni_vcc->rxing--;
+ eni_vcc->rx_pos = ENI_PRV_POS(skb) & (eni_vcc->words-1);
+ dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len,
+ DMA_TO_DEVICE);
+ if (!skb->len) dev_kfree_skb_irq(skb);
+ else {
+ EVENT("pushing (len=%ld)\n",skb->len,0);
+ if (vcc->qos.aal == ATM_AAL0)
+ *(unsigned long *) skb->data =
+ ntohl(*(unsigned long *) skb->data);
+ memset(skb->cb,0,sizeof(struct eni_skb_prv));
+ vcc->push(vcc,skb);
+ pushed++;
+ }
+ atomic_inc(&vcc->stats->rx);
+ }
+ wake_up(&eni_dev->rx_wait);
+}
+
+
+static int open_rx_first(struct atm_vcc *vcc)
+{
+ struct eni_dev *eni_dev;
+ struct eni_vcc *eni_vcc;
+ unsigned long size;
+
+ DPRINTK("open_rx_first\n");
+ eni_dev = ENI_DEV(vcc->dev);
+ eni_vcc = ENI_VCC(vcc);
+ eni_vcc->rx = NULL;
+ if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;
+ size = vcc->qos.rxtp.max_sdu*eni_dev->rx_mult/100;
+ if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <=
+ MID_MAX_BUF_SIZE)
+ size = MID_MAX_BUF_SIZE;
+ eni_vcc->recv = eni_alloc_mem(eni_dev,&size);
+ DPRINTK("rx at 0x%lx\n",eni_vcc->recv);
+ eni_vcc->words = size >> 2;
+ if (!eni_vcc->recv) return -ENOBUFS;
+ eni_vcc->rx = vcc->qos.aal == ATM_AAL5 ? rx_aal5 : rx_aal0;
+ eni_vcc->descr = 0;
+ eni_vcc->rx_pos = 0;
+ eni_vcc->rxing = 0;
+ eni_vcc->servicing = 0;
+ eni_vcc->next = ENI_VCC_NOS;
+ return 0;
+}
+
+
+static int open_rx_second(struct atm_vcc *vcc)
+{
+ void __iomem *here;
+ struct eni_dev *eni_dev;
+ struct eni_vcc *eni_vcc;
+ unsigned long size;
+ int order;
+
+ DPRINTK("open_rx_second\n");
+ eni_dev = ENI_DEV(vcc->dev);
+ eni_vcc = ENI_VCC(vcc);
+ if (!eni_vcc->rx) return 0;
+ /* set up VCI descriptor */
+ here = eni_dev->vci+vcc->vci*16;
+ DPRINTK("loc 0x%x\n",(unsigned) (eni_vcc->recv-eni_dev->ram)/4);
+ size = eni_vcc->words >> 8;
+ for (order = -1; size; order++) size >>= 1;
+ writel(0,here+4); /* descr, read = 0 */
+ writel(0,here+8); /* write, state, count = 0 */
+ if (eni_dev->rx_map[vcc->vci])
+ printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already "
+ "in use\n",vcc->dev->number,vcc->vci);
+ eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */
+ writel(((vcc->qos.aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) <<
+ MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE |
+ (((eni_vcc->recv-eni_dev->ram) >> (MID_LOC_SKIP+2)) <<
+ MID_VCI_LOCATION_SHIFT) | (order << MID_VCI_SIZE_SHIFT),here);
+ return 0;
+}
+
+
+static void close_rx(struct atm_vcc *vcc)
+{
+ DECLARE_WAITQUEUE(wait,current);
+ void __iomem *here;
+ struct eni_dev *eni_dev;
+ struct eni_vcc *eni_vcc;
+
+ eni_vcc = ENI_VCC(vcc);
+ if (!eni_vcc->rx) return;
+ eni_dev = ENI_DEV(vcc->dev);
+ if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
+ here = eni_dev->vci+vcc->vci*16;
+ /* block receiver */
+ writel((readl(here) & ~MID_VCI_MODE) | (MID_MODE_TRASH <<
+ MID_VCI_MODE_SHIFT),here);
+ /* wait for receiver to become idle */
+ udelay(27);
+ /* discard pending cell */
+ writel(readl(here) & ~MID_VCI_IN_SERVICE,here);
+ /* don't accept any new ones */
+ eni_dev->rx_map[vcc->vci] = NULL;
+ /* wait for RX queue to drain */
+ DPRINTK("eni_close: waiting for RX ...\n");
+ EVENT("RX closing\n",0,0);
+ add_wait_queue(&eni_dev->rx_wait,&wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ barrier();
+ for (;;) {
+ /* transition service->rx: rxing++, servicing-- */
+ if (!eni_vcc->servicing) {
+ barrier();
+ if (!eni_vcc->rxing) break;
+ }
+ EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing,
+ eni_vcc->servicing);
+ printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing,
+ eni_vcc->rxing);
+ schedule();
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ }
+ for (;;) {
+ int at_end;
+ u32 tmp;
+
+ tasklet_disable(&eni_dev->task);
+ tmp = readl(eni_dev->vci+vcc->vci*16+4) & MID_VCI_READ;
+ at_end = eni_vcc->rx_pos == tmp >> MID_VCI_READ_SHIFT;
+ tasklet_enable(&eni_dev->task);
+ if (at_end) break;
+ EVENT("drain discard (host 0x%lx, nic 0x%lx)\n",
+ eni_vcc->rx_pos,tmp);
+ printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%x\n",
+ eni_vcc->rx_pos,tmp);
+ schedule();
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&eni_dev->rx_wait,&wait);
+ }
+ eni_free_mem(eni_dev,eni_vcc->recv,eni_vcc->words << 2);
+ eni_vcc->rx = NULL;
+}
+
+
+static int start_rx(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+
+ eni_dev = ENI_DEV(dev);
+ eni_dev->rx_map = (struct atm_vcc **) get_zeroed_page(GFP_KERNEL);
+ if (!eni_dev->rx_map) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
+ dev->number);
+ free_page((unsigned long) eni_dev->free_list);
+ return -ENOMEM;
+ }
+ eni_dev->rx_mult = DEFAULT_RX_MULT;
+ eni_dev->fast = eni_dev->last_fast = NULL;
+ eni_dev->slow = eni_dev->last_slow = NULL;
+ init_waitqueue_head(&eni_dev->rx_wait);
+ skb_queue_head_init(&eni_dev->rx_queue);
+ eni_dev->serv_read = eni_in(MID_SERV_WRITE);
+ eni_out(0,MID_DMA_WR_RX);
+ return 0;
+}
+
+
+/*----------------------------------- TX ------------------------------------*/
+
+
+enum enq_res { enq_ok,enq_next,enq_jam };
+
+
+static inline void put_dma(int chan,u32 *dma,int *j,dma_addr_t paddr,
+ u32 size)
+{
+ u32 init,words;
+
+ DPRINTK("put_dma: 0x%lx+0x%x\n",(unsigned long) paddr,size);
+ EVENT("put_dma: 0x%lx+0x%lx\n",(unsigned long) paddr,size);
+#if 0 /* don't complain anymore */
+ if (paddr & 3)
+ printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr);
+ if (size & 3)
+ printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size);
+#endif
+ if (paddr & 3) {
+ init = 4-(paddr & 3);
+ if (init > size || size < 7) init = size;
+ DPRINTK("put_dma: %lx DMA: %d/%d bytes\n",
+ (unsigned long) paddr,init,size);
+ dma[(*j)++] = MID_DT_BYTE | (init << MID_DMA_COUNT_SHIFT) |
+ (chan << MID_DMA_CHAN_SHIFT);
+ dma[(*j)++] = paddr;
+ paddr += init;
+ size -= init;
+ }
+ words = size >> 2;
+ size &= 3;
+ if (words && (paddr & 31)) {
+ init = 8-((paddr & 31) >> 2);
+ if (init > words) init = words;
+ DPRINTK("put_dma: %lx DMA: %d/%d words\n",
+ (unsigned long) paddr,init,words);
+ dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
+ (chan << MID_DMA_CHAN_SHIFT);
+ dma[(*j)++] = paddr;
+ paddr += init << 2;
+ words -= init;
+ }
+#ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */
+ if (words & ~15) {
+ DPRINTK("put_dma: %lx DMA: %d*16/%d words\n",
+ (unsigned long) paddr,words >> 4,words);
+ dma[(*j)++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT)
+ | (chan << MID_DMA_CHAN_SHIFT);
+ dma[(*j)++] = paddr;
+ paddr += (words & ~15) << 2;
+ words &= 15;
+ }
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */
+ if (words & ~7) {
+ DPRINTK("put_dma: %lx DMA: %d*8/%d words\n",
+ (unsigned long) paddr,words >> 3,words);
+ dma[(*j)++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT)
+ | (chan << MID_DMA_CHAN_SHIFT);
+ dma[(*j)++] = paddr;
+ paddr += (words & ~7) << 2;
+ words &= 7;
+ }
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */
+ if (words & ~3) {
+ DPRINTK("put_dma: %lx DMA: %d*4/%d words\n",
+ (unsigned long) paddr,words >> 2,words);
+ dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT)
+ | (chan << MID_DMA_CHAN_SHIFT);
+ dma[(*j)++] = paddr;
+ paddr += (words & ~3) << 2;
+ words &= 3;
+ }
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */
+ if (words & ~1) {
+ DPRINTK("put_dma: %lx DMA: %d*2/%d words\n",
+ (unsigned long) paddr,words >> 1,words);
+ dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT)
+ | (chan << MID_DMA_CHAN_SHIFT);
+ dma[(*j)++] = paddr;
+ paddr += (words & ~1) << 2;
+ words &= 1;
+ }
+#endif
+ if (words) {
+ DPRINTK("put_dma: %lx DMA: %d words\n",(unsigned long) paddr,
+ words);
+ dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) |
+ (chan << MID_DMA_CHAN_SHIFT);
+ dma[(*j)++] = paddr;
+ paddr += words << 2;
+ }
+ if (size) {
+ DPRINTK("put_dma: %lx DMA: %d bytes\n",(unsigned long) paddr,
+ size);
+ dma[(*j)++] = MID_DT_BYTE | (size << MID_DMA_COUNT_SHIFT) |
+ (chan << MID_DMA_CHAN_SHIFT);
+ dma[(*j)++] = paddr;
+ }
+}
+
+
+static enum enq_res do_tx(struct sk_buff *skb)
+{
+ struct atm_vcc *vcc;
+ struct eni_dev *eni_dev;
+ struct eni_vcc *eni_vcc;
+ struct eni_tx *tx;
+ dma_addr_t paddr;
+ u32 dma_rd,dma_wr;
+ u32 size; /* in words */
+ int aal5,dma_size,i,j;
+
+ DPRINTK(">do_tx\n");
+ NULLCHECK(skb);
+ EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len);
+ vcc = ATM_SKB(skb)->vcc;
+ NULLCHECK(vcc);
+ eni_dev = ENI_DEV(vcc->dev);
+ NULLCHECK(eni_dev);
+ eni_vcc = ENI_VCC(vcc);
+ tx = eni_vcc->tx;
+ NULLCHECK(tx);
+#if 0 /* Enable this for testing with the "align" program */
+ {
+ unsigned int hack = *((char *) skb->data)-'0';
+
+ if (hack < 8) {
+ skb->data += hack;
+ skb->len -= hack;
+ }
+ }
+#endif
+#if 0 /* should work now */
+ if ((unsigned long) skb->data & 3)
+ printk(KERN_ERR DEV_LABEL "(itf %d): VCI %d has mis-aligned "
+ "TX data\n",vcc->dev->number,vcc->vci);
+#endif
+ /*
+ * Potential future IP speedup: make hard_header big enough to put
+ * segmentation descriptor directly into PDU. Saves: 4 slave writes,
+ * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-)
+ */
+
+ aal5 = vcc->qos.aal == ATM_AAL5;
+ /* check space in buffer */
+ if (!aal5)
+ size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE;
+ /* cell without HEC plus segmentation header (includes
+ four-byte cell header) */
+ else {
+ size = skb->len+4*AAL5_TRAILER+ATM_CELL_PAYLOAD-1;
+ /* add AAL5 trailer */
+ size = ((size-(size % ATM_CELL_PAYLOAD)) >> 2)+TX_DESCR_SIZE;
+ /* add segmentation header */
+ }
+ /*
+ * Can I move tx_pos by size bytes without getting closer than TX_GAP
+ * to the read pointer ? TX_GAP means to leave some space for what
+ * the manual calls "too close".
+ */
+ if (!NEPMOK(tx->tx_pos,size+TX_GAP,
+ eni_in(MID_TX_RDPTR(tx->index)),tx->words)) {
+ DPRINTK(DEV_LABEL "(itf %d): TX full (size %d)\n",
+ vcc->dev->number,size);
+ return enq_next;
+ }
+ /* check DMA */
+ dma_wr = eni_in(MID_DMA_WR_TX);
+ dma_rd = eni_in(MID_DMA_RD_TX);
+ dma_size = 3; /* JK for descriptor and final fill, plus final size
+ mis-alignment fix */
+DPRINTK("iovcnt = %d\n",skb_shinfo(skb)->nr_frags);
+ if (!skb_shinfo(skb)->nr_frags) dma_size += 5;
+ else dma_size += 5*(skb_shinfo(skb)->nr_frags+1);
+ if (dma_size > TX_DMA_BUF) {
+ printk(KERN_CRIT DEV_LABEL "(itf %d): needs %d DMA entries "
+ "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF);
+ }
+ DPRINTK("dma_wr is %d, tx_pos is %ld\n",dma_wr,tx->tx_pos);
+ if (dma_wr != dma_rd && ((dma_rd+NR_DMA_TX-dma_wr) & (NR_DMA_TX-1)) <
+ dma_size) {
+ printk(KERN_WARNING DEV_LABEL "(itf %d): TX DMA full\n",
+ vcc->dev->number);
+ return enq_jam;
+ }
+ paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len,
+ DMA_TO_DEVICE);
+ ENI_PRV_PADDR(skb) = paddr;
+ /* prepare DMA queue entries */
+ j = 0;
+ eni_dev->dma[j++] = (((tx->tx_pos+TX_DESCR_SIZE) & (tx->words-1)) <<
+ MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
+ MID_DT_JK;
+ j++;
+ if (!skb_shinfo(skb)->nr_frags)
+ if (aal5) put_dma(tx->index,eni_dev->dma,&j,paddr,skb->len);
+ else put_dma(tx->index,eni_dev->dma,&j,paddr+4,skb->len-4);
+ else {
+DPRINTK("doing direct send\n"); /* @@@ well, this doesn't work anyway */
+ for (i = -1; i < skb_shinfo(skb)->nr_frags; i++)
+ if (i == -1)
+ put_dma(tx->index,eni_dev->dma,&j,(unsigned long)
+ skb->data,
+ skb_headlen(skb));
+ else
+ put_dma(tx->index,eni_dev->dma,&j,(unsigned long)
+ skb_frag_page(&skb_shinfo(skb)->frags[i]) +
+ skb_shinfo(skb)->frags[i].page_offset,
+ skb_frag_size(&skb_shinfo(skb)->frags[i]));
+ }
+ if (skb->len & 3) {
+ put_dma(tx->index, eni_dev->dma, &j, eni_dev->zero.dma,
+ 4 - (skb->len & 3));
+ }
+ /* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */
+ eni_dev->dma[j++] = (((tx->tx_pos+size) & (tx->words-1)) <<
+ MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
+ MID_DMA_END | MID_DT_JK;
+ j++;
+ DPRINTK("DMA at end: %d\n",j);
+ /* store frame */
+ writel((MID_SEG_TX_ID << MID_SEG_ID_SHIFT) |
+ (aal5 ? MID_SEG_AAL5 : 0) | (tx->prescaler << MID_SEG_PR_SHIFT) |
+ (tx->resolution << MID_SEG_RATE_SHIFT) |
+ (size/(ATM_CELL_PAYLOAD/4)),tx->send+tx->tx_pos*4);
+/*printk("dsc = 0x%08lx\n",(unsigned long) readl(tx->send+tx->tx_pos*4));*/
+ writel((vcc->vci << MID_SEG_VCI_SHIFT) |
+ (aal5 ? 0 : (skb->data[3] & 0xf)) |
+ (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0),
+ tx->send+((tx->tx_pos+1) & (tx->words-1))*4);
+ DPRINTK("size: %d, len:%d\n",size,skb->len);
+ if (aal5)
+ writel(skb->len,tx->send+
+ ((tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1))*4);
+ j = j >> 1;
+ for (i = 0; i < j; i++) {
+ writel(eni_dev->dma[i*2],eni_dev->tx_dma+dma_wr*8);
+ writel(eni_dev->dma[i*2+1],eni_dev->tx_dma+dma_wr*8+4);
+ dma_wr = (dma_wr+1) & (NR_DMA_TX-1);
+ }
+ ENI_PRV_POS(skb) = tx->tx_pos;
+ ENI_PRV_SIZE(skb) = size;
+ ENI_VCC(vcc)->txing += size;
+ tx->tx_pos = (tx->tx_pos+size) & (tx->words-1);
+ DPRINTK("dma_wr set to %d, tx_pos is now %ld\n",dma_wr,tx->tx_pos);
+ eni_out(dma_wr,MID_DMA_WR_TX);
+ skb_queue_tail(&eni_dev->tx_queue,skb);
+queued++;
+ return enq_ok;
+}
+
+
+static void poll_tx(struct atm_dev *dev)
+{
+ struct eni_tx *tx;
+ struct sk_buff *skb;
+ enum enq_res res;
+ int i;
+
+ DPRINTK(">poll_tx\n");
+ for (i = NR_CHAN-1; i >= 0; i--) {
+ tx = &ENI_DEV(dev)->tx[i];
+ if (tx->send)
+ while ((skb = skb_dequeue(&tx->backlog))) {
+ res = do_tx(skb);
+ if (res == enq_ok) continue;
+ DPRINTK("re-queuing TX PDU\n");
+ skb_queue_head(&tx->backlog,skb);
+requeued++;
+ if (res == enq_jam) return;
+ break;
+ }
+ }
+}
+
+
+static void dequeue_tx(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+ struct atm_vcc *vcc;
+ struct sk_buff *skb;
+ struct eni_tx *tx;
+
+ NULLCHECK(dev);
+ eni_dev = ENI_DEV(dev);
+ NULLCHECK(eni_dev);
+ while ((skb = skb_dequeue(&eni_dev->tx_queue))) {
+ vcc = ATM_SKB(skb)->vcc;
+ NULLCHECK(vcc);
+ tx = ENI_VCC(vcc)->tx;
+ NULLCHECK(ENI_VCC(vcc)->tx);
+ DPRINTK("dequeue_tx: next 0x%lx curr 0x%x\n",ENI_PRV_POS(skb),
+ (unsigned) eni_in(MID_TX_DESCRSTART(tx->index)));
+ if (ENI_VCC(vcc)->txing < tx->words && ENI_PRV_POS(skb) ==
+ eni_in(MID_TX_DESCRSTART(tx->index))) {
+ skb_queue_head(&eni_dev->tx_queue,skb);
+ break;
+ }
+ ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb);
+ dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len,
+ DMA_TO_DEVICE);
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb_irq(skb);
+ atomic_inc(&vcc->stats->tx);
+ wake_up(&eni_dev->tx_wait);
+dma_complete++;
+ }
+}
+
+
+static struct eni_tx *alloc_tx(struct eni_dev *eni_dev,int ubr)
+{
+ int i;
+
+ for (i = !ubr; i < NR_CHAN; i++)
+ if (!eni_dev->tx[i].send) return eni_dev->tx+i;
+ return NULL;
+}
+
+
+static int comp_tx(struct eni_dev *eni_dev,int *pcr,int reserved,int *pre,
+ int *res,int unlimited)
+{
+ static const int pre_div[] = { 4,16,128,2048 };
+ /* 2^(((x+2)^2-(x+2))/2+1) */
+
+ if (unlimited) *pre = *res = 0;
+ else {
+ if (*pcr > 0) {
+ int div;
+
+ for (*pre = 0; *pre < 3; (*pre)++)
+ if (TS_CLOCK/pre_div[*pre]/64 <= *pcr) break;
+ div = pre_div[*pre]**pcr;
+ DPRINTK("min div %d\n",div);
+ *res = TS_CLOCK/div-1;
+ }
+ else {
+ int div;
+
+ if (!*pcr) *pcr = eni_dev->tx_bw+reserved;
+ for (*pre = 3; *pre >= 0; (*pre)--)
+ if (TS_CLOCK/pre_div[*pre]/64 > -*pcr) break;
+ if (*pre < 3) (*pre)++; /* else fail later */
+ div = pre_div[*pre]*-*pcr;
+ DPRINTK("max div %d\n",div);
+ *res = DIV_ROUND_UP(TS_CLOCK, div)-1;
+ }
+ if (*res < 0) *res = 0;
+ if (*res > MID_SEG_MAX_RATE) *res = MID_SEG_MAX_RATE;
+ }
+ *pcr = TS_CLOCK/pre_div[*pre]/(*res+1);
+ DPRINTK("out pcr: %d (%d:%d)\n",*pcr,*pre,*res);
+ return 0;
+}
+
+
+static int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp,
+ int set_rsv,int set_shp)
+{
+ struct eni_dev *eni_dev = ENI_DEV(vcc->dev);
+ struct eni_vcc *eni_vcc = ENI_VCC(vcc);
+ struct eni_tx *tx;
+ unsigned long size;
+ void __iomem *mem;
+ int rate,ubr,unlimited,new_tx;
+ int pre,res,order;
+ int error;
+
+ rate = atm_pcr_goal(txtp);
+ ubr = txtp->traffic_class == ATM_UBR;
+ unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR ||
+ rate >= ATM_OC3_PCR);
+ if (!unlimited) {
+ size = txtp->max_sdu*eni_dev->tx_mult/100;
+ if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <=
+ MID_MAX_BUF_SIZE)
+ size = MID_MAX_BUF_SIZE;
+ }
+ else {
+ if (eni_dev->ubr) {
+ eni_vcc->tx = eni_dev->ubr;
+ txtp->pcr = ATM_OC3_PCR;
+ return 0;
+ }
+ size = UBR_BUFFER;
+ }
+ new_tx = !eni_vcc->tx;
+ mem = NULL; /* for gcc */
+ if (!new_tx) tx = eni_vcc->tx;
+ else {
+ mem = eni_alloc_mem(eni_dev,&size);
+ if (!mem) return -ENOBUFS;
+ tx = alloc_tx(eni_dev,unlimited);
+ if (!tx) {
+ eni_free_mem(eni_dev,mem,size);
+ return -EBUSY;
+ }
+ DPRINTK("got chan %d\n",tx->index);
+ tx->reserved = tx->shaping = 0;
+ tx->send = mem;
+ tx->words = size >> 2;
+ skb_queue_head_init(&tx->backlog);
+ for (order = 0; size > (1 << (order+10)); order++);
+ eni_out((order << MID_SIZE_SHIFT) |
+ ((tx->send-eni_dev->ram) >> (MID_LOC_SKIP+2)),
+ MID_TX_PLACE(tx->index));
+ tx->tx_pos = eni_in(MID_TX_DESCRSTART(tx->index)) &
+ MID_DESCR_START;
+ }
+ error = comp_tx(eni_dev,&rate,tx->reserved,&pre,&res,unlimited);
+ if (!error && txtp->min_pcr > rate) error = -EINVAL;
+ if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR &&
+ txtp->max_pcr < rate) error = -EINVAL;
+ if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved)
+ error = -EINVAL;
+ if (!error && set_rsv && !set_shp && rate < tx->shaping)
+ error = -EINVAL;
+ if (!error && !set_rsv && rate > tx->reserved && !ubr)
+ error = -EINVAL;
+ if (error) {
+ if (new_tx) {
+ tx->send = NULL;
+ eni_free_mem(eni_dev,mem,size);
+ }
+ return error;
+ }
+ txtp->pcr = rate;
+ if (set_rsv && !ubr) {
+ eni_dev->tx_bw += tx->reserved;
+ tx->reserved = rate;
+ eni_dev->tx_bw -= rate;
+ }
+ if (set_shp || (unlimited && new_tx)) {
+ if (unlimited && new_tx) eni_dev->ubr = tx;
+ tx->prescaler = pre;
+ tx->resolution = res;
+ tx->shaping = rate;
+ }
+ if (set_shp) eni_vcc->tx = tx;
+ DPRINTK("rsv %d shp %d\n",tx->reserved,tx->shaping);
+ return 0;
+}
+
+
+static int open_tx_first(struct atm_vcc *vcc)
+{
+ ENI_VCC(vcc)->tx = NULL;
+ if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
+ ENI_VCC(vcc)->txing = 0;
+ return reserve_or_set_tx(vcc,&vcc->qos.txtp,1,1);
+}
+
+
+static int open_tx_second(struct atm_vcc *vcc)
+{
+ return 0; /* nothing to do */
+}
+
+
+static void close_tx(struct atm_vcc *vcc)
+{
+ DECLARE_WAITQUEUE(wait,current);
+ struct eni_dev *eni_dev;
+ struct eni_vcc *eni_vcc;
+
+ eni_vcc = ENI_VCC(vcc);
+ if (!eni_vcc->tx) return;
+ eni_dev = ENI_DEV(vcc->dev);
+ /* wait for TX queue to drain */
+ DPRINTK("eni_close: waiting for TX ...\n");
+ add_wait_queue(&eni_dev->tx_wait,&wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ for (;;) {
+ int txing;
+
+ tasklet_disable(&eni_dev->task);
+ txing = skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing;
+ tasklet_enable(&eni_dev->task);
+ if (!txing) break;
+ DPRINTK("%d TX left\n",eni_vcc->txing);
+ schedule();
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&eni_dev->tx_wait,&wait);
+ if (eni_vcc->tx != eni_dev->ubr) {
+ /*
+ * Looping a few times in here is probably far cheaper than
+ * keeping track of TX completions all the time, so let's poll
+ * a bit ...
+ */
+ while (eni_in(MID_TX_RDPTR(eni_vcc->tx->index)) !=
+ eni_in(MID_TX_DESCRSTART(eni_vcc->tx->index)))
+ schedule();
+ eni_free_mem(eni_dev,eni_vcc->tx->send,eni_vcc->tx->words << 2);
+ eni_vcc->tx->send = NULL;
+ eni_dev->tx_bw += eni_vcc->tx->reserved;
+ }
+ eni_vcc->tx = NULL;
+}
+
+
+static int start_tx(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+ int i;
+
+ eni_dev = ENI_DEV(dev);
+ eni_dev->lost = 0;
+ eni_dev->tx_bw = ATM_OC3_PCR;
+ eni_dev->tx_mult = DEFAULT_TX_MULT;
+ init_waitqueue_head(&eni_dev->tx_wait);
+ eni_dev->ubr = NULL;
+ skb_queue_head_init(&eni_dev->tx_queue);
+ eni_out(0,MID_DMA_WR_TX);
+ for (i = 0; i < NR_CHAN; i++) {
+ eni_dev->tx[i].send = NULL;
+ eni_dev->tx[i].index = i;
+ }
+ return 0;
+}
+
+
+/*--------------------------------- common ----------------------------------*/
+
+
+#if 0 /* may become useful again when tuning things */
+
+static void foo(void)
+{
+printk(KERN_INFO
+ "tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n"
+ "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n",
+ tx_complete,dma_complete,queued,requeued,submitted,backlogged,
+ rx_enqueued,rx_dequeued,putting,pushed);
+if (eni_boards) printk(KERN_INFO "loss: %ld\n",ENI_DEV(eni_boards)->lost);
+}
+
+#endif
+
+
+static void bug_int(struct atm_dev *dev,unsigned long reason)
+{
+ DPRINTK(">bug_int\n");
+ if (reason & MID_DMA_ERR_ACK)
+ printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA "
+ "error\n",dev->number);
+ if (reason & MID_TX_IDENT_MISM)
+ printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - ident "
+ "mismatch\n",dev->number);
+ if (reason & MID_TX_DMA_OVFL)
+ printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA "
+ "overflow\n",dev->number);
+ EVENT("---dump ends here---\n",0,0);
+ printk(KERN_NOTICE "---recent events---\n");
+ event_dump();
+}
+
+
+static irqreturn_t eni_int(int irq,void *dev_id)
+{
+ struct atm_dev *dev;
+ struct eni_dev *eni_dev;
+ u32 reason;
+
+ DPRINTK(">eni_int\n");
+ dev = dev_id;
+ eni_dev = ENI_DEV(dev);
+ reason = eni_in(MID_ISA);
+ DPRINTK(DEV_LABEL ": int 0x%lx\n",(unsigned long) reason);
+ /*
+ * Must handle these two right now, because reading ISA doesn't clear
+ * them, so they re-occur and we never make it to the tasklet. Since
+ * they're rare, we don't mind the occasional invocation of eni_tasklet
+ * with eni_dev->events == 0.
+ */
+ if (reason & MID_STAT_OVFL) {
+ EVENT("stat overflow\n",0,0);
+ eni_dev->lost += eni_in(MID_STAT) & MID_OVFL_TRASH;
+ }
+ if (reason & MID_SUNI_INT) {
+ EVENT("SUNI int\n",0,0);
+ dev->phy->interrupt(dev);
+#if 0
+ foo();
+#endif
+ }
+ spin_lock(&eni_dev->lock);
+ eni_dev->events |= reason;
+ spin_unlock(&eni_dev->lock);
+ tasklet_schedule(&eni_dev->task);
+ return IRQ_HANDLED;
+}
+
+
+static void eni_tasklet(unsigned long data)
+{
+ struct atm_dev *dev = (struct atm_dev *) data;
+ struct eni_dev *eni_dev = ENI_DEV(dev);
+ unsigned long flags;
+ u32 events;
+
+ DPRINTK("eni_tasklet (dev %p)\n",dev);
+ spin_lock_irqsave(&eni_dev->lock,flags);
+ events = xchg(&eni_dev->events,0);
+ spin_unlock_irqrestore(&eni_dev->lock,flags);
+ if (events & MID_RX_DMA_COMPLETE) {
+ EVENT("INT: RX DMA complete, starting dequeue_rx\n",0,0);
+ dequeue_rx(dev);
+ EVENT("dequeue_rx done, starting poll_rx\n",0,0);
+ poll_rx(dev);
+ EVENT("poll_rx done\n",0,0);
+ /* poll_tx ? */
+ }
+ if (events & MID_SERVICE) {
+ EVENT("INT: service, starting get_service\n",0,0);
+ get_service(dev);
+ EVENT("get_service done, starting poll_rx\n",0,0);
+ poll_rx(dev);
+ EVENT("poll_rx done\n",0,0);
+ }
+ if (events & MID_TX_DMA_COMPLETE) {
+ EVENT("INT: TX DMA COMPLETE\n",0,0);
+ dequeue_tx(dev);
+ }
+ if (events & MID_TX_COMPLETE) {
+ EVENT("INT: TX COMPLETE\n",0,0);
+tx_complete++;
+ wake_up(&eni_dev->tx_wait);
+ /* poll_rx ? */
+ }
+ if (events & (MID_DMA_ERR_ACK | MID_TX_IDENT_MISM | MID_TX_DMA_OVFL)) {
+ EVENT("bug interrupt\n",0,0);
+ bug_int(dev,events);
+ }
+ poll_tx(dev);
+}
+
+
+/*--------------------------------- entries ---------------------------------*/
+
+
+static char * const media_name[] = {
+ "MMF", "SMF", "MMF", "03?", /* 0- 3 */
+ "UTP", "05?", "06?", "07?", /* 4- 7 */
+ "TAXI","09?", "10?", "11?", /* 8-11 */
+ "12?", "13?", "14?", "15?", /* 12-15 */
+ "MMF", "SMF", "18?", "19?", /* 16-19 */
+ "UTP", "21?", "22?", "23?", /* 20-23 */
+ "24?", "25?", "26?", "27?", /* 24-27 */
+ "28?", "29?", "30?", "31?" /* 28-31 */
+};
+
+
+#define SET_SEPROM \
+ ({ if (!error && !pci_error) { \
+ pci_error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,tonga); \
+ udelay(10); /* 10 usecs */ \
+ } })
+#define GET_SEPROM \
+ ({ if (!error && !pci_error) { \
+ pci_error = pci_read_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,&tonga); \
+ udelay(10); /* 10 usecs */ \
+ } })
+
+
+static int get_esi_asic(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+ unsigned char tonga;
+ int error,failed,pci_error;
+ int address,i,j;
+
+ eni_dev = ENI_DEV(dev);
+ error = pci_error = 0;
+ tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK;
+ SET_SEPROM;
+ for (i = 0; i < ESI_LEN && !error && !pci_error; i++) {
+ /* start operation */
+ tonga |= SEPROM_DATA;
+ SET_SEPROM;
+ tonga |= SEPROM_CLK;
+ SET_SEPROM;
+ tonga &= ~SEPROM_DATA;
+ SET_SEPROM;
+ tonga &= ~SEPROM_CLK;
+ SET_SEPROM;
+ /* send address */
+ address = ((i+SEPROM_ESI_BASE) << 1)+1;
+ for (j = 7; j >= 0; j--) {
+ tonga = (address >> j) & 1 ? tonga | SEPROM_DATA :
+ tonga & ~SEPROM_DATA;
+ SET_SEPROM;
+ tonga |= SEPROM_CLK;
+ SET_SEPROM;
+ tonga &= ~SEPROM_CLK;
+ SET_SEPROM;
+ }
+ /* get ack */
+ tonga |= SEPROM_DATA;
+ SET_SEPROM;
+ tonga |= SEPROM_CLK;
+ SET_SEPROM;
+ GET_SEPROM;
+ failed = tonga & SEPROM_DATA;
+ tonga &= ~SEPROM_CLK;
+ SET_SEPROM;
+ tonga |= SEPROM_DATA;
+ SET_SEPROM;
+ if (failed) error = -EIO;
+ else {
+ dev->esi[i] = 0;
+ for (j = 7; j >= 0; j--) {
+ dev->esi[i] <<= 1;
+ tonga |= SEPROM_DATA;
+ SET_SEPROM;
+ tonga |= SEPROM_CLK;
+ SET_SEPROM;
+ GET_SEPROM;
+ if (tonga & SEPROM_DATA) dev->esi[i] |= 1;
+ tonga &= ~SEPROM_CLK;
+ SET_SEPROM;
+ tonga |= SEPROM_DATA;
+ SET_SEPROM;
+ }
+ /* get ack */
+ tonga |= SEPROM_DATA;
+ SET_SEPROM;
+ tonga |= SEPROM_CLK;
+ SET_SEPROM;
+ GET_SEPROM;
+ if (!(tonga & SEPROM_DATA)) error = -EIO;
+ tonga &= ~SEPROM_CLK;
+ SET_SEPROM;
+ tonga |= SEPROM_DATA;
+ SET_SEPROM;
+ }
+ /* stop operation */
+ tonga &= ~SEPROM_DATA;
+ SET_SEPROM;
+ tonga |= SEPROM_CLK;
+ SET_SEPROM;
+ tonga |= SEPROM_DATA;
+ SET_SEPROM;
+ }
+ if (pci_error) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): error reading ESI "
+ "(0x%02x)\n",dev->number,pci_error);
+ error = -EIO;
+ }
+ return error;
+}
+
+
+#undef SET_SEPROM
+#undef GET_SEPROM
+
+
+static int get_esi_fpga(struct atm_dev *dev, void __iomem *base)
+{
+ void __iomem *mac_base;
+ int i;
+
+ mac_base = base+EPROM_SIZE-sizeof(struct midway_eprom);
+ for (i = 0; i < ESI_LEN; i++) dev->esi[i] = readb(mac_base+(i^3));
+ return 0;
+}
+
+
+static int eni_do_init(struct atm_dev *dev)
+{
+ struct midway_eprom __iomem *eprom;
+ struct eni_dev *eni_dev;
+ struct pci_dev *pci_dev;
+ unsigned long real_base;
+ void __iomem *base;
+ int error,i,last;
+
+ DPRINTK(">eni_init\n");
+ dev->ci_range.vpi_bits = 0;
+ dev->ci_range.vci_bits = NR_VCI_LD;
+ dev->link_rate = ATM_OC3_PCR;
+ eni_dev = ENI_DEV(dev);
+ pci_dev = eni_dev->pci_dev;
+ real_base = pci_resource_start(pci_dev, 0);
+ eni_dev->irq = pci_dev->irq;
+ if ((error = pci_write_config_word(pci_dev,PCI_COMMAND,
+ PCI_COMMAND_MEMORY |
+ (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory "
+ "(0x%02x)\n",dev->number,error);
+ return -EIO;
+ }
+ printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%lx,irq=%d,",
+ dev->number,pci_dev->revision,real_base,eni_dev->irq);
+ if (!(base = ioremap_nocache(real_base,MAP_MAX_SIZE))) {
+ printk("\n");
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page "
+ "mapping\n",dev->number);
+ return error;
+ }
+ eni_dev->ioaddr = base;
+ eni_dev->base_diff = real_base - (unsigned long) base;
+ /* id may not be present in ASIC Tonga boards - check this @@@ */
+ if (!eni_dev->asic) {
+ eprom = (base+EPROM_SIZE-sizeof(struct midway_eprom));
+ if (readl(&eprom->magic) != ENI155_MAGIC) {
+ printk("\n");
+ printk(KERN_ERR DEV_LABEL
+ "(itf %d): bad magic - expected 0x%x, got 0x%x\n",
+ dev->number, ENI155_MAGIC,
+ (unsigned)readl(&eprom->magic));
+ error = -EINVAL;
+ goto unmap;
+ }
+ }
+ eni_dev->phy = base+PHY_BASE;
+ eni_dev->reg = base+REG_BASE;
+ eni_dev->ram = base+RAM_BASE;
+ last = MAP_MAX_SIZE-RAM_BASE;
+ for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
+ writel(0x55555555,eni_dev->ram+i);
+ if (readl(eni_dev->ram+i) != 0x55555555) last = i;
+ else {
+ writel(0xAAAAAAAA,eni_dev->ram+i);
+ if (readl(eni_dev->ram+i) != 0xAAAAAAAA) last = i;
+ else writel(i,eni_dev->ram+i);
+ }
+ }
+ for (i = 0; i < last; i += RAM_INCREMENT)
+ if (readl(eni_dev->ram+i) != i) break;
+ eni_dev->mem = i;
+ memset_io(eni_dev->ram,0,eni_dev->mem);
+ /* TODO: should shrink allocation now */
+ printk("mem=%dkB (",eni_dev->mem >> 10);
+ /* TODO: check for non-SUNI, check for TAXI ? */
+ if (!(eni_in(MID_RES_ID_MCON) & 0x200) != !eni_dev->asic) {
+ printk(")\n");
+ printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%x\n",
+ dev->number,(unsigned) eni_in(MID_RES_ID_MCON));
+ error = -EINVAL;
+ goto unmap;
+ }
+ error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base);
+ if (error)
+ goto unmap;
+ for (i = 0; i < ESI_LEN; i++)
+ printk("%s%02X",i ? "-" : "",dev->esi[i]);
+ printk(")\n");
+ printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number,
+ eni_in(MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA",
+ media_name[eni_in(MID_RES_ID_MCON) & DAUGTHER_ID]);
+
+ error = suni_init(dev);
+ if (error)
+ goto unmap;
+out:
+ return error;
+unmap:
+ iounmap(base);
+ goto out;
+}
+
+static void eni_do_release(struct atm_dev *dev)
+{
+ struct eni_dev *ed = ENI_DEV(dev);
+
+ dev->phy->stop(dev);
+ dev->phy = NULL;
+ iounmap(ed->ioaddr);
+}
+
+static int eni_start(struct atm_dev *dev)
+{
+ struct eni_dev *eni_dev;
+
+ void __iomem *buf;
+ unsigned long buffer_mem;
+ int error;
+
+ DPRINTK(">eni_start\n");
+ eni_dev = ENI_DEV(dev);
+ if (request_irq(eni_dev->irq,&eni_int,IRQF_SHARED,DEV_LABEL,dev)) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
+ dev->number,eni_dev->irq);
+ error = -EAGAIN;
+ goto out;
+ }
+ pci_set_master(eni_dev->pci_dev);
+ if ((error = pci_write_config_word(eni_dev->pci_dev,PCI_COMMAND,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+"
+ "master (0x%02x)\n",dev->number,error);
+ goto free_irq;
+ }
+ if ((error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,
+ END_SWAP_DMA))) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap "
+ "(0x%02x)\n",dev->number,error);
+ goto free_irq;
+ }
+ /* determine addresses of internal tables */
+ eni_dev->vci = eni_dev->ram;
+ eni_dev->rx_dma = eni_dev->ram+NR_VCI*16;
+ eni_dev->tx_dma = eni_dev->rx_dma+NR_DMA_RX*8;
+ eni_dev->service = eni_dev->tx_dma+NR_DMA_TX*8;
+ buf = eni_dev->service+NR_SERVICE*4;
+ DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n",
+ eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma,
+ eni_dev->service,buf);
+ spin_lock_init(&eni_dev->lock);
+ tasklet_init(&eni_dev->task,eni_tasklet,(unsigned long) dev);
+ eni_dev->events = 0;
+ /* initialize memory management */
+ buffer_mem = eni_dev->mem - (buf - eni_dev->ram);
+ eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2;
+ eni_dev->free_list = kmalloc(
+ sizeof(struct eni_free)*(eni_dev->free_list_size+1),GFP_KERNEL);
+ if (!eni_dev->free_list) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
+ dev->number);
+ error = -ENOMEM;
+ goto free_irq;
+ }
+ eni_dev->free_len = 0;
+ eni_put_free(eni_dev,buf,buffer_mem);
+ memset_io(eni_dev->vci,0,16*NR_VCI); /* clear VCI table */
+ /*
+ * byte_addr free (k)
+ * 0x00000000 512 VCI table
+ * 0x00004000 496 RX DMA
+ * 0x00005000 492 TX DMA
+ * 0x00006000 488 service list
+ * 0x00007000 484 buffers
+ * 0x00080000 0 end (512kB)
+ */
+ eni_out(0xffffffff,MID_IE);
+ error = start_tx(dev);
+ if (error) goto free_list;
+ error = start_rx(dev);
+ if (error) goto free_list;
+ error = dev->phy->start(dev);
+ if (error) goto free_list;
+ eni_out(eni_in(MID_MC_S) | (1 << MID_INT_SEL_SHIFT) |
+ MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE,
+ MID_MC_S);
+ /* Tonga uses SBus INTReq1 */
+ (void) eni_in(MID_ISA); /* clear Midway interrupts */
+ return 0;
+
+free_list:
+ kfree(eni_dev->free_list);
+
+free_irq:
+ free_irq(eni_dev->irq, dev);
+
+out:
+ return error;
+}
+
+
+static void eni_close(struct atm_vcc *vcc)
+{
+ DPRINTK(">eni_close\n");
+ if (!ENI_VCC(vcc)) return;
+ clear_bit(ATM_VF_READY,&vcc->flags);
+ close_rx(vcc);
+ close_tx(vcc);
+ DPRINTK("eni_close: done waiting\n");
+ /* deallocate memory */
+ kfree(ENI_VCC(vcc));
+ vcc->dev_data = NULL;
+ clear_bit(ATM_VF_ADDR,&vcc->flags);
+ /*foo();*/
+}
+
+
+static int eni_open(struct atm_vcc *vcc)
+{
+ struct eni_vcc *eni_vcc;
+ int error;
+ short vpi = vcc->vpi;
+ int vci = vcc->vci;
+
+ DPRINTK(">eni_open\n");
+ EVENT("eni_open\n",0,0);
+ if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))
+ vcc->dev_data = NULL;
+ if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
+ set_bit(ATM_VF_ADDR,&vcc->flags);
+ if (vcc->qos.aal != ATM_AAL0 && vcc->qos.aal != ATM_AAL5)
+ return -EINVAL;
+ DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
+ vcc->vci);
+ if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
+ eni_vcc = kmalloc(sizeof(struct eni_vcc),GFP_KERNEL);
+ if (!eni_vcc) return -ENOMEM;
+ vcc->dev_data = eni_vcc;
+ eni_vcc->tx = NULL; /* for eni_close after open_rx */
+ if ((error = open_rx_first(vcc))) {
+ eni_close(vcc);
+ return error;
+ }
+ if ((error = open_tx_first(vcc))) {
+ eni_close(vcc);
+ return error;
+ }
+ }
+ if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
+ if ((error = open_rx_second(vcc))) {
+ eni_close(vcc);
+ return error;
+ }
+ if ((error = open_tx_second(vcc))) {
+ eni_close(vcc);
+ return error;
+ }
+ set_bit(ATM_VF_READY,&vcc->flags);
+ /* should power down SUNI while !ref_count @@@ */
+ return 0;
+}
+
+
+static int eni_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs)
+{
+ struct eni_dev *eni_dev = ENI_DEV(vcc->dev);
+ struct eni_tx *tx = ENI_VCC(vcc)->tx;
+ struct sk_buff *skb;
+ int error,rate,rsv,shp;
+
+ if (qos->txtp.traffic_class == ATM_NONE) return 0;
+ if (tx == eni_dev->ubr) return -EBADFD;
+ rate = atm_pcr_goal(&qos->txtp);
+ if (rate < 0) rate = -rate;
+ rsv = shp = 0;
+ if ((flgs & ATM_MF_DEC_RSV) && rate && rate < tx->reserved) rsv = 1;
+ if ((flgs & ATM_MF_INC_RSV) && (!rate || rate > tx->reserved)) rsv = 1;
+ if ((flgs & ATM_MF_DEC_SHP) && rate && rate < tx->shaping) shp = 1;
+ if ((flgs & ATM_MF_INC_SHP) && (!rate || rate > tx->shaping)) shp = 1;
+ if (!rsv && !shp) return 0;
+ error = reserve_or_set_tx(vcc,&qos->txtp,rsv,shp);
+ if (error) return error;
+ if (shp && !(flgs & ATM_MF_IMMED)) return 0;
+ /*
+ * Walk through the send buffer and patch the rate information in all
+ * segmentation buffer descriptors of this VCC.
+ */
+ tasklet_disable(&eni_dev->task);
+ skb_queue_walk(&eni_dev->tx_queue, skb) {
+ void __iomem *dsc;
+
+ if (ATM_SKB(skb)->vcc != vcc) continue;
+ dsc = tx->send+ENI_PRV_POS(skb)*4;
+ writel((readl(dsc) & ~(MID_SEG_RATE | MID_SEG_PR)) |
+ (tx->prescaler << MID_SEG_PR_SHIFT) |
+ (tx->resolution << MID_SEG_RATE_SHIFT), dsc);
+ }
+ tasklet_enable(&eni_dev->task);
+ return 0;
+}
+
+
+static int eni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
+{
+ struct eni_dev *eni_dev = ENI_DEV(dev);
+
+ if (cmd == ENI_MEMDUMP) {
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ printk(KERN_WARNING "Please use /proc/atm/" DEV_LABEL ":%d "
+ "instead of obsolete ioctl ENI_MEMDUMP\n",dev->number);
+ dump(dev);
+ return 0;
+ }
+ if (cmd == ENI_SETMULT) {
+ struct eni_multipliers mult;
+
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ if (copy_from_user(&mult, arg,
+ sizeof(struct eni_multipliers)))
+ return -EFAULT;
+ if ((mult.tx && mult.tx <= 100) || (mult.rx &&mult.rx <= 100) ||
+ mult.tx > 65536 || mult.rx > 65536)
+ return -EINVAL;
+ if (mult.tx) eni_dev->tx_mult = mult.tx;
+ if (mult.rx) eni_dev->rx_mult = mult.rx;
+ return 0;
+ }
+ if (cmd == ATM_SETCIRANGE) {
+ struct atm_cirange ci;
+
+ if (copy_from_user(&ci, arg,sizeof(struct atm_cirange)))
+ return -EFAULT;
+ if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) &&
+ (ci.vci_bits == NR_VCI_LD || ci.vpi_bits == ATM_CI_MAX))
+ return 0;
+ return -EINVAL;
+ }
+ if (!dev->phy->ioctl) return -ENOIOCTLCMD;
+ return dev->phy->ioctl(dev,cmd,arg);
+}
+
+
+static int eni_getsockopt(struct atm_vcc *vcc,int level,int optname,
+ void __user *optval,int optlen)
+{
+ return -EINVAL;
+}
+
+
+static int eni_setsockopt(struct atm_vcc *vcc,int level,int optname,
+ void __user *optval,unsigned int optlen)
+{
+ return -EINVAL;
+}
+
+
+static int eni_send(struct atm_vcc *vcc,struct sk_buff *skb)
+{
+ enum enq_res res;
+
+ DPRINTK(">eni_send\n");
+ if (!ENI_VCC(vcc)->tx) {
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+ if (!skb) {
+ printk(KERN_CRIT "!skb in eni_send ?\n");
+ if (vcc->pop) vcc->pop(vcc,skb);
+ return -EINVAL;
+ }
+ if (vcc->qos.aal == ATM_AAL0) {
+ if (skb->len != ATM_CELL_SIZE-1) {
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+ *(u32 *) skb->data = htonl(*(u32 *) skb->data);
+ }
+submitted++;
+ ATM_SKB(skb)->vcc = vcc;
+ tasklet_disable(&ENI_DEV(vcc->dev)->task);
+ res = do_tx(skb);
+ tasklet_enable(&ENI_DEV(vcc->dev)->task);
+ if (res == enq_ok) return 0;
+ skb_queue_tail(&ENI_VCC(vcc)->tx->backlog,skb);
+backlogged++;
+ tasklet_schedule(&ENI_DEV(vcc->dev)->task);
+ return 0;
+}
+
+static void eni_phy_put(struct atm_dev *dev,unsigned char value,
+ unsigned long addr)
+{
+ writel(value,ENI_DEV(dev)->phy+addr*4);
+}
+
+
+
+static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr)
+{
+ return readl(ENI_DEV(dev)->phy+addr*4);
+}
+
+
+static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page)
+{
+ struct sock *s;
+ static const char *signal[] = { "LOST","unknown","okay" };
+ struct eni_dev *eni_dev = ENI_DEV(dev);
+ struct atm_vcc *vcc;
+ int left,i;
+
+ left = *pos;
+ if (!left)
+ return sprintf(page,DEV_LABEL "(itf %d) signal %s, %dkB, "
+ "%d cps remaining\n",dev->number,signal[(int) dev->signal],
+ eni_dev->mem >> 10,eni_dev->tx_bw);
+ if (!--left)
+ return sprintf(page,"%4sBursts: TX"
+#if !defined(CONFIG_ATM_ENI_BURST_TX_16W) && \
+ !defined(CONFIG_ATM_ENI_BURST_TX_8W) && \
+ !defined(CONFIG_ATM_ENI_BURST_TX_4W) && \
+ !defined(CONFIG_ATM_ENI_BURST_TX_2W)
+ " none"
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_TX_16W
+ " 16W"
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_TX_8W
+ " 8W"
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_TX_4W
+ " 4W"
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_TX_2W
+ " 2W"
+#endif
+ ", RX"
+#if !defined(CONFIG_ATM_ENI_BURST_RX_16W) && \
+ !defined(CONFIG_ATM_ENI_BURST_RX_8W) && \
+ !defined(CONFIG_ATM_ENI_BURST_RX_4W) && \
+ !defined(CONFIG_ATM_ENI_BURST_RX_2W)
+ " none"
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_RX_16W
+ " 16W"
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_RX_8W
+ " 8W"
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_RX_4W
+ " 4W"
+#endif
+#ifdef CONFIG_ATM_ENI_BURST_RX_2W
+ " 2W"
+#endif
+#ifndef CONFIG_ATM_ENI_TUNE_BURST
+ " (default)"
+#endif
+ "\n","");
+ if (!--left)
+ return sprintf(page,"%4sBuffer multipliers: tx %d%%, rx %d%%\n",
+ "",eni_dev->tx_mult,eni_dev->rx_mult);
+ for (i = 0; i < NR_CHAN; i++) {
+ struct eni_tx *tx = eni_dev->tx+i;
+
+ if (!tx->send) continue;
+ if (!--left) {
+ return sprintf(page, "tx[%d]: 0x%lx-0x%lx "
+ "(%6ld bytes), rsv %d cps, shp %d cps%s\n",i,
+ (unsigned long) (tx->send - eni_dev->ram),
+ tx->send-eni_dev->ram+tx->words*4-1,tx->words*4,
+ tx->reserved,tx->shaping,
+ tx == eni_dev->ubr ? " (UBR)" : "");
+ }
+ if (--left) continue;
+ return sprintf(page,"%10sbacklog %u packets\n","",
+ skb_queue_len(&tx->backlog));
+ }
+ read_lock(&vcc_sklist_lock);
+ for(i = 0; i < VCC_HTABLE_SIZE; ++i) {
+ struct hlist_head *head = &vcc_hash[i];
+
+ sk_for_each(s, head) {
+ struct eni_vcc *eni_vcc;
+ int length;
+
+ vcc = atm_sk(s);
+ if (vcc->dev != dev)
+ continue;
+ eni_vcc = ENI_VCC(vcc);
+ if (--left) continue;
+ length = sprintf(page,"vcc %4d: ",vcc->vci);
+ if (eni_vcc->rx) {
+ length += sprintf(page+length, "0x%lx-0x%lx "
+ "(%6ld bytes)",
+ (unsigned long) (eni_vcc->recv - eni_dev->ram),
+ eni_vcc->recv-eni_dev->ram+eni_vcc->words*4-1,
+ eni_vcc->words*4);
+ if (eni_vcc->tx) length += sprintf(page+length,", ");
+ }
+ if (eni_vcc->tx)
+ length += sprintf(page+length,"tx[%d], txing %d bytes",
+ eni_vcc->tx->index,eni_vcc->txing);
+ page[length] = '\n';
+ read_unlock(&vcc_sklist_lock);
+ return length+1;
+ }
+ }
+ read_unlock(&vcc_sklist_lock);
+ for (i = 0; i < eni_dev->free_len; i++) {
+ struct eni_free *fe = eni_dev->free_list+i;
+ unsigned long offset;
+
+ if (--left) continue;
+ offset = (unsigned long) eni_dev->ram+eni_dev->base_diff;
+ return sprintf(page,"free %p-%p (%6d bytes)\n",
+ fe->start-offset,fe->start-offset+(1 << fe->order)-1,
+ 1 << fe->order);
+ }
+ return 0;
+}
+
+
+static const struct atmdev_ops ops = {
+ .open = eni_open,
+ .close = eni_close,
+ .ioctl = eni_ioctl,
+ .getsockopt = eni_getsockopt,
+ .setsockopt = eni_setsockopt,
+ .send = eni_send,
+ .phy_put = eni_phy_put,
+ .phy_get = eni_phy_get,
+ .change_qos = eni_change_qos,
+ .proc_read = eni_proc_read
+};
+
+
+static int eni_init_one(struct pci_dev *pci_dev,
+ const struct pci_device_id *ent)
+{
+ struct atm_dev *dev;
+ struct eni_dev *eni_dev;
+ struct eni_zero *zero;
+ int rc;
+
+ rc = pci_enable_device(pci_dev);
+ if (rc < 0)
+ goto out;
+
+ rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32));
+ if (rc < 0)
+ goto out;
+
+ rc = -ENOMEM;
+ eni_dev = kmalloc(sizeof(struct eni_dev), GFP_KERNEL);
+ if (!eni_dev)
+ goto err_disable;
+
+ zero = &eni_dev->zero;
+ zero->addr = dma_alloc_coherent(&pci_dev->dev,
+ ENI_ZEROES_SIZE, &zero->dma, GFP_KERNEL);
+ if (!zero->addr)
+ goto err_kfree;
+
+ dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &ops, -1, NULL);
+ if (!dev)
+ goto err_free_consistent;
+
+ dev->dev_data = eni_dev;
+ pci_set_drvdata(pci_dev, dev);
+ eni_dev->pci_dev = pci_dev;
+ eni_dev->asic = ent->driver_data;
+
+ rc = eni_do_init(dev);
+ if (rc < 0)
+ goto err_unregister;
+
+ rc = eni_start(dev);
+ if (rc < 0)
+ goto err_eni_release;
+
+ eni_dev->more = eni_boards;
+ eni_boards = dev;
+out:
+ return rc;
+
+err_eni_release:
+ eni_do_release(dev);
+err_unregister:
+ atm_dev_deregister(dev);
+err_free_consistent:
+ dma_free_coherent(&pci_dev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma);
+err_kfree:
+ kfree(eni_dev);
+err_disable:
+ pci_disable_device(pci_dev);
+ goto out;
+}
+
+
+static struct pci_device_id eni_pci_tbl[] = {
+ { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_FPGA), 0 /* FPGA */ },
+ { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_ASIC), 1 /* ASIC */ },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci,eni_pci_tbl);
+
+
+static void eni_remove_one(struct pci_dev *pdev)
+{
+ struct atm_dev *dev = pci_get_drvdata(pdev);
+ struct eni_dev *ed = ENI_DEV(dev);
+ struct eni_zero *zero = &ed->zero;
+
+ eni_do_release(dev);
+ atm_dev_deregister(dev);
+ dma_free_coherent(&pdev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma);
+ kfree(ed);
+ pci_disable_device(pdev);
+}
+
+
+static struct pci_driver eni_driver = {
+ .name = DEV_LABEL,
+ .id_table = eni_pci_tbl,
+ .probe = eni_init_one,
+ .remove = eni_remove_one,
+};
+
+
+static int __init eni_init(void)
+{
+ struct sk_buff *skb; /* dummy for sizeof */
+
+ if (sizeof(skb->cb) < sizeof(struct eni_skb_prv)) {
+ printk(KERN_ERR "eni_detect: skb->cb is too small (%Zd < %Zd)\n",
+ sizeof(skb->cb),sizeof(struct eni_skb_prv));
+ return -EIO;
+ }
+ return pci_register_driver(&eni_driver);
+}
+
+
+module_init(eni_init);
+/* @@@ since exit routine not defined, this module can not be unloaded */
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h
new file mode 100644
index 000000000..565e53a5c
--- /dev/null
+++ b/drivers/atm/eni.h
@@ -0,0 +1,135 @@
+/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#ifndef DRIVER_ATM_ENI_H
+#define DRIVER_ATM_ENI_H
+
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/interrupt.h>
+#include <linux/sonet.h>
+#include <linux/skbuff.h>
+#include <linux/time.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+
+#include "midway.h"
+
+
+#define DEV_LABEL "eni"
+
+#define UBR_BUFFER (128*1024) /* UBR buffer size */
+
+#define RX_DMA_BUF 8 /* burst and skip a few things */
+#define TX_DMA_BUF 100 /* should be enough for 64 kB */
+
+#define DEFAULT_RX_MULT 300 /* max_sdu*3 */
+#define DEFAULT_TX_MULT 300 /* max_sdu*3 */
+
+#define ENI_ZEROES_SIZE 4 /* need that many DMA-able zero bytes */
+
+
+struct eni_free {
+ void __iomem *start; /* counting in bytes */
+ int order;
+};
+
+struct eni_tx {
+ void __iomem *send; /* base, 0 if unused */
+ int prescaler; /* shaping prescaler */
+ int resolution; /* shaping divider */
+ unsigned long tx_pos; /* current TX write position */
+ unsigned long words; /* size of TX queue */
+ int index; /* TX channel number */
+ int reserved; /* reserved peak cell rate */
+ int shaping; /* shaped peak cell rate */
+ struct sk_buff_head backlog; /* queue of waiting TX buffers */
+};
+
+struct eni_vcc {
+ int (*rx)(struct atm_vcc *vcc); /* RX function, NULL if none */
+ void __iomem *recv; /* receive buffer */
+ unsigned long words; /* its size in words */
+ unsigned long descr; /* next descriptor (RX) */
+ unsigned long rx_pos; /* current RX descriptor pos */
+ struct eni_tx *tx; /* TXer, NULL if none */
+ int rxing; /* number of pending PDUs */
+ int servicing; /* number of waiting VCs (0 or 1) */
+ int txing; /* number of pending TX bytes */
+ ktime_t timestamp; /* for RX timing */
+ struct atm_vcc *next; /* next pending RX */
+ struct sk_buff *last; /* last PDU being DMAed (used to carry
+ discard information) */
+};
+
+struct eni_dev {
+ /*-------------------------------- spinlock */
+ spinlock_t lock; /* sync with interrupt */
+ struct tasklet_struct task; /* tasklet for interrupt work */
+ u32 events; /* pending events */
+ /*-------------------------------- base pointers into Midway address
+ space */
+ void __iomem *ioaddr;
+ void __iomem *phy; /* PHY interface chip registers */
+ void __iomem *reg; /* register base */
+ void __iomem *ram; /* RAM base */
+ void __iomem *vci; /* VCI table */
+ void __iomem *rx_dma; /* RX DMA queue */
+ void __iomem *tx_dma; /* TX DMA queue */
+ void __iomem *service; /* service list */
+ /*-------------------------------- TX part */
+ struct eni_tx tx[NR_CHAN]; /* TX channels */
+ struct eni_tx *ubr; /* UBR channel */
+ struct sk_buff_head tx_queue; /* PDUs currently being TX DMAed*/
+ wait_queue_head_t tx_wait; /* for close */
+ int tx_bw; /* remaining bandwidth */
+ u32 dma[TX_DMA_BUF*2]; /* DMA request scratch area */
+ struct eni_zero { /* aligned "magic" zeroes */
+ u32 *addr;
+ dma_addr_t dma;
+ } zero;
+ int tx_mult; /* buffer size multiplier (percent) */
+ /*-------------------------------- RX part */
+ u32 serv_read; /* host service read index */
+ struct atm_vcc *fast,*last_fast;/* queues of VCCs with pending PDUs */
+ struct atm_vcc *slow,*last_slow;
+ struct atm_vcc **rx_map; /* for fast lookups */
+ struct sk_buff_head rx_queue; /* PDUs currently being RX-DMAed */
+ wait_queue_head_t rx_wait; /* for close */
+ int rx_mult; /* buffer size multiplier (percent) */
+ /*-------------------------------- statistics */
+ unsigned long lost; /* number of lost cells (RX) */
+ /*-------------------------------- memory management */
+ unsigned long base_diff; /* virtual-real base address */
+ int free_len; /* free list length */
+ struct eni_free *free_list; /* free list */
+ int free_list_size; /* maximum size of free list */
+ /*-------------------------------- ENI links */
+ struct atm_dev *more; /* other ENI devices */
+ /*-------------------------------- general information */
+ int mem; /* RAM on board (in bytes) */
+ int asic; /* PCI interface type, 0 for FPGA */
+ unsigned int irq; /* IRQ */
+ struct pci_dev *pci_dev; /* PCI stuff */
+};
+
+
+#define ENI_DEV(d) ((struct eni_dev *) (d)->dev_data)
+#define ENI_VCC(d) ((struct eni_vcc *) (d)->dev_data)
+
+
+struct eni_skb_prv {
+ struct atm_skb_data _; /* reserved */
+ unsigned long pos; /* position of next descriptor */
+ int size; /* PDU size in reassembly buffer */
+ dma_addr_t paddr; /* DMA handle */
+};
+
+#define ENI_PRV_SIZE(skb) (((struct eni_skb_prv *) (skb)->cb)->size)
+#define ENI_PRV_POS(skb) (((struct eni_skb_prv *) (skb)->cb)->pos)
+#define ENI_PRV_PADDR(skb) (((struct eni_skb_prv *) (skb)->cb)->paddr)
+
+#endif
diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c
new file mode 100644
index 000000000..82f2ae0d7
--- /dev/null
+++ b/drivers/atm/firestream.c
@@ -0,0 +1,2065 @@
+
+/* drivers/atm/firestream.c - FireStream 155 (MB86697) and
+ * FireStream 50 (MB86695) device driver
+ */
+
+/* Written & (C) 2000 by R.E.Wolff@BitWizard.nl
+ * Copied snippets from zatm.c by Werner Almesberger, EPFL LRC/ICA
+ * and ambassador.c Copyright (C) 1995-1999 Madge Networks Ltd
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian
+ system and in the file COPYING in the Linux kernel source.
+*/
+
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/poison.h>
+#include <linux/errno.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/ioport.h> /* for request_region */
+#include <linux/uio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/capability.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <asm/string.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+#include <asm/uaccess.h>
+#include <linux/wait.h>
+
+#include "firestream.h"
+
+static int loopback = 0;
+static int num=0x5a;
+
+/* According to measurements (but they look suspicious to me!) done in
+ * '97, 37% of the packets are one cell in size. So it pays to have
+ * buffers allocated at that size. A large jump in percentage of
+ * packets occurs at packets around 536 bytes in length. So it also
+ * pays to have those pre-allocated. Unfortunately, we can't fully
+ * take advantage of this as the majority of the packets is likely to
+ * be TCP/IP (As where obviously the measurement comes from) There the
+ * link would be opened with say a 1500 byte MTU, and we can't handle
+ * smaller buffers more efficiently than the larger ones. -- REW
+ */
+
+/* Due to the way Linux memory management works, specifying "576" as
+ * an allocation size here isn't going to help. They are allocated
+ * from 1024-byte regions anyway. With the size of the sk_buffs (quite
+ * large), it doesn't pay to allocate the smallest size (64) -- REW */
+
+/* This is all guesswork. Hard numbers to back this up or disprove this,
+ * are appreciated. -- REW */
+
+/* The last entry should be about 64k. However, the "buffer size" is
+ * passed to the chip in a 16 bit field. I don't know how "65536"
+ * would be interpreted. -- REW */
+
+#define NP FS_NR_FREE_POOLS
+static int rx_buf_sizes[NP] = {128, 256, 512, 1024, 2048, 4096, 16384, 65520};
+/* log2: 7 8 9 10 11 12 14 16 */
+
+#if 0
+static int rx_pool_sizes[NP] = {1024, 1024, 512, 256, 128, 64, 32, 32};
+#else
+/* debug */
+static int rx_pool_sizes[NP] = {128, 128, 128, 64, 64, 64, 32, 32};
+#endif
+/* log2: 10 10 9 8 7 6 5 5 */
+/* sumlog2: 17 18 18 18 18 18 19 21 */
+/* mem allocated: 128k 256k 256k 256k 256k 256k 512k 2M */
+/* tot mem: almost 4M */
+
+/* NP is shorter, so that it fits on a single line. */
+#undef NP
+
+
+/* Small hardware gotcha:
+
+ The FS50 CAM (VP/VC match registers) always take the lowest channel
+ number that matches. This is not a problem.
+
+ However, they also ignore whether the channel is enabled or
+ not. This means that if you allocate channel 0 to 1.2 and then
+ channel 1 to 0.0, then disabeling channel 0 and writing 0 to the
+ match channel for channel 0 will "steal" the traffic from channel
+ 1, even if you correctly disable channel 0.
+
+ Workaround:
+
+ - When disabling channels, write an invalid VP/VC value to the
+ match register. (We use 0xffffffff, which in the worst case
+ matches VP/VC = <maxVP>/<maxVC>, but I expect it not to match
+ anything as some "when not in use, program to 0" bits are now
+ programmed to 1...)
+
+ - Don't initialize the match registers to 0, as 0.0 is a valid
+ channel.
+*/
+
+
+/* Optimization hints and tips.
+
+ The FireStream chips are very capable of reducing the amount of
+ "interrupt-traffic" for the CPU. This driver requests an interrupt on EVERY
+ action. You could try to minimize this a bit.
+
+ Besides that, the userspace->kernel copy and the PCI bus are the
+ performance limiting issues for this driver.
+
+ You could queue up a bunch of outgoing packets without telling the
+ FireStream. I'm not sure that's going to win you much though. The
+ Linux layer won't tell us in advance when it's not going to give us
+ any more packets in a while. So this is tricky to implement right without
+ introducing extra delays.
+
+ -- REW
+ */
+
+
+
+
+/* The strings that define what the RX queue entry is all about. */
+/* Fujitsu: Please tell me which ones can have a pointer to a
+ freepool descriptor! */
+static char *res_strings[] = {
+ "RX OK: streaming not EOP",
+ "RX OK: streaming EOP",
+ "RX OK: Single buffer packet",
+ "RX OK: packet mode",
+ "RX OK: F4 OAM (end to end)",
+ "RX OK: F4 OAM (Segment)",
+ "RX OK: F5 OAM (end to end)",
+ "RX OK: F5 OAM (Segment)",
+ "RX OK: RM cell",
+ "RX OK: TRANSP cell",
+ "RX OK: TRANSPC cell",
+ "Unmatched cell",
+ "reserved 12",
+ "reserved 13",
+ "reserved 14",
+ "Unrecognized cell",
+ "reserved 16",
+ "reassemby abort: AAL5 abort",
+ "packet purged",
+ "packet ageing timeout",
+ "channel ageing timeout",
+ "calculated length error",
+ "programmed length limit error",
+ "aal5 crc32 error",
+ "oam transp or transpc crc10 error",
+ "reserved 25",
+ "reserved 26",
+ "reserved 27",
+ "reserved 28",
+ "reserved 29",
+ "reserved 30",
+ "reassembly abort: no buffers",
+ "receive buffer overflow",
+ "change in GFC",
+ "receive buffer full",
+ "low priority discard - no receive descriptor",
+ "low priority discard - missing end of packet",
+ "reserved 41",
+ "reserved 42",
+ "reserved 43",
+ "reserved 44",
+ "reserved 45",
+ "reserved 46",
+ "reserved 47",
+ "reserved 48",
+ "reserved 49",
+ "reserved 50",
+ "reserved 51",
+ "reserved 52",
+ "reserved 53",
+ "reserved 54",
+ "reserved 55",
+ "reserved 56",
+ "reserved 57",
+ "reserved 58",
+ "reserved 59",
+ "reserved 60",
+ "reserved 61",
+ "reserved 62",
+ "reserved 63",
+};
+
+static char *irq_bitname[] = {
+ "LPCO",
+ "DPCO",
+ "RBRQ0_W",
+ "RBRQ1_W",
+ "RBRQ2_W",
+ "RBRQ3_W",
+ "RBRQ0_NF",
+ "RBRQ1_NF",
+ "RBRQ2_NF",
+ "RBRQ3_NF",
+ "BFP_SC",
+ "INIT",
+ "INIT_ERR",
+ "USCEO",
+ "UPEC0",
+ "VPFCO",
+ "CRCCO",
+ "HECO",
+ "TBRQ_W",
+ "TBRQ_NF",
+ "CTPQ_E",
+ "GFC_C0",
+ "PCI_FTL",
+ "CSQ_W",
+ "CSQ_NF",
+ "EXT_INT",
+ "RXDMA_S"
+};
+
+
+#define PHY_EOF -1
+#define PHY_CLEARALL -2
+
+struct reginit_item {
+ int reg, val;
+};
+
+
+static struct reginit_item PHY_NTC_INIT[] = {
+ { PHY_CLEARALL, 0x40 },
+ { 0x12, 0x0001 },
+ { 0x13, 0x7605 },
+ { 0x1A, 0x0001 },
+ { 0x1B, 0x0005 },
+ { 0x38, 0x0003 },
+ { 0x39, 0x0006 }, /* changed here to make loopback */
+ { 0x01, 0x5262 },
+ { 0x15, 0x0213 },
+ { 0x00, 0x0003 },
+ { PHY_EOF, 0}, /* -1 signals end of list */
+};
+
+
+/* Safetyfeature: If the card interrupts more than this number of times
+ in a jiffy (1/100th of a second) then we just disable the interrupt and
+ print a message. This prevents the system from hanging.
+
+ 150000 packets per second is close to the limit a PC is going to have
+ anyway. We therefore have to disable this for production. -- REW */
+#undef IRQ_RATE_LIMIT // 100
+
+/* Interrupts work now. Unlike serial cards, ATM cards don't work all
+ that great without interrupts. -- REW */
+#undef FS_POLL_FREQ // 100
+
+/*
+ This driver can spew a whole lot of debugging output at you. If you
+ need maximum performance, you should disable the DEBUG define. To
+ aid in debugging in the field, I'm leaving the compile-time debug
+ features enabled, and disable them "runtime". That allows me to
+ instruct people with problems to enable debugging without requiring
+ them to recompile... -- REW
+*/
+#define DEBUG
+
+#ifdef DEBUG
+#define fs_dprintk(f, str...) if (fs_debug & f) printk (str)
+#else
+#define fs_dprintk(f, str...) /* nothing */
+#endif
+
+
+static int fs_keystream = 0;
+
+#ifdef DEBUG
+/* I didn't forget to set this to zero before shipping. Hit me with a stick
+ if you get this with the debug default not set to zero again. -- REW */
+static int fs_debug = 0;
+#else
+#define fs_debug 0
+#endif
+
+#ifdef MODULE
+#ifdef DEBUG
+module_param(fs_debug, int, 0644);
+#endif
+module_param(loopback, int, 0);
+module_param(num, int, 0);
+module_param(fs_keystream, int, 0);
+/* XXX Add rx_buf_sizes, and rx_pool_sizes As per request Amar. -- REW */
+#endif
+
+
+#define FS_DEBUG_FLOW 0x00000001
+#define FS_DEBUG_OPEN 0x00000002
+#define FS_DEBUG_QUEUE 0x00000004
+#define FS_DEBUG_IRQ 0x00000008
+#define FS_DEBUG_INIT 0x00000010
+#define FS_DEBUG_SEND 0x00000020
+#define FS_DEBUG_PHY 0x00000040
+#define FS_DEBUG_CLEANUP 0x00000080
+#define FS_DEBUG_QOS 0x00000100
+#define FS_DEBUG_TXQ 0x00000200
+#define FS_DEBUG_ALLOC 0x00000400
+#define FS_DEBUG_TXMEM 0x00000800
+#define FS_DEBUG_QSIZE 0x00001000
+
+
+#define func_enter() fs_dprintk(FS_DEBUG_FLOW, "fs: enter %s\n", __func__)
+#define func_exit() fs_dprintk(FS_DEBUG_FLOW, "fs: exit %s\n", __func__)
+
+
+static struct fs_dev *fs_boards = NULL;
+
+#ifdef DEBUG
+
+static void my_hd (void *addr, int len)
+{
+ int j, ch;
+ unsigned char *ptr = addr;
+
+ while (len > 0) {
+ printk ("%p ", ptr);
+ for (j=0;j < ((len < 16)?len:16);j++) {
+ printk ("%02x %s", ptr[j], (j==7)?" ":"");
+ }
+ for ( ;j < 16;j++) {
+ printk (" %s", (j==7)?" ":"");
+ }
+ for (j=0;j < ((len < 16)?len:16);j++) {
+ ch = ptr[j];
+ printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch));
+ }
+ printk ("\n");
+ ptr += 16;
+ len -= 16;
+ }
+}
+#else /* DEBUG */
+static void my_hd (void *addr, int len){}
+#endif /* DEBUG */
+
+/********** free an skb (as per ATM device driver documentation) **********/
+
+/* Hmm. If this is ATM specific, why isn't there an ATM routine for this?
+ * I copied it over from the ambassador driver. -- REW */
+
+static inline void fs_kfree_skb (struct sk_buff * skb)
+{
+ if (ATM_SKB(skb)->vcc->pop)
+ ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
+ else
+ dev_kfree_skb_any (skb);
+}
+
+
+
+
+/* It seems the ATM forum recommends this horribly complicated 16bit
+ * floating point format. Turns out the Ambassador uses the exact same
+ * encoding. I just copied it over. If Mitch agrees, I'll move it over
+ * to the atm_misc file or something like that. (and remove it from
+ * here and the ambassador driver) -- REW
+ */
+
+/* The good thing about this format is that it is monotonic. So,
+ a conversion routine need not be very complicated. To be able to
+ round "nearest" we need to take along a few extra bits. Lets
+ put these after 16 bits, so that we can just return the top 16
+ bits of the 32bit number as the result:
+
+ int mr (unsigned int rate, int r)
+ {
+ int e = 16+9;
+ static int round[4]={0, 0, 0xffff, 0x8000};
+ if (!rate) return 0;
+ while (rate & 0xfc000000) {
+ rate >>= 1;
+ e++;
+ }
+ while (! (rate & 0xfe000000)) {
+ rate <<= 1;
+ e--;
+ }
+
+// Now the mantissa is in positions bit 16-25. Excepf for the "hidden 1" that's in bit 26.
+ rate &= ~0x02000000;
+// Next add in the exponent
+ rate |= e << (16+9);
+// And perform the rounding:
+ return (rate + round[r]) >> 16;
+ }
+
+ 14 lines-of-code. Compare that with the 120 that the Ambassador
+ guys needed. (would be 8 lines shorter if I'd try to really reduce
+ the number of lines:
+
+ int mr (unsigned int rate, int r)
+ {
+ int e = 16+9;
+ static int round[4]={0, 0, 0xffff, 0x8000};
+ if (!rate) return 0;
+ for (; rate & 0xfc000000 ;rate >>= 1, e++);
+ for (;!(rate & 0xfe000000);rate <<= 1, e--);
+ return ((rate & ~0x02000000) | (e << (16+9)) + round[r]) >> 16;
+ }
+
+ Exercise for the reader: Remove one more line-of-code, without
+ cheating. (Just joining two lines is cheating). (I know it's
+ possible, don't think you've beat me if you found it... If you
+ manage to lose two lines or more, keep me updated! ;-)
+
+ -- REW */
+
+
+#define ROUND_UP 1
+#define ROUND_DOWN 2
+#define ROUND_NEAREST 3
+/********** make rate (not quite as much fun as Horizon) **********/
+
+static int make_rate(unsigned int rate, int r,
+ u16 *bits, unsigned int *actual)
+{
+ unsigned char exp = -1; /* hush gcc */
+ unsigned int man = -1; /* hush gcc */
+
+ fs_dprintk (FS_DEBUG_QOS, "make_rate %u", rate);
+
+ /* rates in cells per second, ITU format (nasty 16-bit floating-point)
+ given 5-bit e and 9-bit m:
+ rate = EITHER (1+m/2^9)*2^e OR 0
+ bits = EITHER 1<<14 | e<<9 | m OR 0
+ (bit 15 is "reserved", bit 14 "non-zero")
+ smallest rate is 0 (special representation)
+ largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1)
+ smallest non-zero rate is (1+0/512)*2^0 = 1 (> 0)
+ simple algorithm:
+ find position of top bit, this gives e
+ remove top bit and shift (rounding if feeling clever) by 9-e
+ */
+ /* Ambassador ucode bug: please don't set bit 14! so 0 rate not
+ representable. // This should move into the ambassador driver
+ when properly merged. -- REW */
+
+ if (rate > 0xffc00000U) {
+ /* larger than largest representable rate */
+
+ if (r == ROUND_UP) {
+ return -EINVAL;
+ } else {
+ exp = 31;
+ man = 511;
+ }
+
+ } else if (rate) {
+ /* representable rate */
+
+ exp = 31;
+ man = rate;
+
+ /* invariant: rate = man*2^(exp-31) */
+ while (!(man & (1<<31))) {
+ exp = exp - 1;
+ man = man<<1;
+ }
+
+ /* man has top bit set
+ rate = (2^31+(man-2^31))*2^(exp-31)
+ rate = (1+(man-2^31)/2^31)*2^exp
+ */
+ man = man<<1;
+ man &= 0xffffffffU; /* a nop on 32-bit systems */
+ /* rate = (1+man/2^32)*2^exp
+
+ exp is in the range 0 to 31, man is in the range 0 to 2^32-1
+ time to lose significance... we want m in the range 0 to 2^9-1
+ rounding presents a minor problem... we first decide which way
+ we are rounding (based on given rounding direction and possibly
+ the bits of the mantissa that are to be discarded).
+ */
+
+ switch (r) {
+ case ROUND_DOWN: {
+ /* just truncate */
+ man = man>>(32-9);
+ break;
+ }
+ case ROUND_UP: {
+ /* check all bits that we are discarding */
+ if (man & (~0U>>9)) {
+ man = (man>>(32-9)) + 1;
+ if (man == (1<<9)) {
+ /* no need to check for round up outside of range */
+ man = 0;
+ exp += 1;
+ }
+ } else {
+ man = (man>>(32-9));
+ }
+ break;
+ }
+ case ROUND_NEAREST: {
+ /* check msb that we are discarding */
+ if (man & (1<<(32-9-1))) {
+ man = (man>>(32-9)) + 1;
+ if (man == (1<<9)) {
+ /* no need to check for round up outside of range */
+ man = 0;
+ exp += 1;
+ }
+ } else {
+ man = (man>>(32-9));
+ }
+ break;
+ }
+ }
+
+ } else {
+ /* zero rate - not representable */
+
+ if (r == ROUND_DOWN) {
+ return -EINVAL;
+ } else {
+ exp = 0;
+ man = 0;
+ }
+ }
+
+ fs_dprintk (FS_DEBUG_QOS, "rate: man=%u, exp=%hu", man, exp);
+
+ if (bits)
+ *bits = /* (1<<14) | */ (exp<<9) | man;
+
+ if (actual)
+ *actual = (exp >= 9)
+ ? (1 << exp) + (man << (exp-9))
+ : (1 << exp) + ((man + (1<<(9-exp-1))) >> (9-exp));
+
+ return 0;
+}
+
+
+
+
+/* FireStream access routines */
+/* For DEEP-DOWN debugging these can be rigged to intercept accesses to
+ certain registers or to just log all accesses. */
+
+static inline void write_fs (struct fs_dev *dev, int offset, u32 val)
+{
+ writel (val, dev->base + offset);
+}
+
+
+static inline u32 read_fs (struct fs_dev *dev, int offset)
+{
+ return readl (dev->base + offset);
+}
+
+
+
+static inline struct FS_QENTRY *get_qentry (struct fs_dev *dev, struct queue *q)
+{
+ return bus_to_virt (read_fs (dev, Q_WP(q->offset)) & Q_ADDR_MASK);
+}
+
+
+static void submit_qentry (struct fs_dev *dev, struct queue *q, struct FS_QENTRY *qe)
+{
+ u32 wp;
+ struct FS_QENTRY *cqe;
+
+ /* XXX Sanity check: the write pointer can be checked to be
+ still the same as the value passed as qe... -- REW */
+ /* udelay (5); */
+ while ((wp = read_fs (dev, Q_WP (q->offset))) & Q_FULL) {
+ fs_dprintk (FS_DEBUG_TXQ, "Found queue at %x full. Waiting.\n",
+ q->offset);
+ schedule ();
+ }
+
+ wp &= ~0xf;
+ cqe = bus_to_virt (wp);
+ if (qe != cqe) {
+ fs_dprintk (FS_DEBUG_TXQ, "q mismatch! %p %p\n", qe, cqe);
+ }
+
+ write_fs (dev, Q_WP(q->offset), Q_INCWRAP);
+
+ {
+ static int c;
+ if (!(c++ % 100))
+ {
+ int rp, wp;
+ rp = read_fs (dev, Q_RP(q->offset));
+ wp = read_fs (dev, Q_WP(q->offset));
+ fs_dprintk (FS_DEBUG_TXQ, "q at %d: %x-%x: %x entries.\n",
+ q->offset, rp, wp, wp-rp);
+ }
+ }
+}
+
+#ifdef DEBUG_EXTRA
+static struct FS_QENTRY pq[60];
+static int qp;
+
+static struct FS_BPENTRY dq[60];
+static int qd;
+static void *da[60];
+#endif
+
+static void submit_queue (struct fs_dev *dev, struct queue *q,
+ u32 cmd, u32 p1, u32 p2, u32 p3)
+{
+ struct FS_QENTRY *qe;
+
+ qe = get_qentry (dev, q);
+ qe->cmd = cmd;
+ qe->p0 = p1;
+ qe->p1 = p2;
+ qe->p2 = p3;
+ submit_qentry (dev, q, qe);
+
+#ifdef DEBUG_EXTRA
+ pq[qp].cmd = cmd;
+ pq[qp].p0 = p1;
+ pq[qp].p1 = p2;
+ pq[qp].p2 = p3;
+ qp++;
+ if (qp >= 60) qp = 0;
+#endif
+}
+
+/* Test the "other" way one day... -- REW */
+#if 1
+#define submit_command submit_queue
+#else
+
+static void submit_command (struct fs_dev *dev, struct queue *q,
+ u32 cmd, u32 p1, u32 p2, u32 p3)
+{
+ write_fs (dev, CMDR0, cmd);
+ write_fs (dev, CMDR1, p1);
+ write_fs (dev, CMDR2, p2);
+ write_fs (dev, CMDR3, p3);
+}
+#endif
+
+
+
+static void process_return_queue (struct fs_dev *dev, struct queue *q)
+{
+ long rq;
+ struct FS_QENTRY *qe;
+ void *tc;
+
+ while (!((rq = read_fs (dev, Q_RP(q->offset))) & Q_EMPTY)) {
+ fs_dprintk (FS_DEBUG_QUEUE, "reaping return queue entry at %lx\n", rq);
+ qe = bus_to_virt (rq);
+
+ fs_dprintk (FS_DEBUG_QUEUE, "queue entry: %08x %08x %08x %08x. (%d)\n",
+ qe->cmd, qe->p0, qe->p1, qe->p2, STATUS_CODE (qe));
+
+ switch (STATUS_CODE (qe)) {
+ case 5:
+ tc = bus_to_virt (qe->p0);
+ fs_dprintk (FS_DEBUG_ALLOC, "Free tc: %p\n", tc);
+ kfree (tc);
+ break;
+ }
+
+ write_fs (dev, Q_RP(q->offset), Q_INCWRAP);
+ }
+}
+
+
+static void process_txdone_queue (struct fs_dev *dev, struct queue *q)
+{
+ long rq;
+ long tmp;
+ struct FS_QENTRY *qe;
+ struct sk_buff *skb;
+ struct FS_BPENTRY *td;
+
+ while (!((rq = read_fs (dev, Q_RP(q->offset))) & Q_EMPTY)) {
+ fs_dprintk (FS_DEBUG_QUEUE, "reaping txdone entry at %lx\n", rq);
+ qe = bus_to_virt (rq);
+
+ fs_dprintk (FS_DEBUG_QUEUE, "queue entry: %08x %08x %08x %08x: %d\n",
+ qe->cmd, qe->p0, qe->p1, qe->p2, STATUS_CODE (qe));
+
+ if (STATUS_CODE (qe) != 2)
+ fs_dprintk (FS_DEBUG_TXMEM, "queue entry: %08x %08x %08x %08x: %d\n",
+ qe->cmd, qe->p0, qe->p1, qe->p2, STATUS_CODE (qe));
+
+
+ switch (STATUS_CODE (qe)) {
+ case 0x01: /* This is for AAL0 where we put the chip in streaming mode */
+ /* Fall through */
+ case 0x02:
+ /* Process a real txdone entry. */
+ tmp = qe->p0;
+ if (tmp & 0x0f)
+ printk (KERN_WARNING "td not aligned: %ld\n", tmp);
+ tmp &= ~0x0f;
+ td = bus_to_virt (tmp);
+
+ fs_dprintk (FS_DEBUG_QUEUE, "Pool entry: %08x %08x %08x %08x %p.\n",
+ td->flags, td->next, td->bsa, td->aal_bufsize, td->skb );
+
+ skb = td->skb;
+ if (skb == FS_VCC (ATM_SKB(skb)->vcc)->last_skb) {
+ FS_VCC (ATM_SKB(skb)->vcc)->last_skb = NULL;
+ wake_up_interruptible (& FS_VCC (ATM_SKB(skb)->vcc)->close_wait);
+ }
+ td->dev->ntxpckts--;
+
+ {
+ static int c=0;
+
+ if (!(c++ % 100)) {
+ fs_dprintk (FS_DEBUG_QSIZE, "[%d]", td->dev->ntxpckts);
+ }
+ }
+
+ atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
+
+ fs_dprintk (FS_DEBUG_TXMEM, "i");
+ fs_dprintk (FS_DEBUG_ALLOC, "Free t-skb: %p\n", skb);
+ fs_kfree_skb (skb);
+
+ fs_dprintk (FS_DEBUG_ALLOC, "Free trans-d: %p\n", td);
+ memset (td, ATM_POISON_FREE, sizeof(struct FS_BPENTRY));
+ kfree (td);
+ break;
+ default:
+ /* Here we get the tx purge inhibit command ... */
+ /* Action, I believe, is "don't do anything". -- REW */
+ ;
+ }
+
+ write_fs (dev, Q_RP(q->offset), Q_INCWRAP);
+ }
+}
+
+
+static void process_incoming (struct fs_dev *dev, struct queue *q)
+{
+ long rq;
+ struct FS_QENTRY *qe;
+ struct FS_BPENTRY *pe;
+ struct sk_buff *skb;
+ unsigned int channo;
+ struct atm_vcc *atm_vcc;
+
+ while (!((rq = read_fs (dev, Q_RP(q->offset))) & Q_EMPTY)) {
+ fs_dprintk (FS_DEBUG_QUEUE, "reaping incoming queue entry at %lx\n", rq);
+ qe = bus_to_virt (rq);
+
+ fs_dprintk (FS_DEBUG_QUEUE, "queue entry: %08x %08x %08x %08x. ",
+ qe->cmd, qe->p0, qe->p1, qe->p2);
+
+ fs_dprintk (FS_DEBUG_QUEUE, "-> %x: %s\n",
+ STATUS_CODE (qe),
+ res_strings[STATUS_CODE(qe)]);
+
+ pe = bus_to_virt (qe->p0);
+ fs_dprintk (FS_DEBUG_QUEUE, "Pool entry: %08x %08x %08x %08x %p %p.\n",
+ pe->flags, pe->next, pe->bsa, pe->aal_bufsize,
+ pe->skb, pe->fp);
+
+ channo = qe->cmd & 0xffff;
+
+ if (channo < dev->nchannels)
+ atm_vcc = dev->atm_vccs[channo];
+ else
+ atm_vcc = NULL;
+
+ /* Single buffer packet */
+ switch (STATUS_CODE (qe)) {
+ case 0x1:
+ /* Fall through for streaming mode */
+ case 0x2:/* Packet received OK.... */
+ if (atm_vcc) {
+ skb = pe->skb;
+ pe->fp->n--;
+#if 0
+ fs_dprintk (FS_DEBUG_QUEUE, "Got skb: %p\n", skb);
+ if (FS_DEBUG_QUEUE & fs_debug) my_hd (bus_to_virt (pe->bsa), 0x20);
+#endif
+ skb_put (skb, qe->p1 & 0xffff);
+ ATM_SKB(skb)->vcc = atm_vcc;
+ atomic_inc(&atm_vcc->stats->rx);
+ __net_timestamp(skb);
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-skb: %p (pushed)\n", skb);
+ atm_vcc->push (atm_vcc, skb);
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-d: %p\n", pe);
+ kfree (pe);
+ } else {
+ printk (KERN_ERR "Got a receive on a non-open channel %d.\n", channo);
+ }
+ break;
+ case 0x17:/* AAL 5 CRC32 error. IFF the length field is nonzero, a buffer
+ has been consumed and needs to be processed. -- REW */
+ if (qe->p1 & 0xffff) {
+ pe = bus_to_virt (qe->p0);
+ pe->fp->n--;
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-skb: %p\n", pe->skb);
+ dev_kfree_skb_any (pe->skb);
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-d: %p\n", pe);
+ kfree (pe);
+ }
+ if (atm_vcc)
+ atomic_inc(&atm_vcc->stats->rx_drop);
+ break;
+ case 0x1f: /* Reassembly abort: no buffers. */
+ /* Silently increment error counter. */
+ if (atm_vcc)
+ atomic_inc(&atm_vcc->stats->rx_drop);
+ break;
+ default: /* Hmm. Haven't written the code to handle the others yet... -- REW */
+ printk (KERN_WARNING "Don't know what to do with RX status %x: %s.\n",
+ STATUS_CODE(qe), res_strings[STATUS_CODE (qe)]);
+ }
+ write_fs (dev, Q_RP(q->offset), Q_INCWRAP);
+ }
+}
+
+
+
+#define DO_DIRECTION(tp) ((tp)->traffic_class != ATM_NONE)
+
+static int fs_open(struct atm_vcc *atm_vcc)
+{
+ struct fs_dev *dev;
+ struct fs_vcc *vcc;
+ struct fs_transmit_config *tc;
+ struct atm_trafprm * txtp;
+ struct atm_trafprm * rxtp;
+ /* struct fs_receive_config *rc;*/
+ /* struct FS_QENTRY *qe; */
+ int error;
+ int bfp;
+ int to;
+ unsigned short tmc0;
+ short vpi = atm_vcc->vpi;
+ int vci = atm_vcc->vci;
+
+ func_enter ();
+
+ dev = FS_DEV(atm_vcc->dev);
+ fs_dprintk (FS_DEBUG_OPEN, "fs: open on dev: %p, vcc at %p\n",
+ dev, atm_vcc);
+
+ if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
+ set_bit(ATM_VF_ADDR, &atm_vcc->flags);
+
+ if ((atm_vcc->qos.aal != ATM_AAL5) &&
+ (atm_vcc->qos.aal != ATM_AAL2))
+ return -EINVAL; /* XXX AAL0 */
+
+ fs_dprintk (FS_DEBUG_OPEN, "fs: (itf %d): open %d.%d\n",
+ atm_vcc->dev->number, atm_vcc->vpi, atm_vcc->vci);
+
+ /* XXX handle qos parameters (rate limiting) ? */
+
+ vcc = kmalloc(sizeof(struct fs_vcc), GFP_KERNEL);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc VCC: %p(%Zd)\n", vcc, sizeof(struct fs_vcc));
+ if (!vcc) {
+ clear_bit(ATM_VF_ADDR, &atm_vcc->flags);
+ return -ENOMEM;
+ }
+
+ atm_vcc->dev_data = vcc;
+ vcc->last_skb = NULL;
+
+ init_waitqueue_head (&vcc->close_wait);
+
+ txtp = &atm_vcc->qos.txtp;
+ rxtp = &atm_vcc->qos.rxtp;
+
+ if (!test_bit(ATM_VF_PARTIAL, &atm_vcc->flags)) {
+ if (IS_FS50(dev)) {
+ /* Increment the channel numer: take a free one next time. */
+ for (to=33;to;to--, dev->channo++) {
+ /* We only have 32 channels */
+ if (dev->channo >= 32)
+ dev->channo = 0;
+ /* If we need to do RX, AND the RX is inuse, try the next */
+ if (DO_DIRECTION(rxtp) && dev->atm_vccs[dev->channo])
+ continue;
+ /* If we need to do TX, AND the TX is inuse, try the next */
+ if (DO_DIRECTION(txtp) && test_bit (dev->channo, dev->tx_inuse))
+ continue;
+ /* Ok, both are free! (or not needed) */
+ break;
+ }
+ if (!to) {
+ printk ("No more free channels for FS50..\n");
+ return -EBUSY;
+ }
+ vcc->channo = dev->channo;
+ dev->channo &= dev->channel_mask;
+
+ } else {
+ vcc->channo = (vpi << FS155_VCI_BITS) | (vci);
+ if (((DO_DIRECTION(rxtp) && dev->atm_vccs[vcc->channo])) ||
+ ( DO_DIRECTION(txtp) && test_bit (vcc->channo, dev->tx_inuse))) {
+ printk ("Channel is in use for FS155.\n");
+ return -EBUSY;
+ }
+ }
+ fs_dprintk (FS_DEBUG_OPEN, "OK. Allocated channel %x(%d).\n",
+ vcc->channo, vcc->channo);
+ }
+
+ if (DO_DIRECTION (txtp)) {
+ tc = kmalloc (sizeof (struct fs_transmit_config), GFP_KERNEL);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc tc: %p(%Zd)\n",
+ tc, sizeof (struct fs_transmit_config));
+ if (!tc) {
+ fs_dprintk (FS_DEBUG_OPEN, "fs: can't alloc transmit_config.\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate the "open" entry from the high priority txq. This makes
+ it most likely that the chip will notice it. It also prevents us
+ from having to wait for completion. On the other hand, we may
+ need to wait for completion anyway, to see if it completed
+ successfully. */
+
+ switch (atm_vcc->qos.aal) {
+ case ATM_AAL2:
+ case ATM_AAL0:
+ tc->flags = 0
+ | TC_FLAGS_TRANSPARENT_PAYLOAD
+ | TC_FLAGS_PACKET
+ | (1 << 28)
+ | TC_FLAGS_TYPE_UBR /* XXX Change to VBR -- PVDL */
+ | TC_FLAGS_CAL0;
+ break;
+ case ATM_AAL5:
+ tc->flags = 0
+ | TC_FLAGS_AAL5
+ | TC_FLAGS_PACKET /* ??? */
+ | TC_FLAGS_TYPE_CBR
+ | TC_FLAGS_CAL0;
+ break;
+ default:
+ printk ("Unknown aal: %d\n", atm_vcc->qos.aal);
+ tc->flags = 0;
+ }
+ /* Docs are vague about this atm_hdr field. By the way, the FS
+ * chip makes odd errors if lower bits are set.... -- REW */
+ tc->atm_hdr = (vpi << 20) | (vci << 4);
+ tmc0 = 0;
+ {
+ int pcr = atm_pcr_goal (txtp);
+
+ fs_dprintk (FS_DEBUG_OPEN, "pcr = %d.\n", pcr);
+
+ /* XXX Hmm. officially we're only allowed to do this if rounding
+ is round_down -- REW */
+ if (IS_FS50(dev)) {
+ if (pcr > 51840000/53/8) pcr = 51840000/53/8;
+ } else {
+ if (pcr > 155520000/53/8) pcr = 155520000/53/8;
+ }
+ if (!pcr) {
+ /* no rate cap */
+ tmc0 = IS_FS50(dev)?0x61BE:0x64c9; /* Just copied over the bits from Fujitsu -- REW */
+ } else {
+ int r;
+ if (pcr < 0) {
+ r = ROUND_DOWN;
+ pcr = -pcr;
+ } else {
+ r = ROUND_UP;
+ }
+ error = make_rate (pcr, r, &tmc0, NULL);
+ if (error) {
+ kfree(tc);
+ return error;
+ }
+ }
+ fs_dprintk (FS_DEBUG_OPEN, "pcr = %d.\n", pcr);
+ }
+
+ tc->TMC[0] = tmc0 | 0x4000;
+ tc->TMC[1] = 0; /* Unused */
+ tc->TMC[2] = 0; /* Unused */
+ tc->TMC[3] = 0; /* Unused */
+
+ tc->spec = 0; /* UTOPIA address, UDF, HEC: Unused -> 0 */
+ tc->rtag[0] = 0; /* What should I do with routing tags???
+ -- Not used -- AS -- Thanks -- REW*/
+ tc->rtag[1] = 0;
+ tc->rtag[2] = 0;
+
+ if (fs_debug & FS_DEBUG_OPEN) {
+ fs_dprintk (FS_DEBUG_OPEN, "TX config record:\n");
+ my_hd (tc, sizeof (*tc));
+ }
+
+ /* We now use the "submit_command" function to submit commands to
+ the firestream. There is a define up near the definition of
+ that routine that switches this routine between immediate write
+ to the immediate command registers and queuing the commands in
+ the HPTXQ for execution. This last technique might be more
+ efficient if we know we're going to submit a whole lot of
+ commands in one go, but this driver is not setup to be able to
+ use such a construct. So it probably doen't matter much right
+ now. -- REW */
+
+ /* The command is IMMediate and INQueue. The parameters are out-of-line.. */
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_CONFIG_TX | QE_CMD_IMM_INQ | vcc->channo,
+ virt_to_bus (tc), 0, 0);
+
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_TX_EN | QE_CMD_IMM_INQ | vcc->channo,
+ 0, 0, 0);
+ set_bit (vcc->channo, dev->tx_inuse);
+ }
+
+ if (DO_DIRECTION (rxtp)) {
+ dev->atm_vccs[vcc->channo] = atm_vcc;
+
+ for (bfp = 0;bfp < FS_NR_FREE_POOLS; bfp++)
+ if (atm_vcc->qos.rxtp.max_sdu <= dev->rx_fp[bfp].bufsize) break;
+ if (bfp >= FS_NR_FREE_POOLS) {
+ fs_dprintk (FS_DEBUG_OPEN, "No free pool fits sdu: %d.\n",
+ atm_vcc->qos.rxtp.max_sdu);
+ /* XXX Cleanup? -- Would just calling fs_close work??? -- REW */
+
+ /* XXX clear tx inuse. Close TX part? */
+ dev->atm_vccs[vcc->channo] = NULL;
+ kfree (vcc);
+ return -EINVAL;
+ }
+
+ switch (atm_vcc->qos.aal) {
+ case ATM_AAL0:
+ case ATM_AAL2:
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_CONFIG_RX | QE_CMD_IMM_INQ | vcc->channo,
+ RC_FLAGS_TRANSP |
+ RC_FLAGS_BFPS_BFP * bfp |
+ RC_FLAGS_RXBM_PSB, 0, 0);
+ break;
+ case ATM_AAL5:
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_CONFIG_RX | QE_CMD_IMM_INQ | vcc->channo,
+ RC_FLAGS_AAL5 |
+ RC_FLAGS_BFPS_BFP * bfp |
+ RC_FLAGS_RXBM_PSB, 0, 0);
+ break;
+ };
+ if (IS_FS50 (dev)) {
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_REG_WR | QE_CMD_IMM_INQ,
+ 0x80 + vcc->channo,
+ (vpi << 16) | vci, 0 ); /* XXX -- Use defines. */
+ }
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_RX_EN | QE_CMD_IMM_INQ | vcc->channo,
+ 0, 0, 0);
+ }
+
+ /* Indicate we're done! */
+ set_bit(ATM_VF_READY, &atm_vcc->flags);
+
+ func_exit ();
+ return 0;
+}
+
+
+static void fs_close(struct atm_vcc *atm_vcc)
+{
+ struct fs_dev *dev = FS_DEV (atm_vcc->dev);
+ struct fs_vcc *vcc = FS_VCC (atm_vcc);
+ struct atm_trafprm * txtp;
+ struct atm_trafprm * rxtp;
+
+ func_enter ();
+
+ clear_bit(ATM_VF_READY, &atm_vcc->flags);
+
+ fs_dprintk (FS_DEBUG_QSIZE, "--==**[%d]**==--", dev->ntxpckts);
+ if (vcc->last_skb) {
+ fs_dprintk (FS_DEBUG_QUEUE, "Waiting for skb %p to be sent.\n",
+ vcc->last_skb);
+ /* We're going to wait for the last packet to get sent on this VC. It would
+ be impolite not to send them don't you think?
+ XXX
+ We don't know which packets didn't get sent. So if we get interrupted in
+ this sleep_on, we'll lose any reference to these packets. Memory leak!
+ On the other hand, it's awfully convenient that we can abort a "close" that
+ is taking too long. Maybe just use non-interruptible sleep on? -- REW */
+ wait_event_interruptible(vcc->close_wait, !vcc->last_skb);
+ }
+
+ txtp = &atm_vcc->qos.txtp;
+ rxtp = &atm_vcc->qos.rxtp;
+
+
+ /* See App note XXX (Unpublished as of now) for the reason for the
+ removal of the "CMD_IMM_INQ" part of the TX_PURGE_INH... -- REW */
+
+ if (DO_DIRECTION (txtp)) {
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_TX_PURGE_INH | /*QE_CMD_IMM_INQ|*/ vcc->channo, 0,0,0);
+ clear_bit (vcc->channo, dev->tx_inuse);
+ }
+
+ if (DO_DIRECTION (rxtp)) {
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_RX_PURGE_INH | QE_CMD_IMM_INQ | vcc->channo, 0,0,0);
+ dev->atm_vccs [vcc->channo] = NULL;
+
+ /* This means that this is configured as a receive channel */
+ if (IS_FS50 (dev)) {
+ /* Disable the receive filter. Is 0/0 indeed an invalid receive
+ channel? -- REW. Yes it is. -- Hang. Ok. I'll use -1
+ (0xfff...) -- REW */
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_REG_WR | QE_CMD_IMM_INQ,
+ 0x80 + vcc->channo, -1, 0 );
+ }
+ }
+
+ fs_dprintk (FS_DEBUG_ALLOC, "Free vcc: %p\n", vcc);
+ kfree (vcc);
+
+ func_exit ();
+}
+
+
+static int fs_send (struct atm_vcc *atm_vcc, struct sk_buff *skb)
+{
+ struct fs_dev *dev = FS_DEV (atm_vcc->dev);
+ struct fs_vcc *vcc = FS_VCC (atm_vcc);
+ struct FS_BPENTRY *td;
+
+ func_enter ();
+
+ fs_dprintk (FS_DEBUG_TXMEM, "I");
+ fs_dprintk (FS_DEBUG_SEND, "Send: atm_vcc %p skb %p vcc %p dev %p\n",
+ atm_vcc, skb, vcc, dev);
+
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc t-skb: %p (atm_send)\n", skb);
+
+ ATM_SKB(skb)->vcc = atm_vcc;
+
+ vcc->last_skb = skb;
+
+ td = kmalloc (sizeof (struct FS_BPENTRY), GFP_ATOMIC);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc transd: %p(%Zd)\n", td, sizeof (struct FS_BPENTRY));
+ if (!td) {
+ /* Oops out of mem */
+ return -ENOMEM;
+ }
+
+ fs_dprintk (FS_DEBUG_SEND, "first word in buffer: %x\n",
+ *(int *) skb->data);
+
+ td->flags = TD_EPI | TD_DATA | skb->len;
+ td->next = 0;
+ td->bsa = virt_to_bus (skb->data);
+ td->skb = skb;
+ td->dev = dev;
+ dev->ntxpckts++;
+
+#ifdef DEBUG_EXTRA
+ da[qd] = td;
+ dq[qd].flags = td->flags;
+ dq[qd].next = td->next;
+ dq[qd].bsa = td->bsa;
+ dq[qd].skb = td->skb;
+ dq[qd].dev = td->dev;
+ qd++;
+ if (qd >= 60) qd = 0;
+#endif
+
+ submit_queue (dev, &dev->hp_txq,
+ QE_TRANSMIT_DE | vcc->channo,
+ virt_to_bus (td), 0,
+ virt_to_bus (td));
+
+ fs_dprintk (FS_DEBUG_QUEUE, "in send: txq %d txrq %d\n",
+ read_fs (dev, Q_EA (dev->hp_txq.offset)) -
+ read_fs (dev, Q_SA (dev->hp_txq.offset)),
+ read_fs (dev, Q_EA (dev->tx_relq.offset)) -
+ read_fs (dev, Q_SA (dev->tx_relq.offset)));
+
+ func_exit ();
+ return 0;
+}
+
+
+/* Some function placeholders for functions we don't yet support. */
+
+#if 0
+static int fs_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
+{
+ func_enter ();
+ func_exit ();
+ return -ENOIOCTLCMD;
+}
+
+
+static int fs_getsockopt(struct atm_vcc *vcc,int level,int optname,
+ void __user *optval,int optlen)
+{
+ func_enter ();
+ func_exit ();
+ return 0;
+}
+
+
+static int fs_setsockopt(struct atm_vcc *vcc,int level,int optname,
+ void __user *optval,unsigned int optlen)
+{
+ func_enter ();
+ func_exit ();
+ return 0;
+}
+
+
+static void fs_phy_put(struct atm_dev *dev,unsigned char value,
+ unsigned long addr)
+{
+ func_enter ();
+ func_exit ();
+}
+
+
+static unsigned char fs_phy_get(struct atm_dev *dev,unsigned long addr)
+{
+ func_enter ();
+ func_exit ();
+ return 0;
+}
+
+
+static int fs_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flags)
+{
+ func_enter ();
+ func_exit ();
+ return 0;
+};
+
+#endif
+
+
+static const struct atmdev_ops ops = {
+ .open = fs_open,
+ .close = fs_close,
+ .send = fs_send,
+ .owner = THIS_MODULE,
+ /* ioctl: fs_ioctl, */
+ /* getsockopt: fs_getsockopt, */
+ /* setsockopt: fs_setsockopt, */
+ /* change_qos: fs_change_qos, */
+
+ /* For now implement these internally here... */
+ /* phy_put: fs_phy_put, */
+ /* phy_get: fs_phy_get, */
+};
+
+
+static void undocumented_pci_fix(struct pci_dev *pdev)
+{
+ u32 tint;
+
+ /* The Windows driver says: */
+ /* Switch off FireStream Retry Limit Threshold
+ */
+
+ /* The register at 0x28 is documented as "reserved", no further
+ comments. */
+
+ pci_read_config_dword (pdev, 0x28, &tint);
+ if (tint != 0x80) {
+ tint = 0x80;
+ pci_write_config_dword (pdev, 0x28, tint);
+ }
+}
+
+
+
+/**************************************************************************
+ * PHY routines *
+ **************************************************************************/
+
+static void write_phy(struct fs_dev *dev, int regnum, int val)
+{
+ submit_command (dev, &dev->hp_txq, QE_CMD_PRP_WR | QE_CMD_IMM_INQ,
+ regnum, val, 0);
+}
+
+static int init_phy(struct fs_dev *dev, struct reginit_item *reginit)
+{
+ int i;
+
+ func_enter ();
+ while (reginit->reg != PHY_EOF) {
+ if (reginit->reg == PHY_CLEARALL) {
+ /* "PHY_CLEARALL means clear all registers. Numregisters is in "val". */
+ for (i=0;i<reginit->val;i++) {
+ write_phy (dev, i, 0);
+ }
+ } else {
+ write_phy (dev, reginit->reg, reginit->val);
+ }
+ reginit++;
+ }
+ func_exit ();
+ return 0;
+}
+
+static void reset_chip (struct fs_dev *dev)
+{
+ int i;
+
+ write_fs (dev, SARMODE0, SARMODE0_SRTS0);
+
+ /* Undocumented delay */
+ udelay (128);
+
+ /* The "internal registers are documented to all reset to zero, but
+ comments & code in the Windows driver indicates that the pools are
+ NOT reset. */
+ for (i=0;i < FS_NR_FREE_POOLS;i++) {
+ write_fs (dev, FP_CNF (RXB_FP(i)), 0);
+ write_fs (dev, FP_SA (RXB_FP(i)), 0);
+ write_fs (dev, FP_EA (RXB_FP(i)), 0);
+ write_fs (dev, FP_CNT (RXB_FP(i)), 0);
+ write_fs (dev, FP_CTU (RXB_FP(i)), 0);
+ }
+
+ /* The same goes for the match channel registers, although those are
+ NOT documented that way in the Windows driver. -- REW */
+ /* The Windows driver DOES write 0 to these registers somewhere in
+ the init sequence. However, a small hardware-feature, will
+ prevent reception of data on VPI/VCI = 0/0 (Unless the channel
+ allocated happens to have no disabled channels that have a lower
+ number. -- REW */
+
+ /* Clear the match channel registers. */
+ if (IS_FS50 (dev)) {
+ for (i=0;i<FS50_NR_CHANNELS;i++) {
+ write_fs (dev, 0x200 + i * 4, -1);
+ }
+ }
+}
+
+static void *aligned_kmalloc(int size, gfp_t flags, int alignment)
+{
+ void *t;
+
+ if (alignment <= 0x10) {
+ t = kmalloc (size, flags);
+ if ((unsigned long)t & (alignment-1)) {
+ printk ("Kmalloc doesn't align things correctly! %p\n", t);
+ kfree (t);
+ return aligned_kmalloc (size, flags, alignment * 4);
+ }
+ return t;
+ }
+ printk (KERN_ERR "Request for > 0x10 alignment not yet implemented (hard!)\n");
+ return NULL;
+}
+
+static int init_q(struct fs_dev *dev, struct queue *txq, int queue,
+ int nentries, int is_rq)
+{
+ int sz = nentries * sizeof (struct FS_QENTRY);
+ struct FS_QENTRY *p;
+
+ func_enter ();
+
+ fs_dprintk (FS_DEBUG_INIT, "Inititing queue at %x: %d entries:\n",
+ queue, nentries);
+
+ p = aligned_kmalloc (sz, GFP_KERNEL, 0x10);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc queue: %p(%d)\n", p, sz);
+
+ if (!p) return 0;
+
+ write_fs (dev, Q_SA(queue), virt_to_bus(p));
+ write_fs (dev, Q_EA(queue), virt_to_bus(p+nentries-1));
+ write_fs (dev, Q_WP(queue), virt_to_bus(p));
+ write_fs (dev, Q_RP(queue), virt_to_bus(p));
+ if (is_rq) {
+ /* Configuration for the receive queue: 0: interrupt immediately,
+ no pre-warning to empty queues: We do our best to keep the
+ queue filled anyway. */
+ write_fs (dev, Q_CNF(queue), 0 );
+ }
+
+ txq->sa = p;
+ txq->ea = p;
+ txq->offset = queue;
+
+ func_exit ();
+ return 1;
+}
+
+
+static int init_fp(struct fs_dev *dev, struct freepool *fp, int queue,
+ int bufsize, int nr_buffers)
+{
+ func_enter ();
+
+ fs_dprintk (FS_DEBUG_INIT, "Inititing free pool at %x:\n", queue);
+
+ write_fs (dev, FP_CNF(queue), (bufsize * RBFP_RBS) | RBFP_RBSVAL | RBFP_CME);
+ write_fs (dev, FP_SA(queue), 0);
+ write_fs (dev, FP_EA(queue), 0);
+ write_fs (dev, FP_CTU(queue), 0);
+ write_fs (dev, FP_CNT(queue), 0);
+
+ fp->offset = queue;
+ fp->bufsize = bufsize;
+ fp->nr_buffers = nr_buffers;
+
+ func_exit ();
+ return 1;
+}
+
+
+static inline int nr_buffers_in_freepool (struct fs_dev *dev, struct freepool *fp)
+{
+#if 0
+ /* This seems to be unreliable.... */
+ return read_fs (dev, FP_CNT (fp->offset));
+#else
+ return fp->n;
+#endif
+}
+
+
+/* Check if this gets going again if a pool ever runs out. -- Yes, it
+ does. I've seen "receive abort: no buffers" and things started
+ working again after that... -- REW */
+
+static void top_off_fp (struct fs_dev *dev, struct freepool *fp,
+ gfp_t gfp_flags)
+{
+ struct FS_BPENTRY *qe, *ne;
+ struct sk_buff *skb;
+ int n = 0;
+ u32 qe_tmp;
+
+ fs_dprintk (FS_DEBUG_QUEUE, "Topping off queue at %x (%d-%d/%d)\n",
+ fp->offset, read_fs (dev, FP_CNT (fp->offset)), fp->n,
+ fp->nr_buffers);
+ while (nr_buffers_in_freepool(dev, fp) < fp->nr_buffers) {
+
+ skb = alloc_skb (fp->bufsize, gfp_flags);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc rec-skb: %p(%d)\n", skb, fp->bufsize);
+ if (!skb) break;
+ ne = kmalloc (sizeof (struct FS_BPENTRY), gfp_flags);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc rec-d: %p(%Zd)\n", ne, sizeof (struct FS_BPENTRY));
+ if (!ne) {
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-skb: %p\n", skb);
+ dev_kfree_skb_any (skb);
+ break;
+ }
+
+ fs_dprintk (FS_DEBUG_QUEUE, "Adding skb %p desc %p -> %p(%p) ",
+ skb, ne, skb->data, skb->head);
+ n++;
+ ne->flags = FP_FLAGS_EPI | fp->bufsize;
+ ne->next = virt_to_bus (NULL);
+ ne->bsa = virt_to_bus (skb->data);
+ ne->aal_bufsize = fp->bufsize;
+ ne->skb = skb;
+ ne->fp = fp;
+
+ /*
+ * FIXME: following code encodes and decodes
+ * machine pointers (could be 64-bit) into a
+ * 32-bit register.
+ */
+
+ qe_tmp = read_fs (dev, FP_EA(fp->offset));
+ fs_dprintk (FS_DEBUG_QUEUE, "link at %x\n", qe_tmp);
+ if (qe_tmp) {
+ qe = bus_to_virt ((long) qe_tmp);
+ qe->next = virt_to_bus(ne);
+ qe->flags &= ~FP_FLAGS_EPI;
+ } else
+ write_fs (dev, FP_SA(fp->offset), virt_to_bus(ne));
+
+ write_fs (dev, FP_EA(fp->offset), virt_to_bus (ne));
+ fp->n++; /* XXX Atomic_inc? */
+ write_fs (dev, FP_CTU(fp->offset), 1);
+ }
+
+ fs_dprintk (FS_DEBUG_QUEUE, "Added %d entries. \n", n);
+}
+
+static void free_queue(struct fs_dev *dev, struct queue *txq)
+{
+ func_enter ();
+
+ write_fs (dev, Q_SA(txq->offset), 0);
+ write_fs (dev, Q_EA(txq->offset), 0);
+ write_fs (dev, Q_RP(txq->offset), 0);
+ write_fs (dev, Q_WP(txq->offset), 0);
+ /* Configuration ? */
+
+ fs_dprintk (FS_DEBUG_ALLOC, "Free queue: %p\n", txq->sa);
+ kfree (txq->sa);
+
+ func_exit ();
+}
+
+static void free_freepool(struct fs_dev *dev, struct freepool *fp)
+{
+ func_enter ();
+
+ write_fs (dev, FP_CNF(fp->offset), 0);
+ write_fs (dev, FP_SA (fp->offset), 0);
+ write_fs (dev, FP_EA (fp->offset), 0);
+ write_fs (dev, FP_CNT(fp->offset), 0);
+ write_fs (dev, FP_CTU(fp->offset), 0);
+
+ func_exit ();
+}
+
+
+
+static irqreturn_t fs_irq (int irq, void *dev_id)
+{
+ int i;
+ u32 status;
+ struct fs_dev *dev = dev_id;
+
+ status = read_fs (dev, ISR);
+ if (!status)
+ return IRQ_NONE;
+
+ func_enter ();
+
+#ifdef IRQ_RATE_LIMIT
+ /* Aaargh! I'm ashamed. This costs more lines-of-code than the actual
+ interrupt routine!. (Well, used to when I wrote that comment) -- REW */
+ {
+ static int lastjif;
+ static int nintr=0;
+
+ if (lastjif == jiffies) {
+ if (++nintr > IRQ_RATE_LIMIT) {
+ free_irq (dev->irq, dev_id);
+ printk (KERN_ERR "fs: Too many interrupts. Turning off interrupt %d.\n",
+ dev->irq);
+ }
+ } else {
+ lastjif = jiffies;
+ nintr = 0;
+ }
+ }
+#endif
+ fs_dprintk (FS_DEBUG_QUEUE, "in intr: txq %d txrq %d\n",
+ read_fs (dev, Q_EA (dev->hp_txq.offset)) -
+ read_fs (dev, Q_SA (dev->hp_txq.offset)),
+ read_fs (dev, Q_EA (dev->tx_relq.offset)) -
+ read_fs (dev, Q_SA (dev->tx_relq.offset)));
+
+ /* print the bits in the ISR register. */
+ if (fs_debug & FS_DEBUG_IRQ) {
+ /* The FS_DEBUG things are unnecessary here. But this way it is
+ clear for grep that these are debug prints. */
+ fs_dprintk (FS_DEBUG_IRQ, "IRQ status:");
+ for (i=0;i<27;i++)
+ if (status & (1 << i))
+ fs_dprintk (FS_DEBUG_IRQ, " %s", irq_bitname[i]);
+ fs_dprintk (FS_DEBUG_IRQ, "\n");
+ }
+
+ if (status & ISR_RBRQ0_W) {
+ fs_dprintk (FS_DEBUG_IRQ, "Iiiin-coming (0)!!!!\n");
+ process_incoming (dev, &dev->rx_rq[0]);
+ /* items mentioned on RBRQ0 are from FP 0 or 1. */
+ top_off_fp (dev, &dev->rx_fp[0], GFP_ATOMIC);
+ top_off_fp (dev, &dev->rx_fp[1], GFP_ATOMIC);
+ }
+
+ if (status & ISR_RBRQ1_W) {
+ fs_dprintk (FS_DEBUG_IRQ, "Iiiin-coming (1)!!!!\n");
+ process_incoming (dev, &dev->rx_rq[1]);
+ top_off_fp (dev, &dev->rx_fp[2], GFP_ATOMIC);
+ top_off_fp (dev, &dev->rx_fp[3], GFP_ATOMIC);
+ }
+
+ if (status & ISR_RBRQ2_W) {
+ fs_dprintk (FS_DEBUG_IRQ, "Iiiin-coming (2)!!!!\n");
+ process_incoming (dev, &dev->rx_rq[2]);
+ top_off_fp (dev, &dev->rx_fp[4], GFP_ATOMIC);
+ top_off_fp (dev, &dev->rx_fp[5], GFP_ATOMIC);
+ }
+
+ if (status & ISR_RBRQ3_W) {
+ fs_dprintk (FS_DEBUG_IRQ, "Iiiin-coming (3)!!!!\n");
+ process_incoming (dev, &dev->rx_rq[3]);
+ top_off_fp (dev, &dev->rx_fp[6], GFP_ATOMIC);
+ top_off_fp (dev, &dev->rx_fp[7], GFP_ATOMIC);
+ }
+
+ if (status & ISR_CSQ_W) {
+ fs_dprintk (FS_DEBUG_IRQ, "Command executed ok!\n");
+ process_return_queue (dev, &dev->st_q);
+ }
+
+ if (status & ISR_TBRQ_W) {
+ fs_dprintk (FS_DEBUG_IRQ, "Data tramsitted!\n");
+ process_txdone_queue (dev, &dev->tx_relq);
+ }
+
+ func_exit ();
+ return IRQ_HANDLED;
+}
+
+
+#ifdef FS_POLL_FREQ
+static void fs_poll (unsigned long data)
+{
+ struct fs_dev *dev = (struct fs_dev *) data;
+
+ fs_irq (0, dev);
+ dev->timer.expires = jiffies + FS_POLL_FREQ;
+ add_timer (&dev->timer);
+}
+#endif
+
+static int fs_init(struct fs_dev *dev)
+{
+ struct pci_dev *pci_dev;
+ int isr, to;
+ int i;
+
+ func_enter ();
+ pci_dev = dev->pci_dev;
+
+ printk (KERN_INFO "found a FireStream %d card, base %16llx, irq%d.\n",
+ IS_FS50(dev)?50:155,
+ (unsigned long long)pci_resource_start(pci_dev, 0),
+ dev->pci_dev->irq);
+
+ if (fs_debug & FS_DEBUG_INIT)
+ my_hd ((unsigned char *) dev, sizeof (*dev));
+
+ undocumented_pci_fix (pci_dev);
+
+ dev->hw_base = pci_resource_start(pci_dev, 0);
+
+ dev->base = ioremap(dev->hw_base, 0x1000);
+
+ reset_chip (dev);
+
+ write_fs (dev, SARMODE0, 0
+ | (0 * SARMODE0_SHADEN) /* We don't use shadow registers. */
+ | (1 * SARMODE0_INTMODE_READCLEAR)
+ | (1 * SARMODE0_CWRE)
+ | (IS_FS50(dev) ? SARMODE0_PRPWT_FS50_5:
+ SARMODE0_PRPWT_FS155_3)
+ | (1 * SARMODE0_CALSUP_1)
+ | (IS_FS50(dev) ? (0
+ | SARMODE0_RXVCS_32
+ | SARMODE0_ABRVCS_32
+ | SARMODE0_TXVCS_32):
+ (0
+ | SARMODE0_RXVCS_1k
+ | SARMODE0_ABRVCS_1k
+ | SARMODE0_TXVCS_1k)));
+
+ /* 10ms * 100 is 1 second. That should be enough, as AN3:9 says it takes
+ 1ms. */
+ to = 100;
+ while (--to) {
+ isr = read_fs (dev, ISR);
+
+ /* This bit is documented as "RESERVED" */
+ if (isr & ISR_INIT_ERR) {
+ printk (KERN_ERR "Error initializing the FS... \n");
+ goto unmap;
+ }
+ if (isr & ISR_INIT) {
+ fs_dprintk (FS_DEBUG_INIT, "Ha! Initialized OK!\n");
+ break;
+ }
+
+ /* Try again after 10ms. */
+ msleep(10);
+ }
+
+ if (!to) {
+ printk (KERN_ERR "timeout initializing the FS... \n");
+ goto unmap;
+ }
+
+ /* XXX fix for fs155 */
+ dev->channel_mask = 0x1f;
+ dev->channo = 0;
+
+ /* AN3: 10 */
+ write_fs (dev, SARMODE1, 0
+ | (fs_keystream * SARMODE1_DEFHEC) /* XXX PHY */
+ | ((loopback == 1) * SARMODE1_TSTLP) /* XXX Loopback mode enable... */
+ | (1 * SARMODE1_DCRM)
+ | (1 * SARMODE1_DCOAM)
+ | (0 * SARMODE1_OAMCRC)
+ | (0 * SARMODE1_DUMPE)
+ | (0 * SARMODE1_GPLEN)
+ | (0 * SARMODE1_GNAM)
+ | (0 * SARMODE1_GVAS)
+ | (0 * SARMODE1_GPAS)
+ | (1 * SARMODE1_GPRI)
+ | (0 * SARMODE1_PMS)
+ | (0 * SARMODE1_GFCR)
+ | (1 * SARMODE1_HECM2)
+ | (1 * SARMODE1_HECM1)
+ | (1 * SARMODE1_HECM0)
+ | (1 << 12) /* That's what hang's driver does. Program to 0 */
+ | (0 * 0xff) /* XXX FS155 */);
+
+
+ /* Cal prescale etc */
+
+ /* AN3: 11 */
+ write_fs (dev, TMCONF, 0x0000000f);
+ write_fs (dev, CALPRESCALE, 0x01010101 * num);
+ write_fs (dev, 0x80, 0x000F00E4);
+
+ /* AN3: 12 */
+ write_fs (dev, CELLOSCONF, 0
+ | ( 0 * CELLOSCONF_CEN)
+ | ( CELLOSCONF_SC1)
+ | (0x80 * CELLOSCONF_COBS)
+ | (num * CELLOSCONF_COPK) /* Changed from 0xff to 0x5a */
+ | (num * CELLOSCONF_COST));/* after a hint from Hang.
+ * performance jumped 50->70... */
+
+ /* Magic value by Hang */
+ write_fs (dev, CELLOSCONF_COST, 0x0B809191);
+
+ if (IS_FS50 (dev)) {
+ write_fs (dev, RAS0, RAS0_DCD_XHLT);
+ dev->atm_dev->ci_range.vpi_bits = 12;
+ dev->atm_dev->ci_range.vci_bits = 16;
+ dev->nchannels = FS50_NR_CHANNELS;
+ } else {
+ write_fs (dev, RAS0, RAS0_DCD_XHLT
+ | (((1 << FS155_VPI_BITS) - 1) * RAS0_VPSEL)
+ | (((1 << FS155_VCI_BITS) - 1) * RAS0_VCSEL));
+ /* We can chose the split arbitrarily. We might be able to
+ support more. Whatever. This should do for now. */
+ dev->atm_dev->ci_range.vpi_bits = FS155_VPI_BITS;
+ dev->atm_dev->ci_range.vci_bits = FS155_VCI_BITS;
+
+ /* Address bits we can't use should be compared to 0. */
+ write_fs (dev, RAC, 0);
+
+ /* Manual (AN9, page 6) says ASF1=0 means compare Utopia address
+ * too. I can't find ASF1 anywhere. Anyway, we AND with just the
+ * other bits, then compare with 0, which is exactly what we
+ * want. */
+ write_fs (dev, RAM, (1 << (28 - FS155_VPI_BITS - FS155_VCI_BITS)) - 1);
+ dev->nchannels = FS155_NR_CHANNELS;
+ }
+ dev->atm_vccs = kcalloc (dev->nchannels, sizeof (struct atm_vcc *),
+ GFP_KERNEL);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc atmvccs: %p(%Zd)\n",
+ dev->atm_vccs, dev->nchannels * sizeof (struct atm_vcc *));
+
+ if (!dev->atm_vccs) {
+ printk (KERN_WARNING "Couldn't allocate memory for VCC buffers. Woops!\n");
+ /* XXX Clean up..... */
+ goto unmap;
+ }
+
+ dev->tx_inuse = kzalloc (dev->nchannels / 8 /* bits/byte */ , GFP_KERNEL);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc tx_inuse: %p(%d)\n",
+ dev->atm_vccs, dev->nchannels / 8);
+
+ if (!dev->tx_inuse) {
+ printk (KERN_WARNING "Couldn't allocate memory for tx_inuse bits!\n");
+ /* XXX Clean up..... */
+ goto unmap;
+ }
+ /* -- RAS1 : FS155 and 50 differ. Default (0) should be OK for both */
+ /* -- RAS2 : FS50 only: Default is OK. */
+
+ /* DMAMODE, default should be OK. -- REW */
+ write_fs (dev, DMAMR, DMAMR_TX_MODE_FULL);
+
+ init_q (dev, &dev->hp_txq, TX_PQ(TXQ_HP), TXQ_NENTRIES, 0);
+ init_q (dev, &dev->lp_txq, TX_PQ(TXQ_LP), TXQ_NENTRIES, 0);
+ init_q (dev, &dev->tx_relq, TXB_RQ, TXQ_NENTRIES, 1);
+ init_q (dev, &dev->st_q, ST_Q, TXQ_NENTRIES, 1);
+
+ for (i=0;i < FS_NR_FREE_POOLS;i++) {
+ init_fp (dev, &dev->rx_fp[i], RXB_FP(i),
+ rx_buf_sizes[i], rx_pool_sizes[i]);
+ top_off_fp (dev, &dev->rx_fp[i], GFP_KERNEL);
+ }
+
+
+ for (i=0;i < FS_NR_RX_QUEUES;i++)
+ init_q (dev, &dev->rx_rq[i], RXB_RQ(i), RXRQ_NENTRIES, 1);
+
+ dev->irq = pci_dev->irq;
+ if (request_irq (dev->irq, fs_irq, IRQF_SHARED, "firestream", dev)) {
+ printk (KERN_WARNING "couldn't get irq %d for firestream.\n", pci_dev->irq);
+ /* XXX undo all previous stuff... */
+ goto unmap;
+ }
+ fs_dprintk (FS_DEBUG_INIT, "Grabbed irq %d for dev at %p.\n", dev->irq, dev);
+
+ /* We want to be notified of most things. Just the statistics count
+ overflows are not interesting */
+ write_fs (dev, IMR, 0
+ | ISR_RBRQ0_W
+ | ISR_RBRQ1_W
+ | ISR_RBRQ2_W
+ | ISR_RBRQ3_W
+ | ISR_TBRQ_W
+ | ISR_CSQ_W);
+
+ write_fs (dev, SARMODE0, 0
+ | (0 * SARMODE0_SHADEN) /* We don't use shadow registers. */
+ | (1 * SARMODE0_GINT)
+ | (1 * SARMODE0_INTMODE_READCLEAR)
+ | (0 * SARMODE0_CWRE)
+ | (IS_FS50(dev)?SARMODE0_PRPWT_FS50_5:
+ SARMODE0_PRPWT_FS155_3)
+ | (1 * SARMODE0_CALSUP_1)
+ | (IS_FS50 (dev)?(0
+ | SARMODE0_RXVCS_32
+ | SARMODE0_ABRVCS_32
+ | SARMODE0_TXVCS_32):
+ (0
+ | SARMODE0_RXVCS_1k
+ | SARMODE0_ABRVCS_1k
+ | SARMODE0_TXVCS_1k))
+ | (1 * SARMODE0_RUN));
+
+ init_phy (dev, PHY_NTC_INIT);
+
+ if (loopback == 2) {
+ write_phy (dev, 0x39, 0x000e);
+ }
+
+#ifdef FS_POLL_FREQ
+ init_timer (&dev->timer);
+ dev->timer.data = (unsigned long) dev;
+ dev->timer.function = fs_poll;
+ dev->timer.expires = jiffies + FS_POLL_FREQ;
+ add_timer (&dev->timer);
+#endif
+
+ dev->atm_dev->dev_data = dev;
+
+ func_exit ();
+ return 0;
+unmap:
+ iounmap(dev->base);
+ return 1;
+}
+
+static int firestream_init_one(struct pci_dev *pci_dev,
+ const struct pci_device_id *ent)
+{
+ struct atm_dev *atm_dev;
+ struct fs_dev *fs_dev;
+
+ if (pci_enable_device(pci_dev))
+ goto err_out;
+
+ fs_dev = kzalloc (sizeof (struct fs_dev), GFP_KERNEL);
+ fs_dprintk (FS_DEBUG_ALLOC, "Alloc fs-dev: %p(%Zd)\n",
+ fs_dev, sizeof (struct fs_dev));
+ if (!fs_dev)
+ goto err_out;
+ atm_dev = atm_dev_register("fs", &pci_dev->dev, &ops, -1, NULL);
+ if (!atm_dev)
+ goto err_out_free_fs_dev;
+
+ fs_dev->pci_dev = pci_dev;
+ fs_dev->atm_dev = atm_dev;
+ fs_dev->flags = ent->driver_data;
+
+ if (fs_init(fs_dev))
+ goto err_out_free_atm_dev;
+
+ fs_dev->next = fs_boards;
+ fs_boards = fs_dev;
+ return 0;
+
+ err_out_free_atm_dev:
+ atm_dev_deregister(atm_dev);
+ err_out_free_fs_dev:
+ kfree(fs_dev);
+ err_out:
+ return -ENODEV;
+}
+
+static void firestream_remove_one(struct pci_dev *pdev)
+{
+ int i;
+ struct fs_dev *dev, *nxtdev;
+ struct fs_vcc *vcc;
+ struct FS_BPENTRY *fp, *nxt;
+
+ func_enter ();
+
+#if 0
+ printk ("hptxq:\n");
+ for (i=0;i<60;i++) {
+ printk ("%d: %08x %08x %08x %08x \n",
+ i, pq[qp].cmd, pq[qp].p0, pq[qp].p1, pq[qp].p2);
+ qp++;
+ if (qp >= 60) qp = 0;
+ }
+
+ printk ("descriptors:\n");
+ for (i=0;i<60;i++) {
+ printk ("%d: %p: %08x %08x %p %p\n",
+ i, da[qd], dq[qd].flags, dq[qd].bsa, dq[qd].skb, dq[qd].dev);
+ qd++;
+ if (qd >= 60) qd = 0;
+ }
+#endif
+
+ for (dev = fs_boards;dev != NULL;dev=nxtdev) {
+ fs_dprintk (FS_DEBUG_CLEANUP, "Releasing resources for dev at %p.\n", dev);
+
+ /* XXX Hit all the tx channels too! */
+
+ for (i=0;i < dev->nchannels;i++) {
+ if (dev->atm_vccs[i]) {
+ vcc = FS_VCC (dev->atm_vccs[i]);
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_TX_PURGE_INH | QE_CMD_IMM_INQ | vcc->channo, 0,0,0);
+ submit_command (dev, &dev->hp_txq,
+ QE_CMD_RX_PURGE_INH | QE_CMD_IMM_INQ | vcc->channo, 0,0,0);
+
+ }
+ }
+
+ /* XXX Wait a while for the chip to release all buffers. */
+
+ for (i=0;i < FS_NR_FREE_POOLS;i++) {
+ for (fp=bus_to_virt (read_fs (dev, FP_SA(dev->rx_fp[i].offset)));
+ !(fp->flags & FP_FLAGS_EPI);fp = nxt) {
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-skb: %p\n", fp->skb);
+ dev_kfree_skb_any (fp->skb);
+ nxt = bus_to_virt (fp->next);
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-d: %p\n", fp);
+ kfree (fp);
+ }
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-skb: %p\n", fp->skb);
+ dev_kfree_skb_any (fp->skb);
+ fs_dprintk (FS_DEBUG_ALLOC, "Free rec-d: %p\n", fp);
+ kfree (fp);
+ }
+
+ /* Hang the chip in "reset", prevent it clobbering memory that is
+ no longer ours. */
+ reset_chip (dev);
+
+ fs_dprintk (FS_DEBUG_CLEANUP, "Freeing irq%d.\n", dev->irq);
+ free_irq (dev->irq, dev);
+ del_timer_sync (&dev->timer);
+
+ atm_dev_deregister(dev->atm_dev);
+ free_queue (dev, &dev->hp_txq);
+ free_queue (dev, &dev->lp_txq);
+ free_queue (dev, &dev->tx_relq);
+ free_queue (dev, &dev->st_q);
+
+ fs_dprintk (FS_DEBUG_ALLOC, "Free atmvccs: %p\n", dev->atm_vccs);
+ kfree (dev->atm_vccs);
+
+ for (i=0;i< FS_NR_FREE_POOLS;i++)
+ free_freepool (dev, &dev->rx_fp[i]);
+
+ for (i=0;i < FS_NR_RX_QUEUES;i++)
+ free_queue (dev, &dev->rx_rq[i]);
+
+ iounmap(dev->base);
+ fs_dprintk (FS_DEBUG_ALLOC, "Free fs-dev: %p\n", dev);
+ nxtdev = dev->next;
+ kfree (dev);
+ }
+
+ func_exit ();
+}
+
+static struct pci_device_id firestream_pci_tbl[] = {
+ { PCI_VDEVICE(FUJITSU_ME, PCI_DEVICE_ID_FUJITSU_FS50), FS_IS50},
+ { PCI_VDEVICE(FUJITSU_ME, PCI_DEVICE_ID_FUJITSU_FS155), FS_IS155},
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, firestream_pci_tbl);
+
+static struct pci_driver firestream_driver = {
+ .name = "firestream",
+ .id_table = firestream_pci_tbl,
+ .probe = firestream_init_one,
+ .remove = firestream_remove_one,
+};
+
+static int __init firestream_init_module (void)
+{
+ int error;
+
+ func_enter ();
+ error = pci_register_driver(&firestream_driver);
+ func_exit ();
+ return error;
+}
+
+static void __exit firestream_cleanup_module(void)
+{
+ pci_unregister_driver(&firestream_driver);
+}
+
+module_init(firestream_init_module);
+module_exit(firestream_cleanup_module);
+
+MODULE_LICENSE("GPL");
+
+
+
diff --git a/drivers/atm/firestream.h b/drivers/atm/firestream.h
new file mode 100644
index 000000000..364eded31
--- /dev/null
+++ b/drivers/atm/firestream.h
@@ -0,0 +1,517 @@
+/* drivers/atm/firestream.h - FireStream 155 (MB86697) and
+ * FireStream 50 (MB86695) device driver
+ */
+
+/* Written & (C) 2000 by R.E.Wolff@BitWizard.nl
+ * Copied snippets from zatm.c by Werner Almesberger, EPFL LRC/ICA
+ * and ambassador.c Copyright (C) 1995-1999 Madge Networks Ltd
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian
+ system and in the file COPYING in the Linux kernel source.
+*/
+
+
+/***********************************************************************
+ * first the defines for the chip. *
+ ***********************************************************************/
+
+
+/********************* General chip parameters. ************************/
+
+#define FS_NR_FREE_POOLS 8
+#define FS_NR_RX_QUEUES 4
+
+
+/********************* queues and queue access macros ******************/
+
+
+/* A queue entry. */
+struct FS_QENTRY {
+ u32 cmd;
+ u32 p0, p1, p2;
+};
+
+
+/* A freepool entry. */
+struct FS_BPENTRY {
+ u32 flags;
+ u32 next;
+ u32 bsa;
+ u32 aal_bufsize;
+
+ /* The hardware doesn't look at this, but we need the SKB somewhere... */
+ struct sk_buff *skb;
+ struct freepool *fp;
+ struct fs_dev *dev;
+};
+
+
+#define STATUS_CODE(qe) ((qe->cmd >> 22) & 0x3f)
+
+
+/* OFFSETS against the base of a QUEUE... */
+#define QSA 0x00
+#define QEA 0x04
+#define QRP 0x08
+#define QWP 0x0c
+#define QCNF 0x10 /* Only for Release queues! */
+/* Not for the transmit pending queue. */
+
+
+/* OFFSETS against the base of a FREE POOL... */
+#define FPCNF 0x00
+#define FPSA 0x04
+#define FPEA 0x08
+#define FPCNT 0x0c
+#define FPCTU 0x10
+
+#define Q_SA(b) (b + QSA )
+#define Q_EA(b) (b + QEA )
+#define Q_RP(b) (b + QRP )
+#define Q_WP(b) (b + QWP )
+#define Q_CNF(b) (b + QCNF)
+
+#define FP_CNF(b) (b + FPCNF)
+#define FP_SA(b) (b + FPSA)
+#define FP_EA(b) (b + FPEA)
+#define FP_CNT(b) (b + FPCNT)
+#define FP_CTU(b) (b + FPCTU)
+
+/* bits in a queue register. */
+#define Q_FULL 0x1
+#define Q_EMPTY 0x2
+#define Q_INCWRAP 0x4
+#define Q_ADDR_MASK 0xfffffff0
+
+/* bits in a FreePool config register */
+#define RBFP_RBS (0x1 << 16)
+#define RBFP_RBSVAL (0x1 << 15)
+#define RBFP_CME (0x1 << 12)
+#define RBFP_DLP (0x1 << 11)
+#define RBFP_BFPWT (0x1 << 0)
+
+
+
+
+/* FireStream commands. */
+#define QE_CMD_NULL (0x00 << 22)
+#define QE_CMD_REG_RD (0x01 << 22)
+#define QE_CMD_REG_RDM (0x02 << 22)
+#define QE_CMD_REG_WR (0x03 << 22)
+#define QE_CMD_REG_WRM (0x04 << 22)
+#define QE_CMD_CONFIG_TX (0x05 << 22)
+#define QE_CMD_CONFIG_RX (0x06 << 22)
+#define QE_CMD_PRP_RD (0x07 << 22)
+#define QE_CMD_PRP_RDM (0x2a << 22)
+#define QE_CMD_PRP_WR (0x09 << 22)
+#define QE_CMD_PRP_WRM (0x2b << 22)
+#define QE_CMD_RX_EN (0x0a << 22)
+#define QE_CMD_RX_PURGE (0x0b << 22)
+#define QE_CMD_RX_PURGE_INH (0x0c << 22)
+#define QE_CMD_TX_EN (0x0d << 22)
+#define QE_CMD_TX_PURGE (0x0e << 22)
+#define QE_CMD_TX_PURGE_INH (0x0f << 22)
+#define QE_CMD_RST_CG (0x10 << 22)
+#define QE_CMD_SET_CG (0x11 << 22)
+#define QE_CMD_RST_CLP (0x12 << 22)
+#define QE_CMD_SET_CLP (0x13 << 22)
+#define QE_CMD_OVERRIDE (0x14 << 22)
+#define QE_CMD_ADD_BFP (0x15 << 22)
+#define QE_CMD_DUMP_TX (0x16 << 22)
+#define QE_CMD_DUMP_RX (0x17 << 22)
+#define QE_CMD_LRAM_RD (0x18 << 22)
+#define QE_CMD_LRAM_RDM (0x28 << 22)
+#define QE_CMD_LRAM_WR (0x19 << 22)
+#define QE_CMD_LRAM_WRM (0x29 << 22)
+#define QE_CMD_LRAM_BSET (0x1a << 22)
+#define QE_CMD_LRAM_BCLR (0x1b << 22)
+#define QE_CMD_CONFIG_SEGM (0x1c << 22)
+#define QE_CMD_READ_SEGM (0x1d << 22)
+#define QE_CMD_CONFIG_ROUT (0x1e << 22)
+#define QE_CMD_READ_ROUT (0x1f << 22)
+#define QE_CMD_CONFIG_TM (0x20 << 22)
+#define QE_CMD_READ_TM (0x21 << 22)
+#define QE_CMD_CONFIG_TXBM (0x22 << 22)
+#define QE_CMD_READ_TXBM (0x23 << 22)
+#define QE_CMD_CONFIG_RXBM (0x24 << 22)
+#define QE_CMD_READ_RXBM (0x25 << 22)
+#define QE_CMD_CONFIG_REAS (0x26 << 22)
+#define QE_CMD_READ_REAS (0x27 << 22)
+
+#define QE_TRANSMIT_DE (0x0 << 30)
+#define QE_CMD_LINKED (0x1 << 30)
+#define QE_CMD_IMM (0x2 << 30)
+#define QE_CMD_IMM_INQ (0x3 << 30)
+
+#define TD_EPI (0x1 << 27)
+#define TD_COMMAND (0x1 << 28)
+
+#define TD_DATA (0x0 << 29)
+#define TD_RM_CELL (0x1 << 29)
+#define TD_OAM_CELL (0x2 << 29)
+#define TD_OAM_CELL_SEGMENT (0x3 << 29)
+
+#define TD_BPI (0x1 << 20)
+
+#define FP_FLAGS_EPI (0x1 << 27)
+
+
+#define TX_PQ(i) (0x00 + (i) * 0x10)
+#define TXB_RQ (0x20)
+#define ST_Q (0x48)
+#define RXB_FP(i) (0x90 + (i) * 0x14)
+#define RXB_RQ(i) (0x134 + (i) * 0x14)
+
+
+#define TXQ_HP 0
+#define TXQ_LP 1
+
+/* Phew. You don't want to know how many revisions these simple queue
+ * address macros went through before I got them nice and compact as
+ * they are now. -- REW
+ */
+
+
+/* And now for something completely different:
+ * The rest of the registers... */
+
+
+#define CMDR0 0x34
+#define CMDR1 0x38
+#define CMDR2 0x3c
+#define CMDR3 0x40
+
+
+#define SARMODE0 0x5c
+
+#define SARMODE0_TXVCS_0 (0x0 << 0)
+#define SARMODE0_TXVCS_1k (0x1 << 0)
+#define SARMODE0_TXVCS_2k (0x2 << 0)
+#define SARMODE0_TXVCS_4k (0x3 << 0)
+#define SARMODE0_TXVCS_8k (0x4 << 0)
+#define SARMODE0_TXVCS_16k (0x5 << 0)
+#define SARMODE0_TXVCS_32k (0x6 << 0)
+#define SARMODE0_TXVCS_64k (0x7 << 0)
+#define SARMODE0_TXVCS_32 (0x8 << 0)
+
+#define SARMODE0_ABRVCS_0 (0x0 << 4)
+#define SARMODE0_ABRVCS_512 (0x1 << 4)
+#define SARMODE0_ABRVCS_1k (0x2 << 4)
+#define SARMODE0_ABRVCS_2k (0x3 << 4)
+#define SARMODE0_ABRVCS_4k (0x4 << 4)
+#define SARMODE0_ABRVCS_8k (0x5 << 4)
+#define SARMODE0_ABRVCS_16k (0x6 << 4)
+#define SARMODE0_ABRVCS_32k (0x7 << 4)
+#define SARMODE0_ABRVCS_32 (0x9 << 4) /* The others are "8", this one really has to
+ be 9. Tell me you don't believe me. -- REW */
+
+#define SARMODE0_RXVCS_0 (0x0 << 8)
+#define SARMODE0_RXVCS_1k (0x1 << 8)
+#define SARMODE0_RXVCS_2k (0x2 << 8)
+#define SARMODE0_RXVCS_4k (0x3 << 8)
+#define SARMODE0_RXVCS_8k (0x4 << 8)
+#define SARMODE0_RXVCS_16k (0x5 << 8)
+#define SARMODE0_RXVCS_32k (0x6 << 8)
+#define SARMODE0_RXVCS_64k (0x7 << 8)
+#define SARMODE0_RXVCS_32 (0x8 << 8)
+
+#define SARMODE0_CALSUP_1 (0x0 << 12)
+#define SARMODE0_CALSUP_2 (0x1 << 12)
+#define SARMODE0_CALSUP_3 (0x2 << 12)
+#define SARMODE0_CALSUP_4 (0x3 << 12)
+
+#define SARMODE0_PRPWT_FS50_0 (0x0 << 14)
+#define SARMODE0_PRPWT_FS50_2 (0x1 << 14)
+#define SARMODE0_PRPWT_FS50_5 (0x2 << 14)
+#define SARMODE0_PRPWT_FS50_11 (0x3 << 14)
+
+#define SARMODE0_PRPWT_FS155_0 (0x0 << 14)
+#define SARMODE0_PRPWT_FS155_1 (0x1 << 14)
+#define SARMODE0_PRPWT_FS155_2 (0x2 << 14)
+#define SARMODE0_PRPWT_FS155_3 (0x3 << 14)
+
+#define SARMODE0_SRTS0 (0x1 << 23)
+#define SARMODE0_SRTS1 (0x1 << 24)
+
+#define SARMODE0_RUN (0x1 << 25)
+
+#define SARMODE0_UNLOCK (0x1 << 26)
+#define SARMODE0_CWRE (0x1 << 27)
+
+
+#define SARMODE0_INTMODE_READCLEAR (0x0 << 28)
+#define SARMODE0_INTMODE_READNOCLEAR (0x1 << 28)
+#define SARMODE0_INTMODE_READNOCLEARINHIBIT (0x2 << 28)
+#define SARMODE0_INTMODE_READCLEARINHIBIT (0x3 << 28) /* Tell me you don't believe me. */
+
+#define SARMODE0_GINT (0x1 << 30)
+#define SARMODE0_SHADEN (0x1 << 31)
+
+
+#define SARMODE1 0x60
+
+
+#define SARMODE1_TRTL_SHIFT 0 /* Program to 0 */
+#define SARMODE1_RRTL_SHIFT 4 /* Program to 0 */
+
+#define SARMODE1_TAGM (0x1 << 8) /* Program to 0 */
+
+#define SARMODE1_HECM0 (0x1 << 9)
+#define SARMODE1_HECM1 (0x1 << 10)
+#define SARMODE1_HECM2 (0x1 << 11)
+
+#define SARMODE1_GFCE (0x1 << 14)
+#define SARMODE1_GFCR (0x1 << 15)
+#define SARMODE1_PMS (0x1 << 18)
+#define SARMODE1_GPRI (0x1 << 19)
+#define SARMODE1_GPAS (0x1 << 20)
+#define SARMODE1_GVAS (0x1 << 21)
+#define SARMODE1_GNAM (0x1 << 22)
+#define SARMODE1_GPLEN (0x1 << 23)
+#define SARMODE1_DUMPE (0x1 << 24)
+#define SARMODE1_OAMCRC (0x1 << 25)
+#define SARMODE1_DCOAM (0x1 << 26)
+#define SARMODE1_DCRM (0x1 << 27)
+#define SARMODE1_TSTLP (0x1 << 28)
+#define SARMODE1_DEFHEC (0x1 << 29)
+
+
+#define ISR 0x64
+#define IUSR 0x68
+#define IMR 0x6c
+
+#define ISR_LPCO (0x1 << 0)
+#define ISR_DPCO (0x1 << 1)
+#define ISR_RBRQ0_W (0x1 << 2)
+#define ISR_RBRQ1_W (0x1 << 3)
+#define ISR_RBRQ2_W (0x1 << 4)
+#define ISR_RBRQ3_W (0x1 << 5)
+#define ISR_RBRQ0_NF (0x1 << 6)
+#define ISR_RBRQ1_NF (0x1 << 7)
+#define ISR_RBRQ2_NF (0x1 << 8)
+#define ISR_RBRQ3_NF (0x1 << 9)
+#define ISR_BFP_SC (0x1 << 10)
+#define ISR_INIT (0x1 << 11)
+#define ISR_INIT_ERR (0x1 << 12) /* Documented as "reserved" */
+#define ISR_USCEO (0x1 << 13)
+#define ISR_UPEC0 (0x1 << 14)
+#define ISR_VPFCO (0x1 << 15)
+#define ISR_CRCCO (0x1 << 16)
+#define ISR_HECO (0x1 << 17)
+#define ISR_TBRQ_W (0x1 << 18)
+#define ISR_TBRQ_NF (0x1 << 19)
+#define ISR_CTPQ_E (0x1 << 20)
+#define ISR_GFC_C0 (0x1 << 21)
+#define ISR_PCI_FTL (0x1 << 22)
+#define ISR_CSQ_W (0x1 << 23)
+#define ISR_CSQ_NF (0x1 << 24)
+#define ISR_EXT_INT (0x1 << 25)
+#define ISR_RXDMA_S (0x1 << 26)
+
+
+#define TMCONF 0x78
+/* Bits? */
+
+
+#define CALPRESCALE 0x7c
+/* Bits? */
+
+#define CELLOSCONF 0x84
+#define CELLOSCONF_COTS (0x1 << 28)
+#define CELLOSCONF_CEN (0x1 << 27)
+#define CELLOSCONF_SC8 (0x3 << 24)
+#define CELLOSCONF_SC4 (0x2 << 24)
+#define CELLOSCONF_SC2 (0x1 << 24)
+#define CELLOSCONF_SC1 (0x0 << 24)
+
+#define CELLOSCONF_COBS (0x1 << 16)
+#define CELLOSCONF_COPK (0x1 << 8)
+#define CELLOSCONF_COST (0x1 << 0)
+/* Bits? */
+
+#define RAS0 0x1bc
+#define RAS0_DCD_XHLT (0x1 << 31)
+
+#define RAS0_VPSEL (0x1 << 16)
+#define RAS0_VCSEL (0x1 << 0)
+
+#define RAS1 0x1c0
+#define RAS1_UTREG (0x1 << 5)
+
+
+#define DMAMR 0x1cc
+#define DMAMR_TX_MODE_FULL (0x0 << 0)
+#define DMAMR_TX_MODE_PART (0x1 << 0)
+#define DMAMR_TX_MODE_NONE (0x2 << 0) /* And 3 */
+
+
+
+#define RAS2 0x280
+
+#define RAS2_NNI (0x1 << 0)
+#define RAS2_USEL (0x1 << 1)
+#define RAS2_UBS (0x1 << 2)
+
+
+
+struct fs_transmit_config {
+ u32 flags;
+ u32 atm_hdr;
+ u32 TMC[4];
+ u32 spec;
+ u32 rtag[3];
+};
+
+#define TC_FLAGS_AAL5 (0x0 << 29)
+#define TC_FLAGS_TRANSPARENT_PAYLOAD (0x1 << 29)
+#define TC_FLAGS_TRANSPARENT_CELL (0x2 << 29)
+#define TC_FLAGS_STREAMING (0x1 << 28)
+#define TC_FLAGS_PACKET (0x0)
+#define TC_FLAGS_TYPE_ABR (0x0 << 22)
+#define TC_FLAGS_TYPE_CBR (0x1 << 22)
+#define TC_FLAGS_TYPE_VBR (0x2 << 22)
+#define TC_FLAGS_TYPE_UBR (0x3 << 22)
+#define TC_FLAGS_CAL0 (0x0 << 20)
+#define TC_FLAGS_CAL1 (0x1 << 20)
+#define TC_FLAGS_CAL2 (0x2 << 20)
+#define TC_FLAGS_CAL3 (0x3 << 20)
+
+
+#define RC_FLAGS_NAM (0x1 << 13)
+#define RC_FLAGS_RXBM_PSB (0x0 << 14)
+#define RC_FLAGS_RXBM_CIF (0x1 << 14)
+#define RC_FLAGS_RXBM_PMB (0x2 << 14)
+#define RC_FLAGS_RXBM_STR (0x4 << 14)
+#define RC_FLAGS_RXBM_SAF (0x6 << 14)
+#define RC_FLAGS_RXBM_POS (0x6 << 14)
+#define RC_FLAGS_BFPS (0x1 << 17)
+
+#define RC_FLAGS_BFPS_BFP (0x1 << 17)
+
+#define RC_FLAGS_BFPS_BFP0 (0x0 << 17)
+#define RC_FLAGS_BFPS_BFP1 (0x1 << 17)
+#define RC_FLAGS_BFPS_BFP2 (0x2 << 17)
+#define RC_FLAGS_BFPS_BFP3 (0x3 << 17)
+#define RC_FLAGS_BFPS_BFP4 (0x4 << 17)
+#define RC_FLAGS_BFPS_BFP5 (0x5 << 17)
+#define RC_FLAGS_BFPS_BFP6 (0x6 << 17)
+#define RC_FLAGS_BFPS_BFP7 (0x7 << 17)
+#define RC_FLAGS_BFPS_BFP01 (0x8 << 17)
+#define RC_FLAGS_BFPS_BFP23 (0x9 << 17)
+#define RC_FLAGS_BFPS_BFP45 (0xa << 17)
+#define RC_FLAGS_BFPS_BFP67 (0xb << 17)
+#define RC_FLAGS_BFPS_BFP07 (0xc << 17)
+#define RC_FLAGS_BFPS_BFP27 (0xd << 17)
+#define RC_FLAGS_BFPS_BFP47 (0xe << 17)
+
+#define RC_FLAGS_BFPP (0x1 << 21)
+#define RC_FLAGS_TEVC (0x1 << 22)
+#define RC_FLAGS_TEP (0x1 << 23)
+#define RC_FLAGS_AAL5 (0x0 << 24)
+#define RC_FLAGS_TRANSP (0x1 << 24)
+#define RC_FLAGS_TRANSC (0x2 << 24)
+#define RC_FLAGS_ML (0x1 << 27)
+#define RC_FLAGS_TRBRM (0x1 << 28)
+#define RC_FLAGS_PRI (0x1 << 29)
+#define RC_FLAGS_HOAM (0x1 << 30)
+#define RC_FLAGS_CRC10 (0x1 << 31)
+
+
+#define RAC 0x1c8
+#define RAM 0x1c4
+
+
+
+/************************************************************************
+ * Then the datastructures that the DRIVER uses. *
+ ************************************************************************/
+
+#define TXQ_NENTRIES 32
+#define RXRQ_NENTRIES 1024
+
+
+struct fs_vcc {
+ int channo;
+ wait_queue_head_t close_wait;
+ struct sk_buff *last_skb;
+};
+
+
+struct queue {
+ struct FS_QENTRY *sa, *ea;
+ int offset;
+};
+
+struct freepool {
+ int offset;
+ int bufsize;
+ int nr_buffers;
+ int n;
+};
+
+
+struct fs_dev {
+ struct fs_dev *next; /* other FS devices */
+ int flags;
+
+ unsigned char irq; /* IRQ */
+ struct pci_dev *pci_dev; /* PCI stuff */
+ struct atm_dev *atm_dev;
+ struct timer_list timer;
+
+ unsigned long hw_base; /* mem base address */
+ void __iomem *base; /* Mapping of base address */
+ int channo;
+ unsigned long channel_mask;
+
+ struct queue hp_txq, lp_txq, tx_relq, st_q;
+ struct freepool rx_fp[FS_NR_FREE_POOLS];
+ struct queue rx_rq[FS_NR_RX_QUEUES];
+
+ int nchannels;
+ struct atm_vcc **atm_vccs;
+ void *tx_inuse;
+ int ntxpckts;
+};
+
+
+
+
+/* Number of channesl that the FS50 supports. */
+#define FS50_CHANNEL_BITS 5
+#define FS50_NR_CHANNELS (1 << FS50_CHANNEL_BITS)
+
+
+#define FS_DEV(atm_dev) ((struct fs_dev *) (atm_dev)->dev_data)
+#define FS_VCC(atm_vcc) ((struct fs_vcc *) (atm_vcc)->dev_data)
+
+
+#define FS_IS50 0x1
+#define FS_IS155 0x2
+
+#define IS_FS50(dev) (dev->flags & FS_IS50)
+#define IS_FS155(dev) (dev->flags & FS_IS155)
+
+/* Within limits this is user-configurable. */
+/* Note: Currently the sum (10 -> 1k channels) is hardcoded in the driver. */
+#define FS155_VPI_BITS 4
+#define FS155_VCI_BITS 6
+
+#define FS155_CHANNEL_BITS (FS155_VPI_BITS + FS155_VCI_BITS)
+#define FS155_NR_CHANNELS (1 << FS155_CHANNEL_BITS)
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
new file mode 100644
index 000000000..decad35e4
--- /dev/null
+++ b/drivers/atm/fore200e.c
@@ -0,0 +1,3178 @@
+/*
+ A FORE Systems 200E-series driver for ATM on Linux.
+ Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2003.
+
+ Based on the PCA-200E driver from Uwe Dannowski (Uwe.Dannowski@inf.tu-dresden.de).
+
+ This driver simultaneously supports PCA-200E and SBA-200E adapters
+ on i386, alpha (untested), powerpc, sparc and sparc64 architectures.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/atm_suni.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <asm/io.h>
+#include <asm/string.h>
+#include <asm/page.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <linux/atomic.h>
+
+#ifdef CONFIG_SBUS
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <asm/idprom.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/pgtable.h>
+#endif
+
+#if defined(CONFIG_ATM_FORE200E_USE_TASKLET) /* defer interrupt work to a tasklet */
+#define FORE200E_USE_TASKLET
+#endif
+
+#if 0 /* enable the debugging code of the buffer supply queues */
+#define FORE200E_BSQ_DEBUG
+#endif
+
+#if 1 /* ensure correct handling of 52-byte AAL0 SDUs expected by atmdump-like apps */
+#define FORE200E_52BYTE_AAL0_SDU
+#endif
+
+#include "fore200e.h"
+#include "suni.h"
+
+#define FORE200E_VERSION "0.3e"
+
+#define FORE200E "fore200e: "
+
+#if 0 /* override .config */
+#define CONFIG_ATM_FORE200E_DEBUG 1
+#endif
+#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG > 0)
+#define DPRINTK(level, format, args...) do { if (CONFIG_ATM_FORE200E_DEBUG >= (level)) \
+ printk(FORE200E format, ##args); } while (0)
+#else
+#define DPRINTK(level, format, args...) do {} while (0)
+#endif
+
+
+#define FORE200E_ALIGN(addr, alignment) \
+ ((((unsigned long)(addr) + (alignment - 1)) & ~(alignment - 1)) - (unsigned long)(addr))
+
+#define FORE200E_DMA_INDEX(dma_addr, type, index) ((dma_addr) + (index) * sizeof(type))
+
+#define FORE200E_INDEX(virt_addr, type, index) (&((type *)(virt_addr))[ index ])
+
+#define FORE200E_NEXT_ENTRY(index, modulo) (index = ((index) + 1) % (modulo))
+
+#if 1
+#define ASSERT(expr) if (!(expr)) { \
+ printk(FORE200E "assertion failed! %s[%d]: %s\n", \
+ __func__, __LINE__, #expr); \
+ panic(FORE200E "%s", __func__); \
+ }
+#else
+#define ASSERT(expr) do {} while (0)
+#endif
+
+
+static const struct atmdev_ops fore200e_ops;
+static const struct fore200e_bus fore200e_bus[];
+
+static LIST_HEAD(fore200e_boards);
+
+
+MODULE_AUTHOR("Christophe Lizzi - credits to Uwe Dannowski and Heikki Vatiainen");
+MODULE_DESCRIPTION("FORE Systems 200E-series ATM driver - version " FORE200E_VERSION);
+MODULE_SUPPORTED_DEVICE("PCA-200E, SBA-200E");
+
+
+static const int fore200e_rx_buf_nbr[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ] = {
+ { BUFFER_S1_NBR, BUFFER_L1_NBR },
+ { BUFFER_S2_NBR, BUFFER_L2_NBR }
+};
+
+static const int fore200e_rx_buf_size[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ] = {
+ { BUFFER_S1_SIZE, BUFFER_L1_SIZE },
+ { BUFFER_S2_SIZE, BUFFER_L2_SIZE }
+};
+
+
+#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG > 0)
+static const char* fore200e_traffic_class[] = { "NONE", "UBR", "CBR", "VBR", "ABR", "ANY" };
+#endif
+
+
+#if 0 /* currently unused */
+static int
+fore200e_fore2atm_aal(enum fore200e_aal aal)
+{
+ switch(aal) {
+ case FORE200E_AAL0: return ATM_AAL0;
+ case FORE200E_AAL34: return ATM_AAL34;
+ case FORE200E_AAL5: return ATM_AAL5;
+ }
+
+ return -EINVAL;
+}
+#endif
+
+
+static enum fore200e_aal
+fore200e_atm2fore_aal(int aal)
+{
+ switch(aal) {
+ case ATM_AAL0: return FORE200E_AAL0;
+ case ATM_AAL34: return FORE200E_AAL34;
+ case ATM_AAL1:
+ case ATM_AAL2:
+ case ATM_AAL5: return FORE200E_AAL5;
+ }
+
+ return -EINVAL;
+}
+
+
+static char*
+fore200e_irq_itoa(int irq)
+{
+ static char str[8];
+ sprintf(str, "%d", irq);
+ return str;
+}
+
+
+/* allocate and align a chunk of memory intended to hold the data behing exchanged
+ between the driver and the adapter (using streaming DVMA) */
+
+static int
+fore200e_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk, int size, int alignment, int direction)
+{
+ unsigned long offset = 0;
+
+ if (alignment <= sizeof(int))
+ alignment = 0;
+
+ chunk->alloc_size = size + alignment;
+ chunk->align_size = size;
+ chunk->direction = direction;
+
+ chunk->alloc_addr = kzalloc(chunk->alloc_size, GFP_KERNEL | GFP_DMA);
+ if (chunk->alloc_addr == NULL)
+ return -ENOMEM;
+
+ if (alignment > 0)
+ offset = FORE200E_ALIGN(chunk->alloc_addr, alignment);
+
+ chunk->align_addr = chunk->alloc_addr + offset;
+
+ chunk->dma_addr = fore200e->bus->dma_map(fore200e, chunk->align_addr, chunk->align_size, direction);
+
+ return 0;
+}
+
+
+/* free a chunk of memory */
+
+static void
+fore200e_chunk_free(struct fore200e* fore200e, struct chunk* chunk)
+{
+ fore200e->bus->dma_unmap(fore200e, chunk->dma_addr, chunk->dma_size, chunk->direction);
+
+ kfree(chunk->alloc_addr);
+}
+
+
+static void
+fore200e_spin(int msecs)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(msecs);
+ while (time_before(jiffies, timeout));
+}
+
+
+static int
+fore200e_poll(struct fore200e* fore200e, volatile u32* addr, u32 val, int msecs)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(msecs);
+ int ok;
+
+ mb();
+ do {
+ if ((ok = (*addr == val)) || (*addr & STATUS_ERROR))
+ break;
+
+ } while (time_before(jiffies, timeout));
+
+#if 1
+ if (!ok) {
+ printk(FORE200E "cmd polling failed, got status 0x%08x, expected 0x%08x\n",
+ *addr, val);
+ }
+#endif
+
+ return ok;
+}
+
+
+static int
+fore200e_io_poll(struct fore200e* fore200e, volatile u32 __iomem *addr, u32 val, int msecs)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(msecs);
+ int ok;
+
+ do {
+ if ((ok = (fore200e->bus->read(addr) == val)))
+ break;
+
+ } while (time_before(jiffies, timeout));
+
+#if 1
+ if (!ok) {
+ printk(FORE200E "I/O polling failed, got status 0x%08x, expected 0x%08x\n",
+ fore200e->bus->read(addr), val);
+ }
+#endif
+
+ return ok;
+}
+
+
+static void
+fore200e_free_rx_buf(struct fore200e* fore200e)
+{
+ int scheme, magn, nbr;
+ struct buffer* buffer;
+
+ for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) {
+ for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) {
+
+ if ((buffer = fore200e->host_bsq[ scheme ][ magn ].buffer) != NULL) {
+
+ for (nbr = 0; nbr < fore200e_rx_buf_nbr[ scheme ][ magn ]; nbr++) {
+
+ struct chunk* data = &buffer[ nbr ].data;
+
+ if (data->alloc_addr != NULL)
+ fore200e_chunk_free(fore200e, data);
+ }
+ }
+ }
+ }
+}
+
+
+static void
+fore200e_uninit_bs_queue(struct fore200e* fore200e)
+{
+ int scheme, magn;
+
+ for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) {
+ for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) {
+
+ struct chunk* status = &fore200e->host_bsq[ scheme ][ magn ].status;
+ struct chunk* rbd_block = &fore200e->host_bsq[ scheme ][ magn ].rbd_block;
+
+ if (status->alloc_addr)
+ fore200e->bus->dma_chunk_free(fore200e, status);
+
+ if (rbd_block->alloc_addr)
+ fore200e->bus->dma_chunk_free(fore200e, rbd_block);
+ }
+ }
+}
+
+
+static int
+fore200e_reset(struct fore200e* fore200e, int diag)
+{
+ int ok;
+
+ fore200e->cp_monitor = fore200e->virt_base + FORE200E_CP_MONITOR_OFFSET;
+
+ fore200e->bus->write(BSTAT_COLD_START, &fore200e->cp_monitor->bstat);
+
+ fore200e->bus->reset(fore200e);
+
+ if (diag) {
+ ok = fore200e_io_poll(fore200e, &fore200e->cp_monitor->bstat, BSTAT_SELFTEST_OK, 1000);
+ if (ok == 0) {
+
+ printk(FORE200E "device %s self-test failed\n", fore200e->name);
+ return -ENODEV;
+ }
+
+ printk(FORE200E "device %s self-test passed\n", fore200e->name);
+
+ fore200e->state = FORE200E_STATE_RESET;
+ }
+
+ return 0;
+}
+
+
+static void
+fore200e_shutdown(struct fore200e* fore200e)
+{
+ printk(FORE200E "removing device %s at 0x%lx, IRQ %s\n",
+ fore200e->name, fore200e->phys_base,
+ fore200e_irq_itoa(fore200e->irq));
+
+ if (fore200e->state > FORE200E_STATE_RESET) {
+ /* first, reset the board to prevent further interrupts or data transfers */
+ fore200e_reset(fore200e, 0);
+ }
+
+ /* then, release all allocated resources */
+ switch(fore200e->state) {
+
+ case FORE200E_STATE_COMPLETE:
+ kfree(fore200e->stats);
+
+ case FORE200E_STATE_IRQ:
+ free_irq(fore200e->irq, fore200e->atm_dev);
+
+ case FORE200E_STATE_ALLOC_BUF:
+ fore200e_free_rx_buf(fore200e);
+
+ case FORE200E_STATE_INIT_BSQ:
+ fore200e_uninit_bs_queue(fore200e);
+
+ case FORE200E_STATE_INIT_RXQ:
+ fore200e->bus->dma_chunk_free(fore200e, &fore200e->host_rxq.status);
+ fore200e->bus->dma_chunk_free(fore200e, &fore200e->host_rxq.rpd);
+
+ case FORE200E_STATE_INIT_TXQ:
+ fore200e->bus->dma_chunk_free(fore200e, &fore200e->host_txq.status);
+ fore200e->bus->dma_chunk_free(fore200e, &fore200e->host_txq.tpd);
+
+ case FORE200E_STATE_INIT_CMDQ:
+ fore200e->bus->dma_chunk_free(fore200e, &fore200e->host_cmdq.status);
+
+ case FORE200E_STATE_INITIALIZE:
+ /* nothing to do for that state */
+
+ case FORE200E_STATE_START_FW:
+ /* nothing to do for that state */
+
+ case FORE200E_STATE_RESET:
+ /* nothing to do for that state */
+
+ case FORE200E_STATE_MAP:
+ fore200e->bus->unmap(fore200e);
+
+ case FORE200E_STATE_CONFIGURE:
+ /* nothing to do for that state */
+
+ case FORE200E_STATE_REGISTER:
+ /* XXX shouldn't we *start* by deregistering the device? */
+ atm_dev_deregister(fore200e->atm_dev);
+
+ case FORE200E_STATE_BLANK:
+ /* nothing to do for that state */
+ break;
+ }
+}
+
+
+#ifdef CONFIG_PCI
+
+static u32 fore200e_pca_read(volatile u32 __iomem *addr)
+{
+ /* on big-endian hosts, the board is configured to convert
+ the endianess of slave RAM accesses */
+ return le32_to_cpu(readl(addr));
+}
+
+
+static void fore200e_pca_write(u32 val, volatile u32 __iomem *addr)
+{
+ /* on big-endian hosts, the board is configured to convert
+ the endianess of slave RAM accesses */
+ writel(cpu_to_le32(val), addr);
+}
+
+
+static u32
+fore200e_pca_dma_map(struct fore200e* fore200e, void* virt_addr, int size, int direction)
+{
+ u32 dma_addr = dma_map_single(&((struct pci_dev *) fore200e->bus_dev)->dev, virt_addr, size, direction);
+
+ DPRINTK(3, "PCI DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d, --> dma_addr = 0x%08x\n",
+ virt_addr, size, direction, dma_addr);
+
+ return dma_addr;
+}
+
+
+static void
+fore200e_pca_dma_unmap(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
+{
+ DPRINTK(3, "PCI DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d\n",
+ dma_addr, size, direction);
+
+ dma_unmap_single(&((struct pci_dev *) fore200e->bus_dev)->dev, dma_addr, size, direction);
+}
+
+
+static void
+fore200e_pca_dma_sync_for_cpu(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
+{
+ DPRINTK(3, "PCI DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
+
+ dma_sync_single_for_cpu(&((struct pci_dev *) fore200e->bus_dev)->dev, dma_addr, size, direction);
+}
+
+static void
+fore200e_pca_dma_sync_for_device(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
+{
+ DPRINTK(3, "PCI DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
+
+ dma_sync_single_for_device(&((struct pci_dev *) fore200e->bus_dev)->dev, dma_addr, size, direction);
+}
+
+
+/* allocate a DMA consistent chunk of memory intended to act as a communication mechanism
+ (to hold descriptors, status, queues, etc.) shared by the driver and the adapter */
+
+static int
+fore200e_pca_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk,
+ int size, int nbr, int alignment)
+{
+ /* returned chunks are page-aligned */
+ chunk->alloc_size = size * nbr;
+ chunk->alloc_addr = dma_alloc_coherent(&((struct pci_dev *) fore200e->bus_dev)->dev,
+ chunk->alloc_size,
+ &chunk->dma_addr,
+ GFP_KERNEL);
+
+ if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0))
+ return -ENOMEM;
+
+ chunk->align_addr = chunk->alloc_addr;
+
+ return 0;
+}
+
+
+/* free a DMA consistent chunk of memory */
+
+static void
+fore200e_pca_dma_chunk_free(struct fore200e* fore200e, struct chunk* chunk)
+{
+ dma_free_coherent(&((struct pci_dev *) fore200e->bus_dev)->dev,
+ chunk->alloc_size,
+ chunk->alloc_addr,
+ chunk->dma_addr);
+}
+
+
+static int
+fore200e_pca_irq_check(struct fore200e* fore200e)
+{
+ /* this is a 1 bit register */
+ int irq_posted = readl(fore200e->regs.pca.psr);
+
+#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG == 2)
+ if (irq_posted && (readl(fore200e->regs.pca.hcr) & PCA200E_HCR_OUTFULL)) {
+ DPRINTK(2,"FIFO OUT full, device %d\n", fore200e->atm_dev->number);
+ }
+#endif
+
+ return irq_posted;
+}
+
+
+static void
+fore200e_pca_irq_ack(struct fore200e* fore200e)
+{
+ writel(PCA200E_HCR_CLRINTR, fore200e->regs.pca.hcr);
+}
+
+
+static void
+fore200e_pca_reset(struct fore200e* fore200e)
+{
+ writel(PCA200E_HCR_RESET, fore200e->regs.pca.hcr);
+ fore200e_spin(10);
+ writel(0, fore200e->regs.pca.hcr);
+}
+
+
+static int fore200e_pca_map(struct fore200e* fore200e)
+{
+ DPRINTK(2, "device %s being mapped in memory\n", fore200e->name);
+
+ fore200e->virt_base = ioremap(fore200e->phys_base, PCA200E_IOSPACE_LENGTH);
+
+ if (fore200e->virt_base == NULL) {
+ printk(FORE200E "can't map device %s\n", fore200e->name);
+ return -EFAULT;
+ }
+
+ DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base);
+
+ /* gain access to the PCA specific registers */
+ fore200e->regs.pca.hcr = fore200e->virt_base + PCA200E_HCR_OFFSET;
+ fore200e->regs.pca.imr = fore200e->virt_base + PCA200E_IMR_OFFSET;
+ fore200e->regs.pca.psr = fore200e->virt_base + PCA200E_PSR_OFFSET;
+
+ fore200e->state = FORE200E_STATE_MAP;
+ return 0;
+}
+
+
+static void
+fore200e_pca_unmap(struct fore200e* fore200e)
+{
+ DPRINTK(2, "device %s being unmapped from memory\n", fore200e->name);
+
+ if (fore200e->virt_base != NULL)
+ iounmap(fore200e->virt_base);
+}
+
+
+static int fore200e_pca_configure(struct fore200e *fore200e)
+{
+ struct pci_dev* pci_dev = (struct pci_dev*)fore200e->bus_dev;
+ u8 master_ctrl, latency;
+
+ DPRINTK(2, "device %s being configured\n", fore200e->name);
+
+ if ((pci_dev->irq == 0) || (pci_dev->irq == 0xFF)) {
+ printk(FORE200E "incorrect IRQ setting - misconfigured PCI-PCI bridge?\n");
+ return -EIO;
+ }
+
+ pci_read_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, &master_ctrl);
+
+ master_ctrl = master_ctrl
+#if defined(__BIG_ENDIAN)
+ /* request the PCA board to convert the endianess of slave RAM accesses */
+ | PCA200E_CTRL_CONVERT_ENDIAN
+#endif
+#if 0
+ | PCA200E_CTRL_DIS_CACHE_RD
+ | PCA200E_CTRL_DIS_WRT_INVAL
+ | PCA200E_CTRL_ENA_CONT_REQ_MODE
+ | PCA200E_CTRL_2_CACHE_WRT_INVAL
+#endif
+ | PCA200E_CTRL_LARGE_PCI_BURSTS;
+
+ pci_write_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, master_ctrl);
+
+ /* raise latency from 32 (default) to 192, as this seems to prevent NIC
+ lockups (under heavy rx loads) due to continuous 'FIFO OUT full' condition.
+ this may impact the performances of other PCI devices on the same bus, though */
+ latency = 192;
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
+
+ fore200e->state = FORE200E_STATE_CONFIGURE;
+ return 0;
+}
+
+
+static int __init
+fore200e_pca_prom_read(struct fore200e* fore200e, struct prom_data* prom)
+{
+ struct host_cmdq* cmdq = &fore200e->host_cmdq;
+ struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ];
+ struct prom_opcode opcode;
+ int ok;
+ u32 prom_dma;
+
+ FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
+
+ opcode.opcode = OPCODE_GET_PROM;
+ opcode.pad = 0;
+
+ prom_dma = fore200e->bus->dma_map(fore200e, prom, sizeof(struct prom_data), DMA_FROM_DEVICE);
+
+ fore200e->bus->write(prom_dma, &entry->cp_entry->cmd.prom_block.prom_haddr);
+
+ *entry->status = STATUS_PENDING;
+
+ fore200e->bus->write(*(u32*)&opcode, (u32 __iomem *)&entry->cp_entry->cmd.prom_block.opcode);
+
+ ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400);
+
+ *entry->status = STATUS_FREE;
+
+ fore200e->bus->dma_unmap(fore200e, prom_dma, sizeof(struct prom_data), DMA_FROM_DEVICE);
+
+ if (ok == 0) {
+ printk(FORE200E "unable to get PROM data from device %s\n", fore200e->name);
+ return -EIO;
+ }
+
+#if defined(__BIG_ENDIAN)
+
+#define swap_here(addr) (*((u32*)(addr)) = swab32( *((u32*)(addr)) ))
+
+ /* MAC address is stored as little-endian */
+ swap_here(&prom->mac_addr[0]);
+ swap_here(&prom->mac_addr[4]);
+#endif
+
+ return 0;
+}
+
+
+static int
+fore200e_pca_proc_read(struct fore200e* fore200e, char *page)
+{
+ struct pci_dev* pci_dev = (struct pci_dev*)fore200e->bus_dev;
+
+ return sprintf(page, " PCI bus/slot/function:\t%d/%d/%d\n",
+ pci_dev->bus->number, PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn));
+}
+
+#endif /* CONFIG_PCI */
+
+
+#ifdef CONFIG_SBUS
+
+static u32 fore200e_sba_read(volatile u32 __iomem *addr)
+{
+ return sbus_readl(addr);
+}
+
+static void fore200e_sba_write(u32 val, volatile u32 __iomem *addr)
+{
+ sbus_writel(val, addr);
+}
+
+static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int size, int direction)
+{
+ struct platform_device *op = fore200e->bus_dev;
+ u32 dma_addr;
+
+ dma_addr = dma_map_single(&op->dev, virt_addr, size, direction);
+
+ DPRINTK(3, "SBUS DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d --> dma_addr = 0x%08x\n",
+ virt_addr, size, direction, dma_addr);
+
+ return dma_addr;
+}
+
+static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
+{
+ struct platform_device *op = fore200e->bus_dev;
+
+ DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n",
+ dma_addr, size, direction);
+
+ dma_unmap_single(&op->dev, dma_addr, size, direction);
+}
+
+static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
+{
+ struct platform_device *op = fore200e->bus_dev;
+
+ DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
+
+ dma_sync_single_for_cpu(&op->dev, dma_addr, size, direction);
+}
+
+static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
+{
+ struct platform_device *op = fore200e->bus_dev;
+
+ DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
+
+ dma_sync_single_for_device(&op->dev, dma_addr, size, direction);
+}
+
+/* Allocate a DVMA consistent chunk of memory intended to act as a communication mechanism
+ * (to hold descriptors, status, queues, etc.) shared by the driver and the adapter.
+ */
+static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk *chunk,
+ int size, int nbr, int alignment)
+{
+ struct platform_device *op = fore200e->bus_dev;
+
+ chunk->alloc_size = chunk->align_size = size * nbr;
+
+ /* returned chunks are page-aligned */
+ chunk->alloc_addr = dma_alloc_coherent(&op->dev, chunk->alloc_size,
+ &chunk->dma_addr, GFP_ATOMIC);
+
+ if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0))
+ return -ENOMEM;
+
+ chunk->align_addr = chunk->alloc_addr;
+
+ return 0;
+}
+
+/* free a DVMA consistent chunk of memory */
+static void fore200e_sba_dma_chunk_free(struct fore200e *fore200e, struct chunk *chunk)
+{
+ struct platform_device *op = fore200e->bus_dev;
+
+ dma_free_coherent(&op->dev, chunk->alloc_size,
+ chunk->alloc_addr, chunk->dma_addr);
+}
+
+static void fore200e_sba_irq_enable(struct fore200e *fore200e)
+{
+ u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY;
+ fore200e->bus->write(hcr | SBA200E_HCR_INTR_ENA, fore200e->regs.sba.hcr);
+}
+
+static int fore200e_sba_irq_check(struct fore200e *fore200e)
+{
+ return fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_INTR_REQ;
+}
+
+static void fore200e_sba_irq_ack(struct fore200e *fore200e)
+{
+ u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY;
+ fore200e->bus->write(hcr | SBA200E_HCR_INTR_CLR, fore200e->regs.sba.hcr);
+}
+
+static void fore200e_sba_reset(struct fore200e *fore200e)
+{
+ fore200e->bus->write(SBA200E_HCR_RESET, fore200e->regs.sba.hcr);
+ fore200e_spin(10);
+ fore200e->bus->write(0, fore200e->regs.sba.hcr);
+}
+
+static int __init fore200e_sba_map(struct fore200e *fore200e)
+{
+ struct platform_device *op = fore200e->bus_dev;
+ unsigned int bursts;
+
+ /* gain access to the SBA specific registers */
+ fore200e->regs.sba.hcr = of_ioremap(&op->resource[0], 0, SBA200E_HCR_LENGTH, "SBA HCR");
+ fore200e->regs.sba.bsr = of_ioremap(&op->resource[1], 0, SBA200E_BSR_LENGTH, "SBA BSR");
+ fore200e->regs.sba.isr = of_ioremap(&op->resource[2], 0, SBA200E_ISR_LENGTH, "SBA ISR");
+ fore200e->virt_base = of_ioremap(&op->resource[3], 0, SBA200E_RAM_LENGTH, "SBA RAM");
+
+ if (!fore200e->virt_base) {
+ printk(FORE200E "unable to map RAM of device %s\n", fore200e->name);
+ return -EFAULT;
+ }
+
+ DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base);
+
+ fore200e->bus->write(0x02, fore200e->regs.sba.isr); /* XXX hardwired interrupt level */
+
+ /* get the supported DVMA burst sizes */
+ bursts = of_getintprop_default(op->dev.of_node->parent, "burst-sizes", 0x00);
+
+ if (sbus_can_dma_64bit())
+ sbus_set_sbus64(&op->dev, bursts);
+
+ fore200e->state = FORE200E_STATE_MAP;
+ return 0;
+}
+
+static void fore200e_sba_unmap(struct fore200e *fore200e)
+{
+ struct platform_device *op = fore200e->bus_dev;
+
+ of_iounmap(&op->resource[0], fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH);
+ of_iounmap(&op->resource[1], fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH);
+ of_iounmap(&op->resource[2], fore200e->regs.sba.isr, SBA200E_ISR_LENGTH);
+ of_iounmap(&op->resource[3], fore200e->virt_base, SBA200E_RAM_LENGTH);
+}
+
+static int __init fore200e_sba_configure(struct fore200e *fore200e)
+{
+ fore200e->state = FORE200E_STATE_CONFIGURE;
+ return 0;
+}
+
+static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_data *prom)
+{
+ struct platform_device *op = fore200e->bus_dev;
+ const u8 *prop;
+ int len;
+
+ prop = of_get_property(op->dev.of_node, "madaddrlo2", &len);
+ if (!prop)
+ return -ENODEV;
+ memcpy(&prom->mac_addr[4], prop, 4);
+
+ prop = of_get_property(op->dev.of_node, "madaddrhi4", &len);
+ if (!prop)
+ return -ENODEV;
+ memcpy(&prom->mac_addr[2], prop, 4);
+
+ prom->serial_number = of_getintprop_default(op->dev.of_node,
+ "serialnumber", 0);
+ prom->hw_revision = of_getintprop_default(op->dev.of_node,
+ "promversion", 0);
+
+ return 0;
+}
+
+static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page)
+{
+ struct platform_device *op = fore200e->bus_dev;
+ const struct linux_prom_registers *regs;
+
+ regs = of_get_property(op->dev.of_node, "reg", NULL);
+
+ return sprintf(page, " SBUS slot/device:\t\t%d/'%s'\n",
+ (regs ? regs->which_io : 0), op->dev.of_node->name);
+}
+#endif /* CONFIG_SBUS */
+
+
+static void
+fore200e_tx_irq(struct fore200e* fore200e)
+{
+ struct host_txq* txq = &fore200e->host_txq;
+ struct host_txq_entry* entry;
+ struct atm_vcc* vcc;
+ struct fore200e_vc_map* vc_map;
+
+ if (fore200e->host_txq.txing == 0)
+ return;
+
+ for (;;) {
+
+ entry = &txq->host_entry[ txq->tail ];
+
+ if ((*entry->status & STATUS_COMPLETE) == 0) {
+ break;
+ }
+
+ DPRINTK(3, "TX COMPLETED: entry = %p [tail = %d], vc_map = %p, skb = %p\n",
+ entry, txq->tail, entry->vc_map, entry->skb);
+
+ /* free copy of misaligned data */
+ kfree(entry->data);
+
+ /* remove DMA mapping */
+ fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length,
+ DMA_TO_DEVICE);
+
+ vc_map = entry->vc_map;
+
+ /* vcc closed since the time the entry was submitted for tx? */
+ if ((vc_map->vcc == NULL) ||
+ (test_bit(ATM_VF_READY, &vc_map->vcc->flags) == 0)) {
+
+ DPRINTK(1, "no ready vcc found for PDU sent on device %d\n",
+ fore200e->atm_dev->number);
+
+ dev_kfree_skb_any(entry->skb);
+ }
+ else {
+ ASSERT(vc_map->vcc);
+
+ /* vcc closed then immediately re-opened? */
+ if (vc_map->incarn != entry->incarn) {
+
+ /* when a vcc is closed, some PDUs may be still pending in the tx queue.
+ if the same vcc is immediately re-opened, those pending PDUs must
+ not be popped after the completion of their emission, as they refer
+ to the prior incarnation of that vcc. otherwise, sk_atm(vcc)->sk_wmem_alloc
+ would be decremented by the size of the (unrelated) skb, possibly
+ leading to a negative sk->sk_wmem_alloc count, ultimately freezing the vcc.
+ we thus bind the tx entry to the current incarnation of the vcc
+ when the entry is submitted for tx. When the tx later completes,
+ if the incarnation number of the tx entry does not match the one
+ of the vcc, then this implies that the vcc has been closed then re-opened.
+ we thus just drop the skb here. */
+
+ DPRINTK(1, "vcc closed-then-re-opened; dropping PDU sent on device %d\n",
+ fore200e->atm_dev->number);
+
+ dev_kfree_skb_any(entry->skb);
+ }
+ else {
+ vcc = vc_map->vcc;
+ ASSERT(vcc);
+
+ /* notify tx completion */
+ if (vcc->pop) {
+ vcc->pop(vcc, entry->skb);
+ }
+ else {
+ dev_kfree_skb_any(entry->skb);
+ }
+#if 1
+ /* race fixed by the above incarnation mechanism, but... */
+ if (atomic_read(&sk_atm(vcc)->sk_wmem_alloc) < 0) {
+ atomic_set(&sk_atm(vcc)->sk_wmem_alloc, 0);
+ }
+#endif
+ /* check error condition */
+ if (*entry->status & STATUS_ERROR)
+ atomic_inc(&vcc->stats->tx_err);
+ else
+ atomic_inc(&vcc->stats->tx);
+ }
+ }
+
+ *entry->status = STATUS_FREE;
+
+ fore200e->host_txq.txing--;
+
+ FORE200E_NEXT_ENTRY(txq->tail, QUEUE_SIZE_TX);
+ }
+}
+
+
+#ifdef FORE200E_BSQ_DEBUG
+int bsq_audit(int where, struct host_bsq* bsq, int scheme, int magn)
+{
+ struct buffer* buffer;
+ int count = 0;
+
+ buffer = bsq->freebuf;
+ while (buffer) {
+
+ if (buffer->supplied) {
+ printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld supplied but in free list!\n",
+ where, scheme, magn, buffer->index);
+ }
+
+ if (buffer->magn != magn) {
+ printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected magn = %d\n",
+ where, scheme, magn, buffer->index, buffer->magn);
+ }
+
+ if (buffer->scheme != scheme) {
+ printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected scheme = %d\n",
+ where, scheme, magn, buffer->index, buffer->scheme);
+ }
+
+ if ((buffer->index < 0) || (buffer->index >= fore200e_rx_buf_nbr[ scheme ][ magn ])) {
+ printk(FORE200E "bsq_audit(%d): queue %d.%d, out of range buffer index = %ld !\n",
+ where, scheme, magn, buffer->index);
+ }
+
+ count++;
+ buffer = buffer->next;
+ }
+
+ if (count != bsq->freebuf_count) {
+ printk(FORE200E "bsq_audit(%d): queue %d.%d, %d bufs in free list, but freebuf_count = %d\n",
+ where, scheme, magn, count, bsq->freebuf_count);
+ }
+ return 0;
+}
+#endif
+
+
+static void
+fore200e_supply(struct fore200e* fore200e)
+{
+ int scheme, magn, i;
+
+ struct host_bsq* bsq;
+ struct host_bsq_entry* entry;
+ struct buffer* buffer;
+
+ for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) {
+ for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) {
+
+ bsq = &fore200e->host_bsq[ scheme ][ magn ];
+
+#ifdef FORE200E_BSQ_DEBUG
+ bsq_audit(1, bsq, scheme, magn);
+#endif
+ while (bsq->freebuf_count >= RBD_BLK_SIZE) {
+
+ DPRINTK(2, "supplying %d rx buffers to queue %d / %d, freebuf_count = %d\n",
+ RBD_BLK_SIZE, scheme, magn, bsq->freebuf_count);
+
+ entry = &bsq->host_entry[ bsq->head ];
+
+ for (i = 0; i < RBD_BLK_SIZE; i++) {
+
+ /* take the first buffer in the free buffer list */
+ buffer = bsq->freebuf;
+ if (!buffer) {
+ printk(FORE200E "no more free bufs in queue %d.%d, but freebuf_count = %d\n",
+ scheme, magn, bsq->freebuf_count);
+ return;
+ }
+ bsq->freebuf = buffer->next;
+
+#ifdef FORE200E_BSQ_DEBUG
+ if (buffer->supplied)
+ printk(FORE200E "queue %d.%d, buffer %lu already supplied\n",
+ scheme, magn, buffer->index);
+ buffer->supplied = 1;
+#endif
+ entry->rbd_block->rbd[ i ].buffer_haddr = buffer->data.dma_addr;
+ entry->rbd_block->rbd[ i ].handle = FORE200E_BUF2HDL(buffer);
+ }
+
+ FORE200E_NEXT_ENTRY(bsq->head, QUEUE_SIZE_BS);
+
+ /* decrease accordingly the number of free rx buffers */
+ bsq->freebuf_count -= RBD_BLK_SIZE;
+
+ *entry->status = STATUS_PENDING;
+ fore200e->bus->write(entry->rbd_block_dma, &entry->cp_entry->rbd_block_haddr);
+ }
+ }
+ }
+}
+
+
+static int
+fore200e_push_rpd(struct fore200e* fore200e, struct atm_vcc* vcc, struct rpd* rpd)
+{
+ struct sk_buff* skb;
+ struct buffer* buffer;
+ struct fore200e_vcc* fore200e_vcc;
+ int i, pdu_len = 0;
+#ifdef FORE200E_52BYTE_AAL0_SDU
+ u32 cell_header = 0;
+#endif
+
+ ASSERT(vcc);
+
+ fore200e_vcc = FORE200E_VCC(vcc);
+ ASSERT(fore200e_vcc);
+
+#ifdef FORE200E_52BYTE_AAL0_SDU
+ if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.rxtp.max_sdu == ATM_AAL0_SDU)) {
+
+ cell_header = (rpd->atm_header.gfc << ATM_HDR_GFC_SHIFT) |
+ (rpd->atm_header.vpi << ATM_HDR_VPI_SHIFT) |
+ (rpd->atm_header.vci << ATM_HDR_VCI_SHIFT) |
+ (rpd->atm_header.plt << ATM_HDR_PTI_SHIFT) |
+ rpd->atm_header.clp;
+ pdu_len = 4;
+ }
+#endif
+
+ /* compute total PDU length */
+ for (i = 0; i < rpd->nseg; i++)
+ pdu_len += rpd->rsd[ i ].length;
+
+ skb = alloc_skb(pdu_len, GFP_ATOMIC);
+ if (skb == NULL) {
+ DPRINTK(2, "unable to alloc new skb, rx PDU length = %d\n", pdu_len);
+
+ atomic_inc(&vcc->stats->rx_drop);
+ return -ENOMEM;
+ }
+
+ __net_timestamp(skb);
+
+#ifdef FORE200E_52BYTE_AAL0_SDU
+ if (cell_header) {
+ *((u32*)skb_put(skb, 4)) = cell_header;
+ }
+#endif
+
+ /* reassemble segments */
+ for (i = 0; i < rpd->nseg; i++) {
+
+ /* rebuild rx buffer address from rsd handle */
+ buffer = FORE200E_HDL2BUF(rpd->rsd[ i ].handle);
+
+ /* Make device DMA transfer visible to CPU. */
+ fore200e->bus->dma_sync_for_cpu(fore200e, buffer->data.dma_addr, rpd->rsd[ i ].length, DMA_FROM_DEVICE);
+
+ memcpy(skb_put(skb, rpd->rsd[ i ].length), buffer->data.align_addr, rpd->rsd[ i ].length);
+
+ /* Now let the device get at it again. */
+ fore200e->bus->dma_sync_for_device(fore200e, buffer->data.dma_addr, rpd->rsd[ i ].length, DMA_FROM_DEVICE);
+ }
+
+ DPRINTK(3, "rx skb: len = %d, truesize = %d\n", skb->len, skb->truesize);
+
+ if (pdu_len < fore200e_vcc->rx_min_pdu)
+ fore200e_vcc->rx_min_pdu = pdu_len;
+ if (pdu_len > fore200e_vcc->rx_max_pdu)
+ fore200e_vcc->rx_max_pdu = pdu_len;
+ fore200e_vcc->rx_pdu++;
+
+ /* push PDU */
+ if (atm_charge(vcc, skb->truesize) == 0) {
+
+ DPRINTK(2, "receive buffers saturated for %d.%d.%d - PDU dropped\n",
+ vcc->itf, vcc->vpi, vcc->vci);
+
+ dev_kfree_skb_any(skb);
+
+ atomic_inc(&vcc->stats->rx_drop);
+ return -ENOMEM;
+ }
+
+ ASSERT(atomic_read(&sk_atm(vcc)->sk_wmem_alloc) >= 0);
+
+ vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
+
+ ASSERT(atomic_read(&sk_atm(vcc)->sk_wmem_alloc) >= 0);
+
+ return 0;
+}
+
+
+static void
+fore200e_collect_rpd(struct fore200e* fore200e, struct rpd* rpd)
+{
+ struct host_bsq* bsq;
+ struct buffer* buffer;
+ int i;
+
+ for (i = 0; i < rpd->nseg; i++) {
+
+ /* rebuild rx buffer address from rsd handle */
+ buffer = FORE200E_HDL2BUF(rpd->rsd[ i ].handle);
+
+ bsq = &fore200e->host_bsq[ buffer->scheme ][ buffer->magn ];
+
+#ifdef FORE200E_BSQ_DEBUG
+ bsq_audit(2, bsq, buffer->scheme, buffer->magn);
+
+ if (buffer->supplied == 0)
+ printk(FORE200E "queue %d.%d, buffer %ld was not supplied\n",
+ buffer->scheme, buffer->magn, buffer->index);
+ buffer->supplied = 0;
+#endif
+
+ /* re-insert the buffer into the free buffer list */
+ buffer->next = bsq->freebuf;
+ bsq->freebuf = buffer;
+
+ /* then increment the number of free rx buffers */
+ bsq->freebuf_count++;
+ }
+}
+
+
+static void
+fore200e_rx_irq(struct fore200e* fore200e)
+{
+ struct host_rxq* rxq = &fore200e->host_rxq;
+ struct host_rxq_entry* entry;
+ struct atm_vcc* vcc;
+ struct fore200e_vc_map* vc_map;
+
+ for (;;) {
+
+ entry = &rxq->host_entry[ rxq->head ];
+
+ /* no more received PDUs */
+ if ((*entry->status & STATUS_COMPLETE) == 0)
+ break;
+
+ vc_map = FORE200E_VC_MAP(fore200e, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci);
+
+ if ((vc_map->vcc == NULL) ||
+ (test_bit(ATM_VF_READY, &vc_map->vcc->flags) == 0)) {
+
+ DPRINTK(1, "no ready VC found for PDU received on %d.%d.%d\n",
+ fore200e->atm_dev->number,
+ entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci);
+ }
+ else {
+ vcc = vc_map->vcc;
+ ASSERT(vcc);
+
+ if ((*entry->status & STATUS_ERROR) == 0) {
+
+ fore200e_push_rpd(fore200e, vcc, entry->rpd);
+ }
+ else {
+ DPRINTK(2, "damaged PDU on %d.%d.%d\n",
+ fore200e->atm_dev->number,
+ entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci);
+ atomic_inc(&vcc->stats->rx_err);
+ }
+ }
+
+ FORE200E_NEXT_ENTRY(rxq->head, QUEUE_SIZE_RX);
+
+ fore200e_collect_rpd(fore200e, entry->rpd);
+
+ /* rewrite the rpd address to ack the received PDU */
+ fore200e->bus->write(entry->rpd_dma, &entry->cp_entry->rpd_haddr);
+ *entry->status = STATUS_FREE;
+
+ fore200e_supply(fore200e);
+ }
+}
+
+
+#ifndef FORE200E_USE_TASKLET
+static void
+fore200e_irq(struct fore200e* fore200e)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fore200e->q_lock, flags);
+ fore200e_rx_irq(fore200e);
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+
+ spin_lock_irqsave(&fore200e->q_lock, flags);
+ fore200e_tx_irq(fore200e);
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+}
+#endif
+
+
+static irqreturn_t
+fore200e_interrupt(int irq, void* dev)
+{
+ struct fore200e* fore200e = FORE200E_DEV((struct atm_dev*)dev);
+
+ if (fore200e->bus->irq_check(fore200e) == 0) {
+
+ DPRINTK(3, "interrupt NOT triggered by device %d\n", fore200e->atm_dev->number);
+ return IRQ_NONE;
+ }
+ DPRINTK(3, "interrupt triggered by device %d\n", fore200e->atm_dev->number);
+
+#ifdef FORE200E_USE_TASKLET
+ tasklet_schedule(&fore200e->tx_tasklet);
+ tasklet_schedule(&fore200e->rx_tasklet);
+#else
+ fore200e_irq(fore200e);
+#endif
+
+ fore200e->bus->irq_ack(fore200e);
+ return IRQ_HANDLED;
+}
+
+
+#ifdef FORE200E_USE_TASKLET
+static void
+fore200e_tx_tasklet(unsigned long data)
+{
+ struct fore200e* fore200e = (struct fore200e*) data;
+ unsigned long flags;
+
+ DPRINTK(3, "tx tasklet scheduled for device %d\n", fore200e->atm_dev->number);
+
+ spin_lock_irqsave(&fore200e->q_lock, flags);
+ fore200e_tx_irq(fore200e);
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+}
+
+
+static void
+fore200e_rx_tasklet(unsigned long data)
+{
+ struct fore200e* fore200e = (struct fore200e*) data;
+ unsigned long flags;
+
+ DPRINTK(3, "rx tasklet scheduled for device %d\n", fore200e->atm_dev->number);
+
+ spin_lock_irqsave(&fore200e->q_lock, flags);
+ fore200e_rx_irq((struct fore200e*) data);
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+}
+#endif
+
+
+static int
+fore200e_select_scheme(struct atm_vcc* vcc)
+{
+ /* fairly balance the VCs over (identical) buffer schemes */
+ int scheme = vcc->vci % 2 ? BUFFER_SCHEME_ONE : BUFFER_SCHEME_TWO;
+
+ DPRINTK(1, "VC %d.%d.%d uses buffer scheme %d\n",
+ vcc->itf, vcc->vpi, vcc->vci, scheme);
+
+ return scheme;
+}
+
+
+static int
+fore200e_activate_vcin(struct fore200e* fore200e, int activate, struct atm_vcc* vcc, int mtu)
+{
+ struct host_cmdq* cmdq = &fore200e->host_cmdq;
+ struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ];
+ struct activate_opcode activ_opcode;
+ struct deactivate_opcode deactiv_opcode;
+ struct vpvc vpvc;
+ int ok;
+ enum fore200e_aal aal = fore200e_atm2fore_aal(vcc->qos.aal);
+
+ FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
+
+ if (activate) {
+ FORE200E_VCC(vcc)->scheme = fore200e_select_scheme(vcc);
+
+ activ_opcode.opcode = OPCODE_ACTIVATE_VCIN;
+ activ_opcode.aal = aal;
+ activ_opcode.scheme = FORE200E_VCC(vcc)->scheme;
+ activ_opcode.pad = 0;
+ }
+ else {
+ deactiv_opcode.opcode = OPCODE_DEACTIVATE_VCIN;
+ deactiv_opcode.pad = 0;
+ }
+
+ vpvc.vci = vcc->vci;
+ vpvc.vpi = vcc->vpi;
+
+ *entry->status = STATUS_PENDING;
+
+ if (activate) {
+
+#ifdef FORE200E_52BYTE_AAL0_SDU
+ mtu = 48;
+#endif
+ /* the MTU is not used by the cp, except in the case of AAL0 */
+ fore200e->bus->write(mtu, &entry->cp_entry->cmd.activate_block.mtu);
+ fore200e->bus->write(*(u32*)&vpvc, (u32 __iomem *)&entry->cp_entry->cmd.activate_block.vpvc);
+ fore200e->bus->write(*(u32*)&activ_opcode, (u32 __iomem *)&entry->cp_entry->cmd.activate_block.opcode);
+ }
+ else {
+ fore200e->bus->write(*(u32*)&vpvc, (u32 __iomem *)&entry->cp_entry->cmd.deactivate_block.vpvc);
+ fore200e->bus->write(*(u32*)&deactiv_opcode, (u32 __iomem *)&entry->cp_entry->cmd.deactivate_block.opcode);
+ }
+
+ ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400);
+
+ *entry->status = STATUS_FREE;
+
+ if (ok == 0) {
+ printk(FORE200E "unable to %s VC %d.%d.%d\n",
+ activate ? "open" : "close", vcc->itf, vcc->vpi, vcc->vci);
+ return -EIO;
+ }
+
+ DPRINTK(1, "VC %d.%d.%d %sed\n", vcc->itf, vcc->vpi, vcc->vci,
+ activate ? "open" : "clos");
+
+ return 0;
+}
+
+
+#define FORE200E_MAX_BACK2BACK_CELLS 255 /* XXX depends on CDVT */
+
+static void
+fore200e_rate_ctrl(struct atm_qos* qos, struct tpd_rate* rate)
+{
+ if (qos->txtp.max_pcr < ATM_OC3_PCR) {
+
+ /* compute the data cells to idle cells ratio from the tx PCR */
+ rate->data_cells = qos->txtp.max_pcr * FORE200E_MAX_BACK2BACK_CELLS / ATM_OC3_PCR;
+ rate->idle_cells = FORE200E_MAX_BACK2BACK_CELLS - rate->data_cells;
+ }
+ else {
+ /* disable rate control */
+ rate->data_cells = rate->idle_cells = 0;
+ }
+}
+
+
+static int
+fore200e_open(struct atm_vcc *vcc)
+{
+ struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
+ struct fore200e_vcc* fore200e_vcc;
+ struct fore200e_vc_map* vc_map;
+ unsigned long flags;
+ int vci = vcc->vci;
+ short vpi = vcc->vpi;
+
+ ASSERT((vpi >= 0) && (vpi < 1<<FORE200E_VPI_BITS));
+ ASSERT((vci >= 0) && (vci < 1<<FORE200E_VCI_BITS));
+
+ spin_lock_irqsave(&fore200e->q_lock, flags);
+
+ vc_map = FORE200E_VC_MAP(fore200e, vpi, vci);
+ if (vc_map->vcc) {
+
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+
+ printk(FORE200E "VC %d.%d.%d already in use\n",
+ fore200e->atm_dev->number, vpi, vci);
+
+ return -EINVAL;
+ }
+
+ vc_map->vcc = vcc;
+
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+
+ fore200e_vcc = kzalloc(sizeof(struct fore200e_vcc), GFP_ATOMIC);
+ if (fore200e_vcc == NULL) {
+ vc_map->vcc = NULL;
+ return -ENOMEM;
+ }
+
+ DPRINTK(2, "opening %d.%d.%d:%d QoS = (tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; "
+ "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d)\n",
+ vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal),
+ fore200e_traffic_class[ vcc->qos.txtp.traffic_class ],
+ vcc->qos.txtp.min_pcr, vcc->qos.txtp.max_pcr, vcc->qos.txtp.max_cdv, vcc->qos.txtp.max_sdu,
+ fore200e_traffic_class[ vcc->qos.rxtp.traffic_class ],
+ vcc->qos.rxtp.min_pcr, vcc->qos.rxtp.max_pcr, vcc->qos.rxtp.max_cdv, vcc->qos.rxtp.max_sdu);
+
+ /* pseudo-CBR bandwidth requested? */
+ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
+
+ mutex_lock(&fore200e->rate_mtx);
+ if (fore200e->available_cell_rate < vcc->qos.txtp.max_pcr) {
+ mutex_unlock(&fore200e->rate_mtx);
+
+ kfree(fore200e_vcc);
+ vc_map->vcc = NULL;
+ return -EAGAIN;
+ }
+
+ /* reserve bandwidth */
+ fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr;
+ mutex_unlock(&fore200e->rate_mtx);
+ }
+
+ vcc->itf = vcc->dev->number;
+
+ set_bit(ATM_VF_PARTIAL,&vcc->flags);
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+
+ vcc->dev_data = fore200e_vcc;
+
+ if (fore200e_activate_vcin(fore200e, 1, vcc, vcc->qos.rxtp.max_sdu) < 0) {
+
+ vc_map->vcc = NULL;
+
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+
+ vcc->dev_data = NULL;
+
+ fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
+
+ kfree(fore200e_vcc);
+ return -EINVAL;
+ }
+
+ /* compute rate control parameters */
+ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
+
+ fore200e_rate_ctrl(&vcc->qos, &fore200e_vcc->rate);
+ set_bit(ATM_VF_HASQOS, &vcc->flags);
+
+ DPRINTK(3, "tx on %d.%d.%d:%d, tx PCR = %d, rx PCR = %d, data_cells = %u, idle_cells = %u\n",
+ vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal),
+ vcc->qos.txtp.max_pcr, vcc->qos.rxtp.max_pcr,
+ fore200e_vcc->rate.data_cells, fore200e_vcc->rate.idle_cells);
+ }
+
+ fore200e_vcc->tx_min_pdu = fore200e_vcc->rx_min_pdu = MAX_PDU_SIZE + 1;
+ fore200e_vcc->tx_max_pdu = fore200e_vcc->rx_max_pdu = 0;
+ fore200e_vcc->tx_pdu = fore200e_vcc->rx_pdu = 0;
+
+ /* new incarnation of the vcc */
+ vc_map->incarn = ++fore200e->incarn_count;
+
+ /* VC unusable before this flag is set */
+ set_bit(ATM_VF_READY, &vcc->flags);
+
+ return 0;
+}
+
+
+static void
+fore200e_close(struct atm_vcc* vcc)
+{
+ struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
+ struct fore200e_vcc* fore200e_vcc;
+ struct fore200e_vc_map* vc_map;
+ unsigned long flags;
+
+ ASSERT(vcc);
+ ASSERT((vcc->vpi >= 0) && (vcc->vpi < 1<<FORE200E_VPI_BITS));
+ ASSERT((vcc->vci >= 0) && (vcc->vci < 1<<FORE200E_VCI_BITS));
+
+ DPRINTK(2, "closing %d.%d.%d:%d\n", vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal));
+
+ clear_bit(ATM_VF_READY, &vcc->flags);
+
+ fore200e_activate_vcin(fore200e, 0, vcc, 0);
+
+ spin_lock_irqsave(&fore200e->q_lock, flags);
+
+ vc_map = FORE200E_VC_MAP(fore200e, vcc->vpi, vcc->vci);
+
+ /* the vc is no longer considered as "in use" by fore200e_open() */
+ vc_map->vcc = NULL;
+
+ vcc->itf = vcc->vci = vcc->vpi = 0;
+
+ fore200e_vcc = FORE200E_VCC(vcc);
+ vcc->dev_data = NULL;
+
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+
+ /* release reserved bandwidth, if any */
+ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) {
+
+ mutex_lock(&fore200e->rate_mtx);
+ fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
+ mutex_unlock(&fore200e->rate_mtx);
+
+ clear_bit(ATM_VF_HASQOS, &vcc->flags);
+ }
+
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ clear_bit(ATM_VF_PARTIAL,&vcc->flags);
+
+ ASSERT(fore200e_vcc);
+ kfree(fore200e_vcc);
+}
+
+
+static int
+fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
+ struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc);
+ struct fore200e_vc_map* vc_map;
+ struct host_txq* txq = &fore200e->host_txq;
+ struct host_txq_entry* entry;
+ struct tpd* tpd;
+ struct tpd_haddr tpd_haddr;
+ int retry = CONFIG_ATM_FORE200E_TX_RETRY;
+ int tx_copy = 0;
+ int tx_len = skb->len;
+ u32* cell_header = NULL;
+ unsigned char* skb_data;
+ int skb_len;
+ unsigned char* data;
+ unsigned long flags;
+
+ ASSERT(vcc);
+ ASSERT(atomic_read(&sk_atm(vcc)->sk_wmem_alloc) >= 0);
+ ASSERT(fore200e);
+ ASSERT(fore200e_vcc);
+
+ if (!test_bit(ATM_VF_READY, &vcc->flags)) {
+ DPRINTK(1, "VC %d.%d.%d not ready for tx\n", vcc->itf, vcc->vpi, vcc->vpi);
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+#ifdef FORE200E_52BYTE_AAL0_SDU
+ if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.txtp.max_sdu == ATM_AAL0_SDU)) {
+ cell_header = (u32*) skb->data;
+ skb_data = skb->data + 4; /* skip 4-byte cell header */
+ skb_len = tx_len = skb->len - 4;
+
+ DPRINTK(3, "user-supplied cell header = 0x%08x\n", *cell_header);
+ }
+ else
+#endif
+ {
+ skb_data = skb->data;
+ skb_len = skb->len;
+ }
+
+ if (((unsigned long)skb_data) & 0x3) {
+
+ DPRINTK(2, "misaligned tx PDU on device %s\n", fore200e->name);
+ tx_copy = 1;
+ tx_len = skb_len;
+ }
+
+ if ((vcc->qos.aal == ATM_AAL0) && (skb_len % ATM_CELL_PAYLOAD)) {
+
+ /* this simply NUKES the PCA board */
+ DPRINTK(2, "incomplete tx AAL0 PDU on device %s\n", fore200e->name);
+ tx_copy = 1;
+ tx_len = ((skb_len / ATM_CELL_PAYLOAD) + 1) * ATM_CELL_PAYLOAD;
+ }
+
+ if (tx_copy) {
+ data = kmalloc(tx_len, GFP_ATOMIC | GFP_DMA);
+ if (data == NULL) {
+ if (vcc->pop) {
+ vcc->pop(vcc, skb);
+ }
+ else {
+ dev_kfree_skb_any(skb);
+ }
+ return -ENOMEM;
+ }
+
+ memcpy(data, skb_data, skb_len);
+ if (skb_len < tx_len)
+ memset(data + skb_len, 0x00, tx_len - skb_len);
+ }
+ else {
+ data = skb_data;
+ }
+
+ vc_map = FORE200E_VC_MAP(fore200e, vcc->vpi, vcc->vci);
+ ASSERT(vc_map->vcc == vcc);
+
+ retry_here:
+
+ spin_lock_irqsave(&fore200e->q_lock, flags);
+
+ entry = &txq->host_entry[ txq->head ];
+
+ if ((*entry->status != STATUS_FREE) || (txq->txing >= QUEUE_SIZE_TX - 2)) {
+
+ /* try to free completed tx queue entries */
+ fore200e_tx_irq(fore200e);
+
+ if (*entry->status != STATUS_FREE) {
+
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+
+ /* retry once again? */
+ if (--retry > 0) {
+ udelay(50);
+ goto retry_here;
+ }
+
+ atomic_inc(&vcc->stats->tx_err);
+
+ fore200e->tx_sat++;
+ DPRINTK(2, "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n",
+ fore200e->name, fore200e->cp_queues->heartbeat);
+ if (vcc->pop) {
+ vcc->pop(vcc, skb);
+ }
+ else {
+ dev_kfree_skb_any(skb);
+ }
+
+ if (tx_copy)
+ kfree(data);
+
+ return -ENOBUFS;
+ }
+ }
+
+ entry->incarn = vc_map->incarn;
+ entry->vc_map = vc_map;
+ entry->skb = skb;
+ entry->data = tx_copy ? data : NULL;
+
+ tpd = entry->tpd;
+ tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, data, tx_len, DMA_TO_DEVICE);
+ tpd->tsd[ 0 ].length = tx_len;
+
+ FORE200E_NEXT_ENTRY(txq->head, QUEUE_SIZE_TX);
+ txq->txing++;
+
+ /* The dma_map call above implies a dma_sync so the device can use it,
+ * thus no explicit dma_sync call is necessary here.
+ */
+
+ DPRINTK(3, "tx on %d.%d.%d:%d, len = %u (%u)\n",
+ vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal),
+ tpd->tsd[0].length, skb_len);
+
+ if (skb_len < fore200e_vcc->tx_min_pdu)
+ fore200e_vcc->tx_min_pdu = skb_len;
+ if (skb_len > fore200e_vcc->tx_max_pdu)
+ fore200e_vcc->tx_max_pdu = skb_len;
+ fore200e_vcc->tx_pdu++;
+
+ /* set tx rate control information */
+ tpd->rate.data_cells = fore200e_vcc->rate.data_cells;
+ tpd->rate.idle_cells = fore200e_vcc->rate.idle_cells;
+
+ if (cell_header) {
+ tpd->atm_header.clp = (*cell_header & ATM_HDR_CLP);
+ tpd->atm_header.plt = (*cell_header & ATM_HDR_PTI_MASK) >> ATM_HDR_PTI_SHIFT;
+ tpd->atm_header.vci = (*cell_header & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT;
+ tpd->atm_header.vpi = (*cell_header & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT;
+ tpd->atm_header.gfc = (*cell_header & ATM_HDR_GFC_MASK) >> ATM_HDR_GFC_SHIFT;
+ }
+ else {
+ /* set the ATM header, common to all cells conveying the PDU */
+ tpd->atm_header.clp = 0;
+ tpd->atm_header.plt = 0;
+ tpd->atm_header.vci = vcc->vci;
+ tpd->atm_header.vpi = vcc->vpi;
+ tpd->atm_header.gfc = 0;
+ }
+
+ tpd->spec.length = tx_len;
+ tpd->spec.nseg = 1;
+ tpd->spec.aal = fore200e_atm2fore_aal(vcc->qos.aal);
+ tpd->spec.intr = 1;
+
+ tpd_haddr.size = sizeof(struct tpd) / (1<<TPD_HADDR_SHIFT); /* size is expressed in 32 byte blocks */
+ tpd_haddr.pad = 0;
+ tpd_haddr.haddr = entry->tpd_dma >> TPD_HADDR_SHIFT; /* shift the address, as we are in a bitfield */
+
+ *entry->status = STATUS_PENDING;
+ fore200e->bus->write(*(u32*)&tpd_haddr, (u32 __iomem *)&entry->cp_entry->tpd_haddr);
+
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+
+ return 0;
+}
+
+
+static int
+fore200e_getstats(struct fore200e* fore200e)
+{
+ struct host_cmdq* cmdq = &fore200e->host_cmdq;
+ struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ];
+ struct stats_opcode opcode;
+ int ok;
+ u32 stats_dma_addr;
+
+ if (fore200e->stats == NULL) {
+ fore200e->stats = kzalloc(sizeof(struct stats), GFP_KERNEL | GFP_DMA);
+ if (fore200e->stats == NULL)
+ return -ENOMEM;
+ }
+
+ stats_dma_addr = fore200e->bus->dma_map(fore200e, fore200e->stats,
+ sizeof(struct stats), DMA_FROM_DEVICE);
+
+ FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
+
+ opcode.opcode = OPCODE_GET_STATS;
+ opcode.pad = 0;
+
+ fore200e->bus->write(stats_dma_addr, &entry->cp_entry->cmd.stats_block.stats_haddr);
+
+ *entry->status = STATUS_PENDING;
+
+ fore200e->bus->write(*(u32*)&opcode, (u32 __iomem *)&entry->cp_entry->cmd.stats_block.opcode);
+
+ ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400);
+
+ *entry->status = STATUS_FREE;
+
+ fore200e->bus->dma_unmap(fore200e, stats_dma_addr, sizeof(struct stats), DMA_FROM_DEVICE);
+
+ if (ok == 0) {
+ printk(FORE200E "unable to get statistics from device %s\n", fore200e->name);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int
+fore200e_getsockopt(struct atm_vcc* vcc, int level, int optname, void __user *optval, int optlen)
+{
+ /* struct fore200e* fore200e = FORE200E_DEV(vcc->dev); */
+
+ DPRINTK(2, "getsockopt %d.%d.%d, level = %d, optname = 0x%x, optval = 0x%p, optlen = %d\n",
+ vcc->itf, vcc->vpi, vcc->vci, level, optname, optval, optlen);
+
+ return -EINVAL;
+}
+
+
+static int
+fore200e_setsockopt(struct atm_vcc* vcc, int level, int optname, void __user *optval, unsigned int optlen)
+{
+ /* struct fore200e* fore200e = FORE200E_DEV(vcc->dev); */
+
+ DPRINTK(2, "setsockopt %d.%d.%d, level = %d, optname = 0x%x, optval = 0x%p, optlen = %d\n",
+ vcc->itf, vcc->vpi, vcc->vci, level, optname, optval, optlen);
+
+ return -EINVAL;
+}
+
+
+#if 0 /* currently unused */
+static int
+fore200e_get_oc3(struct fore200e* fore200e, struct oc3_regs* regs)
+{
+ struct host_cmdq* cmdq = &fore200e->host_cmdq;
+ struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ];
+ struct oc3_opcode opcode;
+ int ok;
+ u32 oc3_regs_dma_addr;
+
+ oc3_regs_dma_addr = fore200e->bus->dma_map(fore200e, regs, sizeof(struct oc3_regs), DMA_FROM_DEVICE);
+
+ FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
+
+ opcode.opcode = OPCODE_GET_OC3;
+ opcode.reg = 0;
+ opcode.value = 0;
+ opcode.mask = 0;
+
+ fore200e->bus->write(oc3_regs_dma_addr, &entry->cp_entry->cmd.oc3_block.regs_haddr);
+
+ *entry->status = STATUS_PENDING;
+
+ fore200e->bus->write(*(u32*)&opcode, (u32*)&entry->cp_entry->cmd.oc3_block.opcode);
+
+ ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400);
+
+ *entry->status = STATUS_FREE;
+
+ fore200e->bus->dma_unmap(fore200e, oc3_regs_dma_addr, sizeof(struct oc3_regs), DMA_FROM_DEVICE);
+
+ if (ok == 0) {
+ printk(FORE200E "unable to get OC-3 regs of device %s\n", fore200e->name);
+ return -EIO;
+ }
+
+ return 0;
+}
+#endif
+
+
+static int
+fore200e_set_oc3(struct fore200e* fore200e, u32 reg, u32 value, u32 mask)
+{
+ struct host_cmdq* cmdq = &fore200e->host_cmdq;
+ struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ];
+ struct oc3_opcode opcode;
+ int ok;
+
+ DPRINTK(2, "set OC-3 reg = 0x%02x, value = 0x%02x, mask = 0x%02x\n", reg, value, mask);
+
+ FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD);
+
+ opcode.opcode = OPCODE_SET_OC3;
+ opcode.reg = reg;
+ opcode.value = value;
+ opcode.mask = mask;
+
+ fore200e->bus->write(0, &entry->cp_entry->cmd.oc3_block.regs_haddr);
+
+ *entry->status = STATUS_PENDING;
+
+ fore200e->bus->write(*(u32*)&opcode, (u32 __iomem *)&entry->cp_entry->cmd.oc3_block.opcode);
+
+ ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400);
+
+ *entry->status = STATUS_FREE;
+
+ if (ok == 0) {
+ printk(FORE200E "unable to set OC-3 reg 0x%02x of device %s\n", reg, fore200e->name);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int
+fore200e_setloop(struct fore200e* fore200e, int loop_mode)
+{
+ u32 mct_value, mct_mask;
+ int error;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch (loop_mode) {
+
+ case ATM_LM_NONE:
+ mct_value = 0;
+ mct_mask = SUNI_MCT_DLE | SUNI_MCT_LLE;
+ break;
+
+ case ATM_LM_LOC_PHY:
+ mct_value = mct_mask = SUNI_MCT_DLE;
+ break;
+
+ case ATM_LM_RMT_PHY:
+ mct_value = mct_mask = SUNI_MCT_LLE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ error = fore200e_set_oc3(fore200e, SUNI_MCT, mct_value, mct_mask);
+ if (error == 0)
+ fore200e->loop_mode = loop_mode;
+
+ return error;
+}
+
+
+static int
+fore200e_fetch_stats(struct fore200e* fore200e, struct sonet_stats __user *arg)
+{
+ struct sonet_stats tmp;
+
+ if (fore200e_getstats(fore200e) < 0)
+ return -EIO;
+
+ tmp.section_bip = be32_to_cpu(fore200e->stats->oc3.section_bip8_errors);
+ tmp.line_bip = be32_to_cpu(fore200e->stats->oc3.line_bip24_errors);
+ tmp.path_bip = be32_to_cpu(fore200e->stats->oc3.path_bip8_errors);
+ tmp.line_febe = be32_to_cpu(fore200e->stats->oc3.line_febe_errors);
+ tmp.path_febe = be32_to_cpu(fore200e->stats->oc3.path_febe_errors);
+ tmp.corr_hcs = be32_to_cpu(fore200e->stats->oc3.corr_hcs_errors);
+ tmp.uncorr_hcs = be32_to_cpu(fore200e->stats->oc3.ucorr_hcs_errors);
+ tmp.tx_cells = be32_to_cpu(fore200e->stats->aal0.cells_transmitted) +
+ be32_to_cpu(fore200e->stats->aal34.cells_transmitted) +
+ be32_to_cpu(fore200e->stats->aal5.cells_transmitted);
+ tmp.rx_cells = be32_to_cpu(fore200e->stats->aal0.cells_received) +
+ be32_to_cpu(fore200e->stats->aal34.cells_received) +
+ be32_to_cpu(fore200e->stats->aal5.cells_received);
+
+ if (arg)
+ return copy_to_user(arg, &tmp, sizeof(struct sonet_stats)) ? -EFAULT : 0;
+
+ return 0;
+}
+
+
+static int
+fore200e_ioctl(struct atm_dev* dev, unsigned int cmd, void __user * arg)
+{
+ struct fore200e* fore200e = FORE200E_DEV(dev);
+
+ DPRINTK(2, "ioctl cmd = 0x%x (%u), arg = 0x%p (%lu)\n", cmd, cmd, arg, (unsigned long)arg);
+
+ switch (cmd) {
+
+ case SONET_GETSTAT:
+ return fore200e_fetch_stats(fore200e, (struct sonet_stats __user *)arg);
+
+ case SONET_GETDIAG:
+ return put_user(0, (int __user *)arg) ? -EFAULT : 0;
+
+ case ATM_SETLOOP:
+ return fore200e_setloop(fore200e, (int)(unsigned long)arg);
+
+ case ATM_GETLOOP:
+ return put_user(fore200e->loop_mode, (int __user *)arg) ? -EFAULT : 0;
+
+ case ATM_QUERYLOOP:
+ return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY, (int __user *)arg) ? -EFAULT : 0;
+ }
+
+ return -ENOSYS; /* not implemented */
+}
+
+
+static int
+fore200e_change_qos(struct atm_vcc* vcc,struct atm_qos* qos, int flags)
+{
+ struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc);
+ struct fore200e* fore200e = FORE200E_DEV(vcc->dev);
+
+ if (!test_bit(ATM_VF_READY, &vcc->flags)) {
+ DPRINTK(1, "VC %d.%d.%d not ready for QoS change\n", vcc->itf, vcc->vpi, vcc->vpi);
+ return -EINVAL;
+ }
+
+ DPRINTK(2, "change_qos %d.%d.%d, "
+ "(tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; "
+ "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d), flags = 0x%x\n"
+ "available_cell_rate = %u",
+ vcc->itf, vcc->vpi, vcc->vci,
+ fore200e_traffic_class[ qos->txtp.traffic_class ],
+ qos->txtp.min_pcr, qos->txtp.max_pcr, qos->txtp.max_cdv, qos->txtp.max_sdu,
+ fore200e_traffic_class[ qos->rxtp.traffic_class ],
+ qos->rxtp.min_pcr, qos->rxtp.max_pcr, qos->rxtp.max_cdv, qos->rxtp.max_sdu,
+ flags, fore200e->available_cell_rate);
+
+ if ((qos->txtp.traffic_class == ATM_CBR) && (qos->txtp.max_pcr > 0)) {
+
+ mutex_lock(&fore200e->rate_mtx);
+ if (fore200e->available_cell_rate + vcc->qos.txtp.max_pcr < qos->txtp.max_pcr) {
+ mutex_unlock(&fore200e->rate_mtx);
+ return -EAGAIN;
+ }
+
+ fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
+ fore200e->available_cell_rate -= qos->txtp.max_pcr;
+
+ mutex_unlock(&fore200e->rate_mtx);
+
+ memcpy(&vcc->qos, qos, sizeof(struct atm_qos));
+
+ /* update rate control parameters */
+ fore200e_rate_ctrl(qos, &fore200e_vcc->rate);
+
+ set_bit(ATM_VF_HASQOS, &vcc->flags);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static int fore200e_irq_request(struct fore200e *fore200e)
+{
+ if (request_irq(fore200e->irq, fore200e_interrupt, IRQF_SHARED, fore200e->name, fore200e->atm_dev) < 0) {
+
+ printk(FORE200E "unable to reserve IRQ %s for device %s\n",
+ fore200e_irq_itoa(fore200e->irq), fore200e->name);
+ return -EBUSY;
+ }
+
+ printk(FORE200E "IRQ %s reserved for device %s\n",
+ fore200e_irq_itoa(fore200e->irq), fore200e->name);
+
+#ifdef FORE200E_USE_TASKLET
+ tasklet_init(&fore200e->tx_tasklet, fore200e_tx_tasklet, (unsigned long)fore200e);
+ tasklet_init(&fore200e->rx_tasklet, fore200e_rx_tasklet, (unsigned long)fore200e);
+#endif
+
+ fore200e->state = FORE200E_STATE_IRQ;
+ return 0;
+}
+
+
+static int fore200e_get_esi(struct fore200e *fore200e)
+{
+ struct prom_data* prom = kzalloc(sizeof(struct prom_data), GFP_KERNEL | GFP_DMA);
+ int ok, i;
+
+ if (!prom)
+ return -ENOMEM;
+
+ ok = fore200e->bus->prom_read(fore200e, prom);
+ if (ok < 0) {
+ kfree(prom);
+ return -EBUSY;
+ }
+
+ printk(FORE200E "device %s, rev. %c, S/N: %d, ESI: %pM\n",
+ fore200e->name,
+ (prom->hw_revision & 0xFF) + '@', /* probably meaningless with SBA boards */
+ prom->serial_number & 0xFFFF, &prom->mac_addr[2]);
+
+ for (i = 0; i < ESI_LEN; i++) {
+ fore200e->esi[ i ] = fore200e->atm_dev->esi[ i ] = prom->mac_addr[ i + 2 ];
+ }
+
+ kfree(prom);
+
+ return 0;
+}
+
+
+static int fore200e_alloc_rx_buf(struct fore200e *fore200e)
+{
+ int scheme, magn, nbr, size, i;
+
+ struct host_bsq* bsq;
+ struct buffer* buffer;
+
+ for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) {
+ for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) {
+
+ bsq = &fore200e->host_bsq[ scheme ][ magn ];
+
+ nbr = fore200e_rx_buf_nbr[ scheme ][ magn ];
+ size = fore200e_rx_buf_size[ scheme ][ magn ];
+
+ DPRINTK(2, "rx buffers %d / %d are being allocated\n", scheme, magn);
+
+ /* allocate the array of receive buffers */
+ buffer = bsq->buffer = kzalloc(nbr * sizeof(struct buffer), GFP_KERNEL);
+
+ if (buffer == NULL)
+ return -ENOMEM;
+
+ bsq->freebuf = NULL;
+
+ for (i = 0; i < nbr; i++) {
+
+ buffer[ i ].scheme = scheme;
+ buffer[ i ].magn = magn;
+#ifdef FORE200E_BSQ_DEBUG
+ buffer[ i ].index = i;
+ buffer[ i ].supplied = 0;
+#endif
+
+ /* allocate the receive buffer body */
+ if (fore200e_chunk_alloc(fore200e,
+ &buffer[ i ].data, size, fore200e->bus->buffer_alignment,
+ DMA_FROM_DEVICE) < 0) {
+
+ while (i > 0)
+ fore200e_chunk_free(fore200e, &buffer[ --i ].data);
+ kfree(buffer);
+
+ return -ENOMEM;
+ }
+
+ /* insert the buffer into the free buffer list */
+ buffer[ i ].next = bsq->freebuf;
+ bsq->freebuf = &buffer[ i ];
+ }
+ /* all the buffers are free, initially */
+ bsq->freebuf_count = nbr;
+
+#ifdef FORE200E_BSQ_DEBUG
+ bsq_audit(3, bsq, scheme, magn);
+#endif
+ }
+ }
+
+ fore200e->state = FORE200E_STATE_ALLOC_BUF;
+ return 0;
+}
+
+
+static int fore200e_init_bs_queue(struct fore200e *fore200e)
+{
+ int scheme, magn, i;
+
+ struct host_bsq* bsq;
+ struct cp_bsq_entry __iomem * cp_entry;
+
+ for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) {
+ for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) {
+
+ DPRINTK(2, "buffer supply queue %d / %d is being initialized\n", scheme, magn);
+
+ bsq = &fore200e->host_bsq[ scheme ][ magn ];
+
+ /* allocate and align the array of status words */
+ if (fore200e->bus->dma_chunk_alloc(fore200e,
+ &bsq->status,
+ sizeof(enum status),
+ QUEUE_SIZE_BS,
+ fore200e->bus->status_alignment) < 0) {
+ return -ENOMEM;
+ }
+
+ /* allocate and align the array of receive buffer descriptors */
+ if (fore200e->bus->dma_chunk_alloc(fore200e,
+ &bsq->rbd_block,
+ sizeof(struct rbd_block),
+ QUEUE_SIZE_BS,
+ fore200e->bus->descr_alignment) < 0) {
+
+ fore200e->bus->dma_chunk_free(fore200e, &bsq->status);
+ return -ENOMEM;
+ }
+
+ /* get the base address of the cp resident buffer supply queue entries */
+ cp_entry = fore200e->virt_base +
+ fore200e->bus->read(&fore200e->cp_queues->cp_bsq[ scheme ][ magn ]);
+
+ /* fill the host resident and cp resident buffer supply queue entries */
+ for (i = 0; i < QUEUE_SIZE_BS; i++) {
+
+ bsq->host_entry[ i ].status =
+ FORE200E_INDEX(bsq->status.align_addr, enum status, i);
+ bsq->host_entry[ i ].rbd_block =
+ FORE200E_INDEX(bsq->rbd_block.align_addr, struct rbd_block, i);
+ bsq->host_entry[ i ].rbd_block_dma =
+ FORE200E_DMA_INDEX(bsq->rbd_block.dma_addr, struct rbd_block, i);
+ bsq->host_entry[ i ].cp_entry = &cp_entry[ i ];
+
+ *bsq->host_entry[ i ].status = STATUS_FREE;
+
+ fore200e->bus->write(FORE200E_DMA_INDEX(bsq->status.dma_addr, enum status, i),
+ &cp_entry[ i ].status_haddr);
+ }
+ }
+ }
+
+ fore200e->state = FORE200E_STATE_INIT_BSQ;
+ return 0;
+}
+
+
+static int fore200e_init_rx_queue(struct fore200e *fore200e)
+{
+ struct host_rxq* rxq = &fore200e->host_rxq;
+ struct cp_rxq_entry __iomem * cp_entry;
+ int i;
+
+ DPRINTK(2, "receive queue is being initialized\n");
+
+ /* allocate and align the array of status words */
+ if (fore200e->bus->dma_chunk_alloc(fore200e,
+ &rxq->status,
+ sizeof(enum status),
+ QUEUE_SIZE_RX,
+ fore200e->bus->status_alignment) < 0) {
+ return -ENOMEM;
+ }
+
+ /* allocate and align the array of receive PDU descriptors */
+ if (fore200e->bus->dma_chunk_alloc(fore200e,
+ &rxq->rpd,
+ sizeof(struct rpd),
+ QUEUE_SIZE_RX,
+ fore200e->bus->descr_alignment) < 0) {
+
+ fore200e->bus->dma_chunk_free(fore200e, &rxq->status);
+ return -ENOMEM;
+ }
+
+ /* get the base address of the cp resident rx queue entries */
+ cp_entry = fore200e->virt_base + fore200e->bus->read(&fore200e->cp_queues->cp_rxq);
+
+ /* fill the host resident and cp resident rx entries */
+ for (i=0; i < QUEUE_SIZE_RX; i++) {
+
+ rxq->host_entry[ i ].status =
+ FORE200E_INDEX(rxq->status.align_addr, enum status, i);
+ rxq->host_entry[ i ].rpd =
+ FORE200E_INDEX(rxq->rpd.align_addr, struct rpd, i);
+ rxq->host_entry[ i ].rpd_dma =
+ FORE200E_DMA_INDEX(rxq->rpd.dma_addr, struct rpd, i);
+ rxq->host_entry[ i ].cp_entry = &cp_entry[ i ];
+
+ *rxq->host_entry[ i ].status = STATUS_FREE;
+
+ fore200e->bus->write(FORE200E_DMA_INDEX(rxq->status.dma_addr, enum status, i),
+ &cp_entry[ i ].status_haddr);
+
+ fore200e->bus->write(FORE200E_DMA_INDEX(rxq->rpd.dma_addr, struct rpd, i),
+ &cp_entry[ i ].rpd_haddr);
+ }
+
+ /* set the head entry of the queue */
+ rxq->head = 0;
+
+ fore200e->state = FORE200E_STATE_INIT_RXQ;
+ return 0;
+}
+
+
+static int fore200e_init_tx_queue(struct fore200e *fore200e)
+{
+ struct host_txq* txq = &fore200e->host_txq;
+ struct cp_txq_entry __iomem * cp_entry;
+ int i;
+
+ DPRINTK(2, "transmit queue is being initialized\n");
+
+ /* allocate and align the array of status words */
+ if (fore200e->bus->dma_chunk_alloc(fore200e,
+ &txq->status,
+ sizeof(enum status),
+ QUEUE_SIZE_TX,
+ fore200e->bus->status_alignment) < 0) {
+ return -ENOMEM;
+ }
+
+ /* allocate and align the array of transmit PDU descriptors */
+ if (fore200e->bus->dma_chunk_alloc(fore200e,
+ &txq->tpd,
+ sizeof(struct tpd),
+ QUEUE_SIZE_TX,
+ fore200e->bus->descr_alignment) < 0) {
+
+ fore200e->bus->dma_chunk_free(fore200e, &txq->status);
+ return -ENOMEM;
+ }
+
+ /* get the base address of the cp resident tx queue entries */
+ cp_entry = fore200e->virt_base + fore200e->bus->read(&fore200e->cp_queues->cp_txq);
+
+ /* fill the host resident and cp resident tx entries */
+ for (i=0; i < QUEUE_SIZE_TX; i++) {
+
+ txq->host_entry[ i ].status =
+ FORE200E_INDEX(txq->status.align_addr, enum status, i);
+ txq->host_entry[ i ].tpd =
+ FORE200E_INDEX(txq->tpd.align_addr, struct tpd, i);
+ txq->host_entry[ i ].tpd_dma =
+ FORE200E_DMA_INDEX(txq->tpd.dma_addr, struct tpd, i);
+ txq->host_entry[ i ].cp_entry = &cp_entry[ i ];
+
+ *txq->host_entry[ i ].status = STATUS_FREE;
+
+ fore200e->bus->write(FORE200E_DMA_INDEX(txq->status.dma_addr, enum status, i),
+ &cp_entry[ i ].status_haddr);
+
+ /* although there is a one-to-one mapping of tx queue entries and tpds,
+ we do not write here the DMA (physical) base address of each tpd into
+ the related cp resident entry, because the cp relies on this write
+ operation to detect that a new pdu has been submitted for tx */
+ }
+
+ /* set the head and tail entries of the queue */
+ txq->head = 0;
+ txq->tail = 0;
+
+ fore200e->state = FORE200E_STATE_INIT_TXQ;
+ return 0;
+}
+
+
+static int fore200e_init_cmd_queue(struct fore200e *fore200e)
+{
+ struct host_cmdq* cmdq = &fore200e->host_cmdq;
+ struct cp_cmdq_entry __iomem * cp_entry;
+ int i;
+
+ DPRINTK(2, "command queue is being initialized\n");
+
+ /* allocate and align the array of status words */
+ if (fore200e->bus->dma_chunk_alloc(fore200e,
+ &cmdq->status,
+ sizeof(enum status),
+ QUEUE_SIZE_CMD,
+ fore200e->bus->status_alignment) < 0) {
+ return -ENOMEM;
+ }
+
+ /* get the base address of the cp resident cmd queue entries */
+ cp_entry = fore200e->virt_base + fore200e->bus->read(&fore200e->cp_queues->cp_cmdq);
+
+ /* fill the host resident and cp resident cmd entries */
+ for (i=0; i < QUEUE_SIZE_CMD; i++) {
+
+ cmdq->host_entry[ i ].status =
+ FORE200E_INDEX(cmdq->status.align_addr, enum status, i);
+ cmdq->host_entry[ i ].cp_entry = &cp_entry[ i ];
+
+ *cmdq->host_entry[ i ].status = STATUS_FREE;
+
+ fore200e->bus->write(FORE200E_DMA_INDEX(cmdq->status.dma_addr, enum status, i),
+ &cp_entry[ i ].status_haddr);
+ }
+
+ /* set the head entry of the queue */
+ cmdq->head = 0;
+
+ fore200e->state = FORE200E_STATE_INIT_CMDQ;
+ return 0;
+}
+
+
+static void fore200e_param_bs_queue(struct fore200e *fore200e,
+ enum buffer_scheme scheme,
+ enum buffer_magn magn, int queue_length,
+ int pool_size, int supply_blksize)
+{
+ struct bs_spec __iomem * bs_spec = &fore200e->cp_queues->init.bs_spec[ scheme ][ magn ];
+
+ fore200e->bus->write(queue_length, &bs_spec->queue_length);
+ fore200e->bus->write(fore200e_rx_buf_size[ scheme ][ magn ], &bs_spec->buffer_size);
+ fore200e->bus->write(pool_size, &bs_spec->pool_size);
+ fore200e->bus->write(supply_blksize, &bs_spec->supply_blksize);
+}
+
+
+static int fore200e_initialize(struct fore200e *fore200e)
+{
+ struct cp_queues __iomem * cpq;
+ int ok, scheme, magn;
+
+ DPRINTK(2, "device %s being initialized\n", fore200e->name);
+
+ mutex_init(&fore200e->rate_mtx);
+ spin_lock_init(&fore200e->q_lock);
+
+ cpq = fore200e->cp_queues = fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET;
+
+ /* enable cp to host interrupts */
+ fore200e->bus->write(1, &cpq->imask);
+
+ if (fore200e->bus->irq_enable)
+ fore200e->bus->irq_enable(fore200e);
+
+ fore200e->bus->write(NBR_CONNECT, &cpq->init.num_connect);
+
+ fore200e->bus->write(QUEUE_SIZE_CMD, &cpq->init.cmd_queue_len);
+ fore200e->bus->write(QUEUE_SIZE_RX, &cpq->init.rx_queue_len);
+ fore200e->bus->write(QUEUE_SIZE_TX, &cpq->init.tx_queue_len);
+
+ fore200e->bus->write(RSD_EXTENSION, &cpq->init.rsd_extension);
+ fore200e->bus->write(TSD_EXTENSION, &cpq->init.tsd_extension);
+
+ for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++)
+ for (magn = 0; magn < BUFFER_MAGN_NBR; magn++)
+ fore200e_param_bs_queue(fore200e, scheme, magn,
+ QUEUE_SIZE_BS,
+ fore200e_rx_buf_nbr[ scheme ][ magn ],
+ RBD_BLK_SIZE);
+
+ /* issue the initialize command */
+ fore200e->bus->write(STATUS_PENDING, &cpq->init.status);
+ fore200e->bus->write(OPCODE_INITIALIZE, &cpq->init.opcode);
+
+ ok = fore200e_io_poll(fore200e, &cpq->init.status, STATUS_COMPLETE, 3000);
+ if (ok == 0) {
+ printk(FORE200E "device %s initialization failed\n", fore200e->name);
+ return -ENODEV;
+ }
+
+ printk(FORE200E "device %s initialized\n", fore200e->name);
+
+ fore200e->state = FORE200E_STATE_INITIALIZE;
+ return 0;
+}
+
+
+static void fore200e_monitor_putc(struct fore200e *fore200e, char c)
+{
+ struct cp_monitor __iomem * monitor = fore200e->cp_monitor;
+
+#if 0
+ printk("%c", c);
+#endif
+ fore200e->bus->write(((u32) c) | FORE200E_CP_MONITOR_UART_AVAIL, &monitor->soft_uart.send);
+}
+
+
+static int fore200e_monitor_getc(struct fore200e *fore200e)
+{
+ struct cp_monitor __iomem * monitor = fore200e->cp_monitor;
+ unsigned long timeout = jiffies + msecs_to_jiffies(50);
+ int c;
+
+ while (time_before(jiffies, timeout)) {
+
+ c = (int) fore200e->bus->read(&monitor->soft_uart.recv);
+
+ if (c & FORE200E_CP_MONITOR_UART_AVAIL) {
+
+ fore200e->bus->write(FORE200E_CP_MONITOR_UART_FREE, &monitor->soft_uart.recv);
+#if 0
+ printk("%c", c & 0xFF);
+#endif
+ return c & 0xFF;
+ }
+ }
+
+ return -1;
+}
+
+
+static void fore200e_monitor_puts(struct fore200e *fore200e, char *str)
+{
+ while (*str) {
+
+ /* the i960 monitor doesn't accept any new character if it has something to say */
+ while (fore200e_monitor_getc(fore200e) >= 0);
+
+ fore200e_monitor_putc(fore200e, *str++);
+ }
+
+ while (fore200e_monitor_getc(fore200e) >= 0);
+}
+
+/*(DEBLOBBED)*/
+
+static int fore200e_load_and_start_fw(struct fore200e *fore200e)
+{
+ const struct firmware *firmware;
+ struct device *device;
+ struct fw_header *fw_header;
+ const __le32 *fw_data;
+ u32 fw_size;
+ u32 __iomem *load_addr;
+ char buf[48];
+ int err = -ENODEV;
+
+ if (strcmp(fore200e->bus->model_name, "PCA-200E") == 0)
+ device = &((struct pci_dev *) fore200e->bus_dev)->dev;
+#ifdef CONFIG_SBUS
+ else if (strcmp(fore200e->bus->model_name, "SBA-200E") == 0)
+ device = &((struct platform_device *) fore200e->bus_dev)->dev;
+#endif
+ else
+ return err;
+
+ /*(DEBLOBBED)*/
+ if ((err = reject_firmware(&firmware, buf, device)) < 0) {
+ printk(FORE200E "problem loading firmware image %s\n", fore200e->bus->model_name);
+ return err;
+ }
+
+ fw_data = (__le32 *) firmware->data;
+ fw_size = firmware->size / sizeof(u32);
+ fw_header = (struct fw_header *) firmware->data;
+ load_addr = fore200e->virt_base + le32_to_cpu(fw_header->load_offset);
+
+ DPRINTK(2, "device %s firmware being loaded at 0x%p (%d words)\n",
+ fore200e->name, load_addr, fw_size);
+
+ if (le32_to_cpu(fw_header->magic) != FW_HEADER_MAGIC) {
+ printk(FORE200E "corrupted %s firmware image\n", fore200e->bus->model_name);
+ goto release;
+ }
+
+ for (; fw_size--; fw_data++, load_addr++)
+ fore200e->bus->write(le32_to_cpu(*fw_data), load_addr);
+
+ DPRINTK(2, "device %s firmware being started\n", fore200e->name);
+
+#if defined(__sparc_v9__)
+ /* reported to be required by SBA cards on some sparc64 hosts */
+ fore200e_spin(100);
+#endif
+
+ sprintf(buf, "\rgo %x\r", le32_to_cpu(fw_header->start_offset));
+ fore200e_monitor_puts(fore200e, buf);
+
+ if (fore200e_io_poll(fore200e, &fore200e->cp_monitor->bstat, BSTAT_CP_RUNNING, 1000) == 0) {
+ printk(FORE200E "device %s firmware didn't start\n", fore200e->name);
+ goto release;
+ }
+
+ printk(FORE200E "device %s firmware started\n", fore200e->name);
+
+ fore200e->state = FORE200E_STATE_START_FW;
+ err = 0;
+
+release:
+ release_firmware(firmware);
+ return err;
+}
+
+
+static int fore200e_register(struct fore200e *fore200e, struct device *parent)
+{
+ struct atm_dev* atm_dev;
+
+ DPRINTK(2, "device %s being registered\n", fore200e->name);
+
+ atm_dev = atm_dev_register(fore200e->bus->proc_name, parent, &fore200e_ops,
+ -1, NULL);
+ if (atm_dev == NULL) {
+ printk(FORE200E "unable to register device %s\n", fore200e->name);
+ return -ENODEV;
+ }
+
+ atm_dev->dev_data = fore200e;
+ fore200e->atm_dev = atm_dev;
+
+ atm_dev->ci_range.vpi_bits = FORE200E_VPI_BITS;
+ atm_dev->ci_range.vci_bits = FORE200E_VCI_BITS;
+
+ fore200e->available_cell_rate = ATM_OC3_PCR;
+
+ fore200e->state = FORE200E_STATE_REGISTER;
+ return 0;
+}
+
+
+static int fore200e_init(struct fore200e *fore200e, struct device *parent)
+{
+ if (fore200e_register(fore200e, parent) < 0)
+ return -ENODEV;
+
+ if (fore200e->bus->configure(fore200e) < 0)
+ return -ENODEV;
+
+ if (fore200e->bus->map(fore200e) < 0)
+ return -ENODEV;
+
+ if (fore200e_reset(fore200e, 1) < 0)
+ return -ENODEV;
+
+ if (fore200e_load_and_start_fw(fore200e) < 0)
+ return -ENODEV;
+
+ if (fore200e_initialize(fore200e) < 0)
+ return -ENODEV;
+
+ if (fore200e_init_cmd_queue(fore200e) < 0)
+ return -ENOMEM;
+
+ if (fore200e_init_tx_queue(fore200e) < 0)
+ return -ENOMEM;
+
+ if (fore200e_init_rx_queue(fore200e) < 0)
+ return -ENOMEM;
+
+ if (fore200e_init_bs_queue(fore200e) < 0)
+ return -ENOMEM;
+
+ if (fore200e_alloc_rx_buf(fore200e) < 0)
+ return -ENOMEM;
+
+ if (fore200e_get_esi(fore200e) < 0)
+ return -EIO;
+
+ if (fore200e_irq_request(fore200e) < 0)
+ return -EBUSY;
+
+ fore200e_supply(fore200e);
+
+ /* all done, board initialization is now complete */
+ fore200e->state = FORE200E_STATE_COMPLETE;
+ return 0;
+}
+
+#ifdef CONFIG_SBUS
+static const struct of_device_id fore200e_sba_match[];
+static int fore200e_sba_probe(struct platform_device *op)
+{
+ const struct of_device_id *match;
+ const struct fore200e_bus *bus;
+ struct fore200e *fore200e;
+ static int index = 0;
+ int err;
+
+ match = of_match_device(fore200e_sba_match, &op->dev);
+ if (!match)
+ return -EINVAL;
+ bus = match->data;
+
+ fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL);
+ if (!fore200e)
+ return -ENOMEM;
+
+ fore200e->bus = bus;
+ fore200e->bus_dev = op;
+ fore200e->irq = op->archdata.irqs[0];
+ fore200e->phys_base = op->resource[0].start;
+
+ sprintf(fore200e->name, "%s-%d", bus->model_name, index);
+
+ err = fore200e_init(fore200e, &op->dev);
+ if (err < 0) {
+ fore200e_shutdown(fore200e);
+ kfree(fore200e);
+ return err;
+ }
+
+ index++;
+ dev_set_drvdata(&op->dev, fore200e);
+
+ return 0;
+}
+
+static int fore200e_sba_remove(struct platform_device *op)
+{
+ struct fore200e *fore200e = dev_get_drvdata(&op->dev);
+
+ fore200e_shutdown(fore200e);
+ kfree(fore200e);
+
+ return 0;
+}
+
+static const struct of_device_id fore200e_sba_match[] = {
+ {
+ .name = SBA200E_PROM_NAME,
+ .data = (void *) &fore200e_bus[1],
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fore200e_sba_match);
+
+static struct platform_driver fore200e_sba_driver = {
+ .driver = {
+ .name = "fore_200e",
+ .of_match_table = fore200e_sba_match,
+ },
+ .probe = fore200e_sba_probe,
+ .remove = fore200e_sba_remove,
+};
+#endif
+
+#ifdef CONFIG_PCI
+static int fore200e_pca_detect(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_ent)
+{
+ const struct fore200e_bus* bus = (struct fore200e_bus*) pci_ent->driver_data;
+ struct fore200e* fore200e;
+ int err = 0;
+ static int index = 0;
+
+ if (pci_enable_device(pci_dev)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32))) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL);
+ if (fore200e == NULL) {
+ err = -ENOMEM;
+ goto out_disable;
+ }
+
+ fore200e->bus = bus;
+ fore200e->bus_dev = pci_dev;
+ fore200e->irq = pci_dev->irq;
+ fore200e->phys_base = pci_resource_start(pci_dev, 0);
+
+ sprintf(fore200e->name, "%s-%d", bus->model_name, index - 1);
+
+ pci_set_master(pci_dev);
+
+ printk(FORE200E "device %s found at 0x%lx, IRQ %s\n",
+ fore200e->bus->model_name,
+ fore200e->phys_base, fore200e_irq_itoa(fore200e->irq));
+
+ sprintf(fore200e->name, "%s-%d", bus->model_name, index);
+
+ err = fore200e_init(fore200e, &pci_dev->dev);
+ if (err < 0) {
+ fore200e_shutdown(fore200e);
+ goto out_free;
+ }
+
+ ++index;
+ pci_set_drvdata(pci_dev, fore200e);
+
+out:
+ return err;
+
+out_free:
+ kfree(fore200e);
+out_disable:
+ pci_disable_device(pci_dev);
+ goto out;
+}
+
+
+static void fore200e_pca_remove_one(struct pci_dev *pci_dev)
+{
+ struct fore200e *fore200e;
+
+ fore200e = pci_get_drvdata(pci_dev);
+
+ fore200e_shutdown(fore200e);
+ kfree(fore200e);
+ pci_disable_device(pci_dev);
+}
+
+
+static struct pci_device_id fore200e_pca_tbl[] = {
+ { PCI_VENDOR_ID_FORE, PCI_DEVICE_ID_FORE_PCA200E, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, (unsigned long) &fore200e_bus[0] },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, fore200e_pca_tbl);
+
+static struct pci_driver fore200e_pca_driver = {
+ .name = "fore_200e",
+ .probe = fore200e_pca_detect,
+ .remove = fore200e_pca_remove_one,
+ .id_table = fore200e_pca_tbl,
+};
+#endif
+
+static int __init fore200e_module_init(void)
+{
+ int err = 0;
+
+ printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n");
+
+#ifdef CONFIG_SBUS
+ err = platform_driver_register(&fore200e_sba_driver);
+ if (err)
+ return err;
+#endif
+
+#ifdef CONFIG_PCI
+ err = pci_register_driver(&fore200e_pca_driver);
+#endif
+
+#ifdef CONFIG_SBUS
+ if (err)
+ platform_driver_unregister(&fore200e_sba_driver);
+#endif
+
+ return err;
+}
+
+static void __exit fore200e_module_cleanup(void)
+{
+#ifdef CONFIG_PCI
+ pci_unregister_driver(&fore200e_pca_driver);
+#endif
+#ifdef CONFIG_SBUS
+ platform_driver_unregister(&fore200e_sba_driver);
+#endif
+}
+
+static int
+fore200e_proc_read(struct atm_dev *dev, loff_t* pos, char* page)
+{
+ struct fore200e* fore200e = FORE200E_DEV(dev);
+ struct fore200e_vcc* fore200e_vcc;
+ struct atm_vcc* vcc;
+ int i, len, left = *pos;
+ unsigned long flags;
+
+ if (!left--) {
+
+ if (fore200e_getstats(fore200e) < 0)
+ return -EIO;
+
+ len = sprintf(page,"\n"
+ " device:\n"
+ " internal name:\t\t%s\n", fore200e->name);
+
+ /* print bus-specific information */
+ if (fore200e->bus->proc_read)
+ len += fore200e->bus->proc_read(fore200e, page + len);
+
+ len += sprintf(page + len,
+ " interrupt line:\t\t%s\n"
+ " physical base address:\t0x%p\n"
+ " virtual base address:\t0x%p\n"
+ " factory address (ESI):\t%pM\n"
+ " board serial number:\t\t%d\n\n",
+ fore200e_irq_itoa(fore200e->irq),
+ (void*)fore200e->phys_base,
+ fore200e->virt_base,
+ fore200e->esi,
+ fore200e->esi[4] * 256 + fore200e->esi[5]);
+
+ return len;
+ }
+
+ if (!left--)
+ return sprintf(page,
+ " free small bufs, scheme 1:\t%d\n"
+ " free large bufs, scheme 1:\t%d\n"
+ " free small bufs, scheme 2:\t%d\n"
+ " free large bufs, scheme 2:\t%d\n",
+ fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_SMALL ].freebuf_count,
+ fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_LARGE ].freebuf_count,
+ fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_SMALL ].freebuf_count,
+ fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_LARGE ].freebuf_count);
+
+ if (!left--) {
+ u32 hb = fore200e->bus->read(&fore200e->cp_queues->heartbeat);
+
+ len = sprintf(page,"\n\n"
+ " cell processor:\n"
+ " heartbeat state:\t\t");
+
+ if (hb >> 16 != 0xDEAD)
+ len += sprintf(page + len, "0x%08x\n", hb);
+ else
+ len += sprintf(page + len, "*** FATAL ERROR %04x ***\n", hb & 0xFFFF);
+
+ return len;
+ }
+
+ if (!left--) {
+ static const char* media_name[] = {
+ "unshielded twisted pair",
+ "multimode optical fiber ST",
+ "multimode optical fiber SC",
+ "single-mode optical fiber ST",
+ "single-mode optical fiber SC",
+ "unknown"
+ };
+
+ static const char* oc3_mode[] = {
+ "normal operation",
+ "diagnostic loopback",
+ "line loopback",
+ "unknown"
+ };
+
+ u32 fw_release = fore200e->bus->read(&fore200e->cp_queues->fw_release);
+ u32 mon960_release = fore200e->bus->read(&fore200e->cp_queues->mon960_release);
+ u32 oc3_revision = fore200e->bus->read(&fore200e->cp_queues->oc3_revision);
+ u32 media_index = FORE200E_MEDIA_INDEX(fore200e->bus->read(&fore200e->cp_queues->media_type));
+ u32 oc3_index;
+
+ if (media_index > 4)
+ media_index = 5;
+
+ switch (fore200e->loop_mode) {
+ case ATM_LM_NONE: oc3_index = 0;
+ break;
+ case ATM_LM_LOC_PHY: oc3_index = 1;
+ break;
+ case ATM_LM_RMT_PHY: oc3_index = 2;
+ break;
+ default: oc3_index = 3;
+ }
+
+ return sprintf(page,
+ " firmware release:\t\t%d.%d.%d\n"
+ " monitor release:\t\t%d.%d\n"
+ " media type:\t\t\t%s\n"
+ " OC-3 revision:\t\t0x%x\n"
+ " OC-3 mode:\t\t\t%s",
+ fw_release >> 16, fw_release << 16 >> 24, fw_release << 24 >> 24,
+ mon960_release >> 16, mon960_release << 16 >> 16,
+ media_name[ media_index ],
+ oc3_revision,
+ oc3_mode[ oc3_index ]);
+ }
+
+ if (!left--) {
+ struct cp_monitor __iomem * cp_monitor = fore200e->cp_monitor;
+
+ return sprintf(page,
+ "\n\n"
+ " monitor:\n"
+ " version number:\t\t%d\n"
+ " boot status word:\t\t0x%08x\n",
+ fore200e->bus->read(&cp_monitor->mon_version),
+ fore200e->bus->read(&cp_monitor->bstat));
+ }
+
+ if (!left--)
+ return sprintf(page,
+ "\n"
+ " device statistics:\n"
+ " 4b5b:\n"
+ " crc_header_errors:\t\t%10u\n"
+ " framing_errors:\t\t%10u\n",
+ be32_to_cpu(fore200e->stats->phy.crc_header_errors),
+ be32_to_cpu(fore200e->stats->phy.framing_errors));
+
+ if (!left--)
+ return sprintf(page, "\n"
+ " OC-3:\n"
+ " section_bip8_errors:\t%10u\n"
+ " path_bip8_errors:\t\t%10u\n"
+ " line_bip24_errors:\t\t%10u\n"
+ " line_febe_errors:\t\t%10u\n"
+ " path_febe_errors:\t\t%10u\n"
+ " corr_hcs_errors:\t\t%10u\n"
+ " ucorr_hcs_errors:\t\t%10u\n",
+ be32_to_cpu(fore200e->stats->oc3.section_bip8_errors),
+ be32_to_cpu(fore200e->stats->oc3.path_bip8_errors),
+ be32_to_cpu(fore200e->stats->oc3.line_bip24_errors),
+ be32_to_cpu(fore200e->stats->oc3.line_febe_errors),
+ be32_to_cpu(fore200e->stats->oc3.path_febe_errors),
+ be32_to_cpu(fore200e->stats->oc3.corr_hcs_errors),
+ be32_to_cpu(fore200e->stats->oc3.ucorr_hcs_errors));
+
+ if (!left--)
+ return sprintf(page,"\n"
+ " ATM:\t\t\t\t cells\n"
+ " TX:\t\t\t%10u\n"
+ " RX:\t\t\t%10u\n"
+ " vpi out of range:\t\t%10u\n"
+ " vpi no conn:\t\t%10u\n"
+ " vci out of range:\t\t%10u\n"
+ " vci no conn:\t\t%10u\n",
+ be32_to_cpu(fore200e->stats->atm.cells_transmitted),
+ be32_to_cpu(fore200e->stats->atm.cells_received),
+ be32_to_cpu(fore200e->stats->atm.vpi_bad_range),
+ be32_to_cpu(fore200e->stats->atm.vpi_no_conn),
+ be32_to_cpu(fore200e->stats->atm.vci_bad_range),
+ be32_to_cpu(fore200e->stats->atm.vci_no_conn));
+
+ if (!left--)
+ return sprintf(page,"\n"
+ " AAL0:\t\t\t cells\n"
+ " TX:\t\t\t%10u\n"
+ " RX:\t\t\t%10u\n"
+ " dropped:\t\t\t%10u\n",
+ be32_to_cpu(fore200e->stats->aal0.cells_transmitted),
+ be32_to_cpu(fore200e->stats->aal0.cells_received),
+ be32_to_cpu(fore200e->stats->aal0.cells_dropped));
+
+ if (!left--)
+ return sprintf(page,"\n"
+ " AAL3/4:\n"
+ " SAR sublayer:\t\t cells\n"
+ " TX:\t\t\t%10u\n"
+ " RX:\t\t\t%10u\n"
+ " dropped:\t\t\t%10u\n"
+ " CRC errors:\t\t%10u\n"
+ " protocol errors:\t\t%10u\n\n"
+ " CS sublayer:\t\t PDUs\n"
+ " TX:\t\t\t%10u\n"
+ " RX:\t\t\t%10u\n"
+ " dropped:\t\t\t%10u\n"
+ " protocol errors:\t\t%10u\n",
+ be32_to_cpu(fore200e->stats->aal34.cells_transmitted),
+ be32_to_cpu(fore200e->stats->aal34.cells_received),
+ be32_to_cpu(fore200e->stats->aal34.cells_dropped),
+ be32_to_cpu(fore200e->stats->aal34.cells_crc_errors),
+ be32_to_cpu(fore200e->stats->aal34.cells_protocol_errors),
+ be32_to_cpu(fore200e->stats->aal34.cspdus_transmitted),
+ be32_to_cpu(fore200e->stats->aal34.cspdus_received),
+ be32_to_cpu(fore200e->stats->aal34.cspdus_dropped),
+ be32_to_cpu(fore200e->stats->aal34.cspdus_protocol_errors));
+
+ if (!left--)
+ return sprintf(page,"\n"
+ " AAL5:\n"
+ " SAR sublayer:\t\t cells\n"
+ " TX:\t\t\t%10u\n"
+ " RX:\t\t\t%10u\n"
+ " dropped:\t\t\t%10u\n"
+ " congestions:\t\t%10u\n\n"
+ " CS sublayer:\t\t PDUs\n"
+ " TX:\t\t\t%10u\n"
+ " RX:\t\t\t%10u\n"
+ " dropped:\t\t\t%10u\n"
+ " CRC errors:\t\t%10u\n"
+ " protocol errors:\t\t%10u\n",
+ be32_to_cpu(fore200e->stats->aal5.cells_transmitted),
+ be32_to_cpu(fore200e->stats->aal5.cells_received),
+ be32_to_cpu(fore200e->stats->aal5.cells_dropped),
+ be32_to_cpu(fore200e->stats->aal5.congestion_experienced),
+ be32_to_cpu(fore200e->stats->aal5.cspdus_transmitted),
+ be32_to_cpu(fore200e->stats->aal5.cspdus_received),
+ be32_to_cpu(fore200e->stats->aal5.cspdus_dropped),
+ be32_to_cpu(fore200e->stats->aal5.cspdus_crc_errors),
+ be32_to_cpu(fore200e->stats->aal5.cspdus_protocol_errors));
+
+ if (!left--)
+ return sprintf(page,"\n"
+ " AUX:\t\t allocation failures\n"
+ " small b1:\t\t\t%10u\n"
+ " large b1:\t\t\t%10u\n"
+ " small b2:\t\t\t%10u\n"
+ " large b2:\t\t\t%10u\n"
+ " RX PDUs:\t\t\t%10u\n"
+ " TX PDUs:\t\t\t%10lu\n",
+ be32_to_cpu(fore200e->stats->aux.small_b1_failed),
+ be32_to_cpu(fore200e->stats->aux.large_b1_failed),
+ be32_to_cpu(fore200e->stats->aux.small_b2_failed),
+ be32_to_cpu(fore200e->stats->aux.large_b2_failed),
+ be32_to_cpu(fore200e->stats->aux.rpd_alloc_failed),
+ fore200e->tx_sat);
+
+ if (!left--)
+ return sprintf(page,"\n"
+ " receive carrier:\t\t\t%s\n",
+ fore200e->stats->aux.receive_carrier ? "ON" : "OFF!");
+
+ if (!left--) {
+ return sprintf(page,"\n"
+ " VCCs:\n address VPI VCI AAL "
+ "TX PDUs TX min/max size RX PDUs RX min/max size\n");
+ }
+
+ for (i = 0; i < NBR_CONNECT; i++) {
+
+ vcc = fore200e->vc_map[i].vcc;
+
+ if (vcc == NULL)
+ continue;
+
+ spin_lock_irqsave(&fore200e->q_lock, flags);
+
+ if (vcc && test_bit(ATM_VF_READY, &vcc->flags) && !left--) {
+
+ fore200e_vcc = FORE200E_VCC(vcc);
+ ASSERT(fore200e_vcc);
+
+ len = sprintf(page,
+ " %08x %03d %05d %1d %09lu %05d/%05d %09lu %05d/%05d\n",
+ (u32)(unsigned long)vcc,
+ vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal),
+ fore200e_vcc->tx_pdu,
+ fore200e_vcc->tx_min_pdu > 0xFFFF ? 0 : fore200e_vcc->tx_min_pdu,
+ fore200e_vcc->tx_max_pdu,
+ fore200e_vcc->rx_pdu,
+ fore200e_vcc->rx_min_pdu > 0xFFFF ? 0 : fore200e_vcc->rx_min_pdu,
+ fore200e_vcc->rx_max_pdu);
+
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+ return len;
+ }
+
+ spin_unlock_irqrestore(&fore200e->q_lock, flags);
+ }
+
+ return 0;
+}
+
+module_init(fore200e_module_init);
+module_exit(fore200e_module_cleanup);
+
+
+static const struct atmdev_ops fore200e_ops =
+{
+ .open = fore200e_open,
+ .close = fore200e_close,
+ .ioctl = fore200e_ioctl,
+ .getsockopt = fore200e_getsockopt,
+ .setsockopt = fore200e_setsockopt,
+ .send = fore200e_send,
+ .change_qos = fore200e_change_qos,
+ .proc_read = fore200e_proc_read,
+ .owner = THIS_MODULE
+};
+
+
+static const struct fore200e_bus fore200e_bus[] = {
+#ifdef CONFIG_PCI
+ { "PCA-200E", "pca200e", 32, 4, 32,
+ fore200e_pca_read,
+ fore200e_pca_write,
+ fore200e_pca_dma_map,
+ fore200e_pca_dma_unmap,
+ fore200e_pca_dma_sync_for_cpu,
+ fore200e_pca_dma_sync_for_device,
+ fore200e_pca_dma_chunk_alloc,
+ fore200e_pca_dma_chunk_free,
+ fore200e_pca_configure,
+ fore200e_pca_map,
+ fore200e_pca_reset,
+ fore200e_pca_prom_read,
+ fore200e_pca_unmap,
+ NULL,
+ fore200e_pca_irq_check,
+ fore200e_pca_irq_ack,
+ fore200e_pca_proc_read,
+ },
+#endif
+#ifdef CONFIG_SBUS
+ { "SBA-200E", "sba200e", 32, 64, 32,
+ fore200e_sba_read,
+ fore200e_sba_write,
+ fore200e_sba_dma_map,
+ fore200e_sba_dma_unmap,
+ fore200e_sba_dma_sync_for_cpu,
+ fore200e_sba_dma_sync_for_device,
+ fore200e_sba_dma_chunk_alloc,
+ fore200e_sba_dma_chunk_free,
+ fore200e_sba_configure,
+ fore200e_sba_map,
+ fore200e_sba_reset,
+ fore200e_sba_prom_read,
+ fore200e_sba_unmap,
+ fore200e_sba_irq_enable,
+ fore200e_sba_irq_check,
+ fore200e_sba_irq_ack,
+ fore200e_sba_proc_read,
+ },
+#endif
+ {}
+};
+
+MODULE_LICENSE("GPL");
+#ifdef CONFIG_PCI
+#ifdef __LITTLE_ENDIAN__
+/*(DEBLOBBED)*/
+#else
+/*(DEBLOBBED)*/
+#endif
+#endif /* CONFIG_PCI */
+#ifdef CONFIG_SBUS
+/*(DEBLOBBED)*/
+#endif
diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h
new file mode 100644
index 000000000..ba34a02b7
--- /dev/null
+++ b/drivers/atm/fore200e.h
@@ -0,0 +1,979 @@
+#ifndef _FORE200E_H
+#define _FORE200E_H
+
+#ifdef __KERNEL__
+
+/* rx buffer sizes */
+
+#define SMALL_BUFFER_SIZE 384 /* size of small buffers (multiple of 48 (PCA) and 64 (SBA) bytes) */
+#define LARGE_BUFFER_SIZE 4032 /* size of large buffers (multiple of 48 (PCA) and 64 (SBA) bytes) */
+
+
+#define RBD_BLK_SIZE 32 /* nbr of supplied rx buffers per rbd */
+
+
+#define MAX_PDU_SIZE 65535 /* maximum PDU size supported by AALs */
+
+
+#define BUFFER_S1_SIZE SMALL_BUFFER_SIZE /* size of small buffers, scheme 1 */
+#define BUFFER_L1_SIZE LARGE_BUFFER_SIZE /* size of large buffers, scheme 1 */
+
+#define BUFFER_S2_SIZE SMALL_BUFFER_SIZE /* size of small buffers, scheme 2 */
+#define BUFFER_L2_SIZE LARGE_BUFFER_SIZE /* size of large buffers, scheme 2 */
+
+#define BUFFER_S1_NBR (RBD_BLK_SIZE * 6)
+#define BUFFER_L1_NBR (RBD_BLK_SIZE * 4)
+
+#define BUFFER_S2_NBR (RBD_BLK_SIZE * 6)
+#define BUFFER_L2_NBR (RBD_BLK_SIZE * 4)
+
+
+#define QUEUE_SIZE_CMD 16 /* command queue capacity */
+#define QUEUE_SIZE_RX 64 /* receive queue capacity */
+#define QUEUE_SIZE_TX 256 /* transmit queue capacity */
+#define QUEUE_SIZE_BS 32 /* buffer supply queue capacity */
+
+#define FORE200E_VPI_BITS 0
+#define FORE200E_VCI_BITS 10
+#define NBR_CONNECT (1 << (FORE200E_VPI_BITS + FORE200E_VCI_BITS)) /* number of connections */
+
+
+#define TSD_FIXED 2
+#define TSD_EXTENSION 0
+#define TSD_NBR (TSD_FIXED + TSD_EXTENSION)
+
+
+/* the cp starts putting a received PDU into one *small* buffer,
+ then it uses a number of *large* buffers for the trailing data.
+ we compute here the total number of receive segment descriptors
+ required to hold the largest possible PDU */
+
+#define RSD_REQUIRED (((MAX_PDU_SIZE - SMALL_BUFFER_SIZE + LARGE_BUFFER_SIZE) / LARGE_BUFFER_SIZE) + 1)
+
+#define RSD_FIXED 3
+
+/* RSD_REQUIRED receive segment descriptors are enough to describe a max-sized PDU,
+ but we have to keep the size of the receive PDU descriptor multiple of 32 bytes,
+ so we add one extra RSD to RSD_EXTENSION
+ (WARNING: THIS MAY CHANGE IF BUFFER SIZES ARE MODIFIED) */
+
+#define RSD_EXTENSION ((RSD_REQUIRED - RSD_FIXED) + 1)
+#define RSD_NBR (RSD_FIXED + RSD_EXTENSION)
+
+
+#define FORE200E_DEV(d) ((struct fore200e*)((d)->dev_data))
+#define FORE200E_VCC(d) ((struct fore200e_vcc*)((d)->dev_data))
+
+/* bitfields endian games */
+
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+#define BITFIELD2(b1, b2) b1; b2;
+#define BITFIELD3(b1, b2, b3) b1; b2; b3;
+#define BITFIELD4(b1, b2, b3, b4) b1; b2; b3; b4;
+#define BITFIELD5(b1, b2, b3, b4, b5) b1; b2; b3; b4; b5;
+#define BITFIELD6(b1, b2, b3, b4, b5, b6) b1; b2; b3; b4; b5; b6;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+#define BITFIELD2(b1, b2) b2; b1;
+#define BITFIELD3(b1, b2, b3) b3; b2; b1;
+#define BITFIELD4(b1, b2, b3, b4) b4; b3; b2; b1;
+#define BITFIELD5(b1, b2, b3, b4, b5) b5; b4; b3; b2; b1;
+#define BITFIELD6(b1, b2, b3, b4, b5, b6) b6; b5; b4; b3; b2; b1;
+#else
+#error unknown bitfield endianess
+#endif
+
+
+/* ATM cell header (minus HEC byte) */
+
+typedef struct atm_header {
+ BITFIELD5(
+ u32 clp : 1, /* cell loss priority */
+ u32 plt : 3, /* payload type */
+ u32 vci : 16, /* virtual channel identifier */
+ u32 vpi : 8, /* virtual path identifier */
+ u32 gfc : 4 /* generic flow control */
+ )
+} atm_header_t;
+
+
+/* ATM adaptation layer id */
+
+typedef enum fore200e_aal {
+ FORE200E_AAL0 = 0,
+ FORE200E_AAL34 = 4,
+ FORE200E_AAL5 = 5,
+} fore200e_aal_t;
+
+
+/* transmit PDU descriptor specification */
+
+typedef struct tpd_spec {
+ BITFIELD4(
+ u32 length : 16, /* total PDU length */
+ u32 nseg : 8, /* number of transmit segments */
+ enum fore200e_aal aal : 4, /* adaptation layer */
+ u32 intr : 4 /* interrupt requested */
+ )
+} tpd_spec_t;
+
+
+/* transmit PDU rate control */
+
+typedef struct tpd_rate
+{
+ BITFIELD2(
+ u32 idle_cells : 16, /* number of idle cells to insert */
+ u32 data_cells : 16 /* number of data cells to transmit */
+ )
+} tpd_rate_t;
+
+
+/* transmit segment descriptor */
+
+typedef struct tsd {
+ u32 buffer; /* transmit buffer DMA address */
+ u32 length; /* number of bytes in buffer */
+} tsd_t;
+
+
+/* transmit PDU descriptor */
+
+typedef struct tpd {
+ struct atm_header atm_header; /* ATM header minus HEC byte */
+ struct tpd_spec spec; /* tpd specification */
+ struct tpd_rate rate; /* tpd rate control */
+ u32 pad; /* reserved */
+ struct tsd tsd[ TSD_NBR ]; /* transmit segment descriptors */
+} tpd_t;
+
+
+/* receive segment descriptor */
+
+typedef struct rsd {
+ u32 handle; /* host supplied receive buffer handle */
+ u32 length; /* number of bytes in buffer */
+} rsd_t;
+
+
+/* receive PDU descriptor */
+
+typedef struct rpd {
+ struct atm_header atm_header; /* ATM header minus HEC byte */
+ u32 nseg; /* number of receive segments */
+ struct rsd rsd[ RSD_NBR ]; /* receive segment descriptors */
+} rpd_t;
+
+
+/* buffer scheme */
+
+typedef enum buffer_scheme {
+ BUFFER_SCHEME_ONE,
+ BUFFER_SCHEME_TWO,
+ BUFFER_SCHEME_NBR /* always last */
+} buffer_scheme_t;
+
+
+/* buffer magnitude */
+
+typedef enum buffer_magn {
+ BUFFER_MAGN_SMALL,
+ BUFFER_MAGN_LARGE,
+ BUFFER_MAGN_NBR /* always last */
+} buffer_magn_t;
+
+
+/* receive buffer descriptor */
+
+typedef struct rbd {
+ u32 handle; /* host supplied handle */
+ u32 buffer_haddr; /* host DMA address of host buffer */
+} rbd_t;
+
+
+/* receive buffer descriptor block */
+
+typedef struct rbd_block {
+ struct rbd rbd[ RBD_BLK_SIZE ]; /* receive buffer descriptor */
+} rbd_block_t;
+
+
+/* tpd DMA address */
+
+typedef struct tpd_haddr {
+ BITFIELD3(
+ u32 size : 4, /* tpd size expressed in 32 byte blocks */
+ u32 pad : 1, /* reserved */
+ u32 haddr : 27 /* tpd DMA addr aligned on 32 byte boundary */
+ )
+} tpd_haddr_t;
+
+#define TPD_HADDR_SHIFT 5 /* addr aligned on 32 byte boundary */
+
+/* cp resident transmit queue entry */
+
+typedef struct cp_txq_entry {
+ struct tpd_haddr tpd_haddr; /* host DMA address of tpd */
+ u32 status_haddr; /* host DMA address of completion status */
+} cp_txq_entry_t;
+
+
+/* cp resident receive queue entry */
+
+typedef struct cp_rxq_entry {
+ u32 rpd_haddr; /* host DMA address of rpd */
+ u32 status_haddr; /* host DMA address of completion status */
+} cp_rxq_entry_t;
+
+
+/* cp resident buffer supply queue entry */
+
+typedef struct cp_bsq_entry {
+ u32 rbd_block_haddr; /* host DMA address of rbd block */
+ u32 status_haddr; /* host DMA address of completion status */
+} cp_bsq_entry_t;
+
+
+/* completion status */
+
+typedef volatile enum status {
+ STATUS_PENDING = (1<<0), /* initial status (written by host) */
+ STATUS_COMPLETE = (1<<1), /* completion status (written by cp) */
+ STATUS_FREE = (1<<2), /* initial status (written by host) */
+ STATUS_ERROR = (1<<3) /* completion status (written by cp) */
+} status_t;
+
+
+/* cp operation code */
+
+typedef enum opcode {
+ OPCODE_INITIALIZE = 1, /* initialize board */
+ OPCODE_ACTIVATE_VCIN, /* activate incoming VCI */
+ OPCODE_ACTIVATE_VCOUT, /* activate outgoing VCI */
+ OPCODE_DEACTIVATE_VCIN, /* deactivate incoming VCI */
+ OPCODE_DEACTIVATE_VCOUT, /* deactivate incoing VCI */
+ OPCODE_GET_STATS, /* get board statistics */
+ OPCODE_SET_OC3, /* set OC-3 registers */
+ OPCODE_GET_OC3, /* get OC-3 registers */
+ OPCODE_RESET_STATS, /* reset board statistics */
+ OPCODE_GET_PROM, /* get expansion PROM data (PCI specific) */
+ OPCODE_SET_VPI_BITS, /* set x bits of those decoded by the
+ firmware to be low order bits from
+ the VPI field of the ATM cell header */
+ OPCODE_REQUEST_INTR = (1<<7) /* request interrupt */
+} opcode_t;
+
+
+/* virtual path / virtual channel identifiers */
+
+typedef struct vpvc {
+ BITFIELD3(
+ u32 vci : 16, /* virtual channel identifier */
+ u32 vpi : 8, /* virtual path identifier */
+ u32 pad : 8 /* reserved */
+ )
+} vpvc_t;
+
+
+/* activate VC command opcode */
+
+typedef struct activate_opcode {
+ BITFIELD4(
+ enum opcode opcode : 8, /* cp opcode */
+ enum fore200e_aal aal : 8, /* adaptation layer */
+ enum buffer_scheme scheme : 8, /* buffer scheme */
+ u32 pad : 8 /* reserved */
+ )
+} activate_opcode_t;
+
+
+/* activate VC command block */
+
+typedef struct activate_block {
+ struct activate_opcode opcode; /* activate VC command opcode */
+ struct vpvc vpvc; /* VPI/VCI */
+ u32 mtu; /* for AAL0 only */
+
+} activate_block_t;
+
+
+/* deactivate VC command opcode */
+
+typedef struct deactivate_opcode {
+ BITFIELD2(
+ enum opcode opcode : 8, /* cp opcode */
+ u32 pad : 24 /* reserved */
+ )
+} deactivate_opcode_t;
+
+
+/* deactivate VC command block */
+
+typedef struct deactivate_block {
+ struct deactivate_opcode opcode; /* deactivate VC command opcode */
+ struct vpvc vpvc; /* VPI/VCI */
+} deactivate_block_t;
+
+
+/* OC-3 registers */
+
+typedef struct oc3_regs {
+ u32 reg[ 128 ]; /* see the PMC Sierra PC5346 S/UNI-155-Lite
+ Saturn User Network Interface documentation
+ for a description of the OC-3 chip registers */
+} oc3_regs_t;
+
+
+/* set/get OC-3 regs command opcode */
+
+typedef struct oc3_opcode {
+ BITFIELD4(
+ enum opcode opcode : 8, /* cp opcode */
+ u32 reg : 8, /* register index */
+ u32 value : 8, /* register value */
+ u32 mask : 8 /* register mask that specifies which
+ bits of the register value field
+ are significant */
+ )
+} oc3_opcode_t;
+
+
+/* set/get OC-3 regs command block */
+
+typedef struct oc3_block {
+ struct oc3_opcode opcode; /* set/get OC-3 regs command opcode */
+ u32 regs_haddr; /* host DMA address of OC-3 regs buffer */
+} oc3_block_t;
+
+
+/* physical encoding statistics */
+
+typedef struct stats_phy {
+ __be32 crc_header_errors; /* cells received with bad header CRC */
+ __be32 framing_errors; /* cells received with bad framing */
+ __be32 pad[ 2 ]; /* i960 padding */
+} stats_phy_t;
+
+
+/* OC-3 statistics */
+
+typedef struct stats_oc3 {
+ __be32 section_bip8_errors; /* section 8 bit interleaved parity */
+ __be32 path_bip8_errors; /* path 8 bit interleaved parity */
+ __be32 line_bip24_errors; /* line 24 bit interleaved parity */
+ __be32 line_febe_errors; /* line far end block errors */
+ __be32 path_febe_errors; /* path far end block errors */
+ __be32 corr_hcs_errors; /* correctable header check sequence */
+ __be32 ucorr_hcs_errors; /* uncorrectable header check sequence */
+ __be32 pad[ 1 ]; /* i960 padding */
+} stats_oc3_t;
+
+
+/* ATM statistics */
+
+typedef struct stats_atm {
+ __be32 cells_transmitted; /* cells transmitted */
+ __be32 cells_received; /* cells received */
+ __be32 vpi_bad_range; /* cell drops: VPI out of range */
+ __be32 vpi_no_conn; /* cell drops: no connection for VPI */
+ __be32 vci_bad_range; /* cell drops: VCI out of range */
+ __be32 vci_no_conn; /* cell drops: no connection for VCI */
+ __be32 pad[ 2 ]; /* i960 padding */
+} stats_atm_t;
+
+/* AAL0 statistics */
+
+typedef struct stats_aal0 {
+ __be32 cells_transmitted; /* cells transmitted */
+ __be32 cells_received; /* cells received */
+ __be32 cells_dropped; /* cells dropped */
+ __be32 pad[ 1 ]; /* i960 padding */
+} stats_aal0_t;
+
+
+/* AAL3/4 statistics */
+
+typedef struct stats_aal34 {
+ __be32 cells_transmitted; /* cells transmitted from segmented PDUs */
+ __be32 cells_received; /* cells reassembled into PDUs */
+ __be32 cells_crc_errors; /* payload CRC error count */
+ __be32 cells_protocol_errors; /* SAR or CS layer protocol errors */
+ __be32 cells_dropped; /* cells dropped: partial reassembly */
+ __be32 cspdus_transmitted; /* CS PDUs transmitted */
+ __be32 cspdus_received; /* CS PDUs received */
+ __be32 cspdus_protocol_errors; /* CS layer protocol errors */
+ __be32 cspdus_dropped; /* reassembled PDUs drop'd (in cells) */
+ __be32 pad[ 3 ]; /* i960 padding */
+} stats_aal34_t;
+
+
+/* AAL5 statistics */
+
+typedef struct stats_aal5 {
+ __be32 cells_transmitted; /* cells transmitted from segmented SDUs */
+ __be32 cells_received; /* cells reassembled into SDUs */
+ __be32 cells_dropped; /* reassembled PDUs dropped (in cells) */
+ __be32 congestion_experienced; /* CRC error and length wrong */
+ __be32 cspdus_transmitted; /* CS PDUs transmitted */
+ __be32 cspdus_received; /* CS PDUs received */
+ __be32 cspdus_crc_errors; /* CS PDUs CRC errors */
+ __be32 cspdus_protocol_errors; /* CS layer protocol errors */
+ __be32 cspdus_dropped; /* reassembled PDUs dropped */
+ __be32 pad[ 3 ]; /* i960 padding */
+} stats_aal5_t;
+
+
+/* auxiliary statistics */
+
+typedef struct stats_aux {
+ __be32 small_b1_failed; /* receive BD allocation failures */
+ __be32 large_b1_failed; /* receive BD allocation failures */
+ __be32 small_b2_failed; /* receive BD allocation failures */
+ __be32 large_b2_failed; /* receive BD allocation failures */
+ __be32 rpd_alloc_failed; /* receive PDU allocation failures */
+ __be32 receive_carrier; /* no carrier = 0, carrier = 1 */
+ __be32 pad[ 2 ]; /* i960 padding */
+} stats_aux_t;
+
+
+/* whole statistics buffer */
+
+typedef struct stats {
+ struct stats_phy phy; /* physical encoding statistics */
+ struct stats_oc3 oc3; /* OC-3 statistics */
+ struct stats_atm atm; /* ATM statistics */
+ struct stats_aal0 aal0; /* AAL0 statistics */
+ struct stats_aal34 aal34; /* AAL3/4 statistics */
+ struct stats_aal5 aal5; /* AAL5 statistics */
+ struct stats_aux aux; /* auxiliary statistics */
+} stats_t;
+
+
+/* get statistics command opcode */
+
+typedef struct stats_opcode {
+ BITFIELD2(
+ enum opcode opcode : 8, /* cp opcode */
+ u32 pad : 24 /* reserved */
+ )
+} stats_opcode_t;
+
+
+/* get statistics command block */
+
+typedef struct stats_block {
+ struct stats_opcode opcode; /* get statistics command opcode */
+ u32 stats_haddr; /* host DMA address of stats buffer */
+} stats_block_t;
+
+
+/* expansion PROM data (PCI specific) */
+
+typedef struct prom_data {
+ u32 hw_revision; /* hardware revision */
+ u32 serial_number; /* board serial number */
+ u8 mac_addr[ 8 ]; /* board MAC address */
+} prom_data_t;
+
+
+/* get expansion PROM data command opcode */
+
+typedef struct prom_opcode {
+ BITFIELD2(
+ enum opcode opcode : 8, /* cp opcode */
+ u32 pad : 24 /* reserved */
+ )
+} prom_opcode_t;
+
+
+/* get expansion PROM data command block */
+
+typedef struct prom_block {
+ struct prom_opcode opcode; /* get PROM data command opcode */
+ u32 prom_haddr; /* host DMA address of PROM buffer */
+} prom_block_t;
+
+
+/* cp command */
+
+typedef union cmd {
+ enum opcode opcode; /* operation code */
+ struct activate_block activate_block; /* activate VC */
+ struct deactivate_block deactivate_block; /* deactivate VC */
+ struct stats_block stats_block; /* get statistics */
+ struct prom_block prom_block; /* get expansion PROM data */
+ struct oc3_block oc3_block; /* get/set OC-3 registers */
+ u32 pad[ 4 ]; /* i960 padding */
+} cmd_t;
+
+
+/* cp resident command queue */
+
+typedef struct cp_cmdq_entry {
+ union cmd cmd; /* command */
+ u32 status_haddr; /* host DMA address of completion status */
+ u32 pad[ 3 ]; /* i960 padding */
+} cp_cmdq_entry_t;
+
+
+/* host resident transmit queue entry */
+
+typedef struct host_txq_entry {
+ struct cp_txq_entry __iomem *cp_entry; /* addr of cp resident tx queue entry */
+ enum status* status; /* addr of host resident status */
+ struct tpd* tpd; /* addr of transmit PDU descriptor */
+ u32 tpd_dma; /* DMA address of tpd */
+ struct sk_buff* skb; /* related skb */
+ void* data; /* copy of misaligned data */
+ unsigned long incarn; /* vc_map incarnation when submitted for tx */
+ struct fore200e_vc_map* vc_map;
+
+} host_txq_entry_t;
+
+
+/* host resident receive queue entry */
+
+typedef struct host_rxq_entry {
+ struct cp_rxq_entry __iomem *cp_entry; /* addr of cp resident rx queue entry */
+ enum status* status; /* addr of host resident status */
+ struct rpd* rpd; /* addr of receive PDU descriptor */
+ u32 rpd_dma; /* DMA address of rpd */
+} host_rxq_entry_t;
+
+
+/* host resident buffer supply queue entry */
+
+typedef struct host_bsq_entry {
+ struct cp_bsq_entry __iomem *cp_entry; /* addr of cp resident buffer supply queue entry */
+ enum status* status; /* addr of host resident status */
+ struct rbd_block* rbd_block; /* addr of receive buffer descriptor block */
+ u32 rbd_block_dma; /* DMA address od rdb */
+} host_bsq_entry_t;
+
+
+/* host resident command queue entry */
+
+typedef struct host_cmdq_entry {
+ struct cp_cmdq_entry __iomem *cp_entry; /* addr of cp resident cmd queue entry */
+ enum status *status; /* addr of host resident status */
+} host_cmdq_entry_t;
+
+
+/* chunk of memory */
+
+typedef struct chunk {
+ void* alloc_addr; /* base address of allocated chunk */
+ void* align_addr; /* base address of aligned chunk */
+ dma_addr_t dma_addr; /* DMA address of aligned chunk */
+ int direction; /* direction of DMA mapping */
+ u32 alloc_size; /* length of allocated chunk */
+ u32 align_size; /* length of aligned chunk */
+} chunk_t;
+
+#define dma_size align_size /* DMA useable size */
+
+
+/* host resident receive buffer */
+
+typedef struct buffer {
+ struct buffer* next; /* next receive buffer */
+ enum buffer_scheme scheme; /* buffer scheme */
+ enum buffer_magn magn; /* buffer magnitude */
+ struct chunk data; /* data buffer */
+#ifdef FORE200E_BSQ_DEBUG
+ unsigned long index; /* buffer # in queue */
+ int supplied; /* 'buffer supplied' flag */
+#endif
+} buffer_t;
+
+
+#if (BITS_PER_LONG == 32)
+#define FORE200E_BUF2HDL(buffer) ((u32)(buffer))
+#define FORE200E_HDL2BUF(handle) ((struct buffer*)(handle))
+#else /* deal with 64 bit pointers */
+#define FORE200E_BUF2HDL(buffer) ((u32)((u64)(buffer)))
+#define FORE200E_HDL2BUF(handle) ((struct buffer*)(((u64)(handle)) | PAGE_OFFSET))
+#endif
+
+
+/* host resident command queue */
+
+typedef struct host_cmdq {
+ struct host_cmdq_entry host_entry[ QUEUE_SIZE_CMD ]; /* host resident cmd queue entries */
+ int head; /* head of cmd queue */
+ struct chunk status; /* array of completion status */
+} host_cmdq_t;
+
+
+/* host resident transmit queue */
+
+typedef struct host_txq {
+ struct host_txq_entry host_entry[ QUEUE_SIZE_TX ]; /* host resident tx queue entries */
+ int head; /* head of tx queue */
+ int tail; /* tail of tx queue */
+ struct chunk tpd; /* array of tpds */
+ struct chunk status; /* arry of completion status */
+ int txing; /* number of pending PDUs in tx queue */
+} host_txq_t;
+
+
+/* host resident receive queue */
+
+typedef struct host_rxq {
+ struct host_rxq_entry host_entry[ QUEUE_SIZE_RX ]; /* host resident rx queue entries */
+ int head; /* head of rx queue */
+ struct chunk rpd; /* array of rpds */
+ struct chunk status; /* array of completion status */
+} host_rxq_t;
+
+
+/* host resident buffer supply queues */
+
+typedef struct host_bsq {
+ struct host_bsq_entry host_entry[ QUEUE_SIZE_BS ]; /* host resident buffer supply queue entries */
+ int head; /* head of buffer supply queue */
+ struct chunk rbd_block; /* array of rbds */
+ struct chunk status; /* array of completion status */
+ struct buffer* buffer; /* array of rx buffers */
+ struct buffer* freebuf; /* list of free rx buffers */
+ volatile int freebuf_count; /* count of free rx buffers */
+} host_bsq_t;
+
+
+/* header of the firmware image */
+
+typedef struct fw_header {
+ __le32 magic; /* magic number */
+ __le32 version; /* firmware version id */
+ __le32 load_offset; /* fw load offset in board memory */
+ __le32 start_offset; /* fw execution start address in board memory */
+} fw_header_t;
+
+#define FW_HEADER_MAGIC 0x65726f66 /* 'fore' */
+
+
+/* receive buffer supply queues scheme specification */
+
+typedef struct bs_spec {
+ u32 queue_length; /* queue capacity */
+ u32 buffer_size; /* host buffer size */
+ u32 pool_size; /* number of rbds */
+ u32 supply_blksize; /* num of rbds in I/O block (multiple
+ of 4 between 4 and 124 inclusive) */
+} bs_spec_t;
+
+
+/* initialization command block (one-time command, not in cmd queue) */
+
+typedef struct init_block {
+ enum opcode opcode; /* initialize command */
+ enum status status; /* related status word */
+ u32 receive_threshold; /* not used */
+ u32 num_connect; /* ATM connections */
+ u32 cmd_queue_len; /* length of command queue */
+ u32 tx_queue_len; /* length of transmit queue */
+ u32 rx_queue_len; /* length of receive queue */
+ u32 rsd_extension; /* number of extra 32 byte blocks */
+ u32 tsd_extension; /* number of extra 32 byte blocks */
+ u32 conless_vpvc; /* not used */
+ u32 pad[ 2 ]; /* force quad alignment */
+ struct bs_spec bs_spec[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ]; /* buffer supply queues spec */
+} init_block_t;
+
+
+typedef enum media_type {
+ MEDIA_TYPE_CAT5_UTP = 0x06, /* unshielded twisted pair */
+ MEDIA_TYPE_MM_OC3_ST = 0x16, /* multimode fiber ST */
+ MEDIA_TYPE_MM_OC3_SC = 0x26, /* multimode fiber SC */
+ MEDIA_TYPE_SM_OC3_ST = 0x36, /* single-mode fiber ST */
+ MEDIA_TYPE_SM_OC3_SC = 0x46 /* single-mode fiber SC */
+} media_type_t;
+
+#define FORE200E_MEDIA_INDEX(media_type) ((media_type)>>4)
+
+
+/* cp resident queues */
+
+typedef struct cp_queues {
+ u32 cp_cmdq; /* command queue */
+ u32 cp_txq; /* transmit queue */
+ u32 cp_rxq; /* receive queue */
+ u32 cp_bsq[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ]; /* buffer supply queues */
+ u32 imask; /* 1 enables cp to host interrupts */
+ u32 istat; /* 1 for interrupt posted */
+ u32 heap_base; /* offset form beginning of ram */
+ u32 heap_size; /* space available for queues */
+ u32 hlogger; /* non zero for host logging */
+ u32 heartbeat; /* cp heartbeat */
+ u32 fw_release; /* firmware version */
+ u32 mon960_release; /* i960 monitor version */
+ u32 tq_plen; /* transmit throughput measurements */
+ /* make sure the init block remains on a quad word boundary */
+ struct init_block init; /* one time cmd, not in cmd queue */
+ enum media_type media_type; /* media type id */
+ u32 oc3_revision; /* OC-3 revision number */
+} cp_queues_t;
+
+
+/* boot status */
+
+typedef enum boot_status {
+ BSTAT_COLD_START = (u32) 0xc01dc01d, /* cold start */
+ BSTAT_SELFTEST_OK = (u32) 0x02201958, /* self-test ok */
+ BSTAT_SELFTEST_FAIL = (u32) 0xadbadbad, /* self-test failed */
+ BSTAT_CP_RUNNING = (u32) 0xce11feed, /* cp is running */
+ BSTAT_MON_TOO_BIG = (u32) 0x10aded00 /* i960 monitor is too big */
+} boot_status_t;
+
+
+/* software UART */
+
+typedef struct soft_uart {
+ u32 send; /* write register */
+ u32 recv; /* read register */
+} soft_uart_t;
+
+#define FORE200E_CP_MONITOR_UART_FREE 0x00000000
+#define FORE200E_CP_MONITOR_UART_AVAIL 0x01000000
+
+
+/* i960 monitor */
+
+typedef struct cp_monitor {
+ struct soft_uart soft_uart; /* software UART */
+ enum boot_status bstat; /* boot status */
+ u32 app_base; /* application base offset */
+ u32 mon_version; /* i960 monitor version */
+} cp_monitor_t;
+
+
+/* device state */
+
+typedef enum fore200e_state {
+ FORE200E_STATE_BLANK, /* initial state */
+ FORE200E_STATE_REGISTER, /* device registered */
+ FORE200E_STATE_CONFIGURE, /* bus interface configured */
+ FORE200E_STATE_MAP, /* board space mapped in host memory */
+ FORE200E_STATE_RESET, /* board resetted */
+ FORE200E_STATE_START_FW, /* firmware started */
+ FORE200E_STATE_INITIALIZE, /* initialize command successful */
+ FORE200E_STATE_INIT_CMDQ, /* command queue initialized */
+ FORE200E_STATE_INIT_TXQ, /* transmit queue initialized */
+ FORE200E_STATE_INIT_RXQ, /* receive queue initialized */
+ FORE200E_STATE_INIT_BSQ, /* buffer supply queue initialized */
+ FORE200E_STATE_ALLOC_BUF, /* receive buffers allocated */
+ FORE200E_STATE_IRQ, /* host interrupt requested */
+ FORE200E_STATE_COMPLETE /* initialization completed */
+} fore200e_state;
+
+
+/* PCA-200E registers */
+
+typedef struct fore200e_pca_regs {
+ volatile u32 __iomem * hcr; /* address of host control register */
+ volatile u32 __iomem * imr; /* address of host interrupt mask register */
+ volatile u32 __iomem * psr; /* address of PCI specific register */
+} fore200e_pca_regs_t;
+
+
+/* SBA-200E registers */
+
+typedef struct fore200e_sba_regs {
+ u32 __iomem *hcr; /* address of host control register */
+ u32 __iomem *bsr; /* address of burst transfer size register */
+ u32 __iomem *isr; /* address of interrupt level selection register */
+} fore200e_sba_regs_t;
+
+
+/* model-specific registers */
+
+typedef union fore200e_regs {
+ struct fore200e_pca_regs pca; /* PCA-200E registers */
+ struct fore200e_sba_regs sba; /* SBA-200E registers */
+} fore200e_regs;
+
+
+struct fore200e;
+
+/* bus-dependent data */
+
+typedef struct fore200e_bus {
+ char* model_name; /* board model name */
+ char* proc_name; /* board name under /proc/atm */
+ int descr_alignment; /* tpd/rpd/rbd DMA alignment requirement */
+ int buffer_alignment; /* rx buffers DMA alignment requirement */
+ int status_alignment; /* status words DMA alignment requirement */
+ u32 (*read)(volatile u32 __iomem *);
+ void (*write)(u32, volatile u32 __iomem *);
+ u32 (*dma_map)(struct fore200e*, void*, int, int);
+ void (*dma_unmap)(struct fore200e*, u32, int, int);
+ void (*dma_sync_for_cpu)(struct fore200e*, u32, int, int);
+ void (*dma_sync_for_device)(struct fore200e*, u32, int, int);
+ int (*dma_chunk_alloc)(struct fore200e*, struct chunk*, int, int, int);
+ void (*dma_chunk_free)(struct fore200e*, struct chunk*);
+ int (*configure)(struct fore200e*);
+ int (*map)(struct fore200e*);
+ void (*reset)(struct fore200e*);
+ int (*prom_read)(struct fore200e*, struct prom_data*);
+ void (*unmap)(struct fore200e*);
+ void (*irq_enable)(struct fore200e*);
+ int (*irq_check)(struct fore200e*);
+ void (*irq_ack)(struct fore200e*);
+ int (*proc_read)(struct fore200e*, char*);
+} fore200e_bus_t;
+
+/* vc mapping */
+
+typedef struct fore200e_vc_map {
+ struct atm_vcc* vcc; /* vcc entry */
+ unsigned long incarn; /* vcc incarnation number */
+} fore200e_vc_map_t;
+
+#define FORE200E_VC_MAP(fore200e, vpi, vci) \
+ (& (fore200e)->vc_map[ ((vpi) << FORE200E_VCI_BITS) | (vci) ])
+
+
+/* per-device data */
+
+typedef struct fore200e {
+ struct list_head entry; /* next device */
+ const struct fore200e_bus* bus; /* bus-dependent code and data */
+ union fore200e_regs regs; /* bus-dependent registers */
+ struct atm_dev* atm_dev; /* ATM device */
+
+ enum fore200e_state state; /* device state */
+
+ char name[16]; /* device name */
+ void* bus_dev; /* bus-specific kernel data */
+ int irq; /* irq number */
+ unsigned long phys_base; /* physical base address */
+ void __iomem * virt_base; /* virtual base address */
+
+ unsigned char esi[ ESI_LEN ]; /* end system identifier */
+
+ struct cp_monitor __iomem * cp_monitor; /* i960 monitor address */
+ struct cp_queues __iomem * cp_queues; /* cp resident queues */
+ struct host_cmdq host_cmdq; /* host resident cmd queue */
+ struct host_txq host_txq; /* host resident tx queue */
+ struct host_rxq host_rxq; /* host resident rx queue */
+ /* host resident buffer supply queues */
+ struct host_bsq host_bsq[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ];
+
+ u32 available_cell_rate; /* remaining pseudo-CBR bw on link */
+
+ int loop_mode; /* S/UNI loopback mode */
+
+ struct stats* stats; /* last snapshot of the stats */
+
+ struct mutex rate_mtx; /* protects rate reservation ops */
+ spinlock_t q_lock; /* protects queue ops */
+#ifdef FORE200E_USE_TASKLET
+ struct tasklet_struct tx_tasklet; /* performs tx interrupt work */
+ struct tasklet_struct rx_tasklet; /* performs rx interrupt work */
+#endif
+ unsigned long tx_sat; /* tx queue saturation count */
+
+ unsigned long incarn_count;
+ struct fore200e_vc_map vc_map[ NBR_CONNECT ]; /* vc mapping */
+} fore200e_t;
+
+
+/* per-vcc data */
+
+typedef struct fore200e_vcc {
+ enum buffer_scheme scheme; /* rx buffer scheme */
+ struct tpd_rate rate; /* tx rate control data */
+ int rx_min_pdu; /* size of smallest PDU received */
+ int rx_max_pdu; /* size of largest PDU received */
+ int tx_min_pdu; /* size of smallest PDU transmitted */
+ int tx_max_pdu; /* size of largest PDU transmitted */
+ unsigned long tx_pdu; /* nbr of tx pdus */
+ unsigned long rx_pdu; /* nbr of rx pdus */
+} fore200e_vcc_t;
+
+
+
+/* 200E-series common memory layout */
+
+#define FORE200E_CP_MONITOR_OFFSET 0x00000400 /* i960 monitor interface */
+#define FORE200E_CP_QUEUES_OFFSET 0x00004d40 /* cp resident queues */
+
+
+/* PCA-200E memory layout */
+
+#define PCA200E_IOSPACE_LENGTH 0x00200000
+
+#define PCA200E_HCR_OFFSET 0x00100000 /* board control register */
+#define PCA200E_IMR_OFFSET 0x00100004 /* host IRQ mask register */
+#define PCA200E_PSR_OFFSET 0x00100008 /* PCI specific register */
+
+
+/* PCA-200E host control register */
+
+#define PCA200E_HCR_RESET (1<<0) /* read / write */
+#define PCA200E_HCR_HOLD_LOCK (1<<1) /* read / write */
+#define PCA200E_HCR_I960FAIL (1<<2) /* read */
+#define PCA200E_HCR_INTRB (1<<2) /* write */
+#define PCA200E_HCR_HOLD_ACK (1<<3) /* read */
+#define PCA200E_HCR_INTRA (1<<3) /* write */
+#define PCA200E_HCR_OUTFULL (1<<4) /* read */
+#define PCA200E_HCR_CLRINTR (1<<4) /* write */
+#define PCA200E_HCR_ESPHOLD (1<<5) /* read */
+#define PCA200E_HCR_INFULL (1<<6) /* read */
+#define PCA200E_HCR_TESTMODE (1<<7) /* read */
+
+
+/* PCA-200E PCI bus interface regs (offsets in PCI config space) */
+
+#define PCA200E_PCI_LATENCY 0x40 /* maximum slave latenty */
+#define PCA200E_PCI_MASTER_CTRL 0x41 /* master control */
+#define PCA200E_PCI_THRESHOLD 0x42 /* burst / continuous req threshold */
+
+/* PBI master control register */
+
+#define PCA200E_CTRL_DIS_CACHE_RD (1<<0) /* disable cache-line reads */
+#define PCA200E_CTRL_DIS_WRT_INVAL (1<<1) /* disable writes and invalidates */
+#define PCA200E_CTRL_2_CACHE_WRT_INVAL (1<<2) /* require 2 cache-lines for writes and invalidates */
+#define PCA200E_CTRL_IGN_LAT_TIMER (1<<3) /* ignore the latency timer */
+#define PCA200E_CTRL_ENA_CONT_REQ_MODE (1<<4) /* enable continuous request mode */
+#define PCA200E_CTRL_LARGE_PCI_BURSTS (1<<5) /* force large PCI bus bursts */
+#define PCA200E_CTRL_CONVERT_ENDIAN (1<<6) /* convert endianess of slave RAM accesses */
+
+
+
+#define SBA200E_PROM_NAME "FORE,sba-200e" /* device name in openprom tree */
+
+
+/* size of SBA-200E registers */
+
+#define SBA200E_HCR_LENGTH 4
+#define SBA200E_BSR_LENGTH 4
+#define SBA200E_ISR_LENGTH 4
+#define SBA200E_RAM_LENGTH 0x40000
+
+
+/* SBA-200E SBUS burst transfer size register */
+
+#define SBA200E_BSR_BURST4 0x04
+#define SBA200E_BSR_BURST8 0x08
+#define SBA200E_BSR_BURST16 0x10
+
+
+/* SBA-200E host control register */
+
+#define SBA200E_HCR_RESET (1<<0) /* read / write (sticky) */
+#define SBA200E_HCR_HOLD_LOCK (1<<1) /* read / write (sticky) */
+#define SBA200E_HCR_I960FAIL (1<<2) /* read */
+#define SBA200E_HCR_I960SETINTR (1<<2) /* write */
+#define SBA200E_HCR_OUTFULL (1<<3) /* read */
+#define SBA200E_HCR_INTR_CLR (1<<3) /* write */
+#define SBA200E_HCR_INTR_ENA (1<<4) /* read / write (sticky) */
+#define SBA200E_HCR_ESPHOLD (1<<5) /* read */
+#define SBA200E_HCR_INFULL (1<<6) /* read */
+#define SBA200E_HCR_TESTMODE (1<<7) /* read */
+#define SBA200E_HCR_INTR_REQ (1<<8) /* read */
+
+#define SBA200E_HCR_STICKY (SBA200E_HCR_RESET | SBA200E_HCR_HOLD_LOCK | SBA200E_HCR_INTR_ENA)
+
+
+#endif /* __KERNEL__ */
+#endif /* _FORE200E_H */
diff --git a/drivers/atm/he.c b/drivers/atm/he.c
new file mode 100644
index 000000000..93dca2e73
--- /dev/null
+++ b/drivers/atm/he.c
@@ -0,0 +1,2869 @@
+/*
+
+ he.c
+
+ ForeRunnerHE ATM Adapter driver for ATM on Linux
+ Copyright (C) 1999-2001 Naval Research Laboratory
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+/*
+
+ he.c
+
+ ForeRunnerHE ATM Adapter driver for ATM on Linux
+ Copyright (C) 1999-2001 Naval Research Laboratory
+
+ Permission to use, copy, modify and distribute this software and its
+ documentation is hereby granted, provided that both the copyright
+ notice and this permission notice appear in all copies of the software,
+ derivative works or modified versions, and any portions thereof, and
+ that both notices appear in supporting documentation.
+
+ NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
+ DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
+ RESULTING FROM THE USE OF THIS SOFTWARE.
+
+ This driver was written using the "Programmer's Reference Manual for
+ ForeRunnerHE(tm)", MANU0361-01 - Rev. A, 08/21/98.
+
+ AUTHORS:
+ chas williams <chas@cmf.nrl.navy.mil>
+ eric kinzie <ekinzie@cmf.nrl.navy.mil>
+
+ NOTES:
+ 4096 supported 'connections'
+ group 0 is used for all traffic
+ interrupt queue 0 is used for all interrupts
+ aal0 support (based on work from ulrich.u.muller@nokia.com)
+
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitmap.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#include <linux/atmdev.h>
+#include <linux/atm.h>
+#include <linux/sonet.h>
+
+#undef USE_SCATTERGATHER
+#undef USE_CHECKSUM_HW /* still confused about this */
+/* #undef HE_DEBUG */
+
+#include "he.h"
+#include "suni.h"
+#include <linux/atm_he.h>
+
+#define hprintk(fmt,args...) printk(KERN_ERR DEV_LABEL "%d: " fmt, he_dev->number , ##args)
+
+#ifdef HE_DEBUG
+#define HPRINTK(fmt,args...) printk(KERN_DEBUG DEV_LABEL "%d: " fmt, he_dev->number , ##args)
+#else /* !HE_DEBUG */
+#define HPRINTK(fmt,args...) do { } while (0)
+#endif /* HE_DEBUG */
+
+/* declarations */
+
+static int he_open(struct atm_vcc *vcc);
+static void he_close(struct atm_vcc *vcc);
+static int he_send(struct atm_vcc *vcc, struct sk_buff *skb);
+static int he_ioctl(struct atm_dev *dev, unsigned int cmd, void __user *arg);
+static irqreturn_t he_irq_handler(int irq, void *dev_id);
+static void he_tasklet(unsigned long data);
+static int he_proc_read(struct atm_dev *dev,loff_t *pos,char *page);
+static int he_start(struct atm_dev *dev);
+static void he_stop(struct he_dev *dev);
+static void he_phy_put(struct atm_dev *, unsigned char, unsigned long);
+static unsigned char he_phy_get(struct atm_dev *, unsigned long);
+
+static u8 read_prom_byte(struct he_dev *he_dev, int addr);
+
+/* globals */
+
+static struct he_dev *he_devs;
+static bool disable64;
+static short nvpibits = -1;
+static short nvcibits = -1;
+static short rx_skb_reserve = 16;
+static bool irq_coalesce = 1;
+static bool sdh = 0;
+
+/* Read from EEPROM = 0000 0011b */
+static unsigned int readtab[] = {
+ CS_HIGH | CLK_HIGH,
+ CS_LOW | CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW | SI_HIGH,
+ CLK_HIGH | SI_HIGH, /* 1 */
+ CLK_LOW | SI_HIGH,
+ CLK_HIGH | SI_HIGH /* 1 */
+};
+
+/* Clock to read from/write to the EEPROM */
+static unsigned int clocktab[] = {
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW
+};
+
+static struct atmdev_ops he_ops =
+{
+ .open = he_open,
+ .close = he_close,
+ .ioctl = he_ioctl,
+ .send = he_send,
+ .phy_put = he_phy_put,
+ .phy_get = he_phy_get,
+ .proc_read = he_proc_read,
+ .owner = THIS_MODULE
+};
+
+#define he_writel(dev, val, reg) do { writel(val, (dev)->membase + (reg)); wmb(); } while (0)
+#define he_readl(dev, reg) readl((dev)->membase + (reg))
+
+/* section 2.12 connection memory access */
+
+static __inline__ void
+he_writel_internal(struct he_dev *he_dev, unsigned val, unsigned addr,
+ unsigned flags)
+{
+ he_writel(he_dev, val, CON_DAT);
+ (void) he_readl(he_dev, CON_DAT); /* flush posted writes */
+ he_writel(he_dev, flags | CON_CTL_WRITE | CON_CTL_ADDR(addr), CON_CTL);
+ while (he_readl(he_dev, CON_CTL) & CON_CTL_BUSY);
+}
+
+#define he_writel_rcm(dev, val, reg) \
+ he_writel_internal(dev, val, reg, CON_CTL_RCM)
+
+#define he_writel_tcm(dev, val, reg) \
+ he_writel_internal(dev, val, reg, CON_CTL_TCM)
+
+#define he_writel_mbox(dev, val, reg) \
+ he_writel_internal(dev, val, reg, CON_CTL_MBOX)
+
+static unsigned
+he_readl_internal(struct he_dev *he_dev, unsigned addr, unsigned flags)
+{
+ he_writel(he_dev, flags | CON_CTL_READ | CON_CTL_ADDR(addr), CON_CTL);
+ while (he_readl(he_dev, CON_CTL) & CON_CTL_BUSY);
+ return he_readl(he_dev, CON_DAT);
+}
+
+#define he_readl_rcm(dev, reg) \
+ he_readl_internal(dev, reg, CON_CTL_RCM)
+
+#define he_readl_tcm(dev, reg) \
+ he_readl_internal(dev, reg, CON_CTL_TCM)
+
+#define he_readl_mbox(dev, reg) \
+ he_readl_internal(dev, reg, CON_CTL_MBOX)
+
+
+/* figure 2.2 connection id */
+
+#define he_mkcid(dev, vpi, vci) (((vpi << (dev)->vcibits) | vci) & 0x1fff)
+
+/* 2.5.1 per connection transmit state registers */
+
+#define he_writel_tsr0(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 0)
+#define he_readl_tsr0(dev, cid) \
+ he_readl_tcm(dev, CONFIG_TSRA | (cid << 3) | 0)
+
+#define he_writel_tsr1(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 1)
+
+#define he_writel_tsr2(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 2)
+
+#define he_writel_tsr3(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 3)
+
+#define he_writel_tsr4(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 4)
+
+ /* from page 2-20
+ *
+ * NOTE While the transmit connection is active, bits 23 through 0
+ * of this register must not be written by the host. Byte
+ * enables should be used during normal operation when writing
+ * the most significant byte.
+ */
+
+#define he_writel_tsr4_upper(dev, val, cid) \
+ he_writel_internal(dev, val, CONFIG_TSRA | (cid << 3) | 4, \
+ CON_CTL_TCM \
+ | CON_BYTE_DISABLE_2 \
+ | CON_BYTE_DISABLE_1 \
+ | CON_BYTE_DISABLE_0)
+
+#define he_readl_tsr4(dev, cid) \
+ he_readl_tcm(dev, CONFIG_TSRA | (cid << 3) | 4)
+
+#define he_writel_tsr5(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 5)
+
+#define he_writel_tsr6(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 6)
+
+#define he_writel_tsr7(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 7)
+
+
+#define he_writel_tsr8(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRB | (cid << 2) | 0)
+
+#define he_writel_tsr9(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRB | (cid << 2) | 1)
+
+#define he_writel_tsr10(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRB | (cid << 2) | 2)
+
+#define he_writel_tsr11(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRB | (cid << 2) | 3)
+
+
+#define he_writel_tsr12(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRC | (cid << 1) | 0)
+
+#define he_writel_tsr13(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRC | (cid << 1) | 1)
+
+
+#define he_writel_tsr14(dev, val, cid) \
+ he_writel_tcm(dev, val, CONFIG_TSRD | cid)
+
+#define he_writel_tsr14_upper(dev, val, cid) \
+ he_writel_internal(dev, val, CONFIG_TSRD | cid, \
+ CON_CTL_TCM \
+ | CON_BYTE_DISABLE_2 \
+ | CON_BYTE_DISABLE_1 \
+ | CON_BYTE_DISABLE_0)
+
+/* 2.7.1 per connection receive state registers */
+
+#define he_writel_rsr0(dev, val, cid) \
+ he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 0)
+#define he_readl_rsr0(dev, cid) \
+ he_readl_rcm(dev, 0x00000 | (cid << 3) | 0)
+
+#define he_writel_rsr1(dev, val, cid) \
+ he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 1)
+
+#define he_writel_rsr2(dev, val, cid) \
+ he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 2)
+
+#define he_writel_rsr3(dev, val, cid) \
+ he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 3)
+
+#define he_writel_rsr4(dev, val, cid) \
+ he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 4)
+
+#define he_writel_rsr5(dev, val, cid) \
+ he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 5)
+
+#define he_writel_rsr6(dev, val, cid) \
+ he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 6)
+
+#define he_writel_rsr7(dev, val, cid) \
+ he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 7)
+
+static __inline__ struct atm_vcc*
+__find_vcc(struct he_dev *he_dev, unsigned cid)
+{
+ struct hlist_head *head;
+ struct atm_vcc *vcc;
+ struct sock *s;
+ short vpi;
+ int vci;
+
+ vpi = cid >> he_dev->vcibits;
+ vci = cid & ((1 << he_dev->vcibits) - 1);
+ head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)];
+
+ sk_for_each(s, head) {
+ vcc = atm_sk(s);
+ if (vcc->dev == he_dev->atm_dev &&
+ vcc->vci == vci && vcc->vpi == vpi &&
+ vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ return vcc;
+ }
+ }
+ return NULL;
+}
+
+static int he_init_one(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_ent)
+{
+ struct atm_dev *atm_dev = NULL;
+ struct he_dev *he_dev = NULL;
+ int err = 0;
+
+ printk(KERN_INFO "ATM he driver\n");
+
+ if (pci_enable_device(pci_dev))
+ return -EIO;
+ if (dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)) != 0) {
+ printk(KERN_WARNING "he: no suitable dma available\n");
+ err = -EIO;
+ goto init_one_failure;
+ }
+
+ atm_dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &he_ops, -1, NULL);
+ if (!atm_dev) {
+ err = -ENODEV;
+ goto init_one_failure;
+ }
+ pci_set_drvdata(pci_dev, atm_dev);
+
+ he_dev = kzalloc(sizeof(struct he_dev),
+ GFP_KERNEL);
+ if (!he_dev) {
+ err = -ENOMEM;
+ goto init_one_failure;
+ }
+ he_dev->pci_dev = pci_dev;
+ he_dev->atm_dev = atm_dev;
+ he_dev->atm_dev->dev_data = he_dev;
+ atm_dev->dev_data = he_dev;
+ he_dev->number = atm_dev->number;
+ tasklet_init(&he_dev->tasklet, he_tasklet, (unsigned long) he_dev);
+ spin_lock_init(&he_dev->global_lock);
+
+ if (he_start(atm_dev)) {
+ he_stop(he_dev);
+ err = -ENODEV;
+ goto init_one_failure;
+ }
+ he_dev->next = NULL;
+ if (he_devs)
+ he_dev->next = he_devs;
+ he_devs = he_dev;
+ return 0;
+
+init_one_failure:
+ if (atm_dev)
+ atm_dev_deregister(atm_dev);
+ kfree(he_dev);
+ pci_disable_device(pci_dev);
+ return err;
+}
+
+static void he_remove_one(struct pci_dev *pci_dev)
+{
+ struct atm_dev *atm_dev;
+ struct he_dev *he_dev;
+
+ atm_dev = pci_get_drvdata(pci_dev);
+ he_dev = HE_DEV(atm_dev);
+
+ /* need to remove from he_devs */
+
+ he_stop(he_dev);
+ atm_dev_deregister(atm_dev);
+ kfree(he_dev);
+
+ pci_disable_device(pci_dev);
+}
+
+
+static unsigned
+rate_to_atmf(unsigned rate) /* cps to atm forum format */
+{
+#define NONZERO (1 << 14)
+
+ unsigned exp = 0;
+
+ if (rate == 0)
+ return 0;
+
+ rate <<= 9;
+ while (rate > 0x3ff) {
+ ++exp;
+ rate >>= 1;
+ }
+
+ return (NONZERO | (exp << 9) | (rate & 0x1ff));
+}
+
+static void he_init_rx_lbfp0(struct he_dev *he_dev)
+{
+ unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count;
+ unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf;
+ unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD;
+ unsigned row_offset = he_dev->r0_startrow * he_dev->bytes_per_row;
+
+ lbufd_index = 0;
+ lbm_offset = he_readl(he_dev, RCMLBM_BA);
+
+ he_writel(he_dev, lbufd_index, RLBF0_H);
+
+ for (i = 0, lbuf_count = 0; i < he_dev->r0_numbuffs; ++i) {
+ lbufd_index += 2;
+ lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32;
+
+ he_writel_rcm(he_dev, lbuf_addr, lbm_offset);
+ he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1);
+
+ if (++lbuf_count == lbufs_per_row) {
+ lbuf_count = 0;
+ row_offset += he_dev->bytes_per_row;
+ }
+ lbm_offset += 4;
+ }
+
+ he_writel(he_dev, lbufd_index - 2, RLBF0_T);
+ he_writel(he_dev, he_dev->r0_numbuffs, RLBF0_C);
+}
+
+static void he_init_rx_lbfp1(struct he_dev *he_dev)
+{
+ unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count;
+ unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf;
+ unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD;
+ unsigned row_offset = he_dev->r1_startrow * he_dev->bytes_per_row;
+
+ lbufd_index = 1;
+ lbm_offset = he_readl(he_dev, RCMLBM_BA) + (2 * lbufd_index);
+
+ he_writel(he_dev, lbufd_index, RLBF1_H);
+
+ for (i = 0, lbuf_count = 0; i < he_dev->r1_numbuffs; ++i) {
+ lbufd_index += 2;
+ lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32;
+
+ he_writel_rcm(he_dev, lbuf_addr, lbm_offset);
+ he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1);
+
+ if (++lbuf_count == lbufs_per_row) {
+ lbuf_count = 0;
+ row_offset += he_dev->bytes_per_row;
+ }
+ lbm_offset += 4;
+ }
+
+ he_writel(he_dev, lbufd_index - 2, RLBF1_T);
+ he_writel(he_dev, he_dev->r1_numbuffs, RLBF1_C);
+}
+
+static void he_init_tx_lbfp(struct he_dev *he_dev)
+{
+ unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count;
+ unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf;
+ unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD;
+ unsigned row_offset = he_dev->tx_startrow * he_dev->bytes_per_row;
+
+ lbufd_index = he_dev->r0_numbuffs + he_dev->r1_numbuffs;
+ lbm_offset = he_readl(he_dev, RCMLBM_BA) + (2 * lbufd_index);
+
+ he_writel(he_dev, lbufd_index, TLBF_H);
+
+ for (i = 0, lbuf_count = 0; i < he_dev->tx_numbuffs; ++i) {
+ lbufd_index += 1;
+ lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32;
+
+ he_writel_rcm(he_dev, lbuf_addr, lbm_offset);
+ he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1);
+
+ if (++lbuf_count == lbufs_per_row) {
+ lbuf_count = 0;
+ row_offset += he_dev->bytes_per_row;
+ }
+ lbm_offset += 2;
+ }
+
+ he_writel(he_dev, lbufd_index - 1, TLBF_T);
+}
+
+static int he_init_tpdrq(struct he_dev *he_dev)
+{
+ he_dev->tpdrq_base = dma_zalloc_coherent(&he_dev->pci_dev->dev,
+ CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq),
+ &he_dev->tpdrq_phys, GFP_KERNEL);
+ if (he_dev->tpdrq_base == NULL) {
+ hprintk("failed to alloc tpdrq\n");
+ return -ENOMEM;
+ }
+
+ he_dev->tpdrq_tail = he_dev->tpdrq_base;
+ he_dev->tpdrq_head = he_dev->tpdrq_base;
+
+ he_writel(he_dev, he_dev->tpdrq_phys, TPDRQ_B_H);
+ he_writel(he_dev, 0, TPDRQ_T);
+ he_writel(he_dev, CONFIG_TPDRQ_SIZE - 1, TPDRQ_S);
+
+ return 0;
+}
+
+static void he_init_cs_block(struct he_dev *he_dev)
+{
+ unsigned clock, rate, delta;
+ int reg;
+
+ /* 5.1.7 cs block initialization */
+
+ for (reg = 0; reg < 0x20; ++reg)
+ he_writel_mbox(he_dev, 0x0, CS_STTIM0 + reg);
+
+ /* rate grid timer reload values */
+
+ clock = he_is622(he_dev) ? 66667000 : 50000000;
+ rate = he_dev->atm_dev->link_rate;
+ delta = rate / 16 / 2;
+
+ for (reg = 0; reg < 0x10; ++reg) {
+ /* 2.4 internal transmit function
+ *
+ * we initialize the first row in the rate grid.
+ * values are period (in clock cycles) of timer
+ */
+ unsigned period = clock / rate;
+
+ he_writel_mbox(he_dev, period, CS_TGRLD0 + reg);
+ rate -= delta;
+ }
+
+ if (he_is622(he_dev)) {
+ /* table 5.2 (4 cells per lbuf) */
+ he_writel_mbox(he_dev, 0x000800fa, CS_ERTHR0);
+ he_writel_mbox(he_dev, 0x000c33cb, CS_ERTHR1);
+ he_writel_mbox(he_dev, 0x0010101b, CS_ERTHR2);
+ he_writel_mbox(he_dev, 0x00181dac, CS_ERTHR3);
+ he_writel_mbox(he_dev, 0x00280600, CS_ERTHR4);
+
+ /* table 5.3, 5.4, 5.5, 5.6, 5.7 */
+ he_writel_mbox(he_dev, 0x023de8b3, CS_ERCTL0);
+ he_writel_mbox(he_dev, 0x1801, CS_ERCTL1);
+ he_writel_mbox(he_dev, 0x68b3, CS_ERCTL2);
+ he_writel_mbox(he_dev, 0x1280, CS_ERSTAT0);
+ he_writel_mbox(he_dev, 0x68b3, CS_ERSTAT1);
+ he_writel_mbox(he_dev, 0x14585, CS_RTFWR);
+
+ he_writel_mbox(he_dev, 0x4680, CS_RTATR);
+
+ /* table 5.8 */
+ he_writel_mbox(he_dev, 0x00159ece, CS_TFBSET);
+ he_writel_mbox(he_dev, 0x68b3, CS_WCRMAX);
+ he_writel_mbox(he_dev, 0x5eb3, CS_WCRMIN);
+ he_writel_mbox(he_dev, 0xe8b3, CS_WCRINC);
+ he_writel_mbox(he_dev, 0xdeb3, CS_WCRDEC);
+ he_writel_mbox(he_dev, 0x68b3, CS_WCRCEIL);
+
+ /* table 5.9 */
+ he_writel_mbox(he_dev, 0x5, CS_OTPPER);
+ he_writel_mbox(he_dev, 0x14, CS_OTWPER);
+ } else {
+ /* table 5.1 (4 cells per lbuf) */
+ he_writel_mbox(he_dev, 0x000400ea, CS_ERTHR0);
+ he_writel_mbox(he_dev, 0x00063388, CS_ERTHR1);
+ he_writel_mbox(he_dev, 0x00081018, CS_ERTHR2);
+ he_writel_mbox(he_dev, 0x000c1dac, CS_ERTHR3);
+ he_writel_mbox(he_dev, 0x0014051a, CS_ERTHR4);
+
+ /* table 5.3, 5.4, 5.5, 5.6, 5.7 */
+ he_writel_mbox(he_dev, 0x0235e4b1, CS_ERCTL0);
+ he_writel_mbox(he_dev, 0x4701, CS_ERCTL1);
+ he_writel_mbox(he_dev, 0x64b1, CS_ERCTL2);
+ he_writel_mbox(he_dev, 0x1280, CS_ERSTAT0);
+ he_writel_mbox(he_dev, 0x64b1, CS_ERSTAT1);
+ he_writel_mbox(he_dev, 0xf424, CS_RTFWR);
+
+ he_writel_mbox(he_dev, 0x4680, CS_RTATR);
+
+ /* table 5.8 */
+ he_writel_mbox(he_dev, 0x000563b7, CS_TFBSET);
+ he_writel_mbox(he_dev, 0x64b1, CS_WCRMAX);
+ he_writel_mbox(he_dev, 0x5ab1, CS_WCRMIN);
+ he_writel_mbox(he_dev, 0xe4b1, CS_WCRINC);
+ he_writel_mbox(he_dev, 0xdab1, CS_WCRDEC);
+ he_writel_mbox(he_dev, 0x64b1, CS_WCRCEIL);
+
+ /* table 5.9 */
+ he_writel_mbox(he_dev, 0x6, CS_OTPPER);
+ he_writel_mbox(he_dev, 0x1e, CS_OTWPER);
+ }
+
+ he_writel_mbox(he_dev, 0x8, CS_OTTLIM);
+
+ for (reg = 0; reg < 0x8; ++reg)
+ he_writel_mbox(he_dev, 0x0, CS_HGRRT0 + reg);
+
+}
+
+static int he_init_cs_block_rcm(struct he_dev *he_dev)
+{
+ unsigned (*rategrid)[16][16];
+ unsigned rate, delta;
+ int i, j, reg;
+
+ unsigned rate_atmf, exp, man;
+ unsigned long long rate_cps;
+ int mult, buf, buf_limit = 4;
+
+ rategrid = kmalloc( sizeof(unsigned) * 16 * 16, GFP_KERNEL);
+ if (!rategrid)
+ return -ENOMEM;
+
+ /* initialize rate grid group table */
+
+ for (reg = 0x0; reg < 0xff; ++reg)
+ he_writel_rcm(he_dev, 0x0, CONFIG_RCMABR + reg);
+
+ /* initialize rate controller groups */
+
+ for (reg = 0x100; reg < 0x1ff; ++reg)
+ he_writel_rcm(he_dev, 0x0, CONFIG_RCMABR + reg);
+
+ /* initialize tNrm lookup table */
+
+ /* the manual makes reference to a routine in a sample driver
+ for proper configuration; fortunately, we only need this
+ in order to support abr connection */
+
+ /* initialize rate to group table */
+
+ rate = he_dev->atm_dev->link_rate;
+ delta = rate / 32;
+
+ /*
+ * 2.4 transmit internal functions
+ *
+ * we construct a copy of the rate grid used by the scheduler
+ * in order to construct the rate to group table below
+ */
+
+ for (j = 0; j < 16; j++) {
+ (*rategrid)[0][j] = rate;
+ rate -= delta;
+ }
+
+ for (i = 1; i < 16; i++)
+ for (j = 0; j < 16; j++)
+ if (i > 14)
+ (*rategrid)[i][j] = (*rategrid)[i - 1][j] / 4;
+ else
+ (*rategrid)[i][j] = (*rategrid)[i - 1][j] / 2;
+
+ /*
+ * 2.4 transmit internal function
+ *
+ * this table maps the upper 5 bits of exponent and mantissa
+ * of the atm forum representation of the rate into an index
+ * on rate grid
+ */
+
+ rate_atmf = 0;
+ while (rate_atmf < 0x400) {
+ man = (rate_atmf & 0x1f) << 4;
+ exp = rate_atmf >> 5;
+
+ /*
+ instead of '/ 512', use '>> 9' to prevent a call
+ to divdu3 on x86 platforms
+ */
+ rate_cps = (unsigned long long) (1 << exp) * (man + 512) >> 9;
+
+ if (rate_cps < 10)
+ rate_cps = 10; /* 2.2.1 minimum payload rate is 10 cps */
+
+ for (i = 255; i > 0; i--)
+ if ((*rategrid)[i/16][i%16] >= rate_cps)
+ break; /* pick nearest rate instead? */
+
+ /*
+ * each table entry is 16 bits: (rate grid index (8 bits)
+ * and a buffer limit (8 bits)
+ * there are two table entries in each 32-bit register
+ */
+
+#ifdef notdef
+ buf = rate_cps * he_dev->tx_numbuffs /
+ (he_dev->atm_dev->link_rate * 2);
+#else
+ /* this is pretty, but avoids _divdu3 and is mostly correct */
+ mult = he_dev->atm_dev->link_rate / ATM_OC3_PCR;
+ if (rate_cps > (272 * mult))
+ buf = 4;
+ else if (rate_cps > (204 * mult))
+ buf = 3;
+ else if (rate_cps > (136 * mult))
+ buf = 2;
+ else if (rate_cps > (68 * mult))
+ buf = 1;
+ else
+ buf = 0;
+#endif
+ if (buf > buf_limit)
+ buf = buf_limit;
+ reg = (reg << 16) | ((i << 8) | buf);
+
+#define RTGTBL_OFFSET 0x400
+
+ if (rate_atmf & 0x1)
+ he_writel_rcm(he_dev, reg,
+ CONFIG_RCMABR + RTGTBL_OFFSET + (rate_atmf >> 1));
+
+ ++rate_atmf;
+ }
+
+ kfree(rategrid);
+ return 0;
+}
+
+static int he_init_group(struct he_dev *he_dev, int group)
+{
+ struct he_buff *heb, *next;
+ dma_addr_t mapping;
+ int i;
+
+ he_writel(he_dev, 0x0, G0_RBPS_S + (group * 32));
+ he_writel(he_dev, 0x0, G0_RBPS_T + (group * 32));
+ he_writel(he_dev, 0x0, G0_RBPS_QI + (group * 32));
+ he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0),
+ G0_RBPS_BS + (group * 32));
+
+ /* bitmap table */
+ he_dev->rbpl_table = kmalloc(BITS_TO_LONGS(RBPL_TABLE_SIZE)
+ * sizeof(unsigned long), GFP_KERNEL);
+ if (!he_dev->rbpl_table) {
+ hprintk("unable to allocate rbpl bitmap table\n");
+ return -ENOMEM;
+ }
+ bitmap_zero(he_dev->rbpl_table, RBPL_TABLE_SIZE);
+
+ /* rbpl_virt 64-bit pointers */
+ he_dev->rbpl_virt = kmalloc(RBPL_TABLE_SIZE
+ * sizeof(struct he_buff *), GFP_KERNEL);
+ if (!he_dev->rbpl_virt) {
+ hprintk("unable to allocate rbpl virt table\n");
+ goto out_free_rbpl_table;
+ }
+
+ /* large buffer pool */
+ he_dev->rbpl_pool = dma_pool_create("rbpl", &he_dev->pci_dev->dev,
+ CONFIG_RBPL_BUFSIZE, 64, 0);
+ if (he_dev->rbpl_pool == NULL) {
+ hprintk("unable to create rbpl pool\n");
+ goto out_free_rbpl_virt;
+ }
+
+ he_dev->rbpl_base = dma_zalloc_coherent(&he_dev->pci_dev->dev,
+ CONFIG_RBPL_SIZE * sizeof(struct he_rbp),
+ &he_dev->rbpl_phys, GFP_KERNEL);
+ if (he_dev->rbpl_base == NULL) {
+ hprintk("failed to alloc rbpl_base\n");
+ goto out_destroy_rbpl_pool;
+ }
+
+ INIT_LIST_HEAD(&he_dev->rbpl_outstanding);
+
+ for (i = 0; i < CONFIG_RBPL_SIZE; ++i) {
+
+ heb = dma_pool_alloc(he_dev->rbpl_pool, GFP_KERNEL, &mapping);
+ if (!heb)
+ goto out_free_rbpl;
+ heb->mapping = mapping;
+ list_add(&heb->entry, &he_dev->rbpl_outstanding);
+
+ set_bit(i, he_dev->rbpl_table);
+ he_dev->rbpl_virt[i] = heb;
+ he_dev->rbpl_hint = i + 1;
+ he_dev->rbpl_base[i].idx = i << RBP_IDX_OFFSET;
+ he_dev->rbpl_base[i].phys = mapping + offsetof(struct he_buff, data);
+ }
+ he_dev->rbpl_tail = &he_dev->rbpl_base[CONFIG_RBPL_SIZE - 1];
+
+ he_writel(he_dev, he_dev->rbpl_phys, G0_RBPL_S + (group * 32));
+ he_writel(he_dev, RBPL_MASK(he_dev->rbpl_tail),
+ G0_RBPL_T + (group * 32));
+ he_writel(he_dev, (CONFIG_RBPL_BUFSIZE - sizeof(struct he_buff))/4,
+ G0_RBPL_BS + (group * 32));
+ he_writel(he_dev,
+ RBP_THRESH(CONFIG_RBPL_THRESH) |
+ RBP_QSIZE(CONFIG_RBPL_SIZE - 1) |
+ RBP_INT_ENB,
+ G0_RBPL_QI + (group * 32));
+
+ /* rx buffer ready queue */
+
+ he_dev->rbrq_base = dma_zalloc_coherent(&he_dev->pci_dev->dev,
+ CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq),
+ &he_dev->rbrq_phys, GFP_KERNEL);
+ if (he_dev->rbrq_base == NULL) {
+ hprintk("failed to allocate rbrq\n");
+ goto out_free_rbpl;
+ }
+
+ he_dev->rbrq_head = he_dev->rbrq_base;
+ he_writel(he_dev, he_dev->rbrq_phys, G0_RBRQ_ST + (group * 16));
+ he_writel(he_dev, 0, G0_RBRQ_H + (group * 16));
+ he_writel(he_dev,
+ RBRQ_THRESH(CONFIG_RBRQ_THRESH) | RBRQ_SIZE(CONFIG_RBRQ_SIZE - 1),
+ G0_RBRQ_Q + (group * 16));
+ if (irq_coalesce) {
+ hprintk("coalescing interrupts\n");
+ he_writel(he_dev, RBRQ_TIME(768) | RBRQ_COUNT(7),
+ G0_RBRQ_I + (group * 16));
+ } else
+ he_writel(he_dev, RBRQ_TIME(0) | RBRQ_COUNT(1),
+ G0_RBRQ_I + (group * 16));
+
+ /* tx buffer ready queue */
+
+ he_dev->tbrq_base = dma_zalloc_coherent(&he_dev->pci_dev->dev,
+ CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq),
+ &he_dev->tbrq_phys, GFP_KERNEL);
+ if (he_dev->tbrq_base == NULL) {
+ hprintk("failed to allocate tbrq\n");
+ goto out_free_rbpq_base;
+ }
+
+ he_dev->tbrq_head = he_dev->tbrq_base;
+
+ he_writel(he_dev, he_dev->tbrq_phys, G0_TBRQ_B_T + (group * 16));
+ he_writel(he_dev, 0, G0_TBRQ_H + (group * 16));
+ he_writel(he_dev, CONFIG_TBRQ_SIZE - 1, G0_TBRQ_S + (group * 16));
+ he_writel(he_dev, CONFIG_TBRQ_THRESH, G0_TBRQ_THRESH + (group * 16));
+
+ return 0;
+
+out_free_rbpq_base:
+ dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBRQ_SIZE *
+ sizeof(struct he_rbrq), he_dev->rbrq_base,
+ he_dev->rbrq_phys);
+out_free_rbpl:
+ list_for_each_entry_safe(heb, next, &he_dev->rbpl_outstanding, entry)
+ dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping);
+
+ dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBPL_SIZE *
+ sizeof(struct he_rbp), he_dev->rbpl_base,
+ he_dev->rbpl_phys);
+out_destroy_rbpl_pool:
+ dma_pool_destroy(he_dev->rbpl_pool);
+out_free_rbpl_virt:
+ kfree(he_dev->rbpl_virt);
+out_free_rbpl_table:
+ kfree(he_dev->rbpl_table);
+
+ return -ENOMEM;
+}
+
+static int he_init_irq(struct he_dev *he_dev)
+{
+ int i;
+
+ /* 2.9.3.5 tail offset for each interrupt queue is located after the
+ end of the interrupt queue */
+
+ he_dev->irq_base = dma_zalloc_coherent(&he_dev->pci_dev->dev,
+ (CONFIG_IRQ_SIZE + 1)
+ * sizeof(struct he_irq),
+ &he_dev->irq_phys,
+ GFP_KERNEL);
+ if (he_dev->irq_base == NULL) {
+ hprintk("failed to allocate irq\n");
+ return -ENOMEM;
+ }
+ he_dev->irq_tailoffset = (unsigned *)
+ &he_dev->irq_base[CONFIG_IRQ_SIZE];
+ *he_dev->irq_tailoffset = 0;
+ he_dev->irq_head = he_dev->irq_base;
+ he_dev->irq_tail = he_dev->irq_base;
+
+ for (i = 0; i < CONFIG_IRQ_SIZE; ++i)
+ he_dev->irq_base[i].isw = ITYPE_INVALID;
+
+ he_writel(he_dev, he_dev->irq_phys, IRQ0_BASE);
+ he_writel(he_dev,
+ IRQ_SIZE(CONFIG_IRQ_SIZE) | IRQ_THRESH(CONFIG_IRQ_THRESH),
+ IRQ0_HEAD);
+ he_writel(he_dev, IRQ_INT_A | IRQ_TYPE_LINE, IRQ0_CNTL);
+ he_writel(he_dev, 0x0, IRQ0_DATA);
+
+ he_writel(he_dev, 0x0, IRQ1_BASE);
+ he_writel(he_dev, 0x0, IRQ1_HEAD);
+ he_writel(he_dev, 0x0, IRQ1_CNTL);
+ he_writel(he_dev, 0x0, IRQ1_DATA);
+
+ he_writel(he_dev, 0x0, IRQ2_BASE);
+ he_writel(he_dev, 0x0, IRQ2_HEAD);
+ he_writel(he_dev, 0x0, IRQ2_CNTL);
+ he_writel(he_dev, 0x0, IRQ2_DATA);
+
+ he_writel(he_dev, 0x0, IRQ3_BASE);
+ he_writel(he_dev, 0x0, IRQ3_HEAD);
+ he_writel(he_dev, 0x0, IRQ3_CNTL);
+ he_writel(he_dev, 0x0, IRQ3_DATA);
+
+ /* 2.9.3.2 interrupt queue mapping registers */
+
+ he_writel(he_dev, 0x0, GRP_10_MAP);
+ he_writel(he_dev, 0x0, GRP_32_MAP);
+ he_writel(he_dev, 0x0, GRP_54_MAP);
+ he_writel(he_dev, 0x0, GRP_76_MAP);
+
+ if (request_irq(he_dev->pci_dev->irq,
+ he_irq_handler, IRQF_SHARED, DEV_LABEL, he_dev)) {
+ hprintk("irq %d already in use\n", he_dev->pci_dev->irq);
+ return -EINVAL;
+ }
+
+ he_dev->irq = he_dev->pci_dev->irq;
+
+ return 0;
+}
+
+static int he_start(struct atm_dev *dev)
+{
+ struct he_dev *he_dev;
+ struct pci_dev *pci_dev;
+ unsigned long membase;
+
+ u16 command;
+ u32 gen_cntl_0, host_cntl, lb_swap;
+ u8 cache_size, timer;
+
+ unsigned err;
+ unsigned int status, reg;
+ int i, group;
+
+ he_dev = HE_DEV(dev);
+ pci_dev = he_dev->pci_dev;
+
+ membase = pci_resource_start(pci_dev, 0);
+ HPRINTK("membase = 0x%lx irq = %d.\n", membase, pci_dev->irq);
+
+ /*
+ * pci bus controller initialization
+ */
+
+ /* 4.3 pci bus controller-specific initialization */
+ if (pci_read_config_dword(pci_dev, GEN_CNTL_0, &gen_cntl_0) != 0) {
+ hprintk("can't read GEN_CNTL_0\n");
+ return -EINVAL;
+ }
+ gen_cntl_0 |= (MRL_ENB | MRM_ENB | IGNORE_TIMEOUT);
+ if (pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0) != 0) {
+ hprintk("can't write GEN_CNTL_0.\n");
+ return -EINVAL;
+ }
+
+ if (pci_read_config_word(pci_dev, PCI_COMMAND, &command) != 0) {
+ hprintk("can't read PCI_COMMAND.\n");
+ return -EINVAL;
+ }
+
+ command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE);
+ if (pci_write_config_word(pci_dev, PCI_COMMAND, command) != 0) {
+ hprintk("can't enable memory.\n");
+ return -EINVAL;
+ }
+
+ if (pci_read_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, &cache_size)) {
+ hprintk("can't read cache line size?\n");
+ return -EINVAL;
+ }
+
+ if (cache_size < 16) {
+ cache_size = 16;
+ if (pci_write_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, cache_size))
+ hprintk("can't set cache line size to %d\n", cache_size);
+ }
+
+ if (pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &timer)) {
+ hprintk("can't read latency timer?\n");
+ return -EINVAL;
+ }
+
+ /* from table 3.9
+ *
+ * LAT_TIMER = 1 + AVG_LAT + BURST_SIZE/BUS_SIZE
+ *
+ * AVG_LAT: The average first data read/write latency [maximum 16 clock cycles]
+ * BURST_SIZE: 1536 bytes (read) for 622, 768 bytes (read) for 155 [192 clock cycles]
+ *
+ */
+#define LAT_TIMER 209
+ if (timer < LAT_TIMER) {
+ HPRINTK("latency timer was %d, setting to %d\n", timer, LAT_TIMER);
+ timer = LAT_TIMER;
+ if (pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, timer))
+ hprintk("can't set latency timer to %d\n", timer);
+ }
+
+ if (!(he_dev->membase = ioremap(membase, HE_REGMAP_SIZE))) {
+ hprintk("can't set up page mapping\n");
+ return -EINVAL;
+ }
+
+ /* 4.4 card reset */
+ he_writel(he_dev, 0x0, RESET_CNTL);
+ he_writel(he_dev, 0xff, RESET_CNTL);
+
+ msleep(16); /* 16 ms */
+ status = he_readl(he_dev, RESET_CNTL);
+ if ((status & BOARD_RST_STATUS) == 0) {
+ hprintk("reset failed\n");
+ return -EINVAL;
+ }
+
+ /* 4.5 set bus width */
+ host_cntl = he_readl(he_dev, HOST_CNTL);
+ if (host_cntl & PCI_BUS_SIZE64)
+ gen_cntl_0 |= ENBL_64;
+ else
+ gen_cntl_0 &= ~ENBL_64;
+
+ if (disable64 == 1) {
+ hprintk("disabling 64-bit pci bus transfers\n");
+ gen_cntl_0 &= ~ENBL_64;
+ }
+
+ if (gen_cntl_0 & ENBL_64)
+ hprintk("64-bit transfers enabled\n");
+
+ pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0);
+
+ /* 4.7 read prom contents */
+ for (i = 0; i < PROD_ID_LEN; ++i)
+ he_dev->prod_id[i] = read_prom_byte(he_dev, PROD_ID + i);
+
+ he_dev->media = read_prom_byte(he_dev, MEDIA);
+
+ for (i = 0; i < 6; ++i)
+ dev->esi[i] = read_prom_byte(he_dev, MAC_ADDR + i);
+
+ hprintk("%s%s, %pM\n", he_dev->prod_id,
+ he_dev->media & 0x40 ? "SM" : "MM", dev->esi);
+ he_dev->atm_dev->link_rate = he_is622(he_dev) ?
+ ATM_OC12_PCR : ATM_OC3_PCR;
+
+ /* 4.6 set host endianess */
+ lb_swap = he_readl(he_dev, LB_SWAP);
+ if (he_is622(he_dev))
+ lb_swap &= ~XFER_SIZE; /* 4 cells */
+ else
+ lb_swap |= XFER_SIZE; /* 8 cells */
+#ifdef __BIG_ENDIAN
+ lb_swap |= DESC_WR_SWAP | INTR_SWAP | BIG_ENDIAN_HOST;
+#else
+ lb_swap &= ~(DESC_WR_SWAP | INTR_SWAP | BIG_ENDIAN_HOST |
+ DATA_WR_SWAP | DATA_RD_SWAP | DESC_RD_SWAP);
+#endif /* __BIG_ENDIAN */
+ he_writel(he_dev, lb_swap, LB_SWAP);
+
+ /* 4.8 sdram controller initialization */
+ he_writel(he_dev, he_is622(he_dev) ? LB_64_ENB : 0x0, SDRAM_CTL);
+
+ /* 4.9 initialize rnum value */
+ lb_swap |= SWAP_RNUM_MAX(0xf);
+ he_writel(he_dev, lb_swap, LB_SWAP);
+
+ /* 4.10 initialize the interrupt queues */
+ if ((err = he_init_irq(he_dev)) != 0)
+ return err;
+
+ /* 4.11 enable pci bus controller state machines */
+ host_cntl |= (OUTFF_ENB | CMDFF_ENB |
+ QUICK_RD_RETRY | QUICK_WR_RETRY | PERR_INT_ENB);
+ he_writel(he_dev, host_cntl, HOST_CNTL);
+
+ gen_cntl_0 |= INT_PROC_ENBL|INIT_ENB;
+ pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0);
+
+ /*
+ * atm network controller initialization
+ */
+
+ /* 5.1.1 generic configuration state */
+
+ /*
+ * local (cell) buffer memory map
+ *
+ * HE155 HE622
+ *
+ * 0 ____________1023 bytes 0 _______________________2047 bytes
+ * | | | | |
+ * | utility | | rx0 | |
+ * 5|____________| 255|___________________| u |
+ * 6| | 256| | t |
+ * | | | | i |
+ * | rx0 | row | tx | l |
+ * | | | | i |
+ * | | 767|___________________| t |
+ * 517|____________| 768| | y |
+ * row 518| | | rx1 | |
+ * | | 1023|___________________|___|
+ * | |
+ * | tx |
+ * | |
+ * | |
+ * 1535|____________|
+ * 1536| |
+ * | rx1 |
+ * 2047|____________|
+ *
+ */
+
+ /* total 4096 connections */
+ he_dev->vcibits = CONFIG_DEFAULT_VCIBITS;
+ he_dev->vpibits = CONFIG_DEFAULT_VPIBITS;
+
+ if (nvpibits != -1 && nvcibits != -1 && nvpibits+nvcibits != HE_MAXCIDBITS) {
+ hprintk("nvpibits + nvcibits != %d\n", HE_MAXCIDBITS);
+ return -ENODEV;
+ }
+
+ if (nvpibits != -1) {
+ he_dev->vpibits = nvpibits;
+ he_dev->vcibits = HE_MAXCIDBITS - nvpibits;
+ }
+
+ if (nvcibits != -1) {
+ he_dev->vcibits = nvcibits;
+ he_dev->vpibits = HE_MAXCIDBITS - nvcibits;
+ }
+
+
+ if (he_is622(he_dev)) {
+ he_dev->cells_per_row = 40;
+ he_dev->bytes_per_row = 2048;
+ he_dev->r0_numrows = 256;
+ he_dev->tx_numrows = 512;
+ he_dev->r1_numrows = 256;
+ he_dev->r0_startrow = 0;
+ he_dev->tx_startrow = 256;
+ he_dev->r1_startrow = 768;
+ } else {
+ he_dev->cells_per_row = 20;
+ he_dev->bytes_per_row = 1024;
+ he_dev->r0_numrows = 512;
+ he_dev->tx_numrows = 1018;
+ he_dev->r1_numrows = 512;
+ he_dev->r0_startrow = 6;
+ he_dev->tx_startrow = 518;
+ he_dev->r1_startrow = 1536;
+ }
+
+ he_dev->cells_per_lbuf = 4;
+ he_dev->buffer_limit = 4;
+ he_dev->r0_numbuffs = he_dev->r0_numrows *
+ he_dev->cells_per_row / he_dev->cells_per_lbuf;
+ if (he_dev->r0_numbuffs > 2560)
+ he_dev->r0_numbuffs = 2560;
+
+ he_dev->r1_numbuffs = he_dev->r1_numrows *
+ he_dev->cells_per_row / he_dev->cells_per_lbuf;
+ if (he_dev->r1_numbuffs > 2560)
+ he_dev->r1_numbuffs = 2560;
+
+ he_dev->tx_numbuffs = he_dev->tx_numrows *
+ he_dev->cells_per_row / he_dev->cells_per_lbuf;
+ if (he_dev->tx_numbuffs > 5120)
+ he_dev->tx_numbuffs = 5120;
+
+ /* 5.1.2 configure hardware dependent registers */
+
+ he_writel(he_dev,
+ SLICE_X(0x2) | ARB_RNUM_MAX(0xf) | TH_PRTY(0x3) |
+ RH_PRTY(0x3) | TL_PRTY(0x2) | RL_PRTY(0x1) |
+ (he_is622(he_dev) ? BUS_MULTI(0x28) : BUS_MULTI(0x46)) |
+ (he_is622(he_dev) ? NET_PREF(0x50) : NET_PREF(0x8c)),
+ LBARB);
+
+ he_writel(he_dev, BANK_ON |
+ (he_is622(he_dev) ? (REF_RATE(0x384) | WIDE_DATA) : REF_RATE(0x150)),
+ SDRAMCON);
+
+ he_writel(he_dev,
+ (he_is622(he_dev) ? RM_BANK_WAIT(1) : RM_BANK_WAIT(0)) |
+ RM_RW_WAIT(1), RCMCONFIG);
+ he_writel(he_dev,
+ (he_is622(he_dev) ? TM_BANK_WAIT(2) : TM_BANK_WAIT(1)) |
+ TM_RW_WAIT(1), TCMCONFIG);
+
+ he_writel(he_dev, he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD, LB_CONFIG);
+
+ he_writel(he_dev,
+ (he_is622(he_dev) ? UT_RD_DELAY(8) : UT_RD_DELAY(0)) |
+ (he_is622(he_dev) ? RC_UT_MODE(0) : RC_UT_MODE(1)) |
+ RX_VALVP(he_dev->vpibits) |
+ RX_VALVC(he_dev->vcibits), RC_CONFIG);
+
+ he_writel(he_dev, DRF_THRESH(0x20) |
+ (he_is622(he_dev) ? TX_UT_MODE(0) : TX_UT_MODE(1)) |
+ TX_VCI_MASK(he_dev->vcibits) |
+ LBFREE_CNT(he_dev->tx_numbuffs), TX_CONFIG);
+
+ he_writel(he_dev, 0x0, TXAAL5_PROTO);
+
+ he_writel(he_dev, PHY_INT_ENB |
+ (he_is622(he_dev) ? PTMR_PRE(67 - 1) : PTMR_PRE(50 - 1)),
+ RH_CONFIG);
+
+ /* 5.1.3 initialize connection memory */
+
+ for (i = 0; i < TCM_MEM_SIZE; ++i)
+ he_writel_tcm(he_dev, 0, i);
+
+ for (i = 0; i < RCM_MEM_SIZE; ++i)
+ he_writel_rcm(he_dev, 0, i);
+
+ /*
+ * transmit connection memory map
+ *
+ * tx memory
+ * 0x0 ___________________
+ * | |
+ * | |
+ * | TSRa |
+ * | |
+ * | |
+ * 0x8000|___________________|
+ * | |
+ * | TSRb |
+ * 0xc000|___________________|
+ * | |
+ * | TSRc |
+ * 0xe000|___________________|
+ * | TSRd |
+ * 0xf000|___________________|
+ * | tmABR |
+ * 0x10000|___________________|
+ * | |
+ * | tmTPD |
+ * |___________________|
+ * | |
+ * ....
+ * 0x1ffff|___________________|
+ *
+ *
+ */
+
+ he_writel(he_dev, CONFIG_TSRB, TSRB_BA);
+ he_writel(he_dev, CONFIG_TSRC, TSRC_BA);
+ he_writel(he_dev, CONFIG_TSRD, TSRD_BA);
+ he_writel(he_dev, CONFIG_TMABR, TMABR_BA);
+ he_writel(he_dev, CONFIG_TPDBA, TPD_BA);
+
+
+ /*
+ * receive connection memory map
+ *
+ * 0x0 ___________________
+ * | |
+ * | |
+ * | RSRa |
+ * | |
+ * | |
+ * 0x8000|___________________|
+ * | |
+ * | rx0/1 |
+ * | LBM | link lists of local
+ * | tx | buffer memory
+ * | |
+ * 0xd000|___________________|
+ * | |
+ * | rmABR |
+ * 0xe000|___________________|
+ * | |
+ * | RSRb |
+ * |___________________|
+ * | |
+ * ....
+ * 0xffff|___________________|
+ */
+
+ he_writel(he_dev, 0x08000, RCMLBM_BA);
+ he_writel(he_dev, 0x0e000, RCMRSRB_BA);
+ he_writel(he_dev, 0x0d800, RCMABR_BA);
+
+ /* 5.1.4 initialize local buffer free pools linked lists */
+
+ he_init_rx_lbfp0(he_dev);
+ he_init_rx_lbfp1(he_dev);
+
+ he_writel(he_dev, 0x0, RLBC_H);
+ he_writel(he_dev, 0x0, RLBC_T);
+ he_writel(he_dev, 0x0, RLBC_H2);
+
+ he_writel(he_dev, 512, RXTHRSH); /* 10% of r0+r1 buffers */
+ he_writel(he_dev, 256, LITHRSH); /* 5% of r0+r1 buffers */
+
+ he_init_tx_lbfp(he_dev);
+
+ he_writel(he_dev, he_is622(he_dev) ? 0x104780 : 0x800, UBUFF_BA);
+
+ /* 5.1.5 initialize intermediate receive queues */
+
+ if (he_is622(he_dev)) {
+ he_writel(he_dev, 0x000f, G0_INMQ_S);
+ he_writel(he_dev, 0x200f, G0_INMQ_L);
+
+ he_writel(he_dev, 0x001f, G1_INMQ_S);
+ he_writel(he_dev, 0x201f, G1_INMQ_L);
+
+ he_writel(he_dev, 0x002f, G2_INMQ_S);
+ he_writel(he_dev, 0x202f, G2_INMQ_L);
+
+ he_writel(he_dev, 0x003f, G3_INMQ_S);
+ he_writel(he_dev, 0x203f, G3_INMQ_L);
+
+ he_writel(he_dev, 0x004f, G4_INMQ_S);
+ he_writel(he_dev, 0x204f, G4_INMQ_L);
+
+ he_writel(he_dev, 0x005f, G5_INMQ_S);
+ he_writel(he_dev, 0x205f, G5_INMQ_L);
+
+ he_writel(he_dev, 0x006f, G6_INMQ_S);
+ he_writel(he_dev, 0x206f, G6_INMQ_L);
+
+ he_writel(he_dev, 0x007f, G7_INMQ_S);
+ he_writel(he_dev, 0x207f, G7_INMQ_L);
+ } else {
+ he_writel(he_dev, 0x0000, G0_INMQ_S);
+ he_writel(he_dev, 0x0008, G0_INMQ_L);
+
+ he_writel(he_dev, 0x0001, G1_INMQ_S);
+ he_writel(he_dev, 0x0009, G1_INMQ_L);
+
+ he_writel(he_dev, 0x0002, G2_INMQ_S);
+ he_writel(he_dev, 0x000a, G2_INMQ_L);
+
+ he_writel(he_dev, 0x0003, G3_INMQ_S);
+ he_writel(he_dev, 0x000b, G3_INMQ_L);
+
+ he_writel(he_dev, 0x0004, G4_INMQ_S);
+ he_writel(he_dev, 0x000c, G4_INMQ_L);
+
+ he_writel(he_dev, 0x0005, G5_INMQ_S);
+ he_writel(he_dev, 0x000d, G5_INMQ_L);
+
+ he_writel(he_dev, 0x0006, G6_INMQ_S);
+ he_writel(he_dev, 0x000e, G6_INMQ_L);
+
+ he_writel(he_dev, 0x0007, G7_INMQ_S);
+ he_writel(he_dev, 0x000f, G7_INMQ_L);
+ }
+
+ /* 5.1.6 application tunable parameters */
+
+ he_writel(he_dev, 0x0, MCC);
+ he_writel(he_dev, 0x0, OEC);
+ he_writel(he_dev, 0x0, DCC);
+ he_writel(he_dev, 0x0, CEC);
+
+ /* 5.1.7 cs block initialization */
+
+ he_init_cs_block(he_dev);
+
+ /* 5.1.8 cs block connection memory initialization */
+
+ if (he_init_cs_block_rcm(he_dev) < 0)
+ return -ENOMEM;
+
+ /* 5.1.10 initialize host structures */
+
+ he_init_tpdrq(he_dev);
+
+ he_dev->tpd_pool = dma_pool_create("tpd", &he_dev->pci_dev->dev,
+ sizeof(struct he_tpd), TPD_ALIGNMENT, 0);
+ if (he_dev->tpd_pool == NULL) {
+ hprintk("unable to create tpd dma_pool\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&he_dev->outstanding_tpds);
+
+ if (he_init_group(he_dev, 0) != 0)
+ return -ENOMEM;
+
+ for (group = 1; group < HE_NUM_GROUPS; ++group) {
+ he_writel(he_dev, 0x0, G0_RBPS_S + (group * 32));
+ he_writel(he_dev, 0x0, G0_RBPS_T + (group * 32));
+ he_writel(he_dev, 0x0, G0_RBPS_QI + (group * 32));
+ he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0),
+ G0_RBPS_BS + (group * 32));
+
+ he_writel(he_dev, 0x0, G0_RBPL_S + (group * 32));
+ he_writel(he_dev, 0x0, G0_RBPL_T + (group * 32));
+ he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0),
+ G0_RBPL_QI + (group * 32));
+ he_writel(he_dev, 0x0, G0_RBPL_BS + (group * 32));
+
+ he_writel(he_dev, 0x0, G0_RBRQ_ST + (group * 16));
+ he_writel(he_dev, 0x0, G0_RBRQ_H + (group * 16));
+ he_writel(he_dev, RBRQ_THRESH(0x1) | RBRQ_SIZE(0x0),
+ G0_RBRQ_Q + (group * 16));
+ he_writel(he_dev, 0x0, G0_RBRQ_I + (group * 16));
+
+ he_writel(he_dev, 0x0, G0_TBRQ_B_T + (group * 16));
+ he_writel(he_dev, 0x0, G0_TBRQ_H + (group * 16));
+ he_writel(he_dev, TBRQ_THRESH(0x1),
+ G0_TBRQ_THRESH + (group * 16));
+ he_writel(he_dev, 0x0, G0_TBRQ_S + (group * 16));
+ }
+
+ /* host status page */
+
+ he_dev->hsp = dma_zalloc_coherent(&he_dev->pci_dev->dev,
+ sizeof(struct he_hsp),
+ &he_dev->hsp_phys, GFP_KERNEL);
+ if (he_dev->hsp == NULL) {
+ hprintk("failed to allocate host status page\n");
+ return -ENOMEM;
+ }
+ he_writel(he_dev, he_dev->hsp_phys, HSP_BA);
+
+ /* initialize framer */
+
+#ifdef CONFIG_ATM_HE_USE_SUNI
+ if (he_isMM(he_dev))
+ suni_init(he_dev->atm_dev);
+ if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->start)
+ he_dev->atm_dev->phy->start(he_dev->atm_dev);
+#endif /* CONFIG_ATM_HE_USE_SUNI */
+
+ if (sdh) {
+ /* this really should be in suni.c but for now... */
+ int val;
+
+ val = he_phy_get(he_dev->atm_dev, SUNI_TPOP_APM);
+ val = (val & ~SUNI_TPOP_APM_S) | (SUNI_TPOP_S_SDH << SUNI_TPOP_APM_S_SHIFT);
+ he_phy_put(he_dev->atm_dev, val, SUNI_TPOP_APM);
+ he_phy_put(he_dev->atm_dev, SUNI_TACP_IUCHP_CLP, SUNI_TACP_IUCHP);
+ }
+
+ /* 5.1.12 enable transmit and receive */
+
+ reg = he_readl_mbox(he_dev, CS_ERCTL0);
+ reg |= TX_ENABLE|ER_ENABLE;
+ he_writel_mbox(he_dev, reg, CS_ERCTL0);
+
+ reg = he_readl(he_dev, RC_CONFIG);
+ reg |= RX_ENABLE;
+ he_writel(he_dev, reg, RC_CONFIG);
+
+ for (i = 0; i < HE_NUM_CS_STPER; ++i) {
+ he_dev->cs_stper[i].inuse = 0;
+ he_dev->cs_stper[i].pcr = -1;
+ }
+ he_dev->total_bw = 0;
+
+
+ /* atm linux initialization */
+
+ he_dev->atm_dev->ci_range.vpi_bits = he_dev->vpibits;
+ he_dev->atm_dev->ci_range.vci_bits = he_dev->vcibits;
+
+ he_dev->irq_peak = 0;
+ he_dev->rbrq_peak = 0;
+ he_dev->rbpl_peak = 0;
+ he_dev->tbrq_peak = 0;
+
+ HPRINTK("hell bent for leather!\n");
+
+ return 0;
+}
+
+static void
+he_stop(struct he_dev *he_dev)
+{
+ struct he_buff *heb, *next;
+ struct pci_dev *pci_dev;
+ u32 gen_cntl_0, reg;
+ u16 command;
+
+ pci_dev = he_dev->pci_dev;
+
+ /* disable interrupts */
+
+ if (he_dev->membase) {
+ pci_read_config_dword(pci_dev, GEN_CNTL_0, &gen_cntl_0);
+ gen_cntl_0 &= ~(INT_PROC_ENBL | INIT_ENB);
+ pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0);
+
+ tasklet_disable(&he_dev->tasklet);
+
+ /* disable recv and transmit */
+
+ reg = he_readl_mbox(he_dev, CS_ERCTL0);
+ reg &= ~(TX_ENABLE|ER_ENABLE);
+ he_writel_mbox(he_dev, reg, CS_ERCTL0);
+
+ reg = he_readl(he_dev, RC_CONFIG);
+ reg &= ~(RX_ENABLE);
+ he_writel(he_dev, reg, RC_CONFIG);
+ }
+
+#ifdef CONFIG_ATM_HE_USE_SUNI
+ if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->stop)
+ he_dev->atm_dev->phy->stop(he_dev->atm_dev);
+#endif /* CONFIG_ATM_HE_USE_SUNI */
+
+ if (he_dev->irq)
+ free_irq(he_dev->irq, he_dev);
+
+ if (he_dev->irq_base)
+ dma_free_coherent(&he_dev->pci_dev->dev, (CONFIG_IRQ_SIZE + 1)
+ * sizeof(struct he_irq), he_dev->irq_base, he_dev->irq_phys);
+
+ if (he_dev->hsp)
+ dma_free_coherent(&he_dev->pci_dev->dev, sizeof(struct he_hsp),
+ he_dev->hsp, he_dev->hsp_phys);
+
+ if (he_dev->rbpl_base) {
+ list_for_each_entry_safe(heb, next, &he_dev->rbpl_outstanding, entry)
+ dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping);
+
+ dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBPL_SIZE
+ * sizeof(struct he_rbp), he_dev->rbpl_base, he_dev->rbpl_phys);
+ }
+
+ kfree(he_dev->rbpl_virt);
+ kfree(he_dev->rbpl_table);
+
+ if (he_dev->rbpl_pool)
+ dma_pool_destroy(he_dev->rbpl_pool);
+
+ if (he_dev->rbrq_base)
+ dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq),
+ he_dev->rbrq_base, he_dev->rbrq_phys);
+
+ if (he_dev->tbrq_base)
+ dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq),
+ he_dev->tbrq_base, he_dev->tbrq_phys);
+
+ if (he_dev->tpdrq_base)
+ dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq),
+ he_dev->tpdrq_base, he_dev->tpdrq_phys);
+
+ if (he_dev->tpd_pool)
+ dma_pool_destroy(he_dev->tpd_pool);
+
+ if (he_dev->pci_dev) {
+ pci_read_config_word(he_dev->pci_dev, PCI_COMMAND, &command);
+ command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ pci_write_config_word(he_dev->pci_dev, PCI_COMMAND, command);
+ }
+
+ if (he_dev->membase)
+ iounmap(he_dev->membase);
+}
+
+static struct he_tpd *
+__alloc_tpd(struct he_dev *he_dev)
+{
+ struct he_tpd *tpd;
+ dma_addr_t mapping;
+
+ tpd = dma_pool_alloc(he_dev->tpd_pool, GFP_ATOMIC, &mapping);
+ if (tpd == NULL)
+ return NULL;
+
+ tpd->status = TPD_ADDR(mapping);
+ tpd->reserved = 0;
+ tpd->iovec[0].addr = 0; tpd->iovec[0].len = 0;
+ tpd->iovec[1].addr = 0; tpd->iovec[1].len = 0;
+ tpd->iovec[2].addr = 0; tpd->iovec[2].len = 0;
+
+ return tpd;
+}
+
+#define AAL5_LEN(buf,len) \
+ ((((unsigned char *)(buf))[(len)-6] << 8) | \
+ (((unsigned char *)(buf))[(len)-5]))
+
+/* 2.10.1.2 receive
+ *
+ * aal5 packets can optionally return the tcp checksum in the lower
+ * 16 bits of the crc (RSR0_TCP_CKSUM)
+ */
+
+#define TCP_CKSUM(buf,len) \
+ ((((unsigned char *)(buf))[(len)-2] << 8) | \
+ (((unsigned char *)(buf))[(len-1)]))
+
+static int
+he_service_rbrq(struct he_dev *he_dev, int group)
+{
+ struct he_rbrq *rbrq_tail = (struct he_rbrq *)
+ ((unsigned long)he_dev->rbrq_base |
+ he_dev->hsp->group[group].rbrq_tail);
+ unsigned cid, lastcid = -1;
+ struct sk_buff *skb;
+ struct atm_vcc *vcc = NULL;
+ struct he_vcc *he_vcc;
+ struct he_buff *heb, *next;
+ int i;
+ int pdus_assembled = 0;
+ int updated = 0;
+
+ read_lock(&vcc_sklist_lock);
+ while (he_dev->rbrq_head != rbrq_tail) {
+ ++updated;
+
+ HPRINTK("%p rbrq%d 0x%x len=%d cid=0x%x %s%s%s%s%s%s\n",
+ he_dev->rbrq_head, group,
+ RBRQ_ADDR(he_dev->rbrq_head),
+ RBRQ_BUFLEN(he_dev->rbrq_head),
+ RBRQ_CID(he_dev->rbrq_head),
+ RBRQ_CRC_ERR(he_dev->rbrq_head) ? " CRC_ERR" : "",
+ RBRQ_LEN_ERR(he_dev->rbrq_head) ? " LEN_ERR" : "",
+ RBRQ_END_PDU(he_dev->rbrq_head) ? " END_PDU" : "",
+ RBRQ_AAL5_PROT(he_dev->rbrq_head) ? " AAL5_PROT" : "",
+ RBRQ_CON_CLOSED(he_dev->rbrq_head) ? " CON_CLOSED" : "",
+ RBRQ_HBUF_ERR(he_dev->rbrq_head) ? " HBUF_ERR" : "");
+
+ i = RBRQ_ADDR(he_dev->rbrq_head) >> RBP_IDX_OFFSET;
+ heb = he_dev->rbpl_virt[i];
+
+ cid = RBRQ_CID(he_dev->rbrq_head);
+ if (cid != lastcid)
+ vcc = __find_vcc(he_dev, cid);
+ lastcid = cid;
+
+ if (vcc == NULL || (he_vcc = HE_VCC(vcc)) == NULL) {
+ hprintk("vcc/he_vcc == NULL (cid 0x%x)\n", cid);
+ if (!RBRQ_HBUF_ERR(he_dev->rbrq_head)) {
+ clear_bit(i, he_dev->rbpl_table);
+ list_del(&heb->entry);
+ dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping);
+ }
+
+ goto next_rbrq_entry;
+ }
+
+ if (RBRQ_HBUF_ERR(he_dev->rbrq_head)) {
+ hprintk("HBUF_ERR! (cid 0x%x)\n", cid);
+ atomic_inc(&vcc->stats->rx_drop);
+ goto return_host_buffers;
+ }
+
+ heb->len = RBRQ_BUFLEN(he_dev->rbrq_head) * 4;
+ clear_bit(i, he_dev->rbpl_table);
+ list_move_tail(&heb->entry, &he_vcc->buffers);
+ he_vcc->pdu_len += heb->len;
+
+ if (RBRQ_CON_CLOSED(he_dev->rbrq_head)) {
+ lastcid = -1;
+ HPRINTK("wake_up rx_waitq (cid 0x%x)\n", cid);
+ wake_up(&he_vcc->rx_waitq);
+ goto return_host_buffers;
+ }
+
+ if (!RBRQ_END_PDU(he_dev->rbrq_head))
+ goto next_rbrq_entry;
+
+ if (RBRQ_LEN_ERR(he_dev->rbrq_head)
+ || RBRQ_CRC_ERR(he_dev->rbrq_head)) {
+ HPRINTK("%s%s (%d.%d)\n",
+ RBRQ_CRC_ERR(he_dev->rbrq_head)
+ ? "CRC_ERR " : "",
+ RBRQ_LEN_ERR(he_dev->rbrq_head)
+ ? "LEN_ERR" : "",
+ vcc->vpi, vcc->vci);
+ atomic_inc(&vcc->stats->rx_err);
+ goto return_host_buffers;
+ }
+
+ skb = atm_alloc_charge(vcc, he_vcc->pdu_len + rx_skb_reserve,
+ GFP_ATOMIC);
+ if (!skb) {
+ HPRINTK("charge failed (%d.%d)\n", vcc->vpi, vcc->vci);
+ goto return_host_buffers;
+ }
+
+ if (rx_skb_reserve > 0)
+ skb_reserve(skb, rx_skb_reserve);
+
+ __net_timestamp(skb);
+
+ list_for_each_entry(heb, &he_vcc->buffers, entry)
+ memcpy(skb_put(skb, heb->len), &heb->data, heb->len);
+
+ switch (vcc->qos.aal) {
+ case ATM_AAL0:
+ /* 2.10.1.5 raw cell receive */
+ skb->len = ATM_AAL0_SDU;
+ skb_set_tail_pointer(skb, skb->len);
+ break;
+ case ATM_AAL5:
+ /* 2.10.1.2 aal5 receive */
+
+ skb->len = AAL5_LEN(skb->data, he_vcc->pdu_len);
+ skb_set_tail_pointer(skb, skb->len);
+#ifdef USE_CHECKSUM_HW
+ if (vcc->vpi == 0 && vcc->vci >= ATM_NOT_RSV_VCI) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum = TCP_CKSUM(skb->data,
+ he_vcc->pdu_len);
+ }
+#endif
+ break;
+ }
+
+#ifdef should_never_happen
+ if (skb->len > vcc->qos.rxtp.max_sdu)
+ hprintk("pdu_len (%d) > vcc->qos.rxtp.max_sdu (%d)! cid 0x%x\n", skb->len, vcc->qos.rxtp.max_sdu, cid);
+#endif
+
+#ifdef notdef
+ ATM_SKB(skb)->vcc = vcc;
+#endif
+ spin_unlock(&he_dev->global_lock);
+ vcc->push(vcc, skb);
+ spin_lock(&he_dev->global_lock);
+
+ atomic_inc(&vcc->stats->rx);
+
+return_host_buffers:
+ ++pdus_assembled;
+
+ list_for_each_entry_safe(heb, next, &he_vcc->buffers, entry)
+ dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping);
+ INIT_LIST_HEAD(&he_vcc->buffers);
+ he_vcc->pdu_len = 0;
+
+next_rbrq_entry:
+ he_dev->rbrq_head = (struct he_rbrq *)
+ ((unsigned long) he_dev->rbrq_base |
+ RBRQ_MASK(he_dev->rbrq_head + 1));
+
+ }
+ read_unlock(&vcc_sklist_lock);
+
+ if (updated) {
+ if (updated > he_dev->rbrq_peak)
+ he_dev->rbrq_peak = updated;
+
+ he_writel(he_dev, RBRQ_MASK(he_dev->rbrq_head),
+ G0_RBRQ_H + (group * 16));
+ }
+
+ return pdus_assembled;
+}
+
+static void
+he_service_tbrq(struct he_dev *he_dev, int group)
+{
+ struct he_tbrq *tbrq_tail = (struct he_tbrq *)
+ ((unsigned long)he_dev->tbrq_base |
+ he_dev->hsp->group[group].tbrq_tail);
+ struct he_tpd *tpd;
+ int slot, updated = 0;
+ struct he_tpd *__tpd;
+
+ /* 2.1.6 transmit buffer return queue */
+
+ while (he_dev->tbrq_head != tbrq_tail) {
+ ++updated;
+
+ HPRINTK("tbrq%d 0x%x%s%s\n",
+ group,
+ TBRQ_TPD(he_dev->tbrq_head),
+ TBRQ_EOS(he_dev->tbrq_head) ? " EOS" : "",
+ TBRQ_MULTIPLE(he_dev->tbrq_head) ? " MULTIPLE" : "");
+ tpd = NULL;
+ list_for_each_entry(__tpd, &he_dev->outstanding_tpds, entry) {
+ if (TPD_ADDR(__tpd->status) == TBRQ_TPD(he_dev->tbrq_head)) {
+ tpd = __tpd;
+ list_del(&__tpd->entry);
+ break;
+ }
+ }
+
+ if (tpd == NULL) {
+ hprintk("unable to locate tpd for dma buffer %x\n",
+ TBRQ_TPD(he_dev->tbrq_head));
+ goto next_tbrq_entry;
+ }
+
+ if (TBRQ_EOS(he_dev->tbrq_head)) {
+ HPRINTK("wake_up(tx_waitq) cid 0x%x\n",
+ he_mkcid(he_dev, tpd->vcc->vpi, tpd->vcc->vci));
+ if (tpd->vcc)
+ wake_up(&HE_VCC(tpd->vcc)->tx_waitq);
+
+ goto next_tbrq_entry;
+ }
+
+ for (slot = 0; slot < TPD_MAXIOV; ++slot) {
+ if (tpd->iovec[slot].addr)
+ dma_unmap_single(&he_dev->pci_dev->dev,
+ tpd->iovec[slot].addr,
+ tpd->iovec[slot].len & TPD_LEN_MASK,
+ DMA_TO_DEVICE);
+ if (tpd->iovec[slot].len & TPD_LST)
+ break;
+
+ }
+
+ if (tpd->skb) { /* && !TBRQ_MULTIPLE(he_dev->tbrq_head) */
+ if (tpd->vcc && tpd->vcc->pop)
+ tpd->vcc->pop(tpd->vcc, tpd->skb);
+ else
+ dev_kfree_skb_any(tpd->skb);
+ }
+
+next_tbrq_entry:
+ if (tpd)
+ dma_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status));
+ he_dev->tbrq_head = (struct he_tbrq *)
+ ((unsigned long) he_dev->tbrq_base |
+ TBRQ_MASK(he_dev->tbrq_head + 1));
+ }
+
+ if (updated) {
+ if (updated > he_dev->tbrq_peak)
+ he_dev->tbrq_peak = updated;
+
+ he_writel(he_dev, TBRQ_MASK(he_dev->tbrq_head),
+ G0_TBRQ_H + (group * 16));
+ }
+}
+
+static void
+he_service_rbpl(struct he_dev *he_dev, int group)
+{
+ struct he_rbp *new_tail;
+ struct he_rbp *rbpl_head;
+ struct he_buff *heb;
+ dma_addr_t mapping;
+ int i;
+ int moved = 0;
+
+ rbpl_head = (struct he_rbp *) ((unsigned long)he_dev->rbpl_base |
+ RBPL_MASK(he_readl(he_dev, G0_RBPL_S)));
+
+ for (;;) {
+ new_tail = (struct he_rbp *) ((unsigned long)he_dev->rbpl_base |
+ RBPL_MASK(he_dev->rbpl_tail+1));
+
+ /* table 3.42 -- rbpl_tail should never be set to rbpl_head */
+ if (new_tail == rbpl_head)
+ break;
+
+ i = find_next_zero_bit(he_dev->rbpl_table, RBPL_TABLE_SIZE, he_dev->rbpl_hint);
+ if (i > (RBPL_TABLE_SIZE - 1)) {
+ i = find_first_zero_bit(he_dev->rbpl_table, RBPL_TABLE_SIZE);
+ if (i > (RBPL_TABLE_SIZE - 1))
+ break;
+ }
+ he_dev->rbpl_hint = i + 1;
+
+ heb = dma_pool_alloc(he_dev->rbpl_pool, GFP_ATOMIC, &mapping);
+ if (!heb)
+ break;
+ heb->mapping = mapping;
+ list_add(&heb->entry, &he_dev->rbpl_outstanding);
+ he_dev->rbpl_virt[i] = heb;
+ set_bit(i, he_dev->rbpl_table);
+ new_tail->idx = i << RBP_IDX_OFFSET;
+ new_tail->phys = mapping + offsetof(struct he_buff, data);
+
+ he_dev->rbpl_tail = new_tail;
+ ++moved;
+ }
+
+ if (moved)
+ he_writel(he_dev, RBPL_MASK(he_dev->rbpl_tail), G0_RBPL_T);
+}
+
+static void
+he_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ struct he_dev *he_dev = (struct he_dev *) data;
+ int group, type;
+ int updated = 0;
+
+ HPRINTK("tasklet (0x%lx)\n", data);
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+
+ while (he_dev->irq_head != he_dev->irq_tail) {
+ ++updated;
+
+ type = ITYPE_TYPE(he_dev->irq_head->isw);
+ group = ITYPE_GROUP(he_dev->irq_head->isw);
+
+ switch (type) {
+ case ITYPE_RBRQ_THRESH:
+ HPRINTK("rbrq%d threshold\n", group);
+ /* fall through */
+ case ITYPE_RBRQ_TIMER:
+ if (he_service_rbrq(he_dev, group))
+ he_service_rbpl(he_dev, group);
+ break;
+ case ITYPE_TBRQ_THRESH:
+ HPRINTK("tbrq%d threshold\n", group);
+ /* fall through */
+ case ITYPE_TPD_COMPLETE:
+ he_service_tbrq(he_dev, group);
+ break;
+ case ITYPE_RBPL_THRESH:
+ he_service_rbpl(he_dev, group);
+ break;
+ case ITYPE_RBPS_THRESH:
+ /* shouldn't happen unless small buffers enabled */
+ break;
+ case ITYPE_PHY:
+ HPRINTK("phy interrupt\n");
+#ifdef CONFIG_ATM_HE_USE_SUNI
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+ if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->interrupt)
+ he_dev->atm_dev->phy->interrupt(he_dev->atm_dev);
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+#endif
+ break;
+ case ITYPE_OTHER:
+ switch (type|group) {
+ case ITYPE_PARITY:
+ hprintk("parity error\n");
+ break;
+ case ITYPE_ABORT:
+ hprintk("abort 0x%x\n", he_readl(he_dev, ABORT_ADDR));
+ break;
+ }
+ break;
+ case ITYPE_TYPE(ITYPE_INVALID):
+ /* see 8.1.1 -- check all queues */
+
+ HPRINTK("isw not updated 0x%x\n", he_dev->irq_head->isw);
+
+ he_service_rbrq(he_dev, 0);
+ he_service_rbpl(he_dev, 0);
+ he_service_tbrq(he_dev, 0);
+ break;
+ default:
+ hprintk("bad isw 0x%x?\n", he_dev->irq_head->isw);
+ }
+
+ he_dev->irq_head->isw = ITYPE_INVALID;
+
+ he_dev->irq_head = (struct he_irq *) NEXT_ENTRY(he_dev->irq_base, he_dev->irq_head, IRQ_MASK);
+ }
+
+ if (updated) {
+ if (updated > he_dev->irq_peak)
+ he_dev->irq_peak = updated;
+
+ he_writel(he_dev,
+ IRQ_SIZE(CONFIG_IRQ_SIZE) |
+ IRQ_THRESH(CONFIG_IRQ_THRESH) |
+ IRQ_TAIL(he_dev->irq_tail), IRQ0_HEAD);
+ (void) he_readl(he_dev, INT_FIFO); /* 8.1.2 controller errata; flush posted writes */
+ }
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+}
+
+static irqreturn_t
+he_irq_handler(int irq, void *dev_id)
+{
+ unsigned long flags;
+ struct he_dev *he_dev = (struct he_dev * )dev_id;
+ int handled = 0;
+
+ if (he_dev == NULL)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+
+ he_dev->irq_tail = (struct he_irq *) (((unsigned long)he_dev->irq_base) |
+ (*he_dev->irq_tailoffset << 2));
+
+ if (he_dev->irq_tail == he_dev->irq_head) {
+ HPRINTK("tailoffset not updated?\n");
+ he_dev->irq_tail = (struct he_irq *) ((unsigned long)he_dev->irq_base |
+ ((he_readl(he_dev, IRQ0_BASE) & IRQ_MASK) << 2));
+ (void) he_readl(he_dev, INT_FIFO); /* 8.1.2 controller errata */
+ }
+
+#ifdef DEBUG
+ if (he_dev->irq_head == he_dev->irq_tail /* && !IRQ_PENDING */)
+ hprintk("spurious (or shared) interrupt?\n");
+#endif
+
+ if (he_dev->irq_head != he_dev->irq_tail) {
+ handled = 1;
+ tasklet_schedule(&he_dev->tasklet);
+ he_writel(he_dev, INT_CLEAR_A, INT_FIFO); /* clear interrupt */
+ (void) he_readl(he_dev, INT_FIFO); /* flush posted writes */
+ }
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+ return IRQ_RETVAL(handled);
+
+}
+
+static __inline__ void
+__enqueue_tpd(struct he_dev *he_dev, struct he_tpd *tpd, unsigned cid)
+{
+ struct he_tpdrq *new_tail;
+
+ HPRINTK("tpdrq %p cid 0x%x -> tpdrq_tail %p\n",
+ tpd, cid, he_dev->tpdrq_tail);
+
+ /* new_tail = he_dev->tpdrq_tail; */
+ new_tail = (struct he_tpdrq *) ((unsigned long) he_dev->tpdrq_base |
+ TPDRQ_MASK(he_dev->tpdrq_tail+1));
+
+ /*
+ * check to see if we are about to set the tail == head
+ * if true, update the head pointer from the adapter
+ * to see if this is really the case (reading the queue
+ * head for every enqueue would be unnecessarily slow)
+ */
+
+ if (new_tail == he_dev->tpdrq_head) {
+ he_dev->tpdrq_head = (struct he_tpdrq *)
+ (((unsigned long)he_dev->tpdrq_base) |
+ TPDRQ_MASK(he_readl(he_dev, TPDRQ_B_H)));
+
+ if (new_tail == he_dev->tpdrq_head) {
+ int slot;
+
+ hprintk("tpdrq full (cid 0x%x)\n", cid);
+ /*
+ * FIXME
+ * push tpd onto a transmit backlog queue
+ * after service_tbrq, service the backlog
+ * for now, we just drop the pdu
+ */
+ for (slot = 0; slot < TPD_MAXIOV; ++slot) {
+ if (tpd->iovec[slot].addr)
+ dma_unmap_single(&he_dev->pci_dev->dev,
+ tpd->iovec[slot].addr,
+ tpd->iovec[slot].len & TPD_LEN_MASK,
+ DMA_TO_DEVICE);
+ }
+ if (tpd->skb) {
+ if (tpd->vcc->pop)
+ tpd->vcc->pop(tpd->vcc, tpd->skb);
+ else
+ dev_kfree_skb_any(tpd->skb);
+ atomic_inc(&tpd->vcc->stats->tx_err);
+ }
+ dma_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status));
+ return;
+ }
+ }
+
+ /* 2.1.5 transmit packet descriptor ready queue */
+ list_add_tail(&tpd->entry, &he_dev->outstanding_tpds);
+ he_dev->tpdrq_tail->tpd = TPD_ADDR(tpd->status);
+ he_dev->tpdrq_tail->cid = cid;
+ wmb();
+
+ he_dev->tpdrq_tail = new_tail;
+
+ he_writel(he_dev, TPDRQ_MASK(he_dev->tpdrq_tail), TPDRQ_T);
+ (void) he_readl(he_dev, TPDRQ_T); /* flush posted writes */
+}
+
+static int
+he_open(struct atm_vcc *vcc)
+{
+ unsigned long flags;
+ struct he_dev *he_dev = HE_DEV(vcc->dev);
+ struct he_vcc *he_vcc;
+ int err = 0;
+ unsigned cid, rsr0, rsr1, rsr4, tsr0, tsr0_aal, tsr4, period, reg, clock;
+ short vpi = vcc->vpi;
+ int vci = vcc->vci;
+
+ if (vci == ATM_VCI_UNSPEC || vpi == ATM_VPI_UNSPEC)
+ return 0;
+
+ HPRINTK("open vcc %p %d.%d\n", vcc, vpi, vci);
+
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+
+ cid = he_mkcid(he_dev, vpi, vci);
+
+ he_vcc = kmalloc(sizeof(struct he_vcc), GFP_ATOMIC);
+ if (he_vcc == NULL) {
+ hprintk("unable to allocate he_vcc during open\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&he_vcc->buffers);
+ he_vcc->pdu_len = 0;
+ he_vcc->rc_index = -1;
+
+ init_waitqueue_head(&he_vcc->rx_waitq);
+ init_waitqueue_head(&he_vcc->tx_waitq);
+
+ vcc->dev_data = he_vcc;
+
+ if (vcc->qos.txtp.traffic_class != ATM_NONE) {
+ int pcr_goal;
+
+ pcr_goal = atm_pcr_goal(&vcc->qos.txtp);
+ if (pcr_goal == 0)
+ pcr_goal = he_dev->atm_dev->link_rate;
+ if (pcr_goal < 0) /* means round down, technically */
+ pcr_goal = -pcr_goal;
+
+ HPRINTK("open tx cid 0x%x pcr_goal %d\n", cid, pcr_goal);
+
+ switch (vcc->qos.aal) {
+ case ATM_AAL5:
+ tsr0_aal = TSR0_AAL5;
+ tsr4 = TSR4_AAL5;
+ break;
+ case ATM_AAL0:
+ tsr0_aal = TSR0_AAL0_SDU;
+ tsr4 = TSR4_AAL0_SDU;
+ break;
+ default:
+ err = -EINVAL;
+ goto open_failed;
+ }
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+ tsr0 = he_readl_tsr0(he_dev, cid);
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ if (TSR0_CONN_STATE(tsr0) != 0) {
+ hprintk("cid 0x%x not idle (tsr0 = 0x%x)\n", cid, tsr0);
+ err = -EBUSY;
+ goto open_failed;
+ }
+
+ switch (vcc->qos.txtp.traffic_class) {
+ case ATM_UBR:
+ /* 2.3.3.1 open connection ubr */
+
+ tsr0 = TSR0_UBR | TSR0_GROUP(0) | tsr0_aal |
+ TSR0_USE_WMIN | TSR0_UPDATE_GER;
+ break;
+
+ case ATM_CBR:
+ /* 2.3.3.2 open connection cbr */
+
+ /* 8.2.3 cbr scheduler wrap problem -- limit to 90% total link rate */
+ if ((he_dev->total_bw + pcr_goal)
+ > (he_dev->atm_dev->link_rate * 9 / 10))
+ {
+ err = -EBUSY;
+ goto open_failed;
+ }
+
+ spin_lock_irqsave(&he_dev->global_lock, flags); /* also protects he_dev->cs_stper[] */
+
+ /* find an unused cs_stper register */
+ for (reg = 0; reg < HE_NUM_CS_STPER; ++reg)
+ if (he_dev->cs_stper[reg].inuse == 0 ||
+ he_dev->cs_stper[reg].pcr == pcr_goal)
+ break;
+
+ if (reg == HE_NUM_CS_STPER) {
+ err = -EBUSY;
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+ goto open_failed;
+ }
+
+ he_dev->total_bw += pcr_goal;
+
+ he_vcc->rc_index = reg;
+ ++he_dev->cs_stper[reg].inuse;
+ he_dev->cs_stper[reg].pcr = pcr_goal;
+
+ clock = he_is622(he_dev) ? 66667000 : 50000000;
+ period = clock / pcr_goal;
+
+ HPRINTK("rc_index = %d period = %d\n",
+ reg, period);
+
+ he_writel_mbox(he_dev, rate_to_atmf(period/2),
+ CS_STPER0 + reg);
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ tsr0 = TSR0_CBR | TSR0_GROUP(0) | tsr0_aal |
+ TSR0_RC_INDEX(reg);
+
+ break;
+ default:
+ err = -EINVAL;
+ goto open_failed;
+ }
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+
+ he_writel_tsr0(he_dev, tsr0, cid);
+ he_writel_tsr4(he_dev, tsr4 | 1, cid);
+ he_writel_tsr1(he_dev, TSR1_MCR(rate_to_atmf(0)) |
+ TSR1_PCR(rate_to_atmf(pcr_goal)), cid);
+ he_writel_tsr2(he_dev, TSR2_ACR(rate_to_atmf(pcr_goal)), cid);
+ he_writel_tsr9(he_dev, TSR9_OPEN_CONN, cid);
+
+ he_writel_tsr3(he_dev, 0x0, cid);
+ he_writel_tsr5(he_dev, 0x0, cid);
+ he_writel_tsr6(he_dev, 0x0, cid);
+ he_writel_tsr7(he_dev, 0x0, cid);
+ he_writel_tsr8(he_dev, 0x0, cid);
+ he_writel_tsr10(he_dev, 0x0, cid);
+ he_writel_tsr11(he_dev, 0x0, cid);
+ he_writel_tsr12(he_dev, 0x0, cid);
+ he_writel_tsr13(he_dev, 0x0, cid);
+ he_writel_tsr14(he_dev, 0x0, cid);
+ (void) he_readl_tsr0(he_dev, cid); /* flush posted writes */
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+ }
+
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ unsigned aal;
+
+ HPRINTK("open rx cid 0x%x (rx_waitq %p)\n", cid,
+ &HE_VCC(vcc)->rx_waitq);
+
+ switch (vcc->qos.aal) {
+ case ATM_AAL5:
+ aal = RSR0_AAL5;
+ break;
+ case ATM_AAL0:
+ aal = RSR0_RAWCELL;
+ break;
+ default:
+ err = -EINVAL;
+ goto open_failed;
+ }
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+
+ rsr0 = he_readl_rsr0(he_dev, cid);
+ if (rsr0 & RSR0_OPEN_CONN) {
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ hprintk("cid 0x%x not idle (rsr0 = 0x%x)\n", cid, rsr0);
+ err = -EBUSY;
+ goto open_failed;
+ }
+
+ rsr1 = RSR1_GROUP(0) | RSR1_RBPL_ONLY;
+ rsr4 = RSR4_GROUP(0) | RSR4_RBPL_ONLY;
+ rsr0 = vcc->qos.rxtp.traffic_class == ATM_UBR ?
+ (RSR0_EPD_ENABLE|RSR0_PPD_ENABLE) : 0;
+
+#ifdef USE_CHECKSUM_HW
+ if (vpi == 0 && vci >= ATM_NOT_RSV_VCI)
+ rsr0 |= RSR0_TCP_CKSUM;
+#endif
+
+ he_writel_rsr4(he_dev, rsr4, cid);
+ he_writel_rsr1(he_dev, rsr1, cid);
+ /* 5.1.11 last parameter initialized should be
+ the open/closed indication in rsr0 */
+ he_writel_rsr0(he_dev,
+ rsr0 | RSR0_START_PDU | RSR0_OPEN_CONN | aal, cid);
+ (void) he_readl_rsr0(he_dev, cid); /* flush posted writes */
+
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+ }
+
+open_failed:
+
+ if (err) {
+ kfree(he_vcc);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ }
+ else
+ set_bit(ATM_VF_READY, &vcc->flags);
+
+ return err;
+}
+
+static void
+he_close(struct atm_vcc *vcc)
+{
+ unsigned long flags;
+ DECLARE_WAITQUEUE(wait, current);
+ struct he_dev *he_dev = HE_DEV(vcc->dev);
+ struct he_tpd *tpd;
+ unsigned cid;
+ struct he_vcc *he_vcc = HE_VCC(vcc);
+#define MAX_RETRY 30
+ int retry = 0, sleep = 1, tx_inuse;
+
+ HPRINTK("close vcc %p %d.%d\n", vcc, vcc->vpi, vcc->vci);
+
+ clear_bit(ATM_VF_READY, &vcc->flags);
+ cid = he_mkcid(he_dev, vcc->vpi, vcc->vci);
+
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ int timeout;
+
+ HPRINTK("close rx cid 0x%x\n", cid);
+
+ /* 2.7.2.2 close receive operation */
+
+ /* wait for previous close (if any) to finish */
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+ while (he_readl(he_dev, RCC_STAT) & RCC_BUSY) {
+ HPRINTK("close cid 0x%x RCC_BUSY\n", cid);
+ udelay(250);
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&he_vcc->rx_waitq, &wait);
+
+ he_writel_rsr0(he_dev, RSR0_CLOSE_CONN, cid);
+ (void) he_readl_rsr0(he_dev, cid); /* flush posted writes */
+ he_writel_mbox(he_dev, cid, RXCON_CLOSE);
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ timeout = schedule_timeout(30*HZ);
+
+ remove_wait_queue(&he_vcc->rx_waitq, &wait);
+ set_current_state(TASK_RUNNING);
+
+ if (timeout == 0)
+ hprintk("close rx timeout cid 0x%x\n", cid);
+
+ HPRINTK("close rx cid 0x%x complete\n", cid);
+
+ }
+
+ if (vcc->qos.txtp.traffic_class != ATM_NONE) {
+ volatile unsigned tsr4, tsr0;
+ int timeout;
+
+ HPRINTK("close tx cid 0x%x\n", cid);
+
+ /* 2.1.2
+ *
+ * ... the host must first stop queueing packets to the TPDRQ
+ * on the connection to be closed, then wait for all outstanding
+ * packets to be transmitted and their buffers returned to the
+ * TBRQ. When the last packet on the connection arrives in the
+ * TBRQ, the host issues the close command to the adapter.
+ */
+
+ while (((tx_inuse = atomic_read(&sk_atm(vcc)->sk_wmem_alloc)) > 1) &&
+ (retry < MAX_RETRY)) {
+ msleep(sleep);
+ if (sleep < 250)
+ sleep = sleep * 2;
+
+ ++retry;
+ }
+
+ if (tx_inuse > 1)
+ hprintk("close tx cid 0x%x tx_inuse = %d\n", cid, tx_inuse);
+
+ /* 2.3.1.1 generic close operations with flush */
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+ he_writel_tsr4_upper(he_dev, TSR4_FLUSH_CONN, cid);
+ /* also clears TSR4_SESSION_ENDED */
+
+ switch (vcc->qos.txtp.traffic_class) {
+ case ATM_UBR:
+ he_writel_tsr1(he_dev,
+ TSR1_MCR(rate_to_atmf(200000))
+ | TSR1_PCR(0), cid);
+ break;
+ case ATM_CBR:
+ he_writel_tsr14_upper(he_dev, TSR14_DELETE, cid);
+ break;
+ }
+ (void) he_readl_tsr4(he_dev, cid); /* flush posted writes */
+
+ tpd = __alloc_tpd(he_dev);
+ if (tpd == NULL) {
+ hprintk("close tx he_alloc_tpd failed cid 0x%x\n", cid);
+ goto close_tx_incomplete;
+ }
+ tpd->status |= TPD_EOS | TPD_INT;
+ tpd->skb = NULL;
+ tpd->vcc = vcc;
+ wmb();
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&he_vcc->tx_waitq, &wait);
+ __enqueue_tpd(he_dev, tpd, cid);
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ timeout = schedule_timeout(30*HZ);
+
+ remove_wait_queue(&he_vcc->tx_waitq, &wait);
+ set_current_state(TASK_RUNNING);
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+
+ if (timeout == 0) {
+ hprintk("close tx timeout cid 0x%x\n", cid);
+ goto close_tx_incomplete;
+ }
+
+ while (!((tsr4 = he_readl_tsr4(he_dev, cid)) & TSR4_SESSION_ENDED)) {
+ HPRINTK("close tx cid 0x%x !TSR4_SESSION_ENDED (tsr4 = 0x%x)\n", cid, tsr4);
+ udelay(250);
+ }
+
+ while (TSR0_CONN_STATE(tsr0 = he_readl_tsr0(he_dev, cid)) != 0) {
+ HPRINTK("close tx cid 0x%x TSR0_CONN_STATE != 0 (tsr0 = 0x%x)\n", cid, tsr0);
+ udelay(250);
+ }
+
+close_tx_incomplete:
+
+ if (vcc->qos.txtp.traffic_class == ATM_CBR) {
+ int reg = he_vcc->rc_index;
+
+ HPRINTK("cs_stper reg = %d\n", reg);
+
+ if (he_dev->cs_stper[reg].inuse == 0)
+ hprintk("cs_stper[%d].inuse = 0!\n", reg);
+ else
+ --he_dev->cs_stper[reg].inuse;
+
+ he_dev->total_bw -= he_dev->cs_stper[reg].pcr;
+ }
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ HPRINTK("close tx cid 0x%x complete\n", cid);
+ }
+
+ kfree(he_vcc);
+
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+}
+
+static int
+he_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ unsigned long flags;
+ struct he_dev *he_dev = HE_DEV(vcc->dev);
+ unsigned cid = he_mkcid(he_dev, vcc->vpi, vcc->vci);
+ struct he_tpd *tpd;
+#ifdef USE_SCATTERGATHER
+ int i, slot = 0;
+#endif
+
+#define HE_TPD_BUFSIZE 0xffff
+
+ HPRINTK("send %d.%d\n", vcc->vpi, vcc->vci);
+
+ if ((skb->len > HE_TPD_BUFSIZE) ||
+ ((vcc->qos.aal == ATM_AAL0) && (skb->len != ATM_AAL0_SDU))) {
+ hprintk("buffer too large (or small) -- %d bytes\n", skb->len );
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ atomic_inc(&vcc->stats->tx_err);
+ return -EINVAL;
+ }
+
+#ifndef USE_SCATTERGATHER
+ if (skb_shinfo(skb)->nr_frags) {
+ hprintk("no scatter/gather support\n");
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ atomic_inc(&vcc->stats->tx_err);
+ return -EINVAL;
+ }
+#endif
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+
+ tpd = __alloc_tpd(he_dev);
+ if (tpd == NULL) {
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ atomic_inc(&vcc->stats->tx_err);
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+ return -ENOMEM;
+ }
+
+ if (vcc->qos.aal == ATM_AAL5)
+ tpd->status |= TPD_CELLTYPE(TPD_USERCELL);
+ else {
+ char *pti_clp = (void *) (skb->data + 3);
+ int clp, pti;
+
+ pti = (*pti_clp & ATM_HDR_PTI_MASK) >> ATM_HDR_PTI_SHIFT;
+ clp = (*pti_clp & ATM_HDR_CLP);
+ tpd->status |= TPD_CELLTYPE(pti);
+ if (clp)
+ tpd->status |= TPD_CLP;
+
+ skb_pull(skb, ATM_AAL0_SDU - ATM_CELL_PAYLOAD);
+ }
+
+#ifdef USE_SCATTERGATHER
+ tpd->iovec[slot].addr = dma_map_single(&he_dev->pci_dev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ tpd->iovec[slot].len = skb_headlen(skb);
+ ++slot;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ if (slot == TPD_MAXIOV) { /* queue tpd; start new tpd */
+ tpd->vcc = vcc;
+ tpd->skb = NULL; /* not the last fragment
+ so dont ->push() yet */
+ wmb();
+
+ __enqueue_tpd(he_dev, tpd, cid);
+ tpd = __alloc_tpd(he_dev);
+ if (tpd == NULL) {
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ atomic_inc(&vcc->stats->tx_err);
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+ return -ENOMEM;
+ }
+ tpd->status |= TPD_USERCELL;
+ slot = 0;
+ }
+
+ tpd->iovec[slot].addr = dma_map_single(&he_dev->pci_dev->dev,
+ (void *) page_address(frag->page) + frag->page_offset,
+ frag->size, DMA_TO_DEVICE);
+ tpd->iovec[slot].len = frag->size;
+ ++slot;
+
+ }
+
+ tpd->iovec[slot - 1].len |= TPD_LST;
+#else
+ tpd->address0 = dma_map_single(&he_dev->pci_dev->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ tpd->length0 = skb->len | TPD_LST;
+#endif
+ tpd->status |= TPD_INT;
+
+ tpd->vcc = vcc;
+ tpd->skb = skb;
+ wmb();
+ ATM_SKB(skb)->vcc = vcc;
+
+ __enqueue_tpd(he_dev, tpd, cid);
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ atomic_inc(&vcc->stats->tx);
+
+ return 0;
+}
+
+static int
+he_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user *arg)
+{
+ unsigned long flags;
+ struct he_dev *he_dev = HE_DEV(atm_dev);
+ struct he_ioctl_reg reg;
+ int err = 0;
+
+ switch (cmd) {
+ case HE_GET_REG:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&reg, arg,
+ sizeof(struct he_ioctl_reg)))
+ return -EFAULT;
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+ switch (reg.type) {
+ case HE_REGTYPE_PCI:
+ if (reg.addr >= HE_REGMAP_SIZE) {
+ err = -EINVAL;
+ break;
+ }
+
+ reg.val = he_readl(he_dev, reg.addr);
+ break;
+ case HE_REGTYPE_RCM:
+ reg.val =
+ he_readl_rcm(he_dev, reg.addr);
+ break;
+ case HE_REGTYPE_TCM:
+ reg.val =
+ he_readl_tcm(he_dev, reg.addr);
+ break;
+ case HE_REGTYPE_MBOX:
+ reg.val =
+ he_readl_mbox(he_dev, reg.addr);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+ if (err == 0)
+ if (copy_to_user(arg, &reg,
+ sizeof(struct he_ioctl_reg)))
+ return -EFAULT;
+ break;
+ default:
+#ifdef CONFIG_ATM_HE_USE_SUNI
+ if (atm_dev->phy && atm_dev->phy->ioctl)
+ err = atm_dev->phy->ioctl(atm_dev, cmd, arg);
+#else /* CONFIG_ATM_HE_USE_SUNI */
+ err = -EINVAL;
+#endif /* CONFIG_ATM_HE_USE_SUNI */
+ break;
+ }
+
+ return err;
+}
+
+static void
+he_phy_put(struct atm_dev *atm_dev, unsigned char val, unsigned long addr)
+{
+ unsigned long flags;
+ struct he_dev *he_dev = HE_DEV(atm_dev);
+
+ HPRINTK("phy_put(val 0x%x, addr 0x%lx)\n", val, addr);
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+ he_writel(he_dev, val, FRAMER + (addr*4));
+ (void) he_readl(he_dev, FRAMER + (addr*4)); /* flush posted writes */
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+}
+
+
+static unsigned char
+he_phy_get(struct atm_dev *atm_dev, unsigned long addr)
+{
+ unsigned long flags;
+ struct he_dev *he_dev = HE_DEV(atm_dev);
+ unsigned reg;
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+ reg = he_readl(he_dev, FRAMER + (addr*4));
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ HPRINTK("phy_get(addr 0x%lx) =0x%x\n", addr, reg);
+ return reg;
+}
+
+static int
+he_proc_read(struct atm_dev *dev, loff_t *pos, char *page)
+{
+ unsigned long flags;
+ struct he_dev *he_dev = HE_DEV(dev);
+ int left, i;
+#ifdef notdef
+ struct he_rbrq *rbrq_tail;
+ struct he_tpdrq *tpdrq_head;
+ int rbpl_head, rbpl_tail;
+#endif
+ static long mcc = 0, oec = 0, dcc = 0, cec = 0;
+
+
+ left = *pos;
+ if (!left--)
+ return sprintf(page, "ATM he driver\n");
+
+ if (!left--)
+ return sprintf(page, "%s%s\n\n",
+ he_dev->prod_id, he_dev->media & 0x40 ? "SM" : "MM");
+
+ if (!left--)
+ return sprintf(page, "Mismatched Cells VPI/VCI Not Open Dropped Cells RCM Dropped Cells\n");
+
+ spin_lock_irqsave(&he_dev->global_lock, flags);
+ mcc += he_readl(he_dev, MCC);
+ oec += he_readl(he_dev, OEC);
+ dcc += he_readl(he_dev, DCC);
+ cec += he_readl(he_dev, CEC);
+ spin_unlock_irqrestore(&he_dev->global_lock, flags);
+
+ if (!left--)
+ return sprintf(page, "%16ld %16ld %13ld %17ld\n\n",
+ mcc, oec, dcc, cec);
+
+ if (!left--)
+ return sprintf(page, "irq_size = %d inuse = ? peak = %d\n",
+ CONFIG_IRQ_SIZE, he_dev->irq_peak);
+
+ if (!left--)
+ return sprintf(page, "tpdrq_size = %d inuse = ?\n",
+ CONFIG_TPDRQ_SIZE);
+
+ if (!left--)
+ return sprintf(page, "rbrq_size = %d inuse = ? peak = %d\n",
+ CONFIG_RBRQ_SIZE, he_dev->rbrq_peak);
+
+ if (!left--)
+ return sprintf(page, "tbrq_size = %d peak = %d\n",
+ CONFIG_TBRQ_SIZE, he_dev->tbrq_peak);
+
+
+#ifdef notdef
+ rbpl_head = RBPL_MASK(he_readl(he_dev, G0_RBPL_S));
+ rbpl_tail = RBPL_MASK(he_readl(he_dev, G0_RBPL_T));
+
+ inuse = rbpl_head - rbpl_tail;
+ if (inuse < 0)
+ inuse += CONFIG_RBPL_SIZE * sizeof(struct he_rbp);
+ inuse /= sizeof(struct he_rbp);
+
+ if (!left--)
+ return sprintf(page, "rbpl_size = %d inuse = %d\n\n",
+ CONFIG_RBPL_SIZE, inuse);
+#endif
+
+ if (!left--)
+ return sprintf(page, "rate controller periods (cbr)\n pcr #vc\n");
+
+ for (i = 0; i < HE_NUM_CS_STPER; ++i)
+ if (!left--)
+ return sprintf(page, "cs_stper%-2d %8ld %3d\n", i,
+ he_dev->cs_stper[i].pcr,
+ he_dev->cs_stper[i].inuse);
+
+ if (!left--)
+ return sprintf(page, "total bw (cbr): %d (limit %d)\n",
+ he_dev->total_bw, he_dev->atm_dev->link_rate * 10 / 9);
+
+ return 0;
+}
+
+/* eeprom routines -- see 4.7 */
+
+static u8 read_prom_byte(struct he_dev *he_dev, int addr)
+{
+ u32 val = 0, tmp_read = 0;
+ int i, j = 0;
+ u8 byte_read = 0;
+
+ val = readl(he_dev->membase + HOST_CNTL);
+ val &= 0xFFFFE0FF;
+
+ /* Turn on write enable */
+ val |= 0x800;
+ he_writel(he_dev, val, HOST_CNTL);
+
+ /* Send READ instruction */
+ for (i = 0; i < ARRAY_SIZE(readtab); i++) {
+ he_writel(he_dev, val | readtab[i], HOST_CNTL);
+ udelay(EEPROM_DELAY);
+ }
+
+ /* Next, we need to send the byte address to read from */
+ for (i = 7; i >= 0; i--) {
+ he_writel(he_dev, val | clocktab[j++] | (((addr >> i) & 1) << 9), HOST_CNTL);
+ udelay(EEPROM_DELAY);
+ he_writel(he_dev, val | clocktab[j++] | (((addr >> i) & 1) << 9), HOST_CNTL);
+ udelay(EEPROM_DELAY);
+ }
+
+ j = 0;
+
+ val &= 0xFFFFF7FF; /* Turn off write enable */
+ he_writel(he_dev, val, HOST_CNTL);
+
+ /* Now, we can read data from the EEPROM by clocking it in */
+ for (i = 7; i >= 0; i--) {
+ he_writel(he_dev, val | clocktab[j++], HOST_CNTL);
+ udelay(EEPROM_DELAY);
+ tmp_read = he_readl(he_dev, HOST_CNTL);
+ byte_read |= (unsigned char)
+ ((tmp_read & ID_DOUT) >> ID_DOFFSET << i);
+ he_writel(he_dev, val | clocktab[j++], HOST_CNTL);
+ udelay(EEPROM_DELAY);
+ }
+
+ he_writel(he_dev, val | ID_CS, HOST_CNTL);
+ udelay(EEPROM_DELAY);
+
+ return byte_read;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("chas williams <chas@cmf.nrl.navy.mil>");
+MODULE_DESCRIPTION("ForeRunnerHE ATM Adapter driver");
+module_param(disable64, bool, 0);
+MODULE_PARM_DESC(disable64, "disable 64-bit pci bus transfers");
+module_param(nvpibits, short, 0);
+MODULE_PARM_DESC(nvpibits, "numbers of bits for vpi (default 0)");
+module_param(nvcibits, short, 0);
+MODULE_PARM_DESC(nvcibits, "numbers of bits for vci (default 12)");
+module_param(rx_skb_reserve, short, 0);
+MODULE_PARM_DESC(rx_skb_reserve, "padding for receive skb (default 16)");
+module_param(irq_coalesce, bool, 0);
+MODULE_PARM_DESC(irq_coalesce, "use interrupt coalescing (default 1)");
+module_param(sdh, bool, 0);
+MODULE_PARM_DESC(sdh, "use SDH framing (default 0)");
+
+static struct pci_device_id he_pci_tbl[] = {
+ { PCI_VDEVICE(FORE, PCI_DEVICE_ID_FORE_HE), 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, he_pci_tbl);
+
+static struct pci_driver he_driver = {
+ .name = "he",
+ .probe = he_init_one,
+ .remove = he_remove_one,
+ .id_table = he_pci_tbl,
+};
+
+module_pci_driver(he_driver);
diff --git a/drivers/atm/he.h b/drivers/atm/he.h
new file mode 100644
index 000000000..f3f53674e
--- /dev/null
+++ b/drivers/atm/he.h
@@ -0,0 +1,845 @@
+/*
+
+ he.h
+
+ ForeRunnerHE ATM Adapter driver for ATM on Linux
+ Copyright (C) 1999-2001 Naval Research Laboratory
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+/*
+
+ he.h
+
+ ForeRunnerHE ATM Adapter driver for ATM on Linux
+ Copyright (C) 1999-2000 Naval Research Laboratory
+
+ Permission to use, copy, modify and distribute this software and its
+ documentation is hereby granted, provided that both the copyright
+ notice and this permission notice appear in all copies of the software,
+ derivative works or modified versions, and any portions thereof, and
+ that both notices appear in supporting documentation.
+
+ NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
+ DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
+ RESULTING FROM THE USE OF THIS SOFTWARE.
+
+ */
+
+#ifndef _HE_H_
+#define _HE_H_
+
+#define DEV_LABEL "he"
+
+#define CONFIG_DEFAULT_VCIBITS 12
+#define CONFIG_DEFAULT_VPIBITS 0
+
+#define CONFIG_IRQ_SIZE 128
+#define CONFIG_IRQ_THRESH (CONFIG_IRQ_SIZE/2)
+
+#define CONFIG_TPDRQ_SIZE 512
+#define TPDRQ_MASK(x) (((unsigned long)(x))&((CONFIG_TPDRQ_SIZE<<3)-1))
+
+#define CONFIG_RBRQ_SIZE 512
+#define CONFIG_RBRQ_THRESH 400
+#define RBRQ_MASK(x) (((unsigned long)(x))&((CONFIG_RBRQ_SIZE<<3)-1))
+
+#define CONFIG_TBRQ_SIZE 512
+#define CONFIG_TBRQ_THRESH 400
+#define TBRQ_MASK(x) (((unsigned long)(x))&((CONFIG_TBRQ_SIZE<<2)-1))
+
+#define CONFIG_RBPL_SIZE 512
+#define CONFIG_RBPL_THRESH 64
+#define CONFIG_RBPL_BUFSIZE 4096
+#define RBPL_MASK(x) (((unsigned long)(x))&((CONFIG_RBPL_SIZE<<3)-1))
+
+/* 5.1.3 initialize connection memory */
+
+#define CONFIG_RSRA 0x00000
+#define CONFIG_RCMLBM 0x08000
+#define CONFIG_RCMABR 0x0d800
+#define CONFIG_RSRB 0x0e000
+
+#define CONFIG_TSRA 0x00000
+#define CONFIG_TSRB 0x08000
+#define CONFIG_TSRC 0x0c000
+#define CONFIG_TSRD 0x0e000
+#define CONFIG_TMABR 0x0f000
+#define CONFIG_TPDBA 0x10000
+
+#define HE_MAXCIDBITS 12
+
+/* 2.9.3.3 interrupt encodings */
+
+struct he_irq {
+ volatile u32 isw;
+};
+
+#define IRQ_ALIGNMENT 0x1000
+
+#define NEXT_ENTRY(base, tail, mask) \
+ (((unsigned long)base)|(((unsigned long)(tail+1))&mask))
+
+#define ITYPE_INVALID 0xffffffff
+#define ITYPE_TBRQ_THRESH (0<<3)
+#define ITYPE_TPD_COMPLETE (1<<3)
+#define ITYPE_RBPS_THRESH (2<<3)
+#define ITYPE_RBPL_THRESH (3<<3)
+#define ITYPE_RBRQ_THRESH (4<<3)
+#define ITYPE_RBRQ_TIMER (5<<3)
+#define ITYPE_PHY (6<<3)
+#define ITYPE_OTHER 0x80
+#define ITYPE_PARITY 0x81
+#define ITYPE_ABORT 0x82
+
+#define ITYPE_GROUP(x) (x & 0x7)
+#define ITYPE_TYPE(x) (x & 0xf8)
+
+#define HE_NUM_GROUPS 8
+
+/* 2.1.4 transmit packet descriptor */
+
+struct he_tpd {
+
+ /* read by the adapter */
+
+ volatile u32 status;
+ volatile u32 reserved;
+
+#define TPD_MAXIOV 3
+ struct {
+ u32 addr, len;
+ } iovec[TPD_MAXIOV];
+
+#define address0 iovec[0].addr
+#define length0 iovec[0].len
+
+ /* linux-atm extensions */
+
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+
+ struct list_head entry;
+};
+
+#define TPD_ALIGNMENT 64
+#define TPD_LEN_MASK 0xffff
+
+#define TPD_ADDR_SHIFT 6
+#define TPD_MASK 0xffffffc0
+#define TPD_ADDR(x) ((x) & TPD_MASK)
+#define TPD_INDEX(x) (TPD_ADDR(x) >> TPD_ADDR_SHIFT)
+
+
+/* table 2.3 transmit buffer return elements */
+
+struct he_tbrq {
+ volatile u32 tbre;
+};
+
+#define TBRQ_ALIGNMENT CONFIG_TBRQ_SIZE
+
+#define TBRQ_TPD(tbrq) ((tbrq)->tbre & 0xffffffc0)
+#define TBRQ_EOS(tbrq) ((tbrq)->tbre & (1<<3))
+#define TBRQ_MULTIPLE(tbrq) ((tbrq)->tbre & (1))
+
+/* table 2.21 receive buffer return queue element field organization */
+
+struct he_rbrq {
+ volatile u32 addr;
+ volatile u32 cidlen;
+};
+
+#define RBRQ_ALIGNMENT CONFIG_RBRQ_SIZE
+
+#define RBRQ_ADDR(rbrq) ((rbrq)->addr & 0xffffffc0)
+#define RBRQ_CRC_ERR(rbrq) ((rbrq)->addr & (1<<5))
+#define RBRQ_LEN_ERR(rbrq) ((rbrq)->addr & (1<<4))
+#define RBRQ_END_PDU(rbrq) ((rbrq)->addr & (1<<3))
+#define RBRQ_AAL5_PROT(rbrq) ((rbrq)->addr & (1<<2))
+#define RBRQ_CON_CLOSED(rbrq) ((rbrq)->addr & (1<<1))
+#define RBRQ_HBUF_ERR(rbrq) ((rbrq)->addr & 1)
+#define RBRQ_CID(rbrq) (((rbrq)->cidlen >> 16) & 0x1fff)
+#define RBRQ_BUFLEN(rbrq) ((rbrq)->cidlen & 0xffff)
+
+/* figure 2.3 transmit packet descriptor ready queue */
+
+struct he_tpdrq {
+ volatile u32 tpd;
+ volatile u32 cid;
+};
+
+#define TPDRQ_ALIGNMENT CONFIG_TPDRQ_SIZE
+
+/* table 2.30 host status page detail */
+
+#define HSP_ALIGNMENT 0x400 /* must align on 1k boundary */
+
+struct he_hsp {
+ struct he_hsp_entry {
+ volatile u32 tbrq_tail;
+ volatile u32 reserved1[15];
+ volatile u32 rbrq_tail;
+ volatile u32 reserved2[15];
+ } group[HE_NUM_GROUPS];
+};
+
+/*
+ * figure 2.9 receive buffer pools
+ *
+ * since a virtual address might be more than 32 bits, we store an index
+ * in the virt member of he_rbp. NOTE: the lower six bits in the rbrq
+ * addr member are used for buffer status further limiting us to 26 bits.
+ */
+
+struct he_rbp {
+ volatile u32 phys;
+ volatile u32 idx; /* virt */
+};
+
+#define RBP_IDX_OFFSET 6
+
+/*
+ * the he dma engine will try to hold an extra 16 buffers in its local
+ * caches. and add a couple buffers for safety.
+ */
+
+#define RBPL_TABLE_SIZE (CONFIG_RBPL_SIZE + 16 + 2)
+
+struct he_buff {
+ struct list_head entry;
+ dma_addr_t mapping;
+ unsigned long len;
+ u8 data[];
+};
+
+#ifdef notyet
+struct he_group {
+ u32 rpbl_size, rpbl_qsize;
+ struct he_rpb_entry *rbpl_ba;
+};
+#endif
+
+#define HE_LOOKUP_VCC(dev, cid) ((dev)->he_vcc_table[(cid)].vcc)
+
+struct he_vcc_table
+{
+ struct atm_vcc *vcc;
+};
+
+struct he_cs_stper
+{
+ long pcr;
+ int inuse;
+};
+
+#define HE_NUM_CS_STPER 16
+
+struct he_dev {
+ unsigned int number;
+ unsigned int irq;
+ void __iomem *membase;
+
+ char prod_id[30];
+ char mac_addr[6];
+ int media;
+
+ unsigned int vcibits, vpibits;
+ unsigned int cells_per_row;
+ unsigned int bytes_per_row;
+ unsigned int cells_per_lbuf;
+ unsigned int r0_numrows, r0_startrow, r0_numbuffs;
+ unsigned int r1_numrows, r1_startrow, r1_numbuffs;
+ unsigned int tx_numrows, tx_startrow, tx_numbuffs;
+ unsigned int buffer_limit;
+
+ struct he_vcc_table *he_vcc_table;
+
+#ifdef notyet
+ struct he_group group[HE_NUM_GROUPS];
+#endif
+ struct he_cs_stper cs_stper[HE_NUM_CS_STPER];
+ unsigned total_bw;
+
+ dma_addr_t irq_phys;
+ struct he_irq *irq_base, *irq_head, *irq_tail;
+ volatile unsigned *irq_tailoffset;
+ int irq_peak;
+
+ struct tasklet_struct tasklet;
+ struct dma_pool *tpd_pool;
+ struct list_head outstanding_tpds;
+
+ dma_addr_t tpdrq_phys;
+ struct he_tpdrq *tpdrq_base, *tpdrq_tail, *tpdrq_head;
+
+ spinlock_t global_lock; /* 8.1.5 pci transaction ordering
+ error problem */
+ dma_addr_t rbrq_phys;
+ struct he_rbrq *rbrq_base, *rbrq_head;
+ int rbrq_peak;
+
+ struct he_buff **rbpl_virt;
+ unsigned long *rbpl_table;
+ unsigned long rbpl_hint;
+ struct dma_pool *rbpl_pool;
+ dma_addr_t rbpl_phys;
+ struct he_rbp *rbpl_base, *rbpl_tail;
+ struct list_head rbpl_outstanding;
+ int rbpl_peak;
+
+ dma_addr_t tbrq_phys;
+ struct he_tbrq *tbrq_base, *tbrq_head;
+ int tbrq_peak;
+
+ dma_addr_t hsp_phys;
+ struct he_hsp *hsp;
+
+ struct pci_dev *pci_dev;
+ struct atm_dev *atm_dev;
+ struct he_dev *next;
+};
+
+#define HE_MAXIOV 20
+
+struct he_vcc
+{
+ struct list_head buffers;
+ int pdu_len;
+ int rc_index;
+
+ wait_queue_head_t rx_waitq;
+ wait_queue_head_t tx_waitq;
+};
+
+#define HE_VCC(vcc) ((struct he_vcc *)(vcc->dev_data))
+
+#define PCI_VENDOR_ID_FORE 0x1127
+#define PCI_DEVICE_ID_FORE_HE 0x400
+
+#define GEN_CNTL_0 0x40
+#define INT_PROC_ENBL (1<<25)
+#define SLAVE_ENDIAN_MODE (1<<16)
+#define MRL_ENB (1<<5)
+#define MRM_ENB (1<<4)
+#define INIT_ENB (1<<2)
+#define IGNORE_TIMEOUT (1<<1)
+#define ENBL_64 (1<<0)
+
+#define MIN_PCI_LATENCY 32 /* errata 8.1.3 */
+
+#define HE_DEV(dev) ((struct he_dev *) (dev)->dev_data)
+
+#define he_is622(dev) ((dev)->media & 0x1)
+#define he_isMM(dev) ((dev)->media & 0x20)
+
+#define HE_REGMAP_SIZE 0x100000
+
+#define RESET_CNTL 0x80000
+#define BOARD_RST_STATUS (1<<6)
+
+#define HOST_CNTL 0x80004
+#define PCI_BUS_SIZE64 (1<<27)
+#define DESC_RD_STATIC_64 (1<<26)
+#define DATA_RD_STATIC_64 (1<<25)
+#define DATA_WR_STATIC_64 (1<<24)
+#define ID_CS (1<<12)
+#define ID_WREN (1<<11)
+#define ID_DOUT (1<<10)
+#define ID_DOFFSET 10
+#define ID_DIN (1<<9)
+#define ID_CLOCK (1<<8)
+#define QUICK_RD_RETRY (1<<7)
+#define QUICK_WR_RETRY (1<<6)
+#define OUTFF_ENB (1<<5)
+#define CMDFF_ENB (1<<4)
+#define PERR_INT_ENB (1<<2)
+#define IGNORE_INTR (1<<0)
+
+#define LB_SWAP 0x80008
+#define SWAP_RNUM_MAX(x) (x<<27)
+#define DATA_WR_SWAP (1<<20)
+#define DESC_RD_SWAP (1<<19)
+#define DATA_RD_SWAP (1<<18)
+#define INTR_SWAP (1<<17)
+#define DESC_WR_SWAP (1<<16)
+#define SDRAM_INIT (1<<15)
+#define BIG_ENDIAN_HOST (1<<14)
+#define XFER_SIZE (1<<7)
+
+#define LB_MEM_ADDR 0x8000c
+#define LB_MEM_DATA 0x80010
+
+#define LB_MEM_ACCESS 0x80014
+#define LB_MEM_HNDSHK (1<<30)
+#define LM_MEM_WRITE (0x7)
+#define LM_MEM_READ (0x3)
+
+#define SDRAM_CTL 0x80018
+#define LB_64_ENB (1<<3)
+#define LB_TWR (1<<2)
+#define LB_TRP (1<<1)
+#define LB_TRAS (1<<0)
+
+#define INT_FIFO 0x8001c
+#define INT_MASK_D (1<<15)
+#define INT_MASK_C (1<<14)
+#define INT_MASK_B (1<<13)
+#define INT_MASK_A (1<<12)
+#define INT_CLEAR_D (1<<11)
+#define INT_CLEAR_C (1<<10)
+#define INT_CLEAR_B (1<<9)
+#define INT_CLEAR_A (1<<8)
+
+#define ABORT_ADDR 0x80020
+
+#define IRQ0_BASE 0x80080
+#define IRQ_BASE(x) (x<<12)
+#define IRQ_MASK ((CONFIG_IRQ_SIZE<<2)-1) /* was 0x3ff */
+#define IRQ_TAIL(x) (((unsigned long)(x)) & IRQ_MASK)
+#define IRQ0_HEAD 0x80084
+#define IRQ_SIZE(x) (x<<22)
+#define IRQ_THRESH(x) (x<<12)
+#define IRQ_HEAD(x) (x<<2)
+/* #define IRQ_PENDING (1) conflict with linux/irq.h */
+#define IRQ0_CNTL 0x80088
+#define IRQ_ADDRSEL(x) (x<<2)
+#define IRQ_INT_A (0<<2)
+#define IRQ_INT_B (1<<2)
+#define IRQ_INT_C (2<<2)
+#define IRQ_INT_D (3<<2)
+#define IRQ_TYPE_ADDR 0x1
+#define IRQ_TYPE_LINE 0x0
+#define IRQ0_DATA 0x8008c
+
+#define IRQ1_BASE 0x80090
+#define IRQ1_HEAD 0x80094
+#define IRQ1_CNTL 0x80098
+#define IRQ1_DATA 0x8009c
+
+#define IRQ2_BASE 0x800a0
+#define IRQ2_HEAD 0x800a4
+#define IRQ2_CNTL 0x800a8
+#define IRQ2_DATA 0x800ac
+
+#define IRQ3_BASE 0x800b0
+#define IRQ3_HEAD 0x800b4
+#define IRQ3_CNTL 0x800b8
+#define IRQ3_DATA 0x800bc
+
+#define GRP_10_MAP 0x800c0
+#define GRP_32_MAP 0x800c4
+#define GRP_54_MAP 0x800c8
+#define GRP_76_MAP 0x800cc
+
+#define G0_RBPS_S 0x80400
+#define G0_RBPS_T 0x80404
+#define RBP_TAIL(x) ((x)<<3)
+#define RBP_MASK(x) ((x)|0x1fff)
+#define G0_RBPS_QI 0x80408
+#define RBP_QSIZE(x) ((x)<<14)
+#define RBP_INT_ENB (1<<13)
+#define RBP_THRESH(x) (x)
+#define G0_RBPS_BS 0x8040c
+#define G0_RBPL_S 0x80410
+#define G0_RBPL_T 0x80414
+#define G0_RBPL_QI 0x80418
+#define G0_RBPL_BS 0x8041c
+
+#define G1_RBPS_S 0x80420
+#define G1_RBPS_T 0x80424
+#define G1_RBPS_QI 0x80428
+#define G1_RBPS_BS 0x8042c
+#define G1_RBPL_S 0x80430
+#define G1_RBPL_T 0x80434
+#define G1_RBPL_QI 0x80438
+#define G1_RBPL_BS 0x8043c
+
+#define G2_RBPS_S 0x80440
+#define G2_RBPS_T 0x80444
+#define G2_RBPS_QI 0x80448
+#define G2_RBPS_BS 0x8044c
+#define G2_RBPL_S 0x80450
+#define G2_RBPL_T 0x80454
+#define G2_RBPL_QI 0x80458
+#define G2_RBPL_BS 0x8045c
+
+#define G3_RBPS_S 0x80460
+#define G3_RBPS_T 0x80464
+#define G3_RBPS_QI 0x80468
+#define G3_RBPS_BS 0x8046c
+#define G3_RBPL_S 0x80470
+#define G3_RBPL_T 0x80474
+#define G3_RBPL_QI 0x80478
+#define G3_RBPL_BS 0x8047c
+
+#define G4_RBPS_S 0x80480
+#define G4_RBPS_T 0x80484
+#define G4_RBPS_QI 0x80488
+#define G4_RBPS_BS 0x8048c
+#define G4_RBPL_S 0x80490
+#define G4_RBPL_T 0x80494
+#define G4_RBPL_QI 0x80498
+#define G4_RBPL_BS 0x8049c
+
+#define G5_RBPS_S 0x804a0
+#define G5_RBPS_T 0x804a4
+#define G5_RBPS_QI 0x804a8
+#define G5_RBPS_BS 0x804ac
+#define G5_RBPL_S 0x804b0
+#define G5_RBPL_T 0x804b4
+#define G5_RBPL_QI 0x804b8
+#define G5_RBPL_BS 0x804bc
+
+#define G6_RBPS_S 0x804c0
+#define G6_RBPS_T 0x804c4
+#define G6_RBPS_QI 0x804c8
+#define G6_RBPS_BS 0x804cc
+#define G6_RBPL_S 0x804d0
+#define G6_RBPL_T 0x804d4
+#define G6_RBPL_QI 0x804d8
+#define G6_RBPL_BS 0x804dc
+
+#define G7_RBPS_S 0x804e0
+#define G7_RBPS_T 0x804e4
+#define G7_RBPS_QI 0x804e8
+#define G7_RBPS_BS 0x804ec
+
+#define G7_RBPL_S 0x804f0
+#define G7_RBPL_T 0x804f4
+#define G7_RBPL_QI 0x804f8
+#define G7_RBPL_BS 0x804fc
+
+#define G0_RBRQ_ST 0x80500
+#define G0_RBRQ_H 0x80504
+#define G0_RBRQ_Q 0x80508
+#define RBRQ_THRESH(x) ((x)<<13)
+#define RBRQ_SIZE(x) (x)
+#define G0_RBRQ_I 0x8050c
+#define RBRQ_TIME(x) ((x)<<8)
+#define RBRQ_COUNT(x) (x)
+
+/* fill in 1 ... 7 later */
+
+#define G0_TBRQ_B_T 0x80600
+#define G0_TBRQ_H 0x80604
+#define G0_TBRQ_S 0x80608
+#define G0_TBRQ_THRESH 0x8060c
+#define TBRQ_THRESH(x) (x)
+
+/* fill in 1 ... 7 later */
+
+#define RH_CONFIG 0x805c0
+#define PHY_INT_ENB (1<<10)
+#define OAM_GID(x) (x<<7)
+#define PTMR_PRE(x) (x)
+
+#define G0_INMQ_S 0x80580
+#define G0_INMQ_L 0x80584
+#define G1_INMQ_S 0x80588
+#define G1_INMQ_L 0x8058c
+#define G2_INMQ_S 0x80590
+#define G2_INMQ_L 0x80594
+#define G3_INMQ_S 0x80598
+#define G3_INMQ_L 0x8059c
+#define G4_INMQ_S 0x805a0
+#define G4_INMQ_L 0x805a4
+#define G5_INMQ_S 0x805a8
+#define G5_INMQ_L 0x805ac
+#define G6_INMQ_S 0x805b0
+#define G6_INMQ_L 0x805b4
+#define G7_INMQ_S 0x805b8
+#define G7_INMQ_L 0x805bc
+
+#define TPDRQ_B_H 0x80680
+#define TPDRQ_T 0x80684
+#define TPDRQ_S 0x80688
+
+#define UBUFF_BA 0x8068c
+
+#define RLBF0_H 0x806c0
+#define RLBF0_T 0x806c4
+#define RLBF1_H 0x806c8
+#define RLBF1_T 0x806cc
+#define RLBC_H 0x806d0
+#define RLBC_T 0x806d4
+#define RLBC_H2 0x806d8
+#define TLBF_H 0x806e0
+#define TLBF_T 0x806e4
+#define RLBF0_C 0x806e8
+#define RLBF1_C 0x806ec
+#define RXTHRSH 0x806f0
+#define LITHRSH 0x806f4
+
+#define LBARB 0x80700
+#define SLICE_X(x) (x<<28)
+#define ARB_RNUM_MAX(x) (x<<23)
+#define TH_PRTY(x) (x<<21)
+#define RH_PRTY(x) (x<<19)
+#define TL_PRTY(x) (x<<17)
+#define RL_PRTY(x) (x<<15)
+#define BUS_MULTI(x) (x<<8)
+#define NET_PREF(x) (x)
+
+#define SDRAMCON 0x80704
+#define BANK_ON (1<<14)
+#define WIDE_DATA (1<<13)
+#define TWR_WAIT (1<<12)
+#define TRP_WAIT (1<<11)
+#define TRAS_WAIT (1<<10)
+#define REF_RATE(x) (x)
+
+#define LBSTAT 0x80708
+
+#define RCC_STAT 0x8070c
+#define RCC_BUSY (1)
+
+#define TCMCONFIG 0x80740
+#define TM_DESL2 (1<<10)
+#define TM_BANK_WAIT(x) (x<<6)
+#define TM_ADD_BANK4(x) (x<<4)
+#define TM_PAR_CHECK(x) (x<<3)
+#define TM_RW_WAIT(x) (x<<2)
+#define TM_SRAM_TYPE(x) (x)
+
+#define TSRB_BA 0x80744
+#define TSRC_BA 0x80748
+#define TMABR_BA 0x8074c
+#define TPD_BA 0x80750
+#define TSRD_BA 0x80758
+
+#define TX_CONFIG 0x80760
+#define DRF_THRESH(x) (x<<22)
+#define TX_UT_MODE(x) (x<<21)
+#define TX_VCI_MASK(x) (x<<17)
+#define LBFREE_CNT(x) (x)
+
+#define TXAAL5_PROTO 0x80764
+#define CPCS_UU(x) (x<<8)
+#define CPI(x) (x)
+
+#define RCMCONFIG 0x80780
+#define RM_DESL2(x) (x<<10)
+#define RM_BANK_WAIT(x) (x<<6)
+#define RM_ADD_BANK(x) (x<<4)
+#define RM_PAR_CHECK(x) (x<<3)
+#define RM_RW_WAIT(x) (x<<2)
+#define RM_SRAM_TYPE(x) (x)
+
+#define RCMRSRB_BA 0x80784
+#define RCMLBM_BA 0x80788
+#define RCMABR_BA 0x8078c
+
+#define RC_CONFIG 0x807c0
+#define UT_RD_DELAY(x) (x<<11)
+#define WRAP_MODE(x) (x<<10)
+#define RC_UT_MODE(x) (x<<9)
+#define RX_ENABLE (1<<8)
+#define RX_VALVP(x) (x<<4)
+#define RX_VALVC(x) (x)
+
+#define MCC 0x807c4
+#define OEC 0x807c8
+#define DCC 0x807cc
+#define CEC 0x807d0
+
+#define HSP_BA 0x807f0
+
+#define LB_CONFIG 0x807f4
+#define LB_SIZE(x) (x)
+
+#define CON_DAT 0x807f8
+#define CON_CTL 0x807fc
+#define CON_CTL_MBOX (2<<30)
+#define CON_CTL_TCM (1<<30)
+#define CON_CTL_RCM (0<<30)
+#define CON_CTL_WRITE (1<<29)
+#define CON_CTL_READ (0<<29)
+#define CON_CTL_BUSY (1<<28)
+#define CON_BYTE_DISABLE_3 (1<<22) /* 24..31 */
+#define CON_BYTE_DISABLE_2 (1<<21) /* 16..23 */
+#define CON_BYTE_DISABLE_1 (1<<20) /* 8..15 */
+#define CON_BYTE_DISABLE_0 (1<<19) /* 0..7 */
+#define CON_CTL_ADDR(x) (x)
+
+#define FRAMER 0x80800 /* to 0x80bfc */
+
+/* 3.3 network controller (internal) mailbox registers */
+
+#define CS_STPER0 0x0
+ /* ... */
+#define CS_STPER31 0x01f
+
+#define CS_STTIM0 0x020
+ /* ... */
+#define CS_STTIM31 0x03f
+
+#define CS_TGRLD0 0x040
+ /* ... */
+#define CS_TGRLD15 0x04f
+
+#define CS_ERTHR0 0x050
+#define CS_ERTHR1 0x051
+#define CS_ERTHR2 0x052
+#define CS_ERTHR3 0x053
+#define CS_ERTHR4 0x054
+#define CS_ERCTL0 0x055
+#define TX_ENABLE (1<<28)
+#define ER_ENABLE (1<<27)
+#define CS_ERCTL1 0x056
+#define CS_ERCTL2 0x057
+#define CS_ERSTAT0 0x058
+#define CS_ERSTAT1 0x059
+
+#define CS_RTCCT 0x060
+#define CS_RTFWC 0x061
+#define CS_RTFWR 0x062
+#define CS_RTFTC 0x063
+#define CS_RTATR 0x064
+
+#define CS_TFBSET 0x070
+#define CS_TFBADD 0x071
+#define CS_TFBSUB 0x072
+#define CS_WCRMAX 0x073
+#define CS_WCRMIN 0x074
+#define CS_WCRINC 0x075
+#define CS_WCRDEC 0x076
+#define CS_WCRCEIL 0x077
+#define CS_BWDCNT 0x078
+
+#define CS_OTPPER 0x080
+#define CS_OTWPER 0x081
+#define CS_OTTLIM 0x082
+#define CS_OTTCNT 0x083
+
+#define CS_HGRRT0 0x090
+ /* ... */
+#define CS_HGRRT7 0x097
+
+#define CS_ORPTRS 0x0a0
+
+#define RXCON_CLOSE 0x100
+
+
+#define RCM_MEM_SIZE 0x10000 /* 1M of 32-bit registers */
+#define TCM_MEM_SIZE 0x20000 /* 2M of 32-bit registers */
+
+/* 2.5 transmit connection memory registers */
+
+#define TSR0_CONN_STATE(x) ((x>>28) & 0x7)
+#define TSR0_USE_WMIN (1<<23)
+#define TSR0_GROUP(x) ((x & 0x7)<<18)
+#define TSR0_ABR (2<<16)
+#define TSR0_UBR (1<<16)
+#define TSR0_CBR (0<<16)
+#define TSR0_PROT (1<<15)
+#define TSR0_AAL0_SDU (2<<12)
+#define TSR0_AAL0 (1<<12)
+#define TSR0_AAL5 (0<<12)
+#define TSR0_HALT_ER (1<<11)
+#define TSR0_MARK_CI (1<<10)
+#define TSR0_MARK_ER (1<<9)
+#define TSR0_UPDATE_GER (1<<8)
+#define TSR0_RC_INDEX(x) (x & 0x1F)
+
+#define TSR1_PCR(x) ((x & 0x7FFF)<<16)
+#define TSR1_MCR(x) (x & 0x7FFF)
+
+#define TSR2_ACR(x) ((x & 0x7FFF)<<16)
+
+#define TSR3_NRM_CNT(x) ((x & 0xFF)<<24)
+#define TSR3_CRM_CNT(x) (x & 0xFFFF)
+
+#define TSR4_FLUSH_CONN (1<<31)
+#define TSR4_SESSION_ENDED (1<<30)
+#define TSR4_CRC10 (1<<28)
+#define TSR4_NULL_CRC10 (1<<27)
+#define TSR4_PROT (1<<26)
+#define TSR4_AAL0_SDU (2<<23)
+#define TSR4_AAL0 (1<<23)
+#define TSR4_AAL5 (0<<23)
+
+#define TSR9_OPEN_CONN (1<<20)
+
+#define TSR11_ICR(x) ((x & 0x7FFF)<<16)
+#define TSR11_TRM(x) ((x & 0x7)<<13)
+#define TSR11_NRM(x) ((x & 0x7)<<10)
+#define TSR11_ADTF(x) (x & 0x3FF)
+
+#define TSR13_RDF(x) ((x & 0xF)<<23)
+#define TSR13_RIF(x) ((x & 0xF)<<19)
+#define TSR13_CDF(x) ((x & 0x7)<<16)
+#define TSR13_CRM(x) (x & 0xFFFF)
+
+#define TSR14_DELETE (1<<31)
+#define TSR14_ABR_CLOSE (1<<16)
+
+/* 2.7.1 per connection receieve state registers */
+
+#define RSR0_START_PDU (1<<10)
+#define RSR0_OPEN_CONN (1<<6)
+#define RSR0_CLOSE_CONN (0<<6)
+#define RSR0_PPD_ENABLE (1<<5)
+#define RSR0_EPD_ENABLE (1<<4)
+#define RSR0_TCP_CKSUM (1<<3)
+#define RSR0_AAL5 (0)
+#define RSR0_AAL0 (1)
+#define RSR0_AAL0_SDU (2)
+#define RSR0_RAWCELL (3)
+#define RSR0_RAWCELL_CRC10 (4)
+
+#define RSR1_AQI_ENABLE (1<<20)
+#define RSR1_RBPL_ONLY (1<<19)
+#define RSR1_GROUP(x) ((x)<<16)
+
+#define RSR4_AQI_ENABLE (1<<30)
+#define RSR4_GROUP(x) ((x)<<27)
+#define RSR4_RBPL_ONLY (1<<26)
+
+/* 2.1.4 transmit packet descriptor */
+
+#define TPD_USERCELL 0x0
+#define TPD_SEGMENT_OAMF5 0x4
+#define TPD_END2END_OAMF5 0x5
+#define TPD_RMCELL 0x6
+#define TPD_CELLTYPE(x) (x<<3)
+#define TPD_EOS (1<<2)
+#define TPD_CLP (1<<1)
+#define TPD_INT (1<<0)
+#define TPD_LST (1<<31)
+
+/* table 4.3 serial eeprom information */
+
+#define PROD_ID 0x08 /* char[] */
+#define PROD_ID_LEN 30
+#define HW_REV 0x26 /* char[] */
+#define M_SN 0x3a /* integer */
+#define MEDIA 0x3e /* integer */
+#define HE155MM 0x26
+#define HE622MM 0x27
+#define HE155SM 0x46
+#define HE622SM 0x47
+#define MAC_ADDR 0x42 /* char[] */
+
+#define CS_LOW 0x0
+#define CS_HIGH ID_CS /* HOST_CNTL_ID_PROM_SEL */
+#define CLK_LOW 0x0
+#define CLK_HIGH ID_CLOCK /* HOST_CNTL_ID_PROM_CLOCK */
+#define SI_HIGH ID_DIN /* HOST_CNTL_ID_PROM_DATA_IN */
+#define EEPROM_DELAY 400 /* microseconds */
+
+#endif /* _HE_H_ */
diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c
new file mode 100644
index 000000000..527bbd595
--- /dev/null
+++ b/drivers/atm/horizon.c
@@ -0,0 +1,2914 @@
+/*
+ Madge Horizon ATM Adapter driver.
+ Copyright (C) 1995-1999 Madge Networks Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian
+ system and in the file COPYING in the Linux kernel source.
+*/
+
+/*
+ IMPORTANT NOTE: Madge Networks no longer makes the adapters
+ supported by this driver and makes no commitment to maintain it.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/skbuff.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/uio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <linux/atomic.h>
+#include <asm/uaccess.h>
+#include <asm/string.h>
+#include <asm/byteorder.h>
+
+#include "horizon.h"
+
+#define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"
+#define description_string "Madge ATM Horizon [Ultra] driver"
+#define version_string "1.2.1"
+
+static inline void __init show_version (void) {
+ printk ("%s version %s\n", description_string, version_string);
+}
+
+/*
+
+ CREDITS
+
+ Driver and documentation by:
+
+ Chris Aston Madge Networks
+ Giuliano Procida Madge Networks
+ Simon Benham Madge Networks
+ Simon Johnson Madge Networks
+ Various Others Madge Networks
+
+ Some inspiration taken from other drivers by:
+
+ Alexandru Cucos UTBv
+ Kari Mettinen University of Helsinki
+ Werner Almesberger EPFL LRC
+
+ Theory of Operation
+
+ I Hardware, detection, initialisation and shutdown.
+
+ 1. Supported Hardware
+
+ This driver should handle all variants of the PCI Madge ATM adapters
+ with the Horizon chipset. These are all PCI cards supporting PIO, BM
+ DMA and a form of MMIO (registers only, not internal RAM).
+
+ The driver is only known to work with SONET and UTP Horizon Ultra
+ cards at 155Mb/s. However, code is in place to deal with both the
+ original Horizon and 25Mb/s operation.
+
+ There are two revisions of the Horizon ASIC: the original and the
+ Ultra. Details of hardware bugs are in section III.
+
+ The ASIC version can be distinguished by chip markings but is NOT
+ indicated by the PCI revision (all adapters seem to have PCI rev 1).
+
+ I believe that:
+
+ Horizon => Collage 25 PCI Adapter (UTP and STP)
+ Horizon Ultra => Collage 155 PCI Client (UTP or SONET)
+ Ambassador x => Collage 155 PCI Server (completely different)
+
+ Horizon (25Mb/s) is fitted with UTP and STP connectors. It seems to
+ have a Madge B154 plus glue logic serializer. I have also found a
+ really ancient version of this with slightly different glue. It
+ comes with the revision 0 (140-025-01) ASIC.
+
+ Horizon Ultra (155Mb/s) is fitted with either a Pulse Medialink
+ output (UTP) or an HP HFBR 5205 output (SONET). It has either
+ Madge's SAMBA framer or a SUNI-lite device (early versions). It
+ comes with the revision 1 (140-027-01) ASIC.
+
+ 2. Detection
+
+ All Horizon-based cards present with the same PCI Vendor and Device
+ IDs. The standard Linux 2.2 PCI API is used to locate any cards and
+ to enable bus-mastering (with appropriate latency).
+
+ ATM_LAYER_STATUS in the control register distinguishes between the
+ two possible physical layers (25 and 155). It is not clear whether
+ the 155 cards can also operate at 25Mbps. We rely on the fact that a
+ card operates at 155 if and only if it has the newer Horizon Ultra
+ ASIC.
+
+ For 155 cards the two possible framers are probed for and then set
+ up for loop-timing.
+
+ 3. Initialisation
+
+ The card is reset and then put into a known state. The physical
+ layer is configured for normal operation at the appropriate speed;
+ in the case of the 155 cards, the framer is initialised with
+ line-based timing; the internal RAM is zeroed and the allocation of
+ buffers for RX and TX is made; the Burnt In Address is read and
+ copied to the ATM ESI; various policy settings for RX (VPI bits,
+ unknown VCs, oam cells) are made. Ideally all policy items should be
+ configurable at module load (if not actually on-demand), however,
+ only the vpi vs vci bit allocation can be specified at insmod.
+
+ 4. Shutdown
+
+ This is in response to module_cleaup. No VCs are in use and the card
+ should be idle; it is reset.
+
+ II Driver software (as it should be)
+
+ 0. Traffic Parameters
+
+ The traffic classes (not an enumeration) are currently: ATM_NONE (no
+ traffic), ATM_UBR, ATM_CBR, ATM_VBR and ATM_ABR, ATM_ANYCLASS
+ (compatible with everything). Together with (perhaps only some of)
+ the following items they make up the traffic specification.
+
+ struct atm_trafprm {
+ unsigned char traffic_class; traffic class (ATM_UBR, ...)
+ int max_pcr; maximum PCR in cells per second
+ int pcr; desired PCR in cells per second
+ int min_pcr; minimum PCR in cells per second
+ int max_cdv; maximum CDV in microseconds
+ int max_sdu; maximum SDU in bytes
+ };
+
+ Note that these denote bandwidth available not bandwidth used; the
+ possibilities according to ATMF are:
+
+ Real Time (cdv and max CDT given)
+
+ CBR(pcr) pcr bandwidth always available
+ rtVBR(pcr,scr,mbs) scr bandwidth always available, up to pcr at mbs too
+
+ Non Real Time
+
+ nrtVBR(pcr,scr,mbs) scr bandwidth always available, up to pcr at mbs too
+ UBR()
+ ABR(mcr,pcr) mcr bandwidth always available, up to pcr (depending) too
+
+ mbs is max burst size (bucket)
+ pcr and scr have associated cdvt values
+ mcr is like scr but has no cdtv
+ cdtv may differ at each hop
+
+ Some of the above items are qos items (as opposed to traffic
+ parameters). We have nothing to do with qos. All except ABR can have
+ their traffic parameters converted to GCRA parameters. The GCRA may
+ be implemented as a (real-number) leaky bucket. The GCRA can be used
+ in complicated ways by switches and in simpler ways by end-stations.
+ It can be used both to filter incoming cells and shape out-going
+ cells.
+
+ ATM Linux actually supports:
+
+ ATM_NONE() (no traffic in this direction)
+ ATM_UBR(max_frame_size)
+ ATM_CBR(max/min_pcr, max_cdv, max_frame_size)
+
+ 0 or ATM_MAX_PCR are used to indicate maximum available PCR
+
+ A traffic specification consists of the AAL type and separate
+ traffic specifications for either direction. In ATM Linux it is:
+
+ struct atm_qos {
+ struct atm_trafprm txtp;
+ struct atm_trafprm rxtp;
+ unsigned char aal;
+ };
+
+ AAL types are:
+
+ ATM_NO_AAL AAL not specified
+ ATM_AAL0 "raw" ATM cells
+ ATM_AAL1 AAL1 (CBR)
+ ATM_AAL2 AAL2 (VBR)
+ ATM_AAL34 AAL3/4 (data)
+ ATM_AAL5 AAL5 (data)
+ ATM_SAAL signaling AAL
+
+ The Horizon has support for AAL frame types: 0, 3/4 and 5. However,
+ it does not implement AAL 3/4 SAR and it has a different notion of
+ "raw cell" to ATM Linux's (48 bytes vs. 52 bytes) so neither are
+ supported by this driver.
+
+ The Horizon has limited support for ABR (including UBR), VBR and
+ CBR. Each TX channel has a bucket (containing up to 31 cell units)
+ and two timers (PCR and SCR) associated with it that can be used to
+ govern cell emissions and host notification (in the case of ABR this
+ is presumably so that RM cells may be emitted at appropriate times).
+ The timers may either be disabled or may be set to any of 240 values
+ (determined by the clock crystal, a fixed (?) per-device divider, a
+ configurable divider and a configurable timer preload value).
+
+ At the moment only UBR and CBR are supported by the driver. VBR will
+ be supported as soon as ATM for Linux supports it. ABR support is
+ very unlikely as RM cell handling is completely up to the driver.
+
+ 1. TX (TX channel setup and TX transfer)
+
+ The TX half of the driver owns the TX Horizon registers. The TX
+ component in the IRQ handler is the BM completion handler. This can
+ only be entered when tx_busy is true (enforced by hardware). The
+ other TX component can only be entered when tx_busy is false
+ (enforced by driver). So TX is single-threaded.
+
+ Apart from a minor optimisation to not re-select the last channel,
+ the TX send component works as follows:
+
+ Atomic test and set tx_busy until we succeed; we should implement
+ some sort of timeout so that tx_busy will never be stuck at true.
+
+ If no TX channel is set up for this VC we wait for an idle one (if
+ necessary) and set it up.
+
+ At this point we have a TX channel ready for use. We wait for enough
+ buffers to become available then start a TX transmit (set the TX
+ descriptor, schedule transfer, exit).
+
+ The IRQ component handles TX completion (stats, free buffer, tx_busy
+ unset, exit). We also re-schedule further transfers for the same
+ frame if needed.
+
+ TX setup in more detail:
+
+ TX open is a nop, the relevant information is held in the hrz_vcc
+ (vcc->dev_data) structure and is "cached" on the card.
+
+ TX close gets the TX lock and clears the channel from the "cache".
+
+ 2. RX (Data Available and RX transfer)
+
+ The RX half of the driver owns the RX registers. There are two RX
+ components in the IRQ handler: the data available handler deals with
+ fresh data that has arrived on the card, the BM completion handler
+ is very similar to the TX completion handler. The data available
+ handler grabs the rx_lock and it is only released once the data has
+ been discarded or completely transferred to the host. The BM
+ completion handler only runs when the lock is held; the data
+ available handler is locked out over the same period.
+
+ Data available on the card triggers an interrupt. If the data is not
+ suitable for our existing RX channels or we cannot allocate a buffer
+ it is flushed. Otherwise an RX receive is scheduled. Multiple RX
+ transfers may be scheduled for the same frame.
+
+ RX setup in more detail:
+
+ RX open...
+ RX close...
+
+ III Hardware Bugs
+
+ 0. Byte vs Word addressing of adapter RAM.
+
+ A design feature; see the .h file (especially the memory map).
+
+ 1. Bus Master Data Transfers (original Horizon only, fixed in Ultra)
+
+ The host must not start a transmit direction transfer at a
+ non-four-byte boundary in host memory. Instead the host should
+ perform a byte, or a two byte, or one byte followed by two byte
+ transfer in order to start the rest of the transfer on a four byte
+ boundary. RX is OK.
+
+ Simultaneous transmit and receive direction bus master transfers are
+ not allowed.
+
+ The simplest solution to these two is to always do PIO (never DMA)
+ in the TX direction on the original Horizon. More complicated
+ solutions are likely to hurt my brain.
+
+ 2. Loss of buffer on close VC
+
+ When a VC is being closed, the buffer associated with it is not
+ returned to the pool. The host must store the reference to this
+ buffer and when opening a new VC then give it to that new VC.
+
+ The host intervention currently consists of stacking such a buffer
+ pointer at VC close and checking the stack at VC open.
+
+ 3. Failure to close a VC
+
+ If a VC is currently receiving a frame then closing the VC may fail
+ and the frame continues to be received.
+
+ The solution is to make sure any received frames are flushed when
+ ready. This is currently done just before the solution to 2.
+
+ 4. PCI bus (original Horizon only, fixed in Ultra)
+
+ Reading from the data port prior to initialisation will hang the PCI
+ bus. Just don't do that then! We don't.
+
+ IV To Do List
+
+ . Timer code may be broken.
+
+ . Allow users to specify buffer allocation split for TX and RX.
+
+ . Deal once and for all with buggy VC close.
+
+ . Handle interrupted and/or non-blocking operations.
+
+ . Change some macros to functions and move from .h to .c.
+
+ . Try to limit the number of TX frames each VC may have queued, in
+ order to reduce the chances of TX buffer exhaustion.
+
+ . Implement VBR (bucket and timers not understood) and ABR (need to
+ do RM cells manually); also no Linux support for either.
+
+ . Implement QoS changes on open VCs (involves extracting parts of VC open
+ and close into separate functions and using them to make changes).
+
+*/
+
+/********** globals **********/
+
+static void do_housekeeping (unsigned long arg);
+
+static unsigned short debug = 0;
+static unsigned short vpi_bits = 0;
+static int max_tx_size = 9000;
+static int max_rx_size = 9000;
+static unsigned char pci_lat = 0;
+
+/********** access functions **********/
+
+/* Read / Write Horizon registers */
+static inline void wr_regl (const hrz_dev * dev, unsigned char reg, u32 data) {
+ outl (cpu_to_le32 (data), dev->iobase + reg);
+}
+
+static inline u32 rd_regl (const hrz_dev * dev, unsigned char reg) {
+ return le32_to_cpu (inl (dev->iobase + reg));
+}
+
+static inline void wr_regw (const hrz_dev * dev, unsigned char reg, u16 data) {
+ outw (cpu_to_le16 (data), dev->iobase + reg);
+}
+
+static inline u16 rd_regw (const hrz_dev * dev, unsigned char reg) {
+ return le16_to_cpu (inw (dev->iobase + reg));
+}
+
+static inline void wrs_regb (const hrz_dev * dev, unsigned char reg, void * addr, u32 len) {
+ outsb (dev->iobase + reg, addr, len);
+}
+
+static inline void rds_regb (const hrz_dev * dev, unsigned char reg, void * addr, u32 len) {
+ insb (dev->iobase + reg, addr, len);
+}
+
+/* Read / Write to a given address in Horizon buffer memory.
+ Interrupts must be disabled between the address register and data
+ port accesses as these must form an atomic operation. */
+static inline void wr_mem (const hrz_dev * dev, HDW * addr, u32 data) {
+ // wr_regl (dev, MEM_WR_ADDR_REG_OFF, (u32) addr);
+ wr_regl (dev, MEM_WR_ADDR_REG_OFF, (addr - (HDW *) 0) * sizeof(HDW));
+ wr_regl (dev, MEMORY_PORT_OFF, data);
+}
+
+static inline u32 rd_mem (const hrz_dev * dev, HDW * addr) {
+ // wr_regl (dev, MEM_RD_ADDR_REG_OFF, (u32) addr);
+ wr_regl (dev, MEM_RD_ADDR_REG_OFF, (addr - (HDW *) 0) * sizeof(HDW));
+ return rd_regl (dev, MEMORY_PORT_OFF);
+}
+
+static inline void wr_framer (const hrz_dev * dev, u32 addr, u32 data) {
+ wr_regl (dev, MEM_WR_ADDR_REG_OFF, (u32) addr | 0x80000000);
+ wr_regl (dev, MEMORY_PORT_OFF, data);
+}
+
+static inline u32 rd_framer (const hrz_dev * dev, u32 addr) {
+ wr_regl (dev, MEM_RD_ADDR_REG_OFF, (u32) addr | 0x80000000);
+ return rd_regl (dev, MEMORY_PORT_OFF);
+}
+
+/********** specialised access functions **********/
+
+/* RX */
+
+static inline void FLUSH_RX_CHANNEL (hrz_dev * dev, u16 channel) {
+ wr_regw (dev, RX_CHANNEL_PORT_OFF, FLUSH_CHANNEL | channel);
+ return;
+}
+
+static void WAIT_FLUSH_RX_COMPLETE (hrz_dev * dev) {
+ while (rd_regw (dev, RX_CHANNEL_PORT_OFF) & FLUSH_CHANNEL)
+ ;
+ return;
+}
+
+static inline void SELECT_RX_CHANNEL (hrz_dev * dev, u16 channel) {
+ wr_regw (dev, RX_CHANNEL_PORT_OFF, channel);
+ return;
+}
+
+static void WAIT_UPDATE_COMPLETE (hrz_dev * dev) {
+ while (rd_regw (dev, RX_CHANNEL_PORT_OFF) & RX_CHANNEL_UPDATE_IN_PROGRESS)
+ ;
+ return;
+}
+
+/* TX */
+
+static inline void SELECT_TX_CHANNEL (hrz_dev * dev, u16 tx_channel) {
+ wr_regl (dev, TX_CHANNEL_PORT_OFF, tx_channel);
+ return;
+}
+
+/* Update or query one configuration parameter of a particular channel. */
+
+static inline void update_tx_channel_config (hrz_dev * dev, short chan, u8 mode, u16 value) {
+ wr_regw (dev, TX_CHANNEL_CONFIG_COMMAND_OFF,
+ chan * TX_CHANNEL_CONFIG_MULT | mode);
+ wr_regw (dev, TX_CHANNEL_CONFIG_DATA_OFF, value);
+ return;
+}
+
+/********** dump functions **********/
+
+static inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) {
+#ifdef DEBUG_HORIZON
+ unsigned int i;
+ unsigned char * data = skb->data;
+ PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc);
+ for (i=0; i<skb->len && i < 256;i++)
+ PRINTDM (DBG_DATA, "%02x ", data[i]);
+ PRINTDE (DBG_DATA,"");
+#else
+ (void) prefix;
+ (void) vc;
+ (void) skb;
+#endif
+ return;
+}
+
+static inline void dump_regs (hrz_dev * dev) {
+#ifdef DEBUG_HORIZON
+ PRINTD (DBG_REGS, "CONTROL 0: %#x", rd_regl (dev, CONTROL_0_REG));
+ PRINTD (DBG_REGS, "RX CONFIG: %#x", rd_regw (dev, RX_CONFIG_OFF));
+ PRINTD (DBG_REGS, "TX CONFIG: %#x", rd_regw (dev, TX_CONFIG_OFF));
+ PRINTD (DBG_REGS, "TX STATUS: %#x", rd_regw (dev, TX_STATUS_OFF));
+ PRINTD (DBG_REGS, "IRQ ENBLE: %#x", rd_regl (dev, INT_ENABLE_REG_OFF));
+ PRINTD (DBG_REGS, "IRQ SORCE: %#x", rd_regl (dev, INT_SOURCE_REG_OFF));
+#else
+ (void) dev;
+#endif
+ return;
+}
+
+static inline void dump_framer (hrz_dev * dev) {
+#ifdef DEBUG_HORIZON
+ unsigned int i;
+ PRINTDB (DBG_REGS, "framer registers:");
+ for (i = 0; i < 0x10; ++i)
+ PRINTDM (DBG_REGS, " %02x", rd_framer (dev, i));
+ PRINTDE (DBG_REGS,"");
+#else
+ (void) dev;
+#endif
+ return;
+}
+
+/********** VPI/VCI <-> (RX) channel conversions **********/
+
+/* RX channels are 10 bit integers, these fns are quite paranoid */
+
+static inline int vpivci_to_channel (u16 * channel, const short vpi, const int vci) {
+ unsigned short vci_bits = 10 - vpi_bits;
+ if (0 <= vpi && vpi < 1<<vpi_bits && 0 <= vci && vci < 1<<vci_bits) {
+ *channel = vpi<<vci_bits | vci;
+ return *channel ? 0 : -EINVAL;
+ }
+ return -EINVAL;
+}
+
+/********** decode RX queue entries **********/
+
+static inline u16 rx_q_entry_to_length (u32 x) {
+ return x & RX_Q_ENTRY_LENGTH_MASK;
+}
+
+static inline u16 rx_q_entry_to_rx_channel (u32 x) {
+ return (x>>RX_Q_ENTRY_CHANNEL_SHIFT) & RX_CHANNEL_MASK;
+}
+
+/* Cell Transmit Rate Values
+ *
+ * the cell transmit rate (cells per sec) can be set to a variety of
+ * different values by specifying two parameters: a timer preload from
+ * 1 to 16 (stored as 0 to 15) and a clock divider (2 to the power of
+ * an exponent from 0 to 14; the special value 15 disables the timer).
+ *
+ * cellrate = baserate / (preload * 2^divider)
+ *
+ * The maximum cell rate that can be specified is therefore just the
+ * base rate. Halving the preload is equivalent to adding 1 to the
+ * divider and so values 1 to 8 of the preload are redundant except
+ * in the case of a maximal divider (14).
+ *
+ * Given a desired cell rate, an algorithm to determine the preload
+ * and divider is:
+ *
+ * a) x = baserate / cellrate, want p * 2^d = x (as far as possible)
+ * b) if x > 16 * 2^14 then set p = 16, d = 14 (min rate), done
+ * if x <= 16 then set p = x, d = 0 (high rates), done
+ * c) now have 16 < x <= 2^18, or 1 < x/16 <= 2^14 and we want to
+ * know n such that 2^(n-1) < x/16 <= 2^n, so slide a bit until
+ * we find the range (n will be between 1 and 14), set d = n
+ * d) Also have 8 < x/2^n <= 16, so set p nearest x/2^n
+ *
+ * The algorithm used below is a minor variant of the above.
+ *
+ * The base rate is derived from the oscillator frequency (Hz) using a
+ * fixed divider:
+ *
+ * baserate = freq / 32 in the case of some Unknown Card
+ * baserate = freq / 8 in the case of the Horizon 25
+ * baserate = freq / 8 in the case of the Horizon Ultra 155
+ *
+ * The Horizon cards have oscillators and base rates as follows:
+ *
+ * Card Oscillator Base Rate
+ * Unknown Card 33 MHz 1.03125 MHz (33 MHz = PCI freq)
+ * Horizon 25 32 MHz 4 MHz
+ * Horizon Ultra 155 40 MHz 5 MHz
+ *
+ * The following defines give the base rates in Hz. These were
+ * previously a factor of 100 larger, no doubt someone was using
+ * cps*100.
+ */
+
+#define BR_UKN 1031250l
+#define BR_HRZ 4000000l
+#define BR_ULT 5000000l
+
+// d is an exponent
+#define CR_MIND 0
+#define CR_MAXD 14
+
+// p ranges from 1 to a power of 2
+#define CR_MAXPEXP 4
+
+static int make_rate (const hrz_dev * dev, u32 c, rounding r,
+ u16 * bits, unsigned int * actual)
+{
+ // note: rounding the rate down means rounding 'p' up
+ const unsigned long br = test_bit(ultra, &dev->flags) ? BR_ULT : BR_HRZ;
+
+ u32 div = CR_MIND;
+ u32 pre;
+
+ // br_exp and br_man are used to avoid overflowing (c*maxp*2^d) in
+ // the tests below. We could think harder about exact possibilities
+ // of failure...
+
+ unsigned long br_man = br;
+ unsigned int br_exp = 0;
+
+ PRINTD (DBG_QOS|DBG_FLOW, "make_rate b=%lu, c=%u, %s", br, c,
+ r == round_up ? "up" : r == round_down ? "down" : "nearest");
+
+ // avoid div by zero
+ if (!c) {
+ PRINTD (DBG_QOS|DBG_ERR, "zero rate is not allowed!");
+ return -EINVAL;
+ }
+
+ while (br_exp < CR_MAXPEXP + CR_MIND && (br_man % 2 == 0)) {
+ br_man = br_man >> 1;
+ ++br_exp;
+ }
+ // (br >>br_exp) <<br_exp == br and
+ // br_exp <= CR_MAXPEXP+CR_MIND
+
+ if (br_man <= (c << (CR_MAXPEXP+CR_MIND-br_exp))) {
+ // Equivalent to: B <= (c << (MAXPEXP+MIND))
+ // take care of rounding
+ switch (r) {
+ case round_down:
+ pre = DIV_ROUND_UP(br, c<<div);
+ // but p must be non-zero
+ if (!pre)
+ pre = 1;
+ break;
+ case round_nearest:
+ pre = DIV_ROUND_CLOSEST(br, c<<div);
+ // but p must be non-zero
+ if (!pre)
+ pre = 1;
+ break;
+ default: /* round_up */
+ pre = br/(c<<div);
+ // but p must be non-zero
+ if (!pre)
+ return -EINVAL;
+ }
+ PRINTD (DBG_QOS, "A: p=%u, d=%u", pre, div);
+ goto got_it;
+ }
+
+ // at this point we have
+ // d == MIND and (c << (MAXPEXP+MIND)) < B
+ while (div < CR_MAXD) {
+ div++;
+ if (br_man <= (c << (CR_MAXPEXP+div-br_exp))) {
+ // Equivalent to: B <= (c << (MAXPEXP+d))
+ // c << (MAXPEXP+d-1) < B <= c << (MAXPEXP+d)
+ // 1 << (MAXPEXP-1) < B/2^d/c <= 1 << MAXPEXP
+ // MAXP/2 < B/c2^d <= MAXP
+ // take care of rounding
+ switch (r) {
+ case round_down:
+ pre = DIV_ROUND_UP(br, c<<div);
+ break;
+ case round_nearest:
+ pre = DIV_ROUND_CLOSEST(br, c<<div);
+ break;
+ default: /* round_up */
+ pre = br/(c<<div);
+ }
+ PRINTD (DBG_QOS, "B: p=%u, d=%u", pre, div);
+ goto got_it;
+ }
+ }
+ // at this point we have
+ // d == MAXD and (c << (MAXPEXP+MAXD)) < B
+ // but we cannot go any higher
+ // take care of rounding
+ if (r == round_down)
+ return -EINVAL;
+ pre = 1 << CR_MAXPEXP;
+ PRINTD (DBG_QOS, "C: p=%u, d=%u", pre, div);
+got_it:
+ // paranoia
+ if (div > CR_MAXD || (!pre) || pre > 1<<CR_MAXPEXP) {
+ PRINTD (DBG_QOS, "set_cr internal failure: d=%u p=%u",
+ div, pre);
+ return -EINVAL;
+ } else {
+ if (bits)
+ *bits = (div<<CLOCK_SELECT_SHIFT) | (pre-1);
+ if (actual) {
+ *actual = DIV_ROUND_UP(br, pre<<div);
+ PRINTD (DBG_QOS, "actual rate: %u", *actual);
+ }
+ return 0;
+ }
+}
+
+static int make_rate_with_tolerance (const hrz_dev * dev, u32 c, rounding r, unsigned int tol,
+ u16 * bit_pattern, unsigned int * actual) {
+ unsigned int my_actual;
+
+ PRINTD (DBG_QOS|DBG_FLOW, "make_rate_with_tolerance c=%u, %s, tol=%u",
+ c, (r == round_up) ? "up" : (r == round_down) ? "down" : "nearest", tol);
+
+ if (!actual)
+ // actual rate is not returned
+ actual = &my_actual;
+
+ if (make_rate (dev, c, round_nearest, bit_pattern, actual))
+ // should never happen as round_nearest always succeeds
+ return -1;
+
+ if (c - tol <= *actual && *actual <= c + tol)
+ // within tolerance
+ return 0;
+ else
+ // intolerant, try rounding instead
+ return make_rate (dev, c, r, bit_pattern, actual);
+}
+
+/********** Listen on a VC **********/
+
+static int hrz_open_rx (hrz_dev * dev, u16 channel) {
+ // is there any guarantee that we don't get two simulataneous
+ // identical calls of this function from different processes? yes
+ // rate_lock
+ unsigned long flags;
+ u32 channel_type; // u16?
+
+ u16 buf_ptr = RX_CHANNEL_IDLE;
+
+ rx_ch_desc * rx_desc = &memmap->rx_descs[channel];
+
+ PRINTD (DBG_FLOW, "hrz_open_rx %x", channel);
+
+ spin_lock_irqsave (&dev->mem_lock, flags);
+ channel_type = rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK;
+ spin_unlock_irqrestore (&dev->mem_lock, flags);
+
+ // very serious error, should never occur
+ if (channel_type != RX_CHANNEL_DISABLED) {
+ PRINTD (DBG_ERR|DBG_VCC, "RX channel for VC already open");
+ return -EBUSY; // clean up?
+ }
+
+ // Give back spare buffer
+ if (dev->noof_spare_buffers) {
+ buf_ptr = dev->spare_buffers[--dev->noof_spare_buffers];
+ PRINTD (DBG_VCC, "using a spare buffer: %u", buf_ptr);
+ // should never occur
+ if (buf_ptr == RX_CHANNEL_DISABLED || buf_ptr == RX_CHANNEL_IDLE) {
+ // but easy to recover from
+ PRINTD (DBG_ERR|DBG_VCC, "bad spare buffer pointer, using IDLE");
+ buf_ptr = RX_CHANNEL_IDLE;
+ }
+ } else {
+ PRINTD (DBG_VCC, "using IDLE buffer pointer");
+ }
+
+ // Channel is currently disabled so change its status to idle
+
+ // do we really need to save the flags again?
+ spin_lock_irqsave (&dev->mem_lock, flags);
+
+ wr_mem (dev, &rx_desc->wr_buf_type,
+ buf_ptr | CHANNEL_TYPE_AAL5 | FIRST_CELL_OF_AAL5_FRAME);
+ if (buf_ptr != RX_CHANNEL_IDLE)
+ wr_mem (dev, &rx_desc->rd_buf_type, buf_ptr);
+
+ spin_unlock_irqrestore (&dev->mem_lock, flags);
+
+ // rxer->rate = make_rate (qos->peak_cells);
+
+ PRINTD (DBG_FLOW, "hrz_open_rx ok");
+
+ return 0;
+}
+
+#if 0
+/********** change vc rate for a given vc **********/
+
+static void hrz_change_vc_qos (ATM_RXER * rxer, MAAL_QOS * qos) {
+ rxer->rate = make_rate (qos->peak_cells);
+}
+#endif
+
+/********** free an skb (as per ATM device driver documentation) **********/
+
+static void hrz_kfree_skb (struct sk_buff * skb) {
+ if (ATM_SKB(skb)->vcc->pop) {
+ ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
+ } else {
+ dev_kfree_skb_any (skb);
+ }
+}
+
+/********** cancel listen on a VC **********/
+
+static void hrz_close_rx (hrz_dev * dev, u16 vc) {
+ unsigned long flags;
+
+ u32 value;
+
+ u32 r1, r2;
+
+ rx_ch_desc * rx_desc = &memmap->rx_descs[vc];
+
+ int was_idle = 0;
+
+ spin_lock_irqsave (&dev->mem_lock, flags);
+ value = rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK;
+ spin_unlock_irqrestore (&dev->mem_lock, flags);
+
+ if (value == RX_CHANNEL_DISABLED) {
+ // I suppose this could happen once we deal with _NONE traffic properly
+ PRINTD (DBG_VCC, "closing VC: RX channel %u already disabled", vc);
+ return;
+ }
+ if (value == RX_CHANNEL_IDLE)
+ was_idle = 1;
+
+ spin_lock_irqsave (&dev->mem_lock, flags);
+
+ for (;;) {
+ wr_mem (dev, &rx_desc->wr_buf_type, RX_CHANNEL_DISABLED);
+
+ if ((rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK) == RX_CHANNEL_DISABLED)
+ break;
+
+ was_idle = 0;
+ }
+
+ if (was_idle) {
+ spin_unlock_irqrestore (&dev->mem_lock, flags);
+ return;
+ }
+
+ WAIT_FLUSH_RX_COMPLETE(dev);
+
+ // XXX Is this all really necessary? We can rely on the rx_data_av
+ // handler to discard frames that remain queued for delivery. If the
+ // worry is that immediately reopening the channel (perhaps by a
+ // different process) may cause some data to be mis-delivered then
+ // there may still be a simpler solution (such as busy-waiting on
+ // rx_busy once the channel is disabled or before a new one is
+ // opened - does this leave any holes?). Arguably setting up and
+ // tearing down the TX and RX halves of each virtual circuit could
+ // most safely be done within ?x_busy protected regions.
+
+ // OK, current changes are that Simon's marker is disabled and we DO
+ // look for NULL rxer elsewhere. The code here seems flush frames
+ // and then remember the last dead cell belonging to the channel
+ // just disabled - the cell gets relinked at the next vc_open.
+ // However, when all VCs are closed or only a few opened there are a
+ // handful of buffers that are unusable.
+
+ // Does anyone feel like documenting spare_buffers properly?
+ // Does anyone feel like fixing this in a nicer way?
+
+ // Flush any data which is left in the channel
+ for (;;) {
+ // Change the rx channel port to something different to the RX
+ // channel we are trying to close to force Horizon to flush the rx
+ // channel read and write pointers.
+
+ u16 other = vc^(RX_CHANS/2);
+
+ SELECT_RX_CHANNEL (dev, other);
+ WAIT_UPDATE_COMPLETE (dev);
+
+ r1 = rd_mem (dev, &rx_desc->rd_buf_type);
+
+ // Select this RX channel. Flush doesn't seem to work unless we
+ // select an RX channel before hand
+
+ SELECT_RX_CHANNEL (dev, vc);
+ WAIT_UPDATE_COMPLETE (dev);
+
+ // Attempt to flush a frame on this RX channel
+
+ FLUSH_RX_CHANNEL (dev, vc);
+ WAIT_FLUSH_RX_COMPLETE (dev);
+
+ // Force Horizon to flush rx channel read and write pointers as before
+
+ SELECT_RX_CHANNEL (dev, other);
+ WAIT_UPDATE_COMPLETE (dev);
+
+ r2 = rd_mem (dev, &rx_desc->rd_buf_type);
+
+ PRINTD (DBG_VCC|DBG_RX, "r1 = %u, r2 = %u", r1, r2);
+
+ if (r1 == r2) {
+ dev->spare_buffers[dev->noof_spare_buffers++] = (u16)r1;
+ break;
+ }
+ }
+
+#if 0
+ {
+ rx_q_entry * wr_ptr = &memmap->rx_q_entries[rd_regw (dev, RX_QUEUE_WR_PTR_OFF)];
+ rx_q_entry * rd_ptr = dev->rx_q_entry;
+
+ PRINTD (DBG_VCC|DBG_RX, "rd_ptr = %u, wr_ptr = %u", rd_ptr, wr_ptr);
+
+ while (rd_ptr != wr_ptr) {
+ u32 x = rd_mem (dev, (HDW *) rd_ptr);
+
+ if (vc == rx_q_entry_to_rx_channel (x)) {
+ x |= SIMONS_DODGEY_MARKER;
+
+ PRINTD (DBG_RX|DBG_VCC|DBG_WARN, "marking a frame as dodgey");
+
+ wr_mem (dev, (HDW *) rd_ptr, x);
+ }
+
+ if (rd_ptr == dev->rx_q_wrap)
+ rd_ptr = dev->rx_q_reset;
+ else
+ rd_ptr++;
+ }
+ }
+#endif
+
+ spin_unlock_irqrestore (&dev->mem_lock, flags);
+
+ return;
+}
+
+/********** schedule RX transfers **********/
+
+// Note on tail recursion: a GCC developer said that it is not likely
+// to be fixed soon, so do not define TAILRECUSRIONWORKS unless you
+// are sure it does as you may otherwise overflow the kernel stack.
+
+// giving this fn a return value would help GCC, allegedly
+
+static void rx_schedule (hrz_dev * dev, int irq) {
+ unsigned int rx_bytes;
+
+ int pio_instead = 0;
+#ifndef TAILRECURSIONWORKS
+ pio_instead = 1;
+ while (pio_instead) {
+#endif
+ // bytes waiting for RX transfer
+ rx_bytes = dev->rx_bytes;
+
+#if 0
+ spin_count = 0;
+ while (rd_regl (dev, MASTER_RX_COUNT_REG_OFF)) {
+ PRINTD (DBG_RX|DBG_WARN, "RX error: other PCI Bus Master RX still in progress!");
+ if (++spin_count > 10) {
+ PRINTD (DBG_RX|DBG_ERR, "spun out waiting PCI Bus Master RX completion");
+ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0);
+ clear_bit (rx_busy, &dev->flags);
+ hrz_kfree_skb (dev->rx_skb);
+ return;
+ }
+ }
+#endif
+
+ // this code follows the TX code but (at the moment) there is only
+ // one region - the skb itself. I don't know if this will change,
+ // but it doesn't hurt to have the code here, disabled.
+
+ if (rx_bytes) {
+ // start next transfer within same region
+ if (rx_bytes <= MAX_PIO_COUNT) {
+ PRINTD (DBG_RX|DBG_BUS, "(pio)");
+ pio_instead = 1;
+ }
+ if (rx_bytes <= MAX_TRANSFER_COUNT) {
+ PRINTD (DBG_RX|DBG_BUS, "(simple or last multi)");
+ dev->rx_bytes = 0;
+ } else {
+ PRINTD (DBG_RX|DBG_BUS, "(continuing multi)");
+ dev->rx_bytes = rx_bytes - MAX_TRANSFER_COUNT;
+ rx_bytes = MAX_TRANSFER_COUNT;
+ }
+ } else {
+ // rx_bytes == 0 -- we're between regions
+ // regions remaining to transfer
+#if 0
+ unsigned int rx_regions = dev->rx_regions;
+#else
+ unsigned int rx_regions = 0;
+#endif
+
+ if (rx_regions) {
+#if 0
+ // start a new region
+ dev->rx_addr = dev->rx_iovec->iov_base;
+ rx_bytes = dev->rx_iovec->iov_len;
+ ++dev->rx_iovec;
+ dev->rx_regions = rx_regions - 1;
+
+ if (rx_bytes <= MAX_PIO_COUNT) {
+ PRINTD (DBG_RX|DBG_BUS, "(pio)");
+ pio_instead = 1;
+ }
+ if (rx_bytes <= MAX_TRANSFER_COUNT) {
+ PRINTD (DBG_RX|DBG_BUS, "(full region)");
+ dev->rx_bytes = 0;
+ } else {
+ PRINTD (DBG_RX|DBG_BUS, "(start multi region)");
+ dev->rx_bytes = rx_bytes - MAX_TRANSFER_COUNT;
+ rx_bytes = MAX_TRANSFER_COUNT;
+ }
+#endif
+ } else {
+ // rx_regions == 0
+ // that's all folks - end of frame
+ struct sk_buff * skb = dev->rx_skb;
+ // dev->rx_iovec = 0;
+
+ FLUSH_RX_CHANNEL (dev, dev->rx_channel);
+
+ dump_skb ("<<<", dev->rx_channel, skb);
+
+ PRINTD (DBG_RX|DBG_SKB, "push %p %u", skb->data, skb->len);
+
+ {
+ struct atm_vcc * vcc = ATM_SKB(skb)->vcc;
+ // VC layer stats
+ atomic_inc(&vcc->stats->rx);
+ __net_timestamp(skb);
+ // end of our responsibility
+ vcc->push (vcc, skb);
+ }
+ }
+ }
+
+ // note: writing RX_COUNT clears any interrupt condition
+ if (rx_bytes) {
+ if (pio_instead) {
+ if (irq)
+ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0);
+ rds_regb (dev, DATA_PORT_OFF, dev->rx_addr, rx_bytes);
+ } else {
+ wr_regl (dev, MASTER_RX_ADDR_REG_OFF, virt_to_bus (dev->rx_addr));
+ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, rx_bytes);
+ }
+ dev->rx_addr += rx_bytes;
+ } else {
+ if (irq)
+ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0);
+ // allow another RX thread to start
+ YELLOW_LED_ON(dev);
+ clear_bit (rx_busy, &dev->flags);
+ PRINTD (DBG_RX, "cleared rx_busy for dev %p", dev);
+ }
+
+#ifdef TAILRECURSIONWORKS
+ // and we all bless optimised tail calls
+ if (pio_instead)
+ return rx_schedule (dev, 0);
+ return;
+#else
+ // grrrrrrr!
+ irq = 0;
+ }
+ return;
+#endif
+}
+
+/********** handle RX bus master complete events **********/
+
+static void rx_bus_master_complete_handler (hrz_dev * dev) {
+ if (test_bit (rx_busy, &dev->flags)) {
+ rx_schedule (dev, 1);
+ } else {
+ PRINTD (DBG_RX|DBG_ERR, "unexpected RX bus master completion");
+ // clear interrupt condition on adapter
+ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0);
+ }
+ return;
+}
+
+/********** (queue to) become the next TX thread **********/
+
+static int tx_hold (hrz_dev * dev) {
+ PRINTD (DBG_TX, "sleeping at tx lock %p %lu", dev, dev->flags);
+ wait_event_interruptible(dev->tx_queue, (!test_and_set_bit(tx_busy, &dev->flags)));
+ PRINTD (DBG_TX, "woken at tx lock %p %lu", dev, dev->flags);
+ if (signal_pending (current))
+ return -1;
+ PRINTD (DBG_TX, "set tx_busy for dev %p", dev);
+ return 0;
+}
+
+/********** allow another TX thread to start **********/
+
+static inline void tx_release (hrz_dev * dev) {
+ clear_bit (tx_busy, &dev->flags);
+ PRINTD (DBG_TX, "cleared tx_busy for dev %p", dev);
+ wake_up_interruptible (&dev->tx_queue);
+}
+
+/********** schedule TX transfers **********/
+
+static void tx_schedule (hrz_dev * const dev, int irq) {
+ unsigned int tx_bytes;
+
+ int append_desc = 0;
+
+ int pio_instead = 0;
+#ifndef TAILRECURSIONWORKS
+ pio_instead = 1;
+ while (pio_instead) {
+#endif
+ // bytes in current region waiting for TX transfer
+ tx_bytes = dev->tx_bytes;
+
+#if 0
+ spin_count = 0;
+ while (rd_regl (dev, MASTER_TX_COUNT_REG_OFF)) {
+ PRINTD (DBG_TX|DBG_WARN, "TX error: other PCI Bus Master TX still in progress!");
+ if (++spin_count > 10) {
+ PRINTD (DBG_TX|DBG_ERR, "spun out waiting PCI Bus Master TX completion");
+ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0);
+ tx_release (dev);
+ hrz_kfree_skb (dev->tx_skb);
+ return;
+ }
+ }
+#endif
+
+ if (tx_bytes) {
+ // start next transfer within same region
+ if (!test_bit (ultra, &dev->flags) || tx_bytes <= MAX_PIO_COUNT) {
+ PRINTD (DBG_TX|DBG_BUS, "(pio)");
+ pio_instead = 1;
+ }
+ if (tx_bytes <= MAX_TRANSFER_COUNT) {
+ PRINTD (DBG_TX|DBG_BUS, "(simple or last multi)");
+ if (!dev->tx_iovec) {
+ // end of last region
+ append_desc = 1;
+ }
+ dev->tx_bytes = 0;
+ } else {
+ PRINTD (DBG_TX|DBG_BUS, "(continuing multi)");
+ dev->tx_bytes = tx_bytes - MAX_TRANSFER_COUNT;
+ tx_bytes = MAX_TRANSFER_COUNT;
+ }
+ } else {
+ // tx_bytes == 0 -- we're between regions
+ // regions remaining to transfer
+ unsigned int tx_regions = dev->tx_regions;
+
+ if (tx_regions) {
+ // start a new region
+ dev->tx_addr = dev->tx_iovec->iov_base;
+ tx_bytes = dev->tx_iovec->iov_len;
+ ++dev->tx_iovec;
+ dev->tx_regions = tx_regions - 1;
+
+ if (!test_bit (ultra, &dev->flags) || tx_bytes <= MAX_PIO_COUNT) {
+ PRINTD (DBG_TX|DBG_BUS, "(pio)");
+ pio_instead = 1;
+ }
+ if (tx_bytes <= MAX_TRANSFER_COUNT) {
+ PRINTD (DBG_TX|DBG_BUS, "(full region)");
+ dev->tx_bytes = 0;
+ } else {
+ PRINTD (DBG_TX|DBG_BUS, "(start multi region)");
+ dev->tx_bytes = tx_bytes - MAX_TRANSFER_COUNT;
+ tx_bytes = MAX_TRANSFER_COUNT;
+ }
+ } else {
+ // tx_regions == 0
+ // that's all folks - end of frame
+ struct sk_buff * skb = dev->tx_skb;
+ dev->tx_iovec = NULL;
+
+ // VC layer stats
+ atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
+
+ // free the skb
+ hrz_kfree_skb (skb);
+ }
+ }
+
+ // note: writing TX_COUNT clears any interrupt condition
+ if (tx_bytes) {
+ if (pio_instead) {
+ if (irq)
+ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0);
+ wrs_regb (dev, DATA_PORT_OFF, dev->tx_addr, tx_bytes);
+ if (append_desc)
+ wr_regl (dev, TX_DESCRIPTOR_PORT_OFF, cpu_to_be32 (dev->tx_skb->len));
+ } else {
+ wr_regl (dev, MASTER_TX_ADDR_REG_OFF, virt_to_bus (dev->tx_addr));
+ if (append_desc)
+ wr_regl (dev, TX_DESCRIPTOR_REG_OFF, cpu_to_be32 (dev->tx_skb->len));
+ wr_regl (dev, MASTER_TX_COUNT_REG_OFF,
+ append_desc
+ ? tx_bytes | MASTER_TX_AUTO_APPEND_DESC
+ : tx_bytes);
+ }
+ dev->tx_addr += tx_bytes;
+ } else {
+ if (irq)
+ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0);
+ YELLOW_LED_ON(dev);
+ tx_release (dev);
+ }
+
+#ifdef TAILRECURSIONWORKS
+ // and we all bless optimised tail calls
+ if (pio_instead)
+ return tx_schedule (dev, 0);
+ return;
+#else
+ // grrrrrrr!
+ irq = 0;
+ }
+ return;
+#endif
+}
+
+/********** handle TX bus master complete events **********/
+
+static void tx_bus_master_complete_handler (hrz_dev * dev) {
+ if (test_bit (tx_busy, &dev->flags)) {
+ tx_schedule (dev, 1);
+ } else {
+ PRINTD (DBG_TX|DBG_ERR, "unexpected TX bus master completion");
+ // clear interrupt condition on adapter
+ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0);
+ }
+ return;
+}
+
+/********** move RX Q pointer to next item in circular buffer **********/
+
+// called only from IRQ sub-handler
+static u32 rx_queue_entry_next (hrz_dev * dev) {
+ u32 rx_queue_entry;
+ spin_lock (&dev->mem_lock);
+ rx_queue_entry = rd_mem (dev, &dev->rx_q_entry->entry);
+ if (dev->rx_q_entry == dev->rx_q_wrap)
+ dev->rx_q_entry = dev->rx_q_reset;
+ else
+ dev->rx_q_entry++;
+ wr_regw (dev, RX_QUEUE_RD_PTR_OFF, dev->rx_q_entry - dev->rx_q_reset);
+ spin_unlock (&dev->mem_lock);
+ return rx_queue_entry;
+}
+
+/********** handle RX data received by device **********/
+
+// called from IRQ handler
+static void rx_data_av_handler (hrz_dev * dev) {
+ u32 rx_queue_entry;
+ u32 rx_queue_entry_flags;
+ u16 rx_len;
+ u16 rx_channel;
+
+ PRINTD (DBG_FLOW, "hrz_data_av_handler");
+
+ // try to grab rx lock (not possible during RX bus mastering)
+ if (test_and_set_bit (rx_busy, &dev->flags)) {
+ PRINTD (DBG_RX, "locked out of rx lock");
+ return;
+ }
+ PRINTD (DBG_RX, "set rx_busy for dev %p", dev);
+ // lock is cleared if we fail now, o/w after bus master completion
+
+ YELLOW_LED_OFF(dev);
+
+ rx_queue_entry = rx_queue_entry_next (dev);
+
+ rx_len = rx_q_entry_to_length (rx_queue_entry);
+ rx_channel = rx_q_entry_to_rx_channel (rx_queue_entry);
+
+ WAIT_FLUSH_RX_COMPLETE (dev);
+
+ SELECT_RX_CHANNEL (dev, rx_channel);
+
+ PRINTD (DBG_RX, "rx_queue_entry is: %#x", rx_queue_entry);
+ rx_queue_entry_flags = rx_queue_entry & (RX_CRC_32_OK|RX_COMPLETE_FRAME|SIMONS_DODGEY_MARKER);
+
+ if (!rx_len) {
+ // (at least) bus-mastering breaks if we try to handle a
+ // zero-length frame, besides AAL5 does not support them
+ PRINTK (KERN_ERR, "zero-length frame!");
+ rx_queue_entry_flags &= ~RX_COMPLETE_FRAME;
+ }
+
+ if (rx_queue_entry_flags & SIMONS_DODGEY_MARKER) {
+ PRINTD (DBG_RX|DBG_ERR, "Simon's marker detected!");
+ }
+ if (rx_queue_entry_flags == (RX_CRC_32_OK | RX_COMPLETE_FRAME)) {
+ struct atm_vcc * atm_vcc;
+
+ PRINTD (DBG_RX, "got a frame on rx_channel %x len %u", rx_channel, rx_len);
+
+ atm_vcc = dev->rxer[rx_channel];
+ // if no vcc is assigned to this channel, we should drop the frame
+ // (is this what SIMONS etc. was trying to achieve?)
+
+ if (atm_vcc) {
+
+ if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {
+
+ if (rx_len <= atm_vcc->qos.rxtp.max_sdu) {
+
+ struct sk_buff * skb = atm_alloc_charge (atm_vcc, rx_len, GFP_ATOMIC);
+ if (skb) {
+ // remember this so we can push it later
+ dev->rx_skb = skb;
+ // remember this so we can flush it later
+ dev->rx_channel = rx_channel;
+
+ // prepare socket buffer
+ skb_put (skb, rx_len);
+ ATM_SKB(skb)->vcc = atm_vcc;
+
+ // simple transfer
+ // dev->rx_regions = 0;
+ // dev->rx_iovec = 0;
+ dev->rx_bytes = rx_len;
+ dev->rx_addr = skb->data;
+ PRINTD (DBG_RX, "RX start simple transfer (addr %p, len %d)",
+ skb->data, rx_len);
+
+ // do the business
+ rx_schedule (dev, 0);
+ return;
+
+ } else {
+ PRINTD (DBG_SKB|DBG_WARN, "failed to get skb");
+ }
+
+ } else {
+ PRINTK (KERN_INFO, "frame received on TX-only VC %x", rx_channel);
+ // do we count this?
+ }
+
+ } else {
+ PRINTK (KERN_WARNING, "dropped over-size frame");
+ // do we count this?
+ }
+
+ } else {
+ PRINTD (DBG_WARN|DBG_VCC|DBG_RX, "no VCC for this frame (VC closed)");
+ // do we count this?
+ }
+
+ } else {
+ // Wait update complete ? SPONG
+ }
+
+ // RX was aborted
+ YELLOW_LED_ON(dev);
+
+ FLUSH_RX_CHANNEL (dev,rx_channel);
+ clear_bit (rx_busy, &dev->flags);
+
+ return;
+}
+
+/********** interrupt handler **********/
+
+static irqreturn_t interrupt_handler(int irq, void *dev_id)
+{
+ hrz_dev *dev = dev_id;
+ u32 int_source;
+ unsigned int irq_ok;
+
+ PRINTD (DBG_FLOW, "interrupt_handler: %p", dev_id);
+
+ // definitely for us
+ irq_ok = 0;
+ while ((int_source = rd_regl (dev, INT_SOURCE_REG_OFF)
+ & INTERESTING_INTERRUPTS)) {
+ // In the interests of fairness, the handlers below are
+ // called in sequence and without immediate return to the head of
+ // the while loop. This is only of issue for slow hosts (or when
+ // debugging messages are on). Really slow hosts may find a fast
+ // sender keeps them permanently in the IRQ handler. :(
+
+ // (only an issue for slow hosts) RX completion goes before
+ // rx_data_av as the former implies rx_busy and so the latter
+ // would just abort. If it reschedules another transfer
+ // (continuing the same frame) then it will not clear rx_busy.
+
+ // (only an issue for slow hosts) TX completion goes before RX
+ // data available as it is a much shorter routine - there is the
+ // chance that any further transfers it schedules will be complete
+ // by the time of the return to the head of the while loop
+
+ if (int_source & RX_BUS_MASTER_COMPLETE) {
+ ++irq_ok;
+ PRINTD (DBG_IRQ|DBG_BUS|DBG_RX, "rx_bus_master_complete asserted");
+ rx_bus_master_complete_handler (dev);
+ }
+ if (int_source & TX_BUS_MASTER_COMPLETE) {
+ ++irq_ok;
+ PRINTD (DBG_IRQ|DBG_BUS|DBG_TX, "tx_bus_master_complete asserted");
+ tx_bus_master_complete_handler (dev);
+ }
+ if (int_source & RX_DATA_AV) {
+ ++irq_ok;
+ PRINTD (DBG_IRQ|DBG_RX, "rx_data_av asserted");
+ rx_data_av_handler (dev);
+ }
+ }
+ if (irq_ok) {
+ PRINTD (DBG_IRQ, "work done: %u", irq_ok);
+ } else {
+ PRINTD (DBG_IRQ|DBG_WARN, "spurious interrupt source: %#x", int_source);
+ }
+
+ PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id);
+ if (irq_ok)
+ return IRQ_HANDLED;
+ return IRQ_NONE;
+}
+
+/********** housekeeping **********/
+
+static void do_housekeeping (unsigned long arg) {
+ // just stats at the moment
+ hrz_dev * dev = (hrz_dev *) arg;
+
+ // collect device-specific (not driver/atm-linux) stats here
+ dev->tx_cell_count += rd_regw (dev, TX_CELL_COUNT_OFF);
+ dev->rx_cell_count += rd_regw (dev, RX_CELL_COUNT_OFF);
+ dev->hec_error_count += rd_regw (dev, HEC_ERROR_COUNT_OFF);
+ dev->unassigned_cell_count += rd_regw (dev, UNASSIGNED_CELL_COUNT_OFF);
+
+ mod_timer (&dev->housekeeping, jiffies + HZ/10);
+
+ return;
+}
+
+/********** find an idle channel for TX and set it up **********/
+
+// called with tx_busy set
+static short setup_idle_tx_channel (hrz_dev * dev, hrz_vcc * vcc) {
+ unsigned short idle_channels;
+ short tx_channel = -1;
+ unsigned int spin_count;
+ PRINTD (DBG_FLOW|DBG_TX, "setup_idle_tx_channel %p", dev);
+
+ // better would be to fail immediately, the caller can then decide whether
+ // to wait or drop (depending on whether this is UBR etc.)
+ spin_count = 0;
+ while (!(idle_channels = rd_regw (dev, TX_STATUS_OFF) & IDLE_CHANNELS_MASK)) {
+ PRINTD (DBG_TX|DBG_WARN, "waiting for idle TX channel");
+ // delay a bit here
+ if (++spin_count > 100) {
+ PRINTD (DBG_TX|DBG_ERR, "spun out waiting for idle TX channel");
+ return -EBUSY;
+ }
+ }
+
+ // got an idle channel
+ {
+ // tx_idle ensures we look for idle channels in RR order
+ int chan = dev->tx_idle;
+
+ int keep_going = 1;
+ while (keep_going) {
+ if (idle_channels & (1<<chan)) {
+ tx_channel = chan;
+ keep_going = 0;
+ }
+ ++chan;
+ if (chan == TX_CHANS)
+ chan = 0;
+ }
+
+ dev->tx_idle = chan;
+ }
+
+ // set up the channel we found
+ {
+ // Initialise the cell header in the transmit channel descriptor
+ // a.k.a. prepare the channel and remember that we have done so.
+
+ tx_ch_desc * tx_desc = &memmap->tx_descs[tx_channel];
+ u32 rd_ptr;
+ u32 wr_ptr;
+ u16 channel = vcc->channel;
+
+ unsigned long flags;
+ spin_lock_irqsave (&dev->mem_lock, flags);
+
+ // Update the transmit channel record.
+ dev->tx_channel_record[tx_channel] = channel;
+
+ // xBR channel
+ update_tx_channel_config (dev, tx_channel, RATE_TYPE_ACCESS,
+ vcc->tx_xbr_bits);
+
+ // Update the PCR counter preload value etc.
+ update_tx_channel_config (dev, tx_channel, PCR_TIMER_ACCESS,
+ vcc->tx_pcr_bits);
+
+#if 0
+ if (vcc->tx_xbr_bits == VBR_RATE_TYPE) {
+ // SCR timer
+ update_tx_channel_config (dev, tx_channel, SCR_TIMER_ACCESS,
+ vcc->tx_scr_bits);
+
+ // Bucket size...
+ update_tx_channel_config (dev, tx_channel, BUCKET_CAPACITY_ACCESS,
+ vcc->tx_bucket_bits);
+
+ // ... and fullness
+ update_tx_channel_config (dev, tx_channel, BUCKET_FULLNESS_ACCESS,
+ vcc->tx_bucket_bits);
+ }
+#endif
+
+ // Initialise the read and write buffer pointers
+ rd_ptr = rd_mem (dev, &tx_desc->rd_buf_type) & BUFFER_PTR_MASK;
+ wr_ptr = rd_mem (dev, &tx_desc->wr_buf_type) & BUFFER_PTR_MASK;
+
+ // idle TX channels should have identical pointers
+ if (rd_ptr != wr_ptr) {
+ PRINTD (DBG_TX|DBG_ERR, "TX buffer pointers are broken!");
+ // spin_unlock... return -E...
+ // I wonder if gcc would get rid of one of the pointer aliases
+ }
+ PRINTD (DBG_TX, "TX buffer pointers are: rd %x, wr %x.",
+ rd_ptr, wr_ptr);
+
+ switch (vcc->aal) {
+ case aal0:
+ PRINTD (DBG_QOS|DBG_TX, "tx_channel: aal0");
+ rd_ptr |= CHANNEL_TYPE_RAW_CELLS;
+ wr_ptr |= CHANNEL_TYPE_RAW_CELLS;
+ break;
+ case aal34:
+ PRINTD (DBG_QOS|DBG_TX, "tx_channel: aal34");
+ rd_ptr |= CHANNEL_TYPE_AAL3_4;
+ wr_ptr |= CHANNEL_TYPE_AAL3_4;
+ break;
+ case aal5:
+ rd_ptr |= CHANNEL_TYPE_AAL5;
+ wr_ptr |= CHANNEL_TYPE_AAL5;
+ // Initialise the CRC
+ wr_mem (dev, &tx_desc->partial_crc, INITIAL_CRC);
+ break;
+ }
+
+ wr_mem (dev, &tx_desc->rd_buf_type, rd_ptr);
+ wr_mem (dev, &tx_desc->wr_buf_type, wr_ptr);
+
+ // Write the Cell Header
+ // Payload Type, CLP and GFC would go here if non-zero
+ wr_mem (dev, &tx_desc->cell_header, channel);
+
+ spin_unlock_irqrestore (&dev->mem_lock, flags);
+ }
+
+ return tx_channel;
+}
+
+/********** send a frame **********/
+
+static int hrz_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
+ unsigned int spin_count;
+ int free_buffers;
+ hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
+ hrz_vcc * vcc = HRZ_VCC(atm_vcc);
+ u16 channel = vcc->channel;
+
+ u32 buffers_required;
+
+ /* signed for error return */
+ short tx_channel;
+
+ PRINTD (DBG_FLOW|DBG_TX, "hrz_send vc %x data %p len %u",
+ channel, skb->data, skb->len);
+
+ dump_skb (">>>", channel, skb);
+
+ if (atm_vcc->qos.txtp.traffic_class == ATM_NONE) {
+ PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", channel);
+ hrz_kfree_skb (skb);
+ return -EIO;
+ }
+
+ // don't understand this
+ ATM_SKB(skb)->vcc = atm_vcc;
+
+ if (skb->len > atm_vcc->qos.txtp.max_sdu) {
+ PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping...");
+ hrz_kfree_skb (skb);
+ return -EIO;
+ }
+
+ if (!channel) {
+ PRINTD (DBG_ERR|DBG_TX, "attempt to transmit on zero (rx_)channel");
+ hrz_kfree_skb (skb);
+ return -EIO;
+ }
+
+#if 0
+ {
+ // where would be a better place for this? housekeeping?
+ u16 status;
+ pci_read_config_word (dev->pci_dev, PCI_STATUS, &status);
+ if (status & PCI_STATUS_REC_MASTER_ABORT) {
+ PRINTD (DBG_BUS|DBG_ERR, "Clearing PCI Master Abort (and cleaning up)");
+ status &= ~PCI_STATUS_REC_MASTER_ABORT;
+ pci_write_config_word (dev->pci_dev, PCI_STATUS, status);
+ if (test_bit (tx_busy, &dev->flags)) {
+ hrz_kfree_skb (dev->tx_skb);
+ tx_release (dev);
+ }
+ }
+ }
+#endif
+
+#ifdef DEBUG_HORIZON
+ /* wey-hey! */
+ if (channel == 1023) {
+ unsigned int i;
+ unsigned short d = 0;
+ char * s = skb->data;
+ if (*s++ == 'D') {
+ for (i = 0; i < 4; ++i)
+ d = (d << 4) | hex_to_bin(*s++);
+ PRINTK (KERN_INFO, "debug bitmap is now %hx", debug = d);
+ }
+ }
+#endif
+
+ // wait until TX is free and grab lock
+ if (tx_hold (dev)) {
+ hrz_kfree_skb (skb);
+ return -ERESTARTSYS;
+ }
+
+ // Wait for enough space to be available in transmit buffer memory.
+
+ // should be number of cells needed + 2 (according to hardware docs)
+ // = ((framelen+8)+47) / 48 + 2
+ // = (framelen+7) / 48 + 3, hmm... faster to put addition inside XXX
+ buffers_required = (skb->len+(ATM_AAL5_TRAILER-1)) / ATM_CELL_PAYLOAD + 3;
+
+ // replace with timer and sleep, add dev->tx_buffers_queue (max 1 entry)
+ spin_count = 0;
+ while ((free_buffers = rd_regw (dev, TX_FREE_BUFFER_COUNT_OFF)) < buffers_required) {
+ PRINTD (DBG_TX, "waiting for free TX buffers, got %d of %d",
+ free_buffers, buffers_required);
+ // what is the appropriate delay? implement a timeout? (depending on line speed?)
+ // mdelay (1);
+ // what happens if we kill (current_pid, SIGKILL) ?
+ schedule();
+ if (++spin_count > 1000) {
+ PRINTD (DBG_TX|DBG_ERR, "spun out waiting for tx buffers, got %d of %d",
+ free_buffers, buffers_required);
+ tx_release (dev);
+ hrz_kfree_skb (skb);
+ return -ERESTARTSYS;
+ }
+ }
+
+ // Select a channel to transmit the frame on.
+ if (channel == dev->last_vc) {
+ PRINTD (DBG_TX, "last vc hack: hit");
+ tx_channel = dev->tx_last;
+ } else {
+ PRINTD (DBG_TX, "last vc hack: miss");
+ // Are we currently transmitting this VC on one of the channels?
+ for (tx_channel = 0; tx_channel < TX_CHANS; ++tx_channel)
+ if (dev->tx_channel_record[tx_channel] == channel) {
+ PRINTD (DBG_TX, "vc already on channel: hit");
+ break;
+ }
+ if (tx_channel == TX_CHANS) {
+ PRINTD (DBG_TX, "vc already on channel: miss");
+ // Find and set up an idle channel.
+ tx_channel = setup_idle_tx_channel (dev, vcc);
+ if (tx_channel < 0) {
+ PRINTD (DBG_TX|DBG_ERR, "failed to get channel");
+ tx_release (dev);
+ return tx_channel;
+ }
+ }
+
+ PRINTD (DBG_TX, "got channel");
+ SELECT_TX_CHANNEL(dev, tx_channel);
+
+ dev->last_vc = channel;
+ dev->tx_last = tx_channel;
+ }
+
+ PRINTD (DBG_TX, "using channel %u", tx_channel);
+
+ YELLOW_LED_OFF(dev);
+
+ // TX start transfer
+
+ {
+ unsigned int tx_len = skb->len;
+ unsigned int tx_iovcnt = skb_shinfo(skb)->nr_frags;
+ // remember this so we can free it later
+ dev->tx_skb = skb;
+
+ if (tx_iovcnt) {
+ // scatter gather transfer
+ dev->tx_regions = tx_iovcnt;
+ dev->tx_iovec = NULL; /* @@@ needs rewritten */
+ dev->tx_bytes = 0;
+ PRINTD (DBG_TX|DBG_BUS, "TX start scatter-gather transfer (iovec %p, len %d)",
+ skb->data, tx_len);
+ tx_release (dev);
+ hrz_kfree_skb (skb);
+ return -EIO;
+ } else {
+ // simple transfer
+ dev->tx_regions = 0;
+ dev->tx_iovec = NULL;
+ dev->tx_bytes = tx_len;
+ dev->tx_addr = skb->data;
+ PRINTD (DBG_TX|DBG_BUS, "TX start simple transfer (addr %p, len %d)",
+ skb->data, tx_len);
+ }
+
+ // and do the business
+ tx_schedule (dev, 0);
+
+ }
+
+ return 0;
+}
+
+/********** reset a card **********/
+
+static void hrz_reset (const hrz_dev * dev) {
+ u32 control_0_reg = rd_regl (dev, CONTROL_0_REG);
+
+ // why not set RESET_HORIZON to one and wait for the card to
+ // reassert that bit as zero? Like so:
+ control_0_reg = control_0_reg & RESET_HORIZON;
+ wr_regl (dev, CONTROL_0_REG, control_0_reg);
+ while (control_0_reg & RESET_HORIZON)
+ control_0_reg = rd_regl (dev, CONTROL_0_REG);
+
+ // old reset code retained:
+ wr_regl (dev, CONTROL_0_REG, control_0_reg |
+ RESET_ATM | RESET_RX | RESET_TX | RESET_HOST);
+ // just guessing here
+ udelay (1000);
+
+ wr_regl (dev, CONTROL_0_REG, control_0_reg);
+}
+
+/********** read the burnt in address **********/
+
+static void WRITE_IT_WAIT (const hrz_dev *dev, u32 ctrl)
+{
+ wr_regl (dev, CONTROL_0_REG, ctrl);
+ udelay (5);
+}
+
+static void CLOCK_IT (const hrz_dev *dev, u32 ctrl)
+{
+ // DI must be valid around rising SK edge
+ WRITE_IT_WAIT(dev, ctrl & ~SEEPROM_SK);
+ WRITE_IT_WAIT(dev, ctrl | SEEPROM_SK);
+}
+
+static u16 read_bia(const hrz_dev *dev, u16 addr)
+{
+ u32 ctrl = rd_regl (dev, CONTROL_0_REG);
+
+ const unsigned int addr_bits = 6;
+ const unsigned int data_bits = 16;
+
+ unsigned int i;
+
+ u16 res;
+
+ ctrl &= ~(SEEPROM_CS | SEEPROM_SK | SEEPROM_DI);
+ WRITE_IT_WAIT(dev, ctrl);
+
+ // wake Serial EEPROM and send 110 (READ) command
+ ctrl |= (SEEPROM_CS | SEEPROM_DI);
+ CLOCK_IT(dev, ctrl);
+
+ ctrl |= SEEPROM_DI;
+ CLOCK_IT(dev, ctrl);
+
+ ctrl &= ~SEEPROM_DI;
+ CLOCK_IT(dev, ctrl);
+
+ for (i=0; i<addr_bits; i++) {
+ if (addr & (1 << (addr_bits-1)))
+ ctrl |= SEEPROM_DI;
+ else
+ ctrl &= ~SEEPROM_DI;
+
+ CLOCK_IT(dev, ctrl);
+
+ addr = addr << 1;
+ }
+
+ // we could check that we have DO = 0 here
+ ctrl &= ~SEEPROM_DI;
+
+ res = 0;
+ for (i=0;i<data_bits;i++) {
+ res = res >> 1;
+
+ CLOCK_IT(dev, ctrl);
+
+ if (rd_regl (dev, CONTROL_0_REG) & SEEPROM_DO)
+ res |= (1 << (data_bits-1));
+ }
+
+ ctrl &= ~(SEEPROM_SK | SEEPROM_CS);
+ WRITE_IT_WAIT(dev, ctrl);
+
+ return res;
+}
+
+/********** initialise a card **********/
+
+static int hrz_init(hrz_dev *dev)
+{
+ int onefivefive;
+
+ u16 chan;
+
+ int buff_count;
+
+ HDW * mem;
+
+ cell_buf * tx_desc;
+ cell_buf * rx_desc;
+
+ u32 ctrl;
+
+ ctrl = rd_regl (dev, CONTROL_0_REG);
+ PRINTD (DBG_INFO, "ctrl0reg is %#x", ctrl);
+ onefivefive = ctrl & ATM_LAYER_STATUS;
+
+ if (onefivefive)
+ printk (DEV_LABEL ": Horizon Ultra (at 155.52 MBps)");
+ else
+ printk (DEV_LABEL ": Horizon (at 25 MBps)");
+
+ printk (":");
+ // Reset the card to get everything in a known state
+
+ printk (" reset");
+ hrz_reset (dev);
+
+ // Clear all the buffer memory
+
+ printk (" clearing memory");
+
+ for (mem = (HDW *) memmap; mem < (HDW *) (memmap + 1); ++mem)
+ wr_mem (dev, mem, 0);
+
+ printk (" tx channels");
+
+ // All transmit eight channels are set up as AAL5 ABR channels with
+ // a 16us cell spacing. Why?
+
+ // Channel 0 gets the free buffer at 100h, channel 1 gets the free
+ // buffer at 110h etc.
+
+ for (chan = 0; chan < TX_CHANS; ++chan) {
+ tx_ch_desc * tx_desc = &memmap->tx_descs[chan];
+ cell_buf * buf = &memmap->inittxbufs[chan];
+
+ // initialise the read and write buffer pointers
+ wr_mem (dev, &tx_desc->rd_buf_type, BUF_PTR(buf));
+ wr_mem (dev, &tx_desc->wr_buf_type, BUF_PTR(buf));
+
+ // set the status of the initial buffers to empty
+ wr_mem (dev, &buf->next, BUFF_STATUS_EMPTY);
+ }
+
+ // Use space bufn3 at the moment for tx buffers
+
+ printk (" tx buffers");
+
+ tx_desc = memmap->bufn3;
+
+ wr_mem (dev, &memmap->txfreebufstart.next, BUF_PTR(tx_desc) | BUFF_STATUS_EMPTY);
+
+ for (buff_count = 0; buff_count < BUFN3_SIZE-1; buff_count++) {
+ wr_mem (dev, &tx_desc->next, BUF_PTR(tx_desc+1) | BUFF_STATUS_EMPTY);
+ tx_desc++;
+ }
+
+ wr_mem (dev, &tx_desc->next, BUF_PTR(&memmap->txfreebufend) | BUFF_STATUS_EMPTY);
+
+ // Initialise the transmit free buffer count
+ wr_regw (dev, TX_FREE_BUFFER_COUNT_OFF, BUFN3_SIZE);
+
+ printk (" rx channels");
+
+ // Initialise all of the receive channels to be AAL5 disabled with
+ // an interrupt threshold of 0
+
+ for (chan = 0; chan < RX_CHANS; ++chan) {
+ rx_ch_desc * rx_desc = &memmap->rx_descs[chan];
+
+ wr_mem (dev, &rx_desc->wr_buf_type, CHANNEL_TYPE_AAL5 | RX_CHANNEL_DISABLED);
+ }
+
+ printk (" rx buffers");
+
+ // Use space bufn4 at the moment for rx buffers
+
+ rx_desc = memmap->bufn4;
+
+ wr_mem (dev, &memmap->rxfreebufstart.next, BUF_PTR(rx_desc) | BUFF_STATUS_EMPTY);
+
+ for (buff_count = 0; buff_count < BUFN4_SIZE-1; buff_count++) {
+ wr_mem (dev, &rx_desc->next, BUF_PTR(rx_desc+1) | BUFF_STATUS_EMPTY);
+
+ rx_desc++;
+ }
+
+ wr_mem (dev, &rx_desc->next, BUF_PTR(&memmap->rxfreebufend) | BUFF_STATUS_EMPTY);
+
+ // Initialise the receive free buffer count
+ wr_regw (dev, RX_FREE_BUFFER_COUNT_OFF, BUFN4_SIZE);
+
+ // Initialize Horizons registers
+
+ // TX config
+ wr_regw (dev, TX_CONFIG_OFF,
+ ABR_ROUND_ROBIN | TX_NORMAL_OPERATION | DRVR_DRVRBAR_ENABLE);
+
+ // RX config. Use 10-x VC bits, x VP bits, non user cells in channel 0.
+ wr_regw (dev, RX_CONFIG_OFF,
+ DISCARD_UNUSED_VPI_VCI_BITS_SET | NON_USER_CELLS_IN_ONE_CHANNEL | vpi_bits);
+
+ // RX line config
+ wr_regw (dev, RX_LINE_CONFIG_OFF,
+ LOCK_DETECT_ENABLE | FREQUENCY_DETECT_ENABLE | GXTALOUT_SELECT_DIV4);
+
+ // Set the max AAL5 cell count to be just enough to contain the
+ // largest AAL5 frame that the user wants to receive
+ wr_regw (dev, MAX_AAL5_CELL_COUNT_OFF,
+ DIV_ROUND_UP(max_rx_size + ATM_AAL5_TRAILER, ATM_CELL_PAYLOAD));
+
+ // Enable receive
+ wr_regw (dev, RX_CONFIG_OFF, rd_regw (dev, RX_CONFIG_OFF) | RX_ENABLE);
+
+ printk (" control");
+
+ // Drive the OE of the LEDs then turn the green LED on
+ ctrl |= GREEN_LED_OE | YELLOW_LED_OE | GREEN_LED | YELLOW_LED;
+ wr_regl (dev, CONTROL_0_REG, ctrl);
+
+ // Test for a 155-capable card
+
+ if (onefivefive) {
+ // Select 155 mode... make this a choice (or: how do we detect
+ // external line speed and switch?)
+ ctrl |= ATM_LAYER_SELECT;
+ wr_regl (dev, CONTROL_0_REG, ctrl);
+
+ // test SUNI-lite vs SAMBA
+
+ // Register 0x00 in the SUNI will have some of bits 3-7 set, and
+ // they will always be zero for the SAMBA. Ha! Bloody hardware
+ // engineers. It'll never work.
+
+ if (rd_framer (dev, 0) & 0x00f0) {
+ // SUNI
+ printk (" SUNI");
+
+ // Reset, just in case
+ wr_framer (dev, 0x00, 0x0080);
+ wr_framer (dev, 0x00, 0x0000);
+
+ // Configure transmit FIFO
+ wr_framer (dev, 0x63, rd_framer (dev, 0x63) | 0x0002);
+
+ // Set line timed mode
+ wr_framer (dev, 0x05, rd_framer (dev, 0x05) | 0x0001);
+ } else {
+ // SAMBA
+ printk (" SAMBA");
+
+ // Reset, just in case
+ wr_framer (dev, 0, rd_framer (dev, 0) | 0x0001);
+ wr_framer (dev, 0, rd_framer (dev, 0) &~ 0x0001);
+
+ // Turn off diagnostic loopback and enable line-timed mode
+ wr_framer (dev, 0, 0x0002);
+
+ // Turn on transmit outputs
+ wr_framer (dev, 2, 0x0B80);
+ }
+ } else {
+ // Select 25 mode
+ ctrl &= ~ATM_LAYER_SELECT;
+
+ // Madge B154 setup
+ // none required?
+ }
+
+ printk (" LEDs");
+
+ GREEN_LED_ON(dev);
+ YELLOW_LED_ON(dev);
+
+ printk (" ESI=");
+
+ {
+ u16 b = 0;
+ int i;
+ u8 * esi = dev->atm_dev->esi;
+
+ // in the card I have, EEPROM
+ // addresses 0, 1, 2 contain 0
+ // addresess 5, 6 etc. contain ffff
+ // NB: Madge prefix is 00 00 f6 (which is 00 00 6f in Ethernet bit order)
+ // the read_bia routine gets the BIA in Ethernet bit order
+
+ for (i=0; i < ESI_LEN; ++i) {
+ if (i % 2 == 0)
+ b = read_bia (dev, i/2 + 2);
+ else
+ b = b >> 8;
+ esi[i] = b & 0xFF;
+ printk ("%02x", esi[i]);
+ }
+ }
+
+ // Enable RX_Q and ?X_COMPLETE interrupts only
+ wr_regl (dev, INT_ENABLE_REG_OFF, INTERESTING_INTERRUPTS);
+ printk (" IRQ on");
+
+ printk (".\n");
+
+ return onefivefive;
+}
+
+/********** check max_sdu **********/
+
+static int check_max_sdu (hrz_aal aal, struct atm_trafprm * tp, unsigned int max_frame_size) {
+ PRINTD (DBG_FLOW|DBG_QOS, "check_max_sdu");
+
+ switch (aal) {
+ case aal0:
+ if (!(tp->max_sdu)) {
+ PRINTD (DBG_QOS, "defaulting max_sdu");
+ tp->max_sdu = ATM_AAL0_SDU;
+ } else if (tp->max_sdu != ATM_AAL0_SDU) {
+ PRINTD (DBG_QOS|DBG_ERR, "rejecting max_sdu");
+ return -EINVAL;
+ }
+ break;
+ case aal34:
+ if (tp->max_sdu == 0 || tp->max_sdu > ATM_MAX_AAL34_PDU) {
+ PRINTD (DBG_QOS, "%sing max_sdu", tp->max_sdu ? "capp" : "default");
+ tp->max_sdu = ATM_MAX_AAL34_PDU;
+ }
+ break;
+ case aal5:
+ if (tp->max_sdu == 0 || tp->max_sdu > max_frame_size) {
+ PRINTD (DBG_QOS, "%sing max_sdu", tp->max_sdu ? "capp" : "default");
+ tp->max_sdu = max_frame_size;
+ }
+ break;
+ }
+ return 0;
+}
+
+/********** check pcr **********/
+
+// something like this should be part of ATM Linux
+static int atm_pcr_check (struct atm_trafprm * tp, unsigned int pcr) {
+ // we are assuming non-UBR, and non-special values of pcr
+ if (tp->min_pcr == ATM_MAX_PCR)
+ PRINTD (DBG_QOS, "luser gave min_pcr = ATM_MAX_PCR");
+ else if (tp->min_pcr < 0)
+ PRINTD (DBG_QOS, "luser gave negative min_pcr");
+ else if (tp->min_pcr && tp->min_pcr > pcr)
+ PRINTD (DBG_QOS, "pcr less than min_pcr");
+ else
+ // !! max_pcr = UNSPEC (0) is equivalent to max_pcr = MAX (-1)
+ // easier to #define ATM_MAX_PCR 0 and have all rates unsigned?
+ // [this would get rid of next two conditionals]
+ if ((0) && tp->max_pcr == ATM_MAX_PCR)
+ PRINTD (DBG_QOS, "luser gave max_pcr = ATM_MAX_PCR");
+ else if ((tp->max_pcr != ATM_MAX_PCR) && tp->max_pcr < 0)
+ PRINTD (DBG_QOS, "luser gave negative max_pcr");
+ else if (tp->max_pcr && tp->max_pcr != ATM_MAX_PCR && tp->max_pcr < pcr)
+ PRINTD (DBG_QOS, "pcr greater than max_pcr");
+ else {
+ // each limit unspecified or not violated
+ PRINTD (DBG_QOS, "xBR(pcr) OK");
+ return 0;
+ }
+ PRINTD (DBG_QOS, "pcr=%u, tp: min_pcr=%d, pcr=%d, max_pcr=%d",
+ pcr, tp->min_pcr, tp->pcr, tp->max_pcr);
+ return -EINVAL;
+}
+
+/********** open VC **********/
+
+static int hrz_open (struct atm_vcc *atm_vcc)
+{
+ int error;
+ u16 channel;
+
+ struct atm_qos * qos;
+ struct atm_trafprm * txtp;
+ struct atm_trafprm * rxtp;
+
+ hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
+ hrz_vcc vcc;
+ hrz_vcc * vccp; // allocated late
+ short vpi = atm_vcc->vpi;
+ int vci = atm_vcc->vci;
+ PRINTD (DBG_FLOW|DBG_VCC, "hrz_open %x %x", vpi, vci);
+
+#ifdef ATM_VPI_UNSPEC
+ // UNSPEC is deprecated, remove this code eventually
+ if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) {
+ PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)");
+ return -EINVAL;
+ }
+#endif
+
+ error = vpivci_to_channel (&channel, vpi, vci);
+ if (error) {
+ PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci);
+ return error;
+ }
+
+ vcc.channel = channel;
+ // max speed for the moment
+ vcc.tx_rate = 0x0;
+
+ qos = &atm_vcc->qos;
+
+ // check AAL and remember it
+ switch (qos->aal) {
+ case ATM_AAL0:
+ // we would if it were 48 bytes and not 52!
+ PRINTD (DBG_QOS|DBG_VCC, "AAL0");
+ vcc.aal = aal0;
+ break;
+ case ATM_AAL34:
+ // we would if I knew how do the SAR!
+ PRINTD (DBG_QOS|DBG_VCC, "AAL3/4");
+ vcc.aal = aal34;
+ break;
+ case ATM_AAL5:
+ PRINTD (DBG_QOS|DBG_VCC, "AAL5");
+ vcc.aal = aal5;
+ break;
+ default:
+ PRINTD (DBG_QOS|DBG_VCC, "Bad AAL!");
+ return -EINVAL;
+ }
+
+ // TX traffic parameters
+
+ // there are two, interrelated problems here: 1. the reservation of
+ // PCR is not a binary choice, we are given bounds and/or a
+ // desirable value; 2. the device is only capable of certain values,
+ // most of which are not integers. It is almost certainly acceptable
+ // to be off by a maximum of 1 to 10 cps.
+
+ // Pragmatic choice: always store an integral PCR as that which has
+ // been allocated, even if we allocate a little (or a lot) less,
+ // after rounding. The actual allocation depends on what we can
+ // manage with our rate selection algorithm. The rate selection
+ // algorithm is given an integral PCR and a tolerance and told
+ // whether it should round the value up or down if the tolerance is
+ // exceeded; it returns: a) the actual rate selected (rounded up to
+ // the nearest integer), b) a bit pattern to feed to the timer
+ // register, and c) a failure value if no applicable rate exists.
+
+ // Part of the job is done by atm_pcr_goal which gives us a PCR
+ // specification which says: EITHER grab the maximum available PCR
+ // (and perhaps a lower bound which we musn't pass), OR grab this
+ // amount, rounding down if you have to (and perhaps a lower bound
+ // which we musn't pass) OR grab this amount, rounding up if you
+ // have to (and perhaps an upper bound which we musn't pass). If any
+ // bounds ARE passed we fail. Note that rounding is only rounding to
+ // match device limitations, we do not round down to satisfy
+ // bandwidth availability even if this would not violate any given
+ // lower bound.
+
+ // Note: telephony = 64kb/s = 48 byte cell payload @ 500/3 cells/s
+ // (say) so this is not even a binary fixpoint cell rate (but this
+ // device can do it). To avoid this sort of hassle we use a
+ // tolerance parameter (currently fixed at 10 cps).
+
+ PRINTD (DBG_QOS, "TX:");
+
+ txtp = &qos->txtp;
+
+ // set up defaults for no traffic
+ vcc.tx_rate = 0;
+ // who knows what would actually happen if you try and send on this?
+ vcc.tx_xbr_bits = IDLE_RATE_TYPE;
+ vcc.tx_pcr_bits = CLOCK_DISABLE;
+#if 0
+ vcc.tx_scr_bits = CLOCK_DISABLE;
+ vcc.tx_bucket_bits = 0;
+#endif
+
+ if (txtp->traffic_class != ATM_NONE) {
+ error = check_max_sdu (vcc.aal, txtp, max_tx_size);
+ if (error) {
+ PRINTD (DBG_QOS, "TX max_sdu check failed");
+ return error;
+ }
+
+ switch (txtp->traffic_class) {
+ case ATM_UBR: {
+ // we take "the PCR" as a rate-cap
+ // not reserved
+ vcc.tx_rate = 0;
+ make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, NULL);
+ vcc.tx_xbr_bits = ABR_RATE_TYPE;
+ break;
+ }
+#if 0
+ case ATM_ABR: {
+ // reserve min, allow up to max
+ vcc.tx_rate = 0; // ?
+ make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, 0);
+ vcc.tx_xbr_bits = ABR_RATE_TYPE;
+ break;
+ }
+#endif
+ case ATM_CBR: {
+ int pcr = atm_pcr_goal (txtp);
+ rounding r;
+ if (!pcr) {
+ // down vs. up, remaining bandwidth vs. unlimited bandwidth!!
+ // should really have: once someone gets unlimited bandwidth
+ // that no more non-UBR channels can be opened until the
+ // unlimited one closes?? For the moment, round_down means
+ // greedy people actually get something and not nothing
+ r = round_down;
+ // slight race (no locking) here so we may get -EAGAIN
+ // later; the greedy bastards would deserve it :)
+ PRINTD (DBG_QOS, "snatching all remaining TX bandwidth");
+ pcr = dev->tx_avail;
+ } else if (pcr < 0) {
+ r = round_down;
+ pcr = -pcr;
+ } else {
+ r = round_up;
+ }
+ error = make_rate_with_tolerance (dev, pcr, r, 10,
+ &vcc.tx_pcr_bits, &vcc.tx_rate);
+ if (error) {
+ PRINTD (DBG_QOS, "could not make rate from TX PCR");
+ return error;
+ }
+ // not really clear what further checking is needed
+ error = atm_pcr_check (txtp, vcc.tx_rate);
+ if (error) {
+ PRINTD (DBG_QOS, "TX PCR failed consistency check");
+ return error;
+ }
+ vcc.tx_xbr_bits = CBR_RATE_TYPE;
+ break;
+ }
+#if 0
+ case ATM_VBR: {
+ int pcr = atm_pcr_goal (txtp);
+ // int scr = atm_scr_goal (txtp);
+ int scr = pcr/2; // just for fun
+ unsigned int mbs = 60; // just for fun
+ rounding pr;
+ rounding sr;
+ unsigned int bucket;
+ if (!pcr) {
+ pr = round_nearest;
+ pcr = 1<<30;
+ } else if (pcr < 0) {
+ pr = round_down;
+ pcr = -pcr;
+ } else {
+ pr = round_up;
+ }
+ error = make_rate_with_tolerance (dev, pcr, pr, 10,
+ &vcc.tx_pcr_bits, 0);
+ if (!scr) {
+ // see comments for PCR with CBR above
+ sr = round_down;
+ // slight race (no locking) here so we may get -EAGAIN
+ // later; the greedy bastards would deserve it :)
+ PRINTD (DBG_QOS, "snatching all remaining TX bandwidth");
+ scr = dev->tx_avail;
+ } else if (scr < 0) {
+ sr = round_down;
+ scr = -scr;
+ } else {
+ sr = round_up;
+ }
+ error = make_rate_with_tolerance (dev, scr, sr, 10,
+ &vcc.tx_scr_bits, &vcc.tx_rate);
+ if (error) {
+ PRINTD (DBG_QOS, "could not make rate from TX SCR");
+ return error;
+ }
+ // not really clear what further checking is needed
+ // error = atm_scr_check (txtp, vcc.tx_rate);
+ if (error) {
+ PRINTD (DBG_QOS, "TX SCR failed consistency check");
+ return error;
+ }
+ // bucket calculations (from a piece of paper...) cell bucket
+ // capacity must be largest integer smaller than m(p-s)/p + 1
+ // where m = max burst size, p = pcr, s = scr
+ bucket = mbs*(pcr-scr)/pcr;
+ if (bucket*pcr != mbs*(pcr-scr))
+ bucket += 1;
+ if (bucket > BUCKET_MAX_SIZE) {
+ PRINTD (DBG_QOS, "shrinking bucket from %u to %u",
+ bucket, BUCKET_MAX_SIZE);
+ bucket = BUCKET_MAX_SIZE;
+ }
+ vcc.tx_xbr_bits = VBR_RATE_TYPE;
+ vcc.tx_bucket_bits = bucket;
+ break;
+ }
+#endif
+ default: {
+ PRINTD (DBG_QOS, "unsupported TX traffic class");
+ return -EINVAL;
+ }
+ }
+ }
+
+ // RX traffic parameters
+
+ PRINTD (DBG_QOS, "RX:");
+
+ rxtp = &qos->rxtp;
+
+ // set up defaults for no traffic
+ vcc.rx_rate = 0;
+
+ if (rxtp->traffic_class != ATM_NONE) {
+ error = check_max_sdu (vcc.aal, rxtp, max_rx_size);
+ if (error) {
+ PRINTD (DBG_QOS, "RX max_sdu check failed");
+ return error;
+ }
+ switch (rxtp->traffic_class) {
+ case ATM_UBR: {
+ // not reserved
+ break;
+ }
+#if 0
+ case ATM_ABR: {
+ // reserve min
+ vcc.rx_rate = 0; // ?
+ break;
+ }
+#endif
+ case ATM_CBR: {
+ int pcr = atm_pcr_goal (rxtp);
+ if (!pcr) {
+ // slight race (no locking) here so we may get -EAGAIN
+ // later; the greedy bastards would deserve it :)
+ PRINTD (DBG_QOS, "snatching all remaining RX bandwidth");
+ pcr = dev->rx_avail;
+ } else if (pcr < 0) {
+ pcr = -pcr;
+ }
+ vcc.rx_rate = pcr;
+ // not really clear what further checking is needed
+ error = atm_pcr_check (rxtp, vcc.rx_rate);
+ if (error) {
+ PRINTD (DBG_QOS, "RX PCR failed consistency check");
+ return error;
+ }
+ break;
+ }
+#if 0
+ case ATM_VBR: {
+ // int scr = atm_scr_goal (rxtp);
+ int scr = 1<<16; // just for fun
+ if (!scr) {
+ // slight race (no locking) here so we may get -EAGAIN
+ // later; the greedy bastards would deserve it :)
+ PRINTD (DBG_QOS, "snatching all remaining RX bandwidth");
+ scr = dev->rx_avail;
+ } else if (scr < 0) {
+ scr = -scr;
+ }
+ vcc.rx_rate = scr;
+ // not really clear what further checking is needed
+ // error = atm_scr_check (rxtp, vcc.rx_rate);
+ if (error) {
+ PRINTD (DBG_QOS, "RX SCR failed consistency check");
+ return error;
+ }
+ break;
+ }
+#endif
+ default: {
+ PRINTD (DBG_QOS, "unsupported RX traffic class");
+ return -EINVAL;
+ }
+ }
+ }
+
+
+ // late abort useful for diagnostics
+ if (vcc.aal != aal5) {
+ PRINTD (DBG_QOS, "AAL not supported");
+ return -EINVAL;
+ }
+
+ // get space for our vcc stuff and copy parameters into it
+ vccp = kmalloc (sizeof(hrz_vcc), GFP_KERNEL);
+ if (!vccp) {
+ PRINTK (KERN_ERR, "out of memory!");
+ return -ENOMEM;
+ }
+ *vccp = vcc;
+
+ // clear error and grab cell rate resource lock
+ error = 0;
+ spin_lock (&dev->rate_lock);
+
+ if (vcc.tx_rate > dev->tx_avail) {
+ PRINTD (DBG_QOS, "not enough TX PCR left");
+ error = -EAGAIN;
+ }
+
+ if (vcc.rx_rate > dev->rx_avail) {
+ PRINTD (DBG_QOS, "not enough RX PCR left");
+ error = -EAGAIN;
+ }
+
+ if (!error) {
+ // really consume cell rates
+ dev->tx_avail -= vcc.tx_rate;
+ dev->rx_avail -= vcc.rx_rate;
+ PRINTD (DBG_QOS|DBG_VCC, "reserving %u TX PCR and %u RX PCR",
+ vcc.tx_rate, vcc.rx_rate);
+ }
+
+ // release lock and exit on error
+ spin_unlock (&dev->rate_lock);
+ if (error) {
+ PRINTD (DBG_QOS|DBG_VCC, "insufficient cell rate resources");
+ kfree (vccp);
+ return error;
+ }
+
+ // this is "immediately before allocating the connection identifier
+ // in hardware" - so long as the next call does not fail :)
+ set_bit(ATM_VF_ADDR,&atm_vcc->flags);
+
+ // any errors here are very serious and should never occur
+
+ if (rxtp->traffic_class != ATM_NONE) {
+ if (dev->rxer[channel]) {
+ PRINTD (DBG_ERR|DBG_VCC, "VC already open for RX");
+ error = -EBUSY;
+ }
+ if (!error)
+ error = hrz_open_rx (dev, channel);
+ if (error) {
+ kfree (vccp);
+ return error;
+ }
+ // this link allows RX frames through
+ dev->rxer[channel] = atm_vcc;
+ }
+
+ // success, set elements of atm_vcc
+ atm_vcc->dev_data = (void *) vccp;
+
+ // indicate readiness
+ set_bit(ATM_VF_READY,&atm_vcc->flags);
+
+ return 0;
+}
+
+/********** close VC **********/
+
+static void hrz_close (struct atm_vcc * atm_vcc) {
+ hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
+ hrz_vcc * vcc = HRZ_VCC(atm_vcc);
+ u16 channel = vcc->channel;
+ PRINTD (DBG_VCC|DBG_FLOW, "hrz_close");
+
+ // indicate unreadiness
+ clear_bit(ATM_VF_READY,&atm_vcc->flags);
+
+ if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
+ unsigned int i;
+
+ // let any TX on this channel that has started complete
+ // no restart, just keep trying
+ while (tx_hold (dev))
+ ;
+ // remove record of any tx_channel having been setup for this channel
+ for (i = 0; i < TX_CHANS; ++i)
+ if (dev->tx_channel_record[i] == channel) {
+ dev->tx_channel_record[i] = -1;
+ break;
+ }
+ if (dev->last_vc == channel)
+ dev->tx_last = -1;
+ tx_release (dev);
+ }
+
+ if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ // disable RXing - it tries quite hard
+ hrz_close_rx (dev, channel);
+ // forget the vcc - no more skbs will be pushed
+ if (atm_vcc != dev->rxer[channel])
+ PRINTK (KERN_ERR, "%s atm_vcc=%p rxer[channel]=%p",
+ "arghhh! we're going to die!",
+ atm_vcc, dev->rxer[channel]);
+ dev->rxer[channel] = NULL;
+ }
+
+ // atomically release our rate reservation
+ spin_lock (&dev->rate_lock);
+ PRINTD (DBG_QOS|DBG_VCC, "releasing %u TX PCR and %u RX PCR",
+ vcc->tx_rate, vcc->rx_rate);
+ dev->tx_avail += vcc->tx_rate;
+ dev->rx_avail += vcc->rx_rate;
+ spin_unlock (&dev->rate_lock);
+
+ // free our structure
+ kfree (vcc);
+ // say the VPI/VCI is free again
+ clear_bit(ATM_VF_ADDR,&atm_vcc->flags);
+}
+
+#if 0
+static int hrz_getsockopt (struct atm_vcc * atm_vcc, int level, int optname,
+ void *optval, int optlen) {
+ hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
+ PRINTD (DBG_FLOW|DBG_VCC, "hrz_getsockopt");
+ switch (level) {
+ case SOL_SOCKET:
+ switch (optname) {
+// case SO_BCTXOPT:
+// break;
+// case SO_BCRXOPT:
+// break;
+ default:
+ return -ENOPROTOOPT;
+ };
+ break;
+ }
+ return -EINVAL;
+}
+
+static int hrz_setsockopt (struct atm_vcc * atm_vcc, int level, int optname,
+ void *optval, unsigned int optlen) {
+ hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
+ PRINTD (DBG_FLOW|DBG_VCC, "hrz_setsockopt");
+ switch (level) {
+ case SOL_SOCKET:
+ switch (optname) {
+// case SO_BCTXOPT:
+// break;
+// case SO_BCRXOPT:
+// break;
+ default:
+ return -ENOPROTOOPT;
+ };
+ break;
+ }
+ return -EINVAL;
+}
+#endif
+
+#if 0
+static int hrz_ioctl (struct atm_dev * atm_dev, unsigned int cmd, void *arg) {
+ hrz_dev * dev = HRZ_DEV(atm_dev);
+ PRINTD (DBG_FLOW, "hrz_ioctl");
+ return -1;
+}
+
+unsigned char hrz_phy_get (struct atm_dev * atm_dev, unsigned long addr) {
+ hrz_dev * dev = HRZ_DEV(atm_dev);
+ PRINTD (DBG_FLOW, "hrz_phy_get");
+ return 0;
+}
+
+static void hrz_phy_put (struct atm_dev * atm_dev, unsigned char value,
+ unsigned long addr) {
+ hrz_dev * dev = HRZ_DEV(atm_dev);
+ PRINTD (DBG_FLOW, "hrz_phy_put");
+}
+
+static int hrz_change_qos (struct atm_vcc * atm_vcc, struct atm_qos *qos, int flgs) {
+ hrz_dev * dev = HRZ_DEV(vcc->dev);
+ PRINTD (DBG_FLOW, "hrz_change_qos");
+ return -1;
+}
+#endif
+
+/********** proc file contents **********/
+
+static int hrz_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) {
+ hrz_dev * dev = HRZ_DEV(atm_dev);
+ int left = *pos;
+ PRINTD (DBG_FLOW, "hrz_proc_read");
+
+ /* more diagnostics here? */
+
+#if 0
+ if (!left--) {
+ unsigned int count = sprintf (page, "vbr buckets:");
+ unsigned int i;
+ for (i = 0; i < TX_CHANS; ++i)
+ count += sprintf (page, " %u/%u",
+ query_tx_channel_config (dev, i, BUCKET_FULLNESS_ACCESS),
+ query_tx_channel_config (dev, i, BUCKET_CAPACITY_ACCESS));
+ count += sprintf (page+count, ".\n");
+ return count;
+ }
+#endif
+
+ if (!left--)
+ return sprintf (page,
+ "cells: TX %lu, RX %lu, HEC errors %lu, unassigned %lu.\n",
+ dev->tx_cell_count, dev->rx_cell_count,
+ dev->hec_error_count, dev->unassigned_cell_count);
+
+ if (!left--)
+ return sprintf (page,
+ "free cell buffers: TX %hu, RX %hu+%hu.\n",
+ rd_regw (dev, TX_FREE_BUFFER_COUNT_OFF),
+ rd_regw (dev, RX_FREE_BUFFER_COUNT_OFF),
+ dev->noof_spare_buffers);
+
+ if (!left--)
+ return sprintf (page,
+ "cps remaining: TX %u, RX %u\n",
+ dev->tx_avail, dev->rx_avail);
+
+ return 0;
+}
+
+static const struct atmdev_ops hrz_ops = {
+ .open = hrz_open,
+ .close = hrz_close,
+ .send = hrz_send,
+ .proc_read = hrz_proc_read,
+ .owner = THIS_MODULE,
+};
+
+static int hrz_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_ent)
+{
+ hrz_dev * dev;
+ int err = 0;
+
+ // adapter slot free, read resources from PCI configuration space
+ u32 iobase = pci_resource_start (pci_dev, 0);
+ u32 * membase = bus_to_virt (pci_resource_start (pci_dev, 1));
+ unsigned int irq;
+ unsigned char lat;
+
+ PRINTD (DBG_FLOW, "hrz_probe");
+
+ if (pci_enable_device(pci_dev))
+ return -EINVAL;
+
+ /* XXX DEV_LABEL is a guess */
+ if (!request_region(iobase, HRZ_IO_EXTENT, DEV_LABEL)) {
+ err = -EINVAL;
+ goto out_disable;
+ }
+
+ dev = kzalloc(sizeof(hrz_dev), GFP_KERNEL);
+ if (!dev) {
+ // perhaps we should be nice: deregister all adapters and abort?
+ PRINTD(DBG_ERR, "out of memory");
+ err = -ENOMEM;
+ goto out_release;
+ }
+
+ pci_set_drvdata(pci_dev, dev);
+
+ // grab IRQ and install handler - move this someplace more sensible
+ irq = pci_dev->irq;
+ if (request_irq(irq,
+ interrupt_handler,
+ IRQF_SHARED, /* irqflags guess */
+ DEV_LABEL, /* name guess */
+ dev)) {
+ PRINTD(DBG_WARN, "request IRQ failed!");
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ PRINTD(DBG_INFO, "found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p",
+ iobase, irq, membase);
+
+ dev->atm_dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &hrz_ops, -1,
+ NULL);
+ if (!(dev->atm_dev)) {
+ PRINTD(DBG_ERR, "failed to register Madge ATM adapter");
+ err = -EINVAL;
+ goto out_free_irq;
+ }
+
+ PRINTD(DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
+ dev->atm_dev->number, dev, dev->atm_dev);
+ dev->atm_dev->dev_data = (void *) dev;
+ dev->pci_dev = pci_dev;
+
+ // enable bus master accesses
+ pci_set_master(pci_dev);
+
+ // frobnicate latency (upwards, usually)
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &lat);
+ if (pci_lat) {
+ PRINTD(DBG_INFO, "%s PCI latency timer from %hu to %hu",
+ "changing", lat, pci_lat);
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat);
+ } else if (lat < MIN_PCI_LATENCY) {
+ PRINTK(KERN_INFO, "%s PCI latency timer from %hu to %hu",
+ "increasing", lat, MIN_PCI_LATENCY);
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY);
+ }
+
+ dev->iobase = iobase;
+ dev->irq = irq;
+ dev->membase = membase;
+
+ dev->rx_q_entry = dev->rx_q_reset = &memmap->rx_q_entries[0];
+ dev->rx_q_wrap = &memmap->rx_q_entries[RX_CHANS-1];
+
+ // these next three are performance hacks
+ dev->last_vc = -1;
+ dev->tx_last = -1;
+ dev->tx_idle = 0;
+
+ dev->tx_regions = 0;
+ dev->tx_bytes = 0;
+ dev->tx_skb = NULL;
+ dev->tx_iovec = NULL;
+
+ dev->tx_cell_count = 0;
+ dev->rx_cell_count = 0;
+ dev->hec_error_count = 0;
+ dev->unassigned_cell_count = 0;
+
+ dev->noof_spare_buffers = 0;
+
+ {
+ unsigned int i;
+ for (i = 0; i < TX_CHANS; ++i)
+ dev->tx_channel_record[i] = -1;
+ }
+
+ dev->flags = 0;
+
+ // Allocate cell rates and remember ASIC version
+ // Fibre: ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
+ // Copper: (WRONG) we want 6 into the above, close to 25Mb/s
+ // Copper: (plagarise!) 25600000/8/270*260/53 - n/53
+
+ if (hrz_init(dev)) {
+ // to be really pedantic, this should be ATM_OC3c_PCR
+ dev->tx_avail = ATM_OC3_PCR;
+ dev->rx_avail = ATM_OC3_PCR;
+ set_bit(ultra, &dev->flags); // NOT "|= ultra" !
+ } else {
+ dev->tx_avail = ((25600000/8)*26)/(27*53);
+ dev->rx_avail = ((25600000/8)*26)/(27*53);
+ PRINTD(DBG_WARN, "Buggy ASIC: no TX bus-mastering.");
+ }
+
+ // rate changes spinlock
+ spin_lock_init(&dev->rate_lock);
+
+ // on-board memory access spinlock; we want atomic reads and
+ // writes to adapter memory (handles IRQ and SMP)
+ spin_lock_init(&dev->mem_lock);
+
+ init_waitqueue_head(&dev->tx_queue);
+
+ // vpi in 0..4, vci in 6..10
+ dev->atm_dev->ci_range.vpi_bits = vpi_bits;
+ dev->atm_dev->ci_range.vci_bits = 10-vpi_bits;
+
+ init_timer(&dev->housekeeping);
+ dev->housekeeping.function = do_housekeeping;
+ dev->housekeeping.data = (unsigned long) dev;
+ mod_timer(&dev->housekeeping, jiffies);
+
+out:
+ return err;
+
+out_free_irq:
+ free_irq(dev->irq, dev);
+out_free:
+ kfree(dev);
+out_release:
+ release_region(iobase, HRZ_IO_EXTENT);
+out_disable:
+ pci_disable_device(pci_dev);
+ goto out;
+}
+
+static void hrz_remove_one(struct pci_dev *pci_dev)
+{
+ hrz_dev *dev;
+
+ dev = pci_get_drvdata(pci_dev);
+
+ PRINTD(DBG_INFO, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
+ del_timer_sync(&dev->housekeeping);
+ hrz_reset(dev);
+ atm_dev_deregister(dev->atm_dev);
+ free_irq(dev->irq, dev);
+ release_region(dev->iobase, HRZ_IO_EXTENT);
+ kfree(dev);
+
+ pci_disable_device(pci_dev);
+}
+
+static void __init hrz_check_args (void) {
+#ifdef DEBUG_HORIZON
+ PRINTK (KERN_NOTICE, "debug bitmap is %hx", debug &= DBG_MASK);
+#else
+ if (debug)
+ PRINTK (KERN_NOTICE, "no debug support in this image");
+#endif
+
+ if (vpi_bits > HRZ_MAX_VPI)
+ PRINTK (KERN_ERR, "vpi_bits has been limited to %hu",
+ vpi_bits = HRZ_MAX_VPI);
+
+ if (max_tx_size < 0 || max_tx_size > TX_AAL5_LIMIT)
+ PRINTK (KERN_NOTICE, "max_tx_size has been limited to %hu",
+ max_tx_size = TX_AAL5_LIMIT);
+
+ if (max_rx_size < 0 || max_rx_size > RX_AAL5_LIMIT)
+ PRINTK (KERN_NOTICE, "max_rx_size has been limited to %hu",
+ max_rx_size = RX_AAL5_LIMIT);
+
+ return;
+}
+
+MODULE_AUTHOR(maintainer_string);
+MODULE_DESCRIPTION(description_string);
+MODULE_LICENSE("GPL");
+module_param(debug, ushort, 0644);
+module_param(vpi_bits, ushort, 0);
+module_param(max_tx_size, int, 0);
+module_param(max_rx_size, int, 0);
+module_param(pci_lat, byte, 0);
+MODULE_PARM_DESC(debug, "debug bitmap, see .h file");
+MODULE_PARM_DESC(vpi_bits, "number of bits (0..4) to allocate to VPIs");
+MODULE_PARM_DESC(max_tx_size, "maximum size of TX AAL5 frames");
+MODULE_PARM_DESC(max_rx_size, "maximum size of RX AAL5 frames");
+MODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles");
+
+static struct pci_device_id hrz_pci_tbl[] = {
+ { PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, hrz_pci_tbl);
+
+static struct pci_driver hrz_driver = {
+ .name = "horizon",
+ .probe = hrz_probe,
+ .remove = hrz_remove_one,
+ .id_table = hrz_pci_tbl,
+};
+
+/********** module entry **********/
+
+static int __init hrz_module_init (void) {
+ // sanity check - cast is needed since printk does not support %Zu
+ if (sizeof(struct MEMMAP) != 128*1024/4) {
+ PRINTK (KERN_ERR, "Fix struct MEMMAP (is %lu fakewords).",
+ (unsigned long) sizeof(struct MEMMAP));
+ return -ENOMEM;
+ }
+
+ show_version();
+
+ // check arguments
+ hrz_check_args();
+
+ // get the juice
+ return pci_register_driver(&hrz_driver);
+}
+
+/********** module exit **********/
+
+static void __exit hrz_module_exit (void) {
+ PRINTD (DBG_FLOW, "cleanup_module");
+
+ pci_unregister_driver(&hrz_driver);
+}
+
+module_init(hrz_module_init);
+module_exit(hrz_module_exit);
diff --git a/drivers/atm/horizon.h b/drivers/atm/horizon.h
new file mode 100644
index 000000000..b48859d0d
--- /dev/null
+++ b/drivers/atm/horizon.h
@@ -0,0 +1,507 @@
+/*
+ Madge Horizon ATM Adapter driver.
+ Copyright (C) 1995-1999 Madge Networks Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian
+ system and in the file COPYING in the Linux kernel source.
+*/
+
+/*
+ IMPORTANT NOTE: Madge Networks no longer makes the adapters
+ supported by this driver and makes no commitment to maintain it.
+*/
+
+/* too many macros - change to inline functions */
+
+#ifndef DRIVER_ATM_HORIZON_H
+#define DRIVER_ATM_HORIZON_H
+
+
+#ifdef CONFIG_ATM_HORIZON_DEBUG
+#define DEBUG_HORIZON
+#endif
+
+#define DEV_LABEL "hrz"
+
+#ifndef PCI_VENDOR_ID_MADGE
+#define PCI_VENDOR_ID_MADGE 0x10B6
+#endif
+#ifndef PCI_DEVICE_ID_MADGE_HORIZON
+#define PCI_DEVICE_ID_MADGE_HORIZON 0x1000
+#endif
+
+// diagnostic output
+
+#define PRINTK(severity,format,args...) \
+ printk(severity DEV_LABEL ": " format "\n" , ## args)
+
+#ifdef DEBUG_HORIZON
+
+#define DBG_ERR 0x0001
+#define DBG_WARN 0x0002
+#define DBG_INFO 0x0004
+#define DBG_VCC 0x0008
+#define DBG_QOS 0x0010
+#define DBG_TX 0x0020
+#define DBG_RX 0x0040
+#define DBG_SKB 0x0080
+#define DBG_IRQ 0x0100
+#define DBG_FLOW 0x0200
+#define DBG_BUS 0x0400
+#define DBG_REGS 0x0800
+#define DBG_DATA 0x1000
+#define DBG_MASK 0x1fff
+
+/* the ## prevents the annoying double expansion of the macro arguments */
+/* KERN_INFO is used since KERN_DEBUG often does not make it to the console */
+#define PRINTDB(bits,format,args...) \
+ ( (debug & (bits)) ? printk (KERN_INFO DEV_LABEL ": " format , ## args) : 1 )
+#define PRINTDM(bits,format,args...) \
+ ( (debug & (bits)) ? printk (format , ## args) : 1 )
+#define PRINTDE(bits,format,args...) \
+ ( (debug & (bits)) ? printk (format "\n" , ## args) : 1 )
+#define PRINTD(bits,format,args...) \
+ ( (debug & (bits)) ? printk (KERN_INFO DEV_LABEL ": " format "\n" , ## args) : 1 )
+
+#else
+
+#define PRINTD(bits,format,args...)
+#define PRINTDB(bits,format,args...)
+#define PRINTDM(bits,format,args...)
+#define PRINTDE(bits,format,args...)
+
+#endif
+
+#define PRINTDD(sec,fmt,args...)
+#define PRINTDDB(sec,fmt,args...)
+#define PRINTDDM(sec,fmt,args...)
+#define PRINTDDE(sec,fmt,args...)
+
+// fixed constants
+
+#define SPARE_BUFFER_POOL_SIZE MAX_VCS
+#define HRZ_MAX_VPI 4
+#define MIN_PCI_LATENCY 48 // 24 IS TOO SMALL
+
+/* Horizon specific bits */
+/* Register offsets */
+
+#define HRZ_IO_EXTENT 0x80
+
+#define DATA_PORT_OFF 0x00
+#define TX_CHANNEL_PORT_OFF 0x04
+#define TX_DESCRIPTOR_PORT_OFF 0x08
+#define MEMORY_PORT_OFF 0x0C
+#define MEM_WR_ADDR_REG_OFF 0x14
+#define MEM_RD_ADDR_REG_OFF 0x18
+#define CONTROL_0_REG 0x1C
+#define INT_SOURCE_REG_OFF 0x20
+#define INT_ENABLE_REG_OFF 0x24
+#define MASTER_RX_ADDR_REG_OFF 0x28
+#define MASTER_RX_COUNT_REG_OFF 0x2C
+#define MASTER_TX_ADDR_REG_OFF 0x30
+#define MASTER_TX_COUNT_REG_OFF 0x34
+#define TX_DESCRIPTOR_REG_OFF 0x38
+#define TX_CHANNEL_CONFIG_COMMAND_OFF 0x40
+#define TX_CHANNEL_CONFIG_DATA_OFF 0x44
+#define TX_FREE_BUFFER_COUNT_OFF 0x48
+#define RX_FREE_BUFFER_COUNT_OFF 0x4C
+#define TX_CONFIG_OFF 0x50
+#define TX_STATUS_OFF 0x54
+#define RX_CONFIG_OFF 0x58
+#define RX_LINE_CONFIG_OFF 0x5C
+#define RX_QUEUE_RD_PTR_OFF 0x60
+#define RX_QUEUE_WR_PTR_OFF 0x64
+#define MAX_AAL5_CELL_COUNT_OFF 0x68
+#define RX_CHANNEL_PORT_OFF 0x6C
+#define TX_CELL_COUNT_OFF 0x70
+#define RX_CELL_COUNT_OFF 0x74
+#define HEC_ERROR_COUNT_OFF 0x78
+#define UNASSIGNED_CELL_COUNT_OFF 0x7C
+
+/* Register bit definitions */
+
+/* Control 0 register */
+
+#define SEEPROM_DO 0x00000001
+#define SEEPROM_DI 0x00000002
+#define SEEPROM_SK 0x00000004
+#define SEEPROM_CS 0x00000008
+#define DEBUG_BIT_0 0x00000010
+#define DEBUG_BIT_1 0x00000020
+#define DEBUG_BIT_2 0x00000040
+// RESERVED 0x00000080
+#define DEBUG_BIT_0_OE 0x00000100
+#define DEBUG_BIT_1_OE 0x00000200
+#define DEBUG_BIT_2_OE 0x00000400
+// RESERVED 0x00000800
+#define DEBUG_BIT_0_STATE 0x00001000
+#define DEBUG_BIT_1_STATE 0x00002000
+#define DEBUG_BIT_2_STATE 0x00004000
+// RESERVED 0x00008000
+#define GENERAL_BIT_0 0x00010000
+#define GENERAL_BIT_1 0x00020000
+#define GENERAL_BIT_2 0x00040000
+#define GENERAL_BIT_3 0x00080000
+#define RESET_HORIZON 0x00100000
+#define RESET_ATM 0x00200000
+#define RESET_RX 0x00400000
+#define RESET_TX 0x00800000
+#define RESET_HOST 0x01000000
+// RESERVED 0x02000000
+#define TARGET_RETRY_DISABLE 0x04000000
+#define ATM_LAYER_SELECT 0x08000000
+#define ATM_LAYER_STATUS 0x10000000
+// RESERVED 0xE0000000
+
+/* Interrupt source and enable registers */
+
+#define RX_DATA_AV 0x00000001
+#define RX_DISABLED 0x00000002
+#define TIMING_MARKER 0x00000004
+#define FORCED 0x00000008
+#define RX_BUS_MASTER_COMPLETE 0x00000010
+#define TX_BUS_MASTER_COMPLETE 0x00000020
+#define ABR_TX_CELL_COUNT_INT 0x00000040
+#define DEBUG_INT 0x00000080
+// RESERVED 0xFFFFFF00
+
+/* PIO and Bus Mastering */
+
+#define MAX_PIO_COUNT 0x000000ff // 255 - make tunable?
+// 8188 is a hard limit for bus mastering
+#define MAX_TRANSFER_COUNT 0x00001ffc // 8188
+#define MASTER_TX_AUTO_APPEND_DESC 0x80000000
+
+/* TX channel config command port */
+
+#define PCR_TIMER_ACCESS 0x0000
+#define SCR_TIMER_ACCESS 0x0001
+#define BUCKET_CAPACITY_ACCESS 0x0002
+#define BUCKET_FULLNESS_ACCESS 0x0003
+#define RATE_TYPE_ACCESS 0x0004
+// UNUSED 0x00F8
+#define TX_CHANNEL_CONFIG_MULT 0x0100
+// UNUSED 0xF800
+#define BUCKET_MAX_SIZE 0x003f
+
+/* TX channel config data port */
+
+#define CLOCK_SELECT_SHIFT 4
+#define CLOCK_DISABLE 0x00ff
+
+#define IDLE_RATE_TYPE 0x0
+#define ABR_RATE_TYPE 0x1
+#define VBR_RATE_TYPE 0x2
+#define CBR_RATE_TYPE 0x3
+
+/* TX config register */
+
+#define DRVR_DRVRBAR_ENABLE 0x0001
+#define TXCLK_MUX_SELECT_RCLK 0x0002
+#define TRANSMIT_TIMING_MARKER 0x0004
+#define LOOPBACK_TIMING_MARKER 0x0008
+#define TX_TEST_MODE_16MHz 0x0000
+#define TX_TEST_MODE_8MHz 0x0010
+#define TX_TEST_MODE_5_33MHz 0x0020
+#define TX_TEST_MODE_4MHz 0x0030
+#define TX_TEST_MODE_3_2MHz 0x0040
+#define TX_TEST_MODE_2_66MHz 0x0050
+#define TX_TEST_MODE_2_29MHz 0x0060
+#define TX_NORMAL_OPERATION 0x0070
+#define ABR_ROUND_ROBIN 0x0080
+
+/* TX status register */
+
+#define IDLE_CHANNELS_MASK 0x00FF
+#define ABR_CELL_COUNT_REACHED_MULT 0x0100
+#define ABR_CELL_COUNT_REACHED_MASK 0xFF
+
+/* RX config register */
+
+#define NON_USER_CELLS_IN_ONE_CHANNEL 0x0008
+#define RX_ENABLE 0x0010
+#define IGNORE_UNUSED_VPI_VCI_BITS_SET 0x0000
+#define NON_USER_UNUSED_VPI_VCI_BITS_SET 0x0020
+#define DISCARD_UNUSED_VPI_VCI_BITS_SET 0x0040
+
+/* RX line config register */
+
+#define SIGNAL_LOSS 0x0001
+#define FREQUENCY_DETECT_ERROR 0x0002
+#define LOCK_DETECT_ERROR 0x0004
+#define SELECT_INTERNAL_LOOPBACK 0x0008
+#define LOCK_DETECT_ENABLE 0x0010
+#define FREQUENCY_DETECT_ENABLE 0x0020
+#define USER_FRAQ 0x0040
+#define GXTALOUT_SELECT_DIV4 0x0080
+#define GXTALOUT_SELECT_NO_GATING 0x0100
+#define TIMING_MARKER_RECEIVED 0x0200
+
+/* RX channel port */
+
+#define RX_CHANNEL_MASK 0x03FF
+// UNUSED 0x3C00
+#define FLUSH_CHANNEL 0x4000
+#define RX_CHANNEL_UPDATE_IN_PROGRESS 0x8000
+
+/* Receive queue entry */
+
+#define RX_Q_ENTRY_LENGTH_MASK 0x0000FFFF
+#define RX_Q_ENTRY_CHANNEL_SHIFT 16
+#define SIMONS_DODGEY_MARKER 0x08000000
+#define RX_CONGESTION_EXPERIENCED 0x10000000
+#define RX_CRC_10_OK 0x20000000
+#define RX_CRC_32_OK 0x40000000
+#define RX_COMPLETE_FRAME 0x80000000
+
+/* Offsets and constants for use with the buffer memory */
+
+/* Buffer pointers and channel types */
+
+#define BUFFER_PTR_MASK 0x0000FFFF
+#define RX_INT_THRESHOLD_MULT 0x00010000
+#define RX_INT_THRESHOLD_MASK 0x07FF
+#define INT_EVERY_N_CELLS 0x08000000
+#define CONGESTION_EXPERIENCED 0x10000000
+#define FIRST_CELL_OF_AAL5_FRAME 0x20000000
+#define CHANNEL_TYPE_AAL5 0x00000000
+#define CHANNEL_TYPE_RAW_CELLS 0x40000000
+#define CHANNEL_TYPE_AAL3_4 0x80000000
+
+/* Buffer status stuff */
+
+#define BUFF_STATUS_MASK 0x00030000
+#define BUFF_STATUS_EMPTY 0x00000000
+#define BUFF_STATUS_CELL_AV 0x00010000
+#define BUFF_STATUS_LAST_CELL_AV 0x00020000
+
+/* Transmit channel stuff */
+
+/* Receive channel stuff */
+
+#define RX_CHANNEL_DISABLED 0x00000000
+#define RX_CHANNEL_IDLE 0x00000001
+
+/* General things */
+
+#define INITIAL_CRC 0xFFFFFFFF
+
+// A Horizon u32, a byte! Really nasty. Horizon pointers are (32 bit)
+// word addresses and so standard C pointer operations break (as they
+// assume byte addresses); so we pretend that Horizon words (and word
+// pointers) are bytes (and byte pointers) for the purposes of having
+// a memory map that works.
+
+typedef u8 HDW;
+
+typedef struct cell_buf {
+ HDW payload[12];
+ HDW next;
+ HDW cell_count; // AAL5 rx bufs
+ HDW res;
+ union {
+ HDW partial_crc; // AAL5 rx bufs
+ HDW cell_header; // RAW bufs
+ } u;
+} cell_buf;
+
+typedef struct tx_ch_desc {
+ HDW rd_buf_type;
+ HDW wr_buf_type;
+ HDW partial_crc;
+ HDW cell_header;
+} tx_ch_desc;
+
+typedef struct rx_ch_desc {
+ HDW wr_buf_type;
+ HDW rd_buf_type;
+} rx_ch_desc;
+
+typedef struct rx_q_entry {
+ HDW entry;
+} rx_q_entry;
+
+#define TX_CHANS 8
+#define RX_CHANS 1024
+#define RX_QS 1024
+#define MAX_VCS RX_CHANS
+
+/* Horizon buffer memory map */
+
+// TX Channel Descriptors 2
+// TX Initial Buffers 8 // TX_CHANS
+#define BUFN1_SIZE 118 // (126 - TX_CHANS)
+// RX/TX Start/End Buffers 4
+#define BUFN2_SIZE 124
+// RX Queue Entries 64
+#define BUFN3_SIZE 192
+// RX Channel Descriptors 128
+#define BUFN4_SIZE 1408
+// TOTAL cell_buff chunks 2048
+
+// cell_buf bufs[2048];
+// HDW dws[32768];
+
+typedef struct MEMMAP {
+ tx_ch_desc tx_descs[TX_CHANS]; // 8 * 4 = 32 , 0x0020
+ cell_buf inittxbufs[TX_CHANS]; // these are really
+ cell_buf bufn1[BUFN1_SIZE]; // part of this pool
+ cell_buf txfreebufstart;
+ cell_buf txfreebufend;
+ cell_buf rxfreebufstart;
+ cell_buf rxfreebufend; // 8+118+1+1+1+1+124 = 254
+ cell_buf bufn2[BUFN2_SIZE]; // 16 * 254 = 4064 , 0x1000
+ rx_q_entry rx_q_entries[RX_QS]; // 1 * 1024 = 1024 , 0x1400
+ cell_buf bufn3[BUFN3_SIZE]; // 16 * 192 = 3072 , 0x2000
+ rx_ch_desc rx_descs[MAX_VCS]; // 2 * 1024 = 2048 , 0x2800
+ cell_buf bufn4[BUFN4_SIZE]; // 16 * 1408 = 22528 , 0x8000
+} MEMMAP;
+
+#define memmap ((MEMMAP *)0)
+
+/* end horizon specific bits */
+
+typedef enum {
+ aal0,
+ aal34,
+ aal5
+} hrz_aal;
+
+typedef enum {
+ tx_busy,
+ rx_busy,
+ ultra
+} hrz_flags;
+
+// a single struct pointed to by atm_vcc->dev_data
+
+typedef struct {
+ unsigned int tx_rate;
+ unsigned int rx_rate;
+ u16 channel;
+ u16 tx_xbr_bits;
+ u16 tx_pcr_bits;
+#if 0
+ u16 tx_scr_bits;
+ u16 tx_bucket_bits;
+#endif
+ hrz_aal aal;
+} hrz_vcc;
+
+struct hrz_dev {
+
+ u32 iobase;
+ u32 * membase;
+
+ struct sk_buff * rx_skb; // skb being RXed
+ unsigned int rx_bytes; // bytes remaining to RX within region
+ void * rx_addr; // addr to send bytes to (for PIO)
+ unsigned int rx_channel; // channel that the skb is going out on
+
+ struct sk_buff * tx_skb; // skb being TXed
+ unsigned int tx_bytes; // bytes remaining to TX within region
+ void * tx_addr; // addr to send bytes from (for PIO)
+ struct iovec * tx_iovec; // remaining regions
+ unsigned int tx_regions; // number of remaining regions
+
+ spinlock_t mem_lock;
+ wait_queue_head_t tx_queue;
+
+ u8 irq;
+ unsigned long flags;
+ u8 tx_last;
+ u8 tx_idle;
+
+ rx_q_entry * rx_q_reset;
+ rx_q_entry * rx_q_entry;
+ rx_q_entry * rx_q_wrap;
+
+ struct atm_dev * atm_dev;
+
+ u32 last_vc;
+
+ int noof_spare_buffers;
+ u16 spare_buffers[SPARE_BUFFER_POOL_SIZE];
+
+ u16 tx_channel_record[TX_CHANS];
+
+ // this is what we follow when we get incoming data
+ u32 txer[MAX_VCS/32];
+ struct atm_vcc * rxer[MAX_VCS];
+
+ // cell rate allocation
+ spinlock_t rate_lock;
+ unsigned int rx_avail;
+ unsigned int tx_avail;
+
+ // dev stats
+ unsigned long tx_cell_count;
+ unsigned long rx_cell_count;
+ unsigned long hec_error_count;
+ unsigned long unassigned_cell_count;
+
+ struct pci_dev * pci_dev;
+ struct timer_list housekeeping;
+};
+
+typedef struct hrz_dev hrz_dev;
+
+/* macros for use later */
+
+#define BUF_PTR(cbptr) ((cbptr) - (cell_buf *) 0)
+
+#define INTERESTING_INTERRUPTS \
+ (RX_DATA_AV | RX_DISABLED | TX_BUS_MASTER_COMPLETE | RX_BUS_MASTER_COMPLETE)
+
+// 190 cells by default (192 TX buffers - 2 elbow room, see docs)
+#define TX_AAL5_LIMIT (190*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER) // 9112
+
+// Have enough RX buffers (unless we allow other buffer splits)
+#define RX_AAL5_LIMIT ATM_MAX_AAL5_PDU
+
+/* multi-statement macro protector */
+#define DW(x) do{ x } while(0)
+
+#define HRZ_DEV(atm_dev) ((hrz_dev *) (atm_dev)->dev_data)
+#define HRZ_VCC(atm_vcc) ((hrz_vcc *) (atm_vcc)->dev_data)
+
+/* Turn the LEDs on and off */
+// The LEDs bits are upside down in that setting the bit in the debug
+// register will turn the appropriate LED off.
+
+#define YELLOW_LED DEBUG_BIT_0
+#define GREEN_LED DEBUG_BIT_1
+#define YELLOW_LED_OE DEBUG_BIT_0_OE
+#define GREEN_LED_OE DEBUG_BIT_1_OE
+
+#define GREEN_LED_OFF(dev) \
+ wr_regl (dev, CONTROL_0_REG, rd_regl (dev, CONTROL_0_REG) | GREEN_LED)
+#define GREEN_LED_ON(dev) \
+ wr_regl (dev, CONTROL_0_REG, rd_regl (dev, CONTROL_0_REG) &~ GREEN_LED)
+#define YELLOW_LED_OFF(dev) \
+ wr_regl (dev, CONTROL_0_REG, rd_regl (dev, CONTROL_0_REG) | YELLOW_LED)
+#define YELLOW_LED_ON(dev) \
+ wr_regl (dev, CONTROL_0_REG, rd_regl (dev, CONTROL_0_REG) &~ YELLOW_LED)
+
+typedef enum {
+ round_up,
+ round_down,
+ round_nearest
+} rounding;
+
+#endif /* DRIVER_ATM_HORIZON_H */
diff --git a/drivers/atm/idt77105.c b/drivers/atm/idt77105.c
new file mode 100644
index 000000000..909c95bd7
--- /dev/null
+++ b/drivers/atm/idt77105.c
@@ -0,0 +1,378 @@
+/* drivers/atm/idt77105.c - IDT77105 (PHY) driver */
+
+/* Written 1999 by Greg Banks, NEC Australia <gnb@linuxfan.com>. Based on suni.c */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/atm_idt77105.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <asm/param.h>
+#include <asm/uaccess.h>
+
+#include "idt77105.h"
+
+#undef GENERAL_DEBUG
+
+#ifdef GENERAL_DEBUG
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+
+struct idt77105_priv {
+ struct idt77105_stats stats; /* link diagnostics */
+ struct atm_dev *dev; /* device back-pointer */
+ struct idt77105_priv *next;
+ int loop_mode;
+ unsigned char old_mcr; /* storage of MCR reg while signal lost */
+};
+
+static DEFINE_SPINLOCK(idt77105_priv_lock);
+
+#define PRIV(dev) ((struct idt77105_priv *) dev->phy_data)
+
+#define PUT(val,reg) dev->ops->phy_put(dev,val,IDT77105_##reg)
+#define GET(reg) dev->ops->phy_get(dev,IDT77105_##reg)
+
+static void idt77105_stats_timer_func(unsigned long);
+static void idt77105_restart_timer_func(unsigned long);
+
+
+static DEFINE_TIMER(stats_timer, idt77105_stats_timer_func, 0, 0);
+static DEFINE_TIMER(restart_timer, idt77105_restart_timer_func, 0, 0);
+static int start_timer = 1;
+static struct idt77105_priv *idt77105_all = NULL;
+
+/*
+ * Retrieve the value of one of the IDT77105's counters.
+ * `counter' is one of the IDT77105_CTRSEL_* constants.
+ */
+static u16 get_counter(struct atm_dev *dev, int counter)
+{
+ u16 val;
+
+ /* write the counter bit into PHY register 6 */
+ PUT(counter, CTRSEL);
+ /* read the low 8 bits from register 4 */
+ val = GET(CTRLO);
+ /* read the high 8 bits from register 5 */
+ val |= GET(CTRHI)<<8;
+
+ return val;
+}
+
+/*
+ * Timer function called every second to gather statistics
+ * from the 77105. This is done because the h/w registers
+ * will overflow if not read at least once per second. The
+ * kernel's stats are much higher precision. Also, having
+ * a separate copy of the stats allows implementation of
+ * an ioctl which gathers the stats *without* zero'ing them.
+ */
+static void idt77105_stats_timer_func(unsigned long dummy)
+{
+ struct idt77105_priv *walk;
+ struct atm_dev *dev;
+ struct idt77105_stats *stats;
+
+ DPRINTK("IDT77105 gathering statistics\n");
+ for (walk = idt77105_all; walk; walk = walk->next) {
+ dev = walk->dev;
+
+ stats = &walk->stats;
+ stats->symbol_errors += get_counter(dev, IDT77105_CTRSEL_SEC);
+ stats->tx_cells += get_counter(dev, IDT77105_CTRSEL_TCC);
+ stats->rx_cells += get_counter(dev, IDT77105_CTRSEL_RCC);
+ stats->rx_hec_errors += get_counter(dev, IDT77105_CTRSEL_RHEC);
+ }
+ if (!start_timer) mod_timer(&stats_timer,jiffies+IDT77105_STATS_TIMER_PERIOD);
+}
+
+
+/*
+ * A separate timer func which handles restarting PHY chips which
+ * have had the cable re-inserted after being pulled out. This is
+ * done by polling the Good Signal Bit in the Interrupt Status
+ * register every 5 seconds. The other technique (checking Good
+ * Signal Bit in the interrupt handler) cannot be used because PHY
+ * interrupts need to be disabled when the cable is pulled out
+ * to avoid lots of spurious cell error interrupts.
+ */
+static void idt77105_restart_timer_func(unsigned long dummy)
+{
+ struct idt77105_priv *walk;
+ struct atm_dev *dev;
+ unsigned char istat;
+
+ DPRINTK("IDT77105 checking for cable re-insertion\n");
+ for (walk = idt77105_all; walk; walk = walk->next) {
+ dev = walk->dev;
+
+ if (dev->signal != ATM_PHY_SIG_LOST)
+ continue;
+
+ istat = GET(ISTAT); /* side effect: clears all interrupt status bits */
+ if (istat & IDT77105_ISTAT_GOODSIG) {
+ /* Found signal again */
+ atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND);
+ printk(KERN_NOTICE "%s(itf %d): signal detected again\n",
+ dev->type,dev->number);
+ /* flush the receive FIFO */
+ PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG);
+ /* re-enable interrupts */
+ PUT( walk->old_mcr ,MCR);
+ }
+ }
+ if (!start_timer) mod_timer(&restart_timer,jiffies+IDT77105_RESTART_TIMER_PERIOD);
+}
+
+
+static int fetch_stats(struct atm_dev *dev,struct idt77105_stats __user *arg,int zero)
+{
+ unsigned long flags;
+ struct idt77105_stats stats;
+
+ spin_lock_irqsave(&idt77105_priv_lock, flags);
+ memcpy(&stats, &PRIV(dev)->stats, sizeof(struct idt77105_stats));
+ if (zero)
+ memset(&PRIV(dev)->stats, 0, sizeof(struct idt77105_stats));
+ spin_unlock_irqrestore(&idt77105_priv_lock, flags);
+ if (arg == NULL)
+ return 0;
+ return copy_to_user(arg, &stats,
+ sizeof(struct idt77105_stats)) ? -EFAULT : 0;
+}
+
+
+static int set_loopback(struct atm_dev *dev,int mode)
+{
+ int diag;
+
+ diag = GET(DIAG) & ~IDT77105_DIAG_LCMASK;
+ switch (mode) {
+ case ATM_LM_NONE:
+ break;
+ case ATM_LM_LOC_ATM:
+ diag |= IDT77105_DIAG_LC_PHY_LOOPBACK;
+ break;
+ case ATM_LM_RMT_ATM:
+ diag |= IDT77105_DIAG_LC_LINE_LOOPBACK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ PUT(diag,DIAG);
+ printk(KERN_NOTICE "%s(%d) Loopback mode is: %s\n", dev->type,
+ dev->number,
+ (mode == ATM_LM_NONE ? "NONE" :
+ (mode == ATM_LM_LOC_ATM ? "DIAG (local)" :
+ (mode == IDT77105_DIAG_LC_LINE_LOOPBACK ? "LOOP (remote)" :
+ "unknown")))
+ );
+ PRIV(dev)->loop_mode = mode;
+ return 0;
+}
+
+
+static int idt77105_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
+{
+ printk(KERN_NOTICE "%s(%d) idt77105_ioctl() called\n",dev->type,dev->number);
+ switch (cmd) {
+ case IDT77105_GETSTATZ:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ /* fall through */
+ case IDT77105_GETSTAT:
+ return fetch_stats(dev, arg, cmd == IDT77105_GETSTATZ);
+ case ATM_SETLOOP:
+ return set_loopback(dev,(int)(unsigned long) arg);
+ case ATM_GETLOOP:
+ return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ?
+ -EFAULT : 0;
+ case ATM_QUERYLOOP:
+ return put_user(ATM_LM_LOC_ATM | ATM_LM_RMT_ATM,
+ (int __user *) arg) ? -EFAULT : 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+
+
+static void idt77105_int(struct atm_dev *dev)
+{
+ unsigned char istat;
+
+ istat = GET(ISTAT); /* side effect: clears all interrupt status bits */
+
+ DPRINTK("IDT77105 generated an interrupt, istat=%02x\n", (unsigned)istat);
+
+ if (istat & IDT77105_ISTAT_RSCC) {
+ /* Rx Signal Condition Change - line went up or down */
+ if (istat & IDT77105_ISTAT_GOODSIG) { /* signal detected again */
+ /* This should not happen (restart timer does it) but JIC */
+ atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND);
+ } else { /* signal lost */
+ /*
+ * Disable interrupts and stop all transmission and
+ * reception - the restart timer will restore these.
+ */
+ PRIV(dev)->old_mcr = GET(MCR);
+ PUT(
+ (PRIV(dev)->old_mcr|
+ IDT77105_MCR_DREC|
+ IDT77105_MCR_DRIC|
+ IDT77105_MCR_HALTTX
+ ) & ~IDT77105_MCR_EIP, MCR);
+ atm_dev_signal_change(dev, ATM_PHY_SIG_LOST);
+ printk(KERN_NOTICE "%s(itf %d): signal lost\n",
+ dev->type,dev->number);
+ }
+ }
+
+ if (istat & IDT77105_ISTAT_RFO) {
+ /* Rx FIFO Overrun -- perform a FIFO flush */
+ PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG);
+ printk(KERN_NOTICE "%s(itf %d): receive FIFO overrun\n",
+ dev->type,dev->number);
+ }
+#ifdef GENERAL_DEBUG
+ if (istat & (IDT77105_ISTAT_HECERR | IDT77105_ISTAT_SCR |
+ IDT77105_ISTAT_RSE)) {
+ /* normally don't care - just report in stats */
+ printk(KERN_NOTICE "%s(itf %d): received cell with error\n",
+ dev->type,dev->number);
+ }
+#endif
+}
+
+
+static int idt77105_start(struct atm_dev *dev)
+{
+ unsigned long flags;
+
+ if (!(dev->dev_data = kmalloc(sizeof(struct idt77105_priv),GFP_KERNEL)))
+ return -ENOMEM;
+ PRIV(dev)->dev = dev;
+ spin_lock_irqsave(&idt77105_priv_lock, flags);
+ PRIV(dev)->next = idt77105_all;
+ idt77105_all = PRIV(dev);
+ spin_unlock_irqrestore(&idt77105_priv_lock, flags);
+ memset(&PRIV(dev)->stats,0,sizeof(struct idt77105_stats));
+
+ /* initialise dev->signal from Good Signal Bit */
+ atm_dev_signal_change(dev,
+ GET(ISTAT) & IDT77105_ISTAT_GOODSIG ?
+ ATM_PHY_SIG_FOUND : ATM_PHY_SIG_LOST);
+ if (dev->signal == ATM_PHY_SIG_LOST)
+ printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type,
+ dev->number);
+
+ /* initialise loop mode from hardware */
+ switch ( GET(DIAG) & IDT77105_DIAG_LCMASK ) {
+ case IDT77105_DIAG_LC_NORMAL:
+ PRIV(dev)->loop_mode = ATM_LM_NONE;
+ break;
+ case IDT77105_DIAG_LC_PHY_LOOPBACK:
+ PRIV(dev)->loop_mode = ATM_LM_LOC_ATM;
+ break;
+ case IDT77105_DIAG_LC_LINE_LOOPBACK:
+ PRIV(dev)->loop_mode = ATM_LM_RMT_ATM;
+ break;
+ }
+
+ /* enable interrupts, e.g. on loss of signal */
+ PRIV(dev)->old_mcr = GET(MCR);
+ if (dev->signal == ATM_PHY_SIG_FOUND) {
+ PRIV(dev)->old_mcr |= IDT77105_MCR_EIP;
+ PUT(PRIV(dev)->old_mcr, MCR);
+ }
+
+
+ idt77105_stats_timer_func(0); /* clear 77105 counters */
+ (void) fetch_stats(dev,NULL,1); /* clear kernel counters */
+
+ spin_lock_irqsave(&idt77105_priv_lock, flags);
+ if (start_timer) {
+ start_timer = 0;
+
+ init_timer(&stats_timer);
+ stats_timer.expires = jiffies+IDT77105_STATS_TIMER_PERIOD;
+ stats_timer.function = idt77105_stats_timer_func;
+ add_timer(&stats_timer);
+
+ init_timer(&restart_timer);
+ restart_timer.expires = jiffies+IDT77105_RESTART_TIMER_PERIOD;
+ restart_timer.function = idt77105_restart_timer_func;
+ add_timer(&restart_timer);
+ }
+ spin_unlock_irqrestore(&idt77105_priv_lock, flags);
+ return 0;
+}
+
+
+static int idt77105_stop(struct atm_dev *dev)
+{
+ struct idt77105_priv *walk, *prev;
+
+ DPRINTK("%s(itf %d): stopping IDT77105\n",dev->type,dev->number);
+
+ /* disable interrupts */
+ PUT( GET(MCR) & ~IDT77105_MCR_EIP, MCR );
+
+ /* detach private struct from atm_dev & free */
+ for (prev = NULL, walk = idt77105_all ;
+ walk != NULL;
+ prev = walk, walk = walk->next) {
+ if (walk->dev == dev) {
+ if (prev != NULL)
+ prev->next = walk->next;
+ else
+ idt77105_all = walk->next;
+ dev->phy = NULL;
+ dev->dev_data = NULL;
+ kfree(walk);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static const struct atmphy_ops idt77105_ops = {
+ .start = idt77105_start,
+ .ioctl = idt77105_ioctl,
+ .interrupt = idt77105_int,
+ .stop = idt77105_stop,
+};
+
+
+int idt77105_init(struct atm_dev *dev)
+{
+ dev->phy = &idt77105_ops;
+ return 0;
+}
+
+EXPORT_SYMBOL(idt77105_init);
+
+static void __exit idt77105_exit(void)
+{
+ /* turn off timers */
+ del_timer_sync(&stats_timer);
+ del_timer_sync(&restart_timer);
+}
+
+module_exit(idt77105_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/atm/idt77105.h b/drivers/atm/idt77105.h
new file mode 100644
index 000000000..3fd2bc899
--- /dev/null
+++ b/drivers/atm/idt77105.h
@@ -0,0 +1,91 @@
+/* drivers/atm/idt77105.h - IDT77105 (PHY) declarations */
+
+/* Written 1999 by Greg Banks, NEC Australia <gnb@linuxfan.com>. Based on suni.h */
+
+
+#ifndef DRIVER_ATM_IDT77105_H
+#define DRIVER_ATM_IDT77105_H
+
+#include <linux/atmdev.h>
+#include <linux/atmioc.h>
+
+
+/* IDT77105 registers */
+
+#define IDT77105_MCR 0x0 /* Master Control Register */
+#define IDT77105_ISTAT 0x1 /* Interrupt Status */
+#define IDT77105_DIAG 0x2 /* Diagnostic Control */
+#define IDT77105_LEDHEC 0x3 /* LED Driver & HEC Status/Control */
+#define IDT77105_CTRLO 0x4 /* Low Byte Counter Register */
+#define IDT77105_CTRHI 0x5 /* High Byte Counter Register */
+#define IDT77105_CTRSEL 0x6 /* Counter Register Read Select */
+
+/* IDT77105 register values */
+
+/* MCR */
+#define IDT77105_MCR_UPLO 0x80 /* R/W, User Prog'le Output Latch */
+#define IDT77105_MCR_DREC 0x40 /* R/W, Discard Receive Error Cells */
+#define IDT77105_MCR_ECEIO 0x20 /* R/W, Enable Cell Error Interrupts
+ * Only */
+#define IDT77105_MCR_TDPC 0x10 /* R/W, Transmit Data Parity Check */
+#define IDT77105_MCR_DRIC 0x08 /* R/W, Discard Received Idle Cells */
+#define IDT77105_MCR_HALTTX 0x04 /* R/W, Halt Tx */
+#define IDT77105_MCR_UMODE 0x02 /* R/W, Utopia (cell/byte) Mode */
+#define IDT77105_MCR_EIP 0x01 /* R/W, Enable Interrupt Pin */
+
+/* ISTAT */
+#define IDT77105_ISTAT_GOODSIG 0x40 /* R, Good Signal Bit */
+#define IDT77105_ISTAT_HECERR 0x20 /* sticky, HEC Error*/
+#define IDT77105_ISTAT_SCR 0x10 /* sticky, Short Cell Received */
+#define IDT77105_ISTAT_TPE 0x08 /* sticky, Transmit Parity Error */
+#define IDT77105_ISTAT_RSCC 0x04 /* sticky, Rx Signal Condition Change */
+#define IDT77105_ISTAT_RSE 0x02 /* sticky, Rx Symbol Error */
+#define IDT77105_ISTAT_RFO 0x01 /* sticky, Rx FIFO Overrun */
+
+/* DIAG */
+#define IDT77105_DIAG_FTD 0x80 /* R/W, Force TxClav deassert */
+#define IDT77105_DIAG_ROS 0x40 /* R/W, RxClav operation select */
+#define IDT77105_DIAG_MPCS 0x20 /* R/W, Multi-PHY config'n select */
+#define IDT77105_DIAG_RFLUSH 0x10 /* R/W, clear receive FIFO */
+#define IDT77105_DIAG_ITPE 0x08 /* R/W, Insert Tx payload error */
+#define IDT77105_DIAG_ITHE 0x04 /* R/W, Insert Tx HEC error */
+#define IDT77105_DIAG_UMODE 0x02 /* R/W, Utopia (cell/byte) Mode */
+#define IDT77105_DIAG_LCMASK 0x03 /* R/W, Loopback Control */
+
+#define IDT77105_DIAG_LC_NORMAL 0x00 /* Receive from network */
+#define IDT77105_DIAG_LC_PHY_LOOPBACK 0x02
+#define IDT77105_DIAG_LC_LINE_LOOPBACK 0x03
+
+/* LEDHEC */
+#define IDT77105_LEDHEC_DRHC 0x40 /* R/W, Disable Rx HEC check */
+#define IDT77105_LEDHEC_DTHC 0x20 /* R/W, Disable Tx HEC calculation */
+#define IDT77105_LEDHEC_RPWMASK 0x18 /* R/W, RxRef pulse width select */
+#define IDT77105_LEDHEC_TFS 0x04 /* R, Tx FIFO Status (1=empty) */
+#define IDT77105_LEDHEC_TLS 0x02 /* R, Tx LED Status (1=lit) */
+#define IDT77105_LEDHEC_RLS 0x01 /* R, Rx LED Status (1=lit) */
+
+#define IDT77105_LEDHEC_RPW_1 0x00 /* RxRef active for 1 RxClk cycle */
+#define IDT77105_LEDHEC_RPW_2 0x08 /* RxRef active for 2 RxClk cycle */
+#define IDT77105_LEDHEC_RPW_4 0x10 /* RxRef active for 4 RxClk cycle */
+#define IDT77105_LEDHEC_RPW_8 0x18 /* RxRef active for 8 RxClk cycle */
+
+/* CTRSEL */
+#define IDT77105_CTRSEL_SEC 0x08 /* W, Symbol Error Counter */
+#define IDT77105_CTRSEL_TCC 0x04 /* W, Tx Cell Counter */
+#define IDT77105_CTRSEL_RCC 0x02 /* W, Rx Cell Counter */
+#define IDT77105_CTRSEL_RHEC 0x01 /* W, Rx HEC Error Counter */
+
+#ifdef __KERNEL__
+int idt77105_init(struct atm_dev *dev);
+#endif
+
+/*
+ * Tunable parameters
+ */
+
+/* Time between samples of the hardware cell counters. Should be <= 1 sec */
+#define IDT77105_STATS_TIMER_PERIOD (HZ)
+/* Time between checks to see if the signal has been found again */
+#define IDT77105_RESTART_TIMER_PERIOD (5 * HZ)
+
+#endif
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c
new file mode 100644
index 000000000..074616b39
--- /dev/null
+++ b/drivers/atm/idt77252.c
@@ -0,0 +1,3804 @@
+/*******************************************************************
+ *
+ * Copyright (c) 2000 ATecoM GmbH
+ *
+ * The author may be reached at ecd@atecom.com.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ *******************************************************************/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/poison.h>
+#include <linux/skbuff.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/netdevice.h>
+#include <linux/atmdev.h>
+#include <linux/atm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/atomic.h>
+#include <asm/byteorder.h>
+
+#ifdef CONFIG_ATM_IDT77252_USE_SUNI
+#include "suni.h"
+#endif /* CONFIG_ATM_IDT77252_USE_SUNI */
+
+
+#include "idt77252.h"
+#include "idt77252_tables.h"
+
+static unsigned int vpibits = 1;
+
+
+#define ATM_IDT77252_SEND_IDLE 1
+
+
+/*
+ * Debug HACKs.
+ */
+#define DEBUG_MODULE 1
+#undef HAVE_EEPROM /* does not work, yet. */
+
+#ifdef CONFIG_ATM_IDT77252_DEBUG
+static unsigned long debug = DBG_GENERAL;
+#endif
+
+
+#define SAR_RX_DELAY (SAR_CFG_RXINT_NODELAY)
+
+
+/*
+ * SCQ Handling.
+ */
+static struct scq_info *alloc_scq(struct idt77252_dev *, int);
+static void free_scq(struct idt77252_dev *, struct scq_info *);
+static int queue_skb(struct idt77252_dev *, struct vc_map *,
+ struct sk_buff *, int oam);
+static void drain_scq(struct idt77252_dev *, struct vc_map *);
+static unsigned long get_free_scd(struct idt77252_dev *, struct vc_map *);
+static void fill_scd(struct idt77252_dev *, struct scq_info *, int);
+
+/*
+ * FBQ Handling.
+ */
+static int push_rx_skb(struct idt77252_dev *,
+ struct sk_buff *, int queue);
+static void recycle_rx_skb(struct idt77252_dev *, struct sk_buff *);
+static void flush_rx_pool(struct idt77252_dev *, struct rx_pool *);
+static void recycle_rx_pool_skb(struct idt77252_dev *,
+ struct rx_pool *);
+static void add_rx_skb(struct idt77252_dev *, int queue,
+ unsigned int size, unsigned int count);
+
+/*
+ * RSQ Handling.
+ */
+static int init_rsq(struct idt77252_dev *);
+static void deinit_rsq(struct idt77252_dev *);
+static void idt77252_rx(struct idt77252_dev *);
+
+/*
+ * TSQ handling.
+ */
+static int init_tsq(struct idt77252_dev *);
+static void deinit_tsq(struct idt77252_dev *);
+static void idt77252_tx(struct idt77252_dev *);
+
+
+/*
+ * ATM Interface.
+ */
+static void idt77252_dev_close(struct atm_dev *dev);
+static int idt77252_open(struct atm_vcc *vcc);
+static void idt77252_close(struct atm_vcc *vcc);
+static int idt77252_send(struct atm_vcc *vcc, struct sk_buff *skb);
+static int idt77252_send_oam(struct atm_vcc *vcc, void *cell,
+ int flags);
+static void idt77252_phy_put(struct atm_dev *dev, unsigned char value,
+ unsigned long addr);
+static unsigned char idt77252_phy_get(struct atm_dev *dev, unsigned long addr);
+static int idt77252_change_qos(struct atm_vcc *vcc, struct atm_qos *qos,
+ int flags);
+static int idt77252_proc_read(struct atm_dev *dev, loff_t * pos,
+ char *page);
+static void idt77252_softint(struct work_struct *work);
+
+
+static struct atmdev_ops idt77252_ops =
+{
+ .dev_close = idt77252_dev_close,
+ .open = idt77252_open,
+ .close = idt77252_close,
+ .send = idt77252_send,
+ .send_oam = idt77252_send_oam,
+ .phy_put = idt77252_phy_put,
+ .phy_get = idt77252_phy_get,
+ .change_qos = idt77252_change_qos,
+ .proc_read = idt77252_proc_read,
+ .owner = THIS_MODULE
+};
+
+static struct idt77252_dev *idt77252_chain = NULL;
+static unsigned int idt77252_sram_write_errors = 0;
+
+/*****************************************************************************/
+/* */
+/* I/O and Utility Bus */
+/* */
+/*****************************************************************************/
+
+static void
+waitfor_idle(struct idt77252_dev *card)
+{
+ u32 stat;
+
+ stat = readl(SAR_REG_STAT);
+ while (stat & SAR_STAT_CMDBZ)
+ stat = readl(SAR_REG_STAT);
+}
+
+static u32
+read_sram(struct idt77252_dev *card, unsigned long addr)
+{
+ unsigned long flags;
+ u32 value;
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel(SAR_CMD_READ_SRAM | (addr << 2), SAR_REG_CMD);
+ waitfor_idle(card);
+ value = readl(SAR_REG_DR0);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+ return value;
+}
+
+static void
+write_sram(struct idt77252_dev *card, unsigned long addr, u32 value)
+{
+ unsigned long flags;
+
+ if ((idt77252_sram_write_errors == 0) &&
+ (((addr > card->tst[0] + card->tst_size - 2) &&
+ (addr < card->tst[0] + card->tst_size)) ||
+ ((addr > card->tst[1] + card->tst_size - 2) &&
+ (addr < card->tst[1] + card->tst_size)))) {
+ printk("%s: ERROR: TST JMP section at %08lx written: %08x\n",
+ card->name, addr, value);
+ }
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel(value, SAR_REG_DR0);
+ writel(SAR_CMD_WRITE_SRAM | (addr << 2), SAR_REG_CMD);
+ waitfor_idle(card);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+}
+
+static u8
+read_utility(void *dev, unsigned long ubus_addr)
+{
+ struct idt77252_dev *card = dev;
+ unsigned long flags;
+ u8 value;
+
+ if (!card) {
+ printk("Error: No such device.\n");
+ return -1;
+ }
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel(SAR_CMD_READ_UTILITY + ubus_addr, SAR_REG_CMD);
+ waitfor_idle(card);
+ value = readl(SAR_REG_DR0);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+ return value;
+}
+
+static void
+write_utility(void *dev, unsigned long ubus_addr, u8 value)
+{
+ struct idt77252_dev *card = dev;
+ unsigned long flags;
+
+ if (!card) {
+ printk("Error: No such device.\n");
+ return;
+ }
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel((u32) value, SAR_REG_DR0);
+ writel(SAR_CMD_WRITE_UTILITY + ubus_addr, SAR_REG_CMD);
+ waitfor_idle(card);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+}
+
+#ifdef HAVE_EEPROM
+static u32 rdsrtab[] =
+{
+ SAR_GP_EECS | SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ SAR_GP_EEDO,
+ SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ SAR_GP_EEDO,
+ SAR_GP_EESCLK | SAR_GP_EEDO /* 1 */
+};
+
+static u32 wrentab[] =
+{
+ SAR_GP_EECS | SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ SAR_GP_EEDO,
+ SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */
+ SAR_GP_EEDO,
+ SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK /* 0 */
+};
+
+static u32 rdtab[] =
+{
+ SAR_GP_EECS | SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ SAR_GP_EEDO,
+ SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */
+ SAR_GP_EEDO,
+ SAR_GP_EESCLK | SAR_GP_EEDO /* 1 */
+};
+
+static u32 wrtab[] =
+{
+ SAR_GP_EECS | SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ 0,
+ SAR_GP_EESCLK, /* 0 */
+ SAR_GP_EEDO,
+ SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */
+ 0,
+ SAR_GP_EESCLK /* 0 */
+};
+
+static u32 clktab[] =
+{
+ 0,
+ SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK,
+ 0,
+ SAR_GP_EESCLK,
+ 0
+};
+
+static u32
+idt77252_read_gp(struct idt77252_dev *card)
+{
+ u32 gp;
+
+ gp = readl(SAR_REG_GP);
+#if 0
+ printk("RD: %s\n", gp & SAR_GP_EEDI ? "1" : "0");
+#endif
+ return gp;
+}
+
+static void
+idt77252_write_gp(struct idt77252_dev *card, u32 value)
+{
+ unsigned long flags;
+
+#if 0
+ printk("WR: %s %s %s\n", value & SAR_GP_EECS ? " " : "/CS",
+ value & SAR_GP_EESCLK ? "HIGH" : "LOW ",
+ value & SAR_GP_EEDO ? "1" : "0");
+#endif
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ waitfor_idle(card);
+ writel(value, SAR_REG_GP);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+}
+
+static u8
+idt77252_eeprom_read_status(struct idt77252_dev *card)
+{
+ u8 byte;
+ u32 gp;
+ int i, j;
+
+ gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO);
+
+ for (i = 0; i < ARRAY_SIZE(rdsrtab); i++) {
+ idt77252_write_gp(card, gp | rdsrtab[i]);
+ udelay(5);
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+
+ byte = 0;
+ for (i = 0, j = 0; i < 8; i++) {
+ byte <<= 1;
+
+ idt77252_write_gp(card, gp | clktab[j++]);
+ udelay(5);
+
+ byte |= idt77252_read_gp(card) & SAR_GP_EEDI ? 1 : 0;
+
+ idt77252_write_gp(card, gp | clktab[j++]);
+ udelay(5);
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+
+ return byte;
+}
+
+static u8
+idt77252_eeprom_read_byte(struct idt77252_dev *card, u8 offset)
+{
+ u8 byte;
+ u32 gp;
+ int i, j;
+
+ gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO);
+
+ for (i = 0; i < ARRAY_SIZE(rdtab); i++) {
+ idt77252_write_gp(card, gp | rdtab[i]);
+ udelay(5);
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+
+ for (i = 0, j = 0; i < 8; i++) {
+ idt77252_write_gp(card, gp | clktab[j++] |
+ (offset & 1 ? SAR_GP_EEDO : 0));
+ udelay(5);
+
+ idt77252_write_gp(card, gp | clktab[j++] |
+ (offset & 1 ? SAR_GP_EEDO : 0));
+ udelay(5);
+
+ offset >>= 1;
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+
+ byte = 0;
+ for (i = 0, j = 0; i < 8; i++) {
+ byte <<= 1;
+
+ idt77252_write_gp(card, gp | clktab[j++]);
+ udelay(5);
+
+ byte |= idt77252_read_gp(card) & SAR_GP_EEDI ? 1 : 0;
+
+ idt77252_write_gp(card, gp | clktab[j++]);
+ udelay(5);
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+
+ return byte;
+}
+
+static void
+idt77252_eeprom_write_byte(struct idt77252_dev *card, u8 offset, u8 data)
+{
+ u32 gp;
+ int i, j;
+
+ gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO);
+
+ for (i = 0; i < ARRAY_SIZE(wrentab); i++) {
+ idt77252_write_gp(card, gp | wrentab[i]);
+ udelay(5);
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+
+ for (i = 0; i < ARRAY_SIZE(wrtab); i++) {
+ idt77252_write_gp(card, gp | wrtab[i]);
+ udelay(5);
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+
+ for (i = 0, j = 0; i < 8; i++) {
+ idt77252_write_gp(card, gp | clktab[j++] |
+ (offset & 1 ? SAR_GP_EEDO : 0));
+ udelay(5);
+
+ idt77252_write_gp(card, gp | clktab[j++] |
+ (offset & 1 ? SAR_GP_EEDO : 0));
+ udelay(5);
+
+ offset >>= 1;
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+
+ for (i = 0, j = 0; i < 8; i++) {
+ idt77252_write_gp(card, gp | clktab[j++] |
+ (data & 1 ? SAR_GP_EEDO : 0));
+ udelay(5);
+
+ idt77252_write_gp(card, gp | clktab[j++] |
+ (data & 1 ? SAR_GP_EEDO : 0));
+ udelay(5);
+
+ data >>= 1;
+ }
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+}
+
+static void
+idt77252_eeprom_init(struct idt77252_dev *card)
+{
+ u32 gp;
+
+ gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO);
+
+ idt77252_write_gp(card, gp | SAR_GP_EECS | SAR_GP_EESCLK);
+ udelay(5);
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+ idt77252_write_gp(card, gp | SAR_GP_EECS | SAR_GP_EESCLK);
+ udelay(5);
+ idt77252_write_gp(card, gp | SAR_GP_EECS);
+ udelay(5);
+}
+#endif /* HAVE_EEPROM */
+
+
+#ifdef CONFIG_ATM_IDT77252_DEBUG
+static void
+dump_tct(struct idt77252_dev *card, int index)
+{
+ unsigned long tct;
+ int i;
+
+ tct = (unsigned long) (card->tct_base + index * SAR_SRAM_TCT_SIZE);
+
+ printk("%s: TCT %x:", card->name, index);
+ for (i = 0; i < 8; i++) {
+ printk(" %08x", read_sram(card, tct + i));
+ }
+ printk("\n");
+}
+
+static void
+idt77252_tx_dump(struct idt77252_dev *card)
+{
+ struct atm_vcc *vcc;
+ struct vc_map *vc;
+ int i;
+
+ printk("%s\n", __func__);
+ for (i = 0; i < card->tct_size; i++) {
+ vc = card->vcs[i];
+ if (!vc)
+ continue;
+
+ vcc = NULL;
+ if (vc->rx_vcc)
+ vcc = vc->rx_vcc;
+ else if (vc->tx_vcc)
+ vcc = vc->tx_vcc;
+
+ if (!vcc)
+ continue;
+
+ printk("%s: Connection %d:\n", card->name, vc->index);
+ dump_tct(card, vc->index);
+ }
+}
+#endif
+
+
+/*****************************************************************************/
+/* */
+/* SCQ Handling */
+/* */
+/*****************************************************************************/
+
+static int
+sb_pool_add(struct idt77252_dev *card, struct sk_buff *skb, int queue)
+{
+ struct sb_pool *pool = &card->sbpool[queue];
+ int index;
+
+ index = pool->index;
+ while (pool->skb[index]) {
+ index = (index + 1) & FBQ_MASK;
+ if (index == pool->index)
+ return -ENOBUFS;
+ }
+
+ pool->skb[index] = skb;
+ IDT77252_PRV_POOL(skb) = POOL_HANDLE(queue, index);
+
+ pool->index = (index + 1) & FBQ_MASK;
+ return 0;
+}
+
+static void
+sb_pool_remove(struct idt77252_dev *card, struct sk_buff *skb)
+{
+ unsigned int queue, index;
+ u32 handle;
+
+ handle = IDT77252_PRV_POOL(skb);
+
+ queue = POOL_QUEUE(handle);
+ if (queue > 3)
+ return;
+
+ index = POOL_INDEX(handle);
+ if (index > FBQ_SIZE - 1)
+ return;
+
+ card->sbpool[queue].skb[index] = NULL;
+}
+
+static struct sk_buff *
+sb_pool_skb(struct idt77252_dev *card, u32 handle)
+{
+ unsigned int queue, index;
+
+ queue = POOL_QUEUE(handle);
+ if (queue > 3)
+ return NULL;
+
+ index = POOL_INDEX(handle);
+ if (index > FBQ_SIZE - 1)
+ return NULL;
+
+ return card->sbpool[queue].skb[index];
+}
+
+static struct scq_info *
+alloc_scq(struct idt77252_dev *card, int class)
+{
+ struct scq_info *scq;
+
+ scq = kzalloc(sizeof(struct scq_info), GFP_KERNEL);
+ if (!scq)
+ return NULL;
+ scq->base = dma_zalloc_coherent(&card->pcidev->dev, SCQ_SIZE,
+ &scq->paddr, GFP_KERNEL);
+ if (scq->base == NULL) {
+ kfree(scq);
+ return NULL;
+ }
+
+ scq->next = scq->base;
+ scq->last = scq->base + (SCQ_ENTRIES - 1);
+ atomic_set(&scq->used, 0);
+
+ spin_lock_init(&scq->lock);
+ spin_lock_init(&scq->skblock);
+
+ skb_queue_head_init(&scq->transmit);
+ skb_queue_head_init(&scq->pending);
+
+ TXPRINTK("idt77252: SCQ: base 0x%p, next 0x%p, last 0x%p, paddr %08llx\n",
+ scq->base, scq->next, scq->last, (unsigned long long)scq->paddr);
+
+ return scq;
+}
+
+static void
+free_scq(struct idt77252_dev *card, struct scq_info *scq)
+{
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+
+ dma_free_coherent(&card->pcidev->dev, SCQ_SIZE,
+ scq->base, scq->paddr);
+
+ while ((skb = skb_dequeue(&scq->transmit))) {
+ dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb->len, DMA_TO_DEVICE);
+
+ vcc = ATM_SKB(skb)->vcc;
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb(skb);
+ }
+
+ while ((skb = skb_dequeue(&scq->pending))) {
+ dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb->len, DMA_TO_DEVICE);
+
+ vcc = ATM_SKB(skb)->vcc;
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb(skb);
+ }
+
+ kfree(scq);
+}
+
+
+static int
+push_on_scq(struct idt77252_dev *card, struct vc_map *vc, struct sk_buff *skb)
+{
+ struct scq_info *scq = vc->scq;
+ unsigned long flags;
+ struct scqe *tbd;
+ int entries;
+
+ TXPRINTK("%s: SCQ: next 0x%p\n", card->name, scq->next);
+
+ atomic_inc(&scq->used);
+ entries = atomic_read(&scq->used);
+ if (entries > (SCQ_ENTRIES - 1)) {
+ atomic_dec(&scq->used);
+ goto out;
+ }
+
+ skb_queue_tail(&scq->transmit, skb);
+
+ spin_lock_irqsave(&vc->lock, flags);
+ if (vc->estimator) {
+ struct atm_vcc *vcc = vc->tx_vcc;
+ struct sock *sk = sk_atm(vcc);
+
+ vc->estimator->cells += (skb->len + 47) / 48;
+ if (atomic_read(&sk->sk_wmem_alloc) >
+ (sk->sk_sndbuf >> 1)) {
+ u32 cps = vc->estimator->maxcps;
+
+ vc->estimator->cps = cps;
+ vc->estimator->avcps = cps << 5;
+ if (vc->lacr < vc->init_er) {
+ vc->lacr = vc->init_er;
+ writel(TCMDQ_LACR | (vc->lacr << 16) |
+ vc->index, SAR_REG_TCMDQ);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ tbd = &IDT77252_PRV_TBD(skb);
+
+ spin_lock_irqsave(&scq->lock, flags);
+ scq->next->word_1 = cpu_to_le32(tbd->word_1 |
+ SAR_TBD_TSIF | SAR_TBD_GTSI);
+ scq->next->word_2 = cpu_to_le32(tbd->word_2);
+ scq->next->word_3 = cpu_to_le32(tbd->word_3);
+ scq->next->word_4 = cpu_to_le32(tbd->word_4);
+
+ if (scq->next == scq->last)
+ scq->next = scq->base;
+ else
+ scq->next++;
+
+ write_sram(card, scq->scd,
+ scq->paddr +
+ (u32)((unsigned long)scq->next - (unsigned long)scq->base));
+ spin_unlock_irqrestore(&scq->lock, flags);
+
+ scq->trans_start = jiffies;
+
+ if (test_and_clear_bit(VCF_IDLE, &vc->flags)) {
+ writel(TCMDQ_START_LACR | (vc->lacr << 16) | vc->index,
+ SAR_REG_TCMDQ);
+ }
+
+ TXPRINTK("%d entries in SCQ used (push).\n", atomic_read(&scq->used));
+
+ XPRINTK("%s: SCQ (after push %2d) head = 0x%x, next = 0x%p.\n",
+ card->name, atomic_read(&scq->used),
+ read_sram(card, scq->scd + 1), scq->next);
+
+ return 0;
+
+out:
+ if (time_after(jiffies, scq->trans_start + HZ)) {
+ printk("%s: Error pushing TBD for %d.%d\n",
+ card->name, vc->tx_vcc->vpi, vc->tx_vcc->vci);
+#ifdef CONFIG_ATM_IDT77252_DEBUG
+ idt77252_tx_dump(card);
+#endif
+ scq->trans_start = jiffies;
+ }
+
+ return -ENOBUFS;
+}
+
+
+static void
+drain_scq(struct idt77252_dev *card, struct vc_map *vc)
+{
+ struct scq_info *scq = vc->scq;
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+
+ TXPRINTK("%s: SCQ (before drain %2d) next = 0x%p.\n",
+ card->name, atomic_read(&scq->used), scq->next);
+
+ skb = skb_dequeue(&scq->transmit);
+ if (skb) {
+ TXPRINTK("%s: freeing skb at %p.\n", card->name, skb);
+
+ dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb->len, DMA_TO_DEVICE);
+
+ vcc = ATM_SKB(skb)->vcc;
+
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb(skb);
+
+ atomic_inc(&vcc->stats->tx);
+ }
+
+ atomic_dec(&scq->used);
+
+ spin_lock(&scq->skblock);
+ while ((skb = skb_dequeue(&scq->pending))) {
+ if (push_on_scq(card, vc, skb)) {
+ skb_queue_head(&vc->scq->pending, skb);
+ break;
+ }
+ }
+ spin_unlock(&scq->skblock);
+}
+
+static int
+queue_skb(struct idt77252_dev *card, struct vc_map *vc,
+ struct sk_buff *skb, int oam)
+{
+ struct atm_vcc *vcc;
+ struct scqe *tbd;
+ unsigned long flags;
+ int error;
+ int aal;
+
+ if (skb->len == 0) {
+ printk("%s: invalid skb->len (%d)\n", card->name, skb->len);
+ return -EINVAL;
+ }
+
+ TXPRINTK("%s: Sending %d bytes of data.\n",
+ card->name, skb->len);
+
+ tbd = &IDT77252_PRV_TBD(skb);
+ vcc = ATM_SKB(skb)->vcc;
+
+ IDT77252_PRV_PADDR(skb) = dma_map_single(&card->pcidev->dev, skb->data,
+ skb->len, DMA_TO_DEVICE);
+
+ error = -EINVAL;
+
+ if (oam) {
+ if (skb->len != 52)
+ goto errout;
+
+ tbd->word_1 = SAR_TBD_OAM | ATM_CELL_PAYLOAD | SAR_TBD_EPDU;
+ tbd->word_2 = IDT77252_PRV_PADDR(skb) + 4;
+ tbd->word_3 = 0x00000000;
+ tbd->word_4 = (skb->data[0] << 24) | (skb->data[1] << 16) |
+ (skb->data[2] << 8) | (skb->data[3] << 0);
+
+ if (test_bit(VCF_RSV, &vc->flags))
+ vc = card->vcs[0];
+
+ goto done;
+ }
+
+ if (test_bit(VCF_RSV, &vc->flags)) {
+ printk("%s: Trying to transmit on reserved VC\n", card->name);
+ goto errout;
+ }
+
+ aal = vcc->qos.aal;
+
+ switch (aal) {
+ case ATM_AAL0:
+ case ATM_AAL34:
+ if (skb->len > 52)
+ goto errout;
+
+ if (aal == ATM_AAL0)
+ tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL0 |
+ ATM_CELL_PAYLOAD;
+ else
+ tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL34 |
+ ATM_CELL_PAYLOAD;
+
+ tbd->word_2 = IDT77252_PRV_PADDR(skb) + 4;
+ tbd->word_3 = 0x00000000;
+ tbd->word_4 = (skb->data[0] << 24) | (skb->data[1] << 16) |
+ (skb->data[2] << 8) | (skb->data[3] << 0);
+ break;
+
+ case ATM_AAL5:
+ tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL5 | skb->len;
+ tbd->word_2 = IDT77252_PRV_PADDR(skb);
+ tbd->word_3 = skb->len;
+ tbd->word_4 = (vcc->vpi << SAR_TBD_VPI_SHIFT) |
+ (vcc->vci << SAR_TBD_VCI_SHIFT);
+ break;
+
+ case ATM_AAL1:
+ case ATM_AAL2:
+ default:
+ printk("%s: Traffic type not supported.\n", card->name);
+ error = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+done:
+ spin_lock_irqsave(&vc->scq->skblock, flags);
+ skb_queue_tail(&vc->scq->pending, skb);
+
+ while ((skb = skb_dequeue(&vc->scq->pending))) {
+ if (push_on_scq(card, vc, skb)) {
+ skb_queue_head(&vc->scq->pending, skb);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vc->scq->skblock, flags);
+
+ return 0;
+
+errout:
+ dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb->len, DMA_TO_DEVICE);
+ return error;
+}
+
+static unsigned long
+get_free_scd(struct idt77252_dev *card, struct vc_map *vc)
+{
+ int i;
+
+ for (i = 0; i < card->scd_size; i++) {
+ if (!card->scd2vc[i]) {
+ card->scd2vc[i] = vc;
+ vc->scd_index = i;
+ return card->scd_base + i * SAR_SRAM_SCD_SIZE;
+ }
+ }
+ return 0;
+}
+
+static void
+fill_scd(struct idt77252_dev *card, struct scq_info *scq, int class)
+{
+ write_sram(card, scq->scd, scq->paddr);
+ write_sram(card, scq->scd + 1, 0x00000000);
+ write_sram(card, scq->scd + 2, 0xffffffff);
+ write_sram(card, scq->scd + 3, 0x00000000);
+}
+
+static void
+clear_scd(struct idt77252_dev *card, struct scq_info *scq, int class)
+{
+ return;
+}
+
+/*****************************************************************************/
+/* */
+/* RSQ Handling */
+/* */
+/*****************************************************************************/
+
+static int
+init_rsq(struct idt77252_dev *card)
+{
+ struct rsq_entry *rsqe;
+
+ card->rsq.base = dma_zalloc_coherent(&card->pcidev->dev, RSQSIZE,
+ &card->rsq.paddr, GFP_KERNEL);
+ if (card->rsq.base == NULL) {
+ printk("%s: can't allocate RSQ.\n", card->name);
+ return -1;
+ }
+
+ card->rsq.last = card->rsq.base + RSQ_NUM_ENTRIES - 1;
+ card->rsq.next = card->rsq.last;
+ for (rsqe = card->rsq.base; rsqe <= card->rsq.last; rsqe++)
+ rsqe->word_4 = 0;
+
+ writel((unsigned long) card->rsq.last - (unsigned long) card->rsq.base,
+ SAR_REG_RSQH);
+ writel(card->rsq.paddr, SAR_REG_RSQB);
+
+ IPRINTK("%s: RSQ base at 0x%lx (0x%x).\n", card->name,
+ (unsigned long) card->rsq.base,
+ readl(SAR_REG_RSQB));
+ IPRINTK("%s: RSQ head = 0x%x, base = 0x%x, tail = 0x%x.\n",
+ card->name,
+ readl(SAR_REG_RSQH),
+ readl(SAR_REG_RSQB),
+ readl(SAR_REG_RSQT));
+
+ return 0;
+}
+
+static void
+deinit_rsq(struct idt77252_dev *card)
+{
+ dma_free_coherent(&card->pcidev->dev, RSQSIZE,
+ card->rsq.base, card->rsq.paddr);
+}
+
+static void
+dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe)
+{
+ struct atm_vcc *vcc;
+ struct sk_buff *skb;
+ struct rx_pool *rpp;
+ struct vc_map *vc;
+ u32 header, vpi, vci;
+ u32 stat;
+ int i;
+
+ stat = le32_to_cpu(rsqe->word_4);
+
+ if (stat & SAR_RSQE_IDLE) {
+ RXPRINTK("%s: message about inactive connection.\n",
+ card->name);
+ return;
+ }
+
+ skb = sb_pool_skb(card, le32_to_cpu(rsqe->word_2));
+ if (skb == NULL) {
+ printk("%s: NULL skb in %s, rsqe: %08x %08x %08x %08x\n",
+ card->name, __func__,
+ le32_to_cpu(rsqe->word_1), le32_to_cpu(rsqe->word_2),
+ le32_to_cpu(rsqe->word_3), le32_to_cpu(rsqe->word_4));
+ return;
+ }
+
+ header = le32_to_cpu(rsqe->word_1);
+ vpi = (header >> 16) & 0x00ff;
+ vci = (header >> 0) & 0xffff;
+
+ RXPRINTK("%s: SDU for %d.%d received in buffer 0x%p (data 0x%p).\n",
+ card->name, vpi, vci, skb, skb->data);
+
+ if ((vpi >= (1 << card->vpibits)) || (vci != (vci & card->vcimask))) {
+ printk("%s: SDU received for out-of-range vc %u.%u\n",
+ card->name, vpi, vci);
+ recycle_rx_skb(card, skb);
+ return;
+ }
+
+ vc = card->vcs[VPCI2VC(card, vpi, vci)];
+ if (!vc || !test_bit(VCF_RX, &vc->flags)) {
+ printk("%s: SDU received on non RX vc %u.%u\n",
+ card->name, vpi, vci);
+ recycle_rx_skb(card, skb);
+ return;
+ }
+
+ vcc = vc->rx_vcc;
+
+ dma_sync_single_for_cpu(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb_end_pointer(skb) - skb->data,
+ DMA_FROM_DEVICE);
+
+ if ((vcc->qos.aal == ATM_AAL0) ||
+ (vcc->qos.aal == ATM_AAL34)) {
+ struct sk_buff *sb;
+ unsigned char *cell;
+ u32 aal0;
+
+ cell = skb->data;
+ for (i = (stat & SAR_RSQE_CELLCNT); i; i--) {
+ if ((sb = dev_alloc_skb(64)) == NULL) {
+ printk("%s: Can't allocate buffers for aal0.\n",
+ card->name);
+ atomic_add(i, &vcc->stats->rx_drop);
+ break;
+ }
+ if (!atm_charge(vcc, sb->truesize)) {
+ RXPRINTK("%s: atm_charge() dropped aal0 packets.\n",
+ card->name);
+ atomic_add(i - 1, &vcc->stats->rx_drop);
+ dev_kfree_skb(sb);
+ break;
+ }
+ aal0 = (vpi << ATM_HDR_VPI_SHIFT) |
+ (vci << ATM_HDR_VCI_SHIFT);
+ aal0 |= (stat & SAR_RSQE_EPDU) ? 0x00000002 : 0;
+ aal0 |= (stat & SAR_RSQE_CLP) ? 0x00000001 : 0;
+
+ *((u32 *) sb->data) = aal0;
+ skb_put(sb, sizeof(u32));
+ memcpy(skb_put(sb, ATM_CELL_PAYLOAD),
+ cell, ATM_CELL_PAYLOAD);
+
+ ATM_SKB(sb)->vcc = vcc;
+ __net_timestamp(sb);
+ vcc->push(vcc, sb);
+ atomic_inc(&vcc->stats->rx);
+
+ cell += ATM_CELL_PAYLOAD;
+ }
+
+ recycle_rx_skb(card, skb);
+ return;
+ }
+ if (vcc->qos.aal != ATM_AAL5) {
+ printk("%s: Unexpected AAL type in dequeue_rx(): %d.\n",
+ card->name, vcc->qos.aal);
+ recycle_rx_skb(card, skb);
+ return;
+ }
+ skb->len = (stat & SAR_RSQE_CELLCNT) * ATM_CELL_PAYLOAD;
+
+ rpp = &vc->rcv.rx_pool;
+
+ __skb_queue_tail(&rpp->queue, skb);
+ rpp->len += skb->len;
+
+ if (stat & SAR_RSQE_EPDU) {
+ unsigned char *l1l2;
+ unsigned int len;
+
+ l1l2 = (unsigned char *) ((unsigned long) skb->data + skb->len - 6);
+
+ len = (l1l2[0] << 8) | l1l2[1];
+ len = len ? len : 0x10000;
+
+ RXPRINTK("%s: PDU has %d bytes.\n", card->name, len);
+
+ if ((len + 8 > rpp->len) || (len + (47 + 8) < rpp->len)) {
+ RXPRINTK("%s: AAL5 PDU size mismatch: %d != %d. "
+ "(CDC: %08x)\n",
+ card->name, len, rpp->len, readl(SAR_REG_CDC));
+ recycle_rx_pool_skb(card, rpp);
+ atomic_inc(&vcc->stats->rx_err);
+ return;
+ }
+ if (stat & SAR_RSQE_CRC) {
+ RXPRINTK("%s: AAL5 CRC error.\n", card->name);
+ recycle_rx_pool_skb(card, rpp);
+ atomic_inc(&vcc->stats->rx_err);
+ return;
+ }
+ if (skb_queue_len(&rpp->queue) > 1) {
+ struct sk_buff *sb;
+
+ skb = dev_alloc_skb(rpp->len);
+ if (!skb) {
+ RXPRINTK("%s: Can't alloc RX skb.\n",
+ card->name);
+ recycle_rx_pool_skb(card, rpp);
+ atomic_inc(&vcc->stats->rx_err);
+ return;
+ }
+ if (!atm_charge(vcc, skb->truesize)) {
+ recycle_rx_pool_skb(card, rpp);
+ dev_kfree_skb(skb);
+ return;
+ }
+ skb_queue_walk(&rpp->queue, sb)
+ memcpy(skb_put(skb, sb->len),
+ sb->data, sb->len);
+
+ recycle_rx_pool_skb(card, rpp);
+
+ skb_trim(skb, len);
+ ATM_SKB(skb)->vcc = vcc;
+ __net_timestamp(skb);
+
+ vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
+
+ return;
+ }
+
+ flush_rx_pool(card, rpp);
+
+ if (!atm_charge(vcc, skb->truesize)) {
+ recycle_rx_skb(card, skb);
+ return;
+ }
+
+ dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb_end_pointer(skb) - skb->data,
+ DMA_FROM_DEVICE);
+ sb_pool_remove(card, skb);
+
+ skb_trim(skb, len);
+ ATM_SKB(skb)->vcc = vcc;
+ __net_timestamp(skb);
+
+ vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
+
+ if (skb->truesize > SAR_FB_SIZE_3)
+ add_rx_skb(card, 3, SAR_FB_SIZE_3, 1);
+ else if (skb->truesize > SAR_FB_SIZE_2)
+ add_rx_skb(card, 2, SAR_FB_SIZE_2, 1);
+ else if (skb->truesize > SAR_FB_SIZE_1)
+ add_rx_skb(card, 1, SAR_FB_SIZE_1, 1);
+ else
+ add_rx_skb(card, 0, SAR_FB_SIZE_0, 1);
+ return;
+ }
+}
+
+static void
+idt77252_rx(struct idt77252_dev *card)
+{
+ struct rsq_entry *rsqe;
+
+ if (card->rsq.next == card->rsq.last)
+ rsqe = card->rsq.base;
+ else
+ rsqe = card->rsq.next + 1;
+
+ if (!(le32_to_cpu(rsqe->word_4) & SAR_RSQE_VALID)) {
+ RXPRINTK("%s: no entry in RSQ.\n", card->name);
+ return;
+ }
+
+ do {
+ dequeue_rx(card, rsqe);
+ rsqe->word_4 = 0;
+ card->rsq.next = rsqe;
+ if (card->rsq.next == card->rsq.last)
+ rsqe = card->rsq.base;
+ else
+ rsqe = card->rsq.next + 1;
+ } while (le32_to_cpu(rsqe->word_4) & SAR_RSQE_VALID);
+
+ writel((unsigned long) card->rsq.next - (unsigned long) card->rsq.base,
+ SAR_REG_RSQH);
+}
+
+static void
+idt77252_rx_raw(struct idt77252_dev *card)
+{
+ struct sk_buff *queue;
+ u32 head, tail;
+ struct atm_vcc *vcc;
+ struct vc_map *vc;
+ struct sk_buff *sb;
+
+ if (card->raw_cell_head == NULL) {
+ u32 handle = le32_to_cpu(*(card->raw_cell_hnd + 1));
+ card->raw_cell_head = sb_pool_skb(card, handle);
+ }
+
+ queue = card->raw_cell_head;
+ if (!queue)
+ return;
+
+ head = IDT77252_PRV_PADDR(queue) + (queue->data - queue->head - 16);
+ tail = readl(SAR_REG_RAWCT);
+
+ dma_sync_single_for_cpu(&card->pcidev->dev, IDT77252_PRV_PADDR(queue),
+ skb_end_offset(queue) - 16,
+ DMA_FROM_DEVICE);
+
+ while (head != tail) {
+ unsigned int vpi, vci;
+ u32 header;
+
+ header = le32_to_cpu(*(u32 *) &queue->data[0]);
+
+ vpi = (header & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT;
+ vci = (header & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT;
+
+#ifdef CONFIG_ATM_IDT77252_DEBUG
+ if (debug & DBG_RAW_CELL) {
+ int i;
+
+ printk("%s: raw cell %x.%02x.%04x.%x.%x\n",
+ card->name, (header >> 28) & 0x000f,
+ (header >> 20) & 0x00ff,
+ (header >> 4) & 0xffff,
+ (header >> 1) & 0x0007,
+ (header >> 0) & 0x0001);
+ for (i = 16; i < 64; i++)
+ printk(" %02x", queue->data[i]);
+ printk("\n");
+ }
+#endif
+
+ if (vpi >= (1<<card->vpibits) || vci >= (1<<card->vcibits)) {
+ RPRINTK("%s: SDU received for out-of-range vc %u.%u\n",
+ card->name, vpi, vci);
+ goto drop;
+ }
+
+ vc = card->vcs[VPCI2VC(card, vpi, vci)];
+ if (!vc || !test_bit(VCF_RX, &vc->flags)) {
+ RPRINTK("%s: SDU received on non RX vc %u.%u\n",
+ card->name, vpi, vci);
+ goto drop;
+ }
+
+ vcc = vc->rx_vcc;
+
+ if (vcc->qos.aal != ATM_AAL0) {
+ RPRINTK("%s: raw cell for non AAL0 vc %u.%u\n",
+ card->name, vpi, vci);
+ atomic_inc(&vcc->stats->rx_drop);
+ goto drop;
+ }
+
+ if ((sb = dev_alloc_skb(64)) == NULL) {
+ printk("%s: Can't allocate buffers for AAL0.\n",
+ card->name);
+ atomic_inc(&vcc->stats->rx_err);
+ goto drop;
+ }
+
+ if (!atm_charge(vcc, sb->truesize)) {
+ RXPRINTK("%s: atm_charge() dropped AAL0 packets.\n",
+ card->name);
+ dev_kfree_skb(sb);
+ goto drop;
+ }
+
+ *((u32 *) sb->data) = header;
+ skb_put(sb, sizeof(u32));
+ memcpy(skb_put(sb, ATM_CELL_PAYLOAD), &(queue->data[16]),
+ ATM_CELL_PAYLOAD);
+
+ ATM_SKB(sb)->vcc = vcc;
+ __net_timestamp(sb);
+ vcc->push(vcc, sb);
+ atomic_inc(&vcc->stats->rx);
+
+drop:
+ skb_pull(queue, 64);
+
+ head = IDT77252_PRV_PADDR(queue)
+ + (queue->data - queue->head - 16);
+
+ if (queue->len < 128) {
+ struct sk_buff *next;
+ u32 handle;
+
+ head = le32_to_cpu(*(u32 *) &queue->data[0]);
+ handle = le32_to_cpu(*(u32 *) &queue->data[4]);
+
+ next = sb_pool_skb(card, handle);
+ recycle_rx_skb(card, queue);
+
+ if (next) {
+ card->raw_cell_head = next;
+ queue = card->raw_cell_head;
+ dma_sync_single_for_cpu(&card->pcidev->dev,
+ IDT77252_PRV_PADDR(queue),
+ (skb_end_pointer(queue) -
+ queue->data),
+ DMA_FROM_DEVICE);
+ } else {
+ card->raw_cell_head = NULL;
+ printk("%s: raw cell queue overrun\n",
+ card->name);
+ break;
+ }
+ }
+ }
+}
+
+
+/*****************************************************************************/
+/* */
+/* TSQ Handling */
+/* */
+/*****************************************************************************/
+
+static int
+init_tsq(struct idt77252_dev *card)
+{
+ struct tsq_entry *tsqe;
+
+ card->tsq.base = dma_alloc_coherent(&card->pcidev->dev, RSQSIZE,
+ &card->tsq.paddr, GFP_KERNEL);
+ if (card->tsq.base == NULL) {
+ printk("%s: can't allocate TSQ.\n", card->name);
+ return -1;
+ }
+ memset(card->tsq.base, 0, TSQSIZE);
+
+ card->tsq.last = card->tsq.base + TSQ_NUM_ENTRIES - 1;
+ card->tsq.next = card->tsq.last;
+ for (tsqe = card->tsq.base; tsqe <= card->tsq.last; tsqe++)
+ tsqe->word_2 = cpu_to_le32(SAR_TSQE_INVALID);
+
+ writel(card->tsq.paddr, SAR_REG_TSQB);
+ writel((unsigned long) card->tsq.next - (unsigned long) card->tsq.base,
+ SAR_REG_TSQH);
+
+ return 0;
+}
+
+static void
+deinit_tsq(struct idt77252_dev *card)
+{
+ dma_free_coherent(&card->pcidev->dev, TSQSIZE,
+ card->tsq.base, card->tsq.paddr);
+}
+
+static void
+idt77252_tx(struct idt77252_dev *card)
+{
+ struct tsq_entry *tsqe;
+ unsigned int vpi, vci;
+ struct vc_map *vc;
+ u32 conn, stat;
+
+ if (card->tsq.next == card->tsq.last)
+ tsqe = card->tsq.base;
+ else
+ tsqe = card->tsq.next + 1;
+
+ TXPRINTK("idt77252_tx: tsq %p: base %p, next %p, last %p\n", tsqe,
+ card->tsq.base, card->tsq.next, card->tsq.last);
+ TXPRINTK("idt77252_tx: tsqb %08x, tsqt %08x, tsqh %08x, \n",
+ readl(SAR_REG_TSQB),
+ readl(SAR_REG_TSQT),
+ readl(SAR_REG_TSQH));
+
+ stat = le32_to_cpu(tsqe->word_2);
+
+ if (stat & SAR_TSQE_INVALID)
+ return;
+
+ do {
+ TXPRINTK("tsqe: 0x%p [0x%08x 0x%08x]\n", tsqe,
+ le32_to_cpu(tsqe->word_1),
+ le32_to_cpu(tsqe->word_2));
+
+ switch (stat & SAR_TSQE_TYPE) {
+ case SAR_TSQE_TYPE_TIMER:
+ TXPRINTK("%s: Timer RollOver detected.\n", card->name);
+ break;
+
+ case SAR_TSQE_TYPE_IDLE:
+
+ conn = le32_to_cpu(tsqe->word_1);
+
+ if (SAR_TSQE_TAG(stat) == 0x10) {
+#ifdef NOTDEF
+ printk("%s: Connection %d halted.\n",
+ card->name,
+ le32_to_cpu(tsqe->word_1) & 0x1fff);
+#endif
+ break;
+ }
+
+ vc = card->vcs[conn & 0x1fff];
+ if (!vc) {
+ printk("%s: could not find VC from conn %d\n",
+ card->name, conn & 0x1fff);
+ break;
+ }
+
+ printk("%s: Connection %d IDLE.\n",
+ card->name, vc->index);
+
+ set_bit(VCF_IDLE, &vc->flags);
+ break;
+
+ case SAR_TSQE_TYPE_TSR:
+
+ conn = le32_to_cpu(tsqe->word_1);
+
+ vc = card->vcs[conn & 0x1fff];
+ if (!vc) {
+ printk("%s: no VC at index %d\n",
+ card->name,
+ le32_to_cpu(tsqe->word_1) & 0x1fff);
+ break;
+ }
+
+ drain_scq(card, vc);
+ break;
+
+ case SAR_TSQE_TYPE_TBD_COMP:
+
+ conn = le32_to_cpu(tsqe->word_1);
+
+ vpi = (conn >> SAR_TBD_VPI_SHIFT) & 0x00ff;
+ vci = (conn >> SAR_TBD_VCI_SHIFT) & 0xffff;
+
+ if (vpi >= (1 << card->vpibits) ||
+ vci >= (1 << card->vcibits)) {
+ printk("%s: TBD complete: "
+ "out of range VPI.VCI %u.%u\n",
+ card->name, vpi, vci);
+ break;
+ }
+
+ vc = card->vcs[VPCI2VC(card, vpi, vci)];
+ if (!vc) {
+ printk("%s: TBD complete: "
+ "no VC at VPI.VCI %u.%u\n",
+ card->name, vpi, vci);
+ break;
+ }
+
+ drain_scq(card, vc);
+ break;
+ }
+
+ tsqe->word_2 = cpu_to_le32(SAR_TSQE_INVALID);
+
+ card->tsq.next = tsqe;
+ if (card->tsq.next == card->tsq.last)
+ tsqe = card->tsq.base;
+ else
+ tsqe = card->tsq.next + 1;
+
+ TXPRINTK("tsqe: %p: base %p, next %p, last %p\n", tsqe,
+ card->tsq.base, card->tsq.next, card->tsq.last);
+
+ stat = le32_to_cpu(tsqe->word_2);
+
+ } while (!(stat & SAR_TSQE_INVALID));
+
+ writel((unsigned long)card->tsq.next - (unsigned long)card->tsq.base,
+ SAR_REG_TSQH);
+
+ XPRINTK("idt77252_tx-after writel%d: TSQ head = 0x%x, tail = 0x%x, next = 0x%p.\n",
+ card->index, readl(SAR_REG_TSQH),
+ readl(SAR_REG_TSQT), card->tsq.next);
+}
+
+
+static void
+tst_timer(unsigned long data)
+{
+ struct idt77252_dev *card = (struct idt77252_dev *)data;
+ unsigned long base, idle, jump;
+ unsigned long flags;
+ u32 pc;
+ int e;
+
+ spin_lock_irqsave(&card->tst_lock, flags);
+
+ base = card->tst[card->tst_index];
+ idle = card->tst[card->tst_index ^ 1];
+
+ if (test_bit(TST_SWITCH_WAIT, &card->tst_state)) {
+ jump = base + card->tst_size - 2;
+
+ pc = readl(SAR_REG_NOW) >> 2;
+ if ((pc ^ idle) & ~(card->tst_size - 1)) {
+ mod_timer(&card->tst_timer, jiffies + 1);
+ goto out;
+ }
+
+ clear_bit(TST_SWITCH_WAIT, &card->tst_state);
+
+ card->tst_index ^= 1;
+ write_sram(card, jump, TSTE_OPC_JMP | (base << 2));
+
+ base = card->tst[card->tst_index];
+ idle = card->tst[card->tst_index ^ 1];
+
+ for (e = 0; e < card->tst_size - 2; e++) {
+ if (card->soft_tst[e].tste & TSTE_PUSH_IDLE) {
+ write_sram(card, idle + e,
+ card->soft_tst[e].tste & TSTE_MASK);
+ card->soft_tst[e].tste &= ~(TSTE_PUSH_IDLE);
+ }
+ }
+ }
+
+ if (test_and_clear_bit(TST_SWITCH_PENDING, &card->tst_state)) {
+
+ for (e = 0; e < card->tst_size - 2; e++) {
+ if (card->soft_tst[e].tste & TSTE_PUSH_ACTIVE) {
+ write_sram(card, idle + e,
+ card->soft_tst[e].tste & TSTE_MASK);
+ card->soft_tst[e].tste &= ~(TSTE_PUSH_ACTIVE);
+ card->soft_tst[e].tste |= TSTE_PUSH_IDLE;
+ }
+ }
+
+ jump = base + card->tst_size - 2;
+
+ write_sram(card, jump, TSTE_OPC_NULL);
+ set_bit(TST_SWITCH_WAIT, &card->tst_state);
+
+ mod_timer(&card->tst_timer, jiffies + 1);
+ }
+
+out:
+ spin_unlock_irqrestore(&card->tst_lock, flags);
+}
+
+static int
+__fill_tst(struct idt77252_dev *card, struct vc_map *vc,
+ int n, unsigned int opc)
+{
+ unsigned long cl, avail;
+ unsigned long idle;
+ int e, r;
+ u32 data;
+
+ avail = card->tst_size - 2;
+ for (e = 0; e < avail; e++) {
+ if (card->soft_tst[e].vc == NULL)
+ break;
+ }
+ if (e >= avail) {
+ printk("%s: No free TST entries found\n", card->name);
+ return -1;
+ }
+
+ NPRINTK("%s: conn %d: first TST entry at %d.\n",
+ card->name, vc ? vc->index : -1, e);
+
+ r = n;
+ cl = avail;
+ data = opc & TSTE_OPC_MASK;
+ if (vc && (opc != TSTE_OPC_NULL))
+ data = opc | vc->index;
+
+ idle = card->tst[card->tst_index ^ 1];
+
+ /*
+ * Fill Soft TST.
+ */
+ while (r > 0) {
+ if ((cl >= avail) && (card->soft_tst[e].vc == NULL)) {
+ if (vc)
+ card->soft_tst[e].vc = vc;
+ else
+ card->soft_tst[e].vc = (void *)-1;
+
+ card->soft_tst[e].tste = data;
+ if (timer_pending(&card->tst_timer))
+ card->soft_tst[e].tste |= TSTE_PUSH_ACTIVE;
+ else {
+ write_sram(card, idle + e, data);
+ card->soft_tst[e].tste |= TSTE_PUSH_IDLE;
+ }
+
+ cl -= card->tst_size;
+ r--;
+ }
+
+ if (++e == avail)
+ e = 0;
+ cl += n;
+ }
+
+ return 0;
+}
+
+static int
+fill_tst(struct idt77252_dev *card, struct vc_map *vc, int n, unsigned int opc)
+{
+ unsigned long flags;
+ int res;
+
+ spin_lock_irqsave(&card->tst_lock, flags);
+
+ res = __fill_tst(card, vc, n, opc);
+
+ set_bit(TST_SWITCH_PENDING, &card->tst_state);
+ if (!timer_pending(&card->tst_timer))
+ mod_timer(&card->tst_timer, jiffies + 1);
+
+ spin_unlock_irqrestore(&card->tst_lock, flags);
+ return res;
+}
+
+static int
+__clear_tst(struct idt77252_dev *card, struct vc_map *vc)
+{
+ unsigned long idle;
+ int e;
+
+ idle = card->tst[card->tst_index ^ 1];
+
+ for (e = 0; e < card->tst_size - 2; e++) {
+ if (card->soft_tst[e].vc == vc) {
+ card->soft_tst[e].vc = NULL;
+
+ card->soft_tst[e].tste = TSTE_OPC_VAR;
+ if (timer_pending(&card->tst_timer))
+ card->soft_tst[e].tste |= TSTE_PUSH_ACTIVE;
+ else {
+ write_sram(card, idle + e, TSTE_OPC_VAR);
+ card->soft_tst[e].tste |= TSTE_PUSH_IDLE;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+clear_tst(struct idt77252_dev *card, struct vc_map *vc)
+{
+ unsigned long flags;
+ int res;
+
+ spin_lock_irqsave(&card->tst_lock, flags);
+
+ res = __clear_tst(card, vc);
+
+ set_bit(TST_SWITCH_PENDING, &card->tst_state);
+ if (!timer_pending(&card->tst_timer))
+ mod_timer(&card->tst_timer, jiffies + 1);
+
+ spin_unlock_irqrestore(&card->tst_lock, flags);
+ return res;
+}
+
+static int
+change_tst(struct idt77252_dev *card, struct vc_map *vc,
+ int n, unsigned int opc)
+{
+ unsigned long flags;
+ int res;
+
+ spin_lock_irqsave(&card->tst_lock, flags);
+
+ __clear_tst(card, vc);
+ res = __fill_tst(card, vc, n, opc);
+
+ set_bit(TST_SWITCH_PENDING, &card->tst_state);
+ if (!timer_pending(&card->tst_timer))
+ mod_timer(&card->tst_timer, jiffies + 1);
+
+ spin_unlock_irqrestore(&card->tst_lock, flags);
+ return res;
+}
+
+
+static int
+set_tct(struct idt77252_dev *card, struct vc_map *vc)
+{
+ unsigned long tct;
+
+ tct = (unsigned long) (card->tct_base + vc->index * SAR_SRAM_TCT_SIZE);
+
+ switch (vc->class) {
+ case SCHED_CBR:
+ OPRINTK("%s: writing TCT at 0x%lx, SCD 0x%lx.\n",
+ card->name, tct, vc->scq->scd);
+
+ write_sram(card, tct + 0, TCT_CBR | vc->scq->scd);
+ write_sram(card, tct + 1, 0);
+ write_sram(card, tct + 2, 0);
+ write_sram(card, tct + 3, 0);
+ write_sram(card, tct + 4, 0);
+ write_sram(card, tct + 5, 0);
+ write_sram(card, tct + 6, 0);
+ write_sram(card, tct + 7, 0);
+ break;
+
+ case SCHED_UBR:
+ OPRINTK("%s: writing TCT at 0x%lx, SCD 0x%lx.\n",
+ card->name, tct, vc->scq->scd);
+
+ write_sram(card, tct + 0, TCT_UBR | vc->scq->scd);
+ write_sram(card, tct + 1, 0);
+ write_sram(card, tct + 2, TCT_TSIF);
+ write_sram(card, tct + 3, TCT_HALT | TCT_IDLE);
+ write_sram(card, tct + 4, 0);
+ write_sram(card, tct + 5, vc->init_er);
+ write_sram(card, tct + 6, 0);
+ write_sram(card, tct + 7, TCT_FLAG_UBR);
+ break;
+
+ case SCHED_VBR:
+ case SCHED_ABR:
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+/* */
+/* FBQ Handling */
+/* */
+/*****************************************************************************/
+
+static __inline__ int
+idt77252_fbq_level(struct idt77252_dev *card, int queue)
+{
+ return (readl(SAR_REG_STAT) >> (16 + (queue << 2))) & 0x0f;
+}
+
+static __inline__ int
+idt77252_fbq_full(struct idt77252_dev *card, int queue)
+{
+ return (readl(SAR_REG_STAT) >> (16 + (queue << 2))) == 0x0f;
+}
+
+static int
+push_rx_skb(struct idt77252_dev *card, struct sk_buff *skb, int queue)
+{
+ unsigned long flags;
+ u32 handle;
+ u32 addr;
+
+ skb->data = skb->head;
+ skb_reset_tail_pointer(skb);
+ skb->len = 0;
+
+ skb_reserve(skb, 16);
+
+ switch (queue) {
+ case 0:
+ skb_put(skb, SAR_FB_SIZE_0);
+ break;
+ case 1:
+ skb_put(skb, SAR_FB_SIZE_1);
+ break;
+ case 2:
+ skb_put(skb, SAR_FB_SIZE_2);
+ break;
+ case 3:
+ skb_put(skb, SAR_FB_SIZE_3);
+ break;
+ default:
+ return -1;
+ }
+
+ if (idt77252_fbq_full(card, queue))
+ return -1;
+
+ memset(&skb->data[(skb->len & ~(0x3f)) - 64], 0, 2 * sizeof(u32));
+
+ handle = IDT77252_PRV_POOL(skb);
+ addr = IDT77252_PRV_PADDR(skb);
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel(handle, card->fbq[queue]);
+ writel(addr, card->fbq[queue]);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+
+ return 0;
+}
+
+static void
+add_rx_skb(struct idt77252_dev *card, int queue,
+ unsigned int size, unsigned int count)
+{
+ struct sk_buff *skb;
+ dma_addr_t paddr;
+ u32 handle;
+
+ while (count--) {
+ skb = dev_alloc_skb(size);
+ if (!skb)
+ return;
+
+ if (sb_pool_add(card, skb, queue)) {
+ printk("%s: SB POOL full\n", __func__);
+ goto outfree;
+ }
+
+ paddr = dma_map_single(&card->pcidev->dev, skb->data,
+ skb_end_pointer(skb) - skb->data,
+ DMA_FROM_DEVICE);
+ IDT77252_PRV_PADDR(skb) = paddr;
+
+ if (push_rx_skb(card, skb, queue)) {
+ printk("%s: FB QUEUE full\n", __func__);
+ goto outunmap;
+ }
+ }
+
+ return;
+
+outunmap:
+ dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb_end_pointer(skb) - skb->data, DMA_FROM_DEVICE);
+
+ handle = IDT77252_PRV_POOL(skb);
+ card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL;
+
+outfree:
+ dev_kfree_skb(skb);
+}
+
+
+static void
+recycle_rx_skb(struct idt77252_dev *card, struct sk_buff *skb)
+{
+ u32 handle = IDT77252_PRV_POOL(skb);
+ int err;
+
+ dma_sync_single_for_device(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb_end_pointer(skb) - skb->data,
+ DMA_FROM_DEVICE);
+
+ err = push_rx_skb(card, skb, POOL_QUEUE(handle));
+ if (err) {
+ dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb),
+ skb_end_pointer(skb) - skb->data,
+ DMA_FROM_DEVICE);
+ sb_pool_remove(card, skb);
+ dev_kfree_skb(skb);
+ }
+}
+
+static void
+flush_rx_pool(struct idt77252_dev *card, struct rx_pool *rpp)
+{
+ skb_queue_head_init(&rpp->queue);
+ rpp->len = 0;
+}
+
+static void
+recycle_rx_pool_skb(struct idt77252_dev *card, struct rx_pool *rpp)
+{
+ struct sk_buff *skb, *tmp;
+
+ skb_queue_walk_safe(&rpp->queue, skb, tmp)
+ recycle_rx_skb(card, skb);
+
+ flush_rx_pool(card, rpp);
+}
+
+/*****************************************************************************/
+/* */
+/* ATM Interface */
+/* */
+/*****************************************************************************/
+
+static void
+idt77252_phy_put(struct atm_dev *dev, unsigned char value, unsigned long addr)
+{
+ write_utility(dev->dev_data, 0x100 + (addr & 0x1ff), value);
+}
+
+static unsigned char
+idt77252_phy_get(struct atm_dev *dev, unsigned long addr)
+{
+ return read_utility(dev->dev_data, 0x100 + (addr & 0x1ff));
+}
+
+static inline int
+idt77252_send_skb(struct atm_vcc *vcc, struct sk_buff *skb, int oam)
+{
+ struct atm_dev *dev = vcc->dev;
+ struct idt77252_dev *card = dev->dev_data;
+ struct vc_map *vc = vcc->dev_data;
+ int err;
+
+ if (vc == NULL) {
+ printk("%s: NULL connection in send().\n", card->name);
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+ if (!test_bit(VCF_TX, &vc->flags)) {
+ printk("%s: Trying to transmit on a non-tx VC.\n", card->name);
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ switch (vcc->qos.aal) {
+ case ATM_AAL0:
+ case ATM_AAL1:
+ case ATM_AAL5:
+ break;
+ default:
+ printk("%s: Unsupported AAL: %d\n", card->name, vcc->qos.aal);
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ if (skb_shinfo(skb)->nr_frags != 0) {
+ printk("%s: No scatter-gather yet.\n", card->name);
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+ ATM_SKB(skb)->vcc = vcc;
+
+ err = queue_skb(card, vc, skb, oam);
+ if (err) {
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb(skb);
+ return err;
+ }
+
+ return 0;
+}
+
+static int idt77252_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ return idt77252_send_skb(vcc, skb, 0);
+}
+
+static int
+idt77252_send_oam(struct atm_vcc *vcc, void *cell, int flags)
+{
+ struct atm_dev *dev = vcc->dev;
+ struct idt77252_dev *card = dev->dev_data;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(64);
+ if (!skb) {
+ printk("%s: Out of memory in send_oam().\n", card->name);
+ atomic_inc(&vcc->stats->tx_err);
+ return -ENOMEM;
+ }
+ atomic_add(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc);
+
+ memcpy(skb_put(skb, 52), cell, 52);
+
+ return idt77252_send_skb(vcc, skb, 1);
+}
+
+static __inline__ unsigned int
+idt77252_fls(unsigned int x)
+{
+ int r = 1;
+
+ if (x == 0)
+ return 0;
+ if (x & 0xffff0000) {
+ x >>= 16;
+ r += 16;
+ }
+ if (x & 0xff00) {
+ x >>= 8;
+ r += 8;
+ }
+ if (x & 0xf0) {
+ x >>= 4;
+ r += 4;
+ }
+ if (x & 0xc) {
+ x >>= 2;
+ r += 2;
+ }
+ if (x & 0x2)
+ r += 1;
+ return r;
+}
+
+static u16
+idt77252_int_to_atmfp(unsigned int rate)
+{
+ u16 m, e;
+
+ if (rate == 0)
+ return 0;
+ e = idt77252_fls(rate) - 1;
+ if (e < 9)
+ m = (rate - (1 << e)) << (9 - e);
+ else if (e == 9)
+ m = (rate - (1 << e));
+ else /* e > 9 */
+ m = (rate - (1 << e)) >> (e - 9);
+ return 0x4000 | (e << 9) | m;
+}
+
+static u8
+idt77252_rate_logindex(struct idt77252_dev *card, int pcr)
+{
+ u16 afp;
+
+ afp = idt77252_int_to_atmfp(pcr < 0 ? -pcr : pcr);
+ if (pcr < 0)
+ return rate_to_log[(afp >> 5) & 0x1ff];
+ return rate_to_log[((afp >> 5) + 1) & 0x1ff];
+}
+
+static void
+idt77252_est_timer(unsigned long data)
+{
+ struct vc_map *vc = (struct vc_map *)data;
+ struct idt77252_dev *card = vc->card;
+ struct rate_estimator *est;
+ unsigned long flags;
+ u32 rate, cps;
+ u64 ncells;
+ u8 lacr;
+
+ spin_lock_irqsave(&vc->lock, flags);
+ est = vc->estimator;
+ if (!est)
+ goto out;
+
+ ncells = est->cells;
+
+ rate = ((u32)(ncells - est->last_cells)) << (7 - est->interval);
+ est->last_cells = ncells;
+ est->avcps += ((long)rate - (long)est->avcps) >> est->ewma_log;
+ est->cps = (est->avcps + 0x1f) >> 5;
+
+ cps = est->cps;
+ if (cps < (est->maxcps >> 4))
+ cps = est->maxcps >> 4;
+
+ lacr = idt77252_rate_logindex(card, cps);
+ if (lacr > vc->max_er)
+ lacr = vc->max_er;
+
+ if (lacr != vc->lacr) {
+ vc->lacr = lacr;
+ writel(TCMDQ_LACR|(vc->lacr << 16)|vc->index, SAR_REG_TCMDQ);
+ }
+
+ est->timer.expires = jiffies + ((HZ / 4) << est->interval);
+ add_timer(&est->timer);
+
+out:
+ spin_unlock_irqrestore(&vc->lock, flags);
+}
+
+static struct rate_estimator *
+idt77252_init_est(struct vc_map *vc, int pcr)
+{
+ struct rate_estimator *est;
+
+ est = kzalloc(sizeof(struct rate_estimator), GFP_KERNEL);
+ if (!est)
+ return NULL;
+ est->maxcps = pcr < 0 ? -pcr : pcr;
+ est->cps = est->maxcps;
+ est->avcps = est->cps << 5;
+
+ est->interval = 2; /* XXX: make this configurable */
+ est->ewma_log = 2; /* XXX: make this configurable */
+ init_timer(&est->timer);
+ est->timer.data = (unsigned long)vc;
+ est->timer.function = idt77252_est_timer;
+
+ est->timer.expires = jiffies + ((HZ / 4) << est->interval);
+ add_timer(&est->timer);
+
+ return est;
+}
+
+static int
+idt77252_init_cbr(struct idt77252_dev *card, struct vc_map *vc,
+ struct atm_vcc *vcc, struct atm_qos *qos)
+{
+ int tst_free, tst_used, tst_entries;
+ unsigned long tmpl, modl;
+ int tcr, tcra;
+
+ if ((qos->txtp.max_pcr == 0) &&
+ (qos->txtp.pcr == 0) && (qos->txtp.min_pcr == 0)) {
+ printk("%s: trying to open a CBR VC with cell rate = 0\n",
+ card->name);
+ return -EINVAL;
+ }
+
+ tst_used = 0;
+ tst_free = card->tst_free;
+ if (test_bit(VCF_TX, &vc->flags))
+ tst_used = vc->ntste;
+ tst_free += tst_used;
+
+ tcr = atm_pcr_goal(&qos->txtp);
+ tcra = tcr >= 0 ? tcr : -tcr;
+
+ TXPRINTK("%s: CBR target cell rate = %d\n", card->name, tcra);
+
+ tmpl = (unsigned long) tcra * ((unsigned long) card->tst_size - 2);
+ modl = tmpl % (unsigned long)card->utopia_pcr;
+
+ tst_entries = (int) (tmpl / card->utopia_pcr);
+ if (tcr > 0) {
+ if (modl > 0)
+ tst_entries++;
+ } else if (tcr == 0) {
+ tst_entries = tst_free - SAR_TST_RESERVED;
+ if (tst_entries <= 0) {
+ printk("%s: no CBR bandwidth free.\n", card->name);
+ return -ENOSR;
+ }
+ }
+
+ if (tst_entries == 0) {
+ printk("%s: selected CBR bandwidth < granularity.\n",
+ card->name);
+ return -EINVAL;
+ }
+
+ if (tst_entries > (tst_free - SAR_TST_RESERVED)) {
+ printk("%s: not enough CBR bandwidth free.\n", card->name);
+ return -ENOSR;
+ }
+
+ vc->ntste = tst_entries;
+
+ card->tst_free = tst_free - tst_entries;
+ if (test_bit(VCF_TX, &vc->flags)) {
+ if (tst_used == tst_entries)
+ return 0;
+
+ OPRINTK("%s: modify %d -> %d entries in TST.\n",
+ card->name, tst_used, tst_entries);
+ change_tst(card, vc, tst_entries, TSTE_OPC_CBR);
+ return 0;
+ }
+
+ OPRINTK("%s: setting %d entries in TST.\n", card->name, tst_entries);
+ fill_tst(card, vc, tst_entries, TSTE_OPC_CBR);
+ return 0;
+}
+
+static int
+idt77252_init_ubr(struct idt77252_dev *card, struct vc_map *vc,
+ struct atm_vcc *vcc, struct atm_qos *qos)
+{
+ unsigned long flags;
+ int tcr;
+
+ spin_lock_irqsave(&vc->lock, flags);
+ if (vc->estimator) {
+ del_timer(&vc->estimator->timer);
+ kfree(vc->estimator);
+ vc->estimator = NULL;
+ }
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ tcr = atm_pcr_goal(&qos->txtp);
+ if (tcr == 0)
+ tcr = card->link_pcr;
+
+ vc->estimator = idt77252_init_est(vc, tcr);
+
+ vc->class = SCHED_UBR;
+ vc->init_er = idt77252_rate_logindex(card, tcr);
+ vc->lacr = vc->init_er;
+ if (tcr < 0)
+ vc->max_er = vc->init_er;
+ else
+ vc->max_er = 0xff;
+
+ return 0;
+}
+
+static int
+idt77252_init_tx(struct idt77252_dev *card, struct vc_map *vc,
+ struct atm_vcc *vcc, struct atm_qos *qos)
+{
+ int error;
+
+ if (test_bit(VCF_TX, &vc->flags))
+ return -EBUSY;
+
+ switch (qos->txtp.traffic_class) {
+ case ATM_CBR:
+ vc->class = SCHED_CBR;
+ break;
+
+ case ATM_UBR:
+ vc->class = SCHED_UBR;
+ break;
+
+ case ATM_VBR:
+ case ATM_ABR:
+ default:
+ return -EPROTONOSUPPORT;
+ }
+
+ vc->scq = alloc_scq(card, vc->class);
+ if (!vc->scq) {
+ printk("%s: can't get SCQ.\n", card->name);
+ return -ENOMEM;
+ }
+
+ vc->scq->scd = get_free_scd(card, vc);
+ if (vc->scq->scd == 0) {
+ printk("%s: no SCD available.\n", card->name);
+ free_scq(card, vc->scq);
+ return -ENOMEM;
+ }
+
+ fill_scd(card, vc->scq, vc->class);
+
+ if (set_tct(card, vc)) {
+ printk("%s: class %d not supported.\n",
+ card->name, qos->txtp.traffic_class);
+
+ card->scd2vc[vc->scd_index] = NULL;
+ free_scq(card, vc->scq);
+ return -EPROTONOSUPPORT;
+ }
+
+ switch (vc->class) {
+ case SCHED_CBR:
+ error = idt77252_init_cbr(card, vc, vcc, qos);
+ if (error) {
+ card->scd2vc[vc->scd_index] = NULL;
+ free_scq(card, vc->scq);
+ return error;
+ }
+
+ clear_bit(VCF_IDLE, &vc->flags);
+ writel(TCMDQ_START | vc->index, SAR_REG_TCMDQ);
+ break;
+
+ case SCHED_UBR:
+ error = idt77252_init_ubr(card, vc, vcc, qos);
+ if (error) {
+ card->scd2vc[vc->scd_index] = NULL;
+ free_scq(card, vc->scq);
+ return error;
+ }
+
+ set_bit(VCF_IDLE, &vc->flags);
+ break;
+ }
+
+ vc->tx_vcc = vcc;
+ set_bit(VCF_TX, &vc->flags);
+ return 0;
+}
+
+static int
+idt77252_init_rx(struct idt77252_dev *card, struct vc_map *vc,
+ struct atm_vcc *vcc, struct atm_qos *qos)
+{
+ unsigned long flags;
+ unsigned long addr;
+ u32 rcte = 0;
+
+ if (test_bit(VCF_RX, &vc->flags))
+ return -EBUSY;
+
+ vc->rx_vcc = vcc;
+ set_bit(VCF_RX, &vc->flags);
+
+ if ((vcc->vci == 3) || (vcc->vci == 4))
+ return 0;
+
+ flush_rx_pool(card, &vc->rcv.rx_pool);
+
+ rcte |= SAR_RCTE_CONNECTOPEN;
+ rcte |= SAR_RCTE_RAWCELLINTEN;
+
+ switch (qos->aal) {
+ case ATM_AAL0:
+ rcte |= SAR_RCTE_RCQ;
+ break;
+ case ATM_AAL1:
+ rcte |= SAR_RCTE_OAM; /* Let SAR drop Video */
+ break;
+ case ATM_AAL34:
+ rcte |= SAR_RCTE_AAL34;
+ break;
+ case ATM_AAL5:
+ rcte |= SAR_RCTE_AAL5;
+ break;
+ default:
+ rcte |= SAR_RCTE_RCQ;
+ break;
+ }
+
+ if (qos->aal != ATM_AAL5)
+ rcte |= SAR_RCTE_FBP_1;
+ else if (qos->rxtp.max_sdu > SAR_FB_SIZE_2)
+ rcte |= SAR_RCTE_FBP_3;
+ else if (qos->rxtp.max_sdu > SAR_FB_SIZE_1)
+ rcte |= SAR_RCTE_FBP_2;
+ else if (qos->rxtp.max_sdu > SAR_FB_SIZE_0)
+ rcte |= SAR_RCTE_FBP_1;
+ else
+ rcte |= SAR_RCTE_FBP_01;
+
+ addr = card->rct_base + (vc->index << 2);
+
+ OPRINTK("%s: writing RCT at 0x%lx\n", card->name, addr);
+ write_sram(card, addr, rcte);
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel(SAR_CMD_OPEN_CONNECTION | (addr << 2), SAR_REG_CMD);
+ waitfor_idle(card);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+
+ return 0;
+}
+
+static int
+idt77252_open(struct atm_vcc *vcc)
+{
+ struct atm_dev *dev = vcc->dev;
+ struct idt77252_dev *card = dev->dev_data;
+ struct vc_map *vc;
+ unsigned int index;
+ unsigned int inuse;
+ int error;
+ int vci = vcc->vci;
+ short vpi = vcc->vpi;
+
+ if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
+ return 0;
+
+ if (vpi >= (1 << card->vpibits)) {
+ printk("%s: unsupported VPI: %d\n", card->name, vpi);
+ return -EINVAL;
+ }
+
+ if (vci >= (1 << card->vcibits)) {
+ printk("%s: unsupported VCI: %d\n", card->name, vci);
+ return -EINVAL;
+ }
+
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+
+ mutex_lock(&card->mutex);
+
+ OPRINTK("%s: opening vpi.vci: %d.%d\n", card->name, vpi, vci);
+
+ switch (vcc->qos.aal) {
+ case ATM_AAL0:
+ case ATM_AAL1:
+ case ATM_AAL5:
+ break;
+ default:
+ printk("%s: Unsupported AAL: %d\n", card->name, vcc->qos.aal);
+ mutex_unlock(&card->mutex);
+ return -EPROTONOSUPPORT;
+ }
+
+ index = VPCI2VC(card, vpi, vci);
+ if (!card->vcs[index]) {
+ card->vcs[index] = kzalloc(sizeof(struct vc_map), GFP_KERNEL);
+ if (!card->vcs[index]) {
+ printk("%s: can't alloc vc in open()\n", card->name);
+ mutex_unlock(&card->mutex);
+ return -ENOMEM;
+ }
+ card->vcs[index]->card = card;
+ card->vcs[index]->index = index;
+
+ spin_lock_init(&card->vcs[index]->lock);
+ }
+ vc = card->vcs[index];
+
+ vcc->dev_data = vc;
+
+ IPRINTK("%s: idt77252_open: vc = %d (%d.%d) %s/%s (max RX SDU: %u)\n",
+ card->name, vc->index, vcc->vpi, vcc->vci,
+ vcc->qos.rxtp.traffic_class != ATM_NONE ? "rx" : "--",
+ vcc->qos.txtp.traffic_class != ATM_NONE ? "tx" : "--",
+ vcc->qos.rxtp.max_sdu);
+
+ inuse = 0;
+ if (vcc->qos.txtp.traffic_class != ATM_NONE &&
+ test_bit(VCF_TX, &vc->flags))
+ inuse = 1;
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE &&
+ test_bit(VCF_RX, &vc->flags))
+ inuse += 2;
+
+ if (inuse) {
+ printk("%s: %s vci already in use.\n", card->name,
+ inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx");
+ mutex_unlock(&card->mutex);
+ return -EADDRINUSE;
+ }
+
+ if (vcc->qos.txtp.traffic_class != ATM_NONE) {
+ error = idt77252_init_tx(card, vc, vcc, &vcc->qos);
+ if (error) {
+ mutex_unlock(&card->mutex);
+ return error;
+ }
+ }
+
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ error = idt77252_init_rx(card, vc, vcc, &vcc->qos);
+ if (error) {
+ mutex_unlock(&card->mutex);
+ return error;
+ }
+ }
+
+ set_bit(ATM_VF_READY, &vcc->flags);
+
+ mutex_unlock(&card->mutex);
+ return 0;
+}
+
+static void
+idt77252_close(struct atm_vcc *vcc)
+{
+ struct atm_dev *dev = vcc->dev;
+ struct idt77252_dev *card = dev->dev_data;
+ struct vc_map *vc = vcc->dev_data;
+ unsigned long flags;
+ unsigned long addr;
+ unsigned long timeout;
+
+ mutex_lock(&card->mutex);
+
+ IPRINTK("%s: idt77252_close: vc = %d (%d.%d)\n",
+ card->name, vc->index, vcc->vpi, vcc->vci);
+
+ clear_bit(ATM_VF_READY, &vcc->flags);
+
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
+
+ spin_lock_irqsave(&vc->lock, flags);
+ clear_bit(VCF_RX, &vc->flags);
+ vc->rx_vcc = NULL;
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ if ((vcc->vci == 3) || (vcc->vci == 4))
+ goto done;
+
+ addr = card->rct_base + vc->index * SAR_SRAM_RCT_SIZE;
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel(SAR_CMD_CLOSE_CONNECTION | (addr << 2), SAR_REG_CMD);
+ waitfor_idle(card);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+
+ if (skb_queue_len(&vc->rcv.rx_pool.queue) != 0) {
+ DPRINTK("%s: closing a VC with pending rx buffers.\n",
+ card->name);
+
+ recycle_rx_pool_skb(card, &vc->rcv.rx_pool);
+ }
+ }
+
+done:
+ if (vcc->qos.txtp.traffic_class != ATM_NONE) {
+
+ spin_lock_irqsave(&vc->lock, flags);
+ clear_bit(VCF_TX, &vc->flags);
+ clear_bit(VCF_IDLE, &vc->flags);
+ clear_bit(VCF_RSV, &vc->flags);
+ vc->tx_vcc = NULL;
+
+ if (vc->estimator) {
+ del_timer(&vc->estimator->timer);
+ kfree(vc->estimator);
+ vc->estimator = NULL;
+ }
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ timeout = 5 * 1000;
+ while (atomic_read(&vc->scq->used) > 0) {
+ timeout = msleep_interruptible(timeout);
+ if (!timeout) {
+ pr_warn("%s: SCQ drain timeout: %u used\n",
+ card->name, atomic_read(&vc->scq->used));
+ break;
+ }
+ }
+
+ writel(TCMDQ_HALT | vc->index, SAR_REG_TCMDQ);
+ clear_scd(card, vc->scq, vc->class);
+
+ if (vc->class == SCHED_CBR) {
+ clear_tst(card, vc);
+ card->tst_free += vc->ntste;
+ vc->ntste = 0;
+ }
+
+ card->scd2vc[vc->scd_index] = NULL;
+ free_scq(card, vc->scq);
+ }
+
+ mutex_unlock(&card->mutex);
+}
+
+static int
+idt77252_change_qos(struct atm_vcc *vcc, struct atm_qos *qos, int flags)
+{
+ struct atm_dev *dev = vcc->dev;
+ struct idt77252_dev *card = dev->dev_data;
+ struct vc_map *vc = vcc->dev_data;
+ int error = 0;
+
+ mutex_lock(&card->mutex);
+
+ if (qos->txtp.traffic_class != ATM_NONE) {
+ if (!test_bit(VCF_TX, &vc->flags)) {
+ error = idt77252_init_tx(card, vc, vcc, qos);
+ if (error)
+ goto out;
+ } else {
+ switch (qos->txtp.traffic_class) {
+ case ATM_CBR:
+ error = idt77252_init_cbr(card, vc, vcc, qos);
+ if (error)
+ goto out;
+ break;
+
+ case ATM_UBR:
+ error = idt77252_init_ubr(card, vc, vcc, qos);
+ if (error)
+ goto out;
+
+ if (!test_bit(VCF_IDLE, &vc->flags)) {
+ writel(TCMDQ_LACR | (vc->lacr << 16) |
+ vc->index, SAR_REG_TCMDQ);
+ }
+ break;
+
+ case ATM_VBR:
+ case ATM_ABR:
+ error = -EOPNOTSUPP;
+ goto out;
+ }
+ }
+ }
+
+ if ((qos->rxtp.traffic_class != ATM_NONE) &&
+ !test_bit(VCF_RX, &vc->flags)) {
+ error = idt77252_init_rx(card, vc, vcc, qos);
+ if (error)
+ goto out;
+ }
+
+ memcpy(&vcc->qos, qos, sizeof(struct atm_qos));
+
+ set_bit(ATM_VF_HASQOS, &vcc->flags);
+
+out:
+ mutex_unlock(&card->mutex);
+ return error;
+}
+
+static int
+idt77252_proc_read(struct atm_dev *dev, loff_t * pos, char *page)
+{
+ struct idt77252_dev *card = dev->dev_data;
+ int i, left;
+
+ left = (int) *pos;
+ if (!left--)
+ return sprintf(page, "IDT77252 Interrupts:\n");
+ if (!left--)
+ return sprintf(page, "TSIF: %lu\n", card->irqstat[15]);
+ if (!left--)
+ return sprintf(page, "TXICP: %lu\n", card->irqstat[14]);
+ if (!left--)
+ return sprintf(page, "TSQF: %lu\n", card->irqstat[12]);
+ if (!left--)
+ return sprintf(page, "TMROF: %lu\n", card->irqstat[11]);
+ if (!left--)
+ return sprintf(page, "PHYI: %lu\n", card->irqstat[10]);
+ if (!left--)
+ return sprintf(page, "FBQ3A: %lu\n", card->irqstat[8]);
+ if (!left--)
+ return sprintf(page, "FBQ2A: %lu\n", card->irqstat[7]);
+ if (!left--)
+ return sprintf(page, "RSQF: %lu\n", card->irqstat[6]);
+ if (!left--)
+ return sprintf(page, "EPDU: %lu\n", card->irqstat[5]);
+ if (!left--)
+ return sprintf(page, "RAWCF: %lu\n", card->irqstat[4]);
+ if (!left--)
+ return sprintf(page, "FBQ1A: %lu\n", card->irqstat[3]);
+ if (!left--)
+ return sprintf(page, "FBQ0A: %lu\n", card->irqstat[2]);
+ if (!left--)
+ return sprintf(page, "RSQAF: %lu\n", card->irqstat[1]);
+ if (!left--)
+ return sprintf(page, "IDT77252 Transmit Connection Table:\n");
+
+ for (i = 0; i < card->tct_size; i++) {
+ unsigned long tct;
+ struct atm_vcc *vcc;
+ struct vc_map *vc;
+ char *p;
+
+ vc = card->vcs[i];
+ if (!vc)
+ continue;
+
+ vcc = NULL;
+ if (vc->tx_vcc)
+ vcc = vc->tx_vcc;
+ if (!vcc)
+ continue;
+ if (left--)
+ continue;
+
+ p = page;
+ p += sprintf(p, " %4u: %u.%u: ", i, vcc->vpi, vcc->vci);
+ tct = (unsigned long) (card->tct_base + i * SAR_SRAM_TCT_SIZE);
+
+ for (i = 0; i < 8; i++)
+ p += sprintf(p, " %08x", read_sram(card, tct + i));
+ p += sprintf(p, "\n");
+ return p - page;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/* */
+/* Interrupt handler */
+/* */
+/*****************************************************************************/
+
+static void
+idt77252_collect_stat(struct idt77252_dev *card)
+{
+ (void) readl(SAR_REG_CDC);
+ (void) readl(SAR_REG_VPEC);
+ (void) readl(SAR_REG_ICC);
+
+}
+
+static irqreturn_t
+idt77252_interrupt(int irq, void *dev_id)
+{
+ struct idt77252_dev *card = dev_id;
+ u32 stat;
+
+ stat = readl(SAR_REG_STAT) & 0xffff;
+ if (!stat) /* no interrupt for us */
+ return IRQ_NONE;
+
+ if (test_and_set_bit(IDT77252_BIT_INTERRUPT, &card->flags)) {
+ printk("%s: Re-entering irq_handler()\n", card->name);
+ goto out;
+ }
+
+ writel(stat, SAR_REG_STAT); /* reset interrupt */
+
+ if (stat & SAR_STAT_TSIF) { /* entry written to TSQ */
+ INTPRINTK("%s: TSIF\n", card->name);
+ card->irqstat[15]++;
+ idt77252_tx(card);
+ }
+ if (stat & SAR_STAT_TXICP) { /* Incomplete CS-PDU has */
+ INTPRINTK("%s: TXICP\n", card->name);
+ card->irqstat[14]++;
+#ifdef CONFIG_ATM_IDT77252_DEBUG
+ idt77252_tx_dump(card);
+#endif
+ }
+ if (stat & SAR_STAT_TSQF) { /* TSQ 7/8 full */
+ INTPRINTK("%s: TSQF\n", card->name);
+ card->irqstat[12]++;
+ idt77252_tx(card);
+ }
+ if (stat & SAR_STAT_TMROF) { /* Timer overflow */
+ INTPRINTK("%s: TMROF\n", card->name);
+ card->irqstat[11]++;
+ idt77252_collect_stat(card);
+ }
+
+ if (stat & SAR_STAT_EPDU) { /* Got complete CS-PDU */
+ INTPRINTK("%s: EPDU\n", card->name);
+ card->irqstat[5]++;
+ idt77252_rx(card);
+ }
+ if (stat & SAR_STAT_RSQAF) { /* RSQ is 7/8 full */
+ INTPRINTK("%s: RSQAF\n", card->name);
+ card->irqstat[1]++;
+ idt77252_rx(card);
+ }
+ if (stat & SAR_STAT_RSQF) { /* RSQ is full */
+ INTPRINTK("%s: RSQF\n", card->name);
+ card->irqstat[6]++;
+ idt77252_rx(card);
+ }
+ if (stat & SAR_STAT_RAWCF) { /* Raw cell received */
+ INTPRINTK("%s: RAWCF\n", card->name);
+ card->irqstat[4]++;
+ idt77252_rx_raw(card);
+ }
+
+ if (stat & SAR_STAT_PHYI) { /* PHY device interrupt */
+ INTPRINTK("%s: PHYI", card->name);
+ card->irqstat[10]++;
+ if (card->atmdev->phy && card->atmdev->phy->interrupt)
+ card->atmdev->phy->interrupt(card->atmdev);
+ }
+
+ if (stat & (SAR_STAT_FBQ0A | SAR_STAT_FBQ1A |
+ SAR_STAT_FBQ2A | SAR_STAT_FBQ3A)) {
+
+ writel(readl(SAR_REG_CFG) & ~(SAR_CFG_FBIE), SAR_REG_CFG);
+
+ INTPRINTK("%s: FBQA: %04x\n", card->name, stat);
+
+ if (stat & SAR_STAT_FBQ0A)
+ card->irqstat[2]++;
+ if (stat & SAR_STAT_FBQ1A)
+ card->irqstat[3]++;
+ if (stat & SAR_STAT_FBQ2A)
+ card->irqstat[7]++;
+ if (stat & SAR_STAT_FBQ3A)
+ card->irqstat[8]++;
+
+ schedule_work(&card->tqueue);
+ }
+
+out:
+ clear_bit(IDT77252_BIT_INTERRUPT, &card->flags);
+ return IRQ_HANDLED;
+}
+
+static void
+idt77252_softint(struct work_struct *work)
+{
+ struct idt77252_dev *card =
+ container_of(work, struct idt77252_dev, tqueue);
+ u32 stat;
+ int done;
+
+ for (done = 1; ; done = 1) {
+ stat = readl(SAR_REG_STAT) >> 16;
+
+ if ((stat & 0x0f) < SAR_FBQ0_HIGH) {
+ add_rx_skb(card, 0, SAR_FB_SIZE_0, 32);
+ done = 0;
+ }
+
+ stat >>= 4;
+ if ((stat & 0x0f) < SAR_FBQ1_HIGH) {
+ add_rx_skb(card, 1, SAR_FB_SIZE_1, 32);
+ done = 0;
+ }
+
+ stat >>= 4;
+ if ((stat & 0x0f) < SAR_FBQ2_HIGH) {
+ add_rx_skb(card, 2, SAR_FB_SIZE_2, 32);
+ done = 0;
+ }
+
+ stat >>= 4;
+ if ((stat & 0x0f) < SAR_FBQ3_HIGH) {
+ add_rx_skb(card, 3, SAR_FB_SIZE_3, 32);
+ done = 0;
+ }
+
+ if (done)
+ break;
+ }
+
+ writel(readl(SAR_REG_CFG) | SAR_CFG_FBIE, SAR_REG_CFG);
+}
+
+
+static int
+open_card_oam(struct idt77252_dev *card)
+{
+ unsigned long flags;
+ unsigned long addr;
+ struct vc_map *vc;
+ int vpi, vci;
+ int index;
+ u32 rcte;
+
+ for (vpi = 0; vpi < (1 << card->vpibits); vpi++) {
+ for (vci = 3; vci < 5; vci++) {
+ index = VPCI2VC(card, vpi, vci);
+
+ vc = kzalloc(sizeof(struct vc_map), GFP_KERNEL);
+ if (!vc) {
+ printk("%s: can't alloc vc\n", card->name);
+ return -ENOMEM;
+ }
+ vc->index = index;
+ card->vcs[index] = vc;
+
+ flush_rx_pool(card, &vc->rcv.rx_pool);
+
+ rcte = SAR_RCTE_CONNECTOPEN |
+ SAR_RCTE_RAWCELLINTEN |
+ SAR_RCTE_RCQ |
+ SAR_RCTE_FBP_1;
+
+ addr = card->rct_base + (vc->index << 2);
+ write_sram(card, addr, rcte);
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel(SAR_CMD_OPEN_CONNECTION | (addr << 2),
+ SAR_REG_CMD);
+ waitfor_idle(card);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+ }
+ }
+
+ return 0;
+}
+
+static void
+close_card_oam(struct idt77252_dev *card)
+{
+ unsigned long flags;
+ unsigned long addr;
+ struct vc_map *vc;
+ int vpi, vci;
+ int index;
+
+ for (vpi = 0; vpi < (1 << card->vpibits); vpi++) {
+ for (vci = 3; vci < 5; vci++) {
+ index = VPCI2VC(card, vpi, vci);
+ vc = card->vcs[index];
+
+ addr = card->rct_base + vc->index * SAR_SRAM_RCT_SIZE;
+
+ spin_lock_irqsave(&card->cmd_lock, flags);
+ writel(SAR_CMD_CLOSE_CONNECTION | (addr << 2),
+ SAR_REG_CMD);
+ waitfor_idle(card);
+ spin_unlock_irqrestore(&card->cmd_lock, flags);
+
+ if (skb_queue_len(&vc->rcv.rx_pool.queue) != 0) {
+ DPRINTK("%s: closing a VC "
+ "with pending rx buffers.\n",
+ card->name);
+
+ recycle_rx_pool_skb(card, &vc->rcv.rx_pool);
+ }
+ }
+ }
+}
+
+static int
+open_card_ubr0(struct idt77252_dev *card)
+{
+ struct vc_map *vc;
+
+ vc = kzalloc(sizeof(struct vc_map), GFP_KERNEL);
+ if (!vc) {
+ printk("%s: can't alloc vc\n", card->name);
+ return -ENOMEM;
+ }
+ card->vcs[0] = vc;
+ vc->class = SCHED_UBR0;
+
+ vc->scq = alloc_scq(card, vc->class);
+ if (!vc->scq) {
+ printk("%s: can't get SCQ.\n", card->name);
+ return -ENOMEM;
+ }
+
+ card->scd2vc[0] = vc;
+ vc->scd_index = 0;
+ vc->scq->scd = card->scd_base;
+
+ fill_scd(card, vc->scq, vc->class);
+
+ write_sram(card, card->tct_base + 0, TCT_UBR | card->scd_base);
+ write_sram(card, card->tct_base + 1, 0);
+ write_sram(card, card->tct_base + 2, 0);
+ write_sram(card, card->tct_base + 3, 0);
+ write_sram(card, card->tct_base + 4, 0);
+ write_sram(card, card->tct_base + 5, 0);
+ write_sram(card, card->tct_base + 6, 0);
+ write_sram(card, card->tct_base + 7, TCT_FLAG_UBR);
+
+ clear_bit(VCF_IDLE, &vc->flags);
+ writel(TCMDQ_START | 0, SAR_REG_TCMDQ);
+ return 0;
+}
+
+static int
+idt77252_dev_open(struct idt77252_dev *card)
+{
+ u32 conf;
+
+ if (!test_bit(IDT77252_BIT_INIT, &card->flags)) {
+ printk("%s: SAR not yet initialized.\n", card->name);
+ return -1;
+ }
+
+ conf = SAR_CFG_RXPTH| /* enable receive path */
+ SAR_RX_DELAY | /* interrupt on complete PDU */
+ SAR_CFG_RAWIE | /* interrupt enable on raw cells */
+ SAR_CFG_RQFIE | /* interrupt on RSQ almost full */
+ SAR_CFG_TMOIE | /* interrupt on timer overflow */
+ SAR_CFG_FBIE | /* interrupt on low free buffers */
+ SAR_CFG_TXEN | /* transmit operation enable */
+ SAR_CFG_TXINT | /* interrupt on transmit status */
+ SAR_CFG_TXUIE | /* interrupt on transmit underrun */
+ SAR_CFG_TXSFI | /* interrupt on TSQ almost full */
+ SAR_CFG_PHYIE /* enable PHY interrupts */
+ ;
+
+#ifdef CONFIG_ATM_IDT77252_RCV_ALL
+ /* Test RAW cell receive. */
+ conf |= SAR_CFG_VPECA;
+#endif
+
+ writel(readl(SAR_REG_CFG) | conf, SAR_REG_CFG);
+
+ if (open_card_oam(card)) {
+ printk("%s: Error initializing OAM.\n", card->name);
+ return -1;
+ }
+
+ if (open_card_ubr0(card)) {
+ printk("%s: Error initializing UBR0.\n", card->name);
+ return -1;
+ }
+
+ IPRINTK("%s: opened IDT77252 ABR SAR.\n", card->name);
+ return 0;
+}
+
+static void idt77252_dev_close(struct atm_dev *dev)
+{
+ struct idt77252_dev *card = dev->dev_data;
+ u32 conf;
+
+ close_card_oam(card);
+
+ conf = SAR_CFG_RXPTH | /* enable receive path */
+ SAR_RX_DELAY | /* interrupt on complete PDU */
+ SAR_CFG_RAWIE | /* interrupt enable on raw cells */
+ SAR_CFG_RQFIE | /* interrupt on RSQ almost full */
+ SAR_CFG_TMOIE | /* interrupt on timer overflow */
+ SAR_CFG_FBIE | /* interrupt on low free buffers */
+ SAR_CFG_TXEN | /* transmit operation enable */
+ SAR_CFG_TXINT | /* interrupt on transmit status */
+ SAR_CFG_TXUIE | /* interrupt on xmit underrun */
+ SAR_CFG_TXSFI /* interrupt on TSQ almost full */
+ ;
+
+ writel(readl(SAR_REG_CFG) & ~(conf), SAR_REG_CFG);
+
+ DIPRINTK("%s: closed IDT77252 ABR SAR.\n", card->name);
+}
+
+
+/*****************************************************************************/
+/* */
+/* Initialisation and Deinitialization of IDT77252 */
+/* */
+/*****************************************************************************/
+
+
+static void
+deinit_card(struct idt77252_dev *card)
+{
+ struct sk_buff *skb;
+ int i, j;
+
+ if (!test_bit(IDT77252_BIT_INIT, &card->flags)) {
+ printk("%s: SAR not yet initialized.\n", card->name);
+ return;
+ }
+ DIPRINTK("idt77252: deinitialize card %u\n", card->index);
+
+ writel(0, SAR_REG_CFG);
+
+ if (card->atmdev)
+ atm_dev_deregister(card->atmdev);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < FBQ_SIZE; j++) {
+ skb = card->sbpool[i].skb[j];
+ if (skb) {
+ dma_unmap_single(&card->pcidev->dev,
+ IDT77252_PRV_PADDR(skb),
+ (skb_end_pointer(skb) -
+ skb->data),
+ DMA_FROM_DEVICE);
+ card->sbpool[i].skb[j] = NULL;
+ dev_kfree_skb(skb);
+ }
+ }
+ }
+
+ vfree(card->soft_tst);
+
+ vfree(card->scd2vc);
+
+ vfree(card->vcs);
+
+ if (card->raw_cell_hnd) {
+ dma_free_coherent(&card->pcidev->dev, 2 * sizeof(u32),
+ card->raw_cell_hnd, card->raw_cell_paddr);
+ }
+
+ if (card->rsq.base) {
+ DIPRINTK("%s: Release RSQ ...\n", card->name);
+ deinit_rsq(card);
+ }
+
+ if (card->tsq.base) {
+ DIPRINTK("%s: Release TSQ ...\n", card->name);
+ deinit_tsq(card);
+ }
+
+ DIPRINTK("idt77252: Release IRQ.\n");
+ free_irq(card->pcidev->irq, card);
+
+ for (i = 0; i < 4; i++) {
+ if (card->fbq[i])
+ iounmap(card->fbq[i]);
+ }
+
+ if (card->membase)
+ iounmap(card->membase);
+
+ clear_bit(IDT77252_BIT_INIT, &card->flags);
+ DIPRINTK("%s: Card deinitialized.\n", card->name);
+}
+
+
+static void init_sram(struct idt77252_dev *card)
+{
+ int i;
+
+ for (i = 0; i < card->sramsize; i += 4)
+ write_sram(card, (i >> 2), 0);
+
+ /* set SRAM layout for THIS card */
+ if (card->sramsize == (512 * 1024)) {
+ card->tct_base = SAR_SRAM_TCT_128_BASE;
+ card->tct_size = (SAR_SRAM_TCT_128_TOP - card->tct_base + 1)
+ / SAR_SRAM_TCT_SIZE;
+ card->rct_base = SAR_SRAM_RCT_128_BASE;
+ card->rct_size = (SAR_SRAM_RCT_128_TOP - card->rct_base + 1)
+ / SAR_SRAM_RCT_SIZE;
+ card->rt_base = SAR_SRAM_RT_128_BASE;
+ card->scd_base = SAR_SRAM_SCD_128_BASE;
+ card->scd_size = (SAR_SRAM_SCD_128_TOP - card->scd_base + 1)
+ / SAR_SRAM_SCD_SIZE;
+ card->tst[0] = SAR_SRAM_TST1_128_BASE;
+ card->tst[1] = SAR_SRAM_TST2_128_BASE;
+ card->tst_size = SAR_SRAM_TST1_128_TOP - card->tst[0] + 1;
+ card->abrst_base = SAR_SRAM_ABRSTD_128_BASE;
+ card->abrst_size = SAR_ABRSTD_SIZE_8K;
+ card->fifo_base = SAR_SRAM_FIFO_128_BASE;
+ card->fifo_size = SAR_RXFD_SIZE_32K;
+ } else {
+ card->tct_base = SAR_SRAM_TCT_32_BASE;
+ card->tct_size = (SAR_SRAM_TCT_32_TOP - card->tct_base + 1)
+ / SAR_SRAM_TCT_SIZE;
+ card->rct_base = SAR_SRAM_RCT_32_BASE;
+ card->rct_size = (SAR_SRAM_RCT_32_TOP - card->rct_base + 1)
+ / SAR_SRAM_RCT_SIZE;
+ card->rt_base = SAR_SRAM_RT_32_BASE;
+ card->scd_base = SAR_SRAM_SCD_32_BASE;
+ card->scd_size = (SAR_SRAM_SCD_32_TOP - card->scd_base + 1)
+ / SAR_SRAM_SCD_SIZE;
+ card->tst[0] = SAR_SRAM_TST1_32_BASE;
+ card->tst[1] = SAR_SRAM_TST2_32_BASE;
+ card->tst_size = (SAR_SRAM_TST1_32_TOP - card->tst[0] + 1);
+ card->abrst_base = SAR_SRAM_ABRSTD_32_BASE;
+ card->abrst_size = SAR_ABRSTD_SIZE_1K;
+ card->fifo_base = SAR_SRAM_FIFO_32_BASE;
+ card->fifo_size = SAR_RXFD_SIZE_4K;
+ }
+
+ /* Initialize TCT */
+ for (i = 0; i < card->tct_size; i++) {
+ write_sram(card, i * SAR_SRAM_TCT_SIZE + 0, 0);
+ write_sram(card, i * SAR_SRAM_TCT_SIZE + 1, 0);
+ write_sram(card, i * SAR_SRAM_TCT_SIZE + 2, 0);
+ write_sram(card, i * SAR_SRAM_TCT_SIZE + 3, 0);
+ write_sram(card, i * SAR_SRAM_TCT_SIZE + 4, 0);
+ write_sram(card, i * SAR_SRAM_TCT_SIZE + 5, 0);
+ write_sram(card, i * SAR_SRAM_TCT_SIZE + 6, 0);
+ write_sram(card, i * SAR_SRAM_TCT_SIZE + 7, 0);
+ }
+
+ /* Initialize RCT */
+ for (i = 0; i < card->rct_size; i++) {
+ write_sram(card, card->rct_base + i * SAR_SRAM_RCT_SIZE,
+ (u32) SAR_RCTE_RAWCELLINTEN);
+ write_sram(card, card->rct_base + i * SAR_SRAM_RCT_SIZE + 1,
+ (u32) 0);
+ write_sram(card, card->rct_base + i * SAR_SRAM_RCT_SIZE + 2,
+ (u32) 0);
+ write_sram(card, card->rct_base + i * SAR_SRAM_RCT_SIZE + 3,
+ (u32) 0xffffffff);
+ }
+
+ writel((SAR_FBQ0_LOW << 28) | 0x00000000 | 0x00000000 |
+ (SAR_FB_SIZE_0 / 48), SAR_REG_FBQS0);
+ writel((SAR_FBQ1_LOW << 28) | 0x00000000 | 0x00000000 |
+ (SAR_FB_SIZE_1 / 48), SAR_REG_FBQS1);
+ writel((SAR_FBQ2_LOW << 28) | 0x00000000 | 0x00000000 |
+ (SAR_FB_SIZE_2 / 48), SAR_REG_FBQS2);
+ writel((SAR_FBQ3_LOW << 28) | 0x00000000 | 0x00000000 |
+ (SAR_FB_SIZE_3 / 48), SAR_REG_FBQS3);
+
+ /* Initialize rate table */
+ for (i = 0; i < 256; i++) {
+ write_sram(card, card->rt_base + i, log_to_rate[i]);
+ }
+
+ for (i = 0; i < 128; i++) {
+ unsigned int tmp;
+
+ tmp = rate_to_log[(i << 2) + 0] << 0;
+ tmp |= rate_to_log[(i << 2) + 1] << 8;
+ tmp |= rate_to_log[(i << 2) + 2] << 16;
+ tmp |= rate_to_log[(i << 2) + 3] << 24;
+ write_sram(card, card->rt_base + 256 + i, tmp);
+ }
+
+#if 0 /* Fill RDF and AIR tables. */
+ for (i = 0; i < 128; i++) {
+ unsigned int tmp;
+
+ tmp = RDF[0][(i << 1) + 0] << 16;
+ tmp |= RDF[0][(i << 1) + 1] << 0;
+ write_sram(card, card->rt_base + 512 + i, tmp);
+ }
+
+ for (i = 0; i < 128; i++) {
+ unsigned int tmp;
+
+ tmp = AIR[0][(i << 1) + 0] << 16;
+ tmp |= AIR[0][(i << 1) + 1] << 0;
+ write_sram(card, card->rt_base + 640 + i, tmp);
+ }
+#endif
+
+ IPRINTK("%s: initialize rate table ...\n", card->name);
+ writel(card->rt_base << 2, SAR_REG_RTBL);
+
+ /* Initialize TSTs */
+ IPRINTK("%s: initialize TST ...\n", card->name);
+ card->tst_free = card->tst_size - 2; /* last two are jumps */
+
+ for (i = card->tst[0]; i < card->tst[0] + card->tst_size - 2; i++)
+ write_sram(card, i, TSTE_OPC_VAR);
+ write_sram(card, i++, TSTE_OPC_JMP | (card->tst[0] << 2));
+ idt77252_sram_write_errors = 1;
+ write_sram(card, i++, TSTE_OPC_JMP | (card->tst[1] << 2));
+ idt77252_sram_write_errors = 0;
+ for (i = card->tst[1]; i < card->tst[1] + card->tst_size - 2; i++)
+ write_sram(card, i, TSTE_OPC_VAR);
+ write_sram(card, i++, TSTE_OPC_JMP | (card->tst[1] << 2));
+ idt77252_sram_write_errors = 1;
+ write_sram(card, i++, TSTE_OPC_JMP | (card->tst[0] << 2));
+ idt77252_sram_write_errors = 0;
+
+ card->tst_index = 0;
+ writel(card->tst[0] << 2, SAR_REG_TSTB);
+
+ /* Initialize ABRSTD and Receive FIFO */
+ IPRINTK("%s: initialize ABRSTD ...\n", card->name);
+ writel(card->abrst_size | (card->abrst_base << 2),
+ SAR_REG_ABRSTD);
+
+ IPRINTK("%s: initialize receive fifo ...\n", card->name);
+ writel(card->fifo_size | (card->fifo_base << 2),
+ SAR_REG_RXFD);
+
+ IPRINTK("%s: SRAM initialization complete.\n", card->name);
+}
+
+static int init_card(struct atm_dev *dev)
+{
+ struct idt77252_dev *card = dev->dev_data;
+ struct pci_dev *pcidev = card->pcidev;
+ unsigned long tmpl, modl;
+ unsigned int linkrate, rsvdcr;
+ unsigned int tst_entries;
+ struct net_device *tmp;
+ char tname[10];
+
+ u32 size;
+ u_char pci_byte;
+ u32 conf;
+ int i, k;
+
+ if (test_bit(IDT77252_BIT_INIT, &card->flags)) {
+ printk("Error: SAR already initialized.\n");
+ return -1;
+ }
+
+/*****************************************************************/
+/* P C I C O N F I G U R A T I O N */
+/*****************************************************************/
+
+ /* Set PCI Retry-Timeout and TRDY timeout */
+ IPRINTK("%s: Checking PCI retries.\n", card->name);
+ if (pci_read_config_byte(pcidev, 0x40, &pci_byte) != 0) {
+ printk("%s: can't read PCI retry timeout.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+ if (pci_byte != 0) {
+ IPRINTK("%s: PCI retry timeout: %d, set to 0.\n",
+ card->name, pci_byte);
+ if (pci_write_config_byte(pcidev, 0x40, 0) != 0) {
+ printk("%s: can't set PCI retry timeout.\n",
+ card->name);
+ deinit_card(card);
+ return -1;
+ }
+ }
+ IPRINTK("%s: Checking PCI TRDY.\n", card->name);
+ if (pci_read_config_byte(pcidev, 0x41, &pci_byte) != 0) {
+ printk("%s: can't read PCI TRDY timeout.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+ if (pci_byte != 0) {
+ IPRINTK("%s: PCI TRDY timeout: %d, set to 0.\n",
+ card->name, pci_byte);
+ if (pci_write_config_byte(pcidev, 0x41, 0) != 0) {
+ printk("%s: can't set PCI TRDY timeout.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+ }
+ /* Reset Timer register */
+ if (readl(SAR_REG_STAT) & SAR_STAT_TMROF) {
+ printk("%s: resetting timer overflow.\n", card->name);
+ writel(SAR_STAT_TMROF, SAR_REG_STAT);
+ }
+ IPRINTK("%s: Request IRQ ... ", card->name);
+ if (request_irq(pcidev->irq, idt77252_interrupt, IRQF_SHARED,
+ card->name, card) != 0) {
+ printk("%s: can't allocate IRQ.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+ IPRINTK("got %d.\n", pcidev->irq);
+
+/*****************************************************************/
+/* C H E C K A N D I N I T S R A M */
+/*****************************************************************/
+
+ IPRINTK("%s: Initializing SRAM\n", card->name);
+
+ /* preset size of connecton table, so that init_sram() knows about it */
+ conf = SAR_CFG_TX_FIFO_SIZE_9 | /* Use maximum fifo size */
+ SAR_CFG_RXSTQ_SIZE_8k | /* Receive Status Queue is 8k */
+ SAR_CFG_IDLE_CLP | /* Set CLP on idle cells */
+#ifndef ATM_IDT77252_SEND_IDLE
+ SAR_CFG_NO_IDLE | /* Do not send idle cells */
+#endif
+ 0;
+
+ if (card->sramsize == (512 * 1024))
+ conf |= SAR_CFG_CNTBL_1k;
+ else
+ conf |= SAR_CFG_CNTBL_512;
+
+ switch (vpibits) {
+ case 0:
+ conf |= SAR_CFG_VPVCS_0;
+ break;
+ default:
+ case 1:
+ conf |= SAR_CFG_VPVCS_1;
+ break;
+ case 2:
+ conf |= SAR_CFG_VPVCS_2;
+ break;
+ case 8:
+ conf |= SAR_CFG_VPVCS_8;
+ break;
+ }
+
+ writel(readl(SAR_REG_CFG) | conf, SAR_REG_CFG);
+
+ init_sram(card);
+
+/********************************************************************/
+/* A L L O C R A M A N D S E T V A R I O U S T H I N G S */
+/********************************************************************/
+ /* Initialize TSQ */
+ if (0 != init_tsq(card)) {
+ deinit_card(card);
+ return -1;
+ }
+ /* Initialize RSQ */
+ if (0 != init_rsq(card)) {
+ deinit_card(card);
+ return -1;
+ }
+
+ card->vpibits = vpibits;
+ if (card->sramsize == (512 * 1024)) {
+ card->vcibits = 10 - card->vpibits;
+ } else {
+ card->vcibits = 9 - card->vpibits;
+ }
+
+ card->vcimask = 0;
+ for (k = 0, i = 1; k < card->vcibits; k++) {
+ card->vcimask |= i;
+ i <<= 1;
+ }
+
+ IPRINTK("%s: Setting VPI/VCI mask to zero.\n", card->name);
+ writel(0, SAR_REG_VPM);
+
+ /* Little Endian Order */
+ writel(0, SAR_REG_GP);
+
+ /* Initialize RAW Cell Handle Register */
+ card->raw_cell_hnd = dma_zalloc_coherent(&card->pcidev->dev,
+ 2 * sizeof(u32),
+ &card->raw_cell_paddr,
+ GFP_KERNEL);
+ if (!card->raw_cell_hnd) {
+ printk("%s: memory allocation failure.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+ writel(card->raw_cell_paddr, SAR_REG_RAWHND);
+ IPRINTK("%s: raw cell handle is at 0x%p.\n", card->name,
+ card->raw_cell_hnd);
+
+ size = sizeof(struct vc_map *) * card->tct_size;
+ IPRINTK("%s: allocate %d byte for VC map.\n", card->name, size);
+ card->vcs = vzalloc(size);
+ if (!card->vcs) {
+ printk("%s: memory allocation failure.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+
+ size = sizeof(struct vc_map *) * card->scd_size;
+ IPRINTK("%s: allocate %d byte for SCD to VC mapping.\n",
+ card->name, size);
+ card->scd2vc = vzalloc(size);
+ if (!card->scd2vc) {
+ printk("%s: memory allocation failure.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+
+ size = sizeof(struct tst_info) * (card->tst_size - 2);
+ IPRINTK("%s: allocate %d byte for TST to VC mapping.\n",
+ card->name, size);
+ card->soft_tst = vmalloc(size);
+ if (!card->soft_tst) {
+ printk("%s: memory allocation failure.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+ for (i = 0; i < card->tst_size - 2; i++) {
+ card->soft_tst[i].tste = TSTE_OPC_VAR;
+ card->soft_tst[i].vc = NULL;
+ }
+
+ if (dev->phy == NULL) {
+ printk("%s: No LT device defined.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+ if (dev->phy->ioctl == NULL) {
+ printk("%s: LT had no IOCTL function defined.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+
+#ifdef CONFIG_ATM_IDT77252_USE_SUNI
+ /*
+ * this is a jhs hack to get around special functionality in the
+ * phy driver for the atecom hardware; the functionality doesn't
+ * exist in the linux atm suni driver
+ *
+ * it isn't the right way to do things, but as the guy from NIST
+ * said, talking about their measurement of the fine structure
+ * constant, "it's good enough for government work."
+ */
+ linkrate = 149760000;
+#endif
+
+ card->link_pcr = (linkrate / 8 / 53);
+ printk("%s: Linkrate on ATM line : %u bit/s, %u cell/s.\n",
+ card->name, linkrate, card->link_pcr);
+
+#ifdef ATM_IDT77252_SEND_IDLE
+ card->utopia_pcr = card->link_pcr;
+#else
+ card->utopia_pcr = (160000000 / 8 / 54);
+#endif
+
+ rsvdcr = 0;
+ if (card->utopia_pcr > card->link_pcr)
+ rsvdcr = card->utopia_pcr - card->link_pcr;
+
+ tmpl = (unsigned long) rsvdcr * ((unsigned long) card->tst_size - 2);
+ modl = tmpl % (unsigned long)card->utopia_pcr;
+ tst_entries = (int) (tmpl / (unsigned long)card->utopia_pcr);
+ if (modl)
+ tst_entries++;
+ card->tst_free -= tst_entries;
+ fill_tst(card, NULL, tst_entries, TSTE_OPC_NULL);
+
+#ifdef HAVE_EEPROM
+ idt77252_eeprom_init(card);
+ printk("%s: EEPROM: %02x:", card->name,
+ idt77252_eeprom_read_status(card));
+
+ for (i = 0; i < 0x80; i++) {
+ printk(" %02x",
+ idt77252_eeprom_read_byte(card, i)
+ );
+ }
+ printk("\n");
+#endif /* HAVE_EEPROM */
+
+ /*
+ * XXX: <hack>
+ */
+ sprintf(tname, "eth%d", card->index);
+ tmp = dev_get_by_name(&init_net, tname); /* jhs: was "tmp = dev_get(tname);" */
+ if (tmp) {
+ memcpy(card->atmdev->esi, tmp->dev_addr, 6);
+ dev_put(tmp);
+ printk("%s: ESI %pM\n", card->name, card->atmdev->esi);
+ }
+ /*
+ * XXX: </hack>
+ */
+
+ /* Set Maximum Deficit Count for now. */
+ writel(0xffff, SAR_REG_MDFCT);
+
+ set_bit(IDT77252_BIT_INIT, &card->flags);
+
+ XPRINTK("%s: IDT77252 ABR SAR initialization complete.\n", card->name);
+ return 0;
+}
+
+
+/*****************************************************************************/
+/* */
+/* Probing of IDT77252 ABR SAR */
+/* */
+/*****************************************************************************/
+
+
+static int idt77252_preset(struct idt77252_dev *card)
+{
+ u16 pci_command;
+
+/*****************************************************************/
+/* P C I C O N F I G U R A T I O N */
+/*****************************************************************/
+
+ XPRINTK("%s: Enable PCI master and memory access for SAR.\n",
+ card->name);
+ if (pci_read_config_word(card->pcidev, PCI_COMMAND, &pci_command)) {
+ printk("%s: can't read PCI_COMMAND.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+ if (!(pci_command & PCI_COMMAND_IO)) {
+ printk("%s: PCI_COMMAND: %04x (???)\n",
+ card->name, pci_command);
+ deinit_card(card);
+ return (-1);
+ }
+ pci_command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ if (pci_write_config_word(card->pcidev, PCI_COMMAND, pci_command)) {
+ printk("%s: can't write PCI_COMMAND.\n", card->name);
+ deinit_card(card);
+ return -1;
+ }
+/*****************************************************************/
+/* G E N E R I C R E S E T */
+/*****************************************************************/
+
+ /* Software reset */
+ writel(SAR_CFG_SWRST, SAR_REG_CFG);
+ mdelay(1);
+ writel(0, SAR_REG_CFG);
+
+ IPRINTK("%s: Software resetted.\n", card->name);
+ return 0;
+}
+
+
+static unsigned long probe_sram(struct idt77252_dev *card)
+{
+ u32 data, addr;
+
+ writel(0, SAR_REG_DR0);
+ writel(SAR_CMD_WRITE_SRAM | (0 << 2), SAR_REG_CMD);
+
+ for (addr = 0x4000; addr < 0x80000; addr += 0x4000) {
+ writel(ATM_POISON, SAR_REG_DR0);
+ writel(SAR_CMD_WRITE_SRAM | (addr << 2), SAR_REG_CMD);
+
+ writel(SAR_CMD_READ_SRAM | (0 << 2), SAR_REG_CMD);
+ data = readl(SAR_REG_DR0);
+
+ if (data != 0)
+ break;
+ }
+
+ return addr * sizeof(u32);
+}
+
+static int idt77252_init_one(struct pci_dev *pcidev,
+ const struct pci_device_id *id)
+{
+ static struct idt77252_dev **last = &idt77252_chain;
+ static int index = 0;
+
+ unsigned long membase, srambase;
+ struct idt77252_dev *card;
+ struct atm_dev *dev;
+ int i, err;
+
+
+ if ((err = pci_enable_device(pcidev))) {
+ printk("idt77252: can't enable PCI device at %s\n", pci_name(pcidev));
+ return err;
+ }
+
+ if ((err = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)))) {
+ printk("idt77252: can't enable DMA for PCI device at %s\n", pci_name(pcidev));
+ return err;
+ }
+
+ card = kzalloc(sizeof(struct idt77252_dev), GFP_KERNEL);
+ if (!card) {
+ printk("idt77252-%d: can't allocate private data\n", index);
+ err = -ENOMEM;
+ goto err_out_disable_pdev;
+ }
+ card->revision = pcidev->revision;
+ card->index = index;
+ card->pcidev = pcidev;
+ sprintf(card->name, "idt77252-%d", card->index);
+
+ INIT_WORK(&card->tqueue, idt77252_softint);
+
+ membase = pci_resource_start(pcidev, 1);
+ srambase = pci_resource_start(pcidev, 2);
+
+ mutex_init(&card->mutex);
+ spin_lock_init(&card->cmd_lock);
+ spin_lock_init(&card->tst_lock);
+
+ init_timer(&card->tst_timer);
+ card->tst_timer.data = (unsigned long)card;
+ card->tst_timer.function = tst_timer;
+
+ /* Do the I/O remapping... */
+ card->membase = ioremap(membase, 1024);
+ if (!card->membase) {
+ printk("%s: can't ioremap() membase\n", card->name);
+ err = -EIO;
+ goto err_out_free_card;
+ }
+
+ if (idt77252_preset(card)) {
+ printk("%s: preset failed\n", card->name);
+ err = -EIO;
+ goto err_out_iounmap;
+ }
+
+ dev = atm_dev_register("idt77252", &pcidev->dev, &idt77252_ops, -1,
+ NULL);
+ if (!dev) {
+ printk("%s: can't register atm device\n", card->name);
+ err = -EIO;
+ goto err_out_iounmap;
+ }
+ dev->dev_data = card;
+ card->atmdev = dev;
+
+#ifdef CONFIG_ATM_IDT77252_USE_SUNI
+ suni_init(dev);
+ if (!dev->phy) {
+ printk("%s: can't init SUNI\n", card->name);
+ err = -EIO;
+ goto err_out_deinit_card;
+ }
+#endif /* CONFIG_ATM_IDT77252_USE_SUNI */
+
+ card->sramsize = probe_sram(card);
+
+ for (i = 0; i < 4; i++) {
+ card->fbq[i] = ioremap(srambase | 0x200000 | (i << 18), 4);
+ if (!card->fbq[i]) {
+ printk("%s: can't ioremap() FBQ%d\n", card->name, i);
+ err = -EIO;
+ goto err_out_deinit_card;
+ }
+ }
+
+ printk("%s: ABR SAR (Rev %c): MEM %08lx SRAM %08lx [%u KB]\n",
+ card->name, ((card->revision > 1) && (card->revision < 25)) ?
+ 'A' + card->revision - 1 : '?', membase, srambase,
+ card->sramsize / 1024);
+
+ if (init_card(dev)) {
+ printk("%s: init_card failed\n", card->name);
+ err = -EIO;
+ goto err_out_deinit_card;
+ }
+
+ dev->ci_range.vpi_bits = card->vpibits;
+ dev->ci_range.vci_bits = card->vcibits;
+ dev->link_rate = card->link_pcr;
+
+ if (dev->phy->start)
+ dev->phy->start(dev);
+
+ if (idt77252_dev_open(card)) {
+ printk("%s: dev_open failed\n", card->name);
+ err = -EIO;
+ goto err_out_stop;
+ }
+
+ *last = card;
+ last = &card->next;
+ index++;
+
+ return 0;
+
+err_out_stop:
+ if (dev->phy->stop)
+ dev->phy->stop(dev);
+
+err_out_deinit_card:
+ deinit_card(card);
+
+err_out_iounmap:
+ iounmap(card->membase);
+
+err_out_free_card:
+ kfree(card);
+
+err_out_disable_pdev:
+ pci_disable_device(pcidev);
+ return err;
+}
+
+static struct pci_device_id idt77252_pci_tbl[] =
+{
+ { PCI_VDEVICE(IDT, PCI_DEVICE_ID_IDT_IDT77252), 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, idt77252_pci_tbl);
+
+static struct pci_driver idt77252_driver = {
+ .name = "idt77252",
+ .id_table = idt77252_pci_tbl,
+ .probe = idt77252_init_one,
+};
+
+static int __init idt77252_init(void)
+{
+ struct sk_buff *skb;
+
+ printk("%s: at %p\n", __func__, idt77252_init);
+
+ if (sizeof(skb->cb) < sizeof(struct atm_skb_data) +
+ sizeof(struct idt77252_skb_prv)) {
+ printk(KERN_ERR "%s: skb->cb is too small (%lu < %lu)\n",
+ __func__, (unsigned long) sizeof(skb->cb),
+ (unsigned long) sizeof(struct atm_skb_data) +
+ sizeof(struct idt77252_skb_prv));
+ return -EIO;
+ }
+
+ return pci_register_driver(&idt77252_driver);
+}
+
+static void __exit idt77252_exit(void)
+{
+ struct idt77252_dev *card;
+ struct atm_dev *dev;
+
+ pci_unregister_driver(&idt77252_driver);
+
+ while (idt77252_chain) {
+ card = idt77252_chain;
+ dev = card->atmdev;
+ idt77252_chain = card->next;
+
+ if (dev->phy->stop)
+ dev->phy->stop(dev);
+ deinit_card(card);
+ pci_disable_device(card->pcidev);
+ kfree(card);
+ }
+
+ DIPRINTK("idt77252: finished cleanup-module().\n");
+}
+
+module_init(idt77252_init);
+module_exit(idt77252_exit);
+
+MODULE_LICENSE("GPL");
+
+module_param(vpibits, uint, 0);
+MODULE_PARM_DESC(vpibits, "number of VPI bits supported (0, 1, or 2)");
+#ifdef CONFIG_ATM_IDT77252_DEBUG
+module_param(debug, ulong, 0644);
+MODULE_PARM_DESC(debug, "debug bitmap, see drivers/atm/idt77252.h");
+#endif
+
+MODULE_AUTHOR("Eddie C. Dost <ecd@atecom.com>");
+MODULE_DESCRIPTION("IDT77252 ABR SAR Driver");
diff --git a/drivers/atm/idt77252.h b/drivers/atm/idt77252.h
new file mode 100644
index 000000000..3a82cc23a
--- /dev/null
+++ b/drivers/atm/idt77252.h
@@ -0,0 +1,813 @@
+/*******************************************************************
+ *
+ * Copyright (c) 2000 ATecoM GmbH
+ *
+ * The author may be reached at ecd@atecom.com.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ *******************************************************************/
+
+#ifndef _IDT77252_H
+#define _IDT77252_H 1
+
+
+#include <linux/ptrace.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+/*****************************************************************************/
+/* */
+/* Makros */
+/* */
+/*****************************************************************************/
+#define VPCI2VC(card, vpi, vci) \
+ (((vpi) << card->vcibits) | ((vci) & card->vcimask))
+
+/*****************************************************************************/
+/* */
+/* DEBUGGING definitions */
+/* */
+/*****************************************************************************/
+
+#define DBG_RAW_CELL 0x00000400
+#define DBG_TINY 0x00000200
+#define DBG_GENERAL 0x00000100
+#define DBG_XGENERAL 0x00000080
+#define DBG_INIT 0x00000040
+#define DBG_DEINIT 0x00000020
+#define DBG_INTERRUPT 0x00000010
+#define DBG_OPEN_CONN 0x00000008
+#define DBG_CLOSE_CONN 0x00000004
+#define DBG_RX_DATA 0x00000002
+#define DBG_TX_DATA 0x00000001
+
+#ifdef CONFIG_ATM_IDT77252_DEBUG
+
+#define CPRINTK(args...) do { if (debug & DBG_CLOSE_CONN) printk(args); } while(0)
+#define OPRINTK(args...) do { if (debug & DBG_OPEN_CONN) printk(args); } while(0)
+#define IPRINTK(args...) do { if (debug & DBG_INIT) printk(args); } while(0)
+#define INTPRINTK(args...) do { if (debug & DBG_INTERRUPT) printk(args); } while(0)
+#define DIPRINTK(args...) do { if (debug & DBG_DEINIT) printk(args); } while(0)
+#define TXPRINTK(args...) do { if (debug & DBG_TX_DATA) printk(args); } while(0)
+#define RXPRINTK(args...) do { if (debug & DBG_RX_DATA) printk(args); } while(0)
+#define XPRINTK(args...) do { if (debug & DBG_XGENERAL) printk(args); } while(0)
+#define DPRINTK(args...) do { if (debug & DBG_GENERAL) printk(args); } while(0)
+#define NPRINTK(args...) do { if (debug & DBG_TINY) printk(args); } while(0)
+#define RPRINTK(args...) do { if (debug & DBG_RAW_CELL) printk(args); } while(0)
+
+#else
+
+#define CPRINTK(args...) do { } while(0)
+#define OPRINTK(args...) do { } while(0)
+#define IPRINTK(args...) do { } while(0)
+#define INTPRINTK(args...) do { } while(0)
+#define DIPRINTK(args...) do { } while(0)
+#define TXPRINTK(args...) do { } while(0)
+#define RXPRINTK(args...) do { } while(0)
+#define XPRINTK(args...) do { } while(0)
+#define DPRINTK(args...) do { } while(0)
+#define NPRINTK(args...) do { } while(0)
+#define RPRINTK(args...) do { } while(0)
+
+#endif
+
+#define SCHED_UBR0 0
+#define SCHED_UBR 1
+#define SCHED_VBR 2
+#define SCHED_ABR 3
+#define SCHED_CBR 4
+
+#define SCQFULL_TIMEOUT HZ
+
+/*****************************************************************************/
+/* */
+/* Free Buffer Queue Layout */
+/* */
+/*****************************************************************************/
+#define SAR_FB_SIZE_0 (2048 - 256)
+#define SAR_FB_SIZE_1 (4096 - 256)
+#define SAR_FB_SIZE_2 (8192 - 256)
+#define SAR_FB_SIZE_3 (16384 - 256)
+
+#define SAR_FBQ0_LOW 4
+#define SAR_FBQ0_HIGH 8
+#define SAR_FBQ1_LOW 2
+#define SAR_FBQ1_HIGH 4
+#define SAR_FBQ2_LOW 1
+#define SAR_FBQ2_HIGH 2
+#define SAR_FBQ3_LOW 1
+#define SAR_FBQ3_HIGH 2
+
+#if 0
+#define SAR_TST_RESERVED 44 /* Num TST reserved for UBR/ABR/VBR */
+#else
+#define SAR_TST_RESERVED 0 /* Num TST reserved for UBR/ABR/VBR */
+#endif
+
+#define TCT_CBR 0x00000000
+#define TCT_UBR 0x00000000
+#define TCT_VBR 0x40000000
+#define TCT_ABR 0x80000000
+#define TCT_TYPE 0xc0000000
+
+#define TCT_RR 0x20000000
+#define TCT_LMCR 0x08000000
+#define TCT_SCD_MASK 0x0007ffff
+
+#define TCT_TSIF 0x00004000
+#define TCT_HALT 0x80000000
+#define TCT_IDLE 0x40000000
+#define TCT_FLAG_UBR 0x80000000
+
+/*****************************************************************************/
+/* */
+/* Structure describing an IDT77252 */
+/* */
+/*****************************************************************************/
+
+struct scqe
+{
+ u32 word_1;
+ u32 word_2;
+ u32 word_3;
+ u32 word_4;
+};
+
+#define SCQ_ENTRIES 64
+#define SCQ_SIZE (SCQ_ENTRIES * sizeof(struct scqe))
+#define SCQ_MASK (SCQ_SIZE - 1)
+
+struct scq_info
+{
+ struct scqe *base;
+ struct scqe *next;
+ struct scqe *last;
+ dma_addr_t paddr;
+ spinlock_t lock;
+ atomic_t used;
+ unsigned long trans_start;
+ unsigned long scd;
+ spinlock_t skblock;
+ struct sk_buff_head transmit;
+ struct sk_buff_head pending;
+};
+
+struct rx_pool {
+ struct sk_buff_head queue;
+ unsigned int len;
+};
+
+struct aal1 {
+ unsigned int total;
+ unsigned int count;
+ struct sk_buff *data;
+ unsigned char sequence;
+};
+
+struct rate_estimator {
+ struct timer_list timer;
+ unsigned int interval;
+ unsigned int ewma_log;
+ u64 cells;
+ u64 last_cells;
+ long avcps;
+ u32 cps;
+ u32 maxcps;
+};
+
+struct vc_map {
+ unsigned int index;
+ unsigned long flags;
+#define VCF_TX 0
+#define VCF_RX 1
+#define VCF_IDLE 2
+#define VCF_RSV 3
+ unsigned int class;
+ u8 init_er;
+ u8 lacr;
+ u8 max_er;
+ unsigned int ntste;
+ spinlock_t lock;
+ struct atm_vcc *tx_vcc;
+ struct atm_vcc *rx_vcc;
+ struct idt77252_dev *card;
+ struct scq_info *scq; /* To keep track of the SCQ */
+ struct rate_estimator *estimator;
+ int scd_index;
+ union {
+ struct rx_pool rx_pool;
+ struct aal1 aal1;
+ } rcv;
+};
+
+/*****************************************************************************/
+/* */
+/* RCTE - Receive Connection Table Entry */
+/* */
+/*****************************************************************************/
+
+struct rct_entry
+{
+ u32 word_1;
+ u32 buffer_handle;
+ u32 dma_address;
+ u32 aal5_crc32;
+};
+
+/*****************************************************************************/
+/* */
+/* RSQ - Receive Status Queue */
+/* */
+/*****************************************************************************/
+
+#define SAR_RSQE_VALID 0x80000000
+#define SAR_RSQE_IDLE 0x40000000
+#define SAR_RSQE_BUF_MASK 0x00030000
+#define SAR_RSQE_BUF_ASGN 0x00008000
+#define SAR_RSQE_NZGFC 0x00004000
+#define SAR_RSQE_EPDU 0x00002000
+#define SAR_RSQE_BUF_CONT 0x00001000
+#define SAR_RSQE_EFCIE 0x00000800
+#define SAR_RSQE_CLP 0x00000400
+#define SAR_RSQE_CRC 0x00000200
+#define SAR_RSQE_CELLCNT 0x000001FF
+
+
+#define RSQSIZE 8192
+#define RSQ_NUM_ENTRIES (RSQSIZE / 16)
+#define RSQ_ALIGNMENT 8192
+
+struct rsq_entry {
+ u32 word_1;
+ u32 word_2;
+ u32 word_3;
+ u32 word_4;
+};
+
+struct rsq_info {
+ struct rsq_entry *base;
+ struct rsq_entry *next;
+ struct rsq_entry *last;
+ dma_addr_t paddr;
+};
+
+
+/*****************************************************************************/
+/* */
+/* TSQ - Transmit Status Queue */
+/* */
+/*****************************************************************************/
+
+#define SAR_TSQE_INVALID 0x80000000
+#define SAR_TSQE_TIMESTAMP 0x00FFFFFF
+#define SAR_TSQE_TYPE 0x60000000
+#define SAR_TSQE_TYPE_TIMER 0x00000000
+#define SAR_TSQE_TYPE_TSR 0x20000000
+#define SAR_TSQE_TYPE_IDLE 0x40000000
+#define SAR_TSQE_TYPE_TBD_COMP 0x60000000
+
+#define SAR_TSQE_TAG(stat) (((stat) >> 24) & 0x1f)
+
+#define TSQSIZE 8192
+#define TSQ_NUM_ENTRIES 1024
+#define TSQ_ALIGNMENT 8192
+
+struct tsq_entry
+{
+ u32 word_1;
+ u32 word_2;
+};
+
+struct tsq_info
+{
+ struct tsq_entry *base;
+ struct tsq_entry *next;
+ struct tsq_entry *last;
+ dma_addr_t paddr;
+};
+
+struct tst_info
+{
+ struct vc_map *vc;
+ u32 tste;
+};
+
+#define TSTE_MASK 0x601fffff
+
+#define TSTE_OPC_MASK 0x60000000
+#define TSTE_OPC_NULL 0x00000000
+#define TSTE_OPC_CBR 0x20000000
+#define TSTE_OPC_VAR 0x40000000
+#define TSTE_OPC_JMP 0x60000000
+
+#define TSTE_PUSH_IDLE 0x01000000
+#define TSTE_PUSH_ACTIVE 0x02000000
+
+#define TST_SWITCH_DONE 0
+#define TST_SWITCH_PENDING 1
+#define TST_SWITCH_WAIT 2
+
+#define FBQ_SHIFT 9
+#define FBQ_SIZE (1 << FBQ_SHIFT)
+#define FBQ_MASK (FBQ_SIZE - 1)
+
+struct sb_pool
+{
+ unsigned int index;
+ struct sk_buff *skb[FBQ_SIZE];
+};
+
+#define POOL_HANDLE(queue, index) (((queue + 1) << 16) | (index))
+#define POOL_QUEUE(handle) (((handle) >> 16) - 1)
+#define POOL_INDEX(handle) ((handle) & 0xffff)
+
+struct idt77252_dev
+{
+ struct tsq_info tsq; /* Transmit Status Queue */
+ struct rsq_info rsq; /* Receive Status Queue */
+
+ struct pci_dev *pcidev; /* PCI handle (desriptor) */
+ struct atm_dev *atmdev; /* ATM device desriptor */
+
+ void __iomem *membase; /* SAR's memory base address */
+ unsigned long srambase; /* SAR's sram base address */
+ void __iomem *fbq[4]; /* FBQ fill addresses */
+
+ struct mutex mutex;
+ spinlock_t cmd_lock; /* for r/w utility/sram */
+
+ unsigned long softstat;
+ unsigned long flags; /* see blow */
+
+ struct work_struct tqueue;
+
+ unsigned long tct_base; /* TCT base address in SRAM */
+ unsigned long rct_base; /* RCT base address in SRAM */
+ unsigned long rt_base; /* Rate Table base in SRAM */
+ unsigned long scd_base; /* SCD base address in SRAM */
+ unsigned long tst[2]; /* TST base address in SRAM */
+ unsigned long abrst_base; /* ABRST base address in SRAM */
+ unsigned long fifo_base; /* RX FIFO base in SRAM */
+
+ unsigned long irqstat[16];
+
+ unsigned int sramsize; /* SAR's sram size */
+
+ unsigned int tct_size; /* total TCT entries */
+ unsigned int rct_size; /* total RCT entries */
+ unsigned int scd_size; /* length of SCD */
+ unsigned int tst_size; /* total TST entries */
+ unsigned int tst_free; /* free TSTEs in TST */
+ unsigned int abrst_size; /* size of ABRST in words */
+ unsigned int fifo_size; /* size of RX FIFO in words */
+
+ unsigned int vpibits; /* Bits used for VPI index */
+ unsigned int vcibits; /* Bits used for VCI index */
+ unsigned int vcimask; /* Mask for VCI index */
+
+ unsigned int utopia_pcr; /* Utopia Itf's Cell Rate */
+ unsigned int link_pcr; /* PHY's Peek Cell Rate */
+
+ struct vc_map **vcs; /* Open Connections */
+ struct vc_map **scd2vc; /* SCD to Connection map */
+
+ struct tst_info *soft_tst; /* TST to Connection map */
+ unsigned int tst_index; /* Current TST in use */
+ struct timer_list tst_timer;
+ spinlock_t tst_lock;
+ unsigned long tst_state;
+
+ struct sb_pool sbpool[4]; /* Pool of RX skbuffs */
+ struct sk_buff *raw_cell_head; /* Pointer to raw cell queue */
+ u32 *raw_cell_hnd; /* Pointer to RCQ handle */
+ dma_addr_t raw_cell_paddr;
+
+ int index; /* SAR's ID */
+ int revision; /* chip revision */
+
+ char name[16]; /* Device name */
+
+ struct idt77252_dev *next;
+};
+
+
+/* definition for flag field above */
+#define IDT77252_BIT_INIT 1
+#define IDT77252_BIT_INTERRUPT 2
+
+
+#define ATM_CELL_PAYLOAD 48
+
+#define FREEBUF_ALIGNMENT 16
+
+/*****************************************************************************/
+/* */
+/* Makros */
+/* */
+/*****************************************************************************/
+#define ALIGN_ADDRESS(addr, alignment) \
+ ((((u32)(addr)) + (((u32)(alignment))-1)) & ~(((u32)(alignment)) - 1))
+
+
+/*****************************************************************************/
+/* */
+/* ABR SAR Network operation Register */
+/* */
+/*****************************************************************************/
+
+#define SAR_REG_DR0 (card->membase + 0x00)
+#define SAR_REG_DR1 (card->membase + 0x04)
+#define SAR_REG_DR2 (card->membase + 0x08)
+#define SAR_REG_DR3 (card->membase + 0x0C)
+#define SAR_REG_CMD (card->membase + 0x10)
+#define SAR_REG_CFG (card->membase + 0x14)
+#define SAR_REG_STAT (card->membase + 0x18)
+#define SAR_REG_RSQB (card->membase + 0x1C)
+#define SAR_REG_RSQT (card->membase + 0x20)
+#define SAR_REG_RSQH (card->membase + 0x24)
+#define SAR_REG_CDC (card->membase + 0x28)
+#define SAR_REG_VPEC (card->membase + 0x2C)
+#define SAR_REG_ICC (card->membase + 0x30)
+#define SAR_REG_RAWCT (card->membase + 0x34)
+#define SAR_REG_TMR (card->membase + 0x38)
+#define SAR_REG_TSTB (card->membase + 0x3C)
+#define SAR_REG_TSQB (card->membase + 0x40)
+#define SAR_REG_TSQT (card->membase + 0x44)
+#define SAR_REG_TSQH (card->membase + 0x48)
+#define SAR_REG_GP (card->membase + 0x4C)
+#define SAR_REG_VPM (card->membase + 0x50)
+#define SAR_REG_RXFD (card->membase + 0x54)
+#define SAR_REG_RXFT (card->membase + 0x58)
+#define SAR_REG_RXFH (card->membase + 0x5C)
+#define SAR_REG_RAWHND (card->membase + 0x60)
+#define SAR_REG_RXSTAT (card->membase + 0x64)
+#define SAR_REG_ABRSTD (card->membase + 0x68)
+#define SAR_REG_ABRRQ (card->membase + 0x6C)
+#define SAR_REG_VBRRQ (card->membase + 0x70)
+#define SAR_REG_RTBL (card->membase + 0x74)
+#define SAR_REG_MDFCT (card->membase + 0x78)
+#define SAR_REG_TXSTAT (card->membase + 0x7C)
+#define SAR_REG_TCMDQ (card->membase + 0x80)
+#define SAR_REG_IRCP (card->membase + 0x84)
+#define SAR_REG_FBQP0 (card->membase + 0x88)
+#define SAR_REG_FBQP1 (card->membase + 0x8C)
+#define SAR_REG_FBQP2 (card->membase + 0x90)
+#define SAR_REG_FBQP3 (card->membase + 0x94)
+#define SAR_REG_FBQS0 (card->membase + 0x98)
+#define SAR_REG_FBQS1 (card->membase + 0x9C)
+#define SAR_REG_FBQS2 (card->membase + 0xA0)
+#define SAR_REG_FBQS3 (card->membase + 0xA4)
+#define SAR_REG_FBQWP0 (card->membase + 0xA8)
+#define SAR_REG_FBQWP1 (card->membase + 0xAC)
+#define SAR_REG_FBQWP2 (card->membase + 0xB0)
+#define SAR_REG_FBQWP3 (card->membase + 0xB4)
+#define SAR_REG_NOW (card->membase + 0xB8)
+
+
+/*****************************************************************************/
+/* */
+/* Commands */
+/* */
+/*****************************************************************************/
+
+#define SAR_CMD_NO_OPERATION 0x00000000
+#define SAR_CMD_OPENCLOSE_CONNECTION 0x20000000
+#define SAR_CMD_WRITE_SRAM 0x40000000
+#define SAR_CMD_READ_SRAM 0x50000000
+#define SAR_CMD_READ_UTILITY 0x80000000
+#define SAR_CMD_WRITE_UTILITY 0x90000000
+
+#define SAR_CMD_OPEN_CONNECTION (SAR_CMD_OPENCLOSE_CONNECTION | 0x00080000)
+#define SAR_CMD_CLOSE_CONNECTION SAR_CMD_OPENCLOSE_CONNECTION
+
+
+/*****************************************************************************/
+/* */
+/* Configuration Register bits */
+/* */
+/*****************************************************************************/
+
+#define SAR_CFG_SWRST 0x80000000 /* Software reset */
+#define SAR_CFG_LOOP 0x40000000 /* Internal Loopback */
+#define SAR_CFG_RXPTH 0x20000000 /* Receive Path Enable */
+#define SAR_CFG_IDLE_CLP 0x10000000 /* SAR set CLP Bits of Null Cells */
+#define SAR_CFG_TX_FIFO_SIZE_1 0x04000000 /* TX FIFO Size = 1 cell */
+#define SAR_CFG_TX_FIFO_SIZE_2 0x08000000 /* TX FIFO Size = 2 cells */
+#define SAR_CFG_TX_FIFO_SIZE_4 0x0C000000 /* TX FIFO Size = 4 cells */
+#define SAR_CFG_TX_FIFO_SIZE_9 0x00000000 /* TX FIFO Size = 9 cells (full) */
+#define SAR_CFG_NO_IDLE 0x02000000 /* SAR sends no Null Cells */
+#define SAR_CFG_RSVD1 0x01000000 /* Reserved */
+#define SAR_CFG_RXSTQ_SIZE_2k 0x00000000 /* RX Stat Queue Size = 2048 byte */
+#define SAR_CFG_RXSTQ_SIZE_4k 0x00400000 /* RX Stat Queue Size = 4096 byte */
+#define SAR_CFG_RXSTQ_SIZE_8k 0x00800000 /* RX Stat Queue Size = 8192 byte */
+#define SAR_CFG_RXSTQ_SIZE_R 0x00C00000 /* RX Stat Queue Size = reserved */
+#define SAR_CFG_ICAPT 0x00200000 /* accept Invalid Cells */
+#define SAR_CFG_IGGFC 0x00100000 /* Ignore GFC */
+#define SAR_CFG_VPVCS_0 0x00000000 /* VPI/VCI Select bit range */
+#define SAR_CFG_VPVCS_1 0x00040000 /* VPI/VCI Select bit range */
+#define SAR_CFG_VPVCS_2 0x00080000 /* VPI/VCI Select bit range */
+#define SAR_CFG_VPVCS_8 0x000C0000 /* VPI/VCI Select bit range */
+#define SAR_CFG_CNTBL_1k 0x00000000 /* Connection Table Size */
+#define SAR_CFG_CNTBL_4k 0x00010000 /* Connection Table Size */
+#define SAR_CFG_CNTBL_16k 0x00020000 /* Connection Table Size */
+#define SAR_CFG_CNTBL_512 0x00030000 /* Connection Table Size */
+#define SAR_CFG_VPECA 0x00008000 /* VPI/VCI Error Cell Accept */
+#define SAR_CFG_RXINT_NOINT 0x00000000 /* No Interrupt on PDU received */
+#define SAR_CFG_RXINT_NODELAY 0x00001000 /* Interrupt without delay to host*/
+#define SAR_CFG_RXINT_256US 0x00002000 /* Interrupt with delay 256 usec */
+#define SAR_CFG_RXINT_505US 0x00003000 /* Interrupt with delay 505 usec */
+#define SAR_CFG_RXINT_742US 0x00004000 /* Interrupt with delay 742 usec */
+#define SAR_CFG_RAWIE 0x00000800 /* Raw Cell Queue Interrupt Enable*/
+#define SAR_CFG_RQFIE 0x00000400 /* RSQ Almost Full Int Enable */
+#define SAR_CFG_RSVD2 0x00000200 /* Reserved */
+#define SAR_CFG_CACHE 0x00000100 /* DMA on Cache Line Boundary */
+#define SAR_CFG_TMOIE 0x00000080 /* Timer Roll Over Int Enable */
+#define SAR_CFG_FBIE 0x00000040 /* Free Buffer Queue Int Enable */
+#define SAR_CFG_TXEN 0x00000020 /* Transmit Operation Enable */
+#define SAR_CFG_TXINT 0x00000010 /* Transmit status Int Enable */
+#define SAR_CFG_TXUIE 0x00000008 /* Transmit underrun Int Enable */
+#define SAR_CFG_UMODE 0x00000004 /* Utopia Mode Select */
+#define SAR_CFG_TXSFI 0x00000002 /* Transmit status Full Int Enable*/
+#define SAR_CFG_PHYIE 0x00000001 /* PHY Interrupt Enable */
+
+#define SAR_CFG_TX_FIFO_SIZE_MASK 0x0C000000 /* TX FIFO Size Mask */
+#define SAR_CFG_RXSTQSIZE_MASK 0x00C00000
+#define SAR_CFG_CNTBL_MASK 0x00030000
+#define SAR_CFG_RXINT_MASK 0x00007000
+
+
+/*****************************************************************************/
+/* */
+/* Status Register bits */
+/* */
+/*****************************************************************************/
+
+#define SAR_STAT_FRAC_3 0xF0000000 /* Fraction of Free Buffer Queue 3 */
+#define SAR_STAT_FRAC_2 0x0F000000 /* Fraction of Free Buffer Queue 2 */
+#define SAR_STAT_FRAC_1 0x00F00000 /* Fraction of Free Buffer Queue 1 */
+#define SAR_STAT_FRAC_0 0x000F0000 /* Fraction of Free Buffer Queue 0 */
+#define SAR_STAT_TSIF 0x00008000 /* Transmit Status Indicator */
+#define SAR_STAT_TXICP 0x00004000 /* Transmit Status Indicator */
+#define SAR_STAT_RSVD1 0x00002000 /* Reserved */
+#define SAR_STAT_TSQF 0x00001000 /* Transmit Status Queue full */
+#define SAR_STAT_TMROF 0x00000800 /* Timer overflow */
+#define SAR_STAT_PHYI 0x00000400 /* PHY device Interrupt flag */
+#define SAR_STAT_CMDBZ 0x00000200 /* ABR SAR Command Busy Flag */
+#define SAR_STAT_FBQ3A 0x00000100 /* Free Buffer Queue 3 Attention */
+#define SAR_STAT_FBQ2A 0x00000080 /* Free Buffer Queue 2 Attention */
+#define SAR_STAT_RSQF 0x00000040 /* Receive Status Queue full */
+#define SAR_STAT_EPDU 0x00000020 /* End Of PDU Flag */
+#define SAR_STAT_RAWCF 0x00000010 /* Raw Cell Flag */
+#define SAR_STAT_FBQ1A 0x00000008 /* Free Buffer Queue 1 Attention */
+#define SAR_STAT_FBQ0A 0x00000004 /* Free Buffer Queue 0 Attention */
+#define SAR_STAT_RSQAF 0x00000002 /* Receive Status Queue almost full*/
+#define SAR_STAT_RSVD2 0x00000001 /* Reserved */
+
+
+/*****************************************************************************/
+/* */
+/* General Purpose Register bits */
+/* */
+/*****************************************************************************/
+
+#define SAR_GP_TXNCC_MASK 0xff000000 /* Transmit Negative Credit Count */
+#define SAR_GP_EEDI 0x00010000 /* EEPROM Data In */
+#define SAR_GP_BIGE 0x00008000 /* Big Endian Operation */
+#define SAR_GP_RM_NORMAL 0x00000000 /* Normal handling of RM cells */
+#define SAR_GP_RM_TO_RCQ 0x00002000 /* put RM cells into Raw Cell Queue */
+#define SAR_GP_RM_RSVD 0x00004000 /* Reserved */
+#define SAR_GP_RM_INHIBIT 0x00006000 /* Inhibit update of Connection tab */
+#define SAR_GP_PHY_RESET 0x00000008 /* PHY Reset */
+#define SAR_GP_EESCLK 0x00000004 /* EEPROM SCLK */
+#define SAR_GP_EECS 0x00000002 /* EEPROM Chip Select */
+#define SAR_GP_EEDO 0x00000001 /* EEPROM Data Out */
+
+
+/*****************************************************************************/
+/* */
+/* SAR local SRAM layout for 128k work SRAM */
+/* */
+/*****************************************************************************/
+
+#define SAR_SRAM_SCD_SIZE 12
+#define SAR_SRAM_TCT_SIZE 8
+#define SAR_SRAM_RCT_SIZE 4
+
+#define SAR_SRAM_TCT_128_BASE 0x00000
+#define SAR_SRAM_TCT_128_TOP 0x01fff
+#define SAR_SRAM_RCT_128_BASE 0x02000
+#define SAR_SRAM_RCT_128_TOP 0x02fff
+#define SAR_SRAM_FB0_128_BASE 0x03000
+#define SAR_SRAM_FB0_128_TOP 0x033ff
+#define SAR_SRAM_FB1_128_BASE 0x03400
+#define SAR_SRAM_FB1_128_TOP 0x037ff
+#define SAR_SRAM_FB2_128_BASE 0x03800
+#define SAR_SRAM_FB2_128_TOP 0x03bff
+#define SAR_SRAM_FB3_128_BASE 0x03c00
+#define SAR_SRAM_FB3_128_TOP 0x03fff
+#define SAR_SRAM_SCD_128_BASE 0x04000
+#define SAR_SRAM_SCD_128_TOP 0x07fff
+#define SAR_SRAM_TST1_128_BASE 0x08000
+#define SAR_SRAM_TST1_128_TOP 0x0bfff
+#define SAR_SRAM_TST2_128_BASE 0x0c000
+#define SAR_SRAM_TST2_128_TOP 0x0ffff
+#define SAR_SRAM_ABRSTD_128_BASE 0x10000
+#define SAR_SRAM_ABRSTD_128_TOP 0x13fff
+#define SAR_SRAM_RT_128_BASE 0x14000
+#define SAR_SRAM_RT_128_TOP 0x15fff
+
+#define SAR_SRAM_FIFO_128_BASE 0x18000
+#define SAR_SRAM_FIFO_128_TOP 0x1ffff
+
+
+/*****************************************************************************/
+/* */
+/* SAR local SRAM layout for 32k work SRAM */
+/* */
+/*****************************************************************************/
+
+#define SAR_SRAM_TCT_32_BASE 0x00000
+#define SAR_SRAM_TCT_32_TOP 0x00fff
+#define SAR_SRAM_RCT_32_BASE 0x01000
+#define SAR_SRAM_RCT_32_TOP 0x017ff
+#define SAR_SRAM_FB0_32_BASE 0x01800
+#define SAR_SRAM_FB0_32_TOP 0x01bff
+#define SAR_SRAM_FB1_32_BASE 0x01c00
+#define SAR_SRAM_FB1_32_TOP 0x01fff
+#define SAR_SRAM_FB2_32_BASE 0x02000
+#define SAR_SRAM_FB2_32_TOP 0x023ff
+#define SAR_SRAM_FB3_32_BASE 0x02400
+#define SAR_SRAM_FB3_32_TOP 0x027ff
+#define SAR_SRAM_SCD_32_BASE 0x02800
+#define SAR_SRAM_SCD_32_TOP 0x03fff
+#define SAR_SRAM_TST1_32_BASE 0x04000
+#define SAR_SRAM_TST1_32_TOP 0x04fff
+#define SAR_SRAM_TST2_32_BASE 0x05000
+#define SAR_SRAM_TST2_32_TOP 0x05fff
+#define SAR_SRAM_ABRSTD_32_BASE 0x06000
+#define SAR_SRAM_ABRSTD_32_TOP 0x067ff
+#define SAR_SRAM_RT_32_BASE 0x06800
+#define SAR_SRAM_RT_32_TOP 0x06fff
+#define SAR_SRAM_FIFO_32_BASE 0x07000
+#define SAR_SRAM_FIFO_32_TOP 0x07fff
+
+
+/*****************************************************************************/
+/* */
+/* TSR - Transmit Status Request */
+/* */
+/*****************************************************************************/
+
+#define SAR_TSR_TYPE_TSR 0x80000000
+#define SAR_TSR_TYPE_TBD 0x00000000
+#define SAR_TSR_TSIF 0x20000000
+#define SAR_TSR_TAG_MASK 0x01F00000
+
+
+/*****************************************************************************/
+/* */
+/* TBD - Transmit Buffer Descriptor */
+/* */
+/*****************************************************************************/
+
+#define SAR_TBD_EPDU 0x40000000
+#define SAR_TBD_TSIF 0x20000000
+#define SAR_TBD_OAM 0x10000000
+#define SAR_TBD_AAL0 0x00000000
+#define SAR_TBD_AAL34 0x04000000
+#define SAR_TBD_AAL5 0x08000000
+#define SAR_TBD_GTSI 0x02000000
+#define SAR_TBD_TAG_MASK 0x01F00000
+
+#define SAR_TBD_VPI_MASK 0x0FF00000
+#define SAR_TBD_VCI_MASK 0x000FFFF0
+#define SAR_TBD_VC_MASK (SAR_TBD_VPI_MASK | SAR_TBD_VCI_MASK)
+
+#define SAR_TBD_VPI_SHIFT 20
+#define SAR_TBD_VCI_SHIFT 4
+
+
+/*****************************************************************************/
+/* */
+/* RXFD - Receive FIFO Descriptor */
+/* */
+/*****************************************************************************/
+
+#define SAR_RXFD_SIZE_MASK 0x0F000000
+#define SAR_RXFD_SIZE_512 0x00000000 /* 512 words */
+#define SAR_RXFD_SIZE_1K 0x01000000 /* 1k words */
+#define SAR_RXFD_SIZE_2K 0x02000000 /* 2k words */
+#define SAR_RXFD_SIZE_4K 0x03000000 /* 4k words */
+#define SAR_RXFD_SIZE_8K 0x04000000 /* 8k words */
+#define SAR_RXFD_SIZE_16K 0x05000000 /* 16k words */
+#define SAR_RXFD_SIZE_32K 0x06000000 /* 32k words */
+#define SAR_RXFD_SIZE_64K 0x07000000 /* 64k words */
+#define SAR_RXFD_SIZE_128K 0x08000000 /* 128k words */
+#define SAR_RXFD_SIZE_256K 0x09000000 /* 256k words */
+#define SAR_RXFD_ADDR_MASK 0x001ffc00
+
+
+/*****************************************************************************/
+/* */
+/* ABRSTD - ABR + VBR Schedule Tables */
+/* */
+/*****************************************************************************/
+
+#define SAR_ABRSTD_SIZE_MASK 0x07000000
+#define SAR_ABRSTD_SIZE_512 0x00000000 /* 512 words */
+#define SAR_ABRSTD_SIZE_1K 0x01000000 /* 1k words */
+#define SAR_ABRSTD_SIZE_2K 0x02000000 /* 2k words */
+#define SAR_ABRSTD_SIZE_4K 0x03000000 /* 4k words */
+#define SAR_ABRSTD_SIZE_8K 0x04000000 /* 8k words */
+#define SAR_ABRSTD_SIZE_16K 0x05000000 /* 16k words */
+#define SAR_ABRSTD_ADDR_MASK 0x001ffc00
+
+
+/*****************************************************************************/
+/* */
+/* RCTE - Receive Connection Table Entry */
+/* */
+/*****************************************************************************/
+
+#define SAR_RCTE_IL_MASK 0xE0000000 /* inactivity limit */
+#define SAR_RCTE_IC_MASK 0x1C000000 /* inactivity count */
+#define SAR_RCTE_RSVD 0x02000000 /* reserved */
+#define SAR_RCTE_LCD 0x01000000 /* last cell data */
+#define SAR_RCTE_CI_VC 0x00800000 /* EFCI in previous cell of VC */
+#define SAR_RCTE_FBP_01 0x00000000 /* 1. cell->FBQ0, others->FBQ1 */
+#define SAR_RCTE_FBP_1 0x00200000 /* use FBQ 1 for all cells */
+#define SAR_RCTE_FBP_2 0x00400000 /* use FBQ 2 for all cells */
+#define SAR_RCTE_FBP_3 0x00600000 /* use FBQ 3 for all cells */
+#define SAR_RCTE_NZ_GFC 0x00100000 /* non zero GFC in all cell of VC */
+#define SAR_RCTE_CONNECTOPEN 0x00080000 /* VC is open */
+#define SAR_RCTE_AAL_MASK 0x00070000 /* mask for AAL type field s.b. */
+#define SAR_RCTE_RAWCELLINTEN 0x00008000 /* raw cell interrupt enable */
+#define SAR_RCTE_RXCONCELLADDR 0x00004000 /* RX constant cell address */
+#define SAR_RCTE_BUFFSTAT_MASK 0x00003000 /* buffer status */
+#define SAR_RCTE_EFCI 0x00000800 /* EFCI Congestion flag */
+#define SAR_RCTE_CLP 0x00000400 /* Cell Loss Priority flag */
+#define SAR_RCTE_CRC 0x00000200 /* Received CRC Error */
+#define SAR_RCTE_CELLCNT_MASK 0x000001FF /* cell Count */
+
+#define SAR_RCTE_AAL0 0x00000000 /* AAL types for ALL field */
+#define SAR_RCTE_AAL34 0x00010000
+#define SAR_RCTE_AAL5 0x00020000
+#define SAR_RCTE_RCQ 0x00030000
+#define SAR_RCTE_OAM 0x00040000
+
+#define TCMDQ_START 0x01000000
+#define TCMDQ_LACR 0x02000000
+#define TCMDQ_START_LACR 0x03000000
+#define TCMDQ_INIT_ER 0x04000000
+#define TCMDQ_HALT 0x05000000
+
+
+struct idt77252_skb_prv {
+ struct scqe tbd; /* Transmit Buffer Descriptor */
+ dma_addr_t paddr; /* DMA handle */
+ u32 pool; /* sb_pool handle */
+};
+
+#define IDT77252_PRV_TBD(skb) \
+ (((struct idt77252_skb_prv *)(ATM_SKB(skb)+1))->tbd)
+#define IDT77252_PRV_PADDR(skb) \
+ (((struct idt77252_skb_prv *)(ATM_SKB(skb)+1))->paddr)
+#define IDT77252_PRV_POOL(skb) \
+ (((struct idt77252_skb_prv *)(ATM_SKB(skb)+1))->pool)
+
+/*****************************************************************************/
+/* */
+/* PCI related items */
+/* */
+/*****************************************************************************/
+
+#ifndef PCI_VENDOR_ID_IDT
+#define PCI_VENDOR_ID_IDT 0x111D
+#endif /* PCI_VENDOR_ID_IDT */
+
+#ifndef PCI_DEVICE_ID_IDT_IDT77252
+#define PCI_DEVICE_ID_IDT_IDT77252 0x0003
+#endif /* PCI_DEVICE_ID_IDT_IDT772052 */
+
+
+#endif /* !(_IDT77252_H) */
diff --git a/drivers/atm/idt77252_tables.h b/drivers/atm/idt77252_tables.h
new file mode 100644
index 000000000..b6c8ee512
--- /dev/null
+++ b/drivers/atm/idt77252_tables.h
@@ -0,0 +1,780 @@
+/* Do not edit, automatically generated by `./genrtbl'.
+ *
+ * Cell Line Rate: 353207.55 (155520000 bps)
+ */
+
+static unsigned int log_to_rate[] =
+{
+/* 000 */ 0x8d022e27, /* cps = 10.02, nrm = 3, interval = 35264.00 */
+/* 001 */ 0x8d362e11, /* cps = 10.42, nrm = 3, interval = 33856.00 */
+/* 002 */ 0x8d6e2bf8, /* cps = 10.86, nrm = 3, interval = 32512.00 */
+/* 003 */ 0x8da82bcf, /* cps = 11.31, nrm = 3, interval = 31200.00 */
+/* 004 */ 0x8de42ba8, /* cps = 11.78, nrm = 3, interval = 29952.00 */
+/* 005 */ 0x8e242b82, /* cps = 12.28, nrm = 3, interval = 28736.00 */
+/* 006 */ 0x8e662b5e, /* cps = 12.80, nrm = 3, interval = 27584.00 */
+/* 007 */ 0x8eaa2b3c, /* cps = 13.33, nrm = 3, interval = 26496.00 */
+/* 008 */ 0x8ef22b1a, /* cps = 13.89, nrm = 3, interval = 25408.00 */
+/* 009 */ 0x8f3e2afa, /* cps = 14.48, nrm = 3, interval = 24384.00 */
+/* 010 */ 0x8f8a2adc, /* cps = 15.08, nrm = 3, interval = 23424.00 */
+/* 011 */ 0x8fdc2abe, /* cps = 15.72, nrm = 3, interval = 22464.00 */
+/* 012 */ 0x90182aa2, /* cps = 16.38, nrm = 3, interval = 21568.00 */
+/* 013 */ 0x90422a87, /* cps = 17.03, nrm = 3, interval = 20704.00 */
+/* 014 */ 0x90702a6d, /* cps = 17.75, nrm = 3, interval = 19872.00 */
+/* 015 */ 0x90a02a54, /* cps = 18.50, nrm = 3, interval = 19072.00 */
+/* 016 */ 0x90d22a3c, /* cps = 19.28, nrm = 3, interval = 18304.00 */
+/* 017 */ 0x91062a25, /* cps = 20.09, nrm = 3, interval = 17568.00 */
+/* 018 */ 0x913c2a0f, /* cps = 20.94, nrm = 3, interval = 16864.00 */
+/* 019 */ 0x917427f3, /* cps = 21.81, nrm = 3, interval = 16176.00 */
+/* 020 */ 0x91b027ca, /* cps = 22.75, nrm = 3, interval = 15520.00 */
+/* 021 */ 0x91ec27a3, /* cps = 23.69, nrm = 3, interval = 14896.00 */
+/* 022 */ 0x922c277e, /* cps = 24.69, nrm = 3, interval = 14304.00 */
+/* 023 */ 0x926e275a, /* cps = 25.72, nrm = 3, interval = 13728.00 */
+/* 024 */ 0x92b42737, /* cps = 26.81, nrm = 3, interval = 13168.00 */
+/* 025 */ 0x92fc2716, /* cps = 27.94, nrm = 3, interval = 12640.00 */
+/* 026 */ 0x934626f6, /* cps = 29.09, nrm = 3, interval = 12128.00 */
+/* 027 */ 0x939426d8, /* cps = 30.31, nrm = 3, interval = 11648.00 */
+/* 028 */ 0x93e426bb, /* cps = 31.56, nrm = 3, interval = 11184.00 */
+/* 029 */ 0x941e269e, /* cps = 32.94, nrm = 3, interval = 10720.00 */
+/* 030 */ 0x944a2683, /* cps = 34.31, nrm = 3, interval = 10288.00 */
+/* 031 */ 0x9476266a, /* cps = 35.69, nrm = 3, interval = 9888.00 */
+/* 032 */ 0x94a62651, /* cps = 37.19, nrm = 3, interval = 9488.00 */
+/* 033 */ 0x94d82639, /* cps = 38.75, nrm = 3, interval = 9104.00 */
+/* 034 */ 0x950c6622, /* cps = 40.38, nrm = 4, interval = 8736.00 */
+/* 035 */ 0x9544660c, /* cps = 42.12, nrm = 4, interval = 8384.00 */
+/* 036 */ 0x957c63ee, /* cps = 43.88, nrm = 4, interval = 8048.00 */
+/* 037 */ 0x95b663c6, /* cps = 45.69, nrm = 4, interval = 7728.00 */
+/* 038 */ 0x95f4639f, /* cps = 47.62, nrm = 4, interval = 7416.00 */
+/* 039 */ 0x96346379, /* cps = 49.62, nrm = 4, interval = 7112.00 */
+/* 040 */ 0x96766356, /* cps = 51.69, nrm = 4, interval = 6832.00 */
+/* 041 */ 0x96bc6333, /* cps = 53.88, nrm = 4, interval = 6552.00 */
+/* 042 */ 0x97046312, /* cps = 56.12, nrm = 4, interval = 6288.00 */
+/* 043 */ 0x974e62f3, /* cps = 58.44, nrm = 4, interval = 6040.00 */
+/* 044 */ 0x979e62d4, /* cps = 60.94, nrm = 4, interval = 5792.00 */
+/* 045 */ 0x97f062b7, /* cps = 63.50, nrm = 4, interval = 5560.00 */
+/* 046 */ 0x9822629b, /* cps = 66.12, nrm = 4, interval = 5336.00 */
+/* 047 */ 0x984e6280, /* cps = 68.88, nrm = 4, interval = 5120.00 */
+/* 048 */ 0x987e6266, /* cps = 71.88, nrm = 4, interval = 4912.00 */
+/* 049 */ 0x98ac624e, /* cps = 74.75, nrm = 4, interval = 4720.00 */
+/* 050 */ 0x98e06236, /* cps = 78.00, nrm = 4, interval = 4528.00 */
+/* 051 */ 0x9914a21f, /* cps = 81.25, nrm = 8, interval = 4344.00 */
+/* 052 */ 0x994aa209, /* cps = 84.62, nrm = 8, interval = 4168.00 */
+/* 053 */ 0x99829fe9, /* cps = 88.12, nrm = 8, interval = 4004.00 */
+/* 054 */ 0x99be9fc1, /* cps = 91.88, nrm = 8, interval = 3844.00 */
+/* 055 */ 0x99fc9f9a, /* cps = 95.75, nrm = 8, interval = 3688.00 */
+/* 056 */ 0x9a3c9f75, /* cps = 99.75, nrm = 8, interval = 3540.00 */
+/* 057 */ 0x9a809f51, /* cps = 104.00, nrm = 8, interval = 3396.00 */
+/* 058 */ 0x9ac49f2f, /* cps = 108.25, nrm = 8, interval = 3260.00 */
+/* 059 */ 0x9b0e9f0e, /* cps = 112.88, nrm = 8, interval = 3128.00 */
+/* 060 */ 0x9b589eef, /* cps = 117.50, nrm = 8, interval = 3004.00 */
+/* 061 */ 0x9ba69ed1, /* cps = 122.38, nrm = 8, interval = 2884.00 */
+/* 062 */ 0x9bf89eb4, /* cps = 127.50, nrm = 8, interval = 2768.00 */
+/* 063 */ 0x9c269e98, /* cps = 132.75, nrm = 8, interval = 2656.00 */
+/* 064 */ 0x9c549e7d, /* cps = 138.50, nrm = 8, interval = 2548.00 */
+/* 065 */ 0x9c849e63, /* cps = 144.50, nrm = 8, interval = 2444.00 */
+/* 066 */ 0x9cb29e4b, /* cps = 150.25, nrm = 8, interval = 2348.00 */
+/* 067 */ 0x9ce69e33, /* cps = 156.75, nrm = 8, interval = 2252.00 */
+/* 068 */ 0x9d1cde1c, /* cps = 163.50, nrm = 16, interval = 2160.00 */
+/* 069 */ 0x9d50de07, /* cps = 170.00, nrm = 16, interval = 2076.00 */
+/* 070 */ 0x9d8adbe4, /* cps = 177.25, nrm = 16, interval = 1992.00 */
+/* 071 */ 0x9dc4dbbc, /* cps = 184.50, nrm = 16, interval = 1912.00 */
+/* 072 */ 0x9e02db96, /* cps = 192.25, nrm = 16, interval = 1836.00 */
+/* 073 */ 0x9e42db71, /* cps = 200.25, nrm = 16, interval = 1762.00 */
+/* 074 */ 0x9e86db4d, /* cps = 208.75, nrm = 16, interval = 1690.00 */
+/* 075 */ 0x9ecedb2b, /* cps = 217.75, nrm = 16, interval = 1622.00 */
+/* 076 */ 0x9f16db0a, /* cps = 226.75, nrm = 16, interval = 1556.00 */
+/* 077 */ 0x9f62daeb, /* cps = 236.25, nrm = 16, interval = 1494.00 */
+/* 078 */ 0x9fb2dacd, /* cps = 246.25, nrm = 16, interval = 1434.00 */
+/* 079 */ 0xa002dab0, /* cps = 256.50, nrm = 16, interval = 1376.00 */
+/* 080 */ 0xa02eda94, /* cps = 267.50, nrm = 16, interval = 1320.00 */
+/* 081 */ 0xa05ada7a, /* cps = 278.50, nrm = 16, interval = 1268.00 */
+/* 082 */ 0xa088da60, /* cps = 290.00, nrm = 16, interval = 1216.00 */
+/* 083 */ 0xa0b8da48, /* cps = 302.00, nrm = 16, interval = 1168.00 */
+/* 084 */ 0xa0ecda30, /* cps = 315.00, nrm = 16, interval = 1120.00 */
+/* 085 */ 0xa1211a1a, /* cps = 328.00, nrm = 32, interval = 1076.00 */
+/* 086 */ 0xa1591a04, /* cps = 342.00, nrm = 32, interval = 1032.00 */
+/* 087 */ 0xa19117df, /* cps = 356.00, nrm = 32, interval = 991.00 */
+/* 088 */ 0xa1cd17b7, /* cps = 371.00, nrm = 32, interval = 951.00 */
+/* 089 */ 0xa20b1791, /* cps = 386.50, nrm = 32, interval = 913.00 */
+/* 090 */ 0xa24d176c, /* cps = 403.00, nrm = 32, interval = 876.00 */
+/* 091 */ 0xa28f1749, /* cps = 419.50, nrm = 32, interval = 841.00 */
+/* 092 */ 0xa2d71727, /* cps = 437.50, nrm = 32, interval = 807.00 */
+/* 093 */ 0xa31f1707, /* cps = 455.50, nrm = 32, interval = 775.00 */
+/* 094 */ 0xa36d16e7, /* cps = 475.00, nrm = 32, interval = 743.00 */
+/* 095 */ 0xa3bd16c9, /* cps = 495.00, nrm = 32, interval = 713.00 */
+/* 096 */ 0xa40716ad, /* cps = 515.00, nrm = 32, interval = 685.00 */
+/* 097 */ 0xa4331691, /* cps = 537.00, nrm = 32, interval = 657.00 */
+/* 098 */ 0xa45f1677, /* cps = 559.00, nrm = 32, interval = 631.00 */
+/* 099 */ 0xa48f165d, /* cps = 583.00, nrm = 32, interval = 605.00 */
+/* 100 */ 0xa4bf1645, /* cps = 607.00, nrm = 32, interval = 581.00 */
+/* 101 */ 0xa4f1162e, /* cps = 632.00, nrm = 32, interval = 558.00 */
+/* 102 */ 0xa5291617, /* cps = 660.00, nrm = 32, interval = 535.00 */
+/* 103 */ 0xa55f1602, /* cps = 687.00, nrm = 32, interval = 514.00 */
+/* 104 */ 0xa59913da, /* cps = 716.00, nrm = 32, interval = 493.00 */
+/* 105 */ 0xa5d513b2, /* cps = 746.00, nrm = 32, interval = 473.00 */
+/* 106 */ 0xa613138c, /* cps = 777.00, nrm = 32, interval = 454.00 */
+/* 107 */ 0xa6551368, /* cps = 810.00, nrm = 32, interval = 436.00 */
+/* 108 */ 0xa6971345, /* cps = 843.00, nrm = 32, interval = 418.50 */
+/* 109 */ 0xa6df1323, /* cps = 879.00, nrm = 32, interval = 401.50 */
+/* 110 */ 0xa7291303, /* cps = 916.00, nrm = 32, interval = 385.50 */
+/* 111 */ 0xa77512e4, /* cps = 954.00, nrm = 32, interval = 370.00 */
+/* 112 */ 0xa7c512c6, /* cps = 994.00, nrm = 32, interval = 355.00 */
+/* 113 */ 0xa80d12a9, /* cps = 1036.00, nrm = 32, interval = 340.50 */
+/* 114 */ 0xa839128e, /* cps = 1080.00, nrm = 32, interval = 327.00 */
+/* 115 */ 0xa8651274, /* cps = 1124.00, nrm = 32, interval = 314.00 */
+/* 116 */ 0xa895125a, /* cps = 1172.00, nrm = 32, interval = 301.00 */
+/* 117 */ 0xa8c71242, /* cps = 1222.00, nrm = 32, interval = 289.00 */
+/* 118 */ 0xa8f9122b, /* cps = 1272.00, nrm = 32, interval = 277.50 */
+/* 119 */ 0xa92f1214, /* cps = 1326.00, nrm = 32, interval = 266.00 */
+/* 120 */ 0xa9670ffe, /* cps = 1382.00, nrm = 32, interval = 255.50 */
+/* 121 */ 0xa9a10fd5, /* cps = 1440.00, nrm = 32, interval = 245.25 */
+/* 122 */ 0xa9db0fae, /* cps = 1498.00, nrm = 32, interval = 235.50 */
+/* 123 */ 0xaa1b0f88, /* cps = 1562.00, nrm = 32, interval = 226.00 */
+/* 124 */ 0xaa5d0f63, /* cps = 1628.00, nrm = 32, interval = 216.75 */
+/* 125 */ 0xaaa10f41, /* cps = 1696.00, nrm = 32, interval = 208.25 */
+/* 126 */ 0xaae90f1f, /* cps = 1768.00, nrm = 32, interval = 199.75 */
+/* 127 */ 0xab330eff, /* cps = 1842.00, nrm = 32, interval = 191.75 */
+/* 128 */ 0xab7f0ee0, /* cps = 1918.00, nrm = 32, interval = 184.00 */
+/* 129 */ 0xabd10ec2, /* cps = 2000.00, nrm = 32, interval = 176.50 */
+/* 130 */ 0xac110ea6, /* cps = 2080.00, nrm = 32, interval = 169.50 */
+/* 131 */ 0xac3d0e8b, /* cps = 2168.00, nrm = 32, interval = 162.75 */
+/* 132 */ 0xac6d0e70, /* cps = 2264.00, nrm = 32, interval = 156.00 */
+/* 133 */ 0xac9b0e57, /* cps = 2356.00, nrm = 32, interval = 149.75 */
+/* 134 */ 0xaccd0e3f, /* cps = 2456.00, nrm = 32, interval = 143.75 */
+/* 135 */ 0xacff0e28, /* cps = 2556.00, nrm = 32, interval = 138.00 */
+/* 136 */ 0xad350e12, /* cps = 2664.00, nrm = 32, interval = 132.50 */
+/* 137 */ 0xad6d0bf9, /* cps = 2776.00, nrm = 32, interval = 127.12 */
+/* 138 */ 0xada70bd0, /* cps = 2892.00, nrm = 32, interval = 122.00 */
+/* 139 */ 0xade30ba9, /* cps = 3012.00, nrm = 32, interval = 117.12 */
+/* 140 */ 0xae230b83, /* cps = 3140.00, nrm = 32, interval = 112.38 */
+/* 141 */ 0xae650b5f, /* cps = 3272.00, nrm = 32, interval = 107.88 */
+/* 142 */ 0xaeab0b3c, /* cps = 3412.00, nrm = 32, interval = 103.50 */
+/* 143 */ 0xaef10b1b, /* cps = 3552.00, nrm = 32, interval = 99.38 */
+/* 144 */ 0xaf3b0afb, /* cps = 3700.00, nrm = 32, interval = 95.38 */
+/* 145 */ 0xaf8b0adc, /* cps = 3860.00, nrm = 32, interval = 91.50 */
+/* 146 */ 0xafd90abf, /* cps = 4016.00, nrm = 32, interval = 87.88 */
+/* 147 */ 0xb0170aa3, /* cps = 4184.00, nrm = 32, interval = 84.38 */
+/* 148 */ 0xb0430a87, /* cps = 4360.00, nrm = 32, interval = 80.88 */
+/* 149 */ 0xb0710a6d, /* cps = 4544.00, nrm = 32, interval = 77.62 */
+/* 150 */ 0xb0a10a54, /* cps = 4736.00, nrm = 32, interval = 74.50 */
+/* 151 */ 0xb0d30a3c, /* cps = 4936.00, nrm = 32, interval = 71.50 */
+/* 152 */ 0xb1070a25, /* cps = 5144.00, nrm = 32, interval = 68.62 */
+/* 153 */ 0xb13d0a0f, /* cps = 5360.00, nrm = 32, interval = 65.88 */
+/* 154 */ 0xb17507f4, /* cps = 5584.00, nrm = 32, interval = 63.25 */
+/* 155 */ 0xb1af07cb, /* cps = 5816.00, nrm = 32, interval = 60.69 */
+/* 156 */ 0xb1eb07a4, /* cps = 6056.00, nrm = 32, interval = 58.25 */
+/* 157 */ 0xb22b077f, /* cps = 6312.00, nrm = 32, interval = 55.94 */
+/* 158 */ 0xb26d075b, /* cps = 6576.00, nrm = 32, interval = 53.69 */
+/* 159 */ 0xb2b30738, /* cps = 6856.00, nrm = 32, interval = 51.50 */
+/* 160 */ 0xb2fb0717, /* cps = 7144.00, nrm = 32, interval = 49.44 */
+/* 161 */ 0xb34506f7, /* cps = 7440.00, nrm = 32, interval = 47.44 */
+/* 162 */ 0xb39306d9, /* cps = 7752.00, nrm = 32, interval = 45.56 */
+/* 163 */ 0xb3e506bb, /* cps = 8080.00, nrm = 32, interval = 43.69 */
+/* 164 */ 0xb41d069f, /* cps = 8416.00, nrm = 32, interval = 41.94 */
+/* 165 */ 0xb4490684, /* cps = 8768.00, nrm = 32, interval = 40.25 */
+/* 166 */ 0xb477066a, /* cps = 9136.00, nrm = 32, interval = 38.62 */
+/* 167 */ 0xb4a70651, /* cps = 9520.00, nrm = 32, interval = 37.06 */
+/* 168 */ 0xb4d90639, /* cps = 9920.00, nrm = 32, interval = 35.56 */
+/* 169 */ 0xb50d0622, /* cps = 10336.00, nrm = 32, interval = 34.12 */
+/* 170 */ 0xb545060c, /* cps = 10784.00, nrm = 32, interval = 32.75 */
+/* 171 */ 0xb57b03ef, /* cps = 11216.00, nrm = 32, interval = 31.47 */
+/* 172 */ 0xb5b503c7, /* cps = 11680.00, nrm = 32, interval = 30.22 */
+/* 173 */ 0xb5f303a0, /* cps = 12176.00, nrm = 32, interval = 29.00 */
+/* 174 */ 0xb633037a, /* cps = 12688.00, nrm = 32, interval = 27.81 */
+/* 175 */ 0xb6750357, /* cps = 13216.00, nrm = 32, interval = 26.72 */
+/* 176 */ 0xb6bb0334, /* cps = 13776.00, nrm = 32, interval = 25.62 */
+/* 177 */ 0xb7030313, /* cps = 14352.00, nrm = 32, interval = 24.59 */
+/* 178 */ 0xb74f02f3, /* cps = 14960.00, nrm = 32, interval = 23.59 */
+/* 179 */ 0xb79d02d5, /* cps = 15584.00, nrm = 32, interval = 22.66 */
+/* 180 */ 0xb7ed02b8, /* cps = 16224.00, nrm = 32, interval = 21.75 */
+/* 181 */ 0xb821029c, /* cps = 16896.00, nrm = 32, interval = 20.88 */
+/* 182 */ 0xb84f0281, /* cps = 17632.00, nrm = 32, interval = 20.03 */
+/* 183 */ 0xb87d0267, /* cps = 18368.00, nrm = 32, interval = 19.22 */
+/* 184 */ 0xb8ad024e, /* cps = 19136.00, nrm = 32, interval = 18.44 */
+/* 185 */ 0xb8dd0237, /* cps = 19904.00, nrm = 32, interval = 17.72 */
+/* 186 */ 0xb9130220, /* cps = 20768.00, nrm = 32, interval = 17.00 */
+/* 187 */ 0xb949020a, /* cps = 21632.00, nrm = 32, interval = 16.31 */
+/* 188 */ 0xb98301f5, /* cps = 22560.00, nrm = 32, interval = 15.66 */
+/* 189 */ 0xb9bd01e1, /* cps = 23488.00, nrm = 32, interval = 15.03 */
+/* 190 */ 0xb9fd01cd, /* cps = 24512.00, nrm = 32, interval = 14.41 */
+/* 191 */ 0xba3b01bb, /* cps = 25504.00, nrm = 32, interval = 13.84 */
+/* 192 */ 0xba7f01a9, /* cps = 26592.00, nrm = 32, interval = 13.28 */
+/* 193 */ 0xbac30198, /* cps = 27680.00, nrm = 32, interval = 12.75 */
+/* 194 */ 0xbb0f0187, /* cps = 28896.00, nrm = 32, interval = 12.22 */
+/* 195 */ 0xbb570178, /* cps = 30048.00, nrm = 32, interval = 11.75 */
+/* 196 */ 0xbbab0168, /* cps = 31392.00, nrm = 32, interval = 11.25 */
+/* 197 */ 0xbbf9015a, /* cps = 32640.00, nrm = 32, interval = 10.81 */
+/* 198 */ 0xbc27014c, /* cps = 33984.00, nrm = 32, interval = 10.38 */
+/* 199 */ 0xbc53013f, /* cps = 35392.00, nrm = 32, interval = 9.97 */
+/* 200 */ 0xbc830132, /* cps = 36928.00, nrm = 32, interval = 9.56 */
+/* 201 */ 0xbcb50125, /* cps = 38528.00, nrm = 32, interval = 9.16 */
+/* 202 */ 0xbce5011a, /* cps = 40064.00, nrm = 32, interval = 8.81 */
+/* 203 */ 0xbd1d010e, /* cps = 41856.00, nrm = 32, interval = 8.44 */
+/* 204 */ 0xbd530103, /* cps = 43584.00, nrm = 32, interval = 8.09 */
+/* 205 */ 0xbd8b00f9, /* cps = 45376.00, nrm = 32, interval = 7.78 */
+/* 206 */ 0xbdc500ef, /* cps = 47232.00, nrm = 32, interval = 7.47 */
+/* 207 */ 0xbe0700e5, /* cps = 49344.00, nrm = 32, interval = 7.16 */
+/* 208 */ 0xbe4500dc, /* cps = 51328.00, nrm = 32, interval = 6.88 */
+/* 209 */ 0xbe8900d3, /* cps = 53504.00, nrm = 32, interval = 6.59 */
+/* 210 */ 0xbecb00cb, /* cps = 55616.00, nrm = 32, interval = 6.34 */
+/* 211 */ 0xbf1d00c2, /* cps = 58240.00, nrm = 32, interval = 6.06 */
+/* 212 */ 0xbf6100bb, /* cps = 60416.00, nrm = 32, interval = 5.84 */
+/* 213 */ 0xbfb500b3, /* cps = 63104.00, nrm = 32, interval = 5.59 */
+/* 214 */ 0xc00300ac, /* cps = 65664.00, nrm = 32, interval = 5.38 */
+/* 215 */ 0xc02f00a5, /* cps = 68480.00, nrm = 32, interval = 5.16 */
+/* 216 */ 0xc05d009e, /* cps = 71424.00, nrm = 32, interval = 4.94 */
+/* 217 */ 0xc0890098, /* cps = 74240.00, nrm = 32, interval = 4.75 */
+/* 218 */ 0xc0b90092, /* cps = 77312.00, nrm = 32, interval = 4.56 */
+/* 219 */ 0xc0ed008c, /* cps = 80640.00, nrm = 32, interval = 4.38 */
+/* 220 */ 0xc1250086, /* cps = 84224.00, nrm = 32, interval = 4.19 */
+/* 221 */ 0xc1590081, /* cps = 87552.00, nrm = 32, interval = 4.03 */
+/* 222 */ 0xc191007c, /* cps = 91136.00, nrm = 32, interval = 3.88 */
+/* 223 */ 0xc1cd0077, /* cps = 94976.00, nrm = 32, interval = 3.72 */
+/* 224 */ 0xc20d0072, /* cps = 99072.00, nrm = 32, interval = 3.56 */
+/* 225 */ 0xc255006d, /* cps = 103680.00, nrm = 32, interval = 3.41 */
+/* 226 */ 0xc2910069, /* cps = 107520.00, nrm = 32, interval = 3.28 */
+/* 227 */ 0xc2d50065, /* cps = 111872.00, nrm = 32, interval = 3.16 */
+/* 228 */ 0xc32f0060, /* cps = 117632.00, nrm = 32, interval = 3.00 */
+/* 229 */ 0xc36b005d, /* cps = 121472.00, nrm = 32, interval = 2.91 */
+/* 230 */ 0xc3c10059, /* cps = 126976.00, nrm = 32, interval = 2.78 */
+/* 231 */ 0xc40f0055, /* cps = 132864.00, nrm = 32, interval = 2.66 */
+/* 232 */ 0xc4350052, /* cps = 137728.00, nrm = 32, interval = 2.56 */
+/* 233 */ 0xc46d004e, /* cps = 144896.00, nrm = 32, interval = 2.44 */
+/* 234 */ 0xc499004b, /* cps = 150528.00, nrm = 32, interval = 2.34 */
+/* 235 */ 0xc4cb0048, /* cps = 156928.00, nrm = 32, interval = 2.25 */
+/* 236 */ 0xc4ff0045, /* cps = 163584.00, nrm = 32, interval = 2.16 */
+/* 237 */ 0xc5250043, /* cps = 168448.00, nrm = 32, interval = 2.09 */
+/* 238 */ 0xc5630040, /* cps = 176384.00, nrm = 32, interval = 2.00 */
+/* 239 */ 0xc5a7003d, /* cps = 185088.00, nrm = 32, interval = 1.91 */
+/* 240 */ 0xc5d9003b, /* cps = 191488.00, nrm = 32, interval = 1.84 */
+/* 241 */ 0xc6290038, /* cps = 201728.00, nrm = 32, interval = 1.75 */
+/* 242 */ 0xc6630036, /* cps = 209152.00, nrm = 32, interval = 1.69 */
+/* 243 */ 0xc6a30034, /* cps = 217344.00, nrm = 32, interval = 1.62 */
+/* 244 */ 0xc6e70032, /* cps = 226048.00, nrm = 32, interval = 1.56 */
+/* 245 */ 0xc72f0030, /* cps = 235264.00, nrm = 32, interval = 1.50 */
+/* 246 */ 0xc77f002e, /* cps = 245504.00, nrm = 32, interval = 1.44 */
+/* 247 */ 0xc7d7002c, /* cps = 256768.00, nrm = 32, interval = 1.38 */
+/* 248 */ 0xc81b002a, /* cps = 268800.00, nrm = 32, interval = 1.31 */
+/* 249 */ 0xc84f0028, /* cps = 282112.00, nrm = 32, interval = 1.25 */
+/* 250 */ 0xc86d0027, /* cps = 289792.00, nrm = 32, interval = 1.22 */
+/* 251 */ 0xc8a90025, /* cps = 305152.00, nrm = 32, interval = 1.16 */
+/* 252 */ 0xc8cb0024, /* cps = 313856.00, nrm = 32, interval = 1.12 */
+/* 253 */ 0xc9130022, /* cps = 332288.00, nrm = 32, interval = 1.06 */
+/* 254 */ 0xc9390021, /* cps = 342016.00, nrm = 32, interval = 1.03 */
+/* 255 */ 0xc9630020, /* cps = 352768.00, nrm = 32, interval = 1.00 */
+};
+
+static unsigned char rate_to_log[] =
+{
+/* 1.00 => 0 */ 0x00, /* => 10.02 */
+/* 1.06 => 0 */ 0x00, /* => 10.02 */
+/* 1.12 => 0 */ 0x00, /* => 10.02 */
+/* 1.19 => 0 */ 0x00, /* => 10.02 */
+/* 1.25 => 0 */ 0x00, /* => 10.02 */
+/* 1.31 => 0 */ 0x00, /* => 10.02 */
+/* 1.38 => 0 */ 0x00, /* => 10.02 */
+/* 1.44 => 0 */ 0x00, /* => 10.02 */
+/* 1.50 => 0 */ 0x00, /* => 10.02 */
+/* 1.56 => 0 */ 0x00, /* => 10.02 */
+/* 1.62 => 0 */ 0x00, /* => 10.02 */
+/* 1.69 => 0 */ 0x00, /* => 10.02 */
+/* 1.75 => 0 */ 0x00, /* => 10.02 */
+/* 1.81 => 0 */ 0x00, /* => 10.02 */
+/* 1.88 => 0 */ 0x00, /* => 10.02 */
+/* 1.94 => 0 */ 0x00, /* => 10.02 */
+/* 2.00 => 0 */ 0x00, /* => 10.02 */
+/* 2.12 => 0 */ 0x00, /* => 10.02 */
+/* 2.25 => 0 */ 0x00, /* => 10.02 */
+/* 2.38 => 0 */ 0x00, /* => 10.02 */
+/* 2.50 => 0 */ 0x00, /* => 10.02 */
+/* 2.62 => 0 */ 0x00, /* => 10.02 */
+/* 2.75 => 0 */ 0x00, /* => 10.02 */
+/* 2.88 => 0 */ 0x00, /* => 10.02 */
+/* 3.00 => 0 */ 0x00, /* => 10.02 */
+/* 3.12 => 0 */ 0x00, /* => 10.02 */
+/* 3.25 => 0 */ 0x00, /* => 10.02 */
+/* 3.38 => 0 */ 0x00, /* => 10.02 */
+/* 3.50 => 0 */ 0x00, /* => 10.02 */
+/* 3.62 => 0 */ 0x00, /* => 10.02 */
+/* 3.75 => 0 */ 0x00, /* => 10.02 */
+/* 3.88 => 0 */ 0x00, /* => 10.02 */
+/* 4.00 => 0 */ 0x00, /* => 10.02 */
+/* 4.25 => 0 */ 0x00, /* => 10.02 */
+/* 4.50 => 0 */ 0x00, /* => 10.02 */
+/* 4.75 => 0 */ 0x00, /* => 10.02 */
+/* 5.00 => 0 */ 0x00, /* => 10.02 */
+/* 5.25 => 0 */ 0x00, /* => 10.02 */
+/* 5.50 => 0 */ 0x00, /* => 10.02 */
+/* 5.75 => 0 */ 0x00, /* => 10.02 */
+/* 6.00 => 0 */ 0x00, /* => 10.02 */
+/* 6.25 => 0 */ 0x00, /* => 10.02 */
+/* 6.50 => 0 */ 0x00, /* => 10.02 */
+/* 6.75 => 0 */ 0x00, /* => 10.02 */
+/* 7.00 => 0 */ 0x00, /* => 10.02 */
+/* 7.25 => 0 */ 0x00, /* => 10.02 */
+/* 7.50 => 0 */ 0x00, /* => 10.02 */
+/* 7.75 => 0 */ 0x00, /* => 10.02 */
+/* 8.00 => 0 */ 0x00, /* => 10.02 */
+/* 8.50 => 0 */ 0x00, /* => 10.02 */
+/* 9.00 => 0 */ 0x00, /* => 10.02 */
+/* 9.50 => 0 */ 0x00, /* => 10.02 */
+/* 10.00 => 0 */ 0x00, /* => 10.02 */
+/* 10.50 => 1 */ 0x01, /* => 10.42 */
+/* 11.00 => 2 */ 0x02, /* => 10.86 */
+/* 11.50 => 3 */ 0x03, /* => 11.31 */
+/* 12.00 => 4 */ 0x04, /* => 11.78 */
+/* 12.50 => 5 */ 0x05, /* => 12.28 */
+/* 13.00 => 6 */ 0x06, /* => 12.80 */
+/* 13.50 => 7 */ 0x07, /* => 13.33 */
+/* 14.00 => 8 */ 0x08, /* => 13.89 */
+/* 14.50 => 9 */ 0x09, /* => 14.48 */
+/* 15.00 => 9 */ 0x09, /* => 14.48 */
+/* 15.50 => 10 */ 0x0a, /* => 15.08 */
+/* 16.00 => 11 */ 0x0b, /* => 15.72 */
+/* 17.00 => 12 */ 0x0c, /* => 16.38 */
+/* 18.00 => 14 */ 0x0e, /* => 17.75 */
+/* 19.00 => 15 */ 0x0f, /* => 18.50 */
+/* 20.00 => 16 */ 0x10, /* => 19.28 */
+/* 21.00 => 18 */ 0x12, /* => 20.94 */
+/* 22.00 => 19 */ 0x13, /* => 21.81 */
+/* 23.00 => 20 */ 0x14, /* => 22.75 */
+/* 24.00 => 21 */ 0x15, /* => 23.69 */
+/* 25.00 => 22 */ 0x16, /* => 24.69 */
+/* 26.00 => 23 */ 0x17, /* => 25.72 */
+/* 27.00 => 24 */ 0x18, /* => 26.81 */
+/* 28.00 => 25 */ 0x19, /* => 27.94 */
+/* 29.00 => 25 */ 0x19, /* => 27.94 */
+/* 30.00 => 26 */ 0x1a, /* => 29.09 */
+/* 31.00 => 27 */ 0x1b, /* => 30.31 */
+/* 32.00 => 28 */ 0x1c, /* => 31.56 */
+/* 34.00 => 29 */ 0x1d, /* => 32.94 */
+/* 36.00 => 31 */ 0x1f, /* => 35.69 */
+/* 38.00 => 32 */ 0x20, /* => 37.19 */
+/* 40.00 => 33 */ 0x21, /* => 38.75 */
+/* 42.00 => 34 */ 0x22, /* => 40.38 */
+/* 44.00 => 36 */ 0x24, /* => 43.88 */
+/* 46.00 => 37 */ 0x25, /* => 45.69 */
+/* 48.00 => 38 */ 0x26, /* => 47.62 */
+/* 50.00 => 39 */ 0x27, /* => 49.62 */
+/* 52.00 => 40 */ 0x28, /* => 51.69 */
+/* 54.00 => 41 */ 0x29, /* => 53.88 */
+/* 56.00 => 41 */ 0x29, /* => 53.88 */
+/* 58.00 => 42 */ 0x2a, /* => 56.12 */
+/* 60.00 => 43 */ 0x2b, /* => 58.44 */
+/* 62.00 => 44 */ 0x2c, /* => 60.94 */
+/* 64.00 => 45 */ 0x2d, /* => 63.50 */
+/* 68.00 => 46 */ 0x2e, /* => 66.12 */
+/* 72.00 => 48 */ 0x30, /* => 71.88 */
+/* 76.00 => 49 */ 0x31, /* => 74.75 */
+/* 80.00 => 50 */ 0x32, /* => 78.00 */
+/* 84.00 => 51 */ 0x33, /* => 81.25 */
+/* 88.00 => 52 */ 0x34, /* => 84.62 */
+/* 92.00 => 54 */ 0x36, /* => 91.88 */
+/* 96.00 => 55 */ 0x37, /* => 95.75 */
+/* 100.00 => 56 */ 0x38, /* => 99.75 */
+/* 104.00 => 56 */ 0x38, /* => 99.75 */
+/* 108.00 => 57 */ 0x39, /* => 104.00 */
+/* 112.00 => 58 */ 0x3a, /* => 108.25 */
+/* 116.00 => 59 */ 0x3b, /* => 112.88 */
+/* 120.00 => 60 */ 0x3c, /* => 117.50 */
+/* 124.00 => 61 */ 0x3d, /* => 122.38 */
+/* 128.00 => 62 */ 0x3e, /* => 127.50 */
+/* 136.00 => 63 */ 0x3f, /* => 132.75 */
+/* 144.00 => 64 */ 0x40, /* => 138.50 */
+/* 152.00 => 66 */ 0x42, /* => 150.25 */
+/* 160.00 => 67 */ 0x43, /* => 156.75 */
+/* 168.00 => 68 */ 0x44, /* => 163.50 */
+/* 176.00 => 69 */ 0x45, /* => 170.00 */
+/* 184.00 => 70 */ 0x46, /* => 177.25 */
+/* 192.00 => 71 */ 0x47, /* => 184.50 */
+/* 200.00 => 72 */ 0x48, /* => 192.25 */
+/* 208.00 => 73 */ 0x49, /* => 200.25 */
+/* 216.00 => 74 */ 0x4a, /* => 208.75 */
+/* 224.00 => 75 */ 0x4b, /* => 217.75 */
+/* 232.00 => 76 */ 0x4c, /* => 226.75 */
+/* 240.00 => 77 */ 0x4d, /* => 236.25 */
+/* 248.00 => 78 */ 0x4e, /* => 246.25 */
+/* 256.00 => 78 */ 0x4e, /* => 246.25 */
+/* 272.00 => 80 */ 0x50, /* => 267.50 */
+/* 288.00 => 81 */ 0x51, /* => 278.50 */
+/* 304.00 => 83 */ 0x53, /* => 302.00 */
+/* 320.00 => 84 */ 0x54, /* => 315.00 */
+/* 336.00 => 85 */ 0x55, /* => 328.00 */
+/* 352.00 => 86 */ 0x56, /* => 342.00 */
+/* 368.00 => 87 */ 0x57, /* => 356.00 */
+/* 384.00 => 88 */ 0x58, /* => 371.00 */
+/* 400.00 => 89 */ 0x59, /* => 386.50 */
+/* 416.00 => 90 */ 0x5a, /* => 403.00 */
+/* 432.00 => 91 */ 0x5b, /* => 419.50 */
+/* 448.00 => 92 */ 0x5c, /* => 437.50 */
+/* 464.00 => 93 */ 0x5d, /* => 455.50 */
+/* 480.00 => 94 */ 0x5e, /* => 475.00 */
+/* 496.00 => 95 */ 0x5f, /* => 495.00 */
+/* 512.00 => 95 */ 0x5f, /* => 495.00 */
+/* 544.00 => 97 */ 0x61, /* => 537.00 */
+/* 576.00 => 98 */ 0x62, /* => 559.00 */
+/* 608.00 => 100 */ 0x64, /* => 607.00 */
+/* 640.00 => 101 */ 0x65, /* => 632.00 */
+/* 672.00 => 102 */ 0x66, /* => 660.00 */
+/* 704.00 => 103 */ 0x67, /* => 687.00 */
+/* 736.00 => 104 */ 0x68, /* => 716.00 */
+/* 768.00 => 105 */ 0x69, /* => 746.00 */
+/* 800.00 => 106 */ 0x6a, /* => 777.00 */
+/* 832.00 => 107 */ 0x6b, /* => 810.00 */
+/* 864.00 => 108 */ 0x6c, /* => 843.00 */
+/* 896.00 => 109 */ 0x6d, /* => 879.00 */
+/* 928.00 => 110 */ 0x6e, /* => 916.00 */
+/* 960.00 => 111 */ 0x6f, /* => 954.00 */
+/* 992.00 => 111 */ 0x6f, /* => 954.00 */
+/* 1024.00 => 112 */ 0x70, /* => 994.00 */
+/* 1088.00 => 114 */ 0x72, /* => 1080.00 */
+/* 1152.00 => 115 */ 0x73, /* => 1124.00 */
+/* 1216.00 => 116 */ 0x74, /* => 1172.00 */
+/* 1280.00 => 118 */ 0x76, /* => 1272.00 */
+/* 1344.00 => 119 */ 0x77, /* => 1326.00 */
+/* 1408.00 => 120 */ 0x78, /* => 1382.00 */
+/* 1472.00 => 121 */ 0x79, /* => 1440.00 */
+/* 1536.00 => 122 */ 0x7a, /* => 1498.00 */
+/* 1600.00 => 123 */ 0x7b, /* => 1562.00 */
+/* 1664.00 => 124 */ 0x7c, /* => 1628.00 */
+/* 1728.00 => 125 */ 0x7d, /* => 1696.00 */
+/* 1792.00 => 126 */ 0x7e, /* => 1768.00 */
+/* 1856.00 => 127 */ 0x7f, /* => 1842.00 */
+/* 1920.00 => 128 */ 0x80, /* => 1918.00 */
+/* 1984.00 => 128 */ 0x80, /* => 1918.00 */
+/* 2048.00 => 129 */ 0x81, /* => 2000.00 */
+/* 2176.00 => 131 */ 0x83, /* => 2168.00 */
+/* 2304.00 => 132 */ 0x84, /* => 2264.00 */
+/* 2432.00 => 133 */ 0x85, /* => 2356.00 */
+/* 2560.00 => 135 */ 0x87, /* => 2556.00 */
+/* 2688.00 => 136 */ 0x88, /* => 2664.00 */
+/* 2816.00 => 137 */ 0x89, /* => 2776.00 */
+/* 2944.00 => 138 */ 0x8a, /* => 2892.00 */
+/* 3072.00 => 139 */ 0x8b, /* => 3012.00 */
+/* 3200.00 => 140 */ 0x8c, /* => 3140.00 */
+/* 3328.00 => 141 */ 0x8d, /* => 3272.00 */
+/* 3456.00 => 142 */ 0x8e, /* => 3412.00 */
+/* 3584.00 => 143 */ 0x8f, /* => 3552.00 */
+/* 3712.00 => 144 */ 0x90, /* => 3700.00 */
+/* 3840.00 => 144 */ 0x90, /* => 3700.00 */
+/* 3968.00 => 145 */ 0x91, /* => 3860.00 */
+/* 4096.00 => 146 */ 0x92, /* => 4016.00 */
+/* 4352.00 => 147 */ 0x93, /* => 4184.00 */
+/* 4608.00 => 149 */ 0x95, /* => 4544.00 */
+/* 4864.00 => 150 */ 0x96, /* => 4736.00 */
+/* 5120.00 => 151 */ 0x97, /* => 4936.00 */
+/* 5376.00 => 153 */ 0x99, /* => 5360.00 */
+/* 5632.00 => 154 */ 0x9a, /* => 5584.00 */
+/* 5888.00 => 155 */ 0x9b, /* => 5816.00 */
+/* 6144.00 => 156 */ 0x9c, /* => 6056.00 */
+/* 6400.00 => 157 */ 0x9d, /* => 6312.00 */
+/* 6656.00 => 158 */ 0x9e, /* => 6576.00 */
+/* 6912.00 => 159 */ 0x9f, /* => 6856.00 */
+/* 7168.00 => 160 */ 0xa0, /* => 7144.00 */
+/* 7424.00 => 160 */ 0xa0, /* => 7144.00 */
+/* 7680.00 => 161 */ 0xa1, /* => 7440.00 */
+/* 7936.00 => 162 */ 0xa2, /* => 7752.00 */
+/* 8192.00 => 163 */ 0xa3, /* => 8080.00 */
+/* 8704.00 => 164 */ 0xa4, /* => 8416.00 */
+/* 9216.00 => 166 */ 0xa6, /* => 9136.00 */
+/* 9728.00 => 167 */ 0xa7, /* => 9520.00 */
+/* 10240.00 => 168 */ 0xa8, /* => 9920.00 */
+/* 10752.00 => 169 */ 0xa9, /* => 10336.00 */
+/* 11264.00 => 171 */ 0xab, /* => 11216.00 */
+/* 11776.00 => 172 */ 0xac, /* => 11680.00 */
+/* 12288.00 => 173 */ 0xad, /* => 12176.00 */
+/* 12800.00 => 174 */ 0xae, /* => 12688.00 */
+/* 13312.00 => 175 */ 0xaf, /* => 13216.00 */
+/* 13824.00 => 176 */ 0xb0, /* => 13776.00 */
+/* 14336.00 => 176 */ 0xb0, /* => 13776.00 */
+/* 14848.00 => 177 */ 0xb1, /* => 14352.00 */
+/* 15360.00 => 178 */ 0xb2, /* => 14960.00 */
+/* 15872.00 => 179 */ 0xb3, /* => 15584.00 */
+/* 16384.00 => 180 */ 0xb4, /* => 16224.00 */
+/* 17408.00 => 181 */ 0xb5, /* => 16896.00 */
+/* 18432.00 => 183 */ 0xb7, /* => 18368.00 */
+/* 19456.00 => 184 */ 0xb8, /* => 19136.00 */
+/* 20480.00 => 185 */ 0xb9, /* => 19904.00 */
+/* 21504.00 => 186 */ 0xba, /* => 20768.00 */
+/* 22528.00 => 187 */ 0xbb, /* => 21632.00 */
+/* 23552.00 => 189 */ 0xbd, /* => 23488.00 */
+/* 24576.00 => 190 */ 0xbe, /* => 24512.00 */
+/* 25600.00 => 191 */ 0xbf, /* => 25504.00 */
+/* 26624.00 => 192 */ 0xc0, /* => 26592.00 */
+/* 27648.00 => 192 */ 0xc0, /* => 26592.00 */
+/* 28672.00 => 193 */ 0xc1, /* => 27680.00 */
+/* 29696.00 => 194 */ 0xc2, /* => 28896.00 */
+/* 30720.00 => 195 */ 0xc3, /* => 30048.00 */
+/* 31744.00 => 196 */ 0xc4, /* => 31392.00 */
+/* 32768.00 => 197 */ 0xc5, /* => 32640.00 */
+/* 34816.00 => 198 */ 0xc6, /* => 33984.00 */
+/* 36864.00 => 199 */ 0xc7, /* => 35392.00 */
+/* 38912.00 => 201 */ 0xc9, /* => 38528.00 */
+/* 40960.00 => 202 */ 0xca, /* => 40064.00 */
+/* 43008.00 => 203 */ 0xcb, /* => 41856.00 */
+/* 45056.00 => 204 */ 0xcc, /* => 43584.00 */
+/* 47104.00 => 205 */ 0xcd, /* => 45376.00 */
+/* 49152.00 => 206 */ 0xce, /* => 47232.00 */
+/* 51200.00 => 207 */ 0xcf, /* => 49344.00 */
+/* 53248.00 => 208 */ 0xd0, /* => 51328.00 */
+/* 55296.00 => 209 */ 0xd1, /* => 53504.00 */
+/* 57344.00 => 210 */ 0xd2, /* => 55616.00 */
+/* 59392.00 => 211 */ 0xd3, /* => 58240.00 */
+/* 61440.00 => 212 */ 0xd4, /* => 60416.00 */
+/* 63488.00 => 213 */ 0xd5, /* => 63104.00 */
+/* 65536.00 => 213 */ 0xd5, /* => 63104.00 */
+/* 69632.00 => 215 */ 0xd7, /* => 68480.00 */
+/* 73728.00 => 216 */ 0xd8, /* => 71424.00 */
+/* 77824.00 => 218 */ 0xda, /* => 77312.00 */
+/* 81920.00 => 219 */ 0xdb, /* => 80640.00 */
+/* 86016.00 => 220 */ 0xdc, /* => 84224.00 */
+/* 90112.00 => 221 */ 0xdd, /* => 87552.00 */
+/* 94208.00 => 222 */ 0xde, /* => 91136.00 */
+/* 98304.00 => 223 */ 0xdf, /* => 94976.00 */
+/* 102400.00 => 224 */ 0xe0, /* => 99072.00 */
+/* 106496.00 => 225 */ 0xe1, /* => 103680.00 */
+/* 110592.00 => 226 */ 0xe2, /* => 107520.00 */
+/* 114688.00 => 227 */ 0xe3, /* => 111872.00 */
+/* 118784.00 => 228 */ 0xe4, /* => 117632.00 */
+/* 122880.00 => 229 */ 0xe5, /* => 121472.00 */
+/* 126976.00 => 229 */ 0xe5, /* => 121472.00 */
+/* 131072.00 => 230 */ 0xe6, /* => 126976.00 */
+/* 139264.00 => 232 */ 0xe8, /* => 137728.00 */
+/* 147456.00 => 233 */ 0xe9, /* => 144896.00 */
+/* 155648.00 => 234 */ 0xea, /* => 150528.00 */
+/* 163840.00 => 236 */ 0xec, /* => 163584.00 */
+/* 172032.00 => 237 */ 0xed, /* => 168448.00 */
+/* 180224.00 => 238 */ 0xee, /* => 176384.00 */
+/* 188416.00 => 239 */ 0xef, /* => 185088.00 */
+/* 196608.00 => 240 */ 0xf0, /* => 191488.00 */
+/* 204800.00 => 241 */ 0xf1, /* => 201728.00 */
+/* 212992.00 => 242 */ 0xf2, /* => 209152.00 */
+/* 221184.00 => 243 */ 0xf3, /* => 217344.00 */
+/* 229376.00 => 244 */ 0xf4, /* => 226048.00 */
+/* 237568.00 => 245 */ 0xf5, /* => 235264.00 */
+/* 245760.00 => 246 */ 0xf6, /* => 245504.00 */
+/* 253952.00 => 246 */ 0xf6, /* => 245504.00 */
+/* 262144.00 => 247 */ 0xf7, /* => 256768.00 */
+/* 278528.00 => 248 */ 0xf8, /* => 268800.00 */
+/* 294912.00 => 250 */ 0xfa, /* => 289792.00 */
+/* 311296.00 => 251 */ 0xfb, /* => 305152.00 */
+/* 327680.00 => 252 */ 0xfc, /* => 313856.00 */
+/* 344064.00 => 254 */ 0xfe, /* => 342016.00 */
+/* 360448.00 => 255 */ 0xff, /* => 352768.00 */
+/* 376832.00 => 255 */ 0xff, /* => 352768.00 */
+/* 393216.00 => 255 */ 0xff, /* => 352768.00 */
+/* 409600.00 => 255 */ 0xff, /* => 352768.00 */
+/* 425984.00 => 255 */ 0xff, /* => 352768.00 */
+/* 442368.00 => 255 */ 0xff, /* => 352768.00 */
+/* 458752.00 => 255 */ 0xff, /* => 352768.00 */
+/* 475136.00 => 255 */ 0xff, /* => 352768.00 */
+/* 491520.00 => 255 */ 0xff, /* => 352768.00 */
+/* 507904.00 => 255 */ 0xff, /* => 352768.00 */
+/* 524288.00 => 255 */ 0xff, /* => 352768.00 */
+/* 557056.00 => 255 */ 0xff, /* => 352768.00 */
+/* 589824.00 => 255 */ 0xff, /* => 352768.00 */
+/* 622592.00 => 255 */ 0xff, /* => 352768.00 */
+/* 655360.00 => 255 */ 0xff, /* => 352768.00 */
+/* 688128.00 => 255 */ 0xff, /* => 352768.00 */
+/* 720896.00 => 255 */ 0xff, /* => 352768.00 */
+/* 753664.00 => 255 */ 0xff, /* => 352768.00 */
+/* 786432.00 => 255 */ 0xff, /* => 352768.00 */
+/* 819200.00 => 255 */ 0xff, /* => 352768.00 */
+/* 851968.00 => 255 */ 0xff, /* => 352768.00 */
+/* 884736.00 => 255 */ 0xff, /* => 352768.00 */
+/* 917504.00 => 255 */ 0xff, /* => 352768.00 */
+/* 950272.00 => 255 */ 0xff, /* => 352768.00 */
+/* 983040.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1015808.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1048576.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1114112.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1179648.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1245184.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1310720.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1376256.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1441792.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1507328.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1572864.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1638400.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1703936.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1769472.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1835008.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1900544.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1966080.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2031616.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2097152.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2228224.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2359296.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2490368.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2621440.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2752512.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2883584.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3014656.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3145728.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3276800.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3407872.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3538944.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3670016.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3801088.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3932160.00 => 255 */ 0xff, /* => 352768.00 */
+/* 4063232.00 => 255 */ 0xff, /* => 352768.00 */
+/* 4194304.00 => 255 */ 0xff, /* => 352768.00 */
+/* 4456448.00 => 255 */ 0xff, /* => 352768.00 */
+/* 4718592.00 => 255 */ 0xff, /* => 352768.00 */
+/* 4980736.00 => 255 */ 0xff, /* => 352768.00 */
+/* 5242880.00 => 255 */ 0xff, /* => 352768.00 */
+/* 5505024.00 => 255 */ 0xff, /* => 352768.00 */
+/* 5767168.00 => 255 */ 0xff, /* => 352768.00 */
+/* 6029312.00 => 255 */ 0xff, /* => 352768.00 */
+/* 6291456.00 => 255 */ 0xff, /* => 352768.00 */
+/* 6553600.00 => 255 */ 0xff, /* => 352768.00 */
+/* 6815744.00 => 255 */ 0xff, /* => 352768.00 */
+/* 7077888.00 => 255 */ 0xff, /* => 352768.00 */
+/* 7340032.00 => 255 */ 0xff, /* => 352768.00 */
+/* 7602176.00 => 255 */ 0xff, /* => 352768.00 */
+/* 7864320.00 => 255 */ 0xff, /* => 352768.00 */
+/* 8126464.00 => 255 */ 0xff, /* => 352768.00 */
+/* 8388608.00 => 255 */ 0xff, /* => 352768.00 */
+/* 8912896.00 => 255 */ 0xff, /* => 352768.00 */
+/* 9437184.00 => 255 */ 0xff, /* => 352768.00 */
+/* 9961472.00 => 255 */ 0xff, /* => 352768.00 */
+/* 10485760.00 => 255 */ 0xff, /* => 352768.00 */
+/* 11010048.00 => 255 */ 0xff, /* => 352768.00 */
+/* 11534336.00 => 255 */ 0xff, /* => 352768.00 */
+/* 12058624.00 => 255 */ 0xff, /* => 352768.00 */
+/* 12582912.00 => 255 */ 0xff, /* => 352768.00 */
+/* 13107200.00 => 255 */ 0xff, /* => 352768.00 */
+/* 13631488.00 => 255 */ 0xff, /* => 352768.00 */
+/* 14155776.00 => 255 */ 0xff, /* => 352768.00 */
+/* 14680064.00 => 255 */ 0xff, /* => 352768.00 */
+/* 15204352.00 => 255 */ 0xff, /* => 352768.00 */
+/* 15728640.00 => 255 */ 0xff, /* => 352768.00 */
+/* 16252928.00 => 255 */ 0xff, /* => 352768.00 */
+/* 16777216.00 => 255 */ 0xff, /* => 352768.00 */
+/* 17825792.00 => 255 */ 0xff, /* => 352768.00 */
+/* 18874368.00 => 255 */ 0xff, /* => 352768.00 */
+/* 19922944.00 => 255 */ 0xff, /* => 352768.00 */
+/* 20971520.00 => 255 */ 0xff, /* => 352768.00 */
+/* 22020096.00 => 255 */ 0xff, /* => 352768.00 */
+/* 23068672.00 => 255 */ 0xff, /* => 352768.00 */
+/* 24117248.00 => 255 */ 0xff, /* => 352768.00 */
+/* 25165824.00 => 255 */ 0xff, /* => 352768.00 */
+/* 26214400.00 => 255 */ 0xff, /* => 352768.00 */
+/* 27262976.00 => 255 */ 0xff, /* => 352768.00 */
+/* 28311552.00 => 255 */ 0xff, /* => 352768.00 */
+/* 29360128.00 => 255 */ 0xff, /* => 352768.00 */
+/* 30408704.00 => 255 */ 0xff, /* => 352768.00 */
+/* 31457280.00 => 255 */ 0xff, /* => 352768.00 */
+/* 32505856.00 => 255 */ 0xff, /* => 352768.00 */
+/* 33554432.00 => 255 */ 0xff, /* => 352768.00 */
+/* 35651584.00 => 255 */ 0xff, /* => 352768.00 */
+/* 37748736.00 => 255 */ 0xff, /* => 352768.00 */
+/* 39845888.00 => 255 */ 0xff, /* => 352768.00 */
+/* 41943040.00 => 255 */ 0xff, /* => 352768.00 */
+/* 44040192.00 => 255 */ 0xff, /* => 352768.00 */
+/* 46137344.00 => 255 */ 0xff, /* => 352768.00 */
+/* 48234496.00 => 255 */ 0xff, /* => 352768.00 */
+/* 50331648.00 => 255 */ 0xff, /* => 352768.00 */
+/* 52428800.00 => 255 */ 0xff, /* => 352768.00 */
+/* 54525952.00 => 255 */ 0xff, /* => 352768.00 */
+/* 56623104.00 => 255 */ 0xff, /* => 352768.00 */
+/* 58720256.00 => 255 */ 0xff, /* => 352768.00 */
+/* 60817408.00 => 255 */ 0xff, /* => 352768.00 */
+/* 62914560.00 => 255 */ 0xff, /* => 352768.00 */
+/* 65011712.00 => 255 */ 0xff, /* => 352768.00 */
+/* 67108864.00 => 255 */ 0xff, /* => 352768.00 */
+/* 71303168.00 => 255 */ 0xff, /* => 352768.00 */
+/* 75497472.00 => 255 */ 0xff, /* => 352768.00 */
+/* 79691776.00 => 255 */ 0xff, /* => 352768.00 */
+/* 83886080.00 => 255 */ 0xff, /* => 352768.00 */
+/* 88080384.00 => 255 */ 0xff, /* => 352768.00 */
+/* 92274688.00 => 255 */ 0xff, /* => 352768.00 */
+/* 96468992.00 => 255 */ 0xff, /* => 352768.00 */
+/* 100663296.00 => 255 */ 0xff, /* => 352768.00 */
+/* 104857600.00 => 255 */ 0xff, /* => 352768.00 */
+/* 109051904.00 => 255 */ 0xff, /* => 352768.00 */
+/* 113246208.00 => 255 */ 0xff, /* => 352768.00 */
+/* 117440512.00 => 255 */ 0xff, /* => 352768.00 */
+/* 121634816.00 => 255 */ 0xff, /* => 352768.00 */
+/* 125829120.00 => 255 */ 0xff, /* => 352768.00 */
+/* 130023424.00 => 255 */ 0xff, /* => 352768.00 */
+/* 134217728.00 => 255 */ 0xff, /* => 352768.00 */
+/* 142606336.00 => 255 */ 0xff, /* => 352768.00 */
+/* 150994944.00 => 255 */ 0xff, /* => 352768.00 */
+/* 159383552.00 => 255 */ 0xff, /* => 352768.00 */
+/* 167772160.00 => 255 */ 0xff, /* => 352768.00 */
+/* 176160768.00 => 255 */ 0xff, /* => 352768.00 */
+/* 184549376.00 => 255 */ 0xff, /* => 352768.00 */
+/* 192937984.00 => 255 */ 0xff, /* => 352768.00 */
+/* 201326592.00 => 255 */ 0xff, /* => 352768.00 */
+/* 209715200.00 => 255 */ 0xff, /* => 352768.00 */
+/* 218103808.00 => 255 */ 0xff, /* => 352768.00 */
+/* 226492416.00 => 255 */ 0xff, /* => 352768.00 */
+/* 234881024.00 => 255 */ 0xff, /* => 352768.00 */
+/* 243269632.00 => 255 */ 0xff, /* => 352768.00 */
+/* 251658240.00 => 255 */ 0xff, /* => 352768.00 */
+/* 260046848.00 => 255 */ 0xff, /* => 352768.00 */
+/* 268435456.00 => 255 */ 0xff, /* => 352768.00 */
+/* 285212672.00 => 255 */ 0xff, /* => 352768.00 */
+/* 301989888.00 => 255 */ 0xff, /* => 352768.00 */
+/* 318767104.00 => 255 */ 0xff, /* => 352768.00 */
+/* 335544320.00 => 255 */ 0xff, /* => 352768.00 */
+/* 352321536.00 => 255 */ 0xff, /* => 352768.00 */
+/* 369098752.00 => 255 */ 0xff, /* => 352768.00 */
+/* 385875968.00 => 255 */ 0xff, /* => 352768.00 */
+/* 402653184.00 => 255 */ 0xff, /* => 352768.00 */
+/* 419430400.00 => 255 */ 0xff, /* => 352768.00 */
+/* 436207616.00 => 255 */ 0xff, /* => 352768.00 */
+/* 452984832.00 => 255 */ 0xff, /* => 352768.00 */
+/* 469762048.00 => 255 */ 0xff, /* => 352768.00 */
+/* 486539264.00 => 255 */ 0xff, /* => 352768.00 */
+/* 503316480.00 => 255 */ 0xff, /* => 352768.00 */
+/* 520093696.00 => 255 */ 0xff, /* => 352768.00 */
+/* 536870912.00 => 255 */ 0xff, /* => 352768.00 */
+/* 570425344.00 => 255 */ 0xff, /* => 352768.00 */
+/* 603979776.00 => 255 */ 0xff, /* => 352768.00 */
+/* 637534208.00 => 255 */ 0xff, /* => 352768.00 */
+/* 671088640.00 => 255 */ 0xff, /* => 352768.00 */
+/* 704643072.00 => 255 */ 0xff, /* => 352768.00 */
+/* 738197504.00 => 255 */ 0xff, /* => 352768.00 */
+/* 771751936.00 => 255 */ 0xff, /* => 352768.00 */
+/* 805306368.00 => 255 */ 0xff, /* => 352768.00 */
+/* 838860800.00 => 255 */ 0xff, /* => 352768.00 */
+/* 872415232.00 => 255 */ 0xff, /* => 352768.00 */
+/* 905969664.00 => 255 */ 0xff, /* => 352768.00 */
+/* 939524096.00 => 255 */ 0xff, /* => 352768.00 */
+/* 973078528.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1006632960.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1040187392.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1073741824.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1140850688.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1207959552.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1275068416.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1342177280.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1409286144.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1476395008.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1543503872.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1610612736.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1677721600.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1744830464.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1811939328.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1879048192.00 => 255 */ 0xff, /* => 352768.00 */
+/* 1946157056.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2013265920.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2080374784.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2147483648.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2281701376.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2415919104.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2550136832.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2684354560.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2818572288.00 => 255 */ 0xff, /* => 352768.00 */
+/* 2952790016.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3087007744.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3221225472.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3355443200.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3489660928.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3623878656.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3758096384.00 => 255 */ 0xff, /* => 352768.00 */
+/* 3892314112.00 => 255 */ 0xff, /* => 352768.00 */
+/* 4026531840.00 => 255 */ 0xff, /* => 352768.00 */
+/* 4160749568.00 => 255 */ 0xff, /* => 352768.00 */
+};
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
new file mode 100644
index 000000000..924f8e267
--- /dev/null
+++ b/drivers/atm/iphase.c
@@ -0,0 +1,3298 @@
+/******************************************************************************
+ iphase.c: Device driver for Interphase ATM PCI adapter cards
+ Author: Peter Wang <pwang@iphase.com>
+ Some fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ Interphase Corporation <www.iphase.com>
+ Version: 1.0
+*******************************************************************************
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on this skeleton fall under the GPL and must retain
+ the authorship (implicit copyright) notice.
+
+ 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.
+
+ Modified from an incomplete driver for Interphase 5575 1KVC 1M card which
+ was originally written by Monalisa Agrawal at UNH. Now this driver
+ supports a variety of varients of Interphase ATM PCI (i)Chip adapter
+ card family (See www.iphase.com/products/ClassSheet.cfm?ClassID=ATM)
+ in terms of PHY type, the size of control memory and the size of
+ packet memory. The followings are the change log and history:
+
+ Bugfix the Mona's UBR driver.
+ Modify the basic memory allocation and dma logic.
+ Port the driver to the latest kernel from 2.0.46.
+ Complete the ABR logic of the driver, and added the ABR work-
+ around for the hardware anormalies.
+ Add the CBR support.
+ Add the flow control logic to the driver to allow rate-limit VC.
+ Add 4K VC support to the board with 512K control memory.
+ Add the support of all the variants of the Interphase ATM PCI
+ (i)Chip adapter cards including x575 (155M OC3 and UTP155), x525
+ (25M UTP25) and x531 (DS3 and E3).
+ Add SMP support.
+
+ Support and updates available at: ftp://ftp.iphase.com/pub/atm
+
+*******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/skbuff.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/uio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+#include <asm/uaccess.h>
+#include <asm/string.h>
+#include <asm/byteorder.h>
+#include <linux/vmalloc.h>
+#include <linux/jiffies.h>
+#include "iphase.h"
+#include "suni.h"
+#define swap_byte_order(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8))
+
+#define PRIV(dev) ((struct suni_priv *) dev->phy_data)
+
+static unsigned char ia_phy_get(struct atm_dev *dev, unsigned long addr);
+static void desc_dbg(IADEV *iadev);
+
+static IADEV *ia_dev[8];
+static struct atm_dev *_ia_dev[8];
+static int iadev_count;
+static void ia_led_timer(unsigned long arg);
+static DEFINE_TIMER(ia_timer, ia_led_timer, 0, 0);
+static int IA_TX_BUF = DFL_TX_BUFFERS, IA_TX_BUF_SZ = DFL_TX_BUF_SZ;
+static int IA_RX_BUF = DFL_RX_BUFFERS, IA_RX_BUF_SZ = DFL_RX_BUF_SZ;
+static uint IADebugFlag = /* IF_IADBG_ERR | IF_IADBG_CBR| IF_IADBG_INIT_ADAPTER
+ |IF_IADBG_ABR | IF_IADBG_EVENT*/ 0;
+
+module_param(IA_TX_BUF, int, 0);
+module_param(IA_TX_BUF_SZ, int, 0);
+module_param(IA_RX_BUF, int, 0);
+module_param(IA_RX_BUF_SZ, int, 0);
+module_param(IADebugFlag, uint, 0644);
+
+MODULE_LICENSE("GPL");
+
+/**************************** IA_LIB **********************************/
+
+static void ia_init_rtn_q (IARTN_Q *que)
+{
+ que->next = NULL;
+ que->tail = NULL;
+}
+
+static void ia_enque_head_rtn_q (IARTN_Q *que, IARTN_Q * data)
+{
+ data->next = NULL;
+ if (que->next == NULL)
+ que->next = que->tail = data;
+ else {
+ data->next = que->next;
+ que->next = data;
+ }
+ return;
+}
+
+static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) {
+ IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) return -1;
+ entry->data = data;
+ entry->next = NULL;
+ if (que->next == NULL)
+ que->next = que->tail = entry;
+ else {
+ que->tail->next = entry;
+ que->tail = que->tail->next;
+ }
+ return 1;
+}
+
+static IARTN_Q * ia_deque_rtn_q (IARTN_Q *que) {
+ IARTN_Q *tmpdata;
+ if (que->next == NULL)
+ return NULL;
+ tmpdata = que->next;
+ if ( que->next == que->tail)
+ que->next = que->tail = NULL;
+ else
+ que->next = que->next->next;
+ return tmpdata;
+}
+
+static void ia_hack_tcq(IADEV *dev) {
+
+ u_short desc1;
+ u_short tcq_wr;
+ struct ia_vcc *iavcc_r = NULL;
+
+ tcq_wr = readl(dev->seg_reg+TCQ_WR_PTR) & 0xffff;
+ while (dev->host_tcq_wr != tcq_wr) {
+ desc1 = *(u_short *)(dev->seg_ram + dev->host_tcq_wr);
+ if (!desc1) ;
+ else if (!dev->desc_tbl[desc1 -1].timestamp) {
+ IF_ABR(printk(" Desc %d is reset at %ld\n", desc1 -1, jiffies);)
+ *(u_short *) (dev->seg_ram + dev->host_tcq_wr) = 0;
+ }
+ else if (dev->desc_tbl[desc1 -1].timestamp) {
+ if (!(iavcc_r = dev->desc_tbl[desc1 -1].iavcc)) {
+ printk("IA: Fatal err in get_desc\n");
+ continue;
+ }
+ iavcc_r->vc_desc_cnt--;
+ dev->desc_tbl[desc1 -1].timestamp = 0;
+ IF_EVENT(printk("ia_hack: return_q skb = 0x%p desc = %d\n",
+ dev->desc_tbl[desc1 -1].txskb, desc1);)
+ if (iavcc_r->pcr < dev->rate_limit) {
+ IA_SKB_STATE (dev->desc_tbl[desc1-1].txskb) |= IA_TX_DONE;
+ if (ia_enque_rtn_q(&dev->tx_return_q, dev->desc_tbl[desc1 -1]) < 0)
+ printk("ia_hack_tcq: No memory available\n");
+ }
+ dev->desc_tbl[desc1 -1].iavcc = NULL;
+ dev->desc_tbl[desc1 -1].txskb = NULL;
+ }
+ dev->host_tcq_wr += 2;
+ if (dev->host_tcq_wr > dev->ffL.tcq_ed)
+ dev->host_tcq_wr = dev->ffL.tcq_st;
+ }
+} /* ia_hack_tcq */
+
+static u16 get_desc (IADEV *dev, struct ia_vcc *iavcc) {
+ u_short desc_num, i;
+ struct sk_buff *skb;
+ struct ia_vcc *iavcc_r = NULL;
+ unsigned long delta;
+ static unsigned long timer = 0;
+ int ltimeout;
+
+ ia_hack_tcq (dev);
+ if((time_after(jiffies,timer+50)) || ((dev->ffL.tcq_rd==dev->host_tcq_wr))) {
+ timer = jiffies;
+ i=0;
+ while (i < dev->num_tx_desc) {
+ if (!dev->desc_tbl[i].timestamp) {
+ i++;
+ continue;
+ }
+ ltimeout = dev->desc_tbl[i].iavcc->ltimeout;
+ delta = jiffies - dev->desc_tbl[i].timestamp;
+ if (delta >= ltimeout) {
+ IF_ABR(printk("RECOVER run!! desc_tbl %d = %d delta = %ld, time = %ld\n", i,dev->desc_tbl[i].timestamp, delta, jiffies);)
+ if (dev->ffL.tcq_rd == dev->ffL.tcq_st)
+ dev->ffL.tcq_rd = dev->ffL.tcq_ed;
+ else
+ dev->ffL.tcq_rd -= 2;
+ *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd) = i+1;
+ if (!(skb = dev->desc_tbl[i].txskb) ||
+ !(iavcc_r = dev->desc_tbl[i].iavcc))
+ printk("Fatal err, desc table vcc or skb is NULL\n");
+ else
+ iavcc_r->vc_desc_cnt--;
+ dev->desc_tbl[i].timestamp = 0;
+ dev->desc_tbl[i].iavcc = NULL;
+ dev->desc_tbl[i].txskb = NULL;
+ }
+ i++;
+ } /* while */
+ }
+ if (dev->ffL.tcq_rd == dev->host_tcq_wr)
+ return 0xFFFF;
+
+ /* Get the next available descriptor number from TCQ */
+ desc_num = *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd);
+
+ while (!desc_num || (dev->desc_tbl[desc_num -1]).timestamp) {
+ dev->ffL.tcq_rd += 2;
+ if (dev->ffL.tcq_rd > dev->ffL.tcq_ed)
+ dev->ffL.tcq_rd = dev->ffL.tcq_st;
+ if (dev->ffL.tcq_rd == dev->host_tcq_wr)
+ return 0xFFFF;
+ desc_num = *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd);
+ }
+
+ /* get system time */
+ dev->desc_tbl[desc_num -1].timestamp = jiffies;
+ return desc_num;
+}
+
+static void clear_lockup (struct atm_vcc *vcc, IADEV *dev) {
+ u_char foundLockUp;
+ vcstatus_t *vcstatus;
+ u_short *shd_tbl;
+ u_short tempCellSlot, tempFract;
+ struct main_vc *abr_vc = (struct main_vc *)dev->MAIN_VC_TABLE_ADDR;
+ struct ext_vc *eabr_vc = (struct ext_vc *)dev->EXT_VC_TABLE_ADDR;
+ u_int i;
+
+ if (vcc->qos.txtp.traffic_class == ATM_ABR) {
+ vcstatus = (vcstatus_t *) &(dev->testTable[vcc->vci]->vc_status);
+ vcstatus->cnt++;
+ foundLockUp = 0;
+ if( vcstatus->cnt == 0x05 ) {
+ abr_vc += vcc->vci;
+ eabr_vc += vcc->vci;
+ if( eabr_vc->last_desc ) {
+ if( (abr_vc->status & 0x07) == ABR_STATE /* 0x2 */ ) {
+ /* Wait for 10 Micro sec */
+ udelay(10);
+ if ((eabr_vc->last_desc)&&((abr_vc->status & 0x07)==ABR_STATE))
+ foundLockUp = 1;
+ }
+ else {
+ tempCellSlot = abr_vc->last_cell_slot;
+ tempFract = abr_vc->fraction;
+ if((tempCellSlot == dev->testTable[vcc->vci]->lastTime)
+ && (tempFract == dev->testTable[vcc->vci]->fract))
+ foundLockUp = 1;
+ dev->testTable[vcc->vci]->lastTime = tempCellSlot;
+ dev->testTable[vcc->vci]->fract = tempFract;
+ }
+ } /* last descriptor */
+ vcstatus->cnt = 0;
+ } /* vcstatus->cnt */
+
+ if (foundLockUp) {
+ IF_ABR(printk("LOCK UP found\n");)
+ writew(0xFFFD, dev->seg_reg+MODE_REG_0);
+ /* Wait for 10 Micro sec */
+ udelay(10);
+ abr_vc->status &= 0xFFF8;
+ abr_vc->status |= 0x0001; /* state is idle */
+ shd_tbl = (u_short *)dev->ABR_SCHED_TABLE_ADDR;
+ for( i = 0; ((i < dev->num_vc) && (shd_tbl[i])); i++ );
+ if (i < dev->num_vc)
+ shd_tbl[i] = vcc->vci;
+ else
+ IF_ERR(printk("ABR Seg. may not continue on VC %x\n",vcc->vci);)
+ writew(T_ONLINE, dev->seg_reg+MODE_REG_0);
+ writew(~(TRANSMIT_DONE|TCQ_NOT_EMPTY), dev->seg_reg+SEG_MASK_REG);
+ writew(TRANSMIT_DONE, dev->seg_reg+SEG_INTR_STATUS_REG);
+ vcstatus->cnt = 0;
+ } /* foundLockUp */
+
+ } /* if an ABR VC */
+
+
+}
+
+/*
+** Conversion of 24-bit cellrate (cells/sec) to 16-bit floating point format.
+**
+** +----+----+------------------+-------------------------------+
+** | R | NZ | 5-bit exponent | 9-bit mantissa |
+** +----+----+------------------+-------------------------------+
+**
+** R = reserved (written as 0)
+** NZ = 0 if 0 cells/sec; 1 otherwise
+**
+** if NZ = 1, rate = 1.mmmmmmmmm x 2^(eeeee) cells/sec
+*/
+static u16
+cellrate_to_float(u32 cr)
+{
+
+#define NZ 0x4000
+#define M_BITS 9 /* Number of bits in mantissa */
+#define E_BITS 5 /* Number of bits in exponent */
+#define M_MASK 0x1ff
+#define E_MASK 0x1f
+ u16 flot;
+ u32 tmp = cr & 0x00ffffff;
+ int i = 0;
+ if (cr == 0)
+ return 0;
+ while (tmp != 1) {
+ tmp >>= 1;
+ i++;
+ }
+ if (i == M_BITS)
+ flot = NZ | (i << M_BITS) | (cr & M_MASK);
+ else if (i < M_BITS)
+ flot = NZ | (i << M_BITS) | ((cr << (M_BITS - i)) & M_MASK);
+ else
+ flot = NZ | (i << M_BITS) | ((cr >> (i - M_BITS)) & M_MASK);
+ return flot;
+}
+
+#if 0
+/*
+** Conversion of 16-bit floating point format to 24-bit cellrate (cells/sec).
+*/
+static u32
+float_to_cellrate(u16 rate)
+{
+ u32 exp, mantissa, cps;
+ if ((rate & NZ) == 0)
+ return 0;
+ exp = (rate >> M_BITS) & E_MASK;
+ mantissa = rate & M_MASK;
+ if (exp == 0)
+ return 1;
+ cps = (1 << M_BITS) | mantissa;
+ if (exp == M_BITS)
+ cps = cps;
+ else if (exp > M_BITS)
+ cps <<= (exp - M_BITS);
+ else
+ cps >>= (M_BITS - exp);
+ return cps;
+}
+#endif
+
+static void init_abr_vc (IADEV *dev, srv_cls_param_t *srv_p) {
+ srv_p->class_type = ATM_ABR;
+ srv_p->pcr = dev->LineRate;
+ srv_p->mcr = 0;
+ srv_p->icr = 0x055cb7;
+ srv_p->tbe = 0xffffff;
+ srv_p->frtt = 0x3a;
+ srv_p->rif = 0xf;
+ srv_p->rdf = 0xb;
+ srv_p->nrm = 0x4;
+ srv_p->trm = 0x7;
+ srv_p->cdf = 0x3;
+ srv_p->adtf = 50;
+}
+
+static int
+ia_open_abr_vc(IADEV *dev, srv_cls_param_t *srv_p,
+ struct atm_vcc *vcc, u8 flag)
+{
+ f_vc_abr_entry *f_abr_vc;
+ r_vc_abr_entry *r_abr_vc;
+ u32 icr;
+ u8 trm, nrm, crm;
+ u16 adtf, air, *ptr16;
+ f_abr_vc =(f_vc_abr_entry *)dev->MAIN_VC_TABLE_ADDR;
+ f_abr_vc += vcc->vci;
+ switch (flag) {
+ case 1: /* FFRED initialization */
+#if 0 /* sanity check */
+ if (srv_p->pcr == 0)
+ return INVALID_PCR;
+ if (srv_p->pcr > dev->LineRate)
+ srv_p->pcr = dev->LineRate;
+ if ((srv_p->mcr + dev->sum_mcr) > dev->LineRate)
+ return MCR_UNAVAILABLE;
+ if (srv_p->mcr > srv_p->pcr)
+ return INVALID_MCR;
+ if (!(srv_p->icr))
+ srv_p->icr = srv_p->pcr;
+ if ((srv_p->icr < srv_p->mcr) || (srv_p->icr > srv_p->pcr))
+ return INVALID_ICR;
+ if ((srv_p->tbe < MIN_TBE) || (srv_p->tbe > MAX_TBE))
+ return INVALID_TBE;
+ if ((srv_p->frtt < MIN_FRTT) || (srv_p->frtt > MAX_FRTT))
+ return INVALID_FRTT;
+ if (srv_p->nrm > MAX_NRM)
+ return INVALID_NRM;
+ if (srv_p->trm > MAX_TRM)
+ return INVALID_TRM;
+ if (srv_p->adtf > MAX_ADTF)
+ return INVALID_ADTF;
+ else if (srv_p->adtf == 0)
+ srv_p->adtf = 1;
+ if (srv_p->cdf > MAX_CDF)
+ return INVALID_CDF;
+ if (srv_p->rif > MAX_RIF)
+ return INVALID_RIF;
+ if (srv_p->rdf > MAX_RDF)
+ return INVALID_RDF;
+#endif
+ memset ((caddr_t)f_abr_vc, 0, sizeof(*f_abr_vc));
+ f_abr_vc->f_vc_type = ABR;
+ nrm = 2 << srv_p->nrm; /* (2 ** (srv_p->nrm +1)) */
+ /* i.e 2**n = 2 << (n-1) */
+ f_abr_vc->f_nrm = nrm << 8 | nrm;
+ trm = 100000/(2 << (16 - srv_p->trm));
+ if ( trm == 0) trm = 1;
+ f_abr_vc->f_nrmexp =(((srv_p->nrm +1) & 0x0f) << 12)|(MRM << 8) | trm;
+ crm = srv_p->tbe / nrm;
+ if (crm == 0) crm = 1;
+ f_abr_vc->f_crm = crm & 0xff;
+ f_abr_vc->f_pcr = cellrate_to_float(srv_p->pcr);
+ icr = min( srv_p->icr, (srv_p->tbe > srv_p->frtt) ?
+ ((srv_p->tbe/srv_p->frtt)*1000000) :
+ (1000000/(srv_p->frtt/srv_p->tbe)));
+ f_abr_vc->f_icr = cellrate_to_float(icr);
+ adtf = (10000 * srv_p->adtf)/8192;
+ if (adtf == 0) adtf = 1;
+ f_abr_vc->f_cdf = ((7 - srv_p->cdf) << 12 | adtf) & 0xfff;
+ f_abr_vc->f_mcr = cellrate_to_float(srv_p->mcr);
+ f_abr_vc->f_acr = f_abr_vc->f_icr;
+ f_abr_vc->f_status = 0x0042;
+ break;
+ case 0: /* RFRED initialization */
+ ptr16 = (u_short *)(dev->reass_ram + REASS_TABLE*dev->memSize);
+ *(ptr16 + vcc->vci) = NO_AAL5_PKT | REASS_ABR;
+ r_abr_vc = (r_vc_abr_entry*)(dev->reass_ram+ABR_VC_TABLE*dev->memSize);
+ r_abr_vc += vcc->vci;
+ r_abr_vc->r_status_rdf = (15 - srv_p->rdf) & 0x000f;
+ air = srv_p->pcr << (15 - srv_p->rif);
+ if (air == 0) air = 1;
+ r_abr_vc->r_air = cellrate_to_float(air);
+ dev->testTable[vcc->vci]->vc_status = VC_ACTIVE | VC_ABR;
+ dev->sum_mcr += srv_p->mcr;
+ dev->n_abr++;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+static int ia_cbr_setup (IADEV *dev, struct atm_vcc *vcc) {
+ u32 rateLow=0, rateHigh, rate;
+ int entries;
+ struct ia_vcc *ia_vcc;
+
+ int idealSlot =0, testSlot, toBeAssigned, inc;
+ u32 spacing;
+ u16 *SchedTbl, *TstSchedTbl;
+ u16 cbrVC, vcIndex;
+ u32 fracSlot = 0;
+ u32 sp_mod = 0;
+ u32 sp_mod2 = 0;
+
+ /* IpAdjustTrafficParams */
+ if (vcc->qos.txtp.max_pcr <= 0) {
+ IF_ERR(printk("PCR for CBR not defined\n");)
+ return -1;
+ }
+ rate = vcc->qos.txtp.max_pcr;
+ entries = rate / dev->Granularity;
+ IF_CBR(printk("CBR: CBR entries=0x%x for rate=0x%x & Gran=0x%x\n",
+ entries, rate, dev->Granularity);)
+ if (entries < 1)
+ IF_CBR(printk("CBR: Bandwidth smaller than granularity of CBR table\n");)
+ rateLow = entries * dev->Granularity;
+ rateHigh = (entries + 1) * dev->Granularity;
+ if (3*(rate - rateLow) > (rateHigh - rate))
+ entries++;
+ if (entries > dev->CbrRemEntries) {
+ IF_CBR(printk("CBR: Not enough bandwidth to support this PCR.\n");)
+ IF_CBR(printk("Entries = 0x%x, CbrRemEntries = 0x%x.\n",
+ entries, dev->CbrRemEntries);)
+ return -EBUSY;
+ }
+
+ ia_vcc = INPH_IA_VCC(vcc);
+ ia_vcc->NumCbrEntry = entries;
+ dev->sum_mcr += entries * dev->Granularity;
+ /* IaFFrednInsertCbrSched */
+ // Starting at an arbitrary location, place the entries into the table
+ // as smoothly as possible
+ cbrVC = 0;
+ spacing = dev->CbrTotEntries / entries;
+ sp_mod = dev->CbrTotEntries % entries; // get modulo
+ toBeAssigned = entries;
+ fracSlot = 0;
+ vcIndex = vcc->vci;
+ IF_CBR(printk("Vci=0x%x,Spacing=0x%x,Sp_mod=0x%x\n",vcIndex,spacing,sp_mod);)
+ while (toBeAssigned)
+ {
+ // If this is the first time, start the table loading for this connection
+ // as close to entryPoint as possible.
+ if (toBeAssigned == entries)
+ {
+ idealSlot = dev->CbrEntryPt;
+ dev->CbrEntryPt += 2; // Adding 2 helps to prevent clumping
+ if (dev->CbrEntryPt >= dev->CbrTotEntries)
+ dev->CbrEntryPt -= dev->CbrTotEntries;// Wrap if necessary
+ } else {
+ idealSlot += (u32)(spacing + fracSlot); // Point to the next location
+ // in the table that would be smoothest
+ fracSlot = ((sp_mod + sp_mod2) / entries); // get new integer part
+ sp_mod2 = ((sp_mod + sp_mod2) % entries); // calc new fractional part
+ }
+ if (idealSlot >= (int)dev->CbrTotEntries)
+ idealSlot -= dev->CbrTotEntries;
+ // Continuously check around this ideal value until a null
+ // location is encountered.
+ SchedTbl = (u16*)(dev->seg_ram+CBR_SCHED_TABLE*dev->memSize);
+ inc = 0;
+ testSlot = idealSlot;
+ TstSchedTbl = (u16*)(SchedTbl+testSlot); //set index and read in value
+ IF_CBR(printk("CBR Testslot 0x%x AT Location 0x%p, NumToAssign=%d\n",
+ testSlot, TstSchedTbl,toBeAssigned);)
+ memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC));
+ while (cbrVC) // If another VC at this location, we have to keep looking
+ {
+ inc++;
+ testSlot = idealSlot - inc;
+ if (testSlot < 0) { // Wrap if necessary
+ testSlot += dev->CbrTotEntries;
+ IF_CBR(printk("Testslot Wrap. STable Start=0x%p,Testslot=%d\n",
+ SchedTbl,testSlot);)
+ }
+ TstSchedTbl = (u16 *)(SchedTbl + testSlot); // set table index
+ memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC));
+ if (!cbrVC)
+ break;
+ testSlot = idealSlot + inc;
+ if (testSlot >= (int)dev->CbrTotEntries) { // Wrap if necessary
+ testSlot -= dev->CbrTotEntries;
+ IF_CBR(printk("TotCbrEntries=%d",dev->CbrTotEntries);)
+ IF_CBR(printk(" Testslot=0x%x ToBeAssgned=%d\n",
+ testSlot, toBeAssigned);)
+ }
+ // set table index and read in value
+ TstSchedTbl = (u16*)(SchedTbl + testSlot);
+ IF_CBR(printk("Reading CBR Tbl from 0x%p, CbrVal=0x%x Iteration %d\n",
+ TstSchedTbl,cbrVC,inc);)
+ memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC));
+ } /* while */
+ // Move this VCI number into this location of the CBR Sched table.
+ memcpy((caddr_t)TstSchedTbl, (caddr_t)&vcIndex, sizeof(*TstSchedTbl));
+ dev->CbrRemEntries--;
+ toBeAssigned--;
+ } /* while */
+
+ /* IaFFrednCbrEnable */
+ dev->NumEnabledCBR++;
+ if (dev->NumEnabledCBR == 1) {
+ writew((CBR_EN | UBR_EN | ABR_EN | (0x23 << 2)), dev->seg_reg+STPARMS);
+ IF_CBR(printk("CBR is enabled\n");)
+ }
+ return 0;
+}
+static void ia_cbrVc_close (struct atm_vcc *vcc) {
+ IADEV *iadev;
+ u16 *SchedTbl, NullVci = 0;
+ u32 i, NumFound;
+
+ iadev = INPH_IA_DEV(vcc->dev);
+ iadev->NumEnabledCBR--;
+ SchedTbl = (u16*)(iadev->seg_ram+CBR_SCHED_TABLE*iadev->memSize);
+ if (iadev->NumEnabledCBR == 0) {
+ writew((UBR_EN | ABR_EN | (0x23 << 2)), iadev->seg_reg+STPARMS);
+ IF_CBR (printk("CBR support disabled\n");)
+ }
+ NumFound = 0;
+ for (i=0; i < iadev->CbrTotEntries; i++)
+ {
+ if (*SchedTbl == vcc->vci) {
+ iadev->CbrRemEntries++;
+ *SchedTbl = NullVci;
+ IF_CBR(NumFound++;)
+ }
+ SchedTbl++;
+ }
+ IF_CBR(printk("Exit ia_cbrVc_close, NumRemoved=%d\n",NumFound);)
+}
+
+static int ia_avail_descs(IADEV *iadev) {
+ int tmp = 0;
+ ia_hack_tcq(iadev);
+ if (iadev->host_tcq_wr >= iadev->ffL.tcq_rd)
+ tmp = (iadev->host_tcq_wr - iadev->ffL.tcq_rd) / 2;
+ else
+ tmp = (iadev->ffL.tcq_ed - iadev->ffL.tcq_rd + 2 + iadev->host_tcq_wr -
+ iadev->ffL.tcq_st) / 2;
+ return tmp;
+}
+
+static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb);
+
+static int ia_que_tx (IADEV *iadev) {
+ struct sk_buff *skb;
+ int num_desc;
+ struct atm_vcc *vcc;
+ num_desc = ia_avail_descs(iadev);
+
+ while (num_desc && (skb = skb_dequeue(&iadev->tx_backlog))) {
+ if (!(vcc = ATM_SKB(skb)->vcc)) {
+ dev_kfree_skb_any(skb);
+ printk("ia_que_tx: Null vcc\n");
+ break;
+ }
+ if (!test_bit(ATM_VF_READY,&vcc->flags)) {
+ dev_kfree_skb_any(skb);
+ printk("Free the SKB on closed vci %d \n", vcc->vci);
+ break;
+ }
+ if (ia_pkt_tx (vcc, skb)) {
+ skb_queue_head(&iadev->tx_backlog, skb);
+ }
+ num_desc--;
+ }
+ return 0;
+}
+
+static void ia_tx_poll (IADEV *iadev) {
+ struct atm_vcc *vcc = NULL;
+ struct sk_buff *skb = NULL, *skb1 = NULL;
+ struct ia_vcc *iavcc;
+ IARTN_Q * rtne;
+
+ ia_hack_tcq(iadev);
+ while ( (rtne = ia_deque_rtn_q(&iadev->tx_return_q))) {
+ skb = rtne->data.txskb;
+ if (!skb) {
+ printk("ia_tx_poll: skb is null\n");
+ goto out;
+ }
+ vcc = ATM_SKB(skb)->vcc;
+ if (!vcc) {
+ printk("ia_tx_poll: vcc is null\n");
+ dev_kfree_skb_any(skb);
+ goto out;
+ }
+
+ iavcc = INPH_IA_VCC(vcc);
+ if (!iavcc) {
+ printk("ia_tx_poll: iavcc is null\n");
+ dev_kfree_skb_any(skb);
+ goto out;
+ }
+
+ skb1 = skb_dequeue(&iavcc->txing_skb);
+ while (skb1 && (skb1 != skb)) {
+ if (!(IA_SKB_STATE(skb1) & IA_TX_DONE)) {
+ printk("IA_tx_intr: Vci %d lost pkt!!!\n", vcc->vci);
+ }
+ IF_ERR(printk("Release the SKB not match\n");)
+ if ((vcc->pop) && (skb1->len != 0))
+ {
+ vcc->pop(vcc, skb1);
+ IF_EVENT(printk("Tansmit Done - skb 0x%lx return\n",
+ (long)skb1);)
+ }
+ else
+ dev_kfree_skb_any(skb1);
+ skb1 = skb_dequeue(&iavcc->txing_skb);
+ }
+ if (!skb1) {
+ IF_EVENT(printk("IA: Vci %d - skb not found requed\n",vcc->vci);)
+ ia_enque_head_rtn_q (&iadev->tx_return_q, rtne);
+ break;
+ }
+ if ((vcc->pop) && (skb->len != 0))
+ {
+ vcc->pop(vcc, skb);
+ IF_EVENT(printk("Tx Done - skb 0x%lx return\n",(long)skb);)
+ }
+ else
+ dev_kfree_skb_any(skb);
+ kfree(rtne);
+ }
+ ia_que_tx(iadev);
+out:
+ return;
+}
+#if 0
+static void ia_eeprom_put (IADEV *iadev, u32 addr, u_short val)
+{
+ u32 t;
+ int i;
+ /*
+ * Issue a command to enable writes to the NOVRAM
+ */
+ NVRAM_CMD (EXTEND + EWEN);
+ NVRAM_CLR_CE;
+ /*
+ * issue the write command
+ */
+ NVRAM_CMD(IAWRITE + addr);
+ /*
+ * Send the data, starting with D15, then D14, and so on for 16 bits
+ */
+ for (i=15; i>=0; i--) {
+ NVRAM_CLKOUT (val & 0x8000);
+ val <<= 1;
+ }
+ NVRAM_CLR_CE;
+ CFG_OR(NVCE);
+ t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS);
+ while (!(t & NVDO))
+ t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS);
+
+ NVRAM_CLR_CE;
+ /*
+ * disable writes again
+ */
+ NVRAM_CMD(EXTEND + EWDS)
+ NVRAM_CLR_CE;
+ CFG_AND(~NVDI);
+}
+#endif
+
+static u16 ia_eeprom_get (IADEV *iadev, u32 addr)
+{
+ u_short val;
+ u32 t;
+ int i;
+ /*
+ * Read the first bit that was clocked with the falling edge of the
+ * the last command data clock
+ */
+ NVRAM_CMD(IAREAD + addr);
+ /*
+ * Now read the rest of the bits, the next bit read is D14, then D13,
+ * and so on.
+ */
+ val = 0;
+ for (i=15; i>=0; i--) {
+ NVRAM_CLKIN(t);
+ val |= (t << i);
+ }
+ NVRAM_CLR_CE;
+ CFG_AND(~NVDI);
+ return val;
+}
+
+static void ia_hw_type(IADEV *iadev) {
+ u_short memType = ia_eeprom_get(iadev, 25);
+ iadev->memType = memType;
+ if ((memType & MEM_SIZE_MASK) == MEM_SIZE_1M) {
+ iadev->num_tx_desc = IA_TX_BUF;
+ iadev->tx_buf_sz = IA_TX_BUF_SZ;
+ iadev->num_rx_desc = IA_RX_BUF;
+ iadev->rx_buf_sz = IA_RX_BUF_SZ;
+ } else if ((memType & MEM_SIZE_MASK) == MEM_SIZE_512K) {
+ if (IA_TX_BUF == DFL_TX_BUFFERS)
+ iadev->num_tx_desc = IA_TX_BUF / 2;
+ else
+ iadev->num_tx_desc = IA_TX_BUF;
+ iadev->tx_buf_sz = IA_TX_BUF_SZ;
+ if (IA_RX_BUF == DFL_RX_BUFFERS)
+ iadev->num_rx_desc = IA_RX_BUF / 2;
+ else
+ iadev->num_rx_desc = IA_RX_BUF;
+ iadev->rx_buf_sz = IA_RX_BUF_SZ;
+ }
+ else {
+ if (IA_TX_BUF == DFL_TX_BUFFERS)
+ iadev->num_tx_desc = IA_TX_BUF / 8;
+ else
+ iadev->num_tx_desc = IA_TX_BUF;
+ iadev->tx_buf_sz = IA_TX_BUF_SZ;
+ if (IA_RX_BUF == DFL_RX_BUFFERS)
+ iadev->num_rx_desc = IA_RX_BUF / 8;
+ else
+ iadev->num_rx_desc = IA_RX_BUF;
+ iadev->rx_buf_sz = IA_RX_BUF_SZ;
+ }
+ iadev->rx_pkt_ram = TX_PACKET_RAM + (iadev->num_tx_desc * iadev->tx_buf_sz);
+ IF_INIT(printk("BUF: tx=%d,sz=%d rx=%d sz= %d rx_pkt_ram=%d\n",
+ iadev->num_tx_desc, iadev->tx_buf_sz, iadev->num_rx_desc,
+ iadev->rx_buf_sz, iadev->rx_pkt_ram);)
+
+#if 0
+ if ((memType & FE_MASK) == FE_SINGLE_MODE) {
+ iadev->phy_type = PHY_OC3C_S;
+ else if ((memType & FE_MASK) == FE_UTP_OPTION)
+ iadev->phy_type = PHY_UTP155;
+ else
+ iadev->phy_type = PHY_OC3C_M;
+#endif
+
+ iadev->phy_type = memType & FE_MASK;
+ IF_INIT(printk("memType = 0x%x iadev->phy_type = 0x%x\n",
+ memType,iadev->phy_type);)
+ if (iadev->phy_type == FE_25MBIT_PHY)
+ iadev->LineRate = (u32)(((25600000/8)*26)/(27*53));
+ else if (iadev->phy_type == FE_DS3_PHY)
+ iadev->LineRate = (u32)(((44736000/8)*26)/(27*53));
+ else if (iadev->phy_type == FE_E3_PHY)
+ iadev->LineRate = (u32)(((34368000/8)*26)/(27*53));
+ else
+ iadev->LineRate = (u32)(ATM_OC3_PCR);
+ IF_INIT(printk("iadev->LineRate = %d \n", iadev->LineRate);)
+
+}
+
+static u32 ia_phy_read32(struct iadev_priv *ia, unsigned int reg)
+{
+ return readl(ia->phy + (reg >> 2));
+}
+
+static void ia_phy_write32(struct iadev_priv *ia, unsigned int reg, u32 val)
+{
+ writel(val, ia->phy + (reg >> 2));
+}
+
+static void ia_frontend_intr(struct iadev_priv *iadev)
+{
+ u32 status;
+
+ if (iadev->phy_type & FE_25MBIT_PHY) {
+ status = ia_phy_read32(iadev, MB25_INTR_STATUS);
+ iadev->carrier_detect = (status & MB25_IS_GSB) ? 1 : 0;
+ } else if (iadev->phy_type & FE_DS3_PHY) {
+ ia_phy_read32(iadev, SUNI_DS3_FRM_INTR_STAT);
+ status = ia_phy_read32(iadev, SUNI_DS3_FRM_STAT);
+ iadev->carrier_detect = (status & SUNI_DS3_LOSV) ? 0 : 1;
+ } else if (iadev->phy_type & FE_E3_PHY) {
+ ia_phy_read32(iadev, SUNI_E3_FRM_MAINT_INTR_IND);
+ status = ia_phy_read32(iadev, SUNI_E3_FRM_FRAM_INTR_IND_STAT);
+ iadev->carrier_detect = (status & SUNI_E3_LOS) ? 0 : 1;
+ } else {
+ status = ia_phy_read32(iadev, SUNI_RSOP_STATUS);
+ iadev->carrier_detect = (status & SUNI_LOSV) ? 0 : 1;
+ }
+
+ printk(KERN_INFO "IA: SUNI carrier %s\n",
+ iadev->carrier_detect ? "detected" : "lost signal");
+}
+
+static void ia_mb25_init(struct iadev_priv *iadev)
+{
+#if 0
+ mb25->mb25_master_ctrl = MB25_MC_DRIC | MB25_MC_DREC | MB25_MC_ENABLED;
+#endif
+ ia_phy_write32(iadev, MB25_MASTER_CTRL, MB25_MC_DRIC | MB25_MC_DREC);
+ ia_phy_write32(iadev, MB25_DIAG_CONTROL, 0);
+
+ iadev->carrier_detect =
+ (ia_phy_read32(iadev, MB25_INTR_STATUS) & MB25_IS_GSB) ? 1 : 0;
+}
+
+struct ia_reg {
+ u16 reg;
+ u16 val;
+};
+
+static void ia_phy_write(struct iadev_priv *iadev,
+ const struct ia_reg *regs, int len)
+{
+ while (len--) {
+ ia_phy_write32(iadev, regs->reg, regs->val);
+ regs++;
+ }
+}
+
+static void ia_suni_pm7345_init_ds3(struct iadev_priv *iadev)
+{
+ static const struct ia_reg suni_ds3_init [] = {
+ { SUNI_DS3_FRM_INTR_ENBL, 0x17 },
+ { SUNI_DS3_FRM_CFG, 0x01 },
+ { SUNI_DS3_TRAN_CFG, 0x01 },
+ { SUNI_CONFIG, 0 },
+ { SUNI_SPLR_CFG, 0 },
+ { SUNI_SPLT_CFG, 0 }
+ };
+ u32 status;
+
+ status = ia_phy_read32(iadev, SUNI_DS3_FRM_STAT);
+ iadev->carrier_detect = (status & SUNI_DS3_LOSV) ? 0 : 1;
+
+ ia_phy_write(iadev, suni_ds3_init, ARRAY_SIZE(suni_ds3_init));
+}
+
+static void ia_suni_pm7345_init_e3(struct iadev_priv *iadev)
+{
+ static const struct ia_reg suni_e3_init [] = {
+ { SUNI_E3_FRM_FRAM_OPTIONS, 0x04 },
+ { SUNI_E3_FRM_MAINT_OPTIONS, 0x20 },
+ { SUNI_E3_FRM_FRAM_INTR_ENBL, 0x1d },
+ { SUNI_E3_FRM_MAINT_INTR_ENBL, 0x30 },
+ { SUNI_E3_TRAN_STAT_DIAG_OPTIONS, 0 },
+ { SUNI_E3_TRAN_FRAM_OPTIONS, 0x01 },
+ { SUNI_CONFIG, SUNI_PM7345_E3ENBL },
+ { SUNI_SPLR_CFG, 0x41 },
+ { SUNI_SPLT_CFG, 0x41 }
+ };
+ u32 status;
+
+ status = ia_phy_read32(iadev, SUNI_E3_FRM_FRAM_INTR_IND_STAT);
+ iadev->carrier_detect = (status & SUNI_E3_LOS) ? 0 : 1;
+ ia_phy_write(iadev, suni_e3_init, ARRAY_SIZE(suni_e3_init));
+}
+
+static void ia_suni_pm7345_init(struct iadev_priv *iadev)
+{
+ static const struct ia_reg suni_init [] = {
+ /* Enable RSOP loss of signal interrupt. */
+ { SUNI_INTR_ENBL, 0x28 },
+ /* Clear error counters. */
+ { SUNI_ID_RESET, 0 },
+ /* Clear "PMCTST" in master test register. */
+ { SUNI_MASTER_TEST, 0 },
+
+ { SUNI_RXCP_CTRL, 0x2c },
+ { SUNI_RXCP_FCTRL, 0x81 },
+
+ { SUNI_RXCP_IDLE_PAT_H1, 0 },
+ { SUNI_RXCP_IDLE_PAT_H2, 0 },
+ { SUNI_RXCP_IDLE_PAT_H3, 0 },
+ { SUNI_RXCP_IDLE_PAT_H4, 0x01 },
+
+ { SUNI_RXCP_IDLE_MASK_H1, 0xff },
+ { SUNI_RXCP_IDLE_MASK_H2, 0xff },
+ { SUNI_RXCP_IDLE_MASK_H3, 0xff },
+ { SUNI_RXCP_IDLE_MASK_H4, 0xfe },
+
+ { SUNI_RXCP_CELL_PAT_H1, 0 },
+ { SUNI_RXCP_CELL_PAT_H2, 0 },
+ { SUNI_RXCP_CELL_PAT_H3, 0 },
+ { SUNI_RXCP_CELL_PAT_H4, 0x01 },
+
+ { SUNI_RXCP_CELL_MASK_H1, 0xff },
+ { SUNI_RXCP_CELL_MASK_H2, 0xff },
+ { SUNI_RXCP_CELL_MASK_H3, 0xff },
+ { SUNI_RXCP_CELL_MASK_H4, 0xff },
+
+ { SUNI_TXCP_CTRL, 0xa4 },
+ { SUNI_TXCP_INTR_EN_STS, 0x10 },
+ { SUNI_TXCP_IDLE_PAT_H5, 0x55 }
+ };
+
+ if (iadev->phy_type & FE_DS3_PHY)
+ ia_suni_pm7345_init_ds3(iadev);
+ else
+ ia_suni_pm7345_init_e3(iadev);
+
+ ia_phy_write(iadev, suni_init, ARRAY_SIZE(suni_init));
+
+ ia_phy_write32(iadev, SUNI_CONFIG, ia_phy_read32(iadev, SUNI_CONFIG) &
+ ~(SUNI_PM7345_LLB | SUNI_PM7345_CLB |
+ SUNI_PM7345_DLB | SUNI_PM7345_PLB));
+#ifdef __SNMP__
+ suni_pm7345->suni_rxcp_intr_en_sts |= SUNI_OOCDE;
+#endif /* __SNMP__ */
+ return;
+}
+
+
+/***************************** IA_LIB END *****************************/
+
+#ifdef CONFIG_ATM_IA_DEBUG
+static int tcnter = 0;
+static void xdump( u_char* cp, int length, char* prefix )
+{
+ int col, count;
+ u_char prntBuf[120];
+ u_char* pBuf = prntBuf;
+ count = 0;
+ while(count < length){
+ pBuf += sprintf( pBuf, "%s", prefix );
+ for(col = 0;count + col < length && col < 16; col++){
+ if (col != 0 && (col % 4) == 0)
+ pBuf += sprintf( pBuf, " " );
+ pBuf += sprintf( pBuf, "%02X ", cp[count + col] );
+ }
+ while(col++ < 16){ /* pad end of buffer with blanks */
+ if ((col % 4) == 0)
+ sprintf( pBuf, " " );
+ pBuf += sprintf( pBuf, " " );
+ }
+ pBuf += sprintf( pBuf, " " );
+ for(col = 0;count + col < length && col < 16; col++){
+ if (isprint((int)cp[count + col]))
+ pBuf += sprintf( pBuf, "%c", cp[count + col] );
+ else
+ pBuf += sprintf( pBuf, "." );
+ }
+ printk("%s\n", prntBuf);
+ count += col;
+ pBuf = prntBuf;
+ }
+
+} /* close xdump(... */
+#endif /* CONFIG_ATM_IA_DEBUG */
+
+
+static struct atm_dev *ia_boards = NULL;
+
+#define ACTUAL_RAM_BASE \
+ RAM_BASE*((iadev->mem)/(128 * 1024))
+#define ACTUAL_SEG_RAM_BASE \
+ IPHASE5575_FRAG_CONTROL_RAM_BASE*((iadev->mem)/(128 * 1024))
+#define ACTUAL_REASS_RAM_BASE \
+ IPHASE5575_REASS_CONTROL_RAM_BASE*((iadev->mem)/(128 * 1024))
+
+
+/*-- some utilities and memory allocation stuff will come here -------------*/
+
+static void desc_dbg(IADEV *iadev) {
+
+ u_short tcq_wr_ptr, tcq_st_ptr, tcq_ed_ptr;
+ u32 i;
+ void __iomem *tmp;
+ // regval = readl((u32)ia_cmds->maddr);
+ tcq_wr_ptr = readw(iadev->seg_reg+TCQ_WR_PTR);
+ printk("B_tcq_wr = 0x%x desc = %d last desc = %d\n",
+ tcq_wr_ptr, readw(iadev->seg_ram+tcq_wr_ptr),
+ readw(iadev->seg_ram+tcq_wr_ptr-2));
+ printk(" host_tcq_wr = 0x%x host_tcq_rd = 0x%x \n", iadev->host_tcq_wr,
+ iadev->ffL.tcq_rd);
+ tcq_st_ptr = readw(iadev->seg_reg+TCQ_ST_ADR);
+ tcq_ed_ptr = readw(iadev->seg_reg+TCQ_ED_ADR);
+ printk("tcq_st_ptr = 0x%x tcq_ed_ptr = 0x%x \n", tcq_st_ptr, tcq_ed_ptr);
+ i = 0;
+ while (tcq_st_ptr != tcq_ed_ptr) {
+ tmp = iadev->seg_ram+tcq_st_ptr;
+ printk("TCQ slot %d desc = %d Addr = %p\n", i++, readw(tmp), tmp);
+ tcq_st_ptr += 2;
+ }
+ for(i=0; i <iadev->num_tx_desc; i++)
+ printk("Desc_tbl[%d] = %d \n", i, iadev->desc_tbl[i].timestamp);
+}
+
+
+/*----------------------------- Receiving side stuff --------------------------*/
+
+static void rx_excp_rcvd(struct atm_dev *dev)
+{
+#if 0 /* closing the receiving size will cause too many excp int */
+ IADEV *iadev;
+ u_short state;
+ u_short excpq_rd_ptr;
+ //u_short *ptr;
+ int vci, error = 1;
+ iadev = INPH_IA_DEV(dev);
+ state = readl(iadev->reass_reg + STATE_REG) & 0xffff;
+ while((state & EXCPQ_EMPTY) != EXCPQ_EMPTY)
+ { printk("state = %x \n", state);
+ excpq_rd_ptr = readw(iadev->reass_reg + EXCP_Q_RD_PTR) & 0xffff;
+ printk("state = %x excpq_rd_ptr = %x \n", state, excpq_rd_ptr);
+ if (excpq_rd_ptr == *(u16*)(iadev->reass_reg + EXCP_Q_WR_PTR))
+ IF_ERR(printk("excpq_rd_ptr is wrong!!!\n");)
+ // TODO: update exception stat
+ vci = readw(iadev->reass_ram+excpq_rd_ptr);
+ error = readw(iadev->reass_ram+excpq_rd_ptr+2) & 0x0007;
+ // pwang_test
+ excpq_rd_ptr += 4;
+ if (excpq_rd_ptr > (readw(iadev->reass_reg + EXCP_Q_ED_ADR)& 0xffff))
+ excpq_rd_ptr = readw(iadev->reass_reg + EXCP_Q_ST_ADR)& 0xffff;
+ writew( excpq_rd_ptr, iadev->reass_reg + EXCP_Q_RD_PTR);
+ state = readl(iadev->reass_reg + STATE_REG) & 0xffff;
+ }
+#endif
+}
+
+static void free_desc(struct atm_dev *dev, int desc)
+{
+ IADEV *iadev;
+ iadev = INPH_IA_DEV(dev);
+ writew(desc, iadev->reass_ram+iadev->rfL.fdq_wr);
+ iadev->rfL.fdq_wr +=2;
+ if (iadev->rfL.fdq_wr > iadev->rfL.fdq_ed)
+ iadev->rfL.fdq_wr = iadev->rfL.fdq_st;
+ writew(iadev->rfL.fdq_wr, iadev->reass_reg+FREEQ_WR_PTR);
+}
+
+
+static int rx_pkt(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ struct atm_vcc *vcc;
+ unsigned short status;
+ struct rx_buf_desc __iomem *buf_desc_ptr;
+ int desc;
+ struct dle* wr_ptr;
+ int len;
+ struct sk_buff *skb;
+ u_int buf_addr, dma_addr;
+
+ iadev = INPH_IA_DEV(dev);
+ if (iadev->rfL.pcq_rd == (readw(iadev->reass_reg+PCQ_WR_PTR)&0xffff))
+ {
+ printk(KERN_ERR DEV_LABEL "(itf %d) Receive queue empty\n", dev->number);
+ return -EINVAL;
+ }
+ /* mask 1st 3 bits to get the actual descno. */
+ desc = readw(iadev->reass_ram+iadev->rfL.pcq_rd) & 0x1fff;
+ IF_RX(printk("reass_ram = %p iadev->rfL.pcq_rd = 0x%x desc = %d\n",
+ iadev->reass_ram, iadev->rfL.pcq_rd, desc);
+ printk(" pcq_wr_ptr = 0x%x\n",
+ readw(iadev->reass_reg+PCQ_WR_PTR)&0xffff);)
+ /* update the read pointer - maybe we shud do this in the end*/
+ if ( iadev->rfL.pcq_rd== iadev->rfL.pcq_ed)
+ iadev->rfL.pcq_rd = iadev->rfL.pcq_st;
+ else
+ iadev->rfL.pcq_rd += 2;
+ writew(iadev->rfL.pcq_rd, iadev->reass_reg+PCQ_RD_PTR);
+
+ /* get the buffer desc entry.
+ update stuff. - doesn't seem to be any update necessary
+ */
+ buf_desc_ptr = iadev->RX_DESC_BASE_ADDR;
+ /* make the ptr point to the corresponding buffer desc entry */
+ buf_desc_ptr += desc;
+ if (!desc || (desc > iadev->num_rx_desc) ||
+ ((buf_desc_ptr->vc_index & 0xffff) > iadev->num_vc)) {
+ free_desc(dev, desc);
+ IF_ERR(printk("IA: bad descriptor desc = %d \n", desc);)
+ return -1;
+ }
+ vcc = iadev->rx_open[buf_desc_ptr->vc_index & 0xffff];
+ if (!vcc)
+ {
+ free_desc(dev, desc);
+ printk("IA: null vcc, drop PDU\n");
+ return -1;
+ }
+
+
+ /* might want to check the status bits for errors */
+ status = (u_short) (buf_desc_ptr->desc_mode);
+ if (status & (RX_CER | RX_PTE | RX_OFL))
+ {
+ atomic_inc(&vcc->stats->rx_err);
+ IF_ERR(printk("IA: bad packet, dropping it");)
+ if (status & RX_CER) {
+ IF_ERR(printk(" cause: packet CRC error\n");)
+ }
+ else if (status & RX_PTE) {
+ IF_ERR(printk(" cause: packet time out\n");)
+ }
+ else {
+ IF_ERR(printk(" cause: buffer overflow\n");)
+ }
+ goto out_free_desc;
+ }
+
+ /*
+ build DLE.
+ */
+
+ buf_addr = (buf_desc_ptr->buf_start_hi << 16) | buf_desc_ptr->buf_start_lo;
+ dma_addr = (buf_desc_ptr->dma_start_hi << 16) | buf_desc_ptr->dma_start_lo;
+ len = dma_addr - buf_addr;
+ if (len > iadev->rx_buf_sz) {
+ printk("Over %d bytes sdu received, dropped!!!\n", iadev->rx_buf_sz);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out_free_desc;
+ }
+
+ if (!(skb = atm_alloc_charge(vcc, len, GFP_ATOMIC))) {
+ if (vcc->vci < 32)
+ printk("Drop control packets\n");
+ goto out_free_desc;
+ }
+ skb_put(skb,len);
+ // pwang_test
+ ATM_SKB(skb)->vcc = vcc;
+ ATM_DESC(skb) = desc;
+ skb_queue_tail(&iadev->rx_dma_q, skb);
+
+ /* Build the DLE structure */
+ wr_ptr = iadev->rx_dle_q.write;
+ wr_ptr->sys_pkt_addr = dma_map_single(&iadev->pci->dev, skb->data,
+ len, DMA_FROM_DEVICE);
+ wr_ptr->local_pkt_addr = buf_addr;
+ wr_ptr->bytes = len; /* We don't know this do we ?? */
+ wr_ptr->mode = DMA_INT_ENABLE;
+
+ /* shud take care of wrap around here too. */
+ if(++wr_ptr == iadev->rx_dle_q.end)
+ wr_ptr = iadev->rx_dle_q.start;
+ iadev->rx_dle_q.write = wr_ptr;
+ udelay(1);
+ /* Increment transaction counter */
+ writel(1, iadev->dma+IPHASE5575_RX_COUNTER);
+out: return 0;
+out_free_desc:
+ free_desc(dev, desc);
+ goto out;
+}
+
+static void rx_intr(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ u_short status;
+ u_short state, i;
+
+ iadev = INPH_IA_DEV(dev);
+ status = readl(iadev->reass_reg+REASS_INTR_STATUS_REG) & 0xffff;
+ IF_EVENT(printk("rx_intr: status = 0x%x\n", status);)
+ if (status & RX_PKT_RCVD)
+ {
+ /* do something */
+ /* Basically recvd an interrupt for receiving a packet.
+ A descriptor would have been written to the packet complete
+ queue. Get all the descriptors and set up dma to move the
+ packets till the packet complete queue is empty..
+ */
+ state = readl(iadev->reass_reg + STATE_REG) & 0xffff;
+ IF_EVENT(printk("Rx intr status: RX_PKT_RCVD %08x\n", status);)
+ while(!(state & PCQ_EMPTY))
+ {
+ rx_pkt(dev);
+ state = readl(iadev->reass_reg + STATE_REG) & 0xffff;
+ }
+ iadev->rxing = 1;
+ }
+ if (status & RX_FREEQ_EMPT)
+ {
+ if (iadev->rxing) {
+ iadev->rx_tmp_cnt = iadev->rx_pkt_cnt;
+ iadev->rx_tmp_jif = jiffies;
+ iadev->rxing = 0;
+ }
+ else if ((time_after(jiffies, iadev->rx_tmp_jif + 50)) &&
+ ((iadev->rx_pkt_cnt - iadev->rx_tmp_cnt) == 0)) {
+ for (i = 1; i <= iadev->num_rx_desc; i++)
+ free_desc(dev, i);
+printk("Test logic RUN!!!!\n");
+ writew( ~(RX_FREEQ_EMPT|RX_EXCP_RCVD),iadev->reass_reg+REASS_MASK_REG);
+ iadev->rxing = 1;
+ }
+ IF_EVENT(printk("Rx intr status: RX_FREEQ_EMPT %08x\n", status);)
+ }
+
+ if (status & RX_EXCP_RCVD)
+ {
+ /* probably need to handle the exception queue also. */
+ IF_EVENT(printk("Rx intr status: RX_EXCP_RCVD %08x\n", status);)
+ rx_excp_rcvd(dev);
+ }
+
+
+ if (status & RX_RAW_RCVD)
+ {
+ /* need to handle the raw incoming cells. This deepnds on
+ whether we have programmed to receive the raw cells or not.
+ Else ignore. */
+ IF_EVENT(printk("Rx intr status: RX_RAW_RCVD %08x\n", status);)
+ }
+}
+
+
+static void rx_dle_intr(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ struct atm_vcc *vcc;
+ struct sk_buff *skb;
+ int desc;
+ u_short state;
+ struct dle *dle, *cur_dle;
+ u_int dle_lp;
+ int len;
+ iadev = INPH_IA_DEV(dev);
+
+ /* free all the dles done, that is just update our own dle read pointer
+ - do we really need to do this. Think not. */
+ /* DMA is done, just get all the recevie buffers from the rx dma queue
+ and push them up to the higher layer protocol. Also free the desc
+ associated with the buffer. */
+ dle = iadev->rx_dle_q.read;
+ dle_lp = readl(iadev->dma+IPHASE5575_RX_LIST_ADDR) & (sizeof(struct dle)*DLE_ENTRIES - 1);
+ cur_dle = (struct dle*)(iadev->rx_dle_q.start + (dle_lp >> 4));
+ while(dle != cur_dle)
+ {
+ /* free the DMAed skb */
+ skb = skb_dequeue(&iadev->rx_dma_q);
+ if (!skb)
+ goto INCR_DLE;
+ desc = ATM_DESC(skb);
+ free_desc(dev, desc);
+
+ if (!(len = skb->len))
+ {
+ printk("rx_dle_intr: skb len 0\n");
+ dev_kfree_skb_any(skb);
+ }
+ else
+ {
+ struct cpcs_trailer *trailer;
+ u_short length;
+ struct ia_vcc *ia_vcc;
+
+ dma_unmap_single(&iadev->pci->dev, iadev->rx_dle_q.write->sys_pkt_addr,
+ len, DMA_FROM_DEVICE);
+ /* no VCC related housekeeping done as yet. lets see */
+ vcc = ATM_SKB(skb)->vcc;
+ if (!vcc) {
+ printk("IA: null vcc\n");
+ dev_kfree_skb_any(skb);
+ goto INCR_DLE;
+ }
+ ia_vcc = INPH_IA_VCC(vcc);
+ if (ia_vcc == NULL)
+ {
+ atomic_inc(&vcc->stats->rx_err);
+ atm_return(vcc, skb->truesize);
+ dev_kfree_skb_any(skb);
+ goto INCR_DLE;
+ }
+ // get real pkt length pwang_test
+ trailer = (struct cpcs_trailer*)((u_char *)skb->data +
+ skb->len - sizeof(*trailer));
+ length = swap_byte_order(trailer->length);
+ if ((length > iadev->rx_buf_sz) || (length >
+ (skb->len - sizeof(struct cpcs_trailer))))
+ {
+ atomic_inc(&vcc->stats->rx_err);
+ IF_ERR(printk("rx_dle_intr: Bad AAL5 trailer %d (skb len %d)",
+ length, skb->len);)
+ atm_return(vcc, skb->truesize);
+ dev_kfree_skb_any(skb);
+ goto INCR_DLE;
+ }
+ skb_trim(skb, length);
+
+ /* Display the packet */
+ IF_RXPKT(printk("\nDmad Recvd data: len = %d \n", skb->len);
+ xdump(skb->data, skb->len, "RX: ");
+ printk("\n");)
+
+ IF_RX(printk("rx_dle_intr: skb push");)
+ vcc->push(vcc,skb);
+ atomic_inc(&vcc->stats->rx);
+ iadev->rx_pkt_cnt++;
+ }
+INCR_DLE:
+ if (++dle == iadev->rx_dle_q.end)
+ dle = iadev->rx_dle_q.start;
+ }
+ iadev->rx_dle_q.read = dle;
+
+ /* if the interrupts are masked because there were no free desc available,
+ unmask them now. */
+ if (!iadev->rxing) {
+ state = readl(iadev->reass_reg + STATE_REG) & 0xffff;
+ if (!(state & FREEQ_EMPTY)) {
+ state = readl(iadev->reass_reg + REASS_MASK_REG) & 0xffff;
+ writel(state & ~(RX_FREEQ_EMPT |/* RX_EXCP_RCVD |*/ RX_PKT_RCVD),
+ iadev->reass_reg+REASS_MASK_REG);
+ iadev->rxing++;
+ }
+ }
+}
+
+
+static int open_rx(struct atm_vcc *vcc)
+{
+ IADEV *iadev;
+ u_short __iomem *vc_table;
+ u_short __iomem *reass_ptr;
+ IF_EVENT(printk("iadev: open_rx %d.%d\n", vcc->vpi, vcc->vci);)
+
+ if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;
+ iadev = INPH_IA_DEV(vcc->dev);
+ if (vcc->qos.rxtp.traffic_class == ATM_ABR) {
+ if (iadev->phy_type & FE_25MBIT_PHY) {
+ printk("IA: ABR not support\n");
+ return -EINVAL;
+ }
+ }
+ /* Make only this VCI in the vc table valid and let all
+ others be invalid entries */
+ vc_table = iadev->reass_ram+RX_VC_TABLE*iadev->memSize;
+ vc_table += vcc->vci;
+ /* mask the last 6 bits and OR it with 3 for 1K VCs */
+
+ *vc_table = vcc->vci << 6;
+ /* Also keep a list of open rx vcs so that we can attach them with
+ incoming PDUs later. */
+ if ((vcc->qos.rxtp.traffic_class == ATM_ABR) ||
+ (vcc->qos.txtp.traffic_class == ATM_ABR))
+ {
+ srv_cls_param_t srv_p;
+ init_abr_vc(iadev, &srv_p);
+ ia_open_abr_vc(iadev, &srv_p, vcc, 0);
+ }
+ else { /* for UBR later may need to add CBR logic */
+ reass_ptr = iadev->reass_ram+REASS_TABLE*iadev->memSize;
+ reass_ptr += vcc->vci;
+ *reass_ptr = NO_AAL5_PKT;
+ }
+
+ if (iadev->rx_open[vcc->vci])
+ printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d already open\n",
+ vcc->dev->number, vcc->vci);
+ iadev->rx_open[vcc->vci] = vcc;
+ return 0;
+}
+
+static int rx_init(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ struct rx_buf_desc __iomem *buf_desc_ptr;
+ unsigned long rx_pkt_start = 0;
+ void *dle_addr;
+ struct abr_vc_table *abr_vc_table;
+ u16 *vc_table;
+ u16 *reass_table;
+ int i,j, vcsize_sel;
+ u_short freeq_st_adr;
+ u_short *freeq_start;
+
+ iadev = INPH_IA_DEV(dev);
+ // spin_lock_init(&iadev->rx_lock);
+
+ /* Allocate 4k bytes - more aligned than needed (4k boundary) */
+ dle_addr = dma_alloc_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE,
+ &iadev->rx_dle_dma, GFP_KERNEL);
+ if (!dle_addr) {
+ printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n");
+ goto err_out;
+ }
+ iadev->rx_dle_q.start = (struct dle *)dle_addr;
+ iadev->rx_dle_q.read = iadev->rx_dle_q.start;
+ iadev->rx_dle_q.write = iadev->rx_dle_q.start;
+ iadev->rx_dle_q.end = (struct dle*)((unsigned long)dle_addr+sizeof(struct dle)*DLE_ENTRIES);
+ /* the end of the dle q points to the entry after the last
+ DLE that can be used. */
+
+ /* write the upper 20 bits of the start address to rx list address register */
+ /* We know this is 32bit bus addressed so the following is safe */
+ writel(iadev->rx_dle_dma & 0xfffff000,
+ iadev->dma + IPHASE5575_RX_LIST_ADDR);
+ IF_INIT(printk("Tx Dle list addr: 0x%p value: 0x%0x\n",
+ iadev->dma+IPHASE5575_TX_LIST_ADDR,
+ readl(iadev->dma + IPHASE5575_TX_LIST_ADDR));
+ printk("Rx Dle list addr: 0x%p value: 0x%0x\n",
+ iadev->dma+IPHASE5575_RX_LIST_ADDR,
+ readl(iadev->dma + IPHASE5575_RX_LIST_ADDR));)
+
+ writew(0xffff, iadev->reass_reg+REASS_MASK_REG);
+ writew(0, iadev->reass_reg+MODE_REG);
+ writew(RESET_REASS, iadev->reass_reg+REASS_COMMAND_REG);
+
+ /* Receive side control memory map
+ -------------------------------
+
+ Buffer descr 0x0000 (736 - 23K)
+ VP Table 0x5c00 (256 - 512)
+ Except q 0x5e00 (128 - 512)
+ Free buffer q 0x6000 (1K - 2K)
+ Packet comp q 0x6800 (1K - 2K)
+ Reass Table 0x7000 (1K - 2K)
+ VC Table 0x7800 (1K - 2K)
+ ABR VC Table 0x8000 (1K - 32K)
+ */
+
+ /* Base address for Buffer Descriptor Table */
+ writew(RX_DESC_BASE >> 16, iadev->reass_reg+REASS_DESC_BASE);
+ /* Set the buffer size register */
+ writew(iadev->rx_buf_sz, iadev->reass_reg+BUF_SIZE);
+
+ /* Initialize each entry in the Buffer Descriptor Table */
+ iadev->RX_DESC_BASE_ADDR = iadev->reass_ram+RX_DESC_BASE*iadev->memSize;
+ buf_desc_ptr = iadev->RX_DESC_BASE_ADDR;
+ memset_io(buf_desc_ptr, 0, sizeof(*buf_desc_ptr));
+ buf_desc_ptr++;
+ rx_pkt_start = iadev->rx_pkt_ram;
+ for(i=1; i<=iadev->num_rx_desc; i++)
+ {
+ memset_io(buf_desc_ptr, 0, sizeof(*buf_desc_ptr));
+ buf_desc_ptr->buf_start_hi = rx_pkt_start >> 16;
+ buf_desc_ptr->buf_start_lo = rx_pkt_start & 0x0000ffff;
+ buf_desc_ptr++;
+ rx_pkt_start += iadev->rx_buf_sz;
+ }
+ IF_INIT(printk("Rx Buffer desc ptr: 0x%p\n", buf_desc_ptr);)
+ i = FREE_BUF_DESC_Q*iadev->memSize;
+ writew(i >> 16, iadev->reass_reg+REASS_QUEUE_BASE);
+ writew(i, iadev->reass_reg+FREEQ_ST_ADR);
+ writew(i+iadev->num_rx_desc*sizeof(u_short),
+ iadev->reass_reg+FREEQ_ED_ADR);
+ writew(i, iadev->reass_reg+FREEQ_RD_PTR);
+ writew(i+iadev->num_rx_desc*sizeof(u_short),
+ iadev->reass_reg+FREEQ_WR_PTR);
+ /* Fill the FREEQ with all the free descriptors. */
+ freeq_st_adr = readw(iadev->reass_reg+FREEQ_ST_ADR);
+ freeq_start = (u_short *)(iadev->reass_ram+freeq_st_adr);
+ for(i=1; i<=iadev->num_rx_desc; i++)
+ {
+ *freeq_start = (u_short)i;
+ freeq_start++;
+ }
+ IF_INIT(printk("freeq_start: 0x%p\n", freeq_start);)
+ /* Packet Complete Queue */
+ i = (PKT_COMP_Q * iadev->memSize) & 0xffff;
+ writew(i, iadev->reass_reg+PCQ_ST_ADR);
+ writew(i+iadev->num_vc*sizeof(u_short), iadev->reass_reg+PCQ_ED_ADR);
+ writew(i, iadev->reass_reg+PCQ_RD_PTR);
+ writew(i, iadev->reass_reg+PCQ_WR_PTR);
+
+ /* Exception Queue */
+ i = (EXCEPTION_Q * iadev->memSize) & 0xffff;
+ writew(i, iadev->reass_reg+EXCP_Q_ST_ADR);
+ writew(i + NUM_RX_EXCP * sizeof(RX_ERROR_Q),
+ iadev->reass_reg+EXCP_Q_ED_ADR);
+ writew(i, iadev->reass_reg+EXCP_Q_RD_PTR);
+ writew(i, iadev->reass_reg+EXCP_Q_WR_PTR);
+
+ /* Load local copy of FREEQ and PCQ ptrs */
+ iadev->rfL.fdq_st = readw(iadev->reass_reg+FREEQ_ST_ADR) & 0xffff;
+ iadev->rfL.fdq_ed = readw(iadev->reass_reg+FREEQ_ED_ADR) & 0xffff ;
+ iadev->rfL.fdq_rd = readw(iadev->reass_reg+FREEQ_RD_PTR) & 0xffff;
+ iadev->rfL.fdq_wr = readw(iadev->reass_reg+FREEQ_WR_PTR) & 0xffff;
+ iadev->rfL.pcq_st = readw(iadev->reass_reg+PCQ_ST_ADR) & 0xffff;
+ iadev->rfL.pcq_ed = readw(iadev->reass_reg+PCQ_ED_ADR) & 0xffff;
+ iadev->rfL.pcq_rd = readw(iadev->reass_reg+PCQ_RD_PTR) & 0xffff;
+ iadev->rfL.pcq_wr = readw(iadev->reass_reg+PCQ_WR_PTR) & 0xffff;
+
+ IF_INIT(printk("INIT:pcq_st:0x%x pcq_ed:0x%x pcq_rd:0x%x pcq_wr:0x%x",
+ iadev->rfL.pcq_st, iadev->rfL.pcq_ed, iadev->rfL.pcq_rd,
+ iadev->rfL.pcq_wr);)
+ /* just for check - no VP TBL */
+ /* VP Table */
+ /* writew(0x0b80, iadev->reass_reg+VP_LKUP_BASE); */
+ /* initialize VP Table for invalid VPIs
+ - I guess we can write all 1s or 0x000f in the entire memory
+ space or something similar.
+ */
+
+ /* This seems to work and looks right to me too !!! */
+ i = REASS_TABLE * iadev->memSize;
+ writew((i >> 3), iadev->reass_reg+REASS_TABLE_BASE);
+ /* initialize Reassembly table to I don't know what ???? */
+ reass_table = (u16 *)(iadev->reass_ram+i);
+ j = REASS_TABLE_SZ * iadev->memSize;
+ for(i=0; i < j; i++)
+ *reass_table++ = NO_AAL5_PKT;
+ i = 8*1024;
+ vcsize_sel = 0;
+ while (i != iadev->num_vc) {
+ i /= 2;
+ vcsize_sel++;
+ }
+ i = RX_VC_TABLE * iadev->memSize;
+ writew(((i>>3) & 0xfff8) | vcsize_sel, iadev->reass_reg+VC_LKUP_BASE);
+ vc_table = (u16 *)(iadev->reass_ram+RX_VC_TABLE*iadev->memSize);
+ j = RX_VC_TABLE_SZ * iadev->memSize;
+ for(i = 0; i < j; i++)
+ {
+ /* shift the reassembly pointer by 3 + lower 3 bits of
+ vc_lkup_base register (=3 for 1K VCs) and the last byte
+ is those low 3 bits.
+ Shall program this later.
+ */
+ *vc_table = (i << 6) | 15; /* for invalid VCI */
+ vc_table++;
+ }
+ /* ABR VC table */
+ i = ABR_VC_TABLE * iadev->memSize;
+ writew(i >> 3, iadev->reass_reg+ABR_LKUP_BASE);
+
+ i = ABR_VC_TABLE * iadev->memSize;
+ abr_vc_table = (struct abr_vc_table *)(iadev->reass_ram+i);
+ j = REASS_TABLE_SZ * iadev->memSize;
+ memset ((char*)abr_vc_table, 0, j * sizeof(*abr_vc_table));
+ for(i = 0; i < j; i++) {
+ abr_vc_table->rdf = 0x0003;
+ abr_vc_table->air = 0x5eb1;
+ abr_vc_table++;
+ }
+
+ /* Initialize other registers */
+
+ /* VP Filter Register set for VC Reassembly only */
+ writew(0xff00, iadev->reass_reg+VP_FILTER);
+ writew(0, iadev->reass_reg+XTRA_RM_OFFSET);
+ writew(0x1, iadev->reass_reg+PROTOCOL_ID);
+
+ /* Packet Timeout Count related Registers :
+ Set packet timeout to occur in about 3 seconds
+ Set Packet Aging Interval count register to overflow in about 4 us
+ */
+ writew(0xF6F8, iadev->reass_reg+PKT_TM_CNT );
+
+ i = (j >> 6) & 0xFF;
+ j += 2 * (j - 1);
+ i |= ((j << 2) & 0xFF00);
+ writew(i, iadev->reass_reg+TMOUT_RANGE);
+
+ /* initiate the desc_tble */
+ for(i=0; i<iadev->num_tx_desc;i++)
+ iadev->desc_tbl[i].timestamp = 0;
+
+ /* to clear the interrupt status register - read it */
+ readw(iadev->reass_reg+REASS_INTR_STATUS_REG);
+
+ /* Mask Register - clear it */
+ writew(~(RX_FREEQ_EMPT|RX_PKT_RCVD), iadev->reass_reg+REASS_MASK_REG);
+
+ skb_queue_head_init(&iadev->rx_dma_q);
+ iadev->rx_free_desc_qhead = NULL;
+
+ iadev->rx_open = kzalloc(4 * iadev->num_vc, GFP_KERNEL);
+ if (!iadev->rx_open) {
+ printk(KERN_ERR DEV_LABEL "itf %d couldn't get free page\n",
+ dev->number);
+ goto err_free_dle;
+ }
+
+ iadev->rxing = 1;
+ iadev->rx_pkt_cnt = 0;
+ /* Mode Register */
+ writew(R_ONLINE, iadev->reass_reg+MODE_REG);
+ return 0;
+
+err_free_dle:
+ dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->rx_dle_q.start,
+ iadev->rx_dle_dma);
+err_out:
+ return -ENOMEM;
+}
+
+
+/*
+ The memory map suggested in appendix A and the coding for it.
+ Keeping it around just in case we change our mind later.
+
+ Buffer descr 0x0000 (128 - 4K)
+ UBR sched 0x1000 (1K - 4K)
+ UBR Wait q 0x2000 (1K - 4K)
+ Commn queues 0x3000 Packet Ready, Trasmit comp(0x3100)
+ (128 - 256) each
+ extended VC 0x4000 (1K - 8K)
+ ABR sched 0x6000 and ABR wait queue (1K - 2K) each
+ CBR sched 0x7000 (as needed)
+ VC table 0x8000 (1K - 32K)
+*/
+
+static void tx_intr(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ unsigned short status;
+ unsigned long flags;
+
+ iadev = INPH_IA_DEV(dev);
+
+ status = readl(iadev->seg_reg+SEG_INTR_STATUS_REG);
+ if (status & TRANSMIT_DONE){
+
+ IF_EVENT(printk("Tansmit Done Intr logic run\n");)
+ spin_lock_irqsave(&iadev->tx_lock, flags);
+ ia_tx_poll(iadev);
+ spin_unlock_irqrestore(&iadev->tx_lock, flags);
+ writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG);
+ if (iadev->close_pending)
+ wake_up(&iadev->close_wait);
+ }
+ if (status & TCQ_NOT_EMPTY)
+ {
+ IF_EVENT(printk("TCQ_NOT_EMPTY int received\n");)
+ }
+}
+
+static void tx_dle_intr(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ struct dle *dle, *cur_dle;
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+ struct ia_vcc *iavcc;
+ u_int dle_lp;
+ unsigned long flags;
+
+ iadev = INPH_IA_DEV(dev);
+ spin_lock_irqsave(&iadev->tx_lock, flags);
+ dle = iadev->tx_dle_q.read;
+ dle_lp = readl(iadev->dma+IPHASE5575_TX_LIST_ADDR) &
+ (sizeof(struct dle)*DLE_ENTRIES - 1);
+ cur_dle = (struct dle*)(iadev->tx_dle_q.start + (dle_lp >> 4));
+ while (dle != cur_dle)
+ {
+ /* free the DMAed skb */
+ skb = skb_dequeue(&iadev->tx_dma_q);
+ if (!skb) break;
+
+ /* Revenge of the 2 dle (skb + trailer) used in ia_pkt_tx() */
+ if (!((dle - iadev->tx_dle_q.start)%(2*sizeof(struct dle)))) {
+ dma_unmap_single(&iadev->pci->dev, dle->sys_pkt_addr, skb->len,
+ DMA_TO_DEVICE);
+ }
+ vcc = ATM_SKB(skb)->vcc;
+ if (!vcc) {
+ printk("tx_dle_intr: vcc is null\n");
+ spin_unlock_irqrestore(&iadev->tx_lock, flags);
+ dev_kfree_skb_any(skb);
+
+ return;
+ }
+ iavcc = INPH_IA_VCC(vcc);
+ if (!iavcc) {
+ printk("tx_dle_intr: iavcc is null\n");
+ spin_unlock_irqrestore(&iadev->tx_lock, flags);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+ if (vcc->qos.txtp.pcr >= iadev->rate_limit) {
+ if ((vcc->pop) && (skb->len != 0))
+ {
+ vcc->pop(vcc, skb);
+ }
+ else {
+ dev_kfree_skb_any(skb);
+ }
+ }
+ else { /* Hold the rate-limited skb for flow control */
+ IA_SKB_STATE(skb) |= IA_DLED;
+ skb_queue_tail(&iavcc->txing_skb, skb);
+ }
+ IF_EVENT(printk("tx_dle_intr: enque skb = 0x%p \n", skb);)
+ if (++dle == iadev->tx_dle_q.end)
+ dle = iadev->tx_dle_q.start;
+ }
+ iadev->tx_dle_q.read = dle;
+ spin_unlock_irqrestore(&iadev->tx_lock, flags);
+}
+
+static int open_tx(struct atm_vcc *vcc)
+{
+ struct ia_vcc *ia_vcc;
+ IADEV *iadev;
+ struct main_vc *vc;
+ struct ext_vc *evc;
+ int ret;
+ IF_EVENT(printk("iadev: open_tx entered vcc->vci = %d\n", vcc->vci);)
+ if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
+ iadev = INPH_IA_DEV(vcc->dev);
+
+ if (iadev->phy_type & FE_25MBIT_PHY) {
+ if (vcc->qos.txtp.traffic_class == ATM_ABR) {
+ printk("IA: ABR not support\n");
+ return -EINVAL;
+ }
+ if (vcc->qos.txtp.traffic_class == ATM_CBR) {
+ printk("IA: CBR not support\n");
+ return -EINVAL;
+ }
+ }
+ ia_vcc = INPH_IA_VCC(vcc);
+ memset((caddr_t)ia_vcc, 0, sizeof(*ia_vcc));
+ if (vcc->qos.txtp.max_sdu >
+ (iadev->tx_buf_sz - sizeof(struct cpcs_trailer))){
+ printk("IA: SDU size over (%d) the configured SDU size %d\n",
+ vcc->qos.txtp.max_sdu,iadev->tx_buf_sz);
+ vcc->dev_data = NULL;
+ kfree(ia_vcc);
+ return -EINVAL;
+ }
+ ia_vcc->vc_desc_cnt = 0;
+ ia_vcc->txing = 1;
+
+ /* find pcr */
+ if (vcc->qos.txtp.max_pcr == ATM_MAX_PCR)
+ vcc->qos.txtp.pcr = iadev->LineRate;
+ else if ((vcc->qos.txtp.max_pcr == 0)&&( vcc->qos.txtp.pcr <= 0))
+ vcc->qos.txtp.pcr = iadev->LineRate;
+ else if ((vcc->qos.txtp.max_pcr > vcc->qos.txtp.pcr) && (vcc->qos.txtp.max_pcr> 0))
+ vcc->qos.txtp.pcr = vcc->qos.txtp.max_pcr;
+ if (vcc->qos.txtp.pcr > iadev->LineRate)
+ vcc->qos.txtp.pcr = iadev->LineRate;
+ ia_vcc->pcr = vcc->qos.txtp.pcr;
+
+ if (ia_vcc->pcr > (iadev->LineRate / 6) ) ia_vcc->ltimeout = HZ / 10;
+ else if (ia_vcc->pcr > (iadev->LineRate / 130)) ia_vcc->ltimeout = HZ;
+ else if (ia_vcc->pcr <= 170) ia_vcc->ltimeout = 16 * HZ;
+ else ia_vcc->ltimeout = 2700 * HZ / ia_vcc->pcr;
+ if (ia_vcc->pcr < iadev->rate_limit)
+ skb_queue_head_init (&ia_vcc->txing_skb);
+ if (ia_vcc->pcr < iadev->rate_limit) {
+ struct sock *sk = sk_atm(vcc);
+
+ if (vcc->qos.txtp.max_sdu != 0) {
+ if (ia_vcc->pcr > 60000)
+ sk->sk_sndbuf = vcc->qos.txtp.max_sdu * 5;
+ else if (ia_vcc->pcr > 2000)
+ sk->sk_sndbuf = vcc->qos.txtp.max_sdu * 4;
+ else
+ sk->sk_sndbuf = vcc->qos.txtp.max_sdu * 3;
+ }
+ else
+ sk->sk_sndbuf = 24576;
+ }
+
+ vc = (struct main_vc *)iadev->MAIN_VC_TABLE_ADDR;
+ evc = (struct ext_vc *)iadev->EXT_VC_TABLE_ADDR;
+ vc += vcc->vci;
+ evc += vcc->vci;
+ memset((caddr_t)vc, 0, sizeof(*vc));
+ memset((caddr_t)evc, 0, sizeof(*evc));
+
+ /* store the most significant 4 bits of vci as the last 4 bits
+ of first part of atm header.
+ store the last 12 bits of vci as first 12 bits of the second
+ part of the atm header.
+ */
+ evc->atm_hdr1 = (vcc->vci >> 12) & 0x000f;
+ evc->atm_hdr2 = (vcc->vci & 0x0fff) << 4;
+
+ /* check the following for different traffic classes */
+ if (vcc->qos.txtp.traffic_class == ATM_UBR)
+ {
+ vc->type = UBR;
+ vc->status = CRC_APPEND;
+ vc->acr = cellrate_to_float(iadev->LineRate);
+ if (vcc->qos.txtp.pcr > 0)
+ vc->acr = cellrate_to_float(vcc->qos.txtp.pcr);
+ IF_UBR(printk("UBR: txtp.pcr = 0x%x f_rate = 0x%x\n",
+ vcc->qos.txtp.max_pcr,vc->acr);)
+ }
+ else if (vcc->qos.txtp.traffic_class == ATM_ABR)
+ { srv_cls_param_t srv_p;
+ IF_ABR(printk("Tx ABR VCC\n");)
+ init_abr_vc(iadev, &srv_p);
+ if (vcc->qos.txtp.pcr > 0)
+ srv_p.pcr = vcc->qos.txtp.pcr;
+ if (vcc->qos.txtp.min_pcr > 0) {
+ int tmpsum = iadev->sum_mcr+iadev->sum_cbr+vcc->qos.txtp.min_pcr;
+ if (tmpsum > iadev->LineRate)
+ return -EBUSY;
+ srv_p.mcr = vcc->qos.txtp.min_pcr;
+ iadev->sum_mcr += vcc->qos.txtp.min_pcr;
+ }
+ else srv_p.mcr = 0;
+ if (vcc->qos.txtp.icr)
+ srv_p.icr = vcc->qos.txtp.icr;
+ if (vcc->qos.txtp.tbe)
+ srv_p.tbe = vcc->qos.txtp.tbe;
+ if (vcc->qos.txtp.frtt)
+ srv_p.frtt = vcc->qos.txtp.frtt;
+ if (vcc->qos.txtp.rif)
+ srv_p.rif = vcc->qos.txtp.rif;
+ if (vcc->qos.txtp.rdf)
+ srv_p.rdf = vcc->qos.txtp.rdf;
+ if (vcc->qos.txtp.nrm_pres)
+ srv_p.nrm = vcc->qos.txtp.nrm;
+ if (vcc->qos.txtp.trm_pres)
+ srv_p.trm = vcc->qos.txtp.trm;
+ if (vcc->qos.txtp.adtf_pres)
+ srv_p.adtf = vcc->qos.txtp.adtf;
+ if (vcc->qos.txtp.cdf_pres)
+ srv_p.cdf = vcc->qos.txtp.cdf;
+ if (srv_p.icr > srv_p.pcr)
+ srv_p.icr = srv_p.pcr;
+ IF_ABR(printk("ABR:vcc->qos.txtp.max_pcr = %d mcr = %d\n",
+ srv_p.pcr, srv_p.mcr);)
+ ia_open_abr_vc(iadev, &srv_p, vcc, 1);
+ } else if (vcc->qos.txtp.traffic_class == ATM_CBR) {
+ if (iadev->phy_type & FE_25MBIT_PHY) {
+ printk("IA: CBR not support\n");
+ return -EINVAL;
+ }
+ if (vcc->qos.txtp.max_pcr > iadev->LineRate) {
+ IF_CBR(printk("PCR is not available\n");)
+ return -1;
+ }
+ vc->type = CBR;
+ vc->status = CRC_APPEND;
+ if ((ret = ia_cbr_setup (iadev, vcc)) < 0) {
+ return ret;
+ }
+ }
+ else
+ printk("iadev: Non UBR, ABR and CBR traffic not supportedn");
+
+ iadev->testTable[vcc->vci]->vc_status |= VC_ACTIVE;
+ IF_EVENT(printk("ia open_tx returning \n");)
+ return 0;
+}
+
+
+static int tx_init(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ struct tx_buf_desc *buf_desc_ptr;
+ unsigned int tx_pkt_start;
+ void *dle_addr;
+ int i;
+ u_short tcq_st_adr;
+ u_short *tcq_start;
+ u_short prq_st_adr;
+ u_short *prq_start;
+ struct main_vc *vc;
+ struct ext_vc *evc;
+ u_short tmp16;
+ u32 vcsize_sel;
+
+ iadev = INPH_IA_DEV(dev);
+ spin_lock_init(&iadev->tx_lock);
+
+ IF_INIT(printk("Tx MASK REG: 0x%0x\n",
+ readw(iadev->seg_reg+SEG_MASK_REG));)
+
+ /* Allocate 4k (boundary aligned) bytes */
+ dle_addr = dma_alloc_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE,
+ &iadev->tx_dle_dma, GFP_KERNEL);
+ if (!dle_addr) {
+ printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n");
+ goto err_out;
+ }
+ iadev->tx_dle_q.start = (struct dle*)dle_addr;
+ iadev->tx_dle_q.read = iadev->tx_dle_q.start;
+ iadev->tx_dle_q.write = iadev->tx_dle_q.start;
+ iadev->tx_dle_q.end = (struct dle*)((unsigned long)dle_addr+sizeof(struct dle)*DLE_ENTRIES);
+
+ /* write the upper 20 bits of the start address to tx list address register */
+ writel(iadev->tx_dle_dma & 0xfffff000,
+ iadev->dma + IPHASE5575_TX_LIST_ADDR);
+ writew(0xffff, iadev->seg_reg+SEG_MASK_REG);
+ writew(0, iadev->seg_reg+MODE_REG_0);
+ writew(RESET_SEG, iadev->seg_reg+SEG_COMMAND_REG);
+ iadev->MAIN_VC_TABLE_ADDR = iadev->seg_ram+MAIN_VC_TABLE*iadev->memSize;
+ iadev->EXT_VC_TABLE_ADDR = iadev->seg_ram+EXT_VC_TABLE*iadev->memSize;
+ iadev->ABR_SCHED_TABLE_ADDR=iadev->seg_ram+ABR_SCHED_TABLE*iadev->memSize;
+
+ /*
+ Transmit side control memory map
+ --------------------------------
+ Buffer descr 0x0000 (128 - 4K)
+ Commn queues 0x1000 Transmit comp, Packet ready(0x1400)
+ (512 - 1K) each
+ TCQ - 4K, PRQ - 5K
+ CBR Table 0x1800 (as needed) - 6K
+ UBR Table 0x3000 (1K - 4K) - 12K
+ UBR Wait queue 0x4000 (1K - 4K) - 16K
+ ABR sched 0x5000 and ABR wait queue (1K - 2K) each
+ ABR Tbl - 20K, ABR Wq - 22K
+ extended VC 0x6000 (1K - 8K) - 24K
+ VC Table 0x8000 (1K - 32K) - 32K
+
+ Between 0x2000 (8K) and 0x3000 (12K) there is 4K space left for VBR Tbl
+ and Wait q, which can be allotted later.
+ */
+
+ /* Buffer Descriptor Table Base address */
+ writew(TX_DESC_BASE, iadev->seg_reg+SEG_DESC_BASE);
+
+ /* initialize each entry in the buffer descriptor table */
+ buf_desc_ptr =(struct tx_buf_desc *)(iadev->seg_ram+TX_DESC_BASE);
+ memset((caddr_t)buf_desc_ptr, 0, sizeof(*buf_desc_ptr));
+ buf_desc_ptr++;
+ tx_pkt_start = TX_PACKET_RAM;
+ for(i=1; i<=iadev->num_tx_desc; i++)
+ {
+ memset((caddr_t)buf_desc_ptr, 0, sizeof(*buf_desc_ptr));
+ buf_desc_ptr->desc_mode = AAL5;
+ buf_desc_ptr->buf_start_hi = tx_pkt_start >> 16;
+ buf_desc_ptr->buf_start_lo = tx_pkt_start & 0x0000ffff;
+ buf_desc_ptr++;
+ tx_pkt_start += iadev->tx_buf_sz;
+ }
+ iadev->tx_buf = kmalloc(iadev->num_tx_desc*sizeof(struct cpcs_trailer_desc), GFP_KERNEL);
+ if (!iadev->tx_buf) {
+ printk(KERN_ERR DEV_LABEL " couldn't get mem\n");
+ goto err_free_dle;
+ }
+ for (i= 0; i< iadev->num_tx_desc; i++)
+ {
+ struct cpcs_trailer *cpcs;
+
+ cpcs = kmalloc(sizeof(*cpcs), GFP_KERNEL|GFP_DMA);
+ if(!cpcs) {
+ printk(KERN_ERR DEV_LABEL " couldn't get freepage\n");
+ goto err_free_tx_bufs;
+ }
+ iadev->tx_buf[i].cpcs = cpcs;
+ iadev->tx_buf[i].dma_addr = dma_map_single(&iadev->pci->dev,
+ cpcs,
+ sizeof(*cpcs),
+ DMA_TO_DEVICE);
+ }
+ iadev->desc_tbl = kmalloc(iadev->num_tx_desc *
+ sizeof(struct desc_tbl_t), GFP_KERNEL);
+ if (!iadev->desc_tbl) {
+ printk(KERN_ERR DEV_LABEL " couldn't get mem\n");
+ goto err_free_all_tx_bufs;
+ }
+
+ /* Communication Queues base address */
+ i = TX_COMP_Q * iadev->memSize;
+ writew(i >> 16, iadev->seg_reg+SEG_QUEUE_BASE);
+
+ /* Transmit Complete Queue */
+ writew(i, iadev->seg_reg+TCQ_ST_ADR);
+ writew(i, iadev->seg_reg+TCQ_RD_PTR);
+ writew(i+iadev->num_tx_desc*sizeof(u_short),iadev->seg_reg+TCQ_WR_PTR);
+ iadev->host_tcq_wr = i + iadev->num_tx_desc*sizeof(u_short);
+ writew(i+2 * iadev->num_tx_desc * sizeof(u_short),
+ iadev->seg_reg+TCQ_ED_ADR);
+ /* Fill the TCQ with all the free descriptors. */
+ tcq_st_adr = readw(iadev->seg_reg+TCQ_ST_ADR);
+ tcq_start = (u_short *)(iadev->seg_ram+tcq_st_adr);
+ for(i=1; i<=iadev->num_tx_desc; i++)
+ {
+ *tcq_start = (u_short)i;
+ tcq_start++;
+ }
+
+ /* Packet Ready Queue */
+ i = PKT_RDY_Q * iadev->memSize;
+ writew(i, iadev->seg_reg+PRQ_ST_ADR);
+ writew(i+2 * iadev->num_tx_desc * sizeof(u_short),
+ iadev->seg_reg+PRQ_ED_ADR);
+ writew(i, iadev->seg_reg+PRQ_RD_PTR);
+ writew(i, iadev->seg_reg+PRQ_WR_PTR);
+
+ /* Load local copy of PRQ and TCQ ptrs */
+ iadev->ffL.prq_st = readw(iadev->seg_reg+PRQ_ST_ADR) & 0xffff;
+ iadev->ffL.prq_ed = readw(iadev->seg_reg+PRQ_ED_ADR) & 0xffff;
+ iadev->ffL.prq_wr = readw(iadev->seg_reg+PRQ_WR_PTR) & 0xffff;
+
+ iadev->ffL.tcq_st = readw(iadev->seg_reg+TCQ_ST_ADR) & 0xffff;
+ iadev->ffL.tcq_ed = readw(iadev->seg_reg+TCQ_ED_ADR) & 0xffff;
+ iadev->ffL.tcq_rd = readw(iadev->seg_reg+TCQ_RD_PTR) & 0xffff;
+
+ /* Just for safety initializing the queue to have desc 1 always */
+ /* Fill the PRQ with all the free descriptors. */
+ prq_st_adr = readw(iadev->seg_reg+PRQ_ST_ADR);
+ prq_start = (u_short *)(iadev->seg_ram+prq_st_adr);
+ for(i=1; i<=iadev->num_tx_desc; i++)
+ {
+ *prq_start = (u_short)0; /* desc 1 in all entries */
+ prq_start++;
+ }
+ /* CBR Table */
+ IF_INIT(printk("Start CBR Init\n");)
+#if 1 /* for 1K VC board, CBR_PTR_BASE is 0 */
+ writew(0,iadev->seg_reg+CBR_PTR_BASE);
+#else /* Charlie's logic is wrong ? */
+ tmp16 = (iadev->seg_ram+CBR_SCHED_TABLE*iadev->memSize)>>17;
+ IF_INIT(printk("cbr_ptr_base = 0x%x ", tmp16);)
+ writew(tmp16,iadev->seg_reg+CBR_PTR_BASE);
+#endif
+
+ IF_INIT(printk("value in register = 0x%x\n",
+ readw(iadev->seg_reg+CBR_PTR_BASE));)
+ tmp16 = (CBR_SCHED_TABLE*iadev->memSize) >> 1;
+ writew(tmp16, iadev->seg_reg+CBR_TAB_BEG);
+ IF_INIT(printk("cbr_tab_beg = 0x%x in reg = 0x%x \n", tmp16,
+ readw(iadev->seg_reg+CBR_TAB_BEG));)
+ writew(tmp16, iadev->seg_reg+CBR_TAB_END+1); // CBR_PTR;
+ tmp16 = (CBR_SCHED_TABLE*iadev->memSize + iadev->num_vc*6 - 2) >> 1;
+ writew(tmp16, iadev->seg_reg+CBR_TAB_END);
+ IF_INIT(printk("iadev->seg_reg = 0x%p CBR_PTR_BASE = 0x%x\n",
+ iadev->seg_reg, readw(iadev->seg_reg+CBR_PTR_BASE));)
+ IF_INIT(printk("CBR_TAB_BEG = 0x%x, CBR_TAB_END = 0x%x, CBR_PTR = 0x%x\n",
+ readw(iadev->seg_reg+CBR_TAB_BEG), readw(iadev->seg_reg+CBR_TAB_END),
+ readw(iadev->seg_reg+CBR_TAB_END+1));)
+
+ /* Initialize the CBR Schedualing Table */
+ memset_io(iadev->seg_ram+CBR_SCHED_TABLE*iadev->memSize,
+ 0, iadev->num_vc*6);
+ iadev->CbrRemEntries = iadev->CbrTotEntries = iadev->num_vc*3;
+ iadev->CbrEntryPt = 0;
+ iadev->Granularity = MAX_ATM_155 / iadev->CbrTotEntries;
+ iadev->NumEnabledCBR = 0;
+
+ /* UBR scheduling Table and wait queue */
+ /* initialize all bytes of UBR scheduler table and wait queue to 0
+ - SCHEDSZ is 1K (# of entries).
+ - UBR Table size is 4K
+ - UBR wait queue is 4K
+ since the table and wait queues are contiguous, all the bytes
+ can be initialized by one memeset.
+ */
+
+ vcsize_sel = 0;
+ i = 8*1024;
+ while (i != iadev->num_vc) {
+ i /= 2;
+ vcsize_sel++;
+ }
+
+ i = MAIN_VC_TABLE * iadev->memSize;
+ writew(vcsize_sel | ((i >> 8) & 0xfff8),iadev->seg_reg+VCT_BASE);
+ i = EXT_VC_TABLE * iadev->memSize;
+ writew((i >> 8) & 0xfffe, iadev->seg_reg+VCTE_BASE);
+ i = UBR_SCHED_TABLE * iadev->memSize;
+ writew((i & 0xffff) >> 11, iadev->seg_reg+UBR_SBPTR_BASE);
+ i = UBR_WAIT_Q * iadev->memSize;
+ writew((i >> 7) & 0xffff, iadev->seg_reg+UBRWQ_BASE);
+ memset((caddr_t)(iadev->seg_ram+UBR_SCHED_TABLE*iadev->memSize),
+ 0, iadev->num_vc*8);
+ /* ABR scheduling Table(0x5000-0x57ff) and wait queue(0x5800-0x5fff)*/
+ /* initialize all bytes of ABR scheduler table and wait queue to 0
+ - SCHEDSZ is 1K (# of entries).
+ - ABR Table size is 2K
+ - ABR wait queue is 2K
+ since the table and wait queues are contiguous, all the bytes
+ can be initialized by one memeset.
+ */
+ i = ABR_SCHED_TABLE * iadev->memSize;
+ writew((i >> 11) & 0xffff, iadev->seg_reg+ABR_SBPTR_BASE);
+ i = ABR_WAIT_Q * iadev->memSize;
+ writew((i >> 7) & 0xffff, iadev->seg_reg+ABRWQ_BASE);
+
+ i = ABR_SCHED_TABLE*iadev->memSize;
+ memset((caddr_t)(iadev->seg_ram+i), 0, iadev->num_vc*4);
+ vc = (struct main_vc *)iadev->MAIN_VC_TABLE_ADDR;
+ evc = (struct ext_vc *)iadev->EXT_VC_TABLE_ADDR;
+ iadev->testTable = kmalloc(sizeof(long)*iadev->num_vc, GFP_KERNEL);
+ if (!iadev->testTable) {
+ printk("Get freepage failed\n");
+ goto err_free_desc_tbl;
+ }
+ for(i=0; i<iadev->num_vc; i++)
+ {
+ memset((caddr_t)vc, 0, sizeof(*vc));
+ memset((caddr_t)evc, 0, sizeof(*evc));
+ iadev->testTable[i] = kmalloc(sizeof(struct testTable_t),
+ GFP_KERNEL);
+ if (!iadev->testTable[i])
+ goto err_free_test_tables;
+ iadev->testTable[i]->lastTime = 0;
+ iadev->testTable[i]->fract = 0;
+ iadev->testTable[i]->vc_status = VC_UBR;
+ vc++;
+ evc++;
+ }
+
+ /* Other Initialization */
+
+ /* Max Rate Register */
+ if (iadev->phy_type & FE_25MBIT_PHY) {
+ writew(RATE25, iadev->seg_reg+MAXRATE);
+ writew((UBR_EN | (0x23 << 2)), iadev->seg_reg+STPARMS);
+ }
+ else {
+ writew(cellrate_to_float(iadev->LineRate),iadev->seg_reg+MAXRATE);
+ writew((UBR_EN | ABR_EN | (0x23 << 2)), iadev->seg_reg+STPARMS);
+ }
+ /* Set Idle Header Reigisters to be sure */
+ writew(0, iadev->seg_reg+IDLEHEADHI);
+ writew(0, iadev->seg_reg+IDLEHEADLO);
+
+ /* Program ABR UBR Priority Register as PRI_ABR_UBR_EQUAL */
+ writew(0xaa00, iadev->seg_reg+ABRUBR_ARB);
+
+ iadev->close_pending = 0;
+ init_waitqueue_head(&iadev->close_wait);
+ init_waitqueue_head(&iadev->timeout_wait);
+ skb_queue_head_init(&iadev->tx_dma_q);
+ ia_init_rtn_q(&iadev->tx_return_q);
+
+ /* RM Cell Protocol ID and Message Type */
+ writew(RM_TYPE_4_0, iadev->seg_reg+RM_TYPE);
+ skb_queue_head_init (&iadev->tx_backlog);
+
+ /* Mode Register 1 */
+ writew(MODE_REG_1_VAL, iadev->seg_reg+MODE_REG_1);
+
+ /* Mode Register 0 */
+ writew(T_ONLINE, iadev->seg_reg+MODE_REG_0);
+
+ /* Interrupt Status Register - read to clear */
+ readw(iadev->seg_reg+SEG_INTR_STATUS_REG);
+
+ /* Interrupt Mask Reg- don't mask TCQ_NOT_EMPTY interrupt generation */
+ writew(~(TRANSMIT_DONE | TCQ_NOT_EMPTY), iadev->seg_reg+SEG_MASK_REG);
+ writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG);
+ iadev->tx_pkt_cnt = 0;
+ iadev->rate_limit = iadev->LineRate / 3;
+
+ return 0;
+
+err_free_test_tables:
+ while (--i >= 0)
+ kfree(iadev->testTable[i]);
+ kfree(iadev->testTable);
+err_free_desc_tbl:
+ kfree(iadev->desc_tbl);
+err_free_all_tx_bufs:
+ i = iadev->num_tx_desc;
+err_free_tx_bufs:
+ while (--i >= 0) {
+ struct cpcs_trailer_desc *desc = iadev->tx_buf + i;
+
+ dma_unmap_single(&iadev->pci->dev, desc->dma_addr,
+ sizeof(*desc->cpcs), DMA_TO_DEVICE);
+ kfree(desc->cpcs);
+ }
+ kfree(iadev->tx_buf);
+err_free_dle:
+ dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->tx_dle_q.start,
+ iadev->tx_dle_dma);
+err_out:
+ return -ENOMEM;
+}
+
+static irqreturn_t ia_int(int irq, void *dev_id)
+{
+ struct atm_dev *dev;
+ IADEV *iadev;
+ unsigned int status;
+ int handled = 0;
+
+ dev = dev_id;
+ iadev = INPH_IA_DEV(dev);
+ while( (status = readl(iadev->reg+IPHASE5575_BUS_STATUS_REG) & 0x7f))
+ {
+ handled = 1;
+ IF_EVENT(printk("ia_int: status = 0x%x\n", status);)
+ if (status & STAT_REASSINT)
+ {
+ /* do something */
+ IF_EVENT(printk("REASSINT Bus status reg: %08x\n", status);)
+ rx_intr(dev);
+ }
+ if (status & STAT_DLERINT)
+ {
+ /* Clear this bit by writing a 1 to it. */
+ writel(STAT_DLERINT, iadev->reg + IPHASE5575_BUS_STATUS_REG);
+ rx_dle_intr(dev);
+ }
+ if (status & STAT_SEGINT)
+ {
+ /* do something */
+ IF_EVENT(printk("IA: tx_intr \n");)
+ tx_intr(dev);
+ }
+ if (status & STAT_DLETINT)
+ {
+ writel(STAT_DLETINT, iadev->reg + IPHASE5575_BUS_STATUS_REG);
+ tx_dle_intr(dev);
+ }
+ if (status & (STAT_FEINT | STAT_ERRINT | STAT_MARKINT))
+ {
+ if (status & STAT_FEINT)
+ ia_frontend_intr(iadev);
+ }
+ }
+ return IRQ_RETVAL(handled);
+}
+
+
+
+/*----------------------------- entries --------------------------------*/
+static int get_esi(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ int i;
+ u32 mac1;
+ u16 mac2;
+
+ iadev = INPH_IA_DEV(dev);
+ mac1 = cpu_to_be32(le32_to_cpu(readl(
+ iadev->reg+IPHASE5575_MAC1)));
+ mac2 = cpu_to_be16(le16_to_cpu(readl(iadev->reg+IPHASE5575_MAC2)));
+ IF_INIT(printk("ESI: 0x%08x%04x\n", mac1, mac2);)
+ for (i=0; i<MAC1_LEN; i++)
+ dev->esi[i] = mac1 >>(8*(MAC1_LEN-1-i));
+
+ for (i=0; i<MAC2_LEN; i++)
+ dev->esi[i+MAC1_LEN] = mac2 >>(8*(MAC2_LEN - 1 -i));
+ return 0;
+}
+
+static int reset_sar(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ int i, error = 1;
+ unsigned int pci[64];
+
+ iadev = INPH_IA_DEV(dev);
+ for(i=0; i<64; i++)
+ if ((error = pci_read_config_dword(iadev->pci,
+ i*4, &pci[i])) != PCIBIOS_SUCCESSFUL)
+ return error;
+ writel(0, iadev->reg+IPHASE5575_EXT_RESET);
+ for(i=0; i<64; i++)
+ if ((error = pci_write_config_dword(iadev->pci,
+ i*4, pci[i])) != PCIBIOS_SUCCESSFUL)
+ return error;
+ udelay(5);
+ return 0;
+}
+
+
+static int ia_init(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ unsigned long real_base;
+ void __iomem *base;
+ unsigned short command;
+ int error, i;
+
+ /* The device has been identified and registered. Now we read
+ necessary configuration info like memory base address,
+ interrupt number etc */
+
+ IF_INIT(printk(">ia_init\n");)
+ dev->ci_range.vpi_bits = 0;
+ dev->ci_range.vci_bits = NR_VCI_LD;
+
+ iadev = INPH_IA_DEV(dev);
+ real_base = pci_resource_start (iadev->pci, 0);
+ iadev->irq = iadev->pci->irq;
+
+ error = pci_read_config_word(iadev->pci, PCI_COMMAND, &command);
+ if (error) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): init error 0x%x\n",
+ dev->number,error);
+ return -EINVAL;
+ }
+ IF_INIT(printk(DEV_LABEL "(itf %d): rev.%d,realbase=0x%lx,irq=%d\n",
+ dev->number, iadev->pci->revision, real_base, iadev->irq);)
+
+ /* find mapping size of board */
+
+ iadev->pci_map_size = pci_resource_len(iadev->pci, 0);
+
+ if (iadev->pci_map_size == 0x100000){
+ iadev->num_vc = 4096;
+ dev->ci_range.vci_bits = NR_VCI_4K_LD;
+ iadev->memSize = 4;
+ }
+ else if (iadev->pci_map_size == 0x40000) {
+ iadev->num_vc = 1024;
+ iadev->memSize = 1;
+ }
+ else {
+ printk("Unknown pci_map_size = 0x%x\n", iadev->pci_map_size);
+ return -EINVAL;
+ }
+ IF_INIT(printk (DEV_LABEL "map size: %i\n", iadev->pci_map_size);)
+
+ /* enable bus mastering */
+ pci_set_master(iadev->pci);
+
+ /*
+ * Delay at least 1us before doing any mem accesses (how 'bout 10?)
+ */
+ udelay(10);
+
+ /* mapping the physical address to a virtual address in address space */
+ base = ioremap(real_base,iadev->pci_map_size); /* ioremap is not resolved ??? */
+
+ if (!base)
+ {
+ printk(DEV_LABEL " (itf %d): can't set up page mapping\n",
+ dev->number);
+ return -ENOMEM;
+ }
+ IF_INIT(printk(DEV_LABEL " (itf %d): rev.%d,base=%p,irq=%d\n",
+ dev->number, iadev->pci->revision, base, iadev->irq);)
+
+ /* filling the iphase dev structure */
+ iadev->mem = iadev->pci_map_size /2;
+ iadev->real_base = real_base;
+ iadev->base = base;
+
+ /* Bus Interface Control Registers */
+ iadev->reg = base + REG_BASE;
+ /* Segmentation Control Registers */
+ iadev->seg_reg = base + SEG_BASE;
+ /* Reassembly Control Registers */
+ iadev->reass_reg = base + REASS_BASE;
+ /* Front end/ DMA control registers */
+ iadev->phy = base + PHY_BASE;
+ iadev->dma = base + PHY_BASE;
+ /* RAM - Segmentation RAm and Reassembly RAM */
+ iadev->ram = base + ACTUAL_RAM_BASE;
+ iadev->seg_ram = base + ACTUAL_SEG_RAM_BASE;
+ iadev->reass_ram = base + ACTUAL_REASS_RAM_BASE;
+
+ /* lets print out the above */
+ IF_INIT(printk("Base addrs: %p %p %p \n %p %p %p %p\n",
+ iadev->reg,iadev->seg_reg,iadev->reass_reg,
+ iadev->phy, iadev->ram, iadev->seg_ram,
+ iadev->reass_ram);)
+
+ /* lets try reading the MAC address */
+ error = get_esi(dev);
+ if (error) {
+ iounmap(iadev->base);
+ return error;
+ }
+ printk("IA: ");
+ for (i=0; i < ESI_LEN; i++)
+ printk("%s%02X",i ? "-" : "",dev->esi[i]);
+ printk("\n");
+
+ /* reset SAR */
+ if (reset_sar(dev)) {
+ iounmap(iadev->base);
+ printk("IA: reset SAR fail, please try again\n");
+ return 1;
+ }
+ return 0;
+}
+
+static void ia_update_stats(IADEV *iadev) {
+ if (!iadev->carrier_detect)
+ return;
+ iadev->rx_cell_cnt += readw(iadev->reass_reg+CELL_CTR0)&0xffff;
+ iadev->rx_cell_cnt += (readw(iadev->reass_reg+CELL_CTR1) & 0xffff) << 16;
+ iadev->drop_rxpkt += readw(iadev->reass_reg + DRP_PKT_CNTR ) & 0xffff;
+ iadev->drop_rxcell += readw(iadev->reass_reg + ERR_CNTR) & 0xffff;
+ iadev->tx_cell_cnt += readw(iadev->seg_reg + CELL_CTR_LO_AUTO)&0xffff;
+ iadev->tx_cell_cnt += (readw(iadev->seg_reg+CELL_CTR_HIGH_AUTO)&0xffff)<<16;
+ return;
+}
+
+static void ia_led_timer(unsigned long arg) {
+ unsigned long flags;
+ static u_char blinking[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ u_char i;
+ static u32 ctrl_reg;
+ for (i = 0; i < iadev_count; i++) {
+ if (ia_dev[i]) {
+ ctrl_reg = readl(ia_dev[i]->reg+IPHASE5575_BUS_CONTROL_REG);
+ if (blinking[i] == 0) {
+ blinking[i]++;
+ ctrl_reg &= (~CTRL_LED);
+ writel(ctrl_reg, ia_dev[i]->reg+IPHASE5575_BUS_CONTROL_REG);
+ ia_update_stats(ia_dev[i]);
+ }
+ else {
+ blinking[i] = 0;
+ ctrl_reg |= CTRL_LED;
+ writel(ctrl_reg, ia_dev[i]->reg+IPHASE5575_BUS_CONTROL_REG);
+ spin_lock_irqsave(&ia_dev[i]->tx_lock, flags);
+ if (ia_dev[i]->close_pending)
+ wake_up(&ia_dev[i]->close_wait);
+ ia_tx_poll(ia_dev[i]);
+ spin_unlock_irqrestore(&ia_dev[i]->tx_lock, flags);
+ }
+ }
+ }
+ mod_timer(&ia_timer, jiffies + HZ / 4);
+ return;
+}
+
+static void ia_phy_put(struct atm_dev *dev, unsigned char value,
+ unsigned long addr)
+{
+ writel(value, INPH_IA_DEV(dev)->phy+addr);
+}
+
+static unsigned char ia_phy_get(struct atm_dev *dev, unsigned long addr)
+{
+ return readl(INPH_IA_DEV(dev)->phy+addr);
+}
+
+static void ia_free_tx(IADEV *iadev)
+{
+ int i;
+
+ kfree(iadev->desc_tbl);
+ for (i = 0; i < iadev->num_vc; i++)
+ kfree(iadev->testTable[i]);
+ kfree(iadev->testTable);
+ for (i = 0; i < iadev->num_tx_desc; i++) {
+ struct cpcs_trailer_desc *desc = iadev->tx_buf + i;
+
+ dma_unmap_single(&iadev->pci->dev, desc->dma_addr,
+ sizeof(*desc->cpcs), DMA_TO_DEVICE);
+ kfree(desc->cpcs);
+ }
+ kfree(iadev->tx_buf);
+ dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->tx_dle_q.start,
+ iadev->tx_dle_dma);
+}
+
+static void ia_free_rx(IADEV *iadev)
+{
+ kfree(iadev->rx_open);
+ dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->rx_dle_q.start,
+ iadev->rx_dle_dma);
+}
+
+static int ia_start(struct atm_dev *dev)
+{
+ IADEV *iadev;
+ int error;
+ unsigned char phy;
+ u32 ctrl_reg;
+ IF_EVENT(printk(">ia_start\n");)
+ iadev = INPH_IA_DEV(dev);
+ if (request_irq(iadev->irq, &ia_int, IRQF_SHARED, DEV_LABEL, dev)) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
+ dev->number, iadev->irq);
+ error = -EAGAIN;
+ goto err_out;
+ }
+ /* @@@ should release IRQ on error */
+ /* enabling memory + master */
+ if ((error = pci_write_config_word(iadev->pci,
+ PCI_COMMAND,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER )))
+ {
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+"
+ "master (0x%x)\n",dev->number, error);
+ error = -EIO;
+ goto err_free_irq;
+ }
+ udelay(10);
+
+ /* Maybe we should reset the front end, initialize Bus Interface Control
+ Registers and see. */
+
+ IF_INIT(printk("Bus ctrl reg: %08x\n",
+ readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG));)
+ ctrl_reg = readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG);
+ ctrl_reg = (ctrl_reg & (CTRL_LED | CTRL_FE_RST))
+ | CTRL_B8
+ | CTRL_B16
+ | CTRL_B32
+ | CTRL_B48
+ | CTRL_B64
+ | CTRL_B128
+ | CTRL_ERRMASK
+ | CTRL_DLETMASK /* shud be removed l8r */
+ | CTRL_DLERMASK
+ | CTRL_SEGMASK
+ | CTRL_REASSMASK
+ | CTRL_FEMASK
+ | CTRL_CSPREEMPT;
+
+ writel(ctrl_reg, iadev->reg+IPHASE5575_BUS_CONTROL_REG);
+
+ IF_INIT(printk("Bus ctrl reg after initializing: %08x\n",
+ readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG));
+ printk("Bus status reg after init: %08x\n",
+ readl(iadev->reg+IPHASE5575_BUS_STATUS_REG));)
+
+ ia_hw_type(iadev);
+ error = tx_init(dev);
+ if (error)
+ goto err_free_irq;
+ error = rx_init(dev);
+ if (error)
+ goto err_free_tx;
+
+ ctrl_reg = readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG);
+ writel(ctrl_reg | CTRL_FE_RST, iadev->reg+IPHASE5575_BUS_CONTROL_REG);
+ IF_INIT(printk("Bus ctrl reg after initializing: %08x\n",
+ readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG));)
+ phy = 0; /* resolve compiler complaint */
+ IF_INIT (
+ if ((phy=ia_phy_get(dev,0)) == 0x30)
+ printk("IA: pm5346,rev.%d\n",phy&0x0f);
+ else
+ printk("IA: utopia,rev.%0x\n",phy);)
+
+ if (iadev->phy_type & FE_25MBIT_PHY)
+ ia_mb25_init(iadev);
+ else if (iadev->phy_type & (FE_DS3_PHY | FE_E3_PHY))
+ ia_suni_pm7345_init(iadev);
+ else {
+ error = suni_init(dev);
+ if (error)
+ goto err_free_rx;
+ if (dev->phy->start) {
+ error = dev->phy->start(dev);
+ if (error)
+ goto err_free_rx;
+ }
+ /* Get iadev->carrier_detect status */
+ ia_frontend_intr(iadev);
+ }
+ return 0;
+
+err_free_rx:
+ ia_free_rx(iadev);
+err_free_tx:
+ ia_free_tx(iadev);
+err_free_irq:
+ free_irq(iadev->irq, dev);
+err_out:
+ return error;
+}
+
+static void ia_close(struct atm_vcc *vcc)
+{
+ DEFINE_WAIT(wait);
+ u16 *vc_table;
+ IADEV *iadev;
+ struct ia_vcc *ia_vcc;
+ struct sk_buff *skb = NULL;
+ struct sk_buff_head tmp_tx_backlog, tmp_vcc_backlog;
+ unsigned long closetime, flags;
+
+ iadev = INPH_IA_DEV(vcc->dev);
+ ia_vcc = INPH_IA_VCC(vcc);
+ if (!ia_vcc) return;
+
+ IF_EVENT(printk("ia_close: ia_vcc->vc_desc_cnt = %d vci = %d\n",
+ ia_vcc->vc_desc_cnt,vcc->vci);)
+ clear_bit(ATM_VF_READY,&vcc->flags);
+ skb_queue_head_init (&tmp_tx_backlog);
+ skb_queue_head_init (&tmp_vcc_backlog);
+ if (vcc->qos.txtp.traffic_class != ATM_NONE) {
+ iadev->close_pending++;
+ prepare_to_wait(&iadev->timeout_wait, &wait, TASK_UNINTERRUPTIBLE);
+ schedule_timeout(50);
+ finish_wait(&iadev->timeout_wait, &wait);
+ spin_lock_irqsave(&iadev->tx_lock, flags);
+ while((skb = skb_dequeue(&iadev->tx_backlog))) {
+ if (ATM_SKB(skb)->vcc == vcc){
+ if (vcc->pop) vcc->pop(vcc, skb);
+ else dev_kfree_skb_any(skb);
+ }
+ else
+ skb_queue_tail(&tmp_tx_backlog, skb);
+ }
+ while((skb = skb_dequeue(&tmp_tx_backlog)))
+ skb_queue_tail(&iadev->tx_backlog, skb);
+ IF_EVENT(printk("IA TX Done decs_cnt = %d\n", ia_vcc->vc_desc_cnt);)
+ closetime = 300000 / ia_vcc->pcr;
+ if (closetime == 0)
+ closetime = 1;
+ spin_unlock_irqrestore(&iadev->tx_lock, flags);
+ wait_event_timeout(iadev->close_wait, (ia_vcc->vc_desc_cnt <= 0), closetime);
+ spin_lock_irqsave(&iadev->tx_lock, flags);
+ iadev->close_pending--;
+ iadev->testTable[vcc->vci]->lastTime = 0;
+ iadev->testTable[vcc->vci]->fract = 0;
+ iadev->testTable[vcc->vci]->vc_status = VC_UBR;
+ if (vcc->qos.txtp.traffic_class == ATM_ABR) {
+ if (vcc->qos.txtp.min_pcr > 0)
+ iadev->sum_mcr -= vcc->qos.txtp.min_pcr;
+ }
+ if (vcc->qos.txtp.traffic_class == ATM_CBR) {
+ ia_vcc = INPH_IA_VCC(vcc);
+ iadev->sum_mcr -= ia_vcc->NumCbrEntry*iadev->Granularity;
+ ia_cbrVc_close (vcc);
+ }
+ spin_unlock_irqrestore(&iadev->tx_lock, flags);
+ }
+
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ // reset reass table
+ vc_table = (u16 *)(iadev->reass_ram+REASS_TABLE*iadev->memSize);
+ vc_table += vcc->vci;
+ *vc_table = NO_AAL5_PKT;
+ // reset vc table
+ vc_table = (u16 *)(iadev->reass_ram+RX_VC_TABLE*iadev->memSize);
+ vc_table += vcc->vci;
+ *vc_table = (vcc->vci << 6) | 15;
+ if (vcc->qos.rxtp.traffic_class == ATM_ABR) {
+ struct abr_vc_table __iomem *abr_vc_table =
+ (iadev->reass_ram+ABR_VC_TABLE*iadev->memSize);
+ abr_vc_table += vcc->vci;
+ abr_vc_table->rdf = 0x0003;
+ abr_vc_table->air = 0x5eb1;
+ }
+ // Drain the packets
+ rx_dle_intr(vcc->dev);
+ iadev->rx_open[vcc->vci] = NULL;
+ }
+ kfree(INPH_IA_VCC(vcc));
+ ia_vcc = NULL;
+ vcc->dev_data = NULL;
+ clear_bit(ATM_VF_ADDR,&vcc->flags);
+ return;
+}
+
+static int ia_open(struct atm_vcc *vcc)
+{
+ struct ia_vcc *ia_vcc;
+ int error;
+ if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))
+ {
+ IF_EVENT(printk("ia: not partially allocated resources\n");)
+ vcc->dev_data = NULL;
+ }
+ if (vcc->vci != ATM_VPI_UNSPEC && vcc->vpi != ATM_VCI_UNSPEC)
+ {
+ IF_EVENT(printk("iphase open: unspec part\n");)
+ set_bit(ATM_VF_ADDR,&vcc->flags);
+ }
+ if (vcc->qos.aal != ATM_AAL5)
+ return -EINVAL;
+ IF_EVENT(printk(DEV_LABEL "(itf %d): open %d.%d\n",
+ vcc->dev->number, vcc->vpi, vcc->vci);)
+
+ /* Device dependent initialization */
+ ia_vcc = kmalloc(sizeof(*ia_vcc), GFP_KERNEL);
+ if (!ia_vcc) return -ENOMEM;
+ vcc->dev_data = ia_vcc;
+
+ if ((error = open_rx(vcc)))
+ {
+ IF_EVENT(printk("iadev: error in open_rx, closing\n");)
+ ia_close(vcc);
+ return error;
+ }
+
+ if ((error = open_tx(vcc)))
+ {
+ IF_EVENT(printk("iadev: error in open_tx, closing\n");)
+ ia_close(vcc);
+ return error;
+ }
+
+ set_bit(ATM_VF_READY,&vcc->flags);
+
+#if 0
+ {
+ static u8 first = 1;
+ if (first) {
+ ia_timer.expires = jiffies + 3*HZ;
+ add_timer(&ia_timer);
+ first = 0;
+ }
+ }
+#endif
+ IF_EVENT(printk("ia open returning\n");)
+ return 0;
+}
+
+static int ia_change_qos(struct atm_vcc *vcc, struct atm_qos *qos, int flags)
+{
+ IF_EVENT(printk(">ia_change_qos\n");)
+ return 0;
+}
+
+static int ia_ioctl(struct atm_dev *dev, unsigned int cmd, void __user *arg)
+{
+ IA_CMDBUF ia_cmds;
+ IADEV *iadev;
+ int i, board;
+ u16 __user *tmps;
+ IF_EVENT(printk(">ia_ioctl\n");)
+ if (cmd != IA_CMD) {
+ if (!dev->phy->ioctl) return -EINVAL;
+ return dev->phy->ioctl(dev,cmd,arg);
+ }
+ if (copy_from_user(&ia_cmds, arg, sizeof ia_cmds)) return -EFAULT;
+ board = ia_cmds.status;
+ if ((board < 0) || (board > iadev_count))
+ board = 0;
+ iadev = ia_dev[board];
+ switch (ia_cmds.cmd) {
+ case MEMDUMP:
+ {
+ switch (ia_cmds.sub_cmd) {
+ case MEMDUMP_DEV:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ if (copy_to_user(ia_cmds.buf, iadev, sizeof(IADEV)))
+ return -EFAULT;
+ ia_cmds.status = 0;
+ break;
+ case MEMDUMP_SEGREG:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ tmps = (u16 __user *)ia_cmds.buf;
+ for(i=0; i<0x80; i+=2, tmps++)
+ if(put_user((u16)(readl(iadev->seg_reg+i) & 0xffff), tmps)) return -EFAULT;
+ ia_cmds.status = 0;
+ ia_cmds.len = 0x80;
+ break;
+ case MEMDUMP_REASSREG:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ tmps = (u16 __user *)ia_cmds.buf;
+ for(i=0; i<0x80; i+=2, tmps++)
+ if(put_user((u16)(readl(iadev->reass_reg+i) & 0xffff), tmps)) return -EFAULT;
+ ia_cmds.status = 0;
+ ia_cmds.len = 0x80;
+ break;
+ case MEMDUMP_FFL:
+ {
+ ia_regs_t *regs_local;
+ ffredn_t *ffL;
+ rfredn_t *rfL;
+
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ regs_local = kmalloc(sizeof(*regs_local), GFP_KERNEL);
+ if (!regs_local) return -ENOMEM;
+ ffL = &regs_local->ffredn;
+ rfL = &regs_local->rfredn;
+ /* Copy real rfred registers into the local copy */
+ for (i=0; i<(sizeof (rfredn_t))/4; i++)
+ ((u_int *)rfL)[i] = readl(iadev->reass_reg + i) & 0xffff;
+ /* Copy real ffred registers into the local copy */
+ for (i=0; i<(sizeof (ffredn_t))/4; i++)
+ ((u_int *)ffL)[i] = readl(iadev->seg_reg + i) & 0xffff;
+
+ if (copy_to_user(ia_cmds.buf, regs_local,sizeof(ia_regs_t))) {
+ kfree(regs_local);
+ return -EFAULT;
+ }
+ kfree(regs_local);
+ printk("Board %d registers dumped\n", board);
+ ia_cmds.status = 0;
+ }
+ break;
+ case READ_REG:
+ {
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ desc_dbg(iadev);
+ ia_cmds.status = 0;
+ }
+ break;
+ case 0x6:
+ {
+ ia_cmds.status = 0;
+ printk("skb = 0x%lx\n", (long)skb_peek(&iadev->tx_backlog));
+ printk("rtn_q: 0x%lx\n",(long)ia_deque_rtn_q(&iadev->tx_return_q));
+ }
+ break;
+ case 0x8:
+ {
+ struct k_sonet_stats *stats;
+ stats = &PRIV(_ia_dev[board])->sonet_stats;
+ printk("section_bip: %d\n", atomic_read(&stats->section_bip));
+ printk("line_bip : %d\n", atomic_read(&stats->line_bip));
+ printk("path_bip : %d\n", atomic_read(&stats->path_bip));
+ printk("line_febe : %d\n", atomic_read(&stats->line_febe));
+ printk("path_febe : %d\n", atomic_read(&stats->path_febe));
+ printk("corr_hcs : %d\n", atomic_read(&stats->corr_hcs));
+ printk("uncorr_hcs : %d\n", atomic_read(&stats->uncorr_hcs));
+ printk("tx_cells : %d\n", atomic_read(&stats->tx_cells));
+ printk("rx_cells : %d\n", atomic_read(&stats->rx_cells));
+ }
+ ia_cmds.status = 0;
+ break;
+ case 0x9:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ for (i = 1; i <= iadev->num_rx_desc; i++)
+ free_desc(_ia_dev[board], i);
+ writew( ~(RX_FREEQ_EMPT | RX_EXCP_RCVD),
+ iadev->reass_reg+REASS_MASK_REG);
+ iadev->rxing = 1;
+
+ ia_cmds.status = 0;
+ break;
+
+ case 0xb:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ ia_frontend_intr(iadev);
+ break;
+ case 0xa:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ {
+ ia_cmds.status = 0;
+ IADebugFlag = ia_cmds.maddr;
+ printk("New debug option loaded\n");
+ }
+ break;
+ default:
+ ia_cmds.status = 0;
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+
+ }
+ return 0;
+}
+
+static int ia_getsockopt(struct atm_vcc *vcc, int level, int optname,
+ void __user *optval, int optlen)
+{
+ IF_EVENT(printk(">ia_getsockopt\n");)
+ return -EINVAL;
+}
+
+static int ia_setsockopt(struct atm_vcc *vcc, int level, int optname,
+ void __user *optval, unsigned int optlen)
+{
+ IF_EVENT(printk(">ia_setsockopt\n");)
+ return -EINVAL;
+}
+
+static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb) {
+ IADEV *iadev;
+ struct dle *wr_ptr;
+ struct tx_buf_desc __iomem *buf_desc_ptr;
+ int desc;
+ int comp_code;
+ int total_len;
+ struct cpcs_trailer *trailer;
+ struct ia_vcc *iavcc;
+
+ iadev = INPH_IA_DEV(vcc->dev);
+ iavcc = INPH_IA_VCC(vcc);
+ if (!iavcc->txing) {
+ printk("discard packet on closed VC\n");
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ if (skb->len > iadev->tx_buf_sz - 8) {
+ printk("Transmit size over tx buffer size\n");
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+ if ((unsigned long)skb->data & 3) {
+ printk("Misaligned SKB\n");
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+ /* Get a descriptor number from our free descriptor queue
+ We get the descr number from the TCQ now, since I am using
+ the TCQ as a free buffer queue. Initially TCQ will be
+ initialized with all the descriptors and is hence, full.
+ */
+ desc = get_desc (iadev, iavcc);
+ if (desc == 0xffff)
+ return 1;
+ comp_code = desc >> 13;
+ desc &= 0x1fff;
+
+ if ((desc == 0) || (desc > iadev->num_tx_desc))
+ {
+ IF_ERR(printk(DEV_LABEL "invalid desc for send: %d\n", desc);)
+ atomic_inc(&vcc->stats->tx);
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+ return 0; /* return SUCCESS */
+ }
+
+ if (comp_code)
+ {
+ IF_ERR(printk(DEV_LABEL "send desc:%d completion code %d error\n",
+ desc, comp_code);)
+ }
+
+ /* remember the desc and vcc mapping */
+ iavcc->vc_desc_cnt++;
+ iadev->desc_tbl[desc-1].iavcc = iavcc;
+ iadev->desc_tbl[desc-1].txskb = skb;
+ IA_SKB_STATE(skb) = 0;
+
+ iadev->ffL.tcq_rd += 2;
+ if (iadev->ffL.tcq_rd > iadev->ffL.tcq_ed)
+ iadev->ffL.tcq_rd = iadev->ffL.tcq_st;
+ writew(iadev->ffL.tcq_rd, iadev->seg_reg+TCQ_RD_PTR);
+
+ /* Put the descriptor number in the packet ready queue
+ and put the updated write pointer in the DLE field
+ */
+ *(u16*)(iadev->seg_ram+iadev->ffL.prq_wr) = desc;
+
+ iadev->ffL.prq_wr += 2;
+ if (iadev->ffL.prq_wr > iadev->ffL.prq_ed)
+ iadev->ffL.prq_wr = iadev->ffL.prq_st;
+
+ /* Figure out the exact length of the packet and padding required to
+ make it aligned on a 48 byte boundary. */
+ total_len = skb->len + sizeof(struct cpcs_trailer);
+ total_len = ((total_len + 47) / 48) * 48;
+ IF_TX(printk("ia packet len:%d padding:%d\n", total_len, total_len - skb->len);)
+
+ /* Put the packet in a tx buffer */
+ trailer = iadev->tx_buf[desc-1].cpcs;
+ IF_TX(printk("Sent: skb = 0x%p skb->data: 0x%p len: %d, desc: %d\n",
+ skb, skb->data, skb->len, desc);)
+ trailer->control = 0;
+ /*big endian*/
+ trailer->length = ((skb->len & 0xff) << 8) | ((skb->len & 0xff00) >> 8);
+ trailer->crc32 = 0; /* not needed - dummy bytes */
+
+ /* Display the packet */
+ IF_TXPKT(printk("Sent data: len = %d MsgNum = %d\n",
+ skb->len, tcnter++);
+ xdump(skb->data, skb->len, "TX: ");
+ printk("\n");)
+
+ /* Build the buffer descriptor */
+ buf_desc_ptr = iadev->seg_ram+TX_DESC_BASE;
+ buf_desc_ptr += desc; /* points to the corresponding entry */
+ buf_desc_ptr->desc_mode = AAL5 | EOM_EN | APP_CRC32 | CMPL_INT;
+ /* Huh ? p.115 of users guide describes this as a read-only register */
+ writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG);
+ buf_desc_ptr->vc_index = vcc->vci;
+ buf_desc_ptr->bytes = total_len;
+
+ if (vcc->qos.txtp.traffic_class == ATM_ABR)
+ clear_lockup (vcc, iadev);
+
+ /* Build the DLE structure */
+ wr_ptr = iadev->tx_dle_q.write;
+ memset((caddr_t)wr_ptr, 0, sizeof(*wr_ptr));
+ wr_ptr->sys_pkt_addr = dma_map_single(&iadev->pci->dev, skb->data,
+ skb->len, DMA_TO_DEVICE);
+ wr_ptr->local_pkt_addr = (buf_desc_ptr->buf_start_hi << 16) |
+ buf_desc_ptr->buf_start_lo;
+ /* wr_ptr->bytes = swap_byte_order(total_len); didn't seem to affect?? */
+ wr_ptr->bytes = skb->len;
+
+ /* hw bug - DLEs of 0x2d, 0x2e, 0x2f cause DMA lockup */
+ if ((wr_ptr->bytes >> 2) == 0xb)
+ wr_ptr->bytes = 0x30;
+
+ wr_ptr->mode = TX_DLE_PSI;
+ wr_ptr->prq_wr_ptr_data = 0;
+
+ /* end is not to be used for the DLE q */
+ if (++wr_ptr == iadev->tx_dle_q.end)
+ wr_ptr = iadev->tx_dle_q.start;
+
+ /* Build trailer dle */
+ wr_ptr->sys_pkt_addr = iadev->tx_buf[desc-1].dma_addr;
+ wr_ptr->local_pkt_addr = ((buf_desc_ptr->buf_start_hi << 16) |
+ buf_desc_ptr->buf_start_lo) + total_len - sizeof(struct cpcs_trailer);
+
+ wr_ptr->bytes = sizeof(struct cpcs_trailer);
+ wr_ptr->mode = DMA_INT_ENABLE;
+ wr_ptr->prq_wr_ptr_data = iadev->ffL.prq_wr;
+
+ /* end is not to be used for the DLE q */
+ if (++wr_ptr == iadev->tx_dle_q.end)
+ wr_ptr = iadev->tx_dle_q.start;
+
+ iadev->tx_dle_q.write = wr_ptr;
+ ATM_DESC(skb) = vcc->vci;
+ skb_queue_tail(&iadev->tx_dma_q, skb);
+
+ atomic_inc(&vcc->stats->tx);
+ iadev->tx_pkt_cnt++;
+ /* Increment transaction counter */
+ writel(2, iadev->dma+IPHASE5575_TX_COUNTER);
+
+#if 0
+ /* add flow control logic */
+ if (atomic_read(&vcc->stats->tx) % 20 == 0) {
+ if (iavcc->vc_desc_cnt > 10) {
+ vcc->tx_quota = vcc->tx_quota * 3 / 4;
+ printk("Tx1: vcc->tx_quota = %d \n", (u32)vcc->tx_quota );
+ iavcc->flow_inc = -1;
+ iavcc->saved_tx_quota = vcc->tx_quota;
+ } else if ((iavcc->flow_inc < 0) && (iavcc->vc_desc_cnt < 3)) {
+ // vcc->tx_quota = 3 * iavcc->saved_tx_quota / 4;
+ printk("Tx2: vcc->tx_quota = %d \n", (u32)vcc->tx_quota );
+ iavcc->flow_inc = 0;
+ }
+ }
+#endif
+ IF_TX(printk("ia send done\n");)
+ return 0;
+}
+
+static int ia_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ IADEV *iadev;
+ unsigned long flags;
+
+ iadev = INPH_IA_DEV(vcc->dev);
+ if ((!skb)||(skb->len>(iadev->tx_buf_sz-sizeof(struct cpcs_trailer))))
+ {
+ if (!skb)
+ printk(KERN_CRIT "null skb in ia_send\n");
+ else dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&iadev->tx_lock, flags);
+ if (!test_bit(ATM_VF_READY,&vcc->flags)){
+ dev_kfree_skb_any(skb);
+ spin_unlock_irqrestore(&iadev->tx_lock, flags);
+ return -EINVAL;
+ }
+ ATM_SKB(skb)->vcc = vcc;
+
+ if (skb_peek(&iadev->tx_backlog)) {
+ skb_queue_tail(&iadev->tx_backlog, skb);
+ }
+ else {
+ if (ia_pkt_tx (vcc, skb)) {
+ skb_queue_tail(&iadev->tx_backlog, skb);
+ }
+ }
+ spin_unlock_irqrestore(&iadev->tx_lock, flags);
+ return 0;
+
+}
+
+static int ia_proc_read(struct atm_dev *dev,loff_t *pos,char *page)
+{
+ int left = *pos, n;
+ char *tmpPtr;
+ IADEV *iadev = INPH_IA_DEV(dev);
+ if(!left--) {
+ if (iadev->phy_type == FE_25MBIT_PHY) {
+ n = sprintf(page, " Board Type : Iphase5525-1KVC-128K\n");
+ return n;
+ }
+ if (iadev->phy_type == FE_DS3_PHY)
+ n = sprintf(page, " Board Type : Iphase-ATM-DS3");
+ else if (iadev->phy_type == FE_E3_PHY)
+ n = sprintf(page, " Board Type : Iphase-ATM-E3");
+ else if (iadev->phy_type == FE_UTP_OPTION)
+ n = sprintf(page, " Board Type : Iphase-ATM-UTP155");
+ else
+ n = sprintf(page, " Board Type : Iphase-ATM-OC3");
+ tmpPtr = page + n;
+ if (iadev->pci_map_size == 0x40000)
+ n += sprintf(tmpPtr, "-1KVC-");
+ else
+ n += sprintf(tmpPtr, "-4KVC-");
+ tmpPtr = page + n;
+ if ((iadev->memType & MEM_SIZE_MASK) == MEM_SIZE_1M)
+ n += sprintf(tmpPtr, "1M \n");
+ else if ((iadev->memType & MEM_SIZE_MASK) == MEM_SIZE_512K)
+ n += sprintf(tmpPtr, "512K\n");
+ else
+ n += sprintf(tmpPtr, "128K\n");
+ return n;
+ }
+ if (!left) {
+ return sprintf(page, " Number of Tx Buffer: %u\n"
+ " Size of Tx Buffer : %u\n"
+ " Number of Rx Buffer: %u\n"
+ " Size of Rx Buffer : %u\n"
+ " Packets Receiverd : %u\n"
+ " Packets Transmitted: %u\n"
+ " Cells Received : %u\n"
+ " Cells Transmitted : %u\n"
+ " Board Dropped Cells: %u\n"
+ " Board Dropped Pkts : %u\n",
+ iadev->num_tx_desc, iadev->tx_buf_sz,
+ iadev->num_rx_desc, iadev->rx_buf_sz,
+ iadev->rx_pkt_cnt, iadev->tx_pkt_cnt,
+ iadev->rx_cell_cnt, iadev->tx_cell_cnt,
+ iadev->drop_rxcell, iadev->drop_rxpkt);
+ }
+ return 0;
+}
+
+static const struct atmdev_ops ops = {
+ .open = ia_open,
+ .close = ia_close,
+ .ioctl = ia_ioctl,
+ .getsockopt = ia_getsockopt,
+ .setsockopt = ia_setsockopt,
+ .send = ia_send,
+ .phy_put = ia_phy_put,
+ .phy_get = ia_phy_get,
+ .change_qos = ia_change_qos,
+ .proc_read = ia_proc_read,
+ .owner = THIS_MODULE,
+};
+
+static int ia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct atm_dev *dev;
+ IADEV *iadev;
+ int ret;
+
+ iadev = kzalloc(sizeof(*iadev), GFP_KERNEL);
+ if (!iadev) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ iadev->pci = pdev;
+
+ IF_INIT(printk("ia detected at bus:%d dev: %d function:%d\n",
+ pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));)
+ if (pci_enable_device(pdev)) {
+ ret = -ENODEV;
+ goto err_out_free_iadev;
+ }
+ dev = atm_dev_register(DEV_LABEL, &pdev->dev, &ops, -1, NULL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_out_disable_dev;
+ }
+ dev->dev_data = iadev;
+ IF_INIT(printk(DEV_LABEL "registered at (itf :%d)\n", dev->number);)
+ IF_INIT(printk("dev_id = 0x%p iadev->LineRate = %d \n", dev,
+ iadev->LineRate);)
+
+ pci_set_drvdata(pdev, dev);
+
+ ia_dev[iadev_count] = iadev;
+ _ia_dev[iadev_count] = dev;
+ iadev_count++;
+ if (ia_init(dev) || ia_start(dev)) {
+ IF_INIT(printk("IA register failed!\n");)
+ iadev_count--;
+ ia_dev[iadev_count] = NULL;
+ _ia_dev[iadev_count] = NULL;
+ ret = -EINVAL;
+ goto err_out_deregister_dev;
+ }
+ IF_EVENT(printk("iadev_count = %d\n", iadev_count);)
+
+ iadev->next_board = ia_boards;
+ ia_boards = dev;
+
+ return 0;
+
+err_out_deregister_dev:
+ atm_dev_deregister(dev);
+err_out_disable_dev:
+ pci_disable_device(pdev);
+err_out_free_iadev:
+ kfree(iadev);
+err_out:
+ return ret;
+}
+
+static void ia_remove_one(struct pci_dev *pdev)
+{
+ struct atm_dev *dev = pci_get_drvdata(pdev);
+ IADEV *iadev = INPH_IA_DEV(dev);
+
+ /* Disable phy interrupts */
+ ia_phy_put(dev, ia_phy_get(dev, SUNI_RSOP_CIE) & ~(SUNI_RSOP_CIE_LOSE),
+ SUNI_RSOP_CIE);
+ udelay(1);
+
+ if (dev->phy && dev->phy->stop)
+ dev->phy->stop(dev);
+
+ /* De-register device */
+ free_irq(iadev->irq, dev);
+ iadev_count--;
+ ia_dev[iadev_count] = NULL;
+ _ia_dev[iadev_count] = NULL;
+ IF_EVENT(printk("deregistering iav at (itf:%d)\n", dev->number);)
+ atm_dev_deregister(dev);
+
+ iounmap(iadev->base);
+ pci_disable_device(pdev);
+
+ ia_free_rx(iadev);
+ ia_free_tx(iadev);
+
+ kfree(iadev);
+}
+
+static struct pci_device_id ia_pci_tbl[] = {
+ { PCI_VENDOR_ID_IPHASE, 0x0008, PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_IPHASE, 0x0009, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0,}
+};
+MODULE_DEVICE_TABLE(pci, ia_pci_tbl);
+
+static struct pci_driver ia_driver = {
+ .name = DEV_LABEL,
+ .id_table = ia_pci_tbl,
+ .probe = ia_init_one,
+ .remove = ia_remove_one,
+};
+
+static int __init ia_module_init(void)
+{
+ int ret;
+
+ ret = pci_register_driver(&ia_driver);
+ if (ret >= 0) {
+ ia_timer.expires = jiffies + 3*HZ;
+ add_timer(&ia_timer);
+ } else
+ printk(KERN_ERR DEV_LABEL ": no adapter found\n");
+ return ret;
+}
+
+static void __exit ia_module_exit(void)
+{
+ pci_unregister_driver(&ia_driver);
+
+ del_timer(&ia_timer);
+}
+
+module_init(ia_module_init);
+module_exit(ia_module_exit);
diff --git a/drivers/atm/iphase.h b/drivers/atm/iphase.h
new file mode 100644
index 000000000..53ecac5a2
--- /dev/null
+++ b/drivers/atm/iphase.h
@@ -0,0 +1,1453 @@
+/******************************************************************************
+ Device driver for Interphase ATM PCI adapter cards
+ Author: Peter Wang <pwang@iphase.com>
+ Interphase Corporation <www.iphase.com>
+ Version: 1.0
+ iphase.h: This is the header file for iphase.c.
+*******************************************************************************
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on this skeleton fall under the GPL and must retain
+ the authorship (implicit copyright) notice.
+
+ 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.
+
+ Modified from an incomplete driver for Interphase 5575 1KVC 1M card which
+ was originally written by Monalisa Agrawal at UNH. Now this driver
+ supports a variety of varients of Interphase ATM PCI (i)Chip adapter
+ card family (See www.iphase.com/products/ClassSheet.cfm?ClassID=ATM)
+ in terms of PHY type, the size of control memory and the size of
+ packet memory. The followings are the change log and history:
+
+ Bugfix the Mona's UBR driver.
+ Modify the basic memory allocation and dma logic.
+ Port the driver to the latest kernel from 2.0.46.
+ Complete the ABR logic of the driver, and added the ABR work-
+ around for the hardware anormalies.
+ Add the CBR support.
+ Add the flow control logic to the driver to allow rate-limit VC.
+ Add 4K VC support to the board with 512K control memory.
+ Add the support of all the variants of the Interphase ATM PCI
+ (i)Chip adapter cards including x575 (155M OC3 and UTP155), x525
+ (25M UTP25) and x531 (DS3 and E3).
+ Add SMP support.
+
+ Support and updates available at: ftp://ftp.iphase.com/pub/atm
+
+*******************************************************************************/
+
+#ifndef IPHASE_H
+#define IPHASE_H
+
+
+/************************ IADBG DEFINE *********************************/
+/* IADebugFlag Bit Map */
+#define IF_IADBG_INIT_ADAPTER 0x00000001 // init adapter info
+#define IF_IADBG_TX 0x00000002 // debug TX
+#define IF_IADBG_RX 0x00000004 // debug RX
+#define IF_IADBG_QUERY_INFO 0x00000008 // debug Request call
+#define IF_IADBG_SHUTDOWN 0x00000010 // debug shutdown event
+#define IF_IADBG_INTR 0x00000020 // debug interrupt DPC
+#define IF_IADBG_TXPKT 0x00000040 // debug TX PKT
+#define IF_IADBG_RXPKT 0x00000080 // debug RX PKT
+#define IF_IADBG_ERR 0x00000100 // debug system error
+#define IF_IADBG_EVENT 0x00000200 // debug event
+#define IF_IADBG_DIS_INTR 0x00001000 // debug disable interrupt
+#define IF_IADBG_EN_INTR 0x00002000 // debug enable interrupt
+#define IF_IADBG_LOUD 0x00004000 // debugging info
+#define IF_IADBG_VERY_LOUD 0x00008000 // excessive debugging info
+#define IF_IADBG_CBR 0x00100000 //
+#define IF_IADBG_UBR 0x00200000 //
+#define IF_IADBG_ABR 0x00400000 //
+#define IF_IADBG_DESC 0x01000000 //
+#define IF_IADBG_SUNI_STAT 0x02000000 // suni statistics
+#define IF_IADBG_RESET 0x04000000
+
+#define IF_IADBG(f) if (IADebugFlag & (f))
+
+#ifdef CONFIG_ATM_IA_DEBUG /* Debug build */
+
+#define IF_LOUD(A) IF_IADBG(IF_IADBG_LOUD) { A }
+#define IF_ERR(A) IF_IADBG(IF_IADBG_ERR) { A }
+#define IF_VERY_LOUD(A) IF_IADBG( IF_IADBG_VERY_LOUD ) { A }
+
+#define IF_INIT_ADAPTER(A) IF_IADBG( IF_IADBG_INIT_ADAPTER ) { A }
+#define IF_INIT(A) IF_IADBG( IF_IADBG_INIT_ADAPTER ) { A }
+#define IF_SUNI_STAT(A) IF_IADBG( IF_IADBG_SUNI_STAT ) { A }
+#define IF_QUERY_INFO(A) IF_IADBG( IF_IADBG_QUERY_INFO ) { A }
+#define IF_COPY_OVER(A) IF_IADBG( IF_IADBG_COPY_OVER ) { A }
+
+#define IF_INTR(A) IF_IADBG( IF_IADBG_INTR ) { A }
+#define IF_DIS_INTR(A) IF_IADBG( IF_IADBG_DIS_INTR ) { A }
+#define IF_EN_INTR(A) IF_IADBG( IF_IADBG_EN_INTR ) { A }
+
+#define IF_TX(A) IF_IADBG( IF_IADBG_TX ) { A }
+#define IF_RX(A) IF_IADBG( IF_IADBG_RX ) { A }
+#define IF_TXPKT(A) IF_IADBG( IF_IADBG_TXPKT ) { A }
+#define IF_RXPKT(A) IF_IADBG( IF_IADBG_RXPKT ) { A }
+
+#define IF_SHUTDOWN(A) IF_IADBG(IF_IADBG_SHUTDOWN) { A }
+#define IF_CBR(A) IF_IADBG( IF_IADBG_CBR ) { A }
+#define IF_UBR(A) IF_IADBG( IF_IADBG_UBR ) { A }
+#define IF_ABR(A) IF_IADBG( IF_IADBG_ABR ) { A }
+#define IF_EVENT(A) IF_IADBG( IF_IADBG_EVENT) { A }
+
+#else /* free build */
+#define IF_LOUD(A)
+#define IF_VERY_LOUD(A)
+#define IF_INIT_ADAPTER(A)
+#define IF_INIT(A)
+#define IF_SUNI_STAT(A)
+#define IF_PVC_CHKPKT(A)
+#define IF_QUERY_INFO(A)
+#define IF_COPY_OVER(A)
+#define IF_HANG(A)
+#define IF_INTR(A)
+#define IF_DIS_INTR(A)
+#define IF_EN_INTR(A)
+#define IF_TX(A)
+#define IF_RX(A)
+#define IF_TXDEBUG(A)
+#define IF_VC(A)
+#define IF_ERR(A)
+#define IF_CBR(A)
+#define IF_UBR(A)
+#define IF_ABR(A)
+#define IF_SHUTDOWN(A)
+#define DbgPrint(A)
+#define IF_EVENT(A)
+#define IF_TXPKT(A)
+#define IF_RXPKT(A)
+#endif /* CONFIG_ATM_IA_DEBUG */
+
+#define isprint(a) ((a >=' ')&&(a <= '~'))
+#define ATM_DESC(skb) (skb->protocol)
+#define IA_SKB_STATE(skb) (skb->protocol)
+#define IA_DLED 1
+#define IA_TX_DONE 2
+
+/* iadbg defines */
+#define IA_CMD 0x7749
+typedef struct {
+ int cmd;
+ int sub_cmd;
+ int len;
+ u32 maddr;
+ int status;
+ void __user *buf;
+} IA_CMDBUF, *PIA_CMDBUF;
+
+/* cmds */
+#define MEMDUMP 0x01
+
+/* sub_cmds */
+#define MEMDUMP_SEGREG 0x2
+#define MEMDUMP_DEV 0x1
+#define MEMDUMP_REASSREG 0x3
+#define MEMDUMP_FFL 0x4
+#define READ_REG 0x5
+#define WAKE_DBG_WAIT 0x6
+
+/************************ IADBG DEFINE END ***************************/
+
+#define Boolean(x) ((x) ? 1 : 0)
+#define NR_VCI 1024 /* number of VCIs */
+#define NR_VCI_LD 10 /* log2(NR_VCI) */
+#define NR_VCI_4K 4096 /* number of VCIs */
+#define NR_VCI_4K_LD 12 /* log2(NR_VCI) */
+#define MEM_VALID 0xfffffff0 /* mask base address with this */
+
+#ifndef PCI_VENDOR_ID_IPHASE
+#define PCI_VENDOR_ID_IPHASE 0x107e
+#endif
+#ifndef PCI_DEVICE_ID_IPHASE_5575
+#define PCI_DEVICE_ID_IPHASE_5575 0x0008
+#endif
+#define DEV_LABEL "ia"
+#define PCR 207692
+#define ICR 100000
+#define MCR 0
+#define TBE 1000
+#define FRTT 1
+#define RIF 2
+#define RDF 4
+#define NRMCODE 5 /* 0 - 7 */
+#define TRMCODE 3 /* 0 - 7 */
+#define CDFCODE 6
+#define ATDFCODE 2 /* 0 - 15 */
+
+/*---------------------- Packet/Cell Memory ------------------------*/
+#define TX_PACKET_RAM 0x00000 /* start of Trasnmit Packet memory - 0 */
+#define DFL_TX_BUF_SZ 10240 /* 10 K buffers */
+#define DFL_TX_BUFFERS 50 /* number of packet buffers for Tx
+ - descriptor 0 unused */
+#define REASS_RAM_SIZE 0x10000 /* for 64K 1K VC board */
+#define RX_PACKET_RAM 0x80000 /* start of Receive Packet memory - 512K */
+#define DFL_RX_BUF_SZ 10240 /* 10k buffers */
+#define DFL_RX_BUFFERS 50 /* number of packet buffers for Rx
+ - descriptor 0 unused */
+
+struct cpcs_trailer
+{
+ u_short control;
+ u_short length;
+ u_int crc32;
+};
+
+struct cpcs_trailer_desc
+{
+ struct cpcs_trailer *cpcs;
+ dma_addr_t dma_addr;
+};
+
+struct ia_vcc
+{
+ int rxing;
+ int txing;
+ int NumCbrEntry;
+ u32 pcr;
+ u32 saved_tx_quota;
+ int flow_inc;
+ struct sk_buff_head txing_skb;
+ int ltimeout;
+ u8 vc_desc_cnt;
+
+};
+
+struct abr_vc_table
+{
+ u_char status;
+ u_char rdf;
+ u_short air;
+ u_int res[3];
+ u_int req_rm_cell_data1;
+ u_int req_rm_cell_data2;
+ u_int add_rm_cell_data1;
+ u_int add_rm_cell_data2;
+};
+
+/* 32 byte entries */
+struct main_vc
+{
+ u_short type;
+#define ABR 0x8000
+#define UBR 0xc000
+#define CBR 0x0000
+ /* ABR fields */
+ u_short nrm;
+ u_short trm;
+ u_short rm_timestamp_hi;
+ u_short rm_timestamp_lo:8,
+ crm:8;
+ u_short remainder; /* ABR and UBR fields - last 10 bits*/
+ u_short next_vc_sched;
+ u_short present_desc; /* all classes */
+ u_short last_cell_slot; /* ABR and UBR */
+ u_short pcr;
+ u_short fraction;
+ u_short icr;
+ u_short atdf;
+ u_short mcr;
+ u_short acr;
+ u_short unack:8,
+ status:8; /* all classes */
+#define UIOLI 0x80
+#define CRC_APPEND 0x40 /* for status field - CRC-32 append */
+#define ABR_STATE 0x02
+
+};
+
+
+/* 8 byte entries */
+struct ext_vc
+{
+ u_short atm_hdr1;
+ u_short atm_hdr2;
+ u_short last_desc;
+ u_short out_of_rate_link; /* reserved for UBR and CBR */
+};
+
+
+#define DLE_ENTRIES 256
+#define DMA_INT_ENABLE 0x0002 /* use for both Tx and Rx */
+#define TX_DLE_PSI 0x0001
+#define DLE_TOTAL_SIZE (sizeof(struct dle)*DLE_ENTRIES)
+
+/* Descriptor List Entries (DLE) */
+struct dle
+{
+ u32 sys_pkt_addr;
+ u32 local_pkt_addr;
+ u32 bytes;
+ u16 prq_wr_ptr_data;
+ u16 mode;
+};
+
+struct dle_q
+{
+ struct dle *start;
+ struct dle *end;
+ struct dle *read;
+ struct dle *write;
+};
+
+struct free_desc_q
+{
+ int desc; /* Descriptor number */
+ struct free_desc_q *next;
+};
+
+struct tx_buf_desc {
+ unsigned short desc_mode;
+ unsigned short vc_index;
+ unsigned short res1; /* reserved field */
+ unsigned short bytes;
+ unsigned short buf_start_hi;
+ unsigned short buf_start_lo;
+ unsigned short res2[10]; /* reserved field */
+};
+
+
+struct rx_buf_desc {
+ unsigned short desc_mode;
+ unsigned short vc_index;
+ unsigned short vpi;
+ unsigned short bytes;
+ unsigned short buf_start_hi;
+ unsigned short buf_start_lo;
+ unsigned short dma_start_hi;
+ unsigned short dma_start_lo;
+ unsigned short crc_upper;
+ unsigned short crc_lower;
+ unsigned short res:8, timeout:8;
+ unsigned short res2[5]; /* reserved field */
+};
+
+/*--------SAR stuff ---------------------*/
+
+#define EPROM_SIZE 0x40000 /* says 64K in the docs ??? */
+#define MAC1_LEN 4
+#define MAC2_LEN 2
+
+/*------------ PCI Memory Space Map, 128K SAR memory ----------------*/
+#define IPHASE5575_PCI_CONFIG_REG_BASE 0x0000
+#define IPHASE5575_BUS_CONTROL_REG_BASE 0x1000 /* offsets 0x00 - 0x3c */
+#define IPHASE5575_FRAG_CONTROL_REG_BASE 0x2000
+#define IPHASE5575_REASS_CONTROL_REG_BASE 0x3000
+#define IPHASE5575_DMA_CONTROL_REG_BASE 0x4000
+#define IPHASE5575_FRONT_END_REG_BASE IPHASE5575_DMA_CONTROL_REG_BASE
+#define IPHASE5575_FRAG_CONTROL_RAM_BASE 0x10000
+#define IPHASE5575_REASS_CONTROL_RAM_BASE 0x20000
+
+/*------------ Bus interface control registers -----------------*/
+#define IPHASE5575_BUS_CONTROL_REG 0x00
+#define IPHASE5575_BUS_STATUS_REG 0x01 /* actual offset 0x04 */
+#define IPHASE5575_MAC1 0x02
+#define IPHASE5575_REV 0x03
+#define IPHASE5575_MAC2 0x03 /*actual offset 0x0e-reg 0x0c*/
+#define IPHASE5575_EXT_RESET 0x04
+#define IPHASE5575_INT_RESET 0x05 /* addr 1c ?? reg 0x06 */
+#define IPHASE5575_PCI_ADDR_PAGE 0x07 /* reg 0x08, 0x09 ?? */
+#define IPHASE5575_EEPROM_ACCESS 0x0a /* actual offset 0x28 */
+#define IPHASE5575_CELL_FIFO_QUEUE_SZ 0x0b
+#define IPHASE5575_CELL_FIFO_MARK_STATE 0x0c
+#define IPHASE5575_CELL_FIFO_READ_PTR 0x0d
+#define IPHASE5575_CELL_FIFO_WRITE_PTR 0x0e
+#define IPHASE5575_CELL_FIFO_CELLS_AVL 0x0f /* actual offset 0x3c */
+
+/* Bus Interface Control Register bits */
+#define CTRL_FE_RST 0x80000000
+#define CTRL_LED 0x40000000
+#define CTRL_25MBPHY 0x10000000
+#define CTRL_ENCMBMEM 0x08000000
+#define CTRL_ENOFFSEG 0x01000000
+#define CTRL_ERRMASK 0x00400000
+#define CTRL_DLETMASK 0x00100000
+#define CTRL_DLERMASK 0x00080000
+#define CTRL_FEMASK 0x00040000
+#define CTRL_SEGMASK 0x00020000
+#define CTRL_REASSMASK 0x00010000
+#define CTRL_CSPREEMPT 0x00002000
+#define CTRL_B128 0x00000200
+#define CTRL_B64 0x00000100
+#define CTRL_B48 0x00000080
+#define CTRL_B32 0x00000040
+#define CTRL_B16 0x00000020
+#define CTRL_B8 0x00000010
+
+/* Bus Interface Status Register bits */
+#define STAT_CMEMSIZ 0xc0000000
+#define STAT_ADPARCK 0x20000000
+#define STAT_RESVD 0x1fffff80
+#define STAT_ERRINT 0x00000040
+#define STAT_MARKINT 0x00000020
+#define STAT_DLETINT 0x00000010
+#define STAT_DLERINT 0x00000008
+#define STAT_FEINT 0x00000004
+#define STAT_SEGINT 0x00000002
+#define STAT_REASSINT 0x00000001
+
+
+/*--------------- Segmentation control registers -----------------*/
+/* The segmentation registers are 16 bits access and the addresses
+ are defined as such so the addresses are the actual "offsets" */
+#define IDLEHEADHI 0x00
+#define IDLEHEADLO 0x01
+#define MAXRATE 0x02
+/* Values for MAXRATE register for 155Mbps and 25.6 Mbps operation */
+#define RATE155 0x64b1 // 16 bits float format
+#define MAX_ATM_155 352768 // Cells/second p.118
+#define RATE25 0x5f9d
+
+#define STPARMS 0x03
+#define STPARMS_1K 0x008c
+#define STPARMS_2K 0x0049
+#define STPARMS_4K 0x0026
+#define COMP_EN 0x4000
+#define CBR_EN 0x2000
+#define ABR_EN 0x0800
+#define UBR_EN 0x0400
+
+#define ABRUBR_ARB 0x04
+#define RM_TYPE 0x05
+/*Value for RM_TYPE register for ATM Forum Traffic Mangement4.0 support*/
+#define RM_TYPE_4_0 0x0100
+
+#define SEG_COMMAND_REG 0x17
+/* Values for the command register */
+#define RESET_SEG 0x0055
+#define RESET_SEG_STATE 0x00aa
+#define RESET_TX_CELL_CTR 0x00cc
+
+#define CBR_PTR_BASE 0x20
+#define ABR_SBPTR_BASE 0x22
+#define UBR_SBPTR_BASE 0x23
+#define ABRWQ_BASE 0x26
+#define UBRWQ_BASE 0x27
+#define VCT_BASE 0x28
+#define VCTE_BASE 0x29
+#define CBR_TAB_BEG 0x2c
+#define CBR_TAB_END 0x2d
+#define PRQ_ST_ADR 0x30
+#define PRQ_ED_ADR 0x31
+#define PRQ_RD_PTR 0x32
+#define PRQ_WR_PTR 0x33
+#define TCQ_ST_ADR 0x34
+#define TCQ_ED_ADR 0x35
+#define TCQ_RD_PTR 0x36
+#define TCQ_WR_PTR 0x37
+#define SEG_QUEUE_BASE 0x40
+#define SEG_DESC_BASE 0x41
+#define MODE_REG_0 0x45
+#define T_ONLINE 0x0002 /* (i)chipSAR is online */
+
+#define MODE_REG_1 0x46
+#define MODE_REG_1_VAL 0x0400 /*for propoer device operation*/
+
+#define SEG_INTR_STATUS_REG 0x47
+#define SEG_MASK_REG 0x48
+#define TRANSMIT_DONE 0x0200
+#define TCQ_NOT_EMPTY 0x1000 /* this can be used for both the interrupt
+ status registers as well as the mask register */
+
+#define CELL_CTR_HIGH_AUTO 0x49
+#define CELL_CTR_HIGH_NOAUTO 0xc9
+#define CELL_CTR_LO_AUTO 0x4a
+#define CELL_CTR_LO_NOAUTO 0xca
+
+/* Diagnostic registers */
+#define NEXTDESC 0x59
+#define NEXTVC 0x5a
+#define PSLOTCNT 0x5d
+#define NEWDN 0x6a
+#define NEWVC 0x6b
+#define SBPTR 0x6c
+#define ABRWQ_WRPTR 0x6f
+#define ABRWQ_RDPTR 0x70
+#define UBRWQ_WRPTR 0x71
+#define UBRWQ_RDPTR 0x72
+#define CBR_VC 0x73
+#define ABR_SBVC 0x75
+#define UBR_SBVC 0x76
+#define ABRNEXTLINK 0x78
+#define UBRNEXTLINK 0x79
+
+
+/*----------------- Reassembly control registers ---------------------*/
+/* The reassembly registers are 16 bits access and the addresses
+ are defined as such so the addresses are the actual "offsets" */
+#define MODE_REG 0x00
+#define R_ONLINE 0x0002 /* (i)chip is online */
+#define IGN_RAW_FL 0x0004
+
+#define PROTOCOL_ID 0x01
+#define REASS_MASK_REG 0x02
+#define REASS_INTR_STATUS_REG 0x03
+/* Interrupt Status register bits */
+#define RX_PKT_CTR_OF 0x8000
+#define RX_ERR_CTR_OF 0x4000
+#define RX_CELL_CTR_OF 0x1000
+#define RX_FREEQ_EMPT 0x0200
+#define RX_EXCPQ_FL 0x0080
+#define RX_RAWQ_FL 0x0010
+#define RX_EXCP_RCVD 0x0008
+#define RX_PKT_RCVD 0x0004
+#define RX_RAW_RCVD 0x0001
+
+#define DRP_PKT_CNTR 0x04
+#define ERR_CNTR 0x05
+#define RAW_BASE_ADR 0x08
+#define CELL_CTR0 0x0c
+#define CELL_CTR1 0x0d
+#define REASS_COMMAND_REG 0x0f
+/* Values for command register */
+#define RESET_REASS 0x0055
+#define RESET_REASS_STATE 0x00aa
+#define RESET_DRP_PKT_CNTR 0x00f1
+#define RESET_ERR_CNTR 0x00f2
+#define RESET_CELL_CNTR 0x00f8
+#define RESET_REASS_ALL_REGS 0x00ff
+
+#define REASS_DESC_BASE 0x10
+#define VC_LKUP_BASE 0x11
+#define REASS_TABLE_BASE 0x12
+#define REASS_QUEUE_BASE 0x13
+#define PKT_TM_CNT 0x16
+#define TMOUT_RANGE 0x17
+#define INTRVL_CNTR 0x18
+#define TMOUT_INDX 0x19
+#define VP_LKUP_BASE 0x1c
+#define VP_FILTER 0x1d
+#define ABR_LKUP_BASE 0x1e
+#define FREEQ_ST_ADR 0x24
+#define FREEQ_ED_ADR 0x25
+#define FREEQ_RD_PTR 0x26
+#define FREEQ_WR_PTR 0x27
+#define PCQ_ST_ADR 0x28
+#define PCQ_ED_ADR 0x29
+#define PCQ_RD_PTR 0x2a
+#define PCQ_WR_PTR 0x2b
+#define EXCP_Q_ST_ADR 0x2c
+#define EXCP_Q_ED_ADR 0x2d
+#define EXCP_Q_RD_PTR 0x2e
+#define EXCP_Q_WR_PTR 0x2f
+#define CC_FIFO_ST_ADR 0x34
+#define CC_FIFO_ED_ADR 0x35
+#define CC_FIFO_RD_PTR 0x36
+#define CC_FIFO_WR_PTR 0x37
+#define STATE_REG 0x38
+#define BUF_SIZE 0x42
+#define XTRA_RM_OFFSET 0x44
+#define DRP_PKT_CNTR_NC 0x84
+#define ERR_CNTR_NC 0x85
+#define CELL_CNTR0_NC 0x8c
+#define CELL_CNTR1_NC 0x8d
+
+/* State Register bits */
+#define EXCPQ_EMPTY 0x0040
+#define PCQ_EMPTY 0x0010
+#define FREEQ_EMPTY 0x0004
+
+
+/*----------------- Front End registers/ DMA control --------------*/
+/* There is a lot of documentation error regarding these offsets ???
+ eg:- 2 offsets given 800, a00 for rx counter
+ similarly many others
+ Remember again that the offsets are to be 4*register number, so
+ correct the #defines here
+*/
+#define IPHASE5575_TX_COUNTER 0x200 /* offset - 0x800 */
+#define IPHASE5575_RX_COUNTER 0x280 /* offset - 0xa00 */
+#define IPHASE5575_TX_LIST_ADDR 0x300 /* offset - 0xc00 */
+#define IPHASE5575_RX_LIST_ADDR 0x380 /* offset - 0xe00 */
+
+/*--------------------------- RAM ---------------------------*/
+/* These memory maps are actually offsets from the segmentation and reassembly RAM base addresses */
+
+/* Segmentation Control Memory map */
+#define TX_DESC_BASE 0x0000 /* Buffer Decriptor Table */
+#define TX_COMP_Q 0x1000 /* Transmit Complete Queue */
+#define PKT_RDY_Q 0x1400 /* Packet Ready Queue */
+#define CBR_SCHED_TABLE 0x1800 /* CBR Table */
+#define UBR_SCHED_TABLE 0x3000 /* UBR Table */
+#define UBR_WAIT_Q 0x4000 /* UBR Wait Queue */
+#define ABR_SCHED_TABLE 0x5000 /* ABR Table */
+#define ABR_WAIT_Q 0x5800 /* ABR Wait Queue */
+#define EXT_VC_TABLE 0x6000 /* Extended VC Table */
+#define MAIN_VC_TABLE 0x8000 /* Main VC Table */
+#define SCHEDSZ 1024 /* ABR and UBR Scheduling Table size */
+#define TX_DESC_TABLE_SZ 128 /* Number of entries in the Transmit
+ Buffer Descriptor Table */
+
+/* These are used as table offsets in Descriptor Table address generation */
+#define DESC_MODE 0x0
+#define VC_INDEX 0x1
+#define BYTE_CNT 0x3
+#define PKT_START_HI 0x4
+#define PKT_START_LO 0x5
+
+/* Descriptor Mode Word Bits */
+#define EOM_EN 0x0800
+#define AAL5 0x0100
+#define APP_CRC32 0x0400
+#define CMPL_INT 0x1000
+
+#define TABLE_ADDRESS(db, dn, to) \
+ (((unsigned long)(db & 0x04)) << 16) | (dn << 5) | (to << 1)
+
+/* Reassembly Control Memory Map */
+#define RX_DESC_BASE 0x0000 /* Buffer Descriptor Table */
+#define VP_TABLE 0x5c00 /* VP Table */
+#define EXCEPTION_Q 0x5e00 /* Exception Queue */
+#define FREE_BUF_DESC_Q 0x6000 /* Free Buffer Descriptor Queue */
+#define PKT_COMP_Q 0x6800 /* Packet Complete Queue */
+#define REASS_TABLE 0x7000 /* Reassembly Table */
+#define RX_VC_TABLE 0x7800 /* VC Table */
+#define ABR_VC_TABLE 0x8000 /* ABR VC Table */
+#define RX_DESC_TABLE_SZ 736 /* Number of entries in the Receive
+ Buffer Descriptor Table */
+#define VP_TABLE_SZ 256 /* Number of entries in VPTable */
+#define RX_VC_TABLE_SZ 1024 /* Number of entries in VC Table */
+#define REASS_TABLE_SZ 1024 /* Number of entries in Reassembly Table */
+ /* Buffer Descriptor Table */
+#define RX_ACT 0x8000
+#define RX_VPVC 0x4000
+#define RX_CNG 0x0040
+#define RX_CER 0x0008
+#define RX_PTE 0x0004
+#define RX_OFL 0x0002
+#define NUM_RX_EXCP 32
+
+/* Reassembly Table */
+#define NO_AAL5_PKT 0x0000
+#define AAL5_PKT_REASSEMBLED 0x4000
+#define AAL5_PKT_TERMINATED 0x8000
+#define RAW_PKT 0xc000
+#define REASS_ABR 0x2000
+
+/*-------------------- Base Registers --------------------*/
+#define REG_BASE IPHASE5575_BUS_CONTROL_REG_BASE
+#define RAM_BASE IPHASE5575_FRAG_CONTROL_RAM_BASE
+#define PHY_BASE IPHASE5575_FRONT_END_REG_BASE
+#define SEG_BASE IPHASE5575_FRAG_CONTROL_REG_BASE
+#define REASS_BASE IPHASE5575_REASS_CONTROL_REG_BASE
+
+typedef volatile u_int ffreg_t;
+typedef u_int rreg_t;
+
+typedef struct _ffredn_t {
+ ffreg_t idlehead_high; /* Idle cell header (high) */
+ ffreg_t idlehead_low; /* Idle cell header (low) */
+ ffreg_t maxrate; /* Maximum rate */
+ ffreg_t stparms; /* Traffic Management Parameters */
+ ffreg_t abrubr_abr; /* ABRUBR Priority Byte 1, TCR Byte 0 */
+ ffreg_t rm_type; /* */
+ u_int filler5[0x17 - 0x06];
+ ffreg_t cmd_reg; /* Command register */
+ u_int filler18[0x20 - 0x18];
+ ffreg_t cbr_base; /* CBR Pointer Base */
+ ffreg_t vbr_base; /* VBR Pointer Base */
+ ffreg_t abr_base; /* ABR Pointer Base */
+ ffreg_t ubr_base; /* UBR Pointer Base */
+ u_int filler24;
+ ffreg_t vbrwq_base; /* VBR Wait Queue Base */
+ ffreg_t abrwq_base; /* ABR Wait Queue Base */
+ ffreg_t ubrwq_base; /* UBR Wait Queue Base */
+ ffreg_t vct_base; /* Main VC Table Base */
+ ffreg_t vcte_base; /* Extended Main VC Table Base */
+ u_int filler2a[0x2C - 0x2A];
+ ffreg_t cbr_tab_beg; /* CBR Table Begin */
+ ffreg_t cbr_tab_end; /* CBR Table End */
+ ffreg_t cbr_pointer; /* CBR Pointer */
+ u_int filler2f[0x30 - 0x2F];
+ ffreg_t prq_st_adr; /* Packet Ready Queue Start Address */
+ ffreg_t prq_ed_adr; /* Packet Ready Queue End Address */
+ ffreg_t prq_rd_ptr; /* Packet Ready Queue read pointer */
+ ffreg_t prq_wr_ptr; /* Packet Ready Queue write pointer */
+ ffreg_t tcq_st_adr; /* Transmit Complete Queue Start Address*/
+ ffreg_t tcq_ed_adr; /* Transmit Complete Queue End Address */
+ ffreg_t tcq_rd_ptr; /* Transmit Complete Queue read pointer */
+ ffreg_t tcq_wr_ptr; /* Transmit Complete Queue write pointer*/
+ u_int filler38[0x40 - 0x38];
+ ffreg_t queue_base; /* Base address for PRQ and TCQ */
+ ffreg_t desc_base; /* Base address of descriptor table */
+ u_int filler42[0x45 - 0x42];
+ ffreg_t mode_reg_0; /* Mode register 0 */
+ ffreg_t mode_reg_1; /* Mode register 1 */
+ ffreg_t intr_status_reg;/* Interrupt Status register */
+ ffreg_t mask_reg; /* Mask Register */
+ ffreg_t cell_ctr_high1; /* Total cell transfer count (high) */
+ ffreg_t cell_ctr_lo1; /* Total cell transfer count (low) */
+ ffreg_t state_reg; /* Status register */
+ u_int filler4c[0x58 - 0x4c];
+ ffreg_t curr_desc_num; /* Contains the current descriptor num */
+ ffreg_t next_desc; /* Next descriptor */
+ ffreg_t next_vc; /* Next VC */
+ u_int filler5b[0x5d - 0x5b];
+ ffreg_t present_slot_cnt;/* Present slot count */
+ u_int filler5e[0x6a - 0x5e];
+ ffreg_t new_desc_num; /* New descriptor number */
+ ffreg_t new_vc; /* New VC */
+ ffreg_t sched_tbl_ptr; /* Schedule table pointer */
+ ffreg_t vbrwq_wptr; /* VBR wait queue write pointer */
+ ffreg_t vbrwq_rptr; /* VBR wait queue read pointer */
+ ffreg_t abrwq_wptr; /* ABR wait queue write pointer */
+ ffreg_t abrwq_rptr; /* ABR wait queue read pointer */
+ ffreg_t ubrwq_wptr; /* UBR wait queue write pointer */
+ ffreg_t ubrwq_rptr; /* UBR wait queue read pointer */
+ ffreg_t cbr_vc; /* CBR VC */
+ ffreg_t vbr_sb_vc; /* VBR SB VC */
+ ffreg_t abr_sb_vc; /* ABR SB VC */
+ ffreg_t ubr_sb_vc; /* UBR SB VC */
+ ffreg_t vbr_next_link; /* VBR next link */
+ ffreg_t abr_next_link; /* ABR next link */
+ ffreg_t ubr_next_link; /* UBR next link */
+ u_int filler7a[0x7c-0x7a];
+ ffreg_t out_rate_head; /* Out of rate head */
+ u_int filler7d[0xca-0x7d]; /* pad out to full address space */
+ ffreg_t cell_ctr_high1_nc;/* Total cell transfer count (high) */
+ ffreg_t cell_ctr_lo1_nc;/* Total cell transfer count (low) */
+ u_int fillercc[0x100-0xcc]; /* pad out to full address space */
+} ffredn_t;
+
+typedef struct _rfredn_t {
+ rreg_t mode_reg_0; /* Mode register 0 */
+ rreg_t protocol_id; /* Protocol ID */
+ rreg_t mask_reg; /* Mask Register */
+ rreg_t intr_status_reg;/* Interrupt status register */
+ rreg_t drp_pkt_cntr; /* Dropped packet cntr (clear on read) */
+ rreg_t err_cntr; /* Error Counter (cleared on read) */
+ u_int filler6[0x08 - 0x06];
+ rreg_t raw_base_adr; /* Base addr for raw cell Q */
+ u_int filler2[0x0c - 0x09];
+ rreg_t cell_ctr0; /* Cell Counter 0 (cleared when read) */
+ rreg_t cell_ctr1; /* Cell Counter 1 (cleared when read) */
+ u_int filler3[0x0f - 0x0e];
+ rreg_t cmd_reg; /* Command register */
+ rreg_t desc_base; /* Base address for description table */
+ rreg_t vc_lkup_base; /* Base address for VC lookup table */
+ rreg_t reass_base; /* Base address for reassembler table */
+ rreg_t queue_base; /* Base address for Communication queue */
+ u_int filler14[0x16 - 0x14];
+ rreg_t pkt_tm_cnt; /* Packet Timeout and count register */
+ rreg_t tmout_range; /* Range of reassembley IDs for timeout */
+ rreg_t intrvl_cntr; /* Packet aging interval counter */
+ rreg_t tmout_indx; /* index of pkt being tested for aging */
+ u_int filler1a[0x1c - 0x1a];
+ rreg_t vp_lkup_base; /* Base address for VP lookup table */
+ rreg_t vp_filter; /* VP filter register */
+ rreg_t abr_lkup_base; /* Base address of ABR VC Table */
+ u_int filler1f[0x24 - 0x1f];
+ rreg_t fdq_st_adr; /* Free desc queue start address */
+ rreg_t fdq_ed_adr; /* Free desc queue end address */
+ rreg_t fdq_rd_ptr; /* Free desc queue read pointer */
+ rreg_t fdq_wr_ptr; /* Free desc queue write pointer */
+ rreg_t pcq_st_adr; /* Packet Complete queue start address */
+ rreg_t pcq_ed_adr; /* Packet Complete queue end address */
+ rreg_t pcq_rd_ptr; /* Packet Complete queue read pointer */
+ rreg_t pcq_wr_ptr; /* Packet Complete queue write pointer */
+ rreg_t excp_st_adr; /* Exception queue start address */
+ rreg_t excp_ed_adr; /* Exception queue end address */
+ rreg_t excp_rd_ptr; /* Exception queue read pointer */
+ rreg_t excp_wr_ptr; /* Exception queue write pointer */
+ u_int filler30[0x34 - 0x30];
+ rreg_t raw_st_adr; /* Raw Cell start address */
+ rreg_t raw_ed_adr; /* Raw Cell end address */
+ rreg_t raw_rd_ptr; /* Raw Cell read pointer */
+ rreg_t raw_wr_ptr; /* Raw Cell write pointer */
+ rreg_t state_reg; /* State Register */
+ u_int filler39[0x42 - 0x39];
+ rreg_t buf_size; /* Buffer size */
+ u_int filler43;
+ rreg_t xtra_rm_offset; /* Offset of the additional turnaround RM */
+ u_int filler45[0x84 - 0x45];
+ rreg_t drp_pkt_cntr_nc;/* Dropped Packet cntr, Not clear on rd */
+ rreg_t err_cntr_nc; /* Error Counter, Not clear on read */
+ u_int filler86[0x8c - 0x86];
+ rreg_t cell_ctr0_nc; /* Cell Counter 0, Not clear on read */
+ rreg_t cell_ctr1_nc; /* Cell Counter 1, Not clear on read */
+ u_int filler8e[0x100-0x8e]; /* pad out to full address space */
+} rfredn_t;
+
+typedef struct {
+ /* Atlantic */
+ ffredn_t ffredn; /* F FRED */
+ rfredn_t rfredn; /* R FRED */
+} ia_regs_t;
+
+typedef struct {
+ u_short f_vc_type; /* VC type */
+ u_short f_nrm; /* Nrm */
+ u_short f_nrmexp; /* Nrm Exp */
+ u_short reserved6; /* */
+ u_short f_crm; /* Crm */
+ u_short reserved10; /* Reserved */
+ u_short reserved12; /* Reserved */
+ u_short reserved14; /* Reserved */
+ u_short last_cell_slot; /* last_cell_slot_count */
+ u_short f_pcr; /* Peak Cell Rate */
+ u_short fraction; /* fraction */
+ u_short f_icr; /* Initial Cell Rate */
+ u_short f_cdf; /* */
+ u_short f_mcr; /* Minimum Cell Rate */
+ u_short f_acr; /* Allowed Cell Rate */
+ u_short f_status; /* */
+} f_vc_abr_entry;
+
+typedef struct {
+ u_short r_status_rdf; /* status + RDF */
+ u_short r_air; /* AIR */
+ u_short reserved4[14]; /* Reserved */
+} r_vc_abr_entry;
+
+#define MRM 3
+
+typedef struct srv_cls_param {
+ u32 class_type; /* CBR/VBR/ABR/UBR; use the enum above */
+ u32 pcr; /* Peak Cell Rate (24-bit) */
+ /* VBR parameters */
+ u32 scr; /* sustainable cell rate */
+ u32 max_burst_size; /* ?? cell rate or data rate */
+
+ /* ABR only UNI 4.0 Parameters */
+ u32 mcr; /* Min Cell Rate (24-bit) */
+ u32 icr; /* Initial Cell Rate (24-bit) */
+ u32 tbe; /* Transient Buffer Exposure (24-bit) */
+ u32 frtt; /* Fixed Round Trip Time (24-bit) */
+
+#if 0 /* Additional Parameters of TM 4.0 */
+bits 31 30 29 28 27-25 24-22 21-19 18-9
+-----------------------------------------------------------------------------
+| NRM present | TRM prsnt | CDF prsnt | ADTF prsnt | NRM | TRM | CDF | ADTF |
+-----------------------------------------------------------------------------
+#endif /* 0 */
+
+ u8 nrm; /* Max # of Cells for each forward RM
+ cell (3-bit) */
+ u8 trm; /* Time between forward RM cells (3-bit) */
+ u16 adtf; /* ACR Decrease Time Factor (10-bit) */
+ u8 cdf; /* Cutoff Decrease Factor (3-bit) */
+ u8 rif; /* Rate Increment Factor (4-bit) */
+ u8 rdf; /* Rate Decrease Factor (4-bit) */
+ u8 reserved; /* 8 bits to keep structure word aligned */
+} srv_cls_param_t;
+
+struct testTable_t {
+ u16 lastTime;
+ u16 fract;
+ u8 vc_status;
+};
+
+typedef struct {
+ u16 vci;
+ u16 error;
+} RX_ERROR_Q;
+
+typedef struct {
+ u8 active: 1;
+ u8 abr: 1;
+ u8 ubr: 1;
+ u8 cnt: 5;
+#define VC_ACTIVE 0x01
+#define VC_ABR 0x02
+#define VC_UBR 0x04
+} vcstatus_t;
+
+struct ia_rfL_t {
+ u32 fdq_st; /* Free desc queue start address */
+ u32 fdq_ed; /* Free desc queue end address */
+ u32 fdq_rd; /* Free desc queue read pointer */
+ u32 fdq_wr; /* Free desc queue write pointer */
+ u32 pcq_st; /* Packet Complete queue start address */
+ u32 pcq_ed; /* Packet Complete queue end address */
+ u32 pcq_rd; /* Packet Complete queue read pointer */
+ u32 pcq_wr; /* Packet Complete queue write pointer */
+};
+
+struct ia_ffL_t {
+ u32 prq_st; /* Packet Ready Queue Start Address */
+ u32 prq_ed; /* Packet Ready Queue End Address */
+ u32 prq_wr; /* Packet Ready Queue write pointer */
+ u32 tcq_st; /* Transmit Complete Queue Start Address*/
+ u32 tcq_ed; /* Transmit Complete Queue End Address */
+ u32 tcq_rd; /* Transmit Complete Queue read pointer */
+};
+
+struct desc_tbl_t {
+ u32 timestamp;
+ struct ia_vcc *iavcc;
+ struct sk_buff *txskb;
+};
+
+typedef struct ia_rtn_q {
+ struct desc_tbl_t data;
+ struct ia_rtn_q *next, *tail;
+} IARTN_Q;
+
+#define SUNI_LOSV 0x04
+enum ia_suni {
+ SUNI_MASTER_RESET = 0x000, /* SUNI Master Reset and Identity */
+ SUNI_MASTER_CONFIG = 0x004, /* SUNI Master Configuration */
+ SUNI_MASTER_INTR_STAT = 0x008, /* SUNI Master Interrupt Status */
+ SUNI_RESERVED1 = 0x00c, /* Reserved */
+ SUNI_MASTER_CLK_MONITOR = 0x010, /* SUNI Master Clock Monitor */
+ SUNI_MASTER_CONTROL = 0x014, /* SUNI Master Clock Monitor */
+ /* Reserved (10) */
+ SUNI_RSOP_CONTROL = 0x040, /* RSOP Control/Interrupt Enable */
+ SUNI_RSOP_STATUS = 0x044, /* RSOP Status/Interrupt States */
+ SUNI_RSOP_SECTION_BIP8L = 0x048, /* RSOP Section BIP-8 LSB */
+ SUNI_RSOP_SECTION_BIP8M = 0x04c, /* RSOP Section BIP-8 MSB */
+
+ SUNI_TSOP_CONTROL = 0x050, /* TSOP Control */
+ SUNI_TSOP_DIAG = 0x054, /* TSOP Disgnostics */
+ /* Reserved (2) */
+ SUNI_RLOP_CS = 0x060, /* RLOP Control/Status */
+ SUNI_RLOP_INTR = 0x064, /* RLOP Interrupt Enable/Status */
+ SUNI_RLOP_LINE_BIP24L = 0x068, /* RLOP Line BIP-24 LSB */
+ SUNI_RLOP_LINE_BIP24 = 0x06c, /* RLOP Line BIP-24 */
+ SUNI_RLOP_LINE_BIP24M = 0x070, /* RLOP Line BIP-24 MSB */
+ SUNI_RLOP_LINE_FEBEL = 0x074, /* RLOP Line FEBE LSB */
+ SUNI_RLOP_LINE_FEBE = 0x078, /* RLOP Line FEBE */
+ SUNI_RLOP_LINE_FEBEM = 0x07c, /* RLOP Line FEBE MSB */
+
+ SUNI_TLOP_CONTROL = 0x080, /* TLOP Control */
+ SUNI_TLOP_DISG = 0x084, /* TLOP Disgnostics */
+ /* Reserved (14) */
+ SUNI_RPOP_CS = 0x0c0, /* RPOP Status/Control */
+ SUNI_RPOP_INTR = 0x0c4, /* RPOP Interrupt/Status */
+ SUNI_RPOP_RESERVED = 0x0c8, /* RPOP Reserved */
+ SUNI_RPOP_INTR_ENA = 0x0cc, /* RPOP Interrupt Enable */
+ /* Reserved (3) */
+ SUNI_RPOP_PATH_SIG = 0x0dc, /* RPOP Path Signal Label */
+ SUNI_RPOP_BIP8L = 0x0e0, /* RPOP Path BIP-8 LSB */
+ SUNI_RPOP_BIP8M = 0x0e4, /* RPOP Path BIP-8 MSB */
+ SUNI_RPOP_FEBEL = 0x0e8, /* RPOP Path FEBE LSB */
+ SUNI_RPOP_FEBEM = 0x0ec, /* RPOP Path FEBE MSB */
+ /* Reserved (4) */
+ SUNI_TPOP_CNTRL_DAIG = 0x100, /* TPOP Control/Disgnostics */
+ SUNI_TPOP_POINTER_CTRL = 0x104, /* TPOP Pointer Control */
+ SUNI_TPOP_SOURCER_CTRL = 0x108, /* TPOP Source Control */
+ /* Reserved (2) */
+ SUNI_TPOP_ARB_PRTL = 0x114, /* TPOP Arbitrary Pointer LSB */
+ SUNI_TPOP_ARB_PRTM = 0x118, /* TPOP Arbitrary Pointer MSB */
+ SUNI_TPOP_RESERVED2 = 0x11c, /* TPOP Reserved */
+ SUNI_TPOP_PATH_SIG = 0x120, /* TPOP Path Signal Lable */
+ SUNI_TPOP_PATH_STATUS = 0x124, /* TPOP Path Status */
+ /* Reserved (6) */
+ SUNI_RACP_CS = 0x140, /* RACP Control/Status */
+ SUNI_RACP_INTR = 0x144, /* RACP Interrupt Enable/Status */
+ SUNI_RACP_HDR_PATTERN = 0x148, /* RACP Match Header Pattern */
+ SUNI_RACP_HDR_MASK = 0x14c, /* RACP Match Header Mask */
+ SUNI_RACP_CORR_HCS = 0x150, /* RACP Correctable HCS Error Count */
+ SUNI_RACP_UNCORR_HCS = 0x154, /* RACP Uncorrectable HCS Err Count */
+ /* Reserved (10) */
+ SUNI_TACP_CONTROL = 0x180, /* TACP Control */
+ SUNI_TACP_IDLE_HDR_PAT = 0x184, /* TACP Idle Cell Header Pattern */
+ SUNI_TACP_IDLE_PAY_PAY = 0x188, /* TACP Idle Cell Payld Octet Patrn */
+ /* Reserved (5) */
+ /* Reserved (24) */
+ /* FIXME: unused but name conflicts.
+ * SUNI_MASTER_TEST = 0x200, SUNI Master Test */
+ SUNI_RESERVED_TEST = 0x204 /* SUNI Reserved for Test */
+};
+
+typedef struct _SUNI_STATS_
+{
+ u32 valid; // 1 = oc3 PHY card
+ u32 carrier_detect; // GPIN input
+ // RSOP: receive section overhead processor
+ u16 rsop_oof_state; // 1 = out of frame
+ u16 rsop_lof_state; // 1 = loss of frame
+ u16 rsop_los_state; // 1 = loss of signal
+ u32 rsop_los_count; // loss of signal count
+ u32 rsop_bse_count; // section BIP-8 error count
+ // RLOP: receive line overhead processor
+ u16 rlop_ferf_state; // 1 = far end receive failure
+ u16 rlop_lais_state; // 1 = line AIS
+ u32 rlop_lbe_count; // BIP-24 count
+ u32 rlop_febe_count; // FEBE count;
+ // RPOP: receive path overhead processor
+ u16 rpop_lop_state; // 1 = LOP
+ u16 rpop_pais_state; // 1 = path AIS
+ u16 rpop_pyel_state; // 1 = path yellow alert
+ u32 rpop_bip_count; // path BIP-8 error count
+ u32 rpop_febe_count; // path FEBE error count
+ u16 rpop_psig; // path signal label value
+ // RACP: receive ATM cell processor
+ u16 racp_hp_state; // hunt/presync state
+ u32 racp_fu_count; // FIFO underrun count
+ u32 racp_fo_count; // FIFO overrun count
+ u32 racp_chcs_count; // correctable HCS error count
+ u32 racp_uchcs_count; // uncorrectable HCS error count
+} IA_SUNI_STATS;
+
+typedef struct iadev_priv {
+ /*-----base pointers into (i)chipSAR+ address space */
+ u32 __iomem *phy; /* Base pointer into phy (SUNI). */
+ u32 __iomem *dma; /* Base pointer into DMA control registers. */
+ u32 __iomem *reg; /* Base pointer to SAR registers. */
+ u32 __iomem *seg_reg; /* base pointer to segmentation engine
+ internal registers */
+ u32 __iomem *reass_reg; /* base pointer to reassemble engine
+ internal registers */
+ u32 __iomem *ram; /* base pointer to SAR RAM */
+ void __iomem *seg_ram;
+ void __iomem *reass_ram;
+ struct dle_q tx_dle_q;
+ struct free_desc_q *tx_free_desc_qhead;
+ struct sk_buff_head tx_dma_q, tx_backlog;
+ spinlock_t tx_lock;
+ IARTN_Q tx_return_q;
+ u32 close_pending;
+ wait_queue_head_t close_wait;
+ wait_queue_head_t timeout_wait;
+ struct cpcs_trailer_desc *tx_buf;
+ u16 num_tx_desc, tx_buf_sz, rate_limit;
+ u32 tx_cell_cnt, tx_pkt_cnt;
+ void __iomem *MAIN_VC_TABLE_ADDR, *EXT_VC_TABLE_ADDR, *ABR_SCHED_TABLE_ADDR;
+ struct dle_q rx_dle_q;
+ struct free_desc_q *rx_free_desc_qhead;
+ struct sk_buff_head rx_dma_q;
+ spinlock_t rx_lock;
+ struct atm_vcc **rx_open; /* list of all open VCs */
+ u16 num_rx_desc, rx_buf_sz, rxing;
+ u32 rx_pkt_ram, rx_tmp_cnt;
+ unsigned long rx_tmp_jif;
+ void __iomem *RX_DESC_BASE_ADDR;
+ u32 drop_rxpkt, drop_rxcell, rx_cell_cnt, rx_pkt_cnt;
+ struct atm_dev *next_board; /* other iphase devices */
+ struct pci_dev *pci;
+ int mem;
+ unsigned int real_base; /* real and virtual base address */
+ void __iomem *base;
+ unsigned int pci_map_size; /*pci map size of board */
+ unsigned char irq;
+ unsigned char bus;
+ unsigned char dev_fn;
+ u_short phy_type;
+ u_short num_vc, memSize, memType;
+ struct ia_ffL_t ffL;
+ struct ia_rfL_t rfL;
+ /* Suni stat */
+ // IA_SUNI_STATS suni_stats;
+ unsigned char carrier_detect;
+ /* CBR related */
+ // transmit DMA & Receive
+ unsigned int tx_dma_cnt; // number of elements on dma queue
+ unsigned int rx_dma_cnt; // number of elements on rx dma queue
+ unsigned int NumEnabledCBR; // number of CBR VCI's enabled. CBR
+ // receive MARK for Cell FIFO
+ unsigned int rx_mark_cnt; // number of elements on mark queue
+ unsigned int CbrTotEntries; // Total CBR Entries in Scheduling Table.
+ unsigned int CbrRemEntries; // Remaining CBR Entries in Scheduling Table.
+ unsigned int CbrEntryPt; // CBR Sched Table Entry Point.
+ unsigned int Granularity; // CBR Granularity given Table Size.
+ /* ABR related */
+ unsigned int sum_mcr, sum_cbr, LineRate;
+ unsigned int n_abr;
+ struct desc_tbl_t *desc_tbl;
+ u_short host_tcq_wr;
+ struct testTable_t **testTable;
+ dma_addr_t tx_dle_dma;
+ dma_addr_t rx_dle_dma;
+} IADEV;
+
+
+#define INPH_IA_DEV(d) ((IADEV *) (d)->dev_data)
+#define INPH_IA_VCC(v) ((struct ia_vcc *) (v)->dev_data)
+
+/******************* IDT77105 25MB/s PHY DEFINE *****************************/
+enum ia_mb25 {
+ MB25_MASTER_CTRL = 0x00, /* Master control */
+ MB25_INTR_STATUS = 0x04, /* Interrupt status */
+ MB25_DIAG_CONTROL = 0x08, /* Diagnostic control */
+ MB25_LED_HEC = 0x0c, /* LED driver and HEC status/control */
+ MB25_LOW_BYTE_COUNTER = 0x10,
+ MB25_HIGH_BYTE_COUNTER = 0x14
+};
+
+/*
+ * Master Control
+ */
+#define MB25_MC_UPLO 0x80 /* UPLO */
+#define MB25_MC_DREC 0x40 /* Discard receive cell errors */
+#define MB25_MC_ECEIO 0x20 /* Enable Cell Error Interrupts Only */
+#define MB25_MC_TDPC 0x10 /* Transmit data parity check */
+#define MB25_MC_DRIC 0x08 /* Discard receive idle cells */
+#define MB25_MC_HALTTX 0x04 /* Halt Tx */
+#define MB25_MC_UMS 0x02 /* UTOPIA mode select */
+#define MB25_MC_ENABLED 0x01 /* Enable interrupt */
+
+/*
+ * Interrupt Status
+ */
+#define MB25_IS_GSB 0x40 /* GOOD Symbol Bit */
+#define MB25_IS_HECECR 0x20 /* HEC error cell received */
+#define MB25_IS_SCR 0x10 /* "Short Cell" Received */
+#define MB25_IS_TPE 0x08 /* Trnamsit Parity Error */
+#define MB25_IS_RSCC 0x04 /* Receive Signal Condition change */
+#define MB25_IS_RCSE 0x02 /* Received Cell Symbol Error */
+#define MB25_IS_RFIFOO 0x01 /* Received FIFO Overrun */
+
+/*
+ * Diagnostic Control
+ */
+#define MB25_DC_FTXCD 0x80 /* Force TxClav deassert */
+#define MB25_DC_RXCOS 0x40 /* RxClav operation select */
+#define MB25_DC_ECEIO 0x20 /* Single/Multi-PHY config select */
+#define MB25_DC_RLFLUSH 0x10 /* Clear receive FIFO */
+#define MB25_DC_IXPE 0x08 /* Insert xmit payload error */
+#define MB25_DC_IXHECE 0x04 /* Insert Xmit HEC Error */
+#define MB25_DC_LB_MASK 0x03 /* Loopback control mask */
+
+#define MB25_DC_LL 0x03 /* Line Loopback */
+#define MB25_DC_PL 0x02 /* PHY Loopback */
+#define MB25_DC_NM 0x00
+
+#define FE_MASK 0x00F0
+#define FE_MULTI_MODE 0x0000
+#define FE_SINGLE_MODE 0x0010
+#define FE_UTP_OPTION 0x0020
+#define FE_25MBIT_PHY 0x0040
+#define FE_DS3_PHY 0x0080 /* DS3 */
+#define FE_E3_PHY 0x0090 /* E3 */
+
+/*********************** SUNI_PM7345 PHY DEFINE HERE *********************/
+enum suni_pm7345 {
+ SUNI_CONFIG = 0x000, /* SUNI Configuration */
+ SUNI_INTR_ENBL = 0x004, /* SUNI Interrupt Enable */
+ SUNI_INTR_STAT = 0x008, /* SUNI Interrupt Status */
+ SUNI_CONTROL = 0x00c, /* SUNI Control */
+ SUNI_ID_RESET = 0x010, /* SUNI Reset and Identity */
+ SUNI_DATA_LINK_CTRL = 0x014,
+ SUNI_RBOC_CONF_INTR_ENBL = 0x018,
+ SUNI_RBOC_STAT = 0x01c,
+ SUNI_DS3_FRM_CFG = 0x020,
+ SUNI_DS3_FRM_INTR_ENBL = 0x024,
+ SUNI_DS3_FRM_INTR_STAT = 0x028,
+ SUNI_DS3_FRM_STAT = 0x02c,
+ SUNI_RFDL_CFG = 0x030,
+ SUNI_RFDL_ENBL_STAT = 0x034,
+ SUNI_RFDL_STAT = 0x038,
+ SUNI_RFDL_DATA = 0x03c,
+ SUNI_PMON_CHNG = 0x040,
+ SUNI_PMON_INTR_ENBL_STAT = 0x044,
+ /* SUNI_RESERVED1 (0x13 - 0x11) */
+ SUNI_PMON_LCV_EVT_CNT_LSB = 0x050,
+ SUNI_PMON_LCV_EVT_CNT_MSB = 0x054,
+ SUNI_PMON_FBE_EVT_CNT_LSB = 0x058,
+ SUNI_PMON_FBE_EVT_CNT_MSB = 0x05c,
+ SUNI_PMON_SEZ_DET_CNT_LSB = 0x060,
+ SUNI_PMON_SEZ_DET_CNT_MSB = 0x064,
+ SUNI_PMON_PE_EVT_CNT_LSB = 0x068,
+ SUNI_PMON_PE_EVT_CNT_MSB = 0x06c,
+ SUNI_PMON_PPE_EVT_CNT_LSB = 0x070,
+ SUNI_PMON_PPE_EVT_CNT_MSB = 0x074,
+ SUNI_PMON_FEBE_EVT_CNT_LSB = 0x078,
+ SUNI_PMON_FEBE_EVT_CNT_MSB = 0x07c,
+ SUNI_DS3_TRAN_CFG = 0x080,
+ SUNI_DS3_TRAN_DIAG = 0x084,
+ /* SUNI_RESERVED2 (0x23 - 0x21) */
+ SUNI_XFDL_CFG = 0x090,
+ SUNI_XFDL_INTR_ST = 0x094,
+ SUNI_XFDL_XMIT_DATA = 0x098,
+ SUNI_XBOC_CODE = 0x09c,
+ SUNI_SPLR_CFG = 0x0a0,
+ SUNI_SPLR_INTR_EN = 0x0a4,
+ SUNI_SPLR_INTR_ST = 0x0a8,
+ SUNI_SPLR_STATUS = 0x0ac,
+ SUNI_SPLT_CFG = 0x0b0,
+ SUNI_SPLT_CNTL = 0x0b4,
+ SUNI_SPLT_DIAG_G1 = 0x0b8,
+ SUNI_SPLT_F1 = 0x0bc,
+ SUNI_CPPM_LOC_METERS = 0x0c0,
+ SUNI_CPPM_CHG_OF_CPPM_PERF_METR = 0x0c4,
+ SUNI_CPPM_B1_ERR_CNT_LSB = 0x0c8,
+ SUNI_CPPM_B1_ERR_CNT_MSB = 0x0cc,
+ SUNI_CPPM_FRAMING_ERR_CNT_LSB = 0x0d0,
+ SUNI_CPPM_FRAMING_ERR_CNT_MSB = 0x0d4,
+ SUNI_CPPM_FEBE_CNT_LSB = 0x0d8,
+ SUNI_CPPM_FEBE_CNT_MSB = 0x0dc,
+ SUNI_CPPM_HCS_ERR_CNT_LSB = 0x0e0,
+ SUNI_CPPM_HCS_ERR_CNT_MSB = 0x0e4,
+ SUNI_CPPM_IDLE_UN_CELL_CNT_LSB = 0x0e8,
+ SUNI_CPPM_IDLE_UN_CELL_CNT_MSB = 0x0ec,
+ SUNI_CPPM_RCV_CELL_CNT_LSB = 0x0f0,
+ SUNI_CPPM_RCV_CELL_CNT_MSB = 0x0f4,
+ SUNI_CPPM_XMIT_CELL_CNT_LSB = 0x0f8,
+ SUNI_CPPM_XMIT_CELL_CNT_MSB = 0x0fc,
+ SUNI_RXCP_CTRL = 0x100,
+ SUNI_RXCP_FCTRL = 0x104,
+ SUNI_RXCP_INTR_EN_STS = 0x108,
+ SUNI_RXCP_IDLE_PAT_H1 = 0x10c,
+ SUNI_RXCP_IDLE_PAT_H2 = 0x110,
+ SUNI_RXCP_IDLE_PAT_H3 = 0x114,
+ SUNI_RXCP_IDLE_PAT_H4 = 0x118,
+ SUNI_RXCP_IDLE_MASK_H1 = 0x11c,
+ SUNI_RXCP_IDLE_MASK_H2 = 0x120,
+ SUNI_RXCP_IDLE_MASK_H3 = 0x124,
+ SUNI_RXCP_IDLE_MASK_H4 = 0x128,
+ SUNI_RXCP_CELL_PAT_H1 = 0x12c,
+ SUNI_RXCP_CELL_PAT_H2 = 0x130,
+ SUNI_RXCP_CELL_PAT_H3 = 0x134,
+ SUNI_RXCP_CELL_PAT_H4 = 0x138,
+ SUNI_RXCP_CELL_MASK_H1 = 0x13c,
+ SUNI_RXCP_CELL_MASK_H2 = 0x140,
+ SUNI_RXCP_CELL_MASK_H3 = 0x144,
+ SUNI_RXCP_CELL_MASK_H4 = 0x148,
+ SUNI_RXCP_HCS_CS = 0x14c,
+ SUNI_RXCP_LCD_CNT_THRESHOLD = 0x150,
+ /* SUNI_RESERVED3 (0x57 - 0x54) */
+ SUNI_TXCP_CTRL = 0x160,
+ SUNI_TXCP_INTR_EN_STS = 0x164,
+ SUNI_TXCP_IDLE_PAT_H1 = 0x168,
+ SUNI_TXCP_IDLE_PAT_H2 = 0x16c,
+ SUNI_TXCP_IDLE_PAT_H3 = 0x170,
+ SUNI_TXCP_IDLE_PAT_H4 = 0x174,
+ SUNI_TXCP_IDLE_PAT_H5 = 0x178,
+ SUNI_TXCP_IDLE_PAYLOAD = 0x17c,
+ SUNI_E3_FRM_FRAM_OPTIONS = 0x180,
+ SUNI_E3_FRM_MAINT_OPTIONS = 0x184,
+ SUNI_E3_FRM_FRAM_INTR_ENBL = 0x188,
+ SUNI_E3_FRM_FRAM_INTR_IND_STAT = 0x18c,
+ SUNI_E3_FRM_MAINT_INTR_ENBL = 0x190,
+ SUNI_E3_FRM_MAINT_INTR_IND = 0x194,
+ SUNI_E3_FRM_MAINT_STAT = 0x198,
+ SUNI_RESERVED4 = 0x19c,
+ SUNI_E3_TRAN_FRAM_OPTIONS = 0x1a0,
+ SUNI_E3_TRAN_STAT_DIAG_OPTIONS = 0x1a4,
+ SUNI_E3_TRAN_BIP_8_ERR_MASK = 0x1a8,
+ SUNI_E3_TRAN_MAINT_ADAPT_OPTS = 0x1ac,
+ SUNI_TTB_CTRL = 0x1b0,
+ SUNI_TTB_TRAIL_TRACE_ID_STAT = 0x1b4,
+ SUNI_TTB_IND_ADDR = 0x1b8,
+ SUNI_TTB_IND_DATA = 0x1bc,
+ SUNI_TTB_EXP_PAYLOAD_TYPE = 0x1c0,
+ SUNI_TTB_PAYLOAD_TYPE_CTRL_STAT = 0x1c4,
+ /* SUNI_PAD5 (0x7f - 0x71) */
+ SUNI_MASTER_TEST = 0x200,
+ /* SUNI_PAD6 (0xff - 0x80) */
+};
+
+#define SUNI_PM7345_T suni_pm7345_t
+#define SUNI_PM7345 0x20 /* Suni chip type */
+#define SUNI_PM5346 0x30 /* Suni chip type */
+/*
+ * SUNI_PM7345 Configuration
+ */
+#define SUNI_PM7345_CLB 0x01 /* Cell loopback */
+#define SUNI_PM7345_PLB 0x02 /* Payload loopback */
+#define SUNI_PM7345_DLB 0x04 /* Diagnostic loopback */
+#define SUNI_PM7345_LLB 0x80 /* Line loopback */
+#define SUNI_PM7345_E3ENBL 0x40 /* E3 enable bit */
+#define SUNI_PM7345_LOOPT 0x10 /* LOOPT enable bit */
+#define SUNI_PM7345_FIFOBP 0x20 /* FIFO bypass */
+#define SUNI_PM7345_FRMRBP 0x08 /* Framer bypass */
+/*
+ * DS3 FRMR Interrupt Enable
+ */
+#define SUNI_DS3_COFAE 0x80 /* Enable change of frame align */
+#define SUNI_DS3_REDE 0x40 /* Enable DS3 RED state intr */
+#define SUNI_DS3_CBITE 0x20 /* Enable Appl ID channel intr */
+#define SUNI_DS3_FERFE 0x10 /* Enable Far End Receive Failure intr*/
+#define SUNI_DS3_IDLE 0x08 /* Enable Idle signal intr */
+#define SUNI_DS3_AISE 0x04 /* Enable Alarm Indication signal intr*/
+#define SUNI_DS3_OOFE 0x02 /* Enable Out of frame intr */
+#define SUNI_DS3_LOSE 0x01 /* Enable Loss of signal intr */
+
+/*
+ * DS3 FRMR Status
+ */
+#define SUNI_DS3_ACE 0x80 /* Additional Configuration Reg */
+#define SUNI_DS3_REDV 0x40 /* DS3 RED state */
+#define SUNI_DS3_CBITV 0x20 /* Application ID channel state */
+#define SUNI_DS3_FERFV 0x10 /* Far End Receive Failure state*/
+#define SUNI_DS3_IDLV 0x08 /* Idle signal state */
+#define SUNI_DS3_AISV 0x04 /* Alarm Indication signal state*/
+#define SUNI_DS3_OOFV 0x02 /* Out of frame state */
+#define SUNI_DS3_LOSV 0x01 /* Loss of signal state */
+
+/*
+ * E3 FRMR Interrupt/Status
+ */
+#define SUNI_E3_CZDI 0x40 /* Consecutive Zeros indicator */
+#define SUNI_E3_LOSI 0x20 /* Loss of signal intr status */
+#define SUNI_E3_LCVI 0x10 /* Line code violation intr */
+#define SUNI_E3_COFAI 0x08 /* Change of frame align intr */
+#define SUNI_E3_OOFI 0x04 /* Out of frame intr status */
+#define SUNI_E3_LOS 0x02 /* Loss of signal state */
+#define SUNI_E3_OOF 0x01 /* Out of frame state */
+
+/*
+ * E3 FRMR Maintenance Status
+ */
+#define SUNI_E3_AISD 0x80 /* Alarm Indication signal state*/
+#define SUNI_E3_FERF_RAI 0x40 /* FERF/RAI indicator */
+#define SUNI_E3_FEBE 0x20 /* Far End Block Error indicator*/
+
+/*
+ * RXCP Control/Status
+ */
+#define SUNI_DS3_HCSPASS 0x80 /* Pass cell with HEC errors */
+#define SUNI_DS3_HCSDQDB 0x40 /* Control octets in HCS calc */
+#define SUNI_DS3_HCSADD 0x20 /* Add coset poly */
+#define SUNI_DS3_HCK 0x10 /* Control FIFO data path integ chk*/
+#define SUNI_DS3_BLOCK 0x08 /* Enable cell filtering */
+#define SUNI_DS3_DSCR 0x04 /* Disable payload descrambling */
+#define SUNI_DS3_OOCDV 0x02 /* Cell delineation state */
+#define SUNI_DS3_FIFORST 0x01 /* Cell FIFO reset */
+
+/*
+ * RXCP Interrupt Enable/Status
+ */
+#define SUNI_DS3_OOCDE 0x80 /* Intr enable, change in CDS */
+#define SUNI_DS3_HCSE 0x40 /* Intr enable, corr HCS errors */
+#define SUNI_DS3_FIFOE 0x20 /* Intr enable, unco HCS errors */
+#define SUNI_DS3_OOCDI 0x10 /* SYNC state */
+#define SUNI_DS3_UHCSI 0x08 /* Uncorr. HCS errors detected */
+#define SUNI_DS3_COCAI 0x04 /* Corr. HCS errors detected */
+#define SUNI_DS3_FOVRI 0x02 /* FIFO overrun */
+#define SUNI_DS3_FUDRI 0x01 /* FIFO underrun */
+
+///////////////////SUNI_PM7345 PHY DEFINE END /////////////////////////////
+
+/* ia_eeprom define*/
+#define MEM_SIZE_MASK 0x000F /* mask of 4 bits defining memory size*/
+#define MEM_SIZE_128K 0x0000 /* board has 128k buffer */
+#define MEM_SIZE_512K 0x0001 /* board has 512K of buffer */
+#define MEM_SIZE_1M 0x0002 /* board has 1M of buffer */
+ /* 0x3 to 0xF are reserved for future */
+
+#define FE_MASK 0x00F0 /* mask of 4 bits defining FE type */
+#define FE_MULTI_MODE 0x0000 /* 155 MBit multimode fiber */
+#define FE_SINGLE_MODE 0x0010 /* 155 MBit single mode laser */
+#define FE_UTP_OPTION 0x0020 /* 155 MBit UTP front end */
+
+#define NOVRAM_SIZE 64
+#define CMD_LEN 10
+
+/***********
+ *
+ * Switches and defines for header files.
+ *
+ * The following defines are used to turn on and off
+ * various options in the header files. Primarily useful
+ * for debugging.
+ *
+ ***********/
+
+/*
+ * a list of the commands that can be sent to the NOVRAM
+ */
+
+#define EXTEND 0x100
+#define IAWRITE 0x140
+#define IAREAD 0x180
+#define ERASE 0x1c0
+
+#define EWDS 0x00
+#define WRAL 0x10
+#define ERAL 0x20
+#define EWEN 0x30
+
+/*
+ * these bits duplicate the hw_flip.h register settings
+ * note: how the data in / out bits are defined in the flipper specification
+ */
+
+#define NVCE 0x02
+#define NVSK 0x01
+#define NVDO 0x08
+#define NVDI 0x04
+/***********************
+ *
+ * This define ands the value and the current config register and puts
+ * the result in the config register
+ *
+ ***********************/
+
+#define CFG_AND(val) { \
+ u32 t; \
+ t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS); \
+ t &= (val); \
+ writel(t, iadev->reg+IPHASE5575_EEPROM_ACCESS); \
+ }
+
+/***********************
+ *
+ * This define ors the value and the current config register and puts
+ * the result in the config register
+ *
+ ***********************/
+
+#define CFG_OR(val) { \
+ u32 t; \
+ t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS); \
+ t |= (val); \
+ writel(t, iadev->reg+IPHASE5575_EEPROM_ACCESS); \
+ }
+
+/***********************
+ *
+ * Send a command to the NOVRAM, the command is in cmd.
+ *
+ * clear CE and SK. Then assert CE.
+ * Clock each of the command bits out in the correct order with SK
+ * exit with CE still asserted
+ *
+ ***********************/
+
+#define NVRAM_CMD(cmd) { \
+ int i; \
+ u_short c = cmd; \
+ CFG_AND(~(NVCE|NVSK)); \
+ CFG_OR(NVCE); \
+ for (i=0; i<CMD_LEN; i++) { \
+ NVRAM_CLKOUT((c & (1 << (CMD_LEN - 1))) ? 1 : 0); \
+ c <<= 1; \
+ } \
+ }
+
+/***********************
+ *
+ * clear the CE, this must be used after each command is complete
+ *
+ ***********************/
+
+#define NVRAM_CLR_CE {CFG_AND(~NVCE)}
+
+/***********************
+ *
+ * clock the data bit in bitval out to the NOVRAM. The bitval must be
+ * a 1 or 0, or the clockout operation is undefined
+ *
+ ***********************/
+
+#define NVRAM_CLKOUT(bitval) { \
+ CFG_AND(~NVDI); \
+ CFG_OR((bitval) ? NVDI : 0); \
+ CFG_OR(NVSK); \
+ CFG_AND( ~NVSK); \
+ }
+
+/***********************
+ *
+ * clock the data bit in and return a 1 or 0, depending on the value
+ * that was received from the NOVRAM
+ *
+ ***********************/
+
+#define NVRAM_CLKIN(value) { \
+ u32 _t; \
+ CFG_OR(NVSK); \
+ CFG_AND(~NVSK); \
+ _t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS); \
+ value = (_t & NVDO) ? 1 : 0; \
+ }
+
+
+#endif /* IPHASE_H */
diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c
new file mode 100644
index 000000000..ce43ae3e8
--- /dev/null
+++ b/drivers/atm/lanai.c
@@ -0,0 +1,2608 @@
+/* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr <mitch@sfgoth.com>
+ *
+ * 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 driver supports ATM cards based on the Efficient "Lanai"
+ * chipset such as the Speedstream 3010 and the ENI-25p. The
+ * Speedstream 3060 is currently not supported since we don't
+ * have the code to drive the on-board Alcatel DSL chipset (yet).
+ *
+ * Thanks to Efficient for supporting this project with hardware,
+ * documentation, and by answering my questions.
+ *
+ * Things not working yet:
+ *
+ * o We don't support the Speedstream 3060 yet - this card has
+ * an on-board DSL modem chip by Alcatel and the driver will
+ * need some extra code added to handle it
+ *
+ * o Note that due to limitations of the Lanai only one VCC can be
+ * in CBR at once
+ *
+ * o We don't currently parse the EEPROM at all. The code is all
+ * there as per the spec, but it doesn't actually work. I think
+ * there may be some issues with the docs. Anyway, do NOT
+ * enable it yet - bugs in that code may actually damage your
+ * hardware! Because of this you should hardware an ESI before
+ * trying to use this in a LANE or MPOA environment.
+ *
+ * o AAL0 is stubbed in but the actual rx/tx path isn't written yet:
+ * vcc_tx_aal0() needs to send or queue a SKB
+ * vcc_tx_unqueue_aal0() needs to attempt to send queued SKBs
+ * vcc_rx_aal0() needs to handle AAL0 interrupts
+ * This isn't too much work - I just wanted to get other things
+ * done first.
+ *
+ * o lanai_change_qos() isn't written yet
+ *
+ * o There aren't any ioctl's yet -- I'd like to eventually support
+ * setting loopback and LED modes that way.
+ *
+ * o If the segmentation engine or DMA gets shut down we should restart
+ * card as per section 17.0i. (see lanai_reset)
+ *
+ * o setsockopt(SO_CIRANGE) isn't done (although despite what the
+ * API says it isn't exactly commonly implemented)
+ */
+
+/* Version history:
+ * v.1.00 -- 26-JUL-2003 -- PCI/DMA updates
+ * v.0.02 -- 11-JAN-2000 -- Endian fixes
+ * v.0.01 -- 30-NOV-1999 -- Initial release
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/atmdev.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+/* -------------------- TUNABLE PARAMATERS: */
+
+/*
+ * Maximum number of VCIs per card. Setting it lower could theoretically
+ * save some memory, but since we allocate our vcc list with get_free_pages,
+ * it's not really likely for most architectures
+ */
+#define NUM_VCI (1024)
+
+/*
+ * Enable extra debugging
+ */
+#define DEBUG
+/*
+ * Debug _all_ register operations with card, except the memory test.
+ * Also disables the timed poll to prevent extra chattiness. This
+ * isn't for normal use
+ */
+#undef DEBUG_RW
+
+/*
+ * The programming guide specifies a full test of the on-board SRAM
+ * at initialization time. Undefine to remove this
+ */
+#define FULL_MEMORY_TEST
+
+/*
+ * This is the number of (4 byte) service entries that we will
+ * try to allocate at startup. Note that we will end up with
+ * one PAGE_SIZE's worth regardless of what this is set to
+ */
+#define SERVICE_ENTRIES (1024)
+/* TODO: make above a module load-time option */
+
+/*
+ * We normally read the onboard EEPROM in order to discover our MAC
+ * address. Undefine to _not_ do this
+ */
+/* #define READ_EEPROM */ /* ***DONT ENABLE YET*** */
+/* TODO: make above a module load-time option (also) */
+
+/*
+ * Depth of TX fifo (in 128 byte units; range 2-31)
+ * Smaller numbers are better for network latency
+ * Larger numbers are better for PCI latency
+ * I'm really sure where the best tradeoff is, but the BSD driver uses
+ * 7 and it seems to work ok.
+ */
+#define TX_FIFO_DEPTH (7)
+/* TODO: make above a module load-time option */
+
+/*
+ * How often (in jiffies) we will try to unstick stuck connections -
+ * shouldn't need to happen much
+ */
+#define LANAI_POLL_PERIOD (10*HZ)
+/* TODO: make above a module load-time option */
+
+/*
+ * When allocating an AAL5 receiving buffer, try to make it at least
+ * large enough to hold this many max_sdu sized PDUs
+ */
+#define AAL5_RX_MULTIPLIER (3)
+/* TODO: make above a module load-time option */
+
+/*
+ * Same for transmitting buffer
+ */
+#define AAL5_TX_MULTIPLIER (3)
+/* TODO: make above a module load-time option */
+
+/*
+ * When allocating an AAL0 transmiting buffer, how many cells should fit.
+ * Remember we'll end up with a PAGE_SIZE of them anyway, so this isn't
+ * really critical
+ */
+#define AAL0_TX_MULTIPLIER (40)
+/* TODO: make above a module load-time option */
+
+/*
+ * How large should we make the AAL0 receiving buffer. Remember that this
+ * is shared between all AAL0 VC's
+ */
+#define AAL0_RX_BUFFER_SIZE (PAGE_SIZE)
+/* TODO: make above a module load-time option */
+
+/*
+ * Should we use Lanai's "powerdown" feature when no vcc's are bound?
+ */
+/* #define USE_POWERDOWN */
+/* TODO: make above a module load-time option (also) */
+
+/* -------------------- DEBUGGING AIDS: */
+
+#define DEV_LABEL "lanai"
+
+#ifdef DEBUG
+
+#define DPRINTK(format, args...) \
+ printk(KERN_DEBUG DEV_LABEL ": " format, ##args)
+#define APRINTK(truth, format, args...) \
+ do { \
+ if (unlikely(!(truth))) \
+ printk(KERN_ERR DEV_LABEL ": " format, ##args); \
+ } while (0)
+
+#else /* !DEBUG */
+
+#define DPRINTK(format, args...)
+#define APRINTK(truth, format, args...)
+
+#endif /* DEBUG */
+
+#ifdef DEBUG_RW
+#define RWDEBUG(format, args...) \
+ printk(KERN_DEBUG DEV_LABEL ": " format, ##args)
+#else /* !DEBUG_RW */
+#define RWDEBUG(format, args...)
+#endif
+
+/* -------------------- DATA DEFINITIONS: */
+
+#define LANAI_MAPPING_SIZE (0x40000)
+#define LANAI_EEPROM_SIZE (128)
+
+typedef int vci_t;
+typedef void __iomem *bus_addr_t;
+
+/* DMA buffer in host memory for TX, RX, or service list. */
+struct lanai_buffer {
+ u32 *start; /* From get_free_pages */
+ u32 *end; /* One past last byte */
+ u32 *ptr; /* Pointer to current host location */
+ dma_addr_t dmaaddr;
+};
+
+struct lanai_vcc_stats {
+ unsigned rx_nomem;
+ union {
+ struct {
+ unsigned rx_badlen;
+ unsigned service_trash;
+ unsigned service_stream;
+ unsigned service_rxcrc;
+ } aal5;
+ struct {
+ } aal0;
+ } x;
+};
+
+struct lanai_dev; /* Forward declaration */
+
+/*
+ * This is the card-specific per-vcc data. Note that unlike some other
+ * drivers there is NOT a 1-to-1 correspondance between these and
+ * atm_vcc's - each one of these represents an actual 2-way vcc, but
+ * an atm_vcc can be 1-way and share with a 1-way vcc in the other
+ * direction. To make it weirder, there can even be 0-way vccs
+ * bound to us, waiting to do a change_qos
+ */
+struct lanai_vcc {
+ bus_addr_t vbase; /* Base of VCC's registers */
+ struct lanai_vcc_stats stats;
+ int nref; /* # of atm_vcc's who reference us */
+ vci_t vci;
+ struct {
+ struct lanai_buffer buf;
+ struct atm_vcc *atmvcc; /* atm_vcc who is receiver */
+ } rx;
+ struct {
+ struct lanai_buffer buf;
+ struct atm_vcc *atmvcc; /* atm_vcc who is transmitter */
+ int endptr; /* last endptr from service entry */
+ struct sk_buff_head backlog;
+ void (*unqueue)(struct lanai_dev *, struct lanai_vcc *, int);
+ } tx;
+};
+
+enum lanai_type {
+ lanai2 = PCI_DEVICE_ID_EF_ATM_LANAI2,
+ lanaihb = PCI_DEVICE_ID_EF_ATM_LANAIHB
+};
+
+struct lanai_dev_stats {
+ unsigned ovfl_trash; /* # of cells dropped - buffer overflow */
+ unsigned vci_trash; /* # of cells dropped - closed vci */
+ unsigned hec_err; /* # of cells dropped - bad HEC */
+ unsigned atm_ovfl; /* # of cells dropped - rx fifo overflow */
+ unsigned pcierr_parity_detect;
+ unsigned pcierr_serr_set;
+ unsigned pcierr_master_abort;
+ unsigned pcierr_m_target_abort;
+ unsigned pcierr_s_target_abort;
+ unsigned pcierr_master_parity;
+ unsigned service_notx;
+ unsigned service_norx;
+ unsigned service_rxnotaal5;
+ unsigned dma_reenable;
+ unsigned card_reset;
+};
+
+struct lanai_dev {
+ bus_addr_t base;
+ struct lanai_dev_stats stats;
+ struct lanai_buffer service;
+ struct lanai_vcc **vccs;
+#ifdef USE_POWERDOWN
+ int nbound; /* number of bound vccs */
+#endif
+ enum lanai_type type;
+ vci_t num_vci; /* Currently just NUM_VCI */
+ u8 eeprom[LANAI_EEPROM_SIZE];
+ u32 serialno, magicno;
+ struct pci_dev *pci;
+ DECLARE_BITMAP(backlog_vccs, NUM_VCI); /* VCCs with tx backlog */
+ DECLARE_BITMAP(transmit_ready, NUM_VCI); /* VCCs with transmit space */
+ struct timer_list timer;
+ int naal0;
+ struct lanai_buffer aal0buf; /* AAL0 RX buffers */
+ u32 conf1, conf2; /* CONFIG[12] registers */
+ u32 status; /* STATUS register */
+ spinlock_t endtxlock;
+ spinlock_t servicelock;
+ struct atm_vcc *cbrvcc;
+ int number;
+ int board_rev;
+/* TODO - look at race conditions with maintence of conf1/conf2 */
+/* TODO - transmit locking: should we use _irq not _irqsave? */
+/* TODO - organize above in some rational fashion (see <asm/cache.h>) */
+};
+
+/*
+ * Each device has two bitmaps for each VCC (baclog_vccs and transmit_ready)
+ * This function iterates one of these, calling a given function for each
+ * vci with their bit set
+ */
+static void vci_bitfield_iterate(struct lanai_dev *lanai,
+ const unsigned long *lp,
+ void (*func)(struct lanai_dev *,vci_t vci))
+{
+ vci_t vci;
+
+ for_each_set_bit(vci, lp, NUM_VCI)
+ func(lanai, vci);
+}
+
+/* -------------------- BUFFER UTILITIES: */
+
+/*
+ * Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes -
+ * usually any page allocation will do. Just to be safe in case
+ * PAGE_SIZE is insanely tiny, though...
+ */
+#define LANAI_PAGE_SIZE ((PAGE_SIZE >= 1024) ? PAGE_SIZE : 1024)
+
+/*
+ * Allocate a buffer in host RAM for service list, RX, or TX
+ * Returns buf->start==NULL if no memory
+ * Note that the size will be rounded up 2^n bytes, and
+ * if we can't allocate that we'll settle for something smaller
+ * until minbytes
+ */
+static void lanai_buf_allocate(struct lanai_buffer *buf,
+ size_t bytes, size_t minbytes, struct pci_dev *pci)
+{
+ int size;
+
+ if (bytes > (128 * 1024)) /* max lanai buffer size */
+ bytes = 128 * 1024;
+ for (size = LANAI_PAGE_SIZE; size < bytes; size *= 2)
+ ;
+ if (minbytes < LANAI_PAGE_SIZE)
+ minbytes = LANAI_PAGE_SIZE;
+ do {
+ /*
+ * Technically we could use non-consistent mappings for
+ * everything, but the way the lanai uses DMA memory would
+ * make that a terrific pain. This is much simpler.
+ */
+ buf->start = dma_alloc_coherent(&pci->dev,
+ size, &buf->dmaaddr, GFP_KERNEL);
+ if (buf->start != NULL) { /* Success */
+ /* Lanai requires 256-byte alignment of DMA bufs */
+ APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0,
+ "bad dmaaddr: 0x%lx\n",
+ (unsigned long) buf->dmaaddr);
+ buf->ptr = buf->start;
+ buf->end = (u32 *)
+ (&((unsigned char *) buf->start)[size]);
+ memset(buf->start, 0, size);
+ break;
+ }
+ size /= 2;
+ } while (size >= minbytes);
+}
+
+/* size of buffer in bytes */
+static inline size_t lanai_buf_size(const struct lanai_buffer *buf)
+{
+ return ((unsigned long) buf->end) - ((unsigned long) buf->start);
+}
+
+static void lanai_buf_deallocate(struct lanai_buffer *buf,
+ struct pci_dev *pci)
+{
+ if (buf->start != NULL) {
+ dma_free_coherent(&pci->dev, lanai_buf_size(buf),
+ buf->start, buf->dmaaddr);
+ buf->start = buf->end = buf->ptr = NULL;
+ }
+}
+
+/* size of buffer as "card order" (0=1k .. 7=128k) */
+static int lanai_buf_size_cardorder(const struct lanai_buffer *buf)
+{
+ int order = get_order(lanai_buf_size(buf)) + (PAGE_SHIFT - 10);
+
+ /* This can only happen if PAGE_SIZE is gigantic, but just in case */
+ if (order > 7)
+ order = 7;
+ return order;
+}
+
+/* -------------------- PORT I/O UTILITIES: */
+
+/* Registers (and their bit-fields) */
+enum lanai_register {
+ Reset_Reg = 0x00, /* Reset; read for chip type; bits: */
+#define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */
+#define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */
+#define BOARD_ID_LANAI256 (0) /* 25.6M adapter card */
+ Endian_Reg = 0x04, /* Endian setting */
+ IntStatus_Reg = 0x08, /* Interrupt status */
+ IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */
+ IntAck_Reg = 0x10, /* Interrupt acknowledge */
+ IntAckMasked_Reg = 0x14, /* Interrupt acknowledge (masked) */
+ IntStatusSet_Reg = 0x18, /* Get status + enable/disable */
+ IntStatusSetMasked_Reg = 0x1C, /* Get status + en/di (masked) */
+ IntControlEna_Reg = 0x20, /* Interrupt control enable */
+ IntControlDis_Reg = 0x24, /* Interrupt control disable */
+ Status_Reg = 0x28, /* Status */
+#define STATUS_PROMDATA (0x00000001) /* PROM_DATA pin */
+#define STATUS_WAITING (0x00000002) /* Interrupt being delayed */
+#define STATUS_SOOL (0x00000004) /* SOOL alarm */
+#define STATUS_LOCD (0x00000008) /* LOCD alarm */
+#define STATUS_LED (0x00000010) /* LED (HAPPI) output */
+#define STATUS_GPIN (0x00000020) /* GPIN pin */
+#define STATUS_BUTTBUSY (0x00000040) /* Butt register is pending */
+ Config1_Reg = 0x2C, /* Config word 1; bits: */
+#define CONFIG1_PROMDATA (0x00000001) /* PROM_DATA pin */
+#define CONFIG1_PROMCLK (0x00000002) /* PROM_CLK pin */
+#define CONFIG1_SET_READMODE(x) ((x)*0x004) /* PCI BM reads; values: */
+#define READMODE_PLAIN (0) /* Plain memory read */
+#define READMODE_LINE (2) /* Memory read line */
+#define READMODE_MULTIPLE (3) /* Memory read multiple */
+#define CONFIG1_DMA_ENABLE (0x00000010) /* Turn on DMA */
+#define CONFIG1_POWERDOWN (0x00000020) /* Turn off clocks */
+#define CONFIG1_SET_LOOPMODE(x) ((x)*0x080) /* Clock&loop mode; values: */
+#define LOOPMODE_NORMAL (0) /* Normal - no loop */
+#define LOOPMODE_TIME (1)
+#define LOOPMODE_DIAG (2)
+#define LOOPMODE_LINE (3)
+#define CONFIG1_MASK_LOOPMODE (0x00000180)
+#define CONFIG1_SET_LEDMODE(x) ((x)*0x0200) /* Mode of LED; values: */
+#define LEDMODE_NOT_SOOL (0) /* !SOOL */
+#define LEDMODE_OFF (1) /* 0 */
+#define LEDMODE_ON (2) /* 1 */
+#define LEDMODE_NOT_LOCD (3) /* !LOCD */
+#define LEDMORE_GPIN (4) /* GPIN */
+#define LEDMODE_NOT_GPIN (7) /* !GPIN */
+#define CONFIG1_MASK_LEDMODE (0x00000E00)
+#define CONFIG1_GPOUT1 (0x00001000) /* Toggle for reset */
+#define CONFIG1_GPOUT2 (0x00002000) /* Loopback PHY */
+#define CONFIG1_GPOUT3 (0x00004000) /* Loopback lanai */
+ Config2_Reg = 0x30, /* Config word 2; bits: */
+#define CONFIG2_HOWMANY (0x00000001) /* >512 VCIs? */
+#define CONFIG2_PTI7_MODE (0x00000002) /* Make PTI=7 RM, not OAM */
+#define CONFIG2_VPI_CHK_DIS (0x00000004) /* Ignore RX VPI value */
+#define CONFIG2_HEC_DROP (0x00000008) /* Drop cells w/ HEC errors */
+#define CONFIG2_VCI0_NORMAL (0x00000010) /* Treat VCI=0 normally */
+#define CONFIG2_CBR_ENABLE (0x00000020) /* Deal with CBR traffic */
+#define CONFIG2_TRASH_ALL (0x00000040) /* Trashing incoming cells */
+#define CONFIG2_TX_DISABLE (0x00000080) /* Trashing outgoing cells */
+#define CONFIG2_SET_TRASH (0x00000100) /* Turn trashing on */
+ Statistics_Reg = 0x34, /* Statistics; bits: */
+#define STATS_GET_FIFO_OVFL(x) (((x)>> 0)&0xFF) /* FIFO overflowed */
+#define STATS_GET_HEC_ERR(x) (((x)>> 8)&0xFF) /* HEC was bad */
+#define STATS_GET_BAD_VCI(x) (((x)>>16)&0xFF) /* VCI not open */
+#define STATS_GET_BUF_OVFL(x) (((x)>>24)&0xFF) /* VCC buffer full */
+ ServiceStuff_Reg = 0x38, /* Service stuff; bits: */
+#define SSTUFF_SET_SIZE(x) ((x)*0x20000000) /* size of service buffer */
+#define SSTUFF_SET_ADDR(x) ((x)>>8) /* set address of buffer */
+ ServWrite_Reg = 0x3C, /* ServWrite Pointer */
+ ServRead_Reg = 0x40, /* ServRead Pointer */
+ TxDepth_Reg = 0x44, /* FIFO Transmit Depth */
+ Butt_Reg = 0x48, /* Butt register */
+ CBR_ICG_Reg = 0x50,
+ CBR_PTR_Reg = 0x54,
+ PingCount_Reg = 0x58, /* Ping count */
+ DMA_Addr_Reg = 0x5C /* DMA address */
+};
+
+static inline bus_addr_t reg_addr(const struct lanai_dev *lanai,
+ enum lanai_register reg)
+{
+ return lanai->base + reg;
+}
+
+static inline u32 reg_read(const struct lanai_dev *lanai,
+ enum lanai_register reg)
+{
+ u32 t;
+ t = readl(reg_addr(lanai, reg));
+ RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base,
+ (int) reg, t);
+ return t;
+}
+
+static inline void reg_write(const struct lanai_dev *lanai, u32 val,
+ enum lanai_register reg)
+{
+ RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base,
+ (int) reg, val);
+ writel(val, reg_addr(lanai, reg));
+}
+
+static inline void conf1_write(const struct lanai_dev *lanai)
+{
+ reg_write(lanai, lanai->conf1, Config1_Reg);
+}
+
+static inline void conf2_write(const struct lanai_dev *lanai)
+{
+ reg_write(lanai, lanai->conf2, Config2_Reg);
+}
+
+/* Same as conf2_write(), but defers I/O if we're powered down */
+static inline void conf2_write_if_powerup(const struct lanai_dev *lanai)
+{
+#ifdef USE_POWERDOWN
+ if (unlikely((lanai->conf1 & CONFIG1_POWERDOWN) != 0))
+ return;
+#endif /* USE_POWERDOWN */
+ conf2_write(lanai);
+}
+
+static inline void reset_board(const struct lanai_dev *lanai)
+{
+ DPRINTK("about to reset board\n");
+ reg_write(lanai, 0, Reset_Reg);
+ /*
+ * If we don't delay a little while here then we can end up
+ * leaving the card in a VERY weird state and lock up the
+ * PCI bus. This isn't documented anywhere but I've convinced
+ * myself after a lot of painful experimentation
+ */
+ udelay(5);
+}
+
+/* -------------------- CARD SRAM UTILITIES: */
+
+/* The SRAM is mapped into normal PCI memory space - the only catch is
+ * that it is only 16-bits wide but must be accessed as 32-bit. The
+ * 16 high bits will be zero. We don't hide this, since they get
+ * programmed mostly like discrete registers anyway
+ */
+#define SRAM_START (0x20000)
+#define SRAM_BYTES (0x20000) /* Again, half don't really exist */
+
+static inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset)
+{
+ return lanai->base + SRAM_START + offset;
+}
+
+static inline u32 sram_read(const struct lanai_dev *lanai, int offset)
+{
+ return readl(sram_addr(lanai, offset));
+}
+
+static inline void sram_write(const struct lanai_dev *lanai,
+ u32 val, int offset)
+{
+ writel(val, sram_addr(lanai, offset));
+}
+
+static int sram_test_word(const struct lanai_dev *lanai, int offset,
+ u32 pattern)
+{
+ u32 readback;
+ sram_write(lanai, pattern, offset);
+ readback = sram_read(lanai, offset);
+ if (likely(readback == pattern))
+ return 0;
+ printk(KERN_ERR DEV_LABEL
+ "(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n",
+ lanai->number, offset,
+ (unsigned int) pattern, (unsigned int) readback);
+ return -EIO;
+}
+
+static int sram_test_pass(const struct lanai_dev *lanai, u32 pattern)
+{
+ int offset, result = 0;
+ for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4)
+ result = sram_test_word(lanai, offset, pattern);
+ return result;
+}
+
+static int sram_test_and_clear(const struct lanai_dev *lanai)
+{
+#ifdef FULL_MEMORY_TEST
+ int result;
+ DPRINTK("testing SRAM\n");
+ if ((result = sram_test_pass(lanai, 0x5555)) != 0)
+ return result;
+ if ((result = sram_test_pass(lanai, 0xAAAA)) != 0)
+ return result;
+#endif
+ DPRINTK("clearing SRAM\n");
+ return sram_test_pass(lanai, 0x0000);
+}
+
+/* -------------------- CARD-BASED VCC TABLE UTILITIES: */
+
+/* vcc table */
+enum lanai_vcc_offset {
+ vcc_rxaddr1 = 0x00, /* Location1, plus bits: */
+#define RXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of RX buffer */
+#define RXADDR1_SET_RMMODE(x) ((x)*0x00800) /* RM cell action; values: */
+#define RMMODE_TRASH (0) /* discard */
+#define RMMODE_PRESERVE (1) /* input as AAL0 */
+#define RMMODE_PIPE (2) /* pipe to coscheduler */
+#define RMMODE_PIPEALL (3) /* pipe non-RM too */
+#define RXADDR1_OAM_PRESERVE (0x00002000) /* Input OAM cells as AAL0 */
+#define RXADDR1_SET_MODE(x) ((x)*0x0004000) /* Reassembly mode */
+#define RXMODE_TRASH (0) /* discard */
+#define RXMODE_AAL0 (1) /* non-AAL5 mode */
+#define RXMODE_AAL5 (2) /* AAL5, intr. each PDU */
+#define RXMODE_AAL5_STREAM (3) /* AAL5 w/o per-PDU intr */
+ vcc_rxaddr2 = 0x04, /* Location2 */
+ vcc_rxcrc1 = 0x08, /* RX CRC claculation space */
+ vcc_rxcrc2 = 0x0C,
+ vcc_rxwriteptr = 0x10, /* RX writeptr, plus bits: */
+#define RXWRITEPTR_LASTEFCI (0x00002000) /* Last PDU had EFCI bit */
+#define RXWRITEPTR_DROPPING (0x00004000) /* Had error, dropping */
+#define RXWRITEPTR_TRASHING (0x00008000) /* Trashing */
+ vcc_rxbufstart = 0x14, /* RX bufstart, plus bits: */
+#define RXBUFSTART_CLP (0x00004000)
+#define RXBUFSTART_CI (0x00008000)
+ vcc_rxreadptr = 0x18, /* RX readptr */
+ vcc_txicg = 0x1C, /* TX ICG */
+ vcc_txaddr1 = 0x20, /* Location1, plus bits: */
+#define TXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of TX buffer */
+#define TXADDR1_ABR (0x00008000) /* use ABR (doesn't work) */
+ vcc_txaddr2 = 0x24, /* Location2 */
+ vcc_txcrc1 = 0x28, /* TX CRC claculation space */
+ vcc_txcrc2 = 0x2C,
+ vcc_txreadptr = 0x30, /* TX Readptr, plus bits: */
+#define TXREADPTR_GET_PTR(x) ((x)&0x01FFF)
+#define TXREADPTR_MASK_DELTA (0x0000E000) /* ? */
+ vcc_txendptr = 0x34, /* TX Endptr, plus bits: */
+#define TXENDPTR_CLP (0x00002000)
+#define TXENDPTR_MASK_PDUMODE (0x0000C000) /* PDU mode; values: */
+#define PDUMODE_AAL0 (0*0x04000)
+#define PDUMODE_AAL5 (2*0x04000)
+#define PDUMODE_AAL5STREAM (3*0x04000)
+ vcc_txwriteptr = 0x38, /* TX Writeptr */
+#define TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF)
+ vcc_txcbr_next = 0x3C /* # of next CBR VCI in ring */
+#define TXCBR_NEXT_BOZO (0x00008000) /* "bozo bit" */
+};
+
+#define CARDVCC_SIZE (0x40)
+
+static inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai,
+ vci_t vci)
+{
+ return sram_addr(lanai, vci * CARDVCC_SIZE);
+}
+
+static inline u32 cardvcc_read(const struct lanai_vcc *lvcc,
+ enum lanai_vcc_offset offset)
+{
+ u32 val;
+ APRINTK(lvcc->vbase != NULL, "cardvcc_read: unbound vcc!\n");
+ val= readl(lvcc->vbase + offset);
+ RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n",
+ lvcc->vci, (int) offset, val);
+ return val;
+}
+
+static inline void cardvcc_write(const struct lanai_vcc *lvcc,
+ u32 val, enum lanai_vcc_offset offset)
+{
+ APRINTK(lvcc->vbase != NULL, "cardvcc_write: unbound vcc!\n");
+ APRINTK((val & ~0xFFFF) == 0,
+ "cardvcc_write: bad val 0x%X (vci=%d, addr=0x%02X)\n",
+ (unsigned int) val, lvcc->vci, (unsigned int) offset);
+ RWDEBUG("VW vci=%04d 0x%02X > 0x%08X\n",
+ lvcc->vci, (unsigned int) offset, (unsigned int) val);
+ writel(val, lvcc->vbase + offset);
+}
+
+/* -------------------- COMPUTE SIZE OF AN AAL5 PDU: */
+
+/* How many bytes will an AAL5 PDU take to transmit - remember that:
+ * o we need to add 8 bytes for length, CPI, UU, and CRC
+ * o we need to round up to 48 bytes for cells
+ */
+static inline int aal5_size(int size)
+{
+ int cells = (size + 8 + 47) / 48;
+ return cells * 48;
+}
+
+/* -------------------- FREE AN ATM SKB: */
+
+static inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb)
+{
+ if (atmvcc->pop != NULL)
+ atmvcc->pop(atmvcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+}
+
+/* -------------------- TURN VCCS ON AND OFF: */
+
+static void host_vcc_start_rx(const struct lanai_vcc *lvcc)
+{
+ u32 addr1;
+ if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) {
+ dma_addr_t dmaaddr = lvcc->rx.buf.dmaaddr;
+ cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1);
+ cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2);
+ cardvcc_write(lvcc, 0, vcc_rxwriteptr);
+ cardvcc_write(lvcc, 0, vcc_rxbufstart);
+ cardvcc_write(lvcc, 0, vcc_rxreadptr);
+ cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_rxaddr2);
+ addr1 = ((dmaaddr >> 8) & 0xFF) |
+ RXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->rx.buf))|
+ RXADDR1_SET_RMMODE(RMMODE_TRASH) | /* ??? */
+ /* RXADDR1_OAM_PRESERVE | --- no OAM support yet */
+ RXADDR1_SET_MODE(RXMODE_AAL5);
+ } else
+ addr1 = RXADDR1_SET_RMMODE(RMMODE_PRESERVE) | /* ??? */
+ RXADDR1_OAM_PRESERVE | /* ??? */
+ RXADDR1_SET_MODE(RXMODE_AAL0);
+ /* This one must be last! */
+ cardvcc_write(lvcc, addr1, vcc_rxaddr1);
+}
+
+static void host_vcc_start_tx(const struct lanai_vcc *lvcc)
+{
+ dma_addr_t dmaaddr = lvcc->tx.buf.dmaaddr;
+ cardvcc_write(lvcc, 0, vcc_txicg);
+ cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1);
+ cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2);
+ cardvcc_write(lvcc, 0, vcc_txreadptr);
+ cardvcc_write(lvcc, 0, vcc_txendptr);
+ cardvcc_write(lvcc, 0, vcc_txwriteptr);
+ cardvcc_write(lvcc,
+ (lvcc->tx.atmvcc->qos.txtp.traffic_class == ATM_CBR) ?
+ TXCBR_NEXT_BOZO | lvcc->vci : 0, vcc_txcbr_next);
+ cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_txaddr2);
+ cardvcc_write(lvcc,
+ ((dmaaddr >> 8) & 0xFF) |
+ TXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->tx.buf)),
+ vcc_txaddr1);
+}
+
+/* Shutdown receiving on card */
+static void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc)
+{
+ if (lvcc->vbase == NULL) /* We were never bound to a VCI */
+ return;
+ /* 15.1.1 - set to trashing, wait one cell time (15us) */
+ cardvcc_write(lvcc,
+ RXADDR1_SET_RMMODE(RMMODE_TRASH) |
+ RXADDR1_SET_MODE(RXMODE_TRASH), vcc_rxaddr1);
+ udelay(15);
+ /* 15.1.2 - clear rest of entries */
+ cardvcc_write(lvcc, 0, vcc_rxaddr2);
+ cardvcc_write(lvcc, 0, vcc_rxcrc1);
+ cardvcc_write(lvcc, 0, vcc_rxcrc2);
+ cardvcc_write(lvcc, 0, vcc_rxwriteptr);
+ cardvcc_write(lvcc, 0, vcc_rxbufstart);
+ cardvcc_write(lvcc, 0, vcc_rxreadptr);
+}
+
+/* Shutdown transmitting on card.
+ * Unfortunately the lanai needs us to wait until all the data
+ * drains out of the buffer before we can dealloc it, so this
+ * can take awhile -- up to 370ms for a full 128KB buffer
+ * assuming everone else is quiet. In theory the time is
+ * boundless if there's a CBR VCC holding things up.
+ */
+static void lanai_shutdown_tx_vci(struct lanai_dev *lanai,
+ struct lanai_vcc *lvcc)
+{
+ struct sk_buff *skb;
+ unsigned long flags, timeout;
+ int read, write, lastread = -1;
+ APRINTK(!in_interrupt(),
+ "lanai_shutdown_tx_vci called w/o process context!\n");
+ if (lvcc->vbase == NULL) /* We were never bound to a VCI */
+ return;
+ /* 15.2.1 - wait for queue to drain */
+ while ((skb = skb_dequeue(&lvcc->tx.backlog)) != NULL)
+ lanai_free_skb(lvcc->tx.atmvcc, skb);
+ read_lock_irqsave(&vcc_sklist_lock, flags);
+ __clear_bit(lvcc->vci, lanai->backlog_vccs);
+ read_unlock_irqrestore(&vcc_sklist_lock, flags);
+ /*
+ * We need to wait for the VCC to drain but don't wait forever. We
+ * give each 1K of buffer size 1/128th of a second to clear out.
+ * TODO: maybe disable CBR if we're about to timeout?
+ */
+ timeout = jiffies +
+ (((lanai_buf_size(&lvcc->tx.buf) / 1024) * HZ) >> 7);
+ write = TXWRITEPTR_GET_PTR(cardvcc_read(lvcc, vcc_txwriteptr));
+ for (;;) {
+ read = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr));
+ if (read == write && /* Is TX buffer empty? */
+ (lvcc->tx.atmvcc->qos.txtp.traffic_class != ATM_CBR ||
+ (cardvcc_read(lvcc, vcc_txcbr_next) &
+ TXCBR_NEXT_BOZO) == 0))
+ break;
+ if (read != lastread) { /* Has there been any progress? */
+ lastread = read;
+ timeout += HZ / 10;
+ }
+ if (unlikely(time_after(jiffies, timeout))) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): Timed out on "
+ "backlog closing vci %d\n",
+ lvcc->tx.atmvcc->dev->number, lvcc->vci);
+ DPRINTK("read, write = %d, %d\n", read, write);
+ break;
+ }
+ msleep(40);
+ }
+ /* 15.2.2 - clear out all tx registers */
+ cardvcc_write(lvcc, 0, vcc_txreadptr);
+ cardvcc_write(lvcc, 0, vcc_txwriteptr);
+ cardvcc_write(lvcc, 0, vcc_txendptr);
+ cardvcc_write(lvcc, 0, vcc_txcrc1);
+ cardvcc_write(lvcc, 0, vcc_txcrc2);
+ cardvcc_write(lvcc, 0, vcc_txaddr2);
+ cardvcc_write(lvcc, 0, vcc_txaddr1);
+}
+
+/* -------------------- MANAGING AAL0 RX BUFFER: */
+
+static inline int aal0_buffer_allocate(struct lanai_dev *lanai)
+{
+ DPRINTK("aal0_buffer_allocate: allocating AAL0 RX buffer\n");
+ lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80,
+ lanai->pci);
+ return (lanai->aal0buf.start == NULL) ? -ENOMEM : 0;
+}
+
+static inline void aal0_buffer_free(struct lanai_dev *lanai)
+{
+ DPRINTK("aal0_buffer_allocate: freeing AAL0 RX buffer\n");
+ lanai_buf_deallocate(&lanai->aal0buf, lanai->pci);
+}
+
+/* -------------------- EEPROM UTILITIES: */
+
+/* Offsets of data in the EEPROM */
+#define EEPROM_COPYRIGHT (0)
+#define EEPROM_COPYRIGHT_LEN (44)
+#define EEPROM_CHECKSUM (62)
+#define EEPROM_CHECKSUM_REV (63)
+#define EEPROM_MAC (64)
+#define EEPROM_MAC_REV (70)
+#define EEPROM_SERIAL (112)
+#define EEPROM_SERIAL_REV (116)
+#define EEPROM_MAGIC (120)
+#define EEPROM_MAGIC_REV (124)
+
+#define EEPROM_MAGIC_VALUE (0x5AB478D2)
+
+#ifndef READ_EEPROM
+
+/* Stub functions to use if EEPROM reading is disabled */
+static int eeprom_read(struct lanai_dev *lanai)
+{
+ printk(KERN_INFO DEV_LABEL "(itf %d): *NOT* reading EEPROM\n",
+ lanai->number);
+ memset(&lanai->eeprom[EEPROM_MAC], 0, 6);
+ return 0;
+}
+
+static int eeprom_validate(struct lanai_dev *lanai)
+{
+ lanai->serialno = 0;
+ lanai->magicno = EEPROM_MAGIC_VALUE;
+ return 0;
+}
+
+#else /* READ_EEPROM */
+
+static int eeprom_read(struct lanai_dev *lanai)
+{
+ int i, address;
+ u8 data;
+ u32 tmp;
+#define set_config1(x) do { lanai->conf1 = x; conf1_write(lanai); \
+ } while (0)
+#define clock_h() set_config1(lanai->conf1 | CONFIG1_PROMCLK)
+#define clock_l() set_config1(lanai->conf1 &~ CONFIG1_PROMCLK)
+#define data_h() set_config1(lanai->conf1 | CONFIG1_PROMDATA)
+#define data_l() set_config1(lanai->conf1 &~ CONFIG1_PROMDATA)
+#define pre_read() do { data_h(); clock_h(); udelay(5); } while (0)
+#define read_pin() (reg_read(lanai, Status_Reg) & STATUS_PROMDATA)
+#define send_stop() do { data_l(); udelay(5); clock_h(); udelay(5); \
+ data_h(); udelay(5); } while (0)
+ /* start with both clock and data high */
+ data_h(); clock_h(); udelay(5);
+ for (address = 0; address < LANAI_EEPROM_SIZE; address++) {
+ data = (address << 1) | 1; /* Command=read + address */
+ /* send start bit */
+ data_l(); udelay(5);
+ clock_l(); udelay(5);
+ for (i = 128; i != 0; i >>= 1) { /* write command out */
+ tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) |
+ ((data & i) ? CONFIG1_PROMDATA : 0);
+ if (lanai->conf1 != tmp) {
+ set_config1(tmp);
+ udelay(5); /* Let new data settle */
+ }
+ clock_h(); udelay(5); clock_l(); udelay(5);
+ }
+ /* look for ack */
+ data_h(); clock_h(); udelay(5);
+ if (read_pin() != 0)
+ goto error; /* No ack seen */
+ clock_l(); udelay(5);
+ /* read back result */
+ for (data = 0, i = 7; i >= 0; i--) {
+ data_h(); clock_h(); udelay(5);
+ data = (data << 1) | !!read_pin();
+ clock_l(); udelay(5);
+ }
+ /* look again for ack */
+ data_h(); clock_h(); udelay(5);
+ if (read_pin() == 0)
+ goto error; /* Spurious ack */
+ clock_l(); udelay(5);
+ send_stop();
+ lanai->eeprom[address] = data;
+ DPRINTK("EEPROM 0x%04X %02X\n",
+ (unsigned int) address, (unsigned int) data);
+ }
+ return 0;
+ error:
+ clock_l(); udelay(5); /* finish read */
+ send_stop();
+ printk(KERN_ERR DEV_LABEL "(itf %d): error reading EEPROM byte %d\n",
+ lanai->number, address);
+ return -EIO;
+#undef set_config1
+#undef clock_h
+#undef clock_l
+#undef data_h
+#undef data_l
+#undef pre_read
+#undef read_pin
+#undef send_stop
+}
+
+/* read a big-endian 4-byte value out of eeprom */
+static inline u32 eeprom_be4(const struct lanai_dev *lanai, int address)
+{
+ return be32_to_cpup((const u32 *) &lanai->eeprom[address]);
+}
+
+/* Checksum/validate EEPROM contents */
+static int eeprom_validate(struct lanai_dev *lanai)
+{
+ int i, s;
+ u32 v;
+ const u8 *e = lanai->eeprom;
+#ifdef DEBUG
+ /* First, see if we can get an ASCIIZ string out of the copyright */
+ for (i = EEPROM_COPYRIGHT;
+ i < (EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN); i++)
+ if (e[i] < 0x20 || e[i] > 0x7E)
+ break;
+ if ( i != EEPROM_COPYRIGHT &&
+ i != EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN && e[i] == '\0')
+ DPRINTK("eeprom: copyright = \"%s\"\n",
+ (char *) &e[EEPROM_COPYRIGHT]);
+ else
+ DPRINTK("eeprom: copyright not found\n");
+#endif
+ /* Validate checksum */
+ for (i = s = 0; i < EEPROM_CHECKSUM; i++)
+ s += e[i];
+ s &= 0xFF;
+ if (s != e[EEPROM_CHECKSUM]) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM checksum bad "
+ "(wanted 0x%02X, got 0x%02X)\n", lanai->number,
+ (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM]);
+ return -EIO;
+ }
+ s ^= 0xFF;
+ if (s != e[EEPROM_CHECKSUM_REV]) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM inverse checksum "
+ "bad (wanted 0x%02X, got 0x%02X)\n", lanai->number,
+ (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM_REV]);
+ return -EIO;
+ }
+ /* Verify MAC address */
+ for (i = 0; i < 6; i++)
+ if ((e[EEPROM_MAC + i] ^ e[EEPROM_MAC_REV + i]) != 0xFF) {
+ printk(KERN_ERR DEV_LABEL
+ "(itf %d) : EEPROM MAC addresses don't match "
+ "(0x%02X, inverse 0x%02X)\n", lanai->number,
+ (unsigned int) e[EEPROM_MAC + i],
+ (unsigned int) e[EEPROM_MAC_REV + i]);
+ return -EIO;
+ }
+ DPRINTK("eeprom: MAC address = %pM\n", &e[EEPROM_MAC]);
+ /* Verify serial number */
+ lanai->serialno = eeprom_be4(lanai, EEPROM_SERIAL);
+ v = eeprom_be4(lanai, EEPROM_SERIAL_REV);
+ if ((lanai->serialno ^ v) != 0xFFFFFFFF) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM serial numbers "
+ "don't match (0x%08X, inverse 0x%08X)\n", lanai->number,
+ (unsigned int) lanai->serialno, (unsigned int) v);
+ return -EIO;
+ }
+ DPRINTK("eeprom: Serial number = %d\n", (unsigned int) lanai->serialno);
+ /* Verify magic number */
+ lanai->magicno = eeprom_be4(lanai, EEPROM_MAGIC);
+ v = eeprom_be4(lanai, EEPROM_MAGIC_REV);
+ if ((lanai->magicno ^ v) != 0xFFFFFFFF) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM magic numbers "
+ "don't match (0x%08X, inverse 0x%08X)\n", lanai->number,
+ lanai->magicno, v);
+ return -EIO;
+ }
+ DPRINTK("eeprom: Magic number = 0x%08X\n", lanai->magicno);
+ if (lanai->magicno != EEPROM_MAGIC_VALUE)
+ printk(KERN_WARNING DEV_LABEL "(itf %d): warning - EEPROM "
+ "magic not what expected (got 0x%08X, not 0x%08X)\n",
+ lanai->number, (unsigned int) lanai->magicno,
+ (unsigned int) EEPROM_MAGIC_VALUE);
+ return 0;
+}
+
+#endif /* READ_EEPROM */
+
+static inline const u8 *eeprom_mac(const struct lanai_dev *lanai)
+{
+ return &lanai->eeprom[EEPROM_MAC];
+}
+
+/* -------------------- INTERRUPT HANDLING UTILITIES: */
+
+/* Interrupt types */
+#define INT_STATS (0x00000002) /* Statistics counter overflow */
+#define INT_SOOL (0x00000004) /* SOOL changed state */
+#define INT_LOCD (0x00000008) /* LOCD changed state */
+#define INT_LED (0x00000010) /* LED (HAPPI) changed state */
+#define INT_GPIN (0x00000020) /* GPIN changed state */
+#define INT_PING (0x00000040) /* PING_COUNT fulfilled */
+#define INT_WAKE (0x00000080) /* Lanai wants bus */
+#define INT_CBR0 (0x00000100) /* CBR sched hit VCI 0 */
+#define INT_LOCK (0x00000200) /* Service list overflow */
+#define INT_MISMATCH (0x00000400) /* TX magic list mismatch */
+#define INT_AAL0_STR (0x00000800) /* Non-AAL5 buffer half filled */
+#define INT_AAL0 (0x00001000) /* Non-AAL5 data available */
+#define INT_SERVICE (0x00002000) /* Service list entries available */
+#define INT_TABORTSENT (0x00004000) /* Target abort sent by lanai */
+#define INT_TABORTBM (0x00008000) /* Abort rcv'd as bus master */
+#define INT_TIMEOUTBM (0x00010000) /* No response to bus master */
+#define INT_PCIPARITY (0x00020000) /* Parity error on PCI */
+
+/* Sets of the above */
+#define INT_ALL (0x0003FFFE) /* All interrupts */
+#define INT_STATUS (0x0000003C) /* Some status pin changed */
+#define INT_DMASHUT (0x00038000) /* DMA engine got shut down */
+#define INT_SEGSHUT (0x00000700) /* Segmentation got shut down */
+
+static inline u32 intr_pending(const struct lanai_dev *lanai)
+{
+ return reg_read(lanai, IntStatusMasked_Reg);
+}
+
+static inline void intr_enable(const struct lanai_dev *lanai, u32 i)
+{
+ reg_write(lanai, i, IntControlEna_Reg);
+}
+
+static inline void intr_disable(const struct lanai_dev *lanai, u32 i)
+{
+ reg_write(lanai, i, IntControlDis_Reg);
+}
+
+/* -------------------- CARD/PCI STATUS: */
+
+static void status_message(int itf, const char *name, int status)
+{
+ static const char *onoff[2] = { "off to on", "on to off" };
+ printk(KERN_INFO DEV_LABEL "(itf %d): %s changed from %s\n",
+ itf, name, onoff[!status]);
+}
+
+static void lanai_check_status(struct lanai_dev *lanai)
+{
+ u32 new = reg_read(lanai, Status_Reg);
+ u32 changes = new ^ lanai->status;
+ lanai->status = new;
+#define e(flag, name) \
+ if (changes & flag) \
+ status_message(lanai->number, name, new & flag)
+ e(STATUS_SOOL, "SOOL");
+ e(STATUS_LOCD, "LOCD");
+ e(STATUS_LED, "LED");
+ e(STATUS_GPIN, "GPIN");
+#undef e
+}
+
+static void pcistatus_got(int itf, const char *name)
+{
+ printk(KERN_INFO DEV_LABEL "(itf %d): PCI got %s error\n", itf, name);
+}
+
+static void pcistatus_check(struct lanai_dev *lanai, int clearonly)
+{
+ u16 s;
+ int result;
+ result = pci_read_config_word(lanai->pci, PCI_STATUS, &s);
+ if (result != PCIBIOS_SUCCESSFUL) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_STATUS: "
+ "%d\n", lanai->number, result);
+ return;
+ }
+ s &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR |
+ PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT |
+ PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_PARITY;
+ if (s == 0)
+ return;
+ result = pci_write_config_word(lanai->pci, PCI_STATUS, s);
+ if (result != PCIBIOS_SUCCESSFUL)
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't write PCI_STATUS: "
+ "%d\n", lanai->number, result);
+ if (clearonly)
+ return;
+#define e(flag, name, stat) \
+ if (s & flag) { \
+ pcistatus_got(lanai->number, name); \
+ ++lanai->stats.pcierr_##stat; \
+ }
+ e(PCI_STATUS_DETECTED_PARITY, "parity", parity_detect);
+ e(PCI_STATUS_SIG_SYSTEM_ERROR, "signalled system", serr_set);
+ e(PCI_STATUS_REC_MASTER_ABORT, "master", master_abort);
+ e(PCI_STATUS_REC_TARGET_ABORT, "master target", m_target_abort);
+ e(PCI_STATUS_SIG_TARGET_ABORT, "slave", s_target_abort);
+ e(PCI_STATUS_PARITY, "master parity", master_parity);
+#undef e
+}
+
+/* -------------------- VCC TX BUFFER UTILITIES: */
+
+/* space left in tx buffer in bytes */
+static inline int vcc_tx_space(const struct lanai_vcc *lvcc, int endptr)
+{
+ int r;
+ r = endptr * 16;
+ r -= ((unsigned long) lvcc->tx.buf.ptr) -
+ ((unsigned long) lvcc->tx.buf.start);
+ r -= 16; /* Leave "bubble" - if start==end it looks empty */
+ if (r < 0)
+ r += lanai_buf_size(&lvcc->tx.buf);
+ return r;
+}
+
+/* test if VCC is currently backlogged */
+static inline int vcc_is_backlogged(const struct lanai_vcc *lvcc)
+{
+ return !skb_queue_empty(&lvcc->tx.backlog);
+}
+
+/* Bit fields in the segmentation buffer descriptor */
+#define DESCRIPTOR_MAGIC (0xD0000000)
+#define DESCRIPTOR_AAL5 (0x00008000)
+#define DESCRIPTOR_AAL5_STREAM (0x00004000)
+#define DESCRIPTOR_CLP (0x00002000)
+
+/* Add 32-bit descriptor with its padding */
+static inline void vcc_tx_add_aal5_descriptor(struct lanai_vcc *lvcc,
+ u32 flags, int len)
+{
+ int pos;
+ APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 0,
+ "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr);
+ lvcc->tx.buf.ptr += 4; /* Hope the values REALLY don't matter */
+ pos = ((unsigned char *) lvcc->tx.buf.ptr) -
+ (unsigned char *) lvcc->tx.buf.start;
+ APRINTK((pos & ~0x0001FFF0) == 0,
+ "vcc_tx_add_aal5_descriptor: bad pos (%d) before, vci=%d, "
+ "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci,
+ lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end);
+ pos = (pos + len) & (lanai_buf_size(&lvcc->tx.buf) - 1);
+ APRINTK((pos & ~0x0001FFF0) == 0,
+ "vcc_tx_add_aal5_descriptor: bad pos (%d) after, vci=%d, "
+ "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci,
+ lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end);
+ lvcc->tx.buf.ptr[-1] =
+ cpu_to_le32(DESCRIPTOR_MAGIC | DESCRIPTOR_AAL5 |
+ ((lvcc->tx.atmvcc->atm_options & ATM_ATMOPT_CLP) ?
+ DESCRIPTOR_CLP : 0) | flags | pos >> 4);
+ if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end)
+ lvcc->tx.buf.ptr = lvcc->tx.buf.start;
+}
+
+/* Add 32-bit AAL5 trailer and leave room for its CRC */
+static inline void vcc_tx_add_aal5_trailer(struct lanai_vcc *lvcc,
+ int len, int cpi, int uu)
+{
+ APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 8,
+ "vcc_tx_add_aal5_trailer: bad ptr=%p\n", lvcc->tx.buf.ptr);
+ lvcc->tx.buf.ptr += 2;
+ lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len);
+ if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end)
+ lvcc->tx.buf.ptr = lvcc->tx.buf.start;
+}
+
+static inline void vcc_tx_memcpy(struct lanai_vcc *lvcc,
+ const unsigned char *src, int n)
+{
+ unsigned char *e;
+ int m;
+ e = ((unsigned char *) lvcc->tx.buf.ptr) + n;
+ m = e - (unsigned char *) lvcc->tx.buf.end;
+ if (m < 0)
+ m = 0;
+ memcpy(lvcc->tx.buf.ptr, src, n - m);
+ if (m != 0) {
+ memcpy(lvcc->tx.buf.start, src + n - m, m);
+ e = ((unsigned char *) lvcc->tx.buf.start) + m;
+ }
+ lvcc->tx.buf.ptr = (u32 *) e;
+}
+
+static inline void vcc_tx_memzero(struct lanai_vcc *lvcc, int n)
+{
+ unsigned char *e;
+ int m;
+ if (n == 0)
+ return;
+ e = ((unsigned char *) lvcc->tx.buf.ptr) + n;
+ m = e - (unsigned char *) lvcc->tx.buf.end;
+ if (m < 0)
+ m = 0;
+ memset(lvcc->tx.buf.ptr, 0, n - m);
+ if (m != 0) {
+ memset(lvcc->tx.buf.start, 0, m);
+ e = ((unsigned char *) lvcc->tx.buf.start) + m;
+ }
+ lvcc->tx.buf.ptr = (u32 *) e;
+}
+
+/* Update "butt" register to specify new WritePtr */
+static inline void lanai_endtx(struct lanai_dev *lanai,
+ const struct lanai_vcc *lvcc)
+{
+ int i, ptr = ((unsigned char *) lvcc->tx.buf.ptr) -
+ (unsigned char *) lvcc->tx.buf.start;
+ APRINTK((ptr & ~0x0001FFF0) == 0,
+ "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n",
+ ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr,
+ lvcc->tx.buf.end);
+
+ /*
+ * Since the "butt register" is a shared resounce on the card we
+ * serialize all accesses to it through this spinlock. This is
+ * mostly just paranoia since the register is rarely "busy" anyway
+ * but is needed for correctness.
+ */
+ spin_lock(&lanai->endtxlock);
+ /*
+ * We need to check if the "butt busy" bit is set before
+ * updating the butt register. In theory this should
+ * never happen because the ATM card is plenty fast at
+ * updating the register. Still, we should make sure
+ */
+ for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) {
+ if (unlikely(i > 50)) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): butt register "
+ "always busy!\n", lanai->number);
+ break;
+ }
+ udelay(5);
+ }
+ /*
+ * Before we tall the card to start work we need to be sure 100% of
+ * the info in the service buffer has been written before we tell
+ * the card about it
+ */
+ wmb();
+ reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg);
+ spin_unlock(&lanai->endtxlock);
+}
+
+/*
+ * Add one AAL5 PDU to lvcc's transmit buffer. Caller garauntees there's
+ * space available. "pdusize" is the number of bytes the PDU will take
+ */
+static void lanai_send_one_aal5(struct lanai_dev *lanai,
+ struct lanai_vcc *lvcc, struct sk_buff *skb, int pdusize)
+{
+ int pad;
+ APRINTK(pdusize == aal5_size(skb->len),
+ "lanai_send_one_aal5: wrong size packet (%d != %d)\n",
+ pdusize, aal5_size(skb->len));
+ vcc_tx_add_aal5_descriptor(lvcc, 0, pdusize);
+ pad = pdusize - skb->len - 8;
+ APRINTK(pad >= 0, "pad is negative (%d)\n", pad);
+ APRINTK(pad < 48, "pad is too big (%d)\n", pad);
+ vcc_tx_memcpy(lvcc, skb->data, skb->len);
+ vcc_tx_memzero(lvcc, pad);
+ vcc_tx_add_aal5_trailer(lvcc, skb->len, 0, 0);
+ lanai_endtx(lanai, lvcc);
+ lanai_free_skb(lvcc->tx.atmvcc, skb);
+ atomic_inc(&lvcc->tx.atmvcc->stats->tx);
+}
+
+/* Try to fill the buffer - don't call unless there is backlog */
+static void vcc_tx_unqueue_aal5(struct lanai_dev *lanai,
+ struct lanai_vcc *lvcc, int endptr)
+{
+ int n;
+ struct sk_buff *skb;
+ int space = vcc_tx_space(lvcc, endptr);
+ APRINTK(vcc_is_backlogged(lvcc),
+ "vcc_tx_unqueue() called with empty backlog (vci=%d)\n",
+ lvcc->vci);
+ while (space >= 64) {
+ skb = skb_dequeue(&lvcc->tx.backlog);
+ if (skb == NULL)
+ goto no_backlog;
+ n = aal5_size(skb->len);
+ if (n + 16 > space) {
+ /* No room for this packet - put it back on queue */
+ skb_queue_head(&lvcc->tx.backlog, skb);
+ return;
+ }
+ lanai_send_one_aal5(lanai, lvcc, skb, n);
+ space -= n + 16;
+ }
+ if (!vcc_is_backlogged(lvcc)) {
+ no_backlog:
+ __clear_bit(lvcc->vci, lanai->backlog_vccs);
+ }
+}
+
+/* Given an skb that we want to transmit either send it now or queue */
+static void vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc,
+ struct sk_buff *skb)
+{
+ int space, n;
+ if (vcc_is_backlogged(lvcc)) /* Already backlogged */
+ goto queue_it;
+ space = vcc_tx_space(lvcc,
+ TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)));
+ n = aal5_size(skb->len);
+ APRINTK(n + 16 >= 64, "vcc_tx_aal5: n too small (%d)\n", n);
+ if (space < n + 16) { /* No space for this PDU */
+ __set_bit(lvcc->vci, lanai->backlog_vccs);
+ queue_it:
+ skb_queue_tail(&lvcc->tx.backlog, skb);
+ return;
+ }
+ lanai_send_one_aal5(lanai, lvcc, skb, n);
+}
+
+static void vcc_tx_unqueue_aal0(struct lanai_dev *lanai,
+ struct lanai_vcc *lvcc, int endptr)
+{
+ printk(KERN_INFO DEV_LABEL
+ ": vcc_tx_unqueue_aal0: not implemented\n");
+}
+
+static void vcc_tx_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc,
+ struct sk_buff *skb)
+{
+ printk(KERN_INFO DEV_LABEL ": vcc_tx_aal0: not implemented\n");
+ /* Remember to increment lvcc->tx.atmvcc->stats->tx */
+ lanai_free_skb(lvcc->tx.atmvcc, skb);
+}
+
+/* -------------------- VCC RX BUFFER UTILITIES: */
+
+/* unlike the _tx_ cousins, this doesn't update ptr */
+static inline void vcc_rx_memcpy(unsigned char *dest,
+ const struct lanai_vcc *lvcc, int n)
+{
+ int m = ((const unsigned char *) lvcc->rx.buf.ptr) + n -
+ ((const unsigned char *) (lvcc->rx.buf.end));
+ if (m < 0)
+ m = 0;
+ memcpy(dest, lvcc->rx.buf.ptr, n - m);
+ memcpy(dest + n - m, lvcc->rx.buf.start, m);
+ /* Make sure that these copies don't get reordered */
+ barrier();
+}
+
+/* Receive AAL5 data on a VCC with a particular endptr */
+static void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr)
+{
+ int size;
+ struct sk_buff *skb;
+ const u32 *x;
+ u32 *end = &lvcc->rx.buf.start[endptr * 4];
+ int n = ((unsigned long) end) - ((unsigned long) lvcc->rx.buf.ptr);
+ if (n < 0)
+ n += lanai_buf_size(&lvcc->rx.buf);
+ APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15),
+ "vcc_rx_aal5: n out of range (%d/%Zu)\n",
+ n, lanai_buf_size(&lvcc->rx.buf));
+ /* Recover the second-to-last word to get true pdu length */
+ if ((x = &end[-2]) < lvcc->rx.buf.start)
+ x = &lvcc->rx.buf.end[-2];
+ /*
+ * Before we actually read from the buffer, make sure the memory
+ * changes have arrived
+ */
+ rmb();
+ size = be32_to_cpup(x) & 0xffff;
+ if (unlikely(n != aal5_size(size))) {
+ /* Make sure size matches padding */
+ printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length "
+ "on vci=%d - size=%d n=%d\n",
+ lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n);
+ lvcc->stats.x.aal5.rx_badlen++;
+ goto out;
+ }
+ skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC);
+ if (unlikely(skb == NULL)) {
+ lvcc->stats.rx_nomem++;
+ goto out;
+ }
+ skb_put(skb, size);
+ vcc_rx_memcpy(skb->data, lvcc, size);
+ ATM_SKB(skb)->vcc = lvcc->rx.atmvcc;
+ __net_timestamp(skb);
+ lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb);
+ atomic_inc(&lvcc->rx.atmvcc->stats->rx);
+ out:
+ lvcc->rx.buf.ptr = end;
+ cardvcc_write(lvcc, endptr, vcc_rxreadptr);
+}
+
+static void vcc_rx_aal0(struct lanai_dev *lanai)
+{
+ printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n");
+ /* Remember to get read_lock(&vcc_sklist_lock) while looking up VC */
+ /* Remember to increment lvcc->rx.atmvcc->stats->rx */
+}
+
+/* -------------------- MANAGING HOST-BASED VCC TABLE: */
+
+/* Decide whether to use vmalloc or get_zeroed_page for VCC table */
+#if (NUM_VCI * BITS_PER_LONG) <= PAGE_SIZE
+#define VCCTABLE_GETFREEPAGE
+#else
+#include <linux/vmalloc.h>
+#endif
+
+static int vcc_table_allocate(struct lanai_dev *lanai)
+{
+#ifdef VCCTABLE_GETFREEPAGE
+ APRINTK((lanai->num_vci) * sizeof(struct lanai_vcc *) <= PAGE_SIZE,
+ "vcc table > PAGE_SIZE!");
+ lanai->vccs = (struct lanai_vcc **) get_zeroed_page(GFP_KERNEL);
+ return (lanai->vccs == NULL) ? -ENOMEM : 0;
+#else
+ int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *);
+ lanai->vccs = vzalloc(bytes);
+ if (unlikely(lanai->vccs == NULL))
+ return -ENOMEM;
+ return 0;
+#endif
+}
+
+static inline void vcc_table_deallocate(const struct lanai_dev *lanai)
+{
+#ifdef VCCTABLE_GETFREEPAGE
+ free_page((unsigned long) lanai->vccs);
+#else
+ vfree(lanai->vccs);
+#endif
+}
+
+/* Allocate a fresh lanai_vcc, with the appropriate things cleared */
+static inline struct lanai_vcc *new_lanai_vcc(void)
+{
+ struct lanai_vcc *lvcc;
+ lvcc = kzalloc(sizeof(*lvcc), GFP_KERNEL);
+ if (likely(lvcc != NULL)) {
+ skb_queue_head_init(&lvcc->tx.backlog);
+#ifdef DEBUG
+ lvcc->vci = -1;
+#endif
+ }
+ return lvcc;
+}
+
+static int lanai_get_sized_buffer(struct lanai_dev *lanai,
+ struct lanai_buffer *buf, int max_sdu, int multiplier,
+ const char *name)
+{
+ int size;
+ if (unlikely(max_sdu < 1))
+ max_sdu = 1;
+ max_sdu = aal5_size(max_sdu);
+ size = (max_sdu + 16) * multiplier + 16;
+ lanai_buf_allocate(buf, size, max_sdu + 32, lanai->pci);
+ if (unlikely(buf->start == NULL))
+ return -ENOMEM;
+ if (unlikely(lanai_buf_size(buf) < size))
+ printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes "
+ "for %s buffer, got only %Zu\n", lanai->number, size,
+ name, lanai_buf_size(buf));
+ DPRINTK("Allocated %Zu byte %s buffer\n", lanai_buf_size(buf), name);
+ return 0;
+}
+
+/* Setup a RX buffer for a currently unbound AAL5 vci */
+static inline int lanai_setup_rx_vci_aal5(struct lanai_dev *lanai,
+ struct lanai_vcc *lvcc, const struct atm_qos *qos)
+{
+ return lanai_get_sized_buffer(lanai, &lvcc->rx.buf,
+ qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, "RX");
+}
+
+/* Setup a TX buffer for a currently unbound AAL5 vci */
+static int lanai_setup_tx_vci(struct lanai_dev *lanai, struct lanai_vcc *lvcc,
+ const struct atm_qos *qos)
+{
+ int max_sdu, multiplier;
+ if (qos->aal == ATM_AAL0) {
+ lvcc->tx.unqueue = vcc_tx_unqueue_aal0;
+ max_sdu = ATM_CELL_SIZE - 1;
+ multiplier = AAL0_TX_MULTIPLIER;
+ } else {
+ lvcc->tx.unqueue = vcc_tx_unqueue_aal5;
+ max_sdu = qos->txtp.max_sdu;
+ multiplier = AAL5_TX_MULTIPLIER;
+ }
+ return lanai_get_sized_buffer(lanai, &lvcc->tx.buf, max_sdu,
+ multiplier, "TX");
+}
+
+static inline void host_vcc_bind(struct lanai_dev *lanai,
+ struct lanai_vcc *lvcc, vci_t vci)
+{
+ if (lvcc->vbase != NULL)
+ return; /* We already were bound in the other direction */
+ DPRINTK("Binding vci %d\n", vci);
+#ifdef USE_POWERDOWN
+ if (lanai->nbound++ == 0) {
+ DPRINTK("Coming out of powerdown\n");
+ lanai->conf1 &= ~CONFIG1_POWERDOWN;
+ conf1_write(lanai);
+ conf2_write(lanai);
+ }
+#endif
+ lvcc->vbase = cardvcc_addr(lanai, vci);
+ lanai->vccs[lvcc->vci = vci] = lvcc;
+}
+
+static inline void host_vcc_unbind(struct lanai_dev *lanai,
+ struct lanai_vcc *lvcc)
+{
+ if (lvcc->vbase == NULL)
+ return; /* This vcc was never bound */
+ DPRINTK("Unbinding vci %d\n", lvcc->vci);
+ lvcc->vbase = NULL;
+ lanai->vccs[lvcc->vci] = NULL;
+#ifdef USE_POWERDOWN
+ if (--lanai->nbound == 0) {
+ DPRINTK("Going into powerdown\n");
+ lanai->conf1 |= CONFIG1_POWERDOWN;
+ conf1_write(lanai);
+ }
+#endif
+}
+
+/* -------------------- RESET CARD: */
+
+static void lanai_reset(struct lanai_dev *lanai)
+{
+ printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* resetting - not "
+ "implemented\n", lanai->number);
+ /* TODO */
+ /* The following is just a hack until we write the real
+ * resetter - at least ack whatever interrupt sent us
+ * here
+ */
+ reg_write(lanai, INT_ALL, IntAck_Reg);
+ lanai->stats.card_reset++;
+}
+
+/* -------------------- SERVICE LIST UTILITIES: */
+
+/*
+ * Allocate service buffer and tell card about it
+ */
+static int service_buffer_allocate(struct lanai_dev *lanai)
+{
+ lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 8,
+ lanai->pci);
+ if (unlikely(lanai->service.start == NULL))
+ return -ENOMEM;
+ DPRINTK("allocated service buffer at 0x%08lX, size %Zu(%d)\n",
+ (unsigned long) lanai->service.start,
+ lanai_buf_size(&lanai->service),
+ lanai_buf_size_cardorder(&lanai->service));
+ /* Clear ServWrite register to be safe */
+ reg_write(lanai, 0, ServWrite_Reg);
+ /* ServiceStuff register contains size and address of buffer */
+ reg_write(lanai,
+ SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) |
+ SSTUFF_SET_ADDR(lanai->service.dmaaddr),
+ ServiceStuff_Reg);
+ return 0;
+}
+
+static inline void service_buffer_deallocate(struct lanai_dev *lanai)
+{
+ lanai_buf_deallocate(&lanai->service, lanai->pci);
+}
+
+/* Bitfields in service list */
+#define SERVICE_TX (0x80000000) /* Was from transmission */
+#define SERVICE_TRASH (0x40000000) /* RXed PDU was trashed */
+#define SERVICE_CRCERR (0x20000000) /* RXed PDU had CRC error */
+#define SERVICE_CI (0x10000000) /* RXed PDU had CI set */
+#define SERVICE_CLP (0x08000000) /* RXed PDU had CLP set */
+#define SERVICE_STREAM (0x04000000) /* RX Stream mode */
+#define SERVICE_GET_VCI(x) (((x)>>16)&0x3FF)
+#define SERVICE_GET_END(x) ((x)&0x1FFF)
+
+/* Handle one thing from the service list - returns true if it marked a
+ * VCC ready for xmit
+ */
+static int handle_service(struct lanai_dev *lanai, u32 s)
+{
+ vci_t vci = SERVICE_GET_VCI(s);
+ struct lanai_vcc *lvcc;
+ read_lock(&vcc_sklist_lock);
+ lvcc = lanai->vccs[vci];
+ if (unlikely(lvcc == NULL)) {
+ read_unlock(&vcc_sklist_lock);
+ DPRINTK("(itf %d) got service entry 0x%X for nonexistent "
+ "vcc %d\n", lanai->number, (unsigned int) s, vci);
+ if (s & SERVICE_TX)
+ lanai->stats.service_notx++;
+ else
+ lanai->stats.service_norx++;
+ return 0;
+ }
+ if (s & SERVICE_TX) { /* segmentation interrupt */
+ if (unlikely(lvcc->tx.atmvcc == NULL)) {
+ read_unlock(&vcc_sklist_lock);
+ DPRINTK("(itf %d) got service entry 0x%X for non-TX "
+ "vcc %d\n", lanai->number, (unsigned int) s, vci);
+ lanai->stats.service_notx++;
+ return 0;
+ }
+ __set_bit(vci, lanai->transmit_ready);
+ lvcc->tx.endptr = SERVICE_GET_END(s);
+ read_unlock(&vcc_sklist_lock);
+ return 1;
+ }
+ if (unlikely(lvcc->rx.atmvcc == NULL)) {
+ read_unlock(&vcc_sklist_lock);
+ DPRINTK("(itf %d) got service entry 0x%X for non-RX "
+ "vcc %d\n", lanai->number, (unsigned int) s, vci);
+ lanai->stats.service_norx++;
+ return 0;
+ }
+ if (unlikely(lvcc->rx.atmvcc->qos.aal != ATM_AAL5)) {
+ read_unlock(&vcc_sklist_lock);
+ DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 "
+ "vcc %d\n", lanai->number, (unsigned int) s, vci);
+ lanai->stats.service_rxnotaal5++;
+ atomic_inc(&lvcc->rx.atmvcc->stats->rx_err);
+ return 0;
+ }
+ if (likely(!(s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)))) {
+ vcc_rx_aal5(lvcc, SERVICE_GET_END(s));
+ read_unlock(&vcc_sklist_lock);
+ return 0;
+ }
+ if (s & SERVICE_TRASH) {
+ int bytes;
+ read_unlock(&vcc_sklist_lock);
+ DPRINTK("got trashed rx pdu on vci %d\n", vci);
+ atomic_inc(&lvcc->rx.atmvcc->stats->rx_err);
+ lvcc->stats.x.aal5.service_trash++;
+ bytes = (SERVICE_GET_END(s) * 16) -
+ (((unsigned long) lvcc->rx.buf.ptr) -
+ ((unsigned long) lvcc->rx.buf.start)) + 47;
+ if (bytes < 0)
+ bytes += lanai_buf_size(&lvcc->rx.buf);
+ lanai->stats.ovfl_trash += (bytes / 48);
+ return 0;
+ }
+ if (s & SERVICE_STREAM) {
+ read_unlock(&vcc_sklist_lock);
+ atomic_inc(&lvcc->rx.atmvcc->stats->rx_err);
+ lvcc->stats.x.aal5.service_stream++;
+ printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream "
+ "PDU on VCI %d!\n", lanai->number, vci);
+ lanai_reset(lanai);
+ return 0;
+ }
+ DPRINTK("got rx crc error on vci %d\n", vci);
+ atomic_inc(&lvcc->rx.atmvcc->stats->rx_err);
+ lvcc->stats.x.aal5.service_rxcrc++;
+ lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4];
+ cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr);
+ read_unlock(&vcc_sklist_lock);
+ return 0;
+}
+
+/* Try transmitting on all VCIs that we marked ready to serve */
+static void iter_transmit(struct lanai_dev *lanai, vci_t vci)
+{
+ struct lanai_vcc *lvcc = lanai->vccs[vci];
+ if (vcc_is_backlogged(lvcc))
+ lvcc->tx.unqueue(lanai, lvcc, lvcc->tx.endptr);
+}
+
+/* Run service queue -- called from interrupt context or with
+ * interrupts otherwise disabled and with the lanai->servicelock
+ * lock held
+ */
+static void run_service(struct lanai_dev *lanai)
+{
+ int ntx = 0;
+ u32 wreg = reg_read(lanai, ServWrite_Reg);
+ const u32 *end = lanai->service.start + wreg;
+ while (lanai->service.ptr != end) {
+ ntx += handle_service(lanai,
+ le32_to_cpup(lanai->service.ptr++));
+ if (lanai->service.ptr >= lanai->service.end)
+ lanai->service.ptr = lanai->service.start;
+ }
+ reg_write(lanai, wreg, ServRead_Reg);
+ if (ntx != 0) {
+ read_lock(&vcc_sklist_lock);
+ vci_bitfield_iterate(lanai, lanai->transmit_ready,
+ iter_transmit);
+ bitmap_zero(lanai->transmit_ready, NUM_VCI);
+ read_unlock(&vcc_sklist_lock);
+ }
+}
+
+/* -------------------- GATHER STATISTICS: */
+
+static void get_statistics(struct lanai_dev *lanai)
+{
+ u32 statreg = reg_read(lanai, Statistics_Reg);
+ lanai->stats.atm_ovfl += STATS_GET_FIFO_OVFL(statreg);
+ lanai->stats.hec_err += STATS_GET_HEC_ERR(statreg);
+ lanai->stats.vci_trash += STATS_GET_BAD_VCI(statreg);
+ lanai->stats.ovfl_trash += STATS_GET_BUF_OVFL(statreg);
+}
+
+/* -------------------- POLLING TIMER: */
+
+#ifndef DEBUG_RW
+/* Try to undequeue 1 backlogged vcc */
+static void iter_dequeue(struct lanai_dev *lanai, vci_t vci)
+{
+ struct lanai_vcc *lvcc = lanai->vccs[vci];
+ int endptr;
+ if (lvcc == NULL || lvcc->tx.atmvcc == NULL ||
+ !vcc_is_backlogged(lvcc)) {
+ __clear_bit(vci, lanai->backlog_vccs);
+ return;
+ }
+ endptr = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr));
+ lvcc->tx.unqueue(lanai, lvcc, endptr);
+}
+#endif /* !DEBUG_RW */
+
+static void lanai_timed_poll(unsigned long arg)
+{
+ struct lanai_dev *lanai = (struct lanai_dev *) arg;
+#ifndef DEBUG_RW
+ unsigned long flags;
+#ifdef USE_POWERDOWN
+ if (lanai->conf1 & CONFIG1_POWERDOWN)
+ return;
+#endif /* USE_POWERDOWN */
+ local_irq_save(flags);
+ /* If we can grab the spinlock, check if any services need to be run */
+ if (spin_trylock(&lanai->servicelock)) {
+ run_service(lanai);
+ spin_unlock(&lanai->servicelock);
+ }
+ /* ...and see if any backlogged VCs can make progress */
+ /* unfortunately linux has no read_trylock() currently */
+ read_lock(&vcc_sklist_lock);
+ vci_bitfield_iterate(lanai, lanai->backlog_vccs, iter_dequeue);
+ read_unlock(&vcc_sklist_lock);
+ local_irq_restore(flags);
+
+ get_statistics(lanai);
+#endif /* !DEBUG_RW */
+ mod_timer(&lanai->timer, jiffies + LANAI_POLL_PERIOD);
+}
+
+static inline void lanai_timed_poll_start(struct lanai_dev *lanai)
+{
+ init_timer(&lanai->timer);
+ lanai->timer.expires = jiffies + LANAI_POLL_PERIOD;
+ lanai->timer.data = (unsigned long) lanai;
+ lanai->timer.function = lanai_timed_poll;
+ add_timer(&lanai->timer);
+}
+
+static inline void lanai_timed_poll_stop(struct lanai_dev *lanai)
+{
+ del_timer_sync(&lanai->timer);
+}
+
+/* -------------------- INTERRUPT SERVICE: */
+
+static inline void lanai_int_1(struct lanai_dev *lanai, u32 reason)
+{
+ u32 ack = 0;
+ if (reason & INT_SERVICE) {
+ ack = INT_SERVICE;
+ spin_lock(&lanai->servicelock);
+ run_service(lanai);
+ spin_unlock(&lanai->servicelock);
+ }
+ if (reason & (INT_AAL0_STR | INT_AAL0)) {
+ ack |= reason & (INT_AAL0_STR | INT_AAL0);
+ vcc_rx_aal0(lanai);
+ }
+ /* The rest of the interrupts are pretty rare */
+ if (ack == reason)
+ goto done;
+ if (reason & INT_STATS) {
+ reason &= ~INT_STATS; /* No need to ack */
+ get_statistics(lanai);
+ }
+ if (reason & INT_STATUS) {
+ ack |= reason & INT_STATUS;
+ lanai_check_status(lanai);
+ }
+ if (unlikely(reason & INT_DMASHUT)) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): driver error - DMA "
+ "shutdown, reason=0x%08X, address=0x%08X\n",
+ lanai->number, (unsigned int) (reason & INT_DMASHUT),
+ (unsigned int) reg_read(lanai, DMA_Addr_Reg));
+ if (reason & INT_TABORTBM) {
+ lanai_reset(lanai);
+ return;
+ }
+ ack |= (reason & INT_DMASHUT);
+ printk(KERN_ERR DEV_LABEL "(itf %d): re-enabling DMA\n",
+ lanai->number);
+ conf1_write(lanai);
+ lanai->stats.dma_reenable++;
+ pcistatus_check(lanai, 0);
+ }
+ if (unlikely(reason & INT_TABORTSENT)) {
+ ack |= (reason & INT_TABORTSENT);
+ printk(KERN_ERR DEV_LABEL "(itf %d): sent PCI target abort\n",
+ lanai->number);
+ pcistatus_check(lanai, 0);
+ }
+ if (unlikely(reason & INT_SEGSHUT)) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): driver error - "
+ "segmentation shutdown, reason=0x%08X\n", lanai->number,
+ (unsigned int) (reason & INT_SEGSHUT));
+ lanai_reset(lanai);
+ return;
+ }
+ if (unlikely(reason & (INT_PING | INT_WAKE))) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): driver error - "
+ "unexpected interrupt 0x%08X, resetting\n",
+ lanai->number,
+ (unsigned int) (reason & (INT_PING | INT_WAKE)));
+ lanai_reset(lanai);
+ return;
+ }
+#ifdef DEBUG
+ if (unlikely(ack != reason)) {
+ DPRINTK("unacked ints: 0x%08X\n",
+ (unsigned int) (reason & ~ack));
+ ack = reason;
+ }
+#endif
+ done:
+ if (ack != 0)
+ reg_write(lanai, ack, IntAck_Reg);
+}
+
+static irqreturn_t lanai_int(int irq, void *devid)
+{
+ struct lanai_dev *lanai = devid;
+ u32 reason;
+
+#ifdef USE_POWERDOWN
+ /*
+ * If we're powered down we shouldn't be generating any interrupts -
+ * so assume that this is a shared interrupt line and it's for someone
+ * else
+ */
+ if (unlikely(lanai->conf1 & CONFIG1_POWERDOWN))
+ return IRQ_NONE;
+#endif
+
+ reason = intr_pending(lanai);
+ if (reason == 0)
+ return IRQ_NONE; /* Must be for someone else */
+
+ do {
+ if (unlikely(reason == 0xFFFFFFFF))
+ break; /* Maybe we've been unplugged? */
+ lanai_int_1(lanai, reason);
+ reason = intr_pending(lanai);
+ } while (reason != 0);
+
+ return IRQ_HANDLED;
+}
+
+/* TODO - it would be nice if we could use the "delayed interrupt" system
+ * to some advantage
+ */
+
+/* -------------------- CHECK BOARD ID/REV: */
+
+/*
+ * The board id and revision are stored both in the reset register and
+ * in the PCI configuration space - the documentation says to check
+ * each of them. If revp!=NULL we store the revision there
+ */
+static int check_board_id_and_rev(const char *name, u32 val, int *revp)
+{
+ DPRINTK("%s says board_id=%d, board_rev=%d\n", name,
+ (int) RESET_GET_BOARD_ID(val),
+ (int) RESET_GET_BOARD_REV(val));
+ if (RESET_GET_BOARD_ID(val) != BOARD_ID_LANAI256) {
+ printk(KERN_ERR DEV_LABEL ": Found %s board-id %d -- not a "
+ "Lanai 25.6\n", name, (int) RESET_GET_BOARD_ID(val));
+ return -ENODEV;
+ }
+ if (revp != NULL)
+ *revp = RESET_GET_BOARD_REV(val);
+ return 0;
+}
+
+/* -------------------- PCI INITIALIZATION/SHUTDOWN: */
+
+static int lanai_pci_start(struct lanai_dev *lanai)
+{
+ struct pci_dev *pci = lanai->pci;
+ int result;
+
+ if (pci_enable_device(pci) != 0) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't enable "
+ "PCI device", lanai->number);
+ return -ENXIO;
+ }
+ pci_set_master(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)) != 0) {
+ printk(KERN_WARNING DEV_LABEL
+ "(itf %d): No suitable DMA available.\n", lanai->number);
+ return -EBUSY;
+ }
+ result = check_board_id_and_rev("PCI", pci->subsystem_device, NULL);
+ if (result != 0)
+ return result;
+ /* Set latency timer to zero as per lanai docs */
+ result = pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0);
+ if (result != PCIBIOS_SUCCESSFUL) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't write "
+ "PCI_LATENCY_TIMER: %d\n", lanai->number, result);
+ return -EINVAL;
+ }
+ pcistatus_check(lanai, 1);
+ pcistatus_check(lanai, 0);
+ return 0;
+}
+
+/* -------------------- VPI/VCI ALLOCATION: */
+
+/*
+ * We _can_ use VCI==0 for normal traffic, but only for UBR (or we'll
+ * get a CBRZERO interrupt), and we can use it only if no one is receiving
+ * AAL0 traffic (since they will use the same queue) - according to the
+ * docs we shouldn't even use it for AAL0 traffic
+ */
+static inline int vci0_is_ok(struct lanai_dev *lanai,
+ const struct atm_qos *qos)
+{
+ if (qos->txtp.traffic_class == ATM_CBR || qos->aal == ATM_AAL0)
+ return 0;
+ if (qos->rxtp.traffic_class != ATM_NONE) {
+ if (lanai->naal0 != 0)
+ return 0;
+ lanai->conf2 |= CONFIG2_VCI0_NORMAL;
+ conf2_write_if_powerup(lanai);
+ }
+ return 1;
+}
+
+/* return true if vci is currently unused, or if requested qos is
+ * compatible
+ */
+static int vci_is_ok(struct lanai_dev *lanai, vci_t vci,
+ const struct atm_vcc *atmvcc)
+{
+ const struct atm_qos *qos = &atmvcc->qos;
+ const struct lanai_vcc *lvcc = lanai->vccs[vci];
+ if (vci == 0 && !vci0_is_ok(lanai, qos))
+ return 0;
+ if (unlikely(lvcc != NULL)) {
+ if (qos->rxtp.traffic_class != ATM_NONE &&
+ lvcc->rx.atmvcc != NULL && lvcc->rx.atmvcc != atmvcc)
+ return 0;
+ if (qos->txtp.traffic_class != ATM_NONE &&
+ lvcc->tx.atmvcc != NULL && lvcc->tx.atmvcc != atmvcc)
+ return 0;
+ if (qos->txtp.traffic_class == ATM_CBR &&
+ lanai->cbrvcc != NULL && lanai->cbrvcc != atmvcc)
+ return 0;
+ }
+ if (qos->aal == ATM_AAL0 && lanai->naal0 == 0 &&
+ qos->rxtp.traffic_class != ATM_NONE) {
+ const struct lanai_vcc *vci0 = lanai->vccs[0];
+ if (vci0 != NULL && vci0->rx.atmvcc != NULL)
+ return 0;
+ lanai->conf2 &= ~CONFIG2_VCI0_NORMAL;
+ conf2_write_if_powerup(lanai);
+ }
+ return 1;
+}
+
+static int lanai_normalize_ci(struct lanai_dev *lanai,
+ const struct atm_vcc *atmvcc, short *vpip, vci_t *vcip)
+{
+ switch (*vpip) {
+ case ATM_VPI_ANY:
+ *vpip = 0;
+ /* FALLTHROUGH */
+ case 0:
+ break;
+ default:
+ return -EADDRINUSE;
+ }
+ switch (*vcip) {
+ case ATM_VCI_ANY:
+ for (*vcip = ATM_NOT_RSV_VCI; *vcip < lanai->num_vci;
+ (*vcip)++)
+ if (vci_is_ok(lanai, *vcip, atmvcc))
+ return 0;
+ return -EADDRINUSE;
+ default:
+ if (*vcip >= lanai->num_vci || *vcip < 0 ||
+ !vci_is_ok(lanai, *vcip, atmvcc))
+ return -EADDRINUSE;
+ }
+ return 0;
+}
+
+/* -------------------- MANAGE CBR: */
+
+/*
+ * CBR ICG is stored as a fixed-point number with 4 fractional bits.
+ * Note that storing a number greater than 2046.0 will result in
+ * incorrect shaping
+ */
+#define CBRICG_FRAC_BITS (4)
+#define CBRICG_MAX (2046 << CBRICG_FRAC_BITS)
+
+/*
+ * ICG is related to PCR with the formula PCR = MAXPCR / (ICG + 1)
+ * where MAXPCR is (according to the docs) 25600000/(54*8),
+ * which is equal to (3125<<9)/27.
+ *
+ * Solving for ICG, we get:
+ * ICG = MAXPCR/PCR - 1
+ * ICG = (3125<<9)/(27*PCR) - 1
+ * ICG = ((3125<<9) - (27*PCR)) / (27*PCR)
+ *
+ * The end result is supposed to be a fixed-point number with FRAC_BITS
+ * bits of a fractional part, so we keep everything in the numerator
+ * shifted by that much as we compute
+ *
+ */
+static int pcr_to_cbricg(const struct atm_qos *qos)
+{
+ int rounddown = 0; /* 1 = Round PCR down, i.e. round ICG _up_ */
+ int x, icg, pcr = atm_pcr_goal(&qos->txtp);
+ if (pcr == 0) /* Use maximum bandwidth */
+ return 0;
+ if (pcr < 0) {
+ rounddown = 1;
+ pcr = -pcr;
+ }
+ x = pcr * 27;
+ icg = (3125 << (9 + CBRICG_FRAC_BITS)) - (x << CBRICG_FRAC_BITS);
+ if (rounddown)
+ icg += x - 1;
+ icg /= x;
+ if (icg > CBRICG_MAX)
+ icg = CBRICG_MAX;
+ DPRINTK("pcr_to_cbricg: pcr=%d rounddown=%c icg=%d\n",
+ pcr, rounddown ? 'Y' : 'N', icg);
+ return icg;
+}
+
+static inline void lanai_cbr_setup(struct lanai_dev *lanai)
+{
+ reg_write(lanai, pcr_to_cbricg(&lanai->cbrvcc->qos), CBR_ICG_Reg);
+ reg_write(lanai, lanai->cbrvcc->vci, CBR_PTR_Reg);
+ lanai->conf2 |= CONFIG2_CBR_ENABLE;
+ conf2_write(lanai);
+}
+
+static inline void lanai_cbr_shutdown(struct lanai_dev *lanai)
+{
+ lanai->conf2 &= ~CONFIG2_CBR_ENABLE;
+ conf2_write(lanai);
+}
+
+/* -------------------- OPERATIONS: */
+
+/* setup a newly detected device */
+static int lanai_dev_open(struct atm_dev *atmdev)
+{
+ struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data;
+ unsigned long raw_base;
+ int result;
+
+ DPRINTK("In lanai_dev_open()\n");
+ /* Basic device fields */
+ lanai->number = atmdev->number;
+ lanai->num_vci = NUM_VCI;
+ bitmap_zero(lanai->backlog_vccs, NUM_VCI);
+ bitmap_zero(lanai->transmit_ready, NUM_VCI);
+ lanai->naal0 = 0;
+#ifdef USE_POWERDOWN
+ lanai->nbound = 0;
+#endif
+ lanai->cbrvcc = NULL;
+ memset(&lanai->stats, 0, sizeof lanai->stats);
+ spin_lock_init(&lanai->endtxlock);
+ spin_lock_init(&lanai->servicelock);
+ atmdev->ci_range.vpi_bits = 0;
+ atmdev->ci_range.vci_bits = 0;
+ while (1 << atmdev->ci_range.vci_bits < lanai->num_vci)
+ atmdev->ci_range.vci_bits++;
+ atmdev->link_rate = ATM_25_PCR;
+
+ /* 3.2: PCI initialization */
+ if ((result = lanai_pci_start(lanai)) != 0)
+ goto error;
+ raw_base = lanai->pci->resource[0].start;
+ lanai->base = (bus_addr_t) ioremap(raw_base, LANAI_MAPPING_SIZE);
+ if (lanai->base == NULL) {
+ printk(KERN_ERR DEV_LABEL ": couldn't remap I/O space\n");
+ goto error_pci;
+ }
+ /* 3.3: Reset lanai and PHY */
+ reset_board(lanai);
+ lanai->conf1 = reg_read(lanai, Config1_Reg);
+ lanai->conf1 &= ~(CONFIG1_GPOUT1 | CONFIG1_POWERDOWN |
+ CONFIG1_MASK_LEDMODE);
+ lanai->conf1 |= CONFIG1_SET_LEDMODE(LEDMODE_NOT_SOOL);
+ reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg);
+ udelay(1000);
+ conf1_write(lanai);
+
+ /*
+ * 3.4: Turn on endian mode for big-endian hardware
+ * We don't actually want to do this - the actual bit fields
+ * in the endian register are not documented anywhere.
+ * Instead we do the bit-flipping ourselves on big-endian
+ * hardware.
+ *
+ * 3.5: get the board ID/rev by reading the reset register
+ */
+ result = check_board_id_and_rev("register",
+ reg_read(lanai, Reset_Reg), &lanai->board_rev);
+ if (result != 0)
+ goto error_unmap;
+
+ /* 3.6: read EEPROM */
+ if ((result = eeprom_read(lanai)) != 0)
+ goto error_unmap;
+ if ((result = eeprom_validate(lanai)) != 0)
+ goto error_unmap;
+
+ /* 3.7: re-reset PHY, do loopback tests, setup PHY */
+ reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg);
+ udelay(1000);
+ conf1_write(lanai);
+ /* TODO - loopback tests */
+ lanai->conf1 |= (CONFIG1_GPOUT2 | CONFIG1_GPOUT3 | CONFIG1_DMA_ENABLE);
+ conf1_write(lanai);
+
+ /* 3.8/3.9: test and initialize card SRAM */
+ if ((result = sram_test_and_clear(lanai)) != 0)
+ goto error_unmap;
+
+ /* 3.10: initialize lanai registers */
+ lanai->conf1 |= CONFIG1_DMA_ENABLE;
+ conf1_write(lanai);
+ if ((result = service_buffer_allocate(lanai)) != 0)
+ goto error_unmap;
+ if ((result = vcc_table_allocate(lanai)) != 0)
+ goto error_service;
+ lanai->conf2 = (lanai->num_vci >= 512 ? CONFIG2_HOWMANY : 0) |
+ CONFIG2_HEC_DROP | /* ??? */ CONFIG2_PTI7_MODE;
+ conf2_write(lanai);
+ reg_write(lanai, TX_FIFO_DEPTH, TxDepth_Reg);
+ reg_write(lanai, 0, CBR_ICG_Reg); /* CBR defaults to no limit */
+ if ((result = request_irq(lanai->pci->irq, lanai_int, IRQF_SHARED,
+ DEV_LABEL, lanai)) != 0) {
+ printk(KERN_ERR DEV_LABEL ": can't allocate interrupt\n");
+ goto error_vcctable;
+ }
+ mb(); /* Make sure that all that made it */
+ intr_enable(lanai, INT_ALL & ~(INT_PING | INT_WAKE));
+ /* 3.11: initialize loop mode (i.e. turn looping off) */
+ lanai->conf1 = (lanai->conf1 & ~CONFIG1_MASK_LOOPMODE) |
+ CONFIG1_SET_LOOPMODE(LOOPMODE_NORMAL) |
+ CONFIG1_GPOUT2 | CONFIG1_GPOUT3;
+ conf1_write(lanai);
+ lanai->status = reg_read(lanai, Status_Reg);
+ /* We're now done initializing this card */
+#ifdef USE_POWERDOWN
+ lanai->conf1 |= CONFIG1_POWERDOWN;
+ conf1_write(lanai);
+#endif
+ memcpy(atmdev->esi, eeprom_mac(lanai), ESI_LEN);
+ lanai_timed_poll_start(lanai);
+ printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d, base=0x%lx, irq=%u "
+ "(%pMF)\n", lanai->number, (int) lanai->pci->revision,
+ (unsigned long) lanai->base, lanai->pci->irq, atmdev->esi);
+ printk(KERN_NOTICE DEV_LABEL "(itf %d): LANAI%s, serialno=%u(0x%X), "
+ "board_rev=%d\n", lanai->number,
+ lanai->type==lanai2 ? "2" : "HB", (unsigned int) lanai->serialno,
+ (unsigned int) lanai->serialno, lanai->board_rev);
+ return 0;
+
+ error_vcctable:
+ vcc_table_deallocate(lanai);
+ error_service:
+ service_buffer_deallocate(lanai);
+ error_unmap:
+ reset_board(lanai);
+#ifdef USE_POWERDOWN
+ lanai->conf1 = reg_read(lanai, Config1_Reg) | CONFIG1_POWERDOWN;
+ conf1_write(lanai);
+#endif
+ iounmap(lanai->base);
+ error_pci:
+ pci_disable_device(lanai->pci);
+ error:
+ return result;
+}
+
+/* called when device is being shutdown, and all vcc's are gone - higher
+ * levels will deallocate the atm device for us
+ */
+static void lanai_dev_close(struct atm_dev *atmdev)
+{
+ struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data;
+ printk(KERN_INFO DEV_LABEL "(itf %d): shutting down interface\n",
+ lanai->number);
+ lanai_timed_poll_stop(lanai);
+#ifdef USE_POWERDOWN
+ lanai->conf1 = reg_read(lanai, Config1_Reg) & ~CONFIG1_POWERDOWN;
+ conf1_write(lanai);
+#endif
+ intr_disable(lanai, INT_ALL);
+ free_irq(lanai->pci->irq, lanai);
+ reset_board(lanai);
+#ifdef USE_POWERDOWN
+ lanai->conf1 |= CONFIG1_POWERDOWN;
+ conf1_write(lanai);
+#endif
+ pci_disable_device(lanai->pci);
+ vcc_table_deallocate(lanai);
+ service_buffer_deallocate(lanai);
+ iounmap(lanai->base);
+ kfree(lanai);
+}
+
+/* close a vcc */
+static void lanai_close(struct atm_vcc *atmvcc)
+{
+ struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data;
+ struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data;
+ if (lvcc == NULL)
+ return;
+ clear_bit(ATM_VF_READY, &atmvcc->flags);
+ clear_bit(ATM_VF_PARTIAL, &atmvcc->flags);
+ if (lvcc->rx.atmvcc == atmvcc) {
+ lanai_shutdown_rx_vci(lvcc);
+ if (atmvcc->qos.aal == ATM_AAL0) {
+ if (--lanai->naal0 <= 0)
+ aal0_buffer_free(lanai);
+ } else
+ lanai_buf_deallocate(&lvcc->rx.buf, lanai->pci);
+ lvcc->rx.atmvcc = NULL;
+ }
+ if (lvcc->tx.atmvcc == atmvcc) {
+ if (atmvcc == lanai->cbrvcc) {
+ if (lvcc->vbase != NULL)
+ lanai_cbr_shutdown(lanai);
+ lanai->cbrvcc = NULL;
+ }
+ lanai_shutdown_tx_vci(lanai, lvcc);
+ lanai_buf_deallocate(&lvcc->tx.buf, lanai->pci);
+ lvcc->tx.atmvcc = NULL;
+ }
+ if (--lvcc->nref == 0) {
+ host_vcc_unbind(lanai, lvcc);
+ kfree(lvcc);
+ }
+ atmvcc->dev_data = NULL;
+ clear_bit(ATM_VF_ADDR, &atmvcc->flags);
+}
+
+/* open a vcc on the card to vpi/vci */
+static int lanai_open(struct atm_vcc *atmvcc)
+{
+ struct lanai_dev *lanai;
+ struct lanai_vcc *lvcc;
+ int result = 0;
+ int vci = atmvcc->vci;
+ short vpi = atmvcc->vpi;
+ /* we don't support partial open - it's not really useful anyway */
+ if ((test_bit(ATM_VF_PARTIAL, &atmvcc->flags)) ||
+ (vpi == ATM_VPI_UNSPEC) || (vci == ATM_VCI_UNSPEC))
+ return -EINVAL;
+ lanai = (struct lanai_dev *) atmvcc->dev->dev_data;
+ result = lanai_normalize_ci(lanai, atmvcc, &vpi, &vci);
+ if (unlikely(result != 0))
+ goto out;
+ set_bit(ATM_VF_ADDR, &atmvcc->flags);
+ if (atmvcc->qos.aal != ATM_AAL0 && atmvcc->qos.aal != ATM_AAL5)
+ return -EINVAL;
+ DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n", lanai->number,
+ (int) vpi, vci);
+ lvcc = lanai->vccs[vci];
+ if (lvcc == NULL) {
+ lvcc = new_lanai_vcc();
+ if (unlikely(lvcc == NULL))
+ return -ENOMEM;
+ atmvcc->dev_data = lvcc;
+ }
+ lvcc->nref++;
+ if (atmvcc->qos.rxtp.traffic_class != ATM_NONE) {
+ APRINTK(lvcc->rx.atmvcc == NULL, "rx.atmvcc!=NULL, vci=%d\n",
+ vci);
+ if (atmvcc->qos.aal == ATM_AAL0) {
+ if (lanai->naal0 == 0)
+ result = aal0_buffer_allocate(lanai);
+ } else
+ result = lanai_setup_rx_vci_aal5(
+ lanai, lvcc, &atmvcc->qos);
+ if (unlikely(result != 0))
+ goto out_free;
+ lvcc->rx.atmvcc = atmvcc;
+ lvcc->stats.rx_nomem = 0;
+ lvcc->stats.x.aal5.rx_badlen = 0;
+ lvcc->stats.x.aal5.service_trash = 0;
+ lvcc->stats.x.aal5.service_stream = 0;
+ lvcc->stats.x.aal5.service_rxcrc = 0;
+ if (atmvcc->qos.aal == ATM_AAL0)
+ lanai->naal0++;
+ }
+ if (atmvcc->qos.txtp.traffic_class != ATM_NONE) {
+ APRINTK(lvcc->tx.atmvcc == NULL, "tx.atmvcc!=NULL, vci=%d\n",
+ vci);
+ result = lanai_setup_tx_vci(lanai, lvcc, &atmvcc->qos);
+ if (unlikely(result != 0))
+ goto out_free;
+ lvcc->tx.atmvcc = atmvcc;
+ if (atmvcc->qos.txtp.traffic_class == ATM_CBR) {
+ APRINTK(lanai->cbrvcc == NULL,
+ "cbrvcc!=NULL, vci=%d\n", vci);
+ lanai->cbrvcc = atmvcc;
+ }
+ }
+ host_vcc_bind(lanai, lvcc, vci);
+ /*
+ * Make sure everything made it to RAM before we tell the card about
+ * the VCC
+ */
+ wmb();
+ if (atmvcc == lvcc->rx.atmvcc)
+ host_vcc_start_rx(lvcc);
+ if (atmvcc == lvcc->tx.atmvcc) {
+ host_vcc_start_tx(lvcc);
+ if (lanai->cbrvcc == atmvcc)
+ lanai_cbr_setup(lanai);
+ }
+ set_bit(ATM_VF_READY, &atmvcc->flags);
+ return 0;
+ out_free:
+ lanai_close(atmvcc);
+ out:
+ return result;
+}
+
+static int lanai_send(struct atm_vcc *atmvcc, struct sk_buff *skb)
+{
+ struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data;
+ struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data;
+ unsigned long flags;
+ if (unlikely(lvcc == NULL || lvcc->vbase == NULL ||
+ lvcc->tx.atmvcc != atmvcc))
+ goto einval;
+#ifdef DEBUG
+ if (unlikely(skb == NULL)) {
+ DPRINTK("lanai_send: skb==NULL for vci=%d\n", atmvcc->vci);
+ goto einval;
+ }
+ if (unlikely(lanai == NULL)) {
+ DPRINTK("lanai_send: lanai==NULL for vci=%d\n", atmvcc->vci);
+ goto einval;
+ }
+#endif
+ ATM_SKB(skb)->vcc = atmvcc;
+ switch (atmvcc->qos.aal) {
+ case ATM_AAL5:
+ read_lock_irqsave(&vcc_sklist_lock, flags);
+ vcc_tx_aal5(lanai, lvcc, skb);
+ read_unlock_irqrestore(&vcc_sklist_lock, flags);
+ return 0;
+ case ATM_AAL0:
+ if (unlikely(skb->len != ATM_CELL_SIZE-1))
+ goto einval;
+ /* NOTE - this next line is technically invalid - we haven't unshared skb */
+ cpu_to_be32s((u32 *) skb->data);
+ read_lock_irqsave(&vcc_sklist_lock, flags);
+ vcc_tx_aal0(lanai, lvcc, skb);
+ read_unlock_irqrestore(&vcc_sklist_lock, flags);
+ return 0;
+ }
+ DPRINTK("lanai_send: bad aal=%d on vci=%d\n", (int) atmvcc->qos.aal,
+ atmvcc->vci);
+ einval:
+ lanai_free_skb(atmvcc, skb);
+ return -EINVAL;
+}
+
+static int lanai_change_qos(struct atm_vcc *atmvcc,
+ /*const*/ struct atm_qos *qos, int flags)
+{
+ return -EBUSY; /* TODO: need to write this */
+}
+
+#ifndef CONFIG_PROC_FS
+#define lanai_proc_read NULL
+#else
+static int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page)
+{
+ struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data;
+ loff_t left = *pos;
+ struct lanai_vcc *lvcc;
+ if (left-- == 0)
+ return sprintf(page, DEV_LABEL "(itf %d): chip=LANAI%s, "
+ "serial=%u, magic=0x%08X, num_vci=%d\n",
+ atmdev->number, lanai->type==lanai2 ? "2" : "HB",
+ (unsigned int) lanai->serialno,
+ (unsigned int) lanai->magicno, lanai->num_vci);
+ if (left-- == 0)
+ return sprintf(page, "revision: board=%d, pci_if=%d\n",
+ lanai->board_rev, (int) lanai->pci->revision);
+ if (left-- == 0)
+ return sprintf(page, "EEPROM ESI: %pM\n",
+ &lanai->eeprom[EEPROM_MAC]);
+ if (left-- == 0)
+ return sprintf(page, "status: SOOL=%d, LOCD=%d, LED=%d, "
+ "GPIN=%d\n", (lanai->status & STATUS_SOOL) ? 1 : 0,
+ (lanai->status & STATUS_LOCD) ? 1 : 0,
+ (lanai->status & STATUS_LED) ? 1 : 0,
+ (lanai->status & STATUS_GPIN) ? 1 : 0);
+ if (left-- == 0)
+ return sprintf(page, "global buffer sizes: service=%Zu, "
+ "aal0_rx=%Zu\n", lanai_buf_size(&lanai->service),
+ lanai->naal0 ? lanai_buf_size(&lanai->aal0buf) : 0);
+ if (left-- == 0) {
+ get_statistics(lanai);
+ return sprintf(page, "cells in error: overflow=%u, "
+ "closed_vci=%u, bad_HEC=%u, rx_fifo=%u\n",
+ lanai->stats.ovfl_trash, lanai->stats.vci_trash,
+ lanai->stats.hec_err, lanai->stats.atm_ovfl);
+ }
+ if (left-- == 0)
+ return sprintf(page, "PCI errors: parity_detect=%u, "
+ "master_abort=%u, master_target_abort=%u,\n",
+ lanai->stats.pcierr_parity_detect,
+ lanai->stats.pcierr_serr_set,
+ lanai->stats.pcierr_m_target_abort);
+ if (left-- == 0)
+ return sprintf(page, " slave_target_abort=%u, "
+ "master_parity=%u\n", lanai->stats.pcierr_s_target_abort,
+ lanai->stats.pcierr_master_parity);
+ if (left-- == 0)
+ return sprintf(page, " no_tx=%u, "
+ "no_rx=%u, bad_rx_aal=%u\n", lanai->stats.service_norx,
+ lanai->stats.service_notx,
+ lanai->stats.service_rxnotaal5);
+ if (left-- == 0)
+ return sprintf(page, "resets: dma=%u, card=%u\n",
+ lanai->stats.dma_reenable, lanai->stats.card_reset);
+ /* At this point, "left" should be the VCI we're looking for */
+ read_lock(&vcc_sklist_lock);
+ for (; ; left++) {
+ if (left >= NUM_VCI) {
+ left = 0;
+ goto out;
+ }
+ if ((lvcc = lanai->vccs[left]) != NULL)
+ break;
+ (*pos)++;
+ }
+ /* Note that we re-use "left" here since we're done with it */
+ left = sprintf(page, "VCI %4d: nref=%d, rx_nomem=%u", (vci_t) left,
+ lvcc->nref, lvcc->stats.rx_nomem);
+ if (lvcc->rx.atmvcc != NULL) {
+ left += sprintf(&page[left], ",\n rx_AAL=%d",
+ lvcc->rx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0);
+ if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5)
+ left += sprintf(&page[left], ", rx_buf_size=%Zu, "
+ "rx_bad_len=%u,\n rx_service_trash=%u, "
+ "rx_service_stream=%u, rx_bad_crc=%u",
+ lanai_buf_size(&lvcc->rx.buf),
+ lvcc->stats.x.aal5.rx_badlen,
+ lvcc->stats.x.aal5.service_trash,
+ lvcc->stats.x.aal5.service_stream,
+ lvcc->stats.x.aal5.service_rxcrc);
+ }
+ if (lvcc->tx.atmvcc != NULL)
+ left += sprintf(&page[left], ",\n tx_AAL=%d, "
+ "tx_buf_size=%Zu, tx_qos=%cBR, tx_backlogged=%c",
+ lvcc->tx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0,
+ lanai_buf_size(&lvcc->tx.buf),
+ lvcc->tx.atmvcc == lanai->cbrvcc ? 'C' : 'U',
+ vcc_is_backlogged(lvcc) ? 'Y' : 'N');
+ page[left++] = '\n';
+ page[left] = '\0';
+ out:
+ read_unlock(&vcc_sklist_lock);
+ return left;
+}
+#endif /* CONFIG_PROC_FS */
+
+/* -------------------- HOOKS: */
+
+static const struct atmdev_ops ops = {
+ .dev_close = lanai_dev_close,
+ .open = lanai_open,
+ .close = lanai_close,
+ .getsockopt = NULL,
+ .setsockopt = NULL,
+ .send = lanai_send,
+ .phy_put = NULL,
+ .phy_get = NULL,
+ .change_qos = lanai_change_qos,
+ .proc_read = lanai_proc_read,
+ .owner = THIS_MODULE
+};
+
+/* initialize one probed card */
+static int lanai_init_one(struct pci_dev *pci,
+ const struct pci_device_id *ident)
+{
+ struct lanai_dev *lanai;
+ struct atm_dev *atmdev;
+ int result;
+
+ lanai = kmalloc(sizeof(*lanai), GFP_KERNEL);
+ if (lanai == NULL) {
+ printk(KERN_ERR DEV_LABEL
+ ": couldn't allocate dev_data structure!\n");
+ return -ENOMEM;
+ }
+
+ atmdev = atm_dev_register(DEV_LABEL, &pci->dev, &ops, -1, NULL);
+ if (atmdev == NULL) {
+ printk(KERN_ERR DEV_LABEL
+ ": couldn't register atm device!\n");
+ kfree(lanai);
+ return -EBUSY;
+ }
+
+ atmdev->dev_data = lanai;
+ lanai->pci = pci;
+ lanai->type = (enum lanai_type) ident->device;
+
+ result = lanai_dev_open(atmdev);
+ if (result != 0) {
+ DPRINTK("lanai_start() failed, err=%d\n", -result);
+ atm_dev_deregister(atmdev);
+ kfree(lanai);
+ }
+ return result;
+}
+
+static struct pci_device_id lanai_pci_tbl[] = {
+ { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAI2) },
+ { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAIHB) },
+ { 0, } /* terminal entry */
+};
+MODULE_DEVICE_TABLE(pci, lanai_pci_tbl);
+
+static struct pci_driver lanai_driver = {
+ .name = DEV_LABEL,
+ .id_table = lanai_pci_tbl,
+ .probe = lanai_init_one,
+};
+
+module_pci_driver(lanai_driver);
+
+MODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>");
+MODULE_DESCRIPTION("Efficient Networks Speedstream 3010 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/atm/midway.h b/drivers/atm/midway.h
new file mode 100644
index 000000000..432525ad5
--- /dev/null
+++ b/drivers/atm/midway.h
@@ -0,0 +1,265 @@
+/* drivers/atm/midway.h - Efficient Networks Midway (SAR) description */
+
+/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#ifndef DRIVERS_ATM_MIDWAY_H
+#define DRIVERS_ATM_MIDWAY_H
+
+
+#define NR_VCI 1024 /* number of VCIs */
+#define NR_VCI_LD 10 /* log2(NR_VCI) */
+#define NR_DMA_RX 512 /* RX DMA queue entries */
+#define NR_DMA_TX 512 /* TX DMA queue entries */
+#define NR_SERVICE NR_VCI /* service list size */
+#define NR_CHAN 8 /* number of TX channels */
+#define TS_CLOCK 25000000 /* traffic shaper clock (cell/sec) */
+
+#define MAP_MAX_SIZE 0x00400000 /* memory window for max config */
+#define EPROM_SIZE 0x00010000
+#define MEM_VALID 0xffc00000 /* mask base address with this */
+#define PHY_BASE 0x00020000 /* offset of PHY register are */
+#define REG_BASE 0x00040000 /* offset of Midway register area */
+#define RAM_BASE 0x00200000 /* offset of RAM area */
+#define RAM_INCREMENT 0x00020000 /* probe for RAM every 128kB */
+
+#define MID_VCI_BASE RAM_BASE
+#define MID_DMA_RX_BASE (MID_VCI_BASE+NR_VCI*16)
+#define MID_DMA_TX_BASE (MID_DMA_RX_BASE+NR_DMA_RX*8)
+#define MID_SERVICE_BASE (MID_DMA_TX_BASE+NR_DMA_TX*8)
+#define MID_FREE_BASE (MID_SERVICE_BASE+NR_SERVICE*4)
+
+#define MAC_LEN 6 /* atm.h */
+
+#define MID_MIN_BUF_SIZE (1024) /* 1 kB is minimum */
+#define MID_MAX_BUF_SIZE (128*1024) /* 128 kB is maximum */
+
+#define RX_DESCR_SIZE 1 /* RX PDU descr is 1 longword */
+#define TX_DESCR_SIZE 2 /* TX PDU descr is 2 longwords */
+#define AAL5_TRAILER (ATM_AAL5_TRAILER/4) /* AAL5 trailer is 2 longwords */
+
+#define TX_GAP 8 /* TX buffer gap (words) */
+
+/*
+ * Midway Reset/ID
+ *
+ * All values read-only. Writing to this register resets Midway chip.
+ */
+
+#define MID_RES_ID_MCON 0x00 /* Midway Reset/ID */
+
+#define MID_ID 0xf0000000 /* Midway version */
+#define MID_SHIFT 24
+#define MID_MOTHER_ID 0x00000700 /* mother board id */
+#define MID_MOTHER_SHIFT 8
+#define MID_CON_TI 0x00000080 /* 0: normal ctrl; 1: SABRE */
+#define MID_CON_SUNI 0x00000040 /* 0: UTOPIA; 1: SUNI */
+#define MID_CON_V6 0x00000020 /* 0: non-pipel UTOPIA (required iff
+ !CON_SUNI; 1: UTOPIA */
+#define DAUGTHER_ID 0x0000001f /* daugther board id */
+
+/*
+ * Interrupt Status Acknowledge, Interrupt Status & Interrupt Enable
+ */
+
+#define MID_ISA 0x01 /* Interrupt Status Acknowledge */
+#define MID_IS 0x02 /* Interrupt Status */
+#define MID_IE 0x03 /* Interrupt Enable */
+
+#define MID_TX_COMPLETE_7 0x00010000 /* channel N completed a PDU */
+#define MID_TX_COMPLETE_6 0x00008000 /* transmission */
+#define MID_TX_COMPLETE_5 0x00004000
+#define MID_TX_COMPLETE_4 0x00002000
+#define MID_TX_COMPLETE_3 0x00001000
+#define MID_TX_COMPLETE_2 0x00000800
+#define MID_TX_COMPLETE_1 0x00000400
+#define MID_TX_COMPLETE_0 0x00000200
+#define MID_TX_COMPLETE 0x0001fe00 /* any TX */
+#define MID_TX_DMA_OVFL 0x00000100 /* DMA to adapter overflow */
+#define MID_TX_IDENT_MISM 0x00000080 /* TX: ident mismatch => halted */
+#define MID_DMA_LERR_ACK 0x00000040 /* LERR - SBus ? */
+#define MID_DMA_ERR_ACK 0x00000020 /* DMA error */
+#define MID_RX_DMA_COMPLETE 0x00000010 /* DMA to host done */
+#define MID_TX_DMA_COMPLETE 0x00000008 /* DMA from host done */
+#define MID_SERVICE 0x00000004 /* something in service list */
+#define MID_SUNI_INT 0x00000002 /* interrupt from SUNI */
+#define MID_STAT_OVFL 0x00000001 /* statistics overflow */
+
+/*
+ * Master Control/Status
+ */
+
+#define MID_MC_S 0x04
+
+#define MID_INT_SELECT 0x000001C0 /* Interrupt level (000: off) */
+#define MID_INT_SEL_SHIFT 6
+#define MID_TX_LOCK_MODE 0x00000020 /* 0: streaming; 1: TX ovfl->lock */
+#define MID_DMA_ENABLE 0x00000010 /* R: 0: disable; 1: enable
+ W: 0: no change; 1: enable */
+#define MID_TX_ENABLE 0x00000008 /* R: 0: TX disabled; 1: enabled
+ W: 0: no change; 1: enable */
+#define MID_RX_ENABLE 0x00000004 /* like TX */
+#define MID_WAIT_1MS 0x00000002 /* R: 0: timer not running; 1: running
+ W: 0: no change; 1: no interrupts
+ for 1 ms */
+#define MID_WAIT_500US 0x00000001 /* like WAIT_1MS, but 0.5 ms */
+
+/*
+ * Statistics
+ *
+ * Cleared when reading.
+ */
+
+#define MID_STAT 0x05
+
+#define MID_VCI_TRASH 0xFFFF0000 /* trashed cells because of VCI mode */
+#define MID_VCI_TRASH_SHIFT 16
+#define MID_OVFL_TRASH 0x0000FFFF /* trashed cells because of overflow */
+
+/*
+ * Address registers
+ */
+
+#define MID_SERV_WRITE 0x06 /* free pos in service area (R, 10 bits) */
+#define MID_DMA_ADDR 0x07 /* virtual DMA address (R, 32 bits) */
+#define MID_DMA_WR_RX 0x08 /* (RW, 9 bits) */
+#define MID_DMA_RD_RX 0x09
+#define MID_DMA_WR_TX 0x0A
+#define MID_DMA_RD_TX 0x0B
+
+/*
+ * Transmit Place Registers (0x10+4*channel)
+ */
+
+#define MID_TX_PLACE(c) (0x10+4*(c))
+
+#define MID_SIZE 0x00003800 /* size, N*256 x 32 bit */
+#define MID_SIZE_SHIFT 11
+#define MID_LOCATION 0x000007FF /* location in adapter memory (word) */
+
+#define MID_LOC_SKIP 8 /* 8 bits of location are always zero
+ (applies to all uses of location) */
+
+/*
+ * Transmit ReadPtr Registers (0x11+4*channel)
+ */
+
+#define MID_TX_RDPTR(c) (0x11+4*(c))
+
+#define MID_READ_PTR 0x00007FFF /* next word for PHY */
+
+/*
+ * Transmit DescrStart Registers (0x12+4*channel)
+ */
+
+#define MID_TX_DESCRSTART(c) (0x12+4*(c))
+
+#define MID_DESCR_START 0x00007FFF /* seg buffer being DMAed */
+
+#define ENI155_MAGIC 0xa54b872d
+
+struct midway_eprom {
+ unsigned char mac[MAC_LEN],inv_mac[MAC_LEN];
+ unsigned char pad[36];
+ u32 serial,inv_serial;
+ u32 magic,inv_magic;
+};
+
+
+/*
+ * VCI table entry
+ */
+
+#define MID_VCI_IN_SERVICE 0x00000001 /* set if VCI is currently in
+ service list */
+#define MID_VCI_SIZE 0x00038000 /* reassembly buffer size,
+ 2*<size> kB */
+#define MID_VCI_SIZE_SHIFT 15
+#define MID_VCI_LOCATION 0x1ffc0000 /* buffer location */
+#define MID_VCI_LOCATION_SHIFT 18
+#define MID_VCI_PTI_MODE 0x20000000 /* 0: trash, 1: preserve */
+#define MID_VCI_MODE 0xc0000000
+#define MID_VCI_MODE_SHIFT 30
+#define MID_VCI_READ 0x00007fff
+#define MID_VCI_READ_SHIFT 0
+#define MID_VCI_DESCR 0x7fff0000
+#define MID_VCI_DESCR_SHIFT 16
+#define MID_VCI_COUNT 0x000007ff
+#define MID_VCI_COUNT_SHIFT 0
+#define MID_VCI_STATE 0x0000c000
+#define MID_VCI_STATE_SHIFT 14
+#define MID_VCI_WRITE 0x7fff0000
+#define MID_VCI_WRITE_SHIFT 16
+
+#define MID_MODE_TRASH 0
+#define MID_MODE_RAW 1
+#define MID_MODE_AAL5 2
+
+/*
+ * Reassembly buffer descriptor
+ */
+
+#define MID_RED_COUNT 0x000007ff
+#define MID_RED_CRC_ERR 0x00000800
+#define MID_RED_T 0x00001000
+#define MID_RED_CE 0x00010000
+#define MID_RED_CLP 0x01000000
+#define MID_RED_IDEN 0xfe000000
+#define MID_RED_SHIFT 25
+
+#define MID_RED_RX_ID 0x1b /* constant identifier */
+
+/*
+ * Segmentation buffer descriptor
+ */
+
+#define MID_SEG_COUNT MID_RED_COUNT
+#define MID_SEG_RATE 0x01f80000
+#define MID_SEG_RATE_SHIFT 19
+#define MID_SEG_PR 0x06000000
+#define MID_SEG_PR_SHIFT 25
+#define MID_SEG_AAL5 0x08000000
+#define MID_SEG_ID 0xf0000000
+#define MID_SEG_ID_SHIFT 28
+#define MID_SEG_MAX_RATE 63
+
+#define MID_SEG_CLP 0x00000001
+#define MID_SEG_PTI 0x0000000e
+#define MID_SEG_PTI_SHIFT 1
+#define MID_SEG_VCI 0x00003ff0
+#define MID_SEG_VCI_SHIFT 4
+
+#define MID_SEG_TX_ID 0xb /* constant identifier */
+
+/*
+ * DMA entry
+ */
+
+#define MID_DMA_COUNT 0xffff0000
+#define MID_DMA_COUNT_SHIFT 16
+#define MID_DMA_END 0x00000020
+#define MID_DMA_TYPE 0x0000000f
+
+#define MID_DT_JK 0x3
+#define MID_DT_WORD 0x0
+#define MID_DT_2W 0x7
+#define MID_DT_4W 0x4
+#define MID_DT_8W 0x5
+#define MID_DT_16W 0x6
+#define MID_DT_2WM 0xf
+#define MID_DT_4WM 0xc
+#define MID_DT_8WM 0xd
+#define MID_DT_16WM 0xe
+
+/* only for RX*/
+#define MID_DMA_VCI 0x0000ffc0
+#define MID_DMA_VCI_SHIFT 6
+
+/* only for TX */
+#define MID_DMA_CHAN 0x000001c0
+#define MID_DMA_CHAN_SHIFT 6
+
+#define MID_DT_BYTE 0x1
+#define MID_DT_HWORD 0x2
+
+#endif
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
new file mode 100644
index 000000000..ddc4ceb85
--- /dev/null
+++ b/drivers/atm/nicstar.c
@@ -0,0 +1,2747 @@
+/*
+ * nicstar.c
+ *
+ * Device driver supporting CBR for IDT 77201/77211 "NICStAR" based cards.
+ *
+ * IMPORTANT: The included file nicstarmac.c was NOT WRITTEN BY ME.
+ * It was taken from the frle-0.22 device driver.
+ * As the file doesn't have a copyright notice, in the file
+ * nicstarmac.copyright I put the copyright notice from the
+ * frle-0.22 device driver.
+ * Some code is based on the nicstar driver by M. Welsh.
+ *
+ * Author: Rui Prior (rprior@inescn.pt)
+ * PowerPC support by Jay Talbott (jay_talbott@mcg.mot.com) April 1999
+ *
+ *
+ * (C) INESC 1999
+ */
+
+/*
+ * IMPORTANT INFORMATION
+ *
+ * There are currently three types of spinlocks:
+ *
+ * 1 - Per card interrupt spinlock (to protect structures and such)
+ * 2 - Per SCQ scq spinlock
+ * 3 - Per card resource spinlock (to access registers, etc.)
+ *
+ * These must NEVER be grabbed in reverse order.
+ *
+ */
+
+/* Header files */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/atmdev.h>
+#include <linux/atm.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/etherdevice.h>
+#include "nicstar.h"
+#ifdef CONFIG_ATM_NICSTAR_USE_SUNI
+#include "suni.h"
+#endif /* CONFIG_ATM_NICSTAR_USE_SUNI */
+#ifdef CONFIG_ATM_NICSTAR_USE_IDT77105
+#include "idt77105.h"
+#endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */
+
+/* Additional code */
+
+#include "nicstarmac.c"
+
+/* Configurable parameters */
+
+#undef PHY_LOOPBACK
+#undef TX_DEBUG
+#undef RX_DEBUG
+#undef GENERAL_DEBUG
+#undef EXTRA_DEBUG
+
+/* Do not touch these */
+
+#ifdef TX_DEBUG
+#define TXPRINTK(args...) printk(args)
+#else
+#define TXPRINTK(args...)
+#endif /* TX_DEBUG */
+
+#ifdef RX_DEBUG
+#define RXPRINTK(args...) printk(args)
+#else
+#define RXPRINTK(args...)
+#endif /* RX_DEBUG */
+
+#ifdef GENERAL_DEBUG
+#define PRINTK(args...) printk(args)
+#else
+#define PRINTK(args...)
+#endif /* GENERAL_DEBUG */
+
+#ifdef EXTRA_DEBUG
+#define XPRINTK(args...) printk(args)
+#else
+#define XPRINTK(args...)
+#endif /* EXTRA_DEBUG */
+
+/* Macros */
+
+#define CMD_BUSY(card) (readl((card)->membase + STAT) & NS_STAT_CMDBZ)
+
+#define NS_DELAY mdelay(1)
+
+#define PTR_DIFF(a, b) ((u32)((unsigned long)(a) - (unsigned long)(b)))
+
+#ifndef ATM_SKB
+#define ATM_SKB(s) (&(s)->atm)
+#endif
+
+#define scq_virt_to_bus(scq, p) \
+ (scq->dma + ((unsigned long)(p) - (unsigned long)(scq)->org))
+
+/* Function declarations */
+
+static u32 ns_read_sram(ns_dev * card, u32 sram_address);
+static void ns_write_sram(ns_dev * card, u32 sram_address, u32 * value,
+ int count);
+static int ns_init_card(int i, struct pci_dev *pcidev);
+static void ns_init_card_error(ns_dev * card, int error);
+static scq_info *get_scq(ns_dev *card, int size, u32 scd);
+static void free_scq(ns_dev *card, scq_info * scq, struct atm_vcc *vcc);
+static void push_rxbufs(ns_dev *, struct sk_buff *);
+static irqreturn_t ns_irq_handler(int irq, void *dev_id);
+static int ns_open(struct atm_vcc *vcc);
+static void ns_close(struct atm_vcc *vcc);
+static void fill_tst(ns_dev * card, int n, vc_map * vc);
+static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb);
+static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
+ struct sk_buff *skb);
+static void process_tsq(ns_dev * card);
+static void drain_scq(ns_dev * card, scq_info * scq, int pos);
+static void process_rsq(ns_dev * card);
+static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe);
+static void recycle_rx_buf(ns_dev * card, struct sk_buff *skb);
+static void recycle_iovec_rx_bufs(ns_dev * card, struct iovec *iov, int count);
+static void recycle_iov_buf(ns_dev * card, struct sk_buff *iovb);
+static void dequeue_sm_buf(ns_dev * card, struct sk_buff *sb);
+static void dequeue_lg_buf(ns_dev * card, struct sk_buff *lb);
+static int ns_proc_read(struct atm_dev *dev, loff_t * pos, char *page);
+static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
+#ifdef EXTRA_DEBUG
+static void which_list(ns_dev * card, struct sk_buff *skb);
+#endif
+static void ns_poll(unsigned long arg);
+static void ns_phy_put(struct atm_dev *dev, unsigned char value,
+ unsigned long addr);
+static unsigned char ns_phy_get(struct atm_dev *dev, unsigned long addr);
+
+/* Global variables */
+
+static struct ns_dev *cards[NS_MAX_CARDS];
+static unsigned num_cards;
+static struct atmdev_ops atm_ops = {
+ .open = ns_open,
+ .close = ns_close,
+ .ioctl = ns_ioctl,
+ .send = ns_send,
+ .phy_put = ns_phy_put,
+ .phy_get = ns_phy_get,
+ .proc_read = ns_proc_read,
+ .owner = THIS_MODULE,
+};
+
+static struct timer_list ns_timer;
+static char *mac[NS_MAX_CARDS];
+module_param_array(mac, charp, NULL, 0);
+MODULE_LICENSE("GPL");
+
+/* Functions */
+
+static int nicstar_init_one(struct pci_dev *pcidev,
+ const struct pci_device_id *ent)
+{
+ static int index = -1;
+ unsigned int error;
+
+ index++;
+ cards[index] = NULL;
+
+ error = ns_init_card(index, pcidev);
+ if (error) {
+ cards[index--] = NULL; /* don't increment index */
+ goto err_out;
+ }
+
+ return 0;
+err_out:
+ return -ENODEV;
+}
+
+static void nicstar_remove_one(struct pci_dev *pcidev)
+{
+ int i, j;
+ ns_dev *card = pci_get_drvdata(pcidev);
+ struct sk_buff *hb;
+ struct sk_buff *iovb;
+ struct sk_buff *lb;
+ struct sk_buff *sb;
+
+ i = card->index;
+
+ if (cards[i] == NULL)
+ return;
+
+ if (card->atmdev->phy && card->atmdev->phy->stop)
+ card->atmdev->phy->stop(card->atmdev);
+
+ /* Stop everything */
+ writel(0x00000000, card->membase + CFG);
+
+ /* De-register device */
+ atm_dev_deregister(card->atmdev);
+
+ /* Disable PCI device */
+ pci_disable_device(pcidev);
+
+ /* Free up resources */
+ j = 0;
+ PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count);
+ while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL) {
+ dev_kfree_skb_any(hb);
+ j++;
+ }
+ PRINTK("nicstar%d: %d huge buffers freed.\n", i, j);
+ j = 0;
+ PRINTK("nicstar%d: freeing %d iovec buffers.\n", i,
+ card->iovpool.count);
+ while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL) {
+ dev_kfree_skb_any(iovb);
+ j++;
+ }
+ PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j);
+ while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
+ dev_kfree_skb_any(lb);
+ while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
+ dev_kfree_skb_any(sb);
+ free_scq(card, card->scq0, NULL);
+ for (j = 0; j < NS_FRSCD_NUM; j++) {
+ if (card->scd2vc[j] != NULL)
+ free_scq(card, card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc);
+ }
+ idr_destroy(&card->idr);
+ dma_free_coherent(&card->pcidev->dev, NS_RSQSIZE + NS_RSQ_ALIGNMENT,
+ card->rsq.org, card->rsq.dma);
+ dma_free_coherent(&card->pcidev->dev, NS_TSQSIZE + NS_TSQ_ALIGNMENT,
+ card->tsq.org, card->tsq.dma);
+ free_irq(card->pcidev->irq, card);
+ iounmap(card->membase);
+ kfree(card);
+}
+
+static struct pci_device_id nicstar_pci_tbl[] = {
+ { PCI_VDEVICE(IDT, PCI_DEVICE_ID_IDT_IDT77201), 0 },
+ {0,} /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(pci, nicstar_pci_tbl);
+
+static struct pci_driver nicstar_driver = {
+ .name = "nicstar",
+ .id_table = nicstar_pci_tbl,
+ .probe = nicstar_init_one,
+ .remove = nicstar_remove_one,
+};
+
+static int __init nicstar_init(void)
+{
+ unsigned error = 0; /* Initialized to remove compile warning */
+
+ XPRINTK("nicstar: nicstar_init() called.\n");
+
+ error = pci_register_driver(&nicstar_driver);
+
+ TXPRINTK("nicstar: TX debug enabled.\n");
+ RXPRINTK("nicstar: RX debug enabled.\n");
+ PRINTK("nicstar: General debug enabled.\n");
+#ifdef PHY_LOOPBACK
+ printk("nicstar: using PHY loopback.\n");
+#endif /* PHY_LOOPBACK */
+ XPRINTK("nicstar: nicstar_init() returned.\n");
+
+ if (!error) {
+ init_timer(&ns_timer);
+ ns_timer.expires = jiffies + NS_POLL_PERIOD;
+ ns_timer.data = 0UL;
+ ns_timer.function = ns_poll;
+ add_timer(&ns_timer);
+ }
+
+ return error;
+}
+
+static void __exit nicstar_cleanup(void)
+{
+ XPRINTK("nicstar: nicstar_cleanup() called.\n");
+
+ del_timer(&ns_timer);
+
+ pci_unregister_driver(&nicstar_driver);
+
+ XPRINTK("nicstar: nicstar_cleanup() returned.\n");
+}
+
+static u32 ns_read_sram(ns_dev * card, u32 sram_address)
+{
+ unsigned long flags;
+ u32 data;
+ sram_address <<= 2;
+ sram_address &= 0x0007FFFC; /* address must be dword aligned */
+ sram_address |= 0x50000000; /* SRAM read command */
+ spin_lock_irqsave(&card->res_lock, flags);
+ while (CMD_BUSY(card)) ;
+ writel(sram_address, card->membase + CMD);
+ while (CMD_BUSY(card)) ;
+ data = readl(card->membase + DR0);
+ spin_unlock_irqrestore(&card->res_lock, flags);
+ return data;
+}
+
+static void ns_write_sram(ns_dev * card, u32 sram_address, u32 * value,
+ int count)
+{
+ unsigned long flags;
+ int i, c;
+ count--; /* count range now is 0..3 instead of 1..4 */
+ c = count;
+ c <<= 2; /* to use increments of 4 */
+ spin_lock_irqsave(&card->res_lock, flags);
+ while (CMD_BUSY(card)) ;
+ for (i = 0; i <= c; i += 4)
+ writel(*(value++), card->membase + i);
+ /* Note: DR# registers are the first 4 dwords in nicstar's memspace,
+ so card->membase + DR0 == card->membase */
+ sram_address <<= 2;
+ sram_address &= 0x0007FFFC;
+ sram_address |= (0x40000000 | count);
+ writel(sram_address, card->membase + CMD);
+ spin_unlock_irqrestore(&card->res_lock, flags);
+}
+
+static int ns_init_card(int i, struct pci_dev *pcidev)
+{
+ int j;
+ struct ns_dev *card = NULL;
+ unsigned char pci_latency;
+ unsigned error;
+ u32 data;
+ u32 u32d[4];
+ u32 ns_cfg_rctsize;
+ int bcount;
+ unsigned long membase;
+
+ error = 0;
+
+ if (pci_enable_device(pcidev)) {
+ printk("nicstar%d: can't enable PCI device\n", i);
+ error = 2;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ if (dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)) != 0) {
+ printk(KERN_WARNING
+ "nicstar%d: No suitable DMA available.\n", i);
+ error = 2;
+ ns_init_card_error(card, error);
+ return error;
+ }
+
+ if ((card = kmalloc(sizeof(ns_dev), GFP_KERNEL)) == NULL) {
+ printk
+ ("nicstar%d: can't allocate memory for device structure.\n",
+ i);
+ error = 2;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ cards[i] = card;
+ spin_lock_init(&card->int_lock);
+ spin_lock_init(&card->res_lock);
+
+ pci_set_drvdata(pcidev, card);
+
+ card->index = i;
+ card->atmdev = NULL;
+ card->pcidev = pcidev;
+ membase = pci_resource_start(pcidev, 1);
+ card->membase = ioremap(membase, NS_IOREMAP_SIZE);
+ if (!card->membase) {
+ printk("nicstar%d: can't ioremap() membase.\n", i);
+ error = 3;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ PRINTK("nicstar%d: membase at 0x%p.\n", i, card->membase);
+
+ pci_set_master(pcidev);
+
+ if (pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency) != 0) {
+ printk("nicstar%d: can't read PCI latency timer.\n", i);
+ error = 6;
+ ns_init_card_error(card, error);
+ return error;
+ }
+#ifdef NS_PCI_LATENCY
+ if (pci_latency < NS_PCI_LATENCY) {
+ PRINTK("nicstar%d: setting PCI latency timer to %d.\n", i,
+ NS_PCI_LATENCY);
+ for (j = 1; j < 4; j++) {
+ if (pci_write_config_byte
+ (pcidev, PCI_LATENCY_TIMER, NS_PCI_LATENCY) != 0)
+ break;
+ }
+ if (j == 4) {
+ printk
+ ("nicstar%d: can't set PCI latency timer to %d.\n",
+ i, NS_PCI_LATENCY);
+ error = 7;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ }
+#endif /* NS_PCI_LATENCY */
+
+ /* Clear timer overflow */
+ data = readl(card->membase + STAT);
+ if (data & NS_STAT_TMROF)
+ writel(NS_STAT_TMROF, card->membase + STAT);
+
+ /* Software reset */
+ writel(NS_CFG_SWRST, card->membase + CFG);
+ NS_DELAY;
+ writel(0x00000000, card->membase + CFG);
+
+ /* PHY reset */
+ writel(0x00000008, card->membase + GP);
+ NS_DELAY;
+ writel(0x00000001, card->membase + GP);
+ NS_DELAY;
+ while (CMD_BUSY(card)) ;
+ writel(NS_CMD_WRITE_UTILITY | 0x00000100, card->membase + CMD); /* Sync UTOPIA with SAR clock */
+ NS_DELAY;
+
+ /* Detect PHY type */
+ while (CMD_BUSY(card)) ;
+ writel(NS_CMD_READ_UTILITY | 0x00000200, card->membase + CMD);
+ while (CMD_BUSY(card)) ;
+ data = readl(card->membase + DR0);
+ switch (data) {
+ case 0x00000009:
+ printk("nicstar%d: PHY seems to be 25 Mbps.\n", i);
+ card->max_pcr = ATM_25_PCR;
+ while (CMD_BUSY(card)) ;
+ writel(0x00000008, card->membase + DR0);
+ writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD);
+ /* Clear an eventual pending interrupt */
+ writel(NS_STAT_SFBQF, card->membase + STAT);
+#ifdef PHY_LOOPBACK
+ while (CMD_BUSY(card)) ;
+ writel(0x00000022, card->membase + DR0);
+ writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD);
+#endif /* PHY_LOOPBACK */
+ break;
+ case 0x00000030:
+ case 0x00000031:
+ printk("nicstar%d: PHY seems to be 155 Mbps.\n", i);
+ card->max_pcr = ATM_OC3_PCR;
+#ifdef PHY_LOOPBACK
+ while (CMD_BUSY(card)) ;
+ writel(0x00000002, card->membase + DR0);
+ writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD);
+#endif /* PHY_LOOPBACK */
+ break;
+ default:
+ printk("nicstar%d: unknown PHY type (0x%08X).\n", i, data);
+ error = 8;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ writel(0x00000000, card->membase + GP);
+
+ /* Determine SRAM size */
+ data = 0x76543210;
+ ns_write_sram(card, 0x1C003, &data, 1);
+ data = 0x89ABCDEF;
+ ns_write_sram(card, 0x14003, &data, 1);
+ if (ns_read_sram(card, 0x14003) == 0x89ABCDEF &&
+ ns_read_sram(card, 0x1C003) == 0x76543210)
+ card->sram_size = 128;
+ else
+ card->sram_size = 32;
+ PRINTK("nicstar%d: %dK x 32bit SRAM size.\n", i, card->sram_size);
+
+ card->rct_size = NS_MAX_RCTSIZE;
+
+#if (NS_MAX_RCTSIZE == 4096)
+ if (card->sram_size == 128)
+ printk
+ ("nicstar%d: limiting maximum VCI. See NS_MAX_RCTSIZE in nicstar.h\n",
+ i);
+#elif (NS_MAX_RCTSIZE == 16384)
+ if (card->sram_size == 32) {
+ printk
+ ("nicstar%d: wasting memory. See NS_MAX_RCTSIZE in nicstar.h\n",
+ i);
+ card->rct_size = 4096;
+ }
+#else
+#error NS_MAX_RCTSIZE must be either 4096 or 16384 in nicstar.c
+#endif
+
+ card->vpibits = NS_VPIBITS;
+ if (card->rct_size == 4096)
+ card->vcibits = 12 - NS_VPIBITS;
+ else /* card->rct_size == 16384 */
+ card->vcibits = 14 - NS_VPIBITS;
+
+ /* Initialize the nicstar eeprom/eprom stuff, for the MAC addr */
+ if (mac[i] == NULL)
+ nicstar_init_eprom(card->membase);
+
+ /* Set the VPI/VCI MSb mask to zero so we can receive OAM cells */
+ writel(0x00000000, card->membase + VPM);
+
+ /* Initialize TSQ */
+ card->tsq.org = dma_alloc_coherent(&card->pcidev->dev,
+ NS_TSQSIZE + NS_TSQ_ALIGNMENT,
+ &card->tsq.dma, GFP_KERNEL);
+ if (card->tsq.org == NULL) {
+ printk("nicstar%d: can't allocate TSQ.\n", i);
+ error = 10;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ card->tsq.base = PTR_ALIGN(card->tsq.org, NS_TSQ_ALIGNMENT);
+ card->tsq.next = card->tsq.base;
+ card->tsq.last = card->tsq.base + (NS_TSQ_NUM_ENTRIES - 1);
+ for (j = 0; j < NS_TSQ_NUM_ENTRIES; j++)
+ ns_tsi_init(card->tsq.base + j);
+ writel(0x00000000, card->membase + TSQH);
+ writel(ALIGN(card->tsq.dma, NS_TSQ_ALIGNMENT), card->membase + TSQB);
+ PRINTK("nicstar%d: TSQ base at 0x%p.\n", i, card->tsq.base);
+
+ /* Initialize RSQ */
+ card->rsq.org = dma_alloc_coherent(&card->pcidev->dev,
+ NS_RSQSIZE + NS_RSQ_ALIGNMENT,
+ &card->rsq.dma, GFP_KERNEL);
+ if (card->rsq.org == NULL) {
+ printk("nicstar%d: can't allocate RSQ.\n", i);
+ error = 11;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ card->rsq.base = PTR_ALIGN(card->rsq.org, NS_RSQ_ALIGNMENT);
+ card->rsq.next = card->rsq.base;
+ card->rsq.last = card->rsq.base + (NS_RSQ_NUM_ENTRIES - 1);
+ for (j = 0; j < NS_RSQ_NUM_ENTRIES; j++)
+ ns_rsqe_init(card->rsq.base + j);
+ writel(0x00000000, card->membase + RSQH);
+ writel(ALIGN(card->rsq.dma, NS_RSQ_ALIGNMENT), card->membase + RSQB);
+ PRINTK("nicstar%d: RSQ base at 0x%p.\n", i, card->rsq.base);
+
+ /* Initialize SCQ0, the only VBR SCQ used */
+ card->scq1 = NULL;
+ card->scq2 = NULL;
+ card->scq0 = get_scq(card, VBR_SCQSIZE, NS_VRSCD0);
+ if (card->scq0 == NULL) {
+ printk("nicstar%d: can't get SCQ0.\n", i);
+ error = 12;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ u32d[0] = scq_virt_to_bus(card->scq0, card->scq0->base);
+ u32d[1] = (u32) 0x00000000;
+ u32d[2] = (u32) 0xffffffff;
+ u32d[3] = (u32) 0x00000000;
+ ns_write_sram(card, NS_VRSCD0, u32d, 4);
+ ns_write_sram(card, NS_VRSCD1, u32d, 4); /* These last two won't be used */
+ ns_write_sram(card, NS_VRSCD2, u32d, 4); /* but are initialized, just in case... */
+ card->scq0->scd = NS_VRSCD0;
+ PRINTK("nicstar%d: VBR-SCQ0 base at 0x%p.\n", i, card->scq0->base);
+
+ /* Initialize TSTs */
+ card->tst_addr = NS_TST0;
+ card->tst_free_entries = NS_TST_NUM_ENTRIES;
+ data = NS_TST_OPCODE_VARIABLE;
+ for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
+ ns_write_sram(card, NS_TST0 + j, &data, 1);
+ data = ns_tste_make(NS_TST_OPCODE_END, NS_TST0);
+ ns_write_sram(card, NS_TST0 + NS_TST_NUM_ENTRIES, &data, 1);
+ for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
+ ns_write_sram(card, NS_TST1 + j, &data, 1);
+ data = ns_tste_make(NS_TST_OPCODE_END, NS_TST1);
+ ns_write_sram(card, NS_TST1 + NS_TST_NUM_ENTRIES, &data, 1);
+ for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
+ card->tste2vc[j] = NULL;
+ writel(NS_TST0 << 2, card->membase + TSTB);
+
+ /* Initialize RCT. AAL type is set on opening the VC. */
+#ifdef RCQ_SUPPORT
+ u32d[0] = NS_RCTE_RAWCELLINTEN;
+#else
+ u32d[0] = 0x00000000;
+#endif /* RCQ_SUPPORT */
+ u32d[1] = 0x00000000;
+ u32d[2] = 0x00000000;
+ u32d[3] = 0xFFFFFFFF;
+ for (j = 0; j < card->rct_size; j++)
+ ns_write_sram(card, j * 4, u32d, 4);
+
+ memset(card->vcmap, 0, NS_MAX_RCTSIZE * sizeof(vc_map));
+
+ for (j = 0; j < NS_FRSCD_NUM; j++)
+ card->scd2vc[j] = NULL;
+
+ /* Initialize buffer levels */
+ card->sbnr.min = MIN_SB;
+ card->sbnr.init = NUM_SB;
+ card->sbnr.max = MAX_SB;
+ card->lbnr.min = MIN_LB;
+ card->lbnr.init = NUM_LB;
+ card->lbnr.max = MAX_LB;
+ card->iovnr.min = MIN_IOVB;
+ card->iovnr.init = NUM_IOVB;
+ card->iovnr.max = MAX_IOVB;
+ card->hbnr.min = MIN_HB;
+ card->hbnr.init = NUM_HB;
+ card->hbnr.max = MAX_HB;
+
+ card->sm_handle = NULL;
+ card->sm_addr = 0x00000000;
+ card->lg_handle = NULL;
+ card->lg_addr = 0x00000000;
+
+ card->efbie = 1; /* To prevent push_rxbufs from enabling the interrupt */
+
+ idr_init(&card->idr);
+
+ /* Pre-allocate some huge buffers */
+ skb_queue_head_init(&card->hbpool.queue);
+ card->hbpool.count = 0;
+ for (j = 0; j < NUM_HB; j++) {
+ struct sk_buff *hb;
+ hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL);
+ if (hb == NULL) {
+ printk
+ ("nicstar%d: can't allocate %dth of %d huge buffers.\n",
+ i, j, NUM_HB);
+ error = 13;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ NS_PRV_BUFTYPE(hb) = BUF_NONE;
+ skb_queue_tail(&card->hbpool.queue, hb);
+ card->hbpool.count++;
+ }
+
+ /* Allocate large buffers */
+ skb_queue_head_init(&card->lbpool.queue);
+ card->lbpool.count = 0; /* Not used */
+ for (j = 0; j < NUM_LB; j++) {
+ struct sk_buff *lb;
+ lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL);
+ if (lb == NULL) {
+ printk
+ ("nicstar%d: can't allocate %dth of %d large buffers.\n",
+ i, j, NUM_LB);
+ error = 14;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ NS_PRV_BUFTYPE(lb) = BUF_LG;
+ skb_queue_tail(&card->lbpool.queue, lb);
+ skb_reserve(lb, NS_SMBUFSIZE);
+ push_rxbufs(card, lb);
+ /* Due to the implementation of push_rxbufs() this is 1, not 0 */
+ if (j == 1) {
+ card->rcbuf = lb;
+ card->rawcell = (struct ns_rcqe *) lb->data;
+ card->rawch = NS_PRV_DMA(lb);
+ }
+ }
+ /* Test for strange behaviour which leads to crashes */
+ if ((bcount =
+ ns_stat_lfbqc_get(readl(card->membase + STAT))) < card->lbnr.min) {
+ printk
+ ("nicstar%d: Strange... Just allocated %d large buffers and lfbqc = %d.\n",
+ i, j, bcount);
+ error = 14;
+ ns_init_card_error(card, error);
+ return error;
+ }
+
+ /* Allocate small buffers */
+ skb_queue_head_init(&card->sbpool.queue);
+ card->sbpool.count = 0; /* Not used */
+ for (j = 0; j < NUM_SB; j++) {
+ struct sk_buff *sb;
+ sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL);
+ if (sb == NULL) {
+ printk
+ ("nicstar%d: can't allocate %dth of %d small buffers.\n",
+ i, j, NUM_SB);
+ error = 15;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ NS_PRV_BUFTYPE(sb) = BUF_SM;
+ skb_queue_tail(&card->sbpool.queue, sb);
+ skb_reserve(sb, NS_AAL0_HEADER);
+ push_rxbufs(card, sb);
+ }
+ /* Test for strange behaviour which leads to crashes */
+ if ((bcount =
+ ns_stat_sfbqc_get(readl(card->membase + STAT))) < card->sbnr.min) {
+ printk
+ ("nicstar%d: Strange... Just allocated %d small buffers and sfbqc = %d.\n",
+ i, j, bcount);
+ error = 15;
+ ns_init_card_error(card, error);
+ return error;
+ }
+
+ /* Allocate iovec buffers */
+ skb_queue_head_init(&card->iovpool.queue);
+ card->iovpool.count = 0;
+ for (j = 0; j < NUM_IOVB; j++) {
+ struct sk_buff *iovb;
+ iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL);
+ if (iovb == NULL) {
+ printk
+ ("nicstar%d: can't allocate %dth of %d iovec buffers.\n",
+ i, j, NUM_IOVB);
+ error = 16;
+ ns_init_card_error(card, error);
+ return error;
+ }
+ NS_PRV_BUFTYPE(iovb) = BUF_NONE;
+ skb_queue_tail(&card->iovpool.queue, iovb);
+ card->iovpool.count++;
+ }
+
+ /* Configure NICStAR */
+ if (card->rct_size == 4096)
+ ns_cfg_rctsize = NS_CFG_RCTSIZE_4096_ENTRIES;
+ else /* (card->rct_size == 16384) */
+ ns_cfg_rctsize = NS_CFG_RCTSIZE_16384_ENTRIES;
+
+ card->efbie = 1;
+
+ card->intcnt = 0;
+ if (request_irq
+ (pcidev->irq, &ns_irq_handler, IRQF_SHARED, "nicstar", card) != 0) {
+ printk("nicstar%d: can't allocate IRQ %d.\n", i, pcidev->irq);
+ error = 9;
+ ns_init_card_error(card, error);
+ return error;
+ }
+
+ /* Register device */
+ card->atmdev = atm_dev_register("nicstar", &card->pcidev->dev, &atm_ops,
+ -1, NULL);
+ if (card->atmdev == NULL) {
+ printk("nicstar%d: can't register device.\n", i);
+ error = 17;
+ ns_init_card_error(card, error);
+ return error;
+ }
+
+ if (mac[i] == NULL || !mac_pton(mac[i], card->atmdev->esi)) {
+ nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET,
+ card->atmdev->esi, 6);
+ if (ether_addr_equal(card->atmdev->esi, "\x00\x00\x00\x00\x00\x00")) {
+ nicstar_read_eprom(card->membase,
+ NICSTAR_EPROM_MAC_ADDR_OFFSET_ALT,
+ card->atmdev->esi, 6);
+ }
+ }
+
+ printk("nicstar%d: MAC address %pM\n", i, card->atmdev->esi);
+
+ card->atmdev->dev_data = card;
+ card->atmdev->ci_range.vpi_bits = card->vpibits;
+ card->atmdev->ci_range.vci_bits = card->vcibits;
+ card->atmdev->link_rate = card->max_pcr;
+ card->atmdev->phy = NULL;
+
+#ifdef CONFIG_ATM_NICSTAR_USE_SUNI
+ if (card->max_pcr == ATM_OC3_PCR)
+ suni_init(card->atmdev);
+#endif /* CONFIG_ATM_NICSTAR_USE_SUNI */
+
+#ifdef CONFIG_ATM_NICSTAR_USE_IDT77105
+ if (card->max_pcr == ATM_25_PCR)
+ idt77105_init(card->atmdev);
+#endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */
+
+ if (card->atmdev->phy && card->atmdev->phy->start)
+ card->atmdev->phy->start(card->atmdev);
+
+ writel(NS_CFG_RXPATH | NS_CFG_SMBUFSIZE | NS_CFG_LGBUFSIZE | NS_CFG_EFBIE | NS_CFG_RSQSIZE | NS_CFG_VPIBITS | ns_cfg_rctsize | NS_CFG_RXINT_NODELAY | NS_CFG_RAWIE | /* Only enabled if RCQ_SUPPORT */
+ NS_CFG_RSQAFIE | NS_CFG_TXEN | NS_CFG_TXIE | NS_CFG_TSQFIE_OPT | /* Only enabled if ENABLE_TSQFIE */
+ NS_CFG_PHYIE, card->membase + CFG);
+
+ num_cards++;
+
+ return error;
+}
+
+static void ns_init_card_error(ns_dev *card, int error)
+{
+ if (error >= 17) {
+ writel(0x00000000, card->membase + CFG);
+ }
+ if (error >= 16) {
+ struct sk_buff *iovb;
+ while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
+ dev_kfree_skb_any(iovb);
+ }
+ if (error >= 15) {
+ struct sk_buff *sb;
+ while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
+ dev_kfree_skb_any(sb);
+ free_scq(card, card->scq0, NULL);
+ }
+ if (error >= 14) {
+ struct sk_buff *lb;
+ while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
+ dev_kfree_skb_any(lb);
+ }
+ if (error >= 13) {
+ struct sk_buff *hb;
+ while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
+ dev_kfree_skb_any(hb);
+ }
+ if (error >= 12) {
+ kfree(card->rsq.org);
+ }
+ if (error >= 11) {
+ kfree(card->tsq.org);
+ }
+ if (error >= 10) {
+ free_irq(card->pcidev->irq, card);
+ }
+ if (error >= 4) {
+ iounmap(card->membase);
+ }
+ if (error >= 3) {
+ pci_disable_device(card->pcidev);
+ kfree(card);
+ }
+}
+
+static scq_info *get_scq(ns_dev *card, int size, u32 scd)
+{
+ scq_info *scq;
+ int i;
+
+ if (size != VBR_SCQSIZE && size != CBR_SCQSIZE)
+ return NULL;
+
+ scq = kmalloc(sizeof(scq_info), GFP_KERNEL);
+ if (!scq)
+ return NULL;
+ scq->org = dma_alloc_coherent(&card->pcidev->dev,
+ 2 * size, &scq->dma, GFP_KERNEL);
+ if (!scq->org) {
+ kfree(scq);
+ return NULL;
+ }
+ scq->skb = kmalloc(sizeof(struct sk_buff *) *
+ (size / NS_SCQE_SIZE), GFP_KERNEL);
+ if (!scq->skb) {
+ kfree(scq->org);
+ kfree(scq);
+ return NULL;
+ }
+ scq->num_entries = size / NS_SCQE_SIZE;
+ scq->base = PTR_ALIGN(scq->org, size);
+ scq->next = scq->base;
+ scq->last = scq->base + (scq->num_entries - 1);
+ scq->tail = scq->last;
+ scq->scd = scd;
+ scq->num_entries = size / NS_SCQE_SIZE;
+ scq->tbd_count = 0;
+ init_waitqueue_head(&scq->scqfull_waitq);
+ scq->full = 0;
+ spin_lock_init(&scq->lock);
+
+ for (i = 0; i < scq->num_entries; i++)
+ scq->skb[i] = NULL;
+
+ return scq;
+}
+
+/* For variable rate SCQ vcc must be NULL */
+static void free_scq(ns_dev *card, scq_info *scq, struct atm_vcc *vcc)
+{
+ int i;
+
+ if (scq->num_entries == VBR_SCQ_NUM_ENTRIES)
+ for (i = 0; i < scq->num_entries; i++) {
+ if (scq->skb[i] != NULL) {
+ vcc = ATM_SKB(scq->skb[i])->vcc;
+ if (vcc->pop != NULL)
+ vcc->pop(vcc, scq->skb[i]);
+ else
+ dev_kfree_skb_any(scq->skb[i]);
+ }
+ } else { /* vcc must be != NULL */
+
+ if (vcc == NULL) {
+ printk
+ ("nicstar: free_scq() called with vcc == NULL for fixed rate scq.");
+ for (i = 0; i < scq->num_entries; i++)
+ dev_kfree_skb_any(scq->skb[i]);
+ } else
+ for (i = 0; i < scq->num_entries; i++) {
+ if (scq->skb[i] != NULL) {
+ if (vcc->pop != NULL)
+ vcc->pop(vcc, scq->skb[i]);
+ else
+ dev_kfree_skb_any(scq->skb[i]);
+ }
+ }
+ }
+ kfree(scq->skb);
+ dma_free_coherent(&card->pcidev->dev,
+ 2 * (scq->num_entries == VBR_SCQ_NUM_ENTRIES ?
+ VBR_SCQSIZE : CBR_SCQSIZE),
+ scq->org, scq->dma);
+ kfree(scq);
+}
+
+/* The handles passed must be pointers to the sk_buff containing the small
+ or large buffer(s) cast to u32. */
+static void push_rxbufs(ns_dev * card, struct sk_buff *skb)
+{
+ struct sk_buff *handle1, *handle2;
+ int id1, id2;
+ u32 addr1, addr2;
+ u32 stat;
+ unsigned long flags;
+
+ /* *BARF* */
+ handle2 = NULL;
+ addr2 = 0;
+ handle1 = skb;
+ addr1 = dma_map_single(&card->pcidev->dev,
+ skb->data,
+ (NS_PRV_BUFTYPE(skb) == BUF_SM
+ ? NS_SMSKBSIZE : NS_LGSKBSIZE),
+ DMA_TO_DEVICE);
+ NS_PRV_DMA(skb) = addr1; /* save so we can unmap later */
+
+#ifdef GENERAL_DEBUG
+ if (!addr1)
+ printk("nicstar%d: push_rxbufs called with addr1 = 0.\n",
+ card->index);
+#endif /* GENERAL_DEBUG */
+
+ stat = readl(card->membase + STAT);
+ card->sbfqc = ns_stat_sfbqc_get(stat);
+ card->lbfqc = ns_stat_lfbqc_get(stat);
+ if (NS_PRV_BUFTYPE(skb) == BUF_SM) {
+ if (!addr2) {
+ if (card->sm_addr) {
+ addr2 = card->sm_addr;
+ handle2 = card->sm_handle;
+ card->sm_addr = 0x00000000;
+ card->sm_handle = NULL;
+ } else { /* (!sm_addr) */
+
+ card->sm_addr = addr1;
+ card->sm_handle = handle1;
+ }
+ }
+ } else { /* buf_type == BUF_LG */
+
+ if (!addr2) {
+ if (card->lg_addr) {
+ addr2 = card->lg_addr;
+ handle2 = card->lg_handle;
+ card->lg_addr = 0x00000000;
+ card->lg_handle = NULL;
+ } else { /* (!lg_addr) */
+
+ card->lg_addr = addr1;
+ card->lg_handle = handle1;
+ }
+ }
+ }
+
+ if (addr2) {
+ if (NS_PRV_BUFTYPE(skb) == BUF_SM) {
+ if (card->sbfqc >= card->sbnr.max) {
+ skb_unlink(handle1, &card->sbpool.queue);
+ dev_kfree_skb_any(handle1);
+ skb_unlink(handle2, &card->sbpool.queue);
+ dev_kfree_skb_any(handle2);
+ return;
+ } else
+ card->sbfqc += 2;
+ } else { /* (buf_type == BUF_LG) */
+
+ if (card->lbfqc >= card->lbnr.max) {
+ skb_unlink(handle1, &card->lbpool.queue);
+ dev_kfree_skb_any(handle1);
+ skb_unlink(handle2, &card->lbpool.queue);
+ dev_kfree_skb_any(handle2);
+ return;
+ } else
+ card->lbfqc += 2;
+ }
+
+ id1 = idr_alloc(&card->idr, handle1, 0, 0, GFP_ATOMIC);
+ if (id1 < 0)
+ goto out;
+
+ id2 = idr_alloc(&card->idr, handle2, 0, 0, GFP_ATOMIC);
+ if (id2 < 0)
+ goto out;
+
+ spin_lock_irqsave(&card->res_lock, flags);
+ while (CMD_BUSY(card)) ;
+ writel(addr2, card->membase + DR3);
+ writel(id2, card->membase + DR2);
+ writel(addr1, card->membase + DR1);
+ writel(id1, card->membase + DR0);
+ writel(NS_CMD_WRITE_FREEBUFQ | NS_PRV_BUFTYPE(skb),
+ card->membase + CMD);
+ spin_unlock_irqrestore(&card->res_lock, flags);
+
+ XPRINTK("nicstar%d: Pushing %s buffers at 0x%x and 0x%x.\n",
+ card->index,
+ (NS_PRV_BUFTYPE(skb) == BUF_SM ? "small" : "large"),
+ addr1, addr2);
+ }
+
+ if (!card->efbie && card->sbfqc >= card->sbnr.min &&
+ card->lbfqc >= card->lbnr.min) {
+ card->efbie = 1;
+ writel((readl(card->membase + CFG) | NS_CFG_EFBIE),
+ card->membase + CFG);
+ }
+
+out:
+ return;
+}
+
+static irqreturn_t ns_irq_handler(int irq, void *dev_id)
+{
+ u32 stat_r;
+ ns_dev *card;
+ struct atm_dev *dev;
+ unsigned long flags;
+
+ card = (ns_dev *) dev_id;
+ dev = card->atmdev;
+ card->intcnt++;
+
+ PRINTK("nicstar%d: NICStAR generated an interrupt\n", card->index);
+
+ spin_lock_irqsave(&card->int_lock, flags);
+
+ stat_r = readl(card->membase + STAT);
+
+ /* Transmit Status Indicator has been written to T. S. Queue */
+ if (stat_r & NS_STAT_TSIF) {
+ TXPRINTK("nicstar%d: TSI interrupt\n", card->index);
+ process_tsq(card);
+ writel(NS_STAT_TSIF, card->membase + STAT);
+ }
+
+ /* Incomplete CS-PDU has been transmitted */
+ if (stat_r & NS_STAT_TXICP) {
+ writel(NS_STAT_TXICP, card->membase + STAT);
+ TXPRINTK("nicstar%d: Incomplete CS-PDU transmitted.\n",
+ card->index);
+ }
+
+ /* Transmit Status Queue 7/8 full */
+ if (stat_r & NS_STAT_TSQF) {
+ writel(NS_STAT_TSQF, card->membase + STAT);
+ PRINTK("nicstar%d: TSQ full.\n", card->index);
+ process_tsq(card);
+ }
+
+ /* Timer overflow */
+ if (stat_r & NS_STAT_TMROF) {
+ writel(NS_STAT_TMROF, card->membase + STAT);
+ PRINTK("nicstar%d: Timer overflow.\n", card->index);
+ }
+
+ /* PHY device interrupt signal active */
+ if (stat_r & NS_STAT_PHYI) {
+ writel(NS_STAT_PHYI, card->membase + STAT);
+ PRINTK("nicstar%d: PHY interrupt.\n", card->index);
+ if (dev->phy && dev->phy->interrupt) {
+ dev->phy->interrupt(dev);
+ }
+ }
+
+ /* Small Buffer Queue is full */
+ if (stat_r & NS_STAT_SFBQF) {
+ writel(NS_STAT_SFBQF, card->membase + STAT);
+ printk("nicstar%d: Small free buffer queue is full.\n",
+ card->index);
+ }
+
+ /* Large Buffer Queue is full */
+ if (stat_r & NS_STAT_LFBQF) {
+ writel(NS_STAT_LFBQF, card->membase + STAT);
+ printk("nicstar%d: Large free buffer queue is full.\n",
+ card->index);
+ }
+
+ /* Receive Status Queue is full */
+ if (stat_r & NS_STAT_RSQF) {
+ writel(NS_STAT_RSQF, card->membase + STAT);
+ printk("nicstar%d: RSQ full.\n", card->index);
+ process_rsq(card);
+ }
+
+ /* Complete CS-PDU received */
+ if (stat_r & NS_STAT_EOPDU) {
+ RXPRINTK("nicstar%d: End of CS-PDU received.\n", card->index);
+ process_rsq(card);
+ writel(NS_STAT_EOPDU, card->membase + STAT);
+ }
+
+ /* Raw cell received */
+ if (stat_r & NS_STAT_RAWCF) {
+ writel(NS_STAT_RAWCF, card->membase + STAT);
+#ifndef RCQ_SUPPORT
+ printk("nicstar%d: Raw cell received and no support yet...\n",
+ card->index);
+#endif /* RCQ_SUPPORT */
+ /* NOTE: the following procedure may keep a raw cell pending until the
+ next interrupt. As this preliminary support is only meant to
+ avoid buffer leakage, this is not an issue. */
+ while (readl(card->membase + RAWCT) != card->rawch) {
+
+ if (ns_rcqe_islast(card->rawcell)) {
+ struct sk_buff *oldbuf;
+
+ oldbuf = card->rcbuf;
+ card->rcbuf = idr_find(&card->idr,
+ ns_rcqe_nextbufhandle(card->rawcell));
+ card->rawch = NS_PRV_DMA(card->rcbuf);
+ card->rawcell = (struct ns_rcqe *)
+ card->rcbuf->data;
+ recycle_rx_buf(card, oldbuf);
+ } else {
+ card->rawch += NS_RCQE_SIZE;
+ card->rawcell++;
+ }
+ }
+ }
+
+ /* Small buffer queue is empty */
+ if (stat_r & NS_STAT_SFBQE) {
+ int i;
+ struct sk_buff *sb;
+
+ writel(NS_STAT_SFBQE, card->membase + STAT);
+ printk("nicstar%d: Small free buffer queue empty.\n",
+ card->index);
+ for (i = 0; i < card->sbnr.min; i++) {
+ sb = dev_alloc_skb(NS_SMSKBSIZE);
+ if (sb == NULL) {
+ writel(readl(card->membase + CFG) &
+ ~NS_CFG_EFBIE, card->membase + CFG);
+ card->efbie = 0;
+ break;
+ }
+ NS_PRV_BUFTYPE(sb) = BUF_SM;
+ skb_queue_tail(&card->sbpool.queue, sb);
+ skb_reserve(sb, NS_AAL0_HEADER);
+ push_rxbufs(card, sb);
+ }
+ card->sbfqc = i;
+ process_rsq(card);
+ }
+
+ /* Large buffer queue empty */
+ if (stat_r & NS_STAT_LFBQE) {
+ int i;
+ struct sk_buff *lb;
+
+ writel(NS_STAT_LFBQE, card->membase + STAT);
+ printk("nicstar%d: Large free buffer queue empty.\n",
+ card->index);
+ for (i = 0; i < card->lbnr.min; i++) {
+ lb = dev_alloc_skb(NS_LGSKBSIZE);
+ if (lb == NULL) {
+ writel(readl(card->membase + CFG) &
+ ~NS_CFG_EFBIE, card->membase + CFG);
+ card->efbie = 0;
+ break;
+ }
+ NS_PRV_BUFTYPE(lb) = BUF_LG;
+ skb_queue_tail(&card->lbpool.queue, lb);
+ skb_reserve(lb, NS_SMBUFSIZE);
+ push_rxbufs(card, lb);
+ }
+ card->lbfqc = i;
+ process_rsq(card);
+ }
+
+ /* Receive Status Queue is 7/8 full */
+ if (stat_r & NS_STAT_RSQAF) {
+ writel(NS_STAT_RSQAF, card->membase + STAT);
+ RXPRINTK("nicstar%d: RSQ almost full.\n", card->index);
+ process_rsq(card);
+ }
+
+ spin_unlock_irqrestore(&card->int_lock, flags);
+ PRINTK("nicstar%d: end of interrupt service\n", card->index);
+ return IRQ_HANDLED;
+}
+
+static int ns_open(struct atm_vcc *vcc)
+{
+ ns_dev *card;
+ vc_map *vc;
+ unsigned long tmpl, modl;
+ int tcr, tcra; /* target cell rate, and absolute value */
+ int n = 0; /* Number of entries in the TST. Initialized to remove
+ the compiler warning. */
+ u32 u32d[4];
+ int frscdi = 0; /* Index of the SCD. Initialized to remove the compiler
+ warning. How I wish compilers were clever enough to
+ tell which variables can truly be used
+ uninitialized... */
+ int inuse; /* tx or rx vc already in use by another vcc */
+ short vpi = vcc->vpi;
+ int vci = vcc->vci;
+
+ card = (ns_dev *) vcc->dev->dev_data;
+ PRINTK("nicstar%d: opening vpi.vci %d.%d \n", card->index, (int)vpi,
+ vci);
+ if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) {
+ PRINTK("nicstar%d: unsupported AAL.\n", card->index);
+ return -EINVAL;
+ }
+
+ vc = &(card->vcmap[vpi << card->vcibits | vci]);
+ vcc->dev_data = vc;
+
+ inuse = 0;
+ if (vcc->qos.txtp.traffic_class != ATM_NONE && vc->tx)
+ inuse = 1;
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE && vc->rx)
+ inuse += 2;
+ if (inuse) {
+ printk("nicstar%d: %s vci already in use.\n", card->index,
+ inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx");
+ return -EINVAL;
+ }
+
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+
+ /* NOTE: You are not allowed to modify an open connection's QOS. To change
+ that, remove the ATM_VF_PARTIAL flag checking. There may be other changes
+ needed to do that. */
+ if (!test_bit(ATM_VF_PARTIAL, &vcc->flags)) {
+ scq_info *scq;
+
+ set_bit(ATM_VF_PARTIAL, &vcc->flags);
+ if (vcc->qos.txtp.traffic_class == ATM_CBR) {
+ /* Check requested cell rate and availability of SCD */
+ if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0
+ && vcc->qos.txtp.min_pcr == 0) {
+ PRINTK
+ ("nicstar%d: trying to open a CBR vc with cell rate = 0 \n",
+ card->index);
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ return -EINVAL;
+ }
+
+ tcr = atm_pcr_goal(&(vcc->qos.txtp));
+ tcra = tcr >= 0 ? tcr : -tcr;
+
+ PRINTK("nicstar%d: target cell rate = %d.\n",
+ card->index, vcc->qos.txtp.max_pcr);
+
+ tmpl =
+ (unsigned long)tcra *(unsigned long)
+ NS_TST_NUM_ENTRIES;
+ modl = tmpl % card->max_pcr;
+
+ n = (int)(tmpl / card->max_pcr);
+ if (tcr > 0) {
+ if (modl > 0)
+ n++;
+ } else if (tcr == 0) {
+ if ((n =
+ (card->tst_free_entries -
+ NS_TST_RESERVED)) <= 0) {
+ PRINTK
+ ("nicstar%d: no CBR bandwidth free.\n",
+ card->index);
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ return -EINVAL;
+ }
+ }
+
+ if (n == 0) {
+ printk
+ ("nicstar%d: selected bandwidth < granularity.\n",
+ card->index);
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ return -EINVAL;
+ }
+
+ if (n > (card->tst_free_entries - NS_TST_RESERVED)) {
+ PRINTK
+ ("nicstar%d: not enough free CBR bandwidth.\n",
+ card->index);
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ return -EINVAL;
+ } else
+ card->tst_free_entries -= n;
+
+ XPRINTK("nicstar%d: writing %d tst entries.\n",
+ card->index, n);
+ for (frscdi = 0; frscdi < NS_FRSCD_NUM; frscdi++) {
+ if (card->scd2vc[frscdi] == NULL) {
+ card->scd2vc[frscdi] = vc;
+ break;
+ }
+ }
+ if (frscdi == NS_FRSCD_NUM) {
+ PRINTK
+ ("nicstar%d: no SCD available for CBR channel.\n",
+ card->index);
+ card->tst_free_entries += n;
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ return -EBUSY;
+ }
+
+ vc->cbr_scd = NS_FRSCD + frscdi * NS_FRSCD_SIZE;
+
+ scq = get_scq(card, CBR_SCQSIZE, vc->cbr_scd);
+ if (scq == NULL) {
+ PRINTK("nicstar%d: can't get fixed rate SCQ.\n",
+ card->index);
+ card->scd2vc[frscdi] = NULL;
+ card->tst_free_entries += n;
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ return -ENOMEM;
+ }
+ vc->scq = scq;
+ u32d[0] = scq_virt_to_bus(scq, scq->base);
+ u32d[1] = (u32) 0x00000000;
+ u32d[2] = (u32) 0xffffffff;
+ u32d[3] = (u32) 0x00000000;
+ ns_write_sram(card, vc->cbr_scd, u32d, 4);
+
+ fill_tst(card, n, vc);
+ } else if (vcc->qos.txtp.traffic_class == ATM_UBR) {
+ vc->cbr_scd = 0x00000000;
+ vc->scq = card->scq0;
+ }
+
+ if (vcc->qos.txtp.traffic_class != ATM_NONE) {
+ vc->tx = 1;
+ vc->tx_vcc = vcc;
+ vc->tbd_count = 0;
+ }
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ u32 status;
+
+ vc->rx = 1;
+ vc->rx_vcc = vcc;
+ vc->rx_iov = NULL;
+
+ /* Open the connection in hardware */
+ if (vcc->qos.aal == ATM_AAL5)
+ status = NS_RCTE_AAL5 | NS_RCTE_CONNECTOPEN;
+ else /* vcc->qos.aal == ATM_AAL0 */
+ status = NS_RCTE_AAL0 | NS_RCTE_CONNECTOPEN;
+#ifdef RCQ_SUPPORT
+ status |= NS_RCTE_RAWCELLINTEN;
+#endif /* RCQ_SUPPORT */
+ ns_write_sram(card,
+ NS_RCT +
+ (vpi << card->vcibits | vci) *
+ NS_RCT_ENTRY_SIZE, &status, 1);
+ }
+
+ }
+
+ set_bit(ATM_VF_READY, &vcc->flags);
+ return 0;
+}
+
+static void ns_close(struct atm_vcc *vcc)
+{
+ vc_map *vc;
+ ns_dev *card;
+ u32 data;
+ int i;
+
+ vc = vcc->dev_data;
+ card = vcc->dev->dev_data;
+ PRINTK("nicstar%d: closing vpi.vci %d.%d \n", card->index,
+ (int)vcc->vpi, vcc->vci);
+
+ clear_bit(ATM_VF_READY, &vcc->flags);
+
+ if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
+ u32 addr;
+ unsigned long flags;
+
+ addr =
+ NS_RCT +
+ (vcc->vpi << card->vcibits | vcc->vci) * NS_RCT_ENTRY_SIZE;
+ spin_lock_irqsave(&card->res_lock, flags);
+ while (CMD_BUSY(card)) ;
+ writel(NS_CMD_CLOSE_CONNECTION | addr << 2,
+ card->membase + CMD);
+ spin_unlock_irqrestore(&card->res_lock, flags);
+
+ vc->rx = 0;
+ if (vc->rx_iov != NULL) {
+ struct sk_buff *iovb;
+ u32 stat;
+
+ stat = readl(card->membase + STAT);
+ card->sbfqc = ns_stat_sfbqc_get(stat);
+ card->lbfqc = ns_stat_lfbqc_get(stat);
+
+ PRINTK
+ ("nicstar%d: closing a VC with pending rx buffers.\n",
+ card->index);
+ iovb = vc->rx_iov;
+ recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
+ NS_PRV_IOVCNT(iovb));
+ NS_PRV_IOVCNT(iovb) = 0;
+ spin_lock_irqsave(&card->int_lock, flags);
+ recycle_iov_buf(card, iovb);
+ spin_unlock_irqrestore(&card->int_lock, flags);
+ vc->rx_iov = NULL;
+ }
+ }
+
+ if (vcc->qos.txtp.traffic_class != ATM_NONE) {
+ vc->tx = 0;
+ }
+
+ if (vcc->qos.txtp.traffic_class == ATM_CBR) {
+ unsigned long flags;
+ ns_scqe *scqep;
+ scq_info *scq;
+
+ scq = vc->scq;
+
+ for (;;) {
+ spin_lock_irqsave(&scq->lock, flags);
+ scqep = scq->next;
+ if (scqep == scq->base)
+ scqep = scq->last;
+ else
+ scqep--;
+ if (scqep == scq->tail) {
+ spin_unlock_irqrestore(&scq->lock, flags);
+ break;
+ }
+ /* If the last entry is not a TSR, place one in the SCQ in order to
+ be able to completely drain it and then close. */
+ if (!ns_scqe_is_tsr(scqep) && scq->tail != scq->next) {
+ ns_scqe tsr;
+ u32 scdi, scqi;
+ u32 data;
+ int index;
+
+ tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE);
+ scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE;
+ scqi = scq->next - scq->base;
+ tsr.word_2 = ns_tsr_mkword_2(scdi, scqi);
+ tsr.word_3 = 0x00000000;
+ tsr.word_4 = 0x00000000;
+ *scq->next = tsr;
+ index = (int)scqi;
+ scq->skb[index] = NULL;
+ if (scq->next == scq->last)
+ scq->next = scq->base;
+ else
+ scq->next++;
+ data = scq_virt_to_bus(scq, scq->next);
+ ns_write_sram(card, scq->scd, &data, 1);
+ }
+ spin_unlock_irqrestore(&scq->lock, flags);
+ schedule();
+ }
+
+ /* Free all TST entries */
+ data = NS_TST_OPCODE_VARIABLE;
+ for (i = 0; i < NS_TST_NUM_ENTRIES; i++) {
+ if (card->tste2vc[i] == vc) {
+ ns_write_sram(card, card->tst_addr + i, &data,
+ 1);
+ card->tste2vc[i] = NULL;
+ card->tst_free_entries++;
+ }
+ }
+
+ card->scd2vc[(vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE] = NULL;
+ free_scq(card, vc->scq, vcc);
+ }
+
+ /* remove all references to vcc before deleting it */
+ if (vcc->qos.txtp.traffic_class != ATM_NONE) {
+ unsigned long flags;
+ scq_info *scq = card->scq0;
+
+ spin_lock_irqsave(&scq->lock, flags);
+
+ for (i = 0; i < scq->num_entries; i++) {
+ if (scq->skb[i] && ATM_SKB(scq->skb[i])->vcc == vcc) {
+ ATM_SKB(scq->skb[i])->vcc = NULL;
+ atm_return(vcc, scq->skb[i]->truesize);
+ PRINTK
+ ("nicstar: deleted pending vcc mapping\n");
+ }
+ }
+
+ spin_unlock_irqrestore(&scq->lock, flags);
+ }
+
+ vcc->dev_data = NULL;
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+
+#ifdef RX_DEBUG
+ {
+ u32 stat, cfg;
+ stat = readl(card->membase + STAT);
+ cfg = readl(card->membase + CFG);
+ printk("STAT = 0x%08X CFG = 0x%08X \n", stat, cfg);
+ printk
+ ("TSQ: base = 0x%p next = 0x%p last = 0x%p TSQT = 0x%08X \n",
+ card->tsq.base, card->tsq.next,
+ card->tsq.last, readl(card->membase + TSQT));
+ printk
+ ("RSQ: base = 0x%p next = 0x%p last = 0x%p RSQT = 0x%08X \n",
+ card->rsq.base, card->rsq.next,
+ card->rsq.last, readl(card->membase + RSQT));
+ printk("Empty free buffer queue interrupt %s \n",
+ card->efbie ? "enabled" : "disabled");
+ printk("SBCNT = %d count = %d LBCNT = %d count = %d \n",
+ ns_stat_sfbqc_get(stat), card->sbpool.count,
+ ns_stat_lfbqc_get(stat), card->lbpool.count);
+ printk("hbpool.count = %d iovpool.count = %d \n",
+ card->hbpool.count, card->iovpool.count);
+ }
+#endif /* RX_DEBUG */
+}
+
+static void fill_tst(ns_dev * card, int n, vc_map * vc)
+{
+ u32 new_tst;
+ unsigned long cl;
+ int e, r;
+ u32 data;
+
+ /* It would be very complicated to keep the two TSTs synchronized while
+ assuring that writes are only made to the inactive TST. So, for now I
+ will use only one TST. If problems occur, I will change this again */
+
+ new_tst = card->tst_addr;
+
+ /* Fill procedure */
+
+ for (e = 0; e < NS_TST_NUM_ENTRIES; e++) {
+ if (card->tste2vc[e] == NULL)
+ break;
+ }
+ if (e == NS_TST_NUM_ENTRIES) {
+ printk("nicstar%d: No free TST entries found. \n", card->index);
+ return;
+ }
+
+ r = n;
+ cl = NS_TST_NUM_ENTRIES;
+ data = ns_tste_make(NS_TST_OPCODE_FIXED, vc->cbr_scd);
+
+ while (r > 0) {
+ if (cl >= NS_TST_NUM_ENTRIES && card->tste2vc[e] == NULL) {
+ card->tste2vc[e] = vc;
+ ns_write_sram(card, new_tst + e, &data, 1);
+ cl -= NS_TST_NUM_ENTRIES;
+ r--;
+ }
+
+ if (++e == NS_TST_NUM_ENTRIES) {
+ e = 0;
+ }
+ cl += n;
+ }
+
+ /* End of fill procedure */
+
+ data = ns_tste_make(NS_TST_OPCODE_END, new_tst);
+ ns_write_sram(card, new_tst + NS_TST_NUM_ENTRIES, &data, 1);
+ ns_write_sram(card, card->tst_addr + NS_TST_NUM_ENTRIES, &data, 1);
+ card->tst_addr = new_tst;
+}
+
+static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ ns_dev *card;
+ vc_map *vc;
+ scq_info *scq;
+ unsigned long buflen;
+ ns_scqe scqe;
+ u32 flags; /* TBD flags, not CPU flags */
+
+ card = vcc->dev->dev_data;
+ TXPRINTK("nicstar%d: ns_send() called.\n", card->index);
+ if ((vc = (vc_map *) vcc->dev_data) == NULL) {
+ printk("nicstar%d: vcc->dev_data == NULL on ns_send().\n",
+ card->index);
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ if (!vc->tx) {
+ printk("nicstar%d: Trying to transmit on a non-tx VC.\n",
+ card->index);
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) {
+ printk("nicstar%d: Only AAL0 and AAL5 are supported.\n",
+ card->index);
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ if (skb_shinfo(skb)->nr_frags != 0) {
+ printk("nicstar%d: No scatter-gather yet.\n", card->index);
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ ATM_SKB(skb)->vcc = vcc;
+
+ NS_PRV_DMA(skb) = dma_map_single(&card->pcidev->dev, skb->data,
+ skb->len, DMA_TO_DEVICE);
+
+ if (vcc->qos.aal == ATM_AAL5) {
+ buflen = (skb->len + 47 + 8) / 48 * 48; /* Multiple of 48 */
+ flags = NS_TBD_AAL5;
+ scqe.word_2 = cpu_to_le32(NS_PRV_DMA(skb));
+ scqe.word_3 = cpu_to_le32(skb->len);
+ scqe.word_4 =
+ ns_tbd_mkword_4(0, (u32) vcc->vpi, (u32) vcc->vci, 0,
+ ATM_SKB(skb)->
+ atm_options & ATM_ATMOPT_CLP ? 1 : 0);
+ flags |= NS_TBD_EOPDU;
+ } else { /* (vcc->qos.aal == ATM_AAL0) */
+
+ buflen = ATM_CELL_PAYLOAD; /* i.e., 48 bytes */
+ flags = NS_TBD_AAL0;
+ scqe.word_2 = cpu_to_le32(NS_PRV_DMA(skb) + NS_AAL0_HEADER);
+ scqe.word_3 = cpu_to_le32(0x00000000);
+ if (*skb->data & 0x02) /* Payload type 1 - end of pdu */
+ flags |= NS_TBD_EOPDU;
+ scqe.word_4 =
+ cpu_to_le32(*((u32 *) skb->data) & ~NS_TBD_VC_MASK);
+ /* Force the VPI/VCI to be the same as in VCC struct */
+ scqe.word_4 |=
+ cpu_to_le32((((u32) vcc->
+ vpi) << NS_TBD_VPI_SHIFT | ((u32) vcc->
+ vci) <<
+ NS_TBD_VCI_SHIFT) & NS_TBD_VC_MASK);
+ }
+
+ if (vcc->qos.txtp.traffic_class == ATM_CBR) {
+ scqe.word_1 = ns_tbd_mkword_1_novbr(flags, (u32) buflen);
+ scq = ((vc_map *) vcc->dev_data)->scq;
+ } else {
+ scqe.word_1 =
+ ns_tbd_mkword_1(flags, (u32) 1, (u32) 1, (u32) buflen);
+ scq = card->scq0;
+ }
+
+ if (push_scqe(card, vc, scq, &scqe, skb) != 0) {
+ atomic_inc(&vcc->stats->tx_err);
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+ atomic_inc(&vcc->stats->tx);
+
+ return 0;
+}
+
+static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
+ struct sk_buff *skb)
+{
+ unsigned long flags;
+ ns_scqe tsr;
+ u32 scdi, scqi;
+ int scq_is_vbr;
+ u32 data;
+ int index;
+
+ spin_lock_irqsave(&scq->lock, flags);
+ while (scq->tail == scq->next) {
+ if (in_interrupt()) {
+ spin_unlock_irqrestore(&scq->lock, flags);
+ printk("nicstar%d: Error pushing TBD.\n", card->index);
+ return 1;
+ }
+
+ scq->full = 1;
+ wait_event_interruptible_lock_irq_timeout(scq->scqfull_waitq,
+ scq->tail != scq->next,
+ scq->lock,
+ SCQFULL_TIMEOUT);
+
+ if (scq->full) {
+ spin_unlock_irqrestore(&scq->lock, flags);
+ printk("nicstar%d: Timeout pushing TBD.\n",
+ card->index);
+ return 1;
+ }
+ }
+ *scq->next = *tbd;
+ index = (int)(scq->next - scq->base);
+ scq->skb[index] = skb;
+ XPRINTK("nicstar%d: sending skb at 0x%p (pos %d).\n",
+ card->index, skb, index);
+ XPRINTK("nicstar%d: TBD written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%p.\n",
+ card->index, le32_to_cpu(tbd->word_1), le32_to_cpu(tbd->word_2),
+ le32_to_cpu(tbd->word_3), le32_to_cpu(tbd->word_4),
+ scq->next);
+ if (scq->next == scq->last)
+ scq->next = scq->base;
+ else
+ scq->next++;
+
+ vc->tbd_count++;
+ if (scq->num_entries == VBR_SCQ_NUM_ENTRIES) {
+ scq->tbd_count++;
+ scq_is_vbr = 1;
+ } else
+ scq_is_vbr = 0;
+
+ if (vc->tbd_count >= MAX_TBD_PER_VC
+ || scq->tbd_count >= MAX_TBD_PER_SCQ) {
+ int has_run = 0;
+
+ while (scq->tail == scq->next) {
+ if (in_interrupt()) {
+ data = scq_virt_to_bus(scq, scq->next);
+ ns_write_sram(card, scq->scd, &data, 1);
+ spin_unlock_irqrestore(&scq->lock, flags);
+ printk("nicstar%d: Error pushing TSR.\n",
+ card->index);
+ return 0;
+ }
+
+ scq->full = 1;
+ if (has_run++)
+ break;
+ wait_event_interruptible_lock_irq_timeout(scq->scqfull_waitq,
+ scq->tail != scq->next,
+ scq->lock,
+ SCQFULL_TIMEOUT);
+ }
+
+ if (!scq->full) {
+ tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE);
+ if (scq_is_vbr)
+ scdi = NS_TSR_SCDISVBR;
+ else
+ scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE;
+ scqi = scq->next - scq->base;
+ tsr.word_2 = ns_tsr_mkword_2(scdi, scqi);
+ tsr.word_3 = 0x00000000;
+ tsr.word_4 = 0x00000000;
+
+ *scq->next = tsr;
+ index = (int)scqi;
+ scq->skb[index] = NULL;
+ XPRINTK
+ ("nicstar%d: TSR written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%p.\n",
+ card->index, le32_to_cpu(tsr.word_1),
+ le32_to_cpu(tsr.word_2), le32_to_cpu(tsr.word_3),
+ le32_to_cpu(tsr.word_4), scq->next);
+ if (scq->next == scq->last)
+ scq->next = scq->base;
+ else
+ scq->next++;
+ vc->tbd_count = 0;
+ scq->tbd_count = 0;
+ } else
+ PRINTK("nicstar%d: Timeout pushing TSR.\n",
+ card->index);
+ }
+ data = scq_virt_to_bus(scq, scq->next);
+ ns_write_sram(card, scq->scd, &data, 1);
+
+ spin_unlock_irqrestore(&scq->lock, flags);
+
+ return 0;
+}
+
+static void process_tsq(ns_dev * card)
+{
+ u32 scdi;
+ scq_info *scq;
+ ns_tsi *previous = NULL, *one_ahead, *two_ahead;
+ int serviced_entries; /* flag indicating at least on entry was serviced */
+
+ serviced_entries = 0;
+
+ if (card->tsq.next == card->tsq.last)
+ one_ahead = card->tsq.base;
+ else
+ one_ahead = card->tsq.next + 1;
+
+ if (one_ahead == card->tsq.last)
+ two_ahead = card->tsq.base;
+ else
+ two_ahead = one_ahead + 1;
+
+ while (!ns_tsi_isempty(card->tsq.next) || !ns_tsi_isempty(one_ahead) ||
+ !ns_tsi_isempty(two_ahead))
+ /* At most two empty, as stated in the 77201 errata */
+ {
+ serviced_entries = 1;
+
+ /* Skip the one or two possible empty entries */
+ while (ns_tsi_isempty(card->tsq.next)) {
+ if (card->tsq.next == card->tsq.last)
+ card->tsq.next = card->tsq.base;
+ else
+ card->tsq.next++;
+ }
+
+ if (!ns_tsi_tmrof(card->tsq.next)) {
+ scdi = ns_tsi_getscdindex(card->tsq.next);
+ if (scdi == NS_TSI_SCDISVBR)
+ scq = card->scq0;
+ else {
+ if (card->scd2vc[scdi] == NULL) {
+ printk
+ ("nicstar%d: could not find VC from SCD index.\n",
+ card->index);
+ ns_tsi_init(card->tsq.next);
+ return;
+ }
+ scq = card->scd2vc[scdi]->scq;
+ }
+ drain_scq(card, scq, ns_tsi_getscqpos(card->tsq.next));
+ scq->full = 0;
+ wake_up_interruptible(&(scq->scqfull_waitq));
+ }
+
+ ns_tsi_init(card->tsq.next);
+ previous = card->tsq.next;
+ if (card->tsq.next == card->tsq.last)
+ card->tsq.next = card->tsq.base;
+ else
+ card->tsq.next++;
+
+ if (card->tsq.next == card->tsq.last)
+ one_ahead = card->tsq.base;
+ else
+ one_ahead = card->tsq.next + 1;
+
+ if (one_ahead == card->tsq.last)
+ two_ahead = card->tsq.base;
+ else
+ two_ahead = one_ahead + 1;
+ }
+
+ if (serviced_entries)
+ writel(PTR_DIFF(previous, card->tsq.base),
+ card->membase + TSQH);
+}
+
+static void drain_scq(ns_dev * card, scq_info * scq, int pos)
+{
+ struct atm_vcc *vcc;
+ struct sk_buff *skb;
+ int i;
+ unsigned long flags;
+
+ XPRINTK("nicstar%d: drain_scq() called, scq at 0x%p, pos %d.\n",
+ card->index, scq, pos);
+ if (pos >= scq->num_entries) {
+ printk("nicstar%d: Bad index on drain_scq().\n", card->index);
+ return;
+ }
+
+ spin_lock_irqsave(&scq->lock, flags);
+ i = (int)(scq->tail - scq->base);
+ if (++i == scq->num_entries)
+ i = 0;
+ while (i != pos) {
+ skb = scq->skb[i];
+ XPRINTK("nicstar%d: freeing skb at 0x%p (index %d).\n",
+ card->index, skb, i);
+ if (skb != NULL) {
+ dma_unmap_single(&card->pcidev->dev,
+ NS_PRV_DMA(skb),
+ skb->len,
+ DMA_TO_DEVICE);
+ vcc = ATM_SKB(skb)->vcc;
+ if (vcc && vcc->pop != NULL) {
+ vcc->pop(vcc, skb);
+ } else {
+ dev_kfree_skb_irq(skb);
+ }
+ scq->skb[i] = NULL;
+ }
+ if (++i == scq->num_entries)
+ i = 0;
+ }
+ scq->tail = scq->base + pos;
+ spin_unlock_irqrestore(&scq->lock, flags);
+}
+
+static void process_rsq(ns_dev * card)
+{
+ ns_rsqe *previous;
+
+ if (!ns_rsqe_valid(card->rsq.next))
+ return;
+ do {
+ dequeue_rx(card, card->rsq.next);
+ ns_rsqe_init(card->rsq.next);
+ previous = card->rsq.next;
+ if (card->rsq.next == card->rsq.last)
+ card->rsq.next = card->rsq.base;
+ else
+ card->rsq.next++;
+ } while (ns_rsqe_valid(card->rsq.next));
+ writel(PTR_DIFF(previous, card->rsq.base), card->membase + RSQH);
+}
+
+static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe)
+{
+ u32 vpi, vci;
+ vc_map *vc;
+ struct sk_buff *iovb;
+ struct iovec *iov;
+ struct atm_vcc *vcc;
+ struct sk_buff *skb;
+ unsigned short aal5_len;
+ int len;
+ u32 stat;
+ u32 id;
+
+ stat = readl(card->membase + STAT);
+ card->sbfqc = ns_stat_sfbqc_get(stat);
+ card->lbfqc = ns_stat_lfbqc_get(stat);
+
+ id = le32_to_cpu(rsqe->buffer_handle);
+ skb = idr_find(&card->idr, id);
+ if (!skb) {
+ RXPRINTK(KERN_ERR
+ "nicstar%d: idr_find() failed!\n", card->index);
+ return;
+ }
+ idr_remove(&card->idr, id);
+ dma_sync_single_for_cpu(&card->pcidev->dev,
+ NS_PRV_DMA(skb),
+ (NS_PRV_BUFTYPE(skb) == BUF_SM
+ ? NS_SMSKBSIZE : NS_LGSKBSIZE),
+ DMA_FROM_DEVICE);
+ dma_unmap_single(&card->pcidev->dev,
+ NS_PRV_DMA(skb),
+ (NS_PRV_BUFTYPE(skb) == BUF_SM
+ ? NS_SMSKBSIZE : NS_LGSKBSIZE),
+ DMA_FROM_DEVICE);
+ vpi = ns_rsqe_vpi(rsqe);
+ vci = ns_rsqe_vci(rsqe);
+ if (vpi >= 1UL << card->vpibits || vci >= 1UL << card->vcibits) {
+ printk("nicstar%d: SDU received for out-of-range vc %d.%d.\n",
+ card->index, vpi, vci);
+ recycle_rx_buf(card, skb);
+ return;
+ }
+
+ vc = &(card->vcmap[vpi << card->vcibits | vci]);
+ if (!vc->rx) {
+ RXPRINTK("nicstar%d: SDU received on non-rx vc %d.%d.\n",
+ card->index, vpi, vci);
+ recycle_rx_buf(card, skb);
+ return;
+ }
+
+ vcc = vc->rx_vcc;
+
+ if (vcc->qos.aal == ATM_AAL0) {
+ struct sk_buff *sb;
+ unsigned char *cell;
+ int i;
+
+ cell = skb->data;
+ for (i = ns_rsqe_cellcount(rsqe); i; i--) {
+ if ((sb = dev_alloc_skb(NS_SMSKBSIZE)) == NULL) {
+ printk
+ ("nicstar%d: Can't allocate buffers for aal0.\n",
+ card->index);
+ atomic_add(i, &vcc->stats->rx_drop);
+ break;
+ }
+ if (!atm_charge(vcc, sb->truesize)) {
+ RXPRINTK
+ ("nicstar%d: atm_charge() dropped aal0 packets.\n",
+ card->index);
+ atomic_add(i - 1, &vcc->stats->rx_drop); /* already increased by 1 */
+ dev_kfree_skb_any(sb);
+ break;
+ }
+ /* Rebuild the header */
+ *((u32 *) sb->data) = le32_to_cpu(rsqe->word_1) << 4 |
+ (ns_rsqe_clp(rsqe) ? 0x00000001 : 0x00000000);
+ if (i == 1 && ns_rsqe_eopdu(rsqe))
+ *((u32 *) sb->data) |= 0x00000002;
+ skb_put(sb, NS_AAL0_HEADER);
+ memcpy(skb_tail_pointer(sb), cell, ATM_CELL_PAYLOAD);
+ skb_put(sb, ATM_CELL_PAYLOAD);
+ ATM_SKB(sb)->vcc = vcc;
+ __net_timestamp(sb);
+ vcc->push(vcc, sb);
+ atomic_inc(&vcc->stats->rx);
+ cell += ATM_CELL_PAYLOAD;
+ }
+
+ recycle_rx_buf(card, skb);
+ return;
+ }
+
+ /* To reach this point, the AAL layer can only be AAL5 */
+
+ if ((iovb = vc->rx_iov) == NULL) {
+ iovb = skb_dequeue(&(card->iovpool.queue));
+ if (iovb == NULL) { /* No buffers in the queue */
+ iovb = alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC);
+ if (iovb == NULL) {
+ printk("nicstar%d: Out of iovec buffers.\n",
+ card->index);
+ atomic_inc(&vcc->stats->rx_drop);
+ recycle_rx_buf(card, skb);
+ return;
+ }
+ NS_PRV_BUFTYPE(iovb) = BUF_NONE;
+ } else if (--card->iovpool.count < card->iovnr.min) {
+ struct sk_buff *new_iovb;
+ if ((new_iovb =
+ alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC)) != NULL) {
+ NS_PRV_BUFTYPE(iovb) = BUF_NONE;
+ skb_queue_tail(&card->iovpool.queue, new_iovb);
+ card->iovpool.count++;
+ }
+ }
+ vc->rx_iov = iovb;
+ NS_PRV_IOVCNT(iovb) = 0;
+ iovb->len = 0;
+ iovb->data = iovb->head;
+ skb_reset_tail_pointer(iovb);
+ /* IMPORTANT: a pointer to the sk_buff containing the small or large
+ buffer is stored as iovec base, NOT a pointer to the
+ small or large buffer itself. */
+ } else if (NS_PRV_IOVCNT(iovb) >= NS_MAX_IOVECS) {
+ printk("nicstar%d: received too big AAL5 SDU.\n", card->index);
+ atomic_inc(&vcc->stats->rx_err);
+ recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
+ NS_MAX_IOVECS);
+ NS_PRV_IOVCNT(iovb) = 0;
+ iovb->len = 0;
+ iovb->data = iovb->head;
+ skb_reset_tail_pointer(iovb);
+ }
+ iov = &((struct iovec *)iovb->data)[NS_PRV_IOVCNT(iovb)++];
+ iov->iov_base = (void *)skb;
+ iov->iov_len = ns_rsqe_cellcount(rsqe) * 48;
+ iovb->len += iov->iov_len;
+
+#ifdef EXTRA_DEBUG
+ if (NS_PRV_IOVCNT(iovb) == 1) {
+ if (NS_PRV_BUFTYPE(skb) != BUF_SM) {
+ printk
+ ("nicstar%d: Expected a small buffer, and this is not one.\n",
+ card->index);
+ which_list(card, skb);
+ atomic_inc(&vcc->stats->rx_err);
+ recycle_rx_buf(card, skb);
+ vc->rx_iov = NULL;
+ recycle_iov_buf(card, iovb);
+ return;
+ }
+ } else { /* NS_PRV_IOVCNT(iovb) >= 2 */
+
+ if (NS_PRV_BUFTYPE(skb) != BUF_LG) {
+ printk
+ ("nicstar%d: Expected a large buffer, and this is not one.\n",
+ card->index);
+ which_list(card, skb);
+ atomic_inc(&vcc->stats->rx_err);
+ recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
+ NS_PRV_IOVCNT(iovb));
+ vc->rx_iov = NULL;
+ recycle_iov_buf(card, iovb);
+ return;
+ }
+ }
+#endif /* EXTRA_DEBUG */
+
+ if (ns_rsqe_eopdu(rsqe)) {
+ /* This works correctly regardless of the endianness of the host */
+ unsigned char *L1L2 = (unsigned char *)
+ (skb->data + iov->iov_len - 6);
+ aal5_len = L1L2[0] << 8 | L1L2[1];
+ len = (aal5_len == 0x0000) ? 0x10000 : aal5_len;
+ if (ns_rsqe_crcerr(rsqe) ||
+ len + 8 > iovb->len || len + (47 + 8) < iovb->len) {
+ printk("nicstar%d: AAL5 CRC error", card->index);
+ if (len + 8 > iovb->len || len + (47 + 8) < iovb->len)
+ printk(" - PDU size mismatch.\n");
+ else
+ printk(".\n");
+ atomic_inc(&vcc->stats->rx_err);
+ recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
+ NS_PRV_IOVCNT(iovb));
+ vc->rx_iov = NULL;
+ recycle_iov_buf(card, iovb);
+ return;
+ }
+
+ /* By this point we (hopefully) have a complete SDU without errors. */
+
+ if (NS_PRV_IOVCNT(iovb) == 1) { /* Just a small buffer */
+ /* skb points to a small buffer */
+ if (!atm_charge(vcc, skb->truesize)) {
+ push_rxbufs(card, skb);
+ atomic_inc(&vcc->stats->rx_drop);
+ } else {
+ skb_put(skb, len);
+ dequeue_sm_buf(card, skb);
+ ATM_SKB(skb)->vcc = vcc;
+ __net_timestamp(skb);
+ vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
+ }
+ } else if (NS_PRV_IOVCNT(iovb) == 2) { /* One small plus one large buffer */
+ struct sk_buff *sb;
+
+ sb = (struct sk_buff *)(iov - 1)->iov_base;
+ /* skb points to a large buffer */
+
+ if (len <= NS_SMBUFSIZE) {
+ if (!atm_charge(vcc, sb->truesize)) {
+ push_rxbufs(card, sb);
+ atomic_inc(&vcc->stats->rx_drop);
+ } else {
+ skb_put(sb, len);
+ dequeue_sm_buf(card, sb);
+ ATM_SKB(sb)->vcc = vcc;
+ __net_timestamp(sb);
+ vcc->push(vcc, sb);
+ atomic_inc(&vcc->stats->rx);
+ }
+
+ push_rxbufs(card, skb);
+
+ } else { /* len > NS_SMBUFSIZE, the usual case */
+
+ if (!atm_charge(vcc, skb->truesize)) {
+ push_rxbufs(card, skb);
+ atomic_inc(&vcc->stats->rx_drop);
+ } else {
+ dequeue_lg_buf(card, skb);
+ skb_push(skb, NS_SMBUFSIZE);
+ skb_copy_from_linear_data(sb, skb->data,
+ NS_SMBUFSIZE);
+ skb_put(skb, len - NS_SMBUFSIZE);
+ ATM_SKB(skb)->vcc = vcc;
+ __net_timestamp(skb);
+ vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
+ }
+
+ push_rxbufs(card, sb);
+
+ }
+
+ } else { /* Must push a huge buffer */
+
+ struct sk_buff *hb, *sb, *lb;
+ int remaining, tocopy;
+ int j;
+
+ hb = skb_dequeue(&(card->hbpool.queue));
+ if (hb == NULL) { /* No buffers in the queue */
+
+ hb = dev_alloc_skb(NS_HBUFSIZE);
+ if (hb == NULL) {
+ printk
+ ("nicstar%d: Out of huge buffers.\n",
+ card->index);
+ atomic_inc(&vcc->stats->rx_drop);
+ recycle_iovec_rx_bufs(card,
+ (struct iovec *)
+ iovb->data,
+ NS_PRV_IOVCNT(iovb));
+ vc->rx_iov = NULL;
+ recycle_iov_buf(card, iovb);
+ return;
+ } else if (card->hbpool.count < card->hbnr.min) {
+ struct sk_buff *new_hb;
+ if ((new_hb =
+ dev_alloc_skb(NS_HBUFSIZE)) !=
+ NULL) {
+ skb_queue_tail(&card->hbpool.
+ queue, new_hb);
+ card->hbpool.count++;
+ }
+ }
+ NS_PRV_BUFTYPE(hb) = BUF_NONE;
+ } else if (--card->hbpool.count < card->hbnr.min) {
+ struct sk_buff *new_hb;
+ if ((new_hb =
+ dev_alloc_skb(NS_HBUFSIZE)) != NULL) {
+ NS_PRV_BUFTYPE(new_hb) = BUF_NONE;
+ skb_queue_tail(&card->hbpool.queue,
+ new_hb);
+ card->hbpool.count++;
+ }
+ if (card->hbpool.count < card->hbnr.min) {
+ if ((new_hb =
+ dev_alloc_skb(NS_HBUFSIZE)) !=
+ NULL) {
+ NS_PRV_BUFTYPE(new_hb) =
+ BUF_NONE;
+ skb_queue_tail(&card->hbpool.
+ queue, new_hb);
+ card->hbpool.count++;
+ }
+ }
+ }
+
+ iov = (struct iovec *)iovb->data;
+
+ if (!atm_charge(vcc, hb->truesize)) {
+ recycle_iovec_rx_bufs(card, iov,
+ NS_PRV_IOVCNT(iovb));
+ if (card->hbpool.count < card->hbnr.max) {
+ skb_queue_tail(&card->hbpool.queue, hb);
+ card->hbpool.count++;
+ } else
+ dev_kfree_skb_any(hb);
+ atomic_inc(&vcc->stats->rx_drop);
+ } else {
+ /* Copy the small buffer to the huge buffer */
+ sb = (struct sk_buff *)iov->iov_base;
+ skb_copy_from_linear_data(sb, hb->data,
+ iov->iov_len);
+ skb_put(hb, iov->iov_len);
+ remaining = len - iov->iov_len;
+ iov++;
+ /* Free the small buffer */
+ push_rxbufs(card, sb);
+
+ /* Copy all large buffers to the huge buffer and free them */
+ for (j = 1; j < NS_PRV_IOVCNT(iovb); j++) {
+ lb = (struct sk_buff *)iov->iov_base;
+ tocopy =
+ min_t(int, remaining, iov->iov_len);
+ skb_copy_from_linear_data(lb,
+ skb_tail_pointer
+ (hb), tocopy);
+ skb_put(hb, tocopy);
+ iov++;
+ remaining -= tocopy;
+ push_rxbufs(card, lb);
+ }
+#ifdef EXTRA_DEBUG
+ if (remaining != 0 || hb->len != len)
+ printk
+ ("nicstar%d: Huge buffer len mismatch.\n",
+ card->index);
+#endif /* EXTRA_DEBUG */
+ ATM_SKB(hb)->vcc = vcc;
+ __net_timestamp(hb);
+ vcc->push(vcc, hb);
+ atomic_inc(&vcc->stats->rx);
+ }
+ }
+
+ vc->rx_iov = NULL;
+ recycle_iov_buf(card, iovb);
+ }
+
+}
+
+static void recycle_rx_buf(ns_dev * card, struct sk_buff *skb)
+{
+ if (unlikely(NS_PRV_BUFTYPE(skb) == BUF_NONE)) {
+ printk("nicstar%d: What kind of rx buffer is this?\n",
+ card->index);
+ dev_kfree_skb_any(skb);
+ } else
+ push_rxbufs(card, skb);
+}
+
+static void recycle_iovec_rx_bufs(ns_dev * card, struct iovec *iov, int count)
+{
+ while (count-- > 0)
+ recycle_rx_buf(card, (struct sk_buff *)(iov++)->iov_base);
+}
+
+static void recycle_iov_buf(ns_dev * card, struct sk_buff *iovb)
+{
+ if (card->iovpool.count < card->iovnr.max) {
+ skb_queue_tail(&card->iovpool.queue, iovb);
+ card->iovpool.count++;
+ } else
+ dev_kfree_skb_any(iovb);
+}
+
+static void dequeue_sm_buf(ns_dev * card, struct sk_buff *sb)
+{
+ skb_unlink(sb, &card->sbpool.queue);
+ if (card->sbfqc < card->sbnr.init) {
+ struct sk_buff *new_sb;
+ if ((new_sb = dev_alloc_skb(NS_SMSKBSIZE)) != NULL) {
+ NS_PRV_BUFTYPE(new_sb) = BUF_SM;
+ skb_queue_tail(&card->sbpool.queue, new_sb);
+ skb_reserve(new_sb, NS_AAL0_HEADER);
+ push_rxbufs(card, new_sb);
+ }
+ }
+ if (card->sbfqc < card->sbnr.init)
+ {
+ struct sk_buff *new_sb;
+ if ((new_sb = dev_alloc_skb(NS_SMSKBSIZE)) != NULL) {
+ NS_PRV_BUFTYPE(new_sb) = BUF_SM;
+ skb_queue_tail(&card->sbpool.queue, new_sb);
+ skb_reserve(new_sb, NS_AAL0_HEADER);
+ push_rxbufs(card, new_sb);
+ }
+ }
+}
+
+static void dequeue_lg_buf(ns_dev * card, struct sk_buff *lb)
+{
+ skb_unlink(lb, &card->lbpool.queue);
+ if (card->lbfqc < card->lbnr.init) {
+ struct sk_buff *new_lb;
+ if ((new_lb = dev_alloc_skb(NS_LGSKBSIZE)) != NULL) {
+ NS_PRV_BUFTYPE(new_lb) = BUF_LG;
+ skb_queue_tail(&card->lbpool.queue, new_lb);
+ skb_reserve(new_lb, NS_SMBUFSIZE);
+ push_rxbufs(card, new_lb);
+ }
+ }
+ if (card->lbfqc < card->lbnr.init)
+ {
+ struct sk_buff *new_lb;
+ if ((new_lb = dev_alloc_skb(NS_LGSKBSIZE)) != NULL) {
+ NS_PRV_BUFTYPE(new_lb) = BUF_LG;
+ skb_queue_tail(&card->lbpool.queue, new_lb);
+ skb_reserve(new_lb, NS_SMBUFSIZE);
+ push_rxbufs(card, new_lb);
+ }
+ }
+}
+
+static int ns_proc_read(struct atm_dev *dev, loff_t * pos, char *page)
+{
+ u32 stat;
+ ns_dev *card;
+ int left;
+
+ left = (int)*pos;
+ card = (ns_dev *) dev->dev_data;
+ stat = readl(card->membase + STAT);
+ if (!left--)
+ return sprintf(page, "Pool count min init max \n");
+ if (!left--)
+ return sprintf(page, "Small %5d %5d %5d %5d \n",
+ ns_stat_sfbqc_get(stat), card->sbnr.min,
+ card->sbnr.init, card->sbnr.max);
+ if (!left--)
+ return sprintf(page, "Large %5d %5d %5d %5d \n",
+ ns_stat_lfbqc_get(stat), card->lbnr.min,
+ card->lbnr.init, card->lbnr.max);
+ if (!left--)
+ return sprintf(page, "Huge %5d %5d %5d %5d \n",
+ card->hbpool.count, card->hbnr.min,
+ card->hbnr.init, card->hbnr.max);
+ if (!left--)
+ return sprintf(page, "Iovec %5d %5d %5d %5d \n",
+ card->iovpool.count, card->iovnr.min,
+ card->iovnr.init, card->iovnr.max);
+ if (!left--) {
+ int retval;
+ retval =
+ sprintf(page, "Interrupt counter: %u \n", card->intcnt);
+ card->intcnt = 0;
+ return retval;
+ }
+#if 0
+ /* Dump 25.6 Mbps PHY registers */
+ /* Now there's a 25.6 Mbps PHY driver this code isn't needed. I left it
+ here just in case it's needed for debugging. */
+ if (card->max_pcr == ATM_25_PCR && !left--) {
+ u32 phy_regs[4];
+ u32 i;
+
+ for (i = 0; i < 4; i++) {
+ while (CMD_BUSY(card)) ;
+ writel(NS_CMD_READ_UTILITY | 0x00000200 | i,
+ card->membase + CMD);
+ while (CMD_BUSY(card)) ;
+ phy_regs[i] = readl(card->membase + DR0) & 0x000000FF;
+ }
+
+ return sprintf(page, "PHY regs: 0x%02X 0x%02X 0x%02X 0x%02X \n",
+ phy_regs[0], phy_regs[1], phy_regs[2],
+ phy_regs[3]);
+ }
+#endif /* 0 - Dump 25.6 Mbps PHY registers */
+#if 0
+ /* Dump TST */
+ if (left-- < NS_TST_NUM_ENTRIES) {
+ if (card->tste2vc[left + 1] == NULL)
+ return sprintf(page, "%5d - VBR/UBR \n", left + 1);
+ else
+ return sprintf(page, "%5d - %d %d \n", left + 1,
+ card->tste2vc[left + 1]->tx_vcc->vpi,
+ card->tste2vc[left + 1]->tx_vcc->vci);
+ }
+#endif /* 0 */
+ return 0;
+}
+
+static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg)
+{
+ ns_dev *card;
+ pool_levels pl;
+ long btype;
+ unsigned long flags;
+
+ card = dev->dev_data;
+ switch (cmd) {
+ case NS_GETPSTAT:
+ if (get_user
+ (pl.buftype, &((pool_levels __user *) arg)->buftype))
+ return -EFAULT;
+ switch (pl.buftype) {
+ case NS_BUFTYPE_SMALL:
+ pl.count =
+ ns_stat_sfbqc_get(readl(card->membase + STAT));
+ pl.level.min = card->sbnr.min;
+ pl.level.init = card->sbnr.init;
+ pl.level.max = card->sbnr.max;
+ break;
+
+ case NS_BUFTYPE_LARGE:
+ pl.count =
+ ns_stat_lfbqc_get(readl(card->membase + STAT));
+ pl.level.min = card->lbnr.min;
+ pl.level.init = card->lbnr.init;
+ pl.level.max = card->lbnr.max;
+ break;
+
+ case NS_BUFTYPE_HUGE:
+ pl.count = card->hbpool.count;
+ pl.level.min = card->hbnr.min;
+ pl.level.init = card->hbnr.init;
+ pl.level.max = card->hbnr.max;
+ break;
+
+ case NS_BUFTYPE_IOVEC:
+ pl.count = card->iovpool.count;
+ pl.level.min = card->iovnr.min;
+ pl.level.init = card->iovnr.init;
+ pl.level.max = card->iovnr.max;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+
+ }
+ if (!copy_to_user((pool_levels __user *) arg, &pl, sizeof(pl)))
+ return (sizeof(pl));
+ else
+ return -EFAULT;
+
+ case NS_SETBUFLEV:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (copy_from_user(&pl, (pool_levels __user *) arg, sizeof(pl)))
+ return -EFAULT;
+ if (pl.level.min >= pl.level.init
+ || pl.level.init >= pl.level.max)
+ return -EINVAL;
+ if (pl.level.min == 0)
+ return -EINVAL;
+ switch (pl.buftype) {
+ case NS_BUFTYPE_SMALL:
+ if (pl.level.max > TOP_SB)
+ return -EINVAL;
+ card->sbnr.min = pl.level.min;
+ card->sbnr.init = pl.level.init;
+ card->sbnr.max = pl.level.max;
+ break;
+
+ case NS_BUFTYPE_LARGE:
+ if (pl.level.max > TOP_LB)
+ return -EINVAL;
+ card->lbnr.min = pl.level.min;
+ card->lbnr.init = pl.level.init;
+ card->lbnr.max = pl.level.max;
+ break;
+
+ case NS_BUFTYPE_HUGE:
+ if (pl.level.max > TOP_HB)
+ return -EINVAL;
+ card->hbnr.min = pl.level.min;
+ card->hbnr.init = pl.level.init;
+ card->hbnr.max = pl.level.max;
+ break;
+
+ case NS_BUFTYPE_IOVEC:
+ if (pl.level.max > TOP_IOVB)
+ return -EINVAL;
+ card->iovnr.min = pl.level.min;
+ card->iovnr.init = pl.level.init;
+ card->iovnr.max = pl.level.max;
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ return 0;
+
+ case NS_ADJBUFLEV:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ btype = (long)arg; /* a long is the same size as a pointer or bigger */
+ switch (btype) {
+ case NS_BUFTYPE_SMALL:
+ while (card->sbfqc < card->sbnr.init) {
+ struct sk_buff *sb;
+
+ sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL);
+ if (sb == NULL)
+ return -ENOMEM;
+ NS_PRV_BUFTYPE(sb) = BUF_SM;
+ skb_queue_tail(&card->sbpool.queue, sb);
+ skb_reserve(sb, NS_AAL0_HEADER);
+ push_rxbufs(card, sb);
+ }
+ break;
+
+ case NS_BUFTYPE_LARGE:
+ while (card->lbfqc < card->lbnr.init) {
+ struct sk_buff *lb;
+
+ lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL);
+ if (lb == NULL)
+ return -ENOMEM;
+ NS_PRV_BUFTYPE(lb) = BUF_LG;
+ skb_queue_tail(&card->lbpool.queue, lb);
+ skb_reserve(lb, NS_SMBUFSIZE);
+ push_rxbufs(card, lb);
+ }
+ break;
+
+ case NS_BUFTYPE_HUGE:
+ while (card->hbpool.count > card->hbnr.init) {
+ struct sk_buff *hb;
+
+ spin_lock_irqsave(&card->int_lock, flags);
+ hb = skb_dequeue(&card->hbpool.queue);
+ card->hbpool.count--;
+ spin_unlock_irqrestore(&card->int_lock, flags);
+ if (hb == NULL)
+ printk
+ ("nicstar%d: huge buffer count inconsistent.\n",
+ card->index);
+ else
+ dev_kfree_skb_any(hb);
+
+ }
+ while (card->hbpool.count < card->hbnr.init) {
+ struct sk_buff *hb;
+
+ hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL);
+ if (hb == NULL)
+ return -ENOMEM;
+ NS_PRV_BUFTYPE(hb) = BUF_NONE;
+ spin_lock_irqsave(&card->int_lock, flags);
+ skb_queue_tail(&card->hbpool.queue, hb);
+ card->hbpool.count++;
+ spin_unlock_irqrestore(&card->int_lock, flags);
+ }
+ break;
+
+ case NS_BUFTYPE_IOVEC:
+ while (card->iovpool.count > card->iovnr.init) {
+ struct sk_buff *iovb;
+
+ spin_lock_irqsave(&card->int_lock, flags);
+ iovb = skb_dequeue(&card->iovpool.queue);
+ card->iovpool.count--;
+ spin_unlock_irqrestore(&card->int_lock, flags);
+ if (iovb == NULL)
+ printk
+ ("nicstar%d: iovec buffer count inconsistent.\n",
+ card->index);
+ else
+ dev_kfree_skb_any(iovb);
+
+ }
+ while (card->iovpool.count < card->iovnr.init) {
+ struct sk_buff *iovb;
+
+ iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL);
+ if (iovb == NULL)
+ return -ENOMEM;
+ NS_PRV_BUFTYPE(iovb) = BUF_NONE;
+ spin_lock_irqsave(&card->int_lock, flags);
+ skb_queue_tail(&card->iovpool.queue, iovb);
+ card->iovpool.count++;
+ spin_unlock_irqrestore(&card->int_lock, flags);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ return 0;
+
+ default:
+ if (dev->phy && dev->phy->ioctl) {
+ return dev->phy->ioctl(dev, cmd, arg);
+ } else {
+ printk("nicstar%d: %s == NULL \n", card->index,
+ dev->phy ? "dev->phy->ioctl" : "dev->phy");
+ return -ENOIOCTLCMD;
+ }
+ }
+}
+
+#ifdef EXTRA_DEBUG
+static void which_list(ns_dev * card, struct sk_buff *skb)
+{
+ printk("skb buf_type: 0x%08x\n", NS_PRV_BUFTYPE(skb));
+}
+#endif /* EXTRA_DEBUG */
+
+static void ns_poll(unsigned long arg)
+{
+ int i;
+ ns_dev *card;
+ unsigned long flags;
+ u32 stat_r, stat_w;
+
+ PRINTK("nicstar: Entering ns_poll().\n");
+ for (i = 0; i < num_cards; i++) {
+ card = cards[i];
+ if (spin_is_locked(&card->int_lock)) {
+ /* Probably it isn't worth spinning */
+ continue;
+ }
+ spin_lock_irqsave(&card->int_lock, flags);
+
+ stat_w = 0;
+ stat_r = readl(card->membase + STAT);
+ if (stat_r & NS_STAT_TSIF)
+ stat_w |= NS_STAT_TSIF;
+ if (stat_r & NS_STAT_EOPDU)
+ stat_w |= NS_STAT_EOPDU;
+
+ process_tsq(card);
+ process_rsq(card);
+
+ writel(stat_w, card->membase + STAT);
+ spin_unlock_irqrestore(&card->int_lock, flags);
+ }
+ mod_timer(&ns_timer, jiffies + NS_POLL_PERIOD);
+ PRINTK("nicstar: Leaving ns_poll().\n");
+}
+
+static void ns_phy_put(struct atm_dev *dev, unsigned char value,
+ unsigned long addr)
+{
+ ns_dev *card;
+ unsigned long flags;
+
+ card = dev->dev_data;
+ spin_lock_irqsave(&card->res_lock, flags);
+ while (CMD_BUSY(card)) ;
+ writel((u32) value, card->membase + DR0);
+ writel(NS_CMD_WRITE_UTILITY | 0x00000200 | (addr & 0x000000FF),
+ card->membase + CMD);
+ spin_unlock_irqrestore(&card->res_lock, flags);
+}
+
+static unsigned char ns_phy_get(struct atm_dev *dev, unsigned long addr)
+{
+ ns_dev *card;
+ unsigned long flags;
+ u32 data;
+
+ card = dev->dev_data;
+ spin_lock_irqsave(&card->res_lock, flags);
+ while (CMD_BUSY(card)) ;
+ writel(NS_CMD_READ_UTILITY | 0x00000200 | (addr & 0x000000FF),
+ card->membase + CMD);
+ while (CMD_BUSY(card)) ;
+ data = readl(card->membase + DR0) & 0x000000FF;
+ spin_unlock_irqrestore(&card->res_lock, flags);
+ return (unsigned char)data;
+}
+
+module_init(nicstar_init);
+module_exit(nicstar_cleanup);
diff --git a/drivers/atm/nicstar.h b/drivers/atm/nicstar.h
new file mode 100644
index 000000000..9bc27ea50
--- /dev/null
+++ b/drivers/atm/nicstar.h
@@ -0,0 +1,758 @@
+/*
+ * nicstar.h
+ *
+ * Header file for the nicstar device driver.
+ *
+ * Author: Rui Prior (rprior@inescn.pt)
+ * PowerPC support by Jay Talbott (jay_talbott@mcg.mot.com) April 1999
+ *
+ * (C) INESC 1998
+ */
+
+#ifndef _LINUX_NICSTAR_H_
+#define _LINUX_NICSTAR_H_
+
+/* Includes */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/idr.h>
+#include <linux/uio.h>
+#include <linux/skbuff.h>
+#include <linux/atmdev.h>
+#include <linux/atm_nicstar.h>
+
+/* Options */
+
+#define NS_MAX_CARDS 4 /* Maximum number of NICStAR based cards
+ controlled by the device driver. Must
+ be <= 5 */
+
+#undef RCQ_SUPPORT /* Do not define this for now */
+
+#define NS_TST_NUM_ENTRIES 2340 /* + 1 for return */
+#define NS_TST_RESERVED 340 /* N. entries reserved for UBR/ABR/VBR */
+
+#define NS_SMBUFSIZE 48 /* 48, 96, 240 or 2048 */
+#define NS_LGBUFSIZE 16384 /* 2048, 4096, 8192 or 16384 */
+#define NS_RSQSIZE 8192 /* 2048, 4096 or 8192 */
+#define NS_VPIBITS 2 /* 0, 1, 2, or 8 */
+
+#define NS_MAX_RCTSIZE 4096 /* Number of entries. 4096 or 16384.
+ Define 4096 only if (all) your card(s)
+ have 32K x 32bit SRAM, in which case
+ setting this to 16384 will just waste a
+ lot of memory.
+ Setting this to 4096 for a card with
+ 128K x 32bit SRAM will limit the maximum
+ VCI. */
+
+ /*#define NS_PCI_LATENCY 64*//* Must be a multiple of 32 */
+
+ /* Number of buffers initially allocated */
+#define NUM_SB 32 /* Must be even */
+#define NUM_LB 24 /* Must be even */
+#define NUM_HB 8 /* Pre-allocated huge buffers */
+#define NUM_IOVB 48 /* Iovec buffers */
+
+ /* Lower level for count of buffers */
+#define MIN_SB 8 /* Must be even */
+#define MIN_LB 8 /* Must be even */
+#define MIN_HB 6
+#define MIN_IOVB 8
+
+ /* Upper level for count of buffers */
+#define MAX_SB 64 /* Must be even, <= 508 */
+#define MAX_LB 48 /* Must be even, <= 508 */
+#define MAX_HB 10
+#define MAX_IOVB 80
+
+ /* These are the absolute maximum allowed for the ioctl() */
+#define TOP_SB 256 /* Must be even, <= 508 */
+#define TOP_LB 128 /* Must be even, <= 508 */
+#define TOP_HB 64
+#define TOP_IOVB 256
+
+#define MAX_TBD_PER_VC 1 /* Number of TBDs before a TSR */
+#define MAX_TBD_PER_SCQ 10 /* Only meaningful for variable rate SCQs */
+
+#undef ENABLE_TSQFIE
+
+#define SCQFULL_TIMEOUT (5 * HZ)
+
+#define NS_POLL_PERIOD (HZ)
+
+#define PCR_TOLERANCE (1.0001)
+
+/* ESI stuff */
+
+#define NICSTAR_EPROM_MAC_ADDR_OFFSET 0x6C
+#define NICSTAR_EPROM_MAC_ADDR_OFFSET_ALT 0xF6
+
+/* #defines */
+
+#define NS_IOREMAP_SIZE 4096
+
+/*
+ * BUF_XX distinguish the Rx buffers depending on their (small/large) size.
+ * BUG_SM and BUG_LG are both used by the driver and the device.
+ * BUF_NONE is only used by the driver.
+ */
+#define BUF_SM 0x00000000 /* These two are used for push_rxbufs() */
+#define BUF_LG 0x00000001 /* CMD, Write_FreeBufQ, LBUF bit */
+#define BUF_NONE 0xffffffff /* Software only: */
+
+#define NS_HBUFSIZE 65568 /* Size of max. AAL5 PDU */
+#define NS_MAX_IOVECS (2 + (65568 - NS_SMBUFSIZE) / \
+ (NS_LGBUFSIZE - (NS_LGBUFSIZE % 48)))
+#define NS_IOVBUFSIZE (NS_MAX_IOVECS * (sizeof(struct iovec)))
+
+#define NS_SMBUFSIZE_USABLE (NS_SMBUFSIZE - NS_SMBUFSIZE % 48)
+#define NS_LGBUFSIZE_USABLE (NS_LGBUFSIZE - NS_LGBUFSIZE % 48)
+
+#define NS_AAL0_HEADER (ATM_AAL0_SDU - ATM_CELL_PAYLOAD) /* 4 bytes */
+
+#define NS_SMSKBSIZE (NS_SMBUFSIZE + NS_AAL0_HEADER)
+#define NS_LGSKBSIZE (NS_SMBUFSIZE + NS_LGBUFSIZE)
+
+/* NICStAR structures located in host memory */
+
+/*
+ * RSQ - Receive Status Queue
+ *
+ * Written by the NICStAR, read by the device driver.
+ */
+
+typedef struct ns_rsqe {
+ u32 word_1;
+ u32 buffer_handle;
+ u32 final_aal5_crc32;
+ u32 word_4;
+} ns_rsqe;
+
+#define ns_rsqe_vpi(ns_rsqep) \
+ ((le32_to_cpu((ns_rsqep)->word_1) & 0x00FF0000) >> 16)
+#define ns_rsqe_vci(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_1) & 0x0000FFFF)
+
+#define NS_RSQE_VALID 0x80000000
+#define NS_RSQE_NZGFC 0x00004000
+#define NS_RSQE_EOPDU 0x00002000
+#define NS_RSQE_BUFSIZE 0x00001000
+#define NS_RSQE_CONGESTION 0x00000800
+#define NS_RSQE_CLP 0x00000400
+#define NS_RSQE_CRCERR 0x00000200
+
+#define NS_RSQE_BUFSIZE_SM 0x00000000
+#define NS_RSQE_BUFSIZE_LG 0x00001000
+
+#define ns_rsqe_valid(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_VALID)
+#define ns_rsqe_nzgfc(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_NZGFC)
+#define ns_rsqe_eopdu(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_EOPDU)
+#define ns_rsqe_bufsize(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_BUFSIZE)
+#define ns_rsqe_congestion(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_CONGESTION)
+#define ns_rsqe_clp(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_CLP)
+#define ns_rsqe_crcerr(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_CRCERR)
+
+#define ns_rsqe_cellcount(ns_rsqep) \
+ (le32_to_cpu((ns_rsqep)->word_4) & 0x000001FF)
+#define ns_rsqe_init(ns_rsqep) \
+ ((ns_rsqep)->word_4 = cpu_to_le32(0x00000000))
+
+#define NS_RSQ_NUM_ENTRIES (NS_RSQSIZE / 16)
+#define NS_RSQ_ALIGNMENT NS_RSQSIZE
+
+/*
+ * RCQ - Raw Cell Queue
+ *
+ * Written by the NICStAR, read by the device driver.
+ */
+
+typedef struct cell_payload {
+ u32 word[12];
+} cell_payload;
+
+typedef struct ns_rcqe {
+ u32 word_1;
+ u32 word_2;
+ u32 word_3;
+ u32 word_4;
+ cell_payload payload;
+} ns_rcqe;
+
+#define NS_RCQE_SIZE 64 /* bytes */
+
+#define ns_rcqe_islast(ns_rcqep) \
+ (le32_to_cpu((ns_rcqep)->word_2) != 0x00000000)
+#define ns_rcqe_cellheader(ns_rcqep) \
+ (le32_to_cpu((ns_rcqep)->word_1))
+#define ns_rcqe_nextbufhandle(ns_rcqep) \
+ (le32_to_cpu((ns_rcqep)->word_2))
+
+/*
+ * SCQ - Segmentation Channel Queue
+ *
+ * Written by the device driver, read by the NICStAR.
+ */
+
+typedef struct ns_scqe {
+ u32 word_1;
+ u32 word_2;
+ u32 word_3;
+ u32 word_4;
+} ns_scqe;
+
+ /* NOTE: SCQ entries can be either a TBD (Transmit Buffer Descriptors)
+ or TSR (Transmit Status Requests) */
+
+#define NS_SCQE_TYPE_TBD 0x00000000
+#define NS_SCQE_TYPE_TSR 0x80000000
+
+#define NS_TBD_EOPDU 0x40000000
+#define NS_TBD_AAL0 0x00000000
+#define NS_TBD_AAL34 0x04000000
+#define NS_TBD_AAL5 0x08000000
+
+#define NS_TBD_VPI_MASK 0x0FF00000
+#define NS_TBD_VCI_MASK 0x000FFFF0
+#define NS_TBD_VC_MASK (NS_TBD_VPI_MASK | NS_TBD_VCI_MASK)
+
+#define NS_TBD_VPI_SHIFT 20
+#define NS_TBD_VCI_SHIFT 4
+
+#define ns_tbd_mkword_1(flags, m, n, buflen) \
+ (cpu_to_le32((flags) | (m) << 23 | (n) << 16 | (buflen)))
+#define ns_tbd_mkword_1_novbr(flags, buflen) \
+ (cpu_to_le32((flags) | (buflen) | 0x00810000))
+#define ns_tbd_mkword_3(control, pdulen) \
+ (cpu_to_le32((control) << 16 | (pdulen)))
+#define ns_tbd_mkword_4(gfc, vpi, vci, pt, clp) \
+ (cpu_to_le32((gfc) << 28 | (vpi) << 20 | (vci) << 4 | (pt) << 1 | (clp)))
+
+#define NS_TSR_INTENABLE 0x20000000
+
+#define NS_TSR_SCDISVBR 0xFFFF /* Use as scdi for VBR SCD */
+
+#define ns_tsr_mkword_1(flags) \
+ (cpu_to_le32(NS_SCQE_TYPE_TSR | (flags)))
+#define ns_tsr_mkword_2(scdi, scqi) \
+ (cpu_to_le32((scdi) << 16 | 0x00008000 | (scqi)))
+
+#define ns_scqe_is_tsr(ns_scqep) \
+ (le32_to_cpu((ns_scqep)->word_1) & NS_SCQE_TYPE_TSR)
+
+#define VBR_SCQ_NUM_ENTRIES 512
+#define VBR_SCQSIZE 8192
+#define CBR_SCQ_NUM_ENTRIES 64
+#define CBR_SCQSIZE 1024
+
+#define NS_SCQE_SIZE 16
+
+/*
+ * TSQ - Transmit Status Queue
+ *
+ * Written by the NICStAR, read by the device driver.
+ */
+
+typedef struct ns_tsi {
+ u32 word_1;
+ u32 word_2;
+} ns_tsi;
+
+ /* NOTE: The first word can be a status word copied from the TSR which
+ originated the TSI, or a timer overflow indicator. In this last
+ case, the value of the first word is all zeroes. */
+
+#define NS_TSI_EMPTY 0x80000000
+#define NS_TSI_TIMESTAMP_MASK 0x00FFFFFF
+
+#define ns_tsi_isempty(ns_tsip) \
+ (le32_to_cpu((ns_tsip)->word_2) & NS_TSI_EMPTY)
+#define ns_tsi_gettimestamp(ns_tsip) \
+ (le32_to_cpu((ns_tsip)->word_2) & NS_TSI_TIMESTAMP_MASK)
+
+#define ns_tsi_init(ns_tsip) \
+ ((ns_tsip)->word_2 = cpu_to_le32(NS_TSI_EMPTY))
+
+#define NS_TSQSIZE 8192
+#define NS_TSQ_NUM_ENTRIES 1024
+#define NS_TSQ_ALIGNMENT 8192
+
+#define NS_TSI_SCDISVBR NS_TSR_SCDISVBR
+
+#define ns_tsi_tmrof(ns_tsip) \
+ (le32_to_cpu((ns_tsip)->word_1) == 0x00000000)
+#define ns_tsi_getscdindex(ns_tsip) \
+ ((le32_to_cpu((ns_tsip)->word_1) & 0xFFFF0000) >> 16)
+#define ns_tsi_getscqpos(ns_tsip) \
+ (le32_to_cpu((ns_tsip)->word_1) & 0x00007FFF)
+
+/* NICStAR structures located in local SRAM */
+
+/*
+ * RCT - Receive Connection Table
+ *
+ * Written by both the NICStAR and the device driver.
+ */
+
+typedef struct ns_rcte {
+ u32 word_1;
+ u32 buffer_handle;
+ u32 dma_address;
+ u32 aal5_crc32;
+} ns_rcte;
+
+#define NS_RCTE_BSFB 0x00200000 /* Rev. D only */
+#define NS_RCTE_NZGFC 0x00100000
+#define NS_RCTE_CONNECTOPEN 0x00080000
+#define NS_RCTE_AALMASK 0x00070000
+#define NS_RCTE_AAL0 0x00000000
+#define NS_RCTE_AAL34 0x00010000
+#define NS_RCTE_AAL5 0x00020000
+#define NS_RCTE_RCQ 0x00030000
+#define NS_RCTE_RAWCELLINTEN 0x00008000
+#define NS_RCTE_RXCONSTCELLADDR 0x00004000
+#define NS_RCTE_BUFFVALID 0x00002000
+#define NS_RCTE_FBDSIZE 0x00001000
+#define NS_RCTE_EFCI 0x00000800
+#define NS_RCTE_CLP 0x00000400
+#define NS_RCTE_CRCERROR 0x00000200
+#define NS_RCTE_CELLCOUNT_MASK 0x000001FF
+
+#define NS_RCTE_FBDSIZE_SM 0x00000000
+#define NS_RCTE_FBDSIZE_LG 0x00001000
+
+#define NS_RCT_ENTRY_SIZE 4 /* Number of dwords */
+
+ /* NOTE: We could make macros to contruct the first word of the RCTE,
+ but that doesn't seem to make much sense... */
+
+/*
+ * FBD - Free Buffer Descriptor
+ *
+ * Written by the device driver using via the command register.
+ */
+
+typedef struct ns_fbd {
+ u32 buffer_handle;
+ u32 dma_address;
+} ns_fbd;
+
+/*
+ * TST - Transmit Schedule Table
+ *
+ * Written by the device driver.
+ */
+
+typedef u32 ns_tste;
+
+#define NS_TST_OPCODE_MASK 0x60000000
+
+#define NS_TST_OPCODE_NULL 0x00000000 /* Insert null cell */
+#define NS_TST_OPCODE_FIXED 0x20000000 /* Cell from a fixed rate channel */
+#define NS_TST_OPCODE_VARIABLE 0x40000000
+#define NS_TST_OPCODE_END 0x60000000 /* Jump */
+
+#define ns_tste_make(opcode, sramad) (opcode | sramad)
+
+ /* NOTE:
+
+ - When the opcode is FIXED, sramad specifies the SRAM address of the
+ SCD for that fixed rate channel.
+ - When the opcode is END, sramad specifies the SRAM address of the
+ location of the next TST entry to read.
+ */
+
+/*
+ * SCD - Segmentation Channel Descriptor
+ *
+ * Written by both the device driver and the NICStAR
+ */
+
+typedef struct ns_scd {
+ u32 word_1;
+ u32 word_2;
+ u32 partial_aal5_crc;
+ u32 reserved;
+ ns_scqe cache_a;
+ ns_scqe cache_b;
+} ns_scd;
+
+#define NS_SCD_BASE_MASK_VAR 0xFFFFE000 /* Variable rate */
+#define NS_SCD_BASE_MASK_FIX 0xFFFFFC00 /* Fixed rate */
+#define NS_SCD_TAIL_MASK_VAR 0x00001FF0
+#define NS_SCD_TAIL_MASK_FIX 0x000003F0
+#define NS_SCD_HEAD_MASK_VAR 0x00001FF0
+#define NS_SCD_HEAD_MASK_FIX 0x000003F0
+#define NS_SCD_XMITFOREVER 0x02000000
+
+ /* NOTE: There are other fields in word 2 of the SCD, but as they should
+ not be needed in the device driver they are not defined here. */
+
+/* NICStAR local SRAM memory map */
+
+#define NS_RCT 0x00000
+#define NS_RCT_32_END 0x03FFF
+#define NS_RCT_128_END 0x0FFFF
+#define NS_UNUSED_32 0x04000
+#define NS_UNUSED_128 0x10000
+#define NS_UNUSED_END 0x1BFFF
+#define NS_TST_FRSCD 0x1C000
+#define NS_TST_FRSCD_END 0x1E7DB
+#define NS_VRSCD2 0x1E7DC
+#define NS_VRSCD2_END 0x1E7E7
+#define NS_VRSCD1 0x1E7E8
+#define NS_VRSCD1_END 0x1E7F3
+#define NS_VRSCD0 0x1E7F4
+#define NS_VRSCD0_END 0x1E7FF
+#define NS_RXFIFO 0x1E800
+#define NS_RXFIFO_END 0x1F7FF
+#define NS_SMFBQ 0x1F800
+#define NS_SMFBQ_END 0x1FBFF
+#define NS_LGFBQ 0x1FC00
+#define NS_LGFBQ_END 0x1FFFF
+
+/* NISCtAR operation registers */
+
+/* See Section 3.4 of `IDT77211 NICStAR User Manual' from www.idt.com */
+
+enum ns_regs {
+ DR0 = 0x00, /* Data Register 0 R/W */
+ DR1 = 0x04, /* Data Register 1 W */
+ DR2 = 0x08, /* Data Register 2 W */
+ DR3 = 0x0C, /* Data Register 3 W */
+ CMD = 0x10, /* Command W */
+ CFG = 0x14, /* Configuration R/W */
+ STAT = 0x18, /* Status R/W */
+ RSQB = 0x1C, /* Receive Status Queue Base W */
+ RSQT = 0x20, /* Receive Status Queue Tail R */
+ RSQH = 0x24, /* Receive Status Queue Head W */
+ CDC = 0x28, /* Cell Drop Counter R/clear */
+ VPEC = 0x2C, /* VPI/VCI Lookup Error Count R/clear */
+ ICC = 0x30, /* Invalid Cell Count R/clear */
+ RAWCT = 0x34, /* Raw Cell Tail R */
+ TMR = 0x38, /* Timer R */
+ TSTB = 0x3C, /* Transmit Schedule Table Base R/W */
+ TSQB = 0x40, /* Transmit Status Queue Base W */
+ TSQT = 0x44, /* Transmit Status Queue Tail R */
+ TSQH = 0x48, /* Transmit Status Queue Head W */
+ GP = 0x4C, /* General Purpose R/W */
+ VPM = 0x50 /* VPI/VCI Mask W */
+};
+
+/* NICStAR commands issued to the CMD register */
+
+/* Top 4 bits are command opcode, lower 28 are parameters. */
+
+#define NS_CMD_NO_OPERATION 0x00000000
+ /* params always 0 */
+
+#define NS_CMD_OPENCLOSE_CONNECTION 0x20000000
+ /* b19{1=open,0=close} b18-2{SRAM addr} */
+
+#define NS_CMD_WRITE_SRAM 0x40000000
+ /* b18-2{SRAM addr} b1-0{burst size} */
+
+#define NS_CMD_READ_SRAM 0x50000000
+ /* b18-2{SRAM addr} */
+
+#define NS_CMD_WRITE_FREEBUFQ 0x60000000
+ /* b0{large buf indicator} */
+
+#define NS_CMD_READ_UTILITY 0x80000000
+ /* b8{1=select UTL_CS1} b9{1=select UTL_CS0} b7-0{bus addr} */
+
+#define NS_CMD_WRITE_UTILITY 0x90000000
+ /* b8{1=select UTL_CS1} b9{1=select UTL_CS0} b7-0{bus addr} */
+
+#define NS_CMD_OPEN_CONNECTION (NS_CMD_OPENCLOSE_CONNECTION | 0x00080000)
+#define NS_CMD_CLOSE_CONNECTION NS_CMD_OPENCLOSE_CONNECTION
+
+/* NICStAR configuration bits */
+
+#define NS_CFG_SWRST 0x80000000 /* Software Reset */
+#define NS_CFG_RXPATH 0x20000000 /* Receive Path Enable */
+#define NS_CFG_SMBUFSIZE_MASK 0x18000000 /* Small Receive Buffer Size */
+#define NS_CFG_LGBUFSIZE_MASK 0x06000000 /* Large Receive Buffer Size */
+#define NS_CFG_EFBIE 0x01000000 /* Empty Free Buffer Queue
+ Interrupt Enable */
+#define NS_CFG_RSQSIZE_MASK 0x00C00000 /* Receive Status Queue Size */
+#define NS_CFG_ICACCEPT 0x00200000 /* Invalid Cell Accept */
+#define NS_CFG_IGNOREGFC 0x00100000 /* Ignore General Flow Control */
+#define NS_CFG_VPIBITS_MASK 0x000C0000 /* VPI/VCI Bits Size Select */
+#define NS_CFG_RCTSIZE_MASK 0x00030000 /* Receive Connection Table Size */
+#define NS_CFG_VCERRACCEPT 0x00008000 /* VPI/VCI Error Cell Accept */
+#define NS_CFG_RXINT_MASK 0x00007000 /* End of Receive PDU Interrupt
+ Handling */
+#define NS_CFG_RAWIE 0x00000800 /* Raw Cell Qu' Interrupt Enable */
+#define NS_CFG_RSQAFIE 0x00000400 /* Receive Queue Almost Full
+ Interrupt Enable */
+#define NS_CFG_RXRM 0x00000200 /* Receive RM Cells */
+#define NS_CFG_TMRROIE 0x00000080 /* Timer Roll Over Interrupt
+ Enable */
+#define NS_CFG_TXEN 0x00000020 /* Transmit Operation Enable */
+#define NS_CFG_TXIE 0x00000010 /* Transmit Status Interrupt
+ Enable */
+#define NS_CFG_TXURIE 0x00000008 /* Transmit Under-run Interrupt
+ Enable */
+#define NS_CFG_UMODE 0x00000004 /* Utopia Mode (cell/byte) Select */
+#define NS_CFG_TSQFIE 0x00000002 /* Transmit Status Queue Full
+ Interrupt Enable */
+#define NS_CFG_PHYIE 0x00000001 /* PHY Interrupt Enable */
+
+#define NS_CFG_SMBUFSIZE_48 0x00000000
+#define NS_CFG_SMBUFSIZE_96 0x08000000
+#define NS_CFG_SMBUFSIZE_240 0x10000000
+#define NS_CFG_SMBUFSIZE_2048 0x18000000
+
+#define NS_CFG_LGBUFSIZE_2048 0x00000000
+#define NS_CFG_LGBUFSIZE_4096 0x02000000
+#define NS_CFG_LGBUFSIZE_8192 0x04000000
+#define NS_CFG_LGBUFSIZE_16384 0x06000000
+
+#define NS_CFG_RSQSIZE_2048 0x00000000
+#define NS_CFG_RSQSIZE_4096 0x00400000
+#define NS_CFG_RSQSIZE_8192 0x00800000
+
+#define NS_CFG_VPIBITS_0 0x00000000
+#define NS_CFG_VPIBITS_1 0x00040000
+#define NS_CFG_VPIBITS_2 0x00080000
+#define NS_CFG_VPIBITS_8 0x000C0000
+
+#define NS_CFG_RCTSIZE_4096_ENTRIES 0x00000000
+#define NS_CFG_RCTSIZE_8192_ENTRIES 0x00010000
+#define NS_CFG_RCTSIZE_16384_ENTRIES 0x00020000
+
+#define NS_CFG_RXINT_NOINT 0x00000000
+#define NS_CFG_RXINT_NODELAY 0x00001000
+#define NS_CFG_RXINT_314US 0x00002000
+#define NS_CFG_RXINT_624US 0x00003000
+#define NS_CFG_RXINT_899US 0x00004000
+
+/* NICStAR STATus bits */
+
+#define NS_STAT_SFBQC_MASK 0xFF000000 /* hi 8 bits Small Buffer Queue Count */
+#define NS_STAT_LFBQC_MASK 0x00FF0000 /* hi 8 bits Large Buffer Queue Count */
+#define NS_STAT_TSIF 0x00008000 /* Transmit Status Queue Indicator */
+#define NS_STAT_TXICP 0x00004000 /* Transmit Incomplete PDU */
+#define NS_STAT_TSQF 0x00001000 /* Transmit Status Queue Full */
+#define NS_STAT_TMROF 0x00000800 /* Timer Overflow */
+#define NS_STAT_PHYI 0x00000400 /* PHY Device Interrupt */
+#define NS_STAT_CMDBZ 0x00000200 /* Command Busy */
+#define NS_STAT_SFBQF 0x00000100 /* Small Buffer Queue Full */
+#define NS_STAT_LFBQF 0x00000080 /* Large Buffer Queue Full */
+#define NS_STAT_RSQF 0x00000040 /* Receive Status Queue Full */
+#define NS_STAT_EOPDU 0x00000020 /* End of PDU */
+#define NS_STAT_RAWCF 0x00000010 /* Raw Cell Flag */
+#define NS_STAT_SFBQE 0x00000008 /* Small Buffer Queue Empty */
+#define NS_STAT_LFBQE 0x00000004 /* Large Buffer Queue Empty */
+#define NS_STAT_RSQAF 0x00000002 /* Receive Status Queue Almost Full */
+
+#define ns_stat_sfbqc_get(stat) (((stat) & NS_STAT_SFBQC_MASK) >> 23)
+#define ns_stat_lfbqc_get(stat) (((stat) & NS_STAT_LFBQC_MASK) >> 15)
+
+/* #defines which depend on other #defines */
+
+#define NS_TST0 NS_TST_FRSCD
+#define NS_TST1 (NS_TST_FRSCD + NS_TST_NUM_ENTRIES + 1)
+
+#define NS_FRSCD (NS_TST1 + NS_TST_NUM_ENTRIES + 1)
+#define NS_FRSCD_SIZE 12 /* 12 dwords */
+#define NS_FRSCD_NUM ((NS_TST_FRSCD_END + 1 - NS_FRSCD) / NS_FRSCD_SIZE)
+
+#if (NS_SMBUFSIZE == 48)
+#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_48
+#elif (NS_SMBUFSIZE == 96)
+#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_96
+#elif (NS_SMBUFSIZE == 240)
+#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_240
+#elif (NS_SMBUFSIZE == 2048)
+#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_2048
+#else
+#error NS_SMBUFSIZE is incorrect in nicstar.h
+#endif /* NS_SMBUFSIZE */
+
+#if (NS_LGBUFSIZE == 2048)
+#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_2048
+#elif (NS_LGBUFSIZE == 4096)
+#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_4096
+#elif (NS_LGBUFSIZE == 8192)
+#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_8192
+#elif (NS_LGBUFSIZE == 16384)
+#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_16384
+#else
+#error NS_LGBUFSIZE is incorrect in nicstar.h
+#endif /* NS_LGBUFSIZE */
+
+#if (NS_RSQSIZE == 2048)
+#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_2048
+#elif (NS_RSQSIZE == 4096)
+#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_4096
+#elif (NS_RSQSIZE == 8192)
+#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_8192
+#else
+#error NS_RSQSIZE is incorrect in nicstar.h
+#endif /* NS_RSQSIZE */
+
+#if (NS_VPIBITS == 0)
+#define NS_CFG_VPIBITS NS_CFG_VPIBITS_0
+#elif (NS_VPIBITS == 1)
+#define NS_CFG_VPIBITS NS_CFG_VPIBITS_1
+#elif (NS_VPIBITS == 2)
+#define NS_CFG_VPIBITS NS_CFG_VPIBITS_2
+#elif (NS_VPIBITS == 8)
+#define NS_CFG_VPIBITS NS_CFG_VPIBITS_8
+#else
+#error NS_VPIBITS is incorrect in nicstar.h
+#endif /* NS_VPIBITS */
+
+#ifdef RCQ_SUPPORT
+#define NS_CFG_RAWIE_OPT NS_CFG_RAWIE
+#else
+#define NS_CFG_RAWIE_OPT 0x00000000
+#endif /* RCQ_SUPPORT */
+
+#ifdef ENABLE_TSQFIE
+#define NS_CFG_TSQFIE_OPT NS_CFG_TSQFIE
+#else
+#define NS_CFG_TSQFIE_OPT 0x00000000
+#endif /* ENABLE_TSQFIE */
+
+/* PCI stuff */
+
+#ifndef PCI_VENDOR_ID_IDT
+#define PCI_VENDOR_ID_IDT 0x111D
+#endif /* PCI_VENDOR_ID_IDT */
+
+#ifndef PCI_DEVICE_ID_IDT_IDT77201
+#define PCI_DEVICE_ID_IDT_IDT77201 0x0001
+#endif /* PCI_DEVICE_ID_IDT_IDT77201 */
+
+/* Device driver structures */
+
+struct ns_skb_prv {
+ u32 buf_type; /* BUF_SM/BUF_LG/BUF_NONE */
+ u32 dma;
+ int iovcnt;
+};
+
+#define NS_PRV_BUFTYPE(skb) \
+ (((struct ns_skb_prv *)(ATM_SKB(skb)+1))->buf_type)
+#define NS_PRV_DMA(skb) \
+ (((struct ns_skb_prv *)(ATM_SKB(skb)+1))->dma)
+#define NS_PRV_IOVCNT(skb) \
+ (((struct ns_skb_prv *)(ATM_SKB(skb)+1))->iovcnt)
+
+typedef struct tsq_info {
+ void *org;
+ dma_addr_t dma;
+ ns_tsi *base;
+ ns_tsi *next;
+ ns_tsi *last;
+} tsq_info;
+
+typedef struct scq_info {
+ void *org;
+ dma_addr_t dma;
+ ns_scqe *base;
+ ns_scqe *last;
+ ns_scqe *next;
+ volatile ns_scqe *tail; /* Not related to the nicstar register */
+ unsigned num_entries;
+ struct sk_buff **skb; /* Pointer to an array of pointers
+ to the sk_buffs used for tx */
+ u32 scd; /* SRAM address of the corresponding
+ SCD */
+ int tbd_count; /* Only meaningful on variable rate */
+ wait_queue_head_t scqfull_waitq;
+ volatile char full; /* SCQ full indicator */
+ spinlock_t lock; /* SCQ spinlock */
+} scq_info;
+
+typedef struct rsq_info {
+ void *org;
+ dma_addr_t dma;
+ ns_rsqe *base;
+ ns_rsqe *next;
+ ns_rsqe *last;
+} rsq_info;
+
+typedef struct skb_pool {
+ volatile int count; /* number of buffers in the queue */
+ struct sk_buff_head queue;
+} skb_pool;
+
+/* NOTE: for small and large buffer pools, the count is not used, as the
+ actual value used for buffer management is the one read from the
+ card. */
+
+typedef struct vc_map {
+ volatile unsigned int tx:1; /* TX vc? */
+ volatile unsigned int rx:1; /* RX vc? */
+ struct atm_vcc *tx_vcc, *rx_vcc;
+ struct sk_buff *rx_iov; /* RX iovector skb */
+ scq_info *scq; /* To keep track of the SCQ */
+ u32 cbr_scd; /* SRAM address of the corresponding
+ SCD. 0x00000000 for UBR/VBR/ABR */
+ int tbd_count;
+} vc_map;
+
+typedef struct ns_dev {
+ int index; /* Card ID to the device driver */
+ int sram_size; /* In k x 32bit words. 32 or 128 */
+ void __iomem *membase; /* Card's memory base address */
+ unsigned long max_pcr;
+ int rct_size; /* Number of entries */
+ int vpibits;
+ int vcibits;
+ struct pci_dev *pcidev;
+ struct idr idr;
+ struct atm_dev *atmdev;
+ tsq_info tsq;
+ rsq_info rsq;
+ scq_info *scq0, *scq1, *scq2; /* VBR SCQs */
+ skb_pool sbpool; /* Small buffers */
+ skb_pool lbpool; /* Large buffers */
+ skb_pool hbpool; /* Pre-allocated huge buffers */
+ skb_pool iovpool; /* iovector buffers */
+ volatile int efbie; /* Empty free buf. queue int. enabled */
+ volatile u32 tst_addr; /* SRAM address of the TST in use */
+ volatile int tst_free_entries;
+ vc_map vcmap[NS_MAX_RCTSIZE];
+ vc_map *tste2vc[NS_TST_NUM_ENTRIES];
+ vc_map *scd2vc[NS_FRSCD_NUM];
+ buf_nr sbnr;
+ buf_nr lbnr;
+ buf_nr hbnr;
+ buf_nr iovnr;
+ int sbfqc;
+ int lbfqc;
+ struct sk_buff *sm_handle;
+ u32 sm_addr;
+ struct sk_buff *lg_handle;
+ u32 lg_addr;
+ struct sk_buff *rcbuf; /* Current raw cell buffer */
+ struct ns_rcqe *rawcell;
+ u32 rawch; /* Raw cell queue head */
+ unsigned intcnt; /* Interrupt counter */
+ spinlock_t int_lock; /* Interrupt lock */
+ spinlock_t res_lock; /* Card resource lock */
+} ns_dev;
+
+ /* NOTE: Each tste2vc entry relates a given TST entry to the corresponding
+ CBR vc. If the entry is not allocated, it must be NULL.
+
+ There are two TSTs so the driver can modify them on the fly
+ without stopping the transmission.
+
+ scd2vc allows us to find out unused fixed rate SCDs, because
+ they must have a NULL pointer here. */
+
+#endif /* _LINUX_NICSTAR_H_ */
diff --git a/drivers/atm/nicstarmac.c b/drivers/atm/nicstarmac.c
new file mode 100644
index 000000000..f594526f8
--- /dev/null
+++ b/drivers/atm/nicstarmac.c
@@ -0,0 +1,248 @@
+/*
+ * this file included by nicstar.c
+ */
+
+/*
+ * nicstarmac.c
+ * Read this ForeRunner's MAC address from eprom/eeprom
+ */
+
+#include <linux/kernel.h>
+
+typedef void __iomem *virt_addr_t;
+
+#define CYCLE_DELAY 5
+
+/*
+ This was the original definition
+#define osp_MicroDelay(microsec) \
+ do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+*/
+#define osp_MicroDelay(microsec) {unsigned long useconds = (microsec); \
+ udelay((useconds));}
+/*
+ * The following tables represent the timing diagrams found in
+ * the Data Sheet for the Xicor X25020 EEProm. The #defines below
+ * represent the bits in the NICStAR's General Purpose register
+ * that must be toggled for the corresponding actions on the EEProm
+ * to occur.
+ */
+
+/* Write Data To EEProm from SI line on rising edge of CLK */
+/* Read Data From EEProm on falling edge of CLK */
+
+#define CS_HIGH 0x0002 /* Chip select high */
+#define CS_LOW 0x0000 /* Chip select low (active low) */
+#define CLK_HIGH 0x0004 /* Clock high */
+#define CLK_LOW 0x0000 /* Clock low */
+#define SI_HIGH 0x0001 /* Serial input data high */
+#define SI_LOW 0x0000 /* Serial input data low */
+
+/* Read Status Register = 0000 0101b */
+#if 0
+static u_int32_t rdsrtab[] = {
+ CS_HIGH | CLK_HIGH,
+ CS_LOW | CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW | SI_HIGH,
+ CLK_HIGH | SI_HIGH, /* 1 */
+ CLK_LOW | SI_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW | SI_HIGH,
+ CLK_HIGH | SI_HIGH /* 1 */
+};
+#endif /* 0 */
+
+/* Read from EEPROM = 0000 0011b */
+static u_int32_t readtab[] = {
+ /*
+ CS_HIGH | CLK_HIGH,
+ */
+ CS_LOW | CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW,
+ CLK_HIGH, /* 0 */
+ CLK_LOW | SI_HIGH,
+ CLK_HIGH | SI_HIGH, /* 1 */
+ CLK_LOW | SI_HIGH,
+ CLK_HIGH | SI_HIGH /* 1 */
+};
+
+/* Clock to read from/write to the eeprom */
+static u_int32_t clocktab[] = {
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW,
+ CLK_HIGH,
+ CLK_LOW
+};
+
+#define NICSTAR_REG_WRITE(bs, reg, val) \
+ while ( readl(bs + STAT) & 0x0200 ) ; \
+ writel((val),(base)+(reg))
+#define NICSTAR_REG_READ(bs, reg) \
+ readl((base)+(reg))
+#define NICSTAR_REG_GENERAL_PURPOSE GP
+
+/*
+ * This routine will clock the Read_Status_reg function into the X2520
+ * eeprom, then pull the result from bit 16 of the NicSTaR's General Purpose
+ * register.
+ */
+#if 0
+u_int32_t nicstar_read_eprom_status(virt_addr_t base)
+{
+ u_int32_t val;
+ u_int32_t rbyte;
+ int32_t i, j;
+
+ /* Send read instruction */
+ val = NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) & 0xFFFFFFF0;
+
+ for (i = 0; i < ARRAY_SIZE(rdsrtab); i++) {
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | rdsrtab[i]));
+ osp_MicroDelay(CYCLE_DELAY);
+ }
+
+ /* Done sending instruction - now pull data off of bit 16, MSB first */
+ /* Data clocked out of eeprom on falling edge of clock */
+
+ rbyte = 0;
+ for (i = 7, j = 0; i >= 0; i--) {
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | clocktab[j++]));
+ rbyte |= (((NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE)
+ & 0x00010000) >> 16) << i);
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | clocktab[j++]));
+ osp_MicroDelay(CYCLE_DELAY);
+ }
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, 2);
+ osp_MicroDelay(CYCLE_DELAY);
+ return rbyte;
+}
+#endif /* 0 */
+
+/*
+ * This routine will clock the Read_data function into the X2520
+ * eeprom, followed by the address to read from, through the NicSTaR's General
+ * Purpose register.
+ */
+
+static u_int8_t read_eprom_byte(virt_addr_t base, u_int8_t offset)
+{
+ u_int32_t val = 0;
+ int i, j = 0;
+ u_int8_t tempread = 0;
+
+ val = NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) & 0xFFFFFFF0;
+
+ /* Send READ instruction */
+ for (i = 0; i < ARRAY_SIZE(readtab); i++) {
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | readtab[i]));
+ osp_MicroDelay(CYCLE_DELAY);
+ }
+
+ /* Next, we need to send the byte address to read from */
+ for (i = 7; i >= 0; i--) {
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | clocktab[j++] | ((offset >> i) & 1)));
+ osp_MicroDelay(CYCLE_DELAY);
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | clocktab[j++] | ((offset >> i) & 1)));
+ osp_MicroDelay(CYCLE_DELAY);
+ }
+
+ j = 0;
+
+ /* Now, we can read data from the eeprom by clocking it in */
+ for (i = 7; i >= 0; i--) {
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | clocktab[j++]));
+ osp_MicroDelay(CYCLE_DELAY);
+ tempread |=
+ (((NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE)
+ & 0x00010000) >> 16) << i);
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | clocktab[j++]));
+ osp_MicroDelay(CYCLE_DELAY);
+ }
+
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, 2);
+ osp_MicroDelay(CYCLE_DELAY);
+ return tempread;
+}
+
+static void nicstar_init_eprom(virt_addr_t base)
+{
+ u_int32_t val;
+
+ /*
+ * turn chip select off
+ */
+ val = NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) & 0xFFFFFFF0;
+
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | CS_HIGH | CLK_HIGH));
+ osp_MicroDelay(CYCLE_DELAY);
+
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | CS_HIGH | CLK_LOW));
+ osp_MicroDelay(CYCLE_DELAY);
+
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | CS_HIGH | CLK_HIGH));
+ osp_MicroDelay(CYCLE_DELAY);
+
+ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE,
+ (val | CS_HIGH | CLK_LOW));
+ osp_MicroDelay(CYCLE_DELAY);
+}
+
+/*
+ * This routine will be the interface to the ReadPromByte function
+ * above.
+ */
+
+static void
+nicstar_read_eprom(virt_addr_t base,
+ u_int8_t prom_offset, u_int8_t * buffer, u_int32_t nbytes)
+{
+ u_int i;
+
+ for (i = 0; i < nbytes; i++) {
+ buffer[i] = read_eprom_byte(base, prom_offset);
+ ++prom_offset;
+ osp_MicroDelay(CYCLE_DELAY);
+ }
+}
diff --git a/drivers/atm/nicstarmac.copyright b/drivers/atm/nicstarmac.copyright
new file mode 100644
index 000000000..180531a83
--- /dev/null
+++ b/drivers/atm/nicstarmac.copyright
@@ -0,0 +1,61 @@
+/* nicstar.c v0.22 Jawaid Bazyar (bazyar@hypermall.com)
+ * nicstar.c, M. Welsh (matt.welsh@cl.cam.ac.uk)
+ *
+ * Hacked October, 1997 by Jawaid Bazyar, Interlink Advertising Services Inc.
+ * http://www.hypermall.com/
+ * 10/1/97 - commented out CFG_PHYIE bit - we don't care when the PHY
+ * interrupts us (except possibly for removal/insertion of the cable?)
+ * 10/4/97 - began heavy inline documentation of the code. Corrected typos
+ * and spelling mistakes.
+ * 10/5/97 - added code to handle PHY interrupts, disable PHY on
+ * loss of link, and correctly re-enable PHY when link is
+ * re-established. (put back CFG_PHYIE)
+ *
+ * Modified to work with the IDT7721 nicstar -- AAL5 (tested) only.
+ *
+ * R. D. Rechenmacher <ron@fnal.gov>, Aug. 6, 1997
+ *
+ * Linux driver for the IDT77201 NICStAR PCI ATM controller.
+ * PHY component is expected to be 155 Mbps S/UNI-Lite or IDT 77155;
+ * see init_nicstar() for PHY initialization to change this. This driver
+ * expects the Linux ATM stack to support scatter-gather lists
+ * (skb->atm.iovcnt != 0) for Rx skb's passed to vcc->push.
+ *
+ * Implementing minimal-copy of received data:
+ * IDT always receives data into a small buffer, then large buffers
+ * as needed. This means that data must always be copied to create
+ * the linear buffer needed by most non-ATM protocol stacks (e.g. IP)
+ * Fix is simple: make large buffers large enough to hold entire
+ * SDU, and leave <small_buffer_data> bytes empty at the start. Then
+ * copy small buffer contents to head of large buffer.
+ * Trick is to avoid fragmenting Linux, due to need for a lot of large
+ * buffers. This is done by 2 things:
+ * 1) skb->destructor / skb->atm.recycle_buffer
+ * combined, allow nicstar_free_rx_skb to be called to
+ * recycle large data buffers
+ * 2) skb_clone of received buffers
+ * See nicstar_free_rx_skb and linearize_buffer for implementation
+ * details.
+ *
+ *
+ *
+ * Copyright (c) 1996 University of Cambridge Computer Laboratory
+ *
+ * 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.
+ *
+ * M. Welsh, 6 July 1996
+ *
+ *
+ */
diff --git a/drivers/atm/solos-attrlist.c b/drivers/atm/solos-attrlist.c
new file mode 100644
index 000000000..9a676ee30
--- /dev/null
+++ b/drivers/atm/solos-attrlist.c
@@ -0,0 +1,82 @@
+SOLOS_ATTR_RO(DriverVersion)
+SOLOS_ATTR_RO(APIVersion)
+SOLOS_ATTR_RO(FirmwareVersion)
+SOLOS_ATTR_RO(Version)
+// SOLOS_ATTR_RO(DspVersion)
+// SOLOS_ATTR_RO(CommonHandshake)
+SOLOS_ATTR_RO(Connected)
+SOLOS_ATTR_RO(OperationalMode)
+SOLOS_ATTR_RO(State)
+SOLOS_ATTR_RO(Watchdog)
+SOLOS_ATTR_RO(OperationProgress)
+SOLOS_ATTR_RO(LastFailed)
+SOLOS_ATTR_RO(TxBitRate)
+SOLOS_ATTR_RO(RxBitRate)
+// SOLOS_ATTR_RO(DeltACTATPds)
+// SOLOS_ATTR_RO(DeltACTATPus)
+SOLOS_ATTR_RO(TxATTNDR)
+SOLOS_ATTR_RO(RxATTNDR)
+SOLOS_ATTR_RO(AnnexType)
+SOLOS_ATTR_RO(GeneralFailure)
+SOLOS_ATTR_RO(InterleaveDpDn)
+SOLOS_ATTR_RO(InterleaveDpUp)
+SOLOS_ATTR_RO(RSCorrectedErrorsDn)
+SOLOS_ATTR_RO(RSUnCorrectedErrorsDn)
+SOLOS_ATTR_RO(RSCorrectedErrorsUp)
+SOLOS_ATTR_RO(RSUnCorrectedErrorsUp)
+SOLOS_ATTR_RO(InterleaveRDn)
+SOLOS_ATTR_RO(InterleaveRUp)
+SOLOS_ATTR_RO(BisRDn)
+SOLOS_ATTR_RO(BisRUp)
+SOLOS_ATTR_RO(INPdown)
+SOLOS_ATTR_RO(INPup)
+SOLOS_ATTR_RO(ShowtimeStart)
+SOLOS_ATTR_RO(ATURVendor)
+SOLOS_ATTR_RO(ATUCCountry)
+SOLOS_ATTR_RO(ATURANSIRev)
+SOLOS_ATTR_RO(ATURANSISTD)
+SOLOS_ATTR_RO(ATUCANSIRev)
+SOLOS_ATTR_RO(ATUCANSIId)
+SOLOS_ATTR_RO(ATUCANSISTD)
+SOLOS_ATTR_RO(DataBoost)
+SOLOS_ATTR_RO(LocalITUCountryCode)
+SOLOS_ATTR_RO(LocalSEF)
+SOLOS_ATTR_RO(LocalEndLOS)
+SOLOS_ATTR_RO(LocalSNRMargin)
+SOLOS_ATTR_RO(LocalLineAttn)
+SOLOS_ATTR_RO(RawAttn)
+SOLOS_ATTR_RO(LocalTxPower)
+SOLOS_ATTR_RO(RemoteTxPower)
+SOLOS_ATTR_RO(RemoteSEF)
+SOLOS_ATTR_RO(RemoteLOS)
+SOLOS_ATTR_RO(RemoteLineAttn)
+SOLOS_ATTR_RO(RemoteSNRMargin)
+SOLOS_ATTR_RO(LineUpCount)
+SOLOS_ATTR_RO(SRACnt)
+SOLOS_ATTR_RO(SRACntUp)
+SOLOS_ATTR_RO(ProfileStatus)
+SOLOS_ATTR_RW(Action)
+SOLOS_ATTR_RW(ActivateLine)
+SOLOS_ATTR_RO(LineStatus)
+SOLOS_ATTR_RW(HostControl)
+SOLOS_ATTR_RW(AutoStart)
+SOLOS_ATTR_RW(Failsafe)
+SOLOS_ATTR_RW(ShowtimeLed)
+SOLOS_ATTR_RW(Retrain)
+SOLOS_ATTR_RW(Defaults)
+SOLOS_ATTR_RW(LineMode)
+SOLOS_ATTR_RW(Profile)
+SOLOS_ATTR_RW(DetectNoise)
+SOLOS_ATTR_RW(BisAForceSNRMarginDn)
+SOLOS_ATTR_RW(BisMForceSNRMarginDn)
+SOLOS_ATTR_RW(BisAMaxMargin)
+SOLOS_ATTR_RW(BisMMaxMargin)
+SOLOS_ATTR_RW(AnnexAForceSNRMarginDn)
+SOLOS_ATTR_RW(AnnexAMaxMargin)
+SOLOS_ATTR_RW(AnnexMMaxMargin)
+SOLOS_ATTR_RO(SupportedAnnexes)
+SOLOS_ATTR_RO(Status)
+SOLOS_ATTR_RO(TotalStart)
+SOLOS_ATTR_RO(RecentShowtimeStart)
+SOLOS_ATTR_RO(TotalRxBlocks)
+SOLOS_ATTR_RO(TotalTxBlocks)
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
new file mode 100644
index 000000000..af5e66a7c
--- /dev/null
+++ b/drivers/atm/solos-pci.c
@@ -0,0 +1,1493 @@
+/*
+ * Driver for the Solos PCI ADSL2+ card, designed to support Linux by
+ * Traverse Technologies -- http://www.traverse.com.au/
+ * Xrio Limited -- http://www.xrio.com/
+ *
+ *
+ * Copyright © 2008 Traverse Technologies
+ * Copyright © 2008 Intel Corporation
+ *
+ * Authors: Nathan Williams <nathan@traverse.com.au>
+ * David Woodhouse <dwmw2@infradead.org>
+ * Treker Chen <treker@xrio.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#define DEBUG
+#define VERBOSE_DEBUG
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/skbuff.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/firmware.h>
+#include <linux/ctype.h>
+#include <linux/swab.h>
+#include <linux/slab.h>
+
+#define VERSION "1.04"
+#define DRIVER_VERSION 0x01
+#define PTAG "solos-pci"
+
+#define CONFIG_RAM_SIZE 128
+#define FLAGS_ADDR 0x7C
+#define IRQ_EN_ADDR 0x78
+#define FPGA_VER 0x74
+#define IRQ_CLEAR 0x70
+#define WRITE_FLASH 0x6C
+#define PORTS 0x68
+#define FLASH_BLOCK 0x64
+#define FLASH_BUSY 0x60
+#define FPGA_MODE 0x5C
+#define FLASH_MODE 0x58
+#define GPIO_STATUS 0x54
+#define DRIVER_VER 0x50
+#define TX_DMA_ADDR(port) (0x40 + (4 * (port)))
+#define RX_DMA_ADDR(port) (0x30 + (4 * (port)))
+
+#define DATA_RAM_SIZE 32768
+#define BUF_SIZE 2048
+#define OLD_BUF_SIZE 4096 /* For FPGA versions <= 2*/
+/* Old boards use ATMEL AD45DB161D flash */
+#define ATMEL_FPGA_PAGE 528 /* FPGA flash page size*/
+#define ATMEL_SOLOS_PAGE 512 /* Solos flash page size*/
+#define ATMEL_FPGA_BLOCK (ATMEL_FPGA_PAGE * 8) /* FPGA block size*/
+#define ATMEL_SOLOS_BLOCK (ATMEL_SOLOS_PAGE * 8) /* Solos block size*/
+/* Current boards use M25P/M25PE SPI flash */
+#define SPI_FLASH_BLOCK (256 * 64)
+
+#define RX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2)
+#define TX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2 + (card->buffer_size))
+#define FLASH_BUF ((card->buffers) + 4*(card->buffer_size)*2)
+
+#define RX_DMA_SIZE 2048
+
+#define FPGA_VERSION(a,b) (((a) << 8) + (b))
+#define LEGACY_BUFFERS 2
+#define DMA_SUPPORTED 4
+
+static int reset = 0;
+static int atmdebug = 0;
+static int firmware_upgrade = 0;
+static int fpga_upgrade = 0;
+static int db_firmware_upgrade = 0;
+static int db_fpga_upgrade = 0;
+
+struct pkt_hdr {
+ __le16 size;
+ __le16 vpi;
+ __le16 vci;
+ __le16 type;
+};
+
+struct solos_skb_cb {
+ struct atm_vcc *vcc;
+ uint32_t dma_addr;
+};
+
+
+#define SKB_CB(skb) ((struct solos_skb_cb *)skb->cb)
+
+#define PKT_DATA 0
+#define PKT_COMMAND 1
+#define PKT_POPEN 3
+#define PKT_PCLOSE 4
+#define PKT_STATUS 5
+
+struct solos_card {
+ void __iomem *config_regs;
+ void __iomem *buffers;
+ int nr_ports;
+ int tx_mask;
+ struct pci_dev *dev;
+ struct atm_dev *atmdev[4];
+ struct tasklet_struct tlet;
+ spinlock_t tx_lock;
+ spinlock_t tx_queue_lock;
+ spinlock_t cli_queue_lock;
+ spinlock_t param_queue_lock;
+ struct list_head param_queue;
+ struct sk_buff_head tx_queue[4];
+ struct sk_buff_head cli_queue[4];
+ struct sk_buff *tx_skb[4];
+ struct sk_buff *rx_skb[4];
+ unsigned char *dma_bounce;
+ wait_queue_head_t param_wq;
+ wait_queue_head_t fw_wq;
+ int using_dma;
+ int dma_alignment;
+ int fpga_version;
+ int buffer_size;
+ int atmel_flash;
+};
+
+
+struct solos_param {
+ struct list_head list;
+ pid_t pid;
+ int port;
+ struct sk_buff *response;
+};
+
+#define SOLOS_CHAN(atmdev) ((int)(unsigned long)(atmdev)->phy_data)
+
+MODULE_AUTHOR("Traverse Technologies <support@traverse.com.au>");
+MODULE_DESCRIPTION("Solos PCI driver");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
+MODULE_PARM_DESC(reset, "Reset Solos chips on startup");
+MODULE_PARM_DESC(atmdebug, "Print ATM data");
+MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade");
+MODULE_PARM_DESC(fpga_upgrade, "Initiate FPGA upgrade");
+MODULE_PARM_DESC(db_firmware_upgrade, "Initiate daughter board Solos firmware upgrade");
+MODULE_PARM_DESC(db_fpga_upgrade, "Initiate daughter board FPGA upgrade");
+module_param(reset, int, 0444);
+module_param(atmdebug, int, 0644);
+module_param(firmware_upgrade, int, 0444);
+module_param(fpga_upgrade, int, 0444);
+module_param(db_firmware_upgrade, int, 0444);
+module_param(db_fpga_upgrade, int, 0444);
+
+static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb,
+ struct atm_vcc *vcc);
+static uint32_t fpga_tx(struct solos_card *);
+static irqreturn_t solos_irq(int irq, void *dev_id);
+static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci);
+static int atm_init(struct solos_card *, struct device *);
+static void atm_remove(struct solos_card *);
+static int send_command(struct solos_card *card, int dev, const char *buf, size_t size);
+static void solos_bh(unsigned long);
+static int print_buffer(struct sk_buff *buf);
+
+static inline void solos_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+}
+
+static ssize_t solos_param_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
+ struct solos_card *card = atmdev->dev_data;
+ struct solos_param prm;
+ struct sk_buff *skb;
+ struct pkt_hdr *header;
+ int buflen;
+
+ buflen = strlen(attr->attr.name) + 10;
+
+ skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL);
+ if (!skb) {
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_show()\n");
+ return -ENOMEM;
+ }
+
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ buflen = snprintf((void *)&header[1], buflen - 1,
+ "L%05d\n%s\n", current->pid, attr->attr.name);
+ skb_put(skb, buflen);
+
+ header->size = cpu_to_le16(buflen);
+ header->vpi = cpu_to_le16(0);
+ header->vci = cpu_to_le16(0);
+ header->type = cpu_to_le16(PKT_COMMAND);
+
+ prm.pid = current->pid;
+ prm.response = NULL;
+ prm.port = SOLOS_CHAN(atmdev);
+
+ spin_lock_irq(&card->param_queue_lock);
+ list_add(&prm.list, &card->param_queue);
+ spin_unlock_irq(&card->param_queue_lock);
+
+ fpga_queue(card, prm.port, skb, NULL);
+
+ wait_event_timeout(card->param_wq, prm.response, 5 * HZ);
+
+ spin_lock_irq(&card->param_queue_lock);
+ list_del(&prm.list);
+ spin_unlock_irq(&card->param_queue_lock);
+
+ if (!prm.response)
+ return -EIO;
+
+ buflen = prm.response->len;
+ memcpy(buf, prm.response->data, buflen);
+ kfree_skb(prm.response);
+
+ return buflen;
+}
+
+static ssize_t solos_param_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
+ struct solos_card *card = atmdev->dev_data;
+ struct solos_param prm;
+ struct sk_buff *skb;
+ struct pkt_hdr *header;
+ int buflen;
+ ssize_t ret;
+
+ buflen = strlen(attr->attr.name) + 11 + count;
+
+ skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL);
+ if (!skb) {
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_store()\n");
+ return -ENOMEM;
+ }
+
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ buflen = snprintf((void *)&header[1], buflen - 1,
+ "L%05d\n%s\n%s\n", current->pid, attr->attr.name, buf);
+
+ skb_put(skb, buflen);
+ header->size = cpu_to_le16(buflen);
+ header->vpi = cpu_to_le16(0);
+ header->vci = cpu_to_le16(0);
+ header->type = cpu_to_le16(PKT_COMMAND);
+
+ prm.pid = current->pid;
+ prm.response = NULL;
+ prm.port = SOLOS_CHAN(atmdev);
+
+ spin_lock_irq(&card->param_queue_lock);
+ list_add(&prm.list, &card->param_queue);
+ spin_unlock_irq(&card->param_queue_lock);
+
+ fpga_queue(card, prm.port, skb, NULL);
+
+ wait_event_timeout(card->param_wq, prm.response, 5 * HZ);
+
+ spin_lock_irq(&card->param_queue_lock);
+ list_del(&prm.list);
+ spin_unlock_irq(&card->param_queue_lock);
+
+ skb = prm.response;
+
+ if (!skb)
+ return -EIO;
+
+ buflen = skb->len;
+
+ /* Sometimes it has a newline, sometimes it doesn't. */
+ if (skb->data[buflen - 1] == '\n')
+ buflen--;
+
+ if (buflen == 2 && !strncmp(skb->data, "OK", 2))
+ ret = count;
+ else if (buflen == 5 && !strncmp(skb->data, "ERROR", 5))
+ ret = -EIO;
+ else {
+ /* We know we have enough space allocated for this; we allocated
+ it ourselves */
+ skb->data[buflen] = 0;
+
+ dev_warn(&card->dev->dev, "Unexpected parameter response: '%s'\n",
+ skb->data);
+ ret = -EIO;
+ }
+ kfree_skb(skb);
+
+ return ret;
+}
+
+static char *next_string(struct sk_buff *skb)
+{
+ int i = 0;
+ char *this = skb->data;
+
+ for (i = 0; i < skb->len; i++) {
+ if (this[i] == '\n') {
+ this[i] = 0;
+ skb_pull(skb, i + 1);
+ return this;
+ }
+ if (!isprint(this[i]))
+ return NULL;
+ }
+ return NULL;
+}
+
+/*
+ * Status packet has fields separated by \n, starting with a version number
+ * for the information therein. Fields are....
+ *
+ * packet version
+ * RxBitRate (version >= 1)
+ * TxBitRate (version >= 1)
+ * State (version >= 1)
+ * LocalSNRMargin (version >= 1)
+ * LocalLineAttn (version >= 1)
+ */
+static int process_status(struct solos_card *card, int port, struct sk_buff *skb)
+{
+ char *str, *end, *state_str, *snr, *attn;
+ int ver, rate_up, rate_down;
+
+ if (!card->atmdev[port])
+ return -ENODEV;
+
+ str = next_string(skb);
+ if (!str)
+ return -EIO;
+
+ ver = simple_strtol(str, NULL, 10);
+ if (ver < 1) {
+ dev_warn(&card->dev->dev, "Unexpected status interrupt version %d\n",
+ ver);
+ return -EIO;
+ }
+
+ str = next_string(skb);
+ if (!str)
+ return -EIO;
+ if (!strcmp(str, "ERROR")) {
+ dev_dbg(&card->dev->dev, "Status packet indicated Solos error on port %d (starting up?)\n",
+ port);
+ return 0;
+ }
+
+ rate_down = simple_strtol(str, &end, 10);
+ if (*end)
+ return -EIO;
+
+ str = next_string(skb);
+ if (!str)
+ return -EIO;
+ rate_up = simple_strtol(str, &end, 10);
+ if (*end)
+ return -EIO;
+
+ state_str = next_string(skb);
+ if (!state_str)
+ return -EIO;
+
+ /* Anything but 'Showtime' is down */
+ if (strcmp(state_str, "Showtime")) {
+ atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_LOST);
+ dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str);
+ return 0;
+ }
+
+ snr = next_string(skb);
+ if (!snr)
+ return -EIO;
+ attn = next_string(skb);
+ if (!attn)
+ return -EIO;
+
+ dev_info(&card->dev->dev, "Port %d: %s @%d/%d kb/s%s%s%s%s\n",
+ port, state_str, rate_down/1000, rate_up/1000,
+ snr[0]?", SNR ":"", snr, attn[0]?", Attn ":"", attn);
+
+ card->atmdev[port]->link_rate = rate_down / 424;
+ atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_FOUND);
+
+ return 0;
+}
+
+static int process_command(struct solos_card *card, int port, struct sk_buff *skb)
+{
+ struct solos_param *prm;
+ unsigned long flags;
+ int cmdpid;
+ int found = 0;
+
+ if (skb->len < 7)
+ return 0;
+
+ if (skb->data[0] != 'L' || !isdigit(skb->data[1]) ||
+ !isdigit(skb->data[2]) || !isdigit(skb->data[3]) ||
+ !isdigit(skb->data[4]) || !isdigit(skb->data[5]) ||
+ skb->data[6] != '\n')
+ return 0;
+
+ cmdpid = simple_strtol(&skb->data[1], NULL, 10);
+
+ spin_lock_irqsave(&card->param_queue_lock, flags);
+ list_for_each_entry(prm, &card->param_queue, list) {
+ if (prm->port == port && prm->pid == cmdpid) {
+ prm->response = skb;
+ skb_pull(skb, 7);
+ wake_up(&card->param_wq);
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&card->param_queue_lock, flags);
+ return found;
+}
+
+static ssize_t console_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
+ struct solos_card *card = atmdev->dev_data;
+ struct sk_buff *skb;
+ unsigned int len;
+
+ spin_lock(&card->cli_queue_lock);
+ skb = skb_dequeue(&card->cli_queue[SOLOS_CHAN(atmdev)]);
+ spin_unlock(&card->cli_queue_lock);
+ if(skb == NULL)
+ return sprintf(buf, "No data.\n");
+
+ len = skb->len;
+ memcpy(buf, skb->data, len);
+
+ kfree_skb(skb);
+ return len;
+}
+
+static int send_command(struct solos_card *card, int dev, const char *buf, size_t size)
+{
+ struct sk_buff *skb;
+ struct pkt_hdr *header;
+
+ if (size > (BUF_SIZE - sizeof(*header))) {
+ dev_dbg(&card->dev->dev, "Command is too big. Dropping request\n");
+ return 0;
+ }
+ skb = alloc_skb(size + sizeof(*header), GFP_ATOMIC);
+ if (!skb) {
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in send_command()\n");
+ return 0;
+ }
+
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ header->size = cpu_to_le16(size);
+ header->vpi = cpu_to_le16(0);
+ header->vci = cpu_to_le16(0);
+ header->type = cpu_to_le16(PKT_COMMAND);
+
+ memcpy(skb_put(skb, size), buf, size);
+
+ fpga_queue(card, dev, skb, NULL);
+
+ return 0;
+}
+
+static ssize_t console_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
+ struct solos_card *card = atmdev->dev_data;
+ int err;
+
+ err = send_command(card, SOLOS_CHAN(atmdev), buf, count);
+
+ return err?:count;
+}
+
+struct geos_gpio_attr {
+ struct device_attribute attr;
+ int offset;
+};
+
+#define SOLOS_GPIO_ATTR(_name, _mode, _show, _store, _offset) \
+ struct geos_gpio_attr gpio_attr_##_name = { \
+ .attr = __ATTR(_name, _mode, _show, _store), \
+ .offset = _offset }
+
+static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
+ struct solos_card *card = pci_get_drvdata(pdev);
+ uint32_t data32;
+
+ if (count != 1 && (count != 2 || buf[1] != '\n'))
+ return -EINVAL;
+
+ spin_lock_irq(&card->param_queue_lock);
+ data32 = ioread32(card->config_regs + GPIO_STATUS);
+ if (buf[0] == '1') {
+ data32 |= 1 << gattr->offset;
+ iowrite32(data32, card->config_regs + GPIO_STATUS);
+ } else if (buf[0] == '0') {
+ data32 &= ~(1 << gattr->offset);
+ iowrite32(data32, card->config_regs + GPIO_STATUS);
+ } else {
+ count = -EINVAL;
+ }
+ spin_unlock_irq(&card->param_queue_lock);
+ return count;
+}
+
+static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
+ struct solos_card *card = pci_get_drvdata(pdev);
+ uint32_t data32;
+
+ data32 = ioread32(card->config_regs + GPIO_STATUS);
+ data32 = (data32 >> gattr->offset) & 1;
+
+ return sprintf(buf, "%d\n", data32);
+}
+
+static ssize_t hardware_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
+ struct solos_card *card = pci_get_drvdata(pdev);
+ uint32_t data32;
+
+ data32 = ioread32(card->config_regs + GPIO_STATUS);
+ switch (gattr->offset) {
+ case 0:
+ /* HardwareVersion */
+ data32 = data32 & 0x1F;
+ break;
+ case 1:
+ /* HardwareVariant */
+ data32 = (data32 >> 5) & 0x0F;
+ break;
+ }
+ return sprintf(buf, "%d\n", data32);
+}
+
+static DEVICE_ATTR(console, 0644, console_show, console_store);
+
+
+#define SOLOS_ATTR_RO(x) static DEVICE_ATTR(x, 0444, solos_param_show, NULL);
+#define SOLOS_ATTR_RW(x) static DEVICE_ATTR(x, 0644, solos_param_show, solos_param_store);
+
+#include "solos-attrlist.c"
+
+static SOLOS_GPIO_ATTR(GPIO1, 0644, geos_gpio_show, geos_gpio_store, 9);
+static SOLOS_GPIO_ATTR(GPIO2, 0644, geos_gpio_show, geos_gpio_store, 10);
+static SOLOS_GPIO_ATTR(GPIO3, 0644, geos_gpio_show, geos_gpio_store, 11);
+static SOLOS_GPIO_ATTR(GPIO4, 0644, geos_gpio_show, geos_gpio_store, 12);
+static SOLOS_GPIO_ATTR(GPIO5, 0644, geos_gpio_show, geos_gpio_store, 13);
+static SOLOS_GPIO_ATTR(PushButton, 0444, geos_gpio_show, NULL, 14);
+static SOLOS_GPIO_ATTR(HardwareVersion, 0444, hardware_show, NULL, 0);
+static SOLOS_GPIO_ATTR(HardwareVariant, 0444, hardware_show, NULL, 1);
+#undef SOLOS_ATTR_RO
+#undef SOLOS_ATTR_RW
+
+#define SOLOS_ATTR_RO(x) &dev_attr_##x.attr,
+#define SOLOS_ATTR_RW(x) &dev_attr_##x.attr,
+
+static struct attribute *solos_attrs[] = {
+#include "solos-attrlist.c"
+ NULL
+};
+
+static struct attribute_group solos_attr_group = {
+ .attrs = solos_attrs,
+ .name = "parameters",
+};
+
+static struct attribute *gpio_attrs[] = {
+ &gpio_attr_GPIO1.attr.attr,
+ &gpio_attr_GPIO2.attr.attr,
+ &gpio_attr_GPIO3.attr.attr,
+ &gpio_attr_GPIO4.attr.attr,
+ &gpio_attr_GPIO5.attr.attr,
+ &gpio_attr_PushButton.attr.attr,
+ &gpio_attr_HardwareVersion.attr.attr,
+ &gpio_attr_HardwareVariant.attr.attr,
+ NULL
+};
+
+static struct attribute_group gpio_attr_group = {
+ .attrs = gpio_attrs,
+ .name = "gpio",
+};
+
+static int flash_upgrade(struct solos_card *card, int chip)
+{
+ const struct firmware *fw;
+ const char *fw_name;
+ int blocksize = 0;
+ int numblocks = 0;
+ int offset;
+
+ switch (chip) {
+ case 0:
+ fw_name = "/*(DEBLOBBED)*/";
+ if (card->atmel_flash)
+ blocksize = ATMEL_FPGA_BLOCK;
+ else
+ blocksize = SPI_FLASH_BLOCK;
+ break;
+ case 1:
+ fw_name = "/*(DEBLOBBED)*/";
+ if (card->atmel_flash)
+ blocksize = ATMEL_SOLOS_BLOCK;
+ else
+ blocksize = SPI_FLASH_BLOCK;
+ break;
+ case 2:
+ if (card->fpga_version > LEGACY_BUFFERS){
+ fw_name = "/*(DEBLOBBED)*/";
+ if (card->atmel_flash)
+ blocksize = ATMEL_FPGA_BLOCK;
+ else
+ blocksize = SPI_FLASH_BLOCK;
+ } else {
+ dev_info(&card->dev->dev, "FPGA version doesn't support"
+ " daughter board upgrades\n");
+ return -EPERM;
+ }
+ break;
+ case 3:
+ if (card->fpga_version > LEGACY_BUFFERS){
+ fw_name = "/*(DEBLOBBED)*/";
+ if (card->atmel_flash)
+ blocksize = ATMEL_SOLOS_BLOCK;
+ else
+ blocksize = SPI_FLASH_BLOCK;
+ } else {
+ dev_info(&card->dev->dev, "FPGA version doesn't support"
+ " daughter board upgrades\n");
+ return -EPERM;
+ }
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ if (reject_firmware(&fw, fw_name, &card->dev->dev))
+ return -ENOENT;
+
+ dev_info(&card->dev->dev, "Flash upgrade starting\n");
+
+ /* New FPGAs require driver version before permitting flash upgrades */
+ iowrite32(DRIVER_VERSION, card->config_regs + DRIVER_VER);
+
+ numblocks = fw->size / blocksize;
+ dev_info(&card->dev->dev, "Firmware size: %zd\n", fw->size);
+ dev_info(&card->dev->dev, "Number of blocks: %d\n", numblocks);
+
+ dev_info(&card->dev->dev, "Changing FPGA to Update mode\n");
+ iowrite32(1, card->config_regs + FPGA_MODE);
+ (void) ioread32(card->config_regs + FPGA_MODE);
+
+ /* Set mode to Chip Erase */
+ if(chip == 0 || chip == 2)
+ dev_info(&card->dev->dev, "Set FPGA Flash mode to FPGA Chip Erase\n");
+ if(chip == 1 || chip == 3)
+ dev_info(&card->dev->dev, "Set FPGA Flash mode to Solos Chip Erase\n");
+ iowrite32((chip * 2), card->config_regs + FLASH_MODE);
+
+
+ iowrite32(1, card->config_regs + WRITE_FLASH);
+ wait_event(card->fw_wq, !ioread32(card->config_regs + FLASH_BUSY));
+
+ for (offset = 0; offset < fw->size; offset += blocksize) {
+ int i;
+
+ /* Clear write flag */
+ iowrite32(0, card->config_regs + WRITE_FLASH);
+
+ /* Set mode to Block Write */
+ /* dev_info(&card->dev->dev, "Set FPGA Flash mode to Block Write\n"); */
+ iowrite32(((chip * 2) + 1), card->config_regs + FLASH_MODE);
+
+ /* Copy block to buffer, swapping each 16 bits for Atmel flash */
+ for(i = 0; i < blocksize; i += 4) {
+ uint32_t word;
+ if (card->atmel_flash)
+ word = swahb32p((uint32_t *)(fw->data + offset + i));
+ else
+ word = *(uint32_t *)(fw->data + offset + i);
+ if(card->fpga_version > LEGACY_BUFFERS)
+ iowrite32(word, FLASH_BUF + i);
+ else
+ iowrite32(word, RX_BUF(card, 3) + i);
+ }
+
+ /* Specify block number and then trigger flash write */
+ iowrite32(offset / blocksize, card->config_regs + FLASH_BLOCK);
+ iowrite32(1, card->config_regs + WRITE_FLASH);
+ wait_event(card->fw_wq, !ioread32(card->config_regs + FLASH_BUSY));
+ }
+
+ release_firmware(fw);
+ iowrite32(0, card->config_regs + WRITE_FLASH);
+ iowrite32(0, card->config_regs + FPGA_MODE);
+ iowrite32(0, card->config_regs + FLASH_MODE);
+ dev_info(&card->dev->dev, "Returning FPGA to Data mode\n");
+ return 0;
+}
+
+static irqreturn_t solos_irq(int irq, void *dev_id)
+{
+ struct solos_card *card = dev_id;
+ int handled = 1;
+
+ iowrite32(0, card->config_regs + IRQ_CLEAR);
+
+ /* If we're up and running, just kick the tasklet to process TX/RX */
+ if (card->atmdev[0])
+ tasklet_schedule(&card->tlet);
+ else
+ wake_up(&card->fw_wq);
+
+ return IRQ_RETVAL(handled);
+}
+
+static void solos_bh(unsigned long card_arg)
+{
+ struct solos_card *card = (void *)card_arg;
+ uint32_t card_flags;
+ uint32_t rx_done = 0;
+ int port;
+
+ /*
+ * Since fpga_tx() is going to need to read the flags under its lock,
+ * it can return them to us so that we don't have to hit PCI MMIO
+ * again for the same information
+ */
+ card_flags = fpga_tx(card);
+
+ for (port = 0; port < card->nr_ports; port++) {
+ if (card_flags & (0x10 << port)) {
+ struct pkt_hdr _hdr, *header;
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+ int size;
+
+ if (card->using_dma) {
+ skb = card->rx_skb[port];
+ card->rx_skb[port] = NULL;
+
+ dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr,
+ RX_DMA_SIZE, DMA_FROM_DEVICE);
+
+ header = (void *)skb->data;
+ size = le16_to_cpu(header->size);
+ skb_put(skb, size + sizeof(*header));
+ skb_pull(skb, sizeof(*header));
+ } else {
+ header = &_hdr;
+
+ rx_done |= 0x10 << port;
+
+ memcpy_fromio(header, RX_BUF(card, port), sizeof(*header));
+
+ size = le16_to_cpu(header->size);
+ if (size > (card->buffer_size - sizeof(*header))){
+ dev_warn(&card->dev->dev, "Invalid buffer size\n");
+ continue;
+ }
+
+ skb = alloc_skb(size + 1, GFP_ATOMIC);
+ if (!skb) {
+ if (net_ratelimit())
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n");
+ continue;
+ }
+
+ memcpy_fromio(skb_put(skb, size),
+ RX_BUF(card, port) + sizeof(*header),
+ size);
+ }
+ if (atmdebug) {
+ dev_info(&card->dev->dev, "Received: port %d\n", port);
+ dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n",
+ size, le16_to_cpu(header->vpi),
+ le16_to_cpu(header->vci));
+ print_buffer(skb);
+ }
+
+ switch (le16_to_cpu(header->type)) {
+ case PKT_DATA:
+ vcc = find_vcc(card->atmdev[port], le16_to_cpu(header->vpi),
+ le16_to_cpu(header->vci));
+ if (!vcc) {
+ if (net_ratelimit())
+ dev_warn(&card->dev->dev, "Received packet for unknown VPI.VCI %d.%d on port %d\n",
+ le16_to_cpu(header->vpi), le16_to_cpu(header->vci),
+ port);
+ dev_kfree_skb_any(skb);
+ break;
+ }
+ atm_charge(vcc, skb->truesize);
+ vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
+ break;
+
+ case PKT_STATUS:
+ if (process_status(card, port, skb) &&
+ net_ratelimit()) {
+ dev_warn(&card->dev->dev, "Bad status packet of %d bytes on port %d:\n", skb->len, port);
+ print_buffer(skb);
+ }
+ dev_kfree_skb_any(skb);
+ break;
+
+ case PKT_COMMAND:
+ default: /* FIXME: Not really, surely? */
+ if (process_command(card, port, skb))
+ break;
+ spin_lock(&card->cli_queue_lock);
+ if (skb_queue_len(&card->cli_queue[port]) > 10) {
+ if (net_ratelimit())
+ dev_warn(&card->dev->dev, "Dropping console response on port %d\n",
+ port);
+ dev_kfree_skb_any(skb);
+ } else
+ skb_queue_tail(&card->cli_queue[port], skb);
+ spin_unlock(&card->cli_queue_lock);
+ break;
+ }
+ }
+ /* Allocate RX skbs for any ports which need them */
+ if (card->using_dma && card->atmdev[port] &&
+ !card->rx_skb[port]) {
+ struct sk_buff *skb = alloc_skb(RX_DMA_SIZE, GFP_ATOMIC);
+ if (skb) {
+ SKB_CB(skb)->dma_addr =
+ dma_map_single(&card->dev->dev, skb->data,
+ RX_DMA_SIZE, DMA_FROM_DEVICE);
+ iowrite32(SKB_CB(skb)->dma_addr,
+ card->config_regs + RX_DMA_ADDR(port));
+ card->rx_skb[port] = skb;
+ } else {
+ if (net_ratelimit())
+ dev_warn(&card->dev->dev, "Failed to allocate RX skb");
+
+ /* We'll have to try again later */
+ tasklet_schedule(&card->tlet);
+ }
+ }
+ }
+ if (rx_done)
+ iowrite32(rx_done, card->config_regs + FLAGS_ADDR);
+
+ return;
+}
+
+static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci)
+{
+ struct hlist_head *head;
+ struct atm_vcc *vcc = NULL;
+ struct sock *s;
+
+ read_lock(&vcc_sklist_lock);
+ head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)];
+ sk_for_each(s, head) {
+ vcc = atm_sk(s);
+ if (vcc->dev == dev && vcc->vci == vci &&
+ vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE &&
+ test_bit(ATM_VF_READY, &vcc->flags))
+ goto out;
+ }
+ vcc = NULL;
+ out:
+ read_unlock(&vcc_sklist_lock);
+ return vcc;
+}
+
+static int popen(struct atm_vcc *vcc)
+{
+ struct solos_card *card = vcc->dev->dev_data;
+ struct sk_buff *skb;
+ struct pkt_hdr *header;
+
+ if (vcc->qos.aal != ATM_AAL5) {
+ dev_warn(&card->dev->dev, "Unsupported ATM type %d\n",
+ vcc->qos.aal);
+ return -EINVAL;
+ }
+
+ skb = alloc_skb(sizeof(*header), GFP_KERNEL);
+ if (!skb) {
+ if (net_ratelimit())
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in popen()\n");
+ return -ENOMEM;
+ }
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ header->size = cpu_to_le16(0);
+ header->vpi = cpu_to_le16(vcc->vpi);
+ header->vci = cpu_to_le16(vcc->vci);
+ header->type = cpu_to_le16(PKT_POPEN);
+
+ fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL);
+
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+ set_bit(ATM_VF_READY, &vcc->flags);
+
+ return 0;
+}
+
+static void pclose(struct atm_vcc *vcc)
+{
+ struct solos_card *card = vcc->dev->dev_data;
+ unsigned char port = SOLOS_CHAN(vcc->dev);
+ struct sk_buff *skb, *tmpskb;
+ struct pkt_hdr *header;
+
+ /* Remove any yet-to-be-transmitted packets from the pending queue */
+ spin_lock(&card->tx_queue_lock);
+ skb_queue_walk_safe(&card->tx_queue[port], skb, tmpskb) {
+ if (SKB_CB(skb)->vcc == vcc) {
+ skb_unlink(skb, &card->tx_queue[port]);
+ solos_pop(vcc, skb);
+ }
+ }
+ spin_unlock(&card->tx_queue_lock);
+
+ skb = alloc_skb(sizeof(*header), GFP_KERNEL);
+ if (!skb) {
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in pclose()\n");
+ return;
+ }
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ header->size = cpu_to_le16(0);
+ header->vpi = cpu_to_le16(vcc->vpi);
+ header->vci = cpu_to_le16(vcc->vci);
+ header->type = cpu_to_le16(PKT_PCLOSE);
+
+ skb_get(skb);
+ fpga_queue(card, port, skb, NULL);
+
+ if (!wait_event_timeout(card->param_wq, !skb_shared(skb), 5 * HZ))
+ dev_warn(&card->dev->dev,
+ "Timeout waiting for VCC close on port %d\n", port);
+
+ dev_kfree_skb(skb);
+
+ /* Hold up vcc_destroy_socket() (our caller) until solos_bh() in the
+ tasklet has finished processing any incoming packets (and, more to
+ the point, using the vcc pointer). */
+ tasklet_unlock_wait(&card->tlet);
+
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+
+ return;
+}
+
+static int print_buffer(struct sk_buff *buf)
+{
+ int len,i;
+ char msg[500];
+ char item[10];
+
+ len = buf->len;
+ for (i = 0; i < len; i++){
+ if(i % 8 == 0)
+ sprintf(msg, "%02X: ", i);
+
+ sprintf(item,"%02X ",*(buf->data + i));
+ strcat(msg, item);
+ if(i % 8 == 7) {
+ sprintf(item, "\n");
+ strcat(msg, item);
+ printk(KERN_DEBUG "%s", msg);
+ }
+ }
+ if (i % 8 != 0) {
+ sprintf(item, "\n");
+ strcat(msg, item);
+ printk(KERN_DEBUG "%s", msg);
+ }
+ printk(KERN_DEBUG "\n");
+
+ return 0;
+}
+
+static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb,
+ struct atm_vcc *vcc)
+{
+ int old_len;
+ unsigned long flags;
+
+ SKB_CB(skb)->vcc = vcc;
+
+ spin_lock_irqsave(&card->tx_queue_lock, flags);
+ old_len = skb_queue_len(&card->tx_queue[port]);
+ skb_queue_tail(&card->tx_queue[port], skb);
+ if (!old_len)
+ card->tx_mask |= (1 << port);
+ spin_unlock_irqrestore(&card->tx_queue_lock, flags);
+
+ /* Theoretically we could just schedule the tasklet here, but
+ that introduces latency we don't want -- it's noticeable */
+ if (!old_len)
+ fpga_tx(card);
+}
+
+static uint32_t fpga_tx(struct solos_card *card)
+{
+ uint32_t tx_pending, card_flags;
+ uint32_t tx_started = 0;
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+ unsigned char port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->tx_lock, flags);
+
+ card_flags = ioread32(card->config_regs + FLAGS_ADDR);
+ /*
+ * The queue lock is required for _writing_ to tx_mask, but we're
+ * OK to read it here without locking. The only potential update
+ * that we could race with is in fpga_queue() where it sets a bit
+ * for a new port... but it's going to call this function again if
+ * it's doing that, anyway.
+ */
+ tx_pending = card->tx_mask & ~card_flags;
+
+ for (port = 0; tx_pending; tx_pending >>= 1, port++) {
+ if (tx_pending & 1) {
+ struct sk_buff *oldskb = card->tx_skb[port];
+ if (oldskb) {
+ dma_unmap_single(&card->dev->dev, SKB_CB(oldskb)->dma_addr,
+ oldskb->len, DMA_TO_DEVICE);
+ card->tx_skb[port] = NULL;
+ }
+ spin_lock(&card->tx_queue_lock);
+ skb = skb_dequeue(&card->tx_queue[port]);
+ if (!skb)
+ card->tx_mask &= ~(1 << port);
+ spin_unlock(&card->tx_queue_lock);
+
+ if (skb && !card->using_dma) {
+ memcpy_toio(TX_BUF(card, port), skb->data, skb->len);
+ tx_started |= 1 << port;
+ oldskb = skb; /* We're done with this skb already */
+ } else if (skb && card->using_dma) {
+ unsigned char *data = skb->data;
+ if ((unsigned long)data & card->dma_alignment) {
+ data = card->dma_bounce + (BUF_SIZE * port);
+ memcpy(data, skb->data, skb->len);
+ }
+ SKB_CB(skb)->dma_addr = dma_map_single(&card->dev->dev, data,
+ skb->len, DMA_TO_DEVICE);
+ card->tx_skb[port] = skb;
+ iowrite32(SKB_CB(skb)->dma_addr,
+ card->config_regs + TX_DMA_ADDR(port));
+ }
+
+ if (!oldskb)
+ continue;
+
+ /* Clean up and free oldskb now it's gone */
+ if (atmdebug) {
+ struct pkt_hdr *header = (void *)oldskb->data;
+ int size = le16_to_cpu(header->size);
+
+ skb_pull(oldskb, sizeof(*header));
+ dev_info(&card->dev->dev, "Transmitted: port %d\n",
+ port);
+ dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n",
+ size, le16_to_cpu(header->vpi),
+ le16_to_cpu(header->vci));
+ print_buffer(oldskb);
+ }
+
+ vcc = SKB_CB(oldskb)->vcc;
+
+ if (vcc) {
+ atomic_inc(&vcc->stats->tx);
+ solos_pop(vcc, oldskb);
+ } else {
+ dev_kfree_skb_irq(oldskb);
+ wake_up(&card->param_wq);
+ }
+ }
+ }
+ /* For non-DMA TX, write the 'TX start' bit for all four ports simultaneously */
+ if (tx_started)
+ iowrite32(tx_started, card->config_regs + FLAGS_ADDR);
+
+ spin_unlock_irqrestore(&card->tx_lock, flags);
+ return card_flags;
+}
+
+static int psend(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct solos_card *card = vcc->dev->dev_data;
+ struct pkt_hdr *header;
+ int pktlen;
+
+ pktlen = skb->len;
+ if (pktlen > (BUF_SIZE - sizeof(*header))) {
+ dev_warn(&card->dev->dev, "Length of PDU is too large. Dropping PDU.\n");
+ solos_pop(vcc, skb);
+ return 0;
+ }
+
+ if (!skb_clone_writable(skb, sizeof(*header))) {
+ int expand_by = 0;
+ int ret;
+
+ if (skb_headroom(skb) < sizeof(*header))
+ expand_by = sizeof(*header) - skb_headroom(skb);
+
+ ret = pskb_expand_head(skb, expand_by, 0, GFP_ATOMIC);
+ if (ret) {
+ dev_warn(&card->dev->dev, "pskb_expand_head failed.\n");
+ solos_pop(vcc, skb);
+ return ret;
+ }
+ }
+
+ header = (void *)skb_push(skb, sizeof(*header));
+
+ /* This does _not_ include the size of the header */
+ header->size = cpu_to_le16(pktlen);
+ header->vpi = cpu_to_le16(vcc->vpi);
+ header->vci = cpu_to_le16(vcc->vci);
+ header->type = cpu_to_le16(PKT_DATA);
+
+ fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, vcc);
+
+ return 0;
+}
+
+static struct atmdev_ops fpga_ops = {
+ .open = popen,
+ .close = pclose,
+ .ioctl = NULL,
+ .getsockopt = NULL,
+ .setsockopt = NULL,
+ .send = psend,
+ .send_oam = NULL,
+ .phy_put = NULL,
+ .phy_get = NULL,
+ .change_qos = NULL,
+ .proc_read = NULL,
+ .owner = THIS_MODULE
+};
+
+static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ int err;
+ uint16_t fpga_ver;
+ uint8_t major_ver, minor_ver;
+ uint32_t data32;
+ struct solos_card *card;
+
+ card = kzalloc(sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+ init_waitqueue_head(&card->fw_wq);
+ init_waitqueue_head(&card->param_wq);
+
+ err = pci_enable_device(dev);
+ if (err) {
+ dev_warn(&dev->dev, "Failed to enable PCI device\n");
+ goto out;
+ }
+
+ err = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_warn(&dev->dev, "Failed to set 32-bit DMA mask\n");
+ goto out;
+ }
+
+ err = pci_request_regions(dev, "solos");
+ if (err) {
+ dev_warn(&dev->dev, "Failed to request regions\n");
+ goto out;
+ }
+
+ card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE);
+ if (!card->config_regs) {
+ dev_warn(&dev->dev, "Failed to ioremap config registers\n");
+ err = -ENOMEM;
+ goto out_release_regions;
+ }
+ card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE);
+ if (!card->buffers) {
+ dev_warn(&dev->dev, "Failed to ioremap data buffers\n");
+ err = -ENOMEM;
+ goto out_unmap_config;
+ }
+
+ if (reset) {
+ iowrite32(1, card->config_regs + FPGA_MODE);
+ data32 = ioread32(card->config_regs + FPGA_MODE);
+
+ iowrite32(0, card->config_regs + FPGA_MODE);
+ data32 = ioread32(card->config_regs + FPGA_MODE);
+ }
+
+ data32 = ioread32(card->config_regs + FPGA_VER);
+ fpga_ver = (data32 & 0x0000FFFF);
+ major_ver = ((data32 & 0xFF000000) >> 24);
+ minor_ver = ((data32 & 0x00FF0000) >> 16);
+ card->fpga_version = FPGA_VERSION(major_ver,minor_ver);
+ if (card->fpga_version > LEGACY_BUFFERS)
+ card->buffer_size = BUF_SIZE;
+ else
+ card->buffer_size = OLD_BUF_SIZE;
+ dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n",
+ major_ver, minor_ver, fpga_ver);
+
+ if (fpga_ver < 37 && (fpga_upgrade || firmware_upgrade ||
+ db_fpga_upgrade || db_firmware_upgrade)) {
+ dev_warn(&dev->dev,
+ "FPGA too old; cannot upgrade flash. Use JTAG.\n");
+ fpga_upgrade = firmware_upgrade = 0;
+ db_fpga_upgrade = db_firmware_upgrade = 0;
+ }
+
+ /* Stopped using Atmel flash after 0.03-38 */
+ if (fpga_ver < 39)
+ card->atmel_flash = 1;
+ else
+ card->atmel_flash = 0;
+
+ data32 = ioread32(card->config_regs + PORTS);
+ card->nr_ports = (data32 & 0x000000FF);
+
+ if (card->fpga_version >= DMA_SUPPORTED) {
+ pci_set_master(dev);
+ card->using_dma = 1;
+ if (1) { /* All known FPGA versions so far */
+ card->dma_alignment = 3;
+ card->dma_bounce = kmalloc(card->nr_ports * BUF_SIZE, GFP_KERNEL);
+ if (!card->dma_bounce) {
+ dev_warn(&card->dev->dev, "Failed to allocate DMA bounce buffers\n");
+ err = -ENOMEM;
+ /* Fallback to MMIO doesn't work */
+ goto out_unmap_both;
+ }
+ }
+ } else {
+ card->using_dma = 0;
+ /* Set RX empty flag for all ports */
+ iowrite32(0xF0, card->config_regs + FLAGS_ADDR);
+ }
+
+ pci_set_drvdata(dev, card);
+
+ tasklet_init(&card->tlet, solos_bh, (unsigned long)card);
+ spin_lock_init(&card->tx_lock);
+ spin_lock_init(&card->tx_queue_lock);
+ spin_lock_init(&card->cli_queue_lock);
+ spin_lock_init(&card->param_queue_lock);
+ INIT_LIST_HEAD(&card->param_queue);
+
+ err = request_irq(dev->irq, solos_irq, IRQF_SHARED,
+ "solos-pci", card);
+ if (err) {
+ dev_dbg(&card->dev->dev, "Failed to request interrupt IRQ: %d\n", dev->irq);
+ goto out_unmap_both;
+ }
+
+ iowrite32(1, card->config_regs + IRQ_EN_ADDR);
+
+ if (fpga_upgrade)
+ flash_upgrade(card, 0);
+
+ if (firmware_upgrade)
+ flash_upgrade(card, 1);
+
+ if (db_fpga_upgrade)
+ flash_upgrade(card, 2);
+
+ if (db_firmware_upgrade)
+ flash_upgrade(card, 3);
+
+ err = atm_init(card, &dev->dev);
+ if (err)
+ goto out_free_irq;
+
+ if (card->fpga_version >= DMA_SUPPORTED &&
+ sysfs_create_group(&card->dev->dev.kobj, &gpio_attr_group))
+ dev_err(&card->dev->dev, "Could not register parameter group for GPIOs\n");
+
+ return 0;
+
+ out_free_irq:
+ iowrite32(0, card->config_regs + IRQ_EN_ADDR);
+ free_irq(dev->irq, card);
+ tasklet_kill(&card->tlet);
+
+ out_unmap_both:
+ kfree(card->dma_bounce);
+ pci_iounmap(dev, card->buffers);
+ out_unmap_config:
+ pci_iounmap(dev, card->config_regs);
+ out_release_regions:
+ pci_release_regions(dev);
+ out:
+ kfree(card);
+ return err;
+}
+
+static int atm_init(struct solos_card *card, struct device *parent)
+{
+ int i;
+
+ for (i = 0; i < card->nr_ports; i++) {
+ struct sk_buff *skb;
+ struct pkt_hdr *header;
+
+ skb_queue_head_init(&card->tx_queue[i]);
+ skb_queue_head_init(&card->cli_queue[i]);
+
+ card->atmdev[i] = atm_dev_register("solos-pci", parent, &fpga_ops, -1, NULL);
+ if (!card->atmdev[i]) {
+ dev_err(&card->dev->dev, "Could not register ATM device %d\n", i);
+ atm_remove(card);
+ return -ENODEV;
+ }
+ if (device_create_file(&card->atmdev[i]->class_dev, &dev_attr_console))
+ dev_err(&card->dev->dev, "Could not register console for ATM device %d\n", i);
+ if (sysfs_create_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group))
+ dev_err(&card->dev->dev, "Could not register parameter group for ATM device %d\n", i);
+
+ dev_info(&card->dev->dev, "Registered ATM device %d\n", card->atmdev[i]->number);
+
+ card->atmdev[i]->ci_range.vpi_bits = 8;
+ card->atmdev[i]->ci_range.vci_bits = 16;
+ card->atmdev[i]->dev_data = card;
+ card->atmdev[i]->phy_data = (void *)(unsigned long)i;
+ atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_FOUND);
+
+ skb = alloc_skb(sizeof(*header), GFP_KERNEL);
+ if (!skb) {
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in atm_init()\n");
+ continue;
+ }
+
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ header->size = cpu_to_le16(0);
+ header->vpi = cpu_to_le16(0);
+ header->vci = cpu_to_le16(0);
+ header->type = cpu_to_le16(PKT_STATUS);
+
+ fpga_queue(card, i, skb, NULL);
+ }
+ return 0;
+}
+
+static void atm_remove(struct solos_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->nr_ports; i++) {
+ if (card->atmdev[i]) {
+ struct sk_buff *skb;
+
+ dev_info(&card->dev->dev, "Unregistering ATM device %d\n", card->atmdev[i]->number);
+
+ sysfs_remove_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group);
+ atm_dev_deregister(card->atmdev[i]);
+
+ skb = card->rx_skb[i];
+ if (skb) {
+ dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr,
+ RX_DMA_SIZE, DMA_FROM_DEVICE);
+ dev_kfree_skb(skb);
+ }
+ skb = card->tx_skb[i];
+ if (skb) {
+ dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr,
+ skb->len, DMA_TO_DEVICE);
+ dev_kfree_skb(skb);
+ }
+ while ((skb = skb_dequeue(&card->tx_queue[i])))
+ dev_kfree_skb(skb);
+
+ }
+ }
+}
+
+static void fpga_remove(struct pci_dev *dev)
+{
+ struct solos_card *card = pci_get_drvdata(dev);
+
+ /* Disable IRQs */
+ iowrite32(0, card->config_regs + IRQ_EN_ADDR);
+
+ /* Reset FPGA */
+ iowrite32(1, card->config_regs + FPGA_MODE);
+ (void)ioread32(card->config_regs + FPGA_MODE);
+
+ if (card->fpga_version >= DMA_SUPPORTED)
+ sysfs_remove_group(&card->dev->dev.kobj, &gpio_attr_group);
+
+ atm_remove(card);
+
+ free_irq(dev->irq, card);
+ tasklet_kill(&card->tlet);
+
+ kfree(card->dma_bounce);
+
+ /* Release device from reset */
+ iowrite32(0, card->config_regs + FPGA_MODE);
+ (void)ioread32(card->config_regs + FPGA_MODE);
+
+ pci_iounmap(dev, card->buffers);
+ pci_iounmap(dev, card->config_regs);
+
+ pci_release_regions(dev);
+ pci_disable_device(dev);
+
+ kfree(card);
+}
+
+static struct pci_device_id fpga_pci_tbl[] = {
+ { 0x10ee, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci,fpga_pci_tbl);
+
+static struct pci_driver fpga_driver = {
+ .name = "solos",
+ .id_table = fpga_pci_tbl,
+ .probe = fpga_probe,
+ .remove = fpga_remove,
+};
+
+
+static int __init solos_pci_init(void)
+{
+ BUILD_BUG_ON(sizeof(struct solos_skb_cb) > sizeof(((struct sk_buff *)0)->cb));
+
+ printk(KERN_INFO "Solos PCI Driver Version %s\n", VERSION);
+ return pci_register_driver(&fpga_driver);
+}
+
+static void __exit solos_pci_exit(void)
+{
+ pci_unregister_driver(&fpga_driver);
+ printk(KERN_INFO "Solos PCI Driver %s Unloaded\n", VERSION);
+}
+
+module_init(solos_pci_init);
+module_exit(solos_pci_exit);
diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c
new file mode 100644
index 000000000..021593455
--- /dev/null
+++ b/drivers/atm/suni.c
@@ -0,0 +1,392 @@
+/*
+ * drivers/atm/suni.c - S/UNI PHY driver
+ *
+ * Supports the following:
+ * PMC PM5346 S/UNI LITE
+ * PMC PM5350 S/UNI 155 ULTRA
+ * PMC PM5355 S/UNI 622
+ */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/atm_suni.h>
+#include <linux/slab.h>
+#include <asm/param.h>
+#include <asm/uaccess.h>
+#include <linux/atomic.h>
+
+#include "suni.h"
+
+
+#if 0
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+#define PRIV(dev) ((struct suni_priv *) dev->phy_data)
+
+#define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg)
+#define GET(reg) dev->ops->phy_get(dev,SUNI_##reg)
+#define REG_CHANGE(mask,shift,value,reg) \
+ PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg)
+
+
+static struct timer_list poll_timer;
+static struct suni_priv *sunis = NULL;
+static DEFINE_SPINLOCK(sunis_lock);
+
+
+#define ADD_LIMITED(s,v) \
+ atomic_add((v),&stats->s); \
+ if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX);
+
+
+static void suni_hz(unsigned long from_timer)
+{
+ struct suni_priv *walk;
+ struct atm_dev *dev;
+ struct k_sonet_stats *stats;
+
+ for (walk = sunis; walk; walk = walk->next) {
+ dev = walk->dev;
+ stats = &walk->sonet_stats;
+ PUT(0,MRI); /* latch counters */
+ udelay(1);
+ ADD_LIMITED(section_bip,(GET(RSOP_SBL) & 0xff) |
+ ((GET(RSOP_SBM) & 0xff) << 8));
+ ADD_LIMITED(line_bip,(GET(RLOP_LBL) & 0xff) |
+ ((GET(RLOP_LB) & 0xff) << 8) |
+ ((GET(RLOP_LBM) & 0xf) << 16));
+ ADD_LIMITED(path_bip,(GET(RPOP_PBL) & 0xff) |
+ ((GET(RPOP_PBM) & 0xff) << 8));
+ ADD_LIMITED(line_febe,(GET(RLOP_LFL) & 0xff) |
+ ((GET(RLOP_LF) & 0xff) << 8) |
+ ((GET(RLOP_LFM) & 0xf) << 16));
+ ADD_LIMITED(path_febe,(GET(RPOP_PFL) & 0xff) |
+ ((GET(RPOP_PFM) & 0xff) << 8));
+ ADD_LIMITED(corr_hcs,GET(RACP_CHEC) & 0xff);
+ ADD_LIMITED(uncorr_hcs,GET(RACP_UHEC) & 0xff);
+ ADD_LIMITED(rx_cells,(GET(RACP_RCCL) & 0xff) |
+ ((GET(RACP_RCC) & 0xff) << 8) |
+ ((GET(RACP_RCCM) & 7) << 16));
+ ADD_LIMITED(tx_cells,(GET(TACP_TCCL) & 0xff) |
+ ((GET(TACP_TCC) & 0xff) << 8) |
+ ((GET(TACP_TCCM) & 7) << 16));
+ }
+ if (from_timer) mod_timer(&poll_timer,jiffies+HZ);
+}
+
+
+#undef ADD_LIMITED
+
+
+static int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero)
+{
+ struct sonet_stats tmp;
+ int error = 0;
+
+ sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp);
+ if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
+ if (zero && !error) sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp);
+ return error ? -EFAULT : 0;
+}
+
+
+#define HANDLE_FLAG(flag,reg,bit) \
+ if (todo & flag) { \
+ if (set) PUT(GET(reg) | bit,reg); \
+ else PUT(GET(reg) & ~bit,reg); \
+ todo &= ~flag; \
+ }
+
+
+static int change_diag(struct atm_dev *dev,void __user *arg,int set)
+{
+ int todo;
+
+ if (get_user(todo,(int __user *)arg)) return -EFAULT;
+ HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8);
+ HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP);
+ HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3);
+ HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF);
+ HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS);
+ HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS);
+ HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS);
+ HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS);
+ return put_user(todo,(int __user *)arg) ? -EFAULT : 0;
+}
+
+
+#undef HANDLE_FLAG
+
+
+static int get_diag(struct atm_dev *dev,void __user *arg)
+{
+ int set;
+
+ set = 0;
+ if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP;
+ if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP;
+ if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP;
+ /* SONET_INS_FRAME is one-shot only */
+ if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS;
+ if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS;
+ if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS;
+ if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS;
+ return put_user(set,(int __user *)arg) ? -EFAULT : 0;
+}
+
+
+static int set_loopback(struct atm_dev *dev,int mode)
+{
+ unsigned char control;
+ int reg, dle, lle;
+
+ if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
+ reg = SUNI_MCM;
+ dle = SUNI_MCM_DLE;
+ lle = SUNI_MCM_LLE;
+ } else {
+ reg = SUNI_MCT;
+ dle = SUNI_MCT_DLE;
+ lle = SUNI_MCT_LLE;
+ }
+
+ control = dev->ops->phy_get(dev, reg) & ~(dle | lle);
+ switch (mode) {
+ case ATM_LM_NONE:
+ break;
+ case ATM_LM_LOC_PHY:
+ control |= dle;
+ break;
+ case ATM_LM_RMT_PHY:
+ control |= lle;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev->ops->phy_put(dev, control, reg);
+ PRIV(dev)->loop_mode = mode;
+ return 0;
+}
+
+/*
+ * SONET vs. SDH Configuration
+ *
+ * Z0INS (register 0x06): 0 for SONET, 1 for SDH
+ * ENSS (register 0x3D): 0 for SONET, 1 for SDH
+ * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD)
+ * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD)
+ * S[1:0] (register 0x46): 00 for SONET, 10 for SDH
+ */
+
+static int set_sonet(struct atm_dev *dev)
+{
+ if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
+ PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC);
+ PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL);
+ PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL);
+ }
+
+ REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT,
+ SUNI_TPOP_S_SONET, TPOP_APM);
+
+ return 0;
+}
+
+static int set_sdh(struct atm_dev *dev)
+{
+ if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) {
+ PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC);
+ PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL);
+ PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL);
+ }
+
+ REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT,
+ SUNI_TPOP_S_SDH, TPOP_APM);
+
+ return 0;
+}
+
+
+static int get_framing(struct atm_dev *dev, void __user *arg)
+{
+ int framing;
+ unsigned char s;
+
+
+ s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT;
+ if (s == SUNI_TPOP_S_SONET)
+ framing = SONET_FRAME_SONET;
+ else
+ framing = SONET_FRAME_SDH;
+
+ return put_user(framing, (int __user *) arg) ? -EFAULT : 0;
+}
+
+static int set_framing(struct atm_dev *dev, void __user *arg)
+{
+ int mode;
+
+ if (get_user(mode, (int __user *) arg))
+ return -EFAULT;
+
+ if (mode == SONET_FRAME_SONET)
+ return set_sonet(dev);
+ else if (mode == SONET_FRAME_SDH)
+ return set_sdh(dev);
+
+ return -EINVAL;
+}
+
+
+static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
+{
+ switch (cmd) {
+ case SONET_GETSTATZ:
+ case SONET_GETSTAT:
+ return fetch_stats(dev, arg, cmd == SONET_GETSTATZ);
+ case SONET_SETDIAG:
+ return change_diag(dev,arg,1);
+ case SONET_CLRDIAG:
+ return change_diag(dev,arg,0);
+ case SONET_GETDIAG:
+ return get_diag(dev,arg);
+ case SONET_SETFRAMING:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ return set_framing(dev, arg);
+ case SONET_GETFRAMING:
+ return get_framing(dev, arg);
+ case SONET_GETFRSENSE:
+ return -EINVAL;
+ case ATM_SETLOOP:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ return set_loopback(dev,(int)(unsigned long)arg);
+ case ATM_GETLOOP:
+ return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ?
+ -EFAULT : 0;
+ case ATM_QUERYLOOP:
+ return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY,
+ (int __user *) arg) ? -EFAULT : 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+
+static void poll_los(struct atm_dev *dev)
+{
+ atm_dev_signal_change(dev,
+ GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ?
+ ATM_PHY_SIG_LOST : ATM_PHY_SIG_FOUND);
+}
+
+
+static void suni_int(struct atm_dev *dev)
+{
+ poll_los(dev);
+ printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number,
+ dev->signal == ATM_PHY_SIG_LOST ? "lost" : "detected again");
+}
+
+
+static int suni_start(struct atm_dev *dev)
+{
+ unsigned long flags;
+ int first;
+
+ spin_lock_irqsave(&sunis_lock,flags);
+ first = !sunis;
+ PRIV(dev)->next = sunis;
+ sunis = PRIV(dev);
+ spin_unlock_irqrestore(&sunis_lock,flags);
+ memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats));
+ PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE);
+ /* interrupt on loss of signal */
+ poll_los(dev); /* ... and clear SUNI interrupts */
+ if (dev->signal == ATM_PHY_SIG_LOST)
+ printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type,
+ dev->number);
+ PRIV(dev)->loop_mode = ATM_LM_NONE;
+ suni_hz(0); /* clear SUNI counters */
+ (void) fetch_stats(dev,NULL,1); /* clear kernel counters */
+ if (first) {
+ init_timer(&poll_timer);
+ poll_timer.expires = jiffies+HZ;
+ poll_timer.function = suni_hz;
+ poll_timer.data = 1;
+#if 0
+printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.list.prev,
+ (unsigned long) poll_timer.list.next);
+#endif
+ add_timer(&poll_timer);
+ }
+ return 0;
+}
+
+
+static int suni_stop(struct atm_dev *dev)
+{
+ struct suni_priv **walk;
+ unsigned long flags;
+
+ /* let SAR driver worry about stopping interrupts */
+ spin_lock_irqsave(&sunis_lock,flags);
+ for (walk = &sunis; *walk != PRIV(dev);
+ walk = &PRIV((*walk)->dev)->next);
+ *walk = PRIV((*walk)->dev)->next;
+ if (!sunis) del_timer_sync(&poll_timer);
+ spin_unlock_irqrestore(&sunis_lock,flags);
+ kfree(PRIV(dev));
+
+ return 0;
+}
+
+
+static const struct atmphy_ops suni_ops = {
+ .start = suni_start,
+ .ioctl = suni_ioctl,
+ .interrupt = suni_int,
+ .stop = suni_stop,
+};
+
+
+int suni_init(struct atm_dev *dev)
+{
+ unsigned char mri;
+
+ if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
+ return -ENOMEM;
+ PRIV(dev)->dev = dev;
+
+ mri = GET(MRI); /* reset SUNI */
+ PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT;
+ PUT(mri | SUNI_MRI_RESET,MRI);
+ PUT(mri,MRI);
+ PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */
+ set_sonet(dev);
+ REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP,
+ TACP_IUCHP); /* idle cells */
+ PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP);
+ dev->phy = &suni_ops;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(suni_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/atm/suni.h b/drivers/atm/suni.h
new file mode 100644
index 000000000..7e3e656b3
--- /dev/null
+++ b/drivers/atm/suni.h
@@ -0,0 +1,241 @@
+/*
+ * drivers/atm/suni.h - S/UNI PHY driver
+ */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+#ifndef DRIVER_ATM_SUNI_H
+#define DRIVER_ATM_SUNI_H
+
+#include <linux/atmdev.h>
+#include <linux/atmioc.h>
+#include <linux/sonet.h>
+
+/* SUNI registers */
+
+#define SUNI_MRI 0x00 /* Master Reset and Identity / Load
+ Meter */
+#define SUNI_MC 0x01 /* Master Configuration */
+#define SUNI_MIS 0x02 /* Master Interrupt Status */
+ /* no 0x03 */
+#define SUNI_MCM 0x04 /* Master Clock Monitor */
+#define SUNI_MCT 0x05 /* Master Control */
+#define SUNI_CSCS 0x06 /* Clock Synthesis Control and Status */
+#define SUNI_CRCS 0x07 /* Clock Recovery Control and Status */
+ /* 0x08-0x0F reserved */
+#define SUNI_RSOP_CIE 0x10 /* RSOP Control/Interrupt Enable */
+#define SUNI_RSOP_SIS 0x11 /* RSOP Status/Interrupt Status */
+#define SUNI_RSOP_SBL 0x12 /* RSOP Section BIP-8 LSB */
+#define SUNI_RSOP_SBM 0x13 /* RSOP Section BIP-8 MSB */
+#define SUNI_TSOP_CTRL 0x14 /* TSOP Control */
+#define SUNI_TSOP_DIAG 0x15 /* TSOP Diagnostic */
+ /* 0x16-0x17 reserved */
+#define SUNI_RLOP_CS 0x18 /* RLOP Control/Status */
+#define SUNI_RLOP_IES 0x19 /* RLOP Interrupt Enable/Status */
+#define SUNI_RLOP_LBL 0x1A /* RLOP Line BIP-8/24 LSB */
+#define SUNI_RLOP_LB 0x1B /* RLOP Line BIP-8/24 */
+#define SUNI_RLOP_LBM 0x1C /* RLOP Line BIP-8/24 MSB */
+#define SUNI_RLOP_LFL 0x1D /* RLOP Line FEBE LSB */
+#define SUNI_RLOP_LF 0x1E /* RLOP Line FEBE */
+#define SUNI_RLOP_LFM 0x1F /* RLOP Line FEBE MSB */
+#define SUNI_TLOP_CTRL 0x20 /* TLOP Control */
+#define SUNI_TLOP_DIAG 0x21 /* TLOP Diagnostic */
+ /* 0x22-0x27 reserved */
+#define SUNI_SSTB_CTRL 0x28
+#define SUNI_RPOP_SC 0x30 /* RPOP Status/Control */
+#define SUNI_RPOP_IS 0x31 /* RPOP Interrupt Status */
+ /* 0x32 reserved */
+#define SUNI_RPOP_IE 0x33 /* RPOP Interrupt Enable */
+ /* 0x34-0x36 reserved */
+#define SUNI_RPOP_PSL 0x37 /* RPOP Path Signal Label */
+#define SUNI_RPOP_PBL 0x38 /* RPOP Path BIP-8 LSB */
+#define SUNI_RPOP_PBM 0x39 /* RPOP Path BIP-8 MSB */
+#define SUNI_RPOP_PFL 0x3A /* RPOP Path FEBE LSB */
+#define SUNI_RPOP_PFM 0x3B /* RPOP Path FEBE MSB */
+ /* 0x3C reserved */
+#define SUNI_RPOP_PBC 0x3D /* RPOP Path BIP-8 Configuration */
+#define SUNI_RPOP_RC 0x3D /* RPOP Ring Control (PM5355) */
+ /* 0x3E-0x3F reserved */
+#define SUNI_TPOP_CD 0x40 /* TPOP Control/Diagnostic */
+#define SUNI_TPOP_PC 0x41 /* TPOP Pointer Control */
+ /* 0x42-0x44 reserved */
+#define SUNI_TPOP_APL 0x45 /* TPOP Arbitrary Pointer LSB */
+#define SUNI_TPOP_APM 0x46 /* TPOP Arbitrary Pointer MSB */
+ /* 0x47 reserved */
+#define SUNI_TPOP_PSL 0x48 /* TPOP Path Signal Label */
+#define SUNI_TPOP_PS 0x49 /* TPOP Path Status */
+ /* 0x4A-0x4F reserved */
+#define SUNI_RACP_CS 0x50 /* RACP Control/Status */
+#define SUNI_RACP_IES 0x51 /* RACP Interrupt Enable/Status */
+#define SUNI_RACP_MHP 0x52 /* RACP Match Header Pattern */
+#define SUNI_RACP_MHM 0x53 /* RACP Match Header Mask */
+#define SUNI_RACP_CHEC 0x54 /* RACP Correctable HCS Error Count */
+#define SUNI_RACP_UHEC 0x55 /* RACP Uncorrectable HCS Err Count */
+#define SUNI_RACP_RCCL 0x56 /* RACP Receive Cell Counter LSB */
+#define SUNI_RACP_RCC 0x57 /* RACP Receive Cell Counter */
+#define SUNI_RACP_RCCM 0x58 /* RACP Receive Cell Counter MSB */
+#define SUNI_RACP_CFG 0x59 /* RACP Configuration */
+ /* 0x5A-0x5F reserved */
+#define SUNI_TACP_CS 0x60 /* TACP Control/Status */
+#define SUNI_TACP_IUCHP 0x61 /* TACP Idle/Unassigned Cell Hdr Pat */
+#define SUNI_TACP_IUCPOP 0x62 /* TACP Idle/Unassigned Cell Payload
+ Octet Pattern */
+#define SUNI_TACP_FIFO 0x63 /* TACP FIFO Configuration */
+#define SUNI_TACP_TCCL 0x64 /* TACP Transmit Cell Counter LSB */
+#define SUNI_TACP_TCC 0x65 /* TACP Transmit Cell Counter */
+#define SUNI_TACP_TCCM 0x66 /* TACP Transmit Cell Counter MSB */
+#define SUNI_TACP_CFG 0x67 /* TACP Configuration */
+#define SUNI_SPTB_CTRL 0x68 /* SPTB Control */
+ /* 0x69-0x7F reserved */
+#define SUNI_MT 0x80 /* Master Test */
+ /* 0x81-0xFF reserved */
+
+/* SUNI register values */
+
+
+/* MRI is reg 0 */
+#define SUNI_MRI_ID 0x0f /* R, SUNI revision number */
+#define SUNI_MRI_ID_SHIFT 0
+#define SUNI_MRI_TYPE 0x70 /* R, SUNI type (lite is 011) */
+#define SUNI_MRI_TYPE_SHIFT 4
+#define SUNI_MRI_TYPE_PM5346 0x3 /* S/UNI 155 LITE */
+#define SUNI_MRI_TYPE_PM5347 0x4 /* S/UNI 155 PLUS */
+#define SUNI_MRI_TYPE_PM5350 0x7 /* S/UNI 155 ULTRA */
+#define SUNI_MRI_TYPE_PM5355 0x1 /* S/UNI 622 */
+#define SUNI_MRI_RESET 0x80 /* RW, reset & power down chip
+ 0: normal operation
+ 1: reset & low power */
+
+/* MCM is reg 0x4 */
+#define SUNI_MCM_LLE 0x20 /* line loopback (PM5355) */
+#define SUNI_MCM_DLE 0x10 /* diagnostic loopback (PM5355) */
+
+/* MCT is reg 5 */
+#define SUNI_MCT_LOOPT 0x01 /* RW, timing source, 0: from
+ TRCLK+/- */
+#define SUNI_MCT_DLE 0x02 /* RW, diagnostic loopback */
+#define SUNI_MCT_LLE 0x04 /* RW, line loopback */
+#define SUNI_MCT_FIXPTR 0x20 /* RW, disable transmit payload pointer
+ adjustments
+ 0: payload ptr controlled by TPOP
+ ptr control reg
+ 1: payload pointer fixed at 522 */
+#define SUNI_MCT_LCDV 0x40 /* R, loss of cell delineation */
+#define SUNI_MCT_LCDE 0x80 /* RW, loss of cell delineation
+ interrupt (1: on) */
+/* RSOP_CIE is reg 0x10 */
+#define SUNI_RSOP_CIE_OOFE 0x01 /* RW, enable interrupt on frame alarm
+ state change */
+#define SUNI_RSOP_CIE_LOFE 0x02 /* RW, enable interrupt on loss of
+ frame state change */
+#define SUNI_RSOP_CIE_LOSE 0x04 /* RW, enable interrupt on loss of
+ signal state change */
+#define SUNI_RSOP_CIE_BIPEE 0x08 /* RW, enable interrupt on section
+ BIP-8 error (B1) */
+#define SUNI_RSOP_CIE_FOOF 0x20 /* W, force RSOP out of frame at next
+ boundary */
+#define SUNI_RSOP_CIE_DDS 0x40 /* RW, disable scrambling */
+
+/* RSOP_SIS is reg 0x11 */
+#define SUNI_RSOP_SIS_OOFV 0x01 /* R, out of frame */
+#define SUNI_RSOP_SIS_LOFV 0x02 /* R, loss of frame */
+#define SUNI_RSOP_SIS_LOSV 0x04 /* R, loss of signal */
+#define SUNI_RSOP_SIS_OOFI 0x08 /* R, out of frame interrupt */
+#define SUNI_RSOP_SIS_LOFI 0x10 /* R, loss of frame interrupt */
+#define SUNI_RSOP_SIS_LOSI 0x20 /* R, loss of signal interrupt */
+#define SUNI_RSOP_SIS_BIPEI 0x40 /* R, section BIP-8 interrupt */
+
+/* TSOP_CTRL is reg 0x14 */
+#define SUNI_TSOP_CTRL_LAIS 0x01 /* insert alarm indication signal */
+#define SUNI_TSOP_CTRL_DS 0x40 /* disable scrambling */
+
+/* TSOP_DIAG is reg 0x15 */
+#define SUNI_TSOP_DIAG_DFP 0x01 /* insert single bit error cont. */
+#define SUNI_TSOP_DIAG_DBIP8 0x02 /* insert section BIP err (cont) */
+#define SUNI_TSOP_DIAG_DLOS 0x04 /* set line to zero (loss of signal) */
+
+/* TLOP_DIAG is reg 0x21 */
+#define SUNI_TLOP_DIAG_DBIP 0x01 /* insert line BIP err (continuously) */
+
+/* SSTB_CTRL is reg 0x28 */
+#define SUNI_SSTB_CTRL_LEN16 0x01 /* path trace message length bit */
+
+/* RPOP_RC is reg 0x3D (PM5355) */
+#define SUNI_RPOP_RC_ENSS 0x40 /* enable size bit */
+
+/* TPOP_DIAG is reg 0x40 */
+#define SUNI_TPOP_DIAG_PAIS 0x01 /* insert STS path alarm ind (cont) */
+#define SUNI_TPOP_DIAG_DB3 0x02 /* insert path BIP err (continuously) */
+
+/* TPOP_APM is reg 0x46 */
+#define SUNI_TPOP_APM_APTR 0x03 /* RW, arbitrary pointer, upper 2
+ bits */
+#define SUNI_TPOP_APM_APTR_SHIFT 0
+#define SUNI_TPOP_APM_S 0x0c /* RW, "unused" bits of payload
+ pointer */
+#define SUNI_TPOP_APM_S_SHIFT 2
+#define SUNI_TPOP_APM_NDF 0xf0 /* RW, NDF bits */
+#define SUNI_TPOP_APM_NDF_SHIFT 4
+
+#define SUNI_TPOP_S_SONET 0 /* set S bits to 00 */
+#define SUNI_TPOP_S_SDH 2 /* set S bits to 10 */
+
+/* RACP_IES is reg 0x51 */
+#define SUNI_RACP_IES_FOVRI 0x02 /* R, FIFO overrun */
+#define SUNI_RACP_IES_UHCSI 0x04 /* R, uncorrectable HCS error */
+#define SUNI_RACP_IES_CHCSI 0x08 /* R, correctable HCS error */
+#define SUNI_RACP_IES_OOCDI 0x10 /* R, change of cell delineation
+ state */
+#define SUNI_RACP_IES_FIFOE 0x20 /* RW, enable FIFO overrun interrupt */
+#define SUNI_RACP_IES_HCSE 0x40 /* RW, enable HCS error interrupt */
+#define SUNI_RACP_IES_OOCDE 0x80 /* RW, enable cell delineation state
+ change interrupt */
+
+/* TACP_CS is reg 0x60 */
+#define SUNI_TACP_CS_FIFORST 0x01 /* RW, reset transmit FIFO (sticky) */
+#define SUNI_TACP_CS_DSCR 0x02 /* RW, disable payload scrambling */
+#define SUNI_TACP_CS_HCAADD 0x04 /* RW, add coset polynomial to HCS */
+#define SUNI_TACP_CS_DHCS 0x10 /* RW, insert HCS errors */
+#define SUNI_TACP_CS_FOVRI 0x20 /* R, FIFO overrun */
+#define SUNI_TACP_CS_TSOCI 0x40 /* R, TSOC input high */
+#define SUNI_TACP_CS_FIFOE 0x80 /* RW, enable FIFO overrun interrupt */
+
+/* TACP_IUCHP is reg 0x61 */
+#define SUNI_TACP_IUCHP_CLP 0x01 /* RW, 8th bit of 4th octet of i/u
+ pattern */
+#define SUNI_TACP_IUCHP_PTI 0x0e /* RW, 5th-7th bits of 4th octet of i/u
+ pattern */
+#define SUNI_TACP_IUCHP_PTI_SHIFT 1
+#define SUNI_TACP_IUCHP_GFC 0xf0 /* RW, 1st-4th bits of 1st octet of i/u
+ pattern */
+#define SUNI_TACP_IUCHP_GFC_SHIFT 4
+
+/* SPTB_CTRL is reg 0x68 */
+#define SUNI_SPTB_CTRL_LEN16 0x01 /* path trace message length */
+
+/* MT is reg 0x80 */
+#define SUNI_MT_HIZIO 0x01 /* RW, all but data bus & MP interface
+ tri-state */
+#define SUNI_MT_HIZDATA 0x02 /* W, also tri-state data bus */
+#define SUNI_MT_IOTST 0x04 /* RW, enable test mode */
+#define SUNI_MT_DBCTRL 0x08 /* W, control data bus by CSB pin */
+#define SUNI_MT_PMCTST 0x10 /* W, PMC test mode */
+#define SUNI_MT_DS27_53 0x80 /* RW, select between 8- or 16- bit */
+
+
+#define SUNI_IDLE_PATTERN 0x6a /* idle pattern */
+
+
+#ifdef __KERNEL__
+struct suni_priv {
+ struct k_sonet_stats sonet_stats; /* link diagnostics */
+ int loop_mode; /* loopback mode */
+ int type; /* phy type */
+ struct atm_dev *dev; /* device back-pointer */
+ struct suni_priv *next; /* next SUNI */
+};
+
+int suni_init(struct atm_dev *dev);
+#endif
+
+#endif
diff --git a/drivers/atm/tonga.h b/drivers/atm/tonga.h
new file mode 100644
index 000000000..672da9624
--- /dev/null
+++ b/drivers/atm/tonga.h
@@ -0,0 +1,20 @@
+/* drivers/atm/tonga.h - Efficient Networks Tonga (PCI bridge) declarations */
+
+/* Written 1995 by Werner Almesberger, EPFL LRC */
+
+
+#ifndef DRIVER_ATM_TONGA_H
+#define DRIVER_ATM_TONGA_H
+
+#define PCI_TONGA_CTRL 0x60 /* control register */
+
+#define END_SWAP_DMA 0x80 /* endian swap on DMA */
+#define END_SWAP_BYTE 0x40 /* endian swap on slave byte accesses */
+#define END_SWAP_WORD 0x20 /* endian swap on slave word accesses */
+#define SEPROM_MAGIC 0x0c /* obscure required pattern (ASIC only) */
+#define SEPROM_DATA 0x02 /* serial EEPROM data (ASIC only) */
+#define SEPROM_CLK 0x01 /* serial EEPROM clock (ASIC only) */
+
+#define SEPROM_ESI_BASE 64 /* start of ESI in serial EEPROM */
+
+#endif
diff --git a/drivers/atm/uPD98401.h b/drivers/atm/uPD98401.h
new file mode 100644
index 000000000..0ab36503a
--- /dev/null
+++ b/drivers/atm/uPD98401.h
@@ -0,0 +1,292 @@
+/* drivers/atm/uPD98401.h - NEC uPD98401 (SAR) declarations */
+
+/* Written 1995 by Werner Almesberger, EPFL LRC */
+
+
+#ifndef DRIVERS_ATM_uPD98401_H
+#define DRIVERS_ATM_uPD98401_H
+
+
+#define MAX_CRAM_SIZE (1 << 18) /* 2^18 words */
+#define RAM_INCREMENT 1024 /* check in 4 kB increments */
+
+#define uPD98401_PORTS 0x24 /* probably more ? */
+
+
+/*
+ * Commands
+ */
+
+#define uPD98401_OPEN_CHAN 0x20000000 /* open channel */
+#define uPD98401_CHAN_ADDR 0x0003fff8 /* channel address */
+#define uPD98401_CHAN_ADDR_SHIFT 3
+#define uPD98401_CLOSE_CHAN 0x24000000 /* close channel */
+#define uPD98401_CHAN_RT 0x02000000 /* RX/TX (0 TX, 1 RX) */
+#define uPD98401_DEACT_CHAN 0x28000000 /* deactivate channel */
+#define uPD98401_TX_READY 0x30000000 /* TX ready */
+#define uPD98401_ADD_BAT 0x34000000 /* add batches */
+#define uPD98401_POOL 0x000f0000 /* pool number */
+#define uPD98401_POOL_SHIFT 16
+#define uPD98401_POOL_NUMBAT 0x0000ffff /* number of batches */
+#define uPD98401_NOP 0x3f000000 /* NOP */
+#define uPD98401_IND_ACC 0x00000000 /* Indirect Access */
+#define uPD98401_IA_RW 0x10000000 /* Read/Write (0 W, 1 R) */
+#define uPD98401_IA_B3 0x08000000 /* Byte select, 1 enable */
+#define uPD98401_IA_B2 0x04000000
+#define uPD98401_IA_B1 0x02000000
+#define uPD98401_IA_B0 0x01000000
+#define uPD98401_IA_BALL 0x0f000000 /* whole longword */
+#define uPD98401_IA_TGT 0x000c0000 /* Target */
+#define uPD98401_IA_TGT_SHIFT 18
+#define uPD98401_IA_TGT_CM 0 /* - Control Memory */
+#define uPD98401_IA_TGT_SAR 1 /* - uPD98401 registers */
+#define uPD98401_IA_TGT_PHY 3 /* - PHY device */
+#define uPD98401_IA_ADDR 0x0003ffff
+
+/*
+ * Command Register Status
+ */
+
+#define uPD98401_BUSY 0x80000000 /* SAR is busy */
+#define uPD98401_LOCKED 0x40000000 /* SAR is locked by other CPU */
+
+/*
+ * Indications
+ */
+
+/* Normal (AAL5) Receive Indication */
+#define uPD98401_AAL5_UINFO 0xffff0000 /* user-supplied information */
+#define uPD98401_AAL5_UINFO_SHIFT 16
+#define uPD98401_AAL5_SIZE 0x0000ffff /* PDU size (in _CELLS_ !!) */
+#define uPD98401_AAL5_CHAN 0x7fff0000 /* Channel number */
+#define uPD98401_AAL5_CHAN_SHIFT 16
+#define uPD98401_AAL5_ERR 0x00008000 /* Error indication */
+#define uPD98401_AAL5_CI 0x00004000 /* Congestion Indication */
+#define uPD98401_AAL5_CLP 0x00002000 /* CLP (>= 1 cell had CLP=1) */
+#define uPD98401_AAL5_ES 0x00000f00 /* Error Status */
+#define uPD98401_AAL5_ES_SHIFT 8
+#define uPD98401_AAL5_ES_NONE 0 /* No error */
+#define uPD98401_AAL5_ES_FREE 1 /* Receiver free buf underflow */
+#define uPD98401_AAL5_ES_FIFO 2 /* Receiver FIFO overrun */
+#define uPD98401_AAL5_ES_TOOBIG 3 /* Maximum length violation */
+#define uPD98401_AAL5_ES_CRC 4 /* CRC error */
+#define uPD98401_AAL5_ES_ABORT 5 /* User abort */
+#define uPD98401_AAL5_ES_LENGTH 6 /* Length violation */
+#define uPD98401_AAL5_ES_T1 7 /* T1 error (timeout) */
+#define uPD98401_AAL5_ES_DEACT 8 /* Deactivated with DEACT_CHAN */
+#define uPD98401_AAL5_POOL 0x0000001f /* Free buffer pool number */
+
+/* Raw Cell Indication */
+#define uPD98401_RAW_UINFO uPD98401_AAL5_UINFO
+#define uPD98401_RAW_UINFO_SHIFT uPD98401_AAL5_UINFO_SHIFT
+#define uPD98401_RAW_HEC 0x000000ff /* HEC */
+#define uPD98401_RAW_CHAN uPD98401_AAL5_CHAN
+#define uPD98401_RAW_CHAN_SHIFT uPD98401_AAL5_CHAN_SHIFT
+
+/* Transmit Indication */
+#define uPD98401_TXI_CONN 0x7fff0000 /* Connection Number */
+#define uPD98401_TXI_CONN_SHIFT 16
+#define uPD98401_TXI_ACTIVE 0x00008000 /* Channel remains active */
+#define uPD98401_TXI_PQP 0x00007fff /* Packet Queue Pointer */
+
+/*
+ * Directly Addressable Registers
+ */
+
+#define uPD98401_GMR 0x00 /* General Mode Register */
+#define uPD98401_GSR 0x01 /* General Status Register */
+#define uPD98401_IMR 0x02 /* Interrupt Mask Register */
+#define uPD98401_RQU 0x03 /* Receive Queue Underrun */
+#define uPD98401_RQA 0x04 /* Receive Queue Alert */
+#define uPD98401_ADDR 0x05 /* Last Burst Address */
+#define uPD98401_VER 0x06 /* Version Number */
+#define uPD98401_SWR 0x07 /* Software Reset */
+#define uPD98401_CMR 0x08 /* Command Register */
+#define uPD98401_CMR_L 0x09 /* Command Register and Lock/Unlock */
+#define uPD98401_CER 0x0a /* Command Extension Register */
+#define uPD98401_CER_L 0x0b /* Command Ext Reg and Lock/Unlock */
+
+#define uPD98401_MSH(n) (0x10+(n)) /* Mailbox n Start Address High */
+#define uPD98401_MSL(n) (0x14+(n)) /* Mailbox n Start Address High */
+#define uPD98401_MBA(n) (0x18+(n)) /* Mailbox n Bottom Address */
+#define uPD98401_MTA(n) (0x1c+(n)) /* Mailbox n Tail Address */
+#define uPD98401_MWA(n) (0x20+(n)) /* Mailbox n Write Address */
+
+/* GMR is at 0x00 */
+#define uPD98401_GMR_ONE 0x80000000 /* Must be set to one */
+#define uPD98401_GMR_SLM 0x40000000 /* Address mode (0 word, 1 byte) */
+#define uPD98401_GMR_CPE 0x00008000 /* Control Memory Parity Enable */
+#define uPD98401_GMR_LP 0x00004000 /* Loopback */
+#define uPD98401_GMR_WA 0x00002000 /* Early Bus Write Abort/RDY */
+#define uPD98401_GMR_RA 0x00001000 /* Early Read Abort/RDY */
+#define uPD98401_GMR_SZ 0x00000f00 /* Burst Size Enable */
+#define uPD98401_BURST16 0x00000800 /* 16-word burst */
+#define uPD98401_BURST8 0x00000400 /* 8-word burst */
+#define uPD98401_BURST4 0x00000200 /* 4-word burst */
+#define uPD98401_BURST2 0x00000100 /* 2-word burst */
+#define uPD98401_GMR_AD 0x00000080 /* Address (burst resolution) Disable */
+#define uPD98401_GMR_BO 0x00000040 /* Byte Order (0 little, 1 big) */
+#define uPD98401_GMR_PM 0x00000020 /* Bus Parity Mode (0 byte, 1 word)*/
+#define uPD98401_GMR_PC 0x00000010 /* Bus Parity Control (0even,1odd) */
+#define uPD98401_GMR_BPE 0x00000008 /* Bus Parity Enable */
+#define uPD98401_GMR_DR 0x00000004 /* Receive Drop Mode (0drop,1don't)*/
+#define uPD98401_GMR_SE 0x00000002 /* Shapers Enable */
+#define uPD98401_GMR_RE 0x00000001 /* Receiver Enable */
+
+/* GSR is at 0x01, IMR is at 0x02 */
+#define uPD98401_INT_PI 0x80000000 /* PHY interrupt */
+#define uPD98401_INT_RQA 0x40000000 /* Receive Queue Alert */
+#define uPD98401_INT_RQU 0x20000000 /* Receive Queue Underrun */
+#define uPD98401_INT_RD 0x10000000 /* Receiver Deactivated */
+#define uPD98401_INT_SPE 0x08000000 /* System Parity Error */
+#define uPD98401_INT_CPE 0x04000000 /* Control Memory Parity Error */
+#define uPD98401_INT_SBE 0x02000000 /* System Bus Error */
+#define uPD98401_INT_IND 0x01000000 /* Initialization Done */
+#define uPD98401_INT_RCR 0x0000ff00 /* Raw Cell Received */
+#define uPD98401_INT_RCR_SHIFT 8
+#define uPD98401_INT_MF 0x000000f0 /* Mailbox Full */
+#define uPD98401_INT_MF_SHIFT 4
+#define uPD98401_INT_MM 0x0000000f /* Mailbox Modified */
+
+/* VER is at 0x06 */
+#define uPD98401_MAJOR 0x0000ff00 /* Major revision */
+#define uPD98401_MAJOR_SHIFT 8
+#define uPD98401_MINOR 0x000000ff /* Minor revision */
+
+/*
+ * Indirectly Addressable Registers
+ */
+
+#define uPD98401_IM(n) (0x40000+(n)) /* Scheduler n I and M */
+#define uPD98401_X(n) (0x40010+(n)) /* Scheduler n X */
+#define uPD98401_Y(n) (0x40020+(n)) /* Scheduler n Y */
+#define uPD98401_PC(n) (0x40030+(n)) /* Scheduler n P, C, p and c */
+#define uPD98401_PS(n) (0x40040+(n)) /* Scheduler n priority and status */
+
+/* IM contents */
+#define uPD98401_IM_I 0xff000000 /* I */
+#define uPD98401_IM_I_SHIFT 24
+#define uPD98401_IM_M 0x00ffffff /* M */
+
+/* PC contents */
+#define uPD98401_PC_P 0xff000000 /* P */
+#define uPD98401_PC_P_SHIFT 24
+#define uPD98401_PC_C 0x00ff0000 /* C */
+#define uPD98401_PC_C_SHIFT 16
+#define uPD98401_PC_p 0x0000ff00 /* p */
+#define uPD98401_PC_p_SHIFT 8
+#define uPD98401_PC_c 0x000000ff /* c */
+
+/* PS contents */
+#define uPD98401_PS_PRIO 0xf0 /* Priority level (0 high, 15 low) */
+#define uPD98401_PS_PRIO_SHIFT 4
+#define uPD98401_PS_S 0x08 /* Scan - must be 0 (internal) */
+#define uPD98401_PS_R 0x04 /* Round Robin (internal) */
+#define uPD98401_PS_A 0x02 /* Active (internal) */
+#define uPD98401_PS_E 0x01 /* Enabled */
+
+#define uPD98401_TOS 0x40100 /* Top of Stack Control Memory Address */
+#define uPD98401_SMA 0x40200 /* Shapers Control Memory Start Address */
+#define uPD98401_PMA 0x40201 /* Receive Pool Control Memory Start Address */
+#define uPD98401_T1R 0x40300 /* T1 Register */
+#define uPD98401_VRR 0x40301 /* VPI/VCI Reduction Register/Recv. Shutdown */
+#define uPD98401_TSR 0x40302 /* Time-Stamp Register */
+
+/* VRR is at 0x40301 */
+#define uPD98401_VRR_SDM 0x80000000 /* Shutdown Mode */
+#define uPD98401_VRR_SHIFT 0x000f0000 /* VPI/VCI Shift */
+#define uPD98401_VRR_SHIFT_SHIFT 16
+#define uPD98401_VRR_MASK 0x0000ffff /* VPI/VCI mask */
+
+/*
+ * TX packet descriptor
+ */
+
+#define uPD98401_TXPD_SIZE 16 /* descriptor size (in bytes) */
+
+#define uPD98401_TXPD_V 0x80000000 /* Valid bit */
+#define uPD98401_TXPD_DP 0x40000000 /* Descriptor (1) or Pointer (0) */
+#define uPD98401_TXPD_SM 0x20000000 /* Single (1) or Multiple (0) */
+#define uPD98401_TXPD_CLPM 0x18000000 /* CLP mode */
+#define uPD98401_CLPM_0 0 /* 00 CLP = 0 */
+#define uPD98401_CLPM_1 3 /* 11 CLP = 1 */
+#define uPD98401_CLPM_LAST 1 /* 01 CLP unless last cell */
+#define uPD98401_TXPD_CLPM_SHIFT 27
+#define uPD98401_TXPD_PTI 0x07000000 /* PTI pattern */
+#define uPD98401_TXPD_PTI_SHIFT 24
+#define uPD98401_TXPD_GFC 0x00f00000 /* GFC pattern */
+#define uPD98401_TXPD_GFC_SHIFT 20
+#define uPD98401_TXPD_C10 0x00040000 /* insert CRC-10 */
+#define uPD98401_TXPD_AAL5 0x00020000 /* AAL5 processing */
+#define uPD98401_TXPD_MB 0x00010000 /* TX mailbox number */
+#define uPD98401_TXPD_UU 0x0000ff00 /* CPCS-UU */
+#define uPD98401_TXPD_UU_SHIFT 8
+#define uPD98401_TXPD_CPI 0x000000ff /* CPI */
+
+/*
+ * TX buffer descriptor
+ */
+
+#define uPD98401_TXBD_SIZE 8 /* descriptor size (in bytes) */
+
+#define uPD98401_TXBD_LAST 0x80000000 /* last buffer in packet */
+
+/*
+ * TX VC table
+ */
+
+/* 1st word has the same structure as in a TX packet descriptor */
+#define uPD98401_TXVC_L 0x80000000 /* last buffer */
+#define uPD98401_TXVC_SHP 0x0f000000 /* shaper number */
+#define uPD98401_TXVC_SHP_SHIFT 24
+#define uPD98401_TXVC_VPI 0x00ff0000 /* VPI */
+#define uPD98401_TXVC_VPI_SHIFT 16
+#define uPD98401_TXVC_VCI 0x0000ffff /* VCI */
+#define uPD98401_TXVC_QRP 6 /* Queue Read Pointer is in word 6 */
+
+/*
+ * RX free buffer pools descriptor
+ */
+
+#define uPD98401_RXFP_ALERT 0x70000000 /* low water mark */
+#define uPD98401_RXFP_ALERT_SHIFT 28
+#define uPD98401_RXFP_BFSZ 0x0f000000 /* buffer size, 64*2^n */
+#define uPD98401_RXFP_BFSZ_SHIFT 24
+#define uPD98401_RXFP_BTSZ 0x00ff0000 /* batch size, n+1 */
+#define uPD98401_RXFP_BTSZ_SHIFT 16
+#define uPD98401_RXFP_REMAIN 0x0000ffff /* remaining batches in pool */
+
+/*
+ * RX VC table
+ */
+
+#define uPD98401_RXVC_BTSZ 0xff000000 /* remaining free buffers in batch */
+#define uPD98401_RXVC_BTSZ_SHIFT 24
+#define uPD98401_RXVC_MB 0x00200000 /* RX mailbox number */
+#define uPD98401_RXVC_POOL 0x001f0000 /* free buffer pool number */
+#define uPD98401_RXVC_POOL_SHIFT 16
+#define uPD98401_RXVC_UINFO 0x0000ffff /* user-supplied information */
+#define uPD98401_RXVC_T1 0xffff0000 /* T1 timestamp */
+#define uPD98401_RXVC_T1_SHIFT 16
+#define uPD98401_RXVC_PR 0x00008000 /* Packet Reception, 1 if busy */
+#define uPD98401_RXVC_DR 0x00004000 /* FIFO Drop */
+#define uPD98401_RXVC_OD 0x00001000 /* Drop OAM cells */
+#define uPD98401_RXVC_AR 0x00000800 /* AAL5 or raw cell; 1 if AAL5 */
+#define uPD98401_RXVC_MAXSEG 0x000007ff /* max number of segments per PDU */
+#define uPD98401_RXVC_REM 0xfffe0000 /* remaining words in curr buffer */
+#define uPD98401_RXVC_REM_SHIFT 17
+#define uPD98401_RXVC_CLP 0x00010000 /* CLP received */
+#define uPD98401_RXVC_BFA 0x00008000 /* Buffer Assigned */
+#define uPD98401_RXVC_BTA 0x00004000 /* Batch Assigned */
+#define uPD98401_RXVC_CI 0x00002000 /* Congestion Indication */
+#define uPD98401_RXVC_DD 0x00001000 /* Dropping incoming cells */
+#define uPD98401_RXVC_DP 0x00000800 /* like PR ? */
+#define uPD98401_RXVC_CURSEG 0x000007ff /* Current Segment count */
+
+/*
+ * RX lookup table
+ */
+
+#define uPD98401_RXLT_ENBL 0x8000 /* Enable */
+
+#endif
diff --git a/drivers/atm/uPD98402.c b/drivers/atm/uPD98402.c
new file mode 100644
index 000000000..5120a96b3
--- /dev/null
+++ b/drivers/atm/uPD98402.c
@@ -0,0 +1,265 @@
+/* drivers/atm/uPD98402.c - NEC uPD98402 (PHY) declarations */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/atomic.h>
+
+#include "uPD98402.h"
+
+
+#if 0
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+
+struct uPD98402_priv {
+ struct k_sonet_stats sonet_stats;/* link diagnostics */
+ unsigned char framing; /* SONET/SDH framing */
+ int loop_mode; /* loopback mode */
+ spinlock_t lock;
+};
+
+
+#define PRIV(dev) ((struct uPD98402_priv *) dev->phy_data)
+
+#define PUT(val,reg) dev->ops->phy_put(dev,val,uPD98402_##reg)
+#define GET(reg) dev->ops->phy_get(dev,uPD98402_##reg)
+
+
+static int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero)
+{
+ struct sonet_stats tmp;
+ int error = 0;
+
+ atomic_add(GET(HECCT),&PRIV(dev)->sonet_stats.uncorr_hcs);
+ sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp);
+ if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp));
+ if (zero && !error) {
+ /* unused fields are reported as -1, but we must not "adjust"
+ them */
+ tmp.corr_hcs = tmp.tx_cells = tmp.rx_cells = 0;
+ sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp);
+ }
+ return error ? -EFAULT : 0;
+}
+
+
+static int set_framing(struct atm_dev *dev,unsigned char framing)
+{
+ static const unsigned char sonet[] = { 1,2,3,0 };
+ static const unsigned char sdh[] = { 1,0,0,2 };
+ const char *set;
+ unsigned long flags;
+
+ switch (framing) {
+ case SONET_FRAME_SONET:
+ set = sonet;
+ break;
+ case SONET_FRAME_SDH:
+ set = sdh;
+ break;
+ default:
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&PRIV(dev)->lock, flags);
+ PUT(set[0],C11T);
+ PUT(set[1],C12T);
+ PUT(set[2],C13T);
+ PUT((GET(MDR) & ~uPD98402_MDR_SS_MASK) | (set[3] <<
+ uPD98402_MDR_SS_SHIFT),MDR);
+ spin_unlock_irqrestore(&PRIV(dev)->lock, flags);
+ return 0;
+}
+
+
+static int get_sense(struct atm_dev *dev,u8 __user *arg)
+{
+ unsigned long flags;
+ unsigned char s[3];
+
+ spin_lock_irqsave(&PRIV(dev)->lock, flags);
+ s[0] = GET(C11R);
+ s[1] = GET(C12R);
+ s[2] = GET(C13R);
+ spin_unlock_irqrestore(&PRIV(dev)->lock, flags);
+ return (put_user(s[0], arg) || put_user(s[1], arg+1) ||
+ put_user(s[2], arg+2) || put_user(0xff, arg+3) ||
+ put_user(0xff, arg+4) || put_user(0xff, arg+5)) ? -EFAULT : 0;
+}
+
+
+static int set_loopback(struct atm_dev *dev,int mode)
+{
+ unsigned char mode_reg;
+
+ mode_reg = GET(MDR) & ~(uPD98402_MDR_TPLP | uPD98402_MDR_ALP |
+ uPD98402_MDR_RPLP);
+ switch (__ATM_LM_XTLOC(mode)) {
+ case __ATM_LM_NONE:
+ break;
+ case __ATM_LM_PHY:
+ mode_reg |= uPD98402_MDR_TPLP;
+ break;
+ case __ATM_LM_ATM:
+ mode_reg |= uPD98402_MDR_ALP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (__ATM_LM_XTRMT(mode)) {
+ case __ATM_LM_NONE:
+ break;
+ case __ATM_LM_PHY:
+ mode_reg |= uPD98402_MDR_RPLP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ PUT(mode_reg,MDR);
+ PRIV(dev)->loop_mode = mode;
+ return 0;
+}
+
+
+static int uPD98402_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
+{
+ switch (cmd) {
+
+ case SONET_GETSTATZ:
+ case SONET_GETSTAT:
+ return fetch_stats(dev,arg, cmd == SONET_GETSTATZ);
+ case SONET_SETFRAMING:
+ return set_framing(dev, (int)(unsigned long)arg);
+ case SONET_GETFRAMING:
+ return put_user(PRIV(dev)->framing,(int __user *)arg) ?
+ -EFAULT : 0;
+ case SONET_GETFRSENSE:
+ return get_sense(dev,arg);
+ case ATM_SETLOOP:
+ return set_loopback(dev, (int)(unsigned long)arg);
+ case ATM_GETLOOP:
+ return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ?
+ -EFAULT : 0;
+ case ATM_QUERYLOOP:
+ return put_user(ATM_LM_LOC_PHY | ATM_LM_LOC_ATM |
+ ATM_LM_RMT_PHY,(int __user *)arg) ? -EFAULT : 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+
+#define ADD_LIMITED(s,v) \
+ { atomic_add(GET(v),&PRIV(dev)->sonet_stats.s); \
+ if (atomic_read(&PRIV(dev)->sonet_stats.s) < 0) \
+ atomic_set(&PRIV(dev)->sonet_stats.s,INT_MAX); }
+
+
+static void stat_event(struct atm_dev *dev)
+{
+ unsigned char events;
+
+ events = GET(PCR);
+ if (events & uPD98402_PFM_PFEB) ADD_LIMITED(path_febe,PFECB);
+ if (events & uPD98402_PFM_LFEB) ADD_LIMITED(line_febe,LECCT);
+ if (events & uPD98402_PFM_B3E) ADD_LIMITED(path_bip,B3ECT);
+ if (events & uPD98402_PFM_B2E) ADD_LIMITED(line_bip,B2ECT);
+ if (events & uPD98402_PFM_B1E) ADD_LIMITED(section_bip,B1ECT);
+}
+
+
+#undef ADD_LIMITED
+
+
+static void uPD98402_int(struct atm_dev *dev)
+{
+ static unsigned long silence = 0;
+ unsigned char reason;
+
+ while ((reason = GET(PICR))) {
+ if (reason & uPD98402_INT_LOS)
+ printk(KERN_NOTICE "%s(itf %d): signal lost\n",
+ dev->type,dev->number);
+ if (reason & uPD98402_INT_PFM) stat_event(dev);
+ if (reason & uPD98402_INT_PCO) {
+ (void) GET(PCOCR); /* clear interrupt cause */
+ atomic_add(GET(HECCT),
+ &PRIV(dev)->sonet_stats.uncorr_hcs);
+ }
+ if ((reason & uPD98402_INT_RFO) &&
+ (time_after(jiffies, silence) || silence == 0)) {
+ printk(KERN_WARNING "%s(itf %d): uPD98402 receive "
+ "FIFO overflow\n",dev->type,dev->number);
+ silence = (jiffies+HZ/2)|1;
+ }
+ }
+}
+
+
+static int uPD98402_start(struct atm_dev *dev)
+{
+ DPRINTK("phy_start\n");
+ if (!(dev->dev_data = kmalloc(sizeof(struct uPD98402_priv),GFP_KERNEL)))
+ return -ENOMEM;
+ spin_lock_init(&PRIV(dev)->lock);
+ memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats));
+ (void) GET(PCR); /* clear performance events */
+ PUT(uPD98402_PFM_FJ,PCMR); /* ignore frequency adj */
+ (void) GET(PCOCR); /* clear overflows */
+ PUT(~uPD98402_PCO_HECC,PCOMR);
+ (void) GET(PICR); /* clear interrupts */
+ PUT(~(uPD98402_INT_PFM | uPD98402_INT_ALM | uPD98402_INT_RFO |
+ uPD98402_INT_LOS),PIMR); /* enable them */
+ (void) fetch_stats(dev,NULL,1); /* clear kernel counters */
+ atomic_set(&PRIV(dev)->sonet_stats.corr_hcs,-1);
+ atomic_set(&PRIV(dev)->sonet_stats.tx_cells,-1);
+ atomic_set(&PRIV(dev)->sonet_stats.rx_cells,-1);
+ return 0;
+}
+
+
+static int uPD98402_stop(struct atm_dev *dev)
+{
+ /* let SAR driver worry about stopping interrupts */
+ kfree(PRIV(dev));
+ return 0;
+}
+
+
+static const struct atmphy_ops uPD98402_ops = {
+ .start = uPD98402_start,
+ .ioctl = uPD98402_ioctl,
+ .interrupt = uPD98402_int,
+ .stop = uPD98402_stop,
+};
+
+
+int uPD98402_init(struct atm_dev *dev)
+{
+DPRINTK("phy_init\n");
+ dev->phy = &uPD98402_ops;
+ return 0;
+}
+
+
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(uPD98402_init);
+
+static __init int uPD98402_module_init(void)
+{
+ return 0;
+}
+module_init(uPD98402_module_init);
+/* module_exit not defined so not unloadable */
diff --git a/drivers/atm/uPD98402.h b/drivers/atm/uPD98402.h
new file mode 100644
index 000000000..c947214db
--- /dev/null
+++ b/drivers/atm/uPD98402.h
@@ -0,0 +1,106 @@
+/* drivers/atm/uPD98402.h - NEC uPD98402 (PHY) declarations */
+
+/* Written 1995 by Werner Almesberger, EPFL LRC */
+
+
+#ifndef DRIVERS_ATM_uPD98402_H
+#define DRIVERS_ATM_uPD98402_H
+
+/*
+ * Registers
+ */
+
+#define uPD98402_CMR 0x00 /* Command Register */
+#define uPD98402_MDR 0x01 /* Mode Register */
+#define uPD98402_PICR 0x02 /* PHY Interrupt Cause Register */
+#define uPD98402_PIMR 0x03 /* PHY Interrupt Mask Register */
+#define uPD98402_ACR 0x04 /* Alarm Cause Register */
+#define uPD98402_ACMR 0x05 /* Alarm Cause Mask Register */
+#define uPD98402_PCR 0x06 /* Performance Cause Register */
+#define uPD98402_PCMR 0x07 /* Performance Cause Mask Register */
+#define uPD98402_IACM 0x08 /* Internal Alarm Cause Mask Register */
+#define uPD98402_B1ECT 0x09 /* B1 Error Count Register */
+#define uPD98402_B2ECT 0x0a /* B2 Error Count Register */
+#define uPD98402_B3ECT 0x0b /* B3 Error Count Regster */
+#define uPD98402_PFECB 0x0c /* Path FEBE Count Register */
+#define uPD98402_LECCT 0x0d /* Line FEBE Count Register */
+#define uPD98402_HECCT 0x0e /* HEC Error Count Register */
+#define uPD98402_FJCT 0x0f /* Frequence Justification Count Reg */
+#define uPD98402_PCOCR 0x10 /* Perf. Counter Overflow Cause Reg */
+#define uPD98402_PCOMR 0x11 /* Perf. Counter Overflow Mask Reg */
+#define uPD98402_C11T 0x20 /* C11T Data Register */
+#define uPD98402_C12T 0x21 /* C12T Data Register */
+#define uPD98402_C13T 0x22 /* C13T Data Register */
+#define uPD98402_F1T 0x23 /* F1T Data Register */
+#define uPD98402_K2T 0x25 /* K2T Data Register */
+#define uPD98402_C2T 0x26 /* C2T Data Register */
+#define uPD98402_F2T 0x27 /* F2T Data Register */
+#define uPD98402_C11R 0x30 /* C11T Data Register */
+#define uPD98402_C12R 0x31 /* C12T Data Register */
+#define uPD98402_C13R 0x32 /* C13T Data Register */
+#define uPD98402_F1R 0x33 /* F1T Data Register */
+#define uPD98402_K2R 0x35 /* K2T Data Register */
+#define uPD98402_C2R 0x36 /* C2T Data Register */
+#define uPD98402_F2R 0x37 /* F2T Data Register */
+
+/* CMR is at 0x00 */
+#define uPD98402_CMR_PFRF 0x01 /* Send path FERF */
+#define uPD98402_CMR_LFRF 0x02 /* Send line FERF */
+#define uPD98402_CMR_PAIS 0x04 /* Send path AIS */
+#define uPD98402_CMR_LAIS 0x08 /* Send line AIS */
+
+/* MDR is at 0x01 */
+#define uPD98402_MDR_ALP 0x01 /* ATM layer loopback */
+#define uPD98402_MDR_TPLP 0x02 /* PMD loopback, to host */
+#define uPD98402_MDR_RPLP 0x04 /* PMD loopback, to network */
+#define uPD98402_MDR_SS0 0x08 /* SS0 */
+#define uPD98402_MDR_SS1 0x10 /* SS1 */
+#define uPD98402_MDR_SS_MASK 0x18 /* mask */
+#define uPD98402_MDR_SS_SHIFT 3 /* shift */
+#define uPD98402_MDR_HEC 0x20 /* disable HEC inbound processing */
+#define uPD98402_MDR_FSR 0x40 /* disable frame scrambler */
+#define uPD98402_MDR_CSR 0x80 /* disable cell scrambler */
+
+/* PICR is at 0x02, PIMR is at 0x03 */
+#define uPD98402_INT_PFM 0x01 /* performance counter has changed */
+#define uPD98402_INT_ALM 0x02 /* line fault */
+#define uPD98402_INT_RFO 0x04 /* receive FIFO overflow */
+#define uPD98402_INT_PCO 0x08 /* performance counter overflow */
+#define uPD98402_INT_OTD 0x20 /* OTD has occurred */
+#define uPD98402_INT_LOS 0x40 /* Loss Of Signal */
+#define uPD98402_INT_LOF 0x80 /* Loss Of Frame */
+
+/* ACR is as 0x04, ACMR is at 0x05 */
+#define uPD98402_ALM_PFRF 0x01 /* path FERF */
+#define uPD98402_ALM_LFRF 0x02 /* line FERF */
+#define uPD98402_ALM_PAIS 0x04 /* path AIS */
+#define uPD98402_ALM_LAIS 0x08 /* line AIS */
+#define uPD98402_ALM_LOD 0x10 /* loss of delineation */
+#define uPD98402_ALM_LOP 0x20 /* loss of pointer */
+#define uPD98402_ALM_OOF 0x40 /* out of frame */
+
+/* PCR is at 0x06, PCMR is at 0x07 */
+#define uPD98402_PFM_PFEB 0x01 /* path FEBE */
+#define uPD98402_PFM_LFEB 0x02 /* line FEBE */
+#define uPD98402_PFM_B3E 0x04 /* B3 error */
+#define uPD98402_PFM_B2E 0x08 /* B2 error */
+#define uPD98402_PFM_B1E 0x10 /* B1 error */
+#define uPD98402_PFM_FJ 0x20 /* frequency justification */
+
+/* IACM is at 0x08 */
+#define uPD98402_IACM_PFRF 0x01 /* don't generate path FERF */
+#define uPD98402_IACM_LFRF 0x02 /* don't generate line FERF */
+
+/* PCOCR is at 0x010, PCOMR is at 0x11 */
+#define uPD98402_PCO_B1EC 0x01 /* B1ECT overflow */
+#define uPD98402_PCO_B2EC 0x02 /* B2ECT overflow */
+#define uPD98402_PCO_B3EC 0x04 /* B3ECT overflow */
+#define uPD98402_PCO_PFBC 0x08 /* PFEBC overflow */
+#define uPD98402_PCO_LFBC 0x10 /* LFEVC overflow */
+#define uPD98402_PCO_HECC 0x20 /* HECCT overflow */
+#define uPD98402_PCO_FJC 0x40 /* FJCT overflow */
+
+
+int uPD98402_init(struct atm_dev *dev);
+
+#endif
diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c
new file mode 100644
index 000000000..cecfb9437
--- /dev/null
+++ b/drivers/atm/zatm.c
@@ -0,0 +1,1662 @@
+/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/uio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/atm_zatm.h>
+#include <linux/capability.h>
+#include <linux/bitops.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <asm/string.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+#include <asm/uaccess.h>
+
+#include "uPD98401.h"
+#include "uPD98402.h"
+#include "zeprom.h"
+#include "zatm.h"
+
+
+/*
+ * TODO:
+ *
+ * Minor features
+ * - support 64 kB SDUs (will have to use multibuffer batches then :-( )
+ * - proper use of CDV, credit = max(1,CDVT*PCR)
+ * - AAL0
+ * - better receive timestamps
+ * - OAM
+ */
+
+#define ZATM_COPPER 1
+
+#if 0
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+#ifndef CONFIG_ATM_ZATM_DEBUG
+
+
+#define NULLCHECK(x)
+
+#define EVENT(s,a,b)
+
+
+static void event_dump(void)
+{
+}
+
+
+#else
+
+
+/*
+ * NULL pointer checking
+ */
+
+#define NULLCHECK(x) \
+ if ((unsigned long) (x) < 0x30) printk(KERN_CRIT #x "==0x%x\n", (int) (x))
+
+/*
+ * Very extensive activity logging. Greatly improves bug detection speed but
+ * costs a few Mbps if enabled.
+ */
+
+#define EV 64
+
+static const char *ev[EV];
+static unsigned long ev_a[EV],ev_b[EV];
+static int ec = 0;
+
+
+static void EVENT(const char *s,unsigned long a,unsigned long b)
+{
+ ev[ec] = s;
+ ev_a[ec] = a;
+ ev_b[ec] = b;
+ ec = (ec+1) % EV;
+}
+
+
+static void event_dump(void)
+{
+ int n,i;
+
+ printk(KERN_NOTICE "----- event dump follows -----\n");
+ for (n = 0; n < EV; n++) {
+ i = (ec+n) % EV;
+ printk(KERN_NOTICE);
+ printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
+ }
+ printk(KERN_NOTICE "----- event dump ends here -----\n");
+}
+
+
+#endif /* CONFIG_ATM_ZATM_DEBUG */
+
+
+#define RING_BUSY 1 /* indication from do_tx that PDU has to be
+ backlogged */
+
+static struct atm_dev *zatm_boards = NULL;
+static unsigned long dummy[2] = {0,0};
+
+
+#define zin_n(r) inl(zatm_dev->base+r*4)
+#define zin(r) inl(zatm_dev->base+uPD98401_##r*4)
+#define zout(v,r) outl(v,zatm_dev->base+uPD98401_##r*4)
+#define zwait while (zin(CMR) & uPD98401_BUSY)
+
+/* RX0, RX1, TX0, TX1 */
+static const int mbx_entries[NR_MBX] = { 1024,1024,1024,1024 };
+static const int mbx_esize[NR_MBX] = { 16,16,4,4 }; /* entry size in bytes */
+
+#define MBX_SIZE(i) (mbx_entries[i]*mbx_esize[i])
+
+
+/*-------------------------------- utilities --------------------------------*/
+
+
+static void zpokel(struct zatm_dev *zatm_dev,u32 value,u32 addr)
+{
+ zwait;
+ zout(value,CER);
+ zout(uPD98401_IND_ACC | uPD98401_IA_BALL |
+ (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
+}
+
+
+static u32 zpeekl(struct zatm_dev *zatm_dev,u32 addr)
+{
+ zwait;
+ zout(uPD98401_IND_ACC | uPD98401_IA_BALL | uPD98401_IA_RW |
+ (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
+ zwait;
+ return zin(CER);
+}
+
+
+/*------------------------------- free lists --------------------------------*/
+
+
+/*
+ * Free buffer head structure:
+ * [0] pointer to buffer (for SAR)
+ * [1] buffer descr link pointer (for SAR)
+ * [2] back pointer to skb (for poll_rx)
+ * [3] data
+ * ...
+ */
+
+struct rx_buffer_head {
+ u32 buffer; /* pointer to buffer (for SAR) */
+ u32 link; /* buffer descriptor link pointer (for SAR) */
+ struct sk_buff *skb; /* back pointer to skb (for poll_rx) */
+};
+
+
+static void refill_pool(struct atm_dev *dev,int pool)
+{
+ struct zatm_dev *zatm_dev;
+ struct sk_buff *skb;
+ struct rx_buffer_head *first;
+ unsigned long flags;
+ int align,offset,free,count,size;
+
+ EVENT("refill_pool\n",0,0);
+ zatm_dev = ZATM_DEV(dev);
+ size = (64 << (pool <= ZATM_AAL5_POOL_BASE ? 0 :
+ pool-ZATM_AAL5_POOL_BASE))+sizeof(struct rx_buffer_head);
+ if (size < PAGE_SIZE) {
+ align = 32; /* for 32 byte alignment */
+ offset = sizeof(struct rx_buffer_head);
+ }
+ else {
+ align = 4096;
+ offset = zatm_dev->pool_info[pool].offset+
+ sizeof(struct rx_buffer_head);
+ }
+ size += align;
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ free = zpeekl(zatm_dev,zatm_dev->pool_base+2*pool) &
+ uPD98401_RXFP_REMAIN;
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ if (free >= zatm_dev->pool_info[pool].low_water) return;
+ EVENT("starting ... POOL: 0x%x, 0x%x\n",
+ zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),
+ zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));
+ EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
+ count = 0;
+ first = NULL;
+ while (free < zatm_dev->pool_info[pool].high_water) {
+ struct rx_buffer_head *head;
+
+ skb = alloc_skb(size,GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_WARNING DEV_LABEL "(Itf %d): got no new "
+ "skb (%d) with %d free\n",dev->number,size,free);
+ break;
+ }
+ skb_reserve(skb,(unsigned char *) ((((unsigned long) skb->data+
+ align+offset-1) & ~(unsigned long) (align-1))-offset)-
+ skb->data);
+ head = (struct rx_buffer_head *) skb->data;
+ skb_reserve(skb,sizeof(struct rx_buffer_head));
+ if (!first) first = head;
+ count++;
+ head->buffer = virt_to_bus(skb->data);
+ head->link = 0;
+ head->skb = skb;
+ EVENT("enq skb 0x%08lx/0x%08lx\n",(unsigned long) skb,
+ (unsigned long) head);
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ if (zatm_dev->last_free[pool])
+ ((struct rx_buffer_head *) (zatm_dev->last_free[pool]->
+ data))[-1].link = virt_to_bus(head);
+ zatm_dev->last_free[pool] = skb;
+ skb_queue_tail(&zatm_dev->pool[pool],skb);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ free++;
+ }
+ if (first) {
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zwait;
+ zout(virt_to_bus(first),CER);
+ zout(uPD98401_ADD_BAT | (pool << uPD98401_POOL_SHIFT) | count,
+ CMR);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ EVENT ("POOL: 0x%x, 0x%x\n",
+ zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),
+ zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));
+ EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
+ }
+}
+
+
+static void drain_free(struct atm_dev *dev,int pool)
+{
+ skb_queue_purge(&ZATM_DEV(dev)->pool[pool]);
+}
+
+
+static int pool_index(int max_pdu)
+{
+ int i;
+
+ if (max_pdu % ATM_CELL_PAYLOAD)
+ printk(KERN_ERR DEV_LABEL ": driver error in pool_index: "
+ "max_pdu is %d\n",max_pdu);
+ if (max_pdu > 65536) return -1;
+ for (i = 0; (64 << i) < max_pdu; i++);
+ return i+ZATM_AAL5_POOL_BASE;
+}
+
+
+/* use_pool isn't reentrant */
+
+
+static void use_pool(struct atm_dev *dev,int pool)
+{
+ struct zatm_dev *zatm_dev;
+ unsigned long flags;
+ int size;
+
+ zatm_dev = ZATM_DEV(dev);
+ if (!(zatm_dev->pool_info[pool].ref_count++)) {
+ skb_queue_head_init(&zatm_dev->pool[pool]);
+ size = pool-ZATM_AAL5_POOL_BASE;
+ if (size < 0) size = 0; /* 64B... */
+ else if (size > 10) size = 10; /* ... 64kB */
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zpokel(zatm_dev,((zatm_dev->pool_info[pool].low_water/4) <<
+ uPD98401_RXFP_ALERT_SHIFT) |
+ (1 << uPD98401_RXFP_BTSZ_SHIFT) |
+ (size << uPD98401_RXFP_BFSZ_SHIFT),
+ zatm_dev->pool_base+pool*2);
+ zpokel(zatm_dev,(unsigned long) dummy,zatm_dev->pool_base+
+ pool*2+1);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ zatm_dev->last_free[pool] = NULL;
+ refill_pool(dev,pool);
+ }
+ DPRINTK("pool %d: %d\n",pool,zatm_dev->pool_info[pool].ref_count);
+}
+
+
+static void unuse_pool(struct atm_dev *dev,int pool)
+{
+ if (!(--ZATM_DEV(dev)->pool_info[pool].ref_count))
+ drain_free(dev,pool);
+}
+
+/*----------------------------------- RX ------------------------------------*/
+
+
+#if 0
+static void exception(struct atm_vcc *vcc)
+{
+ static int count = 0;
+ struct zatm_dev *zatm_dev = ZATM_DEV(vcc->dev);
+ struct zatm_vcc *zatm_vcc = ZATM_VCC(vcc);
+ unsigned long *qrp;
+ int i;
+
+ if (count++ > 2) return;
+ for (i = 0; i < 8; i++)
+ printk("TX%d: 0x%08lx\n",i,
+ zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+i));
+ for (i = 0; i < 5; i++)
+ printk("SH%d: 0x%08lx\n",i,
+ zpeekl(zatm_dev,uPD98401_IM(zatm_vcc->shaper)+16*i));
+ qrp = (unsigned long *) zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
+ uPD98401_TXVC_QRP);
+ printk("qrp=0x%08lx\n",(unsigned long) qrp);
+ for (i = 0; i < 4; i++) printk("QRP[%d]: 0x%08lx",i,qrp[i]);
+}
+#endif
+
+
+static const char *err_txt[] = {
+ "No error",
+ "RX buf underflow",
+ "RX FIFO overrun",
+ "Maximum len violation",
+ "CRC error",
+ "User abort",
+ "Length violation",
+ "T1 error",
+ "Deactivated",
+ "???",
+ "???",
+ "???",
+ "???",
+ "???",
+ "???",
+ "???"
+};
+
+
+static void poll_rx(struct atm_dev *dev,int mbx)
+{
+ struct zatm_dev *zatm_dev;
+ unsigned long pos;
+ u32 x;
+ int error;
+
+ EVENT("poll_rx\n",0,0);
+ zatm_dev = ZATM_DEV(dev);
+ pos = (zatm_dev->mbx_start[mbx] & ~0xffffUL) | zin(MTA(mbx));
+ while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {
+ u32 *here;
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+ int cells,size,chan;
+
+ EVENT("MBX: host 0x%lx, nic 0x%x\n",pos,x);
+ here = (u32 *) pos;
+ if (((pos += 16) & 0xffff) == zatm_dev->mbx_end[mbx])
+ pos = zatm_dev->mbx_start[mbx];
+ cells = here[0] & uPD98401_AAL5_SIZE;
+#if 0
+printk("RX IND: 0x%x, 0x%x, 0x%x, 0x%x\n",here[0],here[1],here[2],here[3]);
+{
+unsigned long *x;
+ printk("POOL: 0x%08x, 0x%08x\n",zpeekl(zatm_dev,
+ zatm_dev->pool_base),
+ zpeekl(zatm_dev,zatm_dev->pool_base+1));
+ x = (unsigned long *) here[2];
+ printk("[0..3] = 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n",
+ x[0],x[1],x[2],x[3]);
+}
+#endif
+ error = 0;
+ if (here[3] & uPD98401_AAL5_ERR) {
+ error = (here[3] & uPD98401_AAL5_ES) >>
+ uPD98401_AAL5_ES_SHIFT;
+ if (error == uPD98401_AAL5_ES_DEACT ||
+ error == uPD98401_AAL5_ES_FREE) continue;
+ }
+EVENT("error code 0x%x/0x%x\n",(here[3] & uPD98401_AAL5_ES) >>
+ uPD98401_AAL5_ES_SHIFT,error);
+ skb = ((struct rx_buffer_head *) bus_to_virt(here[2]))->skb;
+ __net_timestamp(skb);
+#if 0
+printk("[-3..0] 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",((unsigned *) skb->data)[-3],
+ ((unsigned *) skb->data)[-2],((unsigned *) skb->data)[-1],
+ ((unsigned *) skb->data)[0]);
+#endif
+ EVENT("skb 0x%lx, here 0x%lx\n",(unsigned long) skb,
+ (unsigned long) here);
+#if 0
+printk("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
+#endif
+ size = error ? 0 : ntohs(((__be16 *) skb->data)[cells*
+ ATM_CELL_PAYLOAD/sizeof(u16)-3]);
+ EVENT("got skb 0x%lx, size %d\n",(unsigned long) skb,size);
+ chan = (here[3] & uPD98401_AAL5_CHAN) >>
+ uPD98401_AAL5_CHAN_SHIFT;
+ if (chan < zatm_dev->chans && zatm_dev->rx_map[chan]) {
+ int pos;
+ vcc = zatm_dev->rx_map[chan];
+ pos = ZATM_VCC(vcc)->pool;
+ if (skb == zatm_dev->last_free[pos])
+ zatm_dev->last_free[pos] = NULL;
+ skb_unlink(skb, zatm_dev->pool + pos);
+ }
+ else {
+ printk(KERN_ERR DEV_LABEL "(itf %d): RX indication "
+ "for non-existing channel\n",dev->number);
+ size = 0;
+ vcc = NULL;
+ event_dump();
+ }
+ if (error) {
+ static unsigned long silence = 0;
+ static int last_error = 0;
+
+ if (error != last_error ||
+ time_after(jiffies, silence) || silence == 0){
+ printk(KERN_WARNING DEV_LABEL "(itf %d): "
+ "chan %d error %s\n",dev->number,chan,
+ err_txt[error]);
+ last_error = error;
+ silence = (jiffies+2*HZ)|1;
+ }
+ size = 0;
+ }
+ if (size && (size > cells*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER ||
+ size <= (cells-1)*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER)) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): size %d with %d "
+ "cells\n",dev->number,size,cells);
+ size = 0;
+ event_dump();
+ }
+ if (size > ATM_MAX_AAL5_PDU) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): size too big "
+ "(%d)\n",dev->number,size);
+ size = 0;
+ event_dump();
+ }
+ if (!size) {
+ dev_kfree_skb_irq(skb);
+ if (vcc) atomic_inc(&vcc->stats->rx_err);
+ continue;
+ }
+ if (!atm_charge(vcc,skb->truesize)) {
+ dev_kfree_skb_irq(skb);
+ continue;
+ }
+ skb->len = size;
+ ATM_SKB(skb)->vcc = vcc;
+ vcc->push(vcc,skb);
+ atomic_inc(&vcc->stats->rx);
+ }
+ zout(pos & 0xffff,MTA(mbx));
+#if 0 /* probably a stupid idea */
+ refill_pool(dev,zatm_vcc->pool);
+ /* maybe this saves us a few interrupts */
+#endif
+}
+
+
+static int open_rx_first(struct atm_vcc *vcc)
+{
+ struct zatm_dev *zatm_dev;
+ struct zatm_vcc *zatm_vcc;
+ unsigned long flags;
+ unsigned short chan;
+ int cells;
+
+ DPRINTK("open_rx_first (0x%x)\n",inb_p(0xc053));
+ zatm_dev = ZATM_DEV(vcc->dev);
+ zatm_vcc = ZATM_VCC(vcc);
+ zatm_vcc->rx_chan = 0;
+ if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;
+ if (vcc->qos.aal == ATM_AAL5) {
+ if (vcc->qos.rxtp.max_sdu > 65464)
+ vcc->qos.rxtp.max_sdu = 65464;
+ /* fix this - we may want to receive 64kB SDUs
+ later */
+ cells = DIV_ROUND_UP(vcc->qos.rxtp.max_sdu + ATM_AAL5_TRAILER,
+ ATM_CELL_PAYLOAD);
+ zatm_vcc->pool = pool_index(cells*ATM_CELL_PAYLOAD);
+ }
+ else {
+ cells = 1;
+ zatm_vcc->pool = ZATM_AAL0_POOL;
+ }
+ if (zatm_vcc->pool < 0) return -EMSGSIZE;
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zwait;
+ zout(uPD98401_OPEN_CHAN,CMR);
+ zwait;
+ DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
+ chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ DPRINTK("chan is %d\n",chan);
+ if (!chan) return -EAGAIN;
+ use_pool(vcc->dev,zatm_vcc->pool);
+ DPRINTK("pool %d\n",zatm_vcc->pool);
+ /* set up VC descriptor */
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zpokel(zatm_dev,zatm_vcc->pool << uPD98401_RXVC_POOL_SHIFT,
+ chan*VC_SIZE/4);
+ zpokel(zatm_dev,uPD98401_RXVC_OD | (vcc->qos.aal == ATM_AAL5 ?
+ uPD98401_RXVC_AR : 0) | cells,chan*VC_SIZE/4+1);
+ zpokel(zatm_dev,0,chan*VC_SIZE/4+2);
+ zatm_vcc->rx_chan = chan;
+ zatm_dev->rx_map[chan] = vcc;
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ return 0;
+}
+
+
+static int open_rx_second(struct atm_vcc *vcc)
+{
+ struct zatm_dev *zatm_dev;
+ struct zatm_vcc *zatm_vcc;
+ unsigned long flags;
+ int pos,shift;
+
+ DPRINTK("open_rx_second (0x%x)\n",inb_p(0xc053));
+ zatm_dev = ZATM_DEV(vcc->dev);
+ zatm_vcc = ZATM_VCC(vcc);
+ if (!zatm_vcc->rx_chan) return 0;
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ /* should also handle VPI @@@ */
+ pos = vcc->vci >> 1;
+ shift = (1-(vcc->vci & 1)) << 4;
+ zpokel(zatm_dev,(zpeekl(zatm_dev,pos) & ~(0xffff << shift)) |
+ ((zatm_vcc->rx_chan | uPD98401_RXLT_ENBL) << shift),pos);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ return 0;
+}
+
+
+static void close_rx(struct atm_vcc *vcc)
+{
+ struct zatm_dev *zatm_dev;
+ struct zatm_vcc *zatm_vcc;
+ unsigned long flags;
+ int pos,shift;
+
+ zatm_vcc = ZATM_VCC(vcc);
+ zatm_dev = ZATM_DEV(vcc->dev);
+ if (!zatm_vcc->rx_chan) return;
+ DPRINTK("close_rx\n");
+ /* disable receiver */
+ if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ pos = vcc->vci >> 1;
+ shift = (1-(vcc->vci & 1)) << 4;
+ zpokel(zatm_dev,zpeekl(zatm_dev,pos) & ~(0xffff << shift),pos);
+ zwait;
+ zout(uPD98401_NOP,CMR);
+ zwait;
+ zout(uPD98401_NOP,CMR);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ }
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zwait;
+ zout(uPD98401_DEACT_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
+ uPD98401_CHAN_ADDR_SHIFT),CMR);
+ zwait;
+ udelay(10); /* why oh why ... ? */
+ zout(uPD98401_CLOSE_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
+ uPD98401_CHAN_ADDR_SHIFT),CMR);
+ zwait;
+ if (!(zin(CMR) & uPD98401_CHAN_ADDR))
+ printk(KERN_CRIT DEV_LABEL "(itf %d): can't close RX channel "
+ "%d\n",vcc->dev->number,zatm_vcc->rx_chan);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ zatm_dev->rx_map[zatm_vcc->rx_chan] = NULL;
+ zatm_vcc->rx_chan = 0;
+ unuse_pool(vcc->dev,zatm_vcc->pool);
+}
+
+
+static int start_rx(struct atm_dev *dev)
+{
+ struct zatm_dev *zatm_dev;
+ int size,i;
+
+DPRINTK("start_rx\n");
+ zatm_dev = ZATM_DEV(dev);
+ size = sizeof(struct atm_vcc *)*zatm_dev->chans;
+ zatm_dev->rx_map = kzalloc(size,GFP_KERNEL);
+ if (!zatm_dev->rx_map) return -ENOMEM;
+ /* set VPI/VCI split (use all VCIs and give what's left to VPIs) */
+ zpokel(zatm_dev,(1 << dev->ci_range.vci_bits)-1,uPD98401_VRR);
+ /* prepare free buffer pools */
+ for (i = 0; i <= ZATM_LAST_POOL; i++) {
+ zatm_dev->pool_info[i].ref_count = 0;
+ zatm_dev->pool_info[i].rqa_count = 0;
+ zatm_dev->pool_info[i].rqu_count = 0;
+ zatm_dev->pool_info[i].low_water = LOW_MARK;
+ zatm_dev->pool_info[i].high_water = HIGH_MARK;
+ zatm_dev->pool_info[i].offset = 0;
+ zatm_dev->pool_info[i].next_off = 0;
+ zatm_dev->pool_info[i].next_cnt = 0;
+ zatm_dev->pool_info[i].next_thres = OFF_CNG_THRES;
+ }
+ return 0;
+}
+
+
+/*----------------------------------- TX ------------------------------------*/
+
+
+static int do_tx(struct sk_buff *skb)
+{
+ struct atm_vcc *vcc;
+ struct zatm_dev *zatm_dev;
+ struct zatm_vcc *zatm_vcc;
+ u32 *dsc;
+ unsigned long flags;
+
+ EVENT("do_tx\n",0,0);
+ DPRINTK("sending skb %p\n",skb);
+ vcc = ATM_SKB(skb)->vcc;
+ zatm_dev = ZATM_DEV(vcc->dev);
+ zatm_vcc = ZATM_VCC(vcc);
+ EVENT("iovcnt=%d\n",skb_shinfo(skb)->nr_frags,0);
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ if (!skb_shinfo(skb)->nr_frags) {
+ if (zatm_vcc->txing == RING_ENTRIES-1) {
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ return RING_BUSY;
+ }
+ zatm_vcc->txing++;
+ dsc = zatm_vcc->ring+zatm_vcc->ring_curr;
+ zatm_vcc->ring_curr = (zatm_vcc->ring_curr+RING_WORDS) &
+ (RING_ENTRIES*RING_WORDS-1);
+ dsc[1] = 0;
+ dsc[2] = skb->len;
+ dsc[3] = virt_to_bus(skb->data);
+ mb();
+ dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP | uPD98401_TXPD_SM
+ | (vcc->qos.aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 |
+ (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ?
+ uPD98401_CLPM_1 : uPD98401_CLPM_0));
+ EVENT("dsc (0x%lx)\n",(unsigned long) dsc,0);
+ }
+ else {
+printk("NONONONOO!!!!\n");
+ dsc = NULL;
+#if 0
+ u32 *put;
+ int i;
+
+ dsc = kmalloc(uPD98401_TXPD_SIZE * 2 +
+ uPD98401_TXBD_SIZE * ATM_SKB(skb)->iovcnt, GFP_ATOMIC);
+ if (!dsc) {
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_irq(skb);
+ return -EAGAIN;
+ }
+ /* @@@ should check alignment */
+ put = dsc+8;
+ dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP |
+ (vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 |
+ (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ?
+ uPD98401_CLPM_1 : uPD98401_CLPM_0));
+ dsc[1] = 0;
+ dsc[2] = ATM_SKB(skb)->iovcnt * uPD98401_TXBD_SIZE;
+ dsc[3] = virt_to_bus(put);
+ for (i = 0; i < ATM_SKB(skb)->iovcnt; i++) {
+ *put++ = ((struct iovec *) skb->data)[i].iov_len;
+ *put++ = virt_to_bus(((struct iovec *)
+ skb->data)[i].iov_base);
+ }
+ put[-2] |= uPD98401_TXBD_LAST;
+#endif
+ }
+ ZATM_PRV_DSC(skb) = dsc;
+ skb_queue_tail(&zatm_vcc->tx_queue,skb);
+ DPRINTK("QRP=0x%08lx\n",zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
+ uPD98401_TXVC_QRP));
+ zwait;
+ zout(uPD98401_TX_READY | (zatm_vcc->tx_chan <<
+ uPD98401_CHAN_ADDR_SHIFT),CMR);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ EVENT("done\n",0,0);
+ return 0;
+}
+
+
+static inline void dequeue_tx(struct atm_vcc *vcc)
+{
+ struct zatm_vcc *zatm_vcc;
+ struct sk_buff *skb;
+
+ EVENT("dequeue_tx\n",0,0);
+ zatm_vcc = ZATM_VCC(vcc);
+ skb = skb_dequeue(&zatm_vcc->tx_queue);
+ if (!skb) {
+ printk(KERN_CRIT DEV_LABEL "(itf %d): dequeue_tx but not "
+ "txing\n",vcc->dev->number);
+ return;
+ }
+#if 0 /* @@@ would fail on CLP */
+if (*ZATM_PRV_DSC(skb) != (uPD98401_TXPD_V | uPD98401_TXPD_DP |
+ uPD98401_TXPD_SM | uPD98401_TXPD_AAL5)) printk("@#*$!!!! (%08x)\n",
+ *ZATM_PRV_DSC(skb));
+#endif
+ *ZATM_PRV_DSC(skb) = 0; /* mark as invalid */
+ zatm_vcc->txing--;
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb_irq(skb);
+ while ((skb = skb_dequeue(&zatm_vcc->backlog)))
+ if (do_tx(skb) == RING_BUSY) {
+ skb_queue_head(&zatm_vcc->backlog,skb);
+ break;
+ }
+ atomic_inc(&vcc->stats->tx);
+ wake_up(&zatm_vcc->tx_wait);
+}
+
+
+static void poll_tx(struct atm_dev *dev,int mbx)
+{
+ struct zatm_dev *zatm_dev;
+ unsigned long pos;
+ u32 x;
+
+ EVENT("poll_tx\n",0,0);
+ zatm_dev = ZATM_DEV(dev);
+ pos = (zatm_dev->mbx_start[mbx] & ~0xffffUL) | zin(MTA(mbx));
+ while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {
+ int chan;
+
+#if 1
+ u32 data,*addr;
+
+ EVENT("MBX: host 0x%lx, nic 0x%x\n",pos,x);
+ addr = (u32 *) pos;
+ data = *addr;
+ chan = (data & uPD98401_TXI_CONN) >> uPD98401_TXI_CONN_SHIFT;
+ EVENT("addr = 0x%lx, data = 0x%08x,",(unsigned long) addr,
+ data);
+ EVENT("chan = %d\n",chan,0);
+#else
+NO !
+ chan = (zatm_dev->mbx_start[mbx][pos >> 2] & uPD98401_TXI_CONN)
+ >> uPD98401_TXI_CONN_SHIFT;
+#endif
+ if (chan < zatm_dev->chans && zatm_dev->tx_map[chan])
+ dequeue_tx(zatm_dev->tx_map[chan]);
+ else {
+ printk(KERN_CRIT DEV_LABEL "(itf %d): TX indication "
+ "for non-existing channel %d\n",dev->number,chan);
+ event_dump();
+ }
+ if (((pos += 4) & 0xffff) == zatm_dev->mbx_end[mbx])
+ pos = zatm_dev->mbx_start[mbx];
+ }
+ zout(pos & 0xffff,MTA(mbx));
+}
+
+
+/*
+ * BUG BUG BUG: Doesn't handle "new-style" rate specification yet.
+ */
+
+static int alloc_shaper(struct atm_dev *dev,int *pcr,int min,int max,int ubr)
+{
+ struct zatm_dev *zatm_dev;
+ unsigned long flags;
+ unsigned long i,m,c;
+ int shaper;
+
+ DPRINTK("alloc_shaper (min = %d, max = %d)\n",min,max);
+ zatm_dev = ZATM_DEV(dev);
+ if (!zatm_dev->free_shapers) return -EAGAIN;
+ for (shaper = 0; !((zatm_dev->free_shapers >> shaper) & 1); shaper++);
+ zatm_dev->free_shapers &= ~1 << shaper;
+ if (ubr) {
+ c = 5;
+ i = m = 1;
+ zatm_dev->ubr_ref_cnt++;
+ zatm_dev->ubr = shaper;
+ *pcr = 0;
+ }
+ else {
+ if (min) {
+ if (min <= 255) {
+ i = min;
+ m = ATM_OC3_PCR;
+ }
+ else {
+ i = 255;
+ m = ATM_OC3_PCR*255/min;
+ }
+ }
+ else {
+ if (max > zatm_dev->tx_bw) max = zatm_dev->tx_bw;
+ if (max <= 255) {
+ i = max;
+ m = ATM_OC3_PCR;
+ }
+ else {
+ i = 255;
+ m = DIV_ROUND_UP(ATM_OC3_PCR*255, max);
+ }
+ }
+ if (i > m) {
+ printk(KERN_CRIT DEV_LABEL "shaper algorithm botched "
+ "[%d,%d] -> i=%ld,m=%ld\n",min,max,i,m);
+ m = i;
+ }
+ *pcr = i*ATM_OC3_PCR/m;
+ c = 20; /* @@@ should use max_cdv ! */
+ if ((min && *pcr < min) || (max && *pcr > max)) return -EINVAL;
+ if (zatm_dev->tx_bw < *pcr) return -EAGAIN;
+ zatm_dev->tx_bw -= *pcr;
+ }
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ DPRINTK("i = %d, m = %d, PCR = %d\n",i,m,*pcr);
+ zpokel(zatm_dev,(i << uPD98401_IM_I_SHIFT) | m,uPD98401_IM(shaper));
+ zpokel(zatm_dev,c << uPD98401_PC_C_SHIFT,uPD98401_PC(shaper));
+ zpokel(zatm_dev,0,uPD98401_X(shaper));
+ zpokel(zatm_dev,0,uPD98401_Y(shaper));
+ zpokel(zatm_dev,uPD98401_PS_E,uPD98401_PS(shaper));
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ return shaper;
+}
+
+
+static void dealloc_shaper(struct atm_dev *dev,int shaper)
+{
+ struct zatm_dev *zatm_dev;
+ unsigned long flags;
+
+ zatm_dev = ZATM_DEV(dev);
+ if (shaper == zatm_dev->ubr) {
+ if (--zatm_dev->ubr_ref_cnt) return;
+ zatm_dev->ubr = -1;
+ }
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zpokel(zatm_dev,zpeekl(zatm_dev,uPD98401_PS(shaper)) & ~uPD98401_PS_E,
+ uPD98401_PS(shaper));
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ zatm_dev->free_shapers |= 1 << shaper;
+}
+
+
+static void close_tx(struct atm_vcc *vcc)
+{
+ struct zatm_dev *zatm_dev;
+ struct zatm_vcc *zatm_vcc;
+ unsigned long flags;
+ int chan;
+
+ zatm_vcc = ZATM_VCC(vcc);
+ zatm_dev = ZATM_DEV(vcc->dev);
+ chan = zatm_vcc->tx_chan;
+ if (!chan) return;
+ DPRINTK("close_tx\n");
+ if (skb_peek(&zatm_vcc->backlog)) {
+ printk("waiting for backlog to drain ...\n");
+ event_dump();
+ wait_event(zatm_vcc->tx_wait, !skb_peek(&zatm_vcc->backlog));
+ }
+ if (skb_peek(&zatm_vcc->tx_queue)) {
+ printk("waiting for TX queue to drain ...\n");
+ event_dump();
+ wait_event(zatm_vcc->tx_wait, !skb_peek(&zatm_vcc->tx_queue));
+ }
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+#if 0
+ zwait;
+ zout(uPD98401_DEACT_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
+#endif
+ zwait;
+ zout(uPD98401_CLOSE_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
+ zwait;
+ if (!(zin(CMR) & uPD98401_CHAN_ADDR))
+ printk(KERN_CRIT DEV_LABEL "(itf %d): can't close TX channel "
+ "%d\n",vcc->dev->number,chan);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ zatm_vcc->tx_chan = 0;
+ zatm_dev->tx_map[chan] = NULL;
+ if (zatm_vcc->shaper != zatm_dev->ubr) {
+ zatm_dev->tx_bw += vcc->qos.txtp.min_pcr;
+ dealloc_shaper(vcc->dev,zatm_vcc->shaper);
+ }
+ kfree(zatm_vcc->ring);
+}
+
+
+static int open_tx_first(struct atm_vcc *vcc)
+{
+ struct zatm_dev *zatm_dev;
+ struct zatm_vcc *zatm_vcc;
+ unsigned long flags;
+ u32 *loop;
+ unsigned short chan;
+ int unlimited;
+
+ DPRINTK("open_tx_first\n");
+ zatm_dev = ZATM_DEV(vcc->dev);
+ zatm_vcc = ZATM_VCC(vcc);
+ zatm_vcc->tx_chan = 0;
+ if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zwait;
+ zout(uPD98401_OPEN_CHAN,CMR);
+ zwait;
+ DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
+ chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ DPRINTK("chan is %d\n",chan);
+ if (!chan) return -EAGAIN;
+ unlimited = vcc->qos.txtp.traffic_class == ATM_UBR &&
+ (!vcc->qos.txtp.max_pcr || vcc->qos.txtp.max_pcr == ATM_MAX_PCR ||
+ vcc->qos.txtp.max_pcr >= ATM_OC3_PCR);
+ if (unlimited && zatm_dev->ubr != -1) zatm_vcc->shaper = zatm_dev->ubr;
+ else {
+ int uninitialized_var(pcr);
+
+ if (unlimited) vcc->qos.txtp.max_sdu = ATM_MAX_AAL5_PDU;
+ if ((zatm_vcc->shaper = alloc_shaper(vcc->dev,&pcr,
+ vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,unlimited))
+ < 0) {
+ close_tx(vcc);
+ return zatm_vcc->shaper;
+ }
+ if (pcr > ATM_OC3_PCR) pcr = ATM_OC3_PCR;
+ vcc->qos.txtp.min_pcr = vcc->qos.txtp.max_pcr = pcr;
+ }
+ zatm_vcc->tx_chan = chan;
+ skb_queue_head_init(&zatm_vcc->tx_queue);
+ init_waitqueue_head(&zatm_vcc->tx_wait);
+ /* initialize ring */
+ zatm_vcc->ring = kzalloc(RING_SIZE,GFP_KERNEL);
+ if (!zatm_vcc->ring) return -ENOMEM;
+ loop = zatm_vcc->ring+RING_ENTRIES*RING_WORDS;
+ loop[0] = uPD98401_TXPD_V;
+ loop[1] = loop[2] = 0;
+ loop[3] = virt_to_bus(zatm_vcc->ring);
+ zatm_vcc->ring_curr = 0;
+ zatm_vcc->txing = 0;
+ skb_queue_head_init(&zatm_vcc->backlog);
+ zpokel(zatm_dev,virt_to_bus(zatm_vcc->ring),
+ chan*VC_SIZE/4+uPD98401_TXVC_QRP);
+ return 0;
+}
+
+
+static int open_tx_second(struct atm_vcc *vcc)
+{
+ struct zatm_dev *zatm_dev;
+ struct zatm_vcc *zatm_vcc;
+ unsigned long flags;
+
+ DPRINTK("open_tx_second\n");
+ zatm_dev = ZATM_DEV(vcc->dev);
+ zatm_vcc = ZATM_VCC(vcc);
+ if (!zatm_vcc->tx_chan) return 0;
+ /* set up VC descriptor */
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4);
+ zpokel(zatm_dev,uPD98401_TXVC_L | (zatm_vcc->shaper <<
+ uPD98401_TXVC_SHP_SHIFT) | (vcc->vpi << uPD98401_TXVC_VPI_SHIFT) |
+ vcc->vci,zatm_vcc->tx_chan*VC_SIZE/4+1);
+ zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4+2);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ zatm_dev->tx_map[zatm_vcc->tx_chan] = vcc;
+ return 0;
+}
+
+
+static int start_tx(struct atm_dev *dev)
+{
+ struct zatm_dev *zatm_dev;
+ int i;
+
+ DPRINTK("start_tx\n");
+ zatm_dev = ZATM_DEV(dev);
+ zatm_dev->tx_map = kmalloc(sizeof(struct atm_vcc *)*
+ zatm_dev->chans,GFP_KERNEL);
+ if (!zatm_dev->tx_map) return -ENOMEM;
+ zatm_dev->tx_bw = ATM_OC3_PCR;
+ zatm_dev->free_shapers = (1 << NR_SHAPERS)-1;
+ zatm_dev->ubr = -1;
+ zatm_dev->ubr_ref_cnt = 0;
+ /* initialize shapers */
+ for (i = 0; i < NR_SHAPERS; i++) zpokel(zatm_dev,0,uPD98401_PS(i));
+ return 0;
+}
+
+
+/*------------------------------- interrupts --------------------------------*/
+
+
+static irqreturn_t zatm_int(int irq,void *dev_id)
+{
+ struct atm_dev *dev;
+ struct zatm_dev *zatm_dev;
+ u32 reason;
+ int handled = 0;
+
+ dev = dev_id;
+ zatm_dev = ZATM_DEV(dev);
+ while ((reason = zin(GSR))) {
+ handled = 1;
+ EVENT("reason 0x%x\n",reason,0);
+ if (reason & uPD98401_INT_PI) {
+ EVENT("PHY int\n",0,0);
+ dev->phy->interrupt(dev);
+ }
+ if (reason & uPD98401_INT_RQA) {
+ unsigned long pools;
+ int i;
+
+ pools = zin(RQA);
+ EVENT("RQA (0x%08x)\n",pools,0);
+ for (i = 0; pools; i++) {
+ if (pools & 1) {
+ refill_pool(dev,i);
+ zatm_dev->pool_info[i].rqa_count++;
+ }
+ pools >>= 1;
+ }
+ }
+ if (reason & uPD98401_INT_RQU) {
+ unsigned long pools;
+ int i;
+ pools = zin(RQU);
+ printk(KERN_WARNING DEV_LABEL "(itf %d): RQU 0x%08lx\n",
+ dev->number,pools);
+ event_dump();
+ for (i = 0; pools; i++) {
+ if (pools & 1) {
+ refill_pool(dev,i);
+ zatm_dev->pool_info[i].rqu_count++;
+ }
+ pools >>= 1;
+ }
+ }
+ /* don't handle RD */
+ if (reason & uPD98401_INT_SPE)
+ printk(KERN_ALERT DEV_LABEL "(itf %d): system parity "
+ "error at 0x%08x\n",dev->number,zin(ADDR));
+ if (reason & uPD98401_INT_CPE)
+ printk(KERN_ALERT DEV_LABEL "(itf %d): control memory "
+ "parity error at 0x%08x\n",dev->number,zin(ADDR));
+ if (reason & uPD98401_INT_SBE) {
+ printk(KERN_ALERT DEV_LABEL "(itf %d): system bus "
+ "error at 0x%08x\n",dev->number,zin(ADDR));
+ event_dump();
+ }
+ /* don't handle IND */
+ if (reason & uPD98401_INT_MF) {
+ printk(KERN_CRIT DEV_LABEL "(itf %d): mailbox full "
+ "(0x%x)\n",dev->number,(reason & uPD98401_INT_MF)
+ >> uPD98401_INT_MF_SHIFT);
+ event_dump();
+ /* @@@ should try to recover */
+ }
+ if (reason & uPD98401_INT_MM) {
+ if (reason & 1) poll_rx(dev,0);
+ if (reason & 2) poll_rx(dev,1);
+ if (reason & 4) poll_tx(dev,2);
+ if (reason & 8) poll_tx(dev,3);
+ }
+ /* @@@ handle RCRn */
+ }
+ return IRQ_RETVAL(handled);
+}
+
+
+/*----------------------------- (E)EPROM access -----------------------------*/
+
+
+static void eprom_set(struct zatm_dev *zatm_dev, unsigned long value,
+ unsigned short cmd)
+{
+ int error;
+
+ if ((error = pci_write_config_dword(zatm_dev->pci_dev,cmd,value)))
+ printk(KERN_ERR DEV_LABEL ": PCI write failed (0x%02x)\n",
+ error);
+}
+
+
+static unsigned long eprom_get(struct zatm_dev *zatm_dev, unsigned short cmd)
+{
+ unsigned int value;
+ int error;
+
+ if ((error = pci_read_config_dword(zatm_dev->pci_dev,cmd,&value)))
+ printk(KERN_ERR DEV_LABEL ": PCI read failed (0x%02x)\n",
+ error);
+ return value;
+}
+
+
+static void eprom_put_bits(struct zatm_dev *zatm_dev, unsigned long data,
+ int bits, unsigned short cmd)
+{
+ unsigned long value;
+ int i;
+
+ for (i = bits-1; i >= 0; i--) {
+ value = ZEPROM_CS | (((data >> i) & 1) ? ZEPROM_DI : 0);
+ eprom_set(zatm_dev,value,cmd);
+ eprom_set(zatm_dev,value | ZEPROM_SK,cmd);
+ eprom_set(zatm_dev,value,cmd);
+ }
+}
+
+
+static void eprom_get_byte(struct zatm_dev *zatm_dev, unsigned char *byte,
+ unsigned short cmd)
+{
+ int i;
+
+ *byte = 0;
+ for (i = 8; i; i--) {
+ eprom_set(zatm_dev,ZEPROM_CS,cmd);
+ eprom_set(zatm_dev,ZEPROM_CS | ZEPROM_SK,cmd);
+ *byte <<= 1;
+ if (eprom_get(zatm_dev,cmd) & ZEPROM_DO) *byte |= 1;
+ eprom_set(zatm_dev,ZEPROM_CS,cmd);
+ }
+}
+
+
+static unsigned char eprom_try_esi(struct atm_dev *dev, unsigned short cmd,
+ int offset, int swap)
+{
+ unsigned char buf[ZEPROM_SIZE];
+ struct zatm_dev *zatm_dev;
+ int i;
+
+ zatm_dev = ZATM_DEV(dev);
+ for (i = 0; i < ZEPROM_SIZE; i += 2) {
+ eprom_set(zatm_dev,ZEPROM_CS,cmd); /* select EPROM */
+ eprom_put_bits(zatm_dev,ZEPROM_CMD_READ,ZEPROM_CMD_LEN,cmd);
+ eprom_put_bits(zatm_dev,i >> 1,ZEPROM_ADDR_LEN,cmd);
+ eprom_get_byte(zatm_dev,buf+i+swap,cmd);
+ eprom_get_byte(zatm_dev,buf+i+1-swap,cmd);
+ eprom_set(zatm_dev,0,cmd); /* deselect EPROM */
+ }
+ memcpy(dev->esi,buf+offset,ESI_LEN);
+ return memcmp(dev->esi,"\0\0\0\0\0",ESI_LEN); /* assumes ESI_LEN == 6 */
+}
+
+
+static void eprom_get_esi(struct atm_dev *dev)
+{
+ if (eprom_try_esi(dev,ZEPROM_V1_REG,ZEPROM_V1_ESI_OFF,1)) return;
+ (void) eprom_try_esi(dev,ZEPROM_V2_REG,ZEPROM_V2_ESI_OFF,0);
+}
+
+
+/*--------------------------------- entries ---------------------------------*/
+
+
+static int zatm_init(struct atm_dev *dev)
+{
+ struct zatm_dev *zatm_dev;
+ struct pci_dev *pci_dev;
+ unsigned short command;
+ int error,i,last;
+ unsigned long t0,t1,t2;
+
+ DPRINTK(">zatm_init\n");
+ zatm_dev = ZATM_DEV(dev);
+ spin_lock_init(&zatm_dev->lock);
+ pci_dev = zatm_dev->pci_dev;
+ zatm_dev->base = pci_resource_start(pci_dev, 0);
+ zatm_dev->irq = pci_dev->irq;
+ if ((error = pci_read_config_word(pci_dev,PCI_COMMAND,&command))) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): init error 0x%02x\n",
+ dev->number,error);
+ return -EINVAL;
+ }
+ if ((error = pci_write_config_word(pci_dev,PCI_COMMAND,
+ command | PCI_COMMAND_IO | PCI_COMMAND_MASTER))) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): can't enable IO (0x%02x)"
+ "\n",dev->number,error);
+ return -EIO;
+ }
+ eprom_get_esi(dev);
+ printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",
+ dev->number,pci_dev->revision,zatm_dev->base,zatm_dev->irq);
+ /* reset uPD98401 */
+ zout(0,SWR);
+ while (!(zin(GSR) & uPD98401_INT_IND));
+ zout(uPD98401_GMR_ONE /*uPD98401_BURST4*/,GMR);
+ last = MAX_CRAM_SIZE;
+ for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
+ zpokel(zatm_dev,0x55555555,i);
+ if (zpeekl(zatm_dev,i) != 0x55555555) last = i;
+ else {
+ zpokel(zatm_dev,0xAAAAAAAA,i);
+ if (zpeekl(zatm_dev,i) != 0xAAAAAAAA) last = i;
+ else zpokel(zatm_dev,i,i);
+ }
+ }
+ for (i = 0; i < last; i += RAM_INCREMENT)
+ if (zpeekl(zatm_dev,i) != i) break;
+ zatm_dev->mem = i << 2;
+ while (i) zpokel(zatm_dev,0,--i);
+ /* reset again to rebuild memory pointers */
+ zout(0,SWR);
+ while (!(zin(GSR) & uPD98401_INT_IND));
+ zout(uPD98401_GMR_ONE | uPD98401_BURST8 | uPD98401_BURST4 |
+ uPD98401_BURST2 | uPD98401_GMR_PM | uPD98401_GMR_DR,GMR);
+ /* TODO: should shrink allocation now */
+ printk("mem=%dkB,%s (",zatm_dev->mem >> 10,zatm_dev->copper ? "UTP" :
+ "MMF");
+ for (i = 0; i < ESI_LEN; i++)
+ printk("%02X%s",dev->esi[i],i == ESI_LEN-1 ? ")\n" : "-");
+ do {
+ unsigned long flags;
+
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ t0 = zpeekl(zatm_dev,uPD98401_TSR);
+ udelay(10);
+ t1 = zpeekl(zatm_dev,uPD98401_TSR);
+ udelay(1010);
+ t2 = zpeekl(zatm_dev,uPD98401_TSR);
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ }
+ while (t0 > t1 || t1 > t2); /* loop if wrapping ... */
+ zatm_dev->khz = t2-2*t1+t0;
+ printk(KERN_NOTICE DEV_LABEL "(itf %d): uPD98401 %d.%d at %d.%03d "
+ "MHz\n",dev->number,
+ (zin(VER) & uPD98401_MAJOR) >> uPD98401_MAJOR_SHIFT,
+ zin(VER) & uPD98401_MINOR,zatm_dev->khz/1000,zatm_dev->khz % 1000);
+ return uPD98402_init(dev);
+}
+
+
+static int zatm_start(struct atm_dev *dev)
+{
+ struct zatm_dev *zatm_dev = ZATM_DEV(dev);
+ struct pci_dev *pdev = zatm_dev->pci_dev;
+ unsigned long curr;
+ int pools,vccs,rx;
+ int error, i, ld;
+
+ DPRINTK("zatm_start\n");
+ zatm_dev->rx_map = zatm_dev->tx_map = NULL;
+ for (i = 0; i < NR_MBX; i++)
+ zatm_dev->mbx_start[i] = 0;
+ error = request_irq(zatm_dev->irq, zatm_int, IRQF_SHARED, DEV_LABEL, dev);
+ if (error < 0) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
+ dev->number,zatm_dev->irq);
+ goto done;
+ }
+ /* define memory regions */
+ pools = NR_POOLS;
+ if (NR_SHAPERS*SHAPER_SIZE > pools*POOL_SIZE)
+ pools = NR_SHAPERS*SHAPER_SIZE/POOL_SIZE;
+ vccs = (zatm_dev->mem-NR_SHAPERS*SHAPER_SIZE-pools*POOL_SIZE)/
+ (2*VC_SIZE+RX_SIZE);
+ ld = -1;
+ for (rx = 1; rx < vccs; rx <<= 1) ld++;
+ dev->ci_range.vpi_bits = 0; /* @@@ no VPI for now */
+ dev->ci_range.vci_bits = ld;
+ dev->link_rate = ATM_OC3_PCR;
+ zatm_dev->chans = vccs; /* ??? */
+ curr = rx*RX_SIZE/4;
+ DPRINTK("RX pool 0x%08lx\n",curr);
+ zpokel(zatm_dev,curr,uPD98401_PMA); /* receive pool */
+ zatm_dev->pool_base = curr;
+ curr += pools*POOL_SIZE/4;
+ DPRINTK("Shapers 0x%08lx\n",curr);
+ zpokel(zatm_dev,curr,uPD98401_SMA); /* shapers */
+ curr += NR_SHAPERS*SHAPER_SIZE/4;
+ DPRINTK("Free 0x%08lx\n",curr);
+ zpokel(zatm_dev,curr,uPD98401_TOS); /* free pool */
+ printk(KERN_INFO DEV_LABEL "(itf %d): %d shapers, %d pools, %d RX, "
+ "%ld VCs\n",dev->number,NR_SHAPERS,pools,rx,
+ (zatm_dev->mem-curr*4)/VC_SIZE);
+ /* create mailboxes */
+ for (i = 0; i < NR_MBX; i++) {
+ void *mbx;
+ dma_addr_t mbx_dma;
+
+ if (!mbx_entries[i])
+ continue;
+ mbx = dma_alloc_coherent(&pdev->dev,
+ 2 * MBX_SIZE(i), &mbx_dma, GFP_KERNEL);
+ if (!mbx) {
+ error = -ENOMEM;
+ goto out;
+ }
+ /*
+ * Alignment provided by dma_alloc_coherent() isn't enough
+ * for this device.
+ */
+ if (((unsigned long)mbx ^ mbx_dma) & 0xffff) {
+ printk(KERN_ERR DEV_LABEL "(itf %d): system "
+ "bus incompatible with driver\n", dev->number);
+ dma_free_coherent(&pdev->dev, 2*MBX_SIZE(i), mbx, mbx_dma);
+ error = -ENODEV;
+ goto out;
+ }
+ DPRINTK("mbx@0x%08lx-0x%08lx\n", mbx, mbx + MBX_SIZE(i));
+ zatm_dev->mbx_start[i] = (unsigned long)mbx;
+ zatm_dev->mbx_dma[i] = mbx_dma;
+ zatm_dev->mbx_end[i] = (zatm_dev->mbx_start[i] + MBX_SIZE(i)) &
+ 0xffff;
+ zout(mbx_dma >> 16, MSH(i));
+ zout(mbx_dma, MSL(i));
+ zout(zatm_dev->mbx_end[i], MBA(i));
+ zout((unsigned long)mbx & 0xffff, MTA(i));
+ zout((unsigned long)mbx & 0xffff, MWA(i));
+ }
+ error = start_tx(dev);
+ if (error)
+ goto out;
+ error = start_rx(dev);
+ if (error)
+ goto out_tx;
+ error = dev->phy->start(dev);
+ if (error)
+ goto out_rx;
+ zout(0xffffffff,IMR); /* enable interrupts */
+ /* enable TX & RX */
+ zout(zin(GMR) | uPD98401_GMR_SE | uPD98401_GMR_RE,GMR);
+done:
+ return error;
+
+out_rx:
+ kfree(zatm_dev->rx_map);
+out_tx:
+ kfree(zatm_dev->tx_map);
+out:
+ while (i-- > 0) {
+ dma_free_coherent(&pdev->dev, 2 * MBX_SIZE(i),
+ (void *)zatm_dev->mbx_start[i],
+ zatm_dev->mbx_dma[i]);
+ }
+ free_irq(zatm_dev->irq, dev);
+ goto done;
+}
+
+
+static void zatm_close(struct atm_vcc *vcc)
+{
+ DPRINTK(">zatm_close\n");
+ if (!ZATM_VCC(vcc)) return;
+ clear_bit(ATM_VF_READY,&vcc->flags);
+ close_rx(vcc);
+ EVENT("close_tx\n",0,0);
+ close_tx(vcc);
+ DPRINTK("zatm_close: done waiting\n");
+ /* deallocate memory */
+ kfree(ZATM_VCC(vcc));
+ vcc->dev_data = NULL;
+ clear_bit(ATM_VF_ADDR,&vcc->flags);
+}
+
+
+static int zatm_open(struct atm_vcc *vcc)
+{
+ struct zatm_dev *zatm_dev;
+ struct zatm_vcc *zatm_vcc;
+ short vpi = vcc->vpi;
+ int vci = vcc->vci;
+ int error;
+
+ DPRINTK(">zatm_open\n");
+ zatm_dev = ZATM_DEV(vcc->dev);
+ if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))
+ vcc->dev_data = NULL;
+ if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
+ set_bit(ATM_VF_ADDR,&vcc->flags);
+ if (vcc->qos.aal != ATM_AAL5) return -EINVAL; /* @@@ AAL0 */
+ DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
+ vcc->vci);
+ if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
+ zatm_vcc = kmalloc(sizeof(struct zatm_vcc),GFP_KERNEL);
+ if (!zatm_vcc) {
+ clear_bit(ATM_VF_ADDR,&vcc->flags);
+ return -ENOMEM;
+ }
+ vcc->dev_data = zatm_vcc;
+ ZATM_VCC(vcc)->tx_chan = 0; /* for zatm_close after open_rx */
+ if ((error = open_rx_first(vcc))) {
+ zatm_close(vcc);
+ return error;
+ }
+ if ((error = open_tx_first(vcc))) {
+ zatm_close(vcc);
+ return error;
+ }
+ }
+ if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
+ if ((error = open_rx_second(vcc))) {
+ zatm_close(vcc);
+ return error;
+ }
+ if ((error = open_tx_second(vcc))) {
+ zatm_close(vcc);
+ return error;
+ }
+ set_bit(ATM_VF_READY,&vcc->flags);
+ return 0;
+}
+
+
+static int zatm_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flags)
+{
+ printk("Not yet implemented\n");
+ return -ENOSYS;
+ /* @@@ */
+}
+
+
+static int zatm_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
+{
+ struct zatm_dev *zatm_dev;
+ unsigned long flags;
+
+ zatm_dev = ZATM_DEV(dev);
+ switch (cmd) {
+ case ZATM_GETPOOLZ:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ /* fall through */
+ case ZATM_GETPOOL:
+ {
+ struct zatm_pool_info info;
+ int pool;
+
+ if (get_user(pool,
+ &((struct zatm_pool_req __user *) arg)->pool_num))
+ return -EFAULT;
+ if (pool < 0 || pool > ZATM_LAST_POOL)
+ return -EINVAL;
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ info = zatm_dev->pool_info[pool];
+ if (cmd == ZATM_GETPOOLZ) {
+ zatm_dev->pool_info[pool].rqa_count = 0;
+ zatm_dev->pool_info[pool].rqu_count = 0;
+ }
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ return copy_to_user(
+ &((struct zatm_pool_req __user *) arg)->info,
+ &info,sizeof(info)) ? -EFAULT : 0;
+ }
+ case ZATM_SETPOOL:
+ {
+ struct zatm_pool_info info;
+ int pool;
+
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ if (get_user(pool,
+ &((struct zatm_pool_req __user *) arg)->pool_num))
+ return -EFAULT;
+ if (pool < 0 || pool > ZATM_LAST_POOL)
+ return -EINVAL;
+ if (copy_from_user(&info,
+ &((struct zatm_pool_req __user *) arg)->info,
+ sizeof(info))) return -EFAULT;
+ if (!info.low_water)
+ info.low_water = zatm_dev->
+ pool_info[pool].low_water;
+ if (!info.high_water)
+ info.high_water = zatm_dev->
+ pool_info[pool].high_water;
+ if (!info.next_thres)
+ info.next_thres = zatm_dev->
+ pool_info[pool].next_thres;
+ if (info.low_water >= info.high_water ||
+ info.low_water < 0)
+ return -EINVAL;
+ spin_lock_irqsave(&zatm_dev->lock, flags);
+ zatm_dev->pool_info[pool].low_water =
+ info.low_water;
+ zatm_dev->pool_info[pool].high_water =
+ info.high_water;
+ zatm_dev->pool_info[pool].next_thres =
+ info.next_thres;
+ spin_unlock_irqrestore(&zatm_dev->lock, flags);
+ return 0;
+ }
+ default:
+ if (!dev->phy->ioctl) return -ENOIOCTLCMD;
+ return dev->phy->ioctl(dev,cmd,arg);
+ }
+}
+
+
+static int zatm_getsockopt(struct atm_vcc *vcc,int level,int optname,
+ void __user *optval,int optlen)
+{
+ return -EINVAL;
+}
+
+
+static int zatm_setsockopt(struct atm_vcc *vcc,int level,int optname,
+ void __user *optval,unsigned int optlen)
+{
+ return -EINVAL;
+}
+
+static int zatm_send(struct atm_vcc *vcc,struct sk_buff *skb)
+{
+ int error;
+
+ EVENT(">zatm_send 0x%lx\n",(unsigned long) skb,0);
+ if (!ZATM_VCC(vcc)->tx_chan || !test_bit(ATM_VF_READY,&vcc->flags)) {
+ if (vcc->pop) vcc->pop(vcc,skb);
+ else dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+ if (!skb) {
+ printk(KERN_CRIT "!skb in zatm_send ?\n");
+ if (vcc->pop) vcc->pop(vcc,skb);
+ return -EINVAL;
+ }
+ ATM_SKB(skb)->vcc = vcc;
+ error = do_tx(skb);
+ if (error != RING_BUSY) return error;
+ skb_queue_tail(&ZATM_VCC(vcc)->backlog,skb);
+ return 0;
+}
+
+
+static void zatm_phy_put(struct atm_dev *dev,unsigned char value,
+ unsigned long addr)
+{
+ struct zatm_dev *zatm_dev;
+
+ zatm_dev = ZATM_DEV(dev);
+ zwait;
+ zout(value,CER);
+ zout(uPD98401_IND_ACC | uPD98401_IA_B0 |
+ (uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
+}
+
+
+static unsigned char zatm_phy_get(struct atm_dev *dev,unsigned long addr)
+{
+ struct zatm_dev *zatm_dev;
+
+ zatm_dev = ZATM_DEV(dev);
+ zwait;
+ zout(uPD98401_IND_ACC | uPD98401_IA_B0 | uPD98401_IA_RW |
+ (uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
+ zwait;
+ return zin(CER) & 0xff;
+}
+
+
+static const struct atmdev_ops ops = {
+ .open = zatm_open,
+ .close = zatm_close,
+ .ioctl = zatm_ioctl,
+ .getsockopt = zatm_getsockopt,
+ .setsockopt = zatm_setsockopt,
+ .send = zatm_send,
+ .phy_put = zatm_phy_put,
+ .phy_get = zatm_phy_get,
+ .change_qos = zatm_change_qos,
+};
+
+static int zatm_init_one(struct pci_dev *pci_dev,
+ const struct pci_device_id *ent)
+{
+ struct atm_dev *dev;
+ struct zatm_dev *zatm_dev;
+ int ret = -ENOMEM;
+
+ zatm_dev = kmalloc(sizeof(*zatm_dev), GFP_KERNEL);
+ if (!zatm_dev) {
+ printk(KERN_EMERG "%s: memory shortage\n", DEV_LABEL);
+ goto out;
+ }
+
+ dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &ops, -1, NULL);
+ if (!dev)
+ goto out_free;
+
+ ret = pci_enable_device(pci_dev);
+ if (ret < 0)
+ goto out_deregister;
+
+ ret = pci_request_regions(pci_dev, DEV_LABEL);
+ if (ret < 0)
+ goto out_disable;
+
+ ret = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32));
+ if (ret < 0)
+ goto out_disable;
+
+ zatm_dev->pci_dev = pci_dev;
+ dev->dev_data = zatm_dev;
+ zatm_dev->copper = (int)ent->driver_data;
+ if ((ret = zatm_init(dev)) || (ret = zatm_start(dev)))
+ goto out_release;
+
+ pci_set_drvdata(pci_dev, dev);
+ zatm_dev->more = zatm_boards;
+ zatm_boards = dev;
+ ret = 0;
+out:
+ return ret;
+
+out_release:
+ pci_release_regions(pci_dev);
+out_disable:
+ pci_disable_device(pci_dev);
+out_deregister:
+ atm_dev_deregister(dev);
+out_free:
+ kfree(zatm_dev);
+ goto out;
+}
+
+
+MODULE_LICENSE("GPL");
+
+static struct pci_device_id zatm_pci_tbl[] = {
+ { PCI_VDEVICE(ZEITNET, PCI_DEVICE_ID_ZEITNET_1221), ZATM_COPPER },
+ { PCI_VDEVICE(ZEITNET, PCI_DEVICE_ID_ZEITNET_1225), 0 },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, zatm_pci_tbl);
+
+static struct pci_driver zatm_driver = {
+ .name = DEV_LABEL,
+ .id_table = zatm_pci_tbl,
+ .probe = zatm_init_one,
+};
+
+static int __init zatm_init_module(void)
+{
+ return pci_register_driver(&zatm_driver);
+}
+
+module_init(zatm_init_module);
+/* module_exit not defined so not unloadable */
diff --git a/drivers/atm/zatm.h b/drivers/atm/zatm.h
new file mode 100644
index 000000000..ae9165ce1
--- /dev/null
+++ b/drivers/atm/zatm.h
@@ -0,0 +1,103 @@
+/* drivers/atm/zatm.h - ZeitNet ZN122x device driver declarations */
+
+/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#ifndef DRIVER_ATM_ZATM_H
+#define DRIVER_ATM_ZATM_H
+
+#include <linux/skbuff.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/sonet.h>
+#include <linux/pci.h>
+
+
+#define DEV_LABEL "zatm"
+
+#define MAX_AAL5_PDU 10240 /* allocate for AAL5 PDUs of this size */
+#define MAX_RX_SIZE_LD 14 /* ceil(log2((MAX_AAL5_PDU+47)/48)) */
+
+#define LOW_MARK 12 /* start adding new buffers if less than 12 */
+#define HIGH_MARK 30 /* stop adding buffers after reaching 30 */
+#define OFF_CNG_THRES 5 /* threshold for offset changes */
+
+#define RX_SIZE 2 /* RX lookup entry size (in bytes) */
+#define NR_POOLS 32 /* number of free buffer pointers */
+#define POOL_SIZE 8 /* buffer entry size (in bytes) */
+#define NR_SHAPERS 16 /* number of shapers */
+#define SHAPER_SIZE 4 /* shaper entry size (in bytes) */
+#define VC_SIZE 32 /* VC dsc (TX or RX) size (in bytes) */
+
+#define RING_ENTRIES 32 /* ring entries (without back pointer) */
+#define RING_WORDS 4 /* ring element size */
+#define RING_SIZE (sizeof(unsigned long)*(RING_ENTRIES+1)*RING_WORDS)
+
+#define NR_MBX 4 /* four mailboxes */
+#define MBX_RX_0 0 /* mailbox indices */
+#define MBX_RX_1 1
+#define MBX_TX_0 2
+#define MBX_TX_1 3
+
+struct zatm_vcc {
+ /*-------------------------------- RX part */
+ int rx_chan; /* RX channel, 0 if none */
+ int pool; /* free buffer pool */
+ /*-------------------------------- TX part */
+ int tx_chan; /* TX channel, 0 if none */
+ int shaper; /* shaper, <0 if none */
+ struct sk_buff_head tx_queue; /* list of buffers in transit */
+ wait_queue_head_t tx_wait; /* for close */
+ u32 *ring; /* transmit ring */
+ int ring_curr; /* current write position */
+ int txing; /* number of transmits in progress */
+ struct sk_buff_head backlog; /* list of buffers waiting for ring */
+};
+
+struct zatm_dev {
+ /*-------------------------------- TX part */
+ int tx_bw; /* remaining bandwidth */
+ u32 free_shapers; /* bit set */
+ int ubr; /* UBR shaper; -1 if none */
+ int ubr_ref_cnt; /* number of VCs using UBR shaper */
+ /*-------------------------------- RX part */
+ int pool_ref[NR_POOLS]; /* free buffer pool usage counters */
+ volatile struct sk_buff *last_free[NR_POOLS];
+ /* last entry in respective pool */
+ struct sk_buff_head pool[NR_POOLS];/* free buffer pools */
+ struct zatm_pool_info pool_info[NR_POOLS]; /* pool information */
+ /*-------------------------------- maps */
+ struct atm_vcc **tx_map; /* TX VCCs */
+ struct atm_vcc **rx_map; /* RX VCCs */
+ int chans; /* map size, must be 2^n */
+ /*-------------------------------- mailboxes */
+ unsigned long mbx_start[NR_MBX];/* start addresses */
+ dma_addr_t mbx_dma[NR_MBX];
+ u16 mbx_end[NR_MBX]; /* end offset (in bytes) */
+ /*-------------------------------- other pointers */
+ u32 pool_base; /* Free buffer pool dsc (word addr) */
+ /*-------------------------------- ZATM links */
+ struct atm_dev *more; /* other ZATM devices */
+ /*-------------------------------- general information */
+ int mem; /* RAM on board (in bytes) */
+ int khz; /* timer clock */
+ int copper; /* PHY type */
+ unsigned char irq; /* IRQ */
+ unsigned int base; /* IO base address */
+ struct pci_dev *pci_dev; /* PCI stuff */
+ spinlock_t lock;
+};
+
+
+#define ZATM_DEV(d) ((struct zatm_dev *) (d)->dev_data)
+#define ZATM_VCC(d) ((struct zatm_vcc *) (d)->dev_data)
+
+
+struct zatm_skb_prv {
+ struct atm_skb_data _; /* reserved */
+ u32 *dsc; /* pointer to skb's descriptor */
+};
+
+#define ZATM_PRV_DSC(skb) (((struct zatm_skb_prv *) (skb)->cb)->dsc)
+
+#endif
diff --git a/drivers/atm/zeprom.h b/drivers/atm/zeprom.h
new file mode 100644
index 000000000..019bb8249
--- /dev/null
+++ b/drivers/atm/zeprom.h
@@ -0,0 +1,34 @@
+/* drivers/atm/zeprom.h - ZeitNet ZN122x EEPROM (NM93C46) declarations */
+
+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
+
+
+#ifndef DRIVER_ATM_ZEPROM_H
+#define DRIVER_ATM_ZEPROM_H
+
+/* Different versions use different control registers */
+
+#define ZEPROM_V1_REG PCI_VENDOR_ID /* PCI register */
+#define ZEPROM_V2_REG 0x40
+
+/* Bits in contol register */
+
+#define ZEPROM_SK 0x80000000 /* strobe (probably on raising edge) */
+#define ZEPROM_CS 0x40000000 /* Chip Select */
+#define ZEPROM_DI 0x20000000 /* Data Input */
+#define ZEPROM_DO 0x10000000 /* Data Output */
+
+#define ZEPROM_SIZE 32 /* 32 bytes */
+#define ZEPROM_V1_ESI_OFF 24 /* ESI offset in EEPROM (V1) */
+#define ZEPROM_V2_ESI_OFF 4 /* ESI offset in EEPROM (V2) */
+
+#define ZEPROM_CMD_LEN 3 /* commands are three bits */
+#define ZEPROM_ADDR_LEN 6 /* addresses are six bits */
+
+/* Commands (3 bits) */
+
+#define ZEPROM_CMD_READ 6
+
+/* No other commands are needed. */
+
+#endif