Add Spreadtrum SoC bindings and serial driver support
A message that is not in the archive (sent off-list or not yet received): sprd-serial-v6
v6 cover Add Spreadtrum SoC bindings and serial driver support
This patch-set split the last version, and addressed the review comments from last version on serial driver code.
Changes from v5:
- Used Spreadtrum instead of SPRD in menus
- Changed TTY name to 'ttyS'
- Moved uart_register_driver() to probe()
- Added spinlock as needed
- Removed register states saving and restoring in suspend() and resume()
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 | 801 ++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 7 files changed, 842 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
Signature · 1 line
1.7.9.5
PATCH v6 1/2 Documentation: DT: Add bindings for Spreadtrum SoC Platform
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.
--- 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
Documentation/devicetree/bindings/arm/sprd.txt new file +11 -0
@@ -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";
Documentation/devicetree/bindings/serial/sprd-uart.txt new file +7 -0
@@ -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.
Documentation/devicetree/bindings/vendor-prefixes.txt +1 -0
@@ -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
Signature · 1 line
1.7.9.5
PATCH v6 2/2 tty/serial: Add Spreadtrum sc9836-uart driver support
In reply to Chunyan ZhangAdd a full sc9836-uart driver for SC9836 SoC which is based on the spreadtrum sharkl64 platform.
This driver also support earlycon.
This patch also replaced the spaces between the macros and their values with the tabs in serial_core.h
--- drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 801 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 823 insertions(+) create mode 100644 drivers/tty/serial/sprd_serial.c
drivers/tty/serial/Kconfig +18 -0
@@ -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
drivers/tty/serial/Makefile +1 -0
@@ -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
drivers/tty/serial/sprd_serial.c new file +801 -0
801 lines not on this page · raw message
include/uapi/linux/serial_core.h +3 -0
@@ -248,4 +248,7 @@/* MESON */ #define PORT_MESON 109 +/* SPRD SERIAL */ +#define PORT_SPRD 110 +#endif /* _UAPILINUX_SERIAL_CORE_H */
Signature · 1 line
1.7.9.5
Re: [PATCH v6 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support
Hi Chunyan,
Quoted 3 lines · Chunyan Zhang
Add a full sc9836-uart driver for SC9836 SoC which is based on the spreadtrum sharkl64 platform.
This driver also support earlycon.
Looking good. Comments below.
Quoted 2 lines
This patch also replaced the spaces between the macros and their values with the tabs in
serial_core.h
Notes about patch changes goes...
Quoted 5 lines
Originally-by Lanqing Liu <[email protected]>Signed-off-by Orson Zhai <[email protected]>Signed-off-by Chunyan Zhang <[email protected]>---
...here. For example,
* Replaced spaces with tab for PORT_SPRD define in serial_core.h
Quoted 143 lines
drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 801 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 823 insertions(+) create mode 100644 drivers/tty/serial/sprd_serial.cdrivers/tty/serial/Kconfig +18
-0@@ -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_GPIOdrivers/tty/serial/Makefile +1
-0@@ -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.odrivers/tty/serial/sprd_serial.c new file +89
-0@@ -0,0 +1,801 @@ +/* +* 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_TIMEOUT 2048 + +/* 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) + +/* 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
drivers/tty/serial/sprd_serial.c line 99
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +/* 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 { +uint32_t ien;
u32 ien;
same for others
Quoted 5 lines
drivers/tty/serial/sprd_serial.c new file +5
-0+uint32_t ctrl0; +uint32_t ctrl1; +uint32_t ctrl2; +uint32_t clkd0; +uint32_t clkd1;
drivers/tty/serial/sprd_serial.c line 114
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+uint32_t 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 ] = {NULL };
^^^^^^^^^^
Don't need the initializer.
Quoted 67 lines
drivers/tty/serial/sprd_serial.c new file +67
-0+ +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); +}
drivers/tty/serial/sprd_serial.c line 191
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +/* The Sprd serial does not support this function. */ +static void sprd_break_ctl (struct uart_port *port,int break_state) +{ +/* nothing to do */ +} + +static inline int handle_lsr_errors (struct uart_port *port, +unsigned int *flag, +unsigned int *lsr)
Don't declare this inline. gcc will inline single call site functions.
Quoted 20 lines
drivers/tty/serial/sprd_serial.c new file +20
-0+{ +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 )
drivers/tty/serial/sprd_serial.c line 221
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ *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 (int irq,void *dev_id)
^^^^^^^^^^^^ struct uart_port *port
The 'irq' parameter is unused, remove it.
Don't declare inline.
drivers/tty/serial/sprd_serial.c line 223
Quoted 2 lines
drivers/tty/serial/sprd_serial.c new file +2
-0+{ +struct uart_port *port = dev_id;
^^^^^^^^^
delete this line.
drivers/tty/serial/sprd_serial.c line 225
Quoted 2 lines
drivers/tty/serial/sprd_serial.c new file +2
-0+struct tty_port *tty = &port->state->port; +unsigned int ch, flag, lsr, max_count =SPRD_TIMEOUT ;
^^^^^^^^^
== 2048?That's a lot. Most (all?) other drivers use 256.
drivers/tty/serial/sprd_serial.c line 234
Quoted 9 lines
drivers/tty/serial/sprd_serial.c new file +9
-0+ +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 ))
operators should trail on the previous line, like
if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE | SPRD_LSR_FE | SPRD_LSR_OE))
drivers/tty/serial/sprd_serial.c line 246
Quoted 12 lines
drivers/tty/serial/sprd_serial.c new file +12
-0+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 (int irq,void *dev_id)
^^^^^^^^^^^^^ struct uart_port *port
The 'irq' parameter is unused, remove it.
Don't declare inline.
drivers/tty/serial/sprd_serial.c line 248
Quoted 2 lines
drivers/tty/serial/sprd_serial.c new file +2
-0+{ +struct uart_port *port = dev_id;
^^^^^^^^^
delete this line.
Quoted 25 lines
drivers/tty/serial/sprd_serial.c new file +25
-0+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 )
drivers/tty/serial/sprd_serial.c line 283
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+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 = (struct uart_port *)dev_id;
^^^^^^^^^^^
Don't need the type cast.
Quoted 19 lines
drivers/tty/serial/sprd_serial.c new file +19
-0+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 (irq, port); + +if (ims &SPRD_IMSR_TX_FIFO_EMPTY ) +sprd_tx (irq, port); + +spin_unlock (&port->lock);
drivers/tty/serial/sprd_serial.c line 312
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +return IRQ_HANDLED ; +} + +static int sprd_startup (struct uart_port *port) +{ +int ret =0 ; +unsigned int ien, ctrl1; +unsigned int timeout; +struct sprd_uart_port *sp;
unsigned long flags;
Quoted 5 lines
drivers/tty/serial/sprd_serial.c new file +5
-0+ +serial_out (port,SPRD_CTL2 , ((THLD_TX_EMPTY <<8 ) |THLD_RX_FULL )); + +/* clear rx fifo */ + timeout =SPRD_TIMEOUT ;
drivers/tty/serial/sprd_serial.c line 327
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+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 ,0x0 );
^^^
just 0Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+serial_out (port,SPRD_ICLR , ~0 ); + +/* allocate irq */ + sp = container_of(port, struct sprd_uart_port, port);
drivers/tty/serial/sprd_serial.c line 341
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+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; + } + ctrl1 =serial_in (port,SPRD_CTL1 ); + ctrl1 |=0x3e00 |THLD_RX_FULL ;
^^^^^
What is this magic number?
drivers/tty/serial/sprd_serial.c line 345
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+serial_out (port,SPRD_CTL1 , ctrl1); + +/* enable interrupt */ +spin_lock (&port->lock);
spin_lock_irqsave(&port->lock, flags);
drivers/tty/serial/sprd_serial.c line 349
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+ 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 (&port->lock);
spin_unlock_irqrestore(&port->lock, flags);
drivers/tty/serial/sprd_serial.c line 356
Quoted 7 lines
drivers/tty/serial/sprd_serial.c new file +7
-0+ +return 0 ; +} + +static void sprd_shutdown (struct uart_port *port) +{ +serial_out (port,SPRD_IEN ,0x0 );
^^^
just 0Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+serial_out (port,SPRD_ICLR , ~0 ); +devm_free_irq (port->dev, port->irq, port); +} +
drivers/tty/serial/sprd_serial.c line 370
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+static void sprd_set_termios (struct uart_port *port, +struct ktermios *termios, +struct ktermios *old) +{ +unsigned int baud, quot; +unsigned int lcr, fc; +unsigned long flags; + +/* ask the core to calculate the divisor for us */ + baud =uart_get_baud_rate (port, termios, old,1200 ,3000000 );
^^^^ ^^^^^^
mabye derive these from uartclk?
drivers/tty/serial/sprd_serial.c line 376
Quoted 6 lines
drivers/tty/serial/sprd_serial.c new file +6
-0+ + quot = (unsigned int )((port->uartclk + baud /2 ) / baud); + +/* set data length */ + lcr =serial_in (port,SPRD_LCR ); + lcr &= ~SPRD_LCR_DATA_LEN ;
What bits are being preserved in SPRD_LCR that you need to read the current value? IOW, why can't SPRD_LCR be defined only by the termios c_cflag? Like,
lcr = 0;
Quoted 33 lines
drivers/tty/serial/sprd_serial.c new file +33
-0+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 ; + }
drivers/tty/serial/sprd_serial.c line 419
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +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 & (BRKINT |PARMRK ))
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
Because if IGNBRK is set and a BRK is received, sprd_rx() should operate like this:
/* - RESULT - */
lsr = serial_in(SPRD_LSR) /* lsr = SPRD_LSR_BI */
ch = serial_in(SPRD_RXD) /* ch = 0 */
lsr & SPRD_LSR_BI ? yes
handle_lsr_errors(lsr, flag)
lsr &= read_status_mask /* lsr = SPRD_LSR_BI */
flag = TTY_BREAK
uart_insert_char(lsr, ch, flag)
lsr & ignore_status_mask == 0? no /* ch _not_ inserted */
Quoted 109 lines
drivers/tty/serial/sprd_serial.c new file +109
-0+ 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 |=0x3e00 |THLD_RX_FULL ; +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); +}
drivers/tty/serial/sprd_serial.c line 538
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +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 (oops_in_progress) + locked =spin_trylock (&port->lock);
if (port->sysrq) locked = 0; else if (oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags);
Quoted 114 lines
drivers/tty/serial/sprd_serial.c new file +114
-0+ 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; +static bool seen_dev_with_alias; +static bool seen_dev_without_alias; +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)) { + seen_dev_without_alias = true; + ret = index;
drivers/tty/serial/sprd_serial.c line 662
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ }else { + seen_dev_with_alias = true; +if (ret >=ARRAY_SIZE (sprd_port) || sprd_port[ret] !=NULL ) { +dev_warn (dev,"requested serial port %d not available.\n" , ret); + ret = index; + } + } + +if (seen_dev_with_alias && seen_dev_without_alias) +dev_warn (dev,"aliased and non-aliased serial devices found in device tree. Serial port enumeration may be unpredictable.\n" );
Is this necessary? Why does a user want to know this?
drivers/tty/serial/sprd_serial.c line 674
Quoted 12 lines
drivers/tty/serial/sprd_serial.c new file +12
-0+ +return ret; +} + +static int sprd_remove (struct platform_device *dev) +{ +struct sprd_uart_port *sup =platform_get_drvdata (dev); +bool busy = false; +int i; + +if (sup) +uart_remove_one_port (&sprd_uart_driver, &sup->port);
see comment in sprd_probe()
if (sup) {
uart_remove_one_port();
sprd_port[sup->line] = NULL;
sprd_ports--;
}
if (!sprd_ports)
uart_unregister_driver();Quoted 35 lines
drivers/tty/serial/sprd_serial.c new file +35
-0+ +for (i =0 ; i <ARRAY_SIZE (sprd_port); i++) +if (sprd_port[i] == sup) + sprd_port[i] =NULL ; +else if (sprd_port[i]) + busy = true; + +if (!busy) +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 ;
drivers/tty/serial/sprd_serial.c line 719
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ + 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 =ASYNC_BOOT_AUTOCONF ;
^^^^^^^^^
UPF_BOOT_AUTOCONFsparse will catch errors like this. See Documentation/sparse.txt
Quoted 13 lines
drivers/tty/serial/sprd_serial.c new file +13
-0+ + 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))
drivers/tty/serial/sprd_serial.c line 742
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+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_uart_driver.state) {
^^^^^^^^^^^^^^^^^^^^^^
I know Rob said this was ok, but it's not.
Just use a global counter.
if (!sprd_ports) {drivers/tty/serial/sprd_serial.c line 749
Quoted 7 lines
drivers/tty/serial/sprd_serial.c new file +7
-0+ ret =uart_register_driver (&sprd_uart_driver); +if (ret <0 ) { +pr_err ("Failed to register SPRD-UART driver\n" ); +return ret; + } + } +
sprd_ports++;
Quoted 5 lines
drivers/tty/serial/sprd_serial.c new file +5
-0+ ret =uart_add_one_port (&sprd_uart_driver, up); +if (ret) { + sprd_port[index] =NULL ; +sprd_remove (pdev); + }
drivers/tty/serial/sprd_serial.c line 764
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +platform_set_drvdata (pdev, up); + +return ret; +} + +static int sprd_suspend (struct device *dev) +{ +int id =to_platform_device (dev)->id; +struct uart_port *port = &sprd_port[id]->port;
I'm a little confused regarding the port indexing; is platform_device->id == line ? Where did that happen?
Quoted 50 lines
drivers/tty/serial/sprd_serial.c new file +37
-0+ +uart_suspend_port (&sprd_uart_driver, port); + +return 0 ; +} + +static int sprd_resume (struct device *dev) +{ +int id =to_platform_device (dev)->id; +struct uart_port *port = &sprd_port[id]->port; + +uart_resume_port (&sprd_uart_driver, 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" );include/uapi/linux/serial_core.h +3
-0@@ -248,4 +248,7 @@/* MESON */ #define PORT_MESON 109 +/* SPRD SERIAL */ +#define PORT_SPRD 110 +#endif /* _UAPILINUX_SERIAL_CORE_H */
Lyra Zhang
reviewerRe: [PATCH v6 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support
Hi, Peter
Many thanks to you for reviewing so carefully and giving us so many suggestions and so clear explanations.
I'll address all of your comments and send an updated patch soon.
Quoted 15 lines · Peter Hurley
Hi Chunyan,
Quoted 3 lines · Chunyan Zhang
Add a full sc9836-uart driver for SC9836 SoC which is based on the spreadtrum sharkl64 platform.
This driver also support earlycon.Looking good. Comments below.
Quoted 2 lines
This patch also replaced the spaces between the macros and their values with the tabs in
serial_core.hNotes about patch changes goes...
Quoted 1 line
Quoted 9 lines
Quoted 4 lines
Originally-by Lanqing Liu <[email protected]>Signed-off-by Orson Zhai <[email protected]>Signed-off-by Chunyan Zhang <[email protected]>---
...here. For example,
* Replaced spaces with tab for
PORT_SPRDdefine inserial_core.h
ok
Quoted 473 lines
Quoted 143 lines
drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sprd_serial.c | 801 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 823 insertions(+) create mode 100644 drivers/tty/serial/sprd_serial.cdrivers/tty/serial/Kconfig +18
-0@@ -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_GPIOdrivers/tty/serial/Makefile +1
-0@@ -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.odrivers/tty/serial/sprd_serial.c new file +89
-0@@ -0,0 +1,801 @@ +/* +* 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_TIMEOUT 2048 + +/* 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) + +/* 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 0x0028drivers/tty/serial/sprd_serial.c line 99
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +/* 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 { +uint32_t ien;u32 ien;same for others
Quoted 5 lines
drivers/tty/serial/sprd_serial.c new file +5
-0+uint32_t ctrl0; +uint32_t ctrl1; +uint32_t ctrl2; +uint32_t clkd0; +uint32_t clkd1;drivers/tty/serial/sprd_serial.c line 114
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+uint32_t 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 ] = {NULL };^^^^^^^^^^Don't need the initializer.
Quoted 67 lines
drivers/tty/serial/sprd_serial.c new file +67
-0+ +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); +}drivers/tty/serial/sprd_serial.c line 191
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +/* The Sprd serial does not support this function. */ +static void sprd_break_ctl (struct uart_port *port,int break_state) +{ +/* nothing to do */ +} + +static inline int handle_lsr_errors (struct uart_port *port, +unsigned int *flag, +unsigned int *lsr)Don't declare this inline. gcc will inline single call site functions.
Quoted 20 lines
drivers/tty/serial/sprd_serial.c new file +20
-0+{ +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 )drivers/tty/serial/sprd_serial.c line 221
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ *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 (int irq,void *dev_id)^^^^^^^^^^^^ struct
uart_port*portThe 'irq' parameter is unused, remove it.
Don't declare inline.drivers/tty/serial/sprd_serial.c line 223
Quoted 2 lines
drivers/tty/serial/sprd_serial.c new file +2
-0+{ +struct uart_port *port = dev_id;^^^^^^^^^delete this line.
drivers/tty/serial/sprd_serial.c line 225
Quoted 2 lines
drivers/tty/serial/sprd_serial.c new file +2
-0+struct tty_port *tty = &port->state->port; +unsigned int ch, flag, lsr, max_count =SPRD_TIMEOUT ;^^^^^^^^^ == 2048?That's a lot. Most (all?) other drivers use 256.
drivers/tty/serial/sprd_serial.c line 234
Quoted 9 lines
drivers/tty/serial/sprd_serial.c new file +9
-0+ +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 ))operators should trail on the previous line, like
if (lsr & (
SPRD_LSR_BI|SPRD_LSR_PE|
SPRD_LSR_FE|SPRD_LSR_OE))drivers/tty/serial/sprd_serial.c line 246
Quoted 12 lines
drivers/tty/serial/sprd_serial.c new file +12
-0+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 (int irq,void *dev_id)^^^^^^^^^^^^^ struct
uart_port*portThe 'irq' parameter is unused, remove it.
Don't declare inline.drivers/tty/serial/sprd_serial.c line 248
Quoted 2 lines
drivers/tty/serial/sprd_serial.c new file +2
-0+{ +struct uart_port *port = dev_id;^^^^^^^^^delete this line.
Quoted 25 lines
drivers/tty/serial/sprd_serial.c new file +25
-0+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 )drivers/tty/serial/sprd_serial.c line 283
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+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 = (struct uart_port *)dev_id;^^^^^^^^^^^Don't need the type cast.
Quoted 19 lines
drivers/tty/serial/sprd_serial.c new file +19
-0+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 (irq, port); + +if (ims &SPRD_IMSR_TX_FIFO_EMPTY ) +sprd_tx (irq, port); + +spin_unlock (&port->lock);drivers/tty/serial/sprd_serial.c line 312
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +return IRQ_HANDLED ; +} + +static int sprd_startup (struct uart_port *port) +{ +int ret =0 ; +unsigned int ien, ctrl1; +unsigned int timeout; +struct sprd_uart_port *sp;unsigned long flags;Quoted 5 lines
drivers/tty/serial/sprd_serial.c new file +5
-0+ +serial_out (port,SPRD_CTL2 , ((THLD_TX_EMPTY <<8 ) |THLD_RX_FULL )); + +/* clear rx fifo */ + timeout =SPRD_TIMEOUT ;drivers/tty/serial/sprd_serial.c line 327
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+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 ,0x0 );^^^ just 0Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+serial_out (port,SPRD_ICLR , ~0 ); + +/* allocate irq */ + sp = container_of(port, struct sprd_uart_port, port);drivers/tty/serial/sprd_serial.c line 341
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+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; + } + ctrl1 =serial_in (port,SPRD_CTL1 ); + ctrl1 |=0x3e00 |THLD_RX_FULL ;^^^^^What is this magic number?
drivers/tty/serial/sprd_serial.c line 345
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+serial_out (port,SPRD_CTL1 , ctrl1); + +/* enable interrupt */ +spin_lock (&port->lock);spin_lock_irqsave(&port->lock, flags);drivers/tty/serial/sprd_serial.c line 349
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+ 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 (&port->lock);spin_unlock_irqrestore(&port->lock, flags);drivers/tty/serial/sprd_serial.c line 356
Quoted 7 lines
drivers/tty/serial/sprd_serial.c new file +7
-0+ +return 0 ; +} + +static void sprd_shutdown (struct uart_port *port) +{ +serial_out (port,SPRD_IEN ,0x0 );^^^ just 0
Quoted 16 lines
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+serial_out (port,SPRD_ICLR , ~0 ); +devm_free_irq (port->dev, port->irq, port); +} +drivers/tty/serial/sprd_serial.c line 370
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+static void sprd_set_termios (struct uart_port *port, +struct ktermios *termios, +struct ktermios *old) +{ +unsigned int baud, quot; +unsigned int lcr, fc; +unsigned long flags; + +/* ask the core to calculate the divisor for us */ + baud =uart_get_baud_rate (port, termios, old,1200 ,3000000 );^^^^ ^^^^^^
mabye derive these from uartclk?
I'm afraid I can't understand very clearly, Could you explain more details please?
Quoted 12 lines
drivers/tty/serial/sprd_serial.c line 376
Quoted 7 lines
drivers/tty/serial/sprd_serial.c new file +6
-0+ + quot = (unsigned int )((port->uartclk + baud /2 ) / baud); + +/* set data length */ + lcr =serial_in (port,SPRD_LCR ); + lcr &= ~SPRD_LCR_DATA_LEN ;What bits are being preserved in
SPRD_LCRthat you need to read the current value? IOW, why can'tSPRD_LCRbe defined only by the termiosc_cflag? Like,
Quoted 2 lines
lcr = 0;
yes, I think you're right. I'll change it.
Quoted 305 lines
Quoted 33 lines
drivers/tty/serial/sprd_serial.c new file +33
-0+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 ; + }drivers/tty/serial/sprd_serial.c line 419
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +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 & (BRKINT |PARMRK ))if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))Because if IGNBRK is set and a BRK is received,
sprd_rx()should operate like this:
/* - RESULT - */
lsr =serial_in(SPRD_LSR)/* lsr =SPRD_LSR_BI*/
ch =serial_in(SPRD_RXD)/* ch = 0 */lsr &
SPRD_LSR_BI? yes
handle_lsr_errors(lsr, flag)lsr &=
read_status_mask/* lsr =SPRD_LSR_BI*/
flag =TTY_BREAK
uart_insert_char(lsr, ch, flag)
lsr &ignore_status_mask== 0? no /* ch _not_ inserted */Quoted 109 lines
drivers/tty/serial/sprd_serial.c new file +109
-0+ 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 |=0x3e00 |THLD_RX_FULL ; +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); +}drivers/tty/serial/sprd_serial.c line 538
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +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 (oops_in_progress) + locked =spin_trylock (&port->lock);if (port->sysrq) locked = 0; else if (oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags);Quoted 118 lines
drivers/tty/serial/sprd_serial.c new file +118
-0+ 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; +static bool seen_dev_with_alias; +static bool seen_dev_without_alias; +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)) { + seen_dev_without_alias = true; + ret = index; + }else { + seen_dev_with_alias = true; +if (ret >=ARRAY_SIZE (sprd_port) || sprd_port[ret] !=NULL ) { +dev_warn (dev,"requested serial port %d not available.\n" , ret);
Quoted 8 lines
drivers/tty/serial/sprd_serial.c line 662
Quoted 6 lines
drivers/tty/serial/sprd_serial.c new file +6
-0+ ret = index; + } + } + +if (seen_dev_with_alias && seen_dev_without_alias) +dev_warn (dev,"aliased and non-aliased serial devices found in device tree. Serial port enumeration may be unpredictable.\n" );Is this necessary? Why does a user want to know this?
I just followed pl011, but I think I can remove if nobody care it.
Quoted 15 lines
drivers/tty/serial/sprd_serial.c line 674
Quoted 13 lines
drivers/tty/serial/sprd_serial.c new file +12
-0+ +return ret; +} + +static int sprd_remove (struct platform_device *dev) +{ +struct sprd_uart_port *sup =platform_get_drvdata (dev); +bool busy = false; +int i; + +if (sup) +uart_remove_one_port (&sprd_uart_driver, &sup->port);see comment in
sprd_probe()
ok, I see.
Quoted 58 lines
if (sup) { uart_remove_one_port(); sprd_port[sup->line] = NULL; sprd_ports--; } if (!sprd_ports) uart_unregister_driver();Quoted 35 lines
drivers/tty/serial/sprd_serial.c new file +35
-0+ +for (i =0 ; i <ARRAY_SIZE (sprd_port); i++) +if (sprd_port[i] == sup) + sprd_port[i] =NULL ; +else if (sprd_port[i]) + busy = true; + +if (!busy) +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 ;drivers/tty/serial/sprd_serial.c line 719
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ + 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 =ASYNC_BOOT_AUTOCONF ;^^^^^^^^^ UPF_BOOT_AUTOCONF
Quoted 1 line
sparse will catch errors like this. See
Documentation/sparse.txt
you mean we should use UPF_BOOT_AUTOCONF, right?
Quoted 25 lines
Quoted 14 lines
drivers/tty/serial/sprd_serial.c new file +13
-0+ + 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))drivers/tty/serial/sprd_serial.c line 742
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+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_uart_driver.state) {^^^^^^^^^^^^^^^^^^^^^^
Quoted 3 lines
I know Rob said this was ok, but it's not.
Just use a global counter.
ok.
Quoted 23 lines
if (!sprd_ports) {drivers/tty/serial/sprd_serial.c line 749
Quoted 7 lines
drivers/tty/serial/sprd_serial.c new file +7
-0+ ret =uart_register_driver (&sprd_uart_driver); +if (ret <0 ) { +pr_err ("Failed to register SPRD-UART driver\n" ); +return ret; + } + } +sprd_ports++;Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ 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; +}
Quoted 9 lines
drivers/tty/serial/sprd_serial.c line 764
Quoted 5 lines
drivers/tty/serial/sprd_serial.c new file +5
-0+ +static int sprd_suspend (struct device *dev) +{ +int id =to_platform_device (dev)->id; +struct uart_port *port = &sprd_port[id]->port;I'm a little confused regarding the port indexing; is
platform_device->id== line ? Where did that happen?
Oh, I'll change to assign platform_device->id with port->line in probe()
Quoted 46 lines
Quoted 46 lines
drivers/tty/serial/sprd_serial.c new file +37
-0+ +uart_suspend_port (&sprd_uart_driver, port); + +return 0 ; +} + +static int sprd_resume (struct device *dev) +{ +int id =to_platform_device (dev)->id; +struct uart_port *port = &sprd_port[id]->port; + +uart_resume_port (&sprd_uart_driver, 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" );include/uapi/linux/serial_core.h +0
-0@@ -248,4 +248,7 @@/* MESON */ #define PORT_MESON 109
Quoted 6 lines
include/uapi/linux/serial_core.h line 254
Quoted 5 lines
include/uapi/linux/serial_core.h +3
-0+/* SPRD SERIAL */ +#define PORT_SPRD 110 +#endif /* _UAPILINUX_SERIAL_CORE_H */
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v6 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support
Quoted 4 lines · Lyra Zhang
Hi, Peter
Many thanks to you for reviewing so carefully and giving us so many suggestions and so clear explanations.
:)
Quoted 3 lines
I'll address all of your comments and send an updated patch soon.
On Fri, Jan 23, 2015 at 11:57 AM, Peter Hurley <[email protected]> wrote:
[...]
Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+static void sprd_set_termios (struct uart_port *port, +struct ktermios *termios, +struct ktermios *old) +{Quoted 8 lines
drivers/tty/serial/sprd_serial.c line 370
Quoted 6 lines
drivers/tty/serial/sprd_serial.c new file +6
-0+unsigned int baud, quot; +unsigned int lcr, fc; +unsigned long flags; + +/* ask the core to calculate the divisor for us */ + baud =uart_get_baud_rate (port, termios, old,1200 ,3000000 );^^^^ ^^^^^^
mabye derive these from uartclk?
Quoted 2 lines
I'm afraid I can't understand very clearly, Could you explain more details please?
Is the fixed clock divider == 8 and the uartclk == 26000000 ? If so,
baud = uartclk / 8 = 3250000
I see now this is clamping baud inside the maximum, so this is fine. Please disregard my comment.
[...]
Quoted 35 lines
Quoted 35 lines
Quoted 22 lines
drivers/tty/serial/sprd_serial.c new file +22
-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 ;drivers/tty/serial/sprd_serial.c line 719
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ + 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 =ASYNC_BOOT_AUTOCONF ;^^^^^^^^^ UPF_BOOT_AUTOCONF
Quoted 3 lines
Quoted 1 line
sparse will catch errors like this. See
Documentation/sparse.txtyou mean we should use
UPF_BOOT_AUTOCONF, right?
Yes. Only UPF_* flag definitions should be used with the uart_port.flags field.
My comment regarding the sparse tool and documentation is because the flags field and UPF_* definitions use a type mechanism to generate warnings using the sparse tool if regular integer values are used with the flags field.
The type mechanism was specifically introduced to catch using ASYNC_* definitions with the uart_port.flags field.
[...]
Quoted 10 lines
Quoted 8 lines
drivers/tty/serial/sprd_serial.c line 764
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+static int sprd_suspend (struct device *dev) +{ +int id =to_platform_device (dev)->id; +struct uart_port *port = &sprd_port[id]->port;I'm a little confused regarding the port indexing; is
platform_device->id== line ? Where did that happen?Oh, I'll change to assign
platform_device->idwithport->lineinprobe()
I apologize; I should have made my comment clearer.
The ->id should not be assigned.
Replace
int id = to_platform_device(dev)->id; struct uart_port *port = &sprd_port[id]->port; uart_suspend_port(&sprd_uart_driver, port);
with
struct sprd_uart_port *sup = dev_get_drvdata(dev); uart_suspend_port(&sprd_uart_driver, &sup->port);
I know it's not obvious but platform_get/set_drvdata() is really dev_get/set_drvdata() using the embedded struct device dev.
Quoted 14 lines
Quoted 14 lines
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +2
-0+ +uart_suspend_port (&sprd_uart_driver, port);drivers/tty/serial/sprd_serial.c line 776
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +return 0 ; +} + +static int sprd_resume (struct device *dev) +{ +int id =to_platform_device (dev)->id; +struct uart_port *port = &sprd_port[id]->port; + +uart_resume_port (&sprd_uart_driver, port);
same here
Quoted 1 line
Quoted 1 line
Quoted 1 line
+ return 0;
Lyra Zhang
reviewerRe: [PATCH v6 2/2] tty/serial: Add Spreadtrum sc9836-uart driver support
Quoted 116 lines · Peter Hurley
Quoted 4 lines · Lyra Zhang
Hi, Peter
Many thanks to you for reviewing so carefully and giving us so many suggestions and so clear explanations.
:)
Quoted 3 lines
I'll address all of your comments and send an updated patch soon.
On Fri, Jan 23, 2015 at 11:57 AM, Peter Hurley <[email protected]> wrote:
[...]
Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+static void sprd_set_termios (struct uart_port *port, +struct ktermios *termios, +struct ktermios *old) +{Quoted 8 lines
drivers/tty/serial/sprd_serial.c line 370
Quoted 6 lines
drivers/tty/serial/sprd_serial.c new file +6
-0+unsigned int baud, quot; +unsigned int lcr, fc; +unsigned long flags; + +/* ask the core to calculate the divisor for us */ + baud =uart_get_baud_rate (port, termios, old,1200 ,3000000 );^^^^ ^^^^^^
mabye derive these from uartclk?Quoted 2 lines
I'm afraid I can't understand very clearly, Could you explain more details please?
Is the fixed clock divider == 8 and the uartclk == 26000000 ? If so,
baud = uartclk / 8 = 3250000I see now this is clamping baud inside the maximum, so this is fine. Please disregard my comment.
[...]
Quoted 35 lines
Quoted 35 lines
Quoted 22 lines
drivers/tty/serial/sprd_serial.c new file +22
-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 ;drivers/tty/serial/sprd_serial.c line 719
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ + 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 =ASYNC_BOOT_AUTOCONF ;^^^^^^^^^ UPF_BOOT_AUTOCONFQuoted 3 lines
Quoted 1 line
sparse will catch errors like this. See
Documentation/sparse.txtyou mean we should use
UPF_BOOT_AUTOCONF, right?Yes. Only UPF_* flag definitions should be used with the uart_port.flags field.
My comment regarding the sparse tool and documentation is because the flags field and UPF_* definitions use a type mechanism to generate warnings using the sparse tool if regular integer values are used with the flags field.
The type mechanism was specifically introduced to catch using ASYNC_* definitions with the uart_port.flags field.
[...]
Quoted 10 lines
Quoted 8 lines
drivers/tty/serial/sprd_serial.c line 764
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +4
-0+static int sprd_suspend (struct device *dev) +{ +int id =to_platform_device (dev)->id; +struct uart_port *port = &sprd_port[id]->port;I'm a little confused regarding the port indexing; is
platform_device->id== line ? Where did that happen?Oh, I'll change to assign
platform_device->idwithport->lineinprobe()I apologize; I should have made my comment clearer.
The ->id should not be assigned.
Replace
int id = to_platform_device(dev)->id; struct uart_port *port = &sprd_port[id]->port; uart_suspend_port(&sprd_uart_driver, port);with
Quoted 8 lines
struct sprd_uart_port *sup = dev_get_drvdata(dev); uart_suspend_port(&sprd_uart_driver, &sup->port);I know it's not obvious but platform_get/set_drvdata() is really dev_get/set_drvdata() using the embedded struct device dev.
I've just sent the v7 before I saw your this email, but I'll address this point in the next version.
Thanks a lot,
Chunyan
Quoted 19 lines
Quoted 14 lines
Quoted 14 lines
Quoted 4 lines
drivers/tty/serial/sprd_serial.c new file +2
-0+ +uart_suspend_port (&sprd_uart_driver, port);drivers/tty/serial/sprd_serial.c line 776
Quoted 10 lines
drivers/tty/serial/sprd_serial.c new file +10
-0+ +return 0 ; +} + +static int sprd_resume (struct device *dev) +{ +int id =to_platform_device (dev)->id; +struct uart_port *port = &sprd_port[id]->port; + +uart_resume_port (&sprd_uart_driver, port);same here
Quoted 1 line
Quoted 1 line
Quoted 1 line
+ return 0;