summaryrefslogtreecommitdiff
path: root/ports/risc-v64/gnu/example_build/erbium/uart.c
blob: 1b8f2dfc7cabb2987823c85b1579f317cde396ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/***************************************************************************
 * Copyright (c) 2026 Eclipse ThreadX contributors
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 *
 * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5.5).
 * The AI-generated portions may be considered public domain (CC0-1.0)
 * and not subject to the project's licence. The human contributor has
 * reviewed and verified that the code is correct.
 *
 * SPDX-License-Identifier: MIT and CC0-1.0
 **************************************************************************/

/* Polling console driver for the Erbium UART (original Shakti integration).
 *
 *   Offset  Name          Access  Description
 *   ──────  ────────────  ──────  ─────────────────────────────────────
 *   0x00    BaudReg       R/W     Divisor: clock / (16 * baud)
 *   0x08    TxReg         W       Transmit data
 *   0x10    RxReg         R       Receive data
 *   0x18    StatusReg     R       FIFO and error status
 *   0x20    DelayReg      R/W     Transmit delay
 *   0x28    ControlReg    R/W     Character size, parity, stop bits
 *   0x30    InterruptEn   R/W     Interrupt enables
 *   0x40    Rx_Threshold  R/W     Receive FIFO threshold
 *
 * All registers are accessed as 32-bit words.  The transmit and receive
 * FIFOs hold 16 entries each.
 */

#include "uart.h"
#include "csr.h"
#include <stdint.h>

#define UART_DIVISOR            (ERBIUM_UART_CLOCK_HZ / (16UL * ERBIUM_UART_BAUD))

#if (UART_DIVISOR == 0) || (UART_DIVISOR > 0xFFFF)
#error "The Erbium UART baud divisor must be in the range 1..65535."
#endif

/* SystemConfig in the system register block gates the UART.  */
#define SYSREG_SYSTEM_CONFIG    0x02000008UL
#define SYSTEM_CONFIG_UART_EN   (1u << 6)

#define UART_BAUD               0x00
#define UART_TX                 0x08
#define UART_STATUS             0x18
#define UART_DELAY              0x20
#define UART_CONTROL            0x28
#define UART_IEN                0x30
#define UART_RX_THRESHOLD       0x40

#define STATUS_TX_FULL          (1u << 1)

#define CONTROL_CHARSIZE_SHIFT  5           /* bits 10:5; stop bits and parity 0 */

#define REG32(addr)             (*(volatile uint32_t *)(addr))
#define UART_REG(off)           REG32(UART0 + (off))

int uart_init(void)
{
    /* Ungate the UART.  Read-modify-write keeps the watchdog and the
       other peripheral enables as the boot flow left them.  */
    REG32(SYSREG_SYSTEM_CONFIG) = REG32(SYSREG_SYSTEM_CONFIG) | SYSTEM_CONFIG_UART_EN;

    /* Polling only: no UART interrupts.  */
    UART_REG(UART_IEN) = 0;

    /* 8 data bits, no parity, one stop bit.  */
    UART_REG(UART_BAUD) = (uint32_t)UART_DIVISOR;
    UART_REG(UART_CONTROL) = 8u << CONTROL_CHARSIZE_SHIFT;
    UART_REG(UART_DELAY) = 0;
    UART_REG(UART_RX_THRESHOLD) = 0;

    puts("[UART0] : Erbium UART initialized");
    return 0;
}

int uart_putc(int ch)
{
    int intr_enable;

    for (;;)
    {
        while ((UART_REG(UART_STATUS) & STATUS_TX_FULL) != 0u)
            ;

        intr_enable = riscv_mintr_save();
        if ((UART_REG(UART_STATUS) & STATUS_TX_FULL) == 0u)
        {
            UART_REG(UART_TX) = (uint32_t)(ch & 0xFF);
            riscv_mintr_restore(intr_enable);
            return 1;
        }
        riscv_mintr_restore(intr_enable);
    }
}

int uart_puts(const char *str)
{
    int i;
    for (i = 0; str[i] != 0; i++)
        uart_putc(str[i]);
    uart_putc('\r');
    uart_putc('\n');
    return i;
}