Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: ix1_micro.c,v 2.12.2.4 2004/01/13 23:48:39 keil Exp $ |
2 | * | |
3 | * low level stuff for ITK ix1-micro Rev.2 isdn cards | |
4 | * derived from the original file teles3.c from Karsten Keil | |
5 | * | |
6 | * Author Klaus-Peter Nischke | |
7 | * Copyright by Klaus-Peter Nischke, ITK AG | |
8 | * <klaus@nischke.do.eunet.de> | |
9 | * by Karsten Keil <keil@isdn4linux.de> | |
10 | * | |
11 | * This software may be used and distributed according to the terms | |
12 | * of the GNU General Public License, incorporated herein by reference. | |
13 | * | |
14 | * Klaus-Peter Nischke | |
15 | * Deusener Str. 287 | |
16 | * 44369 Dortmund | |
17 | * Germany | |
18 | */ | |
19 | ||
20 | #include <linux/init.h> | |
21 | #include <linux/isapnp.h> | |
22 | #include "hisax.h" | |
23 | #include "isac.h" | |
24 | #include "hscx.h" | |
25 | #include "isdnl1.h" | |
26 | ||
27 | extern const char *CardType[]; | |
672c3fd9 | 28 | static const char *ix1_revision = "$Revision: 2.12.2.4 $"; |
1da177e4 LT |
29 | |
30 | #define byteout(addr,val) outb(val,addr) | |
31 | #define bytein(addr) inb(addr) | |
32 | ||
33 | #define SPECIAL_PORT_OFFSET 3 | |
34 | ||
35 | #define ISAC_COMMAND_OFFSET 2 | |
36 | #define ISAC_DATA_OFFSET 0 | |
37 | #define HSCX_COMMAND_OFFSET 2 | |
38 | #define HSCX_DATA_OFFSET 1 | |
39 | ||
40 | #define TIMEOUT 50 | |
41 | ||
42 | static inline u_char | |
43 | readreg(unsigned int ale, unsigned int adr, u_char off) | |
44 | { | |
45 | register u_char ret; | |
46 | ||
47 | byteout(ale, off); | |
48 | ret = bytein(adr); | |
49 | return (ret); | |
50 | } | |
51 | ||
52 | static inline void | |
53 | readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) | |
54 | { | |
55 | byteout(ale, off); | |
56 | insb(adr, data, size); | |
57 | } | |
58 | ||
59 | ||
60 | static inline void | |
61 | writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) | |
62 | { | |
63 | byteout(ale, off); | |
64 | byteout(adr, data); | |
65 | } | |
66 | ||
67 | static inline void | |
68 | writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) | |
69 | { | |
70 | byteout(ale, off); | |
71 | outsb(adr, data, size); | |
72 | } | |
73 | ||
74 | /* Interface functions */ | |
75 | ||
76 | static u_char | |
77 | ReadISAC(struct IsdnCardState *cs, u_char offset) | |
78 | { | |
79 | return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset)); | |
80 | } | |
81 | ||
82 | static void | |
83 | WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) | |
84 | { | |
85 | writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value); | |
86 | } | |
87 | ||
88 | static void | |
89 | ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) | |
90 | { | |
91 | readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); | |
92 | } | |
93 | ||
94 | static void | |
95 | WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) | |
96 | { | |
97 | writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); | |
98 | } | |
99 | ||
100 | static u_char | |
101 | ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) | |
102 | { | |
103 | return (readreg(cs->hw.ix1.hscx_ale, | |
104 | cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0))); | |
105 | } | |
106 | ||
107 | static void | |
108 | WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) | |
109 | { | |
110 | writereg(cs->hw.ix1.hscx_ale, | |
111 | cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value); | |
112 | } | |
113 | ||
114 | #define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \ | |
115 | cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0)) | |
116 | #define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \ | |
117 | cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data) | |
118 | ||
119 | #define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \ | |
120 | cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) | |
121 | ||
122 | #define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \ | |
123 | cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) | |
124 | ||
125 | #include "hscx_irq.c" | |
126 | ||
127 | static irqreturn_t | |
128 | ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) | |
129 | { | |
130 | struct IsdnCardState *cs = dev_id; | |
131 | u_char val; | |
132 | u_long flags; | |
133 | ||
134 | spin_lock_irqsave(&cs->lock, flags); | |
135 | val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); | |
136 | Start_HSCX: | |
137 | if (val) | |
138 | hscx_int_main(cs, val); | |
139 | val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); | |
140 | Start_ISAC: | |
141 | if (val) | |
142 | isac_interrupt(cs, val); | |
143 | val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); | |
144 | if (val) { | |
145 | if (cs->debug & L1_DEB_HSCX) | |
146 | debugl1(cs, "HSCX IntStat after IntRoutine"); | |
147 | goto Start_HSCX; | |
148 | } | |
149 | val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); | |
150 | if (val) { | |
151 | if (cs->debug & L1_DEB_ISAC) | |
152 | debugl1(cs, "ISAC IntStat after IntRoutine"); | |
153 | goto Start_ISAC; | |
154 | } | |
155 | writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF); | |
156 | writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF); | |
157 | writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF); | |
158 | writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0); | |
159 | writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0); | |
160 | writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0); | |
161 | spin_unlock_irqrestore(&cs->lock, flags); | |
162 | return IRQ_HANDLED; | |
163 | } | |
164 | ||
672c3fd9 | 165 | static void |
1da177e4 LT |
166 | release_io_ix1micro(struct IsdnCardState *cs) |
167 | { | |
168 | if (cs->hw.ix1.cfg_reg) | |
169 | release_region(cs->hw.ix1.cfg_reg, 4); | |
170 | } | |
171 | ||
172 | static void | |
173 | ix1_reset(struct IsdnCardState *cs) | |
174 | { | |
175 | int cnt; | |
176 | ||
177 | /* reset isac */ | |
178 | cnt = 3 * (HZ / 10) + 1; | |
179 | while (cnt--) { | |
180 | byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1); | |
181 | HZDELAY(1); /* wait >=10 ms */ | |
182 | } | |
183 | byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0); | |
184 | } | |
185 | ||
186 | static int | |
187 | ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) | |
188 | { | |
189 | u_long flags; | |
190 | ||
191 | switch (mt) { | |
192 | case CARD_RESET: | |
193 | spin_lock_irqsave(&cs->lock, flags); | |
194 | ix1_reset(cs); | |
195 | spin_unlock_irqrestore(&cs->lock, flags); | |
196 | return(0); | |
197 | case CARD_RELEASE: | |
198 | release_io_ix1micro(cs); | |
199 | return(0); | |
200 | case CARD_INIT: | |
201 | spin_lock_irqsave(&cs->lock, flags); | |
202 | ix1_reset(cs); | |
203 | inithscxisac(cs, 3); | |
204 | spin_unlock_irqrestore(&cs->lock, flags); | |
205 | return(0); | |
206 | case CARD_TEST: | |
207 | return(0); | |
208 | } | |
209 | return(0); | |
210 | } | |
211 | ||
212 | #ifdef __ISAPNP__ | |
213 | static struct isapnp_device_id itk_ids[] __initdata = { | |
214 | { ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25), | |
215 | ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25), | |
216 | (unsigned long) "ITK micro 2" }, | |
217 | { ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29), | |
218 | ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29), | |
219 | (unsigned long) "ITK micro 2." }, | |
220 | { 0, } | |
221 | }; | |
222 | ||
223 | static struct isapnp_device_id *ipid __initdata = &itk_ids[0]; | |
224 | static struct pnp_card *pnp_c __devinitdata = NULL; | |
225 | #endif | |
226 | ||
227 | ||
228 | int __init | |
229 | setup_ix1micro(struct IsdnCard *card) | |
230 | { | |
231 | struct IsdnCardState *cs = card->cs; | |
232 | char tmp[64]; | |
233 | ||
234 | strcpy(tmp, ix1_revision); | |
235 | printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp)); | |
236 | if (cs->typ != ISDN_CTYPE_IX1MICROR2) | |
237 | return (0); | |
238 | ||
239 | #ifdef __ISAPNP__ | |
240 | if (!card->para[1] && isapnp_present()) { | |
241 | struct pnp_dev *pnp_d; | |
242 | while(ipid->card_vendor) { | |
243 | if ((pnp_c = pnp_find_card(ipid->card_vendor, | |
244 | ipid->card_device, pnp_c))) { | |
245 | pnp_d = NULL; | |
246 | if ((pnp_d = pnp_find_dev(pnp_c, | |
247 | ipid->vendor, ipid->function, pnp_d))) { | |
248 | int err; | |
249 | ||
250 | printk(KERN_INFO "HiSax: %s detected\n", | |
251 | (char *)ipid->driver_data); | |
252 | pnp_disable_dev(pnp_d); | |
253 | err = pnp_activate_dev(pnp_d); | |
254 | if (err<0) { | |
255 | printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", | |
256 | __FUNCTION__, err); | |
257 | return(0); | |
258 | } | |
259 | card->para[1] = pnp_port_start(pnp_d, 0); | |
260 | card->para[0] = pnp_irq(pnp_d, 0); | |
261 | if (!card->para[0] || !card->para[1]) { | |
262 | printk(KERN_ERR "ITK PnP:some resources are missing %ld/%lx\n", | |
263 | card->para[0], card->para[1]); | |
264 | pnp_disable_dev(pnp_d); | |
265 | return(0); | |
266 | } | |
267 | break; | |
268 | } else { | |
269 | printk(KERN_ERR "ITK PnP: PnP error card found, no device\n"); | |
270 | } | |
271 | } | |
272 | ipid++; | |
273 | pnp_c = NULL; | |
274 | } | |
275 | if (!ipid->card_vendor) { | |
276 | printk(KERN_INFO "ITK PnP: no ISAPnP card found\n"); | |
277 | return(0); | |
278 | } | |
279 | } | |
280 | #endif | |
281 | /* IO-Ports */ | |
282 | cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET; | |
283 | cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET; | |
284 | cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET; | |
285 | cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET; | |
286 | cs->hw.ix1.cfg_reg = card->para[1]; | |
287 | cs->irq = card->para[0]; | |
288 | if (cs->hw.ix1.cfg_reg) { | |
289 | if (!request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg")) { | |
290 | printk(KERN_WARNING | |
291 | "HiSax: %s config port %x-%x already in use\n", | |
292 | CardType[card->typ], | |
293 | cs->hw.ix1.cfg_reg, | |
294 | cs->hw.ix1.cfg_reg + 4); | |
295 | return (0); | |
296 | } | |
297 | } | |
298 | printk(KERN_INFO "HiSax: %s config irq:%d io:0x%X\n", | |
299 | CardType[cs->typ], cs->irq, cs->hw.ix1.cfg_reg); | |
300 | setup_isac(cs); | |
301 | cs->readisac = &ReadISAC; | |
302 | cs->writeisac = &WriteISAC; | |
303 | cs->readisacfifo = &ReadISACfifo; | |
304 | cs->writeisacfifo = &WriteISACfifo; | |
305 | cs->BC_Read_Reg = &ReadHSCX; | |
306 | cs->BC_Write_Reg = &WriteHSCX; | |
307 | cs->BC_Send_Data = &hscx_fill_fifo; | |
308 | cs->cardmsg = &ix1_card_msg; | |
309 | cs->irq_func = &ix1micro_interrupt; | |
310 | ISACVersion(cs, "ix1-Micro:"); | |
311 | if (HscxVersion(cs, "ix1-Micro:")) { | |
312 | printk(KERN_WARNING | |
313 | "ix1-Micro: wrong HSCX versions check IO address\n"); | |
314 | release_io_ix1micro(cs); | |
315 | return (0); | |
316 | } | |
317 | return (1); | |
318 | } |