linux/drivers/isdn/hisax/avm_a1p.c
<<
>>
Prefs
   1/* $Id: avm_a1p.c,v 2.9.2.5 2004/01/24 20:47:19 keil Exp $
   2 *
   3 * low level stuff for the following AVM cards:
   4 * A1 PCMCIA
   5 * FRITZ!Card PCMCIA
   6 * FRITZ!Card PCMCIA 2.0
   7 *
   8 * Author       Carsten Paeth
   9 * Copyright    by Carsten Paeth     <calle@calle.de>
  10 *
  11 * This software may be used and distributed according to the terms
  12 * of the GNU General Public License, incorporated herein by reference.
  13 *
  14 */
  15
  16#include <linux/init.h>
  17#include "hisax.h"
  18#include "isac.h"
  19#include "hscx.h"
  20#include "isdnl1.h"
  21
  22/* register offsets */
  23#define ADDRREG_OFFSET          0x02
  24#define DATAREG_OFFSET          0x03
  25#define ASL0_OFFSET             0x04
  26#define ASL1_OFFSET             0x05
  27#define MODREG_OFFSET           0x06
  28#define VERREG_OFFSET           0x07
  29
  30/* address offsets */
  31#define ISAC_FIFO_OFFSET        0x00
  32#define ISAC_REG_OFFSET         0x20
  33#define HSCX_CH_DIFF            0x40
  34#define HSCX_FIFO_OFFSET        0x80
  35#define HSCX_REG_OFFSET         0xa0
  36
  37/* read bits ASL0 */
  38#define  ASL0_R_TIMER           0x10 /* active low */
  39#define  ASL0_R_ISAC            0x20 /* active low */
  40#define  ASL0_R_HSCX            0x40 /* active low */
  41#define  ASL0_R_TESTBIT         0x80
  42#define  ASL0_R_IRQPENDING      (ASL0_R_ISAC | ASL0_R_HSCX | ASL0_R_TIMER)
  43
  44/* write bits ASL0 */
  45#define  ASL0_W_RESET           0x01
  46#define  ASL0_W_TDISABLE        0x02
  47#define  ASL0_W_TRESET          0x04
  48#define  ASL0_W_IRQENABLE       0x08
  49#define  ASL0_W_TESTBIT         0x80
  50
  51/* write bits ASL1 */
  52#define  ASL1_W_LED0            0x10
  53#define  ASL1_W_LED1            0x20
  54#define  ASL1_W_ENABLE_S0       0xC0
  55
  56#define byteout(addr, val) outb(val, addr)
  57#define bytein(addr) inb(addr)
  58
  59static const char *avm_revision = "$Revision: 2.9.2.5 $";
  60
  61static inline u_char
  62ReadISAC(struct IsdnCardState *cs, u_char offset)
  63{
  64        u_char ret;
  65
  66        offset -= 0x20;
  67        byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET, ISAC_REG_OFFSET + offset);
  68        ret = bytein(cs->hw.avm.cfg_reg + DATAREG_OFFSET);
  69        return ret;
  70}
  71
  72static inline void
  73WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
  74{
  75        offset -= 0x20;
  76        byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET, ISAC_REG_OFFSET + offset);
  77        byteout(cs->hw.avm.cfg_reg + DATAREG_OFFSET, value);
  78}
  79
  80static inline void
  81ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
  82{
  83        byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET, ISAC_FIFO_OFFSET);
  84        insb(cs->hw.avm.cfg_reg + DATAREG_OFFSET, data, size);
  85}
  86
  87static inline void
  88WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
  89{
  90        byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET, ISAC_FIFO_OFFSET);
  91        outsb(cs->hw.avm.cfg_reg + DATAREG_OFFSET, data, size);
  92}
  93
  94static inline u_char
  95ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
  96{
  97        u_char ret;
  98
  99        offset -= 0x20;
 100        byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET,
 101                HSCX_REG_OFFSET + hscx * HSCX_CH_DIFF + offset);
 102        ret = bytein(cs->hw.avm.cfg_reg + DATAREG_OFFSET);
 103        return ret;
 104}
 105
 106static inline void
 107WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 108{
 109        offset -= 0x20;
 110        byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET,
 111                HSCX_REG_OFFSET + hscx * HSCX_CH_DIFF + offset);
 112        byteout(cs->hw.avm.cfg_reg + DATAREG_OFFSET, value);
 113}
 114
 115static inline void
 116ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char *data, int size)
 117{
 118        byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET,
 119                HSCX_FIFO_OFFSET + hscx * HSCX_CH_DIFF);
 120        insb(cs->hw.avm.cfg_reg + DATAREG_OFFSET, data, size);
 121}
 122
 123static inline void
 124WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char *data, int size)
 125{
 126        byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET,
 127                HSCX_FIFO_OFFSET + hscx * HSCX_CH_DIFF);
 128        outsb(cs->hw.avm.cfg_reg + DATAREG_OFFSET, data, size);
 129}
 130
 131/*
 132 * fast interrupt HSCX stuff goes here
 133 */
 134
 135#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
 136#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
 137#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt)
 138#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
 139
 140#include "hscx_irq.c"
 141
 142static irqreturn_t
 143avm_a1p_interrupt(int intno, void *dev_id)
 144{
 145        struct IsdnCardState *cs = dev_id;
 146        u_char val, sval;
 147        u_long flags;
 148
 149        spin_lock_irqsave(&cs->lock, flags);
 150        while ((sval = (~bytein(cs->hw.avm.cfg_reg + ASL0_OFFSET) & ASL0_R_IRQPENDING))) {
 151                if (cs->debug & L1_DEB_INTSTAT)
 152                        debugl1(cs, "avm IntStatus %x", sval);
 153                if (sval & ASL0_R_HSCX) {
 154                        val = ReadHSCX(cs, 1, HSCX_ISTA);
 155                        if (val)
 156                                hscx_int_main(cs, val);
 157                }
 158                if (sval & ASL0_R_ISAC) {
 159                        val = ReadISAC(cs, ISAC_ISTA);
 160                        if (val)
 161                                isac_interrupt(cs, val);
 162                }
 163        }
 164        WriteHSCX(cs, 0, HSCX_MASK, 0xff);
 165        WriteHSCX(cs, 1, HSCX_MASK, 0xff);
 166        WriteISAC(cs, ISAC_MASK, 0xff);
 167        WriteISAC(cs, ISAC_MASK, 0x00);
 168        WriteHSCX(cs, 0, HSCX_MASK, 0x00);
 169        WriteHSCX(cs, 1, HSCX_MASK, 0x00);
 170        spin_unlock_irqrestore(&cs->lock, flags);
 171        return IRQ_HANDLED;
 172}
 173
 174static int
 175AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 176{
 177        u_long flags;
 178
 179        switch (mt) {
 180        case CARD_RESET:
 181                spin_lock_irqsave(&cs->lock, flags);
 182                byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, 0x00);
 183                HZDELAY(HZ / 5 + 1);
 184                byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, ASL0_W_RESET);
 185                HZDELAY(HZ / 5 + 1);
 186                byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, 0x00);
 187                spin_unlock_irqrestore(&cs->lock, flags);
 188                return 0;
 189
 190        case CARD_RELEASE:
 191                /* free_irq is done in HiSax_closecard(). */
 192                /* free_irq(cs->irq, cs); */
 193                return 0;
 194
 195        case CARD_INIT:
 196                spin_lock_irqsave(&cs->lock, flags);
 197                byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, ASL0_W_TDISABLE | ASL0_W_TRESET | ASL0_W_IRQENABLE);
 198                clear_pending_isac_ints(cs);
 199                clear_pending_hscx_ints(cs);
 200                inithscxisac(cs, 1);
 201                inithscxisac(cs, 2);
 202                spin_unlock_irqrestore(&cs->lock, flags);
 203                return 0;
 204
 205        case CARD_TEST:
 206                /* we really don't need it for the PCMCIA Version */
 207                return 0;
 208
 209        default:
 210                /* all card drivers ignore others, so we do the same */
 211                return 0;
 212        }
 213        return 0;
 214}
 215
 216int setup_avm_a1_pcmcia(struct IsdnCard *card)
 217{
 218        u_char model, vers;
 219        struct IsdnCardState *cs = card->cs;
 220        char tmp[64];
 221
 222
 223        strcpy(tmp, avm_revision);
 224        printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n",
 225               HiSax_getrev(tmp));
 226        if (cs->typ != ISDN_CTYPE_A1_PCMCIA)
 227                return (0);
 228
 229        cs->hw.avm.cfg_reg = card->para[1];
 230        cs->irq = card->para[0];
 231
 232
 233        byteout(cs->hw.avm.cfg_reg + ASL1_OFFSET, ASL1_W_ENABLE_S0);
 234        byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, 0x00);
 235        HZDELAY(HZ / 5 + 1);
 236        byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, ASL0_W_RESET);
 237        HZDELAY(HZ / 5 + 1);
 238        byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, 0x00);
 239
 240        byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, ASL0_W_TDISABLE | ASL0_W_TRESET);
 241
 242        model = bytein(cs->hw.avm.cfg_reg + MODREG_OFFSET);
 243        vers = bytein(cs->hw.avm.cfg_reg + VERREG_OFFSET);
 244
 245        printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n",
 246               cs->hw.avm.cfg_reg, cs->irq, model, vers);
 247
 248        setup_isac(cs);
 249        cs->readisac = &ReadISAC;
 250        cs->writeisac = &WriteISAC;
 251        cs->readisacfifo = &ReadISACfifo;
 252        cs->writeisacfifo = &WriteISACfifo;
 253        cs->BC_Read_Reg = &ReadHSCX;
 254        cs->BC_Write_Reg = &WriteHSCX;
 255        cs->BC_Send_Data = &hscx_fill_fifo;
 256        cs->cardmsg = &AVM_card_msg;
 257        cs->irq_flags = IRQF_SHARED;
 258        cs->irq_func = &avm_a1p_interrupt;
 259
 260        ISACVersion(cs, "AVM A1 PCMCIA:");
 261        if (HscxVersion(cs, "AVM A1 PCMCIA:")) {
 262                printk(KERN_WARNING
 263                       "AVM A1 PCMCIA: wrong HSCX versions check IO address\n");
 264                return (0);
 265        }
 266        return (1);
 267}
 268