Merge branch 'master' of /home/trondmy/kernel/linux-2.6/
[linux-2.6] / drivers / isdn / hisax / niccy.c
1 /* $Id: niccy.c,v 1.21.2.4 2004/01/13 23:48:39 keil Exp $
2  *
3  * low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
4  * compatible (SAGEM cybermodem)
5  *
6  * Author       Karsten Keil
7  * Copyright    by Karsten Keil      <keil@isdn4linux.de>
8  * 
9  * This software may be used and distributed according to the terms
10  * of the GNU General Public License, incorporated herein by reference.
11  * 
12  * Thanks to Dr. Neuhaus and SAGEM for information
13  *
14  */
15
16
17 #include <linux/init.h>
18 #include "hisax.h"
19 #include "isac.h"
20 #include "hscx.h"
21 #include "isdnl1.h"
22 #include <linux/pci.h>
23 #include <linux/isapnp.h>
24
25 extern const char *CardType[];
26 static const char *niccy_revision = "$Revision: 1.21.2.4 $";
27
28 #define byteout(addr,val) outb(val,addr)
29 #define bytein(addr) inb(addr)
30
31 #define ISAC_PCI_DATA   0
32 #define HSCX_PCI_DATA   1
33 #define ISAC_PCI_ADDR   2
34 #define HSCX_PCI_ADDR   3
35 #define ISAC_PNP        0
36 #define HSCX_PNP        1
37
38 /* SUB Types */
39 #define NICCY_PNP       1
40 #define NICCY_PCI       2
41
42 /* PCI stuff */
43 #define PCI_IRQ_CTRL_REG        0x38
44 #define PCI_IRQ_ENABLE          0x1f00
45 #define PCI_IRQ_DISABLE         0xff0000
46 #define PCI_IRQ_ASSERT          0x800000
47
48 static inline u_char
49 readreg(unsigned int ale, unsigned int adr, u_char off)
50 {
51         register u_char ret;
52
53         byteout(ale, off);
54         ret = bytein(adr);
55         return (ret);
56 }
57
58 static inline void
59 readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
60 {
61         byteout(ale, off);
62         insb(adr, data, size);
63 }
64
65
66 static inline void
67 writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
68 {
69         byteout(ale, off);
70         byteout(adr, data);
71 }
72
73 static inline void
74 writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
75 {
76         byteout(ale, off);
77         outsb(adr, data, size);
78 }
79
80 /* Interface functions */
81
82 static u_char
83 ReadISAC(struct IsdnCardState *cs, u_char offset)
84 {
85         return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset));
86 }
87
88 static void
89 WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
90 {
91         writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value);
92 }
93
94 static void
95 ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
96 {
97         readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
98 }
99
100 static void
101 WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
102 {
103         writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
104 }
105
106 static u_char
107 ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
108 {
109         return (readreg(cs->hw.niccy.hscx_ale,
110                         cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0)));
111 }
112
113 static void
114 WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
115 {
116         writereg(cs->hw.niccy.hscx_ale,
117                  cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value);
118 }
119
120 #define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \
121                 cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0))
122 #define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \
123                 cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data)
124
125 #define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \
126                 cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
127
128 #define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \
129                 cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
130
131 #include "hscx_irq.c"
132
133 static irqreturn_t
134 niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs)
135 {
136         struct IsdnCardState *cs = dev_id;
137         u_char val;
138         u_long flags;
139
140         spin_lock_irqsave(&cs->lock, flags);
141         if (cs->subtyp == NICCY_PCI) {
142                 int ival;
143                 ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
144                 if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */
145                         spin_unlock_irqrestore(&cs->lock, flags);
146                         return IRQ_NONE;
147                 }
148                 outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
149         }
150         val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
151       Start_HSCX:
152         if (val)
153                 hscx_int_main(cs, val);
154         val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
155       Start_ISAC:
156         if (val)
157                 isac_interrupt(cs, val);
158         val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
159         if (val) {
160                 if (cs->debug & L1_DEB_HSCX)
161                         debugl1(cs, "HSCX IntStat after IntRoutine");
162                 goto Start_HSCX;
163         }
164         val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
165         if (val) {
166                 if (cs->debug & L1_DEB_ISAC)
167                         debugl1(cs, "ISAC IntStat after IntRoutine");
168                 goto Start_ISAC;
169         }
170         writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF);
171         writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF);
172         writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF);
173         writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0);
174         writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0);
175         writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0);
176         spin_unlock_irqrestore(&cs->lock, flags);
177         return IRQ_HANDLED;
178 }
179
180 static void
181 release_io_niccy(struct IsdnCardState *cs)
182 {
183         if (cs->subtyp == NICCY_PCI) {
184                 int val;
185                 
186                 val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
187                 val &= PCI_IRQ_DISABLE;
188                 outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
189                 release_region(cs->hw.niccy.cfg_reg, 0x40);
190                 release_region(cs->hw.niccy.isac, 4);
191         } else {
192                 release_region(cs->hw.niccy.isac, 2);
193                 release_region(cs->hw.niccy.isac_ale, 2);
194         }
195 }
196
197 static void
198 niccy_reset(struct IsdnCardState *cs)
199 {
200         if (cs->subtyp == NICCY_PCI) {
201                 int val;
202
203                 val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
204                 val |= PCI_IRQ_ENABLE;
205                 outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
206         }
207         inithscxisac(cs, 3);
208 }
209
210 static int
211 niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
212 {
213         u_long flags;
214
215         switch (mt) {
216                 case CARD_RESET:
217                         spin_lock_irqsave(&cs->lock, flags);
218                         niccy_reset(cs);
219                         spin_unlock_irqrestore(&cs->lock, flags);
220                         return(0);
221                 case CARD_RELEASE:
222                         release_io_niccy(cs);
223                         return(0);
224                 case CARD_INIT:
225                         spin_lock_irqsave(&cs->lock, flags);
226                         niccy_reset(cs);
227                         spin_unlock_irqrestore(&cs->lock, flags);
228                         return(0);
229                 case CARD_TEST:
230                         return(0);
231         }
232         return(0);
233 }
234
235 static struct pci_dev *niccy_dev __initdata = NULL;
236 #ifdef __ISAPNP__
237 static struct pnp_card *pnp_c __devinitdata = NULL;
238 #endif
239
240 int __init
241 setup_niccy(struct IsdnCard *card)
242 {
243         struct IsdnCardState *cs = card->cs;
244         char tmp[64];
245
246         strcpy(tmp, niccy_revision);
247         printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp));
248         if (cs->typ != ISDN_CTYPE_NICCY)
249                 return (0);
250 #ifdef __ISAPNP__
251         if (!card->para[1] && isapnp_present()) {
252                 struct pnp_dev *pnp_d = NULL;
253                 int err;
254
255                 if ((pnp_c = pnp_find_card(
256                         ISAPNP_VENDOR('S', 'D', 'A'),
257                         ISAPNP_FUNCTION(0x0150), pnp_c))) {
258                         if (!(pnp_d = pnp_find_dev(pnp_c,
259                                 ISAPNP_VENDOR('S', 'D', 'A'),
260                                 ISAPNP_FUNCTION(0x0150), pnp_d))) {
261                                 printk(KERN_ERR "NiccyPnP: PnP error card found, no device\n");
262                                 return (0);
263                         }
264                         pnp_disable_dev(pnp_d);
265                         err = pnp_activate_dev(pnp_d);
266                         if (err<0) {
267                                 printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
268                                         __FUNCTION__, err);
269                                 return(0);
270                         }
271                         card->para[1] = pnp_port_start(pnp_d, 0);
272                         card->para[2] = pnp_port_start(pnp_d, 1);
273                         card->para[0] = pnp_irq(pnp_d, 0);
274                         if (!card->para[0] || !card->para[1] || !card->para[2]) {
275                                 printk(KERN_ERR "NiccyPnP:some resources are missing %ld/%lx/%lx\n",
276                                         card->para[0], card->para[1], card->para[2]);
277                                 pnp_disable_dev(pnp_d);
278                                 return(0);
279                         }
280                 } else {
281                         printk(KERN_INFO "NiccyPnP: no ISAPnP card found\n");
282                 }
283         }
284 #endif
285         if (card->para[1]) {
286                 cs->hw.niccy.isac = card->para[1] + ISAC_PNP;
287                 cs->hw.niccy.hscx = card->para[1] + HSCX_PNP;
288                 cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP;
289                 cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP;
290                 cs->hw.niccy.cfg_reg = 0;
291                 cs->subtyp = NICCY_PNP;
292                 cs->irq = card->para[0];
293                 if (!request_region(cs->hw.niccy.isac, 2, "niccy data")) {
294                         printk(KERN_WARNING
295                                 "HiSax: %s data port %x-%x already in use\n",
296                                 CardType[card->typ],
297                                 cs->hw.niccy.isac,
298                                 cs->hw.niccy.isac + 1);
299                         return (0);
300                 }
301                 if (!request_region(cs->hw.niccy.isac_ale, 2, "niccy addr")) {
302                         printk(KERN_WARNING
303                                 "HiSax: %s address port %x-%x already in use\n",
304                                 CardType[card->typ],
305                                 cs->hw.niccy.isac_ale,
306                                 cs->hw.niccy.isac_ale + 1);
307                         release_region(cs->hw.niccy.isac, 2);
308                         return (0);
309                 }
310         } else {
311 #ifdef CONFIG_PCI
312                 u_int pci_ioaddr;
313                 cs->subtyp = 0;
314                 if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM,
315                         PCI_DEVICE_ID_SATSAGEM_NICCY, niccy_dev))) {
316                         if (pci_enable_device(niccy_dev))
317                                 return(0);
318                         /* get IRQ */
319                         if (!niccy_dev->irq) {
320                                 printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
321                                 return(0);
322                         }
323                         cs->irq = niccy_dev->irq;
324                         cs->hw.niccy.cfg_reg = pci_resource_start(niccy_dev, 0);
325                         if (!cs->hw.niccy.cfg_reg) {
326                                 printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
327                                 return(0);
328                         }
329                         pci_ioaddr = pci_resource_start(niccy_dev, 1);
330                         if (!pci_ioaddr) {
331                                 printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
332                                 return(0);
333                         }
334                         cs->subtyp = NICCY_PCI;
335                 } else {
336                         printk(KERN_WARNING "Niccy: No PCI card found\n");
337                         return(0);
338                 }
339                 cs->irq_flags |= IRQF_SHARED;
340                 cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA;
341                 cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR;
342                 cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA;
343                 cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR;
344                 if (!request_region(cs->hw.niccy.isac, 4, "niccy")) {
345                         printk(KERN_WARNING
346                                 "HiSax: %s data port %x-%x already in use\n",
347                                 CardType[card->typ],
348                                 cs->hw.niccy.isac,
349                                 cs->hw.niccy.isac + 4);
350                         return (0);
351                 }
352                 if (!request_region(cs->hw.niccy.cfg_reg, 0x40, "niccy pci")) {
353                         printk(KERN_WARNING
354                                "HiSax: %s pci port %x-%x already in use\n",
355                                 CardType[card->typ],
356                                 cs->hw.niccy.cfg_reg,
357                                 cs->hw.niccy.cfg_reg + 0x40);
358                         release_region(cs->hw.niccy.isac, 4);
359                         return (0);
360                 }
361 #else
362                 printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
363                 printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
364                 return (0);
365 #endif /* CONFIG_PCI */
366         }
367         printk(KERN_INFO "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n",
368                 CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI",
369                 cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
370         setup_isac(cs);
371         cs->readisac = &ReadISAC;
372         cs->writeisac = &WriteISAC;
373         cs->readisacfifo = &ReadISACfifo;
374         cs->writeisacfifo = &WriteISACfifo;
375         cs->BC_Read_Reg = &ReadHSCX;
376         cs->BC_Write_Reg = &WriteHSCX;
377         cs->BC_Send_Data = &hscx_fill_fifo;
378         cs->cardmsg = &niccy_card_msg;
379         cs->irq_func = &niccy_interrupt;
380         ISACVersion(cs, "Niccy:");
381         if (HscxVersion(cs, "Niccy:")) {
382                 printk(KERN_WARNING
383                     "Niccy: wrong HSCX versions check IO address\n");
384                 release_io_niccy(cs);
385                 return (0);
386         }
387         return (1);
388 }