1/* $Id: avm_a1.c,v 1.1.1.1 2008/10/15 03:26:33 james26_jang Exp $
2 *
3 * low level stuff for AVM A1 (Fritz) isdn cards
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#define __NO_VERSION__
14#include <linux/init.h>
15#include "hisax.h"
16#include "isac.h"
17#include "hscx.h"
18#include "isdnl1.h"
19
20extern const char *CardType[];
21static const char *avm_revision = "$Revision: 1.1.1.1 $";
22
23#define	 AVM_A1_STAT_ISAC	0x01
24#define	 AVM_A1_STAT_HSCX	0x02
25#define	 AVM_A1_STAT_TIMER	0x04
26
27#define byteout(addr,val) outb(val,addr)
28#define bytein(addr) inb(addr)
29
30static inline u_char
31readreg(unsigned int adr, u_char off)
32{
33	return (bytein(adr + off));
34}
35
36static inline void
37writereg(unsigned int adr, u_char off, u_char data)
38{
39	byteout(adr + off, data);
40}
41
42
43static inline void
44read_fifo(unsigned int adr, u_char * data, int size)
45{
46	insb(adr, data, size);
47}
48
49static void
50write_fifo(unsigned int adr, u_char * data, int size)
51{
52	outsb(adr, data, size);
53}
54
55/* Interface functions */
56
57static u_char
58ReadISAC(struct IsdnCardState *cs, u_char offset)
59{
60	return (readreg(cs->hw.avm.isac, offset));
61}
62
63static void
64WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
65{
66	writereg(cs->hw.avm.isac, offset, value);
67}
68
69static void
70ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
71{
72	read_fifo(cs->hw.avm.isacfifo, data, size);
73}
74
75static void
76WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
77{
78	write_fifo(cs->hw.avm.isacfifo, data, size);
79}
80
81static u_char
82ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
83{
84	return (readreg(cs->hw.avm.hscx[hscx], offset));
85}
86
87static void
88WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
89{
90	writereg(cs->hw.avm.hscx[hscx], offset, value);
91}
92
93/*
94 * fast interrupt HSCX stuff goes here
95 */
96
97#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg)
98#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data)
99#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
100#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
101
102#include "hscx_irq.c"
103
104static void
105avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs)
106{
107	struct IsdnCardState *cs = dev_id;
108	u_char val, sval;
109
110	if (!cs) {
111		printk(KERN_WARNING "AVM A1: Spurious interrupt!\n");
112		return;
113	}
114	while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) {
115		if (!(sval & AVM_A1_STAT_TIMER)) {
116			byteout(cs->hw.avm.cfg_reg, 0x1E);
117			sval = bytein(cs->hw.avm.cfg_reg);
118		} else if (cs->debug & L1_DEB_INTSTAT)
119			debugl1(cs, "avm IntStatus %x", sval);
120		if (!(sval & AVM_A1_STAT_HSCX)) {
121			val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
122			if (val)
123				hscx_int_main(cs, val);
124		}
125		if (!(sval & AVM_A1_STAT_ISAC)) {
126			val = readreg(cs->hw.avm.isac, ISAC_ISTA);
127			if (val)
128				isac_interrupt(cs, val);
129		}
130	}
131	writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF);
132	writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF);
133	writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF);
134	writereg(cs->hw.avm.isac, ISAC_MASK, 0x0);
135	writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0);
136	writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0);
137}
138
139inline static void
140release_ioregs(struct IsdnCardState *cs, int mask)
141{
142	release_region(cs->hw.avm.cfg_reg, 8);
143	if (mask & 1)
144		release_region(cs->hw.avm.isac + 32, 32);
145	if (mask & 2)
146		release_region(cs->hw.avm.isacfifo, 1);
147	if (mask & 4)
148		release_region(cs->hw.avm.hscx[0] + 32, 32);
149	if (mask & 8)
150		release_region(cs->hw.avm.hscxfifo[0], 1);
151	if (mask & 0x10)
152		release_region(cs->hw.avm.hscx[1] + 32, 32);
153	if (mask & 0x20)
154		release_region(cs->hw.avm.hscxfifo[1], 1);
155}
156
157static int
158AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
159{
160	switch (mt) {
161		case CARD_RESET:
162			return(0);
163		case CARD_RELEASE:
164			release_ioregs(cs, 0x3f);
165			return(0);
166		case CARD_INIT:
167			inithscxisac(cs, 1);
168			byteout(cs->hw.avm.cfg_reg, 0x16);
169			byteout(cs->hw.avm.cfg_reg, 0x1E);
170			inithscxisac(cs, 2);
171			return(0);
172		case CARD_TEST:
173			return(0);
174	}
175	return(0);
176}
177
178int __init
179setup_avm_a1(struct IsdnCard *card)
180{
181	u_char val;
182	struct IsdnCardState *cs = card->cs;
183	long flags;
184	char tmp[64];
185
186	strcpy(tmp, avm_revision);
187	printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
188	if (cs->typ != ISDN_CTYPE_A1)
189		return (0);
190
191	cs->hw.avm.cfg_reg = card->para[1] + 0x1800;
192	cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20;
193	cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20;
194	cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20;
195	cs->hw.avm.isacfifo = card->para[1] + 0x1000;
196	cs->hw.avm.hscxfifo[0] = card->para[1];
197	cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800;
198	cs->irq = card->para[0];
199	if (check_region((cs->hw.avm.cfg_reg), 8)) {
200		printk(KERN_WARNING
201		       "HiSax: %s config port %x-%x already in use\n",
202		       CardType[card->typ],
203		       cs->hw.avm.cfg_reg,
204		       cs->hw.avm.cfg_reg + 8);
205		return (0);
206	} else {
207		request_region(cs->hw.avm.cfg_reg, 8, "avm cfg");
208	}
209	if (check_region((cs->hw.avm.isac + 32), 32)) {
210		printk(KERN_WARNING
211		       "HiSax: %s isac ports %x-%x already in use\n",
212		       CardType[cs->typ],
213		       cs->hw.avm.isac + 32,
214		       cs->hw.avm.isac + 64);
215		release_ioregs(cs, 0);
216		return (0);
217	} else {
218		request_region(cs->hw.avm.isac + 32, 32, "HiSax isac");
219	}
220	if (check_region((cs->hw.avm.isacfifo), 1)) {
221		printk(KERN_WARNING
222		       "HiSax: %s isac fifo port %x already in use\n",
223		       CardType[cs->typ],
224		       cs->hw.avm.isacfifo);
225		release_ioregs(cs, 1);
226		return (0);
227	} else {
228		request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo");
229	}
230	if (check_region((cs->hw.avm.hscx[0]) + 32, 32)) {
231		printk(KERN_WARNING
232		       "HiSax: %s hscx A ports %x-%x already in use\n",
233		       CardType[cs->typ],
234		       cs->hw.avm.hscx[0] + 32,
235		       cs->hw.avm.hscx[0] + 64);
236		release_ioregs(cs, 3);
237		return (0);
238	} else {
239		request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A");
240	}
241	if (check_region(cs->hw.avm.hscxfifo[0], 1)) {
242		printk(KERN_WARNING
243		       "HiSax: %s hscx A fifo port %x already in use\n",
244		       CardType[cs->typ],
245		       cs->hw.avm.hscxfifo[0]);
246		release_ioregs(cs, 7);
247		return (0);
248	} else {
249		request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo");
250	}
251	if (check_region(cs->hw.avm.hscx[1] + 32, 32)) {
252		printk(KERN_WARNING
253		       "HiSax: %s hscx B ports %x-%x already in use\n",
254		       CardType[cs->typ],
255		       cs->hw.avm.hscx[1] + 32,
256		       cs->hw.avm.hscx[1] + 64);
257		release_ioregs(cs, 0xf);
258		return (0);
259	} else {
260		request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B");
261	}
262	if (check_region(cs->hw.avm.hscxfifo[1], 1)) {
263		printk(KERN_WARNING
264		       "HiSax: %s hscx B fifo port %x already in use\n",
265		       CardType[cs->typ],
266		       cs->hw.avm.hscxfifo[1]);
267		release_ioregs(cs, 0x1f);
268		return (0);
269	} else {
270		request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo");
271	}
272	save_flags(flags);
273	byteout(cs->hw.avm.cfg_reg, 0x0);
274	sti();
275	HZDELAY(HZ / 5 + 1);
276	byteout(cs->hw.avm.cfg_reg, 0x1);
277	HZDELAY(HZ / 5 + 1);
278	byteout(cs->hw.avm.cfg_reg, 0x0);
279	HZDELAY(HZ / 5 + 1);
280	val = cs->irq;
281	if (val == 9)
282		val = 2;
283	byteout(cs->hw.avm.cfg_reg + 1, val);
284	HZDELAY(HZ / 5 + 1);
285	byteout(cs->hw.avm.cfg_reg, 0x0);
286	HZDELAY(HZ / 5 + 1);
287	restore_flags(flags);
288
289	val = bytein(cs->hw.avm.cfg_reg);
290	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
291	       cs->hw.avm.cfg_reg, val);
292	val = bytein(cs->hw.avm.cfg_reg + 3);
293	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
294	       cs->hw.avm.cfg_reg + 3, val);
295	val = bytein(cs->hw.avm.cfg_reg + 2);
296	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
297	       cs->hw.avm.cfg_reg + 2, val);
298	val = bytein(cs->hw.avm.cfg_reg);
299	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
300	       cs->hw.avm.cfg_reg, val);
301
302	printk(KERN_INFO
303	       "HiSax: %s config irq:%d cfg:0x%X\n",
304	       CardType[cs->typ], cs->irq,
305	       cs->hw.avm.cfg_reg);
306	printk(KERN_INFO
307	       "HiSax: isac:0x%X/0x%X\n",
308	       cs->hw.avm.isac + 32, cs->hw.avm.isacfifo);
309	printk(KERN_INFO
310	       "HiSax: hscx A:0x%X/0x%X  hscx B:0x%X/0x%X\n",
311	       cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0],
312	       cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]);
313
314	cs->readisac = &ReadISAC;
315	cs->writeisac = &WriteISAC;
316	cs->readisacfifo = &ReadISACfifo;
317	cs->writeisacfifo = &WriteISACfifo;
318	cs->BC_Read_Reg = &ReadHSCX;
319	cs->BC_Write_Reg = &WriteHSCX;
320	cs->BC_Send_Data = &hscx_fill_fifo;
321	cs->cardmsg = &AVM_card_msg;
322	cs->irq_func = &avm_a1_interrupt;
323	ISACVersion(cs, "AVM A1:");
324	if (HscxVersion(cs, "AVM A1:")) {
325		printk(KERN_WARNING
326		       "AVM A1: wrong HSCX versions check IO address\n");
327		release_ioregs(cs, 0x3f);
328		return (0);
329	}
330	return (1);
331}
332