Merge branch 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6
[linux-2.6] / drivers / isdn / hisax / sportster.c
1 /* $Id: sportster.c,v 1.16.2.4 2004/01/13 23:48:39 keil 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
21 extern const char *CardType[];
22 static const char *sportster_revision = "$Revision: 1.16.2.4 $";
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
34 static inline int
35 calc_off(unsigned int base, unsigned int off)
36 {
37         return(base + ((off & 0xfc)<<8) + ((off & 3)<<1));
38 }
39
40 static inline void
41 read_fifo(unsigned int adr, u_char * data, int size)
42 {
43         insb(adr, data, size);
44 }
45
46 static void
47 write_fifo(unsigned int adr, u_char * data, int size)
48 {
49         outsb(adr, data, size);
50 }
51
52 /* Interface functions */
53
54 static u_char
55 ReadISAC(struct IsdnCardState *cs, u_char offset)
56 {
57         return (bytein(calc_off(cs->hw.spt.isac, offset)));
58 }
59
60 static void
61 WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
62 {
63         byteout(calc_off(cs->hw.spt.isac, offset), value);
64 }
65
66 static void
67 ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
68 {
69         read_fifo(cs->hw.spt.isac, data, size);
70 }
71
72 static void
73 WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
74 {
75         write_fifo(cs->hw.spt.isac, data, size);
76 }
77
78 static u_char
79 ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
80 {
81         return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset)));
82 }
83
84 static void
85 WriteHSCX(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
101 static irqreturn_t
102 sportster_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
135 static void
136 release_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
147 static void
148 reset_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
158 static int
159 Sportster_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
187 static int __devinit
188 get_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
212 int __devinit
213 setup_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 }