Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
1da177e4 LT |
63 | #include "hisax.h" |
64 | #include "isac.h" | |
65 | #include "isdnl1.h" | |
66 | #include "amd7930_fn.h" | |
1da177e4 LT |
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 | ||
672c3fd9 AB |
75 | static const char *enternow_pci_rev = "$Revision: 1.1.4.5 $"; |
76 | ||
77 | ||
96de0e25 | 78 | /* for PowerISDN PCI */ |
672c3fd9 AB |
79 | #define TJ_AMD_IRQ 0x20 |
80 | #define TJ_LED1 0x40 | |
81 | #define TJ_LED2 0x80 | |
82 | ||
83 | ||
96de0e25 JE |
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 */ | |
672c3fd9 AB |
88 | #define TJ_AMD_PORT 0xC0 |
89 | ||
1da177e4 LT |
90 | |
91 | ||
92 | /* *************************** I/O-Interface functions ************************************* */ | |
93 | ||
94 | ||
95 | /* cs->readisac, macro rByteAMD */ | |
672c3fd9 AB |
96 | static unsigned char |
97 | ReadByteAmd7930(struct IsdnCardState *cs, unsigned char offset) | |
1da177e4 | 98 | { |
96de0e25 | 99 | /* direct register */ |
475be4d8 JP |
100 | if (offset < 8) |
101 | return (inb(cs->hw.njet.isac + 4 * offset)); | |
1da177e4 | 102 | |
96de0e25 | 103 | /* indirect register */ |
1da177e4 | 104 | else { |
475be4d8 JP |
105 | outb(offset, cs->hw.njet.isac + 4 * AMD_CR); |
106 | return (inb(cs->hw.njet.isac + 4 * AMD_DR)); | |
1da177e4 LT |
107 | } |
108 | } | |
109 | ||
110 | /* cs->writeisac, macro wByteAMD */ | |
672c3fd9 AB |
111 | static void |
112 | WriteByteAmd7930(struct IsdnCardState *cs, unsigned char offset, unsigned char value) | |
1da177e4 | 113 | { |
96de0e25 | 114 | /* direct register */ |
475be4d8 JP |
115 | if (offset < 8) |
116 | outb(value, cs->hw.njet.isac + 4 * offset); | |
1da177e4 | 117 | |
96de0e25 | 118 | /* indirect register */ |
1da177e4 | 119 | else { |
475be4d8 JP |
120 | outb(offset, cs->hw.njet.isac + 4 * AMD_CR); |
121 | outb(value, cs->hw.njet.isac + 4 * AMD_DR); | |
1da177e4 LT |
122 | } |
123 | } | |
124 | ||
125 | ||
672c3fd9 AB |
126 | static void |
127 | enpci_setIrqMask(struct IsdnCardState *cs, unsigned char val) { | |
475be4d8 JP |
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); | |
1da177e4 LT |
132 | } |
133 | ||
134 | ||
672c3fd9 | 135 | static unsigned char dummyrr(struct IsdnCardState *cs, int chan, unsigned char off) |
1da177e4 | 136 | { |
475be4d8 | 137 | return (5); |
1da177e4 LT |
138 | } |
139 | ||
672c3fd9 | 140 | static void dummywr(struct IsdnCardState *cs, int chan, unsigned char off, unsigned char value) |
1da177e4 LT |
141 | { |
142 | ||
143 | } | |
144 | ||
145 | ||
146 | /* ******************************************************************************** */ | |
147 | ||
148 | ||
149 | static void | |
150 | reset_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; | |
672c3fd9 | 157 | outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); |
1da177e4 LT |
158 | mdelay(20); |
159 | /* Reset off */ | |
160 | cs->hw.njet.ctrl_reg = 0x30; | |
672c3fd9 | 161 | outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); |
1da177e4 LT |
162 | /* 20ms delay */ |
163 | mdelay(20); | |
164 | cs->hw.njet.auxd = 0; // LED-status | |
165 | cs->hw.njet.dmactrl = 0; | |
672c3fd9 AB |
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 | |
1da177e4 LT |
169 | } |
170 | ||
171 | ||
172 | static int | |
173 | enpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) | |
174 | { | |
175 | u_long flags; | |
475be4d8 | 176 | unsigned char *chan; |
1da177e4 LT |
177 | |
178 | if (cs->debug & L1_DEB_ISAC) | |
179 | debugl1(cs, "enter:now PCI: card_msg: 0x%04X", mt); | |
180 | ||
475be4d8 JP |
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; | |
1da177e4 LT |
237 | |
238 | } | |
475be4d8 | 239 | return (0); |
1da177e4 LT |
240 | } |
241 | ||
242 | static irqreturn_t | |
7d12e780 | 243 | enpci_interrupt(int intno, void *dev_id) |
1da177e4 LT |
244 | { |
245 | struct IsdnCardState *cs = dev_id; | |
672c3fd9 | 246 | unsigned char s0val, s1val, ir; |
1da177e4 LT |
247 | u_long flags; |
248 | ||
249 | spin_lock_irqsave(&cs->lock, flags); | |
672c3fd9 | 250 | s1val = inb(cs->hw.njet.base + NETJET_IRQSTAT1); |
1da177e4 | 251 | |
475be4d8 | 252 | /* AMD threw an interrupt */ |
1da177e4 | 253 | if (!(s1val & TJ_AMD_IRQ)) { |
475be4d8 | 254 | /* read and clear interrupt-register */ |
1da177e4 LT |
255 | ir = ReadByteAmd7930(cs, 0x00); |
256 | Amd7930_interrupt(cs, ir); | |
257 | s1val = 1; | |
258 | } else | |
259 | s1val = 0; | |
672c3fd9 | 260 | s0val = inb(cs->hw.njet.base + NETJET_IRQSTAT0); |
475be4d8 | 261 | if ((s0val | s1val) == 0) { // shared IRQ |
1da177e4 LT |
262 | spin_unlock_irqrestore(&cs->lock, flags); |
263 | return IRQ_NONE; | |
475be4d8 | 264 | } |
1da177e4 | 265 | if (s0val) |
672c3fd9 | 266 | outb(s0val, cs->hw.njet.base + NETJET_IRQSTAT0); |
1da177e4 LT |
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) < | |
475be4d8 | 271 | inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ)) |
1da177e4 LT |
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) < | |
475be4d8 | 277 | inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ)) |
1da177e4 LT |
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) != | |
475be4d8 | 290 | (cs->hw.njet.last_is0 & NETJET_IRQM0_READ)) |
1da177e4 LT |
291 | /* we have a read dma int */ |
292 | read_tiger(cs); | |
293 | if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) != | |
475be4d8 | 294 | (cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE)) |
1da177e4 LT |
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 | ||
a2b66515 JG |
303 | static int __devinit en_pci_probe(struct pci_dev *dev_netjet, |
304 | struct IsdnCardState *cs) | |
1da177e4 | 305 | { |
a2b66515 | 306 | if (pci_enable_device(dev_netjet)) |
475be4d8 | 307 | return (0); |
a2b66515 JG |
308 | cs->irq = dev_netjet->irq; |
309 | if (!cs->irq) { | |
310 | printk(KERN_WARNING "enter:now PCI: No IRQ for PCI card found\n"); | |
475be4d8 | 311 | return (0); |
a2b66515 JG |
312 | } |
313 | cs->hw.njet.base = pci_resource_start(dev_netjet, 0); | |
314 | if (!cs->hw.njet.base) { | |
315 | printk(KERN_WARNING "enter:now PCI: No IO-Adr for PCI card found\n"); | |
475be4d8 | 316 | return (0); |
a2b66515 JG |
317 | } |
318 | /* checks Sub-Vendor ID because system crashes with Traverse-Card */ | |
319 | if ((dev_netjet->subsystem_vendor != 0x55) || | |
320 | (dev_netjet->subsystem_device != 0x02)) { | |
321 | printk(KERN_WARNING "enter:now: You tried to load this driver with an incompatible TigerJet-card\n"); | |
322 | printk(KERN_WARNING "Use type=20 for Traverse NetJet PCI Card.\n"); | |
475be4d8 | 323 | return (0); |
a2b66515 | 324 | } |
1da177e4 | 325 | |
475be4d8 | 326 | return (1); |
a2b66515 | 327 | } |
1da177e4 | 328 | |
a2b66515 JG |
329 | static void __devinit en_cs_init(struct IsdnCard *card, |
330 | struct IsdnCardState *cs) | |
331 | { | |
332 | cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA; | |
333 | cs->hw.njet.isac = cs->hw.njet.base + 0xC0; // Fenster zum AMD | |
1da177e4 | 334 | |
a2b66515 | 335 | /* Reset an */ |
d36b6910 | 336 | cs->hw.njet.ctrl_reg = 0x07; // geƤndert von 0xff |
a2b66515 JG |
337 | outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); |
338 | /* 20 ms Pause */ | |
339 | mdelay(20); | |
1da177e4 | 340 | |
a2b66515 JG |
341 | cs->hw.njet.ctrl_reg = 0x30; /* Reset Off and status read clear */ |
342 | outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); | |
343 | mdelay(10); | |
1da177e4 | 344 | |
a2b66515 JG |
345 | cs->hw.njet.auxd = 0x00; // war 0xc0 |
346 | cs->hw.njet.dmactrl = 0; | |
1da177e4 | 347 | |
a2b66515 JG |
348 | outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL); |
349 | outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1); | |
350 | outb(cs->hw.njet.auxd, cs->hw.njet.auxa); | |
351 | } | |
1da177e4 | 352 | |
a2b66515 JG |
353 | static int __devinit en_cs_init_rest(struct IsdnCard *card, |
354 | struct IsdnCardState *cs) | |
355 | { | |
356 | const int bytecnt = 256; | |
1da177e4 LT |
357 | |
358 | printk(KERN_INFO | |
475be4d8 JP |
359 | "enter:now PCI: PCI card configured at 0x%lx IRQ %d\n", |
360 | cs->hw.njet.base, cs->irq); | |
1da177e4 LT |
361 | if (!request_region(cs->hw.njet.base, bytecnt, "Fn_ISDN")) { |
362 | printk(KERN_WARNING | |
a2b66515 JG |
363 | "HiSax: enter:now config port %lx-%lx already in use\n", |
364 | cs->hw.njet.base, | |
365 | cs->hw.njet.base + bytecnt); | |
1da177e4 LT |
366 | return (0); |
367 | } | |
a2b66515 | 368 | |
1da177e4 LT |
369 | setup_Amd7930(cs); |
370 | cs->hw.njet.last_is0 = 0; | |
475be4d8 JP |
371 | /* macro rByteAMD */ |
372 | cs->readisac = &ReadByteAmd7930; | |
373 | /* macro wByteAMD */ | |
374 | cs->writeisac = &WriteByteAmd7930; | |
375 | cs->dc.amd7930.setIrqMask = &enpci_setIrqMask; | |
1da177e4 | 376 | |
475be4d8 | 377 | cs->BC_Read_Reg = &dummyrr; |
1da177e4 LT |
378 | cs->BC_Write_Reg = &dummywr; |
379 | cs->BC_Send_Data = &netjet_fill_dma; | |
380 | cs->cardmsg = &enpci_card_msg; | |
381 | cs->irq_func = &enpci_interrupt; | |
9ba02bec | 382 | cs->irq_flags |= IRQF_SHARED; |
1da177e4 | 383 | |
a2b66515 JG |
384 | return (1); |
385 | } | |
386 | ||
387 | static struct pci_dev *dev_netjet __devinitdata = NULL; | |
388 | ||
389 | /* called by config.c */ | |
390 | int __devinit | |
391 | setup_enternow_pci(struct IsdnCard *card) | |
392 | { | |
393 | int ret; | |
394 | struct IsdnCardState *cs = card->cs; | |
395 | char tmp[64]; | |
396 | ||
397 | #ifdef __BIG_ENDIAN | |
398 | #error "not running on big endian machines now" | |
399 | #endif | |
400 | ||
475be4d8 | 401 | strcpy(tmp, enternow_pci_rev); |
a2b66515 JG |
402 | printk(KERN_INFO "HiSax: Formula-n Europe AG enter:now ISDN PCI driver Rev. %s\n", HiSax_getrev(tmp)); |
403 | if (cs->typ != ISDN_CTYPE_ENTERNOW) | |
475be4d8 | 404 | return (0); |
a2b66515 JG |
405 | test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); |
406 | ||
475be4d8 | 407 | for (;;) |
a2b66515 | 408 | { |
41a68a74 | 409 | if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET, |
475be4d8 | 410 | PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) { |
a2b66515 JG |
411 | ret = en_pci_probe(dev_netjet, cs); |
412 | if (!ret) | |
475be4d8 | 413 | return (0); |
a2b66515 | 414 | } else { |
475be4d8 JP |
415 | printk(KERN_WARNING "enter:now PCI: No PCI card found\n"); |
416 | return (0); | |
a2b66515 JG |
417 | } |
418 | ||
419 | en_cs_init(card, cs); | |
420 | break; | |
421 | } | |
422 | ||
475be4d8 | 423 | return en_cs_init_rest(card, cs); |
1da177e4 | 424 | } |