root/sys/arm/freescale/vybrid/vf_uart.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
 * 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.
 */

/*
 * Vybrid Family Universal Asynchronous Receiver/Transmitter
 * Chapter 49, Vybrid Reference Manual, Rev. 5, 07/2013
 */

#include <sys/cdefs.h>
#include "opt_ddb.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/kdb.h>
#include <machine/bus.h>

#include <dev/uart/uart.h>
#include <dev/uart/uart_cpu.h>
#include <dev/uart/uart_cpu_fdt.h>
#include <dev/uart/uart_bus.h>

#include "uart_if.h"

#define UART_BDH        0x00    /* Baud Rate Registers: High */
#define UART_BDL        0x01    /* Baud Rate Registers: Low */
#define UART_C1         0x02    /* Control Register 1 */
#define UART_C2         0x03    /* Control Register 2 */
#define UART_S1         0x04    /* Status Register 1 */
#define UART_S2         0x05    /* Status Register 2 */
#define UART_C3         0x06    /* Control Register 3 */
#define UART_D          0x07    /* Data Register */
#define UART_MA1        0x08    /* Match Address Registers 1 */
#define UART_MA2        0x09    /* Match Address Registers 2 */
#define UART_C4         0x0A    /* Control Register 4 */
#define UART_C5         0x0B    /* Control Register 5 */
#define UART_ED         0x0C    /* Extended Data Register */
#define UART_MODEM      0x0D    /* Modem Register */
#define UART_IR         0x0E    /* Infrared Register */
#define UART_PFIFO      0x10    /* FIFO Parameters */
#define UART_CFIFO      0x11    /* FIFO Control Register */
#define UART_SFIFO      0x12    /* FIFO Status Register */
#define UART_TWFIFO     0x13    /* FIFO Transmit Watermark */
#define UART_TCFIFO     0x14    /* FIFO Transmit Count */
#define UART_RWFIFO     0x15    /* FIFO Receive Watermark */
#define UART_RCFIFO     0x16    /* FIFO Receive Count */
#define UART_C7816      0x18    /* 7816 Control Register */
#define UART_IE7816     0x19    /* 7816 Interrupt Enable Register */
#define UART_IS7816     0x1A    /* 7816 Interrupt Status Register */
#define UART_WP7816T0   0x1B    /* 7816 Wait Parameter Register */
#define UART_WP7816T1   0x1B    /* 7816 Wait Parameter Register */
#define UART_WN7816     0x1C    /* 7816 Wait N Register */
#define UART_WF7816     0x1D    /* 7816 Wait FD Register */
#define UART_ET7816     0x1E    /* 7816 Error Threshold Register */
#define UART_TL7816     0x1F    /* 7816 Transmit Length Register */
#define UART_C6         0x21    /* CEA709.1-B Control Register 6 */
#define UART_PCTH       0x22    /* CEA709.1-B Packet Cycle Time Counter High */
#define UART_PCTL       0x23    /* CEA709.1-B Packet Cycle Time Counter Low */
#define UART_B1T        0x24    /* CEA709.1-B Beta1 Timer */
#define UART_SDTH       0x25    /* CEA709.1-B Secondary Delay Timer High */
#define UART_SDTL       0x26    /* CEA709.1-B Secondary Delay Timer Low */
#define UART_PRE        0x27    /* CEA709.1-B Preamble */
#define UART_TPL        0x28    /* CEA709.1-B Transmit Packet Length */
#define UART_IE         0x29    /* CEA709.1-B Interrupt Enable Register */
#define UART_WB         0x2A    /* CEA709.1-B WBASE */
#define UART_S3         0x2B    /* CEA709.1-B Status Register */
#define UART_S4         0x2C    /* CEA709.1-B Status Register */
#define UART_RPL        0x2D    /* CEA709.1-B Received Packet Length */
#define UART_RPREL      0x2E    /* CEA709.1-B Received Preamble Length */
#define UART_CPW        0x2F    /* CEA709.1-B Collision Pulse Width */
#define UART_RIDT       0x30    /* CEA709.1-B Receive Indeterminate Time */
#define UART_TIDT       0x31    /* CEA709.1-B Transmit Indeterminate Time */

