text: /*	$OpenBSD: if_ie_gsc.c,v 1.25 2004/10/28 19:13:30 mickey Exp $	*/
  
  /*
   * Copyright (c) 1998-2004 Michael Shalayeff
   * 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 ``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 HIS RELATIVES 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 MIND, 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.
   */
  
  /*
   * References:
   * 1. 82596DX and 82596SX High-Performance 32-bit Local Area Network Coprocessor
   *    Intel Corporation, November 1996, Order Number: 290219-006
   *
   * 2. 712 I/O Subsystem ERS Rev 1.0
   *    Hewlett-Packard, June 17 1992, Dwg No. A-A2263-66510-31
   */
  #include <sys/param.h>
  #include <sys/systm.h>
  #include <sys/device.h>
  #include <sys/socket.h>
  #include <sys/sockio.h>
  
  #include <net/if.h>
  #include <net/if_dl.h>
  #include <net/if_types.h>
  #include <net/if_media.h>
  
  #include <netinet/in.h>
  #include <netinet/if_ether.h>
  
  #include <machine/bus.h>
  #include <machine/intr.h>
  #include <machine/iomod.h>
  #include <machine/autoconf.h>
  
  #include <hppa/dev/cpudevs.h>
  #include <hppa/gsc/gscbusvar.h>
  
  #include <dev/ic/i82596reg.h>
  #include <dev/ic/i82596var.h>
  
  #define	IEGSC_GECKO	IEMD_FLAG0
  
  struct ie_gsc_regs {
  	u_int32_t	ie_reset;
  	u_int32_t	ie_port;
  	u_int32_t	ie_attn;
  };
  
  #define	IE_SIZE	0x8000
  
  int	ie_gsc_probe(struct device *, void *, void *);
  void	ie_gsc_attach(struct device *, struct device *, void *);
  
  struct cfattach ie_gsc_ca = {
  	sizeof(struct ie_softc), ie_gsc_probe, ie_gsc_attach
  };
  
  static int ie_gsc_media[] = {
  	IFM_ETHER | IFM_10_2,
  };
  #define	IE_NMEDIA	(sizeof(ie_gsc_media) / sizeof(ie_gsc_media[0]))
  
  char *ie_mem;
  
  void ie_gsc_reset(struct ie_softc *sc, int what);
  void ie_gsc_attend(struct ie_softc *sc);
  void ie_gsc_run(struct ie_softc *sc);
  void ie_gsc_port(struct ie_softc *sc, u_int);
  #ifdef USELEDS
  int ie_gsc_intrhook(struct ie_softc *sc, int what);
  #endif
  u_int16_t ie_gsc_read16(struct ie_softc *sc, int offset);
  void ie_gsc_write16(struct ie_softc *sc, int offset, u_int16_t v);
  void ie_gsc_write24(struct ie_softc *sc, int offset, int addr);
  void ie_gsc_memcopyin(struct ie_softc *sc, void *p, int offset, size_t);
  void ie_gsc_memcopyout(struct ie_softc *sc, const void *p, int, size_t);
  
  
  void
  ie_gsc_reset(sc, what)
  	struct ie_softc *sc;
  	int what;
  {
  	volatile struct ie_gsc_regs *r = (struct ie_gsc_regs *)sc->ioh;
  	int i;
  
  	r->ie_reset = 0;
  	/*
  	 * per [2] 4.6.2.1
  	 * delay for 10 system clocks + 5 transmit clocks,
  	 * NB: works for system clocks over 10MHz
  	 */
  	DELAY(1000);
  
  	switch (what) {
  	case IE_CHIP_PROBE:
  		break;
  
  	case IE_CARD_RESET:
  		/*
  		 * after the hardware reset:
  		 * inform i825[89]6 about new SCP address,
  		 * maddr must be at least 16-byte aligned
  		 */
  		ie_gsc_port(sc, IE_PORT_SCP);
  		ie_gsc_attend(sc);
  
  		for (i = 9000; i-- && ie_gsc_read16(sc, IE_ISCP_BUSY(sc->iscp));
  		     DELAY(100))
  			pdcache(0, sc->sc_maddr + sc->iscp, IE_ISCP_SZ);
  
  #ifdef I82596_DEBUG
  		if (i < 0) {
  			printf("timeout for PORT command (%x)%s\n",
  			       ie_gsc_read16(sc, IE_ISCP_BUSY(sc->iscp)),
  			       (sc->sc_flags & IEGSC_GECKO)? " on gecko":"");
  			return;
  		}
  #endif
  		break;
  	}
  }
  
  void
  ie_gsc_attend(sc)
  	struct ie_softc *sc;
  {
  	volatile struct ie_gsc_regs *r = (struct ie_gsc_regs *)sc->ioh;
  
  	fdcache(0, (vaddr_t)ie_mem, IE_SIZE);
  	DELAY(1);
  	r->ie_attn = 0;
  	DELAY(1);
  }
  
  void
  ie_gsc_run(sc)
  	struct ie_softc *sc;
  {
  }
  
  void
  ie_gsc_port(sc, cmd)
  	struct ie_softc *sc;
  	u_int cmd;
  {
  	switch (cmd) {
  	case IE_PORT_RESET:
  		cmd = 0;
  		break;
  	case IE_PORT_TEST:
  		cmd = ((u_int)sc->sc_maddr + sc->scp) | 1;
  		break;
  	case IE_PORT_SCP:
  		cmd = ((u_int)sc->sc_maddr + sc->scp) | 2;
  		break;
  	case IE_PORT_DUMP:
  		cmd = 3;
  		break;
  	}
  
  	if (sc->sc_flags & IEGSC_GECKO) {
  		volatile struct ie_gsc_regs *r = (struct ie_gsc_regs *)sc->ioh;
  		r->ie_port = cmd & 0xffff;
  		DELAY(1000);
  		r->ie_port = cmd >> 16;
  		DELAY(1000);
  	} else {
  		volatile struct ie_gsc_regs *r = (struct ie_gsc_regs *)sc->ioh;
  		r->ie_port = cmd >> 16;
  		DELAY(1000);
  		r->ie_port = cmd & 0xffff;
  		DELAY(1000);
  	}
  }
  
  #ifdef USELEDS
  int
  ie_gsc_intrhook(sc, where)
  	struct ie_softc *sc;
  	int where;
  {
  	switch (where) {
  	case IE_INTR_ENRCV:
  		ledctl(PALED_NETRCV, 0, 0);
  		break;
  	case IE_INTR_ENSND:
  		ledctl(PALED_NETSND, 0, 0);
  		break;
  	case IE_INTR_EXIT:
  	case IE_INTR_LOOP:
  		fdcache(0, (vaddr_t)ie_mem, IE_SIZE);
  		break;
  	}
  	return 0;
  }
  #endif
  
  u_int16_t
  ie_gsc_read16(sc, offset)
  	struct ie_softc *sc;
  	int offset;
  {
  	volatile u_int16_t *addr = (volatile u_int16_t *)(sc->bh + offset);
  
  	asm __volatile ("fdc	%%r0(%%sr0, %0)" :: "r" (addr));
  	return *addr;
  }
  
  void
  ie_gsc_write16(sc, offset, v)
  	struct ie_softc *sc;
  	int offset;
  	u_int16_t v;
  {
  	volatile u_int16_t *addr = (volatile u_int16_t *)(sc->bh + offset);
  
  	*addr = v;
  	asm __volatile ("fdc	%%r0(%%sr0, %0)" :: "r" (addr));
  }
  
  void
  ie_gsc_write24(sc, offset, v)
  	struct ie_softc *sc;
  	int offset;
  	int v;
  {
  	volatile u_int16_t *addr = (volatile u_int16_t *)(sc->bh + offset);
  
  	addr[0] = (v      ) & 0xffff;
  	addr[1] = (v >> 16) & 0xffff;
  	asm __volatile ("fdc	%%r0(%%sr0, %0)" :: "r" (addr+0));
  	asm __volatile ("fdc	%%r0(%%sr0, %0)" :: "r" (addr+1));
  }
  
  void
  ie_gsc_memcopyin(sc, p, offset, size)
  	struct ie_softc	*sc;
  	void *p;
  	int offset;
  	size_t size;
  {
  	pdcache(0, sc->bh + offset, size);
  	bcopy ((void *)((u_long)sc->bh + offset), p, size);
  }
  
  void
  ie_gsc_memcopyout(sc, p, offset, size)
  	struct ie_softc	*sc;
  	const void *p;
  	int offset;
  	size_t size;
  {
  	bcopy (p, (void *)((u_long)sc->bh + offset), size);
  	fdcache(0, sc->bh + offset, size);
  }
  
  int
  ie_gsc_probe(parent, match, aux)
  	struct device *parent;
  	void *match, *aux;
  {
  	struct gsc_attach_args *ga = aux;
  
  	if (ga->ga_type.iodc_type != HPPA_TYPE_FIO ||
  	    (ga->ga_type.iodc_sv_model != HPPA_FIO_LAN &&
  	     ga->ga_type.iodc_sv_model != HPPA_FIO_GLAN))
  		return 0;
  
  	return 1;
  }
  
  void
  ie_gsc_attach(parent, self, aux)
  	struct device *parent, *self;
  	void *aux;
  {
  	struct pdc_lan_station_id pdc_mac PDC_ALIGNMENT;
  	struct ie_softc *sc = (struct ie_softc *)self;
  	struct gsc_attach_args *ga = aux;
  	/*bus_dma_segment_t seg;
  	int rseg;*/
  	int rv;
  #ifdef PMAPDEBUG
  	extern int pmapdebug;
  	int opmapdebug = pmapdebug;
  	pmapdebug = 0;
  #endif
  
  	sc->iot = sc->bt = ga->ga_iot;
  	if (bus_space_map(sc->iot, ga->ga_hpa, IOMOD_HPASIZE, 0, &sc->ioh)) {
  		printf(": can't map IO space\n");
  		return;
  	}
  
  	if (ga->ga_type.iodc_sv_model == HPPA_FIO_GLAN)
  		sc->sc_flags |= IEGSC_GECKO;
  
  	sc->sc_msize = IE_SIZE;
  	/* XXX memory must be under 16M until the mi part is fixed */
  #if 0
  	if (bus_dmamem_alloc(ga->ga_dmatag, sc->sc_msize, NBPG, 0,
  			     &seg, 1, &rseg, BUS_DMA_NOWAIT)) {
  		printf (": cannot allocate %d bytes of DMA memory\n",
  			sc->sc_msize);
  		return;
  	}
  	if (bus_dmamem_map(ga->ga_dmatag, &seg, rseg, sc->sc_msize,
  			   (caddr_t *)&sc->bh, BUS_DMA_NOWAIT)) {
  		printf (": cannot map DMA memory\n");
  		bus_dmamem_free(ga->ga_dmatag, &seg, rseg);
  		return;
  	}
  
  	bzero((void *)sc->bh, sc->sc_msize);
  	sc->sc_maddr = kvtop((caddr_t)sc->bh);
  
  #else
  	sc->bh = (u_int)ie_mem;
  	sc->sc_maddr = sc->bh;
  #endif
  	sc->sysbus = 0x40 | IE_SYSBUS_82586 | IE_SYSBUS_INTLOW | IE_SYSBUS_TRG | IE_SYSBUS_BE;
  
  	sc->do_xmitnopchain = 0;
  	sc->hwreset = ie_gsc_reset;
  	sc->chan_attn = ie_gsc_attend;
  	sc->port = ie_gsc_port;
  	sc->hwinit = ie_gsc_run;
  	sc->memcopyout = ie_gsc_memcopyout;
  	sc->memcopyin = ie_gsc_memcopyin;
  	sc->ie_bus_read16 = ie_gsc_read16;
  	sc->ie_bus_write16 = ie_gsc_write16;
  	sc->ie_bus_write24 = ie_gsc_write24;
  #ifdef USELEDS
  	sc->intrhook = ie_gsc_intrhook;
  #else
  	sc->intrhook = NULL;
  #endif
  
  #ifdef I82596_DEBUG
  	printf(" mem %x[%p]/%x", sc->bh, sc->sc_maddr, sc->sc_msize);
  	sc->sc_debug = IED_ALL;
  #endif
  	rv = i82596_probe(sc);
  	if (!rv) {
  		/*bus_dmamem_free(ga->ga_dmatag, &seg, sc->sc_msize);*/
  	}
  #ifdef PMAPDEBUG
  	pmapdebug = opmapdebug;
  #endif
  	if (!rv) {
  		printf("\n");
  		return;
  	}
  
  	if (pdc_call((iodcio_t)pdc, 0, PDC_LAN_STATION_ID,
  		     PDC_LAN_STATION_ID_READ, &pdc_mac, ga->ga_hpa) < 0)
  		bcopy((void *)ASP_PROM, sc->sc_arpcom.ac_enaddr,
  		      ETHER_ADDR_LEN);
  	else
  		bcopy(pdc_mac.addr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
  
  	printf(":");
  
  	sc->iscp = 0;
  	sc->scp = 32;
  	sc->scb = 94;
  	sc->buf_area = 256;
  	sc->buf_area_sz = sc->sc_msize - sc->buf_area;
  	sc->sc_type = sc->sc_flags & IEGSC_GECKO? "LASI/i82596CA" : "i82596DX";
  	sc->sc_vers = ga->ga_type.iodc_model * 10 + ga->ga_type.iodc_sv_rev;
  	i82596_attach(sc, sc->sc_type, (char *)sc->sc_arpcom.ac_enaddr,
  		      ie_gsc_media, IE_NMEDIA, ie_gsc_media[0]);
  
  	sc->sc_ih = gsc_intr_establish((struct gsc_softc *)parent,
  	    ga->ga_irq, IPL_NET, i82596_intr, sc, sc->sc_dev.dv_xname);
  }
  