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);

Reply via email to