blob: 801b7645afa4773e82056bb6e10b2da044e91849 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Minkyu Kangdd2c9e62009-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 Kangdd2c9e62009-10-01 17:20:28 +09008 */
9
Simon Glass73e256c2014-09-14 16:36:17 -060010#include <dm.h>
11#include <errno.h>
Rajeshwari Shinded4ec8f02013-06-24 16:47:22 +053012#include <fdtdec.h>
Simon Glass401d1c42020-10-30 21:38:53 -060013#include <asm/global_data.h>
Mike Frysinger6c768ca2011-04-29 18:03:29 +000014#include <linux/compiler.h>
Minkyu Kangdd2c9e62009-10-01 17:20:28 +090015#include <asm/io.h>
Mark Kettenis053827b2023-01-26 14:44:09 +010016#if !IS_ENABLED(CONFIG_ARCH_APPLE)
Minkyu Kangdd2c9e62009-10-01 17:20:28 +090017#include <asm/arch/clk.h>
Mark Kettenisd520e1f2021-10-23 16:58:04 +020018#endif
Simon Glass89ca9352015-07-02 18:15:53 -060019#include <asm/arch/uart.h>
Minkyu Kangdd2c9e62009-10-01 17:20:28 +090020#include <serial.h>
Thomas Abrahamcf75cdf2016-04-23 22:18:11 +053021#include <clk.h>
Minkyu Kangdd2c9e62009-10-01 17:20:28 +090022
Mark Kettenisd520e1f2021-10-23 16:58:04 +020023enum {
24 PORT_S5P = 0,
25 PORT_S5L
26};
27
Sam Protsenkoe79f6302023-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 Kettenisd520e1f2021-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 Protsenkoe79f6302023-11-07 13:06:00 -060039#define S5L_RX_FIFO_FULL BIT(8)
Mark Kettenisd520e1f2021-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 Protsenkoe79f6302023-11-07 13:06:00 -060042#define S5L_TX_FIFO_FULL BIT(9)
Mark Kettenisd520e1f2021-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 Protsenkoe79f6302023-11-07 13:06:00 -060046#define S5P_RX_FIFO_FULL BIT(8)
Mark Kettenisd520e1f2021-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 Protsenkoe79f6302023-11-07 13:06:00 -060049#define S5P_TX_FIFO_FULL BIT(24)
Akshay Saraswatffbff1d2013-03-21 20:33:04 +000050
Rajeshwari Shinded4ec8f02013-06-24 16:47:22 +053051/* Information about a serial port */
Simon Glass8a8d24b2020-12-03 16:55:23 -070052struct s5p_serial_plat {
Sam Protsenkoa627f282023-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 Kettenisd520e1f2021-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 Glass73e256c2014-09-14 16:36:17 -060062};
Minkyu Kangdd2c9e62009-10-01 17:20:28 +090063
64/*
Minkyu Kang46a3b5c2010-03-24 16:59:30 +090065 * The coefficient, used to calculate the baudrate on S5P UARTs is
Minkyu Kangdd2c9e62009-10-01 17:20:28 +090066 * calculated as
67 * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
Sam Protsenkoa627f282023-11-07 13:06:01 -060068 * however, section 31.6.11 of the datasheet doesn't recommend using 1 for 1,
Minkyu Kangdd2c9e62009-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 Glass89ca9352015-07-02 18:15:53 -060090static void __maybe_unused s5p_serial_init(struct s5p_uart *uart)
Minkyu Kangdd2c9e62009-10-01 17:20:28 +090091{
Sam Protsenkoe79f6302023-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 Glass89ca9352015-07-02 18:15:53 -060098 /* No interrupts, no DMA, pure polling */
Sam Protsenkoe79f6302023-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 Glass89ca9352015-07-02 18:15:53 -0600101}
102
Mark Kettenisd520e1f2021-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 Glass89ca9352015-07-02 18:15:53 -0600105{
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900106 u32 val;
107
Minkyu Kangf70409a2010-08-24 15:51:55 +0900108 val = uclk / baudrate;
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900109
110 writel(val / 16 - 1, &uart->ubrdiv);
Minkyu Kang1628cfc2010-09-28 14:35:02 +0900111
Minkyu Kange0617c62011-01-24 14:43:25 +0900112 if (s5p_uart_divslot())
Minkyu Kang1628cfc2010-09-28 14:35:02 +0900113 writew(udivslot[val % 16], &uart->rest.slot);
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200114 else if (reg_width == 4)
115 writel(val % 16, &uart->rest.value);
Minkyu Kang1628cfc2010-09-28 14:35:02 +0900116 else
117 writeb(val % 16, &uart->rest.value);
Simon Glass89ca9352015-07-02 18:15:53 -0600118}
119
Simon Glass7fb57392015-07-02 18:15:55 -0600120#ifndef CONFIG_SPL_BUILD
Simon Glass89ca9352015-07-02 18:15:53 -0600121int s5p_serial_setbrg(struct udevice *dev, int baudrate)
122{
Simon Glass0fd3d912020-12-22 19:30:28 -0700123 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass89ca9352015-07-02 18:15:53 -0600124 struct s5p_uart *const uart = plat->reg;
Thomas Abrahamcf75cdf2016-04-23 22:18:11 +0530125 u32 uclk;
126
Mark Kettenis053827b2023-01-26 14:44:09 +0100127#if IS_ENABLED(CONFIG_CLK_EXYNOS) || IS_ENABLED(CONFIG_ARCH_APPLE)
Stephen Warren135aa952016-06-17 09:44:00 -0600128 struct clk clk;
Sam Protsenko2227f4c2023-11-07 11:34:17 -0600129 int ret;
Thomas Abrahamcf75cdf2016-04-23 22:18:11 +0530130
Stephen Warren135aa952016-06-17 09:44:00 -0600131 ret = clk_get_by_index(dev, 1, &clk);
Thomas Abrahamcf75cdf2016-04-23 22:18:11 +0530132 if (ret < 0)
133 return ret;
Stephen Warren135aa952016-06-17 09:44:00 -0600134 uclk = clk_get_rate(&clk);
Thomas Abrahamcf75cdf2016-04-23 22:18:11 +0530135#else
136 uclk = get_uart_clk(plat->port_id);
137#endif
Simon Glass89ca9352015-07-02 18:15:53 -0600138
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200139 s5p_serial_baud(uart, plat->reg_width, uclk, baudrate);
Simon Glass73e256c2014-09-14 16:36:17 -0600140
141 return 0;
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900142}
143
Simon Glass73e256c2014-09-14 16:36:17 -0600144static int s5p_serial_probe(struct udevice *dev)
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900145{
Simon Glass0fd3d912020-12-22 19:30:28 -0700146 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass73e256c2014-09-14 16:36:17 -0600147 struct s5p_uart *const uart = plat->reg;
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900148
Simon Glass89ca9352015-07-02 18:15:53 -0600149 s5p_serial_init(uart);
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900150
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900151 return 0;
152}
153
Simon Glass73e256c2014-09-14 16:36:17 -0600154static int serial_err_check(const struct s5p_uart *const uart, int op)
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900155{
Minkyu Kang94003222009-11-10 20:23:50 +0900156 unsigned int mask;
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900157
Minkyu Kang94003222009-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 Kangdd2c9e62009-10-01 17:20:28 +0900169
Minkyu Kang94003222009-11-10 20:23:50 +0900170 return readl(&uart->uerstat) & mask;
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900171}
172
Simon Glass73e256c2014-09-14 16:36:17 -0600173static int s5p_serial_getc(struct udevice *dev)
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900174{
Simon Glass0fd3d912020-12-22 19:30:28 -0700175 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass73e256c2014-09-14 16:36:17 -0600176 struct s5p_uart *const uart = plat->reg;
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900177
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200178 if (!(readl(&uart->ufstat) & plat->rx_fifo_count_mask))
Simon Glass73e256c2014-09-14 16:36:17 -0600179 return -EAGAIN;
Rajeshwari Shinded4ec8f02013-06-24 16:47:22 +0530180
Simon Glass73e256c2014-09-14 16:36:17 -0600181 serial_err_check(uart, 0);
Mark Kettenisd520e1f2021-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 Kangdd2c9e62009-10-01 17:20:28 +0900186}
187
Simon Glass73e256c2014-09-14 16:36:17 -0600188static int s5p_serial_putc(struct udevice *dev, const char ch)
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900189{
Simon Glass0fd3d912020-12-22 19:30:28 -0700190 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass73e256c2014-09-14 16:36:17 -0600191 struct s5p_uart *const uart = plat->reg;
Minkyu Kangdd2c9e62009-10-01 17:20:28 +0900192
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200193 if (readl(&uart->ufstat) & plat->tx_fifo_full)
Simon Glass73e256c2014-09-14 16:36:17 -0600194 return -EAGAIN;
Rajeshwari Shinded4ec8f02013-06-24 16:47:22 +0530195
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200196 if (plat->reg_width == 4)
197 writel(ch, &uart->utxh);
198 else
199 writeb(ch, &uart->utxh);
Simon Glass73e256c2014-09-14 16:36:17 -0600200 serial_err_check(uart, 1);
Rajeshwari Shinded4ec8f02013-06-24 16:47:22 +0530201
202 return 0;
203}
Rajeshwari Shinded4ec8f02013-06-24 16:47:22 +0530204
Simon Glass73e256c2014-09-14 16:36:17 -0600205static int s5p_serial_pending(struct udevice *dev, bool input)
Mike Frysinger6c768ca2011-04-29 18:03:29 +0000206{
Simon Glass0fd3d912020-12-22 19:30:28 -0700207 struct s5p_serial_plat *plat = dev_get_plat(dev);
Simon Glass73e256c2014-09-14 16:36:17 -0600208 struct s5p_uart *const uart = plat->reg;
209 uint32_t ufstat = readl(&uart->ufstat);
Rajeshwari Shinded4ec8f02013-06-24 16:47:22 +0530210
Mark Kettenisd520e1f2021-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 }
Mike Frysinger6c768ca2011-04-29 18:03:29 +0000218}
Marek Vasutb4980512012-09-12 19:39:57 +0200219
Simon Glassd1998a92020-12-03 16:55:21 -0700220static int s5p_serial_of_to_plat(struct udevice *dev)
Marek Vasutb4980512012-09-12 19:39:57 +0200221{
Simon Glass0fd3d912020-12-22 19:30:28 -0700222 struct s5p_serial_plat *plat = dev_get_plat(dev);
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200223 const ulong port_type = dev_get_driver_data(dev);
Simon Glass73e256c2014-09-14 16:36:17 -0600224
Sam Protsenko33e7ca52023-11-07 14:13:49 -0600225 plat->reg = dev_read_addr_ptr(dev);
226 if (!plat->reg)
Simon Glass73e256c2014-09-14 16:36:17 -0600227 return -EINVAL;
228
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200229 plat->reg_width = dev_read_u32_default(dev, "reg-io-width", 1);
Sam Protsenko5ad21de2023-11-07 13:05:59 -0600230 plat->port_id = dev_read_u8_default(dev, "id", dev_seq(dev));
Mark Kettenisd520e1f2021-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
Simon Glass73e256c2014-09-14 16:36:17 -0600248 return 0;
Marek Vasutb4980512012-09-12 19:39:57 +0200249}
Simon Glass73e256c2014-09-14 16:36:17 -0600250
251static const struct dm_serial_ops s5p_serial_ops = {
Sam Protsenkoa627f282023-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 Glass73e256c2014-09-14 16:36:17 -0600256};
257
258static const struct udevice_id s5p_serial_ids[] = {
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200259 { .compatible = "samsung,exynos4210-uart", .data = PORT_S5P },
Sam Protsenkoc2e8b972024-01-10 21:09:06 -0600260 { .compatible = "samsung,exynos850-uart", .data = PORT_S5P },
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200261 { .compatible = "apple,s5l-uart", .data = PORT_S5L },
Simon Glass73e256c2014-09-14 16:36:17 -0600262 { }
263};
264
265U_BOOT_DRIVER(serial_s5p) = {
Sam Protsenkoa627f282023-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 Glass8a8d24b2020-12-03 16:55:23 -0700270 .plat_auto = sizeof(struct s5p_serial_plat),
Sam Protsenkoa627f282023-11-07 13:06:01 -0600271 .probe = s5p_serial_probe,
272 .ops = &s5p_serial_ops,
Simon Glass73e256c2014-09-14 16:36:17 -0600273};
Simon Glass7fb57392015-07-02 18:15:55 -0600274#endif
Simon Glassbf6e7022015-07-02 18:15:54 -0600275
276#ifdef CONFIG_DEBUG_UART_S5P
277
278#include <debug_uart.h>
279
Simon Glass97b05972015-10-18 19:51:23 -0600280static inline void _debug_uart_init(void)
Simon Glassbf6e7022015-07-02 18:15:54 -0600281{
Dzmitry Sankouski85fcf942021-10-17 13:45:39 +0300282 if (IS_ENABLED(CONFIG_DEBUG_UART_SKIP_INIT))
283 return;
284
Pali Rohárb62450c2022-05-27 22:15:24 +0200285 struct s5p_uart *uart = (struct s5p_uart *)CONFIG_VAL(DEBUG_UART_BASE);
Simon Glassbf6e7022015-07-02 18:15:54 -0600286
287 s5p_serial_init(uart);
Mark Kettenis053827b2023-01-26 14:44:09 +0100288#if IS_ENABLED(CONFIG_ARCH_APPLE)
Mark Kettenisd520e1f2021-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 Glassbf6e7022015-07-02 18:15:54 -0600293}
294
295static inline void _debug_uart_putc(int ch)
296{
Pali Rohárb62450c2022-05-27 22:15:24 +0200297 struct s5p_uart *uart = (struct s5p_uart *)CONFIG_VAL(DEBUG_UART_BASE);
Simon Glassbf6e7022015-07-02 18:15:54 -0600298
Mark Kettenis053827b2023-01-26 14:44:09 +0100299#if IS_ENABLED(CONFIG_ARCH_APPLE)
Sam Protsenkoa627f282023-11-07 13:06:01 -0600300 while (readl(&uart->ufstat) & S5L_TX_FIFO_FULL)
301 ;
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200302 writel(ch, &uart->utxh);
303#else
Sam Protsenkoa627f282023-11-07 13:06:01 -0600304 while (readl(&uart->ufstat) & S5P_TX_FIFO_FULL)
305 ;
Simon Glassbf6e7022015-07-02 18:15:54 -0600306 writeb(ch, &uart->utxh);
Mark Kettenisd520e1f2021-10-23 16:58:04 +0200307#endif
Simon Glassbf6e7022015-07-02 18:15:54 -0600308}
309
310DEBUG_UART_FUNCS
311
312#endif