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>
21 DECLARE_GLOBAL_DATA_PTR
;
23 #define RX_FIFO_COUNT_SHIFT 0
24 #define RX_FIFO_COUNT_MASK (0xff << RX_FIFO_COUNT_SHIFT)
25 #define RX_FIFO_FULL (1 << 8)
26 #define TX_FIFO_COUNT_SHIFT 16
27 #define TX_FIFO_COUNT_MASK (0xff << TX_FIFO_COUNT_SHIFT)
28 #define TX_FIFO_FULL (1 << 24)
30 /* Information about a serial port */
31 struct s5p_serial_platdata
{
32 struct s5p_uart
*reg
; /* address of registers in physical memory */
33 u8 port_id
; /* uart port number */
37 * The coefficient, used to calculate the baudrate on S5P UARTs is
39 * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
40 * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
41 * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
43 static const int udivslot
[] = {
62 static void __maybe_unused
s5p_serial_init(struct s5p_uart
*uart
)
64 /* enable FIFOs, auto clear Rx FIFO */
65 writel(0x3, &uart
->ufcon
);
66 writel(0, &uart
->umcon
);
68 writel(0x3, &uart
->ulcon
);
69 /* No interrupts, no DMA, pure polling */
70 writel(0x245, &uart
->ucon
);
73 static void __maybe_unused
s5p_serial_baud(struct s5p_uart
*uart
, uint uclk
,
78 val
= uclk
/ baudrate
;
80 writel(val
/ 16 - 1, &uart
->ubrdiv
);
82 if (s5p_uart_divslot())
83 writew(udivslot
[val
% 16], &uart
->rest
.slot
);
85 writeb(val
% 16, &uart
->rest
.value
);
88 int s5p_serial_setbrg(struct udevice
*dev
, int baudrate
)
90 struct s5p_serial_platdata
*plat
= dev
->platdata
;
91 struct s5p_uart
*const uart
= plat
->reg
;
92 u32 uclk
= get_uart_clk(plat
->port_id
);
94 s5p_serial_baud(uart
, uclk
, baudrate
);
99 static int s5p_serial_probe(struct udevice
*dev
)
101 struct s5p_serial_platdata
*plat
= dev
->platdata
;
102 struct s5p_uart
*const uart
= plat
->reg
;
104 s5p_serial_init(uart
);
109 static int serial_err_check(const struct s5p_uart
*const uart
, int op
)
116 * Frame Err [2] : receive operation
117 * Parity Err [1] : receive operation
118 * Overrun Err [0] : receive operation
125 return readl(&uart
->uerstat
) & mask
;
128 static int s5p_serial_getc(struct udevice
*dev
)
130 struct s5p_serial_platdata
*plat
= dev
->platdata
;
131 struct s5p_uart
*const uart
= plat
->reg
;
133 if (!(readl(&uart
->ufstat
) & RX_FIFO_COUNT_MASK
))
136 serial_err_check(uart
, 0);
137 return (int)(readb(&uart
->urxh
) & 0xff);
140 static int s5p_serial_putc(struct udevice
*dev
, const char ch
)
142 struct s5p_serial_platdata
*plat
= dev
->platdata
;
143 struct s5p_uart
*const uart
= plat
->reg
;
145 if (readl(&uart
->ufstat
) & TX_FIFO_FULL
)
148 writeb(ch
, &uart
->utxh
);
149 serial_err_check(uart
, 1);
154 static int s5p_serial_pending(struct udevice
*dev
, bool input
)
156 struct s5p_serial_platdata
*plat
= dev
->platdata
;
157 struct s5p_uart
*const uart
= plat
->reg
;
158 uint32_t ufstat
= readl(&uart
->ufstat
);
161 return (ufstat
& RX_FIFO_COUNT_MASK
) >> RX_FIFO_COUNT_SHIFT
;
163 return (ufstat
& TX_FIFO_COUNT_MASK
) >> TX_FIFO_COUNT_SHIFT
;
166 static int s5p_serial_ofdata_to_platdata(struct udevice
*dev
)
168 struct s5p_serial_platdata
*plat
= dev
->platdata
;
171 addr
= fdtdec_get_addr(gd
->fdt_blob
, dev
->of_offset
, "reg");
172 if (addr
== FDT_ADDR_T_NONE
)
175 plat
->reg
= (struct s5p_uart
*)addr
;
176 plat
->port_id
= fdtdec_get_int(gd
->fdt_blob
, dev
->of_offset
, "id", -1);
181 static const struct dm_serial_ops s5p_serial_ops
= {
182 .putc
= s5p_serial_putc
,
183 .pending
= s5p_serial_pending
,
184 .getc
= s5p_serial_getc
,
185 .setbrg
= s5p_serial_setbrg
,
188 static const struct udevice_id s5p_serial_ids
[] = {
189 { .compatible
= "samsung,exynos4210-uart" },
193 U_BOOT_DRIVER(serial_s5p
) = {
194 .name
= "serial_s5p",
196 .of_match
= s5p_serial_ids
,
197 .ofdata_to_platdata
= s5p_serial_ofdata_to_platdata
,
198 .platdata_auto_alloc_size
= sizeof(struct s5p_serial_platdata
),
199 .probe
= s5p_serial_probe
,
200 .ops
= &s5p_serial_ops
,
201 .flags
= DM_FLAG_PRE_RELOC
,
204 #ifdef CONFIG_DEBUG_UART_S5P
206 #include <debug_uart.h>
208 void debug_uart_init(void)
210 struct s5p_uart
*uart
= (struct s5p_uart
*)CONFIG_DEBUG_UART_BASE
;
212 s5p_serial_init(uart
);
213 s5p_serial_baud(uart
, CONFIG_DEBUG_UART_CLOCK
, CONFIG_BAUDRATE
);
216 static inline void _debug_uart_putc(int ch
)
218 struct s5p_uart
*uart
= (struct s5p_uart
*)CONFIG_DEBUG_UART_BASE
;
220 while (readl(&uart
->ufstat
) & TX_FIFO_FULL
);
222 writeb(ch
, &uart
->utxh
);