1/* $Id: ix1_micro.c,v 1.1.1.1 2008/10/15 03:26:33 james26_jang Exp $
2 *
3 * low level stuff for ITK ix1-micro Rev.2 isdn cards
4 * derived from the original file teles3.c from Karsten Keil
5 *
6 * Author       Klaus-Peter Nischke
7 * Copyright    by Klaus-Peter Nischke, ITK AG
8 *                                   <klaus@nischke.do.eunet.de>
9 *              by Karsten Keil      <keil@isdn4linux.de>
10 *
11 * This software may be used and distributed according to the terms
12 * of the GNU General Public License, incorporated herein by reference.
13 *
14 * Klaus-Peter Nischke
15 * Deusener Str. 287
16 * 44369 Dortmund
17 * Germany
18 */
19
20#define __NO_VERSION__
21#include <linux/init.h>
22#include <linux/isapnp.h>
23#include "hisax.h"
24#include "isac.h"
25#include "hscx.h"
26#include "isdnl1.h"
27
28extern const char *CardType[];
29const char *ix1_revision = "$Revision: 1.1.1.1 $";
30
31#define byteout(addr,val) outb(val,addr)
32#define bytein(addr) inb(addr)
33
34#define SPECIAL_PORT_OFFSET 3
35
36#define ISAC_COMMAND_OFFSET 2
37#define ISAC_DATA_OFFSET 0
38#define HSCX_COMMAND_OFFSET 2
39#define HSCX_DATA_OFFSET 1
40
41#define TIMEOUT 50
42
43static inline u_char
44readreg(unsigned int ale, unsigned int adr, u_char off)
45{
46	register u_char ret;
47	long flags;
48
49	save_flags(flags);
50	cli();
51	byteout(ale, off);
52	ret = bytein(adr);
53	restore_flags(flags);
54	return (ret);
55}
56
57static inline void
58readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
59{
60	/* fifo read without cli because it's allready done  */
61
62	byteout(ale, off);
63	insb(adr, data, size);
64}
65
66
67static inline void
68writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
69{
70	long flags;
71
72	save_flags(flags);
73	cli();
74	byteout(ale, off);
75	byteout(adr, data);
76	restore_flags(flags);
77}
78
79static inline void
80writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
81{
82	/* fifo write without cli because it's allready done  */
83	byteout(ale, off);
84	outsb(adr, data, size);
85}
86
87/* Interface functions */
88
89static u_char
90ReadISAC(struct IsdnCardState *cs, u_char offset)
91{
92	return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset));
93}
94
95static void
96WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
97{
98	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value);
99}
100
101static void
102ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
103{
104	readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
105}
106
107static void
108WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
109{
110	writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
111}
112
113static u_char
114ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
115{
116	return (readreg(cs->hw.ix1.hscx_ale,
117			cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0)));
118}
119
120static void
121WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
122{
123	writereg(cs->hw.ix1.hscx_ale,
124		 cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value);
125}
126
127#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \
128		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0))
129#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \
130		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data)
131
132#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \
133		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
134
135#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \
136		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
137
138#include "hscx_irq.c"
139
140static void
141ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs)
142{
143	struct IsdnCardState *cs = dev_id;
144	u_char val;
145
146	if (!cs) {
147		printk(KERN_WARNING "IX1: Spurious interrupt!\n");
148		return;
149	}
150	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
151      Start_HSCX:
152	if (val)
153		hscx_int_main(cs, val);
154	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
155      Start_ISAC:
156	if (val)
157		isac_interrupt(cs, val);
158	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.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.ix1.isac_ale, cs->hw.ix1.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.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF);
171	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF);
172	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF);
173	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0);
174	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0);
175	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0);
176}
177
178void
179release_io_ix1micro(struct IsdnCardState *cs)
180{
181	if (cs->hw.ix1.cfg_reg)
182		release_region(cs->hw.ix1.cfg_reg, 4);
183}
184
185static void
186ix1_reset(struct IsdnCardState *cs)
187{
188	long flags;
189	int cnt;
190
191	/* reset isac */
192	save_flags(flags);
193	cnt = 3 * (HZ / 10) + 1;
194	sti();
195	while (cnt--) {
196		byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1);
197		HZDELAY(1);	/* wait >=10 ms */
198	}
199	byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0);
200	restore_flags(flags);
201}
202
203static int
204ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg)
205{
206	switch (mt) {
207		case CARD_RESET:
208			ix1_reset(cs);
209			return(0);
210		case CARD_RELEASE:
211			release_io_ix1micro(cs);
212			return(0);
213		case CARD_INIT:
214			inithscxisac(cs, 3);
215			return(0);
216		case CARD_TEST:
217			return(0);
218	}
219	return(0);
220}
221
222#ifdef __ISAPNP__
223static struct isapnp_device_id itk_ids[] __initdata = {
224	{ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25),
225	  ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25),
226	  (unsigned long) "ITK micro 2" },
227	{ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29),
228	  ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29),
229	  (unsigned long) "ITK micro 2." },
230	{ 0, }
231};
232
233static struct isapnp_device_id *idev = &itk_ids[0];
234static struct pci_bus *pnp_c __devinitdata = NULL;
235#endif
236
237
238int __init
239setup_ix1micro(struct IsdnCard *card)
240{
241	struct IsdnCardState *cs = card->cs;
242	char tmp[64];
243
244	strcpy(tmp, ix1_revision);
245	printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
246	if (cs->typ != ISDN_CTYPE_IX1MICROR2)
247		return (0);
248
249#ifdef __ISAPNP__
250	if (!card->para[1] && isapnp_present()) {
251		struct pci_bus *pb;
252		struct pci_dev *pd;
253
254		while(idev->card_vendor) {
255			if ((pb = isapnp_find_card(idev->card_vendor,
256				idev->card_device, pnp_c))) {
257				pnp_c = pb;
258				pd = NULL;
259				if ((pd = isapnp_find_dev(pnp_c,
260					idev->vendor, idev->function, pd))) {
261					printk(KERN_INFO "HiSax: %s detected\n",
262						(char *)idev->driver_data);
263					pd->prepare(pd);
264					pd->deactivate(pd);
265					pd->activate(pd);
266					card->para[1] = pd->resource[0].start;
267					card->para[0] = pd->irq_resource[0].start;
268					if (!card->para[0] || !card->para[1]) {
269						printk(KERN_ERR "ITK PnP:some resources are missing %ld/%lx\n",
270						card->para[0], card->para[1]);
271						pd->deactivate(pd);
272						return(0);
273					}
274					break;
275				} else {
276					printk(KERN_ERR "ITK PnP: PnP error card found, no device\n");
277				}
278			}
279			idev++;
280			pnp_c=NULL;
281		}
282		if (!idev->card_vendor) {
283			printk(KERN_INFO "ITK PnP: no ISAPnP card found\n");
284			return(0);
285		}
286	}
287#endif
288	/* IO-Ports */
289	cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET;
290	cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET;
291	cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET;
292	cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET;
293	cs->hw.ix1.cfg_reg = card->para[1];
294	cs->irq = card->para[0];
295	if (cs->hw.ix1.cfg_reg) {
296		if (check_region((cs->hw.ix1.cfg_reg), 4)) {
297			printk(KERN_WARNING
298			  "HiSax: %s config port %x-%x already in use\n",
299			       CardType[card->typ],
300			       cs->hw.ix1.cfg_reg,
301			       cs->hw.ix1.cfg_reg + 4);
302			return (0);
303		} else
304			request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg");
305	}
306	printk(KERN_INFO
307	       "HiSax: %s config irq:%d io:0x%X\n",
308	       CardType[cs->typ], cs->irq,
309	       cs->hw.ix1.cfg_reg);
310	ix1_reset(cs);
311	cs->readisac = &ReadISAC;
312	cs->writeisac = &WriteISAC;
313	cs->readisacfifo = &ReadISACfifo;
314	cs->writeisacfifo = &WriteISACfifo;
315	cs->BC_Read_Reg = &ReadHSCX;
316	cs->BC_Write_Reg = &WriteHSCX;
317	cs->BC_Send_Data = &hscx_fill_fifo;
318	cs->cardmsg = &ix1_card_msg;
319	cs->irq_func = &ix1micro_interrupt;
320	ISACVersion(cs, "ix1-Micro:");
321	if (HscxVersion(cs, "ix1-Micro:")) {
322		printk(KERN_WARNING
323		    "ix1-Micro: wrong HSCX versions check IO address\n");
324		release_io_ix1micro(cs);
325		return (0);
326	}
327	return (1);
328}
329