blob: 749a6e55bc49ba5ef998cfd3c2d6805f57fdfc74 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (C) 2017 Marvell International Ltd.
* 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 <assert.h>
#include <drivers/mvebu_uart.h>
#include <io.h>
#include <keep.h>
#include <kernel/dt.h>
#include <stdlib.h>
#include <trace.h>
#include <types_ext.h>
#include <util.h>
/* MVEBU UART Registers */
#define UART_RX_REG 0x00
#define UART_TX_REG 0x04
#define UART_CTRL_REG 0x08
#define UART_STATUS_REG 0x0c
#define UART_BAUD_REG 0x10
#define UART_POSSR_REG 0x14
/* Line Status Register bits */
#define UARTLSR_TXFIFOFULL (1 << 11) /* Tx Fifo Full */
#define UARTLSR_TXFIFOEMPTY (1 << 13)
#define UARTLSR_TXEMPTY (1 << 6)
#define UART_RX_READY (1 << 4)
/* UART Control Register bits */
#define UART_CTRL_RXFIFO_RESET (1 << 14)
#define UART_CTRL_TXFIFO_RESET (1 << 15)
static vaddr_t chip_to_base(struct serial_chip *chip)
{
struct mvebu_uart_data *pd =
container_of(chip, struct mvebu_uart_data, chip);
return io_pa_or_va(&pd->base);
}
static void mvebu_uart_flush(struct serial_chip *chip)
{
vaddr_t base = chip_to_base(chip);
/*
* Wait for the transmit FIFO to be empty.
* It can happen that Linux initializes the OP-TEE driver with the
* console UART disabled; avoid an infinite loop by checking the UART
* enabled flag. Checking it in the loop makes the code safe against
* asynchronous disable.
*/
while (!(io_read32(base + UART_STATUS_REG) & UARTLSR_TXFIFOEMPTY))
;
}
static bool mvebu_uart_have_rx_data(struct serial_chip *chip)
{
vaddr_t base = chip_to_base(chip);
return (io_read32(base + UART_STATUS_REG) & UART_RX_READY);
}
static int mvebu_uart_getchar(struct serial_chip *chip)
{
vaddr_t base = chip_to_base(chip);
while (!mvebu_uart_have_rx_data(chip))
;
return io_read32(base + UART_RX_REG) & 0xff;
}
static void mvebu_uart_putc(struct serial_chip *chip, int ch)
{
vaddr_t base = chip_to_base(chip);
uint32_t tmp;
/* wait for space in tx FIFO */
do {
tmp = io_read32(base + UART_STATUS_REG);
tmp &= UARTLSR_TXFIFOFULL;
} while (tmp == UARTLSR_TXFIFOFULL);
io_write32(base + UART_TX_REG, ch);
}
static const struct serial_ops mvebu_uart_ops = {
.flush = mvebu_uart_flush,
.getchar = mvebu_uart_getchar,
.have_rx_data = mvebu_uart_have_rx_data,
.putc = mvebu_uart_putc,
};
KEEP_PAGER(mvebu_uart_ops);
void mvebu_uart_init(struct mvebu_uart_data *pd, paddr_t pbase,
uint32_t uart_clk, uint32_t baud_rate)
{
vaddr_t base;
uint32_t dll = 0;
pd->base.pa = pbase;
pd->chip.ops = &mvebu_uart_ops;
base = io_pa_or_va(&pd->base);
dll = (uart_clk / (baud_rate << 4)) & 0x3FF;
/* init UART */
io_clrsetbits32(base + UART_BAUD_REG, 0x3FF, dll);
/* set UART to default 16x scheme */
io_write32(base + UART_POSSR_REG, 0);
/* reset FIFO */
io_write32(base + UART_CTRL_REG,
UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET);
/* No Parity, 1 stop */
io_write32(base + UART_CTRL_REG, 0);
mvebu_uart_flush(&pd->chip);
}