aboutsummaryrefslogtreecommitdiffstats
path: root/tty/u6715-8250-serial-like-driver.patch
diff options
Diffstat (limited to 'tty/u6715-8250-serial-like-driver.patch')
-rw-r--r--tty/u6715-8250-serial-like-driver.patch340
1 files changed, 340 insertions, 0 deletions
diff --git a/tty/u6715-8250-serial-like-driver.patch b/tty/u6715-8250-serial-like-driver.patch
new file mode 100644
index 00000000000000..a6a45bd69f9ee5
--- /dev/null
+++ b/tty/u6715-8250-serial-like-driver.patch
@@ -0,0 +1,340 @@
+From philippe.langlais@stericsson.com Thu Jul 22 15:53:24 2010
+From: Philippe Langlais <philippe.langlais@stericsson.com>
+To: <linux-kernel@vger.kernel.org>
+Cc: <gregkh@suse.de>, <ludovic.barre@stericsson.com>,
+ Philippe Langlais <philippe.langlais@stericsson.com>
+Subject: U6715 8250 serial like driver
+Date: Mon, 19 Jul 2010 11:27:40 +0200
+Message-ID: <1279531660-16317-1-git-send-email-philippe.langlais@stericsson.com>
+
+UART Features extract from STEricsson U6715 datasheet (arm926 SoC for mobile phone):
+* Fully compatible with industry standard 16C550 and 16C450 from various
+manufacturers
+* RX and TX 64 byte FIFO reduces CPU interrupts
+* Full double buffering
+* Modem control signals include CTS, RTS, (and DSR, DTR on UART1 only)
+* Automatic baud rate selection
+* Manual or automatic RTS/CTS smart hardware flow control
+* Programmable serial characteristics:
+– Baud rate generation (50 to 3.25M baud)
+– 5, 6, 7 or 8-bit characters
+– Even, odd or no-parity bit generation and detection
+– 1, 1.5 or 2 stop bit generation
+* Independent control of transmit, receive, line status, data set interrupts and FIFOs
+* Full status-reporting capabilities
+* Separate DMA signalling for RX and TX
+* Timed interrupt to spread receive interrupt on known duration
+* DMA time-out interrupt to allow detection of end of reception
+* Carkit pulse coding and decoding compliant with USB carkit control interface [40]
+
+This UART IP is auto-detected as a 16550A type
+
+Clock specificities:
+ It's parent clock depend on baud rate.
+ The UART port can be used before u6xxx clock framework initialization
+
+Signed-off-by: Philippe Langlais <philippe.langlais@stericsson.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/serial/8250.c | 26 ++++++
+ drivers/serial/8250_u6.c | 179 ++++++++++++++++++++++++++++++++++++++++++++
+ drivers/serial/Kconfig | 19 ++++
+ drivers/serial/Makefile | 1
+ include/linux/serial_8250.h | 8 +
+ 5 files changed, 232 insertions(+), 1 deletion(-)
+
+--- a/drivers/serial/8250.c
++++ b/drivers/serial/8250.c
+@@ -199,10 +199,16 @@ static const struct serial8250_config ua
+ },
+ [PORT_16550A] = {
+ .name = "16550A",
++#if defined(CONFIG_SERIAL_8250_U6XXX)
++ .fifo_size = 64,
++ .tx_loadsz = 64,
++ .flags = UART_CAP_FIFO | UART_CAP_AFE,
++#else
+ .fifo_size = 16,
+ .tx_loadsz = 16,
+- .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ .flags = UART_CAP_FIFO,
++#endif
++ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ },
+ [PORT_CIRRUS] = {
+ .name = "Cirrus",
+@@ -2268,6 +2274,13 @@ serial8250_set_termios(struct uart_port
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
++#ifdef CONFIG_SERIAL_8250_CUSTOM_CLOCK
++ baud = uart_get_baud_rate(port, termios, old, 0,
++ CONFIG_SERIAL_8250_CUSTOM_MAX_BAUDRATE);
++ /* Calculate the new uart clock frequency if it is tunable */
++ port->uartclk = serial8250_get_custom_clock(port, baud);
++#endif
++
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+ port->uartclk / 16);
+@@ -2298,6 +2311,13 @@ serial8250_set_termios(struct uart_port
+ up->mcr &= ~UART_MCR_AFE;
+ if (termios->c_cflag & CRTSCTS)
+ up->mcr |= UART_MCR_AFE;
++#if defined(CONFIG_SERIAL_8250_U6XXX)
++ /**
++ * When AFE is active, let the HW handle the stop/restart TX
++ * upon CTS change. It reacts much quicker than the SW driver.
++ */
++ port->flags &= ~ASYNC_CTS_FLOW;
++#endif
+ }
+
+ /*
+@@ -2383,6 +2403,10 @@ serial8250_set_termios(struct uart_port
+ serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+ }
+
++#ifdef CONFIG_SERIAL_8250_CUSTOM_CLOCK
++ /* set the new uart clock frequency if it is tunable */
++ serial8250_set_custom_clock(port);
++#endif
+ serial_dl_write(up, quot);
+
+ /*
+--- /dev/null
++++ b/drivers/serial/8250_u6.c
+@@ -0,0 +1,179 @@
++/*
++ * linux/drivers/serial/8250_pnx.c
++ *
++ * Copyright (C) ST-Ericsson SA 2010
++ * Author: Ludovic Barre <ludovic.barre@stericsson.com> for ST-Ericsson.
++ * License terms: GNU General Public License (GPL), version 2
++ */
++
++#include <linux/serial_core.h>
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++
++#include <mach/serial.h>
++#include <mach/hardware.h>
++#include <mach/clock.h>
++
++/* Register description for FDIV_CTRL */
++/* UART FDIV_CTRL Register (8 bits) */
++#define UARTX_FDIV_CTRL_OFFSET 0xC00
++/* UART FDIV_M Register (16 bits) */
++#define UARTX_FDIV_M_OFFSET 0xC04
++/* UART FDIV_N Register (16 bits) */
++#define UARTX_FDIV_N_OFFSET 0xC08
++
++/* Bits definition for register UARTX_FDIV_CTRL */
++#define UARTX_FDIV_ENABLE_SHIFT 7
++#define UARTX_FDIV_ENABLE_FIELD (0xFFFFFFFF - (0x1UL<<UARTX_FDIV_ENABLE_SHIFT))
++#define UARTX_FDIV_ENABLE_OFF (0x0UL<<UARTX_FDIV_ENABLE_SHIFT)
++#define UARTX_FDIV_ENABLE_ON (0x1UL<<UARTX_FDIV_ENABLE_SHIFT)
++#define UARTX_FDIV_ENABLE (0x1UL<<UARTX_FDIV_ENABLE_SHIFT)
++#define UARTX_CLKSEL_SHIFT 0
++#define UARTX_CLKSEL_FIELD (0xFFFFFFFF - (0x3UL<<UARTX_CLKSEL_SHIFT))
++#define UARTX_CLKSEL_PCLK (0x0UL<<UARTX_CLKSEL_SHIFT)
++#define UARTX_CLKSEL_13M (0x1UL<<UARTX_CLKSEL_SHIFT)
++#define UARTX_CLKSEL_26M (0x2UL<<UARTX_CLKSEL_SHIFT)
++#define UARTX_CLKSEL_3 (0x3UL<<UARTX_CLKSEL_SHIFT)
++
++/*
++ * console and pctools has needed to start before serial_init
++ * (with cgu interface)
++ */
++static int uart_enable_clock(struct uart_port *port)
++{
++ u32 v;
++ v = readl(CGU_GATESC1_REG);
++
++ if (port->irq == IRQ_UART1)
++ v |= CGU_UART1EN_1;
++ else if (port->irq == IRQ_UART2)
++ v |= CGU_UART2EN_1;
++
++ writel(v, CGU_GATESC1_REG);
++
++ return 0;
++}
++
++static int uart_disable_clock(struct uart_port *port)
++{
++ u32 v;
++ v = readl(CGU_GATESC1_REG);
++
++ if (port->irq == IRQ_UART1)
++ v &= ~CGU_UART1EN_0;
++ else if (port->irq == IRQ_UART2)
++ v &= ~CGU_UART2EN_0;
++
++ writel(v, CGU_GATESC1_REG);
++
++ return 0;
++}
++
++unsigned int serial8250_enable_clock(struct uart_port *port)
++{
++ struct u6_uart *uart_u6 = port->private_data;
++
++ if (!uart_u6)
++ return uart_enable_clock(port);
++
++ if (IS_ERR(uart_u6->uartClk)) {
++ printk(KERN_WARNING "%s - uart clock failed error:%ld\n",
++ __func__, PTR_ERR(uart_u6->uartClk));
++ return PTR_ERR(uart_u6->uartClk);
++ }
++
++ if (clk_get_usecount(uart_u6->uartClk) == 0)
++ clk_enable(uart_u6->uartClk);
++ return 0;
++}
++
++unsigned int serial8250_disable_clock(struct uart_port *port)
++{
++ struct u6_uart *uart_u6 = port->private_data;
++
++ if (!uart_u6)
++ return uart_disable_clock(port);
++
++ if (IS_ERR(uart_u6->uartClk)) {
++ printk(KERN_WARNING "%s - uart clk error :%ld\n", __func__,
++ PTR_ERR(uart_u6->uartClk));
++ return PTR_ERR(uart_u6->uartClk);
++ }
++ if (clk_get_usecount(uart_u6->uartClk) >= 1)
++ clk_disable(uart_u6->uartClk);
++
++ return 0;
++}
++
++unsigned int serial8250_get_custom_clock(struct uart_port *port,
++ unsigned int baud)
++{
++ switch (baud) {
++ case 3250000:
++ return 52000000;
++ case 2000000:
++ return 32000000;
++ case 1843200:
++ return 29491200;
++ case 921600:
++ return 14745600;
++ default:
++ return 7372800;
++ }
++}
++
++void serial8250_set_custom_clock(struct uart_port *port)
++{
++ u32 fdiv_m = 0x5F37;
++ u32 fdiv_n = 0x3600;
++ u32 fdiv_ctrl = UARTX_FDIV_ENABLE_ON;
++ struct u6_uart *uart_u6 = port->private_data;
++
++ switch (port->uartclk) {
++ case 7372800: /* clk=13MHz */
++ fdiv_ctrl |= UARTX_CLKSEL_13M;
++ break;
++ case 14745600: /* clk=26MHz */
++ fdiv_ctrl |= UARTX_CLKSEL_26M;
++ break;
++ case 29491200: /* clk=pclk */
++ fdiv_ctrl |= UARTX_CLKSEL_PCLK;
++ break;
++ case 32000000: /* clk=pclk */
++ fdiv_n = 0x3A98;
++ fdiv_ctrl |= UARTX_CLKSEL_PCLK;
++ break;
++ case 52000000: /* clk=pclk */
++ fdiv_n = 0x5F37;
++ fdiv_ctrl |= UARTX_CLKSEL_PCLK;
++ break;
++ }
++
++ if (uart_u6 != NULL && !IS_ERR(uart_u6->uartClk)) {
++ /* if cgu interface is ready and u6_serial_init */
++ struct clk *parentClk;
++
++ if (fdiv_ctrl & UARTX_CLKSEL_26M)
++ parentClk = clk_get(NULL, "clk26m_ck");
++ else if (fdiv_ctrl & UARTX_CLKSEL_PCLK)
++ parentClk = clk_get(NULL, "pclk2_ck");
++ else
++ parentClk = clk_get(NULL, "clk13m_ck");
++
++ if (!IS_ERR(parentClk)) {
++ serial8250_disable_clock(port);
++
++ if (clk_set_parent(uart_u6->uartClk, parentClk) != 0)
++ printk(KERN_WARNING "%s: set parent failed\n", __func__);
++
++ serial8250_enable_clock(port);
++ clk_put(parentClk);
++ }
++ }
++
++ writel(fdiv_m, port->membase + UARTX_FDIV_M_OFFSET);
++ writel(fdiv_n, port->membase + UARTX_FDIV_N_OFFSET);
++ writel(fdiv_ctrl, port->membase + UARTX_FDIV_CTRL_OFFSET);
++}
+--- a/drivers/serial/Kconfig
++++ b/drivers/serial/Kconfig
+@@ -163,6 +163,25 @@ config SERIAL_8250_MANY_PORTS
+ say N here to save some memory. You can also say Y if you have an
+ "intelligent" multiport card such as Cyclades, Digiboards, etc.
+
++config SERIAL_8250_U6XXX
++ bool
++ depends on SERIAL_8250_EXTENDED && PLAT_U6XXX
++ default y
++
++config SERIAL_8250_CUSTOM_CLOCK
++ bool "Support serial ports with tunable input clock frequency"
++ depends on SERIAL_8250_EXTENDED && SERIAL_8250_U6XXX
++ default y
++ help
++ Say Y here if your platform has specific registers to change UART clock frequency.
++
++config SERIAL_8250_CUSTOM_MAX_BAUDRATE
++ int "Maximal reachable baudrate"
++ depends on SERIAL_8250_CUSTOM_CLOCK
++ default "3250000"
++ help
++ The value of the maximal reachable baudrate when tuning UART clock frequency (default value: 3.25MBds).
++
+ #
+ # Multi-port serial cards
+ #
+--- a/drivers/serial/Makefile
++++ b/drivers/serial/Makefile
+@@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_b
+ obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
+ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
+ obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
++obj-$(CONFIG_SERIAL_8250_U6XXX) += 8250_u6.o
+ obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
+ obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
+ obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
+--- a/include/linux/serial_8250.h
++++ b/include/linux/serial_8250.h
+@@ -72,4 +72,12 @@ extern int serial8250_find_port(struct u
+ extern int serial8250_find_port_for_earlycon(void);
+ extern int setup_early_serial8250_console(char *cmdline);
+
++#ifdef CONFIG_SERIAL_8250_CUSTOM_CLOCK
++unsigned int serial8250_enable_clock(struct uart_port *port);
++unsigned int serial8250_disable_clock(struct uart_port *port);
++unsigned int serial8250_get_custom_clock(struct uart_port *port,
++ unsigned int baud);
++void serial8250_set_custom_clock(struct uart_port *port);
++#endif
++
+ #endif