1/* $NetBSD: isic_pci_elsa_qs1p.c,v 1.18 2008/04/10 19:13:37 cegger Exp $ */
2
3/*
4 * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *---------------------------------------------------------------------------
28 *
29 *	isic - I4B Siemens ISDN Chipset Driver for ELSA Quickstep 1000pro PCI
30 *	=====================================================================
31 *
32 *---------------------------------------------------------------------------*/
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: isic_pci_elsa_qs1p.c,v 1.18 2008/04/10 19:13:37 cegger Exp $");
36
37#include <sys/param.h>
38#include <sys/kernel.h>
39#include <sys/systm.h>
40#include <sys/mbuf.h>
41#include <sys/socket.h>
42#include <net/if.h>
43#include <sys/callout.h>
44
45#include <sys/bus.h>
46#include <sys/device.h>
47
48#include <dev/pci/pcireg.h>
49#include <dev/pci/pcivar.h>
50#include <dev/pci/pcidevs.h>
51
52#include <netisdn/i4b_debug.h>
53#include <netisdn/i4b_ioctl.h>
54#include <netisdn/i4b_global.h>
55#include <netisdn/i4b_debug.h>
56#include <netisdn/i4b_l2.h>
57#include <netisdn/i4b_l1l2.h>
58#include <netisdn/i4b_mbuf.h>
59
60#include <dev/ic/isic_l1.h>
61#include <dev/ic/isac.h>
62#include <dev/ic/hscx.h>
63#include <dev/ic/ipac.h>
64#include <dev/pci/isic_pci.h>
65
66/* masks for register encoded in base addr */
67
68#define ELSA_BASE_MASK		0x0ffff
69#define ELSA_OFF_MASK		0xf0000
70
71/* register id's to be encoded in base addr */
72
73#define ELSA_IDISAC		0x00000
74#define ELSA_IDHSCXA		0x10000
75#define ELSA_IDHSCXB		0x20000
76#define ELSA_IDIPAC		0x40000
77
78/* offsets from base address */
79
80#define ELSA_OFF_ALE		0x00
81#define ELSA_OFF_RW		0x01
82
83/* LED values */
84#define	ELSA_NO_LED		0xff
85#define	ELSA_GREEN_LED		0x40
86#define	ELSA_YELLOW_LED		0x80
87
88#define ELSA_PORT0_MEM_MAPOFF	PCI_MAPREG_START
89#define ELSA_PORT0_IO_MAPOFF	PCI_MAPREG_START+4
90#define ELSA_PORT1_MAPOFF	PCI_MAPREG_START+12
91
92
93static void elsa_cmd_req(struct isic_softc *sc, int cmd, void *data);
94static void elsa_led_handler(void *token);
95
96/*---------------------------------------------------------------------------*
97 *      ELSA QuickStep 1000pro/PCI ISAC get fifo routine
98 *---------------------------------------------------------------------------*/
99
100static void
101eqs1pp_read_fifo(struct isic_softc *sc, int what, void *buf, size_t size)
102{
103	bus_space_tag_t t = sc->sc_maps[1].t;
104	bus_space_handle_t h = sc->sc_maps[1].h;
105	switch (what) {
106		case ISIC_WHAT_ISAC:
107			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_ISAC_OFF);
108			bus_space_read_multi_1(t, h, ELSA_OFF_RW, buf, size);
109			break;
110		case ISIC_WHAT_HSCXA:
111			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXA_OFF);
112			bus_space_read_multi_1(t, h, ELSA_OFF_RW, buf, size);
113			break;
114		case ISIC_WHAT_HSCXB:
115			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXB_OFF);
116			bus_space_read_multi_1(t, h, ELSA_OFF_RW, buf, size);
117			break;
118	}
119}
120
121/*---------------------------------------------------------------------------*
122 *      ELSA QuickStep 1000pro/PCI ISAC put fifo routine
123 *---------------------------------------------------------------------------*/
124
125static void
126eqs1pp_write_fifo(struct isic_softc *sc, int what, const void *buf, size_t size)
127{
128	bus_space_tag_t t = sc->sc_maps[1].t;
129	bus_space_handle_t h = sc->sc_maps[1].h;
130	switch (what) {
131		case ISIC_WHAT_ISAC:
132			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_ISAC_OFF);
133			bus_space_write_multi_1(t, h, ELSA_OFF_RW, buf, size);
134			break;
135		case ISIC_WHAT_HSCXA:
136			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXA_OFF);
137			bus_space_write_multi_1(t, h, ELSA_OFF_RW, buf, size);
138			break;
139		case ISIC_WHAT_HSCXB:
140			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXB_OFF);
141			bus_space_write_multi_1(t, h, ELSA_OFF_RW, buf, size);
142			break;
143	}
144}
145
146/*---------------------------------------------------------------------------*
147 *      ELSA QuickStep 1000pro/PCI ISAC put register routine
148 *---------------------------------------------------------------------------*/
149
150static void
151eqs1pp_write_reg(struct isic_softc *sc, int what, bus_size_t offs, u_int8_t data)
152{
153	bus_space_tag_t t = sc->sc_maps[1].t;
154	bus_space_handle_t h = sc->sc_maps[1].h;
155	switch (what) {
156		case ISIC_WHAT_ISAC:
157			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_ISAC_OFF+offs);
158			bus_space_write_1(t, h, ELSA_OFF_RW, data);
159			break;
160		case ISIC_WHAT_HSCXA:
161			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXA_OFF+offs);
162			bus_space_write_1(t, h, ELSA_OFF_RW, data);
163			break;
164		case ISIC_WHAT_HSCXB:
165			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXB_OFF+offs);
166			bus_space_write_1(t, h, ELSA_OFF_RW, data);
167			break;
168		case ISIC_WHAT_IPAC:
169			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_IPAC_OFF+offs);
170			bus_space_write_1(t, h, ELSA_OFF_RW, data);
171			break;
172	}
173}
174
175/*---------------------------------------------------------------------------*
176 *	ELSA QuickStep 1000pro/PCI ISAC get register routine
177 *---------------------------------------------------------------------------*/
178
179static u_int8_t
180eqs1pp_read_reg(struct isic_softc *sc, int what, bus_size_t offs)
181{
182	bus_space_tag_t t = sc->sc_maps[1].t;
183	bus_space_handle_t h = sc->sc_maps[1].h;
184	switch (what) {
185		case ISIC_WHAT_ISAC:
186			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_ISAC_OFF+offs);
187			return bus_space_read_1(t, h, ELSA_OFF_RW);
188		case ISIC_WHAT_HSCXA:
189			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXA_OFF+offs);
190			return bus_space_read_1(t, h, ELSA_OFF_RW);
191		case ISIC_WHAT_HSCXB:
192			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXB_OFF+offs);
193			return bus_space_read_1(t, h, ELSA_OFF_RW);
194		case ISIC_WHAT_IPAC:
195		{
196			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_IPAC_OFF+offs);
197			return bus_space_read_1(t, h, ELSA_OFF_RW);
198		}
199	}
200
201	return 0;
202}
203
204/*---------------------------------------------------------------------------*
205 *	isic_attach_Eqs1pp - attach for ELSA QuickStep 1000pro/PCI
206 *---------------------------------------------------------------------------*/
207
208int
209isic_attach_Eqs1pp(struct pci_isic_softc *psc, struct pci_attach_args *pa)
210{
211	struct isic_softc *sc = &psc->sc_isic;
212
213	/* setup io mappings */
214	sc->sc_num_mappings = 2;
215	MALLOC_MAPS(sc);
216	sc->sc_maps[0].size = 0;
217	if (pci_mapreg_map(pa, ELSA_PORT0_MEM_MAPOFF, PCI_MAPREG_TYPE_MEM, 0,
218	    &sc->sc_maps[0].t, &sc->sc_maps[0].h, &psc->sc_base, &psc->sc_size) != 0
219	   && pci_mapreg_map(pa, ELSA_PORT0_IO_MAPOFF, PCI_MAPREG_TYPE_IO, 0,
220	    &sc->sc_maps[0].t, &sc->sc_maps[0].h, &psc->sc_base, &psc->sc_size) != 0) {
221		aprint_error_dev(&sc->sc_dev, "can't map card registers\n");
222		return (0);
223	}
224
225	/* PLX9050 Errata #1 */
226	if (PCI_REVISION(pa->pa_class) == 1 && psc->sc_base & 0x00000080) {
227#ifdef DEBUG
228		printf("%s: no LCR access\n", device_xname(&sc->sc_dev));
229#endif
230	} else
231		psc->flags |= PCIISIC_LCROK;
232
233	sc->sc_maps[1].size = 0;
234	if (pci_mapreg_map(pa, ELSA_PORT1_MAPOFF, PCI_MAPREG_TYPE_IO, 0,
235	    &sc->sc_maps[1].t, &sc->sc_maps[1].h, NULL, NULL)) {
236		aprint_error_dev(&sc->sc_dev, "can't map i/o space\n");
237		return (0);
238	}
239
240	/* setup access routines */
241
242	sc->clearirq = NULL;
243	sc->readreg = eqs1pp_read_reg;
244	sc->writereg = eqs1pp_write_reg;
245
246	sc->readfifo = eqs1pp_read_fifo;
247	sc->writefifo = eqs1pp_write_fifo;
248
249	sc->drv_command = elsa_cmd_req;
250
251	/* setup card type */
252
253	sc->sc_cardtyp = CARD_TYPEP_ELSAQS1PCI;
254
255	/* setup IOM bus type */
256
257	sc->sc_bustyp = BUS_TYPE_IOM2;
258
259	/* setup chip type = IPAC ! */
260
261	sc->sc_ipac = 1;
262	sc->sc_bfifolen = IPAC_BFIFO_LEN;
263
264	IPAC_WRITE(IPAC_ACFG, 0);	/* outputs are open drain */
265	IPAC_WRITE(IPAC_AOE,		/* aux 5..2 are inputs, 7, 6 outputs */
266		(IPAC_AOE_OE5 | IPAC_AOE_OE4 | IPAC_AOE_OE3 | IPAC_AOE_OE2));
267	IPAC_WRITE(IPAC_ATX, ELSA_NO_LED);	/* set all output lines high */
268	callout_init(&((struct pci_isic_softc *)sc)->ledcallout, 0);
269
270	/* disable any interrupts */
271	IPAC_WRITE(IPAC_MASK, 0xff);
272        bus_space_write_1(sc->sc_maps[0].t, sc->sc_maps[0].h, 0x4c, 0x01);
273
274	return (1);
275}
276
277int
278isic_intr_qs1p(void *vsc)
279{
280	struct pci_isic_softc *psc = vsc;
281	struct isic_softc *sc = &psc->sc_isic;
282	u_int32_t intcsr;
283
284	/*
285	 * if we are not hit by the PLX bug we can try a shortcut
286	 * (should improve speed for shared IRQs)
287	 */
288	if (psc->flags & PCIISIC_LCROK) {
289		intcsr = bus_space_read_4(sc->sc_maps[0].t, sc->sc_maps[0].h,
290					  0x4c /* INTCSR */);
291		if (!(intcsr & 0x4 /* LINTi1STAT */))
292			return (0);
293	}
294
295	return (isicintr(sc));
296}
297
298static void
299elsa_cmd_req(struct isic_softc *sc, int cmd, void *data)
300{
301	intptr_t v;
302	int s;
303	struct pci_isic_softc *psc = (struct pci_isic_softc *)sc;
304
305	switch (cmd) {
306	case CMR_DOPEN:
307		s = splnet();
308		/* enable hscx/isac irq's */
309		IPAC_WRITE(IPAC_MASK, (IPAC_MASK_INT1 | IPAC_MASK_INT0));
310		/* enable card interrupt */
311	        bus_space_write_1(sc->sc_maps[0].t, sc->sc_maps[0].h, 0x4c, 0x41);
312		splx(s);
313		break;
314	case CMR_DCLOSE:
315		s = splnet();
316		callout_stop(&psc->ledcallout);
317		IPAC_WRITE(IPAC_ATX, ELSA_NO_LED);
318		IPAC_WRITE(IPAC_MASK, 0xff);
319	        bus_space_write_1(sc->sc_maps[0].t, sc->sc_maps[0].h, 0x4c, 0x01);
320		splx(s);
321		break;
322	case CMR_SETLEDS:
323		v = (intptr_t)data;
324		callout_stop(&psc->ledcallout);
325
326		/* the magic value and keep reset off */
327		psc->ledstat = ELSA_NO_LED;
328		psc->ledblinkmask = 0;
329		psc->ledblinkfreq = 0;
330
331		/* now see what LEDs we want to add */
332		if (v & CMRLEDS_TEI)
333			psc->ledstat &= ~ELSA_GREEN_LED;
334
335		if (v & (CMRLEDS_B0|CMRLEDS_B1)) {
336			psc->ledstat &= ~ELSA_YELLOW_LED;
337			psc->ledblinkmask |= ELSA_YELLOW_LED;
338			if ((v & (CMRLEDS_B0|CMRLEDS_B1))
339			    == (CMRLEDS_B0|CMRLEDS_B1))
340				psc->ledblinkfreq = hz/4;
341			else
342				psc->ledblinkfreq = hz;
343		}
344
345		elsa_led_handler(psc);
346		break;
347	}
348}
349
350static void
351elsa_led_handler(void *token)
352{
353	struct pci_isic_softc *psc = token;
354	struct isic_softc *sc = token; /* XXX */
355	int s;
356
357	s = splnet();
358	IPAC_WRITE(IPAC_ATX, psc->ledstat);
359	splx(s);
360	if (psc->ledblinkfreq) {
361		psc->ledstat ^= psc->ledblinkmask;
362		callout_reset(&psc->ledcallout, psc->ledblinkfreq,
363		    elsa_led_handler, psc);
364	}
365}
366