#define UART_C2_TE      (1 << 3)        /* Transmitter Enable */
#define UART_C2_TIE     (1 << 7)        /* Transmitter Interrupt Enable */
#define UART_C2_RE      (1 << 2)        /* Receiver Enable */
#define UART_C2_RIE     (1 << 5)        /* Receiver Interrupt Enable */
#define UART_S1_TDRE    (1 << 7)        /* Transmit Data Register Empty Flag */
#define UART_S1_RDRF    (1 << 5)        /* Receive Data Register Full Flag */
#define UART_S2_LBKDIF  (1 << 7)        /* LIN Break Detect Interrupt Flag */

#define UART_C4_BRFA    0x1f    /* Baud Rate Fine Adjust */
#define UART_BDH_SBR    0x1f    /* UART Baud Rate Bits */

/*
 * Low-level UART interface.
 */
static int vf_uart_probe(struct uart_bas *bas);
static void vf_uart_init(struct uart_bas *bas, int, int, int, int);
static void vf_uart_term(struct uart_bas *bas);
static void vf_uart_putc(struct uart_bas *bas, int);
static int vf_uart_rxready(struct uart_bas *bas);
static int vf_uart_getc(struct uart_bas *bas, struct mtx *);

void uart_reinit(struct uart_softc *,int,int);

static struct uart_ops uart_vybrid_ops = {
        .probe = vf_uart_probe,
        .init = vf_uart_init,
        .term = vf_uart_term,
        .putc = vf_uart_putc,
        .rxready = vf_uart_rxready,
        .getc = vf_uart_getc,
};

static int
vf_uart_probe(struct uart_bas *bas)
{

        return (0);
}

static void
vf_uart_init(struct uart_bas *bas, int baudrate, int databits,
    int stopbits, int parity)
{

}

static void
vf_uart_term(struct uart_bas *bas)
{

}

static void
vf_uart_putc(struct uart_bas *bas, int c)
{

        while (!(uart_getreg(bas, UART_S1) & UART_S1_TDRE))
                ;

        uart_setreg(bas, UART_D, c);
}

static int
vf_uart_rxready(struct uart_bas *bas)
{
        int usr1;

        usr1 = uart_getreg(bas, UART_S1);
        if (usr1 & UART_S1_RDRF) {
                return (1);
        }

        return (0);
}

static int
vf_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
{
        int c;

        uart_lock(hwmtx);

        while (!(uart_getreg(bas, UART_S1) & UART_S1_RDRF))
                ;

        c = uart_getreg(bas, UART_D);
        uart_unlock(hwmtx);

        return (c & 0xff);
}

/*
 * High-level UART interface.
 */
struct vf_uart_softc {
        struct uart_softc base;
};

void
uart_reinit(struct uart_softc *sc, int clkspeed, int baud)
{
        struct uart_bas *bas;
        int sbr;
        int brfa;
        int reg;

        bas = &sc->sc_bas;
        if (!bas) {
                printf("Error: can't reconfigure bas\n");
                return;
        }

        uart_setreg(bas, UART_MODEM, 0x00);

        /*
         * Disable transmitter and receiver
         * for a while.
         */
        reg = uart_getreg(bas, UART_C2);
        reg &= ~(UART_C2_RE | UART_C2_TE);
        uart_setreg(bas, UART_C2, 0x00);

        uart_setreg(bas, UART_C1, 0x00);

        sbr = (uint16_t) (clkspeed / (baud * 16));
        brfa = (clkspeed / baud) - (sbr * 16);

        reg = uart_getreg(bas, UART_BDH);
        reg &= ~UART_BDH_SBR;
        reg |= ((sbr & 0x1f00) >> 8);
        uart_setreg(bas, UART_BDH, reg);

        reg = sbr & 0x00ff;
        uart_setreg(bas, UART_BDL, reg);

        reg = uart_getreg(bas, UART_C4);
        reg &= ~UART_C4_BRFA;
        reg |= (brfa & UART_C4_BRFA);
        uart_setreg(bas, UART_C4, reg);

        reg = uart_getreg(bas, UART_C2);
        reg |= (UART_C2_RE | UART_C2_TE);
        uart_setreg(bas, UART_C2, reg);

}

