162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/mm.h>
1062306a36Sopenharmony_ci#include <linux/pci.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/atm.h>
1362306a36Sopenharmony_ci#include <linux/atmdev.h>
1462306a36Sopenharmony_ci#include <linux/sonet.h>
1562306a36Sopenharmony_ci#include <linux/skbuff.h>
1662306a36Sopenharmony_ci#include <linux/time.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/uio.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/atm_eni.h>
2162306a36Sopenharmony_ci#include <linux/bitops.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <asm/io.h>
2462306a36Sopenharmony_ci#include <linux/atomic.h>
2562306a36Sopenharmony_ci#include <linux/uaccess.h>
2662306a36Sopenharmony_ci#include <asm/string.h>
2762306a36Sopenharmony_ci#include <asm/byteorder.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "tonga.h"
3062306a36Sopenharmony_ci#include "midway.h"
3162306a36Sopenharmony_ci#include "suni.h"
3262306a36Sopenharmony_ci#include "eni.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * TODO:
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * Show stoppers
3862306a36Sopenharmony_ci *  none
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * Minor
4162306a36Sopenharmony_ci *  - OAM support
4262306a36Sopenharmony_ci *  - fix bugs listed below
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * KNOWN BUGS:
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * - may run into JK-JK bug and deadlock
4962306a36Sopenharmony_ci * - should allocate UBR channel first
5062306a36Sopenharmony_ci * - buffer space allocation algorithm is stupid
5162306a36Sopenharmony_ci *   (RX: should be maxSDU+maxdelay*rate
5262306a36Sopenharmony_ci *    TX: should be maxSDU+min(maxSDU,maxdelay*rate) )
5362306a36Sopenharmony_ci * - doesn't support OAM cells
5462306a36Sopenharmony_ci * - eni_put_free may hang if not putting memory fragments that _complete_
5562306a36Sopenharmony_ci *   2^n block (never happens in real life, though)
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#if 0
6062306a36Sopenharmony_ci#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
6162306a36Sopenharmony_ci#else
6262306a36Sopenharmony_ci#define DPRINTK(format,args...)
6362306a36Sopenharmony_ci#endif
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#ifndef CONFIG_ATM_ENI_TUNE_BURST
6762306a36Sopenharmony_ci#define CONFIG_ATM_ENI_BURST_TX_8W
6862306a36Sopenharmony_ci#define CONFIG_ATM_ENI_BURST_RX_4W
6962306a36Sopenharmony_ci#endif
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#ifndef CONFIG_ATM_ENI_DEBUG
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define NULLCHECK(x)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define EVENT(s,a,b)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic void event_dump(void)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#else
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * NULL pointer checking
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define NULLCHECK(x) \
9362306a36Sopenharmony_ci	if ((unsigned long) (x) < 0x30) \
9462306a36Sopenharmony_ci		printk(KERN_CRIT #x "==0x%lx\n",(unsigned long) (x))
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/*
9762306a36Sopenharmony_ci * Very extensive activity logging. Greatly improves bug detection speed but
9862306a36Sopenharmony_ci * costs a few Mbps if enabled.
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define EV 64
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic const char *ev[EV];
10462306a36Sopenharmony_cistatic unsigned long ev_a[EV],ev_b[EV];
10562306a36Sopenharmony_cistatic int ec = 0;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic void EVENT(const char *s,unsigned long a,unsigned long b)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	ev[ec] = s;
11162306a36Sopenharmony_ci	ev_a[ec] = a;
11262306a36Sopenharmony_ci	ev_b[ec] = b;
11362306a36Sopenharmony_ci	ec = (ec+1) % EV;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void event_dump(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	int n,i;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	for (n = 0; n < EV; n++) {
12262306a36Sopenharmony_ci		i = (ec+n) % EV;
12362306a36Sopenharmony_ci		printk(KERN_NOTICE);
12462306a36Sopenharmony_ci		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#endif /* CONFIG_ATM_ENI_DEBUG */
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/*
13362306a36Sopenharmony_ci * NExx   must not be equal at end
13462306a36Sopenharmony_ci * EExx   may be equal at end
13562306a36Sopenharmony_ci * xxPJOK verify validity of pointer jumps
13662306a36Sopenharmony_ci * xxPMOK operating on a circular buffer of "c" words
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define NEPJOK(a0,a1,b) \
14062306a36Sopenharmony_ci    ((a0) < (a1) ? (b) <= (a0) || (b) > (a1) : (b) <= (a0) && (b) > (a1))
14162306a36Sopenharmony_ci#define EEPJOK(a0,a1,b) \
14262306a36Sopenharmony_ci    ((a0) < (a1) ? (b) < (a0) || (b) >= (a1) : (b) < (a0) && (b) >= (a1))
14362306a36Sopenharmony_ci#define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b)
14462306a36Sopenharmony_ci#define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b)
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0,
14862306a36Sopenharmony_ci  backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0,
14962306a36Sopenharmony_ci  putting = 0;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic struct atm_dev *eni_boards = NULL;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* Read/write registers on card */
15462306a36Sopenharmony_ci#define eni_in(r)	readl(eni_dev->reg+(r)*4)
15562306a36Sopenharmony_ci#define eni_out(v,r)	writel((v),eni_dev->reg+(r)*4)
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*-------------------------------- utilities --------------------------------*/
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void dump_mem(struct eni_dev *eni_dev)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	int i;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = 0; i < eni_dev->free_len; i++)
16662306a36Sopenharmony_ci		printk(KERN_DEBUG "  %d: %p %d\n",i,
16762306a36Sopenharmony_ci		    eni_dev->free_list[i].start,
16862306a36Sopenharmony_ci		    1 << eni_dev->free_list[i].order);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void dump(struct atm_dev *dev)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct eni_dev *eni_dev;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	int i;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
17962306a36Sopenharmony_ci	printk(KERN_NOTICE "Free memory\n");
18062306a36Sopenharmony_ci	dump_mem(eni_dev);
18162306a36Sopenharmony_ci	printk(KERN_NOTICE "TX buffers\n");
18262306a36Sopenharmony_ci	for (i = 0; i < NR_CHAN; i++)
18362306a36Sopenharmony_ci		if (eni_dev->tx[i].send)
18462306a36Sopenharmony_ci			printk(KERN_NOTICE "  TX %d @ %p: %ld\n",i,
18562306a36Sopenharmony_ci			    eni_dev->tx[i].send,eni_dev->tx[i].words*4);
18662306a36Sopenharmony_ci	printk(KERN_NOTICE "RX buffers\n");
18762306a36Sopenharmony_ci	for (i = 0; i < 1024; i++)
18862306a36Sopenharmony_ci		if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx)
18962306a36Sopenharmony_ci			printk(KERN_NOTICE "  RX %d @ %p: %ld\n",i,
19062306a36Sopenharmony_ci			    ENI_VCC(eni_dev->rx_map[i])->recv,
19162306a36Sopenharmony_ci			    ENI_VCC(eni_dev->rx_map[i])->words*4);
19262306a36Sopenharmony_ci	printk(KERN_NOTICE "----\n");
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void eni_put_free(struct eni_dev *eni_dev, void __iomem *start,
19762306a36Sopenharmony_ci    unsigned long size)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct eni_free *list;
20062306a36Sopenharmony_ci	int len,order;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	DPRINTK("init 0x%lx+%ld(0x%lx)\n",start,size,size);
20362306a36Sopenharmony_ci	start += eni_dev->base_diff;
20462306a36Sopenharmony_ci	list = eni_dev->free_list;
20562306a36Sopenharmony_ci	len = eni_dev->free_len;
20662306a36Sopenharmony_ci	while (size) {
20762306a36Sopenharmony_ci		if (len >= eni_dev->free_list_size) {
20862306a36Sopenharmony_ci			printk(KERN_CRIT "eni_put_free overflow (%p,%ld)\n",
20962306a36Sopenharmony_ci			    start,size);
21062306a36Sopenharmony_ci			break;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci		for (order = 0; !(((unsigned long)start | size) & (1 << order)); order++);
21362306a36Sopenharmony_ci		if (MID_MIN_BUF_SIZE > (1 << order)) {
21462306a36Sopenharmony_ci			printk(KERN_CRIT "eni_put_free: order %d too small\n",
21562306a36Sopenharmony_ci			    order);
21662306a36Sopenharmony_ci			break;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci		list[len].start = (void __iomem *) start;
21962306a36Sopenharmony_ci		list[len].order = order;
22062306a36Sopenharmony_ci		len++;
22162306a36Sopenharmony_ci		start += 1 << order;
22262306a36Sopenharmony_ci		size -= 1 << order;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	eni_dev->free_len = len;
22562306a36Sopenharmony_ci	/*dump_mem(eni_dev);*/
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void __iomem *eni_alloc_mem(struct eni_dev *eni_dev, unsigned long *size)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct eni_free *list;
23262306a36Sopenharmony_ci	void __iomem *start;
23362306a36Sopenharmony_ci	int len,i,order,best_order,index;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	list = eni_dev->free_list;
23662306a36Sopenharmony_ci	len = eni_dev->free_len;
23762306a36Sopenharmony_ci	if (*size < MID_MIN_BUF_SIZE) *size = MID_MIN_BUF_SIZE;
23862306a36Sopenharmony_ci	if (*size > MID_MAX_BUF_SIZE) return NULL;
23962306a36Sopenharmony_ci	for (order = 0; (1 << order) < *size; order++)
24062306a36Sopenharmony_ci		;
24162306a36Sopenharmony_ci	DPRINTK("trying: %ld->%d\n",*size,order);
24262306a36Sopenharmony_ci	best_order = 65; /* we don't have more than 2^64 of anything ... */
24362306a36Sopenharmony_ci	index = 0; /* silence GCC */
24462306a36Sopenharmony_ci	for (i = 0; i < len; i++)
24562306a36Sopenharmony_ci		if (list[i].order == order) {
24662306a36Sopenharmony_ci			best_order = order;
24762306a36Sopenharmony_ci			index = i;
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci		else if (best_order > list[i].order && list[i].order > order) {
25162306a36Sopenharmony_ci				best_order = list[i].order;
25262306a36Sopenharmony_ci				index = i;
25362306a36Sopenharmony_ci			}
25462306a36Sopenharmony_ci	if (best_order == 65) return NULL;
25562306a36Sopenharmony_ci	start = list[index].start-eni_dev->base_diff;
25662306a36Sopenharmony_ci	list[index] = list[--len];
25762306a36Sopenharmony_ci	eni_dev->free_len = len;
25862306a36Sopenharmony_ci	*size = 1 << order;
25962306a36Sopenharmony_ci	eni_put_free(eni_dev,start+*size,(1 << best_order)-*size);
26062306a36Sopenharmony_ci	DPRINTK("%ld bytes (order %d) at 0x%lx\n",*size,order,start);
26162306a36Sopenharmony_ci	memset_io(start,0,*size);       /* never leak data */
26262306a36Sopenharmony_ci	/*dump_mem(eni_dev);*/
26362306a36Sopenharmony_ci	return start;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void eni_free_mem(struct eni_dev *eni_dev, void __iomem *start,
26862306a36Sopenharmony_ci    unsigned long size)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct eni_free *list;
27162306a36Sopenharmony_ci	int len,i,order;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	start += eni_dev->base_diff;
27462306a36Sopenharmony_ci	list = eni_dev->free_list;
27562306a36Sopenharmony_ci	len = eni_dev->free_len;
27662306a36Sopenharmony_ci	for (order = -1; size; order++) size >>= 1;
27762306a36Sopenharmony_ci	DPRINTK("eni_free_mem: %p+0x%lx (order %d)\n",start,size,order);
27862306a36Sopenharmony_ci	for (i = 0; i < len; i++)
27962306a36Sopenharmony_ci		if (((unsigned long) list[i].start) == ((unsigned long)start^(1 << order)) &&
28062306a36Sopenharmony_ci		    list[i].order == order) {
28162306a36Sopenharmony_ci			DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i,
28262306a36Sopenharmony_ci			    list[i].start,start,1 << order,list[i].order,order);
28362306a36Sopenharmony_ci			list[i] = list[--len];
28462306a36Sopenharmony_ci			start = (void __iomem *) ((unsigned long) start & ~(unsigned long) (1 << order));
28562306a36Sopenharmony_ci			order++;
28662306a36Sopenharmony_ci			i = -1;
28762306a36Sopenharmony_ci			continue;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci	if (len >= eni_dev->free_list_size) {
29062306a36Sopenharmony_ci		printk(KERN_ALERT "eni_free_mem overflow (%p,%d)\n",start,
29162306a36Sopenharmony_ci		    order);
29262306a36Sopenharmony_ci		return;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci	list[len].start = start;
29562306a36Sopenharmony_ci	list[len].order = order;
29662306a36Sopenharmony_ci	eni_dev->free_len = len+1;
29762306a36Sopenharmony_ci	/*dump_mem(eni_dev);*/
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/*----------------------------------- RX ------------------------------------*/
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci#define ENI_VCC_NOS ((struct atm_vcc *) 1)
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void rx_ident_err(struct atm_vcc *vcc)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct atm_dev *dev;
31062306a36Sopenharmony_ci	struct eni_dev *eni_dev;
31162306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	dev = vcc->dev;
31462306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
31562306a36Sopenharmony_ci	/* immediately halt adapter */
31662306a36Sopenharmony_ci	eni_out(eni_in(MID_MC_S) &
31762306a36Sopenharmony_ci	    ~(MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE),MID_MC_S);
31862306a36Sopenharmony_ci	/* dump useful information */
31962306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
32062306a36Sopenharmony_ci	printk(KERN_ALERT DEV_LABEL "(itf %d): driver error - RX ident "
32162306a36Sopenharmony_ci	    "mismatch\n",dev->number);
32262306a36Sopenharmony_ci	printk(KERN_ALERT "  VCI %d, rxing %d, words %ld\n",vcc->vci,
32362306a36Sopenharmony_ci	    eni_vcc->rxing,eni_vcc->words);
32462306a36Sopenharmony_ci	printk(KERN_ALERT "  host descr 0x%lx, rx pos 0x%lx, descr value "
32562306a36Sopenharmony_ci	    "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos,
32662306a36Sopenharmony_ci	    (unsigned) readl(eni_vcc->recv+eni_vcc->descr*4));
32762306a36Sopenharmony_ci	printk(KERN_ALERT "  last %p, servicing %d\n",eni_vcc->last,
32862306a36Sopenharmony_ci	    eni_vcc->servicing);
32962306a36Sopenharmony_ci	EVENT("---dump ends here---\n",0,0);
33062306a36Sopenharmony_ci	printk(KERN_NOTICE "---recent events---\n");
33162306a36Sopenharmony_ci	event_dump();
33262306a36Sopenharmony_ci	ENI_DEV(dev)->fast = NULL; /* really stop it */
33362306a36Sopenharmony_ci	ENI_DEV(dev)->slow = NULL;
33462306a36Sopenharmony_ci	skb_queue_head_init(&ENI_DEV(dev)->rx_queue);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb,
33962306a36Sopenharmony_ci    unsigned long skip,unsigned long size,unsigned long eff)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct eni_dev *eni_dev;
34262306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
34362306a36Sopenharmony_ci	u32 dma_rd,dma_wr;
34462306a36Sopenharmony_ci	u32 dma[RX_DMA_BUF*2];
34562306a36Sopenharmony_ci	dma_addr_t paddr;
34662306a36Sopenharmony_ci	unsigned long here;
34762306a36Sopenharmony_ci	int i,j;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	eni_dev = ENI_DEV(vcc->dev);
35062306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
35162306a36Sopenharmony_ci	paddr = 0; /* GCC, shut up */
35262306a36Sopenharmony_ci	if (skb) {
35362306a36Sopenharmony_ci		paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len,
35462306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
35562306a36Sopenharmony_ci		if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr))
35662306a36Sopenharmony_ci			goto dma_map_error;
35762306a36Sopenharmony_ci		ENI_PRV_PADDR(skb) = paddr;
35862306a36Sopenharmony_ci		if (paddr & 3)
35962306a36Sopenharmony_ci			printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has "
36062306a36Sopenharmony_ci			    "mis-aligned RX data (0x%lx)\n",vcc->dev->number,
36162306a36Sopenharmony_ci			    vcc->vci,(unsigned long) paddr);
36262306a36Sopenharmony_ci		ENI_PRV_SIZE(skb) = size+skip;
36362306a36Sopenharmony_ci		    /* PDU plus descriptor */
36462306a36Sopenharmony_ci		ATM_SKB(skb)->vcc = vcc;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci	j = 0;
36762306a36Sopenharmony_ci	if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */
36862306a36Sopenharmony_ci		here = (eni_vcc->descr+skip) & (eni_vcc->words-1);
36962306a36Sopenharmony_ci		dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci
37062306a36Sopenharmony_ci		    << MID_DMA_VCI_SHIFT) | MID_DT_JK;
37162306a36Sopenharmony_ci		dma[j++] = 0;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci	here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1);
37462306a36Sopenharmony_ci	if (!eff) size += skip;
37562306a36Sopenharmony_ci	else {
37662306a36Sopenharmony_ci		unsigned long words;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		if (!size) {
37962306a36Sopenharmony_ci			DPRINTK("strange things happen ...\n");
38062306a36Sopenharmony_ci			EVENT("strange things happen ... (skip=%ld,eff=%ld)\n",
38162306a36Sopenharmony_ci			    size,eff);
38262306a36Sopenharmony_ci		}
38362306a36Sopenharmony_ci		words = eff;
38462306a36Sopenharmony_ci		if (paddr & 15) {
38562306a36Sopenharmony_ci			unsigned long init;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci			init = 4-((paddr & 15) >> 2);
38862306a36Sopenharmony_ci			if (init > words) init = words;
38962306a36Sopenharmony_ci			dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
39062306a36Sopenharmony_ci			    (vcc->vci << MID_DMA_VCI_SHIFT);
39162306a36Sopenharmony_ci			dma[j++] = paddr;
39262306a36Sopenharmony_ci			paddr += init << 2;
39362306a36Sopenharmony_ci			words -= init;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */
39662306a36Sopenharmony_ci		if (words & ~15) {
39762306a36Sopenharmony_ci			dma[j++] = MID_DT_16W | ((words >> 4) <<
39862306a36Sopenharmony_ci			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
39962306a36Sopenharmony_ci			    MID_DMA_VCI_SHIFT);
40062306a36Sopenharmony_ci			dma[j++] = paddr;
40162306a36Sopenharmony_ci			paddr += (words & ~15) << 2;
40262306a36Sopenharmony_ci			words &= 15;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci#endif
40562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_8W  /* works only with *some* PCI chipsets ... */
40662306a36Sopenharmony_ci		if (words & ~7) {
40762306a36Sopenharmony_ci			dma[j++] = MID_DT_8W | ((words >> 3) <<
40862306a36Sopenharmony_ci			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
40962306a36Sopenharmony_ci			    MID_DMA_VCI_SHIFT);
41062306a36Sopenharmony_ci			dma[j++] = paddr;
41162306a36Sopenharmony_ci			paddr += (words & ~7) << 2;
41262306a36Sopenharmony_ci			words &= 7;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci#endif
41562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */
41662306a36Sopenharmony_ci		if (words & ~3) {
41762306a36Sopenharmony_ci			dma[j++] = MID_DT_4W | ((words >> 2) <<
41862306a36Sopenharmony_ci			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
41962306a36Sopenharmony_ci			    MID_DMA_VCI_SHIFT);
42062306a36Sopenharmony_ci			dma[j++] = paddr;
42162306a36Sopenharmony_ci			paddr += (words & ~3) << 2;
42262306a36Sopenharmony_ci			words &= 3;
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci#endif
42562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */
42662306a36Sopenharmony_ci		if (words & ~1) {
42762306a36Sopenharmony_ci			dma[j++] = MID_DT_2W | ((words >> 1) <<
42862306a36Sopenharmony_ci			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
42962306a36Sopenharmony_ci			    MID_DMA_VCI_SHIFT);
43062306a36Sopenharmony_ci			dma[j++] = paddr;
43162306a36Sopenharmony_ci			paddr += (words & ~1) << 2;
43262306a36Sopenharmony_ci			words &= 1;
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci#endif
43562306a36Sopenharmony_ci		if (words) {
43662306a36Sopenharmony_ci			dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT)
43762306a36Sopenharmony_ci			    | (vcc->vci << MID_DMA_VCI_SHIFT);
43862306a36Sopenharmony_ci			dma[j++] = paddr;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci	if (size != eff) {
44262306a36Sopenharmony_ci		dma[j++] = (here << MID_DMA_COUNT_SHIFT) |
44362306a36Sopenharmony_ci		    (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK;
44462306a36Sopenharmony_ci		dma[j++] = 0;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci	if (!j || j > 2*RX_DMA_BUF) {
44762306a36Sopenharmony_ci		printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n");
44862306a36Sopenharmony_ci		goto trouble;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci	dma[j-2] |= MID_DMA_END;
45162306a36Sopenharmony_ci	j = j >> 1;
45262306a36Sopenharmony_ci	dma_wr = eni_in(MID_DMA_WR_RX);
45362306a36Sopenharmony_ci	dma_rd = eni_in(MID_DMA_RD_RX);
45462306a36Sopenharmony_ci	/*
45562306a36Sopenharmony_ci	 * Can I move the dma_wr pointer by 2j+1 positions without overwriting
45662306a36Sopenharmony_ci	 * data that hasn't been read (position of dma_rd) yet ?
45762306a36Sopenharmony_ci	 */
45862306a36Sopenharmony_ci	if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */
45962306a36Sopenharmony_ci		printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n",
46062306a36Sopenharmony_ci		    vcc->dev->number);
46162306a36Sopenharmony_ci		goto trouble;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci        for (i = 0; i < j; i++) {
46462306a36Sopenharmony_ci		writel(dma[i*2],eni_dev->rx_dma+dma_wr*8);
46562306a36Sopenharmony_ci		writel(dma[i*2+1],eni_dev->rx_dma+dma_wr*8+4);
46662306a36Sopenharmony_ci		dma_wr = (dma_wr+1) & (NR_DMA_RX-1);
46762306a36Sopenharmony_ci        }
46862306a36Sopenharmony_ci	if (skb) {
46962306a36Sopenharmony_ci		ENI_PRV_POS(skb) = eni_vcc->descr+size+1;
47062306a36Sopenharmony_ci		skb_queue_tail(&eni_dev->rx_queue,skb);
47162306a36Sopenharmony_ci		eni_vcc->last = skb;
47262306a36Sopenharmony_ci		rx_enqueued++;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci	eni_vcc->descr = here;
47562306a36Sopenharmony_ci	eni_out(dma_wr,MID_DMA_WR_RX);
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_citrouble:
47962306a36Sopenharmony_ci	if (paddr)
48062306a36Sopenharmony_ci		dma_unmap_single(&eni_dev->pci_dev->dev,paddr,skb->len,
48162306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
48262306a36Sopenharmony_cidma_map_error:
48362306a36Sopenharmony_ci	if (skb) dev_kfree_skb_irq(skb);
48462306a36Sopenharmony_ci	return -1;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic void discard(struct atm_vcc *vcc,unsigned long size)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
49362306a36Sopenharmony_ci	EVENT("discard (size=%ld)\n",size,0);
49462306a36Sopenharmony_ci	while (do_rx_dma(vcc,NULL,1,size,0)) EVENT("BUSY LOOP",0,0);
49562306a36Sopenharmony_ci	    /* could do a full fallback, but that might be more expensive */
49662306a36Sopenharmony_ci	if (eni_vcc->rxing) ENI_PRV_POS(eni_vcc->last) += size+1;
49762306a36Sopenharmony_ci	else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci/*
50262306a36Sopenharmony_ci * TODO: should check whether direct copies (without DMA setup, dequeuing on
50362306a36Sopenharmony_ci * interrupt, etc.) aren't much faster for AAL0
50462306a36Sopenharmony_ci */
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic int rx_aal0(struct atm_vcc *vcc)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
50962306a36Sopenharmony_ci	unsigned long descr;
51062306a36Sopenharmony_ci	unsigned long length;
51162306a36Sopenharmony_ci	struct sk_buff *skb;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	DPRINTK(">rx_aal0\n");
51462306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
51562306a36Sopenharmony_ci	descr = readl(eni_vcc->recv+eni_vcc->descr*4);
51662306a36Sopenharmony_ci	if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) {
51762306a36Sopenharmony_ci		rx_ident_err(vcc);
51862306a36Sopenharmony_ci		return 1;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci	if (descr & MID_RED_T) {
52162306a36Sopenharmony_ci		DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
52262306a36Sopenharmony_ci		    vcc->dev->number);
52362306a36Sopenharmony_ci		length = 0;
52462306a36Sopenharmony_ci		atomic_inc(&vcc->stats->rx_err);
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	else {
52762306a36Sopenharmony_ci		length = ATM_CELL_SIZE-1; /* no HEC */
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci	skb = length ? atm_alloc_charge(vcc,length,GFP_ATOMIC) : NULL;
53062306a36Sopenharmony_ci	if (!skb) {
53162306a36Sopenharmony_ci		discard(vcc,length >> 2);
53262306a36Sopenharmony_ci		return 0;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci	skb_put(skb,length);
53562306a36Sopenharmony_ci	skb->tstamp = eni_vcc->timestamp;
53662306a36Sopenharmony_ci	DPRINTK("got len %ld\n",length);
53762306a36Sopenharmony_ci	if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1;
53862306a36Sopenharmony_ci	eni_vcc->rxing++;
53962306a36Sopenharmony_ci	return 0;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int rx_aal5(struct atm_vcc *vcc)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
54662306a36Sopenharmony_ci	unsigned long descr;
54762306a36Sopenharmony_ci	unsigned long size,eff,length;
54862306a36Sopenharmony_ci	struct sk_buff *skb;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	EVENT("rx_aal5\n",0,0);
55162306a36Sopenharmony_ci	DPRINTK(">rx_aal5\n");
55262306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
55362306a36Sopenharmony_ci	descr = readl(eni_vcc->recv+eni_vcc->descr*4);
55462306a36Sopenharmony_ci	if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) {
55562306a36Sopenharmony_ci		rx_ident_err(vcc);
55662306a36Sopenharmony_ci		return 1;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci	if (descr & (MID_RED_T | MID_RED_CRC_ERR)) {
55962306a36Sopenharmony_ci		if (descr & MID_RED_T) {
56062306a36Sopenharmony_ci			EVENT("empty cell (descr=0x%lx)\n",descr,0);
56162306a36Sopenharmony_ci			DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
56262306a36Sopenharmony_ci			    vcc->dev->number);
56362306a36Sopenharmony_ci			size = 0;
56462306a36Sopenharmony_ci		}
56562306a36Sopenharmony_ci		else {
56662306a36Sopenharmony_ci			static unsigned long silence = 0;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci			if (time_after(jiffies, silence) || silence == 0) {
56962306a36Sopenharmony_ci				printk(KERN_WARNING DEV_LABEL "(itf %d): "
57062306a36Sopenharmony_ci				    "discarding PDU(s) with CRC error\n",
57162306a36Sopenharmony_ci				    vcc->dev->number);
57262306a36Sopenharmony_ci				silence = (jiffies+2*HZ)|1;
57362306a36Sopenharmony_ci			}
57462306a36Sopenharmony_ci			size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
57562306a36Sopenharmony_ci			EVENT("CRC error (descr=0x%lx,size=%ld)\n",descr,
57662306a36Sopenharmony_ci			    size);
57762306a36Sopenharmony_ci		}
57862306a36Sopenharmony_ci		eff = length = 0;
57962306a36Sopenharmony_ci		atomic_inc(&vcc->stats->rx_err);
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	else {
58262306a36Sopenharmony_ci		size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
58362306a36Sopenharmony_ci		DPRINTK("size=%ld\n",size);
58462306a36Sopenharmony_ci		length = readl(eni_vcc->recv+(((eni_vcc->descr+size-1) &
58562306a36Sopenharmony_ci		    (eni_vcc->words-1)))*4) & 0xffff;
58662306a36Sopenharmony_ci				/* -trailer(2)+header(1) */
58762306a36Sopenharmony_ci		if (length && length <= (size << 2)-8 && length <=
58862306a36Sopenharmony_ci		  ATM_MAX_AAL5_PDU) eff = (length+3) >> 2;
58962306a36Sopenharmony_ci		else {				 /* ^ trailer length (8) */
59062306a36Sopenharmony_ci			EVENT("bad PDU (descr=0x08%lx,length=%ld)\n",descr,
59162306a36Sopenharmony_ci			    length);
59262306a36Sopenharmony_ci			printk(KERN_ERR DEV_LABEL "(itf %d): bad AAL5 PDU "
59362306a36Sopenharmony_ci			    "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n",
59462306a36Sopenharmony_ci			    vcc->dev->number,vcc->vci,length,size << 2,descr);
59562306a36Sopenharmony_ci			length = eff = 0;
59662306a36Sopenharmony_ci			atomic_inc(&vcc->stats->rx_err);
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci	skb = eff ? atm_alloc_charge(vcc,eff << 2,GFP_ATOMIC) : NULL;
60062306a36Sopenharmony_ci	if (!skb) {
60162306a36Sopenharmony_ci		discard(vcc,size);
60262306a36Sopenharmony_ci		return 0;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci	skb_put(skb,length);
60562306a36Sopenharmony_ci	DPRINTK("got len %ld\n",length);
60662306a36Sopenharmony_ci	if (do_rx_dma(vcc,skb,1,size,eff)) return 1;
60762306a36Sopenharmony_ci	eni_vcc->rxing++;
60862306a36Sopenharmony_ci	return 0;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic inline int rx_vcc(struct atm_vcc *vcc)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	void __iomem *vci_dsc;
61562306a36Sopenharmony_ci	unsigned long tmp;
61662306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
61962306a36Sopenharmony_ci	vci_dsc = ENI_DEV(vcc->dev)->vci+vcc->vci*16;
62062306a36Sopenharmony_ci	EVENT("rx_vcc(1)\n",0,0);
62162306a36Sopenharmony_ci	while (eni_vcc->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) >>
62262306a36Sopenharmony_ci	    MID_VCI_DESCR_SHIFT)) {
62362306a36Sopenharmony_ci		EVENT("rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx)\n",
62462306a36Sopenharmony_ci		    eni_vcc->descr,tmp);
62562306a36Sopenharmony_ci		DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr,
62662306a36Sopenharmony_ci		    (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >>
62762306a36Sopenharmony_ci		    MID_VCI_DESCR_SHIFT));
62862306a36Sopenharmony_ci		if (ENI_VCC(vcc)->rx(vcc)) return 1;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci	/* clear IN_SERVICE flag */
63162306a36Sopenharmony_ci	writel(readl(vci_dsc) & ~MID_VCI_IN_SERVICE,vci_dsc);
63262306a36Sopenharmony_ci	/*
63362306a36Sopenharmony_ci	 * If new data has arrived between evaluating the while condition and
63462306a36Sopenharmony_ci	 * clearing IN_SERVICE, we wouldn't be notified until additional data
63562306a36Sopenharmony_ci	 * follows. So we have to loop again to be sure.
63662306a36Sopenharmony_ci	 */
63762306a36Sopenharmony_ci	EVENT("rx_vcc(3)\n",0,0);
63862306a36Sopenharmony_ci	while (ENI_VCC(vcc)->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR)
63962306a36Sopenharmony_ci	    >> MID_VCI_DESCR_SHIFT)) {
64062306a36Sopenharmony_ci		EVENT("rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx)\n",
64162306a36Sopenharmony_ci		    eni_vcc->descr,tmp);
64262306a36Sopenharmony_ci		DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr,
64362306a36Sopenharmony_ci		    (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >>
64462306a36Sopenharmony_ci		    MID_VCI_DESCR_SHIFT));
64562306a36Sopenharmony_ci		if (ENI_VCC(vcc)->rx(vcc)) return 1;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci	return 0;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic void poll_rx(struct atm_dev *dev)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct eni_dev *eni_dev;
65462306a36Sopenharmony_ci	struct atm_vcc *curr;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
65762306a36Sopenharmony_ci	while ((curr = eni_dev->fast)) {
65862306a36Sopenharmony_ci		EVENT("poll_rx.fast\n",0,0);
65962306a36Sopenharmony_ci		if (rx_vcc(curr)) return;
66062306a36Sopenharmony_ci		eni_dev->fast = ENI_VCC(curr)->next;
66162306a36Sopenharmony_ci		ENI_VCC(curr)->next = ENI_VCC_NOS;
66262306a36Sopenharmony_ci		barrier();
66362306a36Sopenharmony_ci		ENI_VCC(curr)->servicing--;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci	while ((curr = eni_dev->slow)) {
66662306a36Sopenharmony_ci		EVENT("poll_rx.slow\n",0,0);
66762306a36Sopenharmony_ci		if (rx_vcc(curr)) return;
66862306a36Sopenharmony_ci		eni_dev->slow = ENI_VCC(curr)->next;
66962306a36Sopenharmony_ci		ENI_VCC(curr)->next = ENI_VCC_NOS;
67062306a36Sopenharmony_ci		barrier();
67162306a36Sopenharmony_ci		ENI_VCC(curr)->servicing--;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic void get_service(struct atm_dev *dev)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct eni_dev *eni_dev;
67962306a36Sopenharmony_ci	struct atm_vcc *vcc;
68062306a36Sopenharmony_ci	unsigned long vci;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	DPRINTK(">get_service\n");
68362306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
68462306a36Sopenharmony_ci	while (eni_in(MID_SERV_WRITE) != eni_dev->serv_read) {
68562306a36Sopenharmony_ci		vci = readl(eni_dev->service+eni_dev->serv_read*4);
68662306a36Sopenharmony_ci		eni_dev->serv_read = (eni_dev->serv_read+1) & (NR_SERVICE-1);
68762306a36Sopenharmony_ci		vcc = eni_dev->rx_map[vci & 1023];
68862306a36Sopenharmony_ci		if (!vcc) {
68962306a36Sopenharmony_ci			printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %ld not "
69062306a36Sopenharmony_ci			    "found\n",dev->number,vci);
69162306a36Sopenharmony_ci			continue; /* nasty but we try to go on anyway */
69262306a36Sopenharmony_ci			/* @@@ nope, doesn't work */
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci		EVENT("getting from service\n",0,0);
69562306a36Sopenharmony_ci		if (ENI_VCC(vcc)->next != ENI_VCC_NOS) {
69662306a36Sopenharmony_ci			EVENT("double service\n",0,0);
69762306a36Sopenharmony_ci			DPRINTK("Grr, servicing VCC %ld twice\n",vci);
69862306a36Sopenharmony_ci			continue;
69962306a36Sopenharmony_ci		}
70062306a36Sopenharmony_ci		ENI_VCC(vcc)->timestamp = ktime_get_real();
70162306a36Sopenharmony_ci		ENI_VCC(vcc)->next = NULL;
70262306a36Sopenharmony_ci		if (vcc->qos.rxtp.traffic_class == ATM_CBR) {
70362306a36Sopenharmony_ci			if (eni_dev->fast)
70462306a36Sopenharmony_ci				ENI_VCC(eni_dev->last_fast)->next = vcc;
70562306a36Sopenharmony_ci			else eni_dev->fast = vcc;
70662306a36Sopenharmony_ci			eni_dev->last_fast = vcc;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci		else {
70962306a36Sopenharmony_ci			if (eni_dev->slow)
71062306a36Sopenharmony_ci				ENI_VCC(eni_dev->last_slow)->next = vcc;
71162306a36Sopenharmony_ci			else eni_dev->slow = vcc;
71262306a36Sopenharmony_ci			eni_dev->last_slow = vcc;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci		putting++;
71562306a36Sopenharmony_ci		ENI_VCC(vcc)->servicing++;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic void dequeue_rx(struct atm_dev *dev)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct eni_dev *eni_dev;
72362306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
72462306a36Sopenharmony_ci	struct atm_vcc *vcc;
72562306a36Sopenharmony_ci	struct sk_buff *skb;
72662306a36Sopenharmony_ci	void __iomem *vci_dsc;
72762306a36Sopenharmony_ci	int first;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
73062306a36Sopenharmony_ci	first = 1;
73162306a36Sopenharmony_ci	while (1) {
73262306a36Sopenharmony_ci		skb = skb_dequeue(&eni_dev->rx_queue);
73362306a36Sopenharmony_ci		if (!skb) {
73462306a36Sopenharmony_ci			if (first) {
73562306a36Sopenharmony_ci				DPRINTK(DEV_LABEL "(itf %d): RX but not "
73662306a36Sopenharmony_ci				    "rxing\n",dev->number);
73762306a36Sopenharmony_ci				EVENT("nothing to dequeue\n",0,0);
73862306a36Sopenharmony_ci			}
73962306a36Sopenharmony_ci			break;
74062306a36Sopenharmony_ci		}
74162306a36Sopenharmony_ci		EVENT("dequeued (size=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb),
74262306a36Sopenharmony_ci		    ENI_PRV_POS(skb));
74362306a36Sopenharmony_ci		rx_dequeued++;
74462306a36Sopenharmony_ci		vcc = ATM_SKB(skb)->vcc;
74562306a36Sopenharmony_ci		eni_vcc = ENI_VCC(vcc);
74662306a36Sopenharmony_ci		first = 0;
74762306a36Sopenharmony_ci		vci_dsc = eni_dev->vci+vcc->vci*16;
74862306a36Sopenharmony_ci		if (!EEPMOK(eni_vcc->rx_pos,ENI_PRV_SIZE(skb),
74962306a36Sopenharmony_ci		    (readl(vci_dsc+4) & MID_VCI_READ) >> MID_VCI_READ_SHIFT,
75062306a36Sopenharmony_ci		    eni_vcc->words)) {
75162306a36Sopenharmony_ci			EVENT("requeuing\n",0,0);
75262306a36Sopenharmony_ci			skb_queue_head(&eni_dev->rx_queue,skb);
75362306a36Sopenharmony_ci			break;
75462306a36Sopenharmony_ci		}
75562306a36Sopenharmony_ci		eni_vcc->rxing--;
75662306a36Sopenharmony_ci		eni_vcc->rx_pos = ENI_PRV_POS(skb) & (eni_vcc->words-1);
75762306a36Sopenharmony_ci		dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len,
75862306a36Sopenharmony_ci			         DMA_TO_DEVICE);
75962306a36Sopenharmony_ci		if (!skb->len) dev_kfree_skb_irq(skb);
76062306a36Sopenharmony_ci		else {
76162306a36Sopenharmony_ci			EVENT("pushing (len=%ld)\n",skb->len,0);
76262306a36Sopenharmony_ci			if (vcc->qos.aal == ATM_AAL0)
76362306a36Sopenharmony_ci				*(unsigned long *) skb->data =
76462306a36Sopenharmony_ci				    ntohl(*(unsigned long *) skb->data);
76562306a36Sopenharmony_ci			memset(skb->cb,0,sizeof(struct eni_skb_prv));
76662306a36Sopenharmony_ci			vcc->push(vcc,skb);
76762306a36Sopenharmony_ci			pushed++;
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci		atomic_inc(&vcc->stats->rx);
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci	wake_up(&eni_dev->rx_wait);
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic int open_rx_first(struct atm_vcc *vcc)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	struct eni_dev *eni_dev;
77862306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
77962306a36Sopenharmony_ci	unsigned long size;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	DPRINTK("open_rx_first\n");
78262306a36Sopenharmony_ci	eni_dev = ENI_DEV(vcc->dev);
78362306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
78462306a36Sopenharmony_ci	eni_vcc->rx = NULL;
78562306a36Sopenharmony_ci	if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;
78662306a36Sopenharmony_ci	size = vcc->qos.rxtp.max_sdu*eni_dev->rx_mult/100;
78762306a36Sopenharmony_ci	if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <=
78862306a36Sopenharmony_ci	    MID_MAX_BUF_SIZE)
78962306a36Sopenharmony_ci		size = MID_MAX_BUF_SIZE;
79062306a36Sopenharmony_ci	eni_vcc->recv = eni_alloc_mem(eni_dev,&size);
79162306a36Sopenharmony_ci	DPRINTK("rx at 0x%lx\n",eni_vcc->recv);
79262306a36Sopenharmony_ci	eni_vcc->words = size >> 2;
79362306a36Sopenharmony_ci	if (!eni_vcc->recv) return -ENOBUFS;
79462306a36Sopenharmony_ci	eni_vcc->rx = vcc->qos.aal == ATM_AAL5 ? rx_aal5 : rx_aal0;
79562306a36Sopenharmony_ci	eni_vcc->descr = 0;
79662306a36Sopenharmony_ci	eni_vcc->rx_pos = 0;
79762306a36Sopenharmony_ci	eni_vcc->rxing = 0;
79862306a36Sopenharmony_ci	eni_vcc->servicing = 0;
79962306a36Sopenharmony_ci	eni_vcc->next = ENI_VCC_NOS;
80062306a36Sopenharmony_ci	return 0;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int open_rx_second(struct atm_vcc *vcc)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	void __iomem *here;
80762306a36Sopenharmony_ci	struct eni_dev *eni_dev;
80862306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
80962306a36Sopenharmony_ci	unsigned long size;
81062306a36Sopenharmony_ci	int order;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	DPRINTK("open_rx_second\n");
81362306a36Sopenharmony_ci	eni_dev = ENI_DEV(vcc->dev);
81462306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
81562306a36Sopenharmony_ci	if (!eni_vcc->rx) return 0;
81662306a36Sopenharmony_ci	/* set up VCI descriptor */
81762306a36Sopenharmony_ci	here = eni_dev->vci+vcc->vci*16;
81862306a36Sopenharmony_ci	DPRINTK("loc 0x%x\n",(unsigned) (eni_vcc->recv-eni_dev->ram)/4);
81962306a36Sopenharmony_ci	size = eni_vcc->words >> 8;
82062306a36Sopenharmony_ci	for (order = -1; size; order++) size >>= 1;
82162306a36Sopenharmony_ci	writel(0,here+4); /* descr, read = 0 */
82262306a36Sopenharmony_ci	writel(0,here+8); /* write, state, count = 0 */
82362306a36Sopenharmony_ci	if (eni_dev->rx_map[vcc->vci])
82462306a36Sopenharmony_ci		printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already "
82562306a36Sopenharmony_ci		    "in use\n",vcc->dev->number,vcc->vci);
82662306a36Sopenharmony_ci	eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */
82762306a36Sopenharmony_ci	writel(((vcc->qos.aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) <<
82862306a36Sopenharmony_ci	    MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE |
82962306a36Sopenharmony_ci	    (((eni_vcc->recv-eni_dev->ram) >> (MID_LOC_SKIP+2)) <<
83062306a36Sopenharmony_ci	    MID_VCI_LOCATION_SHIFT) | (order << MID_VCI_SIZE_SHIFT),here);
83162306a36Sopenharmony_ci	return 0;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic void close_rx(struct atm_vcc *vcc)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait,current);
83862306a36Sopenharmony_ci	void __iomem *here;
83962306a36Sopenharmony_ci	struct eni_dev *eni_dev;
84062306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
84362306a36Sopenharmony_ci	if (!eni_vcc->rx) return;
84462306a36Sopenharmony_ci	eni_dev = ENI_DEV(vcc->dev);
84562306a36Sopenharmony_ci	if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
84662306a36Sopenharmony_ci		here = eni_dev->vci+vcc->vci*16;
84762306a36Sopenharmony_ci		/* block receiver */
84862306a36Sopenharmony_ci		writel((readl(here) & ~MID_VCI_MODE) | (MID_MODE_TRASH <<
84962306a36Sopenharmony_ci		    MID_VCI_MODE_SHIFT),here);
85062306a36Sopenharmony_ci		/* wait for receiver to become idle */
85162306a36Sopenharmony_ci		udelay(27);
85262306a36Sopenharmony_ci		/* discard pending cell */
85362306a36Sopenharmony_ci		writel(readl(here) & ~MID_VCI_IN_SERVICE,here);
85462306a36Sopenharmony_ci		/* don't accept any new ones */
85562306a36Sopenharmony_ci		eni_dev->rx_map[vcc->vci] = NULL;
85662306a36Sopenharmony_ci		/* wait for RX queue to drain */
85762306a36Sopenharmony_ci		DPRINTK("eni_close: waiting for RX ...\n");
85862306a36Sopenharmony_ci		EVENT("RX closing\n",0,0);
85962306a36Sopenharmony_ci		add_wait_queue(&eni_dev->rx_wait,&wait);
86062306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
86162306a36Sopenharmony_ci		barrier();
86262306a36Sopenharmony_ci		for (;;) {
86362306a36Sopenharmony_ci			/* transition service->rx: rxing++, servicing-- */
86462306a36Sopenharmony_ci			if (!eni_vcc->servicing) {
86562306a36Sopenharmony_ci				barrier();
86662306a36Sopenharmony_ci				if (!eni_vcc->rxing) break;
86762306a36Sopenharmony_ci			}
86862306a36Sopenharmony_ci			EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing,
86962306a36Sopenharmony_ci			    eni_vcc->servicing);
87062306a36Sopenharmony_ci			printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing,
87162306a36Sopenharmony_ci			    eni_vcc->rxing);
87262306a36Sopenharmony_ci			schedule();
87362306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci		for (;;) {
87662306a36Sopenharmony_ci			int at_end;
87762306a36Sopenharmony_ci			u32 tmp;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci			tasklet_disable(&eni_dev->task);
88062306a36Sopenharmony_ci			tmp = readl(eni_dev->vci+vcc->vci*16+4) & MID_VCI_READ;
88162306a36Sopenharmony_ci			at_end = eni_vcc->rx_pos == tmp >> MID_VCI_READ_SHIFT;
88262306a36Sopenharmony_ci			tasklet_enable(&eni_dev->task);
88362306a36Sopenharmony_ci			if (at_end) break;
88462306a36Sopenharmony_ci			EVENT("drain discard (host 0x%lx, nic 0x%lx)\n",
88562306a36Sopenharmony_ci			    eni_vcc->rx_pos,tmp);
88662306a36Sopenharmony_ci			printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%x\n",
88762306a36Sopenharmony_ci			    eni_vcc->rx_pos,tmp);
88862306a36Sopenharmony_ci			schedule();
88962306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
89062306a36Sopenharmony_ci		}
89162306a36Sopenharmony_ci		set_current_state(TASK_RUNNING);
89262306a36Sopenharmony_ci		remove_wait_queue(&eni_dev->rx_wait,&wait);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci	eni_free_mem(eni_dev,eni_vcc->recv,eni_vcc->words << 2);
89562306a36Sopenharmony_ci	eni_vcc->rx = NULL;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic int start_rx(struct atm_dev *dev)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	struct eni_dev *eni_dev;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
90462306a36Sopenharmony_ci	eni_dev->rx_map = (struct atm_vcc **) get_zeroed_page(GFP_KERNEL);
90562306a36Sopenharmony_ci	if (!eni_dev->rx_map) {
90662306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
90762306a36Sopenharmony_ci		    dev->number);
90862306a36Sopenharmony_ci		free_page((unsigned long) eni_dev->free_list);
90962306a36Sopenharmony_ci		return -ENOMEM;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci	eni_dev->rx_mult = DEFAULT_RX_MULT;
91262306a36Sopenharmony_ci	eni_dev->fast = eni_dev->last_fast = NULL;
91362306a36Sopenharmony_ci	eni_dev->slow = eni_dev->last_slow = NULL;
91462306a36Sopenharmony_ci	init_waitqueue_head(&eni_dev->rx_wait);
91562306a36Sopenharmony_ci	skb_queue_head_init(&eni_dev->rx_queue);
91662306a36Sopenharmony_ci	eni_dev->serv_read = eni_in(MID_SERV_WRITE);
91762306a36Sopenharmony_ci	eni_out(0,MID_DMA_WR_RX);
91862306a36Sopenharmony_ci	return 0;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci/*----------------------------------- TX ------------------------------------*/
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cienum enq_res { enq_ok,enq_next,enq_jam };
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic inline void put_dma(int chan,u32 *dma,int *j,dma_addr_t paddr,
92962306a36Sopenharmony_ci    u32 size)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	u32 init,words;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	DPRINTK("put_dma: 0x%lx+0x%x\n",(unsigned long) paddr,size);
93462306a36Sopenharmony_ci	EVENT("put_dma: 0x%lx+0x%lx\n",(unsigned long) paddr,size);
93562306a36Sopenharmony_ci#if 0 /* don't complain anymore */
93662306a36Sopenharmony_ci	if (paddr & 3)
93762306a36Sopenharmony_ci		printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr);
93862306a36Sopenharmony_ci	if (size & 3)
93962306a36Sopenharmony_ci		printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size);
94062306a36Sopenharmony_ci#endif
94162306a36Sopenharmony_ci	if (paddr & 3) {
94262306a36Sopenharmony_ci		init = 4-(paddr & 3);
94362306a36Sopenharmony_ci		if (init > size || size < 7) init = size;
94462306a36Sopenharmony_ci		DPRINTK("put_dma: %lx DMA: %d/%d bytes\n",
94562306a36Sopenharmony_ci		    (unsigned long) paddr,init,size);
94662306a36Sopenharmony_ci		dma[(*j)++] = MID_DT_BYTE | (init << MID_DMA_COUNT_SHIFT) |
94762306a36Sopenharmony_ci		    (chan << MID_DMA_CHAN_SHIFT);
94862306a36Sopenharmony_ci		dma[(*j)++] = paddr;
94962306a36Sopenharmony_ci		paddr += init;
95062306a36Sopenharmony_ci		size -= init;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci	words = size >> 2;
95362306a36Sopenharmony_ci	size &= 3;
95462306a36Sopenharmony_ci	if (words && (paddr & 31)) {
95562306a36Sopenharmony_ci		init = 8-((paddr & 31) >> 2);
95662306a36Sopenharmony_ci		if (init > words) init = words;
95762306a36Sopenharmony_ci		DPRINTK("put_dma: %lx DMA: %d/%d words\n",
95862306a36Sopenharmony_ci		    (unsigned long) paddr,init,words);
95962306a36Sopenharmony_ci		dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
96062306a36Sopenharmony_ci		    (chan << MID_DMA_CHAN_SHIFT);
96162306a36Sopenharmony_ci		dma[(*j)++] = paddr;
96262306a36Sopenharmony_ci		paddr += init << 2;
96362306a36Sopenharmony_ci		words -= init;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */
96662306a36Sopenharmony_ci	if (words & ~15) {
96762306a36Sopenharmony_ci		DPRINTK("put_dma: %lx DMA: %d*16/%d words\n",
96862306a36Sopenharmony_ci		    (unsigned long) paddr,words >> 4,words);
96962306a36Sopenharmony_ci		dma[(*j)++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT)
97062306a36Sopenharmony_ci		    | (chan << MID_DMA_CHAN_SHIFT);
97162306a36Sopenharmony_ci		dma[(*j)++] = paddr;
97262306a36Sopenharmony_ci		paddr += (words & ~15) << 2;
97362306a36Sopenharmony_ci		words &= 15;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci#endif
97662306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */
97762306a36Sopenharmony_ci	if (words & ~7) {
97862306a36Sopenharmony_ci		DPRINTK("put_dma: %lx DMA: %d*8/%d words\n",
97962306a36Sopenharmony_ci		    (unsigned long) paddr,words >> 3,words);
98062306a36Sopenharmony_ci		dma[(*j)++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT)
98162306a36Sopenharmony_ci		    | (chan << MID_DMA_CHAN_SHIFT);
98262306a36Sopenharmony_ci		dma[(*j)++] = paddr;
98362306a36Sopenharmony_ci		paddr += (words & ~7) << 2;
98462306a36Sopenharmony_ci		words &= 7;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci#endif
98762306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */
98862306a36Sopenharmony_ci	if (words & ~3) {
98962306a36Sopenharmony_ci		DPRINTK("put_dma: %lx DMA: %d*4/%d words\n",
99062306a36Sopenharmony_ci		    (unsigned long) paddr,words >> 2,words);
99162306a36Sopenharmony_ci		dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT)
99262306a36Sopenharmony_ci		    | (chan << MID_DMA_CHAN_SHIFT);
99362306a36Sopenharmony_ci		dma[(*j)++] = paddr;
99462306a36Sopenharmony_ci		paddr += (words & ~3) << 2;
99562306a36Sopenharmony_ci		words &= 3;
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci#endif
99862306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */
99962306a36Sopenharmony_ci	if (words & ~1) {
100062306a36Sopenharmony_ci		DPRINTK("put_dma: %lx DMA: %d*2/%d words\n",
100162306a36Sopenharmony_ci		    (unsigned long) paddr,words >> 1,words);
100262306a36Sopenharmony_ci		dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT)
100362306a36Sopenharmony_ci		    | (chan << MID_DMA_CHAN_SHIFT);
100462306a36Sopenharmony_ci		dma[(*j)++] = paddr;
100562306a36Sopenharmony_ci		paddr += (words & ~1) << 2;
100662306a36Sopenharmony_ci		words &= 1;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci#endif
100962306a36Sopenharmony_ci	if (words) {
101062306a36Sopenharmony_ci		DPRINTK("put_dma: %lx DMA: %d words\n",(unsigned long) paddr,
101162306a36Sopenharmony_ci		    words);
101262306a36Sopenharmony_ci		dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) |
101362306a36Sopenharmony_ci		    (chan << MID_DMA_CHAN_SHIFT);
101462306a36Sopenharmony_ci		dma[(*j)++] = paddr;
101562306a36Sopenharmony_ci		paddr += words << 2;
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci	if (size) {
101862306a36Sopenharmony_ci		DPRINTK("put_dma: %lx DMA: %d bytes\n",(unsigned long) paddr,
101962306a36Sopenharmony_ci		    size);
102062306a36Sopenharmony_ci		dma[(*j)++] = MID_DT_BYTE | (size << MID_DMA_COUNT_SHIFT) |
102162306a36Sopenharmony_ci		    (chan << MID_DMA_CHAN_SHIFT);
102262306a36Sopenharmony_ci		dma[(*j)++] = paddr;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic enum enq_res do_tx(struct sk_buff *skb)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	struct atm_vcc *vcc;
103062306a36Sopenharmony_ci	struct eni_dev *eni_dev;
103162306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
103262306a36Sopenharmony_ci	struct eni_tx *tx;
103362306a36Sopenharmony_ci	dma_addr_t paddr;
103462306a36Sopenharmony_ci	u32 dma_rd,dma_wr;
103562306a36Sopenharmony_ci	u32 size; /* in words */
103662306a36Sopenharmony_ci	int aal5,dma_size,i,j;
103762306a36Sopenharmony_ci	unsigned char skb_data3;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	DPRINTK(">do_tx\n");
104062306a36Sopenharmony_ci	NULLCHECK(skb);
104162306a36Sopenharmony_ci	EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len);
104262306a36Sopenharmony_ci	vcc = ATM_SKB(skb)->vcc;
104362306a36Sopenharmony_ci	NULLCHECK(vcc);
104462306a36Sopenharmony_ci	eni_dev = ENI_DEV(vcc->dev);
104562306a36Sopenharmony_ci	NULLCHECK(eni_dev);
104662306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
104762306a36Sopenharmony_ci	tx = eni_vcc->tx;
104862306a36Sopenharmony_ci	NULLCHECK(tx);
104962306a36Sopenharmony_ci#if 0 /* Enable this for testing with the "align" program */
105062306a36Sopenharmony_ci	{
105162306a36Sopenharmony_ci		unsigned int hack = *((char *) skb->data)-'0';
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci		if (hack < 8) {
105462306a36Sopenharmony_ci			skb->data += hack;
105562306a36Sopenharmony_ci			skb->len -= hack;
105662306a36Sopenharmony_ci		}
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci#endif
105962306a36Sopenharmony_ci#if 0 /* should work now */
106062306a36Sopenharmony_ci	if ((unsigned long) skb->data & 3)
106162306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): VCI %d has mis-aligned "
106262306a36Sopenharmony_ci		    "TX data\n",vcc->dev->number,vcc->vci);
106362306a36Sopenharmony_ci#endif
106462306a36Sopenharmony_ci	/*
106562306a36Sopenharmony_ci	 * Potential future IP speedup: make hard_header big enough to put
106662306a36Sopenharmony_ci	 * segmentation descriptor directly into PDU. Saves: 4 slave writes,
106762306a36Sopenharmony_ci	 * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-)
106862306a36Sopenharmony_ci	 */
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	aal5 = vcc->qos.aal == ATM_AAL5;
107162306a36Sopenharmony_ci	/* check space in buffer */
107262306a36Sopenharmony_ci	if (!aal5)
107362306a36Sopenharmony_ci		size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE;
107462306a36Sopenharmony_ci			/* cell without HEC plus segmentation header (includes
107562306a36Sopenharmony_ci			   four-byte cell header) */
107662306a36Sopenharmony_ci	else {
107762306a36Sopenharmony_ci		size = skb->len+4*AAL5_TRAILER+ATM_CELL_PAYLOAD-1;
107862306a36Sopenharmony_ci			/* add AAL5 trailer */
107962306a36Sopenharmony_ci		size = ((size-(size % ATM_CELL_PAYLOAD)) >> 2)+TX_DESCR_SIZE;
108062306a36Sopenharmony_ci						/* add segmentation header */
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci	/*
108362306a36Sopenharmony_ci	 * Can I move tx_pos by size bytes without getting closer than TX_GAP
108462306a36Sopenharmony_ci	 * to the read pointer ? TX_GAP means to leave some space for what
108562306a36Sopenharmony_ci	 * the manual calls "too close".
108662306a36Sopenharmony_ci	 */
108762306a36Sopenharmony_ci	if (!NEPMOK(tx->tx_pos,size+TX_GAP,
108862306a36Sopenharmony_ci	    eni_in(MID_TX_RDPTR(tx->index)),tx->words)) {
108962306a36Sopenharmony_ci		DPRINTK(DEV_LABEL "(itf %d): TX full (size %d)\n",
109062306a36Sopenharmony_ci		    vcc->dev->number,size);
109162306a36Sopenharmony_ci		return enq_next;
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci	/* check DMA */
109462306a36Sopenharmony_ci	dma_wr = eni_in(MID_DMA_WR_TX);
109562306a36Sopenharmony_ci	dma_rd = eni_in(MID_DMA_RD_TX);
109662306a36Sopenharmony_ci	dma_size = 3; /* JK for descriptor and final fill, plus final size
109762306a36Sopenharmony_ci			 mis-alignment fix */
109862306a36Sopenharmony_ciDPRINTK("iovcnt = %d\n",skb_shinfo(skb)->nr_frags);
109962306a36Sopenharmony_ci	if (!skb_shinfo(skb)->nr_frags) dma_size += 5;
110062306a36Sopenharmony_ci	else dma_size += 5*(skb_shinfo(skb)->nr_frags+1);
110162306a36Sopenharmony_ci	if (dma_size > TX_DMA_BUF) {
110262306a36Sopenharmony_ci		printk(KERN_CRIT DEV_LABEL "(itf %d): needs %d DMA entries "
110362306a36Sopenharmony_ci		    "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF);
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci	DPRINTK("dma_wr is %d, tx_pos is %ld\n",dma_wr,tx->tx_pos);
110662306a36Sopenharmony_ci	if (dma_wr != dma_rd && ((dma_rd+NR_DMA_TX-dma_wr) & (NR_DMA_TX-1)) <
110762306a36Sopenharmony_ci	     dma_size) {
110862306a36Sopenharmony_ci		printk(KERN_WARNING DEV_LABEL "(itf %d): TX DMA full\n",
110962306a36Sopenharmony_ci		    vcc->dev->number);
111062306a36Sopenharmony_ci		return enq_jam;
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci	skb_data3 = skb->data[3];
111362306a36Sopenharmony_ci	paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len,
111462306a36Sopenharmony_ci			       DMA_TO_DEVICE);
111562306a36Sopenharmony_ci	if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr))
111662306a36Sopenharmony_ci		return enq_next;
111762306a36Sopenharmony_ci	ENI_PRV_PADDR(skb) = paddr;
111862306a36Sopenharmony_ci	/* prepare DMA queue entries */
111962306a36Sopenharmony_ci	j = 0;
112062306a36Sopenharmony_ci	eni_dev->dma[j++] = (((tx->tx_pos+TX_DESCR_SIZE) & (tx->words-1)) <<
112162306a36Sopenharmony_ci	     MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
112262306a36Sopenharmony_ci	     MID_DT_JK;
112362306a36Sopenharmony_ci	j++;
112462306a36Sopenharmony_ci	if (!skb_shinfo(skb)->nr_frags)
112562306a36Sopenharmony_ci		if (aal5) put_dma(tx->index,eni_dev->dma,&j,paddr,skb->len);
112662306a36Sopenharmony_ci		else put_dma(tx->index,eni_dev->dma,&j,paddr+4,skb->len-4);
112762306a36Sopenharmony_ci	else {
112862306a36Sopenharmony_ciDPRINTK("doing direct send\n"); /* @@@ well, this doesn't work anyway */
112962306a36Sopenharmony_ci		for (i = -1; i < skb_shinfo(skb)->nr_frags; i++)
113062306a36Sopenharmony_ci			if (i == -1)
113162306a36Sopenharmony_ci				put_dma(tx->index,eni_dev->dma,&j,(unsigned long)
113262306a36Sopenharmony_ci				    skb->data,
113362306a36Sopenharmony_ci				    skb_headlen(skb));
113462306a36Sopenharmony_ci			else
113562306a36Sopenharmony_ci				put_dma(tx->index,eni_dev->dma,&j,(unsigned long)
113662306a36Sopenharmony_ci				    skb_frag_page(&skb_shinfo(skb)->frags[i]) +
113762306a36Sopenharmony_ci					skb_frag_off(&skb_shinfo(skb)->frags[i]),
113862306a36Sopenharmony_ci				    skb_frag_size(&skb_shinfo(skb)->frags[i]));
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci	if (skb->len & 3) {
114162306a36Sopenharmony_ci		put_dma(tx->index, eni_dev->dma, &j, eni_dev->zero.dma,
114262306a36Sopenharmony_ci			4 - (skb->len & 3));
114362306a36Sopenharmony_ci	}
114462306a36Sopenharmony_ci	/* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */
114562306a36Sopenharmony_ci	eni_dev->dma[j++] = (((tx->tx_pos+size) & (tx->words-1)) <<
114662306a36Sopenharmony_ci	     MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
114762306a36Sopenharmony_ci	     MID_DMA_END | MID_DT_JK;
114862306a36Sopenharmony_ci	j++;
114962306a36Sopenharmony_ci	DPRINTK("DMA at end: %d\n",j);
115062306a36Sopenharmony_ci	/* store frame */
115162306a36Sopenharmony_ci	writel((MID_SEG_TX_ID << MID_SEG_ID_SHIFT) |
115262306a36Sopenharmony_ci	    (aal5 ? MID_SEG_AAL5 : 0) | (tx->prescaler << MID_SEG_PR_SHIFT) |
115362306a36Sopenharmony_ci	    (tx->resolution << MID_SEG_RATE_SHIFT) |
115462306a36Sopenharmony_ci	    (size/(ATM_CELL_PAYLOAD/4)),tx->send+tx->tx_pos*4);
115562306a36Sopenharmony_ci/*printk("dsc = 0x%08lx\n",(unsigned long) readl(tx->send+tx->tx_pos*4));*/
115662306a36Sopenharmony_ci	writel((vcc->vci << MID_SEG_VCI_SHIFT) |
115762306a36Sopenharmony_ci            (aal5 ? 0 : (skb_data3 & 0xf)) |
115862306a36Sopenharmony_ci	    (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0),
115962306a36Sopenharmony_ci	    tx->send+((tx->tx_pos+1) & (tx->words-1))*4);
116062306a36Sopenharmony_ci	DPRINTK("size: %d, len:%d\n",size,skb->len);
116162306a36Sopenharmony_ci	if (aal5)
116262306a36Sopenharmony_ci		writel(skb->len,tx->send+
116362306a36Sopenharmony_ci                    ((tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1))*4);
116462306a36Sopenharmony_ci	j = j >> 1;
116562306a36Sopenharmony_ci	for (i = 0; i < j; i++) {
116662306a36Sopenharmony_ci		writel(eni_dev->dma[i*2],eni_dev->tx_dma+dma_wr*8);
116762306a36Sopenharmony_ci		writel(eni_dev->dma[i*2+1],eni_dev->tx_dma+dma_wr*8+4);
116862306a36Sopenharmony_ci		dma_wr = (dma_wr+1) & (NR_DMA_TX-1);
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci	ENI_PRV_POS(skb) = tx->tx_pos;
117162306a36Sopenharmony_ci	ENI_PRV_SIZE(skb) = size;
117262306a36Sopenharmony_ci	ENI_VCC(vcc)->txing += size;
117362306a36Sopenharmony_ci	tx->tx_pos = (tx->tx_pos+size) & (tx->words-1);
117462306a36Sopenharmony_ci	DPRINTK("dma_wr set to %d, tx_pos is now %ld\n",dma_wr,tx->tx_pos);
117562306a36Sopenharmony_ci	eni_out(dma_wr,MID_DMA_WR_TX);
117662306a36Sopenharmony_ci	skb_queue_tail(&eni_dev->tx_queue,skb);
117762306a36Sopenharmony_ci	queued++;
117862306a36Sopenharmony_ci	return enq_ok;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic void poll_tx(struct atm_dev *dev)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct eni_tx *tx;
118562306a36Sopenharmony_ci	struct sk_buff *skb;
118662306a36Sopenharmony_ci	enum enq_res res;
118762306a36Sopenharmony_ci	int i;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	DPRINTK(">poll_tx\n");
119062306a36Sopenharmony_ci	for (i = NR_CHAN-1; i >= 0; i--) {
119162306a36Sopenharmony_ci		tx = &ENI_DEV(dev)->tx[i];
119262306a36Sopenharmony_ci		if (tx->send)
119362306a36Sopenharmony_ci			while ((skb = skb_dequeue(&tx->backlog))) {
119462306a36Sopenharmony_ci				res = do_tx(skb);
119562306a36Sopenharmony_ci				if (res == enq_ok) continue;
119662306a36Sopenharmony_ci				DPRINTK("re-queuing TX PDU\n");
119762306a36Sopenharmony_ci				skb_queue_head(&tx->backlog,skb);
119862306a36Sopenharmony_ci				requeued++;
119962306a36Sopenharmony_ci				if (res == enq_jam) return;
120062306a36Sopenharmony_ci				break;
120162306a36Sopenharmony_ci			}
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_cistatic void dequeue_tx(struct atm_dev *dev)
120762306a36Sopenharmony_ci{
120862306a36Sopenharmony_ci	struct eni_dev *eni_dev;
120962306a36Sopenharmony_ci	struct atm_vcc *vcc;
121062306a36Sopenharmony_ci	struct sk_buff *skb;
121162306a36Sopenharmony_ci	struct eni_tx *tx;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	NULLCHECK(dev);
121462306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
121562306a36Sopenharmony_ci	NULLCHECK(eni_dev);
121662306a36Sopenharmony_ci	while ((skb = skb_dequeue(&eni_dev->tx_queue))) {
121762306a36Sopenharmony_ci		vcc = ATM_SKB(skb)->vcc;
121862306a36Sopenharmony_ci		NULLCHECK(vcc);
121962306a36Sopenharmony_ci		tx = ENI_VCC(vcc)->tx;
122062306a36Sopenharmony_ci		NULLCHECK(ENI_VCC(vcc)->tx);
122162306a36Sopenharmony_ci		DPRINTK("dequeue_tx: next 0x%lx curr 0x%x\n",ENI_PRV_POS(skb),
122262306a36Sopenharmony_ci		    (unsigned) eni_in(MID_TX_DESCRSTART(tx->index)));
122362306a36Sopenharmony_ci		if (ENI_VCC(vcc)->txing < tx->words && ENI_PRV_POS(skb) ==
122462306a36Sopenharmony_ci		    eni_in(MID_TX_DESCRSTART(tx->index))) {
122562306a36Sopenharmony_ci			skb_queue_head(&eni_dev->tx_queue,skb);
122662306a36Sopenharmony_ci			break;
122762306a36Sopenharmony_ci		}
122862306a36Sopenharmony_ci		ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb);
122962306a36Sopenharmony_ci		dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len,
123062306a36Sopenharmony_ci				 DMA_TO_DEVICE);
123162306a36Sopenharmony_ci		if (vcc->pop) vcc->pop(vcc,skb);
123262306a36Sopenharmony_ci		else dev_kfree_skb_irq(skb);
123362306a36Sopenharmony_ci		atomic_inc(&vcc->stats->tx);
123462306a36Sopenharmony_ci		wake_up(&eni_dev->tx_wait);
123562306a36Sopenharmony_ci		dma_complete++;
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic struct eni_tx *alloc_tx(struct eni_dev *eni_dev,int ubr)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	int i;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	for (i = !ubr; i < NR_CHAN; i++)
124562306a36Sopenharmony_ci		if (!eni_dev->tx[i].send) return eni_dev->tx+i;
124662306a36Sopenharmony_ci	return NULL;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cistatic int comp_tx(struct eni_dev *eni_dev,int *pcr,int reserved,int *pre,
125162306a36Sopenharmony_ci    int *res,int unlimited)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	static const int pre_div[] = { 4,16,128,2048 };
125462306a36Sopenharmony_ci	    /* 2^(((x+2)^2-(x+2))/2+1) */
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (unlimited) *pre = *res = 0;
125762306a36Sopenharmony_ci	else {
125862306a36Sopenharmony_ci		if (*pcr > 0) {
125962306a36Sopenharmony_ci			int div;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci			for (*pre = 0; *pre < 3; (*pre)++)
126262306a36Sopenharmony_ci				if (TS_CLOCK/pre_div[*pre]/64 <= *pcr) break;
126362306a36Sopenharmony_ci			div = pre_div[*pre]**pcr;
126462306a36Sopenharmony_ci			DPRINTK("min div %d\n",div);
126562306a36Sopenharmony_ci			*res = TS_CLOCK/div-1;
126662306a36Sopenharmony_ci		}
126762306a36Sopenharmony_ci		else {
126862306a36Sopenharmony_ci			int div;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci			if (!*pcr) *pcr = eni_dev->tx_bw+reserved;
127162306a36Sopenharmony_ci			for (*pre = 3; *pre >= 0; (*pre)--)
127262306a36Sopenharmony_ci				if (TS_CLOCK/pre_div[*pre]/64 > -*pcr) break;
127362306a36Sopenharmony_ci			if (*pre < 3) (*pre)++; /* else fail later */
127462306a36Sopenharmony_ci			div = pre_div[*pre]*-*pcr;
127562306a36Sopenharmony_ci			DPRINTK("max div %d\n",div);
127662306a36Sopenharmony_ci			*res = DIV_ROUND_UP(TS_CLOCK, div)-1;
127762306a36Sopenharmony_ci		}
127862306a36Sopenharmony_ci		if (*res < 0) *res = 0;
127962306a36Sopenharmony_ci		if (*res > MID_SEG_MAX_RATE) *res = MID_SEG_MAX_RATE;
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci	*pcr = TS_CLOCK/pre_div[*pre]/(*res+1);
128262306a36Sopenharmony_ci	DPRINTK("out pcr: %d (%d:%d)\n",*pcr,*pre,*res);
128362306a36Sopenharmony_ci	return 0;
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_cistatic int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp,
128862306a36Sopenharmony_ci    int set_rsv,int set_shp)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct eni_dev *eni_dev = ENI_DEV(vcc->dev);
129162306a36Sopenharmony_ci	struct eni_vcc *eni_vcc = ENI_VCC(vcc);
129262306a36Sopenharmony_ci	struct eni_tx *tx;
129362306a36Sopenharmony_ci	unsigned long size;
129462306a36Sopenharmony_ci	void __iomem *mem;
129562306a36Sopenharmony_ci	int rate,ubr,unlimited,new_tx;
129662306a36Sopenharmony_ci	int pre,res,order;
129762306a36Sopenharmony_ci	int error;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	rate = atm_pcr_goal(txtp);
130062306a36Sopenharmony_ci	ubr = txtp->traffic_class == ATM_UBR;
130162306a36Sopenharmony_ci	unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR ||
130262306a36Sopenharmony_ci	    rate >= ATM_OC3_PCR);
130362306a36Sopenharmony_ci	if (!unlimited) {
130462306a36Sopenharmony_ci		size = txtp->max_sdu*eni_dev->tx_mult/100;
130562306a36Sopenharmony_ci		if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <=
130662306a36Sopenharmony_ci		    MID_MAX_BUF_SIZE)
130762306a36Sopenharmony_ci			size = MID_MAX_BUF_SIZE;
130862306a36Sopenharmony_ci	}
130962306a36Sopenharmony_ci	else {
131062306a36Sopenharmony_ci		if (eni_dev->ubr) {
131162306a36Sopenharmony_ci			eni_vcc->tx = eni_dev->ubr;
131262306a36Sopenharmony_ci			txtp->pcr = ATM_OC3_PCR;
131362306a36Sopenharmony_ci			return 0;
131462306a36Sopenharmony_ci		}
131562306a36Sopenharmony_ci		size = UBR_BUFFER;
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci	new_tx = !eni_vcc->tx;
131862306a36Sopenharmony_ci	mem = NULL; /* for gcc */
131962306a36Sopenharmony_ci	if (!new_tx) tx = eni_vcc->tx;
132062306a36Sopenharmony_ci	else {
132162306a36Sopenharmony_ci		mem = eni_alloc_mem(eni_dev,&size);
132262306a36Sopenharmony_ci		if (!mem) return -ENOBUFS;
132362306a36Sopenharmony_ci		tx = alloc_tx(eni_dev,unlimited);
132462306a36Sopenharmony_ci		if (!tx) {
132562306a36Sopenharmony_ci			eni_free_mem(eni_dev,mem,size);
132662306a36Sopenharmony_ci			return -EBUSY;
132762306a36Sopenharmony_ci		}
132862306a36Sopenharmony_ci		DPRINTK("got chan %d\n",tx->index);
132962306a36Sopenharmony_ci		tx->reserved = tx->shaping = 0;
133062306a36Sopenharmony_ci		tx->send = mem;
133162306a36Sopenharmony_ci		tx->words = size >> 2;
133262306a36Sopenharmony_ci		skb_queue_head_init(&tx->backlog);
133362306a36Sopenharmony_ci		for (order = 0; size > (1 << (order+10)); order++);
133462306a36Sopenharmony_ci		eni_out((order << MID_SIZE_SHIFT) |
133562306a36Sopenharmony_ci		    ((tx->send-eni_dev->ram) >> (MID_LOC_SKIP+2)),
133662306a36Sopenharmony_ci		    MID_TX_PLACE(tx->index));
133762306a36Sopenharmony_ci		tx->tx_pos = eni_in(MID_TX_DESCRSTART(tx->index)) &
133862306a36Sopenharmony_ci		    MID_DESCR_START;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci	error = comp_tx(eni_dev,&rate,tx->reserved,&pre,&res,unlimited);
134162306a36Sopenharmony_ci	if (!error  && txtp->min_pcr > rate) error = -EINVAL;
134262306a36Sopenharmony_ci	if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR &&
134362306a36Sopenharmony_ci	    txtp->max_pcr < rate) error = -EINVAL;
134462306a36Sopenharmony_ci	if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved)
134562306a36Sopenharmony_ci		error = -EINVAL;
134662306a36Sopenharmony_ci	if (!error && set_rsv && !set_shp && rate < tx->shaping)
134762306a36Sopenharmony_ci		error = -EINVAL;
134862306a36Sopenharmony_ci	if (!error && !set_rsv && rate > tx->reserved && !ubr)
134962306a36Sopenharmony_ci		error = -EINVAL;
135062306a36Sopenharmony_ci	if (error) {
135162306a36Sopenharmony_ci		if (new_tx) {
135262306a36Sopenharmony_ci			tx->send = NULL;
135362306a36Sopenharmony_ci			eni_free_mem(eni_dev,mem,size);
135462306a36Sopenharmony_ci		}
135562306a36Sopenharmony_ci		return error;
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci	txtp->pcr = rate;
135862306a36Sopenharmony_ci	if (set_rsv && !ubr) {
135962306a36Sopenharmony_ci		eni_dev->tx_bw += tx->reserved;
136062306a36Sopenharmony_ci		tx->reserved = rate;
136162306a36Sopenharmony_ci		eni_dev->tx_bw -= rate;
136262306a36Sopenharmony_ci	}
136362306a36Sopenharmony_ci	if (set_shp || (unlimited && new_tx)) {
136462306a36Sopenharmony_ci		if (unlimited && new_tx) eni_dev->ubr = tx;
136562306a36Sopenharmony_ci		tx->prescaler = pre;
136662306a36Sopenharmony_ci		tx->resolution = res;
136762306a36Sopenharmony_ci		tx->shaping = rate;
136862306a36Sopenharmony_ci	}
136962306a36Sopenharmony_ci	if (set_shp) eni_vcc->tx = tx;
137062306a36Sopenharmony_ci	DPRINTK("rsv %d shp %d\n",tx->reserved,tx->shaping);
137162306a36Sopenharmony_ci	return 0;
137262306a36Sopenharmony_ci}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistatic int open_tx_first(struct atm_vcc *vcc)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	ENI_VCC(vcc)->tx = NULL;
137862306a36Sopenharmony_ci	if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
137962306a36Sopenharmony_ci	ENI_VCC(vcc)->txing = 0;
138062306a36Sopenharmony_ci	return reserve_or_set_tx(vcc,&vcc->qos.txtp,1,1);
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic int open_tx_second(struct atm_vcc *vcc)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	return 0; /* nothing to do */
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic void close_tx(struct atm_vcc *vcc)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait,current);
139362306a36Sopenharmony_ci	struct eni_dev *eni_dev;
139462306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	eni_vcc = ENI_VCC(vcc);
139762306a36Sopenharmony_ci	if (!eni_vcc->tx) return;
139862306a36Sopenharmony_ci	eni_dev = ENI_DEV(vcc->dev);
139962306a36Sopenharmony_ci	/* wait for TX queue to drain */
140062306a36Sopenharmony_ci	DPRINTK("eni_close: waiting for TX ...\n");
140162306a36Sopenharmony_ci	add_wait_queue(&eni_dev->tx_wait,&wait);
140262306a36Sopenharmony_ci	set_current_state(TASK_UNINTERRUPTIBLE);
140362306a36Sopenharmony_ci	for (;;) {
140462306a36Sopenharmony_ci		int txing;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci		tasklet_disable(&eni_dev->task);
140762306a36Sopenharmony_ci		txing = skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing;
140862306a36Sopenharmony_ci		tasklet_enable(&eni_dev->task);
140962306a36Sopenharmony_ci		if (!txing) break;
141062306a36Sopenharmony_ci		DPRINTK("%d TX left\n",eni_vcc->txing);
141162306a36Sopenharmony_ci		schedule();
141262306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
141562306a36Sopenharmony_ci	remove_wait_queue(&eni_dev->tx_wait,&wait);
141662306a36Sopenharmony_ci	if (eni_vcc->tx != eni_dev->ubr) {
141762306a36Sopenharmony_ci		/*
141862306a36Sopenharmony_ci		 * Looping a few times in here is probably far cheaper than
141962306a36Sopenharmony_ci		 * keeping track of TX completions all the time, so let's poll
142062306a36Sopenharmony_ci		 * a bit ...
142162306a36Sopenharmony_ci		 */
142262306a36Sopenharmony_ci		while (eni_in(MID_TX_RDPTR(eni_vcc->tx->index)) !=
142362306a36Sopenharmony_ci		    eni_in(MID_TX_DESCRSTART(eni_vcc->tx->index)))
142462306a36Sopenharmony_ci			schedule();
142562306a36Sopenharmony_ci		eni_free_mem(eni_dev,eni_vcc->tx->send,eni_vcc->tx->words << 2);
142662306a36Sopenharmony_ci		eni_vcc->tx->send = NULL;
142762306a36Sopenharmony_ci		eni_dev->tx_bw += eni_vcc->tx->reserved;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci	eni_vcc->tx = NULL;
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_cistatic int start_tx(struct atm_dev *dev)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	struct eni_dev *eni_dev;
143662306a36Sopenharmony_ci	int i;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
143962306a36Sopenharmony_ci	eni_dev->lost = 0;
144062306a36Sopenharmony_ci	eni_dev->tx_bw = ATM_OC3_PCR;
144162306a36Sopenharmony_ci	eni_dev->tx_mult = DEFAULT_TX_MULT;
144262306a36Sopenharmony_ci	init_waitqueue_head(&eni_dev->tx_wait);
144362306a36Sopenharmony_ci	eni_dev->ubr = NULL;
144462306a36Sopenharmony_ci	skb_queue_head_init(&eni_dev->tx_queue);
144562306a36Sopenharmony_ci	eni_out(0,MID_DMA_WR_TX);
144662306a36Sopenharmony_ci	for (i = 0; i < NR_CHAN; i++) {
144762306a36Sopenharmony_ci		eni_dev->tx[i].send = NULL;
144862306a36Sopenharmony_ci		eni_dev->tx[i].index = i;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci	return 0;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci/*--------------------------------- common ----------------------------------*/
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci#if 0 /* may become useful again when tuning things */
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_cistatic void foo(void)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ciprintk(KERN_INFO
146262306a36Sopenharmony_ci  "tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n"
146362306a36Sopenharmony_ci  "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n",
146462306a36Sopenharmony_ci  tx_complete,dma_complete,queued,requeued,submitted,backlogged,
146562306a36Sopenharmony_ci  rx_enqueued,rx_dequeued,putting,pushed);
146662306a36Sopenharmony_ciif (eni_boards) printk(KERN_INFO "loss: %ld\n",ENI_DEV(eni_boards)->lost);
146762306a36Sopenharmony_ci}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci#endif
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_cistatic void bug_int(struct atm_dev *dev,unsigned long reason)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	DPRINTK(">bug_int\n");
147562306a36Sopenharmony_ci	if (reason & MID_DMA_ERR_ACK)
147662306a36Sopenharmony_ci		printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA "
147762306a36Sopenharmony_ci		    "error\n",dev->number);
147862306a36Sopenharmony_ci	if (reason & MID_TX_IDENT_MISM)
147962306a36Sopenharmony_ci		printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - ident "
148062306a36Sopenharmony_ci		    "mismatch\n",dev->number);
148162306a36Sopenharmony_ci	if (reason & MID_TX_DMA_OVFL)
148262306a36Sopenharmony_ci		printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA "
148362306a36Sopenharmony_ci		    "overflow\n",dev->number);
148462306a36Sopenharmony_ci	EVENT("---dump ends here---\n",0,0);
148562306a36Sopenharmony_ci	printk(KERN_NOTICE "---recent events---\n");
148662306a36Sopenharmony_ci	event_dump();
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_cistatic irqreturn_t eni_int(int irq,void *dev_id)
149162306a36Sopenharmony_ci{
149262306a36Sopenharmony_ci	struct atm_dev *dev;
149362306a36Sopenharmony_ci	struct eni_dev *eni_dev;
149462306a36Sopenharmony_ci	u32 reason;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	DPRINTK(">eni_int\n");
149762306a36Sopenharmony_ci	dev = dev_id;
149862306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
149962306a36Sopenharmony_ci	reason = eni_in(MID_ISA);
150062306a36Sopenharmony_ci	DPRINTK(DEV_LABEL ": int 0x%lx\n",(unsigned long) reason);
150162306a36Sopenharmony_ci	/*
150262306a36Sopenharmony_ci	 * Must handle these two right now, because reading ISA doesn't clear
150362306a36Sopenharmony_ci	 * them, so they re-occur and we never make it to the tasklet. Since
150462306a36Sopenharmony_ci	 * they're rare, we don't mind the occasional invocation of eni_tasklet
150562306a36Sopenharmony_ci	 * with eni_dev->events == 0.
150662306a36Sopenharmony_ci	 */
150762306a36Sopenharmony_ci	if (reason & MID_STAT_OVFL) {
150862306a36Sopenharmony_ci		EVENT("stat overflow\n",0,0);
150962306a36Sopenharmony_ci		eni_dev->lost += eni_in(MID_STAT) & MID_OVFL_TRASH;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci	if (reason & MID_SUNI_INT) {
151262306a36Sopenharmony_ci		EVENT("SUNI int\n",0,0);
151362306a36Sopenharmony_ci		dev->phy->interrupt(dev);
151462306a36Sopenharmony_ci#if 0
151562306a36Sopenharmony_ci		foo();
151662306a36Sopenharmony_ci#endif
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ci	spin_lock(&eni_dev->lock);
151962306a36Sopenharmony_ci	eni_dev->events |= reason;
152062306a36Sopenharmony_ci	spin_unlock(&eni_dev->lock);
152162306a36Sopenharmony_ci	tasklet_schedule(&eni_dev->task);
152262306a36Sopenharmony_ci	return IRQ_HANDLED;
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cistatic void eni_tasklet(unsigned long data)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	struct atm_dev *dev = (struct atm_dev *) data;
152962306a36Sopenharmony_ci	struct eni_dev *eni_dev = ENI_DEV(dev);
153062306a36Sopenharmony_ci	unsigned long flags;
153162306a36Sopenharmony_ci	u32 events;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	DPRINTK("eni_tasklet (dev %p)\n",dev);
153462306a36Sopenharmony_ci	spin_lock_irqsave(&eni_dev->lock,flags);
153562306a36Sopenharmony_ci	events = xchg(&eni_dev->events,0);
153662306a36Sopenharmony_ci	spin_unlock_irqrestore(&eni_dev->lock,flags);
153762306a36Sopenharmony_ci	if (events & MID_RX_DMA_COMPLETE) {
153862306a36Sopenharmony_ci		EVENT("INT: RX DMA complete, starting dequeue_rx\n",0,0);
153962306a36Sopenharmony_ci		dequeue_rx(dev);
154062306a36Sopenharmony_ci		EVENT("dequeue_rx done, starting poll_rx\n",0,0);
154162306a36Sopenharmony_ci		poll_rx(dev);
154262306a36Sopenharmony_ci		EVENT("poll_rx done\n",0,0);
154362306a36Sopenharmony_ci		/* poll_tx ? */
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci	if (events & MID_SERVICE) {
154662306a36Sopenharmony_ci		EVENT("INT: service, starting get_service\n",0,0);
154762306a36Sopenharmony_ci		get_service(dev);
154862306a36Sopenharmony_ci		EVENT("get_service done, starting poll_rx\n",0,0);
154962306a36Sopenharmony_ci		poll_rx(dev);
155062306a36Sopenharmony_ci		EVENT("poll_rx done\n",0,0);
155162306a36Sopenharmony_ci	}
155262306a36Sopenharmony_ci 	if (events & MID_TX_DMA_COMPLETE) {
155362306a36Sopenharmony_ci		EVENT("INT: TX DMA COMPLETE\n",0,0);
155462306a36Sopenharmony_ci		dequeue_tx(dev);
155562306a36Sopenharmony_ci	}
155662306a36Sopenharmony_ci	if (events & MID_TX_COMPLETE) {
155762306a36Sopenharmony_ci		EVENT("INT: TX COMPLETE\n",0,0);
155862306a36Sopenharmony_ci		tx_complete++;
155962306a36Sopenharmony_ci		wake_up(&eni_dev->tx_wait);
156062306a36Sopenharmony_ci		/* poll_rx ? */
156162306a36Sopenharmony_ci	}
156262306a36Sopenharmony_ci	if (events & (MID_DMA_ERR_ACK | MID_TX_IDENT_MISM | MID_TX_DMA_OVFL)) {
156362306a36Sopenharmony_ci		EVENT("bug interrupt\n",0,0);
156462306a36Sopenharmony_ci		bug_int(dev,events);
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci	poll_tx(dev);
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci/*--------------------------------- entries ---------------------------------*/
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic char * const media_name[] = {
157462306a36Sopenharmony_ci    "MMF", "SMF", "MMF", "03?", /*  0- 3 */
157562306a36Sopenharmony_ci    "UTP", "05?", "06?", "07?", /*  4- 7 */
157662306a36Sopenharmony_ci    "TAXI","09?", "10?", "11?", /*  8-11 */
157762306a36Sopenharmony_ci    "12?", "13?", "14?", "15?", /* 12-15 */
157862306a36Sopenharmony_ci    "MMF", "SMF", "18?", "19?", /* 16-19 */
157962306a36Sopenharmony_ci    "UTP", "21?", "22?", "23?", /* 20-23 */
158062306a36Sopenharmony_ci    "24?", "25?", "26?", "27?", /* 24-27 */
158162306a36Sopenharmony_ci    "28?", "29?", "30?", "31?"  /* 28-31 */
158262306a36Sopenharmony_ci};
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci#define SET_SEPROM \
158662306a36Sopenharmony_ci  ({ if (!error && !pci_error) { \
158762306a36Sopenharmony_ci    pci_error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,tonga); \
158862306a36Sopenharmony_ci    udelay(10); /* 10 usecs */ \
158962306a36Sopenharmony_ci  } })
159062306a36Sopenharmony_ci#define GET_SEPROM \
159162306a36Sopenharmony_ci  ({ if (!error && !pci_error) { \
159262306a36Sopenharmony_ci    pci_error = pci_read_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,&tonga); \
159362306a36Sopenharmony_ci    udelay(10); /* 10 usecs */ \
159462306a36Sopenharmony_ci  } })
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_cistatic int get_esi_asic(struct atm_dev *dev)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	struct eni_dev *eni_dev;
160062306a36Sopenharmony_ci	unsigned char tonga;
160162306a36Sopenharmony_ci	int error,failed,pci_error;
160262306a36Sopenharmony_ci	int address,i,j;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
160562306a36Sopenharmony_ci	error = pci_error = 0;
160662306a36Sopenharmony_ci	tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK;
160762306a36Sopenharmony_ci	SET_SEPROM;
160862306a36Sopenharmony_ci	for (i = 0; i < ESI_LEN && !error && !pci_error; i++) {
160962306a36Sopenharmony_ci		/* start operation */
161062306a36Sopenharmony_ci		tonga |= SEPROM_DATA;
161162306a36Sopenharmony_ci		SET_SEPROM;
161262306a36Sopenharmony_ci		tonga |= SEPROM_CLK;
161362306a36Sopenharmony_ci		SET_SEPROM;
161462306a36Sopenharmony_ci		tonga &= ~SEPROM_DATA;
161562306a36Sopenharmony_ci		SET_SEPROM;
161662306a36Sopenharmony_ci		tonga &= ~SEPROM_CLK;
161762306a36Sopenharmony_ci		SET_SEPROM;
161862306a36Sopenharmony_ci		/* send address */
161962306a36Sopenharmony_ci		address = ((i+SEPROM_ESI_BASE) << 1)+1;
162062306a36Sopenharmony_ci		for (j = 7; j >= 0; j--) {
162162306a36Sopenharmony_ci			tonga = (address >> j) & 1 ? tonga | SEPROM_DATA :
162262306a36Sopenharmony_ci			    tonga & ~SEPROM_DATA;
162362306a36Sopenharmony_ci			SET_SEPROM;
162462306a36Sopenharmony_ci			tonga |= SEPROM_CLK;
162562306a36Sopenharmony_ci			SET_SEPROM;
162662306a36Sopenharmony_ci			tonga &= ~SEPROM_CLK;
162762306a36Sopenharmony_ci			SET_SEPROM;
162862306a36Sopenharmony_ci		}
162962306a36Sopenharmony_ci		/* get ack */
163062306a36Sopenharmony_ci		tonga |= SEPROM_DATA;
163162306a36Sopenharmony_ci		SET_SEPROM;
163262306a36Sopenharmony_ci		tonga |= SEPROM_CLK;
163362306a36Sopenharmony_ci		SET_SEPROM;
163462306a36Sopenharmony_ci		GET_SEPROM;
163562306a36Sopenharmony_ci		failed = tonga & SEPROM_DATA;
163662306a36Sopenharmony_ci		tonga &= ~SEPROM_CLK;
163762306a36Sopenharmony_ci		SET_SEPROM;
163862306a36Sopenharmony_ci		tonga |= SEPROM_DATA;
163962306a36Sopenharmony_ci		SET_SEPROM;
164062306a36Sopenharmony_ci		if (failed) error = -EIO;
164162306a36Sopenharmony_ci		else {
164262306a36Sopenharmony_ci			dev->esi[i] = 0;
164362306a36Sopenharmony_ci			for (j = 7; j >= 0; j--) {
164462306a36Sopenharmony_ci				dev->esi[i] <<= 1;
164562306a36Sopenharmony_ci				tonga |= SEPROM_DATA;
164662306a36Sopenharmony_ci				SET_SEPROM;
164762306a36Sopenharmony_ci				tonga |= SEPROM_CLK;
164862306a36Sopenharmony_ci				SET_SEPROM;
164962306a36Sopenharmony_ci				GET_SEPROM;
165062306a36Sopenharmony_ci				if (tonga & SEPROM_DATA) dev->esi[i] |= 1;
165162306a36Sopenharmony_ci				tonga &= ~SEPROM_CLK;
165262306a36Sopenharmony_ci				SET_SEPROM;
165362306a36Sopenharmony_ci				tonga |= SEPROM_DATA;
165462306a36Sopenharmony_ci				SET_SEPROM;
165562306a36Sopenharmony_ci			}
165662306a36Sopenharmony_ci			/* get ack */
165762306a36Sopenharmony_ci			tonga |= SEPROM_DATA;
165862306a36Sopenharmony_ci			SET_SEPROM;
165962306a36Sopenharmony_ci			tonga |= SEPROM_CLK;
166062306a36Sopenharmony_ci			SET_SEPROM;
166162306a36Sopenharmony_ci			GET_SEPROM;
166262306a36Sopenharmony_ci			if (!(tonga & SEPROM_DATA)) error = -EIO;
166362306a36Sopenharmony_ci			tonga &= ~SEPROM_CLK;
166462306a36Sopenharmony_ci			SET_SEPROM;
166562306a36Sopenharmony_ci			tonga |= SEPROM_DATA;
166662306a36Sopenharmony_ci			SET_SEPROM;
166762306a36Sopenharmony_ci		}
166862306a36Sopenharmony_ci		/* stop operation */
166962306a36Sopenharmony_ci		tonga &= ~SEPROM_DATA;
167062306a36Sopenharmony_ci		SET_SEPROM;
167162306a36Sopenharmony_ci		tonga |= SEPROM_CLK;
167262306a36Sopenharmony_ci		SET_SEPROM;
167362306a36Sopenharmony_ci		tonga |= SEPROM_DATA;
167462306a36Sopenharmony_ci		SET_SEPROM;
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci	if (pci_error) {
167762306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): error reading ESI "
167862306a36Sopenharmony_ci		    "(0x%02x)\n",dev->number,pci_error);
167962306a36Sopenharmony_ci		error = -EIO;
168062306a36Sopenharmony_ci	}
168162306a36Sopenharmony_ci	return error;
168262306a36Sopenharmony_ci}
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci#undef SET_SEPROM
168662306a36Sopenharmony_ci#undef GET_SEPROM
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic int get_esi_fpga(struct atm_dev *dev, void __iomem *base)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	void __iomem *mac_base;
169262306a36Sopenharmony_ci	int i;
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	mac_base = base+EPROM_SIZE-sizeof(struct midway_eprom);
169562306a36Sopenharmony_ci	for (i = 0; i < ESI_LEN; i++) dev->esi[i] = readb(mac_base+(i^3));
169662306a36Sopenharmony_ci	return 0;
169762306a36Sopenharmony_ci}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_cistatic int eni_do_init(struct atm_dev *dev)
170162306a36Sopenharmony_ci{
170262306a36Sopenharmony_ci	struct midway_eprom __iomem *eprom;
170362306a36Sopenharmony_ci	struct eni_dev *eni_dev;
170462306a36Sopenharmony_ci	struct pci_dev *pci_dev;
170562306a36Sopenharmony_ci	unsigned long real_base;
170662306a36Sopenharmony_ci	void __iomem *base;
170762306a36Sopenharmony_ci	int error,i,last;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	DPRINTK(">eni_init\n");
171062306a36Sopenharmony_ci	dev->ci_range.vpi_bits = 0;
171162306a36Sopenharmony_ci	dev->ci_range.vci_bits = NR_VCI_LD;
171262306a36Sopenharmony_ci	dev->link_rate = ATM_OC3_PCR;
171362306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
171462306a36Sopenharmony_ci	pci_dev = eni_dev->pci_dev;
171562306a36Sopenharmony_ci	real_base = pci_resource_start(pci_dev, 0);
171662306a36Sopenharmony_ci	eni_dev->irq = pci_dev->irq;
171762306a36Sopenharmony_ci	if ((error = pci_write_config_word(pci_dev,PCI_COMMAND,
171862306a36Sopenharmony_ci	    PCI_COMMAND_MEMORY |
171962306a36Sopenharmony_ci	    (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) {
172062306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory "
172162306a36Sopenharmony_ci		    "(0x%02x)\n",dev->number,error);
172262306a36Sopenharmony_ci		return -EIO;
172362306a36Sopenharmony_ci	}
172462306a36Sopenharmony_ci	printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%lx,irq=%d,",
172562306a36Sopenharmony_ci	    dev->number,pci_dev->revision,real_base,eni_dev->irq);
172662306a36Sopenharmony_ci	if (!(base = ioremap(real_base,MAP_MAX_SIZE))) {
172762306a36Sopenharmony_ci		printk("\n");
172862306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page "
172962306a36Sopenharmony_ci		    "mapping\n",dev->number);
173062306a36Sopenharmony_ci		return -ENOMEM;
173162306a36Sopenharmony_ci	}
173262306a36Sopenharmony_ci	eni_dev->ioaddr = base;
173362306a36Sopenharmony_ci	eni_dev->base_diff = real_base - (unsigned long) base;
173462306a36Sopenharmony_ci	/* id may not be present in ASIC Tonga boards - check this @@@ */
173562306a36Sopenharmony_ci	if (!eni_dev->asic) {
173662306a36Sopenharmony_ci		eprom = (base+EPROM_SIZE-sizeof(struct midway_eprom));
173762306a36Sopenharmony_ci		if (readl(&eprom->magic) != ENI155_MAGIC) {
173862306a36Sopenharmony_ci			printk("\n");
173962306a36Sopenharmony_ci			printk(KERN_ERR DEV_LABEL
174062306a36Sopenharmony_ci			       "(itf %d): bad magic - expected 0x%x, got 0x%x\n",
174162306a36Sopenharmony_ci			       dev->number, ENI155_MAGIC,
174262306a36Sopenharmony_ci			       (unsigned)readl(&eprom->magic));
174362306a36Sopenharmony_ci			error = -EINVAL;
174462306a36Sopenharmony_ci			goto unmap;
174562306a36Sopenharmony_ci		}
174662306a36Sopenharmony_ci	}
174762306a36Sopenharmony_ci	eni_dev->phy = base+PHY_BASE;
174862306a36Sopenharmony_ci	eni_dev->reg = base+REG_BASE;
174962306a36Sopenharmony_ci	eni_dev->ram = base+RAM_BASE;
175062306a36Sopenharmony_ci	last = MAP_MAX_SIZE-RAM_BASE;
175162306a36Sopenharmony_ci	for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
175262306a36Sopenharmony_ci		writel(0x55555555,eni_dev->ram+i);
175362306a36Sopenharmony_ci		if (readl(eni_dev->ram+i) != 0x55555555) last = i;
175462306a36Sopenharmony_ci		else {
175562306a36Sopenharmony_ci			writel(0xAAAAAAAA,eni_dev->ram+i);
175662306a36Sopenharmony_ci			if (readl(eni_dev->ram+i) != 0xAAAAAAAA) last = i;
175762306a36Sopenharmony_ci			else writel(i,eni_dev->ram+i);
175862306a36Sopenharmony_ci		}
175962306a36Sopenharmony_ci	}
176062306a36Sopenharmony_ci	for (i = 0; i < last; i += RAM_INCREMENT)
176162306a36Sopenharmony_ci		if (readl(eni_dev->ram+i) != i) break;
176262306a36Sopenharmony_ci	eni_dev->mem = i;
176362306a36Sopenharmony_ci	memset_io(eni_dev->ram,0,eni_dev->mem);
176462306a36Sopenharmony_ci	/* TODO: should shrink allocation now */
176562306a36Sopenharmony_ci	printk("mem=%dkB (",eni_dev->mem >> 10);
176662306a36Sopenharmony_ci	/* TODO: check for non-SUNI, check for TAXI ? */
176762306a36Sopenharmony_ci	if (!(eni_in(MID_RES_ID_MCON) & 0x200) != !eni_dev->asic) {
176862306a36Sopenharmony_ci		printk(")\n");
176962306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%x\n",
177062306a36Sopenharmony_ci		    dev->number,(unsigned) eni_in(MID_RES_ID_MCON));
177162306a36Sopenharmony_ci		error = -EINVAL;
177262306a36Sopenharmony_ci		goto unmap;
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci	error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base);
177562306a36Sopenharmony_ci	if (error)
177662306a36Sopenharmony_ci		goto unmap;
177762306a36Sopenharmony_ci	for (i = 0; i < ESI_LEN; i++)
177862306a36Sopenharmony_ci		printk("%s%02X",i ? "-" : "",dev->esi[i]);
177962306a36Sopenharmony_ci	printk(")\n");
178062306a36Sopenharmony_ci	printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number,
178162306a36Sopenharmony_ci	    eni_in(MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA",
178262306a36Sopenharmony_ci	    media_name[eni_in(MID_RES_ID_MCON) & DAUGHTER_ID]);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	error = suni_init(dev);
178562306a36Sopenharmony_ci	if (error)
178662306a36Sopenharmony_ci		goto unmap;
178762306a36Sopenharmony_ciout:
178862306a36Sopenharmony_ci	return error;
178962306a36Sopenharmony_ciunmap:
179062306a36Sopenharmony_ci	iounmap(base);
179162306a36Sopenharmony_ci	goto out;
179262306a36Sopenharmony_ci}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic void eni_do_release(struct atm_dev *dev)
179562306a36Sopenharmony_ci{
179662306a36Sopenharmony_ci	struct eni_dev *ed = ENI_DEV(dev);
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	dev->phy->stop(dev);
179962306a36Sopenharmony_ci	dev->phy = NULL;
180062306a36Sopenharmony_ci	iounmap(ed->ioaddr);
180162306a36Sopenharmony_ci}
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_cistatic int eni_start(struct atm_dev *dev)
180462306a36Sopenharmony_ci{
180562306a36Sopenharmony_ci	struct eni_dev *eni_dev;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	void __iomem *buf;
180862306a36Sopenharmony_ci	unsigned long buffer_mem;
180962306a36Sopenharmony_ci	int error;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	DPRINTK(">eni_start\n");
181262306a36Sopenharmony_ci	eni_dev = ENI_DEV(dev);
181362306a36Sopenharmony_ci	if (request_irq(eni_dev->irq,&eni_int,IRQF_SHARED,DEV_LABEL,dev)) {
181462306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
181562306a36Sopenharmony_ci		    dev->number,eni_dev->irq);
181662306a36Sopenharmony_ci		error = -EAGAIN;
181762306a36Sopenharmony_ci		goto out;
181862306a36Sopenharmony_ci	}
181962306a36Sopenharmony_ci	pci_set_master(eni_dev->pci_dev);
182062306a36Sopenharmony_ci	if ((error = pci_write_config_word(eni_dev->pci_dev,PCI_COMMAND,
182162306a36Sopenharmony_ci	    PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
182262306a36Sopenharmony_ci	    (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) {
182362306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+"
182462306a36Sopenharmony_ci		    "master (0x%02x)\n",dev->number,error);
182562306a36Sopenharmony_ci		goto free_irq;
182662306a36Sopenharmony_ci	}
182762306a36Sopenharmony_ci	if ((error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,
182862306a36Sopenharmony_ci	    END_SWAP_DMA))) {
182962306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap "
183062306a36Sopenharmony_ci		    "(0x%02x)\n",dev->number,error);
183162306a36Sopenharmony_ci		goto free_irq;
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_ci	/* determine addresses of internal tables */
183462306a36Sopenharmony_ci	eni_dev->vci = eni_dev->ram;
183562306a36Sopenharmony_ci	eni_dev->rx_dma = eni_dev->ram+NR_VCI*16;
183662306a36Sopenharmony_ci	eni_dev->tx_dma = eni_dev->rx_dma+NR_DMA_RX*8;
183762306a36Sopenharmony_ci	eni_dev->service = eni_dev->tx_dma+NR_DMA_TX*8;
183862306a36Sopenharmony_ci	buf = eni_dev->service+NR_SERVICE*4;
183962306a36Sopenharmony_ci	DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n",
184062306a36Sopenharmony_ci	     eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma,
184162306a36Sopenharmony_ci	     eni_dev->service,buf);
184262306a36Sopenharmony_ci	spin_lock_init(&eni_dev->lock);
184362306a36Sopenharmony_ci	tasklet_init(&eni_dev->task,eni_tasklet,(unsigned long) dev);
184462306a36Sopenharmony_ci	eni_dev->events = 0;
184562306a36Sopenharmony_ci	/* initialize memory management */
184662306a36Sopenharmony_ci	buffer_mem = eni_dev->mem - (buf - eni_dev->ram);
184762306a36Sopenharmony_ci	eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2;
184862306a36Sopenharmony_ci	eni_dev->free_list = kmalloc_array(eni_dev->free_list_size + 1,
184962306a36Sopenharmony_ci					   sizeof(*eni_dev->free_list),
185062306a36Sopenharmony_ci					   GFP_KERNEL);
185162306a36Sopenharmony_ci	if (!eni_dev->free_list) {
185262306a36Sopenharmony_ci		printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
185362306a36Sopenharmony_ci		    dev->number);
185462306a36Sopenharmony_ci		error = -ENOMEM;
185562306a36Sopenharmony_ci		goto free_irq;
185662306a36Sopenharmony_ci	}
185762306a36Sopenharmony_ci	eni_dev->free_len = 0;
185862306a36Sopenharmony_ci	eni_put_free(eni_dev,buf,buffer_mem);
185962306a36Sopenharmony_ci	memset_io(eni_dev->vci,0,16*NR_VCI); /* clear VCI table */
186062306a36Sopenharmony_ci	/*
186162306a36Sopenharmony_ci	 * byte_addr  free (k)
186262306a36Sopenharmony_ci	 * 0x00000000     512  VCI table
186362306a36Sopenharmony_ci	 * 0x00004000	  496  RX DMA
186462306a36Sopenharmony_ci	 * 0x00005000	  492  TX DMA
186562306a36Sopenharmony_ci	 * 0x00006000	  488  service list
186662306a36Sopenharmony_ci	 * 0x00007000	  484  buffers
186762306a36Sopenharmony_ci	 * 0x00080000	    0  end (512kB)
186862306a36Sopenharmony_ci	 */
186962306a36Sopenharmony_ci	eni_out(0xffffffff,MID_IE);
187062306a36Sopenharmony_ci	error = start_tx(dev);
187162306a36Sopenharmony_ci	if (error) goto free_list;
187262306a36Sopenharmony_ci	error = start_rx(dev);
187362306a36Sopenharmony_ci	if (error) goto free_list;
187462306a36Sopenharmony_ci	error = dev->phy->start(dev);
187562306a36Sopenharmony_ci	if (error) goto free_list;
187662306a36Sopenharmony_ci	eni_out(eni_in(MID_MC_S) | (1 << MID_INT_SEL_SHIFT) |
187762306a36Sopenharmony_ci	    MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE,
187862306a36Sopenharmony_ci	    MID_MC_S);
187962306a36Sopenharmony_ci	    /* Tonga uses SBus INTReq1 */
188062306a36Sopenharmony_ci	(void) eni_in(MID_ISA); /* clear Midway interrupts */
188162306a36Sopenharmony_ci	return 0;
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_cifree_list:
188462306a36Sopenharmony_ci	kfree(eni_dev->free_list);
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_cifree_irq:
188762306a36Sopenharmony_ci	free_irq(eni_dev->irq, dev);
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ciout:
189062306a36Sopenharmony_ci	return error;
189162306a36Sopenharmony_ci}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_cistatic void eni_close(struct atm_vcc *vcc)
189562306a36Sopenharmony_ci{
189662306a36Sopenharmony_ci	DPRINTK(">eni_close\n");
189762306a36Sopenharmony_ci	if (!ENI_VCC(vcc)) return;
189862306a36Sopenharmony_ci	clear_bit(ATM_VF_READY,&vcc->flags);
189962306a36Sopenharmony_ci	close_rx(vcc);
190062306a36Sopenharmony_ci	close_tx(vcc);
190162306a36Sopenharmony_ci	DPRINTK("eni_close: done waiting\n");
190262306a36Sopenharmony_ci	/* deallocate memory */
190362306a36Sopenharmony_ci	kfree(ENI_VCC(vcc));
190462306a36Sopenharmony_ci	vcc->dev_data = NULL;
190562306a36Sopenharmony_ci	clear_bit(ATM_VF_ADDR,&vcc->flags);
190662306a36Sopenharmony_ci	/*foo();*/
190762306a36Sopenharmony_ci}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_cistatic int eni_open(struct atm_vcc *vcc)
191162306a36Sopenharmony_ci{
191262306a36Sopenharmony_ci	struct eni_vcc *eni_vcc;
191362306a36Sopenharmony_ci	int error;
191462306a36Sopenharmony_ci	short vpi = vcc->vpi;
191562306a36Sopenharmony_ci	int vci = vcc->vci;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	DPRINTK(">eni_open\n");
191862306a36Sopenharmony_ci	EVENT("eni_open\n",0,0);
191962306a36Sopenharmony_ci	if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))
192062306a36Sopenharmony_ci		vcc->dev_data = NULL;
192162306a36Sopenharmony_ci	if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
192262306a36Sopenharmony_ci		set_bit(ATM_VF_ADDR,&vcc->flags);
192362306a36Sopenharmony_ci	if (vcc->qos.aal != ATM_AAL0 && vcc->qos.aal != ATM_AAL5)
192462306a36Sopenharmony_ci		return -EINVAL;
192562306a36Sopenharmony_ci	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
192662306a36Sopenharmony_ci	    vcc->vci);
192762306a36Sopenharmony_ci	if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
192862306a36Sopenharmony_ci		eni_vcc = kmalloc(sizeof(struct eni_vcc),GFP_KERNEL);
192962306a36Sopenharmony_ci		if (!eni_vcc) return -ENOMEM;
193062306a36Sopenharmony_ci		vcc->dev_data = eni_vcc;
193162306a36Sopenharmony_ci		eni_vcc->tx = NULL; /* for eni_close after open_rx */
193262306a36Sopenharmony_ci		if ((error = open_rx_first(vcc))) {
193362306a36Sopenharmony_ci			eni_close(vcc);
193462306a36Sopenharmony_ci			return error;
193562306a36Sopenharmony_ci		}
193662306a36Sopenharmony_ci		if ((error = open_tx_first(vcc))) {
193762306a36Sopenharmony_ci			eni_close(vcc);
193862306a36Sopenharmony_ci			return error;
193962306a36Sopenharmony_ci		}
194062306a36Sopenharmony_ci	}
194162306a36Sopenharmony_ci	if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
194262306a36Sopenharmony_ci	if ((error = open_rx_second(vcc))) {
194362306a36Sopenharmony_ci		eni_close(vcc);
194462306a36Sopenharmony_ci		return error;
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci	if ((error = open_tx_second(vcc))) {
194762306a36Sopenharmony_ci		eni_close(vcc);
194862306a36Sopenharmony_ci		return error;
194962306a36Sopenharmony_ci	}
195062306a36Sopenharmony_ci	set_bit(ATM_VF_READY,&vcc->flags);
195162306a36Sopenharmony_ci	/* should power down SUNI while !ref_count @@@ */
195262306a36Sopenharmony_ci	return 0;
195362306a36Sopenharmony_ci}
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_cistatic int eni_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs)
195762306a36Sopenharmony_ci{
195862306a36Sopenharmony_ci	struct eni_dev *eni_dev = ENI_DEV(vcc->dev);
195962306a36Sopenharmony_ci	struct eni_tx *tx = ENI_VCC(vcc)->tx;
196062306a36Sopenharmony_ci	struct sk_buff *skb;
196162306a36Sopenharmony_ci	int error,rate,rsv,shp;
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	if (qos->txtp.traffic_class == ATM_NONE) return 0;
196462306a36Sopenharmony_ci	if (tx == eni_dev->ubr) return -EBADFD;
196562306a36Sopenharmony_ci	rate = atm_pcr_goal(&qos->txtp);
196662306a36Sopenharmony_ci	if (rate < 0) rate = -rate;
196762306a36Sopenharmony_ci	rsv = shp = 0;
196862306a36Sopenharmony_ci	if ((flgs & ATM_MF_DEC_RSV) && rate && rate < tx->reserved) rsv = 1;
196962306a36Sopenharmony_ci	if ((flgs & ATM_MF_INC_RSV) && (!rate || rate > tx->reserved)) rsv = 1;
197062306a36Sopenharmony_ci	if ((flgs & ATM_MF_DEC_SHP) && rate && rate < tx->shaping) shp = 1;
197162306a36Sopenharmony_ci	if ((flgs & ATM_MF_INC_SHP) && (!rate || rate > tx->shaping)) shp = 1;
197262306a36Sopenharmony_ci	if (!rsv && !shp) return 0;
197362306a36Sopenharmony_ci	error = reserve_or_set_tx(vcc,&qos->txtp,rsv,shp);
197462306a36Sopenharmony_ci	if (error) return error;
197562306a36Sopenharmony_ci	if (shp && !(flgs & ATM_MF_IMMED)) return 0;
197662306a36Sopenharmony_ci	/*
197762306a36Sopenharmony_ci	 * Walk through the send buffer and patch the rate information in all
197862306a36Sopenharmony_ci	 * segmentation buffer descriptors of this VCC.
197962306a36Sopenharmony_ci	 */
198062306a36Sopenharmony_ci	tasklet_disable(&eni_dev->task);
198162306a36Sopenharmony_ci	skb_queue_walk(&eni_dev->tx_queue, skb) {
198262306a36Sopenharmony_ci		void __iomem *dsc;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci		if (ATM_SKB(skb)->vcc != vcc) continue;
198562306a36Sopenharmony_ci		dsc = tx->send+ENI_PRV_POS(skb)*4;
198662306a36Sopenharmony_ci		writel((readl(dsc) & ~(MID_SEG_RATE | MID_SEG_PR)) |
198762306a36Sopenharmony_ci		    (tx->prescaler << MID_SEG_PR_SHIFT) |
198862306a36Sopenharmony_ci		    (tx->resolution << MID_SEG_RATE_SHIFT), dsc);
198962306a36Sopenharmony_ci	}
199062306a36Sopenharmony_ci	tasklet_enable(&eni_dev->task);
199162306a36Sopenharmony_ci	return 0;
199262306a36Sopenharmony_ci}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_cistatic int eni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
199662306a36Sopenharmony_ci{
199762306a36Sopenharmony_ci	struct eni_dev *eni_dev = ENI_DEV(dev);
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	if (cmd == ENI_MEMDUMP) {
200062306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN)) return -EPERM;
200162306a36Sopenharmony_ci		printk(KERN_WARNING "Please use /proc/atm/" DEV_LABEL ":%d "
200262306a36Sopenharmony_ci		    "instead of obsolete ioctl ENI_MEMDUMP\n",dev->number);
200362306a36Sopenharmony_ci		dump(dev);
200462306a36Sopenharmony_ci		return 0;
200562306a36Sopenharmony_ci	}
200662306a36Sopenharmony_ci	if (cmd == ENI_SETMULT) {
200762306a36Sopenharmony_ci		struct eni_multipliers mult;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN)) return -EPERM;
201062306a36Sopenharmony_ci		if (copy_from_user(&mult, arg,
201162306a36Sopenharmony_ci		    sizeof(struct eni_multipliers)))
201262306a36Sopenharmony_ci			return -EFAULT;
201362306a36Sopenharmony_ci		if ((mult.tx && mult.tx <= 100) || (mult.rx &&mult.rx <= 100) ||
201462306a36Sopenharmony_ci		    mult.tx > 65536 || mult.rx > 65536)
201562306a36Sopenharmony_ci			return -EINVAL;
201662306a36Sopenharmony_ci		if (mult.tx) eni_dev->tx_mult = mult.tx;
201762306a36Sopenharmony_ci		if (mult.rx) eni_dev->rx_mult = mult.rx;
201862306a36Sopenharmony_ci		return 0;
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci	if (cmd == ATM_SETCIRANGE) {
202162306a36Sopenharmony_ci		struct atm_cirange ci;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci		if (copy_from_user(&ci, arg,sizeof(struct atm_cirange)))
202462306a36Sopenharmony_ci			return -EFAULT;
202562306a36Sopenharmony_ci		if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) &&
202662306a36Sopenharmony_ci		    (ci.vci_bits == NR_VCI_LD || ci.vpi_bits == ATM_CI_MAX))
202762306a36Sopenharmony_ci		    return 0;
202862306a36Sopenharmony_ci		return -EINVAL;
202962306a36Sopenharmony_ci	}
203062306a36Sopenharmony_ci	if (!dev->phy->ioctl) return -ENOIOCTLCMD;
203162306a36Sopenharmony_ci	return dev->phy->ioctl(dev,cmd,arg);
203262306a36Sopenharmony_ci}
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_cistatic int eni_send(struct atm_vcc *vcc,struct sk_buff *skb)
203562306a36Sopenharmony_ci{
203662306a36Sopenharmony_ci	enum enq_res res;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	DPRINTK(">eni_send\n");
203962306a36Sopenharmony_ci	if (!ENI_VCC(vcc)->tx) {
204062306a36Sopenharmony_ci		if (vcc->pop) vcc->pop(vcc,skb);
204162306a36Sopenharmony_ci		else dev_kfree_skb(skb);
204262306a36Sopenharmony_ci		return -EINVAL;
204362306a36Sopenharmony_ci	}
204462306a36Sopenharmony_ci	if (!skb) {
204562306a36Sopenharmony_ci		printk(KERN_CRIT "!skb in eni_send ?\n");
204662306a36Sopenharmony_ci		if (vcc->pop) vcc->pop(vcc,skb);
204762306a36Sopenharmony_ci		return -EINVAL;
204862306a36Sopenharmony_ci	}
204962306a36Sopenharmony_ci	if (vcc->qos.aal == ATM_AAL0) {
205062306a36Sopenharmony_ci		if (skb->len != ATM_CELL_SIZE-1) {
205162306a36Sopenharmony_ci			if (vcc->pop) vcc->pop(vcc,skb);
205262306a36Sopenharmony_ci			else dev_kfree_skb(skb);
205362306a36Sopenharmony_ci			return -EINVAL;
205462306a36Sopenharmony_ci		}
205562306a36Sopenharmony_ci		*(u32 *) skb->data = htonl(*(u32 *) skb->data);
205662306a36Sopenharmony_ci	}
205762306a36Sopenharmony_ci	submitted++;
205862306a36Sopenharmony_ci	ATM_SKB(skb)->vcc = vcc;
205962306a36Sopenharmony_ci	tasklet_disable_in_atomic(&ENI_DEV(vcc->dev)->task);
206062306a36Sopenharmony_ci	res = do_tx(skb);
206162306a36Sopenharmony_ci	tasklet_enable(&ENI_DEV(vcc->dev)->task);
206262306a36Sopenharmony_ci	if (res == enq_ok) return 0;
206362306a36Sopenharmony_ci	skb_queue_tail(&ENI_VCC(vcc)->tx->backlog,skb);
206462306a36Sopenharmony_ci	backlogged++;
206562306a36Sopenharmony_ci	tasklet_schedule(&ENI_DEV(vcc->dev)->task);
206662306a36Sopenharmony_ci	return 0;
206762306a36Sopenharmony_ci}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_cistatic void eni_phy_put(struct atm_dev *dev,unsigned char value,
207062306a36Sopenharmony_ci    unsigned long addr)
207162306a36Sopenharmony_ci{
207262306a36Sopenharmony_ci	writel(value,ENI_DEV(dev)->phy+addr*4);
207362306a36Sopenharmony_ci}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_cistatic unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr)
207862306a36Sopenharmony_ci{
207962306a36Sopenharmony_ci	return readl(ENI_DEV(dev)->phy+addr*4);
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_cistatic int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page)
208462306a36Sopenharmony_ci{
208562306a36Sopenharmony_ci	struct sock *s;
208662306a36Sopenharmony_ci	static const char *signal[] = { "LOST","unknown","okay" };
208762306a36Sopenharmony_ci	struct eni_dev *eni_dev = ENI_DEV(dev);
208862306a36Sopenharmony_ci	struct atm_vcc *vcc;
208962306a36Sopenharmony_ci	int left,i;
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	left = *pos;
209262306a36Sopenharmony_ci	if (!left)
209362306a36Sopenharmony_ci		return sprintf(page,DEV_LABEL "(itf %d) signal %s, %dkB, "
209462306a36Sopenharmony_ci		    "%d cps remaining\n",dev->number,signal[(int) dev->signal],
209562306a36Sopenharmony_ci		    eni_dev->mem >> 10,eni_dev->tx_bw);
209662306a36Sopenharmony_ci	if (!--left)
209762306a36Sopenharmony_ci		return sprintf(page,"%4sBursts: TX"
209862306a36Sopenharmony_ci#if !defined(CONFIG_ATM_ENI_BURST_TX_16W) && \
209962306a36Sopenharmony_ci    !defined(CONFIG_ATM_ENI_BURST_TX_8W) && \
210062306a36Sopenharmony_ci    !defined(CONFIG_ATM_ENI_BURST_TX_4W) && \
210162306a36Sopenharmony_ci    !defined(CONFIG_ATM_ENI_BURST_TX_2W)
210262306a36Sopenharmony_ci		    " none"
210362306a36Sopenharmony_ci#endif
210462306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_16W
210562306a36Sopenharmony_ci		    " 16W"
210662306a36Sopenharmony_ci#endif
210762306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_8W
210862306a36Sopenharmony_ci		    " 8W"
210962306a36Sopenharmony_ci#endif
211062306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_4W
211162306a36Sopenharmony_ci		    " 4W"
211262306a36Sopenharmony_ci#endif
211362306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_2W
211462306a36Sopenharmony_ci		    " 2W"
211562306a36Sopenharmony_ci#endif
211662306a36Sopenharmony_ci		    ", RX"
211762306a36Sopenharmony_ci#if !defined(CONFIG_ATM_ENI_BURST_RX_16W) && \
211862306a36Sopenharmony_ci    !defined(CONFIG_ATM_ENI_BURST_RX_8W) && \
211962306a36Sopenharmony_ci    !defined(CONFIG_ATM_ENI_BURST_RX_4W) && \
212062306a36Sopenharmony_ci    !defined(CONFIG_ATM_ENI_BURST_RX_2W)
212162306a36Sopenharmony_ci		    " none"
212262306a36Sopenharmony_ci#endif
212362306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_16W
212462306a36Sopenharmony_ci		    " 16W"
212562306a36Sopenharmony_ci#endif
212662306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_8W
212762306a36Sopenharmony_ci		    " 8W"
212862306a36Sopenharmony_ci#endif
212962306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_4W
213062306a36Sopenharmony_ci		    " 4W"
213162306a36Sopenharmony_ci#endif
213262306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_2W
213362306a36Sopenharmony_ci		    " 2W"
213462306a36Sopenharmony_ci#endif
213562306a36Sopenharmony_ci#ifndef CONFIG_ATM_ENI_TUNE_BURST
213662306a36Sopenharmony_ci		    " (default)"
213762306a36Sopenharmony_ci#endif
213862306a36Sopenharmony_ci		    "\n","");
213962306a36Sopenharmony_ci	if (!--left)
214062306a36Sopenharmony_ci		return sprintf(page,"%4sBuffer multipliers: tx %d%%, rx %d%%\n",
214162306a36Sopenharmony_ci		    "",eni_dev->tx_mult,eni_dev->rx_mult);
214262306a36Sopenharmony_ci	for (i = 0; i < NR_CHAN; i++) {
214362306a36Sopenharmony_ci		struct eni_tx *tx = eni_dev->tx+i;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci		if (!tx->send) continue;
214662306a36Sopenharmony_ci		if (!--left) {
214762306a36Sopenharmony_ci			return sprintf(page, "tx[%d]:    0x%lx-0x%lx "
214862306a36Sopenharmony_ci			    "(%6ld bytes), rsv %d cps, shp %d cps%s\n",i,
214962306a36Sopenharmony_ci			    (unsigned long) (tx->send - eni_dev->ram),
215062306a36Sopenharmony_ci			    tx->send-eni_dev->ram+tx->words*4-1,tx->words*4,
215162306a36Sopenharmony_ci			    tx->reserved,tx->shaping,
215262306a36Sopenharmony_ci			    tx == eni_dev->ubr ? " (UBR)" : "");
215362306a36Sopenharmony_ci		}
215462306a36Sopenharmony_ci		if (--left) continue;
215562306a36Sopenharmony_ci		return sprintf(page,"%10sbacklog %u packets\n","",
215662306a36Sopenharmony_ci		    skb_queue_len(&tx->backlog));
215762306a36Sopenharmony_ci	}
215862306a36Sopenharmony_ci	read_lock(&vcc_sklist_lock);
215962306a36Sopenharmony_ci	for(i = 0; i < VCC_HTABLE_SIZE; ++i) {
216062306a36Sopenharmony_ci		struct hlist_head *head = &vcc_hash[i];
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci		sk_for_each(s, head) {
216362306a36Sopenharmony_ci			struct eni_vcc *eni_vcc;
216462306a36Sopenharmony_ci			int length;
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci			vcc = atm_sk(s);
216762306a36Sopenharmony_ci			if (vcc->dev != dev)
216862306a36Sopenharmony_ci				continue;
216962306a36Sopenharmony_ci			eni_vcc = ENI_VCC(vcc);
217062306a36Sopenharmony_ci			if (--left) continue;
217162306a36Sopenharmony_ci			length = sprintf(page,"vcc %4d: ",vcc->vci);
217262306a36Sopenharmony_ci			if (eni_vcc->rx) {
217362306a36Sopenharmony_ci				length += sprintf(page+length, "0x%lx-0x%lx "
217462306a36Sopenharmony_ci				    "(%6ld bytes)",
217562306a36Sopenharmony_ci				    (unsigned long) (eni_vcc->recv - eni_dev->ram),
217662306a36Sopenharmony_ci				    eni_vcc->recv-eni_dev->ram+eni_vcc->words*4-1,
217762306a36Sopenharmony_ci				    eni_vcc->words*4);
217862306a36Sopenharmony_ci				if (eni_vcc->tx) length += sprintf(page+length,", ");
217962306a36Sopenharmony_ci			}
218062306a36Sopenharmony_ci			if (eni_vcc->tx)
218162306a36Sopenharmony_ci				length += sprintf(page+length,"tx[%d], txing %d bytes",
218262306a36Sopenharmony_ci				    eni_vcc->tx->index,eni_vcc->txing);
218362306a36Sopenharmony_ci			page[length] = '\n';
218462306a36Sopenharmony_ci			read_unlock(&vcc_sklist_lock);
218562306a36Sopenharmony_ci			return length+1;
218662306a36Sopenharmony_ci		}
218762306a36Sopenharmony_ci	}
218862306a36Sopenharmony_ci	read_unlock(&vcc_sklist_lock);
218962306a36Sopenharmony_ci	for (i = 0; i < eni_dev->free_len; i++) {
219062306a36Sopenharmony_ci		struct eni_free *fe = eni_dev->free_list+i;
219162306a36Sopenharmony_ci		unsigned long offset;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci		if (--left) continue;
219462306a36Sopenharmony_ci		offset = (unsigned long) eni_dev->ram+eni_dev->base_diff;
219562306a36Sopenharmony_ci		return sprintf(page,"free      %p-%p (%6d bytes)\n",
219662306a36Sopenharmony_ci		    fe->start-offset,fe->start-offset+(1 << fe->order)-1,
219762306a36Sopenharmony_ci		    1 << fe->order);
219862306a36Sopenharmony_ci	}
219962306a36Sopenharmony_ci	return 0;
220062306a36Sopenharmony_ci}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_cistatic const struct atmdev_ops ops = {
220462306a36Sopenharmony_ci	.open		= eni_open,
220562306a36Sopenharmony_ci	.close		= eni_close,
220662306a36Sopenharmony_ci	.ioctl		= eni_ioctl,
220762306a36Sopenharmony_ci	.send		= eni_send,
220862306a36Sopenharmony_ci	.phy_put	= eni_phy_put,
220962306a36Sopenharmony_ci	.phy_get	= eni_phy_get,
221062306a36Sopenharmony_ci	.change_qos	= eni_change_qos,
221162306a36Sopenharmony_ci	.proc_read	= eni_proc_read
221262306a36Sopenharmony_ci};
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_cistatic int eni_init_one(struct pci_dev *pci_dev,
221662306a36Sopenharmony_ci			const struct pci_device_id *ent)
221762306a36Sopenharmony_ci{
221862306a36Sopenharmony_ci	struct atm_dev *dev;
221962306a36Sopenharmony_ci	struct eni_dev *eni_dev;
222062306a36Sopenharmony_ci	struct eni_zero *zero;
222162306a36Sopenharmony_ci	int rc;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	rc = pci_enable_device(pci_dev);
222462306a36Sopenharmony_ci	if (rc < 0)
222562306a36Sopenharmony_ci		goto out;
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32));
222862306a36Sopenharmony_ci	if (rc < 0)
222962306a36Sopenharmony_ci		goto err_disable;
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	rc = -ENOMEM;
223262306a36Sopenharmony_ci	eni_dev = kmalloc(sizeof(struct eni_dev), GFP_KERNEL);
223362306a36Sopenharmony_ci	if (!eni_dev)
223462306a36Sopenharmony_ci		goto err_disable;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	zero = &eni_dev->zero;
223762306a36Sopenharmony_ci	zero->addr = dma_alloc_coherent(&pci_dev->dev,
223862306a36Sopenharmony_ci					ENI_ZEROES_SIZE, &zero->dma, GFP_KERNEL);
223962306a36Sopenharmony_ci	if (!zero->addr)
224062306a36Sopenharmony_ci		goto err_kfree;
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &ops, -1, NULL);
224362306a36Sopenharmony_ci	if (!dev)
224462306a36Sopenharmony_ci		goto err_free_consistent;
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	dev->dev_data = eni_dev;
224762306a36Sopenharmony_ci	pci_set_drvdata(pci_dev, dev);
224862306a36Sopenharmony_ci	eni_dev->pci_dev = pci_dev;
224962306a36Sopenharmony_ci	eni_dev->asic = ent->driver_data;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	rc = eni_do_init(dev);
225262306a36Sopenharmony_ci	if (rc < 0)
225362306a36Sopenharmony_ci		goto err_unregister;
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	rc = eni_start(dev);
225662306a36Sopenharmony_ci	if (rc < 0)
225762306a36Sopenharmony_ci		goto err_eni_release;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	eni_dev->more = eni_boards;
226062306a36Sopenharmony_ci	eni_boards = dev;
226162306a36Sopenharmony_ciout:
226262306a36Sopenharmony_ci	return rc;
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_cierr_eni_release:
226562306a36Sopenharmony_ci	dev->phy = NULL;
226662306a36Sopenharmony_ci	iounmap(ENI_DEV(dev)->ioaddr);
226762306a36Sopenharmony_cierr_unregister:
226862306a36Sopenharmony_ci	atm_dev_deregister(dev);
226962306a36Sopenharmony_cierr_free_consistent:
227062306a36Sopenharmony_ci	dma_free_coherent(&pci_dev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma);
227162306a36Sopenharmony_cierr_kfree:
227262306a36Sopenharmony_ci	kfree(eni_dev);
227362306a36Sopenharmony_cierr_disable:
227462306a36Sopenharmony_ci	pci_disable_device(pci_dev);
227562306a36Sopenharmony_ci	goto out;
227662306a36Sopenharmony_ci}
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_cistatic const struct pci_device_id eni_pci_tbl[] = {
228062306a36Sopenharmony_ci	{ PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_FPGA), 0 /* FPGA */ },
228162306a36Sopenharmony_ci	{ PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_ASIC), 1 /* ASIC */ },
228262306a36Sopenharmony_ci	{ 0, }
228362306a36Sopenharmony_ci};
228462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci,eni_pci_tbl);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_cistatic void eni_remove_one(struct pci_dev *pdev)
228862306a36Sopenharmony_ci{
228962306a36Sopenharmony_ci	struct atm_dev *dev = pci_get_drvdata(pdev);
229062306a36Sopenharmony_ci	struct eni_dev *ed = ENI_DEV(dev);
229162306a36Sopenharmony_ci	struct eni_zero *zero = &ed->zero;
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	eni_do_release(dev);
229462306a36Sopenharmony_ci	atm_dev_deregister(dev);
229562306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma);
229662306a36Sopenharmony_ci	kfree(ed);
229762306a36Sopenharmony_ci	pci_disable_device(pdev);
229862306a36Sopenharmony_ci}
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_cistatic struct pci_driver eni_driver = {
230262306a36Sopenharmony_ci	.name		= DEV_LABEL,
230362306a36Sopenharmony_ci	.id_table	= eni_pci_tbl,
230462306a36Sopenharmony_ci	.probe		= eni_init_one,
230562306a36Sopenharmony_ci	.remove		= eni_remove_one,
230662306a36Sopenharmony_ci};
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_cistatic int __init eni_init(void)
231062306a36Sopenharmony_ci{
231162306a36Sopenharmony_ci	struct sk_buff *skb; /* dummy for sizeof */
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct eni_skb_prv));
231462306a36Sopenharmony_ci	return pci_register_driver(&eni_driver);
231562306a36Sopenharmony_ci}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_cimodule_init(eni_init);
231962306a36Sopenharmony_ci/* @@@ since exit routine not defined, this module can not be unloaded */
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2322