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