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:
Linus Torvalds 2025-04-02 18:17:33 -07:00
commit ddd0172f18
58 changed files with 2238 additions and 1462 deletions

View File

@ -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

View File

@ -30,6 +30,7 @@ properties:
- items:
- enum:
- fsl,imx93-lpuart
- fsl,imx94-lpuart
- fsl,imx95-lpuart
- const: fsl,imx8ulp-lpuart
- const: fsl,imx7ulp-lpuart

View File

@ -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>;
};

View File

@ -92,6 +92,9 @@ properties:
3000ms.
default: 3000
power-domains:
maxItems: 1
resets:
maxItems: 1

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
----

View File

@ -72,7 +72,7 @@ TTY Struct Flags
================
.. kernel-doc:: include/linux/tty.h
:doc: TTY Struct Flags
:identifiers: tty_struct_flags
TTY Struct Reference
====================

View File

@ -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

View File

@ -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
*/

View File

@ -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();

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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;
}
/**

View File

@ -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;

View File

@ -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,

View File

@ -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)

View File

@ -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);

View File

@ -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 {

View 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");

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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),
},
};

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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,
},
};

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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)
{

View File

@ -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)
{
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View 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");

View File

@ -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);

View File

@ -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)

View File

@ -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
*/

View File

@ -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;

View File

@ -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)

View File

@ -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)
{

View 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,

View File

@ -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);

View File

@ -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);

View File

@ -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. */