aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis2021-10-23 16:58:04 +0200
committerTom Rini2021-10-31 08:46:44 -0400
commitd520e1fb7276d9d14f3cf35e028c5fdb934b049c (patch)
tree99b79a26402e562e3dc337007ebf2cda1c38d7f0
parent003b657edcdc7ee96b7df277a16480129a5260a7 (diff)
serial: s5p: Add Apple M1 support
Apple M1 SoCs include an S5L UART which is a variant of the S5P UART. Add support for this variant and enable it by default on Apple SoCs. Signed-off-by: Mark Kettenis <kettenis@openbsd.org> Reviewed-by: Simon Glass <sjg@chromium.org>
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--arch/arm/include/asm/arch-m1/uart.h41
-rw-r--r--configs/apple_m1_defconfig4
-rw-r--r--drivers/serial/Kconfig4
-rw-r--r--drivers/serial/serial_s5p.c104
5 files changed, 130 insertions, 24 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 48188aad018..293e3e39283 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -924,6 +924,7 @@ config ARCH_APPLE
bool "Apple SoCs"
select ARM64
select BLK
+ select CLK
select CMD_USB
select DM
select DM_KEYBOARD
diff --git a/arch/arm/include/asm/arch-m1/uart.h b/arch/arm/include/asm/arch-m1/uart.h
new file mode 100644
index 00000000000..d2a17a221e3
--- /dev/null
+++ b/arch/arm/include/asm/arch-m1/uart.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2009 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Heungjun Kim <riverful.kim@samsung.com>
+ */
+
+#ifndef __ASM_ARCH_UART_H_
+#define __ASM_ARCH_UART_H_
+
+#ifndef __ASSEMBLY__
+/* baudrate rest value */
+union br_rest {
+ unsigned short slot; /* udivslot */
+ unsigned char value; /* ufracval */
+};
+
+struct s5p_uart {
+ unsigned int ulcon;
+ unsigned int ucon;
+ unsigned int ufcon;
+ unsigned int umcon;
+ unsigned int utrstat;
+ unsigned int uerstat;
+ unsigned int ufstat;
+ unsigned int umstat;
+ unsigned int utxh;
+ unsigned int urxh;
+ unsigned int ubrdiv;
+ union br_rest rest;
+ unsigned char res3[0x3fd0];
+};
+
+static inline int s5p_uart_divslot(void)
+{
+ return 0;
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif
diff --git a/configs/apple_m1_defconfig b/configs/apple_m1_defconfig
index 6072e7524c8..520d7c7632a 100644
--- a/configs/apple_m1_defconfig
+++ b/configs/apple_m1_defconfig
@@ -13,3 +13,7 @@ CONFIG_USE_PREBOOT=y
CONFIG_PREBOOT="usb start"
CONFIG_SYS_LOAD_ADDR=0x880000000
# CONFIG_GENERATE_SMBIOS_TABLE is not set
+CONFIG_DEBUG_UART=y
+CONFIG_DEBUG_UART_ANNOUNCE=y
+CONFIG_DEBUG_UART_BASE=0x235200000
+CONFIG_DEBUG_UART_CLOCK=240000
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 122a39789cb..7ee12901e78 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -290,7 +290,7 @@ config DEBUG_SBI_CONSOLE
config DEBUG_UART_S5P
bool "Samsung S5P"
- depends on ARCH_EXYNOS || ARCH_S5PC1XX
+ depends on ARCH_APPLE || ARCH_EXYNOS || ARCH_S5PC1XX
help
Select this to enable a debug UART using the serial_s5p driver. You
will need to provide parameters to make this work. The driver will
@@ -737,7 +737,7 @@ config ROCKCHIP_SERIAL
config S5P_SERIAL
bool "Support for Samsung S5P UART"
- depends on ARCH_EXYNOS || ARCH_S5PC1XX
+ depends on ARCH_APPLE || ARCH_EXYNOS || ARCH_S5PC1XX
default y
help
Select this to enable Samsung S5P UART support.
diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c
index 6d09952a5dc..53a7b0bd1bd 100644
--- a/drivers/serial/serial_s5p.c
+++ b/drivers/serial/serial_s5p.c
@@ -14,24 +14,45 @@
#include <asm/global_data.h>
#include <linux/compiler.h>
#include <asm/io.h>
+#if !CONFIG_IS_ENABLED(ARCH_APPLE)
#include <asm/arch/clk.h>
+#endif
#include <asm/arch/uart.h>
#include <serial.h>
#include <clk.h>
DECLARE_GLOBAL_DATA_PTR;
-#define RX_FIFO_COUNT_SHIFT 0
-#define RX_FIFO_COUNT_MASK (0xff << RX_FIFO_COUNT_SHIFT)
-#define RX_FIFO_FULL (1 << 8)
-#define TX_FIFO_COUNT_SHIFT 16
-#define TX_FIFO_COUNT_MASK (0xff << TX_FIFO_COUNT_SHIFT)
-#define TX_FIFO_FULL (1 << 24)
+enum {
+ PORT_S5P = 0,
+ PORT_S5L
+};
+
+#define S5L_RX_FIFO_COUNT_SHIFT 0
+#define S5L_RX_FIFO_COUNT_MASK (0xf << S5L_RX_FIFO_COUNT_SHIFT)
+#define S5L_RX_FIFO_FULL (1 << 8)
+#define S5L_TX_FIFO_COUNT_SHIFT 4
+#define S5L_TX_FIFO_COUNT_MASK (0xf << S5L_TX_FIFO_COUNT_SHIFT)
+#define S5L_TX_FIFO_FULL (1 << 9)
+
+#define S5P_RX_FIFO_COUNT_SHIFT 0
+#define S5P_RX_FIFO_COUNT_MASK (0xff << S5P_RX_FIFO_COUNT_SHIFT)
+#define S5P_RX_FIFO_FULL (1 << 8)
+#define S5P_TX_FIFO_COUNT_SHIFT 16
+#define S5P_TX_FIFO_COUNT_MASK (0xff << S5P_TX_FIFO_COUNT_SHIFT)
+#define S5P_TX_FIFO_FULL (1 << 24)
/* Information about a serial port */
struct s5p_serial_plat {
struct s5p_uart *reg; /* address of registers in physical memory */
+ u8 reg_width; /* register width */
u8 port_id; /* uart port number */
+ u8 rx_fifo_count_shift;
+ u8 tx_fifo_count_shift;
+ u32 rx_fifo_count_mask;
+ u32 tx_fifo_count_mask;
+ u32 rx_fifo_full;
+ u32 tx_fifo_full;
};
/*
@@ -71,8 +92,8 @@ static void __maybe_unused s5p_serial_init(struct s5p_uart *uart)
writel(0x245, &uart->ucon);
}
-static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk,
- int baudrate)
+static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, u8 reg_width,
+ uint uclk, int baudrate)
{
u32 val;
@@ -82,6 +103,8 @@ static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk,
if (s5p_uart_divslot())
writew(udivslot[val % 16], &uart->rest.slot);
+ else if (reg_width == 4)
+ writel(val % 16, &uart->rest.value);
else
writeb(val % 16, &uart->rest.value);
}
@@ -93,7 +116,7 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate)
struct s5p_uart *const uart = plat->reg;
u32 uclk;
-#ifdef CONFIG_CLK_EXYNOS
+#if CONFIG_IS_ENABLED(CLK_EXYNOS) || CONFIG_IS_ENABLED(ARCH_APPLE)
struct clk clk;
u32 ret;
@@ -105,7 +128,7 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate)
uclk = get_uart_clk(plat->port_id);
#endif
- s5p_serial_baud(uart, uclk, baudrate);
+ s5p_serial_baud(uart, plat->reg_width, uclk, baudrate);
return 0;
}
@@ -144,11 +167,14 @@ static int s5p_serial_getc(struct udevice *dev)
struct s5p_serial_plat *plat = dev_get_plat(dev);
struct s5p_uart *const uart = plat->reg;
- if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK))
+ if (!(readl(&uart->ufstat) & plat->rx_fifo_count_mask))
return -EAGAIN;
serial_err_check(uart, 0);
- return (int)(readb(&uart->urxh) & 0xff);
+ if (plat->reg_width == 4)
+ return (int)(readl(&uart->urxh) & 0xff);
+ else
+ return (int)(readb(&uart->urxh) & 0xff);
}
static int s5p_serial_putc(struct udevice *dev, const char ch)
@@ -156,10 +182,13 @@ static int s5p_serial_putc(struct udevice *dev, const char ch)
struct s5p_serial_plat *plat = dev_get_plat(dev);
struct s5p_uart *const uart = plat->reg;
- if (readl(&uart->ufstat) & TX_FIFO_FULL)
+ if (readl(&uart->ufstat) & plat->tx_fifo_full)
return -EAGAIN;
- writeb(ch, &uart->utxh);
+ if (plat->reg_width == 4)
+ writel(ch, &uart->utxh);
+ else
+ writeb(ch, &uart->utxh);
serial_err_check(uart, 1);
return 0;
@@ -171,15 +200,19 @@ static int s5p_serial_pending(struct udevice *dev, bool input)
struct s5p_uart *const uart = plat->reg;
uint32_t ufstat = readl(&uart->ufstat);
- if (input)
- return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT;
- else
- return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT;
+ if (input) {
+ return (ufstat & plat->rx_fifo_count_mask) >>
+ plat->rx_fifo_count_shift;
+ } else {
+ return (ufstat & plat->tx_fifo_count_mask) >>
+ plat->tx_fifo_count_shift;
+ }
}
static int s5p_serial_of_to_plat(struct udevice *dev)
{
struct s5p_serial_plat *plat = dev_get_plat(dev);
+ const ulong port_type = dev_get_driver_data(dev);
fdt_addr_t addr;
addr = dev_read_addr(dev);
@@ -187,8 +220,26 @@ static int s5p_serial_of_to_plat(struct udevice *dev)
return -EINVAL;
plat->reg = (struct s5p_uart *)addr;
+ plat->reg_width = dev_read_u32_default(dev, "reg-io-width", 1);
plat->port_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
"id", dev_seq(dev));
+
+ if (port_type == PORT_S5L) {
+ plat->rx_fifo_count_shift = S5L_RX_FIFO_COUNT_SHIFT;
+ plat->rx_fifo_count_mask = S5L_RX_FIFO_COUNT_MASK;
+ plat->rx_fifo_full = S5L_RX_FIFO_FULL;
+ plat->tx_fifo_count_shift = S5L_TX_FIFO_COUNT_SHIFT;
+ plat->tx_fifo_count_mask = S5L_TX_FIFO_COUNT_MASK;
+ plat->tx_fifo_full = S5L_TX_FIFO_FULL;
+ } else {
+ plat->rx_fifo_count_shift = S5P_RX_FIFO_COUNT_SHIFT;
+ plat->rx_fifo_count_mask = S5P_RX_FIFO_COUNT_MASK;
+ plat->rx_fifo_full = S5P_RX_FIFO_FULL;
+ plat->tx_fifo_count_shift = S5P_TX_FIFO_COUNT_SHIFT;
+ plat->tx_fifo_count_mask = S5P_TX_FIFO_COUNT_MASK;
+ plat->tx_fifo_full = S5P_TX_FIFO_FULL;
+ }
+
return 0;
}
@@ -200,7 +251,8 @@ static const struct dm_serial_ops s5p_serial_ops = {
};
static const struct udevice_id s5p_serial_ids[] = {
- { .compatible = "samsung,exynos4210-uart" },
+ { .compatible = "samsung,exynos4210-uart", .data = PORT_S5P },
+ { .compatible = "apple,s5l-uart", .data = PORT_S5L },
{ }
};
@@ -224,16 +276,24 @@ static inline void _debug_uart_init(void)
struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE;
s5p_serial_init(uart);
- s5p_serial_baud(uart, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
+#if CONFIG_IS_ENABLED(ARCH_APPLE)
+ s5p_serial_baud(uart, 4, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
+#else
+ s5p_serial_baud(uart, 1, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
+#endif
}
static inline void _debug_uart_putc(int ch)
{
struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE;
- while (readl(&uart->ufstat) & TX_FIFO_FULL);
-
+#if CONFIG_IS_ENABLED(ARCH_APPLE)
+ while (readl(&uart->ufstat) & S5L_TX_FIFO_FULL);
+ writel(ch, &uart->utxh);
+#else
+ while (readl(&uart->ufstat) & S5P_TX_FIFO_FULL);
writeb(ch, &uart->utxh);
+#endif
}
DEBUG_UART_FUNCS