aboutsummaryrefslogtreecommitdiff
path: root/drivers/atm/lanai.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/lanai.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/lanai.c')
-rw-r--r--drivers/atm/lanai.c2770
1 files changed, 2770 insertions, 0 deletions
diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c
new file mode 100644
index 00000000000..ffe3afa723b
--- /dev/null
+++ b/drivers/atm/lanai.c
@@ -0,0 +1,2770 @@
+/* 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. (see lanai_ioctl)
+ *
+ * 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/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>
+#include <linux/dma-mapping.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_VENDOR_ID_EF_ATM_LANAI2,
+ lanaihb = PCI_VENDOR_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;
+ u8 pci_revision;
+/* 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 = find_first_bit(lp, NUM_VCI);
+ while (vci < NUM_VCI) {
+ func(lanai, vci);
+ vci = find_next_bit(lp, NUM_VCI, vci + 1);
+ }
+}
+
+/* -------------------- 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 = pci_alloc_consistent(pci, size, &buf->dmaaddr);
+ 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) {
+ pci_free_consistent(pci, 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 __init 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 __devinit 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 __devinit 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;
+}
+
+/* How many bytes can we send if we have "space" space, assuming we have
+ * to send full cells
+ */
+static inline int aal5_spacefor(int space)
+{
+ int cells = space / 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 __devinit 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 __devinit eeprom_validate(struct lanai_dev *lanai)
+{
+ lanai->serialno = 0;
+ lanai->magicno = EEPROM_MAGIC_VALUE;
+ return 0;
+}
+
+#else /* READ_EEPROM */
+
+static int __devinit 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((u32 *) (&lanai->eeprom[address]));
+}
+
+/* Checksum/validate EEPROM contents */
+static int __devinit 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 = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ e[EEPROM_MAC + 0], e[EEPROM_MAC + 1], e[EEPROM_MAC + 2],
+ e[EEPROM_MAC + 3], e[EEPROM_MAC + 4], e[EEPROM_MAC + 5]);
+ /* 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 -