teles0.c 9.01 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7
/* $Id: teles0.c,v 2.15.2.4 2004/01/13 23:48:39 keil Exp $
 *
 * low level stuff for Teles Memory IO isdn cards
 *
 * Author       Karsten Keil
 *              based on the teles driver from Jan den Ouden
 * Copyright    by Karsten Keil      <keil@isdn4linux.de>
8
 *
Linus Torvalds's avatar
Linus Torvalds committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 *
 * Thanks to    Jan den Ouden
 *              Fritz Elfert
 *              Beat Doebeli
 *
 */

#include <linux/init.h>
#include "hisax.h"
#include "isdnl1.h"
#include "isac.h"
#include "hscx.h"

24
static const char *teles0_revision = "$Revision: 2.15.2.4 $";
Linus Torvalds's avatar
Linus Torvalds committed
25 26

#define TELES_IOMEM_SIZE	0x400
27
#define byteout(addr, val) outb(val, addr)
Linus Torvalds's avatar
Linus Torvalds committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
#define bytein(addr) inb(addr)

static inline u_char
readisac(void __iomem *adr, u_char off)
{
	return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off);
}

static inline void
writeisac(void __iomem *adr, u_char off, u_char data)
{
	writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb();
}


static inline u_char
readhscx(void __iomem *adr, int hscx, u_char off)
{
	return readb(adr + (hscx ? 0x1c0 : 0x180) +
		     ((off & 1) ? 0x1ff : 0) + off);
}

static inline void
writehscx(void __iomem *adr, int hscx, u_char off, u_char data)
{
	writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
	       ((off & 1) ? 0x1ff : 0) + off); mb();
}

static inline void
58
read_fifo_isac(void __iomem *adr, u_char *data, int size)
Linus Torvalds's avatar
Linus Torvalds committed
59 60 61 62 63 64 65 66
{
	register int i;
	register u_char __iomem *ad = adr + 0x100;
	for (i = 0; i < size; i++)
		data[i] = readb(ad);
}

static inline void
67
write_fifo_isac(void __iomem *adr, u_char *data, int size)
Linus Torvalds's avatar
Linus Torvalds committed
68 69 70 71 72 73 74 75 76
{
	register int i;
	register u_char __iomem *ad = adr + 0x100;
	for (i = 0; i < size; i++) {
		writeb(data[i], ad); mb();
	}
}

static inline void
77
read_fifo_hscx(void __iomem *adr, int hscx, u_char *data, int size)
Linus Torvalds's avatar
Linus Torvalds committed
78 79 80 81 82 83 84 85
{
	register int i;
	register u_char __iomem *ad = adr + (hscx ? 0x1c0 : 0x180);
	for (i = 0; i < size; i++)
		data[i] = readb(ad);
}

static inline void
86
write_fifo_hscx(void __iomem *adr, int hscx, u_char *data, int size)
Linus Torvalds's avatar
Linus Torvalds committed
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
{
	int i;
	register u_char __iomem *ad = adr + (hscx ? 0x1c0 : 0x180);
	for (i = 0; i < size; i++) {
		writeb(data[i], ad); mb();
	}
}

/* Interface functions */

static u_char
ReadISAC(struct IsdnCardState *cs, u_char offset)
{
	return (readisac(cs->hw.teles0.membase, offset));
}

static void
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
{
	writeisac(cs->hw.teles0.membase, offset, value);
}

static void
110
ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
Linus Torvalds's avatar
Linus Torvalds committed
111 112 113 114 115
{
	read_fifo_isac(cs->hw.teles0.membase, data, size);
}

static void
116
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
Linus Torvalds's avatar
Linus Torvalds committed
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
{
	write_fifo_isac(cs->hw.teles0.membase, data, size);
}

static u_char
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
{
	return (readhscx(cs->hw.teles0.membase, hscx, offset));
}

static void
WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
{
	writehscx(cs->hw.teles0.membase, hscx, offset, value);
}

/*
 * fast interrupt HSCX stuff goes here
 */

#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)

#include "hscx_irq.c"

