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