| /* |
| * Copyright 2019, Data61 |
| * Commonwealth Scientific and Industrial Research Organisation (CSIRO) |
| * ABN 41 687 119 230. |
| * |
| * This software may be distributed and modified according to the terms of |
| * the BSD 2-Clause license. Note that NO WARRANTY is provided. |
| * See "LICENSE_BSD2.txt" for details. |
| * |
| * @TAG(DATA61_BSD) |
| */ |
| |
| /* $esc:(#include <autoconf.h>) */ |
| |
| $esc:(#ifndef CONFIG_PLAT_KZM) |
| |
| $esc:(#include <stdint.h>) |
| $esc:(#include <stdlib.h>) |
| $esc:(#include <string.h>) |
| $esc:(#include "platsupport/serial.h") |
| $esc:(#include "platsupport/plat/serial.h") |
| |
| $esc:(#include "chardev.h") |
| |
| $esc:(#ifndef UART_REF_CLK) |
| $esc:(#error "UART_REF_CLK undefined") |
| $esc:(#endif) |
| |
| |
| /* Cogent types */ |
| typedef struct _Machine SysState; |
| |
| /* The global state */ |
| struct _Machine { |
| void *priv; |
| ps_chardevice_t *dev; |
| }; |
| |
| |
| #include "generated.c" |
| |
| struct imx_uart_regs { |
| uint32_t rxd; /* 0x000 Receiver Register */ |
| uint32_t res0[15]; |
| uint32_t txd; /* 0x040 Transmitter Register */ |
| uint32_t res1[15]; |
| uint32_t cr1; /* 0x080 Control Register 1 */ |
| uint32_t cr2; /* 0x084 Control Register 2 */ |
| uint32_t cr3; /* 0x088 Control Register 3 */ |
| uint32_t cr4; /* 0x08C Control Register 4 */ |
| uint32_t fcr; /* 0x090 FIFO Control Register */ |
| uint32_t sr1; /* 0x094 Status Register 1 */ |
| uint32_t sr2; /* 0x098 Status Register 2 */ |
| uint32_t esc; /* 0x09c Escape Character Register */ |
| uint32_t tim; /* 0x0a0 Escape Timer Register */ |
| uint32_t bir; /* 0x0a4 BRM Incremental Register */ |
| uint32_t bmr; /* 0x0a8 BRM Modulator Register */ |
| uint32_t brc; /* 0x0ac Baud Rate Counter Register */ |
| uint32_t onems; /* 0x0b0 One Millisecond Register */ |
| uint32_t ts; /* 0x0b4 Test Register */ |
| }; |
| typedef volatile struct imx_uart_regs imx_uart_regs_t; |
| |
| static inline imx_uart_regs_t *imx_uart_get_priv(ps_chardevice_t *d) |
| { |
| return (imx_uart_regs_t *)d->vaddr; |
| } |
| |
| |
| /** |
| * Register readers and writers. |
| */ |
| #define REG_RW_FN(REG) \ |
| $ty:((U32, SysState)) $id:(imx_uart_regs_read_##REG)($ty:(SysState) st) \ |
| { \ |
| imx_uart_regs_t *regs; \ |
| $ty:((U32, SysState)) ret; \ |
| \ |
| regs = imx_uart_get_priv(st->dev); \ |
| ret.p1 = regs->REG; \ |
| ret.p2 = st; \ |
| \ |
| return ret; \ |
| } \ |
| \ |
| $ty:((SysState)) imx_uart_regs_write_##REG($ty:((U32, SysState)) args) \ |
| { \ |
| $ty:(SysState) st = args.p2; \ |
| imx_uart_regs_t *regs; \ |
| \ |
| regs = imx_uart_get_priv(st->dev); \ |
| regs->REG = args.p1; \ |
| \ |
| return st; \ |
| } |
| |
| REG_RW_FN(rxd) |
| REG_RW_FN(txd) |
| REG_RW_FN(cr1) |
| REG_RW_FN(cr2) |
| REG_RW_FN(cr3) |
| REG_RW_FN(cr4) |
| REG_RW_FN(fcr) |
| REG_RW_FN(sr1) |
| REG_RW_FN(sr2) |
| REG_RW_FN(esc) |
| REG_RW_FN(tim) |
| REG_RW_FN(bir) |
| REG_RW_FN(bmr) |
| REG_RW_FN(brc) |
| REG_RW_FN(onems) |
| REG_RW_FN(ts) |
| |
| static void uart_handle_irq(ps_chardevice_t* d) |
| { |
| /* TODO */ |
| } |
| |
| int uart_getchar(ps_chardevice_t* d) |
| { |
| $ty:((SysState, U32)) ret; |
| SysState st; |
| uint32_t reg = 0; |
| int c = -1; |
| |
| st.dev = d; |
| ret = uart_getchar_cg(&st); |
| |
| c = ret.p2; |
| |
| return c; |
| } |
| |
| int uart_putchar(ps_chardevice_t* d, int c) |
| { |
| imx_uart_regs_t* regs = imx_uart_get_priv(d); |
| if (regs->sr2 & UART_SR2_TXFIFO_EMPTY) { |
| if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) { |
| uart_putchar(d, '\r'); |
| } |
| regs->txd = c; |
| return c; |
| } else { |
| return -1; |
| } |
| } |
| |
| $ty:((SysState)) uart_setup_phase1_ac($ty:((SysState)) state) |
| { |
| imx_uart_regs_t* regs = imx_uart_get_priv(state->dev); |
| |
| while (!(regs->cr2 & UART_CR2_SRST)); |
| |
| return state; |
| } |
| |
| int uart_init(const struct dev_defn *defn, |
| const ps_io_ops_t *ops, |
| ps_chardevice_t *dev) |
| { |
| imx_uart_regs_t* regs; |
| SysState st; |
| $ty:((SysState)) ret; |
| |
| /* Attempt to map the virtual address, assure this works */ |
| void* vaddr = chardev_map(defn, ops); |
| if (vaddr == NULL) { |
| return -1; |
| } |
| |
| memset(dev, 0, sizeof(*dev)); |
| |
| /* Set up all the device properties. */ |
| dev->id = defn->id; |
| dev->vaddr = (void*)vaddr; |
| dev->read = &uart_read; |
| dev->write = &uart_write; |
| dev->handle_irq = &uart_handle_irq; |
| dev->irqs = defn->irqs; |
| dev->ioops = *ops; |
| dev->flags = SERIAL_AUTO_CR; |
| |
| st.dev = dev; |
| ret = $exp:uart_setup(&st); |
| |
| $escstm:(#ifdef CONFIG_PLAT_IMX6) |
| /* The UART1 on the IMX6 has the problem that the MUX is not correctly set, |
| * and the RX PIN is not routed correctly. |
| */ |
| if ((defn->id == IMX_UART1) && mux_sys_valid(&ops->mux_sys)) { |
| if (mux_feature_enable(&ops->mux_sys, MUX_UART1, 0)) { |
| // Failed to configure the mux |
| return -1; |
| } |
| } |
| $escstm:(#endif) |
| return 0; |
| } |
| |
| $esc:(#endif) /* end of !CONFIG_PLAT_KZM */ |