xref: /rk3399_rockchip-uboot/drivers/serial/serial_s5p.c (revision a821c4af79e4f5ce9b629b20473863397bbe9b10)
146a3b5c8SMinkyu Kang /*
246a3b5c8SMinkyu Kang  * (C) Copyright 2009 SAMSUNG Electronics
346a3b5c8SMinkyu Kang  * Minkyu Kang <mk7.kang@samsung.com>
446a3b5c8SMinkyu Kang  * Heungjun Kim <riverful.kim@samsung.com>
546a3b5c8SMinkyu Kang  *
646a3b5c8SMinkyu Kang  * based on drivers/serial/s3c64xx.c
746a3b5c8SMinkyu Kang  *
81a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
946a3b5c8SMinkyu Kang  */
1046a3b5c8SMinkyu Kang 
1146a3b5c8SMinkyu Kang #include <common.h>
1273e256c2SSimon Glass #include <dm.h>
1373e256c2SSimon Glass #include <errno.h>
14d4ec8f08SRajeshwari Shinde #include <fdtdec.h>
156c768ca7SMike Frysinger #include <linux/compiler.h>
1646a3b5c8SMinkyu Kang #include <asm/io.h>
1746a3b5c8SMinkyu Kang #include <asm/arch/clk.h>
1889ca9351SSimon Glass #include <asm/arch/uart.h>
1946a3b5c8SMinkyu Kang #include <serial.h>
20cf75cdf9SThomas Abraham #include <clk.h>
2146a3b5c8SMinkyu Kang 
2229565326SJohn Rigby DECLARE_GLOBAL_DATA_PTR;
2329565326SJohn Rigby 
2473e256c2SSimon Glass #define RX_FIFO_COUNT_SHIFT	0
2573e256c2SSimon Glass #define RX_FIFO_COUNT_MASK	(0xff << RX_FIFO_COUNT_SHIFT)
2673e256c2SSimon Glass #define RX_FIFO_FULL		(1 << 8)
2773e256c2SSimon Glass #define TX_FIFO_COUNT_SHIFT	16
2873e256c2SSimon Glass #define TX_FIFO_COUNT_MASK	(0xff << TX_FIFO_COUNT_SHIFT)
2973e256c2SSimon Glass #define TX_FIFO_FULL		(1 << 24)
30ffbff1ddSAkshay Saraswat 
31d4ec8f08SRajeshwari Shinde /* Information about a serial port */
3273e256c2SSimon Glass struct s5p_serial_platdata {
3373e256c2SSimon Glass 	struct s5p_uart *reg;  /* address of registers in physical memory */
34d4ec8f08SRajeshwari Shinde 	u8 port_id;     /* uart port number */
3573e256c2SSimon Glass };
3646a3b5c8SMinkyu Kang 
3746a3b5c8SMinkyu Kang /*
3846a3b5c8SMinkyu Kang  * The coefficient, used to calculate the baudrate on S5P UARTs is
3946a3b5c8SMinkyu Kang  * calculated as
4046a3b5c8SMinkyu Kang  * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
4146a3b5c8SMinkyu Kang  * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
4246a3b5c8SMinkyu Kang  * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
4346a3b5c8SMinkyu Kang  */
4446a3b5c8SMinkyu Kang static const int udivslot[] = {
4546a3b5c8SMinkyu Kang 	0,
4646a3b5c8SMinkyu Kang 	0x0080,
4746a3b5c8SMinkyu Kang 	0x0808,
4846a3b5c8SMinkyu Kang 	0x0888,
4946a3b5c8SMinkyu Kang 	0x2222,
5046a3b5c8SMinkyu Kang 	0x4924,
5146a3b5c8SMinkyu Kang 	0x4a52,
5246a3b5c8SMinkyu Kang 	0x54aa,
5346a3b5c8SMinkyu Kang 	0x5555,
5446a3b5c8SMinkyu Kang 	0xd555,
5546a3b5c8SMinkyu Kang 	0xd5d5,
5646a3b5c8SMinkyu Kang 	0xddd5,
5746a3b5c8SMinkyu Kang 	0xdddd,
5846a3b5c8SMinkyu Kang 	0xdfdd,
5946a3b5c8SMinkyu Kang 	0xdfdf,
6046a3b5c8SMinkyu Kang 	0xffdf,
6146a3b5c8SMinkyu Kang };
6246a3b5c8SMinkyu Kang 
s5p_serial_init(struct s5p_uart * uart)6389ca9351SSimon Glass static void __maybe_unused s5p_serial_init(struct s5p_uart *uart)
6446a3b5c8SMinkyu Kang {
6589ca9351SSimon Glass 	/* enable FIFOs, auto clear Rx FIFO */
6689ca9351SSimon Glass 	writel(0x3, &uart->ufcon);
6789ca9351SSimon Glass 	writel(0, &uart->umcon);
6889ca9351SSimon Glass 	/* 8N1 */
6989ca9351SSimon Glass 	writel(0x3, &uart->ulcon);
7089ca9351SSimon Glass 	/* No interrupts, no DMA, pure polling */
7189ca9351SSimon Glass 	writel(0x245, &uart->ucon);
7289ca9351SSimon Glass }
7389ca9351SSimon Glass 
s5p_serial_baud(struct s5p_uart * uart,uint uclk,int baudrate)7489ca9351SSimon Glass static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk,
7589ca9351SSimon Glass 					   int baudrate)
7689ca9351SSimon Glass {
7746a3b5c8SMinkyu Kang 	u32 val;
7846a3b5c8SMinkyu Kang 
79f70409afSMinkyu Kang 	val = uclk / baudrate;
8046a3b5c8SMinkyu Kang 
8146a3b5c8SMinkyu Kang 	writel(val / 16 - 1, &uart->ubrdiv);
821628cfc4SMinkyu Kang 
83e0617c62SMinkyu Kang 	if (s5p_uart_divslot())
841628cfc4SMinkyu Kang 		writew(udivslot[val % 16], &uart->rest.slot);
851628cfc4SMinkyu Kang 	else
861628cfc4SMinkyu Kang 		writeb(val % 16, &uart->rest.value);
8789ca9351SSimon Glass }
8889ca9351SSimon Glass 
897fb57396SSimon Glass #ifndef CONFIG_SPL_BUILD
s5p_serial_setbrg(struct udevice * dev,int baudrate)9089ca9351SSimon Glass int s5p_serial_setbrg(struct udevice *dev, int baudrate)
9189ca9351SSimon Glass {
9289ca9351SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
9389ca9351SSimon Glass 	struct s5p_uart *const uart = plat->reg;
94cf75cdf9SThomas Abraham 	u32 uclk;
95cf75cdf9SThomas Abraham 
96cf75cdf9SThomas Abraham #ifdef CONFIG_CLK_EXYNOS
97135aa950SStephen Warren 	struct clk clk;
98cf75cdf9SThomas Abraham 	u32 ret;
99cf75cdf9SThomas Abraham 
100135aa950SStephen Warren 	ret = clk_get_by_index(dev, 1, &clk);
101cf75cdf9SThomas Abraham 	if (ret < 0)
102cf75cdf9SThomas Abraham 		return ret;
103135aa950SStephen Warren 	uclk = clk_get_rate(&clk);
104cf75cdf9SThomas Abraham #else
105cf75cdf9SThomas Abraham 	uclk = get_uart_clk(plat->port_id);
106cf75cdf9SThomas Abraham #endif
10789ca9351SSimon Glass 
10889ca9351SSimon Glass 	s5p_serial_baud(uart, uclk, baudrate);
10973e256c2SSimon Glass 
11073e256c2SSimon Glass 	return 0;
11146a3b5c8SMinkyu Kang }
11246a3b5c8SMinkyu Kang 
s5p_serial_probe(struct udevice * dev)11373e256c2SSimon Glass static int s5p_serial_probe(struct udevice *dev)
11446a3b5c8SMinkyu Kang {
11573e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
11673e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
11746a3b5c8SMinkyu Kang 
11889ca9351SSimon Glass 	s5p_serial_init(uart);
11946a3b5c8SMinkyu Kang 
12046a3b5c8SMinkyu Kang 	return 0;
12146a3b5c8SMinkyu Kang }
12246a3b5c8SMinkyu Kang 
serial_err_check(const struct s5p_uart * const uart,int op)12373e256c2SSimon Glass static int serial_err_check(const struct s5p_uart *const uart, int op)
12446a3b5c8SMinkyu Kang {
12546a3b5c8SMinkyu Kang 	unsigned int mask;
12646a3b5c8SMinkyu Kang 
12746a3b5c8SMinkyu Kang 	/*
12846a3b5c8SMinkyu Kang 	 * UERSTAT
12946a3b5c8SMinkyu Kang 	 * Break Detect	[3]
13046a3b5c8SMinkyu Kang 	 * Frame Err	[2] : receive operation
13146a3b5c8SMinkyu Kang 	 * Parity Err	[1] : receive operation
13246a3b5c8SMinkyu Kang 	 * Overrun Err	[0] : receive operation
13346a3b5c8SMinkyu Kang 	 */
13446a3b5c8SMinkyu Kang 	if (op)
13546a3b5c8SMinkyu Kang 		mask = 0x8;
13646a3b5c8SMinkyu Kang 	else
13746a3b5c8SMinkyu Kang 		mask = 0xf;
13846a3b5c8SMinkyu Kang 
13946a3b5c8SMinkyu Kang 	return readl(&uart->uerstat) & mask;
14046a3b5c8SMinkyu Kang }
14146a3b5c8SMinkyu Kang 
s5p_serial_getc(struct udevice * dev)14273e256c2SSimon Glass static int s5p_serial_getc(struct udevice *dev)
14346a3b5c8SMinkyu Kang {
14473e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
14573e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
14646a3b5c8SMinkyu Kang 
14773e256c2SSimon Glass 	if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK))
14873e256c2SSimon Glass 		return -EAGAIN;
149d4ec8f08SRajeshwari Shinde 
15073e256c2SSimon Glass 	serial_err_check(uart, 0);
1511a4106ddSMinkyu Kang 	return (int)(readb(&uart->urxh) & 0xff);
15246a3b5c8SMinkyu Kang }
15346a3b5c8SMinkyu Kang 
s5p_serial_putc(struct udevice * dev,const char ch)15473e256c2SSimon Glass static int s5p_serial_putc(struct udevice *dev, const char ch)
15546a3b5c8SMinkyu Kang {
15673e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
15773e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
15846a3b5c8SMinkyu Kang 
15973e256c2SSimon Glass 	if (readl(&uart->ufstat) & TX_FIFO_FULL)
16073e256c2SSimon Glass 		return -EAGAIN;
161d4ec8f08SRajeshwari Shinde 
16273e256c2SSimon Glass 	writeb(ch, &uart->utxh);
16373e256c2SSimon Glass 	serial_err_check(uart, 1);
164d4ec8f08SRajeshwari Shinde 
165d4ec8f08SRajeshwari Shinde 	return 0;
166d4ec8f08SRajeshwari Shinde }
167d4ec8f08SRajeshwari Shinde 
s5p_serial_pending(struct udevice * dev,bool input)16873e256c2SSimon Glass static int s5p_serial_pending(struct udevice *dev, bool input)
1696c768ca7SMike Frysinger {
17073e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
17173e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
17273e256c2SSimon Glass 	uint32_t ufstat = readl(&uart->ufstat);
173d4ec8f08SRajeshwari Shinde 
17473e256c2SSimon Glass 	if (input)
17573e256c2SSimon Glass 		return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT;
17673e256c2SSimon Glass 	else
17773e256c2SSimon Glass 		return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT;
178d4ec8f08SRajeshwari Shinde }
179d4ec8f08SRajeshwari Shinde 
s5p_serial_ofdata_to_platdata(struct udevice * dev)18073e256c2SSimon Glass static int s5p_serial_ofdata_to_platdata(struct udevice *dev)
181b4980515SMarek Vasut {
18273e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
18373e256c2SSimon Glass 	fdt_addr_t addr;
18473e256c2SSimon Glass 
185*a821c4afSSimon Glass 	addr = devfdt_get_addr(dev);
18673e256c2SSimon Glass 	if (addr == FDT_ADDR_T_NONE)
18773e256c2SSimon Glass 		return -EINVAL;
18873e256c2SSimon Glass 
18973e256c2SSimon Glass 	plat->reg = (struct s5p_uart *)addr;
190e160f7d4SSimon Glass 	plat->port_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
1915ab6c4dfSThomas Abraham 					"id", dev->seq);
19273e256c2SSimon Glass 	return 0;
193b4980515SMarek Vasut }
19473e256c2SSimon Glass 
19573e256c2SSimon Glass static const struct dm_serial_ops s5p_serial_ops = {
19673e256c2SSimon Glass 	.putc = s5p_serial_putc,
19773e256c2SSimon Glass 	.pending = s5p_serial_pending,
19873e256c2SSimon Glass 	.getc = s5p_serial_getc,
19973e256c2SSimon Glass 	.setbrg = s5p_serial_setbrg,
20073e256c2SSimon Glass };
20173e256c2SSimon Glass 
20273e256c2SSimon Glass static const struct udevice_id s5p_serial_ids[] = {
20373e256c2SSimon Glass 	{ .compatible = "samsung,exynos4210-uart" },
20473e256c2SSimon Glass 	{ }
20573e256c2SSimon Glass };
20673e256c2SSimon Glass 
20773e256c2SSimon Glass U_BOOT_DRIVER(serial_s5p) = {
20873e256c2SSimon Glass 	.name	= "serial_s5p",
20973e256c2SSimon Glass 	.id	= UCLASS_SERIAL,
21073e256c2SSimon Glass 	.of_match = s5p_serial_ids,
21173e256c2SSimon Glass 	.ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
21273e256c2SSimon Glass 	.platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
21373e256c2SSimon Glass 	.probe = s5p_serial_probe,
21473e256c2SSimon Glass 	.ops	= &s5p_serial_ops,
21573e256c2SSimon Glass 	.flags = DM_FLAG_PRE_RELOC,
21673e256c2SSimon Glass };
2177fb57396SSimon Glass #endif
218bf6e7022SSimon Glass 
219bf6e7022SSimon Glass #ifdef CONFIG_DEBUG_UART_S5P
220bf6e7022SSimon Glass 
221bf6e7022SSimon Glass #include <debug_uart.h>
222bf6e7022SSimon Glass 
_debug_uart_init(void)22397b05973SSimon Glass static inline void _debug_uart_init(void)
224bf6e7022SSimon Glass {
225bf6e7022SSimon Glass 	struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE;
226bf6e7022SSimon Glass 
227bf6e7022SSimon Glass 	s5p_serial_init(uart);
228bf6e7022SSimon Glass 	s5p_serial_baud(uart, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
229bf6e7022SSimon Glass }
230bf6e7022SSimon Glass 
_debug_uart_putc(int ch)231bf6e7022SSimon Glass static inline void _debug_uart_putc(int ch)
232bf6e7022SSimon Glass {
233bf6e7022SSimon Glass 	struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE;
234bf6e7022SSimon Glass 
235bf6e7022SSimon Glass 	while (readl(&uart->ufstat) & TX_FIFO_FULL);
236bf6e7022SSimon Glass 
237bf6e7022SSimon Glass 	writeb(ch, &uart->utxh);
238bf6e7022SSimon Glass }
239bf6e7022SSimon Glass 
240bf6e7022SSimon Glass DEBUG_UART_FUNCS
241bf6e7022SSimon Glass 
242bf6e7022SSimon Glass #endif
243