mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00
TTY/Serial driver updates for 6.15-rc1
Here is the big set of serial and tty driver updates for 6.15-rc1. Include in here are the following: - more great tty layer cleanups from Jiri. Someday this will be done, but that's not going to be any year soon... - kdb debug driver reverts to fix a reported issue - lots of .dts binding updates for different devices with serial devices - lots of tiny updates and tweaks and a few bugfixes for different serial drivers. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZ+2YPA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yn2OQCgvxCyoeuNPuV4X89JdrgocMTMyTYAn15pGgDa r7w9UDO/D7UqRnKEnFy+ =lJwK -----END PGP SIGNATURE----- Merge tag 'tty-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty Pull tty/serial driver updates from Greg KH: "Here is the big set of serial and tty driver updates for 6.15-rc1. Include in here are the following: - more great tty layer cleanups from Jiri. Someday this will be done, but that's not going to be any year soon... - kdb debug driver reverts to fix a reported issue - lots of .dts binding updates for different devices with serial devices - lots of tiny updates and tweaks and a few bugfixes for different serial drivers. All of these have been in linux-next for a while with no reported issues" * tag 'tty-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (79 commits) tty: serial: fsl_lpuart: Fix unused variable 'sport' build warning serial: stm32: do not deassert RS485 RTS GPIO prematurely serial: 8250: add driver for NI UARTs dt-bindings: serial: snps-dw-apb-uart: document RZ/N1 binding without DMA serial: icom: fix code format problems serial: sh-sci: Save and restore more registers tty: serial: pl011: remove incorrect of_match_ptr annotation dt-bindings: serial: snps-dw-apb-uart: Add support for rk3562 tty: serial: lpuart: only disable CTS instead of overwriting the whole UARTMODIR register tty: caif: removed unused function debugfs_tx() serial: 8250_dma: terminate correct DMA in tx_dma_flush() tty: serial: fsl_lpuart: rename register variables more specifically tty: serial: fsl_lpuart: use port struct directly to simply code tty: serial: fsl_lpuart: Use u32 and u8 for register variables tty: serial: fsl_lpuart: disable transmitter before changing RS485 related registers tty: serial: 8250: Add Brainboxes XC devices dt-bindings: serial: fsl-lpuart: support i.MX94 tty: serial: 8250: Add some more device IDs dt-bindings: serial: samsung: add exynos7870-uart compatible serial: 8250_dw: Comment possible corner cases in serial_out() implementation ...
This commit is contained in:
commit
ddd0172f18
@ -77,7 +77,6 @@ properties:
|
||||
- altr,16550-FIFO64
|
||||
- altr,16550-FIFO128
|
||||
- fsl,16550-FIFO64
|
||||
- fsl,ns16550
|
||||
- andestech,uart16550
|
||||
- nxp,lpc1850-uart
|
||||
- opencores,uart16550-rtlsvn105
|
||||
@ -86,6 +85,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- ns16750
|
||||
- fsl,ns16550
|
||||
- cavium,octeon-3860-uart
|
||||
- xlnx,xps-uart16550-2.00.b
|
||||
- ralink,rt2880-uart
|
||||
|
@ -30,6 +30,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx93-lpuart
|
||||
- fsl,imx94-lpuart
|
||||
- fsl,imx95-lpuart
|
||||
- const: fsl,imx8ulp-lpuart
|
||||
- const: fsl,imx7ulp-lpuart
|
||||
|
@ -0,0 +1,73 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/serial/nvidia,tegra264-utc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NVIDIA Tegra UTC (UART Trace Controller) client
|
||||
|
||||
maintainers:
|
||||
- Kartik Rajput <kkartik@nvidia.com>
|
||||
- Thierry Reding <thierry.reding@gmail.com>
|
||||
- Jonathan Hunter <jonathanh@nvidia.com>
|
||||
|
||||
description:
|
||||
Represents a client interface of the Tegra UTC (UART Trace Controller). The
|
||||
Tegra UTC allows multiple clients within the Tegra SoC to share a physical
|
||||
UART interface. It supports up to 16 clients. Each client operates as an
|
||||
independent UART endpoint with a dedicated interrupt and 128-character TX/RX
|
||||
FIFOs.
|
||||
|
||||
The Tegra UTC clients use 8-N-1 configuration and operates on a baudrate
|
||||
configured by the bootloader at the controller level.
|
||||
|
||||
allOf:
|
||||
- $ref: serial.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: nvidia,tegra264-utc
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: TX region.
|
||||
- description: RX region.
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
tx-threshold:
|
||||
minimum: 1
|
||||
maximum: 128
|
||||
|
||||
rx-threshold:
|
||||
minimum: 1
|
||||
maximum: 128
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- tx-threshold
|
||||
- rx-threshold
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
tegra_utc: serial@c4e0000 {
|
||||
compatible = "nvidia,tegra264-utc";
|
||||
reg = <0xc4e0000 0x8000>, <0xc4e8000 0x8000>;
|
||||
reg-names = "tx", "rx";
|
||||
interrupts = <GIC_SPI 514 IRQ_TYPE_LEVEL_HIGH>;
|
||||
tx-threshold = <4>;
|
||||
rx-threshold = <4>;
|
||||
};
|
@ -92,6 +92,9 @@ properties:
|
||||
3000ms.
|
||||
default: 3000
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
|
@ -42,6 +42,10 @@ properties:
|
||||
- samsung,exynosautov9-uart
|
||||
- samsung,exynosautov920-uart
|
||||
- const: samsung,exynos850-uart
|
||||
- items:
|
||||
- enum:
|
||||
- samsung,exynos7870-uart
|
||||
- const: samsung,exynos8895-uart
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -13,6 +13,20 @@ allOf:
|
||||
- $ref: serial.yaml#
|
||||
- $ref: rs485.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r9a06g032-uart
|
||||
- renesas,r9a06g033-uart
|
||||
- const: renesas,rzn1-uart
|
||||
- const: snps,dw-apb-uart
|
||||
then:
|
||||
properties:
|
||||
dmas: false
|
||||
dma-names: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -30,6 +44,12 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a06g032-uart
|
||||
- renesas,r9a06g033-uart
|
||||
- const: renesas,rzn1-uart
|
||||
- const: snps,dw-apb-uart
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a06g032-uart
|
||||
@ -51,6 +71,7 @@ properties:
|
||||
- rockchip,rk3368-uart
|
||||
- rockchip,rk3399-uart
|
||||
- rockchip,rk3528-uart
|
||||
- rockchip,rk3562-uart
|
||||
- rockchip,rk3568-uart
|
||||
- rockchip,rk3576-uart
|
||||
- rockchip,rk3588-uart
|
||||
|
@ -17,13 +17,18 @@ properties:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- sprd,sc9632-uart
|
||||
- sprd,ums9632-uart
|
||||
- const: sprd,sc9632-uart
|
||||
- items:
|
||||
- enum:
|
||||
- sprd,sc9860-uart
|
||||
- sprd,sc9863a-uart
|
||||
- sprd,ums512-uart
|
||||
- sprd,ums9620-uart
|
||||
- const: sprd,sc9836-uart
|
||||
- const: sprd,sc9836-uart
|
||||
- enum:
|
||||
- sprd,sc9632-uart
|
||||
- sprd,sc9836-uart
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -101,6 +101,6 @@ Modem control lines via GPIO
|
||||
Some helpers are provided in order to set/get modem control lines via GPIO.
|
||||
|
||||
.. kernel-doc:: drivers/tty/serial/serial_mctrl_gpio.c
|
||||
:identifiers: mctrl_gpio_init mctrl_gpio_free mctrl_gpio_to_gpiod
|
||||
:identifiers: mctrl_gpio_init mctrl_gpio_to_gpiod
|
||||
mctrl_gpio_set mctrl_gpio_get mctrl_gpio_enable_ms
|
||||
mctrl_gpio_disable_ms
|
||||
mctrl_gpio_disable_ms_sync mctrl_gpio_disable_ms_no_sync
|
||||
|
@ -25,6 +25,8 @@ freed.
|
||||
For reference, both allocation and deallocation functions are explained here in
|
||||
detail:
|
||||
|
||||
.. kernel-doc:: include/linux/tty_driver.h
|
||||
:identifiers: tty_alloc_driver
|
||||
.. kernel-doc:: drivers/tty/tty_io.c
|
||||
:identifiers: __tty_alloc_driver tty_driver_kref_put
|
||||
|
||||
@ -35,7 +37,7 @@ Here comes the documentation of flags accepted by tty_alloc_driver() (or
|
||||
__tty_alloc_driver()):
|
||||
|
||||
.. kernel-doc:: include/linux/tty_driver.h
|
||||
:doc: TTY Driver Flags
|
||||
:identifiers: tty_driver_flag
|
||||
|
||||
----
|
||||
|
||||
|
@ -72,7 +72,7 @@ TTY Struct Flags
|
||||
================
|
||||
|
||||
.. kernel-doc:: include/linux/tty.h
|
||||
:doc: TTY Struct Flags
|
||||
:identifiers: tty_struct_flags
|
||||
|
||||
TTY Struct Reference
|
||||
====================
|
||||
|
@ -16550,6 +16550,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next
|
||||
F: drivers/mtd/nand/
|
||||
F: include/linux/mtd/*nand*.h
|
||||
|
||||
NATIONAL INSTRUMENTS SERIAL DRIVER
|
||||
M: Chaitanya Vadrevu <chaitanya.vadrevu@emerson.com>
|
||||
L: linux-serial@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/tty/serial/8250/8250_ni.c
|
||||
|
||||
NATIVE INSTRUMENTS USB SOUND INTERFACE DRIVER
|
||||
M: Daniel Mack <zonque@gmail.com>
|
||||
L: linux-sound@vger.kernel.org
|
||||
|
@ -196,40 +196,44 @@ static const struct tty_operations srmcons_ops = {
|
||||
static int __init
|
||||
srmcons_init(void)
|
||||
{
|
||||
struct tty_driver *driver;
|
||||
int err;
|
||||
|
||||
timer_setup(&srmcons_singleton.timer, srmcons_receive_chars, 0);
|
||||
if (srm_is_registered_console) {
|
||||
struct tty_driver *driver;
|
||||
int err;
|
||||
|
||||
driver = tty_alloc_driver(MAX_SRM_CONSOLE_DEVICES, 0);
|
||||
if (IS_ERR(driver))
|
||||
return PTR_ERR(driver);
|
||||
if (!srm_is_registered_console)
|
||||
return -ENODEV;
|
||||
|
||||
tty_port_init(&srmcons_singleton.port);
|
||||
driver = tty_alloc_driver(MAX_SRM_CONSOLE_DEVICES, 0);
|
||||
if (IS_ERR(driver))
|
||||
return PTR_ERR(driver);
|
||||
|
||||
driver->driver_name = "srm";
|
||||
driver->name = "srm";
|
||||
driver->major = 0; /* dynamic */
|
||||
driver->minor_start = 0;
|
||||
driver->type = TTY_DRIVER_TYPE_SYSTEM;
|
||||
driver->subtype = SYSTEM_TYPE_SYSCONS;
|
||||
driver->init_termios = tty_std_termios;
|
||||
tty_set_operations(driver, &srmcons_ops);
|
||||
tty_port_link_device(&srmcons_singleton.port, driver, 0);
|
||||
err = tty_register_driver(driver);
|
||||
if (err) {
|
||||
tty_driver_kref_put(driver);
|
||||
tty_port_destroy(&srmcons_singleton.port);
|
||||
return err;
|
||||
}
|
||||
srmcons_driver = driver;
|
||||
}
|
||||
tty_port_init(&srmcons_singleton.port);
|
||||
|
||||
return -ENODEV;
|
||||
driver->driver_name = "srm";
|
||||
driver->name = "srm";
|
||||
driver->major = 0; /* dynamic */
|
||||
driver->minor_start = 0;
|
||||
driver->type = TTY_DRIVER_TYPE_SYSTEM;
|
||||
driver->subtype = SYSTEM_TYPE_SYSCONS;
|
||||
driver->init_termios = tty_std_termios;
|
||||
tty_set_operations(driver, &srmcons_ops);
|
||||
tty_port_link_device(&srmcons_singleton.port, driver, 0);
|
||||
err = tty_register_driver(driver);
|
||||
if (err)
|
||||
goto err_free_drv;
|
||||
|
||||
srmcons_driver = driver;
|
||||
|
||||
return 0;
|
||||
err_free_drv:
|
||||
tty_driver_kref_put(driver);
|
||||
tty_port_destroy(&srmcons_singleton.port);
|
||||
|
||||
return err;
|
||||
}
|
||||
device_initcall(srmcons_init);
|
||||
|
||||
|
||||
/*
|
||||
* The console driver
|
||||
*/
|
||||
|
@ -330,7 +330,7 @@ static int ldisc_open(struct tty_struct *tty)
|
||||
ser->tty = tty_kref_get(tty);
|
||||
ser->dev = dev;
|
||||
debugfs_init(ser, tty);
|
||||
tty->receive_room = N_TTY_BUF_SIZE;
|
||||
tty->receive_room = 4096;
|
||||
tty->disc_data = ser;
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
rtnl_lock();
|
||||
|
@ -948,7 +948,8 @@ static int gb_tty_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0);
|
||||
gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, TTY_DRIVER_REAL_RAW |
|
||||
TTY_DRIVER_DYNAMIC_DEV);
|
||||
if (IS_ERR(gb_tty_driver)) {
|
||||
pr_err("Can not allocate tty driver\n");
|
||||
retval = -ENOMEM;
|
||||
@ -961,7 +962,6 @@ static int gb_tty_init(void)
|
||||
gb_tty_driver->minor_start = 0;
|
||||
gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
gb_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
gb_tty_driver->init_termios = tty_std_termios;
|
||||
gb_tty_driver->init_termios.c_cflag = B9600 | CS8 |
|
||||
CREAD | HUPCL | CLOCAL;
|
||||
|
@ -210,7 +210,7 @@ config SERIAL_NONSTANDARD
|
||||
|
||||
config MOXA_INTELLIO
|
||||
tristate "Moxa Intellio support"
|
||||
depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
|
||||
depends on SERIAL_NONSTANDARD && PCI
|
||||
select FW_LOADER
|
||||
help
|
||||
Say Y here if you have a Moxa Intellio multiport serial card.
|
||||
|
@ -43,15 +43,6 @@
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define MOXA 0x400
|
||||
#define MOXA_GET_IQUEUE (MOXA + 1) /* get input buffered count */
|
||||
#define MOXA_GET_OQUEUE (MOXA + 2) /* get output buffered count */
|
||||
#define MOXA_GETDATACOUNT (MOXA + 23)
|
||||
#define MOXA_GET_IOQUEUE (MOXA + 27)
|
||||
#define MOXA_FLUSH_QUEUE (MOXA + 28)
|
||||
#define MOXA_GETMSTATUS (MOXA + 65)
|
||||
|
||||
/*
|
||||
* System Configuration
|
||||
@ -347,8 +338,6 @@
|
||||
#define MX_PARMARK 0xA0
|
||||
#define MX_PARSPACE 0x20
|
||||
|
||||
#define MOXA_VERSION "6.0k"
|
||||
|
||||
#define MOXA_FW_HDRLEN 32
|
||||
|
||||
#define MOXAMAJOR 172
|
||||
@ -357,33 +346,21 @@
|
||||
#define MAX_PORTS_PER_BOARD 32 /* Don't change this value */
|
||||
#define MAX_PORTS (MAX_BOARDS * MAX_PORTS_PER_BOARD)
|
||||
|
||||
#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \
|
||||
(brd)->boardType == MOXA_BOARD_C320_PCI)
|
||||
|
||||
/*
|
||||
* Define the Moxa PCI vendor and device IDs.
|
||||
*/
|
||||
#define MOXA_BUS_TYPE_ISA 0
|
||||
#define MOXA_BUS_TYPE_PCI 1
|
||||
#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_PCI)
|
||||
|
||||
enum {
|
||||
MOXA_BOARD_C218_PCI = 1,
|
||||
MOXA_BOARD_C218_ISA,
|
||||
MOXA_BOARD_C320_PCI,
|
||||
MOXA_BOARD_C320_ISA,
|
||||
MOXA_BOARD_CP204J,
|
||||
};
|
||||
|
||||
static char *moxa_brdname[] =
|
||||
{
|
||||
"C218 Turbo PCI series",
|
||||
"C218 Turbo ISA series",
|
||||
"C320 Turbo PCI series",
|
||||
"C320 Turbo ISA series",
|
||||
"CP-204J series",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static const struct pci_device_id moxa_pcibrds[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218),
|
||||
.driver_data = MOXA_BOARD_C218_PCI },
|
||||
@ -394,14 +371,12 @@ static const struct pci_device_id moxa_pcibrds[] = {
|
||||
{ 0 }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, moxa_pcibrds);
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
struct moxa_port;
|
||||
|
||||
static struct moxa_board_conf {
|
||||
int boardType;
|
||||
int numPorts;
|
||||
int busType;
|
||||
|
||||
unsigned int ready;
|
||||
|
||||
@ -413,19 +388,6 @@ static struct moxa_board_conf {
|
||||
void __iomem *intTable;
|
||||
} moxa_boards[MAX_BOARDS];
|
||||
|
||||
struct mxser_mstatus {
|
||||
tcflag_t cflag;
|
||||
int cts;
|
||||
int dsr;
|
||||
int ri;
|
||||
int dcd;
|
||||
};
|
||||
|
||||
struct moxaq_str {
|
||||
int inq;
|
||||
int outq;
|
||||
};
|
||||
|
||||
struct moxa_port {
|
||||
struct tty_port port;
|
||||
struct moxa_board_conf *board;
|
||||
@ -440,12 +402,6 @@ struct moxa_port {
|
||||
u8 lowChkFlag;
|
||||
};
|
||||
|
||||
struct mon_str {
|
||||
int tick;
|
||||
int rxcnt[MAX_PORTS];
|
||||
int txcnt[MAX_PORTS];
|
||||
};
|
||||
|
||||
/* statusflags */
|
||||
#define TXSTOPPED 1
|
||||
#define LOWWAIT 2
|
||||
@ -455,17 +411,11 @@ struct mon_str {
|
||||
#define WAKEUP_CHARS 256
|
||||
|
||||
static int ttymajor = MOXAMAJOR;
|
||||
static struct mon_str moxaLog;
|
||||
static unsigned int moxaFuncTout = HZ / 2;
|
||||
static unsigned int moxaLowWaterChk;
|
||||
static DEFINE_MUTEX(moxa_openlock);
|
||||
static DEFINE_SPINLOCK(moxa_lock);
|
||||
|
||||
static unsigned long baseaddr[MAX_BOARDS];
|
||||
static unsigned int type[MAX_BOARDS];
|
||||
static unsigned int numports[MAX_BOARDS];
|
||||
static struct tty_port moxa_service_port;
|
||||
|
||||
MODULE_AUTHOR("William Chen");
|
||||
MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@ -473,13 +423,6 @@ MODULE_FIRMWARE("c218tunx.cod");
|
||||
MODULE_FIRMWARE("cp204unx.cod");
|
||||
MODULE_FIRMWARE("c320tunx.cod");
|
||||
|
||||
module_param_array(type, uint, NULL, 0);
|
||||
MODULE_PARM_DESC(type, "card type: C218=2, C320=4");
|
||||
module_param_hw_array(baseaddr, ulong, ioport, NULL, 0);
|
||||
MODULE_PARM_DESC(baseaddr, "base address");
|
||||
module_param_array(numports, uint, NULL, 0);
|
||||
MODULE_PARM_DESC(numports, "numports (ignored for C218)");
|
||||
|
||||
module_param(ttymajor, int, 0);
|
||||
|
||||
/*
|
||||
@ -583,104 +526,6 @@ static void moxa_low_water_check(void __iomem *ofsAddr)
|
||||
* TTY operations
|
||||
*/
|
||||
|
||||
static int moxa_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct moxa_port *ch = tty->driver_data;
|
||||
void __user *argp = (void __user *)arg;
|
||||
int status, ret = 0;
|
||||
|
||||
if (tty->index == MAX_PORTS) {
|
||||
if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE &&
|
||||
cmd != MOXA_GETMSTATUS)
|
||||
return -EINVAL;
|
||||
} else if (!ch)
|
||||
return -ENODEV;
|
||||
|
||||
switch (cmd) {
|
||||
case MOXA_GETDATACOUNT:
|
||||
moxaLog.tick = jiffies;
|
||||
if (copy_to_user(argp, &moxaLog, sizeof(moxaLog)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case MOXA_FLUSH_QUEUE:
|
||||
MoxaPortFlushData(ch, arg);
|
||||
break;
|
||||
case MOXA_GET_IOQUEUE: {
|
||||
struct moxaq_str __user *argm = argp;
|
||||
struct moxaq_str tmp;
|
||||
struct moxa_port *p;
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0; i < MAX_BOARDS; i++) {
|
||||
p = moxa_boards[i].ports;
|
||||
for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
spin_lock_bh(&moxa_lock);
|
||||
if (moxa_boards[i].ready) {
|
||||
tmp.inq = MoxaPortRxQueue(p);
|
||||
tmp.outq = MoxaPortTxQueue(p);
|
||||
}
|
||||
spin_unlock_bh(&moxa_lock);
|
||||
if (copy_to_user(argm, &tmp, sizeof(tmp)))
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} case MOXA_GET_OQUEUE:
|
||||
status = MoxaPortTxQueue(ch);
|
||||
ret = put_user(status, (unsigned long __user *)argp);
|
||||
break;
|
||||
case MOXA_GET_IQUEUE:
|
||||
status = MoxaPortRxQueue(ch);
|
||||
ret = put_user(status, (unsigned long __user *)argp);
|
||||
break;
|
||||
case MOXA_GETMSTATUS: {
|
||||
struct mxser_mstatus __user *argm = argp;
|
||||
struct mxser_mstatus tmp;
|
||||
struct moxa_port *p;
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0; i < MAX_BOARDS; i++) {
|
||||
p = moxa_boards[i].ports;
|
||||
for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
|
||||
struct tty_struct *ttyp;
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
spin_lock_bh(&moxa_lock);
|
||||
if (!moxa_boards[i].ready) {
|
||||
spin_unlock_bh(&moxa_lock);
|
||||
goto copy;
|
||||
}
|
||||
|
||||
status = MoxaPortLineStatus(p);
|
||||
spin_unlock_bh(&moxa_lock);
|
||||
|
||||
if (status & 1)
|
||||
tmp.cts = 1;
|
||||
if (status & 2)
|
||||
tmp.dsr = 1;
|
||||
if (status & 4)
|
||||
tmp.dcd = 1;
|
||||
|
||||
ttyp = tty_port_tty_get(&p->port);
|
||||
if (!ttyp)
|
||||
tmp.cflag = p->cflag;
|
||||
else
|
||||
tmp.cflag = ttyp->termios.c_cflag;
|
||||
tty_kref_put(ttyp);
|
||||
copy:
|
||||
if (copy_to_user(argm, &tmp, sizeof(tmp)))
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int moxa_break_ctl(struct tty_struct *tty, int state)
|
||||
{
|
||||
struct moxa_port *port = tty->driver_data;
|
||||
@ -697,7 +542,6 @@ static const struct tty_operations moxa_ops = {
|
||||
.write_room = moxa_write_room,
|
||||
.flush_buffer = moxa_flush_buffer,
|
||||
.chars_in_buffer = moxa_chars_in_buffer,
|
||||
.ioctl = moxa_ioctl,
|
||||
.set_termios = moxa_set_termios,
|
||||
.stop = moxa_stop,
|
||||
.start = moxa_start,
|
||||
@ -725,7 +569,6 @@ static DEFINE_TIMER(moxaTimer, moxa_poll);
|
||||
static int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model)
|
||||
{
|
||||
switch (brd->boardType) {
|
||||
case MOXA_BOARD_C218_ISA:
|
||||
case MOXA_BOARD_C218_PCI:
|
||||
if (model != 1)
|
||||
goto err;
|
||||
@ -769,7 +612,6 @@ static int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf,
|
||||
msleep(2000);
|
||||
|
||||
switch (brd->boardType) {
|
||||
case MOXA_BOARD_C218_ISA:
|
||||
case MOXA_BOARD_C218_PCI:
|
||||
tmp = readw(baseAddr + C218_key);
|
||||
if (tmp != C218_KeyCode)
|
||||
@ -833,7 +675,6 @@ static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr,
|
||||
|
||||
switch (brd->boardType) {
|
||||
case MOXA_BOARD_CP204J:
|
||||
case MOXA_BOARD_C218_ISA:
|
||||
case MOXA_BOARD_C218_PCI:
|
||||
key = C218_key;
|
||||
loadbuf = C218_LoadBuf;
|
||||
@ -898,15 +739,9 @@ static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr,
|
||||
return -EIO;
|
||||
|
||||
if (MOXA_IS_320(brd)) {
|
||||
if (brd->busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */
|
||||
writew(0x3800, baseAddr + TMS320_PORT1);
|
||||
writew(0x3900, baseAddr + TMS320_PORT2);
|
||||
writew(28499, baseAddr + TMS320_CLOCK);
|
||||
} else {
|
||||
writew(0x3200, baseAddr + TMS320_PORT1);
|
||||
writew(0x3400, baseAddr + TMS320_PORT2);
|
||||
writew(19999, baseAddr + TMS320_CLOCK);
|
||||
}
|
||||
writew(0x3800, baseAddr + TMS320_PORT1);
|
||||
writew(0x3900, baseAddr + TMS320_PORT2);
|
||||
writew(28499, baseAddr + TMS320_CLOCK);
|
||||
}
|
||||
writew(1, baseAddr + Disable_IRQ);
|
||||
writew(0, baseAddr + Magic_no);
|
||||
@ -957,7 +792,6 @@ static int moxa_load_code(struct moxa_board_conf *brd, const void *ptr,
|
||||
return retval;
|
||||
|
||||
switch (brd->boardType) {
|
||||
case MOXA_BOARD_C218_ISA:
|
||||
case MOXA_BOARD_C218_PCI:
|
||||
case MOXA_BOARD_CP204J:
|
||||
port = brd->ports;
|
||||
@ -1141,7 +975,6 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev)
|
||||
}
|
||||
|
||||
switch (brd->boardType) {
|
||||
case MOXA_BOARD_C218_ISA:
|
||||
case MOXA_BOARD_C218_PCI:
|
||||
file = "c218tunx.cod";
|
||||
break;
|
||||
@ -1227,7 +1060,6 @@ static void moxa_board_deinit(struct moxa_board_conf *brd)
|
||||
kfree(brd->ports);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static int moxa_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
@ -1270,7 +1102,6 @@ static int moxa_pci_probe(struct pci_dev *pdev,
|
||||
|
||||
board->boardType = board_type;
|
||||
switch (board_type) {
|
||||
case MOXA_BOARD_C218_ISA:
|
||||
case MOXA_BOARD_C218_PCI:
|
||||
board->numPorts = 8;
|
||||
break;
|
||||
@ -1282,7 +1113,6 @@ static int moxa_pci_probe(struct pci_dev *pdev,
|
||||
board->numPorts = 0;
|
||||
break;
|
||||
}
|
||||
board->busType = MOXA_BUS_TYPE_PCI;
|
||||
|
||||
retval = moxa_init_board(board, &pdev->dev);
|
||||
if (retval)
|
||||
@ -1318,21 +1148,12 @@ static struct pci_driver moxa_pci_driver = {
|
||||
.probe = moxa_pci_probe,
|
||||
.remove = moxa_pci_remove
|
||||
};
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
static int __init moxa_init(void)
|
||||
{
|
||||
unsigned int isabrds = 0;
|
||||
int retval = 0;
|
||||
struct moxa_board_conf *brd = moxa_boards;
|
||||
unsigned int i;
|
||||
|
||||
printk(KERN_INFO "MOXA Intellio family driver version %s\n",
|
||||
MOXA_VERSION);
|
||||
|
||||
tty_port_init(&moxa_service_port);
|
||||
|
||||
moxaDriver = tty_alloc_driver(MAX_PORTS + 1,
|
||||
moxaDriver = tty_alloc_driver(MAX_PORTS,
|
||||
TTY_DRIVER_REAL_RAW |
|
||||
TTY_DRIVER_DYNAMIC_DEV);
|
||||
if (IS_ERR(moxaDriver))
|
||||
@ -1348,8 +1169,6 @@ static int __init moxa_init(void)
|
||||
moxaDriver->init_termios.c_ispeed = 9600;
|
||||
moxaDriver->init_termios.c_ospeed = 9600;
|
||||
tty_set_operations(moxaDriver, &moxa_ops);
|
||||
/* Having one more port only for ioctls is ugly */
|
||||
tty_port_link_device(&moxa_service_port, moxaDriver, MAX_PORTS);
|
||||
|
||||
if (tty_register_driver(moxaDriver)) {
|
||||
printk(KERN_ERR "can't register MOXA Smartio tty driver!\n");
|
||||
@ -1357,64 +1176,16 @@ static int __init moxa_init(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find the boards defined from module args. */
|
||||
|
||||
for (i = 0; i < MAX_BOARDS; i++) {
|
||||
if (!baseaddr[i])
|
||||
break;
|
||||
if (type[i] == MOXA_BOARD_C218_ISA ||
|
||||
type[i] == MOXA_BOARD_C320_ISA) {
|
||||
pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n",
|
||||
isabrds + 1, moxa_brdname[type[i] - 1],
|
||||
baseaddr[i]);
|
||||
brd->boardType = type[i];
|
||||
brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 :
|
||||
numports[i];
|
||||
brd->busType = MOXA_BUS_TYPE_ISA;
|
||||
brd->basemem = ioremap(baseaddr[i], 0x4000);
|
||||
if (!brd->basemem) {
|
||||
printk(KERN_ERR "MOXA: can't remap %lx\n",
|
||||
baseaddr[i]);
|
||||
continue;
|
||||
}
|
||||
if (moxa_init_board(brd, NULL)) {
|
||||
iounmap(brd->basemem);
|
||||
brd->basemem = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "MOXA isa board found at 0x%.8lx and "
|
||||
"ready (%u ports, firmware loaded)\n",
|
||||
baseaddr[i], brd->numPorts);
|
||||
|
||||
brd++;
|
||||
isabrds++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
retval = pci_register_driver(&moxa_pci_driver);
|
||||
if (retval) {
|
||||
if (retval)
|
||||
printk(KERN_ERR "Can't register MOXA pci driver!\n");
|
||||
if (isabrds)
|
||||
retval = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit moxa_exit(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
pci_unregister_driver(&moxa_pci_driver);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */
|
||||
if (moxa_boards[i].ready)
|
||||
moxa_board_deinit(&moxa_boards[i]);
|
||||
|
||||
del_timer_sync(&moxaTimer);
|
||||
|
||||
@ -1457,9 +1228,6 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
|
||||
int port;
|
||||
|
||||
port = tty->index;
|
||||
if (port == MAX_PORTS) {
|
||||
return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
|
||||
}
|
||||
if (mutex_lock_interruptible(&moxa_openlock))
|
||||
return -ERESTARTSYS;
|
||||
brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
|
||||
@ -2182,7 +1950,6 @@ static ssize_t MoxaPortWriteData(struct tty_struct *tty, const u8 *buffer,
|
||||
c = (head > tail) ? (head - tail - 1) : (head - tail + tx_mask);
|
||||
if (c > len)
|
||||
c = len;
|
||||
moxaLog.txcnt[port->port.tty->index] += c;
|
||||
total = c;
|
||||
if (spage == epage) {
|
||||
bufhead = readw(ofsAddr + Ofs_txb);
|
||||
@ -2224,7 +1991,6 @@ static ssize_t MoxaPortWriteData(struct tty_struct *tty, const u8 *buffer,
|
||||
|
||||
static int MoxaPortReadData(struct moxa_port *port)
|
||||
{
|
||||
struct tty_struct *tty = port->port.tty;
|
||||
void __iomem *baseAddr, *ofsAddr, *ofs;
|
||||
u8 *dst;
|
||||
unsigned int count, len, total;
|
||||
@ -2243,7 +2009,6 @@ static int MoxaPortReadData(struct moxa_port *port)
|
||||
return 0;
|
||||
|
||||
total = count;
|
||||
moxaLog.rxcnt[tty->index] += total;
|
||||
if (spage == epage) {
|
||||
bufhead = readw(ofsAddr + Ofs_rxb);
|
||||
writew(spage, baseAddr + Control_reg);
|
||||
@ -2331,8 +2096,6 @@ static int moxa_get_serial_info(struct tty_struct *tty,
|
||||
{
|
||||
struct moxa_port *info = tty->driver_data;
|
||||
|
||||
if (tty->index == MAX_PORTS)
|
||||
return -EINVAL;
|
||||
if (!info)
|
||||
return -ENODEV;
|
||||
mutex_lock(&info->port.mutex);
|
||||
@ -2352,8 +2115,6 @@ static int moxa_set_serial_info(struct tty_struct *tty,
|
||||
struct moxa_port *info = tty->driver_data;
|
||||
unsigned int close_delay;
|
||||
|
||||
if (tty->index == MAX_PORTS)
|
||||
return -EINVAL;
|
||||
if (!info)
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -56,6 +56,8 @@
|
||||
*/
|
||||
#define WAKEUP_CHARS 256
|
||||
|
||||
#define N_TTY_BUF_SIZE 4096
|
||||
|
||||
/*
|
||||
* This defines the low- and high-watermarks for throttling and
|
||||
* unthrottling the TTY driver. These watermarks are used for
|
||||
@ -79,14 +81,6 @@
|
||||
#define ECHO_BLOCK 256
|
||||
#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32)
|
||||
|
||||
|
||||
#undef N_TTY_TRACE
|
||||
#ifdef N_TTY_TRACE
|
||||
# define n_tty_trace(f, args...) trace_printk(f, ##args)
|
||||
#else
|
||||
# define n_tty_trace(f, args...) no_printk(f, ##args)
|
||||
#endif
|
||||
|
||||
struct n_tty_data {
|
||||
/* producer-published */
|
||||
size_t read_head;
|
||||
@ -486,18 +480,13 @@ static int do_output_char(u8 c, struct tty_struct *tty, int space)
|
||||
static int process_output(u8 c, struct tty_struct *tty)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
int space, retval;
|
||||
|
||||
mutex_lock(&ldata->output_lock);
|
||||
guard(mutex)(&ldata->output_lock);
|
||||
|
||||
space = tty_write_room(tty);
|
||||
retval = do_output_char(c, tty, space);
|
||||
|
||||
mutex_unlock(&ldata->output_lock);
|
||||
if (retval < 0)
|
||||
if (do_output_char(c, tty, tty_write_room(tty)) < 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -522,17 +511,15 @@ static ssize_t process_output_block(struct tty_struct *tty,
|
||||
const u8 *buf, unsigned int nr)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
int space;
|
||||
int i;
|
||||
unsigned int space, i;
|
||||
const u8 *cp;
|
||||
|
||||
mutex_lock(&ldata->output_lock);
|
||||
guard(mutex)(&ldata->output_lock);
|
||||
|
||||
space = tty_write_room(tty);
|
||||
if (space <= 0) {
|
||||
mutex_unlock(&ldata->output_lock);
|
||||
return space;
|
||||
}
|
||||
if (space == 0)
|
||||
return 0;
|
||||
|
||||
if (nr > space)
|
||||
nr = space;
|
||||
|
||||
@ -544,18 +531,18 @@ static ssize_t process_output_block(struct tty_struct *tty,
|
||||
if (O_ONLRET(tty))
|
||||
ldata->column = 0;
|
||||
if (O_ONLCR(tty))
|
||||
goto break_out;
|
||||
goto do_write;
|
||||
ldata->canon_column = ldata->column;
|
||||
break;
|
||||
case '\r':
|
||||
if (O_ONOCR(tty) && ldata->column == 0)
|
||||
goto break_out;
|
||||
goto do_write;
|
||||
if (O_OCRNL(tty))
|
||||
goto break_out;
|
||||
goto do_write;
|
||||
ldata->canon_column = ldata->column = 0;
|
||||
break;
|
||||
case '\t':
|
||||
goto break_out;
|
||||
goto do_write;
|
||||
case '\b':
|
||||
if (ldata->column > 0)
|
||||
ldata->column--;
|
||||
@ -563,18 +550,15 @@ static ssize_t process_output_block(struct tty_struct *tty,
|
||||
default:
|
||||
if (!iscntrl(c)) {
|
||||
if (O_OLCUC(tty))
|
||||
goto break_out;
|
||||
goto do_write;
|
||||
if (!is_continuation(c, tty))
|
||||
ldata->column++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break_out:
|
||||
i = tty->ops->write(tty, buf, i);
|
||||
|
||||
mutex_unlock(&ldata->output_lock);
|
||||
return i;
|
||||
do_write:
|
||||
return tty->ops->write(tty, buf, i);
|
||||
}
|
||||
|
||||
static int n_tty_process_echo_ops(struct tty_struct *tty, size_t *tail,
|
||||
@ -696,7 +680,7 @@ static int n_tty_process_echo_ops(struct tty_struct *tty, size_t *tail,
|
||||
static size_t __process_echoes(struct tty_struct *tty)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
int space, old_space;
|
||||
unsigned int space, old_space;
|
||||
size_t tail;
|
||||
u8 c;
|
||||
|
||||
@ -2034,9 +2018,6 @@ static bool canon_copy_from_read_buf(const struct tty_struct *tty, u8 **kbp,
|
||||
tail = MASK(ldata->read_tail);
|
||||
size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
|
||||
|
||||
n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n",
|
||||
__func__, *nr, tail, n, size);
|
||||
|
||||
eol = find_next_bit(ldata->read_flags, size, tail);
|
||||
more = n - (size - tail);
|
||||
if (eol == N_TTY_BUF_SIZE && more) {
|
||||
@ -2054,9 +2035,6 @@ static bool canon_copy_from_read_buf(const struct tty_struct *tty, u8 **kbp,
|
||||
if (!found || read_buf(ldata, eol) != __DISABLED_CHAR)
|
||||
n = c;
|
||||
|
||||
n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n",
|
||||
__func__, eol, found, n, c, tail, more);
|
||||
|
||||
tty_copy(tty, *kbp, tail, n);
|
||||
*kbp += n;
|
||||
*nr -= n;
|
||||
@ -2133,6 +2111,66 @@ static int job_control(struct tty_struct *tty, struct file *file)
|
||||
return __tty_check_change(tty, SIGTTIN);
|
||||
}
|
||||
|
||||
/*
|
||||
* We still hold the atomic_read_lock and the termios_rwsem, and can just
|
||||
* continue to copy data.
|
||||
*/
|
||||
static ssize_t n_tty_continue_cookie(struct tty_struct *tty, u8 *kbuf,
|
||||
size_t nr, void **cookie)
|
||||
{
|
||||
struct n_tty_data *ldata = tty->disc_data;
|
||||
u8 *kb = kbuf;
|
||||
|
||||
if (ldata->icanon && !L_EXTPROC(tty)) {
|
||||
/*
|
||||
* If we have filled the user buffer, see if we should skip an
|
||||
* EOF character before releasing the lock and returning done.
|
||||
*/
|
||||
if (!nr)
|
||||
canon_skip_eof(ldata);
|
||||
else if (canon_copy_from_read_buf(tty, &kb, &nr))
|
||||
return kb - kbuf;
|
||||
} else {
|
||||
if (copy_from_read_buf(tty, &kb, &nr))
|
||||
return kb - kbuf;
|
||||
}
|
||||
|
||||
/* No more data - release locks and stop retries */
|
||||
n_tty_kick_worker(tty);
|
||||
n_tty_check_unthrottle(tty);
|
||||
up_read(&tty->termios_rwsem);
|
||||
mutex_unlock(&ldata->atomic_read_lock);
|
||||
*cookie = NULL;
|
||||
|
||||
return kb - kbuf;
|
||||
}
|
||||
|
||||
static int n_tty_wait_for_input(struct tty_struct *tty, struct file *file,
|
||||
struct wait_queue_entry *wait, long *timeout)
|
||||
{
|
||||
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
|
||||
return -EIO;
|
||||
if (tty_hung_up_p(file))
|
||||
return 0;
|
||||
/*
|
||||
* Abort readers for ttys which never actually get hung up.
|
||||
* See __tty_hangup().
|
||||
*/
|
||||
if (test_bit(TTY_HUPPING, &tty->flags))
|
||||
return 0;
|
||||
if (!*timeout)
|
||||
return 0;
|
||||
if (tty_io_nonblock(tty, file))
|
||||
return -EAGAIN;
|
||||
if (signal_pending(current))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
up_read(&tty->termios_rwsem);
|
||||
*timeout = wait_woken(wait, TASK_INTERRUPTIBLE, *timeout);
|
||||
down_read(&tty->termios_rwsem);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* n_tty_read - read function for tty
|
||||
@ -2166,36 +2204,9 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf,
|
||||
bool packet;
|
||||
size_t old_tail;
|
||||
|
||||
/*
|
||||
* Is this a continuation of a read started earler?
|
||||
*
|
||||
* If so, we still hold the atomic_read_lock and the
|
||||
* termios_rwsem, and can just continue to copy data.
|
||||
*/
|
||||
if (*cookie) {
|
||||
if (ldata->icanon && !L_EXTPROC(tty)) {
|
||||
/*
|
||||
* If we have filled the user buffer, see
|
||||
* if we should skip an EOF character before
|
||||
* releasing the lock and returning done.
|
||||
*/
|
||||
if (!nr)
|
||||
canon_skip_eof(ldata);
|
||||
else if (canon_copy_from_read_buf(tty, &kb, &nr))
|
||||
return kb - kbuf;
|
||||
} else {
|
||||
if (copy_from_read_buf(tty, &kb, &nr))
|
||||
return kb - kbuf;
|
||||
}
|
||||
|
||||
/* No more data - release locks and stop retries */
|
||||
n_tty_kick_worker(tty);
|
||||
n_tty_check_unthrottle(tty);
|
||||
up_read(&tty->termios_rwsem);
|
||||
mutex_unlock(&ldata->atomic_read_lock);
|
||||
*cookie = NULL;
|
||||
return kb - kbuf;
|
||||
}
|
||||
/* Is this a continuation of a read started earlier? */
|
||||
if (*cookie)
|
||||
return n_tty_continue_cookie(tty, kbuf, nr, cookie);
|
||||
|
||||
retval = job_control(tty, file);
|
||||
if (retval < 0)
|
||||
@ -2250,34 +2261,12 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf,
|
||||
tty_buffer_flush_work(tty->port);
|
||||
down_read(&tty->termios_rwsem);
|
||||
if (!input_available_p(tty, 0)) {
|
||||
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
|
||||
retval = -EIO;
|
||||
int ret = n_tty_wait_for_input(tty, file, &wait,
|
||||
&timeout);
|
||||
if (ret <= 0) {
|
||||
retval = ret;
|
||||
break;
|
||||
}
|
||||
if (tty_hung_up_p(file))
|
||||
break;
|
||||
/*
|
||||
* Abort readers for ttys which never actually
|
||||
* get hung up. See __tty_hangup().
|
||||
*/
|
||||
if (test_bit(TTY_HUPPING, &tty->flags))
|
||||
break;
|
||||
if (!timeout)
|
||||
break;
|
||||
if (tty_io_nonblock(tty, file)) {
|
||||
retval = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
if (signal_pending(current)) {
|
||||
retval = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
up_read(&tty->termios_rwsem);
|
||||
|
||||
timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
|
||||
timeout);
|
||||
|
||||
down_read(&tty->termios_rwsem);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -2292,21 +2281,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf,
|
||||
nr--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy data, and if there is more to be had
|
||||
* and we have nothing more to wait for, then
|
||||
* let's mark us for retries.
|
||||
*
|
||||
* NOTE! We return here with both the termios_sem
|
||||
* and atomic_read_lock still held, the retries
|
||||
* will release them when done.
|
||||
*/
|
||||
if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) {
|
||||
more_to_be_read:
|
||||
remove_wait_queue(&tty->read_wait, &wait);
|
||||
*cookie = cookie;
|
||||
return kb - kbuf;
|
||||
}
|
||||
if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum)
|
||||
goto more_to_be_read;
|
||||
}
|
||||
|
||||
n_tty_check_unthrottle(tty);
|
||||
@ -2333,6 +2309,18 @@ more_to_be_read:
|
||||
retval = kb - kbuf;
|
||||
|
||||
return retval;
|
||||
more_to_be_read:
|
||||
/*
|
||||
* There is more to be had and we have nothing more to wait for, so
|
||||
* let's mark us for retries.
|
||||
*
|
||||
* NOTE! We return here with both the termios_sem and atomic_read_lock
|
||||
* still held, the retries will release them when done.
|
||||
*/
|
||||
remove_wait_queue(&tty->read_wait, &wait);
|
||||
*cookie = cookie;
|
||||
|
||||
return kb - kbuf;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -316,17 +316,6 @@ void serdev_device_write_flush(struct serdev_device *serdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serdev_device_write_flush);
|
||||
|
||||
int serdev_device_write_room(struct serdev_device *serdev)
|
||||
{
|
||||
struct serdev_controller *ctrl = serdev->ctrl;
|
||||
|
||||
if (!ctrl || !ctrl->ops->write_room)
|
||||
return 0;
|
||||
|
||||
return serdev->ctrl->ops->write_room(ctrl);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serdev_device_write_room);
|
||||
|
||||
unsigned int serdev_device_set_baudrate(struct serdev_device *serdev, unsigned int speed)
|
||||
{
|
||||
struct serdev_controller *ctrl = serdev->ctrl;
|
||||
|
@ -92,14 +92,6 @@ static void ttyport_write_flush(struct serdev_controller *ctrl)
|
||||
tty_driver_flush_buffer(tty);
|
||||
}
|
||||
|
||||
static int ttyport_write_room(struct serdev_controller *ctrl)
|
||||
{
|
||||
struct serport *serport = serdev_controller_get_drvdata(ctrl);
|
||||
struct tty_struct *tty = serport->tty;
|
||||
|
||||
return tty_write_room(tty);
|
||||
}
|
||||
|
||||
static int ttyport_open(struct serdev_controller *ctrl)
|
||||
{
|
||||
struct serport *serport = serdev_controller_get_drvdata(ctrl);
|
||||
@ -259,7 +251,6 @@ static int ttyport_break_ctl(struct serdev_controller *ctrl, unsigned int break_
|
||||
static const struct serdev_controller_ops ctrl_ops = {
|
||||
.write_buf = ttyport_write_buf,
|
||||
.write_flush = ttyport_write_flush,
|
||||
.write_room = ttyport_write_room,
|
||||
.open = ttyport_open,
|
||||
.close = ttyport_close,
|
||||
.set_flow_control = ttyport_set_flow_control,
|
||||
|
@ -162,7 +162,7 @@ void serial8250_tx_dma_flush(struct uart_8250_port *p)
|
||||
*/
|
||||
dma->tx_size = 0;
|
||||
|
||||
dmaengine_terminate_async(dma->rxchan);
|
||||
dmaengine_terminate_async(dma->txchan);
|
||||
}
|
||||
|
||||
int serial8250_rx_dma(struct uart_8250_port *p)
|
||||
|
@ -107,11 +107,23 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is being called as part of the uart_port::serial_out()
|
||||
* routine. Hence, it must not call serial_port_out() or serial_out()
|
||||
* against the modified registers here, i.e. LCR.
|
||||
*/
|
||||
static void dw8250_force_idle(struct uart_port *p)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(p);
|
||||
unsigned int lsr;
|
||||
|
||||
/*
|
||||
* The following call currently performs serial_out()
|
||||
* against the FCR register. Because it differs to LCR
|
||||
* there will be no infinite loop, but if it ever gets
|
||||
* modified, we might need a new custom version of it
|
||||
* that avoids infinite recursion.
|
||||
*/
|
||||
serial8250_clear_and_reinit_fifos(up);
|
||||
|
||||
/*
|
||||
@ -120,14 +132,19 @@ static void dw8250_force_idle(struct uart_port *p)
|
||||
* enabled.
|
||||
*/
|
||||
if (up->fcr & UART_FCR_ENABLE_FIFO) {
|
||||
lsr = p->serial_in(p, UART_LSR);
|
||||
lsr = serial_port_in(p, UART_LSR);
|
||||
if (!(lsr & UART_LSR_DR))
|
||||
return;
|
||||
}
|
||||
|
||||
(void)p->serial_in(p, UART_RX);
|
||||
serial_port_in(p, UART_RX);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is being called as part of the uart_port::serial_out()
|
||||
* routine. Hence, it must not call serial_port_out() or serial_out()
|
||||
* against the modified registers here, i.e. LCR.
|
||||
*/
|
||||
static void dw8250_check_lcr(struct uart_port *p, int offset, int value)
|
||||
{
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
@ -139,7 +156,7 @@ static void dw8250_check_lcr(struct uart_port *p, int offset, int value)
|
||||
|
||||
/* Make sure LCR write wasn't ignored */
|
||||
while (tries--) {
|
||||
unsigned int lcr = p->serial_in(p, offset);
|
||||
unsigned int lcr = serial_port_in(p, offset);
|
||||
|
||||
if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR))
|
||||
return;
|
||||
@ -260,7 +277,7 @@ static int dw8250_handle_irq(struct uart_port *p)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(p);
|
||||
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||
unsigned int iir = p->serial_in(p, UART_IIR);
|
||||
unsigned int iir = serial_port_in(p, UART_IIR);
|
||||
bool rx_timeout = (iir & 0x3f) == UART_IIR_RX_TIMEOUT;
|
||||
unsigned int quirks = d->pdata->quirks;
|
||||
unsigned int status;
|
||||
@ -281,7 +298,7 @@ static int dw8250_handle_irq(struct uart_port *p)
|
||||
status = serial_lsr_in(up);
|
||||
|
||||
if (!(status & (UART_LSR_DR | UART_LSR_BI)))
|
||||
(void) p->serial_in(p, UART_RX);
|
||||
serial_port_in(p, UART_RX);
|
||||
|
||||
uart_port_unlock_irqrestore(p, flags);
|
||||
}
|
||||
@ -303,7 +320,7 @@ static int dw8250_handle_irq(struct uart_port *p)
|
||||
|
||||
if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
|
||||
/* Clear the USR */
|
||||
(void)p->serial_in(p, d->pdata->usr_reg);
|
||||
serial_port_in(p, d->pdata->usr_reg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -390,7 +407,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
|
||||
static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(p);
|
||||
unsigned int mcr = p->serial_in(p, UART_MCR);
|
||||
unsigned int mcr = serial_port_in(p, UART_MCR);
|
||||
|
||||
if (up->capabilities & UART_CAP_IRDA) {
|
||||
if (termios->c_line == N_IRDA)
|
||||
@ -398,7 +415,7 @@ static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios)
|
||||
else
|
||||
mcr &= ~DW_UART_MCR_SIRE;
|
||||
|
||||
p->serial_out(p, UART_MCR, mcr);
|
||||
serial_port_out(p, UART_MCR, mcr);
|
||||
}
|
||||
serial8250_do_set_ldisc(p, termios);
|
||||
}
|
||||
@ -421,6 +438,18 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
|
||||
return param == chan->device->dev;
|
||||
}
|
||||
|
||||
static void dw8250_setup_dma_filter(struct uart_port *p, struct dw8250_data *data)
|
||||
{
|
||||
/* Platforms with iDMA 64-bit */
|
||||
if (platform_get_resource_byname(to_platform_device(p->dev), IORESOURCE_MEM, "lpss_priv")) {
|
||||
data->data.dma.rx_param = p->dev->parent;
|
||||
data->data.dma.tx_param = p->dev->parent;
|
||||
data->data.dma.fn = dw8250_idma_filter;
|
||||
} else {
|
||||
data->data.dma.fn = dw8250_fallback_dma_filter;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 dw8250_rzn1_get_dmacr_burst(int max_burst)
|
||||
{
|
||||
if (max_burst >= 8)
|
||||
@ -459,8 +488,8 @@ static void dw8250_prepare_rx_dma(struct uart_8250_port *p)
|
||||
|
||||
static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
|
||||
{
|
||||
unsigned int quirks = data->pdata ? data->pdata->quirks : 0;
|
||||
u32 cpr_value = data->pdata ? data->pdata->cpr_value : 0;
|
||||
unsigned int quirks = data->pdata->quirks;
|
||||
u32 cpr_value = data->pdata->cpr_value;
|
||||
|
||||
if (quirks & DW_UART_QUIRK_CPR_VALUE)
|
||||
data->data.cpr_value = cpr_value;
|
||||
@ -491,14 +520,6 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
|
||||
p->serial_in = dw8250_serial_in32;
|
||||
data->uart_16550_compatible = true;
|
||||
}
|
||||
|
||||
/* Platforms with iDMA 64-bit */
|
||||
if (platform_get_resource_byname(to_platform_device(p->dev),
|
||||
IORESOURCE_MEM, "lpss_priv")) {
|
||||
data->data.dma.rx_param = p->dev->parent;
|
||||
data->data.dma.tx_param = p->dev->parent;
|
||||
data->data.dma.fn = dw8250_idma_filter;
|
||||
}
|
||||
}
|
||||
|
||||
static void dw8250_reset_control_assert(void *data)
|
||||
@ -520,7 +541,6 @@ static int dw8250_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, -EINVAL, "no registers defined\n");
|
||||
|
||||
spin_lock_init(&p->lock);
|
||||
p->handle_irq = dw8250_handle_irq;
|
||||
p->pm = dw8250_do_pm;
|
||||
p->type = PORT_8250;
|
||||
p->flags = UPF_FIXED_PORT;
|
||||
@ -532,13 +552,8 @@ static int dw8250_probe(struct platform_device *pdev)
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->data.dma.fn = dw8250_fallback_dma_filter;
|
||||
data->pdata = device_get_match_data(p->dev);
|
||||
p->private_data = &data->data;
|
||||
|
||||
data->uart_16550_compatible = device_property_read_bool(dev,
|
||||
"snps,uart-16550-compatible");
|
||||
|
||||
p->mapbase = regs->start;
|
||||
p->mapsize = resource_size(regs);
|
||||
|
||||
@ -626,11 +641,19 @@ static int dw8250_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dw8250_quirks(p, data);
|
||||
data->uart_16550_compatible = device_property_read_bool(dev, "snps,uart-16550-compatible");
|
||||
|
||||
data->pdata = device_get_match_data(p->dev);
|
||||
if (data->pdata)
|
||||
dw8250_quirks(p, data);
|
||||
|
||||
/* If the Busy Functionality is not implemented, don't handle it */
|
||||
if (data->uart_16550_compatible)
|
||||
p->handle_irq = NULL;
|
||||
else if (data->pdata)
|
||||
p->handle_irq = dw8250_handle_irq;
|
||||
|
||||
dw8250_setup_dma_filter(p, data);
|
||||
|
||||
if (!data->skip_autocfg)
|
||||
dw8250_setup_port(p);
|
||||
|
@ -32,7 +32,7 @@ int fsl8250_handle_irq(struct uart_port *port)
|
||||
|
||||
uart_port_lock_irqsave(&up->port, &flags);
|
||||
|
||||
iir = port->serial_in(port, UART_IIR);
|
||||
iir = serial_port_in(port, UART_IIR);
|
||||
if (iir & UART_IIR_NO_INT) {
|
||||
uart_port_unlock_irqrestore(&up->port, flags);
|
||||
return 0;
|
||||
@ -54,12 +54,12 @@ int fsl8250_handle_irq(struct uart_port *port)
|
||||
if (unlikely((iir & UART_IIR_ID) == UART_IIR_RLSI &&
|
||||
(up->lsr_saved_flags & UART_LSR_BI))) {
|
||||
up->lsr_saved_flags &= ~UART_LSR_BI;
|
||||
port->serial_in(port, UART_RX);
|
||||
serial_port_in(port, UART_RX);
|
||||
uart_port_unlock_irqrestore(&up->port, flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
|
||||
lsr = orig_lsr = serial_port_in(port, UART_LSR);
|
||||
|
||||
/* Process incoming characters first */
|
||||
if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
|
||||
@ -71,7 +71,7 @@ int fsl8250_handle_irq(struct uart_port *port)
|
||||
if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
|
||||
unsigned long delay;
|
||||
|
||||
up->ier = port->serial_in(port, UART_IER);
|
||||
up->ier = serial_port_in(port, UART_IER);
|
||||
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
|
||||
port->ops->stop_rx(port);
|
||||
} else {
|
||||
|
461
drivers/tty/serial/8250/8250_ni.c
Normal file
461
drivers/tty/serial/8250/8250_ni.c
Normal file
@ -0,0 +1,461 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* NI 16550 UART Driver
|
||||
*
|
||||
* The National Instruments (NI) 16550 is a UART that is compatible with the
|
||||
* TL16C550C and OX16C950B register interfaces, but has additional functions
|
||||
* for RS-485 transceiver control. This driver implements support for the
|
||||
* additional functionality on top of the standard serial8250 core.
|
||||
*
|
||||
* Copyright 2012-2023 National Instruments Corporation
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
/* Extra bits in UART_ACR */
|
||||
#define NI16550_ACR_AUTO_DTR_EN BIT(4)
|
||||
|
||||
/* TFS - TX FIFO Size */
|
||||
#define NI16550_TFS_OFFSET 0x0C
|
||||
/* RFS - RX FIFO Size */
|
||||
#define NI16550_RFS_OFFSET 0x0D
|
||||
|
||||
/* PMR - Port Mode Register */
|
||||
#define NI16550_PMR_OFFSET 0x0E
|
||||
/* PMR[1:0] - Port Capabilities */
|
||||
#define NI16550_PMR_CAP_MASK GENMASK(1, 0)
|
||||
#define NI16550_PMR_NOT_IMPL FIELD_PREP(NI16550_PMR_CAP_MASK, 0) /* not implemented */
|
||||
#define NI16550_PMR_CAP_RS232 FIELD_PREP(NI16550_PMR_CAP_MASK, 1) /* RS-232 capable */
|
||||
#define NI16550_PMR_CAP_RS485 FIELD_PREP(NI16550_PMR_CAP_MASK, 2) /* RS-485 capable */
|
||||
#define NI16550_PMR_CAP_DUAL FIELD_PREP(NI16550_PMR_CAP_MASK, 3) /* dual-port */
|
||||
/* PMR[4] - Interface Mode */
|
||||
#define NI16550_PMR_MODE_MASK GENMASK(4, 4)
|
||||
#define NI16550_PMR_MODE_RS232 FIELD_PREP(NI16550_PMR_MODE_MASK, 0) /* currently 232 */
|
||||
#define NI16550_PMR_MODE_RS485 FIELD_PREP(NI16550_PMR_MODE_MASK, 1) /* currently 485 */
|
||||
|
||||
/* PCR - Port Control Register */
|
||||
/*
|
||||
* Wire Mode | Tx enabled? | Rx enabled?
|
||||
* ---------------|----------------------|--------------------------
|
||||
* PCR_RS422 | Always | Always
|
||||
* PCR_ECHO_RS485 | When DTR asserted | Always
|
||||
* PCR_DTR_RS485 | When DTR asserted | Disabled when TX enabled
|
||||
* PCR_AUTO_RS485 | When data in TX FIFO | Disabled when TX enabled
|
||||
*/
|
||||
#define NI16550_PCR_OFFSET 0x0F
|
||||
#define NI16550_PCR_WIRE_MODE_MASK GENMASK(1, 0)
|
||||
#define NI16550_PCR_RS422 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 0)
|
||||
#define NI16550_PCR_ECHO_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 1)
|
||||
#define NI16550_PCR_DTR_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 2)
|
||||
#define NI16550_PCR_AUTO_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 3)
|
||||
#define NI16550_PCR_TXVR_ENABLE_BIT BIT(3)
|
||||
#define NI16550_PCR_RS485_TERMINATION_BIT BIT(6)
|
||||
|
||||
/* flags for ni16550_device_info */
|
||||
#define NI_HAS_PMR BIT(0)
|
||||
|
||||
struct ni16550_device_info {
|
||||
u32 uartclk;
|
||||
u8 prescaler;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
struct ni16550_data {
|
||||
int line;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int ni16550_enable_transceivers(struct uart_port *port)
|
||||
{
|
||||
u8 pcr;
|
||||
|
||||
pcr = port->serial_in(port, NI16550_PCR_OFFSET);
|
||||
pcr |= NI16550_PCR_TXVR_ENABLE_BIT;
|
||||
dev_dbg(port->dev, "enable transceivers: write pcr: 0x%02x\n", pcr);
|
||||
port->serial_out(port, NI16550_PCR_OFFSET, pcr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ni16550_disable_transceivers(struct uart_port *port)
|
||||
{
|
||||
u8 pcr;
|
||||
|
||||
pcr = port->serial_in(port, NI16550_PCR_OFFSET);
|
||||
pcr &= ~NI16550_PCR_TXVR_ENABLE_BIT;
|
||||
dev_dbg(port->dev, "disable transceivers: write pcr: 0x%02x\n", pcr);
|
||||
port->serial_out(port, NI16550_PCR_OFFSET, pcr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ni16550_rs485_config(struct uart_port *port,
|
||||
struct ktermios *termios,
|
||||
struct serial_rs485 *rs485)
|
||||
{
|
||||
struct uart_8250_port *up = container_of(port, struct uart_8250_port, port);
|
||||
u8 pcr;
|
||||
|
||||
pcr = serial_in(up, NI16550_PCR_OFFSET);
|
||||
pcr &= ~NI16550_PCR_WIRE_MODE_MASK;
|
||||
|
||||
if ((rs485->flags & SER_RS485_MODE_RS422) ||
|
||||
!(rs485->flags & SER_RS485_ENABLED)) {
|
||||
/* RS-422 */
|
||||
pcr |= NI16550_PCR_RS422;
|
||||
up->acr &= ~NI16550_ACR_AUTO_DTR_EN;
|
||||
} else {
|
||||
/* RS-485 2-wire Auto */
|
||||
pcr |= NI16550_PCR_AUTO_RS485;
|
||||
up->acr |= NI16550_ACR_AUTO_DTR_EN;
|
||||
}
|
||||
|
||||
dev_dbg(port->dev, "config rs485: write pcr: 0x%02x, acr: %02x\n", pcr, up->acr);
|
||||
serial_out(up, NI16550_PCR_OFFSET, pcr);
|
||||
serial_icr_write(up, UART_ACR, up->acr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_pmr_rs232_mode(struct uart_8250_port *up)
|
||||
{
|
||||
u8 pmr = serial_in(up, NI16550_PMR_OFFSET);
|
||||
u8 pmr_mode = pmr & NI16550_PMR_MODE_MASK;
|
||||
u8 pmr_cap = pmr & NI16550_PMR_CAP_MASK;
|
||||
|
||||
/*
|
||||
* If the PMR is not implemented, then by default NI UARTs are
|
||||
* connected to RS-485 transceivers
|
||||
*/
|
||||
if (pmr_cap == NI16550_PMR_NOT_IMPL)
|
||||
return false;
|
||||
|
||||
if (pmr_cap == NI16550_PMR_CAP_DUAL)
|
||||
/*
|
||||
* If the port is dual-mode capable, then read the mode bit
|
||||
* to know the current mode
|
||||
*/
|
||||
return pmr_mode == NI16550_PMR_MODE_RS232;
|
||||
/*
|
||||
* If it is not dual-mode capable, then decide based on the
|
||||
* capability
|
||||
*/
|
||||
return pmr_cap == NI16550_PMR_CAP_RS232;
|
||||
}
|
||||
|
||||
static void ni16550_config_prescaler(struct uart_8250_port *up,
|
||||
u8 prescaler)
|
||||
{
|
||||
/*
|
||||
* Page in the Enhanced Mode Registers
|
||||
* Sets EFR[4] for Enhanced Mode.
|
||||
*/
|
||||
u8 lcr_value;
|
||||
u8 efr_value;
|
||||
|
||||
lcr_value = serial_in(up, UART_LCR);
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
|
||||
efr_value = serial_in(up, UART_EFR);
|
||||
efr_value |= UART_EFR_ECB;
|
||||
|
||||
serial_out(up, UART_EFR, efr_value);
|
||||
|
||||
/* Page out the Enhanced Mode Registers */
|
||||
serial_out(up, UART_LCR, lcr_value);
|
||||
|
||||
/* Set prescaler to CPR register. */
|
||||
serial_out(up, UART_SCR, UART_CPR);
|
||||
serial_out(up, UART_ICR, prescaler);
|
||||
}
|
||||
|
||||
static const struct serial_rs485 ni16550_rs485_supported = {
|
||||
.flags = SER_RS485_ENABLED | SER_RS485_MODE_RS422 | SER_RS485_RTS_ON_SEND |
|
||||
SER_RS485_RTS_AFTER_SEND,
|
||||
/*
|
||||
* delay_rts_* and RX_DURING_TX are not supported.
|
||||
*
|
||||
* RTS_{ON,AFTER}_SEND are supported, but ignored; the transceiver
|
||||
* is connected in only one way and we don't need userspace to tell
|
||||
* us, but want to retain compatibility with applications that do.
|
||||
*/
|
||||
};
|
||||
|
||||
static void ni16550_rs485_setup(struct uart_port *port)
|
||||
{
|
||||
port->rs485_config = ni16550_rs485_config;
|
||||
port->rs485_supported = ni16550_rs485_supported;
|
||||
/*
|
||||
* The hardware comes up by default in 2-wire auto mode and we
|
||||
* set the flags to represent that
|
||||
*/
|
||||
port->rs485.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND;
|
||||
}
|
||||
|
||||
static int ni16550_port_startup(struct uart_port *port)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = serial8250_do_startup(port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ni16550_enable_transceivers(port);
|
||||
}
|
||||
|
||||
static void ni16550_port_shutdown(struct uart_port *port)
|
||||
{
|
||||
ni16550_disable_transceivers(port);
|
||||
|
||||
serial8250_do_shutdown(port);
|
||||
}
|
||||
|
||||
static int ni16550_get_regs(struct platform_device *pdev,
|
||||
struct uart_port *port)
|
||||
{
|
||||
struct resource *regs;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (regs) {
|
||||
port->iotype = UPIO_PORT;
|
||||
port->iobase = regs->start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (regs) {
|
||||
port->iotype = UPIO_MEM;
|
||||
port->mapbase = regs->start;
|
||||
port->mapsize = resource_size(regs);
|
||||
port->flags |= UPF_IOREMAP;
|
||||
|
||||
port->membase = devm_ioremap(&pdev->dev, port->mapbase,
|
||||
port->mapsize);
|
||||
if (!port->membase)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_err(&pdev->dev, "no registers defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Very old implementations don't have the TFS or RFS registers
|
||||
* defined, so we may read all-0s or all-1s. For such devices,
|
||||
* assume a FIFO size of 128.
|
||||
*/
|
||||
static u8 ni16550_read_fifo_size(struct uart_8250_port *uart, int reg)
|
||||
{
|
||||
u8 value = serial_in(uart, reg);
|
||||
|
||||
if (value == 0x00 || value == 0xFF)
|
||||
return 128;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void ni16550_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
|
||||
up->mcr |= UART_MCR_CLKSEL;
|
||||
serial8250_do_set_mctrl(port, mctrl);
|
||||
}
|
||||
|
||||
static int ni16550_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct ni16550_device_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct uart_8250_port uart = {};
|
||||
unsigned int txfifosz, rxfifosz;
|
||||
unsigned int prescaler = 0;
|
||||
struct ni16550_data *data;
|
||||
const char *portmode;
|
||||
bool rs232_property;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&uart.port.lock);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = ni16550_get_regs(pdev, &uart.port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* early setup so that serial_in()/serial_out() work */
|
||||
serial8250_set_defaults(&uart);
|
||||
|
||||
info = device_get_match_data(dev);
|
||||
|
||||
uart.port.dev = dev;
|
||||
uart.port.irq = irq;
|
||||
uart.port.irqflags = IRQF_SHARED;
|
||||
uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF
|
||||
| UPF_FIXED_PORT | UPF_FIXED_TYPE;
|
||||
uart.port.startup = ni16550_port_startup;
|
||||
uart.port.shutdown = ni16550_port_shutdown;
|
||||
|
||||
/*
|
||||
* Hardware instantiation of FIFO sizes are held in registers.
|
||||
*/
|
||||
txfifosz = ni16550_read_fifo_size(&uart, NI16550_TFS_OFFSET);
|
||||
rxfifosz = ni16550_read_fifo_size(&uart, NI16550_RFS_OFFSET);
|
||||
|
||||
dev_dbg(dev, "NI 16550 has TX FIFO size %u, RX FIFO size %u\n",
|
||||
txfifosz, rxfifosz);
|
||||
|
||||
uart.port.type = PORT_16550A;
|
||||
uart.port.fifosize = txfifosz;
|
||||
uart.tx_loadsz = txfifosz;
|
||||
uart.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
|
||||
uart.capabilities = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR;
|
||||
|
||||
/*
|
||||
* Declaration of the base clock frequency can come from one of:
|
||||
* - static declaration in this driver (for older ACPI IDs)
|
||||
* - a "clock-frquency" ACPI
|
||||
*/
|
||||
if (info->uartclk)
|
||||
uart.port.uartclk = info->uartclk;
|
||||
if (device_property_read_u32(dev, "clock-frequency",
|
||||
&uart.port.uartclk)) {
|
||||
data->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (!IS_ERR(data->clk))
|
||||
uart.port.uartclk = clk_get_rate(data->clk);
|
||||
}
|
||||
|
||||
if (!uart.port.uartclk) {
|
||||
dev_err(dev, "unable to determine clock frequency!\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (info->prescaler)
|
||||
prescaler = info->prescaler;
|
||||
device_property_read_u32(dev, "clock-prescaler", &prescaler);
|
||||
|
||||
if (prescaler != 0) {
|
||||
uart.port.set_mctrl = ni16550_set_mctrl;
|
||||
ni16550_config_prescaler(&uart, (u8)prescaler);
|
||||
}
|
||||
|
||||
/*
|
||||
* The determination of whether or not this is an RS-485 or RS-232 port
|
||||
* can come from the PMR (if present), otherwise we're solely an RS-485
|
||||
* port.
|
||||
*
|
||||
* This is a device-specific property, and there are old devices in the
|
||||
* field using "transceiver" as an ACPI property, so we have to check
|
||||
* for that as well.
|
||||
*/
|
||||
if (!device_property_read_string(dev, "transceiver", &portmode)) {
|
||||
rs232_property = strncmp(portmode, "RS-232", 6) == 0;
|
||||
|
||||
dev_dbg(dev, "port is in %s mode (via device property)\n",
|
||||
rs232_property ? "RS-232" : "RS-485");
|
||||
} else if (info->flags & NI_HAS_PMR) {
|
||||
rs232_property = is_pmr_rs232_mode(&uart);
|
||||
|
||||
dev_dbg(dev, "port is in %s mode (via PMR)\n",
|
||||
rs232_property ? "RS-232" : "RS-485");
|
||||
} else {
|
||||
rs232_property = 0;
|
||||
|
||||
dev_dbg(dev, "port is fixed as RS-485\n");
|
||||
}
|
||||
|
||||
if (!rs232_property) {
|
||||
/*
|
||||
* Neither the 'transceiver' property nor the PMR indicate
|
||||
* that this is an RS-232 port, so it must be an RS-485 one.
|
||||
*/
|
||||
ni16550_rs485_setup(&uart.port);
|
||||
}
|
||||
|
||||
ret = serial8250_register_8250_port(&uart);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
data->line = ret;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ni16550_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ni16550_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
serial8250_unregister_port(data->line);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/* NI 16550 RS-485 Interface */
|
||||
static const struct ni16550_device_info nic7750 = {
|
||||
.uartclk = 33333333,
|
||||
};
|
||||
|
||||
/* NI CVS-145x RS-485 Interface */
|
||||
static const struct ni16550_device_info nic7772 = {
|
||||
.uartclk = 1843200,
|
||||
.flags = NI_HAS_PMR,
|
||||
};
|
||||
|
||||
/* NI cRIO-904x RS-485 Interface */
|
||||
static const struct ni16550_device_info nic792b = {
|
||||
/* Sets UART clock rate to 22.222 MHz with 1.125 prescale */
|
||||
.uartclk = 22222222,
|
||||
.prescaler = 0x09,
|
||||
};
|
||||
|
||||
/* NI sbRIO 96x8 RS-232/485 Interfaces */
|
||||
static const struct ni16550_device_info nic7a69 = {
|
||||
/* Set UART clock rate to 29.629 MHz with 1.125 prescale */
|
||||
.uartclk = 29629629,
|
||||
.prescaler = 0x09,
|
||||
};
|
||||
static const struct acpi_device_id ni16550_acpi_match[] = {
|
||||
{ "NIC7750", (kernel_ulong_t)&nic7750 },
|
||||
{ "NIC7772", (kernel_ulong_t)&nic7772 },
|
||||
{ "NIC792B", (kernel_ulong_t)&nic792b },
|
||||
{ "NIC7A69", (kernel_ulong_t)&nic7a69 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ni16550_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver ni16550_driver = {
|
||||
.driver = {
|
||||
.name = "ni16550",
|
||||
.acpi_match_table = ACPI_PTR(ni16550_acpi_match),
|
||||
},
|
||||
.probe = ni16550_probe,
|
||||
.remove = ni16550_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ni16550_driver);
|
||||
|
||||
MODULE_AUTHOR("Emerson Electric Co.");
|
||||
MODULE_DESCRIPTION("NI 16550 Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -692,7 +692,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id)
|
||||
|
||||
/* Synchronize UART_IER access against the console. */
|
||||
uart_port_lock(port);
|
||||
up->ier = port->serial_in(port, UART_IER);
|
||||
up->ier = serial_port_in(port, UART_IER);
|
||||
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
|
||||
port->ops->stop_rx(port);
|
||||
} else {
|
||||
|
@ -2727,6 +2727,22 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
|
||||
.init = pci_oxsemi_tornado_init,
|
||||
.setup = pci_oxsemi_tornado_setup,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTASHIELD,
|
||||
.device = 0x4026,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_oxsemi_tornado_init,
|
||||
.setup = pci_oxsemi_tornado_setup,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTASHIELD,
|
||||
.device = 0x4021,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.init = pci_oxsemi_tornado_init,
|
||||
.setup = pci_oxsemi_tornado_setup,
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = 0x8811,
|
||||
@ -5253,6 +5269,14 @@ static const struct pci_device_id serial_pci_tbl[] = {
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_b2_2_115200 },
|
||||
{ PCI_VENDOR_ID_INTASHIELD, 0x0BA2,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_b2_2_115200 },
|
||||
{ PCI_VENDOR_ID_INTASHIELD, 0x0BA3,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_b2_2_115200 },
|
||||
/*
|
||||
* Brainboxes UC-235/246
|
||||
*/
|
||||
@ -5373,6 +5397,14 @@ static const struct pci_device_id serial_pci_tbl[] = {
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_b2_4_115200 },
|
||||
{ PCI_VENDOR_ID_INTASHIELD, 0x0C42,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_b2_4_115200 },
|
||||
{ PCI_VENDOR_ID_INTASHIELD, 0x0C43,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_b2_4_115200 },
|
||||
/*
|
||||
* Brainboxes UC-420
|
||||
*/
|
||||
@ -5599,6 +5631,20 @@ static const struct pci_device_id serial_pci_tbl[] = {
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_oxsemi_1_15625000 },
|
||||
/*
|
||||
* Brainboxes XC-235
|
||||
*/
|
||||
{ PCI_VENDOR_ID_INTASHIELD, 0x4026,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_oxsemi_1_15625000 },
|
||||
/*
|
||||
* Brainboxes XC-475
|
||||
*/
|
||||
{ PCI_VENDOR_ID_INTASHIELD, 0x4021,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
pbn_oxsemi_1_15625000 },
|
||||
|
||||
/*
|
||||
* Perle PCI-RAS cards
|
||||
|
@ -1678,7 +1678,7 @@ static void serial8250_disable_ms(struct uart_port *port)
|
||||
if (up->bugs & UART_BUG_NOMSR)
|
||||
return;
|
||||
|
||||
mctrl_gpio_disable_ms(up->gpios);
|
||||
mctrl_gpio_disable_ms_no_sync(up->gpios);
|
||||
|
||||
up->ier &= ~UART_IER_MSI;
|
||||
serial_port_out(port, UART_IER, up->ier);
|
||||
@ -2406,28 +2406,26 @@ int serial8250_do_startup(struct uart_port *port)
|
||||
* test if we receive TX irq. This way, we'll never enable
|
||||
* UART_BUG_TXEN.
|
||||
*/
|
||||
if (up->port.quirks & UPQ_NO_TXEN_TEST)
|
||||
goto dont_test_tx_en;
|
||||
if (!(up->port.quirks & UPQ_NO_TXEN_TEST)) {
|
||||
/*
|
||||
* Do a quick test to see if we receive an interrupt when we
|
||||
* enable the TX irq.
|
||||
*/
|
||||
serial_port_out(port, UART_IER, UART_IER_THRI);
|
||||
lsr = serial_port_in(port, UART_LSR);
|
||||
iir = serial_port_in(port, UART_IIR);
|
||||
serial_port_out(port, UART_IER, 0);
|
||||
|
||||
/*
|
||||
* Do a quick test to see if we receive an interrupt when we enable
|
||||
* the TX irq.
|
||||
*/
|
||||
serial_port_out(port, UART_IER, UART_IER_THRI);
|
||||
lsr = serial_port_in(port, UART_LSR);
|
||||
iir = serial_port_in(port, UART_IIR);
|
||||
serial_port_out(port, UART_IER, 0);
|
||||
|
||||
if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
|
||||
if (!(up->bugs & UART_BUG_TXEN)) {
|
||||
up->bugs |= UART_BUG_TXEN;
|
||||
dev_dbg(port->dev, "enabling bad tx status workarounds\n");
|
||||
if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
|
||||
if (!(up->bugs & UART_BUG_TXEN)) {
|
||||
up->bugs |= UART_BUG_TXEN;
|
||||
dev_dbg(port->dev, "enabling bad tx status workarounds\n");
|
||||
}
|
||||
} else {
|
||||
up->bugs &= ~UART_BUG_TXEN;
|
||||
}
|
||||
} else {
|
||||
up->bugs &= ~UART_BUG_TXEN;
|
||||
}
|
||||
|
||||
dont_test_tx_en:
|
||||
uart_port_unlock_irqrestore(port, flags);
|
||||
|
||||
/*
|
||||
@ -2968,7 +2966,6 @@ static int serial8250_request_std_resource(struct uart_8250_port *up)
|
||||
{
|
||||
unsigned int size = serial8250_port_size(up);
|
||||
struct uart_port *port = &up->port;
|
||||
int ret = 0;
|
||||
|
||||
switch (port->iotype) {
|
||||
case UPIO_AU:
|
||||
@ -2977,32 +2974,28 @@ static int serial8250_request_std_resource(struct uart_8250_port *up)
|
||||
case UPIO_MEM32BE:
|
||||
case UPIO_MEM16:
|
||||
case UPIO_MEM:
|
||||
if (!port->mapbase) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (!port->mapbase)
|
||||
return -EINVAL;
|
||||
|
||||
if (!request_mem_region(port->mapbase, size, "serial")) {
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
}
|
||||
if (!request_mem_region(port->mapbase, size, "serial"))
|
||||
return -EBUSY;
|
||||
|
||||
if (port->flags & UPF_IOREMAP) {
|
||||
port->membase = ioremap(port->mapbase, size);
|
||||
if (!port->membase) {
|
||||
release_mem_region(port->mapbase, size);
|
||||
ret = -ENOMEM;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
return 0;
|
||||
case UPIO_HUB6:
|
||||
case UPIO_PORT:
|
||||
if (!request_region(port->iobase, size, "serial"))
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serial8250_release_std_resource(struct uart_8250_port *up)
|
||||
|
@ -16,30 +16,27 @@ static unsigned int probe_rsa_count;
|
||||
|
||||
static int rsa8250_request_resource(struct uart_8250_port *up)
|
||||
{
|
||||
unsigned long start = UART_RSA_BASE << up->port.regshift;
|
||||
unsigned int size = 8 << up->port.regshift;
|
||||
struct uart_port *port = &up->port;
|
||||
int ret = -EINVAL;
|
||||
unsigned long start = UART_RSA_BASE << port->regshift;
|
||||
unsigned int size = 8 << port->regshift;
|
||||
|
||||
switch (port->iotype) {
|
||||
case UPIO_HUB6:
|
||||
case UPIO_PORT:
|
||||
start += port->iobase;
|
||||
if (request_region(start, size, "serial-rsa"))
|
||||
ret = 0;
|
||||
else
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
if (!request_region(start, size, "serial-rsa"))
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rsa8250_release_resource(struct uart_8250_port *up)
|
||||
{
|
||||
unsigned long offset = UART_RSA_BASE << up->port.regshift;
|
||||
unsigned int size = 8 << up->port.regshift;
|
||||
struct uart_port *port = &up->port;
|
||||
unsigned long offset = UART_RSA_BASE << port->regshift;
|
||||
unsigned int size = 8 << port->regshift;
|
||||
|
||||
switch (port->iotype) {
|
||||
case UPIO_HUB6:
|
||||
|
@ -569,6 +569,19 @@ config SERIAL_8250_BCM7271
|
||||
including DMA support and high accuracy BAUD rates, say
|
||||
Y to this option. If unsure, say N.
|
||||
|
||||
config SERIAL_8250_NI
|
||||
tristate "NI 16550 based serial port"
|
||||
depends on SERIAL_8250
|
||||
depends on (X86 && ACPI) || COMPILE_TEST
|
||||
help
|
||||
This driver supports the integrated serial ports on National
|
||||
Instruments (NI) controller hardware. This is required for all NI
|
||||
controller models with onboard RS-485 or dual-mode RS-485/RS-232
|
||||
ports.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called 8250_ni.
|
||||
|
||||
config SERIAL_OF_PLATFORM
|
||||
tristate "Devicetree based probing for 8250 ports"
|
||||
depends on SERIAL_8250 && OF
|
||||
|
@ -40,6 +40,7 @@ obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
|
||||
obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o
|
||||
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
|
||||
obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o
|
||||
obj-$(CONFIG_SERIAL_8250_NI) += 8250_ni.o
|
||||
obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o
|
||||
obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o
|
||||
obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o
|
||||
|
@ -179,25 +179,6 @@ config SERIAL_ATMEL_TTYAT
|
||||
|
||||
Say Y if you have an external 8250/16C550 UART. If unsure, say N.
|
||||
|
||||
config SERIAL_KGDB_NMI
|
||||
bool "Serial console over KGDB NMI debugger port"
|
||||
depends on KGDB_SERIAL_CONSOLE
|
||||
help
|
||||
This special driver allows you to temporary use NMI debugger port
|
||||
as a normal console (assuming that the port is attached to KGDB).
|
||||
|
||||
Unlike KDB's disable_nmi command, with this driver you are always
|
||||
able to go back to the debugger using KGDB escape sequence ($3#33).
|
||||
This is because this console driver processes the input in NMI
|
||||
context, and thus is able to intercept the magic sequence.
|
||||
|
||||
Note that since the console interprets input and uses polling
|
||||
communication methods, for things like PPP you still must fully
|
||||
detach debugger port from the KGDB NMI (i.e. disable_nmi), and
|
||||
use raw console.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SERIAL_MESON
|
||||
tristate "Meson serial port support"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
@ -306,6 +287,29 @@ config SERIAL_TEGRA_TCU_CONSOLE
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config SERIAL_TEGRA_UTC
|
||||
tristate "NVIDIA Tegra UART Trace Controller"
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
select SERIAL_CORE
|
||||
help
|
||||
Support for Tegra UTC (UART Trace controller) client serial port.
|
||||
|
||||
UTC is a HW based serial port that allows multiplexing multiple data
|
||||
streams of up to 16 UTC clients into a single hardware serial port.
|
||||
|
||||
config SERIAL_TEGRA_UTC_CONSOLE
|
||||
bool "Support for console on a Tegra UTC serial port"
|
||||
depends on SERIAL_TEGRA_UTC
|
||||
select SERIAL_CORE_CONSOLE
|
||||
default SERIAL_TEGRA_UTC
|
||||
help
|
||||
If you say Y here, it will be possible to use a Tegra UTC client 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).
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config SERIAL_MAX3100
|
||||
tristate "MAX3100/3110/3111/3222 support"
|
||||
depends on SPI
|
||||
|
@ -86,6 +86,7 @@ obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o
|
||||
obj-$(CONFIG_SERIAL_SUNPLUS) += sunplus-uart.o
|
||||
obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o
|
||||
obj-$(CONFIG_SERIAL_TEGRA_TCU) += tegra-tcu.o
|
||||
obj-$(CONFIG_SERIAL_TEGRA_UTC) += tegra-utc.o
|
||||
obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
|
||||
obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
|
||||
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
|
||||
@ -96,6 +97,5 @@ obj-$(CONFIG_SERIAL_ZS) += zs.o
|
||||
# GPIOLIB helpers for modem control lines
|
||||
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
|
||||
|
||||
obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
|
||||
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
|
||||
obj-$(CONFIG_SERIAL_NUVOTON_MA35D1) += ma35d1_serial.o
|
||||
|
@ -272,6 +272,7 @@ struct uart_amba_port {
|
||||
enum pl011_rs485_tx_state rs485_tx_state;
|
||||
struct hrtimer trigger_start_tx;
|
||||
struct hrtimer trigger_stop_tx;
|
||||
bool console_line_ended;
|
||||
#ifdef CONFIG_DMA_ENGINE
|
||||
/* DMA stuff */
|
||||
unsigned int dmacr; /* dma control reg */
|
||||
@ -2366,50 +2367,7 @@ static void pl011_console_putchar(struct uart_port *port, unsigned char ch)
|
||||
while (pl011_read(uap, REG_FR) & UART01x_FR_TXFF)
|
||||
cpu_relax();
|
||||
pl011_write(ch, uap, REG_DR);
|
||||
}
|
||||
|
||||
static void
|
||||
pl011_console_write(struct console *co, const char *s, unsigned int count)
|
||||
{
|
||||
struct uart_amba_port *uap = amba_ports[co->index];
|
||||
unsigned int old_cr = 0, new_cr;
|
||||
unsigned long flags;
|
||||
int locked = 1;
|
||||
|
||||
clk_enable(uap->clk);
|
||||
|
||||
if (oops_in_progress)
|
||||
locked = uart_port_trylock_irqsave(&uap->port, &flags);
|
||||
else
|
||||
uart_port_lock_irqsave(&uap->port, &flags);
|
||||
|
||||
/*
|
||||
* First save the CR then disable the interrupts
|
||||
*/
|
||||
if (!uap->vendor->always_enabled) {
|
||||
old_cr = pl011_read(uap, REG_CR);
|
||||
new_cr = old_cr & ~UART011_CR_CTSEN;
|
||||
new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
|
||||
pl011_write(new_cr, uap, REG_CR);
|
||||
}
|
||||
|
||||
uart_console_write(&uap->port, s, count, pl011_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty and restore the
|
||||
* TCR. Allow feature register bits to be inverted to work around
|
||||
* errata.
|
||||
*/
|
||||
while ((pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr)
|
||||
& uap->vendor->fr_busy)
|
||||
cpu_relax();
|
||||
if (!uap->vendor->always_enabled)
|
||||
pl011_write(old_cr, uap, REG_CR);
|
||||
|
||||
if (locked)
|
||||
uart_port_unlock_irqrestore(&uap->port, flags);
|
||||
|
||||
clk_disable(uap->clk);
|
||||
uap->console_line_ended = (ch == '\n');
|
||||
}
|
||||
|
||||
static void pl011_console_get_options(struct uart_amba_port *uap, int *baud,
|
||||
@ -2472,6 +2430,8 @@ static int pl011_console_setup(struct console *co, char *options)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uap->console_line_ended = true;
|
||||
|
||||
if (dev_get_platdata(uap->port.dev)) {
|
||||
struct amba_pl011_data *plat;
|
||||
|
||||
@ -2555,14 +2515,105 @@ static int pl011_console_match(struct console *co, char *name, int idx,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void
|
||||
pl011_console_write_atomic(struct console *co, struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct uart_amba_port *uap = amba_ports[co->index];
|
||||
unsigned int old_cr = 0;
|
||||
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
return;
|
||||
|
||||
clk_enable(uap->clk);
|
||||
|
||||
if (!uap->vendor->always_enabled) {
|
||||
old_cr = pl011_read(uap, REG_CR);
|
||||
pl011_write((old_cr & ~UART011_CR_CTSEN) | (UART01x_CR_UARTEN | UART011_CR_TXE),
|
||||
uap, REG_CR);
|
||||
}
|
||||
|
||||
if (!uap->console_line_ended)
|
||||
uart_console_write(&uap->port, "\n", 1, pl011_console_putchar);
|
||||
uart_console_write(&uap->port, wctxt->outbuf, wctxt->len, pl011_console_putchar);
|
||||
|
||||
while ((pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr) & uap->vendor->fr_busy)
|
||||
cpu_relax();
|
||||
|
||||
if (!uap->vendor->always_enabled)
|
||||
pl011_write(old_cr, uap, REG_CR);
|
||||
|
||||
clk_disable(uap->clk);
|
||||
|
||||
nbcon_exit_unsafe(wctxt);
|
||||
}
|
||||
|
||||
static void
|
||||
pl011_console_write_thread(struct console *co, struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct uart_amba_port *uap = amba_ports[co->index];
|
||||
unsigned int old_cr = 0;
|
||||
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
return;
|
||||
|
||||
clk_enable(uap->clk);
|
||||
|
||||
if (!uap->vendor->always_enabled) {
|
||||
old_cr = pl011_read(uap, REG_CR);
|
||||
pl011_write((old_cr & ~UART011_CR_CTSEN) | (UART01x_CR_UARTEN | UART011_CR_TXE),
|
||||
uap, REG_CR);
|
||||
}
|
||||
|
||||
if (nbcon_exit_unsafe(wctxt)) {
|
||||
int i;
|
||||
unsigned int len = READ_ONCE(wctxt->len);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
break;
|
||||
uart_console_write(&uap->port, wctxt->outbuf + i, 1, pl011_console_putchar);
|
||||
if (!nbcon_exit_unsafe(wctxt))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (!nbcon_enter_unsafe(wctxt))
|
||||
nbcon_reacquire_nobuf(wctxt);
|
||||
|
||||
while ((pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr) & uap->vendor->fr_busy)
|
||||
cpu_relax();
|
||||
|
||||
if (!uap->vendor->always_enabled)
|
||||
pl011_write(old_cr, uap, REG_CR);
|
||||
|
||||
clk_disable(uap->clk);
|
||||
|
||||
nbcon_exit_unsafe(wctxt);
|
||||
}
|
||||
|
||||
static void
|
||||
pl011_console_device_lock(struct console *co, unsigned long *flags)
|
||||
{
|
||||
__uart_port_lock_irqsave(&amba_ports[co->index]->port, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
pl011_console_device_unlock(struct console *co, unsigned long flags)
|
||||
{
|
||||
__uart_port_unlock_irqrestore(&amba_ports[co->index]->port, flags);
|
||||
}
|
||||
|
||||
static struct uart_driver amba_reg;
|
||||
static struct console amba_console = {
|
||||
.name = "ttyAMA",
|
||||
.write = pl011_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = pl011_console_setup,
|
||||
.match = pl011_console_match,
|
||||
.flags = CON_PRINTBUFFER | CON_ANYTIME,
|
||||
.write_atomic = pl011_console_write_atomic,
|
||||
.write_thread = pl011_console_write_thread,
|
||||
.device_lock = pl011_console_device_lock,
|
||||
.device_unlock = pl011_console_device_unlock,
|
||||
.flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON,
|
||||
.index = -1,
|
||||
.data = &amba_reg,
|
||||
};
|
||||
@ -3000,7 +3051,7 @@ static const struct of_device_id sbsa_uart_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sbsa_uart_of_match);
|
||||
|
||||
static const struct acpi_device_id __maybe_unused sbsa_uart_acpi_match[] = {
|
||||
static const struct acpi_device_id sbsa_uart_acpi_match[] = {
|
||||
{ "ARMH0011", 0 },
|
||||
{ "ARMHB000", 0 },
|
||||
{},
|
||||
@ -3013,8 +3064,8 @@ static struct platform_driver arm_sbsa_uart_platform_driver = {
|
||||
.driver = {
|
||||
.name = "sbsa-uart",
|
||||
.pm = &pl011_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(sbsa_uart_of_match),
|
||||
.acpi_match_table = ACPI_PTR(sbsa_uart_acpi_match),
|
||||
.of_match_table = sbsa_uart_of_match,
|
||||
.acpi_match_table = sbsa_uart_acpi_match,
|
||||
.suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
|
||||
},
|
||||
};
|
||||
|
@ -700,7 +700,7 @@ static void atmel_disable_ms(struct uart_port *port)
|
||||
|
||||
atmel_port->ms_irq_enabled = false;
|
||||
|
||||
mctrl_gpio_disable_ms(atmel_port->gpios);
|
||||
mctrl_gpio_disable_ms_no_sync(atmel_port->gpios);
|
||||
|
||||
if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS))
|
||||
idr |= ATMEL_US_CTSIC;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1764,11 +1764,10 @@ static int icom_probe(struct pci_dev *dev,
|
||||
goto probe_exit1;
|
||||
}
|
||||
|
||||
/* save off irq and request irq line */
|
||||
retval = request_irq(dev->irq, icom_interrupt, IRQF_SHARED, ICOM_DRIVER_NAME, (void *)icom_adapter);
|
||||
if (retval) {
|
||||
goto probe_exit2;
|
||||
}
|
||||
/* save off irq and request irq line */
|
||||
retval = request_irq(dev->irq, icom_interrupt, IRQF_SHARED, ICOM_DRIVER_NAME, icom_adapter);
|
||||
if (retval)
|
||||
goto probe_exit2;
|
||||
|
||||
retval = icom_load_ports(icom_adapter);
|
||||
|
||||
|
@ -1608,7 +1608,7 @@ static void imx_uart_shutdown(struct uart_port *port)
|
||||
imx_uart_dma_exit(sport);
|
||||
}
|
||||
|
||||
mctrl_gpio_disable_ms(sport->gpios);
|
||||
mctrl_gpio_disable_ms_sync(sport->gpios);
|
||||
|
||||
uart_port_lock_irqsave(&sport->port, &flags);
|
||||
ucr2 = imx_uart_readl(sport, UCR2);
|
||||
|
@ -1,280 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* KGDB NMI serial console
|
||||
*
|
||||
* Copyright 2010 Google, Inc.
|
||||
* Arve Hjønnevåg <arve@android.com>
|
||||
* Colin Cross <ccross@android.com>
|
||||
* Copyright 2012 Linaro Ltd.
|
||||
* Anton Vorontsov <anton.vorontsov@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/kdb.h>
|
||||
|
||||
static atomic_t kgdb_nmi_num_readers = ATOMIC_INIT(0);
|
||||
|
||||
static int kgdb_nmi_console_setup(struct console *co, char *options)
|
||||
{
|
||||
arch_kgdb_ops.enable_nmi(1);
|
||||
|
||||
/* The NMI console uses the dbg_io_ops to issue console messages. To
|
||||
* avoid duplicate messages during kdb sessions we must inform kdb's
|
||||
* I/O utilities that messages sent to the console will automatically
|
||||
* be displayed on the dbg_io.
|
||||
*/
|
||||
dbg_io_ops->cons = co;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kgdb_nmi_console_write(struct console *co, const char *s, uint c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < c; i++)
|
||||
dbg_io_ops->write_char(s[i]);
|
||||
}
|
||||
|
||||
static struct tty_driver *kgdb_nmi_tty_driver;
|
||||
|
||||
static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx)
|
||||
{
|
||||
*idx = co->index;
|
||||
return kgdb_nmi_tty_driver;
|
||||
}
|
||||
|
||||
static struct console kgdb_nmi_console = {
|
||||
.name = "ttyNMI",
|
||||
.setup = kgdb_nmi_console_setup,
|
||||
.write = kgdb_nmi_console_write,
|
||||
.device = kgdb_nmi_console_device,
|
||||
.flags = CON_PRINTBUFFER | CON_ANYTIME,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is usually the maximum rate on debug ports. We make fifo large enough
|
||||
* to make copy-pasting to the terminal usable.
|
||||
*/
|
||||
#define KGDB_NMI_BAUD 115200
|
||||
#define KGDB_NMI_FIFO_SIZE roundup_pow_of_two(KGDB_NMI_BAUD / 8 / HZ)
|
||||
|
||||
struct kgdb_nmi_tty_priv {
|
||||
struct tty_port port;
|
||||
struct timer_list timer;
|
||||
STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo;
|
||||
};
|
||||
|
||||
static struct tty_port *kgdb_nmi_port;
|
||||
|
||||
/*
|
||||
* The tasklet is cheap, it does not cause wakeups when reschedules itself,
|
||||
* instead it waits for the next tick.
|
||||
*/
|
||||
static void kgdb_nmi_tty_receiver(struct timer_list *t)
|
||||
{
|
||||
struct kgdb_nmi_tty_priv *priv = from_timer(priv, t, timer);
|
||||
char ch;
|
||||
|
||||
priv->timer.expires = jiffies + (HZ/100);
|
||||
add_timer(&priv->timer);
|
||||
|
||||
if (likely(!atomic_read(&kgdb_nmi_num_readers) ||
|
||||
!kfifo_len(&priv->fifo)))
|
||||
return;
|
||||
|
||||
while (kfifo_out(&priv->fifo, &ch, 1))
|
||||
tty_insert_flip_char(&priv->port, ch, TTY_NORMAL);
|
||||
tty_flip_buffer_push(&priv->port);
|
||||
}
|
||||
|
||||
static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty)
|
||||
{
|
||||
struct kgdb_nmi_tty_priv *priv =
|
||||
container_of(port, struct kgdb_nmi_tty_priv, port);
|
||||
|
||||
kgdb_nmi_port = port;
|
||||
priv->timer.expires = jiffies + (HZ/100);
|
||||
add_timer(&priv->timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kgdb_nmi_tty_shutdown(struct tty_port *port)
|
||||
{
|
||||
struct kgdb_nmi_tty_priv *priv =
|
||||
container_of(port, struct kgdb_nmi_tty_priv, port);
|
||||
|
||||
del_timer(&priv->timer);
|
||||
kgdb_nmi_port = NULL;
|
||||
}
|
||||
|
||||
static const struct tty_port_operations kgdb_nmi_tty_port_ops = {
|
||||
.activate = kgdb_nmi_tty_activate,
|
||||
.shutdown = kgdb_nmi_tty_shutdown,
|
||||
};
|
||||
|
||||
static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty)
|
||||
{
|
||||
struct kgdb_nmi_tty_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_KFIFO(priv->fifo);
|
||||
timer_setup(&priv->timer, kgdb_nmi_tty_receiver, 0);
|
||||
tty_port_init(&priv->port);
|
||||
priv->port.ops = &kgdb_nmi_tty_port_ops;
|
||||
tty->driver_data = priv;
|
||||
|
||||
ret = tty_port_install(&priv->port, drv, tty);
|
||||
if (ret) {
|
||||
pr_err("%s: can't install tty port: %d\n", __func__, ret);
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
tty_port_destroy(&priv->port);
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kgdb_nmi_tty_cleanup(struct tty_struct *tty)
|
||||
{
|
||||
struct kgdb_nmi_tty_priv *priv = tty->driver_data;
|
||||
|
||||
tty->driver_data = NULL;
|
||||
tty_port_destroy(&priv->port);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static int kgdb_nmi_tty_open(struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
struct kgdb_nmi_tty_priv *priv = tty->driver_data;
|
||||
unsigned int mode = file->f_flags & O_ACCMODE;
|
||||
int ret;
|
||||
|
||||
ret = tty_port_open(&priv->port, tty, file);
|
||||
if (!ret && (mode == O_RDONLY || mode == O_RDWR))
|
||||
atomic_inc(&kgdb_nmi_num_readers);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kgdb_nmi_tty_close(struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
struct kgdb_nmi_tty_priv *priv = tty->driver_data;
|
||||
unsigned int mode = file->f_flags & O_ACCMODE;
|
||||
|
||||
if (mode == O_RDONLY || mode == O_RDWR)
|
||||
atomic_dec(&kgdb_nmi_num_readers);
|
||||
|
||||
tty_port_close(&priv->port, tty, file);
|
||||
}
|
||||
|
||||
static void kgdb_nmi_tty_hangup(struct tty_struct *tty)
|
||||
{
|
||||
struct kgdb_nmi_tty_priv *priv = tty->driver_data;
|
||||
|
||||
tty_port_hangup(&priv->port);
|
||||
}
|
||||
|
||||
static unsigned int kgdb_nmi_tty_write_room(struct tty_struct *tty)
|
||||
{
|
||||
/* Actually, we can handle any amount as we use polled writes. */
|
||||
return 2048;
|
||||
}
|
||||
|
||||
static ssize_t kgdb_nmi_tty_write(struct tty_struct *tty, const u8 *buf,
|
||||
size_t c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < c; i++)
|
||||
dbg_io_ops->write_char(buf[i]);
|
||||
return c;
|
||||
}
|
||||
|
||||
static const struct tty_operations kgdb_nmi_tty_ops = {
|
||||
.open = kgdb_nmi_tty_open,
|
||||
.close = kgdb_nmi_tty_close,
|
||||
.install = kgdb_nmi_tty_install,
|
||||
.cleanup = kgdb_nmi_tty_cleanup,
|
||||
.hangup = kgdb_nmi_tty_hangup,
|
||||
.write_room = kgdb_nmi_tty_write_room,
|
||||
.write = kgdb_nmi_tty_write,
|
||||
};
|
||||
|
||||
int kgdb_register_nmi_console(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!arch_kgdb_ops.enable_nmi)
|
||||
return 0;
|
||||
|
||||
kgdb_nmi_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW);
|
||||
if (IS_ERR(kgdb_nmi_tty_driver)) {
|
||||
pr_err("%s: cannot allocate tty\n", __func__);
|
||||
return PTR_ERR(kgdb_nmi_tty_driver);
|
||||
}
|
||||
kgdb_nmi_tty_driver->driver_name = "ttyNMI";
|
||||
kgdb_nmi_tty_driver->name = "ttyNMI";
|
||||
kgdb_nmi_tty_driver->num = 1;
|
||||
kgdb_nmi_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
kgdb_nmi_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
kgdb_nmi_tty_driver->init_termios = tty_std_termios;
|
||||
tty_termios_encode_baud_rate(&kgdb_nmi_tty_driver->init_termios,
|
||||
KGDB_NMI_BAUD, KGDB_NMI_BAUD);
|
||||
tty_set_operations(kgdb_nmi_tty_driver, &kgdb_nmi_tty_ops);
|
||||
|
||||
ret = tty_register_driver(kgdb_nmi_tty_driver);
|
||||
if (ret) {
|
||||
pr_err("%s: can't register tty driver: %d\n", __func__, ret);
|
||||
goto err_drv_reg;
|
||||
}
|
||||
|
||||
register_console(&kgdb_nmi_console);
|
||||
|
||||
return 0;
|
||||
err_drv_reg:
|
||||
tty_driver_kref_put(kgdb_nmi_tty_driver);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kgdb_register_nmi_console);
|
||||
|
||||
int kgdb_unregister_nmi_console(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!arch_kgdb_ops.enable_nmi)
|
||||
return 0;
|
||||
arch_kgdb_ops.enable_nmi(0);
|
||||
|
||||
ret = unregister_console(&kgdb_nmi_console);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tty_unregister_driver(kgdb_nmi_tty_driver);
|
||||
tty_driver_kref_put(kgdb_nmi_tty_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kgdb_unregister_nmi_console);
|
@ -186,8 +186,6 @@ static void cleanup_kgdboc(void)
|
||||
if (configured != 1)
|
||||
return;
|
||||
|
||||
if (kgdb_unregister_nmi_console())
|
||||
return;
|
||||
kgdboc_unregister_kbd();
|
||||
kgdb_unregister_io_module(&kgdboc_io_ops);
|
||||
}
|
||||
@ -250,16 +248,10 @@ do_register:
|
||||
if (err)
|
||||
goto noconfig;
|
||||
|
||||
err = kgdb_register_nmi_console();
|
||||
if (err)
|
||||
goto nmi_con_failed;
|
||||
|
||||
configured = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
nmi_con_failed:
|
||||
kgdb_unregister_io_module(&kgdboc_io_ops);
|
||||
noconfig:
|
||||
kgdboc_unregister_kbd();
|
||||
configured = 0;
|
||||
|
@ -799,7 +799,7 @@ static struct platform_driver ma35d1serial_driver = {
|
||||
.resume = ma35d1serial_resume,
|
||||
.driver = {
|
||||
.name = "ma35d1-uart",
|
||||
.of_match_table = of_match_ptr(ma35d1_serial_of_match),
|
||||
.of_match_table = ma35d1_serial_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1351,7 +1351,6 @@ static const struct uart_ops mpc52xx_uart_ops = {
|
||||
.startup = mpc52xx_uart_startup,
|
||||
.shutdown = mpc52xx_uart_shutdown,
|
||||
.set_termios = mpc52xx_uart_set_termios,
|
||||
/* .pm = mpc52xx_uart_pm, Not supported yet */
|
||||
.type = mpc52xx_uart_type,
|
||||
.release_port = mpc52xx_uart_release_port,
|
||||
.request_port = mpc52xx_uart_request_port,
|
||||
|
@ -1515,7 +1515,6 @@ static const struct uart_ops pch_uart_ops = {
|
||||
.startup = pch_uart_startup,
|
||||
.shutdown = pch_uart_shutdown,
|
||||
.set_termios = pch_uart_set_termios,
|
||||
/* .pm = pch_uart_pm, Not supported yet */
|
||||
.type = pch_uart_type,
|
||||
.release_port = pch_uart_release_port,
|
||||
.request_port = pch_uart_request_port,
|
||||
|
@ -895,8 +895,8 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
|
||||
{
|
||||
struct uart_port *uport = uart_port_check(state);
|
||||
unsigned long new_port;
|
||||
unsigned int change_irq, change_port, closing_wait;
|
||||
unsigned int old_custom_divisor, close_delay;
|
||||
unsigned int old_custom_divisor, close_delay, closing_wait;
|
||||
bool change_irq, change_port;
|
||||
upf_t old_flags, new_flags;
|
||||
int retval;
|
||||
|
||||
@ -2013,9 +2013,8 @@ static const char *uart_type(struct uart_port *port)
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i)
|
||||
static void uart_line_info(struct seq_file *m, struct uart_state *state)
|
||||
{
|
||||
struct uart_state *state = drv->state + i;
|
||||
struct tty_port *port = &state->port;
|
||||
enum uart_pm_state pm_state;
|
||||
struct uart_port *uport;
|
||||
@ -2100,7 +2099,7 @@ static int uart_proc_show(struct seq_file *m, void *v)
|
||||
|
||||
seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", "", "", "");
|
||||
for (i = 0; i < drv->nr; i++)
|
||||
uart_line_info(m, drv, i);
|
||||
uart_line_info(m, drv->state + i);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -3156,7 +3155,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u
|
||||
if (uport->cons && uport->dev)
|
||||
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
|
||||
|
||||
tty_port_link_device(port, drv->tty_driver, uport->line);
|
||||
uart_configure_port(drv, state, uport);
|
||||
|
||||
port->console = uart_console(uport);
|
||||
|
@ -217,7 +217,7 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context)
|
||||
*
|
||||
* This will get the {cts,rts,...}-gpios from device tree if they are present
|
||||
* and request them, set direction etc, and return an allocated structure.
|
||||
* `devm_*` functions are used, so there's no need to call mctrl_gpio_free().
|
||||
* `devm_*` functions are used, so there's no need to explicitly free.
|
||||
* As this sets up the irq handling, make sure to not handle changes to the
|
||||
* gpio input lines in your driver, too.
|
||||
*/
|
||||
@ -267,32 +267,6 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctrl_gpio_init);
|
||||
|
||||
/**
|
||||
* mctrl_gpio_free - explicitly free uart gpios
|
||||
* @dev: uart port's device
|
||||
* @gpios: gpios structure to be freed
|
||||
*
|
||||
* This will free the requested gpios in mctrl_gpio_init(). As `devm_*`
|
||||
* functions are used, there's generally no need to call this function.
|
||||
*/
|
||||
void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
|
||||
{
|
||||
enum mctrl_gpio_idx i;
|
||||
|
||||
if (gpios == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < UART_GPIO_MAX; i++) {
|
||||
if (gpios->irq[i])
|
||||
devm_free_irq(gpios->port->dev, gpios->irq[i], gpios);
|
||||
|
||||
if (gpios->gpio[i])
|
||||
devm_gpiod_put(dev, gpios->gpio[i]);
|
||||
}
|
||||
devm_kfree(dev, gpios);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctrl_gpio_free);
|
||||
|
||||
/**
|
||||
* mctrl_gpio_enable_ms - enable irqs and handling of changes to the ms lines
|
||||
* @gpios: gpios to enable
|
||||
@ -322,11 +296,7 @@ void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms);
|
||||
|
||||
/**
|
||||
* mctrl_gpio_disable_ms - disable irqs and handling of changes to the ms lines
|
||||
* @gpios: gpios to disable
|
||||
*/
|
||||
void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
|
||||
static void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios, bool sync)
|
||||
{
|
||||
enum mctrl_gpio_idx i;
|
||||
|
||||
@ -342,10 +312,34 @@ void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
|
||||
if (!gpios->irq[i])
|
||||
continue;
|
||||
|
||||
disable_irq(gpios->irq[i]);
|
||||
if (sync)
|
||||
disable_irq(gpios->irq[i]);
|
||||
else
|
||||
disable_irq_nosync(gpios->irq[i]);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms);
|
||||
|
||||
/**
|
||||
* mctrl_gpio_disable_ms_sync - disable irqs and handling of changes to the ms
|
||||
* lines, and wait for any pending IRQ to be processed
|
||||
* @gpios: gpios to disable
|
||||
*/
|
||||
void mctrl_gpio_disable_ms_sync(struct mctrl_gpios *gpios)
|
||||
{
|
||||
mctrl_gpio_disable_ms(gpios, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms_sync);
|
||||
|
||||
/**
|
||||
* mctrl_gpio_disable_ms_no_sync - disable irqs and handling of changes to the
|
||||
* ms lines, and return immediately
|
||||
* @gpios: gpios to disable
|
||||
*/
|
||||
void mctrl_gpio_disable_ms_no_sync(struct mctrl_gpios *gpios)
|
||||
{
|
||||
mctrl_gpio_disable_ms(gpios, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms_no_sync);
|
||||
|
||||
void mctrl_gpio_enable_irq_wake(struct mctrl_gpios *gpios)
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
|
||||
/*
|
||||
* Request and set direction of modem control line GPIOs and set up irq
|
||||
* handling.
|
||||
* devm_* functions are used, so there's no need to call mctrl_gpio_free().
|
||||
* devm_* functions are used, so there's no need to explicitly free.
|
||||
* Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on
|
||||
* allocation error.
|
||||
*/
|
||||
@ -67,29 +67,29 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx);
|
||||
|
||||
/*
|
||||
* Request and set direction of modem control line GPIOs.
|
||||
* devm_* functions are used, so there's no need to call mctrl_gpio_free().
|
||||
* devm_* functions are used, so there's no need to explicitly free.
|
||||
* Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on
|
||||
* allocation error.
|
||||
*/
|
||||
struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev,
|
||||
unsigned int idx);
|
||||
|
||||
/*
|
||||
* Free the mctrl_gpios structure.
|
||||
* Normally, this function will not be called, as the GPIOs will
|
||||
* be disposed of by the resource management code.
|
||||
*/
|
||||
void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios);
|
||||
|
||||
/*
|
||||
* Enable gpio interrupts to report status line changes.
|
||||
*/
|
||||
void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios);
|
||||
|
||||
/*
|
||||
* Disable gpio interrupts to report status line changes.
|
||||
* Disable gpio interrupts to report status line changes, and block until
|
||||
* any corresponding IRQ is processed
|
||||
*/
|
||||
void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios);
|
||||
void mctrl_gpio_disable_ms_sync(struct mctrl_gpios *gpios);
|
||||
|
||||
/*
|
||||
* Disable gpio interrupts to report status line changes, and return
|
||||
* immediately
|
||||
*/
|
||||
void mctrl_gpio_disable_ms_no_sync(struct mctrl_gpios *gpios);
|
||||
|
||||
/*
|
||||
* Enable gpio wakeup interrupts to enable wake up source.
|
||||
@ -139,16 +139,15 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
|
||||
static inline void mctrl_gpio_disable_ms_sync(struct mctrl_gpios *gpios)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mctrl_gpio_disable_ms_no_sync(struct mctrl_gpios *gpios)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,20 @@ struct plat_sci_reg {
|
||||
u8 offset, size;
|
||||
};
|
||||
|
||||
struct sci_suspend_regs {
|
||||
u16 scdl;
|
||||
u16 sccks;
|
||||
u16 scsmr;
|
||||
u16 scscr;
|
||||
u16 scfcr;
|
||||
u16 scsptr;
|
||||
u16 hssrr;
|
||||
u16 scpcr;
|
||||
u16 scpdr;
|
||||
u8 scbrr;
|
||||
u8 semr;
|
||||
};
|
||||
|
||||
struct sci_port_params {
|
||||
const struct plat_sci_reg regs[SCIx_NR_REGS];
|
||||
unsigned int fifosize;
|
||||
@ -134,6 +148,8 @@ struct sci_port {
|
||||
struct dma_chan *chan_tx;
|
||||
struct dma_chan *chan_rx;
|
||||
|
||||
struct reset_control *rstc;
|
||||
|
||||
#ifdef CONFIG_SERIAL_SH_SCI_DMA
|
||||
struct dma_chan *chan_tx_saved;
|
||||
struct dma_chan *chan_rx_saved;
|
||||
@ -153,6 +169,7 @@ struct sci_port {
|
||||
int rx_trigger;
|
||||
struct timer_list rx_fifo_timer;
|
||||
int rx_fifo_timeout;
|
||||
struct sci_suspend_regs suspend_regs;
|
||||
u16 hscif_tot;
|
||||
|
||||
bool has_rtscts;
|
||||
@ -2297,7 +2314,7 @@ static void sci_shutdown(struct uart_port *port)
|
||||
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
|
||||
|
||||
s->autorts = false;
|
||||
mctrl_gpio_disable_ms(to_sci_port(port)->gpios);
|
||||
mctrl_gpio_disable_ms_sync(to_sci_port(port)->gpios);
|
||||
|
||||
uart_port_lock_irqsave(port, &flags);
|
||||
sci_stop_rx(port);
|
||||
@ -3373,6 +3390,7 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
sp = &sci_ports[id];
|
||||
sp->rstc = rstc;
|
||||
*dev_id = id;
|
||||
|
||||
p->type = SCI_OF_TYPE(data);
|
||||
@ -3545,13 +3563,77 @@ static int sci_probe(struct platform_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sci_console_save(struct sci_port *s)
|
||||
{
|
||||
struct sci_suspend_regs *regs = &s->suspend_regs;
|
||||
struct uart_port *port = &s->port;
|
||||
|
||||
if (sci_getreg(port, SCDL)->size)
|
||||
regs->scdl = sci_serial_in(port, SCDL);
|
||||
if (sci_getreg(port, SCCKS)->size)
|
||||
regs->sccks = sci_serial_in(port, SCCKS);
|
||||
if (sci_getreg(port, SCSMR)->size)
|
||||
regs->scsmr = sci_serial_in(port, SCSMR);
|
||||
if (sci_getreg(port, SCSCR)->size)
|
||||
regs->scscr = sci_serial_in(port, SCSCR);
|
||||
if (sci_getreg(port, SCFCR)->size)
|
||||
regs->scfcr = sci_serial_in(port, SCFCR);
|
||||
if (sci_getreg(port, SCSPTR)->size)
|
||||
regs->scsptr = sci_serial_in(port, SCSPTR);
|
||||
if (sci_getreg(port, SCBRR)->size)
|
||||
regs->scbrr = sci_serial_in(port, SCBRR);
|
||||
if (sci_getreg(port, HSSRR)->size)
|
||||
regs->hssrr = sci_serial_in(port, HSSRR);
|
||||
if (sci_getreg(port, SCPCR)->size)
|
||||
regs->scpcr = sci_serial_in(port, SCPCR);
|
||||
if (sci_getreg(port, SCPDR)->size)
|
||||
regs->scpdr = sci_serial_in(port, SCPDR);
|
||||
if (sci_getreg(port, SEMR)->size)
|
||||
regs->semr = sci_serial_in(port, SEMR);
|
||||
}
|
||||
|
||||
static void sci_console_restore(struct sci_port *s)
|
||||
{
|
||||
struct sci_suspend_regs *regs = &s->suspend_regs;
|
||||
struct uart_port *port = &s->port;
|
||||
|
||||
if (sci_getreg(port, SCDL)->size)
|
||||
sci_serial_out(port, SCDL, regs->scdl);
|
||||
if (sci_getreg(port, SCCKS)->size)
|
||||
sci_serial_out(port, SCCKS, regs->sccks);
|
||||
if (sci_getreg(port, SCSMR)->size)
|
||||
sci_serial_out(port, SCSMR, regs->scsmr);
|
||||
if (sci_getreg(port, SCSCR)->size)
|
||||
sci_serial_out(port, SCSCR, regs->scscr);
|
||||
if (sci_getreg(port, SCFCR)->size)
|
||||
sci_serial_out(port, SCFCR, regs->scfcr);
|
||||
if (sci_getreg(port, SCSPTR)->size)
|
||||
sci_serial_out(port, SCSPTR, regs->scsptr);
|
||||
if (sci_getreg(port, SCBRR)->size)
|
||||
sci_serial_out(port, SCBRR, regs->scbrr);
|
||||
if (sci_getreg(port, HSSRR)->size)
|
||||
sci_serial_out(port, HSSRR, regs->hssrr);
|
||||
if (sci_getreg(port, SCPCR)->size)
|
||||
sci_serial_out(port, SCPCR, regs->scpcr);
|
||||
if (sci_getreg(port, SCPDR)->size)
|
||||
sci_serial_out(port, SCPDR, regs->scpdr);
|
||||
if (sci_getreg(port, SEMR)->size)
|
||||
sci_serial_out(port, SEMR, regs->semr);
|
||||
}
|
||||
|
||||
static __maybe_unused int sci_suspend(struct device *dev)
|
||||
{
|
||||
struct sci_port *sport = dev_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
if (sport) {
|
||||
uart_suspend_port(&sci_uart_driver, &sport->port);
|
||||
|
||||
if (!console_suspend_enabled && uart_console(&sport->port))
|
||||
sci_console_save(sport);
|
||||
else
|
||||
return reset_control_assert(sport->rstc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3559,8 +3641,18 @@ static __maybe_unused int sci_resume(struct device *dev)
|
||||
{
|
||||
struct sci_port *sport = dev_get_drvdata(dev);
|
||||
|
||||
if (sport)
|
||||
if (sport) {
|
||||
if (!console_suspend_enabled && uart_console(&sport->port)) {
|
||||
sci_console_restore(sport);
|
||||
} else {
|
||||
int ret = reset_control_deassert(sport->rstc);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
uart_resume_port(&sci_uart_driver, &sport->port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -944,7 +944,7 @@ static void stm32_usart_enable_ms(struct uart_port *port)
|
||||
|
||||
static void stm32_usart_disable_ms(struct uart_port *port)
|
||||
{
|
||||
mctrl_gpio_disable_ms(to_stm32_port(port)->gpios);
|
||||
mctrl_gpio_disable_ms_sync(to_stm32_port(port)->gpios);
|
||||
}
|
||||
|
||||
/* Transmit stop */
|
||||
@ -965,10 +965,8 @@ static void stm32_usart_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct tty_port *tport = &port->state->port;
|
||||
|
||||
if (kfifo_is_empty(&tport->xmit_fifo) && !port->x_char) {
|
||||
stm32_usart_rs485_rts_disable(port);
|
||||
if (kfifo_is_empty(&tport->xmit_fifo) && !port->x_char)
|
||||
return;
|
||||
}
|
||||
|
||||
stm32_usart_rs485_rts_enable(port);
|
||||
|
||||
|
@ -150,16 +150,6 @@ static void serial_out(struct uart_sunsu_port *up, int offset, int value)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We used to support using pause I/O for certain machines. We
|
||||
* haven't supported this for a while, but just in case it's badly
|
||||
* needed for certain old 386 machines, I've left these #define's
|
||||
* in....
|
||||
*/
|
||||
#define serial_inp(up, offset) serial_in(up, offset)
|
||||
#define serial_outp(up, offset, value) serial_out(up, offset, value)
|
||||
|
||||
|
||||
/*
|
||||
* For the 16C950
|
||||
*/
|
||||
@ -169,20 +159,6 @@ static void serial_icr_write(struct uart_sunsu_port *up, int offset, int value)
|
||||
serial_out(up, UART_ICR, value);
|
||||
}
|
||||
|
||||
#if 0 /* Unused currently */
|
||||
static unsigned int serial_icr_read(struct uart_sunsu_port *up, int offset)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
|
||||
serial_out(up, UART_SCR, offset);
|
||||
value = serial_in(up, UART_ICR);
|
||||
serial_icr_write(up, UART_ACR, up->acr);
|
||||
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_RSA
|
||||
/*
|
||||
* Attempts to turn on the RSA FIFO. Returns zero on failure.
|
||||
@ -193,12 +169,12 @@ static int __enable_rsa(struct uart_sunsu_port *up)
|
||||
unsigned char mode;
|
||||
int result;
|
||||
|
||||
mode = serial_inp(up, UART_RSA_MSR);
|
||||
mode = serial_in(up, UART_RSA_MSR);
|
||||
result = mode & UART_RSA_MSR_FIFO;
|
||||
|
||||
if (!result) {
|
||||
serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
|
||||
mode = serial_inp(up, UART_RSA_MSR);
|
||||
serial_out(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
|
||||
mode = serial_in(up, UART_RSA_MSR);
|
||||
result = mode & UART_RSA_MSR_FIFO;
|
||||
}
|
||||
|
||||
@ -217,7 +193,7 @@ static void enable_rsa(struct uart_sunsu_port *up)
|
||||
uart_port_unlock_irq(&up->port);
|
||||
}
|
||||
if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
|
||||
serial_outp(up, UART_RSA_FRR, 0);
|
||||
serial_out(up, UART_RSA_FRR, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,12 +212,12 @@ static void disable_rsa(struct uart_sunsu_port *up)
|
||||
up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
|
||||
uart_port_lock_irq(&up->port);
|
||||
|
||||
mode = serial_inp(up, UART_RSA_MSR);
|
||||
mode = serial_in(up, UART_RSA_MSR);
|
||||
result = !(mode & UART_RSA_MSR_FIFO);
|
||||
|
||||
if (!result) {
|
||||
serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
|
||||
mode = serial_inp(up, UART_RSA_MSR);
|
||||
serial_out(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
|
||||
mode = serial_in(up, UART_RSA_MSR);
|
||||
result = !(mode & UART_RSA_MSR_FIFO);
|
||||
}
|
||||
|
||||
@ -326,7 +302,7 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status)
|
||||
int saw_console_brk = 0;
|
||||
|
||||
do {
|
||||
ch = serial_inp(up, UART_RX);
|
||||
ch = serial_in(up, UART_RX);
|
||||
flag = TTY_NORMAL;
|
||||
up->port.icount.rx++;
|
||||
|
||||
@ -387,7 +363,7 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status)
|
||||
*/
|
||||
tty_insert_flip_char(port, 0, TTY_OVERRUN);
|
||||
ignore_char:
|
||||
*status = serial_inp(up, UART_LSR);
|
||||
*status = serial_in(up, UART_LSR);
|
||||
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
|
||||
|
||||
if (saw_console_brk)
|
||||
@ -401,7 +377,7 @@ static void transmit_chars(struct uart_sunsu_port *up)
|
||||
int count;
|
||||
|
||||
if (up->port.x_char) {
|
||||
serial_outp(up, UART_TX, up->port.x_char);
|
||||
serial_out(up, UART_TX, up->port.x_char);
|
||||
up->port.icount.tx++;
|
||||
up->port.x_char = 0;
|
||||
return;
|
||||
@ -460,7 +436,7 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id)
|
||||
uart_port_lock_irqsave(&up->port, &flags);
|
||||
|
||||
do {
|
||||
status = serial_inp(up, UART_LSR);
|
||||
status = serial_in(up, UART_LSR);
|
||||
if (status & UART_LSR_DR)
|
||||
receive_chars(up, &status);
|
||||
check_modem_status(up);
|
||||
@ -498,7 +474,7 @@ static void sunsu_change_mouse_baud(struct uart_sunsu_port *up)
|
||||
static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break)
|
||||
{
|
||||
do {
|
||||
unsigned char ch = serial_inp(up, UART_RX);
|
||||
unsigned char ch = serial_in(up, UART_RX);
|
||||
|
||||
/* Stop-A is handled by drivers/char/keyboard.c now. */
|
||||
if (up->su_type == SU_PORT_KBD) {
|
||||
@ -530,7 +506,7 @@ static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id)
|
||||
struct uart_sunsu_port *up = dev_id;
|
||||
|
||||
if (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)) {
|
||||
unsigned char status = serial_inp(up, UART_LSR);
|
||||
unsigned char status = serial_in(up, UART_LSR);
|
||||
|
||||
if ((status & UART_LSR_DR) || (status & UART_LSR_BI))
|
||||
receive_kbd_ms_chars(up, (status & UART_LSR_BI) != 0);
|
||||
@ -619,14 +595,14 @@ static int sunsu_startup(struct uart_port *port)
|
||||
if (up->port.type == PORT_16C950) {
|
||||
/* Wake up and initialize UART */
|
||||
up->acr = 0;
|
||||
serial_outp(up, UART_LCR, 0xBF);
|
||||
serial_outp(up, UART_EFR, UART_EFR_ECB);
|
||||
serial_outp(up, UART_IER, 0);
|
||||
serial_outp(up, UART_LCR, 0);
|
||||
serial_out(up, UART_LCR, 0xBF);
|
||||
serial_out(up, UART_EFR, UART_EFR_ECB);
|
||||
serial_out(up, UART_IER, 0);
|
||||
serial_out(up, UART_LCR, 0);
|
||||
serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
|
||||
serial_outp(up, UART_LCR, 0xBF);
|
||||
serial_outp(up, UART_EFR, UART_EFR_ECB);
|
||||
serial_outp(up, UART_LCR, 0);
|
||||
serial_out(up, UART_LCR, 0xBF);
|
||||
serial_out(up, UART_EFR, UART_EFR_ECB);
|
||||
serial_out(up, UART_LCR, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_RSA
|
||||
@ -642,19 +618,19 @@ static int sunsu_startup(struct uart_port *port)
|
||||
* (they will be reenabled in set_termios())
|
||||
*/
|
||||
if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) {
|
||||
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
||||
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_outp(up, UART_FCR, 0);
|
||||
serial_out(up, UART_FCR, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the interrupt registers.
|
||||
*/
|
||||
(void) serial_inp(up, UART_LSR);
|
||||
(void) serial_inp(up, UART_RX);
|
||||
(void) serial_inp(up, UART_IIR);
|
||||
(void) serial_inp(up, UART_MSR);
|
||||
(void) serial_in(up, UART_LSR);
|
||||
(void) serial_in(up, UART_RX);
|
||||
(void) serial_in(up, UART_IIR);
|
||||
(void) serial_in(up, UART_MSR);
|
||||
|
||||
/*
|
||||
* At this point, there's no way the LSR could still be 0xff;
|
||||
@ -662,7 +638,7 @@ static int sunsu_startup(struct uart_port *port)
|
||||
* here.
|
||||
*/
|
||||
if (!(up->port.flags & UPF_BUGGY_UART) &&
|
||||
(serial_inp(up, UART_LSR) == 0xff)) {
|
||||
(serial_in(up, UART_LSR) == 0xff)) {
|
||||
printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -682,7 +658,7 @@ static int sunsu_startup(struct uart_port *port)
|
||||
/*
|
||||
* Now, initialize the UART
|
||||
*/
|
||||
serial_outp(up, UART_LCR, UART_LCR_WLEN8);
|
||||
serial_out(up, UART_LCR, UART_LCR_WLEN8);
|
||||
|
||||
uart_port_lock_irqsave(&up->port, &flags);
|
||||
|
||||
@ -697,7 +673,7 @@ static int sunsu_startup(struct uart_port *port)
|
||||
* anyway, so we don't enable them here.
|
||||
*/
|
||||
up->ier = UART_IER_RLSI | UART_IER_RDI;
|
||||
serial_outp(up, UART_IER, up->ier);
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
|
||||
if (up->port.flags & UPF_FOURPORT) {
|
||||
unsigned int icp;
|
||||
@ -712,10 +688,10 @@ static int sunsu_startup(struct uart_port *port)
|
||||
/*
|
||||
* And clear the interrupt registers again for luck.
|
||||
*/
|
||||
(void) serial_inp(up, UART_LSR);
|
||||
(void) serial_inp(up, UART_RX);
|
||||
(void) serial_inp(up, UART_IIR);
|
||||
(void) serial_inp(up, UART_MSR);
|
||||
(void) serial_in(up, UART_LSR);
|
||||
(void) serial_in(up, UART_RX);
|
||||
(void) serial_in(up, UART_IIR);
|
||||
(void) serial_in(up, UART_MSR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -730,7 +706,7 @@ static void sunsu_shutdown(struct uart_port *port)
|
||||
* Disable interrupts from this port
|
||||
*/
|
||||
up->ier = 0;
|
||||
serial_outp(up, UART_IER, 0);
|
||||
serial_out(up, UART_IER, 0);
|
||||
|
||||
uart_port_lock_irqsave(&up->port, &flags);
|
||||
if (up->port.flags & UPF_FOURPORT) {
|
||||
@ -746,11 +722,11 @@ static void sunsu_shutdown(struct uart_port *port)
|
||||
/*
|
||||
* Disable break condition and FIFOs
|
||||
*/
|
||||
serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
|
||||
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
||||
serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
|
||||
UART_FCR_CLEAR_RCVR |
|
||||
UART_FCR_CLEAR_XMIT);
|
||||
serial_outp(up, UART_FCR, 0);
|
||||
serial_out(up, UART_FCR, 0);
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_RSA
|
||||
/*
|
||||
@ -872,22 +848,22 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag,
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
|
||||
if (uart_config[up->port.type].flags & UART_STARTECH) {
|
||||
serial_outp(up, UART_LCR, 0xBF);
|
||||
serial_outp(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0);
|
||||
serial_out(up, UART_LCR, 0xBF);
|
||||
serial_out(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0);
|
||||
}
|
||||
serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
|
||||
serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */
|
||||
serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */
|
||||
serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
|
||||
serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */
|
||||
serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */
|
||||
if (up->port.type == PORT_16750)
|
||||
serial_outp(up, UART_FCR, fcr); /* set fcr */
|
||||
serial_outp(up, UART_LCR, cval); /* reset DLAB */
|
||||
serial_out(up, UART_FCR, fcr); /* set fcr */
|
||||
serial_out(up, UART_LCR, cval); /* reset DLAB */
|
||||
up->lcr = cval; /* Save LCR */
|
||||
if (up->port.type != PORT_16750) {
|
||||
if (fcr & UART_FCR_ENABLE_FIFO) {
|
||||
/* emulated UARTs (Lucent Venus 167x) need two steps */
|
||||
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
}
|
||||
serial_outp(up, UART_FCR, fcr); /* set fcr */
|
||||
serial_out(up, UART_FCR, fcr); /* set fcr */
|
||||
}
|
||||
|
||||
up->cflag = cflag;
|
||||
@ -1051,18 +1027,18 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up)
|
||||
* 0x80 is a non-existent port; which should be safe since
|
||||
* include/asm/io.h also makes this assumption.
|
||||
*/
|
||||
scratch = serial_inp(up, UART_IER);
|
||||
serial_outp(up, UART_IER, 0);
|
||||
scratch = serial_in(up, UART_IER);
|
||||
serial_out(up, UART_IER, 0);
|
||||
#ifdef __i386__
|
||||
outb(0xff, 0x080);
|
||||
#endif
|
||||
scratch2 = serial_inp(up, UART_IER);
|
||||
serial_outp(up, UART_IER, 0x0f);
|
||||
scratch2 = serial_in(up, UART_IER);
|
||||
serial_out(up, UART_IER, 0x0f);
|
||||
#ifdef __i386__
|
||||
outb(0, 0x080);
|
||||
#endif
|
||||
scratch3 = serial_inp(up, UART_IER);
|
||||
serial_outp(up, UART_IER, scratch);
|
||||
scratch3 = serial_in(up, UART_IER);
|
||||
serial_out(up, UART_IER, scratch);
|
||||
if (scratch2 != 0 || scratch3 != 0x0F)
|
||||
goto out; /* We failed; there's nothing here */
|
||||
}
|
||||
@ -1080,16 +1056,16 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up)
|
||||
* that conflicts with COM 1-4 --- we hope!
|
||||
*/
|
||||
if (!(up->port.flags & UPF_SKIP_TEST)) {
|
||||
serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
|
||||
status1 = serial_inp(up, UART_MSR) & 0xF0;
|
||||
serial_outp(up, UART_MCR, save_mcr);
|
||||
serial_out(up, UART_MCR, UART_MCR_LOOP | 0x0A);
|
||||
status1 = serial_in(up, UART_MSR) & 0xF0;
|
||||
serial_out(up, UART_MCR, save_mcr);
|
||||
if (status1 != 0x90)
|
||||
goto out; /* We failed loopback test */
|
||||
}
|
||||
serial_outp(up, UART_LCR, 0xBF); /* set up for StarTech test */
|
||||
serial_outp(up, UART_EFR, 0); /* EFR is the same as FCR */
|
||||
serial_outp(up, UART_LCR, 0);
|
||||
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
serial_out(up, UART_LCR, 0xBF); /* set up for StarTech test */
|
||||
serial_out(up, UART_EFR, 0); /* EFR is the same as FCR */
|
||||
serial_out(up, UART_LCR, 0);
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
scratch = serial_in(up, UART_IIR) >> 6;
|
||||
switch (scratch) {
|
||||
case 0:
|
||||
@ -1107,19 +1083,19 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up)
|
||||
}
|
||||
if (up->port.type == PORT_16550A) {
|
||||
/* Check for Startech UART's */
|
||||
serial_outp(up, UART_LCR, UART_LCR_DLAB);
|
||||
serial_out(up, UART_LCR, UART_LCR_DLAB);
|
||||
if (serial_in(up, UART_EFR) == 0) {
|
||||
up->port.type = PORT_16650;
|
||||
} else {
|
||||
serial_outp(up, UART_LCR, 0xBF);
|
||||
serial_out(up, UART_LCR, 0xBF);
|
||||
if (serial_in(up, UART_EFR) == 0)
|
||||
up->port.type = PORT_16650V2;
|
||||
}
|
||||
}
|
||||
if (up->port.type == PORT_16550A) {
|
||||
/* Check for TI 16750 */
|
||||
serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB);
|
||||
serial_outp(up, UART_FCR,
|
||||
serial_out(up, UART_LCR, save_lcr | UART_LCR_DLAB);
|
||||
serial_out(up, UART_FCR,
|
||||
UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
|
||||
scratch = serial_in(up, UART_IIR) >> 5;
|
||||
if (scratch == 7) {
|
||||
@ -1129,24 +1105,24 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up)
|
||||
* mode if the UART_FCR7_64BYTE bit was set
|
||||
* while UART_LCR_DLAB was latched.
|
||||
*/
|
||||
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
serial_outp(up, UART_LCR, 0);
|
||||
serial_outp(up, UART_FCR,
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
serial_out(up, UART_LCR, 0);
|
||||
serial_out(up, UART_FCR,
|
||||
UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
|
||||
scratch = serial_in(up, UART_IIR) >> 5;
|
||||
if (scratch == 6)
|
||||
up->port.type = PORT_16750;
|
||||
}
|
||||
serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
|
||||
}
|
||||
serial_outp(up, UART_LCR, save_lcr);
|
||||
serial_out(up, UART_LCR, save_lcr);
|
||||
if (up->port.type == PORT_16450) {
|
||||
scratch = serial_in(up, UART_SCR);
|
||||
serial_outp(up, UART_SCR, 0xa5);
|
||||
serial_out(up, UART_SCR, 0xa5);
|
||||
status1 = serial_in(up, UART_SCR);
|
||||
serial_outp(up, UART_SCR, 0x5a);
|
||||
serial_out(up, UART_SCR, 0x5a);
|
||||
status2 = serial_in(up, UART_SCR);
|
||||
serial_outp(up, UART_SCR, scratch);
|
||||
serial_out(up, UART_SCR, scratch);
|
||||
|
||||
if ((status1 != 0xa5) || (status2 != 0x5a))
|
||||
up->port.type = PORT_8250;
|
||||
@ -1163,15 +1139,15 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up)
|
||||
*/
|
||||
#ifdef CONFIG_SERIAL_8250_RSA
|
||||
if (up->port.type == PORT_RSA)
|
||||
serial_outp(up, UART_RSA_FRR, 0);
|
||||
serial_out(up, UART_RSA_FRR, 0);
|
||||
#endif
|
||||
serial_outp(up, UART_MCR, save_mcr);
|
||||
serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO |
|
||||
serial_out(up, UART_MCR, save_mcr);
|
||||
serial_out(up, UART_FCR, (UART_FCR_ENABLE_FIFO |
|
||||
UART_FCR_CLEAR_RCVR |
|
||||
UART_FCR_CLEAR_XMIT));
|
||||
serial_outp(up, UART_FCR, 0);
|
||||
serial_out(up, UART_FCR, 0);
|
||||
(void)serial_in(up, UART_RX);
|
||||
serial_outp(up, UART_IER, 0);
|
||||
serial_out(up, UART_IER, 0);
|
||||
|
||||
out:
|
||||
uart_port_unlock_irqrestore(&up->port, flags);
|
||||
|
625
drivers/tty/serial/tegra-utc.c
Normal file
625
drivers/tty/serial/tegra-utc.c
Normal file
@ -0,0 +1,625 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// NVIDIA Tegra UTC (UART Trace Controller) driver.
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define TEGRA_UTC_ENABLE 0x000
|
||||
#define TEGRA_UTC_ENABLE_CLIENT_ENABLE BIT(0)
|
||||
|
||||
#define TEGRA_UTC_FIFO_THRESHOLD 0x008
|
||||
|
||||
#define TEGRA_UTC_COMMAND 0x00c
|
||||
#define TEGRA_UTC_COMMAND_RESET BIT(0)
|
||||
#define TEGRA_UTC_COMMAND_FLUSH BIT(1)
|
||||
|
||||
#define TEGRA_UTC_DATA 0x020
|
||||
|
||||
#define TEGRA_UTC_FIFO_STATUS 0x100
|
||||
#define TEGRA_UTC_FIFO_EMPTY BIT(0)
|
||||
#define TEGRA_UTC_FIFO_FULL BIT(1)
|
||||
#define TEGRA_UTC_FIFO_REQ BIT(2)
|
||||
#define TEGRA_UTC_FIFO_OVERFLOW BIT(3)
|
||||
#define TEGRA_UTC_FIFO_TIMEOUT BIT(4)
|
||||
|
||||
#define TEGRA_UTC_FIFO_OCCUPANCY 0x104
|
||||
|
||||
#define TEGRA_UTC_INTR_STATUS 0x108
|
||||
#define TEGRA_UTC_INTR_SET 0x10c
|
||||
#define TEGRA_UTC_INTR_MASK 0x110
|
||||
#define TEGRA_UTC_INTR_CLEAR 0x114
|
||||
#define TEGRA_UTC_INTR_EMPTY BIT(0)
|
||||
#define TEGRA_UTC_INTR_FULL BIT(1)
|
||||
#define TEGRA_UTC_INTR_REQ BIT(2)
|
||||
#define TEGRA_UTC_INTR_OVERFLOW BIT(3)
|
||||
#define TEGRA_UTC_INTR_TIMEOUT BIT(4)
|
||||
|
||||
#define TEGRA_UTC_UART_NR 16
|
||||
|
||||
#define TEGRA_UTC_INTR_COMMON (TEGRA_UTC_INTR_REQ | TEGRA_UTC_INTR_FULL | TEGRA_UTC_INTR_EMPTY)
|
||||
|
||||
struct tegra_utc_port {
|
||||
#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
|
||||
struct console console;
|
||||
#endif
|
||||
struct uart_port port;
|
||||
|
||||
void __iomem *rx_base;
|
||||
void __iomem *tx_base;
|
||||
|
||||
u32 tx_irqmask;
|
||||
u32 rx_irqmask;
|
||||
|
||||
unsigned int fifosize;
|
||||
u32 tx_threshold;
|
||||
u32 rx_threshold;
|
||||
};
|
||||
|
||||
static u32 tegra_utc_rx_readl(struct tegra_utc_port *tup, unsigned int offset)
|
||||
{
|
||||
void __iomem *addr = tup->rx_base + offset;
|
||||
|
||||
return readl_relaxed(addr);
|
||||
}
|
||||
|
||||
static void tegra_utc_rx_writel(struct tegra_utc_port *tup, u32 val, unsigned int offset)
|
||||
{
|
||||
void __iomem *addr = tup->rx_base + offset;
|
||||
|
||||
writel_relaxed(val, addr);
|
||||
}
|
||||
|
||||
static u32 tegra_utc_tx_readl(struct tegra_utc_port *tup, unsigned int offset)
|
||||
{
|
||||
void __iomem *addr = tup->tx_base + offset;
|
||||
|
||||
return readl_relaxed(addr);
|
||||
}
|
||||
|
||||
static void tegra_utc_tx_writel(struct tegra_utc_port *tup, u32 val, unsigned int offset)
|
||||
{
|
||||
void __iomem *addr = tup->tx_base + offset;
|
||||
|
||||
writel_relaxed(val, addr);
|
||||
}
|
||||
|
||||
static void tegra_utc_enable_tx_irq(struct tegra_utc_port *tup)
|
||||
{
|
||||
tup->tx_irqmask = TEGRA_UTC_INTR_REQ;
|
||||
|
||||
tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_MASK);
|
||||
tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_SET);
|
||||
}
|
||||
|
||||
static void tegra_utc_disable_tx_irq(struct tegra_utc_port *tup)
|
||||
{
|
||||
tup->tx_irqmask = 0x0;
|
||||
|
||||
tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_MASK);
|
||||
tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_SET);
|
||||
}
|
||||
|
||||
static void tegra_utc_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
|
||||
tegra_utc_disable_tx_irq(tup);
|
||||
}
|
||||
|
||||
static void tegra_utc_init_tx(struct tegra_utc_port *tup)
|
||||
{
|
||||
/* Disable TX. */
|
||||
tegra_utc_tx_writel(tup, 0x0, TEGRA_UTC_ENABLE);
|
||||
|
||||
/* Update the FIFO Threshold. */
|
||||
tegra_utc_tx_writel(tup, tup->tx_threshold, TEGRA_UTC_FIFO_THRESHOLD);
|
||||
|
||||
/* Clear and mask all the interrupts. */
|
||||
tegra_utc_tx_writel(tup, TEGRA_UTC_INTR_COMMON, TEGRA_UTC_INTR_CLEAR);
|
||||
tegra_utc_disable_tx_irq(tup);
|
||||
|
||||
/* Enable TX. */
|
||||
tegra_utc_tx_writel(tup, TEGRA_UTC_ENABLE_CLIENT_ENABLE, TEGRA_UTC_ENABLE);
|
||||
}
|
||||
|
||||
static void tegra_utc_init_rx(struct tegra_utc_port *tup)
|
||||
{
|
||||
tup->rx_irqmask = TEGRA_UTC_INTR_REQ | TEGRA_UTC_INTR_TIMEOUT;
|
||||
|
||||
tegra_utc_rx_writel(tup, TEGRA_UTC_COMMAND_RESET, TEGRA_UTC_COMMAND);
|
||||
tegra_utc_rx_writel(tup, tup->rx_threshold, TEGRA_UTC_FIFO_THRESHOLD);
|
||||
|
||||
/* Clear all the pending interrupts. */
|
||||
tegra_utc_rx_writel(tup, TEGRA_UTC_INTR_TIMEOUT | TEGRA_UTC_INTR_OVERFLOW |
|
||||
TEGRA_UTC_INTR_COMMON, TEGRA_UTC_INTR_CLEAR);
|
||||
tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_MASK);
|
||||
tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_SET);
|
||||
|
||||
/* Enable RX. */
|
||||
tegra_utc_rx_writel(tup, TEGRA_UTC_ENABLE_CLIENT_ENABLE, TEGRA_UTC_ENABLE);
|
||||
}
|
||||
|
||||
static bool tegra_utc_tx_chars(struct tegra_utc_port *tup)
|
||||
{
|
||||
struct uart_port *port = &tup->port;
|
||||
unsigned int pending;
|
||||
u8 c;
|
||||
|
||||
pending = uart_port_tx(port, c,
|
||||
!(tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_STATUS) & TEGRA_UTC_FIFO_FULL),
|
||||
tegra_utc_tx_writel(tup, c, TEGRA_UTC_DATA));
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
static void tegra_utc_rx_chars(struct tegra_utc_port *tup)
|
||||
{
|
||||
struct tty_port *port = &tup->port.state->port;
|
||||
unsigned int max_chars = 256;
|
||||
u32 status;
|
||||
int sysrq;
|
||||
u32 ch;
|
||||
|
||||
while (max_chars--) {
|
||||
status = tegra_utc_rx_readl(tup, TEGRA_UTC_FIFO_STATUS);
|
||||
if (status & TEGRA_UTC_FIFO_EMPTY)
|
||||
break;
|
||||
|
||||
ch = tegra_utc_rx_readl(tup, TEGRA_UTC_DATA);
|
||||
tup->port.icount.rx++;
|
||||
|
||||
if (status & TEGRA_UTC_FIFO_OVERFLOW)
|
||||
tup->port.icount.overrun++;
|
||||
|
||||
uart_port_unlock(&tup->port);
|
||||
sysrq = uart_handle_sysrq_char(&tup->port, ch);
|
||||
uart_port_lock(&tup->port);
|
||||
|
||||
if (!sysrq)
|
||||
tty_insert_flip_char(port, ch, TTY_NORMAL);
|
||||
}
|
||||
|
||||
tty_flip_buffer_push(port);
|
||||
}
|
||||
|
||||
static irqreturn_t tegra_utc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct tegra_utc_port *tup = dev_id;
|
||||
unsigned int handled = 0;
|
||||
u32 status;
|
||||
|
||||
uart_port_lock(&tup->port);
|
||||
|
||||
/* Process RX_REQ and RX_TIMEOUT interrupts. */
|
||||
do {
|
||||
status = tegra_utc_rx_readl(tup, TEGRA_UTC_INTR_STATUS) & tup->rx_irqmask;
|
||||
if (status) {
|
||||
tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_CLEAR);
|
||||
tegra_utc_rx_chars(tup);
|
||||
handled = 1;
|
||||
}
|
||||
} while (status);
|
||||
|
||||
/* Process TX_REQ interrupt. */
|
||||
do {
|
||||
status = tegra_utc_tx_readl(tup, TEGRA_UTC_INTR_STATUS) & tup->tx_irqmask;
|
||||
if (status) {
|
||||
tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_CLEAR);
|
||||
tegra_utc_tx_chars(tup);
|
||||
handled = 1;
|
||||
}
|
||||
} while (status);
|
||||
|
||||
uart_port_unlock(&tup->port);
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static unsigned int tegra_utc_tx_empty(struct uart_port *port)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
|
||||
return tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_OCCUPANCY) ? 0 : TIOCSER_TEMT;
|
||||
}
|
||||
|
||||
static void tegra_utc_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
}
|
||||
|
||||
static unsigned int tegra_utc_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_utc_start_tx(struct uart_port *port)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
|
||||
if (tegra_utc_tx_chars(tup))
|
||||
tegra_utc_enable_tx_irq(tup);
|
||||
}
|
||||
|
||||
static void tegra_utc_stop_rx(struct uart_port *port)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
|
||||
tup->rx_irqmask = 0x0;
|
||||
tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_MASK);
|
||||
tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_SET);
|
||||
}
|
||||
|
||||
static void tegra_utc_hw_init(struct tegra_utc_port *tup)
|
||||
{
|
||||
tegra_utc_init_tx(tup);
|
||||
tegra_utc_init_rx(tup);
|
||||
}
|
||||
|
||||
static int tegra_utc_startup(struct uart_port *port)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
int ret;
|
||||
|
||||
tegra_utc_hw_init(tup);
|
||||
|
||||
/* Interrupt is dedicated to this UTC client. */
|
||||
ret = request_irq(port->irq, tegra_utc_isr, 0, dev_name(port->dev), tup);
|
||||
if (ret < 0)
|
||||
dev_err(port->dev, "failed to register interrupt handler\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tegra_utc_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
|
||||
tegra_utc_rx_writel(tup, 0x0, TEGRA_UTC_ENABLE);
|
||||
free_irq(port->irq, tup);
|
||||
}
|
||||
|
||||
static void tegra_utc_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
const struct ktermios *old)
|
||||
{
|
||||
/* The Tegra UTC clients supports only 8-N-1 configuration without HW flow control */
|
||||
termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
|
||||
termios->c_cflag &= ~(CMSPAR | CRTSCTS);
|
||||
termios->c_cflag |= CS8 | CLOCAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
|
||||
static int tegra_utc_poll_init(struct uart_port *port)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
|
||||
tegra_utc_hw_init(tup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_utc_get_poll_char(struct uart_port *port)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
|
||||
if (tegra_utc_rx_readl(tup, TEGRA_UTC_FIFO_STATUS) & TEGRA_UTC_FIFO_EMPTY)
|
||||
return NO_POLL_CHAR;
|
||||
|
||||
return tegra_utc_rx_readl(tup, TEGRA_UTC_DATA);
|
||||
}
|
||||
|
||||
static void tegra_utc_put_poll_char(struct uart_port *port, unsigned char ch)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
u32 val;
|
||||
|
||||
read_poll_timeout_atomic(tegra_utc_tx_readl, val, !(val & TEGRA_UTC_FIFO_FULL),
|
||||
0, USEC_PER_SEC, false, tup, TEGRA_UTC_FIFO_STATUS);
|
||||
|
||||
tegra_utc_tx_writel(tup, ch, TEGRA_UTC_DATA);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static const struct uart_ops tegra_utc_uart_ops = {
|
||||
.tx_empty = tegra_utc_tx_empty,
|
||||
.set_mctrl = tegra_utc_set_mctrl,
|
||||
.get_mctrl = tegra_utc_get_mctrl,
|
||||
.stop_tx = tegra_utc_stop_tx,
|
||||
.start_tx = tegra_utc_start_tx,
|
||||
.stop_rx = tegra_utc_stop_rx,
|
||||
.startup = tegra_utc_startup,
|
||||
.shutdown = tegra_utc_shutdown,
|
||||
.set_termios = tegra_utc_set_termios,
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
.poll_init = tegra_utc_poll_init,
|
||||
.poll_get_char = tegra_utc_get_poll_char,
|
||||
.poll_put_char = tegra_utc_put_poll_char,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
|
||||
#define TEGRA_UTC_DEFAULT_FIFO_THRESHOLD 4
|
||||
#define TEGRA_UTC_EARLYCON_MAX_BURST_SIZE 128
|
||||
|
||||
static void tegra_utc_putc(struct uart_port *port, unsigned char c)
|
||||
{
|
||||
writel(c, port->membase + TEGRA_UTC_DATA);
|
||||
}
|
||||
|
||||
static void tegra_utc_early_write(struct console *con, const char *s, unsigned int n)
|
||||
{
|
||||
struct earlycon_device *dev = con->data;
|
||||
|
||||
while (n) {
|
||||
u32 burst_size = TEGRA_UTC_EARLYCON_MAX_BURST_SIZE;
|
||||
|
||||
burst_size -= readl(dev->port.membase + TEGRA_UTC_FIFO_OCCUPANCY);
|
||||
if (n < burst_size)
|
||||
burst_size = n;
|
||||
|
||||
uart_console_write(&dev->port, s, burst_size, tegra_utc_putc);
|
||||
|
||||
n -= burst_size;
|
||||
s += burst_size;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init tegra_utc_early_console_setup(struct earlycon_device *device, const char *opt)
|
||||
{
|
||||
if (!device->port.membase)
|
||||
return -ENODEV;
|
||||
|
||||
/* Configure TX */
|
||||
writel(TEGRA_UTC_COMMAND_FLUSH | TEGRA_UTC_COMMAND_RESET,
|
||||
device->port.membase + TEGRA_UTC_COMMAND);
|
||||
writel(TEGRA_UTC_DEFAULT_FIFO_THRESHOLD, device->port.membase + TEGRA_UTC_FIFO_THRESHOLD);
|
||||
|
||||
/* Clear and mask all the interrupts. */
|
||||
writel(TEGRA_UTC_INTR_COMMON, device->port.membase + TEGRA_UTC_INTR_CLEAR);
|
||||
|
||||
writel(0x0, device->port.membase + TEGRA_UTC_INTR_MASK);
|
||||
writel(0x0, device->port.membase + TEGRA_UTC_INTR_SET);
|
||||
|
||||
/* Enable TX. */
|
||||
writel(TEGRA_UTC_ENABLE_CLIENT_ENABLE, device->port.membase + TEGRA_UTC_ENABLE);
|
||||
|
||||
device->con->write = tegra_utc_early_write;
|
||||
|
||||
return 0;
|
||||
}
|
||||
OF_EARLYCON_DECLARE(tegra_utc, "nvidia,tegra264-utc", tegra_utc_early_console_setup);
|
||||
|
||||
static void tegra_utc_console_putchar(struct uart_port *port, unsigned char ch)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
|
||||
|
||||
tegra_utc_tx_writel(tup, ch, TEGRA_UTC_DATA);
|
||||
}
|
||||
|
||||
static void tegra_utc_console_write_atomic(struct console *cons, struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
|
||||
unsigned int len;
|
||||
char *outbuf;
|
||||
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
return;
|
||||
|
||||
outbuf = wctxt->outbuf;
|
||||
len = wctxt->len;
|
||||
|
||||
while (len) {
|
||||
u32 burst_size = tup->fifosize;
|
||||
|
||||
burst_size -= tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_OCCUPANCY);
|
||||
if (len < burst_size)
|
||||
burst_size = len;
|
||||
|
||||
uart_console_write(&tup->port, outbuf, burst_size, tegra_utc_console_putchar);
|
||||
|
||||
outbuf += burst_size;
|
||||
len -= burst_size;
|
||||
};
|
||||
|
||||
nbcon_exit_unsafe(wctxt);
|
||||
}
|
||||
|
||||
static void tegra_utc_console_write_thread(struct console *cons, struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
|
||||
unsigned int len = READ_ONCE(wctxt->len);
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
break;
|
||||
|
||||
read_poll_timeout_atomic(tegra_utc_tx_readl, val, !(val & TEGRA_UTC_FIFO_FULL),
|
||||
0, USEC_PER_SEC, false, tup, TEGRA_UTC_FIFO_STATUS);
|
||||
uart_console_write(&tup->port, wctxt->outbuf + i, 1, tegra_utc_console_putchar);
|
||||
|
||||
if (!nbcon_exit_unsafe(wctxt))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tegra_utc_console_device_lock(struct console *cons, unsigned long *flags)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
|
||||
struct uart_port *port = &tup->port;
|
||||
|
||||
__uart_port_lock_irqsave(port, flags);
|
||||
}
|
||||
|
||||
static void tegra_utc_console_device_unlock(struct console *cons, unsigned long flags)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
|
||||
struct uart_port *port = &tup->port;
|
||||
|
||||
__uart_port_unlock_irqrestore(port, flags);
|
||||
}
|
||||
|
||||
static int tegra_utc_console_setup(struct console *cons, char *options)
|
||||
{
|
||||
struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
|
||||
|
||||
tegra_utc_init_tx(tup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct uart_driver tegra_utc_driver = {
|
||||
.driver_name = "tegra-utc",
|
||||
.dev_name = "ttyUTC",
|
||||
.nr = TEGRA_UTC_UART_NR,
|
||||
};
|
||||
|
||||
static int tegra_utc_setup_port(struct device *dev, struct tegra_utc_port *tup)
|
||||
{
|
||||
tup->port.dev = dev;
|
||||
tup->port.fifosize = tup->fifosize;
|
||||
tup->port.flags = UPF_BOOT_AUTOCONF;
|
||||
tup->port.iotype = UPIO_MEM;
|
||||
tup->port.ops = &tegra_utc_uart_ops;
|
||||
tup->port.type = PORT_TEGRA_TCU;
|
||||
tup->port.private_data = tup;
|
||||
|
||||
#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
|
||||
strscpy(tup->console.name, "ttyUTC", sizeof(tup->console.name));
|
||||
tup->console.write_atomic = tegra_utc_console_write_atomic;
|
||||
tup->console.write_thread = tegra_utc_console_write_thread;
|
||||
tup->console.device_lock = tegra_utc_console_device_lock;
|
||||
tup->console.device_unlock = tegra_utc_console_device_unlock;
|
||||
tup->console.device = uart_console_device;
|
||||
tup->console.setup = tegra_utc_console_setup;
|
||||
tup->console.flags = CON_PRINTBUFFER | CON_NBCON;
|
||||
tup->console.data = &tegra_utc_driver;
|
||||
#endif
|
||||
|
||||
return uart_read_port_properties(&tup->port);
|
||||
}
|
||||
|
||||
static int tegra_utc_register_port(struct tegra_utc_port *tup)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uart_add_one_port(&tegra_utc_driver, &tup->port);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
|
||||
register_console(&tup->console);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_utc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const unsigned int *soc_fifosize;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct tegra_utc_port *tup;
|
||||
int ret;
|
||||
|
||||
tup = devm_kzalloc(dev, sizeof(*tup), GFP_KERNEL);
|
||||
if (!tup)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = device_property_read_u32(dev, "tx-threshold", &tup->tx_threshold);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "missing %s property\n", "tx-threshold");
|
||||
|
||||
ret = device_property_read_u32(dev, "rx-threshold", &tup->rx_threshold);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "missing %s property\n", "rx-threshold");
|
||||
|
||||
soc_fifosize = device_get_match_data(dev);
|
||||
tup->fifosize = *soc_fifosize;
|
||||
|
||||
tup->tx_base = devm_platform_ioremap_resource_byname(pdev, "tx");
|
||||
if (IS_ERR(tup->tx_base))
|
||||
return PTR_ERR(tup->tx_base);
|
||||
|
||||
tup->rx_base = devm_platform_ioremap_resource_byname(pdev, "rx");
|
||||
if (IS_ERR(tup->rx_base))
|
||||
return PTR_ERR(tup->rx_base);
|
||||
|
||||
ret = tegra_utc_setup_port(dev, tup);
|
||||
if (ret)
|
||||
dev_err_probe(dev, ret, "failed to setup uart port\n");
|
||||
|
||||
platform_set_drvdata(pdev, tup);
|
||||
|
||||
return tegra_utc_register_port(tup);
|
||||
}
|
||||
|
||||
static void tegra_utc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_utc_port *tup = platform_get_drvdata(pdev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
|
||||
unregister_console(&tup->console);
|
||||
#endif
|
||||
uart_remove_one_port(&tegra_utc_driver, &tup->port);
|
||||
}
|
||||
|
||||
static const unsigned int tegra264_utc_soc = 128;
|
||||
|
||||
static const struct of_device_id tegra_utc_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra264-utc", .data = &tegra264_utc_soc },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_utc_of_match);
|
||||
|
||||
static struct platform_driver tegra_utc_platform_driver = {
|
||||
.probe = tegra_utc_probe,
|
||||
.remove = tegra_utc_remove,
|
||||
.driver = {
|
||||
.name = "tegra-utc",
|
||||
.of_match_table = tegra_utc_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init tegra_utc_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uart_register_driver(&tegra_utc_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&tegra_utc_platform_driver);
|
||||
if (ret)
|
||||
uart_unregister_driver(&tegra_utc_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(tegra_utc_init);
|
||||
|
||||
static void __exit tegra_utc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tegra_utc_platform_driver);
|
||||
uart_unregister_driver(&tegra_utc_driver);
|
||||
}
|
||||
module_exit(tegra_utc_exit);
|
||||
|
||||
MODULE_AUTHOR("Kartik Rajput <kkartik@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra UART Trace Controller");
|
||||
MODULE_LICENSE("GPL");
|
@ -12,12 +12,14 @@
|
||||
#include <linux/tty.h>
|
||||
#include "tty.h"
|
||||
|
||||
#define TTY_AUDIT_BUF_SIZE 4096
|
||||
|
||||
struct tty_audit_buf {
|
||||
struct mutex mutex; /* Protects all data below */
|
||||
dev_t dev; /* The TTY which the data is from */
|
||||
bool icanon;
|
||||
size_t valid;
|
||||
u8 *data; /* Allocated size N_TTY_BUF_SIZE */
|
||||
u8 *data; /* Allocated size TTY_AUDIT_BUF_SIZE */
|
||||
};
|
||||
|
||||
static struct tty_audit_buf *tty_audit_buf_ref(void)
|
||||
@ -37,7 +39,7 @@ static struct tty_audit_buf *tty_audit_buf_alloc(void)
|
||||
if (!buf)
|
||||
goto err;
|
||||
|
||||
buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
|
||||
buf->data = kmalloc(TTY_AUDIT_BUF_SIZE, GFP_KERNEL);
|
||||
if (!buf->data)
|
||||
goto err_buf;
|
||||
|
||||
@ -235,14 +237,14 @@ void tty_audit_add_data(const struct tty_struct *tty, const void *data,
|
||||
do {
|
||||
size_t run;
|
||||
|
||||
run = N_TTY_BUF_SIZE - buf->valid;
|
||||
run = TTY_AUDIT_BUF_SIZE - buf->valid;
|
||||
if (run > size)
|
||||
run = size;
|
||||
memcpy(buf->data + buf->valid, data, run);
|
||||
buf->valid += run;
|
||||
data += run;
|
||||
size -= run;
|
||||
if (buf->valid == N_TTY_BUF_SIZE)
|
||||
if (buf->valid == TTY_AUDIT_BUF_SIZE)
|
||||
tty_audit_buf_push(buf);
|
||||
} while (size != 0);
|
||||
mutex_unlock(&buf->mutex);
|
||||
|
@ -3329,10 +3329,12 @@ EXPORT_SYMBOL(tty_unregister_device);
|
||||
* __tty_alloc_driver - allocate tty driver
|
||||
* @lines: count of lines this driver can handle at most
|
||||
* @owner: module which is responsible for this driver
|
||||
* @flags: some of %TTY_DRIVER_ flags, will be set in driver->flags
|
||||
* @flags: some of enum tty_driver_flag, will be set in driver->flags
|
||||
*
|
||||
* This should not be called directly, some of the provided macros should be
|
||||
* used instead. Use IS_ERR() and friends on @retval.
|
||||
* This should not be called directly, tty_alloc_driver() should be used
|
||||
* instead.
|
||||
*
|
||||
* Returns: struct tty_driver or a PTR-encoded error (use IS_ERR() and friends).
|
||||
*/
|
||||
struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
|
||||
unsigned long flags)
|
||||
|
@ -366,23 +366,6 @@ int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout)
|
||||
return __ldsem_down_write_nested(sem, 0, timeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* trylock for writing -- returns 1 if successful, 0 if contention
|
||||
*/
|
||||
int ldsem_down_write_trylock(struct ld_semaphore *sem)
|
||||
{
|
||||
long count = atomic_long_read(&sem->count);
|
||||
|
||||
while ((count & LDSEM_ACTIVE_MASK) == 0) {
|
||||
if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) {
|
||||
rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_);
|
||||
lock_acquired(&sem->dep_map, _RET_IP_);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* release a read lock
|
||||
*/
|
||||
|
@ -257,7 +257,6 @@ extern void kgdb_arch_late(void);
|
||||
* hardware breakpoints.
|
||||
* @correct_hw_break: Allow an architecture to specify how to correct the
|
||||
* hardware debug registers.
|
||||
* @enable_nmi: Manage NMI-triggered entry to KGDB
|
||||
*/
|
||||
struct kgdb_arch {
|
||||
unsigned char gdb_bpt_instr[BREAK_INSTR_SIZE];
|
||||
@ -270,8 +269,6 @@ struct kgdb_arch {
|
||||
void (*disable_hw_break)(struct pt_regs *regs);
|
||||
void (*remove_all_hw_break)(void);
|
||||
void (*correct_hw_break)(void);
|
||||
|
||||
void (*enable_nmi)(bool on);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -306,14 +303,6 @@ extern const struct kgdb_arch arch_kgdb_ops;
|
||||
|
||||
extern unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs);
|
||||
|
||||
#ifdef CONFIG_SERIAL_KGDB_NMI
|
||||
extern int kgdb_register_nmi_console(void);
|
||||
extern int kgdb_unregister_nmi_console(void);
|
||||
#else
|
||||
static inline int kgdb_register_nmi_console(void) { return 0; }
|
||||
static inline int kgdb_unregister_nmi_console(void) { return 0; }
|
||||
#endif
|
||||
|
||||
extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops);
|
||||
extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
|
||||
extern struct kgdb_io *dbg_io_ops;
|
||||
|
@ -84,7 +84,6 @@ enum serdev_parity {
|
||||
struct serdev_controller_ops {
|
||||
ssize_t (*write_buf)(struct serdev_controller *, const u8 *, size_t);
|
||||
void (*write_flush)(struct serdev_controller *);
|
||||
int (*write_room)(struct serdev_controller *);
|
||||
int (*open)(struct serdev_controller *);
|
||||
void (*close)(struct serdev_controller *);
|
||||
void (*set_flow_control)(struct serdev_controller *, bool);
|
||||
@ -212,7 +211,6 @@ int serdev_device_break_ctl(struct serdev_device *serdev, int break_state);
|
||||
void serdev_device_write_wakeup(struct serdev_device *);
|
||||
ssize_t serdev_device_write(struct serdev_device *, const u8 *, size_t, long);
|
||||
void serdev_device_write_flush(struct serdev_device *);
|
||||
int serdev_device_write_room(struct serdev_device *);
|
||||
|
||||
/*
|
||||
* serdev device driver functions
|
||||
@ -273,10 +271,6 @@ static inline ssize_t serdev_device_write(struct serdev_device *sdev,
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline void serdev_device_write_flush(struct serdev_device *sdev) {}
|
||||
static inline int serdev_device_write_room(struct serdev_device *sdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define serdev_device_driver_register(x)
|
||||
#define serdev_device_driver_unregister(x)
|
||||
|
@ -239,7 +239,6 @@ struct tty_struct {
|
||||
|
||||
struct list_head tty_files;
|
||||
|
||||
#define N_TTY_BUF_SIZE 4096
|
||||
struct work_struct SAK_work;
|
||||
} __randomize_layout;
|
||||
|
||||
@ -251,7 +250,7 @@ struct tty_file_private {
|
||||
};
|
||||
|
||||
/**
|
||||
* DOC: TTY Struct Flags
|
||||
* enum tty_struct_flags - TTY Struct Flags
|
||||
*
|
||||
* These bits are used in the :c:member:`tty_struct.flags` field.
|
||||
*
|
||||
@ -260,62 +259,64 @@ struct tty_file_private {
|
||||
* tty->write. Thus, you must use the inline functions set_bit() and
|
||||
* clear_bit() to make things atomic.
|
||||
*
|
||||
* TTY_THROTTLED
|
||||
* @TTY_THROTTLED:
|
||||
* Driver input is throttled. The ldisc should call
|
||||
* :c:member:`tty_driver.unthrottle()` in order to resume reception when
|
||||
* it is ready to process more data (at threshold min).
|
||||
*
|
||||
* TTY_IO_ERROR
|
||||
* @TTY_IO_ERROR:
|
||||
* If set, causes all subsequent userspace read/write calls on the tty to
|
||||
* fail, returning -%EIO. (May be no ldisc too.)
|
||||
*
|
||||
* TTY_OTHER_CLOSED
|
||||
* @TTY_OTHER_CLOSED:
|
||||
* Device is a pty and the other side has closed.
|
||||
*
|
||||
* TTY_EXCLUSIVE
|
||||
* @TTY_EXCLUSIVE:
|
||||
* Exclusive open mode (a single opener).
|
||||
*
|
||||
* TTY_DO_WRITE_WAKEUP
|
||||
* @TTY_DO_WRITE_WAKEUP:
|
||||
* If set, causes the driver to call the
|
||||
* :c:member:`tty_ldisc_ops.write_wakeup()` method in order to resume
|
||||
* transmission when it can accept more data to transmit.
|
||||
*
|
||||
* TTY_LDISC_OPEN
|
||||
* @TTY_LDISC_OPEN:
|
||||
* Indicates that a line discipline is open. For debugging purposes only.
|
||||
*
|
||||
* TTY_PTY_LOCK
|
||||
* @TTY_PTY_LOCK:
|
||||
* A flag private to pty code to implement %TIOCSPTLCK/%TIOCGPTLCK logic.
|
||||
*
|
||||
* TTY_NO_WRITE_SPLIT
|
||||
* @TTY_NO_WRITE_SPLIT:
|
||||
* Prevent driver from splitting up writes into smaller chunks (preserve
|
||||
* write boundaries to driver).
|
||||
*
|
||||
* TTY_HUPPED
|
||||
* @TTY_HUPPED:
|
||||
* The TTY was hung up. This is set post :c:member:`tty_driver.hangup()`.
|
||||
*
|
||||
* TTY_HUPPING
|
||||
* @TTY_HUPPING:
|
||||
* The TTY is in the process of hanging up to abort potential readers.
|
||||
*
|
||||
* TTY_LDISC_CHANGING
|
||||
* @TTY_LDISC_CHANGING:
|
||||
* Line discipline for this TTY is being changed. I/O should not block
|
||||
* when this is set. Use tty_io_nonblock() to check.
|
||||
*
|
||||
* TTY_LDISC_HALTED
|
||||
* @TTY_LDISC_HALTED:
|
||||
* Line discipline for this TTY was stopped. No work should be queued to
|
||||
* this ldisc.
|
||||
*/
|
||||
#define TTY_THROTTLED 0
|
||||
#define TTY_IO_ERROR 1
|
||||
#define TTY_OTHER_CLOSED 2
|
||||
#define TTY_EXCLUSIVE 3
|
||||
#define TTY_DO_WRITE_WAKEUP 5
|
||||
#define TTY_LDISC_OPEN 11
|
||||
#define TTY_PTY_LOCK 16
|
||||
#define TTY_NO_WRITE_SPLIT 17
|
||||
#define TTY_HUPPED 18
|
||||
#define TTY_HUPPING 19
|
||||
#define TTY_LDISC_CHANGING 20
|
||||
#define TTY_LDISC_HALTED 22
|
||||
enum tty_struct_flags {
|
||||
TTY_THROTTLED,
|
||||
TTY_IO_ERROR,
|
||||
TTY_OTHER_CLOSED,
|
||||
TTY_EXCLUSIVE,
|
||||
TTY_DO_WRITE_WAKEUP,
|
||||
TTY_LDISC_OPEN,
|
||||
TTY_PTY_LOCK,
|
||||
TTY_NO_WRITE_SPLIT,
|
||||
TTY_HUPPED,
|
||||
TTY_HUPPING,
|
||||
TTY_LDISC_CHANGING,
|
||||
TTY_LDISC_HALTED,
|
||||
};
|
||||
|
||||
static inline bool tty_io_nonblock(struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
|
@ -16,6 +16,92 @@ struct tty_driver;
|
||||
struct serial_icounter_struct;
|
||||
struct serial_struct;
|
||||
|
||||
/**
|
||||
* enum tty_driver_flag -- TTY Driver Flags
|
||||
*
|
||||
* These are flags passed to tty_alloc_driver().
|
||||
*
|
||||
* @TTY_DRIVER_INSTALLED:
|
||||
* Whether this driver was succesfully installed. This is a tty internal
|
||||
* flag. Do not touch.
|
||||
*
|
||||
* @TTY_DRIVER_RESET_TERMIOS:
|
||||
* Requests the tty layer to reset the termios setting when the last
|
||||
* process has closed the device. Used for PTYs, in particular.
|
||||
*
|
||||
* @TTY_DRIVER_REAL_RAW:
|
||||
* Indicates that the driver will guarantee not to set any special
|
||||
* character handling flags if this is set for the tty:
|
||||
*
|
||||
* ``(IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR || !INPCK)``
|
||||
*
|
||||
* That is, if there is no reason for the driver to
|
||||
* send notifications of parity and break characters up to the line
|
||||
* driver, it won't do so. This allows the line driver to optimize for
|
||||
* this case if this flag is set. (Note that there is also a promise, if
|
||||
* the above case is true, not to signal overruns, either.)
|
||||
*
|
||||
* @TTY_DRIVER_DYNAMIC_DEV:
|
||||
* The individual tty devices need to be registered with a call to
|
||||
* tty_register_device() when the device is found in the system and
|
||||
* unregistered with a call to tty_unregister_device() so the devices will
|
||||
* be show up properly in sysfs. If not set, all &tty_driver.num entries
|
||||
* will be created by the tty core in sysfs when tty_register_driver() is
|
||||
* called. This is to be used by drivers that have tty devices that can
|
||||
* appear and disappear while the main tty driver is registered with the
|
||||
* tty core.
|
||||
*
|
||||
* @TTY_DRIVER_DEVPTS_MEM:
|
||||
* Don't use the standard arrays (&tty_driver.ttys and
|
||||
* &tty_driver.termios), instead use dynamic memory keyed through the
|
||||
* devpts filesystem. This is only applicable to the PTY driver.
|
||||
*
|
||||
* @TTY_DRIVER_HARDWARE_BREAK:
|
||||
* Hardware handles break signals. Pass the requested timeout to the
|
||||
* &tty_operations.break_ctl instead of using a simple on/off interface.
|
||||
*
|
||||
* @TTY_DRIVER_DYNAMIC_ALLOC:
|
||||
* Do not allocate structures which are needed per line for this driver
|
||||
* (&tty_driver.ports) as it would waste memory. The driver will take
|
||||
* care. This is only applicable to the PTY driver.
|
||||
*
|
||||
* @TTY_DRIVER_UNNUMBERED_NODE:
|
||||
* Do not create numbered ``/dev`` nodes. For example, create
|
||||
* ``/dev/ttyprintk`` and not ``/dev/ttyprintk0``. Applicable only when a
|
||||
* driver for a single tty device is being allocated.
|
||||
*/
|
||||
enum tty_driver_flag {
|
||||
TTY_DRIVER_INSTALLED = BIT(0),
|
||||
TTY_DRIVER_RESET_TERMIOS = BIT(1),
|
||||
TTY_DRIVER_REAL_RAW = BIT(2),
|
||||
TTY_DRIVER_DYNAMIC_DEV = BIT(3),
|
||||
TTY_DRIVER_DEVPTS_MEM = BIT(4),
|
||||
TTY_DRIVER_HARDWARE_BREAK = BIT(5),
|
||||
TTY_DRIVER_DYNAMIC_ALLOC = BIT(6),
|
||||
TTY_DRIVER_UNNUMBERED_NODE = BIT(7),
|
||||
};
|
||||
|
||||
enum tty_driver_type {
|
||||
TTY_DRIVER_TYPE_SYSTEM,
|
||||
TTY_DRIVER_TYPE_CONSOLE,
|
||||
TTY_DRIVER_TYPE_SERIAL,
|
||||
TTY_DRIVER_TYPE_PTY,
|
||||
TTY_DRIVER_TYPE_SCC,
|
||||
TTY_DRIVER_TYPE_SYSCONS,
|
||||
};
|
||||
|
||||
enum tty_driver_subtype {
|
||||
SYSTEM_TYPE_TTY = 1,
|
||||
SYSTEM_TYPE_CONSOLE,
|
||||
SYSTEM_TYPE_SYSCONS,
|
||||
SYSTEM_TYPE_SYSPTMX,
|
||||
|
||||
PTY_TYPE_MASTER = 1,
|
||||
PTY_TYPE_SLAVE,
|
||||
|
||||
SERIAL_TYPE_NORMAL = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tty_operations -- interface between driver and tty
|
||||
*
|
||||
@ -414,8 +500,8 @@ struct tty_operations {
|
||||
* @major: major /dev device number (zero for autoassignment)
|
||||
* @minor_start: the first minor /dev device number
|
||||
* @num: number of devices allocated
|
||||
* @type: type of tty driver (%TTY_DRIVER_TYPE_)
|
||||
* @subtype: subtype of tty driver (%SYSTEM_TYPE_, %PTY_TYPE_, %SERIAL_TYPE_)
|
||||
* @type: type of tty driver (enum tty_driver_type)
|
||||
* @subtype: subtype of tty driver (enum tty_driver_subtype)
|
||||
* @init_termios: termios to set to each tty initially (e.g. %tty_std_termios)
|
||||
* @flags: tty driver flags (%TTY_DRIVER_)
|
||||
* @proc_entry: proc fs entry, used internally
|
||||
@ -447,8 +533,8 @@ struct tty_driver {
|
||||
int major;
|
||||
int minor_start;
|
||||
unsigned int num;
|
||||
short type;
|
||||
short subtype;
|
||||
enum tty_driver_type type;
|
||||
enum tty_driver_subtype subtype;
|
||||
struct ktermios init_termios;
|
||||
unsigned long flags;
|
||||
struct proc_dir_entry *proc_entry;
|
||||
@ -478,7 +564,13 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line);
|
||||
|
||||
void tty_driver_kref_put(struct tty_driver *driver);
|
||||
|
||||
/* Use TTY_DRIVER_* flags below */
|
||||
/**
|
||||
* tty_alloc_driver - allocate tty driver
|
||||
* @lines: count of lines this driver can handle at most
|
||||
* @flags: some of enum tty_driver_flag, will be set in driver->flags
|
||||
*
|
||||
* Returns: struct tty_driver or a PTR-encoded error (use IS_ERR() and friends).
|
||||
*/
|
||||
#define tty_alloc_driver(lines, flags) \
|
||||
__tty_alloc_driver(lines, THIS_MODULE, flags)
|
||||
|
||||
@ -494,84 +586,6 @@ static inline void tty_set_operations(struct tty_driver *driver,
|
||||
driver->ops = op;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: TTY Driver Flags
|
||||
*
|
||||
* TTY_DRIVER_RESET_TERMIOS
|
||||
* Requests the tty layer to reset the termios setting when the last
|
||||
* process has closed the device. Used for PTYs, in particular.
|
||||
*
|
||||
* TTY_DRIVER_REAL_RAW
|
||||
* Indicates that the driver will guarantee not to set any special
|
||||
* character handling flags if this is set for the tty:
|
||||
*
|
||||
* ``(IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR || !INPCK)``
|
||||
*
|
||||
* That is, if there is no reason for the driver to
|
||||
* send notifications of parity and break characters up to the line
|
||||
* driver, it won't do so. This allows the line driver to optimize for
|
||||
* this case if this flag is set. (Note that there is also a promise, if
|
||||
* the above case is true, not to signal overruns, either.)
|
||||
*
|
||||
* TTY_DRIVER_DYNAMIC_DEV
|
||||
* The individual tty devices need to be registered with a call to
|
||||
* tty_register_device() when the device is found in the system and
|
||||
* unregistered with a call to tty_unregister_device() so the devices will
|
||||
* be show up properly in sysfs. If not set, all &tty_driver.num entries
|
||||
* will be created by the tty core in sysfs when tty_register_driver() is
|
||||
* called. This is to be used by drivers that have tty devices that can
|
||||
* appear and disappear while the main tty driver is registered with the
|
||||
* tty core.
|
||||
*
|
||||
* TTY_DRIVER_DEVPTS_MEM
|
||||
* Don't use the standard arrays (&tty_driver.ttys and
|
||||
* &tty_driver.termios), instead use dynamic memory keyed through the
|
||||
* devpts filesystem. This is only applicable to the PTY driver.
|
||||
*
|
||||
* TTY_DRIVER_HARDWARE_BREAK
|
||||
* Hardware handles break signals. Pass the requested timeout to the
|
||||
* &tty_operations.break_ctl instead of using a simple on/off interface.
|
||||
*
|
||||
* TTY_DRIVER_DYNAMIC_ALLOC
|
||||
* Do not allocate structures which are needed per line for this driver
|
||||
* (&tty_driver.ports) as it would waste memory. The driver will take
|
||||
* care. This is only applicable to the PTY driver.
|
||||
*
|
||||
* TTY_DRIVER_UNNUMBERED_NODE
|
||||
* Do not create numbered ``/dev`` nodes. For example, create
|
||||
* ``/dev/ttyprintk`` and not ``/dev/ttyprintk0``. Applicable only when a
|
||||
* driver for a single tty device is being allocated.
|
||||
*/
|
||||
#define TTY_DRIVER_INSTALLED 0x0001
|
||||
#define TTY_DRIVER_RESET_TERMIOS 0x0002
|
||||
#define TTY_DRIVER_REAL_RAW 0x0004
|
||||
#define TTY_DRIVER_DYNAMIC_DEV 0x0008
|
||||
#define TTY_DRIVER_DEVPTS_MEM 0x0010
|
||||
#define TTY_DRIVER_HARDWARE_BREAK 0x0020
|
||||
#define TTY_DRIVER_DYNAMIC_ALLOC 0x0040
|
||||
#define TTY_DRIVER_UNNUMBERED_NODE 0x0080
|
||||
|
||||
/* tty driver types */
|
||||
#define TTY_DRIVER_TYPE_SYSTEM 0x0001
|
||||
#define TTY_DRIVER_TYPE_CONSOLE 0x0002
|
||||
#define TTY_DRIVER_TYPE_SERIAL 0x0003
|
||||
#define TTY_DRIVER_TYPE_PTY 0x0004
|
||||
#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */
|
||||
#define TTY_DRIVER_TYPE_SYSCONS 0x0006
|
||||
|
||||
/* system subtypes (magic, used by tty_io.c) */
|
||||
#define SYSTEM_TYPE_TTY 0x0001
|
||||
#define SYSTEM_TYPE_CONSOLE 0x0002
|
||||
#define SYSTEM_TYPE_SYSCONS 0x0003
|
||||
#define SYSTEM_TYPE_SYSPTMX 0x0004
|
||||
|
||||
/* pty subtypes (magic, used by tty_io.c) */
|
||||
#define PTY_TYPE_MASTER 0x0001
|
||||
#define PTY_TYPE_SLAVE 0x0002
|
||||
|
||||
/* serial subtype definitions */
|
||||
#define SERIAL_TYPE_NORMAL 1
|
||||
|
||||
int tty_register_driver(struct tty_driver *driver);
|
||||
void tty_unregister_driver(struct tty_driver *driver);
|
||||
struct device *tty_register_device(struct tty_driver *driver, unsigned index,
|
||||
|
@ -39,7 +39,6 @@ do { \
|
||||
int ldsem_down_read(struct ld_semaphore *sem, long timeout);
|
||||
int ldsem_down_read_trylock(struct ld_semaphore *sem);
|
||||
int ldsem_down_write(struct ld_semaphore *sem, long timeout);
|
||||
int ldsem_down_write_trylock(struct ld_semaphore *sem);
|
||||
void ldsem_up_read(struct ld_semaphore *sem);
|
||||
void ldsem_up_write(struct ld_semaphore *sem);
|
||||
|
||||
|
@ -837,10 +837,6 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
|
||||
{
|
||||
struct kgdb_state kgdb_var;
|
||||
struct kgdb_state *ks = &kgdb_var;
|
||||
int ret = 0;
|
||||
|
||||
if (arch_kgdb_ops.enable_nmi)
|
||||
arch_kgdb_ops.enable_nmi(0);
|
||||
/*
|
||||
* Avoid entering the debugger if we were triggered due to an oops
|
||||
* but panic_timeout indicates the system should automatically
|
||||
@ -858,15 +854,11 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
|
||||
ks->linux_regs = regs;
|
||||
|
||||
if (kgdb_reenter_check(ks))
|
||||
goto out; /* Ouch, double exception ! */
|
||||
return 0; /* Ouch, double exception ! */
|
||||
if (kgdb_info[ks->cpu].enter_kgdb != 0)
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
ret = kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER);
|
||||
out:
|
||||
if (arch_kgdb_ops.enable_nmi)
|
||||
arch_kgdb_ops.enable_nmi(1);
|
||||
return ret;
|
||||
return kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER);
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_handle_exception);
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <linux/smp.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/init.h>
|
||||
@ -2087,32 +2086,6 @@ static int kdb_dmesg(int argc, const char **argv)
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PRINTK */
|
||||
|
||||
/* Make sure we balance enable/disable calls, must disable first. */
|
||||
static atomic_t kdb_nmi_disabled;
|
||||
|
||||
static int kdb_disable_nmi(int argc, const char *argv[])
|
||||
{
|
||||
if (atomic_read(&kdb_nmi_disabled))
|
||||
return 0;
|
||||
atomic_set(&kdb_nmi_disabled, 1);
|
||||
arch_kgdb_ops.enable_nmi(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kdb_param_enable_nmi(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
if (!atomic_add_unless(&kdb_nmi_disabled, -1, 0))
|
||||
return -EINVAL;
|
||||
arch_kgdb_ops.enable_nmi(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops kdb_param_ops_enable_nmi = {
|
||||
.set = kdb_param_enable_nmi,
|
||||
};
|
||||
module_param_cb(enable_nmi, &kdb_param_ops_enable_nmi, NULL, 0600);
|
||||
|
||||
/*
|
||||
* kdb_cpu - This function implements the 'cpu' command.
|
||||
* cpu [<cpunum>]
|
||||
@ -2804,20 +2777,10 @@ static kdbtab_t maintab[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static kdbtab_t nmicmd = {
|
||||
.name = "disable_nmi",
|
||||
.func = kdb_disable_nmi,
|
||||
.usage = "",
|
||||
.help = "Disable NMI entry to KDB",
|
||||
.flags = KDB_ENABLE_ALWAYS_SAFE,
|
||||
};
|
||||
|
||||
/* Initialize the kdb command table. */
|
||||
static void __init kdb_inittab(void)
|
||||
{
|
||||
kdb_register_table(maintab, ARRAY_SIZE(maintab));
|
||||
if (arch_kgdb_ops.enable_nmi)
|
||||
kdb_register_table(&nmicmd, 1);
|
||||
}
|
||||
|
||||
/* Execute any commands defined in kdb_cmds. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user