]> git.sur5r.net Git - u-boot/blob - arch/sparc/cpu/leon3/serial.c
sparc: Serial baud rate register support multiple buses with different frequency
[u-boot] / arch / sparc / cpu / leon3 / serial.c
1 /* GRLIB APBUART Serial controller driver
2  *
3  * (C) Copyright 2007, 2015
4  * Daniel Hellstrom, Cobham Gaisler, daniel@gaisler.com.
5  *
6  * SPDX-License-Identifier:     GPL-2.0+
7  */
8
9 #include <common.h>
10 #include <asm/io.h>
11 #include <ambapp.h>
12 #include <grlib/apbuart.h>
13 #include <serial.h>
14 #include <watchdog.h>
15
16 DECLARE_GLOBAL_DATA_PTR;
17
18 /* Select which UART that will become u-boot console */
19 #ifndef CONFIG_SYS_GRLIB_APBUART_INDEX
20 #define CONFIG_SYS_GRLIB_APBUART_INDEX 0
21 #endif
22
23 static unsigned apbuart_calc_scaler(unsigned apbuart_freq, unsigned baud)
24 {
25         return (((apbuart_freq * 10) / (baud * 8)) - 5) / 10;
26 }
27
28 static int leon3_serial_init(void)
29 {
30         ambapp_dev_apbuart *uart;
31         ambapp_apbdev apbdev;
32         unsigned int tmp;
33
34         /* find UART */
35         if (ambapp_apb_find(&ambapp_plb, VENDOR_GAISLER, GAISLER_APBUART,
36                 CONFIG_SYS_GRLIB_APBUART_INDEX, &apbdev) != 1) {
37                 gd->flags &= ~GD_FLG_SERIAL_READY;
38                 panic("%s: apbuart not found!\n", __func__);
39                 return -1; /* didn't find hardware */
40         }
41
42         /* found apbuart, let's init .. */
43         uart = (ambapp_dev_apbuart *) apbdev.address;
44
45         /* APBUART Frequency is equal to bus frequency */
46         gd->arch.uart_freq = ambapp_bus_freq(&ambapp_plb, apbdev.ahb_bus_index);
47
48         /* Set scaler / baud rate */
49         tmp = apbuart_calc_scaler(gd->arch.uart_freq, CONFIG_BAUDRATE);
50         writel(tmp, &uart->scaler);
51
52         /* Let bit 11 be unchanged (debug bit for GRMON) */
53         tmp = readl(&uart->ctrl) & APBUART_CTRL_DBG;
54         /* Receiver & transmitter enable */
55         tmp |= APBUART_CTRL_RE | APBUART_CTRL_TE;
56         writel(tmp, &uart->ctrl);
57
58         gd->arch.uart = uart;
59         return 0;
60 }
61
62 static inline ambapp_dev_apbuart *leon3_get_uart_regs(void)
63 {
64         ambapp_dev_apbuart *uart = gd->arch.uart;
65         return uart;
66 }
67
68 static void leon3_serial_putc_raw(const char c)
69 {
70         ambapp_dev_apbuart * const uart = leon3_get_uart_regs();
71
72         if (!uart)
73                 return;
74
75         /* Wait for last character to go. */
76         while (!(readl(&uart->status) & APBUART_STATUS_THE))
77                 WATCHDOG_RESET();
78
79         /* Send data */
80         writel(c, &uart->data);
81
82 #ifdef LEON_DEBUG
83         /* Wait for data to be sent */
84         while (!(readl(&uart->status) & APBUART_STATUS_TSE))
85                 WATCHDOG_RESET();
86 #endif
87 }
88
89 static void leon3_serial_putc(const char c)
90 {
91         if (c == '\n')
92                 leon3_serial_putc_raw('\r');
93
94         leon3_serial_putc_raw(c);
95 }
96
97 static int leon3_serial_getc(void)
98 {
99         ambapp_dev_apbuart * const uart = leon3_get_uart_regs();
100
101         if (!uart)
102                 return 0;
103
104         /* Wait for a character to arrive. */
105         while (!(readl(&uart->status) & APBUART_STATUS_DR))
106                 WATCHDOG_RESET();
107
108         /* Read character data */
109         return readl(&uart->data);
110 }
111
112 static int leon3_serial_tstc(void)
113 {
114         ambapp_dev_apbuart * const uart = leon3_get_uart_regs();
115
116         if (!uart)
117                 return 0;
118
119         return readl(&uart->status) & APBUART_STATUS_DR;
120 }
121
122 /* set baud rate for uart */
123 static void leon3_serial_setbrg(void)
124 {
125         ambapp_dev_apbuart * const uart = leon3_get_uart_regs();
126         unsigned int scaler;
127
128         if (!uart)
129                 return;
130
131         if (!gd->baudrate)
132                 gd->baudrate = CONFIG_BAUDRATE;
133
134         if (!gd->arch.uart_freq)
135                 gd->arch.uart_freq = CONFIG_SYS_CLK_FREQ;
136
137         scaler = apbuart_calc_scaler(gd->arch.uart_freq, gd->baudrate);
138
139         writel(scaler, &uart->scaler);
140 }
141
142 static struct serial_device leon3_serial_drv = {
143         .name   = "leon3_serial",
144         .start  = leon3_serial_init,
145         .stop   = NULL,
146         .setbrg = leon3_serial_setbrg,
147         .putc   = leon3_serial_putc,
148         .puts   = default_serial_puts,
149         .getc   = leon3_serial_getc,
150         .tstc   = leon3_serial_tstc,
151 };
152
153 void leon3_serial_initialize(void)
154 {
155         serial_register(&leon3_serial_drv);
156 }
157
158 __weak struct serial_device *default_serial_console(void)
159 {
160         return &leon3_serial_drv;
161 }
162
163 #ifdef CONFIG_DEBUG_UART_APBUART
164
165 #include <debug_uart.h>
166
167 static inline void _debug_uart_init(void)
168 {
169         ambapp_dev_apbuart *uart = (ambapp_dev_apbuart *)CONFIG_DEBUG_UART_BASE;
170         uart->scaler = apbuart_calc_scaler(CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
171         uart->ctrl = APBUART_CTRL_RE | APBUART_CTRL_TE;
172 }
173
174 static inline void _debug_uart_putc(int ch)
175 {
176         ambapp_dev_apbuart *uart = (ambapp_dev_apbuart *)CONFIG_DEBUG_UART_BASE;
177         while (!(readl(&uart->status) & APBUART_STATUS_THE))
178                 WATCHDOG_RESET();
179         writel(ch, &uart->data);
180 }
181
182 DEBUG_UART_FUNCS
183
184 #endif