1/*
2 *   Copyright (c) 1998 Matthias Apitz. All rights reserved.
3 *
4 *   Redistribution and use in source and binary forms, with or without
5 *   modification, are permitted provided that the following conditions
6 *   are met:
7 *
8 *   1. Redistributions of source code must retain the above copyright
9 *      notice, this list of conditions and the following disclaimer.
10 *   2. Redistributions in binary form must reproduce the above copyright
11 *      notice, this list of conditions and the following disclaimer in the
12 *      documentation and/or other materials provided with the distribution.
13 *   3. Neither the name of the author nor the names of any co-contributors
14 *      may be used to endorse or promote products derived from this software
15 *      without specific prior written permission.
16 *   4. Altered versions must be plainly marked as such, and must not be
17 *      misrepresented as being the original software and/or documentation.
18 *
19 *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 *   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 *   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 *   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 *   SUCH DAMAGE.
30 *
31 *---------------------------------------------------------------------------
32 *
33 *	Fritz!Card pcmcia specific routines for isic driver
34 *	---------------------------------------------------
35 *
36 *	$Id: isic_pcmcia_avm_fritz.c,v 1.12 2007/10/19 12:01:05 ad Exp $
37 *
38 *      last edit-date: [Fri Jan  5 11:39:32 2001]
39 *
40 *	-ap	added support for AVM PCMCIA Fritz!Card
41 *	-mh	split into separate file
42 *
43 *---------------------------------------------------------------------------*/
44
45#include <sys/cdefs.h>
46__KERNEL_RCSID(0, "$NetBSD: isic_pcmcia_avm_fritz.c,v 1.11 2007/03/04 06:02:28 christos Exp $");
47
48#include "opt_isicpcmcia.h"
49#ifdef ISICPCMCIA_AVM_A1
50
51#include <sys/param.h>
52#if defined(__FreeBSD__) && __FreeBSD__ >= 3
53#include <sys/ioccom.h>
54#else
55#include <sys/ioctl.h>
56#endif
57#include <sys/kernel.h>
58#include <sys/systm.h>
59#include <sys/mbuf.h>
60
61#ifdef __FreeBSD__
62#include <machine/clock.h>
63#include <i386/isa/isa_device.h>
64#else
65#include <sys/bus.h>
66#include <sys/device.h>
67#endif
68
69#include <sys/socket.h>
70#include <net/if.h>
71
72#if defined(__NetBSD__) && __NetBSD_Version__ >= 104230000
73#include <sys/callout.h>
74#endif
75
76#ifdef __FreeBSD__
77#include <machine/i4b_debug.h>
78#include <machine/i4b_ioctl.h>
79#else
80#include <netisdn/i4b_debug.h>
81#include <netisdn/i4b_ioctl.h>
82#include <netisdn/i4b_l2.h>
83#include <netisdn/i4b_l1l2.h>
84#include <dev/pcmcia/pcmciareg.h>
85#include <dev/pcmcia/pcmciavar.h>
86#endif
87
88#include <dev/ic/isic_l1.h>
89#include <dev/ic/isac.h>
90#include <dev/ic/hscx.h>
91
92#ifndef __FreeBSD__
93#include <dev/pcmcia/isic_pcmcia.h>
94
95/* PCMCIA support routines */
96static u_int8_t avma1_pcmcia_read_reg(struct isic_softc *sc, int what, bus_size_t offs);
97static void avma1_pcmcia_write_reg(struct isic_softc *sc, int what, bus_size_t offs, u_int8_t data);
98static void avma1_pcmcia_read_fifo(struct isic_softc *sc, int what, void *buf, size_t size);
99static void avma1_pcmcia_write_fifo(struct isic_softc *sc, int what, const void *data, size_t size);
100#endif
101
102/*---------------------------------------------------------------------------*
103 *	AVM PCMCIA Fritz!Card special registers
104 *---------------------------------------------------------------------------*/
105
106/*
107 *	register offsets from i/o base 0x140 or 0x300
108 */
109#define ADDR_REG_OFFSET         0x02
110#define DATA_REG_OFFSET         0x03
111#define STAT0_OFFSET            0x04
112#define STAT1_OFFSET            0x05
113#define MODREG_OFFSET           0x06
114#define VERREG_OFFSET           0x07
115/*
116 *	AVM PCMCIA Status Latch 0 read only bits
117 */
118#define ASL_IRQ_TIMER           0x10    /* Timer interrupt, active low */
119#define ASL_IRQ_ISAC            0x20    /* ISAC  interrupt, active low */
120#define ASL_IRQ_HSCX            0x40    /* HSX   interrupt, active low */
121#define ASL_IRQ_BCHAN           ASL_IRQ_HSCX
122#define ASL_IRQ_Pending         (ASL_IRQ_ISAC | ASL_IRQ_HSCX | ASL_IRQ_TIMER)
123/*
124 *	AVM Status Latch 0 write only bits
125 */
126#define ASL_RESET_ALL           0x01  /* reset siemens IC's, active 1 */
127#define ASL_TIMERDISABLE        0x02  /* active high */
128#define ASL_TIMERRESET          0x04  /* active high */
129#define ASL_ENABLE_INT          0x08  /* active high */
130/*
131 *	AVM Status Latch 1 write only bits
132 */
133#define ASL1_LED0                0x10  /* active high */
134#define ASL1_LED1                0x20  /* active high */
135
136#define ASL1_ENABLE_S0           0xc0  /* enable active S0 I/F */
137
138/*----- EEpromless controller -----*/
139/*
140 *	AVM Status Latch read/write bit
141 */
142
143#define ASL_TESTBIT     0x80
144
145
146/*---------------------------------------------------------------------------*
147 *	AVM read fifo routines
148 *---------------------------------------------------------------------------*/
149#ifdef __FreeBSD__
150static int PCMCIA_IO_BASE = 0;		/* ap: XXX hack */
151static void
152avma1_pcmcia_read_fifo(void *buf, const void *base, size_t len)
153{
154	outb(PCMCIA_IO_BASE + ADDR_REG_OFFSET, (int)base - 0x20);
155	insb(PCMCIA_IO_BASE + DATA_REG_OFFSET, (u_char *)buf, (u_int)len);
156}
157#else
158/* offsets of the different 'what' arguments */
159static u_int8_t what_map[] = {
160	0x20-0x20,	/* ISIC_WHAT_ISAC */
161	0xA0-0x20,	/* ISIC_WHAT_HSCXA */
162	0xE0-0x20	/* ISIC_WHAT_HSCXB */
163};
164static void
165avma1_pcmcia_read_fifo(struct isic_softc *sc, int what, void *buf, size_t size)
166{
167	bus_space_tag_t t = sc->sc_maps[0].t;
168	bus_space_handle_t h = sc->sc_maps[0].h;
169	bus_space_write_1(t, h, ADDR_REG_OFFSET, what_map[what]);
170	bus_space_read_multi_1(t, h, DATA_REG_OFFSET, buf, size);
171}
172#endif
173
174/*---------------------------------------------------------------------------*
175 *	AVM write fifo routines
176 *---------------------------------------------------------------------------*/
177#ifdef __FreeBSD__
178static void
179avma1_pcmcia_write_fifo(void *base, const void *buf, size_t len)
180{
181	outb(PCMCIA_IO_BASE + ADDR_REG_OFFSET, (int)base - 0x20);
182	outsb(PCMCIA_IO_BASE + DATA_REG_OFFSET, (u_char *)buf, (u_int)len);
183}
184#else
185static void
186avma1_pcmcia_write_fifo(struct isic_softc *sc, int what, const void *buf, size_t size)
187{
188	bus_space_tag_t t = sc->sc_maps[0].t;
189	bus_space_handle_t h = sc->sc_maps[0].h;
190	bus_space_write_1(t, h, ADDR_REG_OFFSET, what_map[what]);
191	bus_space_write_multi_1(t, h, DATA_REG_OFFSET, buf, size);
192}
193#endif
194
195/*---------------------------------------------------------------------------*
196 *	AVM write register routines
197 *---------------------------------------------------------------------------*/
198#ifdef __FreeBSD__
199static void
200avma1_pcmcia_write_reg(u_char *base, u_int offset, u_int v)
201{
202	/* offset includes 0x20 FIFO ! */
203	outb(PCMCIA_IO_BASE + ADDR_REG_OFFSET, (int)base+offset-0x20);
204	outb(PCMCIA_IO_BASE + DATA_REG_OFFSET, (u_char)v);
205}
206#else
207static void
208avma1_pcmcia_write_reg(struct isic_softc *sc, int what, bus_size_t offs, u_int8_t data)
209{
210	bus_space_tag_t t = sc->sc_maps[0].t;
211	bus_space_handle_t h = sc->sc_maps[0].h;
212	bus_space_write_1(t, h, ADDR_REG_OFFSET, what_map[what]+offs);
213	bus_space_write_1(t, h, DATA_REG_OFFSET, data);
214}
215#endif
216
217/*---------------------------------------------------------------------------*
218 *	AVM read register routines
219 *---------------------------------------------------------------------------*/
220#ifdef __FreeBSD__
221static u_char
222avma1_pcmcia_read_reg(u_char *base, u_int offset)
223{
224	/* offset includes 0x20 FIFO ! */
225	outb(PCMCIA_IO_BASE + ADDR_REG_OFFSET, (int)base+offset-0x20);
226	return (inb(PCMCIA_IO_BASE + DATA_REG_OFFSET));
227}
228#else
229static u_int8_t
230avma1_pcmcia_read_reg(struct isic_softc *sc, int what, bus_size_t offs)
231{
232	bus_space_tag_t t = sc->sc_maps[0].t;
233	bus_space_handle_t h = sc->sc_maps[0].h;
234	bus_space_write_1(t, h, ADDR_REG_OFFSET, what_map[what]+offs);
235	return bus_space_read_1(t, h, DATA_REG_OFFSET);
236}
237#endif
238
239/*---------------------------------------------------------------------------*
240 *	isic_probe_avma1_pcmcia - probe for AVM PCMCIA Fritz!Card
241 *	This is in the bus attachemnt part on NetBSD (isic_pcmcia.c), no
242 *	card specicfic probe is needed on direct config buses like pcmcia.
243 *---------------------------------------------------------------------------*/
244#ifdef __FreeBSD__
245int
246isic_probe_avma1_pcmcia(struct isa_device *dev)
247{
248	struct isic_softc *sc = &l1_sc[dev->id_unit];
249	u_char byte;
250	int i;
251	u_int cardinfo;
252
253	/* check max unit range */
254
255	if(dev->id_unit > 1)
256	{
257		printf("isic%d: Error, unit %d > MAXUNIT for AVM PCMCIA Fritz!Card\n",
258				dev->id_unit, dev->id_unit);
259		return(0);
260	}
261	sc->sc_unit = dev->id_unit;
262
263	/*
264	 * we trust the IRQ we got from PCCARD service
265	 */
266	sc->sc_irq = dev->id_irq;
267
268	/* check if we got an iobase */
269
270	switch(dev->id_iobase)
271	{
272		case 0x140:
273		case 0x300:
274			break;
275		default:
276			printf("isic%d: Error, invalid iobase 0x%x specified for AVM PCMCIA Fritz!Card.\n",
277				dev->id_unit, dev->id_iobase);
278			return(0);
279			break;
280	}
281	sc->sc_port = dev->id_iobase;
282
283	/* ResetController */
284
285	outb(dev->id_iobase + STAT0_OFFSET, 0x00);
286	DELAY(SEC_DELAY / 20);
287	outb(dev->id_iobase + STAT0_OFFSET, 0x01);
288	DELAY(SEC_DELAY / 20);
289	outb(dev->id_iobase + STAT0_OFFSET, 0x00);
290
291	/*
292	 * CheckController
293	 * The logic to check for the PCMCIA was adapted as
294	 * described by AVM.
295	 */
296
297	outb(dev->id_iobase + ADDR_REG_OFFSET, 0x21);	/* ISAC STAR */
298	if ( (byte=inb(dev->id_iobase + DATA_REG_OFFSET) & 0xfd) != 0x48 )
299	{
300		printf("isic%d: Error, ISAC STAR for AVM PCMCIA is 0x%0x (should be 0x48)\n",
301				dev->id_unit, byte);
302		return(0);
303	}
304
305	outb(dev->id_iobase + ADDR_REG_OFFSET, 0xa1);	/* HSCX STAR */
306	if ( (byte=inb(dev->id_iobase + DATA_REG_OFFSET) & 0xfd) != 0x48 )
307	{
308		printf("isic%d: Error, HSCX STAR for AVM PCMCIA is 0x%0x (should be 0x48)\n",
309				dev->id_unit, byte);
310		return(0);
311	}
312
313	byte = ASL_TESTBIT;
314	for (i=0; i<256; i++)	{
315		byte = byte ? 0 : ASL_TESTBIT;
316		outb(dev->id_iobase + STAT0_OFFSET, byte);
317		if ((inb(dev->id_iobase+STAT0_OFFSET)&ASL_TESTBIT)!=byte)   {
318			printf("isic%d: Error during toggle of AVM PCMCIA Status Latch0\n",
319				dev->id_unit);
320			return(0);
321		}
322	}
323
324	sc->clearirq = NULL;
325	sc->readreg   = avma1_pcmcia_read_reg;
326	sc->writereg  = avma1_pcmcia_write_reg;
327
328	sc->readfifo  = avma1_pcmcia_read_fifo;
329	sc->writefifo = avma1_pcmcia_write_fifo;
330
331	/* setup IOM bus type */
332
333	sc->sc_bustyp = BUS_TYPE_IOM2; 			/* ap: XXX ??? */
334
335	sc->sc_ipac = 0;
336	sc->sc_bfifolen = HSCX_FIFO_LEN;
337
338	/* setup ISAC and HSCX base addr */
339	/*
340	 * NOTE: for PCMCIA these are no real addrs; they are
341	 * offsets to be written into the base+ADDR_REG_OFFSET register
342	 * to pick up the values of the bytes fro base+DATA_REG_OFFSET
343	 *
344	 * see also the logic in the avma1_pcmcia_* routines;
345	 * therefore we also must have the base addr in some static
346	 * space or struct; XXX better solution?
347	 */
348
349	PCMCIA_IO_BASE = dev->id_iobase;
350	ISAC_BASE      = (void *)0x20;
351
352	HSCX_A_BASE    = (void *)0xA0;
353	HSCX_B_BASE    = (void *)0xE0;
354
355	/*
356	 * Read HSCX A/B VSTR.
357	 * Expected value for AVM A1 is 0x04 or 0x05 and for the
358	 * AVM Fritz!Card is 0x05 in the least significant bits.
359	 */
360
361	if( (((HSCX_READ(0, H_VSTR) & 0xf) != 0x5) &&
362	     ((HSCX_READ(0, H_VSTR) & 0xf) != 0x4))	||
363            (((HSCX_READ(1, H_VSTR) & 0xf) != 0x5) &&
364	     ((HSCX_READ(1, H_VSTR) & 0xf) != 0x4)) )
365	{
366		printf("isic%d: HSCX VSTR test failed for AVM PCMCIA Fritz!Card\n",
367			dev->id_unit);
368		printf("isic%d: HSC0: VSTR: 0x%0x\n",
369			dev->id_unit, HSCX_READ(0, H_VSTR));
370		printf("isic%d: HSC1: VSTR: 0x%0x\n",
371			dev->id_unit, HSCX_READ(1, H_VSTR));
372		return (0);
373	}
374
375	/*
376	 * seems we really have an AVM PCMCIA Fritz!Card controller
377	 */
378	cardinfo = inb(dev->id_iobase + VERREG_OFFSET)<<8 | inb(dev->id_iobase + MODREG_OFFSET);
379	printf("isic%d: successfully detect AVM PCMCIA cardinfo = 0x%0x\n",
380		dev->id_unit, cardinfo);
381	dev->id_flags = FLAG_AVM_A1_PCMCIA;
382	return (1);
383}
384#endif /* __FreeBSD__ */
385
386
387
388/*---------------------------------------------------------------------------*
389 *	isic_attach_fritzpcmcia - attach Fritz!Card
390 *---------------------------------------------------------------------------*/
391#ifdef __FreeBSD__
392int
393isic_attach_fritzpcmcia(struct isa_device *dev)
394{
395	/* ResetController again just to make sure... */
396
397	outb(dev->id_iobase + STAT0_OFFSET, 0x00);
398	DELAY(SEC_DELAY / 10);
399	outb(dev->id_iobase + STAT0_OFFSET, 0x01);
400	DELAY(SEC_DELAY / 10);
401	outb(dev->id_iobase + STAT0_OFFSET, 0x00);
402	DELAY(SEC_DELAY / 10);
403
404	/* enable IRQ, disable counter IRQ */
405
406	outb(dev->id_iobase + STAT0_OFFSET, ASL_TIMERDISABLE |
407		ASL_TIMERRESET | ASL_ENABLE_INT);
408	/* DELAY(SEC_DELAY / 10); */
409
410	return(1);
411}
412
413#else
414
415int
416isic_attach_fritzpcmcia(struct pcmcia_isic_softc *psc, struct pcmcia_config_entry *cfe, struct pcmcia_attach_args *pa)
417{
418	struct isic_softc *sc = &psc->sc_isic;
419	bus_space_tag_t t;
420	bus_space_handle_t h;
421	int i;
422
423	/* Validate config info */
424	if (cfe->num_memspace != 0)
425		printf(": unexpected number of memory spaces %d should be 0\n",
426			cfe->num_memspace);
427	if (cfe->num_iospace != 1)
428		printf(": unexpected number of memory spaces %d should be 1\n",
429			cfe->num_iospace);
430
431	/* Allocate pcmcia space - but don't listen to the card, it's lying
432	   about the size needed! */
433	for (i = 0; i < cfe->num_iospace; i++)
434	    if (pcmcia_io_alloc(pa->pf, cfe->iospace[i].start, cfe->iospace[i].length, 1, &psc->sc_pcioh) == 0)
435	    	break;
436	if (i >= cfe->num_iospace) {
437		printf(": can't allocate i/o space\n");
438		return 0;
439	}
440
441	/* map the selected space */
442	if (pcmcia_io_map(pa->pf, ((cfe->flags & PCMCIA_CFE_IO16) ?
443	    PCMCIA_WIDTH_IO16 : PCMCIA_WIDTH_IO8), &psc->sc_pcioh,
444	    &psc->sc_io_window)) {
445		printf(": can't map i/o space\n");
446		return 0;
447	}
448
449	/* Setup bus space maps */
450	sc->sc_num_mappings = 1;
451	MALLOC_MAPS(sc);
452
453	/* Copy our handles/tags to the MI maps */
454	sc->sc_maps[0].t = psc->sc_pcioh.iot;
455	sc->sc_maps[0].h = psc->sc_pcioh.ioh;
456	sc->sc_maps[0].offset = 0;
457	sc->sc_maps[0].size = 0;	/* not our mapping */
458
459	t = sc->sc_maps[0].t;
460	h = sc->sc_maps[0].h;
461
462	sc->clearirq = NULL;
463	sc->readreg = avma1_pcmcia_read_reg;
464	sc->writereg = avma1_pcmcia_write_reg;
465
466	sc->readfifo = avma1_pcmcia_read_fifo;
467	sc->writefifo = avma1_pcmcia_write_fifo;
468
469	/* setup IOM bus type */
470
471	sc->sc_bustyp = BUS_TYPE_IOM2;
472
473	sc->sc_ipac = 0;
474	sc->sc_bfifolen = HSCX_FIFO_LEN;
475
476	/* Reset controller again just to make sure... */
477
478	bus_space_write_1(t, h, STAT0_OFFSET, 0x00);
479	DELAY(SEC_DELAY / 10);
480	bus_space_write_1(t, h, STAT0_OFFSET, 0x01);
481	DELAY(SEC_DELAY / 10);
482	bus_space_write_1(t, h, STAT0_OFFSET, 0x00);
483	DELAY(SEC_DELAY / 10);
484
485	/* enable IRQ, disable counter IRQ */
486
487	bus_space_write_1(t, h, STAT0_OFFSET, ASL_TIMERDISABLE |
488		ASL_TIMERRESET | ASL_ENABLE_INT);
489
490	return 1;
491}
492#endif
493
494#endif /* ISICPCMCIA_AVM_A1 */
495