162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr <mitch@sfgoth.com>
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This driver supports ATM cards based on the Efficient "Lanai"
562306a36Sopenharmony_ci * chipset such as the Speedstream 3010 and the ENI-25p.  The
662306a36Sopenharmony_ci * Speedstream 3060 is currently not supported since we don't
762306a36Sopenharmony_ci * have the code to drive the on-board Alcatel DSL chipset (yet).
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Thanks to Efficient for supporting this project with hardware,
1062306a36Sopenharmony_ci * documentation, and by answering my questions.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Things not working yet:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * o  We don't support the Speedstream 3060 yet - this card has
1562306a36Sopenharmony_ci *    an on-board DSL modem chip by Alcatel and the driver will
1662306a36Sopenharmony_ci *    need some extra code added to handle it
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * o  Note that due to limitations of the Lanai only one VCC can be
1962306a36Sopenharmony_ci *    in CBR at once
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * o We don't currently parse the EEPROM at all.  The code is all
2262306a36Sopenharmony_ci *   there as per the spec, but it doesn't actually work.  I think
2362306a36Sopenharmony_ci *   there may be some issues with the docs.  Anyway, do NOT
2462306a36Sopenharmony_ci *   enable it yet - bugs in that code may actually damage your
2562306a36Sopenharmony_ci *   hardware!  Because of this you should hardware an ESI before
2662306a36Sopenharmony_ci *   trying to use this in a LANE or MPOA environment.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * o  AAL0 is stubbed in but the actual rx/tx path isn't written yet:
2962306a36Sopenharmony_ci *	vcc_tx_aal0() needs to send or queue a SKB
3062306a36Sopenharmony_ci *	vcc_tx_unqueue_aal0() needs to attempt to send queued SKBs
3162306a36Sopenharmony_ci *	vcc_rx_aal0() needs to handle AAL0 interrupts
3262306a36Sopenharmony_ci *    This isn't too much work - I just wanted to get other things
3362306a36Sopenharmony_ci *    done first.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * o  lanai_change_qos() isn't written yet
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * o  There aren't any ioctl's yet -- I'd like to eventually support
3862306a36Sopenharmony_ci *    setting loopback and LED modes that way.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * o  If the segmentation engine or DMA gets shut down we should restart
4162306a36Sopenharmony_ci *    card as per section 17.0i.  (see lanai_reset)
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * o setsockopt(SO_CIRANGE) isn't done (although despite what the
4462306a36Sopenharmony_ci *   API says it isn't exactly commonly implemented)
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Version history:
4862306a36Sopenharmony_ci *   v.1.00 -- 26-JUL-2003 -- PCI/DMA updates
4962306a36Sopenharmony_ci *   v.0.02 -- 11-JAN-2000 -- Endian fixes
5062306a36Sopenharmony_ci *   v.0.01 -- 30-NOV-1999 -- Initial release
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#include <linux/module.h>
5462306a36Sopenharmony_ci#include <linux/slab.h>
5562306a36Sopenharmony_ci#include <linux/mm.h>
5662306a36Sopenharmony_ci#include <linux/atmdev.h>
5762306a36Sopenharmony_ci#include <asm/io.h>
5862306a36Sopenharmony_ci#include <asm/byteorder.h>
5962306a36Sopenharmony_ci#include <linux/spinlock.h>
6062306a36Sopenharmony_ci#include <linux/pci.h>
6162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
6262306a36Sopenharmony_ci#include <linux/init.h>
6362306a36Sopenharmony_ci#include <linux/delay.h>
6462306a36Sopenharmony_ci#include <linux/interrupt.h>
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* -------------------- TUNABLE PARAMATERS: */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * Maximum number of VCIs per card.  Setting it lower could theoretically
7062306a36Sopenharmony_ci * save some memory, but since we allocate our vcc list with get_free_pages,
7162306a36Sopenharmony_ci * it's not really likely for most architectures
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ci#define NUM_VCI			(1024)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * Enable extra debugging
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_ci#define DEBUG
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * Debug _all_ register operations with card, except the memory test.
8162306a36Sopenharmony_ci * Also disables the timed poll to prevent extra chattiness.  This
8262306a36Sopenharmony_ci * isn't for normal use
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_ci#undef DEBUG_RW
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/*
8762306a36Sopenharmony_ci * The programming guide specifies a full test of the on-board SRAM
8862306a36Sopenharmony_ci * at initialization time.  Undefine to remove this
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_ci#define FULL_MEMORY_TEST
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/*
9362306a36Sopenharmony_ci * This is the number of (4 byte) service entries that we will
9462306a36Sopenharmony_ci * try to allocate at startup.  Note that we will end up with
9562306a36Sopenharmony_ci * one PAGE_SIZE's worth regardless of what this is set to
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ci#define SERVICE_ENTRIES		(1024)
9862306a36Sopenharmony_ci/* TODO: make above a module load-time option */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * We normally read the onboard EEPROM in order to discover our MAC
10262306a36Sopenharmony_ci * address.  Undefine to _not_ do this
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_ci/* #define READ_EEPROM */ /* ***DONT ENABLE YET*** */
10562306a36Sopenharmony_ci/* TODO: make above a module load-time option (also) */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * Depth of TX fifo (in 128 byte units; range 2-31)
10962306a36Sopenharmony_ci * Smaller numbers are better for network latency
11062306a36Sopenharmony_ci * Larger numbers are better for PCI latency
11162306a36Sopenharmony_ci * I'm really sure where the best tradeoff is, but the BSD driver uses
11262306a36Sopenharmony_ci * 7 and it seems to work ok.
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_ci#define TX_FIFO_DEPTH		(7)
11562306a36Sopenharmony_ci/* TODO: make above a module load-time option */
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci * How often (in jiffies) we will try to unstick stuck connections -
11962306a36Sopenharmony_ci * shouldn't need to happen much
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ci#define LANAI_POLL_PERIOD	(10*HZ)
12262306a36Sopenharmony_ci/* TODO: make above a module load-time option */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * When allocating an AAL5 receiving buffer, try to make it at least
12662306a36Sopenharmony_ci * large enough to hold this many max_sdu sized PDUs
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_ci#define AAL5_RX_MULTIPLIER	(3)
12962306a36Sopenharmony_ci/* TODO: make above a module load-time option */
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * Same for transmitting buffer
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_ci#define AAL5_TX_MULTIPLIER	(3)
13562306a36Sopenharmony_ci/* TODO: make above a module load-time option */
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/*
13862306a36Sopenharmony_ci * When allocating an AAL0 transmiting buffer, how many cells should fit.
13962306a36Sopenharmony_ci * Remember we'll end up with a PAGE_SIZE of them anyway, so this isn't
14062306a36Sopenharmony_ci * really critical
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_ci#define AAL0_TX_MULTIPLIER	(40)
14362306a36Sopenharmony_ci/* TODO: make above a module load-time option */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * How large should we make the AAL0 receiving buffer.  Remember that this
14762306a36Sopenharmony_ci * is shared between all AAL0 VC's
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_ci#define AAL0_RX_BUFFER_SIZE	(PAGE_SIZE)
15062306a36Sopenharmony_ci/* TODO: make above a module load-time option */
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/*
15362306a36Sopenharmony_ci * Should we use Lanai's "powerdown" feature when no vcc's are bound?
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_ci/* #define USE_POWERDOWN */
15662306a36Sopenharmony_ci/* TODO: make above a module load-time option (also) */
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/* -------------------- DEBUGGING AIDS: */
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define DEV_LABEL "lanai"
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#ifdef DEBUG
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci#define DPRINTK(format, args...) \
16562306a36Sopenharmony_ci	printk(KERN_DEBUG DEV_LABEL ": " format, ##args)
16662306a36Sopenharmony_ci#define APRINTK(truth, format, args...) \
16762306a36Sopenharmony_ci	do { \
16862306a36Sopenharmony_ci		if (unlikely(!(truth))) \
16962306a36Sopenharmony_ci			printk(KERN_ERR DEV_LABEL ": " format, ##args); \
17062306a36Sopenharmony_ci	} while (0)
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci#else /* !DEBUG */
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci#define DPRINTK(format, args...)
17562306a36Sopenharmony_ci#define APRINTK(truth, format, args...)
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci#endif /* DEBUG */
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci#ifdef DEBUG_RW
18062306a36Sopenharmony_ci#define RWDEBUG(format, args...) \
18162306a36Sopenharmony_ci	printk(KERN_DEBUG DEV_LABEL ": " format, ##args)
18262306a36Sopenharmony_ci#else /* !DEBUG_RW */
18362306a36Sopenharmony_ci#define RWDEBUG(format, args...)
18462306a36Sopenharmony_ci#endif
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/* -------------------- DATA DEFINITIONS: */
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci#define LANAI_MAPPING_SIZE	(0x40000)
18962306a36Sopenharmony_ci#define LANAI_EEPROM_SIZE	(128)
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_citypedef int vci_t;
19262306a36Sopenharmony_citypedef void __iomem *bus_addr_t;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/* DMA buffer in host memory for TX, RX, or service list. */
19562306a36Sopenharmony_cistruct lanai_buffer {
19662306a36Sopenharmony_ci	u32 *start;	/* From get_free_pages */
19762306a36Sopenharmony_ci	u32 *end;	/* One past last byte */
19862306a36Sopenharmony_ci	u32 *ptr;	/* Pointer to current host location */
19962306a36Sopenharmony_ci	dma_addr_t dmaaddr;
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistruct lanai_vcc_stats {
20362306a36Sopenharmony_ci	unsigned rx_nomem;
20462306a36Sopenharmony_ci	union {
20562306a36Sopenharmony_ci		struct {
20662306a36Sopenharmony_ci			unsigned rx_badlen;
20762306a36Sopenharmony_ci			unsigned service_trash;
20862306a36Sopenharmony_ci			unsigned service_stream;
20962306a36Sopenharmony_ci			unsigned service_rxcrc;
21062306a36Sopenharmony_ci		} aal5;
21162306a36Sopenharmony_ci		struct {
21262306a36Sopenharmony_ci		} aal0;
21362306a36Sopenharmony_ci	} x;
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistruct lanai_dev;			/* Forward declaration */
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/*
21962306a36Sopenharmony_ci * This is the card-specific per-vcc data.  Note that unlike some other
22062306a36Sopenharmony_ci * drivers there is NOT a 1-to-1 correspondance between these and
22162306a36Sopenharmony_ci * atm_vcc's - each one of these represents an actual 2-way vcc, but
22262306a36Sopenharmony_ci * an atm_vcc can be 1-way and share with a 1-way vcc in the other
22362306a36Sopenharmony_ci * direction.  To make it weirder, there can even be 0-way vccs
22462306a36Sopenharmony_ci * bound to us, waiting to do a change_qos
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_cistruct lanai_vcc {
22762306a36Sopenharmony_ci	bus_addr_t vbase;		/* Base of VCC's registers */
22862306a36Sopenharmony_ci	struct lanai_vcc_stats stats;
22962306a36Sopenharmony_ci	int nref;			/* # of atm_vcc's who reference us */
23062306a36Sopenharmony_ci	vci_t vci;
23162306a36Sopenharmony_ci	struct {
23262306a36Sopenharmony_ci		struct lanai_buffer buf;
23362306a36Sopenharmony_ci		struct atm_vcc *atmvcc;	/* atm_vcc who is receiver */
23462306a36Sopenharmony_ci	} rx;
23562306a36Sopenharmony_ci	struct {
23662306a36Sopenharmony_ci		struct lanai_buffer buf;
23762306a36Sopenharmony_ci		struct atm_vcc *atmvcc;	/* atm_vcc who is transmitter */
23862306a36Sopenharmony_ci		int endptr;		/* last endptr from service entry */
23962306a36Sopenharmony_ci		struct sk_buff_head backlog;
24062306a36Sopenharmony_ci		void (*unqueue)(struct lanai_dev *, struct lanai_vcc *, int);
24162306a36Sopenharmony_ci	} tx;
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cienum lanai_type {
24562306a36Sopenharmony_ci	lanai2	= PCI_DEVICE_ID_EF_ATM_LANAI2,
24662306a36Sopenharmony_ci	lanaihb	= PCI_DEVICE_ID_EF_ATM_LANAIHB
24762306a36Sopenharmony_ci};
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistruct lanai_dev_stats {
25062306a36Sopenharmony_ci	unsigned ovfl_trash;	/* # of cells dropped - buffer overflow */
25162306a36Sopenharmony_ci	unsigned vci_trash;	/* # of cells dropped - closed vci */
25262306a36Sopenharmony_ci	unsigned hec_err;	/* # of cells dropped - bad HEC */
25362306a36Sopenharmony_ci	unsigned atm_ovfl;	/* # of cells dropped - rx fifo overflow */
25462306a36Sopenharmony_ci	unsigned pcierr_parity_detect;
25562306a36Sopenharmony_ci	unsigned pcierr_serr_set;
25662306a36Sopenharmony_ci	unsigned pcierr_master_abort;
25762306a36Sopenharmony_ci	unsigned pcierr_m_target_abort;
25862306a36Sopenharmony_ci	unsigned pcierr_s_target_abort;
25962306a36Sopenharmony_ci	unsigned pcierr_master_parity;
26062306a36Sopenharmony_ci	unsigned service_notx;
26162306a36Sopenharmony_ci	unsigned service_norx;
26262306a36Sopenharmony_ci	unsigned service_rxnotaal5;
26362306a36Sopenharmony_ci	unsigned dma_reenable;
26462306a36Sopenharmony_ci	unsigned card_reset;
26562306a36Sopenharmony_ci};
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistruct lanai_dev {
26862306a36Sopenharmony_ci	bus_addr_t base;
26962306a36Sopenharmony_ci	struct lanai_dev_stats stats;
27062306a36Sopenharmony_ci	struct lanai_buffer service;
27162306a36Sopenharmony_ci	struct lanai_vcc **vccs;
27262306a36Sopenharmony_ci#ifdef USE_POWERDOWN
27362306a36Sopenharmony_ci	int nbound;			/* number of bound vccs */
27462306a36Sopenharmony_ci#endif
27562306a36Sopenharmony_ci	enum lanai_type type;
27662306a36Sopenharmony_ci	vci_t num_vci;			/* Currently just NUM_VCI */
27762306a36Sopenharmony_ci	u8 eeprom[LANAI_EEPROM_SIZE];
27862306a36Sopenharmony_ci	u32 serialno, magicno;
27962306a36Sopenharmony_ci	struct pci_dev *pci;
28062306a36Sopenharmony_ci	DECLARE_BITMAP(backlog_vccs, NUM_VCI);   /* VCCs with tx backlog */
28162306a36Sopenharmony_ci	DECLARE_BITMAP(transmit_ready, NUM_VCI); /* VCCs with transmit space */
28262306a36Sopenharmony_ci	struct timer_list timer;
28362306a36Sopenharmony_ci	int naal0;
28462306a36Sopenharmony_ci	struct lanai_buffer aal0buf;	/* AAL0 RX buffers */
28562306a36Sopenharmony_ci	u32 conf1, conf2;		/* CONFIG[12] registers */
28662306a36Sopenharmony_ci	u32 status;			/* STATUS register */
28762306a36Sopenharmony_ci	spinlock_t endtxlock;
28862306a36Sopenharmony_ci	spinlock_t servicelock;
28962306a36Sopenharmony_ci	struct atm_vcc *cbrvcc;
29062306a36Sopenharmony_ci	int number;
29162306a36Sopenharmony_ci	int board_rev;
29262306a36Sopenharmony_ci/* TODO - look at race conditions with maintence of conf1/conf2 */
29362306a36Sopenharmony_ci/* TODO - transmit locking: should we use _irq not _irqsave? */
29462306a36Sopenharmony_ci/* TODO - organize above in some rational fashion (see <asm/cache.h>) */
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/*
29862306a36Sopenharmony_ci * Each device has two bitmaps for each VCC (baclog_vccs and transmit_ready)
29962306a36Sopenharmony_ci * This function iterates one of these, calling a given function for each
30062306a36Sopenharmony_ci * vci with their bit set
30162306a36Sopenharmony_ci */
30262306a36Sopenharmony_cistatic void vci_bitfield_iterate(struct lanai_dev *lanai,
30362306a36Sopenharmony_ci	const unsigned long *lp,
30462306a36Sopenharmony_ci	void (*func)(struct lanai_dev *,vci_t vci))
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	vci_t vci;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	for_each_set_bit(vci, lp, NUM_VCI)
30962306a36Sopenharmony_ci		func(lanai, vci);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/* -------------------- BUFFER  UTILITIES: */
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/*
31562306a36Sopenharmony_ci * Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes -
31662306a36Sopenharmony_ci * usually any page allocation will do.  Just to be safe in case
31762306a36Sopenharmony_ci * PAGE_SIZE is insanely tiny, though...
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_ci#define LANAI_PAGE_SIZE   ((PAGE_SIZE >= 1024) ? PAGE_SIZE : 1024)
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/*
32262306a36Sopenharmony_ci * Allocate a buffer in host RAM for service list, RX, or TX
32362306a36Sopenharmony_ci * Returns buf->start==NULL if no memory
32462306a36Sopenharmony_ci * Note that the size will be rounded up 2^n bytes, and
32562306a36Sopenharmony_ci * if we can't allocate that we'll settle for something smaller
32662306a36Sopenharmony_ci * until minbytes
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_cistatic void lanai_buf_allocate(struct lanai_buffer *buf,
32962306a36Sopenharmony_ci	size_t bytes, size_t minbytes, struct pci_dev *pci)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	int size;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (bytes > (128 * 1024))	/* max lanai buffer size */
33462306a36Sopenharmony_ci		bytes = 128 * 1024;
33562306a36Sopenharmony_ci	for (size = LANAI_PAGE_SIZE; size < bytes; size *= 2)
33662306a36Sopenharmony_ci		;
33762306a36Sopenharmony_ci	if (minbytes < LANAI_PAGE_SIZE)
33862306a36Sopenharmony_ci		minbytes = LANAI_PAGE_SIZE;
33962306a36Sopenharmony_ci	do {
34062306a36Sopenharmony_ci		/*
34162306a36Sopenharmony_ci		 * Technically we could use non-consistent mappings for
34262306a36Sopenharmony_ci		 * everything, but the way the lanai uses DMA memory would
34362306a36Sopenharmony_ci		 * make that a terrific pain.  This is much simpler.
34462306a36Sopenharmony_ci		 */
34562306a36Sopenharmony_ci		buf->start = dma_alloc_coherent(&pci->dev,
34662306a36Sopenharmony_ci						size, &buf->dmaaddr, GFP_KERNEL);
34762306a36Sopenharmony_ci		if (buf->start != NULL) {	/* Success */
34862306a36Sopenharmony_ci			/* Lanai requires 256-byte alignment of DMA bufs */
34962306a36Sopenharmony_ci			APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0,
35062306a36Sopenharmony_ci			    "bad dmaaddr: 0x%lx\n",
35162306a36Sopenharmony_ci			    (unsigned long) buf->dmaaddr);
35262306a36Sopenharmony_ci			buf->ptr = buf->start;
35362306a36Sopenharmony_ci			buf->end = (u32 *)
35462306a36Sopenharmony_ci			    (&((unsigned char *) buf->start)[size]);
35562306a36Sopenharmony_ci			memset(buf->start, 0, size);
35662306a36Sopenharmony_ci			break;
35762306a36Sopenharmony_ci		}
35862306a36Sopenharmony_ci		size /= 2;
35962306a36Sopenharmony_ci	} while (size >= minbytes);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/* size of buffer in bytes */
36362306a36Sopenharmony_cistatic inline size_t lanai_buf_size(const struct lanai_buffer *buf)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	return ((unsigned long) buf->end) - ((unsigned long) buf->start);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic void lanai_buf_deallocate(struct lanai_buffer *buf,
36962306a36Sopenharmony_ci	struct pci_dev *pci)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	if (buf->start != NULL) {
37262306a36Sopenharmony_ci		dma_free_coherent(&pci->dev, lanai_buf_size(buf),
37362306a36Sopenharmony_ci				  buf->start, buf->dmaaddr);
37462306a36Sopenharmony_ci		buf->start = buf->end = buf->ptr = NULL;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/* size of buffer as "card order" (0=1k .. 7=128k) */
37962306a36Sopenharmony_cistatic int lanai_buf_size_cardorder(const struct lanai_buffer *buf)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int order = get_order(lanai_buf_size(buf)) + (PAGE_SHIFT - 10);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* This can only happen if PAGE_SIZE is gigantic, but just in case */
38462306a36Sopenharmony_ci	if (order > 7)
38562306a36Sopenharmony_ci		order = 7;
38662306a36Sopenharmony_ci	return order;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci/* -------------------- PORT I/O UTILITIES: */
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/* Registers (and their bit-fields) */
39262306a36Sopenharmony_cienum lanai_register {
39362306a36Sopenharmony_ci	Reset_Reg		= 0x00,	/* Reset; read for chip type; bits: */
39462306a36Sopenharmony_ci#define   RESET_GET_BOARD_REV(x)    (((x)>> 0)&0x03)	/* Board revision */
39562306a36Sopenharmony_ci#define   RESET_GET_BOARD_ID(x)	    (((x)>> 2)&0x03)	/* Board ID */
39662306a36Sopenharmony_ci#define     BOARD_ID_LANAI256		(0)	/* 25.6M adapter card */
39762306a36Sopenharmony_ci	Endian_Reg		= 0x04,	/* Endian setting */
39862306a36Sopenharmony_ci	IntStatus_Reg		= 0x08,	/* Interrupt status */
39962306a36Sopenharmony_ci	IntStatusMasked_Reg	= 0x0C,	/* Interrupt status (masked) */
40062306a36Sopenharmony_ci	IntAck_Reg		= 0x10,	/* Interrupt acknowledge */
40162306a36Sopenharmony_ci	IntAckMasked_Reg	= 0x14,	/* Interrupt acknowledge (masked) */
40262306a36Sopenharmony_ci	IntStatusSet_Reg	= 0x18,	/* Get status + enable/disable */
40362306a36Sopenharmony_ci	IntStatusSetMasked_Reg	= 0x1C,	/* Get status + en/di (masked) */
40462306a36Sopenharmony_ci	IntControlEna_Reg	= 0x20,	/* Interrupt control enable */
40562306a36Sopenharmony_ci	IntControlDis_Reg	= 0x24,	/* Interrupt control disable */
40662306a36Sopenharmony_ci	Status_Reg		= 0x28,	/* Status */
40762306a36Sopenharmony_ci#define   STATUS_PROMDATA	 (0x00000001)	/* PROM_DATA pin */
40862306a36Sopenharmony_ci#define   STATUS_WAITING	 (0x00000002)	/* Interrupt being delayed */
40962306a36Sopenharmony_ci#define	  STATUS_SOOL		 (0x00000004)	/* SOOL alarm */
41062306a36Sopenharmony_ci#define   STATUS_LOCD		 (0x00000008)	/* LOCD alarm */
41162306a36Sopenharmony_ci#define	  STATUS_LED		 (0x00000010)	/* LED (HAPPI) output */
41262306a36Sopenharmony_ci#define   STATUS_GPIN		 (0x00000020)	/* GPIN pin */
41362306a36Sopenharmony_ci#define   STATUS_BUTTBUSY	 (0x00000040)	/* Butt register is pending */
41462306a36Sopenharmony_ci	Config1_Reg		= 0x2C,	/* Config word 1; bits: */
41562306a36Sopenharmony_ci#define   CONFIG1_PROMDATA	 (0x00000001)	/* PROM_DATA pin */
41662306a36Sopenharmony_ci#define   CONFIG1_PROMCLK	 (0x00000002)	/* PROM_CLK pin */
41762306a36Sopenharmony_ci#define   CONFIG1_SET_READMODE(x) ((x)*0x004)	/* PCI BM reads; values: */
41862306a36Sopenharmony_ci#define     READMODE_PLAIN	    (0)		/*   Plain memory read */
41962306a36Sopenharmony_ci#define     READMODE_LINE	    (2)		/*   Memory read line */
42062306a36Sopenharmony_ci#define     READMODE_MULTIPLE	    (3)		/*   Memory read multiple */
42162306a36Sopenharmony_ci#define   CONFIG1_DMA_ENABLE	 (0x00000010)	/* Turn on DMA */
42262306a36Sopenharmony_ci#define   CONFIG1_POWERDOWN	 (0x00000020)	/* Turn off clocks */
42362306a36Sopenharmony_ci#define   CONFIG1_SET_LOOPMODE(x) ((x)*0x080)	/* Clock&loop mode; values: */
42462306a36Sopenharmony_ci#define     LOOPMODE_NORMAL	    (0)		/*   Normal - no loop */
42562306a36Sopenharmony_ci#define     LOOPMODE_TIME	    (1)
42662306a36Sopenharmony_ci#define     LOOPMODE_DIAG	    (2)
42762306a36Sopenharmony_ci#define     LOOPMODE_LINE	    (3)
42862306a36Sopenharmony_ci#define   CONFIG1_MASK_LOOPMODE  (0x00000180)
42962306a36Sopenharmony_ci#define   CONFIG1_SET_LEDMODE(x) ((x)*0x0200)	/* Mode of LED; values: */
43062306a36Sopenharmony_ci#define     LEDMODE_NOT_SOOL	    (0)		/*   !SOOL */
43162306a36Sopenharmony_ci#define	    LEDMODE_OFF		    (1)		/*   0     */
43262306a36Sopenharmony_ci#define	    LEDMODE_ON		    (2)		/*   1     */
43362306a36Sopenharmony_ci#define	    LEDMODE_NOT_LOCD	    (3)		/*   !LOCD */
43462306a36Sopenharmony_ci#define	    LEDMORE_GPIN	    (4)		/*   GPIN  */
43562306a36Sopenharmony_ci#define     LEDMODE_NOT_GPIN	    (7)		/*   !GPIN */
43662306a36Sopenharmony_ci#define   CONFIG1_MASK_LEDMODE	 (0x00000E00)
43762306a36Sopenharmony_ci#define   CONFIG1_GPOUT1	 (0x00001000)	/* Toggle for reset */
43862306a36Sopenharmony_ci#define   CONFIG1_GPOUT2	 (0x00002000)	/* Loopback PHY */
43962306a36Sopenharmony_ci#define   CONFIG1_GPOUT3	 (0x00004000)	/* Loopback lanai */
44062306a36Sopenharmony_ci	Config2_Reg		= 0x30,	/* Config word 2; bits: */
44162306a36Sopenharmony_ci#define   CONFIG2_HOWMANY	 (0x00000001)	/* >512 VCIs? */
44262306a36Sopenharmony_ci#define   CONFIG2_PTI7_MODE	 (0x00000002)	/* Make PTI=7 RM, not OAM */
44362306a36Sopenharmony_ci#define   CONFIG2_VPI_CHK_DIS	 (0x00000004)	/* Ignore RX VPI value */
44462306a36Sopenharmony_ci#define   CONFIG2_HEC_DROP	 (0x00000008)	/* Drop cells w/ HEC errors */
44562306a36Sopenharmony_ci#define   CONFIG2_VCI0_NORMAL	 (0x00000010)	/* Treat VCI=0 normally */
44662306a36Sopenharmony_ci#define   CONFIG2_CBR_ENABLE	 (0x00000020)	/* Deal with CBR traffic */
44762306a36Sopenharmony_ci#define   CONFIG2_TRASH_ALL	 (0x00000040)	/* Trashing incoming cells */
44862306a36Sopenharmony_ci#define   CONFIG2_TX_DISABLE	 (0x00000080)	/* Trashing outgoing cells */
44962306a36Sopenharmony_ci#define   CONFIG2_SET_TRASH	 (0x00000100)	/* Turn trashing on */
45062306a36Sopenharmony_ci	Statistics_Reg		= 0x34,	/* Statistics; bits: */
45162306a36Sopenharmony_ci#define   STATS_GET_FIFO_OVFL(x)    (((x)>> 0)&0xFF)	/* FIFO overflowed */
45262306a36Sopenharmony_ci#define   STATS_GET_HEC_ERR(x)      (((x)>> 8)&0xFF)	/* HEC was bad */
45362306a36Sopenharmony_ci#define   STATS_GET_BAD_VCI(x)      (((x)>>16)&0xFF)	/* VCI not open */
45462306a36Sopenharmony_ci#define   STATS_GET_BUF_OVFL(x)     (((x)>>24)&0xFF)	/* VCC buffer full */
45562306a36Sopenharmony_ci	ServiceStuff_Reg	= 0x38,	/* Service stuff; bits: */
45662306a36Sopenharmony_ci#define   SSTUFF_SET_SIZE(x) ((x)*0x20000000)	/* size of service buffer */
45762306a36Sopenharmony_ci#define   SSTUFF_SET_ADDR(x)	    ((x)>>8)	/* set address of buffer */
45862306a36Sopenharmony_ci	ServWrite_Reg		= 0x3C,	/* ServWrite Pointer */
45962306a36Sopenharmony_ci	ServRead_Reg		= 0x40,	/* ServRead Pointer */
46062306a36Sopenharmony_ci	TxDepth_Reg		= 0x44,	/* FIFO Transmit Depth */
46162306a36Sopenharmony_ci	Butt_Reg		= 0x48,	/* Butt register */
46262306a36Sopenharmony_ci	CBR_ICG_Reg		= 0x50,
46362306a36Sopenharmony_ci	CBR_PTR_Reg		= 0x54,
46462306a36Sopenharmony_ci	PingCount_Reg		= 0x58,	/* Ping count */
46562306a36Sopenharmony_ci	DMA_Addr_Reg		= 0x5C	/* DMA address */
46662306a36Sopenharmony_ci};
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic inline bus_addr_t reg_addr(const struct lanai_dev *lanai,
46962306a36Sopenharmony_ci	enum lanai_register reg)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	return lanai->base + reg;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic inline u32 reg_read(const struct lanai_dev *lanai,
47562306a36Sopenharmony_ci	enum lanai_register reg)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	u32 t;
47862306a36Sopenharmony_ci	t = readl(reg_addr(lanai, reg));
47962306a36Sopenharmony_ci	RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base,
48062306a36Sopenharmony_ci	    (int) reg, t);
48162306a36Sopenharmony_ci	return t;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic inline void reg_write(const struct lanai_dev *lanai, u32 val,
48562306a36Sopenharmony_ci	enum lanai_register reg)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base,
48862306a36Sopenharmony_ci	    (int) reg, val);
48962306a36Sopenharmony_ci	writel(val, reg_addr(lanai, reg));
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic inline void conf1_write(const struct lanai_dev *lanai)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	reg_write(lanai, lanai->conf1, Config1_Reg);
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic inline void conf2_write(const struct lanai_dev *lanai)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	reg_write(lanai, lanai->conf2, Config2_Reg);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci/* Same as conf2_write(), but defers I/O if we're powered down */
50362306a36Sopenharmony_cistatic inline void conf2_write_if_powerup(const struct lanai_dev *lanai)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci#ifdef USE_POWERDOWN
50662306a36Sopenharmony_ci	if (unlikely((lanai->conf1 & CONFIG1_POWERDOWN) != 0))
50762306a36Sopenharmony_ci		return;
50862306a36Sopenharmony_ci#endif /* USE_POWERDOWN */
50962306a36Sopenharmony_ci	conf2_write(lanai);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic inline void reset_board(const struct lanai_dev *lanai)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	DPRINTK("about to reset board\n");
51562306a36Sopenharmony_ci	reg_write(lanai, 0, Reset_Reg);
51662306a36Sopenharmony_ci	/*
51762306a36Sopenharmony_ci	 * If we don't delay a little while here then we can end up
51862306a36Sopenharmony_ci	 * leaving the card in a VERY weird state and lock up the
51962306a36Sopenharmony_ci	 * PCI bus.  This isn't documented anywhere but I've convinced
52062306a36Sopenharmony_ci	 * myself after a lot of painful experimentation
52162306a36Sopenharmony_ci	 */
52262306a36Sopenharmony_ci	udelay(5);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci/* -------------------- CARD SRAM UTILITIES: */
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci/* The SRAM is mapped into normal PCI memory space - the only catch is
52862306a36Sopenharmony_ci * that it is only 16-bits wide but must be accessed as 32-bit.  The
52962306a36Sopenharmony_ci * 16 high bits will be zero.  We don't hide this, since they get
53062306a36Sopenharmony_ci * programmed mostly like discrete registers anyway
53162306a36Sopenharmony_ci */
53262306a36Sopenharmony_ci#define SRAM_START (0x20000)
53362306a36Sopenharmony_ci#define SRAM_BYTES (0x20000)	/* Again, half don't really exist */
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	return lanai->base + SRAM_START + offset;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic inline u32 sram_read(const struct lanai_dev *lanai, int offset)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	return readl(sram_addr(lanai, offset));
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic inline void sram_write(const struct lanai_dev *lanai,
54662306a36Sopenharmony_ci	u32 val, int offset)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	writel(val, sram_addr(lanai, offset));
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int sram_test_word(const struct lanai_dev *lanai, int offset,
55262306a36Sopenharmony_ci			  u32 pattern)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	u32 readback;
55562306a36Sopenharmony_ci	sram_write(lanai, pattern, offset);
55662306a36Sopenharmony_ci	readback = sram_read(lanai, offset);
55762306a36Sopenharmony_ci	if (likely(readback == pattern))
55862306a36Sopenharmony_ci		return 0;
55962306a36Sopenharmony_ci	printk(KERN_ERR DEV_LABEL
56062306a36Sopenharmony_ci	    "(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n",
56162306a36Sopenharmony_ci	    lanai->number, offset,
56262306a36Sopenharmony_ci	    (unsigned int) pattern, (unsigned int) readback);
56362306a36Sopenharmony_ci	return -EIO;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int sram_test_pass(const struct lanai_dev *lanai, u32 pattern)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	int offset, result = 0;
56962306a36Sopenharmony_ci	for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4)
57062306a36Sopenharmony_ci		result = sram_test_word(lanai, offset, pattern);
57162306a36Sopenharmony_ci	return result;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic int sram_test_and_clear(const struct lanai_dev *lanai)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci#ifdef FULL_MEMORY_TEST
57762306a36Sopenharmony_ci	int result;
57862306a36Sopenharmony_ci	DPRINTK("testing SRAM\n");
57962306a36Sopenharmony_ci	if ((result = sram_test_pass(lanai, 0x5555)) != 0)
58062306a36Sopenharmony_ci		return result;
58162306a36Sopenharmony_ci	if ((result = sram_test_pass(lanai, 0xAAAA)) != 0)
58262306a36Sopenharmony_ci		return result;
58362306a36Sopenharmony_ci#endif
58462306a36Sopenharmony_ci	DPRINTK("clearing SRAM\n");
58562306a36Sopenharmony_ci	return sram_test_pass(lanai, 0x0000);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/* -------------------- CARD-BASED VCC TABLE UTILITIES: */
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci/* vcc table */
59162306a36Sopenharmony_cienum lanai_vcc_offset {
59262306a36Sopenharmony_ci	vcc_rxaddr1		= 0x00,	/* Location1, plus bits: */
59362306a36Sopenharmony_ci#define   RXADDR1_SET_SIZE(x) ((x)*0x0000100)	/* size of RX buffer */
59462306a36Sopenharmony_ci#define   RXADDR1_SET_RMMODE(x) ((x)*0x00800)	/* RM cell action; values: */
59562306a36Sopenharmony_ci#define     RMMODE_TRASH	  (0)		/*   discard */
59662306a36Sopenharmony_ci#define     RMMODE_PRESERVE	  (1)		/*   input as AAL0 */
59762306a36Sopenharmony_ci#define     RMMODE_PIPE		  (2)		/*   pipe to coscheduler */
59862306a36Sopenharmony_ci#define     RMMODE_PIPEALL	  (3)		/*   pipe non-RM too */
59962306a36Sopenharmony_ci#define   RXADDR1_OAM_PRESERVE	 (0x00002000)	/* Input OAM cells as AAL0 */
60062306a36Sopenharmony_ci#define   RXADDR1_SET_MODE(x) ((x)*0x0004000)	/* Reassembly mode */
60162306a36Sopenharmony_ci#define     RXMODE_TRASH	  (0)		/*   discard */
60262306a36Sopenharmony_ci#define     RXMODE_AAL0		  (1)		/*   non-AAL5 mode */
60362306a36Sopenharmony_ci#define     RXMODE_AAL5		  (2)		/*   AAL5, intr. each PDU */
60462306a36Sopenharmony_ci#define     RXMODE_AAL5_STREAM	  (3)		/*   AAL5 w/o per-PDU intr */
60562306a36Sopenharmony_ci	vcc_rxaddr2		= 0x04,	/* Location2 */
60662306a36Sopenharmony_ci	vcc_rxcrc1		= 0x08,	/* RX CRC claculation space */
60762306a36Sopenharmony_ci	vcc_rxcrc2		= 0x0C,
60862306a36Sopenharmony_ci	vcc_rxwriteptr		= 0x10, /* RX writeptr, plus bits: */
60962306a36Sopenharmony_ci#define   RXWRITEPTR_LASTEFCI	 (0x00002000)	/* Last PDU had EFCI bit */
61062306a36Sopenharmony_ci#define   RXWRITEPTR_DROPPING	 (0x00004000)	/* Had error, dropping */
61162306a36Sopenharmony_ci#define   RXWRITEPTR_TRASHING	 (0x00008000)	/* Trashing */
61262306a36Sopenharmony_ci	vcc_rxbufstart		= 0x14,	/* RX bufstart, plus bits: */
61362306a36Sopenharmony_ci#define   RXBUFSTART_CLP	 (0x00004000)
61462306a36Sopenharmony_ci#define   RXBUFSTART_CI		 (0x00008000)
61562306a36Sopenharmony_ci	vcc_rxreadptr		= 0x18,	/* RX readptr */
61662306a36Sopenharmony_ci	vcc_txicg		= 0x1C, /* TX ICG */
61762306a36Sopenharmony_ci	vcc_txaddr1		= 0x20,	/* Location1, plus bits: */
61862306a36Sopenharmony_ci#define   TXADDR1_SET_SIZE(x) ((x)*0x0000100)	/* size of TX buffer */
61962306a36Sopenharmony_ci#define   TXADDR1_ABR		 (0x00008000)	/* use ABR (doesn't work) */
62062306a36Sopenharmony_ci	vcc_txaddr2		= 0x24,	/* Location2 */
62162306a36Sopenharmony_ci	vcc_txcrc1		= 0x28,	/* TX CRC claculation space */
62262306a36Sopenharmony_ci	vcc_txcrc2		= 0x2C,
62362306a36Sopenharmony_ci	vcc_txreadptr		= 0x30, /* TX Readptr, plus bits: */
62462306a36Sopenharmony_ci#define   TXREADPTR_GET_PTR(x) ((x)&0x01FFF)
62562306a36Sopenharmony_ci#define   TXREADPTR_MASK_DELTA	(0x0000E000)	/* ? */
62662306a36Sopenharmony_ci	vcc_txendptr		= 0x34, /* TX Endptr, plus bits: */
62762306a36Sopenharmony_ci#define   TXENDPTR_CLP		(0x00002000)
62862306a36Sopenharmony_ci#define   TXENDPTR_MASK_PDUMODE	(0x0000C000)	/* PDU mode; values: */
62962306a36Sopenharmony_ci#define     PDUMODE_AAL0	 (0*0x04000)
63062306a36Sopenharmony_ci#define     PDUMODE_AAL5	 (2*0x04000)
63162306a36Sopenharmony_ci#define     PDUMODE_AAL5STREAM	 (3*0x04000)
63262306a36Sopenharmony_ci	vcc_txwriteptr		= 0x38,	/* TX Writeptr */
63362306a36Sopenharmony_ci#define   TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF)
63462306a36Sopenharmony_ci	vcc_txcbr_next		= 0x3C	/* # of next CBR VCI in ring */
63562306a36Sopenharmony_ci#define   TXCBR_NEXT_BOZO	(0x00008000)	/* "bozo bit" */
63662306a36Sopenharmony_ci};
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci#define CARDVCC_SIZE	(0x40)
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai,
64162306a36Sopenharmony_ci	vci_t vci)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	return sram_addr(lanai, vci * CARDVCC_SIZE);
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic inline u32 cardvcc_read(const struct lanai_vcc *lvcc,
64762306a36Sopenharmony_ci	enum lanai_vcc_offset offset)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	u32 val;
65062306a36Sopenharmony_ci	APRINTK(lvcc->vbase != NULL, "cardvcc_read: unbound vcc!\n");
65162306a36Sopenharmony_ci	val= readl(lvcc->vbase + offset);
65262306a36Sopenharmony_ci	RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n",
65362306a36Sopenharmony_ci	    lvcc->vci, (int) offset, val);
65462306a36Sopenharmony_ci	return val;
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic inline void cardvcc_write(const struct lanai_vcc *lvcc,
65862306a36Sopenharmony_ci	u32 val, enum lanai_vcc_offset offset)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	APRINTK(lvcc->vbase != NULL, "cardvcc_write: unbound vcc!\n");
66162306a36Sopenharmony_ci	APRINTK((val & ~0xFFFF) == 0,
66262306a36Sopenharmony_ci	    "cardvcc_write: bad val 0x%X (vci=%d, addr=0x%02X)\n",
66362306a36Sopenharmony_ci	    (unsigned int) val, lvcc->vci, (unsigned int) offset);
66462306a36Sopenharmony_ci	RWDEBUG("VW vci=%04d 0x%02X > 0x%08X\n",
66562306a36Sopenharmony_ci	    lvcc->vci, (unsigned int) offset, (unsigned int) val);
66662306a36Sopenharmony_ci	writel(val, lvcc->vbase + offset);
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci/* -------------------- COMPUTE SIZE OF AN AAL5 PDU: */
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci/* How many bytes will an AAL5 PDU take to transmit - remember that:
67262306a36Sopenharmony_ci *   o  we need to add 8 bytes for length, CPI, UU, and CRC
67362306a36Sopenharmony_ci *   o  we need to round up to 48 bytes for cells
67462306a36Sopenharmony_ci */
67562306a36Sopenharmony_cistatic inline int aal5_size(int size)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	int cells = (size + 8 + 47) / 48;
67862306a36Sopenharmony_ci	return cells * 48;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/* -------------------- FREE AN ATM SKB: */
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	if (atmvcc->pop != NULL)
68662306a36Sopenharmony_ci		atmvcc->pop(atmvcc, skb);
68762306a36Sopenharmony_ci	else
68862306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci/* -------------------- TURN VCCS ON AND OFF: */
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic void host_vcc_start_rx(const struct lanai_vcc *lvcc)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	u32 addr1;
69662306a36Sopenharmony_ci	if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) {
69762306a36Sopenharmony_ci		dma_addr_t dmaaddr = lvcc->rx.buf.dmaaddr;
69862306a36Sopenharmony_ci		cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1);
69962306a36Sopenharmony_ci		cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2);
70062306a36Sopenharmony_ci		cardvcc_write(lvcc, 0, vcc_rxwriteptr);
70162306a36Sopenharmony_ci		cardvcc_write(lvcc, 0, vcc_rxbufstart);
70262306a36Sopenharmony_ci		cardvcc_write(lvcc, 0, vcc_rxreadptr);
70362306a36Sopenharmony_ci		cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_rxaddr2);
70462306a36Sopenharmony_ci		addr1 = ((dmaaddr >> 8) & 0xFF) |
70562306a36Sopenharmony_ci		    RXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->rx.buf))|
70662306a36Sopenharmony_ci		    RXADDR1_SET_RMMODE(RMMODE_TRASH) |	/* ??? */
70762306a36Sopenharmony_ci		 /* RXADDR1_OAM_PRESERVE |	--- no OAM support yet */
70862306a36Sopenharmony_ci		    RXADDR1_SET_MODE(RXMODE_AAL5);
70962306a36Sopenharmony_ci	} else
71062306a36Sopenharmony_ci		addr1 = RXADDR1_SET_RMMODE(RMMODE_PRESERVE) | /* ??? */
71162306a36Sopenharmony_ci		    RXADDR1_OAM_PRESERVE |		      /* ??? */
71262306a36Sopenharmony_ci		    RXADDR1_SET_MODE(RXMODE_AAL0);
71362306a36Sopenharmony_ci	/* This one must be last! */
71462306a36Sopenharmony_ci	cardvcc_write(lvcc, addr1, vcc_rxaddr1);
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic void host_vcc_start_tx(const struct lanai_vcc *lvcc)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	dma_addr_t dmaaddr = lvcc->tx.buf.dmaaddr;
72062306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txicg);
72162306a36Sopenharmony_ci	cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1);
72262306a36Sopenharmony_ci	cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2);
72362306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txreadptr);
72462306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txendptr);
72562306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txwriteptr);
72662306a36Sopenharmony_ci	cardvcc_write(lvcc,
72762306a36Sopenharmony_ci		(lvcc->tx.atmvcc->qos.txtp.traffic_class == ATM_CBR) ?
72862306a36Sopenharmony_ci		TXCBR_NEXT_BOZO | lvcc->vci : 0, vcc_txcbr_next);
72962306a36Sopenharmony_ci	cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_txaddr2);
73062306a36Sopenharmony_ci	cardvcc_write(lvcc,
73162306a36Sopenharmony_ci	    ((dmaaddr >> 8) & 0xFF) |
73262306a36Sopenharmony_ci	    TXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->tx.buf)),
73362306a36Sopenharmony_ci	    vcc_txaddr1);
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/* Shutdown receiving on card */
73762306a36Sopenharmony_cistatic void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	if (lvcc->vbase == NULL)	/* We were never bound to a VCI */
74062306a36Sopenharmony_ci		return;
74162306a36Sopenharmony_ci	/* 15.1.1 - set to trashing, wait one cell time (15us) */
74262306a36Sopenharmony_ci	cardvcc_write(lvcc,
74362306a36Sopenharmony_ci	    RXADDR1_SET_RMMODE(RMMODE_TRASH) |
74462306a36Sopenharmony_ci	    RXADDR1_SET_MODE(RXMODE_TRASH), vcc_rxaddr1);
74562306a36Sopenharmony_ci	udelay(15);
74662306a36Sopenharmony_ci	/* 15.1.2 - clear rest of entries */
74762306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_rxaddr2);
74862306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_rxcrc1);
74962306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_rxcrc2);
75062306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_rxwriteptr);
75162306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_rxbufstart);
75262306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_rxreadptr);
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci/* Shutdown transmitting on card.
75662306a36Sopenharmony_ci * Unfortunately the lanai needs us to wait until all the data
75762306a36Sopenharmony_ci * drains out of the buffer before we can dealloc it, so this
75862306a36Sopenharmony_ci * can take awhile -- up to 370ms for a full 128KB buffer
75962306a36Sopenharmony_ci * assuming everone else is quiet.  In theory the time is
76062306a36Sopenharmony_ci * boundless if there's a CBR VCC holding things up.
76162306a36Sopenharmony_ci */
76262306a36Sopenharmony_cistatic void lanai_shutdown_tx_vci(struct lanai_dev *lanai,
76362306a36Sopenharmony_ci	struct lanai_vcc *lvcc)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct sk_buff *skb;
76662306a36Sopenharmony_ci	unsigned long flags, timeout;
76762306a36Sopenharmony_ci	int read, write, lastread = -1;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (lvcc->vbase == NULL)	/* We were never bound to a VCI */
77062306a36Sopenharmony_ci		return;
77162306a36Sopenharmony_ci	/* 15.2.1 - wait for queue to drain */
77262306a36Sopenharmony_ci	while ((skb = skb_dequeue(&lvcc->tx.backlog)) != NULL)
77362306a36Sopenharmony_ci		lanai_free_skb(lvcc->tx.atmvcc, skb);
77462306a36Sopenharmony_ci	read_lock_irqsave(&vcc_sklist_lock, flags);
77562306a36Sopenharmony_ci	__clear_bit(lvcc->vci, lanai->backlog_vccs);
77662306a36Sopenharmony_ci	read_unlock_irqrestore(&vcc_sklist_lock, flags);
77762306a36Sopenharmony_ci	/*
77862306a36Sopenharmony_ci	 * We need to wait for the VCC to drain but don't wait forever.  We
77962306a36Sopenharmony_ci	 * give each 1K of buffer size 1/128th of a second to clear out.
78062306a36Sopenharmony_ci	 * TODO: maybe disable CBR if we're about to timeout?
78162306a36Sopenharmony_ci	 */
78262306a36Sopenharmony_ci	timeout = jiffies +
78362306a36Sopenharmony_ci	    (((lanai_buf_size(&lvcc->tx.buf) / 1024) * HZ) >> 7);
78462306a36Sopenharmony_ci	write = TXWRITEPTR_GET_PTR(cardvcc_read(lvcc, vcc_txwriteptr));
78562306a36Sopenharmony_ci	for (;;) {
78662306a36Sopenharmony_ci		read = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr));
78762306a36Sopenharmony_ci		if (read == write &&	   /* Is TX buffer empty? */
78862306a36Sopenharmony_ci		    (lvcc->tx.atmvcc->qos.txtp.traffic_class != ATM_CBR ||
78962306a36Sopenharmony_ci		    (cardvcc_read(lvcc, vcc_txcbr_next) &
79062306a36Sopenharmony_ci		    TXCBR_NEXT_BOZO) == 0))
79162306a36Sopenharmony_ci			break;
79262306a36Sopenharmony_ci		if (read != lastread) {	   /* Has there been any progress? */
79362306a36Sopenharmony_ci			lastread = read;
79462306a36Sopenharmony_ci			timeout += HZ / 10;
79562306a36Sopenharmony_ci		}
79662306a36Sopenharmony_ci		if (unlikely(time_after(jiffies, timeout))) {
79762306a36Sopenharmony_ci			printk(KERN_ERR DEV_LABEL "(itf %d): Timed out on "
79862306a36Sopenharmony_ci			    "backlog closing vci %d\n",
79962306a36Sopenharmony_ci			    lvcc->tx.atmvcc->dev->number, lvcc->vci);
80062306a36Sopenharmony_ci			DPRINTK("read, write = %d, %d\n", read, write);
80162306a36Sopenharmony_ci			break;
80262306a36Sopenharmony_ci		}
80362306a36Sopenharmony_ci		msleep(40);
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci	/* 15.2.2 - clear out all tx registers */
80662306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txreadptr);
80762306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txwriteptr);
80862306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txendptr);
80962306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txcrc1);
81062306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txcrc2);
81162306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txaddr2);
81262306a36Sopenharmony_ci	cardvcc_write(lvcc, 0, vcc_txaddr1);
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci/* -------------------- MANAGING AAL0 RX BUFFER: */
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic inline int aal0_buffer_allocate(struct lanai_dev *lanai)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	DPRINTK("aal0_buffer_allocate: allocating AAL0 RX buffer\n");
82062306a36Sopenharmony_ci	lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80,
82162306a36Sopenharmony_ci			   lanai->pci);
82262306a36Sopenharmony_ci	return (lanai->aal0buf.start == NULL) ? -ENOMEM : 0;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic inline void aal0_buffer_free(struct lanai_dev *lanai)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	DPRINTK("aal0_buffer_allocate: freeing AAL0 RX buffer\n");
82862306a36Sopenharmony_ci	lanai_buf_deallocate(&lanai->aal0buf, lanai->pci);
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci/* -------------------- EEPROM UTILITIES: */
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci/* Offsets of data in the EEPROM */
83462306a36Sopenharmony_ci#define EEPROM_COPYRIGHT	(0)
83562306a36Sopenharmony_ci#define EEPROM_COPYRIGHT_LEN	(44)
83662306a36Sopenharmony_ci#define EEPROM_CHECKSUM		(62)
83762306a36Sopenharmony_ci#define EEPROM_CHECKSUM_REV	(63)
83862306a36Sopenharmony_ci#define EEPROM_MAC		(64)
83962306a36Sopenharmony_ci#define EEPROM_MAC_REV		(70)
84062306a36Sopenharmony_ci#define EEPROM_SERIAL		(112)
84162306a36Sopenharmony_ci#define EEPROM_SERIAL_REV	(116)
84262306a36Sopenharmony_ci#define EEPROM_MAGIC		(120)
84362306a36Sopenharmony_ci#define EEPROM_MAGIC_REV	(124)
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci#define EEPROM_MAGIC_VALUE	(0x5AB478D2)
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci#ifndef READ_EEPROM
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci/* Stub functions to use if EEPROM reading is disabled */
85062306a36Sopenharmony_cistatic int eeprom_read(struct lanai_dev *lanai)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	printk(KERN_INFO DEV_LABEL "(itf %d): *NOT* reading EEPROM\n",
85362306a36Sopenharmony_ci	    lanai->number);
85462306a36Sopenharmony_ci	memset(&lanai->eeprom[EEPROM_MAC], 0, 6);
85562306a36Sopenharmony_ci	return 0;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic int eeprom_validate(struct lanai_dev *lanai)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	lanai->serialno = 0;
86162306a36Sopenharmony_ci	lanai->magicno = EEPROM_MAGIC_VALUE;
86262306a36Sopenharmony_ci	return 0;
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci#else /* READ_EEPROM */
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic int eeprom_read(struct lanai_dev *lanai)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	int i, address;
87062306a36Sopenharmony_ci	u8 data;
87162306a36Sopenharmony_ci	u32 tmp;
87262306a36Sopenharmony_ci#define set_config1(x)   do { lanai->conf1 = x; conf1_write(lanai); \
87362306a36Sopenharmony_ci			    } while (0)
87462306a36Sopenharmony_ci#define clock_h()	 set_config1(lanai->conf1 | CONFIG1_PROMCLK)
87562306a36Sopenharmony_ci#define clock_l()	 set_config1(lanai->conf1 &~ CONFIG1_PROMCLK)
87662306a36Sopenharmony_ci#define data_h()	 set_config1(lanai->conf1 | CONFIG1_PROMDATA)
87762306a36Sopenharmony_ci#define data_l()	 set_config1(lanai->conf1 &~ CONFIG1_PROMDATA)
87862306a36Sopenharmony_ci#define pre_read()	 do { data_h(); clock_h(); udelay(5); } while (0)
87962306a36Sopenharmony_ci#define read_pin()	 (reg_read(lanai, Status_Reg) & STATUS_PROMDATA)
88062306a36Sopenharmony_ci#define send_stop()	 do { data_l(); udelay(5); clock_h(); udelay(5); \
88162306a36Sopenharmony_ci			      data_h(); udelay(5); } while (0)
88262306a36Sopenharmony_ci	/* start with both clock and data high */
88362306a36Sopenharmony_ci	data_h(); clock_h(); udelay(5);
88462306a36Sopenharmony_ci	for (address = 0; address < LANAI_EEPROM_SIZE; address++) {
88562306a36Sopenharmony_ci		data = (address << 1) | 1;	/* Command=read + address */
88662306a36Sopenharmony_ci		/* send start bit */
88762306a36Sopenharmony_ci		data_l(); udelay(5);
88862306a36Sopenharmony_ci		clock_l(); udelay(5);
88962306a36Sopenharmony_ci		for (i = 128; i != 0; i >>= 1) {   /* write command out */
89062306a36Sopenharmony_ci			tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) |
89162306a36Sopenharmony_ci			    ((data & i) ? CONFIG1_PROMDATA : 0);
89262306a36Sopenharmony_ci			if (lanai->conf1 != tmp) {
89362306a36Sopenharmony_ci				set_config1(tmp);
89462306a36Sopenharmony_ci				udelay(5);	/* Let new data settle */
89562306a36Sopenharmony_ci			}
89662306a36Sopenharmony_ci			clock_h(); udelay(5); clock_l(); udelay(5);
89762306a36Sopenharmony_ci		}
89862306a36Sopenharmony_ci		/* look for ack */
89962306a36Sopenharmony_ci		data_h(); clock_h(); udelay(5);
90062306a36Sopenharmony_ci		if (read_pin() != 0)
90162306a36Sopenharmony_ci			goto error;	/* No ack seen */
90262306a36Sopenharmony_ci		clock_l(); udelay(5);
90362306a36Sopenharmony_ci		/* read back result */
90462306a36Sopenharmony_ci		for (data = 0, i = 7; i >= 0; i--) {
90562306a36Sopenharmony_ci			data_h(); clock_h(); udelay(5);
90662306a36Sopenharmony_ci			data = (data << 1) | !!read_pin();
90762306a36Sopenharmony_ci			clock_l(); udelay(5);
90862306a36Sopenharmony_ci		}
90962306a36Sopenharmony_ci		/* look again for ack */
91062306a36Sopenharmony_ci		data_h(); clock_h(); udelay(5);
91162306a36Sopenharmony_ci		if (read_pin() == 0)
91262306a36Sopenharmony_ci			goto error;	/* Spurious ack */
91362306a36Sopenharmony_ci		clock_l(); udelay(5);
91462306a36Sopenharmony_ci		send_stop();
91562306a36Sopenharmony_ci		lanai->eeprom[address] = data;
91662306a36Sopenharmony_ci		DPRINTK("EEPROM 0x%04X %02X\n",
91762306a36Sopenharmony_ci		    (unsigned int) address, (unsigned int) data);
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci	return 0;
92062306a36Sopenharmony_ci    error:
92162306a36Sopenharmony_ci	clock_l(); udelay(5);		/* finish read */
92262306a36Sopenharmony_ci	send_stop();
92362306a36Sopenharmony_ci	printk(KERN_ERR DEV_LABEL "(itf %d): error reading EEPROM byte %d\n",
92462306a36Sopenharmony_ci	    lanai->number, address);
92562306a36Sopenharmony_ci	return -EIO;
92662306a36Sopenharmony_ci#undef set_config1
92762306a36Sopenharmony_ci#undef clock_h
92862306a36Sopenharmony_ci#undef clock_l
92962306a36Sopenharmony_ci#undef data_h
93062306a36Sopenharmony_ci#undef data_l
93162306a36Sopenharmony_ci#undef pre_read
93262306a36Sopenharmony_ci#undef read_pin
93362306a36Sopenharmony_ci#undef send_stop
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci/* read a big-endian 4-byte value out of eeprom */
93762306a36Sopenharmony_cistatic inline u32 eeprom_be4(const struct lanai_dev *lanai, int address)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	return be32_to_cpup((const u32 *) &lanai->eeprom[address]);
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci/* Checksum/validate EEPROM contents */
94362306a36Sopenharmony_cistatic int eeprom_validate(struct lanai_dev *lanai)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	int i, s;
94662306a36Sopenharmony_ci	u32 v;
94762306a36Sopenharmony_ci	const u8 *e = lanai->eeprom;
94862306a36Sopenharmony_ci#ifdef DEBUG
94962306a36Sopenharmony_ci	/* First, see if we can get an ASCIIZ string out of the copyright */
95062306a36Sopenharmony_ci	for (i = EEPROM_COPYRIGHT;
95162306a36Sopenharmony_ci	    i < (EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN); i++)
95262306a36Sopenharmony_ci		if (e[i] < 0x20 || e[i] > 0x7E)
95362306a36Sopenharmony_ci			break;
95462306a36Sopenharmony_ci	if ( i != EEPROM_COPYRIGHT &&
95562306a36Sopenharmony_ci	    i != EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN && e[i] == '\0')
95662306a36Sopenharmony_ci		DPRINTK("eeprom: copyright = \"%s\"\n",
95762306a36Sopenharmony_ci		    (char *) &e[EEPROM_COPYRIGHT]);
95862306a36Sopenharmony_ci	else
95962306a36Sopenharmony_ci		DPRINTK("eeprom: copyright not found\n");
96062306a36Sopenharmony_ci#endif
96162306a36Sopenharmony_ci	/* Validate checksum */
96262306a36Sopenharmony_ci	for (i = s = 0; i < EEPROM_CHECKSUM; i++)
96362306a36Sopenharmony_ci		s += e[i];
96462306a36Sopenharmony_ci	s &= 0xFF;
96562306a36Sopenharmony_ci	if (s != e[EEPROM_CHECKSUM]) {
96662306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM checksum bad "
96762306a36Sopenharmony_ci		    "(wanted 0x%02X, got 0x%02X)\n", lanai->number,
96862306a36Sopenharmony_ci		    (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM]);
96962306a36Sopenharmony_ci		return -EIO;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci	s ^= 0xFF;
97262306a36Sopenharmony_ci	if (s != e[EEPROM_CHECKSUM_REV]) {
97362306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM inverse checksum "
97462306a36Sopenharmony_ci		    "bad (wanted 0x%02X, got 0x%02X)\n", lanai->number,
97562306a36Sopenharmony_ci		    (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM_REV]);
97662306a36Sopenharmony_ci		return -EIO;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci	/* Verify MAC address */
97962306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
98062306a36Sopenharmony_ci		if ((e[EEPROM_MAC + i] ^ e[EEPROM_MAC_REV + i]) != 0xFF) {
98162306a36Sopenharmony_ci			printk(KERN_ERR DEV_LABEL
98262306a36Sopenharmony_ci			    "(itf %d) : EEPROM MAC addresses don't match "
98362306a36Sopenharmony_ci			    "(0x%02X, inverse 0x%02X)\n", lanai->number,
98462306a36Sopenharmony_ci			    (unsigned int) e[EEPROM_MAC + i],
98562306a36Sopenharmony_ci			    (unsigned int) e[EEPROM_MAC_REV + i]);
98662306a36Sopenharmony_ci			return -EIO;
98762306a36Sopenharmony_ci		}
98862306a36Sopenharmony_ci	DPRINTK("eeprom: MAC address = %pM\n", &e[EEPROM_MAC]);
98962306a36Sopenharmony_ci	/* Verify serial number */
99062306a36Sopenharmony_ci	lanai->serialno = eeprom_be4(lanai, EEPROM_SERIAL);
99162306a36Sopenharmony_ci	v = eeprom_be4(lanai, EEPROM_SERIAL_REV);
99262306a36Sopenharmony_ci	if ((lanai->serialno ^ v) != 0xFFFFFFFF) {
99362306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM serial numbers "
99462306a36Sopenharmony_ci		    "don't match (0x%08X, inverse 0x%08X)\n", lanai->number,
99562306a36Sopenharmony_ci		    (unsigned int) lanai->serialno, (unsigned int) v);
99662306a36Sopenharmony_ci		return -EIO;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci	DPRINTK("eeprom: Serial number = %d\n", (unsigned int) lanai->serialno);
99962306a36Sopenharmony_ci	/* Verify magic number */
100062306a36Sopenharmony_ci	lanai->magicno = eeprom_be4(lanai, EEPROM_MAGIC);
100162306a36Sopenharmony_ci	v = eeprom_be4(lanai, EEPROM_MAGIC_REV);
100262306a36Sopenharmony_ci	if ((lanai->magicno ^ v) != 0xFFFFFFFF) {
100362306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM magic numbers "
100462306a36Sopenharmony_ci		    "don't match (0x%08X, inverse 0x%08X)\n", lanai->number,
100562306a36Sopenharmony_ci		    lanai->magicno, v);
100662306a36Sopenharmony_ci		return -EIO;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci	DPRINTK("eeprom: Magic number = 0x%08X\n", lanai->magicno);
100962306a36Sopenharmony_ci	if (lanai->magicno != EEPROM_MAGIC_VALUE)
101062306a36Sopenharmony_ci		printk(KERN_WARNING DEV_LABEL "(itf %d): warning - EEPROM "
101162306a36Sopenharmony_ci		    "magic not what expected (got 0x%08X, not 0x%08X)\n",
101262306a36Sopenharmony_ci		    lanai->number, (unsigned int) lanai->magicno,
101362306a36Sopenharmony_ci		    (unsigned int) EEPROM_MAGIC_VALUE);
101462306a36Sopenharmony_ci	return 0;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci#endif /* READ_EEPROM */
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic inline const u8 *eeprom_mac(const struct lanai_dev *lanai)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	return &lanai->eeprom[EEPROM_MAC];
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci/* -------------------- INTERRUPT HANDLING UTILITIES: */
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci/* Interrupt types */
102762306a36Sopenharmony_ci#define INT_STATS	(0x00000002)	/* Statistics counter overflow */
102862306a36Sopenharmony_ci#define INT_SOOL	(0x00000004)	/* SOOL changed state */
102962306a36Sopenharmony_ci#define INT_LOCD	(0x00000008)	/* LOCD changed state */
103062306a36Sopenharmony_ci#define INT_LED		(0x00000010)	/* LED (HAPPI) changed state */
103162306a36Sopenharmony_ci#define INT_GPIN	(0x00000020)	/* GPIN changed state */
103262306a36Sopenharmony_ci#define INT_PING	(0x00000040)	/* PING_COUNT fulfilled */
103362306a36Sopenharmony_ci#define INT_WAKE	(0x00000080)	/* Lanai wants bus */
103462306a36Sopenharmony_ci#define INT_CBR0	(0x00000100)	/* CBR sched hit VCI 0 */
103562306a36Sopenharmony_ci#define INT_LOCK	(0x00000200)	/* Service list overflow */
103662306a36Sopenharmony_ci#define INT_MISMATCH	(0x00000400)	/* TX magic list mismatch */
103762306a36Sopenharmony_ci#define INT_AAL0_STR	(0x00000800)	/* Non-AAL5 buffer half filled */
103862306a36Sopenharmony_ci#define INT_AAL0	(0x00001000)	/* Non-AAL5 data available */
103962306a36Sopenharmony_ci#define INT_SERVICE	(0x00002000)	/* Service list entries available */
104062306a36Sopenharmony_ci#define INT_TABORTSENT	(0x00004000)	/* Target abort sent by lanai */
104162306a36Sopenharmony_ci#define INT_TABORTBM	(0x00008000)	/* Abort rcv'd as bus master */
104262306a36Sopenharmony_ci#define INT_TIMEOUTBM	(0x00010000)	/* No response to bus master */
104362306a36Sopenharmony_ci#define INT_PCIPARITY	(0x00020000)	/* Parity error on PCI */
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci/* Sets of the above */
104662306a36Sopenharmony_ci#define INT_ALL		(0x0003FFFE)	/* All interrupts */
104762306a36Sopenharmony_ci#define INT_STATUS	(0x0000003C)	/* Some status pin changed */
104862306a36Sopenharmony_ci#define INT_DMASHUT	(0x00038000)	/* DMA engine got shut down */
104962306a36Sopenharmony_ci#define INT_SEGSHUT	(0x00000700)	/* Segmentation got shut down */
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic inline u32 intr_pending(const struct lanai_dev *lanai)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	return reg_read(lanai, IntStatusMasked_Reg);
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic inline void intr_enable(const struct lanai_dev *lanai, u32 i)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	reg_write(lanai, i, IntControlEna_Reg);
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic inline void intr_disable(const struct lanai_dev *lanai, u32 i)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	reg_write(lanai, i, IntControlDis_Reg);
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci/* -------------------- CARD/PCI STATUS: */
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_cistatic void status_message(int itf, const char *name, int status)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	static const char *onoff[2] = { "off to on", "on to off" };
107162306a36Sopenharmony_ci	printk(KERN_INFO DEV_LABEL "(itf %d): %s changed from %s\n",
107262306a36Sopenharmony_ci	    itf, name, onoff[!status]);
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic void lanai_check_status(struct lanai_dev *lanai)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	u32 new = reg_read(lanai, Status_Reg);
107862306a36Sopenharmony_ci	u32 changes = new ^ lanai->status;
107962306a36Sopenharmony_ci	lanai->status = new;
108062306a36Sopenharmony_ci#define e(flag, name) \
108162306a36Sopenharmony_ci		if (changes & flag) \
108262306a36Sopenharmony_ci			status_message(lanai->number, name, new & flag)
108362306a36Sopenharmony_ci	e(STATUS_SOOL, "SOOL");
108462306a36Sopenharmony_ci	e(STATUS_LOCD, "LOCD");
108562306a36Sopenharmony_ci	e(STATUS_LED, "LED");
108662306a36Sopenharmony_ci	e(STATUS_GPIN, "GPIN");
108762306a36Sopenharmony_ci#undef e
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic void pcistatus_got(int itf, const char *name)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	printk(KERN_INFO DEV_LABEL "(itf %d): PCI got %s error\n", itf, name);
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_cistatic void pcistatus_check(struct lanai_dev *lanai, int clearonly)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci	u16 s;
109862306a36Sopenharmony_ci	int result;
109962306a36Sopenharmony_ci	result = pci_read_config_word(lanai->pci, PCI_STATUS, &s);
110062306a36Sopenharmony_ci	if (result != PCIBIOS_SUCCESSFUL) {
110162306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_STATUS: "
110262306a36Sopenharmony_ci		    "%d\n", lanai->number, result);
110362306a36Sopenharmony_ci		return;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci	s &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR |
110662306a36Sopenharmony_ci	    PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT |
110762306a36Sopenharmony_ci	    PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_PARITY;
110862306a36Sopenharmony_ci	if (s == 0)
110962306a36Sopenharmony_ci		return;
111062306a36Sopenharmony_ci	result = pci_write_config_word(lanai->pci, PCI_STATUS, s);
111162306a36Sopenharmony_ci	if (result != PCIBIOS_SUCCESSFUL)
111262306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): can't write PCI_STATUS: "
111362306a36Sopenharmony_ci		    "%d\n", lanai->number, result);
111462306a36Sopenharmony_ci	if (clearonly)
111562306a36Sopenharmony_ci		return;
111662306a36Sopenharmony_ci#define e(flag, name, stat) \
111762306a36Sopenharmony_ci		if (s & flag) { \
111862306a36Sopenharmony_ci			pcistatus_got(lanai->number, name); \
111962306a36Sopenharmony_ci			++lanai->stats.pcierr_##stat; \
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci	e(PCI_STATUS_DETECTED_PARITY, "parity", parity_detect);
112262306a36Sopenharmony_ci	e(PCI_STATUS_SIG_SYSTEM_ERROR, "signalled system", serr_set);
112362306a36Sopenharmony_ci	e(PCI_STATUS_REC_MASTER_ABORT, "master", master_abort);
112462306a36Sopenharmony_ci	e(PCI_STATUS_REC_TARGET_ABORT, "master target", m_target_abort);
112562306a36Sopenharmony_ci	e(PCI_STATUS_SIG_TARGET_ABORT, "slave", s_target_abort);
112662306a36Sopenharmony_ci	e(PCI_STATUS_PARITY, "master parity", master_parity);
112762306a36Sopenharmony_ci#undef e
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci/* -------------------- VCC TX BUFFER UTILITIES: */
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci/* space left in tx buffer in bytes */
113362306a36Sopenharmony_cistatic inline int vcc_tx_space(const struct lanai_vcc *lvcc, int endptr)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	int r;
113662306a36Sopenharmony_ci	r = endptr * 16;
113762306a36Sopenharmony_ci	r -= ((unsigned long) lvcc->tx.buf.ptr) -
113862306a36Sopenharmony_ci	    ((unsigned long) lvcc->tx.buf.start);
113962306a36Sopenharmony_ci	r -= 16;	/* Leave "bubble" - if start==end it looks empty */
114062306a36Sopenharmony_ci	if (r < 0)
114162306a36Sopenharmony_ci		r += lanai_buf_size(&lvcc->tx.buf);
114262306a36Sopenharmony_ci	return r;
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/* test if VCC is currently backlogged */
114662306a36Sopenharmony_cistatic inline int vcc_is_backlogged(const struct lanai_vcc *lvcc)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	return !skb_queue_empty(&lvcc->tx.backlog);
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci/* Bit fields in the segmentation buffer descriptor */
115262306a36Sopenharmony_ci#define DESCRIPTOR_MAGIC	(0xD0000000)
115362306a36Sopenharmony_ci#define DESCRIPTOR_AAL5		(0x00008000)
115462306a36Sopenharmony_ci#define DESCRIPTOR_AAL5_STREAM	(0x00004000)
115562306a36Sopenharmony_ci#define DESCRIPTOR_CLP		(0x00002000)
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci/* Add 32-bit descriptor with its padding */
115862306a36Sopenharmony_cistatic inline void vcc_tx_add_aal5_descriptor(struct lanai_vcc *lvcc,
115962306a36Sopenharmony_ci	u32 flags, int len)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	int pos;
116262306a36Sopenharmony_ci	APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 0,
116362306a36Sopenharmony_ci	    "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr);
116462306a36Sopenharmony_ci	lvcc->tx.buf.ptr += 4;	/* Hope the values REALLY don't matter */
116562306a36Sopenharmony_ci	pos = ((unsigned char *) lvcc->tx.buf.ptr) -
116662306a36Sopenharmony_ci	    (unsigned char *) lvcc->tx.buf.start;
116762306a36Sopenharmony_ci	APRINTK((pos & ~0x0001FFF0) == 0,
116862306a36Sopenharmony_ci	    "vcc_tx_add_aal5_descriptor: bad pos (%d) before, vci=%d, "
116962306a36Sopenharmony_ci	    "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci,
117062306a36Sopenharmony_ci	    lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end);
117162306a36Sopenharmony_ci	pos = (pos + len) & (lanai_buf_size(&lvcc->tx.buf) - 1);
117262306a36Sopenharmony_ci	APRINTK((pos & ~0x0001FFF0) == 0,
117362306a36Sopenharmony_ci	    "vcc_tx_add_aal5_descriptor: bad pos (%d) after, vci=%d, "
117462306a36Sopenharmony_ci	    "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci,
117562306a36Sopenharmony_ci	    lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end);
117662306a36Sopenharmony_ci	lvcc->tx.buf.ptr[-1] =
117762306a36Sopenharmony_ci	    cpu_to_le32(DESCRIPTOR_MAGIC | DESCRIPTOR_AAL5 |
117862306a36Sopenharmony_ci	    ((lvcc->tx.atmvcc->atm_options & ATM_ATMOPT_CLP) ?
117962306a36Sopenharmony_ci	    DESCRIPTOR_CLP : 0) | flags | pos >> 4);
118062306a36Sopenharmony_ci	if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end)
118162306a36Sopenharmony_ci		lvcc->tx.buf.ptr = lvcc->tx.buf.start;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci/* Add 32-bit AAL5 trailer and leave room for its CRC */
118562306a36Sopenharmony_cistatic inline void vcc_tx_add_aal5_trailer(struct lanai_vcc *lvcc,
118662306a36Sopenharmony_ci	int len, int cpi, int uu)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 8,
118962306a36Sopenharmony_ci	    "vcc_tx_add_aal5_trailer: bad ptr=%p\n", lvcc->tx.buf.ptr);
119062306a36Sopenharmony_ci	lvcc->tx.buf.ptr += 2;
119162306a36Sopenharmony_ci	lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len);
119262306a36Sopenharmony_ci	if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end)
119362306a36Sopenharmony_ci		lvcc->tx.buf.ptr = lvcc->tx.buf.start;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic inline void vcc_tx_memcpy(struct lanai_vcc *lvcc,
119762306a36Sopenharmony_ci	const unsigned char *src, int n)
119862306a36Sopenharmony_ci{
119962306a36Sopenharmony_ci	unsigned char *e;
120062306a36Sopenharmony_ci	int m;
120162306a36Sopenharmony_ci	e = ((unsigned char *) lvcc->tx.buf.ptr) + n;
120262306a36Sopenharmony_ci	m = e - (unsigned char *) lvcc->tx.buf.end;
120362306a36Sopenharmony_ci	if (m < 0)
120462306a36Sopenharmony_ci		m = 0;
120562306a36Sopenharmony_ci	memcpy(lvcc->tx.buf.ptr, src, n - m);
120662306a36Sopenharmony_ci	if (m != 0) {
120762306a36Sopenharmony_ci		memcpy(lvcc->tx.buf.start, src + n - m, m);
120862306a36Sopenharmony_ci		e = ((unsigned char *) lvcc->tx.buf.start) + m;
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci	lvcc->tx.buf.ptr = (u32 *) e;
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_cistatic inline void vcc_tx_memzero(struct lanai_vcc *lvcc, int n)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	unsigned char *e;
121662306a36Sopenharmony_ci	int m;
121762306a36Sopenharmony_ci	if (n == 0)
121862306a36Sopenharmony_ci		return;
121962306a36Sopenharmony_ci	e = ((unsigned char *) lvcc->tx.buf.ptr) + n;
122062306a36Sopenharmony_ci	m = e - (unsigned char *) lvcc->tx.buf.end;
122162306a36Sopenharmony_ci	if (m < 0)
122262306a36Sopenharmony_ci		m = 0;
122362306a36Sopenharmony_ci	memset(lvcc->tx.buf.ptr, 0, n - m);
122462306a36Sopenharmony_ci	if (m != 0) {
122562306a36Sopenharmony_ci		memset(lvcc->tx.buf.start, 0, m);
122662306a36Sopenharmony_ci		e = ((unsigned char *) lvcc->tx.buf.start) + m;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci	lvcc->tx.buf.ptr = (u32 *) e;
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci/* Update "butt" register to specify new WritePtr */
123262306a36Sopenharmony_cistatic inline void lanai_endtx(struct lanai_dev *lanai,
123362306a36Sopenharmony_ci	const struct lanai_vcc *lvcc)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	int i, ptr = ((unsigned char *) lvcc->tx.buf.ptr) -
123662306a36Sopenharmony_ci	    (unsigned char *) lvcc->tx.buf.start;
123762306a36Sopenharmony_ci	APRINTK((ptr & ~0x0001FFF0) == 0,
123862306a36Sopenharmony_ci	    "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n",
123962306a36Sopenharmony_ci	    ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr,
124062306a36Sopenharmony_ci	    lvcc->tx.buf.end);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	/*
124362306a36Sopenharmony_ci	 * Since the "butt register" is a shared resounce on the card we
124462306a36Sopenharmony_ci	 * serialize all accesses to it through this spinlock.  This is
124562306a36Sopenharmony_ci	 * mostly just paranoia since the register is rarely "busy" anyway
124662306a36Sopenharmony_ci	 * but is needed for correctness.
124762306a36Sopenharmony_ci	 */
124862306a36Sopenharmony_ci	spin_lock(&lanai->endtxlock);
124962306a36Sopenharmony_ci	/*
125062306a36Sopenharmony_ci	 * We need to check if the "butt busy" bit is set before
125162306a36Sopenharmony_ci	 * updating the butt register.  In theory this should
125262306a36Sopenharmony_ci	 * never happen because the ATM card is plenty fast at
125362306a36Sopenharmony_ci	 * updating the register.  Still, we should make sure
125462306a36Sopenharmony_ci	 */
125562306a36Sopenharmony_ci	for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) {
125662306a36Sopenharmony_ci		if (unlikely(i > 50)) {
125762306a36Sopenharmony_ci			printk(KERN_ERR DEV_LABEL "(itf %d): butt register "
125862306a36Sopenharmony_ci			    "always busy!\n", lanai->number);
125962306a36Sopenharmony_ci			break;
126062306a36Sopenharmony_ci		}
126162306a36Sopenharmony_ci		udelay(5);
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci	/*
126462306a36Sopenharmony_ci	 * Before we tall the card to start work we need to be sure 100% of
126562306a36Sopenharmony_ci	 * the info in the service buffer has been written before we tell
126662306a36Sopenharmony_ci	 * the card about it
126762306a36Sopenharmony_ci	 */
126862306a36Sopenharmony_ci	wmb();
126962306a36Sopenharmony_ci	reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg);
127062306a36Sopenharmony_ci	spin_unlock(&lanai->endtxlock);
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci/*
127462306a36Sopenharmony_ci * Add one AAL5 PDU to lvcc's transmit buffer.  Caller garauntees there's
127562306a36Sopenharmony_ci * space available.  "pdusize" is the number of bytes the PDU will take
127662306a36Sopenharmony_ci */
127762306a36Sopenharmony_cistatic void lanai_send_one_aal5(struct lanai_dev *lanai,
127862306a36Sopenharmony_ci	struct lanai_vcc *lvcc, struct sk_buff *skb, int pdusize)
127962306a36Sopenharmony_ci{
128062306a36Sopenharmony_ci	int pad;
128162306a36Sopenharmony_ci	APRINTK(pdusize == aal5_size(skb->len),
128262306a36Sopenharmony_ci	    "lanai_send_one_aal5: wrong size packet (%d != %d)\n",
128362306a36Sopenharmony_ci	    pdusize, aal5_size(skb->len));
128462306a36Sopenharmony_ci	vcc_tx_add_aal5_descriptor(lvcc, 0, pdusize);
128562306a36Sopenharmony_ci	pad = pdusize - skb->len - 8;
128662306a36Sopenharmony_ci	APRINTK(pad >= 0, "pad is negative (%d)\n", pad);
128762306a36Sopenharmony_ci	APRINTK(pad < 48, "pad is too big (%d)\n", pad);
128862306a36Sopenharmony_ci	vcc_tx_memcpy(lvcc, skb->data, skb->len);
128962306a36Sopenharmony_ci	vcc_tx_memzero(lvcc, pad);
129062306a36Sopenharmony_ci	vcc_tx_add_aal5_trailer(lvcc, skb->len, 0, 0);
129162306a36Sopenharmony_ci	lanai_endtx(lanai, lvcc);
129262306a36Sopenharmony_ci	lanai_free_skb(lvcc->tx.atmvcc, skb);
129362306a36Sopenharmony_ci	atomic_inc(&lvcc->tx.atmvcc->stats->tx);
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci/* Try to fill the buffer - don't call unless there is backlog */
129762306a36Sopenharmony_cistatic void vcc_tx_unqueue_aal5(struct lanai_dev *lanai,
129862306a36Sopenharmony_ci	struct lanai_vcc *lvcc, int endptr)
129962306a36Sopenharmony_ci{
130062306a36Sopenharmony_ci	int n;
130162306a36Sopenharmony_ci	struct sk_buff *skb;
130262306a36Sopenharmony_ci	int space = vcc_tx_space(lvcc, endptr);
130362306a36Sopenharmony_ci	APRINTK(vcc_is_backlogged(lvcc),
130462306a36Sopenharmony_ci	    "vcc_tx_unqueue() called with empty backlog (vci=%d)\n",
130562306a36Sopenharmony_ci	    lvcc->vci);
130662306a36Sopenharmony_ci	while (space >= 64) {
130762306a36Sopenharmony_ci		skb = skb_dequeue(&lvcc->tx.backlog);
130862306a36Sopenharmony_ci		if (skb == NULL)
130962306a36Sopenharmony_ci			goto no_backlog;
131062306a36Sopenharmony_ci		n = aal5_size(skb->len);
131162306a36Sopenharmony_ci		if (n + 16 > space) {
131262306a36Sopenharmony_ci			/* No room for this packet - put it back on queue */
131362306a36Sopenharmony_ci			skb_queue_head(&lvcc->tx.backlog, skb);
131462306a36Sopenharmony_ci			return;
131562306a36Sopenharmony_ci		}
131662306a36Sopenharmony_ci		lanai_send_one_aal5(lanai, lvcc, skb, n);
131762306a36Sopenharmony_ci		space -= n + 16;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci	if (!vcc_is_backlogged(lvcc)) {
132062306a36Sopenharmony_ci	    no_backlog:
132162306a36Sopenharmony_ci		__clear_bit(lvcc->vci, lanai->backlog_vccs);
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci/* Given an skb that we want to transmit either send it now or queue */
132662306a36Sopenharmony_cistatic void vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc,
132762306a36Sopenharmony_ci	struct sk_buff *skb)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	int space, n;
133062306a36Sopenharmony_ci	if (vcc_is_backlogged(lvcc))		/* Already backlogged */
133162306a36Sopenharmony_ci		goto queue_it;
133262306a36Sopenharmony_ci	space = vcc_tx_space(lvcc,
133362306a36Sopenharmony_ci		    TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)));
133462306a36Sopenharmony_ci	n = aal5_size(skb->len);
133562306a36Sopenharmony_ci	APRINTK(n + 16 >= 64, "vcc_tx_aal5: n too small (%d)\n", n);
133662306a36Sopenharmony_ci	if (space < n + 16) {			/* No space for this PDU */
133762306a36Sopenharmony_ci		__set_bit(lvcc->vci, lanai->backlog_vccs);
133862306a36Sopenharmony_ci	    queue_it:
133962306a36Sopenharmony_ci		skb_queue_tail(&lvcc->tx.backlog, skb);
134062306a36Sopenharmony_ci		return;
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci	lanai_send_one_aal5(lanai, lvcc, skb, n);
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic void vcc_tx_unqueue_aal0(struct lanai_dev *lanai,
134662306a36Sopenharmony_ci	struct lanai_vcc *lvcc, int endptr)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	printk(KERN_INFO DEV_LABEL
134962306a36Sopenharmony_ci	    ": vcc_tx_unqueue_aal0: not implemented\n");
135062306a36Sopenharmony_ci}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_cistatic void vcc_tx_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc,
135362306a36Sopenharmony_ci	struct sk_buff *skb)
135462306a36Sopenharmony_ci{
135562306a36Sopenharmony_ci	printk(KERN_INFO DEV_LABEL ": vcc_tx_aal0: not implemented\n");
135662306a36Sopenharmony_ci	/* Remember to increment lvcc->tx.atmvcc->stats->tx */
135762306a36Sopenharmony_ci	lanai_free_skb(lvcc->tx.atmvcc, skb);
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci/* -------------------- VCC RX BUFFER UTILITIES: */
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci/* unlike the _tx_ cousins, this doesn't update ptr */
136362306a36Sopenharmony_cistatic inline void vcc_rx_memcpy(unsigned char *dest,
136462306a36Sopenharmony_ci	const struct lanai_vcc *lvcc, int n)
136562306a36Sopenharmony_ci{
136662306a36Sopenharmony_ci	int m = ((const unsigned char *) lvcc->rx.buf.ptr) + n -
136762306a36Sopenharmony_ci	    ((const unsigned char *) (lvcc->rx.buf.end));
136862306a36Sopenharmony_ci	if (m < 0)
136962306a36Sopenharmony_ci		m = 0;
137062306a36Sopenharmony_ci	memcpy(dest, lvcc->rx.buf.ptr, n - m);
137162306a36Sopenharmony_ci	memcpy(dest + n - m, lvcc->rx.buf.start, m);
137262306a36Sopenharmony_ci	/* Make sure that these copies don't get reordered */
137362306a36Sopenharmony_ci	barrier();
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci/* Receive AAL5 data on a VCC with a particular endptr */
137762306a36Sopenharmony_cistatic void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	int size;
138062306a36Sopenharmony_ci	struct sk_buff *skb;
138162306a36Sopenharmony_ci	const u32 *x;
138262306a36Sopenharmony_ci	u32 *end = &lvcc->rx.buf.start[endptr * 4];
138362306a36Sopenharmony_ci	int n = ((unsigned long) end) - ((unsigned long) lvcc->rx.buf.ptr);
138462306a36Sopenharmony_ci	if (n < 0)
138562306a36Sopenharmony_ci		n += lanai_buf_size(&lvcc->rx.buf);
138662306a36Sopenharmony_ci	APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15),
138762306a36Sopenharmony_ci	    "vcc_rx_aal5: n out of range (%d/%zu)\n",
138862306a36Sopenharmony_ci	    n, lanai_buf_size(&lvcc->rx.buf));
138962306a36Sopenharmony_ci	/* Recover the second-to-last word to get true pdu length */
139062306a36Sopenharmony_ci	if ((x = &end[-2]) < lvcc->rx.buf.start)
139162306a36Sopenharmony_ci		x = &lvcc->rx.buf.end[-2];
139262306a36Sopenharmony_ci	/*
139362306a36Sopenharmony_ci	 * Before we actually read from the buffer, make sure the memory
139462306a36Sopenharmony_ci	 * changes have arrived
139562306a36Sopenharmony_ci	 */
139662306a36Sopenharmony_ci	rmb();
139762306a36Sopenharmony_ci	size = be32_to_cpup(x) & 0xffff;
139862306a36Sopenharmony_ci	if (unlikely(n != aal5_size(size))) {
139962306a36Sopenharmony_ci		/* Make sure size matches padding */
140062306a36Sopenharmony_ci		printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length "
140162306a36Sopenharmony_ci		    "on vci=%d - size=%d n=%d\n",
140262306a36Sopenharmony_ci		    lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n);
140362306a36Sopenharmony_ci		lvcc->stats.x.aal5.rx_badlen++;
140462306a36Sopenharmony_ci		goto out;
140562306a36Sopenharmony_ci	}
140662306a36Sopenharmony_ci	skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC);
140762306a36Sopenharmony_ci	if (unlikely(skb == NULL)) {
140862306a36Sopenharmony_ci		lvcc->stats.rx_nomem++;
140962306a36Sopenharmony_ci		goto out;
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci	skb_put(skb, size);
141262306a36Sopenharmony_ci	vcc_rx_memcpy(skb->data, lvcc, size);
141362306a36Sopenharmony_ci	ATM_SKB(skb)->vcc = lvcc->rx.atmvcc;
141462306a36Sopenharmony_ci	__net_timestamp(skb);
141562306a36Sopenharmony_ci	lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb);
141662306a36Sopenharmony_ci	atomic_inc(&lvcc->rx.atmvcc->stats->rx);
141762306a36Sopenharmony_ci    out:
141862306a36Sopenharmony_ci	lvcc->rx.buf.ptr = end;
141962306a36Sopenharmony_ci	cardvcc_write(lvcc, endptr, vcc_rxreadptr);
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic void vcc_rx_aal0(struct lanai_dev *lanai)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n");
142562306a36Sopenharmony_ci	/* Remember to get read_lock(&vcc_sklist_lock) while looking up VC */
142662306a36Sopenharmony_ci	/* Remember to increment lvcc->rx.atmvcc->stats->rx */
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci/* -------------------- MANAGING HOST-BASED VCC TABLE: */
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci/* Decide whether to use vmalloc or get_zeroed_page for VCC table */
143262306a36Sopenharmony_ci#if (NUM_VCI * BITS_PER_LONG) <= PAGE_SIZE
143362306a36Sopenharmony_ci#define VCCTABLE_GETFREEPAGE
143462306a36Sopenharmony_ci#else
143562306a36Sopenharmony_ci#include <linux/vmalloc.h>
143662306a36Sopenharmony_ci#endif
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_cistatic int vcc_table_allocate(struct lanai_dev *lanai)
143962306a36Sopenharmony_ci{
144062306a36Sopenharmony_ci#ifdef VCCTABLE_GETFREEPAGE
144162306a36Sopenharmony_ci	APRINTK((lanai->num_vci) * sizeof(struct lanai_vcc *) <= PAGE_SIZE,
144262306a36Sopenharmony_ci	    "vcc table > PAGE_SIZE!");
144362306a36Sopenharmony_ci	lanai->vccs = (struct lanai_vcc **) get_zeroed_page(GFP_KERNEL);
144462306a36Sopenharmony_ci	return (lanai->vccs == NULL) ? -ENOMEM : 0;
144562306a36Sopenharmony_ci#else
144662306a36Sopenharmony_ci	int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *);
144762306a36Sopenharmony_ci	lanai->vccs = vzalloc(bytes);
144862306a36Sopenharmony_ci	if (unlikely(lanai->vccs == NULL))
144962306a36Sopenharmony_ci		return -ENOMEM;
145062306a36Sopenharmony_ci	return 0;
145162306a36Sopenharmony_ci#endif
145262306a36Sopenharmony_ci}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_cistatic inline void vcc_table_deallocate(const struct lanai_dev *lanai)
145562306a36Sopenharmony_ci{
145662306a36Sopenharmony_ci#ifdef VCCTABLE_GETFREEPAGE
145762306a36Sopenharmony_ci	free_page((unsigned long) lanai->vccs);
145862306a36Sopenharmony_ci#else
145962306a36Sopenharmony_ci	vfree(lanai->vccs);
146062306a36Sopenharmony_ci#endif
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci/* Allocate a fresh lanai_vcc, with the appropriate things cleared */
146462306a36Sopenharmony_cistatic inline struct lanai_vcc *new_lanai_vcc(void)
146562306a36Sopenharmony_ci{
146662306a36Sopenharmony_ci	struct lanai_vcc *lvcc;
146762306a36Sopenharmony_ci	lvcc =  kzalloc(sizeof(*lvcc), GFP_KERNEL);
146862306a36Sopenharmony_ci	if (likely(lvcc != NULL)) {
146962306a36Sopenharmony_ci		skb_queue_head_init(&lvcc->tx.backlog);
147062306a36Sopenharmony_ci#ifdef DEBUG
147162306a36Sopenharmony_ci		lvcc->vci = -1;
147262306a36Sopenharmony_ci#endif
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci	return lvcc;
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cistatic int lanai_get_sized_buffer(struct lanai_dev *lanai,
147862306a36Sopenharmony_ci	struct lanai_buffer *buf, int max_sdu, int multiplier,
147962306a36Sopenharmony_ci	const char *name)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	int size;
148262306a36Sopenharmony_ci	if (unlikely(max_sdu < 1))
148362306a36Sopenharmony_ci		max_sdu = 1;
148462306a36Sopenharmony_ci	max_sdu = aal5_size(max_sdu);
148562306a36Sopenharmony_ci	size = (max_sdu + 16) * multiplier + 16;
148662306a36Sopenharmony_ci	lanai_buf_allocate(buf, size, max_sdu + 32, lanai->pci);
148762306a36Sopenharmony_ci	if (unlikely(buf->start == NULL))
148862306a36Sopenharmony_ci		return -ENOMEM;
148962306a36Sopenharmony_ci	if (unlikely(lanai_buf_size(buf) < size))
149062306a36Sopenharmony_ci		printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes "
149162306a36Sopenharmony_ci		    "for %s buffer, got only %zu\n", lanai->number, size,
149262306a36Sopenharmony_ci		    name, lanai_buf_size(buf));
149362306a36Sopenharmony_ci	DPRINTK("Allocated %zu byte %s buffer\n", lanai_buf_size(buf), name);
149462306a36Sopenharmony_ci	return 0;
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci/* Setup a RX buffer for a currently unbound AAL5 vci */
149862306a36Sopenharmony_cistatic inline int lanai_setup_rx_vci_aal5(struct lanai_dev *lanai,
149962306a36Sopenharmony_ci	struct lanai_vcc *lvcc, const struct atm_qos *qos)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	return lanai_get_sized_buffer(lanai, &lvcc->rx.buf,
150262306a36Sopenharmony_ci	    qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, "RX");
150362306a36Sopenharmony_ci}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci/* Setup a TX buffer for a currently unbound AAL5 vci */
150662306a36Sopenharmony_cistatic int lanai_setup_tx_vci(struct lanai_dev *lanai, struct lanai_vcc *lvcc,
150762306a36Sopenharmony_ci	const struct atm_qos *qos)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	int max_sdu, multiplier;
151062306a36Sopenharmony_ci	if (qos->aal == ATM_AAL0) {
151162306a36Sopenharmony_ci		lvcc->tx.unqueue = vcc_tx_unqueue_aal0;
151262306a36Sopenharmony_ci		max_sdu = ATM_CELL_SIZE - 1;
151362306a36Sopenharmony_ci		multiplier = AAL0_TX_MULTIPLIER;
151462306a36Sopenharmony_ci	} else {
151562306a36Sopenharmony_ci		lvcc->tx.unqueue = vcc_tx_unqueue_aal5;
151662306a36Sopenharmony_ci		max_sdu = qos->txtp.max_sdu;
151762306a36Sopenharmony_ci		multiplier = AAL5_TX_MULTIPLIER;
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci	return lanai_get_sized_buffer(lanai, &lvcc->tx.buf, max_sdu,
152062306a36Sopenharmony_ci	    multiplier, "TX");
152162306a36Sopenharmony_ci}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_cistatic inline void host_vcc_bind(struct lanai_dev *lanai,
152462306a36Sopenharmony_ci	struct lanai_vcc *lvcc, vci_t vci)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	if (lvcc->vbase != NULL)
152762306a36Sopenharmony_ci		return;    /* We already were bound in the other direction */
152862306a36Sopenharmony_ci	DPRINTK("Binding vci %d\n", vci);
152962306a36Sopenharmony_ci#ifdef USE_POWERDOWN
153062306a36Sopenharmony_ci	if (lanai->nbound++ == 0) {
153162306a36Sopenharmony_ci		DPRINTK("Coming out of powerdown\n");
153262306a36Sopenharmony_ci		lanai->conf1 &= ~CONFIG1_POWERDOWN;
153362306a36Sopenharmony_ci		conf1_write(lanai);
153462306a36Sopenharmony_ci		conf2_write(lanai);
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci#endif
153762306a36Sopenharmony_ci	lvcc->vbase = cardvcc_addr(lanai, vci);
153862306a36Sopenharmony_ci	lanai->vccs[lvcc->vci = vci] = lvcc;
153962306a36Sopenharmony_ci}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_cistatic inline void host_vcc_unbind(struct lanai_dev *lanai,
154262306a36Sopenharmony_ci	struct lanai_vcc *lvcc)
154362306a36Sopenharmony_ci{
154462306a36Sopenharmony_ci	if (lvcc->vbase == NULL)
154562306a36Sopenharmony_ci		return;	/* This vcc was never bound */
154662306a36Sopenharmony_ci	DPRINTK("Unbinding vci %d\n", lvcc->vci);
154762306a36Sopenharmony_ci	lvcc->vbase = NULL;
154862306a36Sopenharmony_ci	lanai->vccs[lvcc->vci] = NULL;
154962306a36Sopenharmony_ci#ifdef USE_POWERDOWN
155062306a36Sopenharmony_ci	if (--lanai->nbound == 0) {
155162306a36Sopenharmony_ci		DPRINTK("Going into powerdown\n");
155262306a36Sopenharmony_ci		lanai->conf1 |= CONFIG1_POWERDOWN;
155362306a36Sopenharmony_ci		conf1_write(lanai);
155462306a36Sopenharmony_ci	}
155562306a36Sopenharmony_ci#endif
155662306a36Sopenharmony_ci}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci/* -------------------- RESET CARD: */
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_cistatic void lanai_reset(struct lanai_dev *lanai)
156162306a36Sopenharmony_ci{
156262306a36Sopenharmony_ci	printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* resetting - not "
156362306a36Sopenharmony_ci	    "implemented\n", lanai->number);
156462306a36Sopenharmony_ci	/* TODO */
156562306a36Sopenharmony_ci	/* The following is just a hack until we write the real
156662306a36Sopenharmony_ci	 * resetter - at least ack whatever interrupt sent us
156762306a36Sopenharmony_ci	 * here
156862306a36Sopenharmony_ci	 */
156962306a36Sopenharmony_ci	reg_write(lanai, INT_ALL, IntAck_Reg);
157062306a36Sopenharmony_ci	lanai->stats.card_reset++;
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci/* -------------------- SERVICE LIST UTILITIES: */
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci/*
157662306a36Sopenharmony_ci * Allocate service buffer and tell card about it
157762306a36Sopenharmony_ci */
157862306a36Sopenharmony_cistatic int service_buffer_allocate(struct lanai_dev *lanai)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 8,
158162306a36Sopenharmony_ci	    lanai->pci);
158262306a36Sopenharmony_ci	if (unlikely(lanai->service.start == NULL))
158362306a36Sopenharmony_ci		return -ENOMEM;
158462306a36Sopenharmony_ci	DPRINTK("allocated service buffer at %p, size %zu(%d)\n",
158562306a36Sopenharmony_ci	    lanai->service.start,
158662306a36Sopenharmony_ci	    lanai_buf_size(&lanai->service),
158762306a36Sopenharmony_ci	    lanai_buf_size_cardorder(&lanai->service));
158862306a36Sopenharmony_ci	/* Clear ServWrite register to be safe */
158962306a36Sopenharmony_ci	reg_write(lanai, 0, ServWrite_Reg);
159062306a36Sopenharmony_ci	/* ServiceStuff register contains size and address of buffer */
159162306a36Sopenharmony_ci	reg_write(lanai,
159262306a36Sopenharmony_ci	    SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) |
159362306a36Sopenharmony_ci	    SSTUFF_SET_ADDR(lanai->service.dmaaddr),
159462306a36Sopenharmony_ci	    ServiceStuff_Reg);
159562306a36Sopenharmony_ci	return 0;
159662306a36Sopenharmony_ci}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_cistatic inline void service_buffer_deallocate(struct lanai_dev *lanai)
159962306a36Sopenharmony_ci{
160062306a36Sopenharmony_ci	lanai_buf_deallocate(&lanai->service, lanai->pci);
160162306a36Sopenharmony_ci}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci/* Bitfields in service list */
160462306a36Sopenharmony_ci#define SERVICE_TX	(0x80000000)	/* Was from transmission */
160562306a36Sopenharmony_ci#define SERVICE_TRASH	(0x40000000)	/* RXed PDU was trashed */
160662306a36Sopenharmony_ci#define SERVICE_CRCERR	(0x20000000)	/* RXed PDU had CRC error */
160762306a36Sopenharmony_ci#define SERVICE_CI	(0x10000000)	/* RXed PDU had CI set */
160862306a36Sopenharmony_ci#define SERVICE_CLP	(0x08000000)	/* RXed PDU had CLP set */
160962306a36Sopenharmony_ci#define SERVICE_STREAM	(0x04000000)	/* RX Stream mode */
161062306a36Sopenharmony_ci#define SERVICE_GET_VCI(x) (((x)>>16)&0x3FF)
161162306a36Sopenharmony_ci#define SERVICE_GET_END(x) ((x)&0x1FFF)
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci/* Handle one thing from the service list - returns true if it marked a
161462306a36Sopenharmony_ci * VCC ready for xmit
161562306a36Sopenharmony_ci */
161662306a36Sopenharmony_cistatic int handle_service(struct lanai_dev *lanai, u32 s)
161762306a36Sopenharmony_ci{
161862306a36Sopenharmony_ci	vci_t vci = SERVICE_GET_VCI(s);
161962306a36Sopenharmony_ci	struct lanai_vcc *lvcc;
162062306a36Sopenharmony_ci	read_lock(&vcc_sklist_lock);
162162306a36Sopenharmony_ci	lvcc = lanai->vccs[vci];
162262306a36Sopenharmony_ci	if (unlikely(lvcc == NULL)) {
162362306a36Sopenharmony_ci		read_unlock(&vcc_sklist_lock);
162462306a36Sopenharmony_ci		DPRINTK("(itf %d) got service entry 0x%X for nonexistent "
162562306a36Sopenharmony_ci		    "vcc %d\n", lanai->number, (unsigned int) s, vci);
162662306a36Sopenharmony_ci		if (s & SERVICE_TX)
162762306a36Sopenharmony_ci			lanai->stats.service_notx++;
162862306a36Sopenharmony_ci		else
162962306a36Sopenharmony_ci			lanai->stats.service_norx++;
163062306a36Sopenharmony_ci		return 0;
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci	if (s & SERVICE_TX) {			/* segmentation interrupt */
163362306a36Sopenharmony_ci		if (unlikely(lvcc->tx.atmvcc == NULL)) {
163462306a36Sopenharmony_ci			read_unlock(&vcc_sklist_lock);
163562306a36Sopenharmony_ci			DPRINTK("(itf %d) got service entry 0x%X for non-TX "
163662306a36Sopenharmony_ci			    "vcc %d\n", lanai->number, (unsigned int) s, vci);
163762306a36Sopenharmony_ci			lanai->stats.service_notx++;
163862306a36Sopenharmony_ci			return 0;
163962306a36Sopenharmony_ci		}
164062306a36Sopenharmony_ci		__set_bit(vci, lanai->transmit_ready);
164162306a36Sopenharmony_ci		lvcc->tx.endptr = SERVICE_GET_END(s);
164262306a36Sopenharmony_ci		read_unlock(&vcc_sklist_lock);
164362306a36Sopenharmony_ci		return 1;
164462306a36Sopenharmony_ci	}
164562306a36Sopenharmony_ci	if (unlikely(lvcc->rx.atmvcc == NULL)) {
164662306a36Sopenharmony_ci		read_unlock(&vcc_sklist_lock);
164762306a36Sopenharmony_ci		DPRINTK("(itf %d) got service entry 0x%X for non-RX "
164862306a36Sopenharmony_ci		    "vcc %d\n", lanai->number, (unsigned int) s, vci);
164962306a36Sopenharmony_ci		lanai->stats.service_norx++;
165062306a36Sopenharmony_ci		return 0;
165162306a36Sopenharmony_ci	}
165262306a36Sopenharmony_ci	if (unlikely(lvcc->rx.atmvcc->qos.aal != ATM_AAL5)) {
165362306a36Sopenharmony_ci		read_unlock(&vcc_sklist_lock);
165462306a36Sopenharmony_ci		DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 "
165562306a36Sopenharmony_ci		    "vcc %d\n", lanai->number, (unsigned int) s, vci);
165662306a36Sopenharmony_ci		lanai->stats.service_rxnotaal5++;
165762306a36Sopenharmony_ci		atomic_inc(&lvcc->rx.atmvcc->stats->rx_err);
165862306a36Sopenharmony_ci		return 0;
165962306a36Sopenharmony_ci	}
166062306a36Sopenharmony_ci	if (likely(!(s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)))) {
166162306a36Sopenharmony_ci		vcc_rx_aal5(lvcc, SERVICE_GET_END(s));
166262306a36Sopenharmony_ci		read_unlock(&vcc_sklist_lock);
166362306a36Sopenharmony_ci		return 0;
166462306a36Sopenharmony_ci	}
166562306a36Sopenharmony_ci	if (s & SERVICE_TRASH) {
166662306a36Sopenharmony_ci		int bytes;
166762306a36Sopenharmony_ci		read_unlock(&vcc_sklist_lock);
166862306a36Sopenharmony_ci		DPRINTK("got trashed rx pdu on vci %d\n", vci);
166962306a36Sopenharmony_ci		atomic_inc(&lvcc->rx.atmvcc->stats->rx_err);
167062306a36Sopenharmony_ci		lvcc->stats.x.aal5.service_trash++;
167162306a36Sopenharmony_ci		bytes = (SERVICE_GET_END(s) * 16) -
167262306a36Sopenharmony_ci		    (((unsigned long) lvcc->rx.buf.ptr) -
167362306a36Sopenharmony_ci		    ((unsigned long) lvcc->rx.buf.start)) + 47;
167462306a36Sopenharmony_ci		if (bytes < 0)
167562306a36Sopenharmony_ci			bytes += lanai_buf_size(&lvcc->rx.buf);
167662306a36Sopenharmony_ci		lanai->stats.ovfl_trash += (bytes / 48);
167762306a36Sopenharmony_ci		return 0;
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci	if (s & SERVICE_STREAM) {
168062306a36Sopenharmony_ci		read_unlock(&vcc_sklist_lock);
168162306a36Sopenharmony_ci		atomic_inc(&lvcc->rx.atmvcc->stats->rx_err);
168262306a36Sopenharmony_ci		lvcc->stats.x.aal5.service_stream++;
168362306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream "
168462306a36Sopenharmony_ci		    "PDU on VCI %d!\n", lanai->number, vci);
168562306a36Sopenharmony_ci		lanai_reset(lanai);
168662306a36Sopenharmony_ci		return 0;
168762306a36Sopenharmony_ci	}
168862306a36Sopenharmony_ci	DPRINTK("got rx crc error on vci %d\n", vci);
168962306a36Sopenharmony_ci	atomic_inc(&lvcc->rx.atmvcc->stats->rx_err);
169062306a36Sopenharmony_ci	lvcc->stats.x.aal5.service_rxcrc++;
169162306a36Sopenharmony_ci	lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4];
169262306a36Sopenharmony_ci	cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr);
169362306a36Sopenharmony_ci	read_unlock(&vcc_sklist_lock);
169462306a36Sopenharmony_ci	return 0;
169562306a36Sopenharmony_ci}
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci/* Try transmitting on all VCIs that we marked ready to serve */
169862306a36Sopenharmony_cistatic void iter_transmit(struct lanai_dev *lanai, vci_t vci)
169962306a36Sopenharmony_ci{
170062306a36Sopenharmony_ci	struct lanai_vcc *lvcc = lanai->vccs[vci];
170162306a36Sopenharmony_ci	if (vcc_is_backlogged(lvcc))
170262306a36Sopenharmony_ci		lvcc->tx.unqueue(lanai, lvcc, lvcc->tx.endptr);
170362306a36Sopenharmony_ci}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci/* Run service queue -- called from interrupt context or with
170662306a36Sopenharmony_ci * interrupts otherwise disabled and with the lanai->servicelock
170762306a36Sopenharmony_ci * lock held
170862306a36Sopenharmony_ci */
170962306a36Sopenharmony_cistatic void run_service(struct lanai_dev *lanai)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	int ntx = 0;
171262306a36Sopenharmony_ci	u32 wreg = reg_read(lanai, ServWrite_Reg);
171362306a36Sopenharmony_ci	const u32 *end = lanai->service.start + wreg;
171462306a36Sopenharmony_ci	while (lanai->service.ptr != end) {
171562306a36Sopenharmony_ci		ntx += handle_service(lanai,
171662306a36Sopenharmony_ci		    le32_to_cpup(lanai->service.ptr++));
171762306a36Sopenharmony_ci		if (lanai->service.ptr >= lanai->service.end)
171862306a36Sopenharmony_ci			lanai->service.ptr = lanai->service.start;
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci	reg_write(lanai, wreg, ServRead_Reg);
172162306a36Sopenharmony_ci	if (ntx != 0) {
172262306a36Sopenharmony_ci		read_lock(&vcc_sklist_lock);
172362306a36Sopenharmony_ci		vci_bitfield_iterate(lanai, lanai->transmit_ready,
172462306a36Sopenharmony_ci		    iter_transmit);
172562306a36Sopenharmony_ci		bitmap_zero(lanai->transmit_ready, NUM_VCI);
172662306a36Sopenharmony_ci		read_unlock(&vcc_sklist_lock);
172762306a36Sopenharmony_ci	}
172862306a36Sopenharmony_ci}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci/* -------------------- GATHER STATISTICS: */
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cistatic void get_statistics(struct lanai_dev *lanai)
173362306a36Sopenharmony_ci{
173462306a36Sopenharmony_ci	u32 statreg = reg_read(lanai, Statistics_Reg);
173562306a36Sopenharmony_ci	lanai->stats.atm_ovfl += STATS_GET_FIFO_OVFL(statreg);
173662306a36Sopenharmony_ci	lanai->stats.hec_err += STATS_GET_HEC_ERR(statreg);
173762306a36Sopenharmony_ci	lanai->stats.vci_trash += STATS_GET_BAD_VCI(statreg);
173862306a36Sopenharmony_ci	lanai->stats.ovfl_trash += STATS_GET_BUF_OVFL(statreg);
173962306a36Sopenharmony_ci}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci/* -------------------- POLLING TIMER: */
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci#ifndef DEBUG_RW
174462306a36Sopenharmony_ci/* Try to undequeue 1 backlogged vcc */
174562306a36Sopenharmony_cistatic void iter_dequeue(struct lanai_dev *lanai, vci_t vci)
174662306a36Sopenharmony_ci{
174762306a36Sopenharmony_ci	struct lanai_vcc *lvcc = lanai->vccs[vci];
174862306a36Sopenharmony_ci	int endptr;
174962306a36Sopenharmony_ci	if (lvcc == NULL || lvcc->tx.atmvcc == NULL ||
175062306a36Sopenharmony_ci	    !vcc_is_backlogged(lvcc)) {
175162306a36Sopenharmony_ci		__clear_bit(vci, lanai->backlog_vccs);
175262306a36Sopenharmony_ci		return;
175362306a36Sopenharmony_ci	}
175462306a36Sopenharmony_ci	endptr = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr));
175562306a36Sopenharmony_ci	lvcc->tx.unqueue(lanai, lvcc, endptr);
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci#endif /* !DEBUG_RW */
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic void lanai_timed_poll(struct timer_list *t)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	struct lanai_dev *lanai = from_timer(lanai, t, timer);
176262306a36Sopenharmony_ci#ifndef DEBUG_RW
176362306a36Sopenharmony_ci	unsigned long flags;
176462306a36Sopenharmony_ci#ifdef USE_POWERDOWN
176562306a36Sopenharmony_ci	if (lanai->conf1 & CONFIG1_POWERDOWN)
176662306a36Sopenharmony_ci		return;
176762306a36Sopenharmony_ci#endif /* USE_POWERDOWN */
176862306a36Sopenharmony_ci	local_irq_save(flags);
176962306a36Sopenharmony_ci	/* If we can grab the spinlock, check if any services need to be run */
177062306a36Sopenharmony_ci	if (spin_trylock(&lanai->servicelock)) {
177162306a36Sopenharmony_ci		run_service(lanai);
177262306a36Sopenharmony_ci		spin_unlock(&lanai->servicelock);
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci	/* ...and see if any backlogged VCs can make progress */
177562306a36Sopenharmony_ci	/* unfortunately linux has no read_trylock() currently */
177662306a36Sopenharmony_ci	read_lock(&vcc_sklist_lock);
177762306a36Sopenharmony_ci	vci_bitfield_iterate(lanai, lanai->backlog_vccs, iter_dequeue);
177862306a36Sopenharmony_ci	read_unlock(&vcc_sklist_lock);
177962306a36Sopenharmony_ci	local_irq_restore(flags);
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	get_statistics(lanai);
178262306a36Sopenharmony_ci#endif /* !DEBUG_RW */
178362306a36Sopenharmony_ci	mod_timer(&lanai->timer, jiffies + LANAI_POLL_PERIOD);
178462306a36Sopenharmony_ci}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_cistatic inline void lanai_timed_poll_start(struct lanai_dev *lanai)
178762306a36Sopenharmony_ci{
178862306a36Sopenharmony_ci	timer_setup(&lanai->timer, lanai_timed_poll, 0);
178962306a36Sopenharmony_ci	lanai->timer.expires = jiffies + LANAI_POLL_PERIOD;
179062306a36Sopenharmony_ci	add_timer(&lanai->timer);
179162306a36Sopenharmony_ci}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_cistatic inline void lanai_timed_poll_stop(struct lanai_dev *lanai)
179462306a36Sopenharmony_ci{
179562306a36Sopenharmony_ci	del_timer_sync(&lanai->timer);
179662306a36Sopenharmony_ci}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci/* -------------------- INTERRUPT SERVICE: */
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cistatic inline void lanai_int_1(struct lanai_dev *lanai, u32 reason)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	u32 ack = 0;
180362306a36Sopenharmony_ci	if (reason & INT_SERVICE) {
180462306a36Sopenharmony_ci		ack = INT_SERVICE;
180562306a36Sopenharmony_ci		spin_lock(&lanai->servicelock);
180662306a36Sopenharmony_ci		run_service(lanai);
180762306a36Sopenharmony_ci		spin_unlock(&lanai->servicelock);
180862306a36Sopenharmony_ci	}
180962306a36Sopenharmony_ci	if (reason & (INT_AAL0_STR | INT_AAL0)) {
181062306a36Sopenharmony_ci		ack |= reason & (INT_AAL0_STR | INT_AAL0);
181162306a36Sopenharmony_ci		vcc_rx_aal0(lanai);
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_ci	/* The rest of the interrupts are pretty rare */
181462306a36Sopenharmony_ci	if (ack == reason)
181562306a36Sopenharmony_ci		goto done;
181662306a36Sopenharmony_ci	if (reason & INT_STATS) {
181762306a36Sopenharmony_ci		reason &= ~INT_STATS;	/* No need to ack */
181862306a36Sopenharmony_ci		get_statistics(lanai);
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci	if (reason & INT_STATUS) {
182162306a36Sopenharmony_ci		ack |= reason & INT_STATUS;
182262306a36Sopenharmony_ci		lanai_check_status(lanai);
182362306a36Sopenharmony_ci	}
182462306a36Sopenharmony_ci	if (unlikely(reason & INT_DMASHUT)) {
182562306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): driver error - DMA "
182662306a36Sopenharmony_ci		    "shutdown, reason=0x%08X, address=0x%08X\n",
182762306a36Sopenharmony_ci		    lanai->number, (unsigned int) (reason & INT_DMASHUT),
182862306a36Sopenharmony_ci		    (unsigned int) reg_read(lanai, DMA_Addr_Reg));
182962306a36Sopenharmony_ci		if (reason & INT_TABORTBM) {
183062306a36Sopenharmony_ci			lanai_reset(lanai);
183162306a36Sopenharmony_ci			return;
183262306a36Sopenharmony_ci		}
183362306a36Sopenharmony_ci		ack |= (reason & INT_DMASHUT);
183462306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): re-enabling DMA\n",
183562306a36Sopenharmony_ci		    lanai->number);
183662306a36Sopenharmony_ci		conf1_write(lanai);
183762306a36Sopenharmony_ci		lanai->stats.dma_reenable++;
183862306a36Sopenharmony_ci		pcistatus_check(lanai, 0);
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci	if (unlikely(reason & INT_TABORTSENT)) {
184162306a36Sopenharmony_ci		ack |= (reason & INT_TABORTSENT);
184262306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): sent PCI target abort\n",
184362306a36Sopenharmony_ci		    lanai->number);
184462306a36Sopenharmony_ci		pcistatus_check(lanai, 0);
184562306a36Sopenharmony_ci	}
184662306a36Sopenharmony_ci	if (unlikely(reason & INT_SEGSHUT)) {
184762306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): driver error - "
184862306a36Sopenharmony_ci		    "segmentation shutdown, reason=0x%08X\n", lanai->number,
184962306a36Sopenharmony_ci		    (unsigned int) (reason & INT_SEGSHUT));
185062306a36Sopenharmony_ci		lanai_reset(lanai);
185162306a36Sopenharmony_ci		return;
185262306a36Sopenharmony_ci	}
185362306a36Sopenharmony_ci	if (unlikely(reason & (INT_PING | INT_WAKE))) {
185462306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): driver error - "
185562306a36Sopenharmony_ci		    "unexpected interrupt 0x%08X, resetting\n",
185662306a36Sopenharmony_ci		    lanai->number,
185762306a36Sopenharmony_ci		    (unsigned int) (reason & (INT_PING | INT_WAKE)));
185862306a36Sopenharmony_ci		lanai_reset(lanai);
185962306a36Sopenharmony_ci		return;
186062306a36Sopenharmony_ci	}
186162306a36Sopenharmony_ci#ifdef DEBUG
186262306a36Sopenharmony_ci	if (unlikely(ack != reason)) {
186362306a36Sopenharmony_ci		DPRINTK("unacked ints: 0x%08X\n",
186462306a36Sopenharmony_ci		    (unsigned int) (reason & ~ack));
186562306a36Sopenharmony_ci		ack = reason;
186662306a36Sopenharmony_ci	}
186762306a36Sopenharmony_ci#endif
186862306a36Sopenharmony_ci   done:
186962306a36Sopenharmony_ci	if (ack != 0)
187062306a36Sopenharmony_ci		reg_write(lanai, ack, IntAck_Reg);
187162306a36Sopenharmony_ci}
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_cistatic irqreturn_t lanai_int(int irq, void *devid)
187462306a36Sopenharmony_ci{
187562306a36Sopenharmony_ci	struct lanai_dev *lanai = devid;
187662306a36Sopenharmony_ci	u32 reason;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci#ifdef USE_POWERDOWN
187962306a36Sopenharmony_ci	/*
188062306a36Sopenharmony_ci	 * If we're powered down we shouldn't be generating any interrupts -
188162306a36Sopenharmony_ci	 * so assume that this is a shared interrupt line and it's for someone
188262306a36Sopenharmony_ci	 * else
188362306a36Sopenharmony_ci	 */
188462306a36Sopenharmony_ci	if (unlikely(lanai->conf1 & CONFIG1_POWERDOWN))
188562306a36Sopenharmony_ci		return IRQ_NONE;
188662306a36Sopenharmony_ci#endif
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	reason = intr_pending(lanai);
188962306a36Sopenharmony_ci	if (reason == 0)
189062306a36Sopenharmony_ci		return IRQ_NONE;	/* Must be for someone else */
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	do {
189362306a36Sopenharmony_ci		if (unlikely(reason == 0xFFFFFFFF))
189462306a36Sopenharmony_ci			break;		/* Maybe we've been unplugged? */
189562306a36Sopenharmony_ci		lanai_int_1(lanai, reason);
189662306a36Sopenharmony_ci		reason = intr_pending(lanai);
189762306a36Sopenharmony_ci	} while (reason != 0);
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	return IRQ_HANDLED;
190062306a36Sopenharmony_ci}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci/* TODO - it would be nice if we could use the "delayed interrupt" system
190362306a36Sopenharmony_ci *   to some advantage
190462306a36Sopenharmony_ci */
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci/* -------------------- CHECK BOARD ID/REV: */
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci/*
190962306a36Sopenharmony_ci * The board id and revision are stored both in the reset register and
191062306a36Sopenharmony_ci * in the PCI configuration space - the documentation says to check
191162306a36Sopenharmony_ci * each of them.  If revp!=NULL we store the revision there
191262306a36Sopenharmony_ci */
191362306a36Sopenharmony_cistatic int check_board_id_and_rev(const char *name, u32 val, int *revp)
191462306a36Sopenharmony_ci{
191562306a36Sopenharmony_ci	DPRINTK("%s says board_id=%d, board_rev=%d\n", name,
191662306a36Sopenharmony_ci		(int) RESET_GET_BOARD_ID(val),
191762306a36Sopenharmony_ci		(int) RESET_GET_BOARD_REV(val));
191862306a36Sopenharmony_ci	if (RESET_GET_BOARD_ID(val) != BOARD_ID_LANAI256) {
191962306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL ": Found %s board-id %d -- not a "
192062306a36Sopenharmony_ci		    "Lanai 25.6\n", name, (int) RESET_GET_BOARD_ID(val));
192162306a36Sopenharmony_ci		return -ENODEV;
192262306a36Sopenharmony_ci	}
192362306a36Sopenharmony_ci	if (revp != NULL)
192462306a36Sopenharmony_ci		*revp = RESET_GET_BOARD_REV(val);
192562306a36Sopenharmony_ci	return 0;
192662306a36Sopenharmony_ci}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci/* -------------------- PCI INITIALIZATION/SHUTDOWN: */
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_cistatic int lanai_pci_start(struct lanai_dev *lanai)
193162306a36Sopenharmony_ci{
193262306a36Sopenharmony_ci	struct pci_dev *pci = lanai->pci;
193362306a36Sopenharmony_ci	int result;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	if (pci_enable_device(pci) != 0) {
193662306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable "
193762306a36Sopenharmony_ci		    "PCI device", lanai->number);
193862306a36Sopenharmony_ci		return -ENXIO;
193962306a36Sopenharmony_ci	}
194062306a36Sopenharmony_ci	pci_set_master(pci);
194162306a36Sopenharmony_ci	if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)) != 0) {
194262306a36Sopenharmony_ci		printk(KERN_WARNING DEV_LABEL
194362306a36Sopenharmony_ci		    "(itf %d): No suitable DMA available.\n", lanai->number);
194462306a36Sopenharmony_ci		return -EBUSY;
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci	result = check_board_id_and_rev("PCI", pci->subsystem_device, NULL);
194762306a36Sopenharmony_ci	if (result != 0)
194862306a36Sopenharmony_ci		return result;
194962306a36Sopenharmony_ci	/* Set latency timer to zero as per lanai docs */
195062306a36Sopenharmony_ci	result = pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0);
195162306a36Sopenharmony_ci	if (result != PCIBIOS_SUCCESSFUL) {
195262306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): can't write "
195362306a36Sopenharmony_ci		    "PCI_LATENCY_TIMER: %d\n", lanai->number, result);
195462306a36Sopenharmony_ci		return -EINVAL;
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci	pcistatus_check(lanai, 1);
195762306a36Sopenharmony_ci	pcistatus_check(lanai, 0);
195862306a36Sopenharmony_ci	return 0;
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci/* -------------------- VPI/VCI ALLOCATION: */
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci/*
196462306a36Sopenharmony_ci * We _can_ use VCI==0 for normal traffic, but only for UBR (or we'll
196562306a36Sopenharmony_ci * get a CBRZERO interrupt), and we can use it only if no one is receiving
196662306a36Sopenharmony_ci * AAL0 traffic (since they will use the same queue) - according to the
196762306a36Sopenharmony_ci * docs we shouldn't even use it for AAL0 traffic
196862306a36Sopenharmony_ci */
196962306a36Sopenharmony_cistatic inline int vci0_is_ok(struct lanai_dev *lanai,
197062306a36Sopenharmony_ci	const struct atm_qos *qos)
197162306a36Sopenharmony_ci{
197262306a36Sopenharmony_ci	if (qos->txtp.traffic_class == ATM_CBR || qos->aal == ATM_AAL0)
197362306a36Sopenharmony_ci		return 0;
197462306a36Sopenharmony_ci	if (qos->rxtp.traffic_class != ATM_NONE) {
197562306a36Sopenharmony_ci		if (lanai->naal0 != 0)
197662306a36Sopenharmony_ci			return 0;
197762306a36Sopenharmony_ci		lanai->conf2 |= CONFIG2_VCI0_NORMAL;
197862306a36Sopenharmony_ci		conf2_write_if_powerup(lanai);
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci	return 1;
198162306a36Sopenharmony_ci}
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci/* return true if vci is currently unused, or if requested qos is
198462306a36Sopenharmony_ci * compatible
198562306a36Sopenharmony_ci */
198662306a36Sopenharmony_cistatic int vci_is_ok(struct lanai_dev *lanai, vci_t vci,
198762306a36Sopenharmony_ci	const struct atm_vcc *atmvcc)
198862306a36Sopenharmony_ci{
198962306a36Sopenharmony_ci	const struct atm_qos *qos = &atmvcc->qos;
199062306a36Sopenharmony_ci	const struct lanai_vcc *lvcc = lanai->vccs[vci];
199162306a36Sopenharmony_ci	if (vci == 0 && !vci0_is_ok(lanai, qos))
199262306a36Sopenharmony_ci		return 0;
199362306a36Sopenharmony_ci	if (unlikely(lvcc != NULL)) {
199462306a36Sopenharmony_ci		if (qos->rxtp.traffic_class != ATM_NONE &&
199562306a36Sopenharmony_ci		    lvcc->rx.atmvcc != NULL && lvcc->rx.atmvcc != atmvcc)
199662306a36Sopenharmony_ci			return 0;
199762306a36Sopenharmony_ci		if (qos->txtp.traffic_class != ATM_NONE &&
199862306a36Sopenharmony_ci		    lvcc->tx.atmvcc != NULL && lvcc->tx.atmvcc != atmvcc)
199962306a36Sopenharmony_ci			return 0;
200062306a36Sopenharmony_ci		if (qos->txtp.traffic_class == ATM_CBR &&
200162306a36Sopenharmony_ci		    lanai->cbrvcc != NULL && lanai->cbrvcc != atmvcc)
200262306a36Sopenharmony_ci			return 0;
200362306a36Sopenharmony_ci	}
200462306a36Sopenharmony_ci	if (qos->aal == ATM_AAL0 && lanai->naal0 == 0 &&
200562306a36Sopenharmony_ci	    qos->rxtp.traffic_class != ATM_NONE) {
200662306a36Sopenharmony_ci		const struct lanai_vcc *vci0 = lanai->vccs[0];
200762306a36Sopenharmony_ci		if (vci0 != NULL && vci0->rx.atmvcc != NULL)
200862306a36Sopenharmony_ci			return 0;
200962306a36Sopenharmony_ci		lanai->conf2 &= ~CONFIG2_VCI0_NORMAL;
201062306a36Sopenharmony_ci		conf2_write_if_powerup(lanai);
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci	return 1;
201362306a36Sopenharmony_ci}
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_cistatic int lanai_normalize_ci(struct lanai_dev *lanai,
201662306a36Sopenharmony_ci	const struct atm_vcc *atmvcc, short *vpip, vci_t *vcip)
201762306a36Sopenharmony_ci{
201862306a36Sopenharmony_ci	switch (*vpip) {
201962306a36Sopenharmony_ci		case ATM_VPI_ANY:
202062306a36Sopenharmony_ci			*vpip = 0;
202162306a36Sopenharmony_ci			fallthrough;
202262306a36Sopenharmony_ci		case 0:
202362306a36Sopenharmony_ci			break;
202462306a36Sopenharmony_ci		default:
202562306a36Sopenharmony_ci			return -EADDRINUSE;
202662306a36Sopenharmony_ci	}
202762306a36Sopenharmony_ci	switch (*vcip) {
202862306a36Sopenharmony_ci		case ATM_VCI_ANY:
202962306a36Sopenharmony_ci			for (*vcip = ATM_NOT_RSV_VCI; *vcip < lanai->num_vci;
203062306a36Sopenharmony_ci			    (*vcip)++)
203162306a36Sopenharmony_ci				if (vci_is_ok(lanai, *vcip, atmvcc))
203262306a36Sopenharmony_ci					return 0;
203362306a36Sopenharmony_ci			return -EADDRINUSE;
203462306a36Sopenharmony_ci		default:
203562306a36Sopenharmony_ci			if (*vcip >= lanai->num_vci || *vcip < 0 ||
203662306a36Sopenharmony_ci			    !vci_is_ok(lanai, *vcip, atmvcc))
203762306a36Sopenharmony_ci				return -EADDRINUSE;
203862306a36Sopenharmony_ci	}
203962306a36Sopenharmony_ci	return 0;
204062306a36Sopenharmony_ci}
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci/* -------------------- MANAGE CBR: */
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci/*
204562306a36Sopenharmony_ci * CBR ICG is stored as a fixed-point number with 4 fractional bits.
204662306a36Sopenharmony_ci * Note that storing a number greater than 2046.0 will result in
204762306a36Sopenharmony_ci * incorrect shaping
204862306a36Sopenharmony_ci */
204962306a36Sopenharmony_ci#define CBRICG_FRAC_BITS	(4)
205062306a36Sopenharmony_ci#define CBRICG_MAX		(2046 << CBRICG_FRAC_BITS)
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci/*
205362306a36Sopenharmony_ci * ICG is related to PCR with the formula PCR = MAXPCR / (ICG + 1)
205462306a36Sopenharmony_ci * where MAXPCR is (according to the docs) 25600000/(54*8),
205562306a36Sopenharmony_ci * which is equal to (3125<<9)/27.
205662306a36Sopenharmony_ci *
205762306a36Sopenharmony_ci * Solving for ICG, we get:
205862306a36Sopenharmony_ci *    ICG = MAXPCR/PCR - 1
205962306a36Sopenharmony_ci *    ICG = (3125<<9)/(27*PCR) - 1
206062306a36Sopenharmony_ci *    ICG = ((3125<<9) - (27*PCR)) / (27*PCR)
206162306a36Sopenharmony_ci *
206262306a36Sopenharmony_ci * The end result is supposed to be a fixed-point number with FRAC_BITS
206362306a36Sopenharmony_ci * bits of a fractional part, so we keep everything in the numerator
206462306a36Sopenharmony_ci * shifted by that much as we compute
206562306a36Sopenharmony_ci *
206662306a36Sopenharmony_ci */
206762306a36Sopenharmony_cistatic int pcr_to_cbricg(const struct atm_qos *qos)
206862306a36Sopenharmony_ci{
206962306a36Sopenharmony_ci	int rounddown = 0;	/* 1 = Round PCR down, i.e. round ICG _up_ */
207062306a36Sopenharmony_ci	int x, icg, pcr = atm_pcr_goal(&qos->txtp);
207162306a36Sopenharmony_ci	if (pcr == 0)		/* Use maximum bandwidth */
207262306a36Sopenharmony_ci		return 0;
207362306a36Sopenharmony_ci	if (pcr < 0) {
207462306a36Sopenharmony_ci		rounddown = 1;
207562306a36Sopenharmony_ci		pcr = -pcr;
207662306a36Sopenharmony_ci	}
207762306a36Sopenharmony_ci	x = pcr * 27;
207862306a36Sopenharmony_ci	icg = (3125 << (9 + CBRICG_FRAC_BITS)) - (x << CBRICG_FRAC_BITS);
207962306a36Sopenharmony_ci	if (rounddown)
208062306a36Sopenharmony_ci		icg += x - 1;
208162306a36Sopenharmony_ci	icg /= x;
208262306a36Sopenharmony_ci	if (icg > CBRICG_MAX)
208362306a36Sopenharmony_ci		icg = CBRICG_MAX;
208462306a36Sopenharmony_ci	DPRINTK("pcr_to_cbricg: pcr=%d rounddown=%c icg=%d\n",
208562306a36Sopenharmony_ci	    pcr, rounddown ? 'Y' : 'N', icg);
208662306a36Sopenharmony_ci	return icg;
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_cistatic inline void lanai_cbr_setup(struct lanai_dev *lanai)
209062306a36Sopenharmony_ci{
209162306a36Sopenharmony_ci	reg_write(lanai, pcr_to_cbricg(&lanai->cbrvcc->qos), CBR_ICG_Reg);
209262306a36Sopenharmony_ci	reg_write(lanai, lanai->cbrvcc->vci, CBR_PTR_Reg);
209362306a36Sopenharmony_ci	lanai->conf2 |= CONFIG2_CBR_ENABLE;
209462306a36Sopenharmony_ci	conf2_write(lanai);
209562306a36Sopenharmony_ci}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_cistatic inline void lanai_cbr_shutdown(struct lanai_dev *lanai)
209862306a36Sopenharmony_ci{
209962306a36Sopenharmony_ci	lanai->conf2 &= ~CONFIG2_CBR_ENABLE;
210062306a36Sopenharmony_ci	conf2_write(lanai);
210162306a36Sopenharmony_ci}
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci/* -------------------- OPERATIONS: */
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci/* setup a newly detected device */
210662306a36Sopenharmony_cistatic int lanai_dev_open(struct atm_dev *atmdev)
210762306a36Sopenharmony_ci{
210862306a36Sopenharmony_ci	struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data;
210962306a36Sopenharmony_ci	unsigned long raw_base;
211062306a36Sopenharmony_ci	int result;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	DPRINTK("In lanai_dev_open()\n");
211362306a36Sopenharmony_ci	/* Basic device fields */
211462306a36Sopenharmony_ci	lanai->number = atmdev->number;
211562306a36Sopenharmony_ci	lanai->num_vci = NUM_VCI;
211662306a36Sopenharmony_ci	bitmap_zero(lanai->backlog_vccs, NUM_VCI);
211762306a36Sopenharmony_ci	bitmap_zero(lanai->transmit_ready, NUM_VCI);
211862306a36Sopenharmony_ci	lanai->naal0 = 0;
211962306a36Sopenharmony_ci#ifdef USE_POWERDOWN
212062306a36Sopenharmony_ci	lanai->nbound = 0;
212162306a36Sopenharmony_ci#endif
212262306a36Sopenharmony_ci	lanai->cbrvcc = NULL;
212362306a36Sopenharmony_ci	memset(&lanai->stats, 0, sizeof lanai->stats);
212462306a36Sopenharmony_ci	spin_lock_init(&lanai->endtxlock);
212562306a36Sopenharmony_ci	spin_lock_init(&lanai->servicelock);
212662306a36Sopenharmony_ci	atmdev->ci_range.vpi_bits = 0;
212762306a36Sopenharmony_ci	atmdev->ci_range.vci_bits = 0;
212862306a36Sopenharmony_ci	while (1 << atmdev->ci_range.vci_bits < lanai->num_vci)
212962306a36Sopenharmony_ci		atmdev->ci_range.vci_bits++;
213062306a36Sopenharmony_ci	atmdev->link_rate = ATM_25_PCR;
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	/* 3.2: PCI initialization */
213362306a36Sopenharmony_ci	if ((result = lanai_pci_start(lanai)) != 0)
213462306a36Sopenharmony_ci		goto error;
213562306a36Sopenharmony_ci	raw_base = lanai->pci->resource[0].start;
213662306a36Sopenharmony_ci	lanai->base = (bus_addr_t) ioremap(raw_base, LANAI_MAPPING_SIZE);
213762306a36Sopenharmony_ci	if (lanai->base == NULL) {
213862306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL ": couldn't remap I/O space\n");
213962306a36Sopenharmony_ci		result = -ENOMEM;
214062306a36Sopenharmony_ci		goto error_pci;
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci	/* 3.3: Reset lanai and PHY */
214362306a36Sopenharmony_ci	reset_board(lanai);
214462306a36Sopenharmony_ci	lanai->conf1 = reg_read(lanai, Config1_Reg);
214562306a36Sopenharmony_ci	lanai->conf1 &= ~(CONFIG1_GPOUT1 | CONFIG1_POWERDOWN |
214662306a36Sopenharmony_ci	    CONFIG1_MASK_LEDMODE);
214762306a36Sopenharmony_ci	lanai->conf1 |= CONFIG1_SET_LEDMODE(LEDMODE_NOT_SOOL);
214862306a36Sopenharmony_ci	reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg);
214962306a36Sopenharmony_ci	udelay(1000);
215062306a36Sopenharmony_ci	conf1_write(lanai);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	/*
215362306a36Sopenharmony_ci	 * 3.4: Turn on endian mode for big-endian hardware
215462306a36Sopenharmony_ci	 *   We don't actually want to do this - the actual bit fields
215562306a36Sopenharmony_ci	 *   in the endian register are not documented anywhere.
215662306a36Sopenharmony_ci	 *   Instead we do the bit-flipping ourselves on big-endian
215762306a36Sopenharmony_ci	 *   hardware.
215862306a36Sopenharmony_ci	 *
215962306a36Sopenharmony_ci	 * 3.5: get the board ID/rev by reading the reset register
216062306a36Sopenharmony_ci	 */
216162306a36Sopenharmony_ci	result = check_board_id_and_rev("register",
216262306a36Sopenharmony_ci	    reg_read(lanai, Reset_Reg), &lanai->board_rev);
216362306a36Sopenharmony_ci	if (result != 0)
216462306a36Sopenharmony_ci		goto error_unmap;
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	/* 3.6: read EEPROM */
216762306a36Sopenharmony_ci	if ((result = eeprom_read(lanai)) != 0)
216862306a36Sopenharmony_ci		goto error_unmap;
216962306a36Sopenharmony_ci	if ((result = eeprom_validate(lanai)) != 0)
217062306a36Sopenharmony_ci		goto error_unmap;
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	/* 3.7: re-reset PHY, do loopback tests, setup PHY */
217362306a36Sopenharmony_ci	reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg);
217462306a36Sopenharmony_ci	udelay(1000);
217562306a36Sopenharmony_ci	conf1_write(lanai);
217662306a36Sopenharmony_ci	/* TODO - loopback tests */
217762306a36Sopenharmony_ci	lanai->conf1 |= (CONFIG1_GPOUT2 | CONFIG1_GPOUT3 | CONFIG1_DMA_ENABLE);
217862306a36Sopenharmony_ci	conf1_write(lanai);
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	/* 3.8/3.9: test and initialize card SRAM */
218162306a36Sopenharmony_ci	if ((result = sram_test_and_clear(lanai)) != 0)
218262306a36Sopenharmony_ci		goto error_unmap;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	/* 3.10: initialize lanai registers */
218562306a36Sopenharmony_ci	lanai->conf1 |= CONFIG1_DMA_ENABLE;
218662306a36Sopenharmony_ci	conf1_write(lanai);
218762306a36Sopenharmony_ci	if ((result = service_buffer_allocate(lanai)) != 0)
218862306a36Sopenharmony_ci		goto error_unmap;
218962306a36Sopenharmony_ci	if ((result = vcc_table_allocate(lanai)) != 0)
219062306a36Sopenharmony_ci		goto error_service;
219162306a36Sopenharmony_ci	lanai->conf2 = (lanai->num_vci >= 512 ? CONFIG2_HOWMANY : 0) |
219262306a36Sopenharmony_ci	    CONFIG2_HEC_DROP |	/* ??? */ CONFIG2_PTI7_MODE;
219362306a36Sopenharmony_ci	conf2_write(lanai);
219462306a36Sopenharmony_ci	reg_write(lanai, TX_FIFO_DEPTH, TxDepth_Reg);
219562306a36Sopenharmony_ci	reg_write(lanai, 0, CBR_ICG_Reg);	/* CBR defaults to no limit */
219662306a36Sopenharmony_ci	if ((result = request_irq(lanai->pci->irq, lanai_int, IRQF_SHARED,
219762306a36Sopenharmony_ci	    DEV_LABEL, lanai)) != 0) {
219862306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL ": can't allocate interrupt\n");
219962306a36Sopenharmony_ci		goto error_vcctable;
220062306a36Sopenharmony_ci	}
220162306a36Sopenharmony_ci	mb();				/* Make sure that all that made it */
220262306a36Sopenharmony_ci	intr_enable(lanai, INT_ALL & ~(INT_PING | INT_WAKE));
220362306a36Sopenharmony_ci	/* 3.11: initialize loop mode (i.e. turn looping off) */
220462306a36Sopenharmony_ci	lanai->conf1 = (lanai->conf1 & ~CONFIG1_MASK_LOOPMODE) |
220562306a36Sopenharmony_ci	    CONFIG1_SET_LOOPMODE(LOOPMODE_NORMAL) |
220662306a36Sopenharmony_ci	    CONFIG1_GPOUT2 | CONFIG1_GPOUT3;
220762306a36Sopenharmony_ci	conf1_write(lanai);
220862306a36Sopenharmony_ci	lanai->status = reg_read(lanai, Status_Reg);
220962306a36Sopenharmony_ci	/* We're now done initializing this card */
221062306a36Sopenharmony_ci#ifdef USE_POWERDOWN
221162306a36Sopenharmony_ci	lanai->conf1 |= CONFIG1_POWERDOWN;
221262306a36Sopenharmony_ci	conf1_write(lanai);
221362306a36Sopenharmony_ci#endif
221462306a36Sopenharmony_ci	memcpy(atmdev->esi, eeprom_mac(lanai), ESI_LEN);
221562306a36Sopenharmony_ci	lanai_timed_poll_start(lanai);
221662306a36Sopenharmony_ci	printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d, base=%p, irq=%u "
221762306a36Sopenharmony_ci		"(%pMF)\n", lanai->number, (int) lanai->pci->revision,
221862306a36Sopenharmony_ci		lanai->base, lanai->pci->irq, atmdev->esi);
221962306a36Sopenharmony_ci	printk(KERN_NOTICE DEV_LABEL "(itf %d): LANAI%s, serialno=%u(0x%X), "
222062306a36Sopenharmony_ci	    "board_rev=%d\n", lanai->number,
222162306a36Sopenharmony_ci	    lanai->type==lanai2 ? "2" : "HB", (unsigned int) lanai->serialno,
222262306a36Sopenharmony_ci	    (unsigned int) lanai->serialno, lanai->board_rev);
222362306a36Sopenharmony_ci	return 0;
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci    error_vcctable:
222662306a36Sopenharmony_ci	vcc_table_deallocate(lanai);
222762306a36Sopenharmony_ci    error_service:
222862306a36Sopenharmony_ci	service_buffer_deallocate(lanai);
222962306a36Sopenharmony_ci    error_unmap:
223062306a36Sopenharmony_ci	reset_board(lanai);
223162306a36Sopenharmony_ci#ifdef USE_POWERDOWN
223262306a36Sopenharmony_ci	lanai->conf1 = reg_read(lanai, Config1_Reg) | CONFIG1_POWERDOWN;
223362306a36Sopenharmony_ci	conf1_write(lanai);
223462306a36Sopenharmony_ci#endif
223562306a36Sopenharmony_ci	iounmap(lanai->base);
223662306a36Sopenharmony_ci	lanai->base = NULL;
223762306a36Sopenharmony_ci    error_pci:
223862306a36Sopenharmony_ci	pci_disable_device(lanai->pci);
223962306a36Sopenharmony_ci    error:
224062306a36Sopenharmony_ci	return result;
224162306a36Sopenharmony_ci}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci/* called when device is being shutdown, and all vcc's are gone - higher
224462306a36Sopenharmony_ci * levels will deallocate the atm device for us
224562306a36Sopenharmony_ci */
224662306a36Sopenharmony_cistatic void lanai_dev_close(struct atm_dev *atmdev)
224762306a36Sopenharmony_ci{
224862306a36Sopenharmony_ci	struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data;
224962306a36Sopenharmony_ci	if (lanai->base==NULL)
225062306a36Sopenharmony_ci		return;
225162306a36Sopenharmony_ci	printk(KERN_INFO DEV_LABEL "(itf %d): shutting down interface\n",
225262306a36Sopenharmony_ci	    lanai->number);
225362306a36Sopenharmony_ci	lanai_timed_poll_stop(lanai);
225462306a36Sopenharmony_ci#ifdef USE_POWERDOWN
225562306a36Sopenharmony_ci	lanai->conf1 = reg_read(lanai, Config1_Reg) & ~CONFIG1_POWERDOWN;
225662306a36Sopenharmony_ci	conf1_write(lanai);
225762306a36Sopenharmony_ci#endif
225862306a36Sopenharmony_ci	intr_disable(lanai, INT_ALL);
225962306a36Sopenharmony_ci	free_irq(lanai->pci->irq, lanai);
226062306a36Sopenharmony_ci	reset_board(lanai);
226162306a36Sopenharmony_ci#ifdef USE_POWERDOWN
226262306a36Sopenharmony_ci	lanai->conf1 |= CONFIG1_POWERDOWN;
226362306a36Sopenharmony_ci	conf1_write(lanai);
226462306a36Sopenharmony_ci#endif
226562306a36Sopenharmony_ci	pci_disable_device(lanai->pci);
226662306a36Sopenharmony_ci	vcc_table_deallocate(lanai);
226762306a36Sopenharmony_ci	service_buffer_deallocate(lanai);
226862306a36Sopenharmony_ci	iounmap(lanai->base);
226962306a36Sopenharmony_ci	kfree(lanai);
227062306a36Sopenharmony_ci}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci/* close a vcc */
227362306a36Sopenharmony_cistatic void lanai_close(struct atm_vcc *atmvcc)
227462306a36Sopenharmony_ci{
227562306a36Sopenharmony_ci	struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data;
227662306a36Sopenharmony_ci	struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data;
227762306a36Sopenharmony_ci	if (lvcc == NULL)
227862306a36Sopenharmony_ci		return;
227962306a36Sopenharmony_ci	clear_bit(ATM_VF_READY, &atmvcc->flags);
228062306a36Sopenharmony_ci	clear_bit(ATM_VF_PARTIAL, &atmvcc->flags);
228162306a36Sopenharmony_ci	if (lvcc->rx.atmvcc == atmvcc) {
228262306a36Sopenharmony_ci		lanai_shutdown_rx_vci(lvcc);
228362306a36Sopenharmony_ci		if (atmvcc->qos.aal == ATM_AAL0) {
228462306a36Sopenharmony_ci			if (--lanai->naal0 <= 0)
228562306a36Sopenharmony_ci				aal0_buffer_free(lanai);
228662306a36Sopenharmony_ci		} else
228762306a36Sopenharmony_ci			lanai_buf_deallocate(&lvcc->rx.buf, lanai->pci);
228862306a36Sopenharmony_ci		lvcc->rx.atmvcc = NULL;
228962306a36Sopenharmony_ci	}
229062306a36Sopenharmony_ci	if (lvcc->tx.atmvcc == atmvcc) {
229162306a36Sopenharmony_ci		if (atmvcc == lanai->cbrvcc) {
229262306a36Sopenharmony_ci			if (lvcc->vbase != NULL)
229362306a36Sopenharmony_ci				lanai_cbr_shutdown(lanai);
229462306a36Sopenharmony_ci			lanai->cbrvcc = NULL;
229562306a36Sopenharmony_ci		}
229662306a36Sopenharmony_ci		lanai_shutdown_tx_vci(lanai, lvcc);
229762306a36Sopenharmony_ci		lanai_buf_deallocate(&lvcc->tx.buf, lanai->pci);
229862306a36Sopenharmony_ci		lvcc->tx.atmvcc = NULL;
229962306a36Sopenharmony_ci	}
230062306a36Sopenharmony_ci	if (--lvcc->nref == 0) {
230162306a36Sopenharmony_ci		host_vcc_unbind(lanai, lvcc);
230262306a36Sopenharmony_ci		kfree(lvcc);
230362306a36Sopenharmony_ci	}
230462306a36Sopenharmony_ci	atmvcc->dev_data = NULL;
230562306a36Sopenharmony_ci	clear_bit(ATM_VF_ADDR, &atmvcc->flags);
230662306a36Sopenharmony_ci}
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci/* open a vcc on the card to vpi/vci */
230962306a36Sopenharmony_cistatic int lanai_open(struct atm_vcc *atmvcc)
231062306a36Sopenharmony_ci{
231162306a36Sopenharmony_ci	struct lanai_dev *lanai;
231262306a36Sopenharmony_ci	struct lanai_vcc *lvcc;
231362306a36Sopenharmony_ci	int result = 0;
231462306a36Sopenharmony_ci	int vci = atmvcc->vci;
231562306a36Sopenharmony_ci	short vpi = atmvcc->vpi;
231662306a36Sopenharmony_ci	/* we don't support partial open - it's not really useful anyway */
231762306a36Sopenharmony_ci	if ((test_bit(ATM_VF_PARTIAL, &atmvcc->flags)) ||
231862306a36Sopenharmony_ci	    (vpi == ATM_VPI_UNSPEC) || (vci == ATM_VCI_UNSPEC))
231962306a36Sopenharmony_ci		return -EINVAL;
232062306a36Sopenharmony_ci	lanai = (struct lanai_dev *) atmvcc->dev->dev_data;
232162306a36Sopenharmony_ci	result = lanai_normalize_ci(lanai, atmvcc, &vpi, &vci);
232262306a36Sopenharmony_ci	if (unlikely(result != 0))
232362306a36Sopenharmony_ci		goto out;
232462306a36Sopenharmony_ci	set_bit(ATM_VF_ADDR, &atmvcc->flags);
232562306a36Sopenharmony_ci	if (atmvcc->qos.aal != ATM_AAL0 && atmvcc->qos.aal != ATM_AAL5)
232662306a36Sopenharmony_ci		return -EINVAL;
232762306a36Sopenharmony_ci	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n", lanai->number,
232862306a36Sopenharmony_ci	    (int) vpi, vci);
232962306a36Sopenharmony_ci	lvcc = lanai->vccs[vci];
233062306a36Sopenharmony_ci	if (lvcc == NULL) {
233162306a36Sopenharmony_ci		lvcc = new_lanai_vcc();
233262306a36Sopenharmony_ci		if (unlikely(lvcc == NULL))
233362306a36Sopenharmony_ci			return -ENOMEM;
233462306a36Sopenharmony_ci		atmvcc->dev_data = lvcc;
233562306a36Sopenharmony_ci	}
233662306a36Sopenharmony_ci	lvcc->nref++;
233762306a36Sopenharmony_ci	if (atmvcc->qos.rxtp.traffic_class != ATM_NONE) {
233862306a36Sopenharmony_ci		APRINTK(lvcc->rx.atmvcc == NULL, "rx.atmvcc!=NULL, vci=%d\n",
233962306a36Sopenharmony_ci		    vci);
234062306a36Sopenharmony_ci		if (atmvcc->qos.aal == ATM_AAL0) {
234162306a36Sopenharmony_ci			if (lanai->naal0 == 0)
234262306a36Sopenharmony_ci				result = aal0_buffer_allocate(lanai);
234362306a36Sopenharmony_ci		} else
234462306a36Sopenharmony_ci			result = lanai_setup_rx_vci_aal5(
234562306a36Sopenharmony_ci			    lanai, lvcc, &atmvcc->qos);
234662306a36Sopenharmony_ci		if (unlikely(result != 0))
234762306a36Sopenharmony_ci			goto out_free;
234862306a36Sopenharmony_ci		lvcc->rx.atmvcc = atmvcc;
234962306a36Sopenharmony_ci		lvcc->stats.rx_nomem = 0;
235062306a36Sopenharmony_ci		lvcc->stats.x.aal5.rx_badlen = 0;
235162306a36Sopenharmony_ci		lvcc->stats.x.aal5.service_trash = 0;
235262306a36Sopenharmony_ci		lvcc->stats.x.aal5.service_stream = 0;
235362306a36Sopenharmony_ci		lvcc->stats.x.aal5.service_rxcrc = 0;
235462306a36Sopenharmony_ci		if (atmvcc->qos.aal == ATM_AAL0)
235562306a36Sopenharmony_ci			lanai->naal0++;
235662306a36Sopenharmony_ci	}
235762306a36Sopenharmony_ci	if (atmvcc->qos.txtp.traffic_class != ATM_NONE) {
235862306a36Sopenharmony_ci		APRINTK(lvcc->tx.atmvcc == NULL, "tx.atmvcc!=NULL, vci=%d\n",
235962306a36Sopenharmony_ci		    vci);
236062306a36Sopenharmony_ci		result = lanai_setup_tx_vci(lanai, lvcc, &atmvcc->qos);
236162306a36Sopenharmony_ci		if (unlikely(result != 0))
236262306a36Sopenharmony_ci			goto out_free;
236362306a36Sopenharmony_ci		lvcc->tx.atmvcc = atmvcc;
236462306a36Sopenharmony_ci		if (atmvcc->qos.txtp.traffic_class == ATM_CBR) {
236562306a36Sopenharmony_ci			APRINTK(lanai->cbrvcc == NULL,
236662306a36Sopenharmony_ci			    "cbrvcc!=NULL, vci=%d\n", vci);
236762306a36Sopenharmony_ci			lanai->cbrvcc = atmvcc;
236862306a36Sopenharmony_ci		}
236962306a36Sopenharmony_ci	}
237062306a36Sopenharmony_ci	host_vcc_bind(lanai, lvcc, vci);
237162306a36Sopenharmony_ci	/*
237262306a36Sopenharmony_ci	 * Make sure everything made it to RAM before we tell the card about
237362306a36Sopenharmony_ci	 * the VCC
237462306a36Sopenharmony_ci	 */
237562306a36Sopenharmony_ci	wmb();
237662306a36Sopenharmony_ci	if (atmvcc == lvcc->rx.atmvcc)
237762306a36Sopenharmony_ci		host_vcc_start_rx(lvcc);
237862306a36Sopenharmony_ci	if (atmvcc == lvcc->tx.atmvcc) {
237962306a36Sopenharmony_ci		host_vcc_start_tx(lvcc);
238062306a36Sopenharmony_ci		if (lanai->cbrvcc == atmvcc)
238162306a36Sopenharmony_ci			lanai_cbr_setup(lanai);
238262306a36Sopenharmony_ci	}
238362306a36Sopenharmony_ci	set_bit(ATM_VF_READY, &atmvcc->flags);
238462306a36Sopenharmony_ci	return 0;
238562306a36Sopenharmony_ci    out_free:
238662306a36Sopenharmony_ci	lanai_close(atmvcc);
238762306a36Sopenharmony_ci    out:
238862306a36Sopenharmony_ci	return result;
238962306a36Sopenharmony_ci}
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_cistatic int lanai_send(struct atm_vcc *atmvcc, struct sk_buff *skb)
239262306a36Sopenharmony_ci{
239362306a36Sopenharmony_ci	struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data;
239462306a36Sopenharmony_ci	struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data;
239562306a36Sopenharmony_ci	unsigned long flags;
239662306a36Sopenharmony_ci	if (unlikely(lvcc == NULL || lvcc->vbase == NULL ||
239762306a36Sopenharmony_ci	      lvcc->tx.atmvcc != atmvcc))
239862306a36Sopenharmony_ci		goto einval;
239962306a36Sopenharmony_ci#ifdef DEBUG
240062306a36Sopenharmony_ci	if (unlikely(skb == NULL)) {
240162306a36Sopenharmony_ci		DPRINTK("lanai_send: skb==NULL for vci=%d\n", atmvcc->vci);
240262306a36Sopenharmony_ci		goto einval;
240362306a36Sopenharmony_ci	}
240462306a36Sopenharmony_ci	if (unlikely(lanai == NULL)) {
240562306a36Sopenharmony_ci		DPRINTK("lanai_send: lanai==NULL for vci=%d\n", atmvcc->vci);
240662306a36Sopenharmony_ci		goto einval;
240762306a36Sopenharmony_ci	}
240862306a36Sopenharmony_ci#endif
240962306a36Sopenharmony_ci	ATM_SKB(skb)->vcc = atmvcc;
241062306a36Sopenharmony_ci	switch (atmvcc->qos.aal) {
241162306a36Sopenharmony_ci		case ATM_AAL5:
241262306a36Sopenharmony_ci			read_lock_irqsave(&vcc_sklist_lock, flags);
241362306a36Sopenharmony_ci			vcc_tx_aal5(lanai, lvcc, skb);
241462306a36Sopenharmony_ci			read_unlock_irqrestore(&vcc_sklist_lock, flags);
241562306a36Sopenharmony_ci			return 0;
241662306a36Sopenharmony_ci		case ATM_AAL0:
241762306a36Sopenharmony_ci			if (unlikely(skb->len != ATM_CELL_SIZE-1))
241862306a36Sopenharmony_ci				goto einval;
241962306a36Sopenharmony_ci  /* NOTE - this next line is technically invalid - we haven't unshared skb */
242062306a36Sopenharmony_ci			cpu_to_be32s((u32 *) skb->data);
242162306a36Sopenharmony_ci			read_lock_irqsave(&vcc_sklist_lock, flags);
242262306a36Sopenharmony_ci			vcc_tx_aal0(lanai, lvcc, skb);
242362306a36Sopenharmony_ci			read_unlock_irqrestore(&vcc_sklist_lock, flags);
242462306a36Sopenharmony_ci			return 0;
242562306a36Sopenharmony_ci	}
242662306a36Sopenharmony_ci	DPRINTK("lanai_send: bad aal=%d on vci=%d\n", (int) atmvcc->qos.aal,
242762306a36Sopenharmony_ci	    atmvcc->vci);
242862306a36Sopenharmony_ci    einval:
242962306a36Sopenharmony_ci	lanai_free_skb(atmvcc, skb);
243062306a36Sopenharmony_ci	return -EINVAL;
243162306a36Sopenharmony_ci}
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_cistatic int lanai_change_qos(struct atm_vcc *atmvcc,
243462306a36Sopenharmony_ci	/*const*/ struct atm_qos *qos, int flags)
243562306a36Sopenharmony_ci{
243662306a36Sopenharmony_ci	return -EBUSY;		/* TODO: need to write this */
243762306a36Sopenharmony_ci}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci#ifndef CONFIG_PROC_FS
244062306a36Sopenharmony_ci#define lanai_proc_read NULL
244162306a36Sopenharmony_ci#else
244262306a36Sopenharmony_cistatic int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page)
244362306a36Sopenharmony_ci{
244462306a36Sopenharmony_ci	struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data;
244562306a36Sopenharmony_ci	loff_t left = *pos;
244662306a36Sopenharmony_ci	struct lanai_vcc *lvcc;
244762306a36Sopenharmony_ci	if (left-- == 0)
244862306a36Sopenharmony_ci		return sprintf(page, DEV_LABEL "(itf %d): chip=LANAI%s, "
244962306a36Sopenharmony_ci		    "serial=%u, magic=0x%08X, num_vci=%d\n",
245062306a36Sopenharmony_ci		    atmdev->number, lanai->type==lanai2 ? "2" : "HB",
245162306a36Sopenharmony_ci		    (unsigned int) lanai->serialno,
245262306a36Sopenharmony_ci		    (unsigned int) lanai->magicno, lanai->num_vci);
245362306a36Sopenharmony_ci	if (left-- == 0)
245462306a36Sopenharmony_ci		return sprintf(page, "revision: board=%d, pci_if=%d\n",
245562306a36Sopenharmony_ci		    lanai->board_rev, (int) lanai->pci->revision);
245662306a36Sopenharmony_ci	if (left-- == 0)
245762306a36Sopenharmony_ci		return sprintf(page, "EEPROM ESI: %pM\n",
245862306a36Sopenharmony_ci		    &lanai->eeprom[EEPROM_MAC]);
245962306a36Sopenharmony_ci	if (left-- == 0)
246062306a36Sopenharmony_ci		return sprintf(page, "status: SOOL=%d, LOCD=%d, LED=%d, "
246162306a36Sopenharmony_ci		    "GPIN=%d\n", (lanai->status & STATUS_SOOL) ? 1 : 0,
246262306a36Sopenharmony_ci		    (lanai->status & STATUS_LOCD) ? 1 : 0,
246362306a36Sopenharmony_ci		    (lanai->status & STATUS_LED) ? 1 : 0,
246462306a36Sopenharmony_ci		    (lanai->status & STATUS_GPIN) ? 1 : 0);
246562306a36Sopenharmony_ci	if (left-- == 0)
246662306a36Sopenharmony_ci		return sprintf(page, "global buffer sizes: service=%zu, "
246762306a36Sopenharmony_ci		    "aal0_rx=%zu\n", lanai_buf_size(&lanai->service),
246862306a36Sopenharmony_ci		    lanai->naal0 ? lanai_buf_size(&lanai->aal0buf) : 0);
246962306a36Sopenharmony_ci	if (left-- == 0) {
247062306a36Sopenharmony_ci		get_statistics(lanai);
247162306a36Sopenharmony_ci		return sprintf(page, "cells in error: overflow=%u, "
247262306a36Sopenharmony_ci		    "closed_vci=%u, bad_HEC=%u, rx_fifo=%u\n",
247362306a36Sopenharmony_ci		    lanai->stats.ovfl_trash, lanai->stats.vci_trash,
247462306a36Sopenharmony_ci		    lanai->stats.hec_err, lanai->stats.atm_ovfl);
247562306a36Sopenharmony_ci	}
247662306a36Sopenharmony_ci	if (left-- == 0)
247762306a36Sopenharmony_ci		return sprintf(page, "PCI errors: parity_detect=%u, "
247862306a36Sopenharmony_ci		    "master_abort=%u, master_target_abort=%u,\n",
247962306a36Sopenharmony_ci		    lanai->stats.pcierr_parity_detect,
248062306a36Sopenharmony_ci		    lanai->stats.pcierr_serr_set,
248162306a36Sopenharmony_ci		    lanai->stats.pcierr_m_target_abort);
248262306a36Sopenharmony_ci	if (left-- == 0)
248362306a36Sopenharmony_ci		return sprintf(page, "            slave_target_abort=%u, "
248462306a36Sopenharmony_ci		    "master_parity=%u\n", lanai->stats.pcierr_s_target_abort,
248562306a36Sopenharmony_ci		    lanai->stats.pcierr_master_parity);
248662306a36Sopenharmony_ci	if (left-- == 0)
248762306a36Sopenharmony_ci		return sprintf(page, "                     no_tx=%u, "
248862306a36Sopenharmony_ci		    "no_rx=%u, bad_rx_aal=%u\n", lanai->stats.service_norx,
248962306a36Sopenharmony_ci		    lanai->stats.service_notx,
249062306a36Sopenharmony_ci		    lanai->stats.service_rxnotaal5);
249162306a36Sopenharmony_ci	if (left-- == 0)
249262306a36Sopenharmony_ci		return sprintf(page, "resets: dma=%u, card=%u\n",
249362306a36Sopenharmony_ci		    lanai->stats.dma_reenable, lanai->stats.card_reset);
249462306a36Sopenharmony_ci	/* At this point, "left" should be the VCI we're looking for */
249562306a36Sopenharmony_ci	read_lock(&vcc_sklist_lock);
249662306a36Sopenharmony_ci	for (; ; left++) {
249762306a36Sopenharmony_ci		if (left >= NUM_VCI) {
249862306a36Sopenharmony_ci			left = 0;
249962306a36Sopenharmony_ci			goto out;
250062306a36Sopenharmony_ci		}
250162306a36Sopenharmony_ci		if ((lvcc = lanai->vccs[left]) != NULL)
250262306a36Sopenharmony_ci			break;
250362306a36Sopenharmony_ci		(*pos)++;
250462306a36Sopenharmony_ci	}
250562306a36Sopenharmony_ci	/* Note that we re-use "left" here since we're done with it */
250662306a36Sopenharmony_ci	left = sprintf(page, "VCI %4d: nref=%d, rx_nomem=%u",  (vci_t) left,
250762306a36Sopenharmony_ci	    lvcc->nref, lvcc->stats.rx_nomem);
250862306a36Sopenharmony_ci	if (lvcc->rx.atmvcc != NULL) {
250962306a36Sopenharmony_ci		left += sprintf(&page[left], ",\n          rx_AAL=%d",
251062306a36Sopenharmony_ci		    lvcc->rx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0);
251162306a36Sopenharmony_ci		if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5)
251262306a36Sopenharmony_ci			left += sprintf(&page[left], ", rx_buf_size=%zu, "
251362306a36Sopenharmony_ci			    "rx_bad_len=%u,\n          rx_service_trash=%u, "
251462306a36Sopenharmony_ci			    "rx_service_stream=%u, rx_bad_crc=%u",
251562306a36Sopenharmony_ci			    lanai_buf_size(&lvcc->rx.buf),
251662306a36Sopenharmony_ci			    lvcc->stats.x.aal5.rx_badlen,
251762306a36Sopenharmony_ci			    lvcc->stats.x.aal5.service_trash,
251862306a36Sopenharmony_ci			    lvcc->stats.x.aal5.service_stream,
251962306a36Sopenharmony_ci			    lvcc->stats.x.aal5.service_rxcrc);
252062306a36Sopenharmony_ci	}
252162306a36Sopenharmony_ci	if (lvcc->tx.atmvcc != NULL)
252262306a36Sopenharmony_ci		left += sprintf(&page[left], ",\n          tx_AAL=%d, "
252362306a36Sopenharmony_ci		    "tx_buf_size=%zu, tx_qos=%cBR, tx_backlogged=%c",
252462306a36Sopenharmony_ci		    lvcc->tx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0,
252562306a36Sopenharmony_ci		    lanai_buf_size(&lvcc->tx.buf),
252662306a36Sopenharmony_ci		    lvcc->tx.atmvcc == lanai->cbrvcc ? 'C' : 'U',
252762306a36Sopenharmony_ci		    vcc_is_backlogged(lvcc) ? 'Y' : 'N');
252862306a36Sopenharmony_ci	page[left++] = '\n';
252962306a36Sopenharmony_ci	page[left] = '\0';
253062306a36Sopenharmony_ci    out:
253162306a36Sopenharmony_ci	read_unlock(&vcc_sklist_lock);
253262306a36Sopenharmony_ci	return left;
253362306a36Sopenharmony_ci}
253462306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci/* -------------------- HOOKS: */
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_cistatic const struct atmdev_ops ops = {
253962306a36Sopenharmony_ci	.dev_close	= lanai_dev_close,
254062306a36Sopenharmony_ci	.open		= lanai_open,
254162306a36Sopenharmony_ci	.close		= lanai_close,
254262306a36Sopenharmony_ci	.send		= lanai_send,
254362306a36Sopenharmony_ci	.phy_put	= NULL,
254462306a36Sopenharmony_ci	.phy_get	= NULL,
254562306a36Sopenharmony_ci	.change_qos	= lanai_change_qos,
254662306a36Sopenharmony_ci	.proc_read	= lanai_proc_read,
254762306a36Sopenharmony_ci	.owner		= THIS_MODULE
254862306a36Sopenharmony_ci};
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci/* initialize one probed card */
255162306a36Sopenharmony_cistatic int lanai_init_one(struct pci_dev *pci,
255262306a36Sopenharmony_ci			  const struct pci_device_id *ident)
255362306a36Sopenharmony_ci{
255462306a36Sopenharmony_ci	struct lanai_dev *lanai;
255562306a36Sopenharmony_ci	struct atm_dev *atmdev;
255662306a36Sopenharmony_ci	int result;
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	lanai = kzalloc(sizeof(*lanai), GFP_KERNEL);
255962306a36Sopenharmony_ci	if (lanai == NULL) {
256062306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL
256162306a36Sopenharmony_ci		       ": couldn't allocate dev_data structure!\n");
256262306a36Sopenharmony_ci		return -ENOMEM;
256362306a36Sopenharmony_ci	}
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	atmdev = atm_dev_register(DEV_LABEL, &pci->dev, &ops, -1, NULL);
256662306a36Sopenharmony_ci	if (atmdev == NULL) {
256762306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL
256862306a36Sopenharmony_ci		    ": couldn't register atm device!\n");
256962306a36Sopenharmony_ci		kfree(lanai);
257062306a36Sopenharmony_ci		return -EBUSY;
257162306a36Sopenharmony_ci	}
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	atmdev->dev_data = lanai;
257462306a36Sopenharmony_ci	lanai->pci = pci;
257562306a36Sopenharmony_ci	lanai->type = (enum lanai_type) ident->device;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	result = lanai_dev_open(atmdev);
257862306a36Sopenharmony_ci	if (result != 0) {
257962306a36Sopenharmony_ci		DPRINTK("lanai_start() failed, err=%d\n", -result);
258062306a36Sopenharmony_ci		atm_dev_deregister(atmdev);
258162306a36Sopenharmony_ci		kfree(lanai);
258262306a36Sopenharmony_ci	}
258362306a36Sopenharmony_ci	return result;
258462306a36Sopenharmony_ci}
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_cistatic const struct pci_device_id lanai_pci_tbl[] = {
258762306a36Sopenharmony_ci	{ PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAI2) },
258862306a36Sopenharmony_ci	{ PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAIHB) },
258962306a36Sopenharmony_ci	{ 0, }	/* terminal entry */
259062306a36Sopenharmony_ci};
259162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, lanai_pci_tbl);
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_cistatic struct pci_driver lanai_driver = {
259462306a36Sopenharmony_ci	.name     = DEV_LABEL,
259562306a36Sopenharmony_ci	.id_table = lanai_pci_tbl,
259662306a36Sopenharmony_ci	.probe    = lanai_init_one,
259762306a36Sopenharmony_ci};
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_cimodule_pci_driver(lanai_driver);
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ciMODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>");
260262306a36Sopenharmony_ciMODULE_DESCRIPTION("Efficient Networks Speedstream 3010 driver");
260362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2604