Merge tag 'tty-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial updates from Greg KH: "Here is the "big" set of tty/serial driver patches for 5.2-rc1. It's really pretty small, not much happening in this portion of the kernel at the moment. When the "highlight" is the movement of the documentation from .txt to .rst files, it's a good merge window. There's a number of small fixes and updates over the various serial drivers, and a new "tty null" driver for those embedded systems that like to make things even smaller and not break things. All of these have been in linux-next for a while with no reported issues" * tag 'tty-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (45 commits) tty: serial: add driver for the SiFive UART dt-bindings: serial: add documentation for the SiFive UART driver serial: uartps: Add support for cts-override dt-bindings: xilinx-uartps: Add support for cts-override serial: milbeaut_usio: Fix error handling in probe and remove tty: rocket: deprecate the rp_ioctl tty: rocket: Remove RCPK_GET_STRUCT ioctl tty: update obsolete termios comment tty: serial_core: fix error code returned by uart_register_driver() serial: 8250-mtk: modify baudrate setting serial: 8250-mtk: add follow control docs: serial: convert docs to ReST and rename to *.rst serial: 8250_exar: Adjust IOT2000 matching TTY: serial_core, add ->install serial: Fix using plain integer instead of Null pointer tty:serial_core: Spelling mistake tty: Add NULL TTY driver tty: vt: keyboard: Allow Unicode compose base char Revert "tty: fix NULL pointer issue when tty_port ops is not set" serial: Add Milbeaut serial control ...
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
config TTY
|
||||
bool "Enable TTY" if EXPERT
|
||||
default y
|
||||
@@ -83,7 +84,6 @@ config HW_CONSOLE
|
||||
config VT_HW_CONSOLE_BINDING
|
||||
bool "Support for binding and unbinding console drivers"
|
||||
depends on HW_CONSOLE
|
||||
default n
|
||||
---help---
|
||||
The virtual terminal is the device that interacts with the physical
|
||||
terminal through console drivers. On these systems, at least one
|
||||
@@ -175,7 +175,7 @@ config ROCKETPORT
|
||||
This driver supports Comtrol RocketPort and RocketModem PCI boards.
|
||||
These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or
|
||||
modems. For information about the RocketPort/RocketModem boards
|
||||
and this driver read <file:Documentation/serial/rocket.txt>.
|
||||
and this driver read <file:Documentation/serial/rocket.rst>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rocket.
|
||||
@@ -193,7 +193,7 @@ config CYCLADES
|
||||
your Linux box, for instance in order to become a dial-in server.
|
||||
|
||||
For information about the Cyclades-Z card, read
|
||||
<file:Documentation/serial/README.cycladesZ>.
|
||||
<file:Documentation/serial/cyclades_z.rst>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyclades.
|
||||
@@ -312,7 +312,6 @@ config N_GSM
|
||||
config TRACE_ROUTER
|
||||
tristate "Trace data router for MIPI P1149.7 cJTAG standard"
|
||||
depends on TRACE_SINK
|
||||
default n
|
||||
help
|
||||
The trace router uses the Linux tty line discipline framework to
|
||||
route trace data coming from a tty port (say UART for example) to
|
||||
@@ -328,7 +327,6 @@ config TRACE_ROUTER
|
||||
|
||||
config TRACE_SINK
|
||||
tristate "Trace data sink for MIPI P1149.7 cJTAG standard"
|
||||
default n
|
||||
help
|
||||
The trace sink uses the Linux line discipline framework to receive
|
||||
trace data coming from the trace router line discipline driver
|
||||
@@ -376,6 +374,20 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE
|
||||
there simply will be no early console output. This is true also
|
||||
if you don't boot under a hypervisor at all.
|
||||
|
||||
config NULL_TTY
|
||||
tristate "NULL TTY driver"
|
||||
help
|
||||
Say Y here if you want a NULL TTY which simply discards messages.
|
||||
|
||||
This is useful to allow userspace applications which expect a console
|
||||
device to work without modifications even when no console is
|
||||
available or desired.
|
||||
|
||||
In order to use this driver, you should redirect the console to this
|
||||
TTY, or boot the kernel with console=ttynull.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config GOLDFISH_TTY
|
||||
tristate "Goldfish TTY Driver"
|
||||
depends on GOLDFISH
|
||||
|
@@ -25,6 +25,7 @@ obj-$(CONFIG_ISI) += isicom.o
|
||||
obj-$(CONFIG_MOXA_INTELLIO) += moxa.o
|
||||
obj-$(CONFIG_MOXA_SMARTIO) += mxser.o
|
||||
obj-$(CONFIG_NOZOMI) += nozomi.o
|
||||
obj-$(CONFIG_NULL_TTY) += ttynull.o
|
||||
obj-$(CONFIG_ROCKETPORT) += rocket.o
|
||||
obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o
|
||||
obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
if TTY
|
||||
|
||||
config HVC_DRIVER
|
||||
@@ -24,7 +25,6 @@ config HVC_CONSOLE
|
||||
config HVC_OLD_HVSI
|
||||
bool "Old driver for pSeries serial port (/dev/hvsi*)"
|
||||
depends on HVC_CONSOLE
|
||||
default n
|
||||
|
||||
config HVC_OPAL
|
||||
bool "OPAL Console support"
|
||||
@@ -73,7 +73,6 @@ config HVC_UDBG
|
||||
bool "udbg based fake hypervisor console"
|
||||
depends on PPC
|
||||
select HVC_DRIVER
|
||||
default n
|
||||
help
|
||||
This is meant to be used during HW bring up or debugging when
|
||||
no other console mechanism exist but udbg, to get you a quick
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for the IPWireless driver
|
||||
#
|
||||
|
@@ -114,6 +114,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data)
|
||||
|
||||
ipw->common_memory = ioremap(p_dev->resource[2]->start,
|
||||
resource_size(p_dev->resource[2]));
|
||||
if (!ipw->common_memory) {
|
||||
ret = -ENOMEM;
|
||||
goto exit1;
|
||||
}
|
||||
if (!request_mem_region(p_dev->resource[2]->start,
|
||||
resource_size(p_dev->resource[2]),
|
||||
IPWIRELESS_PCCARD_NAME)) {
|
||||
@@ -134,6 +138,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data)
|
||||
|
||||
ipw->attr_memory = ioremap(p_dev->resource[3]->start,
|
||||
resource_size(p_dev->resource[3]));
|
||||
if (!ipw->attr_memory) {
|
||||
ret = -ENOMEM;
|
||||
goto exit3;
|
||||
}
|
||||
if (!request_mem_region(p_dev->resource[3]->start,
|
||||
resource_size(p_dev->resource[3]),
|
||||
IPWIRELESS_PCCARD_NAME)) {
|
||||
|
@@ -550,9 +550,9 @@ static ssize_t process_output_block(struct tty_struct *tty,
|
||||
mutex_lock(&ldata->output_lock);
|
||||
|
||||
space = tty_write_room(tty);
|
||||
if (!space) {
|
||||
if (space <= 0) {
|
||||
mutex_unlock(&ldata->output_lock);
|
||||
return 0;
|
||||
return space;
|
||||
}
|
||||
if (nr > space)
|
||||
nr = space;
|
||||
|
@@ -1283,23 +1283,29 @@ static int rp_ioctl(struct tty_struct *tty,
|
||||
return -ENXIO;
|
||||
|
||||
switch (cmd) {
|
||||
case RCKP_GET_STRUCT:
|
||||
if (copy_to_user(argp, info, sizeof (struct r_port)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case RCKP_GET_CONFIG:
|
||||
dev_warn_ratelimited(tty->dev,
|
||||
"RCKP_GET_CONFIG option is deprecated\n");
|
||||
ret = get_config(info, argp);
|
||||
break;
|
||||
case RCKP_SET_CONFIG:
|
||||
dev_warn_ratelimited(tty->dev,
|
||||
"RCKP_SET_CONFIG option is deprecated\n");
|
||||
ret = set_config(tty, info, argp);
|
||||
break;
|
||||
case RCKP_GET_PORTS:
|
||||
dev_warn_ratelimited(tty->dev,
|
||||
"RCKP_GET_PORTS option is deprecated\n");
|
||||
ret = get_ports(info, argp);
|
||||
break;
|
||||
case RCKP_RESET_RM2:
|
||||
dev_warn_ratelimited(tty->dev,
|
||||
"RCKP_RESET_RM2 option is deprecated\n");
|
||||
ret = reset_rm2(info, argp);
|
||||
break;
|
||||
case RCKP_GET_VERSION:
|
||||
dev_warn_ratelimited(tty->dev,
|
||||
"RCKP_GET_VERSION option is deprecated\n");
|
||||
ret = get_version(info, argp);
|
||||
break;
|
||||
default:
|
||||
|
@@ -71,7 +71,6 @@ struct rocket_version {
|
||||
/*
|
||||
* Rocketport ioctls -- "RP"
|
||||
*/
|
||||
#define RCKP_GET_STRUCT 0x00525001
|
||||
#define RCKP_GET_CONFIG 0x00525002
|
||||
#define RCKP_SET_CONFIG 0x00525003
|
||||
#define RCKP_GET_PORTS 0x00525004
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Serial bus device driver configuration
|
||||
#
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
serdev-objs := core.o
|
||||
|
||||
obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
|
||||
|
@@ -361,12 +361,15 @@ static const struct exar8250_platform iot2040_platform = {
|
||||
.register_gpio = iot2040_register_gpio,
|
||||
};
|
||||
|
||||
/*
|
||||
* For SIMATIC IOT2000, only IOT2040 and its variants have the Exar device,
|
||||
* IOT2020 doesn't have. Therefore it is sufficient to match on the common
|
||||
* board name after the device was found.
|
||||
*/
|
||||
static const struct dmi_system_id exar_platforms[] = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
|
||||
"6ES7647-0AA00-1YA2"),
|
||||
},
|
||||
.driver_data = (void *)&iot2040_platform,
|
||||
},
|
||||
|
@@ -303,8 +303,9 @@ static void fintek_8250_goto_highspeed(struct uart_8250_port *uart,
|
||||
}
|
||||
}
|
||||
|
||||
void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
static void fintek_8250_set_termios(struct uart_port *port,
|
||||
struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
struct fintek_8250 *pdata = port->private_data;
|
||||
unsigned int baud = tty_termios_baud_rate(termios);
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@@ -21,15 +21,32 @@
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
#define UART_MTK_HIGHS 0x09 /* Highspeed register */
|
||||
#define UART_MTK_SAMPLE_COUNT 0x0a /* Sample count register */
|
||||
#define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */
|
||||
#define MTK_UART_HIGHS 0x09 /* Highspeed register */
|
||||
#define MTK_UART_SAMPLE_COUNT 0x0a /* Sample count register */
|
||||
#define MTK_UART_SAMPLE_POINT 0x0b /* Sample point register */
|
||||
#define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */
|
||||
|
||||
#define MTK_UART_ESCAPE_DAT 0x10 /* Escape Character register */
|
||||
#define MTK_UART_ESCAPE_EN 0x11 /* Escape Enable register */
|
||||
#define MTK_UART_DMA_EN 0x13 /* DMA Enable register */
|
||||
#define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */
|
||||
#define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */
|
||||
#define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */
|
||||
#define MTK_UART_IER_XOFFI 0x20 /* Enable XOFF character interrupt */
|
||||
#define MTK_UART_IER_RTSI 0x40 /* Enable RTS Modem status interrupt */
|
||||
#define MTK_UART_IER_CTSI 0x80 /* Enable CTS Modem status interrupt */
|
||||
|
||||
#define MTK_UART_EFR_EN 0x10 /* Enable enhancement feature */
|
||||
#define MTK_UART_EFR_RTS 0x40 /* Enable hardware rx flow control */
|
||||
#define MTK_UART_EFR_CTS 0x80 /* Enable hardware tx flow control */
|
||||
#define MTK_UART_EFR_NO_SW_FC 0x0 /* no sw flow control */
|
||||
#define MTK_UART_EFR_XON1_XOFF1 0xa /* XON1/XOFF1 as sw flow control */
|
||||
#define MTK_UART_EFR_XON2_XOFF2 0x5 /* XON2/XOFF2 as sw flow control */
|
||||
#define MTK_UART_EFR_SW_FC_MASK 0xf /* Enable CTS Modem status interrupt */
|
||||
#define MTK_UART_EFR_HW_FC (MTK_UART_EFR_RTS | MTK_UART_EFR_CTS)
|
||||
#define MTK_UART_DMA_EN_TX 0x2
|
||||
#define MTK_UART_DMA_EN_RX 0x5
|
||||
|
||||
#define MTK_UART_ESCAPE_CHAR 0x77 /* Escape char added under sw fc */
|
||||
#define MTK_UART_TX_SIZE UART_XMIT_SIZE
|
||||
#define MTK_UART_RX_SIZE 0x8000
|
||||
#define MTK_UART_TX_TRIGGER 1
|
||||
@@ -46,6 +63,7 @@ enum dma_rx_status {
|
||||
struct mtk8250_data {
|
||||
int line;
|
||||
unsigned int rx_pos;
|
||||
unsigned int clk_count;
|
||||
struct clk *uart_clk;
|
||||
struct clk *bus_clk;
|
||||
struct uart_8250_dma *dma;
|
||||
@@ -54,6 +72,13 @@ struct mtk8250_data {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* flow control mode */
|
||||
enum {
|
||||
MTK_UART_FC_NONE,
|
||||
MTK_UART_FC_SW,
|
||||
MTK_UART_FC_HW,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
static void mtk8250_rx_dma(struct uart_8250_port *up);
|
||||
|
||||
@@ -192,13 +217,89 @@ static void mtk8250_shutdown(struct uart_port *port)
|
||||
return serial8250_do_shutdown(port);
|
||||
}
|
||||
|
||||
static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask)
|
||||
{
|
||||
serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask));
|
||||
}
|
||||
|
||||
static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask)
|
||||
{
|
||||
serial_out(up, UART_IER, serial_in(up, UART_IER) | mask);
|
||||
}
|
||||
|
||||
static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode)
|
||||
{
|
||||
struct uart_port *port = &up->port;
|
||||
int lcr = serial_in(up, UART_LCR);
|
||||
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
serial_out(up, UART_EFR, UART_EFR_ECB);
|
||||
serial_out(up, UART_LCR, lcr);
|
||||
lcr = serial_in(up, UART_LCR);
|
||||
|
||||
switch (mode) {
|
||||
case MTK_UART_FC_NONE:
|
||||
serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
|
||||
serial_out(up, MTK_UART_ESCAPE_EN, 0x00);
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
serial_out(up, UART_EFR, serial_in(up, UART_EFR) &
|
||||
(~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK)));
|
||||
serial_out(up, UART_LCR, lcr);
|
||||
mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI |
|
||||
MTK_UART_IER_RTSI | MTK_UART_IER_CTSI);
|
||||
break;
|
||||
|
||||
case MTK_UART_FC_HW:
|
||||
serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
|
||||
serial_out(up, MTK_UART_ESCAPE_EN, 0x00);
|
||||
serial_out(up, UART_MCR, UART_MCR_RTS);
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
|
||||
/*enable hw flow control*/
|
||||
serial_out(up, UART_EFR, MTK_UART_EFR_HW_FC |
|
||||
(serial_in(up, UART_EFR) &
|
||||
(~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK))));
|
||||
|
||||
serial_out(up, UART_LCR, lcr);
|
||||
mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI);
|
||||
mtk8250_enable_intrs(up, MTK_UART_IER_CTSI | MTK_UART_IER_RTSI);
|
||||
break;
|
||||
|
||||
case MTK_UART_FC_SW: /*MTK software flow control */
|
||||
serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
|
||||
serial_out(up, MTK_UART_ESCAPE_EN, 0x01);
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
|
||||
/*enable sw flow control */
|
||||
serial_out(up, UART_EFR, MTK_UART_EFR_XON1_XOFF1 |
|
||||
(serial_in(up, UART_EFR) &
|
||||
(~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK))));
|
||||
|
||||
serial_out(up, UART_XON1, START_CHAR(port->state->port.tty));
|
||||
serial_out(up, UART_XOFF1, STOP_CHAR(port->state->port.tty));
|
||||
serial_out(up, UART_LCR, lcr);
|
||||
mtk8250_disable_intrs(up, MTK_UART_IER_CTSI|MTK_UART_IER_RTSI);
|
||||
mtk8250_enable_intrs(up, MTK_UART_IER_XOFFI);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
{
|
||||
unsigned short fraction_L_mapping[] = {
|
||||
0, 1, 0x5, 0x15, 0x55, 0x57, 0x57, 0x77, 0x7F, 0xFF, 0xFF
|
||||
};
|
||||
unsigned short fraction_M_mapping[] = {
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3
|
||||
};
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
unsigned int baud, quot, fraction;
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot;
|
||||
int mode;
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
if (up->dma) {
|
||||
@@ -214,7 +315,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
serial8250_do_set_termios(port, termios, old);
|
||||
|
||||
/*
|
||||
* Mediatek UARTs use an extra highspeed register (UART_MTK_HIGHS)
|
||||
* Mediatek UARTs use an extra highspeed register (MTK_UART_HIGHS)
|
||||
*
|
||||
* We need to recalcualte the quot register, as the claculation depends
|
||||
* on the vaule in the highspeed register.
|
||||
@@ -230,18 +331,11 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
port->uartclk / 16 / UART_DIV_MAX,
|
||||
port->uartclk);
|
||||
|
||||
if (baud <= 115200) {
|
||||
serial_port_out(port, UART_MTK_HIGHS, 0x0);
|
||||
if (baud < 115200) {
|
||||
serial_port_out(port, MTK_UART_HIGHS, 0x0);
|
||||
quot = uart_get_divisor(port, baud);
|
||||
} else if (baud <= 576000) {
|
||||
serial_port_out(port, UART_MTK_HIGHS, 0x2);
|
||||
|
||||
/* Set to next lower baudrate supported */
|
||||
if ((baud == 500000) || (baud == 576000))
|
||||
baud = 460800;
|
||||
quot = DIV_ROUND_UP(port->uartclk, 4 * baud);
|
||||
} else {
|
||||
serial_port_out(port, UART_MTK_HIGHS, 0x3);
|
||||
serial_port_out(port, MTK_UART_HIGHS, 0x3);
|
||||
quot = DIV_ROUND_UP(port->uartclk, 256 * baud);
|
||||
}
|
||||
|
||||
@@ -258,18 +352,40 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
/* reset DLAB */
|
||||
serial_port_out(port, UART_LCR, up->lcr);
|
||||
|
||||
if (baud > 460800) {
|
||||
if (baud >= 115200) {
|
||||
unsigned int tmp;
|
||||
|
||||
tmp = DIV_ROUND_CLOSEST(port->uartclk, quot * baud);
|
||||
serial_port_out(port, UART_MTK_SAMPLE_COUNT, tmp - 1);
|
||||
serial_port_out(port, UART_MTK_SAMPLE_POINT,
|
||||
(tmp - 2) >> 1);
|
||||
tmp = (port->uartclk / (baud * quot)) - 1;
|
||||
serial_port_out(port, MTK_UART_SAMPLE_COUNT, tmp);
|
||||
serial_port_out(port, MTK_UART_SAMPLE_POINT,
|
||||
(tmp >> 1) - 1);
|
||||
|
||||
/*count fraction to set fractoin register */
|
||||
fraction = ((port->uartclk * 100) / baud / quot) % 100;
|
||||
fraction = DIV_ROUND_CLOSEST(fraction, 10);
|
||||
serial_port_out(port, MTK_UART_FRACDIV_L,
|
||||
fraction_L_mapping[fraction]);
|
||||
serial_port_out(port, MTK_UART_FRACDIV_M,
|
||||
fraction_M_mapping[fraction]);
|
||||
} else {
|
||||
serial_port_out(port, UART_MTK_SAMPLE_COUNT, 0x00);
|
||||
serial_port_out(port, UART_MTK_SAMPLE_POINT, 0xff);
|
||||
serial_port_out(port, MTK_UART_SAMPLE_COUNT, 0x00);
|
||||
serial_port_out(port, MTK_UART_SAMPLE_POINT, 0xff);
|
||||
serial_port_out(port, MTK_UART_FRACDIV_L, 0x00);
|
||||
serial_port_out(port, MTK_UART_FRACDIV_M, 0x00);
|
||||
}
|
||||
|
||||
if ((termios->c_cflag & CRTSCTS) && (!(termios->c_iflag & CRTSCTS)))
|
||||
mode = MTK_UART_FC_HW;
|
||||
else if (termios->c_iflag & CRTSCTS)
|
||||
mode = MTK_UART_FC_SW;
|
||||
else
|
||||
mode = MTK_UART_FC_NONE;
|
||||
|
||||
mtk8250_set_flow_ctrl(up, mode);
|
||||
|
||||
if (uart_console(port))
|
||||
up->port.cons->cflag = termios->c_cflag;
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
/* Don't rewrite B0 */
|
||||
if (tty_termios_baud_rate(termios))
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# The 8250/16550 serial drivers. You shouldn't be in this list unless
|
||||
# you somehow have an implicit or explicit dependency on SERIAL_8250.
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Serial device configuration
|
||||
#
|
||||
@@ -369,7 +370,6 @@ config SERIAL_MAX310X
|
||||
depends on SPI_MASTER
|
||||
select SERIAL_CORE
|
||||
select REGMAP_SPI if SPI_MASTER
|
||||
default n
|
||||
help
|
||||
This selects support for an advanced UART from Maxim (Dallas).
|
||||
Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830.
|
||||
@@ -652,7 +652,6 @@ config SERIAL_MUX_CONSOLE
|
||||
config PDC_CONSOLE
|
||||
bool "PDC software console support"
|
||||
depends on PARISC && !SERIAL_MUX && VT
|
||||
default n
|
||||
help
|
||||
Saying Y here will enable the software based PDC console to be
|
||||
used as the system console. This is useful for machines in
|
||||
@@ -1095,6 +1094,30 @@ config SERIAL_OMAP_CONSOLE
|
||||
your boot loader about how to pass options to the kernel at
|
||||
boot time.)
|
||||
|
||||
config SERIAL_SIFIVE
|
||||
tristate "SiFive UART support"
|
||||
depends on OF
|
||||
select SERIAL_CORE
|
||||
help
|
||||
Select this option if you are building a kernel for a device that
|
||||
contains a SiFive UART IP block. This type of UART is present on
|
||||
SiFive FU540 SoCs, among others.
|
||||
|
||||
config SERIAL_SIFIVE_CONSOLE
|
||||
bool "Console on SiFive UART"
|
||||
depends on SERIAL_SIFIVE=y
|
||||
select SERIAL_CORE_CONSOLE
|
||||
help
|
||||
Select this option if you would like to use a SiFive UART as the
|
||||
system console.
|
||||
|
||||
Even if you say Y here, the currently visible virtual console
|
||||
(/dev/tty0) will still be used as the system console by default, but
|
||||
you can alter that using a kernel command line option such as
|
||||
"console=ttySIFx". (Try "man bootparam" or see the documentation of
|
||||
your boot loader about how to pass options to the kernel at
|
||||
boot time.)
|
||||
|
||||
config SERIAL_LANTIQ
|
||||
bool "Lantiq serial driver"
|
||||
depends on LANTIQ
|
||||
@@ -1109,7 +1132,6 @@ config SERIAL_QE
|
||||
depends on QUICC_ENGINE
|
||||
select SERIAL_CORE
|
||||
select FW_LOADER
|
||||
default n
|
||||
help
|
||||
This driver supports the QE serial ports on Freescale embedded
|
||||
PowerPC that contain a QUICC Engine.
|
||||
@@ -1582,6 +1604,32 @@ config SERIAL_RDA_CONSOLE
|
||||
Say 'Y' here if you wish to use the RDA8810PL UART as the system
|
||||
console. Only earlycon is implemented currently.
|
||||
|
||||
config SERIAL_MILBEAUT_USIO
|
||||
tristate "Milbeaut USIO/UART serial port support"
|
||||
depends on ARCH_MILBEAUT || (COMPILE_TEST && OF)
|
||||
default ARCH_MILBEAUT
|
||||
select SERIAL_CORE
|
||||
help
|
||||
This selects the USIO/UART IP found in Socionext Milbeaut SoCs.
|
||||
|
||||
config SERIAL_MILBEAUT_USIO_PORTS
|
||||
int "Maximum number of CSIO/UART ports (1-8)"
|
||||
range 1 8
|
||||
depends on SERIAL_MILBEAUT_USIO
|
||||
default "4"
|
||||
|
||||
config SERIAL_MILBEAUT_USIO_CONSOLE
|
||||
bool "Support for console on MILBEAUT USIO/UART serial port"
|
||||
depends on SERIAL_MILBEAUT_USIO=y
|
||||
default y
|
||||
select SERIAL_CORE_CONSOLE
|
||||
select SERIAL_EARLYCON
|
||||
help
|
||||
Say 'Y' here if you wish to use a USIO/UART of Socionext Milbeaut
|
||||
SoCs as the system console (the system console is the device which
|
||||
receives all kernel messages and warnings and which allows logins in
|
||||
single user mode).
|
||||
|
||||
endmenu
|
||||
|
||||
config SERIAL_MCTRL_GPIO
|
||||
|
@@ -92,6 +92,8 @@ obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o
|
||||
obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
|
||||
obj-$(CONFIG_SERIAL_OWL) += owl-uart.o
|
||||
obj-$(CONFIG_SERIAL_RDA) += rda-uart.o
|
||||
obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o
|
||||
obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o
|
||||
|
||||
# GPIOLIB helpers for modem control lines
|
||||
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for the Motorola 8xx FEC ethernet controller
|
||||
#
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for Jasmine adapter
|
||||
#
|
||||
|
614
drivers/tty/serial/milbeaut_usio.c
Normal file
614
drivers/tty/serial/milbeaut_usio.c
Normal file
@@ -0,0 +1,614 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Socionext Inc.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
#define SUPPORT_SYSRQ
|
||||
#endif
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
||||
#define USIO_NAME "mlb-usio-uart"
|
||||
#define USIO_UART_DEV_NAME "ttyUSI"
|
||||
|
||||
static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS];
|
||||
|
||||
#define RX 0
|
||||
#define TX 1
|
||||
static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2];
|
||||
|
||||
#define MLB_USIO_REG_SMR 0
|
||||
#define MLB_USIO_REG_SCR 1
|
||||
#define MLB_USIO_REG_ESCR 2
|
||||
#define MLB_USIO_REG_SSR 3
|
||||
#define MLB_USIO_REG_DR 4
|
||||
#define MLB_USIO_REG_BGR 6
|
||||
#define MLB_USIO_REG_FCR 12
|
||||
#define MLB_USIO_REG_FBYTE 14
|
||||
|
||||
#define MLB_USIO_SMR_SOE BIT(0)
|
||||
#define MLB_USIO_SMR_SBL BIT(3)
|
||||
#define MLB_USIO_SCR_TXE BIT(0)
|
||||
#define MLB_USIO_SCR_RXE BIT(1)
|
||||
#define MLB_USIO_SCR_TBIE BIT(2)
|
||||
#define MLB_USIO_SCR_TIE BIT(3)
|
||||
#define MLB_USIO_SCR_RIE BIT(4)
|
||||
#define MLB_USIO_SCR_UPCL BIT(7)
|
||||
#define MLB_USIO_ESCR_L_8BIT 0
|
||||
#define MLB_USIO_ESCR_L_5BIT 1
|
||||
#define MLB_USIO_ESCR_L_6BIT 2
|
||||
#define MLB_USIO_ESCR_L_7BIT 3
|
||||
#define MLB_USIO_ESCR_P BIT(3)
|
||||
#define MLB_USIO_ESCR_PEN BIT(4)
|
||||
#define MLB_USIO_ESCR_FLWEN BIT(7)
|
||||
#define MLB_USIO_SSR_TBI BIT(0)
|
||||
#define MLB_USIO_SSR_TDRE BIT(1)
|
||||
#define MLB_USIO_SSR_RDRF BIT(2)
|
||||
#define MLB_USIO_SSR_ORE BIT(3)
|
||||
#define MLB_USIO_SSR_FRE BIT(4)
|
||||
#define MLB_USIO_SSR_PE BIT(5)
|
||||
#define MLB_USIO_SSR_REC BIT(7)
|
||||
#define MLB_USIO_SSR_BRK BIT(8)
|
||||
#define MLB_USIO_FCR_FE1 BIT(0)
|
||||
#define MLB_USIO_FCR_FE2 BIT(1)
|
||||
#define MLB_USIO_FCR_FCL1 BIT(2)
|
||||
#define MLB_USIO_FCR_FCL2 BIT(3)
|
||||
#define MLB_USIO_FCR_FSET BIT(4)
|
||||
#define MLB_USIO_FCR_FTIE BIT(9)
|
||||
#define MLB_USIO_FCR_FDRQ BIT(10)
|
||||
#define MLB_USIO_FCR_FRIIE BIT(11)
|
||||
|
||||
static void mlb_usio_stop_tx(struct uart_port *port)
|
||||
{
|
||||
writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
|
||||
port->membase + MLB_USIO_REG_FCR);
|
||||
writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE,
|
||||
port->membase + MLB_USIO_REG_SCR);
|
||||
}
|
||||
|
||||
static void mlb_usio_tx_chars(struct uart_port *port)
|
||||
{
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
int count;
|
||||
|
||||
writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
|
||||
port->membase + MLB_USIO_REG_FCR);
|
||||
writeb(readb(port->membase + MLB_USIO_REG_SCR) &
|
||||
~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE),
|
||||
port->membase + MLB_USIO_REG_SCR);
|
||||
|
||||
if (port->x_char) {
|
||||
writew(port->x_char, port->membase + MLB_USIO_REG_DR);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return;
|
||||
}
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
mlb_usio_stop_tx(port);
|
||||
return;
|
||||
}
|
||||
|
||||
count = port->fifosize -
|
||||
(readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff);
|
||||
|
||||
do {
|
||||
writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR);
|
||||
|
||||
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx++;
|
||||
if (uart_circ_empty(xmit))
|
||||
break;
|
||||
|
||||
} while (--count > 0);
|
||||
|
||||
writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ,
|
||||
port->membase + MLB_USIO_REG_FCR);
|
||||
|
||||
writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
|
||||
port->membase + MLB_USIO_REG_SCR);
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit))
|
||||
mlb_usio_stop_tx(port);
|
||||
}
|
||||
|
||||
static void mlb_usio_start_tx(struct uart_port *port)
|
||||
{
|
||||
u16 fcr = readw(port->membase + MLB_USIO_REG_FCR);
|
||||
|
||||
writew(fcr | MLB_USIO_FCR_FTIE, port->membase + MLB_USIO_REG_FCR);
|
||||
if (!(fcr & MLB_USIO_FCR_FDRQ))
|
||||
return;
|
||||
|
||||
writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
|
||||
port->membase + MLB_USIO_REG_SCR);
|
||||
|
||||
if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
|
||||
mlb_usio_tx_chars(port);
|
||||
}
|
||||
|
||||
static void mlb_usio_stop_rx(struct uart_port *port)
|
||||
{
|
||||
writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE,
|
||||
port->membase + MLB_USIO_REG_SCR);
|
||||
}
|
||||
|
||||
static void mlb_usio_enable_ms(struct uart_port *port)
|
||||
{
|
||||
writeb(readb(port->membase + MLB_USIO_REG_SCR) |
|
||||
MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE,
|
||||
port->membase + MLB_USIO_REG_SCR);
|
||||
}
|
||||
|
||||
static void mlb_usio_rx_chars(struct uart_port *port)
|
||||
{
|
||||
struct tty_port *ttyport = &port->state->port;
|
||||
unsigned long flag = 0;
|
||||
char ch = 0;
|
||||
u8 status;
|
||||
int max_count = 2;
|
||||
|
||||
while (max_count--) {
|
||||
status = readb(port->membase + MLB_USIO_REG_SSR);
|
||||
|
||||
if (!(status & MLB_USIO_SSR_RDRF))
|
||||
break;
|
||||
|
||||
if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE |
|
||||
MLB_USIO_SSR_PE))) {
|
||||
ch = readw(port->membase + MLB_USIO_REG_DR);
|
||||
flag = TTY_NORMAL;
|
||||
port->icount.rx++;
|
||||
if (uart_handle_sysrq_char(port, ch))
|
||||
continue;
|
||||
uart_insert_char(port, status, MLB_USIO_SSR_ORE,
|
||||
ch, flag);
|
||||
continue;
|
||||
}
|
||||
if (status & MLB_USIO_SSR_PE)
|
||||
port->icount.parity++;
|
||||
if (status & MLB_USIO_SSR_ORE)
|
||||
port->icount.overrun++;
|
||||
status &= port->read_status_mask;
|
||||
if (status & MLB_USIO_SSR_BRK) {
|
||||
flag = TTY_BREAK;
|
||||
ch = 0;
|
||||
} else
|
||||
if (status & MLB_USIO_SSR_PE) {
|
||||
flag = TTY_PARITY;
|
||||
ch = 0;
|
||||
} else
|
||||
if (status & MLB_USIO_SSR_FRE) {
|
||||
flag = TTY_FRAME;
|
||||
ch = 0;
|
||||
}
|
||||
if (flag)
|
||||
uart_insert_char(port, status, MLB_USIO_SSR_ORE,
|
||||
ch, flag);
|
||||
|
||||
writeb(readb(port->membase + MLB_USIO_REG_SSR) |
|
||||
MLB_USIO_SSR_REC,
|
||||
port->membase + MLB_USIO_REG_SSR);
|
||||
|
||||
max_count = readw(port->membase + MLB_USIO_REG_FBYTE) >> 8;
|
||||
writew(readw(port->membase + MLB_USIO_REG_FCR) |
|
||||
MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
|
||||
port->membase + MLB_USIO_REG_FCR);
|
||||
}
|
||||
|
||||
tty_flip_buffer_push(ttyport);
|
||||
}
|
||||
|
||||
static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
|
||||
spin_lock(&port->lock);
|
||||
mlb_usio_rx_chars(port);
|
||||
spin_unlock(&port->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct uart_port *port = dev_id;
|
||||
|
||||
spin_lock(&port->lock);
|
||||
if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
|
||||
mlb_usio_tx_chars(port);
|
||||
spin_unlock(&port->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int mlb_usio_tx_empty(struct uart_port *port)
|
||||
{
|
||||
return (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ?
|
||||
TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
static unsigned int mlb_usio_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
|
||||
|
||||
}
|
||||
|
||||
static void mlb_usio_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
}
|
||||
|
||||
static int mlb_usio_startup(struct uart_port *port)
|
||||
{
|
||||
const char *portname = to_platform_device(port->dev)->name;
|
||||
unsigned long flags;
|
||||
int ret, index = port->line;
|
||||
unsigned char escr;
|
||||
|
||||
ret = request_irq(mlb_usio_irq[index][RX], mlb_usio_rx_irq,
|
||||
0, portname, port);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = request_irq(mlb_usio_irq[index][TX], mlb_usio_tx_irq,
|
||||
0, portname, port);
|
||||
if (ret) {
|
||||
free_irq(mlb_usio_irq[index][RX], port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
escr = readb(port->membase + MLB_USIO_REG_ESCR);
|
||||
if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
|
||||
escr |= MLB_USIO_ESCR_FLWEN;
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
writeb(0, port->membase + MLB_USIO_REG_SCR);
|
||||
writeb(escr, port->membase + MLB_USIO_REG_ESCR);
|
||||
writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
|
||||
writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
|
||||
writew(0, port->membase + MLB_USIO_REG_FCR);
|
||||
writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2,
|
||||
port->membase + MLB_USIO_REG_FCR);
|
||||
writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
|
||||
port->membase + MLB_USIO_REG_FCR);
|
||||
writew(0, port->membase + MLB_USIO_REG_FBYTE);
|
||||
writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
|
||||
|
||||
writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE |
|
||||
MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlb_usio_shutdown(struct uart_port *port)
|
||||
{
|
||||
int index = port->line;
|
||||
|
||||
free_irq(mlb_usio_irq[index][RX], port);
|
||||
free_irq(mlb_usio_irq[index][TX], port);
|
||||
}
|
||||
|
||||
static void mlb_usio_set_termios(struct uart_port *port,
|
||||
struct ktermios *termios, struct ktermios *old)
|
||||
{
|
||||
unsigned int escr, smr = MLB_USIO_SMR_SOE;
|
||||
unsigned long flags, baud, quot;
|
||||
|
||||
switch (termios->c_cflag & CSIZE) {
|
||||
case CS5:
|
||||
escr = MLB_USIO_ESCR_L_5BIT;
|
||||
break;
|
||||
case CS6:
|
||||
escr = MLB_USIO_ESCR_L_6BIT;
|
||||
break;
|
||||
case CS7:
|
||||
escr = MLB_USIO_ESCR_L_7BIT;
|
||||
break;
|
||||
case CS8:
|
||||
default:
|
||||
escr = MLB_USIO_ESCR_L_8BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (termios->c_cflag & CSTOPB)
|
||||
smr |= MLB_USIO_SMR_SBL;
|
||||
|
||||
if (termios->c_cflag & PARENB) {
|
||||
escr |= MLB_USIO_ESCR_PEN;
|
||||
if (termios->c_cflag & PARODD)
|
||||
escr |= MLB_USIO_ESCR_P;
|
||||
}
|
||||
/* Set hard flow control */
|
||||
if (of_property_read_bool(port->dev->of_node, "auto-flow-control") ||
|
||||
(termios->c_cflag & CRTSCTS))
|
||||
escr |= MLB_USIO_ESCR_FLWEN;
|
||||
|
||||
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);
|
||||
if (baud > 1)
|
||||
quot = port->uartclk / baud - 1;
|
||||
else
|
||||
quot = 0;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF |
|
||||
MLB_USIO_SSR_TDRE;
|
||||
if (termios->c_iflag & INPCK)
|
||||
port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
|
||||
|
||||
port->ignore_status_mask = 0;
|
||||
if (termios->c_iflag & IGNPAR)
|
||||
port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
|
||||
if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR))
|
||||
port->ignore_status_mask |= MLB_USIO_SSR_ORE;
|
||||
if ((termios->c_cflag & CREAD) == 0)
|
||||
port->ignore_status_mask |= MLB_USIO_SSR_RDRF;
|
||||
|
||||
writeb(0, port->membase + MLB_USIO_REG_SCR);
|
||||
writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
|
||||
writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
|
||||
writew(0, port->membase + MLB_USIO_REG_FCR);
|
||||
writeb(smr, port->membase + MLB_USIO_REG_SMR);
|
||||
writeb(escr, port->membase + MLB_USIO_REG_ESCR);
|
||||
writew(quot, port->membase + MLB_USIO_REG_BGR);
|
||||
writew(0, port->membase + MLB_USIO_REG_FCR);
|
||||
writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 |
|
||||
MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
|
||||
port->membase + MLB_USIO_REG_FCR);
|
||||
writew(0, port->membase + MLB_USIO_REG_FBYTE);
|
||||
writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
|
||||
writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE |
|
||||
MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static const char *mlb_usio_type(struct uart_port *port)
|
||||
{
|
||||
return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL);
|
||||
}
|
||||
|
||||
static void mlb_usio_config_port(struct uart_port *port, int flags)
|
||||
{
|
||||
if (flags & UART_CONFIG_TYPE)
|
||||
port->type = PORT_MLB_USIO;
|
||||
}
|
||||
|
||||
static const struct uart_ops mlb_usio_ops = {
|
||||
.tx_empty = mlb_usio_tx_empty,
|
||||
.set_mctrl = mlb_usio_set_mctrl,
|
||||
.get_mctrl = mlb_usio_get_mctrl,
|
||||
.stop_tx = mlb_usio_stop_tx,
|
||||
.start_tx = mlb_usio_start_tx,
|
||||
.stop_rx = mlb_usio_stop_rx,
|
||||
.enable_ms = mlb_usio_enable_ms,
|
||||
.break_ctl = mlb_usio_break_ctl,
|
||||
.startup = mlb_usio_startup,
|
||||
.shutdown = mlb_usio_shutdown,
|
||||
.set_termios = mlb_usio_set_termios,
|
||||
.type = mlb_usio_type,
|
||||
.config_port = mlb_usio_config_port,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE
|
||||
|
||||
static void mlb_usio_console_putchar(struct uart_port *port, int c)
|
||||
{
|
||||
while (!(readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE))
|
||||
cpu_relax();
|
||||
|
||||
writew(c, port->membase + MLB_USIO_REG_DR);
|
||||
}
|
||||
|
||||
static void mlb_usio_console_write(struct console *co, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
struct uart_port *port = &mlb_usio_ports[co->index];
|
||||
|
||||
uart_console_write(port, s, count, mlb_usio_console_putchar);
|
||||
}
|
||||
|
||||
static int __init mlb_usio_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
int baud = 115200;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
int bits = 8;
|
||||
|
||||
if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS)
|
||||
return -ENODEV;
|
||||
|
||||
port = &mlb_usio_ports[co->index];
|
||||
if (!port->membase)
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
||||
if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
|
||||
flow = 'r';
|
||||
|
||||
return uart_set_options(port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
|
||||
static struct uart_driver mlb_usio_uart_driver;
|
||||
static struct console mlb_usio_console = {
|
||||
.name = USIO_UART_DEV_NAME,
|
||||
.write = mlb_usio_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = mlb_usio_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &mlb_usio_uart_driver,
|
||||
};
|
||||
|
||||
static int __init mlb_usio_console_init(void)
|
||||
{
|
||||
register_console(&mlb_usio_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(mlb_usio_console_init);
|
||||
|
||||
|
||||
static void mlb_usio_early_console_write(struct console *co, const char *s,
|
||||
u_int count)
|
||||
{
|
||||
struct earlycon_device *dev = co->data;
|
||||
|
||||
uart_console_write(&dev->port, s, count, mlb_usio_console_putchar);
|
||||
}
|
||||
|
||||
static int __init mlb_usio_early_console_setup(struct earlycon_device *device,
|
||||
const char *opt)
|
||||
{
|
||||
if (!device->port.membase)
|
||||
return -ENODEV;
|
||||
device->con->write = mlb_usio_early_console_write;
|
||||
return 0;
|
||||
}
|
||||
|
||||
OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart",
|
||||
mlb_usio_early_console_setup);
|
||||
|
||||
#define USIO_CONSOLE (&mlb_usio_console)
|
||||
#else
|
||||
#define USIO_CONSOLE NULL
|
||||
#endif
|
||||
|
||||
static struct uart_driver mlb_usio_uart_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.driver_name = USIO_NAME,
|
||||
.dev_name = USIO_UART_DEV_NAME,
|
||||
.cons = USIO_CONSOLE,
|
||||
.nr = CONFIG_SERIAL_MILBEAUT_USIO_PORTS,
|
||||
};
|
||||
|
||||
static int mlb_usio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk *clk = devm_clk_get(&pdev->dev, NULL);
|
||||
struct uart_port *port;
|
||||
struct resource *res;
|
||||
int index = 0;
|
||||
int ret;
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "Missing clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Clock enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
of_property_read_u32(pdev->dev.of_node, "index", &index);
|
||||
port = &mlb_usio_ports[index];
|
||||
|
||||
port->private_data = (void *)clk;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
dev_err(&pdev->dev, "Missing regs\n");
|
||||
ret = -ENODEV;
|
||||
goto failed;
|
||||
}
|
||||
port->membase = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
|
||||
ret = platform_get_irq_byname(pdev, "rx");
|
||||
mlb_usio_irq[index][RX] = ret;
|
||||
|
||||
ret = platform_get_irq_byname(pdev, "tx");
|
||||
mlb_usio_irq[index][TX] = ret;
|
||||
|
||||
port->irq = mlb_usio_irq[index][RX];
|
||||
port->uartclk = clk_get_rate(clk);
|
||||
port->fifosize = 128;
|
||||
port->iotype = UPIO_MEM32;
|
||||
port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI;
|
||||
port->line = index;
|
||||
port->ops = &mlb_usio_ops;
|
||||
port->dev = &pdev->dev;
|
||||
|
||||
ret = uart_add_one_port(&mlb_usio_uart_driver, port);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Adding port failed: %d\n", ret);
|
||||
goto failed;
|
||||
}
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlb_usio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_port *port = &mlb_usio_ports[pdev->id];
|
||||
struct clk *clk = port->private_data;
|
||||
|
||||
uart_remove_one_port(&mlb_usio_uart_driver, port);
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mlb_usio_dt_ids[] = {
|
||||
{ .compatible = "socionext,milbeaut-usio-uart" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids);
|
||||
|
||||
static struct platform_driver mlb_usio_driver = {
|
||||
.probe = mlb_usio_probe,
|
||||
.remove = mlb_usio_remove,
|
||||
.driver = {
|
||||
.name = USIO_NAME,
|
||||
.of_match_table = mlb_usio_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mlb_usio_init(void)
|
||||
{
|
||||
int ret = uart_register_driver(&mlb_usio_uart_driver);
|
||||
|
||||
if (ret) {
|
||||
pr_err("%s: uart registration failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = platform_driver_register(&mlb_usio_driver);
|
||||
if (ret) {
|
||||
uart_unregister_driver(&mlb_usio_uart_driver);
|
||||
pr_err("%s: drv registration failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit mlb_usio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mlb_usio_driver);
|
||||
uart_unregister_driver(&mlb_usio_uart_driver);
|
||||
}
|
||||
|
||||
module_init(mlb_usio_init);
|
||||
module_exit(mlb_usio_exit);
|
||||
|
||||
MODULE_AUTHOR("SOCIONEXT");
|
||||
MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver");
|
||||
MODULE_LICENSE("GPL");
|
@@ -14,9 +14,9 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial.h>
|
||||
@@ -1179,7 +1179,8 @@ static int sc16is7xx_probe(struct device *dev,
|
||||
struct regmap *regmap, int irq, unsigned long flags)
|
||||
{
|
||||
struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 };
|
||||
unsigned long freq, *pfreq = dev_get_platdata(dev);
|
||||
unsigned long freq = 0, *pfreq = dev_get_platdata(dev);
|
||||
u32 uartclk = 0;
|
||||
int i, ret;
|
||||
struct sc16is7xx_port *s;
|
||||
|
||||
@@ -1193,10 +1194,17 @@ static int sc16is7xx_probe(struct device *dev,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Always ask for fixed clock rate from a property. */
|
||||
device_property_read_u32(dev, "clock-frequency", &uartclk);
|
||||
|
||||
s->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(s->clk)) {
|
||||
if (uartclk)
|
||||
freq = uartclk;
|
||||
if (pfreq)
|
||||
freq = *pfreq;
|
||||
if (freq)
|
||||
dev_dbg(dev, "Clock frequency: %luHz\n", freq);
|
||||
else
|
||||
return PTR_ERR(s->clk);
|
||||
} else {
|
||||
@@ -1384,13 +1392,9 @@ static int sc16is7xx_spi_probe(struct spi_device *spi)
|
||||
return ret;
|
||||
|
||||
if (spi->dev.of_node) {
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(sc16is7xx_dt_ids, &spi->dev);
|
||||
|
||||
if (!of_id)
|
||||
devtype = device_get_match_data(&spi->dev);
|
||||
if (!devtype)
|
||||
return -ENODEV;
|
||||
|
||||
devtype = (struct sc16is7xx_devtype *)of_id->data;
|
||||
} else {
|
||||
const struct spi_device_id *id_entry = spi_get_device_id(spi);
|
||||
|
||||
@@ -1426,7 +1430,7 @@ MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table);
|
||||
static struct spi_driver sc16is7xx_spi_uart_driver = {
|
||||
.driver = {
|
||||
.name = SC16IS7XX_NAME,
|
||||
.of_match_table = of_match_ptr(sc16is7xx_dt_ids),
|
||||
.of_match_table = sc16is7xx_dt_ids,
|
||||
},
|
||||
.probe = sc16is7xx_spi_probe,
|
||||
.remove = sc16is7xx_spi_remove,
|
||||
@@ -1445,13 +1449,9 @@ static int sc16is7xx_i2c_probe(struct i2c_client *i2c,
|
||||
struct regmap *regmap;
|
||||
|
||||
if (i2c->dev.of_node) {
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(sc16is7xx_dt_ids, &i2c->dev);
|
||||
|
||||
if (!of_id)
|
||||
devtype = device_get_match_data(&i2c->dev);
|
||||
if (!devtype)
|
||||
return -ENODEV;
|
||||
|
||||
devtype = (struct sc16is7xx_devtype *)of_id->data;
|
||||
} else {
|
||||
devtype = (struct sc16is7xx_devtype *)id->driver_data;
|
||||
flags = IRQF_TRIGGER_FALLING;
|
||||
@@ -1484,7 +1484,7 @@ MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table);
|
||||
static struct i2c_driver sc16is7xx_i2c_uart_driver = {
|
||||
.driver = {
|
||||
.name = SC16IS7XX_NAME,
|
||||
.of_match_table = of_match_ptr(sc16is7xx_dt_ids),
|
||||
.of_match_table = sc16is7xx_dt_ids,
|
||||
},
|
||||
.probe = sc16is7xx_i2c_probe,
|
||||
.remove = sc16is7xx_i2c_remove,
|
||||
|
@@ -130,9 +130,6 @@ static void uart_start(struct tty_struct *tty)
|
||||
struct uart_port *port;
|
||||
unsigned long flags;
|
||||
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
port = uart_port_lock(state, flags);
|
||||
__uart_start(tty);
|
||||
uart_port_unlock(port, flags);
|
||||
@@ -730,9 +727,6 @@ static void uart_unthrottle(struct tty_struct *tty)
|
||||
upstat_t mask = UPSTAT_SYNC_FIFO;
|
||||
struct uart_port *port;
|
||||
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
port = uart_port_ref(state);
|
||||
if (!port)
|
||||
return;
|
||||
@@ -1514,7 +1508,7 @@ static void uart_set_termios(struct tty_struct *tty,
|
||||
}
|
||||
|
||||
uart_change_speed(tty, state, old_termios);
|
||||
/* reload cflag from termios; port driver may have overriden flags */
|
||||
/* reload cflag from termios; port driver may have overridden flags */
|
||||
cflag = tty->termios.c_cflag;
|
||||
|
||||
/* Handle transition to B0 status */
|
||||
@@ -1747,6 +1741,16 @@ static void uart_dtr_rts(struct tty_port *port, int raise)
|
||||
uart_port_deref(uport);
|
||||
}
|
||||
|
||||
static int uart_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
{
|
||||
struct uart_driver *drv = driver->driver_state;
|
||||
struct uart_state *state = drv->state + tty->index;
|
||||
|
||||
tty->driver_data = state;
|
||||
|
||||
return tty_standard_install(driver, tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls to uart_open are serialised by the tty_lock in
|
||||
* drivers/tty/tty_io.c:tty_open()
|
||||
@@ -1759,11 +1763,8 @@ static void uart_dtr_rts(struct tty_port *port, int raise)
|
||||
*/
|
||||
static int uart_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct uart_driver *drv = tty->driver->driver_state;
|
||||
int retval, line = tty->index;
|
||||
struct uart_state *state = drv->state + line;
|
||||
|
||||
tty->driver_data = state;
|
||||
struct uart_state *state = tty->driver_data;
|
||||
int retval;
|
||||
|
||||
retval = tty_port_open(&state->port, tty, filp);
|
||||
if (retval > 0)
|
||||
@@ -2448,6 +2449,7 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
|
||||
#endif
|
||||
|
||||
static const struct tty_operations uart_ops = {
|
||||
.install = uart_install,
|
||||
.open = uart_open,
|
||||
.close = uart_close,
|
||||
.write = uart_write,
|
||||
@@ -2505,7 +2507,7 @@ static const struct tty_port_operations uart_port_ops = {
|
||||
int uart_register_driver(struct uart_driver *drv)
|
||||
{
|
||||
struct tty_driver *normal;
|
||||
int i, retval;
|
||||
int i, retval = -ENOMEM;
|
||||
|
||||
BUG_ON(drv->state);
|
||||
|
||||
@@ -2557,7 +2559,7 @@ int uart_register_driver(struct uart_driver *drv)
|
||||
out_kfree:
|
||||
kfree(drv->state);
|
||||
out:
|
||||
return -ENOMEM;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
|
1056
drivers/tty/serial/sifive.c
Normal file
1056
drivers/tty/serial/sifive.c
Normal file
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
Load Diff
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* C-Brick Serial Port (and console) driver for SGI Altix machines.
|
||||
*
|
||||
|
@@ -10,6 +10,9 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma/sprd-dma.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -75,6 +78,7 @@
|
||||
|
||||
/* control register 1 */
|
||||
#define SPRD_CTL1 0x001C
|
||||
#define SPRD_DMA_EN BIT(15)
|
||||
#define RX_HW_FLOW_CTL_THLD BIT(6)
|
||||
#define RX_HW_FLOW_CTL_EN BIT(7)
|
||||
#define TX_HW_FLOW_CTL_EN BIT(8)
|
||||
@@ -86,6 +90,7 @@
|
||||
#define THLD_TX_EMPTY 0x40
|
||||
#define THLD_TX_EMPTY_SHIFT 8
|
||||
#define THLD_RX_FULL 0x40
|
||||
#define THLD_RX_FULL_MASK GENMASK(6, 0)
|
||||
|
||||
/* config baud rate register */
|
||||
#define SPRD_CLKD0 0x0024
|
||||
@@ -100,15 +105,38 @@
|
||||
#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1)
|
||||
#define SPRD_IMSR_BREAK_DETECT BIT(7)
|
||||
#define SPRD_IMSR_TIMEOUT BIT(13)
|
||||
#define SPRD_DEFAULT_SOURCE_CLK 26000000
|
||||
|
||||
#define SPRD_RX_DMA_STEP 1
|
||||
#define SPRD_RX_FIFO_FULL 1
|
||||
#define SPRD_TX_FIFO_FULL 0x20
|
||||
#define SPRD_UART_RX_SIZE (UART_XMIT_SIZE / 4)
|
||||
|
||||
struct sprd_uart_dma {
|
||||
struct dma_chan *chn;
|
||||
unsigned char *virt;
|
||||
dma_addr_t phys_addr;
|
||||
dma_cookie_t cookie;
|
||||
u32 trans_len;
|
||||
bool enable;
|
||||
};
|
||||
|
||||
struct sprd_uart_port {
|
||||
struct uart_port port;
|
||||
char name[16];
|
||||
struct clk *clk;
|
||||
struct sprd_uart_dma tx_dma;
|
||||
struct sprd_uart_dma rx_dma;
|
||||
dma_addr_t pos;
|
||||
unsigned char *rx_buf_tail;
|
||||
};
|
||||
|
||||
static struct sprd_uart_port *sprd_port[UART_NR_MAX];
|
||||
static int sprd_ports_num;
|
||||
|
||||
static int sprd_start_dma_rx(struct uart_port *port);
|
||||
static int sprd_tx_dma_config(struct uart_port *port);
|
||||
|
||||
static inline unsigned int serial_in(struct uart_port *port,
|
||||
unsigned int offset)
|
||||
{
|
||||
@@ -139,35 +167,15 @@ static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
static void sprd_stop_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned int ien, iclr;
|
||||
|
||||
iclr = serial_in(port, SPRD_ICLR);
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
|
||||
iclr |= SPRD_IEN_TX_EMPTY;
|
||||
ien &= ~SPRD_IEN_TX_EMPTY;
|
||||
|
||||
serial_out(port, SPRD_ICLR, iclr);
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
}
|
||||
|
||||
static void sprd_start_tx(struct uart_port *port)
|
||||
{
|
||||
unsigned int ien;
|
||||
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
if (!(ien & SPRD_IEN_TX_EMPTY)) {
|
||||
ien |= SPRD_IEN_TX_EMPTY;
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
}
|
||||
}
|
||||
|
||||
static void sprd_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
unsigned int ien, iclr;
|
||||
|
||||
if (sp->rx_dma.enable)
|
||||
dmaengine_terminate_all(sp->rx_dma.chn);
|
||||
|
||||
iclr = serial_in(port, SPRD_ICLR);
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
|
||||
@@ -178,6 +186,370 @@ static void sprd_stop_rx(struct uart_port *port)
|
||||
serial_out(port, SPRD_ICLR, iclr);
|
||||
}
|
||||
|
||||
static void sprd_uart_dma_enable(struct uart_port *port, bool enable)
|
||||
{
|
||||
u32 val = serial_in(port, SPRD_CTL1);
|
||||
|
||||
if (enable)
|
||||
val |= SPRD_DMA_EN;
|
||||
else
|
||||
val &= ~SPRD_DMA_EN;
|
||||
|
||||
serial_out(port, SPRD_CTL1, val);
|
||||
}
|
||||
|
||||
static void sprd_stop_tx_dma(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
struct dma_tx_state state;
|
||||
u32 trans_len;
|
||||
|
||||
dmaengine_pause(sp->tx_dma.chn);
|
||||
|
||||
dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state);
|
||||
if (state.residue) {
|
||||
trans_len = state.residue - sp->tx_dma.phys_addr;
|
||||
xmit->tail = (xmit->tail + trans_len) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx += trans_len;
|
||||
dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
|
||||
sp->tx_dma.trans_len, DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
dmaengine_terminate_all(sp->tx_dma.chn);
|
||||
sp->tx_dma.trans_len = 0;
|
||||
}
|
||||
|
||||
static int sprd_tx_buf_remap(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
|
||||
sp->tx_dma.trans_len =
|
||||
CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
|
||||
|
||||
sp->tx_dma.phys_addr = dma_map_single(port->dev,
|
||||
(void *)&(xmit->buf[xmit->tail]),
|
||||
sp->tx_dma.trans_len,
|
||||
DMA_TO_DEVICE);
|
||||
return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);
|
||||
}
|
||||
|
||||
static void sprd_complete_tx_dma(void *data)
|
||||
{
|
||||
struct uart_port *port = (struct uart_port *)data;
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
|
||||
sp->tx_dma.trans_len, DMA_TO_DEVICE);
|
||||
|
||||
xmit->tail = (xmit->tail + sp->tx_dma.trans_len) & (UART_XMIT_SIZE - 1);
|
||||
port->icount.tx += sp->tx_dma.trans_len;
|
||||
|
||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(port);
|
||||
|
||||
if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) ||
|
||||
sprd_tx_dma_config(port))
|
||||
sp->tx_dma.trans_len = 0;
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int sprd_uart_dma_submit(struct uart_port *port,
|
||||
struct sprd_uart_dma *ud, u32 trans_len,
|
||||
enum dma_transfer_direction direction,
|
||||
dma_async_tx_callback callback)
|
||||
{
|
||||
struct dma_async_tx_descriptor *dma_des;
|
||||
unsigned long flags;
|
||||
|
||||
flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE,
|
||||
SPRD_DMA_NO_TRG,
|
||||
SPRD_DMA_FRAG_REQ,
|
||||
SPRD_DMA_TRANS_INT);
|
||||
|
||||
dma_des = dmaengine_prep_slave_single(ud->chn, ud->phys_addr, trans_len,
|
||||
direction, flags);
|
||||
if (!dma_des)
|
||||
return -ENODEV;
|
||||
|
||||
dma_des->callback = callback;
|
||||
dma_des->callback_param = port;
|
||||
|
||||
ud->cookie = dmaengine_submit(dma_des);
|
||||
if (dma_submit_error(ud->cookie))
|
||||
return dma_submit_error(ud->cookie);
|
||||
|
||||
dma_async_issue_pending(ud->chn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_tx_dma_config(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
u32 burst = sp->tx_dma.trans_len > SPRD_TX_FIFO_FULL ?
|
||||
SPRD_TX_FIFO_FULL : sp->tx_dma.trans_len;
|
||||
int ret;
|
||||
struct dma_slave_config cfg = {
|
||||
.dst_addr = port->mapbase + SPRD_TXD,
|
||||
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
|
||||
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
|
||||
.src_maxburst = burst,
|
||||
};
|
||||
|
||||
ret = dmaengine_slave_config(sp->tx_dma.chn, &cfg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprd_uart_dma_submit(port, &sp->tx_dma, sp->tx_dma.trans_len,
|
||||
DMA_MEM_TO_DEV, sprd_complete_tx_dma);
|
||||
}
|
||||
|
||||
static void sprd_start_tx_dma(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
struct circ_buf *xmit = &port->state->xmit;
|
||||
|
||||
if (port->x_char) {
|
||||
serial_out(port, SPRD_TXD, port->x_char);
|
||||
port->icount.tx++;
|
||||
port->x_char = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
||||
sprd_stop_tx_dma(port);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sp->tx_dma.trans_len)
|
||||
return;
|
||||
|
||||
if (sprd_tx_buf_remap(port) || sprd_tx_dma_config(port))
|
||||
sp->tx_dma.trans_len = 0;
|
||||
}
|
||||
|
||||
static void sprd_rx_full_thld(struct uart_port *port, u32 thld)
|
||||
{
|
||||
u32 val = serial_in(port, SPRD_CTL2);
|
||||
|
||||
val &= ~THLD_RX_FULL_MASK;
|
||||
val |= thld & THLD_RX_FULL_MASK;
|
||||
serial_out(port, SPRD_CTL2, val);
|
||||
}
|
||||
|
||||
static int sprd_rx_alloc_buf(struct sprd_uart_port *sp)
|
||||
{
|
||||
sp->rx_dma.virt = dma_alloc_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
|
||||
&sp->rx_dma.phys_addr, GFP_KERNEL);
|
||||
if (!sp->rx_dma.virt)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_rx_free_buf(struct sprd_uart_port *sp)
|
||||
{
|
||||
if (sp->rx_dma.virt)
|
||||
dma_free_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
|
||||
sp->rx_dma.virt, sp->rx_dma.phys_addr);
|
||||
|
||||
}
|
||||
|
||||
static int sprd_rx_dma_config(struct uart_port *port, u32 burst)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
struct dma_slave_config cfg = {
|
||||
.src_addr = port->mapbase + SPRD_RXD,
|
||||
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
|
||||
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
|
||||
.src_maxburst = burst,
|
||||
};
|
||||
|
||||
return dmaengine_slave_config(sp->rx_dma.chn, &cfg);
|
||||
}
|
||||
|
||||
static void sprd_uart_dma_rx(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
struct tty_port *tty = &port->state->port;
|
||||
|
||||
port->icount.rx += sp->rx_dma.trans_len;
|
||||
tty_insert_flip_string(tty, sp->rx_buf_tail, sp->rx_dma.trans_len);
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
||||
static void sprd_uart_dma_irq(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
struct dma_tx_state state;
|
||||
enum dma_status status;
|
||||
|
||||
status = dmaengine_tx_status(sp->rx_dma.chn,
|
||||
sp->rx_dma.cookie, &state);
|
||||
if (status == DMA_ERROR)
|
||||
sprd_stop_rx(port);
|
||||
|
||||
if (!state.residue && sp->pos == sp->rx_dma.phys_addr)
|
||||
return;
|
||||
|
||||
if (!state.residue) {
|
||||
sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
|
||||
sp->rx_dma.phys_addr - sp->pos;
|
||||
sp->pos = sp->rx_dma.phys_addr;
|
||||
} else {
|
||||
sp->rx_dma.trans_len = state.residue - sp->pos;
|
||||
sp->pos = state.residue;
|
||||
}
|
||||
|
||||
sprd_uart_dma_rx(port);
|
||||
sp->rx_buf_tail += sp->rx_dma.trans_len;
|
||||
}
|
||||
|
||||
static void sprd_complete_rx_dma(void *data)
|
||||
{
|
||||
struct uart_port *port = (struct uart_port *)data;
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
struct dma_tx_state state;
|
||||
enum dma_status status;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
status = dmaengine_tx_status(sp->rx_dma.chn,
|
||||
sp->rx_dma.cookie, &state);
|
||||
if (status != DMA_COMPLETE) {
|
||||
sprd_stop_rx(port);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sp->pos != sp->rx_dma.phys_addr) {
|
||||
sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
|
||||
sp->rx_dma.phys_addr - sp->pos;
|
||||
sprd_uart_dma_rx(port);
|
||||
sp->rx_buf_tail += sp->rx_dma.trans_len;
|
||||
}
|
||||
|
||||
if (sprd_start_dma_rx(port))
|
||||
sprd_stop_rx(port);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int sprd_start_dma_rx(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
int ret;
|
||||
|
||||
if (!sp->rx_dma.enable)
|
||||
return 0;
|
||||
|
||||
sp->pos = sp->rx_dma.phys_addr;
|
||||
sp->rx_buf_tail = sp->rx_dma.virt;
|
||||
sprd_rx_full_thld(port, SPRD_RX_FIFO_FULL);
|
||||
ret = sprd_rx_dma_config(port, SPRD_RX_DMA_STEP);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprd_uart_dma_submit(port, &sp->rx_dma, SPRD_UART_RX_SIZE,
|
||||
DMA_DEV_TO_MEM, sprd_complete_rx_dma);
|
||||
}
|
||||
|
||||
static void sprd_release_dma(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
|
||||
sprd_uart_dma_enable(port, false);
|
||||
|
||||
if (sp->rx_dma.enable)
|
||||
dma_release_channel(sp->rx_dma.chn);
|
||||
|
||||
if (sp->tx_dma.enable)
|
||||
dma_release_channel(sp->tx_dma.chn);
|
||||
|
||||
sp->tx_dma.enable = false;
|
||||
sp->rx_dma.enable = false;
|
||||
}
|
||||
|
||||
static void sprd_request_dma(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
|
||||
sp->tx_dma.enable = true;
|
||||
sp->rx_dma.enable = true;
|
||||
|
||||
sp->tx_dma.chn = dma_request_chan(port->dev, "tx");
|
||||
if (IS_ERR(sp->tx_dma.chn)) {
|
||||
dev_err(port->dev, "request TX DMA channel failed, ret = %ld\n",
|
||||
PTR_ERR(sp->tx_dma.chn));
|
||||
sp->tx_dma.enable = false;
|
||||
}
|
||||
|
||||
sp->rx_dma.chn = dma_request_chan(port->dev, "rx");
|
||||
if (IS_ERR(sp->rx_dma.chn)) {
|
||||
dev_err(port->dev, "request RX DMA channel failed, ret = %ld\n",
|
||||
PTR_ERR(sp->rx_dma.chn));
|
||||
sp->rx_dma.enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void sprd_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
|
||||
port);
|
||||
unsigned int ien, iclr;
|
||||
|
||||
if (sp->tx_dma.enable) {
|
||||
sprd_stop_tx_dma(port);
|
||||
return;
|
||||
}
|
||||
|
||||
iclr = serial_in(port, SPRD_ICLR);
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
|
||||
iclr |= SPRD_IEN_TX_EMPTY;
|
||||
ien &= ~SPRD_IEN_TX_EMPTY;
|
||||
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
serial_out(port, SPRD_ICLR, iclr);
|
||||
}
|
||||
|
||||
static void sprd_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
|
||||
port);
|
||||
unsigned int ien;
|
||||
|
||||
if (sp->tx_dma.enable) {
|
||||
sprd_start_tx_dma(port);
|
||||
return;
|
||||
}
|
||||
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
if (!(ien & SPRD_IEN_TX_EMPTY)) {
|
||||
ien |= SPRD_IEN_TX_EMPTY;
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
}
|
||||
}
|
||||
|
||||
/* The Sprd serial does not support this function. */
|
||||
static void sprd_break_ctl(struct uart_port *port, int break_state)
|
||||
{
|
||||
@@ -218,9 +590,16 @@ static int handle_lsr_errors(struct uart_port *port,
|
||||
|
||||
static inline void sprd_rx(struct uart_port *port)
|
||||
{
|
||||
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
|
||||
port);
|
||||
struct tty_port *tty = &port->state->port;
|
||||
unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT;
|
||||
|
||||
if (sp->rx_dma.enable) {
|
||||
sprd_uart_dma_irq(port);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) &&
|
||||
max_count--) {
|
||||
lsr = serial_in(port, SPRD_LSR);
|
||||
@@ -304,6 +683,25 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void sprd_uart_dma_startup(struct uart_port *port,
|
||||
struct sprd_uart_port *sp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sprd_request_dma(port);
|
||||
if (!(sp->rx_dma.enable || sp->tx_dma.enable))
|
||||
return;
|
||||
|
||||
ret = sprd_start_dma_rx(port);
|
||||
if (ret) {
|
||||
sp->rx_dma.enable = false;
|
||||
dma_release_channel(sp->rx_dma.chn);
|
||||
dev_warn(port->dev, "fail to start RX dma mode\n");
|
||||
}
|
||||
|
||||
sprd_uart_dma_enable(port, true);
|
||||
}
|
||||
|
||||
static int sprd_startup(struct uart_port *port)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -332,6 +730,9 @@ static int sprd_startup(struct uart_port *port)
|
||||
/* allocate irq */
|
||||
sp = container_of(port, struct sprd_uart_port, port);
|
||||
snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line);
|
||||
|
||||
sprd_uart_dma_startup(port, sp);
|
||||
|
||||
ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq,
|
||||
IRQF_SHARED, sp->name, port);
|
||||
if (ret) {
|
||||
@@ -346,7 +747,9 @@ static int sprd_startup(struct uart_port *port)
|
||||
/* enable interrupt */
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
ien = serial_in(port, SPRD_IEN);
|
||||
ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
|
||||
ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
|
||||
if (!sp->rx_dma.enable)
|
||||
ien |= SPRD_IEN_RX_FULL;
|
||||
serial_out(port, SPRD_IEN, ien);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
@@ -355,6 +758,7 @@ static int sprd_startup(struct uart_port *port)
|
||||
|
||||
static void sprd_shutdown(struct uart_port *port)
|
||||
{
|
||||
sprd_release_dma(port);
|
||||
serial_out(port, SPRD_IEN, 0);
|
||||
serial_out(port, SPRD_ICLR, ~0);
|
||||
devm_free_irq(port->dev, port->irq, port);
|
||||
@@ -491,6 +895,22 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_pm(struct uart_port *port, unsigned int state,
|
||||
unsigned int oldstate)
|
||||
{
|
||||
struct sprd_uart_port *sup =
|
||||
container_of(port, struct sprd_uart_port, port);
|
||||
|
||||
switch (state) {
|
||||
case UART_PM_STATE_ON:
|
||||
clk_prepare_enable(sup->clk);
|
||||
break;
|
||||
case UART_PM_STATE_OFF:
|
||||
clk_disable_unprepare(sup->clk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct uart_ops serial_sprd_ops = {
|
||||
.tx_empty = sprd_tx_empty,
|
||||
.get_mctrl = sprd_get_mctrl,
|
||||
@@ -507,6 +927,7 @@ static const struct uart_ops serial_sprd_ops = {
|
||||
.request_port = sprd_request_port,
|
||||
.config_port = sprd_config_port,
|
||||
.verify_port = sprd_verify_port,
|
||||
.pm = sprd_pm,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_SPRD_CONSOLE
|
||||
@@ -668,6 +1089,43 @@ static int sprd_remove(struct platform_device *dev)
|
||||
if (!sprd_ports_num)
|
||||
uart_unregister_driver(&sprd_uart_driver);
|
||||
|
||||
sprd_rx_free_buf(sup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_clk_init(struct uart_port *uport)
|
||||
{
|
||||
struct clk *clk_uart, *clk_parent;
|
||||
struct sprd_uart_port *u = sprd_port[uport->line];
|
||||
|
||||
clk_uart = devm_clk_get(uport->dev, "uart");
|
||||
if (IS_ERR(clk_uart)) {
|
||||
dev_warn(uport->dev, "uart%d can't get uart clock\n",
|
||||
uport->line);
|
||||
clk_uart = NULL;
|
||||
}
|
||||
|
||||
clk_parent = devm_clk_get(uport->dev, "source");
|
||||
if (IS_ERR(clk_parent)) {
|
||||
dev_warn(uport->dev, "uart%d can't get source clock\n",
|
||||
uport->line);
|
||||
clk_parent = NULL;
|
||||
}
|
||||
|
||||
if (!clk_uart || clk_set_parent(clk_uart, clk_parent))
|
||||
uport->uartclk = SPRD_DEFAULT_SOURCE_CLK;
|
||||
else
|
||||
uport->uartclk = clk_get_rate(clk_uart);
|
||||
|
||||
u->clk = devm_clk_get(uport->dev, "enable");
|
||||
if (IS_ERR(u->clk)) {
|
||||
if (PTR_ERR(u->clk) != -EPROBE_DEFER)
|
||||
dev_err(uport->dev, "uart%d can't get enable clock\n",
|
||||
uport->line);
|
||||
return PTR_ERR(u->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -675,7 +1133,6 @@ static int sprd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct uart_port *up;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
int index;
|
||||
int ret;
|
||||
@@ -704,9 +1161,9 @@ static int sprd_probe(struct platform_device *pdev)
|
||||
up->ops = &serial_sprd_ops;
|
||||
up->flags = UPF_BOOT_AUTOCONF;
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR_OR_NULL(clk))
|
||||
up->uartclk = clk_get_rate(clk);
|
||||
ret = sprd_clk_init(up);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
up->membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
@@ -722,6 +1179,14 @@ static int sprd_probe(struct platform_device *pdev)
|
||||
}
|
||||
up->irq = irq;
|
||||
|
||||
/*
|
||||
* Allocate one dma buffer to prepare for receive transfer, in case
|
||||
* memory allocation failure at runtime.
|
||||
*/
|
||||
ret = sprd_rx_alloc_buf(sprd_port[index]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!sprd_ports_num) {
|
||||
ret = uart_register_driver(&sprd_uart_driver);
|
||||
if (ret < 0) {
|
||||
|
@@ -1081,7 +1081,7 @@ static int qe_uart_verify_port(struct uart_port *port,
|
||||
}
|
||||
/* UART operations
|
||||
*
|
||||
* Details on these functions can be found in Documentation/serial/driver
|
||||
* Details on these functions can be found in Documentation/serial/driver.rst
|
||||
*/
|
||||
static const struct uart_ops qe_uart_pops = {
|
||||
.tx_empty = qe_uart_tx_empty,
|
||||
|
@@ -193,6 +193,7 @@ struct cdns_uart {
|
||||
int id;
|
||||
struct notifier_block clk_rate_change_nb;
|
||||
u32 quirks;
|
||||
bool cts_override;
|
||||
};
|
||||
struct cdns_platform_data {
|
||||
u32 quirks;
|
||||
@@ -1000,6 +1001,11 @@ static void cdns_uart_config_port(struct uart_port *port, int flags)
|
||||
*/
|
||||
static unsigned int cdns_uart_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
struct cdns_uart *cdns_uart_data = port->private_data;
|
||||
|
||||
if (cdns_uart_data->cts_override)
|
||||
return 0;
|
||||
|
||||
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
|
||||
}
|
||||
|
||||
@@ -1007,6 +1013,10 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
u32 val;
|
||||
u32 mode_reg;
|
||||
struct cdns_uart *cdns_uart_data = port->private_data;
|
||||
|
||||
if (cdns_uart_data->cts_override)
|
||||
return;
|
||||
|
||||
val = readl(port->membase + CDNS_UART_MODEMCR);
|
||||
mode_reg = readl(port->membase + CDNS_UART_MR);
|
||||
@@ -1665,6 +1675,8 @@ static int cdns_uart_probe(struct platform_device *pdev)
|
||||
console_port = NULL;
|
||||
#endif
|
||||
|
||||
cdns_uart_data->cts_override = of_property_read_bool(pdev->dev.of_node,
|
||||
"cts-override");
|
||||
return 0;
|
||||
|
||||
err_out_pm_disable:
|
||||
|
@@ -208,7 +208,7 @@ static struct sysrq_key_op sysrq_showlocks_op = {
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static DEFINE_SPINLOCK(show_lock);
|
||||
static DEFINE_RAW_SPINLOCK(show_lock);
|
||||
|
||||
static void showacpu(void *dummy)
|
||||
{
|
||||
@@ -218,10 +218,10 @@ static void showacpu(void *dummy)
|
||||
if (idle_cpu(smp_processor_id()))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&show_lock, flags);
|
||||
raw_spin_lock_irqsave(&show_lock, flags);
|
||||
pr_info("CPU%d:\n", smp_processor_id());
|
||||
show_stack(NULL, NULL);
|
||||
spin_unlock_irqrestore(&show_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&show_lock, flags);
|
||||
}
|
||||
|
||||
static void sysrq_showregs_othercpus(struct work_struct *dummy)
|
||||
|
@@ -1173,7 +1173,7 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
|
||||
* tty_init_termios - helper for termios setup
|
||||
* @tty: the tty to set up
|
||||
*
|
||||
* Initialise the termios structures for this tty. Thus runs under
|
||||
* Initialise the termios structure for this tty. This runs under
|
||||
* the tty_mutex currently so we can be relaxed about ordering.
|
||||
*/
|
||||
|
||||
|
@@ -44,7 +44,7 @@ int __tty_check_change(struct tty_struct *tty, int sig)
|
||||
tty_pgrp = tty->pgrp;
|
||||
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
|
||||
|
||||
if (tty_pgrp && pgrp != tty->pgrp) {
|
||||
if (tty_pgrp && pgrp != tty_pgrp) {
|
||||
if (is_ignored(sig)) {
|
||||
if (sig == SIGTTIN)
|
||||
ret = -EIO;
|
||||
@@ -313,7 +313,7 @@ void disassociate_ctty(int on_exit)
|
||||
read_unlock(&tasklist_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
*
|
||||
* no_tty - Ensure the current process does not have a controlling tty
|
||||
*/
|
||||
|
@@ -325,7 +325,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
|
||||
if (tty && C_HUPCL(tty))
|
||||
tty_port_lower_dtr_rts(port);
|
||||
|
||||
if (port->ops && port->ops->shutdown)
|
||||
if (port->ops->shutdown)
|
||||
port->ops->shutdown(port);
|
||||
}
|
||||
out:
|
||||
@@ -398,7 +398,7 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
|
||||
*/
|
||||
int tty_port_carrier_raised(struct tty_port *port)
|
||||
{
|
||||
if (!port->ops || !port->ops->carrier_raised)
|
||||
if (port->ops->carrier_raised == NULL)
|
||||
return 1;
|
||||
return port->ops->carrier_raised(port);
|
||||
}
|
||||
@@ -414,7 +414,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised);
|
||||
*/
|
||||
void tty_port_raise_dtr_rts(struct tty_port *port)
|
||||
{
|
||||
if (port->ops && port->ops->dtr_rts)
|
||||
if (port->ops->dtr_rts)
|
||||
port->ops->dtr_rts(port, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(tty_port_raise_dtr_rts);
|
||||
@@ -429,7 +429,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts);
|
||||
*/
|
||||
void tty_port_lower_dtr_rts(struct tty_port *port)
|
||||
{
|
||||
if (port->ops && port->ops->dtr_rts)
|
||||
if (port->ops->dtr_rts)
|
||||
port->ops->dtr_rts(port, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(tty_port_lower_dtr_rts);
|
||||
@@ -684,7 +684,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
|
||||
|
||||
if (!tty_port_initialized(port)) {
|
||||
clear_bit(TTY_IO_ERROR, &tty->flags);
|
||||
if (port->ops && port->ops->activate) {
|
||||
if (port->ops->activate) {
|
||||
int retval = port->ops->activate(port, tty);
|
||||
if (retval) {
|
||||
mutex_unlock(&port->mutex);
|
||||
|
109
drivers/tty/ttynull.c
Normal file
109
drivers/tty/ttynull.c
Normal file
@@ -0,0 +1,109 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019 Axis Communications AB
|
||||
*
|
||||
* Based on ttyprintk.c:
|
||||
* Copyright (C) 2010 Samo Pogacnik
|
||||
*/
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
|
||||
static const struct tty_port_operations ttynull_port_ops;
|
||||
static struct tty_driver *ttynull_driver;
|
||||
static struct tty_port ttynull_port;
|
||||
|
||||
static int ttynull_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
return tty_port_open(&ttynull_port, tty, filp);
|
||||
}
|
||||
|
||||
static void ttynull_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
tty_port_close(&ttynull_port, tty, filp);
|
||||
}
|
||||
|
||||
static void ttynull_hangup(struct tty_struct *tty)
|
||||
{
|
||||
tty_port_hangup(&ttynull_port);
|
||||
}
|
||||
|
||||
static int ttynull_write(struct tty_struct *tty, const unsigned char *buf,
|
||||
int count)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static int ttynull_write_room(struct tty_struct *tty)
|
||||
{
|
||||
return 65536;
|
||||
}
|
||||
|
||||
static const struct tty_operations ttynull_ops = {
|
||||
.open = ttynull_open,
|
||||
.close = ttynull_close,
|
||||
.hangup = ttynull_hangup,
|
||||
.write = ttynull_write,
|
||||
.write_room = ttynull_write_room,
|
||||
};
|
||||
|
||||
static struct tty_driver *ttynull_device(struct console *c, int *index)
|
||||
{
|
||||
*index = 0;
|
||||
return ttynull_driver;
|
||||
}
|
||||
|
||||
static struct console ttynull_console = {
|
||||
.name = "ttynull",
|
||||
.device = ttynull_device,
|
||||
};
|
||||
|
||||
static int __init ttynull_init(void)
|
||||
{
|
||||
struct tty_driver *driver;
|
||||
int ret;
|
||||
|
||||
driver = tty_alloc_driver(1,
|
||||
TTY_DRIVER_RESET_TERMIOS |
|
||||
TTY_DRIVER_REAL_RAW |
|
||||
TTY_DRIVER_UNNUMBERED_NODE);
|
||||
if (IS_ERR(driver))
|
||||
return PTR_ERR(driver);
|
||||
|
||||
tty_port_init(&ttynull_port);
|
||||
ttynull_port.ops = &ttynull_port_ops;
|
||||
|
||||
driver->driver_name = "ttynull";
|
||||
driver->name = "ttynull";
|
||||
driver->type = TTY_DRIVER_TYPE_CONSOLE;
|
||||
driver->init_termios = tty_std_termios;
|
||||
driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
|
||||
tty_set_operations(driver, &ttynull_ops);
|
||||
tty_port_link_device(&ttynull_port, driver, 0);
|
||||
|
||||
ret = tty_register_driver(driver);
|
||||
if (ret < 0) {
|
||||
put_tty_driver(driver);
|
||||
tty_port_destroy(&ttynull_port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ttynull_driver = driver;
|
||||
register_console(&ttynull_console);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ttynull_exit(void)
|
||||
{
|
||||
unregister_console(&ttynull_console);
|
||||
tty_unregister_driver(ttynull_driver);
|
||||
put_tty_driver(ttynull_driver);
|
||||
tty_port_destroy(&ttynull_port);
|
||||
}
|
||||
|
||||
module_init(ttynull_init);
|
||||
module_exit(ttynull_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* vcc.c: sun4v virtual channel concentrator
|
||||
*
|
||||
* Copyright (C) 2017 Oracle. All rights reserved.
|
||||
|
1
drivers/tty/vt/.gitignore
vendored
1
drivers/tty/vt/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
consolemap_deftbl.c
|
||||
defkeymap.c
|
||||
|
@@ -542,7 +542,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
|
||||
if (!ct)
|
||||
return 0;
|
||||
|
||||
unilist = memdup_user(list, ct * sizeof(struct unipair));
|
||||
unilist = vmemdup_user(list, ct * sizeof(struct unipair));
|
||||
if (IS_ERR(unilist))
|
||||
return PTR_ERR(unilist);
|
||||
|
||||
@@ -641,7 +641,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
|
||||
|
||||
out_unlock:
|
||||
console_unlock();
|
||||
kfree(unilist);
|
||||
kvfree(unilist);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -743,7 +743,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni
|
||||
struct uni_pagedir *p;
|
||||
struct unipair *unilist;
|
||||
|
||||
unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
|
||||
unilist = kvmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
|
||||
if (!unilist)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -775,7 +775,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni
|
||||
if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair)))
|
||||
ret = -EFAULT;
|
||||
put_user(ect, uct);
|
||||
kfree(unilist);
|
||||
kvfree(unilist);
|
||||
return ret ? ret : (ect <= ct) ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Unicode table for IBM Codepage 437. Note that there are many more
|
||||
# substitutions that could be conceived (for example, thick-line
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Do not edit this file! It was automatically generated by */
|
||||
/* loadkeys --mktable defkeymap.map > defkeymap.c */
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Default kernel keymap. This uses 7 modifier combinations.
|
||||
keymaps 0-2,4-5,8,12
|
||||
# Change the above line into
|
||||
|
@@ -123,6 +123,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
|
||||
static struct input_handler kbd_handler;
|
||||
static DEFINE_SPINLOCK(kbd_event_lock);
|
||||
static DEFINE_SPINLOCK(led_lock);
|
||||
static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */
|
||||
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
|
||||
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
|
||||
static bool dead_key_next;
|
||||
@@ -1449,7 +1450,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
|
||||
KBD_UNICODE, ¶m);
|
||||
if (rc != NOTIFY_STOP)
|
||||
if (down && !raw_mode)
|
||||
to_utf8(vc, keysym);
|
||||
k_unicode(vc, keysym, !down);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1990,11 +1991,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
||||
char *p;
|
||||
u_char *q;
|
||||
u_char __user *up;
|
||||
int sz;
|
||||
int sz, fnw_sz;
|
||||
int delta;
|
||||
char *first_free, *fj, *fnw;
|
||||
int i, j, k;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (!capable(CAP_SYS_TTY_CONFIG))
|
||||
perm = 0;
|
||||
@@ -2037,7 +2039,14 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
||||
goto reterr;
|
||||
}
|
||||
|
||||
fnw = NULL;
|
||||
fnw_sz = 0;
|
||||
/* race aginst other writers */
|
||||
again:
|
||||
spin_lock_irqsave(&func_buf_lock, flags);
|
||||
q = func_table[i];
|
||||
|
||||
/* fj pointer to next entry after 'q' */
|
||||
first_free = funcbufptr + (funcbufsize - funcbufleft);
|
||||
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
|
||||
;
|
||||
@@ -2045,10 +2054,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
||||
fj = func_table[j];
|
||||
else
|
||||
fj = first_free;
|
||||
|
||||
/* buffer usage increase by new entry */
|
||||
delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
|
||||
|
||||
if (delta <= funcbufleft) { /* it fits in current buf */
|
||||
if (j < MAX_NR_FUNC) {
|
||||
/* make enough space for new entry at 'fj' */
|
||||
memmove(fj + delta, fj, first_free - fj);
|
||||
for (k = j; k < MAX_NR_FUNC; k++)
|
||||
if (func_table[k])
|
||||
@@ -2061,20 +2072,28 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
||||
sz = 256;
|
||||
while (sz < funcbufsize - funcbufleft + delta)
|
||||
sz <<= 1;
|
||||
fnw = kmalloc(sz, GFP_KERNEL);
|
||||
if(!fnw) {
|
||||
ret = -ENOMEM;
|
||||
goto reterr;
|
||||
if (fnw_sz != sz) {
|
||||
spin_unlock_irqrestore(&func_buf_lock, flags);
|
||||
kfree(fnw);
|
||||
fnw = kmalloc(sz, GFP_KERNEL);
|
||||
fnw_sz = sz;
|
||||
if (!fnw) {
|
||||
ret = -ENOMEM;
|
||||
goto reterr;
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (!q)
|
||||
func_table[i] = fj;
|
||||
/* copy data before insertion point to new location */
|
||||
if (fj > funcbufptr)
|
||||
memmove(fnw, funcbufptr, fj - funcbufptr);
|
||||
for (k = 0; k < j; k++)
|
||||
if (func_table[k])
|
||||
func_table[k] = fnw + (func_table[k] - funcbufptr);
|
||||
|
||||
/* copy data after insertion point to new location */
|
||||
if (first_free > fj) {
|
||||
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
|
||||
for (k = j; k < MAX_NR_FUNC; k++)
|
||||
@@ -2087,7 +2106,9 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
||||
funcbufleft = funcbufleft - delta + sz - funcbufsize;
|
||||
funcbufsize = sz;
|
||||
}
|
||||
/* finally insert item itself */
|
||||
strcpy(func_table[i], kbs->kb_string);
|
||||
spin_unlock_irqrestore(&func_buf_lock, flags);
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Provide access to virtual console memory.
|
||||
* /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
|
||||
* /dev/vcs: the screen as it is being viewed right now (possibly scrolled)
|
||||
* /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
|
||||
* [minor: N]
|
||||
*
|
||||
|
@@ -4180,8 +4180,6 @@ void do_blank_screen(int entering_gfx)
|
||||
return;
|
||||
}
|
||||
|
||||
if (blank_state != blank_normal_wait)
|
||||
return;
|
||||
blank_state = blank_off;
|
||||
|
||||
/* don't blank graphics */
|
||||
|
Reference in New Issue
Block a user