2005-04-16 22:20:36 +00:00
|
|
|
/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */
|
|
|
|
|
|
|
|
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
|
|
|
|
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/atm.h>
|
|
|
|
#include <linux/atmdev.h>
|
|
|
|
#include <linux/sonet.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/uio.h>
|
|
|
|
#include <linux/init.h>
|
2011-06-06 10:43:46 +00:00
|
|
|
#include <linux/interrupt.h>
|
2005-07-20 19:01:46 +00:00
|
|
|
#include <linux/dma-mapping.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/atm_zatm.h>
|
|
|
|
#include <linux/capability.h>
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/wait.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <asm/byteorder.h>
|
|
|
|
#include <asm/system.h>
|
|
|
|
#include <asm/string.h>
|
|
|
|
#include <asm/io.h>
|
2011-07-26 23:09:06 +00:00
|
|
|
#include <linux/atomic.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
|
|
#include "uPD98401.h"
|
|
|
|
#include "uPD98402.h"
|
|
|
|
#include "zeprom.h"
|
|
|
|
#include "zatm.h"
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO:
|
|
|
|
*
|
|
|
|
* Minor features
|
|
|
|
* - support 64 kB SDUs (will have to use multibuffer batches then :-( )
|
|
|
|
* - proper use of CDV, credit = max(1,CDVT*PCR)
|
|
|
|
* - AAL0
|
|
|
|
* - better receive timestamps
|
|
|
|
* - OAM
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define ZATM_COPPER 1
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
|
|
|
|
#else
|
|
|
|
#define DPRINTK(format,args...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef CONFIG_ATM_ZATM_DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
#define NULLCHECK(x)
|
|
|
|
|
|
|
|
#define EVENT(s,a,b)
|
|
|
|
|
|
|
|
|
|
|
|
static void event_dump(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NULL pointer checking
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define NULLCHECK(x) \
|
|
|
|
if ((unsigned long) (x) < 0x30) printk(KERN_CRIT #x "==0x%x\n", (int) (x))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Very extensive activity logging. Greatly improves bug detection speed but
|
|
|
|
* costs a few Mbps if enabled.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define EV 64
|
|
|
|
|
|
|
|
static const char *ev[EV];
|
|
|
|
static unsigned long ev_a[EV],ev_b[EV];
|
|
|
|
static int ec = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static void EVENT(const char *s,unsigned long a,unsigned long b)
|
|
|
|
{
|
|
|
|
ev[ec] = s;
|
|
|
|
ev_a[ec] = a;
|
|
|
|
ev_b[ec] = b;
|
|
|
|
ec = (ec+1) % EV;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void event_dump(void)
|
|
|
|
{
|
|
|
|
int n,i;
|
|
|
|
|
|
|
|
printk(KERN_NOTICE "----- event dump follows -----\n");
|
|
|
|
for (n = 0; n < EV; n++) {
|
|
|
|
i = (ec+n) % EV;
|
|
|
|
printk(KERN_NOTICE);
|
|
|
|
printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
|
|
|
|
}
|
|
|
|
printk(KERN_NOTICE "----- event dump ends here -----\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* CONFIG_ATM_ZATM_DEBUG */
|
|
|
|
|
|
|
|
|
|
|
|
#define RING_BUSY 1 /* indication from do_tx that PDU has to be
|
|
|
|
backlogged */
|
|
|
|
|
|
|
|
static struct atm_dev *zatm_boards = NULL;
|
|
|
|
static unsigned long dummy[2] = {0,0};
|
|
|
|
|
|
|
|
|
|
|
|
#define zin_n(r) inl(zatm_dev->base+r*4)
|
|
|
|
#define zin(r) inl(zatm_dev->base+uPD98401_##r*4)
|
|
|
|
#define zout(v,r) outl(v,zatm_dev->base+uPD98401_##r*4)
|
|
|
|
#define zwait while (zin(CMR) & uPD98401_BUSY)
|
|
|
|
|
|
|
|
/* RX0, RX1, TX0, TX1 */
|
|
|
|
static const int mbx_entries[NR_MBX] = { 1024,1024,1024,1024 };
|
|
|
|
static const int mbx_esize[NR_MBX] = { 16,16,4,4 }; /* entry size in bytes */
|
|
|
|
|
|
|
|
#define MBX_SIZE(i) (mbx_entries[i]*mbx_esize[i])
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------- utilities --------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
static void zpokel(struct zatm_dev *zatm_dev,u32 value,u32 addr)
|
|
|
|
{
|
|
|
|
zwait;
|
|
|
|
zout(value,CER);
|
|
|
|
zout(uPD98401_IND_ACC | uPD98401_IA_BALL |
|
|
|
|
(uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static u32 zpeekl(struct zatm_dev *zatm_dev,u32 addr)
|
|
|
|
{
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_IND_ACC | uPD98401_IA_BALL | uPD98401_IA_RW |
|
|
|
|
(uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
|
|
|
|
zwait;
|
|
|
|
return zin(CER);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------- free lists --------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free buffer head structure:
|
|
|
|
* [0] pointer to buffer (for SAR)
|
|
|
|
* [1] buffer descr link pointer (for SAR)
|
|
|
|
* [2] back pointer to skb (for poll_rx)
|
|
|
|
* [3] data
|
|
|
|
* ...
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct rx_buffer_head {
|
|
|
|
u32 buffer; /* pointer to buffer (for SAR) */
|
|
|
|
u32 link; /* buffer descriptor link pointer (for SAR) */
|
|
|
|
struct sk_buff *skb; /* back pointer to skb (for poll_rx) */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void refill_pool(struct atm_dev *dev,int pool)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct rx_buffer_head *first;
|
|
|
|
unsigned long flags;
|
|
|
|
int align,offset,free,count,size;
|
|
|
|
|
|
|
|
EVENT("refill_pool\n",0,0);
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
size = (64 << (pool <= ZATM_AAL5_POOL_BASE ? 0 :
|
|
|
|
pool-ZATM_AAL5_POOL_BASE))+sizeof(struct rx_buffer_head);
|
|
|
|
if (size < PAGE_SIZE) {
|
|
|
|
align = 32; /* for 32 byte alignment */
|
|
|
|
offset = sizeof(struct rx_buffer_head);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
align = 4096;
|
|
|
|
offset = zatm_dev->pool_info[pool].offset+
|
|
|
|
sizeof(struct rx_buffer_head);
|
|
|
|
}
|
|
|
|
size += align;
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
free = zpeekl(zatm_dev,zatm_dev->pool_base+2*pool) &
|
|
|
|
uPD98401_RXFP_REMAIN;
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
if (free >= zatm_dev->pool_info[pool].low_water) return;
|
|
|
|
EVENT("starting ... POOL: 0x%x, 0x%x\n",
|
|
|
|
zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),
|
|
|
|
zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));
|
|
|
|
EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
|
|
|
|
count = 0;
|
|
|
|
first = NULL;
|
|
|
|
while (free < zatm_dev->pool_info[pool].high_water) {
|
|
|
|
struct rx_buffer_head *head;
|
|
|
|
|
|
|
|
skb = alloc_skb(size,GFP_ATOMIC);
|
|
|
|
if (!skb) {
|
|
|
|
printk(KERN_WARNING DEV_LABEL "(Itf %d): got no new "
|
|
|
|
"skb (%d) with %d free\n",dev->number,size,free);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
skb_reserve(skb,(unsigned char *) ((((unsigned long) skb->data+
|
|
|
|
align+offset-1) & ~(unsigned long) (align-1))-offset)-
|
|
|
|
skb->data);
|
|
|
|
head = (struct rx_buffer_head *) skb->data;
|
|
|
|
skb_reserve(skb,sizeof(struct rx_buffer_head));
|
|
|
|
if (!first) first = head;
|
|
|
|
count++;
|
|
|
|
head->buffer = virt_to_bus(skb->data);
|
|
|
|
head->link = 0;
|
|
|
|
head->skb = skb;
|
|
|
|
EVENT("enq skb 0x%08lx/0x%08lx\n",(unsigned long) skb,
|
|
|
|
(unsigned long) head);
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
if (zatm_dev->last_free[pool])
|
|
|
|
((struct rx_buffer_head *) (zatm_dev->last_free[pool]->
|
|
|
|
data))[-1].link = virt_to_bus(head);
|
|
|
|
zatm_dev->last_free[pool] = skb;
|
|
|
|
skb_queue_tail(&zatm_dev->pool[pool],skb);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
free++;
|
|
|
|
}
|
|
|
|
if (first) {
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zwait;
|
|
|
|
zout(virt_to_bus(first),CER);
|
|
|
|
zout(uPD98401_ADD_BAT | (pool << uPD98401_POOL_SHIFT) | count,
|
|
|
|
CMR);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
EVENT ("POOL: 0x%x, 0x%x\n",
|
|
|
|
zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),
|
|
|
|
zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));
|
|
|
|
EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void drain_free(struct atm_dev *dev,int pool)
|
|
|
|
{
|
|
|
|
skb_queue_purge(&ZATM_DEV(dev)->pool[pool]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int pool_index(int max_pdu)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (max_pdu % ATM_CELL_PAYLOAD)
|
|
|
|
printk(KERN_ERR DEV_LABEL ": driver error in pool_index: "
|
|
|
|
"max_pdu is %d\n",max_pdu);
|
|
|
|
if (max_pdu > 65536) return -1;
|
|
|
|
for (i = 0; (64 << i) < max_pdu; i++);
|
|
|
|
return i+ZATM_AAL5_POOL_BASE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* use_pool isn't reentrant */
|
|
|
|
|
|
|
|
|
|
|
|
static void use_pool(struct atm_dev *dev,int pool)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
unsigned long flags;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
if (!(zatm_dev->pool_info[pool].ref_count++)) {
|
|
|
|
skb_queue_head_init(&zatm_dev->pool[pool]);
|
|
|
|
size = pool-ZATM_AAL5_POOL_BASE;
|
|
|
|
if (size < 0) size = 0; /* 64B... */
|
|
|
|
else if (size > 10) size = 10; /* ... 64kB */
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zpokel(zatm_dev,((zatm_dev->pool_info[pool].low_water/4) <<
|
|
|
|
uPD98401_RXFP_ALERT_SHIFT) |
|
|
|
|
(1 << uPD98401_RXFP_BTSZ_SHIFT) |
|
|
|
|
(size << uPD98401_RXFP_BFSZ_SHIFT),
|
|
|
|
zatm_dev->pool_base+pool*2);
|
|
|
|
zpokel(zatm_dev,(unsigned long) dummy,zatm_dev->pool_base+
|
|
|
|
pool*2+1);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
zatm_dev->last_free[pool] = NULL;
|
|
|
|
refill_pool(dev,pool);
|
|
|
|
}
|
|
|
|
DPRINTK("pool %d: %d\n",pool,zatm_dev->pool_info[pool].ref_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void unuse_pool(struct atm_dev *dev,int pool)
|
|
|
|
{
|
|
|
|
if (!(--ZATM_DEV(dev)->pool_info[pool].ref_count))
|
|
|
|
drain_free(dev,pool);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------- RX ------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
static void exception(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
static int count = 0;
|
|
|
|
struct zatm_dev *zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
struct zatm_vcc *zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
unsigned long *qrp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (count++ > 2) return;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
printk("TX%d: 0x%08lx\n",i,
|
|
|
|
zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+i));
|
|
|
|
for (i = 0; i < 5; i++)
|
|
|
|
printk("SH%d: 0x%08lx\n",i,
|
|
|
|
zpeekl(zatm_dev,uPD98401_IM(zatm_vcc->shaper)+16*i));
|
|
|
|
qrp = (unsigned long *) zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
|
|
|
|
uPD98401_TXVC_QRP);
|
|
|
|
printk("qrp=0x%08lx\n",(unsigned long) qrp);
|
|
|
|
for (i = 0; i < 4; i++) printk("QRP[%d]: 0x%08lx",i,qrp[i]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static const char *err_txt[] = {
|
|
|
|
"No error",
|
|
|
|
"RX buf underflow",
|
|
|
|
"RX FIFO overrun",
|
|
|
|
"Maximum len violation",
|
|
|
|
"CRC error",
|
|
|
|
"User abort",
|
|
|
|
"Length violation",
|
|
|
|
"T1 error",
|
|
|
|
"Deactivated",
|
|
|
|
"???",
|
|
|
|
"???",
|
|
|
|
"???",
|
|
|
|
"???",
|
|
|
|
"???",
|
|
|
|
"???",
|
|
|
|
"???"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void poll_rx(struct atm_dev *dev,int mbx)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
unsigned long pos;
|
|
|
|
u32 x;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
EVENT("poll_rx\n",0,0);
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
pos = (zatm_dev->mbx_start[mbx] & ~0xffffUL) | zin(MTA(mbx));
|
|
|
|
while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {
|
|
|
|
u32 *here;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct atm_vcc *vcc;
|
|
|
|
int cells,size,chan;
|
|
|
|
|
|
|
|
EVENT("MBX: host 0x%lx, nic 0x%x\n",pos,x);
|
|
|
|
here = (u32 *) pos;
|
|
|
|
if (((pos += 16) & 0xffff) == zatm_dev->mbx_end[mbx])
|
|
|
|
pos = zatm_dev->mbx_start[mbx];
|
|
|
|
cells = here[0] & uPD98401_AAL5_SIZE;
|
|
|
|
#if 0
|
|
|
|
printk("RX IND: 0x%x, 0x%x, 0x%x, 0x%x\n",here[0],here[1],here[2],here[3]);
|
|
|
|
{
|
|
|
|
unsigned long *x;
|
|
|
|
printk("POOL: 0x%08x, 0x%08x\n",zpeekl(zatm_dev,
|
|
|
|
zatm_dev->pool_base),
|
|
|
|
zpeekl(zatm_dev,zatm_dev->pool_base+1));
|
|
|
|
x = (unsigned long *) here[2];
|
|
|
|
printk("[0..3] = 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n",
|
|
|
|
x[0],x[1],x[2],x[3]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
error = 0;
|
|
|
|
if (here[3] & uPD98401_AAL5_ERR) {
|
|
|
|
error = (here[3] & uPD98401_AAL5_ES) >>
|
|
|
|
uPD98401_AAL5_ES_SHIFT;
|
|
|
|
if (error == uPD98401_AAL5_ES_DEACT ||
|
|
|
|
error == uPD98401_AAL5_ES_FREE) continue;
|
|
|
|
}
|
|
|
|
EVENT("error code 0x%x/0x%x\n",(here[3] & uPD98401_AAL5_ES) >>
|
|
|
|
uPD98401_AAL5_ES_SHIFT,error);
|
|
|
|
skb = ((struct rx_buffer_head *) bus_to_virt(here[2]))->skb;
|
2005-08-15 00:24:31 +00:00
|
|
|
__net_timestamp(skb);
|
2005-04-16 22:20:36 +00:00
|
|
|
#if 0
|
|
|
|
printk("[-3..0] 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",((unsigned *) skb->data)[-3],
|
|
|
|
((unsigned *) skb->data)[-2],((unsigned *) skb->data)[-1],
|
|
|
|
((unsigned *) skb->data)[0]);
|
|
|
|
#endif
|
|
|
|
EVENT("skb 0x%lx, here 0x%lx\n",(unsigned long) skb,
|
|
|
|
(unsigned long) here);
|
|
|
|
#if 0
|
|
|
|
printk("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
|
|
|
|
#endif
|
|
|
|
size = error ? 0 : ntohs(((__be16 *) skb->data)[cells*
|
|
|
|
ATM_CELL_PAYLOAD/sizeof(u16)-3]);
|
|
|
|
EVENT("got skb 0x%lx, size %d\n",(unsigned long) skb,size);
|
|
|
|
chan = (here[3] & uPD98401_AAL5_CHAN) >>
|
|
|
|
uPD98401_AAL5_CHAN_SHIFT;
|
|
|
|
if (chan < zatm_dev->chans && zatm_dev->rx_map[chan]) {
|
2005-09-02 19:18:03 +00:00
|
|
|
int pos;
|
2005-04-16 22:20:36 +00:00
|
|
|
vcc = zatm_dev->rx_map[chan];
|
2005-09-02 19:18:03 +00:00
|
|
|
pos = ZATM_VCC(vcc)->pool;
|
2005-08-10 02:25:21 +00:00
|
|
|
if (skb == zatm_dev->last_free[pos])
|
|
|
|
zatm_dev->last_free[pos] = NULL;
|
|
|
|
skb_unlink(skb, zatm_dev->pool + pos);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): RX indication "
|
|
|
|
"for non-existing channel\n",dev->number);
|
|
|
|
size = 0;
|
|
|
|
vcc = NULL;
|
|
|
|
event_dump();
|
|
|
|
}
|
|
|
|
if (error) {
|
|
|
|
static unsigned long silence = 0;
|
|
|
|
static int last_error = 0;
|
|
|
|
|
|
|
|
if (error != last_error ||
|
|
|
|
time_after(jiffies, silence) || silence == 0){
|
|
|
|
printk(KERN_WARNING DEV_LABEL "(itf %d): "
|
|
|
|
"chan %d error %s\n",dev->number,chan,
|
|
|
|
err_txt[error]);
|
|
|
|
last_error = error;
|
|
|
|
silence = (jiffies+2*HZ)|1;
|
|
|
|
}
|
|
|
|
size = 0;
|
|
|
|
}
|
|
|
|
if (size && (size > cells*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER ||
|
|
|
|
size <= (cells-1)*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER)) {
|
|
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): size %d with %d "
|
|
|
|
"cells\n",dev->number,size,cells);
|
|
|
|
size = 0;
|
|
|
|
event_dump();
|
|
|
|
}
|
|
|
|
if (size > ATM_MAX_AAL5_PDU) {
|
|
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): size too big "
|
|
|
|
"(%d)\n",dev->number,size);
|
|
|
|
size = 0;
|
|
|
|
event_dump();
|
|
|
|
}
|
|
|
|
if (!size) {
|
|
|
|
dev_kfree_skb_irq(skb);
|
|
|
|
if (vcc) atomic_inc(&vcc->stats->rx_err);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!atm_charge(vcc,skb->truesize)) {
|
|
|
|
dev_kfree_skb_irq(skb);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
skb->len = size;
|
|
|
|
ATM_SKB(skb)->vcc = vcc;
|
|
|
|
vcc->push(vcc,skb);
|
|
|
|
atomic_inc(&vcc->stats->rx);
|
|
|
|
}
|
|
|
|
zout(pos & 0xffff,MTA(mbx));
|
|
|
|
#if 0 /* probably a stupid idea */
|
|
|
|
refill_pool(dev,zatm_vcc->pool);
|
|
|
|
/* maybe this saves us a few interrupts */
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int open_rx_first(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned short chan;
|
|
|
|
int cells;
|
|
|
|
|
|
|
|
DPRINTK("open_rx_first (0x%x)\n",inb_p(0xc053));
|
|
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
zatm_vcc->rx_chan = 0;
|
|
|
|
if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;
|
|
|
|
if (vcc->qos.aal == ATM_AAL5) {
|
|
|
|
if (vcc->qos.rxtp.max_sdu > 65464)
|
|
|
|
vcc->qos.rxtp.max_sdu = 65464;
|
|
|
|
/* fix this - we may want to receive 64kB SDUs
|
|
|
|
later */
|
drivers/atm: Use DIV_ROUND_UP
The kernel.h macro DIV_ROUND_UP performs the computation (((n) + (d) - 1) /
(d)) but is perhaps more readable.
In the case of the file drivers/atm/eni.c, I am a little bit suspicious of
the -1 at the end of the affected expression. Please check that that is
what is wanted.
An extract of the semantic patch that makes this change is as follows:
(http://www.emn.fr/x-info/coccinelle/)
// <smpl>
@haskernel@
@@
#include <linux/kernel.h>
@depends on haskernel@
expression n,d;
@@
(
- (n + d - 1) / d
+ DIV_ROUND_UP(n,d)
|
- (n + (d - 1)) / d
+ DIV_ROUND_UP(n,d)
)
@depends on haskernel@
expression n,d;
@@
- DIV_ROUND_UP((n),d)
+ DIV_ROUND_UP(n,d)
@depends on haskernel@
expression n,d;
@@
- DIV_ROUND_UP(n,(d))
+ DIV_ROUND_UP(n,d)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-09-23 02:22:58 +00:00
|
|
|
cells = DIV_ROUND_UP(vcc->qos.rxtp.max_sdu + ATM_AAL5_TRAILER,
|
|
|
|
ATM_CELL_PAYLOAD);
|
2005-04-16 22:20:36 +00:00
|
|
|
zatm_vcc->pool = pool_index(cells*ATM_CELL_PAYLOAD);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
cells = 1;
|
|
|
|
zatm_vcc->pool = ZATM_AAL0_POOL;
|
|
|
|
}
|
|
|
|
if (zatm_vcc->pool < 0) return -EMSGSIZE;
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_OPEN_CHAN,CMR);
|
|
|
|
zwait;
|
|
|
|
DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
|
|
|
|
chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
DPRINTK("chan is %d\n",chan);
|
|
|
|
if (!chan) return -EAGAIN;
|
|
|
|
use_pool(vcc->dev,zatm_vcc->pool);
|
|
|
|
DPRINTK("pool %d\n",zatm_vcc->pool);
|
|
|
|
/* set up VC descriptor */
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zpokel(zatm_dev,zatm_vcc->pool << uPD98401_RXVC_POOL_SHIFT,
|
|
|
|
chan*VC_SIZE/4);
|
|
|
|
zpokel(zatm_dev,uPD98401_RXVC_OD | (vcc->qos.aal == ATM_AAL5 ?
|
|
|
|
uPD98401_RXVC_AR : 0) | cells,chan*VC_SIZE/4+1);
|
|
|
|
zpokel(zatm_dev,0,chan*VC_SIZE/4+2);
|
|
|
|
zatm_vcc->rx_chan = chan;
|
|
|
|
zatm_dev->rx_map[chan] = vcc;
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int open_rx_second(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
unsigned long flags;
|
|
|
|
int pos,shift;
|
|
|
|
|
|
|
|
DPRINTK("open_rx_second (0x%x)\n",inb_p(0xc053));
|
|
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
if (!zatm_vcc->rx_chan) return 0;
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
/* should also handle VPI @@@ */
|
|
|
|
pos = vcc->vci >> 1;
|
|
|
|
shift = (1-(vcc->vci & 1)) << 4;
|
|
|
|
zpokel(zatm_dev,(zpeekl(zatm_dev,pos) & ~(0xffff << shift)) |
|
|
|
|
((zatm_vcc->rx_chan | uPD98401_RXLT_ENBL) << shift),pos);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void close_rx(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
unsigned long flags;
|
|
|
|
int pos,shift;
|
|
|
|
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
if (!zatm_vcc->rx_chan) return;
|
|
|
|
DPRINTK("close_rx\n");
|
|
|
|
/* disable receiver */
|
|
|
|
if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
pos = vcc->vci >> 1;
|
|
|
|
shift = (1-(vcc->vci & 1)) << 4;
|
|
|
|
zpokel(zatm_dev,zpeekl(zatm_dev,pos) & ~(0xffff << shift),pos);
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_NOP,CMR);
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_NOP,CMR);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
}
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_DEACT_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
|
|
|
|
uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
|
|
zwait;
|
|
|
|
udelay(10); /* why oh why ... ? */
|
|
|
|
zout(uPD98401_CLOSE_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
|
|
|
|
uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
|
|
zwait;
|
|
|
|
if (!(zin(CMR) & uPD98401_CHAN_ADDR))
|
|
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): can't close RX channel "
|
|
|
|
"%d\n",vcc->dev->number,zatm_vcc->rx_chan);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
zatm_dev->rx_map[zatm_vcc->rx_chan] = NULL;
|
|
|
|
zatm_vcc->rx_chan = 0;
|
|
|
|
unuse_pool(vcc->dev,zatm_vcc->pool);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int start_rx(struct atm_dev *dev)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
int size,i;
|
|
|
|
|
|
|
|
DPRINTK("start_rx\n");
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
size = sizeof(struct atm_vcc *)*zatm_dev->chans;
|
2006-10-03 23:27:18 +00:00
|
|
|
zatm_dev->rx_map = kzalloc(size,GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!zatm_dev->rx_map) return -ENOMEM;
|
|
|
|
/* set VPI/VCI split (use all VCIs and give what's left to VPIs) */
|
|
|
|
zpokel(zatm_dev,(1 << dev->ci_range.vci_bits)-1,uPD98401_VRR);
|
|
|
|
/* prepare free buffer pools */
|
|
|
|
for (i = 0; i <= ZATM_LAST_POOL; i++) {
|
|
|
|
zatm_dev->pool_info[i].ref_count = 0;
|
|
|
|
zatm_dev->pool_info[i].rqa_count = 0;
|
|
|
|
zatm_dev->pool_info[i].rqu_count = 0;
|
|
|
|
zatm_dev->pool_info[i].low_water = LOW_MARK;
|
|
|
|
zatm_dev->pool_info[i].high_water = HIGH_MARK;
|
|
|
|
zatm_dev->pool_info[i].offset = 0;
|
|
|
|
zatm_dev->pool_info[i].next_off = 0;
|
|
|
|
zatm_dev->pool_info[i].next_cnt = 0;
|
|
|
|
zatm_dev->pool_info[i].next_thres = OFF_CNG_THRES;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------- TX ------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
static int do_tx(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct atm_vcc *vcc;
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
u32 *dsc;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
EVENT("do_tx\n",0,0);
|
|
|
|
DPRINTK("sending skb %p\n",skb);
|
|
|
|
vcc = ATM_SKB(skb)->vcc;
|
|
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
EVENT("iovcnt=%d\n",skb_shinfo(skb)->nr_frags,0);
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
if (!skb_shinfo(skb)->nr_frags) {
|
|
|
|
if (zatm_vcc->txing == RING_ENTRIES-1) {
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
return RING_BUSY;
|
|
|
|
}
|
|
|
|
zatm_vcc->txing++;
|
|
|
|
dsc = zatm_vcc->ring+zatm_vcc->ring_curr;
|
|
|
|
zatm_vcc->ring_curr = (zatm_vcc->ring_curr+RING_WORDS) &
|
|
|
|
(RING_ENTRIES*RING_WORDS-1);
|
|
|
|
dsc[1] = 0;
|
|
|
|
dsc[2] = skb->len;
|
|
|
|
dsc[3] = virt_to_bus(skb->data);
|
|
|
|
mb();
|
|
|
|
dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP | uPD98401_TXPD_SM
|
|
|
|
| (vcc->qos.aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 |
|
|
|
|
(ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ?
|
|
|
|
uPD98401_CLPM_1 : uPD98401_CLPM_0));
|
|
|
|
EVENT("dsc (0x%lx)\n",(unsigned long) dsc,0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
printk("NONONONOO!!!!\n");
|
|
|
|
dsc = NULL;
|
|
|
|
#if 0
|
|
|
|
u32 *put;
|
|
|
|
int i;
|
|
|
|
|
2006-01-10 21:09:01 +00:00
|
|
|
dsc = kmalloc(uPD98401_TXPD_SIZE * 2 +
|
|
|
|
uPD98401_TXBD_SIZE * ATM_SKB(skb)->iovcnt, GFP_ATOMIC);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!dsc) {
|
2006-01-10 21:09:01 +00:00
|
|
|
if (vcc->pop)
|
|
|
|
vcc->pop(vcc, skb);
|
|
|
|
else
|
|
|
|
dev_kfree_skb_irq(skb);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
/* @@@ should check alignment */
|
|
|
|
put = dsc+8;
|
|
|
|
dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP |
|
|
|
|
(vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 |
|
|
|
|
(ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ?
|
|
|
|
uPD98401_CLPM_1 : uPD98401_CLPM_0));
|
|
|
|
dsc[1] = 0;
|
2006-01-10 21:09:01 +00:00
|
|
|
dsc[2] = ATM_SKB(skb)->iovcnt * uPD98401_TXBD_SIZE;
|
2005-04-16 22:20:36 +00:00
|
|
|
dsc[3] = virt_to_bus(put);
|
|
|
|
for (i = 0; i < ATM_SKB(skb)->iovcnt; i++) {
|
|
|
|
*put++ = ((struct iovec *) skb->data)[i].iov_len;
|
|
|
|
*put++ = virt_to_bus(((struct iovec *)
|
|
|
|
skb->data)[i].iov_base);
|
|
|
|
}
|
|
|
|
put[-2] |= uPD98401_TXBD_LAST;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
ZATM_PRV_DSC(skb) = dsc;
|
|
|
|
skb_queue_tail(&zatm_vcc->tx_queue,skb);
|
|
|
|
DPRINTK("QRP=0x%08lx\n",zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
|
|
|
|
uPD98401_TXVC_QRP));
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_TX_READY | (zatm_vcc->tx_chan <<
|
|
|
|
uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
EVENT("done\n",0,0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline void dequeue_tx(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
EVENT("dequeue_tx\n",0,0);
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
skb = skb_dequeue(&zatm_vcc->tx_queue);
|
|
|
|
if (!skb) {
|
|
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): dequeue_tx but not "
|
|
|
|
"txing\n",vcc->dev->number);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#if 0 /* @@@ would fail on CLP */
|
|
|
|
if (*ZATM_PRV_DSC(skb) != (uPD98401_TXPD_V | uPD98401_TXPD_DP |
|
|
|
|
uPD98401_TXPD_SM | uPD98401_TXPD_AAL5)) printk("@#*$!!!! (%08x)\n",
|
|
|
|
*ZATM_PRV_DSC(skb));
|
|
|
|
#endif
|
|
|
|
*ZATM_PRV_DSC(skb) = 0; /* mark as invalid */
|
|
|
|
zatm_vcc->txing--;
|
|
|
|
if (vcc->pop) vcc->pop(vcc,skb);
|
|
|
|
else dev_kfree_skb_irq(skb);
|
|
|
|
while ((skb = skb_dequeue(&zatm_vcc->backlog)))
|
|
|
|
if (do_tx(skb) == RING_BUSY) {
|
|
|
|
skb_queue_head(&zatm_vcc->backlog,skb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
atomic_inc(&vcc->stats->tx);
|
|
|
|
wake_up(&zatm_vcc->tx_wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void poll_tx(struct atm_dev *dev,int mbx)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
unsigned long pos;
|
|
|
|
u32 x;
|
|
|
|
|
|
|
|
EVENT("poll_tx\n",0,0);
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
pos = (zatm_dev->mbx_start[mbx] & ~0xffffUL) | zin(MTA(mbx));
|
|
|
|
while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {
|
|
|
|
int chan;
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
u32 data,*addr;
|
|
|
|
|
|
|
|
EVENT("MBX: host 0x%lx, nic 0x%x\n",pos,x);
|
|
|
|
addr = (u32 *) pos;
|
|
|
|
data = *addr;
|
|
|
|
chan = (data & uPD98401_TXI_CONN) >> uPD98401_TXI_CONN_SHIFT;
|
|
|
|
EVENT("addr = 0x%lx, data = 0x%08x,",(unsigned long) addr,
|
|
|
|
data);
|
|
|
|
EVENT("chan = %d\n",chan,0);
|
|
|
|
#else
|
|
|
|
NO !
|
|
|
|
chan = (zatm_dev->mbx_start[mbx][pos >> 2] & uPD98401_TXI_CONN)
|
|
|
|
>> uPD98401_TXI_CONN_SHIFT;
|
|
|
|
#endif
|
|
|
|
if (chan < zatm_dev->chans && zatm_dev->tx_map[chan])
|
|
|
|
dequeue_tx(zatm_dev->tx_map[chan]);
|
|
|
|
else {
|
|
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): TX indication "
|
|
|
|
"for non-existing channel %d\n",dev->number,chan);
|
|
|
|
event_dump();
|
|
|
|
}
|
|
|
|
if (((pos += 4) & 0xffff) == zatm_dev->mbx_end[mbx])
|
|
|
|
pos = zatm_dev->mbx_start[mbx];
|
|
|
|
}
|
|
|
|
zout(pos & 0xffff,MTA(mbx));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* BUG BUG BUG: Doesn't handle "new-style" rate specification yet.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int alloc_shaper(struct atm_dev *dev,int *pcr,int min,int max,int ubr)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned long i,m,c;
|
|
|
|
int shaper;
|
|
|
|
|
|
|
|
DPRINTK("alloc_shaper (min = %d, max = %d)\n",min,max);
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
if (!zatm_dev->free_shapers) return -EAGAIN;
|
|
|
|
for (shaper = 0; !((zatm_dev->free_shapers >> shaper) & 1); shaper++);
|
|
|
|
zatm_dev->free_shapers &= ~1 << shaper;
|
|
|
|
if (ubr) {
|
|
|
|
c = 5;
|
|
|
|
i = m = 1;
|
|
|
|
zatm_dev->ubr_ref_cnt++;
|
|
|
|
zatm_dev->ubr = shaper;
|
2006-10-03 23:28:31 +00:00
|
|
|
*pcr = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (min) {
|
|
|
|
if (min <= 255) {
|
|
|
|
i = min;
|
|
|
|
m = ATM_OC3_PCR;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
i = 255;
|
|
|
|
m = ATM_OC3_PCR*255/min;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (max > zatm_dev->tx_bw) max = zatm_dev->tx_bw;
|
|
|
|
if (max <= 255) {
|
|
|
|
i = max;
|
|
|
|
m = ATM_OC3_PCR;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
i = 255;
|
drivers/atm: Use DIV_ROUND_UP
The kernel.h macro DIV_ROUND_UP performs the computation (((n) + (d) - 1) /
(d)) but is perhaps more readable.
In the case of the file drivers/atm/eni.c, I am a little bit suspicious of
the -1 at the end of the affected expression. Please check that that is
what is wanted.
An extract of the semantic patch that makes this change is as follows:
(http://www.emn.fr/x-info/coccinelle/)
// <smpl>
@haskernel@
@@
#include <linux/kernel.h>
@depends on haskernel@
expression n,d;
@@
(
- (n + d - 1) / d
+ DIV_ROUND_UP(n,d)
|
- (n + (d - 1)) / d
+ DIV_ROUND_UP(n,d)
)
@depends on haskernel@
expression n,d;
@@
- DIV_ROUND_UP((n),d)
+ DIV_ROUND_UP(n,d)
@depends on haskernel@
expression n,d;
@@
- DIV_ROUND_UP(n,(d))
+ DIV_ROUND_UP(n,d)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-09-23 02:22:58 +00:00
|
|
|
m = DIV_ROUND_UP(ATM_OC3_PCR*255, max);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i > m) {
|
|
|
|
printk(KERN_CRIT DEV_LABEL "shaper algorithm botched "
|
|
|
|
"[%d,%d] -> i=%ld,m=%ld\n",min,max,i,m);
|
|
|
|
m = i;
|
|
|
|
}
|
|
|
|
*pcr = i*ATM_OC3_PCR/m;
|
|
|
|
c = 20; /* @@@ should use max_cdv ! */
|
|
|
|
if ((min && *pcr < min) || (max && *pcr > max)) return -EINVAL;
|
|
|
|
if (zatm_dev->tx_bw < *pcr) return -EAGAIN;
|
|
|
|
zatm_dev->tx_bw -= *pcr;
|
|
|
|
}
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
DPRINTK("i = %d, m = %d, PCR = %d\n",i,m,*pcr);
|
|
|
|
zpokel(zatm_dev,(i << uPD98401_IM_I_SHIFT) | m,uPD98401_IM(shaper));
|
|
|
|
zpokel(zatm_dev,c << uPD98401_PC_C_SHIFT,uPD98401_PC(shaper));
|
|
|
|
zpokel(zatm_dev,0,uPD98401_X(shaper));
|
|
|
|
zpokel(zatm_dev,0,uPD98401_Y(shaper));
|
|
|
|
zpokel(zatm_dev,uPD98401_PS_E,uPD98401_PS(shaper));
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
return shaper;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void dealloc_shaper(struct atm_dev *dev,int shaper)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
if (shaper == zatm_dev->ubr) {
|
|
|
|
if (--zatm_dev->ubr_ref_cnt) return;
|
|
|
|
zatm_dev->ubr = -1;
|
|
|
|
}
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zpokel(zatm_dev,zpeekl(zatm_dev,uPD98401_PS(shaper)) & ~uPD98401_PS_E,
|
|
|
|
uPD98401_PS(shaper));
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
zatm_dev->free_shapers |= 1 << shaper;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void close_tx(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
unsigned long flags;
|
|
|
|
int chan;
|
|
|
|
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
chan = zatm_vcc->tx_chan;
|
|
|
|
if (!chan) return;
|
|
|
|
DPRINTK("close_tx\n");
|
|
|
|
if (skb_peek(&zatm_vcc->backlog)) {
|
|
|
|
printk("waiting for backlog to drain ...\n");
|
|
|
|
event_dump();
|
|
|
|
wait_event(zatm_vcc->tx_wait, !skb_peek(&zatm_vcc->backlog));
|
|
|
|
}
|
|
|
|
if (skb_peek(&zatm_vcc->tx_queue)) {
|
|
|
|
printk("waiting for TX queue to drain ...\n");
|
|
|
|
event_dump();
|
|
|
|
wait_event(zatm_vcc->tx_wait, !skb_peek(&zatm_vcc->tx_queue));
|
|
|
|
}
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
#if 0
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_DEACT_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
|
|
#endif
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_CLOSE_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
|
|
zwait;
|
|
|
|
if (!(zin(CMR) & uPD98401_CHAN_ADDR))
|
|
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): can't close TX channel "
|
|
|
|
"%d\n",vcc->dev->number,chan);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
zatm_vcc->tx_chan = 0;
|
|
|
|
zatm_dev->tx_map[chan] = NULL;
|
|
|
|
if (zatm_vcc->shaper != zatm_dev->ubr) {
|
|
|
|
zatm_dev->tx_bw += vcc->qos.txtp.min_pcr;
|
|
|
|
dealloc_shaper(vcc->dev,zatm_vcc->shaper);
|
|
|
|
}
|
2005-06-02 20:04:07 +00:00
|
|
|
kfree(zatm_vcc->ring);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int open_tx_first(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
unsigned long flags;
|
|
|
|
u32 *loop;
|
|
|
|
unsigned short chan;
|
2007-07-17 09:39:58 +00:00
|
|
|
int unlimited;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DPRINTK("open_tx_first\n");
|
|
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
zatm_vcc->tx_chan = 0;
|
|
|
|
if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_OPEN_CHAN,CMR);
|
|
|
|
zwait;
|
|
|
|
DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
|
|
|
|
chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
DPRINTK("chan is %d\n",chan);
|
|
|
|
if (!chan) return -EAGAIN;
|
|
|
|
unlimited = vcc->qos.txtp.traffic_class == ATM_UBR &&
|
|
|
|
(!vcc->qos.txtp.max_pcr || vcc->qos.txtp.max_pcr == ATM_MAX_PCR ||
|
|
|
|
vcc->qos.txtp.max_pcr >= ATM_OC3_PCR);
|
|
|
|
if (unlimited && zatm_dev->ubr != -1) zatm_vcc->shaper = zatm_dev->ubr;
|
|
|
|
else {
|
2007-07-17 09:39:58 +00:00
|
|
|
int uninitialized_var(pcr);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (unlimited) vcc->qos.txtp.max_sdu = ATM_MAX_AAL5_PDU;
|
|
|
|
if ((zatm_vcc->shaper = alloc_shaper(vcc->dev,&pcr,
|
|
|
|
vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,unlimited))
|
|
|
|
< 0) {
|
|
|
|
close_tx(vcc);
|
|
|
|
return zatm_vcc->shaper;
|
|
|
|
}
|
|
|
|
if (pcr > ATM_OC3_PCR) pcr = ATM_OC3_PCR;
|
|
|
|
vcc->qos.txtp.min_pcr = vcc->qos.txtp.max_pcr = pcr;
|
|
|
|
}
|
|
|
|
zatm_vcc->tx_chan = chan;
|
|
|
|
skb_queue_head_init(&zatm_vcc->tx_queue);
|
|
|
|
init_waitqueue_head(&zatm_vcc->tx_wait);
|
|
|
|
/* initialize ring */
|
2006-10-03 23:27:18 +00:00
|
|
|
zatm_vcc->ring = kzalloc(RING_SIZE,GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!zatm_vcc->ring) return -ENOMEM;
|
|
|
|
loop = zatm_vcc->ring+RING_ENTRIES*RING_WORDS;
|
|
|
|
loop[0] = uPD98401_TXPD_V;
|
|
|
|
loop[1] = loop[2] = 0;
|
|
|
|
loop[3] = virt_to_bus(zatm_vcc->ring);
|
|
|
|
zatm_vcc->ring_curr = 0;
|
|
|
|
zatm_vcc->txing = 0;
|
|
|
|
skb_queue_head_init(&zatm_vcc->backlog);
|
|
|
|
zpokel(zatm_dev,virt_to_bus(zatm_vcc->ring),
|
|
|
|
chan*VC_SIZE/4+uPD98401_TXVC_QRP);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int open_tx_second(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
DPRINTK("open_tx_second\n");
|
|
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
|
|
if (!zatm_vcc->tx_chan) return 0;
|
|
|
|
/* set up VC descriptor */
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4);
|
|
|
|
zpokel(zatm_dev,uPD98401_TXVC_L | (zatm_vcc->shaper <<
|
|
|
|
uPD98401_TXVC_SHP_SHIFT) | (vcc->vpi << uPD98401_TXVC_VPI_SHIFT) |
|
|
|
|
vcc->vci,zatm_vcc->tx_chan*VC_SIZE/4+1);
|
|
|
|
zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4+2);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
zatm_dev->tx_map[zatm_vcc->tx_chan] = vcc;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int start_tx(struct atm_dev *dev)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DPRINTK("start_tx\n");
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
2006-12-13 08:35:56 +00:00
|
|
|
zatm_dev->tx_map = kmalloc(sizeof(struct atm_vcc *)*
|
2005-04-16 22:20:36 +00:00
|
|
|
zatm_dev->chans,GFP_KERNEL);
|
|
|
|
if (!zatm_dev->tx_map) return -ENOMEM;
|
|
|
|
zatm_dev->tx_bw = ATM_OC3_PCR;
|
|
|
|
zatm_dev->free_shapers = (1 << NR_SHAPERS)-1;
|
|
|
|
zatm_dev->ubr = -1;
|
|
|
|
zatm_dev->ubr_ref_cnt = 0;
|
|
|
|
/* initialize shapers */
|
|
|
|
for (i = 0; i < NR_SHAPERS; i++) zpokel(zatm_dev,0,uPD98401_PS(i));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------- interrupts --------------------------------*/
|
|
|
|
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 13:55:46 +00:00
|
|
|
static irqreturn_t zatm_int(int irq,void *dev_id)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct atm_dev *dev;
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
u32 reason;
|
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
dev = dev_id;
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
while ((reason = zin(GSR))) {
|
|
|
|
handled = 1;
|
|
|
|
EVENT("reason 0x%x\n",reason,0);
|
|
|
|
if (reason & uPD98401_INT_PI) {
|
|
|
|
EVENT("PHY int\n",0,0);
|
|
|
|
dev->phy->interrupt(dev);
|
|
|
|
}
|
|
|
|
if (reason & uPD98401_INT_RQA) {
|
|
|
|
unsigned long pools;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pools = zin(RQA);
|
|
|
|
EVENT("RQA (0x%08x)\n",pools,0);
|
|
|
|
for (i = 0; pools; i++) {
|
|
|
|
if (pools & 1) {
|
|
|
|
refill_pool(dev,i);
|
|
|
|
zatm_dev->pool_info[i].rqa_count++;
|
|
|
|
}
|
|
|
|
pools >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (reason & uPD98401_INT_RQU) {
|
|
|
|
unsigned long pools;
|
|
|
|
int i;
|
|
|
|
pools = zin(RQU);
|
|
|
|
printk(KERN_WARNING DEV_LABEL "(itf %d): RQU 0x%08lx\n",
|
|
|
|
dev->number,pools);
|
|
|
|
event_dump();
|
|
|
|
for (i = 0; pools; i++) {
|
|
|
|
if (pools & 1) {
|
|
|
|
refill_pool(dev,i);
|
|
|
|
zatm_dev->pool_info[i].rqu_count++;
|
|
|
|
}
|
|
|
|
pools >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* don't handle RD */
|
|
|
|
if (reason & uPD98401_INT_SPE)
|
|
|
|
printk(KERN_ALERT DEV_LABEL "(itf %d): system parity "
|
|
|
|
"error at 0x%08x\n",dev->number,zin(ADDR));
|
|
|
|
if (reason & uPD98401_INT_CPE)
|
|
|
|
printk(KERN_ALERT DEV_LABEL "(itf %d): control memory "
|
|
|
|
"parity error at 0x%08x\n",dev->number,zin(ADDR));
|
|
|
|
if (reason & uPD98401_INT_SBE) {
|
|
|
|
printk(KERN_ALERT DEV_LABEL "(itf %d): system bus "
|
|
|
|
"error at 0x%08x\n",dev->number,zin(ADDR));
|
|
|
|
event_dump();
|
|
|
|
}
|
|
|
|
/* don't handle IND */
|
|
|
|
if (reason & uPD98401_INT_MF) {
|
|
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): mailbox full "
|
|
|
|
"(0x%x)\n",dev->number,(reason & uPD98401_INT_MF)
|
|
|
|
>> uPD98401_INT_MF_SHIFT);
|
|
|
|
event_dump();
|
|
|
|
/* @@@ should try to recover */
|
|
|
|
}
|
|
|
|
if (reason & uPD98401_INT_MM) {
|
|
|
|
if (reason & 1) poll_rx(dev,0);
|
|
|
|
if (reason & 2) poll_rx(dev,1);
|
|
|
|
if (reason & 4) poll_tx(dev,2);
|
|
|
|
if (reason & 8) poll_tx(dev,3);
|
|
|
|
}
|
|
|
|
/* @@@ handle RCRn */
|
|
|
|
}
|
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------- (E)EPROM access -----------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
static void __devinit eprom_set(struct zatm_dev *zatm_dev,unsigned long value,
|
|
|
|
unsigned short cmd)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if ((error = pci_write_config_dword(zatm_dev->pci_dev,cmd,value)))
|
|
|
|
printk(KERN_ERR DEV_LABEL ": PCI write failed (0x%02x)\n",
|
|
|
|
error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned long __devinit eprom_get(struct zatm_dev *zatm_dev,
|
|
|
|
unsigned short cmd)
|
|
|
|
{
|
|
|
|
unsigned int value;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if ((error = pci_read_config_dword(zatm_dev->pci_dev,cmd,&value)))
|
|
|
|
printk(KERN_ERR DEV_LABEL ": PCI read failed (0x%02x)\n",
|
|
|
|
error);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void __devinit eprom_put_bits(struct zatm_dev *zatm_dev,
|
|
|
|
unsigned long data,int bits,unsigned short cmd)
|
|
|
|
{
|
|
|
|
unsigned long value;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = bits-1; i >= 0; i--) {
|
|
|
|
value = ZEPROM_CS | (((data >> i) & 1) ? ZEPROM_DI : 0);
|
|
|
|
eprom_set(zatm_dev,value,cmd);
|
|
|
|
eprom_set(zatm_dev,value | ZEPROM_SK,cmd);
|
|
|
|
eprom_set(zatm_dev,value,cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void __devinit eprom_get_byte(struct zatm_dev *zatm_dev,
|
|
|
|
unsigned char *byte,unsigned short cmd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
*byte = 0;
|
|
|
|
for (i = 8; i; i--) {
|
|
|
|
eprom_set(zatm_dev,ZEPROM_CS,cmd);
|
|
|
|
eprom_set(zatm_dev,ZEPROM_CS | ZEPROM_SK,cmd);
|
|
|
|
*byte <<= 1;
|
|
|
|
if (eprom_get(zatm_dev,cmd) & ZEPROM_DO) *byte |= 1;
|
|
|
|
eprom_set(zatm_dev,ZEPROM_CS,cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned char __devinit eprom_try_esi(struct atm_dev *dev,
|
|
|
|
unsigned short cmd,int offset,int swap)
|
|
|
|
{
|
|
|
|
unsigned char buf[ZEPROM_SIZE];
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
for (i = 0; i < ZEPROM_SIZE; i += 2) {
|
|
|
|
eprom_set(zatm_dev,ZEPROM_CS,cmd); /* select EPROM */
|
|
|
|
eprom_put_bits(zatm_dev,ZEPROM_CMD_READ,ZEPROM_CMD_LEN,cmd);
|
|
|
|
eprom_put_bits(zatm_dev,i >> 1,ZEPROM_ADDR_LEN,cmd);
|
|
|
|
eprom_get_byte(zatm_dev,buf+i+swap,cmd);
|
|
|
|
eprom_get_byte(zatm_dev,buf+i+1-swap,cmd);
|
|
|
|
eprom_set(zatm_dev,0,cmd); /* deselect EPROM */
|
|
|
|
}
|
|
|
|
memcpy(dev->esi,buf+offset,ESI_LEN);
|
|
|
|
return memcmp(dev->esi,"\0\0\0\0\0",ESI_LEN); /* assumes ESI_LEN == 6 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void __devinit eprom_get_esi(struct atm_dev *dev)
|
|
|
|
{
|
|
|
|
if (eprom_try_esi(dev,ZEPROM_V1_REG,ZEPROM_V1_ESI_OFF,1)) return;
|
|
|
|
(void) eprom_try_esi(dev,ZEPROM_V2_REG,ZEPROM_V2_ESI_OFF,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------- entries ---------------------------------*/
|
|
|
|
|
|
|
|
|
2007-03-14 09:05:01 +00:00
|
|
|
static int __devinit zatm_init(struct atm_dev *dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct pci_dev *pci_dev;
|
|
|
|
unsigned short command;
|
|
|
|
int error,i,last;
|
|
|
|
unsigned long t0,t1,t2;
|
|
|
|
|
|
|
|
DPRINTK(">zatm_init\n");
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
spin_lock_init(&zatm_dev->lock);
|
|
|
|
pci_dev = zatm_dev->pci_dev;
|
|
|
|
zatm_dev->base = pci_resource_start(pci_dev, 0);
|
|
|
|
zatm_dev->irq = pci_dev->irq;
|
2007-06-08 22:46:36 +00:00
|
|
|
if ((error = pci_read_config_word(pci_dev,PCI_COMMAND,&command))) {
|
2005-04-16 22:20:36 +00:00
|
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): init error 0x%02x\n",
|
|
|
|
dev->number,error);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if ((error = pci_write_config_word(pci_dev,PCI_COMMAND,
|
|
|
|
command | PCI_COMMAND_IO | PCI_COMMAND_MASTER))) {
|
|
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): can't enable IO (0x%02x)"
|
|
|
|
"\n",dev->number,error);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
eprom_get_esi(dev);
|
|
|
|
printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",
|
2007-06-08 22:46:36 +00:00
|
|
|
dev->number,pci_dev->revision,zatm_dev->base,zatm_dev->irq);
|
2005-04-16 22:20:36 +00:00
|
|
|
/* reset uPD98401 */
|
|
|
|
zout(0,SWR);
|
|
|
|
while (!(zin(GSR) & uPD98401_INT_IND));
|
|
|
|
zout(uPD98401_GMR_ONE /*uPD98401_BURST4*/,GMR);
|
|
|
|
last = MAX_CRAM_SIZE;
|
|
|
|
for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
|
|
|
|
zpokel(zatm_dev,0x55555555,i);
|
|
|
|
if (zpeekl(zatm_dev,i) != 0x55555555) last = i;
|
|
|
|
else {
|
|
|
|
zpokel(zatm_dev,0xAAAAAAAA,i);
|
|
|
|
if (zpeekl(zatm_dev,i) != 0xAAAAAAAA) last = i;
|
|
|
|
else zpokel(zatm_dev,i,i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < last; i += RAM_INCREMENT)
|
|
|
|
if (zpeekl(zatm_dev,i) != i) break;
|
|
|
|
zatm_dev->mem = i << 2;
|
|
|
|
while (i) zpokel(zatm_dev,0,--i);
|
|
|
|
/* reset again to rebuild memory pointers */
|
|
|
|
zout(0,SWR);
|
|
|
|
while (!(zin(GSR) & uPD98401_INT_IND));
|
|
|
|
zout(uPD98401_GMR_ONE | uPD98401_BURST8 | uPD98401_BURST4 |
|
|
|
|
uPD98401_BURST2 | uPD98401_GMR_PM | uPD98401_GMR_DR,GMR);
|
|
|
|
/* TODO: should shrink allocation now */
|
|
|
|
printk("mem=%dkB,%s (",zatm_dev->mem >> 10,zatm_dev->copper ? "UTP" :
|
|
|
|
"MMF");
|
|
|
|
for (i = 0; i < ESI_LEN; i++)
|
|
|
|
printk("%02X%s",dev->esi[i],i == ESI_LEN-1 ? ")\n" : "-");
|
|
|
|
do {
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
t0 = zpeekl(zatm_dev,uPD98401_TSR);
|
|
|
|
udelay(10);
|
|
|
|
t1 = zpeekl(zatm_dev,uPD98401_TSR);
|
|
|
|
udelay(1010);
|
|
|
|
t2 = zpeekl(zatm_dev,uPD98401_TSR);
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
}
|
|
|
|
while (t0 > t1 || t1 > t2); /* loop if wrapping ... */
|
|
|
|
zatm_dev->khz = t2-2*t1+t0;
|
|
|
|
printk(KERN_NOTICE DEV_LABEL "(itf %d): uPD98401 %d.%d at %d.%03d "
|
|
|
|
"MHz\n",dev->number,
|
|
|
|
(zin(VER) & uPD98401_MAJOR) >> uPD98401_MAJOR_SHIFT,
|
|
|
|
zin(VER) & uPD98401_MINOR,zatm_dev->khz/1000,zatm_dev->khz % 1000);
|
|
|
|
return uPD98402_init(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-14 09:05:01 +00:00
|
|
|
static int __devinit zatm_start(struct atm_dev *dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-07-20 19:01:46 +00:00
|
|
|
struct zatm_dev *zatm_dev = ZATM_DEV(dev);
|
|
|
|
struct pci_dev *pdev = zatm_dev->pci_dev;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned long curr;
|
|
|
|
int pools,vccs,rx;
|
2005-07-20 19:01:46 +00:00
|
|
|
int error, i, ld;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
DPRINTK("zatm_start\n");
|
|
|
|
zatm_dev->rx_map = zatm_dev->tx_map = NULL;
|
2005-07-20 19:01:46 +00:00
|
|
|
for (i = 0; i < NR_MBX; i++)
|
|
|
|
zatm_dev->mbx_start[i] = 0;
|
2006-07-02 02:29:38 +00:00
|
|
|
error = request_irq(zatm_dev->irq, zatm_int, IRQF_SHARED, DEV_LABEL, dev);
|
2005-07-20 19:01:46 +00:00
|
|
|
if (error < 0) {
|
|
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
|
|
|
|
dev->number,zatm_dev->irq);
|
|
|
|
goto done;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
/* define memory regions */
|
|
|
|
pools = NR_POOLS;
|
|
|
|
if (NR_SHAPERS*SHAPER_SIZE > pools*POOL_SIZE)
|
|
|
|
pools = NR_SHAPERS*SHAPER_SIZE/POOL_SIZE;
|
|
|
|
vccs = (zatm_dev->mem-NR_SHAPERS*SHAPER_SIZE-pools*POOL_SIZE)/
|
|
|
|
(2*VC_SIZE+RX_SIZE);
|
|
|
|
ld = -1;
|
|
|
|
for (rx = 1; rx < vccs; rx <<= 1) ld++;
|
|
|
|
dev->ci_range.vpi_bits = 0; /* @@@ no VPI for now */
|
|
|
|
dev->ci_range.vci_bits = ld;
|
|
|
|
dev->link_rate = ATM_OC3_PCR;
|
|
|
|
zatm_dev->chans = vccs; /* ??? */
|
|
|
|
curr = rx*RX_SIZE/4;
|
|
|
|
DPRINTK("RX pool 0x%08lx\n",curr);
|
|
|
|
zpokel(zatm_dev,curr,uPD98401_PMA); /* receive pool */
|
|
|
|
zatm_dev->pool_base = curr;
|
|
|
|
curr += pools*POOL_SIZE/4;
|
|
|
|
DPRINTK("Shapers 0x%08lx\n",curr);
|
|
|
|
zpokel(zatm_dev,curr,uPD98401_SMA); /* shapers */
|
|
|
|
curr += NR_SHAPERS*SHAPER_SIZE/4;
|
|
|
|
DPRINTK("Free 0x%08lx\n",curr);
|
|
|
|
zpokel(zatm_dev,curr,uPD98401_TOS); /* free pool */
|
|
|
|
printk(KERN_INFO DEV_LABEL "(itf %d): %d shapers, %d pools, %d RX, "
|
|
|
|
"%ld VCs\n",dev->number,NR_SHAPERS,pools,rx,
|
|
|
|
(zatm_dev->mem-curr*4)/VC_SIZE);
|
|
|
|
/* create mailboxes */
|
2005-07-20 19:01:46 +00:00
|
|
|
for (i = 0; i < NR_MBX; i++) {
|
|
|
|
void *mbx;
|
|
|
|
dma_addr_t mbx_dma;
|
|
|
|
|
|
|
|
if (!mbx_entries[i])
|
|
|
|
continue;
|
|
|
|
mbx = pci_alloc_consistent(pdev, 2*MBX_SIZE(i), &mbx_dma);
|
|
|
|
if (!mbx) {
|
|
|
|
error = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Alignment provided by pci_alloc_consistent() isn't enough
|
|
|
|
* for this device.
|
|
|
|
*/
|
|
|
|
if (((unsigned long)mbx ^ mbx_dma) & 0xffff) {
|
|
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): system "
|
|
|
|
"bus incompatible with driver\n", dev->number);
|
|
|
|
pci_free_consistent(pdev, 2*MBX_SIZE(i), mbx, mbx_dma);
|
|
|
|
error = -ENODEV;
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2005-07-20 19:01:46 +00:00
|
|
|
DPRINTK("mbx@0x%08lx-0x%08lx\n", mbx, mbx + MBX_SIZE(i));
|
|
|
|
zatm_dev->mbx_start[i] = (unsigned long)mbx;
|
|
|
|
zatm_dev->mbx_dma[i] = mbx_dma;
|
|
|
|
zatm_dev->mbx_end[i] = (zatm_dev->mbx_start[i] + MBX_SIZE(i)) &
|
|
|
|
0xffff;
|
|
|
|
zout(mbx_dma >> 16, MSH(i));
|
|
|
|
zout(mbx_dma, MSL(i));
|
|
|
|
zout(zatm_dev->mbx_end[i], MBA(i));
|
|
|
|
zout((unsigned long)mbx & 0xffff, MTA(i));
|
|
|
|
zout((unsigned long)mbx & 0xffff, MWA(i));
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
error = start_tx(dev);
|
2005-07-20 19:01:46 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
error = start_rx(dev);
|
2005-07-20 19:01:46 +00:00
|
|
|
if (error)
|
|
|
|
goto out_tx;
|
2005-04-16 22:20:36 +00:00
|
|
|
error = dev->phy->start(dev);
|
2005-07-20 19:01:46 +00:00
|
|
|
if (error)
|
|
|
|
goto out_rx;
|
2005-04-16 22:20:36 +00:00
|
|
|
zout(0xffffffff,IMR); /* enable interrupts */
|
|
|
|
/* enable TX & RX */
|
|
|
|
zout(zin(GMR) | uPD98401_GMR_SE | uPD98401_GMR_RE,GMR);
|
2005-07-20 19:01:46 +00:00
|
|
|
done:
|
|
|
|
return error;
|
|
|
|
|
|
|
|
out_rx:
|
2005-06-02 20:04:07 +00:00
|
|
|
kfree(zatm_dev->rx_map);
|
2005-07-20 19:01:46 +00:00
|
|
|
out_tx:
|
2005-06-02 20:04:07 +00:00
|
|
|
kfree(zatm_dev->tx_map);
|
2005-07-20 19:01:46 +00:00
|
|
|
out:
|
|
|
|
while (i-- > 0) {
|
|
|
|
pci_free_consistent(pdev, 2*MBX_SIZE(i),
|
|
|
|
(void *)zatm_dev->mbx_start[i],
|
|
|
|
zatm_dev->mbx_dma[i]);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
free_irq(zatm_dev->irq, dev);
|
2005-07-20 19:01:46 +00:00
|
|
|
goto done;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void zatm_close(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
DPRINTK(">zatm_close\n");
|
|
|
|
if (!ZATM_VCC(vcc)) return;
|
|
|
|
clear_bit(ATM_VF_READY,&vcc->flags);
|
|
|
|
close_rx(vcc);
|
|
|
|
EVENT("close_tx\n",0,0);
|
|
|
|
close_tx(vcc);
|
|
|
|
DPRINTK("zatm_close: done waiting\n");
|
|
|
|
/* deallocate memory */
|
|
|
|
kfree(ZATM_VCC(vcc));
|
|
|
|
vcc->dev_data = NULL;
|
|
|
|
clear_bit(ATM_VF_ADDR,&vcc->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int zatm_open(struct atm_vcc *vcc)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
struct zatm_vcc *zatm_vcc;
|
|
|
|
short vpi = vcc->vpi;
|
|
|
|
int vci = vcc->vci;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
DPRINTK(">zatm_open\n");
|
|
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
|
|
if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))
|
|
|
|
vcc->dev_data = NULL;
|
|
|
|
if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
|
|
|
|
set_bit(ATM_VF_ADDR,&vcc->flags);
|
|
|
|
if (vcc->qos.aal != ATM_AAL5) return -EINVAL; /* @@@ AAL0 */
|
|
|
|
DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
|
|
|
|
vcc->vci);
|
|
|
|
if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
|
|
|
|
zatm_vcc = kmalloc(sizeof(struct zatm_vcc),GFP_KERNEL);
|
|
|
|
if (!zatm_vcc) {
|
|
|
|
clear_bit(ATM_VF_ADDR,&vcc->flags);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
vcc->dev_data = zatm_vcc;
|
|
|
|
ZATM_VCC(vcc)->tx_chan = 0; /* for zatm_close after open_rx */
|
|
|
|
if ((error = open_rx_first(vcc))) {
|
|
|
|
zatm_close(vcc);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if ((error = open_tx_first(vcc))) {
|
|
|
|
zatm_close(vcc);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
|
|
|
|
if ((error = open_rx_second(vcc))) {
|
|
|
|
zatm_close(vcc);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if ((error = open_tx_second(vcc))) {
|
|
|
|
zatm_close(vcc);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
set_bit(ATM_VF_READY,&vcc->flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int zatm_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flags)
|
|
|
|
{
|
|
|
|
printk("Not yet implemented\n");
|
|
|
|
return -ENOSYS;
|
|
|
|
/* @@@ */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int zatm_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
switch (cmd) {
|
|
|
|
case ZATM_GETPOOLZ:
|
|
|
|
if (!capable(CAP_NET_ADMIN)) return -EPERM;
|
|
|
|
/* fall through */
|
|
|
|
case ZATM_GETPOOL:
|
|
|
|
{
|
|
|
|
struct zatm_pool_info info;
|
|
|
|
int pool;
|
|
|
|
|
|
|
|
if (get_user(pool,
|
|
|
|
&((struct zatm_pool_req __user *) arg)->pool_num))
|
|
|
|
return -EFAULT;
|
|
|
|
if (pool < 0 || pool > ZATM_LAST_POOL)
|
|
|
|
return -EINVAL;
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
info = zatm_dev->pool_info[pool];
|
|
|
|
if (cmd == ZATM_GETPOOLZ) {
|
|
|
|
zatm_dev->pool_info[pool].rqa_count = 0;
|
|
|
|
zatm_dev->pool_info[pool].rqu_count = 0;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
return copy_to_user(
|
|
|
|
&((struct zatm_pool_req __user *) arg)->info,
|
|
|
|
&info,sizeof(info)) ? -EFAULT : 0;
|
|
|
|
}
|
|
|
|
case ZATM_SETPOOL:
|
|
|
|
{
|
|
|
|
struct zatm_pool_info info;
|
|
|
|
int pool;
|
|
|
|
|
|
|
|
if (!capable(CAP_NET_ADMIN)) return -EPERM;
|
|
|
|
if (get_user(pool,
|
|
|
|
&((struct zatm_pool_req __user *) arg)->pool_num))
|
|
|
|
return -EFAULT;
|
|
|
|
if (pool < 0 || pool > ZATM_LAST_POOL)
|
|
|
|
return -EINVAL;
|
|
|
|
if (copy_from_user(&info,
|
|
|
|
&((struct zatm_pool_req __user *) arg)->info,
|
|
|
|
sizeof(info))) return -EFAULT;
|
|
|
|
if (!info.low_water)
|
|
|
|
info.low_water = zatm_dev->
|
|
|
|
pool_info[pool].low_water;
|
|
|
|
if (!info.high_water)
|
|
|
|
info.high_water = zatm_dev->
|
|
|
|
pool_info[pool].high_water;
|
|
|
|
if (!info.next_thres)
|
|
|
|
info.next_thres = zatm_dev->
|
|
|
|
pool_info[pool].next_thres;
|
|
|
|
if (info.low_water >= info.high_water ||
|
|
|
|
info.low_water < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
|
|
zatm_dev->pool_info[pool].low_water =
|
|
|
|
info.low_water;
|
|
|
|
zatm_dev->pool_info[pool].high_water =
|
|
|
|
info.high_water;
|
|
|
|
zatm_dev->pool_info[pool].next_thres =
|
|
|
|
info.next_thres;
|
|
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if (!dev->phy->ioctl) return -ENOIOCTLCMD;
|
|
|
|
return dev->phy->ioctl(dev,cmd,arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int zatm_getsockopt(struct atm_vcc *vcc,int level,int optname,
|
|
|
|
void __user *optval,int optlen)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int zatm_setsockopt(struct atm_vcc *vcc,int level,int optname,
|
2009-09-30 23:12:20 +00:00
|
|
|
void __user *optval,unsigned int optlen)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int zatm_send(struct atm_vcc *vcc,struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
EVENT(">zatm_send 0x%lx\n",(unsigned long) skb,0);
|
|
|
|
if (!ZATM_VCC(vcc)->tx_chan || !test_bit(ATM_VF_READY,&vcc->flags)) {
|
|
|
|
if (vcc->pop) vcc->pop(vcc,skb);
|
|
|
|
else dev_kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (!skb) {
|
|
|
|
printk(KERN_CRIT "!skb in zatm_send ?\n");
|
|
|
|
if (vcc->pop) vcc->pop(vcc,skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
ATM_SKB(skb)->vcc = vcc;
|
|
|
|
error = do_tx(skb);
|
|
|
|
if (error != RING_BUSY) return error;
|
|
|
|
skb_queue_tail(&ZATM_VCC(vcc)->backlog,skb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void zatm_phy_put(struct atm_dev *dev,unsigned char value,
|
|
|
|
unsigned long addr)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
zwait;
|
|
|
|
zout(value,CER);
|
|
|
|
zout(uPD98401_IND_ACC | uPD98401_IA_B0 |
|
|
|
|
(uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned char zatm_phy_get(struct atm_dev *dev,unsigned long addr)
|
|
|
|
{
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
|
|
zwait;
|
|
|
|
zout(uPD98401_IND_ACC | uPD98401_IA_B0 | uPD98401_IA_RW |
|
|
|
|
(uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
|
|
|
|
zwait;
|
|
|
|
return zin(CER) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct atmdev_ops ops = {
|
|
|
|
.open = zatm_open,
|
|
|
|
.close = zatm_close,
|
|
|
|
.ioctl = zatm_ioctl,
|
|
|
|
.getsockopt = zatm_getsockopt,
|
|
|
|
.setsockopt = zatm_setsockopt,
|
|
|
|
.send = zatm_send,
|
|
|
|
.phy_put = zatm_phy_put,
|
|
|
|
.phy_get = zatm_phy_get,
|
|
|
|
.change_qos = zatm_change_qos,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __devinit zatm_init_one(struct pci_dev *pci_dev,
|
|
|
|
const struct pci_device_id *ent)
|
|
|
|
{
|
|
|
|
struct atm_dev *dev;
|
|
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
int ret = -ENOMEM;
|
|
|
|
|
2006-12-13 08:35:56 +00:00
|
|
|
zatm_dev = kmalloc(sizeof(*zatm_dev), GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!zatm_dev) {
|
|
|
|
printk(KERN_EMERG "%s: memory shortage\n", DEV_LABEL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-12-08 19:40:47 +00:00
|
|
|
dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &ops, -1, NULL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!dev)
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
ret = pci_enable_device(pci_dev);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_deregister;
|
|
|
|
|
|
|
|
ret = pci_request_regions(pci_dev, DEV_LABEL);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_disable;
|
|
|
|
|
|
|
|
zatm_dev->pci_dev = pci_dev;
|
|
|
|
dev->dev_data = zatm_dev;
|
|
|
|
zatm_dev->copper = (int)ent->driver_data;
|
|
|
|
if ((ret = zatm_init(dev)) || (ret = zatm_start(dev)))
|
|
|
|
goto out_release;
|
|
|
|
|
|
|
|
pci_set_drvdata(pci_dev, dev);
|
|
|
|
zatm_dev->more = zatm_boards;
|
|
|
|
zatm_boards = dev;
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
pci_release_regions(pci_dev);
|
|
|
|
out_disable:
|
|
|
|
pci_disable_device(pci_dev);
|
|
|
|
out_deregister:
|
|
|
|
atm_dev_deregister(dev);
|
|
|
|
out_free:
|
|
|
|
kfree(zatm_dev);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
static struct pci_device_id zatm_pci_tbl[] __devinitdata = {
|
2010-07-15 08:49:32 +00:00
|
|
|
{ PCI_VDEVICE(ZEITNET, PCI_DEVICE_ID_ZEITNET_1221), ZATM_COPPER },
|
|
|
|
{ PCI_VDEVICE(ZEITNET, PCI_DEVICE_ID_ZEITNET_1225), 0 },
|
2005-04-16 22:20:36 +00:00
|
|
|
{ 0, }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, zatm_pci_tbl);
|
|
|
|
|
|
|
|
static struct pci_driver zatm_driver = {
|
|
|
|
.name = DEV_LABEL,
|
|
|
|
.id_table = zatm_pci_tbl,
|
|
|
|
.probe = zatm_init_one,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init zatm_init_module(void)
|
|
|
|
{
|
|
|
|
return pci_register_driver(&zatm_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(zatm_init_module);
|
|
|
|
/* module_exit not defined so not unloadable */
|