serial: s5p: Add Apple M1 support
authorMark Kettenis <kettenis@openbsd.org>
Sat, 23 Oct 2021 14:58:04 +0000 (16:58 +0200)
committerTom Rini <trini@konsulko.com>
Sun, 31 Oct 2021 12:46:44 +0000 (08:46 -0400)
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>
arch/arm/Kconfig
arch/arm/include/asm/arch-m1/uart.h [new file with mode: 0644]
configs/apple_m1_defconfig
drivers/serial/Kconfig
drivers/serial/serial_s5p.c

index 48188aa..293e3e3 100644 (file)
@@ -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 (file)
index 0000000..d2a17a2
--- /dev/null
@@ -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
index 6072e75..520d7c7 100644 (file)
@@ -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
index 122a397..7ee1290 100644 (file)
@@ -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.
index 6d09952..53a7b0b 100644 (file)
 #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