static irqreturn_t
145
teles0_interrupt(int intno, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149 150 151 152 153
{
	struct IsdnCardState *cs = dev_id;
	u_char val;
	u_long flags;
	int count = 0;

	spin_lock_irqsave(&cs->lock, flags);
	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
154
Start_HSCX:
Linus Torvalds's avatar
Linus Torvalds committed
155 156 157
	if (val)
		hscx_int_main(cs, val);
	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
158
Start_ISAC:
Linus Torvalds's avatar
Linus Torvalds committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	if (val)
		isac_interrupt(cs, val);
	count++;
	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
	if (val && count < 5) {
		if (cs->debug & L1_DEB_HSCX)
			debugl1(cs, "HSCX IntStat after IntRoutine");
		goto Start_HSCX;
	}
	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
	if (val && count < 5) {
		if (cs->debug & L1_DEB_ISAC)
			debugl1(cs, "ISAC IntStat after IntRoutine");
		goto Start_ISAC;
	}
	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
	spin_unlock_irqrestore(&cs->lock, flags);
	return IRQ_HANDLED;
}

184
static void
Linus Torvalds's avatar
Linus Torvalds committed
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
release_io_teles0(struct IsdnCardState *cs)
{
	if (cs->hw.teles0.cfg_reg)
		release_region(cs->hw.teles0.cfg_reg, 8);
	iounmap(cs->hw.teles0.membase);
	release_mem_region(cs->hw.teles0.phymem, TELES_IOMEM_SIZE);
}

static int
reset_teles0(struct IsdnCardState *cs)
{
	u_char cfval;

	if (cs->hw.teles0.cfg_reg) {
		switch (cs->irq) {
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
		case 2:
		case 9:
			cfval = 0x00;
			break;
		case 3:
			cfval = 0x02;
			break;
		case 4:
			cfval = 0x04;
			break;
		case 5:
			cfval = 0x06;
			break;
		case 10:
			cfval = 0x08;
			break;
		case 11:
			cfval = 0x0A;
			break;
		case 12:
			cfval = 0x0C;
			break;
		case 15:
			cfval = 0x0E;
			break;
		default:
			return (1);
Linus Torvalds's avatar
Linus Torvalds committed
227 228 229 230 231 232 233 234 235 236 237
		}
		cfval |= ((cs->hw.teles0.phymem >> 9) & 0xF0);
		byteout(cs->hw.teles0.cfg_reg + 4, cfval);
		HZDELAY(HZ / 10 + 1);
		byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
		HZDELAY(HZ / 10 + 1);
	}
	writeb(0, cs->hw.teles0.membase + 0x80); mb();
	HZDELAY(HZ / 5 + 1);
	writeb(1, cs->hw.teles0.membase + 0x80); mb();
	HZDELAY(HZ / 5 + 1);
238
	return (0);
Linus Torvalds's avatar
Linus Torvalds committed
239 240 241 242 243 244 245 246
}

static int
Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
{
	u_long flags;

	switch (mt) {
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
	case CARD_RESET:
		spin_lock_irqsave(&cs->lock, flags);
		reset_teles0(cs);
		spin_unlock_irqrestore(&cs->lock, flags);
		return (0);
	case CARD_RELEASE:
		release_io_teles0(cs);
		return (0);
	case CARD_INIT:
		spin_lock_irqsave(&cs->lock, flags);
		inithscxisac(cs, 3);
		spin_unlock_irqrestore(&cs->lock, flags);
		return (0);
	case CARD_TEST:
		return (0);
Linus Torvalds's avatar
Linus Torvalds committed
262
	}
263
	return (0);
Linus Torvalds's avatar
Linus Torvalds committed
264 265
}

266
int __devinit
Linus Torvalds's avatar
Linus Torvalds committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
setup_teles0(struct IsdnCard *card)
{
	u_char val;
	struct IsdnCardState *cs = card->cs;
	char tmp[64];

	strcpy(tmp, teles0_revision);
	printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
	if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0))
		return (0);

	if (cs->typ == ISDN_CTYPE_16_0)
		cs->hw.teles0.cfg_reg = card->para[2];
	else			/* 8.0 */
		cs->hw.teles0.cfg_reg = 0;

	if (card->para[1] < 0x10000) {
		card->para[1] <<= 4;
		printk(KERN_INFO
286
		       "Teles0: membase configured DOSish, assuming 0x%lx\n",
Linus Torvalds's avatar
Linus Torvalds committed
287 288 289 290 291 292
		       (unsigned long) card->para[1]);
	}
	cs->irq = card->para[0];
	if (cs->hw.teles0.cfg_reg) {
		if (!request_region(cs->hw.teles0.cfg_reg, 8, "teles cfg")) {
			printk(KERN_WARNING
293
			       "HiSax: %s config port %x-%x already in use\n",
Linus Torvalds's avatar
Linus Torvalds committed
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
			       CardType[card->typ],
			       cs->hw.teles0.cfg_reg,
			       cs->hw.teles0.cfg_reg + 8);
			return (0);
		}
	}
	if (cs->hw.teles0.cfg_reg) {
		if ((val = bytein(cs->hw.teles0.cfg_reg + 0)) != 0x51) {
			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
			       cs->hw.teles0.cfg_reg + 0, val);
			release_region(cs->hw.teles0.cfg_reg, 8);
			return (0);
		}
		if ((val = bytein(cs->hw.teles0.cfg_reg + 1)) != 0x93) {
			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
			       cs->hw.teles0.cfg_reg + 1, val);
			release_region(cs->hw.teles0.cfg_reg, 8);
			return (0);
		}
		val = bytein(cs->hw.teles0.cfg_reg + 2);	/* 0x1e=without AB
314 315
								 * 0x1f=with AB
								 * 0x1c 16.3 ???
Linus Torvalds's avatar
Linus Torvalds committed
316 317 318 319 320 321 322 323 324 325 326 327 328
								 */
		if (val != 0x1e && val != 0x1f) {
			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
			       cs->hw.teles0.cfg_reg + 2, val);
			release_region(cs->hw.teles0.cfg_reg, 8);
			return (0);
		}
	}
	/* 16.0 and 8.0 designed for IOM1 */
	test_and_set_bit(HW_IOM1, &cs->HW_Flags);
	cs->hw.teles0.phymem = card->para[1];
	if (!request_mem_region(cs->hw.teles0.phymem, TELES_IOMEM_SIZE, "teles iomem")) {
		printk(KERN_WARNING
329 330 331 332
		       "HiSax: %s memory region %lx-%lx already in use\n",
		       CardType[card->typ],
		       cs->hw.teles0.phymem,
		       cs->hw.teles0.phymem + TELES_IOMEM_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
		if (cs->hw.teles0.cfg_reg)
			release_region(cs->hw.teles0.cfg_reg, 8);
		return (0);
	}
	cs->hw.teles0.membase = ioremap(cs->hw.teles0.phymem, TELES_IOMEM_SIZE);
	printk(KERN_INFO
	       "HiSax: %s config irq:%d mem:%p cfg:0x%X\n",
	       CardType[cs->typ], cs->irq,
	       cs->hw.teles0.membase, cs->hw.teles0.cfg_reg);
	if (reset_teles0(cs)) {
		printk(KERN_WARNING "Teles0: wrong IRQ\n");
		release_io_teles0(cs);
		return (0);
	}
	setup_isac(cs);
	cs->readisac = &ReadISAC;
	cs->writeisac = &WriteISAC;
	cs->readisacfifo = &ReadISACfifo;
	cs->writeisacfifo = &WriteISACfifo;
	cs->BC_Read_Reg = &ReadHSCX;
	cs->BC_Write_Reg = &WriteHSCX;
	cs->BC_Send_Data = &hscx_fill_fifo;
	cs->cardmsg = &Teles_card_msg;
	cs->irq_func = &teles0_interrupt;
	ISACVersion(cs, "Teles0:");
	if (HscxVersion(cs, "Teles0:")) {
		printk(KERN_WARNING
360
		       "Teles0: wrong HSCX versions check IO/MEM addresses\n");
Linus Torvalds's avatar
Linus Torvalds committed
361 362 363 364 365
		release_io_teles0(cs);
		return (0);
	}
	return (1);
}