77fddb890c908df658a8ad0c0bb39b5de55ee75a
[barrelfish] / kernel / arch / arm / lpuart.c
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
23 static 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
30 static 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  */
37 errval_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  */
57 errval_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  */
74 void 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
89 static 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  */
119 void 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
127     lpuart_txdata_t txdata = lpuart_txdata_default;
128     // We don't handle break/idle char currently
129     txdata = lpuart_txdata_tsc_insert(txdata, 0);
130     txdata = lpuart_txdata_buf_insert(txdata, c);
131     lpuart_txdata_wr(u, txdata);
132 }
133
134 /*
135  * \brief Read a character from a port
136  */
137 char serial_getchar(unsigned port)
138 {
139     assert(port < LPUART_MAX_PORTS);
140     lpuart_t *u = &uarts[port];
141     assert(u->base != 0);
142
143     /* Wait for data. */
144     while(lpuart_stat_rdrf_rdf(u) == 0);
145
146     return (char)lpuart_rxdata_buf_rdf(u);
147 }