2017-11-07 00:11:51 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2010-09-27 21:50:49 +07:00
|
|
|
/*
|
|
|
|
* Driver for OMAP-UART controller.
|
|
|
|
* Based on drivers/serial/8250.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 Texas Instruments.
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Govindraj R <govindraj.raja@ti.com>
|
|
|
|
* Thara Gopinath <thara@ti.com>
|
|
|
|
*
|
2011-03-31 08:57:33 +07:00
|
|
|
* Note: This driver is made separate from 8250 driver as we cannot
|
2010-09-27 21:50:49 +07:00
|
|
|
* over load 8250 driver with omap platform specific configuration for
|
|
|
|
* features like DMA, it makes easier to implement features like DMA and
|
|
|
|
* hardware flow control and software flow control configuration with
|
|
|
|
* this driver as required for the omap-platform.
|
|
|
|
*/
|
|
|
|
|
2011-02-01 14:30:41 +07:00
|
|
|
#if defined(CONFIG_SERIAL_OMAP_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
|
|
|
#define SUPPORT_SYSRQ
|
|
|
|
#endif
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/console.h>
|
|
|
|
#include <linux/serial_reg.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/tty_flip.h>
|
2012-09-06 19:45:38 +07:00
|
|
|
#include <linux/platform_device.h>
|
2010-09-27 21:50:49 +07:00
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/serial_core.h>
|
|
|
|
#include <linux/irq.h>
|
2011-02-28 19:42:23 +07:00
|
|
|
#include <linux/pm_runtime.h>
|
2015-06-10 13:35:00 +07:00
|
|
|
#include <linux/pm_wakeirq.h>
|
2011-12-14 18:55:45 +07:00
|
|
|
#include <linux/of.h>
|
2013-10-22 20:49:48 +07:00
|
|
|
#include <linux/of_irq.h>
|
2012-07-30 07:30:26 +07:00
|
|
|
#include <linux/gpio.h>
|
2013-08-14 17:29:38 +07:00
|
|
|
#include <linux/of_gpio.h>
|
2012-12-15 00:09:11 +07:00
|
|
|
#include <linux/platform_data/serial-omap.h>
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2013-08-14 17:29:38 +07:00
|
|
|
#include <dt-bindings/gpio/gpio.h>
|
|
|
|
|
2014-10-22 19:46:50 +07:00
|
|
|
#define OMAP_MAX_HSUART_PORTS 10
|
2012-10-06 16:50:58 +07:00
|
|
|
|
2012-04-03 20:42:34 +07:00
|
|
|
#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y))
|
|
|
|
|
|
|
|
#define OMAP_UART_REV_42 0x0402
|
|
|
|
#define OMAP_UART_REV_46 0x0406
|
|
|
|
#define OMAP_UART_REV_52 0x0502
|
|
|
|
#define OMAP_UART_REV_63 0x0603
|
|
|
|
|
2013-07-05 22:25:59 +07:00
|
|
|
#define OMAP_UART_TX_WAKEUP_EN BIT(7)
|
|
|
|
|
|
|
|
/* Feature flags */
|
|
|
|
#define OMAP_UART_WER_HAS_TX_WAKEUP BIT(0)
|
|
|
|
|
2012-10-06 16:50:58 +07:00
|
|
|
#define UART_ERRATA_i202_MDR1_ACCESS BIT(0)
|
|
|
|
#define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1)
|
|
|
|
|
2014-12-12 04:44:26 +07:00
|
|
|
#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz */
|
2011-12-14 18:55:44 +07:00
|
|
|
|
2012-01-26 09:50:36 +07:00
|
|
|
/* SCR register bitmasks */
|
|
|
|
#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7)
|
2013-02-05 00:19:46 +07:00
|
|
|
#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6)
|
2012-10-06 16:50:58 +07:00
|
|
|
#define OMAP_UART_SCR_TX_EMPTY (1 << 3)
|
2012-01-26 09:50:36 +07:00
|
|
|
|
|
|
|
/* FCR register bitmasks */
|
|
|
|
#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6)
|
2012-09-06 19:45:40 +07:00
|
|
|
#define OMAP_UART_FCR_TX_FIFO_TRIG_MASK (0x3 << 4)
|
2012-01-26 09:50:36 +07:00
|
|
|
|
2012-04-03 20:42:34 +07:00
|
|
|
/* MVR register bitmasks */
|
|
|
|
#define OMAP_UART_MVR_SCHEME_SHIFT 30
|
|
|
|
|
|
|
|
#define OMAP_UART_LEGACY_MVR_MAJ_MASK 0xf0
|
|
|
|
#define OMAP_UART_LEGACY_MVR_MAJ_SHIFT 4
|
|
|
|
#define OMAP_UART_LEGACY_MVR_MIN_MASK 0x0f
|
|
|
|
|
|
|
|
#define OMAP_UART_MVR_MAJ_MASK 0x700
|
|
|
|
#define OMAP_UART_MVR_MAJ_SHIFT 8
|
|
|
|
#define OMAP_UART_MVR_MIN_MASK 0x3f
|
|
|
|
|
2012-10-06 16:50:58 +07:00
|
|
|
#define OMAP_UART_DMA_CH_FREE -1
|
|
|
|
|
|
|
|
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
|
|
|
|
#define OMAP_MODE13X_SPEED 230400
|
|
|
|
|
|
|
|
/* WER = 0x7F
|
|
|
|
* Enable module level wakeup in WER reg
|
|
|
|
*/
|
2014-12-12 04:44:26 +07:00
|
|
|
#define OMAP_UART_WER_MOD_WKUP 0x7F
|
2012-10-06 16:50:58 +07:00
|
|
|
|
|
|
|
/* Enable XON/XOFF flow control on output */
|
2012-10-05 19:32:08 +07:00
|
|
|
#define OMAP_UART_SW_TX 0x08
|
2012-10-06 16:50:58 +07:00
|
|
|
|
|
|
|
/* Enable XON/XOFF flow control on input */
|
2012-10-05 19:32:08 +07:00
|
|
|
#define OMAP_UART_SW_RX 0x02
|
2012-10-06 16:50:58 +07:00
|
|
|
|
|
|
|
#define OMAP_UART_SW_CLR 0xF0
|
|
|
|
|
|
|
|
#define OMAP_UART_TCR_TRIG 0x0F
|
|
|
|
|
|
|
|
struct uart_omap_dma {
|
|
|
|
u8 uart_dma_tx;
|
|
|
|
u8 uart_dma_rx;
|
|
|
|
int rx_dma_channel;
|
|
|
|
int tx_dma_channel;
|
|
|
|
dma_addr_t rx_buf_dma_phys;
|
|
|
|
dma_addr_t tx_buf_dma_phys;
|
|
|
|
unsigned int uart_base;
|
|
|
|
/*
|
2014-12-12 04:44:26 +07:00
|
|
|
* Buffer for rx dma. It is not required for tx because the buffer
|
2012-10-06 16:50:58 +07:00
|
|
|
* comes from port structure.
|
|
|
|
*/
|
|
|
|
unsigned char *rx_buf;
|
|
|
|
unsigned int prev_rx_dma_pos;
|
|
|
|
int tx_buf_size;
|
|
|
|
int tx_dma_used;
|
|
|
|
int rx_dma_used;
|
|
|
|
spinlock_t tx_lock;
|
|
|
|
spinlock_t rx_lock;
|
|
|
|
/* timer to poll activity on rx dma */
|
|
|
|
struct timer_list rx_timer;
|
|
|
|
unsigned int rx_buf_size;
|
|
|
|
unsigned int rx_poll_rate;
|
|
|
|
unsigned int rx_timeout;
|
|
|
|
};
|
|
|
|
|
2012-09-06 19:45:39 +07:00
|
|
|
struct uart_omap_port {
|
|
|
|
struct uart_port port;
|
|
|
|
struct uart_omap_dma uart_dma;
|
|
|
|
struct device *dev;
|
2013-10-22 20:49:48 +07:00
|
|
|
int wakeirq;
|
2012-09-06 19:45:39 +07:00
|
|
|
|
|
|
|
unsigned char ier;
|
|
|
|
unsigned char lcr;
|
|
|
|
unsigned char mcr;
|
|
|
|
unsigned char fcr;
|
|
|
|
unsigned char efr;
|
|
|
|
unsigned char dll;
|
|
|
|
unsigned char dlh;
|
|
|
|
unsigned char mdr1;
|
|
|
|
unsigned char scr;
|
2013-07-05 22:25:59 +07:00
|
|
|
unsigned char wer;
|
2012-09-06 19:45:39 +07:00
|
|
|
|
|
|
|
int use_dma;
|
|
|
|
/*
|
|
|
|
* Some bits in registers are cleared on a read, so they must
|
2014-12-12 04:44:26 +07:00
|
|
|
* be saved whenever the register is read, but the bits will not
|
2012-09-06 19:45:39 +07:00
|
|
|
* be immediately processed.
|
|
|
|
*/
|
|
|
|
unsigned int lsr_break_flag;
|
|
|
|
unsigned char msr_saved_flags;
|
|
|
|
char name[20];
|
|
|
|
unsigned long port_activity;
|
2012-10-03 18:54:36 +07:00
|
|
|
int context_loss_cnt;
|
2012-09-06 19:45:39 +07:00
|
|
|
u32 errata;
|
2013-07-05 22:25:59 +07:00
|
|
|
u32 features;
|
2012-09-06 19:45:39 +07:00
|
|
|
|
2013-08-14 17:29:38 +07:00
|
|
|
int rts_gpio;
|
|
|
|
|
2012-09-06 19:45:39 +07:00
|
|
|
struct pm_qos_request pm_qos_request;
|
|
|
|
u32 latency;
|
|
|
|
u32 calc_latency;
|
|
|
|
struct work_struct qos_work;
|
2013-05-15 22:35:38 +07:00
|
|
|
bool is_suspending;
|
2012-09-06 19:45:39 +07:00
|
|
|
};
|
|
|
|
|
2013-10-24 05:49:59 +07:00
|
|
|
#define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port)))
|
2012-09-06 19:45:39 +07:00
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
|
|
|
|
|
|
|
|
/* Forward declaration of functions */
|
2011-11-07 20:30:33 +07:00
|
|
|
static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
static inline unsigned int serial_in(struct uart_omap_port *up, int offset)
|
|
|
|
{
|
|
|
|
offset <<= up->port.regshift;
|
|
|
|
return readw(up->port.membase + offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void serial_out(struct uart_omap_port *up, int offset, int value)
|
|
|
|
{
|
|
|
|
offset <<= up->port.regshift;
|
|
|
|
writew(value, up->port.membase + offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
|
|
|
|
{
|
|
|
|
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
|
|
|
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
|
|
|
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
|
|
|
|
serial_out(up, UART_FCR, 0);
|
|
|
|
}
|
|
|
|
|
2015-10-04 02:45:35 +07:00
|
|
|
#ifdef CONFIG_PM
|
2012-08-23 17:32:42 +07:00
|
|
|
static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
|
|
|
|
{
|
2013-07-30 15:06:57 +07:00
|
|
|
struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
|
2012-08-23 17:32:42 +07:00
|
|
|
|
2012-09-08 01:10:33 +07:00
|
|
|
if (!pdata || !pdata->get_context_loss_count)
|
2013-06-10 21:39:09 +07:00
|
|
|
return -EINVAL;
|
2012-08-23 17:32:42 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
return pdata->get_context_loss_count(up->dev);
|
2012-08-23 17:32:42 +07:00
|
|
|
}
|
|
|
|
|
2015-06-10 13:35:00 +07:00
|
|
|
/* REVISIT: Remove this when omap3 boots in device tree only mode */
|
2012-08-23 17:32:42 +07:00
|
|
|
static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
|
|
|
|
{
|
2013-07-30 15:06:57 +07:00
|
|
|
struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
|
2012-08-23 17:32:42 +07:00
|
|
|
|
2012-09-08 01:10:33 +07:00
|
|
|
if (!pdata || !pdata->enable_wakeup)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pdata->enable_wakeup(up->dev, enable);
|
2012-08-23 17:32:42 +07:00
|
|
|
}
|
2015-10-04 02:45:35 +07:00
|
|
|
#endif /* CONFIG_PM */
|
2012-08-23 17:32:42 +07:00
|
|
|
|
2014-09-24 14:55:22 +07:00
|
|
|
/*
|
|
|
|
* Calculate the absolute difference between the desired and actual baud
|
|
|
|
* rate for the given mode.
|
|
|
|
*/
|
|
|
|
static inline int calculate_baud_abs_diff(struct uart_port *port,
|
|
|
|
unsigned int baud, unsigned int mode)
|
|
|
|
{
|
|
|
|
unsigned int n = port->uartclk / (mode * baud);
|
|
|
|
int abs_diff;
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
n = 1;
|
|
|
|
|
|
|
|
abs_diff = baud - (port->uartclk / (mode * n));
|
|
|
|
if (abs_diff < 0)
|
|
|
|
abs_diff = -abs_diff;
|
|
|
|
|
|
|
|
return abs_diff;
|
|
|
|
}
|
|
|
|
|
OMAP/serial: Support 1Mbaud and similar baudrates that require Mode16 instead of Mode13
Original table in OMAP TRM named "UART Mode Baud Rates, Divisor
Values, and Error Rates" determines modes not for all common baud
rates. E.g. for 1000000 baud rate mode should be 16x, but according to
that table it's determined as 13x. According to current implementation
of mode divisor selection, after requesting 1000000 baudrate from
driver, later one will configure chip to use MODE13 divisor. Assuming
48Mhz as common UART clock speed, MODE13 divisor will effectively give
1230769 baudrate, what is quite far from desired 1000000 baudrate.
While with MODE16 divisor, chip will produce exact 1000000 baudrate.
In old driver that served UART devices (8250.c and serial_core.c) this
divisor could have been configured by user-space program, but in
omap_serial.c driver implementation this ability was not implemented
(afaik, by design) thus disallowing proper usage of MODE16-compatible
baudrates.
Signed-off-by: Alexey Pelykh <alexey.pelykh@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-16 17:08:06 +07:00
|
|
|
/*
|
|
|
|
* serial_omap_baud_is_mode16 - check if baud rate is MODE16X
|
|
|
|
* @port: uart port info
|
|
|
|
* @baud: baudrate for which mode needs to be determined
|
|
|
|
*
|
|
|
|
* Returns true if baud rate is MODE16X and false if MODE13X
|
|
|
|
* Original table in OMAP TRM named "UART Mode Baud Rates, Divisor Values,
|
|
|
|
* and Error Rates" determines modes not for all common baud rates.
|
|
|
|
* E.g. for 1000000 baud rate mode must be 16x, but according to that
|
|
|
|
* table it's determined as 13x.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud)
|
|
|
|
{
|
2014-09-24 14:55:22 +07:00
|
|
|
int abs_diff_13 = calculate_baud_abs_diff(port, baud, 13);
|
|
|
|
int abs_diff_16 = calculate_baud_abs_diff(port, baud, 16);
|
|
|
|
|
|
|
|
return (abs_diff_13 >= abs_diff_16);
|
OMAP/serial: Support 1Mbaud and similar baudrates that require Mode16 instead of Mode13
Original table in OMAP TRM named "UART Mode Baud Rates, Divisor
Values, and Error Rates" determines modes not for all common baud
rates. E.g. for 1000000 baud rate mode should be 16x, but according to
that table it's determined as 13x. According to current implementation
of mode divisor selection, after requesting 1000000 baudrate from
driver, later one will configure chip to use MODE13 divisor. Assuming
48Mhz as common UART clock speed, MODE13 divisor will effectively give
1230769 baudrate, what is quite far from desired 1000000 baudrate.
While with MODE16 divisor, chip will produce exact 1000000 baudrate.
In old driver that served UART devices (8250.c and serial_core.c) this
divisor could have been configured by user-space program, but in
omap_serial.c driver implementation this ability was not implemented
(afaik, by design) thus disallowing proper usage of MODE16-compatible
baudrates.
Signed-off-by: Alexey Pelykh <alexey.pelykh@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-16 17:08:06 +07:00
|
|
|
}
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
/*
|
|
|
|
* serial_omap_get_divisor - calculate divisor value
|
|
|
|
* @port: uart port info
|
|
|
|
* @baud: baudrate for which divisor needs to be calculated.
|
|
|
|
*/
|
|
|
|
static unsigned int
|
|
|
|
serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
|
|
|
|
{
|
2013-09-21 15:04:35 +07:00
|
|
|
unsigned int mode;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
OMAP/serial: Support 1Mbaud and similar baudrates that require Mode16 instead of Mode13
Original table in OMAP TRM named "UART Mode Baud Rates, Divisor
Values, and Error Rates" determines modes not for all common baud
rates. E.g. for 1000000 baud rate mode should be 16x, but according to
that table it's determined as 13x. According to current implementation
of mode divisor selection, after requesting 1000000 baudrate from
driver, later one will configure chip to use MODE13 divisor. Assuming
48Mhz as common UART clock speed, MODE13 divisor will effectively give
1230769 baudrate, what is quite far from desired 1000000 baudrate.
While with MODE16 divisor, chip will produce exact 1000000 baudrate.
In old driver that served UART devices (8250.c and serial_core.c) this
divisor could have been configured by user-space program, but in
omap_serial.c driver implementation this ability was not implemented
(afaik, by design) thus disallowing proper usage of MODE16-compatible
baudrates.
Signed-off-by: Alexey Pelykh <alexey.pelykh@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-16 17:08:06 +07:00
|
|
|
if (!serial_omap_baud_is_mode16(port, baud))
|
2013-09-21 15:04:35 +07:00
|
|
|
mode = 13;
|
2010-09-27 21:50:49 +07:00
|
|
|
else
|
2013-09-21 15:04:35 +07:00
|
|
|
mode = 16;
|
|
|
|
return port->uartclk/(mode * baud);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_enable_ms(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->port.line);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
up->ier |= UART_IER_MSI;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_stop_tx(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2013-08-14 17:29:38 +07:00
|
|
|
int res;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2013-08-14 17:29:38 +07:00
|
|
|
|
2013-10-24 05:49:58 +07:00
|
|
|
/* Handle RS-485 */
|
2014-11-07 04:46:14 +07:00
|
|
|
if (port->rs485.flags & SER_RS485_ENABLED) {
|
2013-10-24 05:49:58 +07:00
|
|
|
if (up->scr & OMAP_UART_SCR_TX_EMPTY) {
|
|
|
|
/* THR interrupt is fired when both TX FIFO and TX
|
|
|
|
* shift register are empty. This means there's nothing
|
|
|
|
* left to transmit now, so make sure the THR interrupt
|
|
|
|
* is fired when TX FIFO is below the trigger level,
|
|
|
|
* disable THR interrupts and toggle the RS-485 GPIO
|
|
|
|
* data direction pin if needed.
|
|
|
|
*/
|
|
|
|
up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
|
|
|
|
serial_out(up, UART_OMAP_SCR, up->scr);
|
2014-11-07 04:46:14 +07:00
|
|
|
res = (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) ?
|
|
|
|
1 : 0;
|
2013-08-14 17:29:38 +07:00
|
|
|
if (gpio_get_value(up->rts_gpio) != res) {
|
2014-11-07 04:46:14 +07:00
|
|
|
if (port->rs485.delay_rts_after_send > 0)
|
|
|
|
mdelay(
|
|
|
|
port->rs485.delay_rts_after_send);
|
2013-08-14 17:29:38 +07:00
|
|
|
gpio_set_value(up->rts_gpio, res);
|
|
|
|
}
|
2013-10-24 05:49:58 +07:00
|
|
|
} else {
|
|
|
|
/* We're asked to stop, but there's still stuff in the
|
|
|
|
* UART FIFO, so make sure the THR interrupt is fired
|
|
|
|
* when both TX FIFO and TX shift register are empty.
|
|
|
|
* The next THR interrupt (if no transmission is started
|
|
|
|
* in the meantime) will indicate the end of a
|
|
|
|
* transmission. Therefore we _don't_ disable THR
|
|
|
|
* interrupts in this situation.
|
|
|
|
*/
|
|
|
|
up->scr |= OMAP_UART_SCR_TX_EMPTY;
|
|
|
|
serial_out(up, UART_OMAP_SCR, up->scr);
|
|
|
|
return;
|
2013-08-14 17:29:38 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
if (up->ier & UART_IER_THRI) {
|
|
|
|
up->ier &= ~UART_IER_THRI;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
}
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2014-11-07 04:46:14 +07:00
|
|
|
if ((port->rs485.flags & SER_RS485_ENABLED) &&
|
|
|
|
!(port->rs485.flags & SER_RS485_RX_DURING_TX)) {
|
2014-03-13 20:11:47 +07:00
|
|
|
/*
|
|
|
|
* Empty the RX FIFO, we are not interested in anything
|
|
|
|
* received during the half-duplex transmission.
|
|
|
|
*/
|
|
|
|
serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_RCVR);
|
|
|
|
/* Re-enable RX interrupts */
|
2014-03-13 20:11:46 +07:00
|
|
|
up->ier |= UART_IER_RLSI | UART_IER_RDI;
|
|
|
|
up->port.read_status_mask |= UART_LSR_DR;
|
2013-08-14 17:29:38 +07:00
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
}
|
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_stop_rx(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2014-03-13 20:11:46 +07:00
|
|
|
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
|
2010-09-27 21:50:49 +07:00
|
|
|
up->port.read_status_mask &= ~UART_LSR_DR;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
2012-09-06 19:45:25 +07:00
|
|
|
static void transmit_chars(struct uart_omap_port *up, unsigned int lsr)
|
2010-09-27 21:50:49 +07:00
|
|
|
{
|
|
|
|
struct circ_buf *xmit = &up->port.state->xmit;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
if (up->port.x_char) {
|
|
|
|
serial_out(up, UART_TX, up->port.x_char);
|
|
|
|
up->port.icount.tx++;
|
|
|
|
up->port.x_char = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
|
|
|
|
serial_omap_stop_tx(&up->port);
|
|
|
|
return;
|
|
|
|
}
|
2013-08-28 06:02:18 +07:00
|
|
|
count = up->port.fifosize / 4;
|
2010-09-27 21:50:49 +07:00
|
|
|
do {
|
|
|
|
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
|
|
|
|
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
|
|
|
up->port.icount.tx++;
|
|
|
|
if (uart_circ_empty(xmit))
|
|
|
|
break;
|
|
|
|
} while (--count > 0);
|
|
|
|
|
2014-04-23 21:58:27 +07:00
|
|
|
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
2010-09-27 21:50:49 +07:00
|
|
|
uart_write_wakeup(&up->port);
|
|
|
|
|
|
|
|
if (uart_circ_empty(xmit))
|
|
|
|
serial_omap_stop_tx(&up->port);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
|
|
|
|
{
|
|
|
|
if (!(up->ier & UART_IER_THRI)) {
|
|
|
|
up->ier |= UART_IER_THRI;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_start_tx(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2013-08-14 17:29:38 +07:00
|
|
|
int res;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-06 19:45:21 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2013-08-14 17:29:38 +07:00
|
|
|
|
2013-10-24 05:49:58 +07:00
|
|
|
/* Handle RS-485 */
|
2014-11-07 04:46:14 +07:00
|
|
|
if (port->rs485.flags & SER_RS485_ENABLED) {
|
2013-10-24 05:49:58 +07:00
|
|
|
/* Fire THR interrupts when FIFO is below trigger level */
|
|
|
|
up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
|
|
|
|
serial_out(up, UART_OMAP_SCR, up->scr);
|
|
|
|
|
2013-08-14 17:29:38 +07:00
|
|
|
/* if rts not already enabled */
|
2014-11-07 04:46:14 +07:00
|
|
|
res = (port->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0;
|
2013-08-14 17:29:38 +07:00
|
|
|
if (gpio_get_value(up->rts_gpio) != res) {
|
|
|
|
gpio_set_value(up->rts_gpio, res);
|
2014-11-07 04:46:14 +07:00
|
|
|
if (port->rs485.delay_rts_before_send > 0)
|
|
|
|
mdelay(port->rs485.delay_rts_before_send);
|
2013-08-14 17:29:38 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-07 04:46:14 +07:00
|
|
|
if ((port->rs485.flags & SER_RS485_ENABLED) &&
|
|
|
|
!(port->rs485.flags & SER_RS485_RX_DURING_TX))
|
2013-08-14 17:29:38 +07:00
|
|
|
serial_omap_stop_rx(port);
|
|
|
|
|
2012-09-06 19:45:21 +07:00
|
|
|
serial_omap_enable_ier_thri(up);
|
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
2012-10-05 19:32:08 +07:00
|
|
|
static void serial_omap_throttle(struct uart_port *port)
|
|
|
|
{
|
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
pm_runtime_get_sync(up->dev);
|
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
|
|
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_unthrottle(struct uart_port *port)
|
|
|
|
{
|
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
pm_runtime_get_sync(up->dev);
|
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
|
|
up->ier |= UART_IER_RLSI | UART_IER_RDI;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
|
|
|
}
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
static unsigned int check_modem_status(struct uart_omap_port *up)
|
|
|
|
{
|
|
|
|
unsigned int status;
|
|
|
|
|
|
|
|
status = serial_in(up, UART_MSR);
|
|
|
|
status |= up->msr_saved_flags;
|
|
|
|
up->msr_saved_flags = 0;
|
|
|
|
if ((status & UART_MSR_ANY_DELTA) == 0)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
|
|
|
|
up->port.state != NULL) {
|
|
|
|
if (status & UART_MSR_TERI)
|
|
|
|
up->port.icount.rng++;
|
|
|
|
if (status & UART_MSR_DDSR)
|
|
|
|
up->port.icount.dsr++;
|
|
|
|
if (status & UART_MSR_DDCD)
|
|
|
|
uart_handle_dcd_change
|
|
|
|
(&up->port, status & UART_MSR_DCD);
|
|
|
|
if (status & UART_MSR_DCTS)
|
|
|
|
uart_handle_cts_change
|
|
|
|
(&up->port, status & UART_MSR_CTS);
|
|
|
|
wake_up_interruptible(&up->port.state->port.delta_msr_wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-09-06 19:45:24 +07:00
|
|
|
static void serial_omap_rlsi(struct uart_omap_port *up, unsigned int lsr)
|
|
|
|
{
|
|
|
|
unsigned int flag;
|
2012-09-21 21:37:19 +07:00
|
|
|
unsigned char ch = 0;
|
|
|
|
|
|
|
|
if (likely(lsr & UART_LSR_DR))
|
|
|
|
ch = serial_in(up, UART_RX);
|
2012-09-06 19:45:24 +07:00
|
|
|
|
|
|
|
up->port.icount.rx++;
|
|
|
|
flag = TTY_NORMAL;
|
|
|
|
|
|
|
|
if (lsr & UART_LSR_BI) {
|
|
|
|
flag = TTY_BREAK;
|
|
|
|
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
|
|
|
|
up->port.icount.brk++;
|
|
|
|
/*
|
|
|
|
* We do the SysRQ and SAK checking
|
|
|
|
* here because otherwise the break
|
|
|
|
* may get masked by ignore_status_mask
|
|
|
|
* or read_status_mask.
|
|
|
|
*/
|
|
|
|
if (uart_handle_break(&up->port))
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lsr & UART_LSR_PE) {
|
|
|
|
flag = TTY_PARITY;
|
|
|
|
up->port.icount.parity++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lsr & UART_LSR_FE) {
|
|
|
|
flag = TTY_FRAME;
|
|
|
|
up->port.icount.frame++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lsr & UART_LSR_OE)
|
|
|
|
up->port.icount.overrun++;
|
|
|
|
|
|
|
|
#ifdef CONFIG_SERIAL_OMAP_CONSOLE
|
|
|
|
if (up->port.line == up->port.cons->index) {
|
|
|
|
/* Recover the break flag from console xmit */
|
|
|
|
lsr |= up->lsr_break_flag;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
uart_insert_char(&up->port, lsr, UART_LSR_OE, 0, flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_rdi(struct uart_omap_port *up, unsigned int lsr)
|
|
|
|
{
|
|
|
|
unsigned char ch = 0;
|
|
|
|
unsigned int flag;
|
|
|
|
|
|
|
|
if (!(lsr & UART_LSR_DR))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ch = serial_in(up, UART_RX);
|
|
|
|
flag = TTY_NORMAL;
|
|
|
|
up->port.icount.rx++;
|
|
|
|
|
|
|
|
if (uart_handle_sysrq_char(&up->port, ch))
|
|
|
|
return;
|
|
|
|
|
|
|
|
uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
|
|
|
|
}
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
/**
|
|
|
|
* serial_omap_irq() - This handles the interrupt from one port
|
|
|
|
* @irq: uart port irq number
|
|
|
|
* @dev_id: uart port info
|
|
|
|
*/
|
2012-09-06 19:45:33 +07:00
|
|
|
static irqreturn_t serial_omap_irq(int irq, void *dev_id)
|
2010-09-27 21:50:49 +07:00
|
|
|
{
|
|
|
|
struct uart_omap_port *up = dev_id;
|
|
|
|
unsigned int iir, lsr;
|
2012-09-06 19:45:23 +07:00
|
|
|
unsigned int type;
|
2013-08-28 05:59:53 +07:00
|
|
|
irqreturn_t ret = IRQ_NONE;
|
2012-09-06 19:45:24 +07:00
|
|
|
int max_count = 256;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-06 19:45:30 +07:00
|
|
|
spin_lock(&up->port.lock);
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2012-09-06 19:45:24 +07:00
|
|
|
|
|
|
|
do {
|
2012-09-06 19:45:23 +07:00
|
|
|
iir = serial_in(up, UART_IIR);
|
2012-09-06 19:45:24 +07:00
|
|
|
if (iir & UART_IIR_NO_INT)
|
|
|
|
break;
|
|
|
|
|
2013-08-28 05:59:53 +07:00
|
|
|
ret = IRQ_HANDLED;
|
2012-09-06 19:45:24 +07:00
|
|
|
lsr = serial_in(up, UART_LSR);
|
|
|
|
|
|
|
|
/* extract IRQ type from IIR register */
|
|
|
|
type = iir & 0x3e;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case UART_IIR_MSI:
|
|
|
|
check_modem_status(up);
|
|
|
|
break;
|
|
|
|
case UART_IIR_THRI:
|
2012-09-06 19:45:25 +07:00
|
|
|
transmit_chars(up, lsr);
|
2012-09-06 19:45:24 +07:00
|
|
|
break;
|
|
|
|
case UART_IIR_RX_TIMEOUT:
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case UART_IIR_RDI:
|
|
|
|
serial_omap_rdi(up, lsr);
|
|
|
|
break;
|
|
|
|
case UART_IIR_RLSI:
|
|
|
|
serial_omap_rlsi(up, lsr);
|
|
|
|
break;
|
|
|
|
case UART_IIR_CTS_RTS_DSR:
|
|
|
|
/* simply try again */
|
|
|
|
break;
|
|
|
|
case UART_IIR_XOFF:
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-10-21 04:17:52 +07:00
|
|
|
} while (max_count--);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-06 19:45:30 +07:00
|
|
|
spin_unlock(&up->port.lock);
|
2012-09-06 19:45:24 +07:00
|
|
|
|
2013-01-03 21:53:06 +07:00
|
|
|
tty_flip_buffer_push(&up->port.state->port);
|
2012-09-06 19:45:24 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
up->port_activity = jiffies;
|
2012-09-06 19:45:23 +07:00
|
|
|
|
2013-08-28 05:59:53 +07:00
|
|
|
return ret;
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int serial_omap_tx_empty(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
unsigned long flags = 0;
|
|
|
|
unsigned int ret = 0;
|
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->port.line);
|
2010-09-27 21:50:49 +07:00
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
|
|
ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
|
|
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int serial_omap_get_mctrl(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2011-11-21 17:13:28 +07:00
|
|
|
unsigned int status;
|
2010-09-27 21:50:49 +07:00
|
|
|
unsigned int ret = 0;
|
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
status = check_modem_status(up);
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->port.line);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
if (status & UART_MSR_DCD)
|
|
|
|
ret |= TIOCM_CAR;
|
|
|
|
if (status & UART_MSR_RI)
|
|
|
|
ret |= TIOCM_RNG;
|
|
|
|
if (status & UART_MSR_DSR)
|
|
|
|
ret |= TIOCM_DSR;
|
|
|
|
if (status & UART_MSR_CTS)
|
|
|
|
ret |= TIOCM_CTS;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2015-01-26 02:44:53 +07:00
|
|
|
unsigned char mcr = 0, old_mcr, lcr;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line);
|
2010-09-27 21:50:49 +07:00
|
|
|
if (mctrl & TIOCM_RTS)
|
|
|
|
mcr |= UART_MCR_RTS;
|
|
|
|
if (mctrl & TIOCM_DTR)
|
|
|
|
mcr |= UART_MCR_DTR;
|
|
|
|
if (mctrl & TIOCM_OUT1)
|
|
|
|
mcr |= UART_MCR_OUT1;
|
|
|
|
if (mctrl & TIOCM_OUT2)
|
|
|
|
mcr |= UART_MCR_OUT2;
|
|
|
|
if (mctrl & TIOCM_LOOP)
|
|
|
|
mcr |= UART_MCR_LOOP;
|
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2012-10-05 18:23:28 +07:00
|
|
|
old_mcr = serial_in(up, UART_MCR);
|
|
|
|
old_mcr &= ~(UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_OUT1 |
|
|
|
|
UART_MCR_DTR | UART_MCR_RTS);
|
|
|
|
up->mcr = old_mcr | mcr;
|
2011-11-07 20:27:03 +07:00
|
|
|
serial_out(up, UART_MCR, up->mcr);
|
2015-01-26 02:44:53 +07:00
|
|
|
|
|
|
|
/* Turn off autoRTS if RTS is lowered; restore autoRTS if RTS raised */
|
|
|
|
lcr = serial_in(up, UART_LCR);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
|
|
|
|
up->efr |= UART_EFR_RTS;
|
|
|
|
else
|
2017-10-21 15:50:18 +07:00
|
|
|
up->efr &= ~UART_EFR_RTS;
|
2015-01-26 02:44:53 +07:00
|
|
|
serial_out(up, UART_EFR, up->efr);
|
|
|
|
serial_out(up, UART_LCR, lcr);
|
|
|
|
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_break_ctl(struct uart_port *port, int break_state)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
unsigned long flags = 0;
|
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->port.line);
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
|
|
if (break_state == -1)
|
|
|
|
up->lcr |= UART_LCR_SBC;
|
|
|
|
else
|
|
|
|
up->lcr &= ~UART_LCR_SBC;
|
|
|
|
serial_out(up, UART_LCR, up->lcr);
|
|
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int serial_omap_startup(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
unsigned long flags = 0;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate the IRQ
|
|
|
|
*/
|
|
|
|
retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags,
|
|
|
|
up->name, up);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2013-10-22 20:49:48 +07:00
|
|
|
/* Optional wake-up IRQ */
|
|
|
|
if (up->wakeirq) {
|
2015-06-10 13:35:00 +07:00
|
|
|
retval = dev_pm_set_dedicated_wake_irq(up->dev, up->wakeirq);
|
2013-10-22 20:49:48 +07:00
|
|
|
if (retval) {
|
|
|
|
free_irq(up->port.irq, up);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
/*
|
|
|
|
* Clear the FIFO buffers and disable them.
|
|
|
|
* (they will be reenabled in set_termios())
|
|
|
|
*/
|
|
|
|
serial_omap_clear_fifos(up);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the interrupt registers.
|
|
|
|
*/
|
|
|
|
(void) serial_in(up, UART_LSR);
|
|
|
|
if (serial_in(up, UART_LSR) & UART_LSR_DR)
|
|
|
|
(void) serial_in(up, UART_RX);
|
|
|
|
(void) serial_in(up, UART_IIR);
|
|
|
|
(void) serial_in(up, UART_MSR);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now, initialize the UART
|
|
|
|
*/
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_WLEN8);
|
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
|
|
/*
|
|
|
|
* Most PC uarts need OUT2 raised to enable interrupts.
|
|
|
|
*/
|
|
|
|
up->port.mctrl |= TIOCM_OUT2;
|
|
|
|
serial_omap_set_mctrl(&up->port, up->port.mctrl);
|
|
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
|
|
|
|
|
|
up->msr_saved_flags = 0;
|
|
|
|
/*
|
|
|
|
* Finally, enable interrupts. Note: Modem status interrupts
|
|
|
|
* are set via set_termios(), which will be occurring imminently
|
|
|
|
* anyway, so we don't enable them here.
|
|
|
|
*/
|
|
|
|
up->ier = UART_IER_RLSI | UART_IER_RDI;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
|
2011-01-24 22:51:22 +07:00
|
|
|
/* Enable module level wake up */
|
2013-07-05 22:25:59 +07:00
|
|
|
up->wer = OMAP_UART_WER_MOD_WKUP;
|
|
|
|
if (up->features & OMAP_UART_WER_HAS_TX_WAKEUP)
|
|
|
|
up->wer |= OMAP_UART_TX_WAKEUP_EN;
|
|
|
|
|
|
|
|
serial_out(up, UART_OMAP_WER, up->wer);
|
2011-01-24 22:51:22 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
up->port_activity = jiffies;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_shutdown(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
unsigned long flags = 0;
|
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->port.line);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
/*
|
|
|
|
* Disable interrupts from this port
|
|
|
|
*/
|
|
|
|
up->ier = 0;
|
|
|
|
serial_out(up, UART_IER, 0);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
|
|
up->port.mctrl &= ~TIOCM_OUT2;
|
|
|
|
serial_omap_set_mctrl(&up->port, up->port.mctrl);
|
|
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable break condition and FIFOs
|
|
|
|
*/
|
|
|
|
serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
|
|
|
|
serial_omap_clear_fifos(up);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read data port to reset things, and then free the irq
|
|
|
|
*/
|
|
|
|
if (serial_in(up, UART_LSR) & UART_LSR_DR)
|
|
|
|
(void) serial_in(up, UART_RX);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
free_irq(up->port.irq, up);
|
2015-06-10 13:35:00 +07:00
|
|
|
dev_pm_clear_wake_irq(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
2011-11-09 19:11:21 +07:00
|
|
|
static void serial_omap_uart_qos_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct uart_omap_port *up = container_of(work, struct uart_omap_port,
|
|
|
|
qos_work);
|
|
|
|
|
|
|
|
pm_qos_update_request(&up->pm_qos_request, up->latency);
|
|
|
|
}
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
static void
|
|
|
|
serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
|
|
|
|
struct ktermios *old)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
unsigned char cval = 0;
|
|
|
|
unsigned long flags = 0;
|
|
|
|
unsigned int baud, quot;
|
|
|
|
|
|
|
|
switch (termios->c_cflag & CSIZE) {
|
|
|
|
case CS5:
|
|
|
|
cval = UART_LCR_WLEN5;
|
|
|
|
break;
|
|
|
|
case CS6:
|
|
|
|
cval = UART_LCR_WLEN6;
|
|
|
|
break;
|
|
|
|
case CS7:
|
|
|
|
cval = UART_LCR_WLEN7;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case CS8:
|
|
|
|
cval = UART_LCR_WLEN8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (termios->c_cflag & CSTOPB)
|
|
|
|
cval |= UART_LCR_STOP;
|
|
|
|
if (termios->c_cflag & PARENB)
|
|
|
|
cval |= UART_LCR_PARITY;
|
|
|
|
if (!(termios->c_cflag & PARODD))
|
|
|
|
cval |= UART_LCR_EPAR;
|
2012-12-06 15:45:04 +07:00
|
|
|
if (termios->c_cflag & CMSPAR)
|
|
|
|
cval |= UART_LCR_SPAR;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ask the core to calculate the divisor for us.
|
|
|
|
*/
|
|
|
|
|
|
|
|
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13);
|
|
|
|
quot = serial_omap_get_divisor(port, baud);
|
|
|
|
|
2011-11-09 19:11:21 +07:00
|
|
|
/* calculate wakeup latency constraint */
|
2012-01-26 09:50:56 +07:00
|
|
|
up->calc_latency = (USEC_PER_SEC * up->port.fifosize) / (baud / 8);
|
2011-11-09 19:11:21 +07:00
|
|
|
up->latency = up->calc_latency;
|
|
|
|
schedule_work(&up->qos_work);
|
|
|
|
|
2011-11-07 20:27:03 +07:00
|
|
|
up->dll = quot & 0xff;
|
|
|
|
up->dlh = quot >> 8;
|
|
|
|
up->mdr1 = UART_OMAP_MDR1_DISABLE;
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 |
|
|
|
|
UART_FCR_ENABLE_FIFO;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, we're now changing the port state. Do it with
|
|
|
|
* interrupts disabled.
|
|
|
|
*/
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
spin_lock_irqsave(&up->port.lock, flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the per-port timeout.
|
|
|
|
*/
|
|
|
|
uart_update_timeout(port, termios->c_cflag, baud);
|
|
|
|
|
|
|
|
up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
|
|
|
|
if (termios->c_iflag & INPCK)
|
|
|
|
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
|
|
|
|
if (termios->c_iflag & (BRKINT | PARMRK))
|
|
|
|
up->port.read_status_mask |= UART_LSR_BI;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Characters to ignore
|
|
|
|
*/
|
|
|
|
up->port.ignore_status_mask = 0;
|
|
|
|
if (termios->c_iflag & IGNPAR)
|
|
|
|
up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
|
|
|
|
if (termios->c_iflag & IGNBRK) {
|
|
|
|
up->port.ignore_status_mask |= UART_LSR_BI;
|
|
|
|
/*
|
|
|
|
* If we're ignoring parity and break indicators,
|
|
|
|
* ignore overruns too (for real raw support).
|
|
|
|
*/
|
|
|
|
if (termios->c_iflag & IGNPAR)
|
|
|
|
up->port.ignore_status_mask |= UART_LSR_OE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ignore all characters if CREAD is not set
|
|
|
|
*/
|
|
|
|
if ((termios->c_cflag & CREAD) == 0)
|
|
|
|
up->port.ignore_status_mask |= UART_LSR_DR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Modem status interrupts
|
|
|
|
*/
|
|
|
|
up->ier &= ~UART_IER_MSI;
|
|
|
|
if (UART_ENABLE_MS(&up->port, termios->c_cflag))
|
|
|
|
up->ier |= UART_IER_MSI;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
serial_out(up, UART_LCR, cval); /* reset DLAB */
|
2011-11-07 20:27:03 +07:00
|
|
|
up->lcr = cval;
|
2013-02-05 00:19:46 +07:00
|
|
|
up->scr = 0;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
/* FIFOs and DMA Settings */
|
|
|
|
|
|
|
|
/* FCR can be changed only when the
|
|
|
|
* baud clock is not running
|
|
|
|
* DLL_REG and DLH_REG set to 0.
|
|
|
|
*/
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
|
2010-09-27 21:50:49 +07:00
|
|
|
serial_out(up, UART_DLL, 0);
|
|
|
|
serial_out(up, UART_DLM, 0);
|
|
|
|
serial_out(up, UART_LCR, 0);
|
|
|
|
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-10-05 19:54:53 +07:00
|
|
|
up->efr = serial_in(up, UART_EFR) & ~UART_EFR_ECB;
|
2012-10-06 06:51:17 +07:00
|
|
|
up->efr &= ~UART_EFR_SCD;
|
2010-09-27 21:50:49 +07:00
|
|
|
serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
|
|
|
|
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
|
2012-10-05 19:54:53 +07:00
|
|
|
up->mcr = serial_in(up, UART_MCR) & ~UART_MCR_TCRTLR;
|
2010-09-27 21:50:49 +07:00
|
|
|
serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
|
|
|
|
/* FIFO ENABLE, DMA MODE */
|
2012-01-26 09:50:36 +07:00
|
|
|
|
2013-04-04 01:31:46 +07:00
|
|
|
up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK;
|
|
|
|
/*
|
|
|
|
* NOTE: Setting OMAP_UART_SCR_RX_TRIG_GRANU1_MASK
|
|
|
|
* sets Enables the granularity of 1 for TRIGGER RX
|
|
|
|
* level. Along with setting RX FIFO trigger level
|
|
|
|
* to 1 (as noted below, 16 characters) and TLR[3:0]
|
|
|
|
* to zero this will result RX FIFO threshold level
|
|
|
|
* to 1 character, instead of 16 as noted in comment
|
|
|
|
* below.
|
|
|
|
*/
|
|
|
|
|
2012-09-06 19:45:40 +07:00
|
|
|
/* Set receive FIFO threshold to 16 characters and
|
2013-10-24 05:49:58 +07:00
|
|
|
* transmit FIFO threshold to 32 spaces
|
2012-09-06 19:45:40 +07:00
|
|
|
*/
|
2012-09-06 19:45:21 +07:00
|
|
|
up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
|
2012-09-06 19:45:40 +07:00
|
|
|
up->fcr &= ~OMAP_UART_FCR_TX_FIFO_TRIG_MASK;
|
|
|
|
up->fcr |= UART_FCR6_R_TRIGGER_16 | UART_FCR6_T_TRIGGER_24 |
|
|
|
|
UART_FCR_ENABLE_FIFO;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-01-26 09:50:36 +07:00
|
|
|
serial_out(up, UART_FCR, up->fcr);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
|
2011-11-07 20:27:03 +07:00
|
|
|
serial_out(up, UART_OMAP_SCR, up->scr);
|
|
|
|
|
2012-10-05 19:54:53 +07:00
|
|
|
/* Reset UART_MCR_TCRTLR: this must be done with the EFR_ECB bit set */
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
|
2010-09-27 21:50:49 +07:00
|
|
|
serial_out(up, UART_MCR, up->mcr);
|
2012-10-05 19:54:53 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
serial_out(up, UART_EFR, up->efr);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
/* Protocol, Baud Rate, and Interrupt Settings */
|
|
|
|
|
2011-11-07 20:30:33 +07:00
|
|
|
if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
|
|
|
|
serial_omap_mdr1_errataset(up, up->mdr1);
|
|
|
|
else
|
|
|
|
serial_out(up, UART_OMAP_MDR1, up->mdr1);
|
|
|
|
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2010-09-27 21:50:49 +07:00
|
|
|
serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
|
|
|
|
|
|
|
|
serial_out(up, UART_LCR, 0);
|
|
|
|
serial_out(up, UART_IER, 0);
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2011-11-07 20:27:03 +07:00
|
|
|
serial_out(up, UART_DLL, up->dll); /* LS of divisor */
|
|
|
|
serial_out(up, UART_DLM, up->dlh); /* MS of divisor */
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
serial_out(up, UART_LCR, 0);
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
serial_out(up, UART_EFR, up->efr);
|
|
|
|
serial_out(up, UART_LCR, cval);
|
|
|
|
|
OMAP/serial: Support 1Mbaud and similar baudrates that require Mode16 instead of Mode13
Original table in OMAP TRM named "UART Mode Baud Rates, Divisor
Values, and Error Rates" determines modes not for all common baud
rates. E.g. for 1000000 baud rate mode should be 16x, but according to
that table it's determined as 13x. According to current implementation
of mode divisor selection, after requesting 1000000 baudrate from
driver, later one will configure chip to use MODE13 divisor. Assuming
48Mhz as common UART clock speed, MODE13 divisor will effectively give
1230769 baudrate, what is quite far from desired 1000000 baudrate.
While with MODE16 divisor, chip will produce exact 1000000 baudrate.
In old driver that served UART devices (8250.c and serial_core.c) this
divisor could have been configured by user-space program, but in
omap_serial.c driver implementation this ability was not implemented
(afaik, by design) thus disallowing proper usage of MODE16-compatible
baudrates.
Signed-off-by: Alexey Pelykh <alexey.pelykh@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-16 17:08:06 +07:00
|
|
|
if (!serial_omap_baud_is_mode16(port, baud))
|
2011-11-07 20:27:03 +07:00
|
|
|
up->mdr1 = UART_OMAP_MDR1_13X_MODE;
|
2010-09-27 21:50:49 +07:00
|
|
|
else
|
2011-11-07 20:27:03 +07:00
|
|
|
up->mdr1 = UART_OMAP_MDR1_16X_MODE;
|
|
|
|
|
2011-11-07 20:30:33 +07:00
|
|
|
if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
|
|
|
|
serial_omap_mdr1_errataset(up, up->mdr1);
|
|
|
|
else
|
|
|
|
serial_out(up, UART_OMAP_MDR1, up->mdr1);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-10-06 15:34:36 +07:00
|
|
|
/* Configure flow control */
|
2012-10-06 15:12:44 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2012-10-06 15:34:36 +07:00
|
|
|
|
|
|
|
/* XON1/XOFF1 accessible mode B, TCRTLR=0, ECB=0 */
|
|
|
|
serial_out(up, UART_XON1, termios->c_cc[VSTART]);
|
|
|
|
serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]);
|
|
|
|
|
|
|
|
/* Enable access to TCR/TLR */
|
2012-10-06 15:12:44 +07:00
|
|
|
serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
|
|
|
|
serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-10-06 15:12:44 +07:00
|
|
|
serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core
in v3.8 with commits,
dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support")
2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support")
9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware
assisted flow control")
Since then, additional requirements for serial core support have arisen.
Specifically,
1. Separate tx and rx flow control settings for UARTs which only support
tx flow control (ie., autoCTS).
2. Disable sw-assisted CTS flow control in autoCTS mode
3. Support for RTS flow control by serial core and userspace in autoRTS mode
Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS
and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core
support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF
rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow
control does not require serial core support/intervention and can be
enabled by the uart driver when required.]
These modes must be set/reset in the driver's set_termios() method, based
on termios settings, and thus can be safely queried in any context in which
one of the port lock, port mutex or termios rwsem are held. Set these modes
in the 2 in-tree drivers, omap-serial and 8250_omap, which currently
use UPF_HARD_FLOW/UPF_SOFT_FLOW support.
Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define
UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct
and separate rx and tx flow control capabilities.
Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-26 02:44:51 +07:00
|
|
|
up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
|
|
|
|
|
2012-10-06 15:12:44 +07:00
|
|
|
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
|
2015-01-26 02:44:53 +07:00
|
|
|
/* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
|
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core
in v3.8 with commits,
dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support")
2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support")
9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware
assisted flow control")
Since then, additional requirements for serial core support have arisen.
Specifically,
1. Separate tx and rx flow control settings for UARTs which only support
tx flow control (ie., autoCTS).
2. Disable sw-assisted CTS flow control in autoCTS mode
3. Support for RTS flow control by serial core and userspace in autoRTS mode
Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS
and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core
support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF
rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow
control does not require serial core support/intervention and can be
enabled by the uart driver when required.]
These modes must be set/reset in the driver's set_termios() method, based
on termios settings, and thus can be safely queried in any context in which
one of the port lock, port mutex or termios rwsem are held. Set these modes
in the 2 in-tree drivers, omap-serial and 8250_omap, which currently
use UPF_HARD_FLOW/UPF_SOFT_FLOW support.
Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define
UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct
and separate rx and tx flow control capabilities.
Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-26 02:44:51 +07:00
|
|
|
up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
|
2015-01-26 02:44:53 +07:00
|
|
|
up->efr |= UART_EFR_CTS;
|
2012-10-06 05:48:28 +07:00
|
|
|
} else {
|
|
|
|
/* Disable AUTORTS and AUTOCTS */
|
|
|
|
up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
2012-10-15 22:50:59 +07:00
|
|
|
if (up->port.flags & UPF_SOFT_FLOW) {
|
|
|
|
/* clear SW control mode bits */
|
|
|
|
up->efr &= OMAP_UART_SW_CLR;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-10-15 22:50:59 +07:00
|
|
|
/*
|
|
|
|
* IXON Flag:
|
2012-10-05 19:32:08 +07:00
|
|
|
* Enable XON/XOFF flow control on input.
|
|
|
|
* Receiver compares XON1, XOFF1.
|
2012-10-15 22:50:59 +07:00
|
|
|
*/
|
|
|
|
if (termios->c_iflag & IXON)
|
2012-10-05 19:32:08 +07:00
|
|
|
up->efr |= OMAP_UART_SW_RX;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-10-15 22:50:59 +07:00
|
|
|
/*
|
|
|
|
* IXOFF Flag:
|
2012-10-05 19:32:08 +07:00
|
|
|
* Enable XON/XOFF flow control on output.
|
|
|
|
* Transmit XON1, XOFF1
|
2012-10-15 22:50:59 +07:00
|
|
|
*/
|
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core
in v3.8 with commits,
dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support")
2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support")
9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware
assisted flow control")
Since then, additional requirements for serial core support have arisen.
Specifically,
1. Separate tx and rx flow control settings for UARTs which only support
tx flow control (ie., autoCTS).
2. Disable sw-assisted CTS flow control in autoCTS mode
3. Support for RTS flow control by serial core and userspace in autoRTS mode
Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS
and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core
support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF
rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow
control does not require serial core support/intervention and can be
enabled by the uart driver when required.]
These modes must be set/reset in the driver's set_termios() method, based
on termios settings, and thus can be safely queried in any context in which
one of the port lock, port mutex or termios rwsem are held. Set these modes
in the 2 in-tree drivers, omap-serial and 8250_omap, which currently
use UPF_HARD_FLOW/UPF_SOFT_FLOW support.
Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define
UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct
and separate rx and tx flow control capabilities.
Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-26 02:44:51 +07:00
|
|
|
if (termios->c_iflag & IXOFF) {
|
|
|
|
up->port.status |= UPSTAT_AUTOXOFF;
|
2012-10-05 19:32:08 +07:00
|
|
|
up->efr |= OMAP_UART_SW_TX;
|
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core
in v3.8 with commits,
dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support")
2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support")
9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware
assisted flow control")
Since then, additional requirements for serial core support have arisen.
Specifically,
1. Separate tx and rx flow control settings for UARTs which only support
tx flow control (ie., autoCTS).
2. Disable sw-assisted CTS flow control in autoCTS mode
3. Support for RTS flow control by serial core and userspace in autoRTS mode
Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS
and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core
support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF
rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow
control does not require serial core support/intervention and can be
enabled by the uart driver when required.]
These modes must be set/reset in the driver's set_termios() method, based
on termios settings, and thus can be safely queried in any context in which
one of the port lock, port mutex or termios rwsem are held. Set these modes
in the 2 in-tree drivers, omap-serial and 8250_omap, which currently
use UPF_HARD_FLOW/UPF_SOFT_FLOW support.
Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define
UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct
and separate rx and tx flow control capabilities.
Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-26 02:44:51 +07:00
|
|
|
}
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-10-15 22:50:59 +07:00
|
|
|
/*
|
|
|
|
* IXANY Flag:
|
|
|
|
* Enable any character to restart output.
|
|
|
|
* Operation resumes after receiving any
|
|
|
|
* character after recognition of the XOFF character
|
|
|
|
*/
|
|
|
|
if (termios->c_iflag & IXANY)
|
|
|
|
up->mcr |= UART_MCR_XONANY;
|
|
|
|
else
|
|
|
|
up->mcr &= ~UART_MCR_XONANY;
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
2012-10-06 15:12:44 +07:00
|
|
|
serial_out(up, UART_MCR, up->mcr);
|
2012-10-06 15:08:20 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
serial_out(up, UART_EFR, up->efr);
|
|
|
|
serial_out(up, UART_LCR, up->lcr);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
serial_omap_set_mctrl(&up->port, up->port.mctrl);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
serial_omap_pm(struct uart_port *port, unsigned int state,
|
|
|
|
unsigned int oldstate)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
unsigned char efr;
|
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->port.line);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2010-09-27 21:50:49 +07:00
|
|
|
efr = serial_in(up, UART_EFR);
|
|
|
|
serial_out(up, UART_EFR, efr | UART_EFR_ECB);
|
|
|
|
serial_out(up, UART_LCR, 0);
|
|
|
|
|
|
|
|
serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
|
2010-12-01 05:11:49 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2010-09-27 21:50:49 +07:00
|
|
|
serial_out(up, UART_EFR, efr);
|
|
|
|
serial_out(up, UART_LCR, 0);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_release_port(struct uart_port *port)
|
|
|
|
{
|
|
|
|
dev_dbg(port->dev, "serial_omap_release_port+\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int serial_omap_request_port(struct uart_port *port)
|
|
|
|
{
|
|
|
|
dev_dbg(port->dev, "serial_omap_request_port+\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_config_port(struct uart_port *port, int flags)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
dev_dbg(up->port.dev, "serial_omap_config_port+%d\n",
|
2011-12-14 18:55:43 +07:00
|
|
|
up->port.line);
|
2010-09-27 21:50:49 +07:00
|
|
|
up->port.type = PORT_OMAP;
|
2012-10-05 19:32:08 +07:00
|
|
|
up->port.flags |= UPF_SOFT_FLOW | UPF_HARD_FLOW;
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser)
|
|
|
|
{
|
|
|
|
/* we don't want the core code to modify any port params */
|
|
|
|
dev_dbg(port->dev, "serial_omap_verify_port+\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
serial_omap_type(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->port.line);
|
2010-09-27 21:50:49 +07:00
|
|
|
return up->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
|
|
|
|
|
2016-01-14 03:59:23 +07:00
|
|
|
static void __maybe_unused wait_for_xmitr(struct uart_omap_port *up)
|
2010-09-27 21:50:49 +07:00
|
|
|
{
|
|
|
|
unsigned int status, tmout = 10000;
|
|
|
|
|
|
|
|
/* Wait up to 10ms for the character(s) to be sent. */
|
|
|
|
do {
|
|
|
|
status = serial_in(up, UART_LSR);
|
|
|
|
|
|
|
|
if (status & UART_LSR_BI)
|
|
|
|
up->lsr_break_flag = UART_LSR_BI;
|
|
|
|
|
|
|
|
if (--tmout == 0)
|
|
|
|
break;
|
|
|
|
udelay(1);
|
|
|
|
} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
|
|
|
|
|
|
|
|
/* Wait up to 1s for flow control if necessary */
|
|
|
|
if (up->port.flags & UPF_CONS_FLOW) {
|
|
|
|
tmout = 1000000;
|
|
|
|
for (tmout = 1000000; tmout; tmout--) {
|
|
|
|
unsigned int msr = serial_in(up, UART_MSR);
|
|
|
|
|
|
|
|
up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
|
|
|
|
if (msr & UART_MSR_CTS)
|
|
|
|
break;
|
|
|
|
|
|
|
|
udelay(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-05 22:15:10 +07:00
|
|
|
#ifdef CONFIG_CONSOLE_POLL
|
|
|
|
|
|
|
|
static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2010-12-05 22:15:10 +07:00
|
|
|
wait_for_xmitr(up);
|
|
|
|
serial_out(up, UART_TX, ch);
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-12-05 22:15:10 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int serial_omap_poll_get_char(struct uart_port *port)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2011-02-28 19:42:23 +07:00
|
|
|
unsigned int status;
|
2010-12-05 22:15:10 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2011-02-28 19:42:23 +07:00
|
|
|
status = serial_in(up, UART_LSR);
|
2012-09-06 19:45:36 +07:00
|
|
|
if (!(status & UART_LSR_DR)) {
|
|
|
|
status = NO_POLL_CHAR;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-12-05 22:15:10 +07:00
|
|
|
|
2011-02-28 19:42:23 +07:00
|
|
|
status = serial_in(up, UART_RX);
|
2012-09-06 19:45:36 +07:00
|
|
|
|
|
|
|
out:
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2012-09-06 19:45:36 +07:00
|
|
|
|
2011-02-28 19:42:23 +07:00
|
|
|
return status;
|
2010-12-05 22:15:10 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_CONSOLE_POLL */
|
|
|
|
|
|
|
|
#ifdef CONFIG_SERIAL_OMAP_CONSOLE
|
|
|
|
|
2017-01-19 16:59:38 +07:00
|
|
|
#ifdef CONFIG_SERIAL_EARLYCON
|
2017-07-18 13:02:55 +07:00
|
|
|
static unsigned int omap_serial_early_in(struct uart_port *port, int offset)
|
2017-01-19 16:59:38 +07:00
|
|
|
{
|
|
|
|
offset <<= port->regshift;
|
|
|
|
return readw(port->membase + offset);
|
|
|
|
}
|
|
|
|
|
2017-07-18 13:02:55 +07:00
|
|
|
static void omap_serial_early_out(struct uart_port *port, int offset,
|
|
|
|
int value)
|
2017-01-19 16:59:38 +07:00
|
|
|
{
|
|
|
|
offset <<= port->regshift;
|
|
|
|
writew(value, port->membase + offset);
|
|
|
|
}
|
|
|
|
|
2017-07-18 13:02:55 +07:00
|
|
|
static void omap_serial_early_putc(struct uart_port *port, int c)
|
2017-01-19 16:59:38 +07:00
|
|
|
{
|
|
|
|
unsigned int status;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
status = omap_serial_early_in(port, UART_LSR);
|
|
|
|
if ((status & BOTH_EMPTY) == BOTH_EMPTY)
|
|
|
|
break;
|
|
|
|
cpu_relax();
|
|
|
|
}
|
|
|
|
omap_serial_early_out(port, UART_TX, c);
|
|
|
|
}
|
|
|
|
|
2017-07-18 13:02:55 +07:00
|
|
|
static void early_omap_serial_write(struct console *console, const char *s,
|
|
|
|
unsigned int count)
|
2017-01-19 16:59:38 +07:00
|
|
|
{
|
|
|
|
struct earlycon_device *device = console->data;
|
|
|
|
struct uart_port *port = &device->port;
|
|
|
|
|
|
|
|
uart_console_write(port, s, count, omap_serial_early_putc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init early_omap_serial_setup(struct earlycon_device *device,
|
|
|
|
const char *options)
|
|
|
|
{
|
|
|
|
struct uart_port *port = &device->port;
|
|
|
|
|
|
|
|
if (!(device->port.membase || device->port.iobase))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
port->regshift = 2;
|
|
|
|
device->con->write = early_omap_serial_write;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
OF_EARLYCON_DECLARE(omapserial, "ti,omap2-uart", early_omap_serial_setup);
|
|
|
|
OF_EARLYCON_DECLARE(omapserial, "ti,omap3-uart", early_omap_serial_setup);
|
|
|
|
OF_EARLYCON_DECLARE(omapserial, "ti,omap4-uart", early_omap_serial_setup);
|
|
|
|
#endif /* CONFIG_SERIAL_EARLYCON */
|
|
|
|
|
2012-10-03 18:54:38 +07:00
|
|
|
static struct uart_omap_port *serial_omap_console_ports[OMAP_MAX_HSUART_PORTS];
|
2010-12-05 22:15:10 +07:00
|
|
|
|
|
|
|
static struct uart_driver serial_omap_reg;
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
static void serial_omap_console_putchar(struct uart_port *port, int ch)
|
|
|
|
{
|
2012-08-23 17:32:41 +07:00
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
wait_for_xmitr(up);
|
|
|
|
serial_out(up, UART_TX, ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
serial_omap_console_write(struct console *co, const char *s,
|
|
|
|
unsigned int count)
|
|
|
|
{
|
|
|
|
struct uart_omap_port *up = serial_omap_console_ports[co->index];
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned int ier;
|
|
|
|
int locked = 1;
|
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
local_irq_save(flags);
|
|
|
|
if (up->port.sysrq)
|
|
|
|
locked = 0;
|
|
|
|
else if (oops_in_progress)
|
|
|
|
locked = spin_trylock(&up->port.lock);
|
|
|
|
else
|
|
|
|
spin_lock(&up->port.lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First save the IER then disable the interrupts
|
|
|
|
*/
|
|
|
|
ier = serial_in(up, UART_IER);
|
|
|
|
serial_out(up, UART_IER, 0);
|
|
|
|
|
|
|
|
uart_console_write(&up->port, s, count, serial_omap_console_putchar);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, wait for transmitter to become empty
|
|
|
|
* and restore the IER
|
|
|
|
*/
|
|
|
|
wait_for_xmitr(up);
|
|
|
|
serial_out(up, UART_IER, ier);
|
|
|
|
/*
|
|
|
|
* The receive handling will happen properly because the
|
|
|
|
* receive ready bit will still be set; it is not cleared
|
|
|
|
* on read. However, modem control will not, we must
|
|
|
|
* call it if we have saved something in the saved flags
|
|
|
|
* while processing with interrupts off.
|
|
|
|
*/
|
|
|
|
if (up->msr_saved_flags)
|
|
|
|
check_modem_status(up);
|
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
if (locked)
|
|
|
|
spin_unlock(&up->port.lock);
|
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init
|
|
|
|
serial_omap_console_setup(struct console *co, char *options)
|
|
|
|
{
|
|
|
|
struct uart_omap_port *up;
|
|
|
|
int baud = 115200;
|
|
|
|
int bits = 8;
|
|
|
|
int parity = 'n';
|
|
|
|
int flow = 'n';
|
|
|
|
|
|
|
|
if (serial_omap_console_ports[co->index] == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
up = serial_omap_console_ports[co->index];
|
|
|
|
|
|
|
|
if (options)
|
|
|
|
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
|
|
|
|
|
|
|
return uart_set_options(&up->port, co, baud, parity, bits, flow);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct console serial_omap_console = {
|
|
|
|
.name = OMAP_SERIAL_NAME,
|
|
|
|
.write = serial_omap_console_write,
|
|
|
|
.device = uart_console_device,
|
|
|
|
.setup = serial_omap_console_setup,
|
|
|
|
.flags = CON_PRINTBUFFER,
|
|
|
|
.index = -1,
|
|
|
|
.data = &serial_omap_reg,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void serial_omap_add_console_port(struct uart_omap_port *up)
|
|
|
|
{
|
2011-12-14 18:55:43 +07:00
|
|
|
serial_omap_console_ports[up->port.line] = up;
|
2010-09-27 21:50:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define OMAP_CONSOLE (&serial_omap_console)
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#define OMAP_CONSOLE NULL
|
|
|
|
|
|
|
|
static inline void serial_omap_add_console_port(struct uart_omap_port *up)
|
|
|
|
{}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2013-08-14 17:29:38 +07:00
|
|
|
/* Enable or disable the rs485 support */
|
2014-11-07 04:46:14 +07:00
|
|
|
static int
|
2016-01-13 06:14:46 +07:00
|
|
|
serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485)
|
2013-08-14 17:29:38 +07:00
|
|
|
{
|
|
|
|
struct uart_omap_port *up = to_uart_omap_port(port);
|
|
|
|
unsigned int mode;
|
|
|
|
int val;
|
|
|
|
|
|
|
|
pm_runtime_get_sync(up->dev);
|
|
|
|
|
|
|
|
/* Disable interrupts from this port */
|
|
|
|
mode = up->ier;
|
|
|
|
up->ier = 0;
|
|
|
|
serial_out(up, UART_IER, 0);
|
|
|
|
|
2016-01-13 06:14:46 +07:00
|
|
|
/* Clamp the delays to [0, 100ms] */
|
|
|
|
rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U);
|
|
|
|
rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U);
|
|
|
|
|
2013-08-14 17:29:38 +07:00
|
|
|
/* store new config */
|
2016-01-13 06:14:46 +07:00
|
|
|
port->rs485 = *rs485;
|
2013-08-14 17:29:38 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Just as a precaution, only allow rs485
|
|
|
|
* to be enabled if the gpio pin is valid
|
|
|
|
*/
|
|
|
|
if (gpio_is_valid(up->rts_gpio)) {
|
|
|
|
/* enable / disable rts */
|
2014-11-07 04:46:14 +07:00
|
|
|
val = (port->rs485.flags & SER_RS485_ENABLED) ?
|
2013-08-14 17:29:38 +07:00
|
|
|
SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND;
|
2014-11-07 04:46:14 +07:00
|
|
|
val = (port->rs485.flags & val) ? 1 : 0;
|
2013-08-14 17:29:38 +07:00
|
|
|
gpio_set_value(up->rts_gpio, val);
|
|
|
|
} else
|
2014-11-07 04:46:14 +07:00
|
|
|
port->rs485.flags &= ~SER_RS485_ENABLED;
|
2013-08-14 17:29:38 +07:00
|
|
|
|
|
|
|
/* Enable interrupts */
|
|
|
|
up->ier = mode;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
|
2013-10-24 05:49:58 +07:00
|
|
|
/* If RS-485 is disabled, make sure the THR interrupt is fired when
|
|
|
|
* TX FIFO is below the trigger level.
|
|
|
|
*/
|
2014-11-07 04:46:14 +07:00
|
|
|
if (!(port->rs485.flags & SER_RS485_ENABLED) &&
|
2013-10-24 05:49:58 +07:00
|
|
|
(up->scr & OMAP_UART_SCR_TX_EMPTY)) {
|
|
|
|
up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
|
|
|
|
serial_out(up, UART_OMAP_SCR, up->scr);
|
|
|
|
}
|
|
|
|
|
2013-08-14 17:29:38 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-26 00:48:52 +07:00
|
|
|
static const struct uart_ops serial_omap_pops = {
|
2010-09-27 21:50:49 +07:00
|
|
|
.tx_empty = serial_omap_tx_empty,
|
|
|
|
.set_mctrl = serial_omap_set_mctrl,
|
|
|
|
.get_mctrl = serial_omap_get_mctrl,
|
|
|
|
.stop_tx = serial_omap_stop_tx,
|
|
|
|
.start_tx = serial_omap_start_tx,
|
2012-10-05 19:32:08 +07:00
|
|
|
.throttle = serial_omap_throttle,
|
|
|
|
.unthrottle = serial_omap_unthrottle,
|
2010-09-27 21:50:49 +07:00
|
|
|
.stop_rx = serial_omap_stop_rx,
|
|
|
|
.enable_ms = serial_omap_enable_ms,
|
|
|
|
.break_ctl = serial_omap_break_ctl,
|
|
|
|
.startup = serial_omap_startup,
|
|
|
|
.shutdown = serial_omap_shutdown,
|
|
|
|
.set_termios = serial_omap_set_termios,
|
|
|
|
.pm = serial_omap_pm,
|
|
|
|
.type = serial_omap_type,
|
|
|
|
.release_port = serial_omap_release_port,
|
|
|
|
.request_port = serial_omap_request_port,
|
|
|
|
.config_port = serial_omap_config_port,
|
|
|
|
.verify_port = serial_omap_verify_port,
|
2010-12-05 22:15:10 +07:00
|
|
|
#ifdef CONFIG_CONSOLE_POLL
|
|
|
|
.poll_put_char = serial_omap_poll_put_char,
|
|
|
|
.poll_get_char = serial_omap_poll_get_char,
|
|
|
|
#endif
|
2010-09-27 21:50:49 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct uart_driver serial_omap_reg = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.driver_name = "OMAP-SERIAL",
|
|
|
|
.dev_name = OMAP_SERIAL_NAME,
|
|
|
|
.nr = OMAP_MAX_HSUART_PORTS,
|
|
|
|
.cons = OMAP_CONSOLE,
|
|
|
|
};
|
|
|
|
|
2012-01-16 17:22:36 +07:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2013-05-15 22:35:38 +07:00
|
|
|
static int serial_omap_prepare(struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_omap_port *up = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
up->is_suspending = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void serial_omap_complete(struct device *dev)
|
|
|
|
{
|
|
|
|
struct uart_omap_port *up = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
up->is_suspending = false;
|
|
|
|
}
|
|
|
|
|
2011-02-28 19:42:23 +07:00
|
|
|
static int serial_omap_suspend(struct device *dev)
|
2010-09-27 21:50:49 +07:00
|
|
|
{
|
2011-02-28 19:42:23 +07:00
|
|
|
struct uart_omap_port *up = dev_get_drvdata(dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-18 18:35:54 +07:00
|
|
|
uart_suspend_port(&serial_omap_reg, &up->port);
|
2012-10-02 23:54:49 +07:00
|
|
|
flush_work(&up->qos_work);
|
2011-11-09 19:11:21 +07:00
|
|
|
|
2014-03-26 01:48:47 +07:00
|
|
|
if (device_may_wakeup(dev))
|
|
|
|
serial_omap_enable_wakeup(up, true);
|
|
|
|
else
|
|
|
|
serial_omap_enable_wakeup(up, false);
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-28 19:42:23 +07:00
|
|
|
static int serial_omap_resume(struct device *dev)
|
2010-09-27 21:50:49 +07:00
|
|
|
{
|
2011-02-28 19:42:23 +07:00
|
|
|
struct uart_omap_port *up = dev_get_drvdata(dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2014-03-26 01:48:47 +07:00
|
|
|
if (device_may_wakeup(dev))
|
|
|
|
serial_omap_enable_wakeup(up, false);
|
|
|
|
|
2012-09-18 18:35:54 +07:00
|
|
|
uart_resume_port(&serial_omap_reg, &up->port);
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2013-05-15 22:35:38 +07:00
|
|
|
#else
|
|
|
|
#define serial_omap_prepare NULL
|
2013-06-01 16:18:13 +07:00
|
|
|
#define serial_omap_complete NULL
|
2013-05-15 22:35:38 +07:00
|
|
|
#endif /* CONFIG_PM_SLEEP */
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-11-20 01:21:50 +07:00
|
|
|
static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
|
2012-04-03 20:42:34 +07:00
|
|
|
{
|
|
|
|
u32 mvr, scheme;
|
|
|
|
u16 revision, major, minor;
|
|
|
|
|
2013-07-08 14:28:57 +07:00
|
|
|
mvr = readl(up->port.membase + (UART_OMAP_MVER << up->port.regshift));
|
2012-04-03 20:42:34 +07:00
|
|
|
|
|
|
|
/* Check revision register scheme */
|
|
|
|
scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
|
|
|
|
|
|
|
|
switch (scheme) {
|
|
|
|
case 0: /* Legacy Scheme: OMAP2/3 */
|
|
|
|
/* MINOR_REV[0:4], MAJOR_REV[4:7] */
|
|
|
|
major = (mvr & OMAP_UART_LEGACY_MVR_MAJ_MASK) >>
|
|
|
|
OMAP_UART_LEGACY_MVR_MAJ_SHIFT;
|
|
|
|
minor = (mvr & OMAP_UART_LEGACY_MVR_MIN_MASK);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* New Scheme: OMAP4+ */
|
|
|
|
/* MINOR_REV[0:5], MAJOR_REV[8:10] */
|
|
|
|
major = (mvr & OMAP_UART_MVR_MAJ_MASK) >>
|
|
|
|
OMAP_UART_MVR_MAJ_SHIFT;
|
|
|
|
minor = (mvr & OMAP_UART_MVR_MIN_MASK);
|
|
|
|
break;
|
|
|
|
default:
|
2012-09-06 19:45:20 +07:00
|
|
|
dev_warn(up->dev,
|
2012-04-03 20:42:34 +07:00
|
|
|
"Unknown %s revision, defaulting to highest\n",
|
|
|
|
up->name);
|
|
|
|
/* highest possible revision */
|
|
|
|
major = 0xff;
|
|
|
|
minor = 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* normalize revision for the driver */
|
|
|
|
revision = UART_BUILD_REVISION(major, minor);
|
|
|
|
|
|
|
|
switch (revision) {
|
|
|
|
case OMAP_UART_REV_46:
|
|
|
|
up->errata |= (UART_ERRATA_i202_MDR1_ACCESS |
|
|
|
|
UART_ERRATA_i291_DMA_FORCEIDLE);
|
|
|
|
break;
|
|
|
|
case OMAP_UART_REV_52:
|
|
|
|
up->errata |= (UART_ERRATA_i202_MDR1_ACCESS |
|
|
|
|
UART_ERRATA_i291_DMA_FORCEIDLE);
|
2013-07-05 22:25:59 +07:00
|
|
|
up->features |= OMAP_UART_WER_HAS_TX_WAKEUP;
|
2012-04-03 20:42:34 +07:00
|
|
|
break;
|
|
|
|
case OMAP_UART_REV_63:
|
|
|
|
up->errata |= UART_ERRATA_i202_MDR1_ACCESS;
|
2013-07-05 22:25:59 +07:00
|
|
|
up->features |= OMAP_UART_WER_HAS_TX_WAKEUP;
|
2012-04-03 20:42:34 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-20 01:21:50 +07:00
|
|
|
static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
|
2011-12-14 18:55:45 +07:00
|
|
|
{
|
|
|
|
struct omap_uart_port_info *omap_up_info;
|
|
|
|
|
|
|
|
omap_up_info = devm_kzalloc(dev, sizeof(*omap_up_info), GFP_KERNEL);
|
|
|
|
if (!omap_up_info)
|
|
|
|
return NULL; /* out of memory */
|
|
|
|
|
|
|
|
of_property_read_u32(dev->of_node, "clock-frequency",
|
|
|
|
&omap_up_info->uartclk);
|
2017-03-28 22:59:30 +07:00
|
|
|
|
|
|
|
omap_up_info->flags = UPF_BOOT_AUTOCONF;
|
|
|
|
|
2011-12-14 18:55:45 +07:00
|
|
|
return omap_up_info;
|
|
|
|
}
|
|
|
|
|
2013-08-14 17:29:38 +07:00
|
|
|
static int serial_omap_probe_rs485(struct uart_omap_port *up,
|
|
|
|
struct device_node *np)
|
|
|
|
{
|
2014-11-07 04:46:14 +07:00
|
|
|
struct serial_rs485 *rs485conf = &up->port.rs485;
|
2013-08-14 17:29:38 +07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
rs485conf->flags = 0;
|
|
|
|
up->rts_gpio = -EINVAL;
|
|
|
|
|
|
|
|
if (!np)
|
|
|
|
return 0;
|
|
|
|
|
2017-11-25 05:26:40 +07:00
|
|
|
uart_get_rs485_mode(up->dev, rs485conf);
|
|
|
|
|
2017-11-25 05:26:40 +07:00
|
|
|
if (of_property_read_bool(np, "rs485-rts-active-high")) {
|
2013-08-14 17:29:38 +07:00
|
|
|
rs485conf->flags |= SER_RS485_RTS_ON_SEND;
|
2017-11-25 05:26:40 +07:00
|
|
|
rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
|
|
|
|
} else {
|
|
|
|
rs485conf->flags &= ~SER_RS485_RTS_ON_SEND;
|
2013-08-14 17:29:38 +07:00
|
|
|
rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
|
2017-11-25 05:26:40 +07:00
|
|
|
}
|
2013-08-14 17:29:38 +07:00
|
|
|
|
|
|
|
/* check for tx enable gpio */
|
2017-12-21 18:55:30 +07:00
|
|
|
up->rts_gpio = of_get_named_gpio(np, "rts-gpio", 0);
|
2013-08-14 17:29:38 +07:00
|
|
|
if (gpio_is_valid(up->rts_gpio)) {
|
2014-04-23 21:58:30 +07:00
|
|
|
ret = devm_gpio_request(up->dev, up->rts_gpio, "omap-serial");
|
2013-08-14 17:29:38 +07:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2017-12-21 18:55:30 +07:00
|
|
|
ret = rs485conf->flags & SER_RS485_RTS_AFTER_SEND ? 1 : 0;
|
|
|
|
ret = gpio_direction_output(up->rts_gpio, ret);
|
2013-08-14 17:29:38 +07:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2014-02-13 16:52:03 +07:00
|
|
|
} else if (up->rts_gpio == -EPROBE_DEFER) {
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
} else {
|
2013-08-14 17:29:38 +07:00
|
|
|
up->rts_gpio = -EINVAL;
|
2014-02-13 16:52:03 +07:00
|
|
|
}
|
2013-08-14 17:29:38 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-20 01:21:50 +07:00
|
|
|
static int serial_omap_probe(struct platform_device *pdev)
|
2010-09-27 21:50:49 +07:00
|
|
|
{
|
2013-07-30 15:06:57 +07:00
|
|
|
struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev);
|
2014-04-23 21:58:31 +07:00
|
|
|
struct uart_omap_port *up;
|
|
|
|
struct resource *mem;
|
2014-04-23 21:58:33 +07:00
|
|
|
void __iomem *base;
|
2014-04-23 21:58:31 +07:00
|
|
|
int uartirq = 0;
|
|
|
|
int wakeirq = 0;
|
|
|
|
int ret;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2013-10-22 20:49:48 +07:00
|
|
|
/* The optional wakeirq may be specified in the board dts file */
|
2013-07-08 14:25:43 +07:00
|
|
|
if (pdev->dev.of_node) {
|
2013-10-22 20:49:48 +07:00
|
|
|
uartirq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
|
|
|
if (!uartirq)
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1);
|
2011-12-14 18:55:45 +07:00
|
|
|
omap_up_info = of_get_uart_port_info(&pdev->dev);
|
2013-07-08 14:25:43 +07:00
|
|
|
pdev->dev.platform_data = omap_up_info;
|
2013-10-22 20:49:48 +07:00
|
|
|
} else {
|
2014-04-23 21:58:32 +07:00
|
|
|
uartirq = platform_get_irq(pdev, 0);
|
|
|
|
if (uartirq < 0)
|
|
|
|
return -EPROBE_DEFER;
|
2013-07-08 14:25:43 +07:00
|
|
|
}
|
2011-12-14 18:55:45 +07:00
|
|
|
|
2014-04-23 21:58:33 +07:00
|
|
|
up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
|
|
|
|
if (!up)
|
|
|
|
return -ENOMEM;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2014-04-23 21:58:33 +07:00
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
base = devm_ioremap_resource(&pdev->dev, mem);
|
|
|
|
if (IS_ERR(base))
|
|
|
|
return PTR_ERR(base);
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-06 19:45:20 +07:00
|
|
|
up->dev = &pdev->dev;
|
2010-09-27 21:50:49 +07:00
|
|
|
up->port.dev = &pdev->dev;
|
|
|
|
up->port.type = PORT_OMAP;
|
|
|
|
up->port.iotype = UPIO_MEM;
|
2013-10-22 20:49:48 +07:00
|
|
|
up->port.irq = uartirq;
|
2010-09-27 21:50:49 +07:00
|
|
|
up->port.regshift = 2;
|
|
|
|
up->port.fifosize = 64;
|
|
|
|
up->port.ops = &serial_omap_pops;
|
|
|
|
|
2011-12-14 18:55:45 +07:00
|
|
|
if (pdev->dev.of_node)
|
2014-11-12 16:28:34 +07:00
|
|
|
ret = of_alias_get_id(pdev->dev.of_node, "serial");
|
2011-12-14 18:55:45 +07:00
|
|
|
else
|
2014-11-12 16:28:34 +07:00
|
|
|
ret = pdev->id;
|
2011-12-14 18:55:45 +07:00
|
|
|
|
2014-11-12 16:28:34 +07:00
|
|
|
if (ret < 0) {
|
2011-12-14 18:55:45 +07:00
|
|
|
dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n",
|
2014-11-12 16:28:34 +07:00
|
|
|
ret);
|
2012-03-21 18:52:22 +07:00
|
|
|
goto err_port_line;
|
2011-12-14 18:55:45 +07:00
|
|
|
}
|
2014-11-12 16:28:34 +07:00
|
|
|
up->port.line = ret;
|
2011-12-14 18:55:45 +07:00
|
|
|
|
2014-10-22 19:46:50 +07:00
|
|
|
if (up->port.line >= OMAP_MAX_HSUART_PORTS) {
|
|
|
|
dev_err(&pdev->dev, "uart ID %d > MAX %d.\n", up->port.line,
|
|
|
|
OMAP_MAX_HSUART_PORTS);
|
|
|
|
ret = -ENXIO;
|
|
|
|
goto err_port_line;
|
|
|
|
}
|
|
|
|
|
tty/serial: omap: fix !wakeirq message
When wakeirq is not used/enabled, "no wakeirq for uart0" is output for
all TTY as the log message is generated before the port line is
initialized.
[ 0.802656] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
[ 0.811700] omap_uart 44e09000.serial: no wakeirq for uart0
[ 0.812379] 44e09000.serial: ttyO0 at MMIO 0x44e09000 (irq = 88, base_baud = 3000000) is a OMAP UART0
[ 1.503622] console [ttyO0] enabled
[ 1.509836] omap_uart 48022000.serial: no wakeirq for uart0
[ 1.516118] 48022000.serial: ttyO1 at MMIO 0x48022000 (irq = 89, base_baud = 3000000) is a OMAP UART1
[ 1.527711] omap_uart 48024000.serial: no wakeirq for uart0
[ 1.533881] 48024000.serial: ttyO2 at MMIO 0x48024000 (irq = 90, base_baud = 3000000) is a OMAP UART2
[ 1.545324] omap_uart 481a6000.serial: no wakeirq for uart0
[ 1.551410] 481a6000.serial: ttyO3 at MMIO 0x481a6000 (irq = 60, base_baud = 3000000) is a OMAP UART3
[ 1.562946] omap_uart 481a8000.serial: no wakeirq for uart0
[ 1.569036] 481a8000.serial: ttyO4 at MMIO 0x481a8000 (irq = 61, base_baud = 3000000) is a OMAP UART4
Fix by moving wakeirq initialization, check, and dev_info() call to
after port line initialization and validation.
Signed-off-by: Doug Kehn <rdkehn@yahoo.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-03-24 20:19:27 +07:00
|
|
|
up->wakeirq = wakeirq;
|
|
|
|
if (!up->wakeirq)
|
|
|
|
dev_info(up->port.dev, "no wakeirq for uart%d\n",
|
|
|
|
up->port.line);
|
|
|
|
|
2013-08-14 17:29:38 +07:00
|
|
|
ret = serial_omap_probe_rs485(up, pdev->dev.of_node);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err_rs485;
|
|
|
|
|
2011-12-14 18:55:45 +07:00
|
|
|
sprintf(up->name, "OMAP UART%d", up->port.line);
|
2011-10-11 16:25:41 +07:00
|
|
|
up->port.mapbase = mem->start;
|
2014-04-23 21:58:33 +07:00
|
|
|
up->port.membase = base;
|
2010-09-27 21:50:49 +07:00
|
|
|
up->port.flags = omap_up_info->flags;
|
|
|
|
up->port.uartclk = omap_up_info->uartclk;
|
2014-11-07 04:46:14 +07:00
|
|
|
up->port.rs485_config = serial_omap_config_rs485;
|
2011-12-14 18:55:44 +07:00
|
|
|
if (!up->port.uartclk) {
|
|
|
|
up->port.uartclk = DEFAULT_CLK_SPEED;
|
2013-10-24 05:49:59 +07:00
|
|
|
dev_warn(&pdev->dev,
|
2013-10-31 20:39:58 +07:00
|
|
|
"No clock speed specified: using default: %d\n",
|
2013-10-24 05:49:59 +07:00
|
|
|
DEFAULT_CLK_SPEED);
|
2011-12-14 18:55:44 +07:00
|
|
|
}
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2011-11-09 19:11:21 +07:00
|
|
|
up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
|
|
|
|
up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
|
|
|
|
pm_qos_add_request(&up->pm_qos_request,
|
|
|
|
PM_QOS_CPU_DMA_LATENCY, up->latency);
|
|
|
|
INIT_WORK(&up->qos_work, serial_omap_uart_qos_work);
|
|
|
|
|
2012-09-06 19:45:27 +07:00
|
|
|
platform_set_drvdata(pdev, up);
|
2013-06-10 21:39:09 +07:00
|
|
|
if (omap_up_info->autosuspend_timeout == 0)
|
|
|
|
omap_up_info->autosuspend_timeout = -1;
|
2014-04-23 21:58:29 +07:00
|
|
|
|
2013-06-10 21:39:09 +07:00
|
|
|
device_init_wakeup(up->dev, true);
|
2011-02-28 19:42:23 +07:00
|
|
|
pm_runtime_use_autosuspend(&pdev->dev);
|
|
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev,
|
2011-11-09 19:03:38 +07:00
|
|
|
omap_up_info->autosuspend_timeout);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
|
|
|
pm_runtime_irq_safe(&pdev->dev);
|
2013-07-22 17:01:15 +07:00
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
|
2011-02-28 19:42:23 +07:00
|
|
|
pm_runtime_get_sync(&pdev->dev);
|
|
|
|
|
2012-04-03 20:42:34 +07:00
|
|
|
omap_serial_fill_features_erratas(up);
|
|
|
|
|
2011-12-14 18:55:43 +07:00
|
|
|
ui[up->port.line] = up;
|
2010-09-27 21:50:49 +07:00
|
|
|
serial_omap_add_console_port(up);
|
|
|
|
|
|
|
|
ret = uart_add_one_port(&serial_omap_reg, &up->port);
|
|
|
|
if (ret != 0)
|
2012-03-21 18:52:22 +07:00
|
|
|
goto err_add_port;
|
2010-09-27 21:50:49 +07:00
|
|
|
|
2012-09-06 19:45:26 +07:00
|
|
|
pm_runtime_mark_last_busy(up->dev);
|
|
|
|
pm_runtime_put_autosuspend(up->dev);
|
2010-09-27 21:50:49 +07:00
|
|
|
return 0;
|
2012-03-21 18:52:22 +07:00
|
|
|
|
|
|
|
err_add_port:
|
2017-04-10 16:21:39 +07:00
|
|
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
2012-03-21 18:52:22 +07:00
|
|
|
pm_runtime_disable(&pdev->dev);
|
2015-04-30 22:35:27 +07:00
|
|
|
pm_qos_remove_request(&up->pm_qos_request);
|
|
|
|
device_init_wakeup(up->dev, false);
|
2013-08-14 17:29:38 +07:00
|
|
|
err_rs485:
|
2012-03-21 18:52:22 +07:00
|
|
|
err_port_line:
|
2010-09-27 21:50:49 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-11-20 01:26:18 +07:00
|
|
|
static int serial_omap_remove(struct platform_device *dev)
|
2010-09-27 21:50:49 +07:00
|
|
|
{
|
|
|
|
struct uart_omap_port *up = platform_get_drvdata(dev);
|
|
|
|
|
2017-04-10 16:21:38 +07:00
|
|
|
pm_runtime_get_sync(up->dev);
|
|
|
|
|
|
|
|
uart_remove_one_port(&serial_omap_reg, &up->port);
|
|
|
|
|
|
|
|
pm_runtime_dont_use_autosuspend(up->dev);
|
2012-09-06 19:45:29 +07:00
|
|
|
pm_runtime_put_sync(up->dev);
|
2012-09-06 19:45:28 +07:00
|
|
|
pm_runtime_disable(up->dev);
|
|
|
|
pm_qos_remove_request(&up->pm_qos_request);
|
2014-03-21 15:25:10 +07:00
|
|
|
device_init_wakeup(&dev->dev, false);
|
2011-02-28 19:42:23 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-07 20:30:33 +07:00
|
|
|
/*
|
|
|
|
* Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
|
|
|
|
* The access to uart register after MDR1 Access
|
|
|
|
* causes UART to corrupt data.
|
|
|
|
*
|
|
|
|
* Need a delay =
|
|
|
|
* 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS)
|
|
|
|
* give 10 times as much
|
|
|
|
*/
|
|
|
|
static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1)
|
|
|
|
{
|
|
|
|
u8 timeout = 255;
|
|
|
|
|
|
|
|
serial_out(up, UART_OMAP_MDR1, mdr1);
|
|
|
|
udelay(2);
|
|
|
|
serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_XMIT |
|
|
|
|
UART_FCR_CLEAR_RCVR);
|
|
|
|
/*
|
|
|
|
* Wait for FIFO to empty: when empty, RX_FIFO_E bit is 0 and
|
|
|
|
* TX_FIFO_E bit is 1.
|
|
|
|
*/
|
|
|
|
while (UART_LSR_THRE != (serial_in(up, UART_LSR) &
|
|
|
|
(UART_LSR_THRE | UART_LSR_DR))) {
|
|
|
|
timeout--;
|
|
|
|
if (!timeout) {
|
|
|
|
/* Should *never* happen. we warn and carry on */
|
2012-09-06 19:45:20 +07:00
|
|
|
dev_crit(up->dev, "Errata i202: timedout %x\n",
|
2011-11-07 20:30:33 +07:00
|
|
|
serial_in(up, UART_LSR));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
udelay(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-13 06:41:36 +07:00
|
|
|
#ifdef CONFIG_PM
|
2011-11-07 20:26:12 +07:00
|
|
|
static void serial_omap_restore_context(struct uart_omap_port *up)
|
|
|
|
{
|
2011-11-07 20:30:33 +07:00
|
|
|
if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
|
|
|
|
serial_omap_mdr1_errataset(up, UART_OMAP_MDR1_DISABLE);
|
|
|
|
else
|
|
|
|
serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE);
|
|
|
|
|
2011-11-07 20:26:12 +07:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
|
|
|
|
serial_out(up, UART_EFR, UART_EFR_ECB);
|
|
|
|
serial_out(up, UART_LCR, 0x0); /* Operational mode */
|
|
|
|
serial_out(up, UART_IER, 0x0);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
|
2011-11-07 20:27:03 +07:00
|
|
|
serial_out(up, UART_DLL, up->dll);
|
|
|
|
serial_out(up, UART_DLM, up->dlh);
|
2011-11-07 20:26:12 +07:00
|
|
|
serial_out(up, UART_LCR, 0x0); /* Operational mode */
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
serial_out(up, UART_FCR, up->fcr);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
|
|
|
|
serial_out(up, UART_MCR, up->mcr);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
|
2011-11-07 20:27:03 +07:00
|
|
|
serial_out(up, UART_OMAP_SCR, up->scr);
|
2011-11-07 20:26:12 +07:00
|
|
|
serial_out(up, UART_EFR, up->efr);
|
|
|
|
serial_out(up, UART_LCR, up->lcr);
|
2011-11-07 20:30:33 +07:00
|
|
|
if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
|
|
|
|
serial_omap_mdr1_errataset(up, up->mdr1);
|
|
|
|
else
|
|
|
|
serial_out(up, UART_OMAP_MDR1, up->mdr1);
|
2013-07-05 22:25:59 +07:00
|
|
|
serial_out(up, UART_OMAP_WER, up->wer);
|
2011-11-07 20:26:12 +07:00
|
|
|
}
|
|
|
|
|
2011-02-28 19:42:23 +07:00
|
|
|
static int serial_omap_runtime_suspend(struct device *dev)
|
|
|
|
{
|
2011-10-11 20:41:27 +07:00
|
|
|
struct uart_omap_port *up = dev_get_drvdata(dev);
|
|
|
|
|
2013-06-05 09:04:49 +07:00
|
|
|
if (!up)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-05-15 22:35:38 +07:00
|
|
|
/*
|
|
|
|
* When using 'no_console_suspend', the console UART must not be
|
|
|
|
* suspended. Since driver suspend is managed by runtime suspend,
|
|
|
|
* preventing runtime suspend (by returning error) will keep device
|
|
|
|
* active during suspend.
|
|
|
|
*/
|
|
|
|
if (up->is_suspending && !console_suspend_enabled &&
|
|
|
|
uart_console(&up->port))
|
|
|
|
return -EBUSY;
|
|
|
|
|
2012-08-23 17:32:42 +07:00
|
|
|
up->context_loss_cnt = serial_omap_get_context_loss_count(up);
|
2011-10-11 20:41:27 +07:00
|
|
|
|
2014-03-26 01:48:47 +07:00
|
|
|
serial_omap_enable_wakeup(up, true);
|
2011-10-13 15:41:09 +07:00
|
|
|
|
2011-11-09 19:11:21 +07:00
|
|
|
up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
|
|
|
|
schedule_work(&up->qos_work);
|
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-28 19:42:23 +07:00
|
|
|
static int serial_omap_runtime_resume(struct device *dev)
|
|
|
|
{
|
2011-11-07 20:26:12 +07:00
|
|
|
struct uart_omap_port *up = dev_get_drvdata(dev);
|
|
|
|
|
2012-10-03 18:54:36 +07:00
|
|
|
int loss_cnt = serial_omap_get_context_loss_count(up);
|
2011-10-11 20:41:27 +07:00
|
|
|
|
2014-03-26 01:48:47 +07:00
|
|
|
serial_omap_enable_wakeup(up, false);
|
|
|
|
|
2012-10-03 18:54:36 +07:00
|
|
|
if (loss_cnt < 0) {
|
2013-06-10 21:39:09 +07:00
|
|
|
dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n",
|
2012-10-03 18:54:36 +07:00
|
|
|
loss_cnt);
|
2012-09-18 18:35:54 +07:00
|
|
|
serial_omap_restore_context(up);
|
2012-10-03 18:54:36 +07:00
|
|
|
} else if (up->context_loss_cnt != loss_cnt) {
|
|
|
|
serial_omap_restore_context(up);
|
|
|
|
}
|
2012-09-18 18:35:54 +07:00
|
|
|
up->latency = up->calc_latency;
|
|
|
|
schedule_work(&up->qos_work);
|
2011-11-07 20:26:12 +07:00
|
|
|
|
2010-09-27 21:50:49 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2011-02-28 19:42:23 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static const struct dev_pm_ops serial_omap_dev_pm_ops = {
|
|
|
|
SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume)
|
|
|
|
SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend,
|
|
|
|
serial_omap_runtime_resume, NULL)
|
2013-05-15 22:35:38 +07:00
|
|
|
.prepare = serial_omap_prepare,
|
|
|
|
.complete = serial_omap_complete,
|
2011-02-28 19:42:23 +07:00
|
|
|
};
|
|
|
|
|
2011-12-14 18:55:45 +07:00
|
|
|
#if defined(CONFIG_OF)
|
|
|
|
static const struct of_device_id omap_serial_of_match[] = {
|
|
|
|
{ .compatible = "ti,omap2-uart" },
|
|
|
|
{ .compatible = "ti,omap3-uart" },
|
|
|
|
{ .compatible = "ti,omap4-uart" },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, omap_serial_of_match);
|
|
|
|
#endif
|
2010-09-27 21:50:49 +07:00
|
|
|
|
|
|
|
static struct platform_driver serial_omap_driver = {
|
|
|
|
.probe = serial_omap_probe,
|
2012-11-20 01:21:34 +07:00
|
|
|
.remove = serial_omap_remove,
|
2010-09-27 21:50:49 +07:00
|
|
|
.driver = {
|
2016-01-21 15:46:12 +07:00
|
|
|
.name = OMAP_SERIAL_DRIVER_NAME,
|
2011-02-28 19:42:23 +07:00
|
|
|
.pm = &serial_omap_dev_pm_ops,
|
2011-12-14 18:55:45 +07:00
|
|
|
.of_match_table = of_match_ptr(omap_serial_of_match),
|
2010-09-27 21:50:49 +07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init serial_omap_init(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = uart_register_driver(&serial_omap_reg);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
ret = platform_driver_register(&serial_omap_driver);
|
|
|
|
if (ret != 0)
|
|
|
|
uart_unregister_driver(&serial_omap_reg);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit serial_omap_exit(void)
|
|
|
|
{
|
|
|
|
platform_driver_unregister(&serial_omap_driver);
|
|
|
|
uart_unregister_driver(&serial_omap_reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(serial_omap_init);
|
|
|
|
module_exit(serial_omap_exit);
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("OMAP High Speed UART driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Texas Instruments Inc");
|