]>
Commit | Line | Data |
---|---|---|
da2272c9 KK |
1 | /* |
2 | * speedfax.c low level stuff for Sedlbauer Speedfax+ cards | |
3 | * based on the ISAR DSP | |
4 | * Thanks to Sedlbauer AG for informations and HW | |
5 | * | |
6 | * Author Karsten Keil <keil@isdn4linux.de> | |
7 | * | |
8 | * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | * | |
23 | */ | |
24 | ||
25 | #include <linux/module.h> | |
5a0e3ad6 | 26 | #include <linux/slab.h> |
da2272c9 KK |
27 | #include <linux/pci.h> |
28 | #include <linux/delay.h> | |
29 | #include <linux/mISDNhw.h> | |
30 | #include <linux/firmware.h> | |
31 | #include "ipac.h" | |
32 | #include "isar.h" | |
33 | ||
34 | #define SPEEDFAX_REV "2.0" | |
35 | ||
36 | #define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51 | |
37 | #define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54 | |
38 | #define PCI_SUB_ID_SEDLBAUER 0x01 | |
39 | ||
40 | #define SFAX_PCI_ADDR 0xc8 | |
41 | #define SFAX_PCI_ISAC 0xd0 | |
42 | #define SFAX_PCI_ISAR 0xe0 | |
43 | ||
44 | /* TIGER 100 Registers */ | |
45 | ||
46 | #define TIGER_RESET_ADDR 0x00 | |
47 | #define TIGER_EXTERN_RESET_ON 0x01 | |
48 | #define TIGER_EXTERN_RESET_OFF 0x00 | |
49 | #define TIGER_AUX_CTRL 0x02 | |
50 | #define TIGER_AUX_DATA 0x03 | |
51 | #define TIGER_AUX_IRQMASK 0x05 | |
52 | #define TIGER_AUX_STATUS 0x07 | |
53 | ||
54 | /* Tiger AUX BITs */ | |
55 | #define SFAX_AUX_IOMASK 0xdd /* 1 and 5 are inputs */ | |
56 | #define SFAX_ISAR_RESET_BIT_OFF 0x00 | |
57 | #define SFAX_ISAR_RESET_BIT_ON 0x01 | |
58 | #define SFAX_TIGER_IRQ_BIT 0x02 | |
59 | #define SFAX_LED1_BIT 0x08 | |
60 | #define SFAX_LED2_BIT 0x10 | |
61 | ||
62 | #define SFAX_PCI_RESET_ON (SFAX_ISAR_RESET_BIT_ON) | |
63 | #define SFAX_PCI_RESET_OFF (SFAX_LED1_BIT | SFAX_LED2_BIT) | |
64 | ||
65 | static int sfax_cnt; | |
66 | static u32 debug; | |
67 | static u32 irqloops = 4; | |
68 | ||
69 | struct sfax_hw { | |
70 | struct list_head list; | |
71 | struct pci_dev *pdev; | |
72 | char name[MISDN_MAX_IDLEN]; | |
73 | u32 irq; | |
74 | u32 irqcnt; | |
75 | u32 cfg; | |
76 | struct _ioport p_isac; | |
77 | struct _ioport p_isar; | |
78 | u8 aux_data; | |
79 | spinlock_t lock; /* HW access lock */ | |
80 | struct isac_hw isac; | |
81 | struct isar_hw isar; | |
82 | }; | |
83 | ||
84 | static LIST_HEAD(Cards); | |
85 | static DEFINE_RWLOCK(card_lock); /* protect Cards */ | |
86 | ||
87 | static void | |
88 | _set_debug(struct sfax_hw *card) | |
89 | { | |
90 | card->isac.dch.debug = debug; | |
91 | card->isar.ch[0].bch.debug = debug; | |
92 | card->isar.ch[1].bch.debug = debug; | |
93 | } | |
94 | ||
95 | static int | |
96 | set_debug(const char *val, struct kernel_param *kp) | |
97 | { | |
98 | int ret; | |
99 | struct sfax_hw *card; | |
100 | ||
101 | ret = param_set_uint(val, kp); | |
102 | if (!ret) { | |
103 | read_lock(&card_lock); | |
104 | list_for_each_entry(card, &Cards, list) | |
105 | _set_debug(card); | |
106 | read_unlock(&card_lock); | |
107 | } | |
108 | return ret; | |
109 | } | |
110 | ||
111 | MODULE_AUTHOR("Karsten Keil"); | |
112 | MODULE_LICENSE("GPL v2"); | |
113 | MODULE_VERSION(SPEEDFAX_REV); | |
4a9b5e50 | 114 | MODULE_FIRMWARE("isdn/ISAR.BIN"); |
da2272c9 KK |
115 | module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); |
116 | MODULE_PARM_DESC(debug, "Speedfax debug mask"); | |
117 | module_param(irqloops, uint, S_IRUGO | S_IWUSR); | |
118 | MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)"); | |
119 | ||
120 | IOFUNC_IND(ISAC, sfax_hw, p_isac) | |
121 | IOFUNC_IND(ISAR, sfax_hw, p_isar) | |
122 | ||
123 | static irqreturn_t | |
124 | speedfax_irq(int intno, void *dev_id) | |
125 | { | |
126 | struct sfax_hw *sf = dev_id; | |
127 | u8 val; | |
128 | int cnt = irqloops; | |
129 | ||
130 | spin_lock(&sf->lock); | |
131 | val = inb(sf->cfg + TIGER_AUX_STATUS); | |
132 | if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */ | |
133 | spin_unlock(&sf->lock); | |
134 | return IRQ_NONE; /* shared */ | |
135 | } | |
136 | sf->irqcnt++; | |
137 | val = ReadISAR_IND(sf, ISAR_IRQBIT); | |
138 | Start_ISAR: | |
139 | if (val & ISAR_IRQSTA) | |
140 | mISDNisar_irq(&sf->isar); | |
141 | val = ReadISAC_IND(sf, ISAC_ISTA); | |
142 | if (val) | |
143 | mISDNisac_irq(&sf->isac, val); | |
144 | val = ReadISAR_IND(sf, ISAR_IRQBIT); | |
145 | if ((val & ISAR_IRQSTA) && cnt--) | |
146 | goto Start_ISAR; | |
147 | if (cnt < irqloops) | |
148 | pr_debug("%s: %d irqloops cpu%d\n", sf->name, | |
149 | irqloops - cnt, smp_processor_id()); | |
150 | if (irqloops && !cnt) | |
151 | pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name, | |
152 | irqloops, smp_processor_id()); | |
153 | spin_unlock(&sf->lock); | |
154 | return IRQ_HANDLED; | |
155 | } | |
156 | ||
157 | static void | |
158 | enable_hwirq(struct sfax_hw *sf) | |
159 | { | |
160 | WriteISAC_IND(sf, ISAC_MASK, 0); | |
161 | WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK); | |
162 | outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK); | |
163 | } | |
164 | ||
165 | static void | |
166 | disable_hwirq(struct sfax_hw *sf) | |
167 | { | |
168 | WriteISAC_IND(sf, ISAC_MASK, 0xFF); | |
169 | WriteISAR_IND(sf, ISAR_IRQBIT, 0); | |
170 | outb(0, sf->cfg + TIGER_AUX_IRQMASK); | |
171 | } | |
172 | ||
173 | static void | |
174 | reset_speedfax(struct sfax_hw *sf) | |
175 | { | |
176 | ||
177 | pr_debug("%s: resetting card\n", sf->name); | |
178 | outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR); | |
179 | outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA); | |
180 | mdelay(1); | |
181 | outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR); | |
182 | sf->aux_data = SFAX_PCI_RESET_OFF; | |
183 | outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); | |
184 | mdelay(1); | |
185 | } | |
186 | ||
187 | static int | |
188 | sfax_ctrl(struct sfax_hw *sf, u32 cmd, u_long arg) | |
189 | { | |
190 | int ret = 0; | |
191 | ||
192 | switch (cmd) { | |
193 | case HW_RESET_REQ: | |
194 | reset_speedfax(sf); | |
195 | break; | |
196 | case HW_ACTIVATE_IND: | |
197 | if (arg & 1) | |
198 | sf->aux_data &= ~SFAX_LED1_BIT; | |
199 | if (arg & 2) | |
200 | sf->aux_data &= ~SFAX_LED2_BIT; | |
201 | outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); | |
202 | break; | |
203 | case HW_DEACT_IND: | |
204 | if (arg & 1) | |
205 | sf->aux_data |= SFAX_LED1_BIT; | |
206 | if (arg & 2) | |
207 | sf->aux_data |= SFAX_LED2_BIT; | |
208 | outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); | |
209 | break; | |
210 | default: | |
211 | pr_info("%s: %s unknown command %x %lx\n", | |
212 | sf->name, __func__, cmd, arg); | |
213 | ret = -EINVAL; | |
214 | break; | |
215 | } | |
216 | return ret; | |
217 | } | |
218 | ||
219 | static int | |
220 | channel_ctrl(struct sfax_hw *sf, struct mISDN_ctrl_req *cq) | |
221 | { | |
222 | int ret = 0; | |
223 | ||
224 | switch (cq->op) { | |
225 | case MISDN_CTRL_GETOP: | |
226 | cq->op = MISDN_CTRL_LOOP; | |
227 | break; | |
228 | case MISDN_CTRL_LOOP: | |
229 | /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ | |
230 | if (cq->channel < 0 || cq->channel > 3) { | |
231 | ret = -EINVAL; | |
232 | break; | |
233 | } | |
234 | ret = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel); | |
235 | break; | |
236 | default: | |
237 | pr_info("%s: unknown Op %x\n", sf->name, cq->op); | |
238 | ret = -EINVAL; | |
239 | break; | |
240 | } | |
241 | return ret; | |
242 | } | |
243 | ||
244 | static int | |
245 | sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) | |
246 | { | |
247 | struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); | |
248 | struct dchannel *dch = container_of(dev, struct dchannel, dev); | |
249 | struct sfax_hw *sf = dch->hw; | |
250 | struct channel_req *rq; | |
251 | int err = 0; | |
252 | ||
253 | pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg); | |
254 | switch (cmd) { | |
255 | case OPEN_CHANNEL: | |
256 | rq = arg; | |
257 | if (rq->protocol == ISDN_P_TE_S0) | |
258 | err = sf->isac.open(&sf->isac, rq); | |
259 | else | |
260 | err = sf->isar.open(&sf->isar, rq); | |
261 | if (err) | |
262 | break; | |
263 | if (!try_module_get(THIS_MODULE)) | |
264 | pr_info("%s: cannot get module\n", sf->name); | |
265 | break; | |
266 | case CLOSE_CHANNEL: | |
267 | pr_debug("%s: dev(%d) close from %p\n", sf->name, | |
268 | dch->dev.id, __builtin_return_address(0)); | |
269 | module_put(THIS_MODULE); | |
270 | break; | |
271 | case CONTROL_CHANNEL: | |
272 | err = channel_ctrl(sf, arg); | |
273 | break; | |
274 | default: | |
275 | pr_debug("%s: unknown command %x\n", sf->name, cmd); | |
276 | return -EINVAL; | |
277 | } | |
278 | return err; | |
279 | } | |
280 | ||
281 | static int __devinit | |
282 | init_card(struct sfax_hw *sf) | |
283 | { | |
284 | int ret, cnt = 3; | |
285 | u_long flags; | |
286 | ||
287 | ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf); | |
288 | if (ret) { | |
289 | pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq); | |
290 | return ret; | |
291 | } | |
292 | while (cnt--) { | |
293 | spin_lock_irqsave(&sf->lock, flags); | |
294 | ret = sf->isac.init(&sf->isac); | |
295 | if (ret) { | |
296 | spin_unlock_irqrestore(&sf->lock, flags); | |
297 | pr_info("%s: ISAC init failed with %d\n", | |
298 | sf->name, ret); | |
299 | break; | |
300 | } | |
301 | enable_hwirq(sf); | |
302 | /* RESET Receiver and Transmitter */ | |
303 | WriteISAC_IND(sf, ISAC_CMDR, 0x41); | |
304 | spin_unlock_irqrestore(&sf->lock, flags); | |
305 | msleep_interruptible(10); | |
306 | if (debug & DEBUG_HW) | |
307 | pr_notice("%s: IRQ %d count %d\n", sf->name, | |
308 | sf->irq, sf->irqcnt); | |
309 | if (!sf->irqcnt) { | |
310 | pr_info("%s: IRQ(%d) got no requests during init %d\n", | |
311 | sf->name, sf->irq, 3 - cnt); | |
312 | } else | |
313 | return 0; | |
314 | } | |
315 | free_irq(sf->irq, sf); | |
316 | return -EIO; | |
317 | } | |
318 | ||
319 | ||
320 | static int __devinit | |
321 | setup_speedfax(struct sfax_hw *sf) | |
322 | { | |
323 | u_long flags; | |
324 | ||
325 | if (!request_region(sf->cfg, 256, sf->name)) { | |
326 | pr_info("mISDN: %s config port %x-%x already in use\n", | |
327 | sf->name, sf->cfg, sf->cfg + 255); | |
328 | return -EIO; | |
329 | } | |
330 | outb(0xff, sf->cfg); | |
331 | outb(0, sf->cfg); | |
332 | outb(0xdd, sf->cfg + TIGER_AUX_CTRL); | |
333 | outb(0, sf->cfg + TIGER_AUX_IRQMASK); | |
334 | ||
335 | sf->isac.type = IPAC_TYPE_ISAC; | |
336 | sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR; | |
337 | sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC; | |
338 | sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR; | |
339 | sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR; | |
340 | ASSIGN_FUNC(IND, ISAC, sf->isac); | |
341 | ASSIGN_FUNC(IND, ISAR, sf->isar); | |
342 | spin_lock_irqsave(&sf->lock, flags); | |
343 | reset_speedfax(sf); | |
344 | disable_hwirq(sf); | |
345 | spin_unlock_irqrestore(&sf->lock, flags); | |
346 | return 0; | |
347 | } | |
348 | ||
349 | static void | |
350 | release_card(struct sfax_hw *card) { | |
351 | u_long flags; | |
352 | ||
353 | spin_lock_irqsave(&card->lock, flags); | |
354 | disable_hwirq(card); | |
355 | spin_unlock_irqrestore(&card->lock, flags); | |
356 | card->isac.release(&card->isac); | |
357 | free_irq(card->irq, card); | |
358 | card->isar.release(&card->isar); | |
359 | mISDN_unregister_device(&card->isac.dch.dev); | |
360 | release_region(card->cfg, 256); | |
361 | pci_disable_device(card->pdev); | |
362 | pci_set_drvdata(card->pdev, NULL); | |
363 | write_lock_irqsave(&card_lock, flags); | |
364 | list_del(&card->list); | |
365 | write_unlock_irqrestore(&card_lock, flags); | |
366 | kfree(card); | |
367 | sfax_cnt--; | |
368 | } | |
369 | ||
370 | static int __devinit | |
371 | setup_instance(struct sfax_hw *card) | |
372 | { | |
373 | const struct firmware *firmware; | |
374 | int i, err; | |
375 | u_long flags; | |
376 | ||
377 | snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1); | |
378 | write_lock_irqsave(&card_lock, flags); | |
379 | list_add_tail(&card->list, &Cards); | |
380 | write_unlock_irqrestore(&card_lock, flags); | |
381 | _set_debug(card); | |
382 | spin_lock_init(&card->lock); | |
383 | card->isac.hwlock = &card->lock; | |
384 | card->isar.hwlock = &card->lock; | |
385 | card->isar.ctrl = (void *)&sfax_ctrl; | |
386 | card->isac.name = card->name; | |
387 | card->isar.name = card->name; | |
388 | card->isar.owner = THIS_MODULE; | |
389 | ||
390 | err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev); | |
391 | if (err < 0) { | |
392 | pr_info("%s: firmware request failed %d\n", | |
393 | card->name, err); | |
394 | goto error_fw; | |
395 | } | |
396 | if (debug & DEBUG_HW) | |
397 | pr_notice("%s: got firmware %zu bytes\n", | |
398 | card->name, firmware->size); | |
399 | ||
400 | mISDNisac_init(&card->isac, card); | |
401 | ||
402 | card->isac.dch.dev.D.ctrl = sfax_dctrl; | |
403 | card->isac.dch.dev.Bprotocols = | |
404 | mISDNisar_init(&card->isar, card); | |
405 | for (i = 0; i < 2; i++) { | |
406 | set_channelmap(i + 1, card->isac.dch.dev.channelmap); | |
407 | list_add(&card->isar.ch[i].bch.ch.list, | |
408 | &card->isac.dch.dev.bchannels); | |
409 | } | |
410 | ||
411 | err = setup_speedfax(card); | |
412 | if (err) | |
413 | goto error_setup; | |
414 | err = card->isar.init(&card->isar); | |
415 | if (err) | |
416 | goto error; | |
417 | err = mISDN_register_device(&card->isac.dch.dev, | |
418 | &card->pdev->dev, card->name); | |
419 | if (err) | |
420 | goto error; | |
421 | err = init_card(card); | |
422 | if (err) | |
423 | goto error_init; | |
424 | err = card->isar.firmware(&card->isar, firmware->data, firmware->size); | |
425 | if (!err) { | |
426 | release_firmware(firmware); | |
427 | sfax_cnt++; | |
428 | pr_notice("SpeedFax %d cards installed\n", sfax_cnt); | |
429 | return 0; | |
430 | } | |
431 | disable_hwirq(card); | |
432 | free_irq(card->irq, card); | |
433 | error_init: | |
434 | mISDN_unregister_device(&card->isac.dch.dev); | |
435 | error: | |
436 | release_region(card->cfg, 256); | |
437 | error_setup: | |
438 | card->isac.release(&card->isac); | |
439 | card->isar.release(&card->isar); | |
440 | release_firmware(firmware); | |
441 | error_fw: | |
442 | pci_disable_device(card->pdev); | |
443 | write_lock_irqsave(&card_lock, flags); | |
444 | list_del(&card->list); | |
445 | write_unlock_irqrestore(&card_lock, flags); | |
446 | kfree(card); | |
447 | return err; | |
448 | } | |
449 | ||
450 | static int __devinit | |
451 | sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |
452 | { | |
453 | int err = -ENOMEM; | |
454 | struct sfax_hw *card = kzalloc(sizeof(struct sfax_hw), GFP_KERNEL); | |
455 | ||
456 | if (!card) { | |
457 | pr_info("No memory for Speedfax+ PCI\n"); | |
458 | return err; | |
459 | } | |
460 | card->pdev = pdev; | |
461 | err = pci_enable_device(pdev); | |
462 | if (err) { | |
463 | kfree(card); | |
464 | return err; | |
465 | } | |
466 | ||
467 | pr_notice("mISDN: Speedfax found adapter %s at %s\n", | |
468 | (char *)ent->driver_data, pci_name(pdev)); | |
469 | ||
470 | card->cfg = pci_resource_start(pdev, 0); | |
471 | card->irq = pdev->irq; | |
472 | pci_set_drvdata(pdev, card); | |
473 | err = setup_instance(card); | |
474 | if (err) | |
475 | pci_set_drvdata(pdev, NULL); | |
476 | return err; | |
477 | } | |
478 | ||
479 | static void __devexit | |
480 | sfax_remove_pci(struct pci_dev *pdev) | |
481 | { | |
482 | struct sfax_hw *card = pci_get_drvdata(pdev); | |
483 | ||
484 | if (card) | |
485 | release_card(card); | |
486 | else | |
487 | pr_debug("%s: drvdata allready removed\n", __func__); | |
488 | } | |
489 | ||
490 | static struct pci_device_id sfaxpci_ids[] __devinitdata = { | |
491 | { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, | |
492 | PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER, | |
493 | 0, 0, (unsigned long) "Pyramid Speedfax + PCI" | |
494 | }, | |
495 | { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, | |
496 | PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER, | |
497 | 0, 0, (unsigned long) "Sedlbauer Speedfax + PCI" | |
498 | }, | |
499 | { } | |
500 | }; | |
501 | MODULE_DEVICE_TABLE(pci, sfaxpci_ids); | |
502 | ||
503 | static struct pci_driver sfaxpci_driver = { | |
504 | .name = "speedfax+ pci", | |
505 | .probe = sfaxpci_probe, | |
506 | .remove = __devexit_p(sfax_remove_pci), | |
507 | .id_table = sfaxpci_ids, | |
508 | }; | |
509 | ||
510 | static int __init | |
511 | Speedfax_init(void) | |
512 | { | |
513 | int err; | |
514 | ||
515 | pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n", | |
516 | SPEEDFAX_REV); | |
517 | err = pci_register_driver(&sfaxpci_driver); | |
518 | return err; | |
519 | } | |
520 | ||
521 | static void __exit | |
522 | Speedfax_cleanup(void) | |
523 | { | |
524 | pci_unregister_driver(&sfaxpci_driver); | |
525 | } | |
526 | ||
527 | module_init(Speedfax_init); | |
528 | module_exit(Speedfax_cleanup); |