File size: 3,272 Bytes
829b19c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
110
111
/*
 * HAL — UART driver for OMAP3530 (NS16550A-compatible)
 */

#include "uart.h"

void uart_init(uart_dev_t *dev, uint32_t base, uint32_t clock_hz) {
    dev->base = (hw_reg32_t)base;
    dev->clock_hz = clock_hz;
}

void uart_configure(uart_dev_t *dev, uint32_t baud, uint8_t data_bits, uint8_t parity, uint8_t stop_bits) {
    hw_reg32_t base = dev->base;

    /* Switch to UART mode (disable IrDA / disable modem) */
    reg_write32(base + UART_MDR1, 0x07);   /* Disable UART */
    delay_cycles(100);

    /* Reset TX/RX FIFOs and enable FIFO mode */
    reg_write32(base + UART_FCR, UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR);
    delay_cycles(10);

    /* Set DLAB to access divisor registers */
    reg32_t lcr = 0;
    switch (data_bits) {
        case 5:  lcr |= 0x00; break;
        case 6:  lcr |= 0x01; break;
        case 7:  lcr |= 0x02; break;
        case 8:
        default: lcr |= 0x03; break;
    }
    switch (parity) {
        case 0: break;                    /* No parity */
        case 1: lcr |= (1 << 3); break;  /* Odd parity */
        case 2: lcr |= (3 << 3); break;  /* Even parity */
    }
    if (stop_bits == 2)
        lcr |= (1 << 2);

    /* Set DLAB, write divisor, clear DLAB */
    reg_write32(base + UART_LCR, lcr | UART_LCR_DLAB);

    uint16_t divisor = (uint16_t)(dev->clock_hz / (16 * baud));
    reg_write32(base + UART_DLL, divisor & 0xFF);
    reg_write32(base + UART_DLH, (divisor >> 8) & 0xFF);

    /* Clear DLAB — restore normal register access */
    reg_write32(base + UART_LCR, lcr);

    /* No flow control, no interrupts */
    reg_write32(base + UART_IER, 0x00);
    reg_write32(base + UART_MCR, 0x00);

    /* Enable FIFOs with 8-byte trigger */
    reg_write32(base + UART_FCR, UART_FCR_FIFO_EN | (0x3 << 6));

    /* Re-enable UART in 16x mode */
    reg_write32(base + UART_MDR1, 0x00);
}

void uart_putc(uart_dev_t *dev, char c) {
    hw_reg32_t base = dev->base;
    /* Wait for TX holding register empty */
    while (!(reg_read32(base + UART_LSR) & UART_LSR_THRE))
        ;
    reg_write32(base + UART_THR, (reg32_t)c);
}

char uart_getc(uart_dev_t *dev) {
    hw_reg32_t base = dev->base;
    /* Wait for data ready */
    while (!(reg_read32(base + UART_LSR) & UART_LSR_DR))
        ;
    return (char)(reg_read32(base + UART_RBR) & 0xFF);
}

int uart_try_getc(uart_dev_t *dev) {
    hw_reg32_t base = dev->base;
    if (reg_read32(base + UART_LSR) & UART_LSR_DR)
        return (int)(reg_read32(base + UART_RBR) & 0xFF);
    return -1;
}

void uart_puts(uart_dev_t *dev, const char *s) {
    while (*s) {
        if (*s == '\n')
            uart_putc(dev, '\r');
        uart_putc(dev, *s++);
    }
}

void uart_put_hex_byte(uart_dev_t *dev, uint8_t val) {
    static const char hex[] = "0123456789ABCDEF";
    uart_putc(dev, hex[(val >> 4) & 0xF]);
    uart_putc(dev, hex[val & 0xF]);
}

void uart_put_hex(uart_dev_t *dev, uint32_t val) {
    uart_puts(dev, "0x");
    uart_put_hex_byte(dev, (val >> 24) & 0xFF);
    uart_put_hex_byte(dev, (val >> 16) & 0xFF);
    uart_put_hex_byte(dev, (val >> 8) & 0xFF);
    uart_put_hex_byte(dev, val & 0xFF);
}

void uart_flush(uart_dev_t *dev) {
    hw_reg32_t base = dev->base;
    while (!(reg_read32(base + UART_LSR) & UART_LSR_TEMT))
        ;
}