PageRenderTime 40ms CodeModel.GetById 12ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/isdn/hisax/enternow_pci.c

http://github.com/mirrors/linux
C | 420 lines | 263 code | 60 blank | 97 comment | 35 complexity | 06ccb41a931c3c889986269e4bf0bf90 MD5 | raw file
  1/* enternow_pci.c,v 0.99 2001/10/02
  2 *
  3 * enternow_pci.c       Card-specific routines for
  4 *                      Formula-n enter:now ISDN PCI ab
  5 *                      Gerdes AG Power ISDN PCI
  6 *                      Woerltronic SA 16 PCI
  7 *                      (based on HiSax driver by Karsten Keil)
  8 *
  9 * Author               Christoph Ersfeld <info@formula-n.de>
 10 *                      Formula-n Europe AG (www.formula-n.com)
 11 *                      previously Gerdes AG
 12 *
 13 *
 14 *                      This file is (c) under GNU PUBLIC LICENSE
 15 *
 16 * Notes:
 17 * This driver interfaces to netjet.c which performs B-channel
 18 * processing.
 19 *
 20 * Version 0.99 is the first release of this driver and there are
 21 * certainly a few bugs.
 22 * It isn't testet on linux 2.4 yet, so consider this code to be
 23 * beta.
 24 *
 25 * Please don't report me any malfunction without sending
 26 * (compressed) debug-logs.
 27 * It would be nearly impossible to retrace it.
 28 *
 29 * Log D-channel-processing as follows:
 30 *
 31 * 1. Load hisax with card-specific parameters, this example ist for
 32 *    Formula-n enter:now ISDN PCI and compatible
 33 *    (f.e. Gerdes Power ISDN PCI)
 34 *
 35 *    modprobe hisax type=41 protocol=2 id=gerdes
 36 *
 37 *    if you chose an other value for id, you need to modify the
 38 *    code below, too.
 39 *
 40 * 2. set debug-level
 41 *
 42 *    hisaxctrl gerdes 1 0x3ff
 43 *    hisaxctrl gerdes 11 0x4f
 44 *    cat /dev/isdnctrl >> ~/log &
 45 *
 46 * Please take also a look into /var/log/messages if there is
 47 * anything importand concerning HISAX.
 48 *
 49 *
 50 * Credits:
 51 * Programming the driver for Formula-n enter:now ISDN PCI and
 52 * necessary the driver for the used Amd 7930 D-channel-controller
 53 * was spnsored by Formula-n Europe AG.
 54 * Thanks to Karsten Keil and Petr Novak, who gave me support in
 55 * Hisax-specific questions.
 56 * I want so say special thanks to Carl-Friedrich Braun, who had to
 57 * answer a lot of questions about generally ISDN and about handling
 58 * of the Amd-Chip.
 59 *
 60 */
 61
 62
 63#include "hisax.h"
 64#include "isac.h"
 65#include "isdnl1.h"
 66#include "amd7930_fn.h"
 67#include <linux/interrupt.h>
 68#include <linux/ppp_defs.h>
 69#include <linux/pci.h>
 70#include <linux/init.h>
 71#include "netjet.h"
 72
 73
 74
 75static const char *enternow_pci_rev = "$Revision: 1.1.4.5 $";
 76
 77
 78/* for PowerISDN PCI */
 79#define TJ_AMD_IRQ                                              0x20
 80#define TJ_LED1                                                 0x40
 81#define TJ_LED2                                                 0x80
 82
 83
 84/* The window to [the] AMD [chip]...
 85 * From address hw.njet.base + TJ_AMD_PORT onwards, the AMD
 86 * maps [consecutive/multiple] 8 bits into the TigerJet I/O space
 87 * -> 0x01 of the AMD at hw.njet.base + 0C4 */
 88#define TJ_AMD_PORT                                             0xC0
 89
 90
 91
 92/* *************************** I/O-Interface functions ************************************* */
 93
 94
 95/* cs->readisac, macro rByteAMD */
 96static unsigned char
 97ReadByteAmd7930(struct IsdnCardState *cs, unsigned char offset)
 98{
 99	/* direct register */
100	if (offset < 8)
101		return (inb(cs->hw.njet.isac + 4 * offset));
102
103	/* indirect register */
104	else {
105		outb(offset, cs->hw.njet.isac + 4 * AMD_CR);
106		return (inb(cs->hw.njet.isac + 4 * AMD_DR));
107	}
108}
109
110/* cs->writeisac, macro wByteAMD */
111static void
112WriteByteAmd7930(struct IsdnCardState *cs, unsigned char offset, unsigned char value)
113{
114	/* direct register */
115	if (offset < 8)
116		outb(value, cs->hw.njet.isac + 4 * offset);
117
118	/* indirect register */
119	else {
120		outb(offset, cs->hw.njet.isac + 4 * AMD_CR);
121		outb(value, cs->hw.njet.isac + 4 * AMD_DR);
122	}
123}
124
125
126static void
127enpci_setIrqMask(struct IsdnCardState *cs, unsigned char val) {
128	if (!val)
129		outb(0x00, cs->hw.njet.base + NETJET_IRQMASK1);
130	else
131		outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1);
132}
133
134
135static unsigned char dummyrr(struct IsdnCardState *cs, int chan, unsigned char off)
136{
137	return (5);
138}
139
140static void dummywr(struct IsdnCardState *cs, int chan, unsigned char off, unsigned char value)
141{
142
143}
144
145
146/* ******************************************************************************** */
147
148
149static void
150reset_enpci(struct IsdnCardState *cs)
151{
152	if (cs->debug & L1_DEB_ISAC)
153		debugl1(cs, "enter:now PCI: reset");
154
155	/* Reset on, (also for AMD) */
156	cs->hw.njet.ctrl_reg = 0x07;
157	outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
158	mdelay(20);
159	/* Reset off */
160	cs->hw.njet.ctrl_reg = 0x30;
161	outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
162	/* 20ms delay */
163	mdelay(20);
164	cs->hw.njet.auxd = 0;  // LED-status
165	cs->hw.njet.dmactrl = 0;
166	outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL);
167	outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1);
168	outb(cs->hw.njet.auxd, cs->hw.njet.auxa); // LED off
169}
170
171
172static int
173enpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
174{
175	u_long flags;
176	unsigned char *chan;
177
178	if (cs->debug & L1_DEB_ISAC)
179		debugl1(cs, "enter:now PCI: card_msg: 0x%04X", mt);
180
181	switch (mt) {
182	case CARD_RESET:
183		spin_lock_irqsave(&cs->lock, flags);
184		reset_enpci(cs);
185		Amd7930_init(cs);
186		spin_unlock_irqrestore(&cs->lock, flags);
187		break;
188	case CARD_RELEASE:
189		release_io_netjet(cs);
190		break;
191	case CARD_INIT:
192		reset_enpci(cs);
193		inittiger(cs);
194		/* irq must be on here */
195		Amd7930_init(cs);
196		break;
197	case CARD_TEST:
198		break;
199	case MDL_ASSIGN:
200		/* TEI assigned, LED1 on */
201		cs->hw.njet.auxd = TJ_AMD_IRQ << 1;
202		outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
203		break;
204	case MDL_REMOVE:
205		/* TEI removed, LEDs off */
206		cs->hw.njet.auxd = 0;
207		outb(0x00, cs->hw.njet.base + NETJET_AUXDATA);
208		break;
209	case MDL_BC_ASSIGN:
210		/* activate B-channel */
211		chan = (unsigned char *)arg;
212
213		if (cs->debug & L1_DEB_ISAC)
214			debugl1(cs, "enter:now PCI: assign phys. BC %d in AMD LMR1", *chan);
215
216		cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 | (*chan + 1)), "MDL_BC_ASSIGN");
217		/* at least one b-channel in use, LED 2 on */
218		cs->hw.njet.auxd |= TJ_AMD_IRQ << 2;
219		outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
220		break;
221	case MDL_BC_RELEASE:
222		/* deactivate B-channel */
223		chan = (unsigned char *)arg;
224
225		if (cs->debug & L1_DEB_ISAC)
226			debugl1(cs, "enter:now PCI: release phys. BC %d in Amd LMR1", *chan);
227
228		cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 & ~(*chan + 1)), "MDL_BC_RELEASE");
229		/* no b-channel active -> LED2 off */
230		if (!(cs->dc.amd7930.lmr1 & 3)) {
231			cs->hw.njet.auxd &= ~(TJ_AMD_IRQ << 2);
232			outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
233		}
234		break;
235	default:
236		break;
237
238	}
239	return (0);
240}
241
242static irqreturn_t
243enpci_interrupt(int intno, void *dev_id)
244{
245	struct IsdnCardState *cs = dev_id;
246	unsigned char s0val, s1val, ir;
247	u_long flags;
248
249	spin_lock_irqsave(&cs->lock, flags);
250	s1val = inb(cs->hw.njet.base + NETJET_IRQSTAT1);
251
252	/* AMD threw an interrupt */
253	if (!(s1val & TJ_AMD_IRQ)) {
254		/* read and clear interrupt-register */
255		ir = ReadByteAmd7930(cs, 0x00);
256		Amd7930_interrupt(cs, ir);
257		s1val = 1;
258	} else
259		s1val = 0;
260	s0val = inb(cs->hw.njet.base + NETJET_IRQSTAT0);
261	if ((s0val | s1val) == 0) { // shared IRQ
262		spin_unlock_irqrestore(&cs->lock, flags);
263		return IRQ_NONE;
264	}
265	if (s0val)
266		outb(s0val, cs->hw.njet.base + NETJET_IRQSTAT0);
267
268	/* DMA-Interrupt: B-channel-stuff */
269	/* set bits in sval to indicate which page is free */
270	if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
271	    inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
272		/* the 2nd write page is free */
273		s0val = 0x08;
274	else	/* the 1st write page is free */
275		s0val = 0x04;
276	if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
277	    inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
278		/* the 2nd read page is free */
279		s0val = s0val | 0x02;
280	else	/* the 1st read page is free */
281		s0val = s0val | 0x01;
282	if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */
283	{
284		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
285			spin_unlock_irqrestore(&cs->lock, flags);
286			return IRQ_HANDLED;
287		}
288		cs->hw.njet.irqstat0 = s0val;
289		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) !=
290		    (cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
291			/* we have a read dma int */
292			read_tiger(cs);
293		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
294		    (cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
295			/* we have a write dma int */
296			write_tiger(cs);
297		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
298	}
299	spin_unlock_irqrestore(&cs->lock, flags);
300	return IRQ_HANDLED;
301}
302
303static int en_pci_probe(struct pci_dev *dev_netjet, struct IsdnCardState *cs)
304{
305	if (pci_enable_device(dev_netjet))
306		return (0);
307	cs->irq = dev_netjet->irq;
308	if (!cs->irq) {
309		printk(KERN_WARNING "enter:now PCI: No IRQ for PCI card found\n");
310		return (0);
311	}
312	cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
313	if (!cs->hw.njet.base) {
314		printk(KERN_WARNING "enter:now PCI: No IO-Adr for PCI card found\n");
315		return (0);
316	}
317	/* checks Sub-Vendor ID because system crashes with Traverse-Card */
318	if ((dev_netjet->subsystem_vendor != 0x55) ||
319	    (dev_netjet->subsystem_device != 0x02)) {
320		printk(KERN_WARNING "enter:now: You tried to load this driver with an incompatible TigerJet-card\n");
321		printk(KERN_WARNING "Use type=20 for Traverse NetJet PCI Card.\n");
322		return (0);
323	}
324
325	return (1);
326}
327
328static void en_cs_init(struct IsdnCard *card, struct IsdnCardState *cs)
329{
330	cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
331	cs->hw.njet.isac = cs->hw.njet.base + 0xC0; // Fenster zum AMD
332
333	/* Reset an */
334	cs->hw.njet.ctrl_reg = 0x07;  // geändert von 0xff
335	outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
336	/* 20 ms Pause */
337	mdelay(20);
338
339	cs->hw.njet.ctrl_reg = 0x30;  /* Reset Off and status read clear */
340	outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
341	mdelay(10);
342
343	cs->hw.njet.auxd = 0x00; // war 0xc0
344	cs->hw.njet.dmactrl = 0;
345
346	outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL);
347	outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1);
348	outb(cs->hw.njet.auxd, cs->hw.njet.auxa);
349}
350
351static int en_cs_init_rest(struct IsdnCard *card, struct IsdnCardState *cs)
352{
353	const int bytecnt = 256;
354
355	printk(KERN_INFO
356	       "enter:now PCI: PCI card configured at 0x%lx IRQ %d\n",
357	       cs->hw.njet.base, cs->irq);
358	if (!request_region(cs->hw.njet.base, bytecnt, "Fn_ISDN")) {
359		printk(KERN_WARNING
360		       "HiSax: enter:now config port %lx-%lx already in use\n",
361		       cs->hw.njet.base,
362		       cs->hw.njet.base + bytecnt);
363		return (0);
364	}
365
366	setup_Amd7930(cs);
367	cs->hw.njet.last_is0 = 0;
368	/* macro rByteAMD */
369	cs->readisac = &ReadByteAmd7930;
370	/* macro wByteAMD */
371	cs->writeisac = &WriteByteAmd7930;
372	cs->dc.amd7930.setIrqMask = &enpci_setIrqMask;
373
374	cs->BC_Read_Reg  = &dummyrr;
375	cs->BC_Write_Reg = &dummywr;
376	cs->BC_Send_Data = &netjet_fill_dma;
377	cs->cardmsg = &enpci_card_msg;
378	cs->irq_func = &enpci_interrupt;
379	cs->irq_flags |= IRQF_SHARED;
380
381	return (1);
382}
383
384static struct pci_dev *dev_netjet = NULL;
385
386/* called by config.c */
387int setup_enternow_pci(struct IsdnCard *card)
388{
389	int ret;
390	struct IsdnCardState *cs = card->cs;
391	char tmp[64];
392
393#ifdef __BIG_ENDIAN
394#error "not running on big endian machines now"
395#endif
396
397	strcpy(tmp, enternow_pci_rev);
398	printk(KERN_INFO "HiSax: Formula-n Europe AG enter:now ISDN PCI driver Rev. %s\n", HiSax_getrev(tmp));
399	if (cs->typ != ISDN_CTYPE_ENTERNOW)
400		return (0);
401	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
402
403	for (;;)
404	{
405		if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET,
406							PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
407			ret = en_pci_probe(dev_netjet, cs);
408			if (!ret)
409				return (0);
410		} else {
411			printk(KERN_WARNING "enter:now PCI: No PCI card found\n");
412			return (0);
413		}
414
415		en_cs_init(card, cs);
416		break;
417	}
418
419	return en_cs_init_rest(card, cs);
420}