static int vf_uart_bus_attach(struct uart_softc *);
static int vf_uart_bus_detach(struct uart_softc *);
static int vf_uart_bus_flush(struct uart_softc *, int);
static int vf_uart_bus_getsig(struct uart_softc *);
static int vf_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
static int vf_uart_bus_ipend(struct uart_softc *);
static int vf_uart_bus_param(struct uart_softc *, int, int, int, int);
static int vf_uart_bus_probe(struct uart_softc *);
static int vf_uart_bus_receive(struct uart_softc *);
static int vf_uart_bus_setsig(struct uart_softc *, int);
static int vf_uart_bus_transmit(struct uart_softc *);

static kobj_method_t vf_uart_methods[] = {
        KOBJMETHOD(uart_attach,         vf_uart_bus_attach),
        KOBJMETHOD(uart_detach,         vf_uart_bus_detach),
        KOBJMETHOD(uart_flush,          vf_uart_bus_flush),
        KOBJMETHOD(uart_getsig,         vf_uart_bus_getsig),
        KOBJMETHOD(uart_ioctl,          vf_uart_bus_ioctl),
        KOBJMETHOD(uart_ipend,          vf_uart_bus_ipend),
        KOBJMETHOD(uart_param,          vf_uart_bus_param),
        KOBJMETHOD(uart_probe,          vf_uart_bus_probe),
        KOBJMETHOD(uart_receive,        vf_uart_bus_receive),
        KOBJMETHOD(uart_setsig,         vf_uart_bus_setsig),
        KOBJMETHOD(uart_transmit,       vf_uart_bus_transmit),
        { 0, 0 }
};

static struct uart_class uart_vybrid_class = {
        "vybrid",
        vf_uart_methods,
        sizeof(struct vf_uart_softc),
        .uc_ops = &uart_vybrid_ops,
        .uc_range = 0x100,
        .uc_rclk = 24000000, /* TODO: get value from CCM */
        .uc_rshift = 0
};

static struct ofw_compat_data compat_data[] = {
        {"fsl,mvf600-uart",     (uintptr_t)&uart_vybrid_class},
        {NULL,                  (uintptr_t)NULL},
};
UART_FDT_CLASS_AND_DEVICE(compat_data);

static int
vf_uart_bus_attach(struct uart_softc *sc)
{
        struct uart_bas *bas;
        int reg;

        bas = &sc->sc_bas;

        sc->sc_hwiflow = 0;
        sc->sc_hwoflow = 0;

        uart_reinit(sc, 66000000, 115200);

        reg = uart_getreg(bas, UART_C2);
        if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
                reg &= ~UART_C2_RIE;
        } else {
                reg |= UART_C2_RIE;
        }
        uart_setreg(bas, UART_C2, reg);

        return (0);
}

static int
vf_uart_bus_detach(struct uart_softc *sc)
{

        /* TODO */
        return (0);
}

static int
vf_uart_bus_flush(struct uart_softc *sc, int what)
{

        /* TODO */
        return (0);
}

static int
vf_uart_bus_getsig(struct uart_softc *sc)
{

        /* TODO */
        return (0);
}

static int
vf_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
{
        int error;

        error = 0;
        uart_lock(sc->sc_hwmtx);
        switch (request) {
        case UART_IOCTL_BREAK:
        /* TODO */
                break;
        case UART_IOCTL_BAUD:
        /* TODO */
                *(int*)data = 115200;
                break;
        default:
                error = EINVAL;
                break;
        }
        uart_unlock(sc->sc_hwmtx);

        return (error);
}

