/*
* avm_fritz.c low level stuff for AVM FRITZ!CARD PCI ISDN cards
* Thanks to AVM, Berlin for informations
*
* 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 <asm/unaligned.h>
#include "ipac.h"
#define AVMFRITZ_REV "2.3"
static int AVM_cnt;
static int debug;
enum {
AVM_FRITZ_PCI,
AVM_FRITZ_PCIV2,
};
#define HDLC_FIFO 0x0
#define HDLC_STATUS 0x4
#define CHIP_WINDOW 0x10
#define CHIP_INDEX 0x4
#define AVM_HDLC_1 0x00
#define AVM_HDLC_2 0x01
#define AVM_ISAC_FIFO 0x02
#define AVM_ISAC_REG_LOW 0x04
#define AVM_ISAC_REG_HIGH 0x06
#define AVM_STATUS0_IRQ_ISAC 0x01
#define AVM_STATUS0_IRQ_HDLC 0x02
#define AVM_STATUS0_IRQ_TIMER 0x04
#define AVM_STATUS0_IRQ_MASK 0x07
#define AVM_STATUS0_RESET 0x01
#define AVM_STATUS0_DIS_TIMER 0x02
#define AVM_STATUS0_RES_TIMER 0x04
#define AVM_STATUS0_ENA_IRQ 0x08
#define AVM_STATUS0_TESTBIT 0x10
#define AVM_STATUS1_INT_SEL 0x0f
#define AVM_STATUS1_ENA_IOM 0x80
#define HDLC_MODE_ITF_FLG 0x01
#define HDLC_MODE_TRANS 0x02
#define HDLC_MODE_CCR_7 0x04
#define HDLC_MODE_CCR_16 0x08
#define HDLC_FIFO_SIZE_128 0x20
#define HDLC_MODE_TESTLOOP 0x80
#define HDLC_INT_XPR 0x80
#define HDLC_INT_XDU 0x40
#define HDLC_INT_RPR 0x20
#define HDLC_INT_MASK 0xE0
#define HDLC_STAT_RME 0x01
#define HDLC_STAT_RDO 0x10
#define HDLC_STAT_CRCVFRRAB 0x0E
#define HDLC_STAT_CRCVFR 0x06
#define HDLC_STAT_RML_MASK_V1 0x3f00
#define HDLC_STAT_RML_MASK_V2 0x7f00
#define HDLC_CMD_XRS 0x80
#define HDLC_CMD_XME 0x01
#define HDLC_CMD_RRS 0x20
#define HDLC_CMD_XML_MASK 0x3f00
#define HDLC_FIFO_SIZE_V1 32
#define HDLC_FIFO_SIZE_V2 128
/* Fritz PCI v2.0 */
#define AVM_HDLC_FIFO_1 0x10
#define AVM_HDLC_FIFO_2 0x18
#define AVM_HDLC_STATUS_1 0x14
#define AVM_HDLC_STATUS_2 0x1c
#define AVM_ISACX_INDEX 0x04
#define AVM_ISACX_DATA 0x08
/* data struct */
#define LOG_SIZE 63
struct hdlc_stat_reg {
#ifdef __BIG_ENDIAN
u8 fill;
u8 mode;
u8 xml;
u8 cmd;
#else
u8 cmd;
u8 xml;
u8 mode;
u8 fill;
#endif
} __attribute__((packed));
struct hdlc_hw {
union {
u32 ctrl;
struct hdlc_stat_reg sr;
} ctrl;
u32 stat;
};
struct fritzcard {
struct list_head list;
struct pci_dev *pdev;
char name[MISDN_MAX_IDLEN];
u8 type;
u8 ctrlreg;
u16 irq;
u32 irqcnt;
u32 addr;
spinlock_t lock; /* hw lock */
struct isac_hw isac;
struct hdlc_hw hdlc[2];
struct bchannel bch[2];
char log[LOG_SIZE + 1];
};
static LIST_HEAD(Cards);
static DEFINE_RWLOCK(card_lock); /* protect Cards */
static void
_set_debug(struct fritzcard *card)
{
card->isac.dch.debug = debug;
card->bch[0].debug = debug;
card->bch[1].debug = debug;
}
static int
set_debug(const char *val, struct kernel_param *kp)
{
int ret;
struct fritzcard *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(AVMFRITZ_REV);
module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "avmfritz debug mask");
/* Interface functions */
static u8
ReadISAC_V1(void *p, u8 offset)
{
struct fritzcard<