1/**
2 * \file
3 * \brief ARM pl011 UART kernel-level driver.
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <arch/arm/lpuart.h>
16#include <kernel.h>
17#include <paging_kernel_arch.h>
18#include <arch/arm/arm.h>
19#include <arch/arm/platform.h>
20#include <serial.h>
21#include <dev/lpuart_dev.h>
22
23static lpuart_t uarts[LPUART_MAX_PORTS];
24
25// Mask all interrupts in the IMSC register
26#define INTERRUPTS_MASK		0
27
28#define MSG(format, ...) printk( LOG_NOTE, "lpuart: "format, ## __VA_ARGS__ )
29
30static void lpuart_hw_init(lpuart_t *uart);
31
32/**
33 * \brief Configure the serial interface, from a caller that knows
34 * that this is a bunch of PL011s, and furthermore where they are in
35 * the physical address space.
36 */
37errval_t serial_early_init(unsigned n)
38{
39    assert(!paging_mmu_enabled());
40    assert(n < serial_num_physical_ports);
41
42    lpuart_initialize(&uarts[n],
43        (mackerel_addr_t)local_phys_to_mem(platform_uart_base[n]));
44
45    // Make sure that the UART is enabled and transmitting - not all platforms
46    // do this for us.
47    lpuart_hw_init(&uarts[n]);
48
49    return SYS_ERR_OK;
50}
51
52/**
53 * \brief Configure the serial interface, from a caller that knows
54 * that this is a bunch of PL011s, and furthermore where they are in
55 * the physical address space.
56 */
57errval_t serial_early_init_mmu_enabled(unsigned n)
58{
59    assert(paging_mmu_enabled());
60    assert(n < serial_num_physical_ports);
61
62    lpuart_initialize(&uarts[n],
63        (mackerel_addr_t)local_phys_to_mem(platform_uart_base[n]));
64
65    // Make sure that the UART is enabled and transmitting - not all platforms
66    // do this for us.
67    lpuart_hw_init(&uarts[n]);
68
69    return SYS_ERR_OK;
70}
71/*
72 * \brief Initialize a serial port.  The MMU is turned on.
73 */
74void lpuart_init(unsigned port, lvaddr_t base, bool hwinit)
75{
76    assert(paging_mmu_enabled());
77    assert(port < serial_num_physical_ports);
78
79    lpuart_t *u = &uarts[port];
80
81    // [Re]initialize the Mackerel state for the UART
82    lpuart_initialize(u, (mackerel_addr_t) base);
83
84    if (hwinit) {
85        lpuart_hw_init(u);
86    }
87}
88
89static void lpuart_hw_init(lpuart_t *uart)
90{
91    // Disable transceiver
92    lpuart_ctrl_t ctrl = lpuart_ctrl_rawrd(uart);
93    ctrl = lpuart_ctrl_te_insert(ctrl, 0);
94    ctrl = lpuart_ctrl_re_insert(ctrl, 0);
95    lpuart_ctrl_rawwr(uart, ctrl);
96
97    //TODO: Figure out more advanced configuration (FIFO etc.)
98
99    // Set baudrate
100    // baudrate = clock rate / (over sampling rate * SBR)
101    // TODO: Currently we assume UART clock is set to 8MHz
102    lpuart_baud_t baud = lpuart_baud_default;
103    baud = lpuart_baud_osr_insert(baud, lpuart_ratio5);
104    // OSR of 5 needs bothedge set
105    baud = lpuart_baud_bothedge_insert(baud, 1);
106    baud = lpuart_baud_sbr_insert(baud, 139);
107    lpuart_baud_rawwr(uart, baud);
108
109    // Enable transceiver
110    ctrl = lpuart_ctrl_default;
111    ctrl = lpuart_ctrl_te_insert(ctrl, 1);
112    ctrl = lpuart_ctrl_re_insert(ctrl, 1);
113    lpuart_ctrl_rawwr(uart, ctrl);
114}
115
116/*
117 * \brief Put a character to the port
118 */
119void serial_putchar(unsigned port, char c)
120{
121    assert(port < LPUART_MAX_PORTS);
122    lpuart_t *u = &uarts[port];
123    assert(u->base != 0);
124
125    while(lpuart_stat_tdre_rdf(u) == 0);
126    lpuart_txdata_wr(u,c);
127}
128
129/*
130 * \brief Read a character from a port
131 */
132char serial_getchar(unsigned port)
133{
134    assert(port < LPUART_MAX_PORTS);
135    lpuart_t *u = &uarts[port];
136    assert(u->base != 0);
137
138    /* Wait for data. */
139    while(lpuart_stat_rdrf_rdf(u) == 0);
140
141    return (char)lpuart_rxdata_buf_rdf(u);
142}
143