2 * (C) Copyright 2009 SAMSUNG Electronics
3 * Minkyu Kang <mk7.kang@samsung.com>
4 * Heungjun Kim <riverful.kim@samsung.com>
6 * based on drivers/serial/s3c64xx.c
8 * SPDX-License-Identifier: GPL-2.0+
15 #include <linux/compiler.h>
17 #include <asm/arch/clk.h>
18 #include <asm/arch/uart.h>
22 DECLARE_GLOBAL_DATA_PTR
;
24 #define RX_FIFO_COUNT_SHIFT 0
25 #define RX_FIFO_COUNT_MASK (0xff << RX_FIFO_COUNT_SHIFT)
26 #define RX_FIFO_FULL (1 << 8)
27 #define TX_FIFO_COUNT_SHIFT 16
28 #define TX_FIFO_COUNT_MASK (0xff << TX_FIFO_COUNT_SHIFT)
29 #define TX_FIFO_FULL (1 << 24)
31 /* Information about a serial port */
32 struct s5p_serial_platdata
{
33 struct s5p_uart
*reg
; /* address of registers in physical memory */
34 u8 port_id
; /* uart port number */
38 * The coefficient, used to calculate the baudrate on S5P UARTs is
40 * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
41 * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
42 * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
44 static const int udivslot
[] = {
63 static void __maybe_unused
s5p_serial_init(struct s5p_uart
*uart
)
65 /* enable FIFOs, auto clear Rx FIFO */
66 writel(0x3, &uart
->ufcon
);
67 writel(0, &uart
->umcon
);
69 writel(0x3, &uart
->ulcon
);
70 /* No interrupts, no DMA, pure polling */
71 writel(0x245, &uart
->ucon
);
74 static void __maybe_unused
s5p_serial_baud(struct s5p_uart
*uart
, uint uclk
,
79 val
= uclk
/ baudrate
;
81 writel(val
/ 16 - 1, &uart
->ubrdiv
);
83 if (s5p_uart_divslot())
84 writew(udivslot
[val
% 16], &uart
->rest
.slot
);
86 writeb(val
% 16, &uart
->rest
.value
);
89 #ifndef CONFIG_SPL_BUILD
90 int s5p_serial_setbrg(struct udevice
*dev
, int baudrate
)
92 struct s5p_serial_platdata
*plat
= dev
->platdata
;
93 struct s5p_uart
*const uart
= plat
->reg
;
96 #ifdef CONFIG_CLK_EXYNOS
100 ret
= clk_get_by_index(dev
, 1, &clk
);
103 uclk
= clk_get_rate(&clk
);
105 uclk
= get_uart_clk(plat
->port_id
);
108 s5p_serial_baud(uart
, uclk
, baudrate
);
113 static int s5p_serial_probe(struct udevice
*dev
)
115 struct s5p_serial_platdata
*plat
= dev
->platdata
;
116 struct s5p_uart
*const uart
= plat
->reg
;
118 s5p_serial_init(uart
);
123 static int serial_err_check(const struct s5p_uart
*const uart
, int op
)
130 * Frame Err [2] : receive operation
131 * Parity Err [1] : receive operation
132 * Overrun Err [0] : receive operation
139 return readl(&uart
->uerstat
) & mask
;
142 static int s5p_serial_getc(struct udevice
*dev
)
144 struct s5p_serial_platdata
*plat
= dev
->platdata
;
145 struct s5p_uart
*const uart
= plat
->reg
;
147 if (!(readl(&uart
->ufstat
) & RX_FIFO_COUNT_MASK
))
150 serial_err_check(uart
, 0);
151 return (int)(readb(&uart
->urxh
) & 0xff);
154 static int s5p_serial_putc(struct udevice
*dev
, const char ch
)
156 struct s5p_serial_platdata
*plat
= dev
->platdata
;
157 struct s5p_uart
*const uart
= plat
->reg
;
159 if (readl(&uart
->ufstat
) & TX_FIFO_FULL
)
162 writeb(ch
, &uart
->utxh
);
163 serial_err_check(uart
, 1);
168 static int s5p_serial_pending(struct udevice
*dev
, bool input
)
170 struct s5p_serial_platdata
*plat
= dev
->platdata
;
171 struct s5p_uart
*const uart
= plat
->reg
;
172 uint32_t ufstat
= readl(&uart
->ufstat
);
175 return (ufstat
& RX_FIFO_COUNT_MASK
) >> RX_FIFO_COUNT_SHIFT
;
177 return (ufstat
& TX_FIFO_COUNT_MASK
) >> TX_FIFO_COUNT_SHIFT
;
180 static int s5p_serial_ofdata_to_platdata(struct udevice
*dev
)
182 struct s5p_serial_platdata
*plat
= dev
->platdata
;
185 addr
= devfdt_get_addr(dev
);
186 if (addr
== FDT_ADDR_T_NONE
)
189 plat
->reg
= (struct s5p_uart
*)addr
;
190 plat
->port_id
= fdtdec_get_int(gd
->fdt_blob
, dev_of_offset(dev
),
195 static const struct dm_serial_ops s5p_serial_ops
= {
196 .putc
= s5p_serial_putc
,
197 .pending
= s5p_serial_pending
,
198 .getc
= s5p_serial_getc
,
199 .setbrg
= s5p_serial_setbrg
,
202 static const struct udevice_id s5p_serial_ids
[] = {
203 { .compatible
= "samsung,exynos4210-uart" },
207 U_BOOT_DRIVER(serial_s5p
) = {
208 .name
= "serial_s5p",
210 .of_match
= s5p_serial_ids
,
211 .ofdata_to_platdata
= s5p_serial_ofdata_to_platdata
,
212 .platdata_auto_alloc_size
= sizeof(struct s5p_serial_platdata
),
213 .probe
= s5p_serial_probe
,
214 .ops
= &s5p_serial_ops
,
215 .flags
= DM_FLAG_PRE_RELOC
,
219 #ifdef CONFIG_DEBUG_UART_S5P
221 #include <debug_uart.h>
223 static inline void _debug_uart_init(void)
225 struct s5p_uart
*uart
= (struct s5p_uart
*)CONFIG_DEBUG_UART_BASE
;
227 s5p_serial_init(uart
);
228 s5p_serial_baud(uart
, CONFIG_DEBUG_UART_CLOCK
, CONFIG_BAUDRATE
);
231 static inline void _debug_uart_putc(int ch
)
233 struct s5p_uart
*uart
= (struct s5p_uart
*)CONFIG_DEBUG_UART_BASE
;
235 while (readl(&uart
->ufstat
) & TX_FIFO_FULL
);
237 writeb(ch
, &uart
->utxh
);