Module Name: src Committed By: jmcneill Date: Thu Jul 5 13:11:58 UTC 2018
Modified Files: src/sys/arch/arm/samsung: exynos5422_clock.c exynos_soc.c files.exynos sscom_reg.h src/sys/arch/evbarm/conf: EXYNOS src/sys/arch/evbarm/exynos: exynos_start.S Added Files: src/sys/arch/arm/samsung: exynos_uart.c Removed Files: src/sys/arch/arm/samsung: exynos_sscom.c sscom.c sscom_var.h Log Message: Replace sscom with a much simpler uart driver. The simpler driver is 1/4th the size and has the added benefit of not freezing when an arrow key is pressed. To generate a diff of this commit: cvs rdiff -u -r1.11 -r1.12 src/sys/arch/arm/samsung/exynos5422_clock.c cvs rdiff -u -r1.32 -r1.33 src/sys/arch/arm/samsung/exynos_soc.c cvs rdiff -u -r1.11 -r0 src/sys/arch/arm/samsung/exynos_sscom.c cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/samsung/exynos_uart.c cvs rdiff -u -r1.29 -r1.30 src/sys/arch/arm/samsung/files.exynos cvs rdiff -u -r1.10 -r0 src/sys/arch/arm/samsung/sscom.c cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/samsung/sscom_reg.h cvs rdiff -u -r1.5 -r0 src/sys/arch/arm/samsung/sscom_var.h cvs rdiff -u -r1.29 -r1.30 src/sys/arch/evbarm/conf/EXYNOS cvs rdiff -u -r1.5 -r1.6 src/sys/arch/evbarm/exynos/exynos_start.S Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/samsung/exynos5422_clock.c diff -u src/sys/arch/arm/samsung/exynos5422_clock.c:1.11 src/sys/arch/arm/samsung/exynos5422_clock.c:1.12 --- src/sys/arch/arm/samsung/exynos5422_clock.c:1.11 Wed Jul 4 23:06:28 2018 +++ src/sys/arch/arm/samsung/exynos5422_clock.c Thu Jul 5 13:11:58 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: exynos5422_clock.c,v 1.11 2018/07/04 23:06:28 jmcneill Exp $ */ +/* $NetBSD: exynos5422_clock.c,v 1.12 2018/07/05 13:11:58 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -29,7 +29,7 @@ #include "locators.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: exynos5422_clock.c,v 1.11 2018/07/04 23:06:28 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: exynos5422_clock.c,v 1.12 2018/07/05 13:11:58 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -555,6 +555,24 @@ static struct exynos_clk exynos5422_cloc CLK_GATE("usbd301", "aclk200_fsys", EXYNOS5422_GATE_IP_FSYS, __BIT(20), CLK_SET_RATE_PARENT), + CLK_GATE("uart0", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(0), CLK_SET_RATE_PARENT), + CLK_GATE("uart1", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(1), CLK_SET_RATE_PARENT), + CLK_GATE("uart2", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(2), CLK_SET_RATE_PARENT), + CLK_GATE("uart3", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(3), CLK_SET_RATE_PARENT), + CLK_GATE("i2c0", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(6), CLK_SET_RATE_PARENT), + CLK_GATE("i2c1", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(7), CLK_SET_RATE_PARENT), + CLK_GATE("i2c2", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(8), CLK_SET_RATE_PARENT), + CLK_GATE("i2c3", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(9), CLK_SET_RATE_PARENT), + CLK_GATE("i2c_hdmi", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, + __BIT(14), CLK_SET_RATE_PARENT), CLK_GATE("pwm", "mout_user_aclk66_peric", EXYNOS5422_GATE_IP_PERIC, __BIT(24), CLK_SET_RATE_PARENT), }; Index: src/sys/arch/arm/samsung/exynos_soc.c diff -u src/sys/arch/arm/samsung/exynos_soc.c:1.32 src/sys/arch/arm/samsung/exynos_soc.c:1.33 --- src/sys/arch/arm/samsung/exynos_soc.c:1.32 Sat Jun 10 15:13:18 2017 +++ src/sys/arch/arm/samsung/exynos_soc.c Thu Jul 5 13:11:58 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: exynos_soc.c,v 1.32 2017/06/10 15:13:18 jmcneill Exp $ */ +/* $NetBSD: exynos_soc.c,v 1.33 2018/07/05 13:11:58 jmcneill Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ #include "opt_exynos.h" #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: exynos_soc.c,v 1.32 2017/06/10 15:13:18 jmcneill Exp $"); +__KERNEL_RCSID(1, "$NetBSD: exynos_soc.c,v 1.33 2018/07/05 13:11:58 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -137,47 +137,6 @@ bus_space_handle_t exynos_sysreg_bsh; static int sysctl_cpufreq_target(SYSCTLFN_ARGS); static int sysctl_cpufreq_current(SYSCTLFN_ARGS); -/* - * the early serial console - */ -#ifdef EXYNOS_CONSOLE_EARLY - -#include "opt_sscom.h" -#include <arm/samsung/sscom_reg.h> -#include <arm/samsung/sscom_var.h> -#include <dev/cons.h> - -static volatile uint8_t *uart_base; - -#define CON_REG(a) (*((volatile uint32_t *)(uart_base + (a)))) - -static int -exynos_cngetc(dev_t dv) -{ - if ((CON_REG(SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == 0) - return -1; - - return CON_REG(SSCOM_URXH); -} - -static void -exynos_cnputc(dev_t dv, int c) -{ - int timo = 150000; - - while ((CON_REG(SSCOM_UFSTAT) & UFSTAT_TXFULL) && --timo > 0); - - CON_REG(SSCOM_UTXH) = c & 0xff; -} - -static struct consdev exynos_earlycons = { - .cn_putc = exynos_cnputc, - .cn_getc = exynos_cngetc, - .cn_pollc = nullcnpollc, -}; -#endif /* EXYNOS_CONSOLE_EARLY */ - - #ifdef ARM_TRUSTZONE_FIRMWARE int exynos_do_idle(void) Index: src/sys/arch/arm/samsung/files.exynos diff -u src/sys/arch/arm/samsung/files.exynos:1.29 src/sys/arch/arm/samsung/files.exynos:1.30 --- src/sys/arch/arm/samsung/files.exynos:1.29 Wed Jul 4 23:06:54 2018 +++ src/sys/arch/arm/samsung/files.exynos Thu Jul 5 13:11:58 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.exynos,v 1.29 2018/07/04 23:06:54 jmcneill Exp $ +# $NetBSD: files.exynos,v 1.30 2018/07/05 13:11:58 jmcneill Exp $ # # Configuration info for Samsung Exynos SoC ARM Peripherals # @@ -66,12 +66,9 @@ attach exyowdt at fdt with exynos_wdt file arch/arm/samsung/exynos_wdt.c exynos_wdt needs-flag # UARTs -device sscom { } : bus_space_generic -attach sscom at fdt with exynos_sscom -file arch/arm/samsung/sscom.c sscom needs-flag -file arch/arm/samsung/exynos_sscom.c exynos_sscom -defflag opt_sscom.h SSCOM0CONSOLE SSCOM1CONSOLE -defparam opt_sscom.h SSCOM_FREQ +device exuart +attach exuart at fdt with exynos_uart +file arch/arm/samsung/exynos_uart.c exynos_uart # PINCTL device exyopctl : gpiobus Index: src/sys/arch/arm/samsung/sscom_reg.h diff -u src/sys/arch/arm/samsung/sscom_reg.h:1.2 src/sys/arch/arm/samsung/sscom_reg.h:1.3 --- src/sys/arch/arm/samsung/sscom_reg.h:1.2 Mon Apr 14 21:16:15 2014 +++ src/sys/arch/arm/samsung/sscom_reg.h Thu Jul 5 13:11:58 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sscom_reg.h,v 1.2 2014/04/14 21:16:15 reinoud Exp $ */ +/* $NetBSD: sscom_reg.h,v 1.3 2018/07/05 13:11:58 jmcneill Exp $ */ /* * Copyright (c) 2002, 2003 Fujitsu Component Limited @@ -49,6 +49,7 @@ #define ULCON_PARITY_ONE __SHIFTIN(6, ULCON_PARITY) #define ULCON_PARITY_ZERO __SHIFTIN(7, ULCON_PARITY) #define ULCON_STOP __BIT(2) +#define ULCON_LENGTH __BITS(1,0) #define ULCON_LENGTH_5 0 #define ULCON_LENGTH_6 1 #define ULCON_LENGTH_7 2 Index: src/sys/arch/evbarm/conf/EXYNOS diff -u src/sys/arch/evbarm/conf/EXYNOS:1.29 src/sys/arch/evbarm/conf/EXYNOS:1.30 --- src/sys/arch/evbarm/conf/EXYNOS:1.29 Wed Jul 4 23:08:29 2018 +++ src/sys/arch/evbarm/conf/EXYNOS Thu Jul 5 13:11:57 2018 @@ -1,5 +1,5 @@ # -# $NetBSD: EXYNOS,v 1.29 2018/07/04 23:08:29 jmcneill Exp $ +# $NetBSD: EXYNOS,v 1.30 2018/07/05 13:11:57 jmcneill Exp $ # # Samsung Exynos SoC kernel # @@ -28,8 +28,6 @@ pseudo-device openfirm # /dev/openfirm #options PMAP_DEBUG # Enable pmap_debug_level code #options IPKDB # remote kernel debugging #options VERBOSE_INIT_ARM # verbose bootstrapping messages -# SSCOMnCONSOLE is required for early init messages from VERBOSE_INIT_ARM. -#options SSCOM2CONSOLE makeoptions DEBUG="-g" # compile full symbol table makeoptions COPY_SYMTAB=1 @@ -76,7 +74,7 @@ expwm* at fdt? pass 4 # PWM pwmfan* at fdt? # PWM Fan controls # UART -sscom* at fdt? # UART +exuart* at fdt? # UART # I2C exyoi2c* at fdt? # I2C Index: src/sys/arch/evbarm/exynos/exynos_start.S diff -u src/sys/arch/evbarm/exynos/exynos_start.S:1.5 src/sys/arch/evbarm/exynos/exynos_start.S:1.6 --- src/sys/arch/evbarm/exynos/exynos_start.S:1.5 Sat Mar 3 13:46:32 2018 +++ src/sys/arch/evbarm/exynos/exynos_start.S Thu Jul 5 13:11:58 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: exynos_start.S,v 1.5 2018/03/03 13:46:32 skrll Exp $ */ +/* $NetBSD: exynos_start.S,v 1.6 2018/07/05 13:11:58 jmcneill Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -30,7 +30,6 @@ */ #include "opt_exynos.h" -#include "opt_sscom.h" #include "opt_cpuoptions.h" #include "opt_cputypes.h" #include "opt_multiprocessor.h" @@ -46,24 +45,10 @@ #include <evbarm/exynos/platform.h> -RCSID("$NetBSD: exynos_start.S,v 1.5 2018/03/03 13:46:32 skrll Exp $") +RCSID("$NetBSD: exynos_start.S,v 1.6 2018/07/05 13:11:58 jmcneill Exp $") - -#if defined(VERBOSE_INIT_ARM) - -#define XPUTC(n) mov r0, n; bl exynos_putc -#if KERNEL_BASE_VOFFSET == 0 -#define XPUTC2(n) mov r0, n; bl exynos_putc -#else -#define XPUTC2(n) mov r0, n; blx r11 -#endif -#ifdef __ARMEB__ -#define COM_BSWAP -#endif -#else -#define XPUTC(n) -#define XPUTC2(n) -#endif +#define XPUTC(n) +#define XPUTC2(n) #define INIT_MEMSIZE 128 @@ -135,23 +120,6 @@ _C_LABEL(exynos_start): str r0, [r4] // save soc_id mov r5, r0 // save soc_id - /* Pick uart address for the SoC */ - adr r1, .Lsscom_exynos5_table -#ifdef SSCOM0CONSOLE - ldr r2, [r1, #0*8+4] -#endif -#ifdef SSCOM1CONSOLE - ldr r2, [r1, #1*8+4] -#endif -#ifdef SSCOM2CONSOLE - ldr r2, [r1, #2*8+4] -#endif -#ifdef SSCOM3CONSOLE - ldr r2, [r1, #3*8+4] -#endif - add r2, r2, #EXYNOS_CORE_PBASE - mcr p15, 0, r2, c13, c0, 3 // TPIDRURO set (uart address) - /* * Turn on the SMP bit */ @@ -183,9 +151,6 @@ _C_LABEL(exynos_start): * so setup the lr to be in .text. Cache the address for exynos_putc * before we go. */ -#if defined(VERBOSE_INIT_ARM) - adr r11, exynos_putc @ for XPUTC2 -#endif movw lr, #:lower16:1f movt lr, #:upper16:1f b arm_cpuinit @@ -207,63 +172,6 @@ _C_LABEL(exynos_start): .popsection #endif - .align 0 - .global _C_LABEL(num_exynos_uarts_entries) -_C_LABEL(num_exynos_uarts_entries): - .word 8 // update number of entries!!! - .global _C_LABEL(exynos_uarts) -_C_LABEL(exynos_uarts): -.Lsscom_exynos5_table: - .word 0 - .word EXYNOS5_UART0_OFFSET - .word 1 - .word EXYNOS5_UART1_OFFSET - .word 2 - .word EXYNOS5_UART2_OFFSET - .word 3 - .word EXYNOS5_UART3_OFFSET - - -#if defined(VERBOSE_INIT_ARM) - .align 0 - .global exynos_putc - .global _C_LABEL(exynos_putchar) - .type exynos_putc,%function - -#define TIMO 0x25000 -_C_LABEL(exynos_putchar): -exynos_putc: - mov r2, #TIMO - mrc p15, 0, r3, c13, c0, 3 // TPIDRURO get (uart address) -1: - ldr r1, [r3, #SSCOM_UTRSTAT] -#ifdef __ARMEB__ - rev r1, r1 -#endif - tst r1, #UTRSTAT_TXEMPTY - bne 2f - subs r2, r2, #1 - bne 1b -2: -#ifdef __ARMEB__ - rev r0, r0 -#endif - str r0, [r3, #SSCOM_UTXH] - - mov r2, #TIMO -3: - ldr r1, [r3, #SSCOM_UTRSTAT] -#ifdef __ARMEB__ - rev r1, r1 -#endif - tst r1, #UTRSTAT_TXSHIFTER_EMPTY - bne 4f - subs r2, r2, #1 - bne 3b -4: - bx lr -#endif - #include <arm/cortex/a9_mpsubr.S> .align 0 @@ -289,6 +197,26 @@ mmu_init_table: EXYNOS_CORE_SIZE / L1_S_SIZE, L1_S_PROTO_armv7 | L1_S_APv7_KRW | L1_S_V6_XN) + /* Map EXYNOS AUDIOBASE */ + MMU_INIT(EXYNOS5_AUDIOCORE_VBASE, EXYNOS5_AUDIOCORE_VBASE, + EXYNOS5_AUDIOCORE_SIZE / L1_S_SIZE, + L1_S_PROTO_armv7 | L1_S_APv7_KRW | L1_S_V6_XN) + + /* Map EXYNOS AUDIOBASE */ + MMU_INIT(EXYNOS5_AUDIOCORE_PBASE, EXYNOS5_AUDIOCORE_VBASE, + EXYNOS5_AUDIOCORE_SIZE / L1_S_SIZE, + L1_S_PROTO_armv7 | L1_S_APv7_KRW | L1_S_V6_XN) + + /* Map sysram for MP startup */ + MMU_INIT(EXYNOS5_SYSRAM_VBASE, EXYNOS5_SYSRAM_PBASE, + EXYNOS5_SYSRAM_SIZE / L1_S_SIZE, + L1_S_PROTO_armv7 | L1_S_APv7_KRW | L1_S_V6_XN) + + /* Map sysram for MP startup */ + MMU_INIT(EXYNOS5_SYSRAM_PBASE, EXYNOS5_SYSRAM_PBASE, + EXYNOS5_SYSRAM_SIZE / L1_S_SIZE, + L1_S_PROTO_armv7 | L1_S_APv7_KRW | L1_S_V6_XN) + /* Map DTB location in SDRAM, patched in later */ .Lmmu_init_table_dtb: MMU_INIT(0, 0, 0, 0) Added files: Index: src/sys/arch/arm/samsung/exynos_uart.c diff -u /dev/null src/sys/arch/arm/samsung/exynos_uart.c:1.1 --- /dev/null Thu Jul 5 13:11:58 2018 +++ src/sys/arch/arm/samsung/exynos_uart.c Thu Jul 5 13:11:58 2018 @@ -0,0 +1,598 @@ +/* $NetBSD: exynos_uart.c,v 1.1 2018/07/05 13:11:58 jmcneill Exp $ */ + +/*- + * Copyright (c) 2013-2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Thomas of 3am Software Foundry and Jared McNeill. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "locators.h" + +#include <sys/cdefs.h> + +__KERNEL_RCSID(1, "$NetBSD: exynos_uart.c,v 1.1 2018/07/05 13:11:58 jmcneill Exp $"); + +#define cn_trap() \ + do { \ + console_debugger(); \ + cn_trapped = 1; \ + } while (/* CONSTCOND */ 0) + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/intr.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/termios.h> +#include <sys/kauth.h> +#include <sys/lwp.h> +#include <sys/tty.h> + +#include <dev/cons.h> + +#include <dev/fdt/fdtvar.h> + +#include <arm/samsung/sscom_reg.h> + +static int exynos_uart_match(device_t, cfdata_t, void *); +static void exynos_uart_attach(device_t, device_t, void *); + +static int exynos_uart_intr(void *); + +static int exynos_uart_cngetc(dev_t); +static void exynos_uart_cnputc(dev_t, int); +static void exynos_uart_cnpollc(dev_t, int); + +static void exynos_uart_start(struct tty *); +static int exynos_uart_param(struct tty *, struct termios *); + +extern struct cfdriver exuart_cd; + +struct exynos_uart_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + u_int sc_freq; + void *sc_ih; + + bool sc_console; + struct tty *sc_tty; + + int sc_ospeed; + tcflag_t sc_cflag; + + u_char sc_buf[1024]; +}; + +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +static bus_addr_t exynos_uart_consaddr; + +static struct exynos_uart_softc exynos_uart_cnsc; + +static struct cnm_state exynos_uart_cnm_state; + +struct consdev exynos_uart_consdev = { + .cn_getc = exynos_uart_cngetc, + .cn_putc = exynos_uart_cnputc, + .cn_pollc = exynos_uart_cnpollc, + .cn_dev = NODEV, + .cn_pri = CN_NORMAL, +}; + +static dev_type_open(exynos_uart_open); +static dev_type_open(exynos_uart_close); +static dev_type_read(exynos_uart_read); +static dev_type_write(exynos_uart_write); +static dev_type_ioctl(exynos_uart_ioctl); +static dev_type_tty(exynos_uart_tty); +static dev_type_poll(exynos_uart_poll); +static dev_type_stop(exynos_uart_stop); + +const struct cdevsw exuart_cdevsw = { + .d_open = exynos_uart_open, + .d_close = exynos_uart_close, + .d_read = exynos_uart_read, + .d_write = exynos_uart_write, + .d_ioctl = exynos_uart_ioctl, + .d_stop = exynos_uart_stop, + .d_tty = exynos_uart_tty, + .d_poll = exynos_uart_poll, + .d_mmap = nommap, + .d_kqfilter = ttykqfilter, + .d_discard = nodiscard, + .d_flag = D_TTY +}; + +static int exynos_uart_cmajor = -1; + +static const char * const compatible[] = { + "samsung,exynos4210-uart", + NULL +}; + +CFATTACH_DECL_NEW(exynos_uart, sizeof(struct exynos_uart_softc), + exynos_uart_match, exynos_uart_attach, NULL, NULL); + +static int +exynos_uart_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +exynos_uart_attach(device_t parent, device_t self, void *aux) +{ + struct exynos_uart_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + char intrstr[128]; + struct clk *clk_uart, *clk_uart_baud0; + struct tty *tp; + int major, minor; + bus_addr_t addr; + bus_size_t size; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": failed to decode interrupt\n"); + return; + } + clk_uart = fdtbus_clock_get(phandle, "uart"); + if (clk_uart == NULL || clk_enable(clk_uart) != 0) { + aprint_error(": failed to enable uart clock\n"); + return; + } + clk_uart_baud0 = fdtbus_clock_get(phandle, "clk_uart_baud0"); + if (clk_uart_baud0 == NULL || clk_enable(clk_uart_baud0) != 0) { + aprint_error(": failed to enable clk_uart_baud0 clock\n"); + return; + } + + const bool is_console = exynos_uart_consaddr == addr; + + sc->sc_dev = self; + sc->sc_bst = faa->faa_bst; + sc->sc_console = is_console; + if (is_console) { + sc->sc_bsh = exynos_uart_cnsc.sc_bsh; + } else { + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": failed to map registers\n"); + return; + } + } + sc->sc_freq = clk_get_rate(clk_uart_baud0); + + sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_SERIAL, + 0, exynos_uart_intr, sc); + if (sc->sc_ih == NULL) { + aprint_error(": failed to establish interrupt on %s\n", + intrstr); + return; + } + + if (exynos_uart_cmajor == -1) { + /* allocate a major number */ + int bmajor = -1, cmajor = -1; + int error = devsw_attach("exuart", NULL, &bmajor, + &exuart_cdevsw, &cmajor); + if (error) { + aprint_error(": couldn't allocate major number\n"); + return; + } + exynos_uart_cmajor = cmajor; + } + + major = cdevsw_lookup_major(&exuart_cdevsw); + minor = device_unit(self); + + tp = sc->sc_tty = tty_alloc(); + tp->t_oproc = exynos_uart_start; + tp->t_param = exynos_uart_param; + tp->t_dev = makedev(major, minor); + tp->t_sc = sc; + tty_attach(tp); + + aprint_naive("\n"); + if (is_console) { + cn_tab->cn_dev = tp->t_dev; + aprint_normal(": console"); + } + aprint_normal("\n"); + + if (is_console) + delay(10000); + + /* Initialize device */ + WR4(sc, SSCOM_UFCON, + __SHIFTIN(2, UFCON_TXTRIGGER) | + __SHIFTIN(1, UFCON_RXTRIGGER) | + UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET | + UFCON_FIFO_ENABLE); + /* Configure PIO mode with RX timeout interrupts */ + WR4(sc, SSCOM_UCON, + __SHIFTIN(3, UCON_RXTO) | + UCON_TOINT | UCON_ERRINT | + UCON_TXMODE_INT | UCON_RXMODE_INT); + + /* Disable interrupts */ + WR4(sc, SSCOM_UINTM, ~0u); + + aprint_normal_dev(self, "interrupting on %s\n", intrstr); +} + +static int +exynos_uart_cngetc(dev_t dev) +{ + struct exynos_uart_softc * const sc = &exynos_uart_cnsc; + uint32_t status; + int s, c; + + s = splserial(); + + status = RD4(sc, SSCOM_UTRSTAT); + if ((status & UTRSTAT_RXREADY) == 0) { + splx(s); + return -1; + } + + c = bus_space_read_1(sc->sc_bst, sc->sc_bsh, SSCOM_URXH); +#if defined(DDB) + extern int db_active; + if (!db_active) +#endif + { + int cn_trapped __unused = 0; + cn_check_magic(dev, c, exynos_uart_cnm_state); + } + + splx(s); + + return c & 0xff; +} + +static void +exynos_uart_cnputc(dev_t dev, int c) +{ + struct exynos_uart_softc * const sc = &exynos_uart_cnsc; + int s; + + s = splserial(); + while ((RD4(sc, SSCOM_UFSTAT) & UFSTAT_TXFULL) != 0) + ; + + bus_space_write_1(sc->sc_bst, sc->sc_bsh, SSCOM_UTXH, c); + + splx(s); +} + + +static void +exynos_uart_cnpollc(dev_t dev, int on) +{ +} + +static void +exynos_uart_cnattach(bus_space_tag_t bst, bus_space_handle_t bsh, + int ospeed, tcflag_t cflag) +{ + struct exynos_uart_softc *sc = &exynos_uart_cnsc; + + cn_tab = &exynos_uart_consdev; + cn_init_magic(&exynos_uart_cnm_state); + cn_set_magic("\047\001"); + + sc->sc_bst = bst; + sc->sc_bsh = bsh; + sc->sc_ospeed = ospeed; + sc->sc_cflag = cflag; +} + +static int +exynos_uart_open(dev_t dev, int flag, int mode, lwp_t *l) +{ + struct exynos_uart_softc *sc = + device_lookup_private(&exuart_cd, minor(dev)); + struct tty *tp = sc->sc_tty; + + if (kauth_authorize_device_tty(l->l_cred, + KAUTH_DEVICE_TTY_OPEN, tp) != 0) { + return EBUSY; + } + + if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) { + tp->t_dev = dev; + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_lflag = TTYDEF_LFLAG; + if (sc->sc_console) { + tp->t_ispeed = tp->t_ospeed = exynos_uart_cnsc.sc_ospeed; + tp->t_cflag = exynos_uart_cnsc.sc_cflag; + } else { + tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + tp->t_cflag = TTYDEF_CFLAG; + } + ttsetwater(tp); + } + tp->t_state |= TS_CARR_ON; + + /* Enable RX and error interrupts */ + WR4(sc, SSCOM_UINTM, ~0u & ~(UINT_RXD|UINT_ERROR)); + + return tp->t_linesw->l_open(dev, tp); +} + +static int +exynos_uart_close(dev_t dev, int flag, int mode, lwp_t *l) +{ + struct exynos_uart_softc *sc = + device_lookup_private(&exuart_cd, minor(dev)); + struct tty *tp = sc->sc_tty; + + tp->t_linesw->l_close(tp, flag); + ttyclose(tp); + + /* Disable interrupts */ + WR4(sc, SSCOM_UINTM, ~0u); + + return 0; +} + +static int +exynos_uart_read(dev_t dev, struct uio *uio, int flag) +{ + struct exynos_uart_softc *sc = + device_lookup_private(&exuart_cd, minor(dev)); + struct tty *tp = sc->sc_tty; + + return tp->t_linesw->l_read(tp, uio, flag); +} + +static int +exynos_uart_write(dev_t dev, struct uio *uio, int flag) +{ + struct exynos_uart_softc *sc = + device_lookup_private(&exuart_cd, minor(dev)); + struct tty *tp = sc->sc_tty; + + return tp->t_linesw->l_write(tp, uio, flag); +} + +static int +exynos_uart_poll(dev_t dev, int events, lwp_t *l) +{ + struct exynos_uart_softc *sc = + device_lookup_private(&exuart_cd, minor(dev)); + struct tty *tp = sc->sc_tty; + + return tp->t_linesw->l_poll(tp, events, l); +} + +static int +exynos_uart_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) +{ + struct exynos_uart_softc *sc = + device_lookup_private(&exuart_cd, minor(dev)); + struct tty *tp = sc->sc_tty; + int error; + + error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l); + if (error != EPASSTHROUGH) + return error; + + return ttioctl(tp, cmd, data, flag, l); +} + +static struct tty * +exynos_uart_tty(dev_t dev) +{ + struct exynos_uart_softc *sc = + device_lookup_private(&exuart_cd, minor(dev)); + + return sc->sc_tty; +} + +static void +exynos_uart_stop(struct tty *tp, int flag) +{ +} + +static void +exynos_uart_start(struct tty *tp) +{ + struct exynos_uart_softc *sc = tp->t_sc; + u_char *p = sc->sc_buf; + int s, brem; + + s = spltty(); + + if (tp->t_state & (TS_TTSTOP | TS_BUSY | TS_TIMEOUT)) { + splx(s); + return; + } + tp->t_state |= TS_BUSY; + + splx(s); + + for (brem = q_to_b(&tp->t_outq, sc->sc_buf, sizeof(sc->sc_buf)); + brem > 0; + brem--, p++) { + while ((RD4(sc, SSCOM_UFSTAT) & UFSTAT_TXFULL) != 0) + ; + + bus_space_write_1(sc->sc_bst, sc->sc_bsh, + SSCOM_UTXH, *p); + } + + s = spltty(); + tp->t_state &= ~TS_BUSY; + if (ttypull(tp)) { + tp->t_state |= TS_TIMEOUT; + callout_schedule(&tp->t_rstrt_ch, 1); + } + splx(s); +} + +static int +exynos_uart_param(struct tty *tp, struct termios *t) +{ + struct exynos_uart_softc *sc = tp->t_sc; + + if (tp->t_ospeed == t->c_ospeed && + tp->t_cflag == t->c_cflag) + return 0; + + uint32_t ulcon = 0, ubrdiv; + switch (ISSET(t->c_cflag, CSIZE)) { + case CS5: + ulcon |= ULCON_LENGTH_5; + break; + case CS6: + ulcon |= ULCON_LENGTH_6; + break; + case CS7: + ulcon |= ULCON_LENGTH_7; + break; + case CS8: + ulcon |= ULCON_LENGTH_8; + break; + } + switch (ISSET(t->c_cflag, PARENB|PARODD)) { + case PARENB|PARODD: + ulcon |= ULCON_PARITY_ODD; + break; + case PARENB: + ulcon |= ULCON_PARITY_EVEN; + break; + default: + ulcon |= ULCON_PARITY_NONE; + break; + } + if (ISSET(t->c_cflag, CSTOPB)) + ulcon |= ULCON_STOP; + WR4(sc, SSCOM_ULCON, ulcon); + + ubrdiv = (sc->sc_freq / 16) / t->c_ospeed - 1; + WR4(sc, SSCOM_UBRDIV, ubrdiv); + + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + + return 0; +} + +static int +exynos_uart_intr(void *priv) +{ + struct exynos_uart_softc *sc = priv; + struct tty *tp = sc->sc_tty; + uint32_t uintp, uerstat, ufstat, c; + + uintp = RD4(sc, SSCOM_UINTP); + + for (;;) { + int cn_trapped = 0; + + uerstat = RD4(sc, SSCOM_UERSTAT); + if (uerstat & UERSTAT_BREAK) { + cn_check_magic(tp->t_dev, CNC_BREAK, + exynos_uart_cnm_state); + if (cn_trapped) + continue; + } + + ufstat = RD4(sc, SSCOM_UFSTAT); + if (__SHIFTOUT(ufstat, UFSTAT_RXCOUNT) == 0) { + break; + } + + c = bus_space_read_1(sc->sc_bst, sc->sc_bsh, SSCOM_URXH); + cn_check_magic(tp->t_dev, c & 0xff, exynos_uart_cnm_state); + if (cn_trapped) + continue; + tp->t_linesw->l_rint(c & 0xff, tp); + } + + WR4(sc, SSCOM_UINTP, uintp); + + return 1; +} + +/* + * Console support + */ + +static int +exynos_uart_console_match(int phandle) +{ + return of_match_compatible(phandle, compatible); +} + +static void +exynos_uart_console_consinit(struct fdt_attach_args *faa, u_int uart_freq) +{ + const int phandle = faa->faa_phandle; + bus_space_tag_t bst = faa->faa_bst; + bus_space_handle_t bsh; + bus_addr_t addr; + bus_size_t size; + tcflag_t flags; + int speed; + + speed = fdtbus_get_stdout_speed(); + if (speed < 0) + speed = 115200; /* default */ + flags = fdtbus_get_stdout_flags(); + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) + panic("exynos_uart: couldn't get registers"); + if (bus_space_map(bst, addr, size, 0, &bsh) != 0) + panic("exynos_uart: couldn't map registers"); + + exynos_uart_consaddr = addr; + + exynos_uart_cnattach(bst, bsh, speed, flags); +} + +static const struct fdt_console exynos_uart_console = { + .match = exynos_uart_console_match, + .consinit = exynos_uart_console_consinit, +}; + +FDT_CONSOLE(exynos_uart, &exynos_uart_console);