* [PATCH v9 0/2] Add Spreadtrum SoC bindings and serial driver support [not found] <sc9836-serial-v9> 2015-01-28 2:47 ` Chunyan Zhang @ 2015-01-28 2:47 ` Chunyan Zhang 0 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8, arnd-r2nGTMty4D4, gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io, peter-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8, pawel.moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg, galak-sgV2jX0FEOL9JmXXK+q4OQ, grant.likely-QSEj5FYQhm4dnm+yROfE0A, jslaby-AlSwsSmVLrQ, heiko-4mtYJXux2i+zQB+pC5nmwQ, jason-NLaQJdtUoK4Be96aLqz0jA, florian.vaussard-p8DiymsW2f8, andrew-g2DYL2Zd6BY, hytszk-Re5JQEeQqe8AvxtiuMwx3w, antonynpavlov-Re5JQEeQqe8AvxtiuMwx3w, shawn.guo-QSEj5FYQhm4dnm+yROfE0A, orsonzhai-Re5JQEeQqe8AvxtiuMwx3w, geng.ren-lxIno14LUO0EEoCn2XhGlw, zhizhou.zhang-lxIno14LUO0EEoCn2XhGlw, lanqing.liu-lxIno14LUO0EEoCn2XhGlw, zhang.lyra-Re5JQEeQqe8AvxtiuMwx3w, wei.qiao-lxIno14LUO0EEoCn2XhGlw, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-serial-u79uwXL29TY76Z2rM5mHXA, linux-api-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r Changes from v8: - Moved a few unuseful code lines. Chunyan Zhang (2): Documentation: DT: Add bindings for Spreadtrum SoC Platform tty/serial: Add Spreadtrum sc9836-uart driver support Documentation/devicetree/bindings/arm/sprd.txt | 11 + .../devicetree/bindings/serial/sprd-uart.txt | 7 + .../devicetree/bindings/vendor-prefixes.txt | 1 + drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 789 ++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 7 files changed, 830 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/sprd.txt create mode 100644 Documentation/devicetree/bindings/serial/sprd-uart.txt create mode 100644 drivers/tty/serial/sprd_serial.c -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v9 0/2] Add Spreadtrum SoC bindings and serial driver support @ 2015-01-28 2:47 ` Chunyan Zhang 0 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: gregkh Cc: robh+dt, mark.rutland, arnd, gnomes, peter, pawel.moll, ijc+devicetree, galak, grant.likely, jslaby, heiko, jason, florian.vaussard, andrew, hytszk, antonynpavlov, shawn.guo, orsonzhai, geng.ren, zhizhou.zhang, lanqing.liu, zhang.lyra, wei.qiao, devicetree, linux-kernel, linux-serial, linux-api, linux-arm-kernel Changes from v8: - Moved a few unuseful code lines. Chunyan Zhang (2): Documentation: DT: Add bindings for Spreadtrum SoC Platform tty/serial: Add Spreadtrum sc9836-uart driver support Documentation/devicetree/bindings/arm/sprd.txt | 11 + .../devicetree/bindings/serial/sprd-uart.txt | 7 + .../devicetree/bindings/vendor-prefixes.txt | 1 + drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 789 ++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 7 files changed, 830 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/sprd.txt create mode 100644 Documentation/devicetree/bindings/serial/sprd-uart.txt create mode 100644 drivers/tty/serial/sprd_serial.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v9 0/2] Add Spreadtrum SoC bindings and serial driver support @ 2015-01-28 2:47 ` Chunyan Zhang 0 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: linux-arm-kernel Changes from v8: - Moved a few unuseful code lines. Chunyan Zhang (2): Documentation: DT: Add bindings for Spreadtrum SoC Platform tty/serial: Add Spreadtrum sc9836-uart driver support Documentation/devicetree/bindings/arm/sprd.txt | 11 + .../devicetree/bindings/serial/sprd-uart.txt | 7 + .../devicetree/bindings/vendor-prefixes.txt | 1 + drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 789 ++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 7 files changed, 830 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/sprd.txt create mode 100644 Documentation/devicetree/bindings/serial/sprd-uart.txt create mode 100644 drivers/tty/serial/sprd_serial.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 24+ messages in thread
[parent not found: <1422413261-17184-1-git-send-email-chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org>]
* [PATCH v9 1/2] Documentation: DT: Add bindings for Spreadtrum SoC Platform 2015-01-28 2:47 ` Chunyan Zhang (?) @ 2015-01-28 2:47 ` Chunyan Zhang -1 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8, arnd-r2nGTMty4D4, gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io, peter-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8, pawel.moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg, galak-sgV2jX0FEOL9JmXXK+q4OQ, grant.likely-QSEj5FYQhm4dnm+yROfE0A, jslaby-AlSwsSmVLrQ, heiko-4mtYJXux2i+zQB+pC5nmwQ, jason-NLaQJdtUoK4Be96aLqz0jA, florian.vaussard-p8DiymsW2f8, andrew-g2DYL2Zd6BY, hytszk-Re5JQEeQqe8AvxtiuMwx3w, antonynpavlov-Re5JQEeQqe8AvxtiuMwx3w, shawn.guo-QSEj5FYQhm4dnm+yROfE0A, orsonzhai-Re5JQEeQqe8AvxtiuMwx3w, geng.ren-lxIno14LUO0EEoCn2XhGlw, zhizhou.zhang-lxIno14LUO0EEoCn2XhGlw, lanqing.liu-lxIno14LUO0EEoCn2XhGlw, zhang.lyra-Re5JQEeQqe8AvxtiuMwx3w, wei.qiao-lxIno14LUO0EEoCn2XhGlw, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-serial-u79uwXL29TY76Z2rM5mHXA, linux-api-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r Adds Spreadtrum's prefix "sprd" to vendor-prefixes file. Adds the devicetree binding documentations for Spreadtrum's sc9836-uart and SC9836 SoC based on the Sharkl64 Platform which is a 64-bit SoC Platform of Spreadtrum. Signed-off-by: Chunyan Zhang <chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org> --- Documentation/devicetree/bindings/arm/sprd.txt | 11 +++++++++++ .../devicetree/bindings/serial/sprd-uart.txt | 7 +++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + 3 files changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/sprd.txt create mode 100644 Documentation/devicetree/bindings/serial/sprd-uart.txt diff --git a/Documentation/devicetree/bindings/arm/sprd.txt b/Documentation/devicetree/bindings/arm/sprd.txt new file mode 100644 index 0000000..31a629d --- /dev/null +++ b/Documentation/devicetree/bindings/arm/sprd.txt @@ -0,0 +1,11 @@ +Spreadtrum SoC Platforms Device Tree Bindings +---------------------------------------------------- + +Sharkl64 is a Spreadtrum's SoC Platform which is based +on ARM 64-bit processor. + +SC9836 openphone board with SC9836 SoC based on the +Sharkl64 Platform shall have the following properties. + +Required root node properties: + - compatible = "sprd,sc9836-openphone", "sprd,sc9836"; diff --git a/Documentation/devicetree/bindings/serial/sprd-uart.txt b/Documentation/devicetree/bindings/serial/sprd-uart.txt new file mode 100644 index 0000000..2aff0f2 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/sprd-uart.txt @@ -0,0 +1,7 @@ +* Spreadtrum serial UART + +Required properties: +- compatible: must be "sprd,sc9836-uart" +- reg: offset and length of the register set for the device +- interrupts: exactly one interrupt specifier +- clocks: phandles to input clocks. diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index b1df0ad..0a8384f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -153,6 +153,7 @@ snps Synopsys, Inc. solidrun SolidRun sony Sony Corporation spansion Spansion Inc. +sprd Spreadtrum Communications Inc. st STMicroelectronics ste ST-Ericsson stericsson ST-Ericsson -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v9 1/2] Documentation: DT: Add bindings for Spreadtrum SoC Platform @ 2015-01-28 2:47 ` Chunyan Zhang 0 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: gregkh Cc: robh+dt, mark.rutland, arnd, gnomes, peter, pawel.moll, ijc+devicetree, galak, grant.likely, jslaby, heiko, jason, florian.vaussard, andrew, hytszk, antonynpavlov, shawn.guo, orsonzhai, geng.ren, zhizhou.zhang, lanqing.liu, zhang.lyra, wei.qiao, devicetree, linux-kernel, linux-serial, linux-api, linux-arm-kernel Adds Spreadtrum's prefix "sprd" to vendor-prefixes file. Adds the devicetree binding documentations for Spreadtrum's sc9836-uart and SC9836 SoC based on the Sharkl64 Platform which is a 64-bit SoC Platform of Spreadtrum. Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com> --- Documentation/devicetree/bindings/arm/sprd.txt | 11 +++++++++++ .../devicetree/bindings/serial/sprd-uart.txt | 7 +++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + 3 files changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/sprd.txt create mode 100644 Documentation/devicetree/bindings/serial/sprd-uart.txt diff --git a/Documentation/devicetree/bindings/arm/sprd.txt b/Documentation/devicetree/bindings/arm/sprd.txt new file mode 100644 index 0000000..31a629d --- /dev/null +++ b/Documentation/devicetree/bindings/arm/sprd.txt @@ -0,0 +1,11 @@ +Spreadtrum SoC Platforms Device Tree Bindings +---------------------------------------------------- + +Sharkl64 is a Spreadtrum's SoC Platform which is based +on ARM 64-bit processor. + +SC9836 openphone board with SC9836 SoC based on the +Sharkl64 Platform shall have the following properties. + +Required root node properties: + - compatible = "sprd,sc9836-openphone", "sprd,sc9836"; diff --git a/Documentation/devicetree/bindings/serial/sprd-uart.txt b/Documentation/devicetree/bindings/serial/sprd-uart.txt new file mode 100644 index 0000000..2aff0f2 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/sprd-uart.txt @@ -0,0 +1,7 @@ +* Spreadtrum serial UART + +Required properties: +- compatible: must be "sprd,sc9836-uart" +- reg: offset and length of the register set for the device +- interrupts: exactly one interrupt specifier +- clocks: phandles to input clocks. diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index b1df0ad..0a8384f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -153,6 +153,7 @@ snps Synopsys, Inc. solidrun SolidRun sony Sony Corporation spansion Spansion Inc. +sprd Spreadtrum Communications Inc. st STMicroelectronics ste ST-Ericsson stericsson ST-Ericsson -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v9 1/2] Documentation: DT: Add bindings for Spreadtrum SoC Platform @ 2015-01-28 2:47 ` Chunyan Zhang 0 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: linux-arm-kernel Adds Spreadtrum's prefix "sprd" to vendor-prefixes file. Adds the devicetree binding documentations for Spreadtrum's sc9836-uart and SC9836 SoC based on the Sharkl64 Platform which is a 64-bit SoC Platform of Spreadtrum. Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com> --- Documentation/devicetree/bindings/arm/sprd.txt | 11 +++++++++++ .../devicetree/bindings/serial/sprd-uart.txt | 7 +++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + 3 files changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/sprd.txt create mode 100644 Documentation/devicetree/bindings/serial/sprd-uart.txt diff --git a/Documentation/devicetree/bindings/arm/sprd.txt b/Documentation/devicetree/bindings/arm/sprd.txt new file mode 100644 index 0000000..31a629d --- /dev/null +++ b/Documentation/devicetree/bindings/arm/sprd.txt @@ -0,0 +1,11 @@ +Spreadtrum SoC Platforms Device Tree Bindings +---------------------------------------------------- + +Sharkl64 is a Spreadtrum's SoC Platform which is based +on ARM 64-bit processor. + +SC9836 openphone board with SC9836 SoC based on the +Sharkl64 Platform shall have the following properties. + +Required root node properties: + - compatible = "sprd,sc9836-openphone", "sprd,sc9836"; diff --git a/Documentation/devicetree/bindings/serial/sprd-uart.txt b/Documentation/devicetree/bindings/serial/sprd-uart.txt new file mode 100644 index 0000000..2aff0f2 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/sprd-uart.txt @@ -0,0 +1,7 @@ +* Spreadtrum serial UART + +Required properties: +- compatible: must be "sprd,sc9836-uart" +- reg: offset and length of the register set for the device +- interrupts: exactly one interrupt specifier +- clocks: phandles to input clocks. diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index b1df0ad..0a8384f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -153,6 +153,7 @@ snps Synopsys, Inc. solidrun SolidRun sony Sony Corporation spansion Spansion Inc. +sprd Spreadtrum Communications Inc. st STMicroelectronics ste ST-Ericsson stericsson ST-Ericsson -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support 2015-01-28 2:47 ` Chunyan Zhang (?) @ 2015-01-28 2:47 ` Chunyan Zhang -1 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8, arnd-r2nGTMty4D4, gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io, peter-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8, pawel.moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg, galak-sgV2jX0FEOL9JmXXK+q4OQ, grant.likely-QSEj5FYQhm4dnm+yROfE0A, jslaby-AlSwsSmVLrQ, heiko-4mtYJXux2i+zQB+pC5nmwQ, jason-NLaQJdtUoK4Be96aLqz0jA, florian.vaussard-p8DiymsW2f8, andrew-g2DYL2Zd6BY, hytszk-Re5JQEeQqe8AvxtiuMwx3w, antonynpavlov-Re5JQEeQqe8AvxtiuMwx3w, shawn.guo-QSEj5FYQhm4dnm+yROfE0A, orsonzhai-Re5JQEeQqe8AvxtiuMwx3w, geng.ren-lxIno14LUO0EEoCn2XhGlw, zhizhou.zhang-lxIno14LUO0EEoCn2XhGlw, lanqing.liu-lxIno14LUO0EEoCn2XhGlw, zhang.lyra-Re5JQEeQqe8AvxtiuMwx3w, wei.qiao-lxIno14LUO0EEoCn2XhGlw, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-serial-u79uwXL29TY76Z2rM5mHXA, linux-api-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r Add a full sc9836-uart driver for SC9836 SoC which is based on the spreadtrum sharkl64 platform. This driver also support earlycon. Originally-by: Lanqing Liu <lanqing.liu-lxIno14LUO0EEoCn2XhGlw@public.gmane.org> Signed-off-by: Orson Zhai <orson.zhai-lxIno14LUO0EEoCn2XhGlw@public.gmane.org> Signed-off-by: Chunyan Zhang <chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org> Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> --- drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 789 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 811 insertions(+) create mode 100644 drivers/tty/serial/sprd_serial.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index c79b43c..13211f7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1577,6 +1577,24 @@ config SERIAL_MEN_Z135 This driver can also be build as a module. If so, the module will be called men_z135_uart.ko +config SERIAL_SPRD + tristate "Support for Spreadtrum serial" + depends on ARCH_SPRD + select SERIAL_CORE + help + This enables the driver for the Spreadtrum's serial. + +config SERIAL_SPRD_CONSOLE + bool "Spreadtrum UART console support" + depends on SERIAL_SPRD=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Support for early debug console using Spreadtrum's serial. This enables + the console before standard serial driver is probed. This is enabled + with "earlycon" on the kernel command line. The console is + enabled when early_param is processed. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 9a548ac..4801aca 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_ARC) += arc_uart.o obj-$(CONFIG_SERIAL_RP2) += rp2.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o +obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c new file mode 100644 index 0000000..a08a57f --- /dev/null +++ b/drivers/tty/serial/sprd_serial.c @@ -0,0 +1,789 @@ +/* + * Copyright (C) 2012-2015 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +/* device name */ +#define UART_NR_MAX 8 +#define SPRD_TTY_NAME "ttyS" +#define SPRD_FIFO_SIZE 128 +#define SPRD_DEF_RATE 26000000 +#define SPRD_BAUD_IO_LIMIT 3000000 +#define SPRD_TIMEOUT 256 + +/* the offset of serial registers and BITs for them */ +/* data registers */ +#define SPRD_TXD 0x0000 +#define SPRD_RXD 0x0004 + +/* line status register and its BITs */ +#define SPRD_LSR 0x0008 +#define SPRD_LSR_OE BIT(4) +#define SPRD_LSR_FE BIT(3) +#define SPRD_LSR_PE BIT(2) +#define SPRD_LSR_BI BIT(7) +#define SPRD_LSR_TX_OVER BIT(15) + +/* data number in TX and RX fifo */ +#define SPRD_STS1 0x000C + +/* interrupt enable register and its BITs */ +#define SPRD_IEN 0x0010 +#define SPRD_IEN_RX_FULL BIT(0) +#define SPRD_IEN_TX_EMPTY BIT(1) +#define SPRD_IEN_BREAK_DETECT BIT(7) +#define SPRD_IEN_TIMEOUT BIT(13) + +/* interrupt clear register */ +#define SPRD_ICLR 0x0014 + +/* line control register */ +#define SPRD_LCR 0x0018 +#define SPRD_LCR_STOP_1BIT 0x10 +#define SPRD_LCR_STOP_2BIT 0x30 +#define SPRD_LCR_DATA_LEN (BIT(2) | BIT(3)) +#define SPRD_LCR_DATA_LEN5 0x0 +#define SPRD_LCR_DATA_LEN6 0x4 +#define SPRD_LCR_DATA_LEN7 0x8 +#define SPRD_LCR_DATA_LEN8 0xc +#define SPRD_LCR_PARITY (BIT(0) | BIT(1)) +#define SPRD_LCR_PARITY_EN 0x2 +#define SPRD_LCR_EVEN_PAR 0x0 +#define SPRD_LCR_ODD_PAR 0x1 + +/* control register 1 */ +#define SPRD_CTL1 0x001C +#define RX_HW_FLOW_CTL_THLD BIT(6) +#define RX_HW_FLOW_CTL_EN BIT(7) +#define TX_HW_FLOW_CTL_EN BIT(8) +#define RX_TOUT_THLD_DEF 0x3E00 +#define RX_HFC_THLD_DEF 0x40 + +/* fifo threshold register */ +#define SPRD_CTL2 0x0020 +#define THLD_TX_EMPTY 0x40 +#define THLD_RX_FULL 0x40 + +/* config baud rate register */ +#define SPRD_CLKD0 0x0024 +#define SPRD_CLKD1 0x0028 + +/* interrupt mask status register */ +#define SPRD_IMSR 0x002C +#define SPRD_IMSR_RX_FIFO_FULL BIT(0) +#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) +#define SPRD_IMSR_BREAK_DETECT BIT(7) +#define SPRD_IMSR_TIMEOUT BIT(13) + +struct reg_backup { + u32 ien; + u32 ctrl0; + u32 ctrl1; + u32 ctrl2; + u32 clkd0; + u32 clkd1; + u32 dspwait; +}; + +struct sprd_uart_port { + struct uart_port port; + struct reg_backup reg_bak; + char name[16]; +}; + +static struct sprd_uart_port *sprd_port[UART_NR_MAX]; +static int sprd_ports_num; + +static inline unsigned int serial_in(struct uart_port *port, int offset) +{ + return readl_relaxed(port->membase + offset); +} + +static inline void serial_out(struct uart_port *port, int offset, int value) +{ + writel_relaxed(value, port->membase + offset); +} + +static unsigned int sprd_tx_empty(struct uart_port *port) +{ + if (serial_in(port, SPRD_STS1) & 0xff00) + return 0; + else + return TIOCSER_TEMT; +} + +static unsigned int sprd_get_mctrl(struct uart_port *port) +{ + return TIOCM_DSR | TIOCM_CTS; +} + +static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* nothing to do */ +} + +static void sprd_stop_tx(struct uart_port *port) +{ + unsigned int ien, iclr; + + iclr = serial_in(port, SPRD_ICLR); + ien = serial_in(port, SPRD_IEN); + + iclr |= SPRD_IEN_TX_EMPTY; + ien &= ~SPRD_IEN_TX_EMPTY; + + serial_out(port, SPRD_ICLR, iclr); + serial_out(port, SPRD_IEN, ien); +} + +static void sprd_start_tx(struct uart_port *port) +{ + unsigned int ien; + + ien = serial_in(port, SPRD_IEN); + if (!(ien & SPRD_IEN_TX_EMPTY)) { + ien |= SPRD_IEN_TX_EMPTY; + serial_out(port, SPRD_IEN, ien); + } +} + +static void sprd_stop_rx(struct uart_port *port) +{ + unsigned int ien, iclr; + + iclr = serial_in(port, SPRD_ICLR); + ien = serial_in(port, SPRD_IEN); + + ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT); + iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT; + + serial_out(port, SPRD_IEN, ien); + serial_out(port, SPRD_ICLR, iclr); +} + +/* The Sprd serial does not support this function. */ +static void sprd_break_ctl(struct uart_port *port, int break_state) +{ + /* nothing to do */ +} + +static int handle_lsr_errors(struct uart_port *port, + unsigned int *flag, + unsigned int *lsr) +{ + int ret = 0; + + /* statistics */ + if (*lsr & SPRD_LSR_BI) { + *lsr &= ~(SPRD_LSR_FE | SPRD_LSR_PE); + port->icount.brk++; + ret = uart_handle_break(port); + if (ret) + return ret; + } else if (*lsr & SPRD_LSR_PE) + port->icount.parity++; + else if (*lsr & SPRD_LSR_FE) + port->icount.frame++; + if (*lsr & SPRD_LSR_OE) + port->icount.overrun++; + + /* mask off conditions which should be ignored */ + *lsr &= port->read_status_mask; + if (*lsr & SPRD_LSR_BI) + *flag = TTY_BREAK; + else if (*lsr & SPRD_LSR_PE) + *flag = TTY_PARITY; + else if (*lsr & SPRD_LSR_FE) + *flag = TTY_FRAME; + + return ret; +} + +static inline void sprd_rx(struct uart_port *port) +{ + struct tty_port *tty = &port->state->port; + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; + + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { + lsr = serial_in(port, SPRD_LSR); + ch = serial_in(port, SPRD_RXD); + flag = TTY_NORMAL; + port->icount.rx++; + + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | + SPRD_LSR_FE | SPRD_LSR_OE)) + if (handle_lsr_errors(port, &lsr, &flag)) + continue; + if (uart_handle_sysrq_char(port, ch)) + continue; + + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); + } + + tty_flip_buffer_push(tty); +} + +static inline void sprd_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int count; + + if (port->x_char) { + serial_out(port, SPRD_TXD, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sprd_stop_tx(port); + return; + } + + count = THLD_TX_EMPTY; + do { + serial_out(port, SPRD_TXD, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + sprd_stop_tx(port); +} + +/* this handles the interrupt from one port */ +static irqreturn_t sprd_handle_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int ims; + + spin_lock(&port->lock); + + ims = serial_in(port, SPRD_IMSR); + + if (!ims) + return IRQ_NONE; + + serial_out(port, SPRD_ICLR, ~0); + + if (ims & (SPRD_IMSR_RX_FIFO_FULL | + SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT)) + sprd_rx(port); + + if (ims & SPRD_IMSR_TX_FIFO_EMPTY) + sprd_tx(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static int sprd_startup(struct uart_port *port) +{ + int ret = 0; + unsigned int ien, fc; + unsigned int timeout; + struct sprd_uart_port *sp; + unsigned long flags; + + serial_out(port, SPRD_CTL2, ((THLD_TX_EMPTY << 8) | THLD_RX_FULL)); + + /* clear rx fifo */ + timeout = SPRD_TIMEOUT; + while (timeout-- && serial_in(port, SPRD_STS1) & 0x00ff) + serial_in(port, SPRD_RXD); + + /* clear tx fifo */ + timeout = SPRD_TIMEOUT; + while (timeout-- && serial_in(port, SPRD_STS1) & 0xff00) + cpu_relax(); + + /* clear interrupt */ + serial_out(port, SPRD_IEN, 0); + serial_out(port, SPRD_ICLR, ~0); + + /* allocate irq */ + sp = container_of(port, struct sprd_uart_port, port); + snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line); + ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq, + IRQF_SHARED, sp->name, port); + if (ret) { + dev_err(port->dev, "fail to request serial irq %d, ret=%d\n", + port->irq, ret); + return ret; + } + fc = serial_in(port, SPRD_CTL1); + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + + /* enable interrupt */ + spin_lock_irqsave(&port->lock, flags); + ien = serial_in(port, SPRD_IEN); + ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + serial_out(port, SPRD_IEN, ien); + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void sprd_shutdown(struct uart_port *port) +{ + serial_out(port, SPRD_IEN, 0); + serial_out(port, SPRD_ICLR, ~0); + devm_free_irq(port->dev, port->irq, port); +} + +static void sprd_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud, quot; + unsigned int lcr = 0, fc; + unsigned long flags; + + /* ask the core to calculate the divisor for us */ + baud = uart_get_baud_rate(port, termios, old, 0, SPRD_BAUD_IO_LIMIT); + + quot = (unsigned int)((port->uartclk + baud / 2) / baud); + + /* set data length */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= SPRD_LCR_DATA_LEN5; + break; + case CS6: + lcr |= SPRD_LCR_DATA_LEN6; + break; + case CS7: + lcr |= SPRD_LCR_DATA_LEN7; + break; + case CS8: + default: + lcr |= SPRD_LCR_DATA_LEN8; + break; + } + + /* calculate stop bits */ + lcr &= ~(SPRD_LCR_STOP_1BIT | SPRD_LCR_STOP_2BIT); + if (termios->c_cflag & CSTOPB) + lcr |= SPRD_LCR_STOP_2BIT; + else + lcr |= SPRD_LCR_STOP_1BIT; + + /* calculate parity */ + lcr &= ~SPRD_LCR_PARITY; + termios->c_cflag &= ~CMSPAR; /* no support mark/space */ + if (termios->c_cflag & PARENB) { + lcr |= SPRD_LCR_PARITY_EN; + if (termios->c_cflag & PARODD) + lcr |= SPRD_LCR_ODD_PAR; + else + lcr |= SPRD_LCR_EVEN_PAR; + } + + spin_lock_irqsave(&port->lock, flags); + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = SPRD_LSR_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SPRD_LSR_FE | SPRD_LSR_PE; + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= SPRD_LSR_BI; + + /* characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SPRD_LSR_PE | SPRD_LSR_FE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= SPRD_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SPRD_LSR_OE; + } + + /* flow control */ + fc = serial_in(port, SPRD_CTL1); + fc &= ~(RX_HW_FLOW_CTL_THLD | RX_HW_FLOW_CTL_EN | TX_HW_FLOW_CTL_EN); + if (termios->c_cflag & CRTSCTS) { + fc |= RX_HW_FLOW_CTL_THLD; + fc |= RX_HW_FLOW_CTL_EN; + fc |= TX_HW_FLOW_CTL_EN; + } + + /* clock divider bit0~bit15 */ + serial_out(port, SPRD_CLKD0, quot & 0xffff); + + /* clock divider bit16~bit20 */ + serial_out(port, SPRD_CLKD1, (quot & 0x1f0000) >> 16); + serial_out(port, SPRD_LCR, lcr); + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static const char *sprd_type(struct uart_port *port) +{ + return "SPX"; +} + +static void sprd_release_port(struct uart_port *port) +{ + /* nothing to do */ +} + +static int sprd_request_port(struct uart_port *port) +{ + return 0; +} + +static void sprd_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SPRD; +} + +static int sprd_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_SPRD) + return -EINVAL; + if (port->irq != ser->irq) + return -EINVAL; + return 0; +} + +static struct uart_ops serial_sprd_ops = { + .tx_empty = sprd_tx_empty, + .get_mctrl = sprd_get_mctrl, + .set_mctrl = sprd_set_mctrl, + .stop_tx = sprd_stop_tx, + .start_tx = sprd_start_tx, + .stop_rx = sprd_stop_rx, + .break_ctl = sprd_break_ctl, + .startup = sprd_startup, + .shutdown = sprd_shutdown, + .set_termios = sprd_set_termios, + .type = sprd_type, + .release_port = sprd_release_port, + .request_port = sprd_request_port, + .config_port = sprd_config_port, + .verify_port = sprd_verify_port, +}; + +#ifdef CONFIG_SERIAL_SPRD_CONSOLE +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int status, tmout = 10000; + + /* wait up to 10ms for the character(s) to be sent */ + do { + status = serial_in(port, SPRD_STS1); + if (--tmout == 0) + break; + udelay(1); + } while (status & 0xff00); +} + +static void sprd_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + serial_out(port, SPRD_TXD, ch); +} + +static void sprd_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &sprd_port[co->index]->port; + int locked = 1; + unsigned long flags; + + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); + + uart_console_write(port, s, count, sprd_console_putchar); + + /* wait for transmitter to become empty */ + wait_for_xmitr(port); + + if (locked) + spin_unlock_irqrestore(&port->lock, flags); +} + +static int __init sprd_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= UART_NR_MAX || co->index < 0) + co->index = 0; + + port = &sprd_port[co->index]->port; + if (port == NULL) { + pr_info("serial port %d not yet initialized\n", co->index); + return -ENODEV; + } + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver sprd_uart_driver; +static struct console sprd_console = { + .name = SPRD_TTY_NAME, + .write = sprd_console_write, + .device = uart_console_device, + .setup = sprd_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sprd_uart_driver, +}; + +#define SPRD_CONSOLE (&sprd_console) + +/* Support for earlycon */ +static void sprd_putc(struct uart_port *port, int c) +{ + unsigned int timeout = SPRD_TIMEOUT; + + while (timeout-- && + !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER)) + cpu_relax(); + + writeb(c, port->membase + SPRD_TXD); +} + +static void sprd_early_write(struct console *con, const char *s, + unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, sprd_putc); +} + +static int __init sprd_early_console_setup( + struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = sprd_early_write; + return 0; +} + +EARLYCON_DECLARE(sprd_serial, sprd_early_console_setup); +OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart", + sprd_early_console_setup); + +#else /* !CONFIG_SERIAL_SPRD_CONSOLE */ +#define SPRD_CONSOLE NULL +#endif + +static struct uart_driver sprd_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "sprd_serial", + .dev_name = SPRD_TTY_NAME, + .major = 0, + .minor = 0, + .nr = UART_NR_MAX, + .cons = SPRD_CONSOLE, +}; + +static int sprd_probe_dt_alias(int index, struct device *dev) +{ + struct device_node *np; + int ret = index; + + if (!IS_ENABLED(CONFIG_OF)) + return ret; + + np = dev->of_node; + if (!np) + return ret; + + ret = of_alias_get_id(np, "serial"); + if (IS_ERR_VALUE(ret)) + ret = index; + else if (ret >= ARRAY_SIZE(sprd_port) || sprd_port[ret] != NULL) { + dev_warn(dev, "requested serial port %d not available.\n", ret); + ret = index; + } + + return ret; +} + +static int sprd_remove(struct platform_device *dev) +{ + struct sprd_uart_port *sup = platform_get_drvdata(dev); + + if (sup) { + uart_remove_one_port(&sprd_uart_driver, &sup->port); + sprd_port[sup->port.line] = NULL; + sprd_ports_num--; + } + + if (!sprd_ports_num) + uart_unregister_driver(&sprd_uart_driver); + + return 0; +} + +static int sprd_probe(struct platform_device *pdev) +{ + struct resource *res; + struct uart_port *up; + struct clk *clk; + int irq; + int index; + int ret; + + for (index = 0; index < ARRAY_SIZE(sprd_port); index++) + if (sprd_port[index] == NULL) + break; + + if (index == ARRAY_SIZE(sprd_port)) + return -EBUSY; + + index = sprd_probe_dt_alias(index, &pdev->dev); + + sprd_port[index] = devm_kzalloc(&pdev->dev, + sizeof(*sprd_port[index]), GFP_KERNEL); + if (!sprd_port[index]) + return -ENOMEM; + + up = &sprd_port[index]->port; + up->dev = &pdev->dev; + up->line = index; + up->type = PORT_SPRD; + up->iotype = SERIAL_IO_PORT; + up->uartclk = SPRD_DEF_RATE; + up->fifosize = SPRD_FIFO_SIZE; + up->ops = &serial_sprd_ops; + up->flags = UPF_BOOT_AUTOCONF; + + clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) + up->uartclk = clk_get_rate(clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "not provide mem resource\n"); + return -ENODEV; + } + up->mapbase = res->start; + up->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(up->membase)) + return PTR_ERR(up->membase); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "not provide irq resource\n"); + return -ENODEV; + } + up->irq = irq; + + if (!sprd_ports_num) { + ret = uart_register_driver(&sprd_uart_driver); + if (ret < 0) { + pr_err("Failed to register SPRD-UART driver\n"); + return ret; + } + } + sprd_ports_num++; + + ret = uart_add_one_port(&sprd_uart_driver, up); + if (ret) { + sprd_port[index] = NULL; + sprd_remove(pdev); + } + + platform_set_drvdata(pdev, up); + + return ret; +} + +static int sprd_suspend(struct device *dev) +{ + struct sprd_uart_port *sup = dev_get_drvdata(dev); + + uart_suspend_port(&sprd_uart_driver, &sup->port); + + return 0; +} + +static int sprd_resume(struct device *dev) +{ + struct sprd_uart_port *sup = dev_get_drvdata(dev); + + uart_resume_port(&sprd_uart_driver, &sup->port); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume); + +static const struct of_device_id serial_ids[] = { + {.compatible = "sprd,sc9836-uart",}, + {} +}; + +static struct platform_driver sprd_platform_driver = { + .probe = sprd_probe, + .remove = sprd_remove, + .driver = { + .name = "sprd_serial", + .of_match_table = of_match_ptr(serial_ids), + .pm = &sprd_pm_ops, + }, +}; + +module_platform_driver(sprd_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Spreadtrum SoC serial driver series"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index c172180..7e6eb39 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -248,4 +248,7 @@ /* MESON */ #define PORT_MESON 109 +/* SPRD SERIAL */ +#define PORT_SPRD 110 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 2:47 ` Chunyan Zhang 0 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: gregkh Cc: robh+dt, mark.rutland, arnd, gnomes, peter, pawel.moll, ijc+devicetree, galak, grant.likely, jslaby, heiko, jason, florian.vaussard, andrew, hytszk, antonynpavlov, shawn.guo, orsonzhai, geng.ren, zhizhou.zhang, lanqing.liu, zhang.lyra, wei.qiao, devicetree, linux-kernel, linux-serial, linux-api, linux-arm-kernel Add a full sc9836-uart driver for SC9836 SoC which is based on the spreadtrum sharkl64 platform. This driver also support earlycon. Originally-by: Lanqing Liu <lanqing.liu@spreadtrum.com> Signed-off-by: Orson Zhai <orson.zhai@spreadtrum.com> Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com> Acked-by: Arnd Bergmann <arnd@arndb.de> --- drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 789 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 811 insertions(+) create mode 100644 drivers/tty/serial/sprd_serial.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index c79b43c..13211f7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1577,6 +1577,24 @@ config SERIAL_MEN_Z135 This driver can also be build as a module. If so, the module will be called men_z135_uart.ko +config SERIAL_SPRD + tristate "Support for Spreadtrum serial" + depends on ARCH_SPRD + select SERIAL_CORE + help + This enables the driver for the Spreadtrum's serial. + +config SERIAL_SPRD_CONSOLE + bool "Spreadtrum UART console support" + depends on SERIAL_SPRD=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Support for early debug console using Spreadtrum's serial. This enables + the console before standard serial driver is probed. This is enabled + with "earlycon" on the kernel command line. The console is + enabled when early_param is processed. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 9a548ac..4801aca 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_ARC) += arc_uart.o obj-$(CONFIG_SERIAL_RP2) += rp2.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o +obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c new file mode 100644 index 0000000..a08a57f --- /dev/null +++ b/drivers/tty/serial/sprd_serial.c @@ -0,0 +1,789 @@ +/* + * Copyright (C) 2012-2015 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +/* device name */ +#define UART_NR_MAX 8 +#define SPRD_TTY_NAME "ttyS" +#define SPRD_FIFO_SIZE 128 +#define SPRD_DEF_RATE 26000000 +#define SPRD_BAUD_IO_LIMIT 3000000 +#define SPRD_TIMEOUT 256 + +/* the offset of serial registers and BITs for them */ +/* data registers */ +#define SPRD_TXD 0x0000 +#define SPRD_RXD 0x0004 + +/* line status register and its BITs */ +#define SPRD_LSR 0x0008 +#define SPRD_LSR_OE BIT(4) +#define SPRD_LSR_FE BIT(3) +#define SPRD_LSR_PE BIT(2) +#define SPRD_LSR_BI BIT(7) +#define SPRD_LSR_TX_OVER BIT(15) + +/* data number in TX and RX fifo */ +#define SPRD_STS1 0x000C + +/* interrupt enable register and its BITs */ +#define SPRD_IEN 0x0010 +#define SPRD_IEN_RX_FULL BIT(0) +#define SPRD_IEN_TX_EMPTY BIT(1) +#define SPRD_IEN_BREAK_DETECT BIT(7) +#define SPRD_IEN_TIMEOUT BIT(13) + +/* interrupt clear register */ +#define SPRD_ICLR 0x0014 + +/* line control register */ +#define SPRD_LCR 0x0018 +#define SPRD_LCR_STOP_1BIT 0x10 +#define SPRD_LCR_STOP_2BIT 0x30 +#define SPRD_LCR_DATA_LEN (BIT(2) | BIT(3)) +#define SPRD_LCR_DATA_LEN5 0x0 +#define SPRD_LCR_DATA_LEN6 0x4 +#define SPRD_LCR_DATA_LEN7 0x8 +#define SPRD_LCR_DATA_LEN8 0xc +#define SPRD_LCR_PARITY (BIT(0) | BIT(1)) +#define SPRD_LCR_PARITY_EN 0x2 +#define SPRD_LCR_EVEN_PAR 0x0 +#define SPRD_LCR_ODD_PAR 0x1 + +/* control register 1 */ +#define SPRD_CTL1 0x001C +#define RX_HW_FLOW_CTL_THLD BIT(6) +#define RX_HW_FLOW_CTL_EN BIT(7) +#define TX_HW_FLOW_CTL_EN BIT(8) +#define RX_TOUT_THLD_DEF 0x3E00 +#define RX_HFC_THLD_DEF 0x40 + +/* fifo threshold register */ +#define SPRD_CTL2 0x0020 +#define THLD_TX_EMPTY 0x40 +#define THLD_RX_FULL 0x40 + +/* config baud rate register */ +#define SPRD_CLKD0 0x0024 +#define SPRD_CLKD1 0x0028 + +/* interrupt mask status register */ +#define SPRD_IMSR 0x002C +#define SPRD_IMSR_RX_FIFO_FULL BIT(0) +#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) +#define SPRD_IMSR_BREAK_DETECT BIT(7) +#define SPRD_IMSR_TIMEOUT BIT(13) + +struct reg_backup { + u32 ien; + u32 ctrl0; + u32 ctrl1; + u32 ctrl2; + u32 clkd0; + u32 clkd1; + u32 dspwait; +}; + +struct sprd_uart_port { + struct uart_port port; + struct reg_backup reg_bak; + char name[16]; +}; + +static struct sprd_uart_port *sprd_port[UART_NR_MAX]; +static int sprd_ports_num; + +static inline unsigned int serial_in(struct uart_port *port, int offset) +{ + return readl_relaxed(port->membase + offset); +} + +static inline void serial_out(struct uart_port *port, int offset, int value) +{ + writel_relaxed(value, port->membase + offset); +} + +static unsigned int sprd_tx_empty(struct uart_port *port) +{ + if (serial_in(port, SPRD_STS1) & 0xff00) + return 0; + else + return TIOCSER_TEMT; +} + +static unsigned int sprd_get_mctrl(struct uart_port *port) +{ + return TIOCM_DSR | TIOCM_CTS; +} + +static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* nothing to do */ +} + +static void sprd_stop_tx(struct uart_port *port) +{ + unsigned int ien, iclr; + + iclr = serial_in(port, SPRD_ICLR); + ien = serial_in(port, SPRD_IEN); + + iclr |= SPRD_IEN_TX_EMPTY; + ien &= ~SPRD_IEN_TX_EMPTY; + + serial_out(port, SPRD_ICLR, iclr); + serial_out(port, SPRD_IEN, ien); +} + +static void sprd_start_tx(struct uart_port *port) +{ + unsigned int ien; + + ien = serial_in(port, SPRD_IEN); + if (!(ien & SPRD_IEN_TX_EMPTY)) { + ien |= SPRD_IEN_TX_EMPTY; + serial_out(port, SPRD_IEN, ien); + } +} + +static void sprd_stop_rx(struct uart_port *port) +{ + unsigned int ien, iclr; + + iclr = serial_in(port, SPRD_ICLR); + ien = serial_in(port, SPRD_IEN); + + ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT); + iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT; + + serial_out(port, SPRD_IEN, ien); + serial_out(port, SPRD_ICLR, iclr); +} + +/* The Sprd serial does not support this function. */ +static void sprd_break_ctl(struct uart_port *port, int break_state) +{ + /* nothing to do */ +} + +static int handle_lsr_errors(struct uart_port *port, + unsigned int *flag, + unsigned int *lsr) +{ + int ret = 0; + + /* statistics */ + if (*lsr & SPRD_LSR_BI) { + *lsr &= ~(SPRD_LSR_FE | SPRD_LSR_PE); + port->icount.brk++; + ret = uart_handle_break(port); + if (ret) + return ret; + } else if (*lsr & SPRD_LSR_PE) + port->icount.parity++; + else if (*lsr & SPRD_LSR_FE) + port->icount.frame++; + if (*lsr & SPRD_LSR_OE) + port->icount.overrun++; + + /* mask off conditions which should be ignored */ + *lsr &= port->read_status_mask; + if (*lsr & SPRD_LSR_BI) + *flag = TTY_BREAK; + else if (*lsr & SPRD_LSR_PE) + *flag = TTY_PARITY; + else if (*lsr & SPRD_LSR_FE) + *flag = TTY_FRAME; + + return ret; +} + +static inline void sprd_rx(struct uart_port *port) +{ + struct tty_port *tty = &port->state->port; + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; + + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { + lsr = serial_in(port, SPRD_LSR); + ch = serial_in(port, SPRD_RXD); + flag = TTY_NORMAL; + port->icount.rx++; + + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | + SPRD_LSR_FE | SPRD_LSR_OE)) + if (handle_lsr_errors(port, &lsr, &flag)) + continue; + if (uart_handle_sysrq_char(port, ch)) + continue; + + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); + } + + tty_flip_buffer_push(tty); +} + +static inline void sprd_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int count; + + if (port->x_char) { + serial_out(port, SPRD_TXD, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sprd_stop_tx(port); + return; + } + + count = THLD_TX_EMPTY; + do { + serial_out(port, SPRD_TXD, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + sprd_stop_tx(port); +} + +/* this handles the interrupt from one port */ +static irqreturn_t sprd_handle_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int ims; + + spin_lock(&port->lock); + + ims = serial_in(port, SPRD_IMSR); + + if (!ims) + return IRQ_NONE; + + serial_out(port, SPRD_ICLR, ~0); + + if (ims & (SPRD_IMSR_RX_FIFO_FULL | + SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT)) + sprd_rx(port); + + if (ims & SPRD_IMSR_TX_FIFO_EMPTY) + sprd_tx(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static int sprd_startup(struct uart_port *port) +{ + int ret = 0; + unsigned int ien, fc; + unsigned int timeout; + struct sprd_uart_port *sp; + unsigned long flags; + + serial_out(port, SPRD_CTL2, ((THLD_TX_EMPTY << 8) | THLD_RX_FULL)); + + /* clear rx fifo */ + timeout = SPRD_TIMEOUT; + while (timeout-- && serial_in(port, SPRD_STS1) & 0x00ff) + serial_in(port, SPRD_RXD); + + /* clear tx fifo */ + timeout = SPRD_TIMEOUT; + while (timeout-- && serial_in(port, SPRD_STS1) & 0xff00) + cpu_relax(); + + /* clear interrupt */ + serial_out(port, SPRD_IEN, 0); + serial_out(port, SPRD_ICLR, ~0); + + /* allocate irq */ + sp = container_of(port, struct sprd_uart_port, port); + snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line); + ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq, + IRQF_SHARED, sp->name, port); + if (ret) { + dev_err(port->dev, "fail to request serial irq %d, ret=%d\n", + port->irq, ret); + return ret; + } + fc = serial_in(port, SPRD_CTL1); + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + + /* enable interrupt */ + spin_lock_irqsave(&port->lock, flags); + ien = serial_in(port, SPRD_IEN); + ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + serial_out(port, SPRD_IEN, ien); + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void sprd_shutdown(struct uart_port *port) +{ + serial_out(port, SPRD_IEN, 0); + serial_out(port, SPRD_ICLR, ~0); + devm_free_irq(port->dev, port->irq, port); +} + +static void sprd_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud, quot; + unsigned int lcr = 0, fc; + unsigned long flags; + + /* ask the core to calculate the divisor for us */ + baud = uart_get_baud_rate(port, termios, old, 0, SPRD_BAUD_IO_LIMIT); + + quot = (unsigned int)((port->uartclk + baud / 2) / baud); + + /* set data length */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= SPRD_LCR_DATA_LEN5; + break; + case CS6: + lcr |= SPRD_LCR_DATA_LEN6; + break; + case CS7: + lcr |= SPRD_LCR_DATA_LEN7; + break; + case CS8: + default: + lcr |= SPRD_LCR_DATA_LEN8; + break; + } + + /* calculate stop bits */ + lcr &= ~(SPRD_LCR_STOP_1BIT | SPRD_LCR_STOP_2BIT); + if (termios->c_cflag & CSTOPB) + lcr |= SPRD_LCR_STOP_2BIT; + else + lcr |= SPRD_LCR_STOP_1BIT; + + /* calculate parity */ + lcr &= ~SPRD_LCR_PARITY; + termios->c_cflag &= ~CMSPAR; /* no support mark/space */ + if (termios->c_cflag & PARENB) { + lcr |= SPRD_LCR_PARITY_EN; + if (termios->c_cflag & PARODD) + lcr |= SPRD_LCR_ODD_PAR; + else + lcr |= SPRD_LCR_EVEN_PAR; + } + + spin_lock_irqsave(&port->lock, flags); + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = SPRD_LSR_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SPRD_LSR_FE | SPRD_LSR_PE; + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= SPRD_LSR_BI; + + /* characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SPRD_LSR_PE | SPRD_LSR_FE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= SPRD_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SPRD_LSR_OE; + } + + /* flow control */ + fc = serial_in(port, SPRD_CTL1); + fc &= ~(RX_HW_FLOW_CTL_THLD | RX_HW_FLOW_CTL_EN | TX_HW_FLOW_CTL_EN); + if (termios->c_cflag & CRTSCTS) { + fc |= RX_HW_FLOW_CTL_THLD; + fc |= RX_HW_FLOW_CTL_EN; + fc |= TX_HW_FLOW_CTL_EN; + } + + /* clock divider bit0~bit15 */ + serial_out(port, SPRD_CLKD0, quot & 0xffff); + + /* clock divider bit16~bit20 */ + serial_out(port, SPRD_CLKD1, (quot & 0x1f0000) >> 16); + serial_out(port, SPRD_LCR, lcr); + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static const char *sprd_type(struct uart_port *port) +{ + return "SPX"; +} + +static void sprd_release_port(struct uart_port *port) +{ + /* nothing to do */ +} + +static int sprd_request_port(struct uart_port *port) +{ + return 0; +} + +static void sprd_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SPRD; +} + +static int sprd_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_SPRD) + return -EINVAL; + if (port->irq != ser->irq) + return -EINVAL; + return 0; +} + +static struct uart_ops serial_sprd_ops = { + .tx_empty = sprd_tx_empty, + .get_mctrl = sprd_get_mctrl, + .set_mctrl = sprd_set_mctrl, + .stop_tx = sprd_stop_tx, + .start_tx = sprd_start_tx, + .stop_rx = sprd_stop_rx, + .break_ctl = sprd_break_ctl, + .startup = sprd_startup, + .shutdown = sprd_shutdown, + .set_termios = sprd_set_termios, + .type = sprd_type, + .release_port = sprd_release_port, + .request_port = sprd_request_port, + .config_port = sprd_config_port, + .verify_port = sprd_verify_port, +}; + +#ifdef CONFIG_SERIAL_SPRD_CONSOLE +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int status, tmout = 10000; + + /* wait up to 10ms for the character(s) to be sent */ + do { + status = serial_in(port, SPRD_STS1); + if (--tmout == 0) + break; + udelay(1); + } while (status & 0xff00); +} + +static void sprd_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + serial_out(port, SPRD_TXD, ch); +} + +static void sprd_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &sprd_port[co->index]->port; + int locked = 1; + unsigned long flags; + + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); + + uart_console_write(port, s, count, sprd_console_putchar); + + /* wait for transmitter to become empty */ + wait_for_xmitr(port); + + if (locked) + spin_unlock_irqrestore(&port->lock, flags); +} + +static int __init sprd_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= UART_NR_MAX || co->index < 0) + co->index = 0; + + port = &sprd_port[co->index]->port; + if (port == NULL) { + pr_info("serial port %d not yet initialized\n", co->index); + return -ENODEV; + } + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver sprd_uart_driver; +static struct console sprd_console = { + .name = SPRD_TTY_NAME, + .write = sprd_console_write, + .device = uart_console_device, + .setup = sprd_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sprd_uart_driver, +}; + +#define SPRD_CONSOLE (&sprd_console) + +/* Support for earlycon */ +static void sprd_putc(struct uart_port *port, int c) +{ + unsigned int timeout = SPRD_TIMEOUT; + + while (timeout-- && + !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER)) + cpu_relax(); + + writeb(c, port->membase + SPRD_TXD); +} + +static void sprd_early_write(struct console *con, const char *s, + unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, sprd_putc); +} + +static int __init sprd_early_console_setup( + struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = sprd_early_write; + return 0; +} + +EARLYCON_DECLARE(sprd_serial, sprd_early_console_setup); +OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart", + sprd_early_console_setup); + +#else /* !CONFIG_SERIAL_SPRD_CONSOLE */ +#define SPRD_CONSOLE NULL +#endif + +static struct uart_driver sprd_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "sprd_serial", + .dev_name = SPRD_TTY_NAME, + .major = 0, + .minor = 0, + .nr = UART_NR_MAX, + .cons = SPRD_CONSOLE, +}; + +static int sprd_probe_dt_alias(int index, struct device *dev) +{ + struct device_node *np; + int ret = index; + + if (!IS_ENABLED(CONFIG_OF)) + return ret; + + np = dev->of_node; + if (!np) + return ret; + + ret = of_alias_get_id(np, "serial"); + if (IS_ERR_VALUE(ret)) + ret = index; + else if (ret >= ARRAY_SIZE(sprd_port) || sprd_port[ret] != NULL) { + dev_warn(dev, "requested serial port %d not available.\n", ret); + ret = index; + } + + return ret; +} + +static int sprd_remove(struct platform_device *dev) +{ + struct sprd_uart_port *sup = platform_get_drvdata(dev); + + if (sup) { + uart_remove_one_port(&sprd_uart_driver, &sup->port); + sprd_port[sup->port.line] = NULL; + sprd_ports_num--; + } + + if (!sprd_ports_num) + uart_unregister_driver(&sprd_uart_driver); + + return 0; +} + +static int sprd_probe(struct platform_device *pdev) +{ + struct resource *res; + struct uart_port *up; + struct clk *clk; + int irq; + int index; + int ret; + + for (index = 0; index < ARRAY_SIZE(sprd_port); index++) + if (sprd_port[index] == NULL) + break; + + if (index == ARRAY_SIZE(sprd_port)) + return -EBUSY; + + index = sprd_probe_dt_alias(index, &pdev->dev); + + sprd_port[index] = devm_kzalloc(&pdev->dev, + sizeof(*sprd_port[index]), GFP_KERNEL); + if (!sprd_port[index]) + return -ENOMEM; + + up = &sprd_port[index]->port; + up->dev = &pdev->dev; + up->line = index; + up->type = PORT_SPRD; + up->iotype = SERIAL_IO_PORT; + up->uartclk = SPRD_DEF_RATE; + up->fifosize = SPRD_FIFO_SIZE; + up->ops = &serial_sprd_ops; + up->flags = UPF_BOOT_AUTOCONF; + + clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) + up->uartclk = clk_get_rate(clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "not provide mem resource\n"); + return -ENODEV; + } + up->mapbase = res->start; + up->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(up->membase)) + return PTR_ERR(up->membase); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "not provide irq resource\n"); + return -ENODEV; + } + up->irq = irq; + + if (!sprd_ports_num) { + ret = uart_register_driver(&sprd_uart_driver); + if (ret < 0) { + pr_err("Failed to register SPRD-UART driver\n"); + return ret; + } + } + sprd_ports_num++; + + ret = uart_add_one_port(&sprd_uart_driver, up); + if (ret) { + sprd_port[index] = NULL; + sprd_remove(pdev); + } + + platform_set_drvdata(pdev, up); + + return ret; +} + +static int sprd_suspend(struct device *dev) +{ + struct sprd_uart_port *sup = dev_get_drvdata(dev); + + uart_suspend_port(&sprd_uart_driver, &sup->port); + + return 0; +} + +static int sprd_resume(struct device *dev) +{ + struct sprd_uart_port *sup = dev_get_drvdata(dev); + + uart_resume_port(&sprd_uart_driver, &sup->port); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume); + +static const struct of_device_id serial_ids[] = { + {.compatible = "sprd,sc9836-uart",}, + {} +}; + +static struct platform_driver sprd_platform_driver = { + .probe = sprd_probe, + .remove = sprd_remove, + .driver = { + .name = "sprd_serial", + .of_match_table = of_match_ptr(serial_ids), + .pm = &sprd_pm_ops, + }, +}; + +module_platform_driver(sprd_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Spreadtrum SoC serial driver series"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index c172180..7e6eb39 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -248,4 +248,7 @@ /* MESON */ #define PORT_MESON 109 +/* SPRD SERIAL */ +#define PORT_SPRD 110 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 2:47 ` Chunyan Zhang 0 siblings, 0 replies; 24+ messages in thread From: Chunyan Zhang @ 2015-01-28 2:47 UTC (permalink / raw) To: linux-arm-kernel Add a full sc9836-uart driver for SC9836 SoC which is based on the spreadtrum sharkl64 platform. This driver also support earlycon. Originally-by: Lanqing Liu <lanqing.liu@spreadtrum.com> Signed-off-by: Orson Zhai <orson.zhai@spreadtrum.com> Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com> Acked-by: Arnd Bergmann <arnd@arndb.de> --- drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 789 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 811 insertions(+) create mode 100644 drivers/tty/serial/sprd_serial.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index c79b43c..13211f7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1577,6 +1577,24 @@ config SERIAL_MEN_Z135 This driver can also be build as a module. If so, the module will be called men_z135_uart.ko +config SERIAL_SPRD + tristate "Support for Spreadtrum serial" + depends on ARCH_SPRD + select SERIAL_CORE + help + This enables the driver for the Spreadtrum's serial. + +config SERIAL_SPRD_CONSOLE + bool "Spreadtrum UART console support" + depends on SERIAL_SPRD=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Support for early debug console using Spreadtrum's serial. This enables + the console before standard serial driver is probed. This is enabled + with "earlycon" on the kernel command line. The console is + enabled when early_param is processed. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 9a548ac..4801aca 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_SERIAL_ARC) += arc_uart.o obj-$(CONFIG_SERIAL_RP2) += rp2.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o +obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c new file mode 100644 index 0000000..a08a57f --- /dev/null +++ b/drivers/tty/serial/sprd_serial.c @@ -0,0 +1,789 @@ +/* + * Copyright (C) 2012-2015 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +/* device name */ +#define UART_NR_MAX 8 +#define SPRD_TTY_NAME "ttyS" +#define SPRD_FIFO_SIZE 128 +#define SPRD_DEF_RATE 26000000 +#define SPRD_BAUD_IO_LIMIT 3000000 +#define SPRD_TIMEOUT 256 + +/* the offset of serial registers and BITs for them */ +/* data registers */ +#define SPRD_TXD 0x0000 +#define SPRD_RXD 0x0004 + +/* line status register and its BITs */ +#define SPRD_LSR 0x0008 +#define SPRD_LSR_OE BIT(4) +#define SPRD_LSR_FE BIT(3) +#define SPRD_LSR_PE BIT(2) +#define SPRD_LSR_BI BIT(7) +#define SPRD_LSR_TX_OVER BIT(15) + +/* data number in TX and RX fifo */ +#define SPRD_STS1 0x000C + +/* interrupt enable register and its BITs */ +#define SPRD_IEN 0x0010 +#define SPRD_IEN_RX_FULL BIT(0) +#define SPRD_IEN_TX_EMPTY BIT(1) +#define SPRD_IEN_BREAK_DETECT BIT(7) +#define SPRD_IEN_TIMEOUT BIT(13) + +/* interrupt clear register */ +#define SPRD_ICLR 0x0014 + +/* line control register */ +#define SPRD_LCR 0x0018 +#define SPRD_LCR_STOP_1BIT 0x10 +#define SPRD_LCR_STOP_2BIT 0x30 +#define SPRD_LCR_DATA_LEN (BIT(2) | BIT(3)) +#define SPRD_LCR_DATA_LEN5 0x0 +#define SPRD_LCR_DATA_LEN6 0x4 +#define SPRD_LCR_DATA_LEN7 0x8 +#define SPRD_LCR_DATA_LEN8 0xc +#define SPRD_LCR_PARITY (BIT(0) | BIT(1)) +#define SPRD_LCR_PARITY_EN 0x2 +#define SPRD_LCR_EVEN_PAR 0x0 +#define SPRD_LCR_ODD_PAR 0x1 + +/* control register 1 */ +#define SPRD_CTL1 0x001C +#define RX_HW_FLOW_CTL_THLD BIT(6) +#define RX_HW_FLOW_CTL_EN BIT(7) +#define TX_HW_FLOW_CTL_EN BIT(8) +#define RX_TOUT_THLD_DEF 0x3E00 +#define RX_HFC_THLD_DEF 0x40 + +/* fifo threshold register */ +#define SPRD_CTL2 0x0020 +#define THLD_TX_EMPTY 0x40 +#define THLD_RX_FULL 0x40 + +/* config baud rate register */ +#define SPRD_CLKD0 0x0024 +#define SPRD_CLKD1 0x0028 + +/* interrupt mask status register */ +#define SPRD_IMSR 0x002C +#define SPRD_IMSR_RX_FIFO_FULL BIT(0) +#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) +#define SPRD_IMSR_BREAK_DETECT BIT(7) +#define SPRD_IMSR_TIMEOUT BIT(13) + +struct reg_backup { + u32 ien; + u32 ctrl0; + u32 ctrl1; + u32 ctrl2; + u32 clkd0; + u32 clkd1; + u32 dspwait; +}; + +struct sprd_uart_port { + struct uart_port port; + struct reg_backup reg_bak; + char name[16]; +}; + +static struct sprd_uart_port *sprd_port[UART_NR_MAX]; +static int sprd_ports_num; + +static inline unsigned int serial_in(struct uart_port *port, int offset) +{ + return readl_relaxed(port->membase + offset); +} + +static inline void serial_out(struct uart_port *port, int offset, int value) +{ + writel_relaxed(value, port->membase + offset); +} + +static unsigned int sprd_tx_empty(struct uart_port *port) +{ + if (serial_in(port, SPRD_STS1) & 0xff00) + return 0; + else + return TIOCSER_TEMT; +} + +static unsigned int sprd_get_mctrl(struct uart_port *port) +{ + return TIOCM_DSR | TIOCM_CTS; +} + +static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* nothing to do */ +} + +static void sprd_stop_tx(struct uart_port *port) +{ + unsigned int ien, iclr; + + iclr = serial_in(port, SPRD_ICLR); + ien = serial_in(port, SPRD_IEN); + + iclr |= SPRD_IEN_TX_EMPTY; + ien &= ~SPRD_IEN_TX_EMPTY; + + serial_out(port, SPRD_ICLR, iclr); + serial_out(port, SPRD_IEN, ien); +} + +static void sprd_start_tx(struct uart_port *port) +{ + unsigned int ien; + + ien = serial_in(port, SPRD_IEN); + if (!(ien & SPRD_IEN_TX_EMPTY)) { + ien |= SPRD_IEN_TX_EMPTY; + serial_out(port, SPRD_IEN, ien); + } +} + +static void sprd_stop_rx(struct uart_port *port) +{ + unsigned int ien, iclr; + + iclr = serial_in(port, SPRD_ICLR); + ien = serial_in(port, SPRD_IEN); + + ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT); + iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT; + + serial_out(port, SPRD_IEN, ien); + serial_out(port, SPRD_ICLR, iclr); +} + +/* The Sprd serial does not support this function. */ +static void sprd_break_ctl(struct uart_port *port, int break_state) +{ + /* nothing to do */ +} + +static int handle_lsr_errors(struct uart_port *port, + unsigned int *flag, + unsigned int *lsr) +{ + int ret = 0; + + /* statistics */ + if (*lsr & SPRD_LSR_BI) { + *lsr &= ~(SPRD_LSR_FE | SPRD_LSR_PE); + port->icount.brk++; + ret = uart_handle_break(port); + if (ret) + return ret; + } else if (*lsr & SPRD_LSR_PE) + port->icount.parity++; + else if (*lsr & SPRD_LSR_FE) + port->icount.frame++; + if (*lsr & SPRD_LSR_OE) + port->icount.overrun++; + + /* mask off conditions which should be ignored */ + *lsr &= port->read_status_mask; + if (*lsr & SPRD_LSR_BI) + *flag = TTY_BREAK; + else if (*lsr & SPRD_LSR_PE) + *flag = TTY_PARITY; + else if (*lsr & SPRD_LSR_FE) + *flag = TTY_FRAME; + + return ret; +} + +static inline void sprd_rx(struct uart_port *port) +{ + struct tty_port *tty = &port->state->port; + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; + + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { + lsr = serial_in(port, SPRD_LSR); + ch = serial_in(port, SPRD_RXD); + flag = TTY_NORMAL; + port->icount.rx++; + + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | + SPRD_LSR_FE | SPRD_LSR_OE)) + if (handle_lsr_errors(port, &lsr, &flag)) + continue; + if (uart_handle_sysrq_char(port, ch)) + continue; + + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); + } + + tty_flip_buffer_push(tty); +} + +static inline void sprd_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int count; + + if (port->x_char) { + serial_out(port, SPRD_TXD, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sprd_stop_tx(port); + return; + } + + count = THLD_TX_EMPTY; + do { + serial_out(port, SPRD_TXD, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + sprd_stop_tx(port); +} + +/* this handles the interrupt from one port */ +static irqreturn_t sprd_handle_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int ims; + + spin_lock(&port->lock); + + ims = serial_in(port, SPRD_IMSR); + + if (!ims) + return IRQ_NONE; + + serial_out(port, SPRD_ICLR, ~0); + + if (ims & (SPRD_IMSR_RX_FIFO_FULL | + SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT)) + sprd_rx(port); + + if (ims & SPRD_IMSR_TX_FIFO_EMPTY) + sprd_tx(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static int sprd_startup(struct uart_port *port) +{ + int ret = 0; + unsigned int ien, fc; + unsigned int timeout; + struct sprd_uart_port *sp; + unsigned long flags; + + serial_out(port, SPRD_CTL2, ((THLD_TX_EMPTY << 8) | THLD_RX_FULL)); + + /* clear rx fifo */ + timeout = SPRD_TIMEOUT; + while (timeout-- && serial_in(port, SPRD_STS1) & 0x00ff) + serial_in(port, SPRD_RXD); + + /* clear tx fifo */ + timeout = SPRD_TIMEOUT; + while (timeout-- && serial_in(port, SPRD_STS1) & 0xff00) + cpu_relax(); + + /* clear interrupt */ + serial_out(port, SPRD_IEN, 0); + serial_out(port, SPRD_ICLR, ~0); + + /* allocate irq */ + sp = container_of(port, struct sprd_uart_port, port); + snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line); + ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq, + IRQF_SHARED, sp->name, port); + if (ret) { + dev_err(port->dev, "fail to request serial irq %d, ret=%d\n", + port->irq, ret); + return ret; + } + fc = serial_in(port, SPRD_CTL1); + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + + /* enable interrupt */ + spin_lock_irqsave(&port->lock, flags); + ien = serial_in(port, SPRD_IEN); + ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + serial_out(port, SPRD_IEN, ien); + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void sprd_shutdown(struct uart_port *port) +{ + serial_out(port, SPRD_IEN, 0); + serial_out(port, SPRD_ICLR, ~0); + devm_free_irq(port->dev, port->irq, port); +} + +static void sprd_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud, quot; + unsigned int lcr = 0, fc; + unsigned long flags; + + /* ask the core to calculate the divisor for us */ + baud = uart_get_baud_rate(port, termios, old, 0, SPRD_BAUD_IO_LIMIT); + + quot = (unsigned int)((port->uartclk + baud / 2) / baud); + + /* set data length */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= SPRD_LCR_DATA_LEN5; + break; + case CS6: + lcr |= SPRD_LCR_DATA_LEN6; + break; + case CS7: + lcr |= SPRD_LCR_DATA_LEN7; + break; + case CS8: + default: + lcr |= SPRD_LCR_DATA_LEN8; + break; + } + + /* calculate stop bits */ + lcr &= ~(SPRD_LCR_STOP_1BIT | SPRD_LCR_STOP_2BIT); + if (termios->c_cflag & CSTOPB) + lcr |= SPRD_LCR_STOP_2BIT; + else + lcr |= SPRD_LCR_STOP_1BIT; + + /* calculate parity */ + lcr &= ~SPRD_LCR_PARITY; + termios->c_cflag &= ~CMSPAR; /* no support mark/space */ + if (termios->c_cflag & PARENB) { + lcr |= SPRD_LCR_PARITY_EN; + if (termios->c_cflag & PARODD) + lcr |= SPRD_LCR_ODD_PAR; + else + lcr |= SPRD_LCR_EVEN_PAR; + } + + spin_lock_irqsave(&port->lock, flags); + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = SPRD_LSR_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SPRD_LSR_FE | SPRD_LSR_PE; + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= SPRD_LSR_BI; + + /* characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SPRD_LSR_PE | SPRD_LSR_FE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= SPRD_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SPRD_LSR_OE; + } + + /* flow control */ + fc = serial_in(port, SPRD_CTL1); + fc &= ~(RX_HW_FLOW_CTL_THLD | RX_HW_FLOW_CTL_EN | TX_HW_FLOW_CTL_EN); + if (termios->c_cflag & CRTSCTS) { + fc |= RX_HW_FLOW_CTL_THLD; + fc |= RX_HW_FLOW_CTL_EN; + fc |= TX_HW_FLOW_CTL_EN; + } + + /* clock divider bit0~bit15 */ + serial_out(port, SPRD_CLKD0, quot & 0xffff); + + /* clock divider bit16~bit20 */ + serial_out(port, SPRD_CLKD1, (quot & 0x1f0000) >> 16); + serial_out(port, SPRD_LCR, lcr); + fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; + serial_out(port, SPRD_CTL1, fc); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static const char *sprd_type(struct uart_port *port) +{ + return "SPX"; +} + +static void sprd_release_port(struct uart_port *port) +{ + /* nothing to do */ +} + +static int sprd_request_port(struct uart_port *port) +{ + return 0; +} + +static void sprd_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SPRD; +} + +static int sprd_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_SPRD) + return -EINVAL; + if (port->irq != ser->irq) + return -EINVAL; + return 0; +} + +static struct uart_ops serial_sprd_ops = { + .tx_empty = sprd_tx_empty, + .get_mctrl = sprd_get_mctrl, + .set_mctrl = sprd_set_mctrl, + .stop_tx = sprd_stop_tx, + .start_tx = sprd_start_tx, + .stop_rx = sprd_stop_rx, + .break_ctl = sprd_break_ctl, + .startup = sprd_startup, + .shutdown = sprd_shutdown, + .set_termios = sprd_set_termios, + .type = sprd_type, + .release_port = sprd_release_port, + .request_port = sprd_request_port, + .config_port = sprd_config_port, + .verify_port = sprd_verify_port, +}; + +#ifdef CONFIG_SERIAL_SPRD_CONSOLE +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int status, tmout = 10000; + + /* wait up to 10ms for the character(s) to be sent */ + do { + status = serial_in(port, SPRD_STS1); + if (--tmout == 0) + break; + udelay(1); + } while (status & 0xff00); +} + +static void sprd_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + serial_out(port, SPRD_TXD, ch); +} + +static void sprd_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &sprd_port[co->index]->port; + int locked = 1; + unsigned long flags; + + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); + + uart_console_write(port, s, count, sprd_console_putchar); + + /* wait for transmitter to become empty */ + wait_for_xmitr(port); + + if (locked) + spin_unlock_irqrestore(&port->lock, flags); +} + +static int __init sprd_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= UART_NR_MAX || co->index < 0) + co->index = 0; + + port = &sprd_port[co->index]->port; + if (port == NULL) { + pr_info("serial port %d not yet initialized\n", co->index); + return -ENODEV; + } + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver sprd_uart_driver; +static struct console sprd_console = { + .name = SPRD_TTY_NAME, + .write = sprd_console_write, + .device = uart_console_device, + .setup = sprd_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sprd_uart_driver, +}; + +#define SPRD_CONSOLE (&sprd_console) + +/* Support for earlycon */ +static void sprd_putc(struct uart_port *port, int c) +{ + unsigned int timeout = SPRD_TIMEOUT; + + while (timeout-- && + !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER)) + cpu_relax(); + + writeb(c, port->membase + SPRD_TXD); +} + +static void sprd_early_write(struct console *con, const char *s, + unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, sprd_putc); +} + +static int __init sprd_early_console_setup( + struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = sprd_early_write; + return 0; +} + +EARLYCON_DECLARE(sprd_serial, sprd_early_console_setup); +OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart", + sprd_early_console_setup); + +#else /* !CONFIG_SERIAL_SPRD_CONSOLE */ +#define SPRD_CONSOLE NULL +#endif + +static struct uart_driver sprd_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "sprd_serial", + .dev_name = SPRD_TTY_NAME, + .major = 0, + .minor = 0, + .nr = UART_NR_MAX, + .cons = SPRD_CONSOLE, +}; + +static int sprd_probe_dt_alias(int index, struct device *dev) +{ + struct device_node *np; + int ret = index; + + if (!IS_ENABLED(CONFIG_OF)) + return ret; + + np = dev->of_node; + if (!np) + return ret; + + ret = of_alias_get_id(np, "serial"); + if (IS_ERR_VALUE(ret)) + ret = index; + else if (ret >= ARRAY_SIZE(sprd_port) || sprd_port[ret] != NULL) { + dev_warn(dev, "requested serial port %d not available.\n", ret); + ret = index; + } + + return ret; +} + +static int sprd_remove(struct platform_device *dev) +{ + struct sprd_uart_port *sup = platform_get_drvdata(dev); + + if (sup) { + uart_remove_one_port(&sprd_uart_driver, &sup->port); + sprd_port[sup->port.line] = NULL; + sprd_ports_num--; + } + + if (!sprd_ports_num) + uart_unregister_driver(&sprd_uart_driver); + + return 0; +} + +static int sprd_probe(struct platform_device *pdev) +{ + struct resource *res; + struct uart_port *up; + struct clk *clk; + int irq; + int index; + int ret; + + for (index = 0; index < ARRAY_SIZE(sprd_port); index++) + if (sprd_port[index] == NULL) + break; + + if (index == ARRAY_SIZE(sprd_port)) + return -EBUSY; + + index = sprd_probe_dt_alias(index, &pdev->dev); + + sprd_port[index] = devm_kzalloc(&pdev->dev, + sizeof(*sprd_port[index]), GFP_KERNEL); + if (!sprd_port[index]) + return -ENOMEM; + + up = &sprd_port[index]->port; + up->dev = &pdev->dev; + up->line = index; + up->type = PORT_SPRD; + up->iotype = SERIAL_IO_PORT; + up->uartclk = SPRD_DEF_RATE; + up->fifosize = SPRD_FIFO_SIZE; + up->ops = &serial_sprd_ops; + up->flags = UPF_BOOT_AUTOCONF; + + clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) + up->uartclk = clk_get_rate(clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "not provide mem resource\n"); + return -ENODEV; + } + up->mapbase = res->start; + up->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(up->membase)) + return PTR_ERR(up->membase); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "not provide irq resource\n"); + return -ENODEV; + } + up->irq = irq; + + if (!sprd_ports_num) { + ret = uart_register_driver(&sprd_uart_driver); + if (ret < 0) { + pr_err("Failed to register SPRD-UART driver\n"); + return ret; + } + } + sprd_ports_num++; + + ret = uart_add_one_port(&sprd_uart_driver, up); + if (ret) { + sprd_port[index] = NULL; + sprd_remove(pdev); + } + + platform_set_drvdata(pdev, up); + + return ret; +} + +static int sprd_suspend(struct device *dev) +{ + struct sprd_uart_port *sup = dev_get_drvdata(dev); + + uart_suspend_port(&sprd_uart_driver, &sup->port); + + return 0; +} + +static int sprd_resume(struct device *dev) +{ + struct sprd_uart_port *sup = dev_get_drvdata(dev); + + uart_resume_port(&sprd_uart_driver, &sup->port); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume); + +static const struct of_device_id serial_ids[] = { + {.compatible = "sprd,sc9836-uart",}, + {} +}; + +static struct platform_driver sprd_platform_driver = { + .probe = sprd_probe, + .remove = sprd_remove, + .driver = { + .name = "sprd_serial", + .of_match_table = of_match_ptr(serial_ids), + .pm = &sprd_pm_ops, + }, +}; + +module_platform_driver(sprd_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Spreadtrum SoC serial driver series"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index c172180..7e6eb39 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -248,4 +248,7 @@ /* MESON */ #define PORT_MESON 109 +/* SPRD SERIAL */ +#define PORT_SPRD 110 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 24+ messages in thread
[parent not found: <1422413261-17184-3-git-send-email-chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org>]
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support 2015-01-28 2:47 ` Chunyan Zhang (?) @ 2015-01-28 3:59 ` Peter Hurley -1 siblings, 0 replies; 24+ messages in thread From: Peter Hurley @ 2015-01-28 3:59 UTC (permalink / raw) To: Chunyan Zhang, gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8, arnd-r2nGTMty4D4, gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io, pawel.moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg, galak-sgV2jX0FEOL9JmXXK+q4OQ, grant.likely-QSEj5FYQhm4dnm+yROfE0A, jslaby-AlSwsSmVLrQ, heiko-4mtYJXux2i+zQB+pC5nmwQ, jason-NLaQJdtUoK4Be96aLqz0jA, florian.vaussard-p8DiymsW2f8, andrew-g2DYL2Zd6BY, hytszk-Re5JQEeQqe8AvxtiuMwx3w, antonynpavlov-Re5JQEeQqe8AvxtiuMwx3w, shawn.guo-QSEj5FYQhm4dnm+yROfE0A, orsonzhai-Re5JQEeQqe8AvxtiuMwx3w, geng.ren-lxIno14LUO0EEoCn2XhGlw, zhizhou.zhang-lxIno14LUO0EEoCn2XhGlw, lanqing.liu-lxIno14LUO0EEoCn2XhGlw, zhang.lyra-Re5JQEeQqe8AvxtiuMwx3w, wei.qiao-lxIno14LUO0EEoCn2XhGlw, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-serial-u79uwXL29TY76Z2rM5mHXA, linux-api-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r On 01/27/2015 09:47 PM, Chunyan Zhang wrote: > Add a full sc9836-uart driver for SC9836 SoC which is based on the > spreadtrum sharkl64 platform. > This driver also support earlycon. Reviewed-by: Peter Hurley <peter-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8@public.gmane.org> -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 3:59 ` Peter Hurley 0 siblings, 0 replies; 24+ messages in thread From: Peter Hurley @ 2015-01-28 3:59 UTC (permalink / raw) To: Chunyan Zhang, gregkh Cc: robh+dt, mark.rutland, arnd, gnomes, pawel.moll, ijc+devicetree, galak, grant.likely, jslaby, heiko, jason, florian.vaussard, andrew, hytszk, antonynpavlov, shawn.guo, orsonzhai, geng.ren, zhizhou.zhang, lanqing.liu, zhang.lyra, wei.qiao, devicetree, linux-kernel, linux-serial, linux-api, linux-arm-kernel On 01/27/2015 09:47 PM, Chunyan Zhang wrote: > Add a full sc9836-uart driver for SC9836 SoC which is based on the > spreadtrum sharkl64 platform. > This driver also support earlycon. Reviewed-by: Peter Hurley <peter@hurleysoftware.com> ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 3:59 ` Peter Hurley 0 siblings, 0 replies; 24+ messages in thread From: Peter Hurley @ 2015-01-28 3:59 UTC (permalink / raw) To: linux-arm-kernel On 01/27/2015 09:47 PM, Chunyan Zhang wrote: > Add a full sc9836-uart driver for SC9836 SoC which is based on the > spreadtrum sharkl64 platform. > This driver also support earlycon. Reviewed-by: Peter Hurley <peter@hurleysoftware.com> ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support 2015-01-28 2:47 ` Chunyan Zhang (?) @ 2015-01-28 6:12 ` Baruch Siach -1 siblings, 0 replies; 24+ messages in thread From: Baruch Siach @ 2015-01-28 6:12 UTC (permalink / raw) To: Chunyan Zhang Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, mark.rutland-5wv7dgnIgG8, gnomes-qBU/x9rampVanCEyBjwyrvXRex20P6io, heiko-4mtYJXux2i+zQB+pC5nmwQ, andrew-g2DYL2Zd6BY, jslaby-AlSwsSmVLrQ, lanqing.liu-lxIno14LUO0EEoCn2XhGlw, arnd-r2nGTMty4D4, zhang.lyra-Re5JQEeQqe8AvxtiuMwx3w, zhizhou.zhang-lxIno14LUO0EEoCn2XhGlw, geng.ren-lxIno14LUO0EEoCn2XhGlw, antonynpavlov-Re5JQEeQqe8AvxtiuMwx3w, linux-serial-u79uwXL29TY76Z2rM5mHXA, grant.likely-QSEj5FYQhm4dnm+yROfE0A, orsonzhai-Re5JQEeQqe8AvxtiuMwx3w, florian.vaussard-p8DiymsW2f8, devicetree-u79uwXL29TY76Z2rM5mHXA, jason-NLaQJdtUoK4Be96aLqz0jA, pawel.moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg, hytszk-Re5JQEeQqe8AvxtiuMwx3w, robh+dt-DgEjT+Ai2ygdnm+yROfE0A, wei.qiao-lxIno14LUO0EEoCn2XhGlw, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, peter-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8, linux-api-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, galak-sgV2jX0FEOL9JmXXK+q4OQ, shawn.guo-QSEj5FYQhm4dnm+yROfE0A Hi Chunyan Zhang, On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: [...] > +static inline void sprd_rx(struct uart_port *port) > +{ > + struct tty_port *tty = &port->state->port; > + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; > + > + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { > + lsr = serial_in(port, SPRD_LSR); > + ch = serial_in(port, SPRD_RXD); > + flag = TTY_NORMAL; > + port->icount.rx++; > + > + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | > + SPRD_LSR_FE | SPRD_LSR_OE)) > + if (handle_lsr_errors(port, &lsr, &flag)) > + continue; > + if (uart_handle_sysrq_char(port, ch)) > + continue; My comment[1] on a previous version of this patch still stands. uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > + > + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); > + } > + > + tty_flip_buffer_push(tty); > +} [...] > +static void sprd_console_write(struct console *co, const char *s, > + unsigned int count) > +{ > + struct uart_port *port = &sprd_port[co->index]->port; > + int locked = 1; > + unsigned long flags; > + > + if (port->sysrq) > + locked = 0; The sysrq field of struct uart_port is only defined when CONFIG_SERIAL_CORE_CONSOLE or SUPPORT_SYSRQ are defined. You should #ifdef this part accordingly to avoid build breakage. > + else if (oops_in_progress) > + locked = spin_trylock_irqsave(&port->lock, flags); > + else > + spin_lock_irqsave(&port->lock, flags); > + > + uart_console_write(port, s, count, sprd_console_putchar); > + > + /* wait for transmitter to become empty */ > + wait_for_xmitr(port); > + > + if (locked) > + spin_unlock_irqrestore(&port->lock, flags); > +} [1] http://article.gmane.org/gmane.linux.drivers.devicetree/106483 baruch -- http://baruch.siach.name/blog/ ~. .~ Tk Open Systems =}------------------------------------------------ooO--U--Ooo------------{= - baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 6:12 ` Baruch Siach 0 siblings, 0 replies; 24+ messages in thread From: Baruch Siach @ 2015-01-28 6:12 UTC (permalink / raw) To: Chunyan Zhang Cc: gregkh, mark.rutland, gnomes, heiko, andrew, jslaby, lanqing.liu, arnd, zhang.lyra, zhizhou.zhang, geng.ren, antonynpavlov, linux-serial, grant.likely, orsonzhai, florian.vaussard, devicetree, jason, pawel.moll, ijc+devicetree, hytszk, robh+dt, wei.qiao, linux-arm-kernel, peter, linux-api, linux-kernel, galak, shawn.guo Hi Chunyan Zhang, On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: [...] > +static inline void sprd_rx(struct uart_port *port) > +{ > + struct tty_port *tty = &port->state->port; > + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; > + > + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { > + lsr = serial_in(port, SPRD_LSR); > + ch = serial_in(port, SPRD_RXD); > + flag = TTY_NORMAL; > + port->icount.rx++; > + > + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | > + SPRD_LSR_FE | SPRD_LSR_OE)) > + if (handle_lsr_errors(port, &lsr, &flag)) > + continue; > + if (uart_handle_sysrq_char(port, ch)) > + continue; My comment[1] on a previous version of this patch still stands. uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > + > + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); > + } > + > + tty_flip_buffer_push(tty); > +} [...] > +static void sprd_console_write(struct console *co, const char *s, > + unsigned int count) > +{ > + struct uart_port *port = &sprd_port[co->index]->port; > + int locked = 1; > + unsigned long flags; > + > + if (port->sysrq) > + locked = 0; The sysrq field of struct uart_port is only defined when CONFIG_SERIAL_CORE_CONSOLE or SUPPORT_SYSRQ are defined. You should #ifdef this part accordingly to avoid build breakage. > + else if (oops_in_progress) > + locked = spin_trylock_irqsave(&port->lock, flags); > + else > + spin_lock_irqsave(&port->lock, flags); > + > + uart_console_write(port, s, count, sprd_console_putchar); > + > + /* wait for transmitter to become empty */ > + wait_for_xmitr(port); > + > + if (locked) > + spin_unlock_irqrestore(&port->lock, flags); > +} [1] http://article.gmane.org/gmane.linux.drivers.devicetree/106483 baruch -- http://baruch.siach.name/blog/ ~. .~ Tk Open Systems =}------------------------------------------------ooO--U--Ooo------------{= - baruch@tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 6:12 ` Baruch Siach 0 siblings, 0 replies; 24+ messages in thread From: Baruch Siach @ 2015-01-28 6:12 UTC (permalink / raw) To: linux-arm-kernel Hi Chunyan Zhang, On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: [...] > +static inline void sprd_rx(struct uart_port *port) > +{ > + struct tty_port *tty = &port->state->port; > + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; > + > + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { > + lsr = serial_in(port, SPRD_LSR); > + ch = serial_in(port, SPRD_RXD); > + flag = TTY_NORMAL; > + port->icount.rx++; > + > + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | > + SPRD_LSR_FE | SPRD_LSR_OE)) > + if (handle_lsr_errors(port, &lsr, &flag)) > + continue; > + if (uart_handle_sysrq_char(port, ch)) > + continue; My comment[1] on a previous version of this patch still stands. uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > + > + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); > + } > + > + tty_flip_buffer_push(tty); > +} [...] > +static void sprd_console_write(struct console *co, const char *s, > + unsigned int count) > +{ > + struct uart_port *port = &sprd_port[co->index]->port; > + int locked = 1; > + unsigned long flags; > + > + if (port->sysrq) > + locked = 0; The sysrq field of struct uart_port is only defined when CONFIG_SERIAL_CORE_CONSOLE or SUPPORT_SYSRQ are defined. You should #ifdef this part accordingly to avoid build breakage. > + else if (oops_in_progress) > + locked = spin_trylock_irqsave(&port->lock, flags); > + else > + spin_lock_irqsave(&port->lock, flags); > + > + uart_console_write(port, s, count, sprd_console_putchar); > + > + /* wait for transmitter to become empty */ > + wait_for_xmitr(port); > + > + if (locked) > + spin_unlock_irqrestore(&port->lock, flags); > +} [1] http://article.gmane.org/gmane.linux.drivers.devicetree/106483 baruch -- http://baruch.siach.name/blog/ ~. .~ Tk Open Systems =}------------------------------------------------ooO--U--Ooo------------{= - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support 2015-01-28 6:12 ` Baruch Siach (?) @ 2015-01-28 8:46 ` Lyra Zhang -1 siblings, 0 replies; 24+ messages in thread From: Lyra Zhang @ 2015-01-28 8:46 UTC (permalink / raw) To: Baruch Siach Cc: Mark Rutland, andrew@lunn.ch, Heiko Stübner, gnomes@lxorguk.ukuu.org.uk, jslaby@suse.cz, lanqing.liu@spreadtrum.com, Pawel Moll, Chunyan Zhang, zhizhou.zhang, geng.ren@spreadtrum.com, antonynpavlov@gmail.com, linux-serial@vger.kernel.org, Grant Likely, Orson Zhai, florian.vaussard@epfl.ch, devicetree@vger.kernel.org, jason@lakedaemon.net, Arnd Bergmann Hi, Baruch I'm sorry that I didn't receive your previous email. and my explanations are below. On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: > Hi Chunyan Zhang, > > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: > [...] >> +static inline void sprd_rx(struct uart_port *port) >> +{ >> + struct tty_port *tty = &port->state->port; >> + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; >> + >> + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { >> + lsr = serial_in(port, SPRD_LSR); >> + ch = serial_in(port, SPRD_RXD); >> + flag = TTY_NORMAL; >> + port->icount.rx++; >> + >> + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | >> + SPRD_LSR_FE | SPRD_LSR_OE)) >> + if (handle_lsr_errors(port, &lsr, &flag)) >> + continue; >> + if (uart_handle_sysrq_char(port, ch)) >> + continue; > > My comment[1] on a previous version of this patch still stands. > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > yes, there are two definitions in serial_core.h, and it returns 0 when SUPPORT_SYSRQ is not defined. so, I think my code behavior does not hurt, right? >> + >> + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); >> + } >> + >> + tty_flip_buffer_push(tty); >> +} > > [...] > >> +static void sprd_console_write(struct console *co, const char *s, >> + unsigned int count) >> +{ >> + struct uart_port *port = &sprd_port[co->index]->port; >> + int locked = 1; >> + unsigned long flags; >> + >> + if (port->sysrq) >> + locked = 0; > > The sysrq field of struct uart_port is only defined when > CONFIG_SERIAL_CORE_CONSOLE or SUPPORT_SYSRQ are defined. You should #ifdef > this part accordingly to avoid build breakage. > we have this part to been included under #ifdef CONFIG_SERIAL_SPRD_CONSOLE which is selected with SERIAL_CORE_CONSOLE. Thank you very much! Chunyan >> + else if (oops_in_progress) >> + locked = spin_trylock_irqsave(&port->lock, flags); >> + else >> + spin_lock_irqsave(&port->lock, flags); >> + >> + uart_console_write(port, s, count, sprd_console_putchar); >> + >> + /* wait for transmitter to become empty */ >> + wait_for_xmitr(port); >> + >> + if (locked) >> + spin_unlock_irqrestore(&port->lock, flags); >> +} > > [1] > http://article.gmane.org/gmane.linux.drivers.devicetree/106483 > > baruch > > -- > http://baruch.siach.name/blog/ ~. .~ Tk Open Systems > =}------------------------------------------------ooO--U--Ooo------------{= > - baruch@tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 8:46 ` Lyra Zhang 0 siblings, 0 replies; 24+ messages in thread From: Lyra Zhang @ 2015-01-28 8:46 UTC (permalink / raw) To: Baruch Siach Cc: Chunyan Zhang, gregkh@linuxfoundation.org, Mark Rutland, gnomes@lxorguk.ukuu.org.uk, Heiko Stübner, andrew@lunn.ch, jslaby@suse.cz, lanqing.liu@spreadtrum.com, Arnd Bergmann, zhizhou.zhang, geng.ren@spreadtrum.com, antonynpavlov@gmail.com, linux-serial@vger.kernel.org, Grant Likely, Orson Zhai, florian.vaussard@epfl.ch, devicetree@vger.kernel.org, jason@lakedaemon.net, Pawel Moll, ijc+devicetree@hellion.org.uk, Hayato Suzuki, robh+dt@kernel.org, Wei Qiao (乔伟), linux-arm-kernel@lists.infradead.org, Peter Hurley, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, Kumar Gala, Shawn Guo Hi, Baruch I'm sorry that I didn't receive your previous email. and my explanations are below. On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: > Hi Chunyan Zhang, > > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: > [...] >> +static inline void sprd_rx(struct uart_port *port) >> +{ >> + struct tty_port *tty = &port->state->port; >> + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; >> + >> + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { >> + lsr = serial_in(port, SPRD_LSR); >> + ch = serial_in(port, SPRD_RXD); >> + flag = TTY_NORMAL; >> + port->icount.rx++; >> + >> + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | >> + SPRD_LSR_FE | SPRD_LSR_OE)) >> + if (handle_lsr_errors(port, &lsr, &flag)) >> + continue; >> + if (uart_handle_sysrq_char(port, ch)) >> + continue; > > My comment[1] on a previous version of this patch still stands. > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > yes, there are two definitions in serial_core.h, and it returns 0 when SUPPORT_SYSRQ is not defined. so, I think my code behavior does not hurt, right? >> + >> + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); >> + } >> + >> + tty_flip_buffer_push(tty); >> +} > > [...] > >> +static void sprd_console_write(struct console *co, const char *s, >> + unsigned int count) >> +{ >> + struct uart_port *port = &sprd_port[co->index]->port; >> + int locked = 1; >> + unsigned long flags; >> + >> + if (port->sysrq) >> + locked = 0; > > The sysrq field of struct uart_port is only defined when > CONFIG_SERIAL_CORE_CONSOLE or SUPPORT_SYSRQ are defined. You should #ifdef > this part accordingly to avoid build breakage. > we have this part to been included under #ifdef CONFIG_SERIAL_SPRD_CONSOLE which is selected with SERIAL_CORE_CONSOLE. Thank you very much! Chunyan >> + else if (oops_in_progress) >> + locked = spin_trylock_irqsave(&port->lock, flags); >> + else >> + spin_lock_irqsave(&port->lock, flags); >> + >> + uart_console_write(port, s, count, sprd_console_putchar); >> + >> + /* wait for transmitter to become empty */ >> + wait_for_xmitr(port); >> + >> + if (locked) >> + spin_unlock_irqrestore(&port->lock, flags); >> +} > > [1] > http://article.gmane.org/gmane.linux.drivers.devicetree/106483 > > baruch > > -- > http://baruch.siach.name/blog/ ~. .~ Tk Open Systems > =}------------------------------------------------ooO--U--Ooo------------{= > - baruch@tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 8:46 ` Lyra Zhang 0 siblings, 0 replies; 24+ messages in thread From: Lyra Zhang @ 2015-01-28 8:46 UTC (permalink / raw) To: linux-arm-kernel Hi, Baruch I'm sorry that I didn't receive your previous email. and my explanations are below. On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: > Hi Chunyan Zhang, > > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: > [...] >> +static inline void sprd_rx(struct uart_port *port) >> +{ >> + struct tty_port *tty = &port->state->port; >> + unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT; >> + >> + while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) { >> + lsr = serial_in(port, SPRD_LSR); >> + ch = serial_in(port, SPRD_RXD); >> + flag = TTY_NORMAL; >> + port->icount.rx++; >> + >> + if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | >> + SPRD_LSR_FE | SPRD_LSR_OE)) >> + if (handle_lsr_errors(port, &lsr, &flag)) >> + continue; >> + if (uart_handle_sysrq_char(port, ch)) >> + continue; > > My comment[1] on a previous version of this patch still stands. > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > yes, there are two definitions in serial_core.h, and it returns 0 when SUPPORT_SYSRQ is not defined. so, I think my code behavior does not hurt, right? >> + >> + uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag); >> + } >> + >> + tty_flip_buffer_push(tty); >> +} > > [...] > >> +static void sprd_console_write(struct console *co, const char *s, >> + unsigned int count) >> +{ >> + struct uart_port *port = &sprd_port[co->index]->port; >> + int locked = 1; >> + unsigned long flags; >> + >> + if (port->sysrq) >> + locked = 0; > > The sysrq field of struct uart_port is only defined when > CONFIG_SERIAL_CORE_CONSOLE or SUPPORT_SYSRQ are defined. You should #ifdef > this part accordingly to avoid build breakage. > we have this part to been included under #ifdef CONFIG_SERIAL_SPRD_CONSOLE which is selected with SERIAL_CORE_CONSOLE. Thank you very much! Chunyan >> + else if (oops_in_progress) >> + locked = spin_trylock_irqsave(&port->lock, flags); >> + else >> + spin_lock_irqsave(&port->lock, flags); >> + >> + uart_console_write(port, s, count, sprd_console_putchar); >> + >> + /* wait for transmitter to become empty */ >> + wait_for_xmitr(port); >> + >> + if (locked) >> + spin_unlock_irqrestore(&port->lock, flags); >> +} > > [1] > http://article.gmane.org/gmane.linux.drivers.devicetree/106483 > > baruch > > -- > http://baruch.siach.name/blog/ ~. .~ Tk Open Systems > =}------------------------------------------------ooO--U--Ooo------------{= > - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support 2015-01-28 8:46 ` Lyra Zhang (?) @ 2015-01-28 9:54 ` Baruch Siach -1 siblings, 0 replies; 24+ messages in thread From: Baruch Siach @ 2015-01-28 9:54 UTC (permalink / raw) To: Lyra Zhang Cc: Mark Rutland, andrew@lunn.ch, Heiko Stübner, gnomes@lxorguk.ukuu.org.uk, jslaby@suse.cz, lanqing.liu@spreadtrum.com, Pawel Moll, Chunyan Zhang, zhizhou.zhang, geng.ren@spreadtrum.com, antonynpavlov@gmail.com, linux-serial@vger.kernel.org, Grant Likely, Orson Zhai, florian.vaussard@epfl.ch, devicetree@vger.kernel.org, jason@lakedaemon.net, Arnd Bergmann Hi Lyra Zhang, On Wed, Jan 28, 2015 at 04:46:33PM +0800, Lyra Zhang wrote: > On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: > > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: > >> + if (uart_handle_sysrq_char(port, ch)) > >> + continue; > > > > My comment[1] on a previous version of this patch still stands. > > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > > yes, there are two definitions in serial_core.h, and it returns 0 when > SUPPORT_SYSRQ is not defined. > so, I think my code behavior does not hurt, right? Right. I just wanted to point out that you can easily define SUPPORT_SYSRQ as appropriate like many other serial drivers do to get sysrq support. baruch -- http://baruch.siach.name/blog/ ~. .~ Tk Open Systems =}------------------------------------------------ooO--U--Ooo------------{= - baruch@tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 9:54 ` Baruch Siach 0 siblings, 0 replies; 24+ messages in thread From: Baruch Siach @ 2015-01-28 9:54 UTC (permalink / raw) To: Lyra Zhang Cc: Chunyan Zhang, gregkh@linuxfoundation.org, Mark Rutland, gnomes@lxorguk.ukuu.org.uk, Heiko Stübner, andrew@lunn.ch, jslaby@suse.cz, lanqing.liu@spreadtrum.com, Arnd Bergmann, zhizhou.zhang, geng.ren@spreadtrum.com, antonynpavlov@gmail.com, linux-serial@vger.kernel.org, Grant Likely, Orson Zhai, florian.vaussard@epfl.ch, devicetree@vger.kernel.org, jason@lakedaemon.net, Pawel Moll, ijc+devicetree@hellion.org.uk, Hayato Suzuki, robh+dt@kernel.org, Wei Qiao (乔伟), linux-arm-kernel@lists.infradead.org, Peter Hurley, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, Kumar Gala, Shawn Guo Hi Lyra Zhang, On Wed, Jan 28, 2015 at 04:46:33PM +0800, Lyra Zhang wrote: > On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: > > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: > >> + if (uart_handle_sysrq_char(port, ch)) > >> + continue; > > > > My comment[1] on a previous version of this patch still stands. > > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > > yes, there are two definitions in serial_core.h, and it returns 0 when > SUPPORT_SYSRQ is not defined. > so, I think my code behavior does not hurt, right? Right. I just wanted to point out that you can easily define SUPPORT_SYSRQ as appropriate like many other serial drivers do to get sysrq support. baruch -- http://baruch.siach.name/blog/ ~. .~ Tk Open Systems =}------------------------------------------------ooO--U--Ooo------------{= - baruch@tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 9:54 ` Baruch Siach 0 siblings, 0 replies; 24+ messages in thread From: Baruch Siach @ 2015-01-28 9:54 UTC (permalink / raw) To: linux-arm-kernel Hi Lyra Zhang, On Wed, Jan 28, 2015 at 04:46:33PM +0800, Lyra Zhang wrote: > On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: > > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: > >> + if (uart_handle_sysrq_char(port, ch)) > >> + continue; > > > > My comment[1] on a previous version of this patch still stands. > > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. > > yes, there are two definitions in serial_core.h, and it returns 0 when > SUPPORT_SYSRQ is not defined. > so, I think my code behavior does not hurt, right? Right. I just wanted to point out that you can easily define SUPPORT_SYSRQ as appropriate like many other serial drivers do to get sysrq support. baruch -- http://baruch.siach.name/blog/ ~. .~ Tk Open Systems =}------------------------------------------------ooO--U--Ooo------------{= - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support 2015-01-28 9:54 ` Baruch Siach (?) @ 2015-01-28 10:46 ` Lyra Zhang -1 siblings, 0 replies; 24+ messages in thread From: Lyra Zhang @ 2015-01-28 10:46 UTC (permalink / raw) To: Baruch Siach Cc: Mark Rutland, andrew@lunn.ch, Heiko Stübner, gnomes@lxorguk.ukuu.org.uk, jslaby@suse.cz, lanqing.liu@spreadtrum.com, Pawel Moll, Chunyan Zhang, zhizhou.zhang, geng.ren@spreadtrum.com, antonynpavlov@gmail.com, linux-serial@vger.kernel.org, Grant Likely, Orson Zhai, florian.vaussard@epfl.ch, devicetree@vger.kernel.org, jason@lakedaemon.net, Arnd Bergmann On Wed, Jan 28, 2015 at 5:54 PM, Baruch Siach <baruch@tkos.co.il> wrote: > Hi Lyra Zhang, > > On Wed, Jan 28, 2015 at 04:46:33PM +0800, Lyra Zhang wrote: >> On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: >> > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: >> >> + if (uart_handle_sysrq_char(port, ch)) >> >> + continue; >> > >> > My comment[1] on a previous version of this patch still stands. >> > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. >> >> yes, there are two definitions in serial_core.h, and it returns 0 when >> SUPPORT_SYSRQ is not defined. >> so, I think my code behavior does not hurt, right? > > Right. I just wanted to point out that you can easily define SUPPORT_SYSRQ as > appropriate like many other serial drivers do to get sysrq support. > Ok, I got it. I'll add SUPPORT_SYSRQ definition, and send v10 to Greg. thanks, Chunyan > baruch > > -- > http://baruch.siach.name/blog/ ~. .~ Tk Open Systems > =}------------------------------------------------ooO--U--Ooo------------{= > - baruch@tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 10:46 ` Lyra Zhang 0 siblings, 0 replies; 24+ messages in thread From: Lyra Zhang @ 2015-01-28 10:46 UTC (permalink / raw) To: Baruch Siach Cc: Chunyan Zhang, gregkh@linuxfoundation.org, Mark Rutland, gnomes@lxorguk.ukuu.org.uk, Heiko Stübner, andrew@lunn.ch, jslaby@suse.cz, lanqing.liu@spreadtrum.com, Arnd Bergmann, zhizhou.zhang, geng.ren@spreadtrum.com, antonynpavlov@gmail.com, linux-serial@vger.kernel.org, Grant Likely, Orson Zhai, florian.vaussard@epfl.ch, devicetree@vger.kernel.org, jason@lakedaemon.net, Pawel Moll, ijc+devicetree@hellion.org.uk, Hayato Suzuki, robh+dt@kernel.org, Wei Qiao (乔伟), linux-arm-kernel@lists.infradead.org, Peter Hurley, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, Kumar Gala, Shawn Guo On Wed, Jan 28, 2015 at 5:54 PM, Baruch Siach <baruch@tkos.co.il> wrote: > Hi Lyra Zhang, > > On Wed, Jan 28, 2015 at 04:46:33PM +0800, Lyra Zhang wrote: >> On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: >> > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: >> >> + if (uart_handle_sysrq_char(port, ch)) >> >> + continue; >> > >> > My comment[1] on a previous version of this patch still stands. >> > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. >> >> yes, there are two definitions in serial_core.h, and it returns 0 when >> SUPPORT_SYSRQ is not defined. >> so, I think my code behavior does not hurt, right? > > Right. I just wanted to point out that you can easily define SUPPORT_SYSRQ as > appropriate like many other serial drivers do to get sysrq support. > Ok, I got it. I'll add SUPPORT_SYSRQ definition, and send v10 to Greg. thanks, Chunyan > baruch > > -- > http://baruch.siach.name/blog/ ~. .~ Tk Open Systems > =}------------------------------------------------ooO--U--Ooo------------{= > - baruch@tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support @ 2015-01-28 10:46 ` Lyra Zhang 0 siblings, 0 replies; 24+ messages in thread From: Lyra Zhang @ 2015-01-28 10:46 UTC (permalink / raw) To: linux-arm-kernel On Wed, Jan 28, 2015 at 5:54 PM, Baruch Siach <baruch@tkos.co.il> wrote: > Hi Lyra Zhang, > > On Wed, Jan 28, 2015 at 04:46:33PM +0800, Lyra Zhang wrote: >> On Wed, Jan 28, 2015 at 2:12 PM, Baruch Siach <baruch@tkos.co.il> wrote: >> > On Wed, Jan 28, 2015 at 10:47:41AM +0800, Chunyan Zhang wrote: >> >> + if (uart_handle_sysrq_char(port, ch)) >> >> + continue; >> > >> > My comment[1] on a previous version of this patch still stands. >> > uart_handle_sysrq_char is a NOP when SUPPORT_SYSRQ is not defined. >> >> yes, there are two definitions in serial_core.h, and it returns 0 when >> SUPPORT_SYSRQ is not defined. >> so, I think my code behavior does not hurt, right? > > Right. I just wanted to point out that you can easily define SUPPORT_SYSRQ as > appropriate like many other serial drivers do to get sysrq support. > Ok, I got it. I'll add SUPPORT_SYSRQ definition, and send v10 to Greg. thanks, Chunyan > baruch > > -- > http://baruch.siach.name/blog/ ~. .~ Tk Open Systems > =}------------------------------------------------ooO--U--Ooo------------{= > - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il - ^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2015-01-29 3:38 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <sc9836-serial-v9>
2015-01-28 2:47 ` [PATCH v9 0/2] Add Spreadtrum SoC bindings and serial driver support Chunyan Zhang
2015-01-28 2:47 ` Chunyan Zhang
2015-01-28 2:47 ` Chunyan Zhang
[not found] ` <1422413261-17184-1-git-send-email-chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org>
2015-01-28 2:47 ` [PATCH v9 1/2] Documentation: DT: Add bindings for Spreadtrum SoC Platform Chunyan Zhang
2015-01-28 2:47 ` Chunyan Zhang
2015-01-28 2:47 ` Chunyan Zhang
2015-01-28 2:47 ` [PATCH v9 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support Chunyan Zhang
2015-01-28 2:47 ` Chunyan Zhang
2015-01-28 2:47 ` Chunyan Zhang
[not found] ` <1422413261-17184-3-git-send-email-chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org>
2015-01-28 3:59 ` Peter Hurley
2015-01-28 3:59 ` Peter Hurley
2015-01-28 3:59 ` Peter Hurley
2015-01-28 6:12 ` Baruch Siach
2015-01-28 6:12 ` Baruch Siach
2015-01-28 6:12 ` Baruch Siach
2015-01-28 8:46 ` Lyra Zhang
2015-01-28 8:46 ` Lyra Zhang
2015-01-28 8:46 ` Lyra Zhang
2015-01-28 9:54 ` Baruch Siach
2015-01-28 9:54 ` Baruch Siach
2015-01-28 9:54 ` Baruch Siach
2015-01-28 10:46 ` Lyra Zhang
2015-01-28 10:46 ` Lyra Zhang
2015-01-28 10:46 ` Lyra Zhang
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.