/* enternow_pci.c,v 0.99 2001/10/02 * * enternow_pci.c Card-specific routines for * Formula-n enter:now ISDN PCI ab * Gerdes AG Power ISDN PCI * Woerltronic SA 16 PCI * (based on HiSax driver by Karsten Keil) * * Author Christoph Ersfeld <info@formula-n.de> * Formula-n Europe AG (www.formula-n.com) * previously Gerdes AG * * * This file is (c) under GNU PUBLIC LICENSE * * Notes: * This driver interfaces to netjet.c which performs B-channel * processing. * * Version 0.99 is the first release of this driver and there are * certainly a few bugs. * It isn't testet on linux 2.4 yet, so consider this code to be * beta. * * Please don't report me any malfunction without sending * (compressed) debug-logs. * It would be nearly impossible to retrace it. * * Log D-channel-processing as follows: * * 1. Load hisax with card-specific parameters, this example ist for * Formula-n enter:now ISDN PCI and compatible * (f.e. Gerdes Power ISDN PCI) * * modprobe hisax type=41 protocol=2 id=gerdes * * if you chose an other value for id, you need to modify the * code below, too. * * 2. set debug-level * * hisaxctrl gerdes 1 0x3ff * hisaxctrl gerdes 11 0x4f * cat /dev/isdnctrl >> ~/log & * * Please take also a look into /var/log/messages if there is * anything importand concerning HISAX. * * * Credits: * Programming the driver for Formula-n enter:now ISDN PCI and * necessary the driver for the used Amd 7930 D-channel-controller * was spnsored by Formula-n Europe AG. * Thanks to Karsten Keil and Petr Novak, who gave me support in * Hisax-specific questions. * I want so say special thanks to Carl-Friedrich Braun, who had to * answer a lot of questions about generally ISDN and about handling * of the Amd-Chip. * */ #include "hisax.h" #include "isac.h" #include "isdnl1.h" #include "amd7930_fn.h" #include <linux/interrupt.h> #include <linux/ppp_defs.h> #include <linux/pci.h> #include <linux/init.h> #include "netjet.h" static const char *enternow_pci_rev = "$Revision: 1.1.4.5 $"; /* for PowerISDN PCI */ #define TJ_AMD_IRQ 0x20 #define TJ_LED1 0x40 #define TJ_LED2 0x80 /* The window to [the] AMD [chip]... * From address hw.njet.base + TJ_AMD_PORT onwards, the AMD * maps [consecutive/multiple] 8 bits into the TigerJet I/O space * -> 0x01 of the AMD at hw.njet.base + 0C4 */ #define TJ_AMD_PORT 0xC0 /* *************************** I/O-Interface functions ************************************* */ /* cs->readisac, macro rByteAMD */ static unsigned char ReadByteAmd7930(struct IsdnCardState *cs, unsigned char offset) { /* direct register */ if (offset < 8) return (inb(cs->hw.njet.isac + 4 * offset)); /* indirect register */ else { outb(offset, cs->hw.njet.isac + 4 * AMD_CR); return (inb(cs->hw.njet.isac + 4 * AMD_DR)); } } /* cs->writeisac, macro wByteAMD */ static void WriteByteAmd7930(struct IsdnCardState *cs, unsigned char offset, unsigned char value) { /* direct register */ if (offset < 8) outb(value, cs->hw.njet.isac + 4 * offset); /* indirect register */ else { outb(offset, cs->hw.njet.isac + 4 * AMD_CR); outb(value, cs->hw.njet.isac + 4 * AMD_DR); } } static void enpci_setIrqMask(struct IsdnCardState *cs, unsigned char val) { if (!val) outb(0x00, cs->hw.njet.base + NETJET_IRQMASK1); else outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1); } static unsigned char dummyrr(struct IsdnCardState *cs, int chan, unsigned char off) { return (5); } static void dummywr(struct IsdnCardState *cs, int chan, unsigned char off, unsigned char value) { } /* ******************************************************************************** */ static void reset_enpci(struct IsdnCardState *cs) { if (cs->debug & L1_DEB_ISAC) debugl1(cs, "enter:now PCI: reset"); /* Reset on, (also for AMD) */ cs->hw.njet.ctrl_reg = 0x07; outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); mdelay(20); /* Reset off */ cs->hw.njet.ctrl_reg = 0x30; outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); /* 20ms delay */ mdelay(20); cs->hw.njet.auxd = 0; // LED-status cs->hw.njet.dmactrl = 0; outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL); outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1); outb(cs->hw.njet.auxd, cs->hw.njet.auxa); // LED off } static int enpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) { u_long flags; unsigned char *chan; if (cs->debug & L1_DEB_ISAC) debugl1(cs, "enter:now PCI: card_msg: 0x%04X", mt); switch (mt) { case CARD_RESET: spin_lock_irqsave(&cs->lock, flags); reset_enpci(cs); Amd7930_init(cs); spin_unlock_irqrestore(&cs->lock, flags); break; case CARD_RELEASE: release_io_netjet(cs); break; case CARD_INIT: reset_enpci(cs); inittiger(cs); /* irq must be on here */ Amd7930_init(cs); break; case CARD_TEST: break; case MDL_ASSIGN: /* TEI assigned, LED1 on */ cs->hw.njet.auxd = TJ_AMD_IRQ << 1; outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA); break; case MDL_REMOVE: /* TEI removed, LEDs off */ cs->hw.njet.auxd = 0; outb(0x00, cs->hw.njet.base + NETJET_AUXDATA); break; case MDL_BC_ASSIGN: /* activate B-channel */ chan = (unsigned char *)arg; if (cs->debug & L1_DEB_ISAC) debugl1(cs, "enter:now PCI: assign phys. BC %d in AMD LMR1", *chan); cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 | (*chan + 1)), "MDL_BC_ASSIGN"); /* at least one b-channel in use, LED 2 on */ cs->hw.njet.auxd |= TJ_AMD_IRQ << 2; outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA); break; case MDL_BC_RELEASE: /* deactivate B-channel */ chan = (unsigned char *)arg; if (cs->debug & L1_DEB_ISAC) debugl1(cs, "enter:now PCI: release phys. BC %d in Amd LMR1", *chan); cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 & ~(*chan + 1)), "MDL_BC_RELEASE"); /* no b-channel active -> LED2 off */ if (!(cs->dc.amd7930.lmr1 & 3)) { cs->hw.njet.auxd &= ~(TJ_AMD_IRQ << 2); outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA); } break; default: break; } return (0); } static irqreturn_t enpci_interrupt(int intno, void *dev_id) { struct IsdnCardState *cs = dev_id; unsigned char s0val, s1val, ir; u_long flags; spin_lock_irqsave(&cs->lock, flags); s1val = inb(cs->hw.njet.base + NETJET_IRQSTAT1); /* AMD threw an interrupt */ if (!(s1val & TJ_AMD_IRQ)) { /* read and clear interrupt-register */ ir = ReadByteAmd7930(cs, 0x00); Amd7930_interrupt(cs, ir); s1val = 1; } else s1val = 0; s0val = inb(cs->hw.njet.base + NETJET_IRQSTAT0); if ((s0val | s1val) == 0) { // shared IRQ spin_unlock_irqrestore(&cs->lock, flags); return IRQ_NONE; } if (s0val) outb(s0val, cs->hw.njet.base + NETJET_IRQSTAT0); /* DMA-Interrupt: B-channel-stuff */ /* set bits in sval to indicate which page is free */ if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) < inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ)) /* the 2nd write page is free */ s0val = 0x08; else /* the 1st write page is free */ s0val = 0x04; if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) < inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ)) /* the 2nd read page is free */ s0val = s0val | 0x02; else /* the 1st read page is free */ s0val = s0val | 0x01; if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */ { if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { spin_unlock_irqrestore(&cs->lock, flags); return IRQ_HANDLED; } cs->hw.njet.irqstat0 = s0val; if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) != (cs->hw.njet.last_is0 & NETJET_IRQM0_READ)) /* we have a read dma int */ read_tiger(cs); if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) != (cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE)) /* we have a write dma int */ write_tiger(cs); test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); } spin_unlock_irqrestore(&cs->lock, flags); return IRQ_HANDLED; } static int en_pci_probe(struct pci_dev *dev_netjet, struct IsdnCardState *cs) { if (pci_enable_device(dev_netjet)) return (0); cs->irq = dev_netjet->irq; if (!cs->irq) { printk(KERN_WARNING "enter:now PCI: No IRQ for PCI card found\n"); return (0); } cs->hw.njet.base = pci_resource_start(dev_netjet, 0); if (!cs->hw.njet.base) { printk(KERN_WARNING "enter:now PCI: No IO-Adr for PCI card found\n"); return (0); } /* checks Sub-Vendor ID because system crashes with Traverse-Card */ if ((dev_netjet->subsystem_vendor != 0x55) || (dev_netjet->subsystem_device != 0x02)) { printk(KERN_WARNING "enter:now: You tried to load this driver with an incompatible TigerJet-card\n"); printk(KERN_WARNING "Use type=20 for Traverse NetJet PCI Card.\n"); return (0); } return (1); } static void en_cs_init(struct IsdnCard *card, struct IsdnCardState *cs) { cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA; cs->hw.njet.isac = cs->hw.njet.base + 0xC0; // Fenster zum AMD /* Reset an */ cs->hw.njet.ctrl_reg = 0x07; // geändert von 0xff outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); /* 20 ms Pause */ mdelay(20); cs->hw.njet.ctrl_reg = 0x30; /* Reset Off and status read clear */ outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); mdelay(10); cs->hw.njet.auxd = 0x00; // war 0xc0 cs->hw.njet.dmactrl = 0; outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL); outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1); outb(cs->hw.njet.auxd, cs->hw.njet.auxa); } static int en_cs_init_rest(struct IsdnCard *card, struct IsdnCardState *cs) { const int bytecnt = 256; printk(KERN_INFO "enter:now PCI: PCI card configured at 0x%lx IRQ %d\n", cs->hw.njet.base, cs->irq); if (!request_region(cs->hw.njet.base, bytecnt, "Fn_ISDN")) { printk(KERN_WARNING "HiSax: enter:now config port %lx-%lx already in use\n", cs->hw.njet.base, cs->hw.njet.base + bytecnt); return (0); } setup_Amd7930(cs); cs->hw.njet.last_is0 = 0; /* macro rByteAMD */ cs->readisac = &ReadByteAmd7930; /* macro wByteAMD */ cs->writeisac = &WriteByteAmd7930; cs->dc.amd7930.setIrqMask = &enpci_setIrqMask; cs->BC_Read_Reg = &dummyrr; cs->BC_Write_Reg = &dummywr; cs->BC_Send_Data = &netjet_fill_dma; cs->cardmsg = &enpci_card_msg; cs->irq_func = &enpci_interrupt; cs->irq_flags |= IRQF_SHARED; return (1); } static struct pci_dev *dev_netjet = NULL; /* called by config.c */ int setup_enternow_pci(struct IsdnCard *card) { int ret; struct IsdnCardState *cs = card->cs; char tmp[64]; #ifdef __BIG_ENDIAN #error "not running on big endian machines now" #endif strcpy(tmp, enternow_pci_rev); printk(KERN_INFO "HiSax: Formula-n Europe AG enter:now ISDN PCI driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_ENTERNOW) return (0); test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); for (;;) { if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) { ret = en_pci_probe(dev_netjet, cs); if (!ret) return (0); } else { printk(KERN_WARNING "enter:now PCI: No PCI card found\n"); return (0); } en_cs_init(card, cs); break; } return en_cs_init_rest(card, cs); }