File: /usr/src/linux/drivers/isdn/hysdn/boardergo.c

1     /* $Id: boardergo.c,v 1.5.6.5 2001/07/18 16:02:16 kai Exp $
2     
3      * Linux driver for HYSDN cards, specific routines for ergo type boards.
4      *
5      * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
6      * DPRAM interface and layout with only minor differences all related
7      * stuff is done here, not in separate modules.
8      *
9      * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
10      *
11      * Copyright 1999  by Werner Cornelius (werner@titro.de)
12      *
13      * This program is free software; you can redistribute it and/or modify
14      * it under the terms of the GNU General Public License as published by
15      * the Free Software Foundation; either version 2, or (at your option)
16      * any later version.
17      *
18      * This program is distributed in the hope that it will be useful,
19      * but WITHOUT ANY WARRANTY; without even the implied warranty of
20      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21      * GNU General Public License for more details.
22      *
23      * You should have received a copy of the GNU General Public License
24      * along with this program; if not, write to the Free Software
25      * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26      *
27      */
28     
29     #define __NO_VERSION__
30     #include <linux/config.h>
31     #include <linux/module.h>
32     #include <linux/version.h>
33     #include <asm/io.h>
34     #include <linux/signal.h>
35     #include <linux/kernel.h>
36     #include <linux/ioport.h>
37     #include <linux/interrupt.h>
38     #include <linux/vmalloc.h>
39     
40     #include "hysdn_defs.h"
41     #include "boardergo.h"
42     
43     #define byteout(addr,val) outb(val,addr)
44     #define bytein(addr) inb(addr)
45     
46     /***************************************************/
47     /* The cards interrupt handler. Called from system */
48     /***************************************************/
49     static void
50     ergo_interrupt(int intno, void *dev_id, struct pt_regs *regs)
51     {
52     	hysdn_card *card = dev_id;	/* parameter from irq */
53     	tErgDpram *dpr;
54     	ulong flags;
55     	uchar volatile b;
56     
57     	if (!card)
58     		return;		/* error -> spurious interrupt */
59     	if (!card->irq_enabled)
60     		return;		/* other device interrupting or irq switched off */
61     
62     	save_flags(flags);
63     	cli();			/* no further irqs allowed */
64     
65     	if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
66     		restore_flags(flags);	/* restore old state */
67     		return;		/* no interrupt requested by E1 */
68     	}
69     	/* clear any pending ints on the board */
70     	dpr = card->dpram;
71     	b = dpr->ToPcInt;	/* clear for ergo */
72     	b |= dpr->ToPcIntMetro;	/* same for metro */
73     	b |= dpr->ToHyInt;	/* and for champ */
74     
75     	/* start kernel task immediately after leaving all interrupts */
76     	if (!card->hw_lock) {
77     		queue_task(&card->irq_queue, &tq_immediate);
78     		mark_bh(IMMEDIATE_BH);
79     	}
80     	restore_flags(flags);
81     }				/* ergo_interrupt */
82     
83     /******************************************************************************/
84     /* ergo_irq_bh is the function called by the immediate kernel task list after */
85     /* being activated with queue_task and no interrupts active. This task is the */
86     /* only one handling data transfer from or to the card after booting. The task */
87     /* may be queued from everywhere (interrupts included).                       */
88     /******************************************************************************/
89     static void
90     ergo_irq_bh(hysdn_card * card)
91     {
92     	tErgDpram *dpr;
93     	int again;
94     	ulong flags;
95     
96     	if (card->state != CARD_STATE_RUN)
97     		return;		/* invalid call */
98     
99     	dpr = card->dpram;	/* point to DPRAM */
100     
101     	save_flags(flags);
102     	cli();
103     	if (card->hw_lock) {
104     		restore_flags(flags);	/* hardware currently unavailable */
105     		return;
106     	}
107     	card->hw_lock = 1;	/* we now lock the hardware */
108     
109     	do {
110     		sti();		/* reenable other ints */
111     		again = 0;	/* assume loop not to be repeated */
112     
113     		if (!dpr->ToHyFlag) {
114     			/* we are able to send a buffer */
115     
116     			if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
117     					   ERG_TO_HY_BUF_SIZE)) {
118     				dpr->ToHyFlag = 1;	/* enable tx */
119     				again = 1;	/* restart loop */
120     			}
121     		}		/* we are able to send a buffer */
122     		if (dpr->ToPcFlag) {
123     			/* a message has arrived for us, handle it */
124     
125     			if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
126     				dpr->ToPcFlag = 0;	/* we worked the data */
127     				again = 1;	/* restart loop */
128     			}
129     		}		/* a message has arrived for us */
130     		cli();		/* no further ints */
131     		if (again) {
132     			dpr->ToHyInt = 1;
133     			dpr->ToPcInt = 1;	/* interrupt to E1 for all cards */
134     		} else
135     			card->hw_lock = 0;	/* free hardware again */
136     	} while (again);	/* until nothing more to do */
137     
138     	restore_flags(flags);
139     }				/* ergo_irq_bh */
140     
141     
142     /*********************************************************/
143     /* stop the card (hardware reset) and disable interrupts */
144     /*********************************************************/
145     static void
146     ergo_stopcard(hysdn_card * card)
147     {
148     	ulong flags;
149     	uchar val;
150     
151     	hysdn_net_release(card);	/* first release the net device if existing */
152     #ifdef CONFIG_HYSDN_CAPI
153     	hycapi_capi_stop(card);
154     #endif /* CONFIG_HYSDN_CAPI */
155     	save_flags(flags);
156     	cli();
157     	val = bytein(card->iobase + PCI9050_INTR_REG);	/* get actual value */
158     	val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1);	/* mask irq */
159     	byteout(card->iobase + PCI9050_INTR_REG, val);
160     	card->irq_enabled = 0;
161     	byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET);	/* reset E1 processor */
162     	card->state = CARD_STATE_UNUSED;
163     	card->err_log_state = ERRLOG_STATE_OFF;		/* currently no log active */
164     
165     	restore_flags(flags);
166     }				/* ergo_stopcard */
167     
168     /**************************************************************************/
169     /* enable or disable the cards error log. The event is queued if possible */
170     /**************************************************************************/
171     static void
172     ergo_set_errlog_state(hysdn_card * card, int on)
173     {
174     	ulong flags;
175     
176     	if (card->state != CARD_STATE_RUN) {
177     		card->err_log_state = ERRLOG_STATE_OFF;		/* must be off */
178     		return;
179     	}
180     	save_flags(flags);
181     	cli();
182     
183     	if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
184     	    ((card->err_log_state == ERRLOG_STATE_ON) && on)) {
185     		restore_flags(flags);
186     		return;		/* nothing to do */
187     	}
188     	if (on)
189     		card->err_log_state = ERRLOG_STATE_START;	/* request start */
190     	else
191     		card->err_log_state = ERRLOG_STATE_STOP;	/* request stop */
192     
193     	restore_flags(flags);
194     	queue_task(&card->irq_queue, &tq_immediate);
195     	mark_bh(IMMEDIATE_BH);
196     }				/* ergo_set_errlog_state */
197     
198     /******************************************/
199     /* test the cards RAM and return 0 if ok. */
200     /******************************************/
201     static const char TestText[36] = "This Message is filler, why read it";
202     
203     static int
204     ergo_testram(hysdn_card * card)
205     {
206     	tErgDpram *dpr = card->dpram;
207     
208     	memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable));	/* clear all Traps */
209     	dpr->ToHyInt = 1;	/* E1 INTR state forced */
210     
211     	memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
212     	       sizeof(TestText));
213     	if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
214     		   sizeof(TestText)))
215     		return (-1);
216     
217     	memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
218     	       sizeof(TestText));
219     	if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
220     		   sizeof(TestText)))
221     		return (-1);
222     
223     	return (0);
224     }				/* ergo_testram */
225     
226     /*****************************************************************************/
227     /* this function is intended to write stage 1 boot image to the cards buffer */
228     /* this is done in two steps. First the 1024 hi-words are written (offs=0),  */
229     /* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the   */
230     /* PCI-write-buffers flushed and the card is taken out of reset.             */
231     /* The function then waits for a reaction of the E1 processor or a timeout.  */
232     /* Negative return values are interpreted as errors.                         */
233     /*****************************************************************************/
234     static int
235     ergo_writebootimg(struct HYSDN_CARD *card, uchar * buf, ulong offs)
236     {
237     	uchar *dst;
238     	tErgDpram *dpram;
239     	int cnt = (BOOT_IMG_SIZE >> 2);		/* number of words to move and swap (byte order!) */
240     	
241     	if (card->debug_flags & LOG_POF_CARD)
242     		hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
243     
244     	dst = card->dpram;	/* pointer to start of DPRAM */
245     	dst += (offs + ERG_DPRAM_FILL_SIZE);	/* offset in the DPRAM */
246     	while (cnt--) {
247     		*dst++ = *(buf + 1);	/* high byte */
248     		*dst++ = *buf;	/* low byte */
249     		dst += 2;	/* point to next longword */
250     		buf += 2;	/* buffer only filled with words */
251     	}
252     
253     	/* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
254     	/* flush the PCI-write-buffer and take the E1 out of reset */
255     	if (offs) {
256     		memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE);	/* fill the DPRAM still not cleared */
257     		dpram = card->dpram;	/* get pointer to dpram structure */
258     		dpram->ToHyNoDpramErrLog = 0xFF;	/* write a dpram register */
259     		while (!dpram->ToHyNoDpramErrLog);	/* reread volatile register to flush PCI */
260     
261     		byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN);	/* start E1 processor */
262     		/* the interrupts are still masked */
263     
264     		sti();
265     		set_current_state(TASK_INTERRUPTIBLE);
266     		schedule_timeout((20 * HZ) / 1000);	/* Timeout 20ms */
267     
268     		if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
269     			if (card->debug_flags & LOG_POF_CARD)
270     				hysdn_addlog(card, "ERGO: write bootldr no answer");
271     			return (-ERR_BOOTIMG_FAIL);
272     		}
273     	}			/* start_boot_img */
274     	return (0);		/* successful */
275     }				/* ergo_writebootimg */
276     
277     /********************************************************************************/
278     /* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
279     /* using the boot spool mechanism. If everything works fine 0 is returned. In   */
280     /* case of errors a negative error value is returned.                           */
281     /********************************************************************************/
282     static int
283     ergo_writebootseq(struct HYSDN_CARD *card, uchar * buf, int len)
284     {
285     	tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
286     	uchar *dst;
287     	uchar buflen;
288     	int nr_write;
289     	uchar tmp_rdptr;
290     	uchar wr_mirror;
291     	int i;
292     
293     	if (card->debug_flags & LOG_POF_CARD)
294     		hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
295     
296     	dst = sp->Data;		/* point to data in spool structure */
297     	buflen = sp->Len;	/* maximum len of spooled data */
298     	wr_mirror = sp->WrPtr;	/* only once read */
299     	sti();
300     
301     	/* try until all bytes written or error */
302     	i = 0x1000;		/* timeout value */
303     	while (len) {
304     
305     		/* first determine the number of bytes that may be buffered */
306     		do {
307     			tmp_rdptr = sp->RdPtr;	/* first read the pointer */
308     			i--;	/* decrement timeout */
309     		} while (i && (tmp_rdptr != sp->RdPtr));	/* wait for stable pointer */
310     
311     		if (!i) {
312     			if (card->debug_flags & LOG_POF_CARD)
313     				hysdn_addlog(card, "ERGO: write boot seq timeout");
314     			return (-ERR_BOOTSEQ_FAIL);	/* value not stable -> timeout */
315     		}
316     		if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
317     			nr_write += buflen;	/* now we got number of free bytes - 1 in buffer */
318     
319     		if (!nr_write)
320     			continue;	/* no free bytes in buffer */
321     
322     		if (nr_write > len)
323     			nr_write = len;		/* limit if last few bytes */
324     		i = 0x1000;	/* reset timeout value */
325     
326     		/* now we know how much bytes we may put in the puffer */
327     		len -= nr_write;	/* we savely could adjust len before output */
328     		while (nr_write--) {
329     			*(dst + wr_mirror) = *buf++;	/* output one byte */
330     			if (++wr_mirror >= buflen)
331     				wr_mirror = 0;
332     			sp->WrPtr = wr_mirror;	/* announce the next byte to E1 */
333     		}		/* while (nr_write) */
334     
335     	}			/* while (len) */
336     	return (0);
337     }				/* ergo_writebootseq */
338     
339     /***********************************************************************************/
340     /* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
341     /* boot process. If the process has been successful 0 is returned otherwise a     */
342     /* negative error code is returned.                                                */
343     /***********************************************************************************/
344     static int
345     ergo_waitpofready(struct HYSDN_CARD *card)
346     {
347     	tErgDpram *dpr = card->dpram;	/* pointer to DPRAM structure */
348     	int timecnt = 10000 / 50;	/* timeout is 10 secs max. */
349     	ulong flags;
350     	int msg_size;
351     	int i;
352     
353     	if (card->debug_flags & LOG_POF_CARD)
354     		hysdn_addlog(card, "ERGO: waiting for pof ready");
355     	while (timecnt--) {
356     		/* wait until timeout  */
357     
358     		if (dpr->ToPcFlag) {
359     			/* data has arrived */
360     
361     			if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
362     			    (dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
363     			    (dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
364     			    ((*(ulong *) dpr->ToPcBuf) != RDY_MAGIC))
365     				break;	/* an error occurred */
366     
367     			/* Check for additional data delivered during SysReady */
368     			msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
369     			if (msg_size > 0)
370     				if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
371     					break;
372     
373     			if (card->debug_flags & LOG_POF_RECORD)
374     				hysdn_addlog(card, "ERGO: pof boot success");
375     			save_flags(flags);
376     			cli();
377     
378     			card->state = CARD_STATE_RUN;	/* now card is running */
379     			/* enable the cards interrupt */
380     			byteout(card->iobase + PCI9050_INTR_REG,
381     				bytein(card->iobase + PCI9050_INTR_REG) |
382     			(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
383     			card->irq_enabled = 1;	/* we are ready to receive interrupts */
384     
385     			dpr->ToPcFlag = 0;	/* reset data indicator */
386     			dpr->ToHyInt = 1;
387     			dpr->ToPcInt = 1;	/* interrupt to E1 for all cards */
388     
389     			restore_flags(flags);
390     			if ((hynet_enable & (1 << card->myid)) 
391     			    && (i = hysdn_net_create(card))) 
392     			{
393     				ergo_stopcard(card);
394     				card->state = CARD_STATE_BOOTERR;
395     				return (i);
396     			}
397     #ifdef CONFIG_HYSDN_CAPI
398     			if((i = hycapi_capi_create(card))) {
399     				printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n");
400     			}
401     #endif /* CONFIG_HYSDN_CAPI */
402     			return (0);	/* success */
403     		}		/* data has arrived */
404     		sti();
405     		set_current_state(TASK_INTERRUPTIBLE);
406     		schedule_timeout((50 * HZ) / 1000);	/* Timeout 50ms */
407     	}			/* wait until timeout */
408     
409     	if (card->debug_flags & LOG_POF_CARD)
410     		hysdn_addlog(card, "ERGO: pof boot ready timeout");
411     	return (-ERR_POF_TIMEOUT);
412     }				/* ergo_waitpofready */
413     
414     
415     
416     /************************************************************************************/
417     /* release the cards hardware. Before releasing do a interrupt disable and hardware */
418     /* reset. Also unmap dpram.                                                         */
419     /* Use only during module release.                                                  */
420     /************************************************************************************/
421     static void
422     ergo_releasehardware(hysdn_card * card)
423     {
424     	ergo_stopcard(card);	/* first stop the card if not already done */
425     	free_irq(card->irq, card);	/* release interrupt */
426     	release_region(card->iobase + PCI9050_INTR_REG, 1);	/* release all io ports */
427     	release_region(card->iobase + PCI9050_USER_IO, 1);
428     	vfree(card->dpram);
429     	card->dpram = NULL;	/* release shared mem */
430     }				/* ergo_releasehardware */
431     
432     
433     /*********************************************************************************/
434     /* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
435     /* value is returned.                                                            */
436     /* Use only during module init.                                                  */
437     /*********************************************************************************/
438     int
439     ergo_inithardware(hysdn_card * card)
440     {
441     	if (check_region(card->iobase + PCI9050_INTR_REG, 1) ||
442     	    check_region(card->iobase + PCI9050_USER_IO, 1))
443     		return (-1);	/* ports already in use */
444     
445     	card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
446     	if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE)))
447     		return (-1);
448     
449     	request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN");
450     	request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN");
451     	ergo_stopcard(card);	/* disable interrupts */
452     	if (request_irq(card->irq, ergo_interrupt, SA_SHIRQ, "HYSDN", card)) {
453     		ergo_releasehardware(card); /* return the acquired hardware */
454     		return (-1);
455     	}
456     	/* success, now setup the function pointers */
457     	card->stopcard = ergo_stopcard;
458     	card->releasehardware = ergo_releasehardware;
459     	card->testram = ergo_testram;
460     	card->writebootimg = ergo_writebootimg;
461     	card->writebootseq = ergo_writebootseq;
462     	card->waitpofready = ergo_waitpofready;
463     	card->set_errlog_state = ergo_set_errlog_state;
464     	card->irq_queue.sync = 0;
465     	card->irq_queue.data = card;	/* init task queue for interrupt */
466     	card->irq_queue.routine = (void *) (void *) ergo_irq_bh;
467     
468     	return (0);
469     }				/* ergo_inithardware */
470