xref: /rk3399_rockchip-uboot/drivers/serial/serial_s5p.c (revision d4ec8f08856bea73a0def00a7f09dff6a3541604)
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  *
846a3b5c8SMinkyu Kang  * This program is free software; you can redistribute it and/or modify
946a3b5c8SMinkyu Kang  * it under the terms of the GNU General Public License as published by
1046a3b5c8SMinkyu Kang  * the Free Software Foundation; either version 2 of the License, or
1146a3b5c8SMinkyu Kang  * (at your option) any later version.
1246a3b5c8SMinkyu Kang  *
1346a3b5c8SMinkyu Kang  * This program is distributed in the hope that it will be useful,
1446a3b5c8SMinkyu Kang  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1546a3b5c8SMinkyu Kang  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1646a3b5c8SMinkyu Kang  * GNU General Public License for more details.
1746a3b5c8SMinkyu Kang  *
1846a3b5c8SMinkyu Kang  * You should have received a copy of the GNU General Public License
1946a3b5c8SMinkyu Kang  * along with this program; if not, write to the Free Software
2046a3b5c8SMinkyu Kang  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2146a3b5c8SMinkyu Kang  *
2246a3b5c8SMinkyu Kang  */
2346a3b5c8SMinkyu Kang 
2446a3b5c8SMinkyu Kang #include <common.h>
25*d4ec8f08SRajeshwari Shinde #include <fdtdec.h>
266c768ca7SMike Frysinger #include <linux/compiler.h>
2746a3b5c8SMinkyu Kang #include <asm/io.h>
2846a3b5c8SMinkyu Kang #include <asm/arch/uart.h>
2946a3b5c8SMinkyu Kang #include <asm/arch/clk.h>
3046a3b5c8SMinkyu Kang #include <serial.h>
3146a3b5c8SMinkyu Kang 
3229565326SJohn Rigby DECLARE_GLOBAL_DATA_PTR;
3329565326SJohn Rigby 
34ffbff1ddSAkshay Saraswat #define RX_FIFO_COUNT_MASK	0xff
35ffbff1ddSAkshay Saraswat #define RX_FIFO_FULL_MASK	(1 << 8)
36ffbff1ddSAkshay Saraswat #define TX_FIFO_FULL_MASK	(1 << 24)
37ffbff1ddSAkshay Saraswat 
38*d4ec8f08SRajeshwari Shinde /* Information about a serial port */
39*d4ec8f08SRajeshwari Shinde struct fdt_serial {
40*d4ec8f08SRajeshwari Shinde 	u32 base_addr;  /* address of registers in physical memory */
41*d4ec8f08SRajeshwari Shinde 	u8 port_id;     /* uart port number */
42*d4ec8f08SRajeshwari Shinde 	u8 enabled;     /* 1 if enabled, 0 if disabled */
43*d4ec8f08SRajeshwari Shinde } config __attribute__ ((section(".data")));
44*d4ec8f08SRajeshwari Shinde 
4546a3b5c8SMinkyu Kang static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
4646a3b5c8SMinkyu Kang {
47*d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL
48*d4ec8f08SRajeshwari Shinde 	return (struct s5p_uart *)(config.base_addr);
49*d4ec8f08SRajeshwari Shinde #else
5046a3b5c8SMinkyu Kang 	u32 offset = dev_index * sizeof(struct s5p_uart);
51d93d0f0cSMinkyu Kang 	return (struct s5p_uart *)(samsung_get_base_uart() + offset);
52*d4ec8f08SRajeshwari Shinde #endif
5346a3b5c8SMinkyu Kang }
5446a3b5c8SMinkyu Kang 
5546a3b5c8SMinkyu Kang /*
5646a3b5c8SMinkyu Kang  * The coefficient, used to calculate the baudrate on S5P UARTs is
5746a3b5c8SMinkyu Kang  * calculated as
5846a3b5c8SMinkyu Kang  * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
5946a3b5c8SMinkyu Kang  * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
6046a3b5c8SMinkyu Kang  * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
6146a3b5c8SMinkyu Kang  */
6246a3b5c8SMinkyu Kang static const int udivslot[] = {
6346a3b5c8SMinkyu Kang 	0,
6446a3b5c8SMinkyu Kang 	0x0080,
6546a3b5c8SMinkyu Kang 	0x0808,
6646a3b5c8SMinkyu Kang 	0x0888,
6746a3b5c8SMinkyu Kang 	0x2222,
6846a3b5c8SMinkyu Kang 	0x4924,
6946a3b5c8SMinkyu Kang 	0x4a52,
7046a3b5c8SMinkyu Kang 	0x54aa,
7146a3b5c8SMinkyu Kang 	0x5555,
7246a3b5c8SMinkyu Kang 	0xd555,
7346a3b5c8SMinkyu Kang 	0xd5d5,
7446a3b5c8SMinkyu Kang 	0xddd5,
7546a3b5c8SMinkyu Kang 	0xdddd,
7646a3b5c8SMinkyu Kang 	0xdfdd,
7746a3b5c8SMinkyu Kang 	0xdfdf,
7846a3b5c8SMinkyu Kang 	0xffdf,
7946a3b5c8SMinkyu Kang };
8046a3b5c8SMinkyu Kang 
8146a3b5c8SMinkyu Kang void serial_setbrg_dev(const int dev_index)
8246a3b5c8SMinkyu Kang {
8346a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
84f70409afSMinkyu Kang 	u32 uclk = get_uart_clk(dev_index);
8546a3b5c8SMinkyu Kang 	u32 baudrate = gd->baudrate;
8646a3b5c8SMinkyu Kang 	u32 val;
8746a3b5c8SMinkyu Kang 
88*d4ec8f08SRajeshwari Shinde #if defined(CONFIG_SILENT_CONSOLE) && \
89*d4ec8f08SRajeshwari Shinde 		defined(CONFIG_OF_CONTROL) && \
90*d4ec8f08SRajeshwari Shinde 		!defined(CONFIG_SPL_BUILD)
91*d4ec8f08SRajeshwari Shinde 	if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0))
92*d4ec8f08SRajeshwari Shinde 		gd->flags |= GD_FLG_SILENT;
93*d4ec8f08SRajeshwari Shinde #endif
94*d4ec8f08SRajeshwari Shinde 
95*d4ec8f08SRajeshwari Shinde 	if (!config.enabled)
96*d4ec8f08SRajeshwari Shinde 		return;
97*d4ec8f08SRajeshwari Shinde 
98f70409afSMinkyu Kang 	val = uclk / baudrate;
9946a3b5c8SMinkyu Kang 
10046a3b5c8SMinkyu Kang 	writel(val / 16 - 1, &uart->ubrdiv);
1011628cfc4SMinkyu Kang 
102e0617c62SMinkyu Kang 	if (s5p_uart_divslot())
1031628cfc4SMinkyu Kang 		writew(udivslot[val % 16], &uart->rest.slot);
1041628cfc4SMinkyu Kang 	else
1051628cfc4SMinkyu Kang 		writeb(val % 16, &uart->rest.value);
10646a3b5c8SMinkyu Kang }
10746a3b5c8SMinkyu Kang 
10846a3b5c8SMinkyu Kang /*
10946a3b5c8SMinkyu Kang  * Initialise the serial port with the given baudrate. The settings
11046a3b5c8SMinkyu Kang  * are always 8 data bits, no parity, 1 stop bit, no start bits.
11146a3b5c8SMinkyu Kang  */
11246a3b5c8SMinkyu Kang int serial_init_dev(const int dev_index)
11346a3b5c8SMinkyu Kang {
11446a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
11546a3b5c8SMinkyu Kang 
116ffbff1ddSAkshay Saraswat 	/* enable FIFOs */
117ffbff1ddSAkshay Saraswat 	writel(0x1, &uart->ufcon);
11846a3b5c8SMinkyu Kang 	writel(0, &uart->umcon);
11946a3b5c8SMinkyu Kang 	/* 8N1 */
12046a3b5c8SMinkyu Kang 	writel(0x3, &uart->ulcon);
12146a3b5c8SMinkyu Kang 	/* No interrupts, no DMA, pure polling */
12246a3b5c8SMinkyu Kang 	writel(0x245, &uart->ucon);
12346a3b5c8SMinkyu Kang 
12446a3b5c8SMinkyu Kang 	serial_setbrg_dev(dev_index);
12546a3b5c8SMinkyu Kang 
12646a3b5c8SMinkyu Kang 	return 0;
12746a3b5c8SMinkyu Kang }
12846a3b5c8SMinkyu Kang 
12946a3b5c8SMinkyu Kang static int serial_err_check(const int dev_index, int op)
13046a3b5c8SMinkyu Kang {
13146a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
13246a3b5c8SMinkyu Kang 	unsigned int mask;
13346a3b5c8SMinkyu Kang 
13446a3b5c8SMinkyu Kang 	/*
13546a3b5c8SMinkyu Kang 	 * UERSTAT
13646a3b5c8SMinkyu Kang 	 * Break Detect	[3]
13746a3b5c8SMinkyu Kang 	 * Frame Err	[2] : receive operation
13846a3b5c8SMinkyu Kang 	 * Parity Err	[1] : receive operation
13946a3b5c8SMinkyu Kang 	 * Overrun Err	[0] : receive operation
14046a3b5c8SMinkyu Kang 	 */
14146a3b5c8SMinkyu Kang 	if (op)
14246a3b5c8SMinkyu Kang 		mask = 0x8;
14346a3b5c8SMinkyu Kang 	else
14446a3b5c8SMinkyu Kang 		mask = 0xf;
14546a3b5c8SMinkyu Kang 
14646a3b5c8SMinkyu Kang 	return readl(&uart->uerstat) & mask;
14746a3b5c8SMinkyu Kang }
14846a3b5c8SMinkyu Kang 
14946a3b5c8SMinkyu Kang /*
15046a3b5c8SMinkyu Kang  * Read a single byte from the serial port. Returns 1 on success, 0
15146a3b5c8SMinkyu Kang  * otherwise. When the function is succesfull, the character read is
15246a3b5c8SMinkyu Kang  * written into its argument c.
15346a3b5c8SMinkyu Kang  */
15446a3b5c8SMinkyu Kang int serial_getc_dev(const int dev_index)
15546a3b5c8SMinkyu Kang {
15646a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
15746a3b5c8SMinkyu Kang 
158*d4ec8f08SRajeshwari Shinde 	if (!config.enabled)
159*d4ec8f08SRajeshwari Shinde 		return 0;
160*d4ec8f08SRajeshwari Shinde 
16146a3b5c8SMinkyu Kang 	/* wait for character to arrive */
162ffbff1ddSAkshay Saraswat 	while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK |
163ffbff1ddSAkshay Saraswat 					 RX_FIFO_FULL_MASK))) {
16446a3b5c8SMinkyu Kang 		if (serial_err_check(dev_index, 0))
16546a3b5c8SMinkyu Kang 			return 0;
16646a3b5c8SMinkyu Kang 	}
16746a3b5c8SMinkyu Kang 
1681a4106ddSMinkyu Kang 	return (int)(readb(&uart->urxh) & 0xff);
16946a3b5c8SMinkyu Kang }
17046a3b5c8SMinkyu Kang 
17146a3b5c8SMinkyu Kang /*
17246a3b5c8SMinkyu Kang  * Output a single byte to the serial port.
17346a3b5c8SMinkyu Kang  */
17446a3b5c8SMinkyu Kang void serial_putc_dev(const char c, const int dev_index)
17546a3b5c8SMinkyu Kang {
17646a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
17746a3b5c8SMinkyu Kang 
178*d4ec8f08SRajeshwari Shinde 	if (!config.enabled)
179*d4ec8f08SRajeshwari Shinde 		return;
180*d4ec8f08SRajeshwari Shinde 
18146a3b5c8SMinkyu Kang 	/* wait for room in the tx FIFO */
182ffbff1ddSAkshay Saraswat 	while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) {
18346a3b5c8SMinkyu Kang 		if (serial_err_check(dev_index, 1))
18446a3b5c8SMinkyu Kang 			return;
18546a3b5c8SMinkyu Kang 	}
18646a3b5c8SMinkyu Kang 
1871a4106ddSMinkyu Kang 	writeb(c, &uart->utxh);
18846a3b5c8SMinkyu Kang 
18946a3b5c8SMinkyu Kang 	/* If \n, also do \r */
19046a3b5c8SMinkyu Kang 	if (c == '\n')
19146a3b5c8SMinkyu Kang 		serial_putc('\r');
19246a3b5c8SMinkyu Kang }
19346a3b5c8SMinkyu Kang 
19446a3b5c8SMinkyu Kang /*
19546a3b5c8SMinkyu Kang  * Test whether a character is in the RX buffer
19646a3b5c8SMinkyu Kang  */
19746a3b5c8SMinkyu Kang int serial_tstc_dev(const int dev_index)
19846a3b5c8SMinkyu Kang {
19946a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
20046a3b5c8SMinkyu Kang 
201*d4ec8f08SRajeshwari Shinde 	if (!config.enabled)
202*d4ec8f08SRajeshwari Shinde 		return 0;
203*d4ec8f08SRajeshwari Shinde 
20446a3b5c8SMinkyu Kang 	return (int)(readl(&uart->utrstat) & 0x1);
20546a3b5c8SMinkyu Kang }
20646a3b5c8SMinkyu Kang 
20746a3b5c8SMinkyu Kang void serial_puts_dev(const char *s, const int dev_index)
20846a3b5c8SMinkyu Kang {
20946a3b5c8SMinkyu Kang 	while (*s)
21046a3b5c8SMinkyu Kang 		serial_putc_dev(*s++, dev_index);
21146a3b5c8SMinkyu Kang }
21246a3b5c8SMinkyu Kang 
21346a3b5c8SMinkyu Kang /* Multi serial device functions */
21446a3b5c8SMinkyu Kang #define DECLARE_S5P_SERIAL_FUNCTIONS(port) \
21546a3b5c8SMinkyu Kang int s5p_serial##port##_init(void) { return serial_init_dev(port); } \
21646a3b5c8SMinkyu Kang void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \
21746a3b5c8SMinkyu Kang int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \
21846a3b5c8SMinkyu Kang int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \
21946a3b5c8SMinkyu Kang void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \
22046a3b5c8SMinkyu Kang void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); }
22146a3b5c8SMinkyu Kang 
22290bad891SMarek Vasut #define INIT_S5P_SERIAL_STRUCTURE(port, __name) {	\
22390bad891SMarek Vasut 	.name	= __name,				\
22490bad891SMarek Vasut 	.start	= s5p_serial##port##_init,		\
22590bad891SMarek Vasut 	.stop	= NULL,					\
22690bad891SMarek Vasut 	.setbrg	= s5p_serial##port##_setbrg,		\
22790bad891SMarek Vasut 	.getc	= s5p_serial##port##_getc,		\
22890bad891SMarek Vasut 	.tstc	= s5p_serial##port##_tstc,		\
22990bad891SMarek Vasut 	.putc	= s5p_serial##port##_putc,		\
23090bad891SMarek Vasut 	.puts	= s5p_serial##port##_puts,		\
23190bad891SMarek Vasut }
23246a3b5c8SMinkyu Kang 
23346a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(0);
23446a3b5c8SMinkyu Kang struct serial_device s5p_serial0_device =
2351c9a5606SMike Frysinger 	INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0");
23646a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(1);
23746a3b5c8SMinkyu Kang struct serial_device s5p_serial1_device =
2381c9a5606SMike Frysinger 	INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1");
23946a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(2);
24046a3b5c8SMinkyu Kang struct serial_device s5p_serial2_device =
2411c9a5606SMike Frysinger 	INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2");
24246a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(3);
24346a3b5c8SMinkyu Kang struct serial_device s5p_serial3_device =
2441c9a5606SMike Frysinger 	INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3");
2456c768ca7SMike Frysinger 
246*d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL
247*d4ec8f08SRajeshwari Shinde int fdtdec_decode_console(int *index, struct fdt_serial *uart)
248*d4ec8f08SRajeshwari Shinde {
249*d4ec8f08SRajeshwari Shinde 	const void *blob = gd->fdt_blob;
250*d4ec8f08SRajeshwari Shinde 	int node;
251*d4ec8f08SRajeshwari Shinde 
252*d4ec8f08SRajeshwari Shinde 	node = fdt_path_offset(blob, "console");
253*d4ec8f08SRajeshwari Shinde 	if (node < 0)
254*d4ec8f08SRajeshwari Shinde 		return node;
255*d4ec8f08SRajeshwari Shinde 
256*d4ec8f08SRajeshwari Shinde 	uart->base_addr = fdtdec_get_addr(blob, node, "reg");
257*d4ec8f08SRajeshwari Shinde 	if (uart->base_addr == FDT_ADDR_T_NONE)
258*d4ec8f08SRajeshwari Shinde 		return -FDT_ERR_NOTFOUND;
259*d4ec8f08SRajeshwari Shinde 
260*d4ec8f08SRajeshwari Shinde 	uart->port_id = fdtdec_get_int(blob, node, "id", -1);
261*d4ec8f08SRajeshwari Shinde 	uart->enabled = fdtdec_get_is_enabled(blob, node);
262*d4ec8f08SRajeshwari Shinde 
263*d4ec8f08SRajeshwari Shinde 	return 0;
264*d4ec8f08SRajeshwari Shinde }
265*d4ec8f08SRajeshwari Shinde #endif
266*d4ec8f08SRajeshwari Shinde 
2676c768ca7SMike Frysinger __weak struct serial_device *default_serial_console(void)
2686c768ca7SMike Frysinger {
269*d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL
270*d4ec8f08SRajeshwari Shinde 	int index = 0;
271*d4ec8f08SRajeshwari Shinde 
272*d4ec8f08SRajeshwari Shinde 	if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) {
273*d4ec8f08SRajeshwari Shinde 		debug("Cannot decode default console node\n");
274*d4ec8f08SRajeshwari Shinde 		return NULL;
275*d4ec8f08SRajeshwari Shinde 	}
276*d4ec8f08SRajeshwari Shinde 
277*d4ec8f08SRajeshwari Shinde 	switch (config.port_id) {
278*d4ec8f08SRajeshwari Shinde 	case 0:
279*d4ec8f08SRajeshwari Shinde 		return &s5p_serial0_device;
280*d4ec8f08SRajeshwari Shinde 	case 1:
281*d4ec8f08SRajeshwari Shinde 		return &s5p_serial1_device;
282*d4ec8f08SRajeshwari Shinde 	case 2:
283*d4ec8f08SRajeshwari Shinde 		return &s5p_serial2_device;
284*d4ec8f08SRajeshwari Shinde 	case 3:
285*d4ec8f08SRajeshwari Shinde 		return &s5p_serial3_device;
286*d4ec8f08SRajeshwari Shinde 	default:
287*d4ec8f08SRajeshwari Shinde 		debug("Unknown config.port_id: %d", config.port_id);
288*d4ec8f08SRajeshwari Shinde 		break;
289*d4ec8f08SRajeshwari Shinde 	}
290*d4ec8f08SRajeshwari Shinde 
291*d4ec8f08SRajeshwari Shinde 	return NULL;
292*d4ec8f08SRajeshwari Shinde #else
293*d4ec8f08SRajeshwari Shinde 	config.enabled = 1;
2946c768ca7SMike Frysinger #if defined(CONFIG_SERIAL0)
2956c768ca7SMike Frysinger 	return &s5p_serial0_device;
2966c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL1)
2976c768ca7SMike Frysinger 	return &s5p_serial1_device;
2986c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL2)
2996c768ca7SMike Frysinger 	return &s5p_serial2_device;
3006c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL3)
3016c768ca7SMike Frysinger 	return &s5p_serial3_device;
3026c768ca7SMike Frysinger #else
3036c768ca7SMike Frysinger #error "CONFIG_SERIAL? missing."
3046c768ca7SMike Frysinger #endif
305*d4ec8f08SRajeshwari Shinde #endif
3066c768ca7SMike Frysinger }
307b4980515SMarek Vasut 
308b4980515SMarek Vasut void s5p_serial_initialize(void)
309b4980515SMarek Vasut {
310b4980515SMarek Vasut 	serial_register(&s5p_serial0_device);
311b4980515SMarek Vasut 	serial_register(&s5p_serial1_device);
312b4980515SMarek Vasut 	serial_register(&s5p_serial2_device);
313b4980515SMarek Vasut 	serial_register(&s5p_serial3_device);
314b4980515SMarek Vasut }
315