/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012 Juli Mallett * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #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