366 lines
11 KiB
C
366 lines
11 KiB
C
/* $NetBSD: isic_pci_elsa_qs1p.c,v 1.19 2009/03/14 15:36:19 dsl 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.19 2009/03/14 15:36:19 dsl 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(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(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);
|
|
}
|
|
}
|