xref: /rk3399_rockchip-uboot/drivers/serial/serial_s5p.c (revision e6252fab2d6989025bfb97e33abca03b5cb10ff1)
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>
12d4ec8f08SRajeshwari Shinde #include <fdtdec.h>
136c768ca7SMike Frysinger #include <linux/compiler.h>
1446a3b5c8SMinkyu Kang #include <asm/io.h>
1546a3b5c8SMinkyu Kang #include <asm/arch/uart.h>
1646a3b5c8SMinkyu Kang #include <asm/arch/clk.h>
1746a3b5c8SMinkyu Kang #include <serial.h>
1846a3b5c8SMinkyu Kang 
1929565326SJohn Rigby DECLARE_GLOBAL_DATA_PTR;
2029565326SJohn Rigby 
21ffbff1ddSAkshay Saraswat #define RX_FIFO_COUNT_MASK	0xff
22ffbff1ddSAkshay Saraswat #define RX_FIFO_FULL_MASK	(1 << 8)
23ffbff1ddSAkshay Saraswat #define TX_FIFO_FULL_MASK	(1 << 24)
24ffbff1ddSAkshay Saraswat 
25d4ec8f08SRajeshwari Shinde /* Information about a serial port */
26d4ec8f08SRajeshwari Shinde struct fdt_serial {
27d4ec8f08SRajeshwari Shinde 	u32 base_addr;  /* address of registers in physical memory */
28d4ec8f08SRajeshwari Shinde 	u8 port_id;     /* uart port number */
29d4ec8f08SRajeshwari Shinde 	u8 enabled;     /* 1 if enabled, 0 if disabled */
30d4ec8f08SRajeshwari Shinde } config __attribute__ ((section(".data")));
31d4ec8f08SRajeshwari Shinde 
3246a3b5c8SMinkyu Kang static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
3346a3b5c8SMinkyu Kang {
34d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL
35d4ec8f08SRajeshwari Shinde 	return (struct s5p_uart *)(config.base_addr);
36d4ec8f08SRajeshwari Shinde #else
3746a3b5c8SMinkyu Kang 	u32 offset = dev_index * sizeof(struct s5p_uart);
38d93d0f0cSMinkyu Kang 	return (struct s5p_uart *)(samsung_get_base_uart() + offset);
39d4ec8f08SRajeshwari Shinde #endif
4046a3b5c8SMinkyu Kang }
4146a3b5c8SMinkyu Kang 
4246a3b5c8SMinkyu Kang /*
4346a3b5c8SMinkyu Kang  * The coefficient, used to calculate the baudrate on S5P UARTs is
4446a3b5c8SMinkyu Kang  * calculated as
4546a3b5c8SMinkyu Kang  * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
4646a3b5c8SMinkyu Kang  * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
4746a3b5c8SMinkyu Kang  * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
4846a3b5c8SMinkyu Kang  */
4946a3b5c8SMinkyu Kang static const int udivslot[] = {
5046a3b5c8SMinkyu Kang 	0,
5146a3b5c8SMinkyu Kang 	0x0080,
5246a3b5c8SMinkyu Kang 	0x0808,
5346a3b5c8SMinkyu Kang 	0x0888,
5446a3b5c8SMinkyu Kang 	0x2222,
5546a3b5c8SMinkyu Kang 	0x4924,
5646a3b5c8SMinkyu Kang 	0x4a52,
5746a3b5c8SMinkyu Kang 	0x54aa,
5846a3b5c8SMinkyu Kang 	0x5555,
5946a3b5c8SMinkyu Kang 	0xd555,
6046a3b5c8SMinkyu Kang 	0xd5d5,
6146a3b5c8SMinkyu Kang 	0xddd5,
6246a3b5c8SMinkyu Kang 	0xdddd,
6346a3b5c8SMinkyu Kang 	0xdfdd,
6446a3b5c8SMinkyu Kang 	0xdfdf,
6546a3b5c8SMinkyu Kang 	0xffdf,
6646a3b5c8SMinkyu Kang };
6746a3b5c8SMinkyu Kang 
68a81630e0SAxel Lin static void serial_setbrg_dev(const int dev_index)
6946a3b5c8SMinkyu Kang {
7046a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
71f70409afSMinkyu Kang 	u32 uclk = get_uart_clk(dev_index);
7246a3b5c8SMinkyu Kang 	u32 baudrate = gd->baudrate;
7346a3b5c8SMinkyu Kang 	u32 val;
7446a3b5c8SMinkyu Kang 
75d4ec8f08SRajeshwari Shinde #if defined(CONFIG_SILENT_CONSOLE) && \
76d4ec8f08SRajeshwari Shinde 		defined(CONFIG_OF_CONTROL) && \
77d4ec8f08SRajeshwari Shinde 		!defined(CONFIG_SPL_BUILD)
78d4ec8f08SRajeshwari Shinde 	if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0))
79d4ec8f08SRajeshwari Shinde 		gd->flags |= GD_FLG_SILENT;
80d4ec8f08SRajeshwari Shinde #endif
81d4ec8f08SRajeshwari Shinde 
82d4ec8f08SRajeshwari Shinde 	if (!config.enabled)
83d4ec8f08SRajeshwari Shinde 		return;
84d4ec8f08SRajeshwari Shinde 
85f70409afSMinkyu Kang 	val = uclk / baudrate;
8646a3b5c8SMinkyu Kang 
8746a3b5c8SMinkyu Kang 	writel(val / 16 - 1, &uart->ubrdiv);
881628cfc4SMinkyu Kang 
89e0617c62SMinkyu Kang 	if (s5p_uart_divslot())
901628cfc4SMinkyu Kang 		writew(udivslot[val % 16], &uart->rest.slot);
911628cfc4SMinkyu Kang 	else
921628cfc4SMinkyu Kang 		writeb(val % 16, &uart->rest.value);
9346a3b5c8SMinkyu Kang }
9446a3b5c8SMinkyu Kang 
9546a3b5c8SMinkyu Kang /*
9646a3b5c8SMinkyu Kang  * Initialise the serial port with the given baudrate. The settings
9746a3b5c8SMinkyu Kang  * are always 8 data bits, no parity, 1 stop bit, no start bits.
9846a3b5c8SMinkyu Kang  */
99a81630e0SAxel Lin static int serial_init_dev(const int dev_index)
10046a3b5c8SMinkyu Kang {
10146a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
10246a3b5c8SMinkyu Kang 
103*e6252fabSInha Song 	/* enable FIFOs, auto clear Rx FIFO */
104*e6252fabSInha Song 	writel(0x3, &uart->ufcon);
10546a3b5c8SMinkyu Kang 	writel(0, &uart->umcon);
10646a3b5c8SMinkyu Kang 	/* 8N1 */
10746a3b5c8SMinkyu Kang 	writel(0x3, &uart->ulcon);
10846a3b5c8SMinkyu Kang 	/* No interrupts, no DMA, pure polling */
10946a3b5c8SMinkyu Kang 	writel(0x245, &uart->ucon);
11046a3b5c8SMinkyu Kang 
11146a3b5c8SMinkyu Kang 	serial_setbrg_dev(dev_index);
11246a3b5c8SMinkyu Kang 
11346a3b5c8SMinkyu Kang 	return 0;
11446a3b5c8SMinkyu Kang }
11546a3b5c8SMinkyu Kang 
11646a3b5c8SMinkyu Kang static int serial_err_check(const int dev_index, int op)
11746a3b5c8SMinkyu Kang {
11846a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
11946a3b5c8SMinkyu Kang 	unsigned int mask;
12046a3b5c8SMinkyu Kang 
12146a3b5c8SMinkyu Kang 	/*
12246a3b5c8SMinkyu Kang 	 * UERSTAT
12346a3b5c8SMinkyu Kang 	 * Break Detect	[3]
12446a3b5c8SMinkyu Kang 	 * Frame Err	[2] : receive operation
12546a3b5c8SMinkyu Kang 	 * Parity Err	[1] : receive operation
12646a3b5c8SMinkyu Kang 	 * Overrun Err	[0] : receive operation
12746a3b5c8SMinkyu Kang 	 */
12846a3b5c8SMinkyu Kang 	if (op)
12946a3b5c8SMinkyu Kang 		mask = 0x8;
13046a3b5c8SMinkyu Kang 	else
13146a3b5c8SMinkyu Kang 		mask = 0xf;
13246a3b5c8SMinkyu Kang 
13346a3b5c8SMinkyu Kang 	return readl(&uart->uerstat) & mask;
13446a3b5c8SMinkyu Kang }
13546a3b5c8SMinkyu Kang 
13646a3b5c8SMinkyu Kang /*
13746a3b5c8SMinkyu Kang  * Read a single byte from the serial port. Returns 1 on success, 0
13846a3b5c8SMinkyu Kang  * otherwise. When the function is succesfull, the character read is
13946a3b5c8SMinkyu Kang  * written into its argument c.
14046a3b5c8SMinkyu Kang  */
141a81630e0SAxel Lin static int serial_getc_dev(const int dev_index)
14246a3b5c8SMinkyu Kang {
14346a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
14446a3b5c8SMinkyu Kang 
145d4ec8f08SRajeshwari Shinde 	if (!config.enabled)
146d4ec8f08SRajeshwari Shinde 		return 0;
147d4ec8f08SRajeshwari Shinde 
14846a3b5c8SMinkyu Kang 	/* wait for character to arrive */
149ffbff1ddSAkshay Saraswat 	while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK |
150ffbff1ddSAkshay Saraswat 					 RX_FIFO_FULL_MASK))) {
15146a3b5c8SMinkyu Kang 		if (serial_err_check(dev_index, 0))
15246a3b5c8SMinkyu Kang 			return 0;
15346a3b5c8SMinkyu Kang 	}
15446a3b5c8SMinkyu Kang 
1551a4106ddSMinkyu Kang 	return (int)(readb(&uart->urxh) & 0xff);
15646a3b5c8SMinkyu Kang }
15746a3b5c8SMinkyu Kang 
15846a3b5c8SMinkyu Kang /*
15946a3b5c8SMinkyu Kang  * Output a single byte to the serial port.
16046a3b5c8SMinkyu Kang  */
161a81630e0SAxel Lin static void serial_putc_dev(const char c, const int dev_index)
16246a3b5c8SMinkyu Kang {
16346a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
16446a3b5c8SMinkyu Kang 
165d4ec8f08SRajeshwari Shinde 	if (!config.enabled)
166d4ec8f08SRajeshwari Shinde 		return;
167d4ec8f08SRajeshwari Shinde 
16846a3b5c8SMinkyu Kang 	/* wait for room in the tx FIFO */
169ffbff1ddSAkshay Saraswat 	while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) {
17046a3b5c8SMinkyu Kang 		if (serial_err_check(dev_index, 1))
17146a3b5c8SMinkyu Kang 			return;
17246a3b5c8SMinkyu Kang 	}
17346a3b5c8SMinkyu Kang 
1741a4106ddSMinkyu Kang 	writeb(c, &uart->utxh);
17546a3b5c8SMinkyu Kang 
17646a3b5c8SMinkyu Kang 	/* If \n, also do \r */
17746a3b5c8SMinkyu Kang 	if (c == '\n')
17846a3b5c8SMinkyu Kang 		serial_putc('\r');
17946a3b5c8SMinkyu Kang }
18046a3b5c8SMinkyu Kang 
18146a3b5c8SMinkyu Kang /*
18246a3b5c8SMinkyu Kang  * Test whether a character is in the RX buffer
18346a3b5c8SMinkyu Kang  */
184a81630e0SAxel Lin static int serial_tstc_dev(const int dev_index)
18546a3b5c8SMinkyu Kang {
18646a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
18746a3b5c8SMinkyu Kang 
188d4ec8f08SRajeshwari Shinde 	if (!config.enabled)
189d4ec8f08SRajeshwari Shinde 		return 0;
190d4ec8f08SRajeshwari Shinde 
19146a3b5c8SMinkyu Kang 	return (int)(readl(&uart->utrstat) & 0x1);
19246a3b5c8SMinkyu Kang }
19346a3b5c8SMinkyu Kang 
194a81630e0SAxel Lin static void serial_puts_dev(const char *s, const int dev_index)
19546a3b5c8SMinkyu Kang {
19646a3b5c8SMinkyu Kang 	while (*s)
19746a3b5c8SMinkyu Kang 		serial_putc_dev(*s++, dev_index);
19846a3b5c8SMinkyu Kang }
19946a3b5c8SMinkyu Kang 
20046a3b5c8SMinkyu Kang /* Multi serial device functions */
20146a3b5c8SMinkyu Kang #define DECLARE_S5P_SERIAL_FUNCTIONS(port) \
202a81630e0SAxel Lin static int s5p_serial##port##_init(void) { return serial_init_dev(port); } \
203a81630e0SAxel Lin static void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \
204a81630e0SAxel Lin static int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \
205a81630e0SAxel Lin static int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \
206a81630e0SAxel Lin static void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \
207a81630e0SAxel Lin static void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); }
20846a3b5c8SMinkyu Kang 
20990bad891SMarek Vasut #define INIT_S5P_SERIAL_STRUCTURE(port, __name) {	\
21090bad891SMarek Vasut 	.name	= __name,				\
21190bad891SMarek Vasut 	.start	= s5p_serial##port##_init,		\
21290bad891SMarek Vasut 	.stop	= NULL,					\
21390bad891SMarek Vasut 	.setbrg	= s5p_serial##port##_setbrg,		\
21490bad891SMarek Vasut 	.getc	= s5p_serial##port##_getc,		\
21590bad891SMarek Vasut 	.tstc	= s5p_serial##port##_tstc,		\
21690bad891SMarek Vasut 	.putc	= s5p_serial##port##_putc,		\
21790bad891SMarek Vasut 	.puts	= s5p_serial##port##_puts,		\
21890bad891SMarek Vasut }
21946a3b5c8SMinkyu Kang 
22046a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(0);
22146a3b5c8SMinkyu Kang struct serial_device s5p_serial0_device =
2221c9a5606SMike Frysinger 	INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0");
22346a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(1);
22446a3b5c8SMinkyu Kang struct serial_device s5p_serial1_device =
2251c9a5606SMike Frysinger 	INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1");
22646a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(2);
22746a3b5c8SMinkyu Kang struct serial_device s5p_serial2_device =
2281c9a5606SMike Frysinger 	INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2");
22946a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(3);
23046a3b5c8SMinkyu Kang struct serial_device s5p_serial3_device =
2311c9a5606SMike Frysinger 	INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3");
2326c768ca7SMike Frysinger 
233d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL
234d4ec8f08SRajeshwari Shinde int fdtdec_decode_console(int *index, struct fdt_serial *uart)
235d4ec8f08SRajeshwari Shinde {
236d4ec8f08SRajeshwari Shinde 	const void *blob = gd->fdt_blob;
237d4ec8f08SRajeshwari Shinde 	int node;
238d4ec8f08SRajeshwari Shinde 
239d4ec8f08SRajeshwari Shinde 	node = fdt_path_offset(blob, "console");
240d4ec8f08SRajeshwari Shinde 	if (node < 0)
241d4ec8f08SRajeshwari Shinde 		return node;
242d4ec8f08SRajeshwari Shinde 
243d4ec8f08SRajeshwari Shinde 	uart->base_addr = fdtdec_get_addr(blob, node, "reg");
244d4ec8f08SRajeshwari Shinde 	if (uart->base_addr == FDT_ADDR_T_NONE)
245d4ec8f08SRajeshwari Shinde 		return -FDT_ERR_NOTFOUND;
246d4ec8f08SRajeshwari Shinde 
247d4ec8f08SRajeshwari Shinde 	uart->port_id = fdtdec_get_int(blob, node, "id", -1);
248d4ec8f08SRajeshwari Shinde 	uart->enabled = fdtdec_get_is_enabled(blob, node);
249d4ec8f08SRajeshwari Shinde 
250d4ec8f08SRajeshwari Shinde 	return 0;
251d4ec8f08SRajeshwari Shinde }
252d4ec8f08SRajeshwari Shinde #endif
253d4ec8f08SRajeshwari Shinde 
2546c768ca7SMike Frysinger __weak struct serial_device *default_serial_console(void)
2556c768ca7SMike Frysinger {
256d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL
257d4ec8f08SRajeshwari Shinde 	int index = 0;
258d4ec8f08SRajeshwari Shinde 
259d4ec8f08SRajeshwari Shinde 	if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) {
260d4ec8f08SRajeshwari Shinde 		debug("Cannot decode default console node\n");
261d4ec8f08SRajeshwari Shinde 		return NULL;
262d4ec8f08SRajeshwari Shinde 	}
263d4ec8f08SRajeshwari Shinde 
264d4ec8f08SRajeshwari Shinde 	switch (config.port_id) {
265d4ec8f08SRajeshwari Shinde 	case 0:
266d4ec8f08SRajeshwari Shinde 		return &s5p_serial0_device;
267d4ec8f08SRajeshwari Shinde 	case 1:
268d4ec8f08SRajeshwari Shinde 		return &s5p_serial1_device;
269d4ec8f08SRajeshwari Shinde 	case 2:
270d4ec8f08SRajeshwari Shinde 		return &s5p_serial2_device;
271d4ec8f08SRajeshwari Shinde 	case 3:
272d4ec8f08SRajeshwari Shinde 		return &s5p_serial3_device;
273d4ec8f08SRajeshwari Shinde 	default:
274d4ec8f08SRajeshwari Shinde 		debug("Unknown config.port_id: %d", config.port_id);
275d4ec8f08SRajeshwari Shinde 		break;
276d4ec8f08SRajeshwari Shinde 	}
277d4ec8f08SRajeshwari Shinde 
278d4ec8f08SRajeshwari Shinde 	return NULL;
279d4ec8f08SRajeshwari Shinde #else
280d4ec8f08SRajeshwari Shinde 	config.enabled = 1;
2816c768ca7SMike Frysinger #if defined(CONFIG_SERIAL0)
2826c768ca7SMike Frysinger 	return &s5p_serial0_device;
2836c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL1)
2846c768ca7SMike Frysinger 	return &s5p_serial1_device;
2856c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL2)
2866c768ca7SMike Frysinger 	return &s5p_serial2_device;
2876c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL3)
2886c768ca7SMike Frysinger 	return &s5p_serial3_device;
2896c768ca7SMike Frysinger #else
2906c768ca7SMike Frysinger #error "CONFIG_SERIAL? missing."
2916c768ca7SMike Frysinger #endif
292d4ec8f08SRajeshwari Shinde #endif
2936c768ca7SMike Frysinger }
294b4980515SMarek Vasut 
295b4980515SMarek Vasut void s5p_serial_initialize(void)
296b4980515SMarek Vasut {
297b4980515SMarek Vasut 	serial_register(&s5p_serial0_device);
298b4980515SMarek Vasut 	serial_register(&s5p_serial1_device);
299b4980515SMarek Vasut 	serial_register(&s5p_serial2_device);
300b4980515SMarek Vasut 	serial_register(&s5p_serial3_device);
301b4980515SMarek Vasut }
302