xref: /rk3399_rockchip-uboot/drivers/serial/serial_s5p.c (revision 1628cfc4fe4b2c3caa7e9d5622f0665c54e8ba6e)
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>
2546a3b5c8SMinkyu Kang #include <asm/io.h>
2646a3b5c8SMinkyu Kang #include <asm/arch/uart.h>
2746a3b5c8SMinkyu Kang #include <asm/arch/clk.h>
2846a3b5c8SMinkyu Kang #include <serial.h>
2946a3b5c8SMinkyu Kang 
3046a3b5c8SMinkyu Kang static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
3146a3b5c8SMinkyu Kang {
3246a3b5c8SMinkyu Kang 	u32 offset = dev_index * sizeof(struct s5p_uart);
33d93d0f0cSMinkyu Kang 	return (struct s5p_uart *)(samsung_get_base_uart() + offset);
3446a3b5c8SMinkyu Kang }
3546a3b5c8SMinkyu Kang 
3646a3b5c8SMinkyu Kang /*
3746a3b5c8SMinkyu Kang  * The coefficient, used to calculate the baudrate on S5P UARTs is
3846a3b5c8SMinkyu Kang  * calculated as
3946a3b5c8SMinkyu Kang  * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
4046a3b5c8SMinkyu Kang  * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
4146a3b5c8SMinkyu Kang  * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
4246a3b5c8SMinkyu Kang  */
4346a3b5c8SMinkyu Kang static const int udivslot[] = {
4446a3b5c8SMinkyu Kang 	0,
4546a3b5c8SMinkyu Kang 	0x0080,
4646a3b5c8SMinkyu Kang 	0x0808,
4746a3b5c8SMinkyu Kang 	0x0888,
4846a3b5c8SMinkyu Kang 	0x2222,
4946a3b5c8SMinkyu Kang 	0x4924,
5046a3b5c8SMinkyu Kang 	0x4a52,
5146a3b5c8SMinkyu Kang 	0x54aa,
5246a3b5c8SMinkyu Kang 	0x5555,
5346a3b5c8SMinkyu Kang 	0xd555,
5446a3b5c8SMinkyu Kang 	0xd5d5,
5546a3b5c8SMinkyu Kang 	0xddd5,
5646a3b5c8SMinkyu Kang 	0xdddd,
5746a3b5c8SMinkyu Kang 	0xdfdd,
5846a3b5c8SMinkyu Kang 	0xdfdf,
5946a3b5c8SMinkyu Kang 	0xffdf,
6046a3b5c8SMinkyu Kang };
6146a3b5c8SMinkyu Kang 
6246a3b5c8SMinkyu Kang void serial_setbrg_dev(const int dev_index)
6346a3b5c8SMinkyu Kang {
6446a3b5c8SMinkyu Kang 	DECLARE_GLOBAL_DATA_PTR;
6546a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
66f70409afSMinkyu Kang 	u32 uclk = get_uart_clk(dev_index);
6746a3b5c8SMinkyu Kang 	u32 baudrate = gd->baudrate;
6846a3b5c8SMinkyu Kang 	u32 val;
6946a3b5c8SMinkyu Kang 
70f70409afSMinkyu Kang 	val = uclk / baudrate;
7146a3b5c8SMinkyu Kang 
7246a3b5c8SMinkyu Kang 	writel(val / 16 - 1, &uart->ubrdiv);
73*1628cfc4SMinkyu Kang 
74*1628cfc4SMinkyu Kang 	if (use_divslot)
75*1628cfc4SMinkyu Kang 		writew(udivslot[val % 16], &uart->rest.slot);
76*1628cfc4SMinkyu Kang 	else
77*1628cfc4SMinkyu Kang 		writeb(val % 16, &uart->rest.value);
7846a3b5c8SMinkyu Kang }
7946a3b5c8SMinkyu Kang 
8046a3b5c8SMinkyu Kang /*
8146a3b5c8SMinkyu Kang  * Initialise the serial port with the given baudrate. The settings
8246a3b5c8SMinkyu Kang  * are always 8 data bits, no parity, 1 stop bit, no start bits.
8346a3b5c8SMinkyu Kang  */
8446a3b5c8SMinkyu Kang int serial_init_dev(const int dev_index)
8546a3b5c8SMinkyu Kang {
8646a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
8746a3b5c8SMinkyu Kang 
8846a3b5c8SMinkyu Kang 	/* reset and enable FIFOs, set triggers to the maximum */
8946a3b5c8SMinkyu Kang 	writel(0, &uart->ufcon);
9046a3b5c8SMinkyu Kang 	writel(0, &uart->umcon);
9146a3b5c8SMinkyu Kang 	/* 8N1 */
9246a3b5c8SMinkyu Kang 	writel(0x3, &uart->ulcon);
9346a3b5c8SMinkyu Kang 	/* No interrupts, no DMA, pure polling */
9446a3b5c8SMinkyu Kang 	writel(0x245, &uart->ucon);
9546a3b5c8SMinkyu Kang 
9646a3b5c8SMinkyu Kang 	serial_setbrg_dev(dev_index);
9746a3b5c8SMinkyu Kang 
9846a3b5c8SMinkyu Kang 	return 0;
9946a3b5c8SMinkyu Kang }
10046a3b5c8SMinkyu Kang 
10146a3b5c8SMinkyu Kang static int serial_err_check(const int dev_index, int op)
10246a3b5c8SMinkyu Kang {
10346a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
10446a3b5c8SMinkyu Kang 	unsigned int mask;
10546a3b5c8SMinkyu Kang 
10646a3b5c8SMinkyu Kang 	/*
10746a3b5c8SMinkyu Kang 	 * UERSTAT
10846a3b5c8SMinkyu Kang 	 * Break Detect	[3]
10946a3b5c8SMinkyu Kang 	 * Frame Err	[2] : receive operation
11046a3b5c8SMinkyu Kang 	 * Parity Err	[1] : receive operation
11146a3b5c8SMinkyu Kang 	 * Overrun Err	[0] : receive operation
11246a3b5c8SMinkyu Kang 	 */
11346a3b5c8SMinkyu Kang 	if (op)
11446a3b5c8SMinkyu Kang 		mask = 0x8;
11546a3b5c8SMinkyu Kang 	else
11646a3b5c8SMinkyu Kang 		mask = 0xf;
11746a3b5c8SMinkyu Kang 
11846a3b5c8SMinkyu Kang 	return readl(&uart->uerstat) & mask;
11946a3b5c8SMinkyu Kang }
12046a3b5c8SMinkyu Kang 
12146a3b5c8SMinkyu Kang /*
12246a3b5c8SMinkyu Kang  * Read a single byte from the serial port. Returns 1 on success, 0
12346a3b5c8SMinkyu Kang  * otherwise. When the function is succesfull, the character read is
12446a3b5c8SMinkyu Kang  * written into its argument c.
12546a3b5c8SMinkyu Kang  */
12646a3b5c8SMinkyu Kang int serial_getc_dev(const int dev_index)
12746a3b5c8SMinkyu Kang {
12846a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
12946a3b5c8SMinkyu Kang 
13046a3b5c8SMinkyu Kang 	/* wait for character to arrive */
13146a3b5c8SMinkyu Kang 	while (!(readl(&uart->utrstat) & 0x1)) {
13246a3b5c8SMinkyu Kang 		if (serial_err_check(dev_index, 0))
13346a3b5c8SMinkyu Kang 			return 0;
13446a3b5c8SMinkyu Kang 	}
13546a3b5c8SMinkyu Kang 
1361a4106ddSMinkyu Kang 	return (int)(readb(&uart->urxh) & 0xff);
13746a3b5c8SMinkyu Kang }
13846a3b5c8SMinkyu Kang 
13946a3b5c8SMinkyu Kang /*
14046a3b5c8SMinkyu Kang  * Output a single byte to the serial port.
14146a3b5c8SMinkyu Kang  */
14246a3b5c8SMinkyu Kang void serial_putc_dev(const char c, const int dev_index)
14346a3b5c8SMinkyu Kang {
14446a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
14546a3b5c8SMinkyu Kang 
14646a3b5c8SMinkyu Kang 	/* wait for room in the tx FIFO */
14746a3b5c8SMinkyu Kang 	while (!(readl(&uart->utrstat) & 0x2)) {
14846a3b5c8SMinkyu Kang 		if (serial_err_check(dev_index, 1))
14946a3b5c8SMinkyu Kang 			return;
15046a3b5c8SMinkyu Kang 	}
15146a3b5c8SMinkyu Kang 
1521a4106ddSMinkyu Kang 	writeb(c, &uart->utxh);
15346a3b5c8SMinkyu Kang 
15446a3b5c8SMinkyu Kang 	/* If \n, also do \r */
15546a3b5c8SMinkyu Kang 	if (c == '\n')
15646a3b5c8SMinkyu Kang 		serial_putc('\r');
15746a3b5c8SMinkyu Kang }
15846a3b5c8SMinkyu Kang 
15946a3b5c8SMinkyu Kang /*
16046a3b5c8SMinkyu Kang  * Test whether a character is in the RX buffer
16146a3b5c8SMinkyu Kang  */
16246a3b5c8SMinkyu Kang int serial_tstc_dev(const int dev_index)
16346a3b5c8SMinkyu Kang {
16446a3b5c8SMinkyu Kang 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
16546a3b5c8SMinkyu Kang 
16646a3b5c8SMinkyu Kang 	return (int)(readl(&uart->utrstat) & 0x1);
16746a3b5c8SMinkyu Kang }
16846a3b5c8SMinkyu Kang 
16946a3b5c8SMinkyu Kang void serial_puts_dev(const char *s, const int dev_index)
17046a3b5c8SMinkyu Kang {
17146a3b5c8SMinkyu Kang 	while (*s)
17246a3b5c8SMinkyu Kang 		serial_putc_dev(*s++, dev_index);
17346a3b5c8SMinkyu Kang }
17446a3b5c8SMinkyu Kang 
17546a3b5c8SMinkyu Kang /* Multi serial device functions */
17646a3b5c8SMinkyu Kang #define DECLARE_S5P_SERIAL_FUNCTIONS(port) \
17746a3b5c8SMinkyu Kang int s5p_serial##port##_init(void) { return serial_init_dev(port); } \
17846a3b5c8SMinkyu Kang void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \
17946a3b5c8SMinkyu Kang int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \
18046a3b5c8SMinkyu Kang int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \
18146a3b5c8SMinkyu Kang void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \
18246a3b5c8SMinkyu Kang void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); }
18346a3b5c8SMinkyu Kang 
18446a3b5c8SMinkyu Kang #define INIT_S5P_SERIAL_STRUCTURE(port, name, bus) { \
18546a3b5c8SMinkyu Kang 	name, \
18646a3b5c8SMinkyu Kang 	bus, \
18746a3b5c8SMinkyu Kang 	s5p_serial##port##_init, \
18846a3b5c8SMinkyu Kang 	NULL, \
18946a3b5c8SMinkyu Kang 	s5p_serial##port##_setbrg, \
19046a3b5c8SMinkyu Kang 	s5p_serial##port##_getc, \
19146a3b5c8SMinkyu Kang 	s5p_serial##port##_tstc, \
19246a3b5c8SMinkyu Kang 	s5p_serial##port##_putc, \
19346a3b5c8SMinkyu Kang 	s5p_serial##port##_puts, }
19446a3b5c8SMinkyu Kang 
19546a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(0);
19646a3b5c8SMinkyu Kang struct serial_device s5p_serial0_device =
19746a3b5c8SMinkyu Kang 	INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0", "S5PUART0");
19846a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(1);
19946a3b5c8SMinkyu Kang struct serial_device s5p_serial1_device =
20046a3b5c8SMinkyu Kang 	INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1", "S5PUART1");
20146a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(2);
20246a3b5c8SMinkyu Kang struct serial_device s5p_serial2_device =
20346a3b5c8SMinkyu Kang 	INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2", "S5PUART2");
20446a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(3);
20546a3b5c8SMinkyu Kang struct serial_device s5p_serial3_device =
20646a3b5c8SMinkyu Kang 	INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3", "S5PUART3");
207