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#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
24static const char *niccy_revision = "$Revision: 1.21.2.4 $";
25
26#define byteout(addr, val) outb(val, addr)
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
46static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off)
47{
48	register u_char ret;
49
50	byteout(ale, off);
51	ret = bytein(adr);
52	return ret;
53}
54
55static inline void readfifo(unsigned int ale, unsigned int adr, u_char off,
56			    u_char *data, int size)
57{
58	byteout(ale, off);
59	insb(adr, data, size);
60}
61
62static inline void writereg(unsigned int ale, unsigned int adr, u_char off,
63			    u_char data)
64{
65	byteout(ale, off);
66	byteout(adr, data);
67}
68
69static inline void writefifo(unsigned int ale, unsigned int adr, u_char off,
70			     u_char *data, int size)
71{
72	byteout(ale, off);
73	outsb(adr, data, size);
74}
75
76/* Interface functions */
77
78static u_char ReadISAC(struct IsdnCardState *cs, u_char offset)
79{
80	return readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset);
81}
82
83static void WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
84{
85	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value);
86}
87
88static void ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
89{
90	readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
91}
92
93static void WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
94{
95	writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
96}
97
98static u_char ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
99{
100	return readreg(cs->hw.niccy.hscx_ale,
101		       cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0));
102}
103
104static void WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset,
105		      u_char value)
106{
107	writereg(cs->hw.niccy.hscx_ale,
108		 cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value);
109}
110
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)
115
116#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale,	\
117						cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
118
119#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \
120						  cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
121
122#include "hscx_irq.c"
123
124static irqreturn_t niccy_interrupt(int intno, void *dev_id)
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);
134		if (!(ival & PCI_IRQ_ASSERT)) {	/* IRQ not for us (shared) */
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	}
140	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx,
141		      HSCX_ISTA + 0x40);
142Start_HSCX:
143	if (val)
144		hscx_int_main(cs, val);
145	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
146Start_ISAC:
147	if (val)
148		isac_interrupt(cs, val);
149	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx,
150		      HSCX_ISTA + 0x40);
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);
163	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40,
164		 0xFF);
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);
168	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0);
169	spin_unlock_irqrestore(&cs->lock, flags);
170	return IRQ_HANDLED;
171}
172
173static void release_io_niccy(struct IsdnCardState *cs)
174{
175	if (cs->subtyp == NICCY_PCI) {
176		int val;
177
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
189static void niccy_reset(struct IsdnCardState *cs)
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
201static int niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
202{
203	u_long flags;
204
205	switch (mt) {
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;
221	}
222	return 0;
223}
224
225#ifdef __ISAPNP__
226static struct pnp_card *pnp_c = NULL;
227#endif
228
229int setup_niccy(struct IsdnCard *card)
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)
237		return 0;
238#ifdef __ISAPNP__
239	if (!card->para[1] && isapnp_present()) {
240		struct pnp_dev *pnp_d = NULL;
241		int err;
242
243		pnp_c = pnp_find_card(ISAPNP_VENDOR('S', 'D', 'A'),
244				      ISAPNP_FUNCTION(0x0150), pnp_c);
245		if (pnp_c) {
246			pnp_d = pnp_find_dev(pnp_c,
247					     ISAPNP_VENDOR('S', 'D', 'A'),
248					     ISAPNP_FUNCTION(0x0150), pnp_d);
249			if (!pnp_d) {
250				printk(KERN_ERR "NiccyPnP: PnP error card "
251				       "found, no device\n");
252				return 0;
253			}
254			pnp_disable_dev(pnp_d);
255			err = pnp_activate_dev(pnp_d);
256			if (err < 0) {
257				printk(KERN_WARNING "%s: pnp_activate_dev "
258				       "ret(%d)\n", __func__, err);
259				return 0;
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);
264			if (!card->para[0] || !card->para[1] ||
265			    !card->para[2]) {
266				printk(KERN_ERR "NiccyPnP:some resources are "
267				       "missing %ld/%lx/%lx\n",
268				       card->para[0], card->para[1],
269				       card->para[2]);
270				pnp_disable_dev(pnp_d);
271				return 0;
272			}
273		} else
274			printk(KERN_INFO "NiccyPnP: no ISAPnP card found\n");
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")) {
286			printk(KERN_WARNING "HiSax: NICCY data port %x-%x "
287			       "already in use\n",
288			       cs->hw.niccy.isac, cs->hw.niccy.isac + 1);
289			return 0;
290		}
291		if (!request_region(cs->hw.niccy.isac_ale, 2, "niccy addr")) {
292			printk(KERN_WARNING "HiSax: NICCY address port %x-%x "
293			       "already in use\n",
294			       cs->hw.niccy.isac_ale,
295			       cs->hw.niccy.isac_ale + 1);
296			release_region(cs->hw.niccy.isac, 2);
297			return 0;
298		}
299	} else {
300#ifdef CONFIG_PCI
301		static struct pci_dev *niccy_dev;
302
303		u_int pci_ioaddr;
304		cs->subtyp = 0;
305		if ((niccy_dev = hisax_find_pci_device(PCI_VENDOR_ID_SATSAGEM,
306						       PCI_DEVICE_ID_SATSAGEM_NICCY,
307						       niccy_dev))) {
308			if (pci_enable_device(niccy_dev))
309				return 0;
310			/* get IRQ */
311			if (!niccy_dev->irq) {
312				printk(KERN_WARNING
313				       "Niccy: No IRQ for PCI card found\n");
314				return 0;
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) {
319				printk(KERN_WARNING
320				       "Niccy: No IO-Adr for PCI cfg found\n");
321				return 0;
322			}
323			pci_ioaddr = pci_resource_start(niccy_dev, 1);
324			if (!pci_ioaddr) {
325				printk(KERN_WARNING
326				       "Niccy: No IO-Adr for PCI card found\n");
327				return 0;
328			}
329			cs->subtyp = NICCY_PCI;
330		} else {
331			printk(KERN_WARNING "Niccy: No PCI card found\n");
332			return 0;
333		}
334		cs->irq_flags |= IRQF_SHARED;
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
341			       "HiSax: NICCY data port %x-%x already in use\n",
342			       cs->hw.niccy.isac, cs->hw.niccy.isac + 4);
343			return 0;
344		}
345		if (!request_region(cs->hw.niccy.cfg_reg, 0x40, "niccy pci")) {
346			printk(KERN_WARNING
347			       "HiSax: NICCY pci port %x-%x already in use\n",
348			       cs->hw.niccy.cfg_reg,
349			       cs->hw.niccy.cfg_reg + 0x40);
350			release_region(cs->hw.niccy.isac, 4);
351			return 0;
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");
356		return 0;
357#endif				/* CONFIG_PCI */
358	}
359	printk(KERN_INFO "HiSax: NICCY %s config irq:%d data:0x%X ale:0x%X\n",
360	       (cs->subtyp == 1) ? "PnP" : "PCI",
361	       cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
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:")) {
374		printk(KERN_WARNING "Niccy: wrong HSCX versions check IO "
375		       "address\n");
376		release_io_niccy(cs);
377		return 0;
378	}
379	return 1;
380}
381