NetBSD-5.0.2/sys/dev/pci/isic_pci_elsa_qs1p.c

Compare this file to the similar file:
Show the results in this format:

/* $NetBSD: isic_pci_elsa_qs1p.c,v 1.18 2008/04/10 19:13:37 cegger Exp $ */

/*
 * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *---------------------------------------------------------------------------
 *
 *	isic - I4B Siemens ISDN Chipset Driver for ELSA Quickstep 1000pro PCI
 *	=====================================================================
 *
 *---------------------------------------------------------------------------*/

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: isic_pci_elsa_qs1p.c,v 1.18 2008/04/10 19:13:37 cegger Exp $");

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/callout.h>

#include <sys/bus.h>
#include <sys/device.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <netisdn/i4b_debug.h>
#include <netisdn/i4b_ioctl.h>
#include <netisdn/i4b_global.h>
#include <netisdn/i4b_debug.h>
#include <netisdn/i4b_l2.h>
#include <netisdn/i4b_l1l2.h>
#include <netisdn/i4b_mbuf.h>

#include <dev/ic/isic_l1.h>
#include <dev/ic/isac.h>
#include <dev/ic/hscx.h>
#include <dev/ic/ipac.h>
#include <dev/pci/isic_pci.h>

/* masks for register encoded in base addr */

#define ELSA_BASE_MASK		0x0ffff
#define ELSA_OFF_MASK		0xf0000

/* register id's to be encoded in base addr */

#define ELSA_IDISAC		0x00000
#define ELSA_IDHSCXA		0x10000
#define ELSA_IDHSCXB		0x20000
#define ELSA_IDIPAC		0x40000

/* offsets from base address */

#define ELSA_OFF_ALE		0x00
#define ELSA_OFF_RW		0x01

/* LED values */
#define	ELSA_NO_LED		0xff
#define	ELSA_GREEN_LED		0x40
#define	ELSA_YELLOW_LED		0x80

#define ELSA_PORT0_MEM_MAPOFF	PCI_MAPREG_START
#define ELSA_PORT0_IO_MAPOFF	PCI_MAPREG_START+4
#define ELSA_PORT1_MAPOFF	PCI_MAPREG_START+12


static void elsa_cmd_req(struct isic_softc *sc, int cmd, void *data);
static void elsa_led_handler(void *token);

/*---------------------------------------------------------------------------*
 *      ELSA QuickStep 1000pro/PCI ISAC get fifo routine
 *---------------------------------------------------------------------------*/

static void
eqs1pp_read_fifo(struct isic_softc *sc, int what, void *buf, size_t size)
{
	bus_space_tag_t t = sc->sc_maps[1].t;
	bus_space_handle_t h = sc->sc_maps[1].h;
	switch (what) {
		case ISIC_WHAT_ISAC:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_ISAC_OFF);
			bus_space_read_multi_1(t, h, ELSA_OFF_RW, buf, size);
			break;
		case ISIC_WHAT_HSCXA:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXA_OFF);
			bus_space_read_multi_1(t, h, ELSA_OFF_RW, buf, size);
			break;
		case ISIC_WHAT_HSCXB:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXB_OFF);
			bus_space_read_multi_1(t, h, ELSA_OFF_RW, buf, size);
			break;
	}
}

/*---------------------------------------------------------------------------*
 *      ELSA QuickStep 1000pro/PCI ISAC put fifo routine
 *---------------------------------------------------------------------------*/

static void
eqs1pp_write_fifo(struct isic_softc *sc, int what, const void *buf, size_t size)
{
	bus_space_tag_t t = sc->sc_maps[1].t;
	bus_space_handle_t h = sc->sc_maps[1].h;
	switch (what) {
		case ISIC_WHAT_ISAC:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_ISAC_OFF);
			bus_space_write_multi_1(t, h, ELSA_OFF_RW, buf, size);
			break;
		case ISIC_WHAT_HSCXA:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXA_OFF);
			bus_space_write_multi_1(t, h, ELSA_OFF_RW, buf, size);
			break;
		case ISIC_WHAT_HSCXB:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXB_OFF);
			bus_space_write_multi_1(t, h, ELSA_OFF_RW, buf, size);
			break;
	}
}

/*---------------------------------------------------------------------------*
 *      ELSA QuickStep 1000pro/PCI ISAC put register routine
 *---------------------------------------------------------------------------*/

