Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: hscx.c,v 1.24.2.4 2004/01/24 20:47:23 keil Exp $ |
2 | * | |
3 | * HSCX specific routines | |
4 | * | |
5 | * Author Karsten Keil | |
6 | * Copyright by Karsten Keil <keil@isdn4linux.de> | |
7 | * | |
8 | * This software may be used and distributed according to the terms | |
9 | * of the GNU General Public License, incorporated herein by reference. | |
10 | * | |
11 | */ | |
12 | ||
13 | #include <linux/init.h> | |
14 | #include "hisax.h" | |
15 | #include "hscx.h" | |
16 | #include "isac.h" | |
17 | #include "isdnl1.h" | |
18 | #include <linux/interrupt.h> | |
19 | ||
20 | static char *HSCXVer[] = | |
21 | {"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", | |
22 | "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; | |
23 | ||
24 | int | |
25 | HscxVersion(struct IsdnCardState *cs, char *s) | |
26 | { | |
27 | int verA, verB; | |
28 | ||
29 | verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf; | |
30 | verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf; | |
31 | printk(KERN_INFO "%s HSCX version A: %s B: %s\n", s, | |
32 | HSCXVer[verA], HSCXVer[verB]); | |
33 | if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) | |
34 | return (1); | |
35 | else | |
36 | return (0); | |
37 | } | |
38 | ||
39 | void | |
40 | modehscx(struct BCState *bcs, int mode, int bc) | |
41 | { | |
42 | struct IsdnCardState *cs = bcs->cs; | |
43 | int hscx = bcs->hw.hscx.hscx; | |
44 | ||
45 | if (cs->debug & L1_DEB_HSCX) | |
46 | debugl1(cs, "hscx %c mode %d ichan %d", | |
47 | 'A' + hscx, mode, bc); | |
48 | bcs->mode = mode; | |
49 | bcs->channel = bc; | |
50 | cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF); | |
51 | cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF); | |
52 | cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF); | |
53 | cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0); | |
54 | cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0); | |
55 | cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, | |
56 | test_bit(HW_IPAC, &cs->HW_Flags) ? 0x82 : 0x85); | |
57 | cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30); | |
58 | cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7); | |
59 | cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7); | |
60 | ||
61 | /* Switch IOM 1 SSI */ | |
62 | if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0)) | |
63 | bc = 1 - bc; | |
64 | ||
65 | if (bc == 0) { | |
66 | cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, | |
67 | test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0); | |
68 | cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, | |
69 | test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0); | |
70 | } else { | |
71 | cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, bcs->hw.hscx.tsaxr1); | |
72 | cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, bcs->hw.hscx.tsaxr1); | |
73 | } | |
74 | switch (mode) { | |
75 | case (L1_MODE_NULL): | |
76 | cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x1f); | |
77 | cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x1f); | |
78 | cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84); | |
79 | break; | |
80 | case (L1_MODE_TRANS): | |
81 | cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4); | |
82 | break; | |
83 | case (L1_MODE_HDLC): | |
84 | cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, | |
85 | test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d); | |
86 | cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c); | |
87 | break; | |
88 | } | |
89 | if (mode) | |
90 | cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41); | |
91 | cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00); | |
92 | } | |
93 | ||
94 | void | |
95 | hscx_l2l1(struct PStack *st, int pr, void *arg) | |
96 | { | |
97 | struct BCState *bcs = st->l1.bcs; | |
98 | u_long flags; | |
99 | struct sk_buff *skb = arg; | |
100 | ||
101 | switch (pr) { | |
102 | case (PH_DATA | REQUEST): | |
103 | spin_lock_irqsave(&bcs->cs->lock, flags); | |
104 | if (bcs->tx_skb) { | |
105 | skb_queue_tail(&bcs->squeue, skb); | |
106 | } else { | |
107 | bcs->tx_skb = skb; | |
108 | test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); | |
109 | bcs->hw.hscx.count = 0; | |
110 | bcs->cs->BC_Send_Data(bcs); | |
111 | } | |
112 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | |
113 | break; | |
114 | case (PH_PULL | INDICATION): | |
115 | spin_lock_irqsave(&bcs->cs->lock, flags); | |
116 | if (bcs->tx_skb) { | |
117 | printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n"); | |
118 | } else { | |
119 | test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); | |
120 | bcs->tx_skb = skb; | |
121 | bcs->hw.hscx.count = 0; | |
122 | bcs->cs->BC_Send_Data(bcs); | |
123 | } | |
124 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | |
125 | break; | |
126 | case (PH_PULL | REQUEST): | |
127 | if (!bcs->tx_skb) { | |
128 | test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); | |
129 | st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); | |
130 | } else | |
131 | test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); | |
132 | break; | |
133 | case (PH_ACTIVATE | REQUEST): | |
134 | spin_lock_irqsave(&bcs->cs->lock, flags); | |
135 | test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag); | |
136 | modehscx(bcs, st->l1.mode, st->l1.bc); | |
137 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | |
138 | l1_msg_b(st, pr, arg); | |
139 | break; | |
140 | case (PH_DEACTIVATE | REQUEST): | |
141 | l1_msg_b(st, pr, arg); | |
142 | break; | |
143 | case (PH_DEACTIVATE | CONFIRM): | |
144 | spin_lock_irqsave(&bcs->cs->lock, flags); | |
145 | test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag); | |
146 | test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | |
147 | modehscx(bcs, 0, st->l1.bc); | |
148 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | |
149 | st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); | |
150 | break; | |
151 | } | |
152 | } | |
153 | ||
672c3fd9 | 154 | static void |
1da177e4 LT |
155 | close_hscxstate(struct BCState *bcs) |
156 | { | |
157 | modehscx(bcs, 0, bcs->channel); | |
158 | if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { | |
159 | if (bcs->hw.hscx.rcvbuf) { | |
160 | kfree(bcs->hw.hscx.rcvbuf); | |
161 | bcs->hw.hscx.rcvbuf = NULL; | |
162 | } | |
163 | if (bcs->blog) { | |
164 | kfree(bcs->blog); | |
165 | bcs->blog = NULL; | |
166 | } | |
167 | skb_queue_purge(&bcs->rqueue); | |
168 | skb_queue_purge(&bcs->squeue); | |
169 | if (bcs->tx_skb) { | |
170 | dev_kfree_skb_any(bcs->tx_skb); | |
171 | bcs->tx_skb = NULL; | |
172 | test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | int | |
178 | open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs) | |
179 | { | |
180 | if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { | |
181 | if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { | |
182 | printk(KERN_WARNING | |
183 | "HiSax: No memory for hscx.rcvbuf\n"); | |
184 | test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); | |
185 | return (1); | |
186 | } | |
187 | if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) { | |
188 | printk(KERN_WARNING | |
189 | "HiSax: No memory for bcs->blog\n"); | |
190 | test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); | |
191 | kfree(bcs->hw.hscx.rcvbuf); | |
192 | bcs->hw.hscx.rcvbuf = NULL; | |
193 | return (2); | |
194 | } | |
195 | skb_queue_head_init(&bcs->rqueue); | |
196 | skb_queue_head_init(&bcs->squeue); | |
197 | } | |
198 | bcs->tx_skb = NULL; | |
199 | test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | |
200 | bcs->event = 0; | |
201 | bcs->hw.hscx.rcvidx = 0; | |
202 | bcs->tx_cnt = 0; | |
203 | return (0); | |
204 | } | |
205 | ||
672c3fd9 | 206 | static int |
1da177e4 LT |
207 | setstack_hscx(struct PStack *st, struct BCState *bcs) |
208 | { | |
209 | bcs->channel = st->l1.bc; | |
210 | if (open_hscxstate(st->l1.hardware, bcs)) | |
211 | return (-1); | |
212 | st->l1.bcs = bcs; | |
213 | st->l2.l2l1 = hscx_l2l1; | |
214 | setstack_manager(st); | |
215 | bcs->st = st; | |
216 | setstack_l1_B(st); | |
217 | return (0); | |
218 | } | |
219 | ||
220 | void | |
221 | clear_pending_hscx_ints(struct IsdnCardState *cs) | |
222 | { | |
223 | int val, eval; | |
224 | ||
225 | val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA); | |
226 | debugl1(cs, "HSCX B ISTA %x", val); | |
227 | if (val & 0x01) { | |
228 | eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR); | |
229 | debugl1(cs, "HSCX B EXIR %x", eval); | |
230 | } | |
231 | if (val & 0x02) { | |
232 | eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR); | |
233 | debugl1(cs, "HSCX A EXIR %x", eval); | |
234 | } | |
235 | val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA); | |
236 | debugl1(cs, "HSCX A ISTA %x", val); | |
237 | val = cs->BC_Read_Reg(cs, 1, HSCX_STAR); | |
238 | debugl1(cs, "HSCX B STAR %x", val); | |
239 | val = cs->BC_Read_Reg(cs, 0, HSCX_STAR); | |
240 | debugl1(cs, "HSCX A STAR %x", val); | |
241 | /* disable all IRQ */ | |
242 | cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF); | |
243 | cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF); | |
244 | } | |
245 | ||
246 | void | |
247 | inithscx(struct IsdnCardState *cs) | |
248 | { | |
249 | cs->bcs[0].BC_SetStack = setstack_hscx; | |
250 | cs->bcs[1].BC_SetStack = setstack_hscx; | |
251 | cs->bcs[0].BC_Close = close_hscxstate; | |
252 | cs->bcs[1].BC_Close = close_hscxstate; | |
253 | cs->bcs[0].hw.hscx.hscx = 0; | |
254 | cs->bcs[1].hw.hscx.hscx = 1; | |
255 | cs->bcs[0].hw.hscx.tsaxr0 = 0x2f; | |
256 | cs->bcs[0].hw.hscx.tsaxr1 = 3; | |
257 | cs->bcs[1].hw.hscx.tsaxr0 = 0x2f; | |
258 | cs->bcs[1].hw.hscx.tsaxr1 = 3; | |
259 | modehscx(cs->bcs, 0, 0); | |
260 | modehscx(cs->bcs + 1, 0, 0); | |
261 | } | |
262 | ||
263 | void | |
264 | inithscxisac(struct IsdnCardState *cs, int part) | |
265 | { | |
266 | if (part & 1) { | |
267 | clear_pending_isac_ints(cs); | |
268 | clear_pending_hscx_ints(cs); | |
269 | initisac(cs); | |
270 | inithscx(cs); | |
271 | } | |
272 | if (part & 2) { | |
273 | /* Reenable all IRQ */ | |
274 | cs->writeisac(cs, ISAC_MASK, 0); | |
275 | cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0); | |
276 | cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0); | |
277 | /* RESET Receiver and Transmitter */ | |
278 | cs->writeisac(cs, ISAC_CMDR, 0x41); | |
279 | } | |
280 | } |