blob: 825a276bf0ae581b0a2f685e6cbcf1c539e12640 [file] [log] [blame]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <stdlib.h>
#include <platsupport/serial.h>
#include <platsupport/plat/serial.h>
#include <string.h>
#include <utils/fence.h>
#include "../../chardev.h"
#define UART_BYTE_MASK 0xff
#define NV_UART_INPUT_CLOCK_FREQ_HZ (408000000)
#define LCR_DLAB BIT(7)
#define LCR_SET_BREAK BIT(6)
#define LCR_SET_PARITY BIT(5) /* Force parity to value of BIT(4) */
#define LCR_EVEN BIT(4) /* even parity? */
#define LCR_PAR BIT(3) /* send parity? */
#define LCR_STOP BIT(2) /* transmit 1 (0) or 2 (1) stop bits */
#define LCR_WD_SIZE_5 0
#define LCR_WD_SIZE_6 1
#define LCR_WD_SIZE_7 2
#define LCR_WD_SIZE_8 3
#define LSR_THRE_EMPTY (BIT(5))
#define LSR_RDR_READY BIT(0)
#define IER_RHR_ENABLE BIT(0)
#define IER_THR_EMPTY_ENABLE BIT(1)
#define IER_RX_FIFO_TIMEDOUT BIT(4)
#define IER_EO_RECEIVE_DATA BIT(5)
/* IIR is read-only, FCR is write-only. Both share the same address mapping.
* IIR is automatically selected when you do a read-access.
* FCR is automatically selected when you do a write-access
*/
#define FCR_FIFO_ENABLE BIT(0)
#define FCR_DMA_MODE0 (0)
#define FCR_DMA_MODE1 BIT(3)
#define FCR_RX_TRIG_SHIFT (6)
#define FCR_RX_TRIG_MASK (0x3)
#define FCR_RX_TRIG_FIFO_GT_1 (0)
#define FCR_RX_TRIG_FIFO_GT_4 (1)
#define FCR_RX_TRIG_FIFO_GT_8 (2)
#define FCR_RX_TRIG_FIFO_GT_16 (3)
#define FCR_TX_TRIG_SHIFT (4)
#define FCR_TX_TRIG_MASK (0x3)
#define FCR_TX_TRIG_FIFO_GT_1 (3)
#define FCR_TX_TRIG_FIFO_GT_4 (2)
#define FCR_TX_TRIG_FIFO_GT_8 (1)
#define FCR_TX_TRIG_FIFO_GT_16 (0)
#define FCR_TX_FIFO_CLEAR_SHIFT (2)
#define FCR_RX_FIFO_CLEAR_SHIFT (1)
#define IIR_FIFO_MODE_STATUS_SHIFT (6)
#define IIR_FIFO_MODE_STATUS_MASK (0x3)
#define IIR_INT_PENDING BIT(0)
#define IIR_INT_SOURCE_SHIFT (0)
#define IIR_INT_SOURCE_MASK (0xF)
enum iir_int_source {
IIR_INT_SOURCE_NO_INTERRUPT = 0x1,
IIR_INT_SOURCE_DATA_ERROR = 0x6,
IIR_INT_SOURCE_RECEIVED_DATA_AVAILABLE = 0x4,
IIR_INT_SOURCE_RECEIVED_DATA_TIMEOUT = 0xC,
IIR_INT_SOURCE_EO_RECEIVED_DATA = 0x8,
IIR_INT_SOURCE_THR_TXRDY = 0x2,
IIR_INT_SOURCE_MODEM_STATUS = 0x0
};
#define EXTRACT_BITS(bf,shift,mask) (((bf) >> (shift)) & (mask))
#define ENCODE_BITS(bf,shift,mask,v) (((bf) & ~(((mask) << (shift)))) \
| (((v) & (mask)) << (shift)))
struct tk1_uart_regs {
uint32_t thr_dlab; /* 0x0: tx holding register */
uint32_t ier_dlab; /* 0x4: IER and DLH registers */
uint32_t iir_fcr; /* 0x8: FIFO control; interrupt identification */
uint32_t lcr; /* 0xc: line control */
uint32_t mcr; /* 0x10: modem control */
uint32_t lsr; /* 0x14: line status */
uint32_t msr; /* 0x18: modem status */
uint32_t spr; /* 0x1c: scratchpad */
uint32_t csr; /* 0x20: IrDA pulse coding */
uint32_t rx_fifo_cfg;/* 0x24: */
uint32_t mie; /* 0x28: modem interrupt enable */
uint32_t asr; /* 0x3c: auto sense baud */
};
typedef volatile struct tk1_uart_regs tk1_uart_regs_t;
static inline tk1_uart_regs_t*
tk1_uart_get_priv(ps_chardevice_t *d)
{
return (tk1_uart_regs_t*)d->vaddr;
}
static inline bool
tk1_uart_is_async(ps_chardevice_t *d)
{
/* NV_UART[ABCD] are polling, while NV_UART[ABCD]_ASYNC are asynchronous
* versions of the first 4 devices.
*/
return (d->id >= NV_UARTA_ASYNC);
}
static inline void
tk1_uart_set_thr_irq(tk1_uart_regs_t *regs, bool enable)
{
uint32_t ier;
ier = regs->ier_dlab;
if (enable) {
ier |= IER_THR_EMPTY_ENABLE;
} else {
ier &= ~IER_THR_EMPTY_ENABLE;
}
regs->ier_dlab = ier;
}
static inline void
tk1_uart_set_rbr_irq(tk1_uart_regs_t *regs, bool enable)
{
uint32_t ier;
ier = regs->ier_dlab;
if (enable) {
ier |= IER_RHR_ENABLE;
ier |= IER_RX_FIFO_TIMEDOUT;
} else {
ier &= ~IER_RHR_ENABLE;
ier &= ~IER_RX_FIFO_TIMEDOUT;
}
regs->ier_dlab = ier;
}
int uart_getchar(ps_chardevice_t *d)
{
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
uint32_t reg = 0;
int c = -1;
if (regs->lsr & LSR_RDR_READY) {
reg = regs->thr_dlab;
c = reg & UART_BYTE_MASK;
}
return c;
}
int uart_putchar(ps_chardevice_t* d, int c)
{
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
uint32_t lsr = regs->lsr;
if (((lsr & LSR_THRE_EMPTY) == LSR_THRE_EMPTY)) {
if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) {
uart_putchar(d, '\r');
}
regs->thr_dlab = (uint8_t) c;
return c;
} else {
return -1;
}
}
static void
tk1_uart_invoke_callback(ps_chardevice_t *d, enum chardev_status stat,
size_t n_bytes_xferred,
bool invoke_read, bool invoke_write)
{
tk1_uart_regs_t *regs = tk1_uart_get_priv(d);
if (!invoke_read && !invoke_write) {
ZF_LOGW("invoke_cb called with no indication of what should be done.");
return;
}
if (invoke_read) {
if (d->read_descriptor.callback != NULL
&& d->read_descriptor.bytes_requested > 0) {
d->read_descriptor.callback(d, stat,
n_bytes_xferred,
d->read_descriptor.token);
/* If the client set bytes_requested to 0 inside of the callback,
* that is a signal to us that we should close any asynchronous
* reading from the line.
*/
if (d->read_descriptor.bytes_requested == 0) {
tk1_uart_set_rbr_irq(regs, false);
}
}
}
if (invoke_write) {
if (d->write_descriptor.callback != NULL
&& d->write_descriptor.bytes_requested > 0) {
d->write_descriptor.callback(d, stat,
n_bytes_xferred,
d->write_descriptor.token);
/* If the client set bytes_requested to 0 inside of the callback,
* that is a signal to us that we should consider the async write
* to be closed.
*/
if (d->write_descriptor.bytes_requested == 0) {
tk1_uart_set_thr_irq(regs, false);
}
}
}
}
static void
uart_handle_irq(ps_chardevice_t* d)
{
tk1_uart_regs_t *regs = tk1_uart_get_priv(d);
uintptr_t irq_ident_val;
/* Determine the cause of the IRQ. */
irq_ident_val = regs->iir_fcr;
irq_ident_val &= IIR_INT_SOURCE_MASK;
switch (irq_ident_val) {
case IIR_INT_SOURCE_NO_INTERRUPT:
break;
case IIR_INT_SOURCE_DATA_ERROR:
ZF_LOGE("Parity, overrun, or framing error.");
if (d->read_descriptor.data != NULL) {
tk1_uart_invoke_callback(d, CHARDEV_STAT_ERROR, 0, true, false);
}
break;
case IIR_INT_SOURCE_RECEIVED_DATA_TIMEOUT:
case IIR_INT_SOURCE_EO_RECEIVED_DATA:
case IIR_INT_SOURCE_RECEIVED_DATA_AVAILABLE: {
uint8_t *client_buff;
int c;
struct chardev_xmit_descriptor *rd = &d->read_descriptor;
if (irq_ident_val == IIR_INT_SOURCE_EO_RECEIVED_DATA) {
/* Tegra K1 Mobile Processor TRM, section 23.4.2:
*
* "EORD (End of Receive Data) Interrupt occurs when the receiver
* detects that data stops coming in for more than 4 character
* times. This interrupt is useful for determining that the sending
* device has completed sending all its data. EORD timeout will not
* occur if the receiving data stream is stopped because of hardware
* handshaking."
*
* "To clear the EORD timeout interrupt you must DISABLE the EORD
* interrupt enable (IE_EORD)."
*
* But I assume I have to re-enable it too, because otherwise I
* won't get them anymore.
*/
ZF_LOGV("Int reason EO received data.");
regs->ier_dlab &= ~IER_EO_RECEIVE_DATA;
regs->ier_dlab |= IER_EO_RECEIVE_DATA;
}
if (rd->data == NULL || rd->bytes_requested == 0) {
/* Even if there is no rx buffer, or no bytes have been requested,
* or some other unusual case has been triggered, we should read the
* RBR register and consume the bytes in it.
*/
ZF_LOGW("Draining.");
while (uart_getchar(d) != -1) {
/* Just read the bytes out to clear the FIFO */
}
break;
}
/* So everytime a new RX data IRQ comes in, the buffer cursor gets
* reset to the beginning of the client-supplied buffer.
*
* Therefore the client should try to service IRQs as quickly as possible.
*/
client_buff = rd->data;
c = uart_getchar(d);
while (c != -1) {
/* Don't overrun the client-supplied buffer */
if (rd->bytes_transfered >= rd->bytes_requested) {
ZF_LOGV("Buffer of %dB will be overrun.", rd->bytes_requested);
tk1_uart_invoke_callback(d, CHARDEV_STAT_INCOMPLETE,
rd->bytes_transfered,
true, false);
/* We use bytes_requested as a flag to indicate to the this IRQ
* handler that it shouldn't call the callback again.
* If bytes_requested is 0, we won't get here, so this callback
* won't be called repeatedly if there's more data than the
* caller's buffer can hold.
*/
rd->bytes_requested = 0;
break;
}
client_buff[rd->bytes_transfered] = (char)c;
rd->bytes_transfered++;
c = uart_getchar(d);
}
/* If the loop exits early because the buffer was overrun, "c" will
* not be -1, because the UART would have returned a character. We
* just didn't have any buffer memory remaining.
*
* I.e, c will be -1 if the loop exited normally.
*/
if (c == -1 && rd->bytes_transfered > 0) {
tk1_uart_invoke_callback(d, CHARDEV_STAT_COMPLETE,
rd->bytes_transfered,
true, false);
}
break;
}
case IIR_INT_SOURCE_THR_TXRDY:
ZF_LOGV("Int reason THR ready: %d of %d bytes transferred.",
d->write_descriptor.bytes_transfered,
d->write_descriptor.bytes_requested);
if (d->write_descriptor.data != NULL) {
struct chardev_xmit_descriptor *wd = &d->write_descriptor;
uint8_t *client_data;
int status;
client_data = wd->data;
while (wd->bytes_transfered < wd->bytes_requested) {
status = uart_putchar(d, client_data[wd->bytes_transfered]);
if (status == -1) {
ZF_LOGV("One DMA pass finished: written %d of %dB!",
wd->bytes_transfered, wd->bytes_requested);
break;
}
wd->bytes_transfered++;
}
if (wd->bytes_transfered >= wd->bytes_requested) {
/* Disable the THR_EMPTY IRQ until a new write request is made. */
tk1_uart_set_thr_irq(regs, false);
tk1_uart_invoke_callback(d, CHARDEV_STAT_COMPLETE,
wd->bytes_transfered,
false, true);
}
} else {
/* If there's no input data buffer to read from, disable the
* THR_EMPTY IRQ, because it was triggered, for one reason or
* another.
*/
tk1_uart_set_thr_irq(regs, false);
}
break;
case IIR_INT_SOURCE_MODEM_STATUS:
ZF_LOGV("Modem status changed.");
break;
default:
ZF_LOGW("Unknown interrupt reason %d.", irq_ident_val);
};
}
static ssize_t
tk1_uart_write(ps_chardevice_t* d, const void* vdata,
size_t count, chardev_callback_t rcb,
void* token)
{
struct chardev_xmit_descriptor wd = {
.callback = rcb,
.token = token,
.bytes_transfered = 0,
.bytes_requested = count,
.data = (void *)vdata
};
d->write_descriptor = wd;
if (count == 0) {
/* Call the callback immediately */
if (rcb != NULL) {
rcb(d, CHARDEV_STAT_COMPLETE, count, token);
}
return 0;
}
if (!tk1_uart_is_async(d)) {
/* Write the data out over the line synchronously. */
for (int i = 0; i < count; i++) {
while (uart_putchar(d, ((uint8_t *)vdata)[i]) == -1) {
}
d->write_descriptor.bytes_transfered++;
}
if (rcb != NULL) {
rcb(d, CHARDEV_STAT_COMPLETE, d->write_descriptor.bytes_transfered,
token);
}
} else {
/* Else enable the THRE IRQ and return. */
tk1_uart_set_thr_irq(tk1_uart_get_priv(d), true);
THREAD_MEMORY_RELEASE();
}
return d->write_descriptor.bytes_transfered;
}
static ssize_t
tk1_uart_read(ps_chardevice_t* d, void* vdata,
size_t count, chardev_callback_t rcb,
void* token)
{
if (count < 1) {
count = 0;
}
struct chardev_xmit_descriptor rd = {
.callback = rcb,
.token = token,
.bytes_transfered = 0,
.bytes_requested = count,
.data = vdata
};
d->read_descriptor = rd;
if (count == 0 && rcb != NULL) {
ZF_LOGV("read call with 0 count.");
rcb(d, CHARDEV_STAT_COMPLETE, count, token);
}
if (!tk1_uart_is_async(d)) {
int n_chars_read = 0;
int c;
while ((c = uart_getchar(d)) == -1) {
/* Ideally we should use a cpu_relax() type of opcode here. */
}
/* Read the data synchronously. */
while (c != -1) {
((uint8_t *)vdata)[n_chars_read] = c;
c = uart_getchar(d);
n_chars_read++;
}
d->read_descriptor.bytes_transfered = n_chars_read;
if (rcb != NULL) {
rcb(d, CHARDEV_STAT_COMPLETE, d->read_descriptor.bytes_transfered,
token);
}
}
return d->read_descriptor.bytes_transfered;
}
/** Used for debugging. Concats the dlab hi and lo bytes into a 16-bit int.
* @param d Pointer to the device whose divisor you want to get.
* @return 16-bit divisor.
*/
UNUSED static uint16_t
tk1_uart_get_dlab_divisor(ps_chardevice_t *d)
{
uint16_t ret=0;
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
regs->lcr |= LCR_DLAB;
THREAD_MEMORY_RELEASE();
ret = regs->thr_dlab & 0xFF;
ret |= (regs->ier_dlab & 0xFF) << 8;
THREAD_MEMORY_RELEASE();
regs->lcr &= ~LCR_DLAB;
return ret;
}
static void
tk1_uart_set_dlab_divisor(ps_chardevice_t *d, uint16_t divisor)
{
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
regs->lcr |= LCR_DLAB;
THREAD_MEMORY_RELEASE();
regs->thr_dlab = divisor & 0xFF;
regs->ier_dlab = (divisor >> 8) & 0xFF;
THREAD_MEMORY_RELEASE();
regs->lcr &= ~LCR_DLAB;
}
static int
tk1_uart_get_divisor_for(int baud)
{
int ret;
switch (baud) {
case 115200:
case 57600:
case 38400:
case 19200:
case 9600:
case 4800:
case 2400:
case 1200:
case 300:
/* Do nothing; This switch is for input validation. */
break;
default:
ZF_LOGE("TK1-uart: Unsupported baud rate %d.",
baud);
return -1;
}
/* Both we an u-boot program the UARTs to use PllP_out as their input clock,
* which is fixed at 408MHz:
*
* TegraK1 TRM, Section 5.22, Table 14:
* "pllP_out: This is the PLLP’s output clock which is set to 408 MHz."
*
* This 408MHz output, is then channeled into the UART-controllers after
* being passed through a divider. The divider's default divisor is 17
* on hardware #RESET, but u-boot sets the divider's divisor to 0, and so
* the UART controller gets the full 408 MHz as its input.
*
* From there, we just calculate a divisor to put into the UART controller's
* DLAB (divisor latch) registers, based on the caller's requested baud
* rate, according to the formula:
* Divisor = input_clock_freq / (16 * desired_baud)
*
* The number "16" comes from the fact that the UART controller takes 16
* clock phases to generate one bit of output on the line (TK1 TRM, section
* 34.1.1.)
*/
ret = (NV_UART_INPUT_CLOCK_FREQ_HZ / 16) / baud;
return ret;
}
int
serial_configure(ps_chardevice_t* d, long bps, int char_size, enum serial_parity parity, int stop_bits)
{
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
int divisor;
/* line control register */
uint32_t lcr = 0;
/* Disable the receive IRQ while changing line configuration. */
tk1_uart_set_rbr_irq(regs, false);
THREAD_MEMORY_RELEASE();
switch (char_size) {
case 5:
lcr |= LCR_WD_SIZE_5;
break;
case 6:
lcr |= LCR_WD_SIZE_6;
break;
case 7:
lcr |= LCR_WD_SIZE_7;
break;
case 8:
lcr |= LCR_WD_SIZE_8;
break;
default:
return -1;
}
switch (parity) {
case PARITY_NONE:
break;
case PARITY_EVEN:
lcr |= LCR_EVEN | LCR_PAR;
break;
case PARITY_ODD:
lcr |= LCR_PAR;
break;
/*
* Uncomment if we ever need fixed values for parity bits
*
case PARITY_ONE:
lcr |= LCR_SET_PARITY | LCR_EVEN;
break;
case PARITY_ZERO:
lcr |= LCR_SET_PARITY;
break
*/
default:
return -1;
}
/* one stop bit */
regs->lcr = lcr;
divisor = tk1_uart_get_divisor_for(bps);
if (divisor < 1) {
/* Unsupported baud rate. */
return -1;
}
tk1_uart_set_dlab_divisor(d, divisor);
/* Disable hardware flow control for all UARTs other than UARTD,
* because UARTD actually has an RS232 pinout port.
*/
if (d->id != NV_UARTD && d->id != NV_UARTD_ASYNC) {
uint32_t mcr = regs->mcr;
/* Clear RTS_EN and CTS_EN. */
mcr &= ~(BIT(6) | BIT(5));
/* Force RTS and DTR to low (active) */
mcr |= (BIT(1) | BIT(0));
regs->mcr = mcr;
}
/* Drain the RX buffer when configuring a new set of line options.
* By implication, the caller should wait for previous transmissions to be
* completed before reconfiguring.
*/
while (uart_getchar(d) != -1) {
}
if (tk1_uart_is_async(d)) {
/* Re-enable receive IRQ. */
tk1_uart_set_rbr_irq(regs, true);
}
return 0;
}
/** Initialize a ps_chardevice_t instance.
*
* Expects an already valid mapping to the TK1 UART MMIO vaddr range.
*/
int
tk1_uart_init_common(const struct dev_defn *defn, void *const uart_mmio_vaddr,
ps_chardevice_t *dev)
{
volatile void *uart_vaddr = 0;
tk1_uart_regs_t* regs;
uint32_t iir_fcr;
struct chardev_xmit_descriptor cxd_zero = {0};
ps_io_ops_t ioops_zero = {{0}};
/* add offsets properly */
switch (defn->id) {
case NV_UARTA:
case NV_UARTA_ASYNC:
uart_vaddr = uart_mmio_vaddr;
break;
case NV_UARTB:
case NV_UARTB_ASYNC:
uart_vaddr = uart_mmio_vaddr + UARTB_OFFSET;
break;
case NV_UARTC:
case NV_UARTC_ASYNC:
uart_vaddr = uart_mmio_vaddr + UARTC_OFFSET;
break;
case NV_UARTD:
case NV_UARTD_ASYNC:
uart_vaddr = uart_mmio_vaddr + UARTD_OFFSET;
/* The kernel uses UART-D. Recommend not conflicting with it. */
break;
default:
return -1;
}
memset(dev, 0, sizeof(*dev));
/* Set up all the device properties. */
dev->id = defn->id;
dev->vaddr = (void*)uart_vaddr;
dev->read = &tk1_uart_read;
dev->write = &tk1_uart_write;
dev->handle_irq = &uart_handle_irq;
dev->irqs = defn->irqs;
dev->ioops = ioops_zero;
dev->flags = SERIAL_AUTO_CR;
/* Zero out the client state. */
dev->write_descriptor = cxd_zero;
dev->read_descriptor = cxd_zero;
regs = tk1_uart_get_priv(dev);
/* Disable IRQs. */
tk1_uart_set_rbr_irq(regs, false);
tk1_uart_set_thr_irq(regs, false);
/* Line configuration */
serial_configure(dev, 115200, 8, PARITY_NONE, 1);
/* Set FCR[0] to 1 to enable FIFO mode, and enable DMA mode 1 which will
* generate an interrupt only when the buffer has.
*
* There's no point in doing an R-M-W sequence because reading the FCR
* actually returns the values from the IIR, so you can't actually read FCR.
*/
iir_fcr = 0
| FCR_FIFO_ENABLE | FCR_DMA_MODE0
| ENCODE_BITS(0, FCR_RX_TRIG_SHIFT, FCR_RX_TRIG_MASK, FCR_RX_TRIG_FIFO_GT_16)
| ENCODE_BITS(0, FCR_TX_TRIG_SHIFT, FCR_TX_TRIG_MASK, FCR_TX_TRIG_FIFO_GT_16)
| BIT(FCR_TX_FIFO_CLEAR_SHIFT)
| BIT(FCR_RX_FIFO_CLEAR_SHIFT);
regs->iir_fcr = iir_fcr;
/* Read the status bit to ensure the FIFO was enabled. */
iir_fcr = regs->iir_fcr;
if (EXTRACT_BITS(iir_fcr,
IIR_FIFO_MODE_STATUS_SHIFT,
IIR_FIFO_MODE_STATUS_MASK) != 3) {
ZF_LOGE("FIFO mode wasn't enabled.\n");
return -1;
}
return 0;
}
/** Initializes a ps_chardevice_t.
*
* Expects a viable ps_io_ops_t for mapping the registers.
*/
int
uart_init(const struct dev_defn* defn,
const ps_io_ops_t* ops,
ps_chardevice_t* dev)
{
static void *vaddr = 0;
int ret;
/* Attempt to map the virtual address, assure this works */
if (vaddr == 0) {
vaddr = chardev_map(defn, ops);
if (vaddr == NULL) {
return -1;
}
}
ret = tk1_uart_init_common(defn, vaddr, dev);
dev->ioops = *ops;
return ret;
}