xref: /rk3399_rockchip-uboot/drivers/serial/serial_s5p.c (revision 73e256c2ac7a67b00be69a397997d80d04ec994d)
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>
12*73e256c2SSimon Glass #include <dm.h>
13*73e256c2SSimon 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/uart.h>
1846a3b5c8SMinkyu Kang #include <asm/arch/clk.h>
1946a3b5c8SMinkyu Kang #include <serial.h>
2046a3b5c8SMinkyu Kang 
2129565326SJohn Rigby DECLARE_GLOBAL_DATA_PTR;
2229565326SJohn Rigby 
23*73e256c2SSimon Glass #define RX_FIFO_COUNT_SHIFT	0
24*73e256c2SSimon Glass #define RX_FIFO_COUNT_MASK	(0xff << RX_FIFO_COUNT_SHIFT)
25*73e256c2SSimon Glass #define RX_FIFO_FULL		(1 << 8)
26*73e256c2SSimon Glass #define TX_FIFO_COUNT_SHIFT	16
27*73e256c2SSimon Glass #define TX_FIFO_COUNT_MASK	(0xff << TX_FIFO_COUNT_SHIFT)
28*73e256c2SSimon Glass #define TX_FIFO_FULL		(1 << 24)
29ffbff1ddSAkshay Saraswat 
30d4ec8f08SRajeshwari Shinde /* Information about a serial port */
31*73e256c2SSimon Glass struct s5p_serial_platdata {
32*73e256c2SSimon Glass 	struct s5p_uart *reg;  /* address of registers in physical memory */
33d4ec8f08SRajeshwari Shinde 	u8 port_id;     /* uart port number */
34*73e256c2SSimon Glass };
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 
62*73e256c2SSimon Glass int s5p_serial_setbrg(struct udevice *dev, int baudrate)
6346a3b5c8SMinkyu Kang {
64*73e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
65*73e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
66*73e256c2SSimon Glass 	u32 uclk = get_uart_clk(plat->port_id);
6746a3b5c8SMinkyu Kang 	u32 val;
6846a3b5c8SMinkyu Kang 
69f70409afSMinkyu Kang 	val = uclk / baudrate;
7046a3b5c8SMinkyu Kang 
7146a3b5c8SMinkyu Kang 	writel(val / 16 - 1, &uart->ubrdiv);
721628cfc4SMinkyu Kang 
73e0617c62SMinkyu Kang 	if (s5p_uart_divslot())
741628cfc4SMinkyu Kang 		writew(udivslot[val % 16], &uart->rest.slot);
751628cfc4SMinkyu Kang 	else
761628cfc4SMinkyu Kang 		writeb(val % 16, &uart->rest.value);
77*73e256c2SSimon Glass 
78*73e256c2SSimon Glass 	return 0;
7946a3b5c8SMinkyu Kang }
8046a3b5c8SMinkyu Kang 
81*73e256c2SSimon Glass static int s5p_serial_probe(struct udevice *dev)
8246a3b5c8SMinkyu Kang {
83*73e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
84*73e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
8546a3b5c8SMinkyu Kang 
86e6252fabSInha Song 	/* enable FIFOs, auto clear Rx FIFO */
87e6252fabSInha Song 	writel(0x3, &uart->ufcon);
8846a3b5c8SMinkyu Kang 	writel(0, &uart->umcon);
8946a3b5c8SMinkyu Kang 	/* 8N1 */
9046a3b5c8SMinkyu Kang 	writel(0x3, &uart->ulcon);
9146a3b5c8SMinkyu Kang 	/* No interrupts, no DMA, pure polling */
9246a3b5c8SMinkyu Kang 	writel(0x245, &uart->ucon);
9346a3b5c8SMinkyu Kang 
9446a3b5c8SMinkyu Kang 	return 0;
9546a3b5c8SMinkyu Kang }
9646a3b5c8SMinkyu Kang 
97*73e256c2SSimon Glass static int serial_err_check(const struct s5p_uart *const uart, int op)
9846a3b5c8SMinkyu Kang {
9946a3b5c8SMinkyu Kang 	unsigned int mask;
10046a3b5c8SMinkyu Kang 
10146a3b5c8SMinkyu Kang 	/*
10246a3b5c8SMinkyu Kang 	 * UERSTAT
10346a3b5c8SMinkyu Kang 	 * Break Detect	[3]
10446a3b5c8SMinkyu Kang 	 * Frame Err	[2] : receive operation
10546a3b5c8SMinkyu Kang 	 * Parity Err	[1] : receive operation
10646a3b5c8SMinkyu Kang 	 * Overrun Err	[0] : receive operation
10746a3b5c8SMinkyu Kang 	 */
10846a3b5c8SMinkyu Kang 	if (op)
10946a3b5c8SMinkyu Kang 		mask = 0x8;
11046a3b5c8SMinkyu Kang 	else
11146a3b5c8SMinkyu Kang 		mask = 0xf;
11246a3b5c8SMinkyu Kang 
11346a3b5c8SMinkyu Kang 	return readl(&uart->uerstat) & mask;
11446a3b5c8SMinkyu Kang }
11546a3b5c8SMinkyu Kang 
116*73e256c2SSimon Glass static int s5p_serial_getc(struct udevice *dev)
11746a3b5c8SMinkyu Kang {
118*73e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
119*73e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
12046a3b5c8SMinkyu Kang 
121*73e256c2SSimon Glass 	if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK))
122*73e256c2SSimon Glass 		return -EAGAIN;
123d4ec8f08SRajeshwari Shinde 
124*73e256c2SSimon Glass 	serial_err_check(uart, 0);
1251a4106ddSMinkyu Kang 	return (int)(readb(&uart->urxh) & 0xff);
12646a3b5c8SMinkyu Kang }
12746a3b5c8SMinkyu Kang 
128*73e256c2SSimon Glass static int s5p_serial_putc(struct udevice *dev, const char ch)
12946a3b5c8SMinkyu Kang {
130*73e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
131*73e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
13246a3b5c8SMinkyu Kang 
133*73e256c2SSimon Glass 	if (readl(&uart->ufstat) & TX_FIFO_FULL)
134*73e256c2SSimon Glass 		return -EAGAIN;
135d4ec8f08SRajeshwari Shinde 
136*73e256c2SSimon Glass 	writeb(ch, &uart->utxh);
137*73e256c2SSimon Glass 	serial_err_check(uart, 1);
138d4ec8f08SRajeshwari Shinde 
139d4ec8f08SRajeshwari Shinde 	return 0;
140d4ec8f08SRajeshwari Shinde }
141d4ec8f08SRajeshwari Shinde 
142*73e256c2SSimon Glass static int s5p_serial_pending(struct udevice *dev, bool input)
1436c768ca7SMike Frysinger {
144*73e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
145*73e256c2SSimon Glass 	struct s5p_uart *const uart = plat->reg;
146*73e256c2SSimon Glass 	uint32_t ufstat = readl(&uart->ufstat);
147d4ec8f08SRajeshwari Shinde 
148*73e256c2SSimon Glass 	if (input)
149*73e256c2SSimon Glass 		return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT;
150*73e256c2SSimon Glass 	else
151*73e256c2SSimon Glass 		return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT;
152d4ec8f08SRajeshwari Shinde }
153d4ec8f08SRajeshwari Shinde 
154*73e256c2SSimon Glass static int s5p_serial_ofdata_to_platdata(struct udevice *dev)
155b4980515SMarek Vasut {
156*73e256c2SSimon Glass 	struct s5p_serial_platdata *plat = dev->platdata;
157*73e256c2SSimon Glass 	fdt_addr_t addr;
158*73e256c2SSimon Glass 
159*73e256c2SSimon Glass 	addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
160*73e256c2SSimon Glass 	if (addr == FDT_ADDR_T_NONE)
161*73e256c2SSimon Glass 		return -EINVAL;
162*73e256c2SSimon Glass 
163*73e256c2SSimon Glass 	plat->reg = (struct s5p_uart *)addr;
164*73e256c2SSimon Glass 	plat->port_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "id", -1);
165*73e256c2SSimon Glass 
166*73e256c2SSimon Glass 	return 0;
167b4980515SMarek Vasut }
168*73e256c2SSimon Glass 
169*73e256c2SSimon Glass static const struct dm_serial_ops s5p_serial_ops = {
170*73e256c2SSimon Glass 	.putc = s5p_serial_putc,
171*73e256c2SSimon Glass 	.pending = s5p_serial_pending,
172*73e256c2SSimon Glass 	.getc = s5p_serial_getc,
173*73e256c2SSimon Glass 	.setbrg = s5p_serial_setbrg,
174*73e256c2SSimon Glass };
175*73e256c2SSimon Glass 
176*73e256c2SSimon Glass static const struct udevice_id s5p_serial_ids[] = {
177*73e256c2SSimon Glass 	{ .compatible = "samsung,exynos4210-uart" },
178*73e256c2SSimon Glass 	{ }
179*73e256c2SSimon Glass };
180*73e256c2SSimon Glass 
181*73e256c2SSimon Glass U_BOOT_DRIVER(serial_s5p) = {
182*73e256c2SSimon Glass 	.name	= "serial_s5p",
183*73e256c2SSimon Glass 	.id	= UCLASS_SERIAL,
184*73e256c2SSimon Glass 	.of_match = s5p_serial_ids,
185*73e256c2SSimon Glass 	.ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
186*73e256c2SSimon Glass 	.platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
187*73e256c2SSimon Glass 	.probe = s5p_serial_probe,
188*73e256c2SSimon Glass 	.ops	= &s5p_serial_ops,
189*73e256c2SSimon Glass 	.flags = DM_FLAG_PRE_RELOC,
190*73e256c2SSimon Glass };
191