static void
eqs1pp_write_reg(struct isic_softc *sc, int what, bus_size_t offs, u_int8_t data)
{
	bus_space_tag_t t = sc->sc_maps[1].t;
	bus_space_handle_t h = sc->sc_maps[1].h;
	switch (what) {
		case ISIC_WHAT_ISAC:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_ISAC_OFF+offs);
			bus_space_write_1(t, h, ELSA_OFF_RW, data);
			break;
		case ISIC_WHAT_HSCXA:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXA_OFF+offs);
			bus_space_write_1(t, h, ELSA_OFF_RW, data);
			break;
		case ISIC_WHAT_HSCXB:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXB_OFF+offs);
			bus_space_write_1(t, h, ELSA_OFF_RW, data);
			break;
		case ISIC_WHAT_IPAC:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_IPAC_OFF+offs);
			bus_space_write_1(t, h, ELSA_OFF_RW, data);
			break;
	}
}

/*---------------------------------------------------------------------------*
 *	ELSA QuickStep 1000pro/PCI ISAC get register routine
 *---------------------------------------------------------------------------*/

static u_int8_t
eqs1pp_read_reg(struct isic_softc *sc, int what, bus_size_t offs)
{
	bus_space_tag_t t = sc->sc_maps[1].t;
	bus_space_handle_t h = sc->sc_maps[1].h;
	switch (what) {
		case ISIC_WHAT_ISAC:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_ISAC_OFF+offs);
			return bus_space_read_1(t, h, ELSA_OFF_RW);
		case ISIC_WHAT_HSCXA:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXA_OFF+offs);
			return bus_space_read_1(t, h, ELSA_OFF_RW);
		case ISIC_WHAT_HSCXB:
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_HSCXB_OFF+offs);
			return bus_space_read_1(t, h, ELSA_OFF_RW);
		case ISIC_WHAT_IPAC:
		{
			bus_space_write_1(t, h, ELSA_OFF_ALE, IPAC_IPAC_OFF+offs);
			return bus_space_read_1(t, h, ELSA_OFF_RW);
		}
	}

	return 0;
}

/*---------------------------------------------------------------------------*
 *	isic_attach_Eqs1pp - attach for ELSA QuickStep 1000pro/PCI
 *---------------------------------------------------------------------------*/

int
isic_attach_Eqs1pp(psc, pa)
	struct pci_isic_softc *psc;
	struct pci_attach_args *pa;
{
	struct isic_softc *sc = &psc->sc_isic;

	/* setup io mappings */
	sc->sc_num_mappings = 2;
	MALLOC_MAPS(sc);
	sc->sc_maps[0].size = 0;
	if (pci_mapreg_map(pa, ELSA_PORT0_MEM_MAPOFF, PCI_MAPREG_TYPE_MEM, 0,
	    &sc->sc_maps[0].t, &sc->sc_maps[0].h, &psc->sc_base, &psc->sc_size) != 0
	   && pci_mapreg_map(pa, ELSA_PORT0_IO_MAPOFF, PCI_MAPREG_TYPE_IO, 0,
	    &sc->sc_maps[0].t, &sc->sc_maps[0].h, &psc->sc_base, &psc->sc_size) != 0) {
		aprint_error_dev(&sc->sc_dev, "can't map card registers\n");
		return (0);
	}

	/* PLX9050 Errata #1 */
	if (PCI_REVISION(pa->pa_class) == 1 && psc->sc_base & 0x00000080) {
#ifdef DEBUG
		printf("%s: no LCR access\n", device_xname(&sc->sc_dev));
#endif
	} else
		psc->flags |= PCIISIC_LCROK;

	sc->sc_maps[1].size = 0;
	if (pci_mapreg_map(pa, ELSA_PORT1_MAPOFF, PCI_MAPREG_TYPE_IO, 0,
	    &sc->sc_maps[1].t, &sc->sc_maps[1].h, NULL, NULL)) {
		aprint_error_dev(&sc->sc_dev, "can't map i/o space\n");
		return (0);
	}

	/* setup access routines */

	sc->clearirq = NULL;
	sc->readreg = eqs1pp_read_reg;
	sc->writereg = eqs1pp_write_reg;

	sc->readfifo = eqs1pp_read_fifo;
	sc->writefifo = eqs1pp_write_fifo;

