1/**
2 * \file
3 * \brief Kernel serial driver for the OMAP44xx UARTs.
4 */
5
6/*
7 * Copyright (c) 2012-2015, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <omap_uart.h>
16#include <dev/omap/omap44xx_uart3_dev.h>
17#include <kernel.h>
18#include <arm.h>
19#include <paging_kernel_arch.h>
20#include <platform.h>
21#include <serial.h>
22
23//
24// Serial console and debugger interfaces
25//
26#define NUM_PORTS 4
27static omap44xx_uart3_t ports[NUM_PORTS];
28
29static void omap_uart_hw_init(omap44xx_uart3_t *uart);
30
31#define MSG(port, format, ...) \
32    printk( LOG_NOTE, "OMAP serial[%d]: "format, port, ## __VA_ARGS__ )
33
34/*
35 * Initialize UARTs before the MMU is on.
36 */
37errval_t serial_early_init(unsigned port)
38{
39    assert(!paging_mmu_enabled());
40    assert(port < serial_num_physical_ports);
41    omap44xx_uart3_initialize(&ports[port], (mackerel_addr_t)uart_base[port]);
42    omap_uart_hw_init(&ports[port]);
43    return SYS_ERR_OK;
44}
45
46/*
47 * Re-initialize UARTs after the MMU is on.
48 */
49void omap_uart_init(unsigned port, lvaddr_t base, bool initialize_hw)
50{
51    assert(paging_mmu_enabled());
52    assert(port < serial_num_physical_ports);
53    omap44xx_uart3_initialize(&ports[port], (mackerel_addr_t)base);
54    if (initialize_hw) omap_uart_hw_init(&ports[port]);
55}
56
57static void
58uart_reset(omap44xx_uart3_t *uart) {
59    /* Do soft reset */
60    omap44xx_uart3_sysc_softreset_wrf(uart, 1);
61    while(!omap44xx_uart3_syss_resetdone_rdf(uart)); // Poll for completion
62}
63
64static void
65init_fifos(omap44xx_uart3_t *uart) {
66    /* Configure FIFOs and DMA. TRM S23.3.5.1.1.2. */
67    omap44xx_uart3_lcr_t lcr_reset= omap44xx_uart3_lcr_rd(uart);
68
69    /* Set configuration mode B. */
70    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x00BF); // Step 1(b)
71
72    /* Enable register submode TCR_TLR (part 1). */
73    int enhanced_en_reset= omap44xx_uart3_efr_enhanced_en_rdf(uart);
74    omap44xx_uart3_efr_enhanced_en_wrf(uart, 1);
75
76    /* Set configuration mode A. */
77    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x0080); // Step 3
78
79    /* Enable register submode TCR_TLR (part 2). */
80    int tcr_tlr_reset= omap44xx_uart3_mcr_tcr_tlr_rdf(uart);
81    omap44xx_uart3_mcr_tcr_tlr_wrf(uart, 1);
82
83    /* Disable FIFOs, and set send and receive triggers to one byte. */
84    omap44xx_uart3_fcr_rx_fifo_trig_wrf(uart, 0x1); // Low 2 bits
85    omap44xx_uart3_fcr_tx_fifo_trig_wrf(uart, 0x1);
86    omap44xx_uart3_fcr_dma_mode_wrf(uart, 0);       // Ignored
87    omap44xx_uart3_fcr_fifo_en_wrf(uart, 0);
88
89    /* Reset Tx and Rx FIFOs. */
90    omap44xx_uart3_fcr_tx_fifo_clear_wrf(uart, 1);
91    omap44xx_uart3_fcr_rx_fifo_clear_wrf(uart, 1);
92
93    /* Set configuration mode B. */
94    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x00BF); // Step 6
95
96    /* Set the upper 4 bits of the trigger levels to zero. */
97    omap44xx_uart3_tlr_rx_fifo_trig_dma_wrf(uart, 0x0);
98    omap44xx_uart3_tlr_tx_fifo_trig_dma_wrf(uart, 0x0);
99
100    /* Disable DMA, and set trigger levels using both tlr_rx_fifo_trig_dma
101     * (high 4 bits), and fcr_rx_fifo_trig (low 2 bits).  */
102    omap44xx_uart3_scr_rx_trig_granu1_wrf(uart, 1);
103    omap44xx_uart3_scr_tx_trig_granu1_wrf(uart, 1);
104    omap44xx_uart3_scr_dma_mode_2_wrf(uart, 0);
105    omap44xx_uart3_scr_dma_mode_ctl_wrf(uart, 1);
106
107    /* Restore enhanced_en. */
108    omap44xx_uart3_efr_enhanced_en_wrf(uart, enhanced_en_reset);
109
110    /* Set configuration mode A. */
111    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x0080); // Step 10
112
113    /* Restore tcr_tlr. */
114    omap44xx_uart3_mcr_tcr_tlr_wrf(uart, tcr_tlr_reset);
115
116    /* Restore LCR. */
117    omap44xx_uart3_lcr_wr(uart, lcr_reset);
118}
119
120static void
121init_protocol(omap44xx_uart3_t *uart) {
122    /* Configure protocol, baud rate and interrupts. TRM S13.3.5.1.1.4. */
123
124    /* Disable UART, to access DLL and DLH. */
125    omap44xx_uart3_mdr1_mode_select_wrf(uart, omap44xx_uart3_MODE_SELECT_7);
126
127    /* Set configuration mode B. */
128    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x00BF);
129
130    /* Enable access to IER[7:4] bit field. */
131    int enhanced_en_reset= omap44xx_uart3_efr_enhanced_en_rdf(uart);
132    omap44xx_uart3_efr_enhanced_en_wrf(uart, 1);
133
134    /* Switch to register operational mode. */
135    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x0000); // Step 4
136
137    /* Clear the interrupt enable register. */
138    omap44xx_uart3_ier_wr(uart, (omap44xx_uart3_ier_t)0x0000); // Step 5
139
140    /* Set configuration mode B. */
141    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x00BF);
142
143    /* Set the Baud rate divisor for ~115200bps, from a 48MHz clock.
144     * We'll set 16x mode, so 115385 ~ 48MHz / (16 * 26). */
145    omap44xx_uart3_dlh_clock_msb_wrf(uart, 0);
146    omap44xx_uart3_dll_clock_lsb_wrf(uart, 26);
147
148    /* Switch to register operational mode. */
149    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x0000);
150
151    /* Enable the receive interrupt. */
152    omap44xx_uart3_ier_rhr_it_wrf(uart, 1);
153
154    /* Set configuration mode B. */
155    omap44xx_uart3_lcr_wr(uart, (omap44xx_uart3_lcr_t)0x00BF);
156
157    /* Restore enhanced_en. */
158    omap44xx_uart3_efr_enhanced_en_wrf(uart, enhanced_en_reset);
159
160    /* Set protocol formatting (8n1). */
161    omap44xx_uart3_lcr_div_en_wrf(uart, 0);
162    omap44xx_uart3_lcr_break_en_wrf(uart, 0);
163    omap44xx_uart3_lcr_parity_en_wrf(uart, 0);
164    omap44xx_uart3_lcr_nb_stop_wrf(uart, omap44xx_uart3_NB_STOP_0);
165    omap44xx_uart3_lcr_char_length_wrf(uart, omap44xx_uart3_cl8);
166
167    /* Set UART to 16x mode. */
168    omap44xx_uart3_mdr1_mode_select_wrf(uart, 0);
169}
170
171/*
172 * Initialise OMAP UART hardware
173 * UART TRM 23.3
174 */
175static void omap_uart_hw_init(omap44xx_uart3_t *uart)
176{
177    uart_reset(uart);
178    init_fifos(uart);
179    init_protocol(uart);
180}
181
182/**
183 * \brief Prints a single character to a serial port.
184 */
185void serial_putchar(unsigned port, char c)
186{
187    assert(port <= NUM_PORTS);
188    omap44xx_uart3_t *uart = &ports[port];
189
190    // Wait until FIFO can hold more characters
191    while(!omap44xx_uart3_lsr_tx_fifo_e_rdf(uart));
192
193    // Write character
194    omap44xx_uart3_thr_thr_wrf(uart, c);
195}
196
197/**
198 * \brief Reads a single character from the default serial port.
199 */
200char serial_getchar(unsigned port)
201{
202    assert(port <= NUM_PORTS);
203    omap44xx_uart3_t *uart = &ports[port];
204
205    /* Read until the interrupt is deasserted. */
206    char c= '\0';
207    while(omap44xx_uart3_iir_it_pending_rdf(uart) == 0) {
208        c= omap44xx_uart3_rhr_rhr_rdf(uart);
209    }
210
211    return c;
212}
213