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