[PATCH v2 4/7] serial: s5p: Add Apple M1 support

Mark Kettenis kettenis at openbsd.org
Sun Oct 3 20:30:30 CEST 2021


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 at openbsd.org>
---
 arch/arm/Kconfig                    |   1 +
 arch/arm/include/asm/arch-m1/uart.h |  41 +++++++++++
 configs/apple_m1_defconfig          |   4 ++
 drivers/serial/Kconfig              |   4 +-
 drivers/serial/serial_s5p.c         | 104 ++++++++++++++++++++++------
 5 files changed, 130 insertions(+), 24 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-m1/uart.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4948dad4df..876d9c4044 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -899,6 +899,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 0000000000..d2a17a221e
--- /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 at samsung.com>
+ * Heungjun Kim <riverful.kim at 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 a7ae15576b..674b74b90b 100644
--- a/configs/apple_m1_defconfig
+++ b/configs/apple_m1_defconfig
@@ -12,3 +12,7 @@ CONFIG_USB_STORAGE=y
 CONFIG_USE_PREBOOT=y
 CONFIG_PREBOOT="usb start"
 # 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 93348c0929..033d160579 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -272,7 +272,7 @@ config DEBUG_EFI_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
@@ -719,7 +719,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 6d09952a5d..53a7b0bd1b 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
-- 
2.33.0



More information about the U-Boot mailing list