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 static const char *ix1_revision = "$Revision: 2.12.2.4 $";
28 
29 #define byteout(addr, val) outb(val, addr)
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
readreg(unsigned int ale,unsigned int adr,u_char off)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
readfifo(unsigned int ale,unsigned int adr,u_char off,u_char * data,int size)52 readfifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
53 {
54 	byteout(ale, off);
55 	insb(adr, data, size);
56 }
57 
58 
59 static inline void
writereg(unsigned int ale,unsigned int adr,u_char off,u_char data)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
writefifo(unsigned int ale,unsigned int adr,u_char off,u_char * data,int size)67 writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
68 {
69 	byteout(ale, off);
70 	outsb(adr, data, size);
71 }
72 
73 /* Interface functions */
74 
75 static u_char
ReadISAC(struct IsdnCardState * cs,u_char offset)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
WriteISAC(struct IsdnCardState * cs,u_char offset,u_char value)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
ReadISACfifo(struct IsdnCardState * cs,u_char * data,int size)88 ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
89 {
90 	readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
91 }
92 
93 static void
WriteISACfifo(struct IsdnCardState * cs,u_char * data,int size)94 WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
95 {
96 	writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
97 }
98 
99 static u_char
ReadHSCX(struct IsdnCardState * cs,int hscx,u_char offset)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
WriteHSCX(struct IsdnCardState * cs,int hscx,u_char offset,u_char value)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 
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)
117 
118 #define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale,	\
119 						cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
120 
121 #define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale,	\
122 						  cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
123 
124 #include "hscx_irq.c"
125 
126 static irqreturn_t
ix1micro_interrupt(int intno,void * dev_id)127 ix1micro_interrupt(int intno, void *dev_id)
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);
135 Start_HSCX:
136 	if (val)
137 		hscx_int_main(cs, val);
138 	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
139 Start_ISAC:
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 
164 static void
release_io_ix1micro(struct IsdnCardState * cs)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
ix1_reset(struct IsdnCardState * cs)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
ix1_card_msg(struct IsdnCardState * cs,int mt,void * arg)186 ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg)
187 {
188 	u_long flags;
189 
190 	switch (mt) {
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);
207 	}
208 	return (0);
209 }
210 
211 #ifdef __ISAPNP__
212 static struct isapnp_device_id itk_ids[] = {
213 	{ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25),
214 	  ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25),
215 	  (unsigned long) "ITK micro 2" },
216 	{ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29),
217 	  ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29),
218 	  (unsigned long) "ITK micro 2." },
219 	{ 0, }
220 };
221 
222 static struct isapnp_device_id *ipid = &itk_ids[0];
223 static struct pnp_card *pnp_c = NULL;
224 #endif
225 
226 
setup_ix1micro(struct IsdnCard * card)227 int setup_ix1micro(struct IsdnCard *card)
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;
240 		while (ipid->card_vendor) {
241 			if ((pnp_c = pnp_find_card(ipid->card_vendor,
242 						   ipid->card_device, pnp_c))) {
243 				pnp_d = NULL;
244 				if ((pnp_d = pnp_find_dev(pnp_c,
245 							  ipid->vendor, ipid->function, pnp_d))) {
246 					int err;
247 
248 					printk(KERN_INFO "HiSax: %s detected\n",
249 					       (char *)ipid->driver_data);
250 					pnp_disable_dev(pnp_d);
251 					err = pnp_activate_dev(pnp_d);
252 					if (err < 0) {
253 						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
254 						       __func__, err);
255 						return (0);
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] == -1 || !card->para[1]) {
260 						printk(KERN_ERR "ITK PnP:some resources are missing %ld/%lx\n",
261 						       card->para[0], card->para[1]);
262 						pnp_disable_dev(pnp_d);
263 						return (0);
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;
272 		}
273 		if (!ipid->card_vendor) {
274 			printk(KERN_INFO "ITK PnP: no ISAPnP card found\n");
275 			return (0);
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
289 			       "HiSax: ITK ix1-micro Rev.2 config port "
290 			       "%x-%x already in use\n",
291 			       cs->hw.ix1.cfg_reg,
292 			       cs->hw.ix1.cfg_reg + 4);
293 			return (0);
294 		}
295 	}
296 	printk(KERN_INFO "HiSax: ITK ix1-micro Rev.2 config irq:%d io:0x%X\n",
297 	       cs->irq, cs->hw.ix1.cfg_reg);
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
311 		       "ix1-Micro: wrong HSCX versions check IO address\n");
312 		release_io_ix1micro(cs);
313 		return (0);
314 	}
315 	return (1);
316 }
317