blob: 734780a124ad47ed97811784edfdb45a75d37bf7 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Minkyu Kangfca30842009-10-01 17:20:28 +09002/*
3 * (C) Copyright 2009 SAMSUNG Electronics
4 * Minkyu Kang <mk7.kang@samsung.com>
5 * Heungjun Kim <riverful.kim@samsung.com>
6 *
7 * based on drivers/serial/s3c64xx.c
Minkyu Kangfca30842009-10-01 17:20:28 +09008 */
9
Simon Glass767e7372014-09-14 16:36:17 -060010#include <dm.h>
11#include <errno.h>
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +053012#include <fdtdec.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060013#include <asm/global_data.h>
Mike Frysingerf96c0422011-04-29 18:03:29 +000014#include <linux/compiler.h>
Minkyu Kangfca30842009-10-01 17:20:28 +090015#include <asm/io.h>
Mark Kettenisa35aee52023-01-26 14:44:09 +010016#if !IS_ENABLED(CONFIG_ARCH_APPLE)
Minkyu Kangfca30842009-10-01 17:20:28 +090017#include <asm/arch/clk.h>
Mark Kettenis835cb5d2021-10-23 16:58:04 +020018#endif
Simon Glass405fd142015-07-02 18:15:53 -060019#include <asm/arch/uart.h>
Minkyu Kangfca30842009-10-01 17:20:28 +090020#include <serial.h>
Thomas Abrahamc81fdbe2016-04-23 22:18:11 +053021#include <clk.h>
Minkyu Kangfca30842009-10-01 17:20:28 +090022
Mark Kettenis835cb5d2021-10-23 16:58:04 +020023enum {
24 PORT_S5P = 0,
25 PORT_S5L
26};
27
Sam Protsenkoc6c27f52023-11-07 13:06:00 -060028#define UFCON_FIFO_EN BIT(0)
29#define UFCON_RX_FIFO_RESET BIT(1)
30#define UMCON_RESET_VAL 0x0
31#define ULCON_WORD_8_BIT 0x3
32#define UCON_RX_IRQ_OR_POLLING BIT(0)
33#define UCON_TX_IRQ_OR_POLLING BIT(2)
34#define UCON_RX_ERR_IRQ_EN BIT(6)
35#define UCON_TX_IRQ_LEVEL BIT(9)
36
Mark Kettenis835cb5d2021-10-23 16:58:04 +020037#define S5L_RX_FIFO_COUNT_SHIFT 0
38#define S5L_RX_FIFO_COUNT_MASK (0xf << S5L_RX_FIFO_COUNT_SHIFT)
Sam Protsenkoc6c27f52023-11-07 13:06:00 -060039#define S5L_RX_FIFO_FULL BIT(8)
Mark Kettenis835cb5d2021-10-23 16:58:04 +020040#define S5L_TX_FIFO_COUNT_SHIFT 4
41#define S5L_TX_FIFO_COUNT_MASK (0xf << S5L_TX_FIFO_COUNT_SHIFT)
Sam Protsenkoc6c27f52023-11-07 13:06:00 -060042#define S5L_TX_FIFO_FULL BIT(9)
Mark Kettenis835cb5d2021-10-23 16:58:04 +020043
44#define S5P_RX_FIFO_COUNT_SHIFT 0
45#define S5P_RX_FIFO_COUNT_MASK (0xff << S5P_RX_FIFO_COUNT_SHIFT)
Sam Protsenkoc6c27f52023-11-07 13:06:00 -060046#define S5P_RX_FIFO_FULL BIT(8)
Mark Kettenis835cb5d2021-10-23 16:58:04 +020047#define S5P_TX_FIFO_COUNT_SHIFT 16
48#define S5P_TX_FIFO_COUNT_MASK (0xff << S5P_TX_FIFO_COUNT_SHIFT)
Sam Protsenkoc6c27f52023-11-07 13:06:00 -060049#define S5P_TX_FIFO_FULL BIT(24)
Akshay Saraswat63f10902013-03-21 20:33:04 +000050
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +053051/* Information about a serial port */
Simon Glassb75b15b2020-12-03 16:55:23 -070052struct s5p_serial_plat {
Sam Protsenkof3b87e52023-11-07 13:06:01 -060053 struct s5p_uart *reg; /* address of registers in physical memory */
54 u8 reg_width; /* register width */
55 u8 port_id; /* uart port number */
Mark Kettenis835cb5d2021-10-23 16:58:04 +020056 u8 rx_fifo_count_shift;
57 u8 tx_fifo_count_shift;
58 u32 rx_fifo_count_mask;
59 u32 tx_fifo_count_mask;
60 u32 rx_fifo_full;
61 u32 tx_fifo_full;
Simon Glass767e7372014-09-14 16:36:17 -060062};
Minkyu Kangfca30842009-10-01 17:20:28 +090063
64/*
Minkyu Kangbaa36882010-03-24 16:59:30 +090065 * The coefficient, used to calculate the baudrate on S5P UARTs is
Minkyu Kangfca30842009-10-01 17:20:28 +090066 * calculated as
67 * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
Sam Protsenkof3b87e52023-11-07 13:06:01 -060068 * however, section 31.6.11 of the datasheet doesn't recommend using 1 for 1,
Minkyu Kangfca30842009-10-01 17:20:28 +090069 * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
70 */
71static const int udivslot[] = {
72 0,
73 0x0080,
74 0x0808,
75 0x0888,
76 0x2222,
77 0x4924,
78 0x4a52,
79 0x54aa,
80 0x5555,
81 0xd555,
82 0xd5d5,
83 0xddd5,
84 0xdddd,
85 0xdfdd,
86 0xdfdf,
87 0xffdf,
88};
89
Simon Glass405fd142015-07-02 18:15:53 -060090static void __maybe_unused s5p_serial_init(struct s5p_uart *uart)
Minkyu Kangfca30842009-10-01 17:20:28 +090091{
Sam Protsenkoc6c27f52023-11-07 13:06:00 -060092 /* Enable FIFOs, auto clear Rx FIFO */
93 writel(UFCON_FIFO_EN | UFCON_RX_FIFO_RESET, &uart->ufcon);
94 /* No auto flow control, disable nRTS signal */
95 writel(UMCON_RESET_VAL, &uart->umcon);
96 /* 8N1, no parity bit */
97 writel(ULCON_WORD_8_BIT, &uart->ulcon);
Simon Glass405fd142015-07-02 18:15:53 -060098 /* No interrupts, no DMA, pure polling */
Sam Protsenkoc6c27f52023-11-07 13:06:00 -060099 writel(UCON_RX_IRQ_OR_POLLING | UCON_TX_IRQ_OR_POLLING |
100 UCON_RX_ERR_IRQ_EN | UCON_TX_IRQ_LEVEL, &uart->ucon);
Simon Glass405fd142015-07-02 18:15:53 -0600101}
102
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200103static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, u8 reg_width,
104 uint uclk, int baudrate)
Simon Glass405fd142015-07-02 18:15:53 -0600105{
Minkyu Kangfca30842009-10-01 17:20:28 +0900106 u32 val;
107
Minkyu Kang36f25cf2010-08-24 15:51:55 +0900108 val = uclk / baudrate;
Minkyu Kangfca30842009-10-01 17:20:28 +0900109
110 writel(val / 16 - 1, &uart->ubrdiv);
Minkyu Kangbfa14242010-09-28 14:35:02 +0900111
Minkyu Kangafae8aa2011-01-24 14:43:25 +0900112 if (s5p_uart_divslot())
Minkyu Kangbfa14242010-09-28 14:35:02 +0900113 writew(udivslot[val % 16], &uart->rest.slot);
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200114 else if (reg_width == 4)
115 writel(val % 16, &uart->rest.value);
Minkyu Kangbfa14242010-09-28 14:35:02 +0900116 else
117 writeb(val % 16, &uart->rest.value);
Simon Glass405fd142015-07-02 18:15:53 -0600118}
119
Simon Glass7ec24132024-09-29 19:49:48 -0600120#ifndef CONFIG_XPL_BUILD
Simon Glass405fd142015-07-02 18:15:53 -0600121int s5p_serial_setbrg(struct udevice *dev, int baudrate)
122{
Simon Glass95588622020-12-22 19:30:28 -0700123 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass405fd142015-07-02 18:15:53 -0600124 struct s5p_uart *const uart = plat->reg;
Thomas Abrahamc81fdbe2016-04-23 22:18:11 +0530125 u32 uclk;
126
Mark Kettenisa35aee52023-01-26 14:44:09 +0100127#if IS_ENABLED(CONFIG_CLK_EXYNOS) || IS_ENABLED(CONFIG_ARCH_APPLE)
Stephen Warrena9622432016-06-17 09:44:00 -0600128 struct clk clk;
Sam Protsenko6979cc02023-11-07 11:34:17 -0600129 int ret;
Thomas Abrahamc81fdbe2016-04-23 22:18:11 +0530130
Stephen Warrena9622432016-06-17 09:44:00 -0600131 ret = clk_get_by_index(dev, 1, &clk);
Thomas Abrahamc81fdbe2016-04-23 22:18:11 +0530132 if (ret < 0)
133 return ret;
Stephen Warrena9622432016-06-17 09:44:00 -0600134 uclk = clk_get_rate(&clk);
Thomas Abrahamc81fdbe2016-04-23 22:18:11 +0530135#else
136 uclk = get_uart_clk(plat->port_id);
137#endif
Simon Glass405fd142015-07-02 18:15:53 -0600138
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200139 s5p_serial_baud(uart, plat->reg_width, uclk, baudrate);
Simon Glass767e7372014-09-14 16:36:17 -0600140
141 return 0;
Minkyu Kangfca30842009-10-01 17:20:28 +0900142}
143
Simon Glass767e7372014-09-14 16:36:17 -0600144static int s5p_serial_probe(struct udevice *dev)
Minkyu Kangfca30842009-10-01 17:20:28 +0900145{
Simon Glass95588622020-12-22 19:30:28 -0700146 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass767e7372014-09-14 16:36:17 -0600147 struct s5p_uart *const uart = plat->reg;
Minkyu Kangfca30842009-10-01 17:20:28 +0900148
Simon Glass405fd142015-07-02 18:15:53 -0600149 s5p_serial_init(uart);
Minkyu Kangfca30842009-10-01 17:20:28 +0900150
Minkyu Kangfca30842009-10-01 17:20:28 +0900151 return 0;
152}
153
Simon Glass767e7372014-09-14 16:36:17 -0600154static int serial_err_check(const struct s5p_uart *const uart, int op)
Minkyu Kangfca30842009-10-01 17:20:28 +0900155{
Minkyu Kang9455aab2009-11-10 20:23:50 +0900156 unsigned int mask;
Minkyu Kangfca30842009-10-01 17:20:28 +0900157
Minkyu Kang9455aab2009-11-10 20:23:50 +0900158 /*
159 * UERSTAT
160 * Break Detect [3]
161 * Frame Err [2] : receive operation
162 * Parity Err [1] : receive operation
163 * Overrun Err [0] : receive operation
164 */
165 if (op)
166 mask = 0x8;
167 else
168 mask = 0xf;
Minkyu Kangfca30842009-10-01 17:20:28 +0900169
Minkyu Kang9455aab2009-11-10 20:23:50 +0900170 return readl(&uart->uerstat) & mask;
Minkyu Kangfca30842009-10-01 17:20:28 +0900171}
172
Simon Glass767e7372014-09-14 16:36:17 -0600173static int s5p_serial_getc(struct udevice *dev)
Minkyu Kangfca30842009-10-01 17:20:28 +0900174{
Simon Glass95588622020-12-22 19:30:28 -0700175 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass767e7372014-09-14 16:36:17 -0600176 struct s5p_uart *const uart = plat->reg;
Minkyu Kangfca30842009-10-01 17:20:28 +0900177
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200178 if (!(readl(&uart->ufstat) & plat->rx_fifo_count_mask))
Simon Glass767e7372014-09-14 16:36:17 -0600179 return -EAGAIN;
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +0530180
Simon Glass767e7372014-09-14 16:36:17 -0600181 serial_err_check(uart, 0);
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200182 if (plat->reg_width == 4)
183 return (int)(readl(&uart->urxh) & 0xff);
184 else
185 return (int)(readb(&uart->urxh) & 0xff);
Minkyu Kangfca30842009-10-01 17:20:28 +0900186}
187
Simon Glass767e7372014-09-14 16:36:17 -0600188static int s5p_serial_putc(struct udevice *dev, const char ch)
Minkyu Kangfca30842009-10-01 17:20:28 +0900189{
Simon Glass95588622020-12-22 19:30:28 -0700190 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass767e7372014-09-14 16:36:17 -0600191 struct s5p_uart *const uart = plat->reg;
Minkyu Kangfca30842009-10-01 17:20:28 +0900192
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200193 if (readl(&uart->ufstat) & plat->tx_fifo_full)
Simon Glass767e7372014-09-14 16:36:17 -0600194 return -EAGAIN;
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +0530195
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200196 if (plat->reg_width == 4)
197 writel(ch, &uart->utxh);
198 else
199 writeb(ch, &uart->utxh);
Simon Glass767e7372014-09-14 16:36:17 -0600200 serial_err_check(uart, 1);
Minkyu Kangfca30842009-10-01 17:20:28 +0900201
Simon Glass767e7372014-09-14 16:36:17 -0600202 return 0;
Minkyu Kangfca30842009-10-01 17:20:28 +0900203}
204
Simon Glass767e7372014-09-14 16:36:17 -0600205static int s5p_serial_pending(struct udevice *dev, bool input)
Minkyu Kangfca30842009-10-01 17:20:28 +0900206{
Simon Glass95588622020-12-22 19:30:28 -0700207 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass767e7372014-09-14 16:36:17 -0600208 struct s5p_uart *const uart = plat->reg;
209 uint32_t ufstat = readl(&uart->ufstat);
Minkyu Kangfca30842009-10-01 17:20:28 +0900210
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200211 if (input) {
212 return (ufstat & plat->rx_fifo_count_mask) >>
213 plat->rx_fifo_count_shift;
214 } else {
215 return (ufstat & plat->tx_fifo_count_mask) >>
216 plat->tx_fifo_count_shift;
217 }
Marek Vasut5bcdf242012-09-09 18:48:28 +0200218}
Minkyu Kangfca30842009-10-01 17:20:28 +0900219
Simon Glassaad29ae2020-12-03 16:55:21 -0700220static int s5p_serial_of_to_plat(struct udevice *dev)
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +0530221{
Simon Glass95588622020-12-22 19:30:28 -0700222 struct s5p_serial_plat *plat = dev_get_plat(dev);
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200223 const ulong port_type = dev_get_driver_data(dev);
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +0530224
Sam Protsenkoaef33932023-11-07 14:13:49 -0600225 plat->reg = dev_read_addr_ptr(dev);
226 if (!plat->reg)
Simon Glass767e7372014-09-14 16:36:17 -0600227 return -EINVAL;
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +0530228
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200229 plat->reg_width = dev_read_u32_default(dev, "reg-io-width", 1);
Sam Protsenko243aaa42023-11-07 13:05:59 -0600230 plat->port_id = dev_read_u8_default(dev, "id", dev_seq(dev));
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200231
232 if (port_type == PORT_S5L) {
233 plat->rx_fifo_count_shift = S5L_RX_FIFO_COUNT_SHIFT;
234 plat->rx_fifo_count_mask = S5L_RX_FIFO_COUNT_MASK;
235 plat->rx_fifo_full = S5L_RX_FIFO_FULL;
236 plat->tx_fifo_count_shift = S5L_TX_FIFO_COUNT_SHIFT;
237 plat->tx_fifo_count_mask = S5L_TX_FIFO_COUNT_MASK;
238 plat->tx_fifo_full = S5L_TX_FIFO_FULL;
239 } else {
240 plat->rx_fifo_count_shift = S5P_RX_FIFO_COUNT_SHIFT;
241 plat->rx_fifo_count_mask = S5P_RX_FIFO_COUNT_MASK;
242 plat->rx_fifo_full = S5P_RX_FIFO_FULL;
243 plat->tx_fifo_count_shift = S5P_TX_FIFO_COUNT_SHIFT;
244 plat->tx_fifo_count_mask = S5P_TX_FIFO_COUNT_MASK;
245 plat->tx_fifo_full = S5P_TX_FIFO_FULL;
246 }
247
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +0530248 return 0;
249}
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +0530250
Simon Glass767e7372014-09-14 16:36:17 -0600251static const struct dm_serial_ops s5p_serial_ops = {
Sam Protsenkof3b87e52023-11-07 13:06:01 -0600252 .putc = s5p_serial_putc,
253 .pending = s5p_serial_pending,
254 .getc = s5p_serial_getc,
255 .setbrg = s5p_serial_setbrg,
Simon Glass767e7372014-09-14 16:36:17 -0600256};
Rajeshwari Shindebd19fa92013-06-24 16:47:22 +0530257
Simon Glass767e7372014-09-14 16:36:17 -0600258static const struct udevice_id s5p_serial_ids[] = {
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200259 { .compatible = "samsung,exynos4210-uart", .data = PORT_S5P },
Sam Protsenko133d24f2024-01-10 21:09:06 -0600260 { .compatible = "samsung,exynos850-uart", .data = PORT_S5P },
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200261 { .compatible = "apple,s5l-uart", .data = PORT_S5L },
Simon Glass767e7372014-09-14 16:36:17 -0600262 { }
263};
Marek Vasut533e31e2012-09-12 19:39:57 +0200264
Simon Glass767e7372014-09-14 16:36:17 -0600265U_BOOT_DRIVER(serial_s5p) = {
Sam Protsenkof3b87e52023-11-07 13:06:01 -0600266 .name = "serial_s5p",
267 .id = UCLASS_SERIAL,
268 .of_match = s5p_serial_ids,
269 .of_to_plat = s5p_serial_of_to_plat,
Simon Glassb75b15b2020-12-03 16:55:23 -0700270 .plat_auto = sizeof(struct s5p_serial_plat),
Sam Protsenkof3b87e52023-11-07 13:06:01 -0600271 .probe = s5p_serial_probe,
272 .ops = &s5p_serial_ops,
Simon Glass767e7372014-09-14 16:36:17 -0600273};
Simon Glass8ed4bc12015-07-02 18:15:55 -0600274#endif
Simon Glass74afb292015-07-02 18:15:54 -0600275
276#ifdef CONFIG_DEBUG_UART_S5P
277
278#include <debug_uart.h>
279
Simon Glass60517d72015-10-18 19:51:23 -0600280static inline void _debug_uart_init(void)
Simon Glass74afb292015-07-02 18:15:54 -0600281{
Dzmitry Sankouski2993e972021-10-17 13:45:39 +0300282 if (IS_ENABLED(CONFIG_DEBUG_UART_SKIP_INIT))
283 return;
284
Pali Rohár8864b352022-05-27 22:15:24 +0200285 struct s5p_uart *uart = (struct s5p_uart *)CONFIG_VAL(DEBUG_UART_BASE);
Simon Glass74afb292015-07-02 18:15:54 -0600286
287 s5p_serial_init(uart);
Mark Kettenisa35aee52023-01-26 14:44:09 +0100288#if IS_ENABLED(CONFIG_ARCH_APPLE)
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200289 s5p_serial_baud(uart, 4, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
290#else
291 s5p_serial_baud(uart, 1, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
292#endif
Simon Glass74afb292015-07-02 18:15:54 -0600293}
294
295static inline void _debug_uart_putc(int ch)
296{
Pali Rohár8864b352022-05-27 22:15:24 +0200297 struct s5p_uart *uart = (struct s5p_uart *)CONFIG_VAL(DEBUG_UART_BASE);
Simon Glass74afb292015-07-02 18:15:54 -0600298
Mark Kettenisa35aee52023-01-26 14:44:09 +0100299#if IS_ENABLED(CONFIG_ARCH_APPLE)
Sam Protsenkof3b87e52023-11-07 13:06:01 -0600300 while (readl(&uart->ufstat) & S5L_TX_FIFO_FULL)
301 ;
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200302 writel(ch, &uart->utxh);
303#else
Sam Protsenkof3b87e52023-11-07 13:06:01 -0600304 while (readl(&uart->ufstat) & S5P_TX_FIFO_FULL)
305 ;
Simon Glass74afb292015-07-02 18:15:54 -0600306 writeb(ch, &uart->utxh);
Mark Kettenis835cb5d2021-10-23 16:58:04 +0200307#endif
Simon Glass74afb292015-07-02 18:15:54 -0600308}
309
310DEBUG_UART_FUNCS
311
312#endif