1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * mISDNinfineon.c |
4 | * Support for cards based on following Infineon ISDN chipsets |
5 | * - ISAC + HSCX |
6 | * - IPAC and IPAC-X |
7 | * - ISAC-SX + HSCX |
8 | * |
9 | * Supported cards: |
10 | * - Dialogic Diva 2.0 |
11 | * - Dialogic Diva 2.0U |
12 | * - Dialogic Diva 2.01 |
13 | * - Dialogic Diva 2.02 |
14 | * - Sedlbauer Speedwin |
15 | * - HST Saphir3 |
16 | * - Develo (former ELSA) Microlink PCI (Quickstep 1000) |
17 | * - Develo (former ELSA) Quickstep 3000 |
18 | * - Berkom Scitel BRIX Quadro |
19 | * - Dr.Neuhaus (Sagem) Niccy |
20 | * |
21 | * Author Karsten Keil <keil@isdn4linux.de> |
22 | * |
23 | * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> |
24 | */ |
25 | |
26 | #include <linux/interrupt.h> |
27 | #include <linux/module.h> |
28 | #include <linux/pci.h> |
29 | #include <linux/delay.h> |
30 | #include <linux/mISDNhw.h> |
31 | #include <linux/slab.h> |
32 | #include "ipac.h" |
33 | |
34 | #define INFINEON_REV "1.0" |
35 | |
36 | static int inf_cnt; |
37 | static u32 debug; |
38 | static u32 irqloops = 4; |
39 | |
40 | enum inf_types { |
41 | INF_NONE, |
42 | INF_DIVA20, |
43 | INF_DIVA20U, |
44 | INF_DIVA201, |
45 | INF_DIVA202, |
46 | INF_SPEEDWIN, |
47 | INF_SAPHIR3, |
48 | INF_QS1000, |
49 | INF_QS3000, |
50 | INF_NICCY, |
51 | INF_SCT_1, |
52 | INF_SCT_2, |
53 | INF_SCT_3, |
54 | INF_SCT_4, |
55 | INF_GAZEL_R685, |
56 | INF_GAZEL_R753 |
57 | }; |
58 | |
59 | enum addr_mode { |
60 | AM_NONE = 0, |
61 | AM_IO, |
62 | AM_MEMIO, |
63 | AM_IND_IO, |
64 | }; |
65 | |
66 | struct inf_cinfo { |
67 | enum inf_types typ; |
68 | const char *full; |
69 | const char *name; |
70 | enum addr_mode cfg_mode; |
71 | enum addr_mode addr_mode; |
72 | u8 cfg_bar; |
73 | u8 addr_bar; |
74 | void *irqfunc; |
75 | }; |
76 | |
77 | struct _ioaddr { |
78 | enum addr_mode mode; |
79 | union { |
80 | void __iomem *p; |
81 | struct _ioport io; |
82 | } a; |
83 | }; |
84 | |
85 | struct _iohandle { |
86 | enum addr_mode mode; |
87 | resource_size_t size; |
88 | resource_size_t start; |
89 | void __iomem *p; |
90 | }; |
91 | |
92 | struct inf_hw { |
93 | struct list_head list; |
94 | struct pci_dev *pdev; |
95 | const struct inf_cinfo *ci; |
96 | char name[MISDN_MAX_IDLEN]; |
97 | u32 irq; |
98 | u32 irqcnt; |
99 | struct _iohandle cfg; |
100 | struct _iohandle addr; |
101 | struct _ioaddr isac; |
102 | struct _ioaddr hscx; |
103 | spinlock_t lock; /* HW access lock */ |
104 | struct ipac_hw ipac; |
105 | struct inf_hw *sc[3]; /* slave cards */ |
106 | }; |
107 | |
108 | |
109 | #define PCI_SUBVENDOR_HST_SAPHIR3 0x52 |
110 | #define PCI_SUBVENDOR_SEDLBAUER_PCI 0x53 |
111 | #define PCI_SUB_ID_SEDLBAUER 0x01 |
112 | |
113 | static struct pci_device_id infineon_ids[] = { |
114 | { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20), INF_DIVA20 }, |
115 | { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20_U), INF_DIVA20U }, |
116 | { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA201), INF_DIVA201 }, |
117 | { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA202), INF_DIVA202 }, |
118 | { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, |
119 | PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0, |
120 | INF_SPEEDWIN }, |
121 | { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, |
122 | PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3 }, |
123 | { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_MICROLINK), INF_QS1000 }, |
124 | { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_QS3000), INF_QS3000 }, |
125 | { PCI_VDEVICE(SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY), INF_NICCY }, |
126 | { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, |
127 | PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0, |
128 | INF_SCT_1 }, |
129 | { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R685), INF_GAZEL_R685 }, |
130 | { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R753), INF_GAZEL_R753 }, |
131 | { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO), INF_GAZEL_R753 }, |
132 | { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_OLITEC), INF_GAZEL_R753 }, |
133 | { } |
134 | }; |
135 | MODULE_DEVICE_TABLE(pci, infineon_ids); |
136 | |
137 | /* PCI interface specific defines */ |
138 | /* Diva 2.0/2.0U */ |
139 | #define DIVA_HSCX_PORT 0x00 |
140 | #define DIVA_HSCX_ALE 0x04 |
141 | #define DIVA_ISAC_PORT 0x08 |
142 | #define DIVA_ISAC_ALE 0x0C |
143 | #define DIVA_PCI_CTRL 0x10 |
144 | |
145 | /* DIVA_PCI_CTRL bits */ |
146 | #define DIVA_IRQ_BIT 0x01 |
147 | #define DIVA_RESET_BIT 0x08 |
148 | #define DIVA_EEPROM_CLK 0x40 |
149 | #define DIVA_LED_A 0x10 |
150 | #define DIVA_LED_B 0x20 |
151 | #define DIVA_IRQ_CLR 0x80 |
152 | |
153 | /* Diva 2.01/2.02 */ |
154 | /* Siemens PITA */ |
155 | #define PITA_ICR_REG 0x00 |
156 | #define PITA_INT0_STATUS 0x02 |
157 | |
158 | #define PITA_MISC_REG 0x1c |
159 | #define PITA_PARA_SOFTRESET 0x01000000 |
160 | #define PITA_SER_SOFTRESET 0x02000000 |
161 | #define PITA_PARA_MPX_MODE 0x04000000 |
162 | #define PITA_INT0_ENABLE 0x00020000 |
163 | |
164 | /* TIGER 100 Registers */ |
165 | #define TIGER_RESET_ADDR 0x00 |
166 | #define TIGER_EXTERN_RESET 0x01 |
167 | #define TIGER_AUX_CTRL 0x02 |
168 | #define TIGER_AUX_DATA 0x03 |
169 | #define TIGER_AUX_IRQMASK 0x05 |
170 | #define TIGER_AUX_STATUS 0x07 |
171 | |
172 | /* Tiger AUX BITs */ |
173 | #define TIGER_IOMASK 0xdd /* 1 and 5 are inputs */ |
174 | #define TIGER_IRQ_BIT 0x02 |
175 | |
176 | #define TIGER_IPAC_ALE 0xC0 |
177 | #define TIGER_IPAC_PORT 0xC8 |
178 | |
179 | /* ELSA (now Develo) PCI cards */ |
180 | #define ELSA_IRQ_ADDR 0x4c |
181 | #define ELSA_IRQ_MASK 0x04 |
182 | #define QS1000_IRQ_OFF 0x01 |
183 | #define QS3000_IRQ_OFF 0x03 |
184 | #define QS1000_IRQ_ON 0x41 |
185 | #define QS3000_IRQ_ON 0x43 |
186 | |
187 | /* Dr Neuhaus/Sagem Niccy */ |
188 | #define NICCY_ISAC_PORT 0x00 |
189 | #define NICCY_HSCX_PORT 0x01 |
190 | #define NICCY_ISAC_ALE 0x02 |
191 | #define NICCY_HSCX_ALE 0x03 |
192 | |
193 | #define NICCY_IRQ_CTRL_REG 0x38 |
194 | #define NICCY_IRQ_ENABLE 0x001f00 |
195 | #define NICCY_IRQ_DISABLE 0xff0000 |
196 | #define NICCY_IRQ_BIT 0x800000 |
197 | |
198 | |
199 | /* Scitel PLX */ |
200 | #define SCT_PLX_IRQ_ADDR 0x4c |
201 | #define SCT_PLX_RESET_ADDR 0x50 |
202 | #define SCT_PLX_IRQ_ENABLE 0x41 |
203 | #define SCT_PLX_RESET_BIT 0x04 |
204 | |
205 | /* Gazel */ |
206 | #define GAZEL_IPAC_DATA_PORT 0x04 |
207 | /* Gazel PLX */ |
208 | #define GAZEL_CNTRL 0x50 |
209 | #define GAZEL_RESET 0x04 |
210 | #define GAZEL_RESET_9050 0x40000000 |
211 | #define GAZEL_INCSR 0x4C |
212 | #define GAZEL_ISAC_EN 0x08 |
213 | #define GAZEL_INT_ISAC 0x20 |
214 | #define GAZEL_HSCX_EN 0x01 |
215 | #define GAZEL_INT_HSCX 0x04 |
216 | #define GAZEL_PCI_EN 0x40 |
217 | #define GAZEL_IPAC_EN 0x03 |
218 | |
219 | |
220 | static LIST_HEAD(Cards); |
221 | static DEFINE_RWLOCK(card_lock); /* protect Cards */ |
222 | |
223 | static void |
224 | _set_debug(struct inf_hw *card) |
225 | { |
226 | card->ipac.isac.dch.debug = debug; |
227 | card->ipac.hscx[0].bch.debug = debug; |
228 | card->ipac.hscx[1].bch.debug = debug; |
229 | } |
230 | |
231 | static int |
232 | set_debug(const char *val, const struct kernel_param *kp) |
233 | { |
234 | int ret; |
235 | struct inf_hw *card; |
236 | |
237 | ret = param_set_uint(val, kp); |
238 | if (!ret) { |
239 | read_lock(&card_lock); |
240 | list_for_each_entry(card, &Cards, list) |
241 | _set_debug(card); |
242 | read_unlock(&card_lock); |
243 | } |
244 | return ret; |
245 | } |
246 | |
247 | MODULE_AUTHOR("Karsten Keil" ); |
248 | MODULE_LICENSE("GPL v2" ); |
249 | MODULE_VERSION(INFINEON_REV); |
250 | module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); |
251 | MODULE_PARM_DESC(debug, "infineon debug mask" ); |
252 | module_param(irqloops, uint, S_IRUGO | S_IWUSR); |
253 | MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)" ); |
254 | |
255 | /* Interface functions */ |
256 | |
257 | IOFUNC_IO(ISAC, inf_hw, isac.a.io) |
258 | IOFUNC_IO(IPAC, inf_hw, hscx.a.io) |
259 | IOFUNC_IND(ISAC, inf_hw, isac.a.io) |
260 | IOFUNC_IND(IPAC, inf_hw, hscx.a.io) |
261 | IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p) |
262 | IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p) |
263 | |
264 | static irqreturn_t |
265 | diva_irq(int intno, void *dev_id) |
266 | { |
267 | struct inf_hw *hw = dev_id; |
268 | u8 val; |
269 | |
270 | spin_lock(lock: &hw->lock); |
271 | val = inb(port: (u32)hw->cfg.start + DIVA_PCI_CTRL); |
272 | if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */ |
273 | spin_unlock(lock: &hw->lock); |
274 | return IRQ_NONE; /* shared */ |
275 | } |
276 | hw->irqcnt++; |
277 | mISDNipac_irq(&hw->ipac, irqloops); |
278 | spin_unlock(lock: &hw->lock); |
279 | return IRQ_HANDLED; |
280 | } |
281 | |
282 | static irqreturn_t |
283 | diva20x_irq(int intno, void *dev_id) |
284 | { |
285 | struct inf_hw *hw = dev_id; |
286 | u8 val; |
287 | |
288 | spin_lock(lock: &hw->lock); |
289 | val = readb(addr: hw->cfg.p); |
290 | if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */ |
291 | spin_unlock(lock: &hw->lock); |
292 | return IRQ_NONE; /* shared */ |
293 | } |
294 | hw->irqcnt++; |
295 | mISDNipac_irq(&hw->ipac, irqloops); |
296 | writeb(PITA_INT0_STATUS, addr: hw->cfg.p); /* ACK PITA INT0 */ |
297 | spin_unlock(lock: &hw->lock); |
298 | return IRQ_HANDLED; |
299 | } |
300 | |
301 | static irqreturn_t |
302 | tiger_irq(int intno, void *dev_id) |
303 | { |
304 | struct inf_hw *hw = dev_id; |
305 | u8 val; |
306 | |
307 | spin_lock(lock: &hw->lock); |
308 | val = inb(port: (u32)hw->cfg.start + TIGER_AUX_STATUS); |
309 | if (val & TIGER_IRQ_BIT) { /* for us or shared ? */ |
310 | spin_unlock(lock: &hw->lock); |
311 | return IRQ_NONE; /* shared */ |
312 | } |
313 | hw->irqcnt++; |
314 | mISDNipac_irq(&hw->ipac, irqloops); |
315 | spin_unlock(lock: &hw->lock); |
316 | return IRQ_HANDLED; |
317 | } |
318 | |
319 | static irqreturn_t |
320 | elsa_irq(int intno, void *dev_id) |
321 | { |
322 | struct inf_hw *hw = dev_id; |
323 | u8 val; |
324 | |
325 | spin_lock(lock: &hw->lock); |
326 | val = inb(port: (u32)hw->cfg.start + ELSA_IRQ_ADDR); |
327 | if (!(val & ELSA_IRQ_MASK)) { |
328 | spin_unlock(lock: &hw->lock); |
329 | return IRQ_NONE; /* shared */ |
330 | } |
331 | hw->irqcnt++; |
332 | mISDNipac_irq(&hw->ipac, irqloops); |
333 | spin_unlock(lock: &hw->lock); |
334 | return IRQ_HANDLED; |
335 | } |
336 | |
337 | static irqreturn_t |
338 | niccy_irq(int intno, void *dev_id) |
339 | { |
340 | struct inf_hw *hw = dev_id; |
341 | u32 val; |
342 | |
343 | spin_lock(lock: &hw->lock); |
344 | val = inl(port: (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); |
345 | if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */ |
346 | spin_unlock(lock: &hw->lock); |
347 | return IRQ_NONE; /* shared */ |
348 | } |
349 | outl(value: val, port: (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); |
350 | hw->irqcnt++; |
351 | mISDNipac_irq(&hw->ipac, irqloops); |
352 | spin_unlock(lock: &hw->lock); |
353 | return IRQ_HANDLED; |
354 | } |
355 | |
356 | static irqreturn_t |
357 | gazel_irq(int intno, void *dev_id) |
358 | { |
359 | struct inf_hw *hw = dev_id; |
360 | irqreturn_t ret; |
361 | |
362 | spin_lock(lock: &hw->lock); |
363 | ret = mISDNipac_irq(&hw->ipac, irqloops); |
364 | spin_unlock(lock: &hw->lock); |
365 | return ret; |
366 | } |
367 | |
368 | static irqreturn_t |
369 | ipac_irq(int intno, void *dev_id) |
370 | { |
371 | struct inf_hw *hw = dev_id; |
372 | u8 val; |
373 | |
374 | spin_lock(lock: &hw->lock); |
375 | val = hw->ipac.read_reg(hw, IPAC_ISTA); |
376 | if (!(val & 0x3f)) { |
377 | spin_unlock(lock: &hw->lock); |
378 | return IRQ_NONE; /* shared */ |
379 | } |
380 | hw->irqcnt++; |
381 | mISDNipac_irq(&hw->ipac, irqloops); |
382 | spin_unlock(lock: &hw->lock); |
383 | return IRQ_HANDLED; |
384 | } |
385 | |
386 | static void |
387 | enable_hwirq(struct inf_hw *hw) |
388 | { |
389 | u16 w; |
390 | u32 val; |
391 | |
392 | switch (hw->ci->typ) { |
393 | case INF_DIVA201: |
394 | case INF_DIVA202: |
395 | writel(PITA_INT0_ENABLE, addr: hw->cfg.p); |
396 | break; |
397 | case INF_SPEEDWIN: |
398 | case INF_SAPHIR3: |
399 | outb(TIGER_IRQ_BIT, port: (u32)hw->cfg.start + TIGER_AUX_IRQMASK); |
400 | break; |
401 | case INF_QS1000: |
402 | outb(QS1000_IRQ_ON, port: (u32)hw->cfg.start + ELSA_IRQ_ADDR); |
403 | break; |
404 | case INF_QS3000: |
405 | outb(QS3000_IRQ_ON, port: (u32)hw->cfg.start + ELSA_IRQ_ADDR); |
406 | break; |
407 | case INF_NICCY: |
408 | val = inl(port: (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); |
409 | val |= NICCY_IRQ_ENABLE; |
410 | outl(value: val, port: (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); |
411 | break; |
412 | case INF_SCT_1: |
413 | w = inw(port: (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); |
414 | w |= SCT_PLX_IRQ_ENABLE; |
415 | outw(value: w, port: (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); |
416 | break; |
417 | case INF_GAZEL_R685: |
418 | outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN, |
419 | port: (u32)hw->cfg.start + GAZEL_INCSR); |
420 | break; |
421 | case INF_GAZEL_R753: |
422 | outb(GAZEL_IPAC_EN + GAZEL_PCI_EN, |
423 | port: (u32)hw->cfg.start + GAZEL_INCSR); |
424 | break; |
425 | default: |
426 | break; |
427 | } |
428 | } |
429 | |
430 | static void |
431 | disable_hwirq(struct inf_hw *hw) |
432 | { |
433 | u16 w; |
434 | u32 val; |
435 | |
436 | switch (hw->ci->typ) { |
437 | case INF_DIVA201: |
438 | case INF_DIVA202: |
439 | writel(val: 0, addr: hw->cfg.p); |
440 | break; |
441 | case INF_SPEEDWIN: |
442 | case INF_SAPHIR3: |
443 | outb(value: 0, port: (u32)hw->cfg.start + TIGER_AUX_IRQMASK); |
444 | break; |
445 | case INF_QS1000: |
446 | outb(QS1000_IRQ_OFF, port: (u32)hw->cfg.start + ELSA_IRQ_ADDR); |
447 | break; |
448 | case INF_QS3000: |
449 | outb(QS3000_IRQ_OFF, port: (u32)hw->cfg.start + ELSA_IRQ_ADDR); |
450 | break; |
451 | case INF_NICCY: |
452 | val = inl(port: (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); |
453 | val &= NICCY_IRQ_DISABLE; |
454 | outl(value: val, port: (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); |
455 | break; |
456 | case INF_SCT_1: |
457 | w = inw(port: (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); |
458 | w &= (~SCT_PLX_IRQ_ENABLE); |
459 | outw(value: w, port: (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); |
460 | break; |
461 | case INF_GAZEL_R685: |
462 | case INF_GAZEL_R753: |
463 | outb(value: 0, port: (u32)hw->cfg.start + GAZEL_INCSR); |
464 | break; |
465 | default: |
466 | break; |
467 | } |
468 | } |
469 | |
470 | static void |
471 | ipac_chip_reset(struct inf_hw *hw) |
472 | { |
473 | hw->ipac.write_reg(hw, IPAC_POTA2, 0x20); |
474 | mdelay(5); |
475 | hw->ipac.write_reg(hw, IPAC_POTA2, 0x00); |
476 | mdelay(5); |
477 | hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf); |
478 | hw->ipac.write_reg(hw, IPAC_MASK, 0xc0); |
479 | } |
480 | |
481 | static void |
482 | reset_inf(struct inf_hw *hw) |
483 | { |
484 | u16 w; |
485 | u32 val; |
486 | |
487 | if (debug & DEBUG_HW) |
488 | pr_notice("%s: resetting card\n" , hw->name); |
489 | switch (hw->ci->typ) { |
490 | case INF_DIVA20: |
491 | case INF_DIVA20U: |
492 | outb(value: 0, port: (u32)hw->cfg.start + DIVA_PCI_CTRL); |
493 | mdelay(10); |
494 | outb(DIVA_RESET_BIT, port: (u32)hw->cfg.start + DIVA_PCI_CTRL); |
495 | mdelay(10); |
496 | /* Workaround PCI9060 */ |
497 | outb(value: 9, port: (u32)hw->cfg.start + 0x69); |
498 | outb(DIVA_RESET_BIT | DIVA_LED_A, |
499 | port: (u32)hw->cfg.start + DIVA_PCI_CTRL); |
500 | break; |
501 | case INF_DIVA201: |
502 | writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, |
503 | addr: hw->cfg.p + PITA_MISC_REG); |
504 | mdelay(1); |
505 | writel(PITA_PARA_MPX_MODE, addr: hw->cfg.p + PITA_MISC_REG); |
506 | mdelay(10); |
507 | break; |
508 | case INF_DIVA202: |
509 | writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, |
510 | addr: hw->cfg.p + PITA_MISC_REG); |
511 | mdelay(1); |
512 | writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET, |
513 | addr: hw->cfg.p + PITA_MISC_REG); |
514 | mdelay(10); |
515 | break; |
516 | case INF_SPEEDWIN: |
517 | case INF_SAPHIR3: |
518 | ipac_chip_reset(hw); |
519 | hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); |
520 | hw->ipac.write_reg(hw, IPAC_AOE, 0x00); |
521 | hw->ipac.write_reg(hw, IPAC_PCFG, 0x12); |
522 | break; |
523 | case INF_QS1000: |
524 | case INF_QS3000: |
525 | ipac_chip_reset(hw); |
526 | hw->ipac.write_reg(hw, IPAC_ACFG, 0x00); |
527 | hw->ipac.write_reg(hw, IPAC_AOE, 0x3c); |
528 | hw->ipac.write_reg(hw, IPAC_ATX, 0xff); |
529 | break; |
530 | case INF_NICCY: |
531 | break; |
532 | case INF_SCT_1: |
533 | w = inw(port: (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); |
534 | w &= (~SCT_PLX_RESET_BIT); |
535 | outw(value: w, port: (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); |
536 | mdelay(10); |
537 | w = inw(port: (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); |
538 | w |= SCT_PLX_RESET_BIT; |
539 | outw(value: w, port: (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); |
540 | mdelay(10); |
541 | break; |
542 | case INF_GAZEL_R685: |
543 | val = inl(port: (u32)hw->cfg.start + GAZEL_CNTRL); |
544 | val |= (GAZEL_RESET_9050 + GAZEL_RESET); |
545 | outl(value: val, port: (u32)hw->cfg.start + GAZEL_CNTRL); |
546 | val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); |
547 | mdelay(4); |
548 | outl(value: val, port: (u32)hw->cfg.start + GAZEL_CNTRL); |
549 | mdelay(10); |
550 | hw->ipac.isac.adf2 = 0x87; |
551 | hw->ipac.hscx[0].slot = 0x1f; |
552 | hw->ipac.hscx[1].slot = 0x23; |
553 | break; |
554 | case INF_GAZEL_R753: |
555 | val = inl(port: (u32)hw->cfg.start + GAZEL_CNTRL); |
556 | val |= (GAZEL_RESET_9050 + GAZEL_RESET); |
557 | outl(value: val, port: (u32)hw->cfg.start + GAZEL_CNTRL); |
558 | val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); |
559 | mdelay(4); |
560 | outl(value: val, port: (u32)hw->cfg.start + GAZEL_CNTRL); |
561 | mdelay(10); |
562 | ipac_chip_reset(hw); |
563 | hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); |
564 | hw->ipac.write_reg(hw, IPAC_AOE, 0x00); |
565 | hw->ipac.conf = 0x01; /* IOM off */ |
566 | break; |
567 | default: |
568 | return; |
569 | } |
570 | enable_hwirq(hw); |
571 | } |
572 | |
573 | static int |
574 | inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg) |
575 | { |
576 | int ret = 0; |
577 | |
578 | switch (cmd) { |
579 | case HW_RESET_REQ: |
580 | reset_inf(hw); |
581 | break; |
582 | default: |
583 | pr_info("%s: %s unknown command %x %lx\n" , |
584 | hw->name, __func__, cmd, arg); |
585 | ret = -EINVAL; |
586 | break; |
587 | } |
588 | return ret; |
589 | } |
590 | |
591 | static int |
592 | init_irq(struct inf_hw *hw) |
593 | { |
594 | int ret, cnt = 3; |
595 | u_long flags; |
596 | |
597 | if (!hw->ci->irqfunc) |
598 | return -EINVAL; |
599 | ret = request_irq(irq: hw->irq, handler: hw->ci->irqfunc, IRQF_SHARED, name: hw->name, dev: hw); |
600 | if (ret) { |
601 | pr_info("%s: couldn't get interrupt %d\n" , hw->name, hw->irq); |
602 | return ret; |
603 | } |
604 | while (cnt--) { |
605 | spin_lock_irqsave(&hw->lock, flags); |
606 | reset_inf(hw); |
607 | ret = hw->ipac.init(&hw->ipac); |
608 | if (ret) { |
609 | spin_unlock_irqrestore(lock: &hw->lock, flags); |
610 | pr_info("%s: ISAC init failed with %d\n" , |
611 | hw->name, ret); |
612 | break; |
613 | } |
614 | spin_unlock_irqrestore(lock: &hw->lock, flags); |
615 | msleep_interruptible(msecs: 10); |
616 | if (debug & DEBUG_HW) |
617 | pr_notice("%s: IRQ %d count %d\n" , hw->name, |
618 | hw->irq, hw->irqcnt); |
619 | if (!hw->irqcnt) { |
620 | pr_info("%s: IRQ(%d) got no requests during init %d\n" , |
621 | hw->name, hw->irq, 3 - cnt); |
622 | } else |
623 | return 0; |
624 | } |
625 | free_irq(hw->irq, hw); |
626 | return -EIO; |
627 | } |
628 | |
629 | static void |
630 | release_io(struct inf_hw *hw) |
631 | { |
632 | if (hw->cfg.mode) { |
633 | if (hw->cfg.mode == AM_MEMIO) { |
634 | release_mem_region(hw->cfg.start, hw->cfg.size); |
635 | if (hw->cfg.p) |
636 | iounmap(addr: hw->cfg.p); |
637 | } else |
638 | release_region(hw->cfg.start, hw->cfg.size); |
639 | hw->cfg.mode = AM_NONE; |
640 | } |
641 | if (hw->addr.mode) { |
642 | if (hw->addr.mode == AM_MEMIO) { |
643 | release_mem_region(hw->addr.start, hw->addr.size); |
644 | if (hw->addr.p) |
645 | iounmap(addr: hw->addr.p); |
646 | } else |
647 | release_region(hw->addr.start, hw->addr.size); |
648 | hw->addr.mode = AM_NONE; |
649 | } |
650 | } |
651 | |
652 | static int |
653 | setup_io(struct inf_hw *hw) |
654 | { |
655 | int err = 0; |
656 | |
657 | if (hw->ci->cfg_mode) { |
658 | hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar); |
659 | hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar); |
660 | if (hw->ci->cfg_mode == AM_MEMIO) { |
661 | if (!request_mem_region(hw->cfg.start, hw->cfg.size, |
662 | hw->name)) |
663 | err = -EBUSY; |
664 | } else { |
665 | if (!request_region(hw->cfg.start, hw->cfg.size, |
666 | hw->name)) |
667 | err = -EBUSY; |
668 | } |
669 | if (err) { |
670 | pr_info("mISDN: %s config port %lx (%lu bytes)" |
671 | "already in use\n" , hw->name, |
672 | (ulong)hw->cfg.start, (ulong)hw->cfg.size); |
673 | return err; |
674 | } |
675 | hw->cfg.mode = hw->ci->cfg_mode; |
676 | if (hw->ci->cfg_mode == AM_MEMIO) { |
677 | hw->cfg.p = ioremap(offset: hw->cfg.start, size: hw->cfg.size); |
678 | if (!hw->cfg.p) |
679 | return -ENOMEM; |
680 | } |
681 | if (debug & DEBUG_HW) |
682 | pr_notice("%s: IO cfg %lx (%lu bytes) mode%d\n" , |
683 | hw->name, (ulong)hw->cfg.start, |
684 | (ulong)hw->cfg.size, hw->ci->cfg_mode); |
685 | |
686 | } |
687 | if (hw->ci->addr_mode) { |
688 | hw->addr.start = pci_resource_start(hw->pdev, hw->ci->addr_bar); |
689 | hw->addr.size = pci_resource_len(hw->pdev, hw->ci->addr_bar); |
690 | if (hw->ci->addr_mode == AM_MEMIO) { |
691 | if (!request_mem_region(hw->addr.start, hw->addr.size, |
692 | hw->name)) |
693 | err = -EBUSY; |
694 | } else { |
695 | if (!request_region(hw->addr.start, hw->addr.size, |
696 | hw->name)) |
697 | err = -EBUSY; |
698 | } |
699 | if (err) { |
700 | pr_info("mISDN: %s address port %lx (%lu bytes)" |
701 | "already in use\n" , hw->name, |
702 | (ulong)hw->addr.start, (ulong)hw->addr.size); |
703 | return err; |
704 | } |
705 | hw->addr.mode = hw->ci->addr_mode; |
706 | if (hw->ci->addr_mode == AM_MEMIO) { |
707 | hw->addr.p = ioremap(offset: hw->addr.start, size: hw->addr.size); |
708 | if (!hw->addr.p) |
709 | return -ENOMEM; |
710 | } |
711 | if (debug & DEBUG_HW) |
712 | pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n" , |
713 | hw->name, (ulong)hw->addr.start, |
714 | (ulong)hw->addr.size, hw->ci->addr_mode); |
715 | |
716 | } |
717 | |
718 | switch (hw->ci->typ) { |
719 | case INF_DIVA20: |
720 | case INF_DIVA20U: |
721 | hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; |
722 | hw->isac.mode = hw->cfg.mode; |
723 | hw->isac.a.io.ale = (u32)hw->cfg.start + DIVA_ISAC_ALE; |
724 | hw->isac.a.io.port = (u32)hw->cfg.start + DIVA_ISAC_PORT; |
725 | hw->hscx.mode = hw->cfg.mode; |
726 | hw->hscx.a.io.ale = (u32)hw->cfg.start + DIVA_HSCX_ALE; |
727 | hw->hscx.a.io.port = (u32)hw->cfg.start + DIVA_HSCX_PORT; |
728 | break; |
729 | case INF_DIVA201: |
730 | hw->ipac.type = IPAC_TYPE_IPAC; |
731 | hw->ipac.isac.off = 0x80; |
732 | hw->isac.mode = hw->addr.mode; |
733 | hw->isac.a.p = hw->addr.p; |
734 | hw->hscx.mode = hw->addr.mode; |
735 | hw->hscx.a.p = hw->addr.p; |
736 | break; |
737 | case INF_DIVA202: |
738 | hw->ipac.type = IPAC_TYPE_IPACX; |
739 | hw->isac.mode = hw->addr.mode; |
740 | hw->isac.a.p = hw->addr.p; |
741 | hw->hscx.mode = hw->addr.mode; |
742 | hw->hscx.a.p = hw->addr.p; |
743 | break; |
744 | case INF_SPEEDWIN: |
745 | case INF_SAPHIR3: |
746 | hw->ipac.type = IPAC_TYPE_IPAC; |
747 | hw->ipac.isac.off = 0x80; |
748 | hw->isac.mode = hw->cfg.mode; |
749 | hw->isac.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE; |
750 | hw->isac.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT; |
751 | hw->hscx.mode = hw->cfg.mode; |
752 | hw->hscx.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE; |
753 | hw->hscx.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT; |
754 | outb(value: 0xff, port: (ulong)hw->cfg.start); |
755 | mdelay(1); |
756 | outb(value: 0x00, port: (ulong)hw->cfg.start); |
757 | mdelay(1); |
758 | outb(TIGER_IOMASK, port: (ulong)hw->cfg.start + TIGER_AUX_CTRL); |
759 | break; |
760 | case INF_QS1000: |
761 | case INF_QS3000: |
762 | hw->ipac.type = IPAC_TYPE_IPAC; |
763 | hw->ipac.isac.off = 0x80; |
764 | hw->isac.a.io.ale = (u32)hw->addr.start; |
765 | hw->isac.a.io.port = (u32)hw->addr.start + 1; |
766 | hw->isac.mode = hw->addr.mode; |
767 | hw->hscx.a.io.ale = (u32)hw->addr.start; |
768 | hw->hscx.a.io.port = (u32)hw->addr.start + 1; |
769 | hw->hscx.mode = hw->addr.mode; |
770 | break; |
771 | case INF_NICCY: |
772 | hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; |
773 | hw->isac.mode = hw->addr.mode; |
774 | hw->isac.a.io.ale = (u32)hw->addr.start + NICCY_ISAC_ALE; |
775 | hw->isac.a.io.port = (u32)hw->addr.start + NICCY_ISAC_PORT; |
776 | hw->hscx.mode = hw->addr.mode; |
777 | hw->hscx.a.io.ale = (u32)hw->addr.start + NICCY_HSCX_ALE; |
778 | hw->hscx.a.io.port = (u32)hw->addr.start + NICCY_HSCX_PORT; |
779 | break; |
780 | case INF_SCT_1: |
781 | hw->ipac.type = IPAC_TYPE_IPAC; |
782 | hw->ipac.isac.off = 0x80; |
783 | hw->isac.a.io.ale = (u32)hw->addr.start; |
784 | hw->isac.a.io.port = hw->isac.a.io.ale + 4; |
785 | hw->isac.mode = hw->addr.mode; |
786 | hw->hscx.a.io.ale = hw->isac.a.io.ale; |
787 | hw->hscx.a.io.port = hw->isac.a.io.port; |
788 | hw->hscx.mode = hw->addr.mode; |
789 | break; |
790 | case INF_SCT_2: |
791 | hw->ipac.type = IPAC_TYPE_IPAC; |
792 | hw->ipac.isac.off = 0x80; |
793 | hw->isac.a.io.ale = (u32)hw->addr.start + 0x08; |
794 | hw->isac.a.io.port = hw->isac.a.io.ale + 4; |
795 | hw->isac.mode = hw->addr.mode; |
796 | hw->hscx.a.io.ale = hw->isac.a.io.ale; |
797 | hw->hscx.a.io.port = hw->isac.a.io.port; |
798 | hw->hscx.mode = hw->addr.mode; |
799 | break; |
800 | case INF_SCT_3: |
801 | hw->ipac.type = IPAC_TYPE_IPAC; |
802 | hw->ipac.isac.off = 0x80; |
803 | hw->isac.a.io.ale = (u32)hw->addr.start + 0x10; |
804 | hw->isac.a.io.port = hw->isac.a.io.ale + 4; |
805 | hw->isac.mode = hw->addr.mode; |
806 | hw->hscx.a.io.ale = hw->isac.a.io.ale; |
807 | hw->hscx.a.io.port = hw->isac.a.io.port; |
808 | hw->hscx.mode = hw->addr.mode; |
809 | break; |
810 | case INF_SCT_4: |
811 | hw->ipac.type = IPAC_TYPE_IPAC; |
812 | hw->ipac.isac.off = 0x80; |
813 | hw->isac.a.io.ale = (u32)hw->addr.start + 0x20; |
814 | hw->isac.a.io.port = hw->isac.a.io.ale + 4; |
815 | hw->isac.mode = hw->addr.mode; |
816 | hw->hscx.a.io.ale = hw->isac.a.io.ale; |
817 | hw->hscx.a.io.port = hw->isac.a.io.port; |
818 | hw->hscx.mode = hw->addr.mode; |
819 | break; |
820 | case INF_GAZEL_R685: |
821 | hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; |
822 | hw->ipac.isac.off = 0x80; |
823 | hw->isac.mode = hw->addr.mode; |
824 | hw->isac.a.io.port = (u32)hw->addr.start; |
825 | hw->hscx.mode = hw->addr.mode; |
826 | hw->hscx.a.io.port = hw->isac.a.io.port; |
827 | break; |
828 | case INF_GAZEL_R753: |
829 | hw->ipac.type = IPAC_TYPE_IPAC; |
830 | hw->ipac.isac.off = 0x80; |
831 | hw->isac.mode = hw->addr.mode; |
832 | hw->isac.a.io.ale = (u32)hw->addr.start; |
833 | hw->isac.a.io.port = (u32)hw->addr.start + GAZEL_IPAC_DATA_PORT; |
834 | hw->hscx.mode = hw->addr.mode; |
835 | hw->hscx.a.io.ale = hw->isac.a.io.ale; |
836 | hw->hscx.a.io.port = hw->isac.a.io.port; |
837 | break; |
838 | default: |
839 | return -EINVAL; |
840 | } |
841 | switch (hw->isac.mode) { |
842 | case AM_MEMIO: |
843 | ASSIGN_FUNC_IPAC(MIO, hw->ipac); |
844 | break; |
845 | case AM_IND_IO: |
846 | ASSIGN_FUNC_IPAC(IND, hw->ipac); |
847 | break; |
848 | case AM_IO: |
849 | ASSIGN_FUNC_IPAC(IO, hw->ipac); |
850 | break; |
851 | default: |
852 | return -EINVAL; |
853 | } |
854 | return 0; |
855 | } |
856 | |
857 | static void |
858 | release_card(struct inf_hw *card) { |
859 | ulong flags; |
860 | int i; |
861 | |
862 | spin_lock_irqsave(&card->lock, flags); |
863 | disable_hwirq(hw: card); |
864 | spin_unlock_irqrestore(lock: &card->lock, flags); |
865 | card->ipac.isac.release(&card->ipac.isac); |
866 | free_irq(card->irq, card); |
867 | mISDN_unregister_device(&card->ipac.isac.dch.dev); |
868 | release_io(hw: card); |
869 | write_lock_irqsave(&card_lock, flags); |
870 | list_del(entry: &card->list); |
871 | write_unlock_irqrestore(&card_lock, flags); |
872 | switch (card->ci->typ) { |
873 | case INF_SCT_2: |
874 | case INF_SCT_3: |
875 | case INF_SCT_4: |
876 | break; |
877 | case INF_SCT_1: |
878 | for (i = 0; i < 3; i++) { |
879 | if (card->sc[i]) |
880 | release_card(card: card->sc[i]); |
881 | card->sc[i] = NULL; |
882 | } |
883 | fallthrough; |
884 | default: |
885 | pci_disable_device(dev: card->pdev); |
886 | pci_set_drvdata(pdev: card->pdev, NULL); |
887 | break; |
888 | } |
889 | kfree(objp: card); |
890 | inf_cnt--; |
891 | } |
892 | |
893 | static int |
894 | setup_instance(struct inf_hw *card) |
895 | { |
896 | int err; |
897 | ulong flags; |
898 | |
899 | snprintf(buf: card->name, MISDN_MAX_IDLEN - 1, fmt: "%s.%d" , card->ci->name, |
900 | inf_cnt + 1); |
901 | write_lock_irqsave(&card_lock, flags); |
902 | list_add_tail(new: &card->list, head: &Cards); |
903 | write_unlock_irqrestore(&card_lock, flags); |
904 | |
905 | _set_debug(card); |
906 | card->ipac.isac.name = card->name; |
907 | card->ipac.name = card->name; |
908 | card->ipac.owner = THIS_MODULE; |
909 | spin_lock_init(&card->lock); |
910 | card->ipac.isac.hwlock = &card->lock; |
911 | card->ipac.hwlock = &card->lock; |
912 | card->ipac.ctrl = (void *)&inf_ctrl; |
913 | |
914 | err = setup_io(card); |
915 | if (err) |
916 | goto error_setup; |
917 | |
918 | card->ipac.isac.dch.dev.Bprotocols = |
919 | mISDNipac_init(&card->ipac, card); |
920 | |
921 | if (card->ipac.isac.dch.dev.Bprotocols == 0) |
922 | goto error_setup; |
923 | |
924 | err = mISDN_register_device(&card->ipac.isac.dch.dev, |
925 | parent: &card->pdev->dev, name: card->name); |
926 | if (err) |
927 | goto error; |
928 | |
929 | err = init_irq(hw: card); |
930 | if (!err) { |
931 | inf_cnt++; |
932 | pr_notice("Infineon %d cards installed\n" , inf_cnt); |
933 | return 0; |
934 | } |
935 | mISDN_unregister_device(&card->ipac.isac.dch.dev); |
936 | error: |
937 | card->ipac.release(&card->ipac); |
938 | error_setup: |
939 | release_io(hw: card); |
940 | write_lock_irqsave(&card_lock, flags); |
941 | list_del(entry: &card->list); |
942 | write_unlock_irqrestore(&card_lock, flags); |
943 | return err; |
944 | } |
945 | |
946 | static const struct inf_cinfo inf_card_info[] = { |
947 | { |
948 | INF_DIVA20, |
949 | "Dialogic Diva 2.0" , |
950 | "diva20" , |
951 | AM_IND_IO, AM_NONE, 2, 0, |
952 | &diva_irq |
953 | }, |
954 | { |
955 | INF_DIVA20U, |
956 | "Dialogic Diva 2.0U" , |
957 | "diva20U" , |
958 | AM_IND_IO, AM_NONE, 2, 0, |
959 | &diva_irq |
960 | }, |
961 | { |
962 | INF_DIVA201, |
963 | "Dialogic Diva 2.01" , |
964 | "diva201" , |
965 | AM_MEMIO, AM_MEMIO, 0, 1, |
966 | &diva20x_irq |
967 | }, |
968 | { |
969 | INF_DIVA202, |
970 | "Dialogic Diva 2.02" , |
971 | "diva202" , |
972 | AM_MEMIO, AM_MEMIO, 0, 1, |
973 | &diva20x_irq |
974 | }, |
975 | { |
976 | INF_SPEEDWIN, |
977 | "Sedlbauer SpeedWin PCI" , |
978 | "speedwin" , |
979 | AM_IND_IO, AM_NONE, 0, 0, |
980 | &tiger_irq |
981 | }, |
982 | { |
983 | INF_SAPHIR3, |
984 | "HST Saphir 3" , |
985 | "saphir" , |
986 | AM_IND_IO, AM_NONE, 0, 0, |
987 | &tiger_irq |
988 | }, |
989 | { |
990 | INF_QS1000, |
991 | "Develo Microlink PCI" , |
992 | "qs1000" , |
993 | AM_IO, AM_IND_IO, 1, 3, |
994 | &elsa_irq |
995 | }, |
996 | { |
997 | INF_QS3000, |
998 | "Develo QuickStep 3000" , |
999 | "qs3000" , |
1000 | AM_IO, AM_IND_IO, 1, 3, |
1001 | &elsa_irq |
1002 | }, |
1003 | { |
1004 | INF_NICCY, |
1005 | "Sagem NICCY" , |
1006 | "niccy" , |
1007 | AM_IO, AM_IND_IO, 0, 1, |
1008 | &niccy_irq |
1009 | }, |
1010 | { |
1011 | INF_SCT_1, |
1012 | "SciTel Quadro" , |
1013 | "p1_scitel" , |
1014 | AM_IO, AM_IND_IO, 1, 5, |
1015 | &ipac_irq |
1016 | }, |
1017 | { |
1018 | INF_SCT_2, |
1019 | "SciTel Quadro" , |
1020 | "p2_scitel" , |
1021 | AM_NONE, AM_IND_IO, 0, 4, |
1022 | &ipac_irq |
1023 | }, |
1024 | { |
1025 | INF_SCT_3, |
1026 | "SciTel Quadro" , |
1027 | "p3_scitel" , |
1028 | AM_NONE, AM_IND_IO, 0, 3, |
1029 | &ipac_irq |
1030 | }, |
1031 | { |
1032 | INF_SCT_4, |
1033 | "SciTel Quadro" , |
1034 | "p4_scitel" , |
1035 | AM_NONE, AM_IND_IO, 0, 2, |
1036 | &ipac_irq |
1037 | }, |
1038 | { |
1039 | INF_GAZEL_R685, |
1040 | "Gazel R685" , |
1041 | "gazel685" , |
1042 | AM_IO, AM_IO, 1, 2, |
1043 | &gazel_irq |
1044 | }, |
1045 | { |
1046 | INF_GAZEL_R753, |
1047 | "Gazel R753" , |
1048 | "gazel753" , |
1049 | AM_IO, AM_IND_IO, 1, 2, |
1050 | &ipac_irq |
1051 | }, |
1052 | { |
1053 | INF_NONE, |
1054 | } |
1055 | }; |
1056 | |
1057 | static const struct inf_cinfo * |
1058 | get_card_info(enum inf_types typ) |
1059 | { |
1060 | const struct inf_cinfo *ci = inf_card_info; |
1061 | |
1062 | while (ci->typ != INF_NONE) { |
1063 | if (ci->typ == typ) |
1064 | return ci; |
1065 | ci++; |
1066 | } |
1067 | return NULL; |
1068 | } |
1069 | |
1070 | static int |
1071 | inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
1072 | { |
1073 | int err = -ENOMEM; |
1074 | struct inf_hw *card; |
1075 | |
1076 | card = kzalloc(size: sizeof(struct inf_hw), GFP_KERNEL); |
1077 | if (!card) { |
1078 | pr_info("No memory for Infineon ISDN card\n" ); |
1079 | return err; |
1080 | } |
1081 | card->pdev = pdev; |
1082 | err = pci_enable_device(dev: pdev); |
1083 | if (err) { |
1084 | kfree(objp: card); |
1085 | return err; |
1086 | } |
1087 | card->ci = get_card_info(typ: ent->driver_data); |
1088 | if (!card->ci) { |
1089 | pr_info("mISDN: do not have information about adapter at %s\n" , |
1090 | pci_name(pdev)); |
1091 | kfree(objp: card); |
1092 | pci_disable_device(dev: pdev); |
1093 | return -EINVAL; |
1094 | } else |
1095 | pr_notice("mISDN: found adapter %s at %s\n" , |
1096 | card->ci->full, pci_name(pdev)); |
1097 | |
1098 | card->irq = pdev->irq; |
1099 | pci_set_drvdata(pdev, data: card); |
1100 | err = setup_instance(card); |
1101 | if (err) { |
1102 | pci_disable_device(dev: pdev); |
1103 | kfree(objp: card); |
1104 | pci_set_drvdata(pdev, NULL); |
1105 | } else if (ent->driver_data == INF_SCT_1) { |
1106 | int i; |
1107 | struct inf_hw *sc; |
1108 | |
1109 | for (i = 1; i < 4; i++) { |
1110 | sc = kzalloc(size: sizeof(struct inf_hw), GFP_KERNEL); |
1111 | if (!sc) { |
1112 | release_card(card); |
1113 | pci_disable_device(dev: pdev); |
1114 | return -ENOMEM; |
1115 | } |
1116 | sc->irq = card->irq; |
1117 | sc->pdev = card->pdev; |
1118 | sc->ci = card->ci + i; |
1119 | err = setup_instance(sc); |
1120 | if (err) { |
1121 | pci_disable_device(dev: pdev); |
1122 | kfree(objp: sc); |
1123 | release_card(card); |
1124 | break; |
1125 | } else |
1126 | card->sc[i - 1] = sc; |
1127 | } |
1128 | } |
1129 | return err; |
1130 | } |
1131 | |
1132 | static void |
1133 | inf_remove(struct pci_dev *pdev) |
1134 | { |
1135 | struct inf_hw *card = pci_get_drvdata(pdev); |
1136 | |
1137 | if (card) |
1138 | release_card(card); |
1139 | else |
1140 | pr_debug("%s: drvdata already removed\n" , __func__); |
1141 | } |
1142 | |
1143 | static struct pci_driver infineon_driver = { |
1144 | .name = "ISDN Infineon pci" , |
1145 | .probe = inf_probe, |
1146 | .remove = inf_remove, |
1147 | .id_table = infineon_ids, |
1148 | }; |
1149 | |
1150 | static int __init |
1151 | infineon_init(void) |
1152 | { |
1153 | int err; |
1154 | |
1155 | pr_notice("Infineon ISDN Driver Rev. %s\n" , INFINEON_REV); |
1156 | err = pci_register_driver(&infineon_driver); |
1157 | return err; |
1158 | } |
1159 | |
1160 | static void __exit |
1161 | infineon_cleanup(void) |
1162 | { |
1163 | pci_unregister_driver(dev: &infineon_driver); |
1164 | } |
1165 | |
1166 | module_init(infineon_init); |
1167 | module_exit(infineon_cleanup); |
1168 | |