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