	sc->drv_command = elsa_cmd_req;

	/* setup card type */

	sc->sc_cardtyp = CARD_TYPEP_ELSAQS1PCI;

	/* setup IOM bus type */

	sc->sc_bustyp = BUS_TYPE_IOM2;

	/* setup chip type = IPAC ! */

	sc->sc_ipac = 1;
	sc->sc_bfifolen = IPAC_BFIFO_LEN;

	IPAC_WRITE(IPAC_ACFG, 0);	/* outputs are open drain */
	IPAC_WRITE(IPAC_AOE,		/* aux 5..2 are inputs, 7, 6 outputs */
		(IPAC_AOE_OE5 | IPAC_AOE_OE4 | IPAC_AOE_OE3 | IPAC_AOE_OE2));
	IPAC_WRITE(IPAC_ATX, ELSA_NO_LED);	/* set all output lines high */
	callout_init(&((struct pci_isic_softc *)sc)->ledcallout, 0);

	/* disable any interrupts */
	IPAC_WRITE(IPAC_MASK, 0xff);
        bus_space_write_1(sc->sc_maps[0].t, sc->sc_maps[0].h, 0x4c, 0x01);

	return (1);
}

int
isic_intr_qs1p(vsc)
	void *vsc;
{
	struct pci_isic_softc *psc = vsc;
	struct isic_softc *sc = &psc->sc_isic;
	u_int32_t intcsr;

	/*
	 * if we are not hit by the PLX bug we can try a shortcut
	 * (should improve speed for shared IRQs)
	 */
	if (psc->flags & PCIISIC_LCROK) {
		intcsr = bus_space_read_4(sc->sc_maps[0].t, sc->sc_maps[0].h,
					  0x4c /* INTCSR */);
		if (!(intcsr & 0x4 /* LINTi1STAT */))
			return (0);
	}

	return (isicintr(sc));
}

static void
elsa_cmd_req(struct isic_softc *sc, int cmd, void *data)
{
	intptr_t v;
	int s;
	struct pci_isic_softc *psc = (struct pci_isic_softc *)sc;

	switch (cmd) {
	case CMR_DOPEN:
		s = splnet();
		/* enable hscx/isac irq's */
		IPAC_WRITE(IPAC_MASK, (IPAC_MASK_INT1 | IPAC_MASK_INT0));
		/* enable card interrupt */
	        bus_space_write_1(sc->sc_maps[0].t, sc->sc_maps[0].h, 0x4c, 0x41);
		splx(s);
		break;
	case CMR_DCLOSE:
		s = splnet();
		callout_stop(&psc->ledcallout);
		IPAC_WRITE(IPAC_ATX, ELSA_NO_LED);
		IPAC_WRITE(IPAC_MASK, 0xff);
	        bus_space_write_1(sc->sc_maps[0].t, sc->sc_maps[0].h, 0x4c, 0x01);
		splx(s);
		break;
	case CMR_SETLEDS:
		v = (intptr_t)data;
		callout_stop(&psc->ledcallout);

		/* the magic value and keep reset off */
		psc->ledstat = ELSA_NO_LED;
		psc->ledblinkmask = 0;
		psc->ledblinkfreq = 0;

		/* now see what LEDs we want to add */
		if (v & CMRLEDS_TEI)
			psc->ledstat &= ~ELSA_GREEN_LED;

		if (v & (CMRLEDS_B0|CMRLEDS_B1)) {
			psc->ledstat &= ~ELSA_YELLOW_LED;
			psc->ledblinkmask |= ELSA_YELLOW_LED;
			if ((v & (CMRLEDS_B0|CMRLEDS_B1))
			    == (CMRLEDS_B0|CMRLEDS_B1))
				psc->ledblinkfreq = hz/4;
			else
				psc->ledblinkfreq = hz;
		}

		elsa_led_handler(psc);
		break;
	}
}

static void
elsa_led_handler(void *token)
{
	struct pci_isic_softc *psc = token;
	struct isic_softc *sc = token; /* XXX */
	int s;

	s = splnet();
	IPAC_WRITE(IPAC_ATX, psc->ledstat);
	splx(s);
	if (psc->ledblinkfreq) {
		psc->ledstat ^= psc->ledblinkmask;
		callout_reset(&psc->ledcallout, psc->ledblinkfreq,
		    elsa_led_handler, psc);
	}
}