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 | ||
ed5a84cd | 303 | static int en_pci_probe(struct pci_dev *dev_netjet, struct IsdnCardState *cs) |
1da177e4 | 304 | { |
a2b66515 | 305 | if (pci_enable_device(dev_netjet)) |
475be4d8 | 306 | return (0); |
a2b66515 JG |
307 | cs->irq = dev_netjet->irq; |
308 | if (!cs->irq) { | |
309 | printk(KERN_WARNING "enter:now PCI: No IRQ for PCI card found\n"); | |
475be4d8 | 310 | return (0); |
a2b66515 JG |
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"); | |
475be4d8 | 315 | return (0); |
a2b66515 JG |
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"); | |
475be4d8 | 322 | return (0); |
a2b66515 | 323 | } |
1da177e4 | 324 | |
475be4d8 | 325 | return (1); |
a2b66515 | 326 | } |
1da177e4 | 327 | |
ed5a84cd | 328 | static void en_cs_init(struct IsdnCard *card, struct IsdnCardState *cs) |
a2b66515 JG |
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 | |
1da177e4 | 332 | |
a2b66515 | 333 | /* Reset an */ |
d36b6910 | 334 | cs->hw.njet.ctrl_reg = 0x07; // geƤndert von 0xff |
a2b66515 JG |
335 | outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL); |
336 | /* 20 ms Pause */ | |
337 | mdelay(20); | |
1da177e4 | 338 | |
a2b66515 JG |
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); | |
1da177e4 | 342 | |
a2b66515 JG |
343 | cs->hw.njet.auxd = 0x00; // war 0xc0 |
344 | cs->hw.njet.dmactrl = 0; | |
1da177e4 | 345 | |
a2b66515 JG |
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 | } | |
1da177e4 | 350 | |
ed5a84cd | 351 | static int en_cs_init_rest(struct IsdnCard *card, struct IsdnCardState *cs) |
a2b66515 JG |
352 | { |
353 | const int bytecnt = 256; | |
1da177e4 LT |
354 | |
355 | printk(KERN_INFO | |
475be4d8 JP |
356 | "enter:now PCI: PCI card configured at 0x%lx IRQ %d\n", |
357 | cs->hw.njet.base, cs->irq); | |
1da177e4 LT |
358 | if (!request_region(cs->hw.njet.base, bytecnt, "Fn_ISDN")) { |
359 | printk(KERN_WARNING | |
a2b66515 JG |
360 | "HiSax: enter:now config port %lx-%lx already in use\n", |
361 | cs->hw.njet.base, | |
362 | cs->hw.njet.base + bytecnt); | |
1da177e4 LT |
363 | return (0); |
364 | } | |
a2b66515 | 365 | |
1da177e4 LT |
366 | setup_Amd7930(cs); |
367 | cs->hw.njet.last_is0 = 0; | |
475be4d8 JP |
368 | /* macro rByteAMD */ |
369 | cs->readisac = &ReadByteAmd7930; | |
370 | /* macro wByteAMD */ | |
371 | cs->writeisac = &WriteByteAmd7930; | |
372 | cs->dc.amd7930.setIrqMask = &enpci_setIrqMask; | |
1da177e4 | 373 | |
475be4d8 | 374 | cs->BC_Read_Reg = &dummyrr; |
1da177e4 LT |
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; | |
9ba02bec | 379 | cs->irq_flags |= IRQF_SHARED; |
1da177e4 | 380 | |
a2b66515 JG |
381 | return (1); |
382 | } | |
383 | ||
ed5a84cd | 384 | static struct pci_dev *dev_netjet = NULL; |
a2b66515 JG |
385 | |
386 | /* called by config.c */ | |
ed5a84cd | 387 | int setup_enternow_pci(struct IsdnCard *card) |
a2b66515 JG |
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 | ||
475be4d8 | 397 | strcpy(tmp, enternow_pci_rev); |
a2b66515 JG |
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) | |
475be4d8 | 400 | return (0); |
a2b66515 JG |
401 | test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); |
402 | ||
475be4d8 | 403 | for (;;) |
a2b66515 | 404 | { |
41a68a74 | 405 | if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET, |
475be4d8 | 406 | PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) { |
a2b66515 JG |
407 | ret = en_pci_probe(dev_netjet, cs); |
408 | if (!ret) | |
475be4d8 | 409 | return (0); |
a2b66515 | 410 | } else { |
475be4d8 JP |
411 | printk(KERN_WARNING "enter:now PCI: No PCI card found\n"); |
412 | return (0); | |
a2b66515 JG |
413 | } |
414 | ||
415 | en_cs_init(card, cs); | |
416 | break; | |
417 | } | |
418 | ||
475be4d8 | 419 | return en_cs_init_rest(card, cs); |
1da177e4 | 420 | } |