xref: /openbmc/linux/drivers/isdn/hardware/mISDN/mISDNinfineon.c (revision 5a0e3ad6af8660be21ca98a971cd00f331318c05)
1cae86d4aSKarsten Keil /*
2cae86d4aSKarsten Keil  * mISDNinfineon.c
3cae86d4aSKarsten Keil  *		Support for cards based on following Infineon ISDN chipsets
4cae86d4aSKarsten Keil  *		- ISAC + HSCX
5cae86d4aSKarsten Keil  *		- IPAC and IPAC-X
6cae86d4aSKarsten Keil  *		- ISAC-SX + HSCX
7cae86d4aSKarsten Keil  *
8cae86d4aSKarsten Keil  * Supported cards:
9cae86d4aSKarsten Keil  *		- Dialogic Diva 2.0
10cae86d4aSKarsten Keil  *		- Dialogic Diva 2.0U
11cae86d4aSKarsten Keil  *		- Dialogic Diva 2.01
12cae86d4aSKarsten Keil  *		- Dialogic Diva 2.02
13cae86d4aSKarsten Keil  *		- Sedlbauer Speedwin
14cae86d4aSKarsten Keil  *		- HST Saphir3
15cae86d4aSKarsten Keil  *		- Develo (former ELSA) Microlink PCI (Quickstep 1000)
16cae86d4aSKarsten Keil  *		- Develo (former ELSA) Quickstep 3000
17cae86d4aSKarsten Keil  *		- Berkom Scitel BRIX Quadro
18cae86d4aSKarsten Keil  *		- Dr.Neuhaus (Sagem) Niccy
19cae86d4aSKarsten Keil  *
20cae86d4aSKarsten Keil  *
21cae86d4aSKarsten Keil  *
22cae86d4aSKarsten Keil  * Author       Karsten Keil <keil@isdn4linux.de>
23cae86d4aSKarsten Keil  *
24cae86d4aSKarsten Keil  * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
25cae86d4aSKarsten Keil  *
26cae86d4aSKarsten Keil  * This program is free software; you can redistribute it and/or modify
27cae86d4aSKarsten Keil  * it under the terms of the GNU General Public License version 2 as
28cae86d4aSKarsten Keil  * published by the Free Software Foundation.
29cae86d4aSKarsten Keil  *
30cae86d4aSKarsten Keil  * This program is distributed in the hope that it will be useful,
31cae86d4aSKarsten Keil  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32cae86d4aSKarsten Keil  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33cae86d4aSKarsten Keil  * GNU General Public License for more details.
34cae86d4aSKarsten Keil  *
35cae86d4aSKarsten Keil  * You should have received a copy of the GNU General Public License
36cae86d4aSKarsten Keil  * along with this program; if not, write to the Free Software
37cae86d4aSKarsten Keil  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
38cae86d4aSKarsten Keil  *
39cae86d4aSKarsten Keil  */
40cae86d4aSKarsten Keil 
41cae86d4aSKarsten Keil #include <linux/module.h>
42cae86d4aSKarsten Keil #include <linux/pci.h>
43cae86d4aSKarsten Keil #include <linux/delay.h>
44cae86d4aSKarsten Keil #include <linux/mISDNhw.h>
45*5a0e3ad6STejun Heo #include <linux/slab.h>
46cae86d4aSKarsten Keil #include "ipac.h"
47cae86d4aSKarsten Keil 
48cae86d4aSKarsten Keil #define INFINEON_REV	"1.0"
49cae86d4aSKarsten Keil 
50cae86d4aSKarsten Keil static int inf_cnt;
51cae86d4aSKarsten Keil static u32 debug;
52cae86d4aSKarsten Keil static u32 irqloops = 4;
53cae86d4aSKarsten Keil 
54cae86d4aSKarsten Keil enum inf_types {
55cae86d4aSKarsten Keil 	INF_NONE,
56cae86d4aSKarsten Keil 	INF_DIVA20,
57cae86d4aSKarsten Keil 	INF_DIVA20U,
58cae86d4aSKarsten Keil 	INF_DIVA201,
59cae86d4aSKarsten Keil 	INF_DIVA202,
60cae86d4aSKarsten Keil 	INF_SPEEDWIN,
61cae86d4aSKarsten Keil 	INF_SAPHIR3,
62cae86d4aSKarsten Keil 	INF_QS1000,
63cae86d4aSKarsten Keil 	INF_QS3000,
64cae86d4aSKarsten Keil 	INF_NICCY,
65cae86d4aSKarsten Keil 	INF_SCT_1,
66cae86d4aSKarsten Keil 	INF_SCT_2,
67cae86d4aSKarsten Keil 	INF_SCT_3,
68cae86d4aSKarsten Keil 	INF_SCT_4,
69cae86d4aSKarsten Keil 	INF_GAZEL_R685,
70cae86d4aSKarsten Keil 	INF_GAZEL_R753
71cae86d4aSKarsten Keil };
72cae86d4aSKarsten Keil 
73cae86d4aSKarsten Keil enum addr_mode {
74cae86d4aSKarsten Keil 	AM_NONE = 0,
75cae86d4aSKarsten Keil 	AM_IO,
76cae86d4aSKarsten Keil 	AM_MEMIO,
77cae86d4aSKarsten Keil 	AM_IND_IO,
78cae86d4aSKarsten Keil };
79cae86d4aSKarsten Keil 
80cae86d4aSKarsten Keil struct inf_cinfo {
81cae86d4aSKarsten Keil 	enum inf_types	typ;
82cae86d4aSKarsten Keil 	const char	*full;
83cae86d4aSKarsten Keil 	const char	*name;
84cae86d4aSKarsten Keil 	enum addr_mode	cfg_mode;
85cae86d4aSKarsten Keil 	enum addr_mode	addr_mode;
86cae86d4aSKarsten Keil 	u8		cfg_bar;
87cae86d4aSKarsten Keil 	u8		addr_bar;
88cae86d4aSKarsten Keil 	void		*irqfunc;
89cae86d4aSKarsten Keil };
90cae86d4aSKarsten Keil 
91cae86d4aSKarsten Keil struct _ioaddr {
92cae86d4aSKarsten Keil 	enum addr_mode	mode;
93cae86d4aSKarsten Keil 	union {
94cae86d4aSKarsten Keil 		void __iomem	*p;
95cae86d4aSKarsten Keil 		struct _ioport	io;
96cae86d4aSKarsten Keil 	} a;
97cae86d4aSKarsten Keil };
98cae86d4aSKarsten Keil 
99cae86d4aSKarsten Keil struct _iohandle {
100cae86d4aSKarsten Keil 	enum addr_mode	mode;
101cae86d4aSKarsten Keil 	resource_size_t	size;
102cae86d4aSKarsten Keil 	resource_size_t	start;
103cae86d4aSKarsten Keil 	void __iomem	*p;
104cae86d4aSKarsten Keil };
105cae86d4aSKarsten Keil 
106cae86d4aSKarsten Keil struct inf_hw {
107cae86d4aSKarsten Keil 	struct list_head	list;
108cae86d4aSKarsten Keil 	struct pci_dev		*pdev;
109cae86d4aSKarsten Keil 	const struct inf_cinfo	*ci;
110cae86d4aSKarsten Keil 	char			name[MISDN_MAX_IDLEN];
111cae86d4aSKarsten Keil 	u32			irq;
112cae86d4aSKarsten Keil 	u32			irqcnt;
113cae86d4aSKarsten Keil 	struct _iohandle	cfg;
114cae86d4aSKarsten Keil 	struct _iohandle	addr;
115cae86d4aSKarsten Keil 	struct _ioaddr		isac;
116cae86d4aSKarsten Keil 	struct _ioaddr		hscx;
117cae86d4aSKarsten Keil 	spinlock_t		lock;	/* HW access lock */
118cae86d4aSKarsten Keil 	struct ipac_hw		ipac;
119cae86d4aSKarsten Keil 	struct inf_hw		*sc[3];	/* slave cards */
120cae86d4aSKarsten Keil };
121cae86d4aSKarsten Keil 
122cae86d4aSKarsten Keil 
123cae86d4aSKarsten Keil #define PCI_SUBVENDOR_HST_SAPHIR3       0x52
124cae86d4aSKarsten Keil #define PCI_SUBVENDOR_SEDLBAUER_PCI     0x53
125cae86d4aSKarsten Keil #define PCI_SUB_ID_SEDLBAUER            0x01
126cae86d4aSKarsten Keil 
127cae86d4aSKarsten Keil static struct pci_device_id infineon_ids[] __devinitdata = {
128cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20,
129cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA20},
130cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20_U,
131cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA20U},
132cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA201,
133cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA201},
134cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA202,
135cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA202},
136cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
137cae86d4aSKarsten Keil 	  PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0,
138cae86d4aSKarsten Keil 	  INF_SPEEDWIN},
139cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
140cae86d4aSKarsten Keil 	  PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3},
141cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK,
142cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_QS1000},
143cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_QS3000,
144cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_QS3000},
145cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY,
146cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_NICCY},
147cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
148cae86d4aSKarsten Keil 	  PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0,
149cae86d4aSKarsten Keil 	  INF_SCT_1},
150cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R685,
151cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R685},
152cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R753,
153cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
154cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO,
155cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
156cae86d4aSKarsten Keil 	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_OLITEC,
157cae86d4aSKarsten Keil 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
158cae86d4aSKarsten Keil 	{ }
159cae86d4aSKarsten Keil };
160cae86d4aSKarsten Keil MODULE_DEVICE_TABLE(pci, infineon_ids);
161cae86d4aSKarsten Keil 
162cae86d4aSKarsten Keil /* PCI interface specific defines */
163cae86d4aSKarsten Keil /* Diva 2.0/2.0U */
164cae86d4aSKarsten Keil #define DIVA_HSCX_PORT		0x00
165cae86d4aSKarsten Keil #define DIVA_HSCX_ALE		0x04
166cae86d4aSKarsten Keil #define DIVA_ISAC_PORT		0x08
167cae86d4aSKarsten Keil #define DIVA_ISAC_ALE		0x0C
168cae86d4aSKarsten Keil #define DIVA_PCI_CTRL           0x10
169cae86d4aSKarsten Keil 
170cae86d4aSKarsten Keil /* DIVA_PCI_CTRL bits */
171cae86d4aSKarsten Keil #define DIVA_IRQ_BIT		0x01
172cae86d4aSKarsten Keil #define DIVA_RESET_BIT		0x08
173cae86d4aSKarsten Keil #define DIVA_EEPROM_CLK		0x40
174cae86d4aSKarsten Keil #define DIVA_LED_A		0x10
175cae86d4aSKarsten Keil #define DIVA_LED_B		0x20
176cae86d4aSKarsten Keil #define DIVA_IRQ_CLR		0x80
177cae86d4aSKarsten Keil 
178cae86d4aSKarsten Keil /* Diva 2.01/2.02 */
179cae86d4aSKarsten Keil /* Siemens PITA */
180cae86d4aSKarsten Keil #define PITA_ICR_REG		0x00
181cae86d4aSKarsten Keil #define PITA_INT0_STATUS	0x02
182cae86d4aSKarsten Keil 
183cae86d4aSKarsten Keil #define PITA_MISC_REG		0x1c
184cae86d4aSKarsten Keil #define PITA_PARA_SOFTRESET	0x01000000
185cae86d4aSKarsten Keil #define PITA_SER_SOFTRESET	0x02000000
186cae86d4aSKarsten Keil #define PITA_PARA_MPX_MODE	0x04000000
187cae86d4aSKarsten Keil #define PITA_INT0_ENABLE	0x00020000
188cae86d4aSKarsten Keil 
189cae86d4aSKarsten Keil /* TIGER 100 Registers */
190cae86d4aSKarsten Keil #define TIGER_RESET_ADDR	0x00
191cae86d4aSKarsten Keil #define TIGER_EXTERN_RESET	0x01
192cae86d4aSKarsten Keil #define TIGER_AUX_CTRL		0x02
193cae86d4aSKarsten Keil #define TIGER_AUX_DATA		0x03
194cae86d4aSKarsten Keil #define TIGER_AUX_IRQMASK	0x05
195cae86d4aSKarsten Keil #define TIGER_AUX_STATUS	0x07
196cae86d4aSKarsten Keil 
197cae86d4aSKarsten Keil /* Tiger AUX BITs */
198cae86d4aSKarsten Keil #define TIGER_IOMASK		0xdd	/* 1 and 5 are inputs */
199cae86d4aSKarsten Keil #define TIGER_IRQ_BIT		0x02
200cae86d4aSKarsten Keil 
201cae86d4aSKarsten Keil #define TIGER_IPAC_ALE		0xC0
202cae86d4aSKarsten Keil #define TIGER_IPAC_PORT		0xC8
203cae86d4aSKarsten Keil 
204cae86d4aSKarsten Keil /* ELSA (now Develo) PCI cards */
205cae86d4aSKarsten Keil #define ELSA_IRQ_ADDR		0x4c
206cae86d4aSKarsten Keil #define ELSA_IRQ_MASK		0x04
207cae86d4aSKarsten Keil #define QS1000_IRQ_OFF		0x01
208cae86d4aSKarsten Keil #define QS3000_IRQ_OFF		0x03
209cae86d4aSKarsten Keil #define QS1000_IRQ_ON		0x41
210cae86d4aSKarsten Keil #define QS3000_IRQ_ON		0x43
211cae86d4aSKarsten Keil 
212cae86d4aSKarsten Keil /* Dr Neuhaus/Sagem Niccy */
213cae86d4aSKarsten Keil #define NICCY_ISAC_PORT		0x00
214cae86d4aSKarsten Keil #define NICCY_HSCX_PORT		0x01
215cae86d4aSKarsten Keil #define NICCY_ISAC_ALE		0x02
216cae86d4aSKarsten Keil #define NICCY_HSCX_ALE		0x03
217cae86d4aSKarsten Keil 
218cae86d4aSKarsten Keil #define NICCY_IRQ_CTRL_REG	0x38
219cae86d4aSKarsten Keil #define NICCY_IRQ_ENABLE	0x001f00
220cae86d4aSKarsten Keil #define NICCY_IRQ_DISABLE	0xff0000
221cae86d4aSKarsten Keil #define NICCY_IRQ_BIT		0x800000
222cae86d4aSKarsten Keil 
223cae86d4aSKarsten Keil 
224cae86d4aSKarsten Keil /* Scitel PLX */
225cae86d4aSKarsten Keil #define SCT_PLX_IRQ_ADDR	0x4c
226cae86d4aSKarsten Keil #define SCT_PLX_RESET_ADDR	0x50
227cae86d4aSKarsten Keil #define SCT_PLX_IRQ_ENABLE	0x41
228cae86d4aSKarsten Keil #define SCT_PLX_RESET_BIT	0x04
229cae86d4aSKarsten Keil 
230cae86d4aSKarsten Keil /* Gazel */
231cae86d4aSKarsten Keil #define	GAZEL_IPAC_DATA_PORT	0x04
232cae86d4aSKarsten Keil /* Gazel PLX */
233cae86d4aSKarsten Keil #define GAZEL_CNTRL		0x50
234cae86d4aSKarsten Keil #define GAZEL_RESET		0x04
235cae86d4aSKarsten Keil #define GAZEL_RESET_9050	0x40000000
236cae86d4aSKarsten Keil #define GAZEL_INCSR		0x4C
237cae86d4aSKarsten Keil #define GAZEL_ISAC_EN		0x08
238cae86d4aSKarsten Keil #define GAZEL_INT_ISAC		0x20
239cae86d4aSKarsten Keil #define GAZEL_HSCX_EN		0x01
240cae86d4aSKarsten Keil #define GAZEL_INT_HSCX		0x04
241cae86d4aSKarsten Keil #define GAZEL_PCI_EN		0x40
242cae86d4aSKarsten Keil #define GAZEL_IPAC_EN		0x03
243cae86d4aSKarsten Keil 
244cae86d4aSKarsten Keil 
245cae86d4aSKarsten Keil static LIST_HEAD(Cards);
246cae86d4aSKarsten Keil static DEFINE_RWLOCK(card_lock); /* protect Cards */
247cae86d4aSKarsten Keil 
248cae86d4aSKarsten Keil static void
249cae86d4aSKarsten Keil _set_debug(struct inf_hw *card)
250cae86d4aSKarsten Keil {
251cae86d4aSKarsten Keil 	card->ipac.isac.dch.debug = debug;
252cae86d4aSKarsten Keil 	card->ipac.hscx[0].bch.debug = debug;
253cae86d4aSKarsten Keil 	card->ipac.hscx[1].bch.debug = debug;
254cae86d4aSKarsten Keil }
255cae86d4aSKarsten Keil 
256cae86d4aSKarsten Keil static int
257cae86d4aSKarsten Keil set_debug(const char *val, struct kernel_param *kp)
258cae86d4aSKarsten Keil {
259cae86d4aSKarsten Keil 	int ret;
260cae86d4aSKarsten Keil 	struct inf_hw *card;
261cae86d4aSKarsten Keil 
262cae86d4aSKarsten Keil 	ret = param_set_uint(val, kp);
263cae86d4aSKarsten Keil 	if (!ret) {
264cae86d4aSKarsten Keil 		read_lock(&card_lock);
265cae86d4aSKarsten Keil 		list_for_each_entry(card, &Cards, list)
266cae86d4aSKarsten Keil 			_set_debug(card);
267cae86d4aSKarsten Keil 		read_unlock(&card_lock);
268cae86d4aSKarsten Keil 	}
269cae86d4aSKarsten Keil 	return ret;
270cae86d4aSKarsten Keil }
271cae86d4aSKarsten Keil 
272cae86d4aSKarsten Keil MODULE_AUTHOR("Karsten Keil");
273cae86d4aSKarsten Keil MODULE_LICENSE("GPL v2");
274cae86d4aSKarsten Keil MODULE_VERSION(INFINEON_REV);
275cae86d4aSKarsten Keil module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
276cae86d4aSKarsten Keil MODULE_PARM_DESC(debug, "infineon debug mask");
277cae86d4aSKarsten Keil module_param(irqloops, uint, S_IRUGO | S_IWUSR);
278cae86d4aSKarsten Keil MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)");
279cae86d4aSKarsten Keil 
280cae86d4aSKarsten Keil /* Interface functions */
281cae86d4aSKarsten Keil 
282cae86d4aSKarsten Keil IOFUNC_IO(ISAC, inf_hw, isac.a.io)
283cae86d4aSKarsten Keil IOFUNC_IO(IPAC, inf_hw, hscx.a.io)
284cae86d4aSKarsten Keil IOFUNC_IND(ISAC, inf_hw, isac.a.io)
285cae86d4aSKarsten Keil IOFUNC_IND(IPAC, inf_hw, hscx.a.io)
286cae86d4aSKarsten Keil IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p)
287cae86d4aSKarsten Keil IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p)
288cae86d4aSKarsten Keil 
289cae86d4aSKarsten Keil static irqreturn_t
290cae86d4aSKarsten Keil diva_irq(int intno, void *dev_id)
291cae86d4aSKarsten Keil {
292cae86d4aSKarsten Keil 	struct inf_hw *hw = dev_id;
293cae86d4aSKarsten Keil 	u8 val;
294cae86d4aSKarsten Keil 
295cae86d4aSKarsten Keil 	spin_lock(&hw->lock);
296cae86d4aSKarsten Keil 	val = inb((u32)hw->cfg.start + DIVA_PCI_CTRL);
297cae86d4aSKarsten Keil 	if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */
298cae86d4aSKarsten Keil 		spin_unlock(&hw->lock);
299cae86d4aSKarsten Keil 		return IRQ_NONE; /* shared */
300cae86d4aSKarsten Keil 	}
301cae86d4aSKarsten Keil 	hw->irqcnt++;
302cae86d4aSKarsten Keil 	mISDNipac_irq(&hw->ipac, irqloops);
303cae86d4aSKarsten Keil 	spin_unlock(&hw->lock);
304cae86d4aSKarsten Keil 	return IRQ_HANDLED;
305cae86d4aSKarsten Keil }
306cae86d4aSKarsten Keil 
307cae86d4aSKarsten Keil static irqreturn_t
308cae86d4aSKarsten Keil diva20x_irq(int intno, void *dev_id)
309cae86d4aSKarsten Keil {
310cae86d4aSKarsten Keil 	struct inf_hw *hw = dev_id;
311cae86d4aSKarsten Keil 	u8 val;
312cae86d4aSKarsten Keil 
313cae86d4aSKarsten Keil 	spin_lock(&hw->lock);
314cae86d4aSKarsten Keil 	val = readb(hw->cfg.p);
315cae86d4aSKarsten Keil 	if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */
316cae86d4aSKarsten Keil 		spin_unlock(&hw->lock);
317cae86d4aSKarsten Keil 		return IRQ_NONE; /* shared */
318cae86d4aSKarsten Keil 	}
319cae86d4aSKarsten Keil 	hw->irqcnt++;
320cae86d4aSKarsten Keil 	mISDNipac_irq(&hw->ipac, irqloops);
321cae86d4aSKarsten Keil 	writeb(PITA_INT0_STATUS, hw->cfg.p); /* ACK PITA INT0 */
322cae86d4aSKarsten Keil 	spin_unlock(&hw->lock);
323cae86d4aSKarsten Keil 	return IRQ_HANDLED;
324cae86d4aSKarsten Keil }
325cae86d4aSKarsten Keil 
326cae86d4aSKarsten Keil static irqreturn_t
327cae86d4aSKarsten Keil tiger_irq(int intno, void *dev_id)
328cae86d4aSKarsten Keil {
329cae86d4aSKarsten Keil 	struct inf_hw *hw = dev_id;
330cae86d4aSKarsten Keil 	u8 val;
331cae86d4aSKarsten Keil 
332cae86d4aSKarsten Keil 	spin_lock(&hw->lock);
333cae86d4aSKarsten Keil 	val = inb((u32)hw->cfg.start + TIGER_AUX_STATUS);
334cae86d4aSKarsten Keil 	if (val & TIGER_IRQ_BIT) { /* for us or shared ? */
335cae86d4aSKarsten Keil 		spin_unlock(&hw->lock);
336cae86d4aSKarsten Keil 		return IRQ_NONE; /* shared */
337cae86d4aSKarsten Keil 	}
338cae86d4aSKarsten Keil 	hw->irqcnt++;
339cae86d4aSKarsten Keil 	mISDNipac_irq(&hw->ipac, irqloops);
340cae86d4aSKarsten Keil 	spin_unlock(&hw->lock);
341cae86d4aSKarsten Keil 	return IRQ_HANDLED;
342cae86d4aSKarsten Keil }
343cae86d4aSKarsten Keil 
344cae86d4aSKarsten Keil static irqreturn_t
345cae86d4aSKarsten Keil elsa_irq(int intno, void *dev_id)
346cae86d4aSKarsten Keil {
347cae86d4aSKarsten Keil 	struct inf_hw *hw = dev_id;
348cae86d4aSKarsten Keil 	u8 val;
349cae86d4aSKarsten Keil 
350cae86d4aSKarsten Keil 	spin_lock(&hw->lock);
351cae86d4aSKarsten Keil 	val = inb((u32)hw->cfg.start + ELSA_IRQ_ADDR);
352cae86d4aSKarsten Keil 	if (!(val & ELSA_IRQ_MASK)) {
353cae86d4aSKarsten Keil 		spin_unlock(&hw->lock);
354cae86d4aSKarsten Keil 		return IRQ_NONE; /* shared */
355cae86d4aSKarsten Keil 	}
356cae86d4aSKarsten Keil 	hw->irqcnt++;
357cae86d4aSKarsten Keil 	mISDNipac_irq(&hw->ipac, irqloops);
358cae86d4aSKarsten Keil 	spin_unlock(&hw->lock);
359cae86d4aSKarsten Keil 	return IRQ_HANDLED;
360cae86d4aSKarsten Keil }
361cae86d4aSKarsten Keil 
362cae86d4aSKarsten Keil static irqreturn_t
363cae86d4aSKarsten Keil niccy_irq(int intno, void *dev_id)
364cae86d4aSKarsten Keil {
365cae86d4aSKarsten Keil 	struct inf_hw *hw = dev_id;
366cae86d4aSKarsten Keil 	u32 val;
367cae86d4aSKarsten Keil 
368cae86d4aSKarsten Keil 	spin_lock(&hw->lock);
369cae86d4aSKarsten Keil 	val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
370cae86d4aSKarsten Keil 	if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */
371cae86d4aSKarsten Keil 		spin_unlock(&hw->lock);
372cae86d4aSKarsten Keil 		return IRQ_NONE; /* shared */
373cae86d4aSKarsten Keil 	}
374cae86d4aSKarsten Keil 	outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
375cae86d4aSKarsten Keil 	hw->irqcnt++;
376cae86d4aSKarsten Keil 	mISDNipac_irq(&hw->ipac, irqloops);
377cae86d4aSKarsten Keil 	spin_unlock(&hw->lock);
378cae86d4aSKarsten Keil 	return IRQ_HANDLED;
379cae86d4aSKarsten Keil }
380cae86d4aSKarsten Keil 
381cae86d4aSKarsten Keil static irqreturn_t
382cae86d4aSKarsten Keil gazel_irq(int intno, void *dev_id)
383cae86d4aSKarsten Keil {
384cae86d4aSKarsten Keil 	struct inf_hw *hw = dev_id;
385cae86d4aSKarsten Keil 	irqreturn_t ret;
386cae86d4aSKarsten Keil 
387cae86d4aSKarsten Keil 	spin_lock(&hw->lock);
388cae86d4aSKarsten Keil 	ret = mISDNipac_irq(&hw->ipac, irqloops);
389cae86d4aSKarsten Keil 	spin_unlock(&hw->lock);
390cae86d4aSKarsten Keil 	return ret;
391cae86d4aSKarsten Keil }
392cae86d4aSKarsten Keil 
393cae86d4aSKarsten Keil static irqreturn_t
394cae86d4aSKarsten Keil ipac_irq(int intno, void *dev_id)
395cae86d4aSKarsten Keil {
396cae86d4aSKarsten Keil 	struct inf_hw *hw = dev_id;
397cae86d4aSKarsten Keil 	u8 val;
398cae86d4aSKarsten Keil 
399cae86d4aSKarsten Keil 	spin_lock(&hw->lock);
400cae86d4aSKarsten Keil 	val = hw->ipac.read_reg(hw, IPAC_ISTA);
401cae86d4aSKarsten Keil 	if (!(val & 0x3f)) {
402cae86d4aSKarsten Keil 		spin_unlock(&hw->lock);
403cae86d4aSKarsten Keil 		return IRQ_NONE; /* shared */
404cae86d4aSKarsten Keil 	}
405cae86d4aSKarsten Keil 	hw->irqcnt++;
406cae86d4aSKarsten Keil 	mISDNipac_irq(&hw->ipac, irqloops);
407cae86d4aSKarsten Keil 	spin_unlock(&hw->lock);
408cae86d4aSKarsten Keil 	return IRQ_HANDLED;
409cae86d4aSKarsten Keil }
410cae86d4aSKarsten Keil 
411cae86d4aSKarsten Keil static void
412cae86d4aSKarsten Keil enable_hwirq(struct inf_hw *hw)
413cae86d4aSKarsten Keil {
414cae86d4aSKarsten Keil 	u16 w;
415cae86d4aSKarsten Keil 	u32 val;
416cae86d4aSKarsten Keil 
417cae86d4aSKarsten Keil 	switch (hw->ci->typ) {
418cae86d4aSKarsten Keil 	case INF_DIVA201:
419cae86d4aSKarsten Keil 	case INF_DIVA202:
420cae86d4aSKarsten Keil 		writel(PITA_INT0_ENABLE, hw->cfg.p);
421cae86d4aSKarsten Keil 		break;
422cae86d4aSKarsten Keil 	case INF_SPEEDWIN:
423cae86d4aSKarsten Keil 	case INF_SAPHIR3:
424cae86d4aSKarsten Keil 		outb(TIGER_IRQ_BIT, (u32)hw->cfg.start + TIGER_AUX_IRQMASK);
425cae86d4aSKarsten Keil 		break;
426cae86d4aSKarsten Keil 	case INF_QS1000:
427cae86d4aSKarsten Keil 		outb(QS1000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
428cae86d4aSKarsten Keil 		break;
429cae86d4aSKarsten Keil 	case INF_QS3000:
430cae86d4aSKarsten Keil 		outb(QS3000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
431cae86d4aSKarsten Keil 		break;
432cae86d4aSKarsten Keil 	case INF_NICCY:
433cae86d4aSKarsten Keil 		val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
434cae86d4aSKarsten Keil 		val |= NICCY_IRQ_ENABLE;;
435cae86d4aSKarsten Keil 		outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
436cae86d4aSKarsten Keil 		break;
437cae86d4aSKarsten Keil 	case INF_SCT_1:
438cae86d4aSKarsten Keil 		w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
439cae86d4aSKarsten Keil 		w |= SCT_PLX_IRQ_ENABLE;
440cae86d4aSKarsten Keil 		outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
441cae86d4aSKarsten Keil 		break;
442cae86d4aSKarsten Keil 	case INF_GAZEL_R685:
443cae86d4aSKarsten Keil 		outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN,
444cae86d4aSKarsten Keil 			(u32)hw->cfg.start + GAZEL_INCSR);
445cae86d4aSKarsten Keil 		break;
446cae86d4aSKarsten Keil 	case INF_GAZEL_R753:
447cae86d4aSKarsten Keil 		outb(GAZEL_IPAC_EN + GAZEL_PCI_EN,
448cae86d4aSKarsten Keil 			(u32)hw->cfg.start + GAZEL_INCSR);
449cae86d4aSKarsten Keil 		break;
450cae86d4aSKarsten Keil 	default:
451cae86d4aSKarsten Keil 		break;
452cae86d4aSKarsten Keil 	}
453cae86d4aSKarsten Keil }
454cae86d4aSKarsten Keil 
455cae86d4aSKarsten Keil static void
456cae86d4aSKarsten Keil disable_hwirq(struct inf_hw *hw)
457cae86d4aSKarsten Keil {
458cae86d4aSKarsten Keil 	u16 w;
459cae86d4aSKarsten Keil 	u32 val;
460cae86d4aSKarsten Keil 
461cae86d4aSKarsten Keil 	switch (hw->ci->typ) {
462cae86d4aSKarsten Keil 	case INF_DIVA201:
463cae86d4aSKarsten Keil 	case INF_DIVA202:
464cae86d4aSKarsten Keil 		writel(0, hw->cfg.p);
465cae86d4aSKarsten Keil 		break;
466cae86d4aSKarsten Keil 	case INF_SPEEDWIN:
467cae86d4aSKarsten Keil 	case INF_SAPHIR3:
468cae86d4aSKarsten Keil 		outb(0, (u32)hw->cfg.start + TIGER_AUX_IRQMASK);
469cae86d4aSKarsten Keil 		break;
470cae86d4aSKarsten Keil 	case INF_QS1000:
471cae86d4aSKarsten Keil 		outb(QS1000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
472cae86d4aSKarsten Keil 		break;
473cae86d4aSKarsten Keil 	case INF_QS3000:
474cae86d4aSKarsten Keil 		outb(QS3000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
475cae86d4aSKarsten Keil 		break;
476cae86d4aSKarsten Keil 	case INF_NICCY:
477cae86d4aSKarsten Keil 		val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
478cae86d4aSKarsten Keil 		val &= NICCY_IRQ_DISABLE;
479cae86d4aSKarsten Keil 		outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
480cae86d4aSKarsten Keil 		break;
481cae86d4aSKarsten Keil 	case INF_SCT_1:
482cae86d4aSKarsten Keil 		w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
483cae86d4aSKarsten Keil 		w &= (~SCT_PLX_IRQ_ENABLE);
484cae86d4aSKarsten Keil 		outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
485cae86d4aSKarsten Keil 		break;
486cae86d4aSKarsten Keil 	case INF_GAZEL_R685:
487cae86d4aSKarsten Keil 	case INF_GAZEL_R753:
488cae86d4aSKarsten Keil 		outb(0, (u32)hw->cfg.start + GAZEL_INCSR);
489cae86d4aSKarsten Keil 		break;
490cae86d4aSKarsten Keil 	default:
491cae86d4aSKarsten Keil 		break;
492cae86d4aSKarsten Keil 	}
493cae86d4aSKarsten Keil }
494cae86d4aSKarsten Keil 
495cae86d4aSKarsten Keil static void
496cae86d4aSKarsten Keil ipac_chip_reset(struct inf_hw *hw)
497cae86d4aSKarsten Keil {
498cae86d4aSKarsten Keil 	hw->ipac.write_reg(hw, IPAC_POTA2, 0x20);
499cae86d4aSKarsten Keil 	mdelay(5);
500cae86d4aSKarsten Keil 	hw->ipac.write_reg(hw, IPAC_POTA2, 0x00);
501cae86d4aSKarsten Keil 	mdelay(5);
502cae86d4aSKarsten Keil 	hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf);
503cae86d4aSKarsten Keil 	hw->ipac.write_reg(hw, IPAC_MASK, 0xc0);
504cae86d4aSKarsten Keil }
505cae86d4aSKarsten Keil 
506cae86d4aSKarsten Keil static void
507cae86d4aSKarsten Keil reset_inf(struct inf_hw *hw)
508cae86d4aSKarsten Keil {
509cae86d4aSKarsten Keil 	u16 w;
510cae86d4aSKarsten Keil 	u32 val;
511cae86d4aSKarsten Keil 
512cae86d4aSKarsten Keil 	if (debug & DEBUG_HW)
513cae86d4aSKarsten Keil 		pr_notice("%s: resetting card\n", hw->name);
514cae86d4aSKarsten Keil 	switch (hw->ci->typ) {
515cae86d4aSKarsten Keil 	case INF_DIVA20:
516cae86d4aSKarsten Keil 	case INF_DIVA20U:
517cae86d4aSKarsten Keil 		outb(0, (u32)hw->cfg.start + DIVA_PCI_CTRL);
518cae86d4aSKarsten Keil 		mdelay(10);
519cae86d4aSKarsten Keil 		outb(DIVA_RESET_BIT, (u32)hw->cfg.start + DIVA_PCI_CTRL);
520cae86d4aSKarsten Keil 		mdelay(10);
521cae86d4aSKarsten Keil 		/* Workaround PCI9060 */
522cae86d4aSKarsten Keil 		outb(9, (u32)hw->cfg.start + 0x69);
523cae86d4aSKarsten Keil 		outb(DIVA_RESET_BIT | DIVA_LED_A,
524cae86d4aSKarsten Keil 			(u32)hw->cfg.start + DIVA_PCI_CTRL);
525cae86d4aSKarsten Keil 		break;
526cae86d4aSKarsten Keil 	case INF_DIVA201:
527cae86d4aSKarsten Keil 		writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE,
528cae86d4aSKarsten Keil 			hw->cfg.p + PITA_MISC_REG);
529cae86d4aSKarsten Keil 		mdelay(1);
530cae86d4aSKarsten Keil 		writel(PITA_PARA_MPX_MODE, hw->cfg.p + PITA_MISC_REG);
531cae86d4aSKarsten Keil 		mdelay(10);
532cae86d4aSKarsten Keil 		break;
533cae86d4aSKarsten Keil 	case INF_DIVA202:
534cae86d4aSKarsten Keil 		writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE,
535cae86d4aSKarsten Keil 			hw->cfg.p + PITA_MISC_REG);
536cae86d4aSKarsten Keil 		mdelay(1);
537cae86d4aSKarsten Keil 		writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET,
538cae86d4aSKarsten Keil 			hw->cfg.p + PITA_MISC_REG);
539cae86d4aSKarsten Keil 		mdelay(10);
540cae86d4aSKarsten Keil 		break;
541cae86d4aSKarsten Keil 	case INF_SPEEDWIN:
542cae86d4aSKarsten Keil 	case INF_SAPHIR3:
543cae86d4aSKarsten Keil 		ipac_chip_reset(hw);
544cae86d4aSKarsten Keil 		hw->ipac.write_reg(hw, IPAC_ACFG, 0xff);
545cae86d4aSKarsten Keil 		hw->ipac.write_reg(hw, IPAC_AOE, 0x00);
546cae86d4aSKarsten Keil 		hw->ipac.write_reg(hw, IPAC_PCFG, 0x12);
547cae86d4aSKarsten Keil 		break;
548cae86d4aSKarsten Keil 	case INF_QS1000:
549cae86d4aSKarsten Keil 	case INF_QS3000:
550cae86d4aSKarsten Keil 		ipac_chip_reset(hw);
551cae86d4aSKarsten Keil 		hw->ipac.write_reg(hw, IPAC_ACFG, 0x00);
552cae86d4aSKarsten Keil 		hw->ipac.write_reg(hw, IPAC_AOE, 0x3c);
553cae86d4aSKarsten Keil 		hw->ipac.write_reg(hw, IPAC_ATX, 0xff);
554cae86d4aSKarsten Keil 		break;
555cae86d4aSKarsten Keil 	case INF_NICCY:
556cae86d4aSKarsten Keil 		break;
557cae86d4aSKarsten Keil 	case INF_SCT_1:
558cae86d4aSKarsten Keil 		w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
559cae86d4aSKarsten Keil 		w &= (~SCT_PLX_RESET_BIT);
560cae86d4aSKarsten Keil 		outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
561cae86d4aSKarsten Keil 		mdelay(10);
562cae86d4aSKarsten Keil 		w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
563cae86d4aSKarsten Keil 		w |= SCT_PLX_RESET_BIT;
564cae86d4aSKarsten Keil 		outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
565cae86d4aSKarsten Keil 		mdelay(10);
566cae86d4aSKarsten Keil 		break;
567cae86d4aSKarsten Keil 	case INF_GAZEL_R685:
568cae86d4aSKarsten Keil 		val = inl((u32)hw->cfg.start + GAZEL_CNTRL);
569cae86d4aSKarsten Keil 		val |= (GAZEL_RESET_9050 + GAZEL_RESET);
570cae86d4aSKarsten Keil 		outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
571cae86d4aSKarsten Keil 		val &= ~(GAZEL_RESET_9050 + GAZEL_RESET);
572cae86d4aSKarsten Keil 		mdelay(4);
573cae86d4aSKarsten Keil 		outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
574cae86d4aSKarsten Keil 		mdelay(10);
575cae86d4aSKarsten Keil 		hw->ipac.isac.adf2 = 0x87;
576cae86d4aSKarsten Keil 		hw->ipac.hscx[0].slot = 0x1f;
577cae86d4aSKarsten Keil 		hw->ipac.hscx[0].slot = 0x23;
578cae86d4aSKarsten Keil 		break;
579cae86d4aSKarsten Keil 	case INF_GAZEL_R753:
580cae86d4aSKarsten Keil 		val = inl((u32)hw->cfg.start + GAZEL_CNTRL);
581cae86d4aSKarsten Keil 		val |= (GAZEL_RESET_9050 + GAZEL_RESET);
582cae86d4aSKarsten Keil 		outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
583cae86d4aSKarsten Keil 		val &= ~(GAZEL_RESET_9050 + GAZEL_RESET);
584cae86d4aSKarsten Keil 		mdelay(4);
585cae86d4aSKarsten Keil 		outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
586cae86d4aSKarsten Keil 		mdelay(10);
587cae86d4aSKarsten Keil 		ipac_chip_reset(hw);
588cae86d4aSKarsten Keil 		hw->ipac.write_reg(hw, IPAC_ACFG, 0xff);
589cae86d4aSKarsten Keil 		hw->ipac.write_reg(hw, IPAC_AOE, 0x00);
590cae86d4aSKarsten Keil 		hw->ipac.conf = 0x01; /* IOM off */
591cae86d4aSKarsten Keil 		break;
592cae86d4aSKarsten Keil 	default:
593cae86d4aSKarsten Keil 		return;
594cae86d4aSKarsten Keil 	}
595cae86d4aSKarsten Keil 	enable_hwirq(hw);
596cae86d4aSKarsten Keil }
597cae86d4aSKarsten Keil 
598cae86d4aSKarsten Keil static int
599cae86d4aSKarsten Keil inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg)
600cae86d4aSKarsten Keil {
601cae86d4aSKarsten Keil 	int ret = 0;
602cae86d4aSKarsten Keil 
603cae86d4aSKarsten Keil 	switch (cmd) {
604cae86d4aSKarsten Keil 	case HW_RESET_REQ:
605cae86d4aSKarsten Keil 		reset_inf(hw);
606cae86d4aSKarsten Keil 		break;
607cae86d4aSKarsten Keil 	default:
608cae86d4aSKarsten Keil 		pr_info("%s: %s unknown command %x %lx\n",
609cae86d4aSKarsten Keil 			hw->name, __func__, cmd, arg);
610cae86d4aSKarsten Keil 		ret = -EINVAL;
611cae86d4aSKarsten Keil 		break;
612cae86d4aSKarsten Keil 	}
613cae86d4aSKarsten Keil 	return ret;
614cae86d4aSKarsten Keil }
615cae86d4aSKarsten Keil 
616cae86d4aSKarsten Keil static int __devinit
617cae86d4aSKarsten Keil init_irq(struct inf_hw *hw)
618cae86d4aSKarsten Keil {
619cae86d4aSKarsten Keil 	int	ret, cnt = 3;
620cae86d4aSKarsten Keil 	u_long	flags;
621cae86d4aSKarsten Keil 
622cae86d4aSKarsten Keil 	if (!hw->ci->irqfunc)
623cae86d4aSKarsten Keil 		return -EINVAL;
624cae86d4aSKarsten Keil 	ret = request_irq(hw->irq, hw->ci->irqfunc, IRQF_SHARED, hw->name, hw);
625cae86d4aSKarsten Keil 	if (ret) {
626cae86d4aSKarsten Keil 		pr_info("%s: couldn't get interrupt %d\n", hw->name, hw->irq);
627cae86d4aSKarsten Keil 		return ret;
628cae86d4aSKarsten Keil 	}
629cae86d4aSKarsten Keil 	while (cnt--) {
630cae86d4aSKarsten Keil 		spin_lock_irqsave(&hw->lock, flags);
631cae86d4aSKarsten Keil 		reset_inf(hw);
632cae86d4aSKarsten Keil 		ret = hw->ipac.init(&hw->ipac);
633cae86d4aSKarsten Keil 		if (ret) {
634cae86d4aSKarsten Keil 			spin_unlock_irqrestore(&hw->lock, flags);
635cae86d4aSKarsten Keil 			pr_info("%s: ISAC init failed with %d\n",
636cae86d4aSKarsten Keil 				hw->name, ret);
637cae86d4aSKarsten Keil 			break;
638cae86d4aSKarsten Keil 		}
639cae86d4aSKarsten Keil 		spin_unlock_irqrestore(&hw->lock, flags);
640cae86d4aSKarsten Keil 		msleep_interruptible(10);
641cae86d4aSKarsten Keil 		if (debug & DEBUG_HW)
642cae86d4aSKarsten Keil 			pr_notice("%s: IRQ %d count %d\n", hw->name,
643cae86d4aSKarsten Keil 				hw->irq, hw->irqcnt);
644cae86d4aSKarsten Keil 		if (!hw->irqcnt) {
645cae86d4aSKarsten Keil 			pr_info("%s: IRQ(%d) got no requests during init %d\n",
646cae86d4aSKarsten Keil 				hw->name, hw->irq, 3 - cnt);
647cae86d4aSKarsten Keil 		} else
648cae86d4aSKarsten Keil 			return 0;
649cae86d4aSKarsten Keil 	}
650cae86d4aSKarsten Keil 	free_irq(hw->irq, hw);
651cae86d4aSKarsten Keil 	return -EIO;
652cae86d4aSKarsten Keil }
653cae86d4aSKarsten Keil 
654cae86d4aSKarsten Keil static void
655cae86d4aSKarsten Keil release_io(struct inf_hw *hw)
656cae86d4aSKarsten Keil {
657cae86d4aSKarsten Keil 	if (hw->cfg.mode) {
658cae86d4aSKarsten Keil 		if (hw->cfg.p) {
659cae86d4aSKarsten Keil 			release_mem_region(hw->cfg.start, hw->cfg.size);
660cae86d4aSKarsten Keil 			iounmap(hw->cfg.p);
661cae86d4aSKarsten Keil 		} else
662cae86d4aSKarsten Keil 			release_region(hw->cfg.start, hw->cfg.size);
663cae86d4aSKarsten Keil 		hw->cfg.mode = AM_NONE;
664cae86d4aSKarsten Keil 	}
665cae86d4aSKarsten Keil 	if (hw->addr.mode) {
666cae86d4aSKarsten Keil 		if (hw->addr.p) {
667cae86d4aSKarsten Keil 			release_mem_region(hw->addr.start, hw->addr.size);
668cae86d4aSKarsten Keil 			iounmap(hw->addr.p);
669cae86d4aSKarsten Keil 		} else
670cae86d4aSKarsten Keil 			release_region(hw->addr.start, hw->addr.size);
671cae86d4aSKarsten Keil 		hw->addr.mode = AM_NONE;
672cae86d4aSKarsten Keil 	}
673cae86d4aSKarsten Keil }
674cae86d4aSKarsten Keil 
675cae86d4aSKarsten Keil static int __devinit
676cae86d4aSKarsten Keil setup_io(struct inf_hw *hw)
677cae86d4aSKarsten Keil {
678cae86d4aSKarsten Keil 	int err = 0;
679cae86d4aSKarsten Keil 
680cae86d4aSKarsten Keil 	if (hw->ci->cfg_mode) {
681cae86d4aSKarsten Keil 		hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar);
682cae86d4aSKarsten Keil 		hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar);
683cae86d4aSKarsten Keil 		if (hw->ci->cfg_mode == AM_MEMIO) {
684cae86d4aSKarsten Keil 			if (!request_mem_region(hw->cfg.start, hw->cfg.size,
685cae86d4aSKarsten Keil 			    hw->name))
686cae86d4aSKarsten Keil 				err = -EBUSY;
687cae86d4aSKarsten Keil 		} else {
688cae86d4aSKarsten Keil 			if (!request_region(hw->cfg.start, hw->cfg.size,
689cae86d4aSKarsten Keil 			    hw->name))
690cae86d4aSKarsten Keil 				err = -EBUSY;
691cae86d4aSKarsten Keil 		}
692cae86d4aSKarsten Keil 		if (err) {
693cae86d4aSKarsten Keil 			pr_info("mISDN: %s config port %lx (%lu bytes)"
694cae86d4aSKarsten Keil 				"already in use\n", hw->name,
695cae86d4aSKarsten Keil 				(ulong)hw->cfg.start, (ulong)hw->cfg.size);
696cae86d4aSKarsten Keil 			return err;
697cae86d4aSKarsten Keil 		}
698cae86d4aSKarsten Keil 		if (hw->ci->cfg_mode == AM_MEMIO)
699cae86d4aSKarsten Keil 			hw->cfg.p = ioremap(hw->cfg.start, hw->cfg.size);
700cae86d4aSKarsten Keil 		hw->cfg.mode = hw->ci->cfg_mode;
701cae86d4aSKarsten Keil 		if (debug & DEBUG_HW)
702cae86d4aSKarsten Keil 			pr_notice("%s: IO cfg %lx (%lu bytes) mode%d\n",
703cae86d4aSKarsten Keil 				hw->name, (ulong)hw->cfg.start,
704cae86d4aSKarsten Keil 				(ulong)hw->cfg.size, hw->ci->cfg_mode);
705cae86d4aSKarsten Keil 
706cae86d4aSKarsten Keil 	}
707cae86d4aSKarsten Keil 	if (hw->ci->addr_mode) {
708cae86d4aSKarsten Keil 		hw->addr.start = pci_resource_start(hw->pdev, hw->ci->addr_bar);
709cae86d4aSKarsten Keil 		hw->addr.size = pci_resource_len(hw->pdev, hw->ci->addr_bar);
710cae86d4aSKarsten Keil 		if (hw->ci->addr_mode == AM_MEMIO) {
711cae86d4aSKarsten Keil 			if (!request_mem_region(hw->addr.start, hw->addr.size,
712cae86d4aSKarsten Keil 			    hw->name))
713cae86d4aSKarsten Keil 				err = -EBUSY;
714cae86d4aSKarsten Keil 		} else {
715cae86d4aSKarsten Keil 			if (!request_region(hw->addr.start, hw->addr.size,
716cae86d4aSKarsten Keil 			    hw->name))
717cae86d4aSKarsten Keil 				err = -EBUSY;
718cae86d4aSKarsten Keil 		}
719cae86d4aSKarsten Keil 		if (err) {
720cae86d4aSKarsten Keil 			pr_info("mISDN: %s address port %lx (%lu bytes)"
721cae86d4aSKarsten Keil 				"already in use\n", hw->name,
722cae86d4aSKarsten Keil 				(ulong)hw->addr.start, (ulong)hw->addr.size);
723cae86d4aSKarsten Keil 			return err;
724cae86d4aSKarsten Keil 		}
725cae86d4aSKarsten Keil 		if (hw->ci->addr_mode == AM_MEMIO)
726cae86d4aSKarsten Keil 			hw->addr.p = ioremap(hw->addr.start, hw->addr.size);
727cae86d4aSKarsten Keil 		hw->addr.mode = hw->ci->addr_mode;
728cae86d4aSKarsten Keil 		if (debug & DEBUG_HW)
729cae86d4aSKarsten Keil 			pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n",
730cae86d4aSKarsten Keil 				hw->name, (ulong)hw->addr.start,
731cae86d4aSKarsten Keil 				(ulong)hw->addr.size, hw->ci->addr_mode);
732cae86d4aSKarsten Keil 
733cae86d4aSKarsten Keil 	}
734cae86d4aSKarsten Keil 
735cae86d4aSKarsten Keil 	switch (hw->ci->typ) {
736cae86d4aSKarsten Keil 	case INF_DIVA20:
737cae86d4aSKarsten Keil 	case INF_DIVA20U:
738cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
739cae86d4aSKarsten Keil 		hw->isac.mode = hw->cfg.mode;
740cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->cfg.start + DIVA_ISAC_ALE;
741cae86d4aSKarsten Keil 		hw->isac.a.io.port = (u32)hw->cfg.start + DIVA_ISAC_PORT;
742cae86d4aSKarsten Keil 		hw->hscx.mode = hw->cfg.mode;
743cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = (u32)hw->cfg.start + DIVA_HSCX_ALE;
744cae86d4aSKarsten Keil 		hw->hscx.a.io.port = (u32)hw->cfg.start + DIVA_HSCX_PORT;
745cae86d4aSKarsten Keil 		break;
746cae86d4aSKarsten Keil 	case INF_DIVA201:
747cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPAC;
748cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
749cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
750cae86d4aSKarsten Keil 		hw->isac.a.p = hw->addr.p;
751cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
752cae86d4aSKarsten Keil 		hw->hscx.a.p = hw->addr.p;
753cae86d4aSKarsten Keil 		break;
754cae86d4aSKarsten Keil 	case INF_DIVA202:
755cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPACX;
756cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
757cae86d4aSKarsten Keil 		hw->isac.a.p = hw->addr.p;
758cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
759cae86d4aSKarsten Keil 		hw->hscx.a.p = hw->addr.p;
760cae86d4aSKarsten Keil 		break;
761cae86d4aSKarsten Keil 	case INF_SPEEDWIN:
762cae86d4aSKarsten Keil 	case INF_SAPHIR3:
763cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPAC;
764cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
765cae86d4aSKarsten Keil 		hw->isac.mode = hw->cfg.mode;
766cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE;
767cae86d4aSKarsten Keil 		hw->isac.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT;
768cae86d4aSKarsten Keil 		hw->hscx.mode = hw->cfg.mode;
769cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE;
770cae86d4aSKarsten Keil 		hw->hscx.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT;
771cae86d4aSKarsten Keil 		outb(0xff, (ulong)hw->cfg.start);
772cae86d4aSKarsten Keil 		mdelay(1);
773cae86d4aSKarsten Keil 		outb(0x00, (ulong)hw->cfg.start);
774cae86d4aSKarsten Keil 		mdelay(1);
775cae86d4aSKarsten Keil 		outb(TIGER_IOMASK, (ulong)hw->cfg.start + TIGER_AUX_CTRL);
776cae86d4aSKarsten Keil 		break;
777cae86d4aSKarsten Keil 	case INF_QS1000:
778cae86d4aSKarsten Keil 	case INF_QS3000:
779cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPAC;
780cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
781cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->addr.start;
782cae86d4aSKarsten Keil 		hw->isac.a.io.port = (u32)hw->addr.start + 1;
783cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
784cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = (u32)hw->addr.start;
785cae86d4aSKarsten Keil 		hw->hscx.a.io.port = (u32)hw->addr.start + 1;
786cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
787cae86d4aSKarsten Keil 		break;
788cae86d4aSKarsten Keil 	case INF_NICCY:
789cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
790cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
791cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->addr.start + NICCY_ISAC_ALE;
792cae86d4aSKarsten Keil 		hw->isac.a.io.port = (u32)hw->addr.start + NICCY_ISAC_PORT;
793cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
794cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = (u32)hw->addr.start + NICCY_HSCX_ALE;
795cae86d4aSKarsten Keil 		hw->hscx.a.io.port = (u32)hw->addr.start + NICCY_HSCX_PORT;
796cae86d4aSKarsten Keil 		break;
797cae86d4aSKarsten Keil 	case INF_SCT_1:
798cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPAC;
799cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
800cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->addr.start;
801cae86d4aSKarsten Keil 		hw->isac.a.io.port = hw->isac.a.io.ale + 4;
802cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
803cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = hw->isac.a.io.ale;
804cae86d4aSKarsten Keil 		hw->hscx.a.io.port = hw->isac.a.io.port;
805cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
806cae86d4aSKarsten Keil 		break;
807cae86d4aSKarsten Keil 	case INF_SCT_2:
808cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPAC;
809cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
810cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->addr.start + 0x08;
811cae86d4aSKarsten Keil 		hw->isac.a.io.port = hw->isac.a.io.ale + 4;
812cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
813cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = hw->isac.a.io.ale;
814cae86d4aSKarsten Keil 		hw->hscx.a.io.port = hw->isac.a.io.port;
815cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
816cae86d4aSKarsten Keil 		break;
817cae86d4aSKarsten Keil 	case INF_SCT_3:
818cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPAC;
819cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
820cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->addr.start + 0x10;
821cae86d4aSKarsten Keil 		hw->isac.a.io.port = hw->isac.a.io.ale + 4;
822cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
823cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = hw->isac.a.io.ale;
824cae86d4aSKarsten Keil 		hw->hscx.a.io.port = hw->isac.a.io.port;
825cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
826cae86d4aSKarsten Keil 		break;
827cae86d4aSKarsten Keil 	case INF_SCT_4:
828cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPAC;
829cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
830cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->addr.start + 0x20;
831cae86d4aSKarsten Keil 		hw->isac.a.io.port = hw->isac.a.io.ale + 4;
832cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
833cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = hw->isac.a.io.ale;
834cae86d4aSKarsten Keil 		hw->hscx.a.io.port = hw->isac.a.io.port;
835cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
836cae86d4aSKarsten Keil 		break;
837cae86d4aSKarsten Keil 	case INF_GAZEL_R685:
838cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
839cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
840cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
841cae86d4aSKarsten Keil 		hw->isac.a.io.port = (u32)hw->addr.start;
842cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
843cae86d4aSKarsten Keil 		hw->hscx.a.io.port = hw->isac.a.io.port;
844cae86d4aSKarsten Keil 		break;
845cae86d4aSKarsten Keil 	case INF_GAZEL_R753:
846cae86d4aSKarsten Keil 		hw->ipac.type = IPAC_TYPE_IPAC;
847cae86d4aSKarsten Keil 		hw->ipac.isac.off = 0x80;
848cae86d4aSKarsten Keil 		hw->isac.mode = hw->addr.mode;
849cae86d4aSKarsten Keil 		hw->isac.a.io.ale = (u32)hw->addr.start;
850cae86d4aSKarsten Keil 		hw->isac.a.io.port = (u32)hw->addr.start + GAZEL_IPAC_DATA_PORT;
851cae86d4aSKarsten Keil 		hw->hscx.mode = hw->addr.mode;
852cae86d4aSKarsten Keil 		hw->hscx.a.io.ale = hw->isac.a.io.ale;
853cae86d4aSKarsten Keil 		hw->hscx.a.io.port = hw->isac.a.io.port;
854cae86d4aSKarsten Keil 		break;
855cae86d4aSKarsten Keil 	default:
856cae86d4aSKarsten Keil 		return -EINVAL;
857cae86d4aSKarsten Keil 	}
858cae86d4aSKarsten Keil 	switch (hw->isac.mode) {
859cae86d4aSKarsten Keil 	case AM_MEMIO:
860cae86d4aSKarsten Keil 		ASSIGN_FUNC_IPAC(MIO, hw->ipac);
861cae86d4aSKarsten Keil 		break;
862cae86d4aSKarsten Keil 	case AM_IND_IO:
863cae86d4aSKarsten Keil 		ASSIGN_FUNC_IPAC(IND, hw->ipac);
864cae86d4aSKarsten Keil 		break;
865cae86d4aSKarsten Keil 	case AM_IO:
866cae86d4aSKarsten Keil 		ASSIGN_FUNC_IPAC(IO, hw->ipac);
867cae86d4aSKarsten Keil 		break;
868cae86d4aSKarsten Keil 	default:
869cae86d4aSKarsten Keil 		return -EINVAL;
870cae86d4aSKarsten Keil 	}
871cae86d4aSKarsten Keil 	return 0;
872cae86d4aSKarsten Keil }
873cae86d4aSKarsten Keil 
874cae86d4aSKarsten Keil static void
875cae86d4aSKarsten Keil release_card(struct inf_hw *card) {
876cae86d4aSKarsten Keil 	ulong	flags;
877cae86d4aSKarsten Keil 	int	i;
878cae86d4aSKarsten Keil 
879cae86d4aSKarsten Keil 	spin_lock_irqsave(&card->lock, flags);
880cae86d4aSKarsten Keil 	disable_hwirq(card);
881cae86d4aSKarsten Keil 	spin_unlock_irqrestore(&card->lock, flags);
882cae86d4aSKarsten Keil 	card->ipac.isac.release(&card->ipac.isac);
883cae86d4aSKarsten Keil 	free_irq(card->irq, card);
884cae86d4aSKarsten Keil 	mISDN_unregister_device(&card->ipac.isac.dch.dev);
885cae86d4aSKarsten Keil 	release_io(card);
886cae86d4aSKarsten Keil 	write_lock_irqsave(&card_lock, flags);
887cae86d4aSKarsten Keil 	list_del(&card->list);
888cae86d4aSKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
889cae86d4aSKarsten Keil 	switch (card->ci->typ) {
890cae86d4aSKarsten Keil 	case INF_SCT_2:
891cae86d4aSKarsten Keil 	case INF_SCT_3:
892cae86d4aSKarsten Keil 	case INF_SCT_4:
893cae86d4aSKarsten Keil 		break;
894cae86d4aSKarsten Keil 	case INF_SCT_1:
895cae86d4aSKarsten Keil 		for (i = 0; i < 3; i++) {
896cae86d4aSKarsten Keil 			if (card->sc[i])
897cae86d4aSKarsten Keil 				release_card(card->sc[i]);
898cae86d4aSKarsten Keil 			card->sc[i] = NULL;
899cae86d4aSKarsten Keil 		}
900cae86d4aSKarsten Keil 	default:
901cae86d4aSKarsten Keil 		pci_disable_device(card->pdev);
902cae86d4aSKarsten Keil 		pci_set_drvdata(card->pdev, NULL);
903cae86d4aSKarsten Keil 		break;
904cae86d4aSKarsten Keil 	}
905cae86d4aSKarsten Keil 	kfree(card);
906cae86d4aSKarsten Keil 	inf_cnt--;
907cae86d4aSKarsten Keil }
908cae86d4aSKarsten Keil 
909cae86d4aSKarsten Keil static int __devinit
910cae86d4aSKarsten Keil setup_instance(struct inf_hw *card)
911cae86d4aSKarsten Keil {
912cae86d4aSKarsten Keil 	int err;
913cae86d4aSKarsten Keil 	ulong flags;
914cae86d4aSKarsten Keil 
915cae86d4aSKarsten Keil 	snprintf(card->name, MISDN_MAX_IDLEN - 1, "%s.%d", card->ci->name,
916cae86d4aSKarsten Keil 		inf_cnt + 1);
917cae86d4aSKarsten Keil 	write_lock_irqsave(&card_lock, flags);
918cae86d4aSKarsten Keil 	list_add_tail(&card->list, &Cards);
919cae86d4aSKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
920cae86d4aSKarsten Keil 
921cae86d4aSKarsten Keil 	_set_debug(card);
922cae86d4aSKarsten Keil 	card->ipac.isac.name = card->name;
923cae86d4aSKarsten Keil 	card->ipac.name = card->name;
924cae86d4aSKarsten Keil 	card->ipac.owner = THIS_MODULE;
925cae86d4aSKarsten Keil 	spin_lock_init(&card->lock);
926cae86d4aSKarsten Keil 	card->ipac.isac.hwlock = &card->lock;
927cae86d4aSKarsten Keil 	card->ipac.hwlock = &card->lock;
928cae86d4aSKarsten Keil 	card->ipac.ctrl = (void *)&inf_ctrl;
929cae86d4aSKarsten Keil 
930cae86d4aSKarsten Keil 	err = setup_io(card);
931cae86d4aSKarsten Keil 	if (err)
932cae86d4aSKarsten Keil 		goto error_setup;
933cae86d4aSKarsten Keil 
934cae86d4aSKarsten Keil 	card->ipac.isac.dch.dev.Bprotocols =
935cae86d4aSKarsten Keil 		mISDNipac_init(&card->ipac, card);
936cae86d4aSKarsten Keil 
937cae86d4aSKarsten Keil 	if (card->ipac.isac.dch.dev.Bprotocols == 0)
938cae86d4aSKarsten Keil 		goto error_setup;;
939cae86d4aSKarsten Keil 
940cae86d4aSKarsten Keil 	err = mISDN_register_device(&card->ipac.isac.dch.dev,
941cae86d4aSKarsten Keil 		&card->pdev->dev, card->name);
942cae86d4aSKarsten Keil 	if (err)
943cae86d4aSKarsten Keil 		goto error;
944cae86d4aSKarsten Keil 
945cae86d4aSKarsten Keil 	err = init_irq(card);
946cae86d4aSKarsten Keil 	if (!err)  {
947cae86d4aSKarsten Keil 		inf_cnt++;
948cae86d4aSKarsten Keil 		pr_notice("Infineon %d cards installed\n", inf_cnt);
949cae86d4aSKarsten Keil 		return 0;
950cae86d4aSKarsten Keil 	}
951cae86d4aSKarsten Keil 	mISDN_unregister_device(&card->ipac.isac.dch.dev);
952cae86d4aSKarsten Keil error:
953cae86d4aSKarsten Keil 	card->ipac.release(&card->ipac);
954cae86d4aSKarsten Keil error_setup:
955cae86d4aSKarsten Keil 	release_io(card);
956cae86d4aSKarsten Keil 	write_lock_irqsave(&card_lock, flags);
957cae86d4aSKarsten Keil 	list_del(&card->list);
958cae86d4aSKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
959cae86d4aSKarsten Keil 	return err;
960cae86d4aSKarsten Keil }
961cae86d4aSKarsten Keil 
962cae86d4aSKarsten Keil static const struct inf_cinfo inf_card_info[] = {
963cae86d4aSKarsten Keil 	{
964cae86d4aSKarsten Keil 		INF_DIVA20,
965cae86d4aSKarsten Keil 		"Dialogic Diva 2.0",
966cae86d4aSKarsten Keil 		"diva20",
967cae86d4aSKarsten Keil 		AM_IND_IO, AM_NONE, 2, 0,
968cae86d4aSKarsten Keil 		&diva_irq
969cae86d4aSKarsten Keil 	},
970cae86d4aSKarsten Keil 	{
971cae86d4aSKarsten Keil 		INF_DIVA20U,
972cae86d4aSKarsten Keil 		"Dialogic Diva 2.0U",
973cae86d4aSKarsten Keil 		"diva20U",
974cae86d4aSKarsten Keil 		AM_IND_IO, AM_NONE, 2, 0,
975cae86d4aSKarsten Keil 		&diva_irq
976cae86d4aSKarsten Keil 	},
977cae86d4aSKarsten Keil 	{
978cae86d4aSKarsten Keil 		INF_DIVA201,
979cae86d4aSKarsten Keil 		"Dialogic Diva 2.01",
980cae86d4aSKarsten Keil 		"diva201",
981cae86d4aSKarsten Keil 		AM_MEMIO, AM_MEMIO, 0, 1,
982cae86d4aSKarsten Keil 		&diva20x_irq
983cae86d4aSKarsten Keil 	},
984cae86d4aSKarsten Keil 	{
985cae86d4aSKarsten Keil 		INF_DIVA202,
986cae86d4aSKarsten Keil 		"Dialogic Diva 2.02",
987cae86d4aSKarsten Keil 		"diva202",
988cae86d4aSKarsten Keil 		AM_MEMIO, AM_MEMIO, 0, 1,
989cae86d4aSKarsten Keil 		&diva20x_irq
990cae86d4aSKarsten Keil 	},
991cae86d4aSKarsten Keil 	{
992cae86d4aSKarsten Keil 		INF_SPEEDWIN,
993cae86d4aSKarsten Keil 		"Sedlbauer SpeedWin PCI",
994cae86d4aSKarsten Keil 		"speedwin",
995cae86d4aSKarsten Keil 		AM_IND_IO, AM_NONE, 0, 0,
996cae86d4aSKarsten Keil 		&tiger_irq
997cae86d4aSKarsten Keil 	},
998cae86d4aSKarsten Keil 	{
999cae86d4aSKarsten Keil 		INF_SAPHIR3,
1000cae86d4aSKarsten Keil 		"HST Saphir 3",
1001cae86d4aSKarsten Keil 		"saphir",
1002cae86d4aSKarsten Keil 		AM_IND_IO, AM_NONE, 0, 0,
1003cae86d4aSKarsten Keil 		&tiger_irq
1004cae86d4aSKarsten Keil 	},
1005cae86d4aSKarsten Keil 	{
1006cae86d4aSKarsten Keil 		INF_QS1000,
1007cae86d4aSKarsten Keil 		"Develo Microlink PCI",
1008cae86d4aSKarsten Keil 		"qs1000",
1009cae86d4aSKarsten Keil 		AM_IO, AM_IND_IO, 1, 3,
1010cae86d4aSKarsten Keil 		&elsa_irq
1011cae86d4aSKarsten Keil 	},
1012cae86d4aSKarsten Keil 	{
1013cae86d4aSKarsten Keil 		INF_QS3000,
1014cae86d4aSKarsten Keil 		"Develo QuickStep 3000",
1015cae86d4aSKarsten Keil 		"qs3000",
1016cae86d4aSKarsten Keil 		AM_IO, AM_IND_IO, 1, 3,
1017cae86d4aSKarsten Keil 		&elsa_irq
1018cae86d4aSKarsten Keil 	},
1019cae86d4aSKarsten Keil 	{
1020cae86d4aSKarsten Keil 		INF_NICCY,
1021cae86d4aSKarsten Keil 		"Sagem NICCY",
1022cae86d4aSKarsten Keil 		"niccy",
1023cae86d4aSKarsten Keil 		AM_IO, AM_IND_IO, 0, 1,
1024cae86d4aSKarsten Keil 		&niccy_irq
1025cae86d4aSKarsten Keil 	},
1026cae86d4aSKarsten Keil 	{
1027cae86d4aSKarsten Keil 		INF_SCT_1,
1028cae86d4aSKarsten Keil 		"SciTel Quadro",
1029cae86d4aSKarsten Keil 		"p1_scitel",
1030cae86d4aSKarsten Keil 		AM_IO, AM_IND_IO, 1, 5,
1031cae86d4aSKarsten Keil 		&ipac_irq
1032cae86d4aSKarsten Keil 	},
1033cae86d4aSKarsten Keil 	{
1034cae86d4aSKarsten Keil 		INF_SCT_2,
1035cae86d4aSKarsten Keil 		"SciTel Quadro",
1036cae86d4aSKarsten Keil 		"p2_scitel",
1037cae86d4aSKarsten Keil 		AM_NONE, AM_IND_IO, 0, 4,
1038cae86d4aSKarsten Keil 		&ipac_irq
1039cae86d4aSKarsten Keil 	},
1040cae86d4aSKarsten Keil 	{
1041cae86d4aSKarsten Keil 		INF_SCT_3,
1042cae86d4aSKarsten Keil 		"SciTel Quadro",
1043cae86d4aSKarsten Keil 		"p3_scitel",
1044cae86d4aSKarsten Keil 		AM_NONE, AM_IND_IO, 0, 3,
1045cae86d4aSKarsten Keil 		&ipac_irq
1046cae86d4aSKarsten Keil 	},
1047cae86d4aSKarsten Keil 	{
1048cae86d4aSKarsten Keil 		INF_SCT_4,
1049cae86d4aSKarsten Keil 		"SciTel Quadro",
1050cae86d4aSKarsten Keil 		"p4_scitel",
1051cae86d4aSKarsten Keil 		AM_NONE, AM_IND_IO, 0, 2,
1052cae86d4aSKarsten Keil 		&ipac_irq
1053cae86d4aSKarsten Keil 	},
1054cae86d4aSKarsten Keil 	{
1055cae86d4aSKarsten Keil 		INF_GAZEL_R685,
1056cae86d4aSKarsten Keil 		"Gazel R685",
1057cae86d4aSKarsten Keil 		"gazel685",
1058cae86d4aSKarsten Keil 		AM_IO, AM_IO, 1, 2,
1059cae86d4aSKarsten Keil 		&gazel_irq
1060cae86d4aSKarsten Keil 	},
1061cae86d4aSKarsten Keil 	{
1062cae86d4aSKarsten Keil 		INF_GAZEL_R753,
1063cae86d4aSKarsten Keil 		"Gazel R753",
1064cae86d4aSKarsten Keil 		"gazel753",
1065cae86d4aSKarsten Keil 		AM_IO, AM_IND_IO, 1, 2,
1066cae86d4aSKarsten Keil 		&ipac_irq
1067cae86d4aSKarsten Keil 	},
1068cae86d4aSKarsten Keil 	{
1069cae86d4aSKarsten Keil 		INF_NONE,
1070cae86d4aSKarsten Keil 	}
1071cae86d4aSKarsten Keil };
1072cae86d4aSKarsten Keil 
1073cae86d4aSKarsten Keil static const struct inf_cinfo * __devinit
1074cae86d4aSKarsten Keil get_card_info(enum inf_types typ)
1075cae86d4aSKarsten Keil {
1076cae86d4aSKarsten Keil 	const struct inf_cinfo *ci = inf_card_info;
1077cae86d4aSKarsten Keil 
1078cae86d4aSKarsten Keil 	while (ci->typ != INF_NONE) {
1079cae86d4aSKarsten Keil 		if (ci->typ == typ)
1080cae86d4aSKarsten Keil 			return ci;
1081cae86d4aSKarsten Keil 		ci++;
1082cae86d4aSKarsten Keil 	}
1083cae86d4aSKarsten Keil 	return NULL;
1084cae86d4aSKarsten Keil }
1085cae86d4aSKarsten Keil 
1086cae86d4aSKarsten Keil static int __devinit
1087cae86d4aSKarsten Keil inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
1088cae86d4aSKarsten Keil {
1089cae86d4aSKarsten Keil 	int err = -ENOMEM;
1090cae86d4aSKarsten Keil 	struct inf_hw *card;
1091cae86d4aSKarsten Keil 
1092cae86d4aSKarsten Keil 	card = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
1093cae86d4aSKarsten Keil 	if (!card) {
1094cae86d4aSKarsten Keil 		pr_info("No memory for Infineon ISDN card\n");
1095cae86d4aSKarsten Keil 		return err;
1096cae86d4aSKarsten Keil 	}
1097cae86d4aSKarsten Keil 	card->pdev = pdev;
1098cae86d4aSKarsten Keil 	err = pci_enable_device(pdev);
1099cae86d4aSKarsten Keil 	if (err) {
1100cae86d4aSKarsten Keil 		kfree(card);
1101cae86d4aSKarsten Keil 		return err;
1102cae86d4aSKarsten Keil 	}
1103cae86d4aSKarsten Keil 	card->ci = get_card_info(ent->driver_data);
1104cae86d4aSKarsten Keil 	if (!card->ci) {
1105cae86d4aSKarsten Keil 		pr_info("mISDN: do not have informations about adapter at %s\n",
1106cae86d4aSKarsten Keil 			pci_name(pdev));
1107cae86d4aSKarsten Keil 		kfree(card);
1108cae86d4aSKarsten Keil 		return -EINVAL;
1109cae86d4aSKarsten Keil 	} else
1110cae86d4aSKarsten Keil 		pr_notice("mISDN: found adapter %s at %s\n",
1111cae86d4aSKarsten Keil 			card->ci->full, pci_name(pdev));
1112cae86d4aSKarsten Keil 
1113cae86d4aSKarsten Keil 	card->irq = pdev->irq;
1114cae86d4aSKarsten Keil 	pci_set_drvdata(pdev, card);
1115cae86d4aSKarsten Keil 	err = setup_instance(card);
1116cae86d4aSKarsten Keil 	if (err) {
1117cae86d4aSKarsten Keil 		pci_disable_device(card->pdev);
1118cae86d4aSKarsten Keil 		kfree(card);
1119cae86d4aSKarsten Keil 		pci_set_drvdata(pdev, NULL);
1120cae86d4aSKarsten Keil 	} else if (ent->driver_data == INF_SCT_1) {
1121cae86d4aSKarsten Keil 		int i;
1122cae86d4aSKarsten Keil 		struct inf_hw *sc;
1123cae86d4aSKarsten Keil 
1124cae86d4aSKarsten Keil 		for (i = 1; i < 4; i++) {
1125cae86d4aSKarsten Keil 			sc = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
1126cae86d4aSKarsten Keil 			if (!sc) {
1127cae86d4aSKarsten Keil 				release_card(card);
1128cae86d4aSKarsten Keil 				return -ENOMEM;
1129cae86d4aSKarsten Keil 			}
1130cae86d4aSKarsten Keil 			sc->irq = card->irq;
1131cae86d4aSKarsten Keil 			sc->pdev = card->pdev;
1132cae86d4aSKarsten Keil 			sc->ci = card->ci + i;
1133cae86d4aSKarsten Keil 			err = setup_instance(sc);
1134cae86d4aSKarsten Keil 			if (err) {
1135cae86d4aSKarsten Keil 				kfree(sc);
1136cae86d4aSKarsten Keil 				release_card(card);
1137f0f4d641SDarren Jenkins 				break;
1138cae86d4aSKarsten Keil 			} else
1139cae86d4aSKarsten Keil 				card->sc[i - 1] = sc;
1140cae86d4aSKarsten Keil 		}
1141cae86d4aSKarsten Keil 	}
1142cae86d4aSKarsten Keil 	return err;
1143cae86d4aSKarsten Keil }
1144cae86d4aSKarsten Keil 
1145cae86d4aSKarsten Keil static void __devexit
1146cae86d4aSKarsten Keil inf_remove(struct pci_dev *pdev)
1147cae86d4aSKarsten Keil {
1148cae86d4aSKarsten Keil 	struct inf_hw	*card = pci_get_drvdata(pdev);
1149cae86d4aSKarsten Keil 
1150cae86d4aSKarsten Keil 	if (card)
1151cae86d4aSKarsten Keil 		release_card(card);
1152cae86d4aSKarsten Keil 	else
1153cae86d4aSKarsten Keil 		pr_debug("%s: drvdata allready removed\n", __func__);
1154cae86d4aSKarsten Keil }
1155cae86d4aSKarsten Keil 
1156cae86d4aSKarsten Keil static struct pci_driver infineon_driver = {
1157cae86d4aSKarsten Keil 	.name = "ISDN Infineon pci",
1158cae86d4aSKarsten Keil 	.probe = inf_probe,
1159cae86d4aSKarsten Keil 	.remove = __devexit_p(inf_remove),
1160cae86d4aSKarsten Keil 	.id_table = infineon_ids,
1161cae86d4aSKarsten Keil };
1162cae86d4aSKarsten Keil 
1163cae86d4aSKarsten Keil static int __init
1164cae86d4aSKarsten Keil infineon_init(void)
1165cae86d4aSKarsten Keil {
1166cae86d4aSKarsten Keil 	int err;
1167cae86d4aSKarsten Keil 
1168cae86d4aSKarsten Keil 	pr_notice("Infineon ISDN Driver Rev. %s\n", INFINEON_REV);
1169cae86d4aSKarsten Keil 	err = pci_register_driver(&infineon_driver);
1170cae86d4aSKarsten Keil 	return err;
1171cae86d4aSKarsten Keil }
1172cae86d4aSKarsten Keil 
1173cae86d4aSKarsten Keil static void __exit
1174cae86d4aSKarsten Keil infineon_cleanup(void)
1175cae86d4aSKarsten Keil {
1176cae86d4aSKarsten Keil 	pci_unregister_driver(&infineon_driver);
1177cae86d4aSKarsten Keil }
1178cae86d4aSKarsten Keil 
1179cae86d4aSKarsten Keil module_init(infineon_init);
1180cae86d4aSKarsten Keil module_exit(infineon_cleanup);
1181