55de06953540339103bad27f8a3ec18d2469f002
[linux-2.6.git] / drivers / isdn / hisax / isurf.c
1 /* $Id: isurf.c,v 1.12.2.4 2004/01/13 21:46:03 keil Exp $
2  *
3  * low level stuff for Siemens I-Surf/I-Talk 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 #include <linux/init.h>
14 #include "hisax.h"
15 #include "isac.h"
16 #include "isar.h"
17 #include "isdnl1.h"
18 #include <linux/isapnp.h>
19
20 extern const char *CardType[];
21
22 static const char *ISurf_revision = "$Revision: 1.12.2.4 $";
23
24 #define byteout(addr,val) outb(val,addr)
25 #define bytein(addr) inb(addr)
26
27 #define ISURF_ISAR_RESET        1
28 #define ISURF_ISAC_RESET        2
29 #define ISURF_ISAR_EA           4
30 #define ISURF_ARCOFI_RESET      8
31 #define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET)
32
33 #define ISURF_ISAR_OFFSET       0
34 #define ISURF_ISAC_OFFSET       0x100
35 #define ISURF_IOMEM_SIZE        0x400
36 /* Interface functions */
37
38 static u_char
39 ReadISAC(struct IsdnCardState *cs, u_char offset)
40 {
41         return (readb(cs->hw.isurf.isac + offset));
42 }
43
44 static void
45 WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
46 {
47         writeb(value, cs->hw.isurf.isac + offset); mb();
48 }
49
50 static void
51 ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
52 {
53         register int i;
54         for (i = 0; i < size; i++)
55                 data[i] = readb(cs->hw.isurf.isac);
56 }
57
58 static void
59 WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
60 {
61         register int i;
62         for (i = 0; i < size; i++){
63                 writeb(data[i], cs->hw.isurf.isac);mb();
64         }
65 }
66
67 /* ISAR access routines
68  * mode = 0 access with IRQ on
69  * mode = 1 access with IRQ off
70  * mode = 2 access with IRQ off and using last offset
71  */
72   
73 static u_char
74 ReadISAR(struct IsdnCardState *cs, int mode, u_char offset)
75 {       
76         return(readb(cs->hw.isurf.isar + offset));
77 }
78
79 static void
80 WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
81 {
82         writeb(value, cs->hw.isurf.isar + offset);mb();
83 }
84
85 static irqreturn_t
86 isurf_interrupt(int intno, void *dev_id)
87 {
88         struct IsdnCardState *cs = dev_id;
89         u_char val;
90         int cnt = 5;
91         u_long flags;
92
93         spin_lock_irqsave(&cs->lock, flags);
94         val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
95       Start_ISAR:
96         if (val & ISAR_IRQSTA)
97                 isar_int_main(cs);
98         val = readb(cs->hw.isurf.isac + ISAC_ISTA);
99       Start_ISAC:
100         if (val)
101                 isac_interrupt(cs, val);
102         val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
103         if ((val & ISAR_IRQSTA) && --cnt) {
104                 if (cs->debug & L1_DEB_HSCX)
105                         debugl1(cs, "ISAR IntStat after IntRoutine");
106                 goto Start_ISAR;
107         }
108         val = readb(cs->hw.isurf.isac + ISAC_ISTA);
109         if (val && --cnt) {
110                 if (cs->debug & L1_DEB_ISAC)
111                         debugl1(cs, "ISAC IntStat after IntRoutine");
112                 goto Start_ISAC;
113         }
114         if (!cnt)
115                 printk(KERN_WARNING "ISurf IRQ LOOP\n");
116
117         writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
118         writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK);mb();
119         writeb(0, cs->hw.isurf.isac + ISAC_MASK);mb();
120         writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
121         spin_unlock_irqrestore(&cs->lock, flags);
122         return IRQ_HANDLED;
123 }
124
125 static void
126 release_io_isurf(struct IsdnCardState *cs)
127 {
128         release_region(cs->hw.isurf.reset, 1);
129         iounmap(cs->hw.isurf.isar);
130         release_mem_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
131 }
132
133 static void
134 reset_isurf(struct IsdnCardState *cs, u_char chips)
135 {
136         printk(KERN_INFO "ISurf: resetting card\n");
137
138         byteout(cs->hw.isurf.reset, chips); /* Reset On */
139         mdelay(10);
140         byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */
141         mdelay(10);
142 }
143
144 static int
145 ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg)
146 {
147         u_long flags;
148
149         switch (mt) {
150                 case CARD_RESET:
151                         spin_lock_irqsave(&cs->lock, flags);
152                         reset_isurf(cs, ISURF_RESET);
153                         spin_unlock_irqrestore(&cs->lock, flags);
154                         return(0);
155                 case CARD_RELEASE:
156                         release_io_isurf(cs);
157                         return(0);
158                 case CARD_INIT:
159                         spin_lock_irqsave(&cs->lock, flags);
160                         reset_isurf(cs, ISURF_RESET);
161                         clear_pending_isac_ints(cs);
162                         writeb(0, cs->hw.isurf.isar+ISAR_IRQBIT);mb();
163                         initisac(cs);
164                         initisar(cs);
165                         /* Reenable ISAC IRQ */
166                         cs->writeisac(cs, ISAC_MASK, 0);
167                         /* RESET Receiver and Transmitter */
168                         cs->writeisac(cs, ISAC_CMDR, 0x41);
169                         spin_unlock_irqrestore(&cs->lock, flags);
170                         return(0);
171                 case CARD_TEST:
172                         return(0);
173         }
174         return(0);
175 }
176
177 static int
178 isurf_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
179         int ret;
180         u_long flags;
181
182         if ((ic->command == ISDN_CMD_IOCTL) && (ic->arg == 9)) {
183                 ret = isar_auxcmd(cs, ic);
184                 spin_lock_irqsave(&cs->lock, flags);
185                 if (!ret) {
186                         reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET |
187                                 ISURF_ARCOFI_RESET);
188                         initisac(cs);
189                         cs->writeisac(cs, ISAC_MASK, 0);
190                         cs->writeisac(cs, ISAC_CMDR, 0x41);
191                 }
192                 spin_unlock_irqrestore(&cs->lock, flags);
193                 return(ret);
194         }
195         return(isar_auxcmd(cs, ic));
196 }
197
198 #ifdef __ISAPNP__
199 static struct pnp_card *pnp_c __devinitdata = NULL;
200 #endif
201
202 int __devinit
203 setup_isurf(struct IsdnCard *card)
204 {
205         int ver;
206         struct IsdnCardState *cs = card->cs;
207         char tmp[64];
208
209         strcpy(tmp, ISurf_revision);
210         printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp));
211         
212         if (cs->typ != ISDN_CTYPE_ISURF)
213                 return(0);
214         if (card->para[1] && card->para[2]) {
215                 cs->hw.isurf.reset = card->para[1];
216                 cs->hw.isurf.phymem = card->para[2];
217                 cs->irq = card->para[0];
218         } else {
219 #ifdef __ISAPNP__
220                 if (isapnp_present()) {
221                         struct pnp_dev *pnp_d = NULL;
222                         int err;
223
224                         cs->subtyp = 0;
225                         if ((pnp_c = pnp_find_card(
226                                 ISAPNP_VENDOR('S', 'I', 'E'),
227                                 ISAPNP_FUNCTION(0x0010), pnp_c))) {
228                                 if (!(pnp_d = pnp_find_dev(pnp_c,
229                                         ISAPNP_VENDOR('S', 'I', 'E'),
230                                         ISAPNP_FUNCTION(0x0010), pnp_d))) {
231                                         printk(KERN_ERR "ISurfPnP: PnP error card found, no device\n");
232                                         return (0);
233                                 }
234                                 pnp_disable_dev(pnp_d);
235                                 err = pnp_activate_dev(pnp_d);
236                                 cs->hw.isurf.reset = pnp_port_start(pnp_d, 0);
237                                 cs->hw.isurf.phymem = pnp_mem_start(pnp_d, 1);
238                                 cs->irq = pnp_irq(pnp_d, 0);
239                                 if (!cs->irq || !cs->hw.isurf.reset || !cs->hw.isurf.phymem) {
240                                         printk(KERN_ERR "ISurfPnP:some resources are missing %d/%x/%lx\n",
241                                                 cs->irq, cs->hw.isurf.reset, cs->hw.isurf.phymem);
242                                         pnp_disable_dev(pnp_d);
243                                         return(0);
244                                 }
245                         } else {
246                                 printk(KERN_INFO "ISurfPnP: no ISAPnP card found\n");
247                                 return(0);
248                         }
249                 } else {
250                         printk(KERN_INFO "ISurfPnP: no ISAPnP bus found\n");
251                         return(0);
252                 }
253 #else
254                 printk(KERN_WARNING "HiSax: %s port/mem not set\n",
255                         CardType[card->typ]);
256                 return (0);
257 #endif
258         }
259         if (!request_region(cs->hw.isurf.reset, 1, "isurf isdn")) {
260                 printk(KERN_WARNING
261                         "HiSax: %s config port %x already in use\n",
262                         CardType[card->typ],
263                         cs->hw.isurf.reset);
264                         return (0);
265         }
266         if (!request_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE, "isurf iomem")) {
267                 printk(KERN_WARNING
268                         "HiSax: %s memory region %lx-%lx already in use\n",
269                         CardType[card->typ],
270                         cs->hw.isurf.phymem,
271                         cs->hw.isurf.phymem + ISURF_IOMEM_SIZE);
272                 release_region(cs->hw.isurf.reset, 1);
273                 return (0);
274         }
275         cs->hw.isurf.isar = ioremap(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
276         cs->hw.isurf.isac = cs->hw.isurf.isar + ISURF_ISAC_OFFSET;
277         printk(KERN_INFO
278                "ISurf: defined at 0x%x 0x%lx IRQ %d\n",
279                cs->hw.isurf.reset,
280                cs->hw.isurf.phymem,
281                cs->irq);
282
283         setup_isac(cs);
284         cs->cardmsg = &ISurf_card_msg;
285         cs->irq_func = &isurf_interrupt;
286         cs->auxcmd = &isurf_auxcmd;
287         cs->readisac = &ReadISAC;
288         cs->writeisac = &WriteISAC;
289         cs->readisacfifo = &ReadISACfifo;
290         cs->writeisacfifo = &WriteISACfifo;
291         cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r;
292         cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r;
293         test_and_set_bit(HW_ISAR, &cs->HW_Flags);
294         ISACVersion(cs, "ISurf:");
295         cs->BC_Read_Reg = &ReadISAR;
296         cs->BC_Write_Reg = &WriteISAR;
297         cs->BC_Send_Data = &isar_fill_fifo;
298         ver = ISARVersion(cs, "ISurf:");
299         if (ver < 0) {
300                 printk(KERN_WARNING
301                         "ISurf: wrong ISAR version (ret = %d)\n", ver);
302                 release_io_isurf(cs);
303                 return (0);
304         }
305         return (1);
306 }