linux/drivers/isdn/hisax/isurf.c
<<
>>
Prefs
   1/* $Id: isurf.c,v 1.12.2.4 2004/01/13 21:46:03 keil Exp $
   2 *
   3 * low level stuff for Siemens I-Surf/I-Talk cards
   4 *
   5 * Author       Karsten Keil
   6 * Copyright    by Karsten Keil      <keil@isdn4linux.de>
   7 *
   8 * This software may be used and distributed according to the terms
   9 * of the GNU General Public License, incorporated herein by reference.
  10 *
  11 */
  12
  13#include <linux/init.h>
  14#include "hisax.h"
  15#include "isac.h"
  16#include "isar.h"
  17#include "isdnl1.h"
  18#include <linux/isapnp.h>
  19
  20static const char *ISurf_revision = "$Revision: 1.12.2.4 $";
  21
  22#define byteout(addr, val) outb(val, addr)
  23#define bytein(addr) inb(addr)
  24
  25#define ISURF_ISAR_RESET        1
  26#define ISURF_ISAC_RESET        2
  27#define ISURF_ISAR_EA           4
  28#define ISURF_ARCOFI_RESET      8
  29#define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET)
  30
  31#define ISURF_ISAR_OFFSET       0
  32#define ISURF_ISAC_OFFSET       0x100
  33#define ISURF_IOMEM_SIZE        0x400
  34/* Interface functions */
  35
  36static u_char
  37ReadISAC(struct IsdnCardState *cs, u_char offset)
  38{
  39        return (readb(cs->hw.isurf.isac + offset));
  40}
  41
  42static void
  43WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
  44{
  45        writeb(value, cs->hw.isurf.isac + offset); mb();
  46}
  47
  48static void
  49ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
  50{
  51        register int i;
  52        for (i = 0; i < size; i++)
  53                data[i] = readb(cs->hw.isurf.isac);
  54}
  55
  56static void
  57WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
  58{
  59        register int i;
  60        for (i = 0; i < size; i++) {
  61                writeb(data[i], cs->hw.isurf.isac); mb();
  62        }
  63}
  64
  65/* ISAR access routines
  66 * mode = 0 access with IRQ on
  67 * mode = 1 access with IRQ off
  68 * mode = 2 access with IRQ off and using last offset
  69 */
  70
  71static u_char
  72ReadISAR(struct IsdnCardState *cs, int mode, u_char offset)
  73{
  74        return (readb(cs->hw.isurf.isar + offset));
  75}
  76
  77static void
  78WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
  79{
  80        writeb(value, cs->hw.isurf.isar + offset); mb();
  81}
  82
  83static irqreturn_t
  84isurf_interrupt(int intno, void *dev_id)
  85{
  86        struct IsdnCardState *cs = dev_id;
  87        u_char val;
  88        int cnt = 5;
  89        u_long flags;
  90
  91        spin_lock_irqsave(&cs->lock, flags);
  92        val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
  93Start_ISAR:
  94        if (val & ISAR_IRQSTA)
  95                isar_int_main(cs);
  96        val = readb(cs->hw.isurf.isac + ISAC_ISTA);
  97Start_ISAC:
  98        if (val)
  99                isac_interrupt(cs, val);
 100        val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
 101        if ((val & ISAR_IRQSTA) && --cnt) {
 102                if (cs->debug & L1_DEB_HSCX)
 103                        debugl1(cs, "ISAR IntStat after IntRoutine");
 104                goto Start_ISAR;
 105        }
 106        val = readb(cs->hw.isurf.isac + ISAC_ISTA);
 107        if (val && --cnt) {
 108                if (cs->debug & L1_DEB_ISAC)
 109                        debugl1(cs, "ISAC IntStat after IntRoutine");
 110                goto Start_ISAC;
 111        }
 112        if (!cnt)
 113                printk(KERN_WARNING "ISurf IRQ LOOP\n");
 114
 115        writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
 116        writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK); mb();
 117        writeb(0, cs->hw.isurf.isac + ISAC_MASK); mb();
 118        writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
 119        spin_unlock_irqrestore(&cs->lock, flags);
 120        return IRQ_HANDLED;
 121}
 122
 123static void
 124release_io_isurf(struct IsdnCardState *cs)
 125{
 126        release_region(cs->hw.isurf.reset, 1);
 127        iounmap(cs->hw.isurf.isar);
 128        release_mem_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
 129}
 130
 131static void
 132reset_isurf(struct IsdnCardState *cs, u_char chips)
 133{
 134        printk(KERN_INFO "ISurf: resetting card\n");
 135
 136        byteout(cs->hw.isurf.reset, chips); /* Reset On */
 137        mdelay(10);
 138        byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */
 139        mdelay(10);
 140}
 141
 142static int
 143ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 144{
 145        u_long flags;
 146
 147        switch (mt) {
 148        case CARD_RESET:
 149                spin_lock_irqsave(&cs->lock, flags);
 150                reset_isurf(cs, ISURF_RESET);
 151                spin_unlock_irqrestore(&cs->lock, flags);
 152                return (0);
 153        case CARD_RELEASE:
 154                release_io_isurf(cs);
 155                return (0);
 156        case CARD_INIT:
 157                spin_lock_irqsave(&cs->lock, flags);
 158                reset_isurf(cs, ISURF_RESET);
 159                clear_pending_isac_ints(cs);
 160                writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
 161                initisac(cs);
 162                initisar(cs);
 163                /* Reenable ISAC IRQ */
 164                cs->writeisac(cs, ISAC_MASK, 0);
 165                /* RESET Receiver and Transmitter */
 166                cs->writeisac(cs, ISAC_CMDR, 0x41);
 167                spin_unlock_irqrestore(&cs->lock, flags);
 168                return (0);
 169        case CARD_TEST:
 170                return (0);
 171        }
 172        return (0);
 173}
 174
 175static int
 176isurf_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
 177        int ret;
 178        u_long flags;
 179
 180        if ((ic->command == ISDN_CMD_IOCTL) && (ic->arg == 9)) {
 181                ret = isar_auxcmd(cs, ic);
 182                spin_lock_irqsave(&cs->lock, flags);
 183                if (!ret) {
 184                        reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET |
 185                                    ISURF_ARCOFI_RESET);
 186                        initisac(cs);
 187                        cs->writeisac(cs, ISAC_MASK, 0);
 188                        cs->writeisac(cs, ISAC_CMDR, 0x41);
 189                }
 190                spin_unlock_irqrestore(&cs->lock, flags);
 191                return (ret);
 192        }
 193        return (isar_auxcmd(cs, ic));
 194}
 195
 196#ifdef __ISAPNP__
 197static struct pnp_card *pnp_c = NULL;
 198#endif
 199
 200int setup_isurf(struct IsdnCard *card)
 201{
 202        int ver;
 203        struct IsdnCardState *cs = card->cs;
 204        char tmp[64];
 205
 206        strcpy(tmp, ISurf_revision);
 207        printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp));
 208
 209        if (cs->typ != ISDN_CTYPE_ISURF)
 210                return (0);
 211        if (card->para[1] && card->para[2]) {
 212                cs->hw.isurf.reset = card->para[1];
 213                cs->hw.isurf.phymem = card->para[2];
 214                cs->irq = card->para[0];
 215        } else {
 216#ifdef __ISAPNP__
 217                if (isapnp_present()) {
 218                        struct pnp_dev *pnp_d = NULL;
 219                        int err;
 220
 221                        cs->subtyp = 0;
 222                        if ((pnp_c = pnp_find_card(
 223                                     ISAPNP_VENDOR('S', 'I', 'E'),
 224                                     ISAPNP_FUNCTION(0x0010), pnp_c))) {
 225                                if (!(pnp_d = pnp_find_dev(pnp_c,
 226                                                           ISAPNP_VENDOR('S', 'I', 'E'),
 227                                                           ISAPNP_FUNCTION(0x0010), pnp_d))) {
 228                                        printk(KERN_ERR "ISurfPnP: PnP error card found, no device\n");
 229                                        return (0);
 230                                }
 231                                pnp_disable_dev(pnp_d);
 232                                err = pnp_activate_dev(pnp_d);
 233                                if (err < 0) {
 234                                        pr_warn("%s: pnp_activate_dev ret=%d\n",
 235                                                __func__, err);
 236                                        return 0;
 237                                }
 238                                cs->hw.isurf.reset = pnp_port_start(pnp_d, 0);
 239                                cs->hw.isurf.phymem = pnp_mem_start(pnp_d, 1);
 240                                cs->irq = pnp_irq(pnp_d, 0);
 241                                if (!cs->irq || !cs->hw.isurf.reset || !cs->hw.isurf.phymem) {
 242                                        printk(KERN_ERR "ISurfPnP:some resources are missing %d/%x/%lx\n",
 243                                               cs->irq, cs->hw.isurf.reset, cs->hw.isurf.phymem);
 244                                        pnp_disable_dev(pnp_d);
 245                                        return (0);
 246                                }
 247                        } else {
 248                                printk(KERN_INFO "ISurfPnP: no ISAPnP card found\n");
 249                                return (0);
 250                        }
 251                } else {
 252                        printk(KERN_INFO "ISurfPnP: no ISAPnP bus found\n");
 253                        return (0);
 254                }
 255#else
 256                printk(KERN_WARNING "HiSax: Siemens I-Surf port/mem not set\n");
 257                return (0);
 258#endif
 259        }
 260        if (!request_region(cs->hw.isurf.reset, 1, "isurf isdn")) {
 261                printk(KERN_WARNING
 262                       "HiSax: Siemens I-Surf config port %x already in use\n",
 263                       cs->hw.isurf.reset);
 264                return (0);
 265        }
 266        if (!request_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE, "isurf iomem")) {
 267                printk(KERN_WARNING "HiSax: Siemens I-Surf memory region "
 268                       "%lx-%lx already in use\n",
 269                       cs->hw.isurf.phymem,
 270                       cs->hw.isurf.phymem + ISURF_IOMEM_SIZE);
 271                release_region(cs->hw.isurf.reset, 1);
 272                return (0);
 273        }
 274        cs->hw.isurf.isar = ioremap(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
 275        cs->hw.isurf.isac = cs->hw.isurf.isar + ISURF_ISAC_OFFSET;
 276        printk(KERN_INFO
 277               "ISurf: defined at 0x%x 0x%lx IRQ %d\n",
 278               cs->hw.isurf.reset,
 279               cs->hw.isurf.phymem,
 280               cs->irq);
 281
 282        setup_isac(cs);
 283        cs->cardmsg = &ISurf_card_msg;
 284        cs->irq_func = &isurf_interrupt;
 285        cs->auxcmd = &isurf_auxcmd;
 286        cs->readisac = &ReadISAC;
 287        cs->writeisac = &WriteISAC;
 288        cs->readisacfifo = &ReadISACfifo;
 289        cs->writeisacfifo = &WriteISACfifo;
 290        cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r;
 291        cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r;
 292        test_and_set_bit(HW_ISAR, &cs->HW_Flags);
 293        ISACVersion(cs, "ISurf:");
 294        cs->BC_Read_Reg = &ReadISAR;
 295        cs->BC_Write_Reg = &WriteISAR;
 296        cs->BC_Send_Data = &isar_fill_fifo;
 297        ver = ISARVersion(cs, "ISurf:");
 298        if (ver < 0) {
 299                printk(KERN_WARNING
 300                       "ISurf: wrong ISAR version (ret = %d)\n", ver);
 301                release_io_isurf(cs);
 302                return (0);
 303        }
 304        return (1);
 305}
 306