/*
* NETJet mISDN driver
*
* Author Karsten Keil <keil@isdn4linux.de>
*
* Copyright 2009 by Karsten Keil <keil@isdn4linux.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/mISDNhw.h>
#include <linux/slab.h>
#include "ipac.h"
#include "iohelper.h"
#include "netjet.h"
#include <linux/isdn/hdlc.h>
#define NETJET_REV "2.0"
enum nj_types {
NETJET_S_TJ300,
NETJET_S_TJ320,
ENTERNOW__TJ320,
};
struct tiger_dma {
size_t size;
u32 *start;
int idx;
u32 dmastart;
u32 dmairq;
u32 dmaend;
u32 dmacur;
};
struct tiger_hw;
struct tiger_ch {
struct bchannel bch;
struct tiger_hw *nj;
int idx;
int free;
int lastrx;
u16 rxstate;
u16 txstate;
struct isdnhdlc_vars hsend;
struct isdnhdlc_vars hrecv;
u8 *hsbuf;
u8 *hrbuf;
};
#define TX_INIT 0x0001
#define TX_IDLE 0x0002
#define TX_RUN 0x0004
#define TX_UNDERRUN 0x0100
#define RX_OVERRUN 0x0100
#define LOG_SIZE 64
struct tiger_hw {
struct list_head list;
struct pci_dev *pdev;
char name[MISDN_MAX_IDLEN];
enum nj_types typ;
int irq;
u32 irqcnt;
u32 base;
size_t base_s;
dma_addr_t dma;
void *dma_p;
spinlock_t lock; /* lock HW */
struct isac_hw isac;
struct tiger_dma send;
struct tiger_dma recv;
struct tiger_ch bc[2];
u8 ctrlreg;
u8 dmactrl;
u8 auxd;
u8 last_is0;
u8 irqmask0;
char log[LOG_SIZE];
};
static LIST_HEAD(Cards);
static DEFINE_RWLOCK(card_lock); /* protect Cards */
static u32 debug;
static int nj_cnt;
static void
_set_debug(struct tiger_hw *card)
{
card->isac.dch.debug = debug;
card->bc[0].bch.debug = debug;
card->bc[1].bch.debug = debug;
}
static int
set_debug(const char *val, struct kernel_param *kp)
{
int ret;
struct tiger_hw *card;
ret = param_set_uint(val, kp);
if (!ret) {
read_lock(&card_lock);
list_for_each_entry(card, &Cards, list)
_set_debug(card);
read_unlock(&card_lock);
}
return ret;
}
MODULE_AUTHOR("Karsten Keil");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(NETJET_REV);
module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Netjet debug mask");
static void
nj_disable_hwirq(struct tiger_hw *card)
{
outb(0, card->base + NJ_IRQMASK0);
outb(0, card->base + NJ_IRQMASK1);
}
static u8
ReadISAC_nj(void *p, u8 offset)
{
struct tiger_hw *card = p;
u8 ret;
card->auxd &= 0xfc;
card->auxd |= (offset >> 4) & 3;
outb(card->auxd, card->base + NJ_AUXDATA);
ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
return ret;
}
static void
WriteISAC_nj(void *p, u8 offset, u8 value)
{
struct tiger_hw *card = p;
card->auxd &= 0xfc;
card->auxd |= (offset >> 4) & 3;
outb(card->auxd, card->base + NJ_AUXDATA);
outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
}
static void
ReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
{
struct tiger_hw *card = p;
card->auxd &= 0xfc;
outb(card->auxd, card->base + NJ_AUXDATA);
insb(card->base + NJ_ISAC_OFF, data, size);
}
static void
WriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
{
struct tiger_hw *card = p;
card->auxd &= 0xfc;
outb(card->auxd, card->base + NJ_AUXDATA);
outsb(card->base + NJ_ISAC_OFF, data, size);
}
static void
fill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill)
{
struct tiger_hw *card = bc->bch.hw;
u32 mask = 0xff, val;
pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name,
bc->bch.nr, fill, cnt, idx, card->send.idx);
if (bc->bch.nr & 2) {
fill <<= 8;
mask <<= 8;
}
mask ^= 0xffffffff;
while (cnt--) {
val = card->send.start[idx];
val &= mask;
val |= fill;
card->send.start[idx++] = val;
if (idx >= card->send.size)
idx = 0;
}
}
static int
mode_tiger(struct tiger_ch *bc, u32 protocol)
{
struct tiger_hw *card = bc->bch.hw;
pr_debug("%s: B%1d protocol %x-->%x\n", card->name,
bc->bch.nr, bc->bch.state, protocol);
switch (protocol) {
case ISDN_P_NONE:
if (bc->bch.state == ISDN_P_NONE)
bre
|