]> git.ipfire.org Git - thirdparty/u-boot.git/blame - drivers/serial/serial_sifive.c
pinctrl: meson-axg: add support for getting pinmux status
[thirdparty/u-boot.git] / drivers / serial / serial_sifive.c
CommitLineData
e2842496
AP
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018 Anup Patel <anup@brainfault.org>
4 */
5
e2842496 6#include <common.h>
b24f9057 7#include <clk.h>
e2842496
AP
8#include <debug_uart.h>
9#include <dm.h>
10#include <errno.h>
11#include <fdtdec.h>
12#include <watchdog.h>
13#include <asm/io.h>
14#include <linux/compiler.h>
15#include <serial.h>
16
17DECLARE_GLOBAL_DATA_PTR;
18
19#define UART_TXFIFO_FULL 0x80000000
20#define UART_RXFIFO_EMPTY 0x80000000
21#define UART_RXFIFO_DATA 0x000000ff
22#define UART_TXCTRL_TXEN 0x1
23#define UART_RXCTRL_RXEN 0x1
24
25struct uart_sifive {
26 u32 txfifo;
27 u32 rxfifo;
28 u32 txctrl;
29 u32 rxctrl;
30 u32 ie;
31 u32 ip;
32 u32 div;
33};
34
35struct sifive_uart_platdata {
a3682008 36 unsigned long clock;
e2842496
AP
37 int saved_input_char;
38 struct uart_sifive *regs;
39};
40
a3682008
AP
41/**
42 * Find minimum divisor divides in_freq to max_target_hz;
43 * Based on uart driver n SiFive FSBL.
44 *
45 * f_baud = f_in / (div + 1) => div = (f_in / f_baud) - 1
46 * The nearest integer solution requires rounding up as to not exceed
47 * max_target_hz.
48 * div = ceil(f_in / f_baud) - 1
49 * = floor((f_in - 1 + f_baud) / f_baud) - 1
50 * This should not overflow as long as (f_in - 1 + f_baud) does not exceed
51 * 2^32 - 1, which is unlikely since we represent frequencies in kHz.
52 */
53static inline unsigned int uart_min_clk_divisor(unsigned long in_freq,
54 unsigned long max_target_hz)
55{
56 unsigned long quotient =
57 (in_freq + max_target_hz - 1) / (max_target_hz);
58 /* Avoid underflow */
59 if (quotient == 0)
60 return 0;
61 else
62 return quotient - 1;
63}
64
e2842496
AP
65/* Set up the baud rate in gd struct */
66static void _sifive_serial_setbrg(struct uart_sifive *regs,
67 unsigned long clock, unsigned long baud)
68{
a3682008 69 writel((uart_min_clk_divisor(clock, baud)), &regs->div);
e2842496
AP
70}
71
72static void _sifive_serial_init(struct uart_sifive *regs)
73{
74 writel(UART_TXCTRL_TXEN, &regs->txctrl);
75 writel(UART_RXCTRL_RXEN, &regs->rxctrl);
76 writel(0, &regs->ie);
77}
78
79static int _sifive_serial_putc(struct uart_sifive *regs, const char c)
80{
81 if (readl(&regs->txfifo) & UART_TXFIFO_FULL)
82 return -EAGAIN;
83
84 writel(c, &regs->txfifo);
85
86 return 0;
87}
88
89static int _sifive_serial_getc(struct uart_sifive *regs)
90{
91 int ch = readl(&regs->rxfifo);
92
93 if (ch & UART_RXFIFO_EMPTY)
94 return -EAGAIN;
95 ch &= UART_RXFIFO_DATA;
96
97 return (!ch) ? -EAGAIN : ch;
98}
99
100static int sifive_serial_setbrg(struct udevice *dev, int baudrate)
101{
ee0633ef 102 int ret;
e2842496
AP
103 struct clk clk;
104 struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
ee0633ef 105 u32 clock = 0;
e2842496 106
ee0633ef
AP
107 ret = clk_get_by_index(dev, 0, &clk);
108 if (IS_ERR_VALUE(ret)) {
e2842496 109 debug("SiFive UART failed to get clock\n");
ee0633ef
AP
110 ret = dev_read_u32(dev, "clock-frequency", &clock);
111 if (IS_ERR_VALUE(ret)) {
112 debug("SiFive UART clock not defined\n");
113 return 0;
114 }
115 } else {
116 clock = clk_get_rate(&clk);
117 if (IS_ERR_VALUE(clock)) {
118 debug("SiFive UART clock get rate failed\n");
119 return 0;
120 }
e2842496 121 }
ee0633ef 122 platdata->clock = clock;
e2842496
AP
123 _sifive_serial_setbrg(platdata->regs, platdata->clock, baudrate);
124
125 return 0;
126}
127
128static int sifive_serial_probe(struct udevice *dev)
129{
130 struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
131
132 /* No need to reinitialize the UART after relocation */
133 if (gd->flags & GD_FLG_RELOC)
134 return 0;
135
136 platdata->saved_input_char = 0;
137 _sifive_serial_init(platdata->regs);
138
139 return 0;
140}
141
142static int sifive_serial_getc(struct udevice *dev)
143{
144 int c;
145 struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
146 struct uart_sifive *regs = platdata->regs;
147
148 if (platdata->saved_input_char > 0) {
149 c = platdata->saved_input_char;
150 platdata->saved_input_char = 0;
151 return c;
152 }
153
154 while ((c = _sifive_serial_getc(regs)) == -EAGAIN) ;
155
156 return c;
157}
158
159static int sifive_serial_putc(struct udevice *dev, const char ch)
160{
161 int rc;
162 struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
163
164 while ((rc = _sifive_serial_putc(platdata->regs, ch)) == -EAGAIN) ;
165
166 return rc;
167}
168
169static int sifive_serial_pending(struct udevice *dev, bool input)
170{
171 struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
172 struct uart_sifive *regs = platdata->regs;
173
174 if (input) {
175 if (platdata->saved_input_char > 0)
176 return 1;
177 platdata->saved_input_char = _sifive_serial_getc(regs);
178 return (platdata->saved_input_char > 0) ? 1 : 0;
179 } else {
180 return !!(readl(&regs->txfifo) & UART_TXFIFO_FULL);
181 }
182}
183
184static int sifive_serial_ofdata_to_platdata(struct udevice *dev)
185{
186 struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
187
188 platdata->regs = (struct uart_sifive *)dev_read_addr(dev);
189 if (IS_ERR(platdata->regs))
190 return PTR_ERR(platdata->regs);
191
192 return 0;
193}
194
195static const struct dm_serial_ops sifive_serial_ops = {
196 .putc = sifive_serial_putc,
197 .getc = sifive_serial_getc,
198 .pending = sifive_serial_pending,
199 .setbrg = sifive_serial_setbrg,
200};
201
202static const struct udevice_id sifive_serial_ids[] = {
203 { .compatible = "sifive,uart0" },
204 { }
205};
206
207U_BOOT_DRIVER(serial_sifive) = {
208 .name = "serial_sifive",
209 .id = UCLASS_SERIAL,
210 .of_match = sifive_serial_ids,
211 .ofdata_to_platdata = sifive_serial_ofdata_to_platdata,
212 .platdata_auto_alloc_size = sizeof(struct sifive_uart_platdata),
213 .probe = sifive_serial_probe,
214 .ops = &sifive_serial_ops,
215};
216
217#ifdef CONFIG_DEBUG_UART_SIFIVE
218static inline void _debug_uart_init(void)
219{
220 struct uart_sifive *regs =
221 (struct uart_sifive *)CONFIG_DEBUG_UART_BASE;
222
223 _sifive_serial_setbrg(regs, CONFIG_DEBUG_UART_CLOCK,
224 CONFIG_BAUDRATE);
225 _sifive_serial_init(regs);
226}
227
228static inline void _debug_uart_putc(int ch)
229{
230 struct uart_sifive *regs =
231 (struct uart_sifive *)CONFIG_DEBUG_UART_BASE;
232
233 while (_sifive_serial_putc(regs, ch) == -EAGAIN)
234 WATCHDOG_RESET();
235}
236
237DEBUG_UART_FUNCS
238
239#endif