diff options
author | Karsten Keil <keil@b1-systems.de> | 2009-07-22 14:06:05 -0400 |
---|---|---|
committer | Karsten Keil <keil@b1-systems.de> | 2009-07-25 14:21:28 -0400 |
commit | 707b2ce6c1f4f1261788f2ff09ad82c35e0e6240 (patch) | |
tree | 3e05c62c75da21a51a716d4013b58c6cfea91329 /drivers/isdn | |
parent | da2272c91ae81b41ae6fa6ebdc767a6cef73b770 (diff) |
mISDN: Add driver for Winbond cards
Add driver for Winbond W6692 based PCI cards.
Signed-off-by: Karsten Keil <keil@b1-systems.de>
Diffstat (limited to 'drivers/isdn')
-rw-r--r-- | drivers/isdn/hardware/mISDN/Kconfig | 7 | ||||
-rw-r--r-- | drivers/isdn/hardware/mISDN/Makefile | 1 | ||||
-rw-r--r-- | drivers/isdn/hardware/mISDN/w6692.c | 1440 | ||||
-rw-r--r-- | drivers/isdn/hardware/mISDN/w6692.h | 190 |
4 files changed, 1638 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig index 2600534be07e..d72586e68383 100644 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ b/drivers/isdn/hardware/mISDN/Kconfig | |||
@@ -65,6 +65,13 @@ config MISDN_INFINEON | |||
65 | Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX | 65 | Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX |
66 | chip from Infineon (former manufacturer Siemens). | 66 | chip from Infineon (former manufacturer Siemens). |
67 | 67 | ||
68 | config MISDN_W6692 | ||
69 | tristate "Support for cards with Winbond 6692" | ||
70 | depends on MISDN | ||
71 | depends on PCI | ||
72 | help | ||
73 | Enable support for Winbond 6692 PCI chip based cards. | ||
74 | |||
68 | 75 | ||
69 | config MISDN_IPAC | 76 | config MISDN_IPAC |
70 | tristate | 77 | tristate |
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile index e18f964e185b..61dd5a5f300b 100644 --- a/drivers/isdn/hardware/mISDN/Makefile +++ b/drivers/isdn/hardware/mISDN/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o | |||
9 | obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o | 9 | obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o |
10 | obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o | 10 | obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o |
11 | obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o | 11 | obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o |
12 | obj-$(CONFIG_MISDN_W6692) += w6692.o | ||
12 | # chip modules | 13 | # chip modules |
13 | obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o | 14 | obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o |
14 | obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o | 15 | obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o |
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c new file mode 100644 index 000000000000..1b9008f13d6a --- /dev/null +++ b/drivers/isdn/hardware/mISDN/w6692.c | |||
@@ -0,0 +1,1440 @@ | |||
1 | /* | ||
2 | * w6692.c mISDN driver for Winbond w6692 based cards | ||
3 | * | ||
4 | * Author Karsten Keil <kkeil@suse.de> | ||
5 | * based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz> | ||
6 | * | ||
7 | * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/pci.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/mISDNhw.h> | ||
28 | #include "w6692.h" | ||
29 | |||
30 | #define W6692_REV "2.0" | ||
31 | |||
32 | #define DBUSY_TIMER_VALUE 80 | ||
33 | |||
34 | enum { | ||
35 | W6692_ASUS, | ||
36 | W6692_WINBOND, | ||
37 | W6692_USR | ||
38 | }; | ||
39 | |||
40 | /* private data in the PCI devices list */ | ||
41 | struct w6692map { | ||
42 | u_int subtype; | ||
43 | char *name; | ||
44 | }; | ||
45 | |||
46 | static const struct w6692map w6692_map[] = | ||
47 | { | ||
48 | {W6692_ASUS, "Dynalink/AsusCom IS64PH"}, | ||
49 | {W6692_WINBOND, "Winbond W6692"}, | ||
50 | {W6692_USR, "USR W6692"} | ||
51 | }; | ||
52 | |||
53 | #ifndef PCI_VENDOR_ID_USR | ||
54 | #define PCI_VENDOR_ID_USR 0x16ec | ||
55 | #define PCI_DEVICE_ID_USR_6692 0x3409 | ||
56 | #endif | ||
57 | |||
58 | struct w6692_ch { | ||
59 | struct bchannel bch; | ||
60 | u32 addr; | ||
61 | struct timer_list timer; | ||
62 | u8 b_mode; | ||
63 | }; | ||
64 | |||
65 | struct w6692_hw { | ||
66 | struct list_head list; | ||
67 | struct pci_dev *pdev; | ||
68 | char name[MISDN_MAX_IDLEN]; | ||
69 | u32 irq; | ||
70 | u32 irqcnt; | ||
71 | u32 addr; | ||
72 | u32 fmask; /* feature mask - bit set per card nr */ | ||
73 | int subtype; | ||
74 | spinlock_t lock; /* hw lock */ | ||
75 | u8 imask; | ||
76 | u8 pctl; | ||
77 | u8 xaddr; | ||
78 | u8 xdata; | ||
79 | u8 state; | ||
80 | struct w6692_ch bc[2]; | ||
81 | struct dchannel dch; | ||
82 | char log[64]; | ||
83 | }; | ||
84 | |||
85 | static LIST_HEAD(Cards); | ||
86 | static DEFINE_RWLOCK(card_lock); /* protect Cards */ | ||
87 | |||
88 | static int w6692_cnt; | ||
89 | static int debug; | ||
90 | static u32 led; | ||
91 | static u32 pots; | ||
92 | |||
93 | static void | ||
94 | _set_debug(struct w6692_hw *card) | ||
95 | { | ||
96 | card->dch.debug = debug; | ||
97 | card->bc[0].bch.debug = debug; | ||
98 | card->bc[1].bch.debug = debug; | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | set_debug(const char *val, struct kernel_param *kp) | ||
103 | { | ||
104 | int ret; | ||
105 | struct w6692_hw *card; | ||
106 | |||
107 | ret = param_set_uint(val, kp); | ||
108 | if (!ret) { | ||
109 | read_lock(&card_lock); | ||
110 | list_for_each_entry(card, &Cards, list) | ||
111 | _set_debug(card); | ||
112 | read_unlock(&card_lock); | ||
113 | } | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | MODULE_AUTHOR("Karsten Keil"); | ||
118 | MODULE_LICENSE("GPL v2"); | ||
119 | MODULE_VERSION(W6692_REV); | ||
120 | module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); | ||
121 | MODULE_PARM_DESC(debug, "W6692 debug mask"); | ||
122 | module_param(led, uint, S_IRUGO | S_IWUSR); | ||
123 | MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)"); | ||
124 | module_param(pots, uint, S_IRUGO | S_IWUSR); | ||
125 | MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)"); | ||
126 | |||
127 | static inline u8 | ||
128 | ReadW6692(struct w6692_hw *card, u8 offset) | ||
129 | { | ||
130 | return inb(card->addr + offset); | ||
131 | } | ||
132 | |||
133 | static inline void | ||
134 | WriteW6692(struct w6692_hw *card, u8 offset, u8 value) | ||
135 | { | ||
136 | outb(value, card->addr + offset); | ||
137 | } | ||
138 | |||
139 | static inline u8 | ||
140 | ReadW6692B(struct w6692_ch *bc, u8 offset) | ||
141 | { | ||
142 | return inb(bc->addr + offset); | ||
143 | } | ||
144 | |||
145 | static inline void | ||
146 | WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value) | ||
147 | { | ||
148 | outb(value, bc->addr + offset); | ||
149 | } | ||
150 | |||
151 | static void | ||
152 | enable_hwirq(struct w6692_hw *card) | ||
153 | { | ||
154 | WriteW6692(card, W_IMASK, card->imask); | ||
155 | } | ||
156 | |||
157 | static void | ||
158 | disable_hwirq(struct w6692_hw *card) | ||
159 | { | ||
160 | WriteW6692(card, W_IMASK, 0xff); | ||
161 | } | ||
162 | |||
163 | static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"}; | ||
164 | |||
165 | static void | ||
166 | W6692Version(struct w6692_hw *card) | ||
167 | { | ||
168 | int val; | ||
169 | |||
170 | val = ReadW6692(card, W_D_RBCH); | ||
171 | pr_notice("%s: Winbond W6692 version: %s\n", card->name, | ||
172 | W6692Ver[(val >> 6) & 3]); | ||
173 | } | ||
174 | |||
175 | static void | ||
176 | w6692_led_handler(struct w6692_hw *card, int on) | ||
177 | { | ||
178 | if ((!(card->fmask & led)) || card->subtype == W6692_USR) | ||
179 | return; | ||
180 | if (on) { | ||
181 | card->xdata &= 0xfb; /* LED ON */ | ||
182 | WriteW6692(card, W_XDATA, card->xdata); | ||
183 | } else { | ||
184 | card->xdata |= 0x04; /* LED OFF */ | ||
185 | WriteW6692(card, W_XDATA, card->xdata); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | static void | ||
190 | ph_command(struct w6692_hw *card, u8 cmd) | ||
191 | { | ||
192 | pr_debug("%s: ph_command %x\n", card->name, cmd); | ||
193 | WriteW6692(card, W_CIX, cmd); | ||
194 | } | ||
195 | |||
196 | static void | ||
197 | W6692_new_ph(struct w6692_hw *card) | ||
198 | { | ||
199 | if (card->state == W_L1CMD_RST) | ||
200 | ph_command(card, W_L1CMD_DRC); | ||
201 | schedule_event(&card->dch, FLG_PHCHANGE); | ||
202 | } | ||
203 | |||
204 | static void | ||
205 | W6692_ph_bh(struct dchannel *dch) | ||
206 | { | ||
207 | struct w6692_hw *card = dch->hw; | ||
208 | |||
209 | switch (card->state) { | ||
210 | case W_L1CMD_RST: | ||
211 | dch->state = 0; | ||
212 | l1_event(dch->l1, HW_RESET_IND); | ||
213 | break; | ||
214 | case W_L1IND_CD: | ||
215 | dch->state = 3; | ||
216 | l1_event(dch->l1, HW_DEACT_CNF); | ||
217 | break; | ||
218 | case W_L1IND_DRD: | ||
219 | dch->state = 3; | ||
220 | l1_event(dch->l1, HW_DEACT_IND); | ||
221 | break; | ||
222 | case W_L1IND_CE: | ||
223 | dch->state = 4; | ||
224 | l1_event(dch->l1, HW_POWERUP_IND); | ||
225 | break; | ||
226 | case W_L1IND_LD: | ||
227 | if (dch->state <= 5) { | ||
228 | dch->state = 5; | ||
229 | l1_event(dch->l1, ANYSIGNAL); | ||
230 | } else { | ||
231 | dch->state = 8; | ||
232 | l1_event(dch->l1, LOSTFRAMING); | ||
233 | } | ||
234 | break; | ||
235 | case W_L1IND_ARD: | ||
236 | dch->state = 6; | ||
237 | l1_event(dch->l1, INFO2); | ||
238 | break; | ||
239 | case W_L1IND_AI8: | ||
240 | dch->state = 7; | ||
241 | l1_event(dch->l1, INFO4_P8); | ||
242 | break; | ||
243 | case W_L1IND_AI10: | ||
244 | dch->state = 7; | ||
245 | l1_event(dch->l1, INFO4_P10); | ||
246 | break; | ||
247 | default: | ||
248 | pr_debug("%s: TE unknown state %02x dch state %02x\n", | ||
249 | card->name, card->state, dch->state); | ||
250 | break; | ||
251 | } | ||
252 | pr_debug("%s: TE newstate %02x\n", card->name, dch->state); | ||
253 | } | ||
254 | |||
255 | static void | ||
256 | W6692_empty_Dfifo(struct w6692_hw *card, int count) | ||
257 | { | ||
258 | struct dchannel *dch = &card->dch; | ||
259 | u8 *ptr; | ||
260 | |||
261 | pr_debug("%s: empty_Dfifo %d\n", card->name, count); | ||
262 | if (!dch->rx_skb) { | ||
263 | dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC); | ||
264 | if (!dch->rx_skb) { | ||
265 | pr_info("%s: D receive out of memory\n", card->name); | ||
266 | WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); | ||
267 | return; | ||
268 | } | ||
269 | } | ||
270 | if ((dch->rx_skb->len + count) >= dch->maxlen) { | ||
271 | pr_debug("%s: empty_Dfifo overrun %d\n", card->name, | ||
272 | dch->rx_skb->len + count); | ||
273 | WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); | ||
274 | return; | ||
275 | } | ||
276 | ptr = skb_put(dch->rx_skb, count); | ||
277 | insb(card->addr + W_D_RFIFO, ptr, count); | ||
278 | WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); | ||
279 | if (debug & DEBUG_HW_DFIFO) { | ||
280 | snprintf(card->log, 63, "D-recv %s %d ", | ||
281 | card->name, count); | ||
282 | print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static void | ||
287 | W6692_fill_Dfifo(struct w6692_hw *card) | ||
288 | { | ||
289 | struct dchannel *dch = &card->dch; | ||
290 | int count; | ||
291 | u8 *ptr; | ||
292 | u8 cmd = W_D_CMDR_XMS; | ||
293 | |||
294 | pr_debug("%s: fill_Dfifo\n", card->name); | ||
295 | if (!dch->tx_skb) | ||
296 | return; | ||
297 | count = dch->tx_skb->len - dch->tx_idx; | ||
298 | if (count <= 0) | ||
299 | return; | ||
300 | if (count > W_D_FIFO_THRESH) | ||
301 | count = W_D_FIFO_THRESH; | ||
302 | else | ||
303 | cmd |= W_D_CMDR_XME; | ||
304 | ptr = dch->tx_skb->data + dch->tx_idx; | ||
305 | dch->tx_idx += count; | ||
306 | outsb(card->addr + W_D_XFIFO, ptr, count); | ||
307 | WriteW6692(card, W_D_CMDR, cmd); | ||
308 | if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) { | ||
309 | pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name); | ||
310 | del_timer(&dch->timer); | ||
311 | } | ||
312 | init_timer(&dch->timer); | ||
313 | dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); | ||
314 | add_timer(&dch->timer); | ||
315 | if (debug & DEBUG_HW_DFIFO) { | ||
316 | snprintf(card->log, 63, "D-send %s %d ", | ||
317 | card->name, count); | ||
318 | print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | static void | ||
323 | d_retransmit(struct w6692_hw *card) | ||
324 | { | ||
325 | struct dchannel *dch = &card->dch; | ||
326 | |||
327 | if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) | ||
328 | del_timer(&dch->timer); | ||
329 | #ifdef FIXME | ||
330 | if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) | ||
331 | dchannel_sched_event(dch, D_CLEARBUSY); | ||
332 | #endif | ||
333 | if (test_bit(FLG_TX_BUSY, &dch->Flags)) { | ||
334 | /* Restart frame */ | ||
335 | dch->tx_idx = 0; | ||
336 | W6692_fill_Dfifo(card); | ||
337 | } else if (dch->tx_skb) { /* should not happen */ | ||
338 | pr_info("%s: %s without TX_BUSY\n", card->name, __func__); | ||
339 | test_and_set_bit(FLG_TX_BUSY, &dch->Flags); | ||
340 | dch->tx_idx = 0; | ||
341 | W6692_fill_Dfifo(card); | ||
342 | } else { | ||
343 | pr_info("%s: XDU no TX_BUSY\n", card->name); | ||
344 | if (get_next_dframe(dch)) | ||
345 | W6692_fill_Dfifo(card); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | static void | ||
350 | handle_rxD(struct w6692_hw *card) { | ||
351 | u8 stat; | ||
352 | int count; | ||
353 | |||
354 | stat = ReadW6692(card, W_D_RSTA); | ||
355 | if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) { | ||
356 | if (stat & W_D_RSTA_RDOV) { | ||
357 | pr_debug("%s: D-channel RDOV\n", card->name); | ||
358 | #ifdef ERROR_STATISTIC | ||
359 | card->dch.err_rx++; | ||
360 | #endif | ||
361 | } | ||
362 | if (stat & W_D_RSTA_CRCE) { | ||
363 | pr_debug("%s: D-channel CRC error\n", card->name); | ||
364 | #ifdef ERROR_STATISTIC | ||
365 | card->dch.err_crc++; | ||
366 | #endif | ||
367 | } | ||
368 | if (stat & W_D_RSTA_RMB) { | ||
369 | pr_debug("%s: D-channel ABORT\n", card->name); | ||
370 | #ifdef ERROR_STATISTIC | ||
371 | card->dch.err_rx++; | ||
372 | #endif | ||
373 | } | ||
374 | if (card->dch.rx_skb) | ||
375 | dev_kfree_skb(card->dch.rx_skb); | ||
376 | card->dch.rx_skb = NULL; | ||
377 | WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST); | ||
378 | } else { | ||
379 | count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1); | ||
380 | if (count == 0) | ||
381 | count = W_D_FIFO_THRESH; | ||
382 | W6692_empty_Dfifo(card, count); | ||
383 | recv_Dchannel(&card->dch); | ||
384 | } | ||
385 | } | ||
386 | |||
387 | static void | ||
388 | handle_txD(struct w6692_hw *card) { | ||
389 | if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags)) | ||
390 | del_timer(&card->dch.timer); | ||
391 | if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) { | ||
392 | W6692_fill_Dfifo(card); | ||
393 | } else { | ||
394 | if (card->dch.tx_skb) | ||
395 | dev_kfree_skb(card->dch.tx_skb); | ||
396 | if (get_next_dframe(&card->dch)) | ||
397 | W6692_fill_Dfifo(card); | ||
398 | } | ||
399 | } | ||
400 | |||
401 | static void | ||
402 | handle_statusD(struct w6692_hw *card) | ||
403 | { | ||
404 | struct dchannel *dch = &card->dch; | ||
405 | u8 exval, v1, cir; | ||
406 | |||
407 | exval = ReadW6692(card, W_D_EXIR); | ||
408 | |||
409 | pr_debug("%s: D_EXIR %02x\n", card->name, exval); | ||
410 | if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { | ||
411 | /* Transmit underrun/collision */ | ||
412 | pr_debug("%s: D-channel underrun/collision\n", card->name); | ||
413 | #ifdef ERROR_STATISTIC | ||
414 | dch->err_tx++; | ||
415 | #endif | ||
416 | d_retransmit(card); | ||
417 | } | ||
418 | if (exval & W_D_EXI_RDOV) { /* RDOV */ | ||
419 | pr_debug("%s: D-channel RDOV\n", card->name); | ||
420 | WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST); | ||
421 | } | ||
422 | if (exval & W_D_EXI_TIN2) /* TIN2 - never */ | ||
423 | pr_debug("%s: spurious TIN2 interrupt\n", card->name); | ||
424 | if (exval & W_D_EXI_MOC) { /* MOC - not supported */ | ||
425 | v1 = ReadW6692(card, W_MOSR); | ||
426 | pr_debug("%s: spurious MOC interrupt MOSR %02x\n", | ||
427 | card->name, v1); | ||
428 | } | ||
429 | if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */ | ||
430 | cir = ReadW6692(card, W_CIR); | ||
431 | pr_debug("%s: ISC CIR %02X\n", card->name, cir); | ||
432 | if (cir & W_CIR_ICC) { | ||
433 | v1 = cir & W_CIR_COD_MASK; | ||
434 | pr_debug("%s: ph_state_change %x -> %x\n", card->name, | ||
435 | dch->state, v1); | ||
436 | card->state = v1; | ||
437 | if (card->fmask & led) { | ||
438 | switch (v1) { | ||
439 | case W_L1IND_AI8: | ||
440 | case W_L1IND_AI10: | ||
441 | w6692_led_handler(card, 1); | ||
442 | break; | ||
443 | default: | ||
444 | w6692_led_handler(card, 0); | ||
445 | break; | ||
446 | } | ||
447 | } | ||
448 | W6692_new_ph(card); | ||
449 | } | ||
450 | if (cir & W_CIR_SCC) { | ||
451 | v1 = ReadW6692(card, W_SQR); | ||
452 | pr_debug("%s: SCC SQR %02X\n", card->name, v1); | ||
453 | } | ||
454 | } | ||
455 | if (exval & W_D_EXI_WEXP) | ||
456 | pr_debug("%s: spurious WEXP interrupt!\n", card->name); | ||
457 | if (exval & W_D_EXI_TEXP) | ||
458 | pr_debug("%s: spurious TEXP interrupt!\n", card->name); | ||
459 | } | ||
460 | |||
461 | static void | ||
462 | W6692_empty_Bfifo(struct w6692_ch *wch, int count) | ||
463 | { | ||
464 | struct w6692_hw *card = wch->bch.hw; | ||
465 | u8 *ptr; | ||
466 | |||
467 | pr_debug("%s: empty_Bfifo %d\n", card->name, count); | ||
468 | if (unlikely(wch->bch.state == ISDN_P_NONE)) { | ||
469 | pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name); | ||
470 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); | ||
471 | if (wch->bch.rx_skb) | ||
472 | skb_trim(wch->bch.rx_skb, 0); | ||
473 | return; | ||
474 | } | ||
475 | if (!wch->bch.rx_skb) { | ||
476 | wch->bch.rx_skb = mI_alloc_skb(wch->bch.maxlen, GFP_ATOMIC); | ||
477 | if (unlikely(!wch->bch.rx_skb)) { | ||
478 | pr_info("%s: B receive out of memory\n", card->name); | ||
479 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | | ||
480 | W_B_CMDR_RACT); | ||
481 | return; | ||
482 | } | ||
483 | } | ||
484 | if (wch->bch.rx_skb->len + count > wch->bch.maxlen) { | ||
485 | pr_debug("%s: empty_Bfifo incoming packet too large\n", | ||
486 | card->name); | ||
487 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); | ||
488 | skb_trim(wch->bch.rx_skb, 0); | ||
489 | return; | ||
490 | } | ||
491 | ptr = skb_put(wch->bch.rx_skb, count); | ||
492 | insb(wch->addr + W_B_RFIFO, ptr, count); | ||
493 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); | ||
494 | if (debug & DEBUG_HW_DFIFO) { | ||
495 | snprintf(card->log, 63, "B%1d-recv %s %d ", | ||
496 | wch->bch.nr, card->name, count); | ||
497 | print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); | ||
498 | } | ||
499 | } | ||
500 | |||
501 | static void | ||
502 | W6692_fill_Bfifo(struct w6692_ch *wch) | ||
503 | { | ||
504 | struct w6692_hw *card = wch->bch.hw; | ||
505 | int count; | ||
506 | u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS; | ||
507 | |||
508 | pr_debug("%s: fill Bfifo\n", card->name); | ||
509 | if (!wch->bch.tx_skb) | ||
510 | return; | ||
511 | count = wch->bch.tx_skb->len - wch->bch.tx_idx; | ||
512 | if (count <= 0) | ||
513 | return; | ||
514 | ptr = wch->bch.tx_skb->data + wch->bch.tx_idx; | ||
515 | if (count > W_B_FIFO_THRESH) | ||
516 | count = W_B_FIFO_THRESH; | ||
517 | else if (test_bit(FLG_HDLC, &wch->bch.Flags)) | ||
518 | cmd |= W_B_CMDR_XME; | ||
519 | |||
520 | pr_debug("%s: fill Bfifo%d/%d\n", card->name, | ||
521 | count, wch->bch.tx_idx); | ||
522 | wch->bch.tx_idx += count; | ||
523 | outsb(wch->addr + W_B_XFIFO, ptr, count); | ||
524 | WriteW6692B(wch, W_B_CMDR, cmd); | ||
525 | if (debug & DEBUG_HW_DFIFO) { | ||
526 | snprintf(card->log, 63, "B%1d-send %s %d ", | ||
527 | wch->bch.nr, card->name, count); | ||
528 | print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); | ||
529 | } | ||
530 | } | ||
531 | |||
532 | static int | ||
533 | setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb) | ||
534 | { | ||
535 | struct w6692_hw *card = wch->bch.hw; | ||
536 | u16 *vol = (u16 *)skb->data; | ||
537 | u8 val; | ||
538 | |||
539 | if ((!(card->fmask & pots)) || | ||
540 | !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) | ||
541 | return -ENODEV; | ||
542 | if (skb->len < 2) | ||
543 | return -EINVAL; | ||
544 | if (*vol > 7) | ||
545 | return -EINVAL; | ||
546 | val = *vol & 7; | ||
547 | val = 7 - val; | ||
548 | if (mic) { | ||
549 | val <<= 3; | ||
550 | card->xaddr &= 0xc7; | ||
551 | } else { | ||
552 | card->xaddr &= 0xf8; | ||
553 | } | ||
554 | card->xaddr |= val; | ||
555 | WriteW6692(card, W_XADDR, card->xaddr); | ||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static int | ||
560 | enable_pots(struct w6692_ch *wch) | ||
561 | { | ||
562 | struct w6692_hw *card = wch->bch.hw; | ||
563 | |||
564 | if ((!(card->fmask & pots)) || | ||
565 | !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) | ||
566 | return -ENODEV; | ||
567 | wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0; | ||
568 | WriteW6692B(wch, W_B_MODE, wch->b_mode); | ||
569 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); | ||
570 | card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0); | ||
571 | WriteW6692(card, W_PCTL, card->pctl); | ||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static int | ||
576 | disable_pots(struct w6692_ch *wch) | ||
577 | { | ||
578 | struct w6692_hw *card = wch->bch.hw; | ||
579 | |||
580 | if (!(card->fmask & pots)) | ||
581 | return -ENODEV; | ||
582 | wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0); | ||
583 | WriteW6692B(wch, W_B_MODE, wch->b_mode); | ||
584 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | | ||
585 | W_B_CMDR_XRST); | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static int | ||
590 | w6692_mode(struct w6692_ch *wch, u32 pr) | ||
591 | { | ||
592 | struct w6692_hw *card; | ||
593 | |||
594 | card = wch->bch.hw; | ||
595 | pr_debug("%s: B%d protocol %x-->%x\n", card->name, | ||
596 | wch->bch.nr, wch->bch.state, pr); | ||
597 | switch (pr) { | ||
598 | case ISDN_P_NONE: | ||
599 | if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM)) | ||
600 | disable_pots(wch); | ||
601 | wch->b_mode = 0; | ||
602 | mISDN_clear_bchannel(&wch->bch); | ||
603 | WriteW6692B(wch, W_B_MODE, wch->b_mode); | ||
604 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); | ||
605 | test_and_clear_bit(FLG_HDLC, &wch->bch.Flags); | ||
606 | test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags); | ||
607 | break; | ||
608 | case ISDN_P_B_RAW: | ||
609 | wch->b_mode = W_B_MODE_MMS; | ||
610 | WriteW6692B(wch, W_B_MODE, wch->b_mode); | ||
611 | WriteW6692B(wch, W_B_EXIM, 0); | ||
612 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | | ||
613 | W_B_CMDR_XRST); | ||
614 | test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags); | ||
615 | break; | ||
616 | case ISDN_P_B_HDLC: | ||
617 | wch->b_mode = W_B_MODE_ITF; | ||
618 | WriteW6692B(wch, W_B_MODE, wch->b_mode); | ||
619 | WriteW6692B(wch, W_B_ADM1, 0xff); | ||
620 | WriteW6692B(wch, W_B_ADM2, 0xff); | ||
621 | WriteW6692B(wch, W_B_EXIM, 0); | ||
622 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | | ||
623 | W_B_CMDR_XRST); | ||
624 | test_and_set_bit(FLG_HDLC, &wch->bch.Flags); | ||
625 | break; | ||
626 | default: | ||
627 | pr_info("%s: protocol %x not known\n", card->name, pr); | ||
628 | return -ENOPROTOOPT; | ||
629 | } | ||
630 | wch->bch.state = pr; | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static void | ||
635 | send_next(struct w6692_ch *wch) | ||
636 | { | ||
637 | if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len) | ||
638 | W6692_fill_Bfifo(wch); | ||
639 | else { | ||
640 | if (wch->bch.tx_skb) { | ||
641 | /* send confirm, on trans, free on hdlc. */ | ||
642 | if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) | ||
643 | confirm_Bsend(&wch->bch); | ||
644 | dev_kfree_skb(wch->bch.tx_skb); | ||
645 | } | ||
646 | if (get_next_bframe(&wch->bch)) | ||
647 | W6692_fill_Bfifo(wch); | ||
648 | } | ||
649 | } | ||
650 | |||
651 | static void | ||
652 | W6692B_interrupt(struct w6692_hw *card, int ch) | ||
653 | { | ||
654 | struct w6692_ch *wch = &card->bc[ch]; | ||
655 | int count; | ||
656 | u8 stat, star = 0; | ||
657 | |||
658 | stat = ReadW6692B(wch, W_B_EXIR); | ||
659 | pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat); | ||
660 | if (stat & W_B_EXI_RME) { | ||
661 | star = ReadW6692B(wch, W_B_STAR); | ||
662 | if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) { | ||
663 | if ((star & W_B_STAR_RDOV) && | ||
664 | test_bit(FLG_ACTIVE, &wch->bch.Flags)) { | ||
665 | pr_debug("%s: B%d RDOV proto=%x\n", card->name, | ||
666 | wch->bch.nr, wch->bch.state); | ||
667 | #ifdef ERROR_STATISTIC | ||
668 | wch->bch.err_rdo++; | ||
669 | #endif | ||
670 | } | ||
671 | if (test_bit(FLG_HDLC, &wch->bch.Flags)) { | ||
672 | if (star & W_B_STAR_CRCE) { | ||
673 | pr_debug("%s: B%d CRC error\n", | ||
674 | card->name, wch->bch.nr); | ||
675 | #ifdef ERROR_STATISTIC | ||
676 | wch->bch.err_crc++; | ||
677 | #endif | ||
678 | } | ||
679 | if (star & W_B_STAR_RMB) { | ||
680 | pr_debug("%s: B%d message abort\n", | ||
681 | card->name, wch->bch.nr); | ||
682 | #ifdef ERROR_STATISTIC | ||
683 | wch->bch.err_inv++; | ||
684 | #endif | ||
685 | } | ||
686 | } | ||
687 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | | ||
688 | W_B_CMDR_RRST | W_B_CMDR_RACT); | ||
689 | if (wch->bch.rx_skb) | ||
690 | skb_trim(wch->bch.rx_skb, 0); | ||
691 | } else { | ||
692 | count = ReadW6692B(wch, W_B_RBCL) & | ||
693 | (W_B_FIFO_THRESH - 1); | ||
694 | if (count == 0) | ||
695 | count = W_B_FIFO_THRESH; | ||
696 | W6692_empty_Bfifo(wch, count); | ||
697 | recv_Bchannel(&wch->bch, 0); | ||
698 | } | ||
699 | } | ||
700 | if (stat & W_B_EXI_RMR) { | ||
701 | if (!(stat & W_B_EXI_RME)) | ||
702 | star = ReadW6692B(wch, W_B_STAR); | ||
703 | if (star & W_B_STAR_RDOV) { | ||
704 | pr_debug("%s: B%d RDOV proto=%x\n", card->name, | ||
705 | wch->bch.nr, wch->bch.state); | ||
706 | #ifdef ERROR_STATISTIC | ||
707 | wch->bch.err_rdo++; | ||
708 | #endif | ||
709 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | | ||
710 | W_B_CMDR_RRST | W_B_CMDR_RACT); | ||
711 | } else { | ||
712 | W6692_empty_Bfifo(wch, W_B_FIFO_THRESH); | ||
713 | if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags) && | ||
714 | wch->bch.rx_skb && (wch->bch.rx_skb->len > 0)) | ||
715 | recv_Bchannel(&wch->bch, 0); | ||
716 | } | ||
717 | } | ||
718 | if (stat & W_B_EXI_RDOV) { | ||
719 | /* only if it is not handled yet */ | ||
720 | if (!(star & W_B_STAR_RDOV)) { | ||
721 | pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name, | ||
722 | wch->bch.nr, wch->bch.state); | ||
723 | #ifdef ERROR_STATISTIC | ||
724 | wch->bch.err_rdo++; | ||
725 | #endif | ||
726 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | | ||
727 | W_B_CMDR_RRST | W_B_CMDR_RACT); | ||
728 | } | ||
729 | } | ||
730 | if (stat & W_B_EXI_XFR) { | ||
731 | if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) { | ||
732 | star = ReadW6692B(wch, W_B_STAR); | ||
733 | pr_debug("%s: B%d star %02x\n", card->name, | ||
734 | wch->bch.nr, star); | ||
735 | } | ||
736 | if (star & W_B_STAR_XDOW) { | ||
737 | pr_debug("%s: B%d XDOW proto=%x\n", card->name, | ||
738 | wch->bch.nr, wch->bch.state); | ||
739 | #ifdef ERROR_STATISTIC | ||
740 | wch->bch.err_xdu++; | ||
741 | #endif | ||
742 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | | ||
743 | W_B_CMDR_RACT); | ||
744 | /* resend */ | ||
745 | if (wch->bch.tx_skb) { | ||
746 | if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) | ||
747 | wch->bch.tx_idx = 0; | ||
748 | } | ||
749 | } | ||
750 | send_next(wch); | ||
751 | if (stat & W_B_EXI_XDUN) | ||
752 | return; /* handle XDOW only once */ | ||
753 | } | ||
754 | if (stat & W_B_EXI_XDUN) { | ||
755 | pr_debug("%s: B%d XDUN proto=%x\n", card->name, | ||
756 | wch->bch.nr, wch->bch.state); | ||
757 | #ifdef ERROR_STATISTIC | ||
758 | wch->bch.err_xdu++; | ||
759 | #endif | ||
760 | WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); | ||
761 | /* resend */ | ||
762 | if (wch->bch.tx_skb) { | ||
763 | if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) | ||
764 | wch->bch.tx_idx = 0; | ||
765 | } | ||
766 | send_next(wch); | ||
767 | } | ||
768 | } | ||
769 | |||
770 | static irqreturn_t | ||
771 | w6692_irq(int intno, void *dev_id) | ||
772 | { | ||
773 | struct w6692_hw *card = dev_id; | ||
774 | u8 ista; | ||
775 | |||
776 | spin_lock(&card->lock); | ||
777 | ista = ReadW6692(card, W_ISTA); | ||
778 | if ((ista | card->imask) == card->imask) { | ||
779 | /* possible a shared IRQ reqest */ | ||
780 | spin_unlock(&card->lock); | ||
781 | return IRQ_NONE; | ||
782 | } | ||
783 | card->irqcnt++; | ||
784 | pr_debug("%s: ista %02x\n", card->name, ista); | ||
785 | ista &= ~card->imask; | ||
786 | if (ista & W_INT_B1_EXI) | ||
787 | W6692B_interrupt(card, 0); | ||
788 | if (ista & W_INT_B2_EXI) | ||
789 | W6692B_interrupt(card, 1); | ||
790 | if (ista & W_INT_D_RME) | ||
791 | handle_rxD(card); | ||
792 | if (ista & W_INT_D_RMR) | ||
793 | W6692_empty_Dfifo(card, W_D_FIFO_THRESH); | ||
794 | if (ista & W_INT_D_XFR) | ||
795 | handle_txD(card); | ||
796 | if (ista & W_INT_D_EXI) | ||
797 | handle_statusD(card); | ||
798 | if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */ | ||
799 | pr_debug("%s: W6692 spurious XINT!\n", card->name); | ||
800 | /* End IRQ Handler */ | ||
801 | spin_unlock(&card->lock); | ||
802 | return IRQ_HANDLED; | ||
803 | } | ||
804 | |||
805 | static void | ||
806 | dbusy_timer_handler(struct dchannel *dch) | ||
807 | { | ||
808 | struct w6692_hw *card = dch->hw; | ||
809 | int rbch, star; | ||
810 | u_long flags; | ||
811 | |||
812 | if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) { | ||
813 | spin_lock_irqsave(&card->lock, flags); | ||
814 | rbch = ReadW6692(card, W_D_RBCH); | ||
815 | star = ReadW6692(card, W_D_STAR); | ||
816 | pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n", | ||
817 | card->name, rbch, star); | ||
818 | if (star & W_D_STAR_XBZ) /* D-Channel Busy */ | ||
819 | test_and_set_bit(FLG_L1_BUSY, &dch->Flags); | ||
820 | else { | ||
821 | /* discard frame; reset transceiver */ | ||
822 | test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags); | ||
823 | if (dch->tx_idx) | ||
824 | dch->tx_idx = 0; | ||
825 | else | ||
826 | pr_info("%s: W6692 D-Channel Busy no tx_idx\n", | ||
827 | card->name); | ||
828 | /* Transmitter reset */ | ||
829 | WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST); | ||
830 | } | ||
831 | spin_unlock_irqrestore(&card->lock, flags); | ||
832 | } | ||
833 | } | ||
834 | |||
835 | void initW6692(struct w6692_hw *card) | ||
836 | { | ||
837 | u8 val; | ||
838 | |||
839 | card->dch.timer.function = (void *)dbusy_timer_handler; | ||
840 | card->dch.timer.data = (u_long)&card->dch; | ||
841 | init_timer(&card->dch.timer); | ||
842 | w6692_mode(&card->bc[0], ISDN_P_NONE); | ||
843 | w6692_mode(&card->bc[1], ISDN_P_NONE); | ||
844 | WriteW6692(card, W_D_CTL, 0x00); | ||
845 | disable_hwirq(card); | ||
846 | WriteW6692(card, W_D_SAM, 0xff); | ||
847 | WriteW6692(card, W_D_TAM, 0xff); | ||
848 | WriteW6692(card, W_D_MODE, W_D_MODE_RACT); | ||
849 | card->state = W_L1CMD_RST; | ||
850 | ph_command(card, W_L1CMD_RST); | ||
851 | ph_command(card, W_L1CMD_ECK); | ||
852 | /* enable all IRQ but extern */ | ||
853 | card->imask = 0x18; | ||
854 | WriteW6692(card, W_D_EXIM, 0x00); | ||
855 | WriteW6692B(&card->bc[0], W_B_EXIM, 0); | ||
856 | WriteW6692B(&card->bc[1], W_B_EXIM, 0); | ||
857 | /* Reset D-chan receiver and transmitter */ | ||
858 | WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST); | ||
859 | /* Reset B-chan receiver and transmitter */ | ||
860 | WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); | ||
861 | WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); | ||
862 | /* enable peripheral */ | ||
863 | if (card->subtype == W6692_USR) { | ||
864 | /* seems that USR implemented some power control features | ||
865 | * Pin 79 is connected to the oscilator circuit so we | ||
866 | * have to handle it here | ||
867 | */ | ||
868 | card->pctl = 0x80; | ||
869 | card->xdata = 0; | ||
870 | WriteW6692(card, W_PCTL, card->pctl); | ||
871 | WriteW6692(card, W_XDATA, card->xdata); | ||
872 | } else { | ||
873 | card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | | ||
874 | W_PCTL_OE1 | W_PCTL_OE0; | ||
875 | card->xaddr = 0x00;/* all sw off */ | ||
876 | if (card->fmask & pots) | ||
877 | card->xdata |= 0x06; /* POWER UP/ LED OFF / ALAW */ | ||
878 | if (card->fmask & led) | ||
879 | card->xdata |= 0x04; /* LED OFF */ | ||
880 | if ((card->fmask & pots) || (card->fmask & led)) { | ||
881 | WriteW6692(card, W_PCTL, card->pctl); | ||
882 | WriteW6692(card, W_XADDR, card->xaddr); | ||
883 | WriteW6692(card, W_XDATA, card->xdata); | ||
884 | val = ReadW6692(card, W_XADDR); | ||
885 | if (debug & DEBUG_HW) | ||
886 | pr_notice("%s: W_XADDR=%02x\n", | ||
887 | card->name, val); | ||
888 | } | ||
889 | } | ||
890 | } | ||
891 | |||
892 | static void | ||
893 | reset_w6692(struct w6692_hw *card) | ||
894 | { | ||
895 | WriteW6692(card, W_D_CTL, W_D_CTL_SRST); | ||
896 | mdelay(10); | ||
897 | WriteW6692(card, W_D_CTL, 0); | ||
898 | } | ||
899 | |||
900 | static int | ||
901 | init_card(struct w6692_hw *card) | ||
902 | { | ||
903 | int cnt = 3; | ||
904 | u_long flags; | ||
905 | |||
906 | spin_lock_irqsave(&card->lock, flags); | ||
907 | disable_hwirq(card); | ||
908 | spin_unlock_irqrestore(&card->lock, flags); | ||
909 | if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) { | ||
910 | pr_info("%s: couldn't get interrupt %d\n", card->name, | ||
911 | card->irq); | ||
912 | return -EIO; | ||
913 | } | ||
914 | while (cnt--) { | ||
915 | spin_lock_irqsave(&card->lock, flags); | ||
916 | initW6692(card); | ||
917 | enable_hwirq(card); | ||
918 | spin_unlock_irqrestore(&card->lock, flags); | ||
919 | /* Timeout 10ms */ | ||
920 | msleep_interruptible(10); | ||
921 | if (debug & DEBUG_HW) | ||
922 | pr_notice("%s: IRQ %d count %d\n", card->name, | ||
923 | card->irq, card->irqcnt); | ||
924 | if (!card->irqcnt) { | ||
925 | pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", | ||
926 | card->name, card->irq, 3 - cnt); | ||
927 | reset_w6692(card); | ||
928 | } else | ||
929 | return 0; | ||
930 | } | ||
931 | free_irq(card->irq, card); | ||
932 | return -EIO; | ||
933 | } | ||
934 | |||
935 | static int | ||
936 | w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) | ||
937 | { | ||
938 | struct bchannel *bch = container_of(ch, struct bchannel, ch); | ||
939 | struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); | ||
940 | struct w6692_hw *card = bch->hw; | ||
941 | int ret = -EINVAL; | ||
942 | struct mISDNhead *hh = mISDN_HEAD_P(skb); | ||
943 | u32 id; | ||
944 | u_long flags; | ||
945 | |||
946 | switch (hh->prim) { | ||
947 | case PH_DATA_REQ: | ||
948 | spin_lock_irqsave(&card->lock, flags); | ||
949 | ret = bchannel_senddata(bch, skb); | ||
950 | if (ret > 0) { /* direct TX */ | ||
951 | id = hh->id; /* skb can be freed */ | ||
952 | ret = 0; | ||
953 | W6692_fill_Bfifo(bc); | ||
954 | spin_unlock_irqrestore(&card->lock, flags); | ||
955 | if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) | ||
956 | queue_ch_frame(ch, PH_DATA_CNF, id, NULL); | ||
957 | } else | ||
958 | spin_unlock_irqrestore(&card->lock, flags); | ||
959 | return ret; | ||
960 | case PH_ACTIVATE_REQ: | ||
961 | spin_lock_irqsave(&card->lock, flags); | ||
962 | if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) | ||
963 | ret = w6692_mode(bc, ch->protocol); | ||
964 | else | ||
965 | ret = 0; | ||
966 | spin_unlock_irqrestore(&card->lock, flags); | ||
967 | if (!ret) | ||
968 | _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, | ||
969 | NULL, GFP_KERNEL); | ||
970 | break; | ||
971 | case PH_DEACTIVATE_REQ: | ||
972 | spin_lock_irqsave(&card->lock, flags); | ||
973 | mISDN_clear_bchannel(bch); | ||
974 | w6692_mode(bc, ISDN_P_NONE); | ||
975 | spin_unlock_irqrestore(&card->lock, flags); | ||
976 | _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, | ||
977 | NULL, GFP_KERNEL); | ||
978 | ret = 0; | ||
979 | break; | ||
980 | default: | ||
981 | pr_info("%s: %s unknown prim(%x,%x)\n", | ||
982 | card->name, __func__, hh->prim, hh->id); | ||
983 | ret = -EINVAL; | ||
984 | } | ||
985 | if (!ret) | ||
986 | dev_kfree_skb(skb); | ||
987 | return ret; | ||
988 | } | ||
989 | |||
990 | static int | ||
991 | channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) | ||
992 | { | ||
993 | int ret = 0; | ||
994 | |||
995 | switch (cq->op) { | ||
996 | case MISDN_CTRL_GETOP: | ||
997 | cq->op = 0; | ||
998 | break; | ||
999 | /* Nothing implemented yet */ | ||
1000 | case MISDN_CTRL_FILL_EMPTY: | ||
1001 | default: | ||
1002 | pr_info("%s: unknown Op %x\n", __func__, cq->op); | ||
1003 | ret = -EINVAL; | ||
1004 | break; | ||
1005 | } | ||
1006 | return ret; | ||
1007 | } | ||
1008 | |||
1009 | static int | ||
1010 | open_bchannel(struct w6692_hw *card, struct channel_req *rq) | ||
1011 | { | ||
1012 | struct bchannel *bch; | ||
1013 | |||
1014 | if (rq->adr.channel > 2) | ||
1015 | return -EINVAL; | ||
1016 | if (rq->protocol == ISDN_P_NONE) | ||
1017 | return -EINVAL; | ||
1018 | bch = &card->bc[rq->adr.channel - 1].bch; | ||
1019 | if (test_and_set_bit(FLG_OPEN, &bch->Flags)) | ||
1020 | return -EBUSY; /* b-channel can be only open once */ | ||
1021 | test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); | ||
1022 | bch->ch.protocol = rq->protocol; | ||
1023 | rq->ch = &bch->ch; | ||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | static int | ||
1028 | channel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq) | ||
1029 | { | ||
1030 | int ret = 0; | ||
1031 | |||
1032 | switch (cq->op) { | ||
1033 | case MISDN_CTRL_GETOP: | ||
1034 | cq->op = 0; | ||
1035 | break; | ||
1036 | default: | ||
1037 | pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op); | ||
1038 | ret = -EINVAL; | ||
1039 | break; | ||
1040 | } | ||
1041 | return ret; | ||
1042 | } | ||
1043 | |||
1044 | static int | ||
1045 | w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) | ||
1046 | { | ||
1047 | struct bchannel *bch = container_of(ch, struct bchannel, ch); | ||
1048 | struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); | ||
1049 | struct w6692_hw *card = bch->hw; | ||
1050 | int ret = -EINVAL; | ||
1051 | u_long flags; | ||
1052 | |||
1053 | pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); | ||
1054 | switch (cmd) { | ||
1055 | case CLOSE_CHANNEL: | ||
1056 | test_and_clear_bit(FLG_OPEN, &bch->Flags); | ||
1057 | if (test_bit(FLG_ACTIVE, &bch->Flags)) { | ||
1058 | spin_lock_irqsave(&card->lock, flags); | ||
1059 | mISDN_freebchannel(bch); | ||
1060 | w6692_mode(bc, ISDN_P_NONE); | ||
1061 | spin_unlock_irqrestore(&card->lock, flags); | ||
1062 | } else { | ||
1063 | skb_queue_purge(&bch->rqueue); | ||
1064 | bch->rcount = 0; | ||
1065 | } | ||
1066 | ch->protocol = ISDN_P_NONE; | ||
1067 | ch->peer = NULL; | ||
1068 | module_put(THIS_MODULE); | ||
1069 | ret = 0; | ||
1070 | break; | ||
1071 | case CONTROL_CHANNEL: | ||
1072 | ret = channel_bctrl(bch, arg); | ||
1073 | break; | ||
1074 | default: | ||
1075 | pr_info("%s: %s unknown prim(%x)\n", | ||
1076 | card->name, __func__, cmd); | ||
1077 | } | ||
1078 | return ret; | ||
1079 | } | ||
1080 | |||
1081 | static int | ||
1082 | w6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) | ||
1083 | { | ||
1084 | struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); | ||
1085 | struct dchannel *dch = container_of(dev, struct dchannel, dev); | ||
1086 | struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); | ||
1087 | int ret = -EINVAL; | ||
1088 | struct mISDNhead *hh = mISDN_HEAD_P(skb); | ||
1089 | u32 id; | ||
1090 | u_long flags; | ||
1091 | |||
1092 | switch (hh->prim) { | ||
1093 | case PH_DATA_REQ: | ||
1094 | spin_lock_irqsave(&card->lock, flags); | ||
1095 | ret = dchannel_senddata(dch, skb); | ||
1096 | if (ret > 0) { /* direct TX */ | ||
1097 | id = hh->id; /* skb can be freed */ | ||
1098 | W6692_fill_Dfifo(card); | ||
1099 | ret = 0; | ||
1100 | spin_unlock_irqrestore(&card->lock, flags); | ||
1101 | queue_ch_frame(ch, PH_DATA_CNF, id, NULL); | ||
1102 | } else | ||
1103 | spin_unlock_irqrestore(&card->lock, flags); | ||
1104 | return ret; | ||
1105 | case PH_ACTIVATE_REQ: | ||
1106 | ret = l1_event(dch->l1, hh->prim); | ||
1107 | break; | ||
1108 | case PH_DEACTIVATE_REQ: | ||
1109 | test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); | ||
1110 | ret = l1_event(dch->l1, hh->prim); | ||
1111 | break; | ||
1112 | } | ||
1113 | |||
1114 | if (!ret) | ||
1115 | dev_kfree_skb(skb); | ||
1116 | return ret; | ||
1117 | } | ||
1118 | |||
1119 | static int | ||
1120 | w6692_l1callback(struct dchannel *dch, u32 cmd) | ||
1121 | { | ||
1122 | struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); | ||
1123 | u_long flags; | ||
1124 | |||
1125 | pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state); | ||
1126 | switch (cmd) { | ||
1127 | case INFO3_P8: | ||
1128 | spin_lock_irqsave(&card->lock, flags); | ||
1129 | ph_command(card, W_L1CMD_AR8); | ||
1130 | spin_unlock_irqrestore(&card->lock, flags); | ||
1131 | break; | ||
1132 | case INFO3_P10: | ||
1133 | spin_lock_irqsave(&card->lock, flags); | ||
1134 | ph_command(card, W_L1CMD_AR10); | ||
1135 | spin_unlock_irqrestore(&card->lock, flags); | ||
1136 | break; | ||
1137 | case HW_RESET_REQ: | ||
1138 | spin_lock_irqsave(&card->lock, flags); | ||
1139 | if (card->state != W_L1IND_DRD) | ||
1140 | ph_command(card, W_L1CMD_RST); | ||
1141 | ph_command(card, W_L1CMD_ECK); | ||
1142 | spin_unlock_irqrestore(&card->lock, flags); | ||
1143 | break; | ||
1144 | case HW_DEACT_REQ: | ||
1145 | skb_queue_purge(&dch->squeue); | ||
1146 | if (dch->tx_skb) { | ||
1147 | dev_kfree_skb(dch->tx_skb); | ||
1148 | dch->tx_skb = NULL; | ||
1149 | } | ||
1150 | dch->tx_idx = 0; | ||
1151 | if (dch->rx_skb) { | ||
1152 | dev_kfree_skb(dch->rx_skb); | ||
1153 | dch->rx_skb = NULL; | ||
1154 | } | ||
1155 | test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); | ||
1156 | if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) | ||
1157 | del_timer(&dch->timer); | ||
1158 | break; | ||
1159 | case HW_POWERUP_REQ: | ||
1160 | spin_lock_irqsave(&card->lock, flags); | ||
1161 | ph_command(card, W_L1CMD_ECK); | ||
1162 | spin_unlock_irqrestore(&card->lock, flags); | ||
1163 | break; | ||
1164 | case PH_ACTIVATE_IND: | ||
1165 | test_and_set_bit(FLG_ACTIVE, &dch->Flags); | ||
1166 | _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, | ||
1167 | GFP_ATOMIC); | ||
1168 | break; | ||
1169 | case PH_DEACTIVATE_IND: | ||
1170 | test_and_clear_bit(FLG_ACTIVE, &dch->Flags); | ||
1171 | _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, | ||
1172 | GFP_ATOMIC); | ||
1173 | break; | ||
1174 | default: | ||
1175 | pr_debug("%s: %s unknown command %x\n", card->name, | ||
1176 | __func__, cmd); | ||
1177 | return -1; | ||
1178 | } | ||
1179 | return 0; | ||
1180 | } | ||
1181 | |||
1182 | static int | ||
1183 | open_dchannel(struct w6692_hw *card, struct channel_req *rq) | ||
1184 | { | ||
1185 | pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__, | ||
1186 | card->dch.dev.id, __builtin_return_address(1)); | ||
1187 | if (rq->protocol != ISDN_P_TE_S0) | ||
1188 | return -EINVAL; | ||
1189 | if (rq->adr.channel == 1) | ||
1190 | /* E-Channel not supported */ | ||
1191 | return -EINVAL; | ||
1192 | rq->ch = &card->dch.dev.D; | ||
1193 | rq->ch->protocol = rq->protocol; | ||
1194 | if (card->dch.state == 7) | ||
1195 | _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY, | ||
1196 | 0, NULL, GFP_KERNEL); | ||
1197 | return 0; | ||
1198 | } | ||
1199 | |||
1200 | static int | ||
1201 | w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) | ||
1202 | { | ||
1203 | struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); | ||
1204 | struct dchannel *dch = container_of(dev, struct dchannel, dev); | ||
1205 | struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); | ||
1206 | struct channel_req *rq; | ||
1207 | int err = 0; | ||
1208 | |||
1209 | pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg); | ||
1210 | switch (cmd) { | ||
1211 | case OPEN_CHANNEL: | ||
1212 | rq = arg; | ||
1213 | if (rq->protocol == ISDN_P_TE_S0) | ||
1214 | err = open_dchannel(card, rq); | ||
1215 | else | ||
1216 | err = open_bchannel(card, rq); | ||
1217 | if (err) | ||
1218 | break; | ||
1219 | if (!try_module_get(THIS_MODULE)) | ||
1220 | pr_info("%s: cannot get module\n", card->name); | ||
1221 | break; | ||
1222 | case CLOSE_CHANNEL: | ||
1223 | pr_debug("%s: dev(%d) close from %p\n", card->name, | ||
1224 | dch->dev.id, __builtin_return_address(0)); | ||
1225 | module_put(THIS_MODULE); | ||
1226 | break; | ||
1227 | case CONTROL_CHANNEL: | ||
1228 | err = channel_ctrl(card, arg); | ||
1229 | break; | ||
1230 | default: | ||
1231 | pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd); | ||
1232 | return -EINVAL; | ||
1233 | } | ||
1234 | return err; | ||
1235 | } | ||
1236 | |||
1237 | int | ||
1238 | setup_w6692(struct w6692_hw *card) | ||
1239 | { | ||
1240 | u32 val; | ||
1241 | |||
1242 | if (!request_region(card->addr, 256, card->name)) { | ||
1243 | pr_info("%s: config port %x-%x already in use\n", card->name, | ||
1244 | card->addr, card->addr + 255); | ||
1245 | return -EIO; | ||
1246 | } | ||
1247 | W6692Version(card); | ||
1248 | card->bc[0].addr = card->addr; | ||
1249 | card->bc[1].addr = card->addr + 0x40; | ||
1250 | val = ReadW6692(card, W_ISTA); | ||
1251 | if (debug & DEBUG_HW) | ||
1252 | pr_notice("%s ISTA=%02x\n", card->name, val); | ||
1253 | val = ReadW6692(card, W_IMASK); | ||
1254 | if (debug & DEBUG_HW) | ||
1255 | pr_notice("%s IMASK=%02x\n", card->name, val); | ||
1256 | val = ReadW6692(card, W_D_EXIR); | ||
1257 | if (debug & DEBUG_HW) | ||
1258 | pr_notice("%s D_EXIR=%02x\n", card->name, val); | ||
1259 | val = ReadW6692(card, W_D_EXIM); | ||
1260 | if (debug & DEBUG_HW) | ||
1261 | pr_notice("%s D_EXIM=%02x\n", card->name, val); | ||
1262 | val = ReadW6692(card, W_D_RSTA); | ||
1263 | if (debug & DEBUG_HW) | ||
1264 | pr_notice("%s D_RSTA=%02x\n", card->name, val); | ||
1265 | return 0; | ||
1266 | } | ||
1267 | |||
1268 | static void | ||
1269 | release_card(struct w6692_hw *card) | ||
1270 | { | ||
1271 | u_long flags; | ||
1272 | |||
1273 | spin_lock_irqsave(&card->lock, flags); | ||
1274 | disable_hwirq(card); | ||
1275 | w6692_mode(&card->bc[0], ISDN_P_NONE); | ||
1276 | w6692_mode(&card->bc[1], ISDN_P_NONE); | ||
1277 | if ((card->fmask & led) || card->subtype == W6692_USR) { | ||
1278 | card->xdata |= 0x04; /* LED OFF */ | ||
1279 | WriteW6692(card, W_XDATA, card->xdata); | ||
1280 | } | ||
1281 | spin_unlock_irqrestore(&card->lock, flags); | ||
1282 | free_irq(card->irq, card); | ||
1283 | l1_event(card->dch.l1, CLOSE_CHANNEL); | ||
1284 | mISDN_unregister_device(&card->dch.dev); | ||
1285 | release_region(card->addr, 256); | ||
1286 | mISDN_freebchannel(&card->bc[1].bch); | ||
1287 | mISDN_freebchannel(&card->bc[0].bch); | ||
1288 | mISDN_freedchannel(&card->dch); | ||
1289 | write_lock_irqsave(&card_lock, flags); | ||
1290 | list_del(&card->list); | ||
1291 | write_unlock_irqrestore(&card_lock, flags); | ||
1292 | pci_disable_device(card->pdev); | ||
1293 | pci_set_drvdata(card->pdev, NULL); | ||
1294 | kfree(card); | ||
1295 | } | ||
1296 | |||
1297 | static int | ||
1298 | setup_instance(struct w6692_hw *card) | ||
1299 | { | ||
1300 | int i, err; | ||
1301 | u_long flags; | ||
1302 | |||
1303 | snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1); | ||
1304 | write_lock_irqsave(&card_lock, flags); | ||
1305 | list_add_tail(&card->list, &Cards); | ||
1306 | write_unlock_irqrestore(&card_lock, flags); | ||
1307 | card->fmask = (1 << w6692_cnt); | ||
1308 | _set_debug(card); | ||
1309 | spin_lock_init(&card->lock); | ||
1310 | mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh); | ||
1311 | card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0); | ||
1312 | card->dch.dev.D.send = w6692_l2l1D; | ||
1313 | card->dch.dev.D.ctrl = w6692_dctrl; | ||
1314 | card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | | ||
1315 | (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); | ||
1316 | card->dch.hw = card; | ||
1317 | card->dch.dev.nrbchan = 2; | ||
1318 | for (i = 0; i < 2; i++) { | ||
1319 | mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM); | ||
1320 | card->bc[i].bch.hw = card; | ||
1321 | card->bc[i].bch.nr = i + 1; | ||
1322 | card->bc[i].bch.ch.nr = i + 1; | ||
1323 | card->bc[i].bch.ch.send = w6692_l2l1B; | ||
1324 | card->bc[i].bch.ch.ctrl = w6692_bctrl; | ||
1325 | set_channelmap(i + 1, card->dch.dev.channelmap); | ||
1326 | list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels); | ||
1327 | } | ||
1328 | err = setup_w6692(card); | ||
1329 | if (err) | ||
1330 | goto error_setup; | ||
1331 | err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, | ||
1332 | card->name); | ||
1333 | if (err) | ||
1334 | goto error_reg; | ||
1335 | err = init_card(card); | ||
1336 | if (err) | ||
1337 | goto error_init; | ||
1338 | err = create_l1(&card->dch, w6692_l1callback); | ||
1339 | if (!err) { | ||
1340 | w6692_cnt++; | ||
1341 | pr_notice("W6692 %d cards installed\n", w6692_cnt); | ||
1342 | return 0; | ||
1343 | } | ||
1344 | |||
1345 | free_irq(card->irq, card); | ||
1346 | error_init: | ||
1347 | mISDN_unregister_device(&card->dch.dev); | ||
1348 | error_reg: | ||
1349 | release_region(card->addr, 256); | ||
1350 | error_setup: | ||
1351 | mISDN_freebchannel(&card->bc[1].bch); | ||
1352 | mISDN_freebchannel(&card->bc[0].bch); | ||
1353 | mISDN_freedchannel(&card->dch); | ||
1354 | write_lock_irqsave(&card_lock, flags); | ||
1355 | list_del(&card->list); | ||
1356 | write_unlock_irqrestore(&card_lock, flags); | ||
1357 | kfree(card); | ||
1358 | return err; | ||
1359 | } | ||
1360 | |||
1361 | static int __devinit | ||
1362 | w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||
1363 | { | ||
1364 | int err = -ENOMEM; | ||
1365 | struct w6692_hw *card; | ||
1366 | struct w6692map *m = (struct w6692map *)ent->driver_data; | ||
1367 | |||
1368 | card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL); | ||
1369 | if (!card) { | ||
1370 | pr_info("No kmem for w6692 card\n"); | ||
1371 | return err; | ||
1372 | } | ||
1373 | card->pdev = pdev; | ||
1374 | card->subtype = m->subtype; | ||
1375 | err = pci_enable_device(pdev); | ||
1376 | if (err) { | ||
1377 | kfree(card); | ||
1378 | return err; | ||
1379 | } | ||
1380 | |||
1381 | printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n", | ||
1382 | m->name, pci_name(pdev)); | ||
1383 | |||
1384 | card->addr = pci_resource_start(pdev, 1); | ||
1385 | card->irq = pdev->irq; | ||
1386 | pci_set_drvdata(pdev, card); | ||
1387 | err = setup_instance(card); | ||
1388 | if (err) | ||
1389 | pci_set_drvdata(pdev, NULL); | ||
1390 | return err; | ||
1391 | } | ||
1392 | |||
1393 | static void __devexit | ||
1394 | w6692_remove_pci(struct pci_dev *pdev) | ||
1395 | { | ||
1396 | struct w6692_hw *card = pci_get_drvdata(pdev); | ||
1397 | |||
1398 | if (card) | ||
1399 | release_card(card); | ||
1400 | else | ||
1401 | if (debug) | ||
1402 | pr_notice("%s: drvdata allready removed\n", __func__); | ||
1403 | } | ||
1404 | |||
1405 | static struct pci_device_id w6692_ids[] = { | ||
1406 | { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, | ||
1407 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]}, | ||
1408 | { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, | ||
1409 | PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0, | ||
1410 | (ulong)&w6692_map[2]}, | ||
1411 | { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, | ||
1412 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]}, | ||
1413 | { } | ||
1414 | }; | ||
1415 | MODULE_DEVICE_TABLE(pci, w6692_ids); | ||
1416 | |||
1417 | static struct pci_driver w6692_driver = { | ||
1418 | .name = "w6692", | ||
1419 | .probe = w6692_probe, | ||
1420 | .remove = __devexit_p(w6692_remove_pci), | ||
1421 | .id_table = w6692_ids, | ||
1422 | }; | ||
1423 | |||
1424 | static int __init w6692_init(void) | ||
1425 | { | ||
1426 | int err; | ||
1427 | |||
1428 | pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV); | ||
1429 | |||
1430 | err = pci_register_driver(&w6692_driver); | ||
1431 | return err; | ||
1432 | } | ||
1433 | |||
1434 | static void __exit w6692_cleanup(void) | ||
1435 | { | ||
1436 | pci_unregister_driver(&w6692_driver); | ||
1437 | } | ||
1438 | |||
1439 | module_init(w6692_init); | ||
1440 | module_exit(w6692_cleanup); | ||
diff --git a/drivers/isdn/hardware/mISDN/w6692.h b/drivers/isdn/hardware/mISDN/w6692.h new file mode 100644 index 000000000000..f95697757fd0 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/w6692.h | |||
@@ -0,0 +1,190 @@ | |||
1 | /* | ||
2 | * Winbond W6692 specific defines | ||
3 | * | ||
4 | * Author Karsten Keil <keil@isdn4linux.de> | ||
5 | * based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz> | ||
6 | * | ||
7 | * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | /* Specifications of W6692 registers */ | ||
25 | |||
26 | #define W_D_RFIFO 0x00 /* R */ | ||
27 | #define W_D_XFIFO 0x04 /* W */ | ||
28 | #define W_D_CMDR 0x08 /* W */ | ||
29 | #define W_D_MODE 0x0c /* R/W */ | ||
30 | #define W_D_TIMR 0x10 /* R/W */ | ||
31 | #define W_ISTA 0x14 /* R_clr */ | ||
32 | #define W_IMASK 0x18 /* R/W */ | ||
33 | #define W_D_EXIR 0x1c /* R_clr */ | ||
34 | #define W_D_EXIM 0x20 /* R/W */ | ||
35 | #define W_D_STAR 0x24 /* R */ | ||
36 | #define W_D_RSTA 0x28 /* R */ | ||
37 | #define W_D_SAM 0x2c /* R/W */ | ||
38 | #define W_D_SAP1 0x30 /* R/W */ | ||
39 | #define W_D_SAP2 0x34 /* R/W */ | ||
40 | #define W_D_TAM 0x38 /* R/W */ | ||
41 | #define W_D_TEI1 0x3c /* R/W */ | ||
42 | #define W_D_TEI2 0x40 /* R/W */ | ||
43 | #define W_D_RBCH 0x44 /* R */ | ||
44 | #define W_D_RBCL 0x48 /* R */ | ||
45 | #define W_TIMR2 0x4c /* W */ | ||
46 | #define W_L1_RC 0x50 /* R/W */ | ||
47 | #define W_D_CTL 0x54 /* R/W */ | ||
48 | #define W_CIR 0x58 /* R */ | ||
49 | #define W_CIX 0x5c /* W */ | ||
50 | #define W_SQR 0x60 /* R */ | ||
51 | #define W_SQX 0x64 /* W */ | ||
52 | #define W_PCTL 0x68 /* R/W */ | ||
53 | #define W_MOR 0x6c /* R */ | ||
54 | #define W_MOX 0x70 /* R/W */ | ||
55 | #define W_MOSR 0x74 /* R_clr */ | ||
56 | #define W_MOCR 0x78 /* R/W */ | ||
57 | #define W_GCR 0x7c /* R/W */ | ||
58 | |||
59 | #define W_B_RFIFO 0x80 /* R */ | ||
60 | #define W_B_XFIFO 0x84 /* W */ | ||
61 | #define W_B_CMDR 0x88 /* W */ | ||
62 | #define W_B_MODE 0x8c /* R/W */ | ||
63 | #define W_B_EXIR 0x90 /* R_clr */ | ||
64 | #define W_B_EXIM 0x94 /* R/W */ | ||
65 | #define W_B_STAR 0x98 /* R */ | ||
66 | #define W_B_ADM1 0x9c /* R/W */ | ||
67 | #define W_B_ADM2 0xa0 /* R/W */ | ||
68 | #define W_B_ADR1 0xa4 /* R/W */ | ||
69 | #define W_B_ADR2 0xa8 /* R/W */ | ||
70 | #define W_B_RBCL 0xac /* R */ | ||
71 | #define W_B_RBCH 0xb0 /* R */ | ||
72 | |||
73 | #define W_XADDR 0xf4 /* R/W */ | ||
74 | #define W_XDATA 0xf8 /* R/W */ | ||
75 | #define W_EPCTL 0xfc /* W */ | ||
76 | |||
77 | /* W6692 register bits */ | ||
78 | |||
79 | #define W_D_CMDR_XRST 0x01 | ||
80 | #define W_D_CMDR_XME 0x02 | ||
81 | #define W_D_CMDR_XMS 0x08 | ||
82 | #define W_D_CMDR_STT 0x10 | ||
83 | #define W_D_CMDR_RRST 0x40 | ||
84 | #define W_D_CMDR_RACK 0x80 | ||
85 | |||
86 | #define W_D_MODE_RLP 0x01 | ||
87 | #define W_D_MODE_DLP 0x02 | ||
88 | #define W_D_MODE_MFD 0x04 | ||
89 | #define W_D_MODE_TEE 0x08 | ||
90 | #define W_D_MODE_TMS 0x10 | ||
91 | #define W_D_MODE_RACT 0x40 | ||
92 | #define W_D_MODE_MMS 0x80 | ||
93 | |||
94 | #define W_INT_B2_EXI 0x01 | ||
95 | #define W_INT_B1_EXI 0x02 | ||
96 | #define W_INT_D_EXI 0x04 | ||
97 | #define W_INT_XINT0 0x08 | ||
98 | #define W_INT_XINT1 0x10 | ||
99 | #define W_INT_D_XFR 0x20 | ||
100 | #define W_INT_D_RME 0x40 | ||
101 | #define W_INT_D_RMR 0x80 | ||
102 | |||
103 | #define W_D_EXI_WEXP 0x01 | ||
104 | #define W_D_EXI_TEXP 0x02 | ||
105 | #define W_D_EXI_ISC 0x04 | ||
106 | #define W_D_EXI_MOC 0x08 | ||
107 | #define W_D_EXI_TIN2 0x10 | ||
108 | #define W_D_EXI_XCOL 0x20 | ||
109 | #define W_D_EXI_XDUN 0x40 | ||
110 | #define W_D_EXI_RDOV 0x80 | ||
111 | |||
112 | #define W_D_STAR_DRDY 0x10 | ||
113 | #define W_D_STAR_XBZ 0x20 | ||
114 | #define W_D_STAR_XDOW 0x80 | ||
115 | |||
116 | #define W_D_RSTA_RMB 0x10 | ||
117 | #define W_D_RSTA_CRCE 0x20 | ||
118 | #define W_D_RSTA_RDOV 0x40 | ||
119 | |||
120 | #define W_D_CTL_SRST 0x20 | ||
121 | |||
122 | #define W_CIR_SCC 0x80 | ||
123 | #define W_CIR_ICC 0x40 | ||
124 | #define W_CIR_COD_MASK 0x0f | ||
125 | |||
126 | #define W_PCTL_PCX 0x01 | ||
127 | #define W_PCTL_XMODE 0x02 | ||
128 | #define W_PCTL_OE0 0x04 | ||
129 | #define W_PCTL_OE1 0x08 | ||
130 | #define W_PCTL_OE2 0x10 | ||
131 | #define W_PCTL_OE3 0x20 | ||
132 | #define W_PCTL_OE4 0x40 | ||
133 | #define W_PCTL_OE5 0x80 | ||
134 | |||
135 | #define W_B_CMDR_XRST 0x01 | ||
136 | #define W_B_CMDR_XME 0x02 | ||
137 | #define W_B_CMDR_XMS 0x04 | ||
138 | #define W_B_CMDR_RACT 0x20 | ||
139 | #define W_B_CMDR_RRST 0x40 | ||
140 | #define W_B_CMDR_RACK 0x80 | ||
141 | |||
142 | #define W_B_MODE_FTS0 0x01 | ||
143 | #define W_B_MODE_FTS1 0x02 | ||
144 | #define W_B_MODE_SW56 0x04 | ||
145 | #define W_B_MODE_BSW0 0x08 | ||
146 | #define W_B_MODE_BSW1 0x10 | ||
147 | #define W_B_MODE_EPCM 0x20 | ||
148 | #define W_B_MODE_ITF 0x40 | ||
149 | #define W_B_MODE_MMS 0x80 | ||
150 | |||
151 | #define W_B_EXI_XDUN 0x01 | ||
152 | #define W_B_EXI_XFR 0x02 | ||
153 | #define W_B_EXI_RDOV 0x10 | ||
154 | #define W_B_EXI_RME 0x20 | ||
155 | #define W_B_EXI_RMR 0x40 | ||
156 | |||
157 | #define W_B_STAR_XBZ 0x01 | ||
158 | #define W_B_STAR_XDOW 0x04 | ||
159 | #define W_B_STAR_RMB 0x10 | ||
160 | #define W_B_STAR_CRCE 0x20 | ||
161 | #define W_B_STAR_RDOV 0x40 | ||
162 | |||
163 | #define W_B_RBCH_LOV 0x20 | ||
164 | |||
165 | /* W6692 Layer1 commands */ | ||
166 | |||
167 | #define W_L1CMD_ECK 0x00 | ||
168 | #define W_L1CMD_RST 0x01 | ||
169 | #define W_L1CMD_SCP 0x04 | ||
170 | #define W_L1CMD_SSP 0x02 | ||
171 | #define W_L1CMD_AR8 0x08 | ||
172 | #define W_L1CMD_AR10 0x09 | ||
173 | #define W_L1CMD_EAL 0x0a | ||
174 | #define W_L1CMD_DRC 0x0f | ||
175 | |||
176 | /* W6692 Layer1 indications */ | ||
177 | |||
178 | #define W_L1IND_CE 0x07 | ||
179 | #define W_L1IND_DRD 0x00 | ||
180 | #define W_L1IND_LD 0x04 | ||
181 | #define W_L1IND_ARD 0x08 | ||
182 | #define W_L1IND_TI 0x0a | ||
183 | #define W_L1IND_ATI 0x0b | ||
184 | #define W_L1IND_AI8 0x0c | ||
185 | #define W_L1IND_AI10 0x0d | ||
186 | #define W_L1IND_CD 0x0f | ||
187 | |||
188 | /* FIFO thresholds */ | ||
189 | #define W_D_FIFO_THRESH 64 | ||
190 | #define W_B_FIFO_THRESH 64 | ||