text: /*-
   * Copyright (c) 2012 Juli Mallett <jmallett@FreeBSD.org>
   * 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.
   *
   * $FreeBSD: releng/11.0/sys/mips/cavium/octeon_pci_console.c 243255 2012-11-19 01:58:20Z jmallett $
   */
  
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: releng/11.0/sys/mips/cavium/octeon_pci_console.c 243255 2012-11-19 01:58:20Z jmallett $");
  
  #include <sys/param.h>
  #include <sys/systm.h>
  #include <sys/bus.h>
  #include <sys/conf.h>
  #include <sys/cons.h>
  #include <sys/kernel.h>
  #include <sys/reboot.h>
  
  #include <contrib/octeon-sdk/cvmx.h>
  #include <contrib/octeon-sdk/cvmx-bootmem.h>
  #include <contrib/octeon-sdk/cvmx-interrupt.h>
  #include <contrib/octeon-sdk/octeon-pci-console.h>
  
  #ifdef OCTEON_VENDOR_RADISYS
  #define	OPCIC_FLAG_RSYS		(0x00000001)
  
  #define	OPCIC_RSYS_FIFO_SIZE	(0x2000)
  #endif
  
  struct opcic_softc {
  	unsigned sc_flags;
  	uint64_t sc_base_addr;
  };
  
  static struct opcic_softc opcic_instance;
  
  static cn_probe_t opcic_cnprobe;
  static cn_init_t opcic_cninit;
  static cn_term_t opcic_cnterm;
  static cn_getc_t opcic_cngetc;
  static cn_putc_t opcic_cnputc;
  static cn_grab_t opcic_cngrab;
  static cn_ungrab_t opcic_cnungrab;
  
  #ifdef OCTEON_VENDOR_RADISYS
  static int opcic_rsys_cngetc(struct opcic_softc *);
  static void opcic_rsys_cnputc(struct opcic_softc *, int);
  #endif
  
  CONSOLE_DRIVER(opcic);
  
  static void
  opcic_cnprobe(struct consdev *cp)
  {
  	const struct cvmx_bootmem_named_block_desc *pci_console_block;
  	struct opcic_softc *sc;
  
  	sc = &opcic_instance;
  	sc->sc_flags = 0;
  	sc->sc_base_addr = 0;
  
  	cp->cn_pri = CN_DEAD;
  
  	switch (cvmx_sysinfo_get()->board_type) {
  #ifdef OCTEON_VENDOR_RADISYS
  	case CVMX_BOARD_TYPE_CUST_RADISYS_RSYS4GBE:
  		pci_console_block =
  		    cvmx_bootmem_find_named_block("rsys_gbl_memory");
  		if (pci_console_block != NULL) {
  			sc->sc_flags |= OPCIC_FLAG_RSYS;
  			sc->sc_base_addr = pci_console_block->base_addr;
  			break;
  		}
  #endif
  	default:
  		pci_console_block =
  		    cvmx_bootmem_find_named_block(OCTEON_PCI_CONSOLE_BLOCK_NAME);
  		if (pci_console_block == NULL)
  			return;
  		sc->sc_base_addr = pci_console_block->base_addr;
  		break;
  	}
  
  	cp->cn_arg = sc;
  	snprintf(cp->cn_name, sizeof cp->cn_name, "opcic@%p", cp->cn_arg);
  	cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
  }
  
  static void
  opcic_cninit(struct consdev *cp)
  {
  	(void)cp;
  }
  
  static void
  opcic_cnterm(struct consdev *cp)
  {
  	(void)cp;
  }
  
  static int
  opcic_cngetc(struct consdev *cp)
  {
  	struct opcic_softc *sc;
  	char ch;
  	int rv;
  
  	sc = cp->cn_arg;
  
  #ifdef OCTEON_VENDOR_RADISYS
  	if ((sc->sc_flags & OPCIC_FLAG_RSYS) != 0)
  		return (opcic_rsys_cngetc(sc));
  #endif
  
  	rv = octeon_pci_console_read(sc->sc_base_addr, 0, &ch, 1,
  	    OCT_PCI_CON_FLAG_NONBLOCK);
  	if (rv != 1)
  		return (-1);
  	return (ch);
  }
  
  static void
  opcic_cnputc(struct consdev *cp, int c)
  {
  	struct opcic_softc *sc;
  	char ch;
  	int rv;
  
  	sc = cp->cn_arg;
  	ch = c;
  
  #ifdef OCTEON_VENDOR_RADISYS
  	if ((sc->sc_flags & OPCIC_FLAG_RSYS) != 0) {
  		opcic_rsys_cnputc(sc, c);
  		return;
  	}
  #endif
  
  	rv = octeon_pci_console_write(sc->sc_base_addr, 0, &ch, 1, 0);
  	if (rv == -1)
  		panic("%s: octeon_pci_console_write failed.", __func__);
  }
  
  static void
  opcic_cngrab(struct consdev *cp)
  {
  	(void)cp;
  }
  
  static void
  opcic_cnungrab(struct consdev *cp)
  {
  	(void)cp;
  }
  
  #ifdef OCTEON_VENDOR_RADISYS
  static int
  opcic_rsys_cngetc(struct opcic_softc *sc)
  {
  	uint64_t gbl_base;
  	uint64_t console_base;
  	uint64_t console_rbuf;
  	uint64_t console_rcnt[2];
  	uint16_t rcnt[2];
  	uint16_t roff;
  	int c;
  
  	gbl_base = CVMX_ADD_IO_SEG(sc->sc_base_addr);
  	console_base = gbl_base + 0x10;
  
  	console_rbuf = console_base + 0x2018;
  	console_rcnt[0] = console_base + 0x08;
  	console_rcnt[1] = console_base + 0x0a;
  
  	/* Check if there is anything new in the FIFO.  */
  	rcnt[0] = cvmx_read64_uint16(console_rcnt[0]);
  	rcnt[1] = cvmx_read64_uint16(console_rcnt[1]);
  	if (rcnt[0] == rcnt[1])
  		return (-1);
  
  	/* Get first new character in the FIFO.  */
  	if (rcnt[0] != 0)
  		roff = rcnt[0] - 1;
  	else
  		roff = OPCIC_RSYS_FIFO_SIZE - 1;
  	c = cvmx_read64_uint8(console_rbuf + roff);
  
  	/* Advance FIFO.  */
  	rcnt[1] = (rcnt[1] + 1) % OPCIC_RSYS_FIFO_SIZE;
  	cvmx_write64_uint16(console_rcnt[1], rcnt[1]);
  
  	return (c);
  }
  
  static void
  opcic_rsys_cnputc(struct opcic_softc *sc, int c)
  {
  	uint64_t gbl_base;
  	uint64_t console_base;
  	uint64_t console_wbuf;
  	uint64_t console_wcnt;
  	uint16_t wcnt;
  
  	gbl_base = CVMX_ADD_IO_SEG(sc->sc_base_addr);
  	console_base = gbl_base + 0x10;
  
  	console_wbuf = console_base + 0x0018;
  	console_wcnt = console_base + 0x0c;
  
  	/* Append character to FIFO.  */
  	wcnt = cvmx_read64_uint16(console_wcnt) % OPCIC_RSYS_FIFO_SIZE;
  	cvmx_write64_uint8(console_wbuf + wcnt, (uint8_t)c);
  	cvmx_write64_uint16(console_wcnt, wcnt + 1);
  }
  #endif
  