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