xref: /linux/drivers/isdn/hardware/mISDN/speedfax.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
182c29810SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2da2272c9SKarsten Keil /*
3da2272c9SKarsten Keil  * speedfax.c	low level stuff for Sedlbauer Speedfax+ cards
4da2272c9SKarsten Keil  *		based on the ISAR DSP
5da2272c9SKarsten Keil  *		Thanks to Sedlbauer AG for informations and HW
6da2272c9SKarsten Keil  *
7da2272c9SKarsten Keil  * Author       Karsten Keil <keil@isdn4linux.de>
8da2272c9SKarsten Keil  *
9da2272c9SKarsten Keil  * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
10da2272c9SKarsten Keil  */
11da2272c9SKarsten Keil 
12a6b7a407SAlexey Dobriyan #include <linux/interrupt.h>
13da2272c9SKarsten Keil #include <linux/module.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
15da2272c9SKarsten Keil #include <linux/pci.h>
16da2272c9SKarsten Keil #include <linux/delay.h>
17da2272c9SKarsten Keil #include <linux/mISDNhw.h>
18da2272c9SKarsten Keil #include <linux/firmware.h>
19da2272c9SKarsten Keil #include "ipac.h"
20da2272c9SKarsten Keil #include "isar.h"
21da2272c9SKarsten Keil 
22da2272c9SKarsten Keil #define SPEEDFAX_REV	"2.0"
23da2272c9SKarsten Keil 
24da2272c9SKarsten Keil #define PCI_SUBVENDOR_SPEEDFAX_PYRAMID	0x51
25da2272c9SKarsten Keil #define PCI_SUBVENDOR_SPEEDFAX_PCI	0x54
26da2272c9SKarsten Keil #define PCI_SUB_ID_SEDLBAUER		0x01
27da2272c9SKarsten Keil 
28da2272c9SKarsten Keil #define SFAX_PCI_ADDR		0xc8
29da2272c9SKarsten Keil #define SFAX_PCI_ISAC		0xd0
30da2272c9SKarsten Keil #define SFAX_PCI_ISAR		0xe0
31da2272c9SKarsten Keil 
32da2272c9SKarsten Keil /* TIGER 100 Registers */
33da2272c9SKarsten Keil 
34da2272c9SKarsten Keil #define TIGER_RESET_ADDR	0x00
35da2272c9SKarsten Keil #define TIGER_EXTERN_RESET_ON	0x01
36da2272c9SKarsten Keil #define TIGER_EXTERN_RESET_OFF	0x00
37da2272c9SKarsten Keil #define TIGER_AUX_CTRL		0x02
38da2272c9SKarsten Keil #define TIGER_AUX_DATA		0x03
39da2272c9SKarsten Keil #define TIGER_AUX_IRQMASK	0x05
40da2272c9SKarsten Keil #define TIGER_AUX_STATUS	0x07
41da2272c9SKarsten Keil 
42da2272c9SKarsten Keil /* Tiger AUX BITs */
43da2272c9SKarsten Keil #define SFAX_AUX_IOMASK		0xdd	/* 1 and 5 are inputs */
44da2272c9SKarsten Keil #define SFAX_ISAR_RESET_BIT_OFF 0x00
45da2272c9SKarsten Keil #define SFAX_ISAR_RESET_BIT_ON	0x01
46da2272c9SKarsten Keil #define SFAX_TIGER_IRQ_BIT	0x02
47da2272c9SKarsten Keil #define SFAX_LED1_BIT		0x08
48da2272c9SKarsten Keil #define SFAX_LED2_BIT		0x10
49da2272c9SKarsten Keil 
50da2272c9SKarsten Keil #define SFAX_PCI_RESET_ON	(SFAX_ISAR_RESET_BIT_ON)
51da2272c9SKarsten Keil #define SFAX_PCI_RESET_OFF	(SFAX_LED1_BIT | SFAX_LED2_BIT)
52da2272c9SKarsten Keil 
53da2272c9SKarsten Keil static int sfax_cnt;
54da2272c9SKarsten Keil static u32 debug;
55da2272c9SKarsten Keil static u32 irqloops = 4;
56da2272c9SKarsten Keil 
57da2272c9SKarsten Keil struct sfax_hw {
58da2272c9SKarsten Keil 	struct list_head	list;
59da2272c9SKarsten Keil 	struct pci_dev		*pdev;
60da2272c9SKarsten Keil 	char			name[MISDN_MAX_IDLEN];
61da2272c9SKarsten Keil 	u32			irq;
62da2272c9SKarsten Keil 	u32			irqcnt;
63da2272c9SKarsten Keil 	u32			cfg;
64da2272c9SKarsten Keil 	struct _ioport		p_isac;
65da2272c9SKarsten Keil 	struct _ioport		p_isar;
66da2272c9SKarsten Keil 	u8			aux_data;
67da2272c9SKarsten Keil 	spinlock_t		lock;	/* HW access lock */
68da2272c9SKarsten Keil 	struct isac_hw		isac;
69da2272c9SKarsten Keil 	struct isar_hw		isar;
70da2272c9SKarsten Keil };
71da2272c9SKarsten Keil 
72da2272c9SKarsten Keil static LIST_HEAD(Cards);
73da2272c9SKarsten Keil static DEFINE_RWLOCK(card_lock); /* protect Cards */
74da2272c9SKarsten Keil 
75da2272c9SKarsten Keil static void
_set_debug(struct sfax_hw * card)76da2272c9SKarsten Keil _set_debug(struct sfax_hw *card)
77da2272c9SKarsten Keil {
78da2272c9SKarsten Keil 	card->isac.dch.debug = debug;
79da2272c9SKarsten Keil 	card->isar.ch[0].bch.debug = debug;
80da2272c9SKarsten Keil 	card->isar.ch[1].bch.debug = debug;
81da2272c9SKarsten Keil }
82da2272c9SKarsten Keil 
83da2272c9SKarsten Keil static int
set_debug(const char * val,const struct kernel_param * kp)84e4dca7b7SKees Cook set_debug(const char *val, const struct kernel_param *kp)
85da2272c9SKarsten Keil {
86da2272c9SKarsten Keil 	int ret;
87da2272c9SKarsten Keil 	struct sfax_hw *card;
88da2272c9SKarsten Keil 
89da2272c9SKarsten Keil 	ret = param_set_uint(val, kp);
90da2272c9SKarsten Keil 	if (!ret) {
91da2272c9SKarsten Keil 		read_lock(&card_lock);
92da2272c9SKarsten Keil 		list_for_each_entry(card, &Cards, list)
93da2272c9SKarsten Keil 			_set_debug(card);
94da2272c9SKarsten Keil 		read_unlock(&card_lock);
95da2272c9SKarsten Keil 	}
96da2272c9SKarsten Keil 	return ret;
97da2272c9SKarsten Keil }
98da2272c9SKarsten Keil 
99da2272c9SKarsten Keil MODULE_AUTHOR("Karsten Keil");
100*2ebb87f4SJeff Johnson MODULE_DESCRIPTION("mISDN driver for Sedlbauer Speedfax+ cards");
101da2272c9SKarsten Keil MODULE_LICENSE("GPL v2");
102da2272c9SKarsten Keil MODULE_VERSION(SPEEDFAX_REV);
1034a9b5e50SBen Hutchings MODULE_FIRMWARE("isdn/ISAR.BIN");
104da2272c9SKarsten Keil module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
105da2272c9SKarsten Keil MODULE_PARM_DESC(debug, "Speedfax debug mask");
106da2272c9SKarsten Keil module_param(irqloops, uint, S_IRUGO | S_IWUSR);
107da2272c9SKarsten Keil MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)");
108da2272c9SKarsten Keil 
IOFUNC_IND(ISAC,sfax_hw,p_isac)109da2272c9SKarsten Keil IOFUNC_IND(ISAC, sfax_hw, p_isac)
110da2272c9SKarsten Keil IOFUNC_IND(ISAR, sfax_hw, p_isar)
111da2272c9SKarsten Keil 
112da2272c9SKarsten Keil static irqreturn_t
113da2272c9SKarsten Keil speedfax_irq(int intno, void *dev_id)
114da2272c9SKarsten Keil {
115da2272c9SKarsten Keil 	struct sfax_hw	*sf = dev_id;
116da2272c9SKarsten Keil 	u8 val;
117da2272c9SKarsten Keil 	int cnt = irqloops;
118da2272c9SKarsten Keil 
119da2272c9SKarsten Keil 	spin_lock(&sf->lock);
120da2272c9SKarsten Keil 	val = inb(sf->cfg + TIGER_AUX_STATUS);
121da2272c9SKarsten Keil 	if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */
122da2272c9SKarsten Keil 		spin_unlock(&sf->lock);
123da2272c9SKarsten Keil 		return IRQ_NONE; /* shared */
124da2272c9SKarsten Keil 	}
125da2272c9SKarsten Keil 	sf->irqcnt++;
126da2272c9SKarsten Keil 	val = ReadISAR_IND(sf, ISAR_IRQBIT);
127da2272c9SKarsten Keil Start_ISAR:
128da2272c9SKarsten Keil 	if (val & ISAR_IRQSTA)
129da2272c9SKarsten Keil 		mISDNisar_irq(&sf->isar);
130da2272c9SKarsten Keil 	val = ReadISAC_IND(sf, ISAC_ISTA);
131da2272c9SKarsten Keil 	if (val)
132da2272c9SKarsten Keil 		mISDNisac_irq(&sf->isac, val);
133da2272c9SKarsten Keil 	val = ReadISAR_IND(sf, ISAR_IRQBIT);
134da2272c9SKarsten Keil 	if ((val & ISAR_IRQSTA) && cnt--)
135da2272c9SKarsten Keil 		goto Start_ISAR;
136da2272c9SKarsten Keil 	if (cnt < irqloops)
137da2272c9SKarsten Keil 		pr_debug("%s: %d irqloops cpu%d\n", sf->name,
138da2272c9SKarsten Keil 			 irqloops - cnt, smp_processor_id());
139da2272c9SKarsten Keil 	if (irqloops && !cnt)
140da2272c9SKarsten Keil 		pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name,
141da2272c9SKarsten Keil 			  irqloops, smp_processor_id());
142da2272c9SKarsten Keil 	spin_unlock(&sf->lock);
143da2272c9SKarsten Keil 	return IRQ_HANDLED;
144da2272c9SKarsten Keil }
145da2272c9SKarsten Keil 
146da2272c9SKarsten Keil static void
enable_hwirq(struct sfax_hw * sf)147da2272c9SKarsten Keil enable_hwirq(struct sfax_hw *sf)
148da2272c9SKarsten Keil {
149da2272c9SKarsten Keil 	WriteISAC_IND(sf, ISAC_MASK, 0);
150da2272c9SKarsten Keil 	WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK);
151da2272c9SKarsten Keil 	outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK);
152da2272c9SKarsten Keil }
153da2272c9SKarsten Keil 
154da2272c9SKarsten Keil static void
disable_hwirq(struct sfax_hw * sf)155da2272c9SKarsten Keil disable_hwirq(struct sfax_hw *sf)
156da2272c9SKarsten Keil {
157da2272c9SKarsten Keil 	WriteISAC_IND(sf, ISAC_MASK, 0xFF);
158da2272c9SKarsten Keil 	WriteISAR_IND(sf, ISAR_IRQBIT, 0);
159da2272c9SKarsten Keil 	outb(0, sf->cfg + TIGER_AUX_IRQMASK);
160da2272c9SKarsten Keil }
161da2272c9SKarsten Keil 
162da2272c9SKarsten Keil static void
reset_speedfax(struct sfax_hw * sf)163da2272c9SKarsten Keil reset_speedfax(struct sfax_hw *sf)
164da2272c9SKarsten Keil {
165da2272c9SKarsten Keil 
166da2272c9SKarsten Keil 	pr_debug("%s: resetting card\n", sf->name);
167da2272c9SKarsten Keil 	outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR);
168da2272c9SKarsten Keil 	outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA);
169da2272c9SKarsten Keil 	mdelay(1);
170da2272c9SKarsten Keil 	outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR);
171da2272c9SKarsten Keil 	sf->aux_data = SFAX_PCI_RESET_OFF;
172da2272c9SKarsten Keil 	outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
173da2272c9SKarsten Keil 	mdelay(1);
174da2272c9SKarsten Keil }
175da2272c9SKarsten Keil 
176da2272c9SKarsten Keil static int
sfax_ctrl(struct sfax_hw * sf,u32 cmd,u_long arg)177da2272c9SKarsten Keil sfax_ctrl(struct sfax_hw  *sf, u32 cmd, u_long arg)
178da2272c9SKarsten Keil {
179da2272c9SKarsten Keil 	int ret = 0;
180da2272c9SKarsten Keil 
181da2272c9SKarsten Keil 	switch (cmd) {
182da2272c9SKarsten Keil 	case HW_RESET_REQ:
183da2272c9SKarsten Keil 		reset_speedfax(sf);
184da2272c9SKarsten Keil 		break;
185da2272c9SKarsten Keil 	case HW_ACTIVATE_IND:
186da2272c9SKarsten Keil 		if (arg & 1)
187da2272c9SKarsten Keil 			sf->aux_data &= ~SFAX_LED1_BIT;
188da2272c9SKarsten Keil 		if (arg & 2)
189da2272c9SKarsten Keil 			sf->aux_data &= ~SFAX_LED2_BIT;
190da2272c9SKarsten Keil 		outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
191da2272c9SKarsten Keil 		break;
192da2272c9SKarsten Keil 	case HW_DEACT_IND:
193da2272c9SKarsten Keil 		if (arg & 1)
194da2272c9SKarsten Keil 			sf->aux_data |= SFAX_LED1_BIT;
195da2272c9SKarsten Keil 		if (arg & 2)
196da2272c9SKarsten Keil 			sf->aux_data |= SFAX_LED2_BIT;
197da2272c9SKarsten Keil 		outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
198da2272c9SKarsten Keil 		break;
199da2272c9SKarsten Keil 	default:
200da2272c9SKarsten Keil 		pr_info("%s: %s unknown command %x %lx\n",
201da2272c9SKarsten Keil 			sf->name, __func__, cmd, arg);
202da2272c9SKarsten Keil 		ret = -EINVAL;
203da2272c9SKarsten Keil 		break;
204da2272c9SKarsten Keil 	}
205da2272c9SKarsten Keil 	return ret;
206da2272c9SKarsten Keil }
207da2272c9SKarsten Keil 
208da2272c9SKarsten Keil static int
channel_ctrl(struct sfax_hw * sf,struct mISDN_ctrl_req * cq)209da2272c9SKarsten Keil channel_ctrl(struct sfax_hw  *sf, struct mISDN_ctrl_req *cq)
210da2272c9SKarsten Keil {
211da2272c9SKarsten Keil 	int	ret = 0;
212da2272c9SKarsten Keil 
213da2272c9SKarsten Keil 	switch (cq->op) {
214da2272c9SKarsten Keil 	case MISDN_CTRL_GETOP:
215c626c127SKarsten Keil 		cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3;
216da2272c9SKarsten Keil 		break;
217da2272c9SKarsten Keil 	case MISDN_CTRL_LOOP:
218da2272c9SKarsten Keil 		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
219da2272c9SKarsten Keil 		if (cq->channel < 0 || cq->channel > 3) {
220da2272c9SKarsten Keil 			ret = -EINVAL;
221da2272c9SKarsten Keil 			break;
222da2272c9SKarsten Keil 		}
223da2272c9SKarsten Keil 		ret = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel);
224da2272c9SKarsten Keil 		break;
225c626c127SKarsten Keil 	case MISDN_CTRL_L1_TIMER3:
226c626c127SKarsten Keil 		ret = sf->isac.ctrl(&sf->isac, HW_TIMER3_VALUE, cq->p1);
227c626c127SKarsten Keil 		break;
228da2272c9SKarsten Keil 	default:
229da2272c9SKarsten Keil 		pr_info("%s: unknown Op %x\n", sf->name, cq->op);
230da2272c9SKarsten Keil 		ret = -EINVAL;
231da2272c9SKarsten Keil 		break;
232da2272c9SKarsten Keil 	}
233da2272c9SKarsten Keil 	return ret;
234da2272c9SKarsten Keil }
235da2272c9SKarsten Keil 
236da2272c9SKarsten Keil static int
sfax_dctrl(struct mISDNchannel * ch,u32 cmd,void * arg)237da2272c9SKarsten Keil sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
238da2272c9SKarsten Keil {
239da2272c9SKarsten Keil 	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
240da2272c9SKarsten Keil 	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
241da2272c9SKarsten Keil 	struct sfax_hw		*sf = dch->hw;
242da2272c9SKarsten Keil 	struct channel_req	*rq;
243da2272c9SKarsten Keil 	int			err = 0;
244da2272c9SKarsten Keil 
245da2272c9SKarsten Keil 	pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg);
246da2272c9SKarsten Keil 	switch (cmd) {
247da2272c9SKarsten Keil 	case OPEN_CHANNEL:
248da2272c9SKarsten Keil 		rq = arg;
249da2272c9SKarsten Keil 		if (rq->protocol == ISDN_P_TE_S0)
250da2272c9SKarsten Keil 			err = sf->isac.open(&sf->isac, rq);
251da2272c9SKarsten Keil 		else
252da2272c9SKarsten Keil 			err = sf->isar.open(&sf->isar, rq);
253da2272c9SKarsten Keil 		if (err)
254da2272c9SKarsten Keil 			break;
255da2272c9SKarsten Keil 		if (!try_module_get(THIS_MODULE))
256da2272c9SKarsten Keil 			pr_info("%s: cannot get module\n", sf->name);
257da2272c9SKarsten Keil 		break;
258da2272c9SKarsten Keil 	case CLOSE_CHANNEL:
259da2272c9SKarsten Keil 		pr_debug("%s: dev(%d) close from %p\n", sf->name,
260da2272c9SKarsten Keil 			 dch->dev.id, __builtin_return_address(0));
261da2272c9SKarsten Keil 		module_put(THIS_MODULE);
262da2272c9SKarsten Keil 		break;
263da2272c9SKarsten Keil 	case CONTROL_CHANNEL:
264da2272c9SKarsten Keil 		err = channel_ctrl(sf, arg);
265da2272c9SKarsten Keil 		break;
266da2272c9SKarsten Keil 	default:
267da2272c9SKarsten Keil 		pr_debug("%s: unknown command %x\n", sf->name, cmd);
268da2272c9SKarsten Keil 		return -EINVAL;
269da2272c9SKarsten Keil 	}
270da2272c9SKarsten Keil 	return err;
271da2272c9SKarsten Keil }
272da2272c9SKarsten Keil 
273ed5a84cdSGreg Kroah-Hartman static int
init_card(struct sfax_hw * sf)274da2272c9SKarsten Keil init_card(struct sfax_hw *sf)
275da2272c9SKarsten Keil {
276da2272c9SKarsten Keil 	int	ret, cnt = 3;
277da2272c9SKarsten Keil 	u_long	flags;
278da2272c9SKarsten Keil 
279da2272c9SKarsten Keil 	ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf);
280da2272c9SKarsten Keil 	if (ret) {
281da2272c9SKarsten Keil 		pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq);
282da2272c9SKarsten Keil 		return ret;
283da2272c9SKarsten Keil 	}
284da2272c9SKarsten Keil 	while (cnt--) {
285da2272c9SKarsten Keil 		spin_lock_irqsave(&sf->lock, flags);
286da2272c9SKarsten Keil 		ret = sf->isac.init(&sf->isac);
287da2272c9SKarsten Keil 		if (ret) {
288da2272c9SKarsten Keil 			spin_unlock_irqrestore(&sf->lock, flags);
289da2272c9SKarsten Keil 			pr_info("%s: ISAC init failed with %d\n",
290da2272c9SKarsten Keil 				sf->name, ret);
291da2272c9SKarsten Keil 			break;
292da2272c9SKarsten Keil 		}
293da2272c9SKarsten Keil 		enable_hwirq(sf);
294da2272c9SKarsten Keil 		/* RESET Receiver and Transmitter */
295da2272c9SKarsten Keil 		WriteISAC_IND(sf, ISAC_CMDR, 0x41);
296da2272c9SKarsten Keil 		spin_unlock_irqrestore(&sf->lock, flags);
297da2272c9SKarsten Keil 		msleep_interruptible(10);
298da2272c9SKarsten Keil 		if (debug & DEBUG_HW)
299da2272c9SKarsten Keil 			pr_notice("%s: IRQ %d count %d\n", sf->name,
300da2272c9SKarsten Keil 				  sf->irq, sf->irqcnt);
301da2272c9SKarsten Keil 		if (!sf->irqcnt) {
302da2272c9SKarsten Keil 			pr_info("%s: IRQ(%d) got no requests during init %d\n",
303da2272c9SKarsten Keil 				sf->name, sf->irq, 3 - cnt);
304da2272c9SKarsten Keil 		} else
305da2272c9SKarsten Keil 			return 0;
306da2272c9SKarsten Keil 	}
307da2272c9SKarsten Keil 	free_irq(sf->irq, sf);
308da2272c9SKarsten Keil 	return -EIO;
309da2272c9SKarsten Keil }
310da2272c9SKarsten Keil 
311da2272c9SKarsten Keil 
312ed5a84cdSGreg Kroah-Hartman static int
setup_speedfax(struct sfax_hw * sf)313da2272c9SKarsten Keil setup_speedfax(struct sfax_hw *sf)
314da2272c9SKarsten Keil {
315da2272c9SKarsten Keil 	u_long flags;
316da2272c9SKarsten Keil 
317da2272c9SKarsten Keil 	if (!request_region(sf->cfg, 256, sf->name)) {
318da2272c9SKarsten Keil 		pr_info("mISDN: %s config port %x-%x already in use\n",
319da2272c9SKarsten Keil 			sf->name, sf->cfg, sf->cfg + 255);
320da2272c9SKarsten Keil 		return -EIO;
321da2272c9SKarsten Keil 	}
322da2272c9SKarsten Keil 	outb(0xff, sf->cfg);
323da2272c9SKarsten Keil 	outb(0, sf->cfg);
324da2272c9SKarsten Keil 	outb(0xdd, sf->cfg + TIGER_AUX_CTRL);
325da2272c9SKarsten Keil 	outb(0, sf->cfg + TIGER_AUX_IRQMASK);
326da2272c9SKarsten Keil 
327da2272c9SKarsten Keil 	sf->isac.type = IPAC_TYPE_ISAC;
328da2272c9SKarsten Keil 	sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR;
329da2272c9SKarsten Keil 	sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC;
330da2272c9SKarsten Keil 	sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR;
331da2272c9SKarsten Keil 	sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR;
332da2272c9SKarsten Keil 	ASSIGN_FUNC(IND, ISAC, sf->isac);
333da2272c9SKarsten Keil 	ASSIGN_FUNC(IND, ISAR, sf->isar);
334da2272c9SKarsten Keil 	spin_lock_irqsave(&sf->lock, flags);
335da2272c9SKarsten Keil 	reset_speedfax(sf);
336da2272c9SKarsten Keil 	disable_hwirq(sf);
337da2272c9SKarsten Keil 	spin_unlock_irqrestore(&sf->lock, flags);
338da2272c9SKarsten Keil 	return 0;
339da2272c9SKarsten Keil }
340da2272c9SKarsten Keil 
341da2272c9SKarsten Keil static void
release_card(struct sfax_hw * card)342da2272c9SKarsten Keil release_card(struct sfax_hw *card) {
343da2272c9SKarsten Keil 	u_long	flags;
344da2272c9SKarsten Keil 
345da2272c9SKarsten Keil 	spin_lock_irqsave(&card->lock, flags);
346da2272c9SKarsten Keil 	disable_hwirq(card);
347da2272c9SKarsten Keil 	spin_unlock_irqrestore(&card->lock, flags);
348da2272c9SKarsten Keil 	card->isac.release(&card->isac);
349da2272c9SKarsten Keil 	free_irq(card->irq, card);
350da2272c9SKarsten Keil 	card->isar.release(&card->isar);
351da2272c9SKarsten Keil 	mISDN_unregister_device(&card->isac.dch.dev);
352da2272c9SKarsten Keil 	release_region(card->cfg, 256);
353da2272c9SKarsten Keil 	pci_disable_device(card->pdev);
354da2272c9SKarsten Keil 	pci_set_drvdata(card->pdev, NULL);
355da2272c9SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
356da2272c9SKarsten Keil 	list_del(&card->list);
357da2272c9SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
358da2272c9SKarsten Keil 	kfree(card);
359da2272c9SKarsten Keil 	sfax_cnt--;
360da2272c9SKarsten Keil }
361da2272c9SKarsten Keil 
362ed5a84cdSGreg Kroah-Hartman static int
setup_instance(struct sfax_hw * card)363da2272c9SKarsten Keil setup_instance(struct sfax_hw *card)
364da2272c9SKarsten Keil {
365da2272c9SKarsten Keil 	const struct firmware *firmware;
366da2272c9SKarsten Keil 	int i, err;
367da2272c9SKarsten Keil 	u_long flags;
368da2272c9SKarsten Keil 
369da2272c9SKarsten Keil 	snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1);
370da2272c9SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
371da2272c9SKarsten Keil 	list_add_tail(&card->list, &Cards);
372da2272c9SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
373da2272c9SKarsten Keil 	_set_debug(card);
374da2272c9SKarsten Keil 	spin_lock_init(&card->lock);
375da2272c9SKarsten Keil 	card->isac.hwlock = &card->lock;
376da2272c9SKarsten Keil 	card->isar.hwlock = &card->lock;
377da2272c9SKarsten Keil 	card->isar.ctrl = (void *)&sfax_ctrl;
378da2272c9SKarsten Keil 	card->isac.name = card->name;
379da2272c9SKarsten Keil 	card->isar.name = card->name;
380da2272c9SKarsten Keil 	card->isar.owner = THIS_MODULE;
381da2272c9SKarsten Keil 
382da2272c9SKarsten Keil 	err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev);
383da2272c9SKarsten Keil 	if (err < 0) {
384da2272c9SKarsten Keil 		pr_info("%s: firmware request failed %d\n",
385da2272c9SKarsten Keil 			card->name, err);
386da2272c9SKarsten Keil 		goto error_fw;
387da2272c9SKarsten Keil 	}
388da2272c9SKarsten Keil 	if (debug & DEBUG_HW)
389da2272c9SKarsten Keil 		pr_notice("%s: got firmware %zu bytes\n",
390da2272c9SKarsten Keil 			  card->name, firmware->size);
391da2272c9SKarsten Keil 
392da2272c9SKarsten Keil 	mISDNisac_init(&card->isac, card);
393da2272c9SKarsten Keil 
394da2272c9SKarsten Keil 	card->isac.dch.dev.D.ctrl = sfax_dctrl;
395da2272c9SKarsten Keil 	card->isac.dch.dev.Bprotocols =
396da2272c9SKarsten Keil 		mISDNisar_init(&card->isar, card);
397da2272c9SKarsten Keil 	for (i = 0; i < 2; i++) {
398da2272c9SKarsten Keil 		set_channelmap(i + 1, card->isac.dch.dev.channelmap);
399da2272c9SKarsten Keil 		list_add(&card->isar.ch[i].bch.ch.list,
400da2272c9SKarsten Keil 			 &card->isac.dch.dev.bchannels);
401da2272c9SKarsten Keil 	}
402da2272c9SKarsten Keil 
403da2272c9SKarsten Keil 	err = setup_speedfax(card);
404da2272c9SKarsten Keil 	if (err)
405da2272c9SKarsten Keil 		goto error_setup;
406da2272c9SKarsten Keil 	err = card->isar.init(&card->isar);
407da2272c9SKarsten Keil 	if (err)
408da2272c9SKarsten Keil 		goto error;
409da2272c9SKarsten Keil 	err = mISDN_register_device(&card->isac.dch.dev,
410da2272c9SKarsten Keil 				    &card->pdev->dev, card->name);
411da2272c9SKarsten Keil 	if (err)
412da2272c9SKarsten Keil 		goto error;
413da2272c9SKarsten Keil 	err = init_card(card);
414da2272c9SKarsten Keil 	if (err)
415da2272c9SKarsten Keil 		goto error_init;
416da2272c9SKarsten Keil 	err = card->isar.firmware(&card->isar, firmware->data, firmware->size);
417da2272c9SKarsten Keil 	if (!err)  {
418da2272c9SKarsten Keil 		release_firmware(firmware);
419da2272c9SKarsten Keil 		sfax_cnt++;
420da2272c9SKarsten Keil 		pr_notice("SpeedFax %d cards installed\n", sfax_cnt);
421da2272c9SKarsten Keil 		return 0;
422da2272c9SKarsten Keil 	}
423da2272c9SKarsten Keil 	disable_hwirq(card);
424da2272c9SKarsten Keil 	free_irq(card->irq, card);
425da2272c9SKarsten Keil error_init:
426da2272c9SKarsten Keil 	mISDN_unregister_device(&card->isac.dch.dev);
427da2272c9SKarsten Keil error:
428da2272c9SKarsten Keil 	release_region(card->cfg, 256);
429da2272c9SKarsten Keil error_setup:
430da2272c9SKarsten Keil 	card->isac.release(&card->isac);
431da2272c9SKarsten Keil 	card->isar.release(&card->isar);
432da2272c9SKarsten Keil 	release_firmware(firmware);
433da2272c9SKarsten Keil error_fw:
434da2272c9SKarsten Keil 	pci_disable_device(card->pdev);
435da2272c9SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
436da2272c9SKarsten Keil 	list_del(&card->list);
437da2272c9SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
438da2272c9SKarsten Keil 	kfree(card);
439da2272c9SKarsten Keil 	return err;
440da2272c9SKarsten Keil }
441da2272c9SKarsten Keil 
442ed5a84cdSGreg Kroah-Hartman static int
sfaxpci_probe(struct pci_dev * pdev,const struct pci_device_id * ent)443da2272c9SKarsten Keil sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
444da2272c9SKarsten Keil {
445da2272c9SKarsten Keil 	int err = -ENOMEM;
446da2272c9SKarsten Keil 	struct sfax_hw *card = kzalloc(sizeof(struct sfax_hw), GFP_KERNEL);
447da2272c9SKarsten Keil 
448da2272c9SKarsten Keil 	if (!card) {
449da2272c9SKarsten Keil 		pr_info("No memory for Speedfax+ PCI\n");
450da2272c9SKarsten Keil 		return err;
451da2272c9SKarsten Keil 	}
452da2272c9SKarsten Keil 	card->pdev = pdev;
453da2272c9SKarsten Keil 	err = pci_enable_device(pdev);
454da2272c9SKarsten Keil 	if (err) {
455da2272c9SKarsten Keil 		kfree(card);
456da2272c9SKarsten Keil 		return err;
457da2272c9SKarsten Keil 	}
458da2272c9SKarsten Keil 
459da2272c9SKarsten Keil 	pr_notice("mISDN: Speedfax found adapter %s at %s\n",
460da2272c9SKarsten Keil 		  (char *)ent->driver_data, pci_name(pdev));
461da2272c9SKarsten Keil 
462da2272c9SKarsten Keil 	card->cfg = pci_resource_start(pdev, 0);
463da2272c9SKarsten Keil 	card->irq = pdev->irq;
464da2272c9SKarsten Keil 	pci_set_drvdata(pdev, card);
465da2272c9SKarsten Keil 	err = setup_instance(card);
466da2272c9SKarsten Keil 	if (err)
467da2272c9SKarsten Keil 		pci_set_drvdata(pdev, NULL);
468da2272c9SKarsten Keil 	return err;
469da2272c9SKarsten Keil }
470da2272c9SKarsten Keil 
471ed5a84cdSGreg Kroah-Hartman static void
sfax_remove_pci(struct pci_dev * pdev)472da2272c9SKarsten Keil sfax_remove_pci(struct pci_dev *pdev)
473da2272c9SKarsten Keil {
474da2272c9SKarsten Keil 	struct sfax_hw	*card = pci_get_drvdata(pdev);
475da2272c9SKarsten Keil 
476da2272c9SKarsten Keil 	if (card)
477da2272c9SKarsten Keil 		release_card(card);
478da2272c9SKarsten Keil 	else
479698f9315SUwe Kleine-König 		pr_debug("%s: drvdata already removed\n", __func__);
480da2272c9SKarsten Keil }
481da2272c9SKarsten Keil 
482ed5a84cdSGreg Kroah-Hartman static struct pci_device_id sfaxpci_ids[] = {
483da2272c9SKarsten Keil 	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
484da2272c9SKarsten Keil 	  PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER,
485da2272c9SKarsten Keil 	  0, 0, (unsigned long) "Pyramid Speedfax + PCI"
486da2272c9SKarsten Keil 	},
487da2272c9SKarsten Keil 	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
488da2272c9SKarsten Keil 	  PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER,
489da2272c9SKarsten Keil 	  0, 0, (unsigned long) "Sedlbauer Speedfax + PCI"
490da2272c9SKarsten Keil 	},
491da2272c9SKarsten Keil 	{ }
492da2272c9SKarsten Keil };
493da2272c9SKarsten Keil MODULE_DEVICE_TABLE(pci, sfaxpci_ids);
494da2272c9SKarsten Keil 
495da2272c9SKarsten Keil static struct pci_driver sfaxpci_driver = {
496da2272c9SKarsten Keil 	.name = "speedfax+ pci",
497da2272c9SKarsten Keil 	.probe = sfaxpci_probe,
498ed5a84cdSGreg Kroah-Hartman 	.remove = sfax_remove_pci,
499da2272c9SKarsten Keil 	.id_table = sfaxpci_ids,
500da2272c9SKarsten Keil };
501da2272c9SKarsten Keil 
502da2272c9SKarsten Keil static int __init
Speedfax_init(void)503da2272c9SKarsten Keil Speedfax_init(void)
504da2272c9SKarsten Keil {
505da2272c9SKarsten Keil 	int err;
506da2272c9SKarsten Keil 
507da2272c9SKarsten Keil 	pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n",
508da2272c9SKarsten Keil 		  SPEEDFAX_REV);
509da2272c9SKarsten Keil 	err = pci_register_driver(&sfaxpci_driver);
510da2272c9SKarsten Keil 	return err;
511da2272c9SKarsten Keil }
512da2272c9SKarsten Keil 
513da2272c9SKarsten Keil static void __exit
Speedfax_cleanup(void)514da2272c9SKarsten Keil Speedfax_cleanup(void)
515da2272c9SKarsten Keil {
516da2272c9SKarsten Keil 	pci_unregister_driver(&sfaxpci_driver);
517da2272c9SKarsten Keil }
518da2272c9SKarsten Keil 
519da2272c9SKarsten Keil module_init(Speedfax_init);
520da2272c9SKarsten Keil module_exit(Speedfax_cleanup);
521