static int
vf_uart_bus_ipend(struct uart_softc *sc)
{
        struct uart_bas *bas;
        int ipend;
        uint32_t usr1, usr2;
        int reg;

        bas = &sc->sc_bas;
        ipend = 0;

        uart_lock(sc->sc_hwmtx);

        usr1 = uart_getreg(bas, UART_S1);
        usr2 = uart_getreg(bas, UART_S2);
        (void)uart_getreg(bas, UART_SFIFO);

        /* ack usr2 */
        uart_setreg(bas, UART_S2, usr2);

        if (usr1 & UART_S1_TDRE) {
                reg = uart_getreg(bas, UART_C2);
                reg &= ~(UART_C2_TIE);
                uart_setreg(bas, UART_C2, reg);

                if (sc->sc_txbusy != 0) {
                        ipend |= SER_INT_TXIDLE;
                }
        }

        if (usr1 & UART_S1_RDRF) {
                reg = uart_getreg(bas, UART_C2);
                reg &= ~(UART_C2_RIE);
                uart_setreg(bas, UART_C2, reg);

                ipend |= SER_INT_RXREADY;
        }

        if (usr2 & UART_S2_LBKDIF) {
                ipend |= SER_INT_BREAK;
        }

        uart_unlock(sc->sc_hwmtx);

        return (ipend);
}

static int
vf_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
    int stopbits, int parity)
{

        uart_lock(sc->sc_hwmtx);
        vf_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity);
        uart_unlock(sc->sc_hwmtx);

        return (0);
}

static int
vf_uart_bus_probe(struct uart_softc *sc)
{
        int error;

        error = vf_uart_probe(&sc->sc_bas);
        if (error)
                return (error);

        sc->sc_rxfifosz = 1;
        sc->sc_txfifosz = 1;

        device_set_desc(sc->sc_dev, "Vybrid Family UART");
        return (0);
}

static int
vf_uart_bus_receive(struct uart_softc *sc)
{
        struct uart_bas *bas;
        int reg;
        int c;

        bas = &sc->sc_bas;
        uart_lock(sc->sc_hwmtx);

        /* Read FIFO */
        while (uart_getreg(bas, UART_S1) & UART_S1_RDRF) {
                if (uart_rx_full(sc)) {
                /* No space left in input buffer */
                        sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
                        break;
                }

                c = uart_getreg(bas, UART_D);
                uart_rx_put(sc, c);
        }

        /* Reenable Data Ready interrupt */
        reg = uart_getreg(bas, UART_C2);
        reg |= (UART_C2_RIE);
        uart_setreg(bas, UART_C2, reg);

        uart_unlock(sc->sc_hwmtx);
        return (0);
}

static int
vf_uart_bus_setsig(struct uart_softc *sc, int sig)
{
        struct uart_bas *bas;
        int reg;

        /* TODO: implement (?) */

        /* XXX workaround to have working console on mount prompt */
        /* Enable RX interrupt */
        bas = &sc->sc_bas;
        if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
                reg = uart_getreg(bas, UART_C2);
                reg |= (UART_C2_RIE);
                uart_setreg(bas, UART_C2, reg);
        }

        return (0);
}

static int
vf_uart_bus_transmit(struct uart_softc *sc)
{
        struct uart_bas *bas = &sc->sc_bas;
        int i;
        int reg;

        bas = &sc->sc_bas;
        uart_lock(sc->sc_hwmtx);

        /* Fill TX FIFO */
        for (i = 0; i < sc->sc_txdatasz; i++) {
                uart_setreg(bas, UART_D, sc->sc_txbuf[i] & 0xff);
                uart_barrier(&sc->sc_bas);
        }

        sc->sc_txbusy = 1;

        /* Call me when ready */
        reg = uart_getreg(bas, UART_C2);
        reg |= (UART_C2_TIE);
        uart_setreg(bas, UART_C2, reg);

        uart_unlock(sc->sc_hwmtx);

        return (0);
}