aboutsummaryrefslogtreecommitdiff
path: root/drivers/atm/he.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/atm/he.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/atm/he.c')
-rw-r--r--drivers/atm/he.c3091
1 files changed, 3091 insertions, 0 deletions
diff --git a/drivers/atm/he.c b/drivers/atm/he.c
new file mode 100644
index 00000000000..c2c31a5f451
--- /dev/null
+++ b/drivers/atm/he.c
@@ -0,0 +1,3091 @@
+/* $Id: he.c,v 1.18 2003/05/06 22:57:15 chas Exp $ */
+
+/*
+
+ 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/config.h>
+#include <linux/module.h>
+#include <linux/version.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 <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#include <linux/atmdev.h>
+#include <linux/atm.h>
+#include <linux/sonet.h>
+
+#define USE_TASKLET
+#undef USE_SCATTERGATHER
+#undef USE_CHECKSUM_HW /* still confused about this */
+#define USE_RBPS
+#undef USE_RBPS_POOL /* if memory is tight try this */
+#undef USE_RBPL_POOL /* if memory is tight try this */
+#define USE_TPD_POOL
+/* #undef CONFIG_ATM_HE_USE_SUNI */
+/* #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 */
+
+/* version definition */
+
+static char *version = "$Id: he.c,v 1.18 2003/05/06 22:57:15 chas Exp $";
+
+/* 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, struct pt_regs *regs);
+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 int disable64;
+static short nvpibits = -1;
+static short nvcibits = -1;
+static short rx_skb_reserve = 16;
+static int irq_coalesce = 1;
+static int 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 hlist_node *node;
+ 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, node, 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 __devinit
+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 "he: %s\n", version);
+
+ if (pci_enable_device(pci_dev))
+ return -EIO;
+ if (pci_set_dma_mask(pci_dev, HE_DMA_MASK) != 0) {
+ printk(KERN_WARNING "he: no suitable dma available\n");
+ err = -EIO;
+ goto init_one_failure;
+ }
+
+ atm_dev = atm_dev_register(DEV_LABEL, &he_ops, -1, NULL);
+ if (!atm_dev) {
+ err = -ENODEV;
+ goto init_one_failure;
+ }
+ pci_set_drvdata(pci_dev, atm_dev);
+
+ he_dev = (struct he_dev *) kmalloc(sizeof(struct he_dev),
+ GFP_KERNEL);
+ if (!he_dev) {
+ err = -ENOMEM;
+ goto init_one_failure;
+ }
+ memset(he_dev, 0, sizeof(struct he_dev));
+
+ 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;
+ 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);
+ if (he_dev)
+ kfree(he_dev);
+ pci_disable_device(pci_dev);
+ return err;
+}
+
+static void __devexit
+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_set_drvdata(pci_dev, NULL);
+ 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 __init
+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 __init
+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 __init
+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 __init
+he_init_tpdrq(struct he_dev *he_dev)
+{
+ he_dev->tpdrq_base = pci_alloc_consistent(he_dev->pci_dev,
+ CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq), &he_dev->tpdrq_phys);
+ if (he_dev->tpdrq_base == NULL) {
+ hprintk("failed to alloc tpdrq\n");
+ return -ENOMEM;
+ }
+ memset(he_dev->tpdrq_base, 0,
+ CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq));
+
+ 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 __init
+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 __init
+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 __init
+he_init_group(struct he_dev *he_dev, int group)
+{
+ int i;
+
+#ifdef USE_RBPS
+ /* small buffer pool */
+#ifdef USE_RBPS_POOL
+ he_dev->rbps_pool = pci_pool_create("rbps", he_dev->pci_dev,
+ CONFIG_RBPS_BUFSIZE, 8, 0);
+ if (he_dev->rbps_pool == NULL) {
+ hprintk("unable to create rbps pages\n");
+ return -ENOMEM;
+ }
+#else /* !USE_RBPS_POOL */
+ he_dev->rbps_pages = pci_alloc_consistent(he_dev->pci_dev,
+ CONFIG_RBPS_SIZE * CONFIG_RBPS_BUFSIZE, &he_dev->rbps_pages_phys);
+ if (he_dev->rbps_pages == NULL) {
+ hprintk("unable to create rbps page pool\n");
+ return -ENOMEM;
+ }
+#endif /* USE_RBPS_POOL */
+
+ he_dev->rbps_base = pci_alloc_consistent(he_dev->pci_dev,
+ CONFIG_RBPS_SIZE * sizeof(struct he_rbp), &he_dev->rbps_phys);
+ if (he_dev->rbps_base == NULL) {
+ hprintk("failed to alloc rbps\n");
+ return -ENOMEM;
+ }
+ memset(he_dev->rbps_base, 0, CONFIG_RBPS_SIZE * sizeof(struct he_rbp));
+ he_dev->rbps_virt = kmalloc(CONFIG_RBPS_SIZE * sizeof(struct he_virt), GFP_KERNEL);
+
+ for (i = 0; i < CONFIG_RBPS_SIZE; ++i) {
+ dma_addr_t dma_handle;
+ void *cpuaddr;
+
+#ifdef USE_RBPS_POOL
+ cpuaddr = pci_pool_alloc(he_dev->rbps_pool, SLAB_KERNEL|SLAB_DMA, &dma_handle);
+ if (cpuaddr == NULL)
+ return -ENOMEM;
+#else
+ cpuaddr = he_dev->rbps_pages + (i * CONFIG_RBPS_BUFSIZE);
+ dma_handle = he_dev->rbps_pages_phys + (i * CONFIG_RBPS_BUFSIZE);
+#endif
+
+ he_dev->rbps_virt[i].virt = cpuaddr;
+ he_dev->rbps_base[i].status = RBP_LOANED | RBP_SMALLBUF | (i << RBP_INDEX_OFF);
+ he_dev->rbps_base[i].phys = dma_handle;
+
+ }
+ he_dev->rbps_tail = &he_dev->rbps_base[CONFIG_RBPS_SIZE - 1];
+
+ he_writel(he_dev, he_dev->rbps_phys, G0_RBPS_S + (group * 32));
+ he_writel(he_dev, RBPS_MASK(he_dev->rbps_tail),
+ G0_RBPS_T + (group * 32));
+ he_writel(he_dev, CONFIG_RBPS_BUFSIZE/4,
+ G0_RBPS_BS + (group * 32));
+ he_writel(he_dev,
+ RBP_THRESH(CONFIG_RBPS_THRESH) |
+ RBP_QSIZE(CONFIG_RBPS_SIZE - 1) |
+ RBP_INT_ENB,
+ G0_RBPS_QI + (group * 32));
+#else /* !USE_RBPS */
+ 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));
+#endif /* USE_RBPS */
+
+ /* large buffer pool */
+#ifdef USE_RBPL_POOL
+ he_dev->rbpl_pool = pci_pool_create("rbpl", he_dev->pci_dev,
+ CONFIG_RBPL_BUFSIZE, 8, 0);
+ if (he_dev->rbpl_pool == NULL) {
+ hprintk("unable to create rbpl pool\n");
+ return -ENOMEM;
+ }
+#else /* !USE_RBPL_POOL */
+ he_dev->rbpl_pages = (void *) pci_alloc_consistent(he_dev->pci_dev,
+ CONFIG_RBPL_SIZE * CONFIG_RBPL_BUFSIZE, &he_dev->rbpl_pages_phys);
+ if (he_dev->rbpl_pages == NULL) {
+ hprintk("unable to create rbpl pages\n");
+ return -ENOMEM;
+ }
+#endif /* USE_RBPL_POOL */
+
+ he_dev->rbpl_base = pci_alloc_consistent(he_dev->pci_dev,
+ CONFIG_RBPL_SIZE * sizeof(struct he_rbp), &he_dev->rbpl_phys);
+ if (he_dev->rbpl_base == NULL) {
+ hprintk("failed to alloc rbpl\n");
+ return -ENOMEM;
+ }
+ memset(he_dev->rbpl_base, 0, CONFIG_RBPL_SIZE * sizeof(struct he_rbp));
+ he_dev->rbpl_virt = kmalloc(CONFIG_RBPL_SIZE * sizeof(struct he_virt), GFP_KERNEL);
+
+ for (i = 0; i < CONFIG_RBPL_SIZE; ++i) {
+ dma_addr_t dma_handle;
+ void *cpuaddr;
+
+#ifdef USE_RBPL_POOL
+ cpuaddr = pci_pool_alloc(he_dev->rbpl_pool, SLAB_KERNEL|SLAB_DMA, &dma_handle);
+ if (cpuaddr == NULL)
+ return -ENOMEM;
+#else
+ cpuaddr = he_dev->rbpl_pages + (i * CONFIG_RBPL_BUFSIZE);
+ dma_handle = he_dev->rbpl_pages_phys + (i * CONFIG_RBPL_BUFSIZE);
+#endif
+
+ he_dev->rbpl_virt[i].virt = cpuaddr;
+ he_dev->rbpl_base[i].status = RBP_LOANED | (i << RBP_INDEX_OFF);
+ he_dev->rbpl_base[i].phys = dma_handle;
+ }
+ 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/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 = pci_alloc_consistent(he_dev->pci_dev,
+ CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), &he_dev->rbrq_phys);
+ if (he_dev->rbrq_base == NULL) {
+ hprintk("failed to allocate rbrq\n");
+ return -ENOMEM;
+ }
+ memset(he_dev->rbrq_base, 0, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq));
+
+ 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 = pci_alloc_consistent(he_dev->pci_dev,
+ CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), &he_dev->tbrq_phys);
+ if (he_dev->tbrq_base == NULL) {
+ hprintk("failed to allocate tbrq\n");
+ return -ENOMEM;
+ }
+ memset(he_dev->tbrq_base, 0, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq));
+
+ 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;
+}
+
+static int __init
+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 = pci_alloc_consistent(he_dev->pci_dev,
+ (CONFIG_IRQ_SIZE+1) * sizeof(struct he_irq), &he_dev->irq_phys);
+ 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, SA_INTERRUPT|SA_SHIRQ, 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 __init
+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);
+
+ udelay(16*1000); /* 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");