1/* $Id: sportster.c,v 1.1.1.1 2007/08/03 18:52:36 Exp $
2 *
3 * low level stuff for USR Sportster internal TA
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 * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
12 *
13 *
14 */
15#include <linux/init.h>
16#include "hisax.h"
17#include "isac.h"
18#include "hscx.h"
19#include "isdnl1.h"
20
21extern const char *CardType[];
22static const char *sportster_revision = "$Revision: 1.1.1.1 $";
23
24#define byteout(addr,val) outb(val,addr)
25#define bytein(addr) inb(addr)
26
27#define	 SPORTSTER_ISAC		0xC000
28#define	 SPORTSTER_HSCXA	0x0000
29#define	 SPORTSTER_HSCXB	0x4000
30#define	 SPORTSTER_RES_IRQ	0x8000
31#define	 SPORTSTER_RESET	0x80
32#define	 SPORTSTER_INTE		0x40
33
34static inline int
35calc_off(unsigned int base, unsigned int off)
36{
37	return(base + ((off & 0xfc)<<8) + ((off & 3)<<1));
38}
39
40static inline void
41read_fifo(unsigned int adr, u_char * data, int size)
42{
43	insb(adr, data, size);
44}
45
46static void
47write_fifo(unsigned int adr, u_char * data, int size)
48{
49	outsb(adr, data, size);
50}
51
52/* Interface functions */
53
54static u_char
55ReadISAC(struct IsdnCardState *cs, u_char offset)
56{
57	return (bytein(calc_off(cs->hw.spt.isac, offset)));
58}
59
60static void
61WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
62{
63	byteout(calc_off(cs->hw.spt.isac, offset), value);
64}
65
66static void
67ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
68{
69	read_fifo(cs->hw.spt.isac, data, size);
70}
71
72static void
73WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
74{
75	write_fifo(cs->hw.spt.isac, data, size);
76}
77
78static u_char
79ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
80{
81	return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset)));
82}
83
84static void
85WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
86{
87	byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value);
88}
89
90/*
91 * fast interrupt HSCX stuff goes here
92 */
93
94#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg))
95#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data)
96#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
97#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
98
99#include "hscx_irq.c"
100
101static irqreturn_t
102sportster_interrupt(int intno, void *dev_id)
103{
104	struct IsdnCardState *cs = dev_id;
105	u_char val;
106	u_long flags;
107
108	spin_lock_irqsave(&cs->lock, flags);
109	val = READHSCX(cs, 1, HSCX_ISTA);
110      Start_HSCX:
111	if (val)
112		hscx_int_main(cs, val);
113	val = ReadISAC(cs, ISAC_ISTA);
114      Start_ISAC:
115	if (val)
116		isac_interrupt(cs, val);
117	val = READHSCX(cs, 1, HSCX_ISTA);
118	if (val) {
119		if (cs->debug & L1_DEB_HSCX)
120			debugl1(cs, "HSCX IntStat after IntRoutine");
121		goto Start_HSCX;
122	}
123	val = ReadISAC(cs, ISAC_ISTA);
124	if (val) {
125		if (cs->debug & L1_DEB_ISAC)
126			debugl1(cs, "ISAC IntStat after IntRoutine");
127		goto Start_ISAC;
128	}
129	/* get a new irq impulse if there any pending */
130	bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1);
131	spin_unlock_irqrestore(&cs->lock, flags);
132	return IRQ_HANDLED;
133}
134
135static void
136release_io_sportster(struct IsdnCardState *cs)
137{
138	int i, adr;
139
140	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0);
141	for (i=0; i<64; i++) {
142		adr = cs->hw.spt.cfg_reg + i *1024;
143		release_region(adr, 8);
144	}
145}
146
147static void
148reset_sportster(struct IsdnCardState *cs)
149{
150	cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */
151	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
152	mdelay(10);
153	cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */
154	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
155	mdelay(10);
156}
157
158static int
159Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg)
160{
161	u_long flags;
162
163	switch (mt) {
164		case CARD_RESET:
165			spin_lock_irqsave(&cs->lock, flags);
166			reset_sportster(cs);
167			spin_unlock_irqrestore(&cs->lock, flags);
168			return(0);
169		case CARD_RELEASE:
170			release_io_sportster(cs);
171			return(0);
172		case CARD_INIT:
173			spin_lock_irqsave(&cs->lock, flags);
174			reset_sportster(cs);
175			inithscxisac(cs, 1);
176			cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */
177			byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
178			inithscxisac(cs, 2);
179			spin_unlock_irqrestore(&cs->lock, flags);
180			return(0);
181		case CARD_TEST:
182			return(0);
183	}
184	return(0);
185}
186
187static int __devinit
188get_io_range(struct IsdnCardState *cs)
189{
190	int i, j, adr;
191
192	for (i=0;i<64;i++) {
193		adr = cs->hw.spt.cfg_reg + i *1024;
194		if (!request_region(adr, 8, "sportster")) {
195			printk(KERN_WARNING
196				"HiSax: %s config port %x-%x already in use\n",
197				CardType[cs->typ], adr, adr + 8);
198			break;
199		}
200	}
201	if (i==64)
202		return(1);
203	else {
204		for (j=0; j<i; j++) {
205			adr = cs->hw.spt.cfg_reg + j *1024;
206			release_region(adr, 8);
207		}
208		return(0);
209	}
210}
211
212int __devinit
213setup_sportster(struct IsdnCard *card)
214{
215	struct IsdnCardState *cs = card->cs;
216	char tmp[64];
217
218	strcpy(tmp, sportster_revision);
219	printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp));
220	if (cs->typ != ISDN_CTYPE_SPORTSTER)
221		return (0);
222
223	cs->hw.spt.cfg_reg = card->para[1];
224	cs->irq = card->para[0];
225	if (!get_io_range(cs))
226		return (0);
227	cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC;
228	cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA;
229	cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB;
230
231	switch(cs->irq) {
232		case 5:	cs->hw.spt.res_irq = 1;
233			break;
234		case 7:	cs->hw.spt.res_irq = 2;
235			break;
236		case 10:cs->hw.spt.res_irq = 3;
237			break;
238		case 11:cs->hw.spt.res_irq = 4;
239			break;
240		case 12:cs->hw.spt.res_irq = 5;
241			break;
242		case 14:cs->hw.spt.res_irq = 6;
243			break;
244		case 15:cs->hw.spt.res_irq = 7;
245			break;
246		default:release_io_sportster(cs);
247			printk(KERN_WARNING "Sportster: wrong IRQ\n");
248			return(0);
249	}
250	printk(KERN_INFO "HiSax: %s config irq:%d cfg:0x%X\n",
251		CardType[cs->typ], cs->irq, cs->hw.spt.cfg_reg);
252	setup_isac(cs);
253	cs->readisac = &ReadISAC;
254	cs->writeisac = &WriteISAC;
255	cs->readisacfifo = &ReadISACfifo;
256	cs->writeisacfifo = &WriteISACfifo;
257	cs->BC_Read_Reg = &ReadHSCX;
258	cs->BC_Write_Reg = &WriteHSCX;
259	cs->BC_Send_Data = &hscx_fill_fifo;
260	cs->cardmsg = &Sportster_card_msg;
261	cs->irq_func = &sportster_interrupt;
262	ISACVersion(cs, "Sportster:");
263	if (HscxVersion(cs, "Sportster:")) {
264		printk(KERN_WARNING
265		       "Sportster: wrong HSCX versions check IO address\n");
266		release_io_sportster(cs);
267		return (0);
268	}
269	return (1);
270}
271