Merge tag 'tty-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial updates from Greg KH:
 "Here is the big set of tty/serial driver changes for 6.19-rc1. Nothing
  major at all, just small constant churn to make the tty layer
  "cleaner" as well as serial driver updates and even a new test added!
  Included in here are:

   - More tty/serial cleanups from Jiri

   - tty tiocsti test added to hopefully ensure we don't regress in this
     area again

   - sc16is7xx driver updates

   - imx serial driver updates

   - 8250 driver updates

   - new hardware device ids added

   - other minor serial/tty driver cleanups and tweaks

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'tty-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (60 commits)
  serial: sh-sci: Fix deadlock during RSCI FIFO overrun error
  dt-bindings: serial: rsci: Drop "uart-has-rtscts: false"
  LoongArch: dts: Add uart new compatible string
  serial: 8250: Add Loongson uart driver support
  dt-bindings: serial: 8250: Add Loongson uart compatible
  serial: 8250: add driver for KEBA UART
  serial: Keep rs485 settings for devices without firmware node
  serial: qcom-geni: Enable Serial on SA8255p Qualcomm platforms
  serial: qcom-geni: Enable PM runtime for serial driver
  serial: sprd: Return -EPROBE_DEFER when uart clock is not ready
  tty: serial: samsung: Declare earlycon for Exynos850
  serial: icom: Convert PCIBIOS_* return codes to errnos
  serial: 8250-of: Fix style issues in 8250_of.c
  serial: add support of CPCI cards
  serial: mux: Fix kernel doc for mux_poll()
  tty: replace use of system_unbound_wq with system_dfl_wq
  serial: 8250_platform: simplify IRQF_SHARED handling
  serial: 8250: make share_irqs local to 8250_platform
  serial: 8250: move skip_txen_test to core
  serial: drop SERIAL_8250_DEPRECATED_OPTIONS
  ...
This commit is contained in:
Linus Torvalds
2025-12-06 18:38:19 -08:00
71 changed files with 2338 additions and 1161 deletions

View File

@@ -125,6 +125,8 @@ properties:
- nxp,lpc1850-uart
- opencores,uart16550-rtlsvn105
- ti,da830-uart
- loongson,ls2k0500-uart
- loongson,ls2k1500-uart
- const: ns16550a
- items:
- enum:
@@ -169,6 +171,18 @@ properties:
- nvidia,tegra194-uart
- nvidia,tegra234-uart
- const: nvidia,tegra20-uart
- items:
- enum:
- loongson,ls2k1000-uart
- const: loongson,ls2k0500-uart
- const: ns16550a
- items:
- enum:
- loongson,ls3a5000-uart
- loongson,ls3a6000-uart
- loongson,ls2k2000-uart
- const: loongson,ls2k1500-uart
- const: ns16550a
reg:
maxItems: 1

View File

@@ -54,8 +54,6 @@ properties:
power-domains:
maxItems: 1
uart-has-rtscts: false
required:
- compatible
- reg

View File

@@ -48,7 +48,9 @@ properties:
- const: samsung,exynos850-uart
- items:
- enum:
- axis,artpec9-uart
- samsung,exynos7870-uart
- samsung,exynos8890-uart
- const: samsung,exynos8895-uart
reg:

View File

@@ -65,6 +65,7 @@ properties:
- rockchip,rk3328-uart
- rockchip,rk3368-uart
- rockchip,rk3399-uart
- rockchip,rk3506-uart
- rockchip,rk3528-uart
- rockchip,rk3562-uart
- rockchip,rk3568-uart

View File

@@ -117,7 +117,6 @@ CONFIG_KEYBOARD_GPIO_POLLED=y
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=6
CONFIG_SERIAL_8250_RUNTIME_UARTS=6

View File

@@ -138,7 +138,6 @@ CONFIG_SERIO_RAW=y
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=6
CONFIG_SERIAL_8250_RUNTIME_UARTS=6

View File

@@ -35,7 +35,6 @@ CONFIG_NETDEVICES=y
CONFIG_HIX5HD2_GMAC=y
CONFIG_HIP04_ETH=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=2
CONFIG_SERIAL_8250_RUNTIME_UARTS=2

View File

@@ -90,7 +90,6 @@ CONFIG_KEYBOARD_GPIO_POLLED=y
# CONFIG_UNIX98_PTYS is not set
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SERIAL_NONSTANDARD=y

View File

@@ -75,7 +75,6 @@ CONFIG_INPUT_DA9063_ONKEY=y
CONFIG_INPUT_ADXL34X=y
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
# CONFIG_SERIAL_8250_16550A_VARIANTS is not set
CONFIG_SERIAL_8250_CONSOLE=y
# CONFIG_SERIAL_8250_PCI is not set

View File

@@ -380,7 +380,7 @@
};
uart0: serial@1ff40800 {
compatible = "ns16550a";
compatible = "loongson,ls2k0500-uart", "ns16550a";
reg = <0x0 0x1ff40800 0x0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&eiointc>;

View File

@@ -297,7 +297,7 @@
};
uart0: serial@1fe20000 {
compatible = "ns16550a";
compatible = "loongson,ls2k1000-uart", "loongson,ls2k0500-uart", "ns16550a";
reg = <0x0 0x1fe20000 0x0 0x10>;
clock-frequency = <125000000>;
interrupt-parent = <&liointc0>;

View File

@@ -250,7 +250,7 @@
};
uart0: serial@1fe001e0 {
compatible = "ns16550a";
compatible = "loongson,ls2k2000-uart", "loongson,ls2k1500-uart", "ns16550a";
reg = <0x0 0x1fe001e0 0x0 0x10>;
clock-frequency = <100000000>;
interrupt-parent = <&liointc>;

View File

@@ -51,7 +51,6 @@ CONFIG_B43LEGACY=y
CONFIG_BRCMSMAC=y
CONFIG_ISDN=y
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
# CONFIG_SERIAL_8250_PCI is not set
CONFIG_SERIAL_8250_NR_UARTS=2

View File

@@ -119,7 +119,6 @@ CONFIG_INPUT_UINPUT=y
CONFIG_VT=y
CONFIG_VT_HW_CONSOLE_BINDING=y
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
# CONFIG_HW_RANDOM is not set

View File

@@ -52,7 +52,6 @@ CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_PWM_VIBRA=y
# CONFIG_SERIO is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_INGENIC=y
CONFIG_HW_RANDOM=y

View File

@@ -51,7 +51,6 @@ CONFIG_MARVELL_PHY=y
# CONFIG_SERIO_SERPORT is not set
# CONFIG_VT is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SERIAL_ALTERA_JTAGUART=y

View File

@@ -119,7 +119,6 @@ CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=m
CONFIG_LEGACY_PTY_COUNT=64
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=8
CONFIG_SERIAL_8250_EXTENDED=y

View File

@@ -158,7 +158,6 @@ CONFIG_SERIO_SERPORT=m
CONFIG_SERIO_RAW=m
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=8
CONFIG_SERIAL_8250_RUNTIME_UARTS=8

View File

@@ -85,7 +85,6 @@ CONFIG_IBM_EMAC=y
# CONFIG_SERIO is not set
# CONFIG_VT is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_SHARE_IRQ=y

View File

@@ -62,7 +62,6 @@ CONFIG_LITEX_LITEETH=y
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SERIAL_NONSTANDARD=y

View File

@@ -48,7 +48,6 @@ CONFIG_VIRTIO_BLK=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_LDISC_AUTOLOAD is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=1
CONFIG_SERIAL_8250_RUNTIME_UARTS=1

View File

@@ -81,7 +81,6 @@ CONFIG_MARVELL_PHY=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_HW_RANDOM=y

View File

@@ -79,7 +79,6 @@ CONFIG_MARVELL_PHY=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_HW_RANDOM=y

View File

@@ -81,7 +81,6 @@ CONFIG_MARVELL_PHY=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_HW_RANDOM=y

View File

@@ -83,7 +83,6 @@ CONFIG_MARVELL_PHY=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_HW_RANDOM=y

View File

@@ -72,7 +72,6 @@ CONFIG_MARVELL_PHY=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_SERIAL_8250=y
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
# CONFIG_HWMON is not set

View File

@@ -487,25 +487,20 @@ static void moxa_wait_finish(void __iomem *ofsAddr)
static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
{
unsigned long flags;
spin_lock_irqsave(&moxafunc_lock, flags);
guard(spinlock_irqsave)(&moxafunc_lock);
writew(arg, ofsAddr + FuncArg);
writew(cmd, ofsAddr + FuncCode);
moxa_wait_finish(ofsAddr);
spin_unlock_irqrestore(&moxafunc_lock, flags);
}
static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
{
unsigned long flags;
u16 ret;
spin_lock_irqsave(&moxafunc_lock, flags);
guard(spinlock_irqsave)(&moxafunc_lock);
writew(arg, ofsAddr + FuncArg);
writew(cmd, ofsAddr + FuncCode);
moxa_wait_finish(ofsAddr);
ret = readw(ofsAddr + FuncArg);
spin_unlock_irqrestore(&moxafunc_lock, flags);
return ret;
return readw(ofsAddr + FuncArg);
}
static void moxa_low_water_check(void __iomem *ofsAddr)
@@ -1002,11 +997,11 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev)
if (ret)
goto err_free;
spin_lock_bh(&moxa_lock);
brd->ready = 1;
if (!timer_pending(&moxaTimer))
mod_timer(&moxaTimer, jiffies + HZ / 50);
spin_unlock_bh(&moxa_lock);
scoped_guard(spinlock_bh, &moxa_lock) {
brd->ready = 1;
if (!timer_pending(&moxaTimer))
mod_timer(&moxaTimer, jiffies + HZ / 50);
}
first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD;
for (i = 0; i < brd->numPorts; i++)
@@ -1026,29 +1021,29 @@ static void moxa_board_deinit(struct moxa_board_conf *brd)
{
unsigned int a, opened, first_idx;
mutex_lock(&moxa_openlock);
spin_lock_bh(&moxa_lock);
brd->ready = 0;
spin_unlock_bh(&moxa_lock);
scoped_guard(mutex, &moxa_openlock) {
scoped_guard(spinlock_bh, &moxa_lock)
brd->ready = 0;
/* pci hot-un-plug support */
for (a = 0; a < brd->numPorts; a++)
if (tty_port_initialized(&brd->ports[a].port))
tty_port_tty_hangup(&brd->ports[a].port, false);
for (a = 0; a < MAX_PORTS_PER_BOARD; a++)
tty_port_destroy(&brd->ports[a].port);
while (1) {
opened = 0;
/* pci hot-un-plug support */
for (a = 0; a < brd->numPorts; a++)
if (tty_port_initialized(&brd->ports[a].port))
opened++;
mutex_unlock(&moxa_openlock);
if (!opened)
break;
msleep(50);
mutex_lock(&moxa_openlock);
tty_port_tty_hangup(&brd->ports[a].port, false);
for (a = 0; a < MAX_PORTS_PER_BOARD; a++)
tty_port_destroy(&brd->ports[a].port);
while (1) {
opened = 0;
for (a = 0; a < brd->numPorts; a++)
if (tty_port_initialized(&brd->ports[a].port))
opened++;
if (!opened)
break;
mutex_unlock(&moxa_openlock);
msleep(50);
mutex_lock(&moxa_openlock);
}
}
first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD;
@@ -1206,12 +1201,9 @@ static void moxa_shutdown(struct tty_port *port)
static bool moxa_carrier_raised(struct tty_port *port)
{
struct moxa_port *ch = container_of(port, struct moxa_port, port);
int dcd;
spin_lock_irq(&port->lock);
dcd = ch->DCDState;
spin_unlock_irq(&port->lock);
return dcd;
guard(spinlock_irq)(&port->lock);
return ch->DCDState;
}
static void moxa_dtr_rts(struct tty_port *port, bool active)
@@ -1225,37 +1217,31 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
{
struct moxa_board_conf *brd;
struct moxa_port *ch;
int port;
int port = tty->index;
port = tty->index;
if (mutex_lock_interruptible(&moxa_openlock))
return -ERESTARTSYS;
brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
if (!brd->ready) {
mutex_unlock(&moxa_openlock);
return -ENODEV;
}
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &moxa_openlock) {
brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
if (!brd->ready)
return -ENODEV;
if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) {
mutex_unlock(&moxa_openlock);
return -ENODEV;
}
if (port % MAX_PORTS_PER_BOARD >= brd->numPorts)
return -ENODEV;
ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
ch->port.count++;
tty->driver_data = ch;
tty_port_tty_set(&ch->port, tty);
mutex_lock(&ch->port.mutex);
if (!tty_port_initialized(&ch->port)) {
ch->statusflags = 0;
moxa_set_tty_param(tty, &tty->termios);
MoxaPortLineCtrl(ch, true, true);
MoxaPortEnable(ch);
MoxaSetFifo(ch, ch->type == PORT_16550A);
tty_port_set_initialized(&ch->port, true);
ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
ch->port.count++;
tty->driver_data = ch;
tty_port_tty_set(&ch->port, tty);
guard(mutex)(&ch->port.mutex);
if (!tty_port_initialized(&ch->port)) {
ch->statusflags = 0;
moxa_set_tty_param(tty, &tty->termios);
MoxaPortLineCtrl(ch, true, true);
MoxaPortEnable(ch);
MoxaSetFifo(ch, ch->type == PORT_16550A);
tty_port_set_initialized(&ch->port, true);
}
}
mutex_unlock(&ch->port.mutex);
mutex_unlock(&moxa_openlock);
return tty_port_block_til_ready(&ch->port, tty, filp);
}
@@ -1270,15 +1256,13 @@ static void moxa_close(struct tty_struct *tty, struct file *filp)
static ssize_t moxa_write(struct tty_struct *tty, const u8 *buf, size_t count)
{
struct moxa_port *ch = tty->driver_data;
unsigned long flags;
int len;
if (ch == NULL)
return 0;
spin_lock_irqsave(&moxa_lock, flags);
len = MoxaPortWriteData(tty, buf, count);
spin_unlock_irqrestore(&moxa_lock, flags);
scoped_guard(spinlock_irqsave, &moxa_lock)
len = MoxaPortWriteData(tty, buf, count);
set_bit(LOWWAIT, &ch->statusflags);
return len;
@@ -1349,12 +1333,10 @@ static int moxa_tiocmset(struct tty_struct *tty,
bool dtr_active, rts_active;
struct moxa_port *ch;
mutex_lock(&moxa_openlock);
guard(mutex)(&moxa_openlock);
ch = tty->driver_data;
if (!ch) {
mutex_unlock(&moxa_openlock);
if (!ch)
return -EINVAL;
}
MoxaPortGetLineOut(ch, &dtr_active, &rts_active);
if (set & TIOCM_RTS)
@@ -1366,7 +1348,7 @@ static int moxa_tiocmset(struct tty_struct *tty,
if (clear & TIOCM_DTR)
dtr_active = false;
MoxaPortLineCtrl(ch, dtr_active, rts_active);
mutex_unlock(&moxa_openlock);
return 0;
}
@@ -1415,18 +1397,17 @@ static void moxa_hangup(struct tty_struct *tty)
static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
{
unsigned long flags;
dcd = !!dcd;
spin_lock_irqsave(&p->port.lock, flags);
if (dcd != p->DCDState) {
p->DCDState = dcd;
spin_unlock_irqrestore(&p->port.lock, flags);
if (!dcd)
tty_port_tty_hangup(&p->port, true);
scoped_guard(spinlock_irqsave, &p->port.lock) {
if (dcd == p->DCDState)
return;
p->DCDState = dcd;
}
else
spin_unlock_irqrestore(&p->port.lock, flags);
if (!dcd)
tty_port_tty_hangup(&p->port, true);
}
static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
@@ -1494,7 +1475,7 @@ static void moxa_poll(struct timer_list *unused)
u16 __iomem *ip;
unsigned int card, port, served = 0;
spin_lock(&moxa_lock);
guard(spinlock)(&moxa_lock);
for (card = 0; card < MAX_BOARDS; card++) {
brd = &moxa_boards[card];
if (!brd->ready)
@@ -1525,7 +1506,6 @@ static void moxa_poll(struct timer_list *unused)
if (served)
mod_timer(&moxaTimer, jiffies + HZ / 50);
spin_unlock(&moxa_lock);
}
/******************************************************************************/
@@ -1861,13 +1841,11 @@ static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio,
baud = MoxaPortSetBaud(port, baud);
if (termio->c_iflag & (IXON | IXOFF | IXANY)) {
spin_lock_irq(&moxafunc_lock);
guard(spinlock_irq)(&moxafunc_lock);
writeb(termio->c_cc[VSTART], ofsAddr + FuncArg);
writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
writeb(FC_SetXonXoff, ofsAddr + FuncCode);
moxa_wait_finish(ofsAddr);
spin_unlock_irq(&moxafunc_lock);
}
return baud;
}
@@ -2098,13 +2076,13 @@ static int moxa_get_serial_info(struct tty_struct *tty,
if (!info)
return -ENODEV;
mutex_lock(&info->port.mutex);
guard(mutex)(&info->port.mutex);
ss->type = info->type;
ss->line = info->port.tty->index;
ss->flags = info->port.flags;
ss->baud_base = 921600;
ss->close_delay = jiffies_to_msecs(info->port.close_delay) / 10;
mutex_unlock(&info->port.mutex);
return 0;
}
@@ -2120,13 +2098,12 @@ static int moxa_set_serial_info(struct tty_struct *tty,
close_delay = msecs_to_jiffies(ss->close_delay * 10);
mutex_lock(&info->port.mutex);
guard(mutex)(&info->port.mutex);
if (!capable(CAP_SYS_ADMIN)) {
if (close_delay != info->port.close_delay ||
ss->type != info->type ||
((ss->flags & ~ASYNC_USR_MASK) !=
(info->port.flags & ~ASYNC_USR_MASK))) {
mutex_unlock(&info->port.mutex);
return -EPERM;
}
} else {
@@ -2136,7 +2113,7 @@ static int moxa_set_serial_info(struct tty_struct *tty,
info->type = ss->type;
}
mutex_unlock(&info->port.mutex);
return 0;
}

View File

@@ -4165,7 +4165,7 @@ static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk)
/**
* gsm_modem_send_initial_msc - Send initial modem status message
*
* @dlci channel
* @dlci: channel
*
* Send an initial MSC message after DLCI open to set the initial
* modem status lines. This is only done for basic mode.

View File

@@ -263,21 +263,18 @@ static int n_hdlc_tty_open(struct tty_struct *tty)
*/
static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
{
unsigned long flags;
struct n_hdlc_buf *tbuf;
ssize_t actual;
check_again:
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
if (n_hdlc->tbusy) {
n_hdlc->woke_up = true;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
return;
scoped_guard(spinlock_irqsave, &n_hdlc->tx_buf_list.spinlock) {
if (n_hdlc->tbusy) {
n_hdlc->woke_up = true;
return;
}
n_hdlc->tbusy = true;
n_hdlc->woke_up = false;
}
n_hdlc->tbusy = true;
n_hdlc->woke_up = false;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
while (tbuf) {
@@ -324,9 +321,8 @@ check_again:
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
/* Clear the re-entry flag */
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
n_hdlc->tbusy = false;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
scoped_guard(spinlock_irqsave, &n_hdlc->tx_buf_list.spinlock)
n_hdlc->tbusy = false;
if (n_hdlc->woke_up)
goto check_again;
@@ -584,9 +580,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg)
{
struct n_hdlc *n_hdlc = tty->disc_data;
int error = 0;
int count;
unsigned long flags;
struct n_hdlc_buf *buf = NULL;
pr_debug("%s() called %d\n", __func__, cmd);
@@ -595,29 +589,27 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
case FIONREAD:
/* report count of read data available */
/* in next available frame (if any) */
spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock, flags);
buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
struct n_hdlc_buf, list_item);
if (buf)
count = buf->count;
else
count = 0;
spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock, flags);
error = put_user(count, (int __user *)arg);
break;
scoped_guard(spinlock_irqsave, &n_hdlc->rx_buf_list.spinlock) {
buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
struct n_hdlc_buf, list_item);
if (buf)
count = buf->count;
else
count = 0;
}
return put_user(count, (int __user *)arg);
case TIOCOUTQ:
/* get the pending tx byte count in the driver */
count = tty_chars_in_buffer(tty);
/* add size of next output frame in queue */
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
struct n_hdlc_buf, list_item);
if (buf)
count += buf->count;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
error = put_user(count, (int __user *)arg);
break;
scoped_guard(spinlock_irqsave, &n_hdlc->tx_buf_list.spinlock) {
buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
struct n_hdlc_buf, list_item);
if (buf)
count += buf->count;
}
return put_user(count, (int __user *)arg);
case TCFLSH:
switch (arg) {
@@ -628,11 +620,8 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
fallthrough; /* to default */
default:
error = n_tty_ioctl_helper(tty, cmd, arg);
break;
return n_tty_ioctl_helper(tty, cmd, arg);
}
return error;
} /* end of n_hdlc_tty_ioctl() */
/**
@@ -726,14 +715,10 @@ static struct n_hdlc *n_hdlc_alloc(void)
static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
struct n_hdlc_buf *buf)
{
unsigned long flags;
spin_lock_irqsave(&buf_list->spinlock, flags);
guard(spinlock_irqsave)(&buf_list->spinlock);
list_add(&buf->list_item, &buf_list->list);
buf_list->count++;
spin_unlock_irqrestore(&buf_list->spinlock, flags);
}
/**
@@ -744,14 +729,10 @@ static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
struct n_hdlc_buf *buf)
{
unsigned long flags;
spin_lock_irqsave(&buf_list->spinlock, flags);
guard(spinlock_irqsave)(&buf_list->spinlock);
list_add_tail(&buf->list_item, &buf_list->list);
buf_list->count++;
spin_unlock_irqrestore(&buf_list->spinlock, flags);
} /* end of n_hdlc_buf_put() */
/**
@@ -764,10 +745,9 @@ static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
*/
static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
{
unsigned long flags;
struct n_hdlc_buf *buf;
spin_lock_irqsave(&buf_list->spinlock, flags);
guard(spinlock_irqsave)(&buf_list->spinlock);
buf = list_first_entry_or_null(&buf_list->list,
struct n_hdlc_buf, list_item);
@@ -776,7 +756,6 @@ static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
buf_list->count--;
}
spin_unlock_irqrestore(&buf_list->spinlock, flags);
return buf;
} /* end of n_hdlc_buf_get() */

View File

@@ -324,12 +324,9 @@ static void reset_buffer_flags(struct n_tty_data *ldata)
static void n_tty_packet_mode_flush(struct tty_struct *tty)
{
unsigned long flags;
if (tty->link->ctrl.packet) {
spin_lock_irqsave(&tty->ctrl.lock, flags);
tty->ctrl.pktstatus |= TIOCPKT_FLUSHREAD;
spin_unlock_irqrestore(&tty->ctrl.lock, flags);
scoped_guard(spinlock_irqsave, &tty->ctrl.lock)
tty->ctrl.pktstatus |= TIOCPKT_FLUSHREAD;
wake_up_interruptible(&tty->link->read_wait);
}
}
@@ -349,13 +346,12 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty)
*/
static void n_tty_flush_buffer(struct tty_struct *tty)
{
down_write(&tty->termios_rwsem);
guard(rwsem_write)(&tty->termios_rwsem);
reset_buffer_flags(tty->disc_data);
n_tty_kick_worker(tty);
if (tty->link)
n_tty_packet_mode_flush(tty);
up_write(&tty->termios_rwsem);
}
/**
@@ -737,25 +733,23 @@ static void commit_echoes(struct tty_struct *tty)
size_t nr, old, echoed;
size_t head;
mutex_lock(&ldata->output_lock);
head = ldata->echo_head;
ldata->echo_mark = head;
old = ldata->echo_commit - ldata->echo_tail;
scoped_guard(mutex, &ldata->output_lock) {
head = ldata->echo_head;
ldata->echo_mark = head;
old = ldata->echo_commit - ldata->echo_tail;
/* Process committed echoes if the accumulated # of bytes
* is over the threshold (and try again each time another
* block is accumulated) */
nr = head - ldata->echo_tail;
if (nr < ECHO_COMMIT_WATERMARK ||
(nr % ECHO_BLOCK > old % ECHO_BLOCK)) {
mutex_unlock(&ldata->output_lock);
return;
/*
* Process committed echoes if the accumulated # of bytes is over the threshold
* (and try again each time another block is accumulated)
*/
nr = head - ldata->echo_tail;
if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK))
return;
ldata->echo_commit = head;
echoed = __process_echoes(tty);
}
ldata->echo_commit = head;
echoed = __process_echoes(tty);
mutex_unlock(&ldata->output_lock);
if (echoed && tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
@@ -768,10 +762,10 @@ static void process_echoes(struct tty_struct *tty)
if (ldata->echo_mark == ldata->echo_tail)
return;
mutex_lock(&ldata->output_lock);
ldata->echo_commit = ldata->echo_mark;
echoed = __process_echoes(tty);
mutex_unlock(&ldata->output_lock);
scoped_guard(mutex, &ldata->output_lock) {
ldata->echo_commit = ldata->echo_mark;
echoed = __process_echoes(tty);
}
if (echoed && tty->ops->flush_chars)
tty->ops->flush_chars(tty);
@@ -786,10 +780,9 @@ static void flush_echoes(struct tty_struct *tty)
ldata->echo_commit == ldata->echo_head)
return;
mutex_lock(&ldata->output_lock);
guard(mutex)(&ldata->output_lock);
ldata->echo_commit = ldata->echo_head;
__process_echoes(tty);
mutex_unlock(&ldata->output_lock);
}
/**
@@ -1078,18 +1071,19 @@ static void isig(int sig, struct tty_struct *tty)
if (L_NOFLSH(tty)) {
/* signal only */
__isig(sig, tty);
return;
}
} else { /* signal and flush */
up_read(&tty->termios_rwsem);
down_write(&tty->termios_rwsem);
/* signal and flush */
up_read(&tty->termios_rwsem);
scoped_guard(rwsem_write, &tty->termios_rwsem) {
__isig(sig, tty);
/* clear echo buffer */
mutex_lock(&ldata->output_lock);
ldata->echo_head = ldata->echo_tail = 0;
ldata->echo_mark = ldata->echo_commit = 0;
mutex_unlock(&ldata->output_lock);
scoped_guard(mutex, &ldata->output_lock) {
ldata->echo_head = ldata->echo_tail = 0;
ldata->echo_mark = ldata->echo_commit = 0;
}
/* clear output buffer */
tty_driver_flush_buffer(tty);
@@ -1100,10 +1094,8 @@ static void isig(int sig, struct tty_struct *tty)
/* notify pty master of flush */
if (tty->link)
n_tty_packet_mode_flush(tty);
up_write(&tty->termios_rwsem);
down_read(&tty->termios_rwsem);
}
down_read(&tty->termios_rwsem);
}
/**
@@ -1683,7 +1675,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const u8 *cp, const u8 *fp,
size_t n, rcvd = 0;
int room, overflow;
down_read(&tty->termios_rwsem);
guard(rwsem_read)(&tty->termios_rwsem);
do {
/*
@@ -1752,8 +1744,6 @@ n_tty_receive_buf_common(struct tty_struct *tty, const u8 *cp, const u8 *fp,
n_tty_kick_worker(tty);
}
up_read(&tty->termios_rwsem);
return rcvd;
}
@@ -1879,10 +1869,9 @@ static void n_tty_close(struct tty_struct *tty)
if (tty->link)
n_tty_packet_mode_flush(tty);
down_write(&tty->termios_rwsem);
guard(rwsem_write)(&tty->termios_rwsem);
vfree(ldata);
tty->disc_data = NULL;
up_write(&tty->termios_rwsem);
}
/**
@@ -2247,10 +2236,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf,
u8 cs;
if (kb != kbuf)
break;
spin_lock_irq(&tty->link->ctrl.lock);
cs = tty->link->ctrl.pktstatus;
tty->link->ctrl.pktstatus = 0;
spin_unlock_irq(&tty->link->ctrl.lock);
scoped_guard(spinlock_irq, &tty->link->ctrl.lock) {
cs = tty->link->ctrl.pktstatus;
tty->link->ctrl.pktstatus = 0;
}
*kb++ = cs;
nr--;
break;
@@ -2357,7 +2346,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
return retval;
}
down_read(&tty->termios_rwsem);
guard(rwsem_read)(&tty->termios_rwsem);
/* Write out any echoed characters that are still pending */
process_echoes(tty);
@@ -2395,9 +2384,8 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
struct n_tty_data *ldata = tty->disc_data;
while (nr > 0) {
mutex_lock(&ldata->output_lock);
num = tty->ops->write(tty, b, nr);
mutex_unlock(&ldata->output_lock);
scoped_guard(mutex, &ldata->output_lock)
num = tty->ops->write(tty, b, nr);
if (num < 0) {
retval = num;
goto break_out;
@@ -2424,7 +2412,7 @@ break_out:
remove_wait_queue(&tty->write_wait, &wait);
if (nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
up_read(&tty->termios_rwsem);
return (b - buf) ? b - buf : retval;
}
@@ -2498,12 +2486,11 @@ static int n_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
case TIOCOUTQ:
return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
case TIOCINQ:
down_write(&tty->termios_rwsem);
if (L_ICANON(tty) && !L_EXTPROC(tty))
num = inq_canon(ldata);
else
num = read_cnt(ldata);
up_write(&tty->termios_rwsem);
scoped_guard(rwsem_write, &tty->termios_rwsem)
if (L_ICANON(tty) && !L_EXTPROC(tty))
num = inq_canon(ldata);
else
num = read_cnt(ldata);
return put_user(num, (unsigned int __user *) arg);
default:
return n_tty_ioctl_helper(tty, cmd, arg);

View File

@@ -57,9 +57,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
set_bit(TTY_IO_ERROR, &tty->flags);
wake_up_interruptible(&tty->read_wait);
wake_up_interruptible(&tty->write_wait);
spin_lock_irq(&tty->ctrl.lock);
tty->ctrl.packet = false;
spin_unlock_irq(&tty->ctrl.lock);
scoped_guard(spinlock_irq, &tty->ctrl.lock)
tty->ctrl.packet = false;
/* Review - krefs on tty_link ?? */
if (!tty->link)
return;
@@ -70,10 +69,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
set_bit(TTY_OTHER_CLOSED, &tty->flags);
#ifdef CONFIG_UNIX98_PTYS
if (tty->driver == ptm_driver) {
mutex_lock(&devpts_mutex);
guard(mutex)(&devpts_mutex);
if (tty->link->driver_data)
devpts_pty_kill(tty->link->driver_data);
mutex_unlock(&devpts_mutex);
}
#endif
tty_vhangup(tty->link);
@@ -157,21 +155,23 @@ static int pty_get_lock(struct tty_struct *tty, int __user *arg)
/* Set the packet mode on a pty */
static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
{
int pktmode;
int want_pktmode;
if (get_user(pktmode, arg))
if (get_user(want_pktmode, arg))
return -EFAULT;
spin_lock_irq(&tty->ctrl.lock);
if (pktmode) {
if (!tty->ctrl.packet) {
tty->link->ctrl.pktstatus = 0;
smp_mb();
tty->ctrl.packet = true;
}
} else
guard(spinlock_irq)(&tty->ctrl.lock);
if (!want_pktmode) {
tty->ctrl.packet = false;
spin_unlock_irq(&tty->ctrl.lock);
return 0;
}
if (tty->ctrl.packet)
return 0;
tty->link->ctrl.pktstatus = 0;
smp_mb();
tty->ctrl.packet = true;
return 0;
}
@@ -210,10 +210,9 @@ static void pty_flush_buffer(struct tty_struct *tty)
tty_buffer_flush(to, NULL);
if (to->ctrl.packet) {
spin_lock_irq(&tty->ctrl.lock);
guard(spinlock_irq)(&tty->ctrl.lock);
tty->ctrl.pktstatus |= TIOCPKT_FLUSHWRITE;
wake_up_interruptible(&to->read_wait);
spin_unlock_irq(&tty->ctrl.lock);
}
}
@@ -252,17 +251,17 @@ static void pty_set_termios(struct tty_struct *tty,
STOP_CHAR(tty) == '\023' &&
START_CHAR(tty) == '\021');
if ((old_flow != new_flow) || extproc) {
spin_lock_irq(&tty->ctrl.lock);
if (old_flow != new_flow) {
tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
if (new_flow)
tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;
else
tty->ctrl.pktstatus |= TIOCPKT_NOSTOP;
scoped_guard(spinlock_irq, &tty->ctrl.lock) {
if (old_flow != new_flow) {
tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
if (new_flow)
tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;
else
tty->ctrl.pktstatus |= TIOCPKT_NOSTOP;
}
if (extproc)
tty->ctrl.pktstatus |= TIOCPKT_IOCTL;
}
if (extproc)
tty->ctrl.pktstatus |= TIOCPKT_IOCTL;
spin_unlock_irq(&tty->ctrl.lock);
wake_up_interruptible(&tty->link->read_wait);
}
}
@@ -286,9 +285,9 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws)
struct tty_struct *pty = tty->link;
/* For a PTY we need to lock the tty side */
mutex_lock(&tty->winsize_mutex);
guard(mutex)(&tty->winsize_mutex);
if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
goto done;
return 0;
/* Signal the foreground process group of both ptys */
pgrp = tty_get_pgrp(tty);
@@ -304,8 +303,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws)
tty->winsize = *ws;
pty->winsize = *ws; /* Never used so will go away soon */
done:
mutex_unlock(&tty->winsize_mutex);
return 0;
}
@@ -321,28 +319,26 @@ done:
*/
static void pty_start(struct tty_struct *tty)
{
unsigned long flags;
if (!tty->link || !tty->link->ctrl.packet)
return;
if (tty->link && tty->link->ctrl.packet) {
spin_lock_irqsave(&tty->ctrl.lock, flags);
scoped_guard(spinlock_irqsave, &tty->ctrl.lock) {
tty->ctrl.pktstatus &= ~TIOCPKT_STOP;
tty->ctrl.pktstatus |= TIOCPKT_START;
spin_unlock_irqrestore(&tty->ctrl.lock, flags);
wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
}
wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
}
static void pty_stop(struct tty_struct *tty)
{
unsigned long flags;
if (!tty->link || !tty->link->ctrl.packet)
return;
if (tty->link && tty->link->ctrl.packet) {
spin_lock_irqsave(&tty->ctrl.lock, flags);
scoped_guard(spinlock_irqsave, &tty->ctrl.lock) {
tty->ctrl.pktstatus &= ~TIOCPKT_START;
tty->ctrl.pktstatus |= TIOCPKT_STOP;
spin_unlock_irqrestore(&tty->ctrl.lock, flags);
wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
}
wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
}
/**
@@ -690,15 +686,9 @@ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
struct file *file, int idx)
{
struct tty_struct *tty;
mutex_lock(&devpts_mutex);
tty = devpts_get_priv(file->f_path.dentry);
mutex_unlock(&devpts_mutex);
guard(mutex)(&devpts_mutex);
/* Master must be open before slave */
if (!tty)
return ERR_PTR(-EIO);
return tty;
return devpts_get_priv(file->f_path.dentry) ? : ERR_PTR(-EIO);
}
static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
@@ -796,20 +786,17 @@ static int ptmx_open(struct inode *inode, struct file *filp)
}
/* find a device that is not in use. */
mutex_lock(&devpts_mutex);
index = devpts_new_index(fsi);
mutex_unlock(&devpts_mutex);
scoped_guard(mutex, &devpts_mutex)
index = devpts_new_index(fsi);
retval = index;
if (index < 0)
goto out_put_fsi;
mutex_lock(&tty_mutex);
tty = tty_init_dev(ptm_driver, index);
/* The tty returned here is locked so we can safely
drop the mutex */
mutex_unlock(&tty_mutex);
/* The tty returned here is locked so we can safely drop the mutex */
scoped_guard(mutex, &tty_mutex)
tty = tty_init_dev(ptm_driver, index);
retval = PTR_ERR(tty);
if (IS_ERR(tty))

View File

@@ -98,15 +98,6 @@ struct serial8250_config {
extern unsigned int nr_uarts;
#ifdef CONFIG_SERIAL_8250_SHARE_IRQ
#define SERIAL8250_SHARE_IRQS 1
#else
#define SERIAL8250_SHARE_IRQS 0
#endif
extern unsigned int share_irqs;
extern unsigned int skip_txen_test;
#define SERIAL8250_PORT_FLAGS(_base, _irq, _flags) \
{ \
.iobase = _base, \

View File

@@ -52,6 +52,10 @@ struct irq_info {
static DEFINE_HASHTABLE(irq_lists, IRQ_HASH_BITS);
static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */
static bool skip_txen_test;
module_param(skip_txen_test, bool, 0644);
MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time");
/*
* This is the serial driver's interrupt routine.
*

View File

@@ -361,7 +361,7 @@ static int dw8250_clk_notifier_cb(struct notifier_block *nb,
* deferred event handling complication.
*/
if (event == POST_RATE_CHANGE) {
queue_work(system_unbound_wq, &d->clk_work);
queue_work(system_dfl_wq, &d->clk_work);
return NOTIFY_OK;
}
@@ -680,7 +680,7 @@ static int dw8250_probe(struct platform_device *pdev)
err = clk_notifier_register(data->clk, &data->clk_notifier);
if (err)
return dev_err_probe(dev, err, "Failed to set the clock notifier\n");
queue_work(system_unbound_wq, &data->clk_work);
queue_work(system_dfl_wq, &data->clk_work);
}
platform_set_drvdata(pdev, data);

View File

@@ -505,7 +505,7 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev,
unsigned char status;
int err;
err = serial8250_pci_setup_port(pcidev, port, 0, offset, board->reg_shift);
err = serial8250_pci_setup_port(pcidev, port, 0, offset, board->reg_shift, priv->virt);
if (err)
return err;
@@ -833,7 +833,7 @@ static int cti_port_setup_common(struct exar8250 *priv,
port->port.port_id = idx;
port->port.uartclk = priv->osc_freq;
ret = serial8250_pci_setup_port(pcidev, port, 0, offset, 0);
ret = serial8250_pci_setup_port(pcidev, port, 0, offset, 0, priv->virt);
if (ret)
return ret;

View File

@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 KEBA Industrial Automation GmbH
*
* Driver for KEBA UART FPGA IP core
*/
#include <linux/auxiliary_bus.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/misc/keba.h>
#include <linux/module.h>
#include "8250.h"
#define KUART "kuart"
/* flags */
#define KUART_RS485 BIT(0)
#define KUART_USE_CAPABILITY BIT(1)
/* registers */
#define KUART_VERSION 0x0000
#define KUART_REVISION 0x0001
#define KUART_CAPABILITY 0x0002
#define KUART_CONTROL 0x0004
#define KUART_BASE 0x000C
#define KUART_REGSHIFT 2
#define KUART_CLK 1843200
/* mode flags */
enum kuart_mode {
KUART_MODE_NONE = 0,
KUART_MODE_RS485,
KUART_MODE_RS422,
KUART_MODE_RS232
};
/* capability flags */
#define KUART_CAPABILITY_NONE BIT(KUART_MODE_NONE)
#define KUART_CAPABILITY_RS485 BIT(KUART_MODE_RS485)
#define KUART_CAPABILITY_RS422 BIT(KUART_MODE_RS422)
#define KUART_CAPABILITY_RS232 BIT(KUART_MODE_RS232)
#define KUART_CAPABILITY_MASK GENMASK(3, 0)
/* Additional Control Register DTR line configuration */
#define UART_ACR_DTRLC_MASK 0x18
#define UART_ACR_DTRLC_COMPAT 0x00
#define UART_ACR_DTRLC_ENABLE_LOW 0x10
struct kuart {
struct keba_uart_auxdev *auxdev;
void __iomem *base;
unsigned int line;
unsigned int flags;
u8 capability;
enum kuart_mode mode;
};
static void kuart_set_phy_mode(struct kuart *kuart, enum kuart_mode mode)
{
iowrite8(mode, kuart->base + KUART_CONTROL);
}
static void kuart_enhanced_mode(struct uart_8250_port *up, bool enable)
{
u8 lcr, efr;
/* backup LCR register */
lcr = serial_in(up, UART_LCR);
/* enable 650 compatible register set (EFR, ...) */
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
/* enable/disable enhanced mode with indexed control registers */
efr = serial_in(up, UART_EFR);
if (enable)
efr |= UART_EFR_ECB;
else
efr &= ~UART_EFR_ECB;
serial_out(up, UART_EFR, efr);
/* disable 650 compatible register set, restore LCR */
serial_out(up, UART_LCR, lcr);
}
static void kuart_dtr_line_config(struct uart_8250_port *up, u8 dtrlc)
{
u8 acr;
/* set index register to 0 to access ACR register */
serial_out(up, UART_SCR, UART_ACR);
/* set value register to 0x10 writing DTR mode (1,0) */
acr = serial_in(up, UART_LSR);
acr &= ~UART_ACR_DTRLC_MASK;
acr |= dtrlc;
serial_out(up, UART_LSR, acr);
}
static int kuart_rs485_config(struct uart_port *port, struct ktermios *termios,
struct serial_rs485 *rs485)
{
struct uart_8250_port *up = up_to_u8250p(port);
struct kuart *kuart = port->private_data;
enum kuart_mode mode;
u8 dtrlc;
if (rs485->flags & SER_RS485_ENABLED) {
if (rs485->flags & SER_RS485_MODE_RS422)
mode = KUART_MODE_RS422;
else
mode = KUART_MODE_RS485;
} else {
mode = KUART_MODE_RS232;
}
if (mode == kuart->mode)
return 0;
if (kuart->flags & KUART_USE_CAPABILITY) {
/* deactivate physical interface, break before make */
kuart_set_phy_mode(kuart, KUART_MODE_NONE);
}
if (mode == KUART_MODE_RS485) {
/*
* Set DTR line configuration of 95x UART to DTR mode (1,0).
* In this mode the DTR pin drives the active-low enable pin of
* an external RS485 buffer. The DTR pin will be forced low
* whenever the transmitter is not empty, otherwise DTR pin is
* high.
*/
dtrlc = UART_ACR_DTRLC_ENABLE_LOW;
} else {
/*
* Set DTR line configuration of 95x UART to DTR mode (0,0).
* In this mode the DTR pin is compatible with 16C450, 16C550,
* 16C650 and 16c670 (i.e. normal).
*/
dtrlc = UART_ACR_DTRLC_COMPAT;
}
kuart_enhanced_mode(up, true);
kuart_dtr_line_config(up, dtrlc);
kuart_enhanced_mode(up, false);
if (kuart->flags & KUART_USE_CAPABILITY) {
/* activate selected physical interface */
kuart_set_phy_mode(kuart, mode);
}
kuart->mode = mode;
return 0;
}
static int kuart_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *id)
{
struct device *dev = &auxdev->dev;
struct uart_8250_port uart = {};
struct resource res;
struct kuart *kuart;
int retval;
kuart = devm_kzalloc(dev, sizeof(*kuart), GFP_KERNEL);
if (!kuart)
return -ENOMEM;
kuart->auxdev = container_of(auxdev, struct keba_uart_auxdev, auxdev);
kuart->flags = id->driver_data;
auxiliary_set_drvdata(auxdev, kuart);
/*
* map only memory in front of UART registers, UART registers will be
* mapped by serial port
*/
res = kuart->auxdev->io;
res.end = res.start + KUART_BASE - 1;
kuart->base = devm_ioremap_resource(dev, &res);
if (IS_ERR(kuart->base))
return PTR_ERR(kuart->base);
if (kuart->flags & KUART_USE_CAPABILITY) {
/*
* supported modes are read from capability register, at least
* one mode other than none must be supported
*/
kuart->capability = ioread8(kuart->base + KUART_CAPABILITY) &
KUART_CAPABILITY_MASK;
if ((kuart->capability & ~KUART_CAPABILITY_NONE) == 0)
return -EIO;
}
spin_lock_init(&uart.port.lock);
uart.port.dev = dev;
uart.port.mapbase = kuart->auxdev->io.start + KUART_BASE;
uart.port.irq = kuart->auxdev->irq;
uart.port.uartclk = KUART_CLK;
uart.port.private_data = kuart;
/* 8 bit registers are 32 bit aligned => shift register offset */
uart.port.iotype = UPIO_MEM32;
uart.port.regshift = KUART_REGSHIFT;
/*
* UART mixes 16550, 16750 and 16C950 (for RS485) standard => auto
* configuration works best
*/
uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_IOREMAP;
/*
* UART supports RS485, RS422 and RS232 with switching of physical
* interface
*/
uart.port.rs485_config = kuart_rs485_config;
if (kuart->flags & KUART_RS485) {
uart.port.rs485_supported.flags = SER_RS485_ENABLED |
SER_RS485_RTS_ON_SEND;
uart.port.rs485.flags = SER_RS485_ENABLED |
SER_RS485_RTS_ON_SEND;
}
if (kuart->flags & KUART_USE_CAPABILITY) {
/* default mode priority is RS485 > RS422 > RS232 */
if (kuart->capability & KUART_CAPABILITY_RS422) {
uart.port.rs485_supported.flags |= SER_RS485_ENABLED |
SER_RS485_RTS_ON_SEND |
SER_RS485_MODE_RS422;
uart.port.rs485.flags = SER_RS485_ENABLED |
SER_RS485_RTS_ON_SEND |
SER_RS485_MODE_RS422;
}
if (kuart->capability & KUART_CAPABILITY_RS485) {
uart.port.rs485_supported.flags |= SER_RS485_ENABLED |
SER_RS485_RTS_ON_SEND;
uart.port.rs485.flags = SER_RS485_ENABLED |
SER_RS485_RTS_ON_SEND;
}
}
retval = serial8250_register_8250_port(&uart);
if (retval < 0) {
dev_err(&auxdev->dev, "UART registration failed!\n");
return retval;
}
kuart->line = retval;
return 0;
}
static void kuart_remove(struct auxiliary_device *auxdev)
{
struct kuart *kuart = auxiliary_get_drvdata(auxdev);
if (kuart->flags & KUART_USE_CAPABILITY)
kuart_set_phy_mode(kuart, KUART_MODE_NONE);
serial8250_unregister_port(kuart->line);
}
static const struct auxiliary_device_id kuart_devtype_aux[] = {
{ .name = "keba.rs485-uart", .driver_data = KUART_RS485 },
{ .name = "keba.rs232-uart", .driver_data = 0 },
{ .name = "keba.uart", .driver_data = KUART_USE_CAPABILITY },
{}
};
MODULE_DEVICE_TABLE(auxiliary, kuart_devtype_aux);
static struct auxiliary_driver kuart_driver_aux = {
.name = KUART,
.id_table = kuart_devtype_aux,
.probe = kuart_probe,
.remove = kuart_remove,
};
module_auxiliary_driver(kuart_driver_aux);
MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
MODULE_DESCRIPTION("KEBA 8250 serial port driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Serial Port driver for Loongson family chips
*
* Copyright (C) 2020-2025 Loongson Technology Corporation Limited
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/property.h>
#include <linux/math.h>
#include <linux/mod_devicetable.h>
#include <linux/pm.h>
#include <linux/reset.h>
#include "8250.h"
/* Divisor Latch Fraction Register */
#define LOONGSON_UART_DLF 0x2
#define LOONGSON_QUOT_FRAC_MASK GENMASK(7, 0)
#define LOONGSON_QUOT_DIV_MASK GENMASK(15, 8)
struct loongson_uart_ddata {
bool has_frac;
u8 mcr_invert;
u8 msr_invert;
};
static const struct loongson_uart_ddata ls2k0500_uart_data = {
.has_frac = false,
.mcr_invert = UART_MCR_RTS | UART_MCR_DTR,
.msr_invert = UART_MSR_CTS | UART_MSR_DSR,
};
static const struct loongson_uart_ddata ls2k1500_uart_data = {
.has_frac = true,
.mcr_invert = UART_MCR_RTS | UART_MCR_DTR,
.msr_invert = 0,
};
struct loongson_uart_priv {
int line;
struct clk *clk;
struct resource *res;
struct reset_control *rst;
const struct loongson_uart_ddata *ddata;
};
static u8 serial_fixup(struct uart_port *p, unsigned int offset, u8 val)
{
struct loongson_uart_priv *priv = p->private_data;
switch (offset) {
case UART_MCR:
return val ^ priv->ddata->mcr_invert;
case UART_MSR:
return val ^ priv->ddata->msr_invert;
default:
return val;
}
}
static u32 loongson_serial_in(struct uart_port *p, unsigned int offset)
{
u8 val;
val = readb(p->membase + (offset << p->regshift));
return serial_fixup(p, offset, val);
}
static void loongson_serial_out(struct uart_port *p, unsigned int offset, unsigned int value)
{
u8 val;
offset <<= p->regshift;
val = serial_fixup(p, offset, value);
writeb(val, p->membase + offset);
}
static unsigned int loongson_frac_get_divisor(struct uart_port *port, unsigned int baud,
unsigned int *frac)
{
unsigned int quot;
quot = DIV_ROUND_CLOSEST((port->uartclk << 4), baud);
*frac = FIELD_GET(LOONGSON_QUOT_FRAC_MASK, quot);
return FIELD_GET(LOONGSON_QUOT_DIV_MASK, quot);
}
static void loongson_frac_set_divisor(struct uart_port *port, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
{
struct uart_8250_port *up = up_to_u8250p(port);
serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
serial_dl_write(up, quot);
serial_port_out(port, LOONGSON_UART_DLF, quot_frac);
}
static int loongson_uart_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uart_8250_port uart = {};
struct loongson_uart_priv *priv;
struct uart_port *port;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->ddata = device_get_match_data(dev);
port = &uart.port;
spin_lock_init(&port->lock);
port->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_IOREMAP;
port->iotype = UPIO_MEM;
port->regshift = 0;
port->dev = dev;
port->type = PORT_16550A;
port->private_data = priv;
port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->res);
if (!port->membase)
return -ENOMEM;
port->mapbase = priv->res->start;
port->mapsize = resource_size(priv->res);
port->serial_in = loongson_serial_in;
port->serial_out = loongson_serial_out;
if (priv->ddata->has_frac) {
port->get_divisor = loongson_frac_get_divisor;
port->set_divisor = loongson_frac_set_divisor;
}
ret = uart_read_port_properties(port);
if (ret)
return ret;
if (!port->uartclk) {
priv->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"Unable to determine clock frequency!\n");
port->uartclk = clk_get_rate(priv->clk);
}
priv->rst = devm_reset_control_get_optional_shared(dev, NULL);
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
ret = reset_control_deassert(priv->rst);
if (ret)
return ret;
ret = serial8250_register_8250_port(&uart);
if (ret < 0) {
reset_control_assert(priv->rst);
return ret;
}
priv->line = ret;
platform_set_drvdata(pdev, priv);
return 0;
}
static void loongson_uart_remove(struct platform_device *pdev)
{
struct loongson_uart_priv *priv = platform_get_drvdata(pdev);
serial8250_unregister_port(priv->line);
reset_control_assert(priv->rst);
}
static int loongson_uart_suspend(struct device *dev)
{
struct loongson_uart_priv *priv = dev_get_drvdata(dev);
struct uart_8250_port *up = serial8250_get_port(priv->line);
serial8250_suspend_port(priv->line);
if (!uart_console(&up->port) || console_suspend_enabled)
clk_disable_unprepare(priv->clk);
return 0;
}
static int loongson_uart_resume(struct device *dev)
{
struct loongson_uart_priv *priv = dev_get_drvdata(dev);
struct uart_8250_port *up = serial8250_get_port(priv->line);
int ret;
if (!uart_console(&up->port) || console_suspend_enabled) {
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
}
serial8250_resume_port(priv->line);
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(loongson_uart_pm_ops, loongson_uart_suspend,
loongson_uart_resume);
static const struct of_device_id loongson_uart_of_ids[] = {
{ .compatible = "loongson,ls2k0500-uart", .data = &ls2k0500_uart_data },
{ .compatible = "loongson,ls2k1500-uart", .data = &ls2k1500_uart_data },
{ },
};
MODULE_DEVICE_TABLE(of, loongson_uart_of_ids);
static struct platform_driver loongson_uart_driver = {
.probe = loongson_uart_probe,
.remove = loongson_uart_remove,
.driver = {
.name = "loongson-uart",
.pm = pm_ptr(&loongson_uart_pm_ops),
.of_match_table = loongson_uart_of_ids,
},
};
module_platform_driver(loongson_uart_driver);
MODULE_DESCRIPTION("Loongson UART driver");
MODULE_AUTHOR("Loongson Technology Corporation Limited.");
MODULE_LICENSE("GPL");

View File

@@ -95,7 +95,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
u32 spd;
int ret;
memset(port, 0, sizeof *port);
memset(port, 0, sizeof(*port));
pm_runtime_enable(&ofdev->dev);
pm_runtime_get_sync(&ofdev->dev);

View File

@@ -95,6 +95,11 @@
#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381
#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683
#define PCI_DEVICE_ID_ADDIDATA_CPCI7500 0x7003
#define PCI_DEVICE_ID_ADDIDATA_CPCI7500_NG 0x7024
#define PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG 0x7025
#define PCI_DEVICE_ID_ADDIDATA_CPCI7300_NG 0x7026
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588
@@ -165,7 +170,15 @@ static int
setup_port(struct serial_private *priv, struct uart_8250_port *port,
u8 bar, unsigned int offset, int regshift)
{
return serial8250_pci_setup_port(priv->dev, port, bar, offset, regshift);
void __iomem *iomem = NULL;
if (pci_resource_flags(priv->dev, bar) & IORESOURCE_MEM) {
iomem = pcim_iomap(priv->dev, bar, 0);
if (!iomem)
return -ENOMEM;
}
return serial8250_pci_setup_port(priv->dev, port, bar, offset, regshift, iomem);
}
/*
@@ -5996,6 +6009,38 @@ static const struct pci_device_id serial_pci_tbl[] = {
0,
pbn_ADDIDATA_PCIe_8_3906250 },
{ PCI_VENDOR_ID_ADDIDATA,
PCI_DEVICE_ID_ADDIDATA_CPCI7500,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
pbn_b0_4_115200 },
{ PCI_VENDOR_ID_ADDIDATA,
PCI_DEVICE_ID_ADDIDATA_CPCI7500_NG,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
pbn_b0_4_115200 },
{ PCI_VENDOR_ID_ADDIDATA,
PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
pbn_b0_2_115200 },
{ PCI_VENDOR_ID_ADDIDATA,
PCI_DEVICE_ID_ADDIDATA_CPCI7300_NG,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
pbn_b0_1_115200 },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
PCI_VENDOR_ID_IBM, 0x0299,
0, 0, pbn_b0_bt_2_115200 },

View File

@@ -671,7 +671,7 @@ static int pci1xxxx_resume(struct device *dev)
}
static int pci1xxxx_setup(struct pci_dev *pdev,
struct uart_8250_port *port, int port_idx, int rev)
struct uart_8250_port *port, int port_idx, struct pci1xxxx_8250 *priv)
{
int ret;
@@ -698,12 +698,12 @@ static int pci1xxxx_setup(struct pci_dev *pdev,
* C0 and later revisions support Burst operation.
* RTS workaround in mctrl is applicable only to B0.
*/
if (rev >= 0xC0)
if (priv->dev_rev >= 0xC0)
port->port.handle_irq = pci1xxxx_handle_irq;
else if (rev == 0xB0)
else if (priv->dev_rev == 0xB0)
port->port.set_mctrl = pci1xxxx_set_mctrl;
ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0);
ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0, priv->membase);
if (ret < 0)
return ret;
@@ -821,7 +821,7 @@ static int pci1xxxx_serial_probe(struct pci_dev *pdev,
else
uart.port.irq = pci_irq_vector(pdev, 0);
rc = pci1xxxx_setup(pdev, &uart, port_idx, priv->dev_rev);
rc = pci1xxxx_setup(pdev, &uart, port_idx, priv);
if (rc) {
dev_warn(dev, "Failed to setup port %u\n", i);
continue;

View File

@@ -22,19 +22,16 @@ int serial_8250_warn_need_ioport(struct pci_dev *dev)
EXPORT_SYMBOL_NS_GPL(serial_8250_warn_need_ioport, "SERIAL_8250_PCI");
int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port,
u8 bar, unsigned int offset, int regshift)
u8 bar, unsigned int offset, int regshift, void __iomem *iomem)
{
if (bar >= PCI_STD_NUM_BARS)
return -EINVAL;
if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev))
return -ENOMEM;
port->port.iotype = UPIO_MEM;
port->port.iobase = 0;
port->port.mapbase = pci_resource_start(dev, bar) + offset;
port->port.membase = pcim_iomap_table(dev)[bar] + offset;
port->port.membase = iomem + offset;
port->port.regshift = regshift;
} else if (IS_ENABLED(CONFIG_HAS_IOPORT)) {
port->port.iotype = UPIO_PORT;

View File

@@ -12,6 +12,6 @@ struct pci_dev;
struct uart_8250_port;
int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, u8 bar,
unsigned int offset, int regshift);
unsigned int offset, int regshift, void __iomem *iomem);
int serial_8250_warn_need_ioport(struct pci_dev *dev);

View File

@@ -29,10 +29,8 @@
* Configuration:
* share_irqs: Whether we pass IRQF_SHARED to request_irq().
* This option is unsafe when used on edge-triggered interrupts.
* skip_txen_test: Force skip of txen test at init time.
*/
unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
unsigned int skip_txen_test;
static bool share_irqs = IS_ENABLED(CONFIG_SERIAL_8250_SHARE_IRQ);
unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
@@ -60,7 +58,7 @@ EXPORT_SYMBOL(serial8250_set_isa_configurator);
static void __init __serial8250_isa_init_ports(void)
{
int i, irqflag = 0;
int i;
if (nr_uarts > UART_NR)
nr_uarts = UART_NR;
@@ -77,9 +75,6 @@ static void __init __serial8250_isa_init_ports(void)
univ8250_port_ops = *univ8250_port_base_ops;
univ8250_rsa_support(&univ8250_port_ops, univ8250_port_base_ops);
if (share_irqs)
irqflag = IRQF_SHARED;
for (i = 0; i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++) {
struct uart_8250_port *up = serial8250_get_port(i);
struct uart_port *port = &up->port;
@@ -94,7 +89,9 @@ static void __init __serial8250_isa_init_ports(void)
port->iotype = old_serial_port[i].io_type;
port->regshift = old_serial_port[i].iomem_reg_shift;
port->irqflags |= irqflag;
if (share_irqs)
port->irqflags |= IRQF_SHARED;
if (serial8250_isa_config != NULL)
serial8250_isa_config(i, &up->port, &up->capabilities);
}
@@ -157,15 +154,12 @@ static int serial8250_probe_acpi(struct platform_device *pdev)
static int serial8250_probe_platform(struct platform_device *dev, struct plat_serial8250_port *p)
{
int ret, i, irqflag = 0;
int ret, i;
struct uart_8250_port *uart __free(kfree) = kzalloc(sizeof(*uart), GFP_KERNEL);
if (!uart)
return -ENOMEM;
if (share_irqs)
irqflag = IRQF_SHARED;
for (i = 0; p && p->flags != 0; p++, i++) {
uart->port.iobase = p->iobase;
uart->port.membase = p->membase;
@@ -193,7 +187,10 @@ static int serial8250_probe_platform(struct platform_device *dev, struct plat_se
uart->port.get_mctrl = p->get_mctrl;
uart->port.pm = p->pm;
uart->port.dev = &dev->dev;
uart->port.irqflags |= irqflag;
if (share_irqs)
uart->port.irqflags |= IRQF_SHARED;
ret = serial8250_register_8250_port(uart);
if (ret < 0) {
dev_err(&dev->dev, "unable to register port at index %d "
@@ -380,40 +377,10 @@ module_exit(serial8250_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 serial platform driver");
module_param_hw(share_irqs, uint, other, 0644);
module_param_hw(share_irqs, bool, other, 0644);
MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices (unsafe)");
module_param(nr_uarts, uint, 0644);
MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")");
module_param(skip_txen_test, uint, 0644);
MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time");
MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS
#ifndef MODULE
/*
* This module was renamed to 8250_core in 3.7. Keep the old "8250" name
* working as well for the module options so we don't break people. We
* need to keep the names identical and the convenient macros will happily
* refuse to let us do that by failing the build with redefinition errors
* of global variables. So we stick them inside a dummy function to avoid
* those conflicts. The options still get parsed, and the redefined
* MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive.
*
* This is hacky. I'm sorry.
*/
static void __used s8250_options(void)
{
#undef MODULE_PARAM_PREFIX
#define MODULE_PARAM_PREFIX "8250_core."
module_param_cb(share_irqs, &param_ops_uint, &share_irqs, 0644);
module_param_cb(nr_uarts, &param_ops_uint, &nr_uarts, 0644);
module_param_cb(skip_txen_test, &param_ops_uint, &skip_txen_test, 0644);
}
#else
MODULE_ALIAS("8250_core");
#endif
#endif

View File

@@ -209,27 +209,3 @@ void rsa_reset(struct uart_8250_port *up)
serial_out(up, UART_RSA_FRR, 0);
}
#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS
#ifndef MODULE
/*
* Keep the old "8250" name working as well for the module options so we don't
* break people. We need to keep the names identical and the convenient macros
* will happily refuse to let us do that by failing the build with redefinition
* errors of global variables. So we stick them inside a dummy function to
* avoid those conflicts. The options still get parsed, and the redefined
* MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive.
*
* This is hacky. I'm sorry.
*/
static void __used rsa8250_options(void)
{
#undef MODULE_PARAM_PREFIX
#define MODULE_PARAM_PREFIX "8250_core."
__module_param_call(MODULE_PARAM_PREFIX, probe_rsa,
&param_array_ops, .arr = &__param_arr_probe_rsa,
0444, -1, 0);
}
#endif
#endif

View File

@@ -34,23 +34,6 @@ config SERIAL_8250
Most people will say Y or M here, so that they can use serial mice,
modems and similar devices connecting to the standard serial ports.
config SERIAL_8250_DEPRECATED_OPTIONS
bool "Support 8250_core.* kernel options (DEPRECATED)"
depends on SERIAL_8250
default y
help
In 3.7 we renamed 8250 to 8250_core by mistake, so now we have to
accept kernel parameters in both forms like 8250_core.nr_uarts=4 and
8250.nr_uarts=4. We now renamed the module back to 8250, but if
anybody noticed in 3.7 and changed their userspace we still have to
keep the 8250_core.* options around until they revert the changes
they already did.
If 8250 is built as a module, this adds 8250_core alias instead.
If you did not notice yet and/or you have userspace from pre-3.7, it
is safe (and recommended) to say N here.
config SERIAL_8250_PNP
bool "8250/16550 PNP device support" if EXPERT
depends on SERIAL_8250 && PNP
@@ -430,6 +413,19 @@ config SERIAL_8250_IOC3
behind the IOC3 device on those systems. Maximum baud speed is
38400bps using this driver.
config SERIAL_8250_KEBA
tristate "Support for KEBA 8250 UART"
depends on SERIAL_8250
depends on KEBA_CP500
help
Selecting this option will add support for KEBA UARTs. These UARTs
are used for the serial interfaces of KEBA PLCs.
This driver can also be built as a module. If so, the module will
be called 8250_keba.
If unsure, say N.
config SERIAL_8250_RT288X
bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support"
depends on SERIAL_8250
@@ -468,6 +464,16 @@ config SERIAL_8250_OMAP_TTYO_FIXUP
not booting kernel because the serial console remains silent in case
they forgot to update the command line.
config SERIAL_8250_LOONGSON
tristate "Loongson 8250 based serial port"
depends on SERIAL_8250
depends on LOONGARCH || COMPILE_TEST
help
If you have a machine based on LoongArch CPU you can enable
its onboard serial ports by enabling this option. The option
is applicable to both devicetree and ACPI, say Y to this option.
If unsure, say N.
config SERIAL_8250_LPC18XX
tristate "NXP LPC18xx/43xx serial port support"
depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST)

View File

@@ -38,6 +38,8 @@ obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.o
obj-$(CONFIG_SERIAL_8250_KEBA) += 8250_keba.o
obj-$(CONFIG_SERIAL_8250_LOONGSON) += 8250_loongson.o
obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o
obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o

View File

@@ -1044,7 +1044,7 @@ config SERIAL_SCCNXP_CONSOLE
config SERIAL_SC16IS7XX
tristate "NXP SC16IS7xx UART support"
depends on SPI_MASTER || I2C
depends on SPI_MASTER || I2C || COMPILE_TEST
select SERIAL_CORE
select SERIAL_SC16IS7XX_SPI if SPI_MASTER
select SERIAL_SC16IS7XX_I2C if I2C

View File

@@ -560,6 +560,64 @@ static int ar933x_uart_verify_port(struct uart_port *port,
return 0;
}
#ifdef CONFIG_CONSOLE_POLL
static int ar933x_poll_get_char(struct uart_port *port)
{
struct ar933x_uart_port *up =
container_of(port, struct ar933x_uart_port, port);
unsigned int rdata;
unsigned char ch;
u32 imr;
/* Disable all interrupts */
imr = ar933x_uart_read(up, AR933X_UART_INT_EN_REG);
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, 0);
rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
if ((rdata & AR933X_UART_DATA_RX_CSR) == 0) {
/* Enable interrupts */
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, imr);
return NO_POLL_CHAR;
}
/* remove the character from the FIFO */
ar933x_uart_write(up, AR933X_UART_DATA_REG,
AR933X_UART_DATA_RX_CSR);
ch = rdata & AR933X_UART_DATA_TX_RX_MASK;
/* Enable interrupts */
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, imr);
return ch;
}
static void ar933x_poll_put_char(struct uart_port *port, unsigned char c)
{
struct ar933x_uart_port *up =
container_of(port, struct ar933x_uart_port, port);
u32 imr;
/* Disable all interrupts */
imr = ar933x_uart_read(up, AR933X_UART_INT_EN_REG);
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, 0);
/* Wait until FIFO is empty */
while (!(ar933x_uart_read(up, AR933X_UART_DATA_REG) & AR933X_UART_DATA_TX_CSR))
cpu_relax();
/* Write a character */
ar933x_uart_putc(up, c);
/* Wait until FIFO is empty */
while (!(ar933x_uart_read(up, AR933X_UART_DATA_REG) & AR933X_UART_DATA_TX_CSR))
cpu_relax();
/* Enable interrupts */
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, imr);
}
#endif
static const struct uart_ops ar933x_uart_ops = {
.tx_empty = ar933x_uart_tx_empty,
.set_mctrl = ar933x_uart_set_mctrl,
@@ -576,6 +634,10 @@ static const struct uart_ops ar933x_uart_ops = {
.request_port = ar933x_uart_request_port,
.config_port = ar933x_uart_config_port,
.verify_port = ar933x_uart_verify_port,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = ar933x_poll_get_char,
.poll_put_char = ar933x_poll_put_char,
#endif
};
static int ar933x_config_rs485(struct uart_port *port, struct ktermios *termios,

View File

@@ -3087,6 +3087,8 @@ static int lpuart_suspend_noirq(struct device *dev)
static int lpuart_resume_noirq(struct device *dev)
{
struct lpuart_port *sport = dev_get_drvdata(dev);
struct tty_port *port = &sport->port.state->port;
bool wake_active;
u32 stat;
pinctrl_pm_select_default_state(dev);
@@ -3098,6 +3100,12 @@ static int lpuart_resume_noirq(struct device *dev)
if (lpuart_is_32(sport)) {
stat = lpuart32_read(&sport->port, UARTSTAT);
lpuart32_write(&sport->port, stat, UARTSTAT);
/* check whether lpuart wakeup was triggered */
wake_active = stat & (UARTSTAT_RDRF | UARTSTAT_RXEDGIF);
if (wake_active && irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq)))
pm_wakeup_event(tty_port_tty_get(port)->dev, 0);
}
}

View File

@@ -1723,6 +1723,7 @@ static int icom_probe(struct pci_dev *dev,
retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg);
if (retval) {
dev_err(&dev->dev, "PCI Config read FAILED\n");
retval = pcibios_err_to_errno(retval);
goto probe_exit0;
}

View File

@@ -30,7 +30,7 @@
#include <linux/iopoll.h>
#include <linux/dma-mapping.h>
#include <asm/irq.h>
#include <linux/irq.h>
#include <linux/dma/imx-dma.h>
#include "serial_mctrl_gpio.h"
@@ -2697,16 +2697,32 @@ static void imx_uart_save_context(struct imx_port *sport)
/* called with irq off */
static void imx_uart_enable_wakeup(struct imx_port *sport, bool on)
{
u32 ucr3;
struct tty_port *port = &sport->port.state->port;
struct device *tty_dev;
bool may_wake = false, wake_active = false;
u32 ucr3, usr1;
scoped_guard(tty_port_tty, port) {
struct tty_struct *tty = scoped_tty();
tty_dev = tty->dev;
may_wake = tty_dev && device_may_wakeup(tty_dev);
}
/* only configure the wake register when device set as wakeup source */
if (!may_wake)
return;
uart_port_lock_irq(&sport->port);
usr1 = imx_uart_readl(sport, USR1);
ucr3 = imx_uart_readl(sport, UCR3);
if (on) {
imx_uart_writel(sport, USR1_AWAKE, USR1);
ucr3 |= UCR3_AWAKEN;
} else {
ucr3 &= ~UCR3_AWAKEN;
wake_active = usr1 & USR1_AWAKE;
}
imx_uart_writel(sport, ucr3, UCR3);
@@ -2717,10 +2733,14 @@ static void imx_uart_enable_wakeup(struct imx_port *sport, bool on)
ucr1 |= UCR1_RTSDEN;
} else {
ucr1 &= ~UCR1_RTSDEN;
wake_active = wake_active || (usr1 & USR1_RTSD);
}
imx_uart_writel(sport, ucr1, UCR1);
}
if (wake_active && irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq)))
pm_wakeup_event(tty_port_tty_get(port)->dev, 0);
uart_port_unlock_irq(&sport->port);
}

View File

@@ -343,7 +343,7 @@ static int mux_verify_port(struct uart_port *port, struct serial_struct *ser)
}
/**
* mux_drv_poll - Mux poll function.
* mux_poll - Mux poll function.
* @unused: Unused variable
*
* This function periodically polls the Serial MUX to check for new data.

View File

@@ -14,6 +14,7 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -101,10 +102,16 @@
#define DMA_RX_BUF_SIZE 2048
static DEFINE_IDA(port_ida);
#define DOMAIN_IDX_POWER 0
#define DOMAIN_IDX_PERF 1
struct qcom_geni_device_data {
bool console;
enum geni_se_xfer_mode mode;
struct dev_pm_domain_attach_data pd_data;
int (*resources_init)(struct uart_port *uport);
int (*set_rate)(struct uart_port *uport, unsigned int baud);
int (*power_state)(struct uart_port *uport, bool state);
};
struct qcom_geni_private_data {
@@ -142,6 +149,7 @@ struct qcom_geni_serial_port {
struct qcom_geni_private_data private_data;
const struct qcom_geni_device_data *dev_data;
struct dev_pm_domain_list *pd_list;
};
static const struct uart_ops qcom_geni_console_pops;
@@ -1299,6 +1307,42 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud)
return 0;
}
static int geni_serial_set_level(struct uart_port *uport, unsigned int baud)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
struct device *perf_dev = port->pd_list->pd_devs[DOMAIN_IDX_PERF];
/*
* The performance protocol sets UART communication
* speeds by selecting different performance levels
* through the OPP framework.
*
* Supported perf levels for baudrates in firmware are below
* +---------------------+--------------------+
* | Perf level value | Baudrate values |
* +---------------------+--------------------+
* | 300 | 300 |
* | 1200 | 1200 |
* | 2400 | 2400 |
* | 4800 | 4800 |
* | 9600 | 9600 |
* | 19200 | 19200 |
* | 38400 | 38400 |
* | 57600 | 57600 |
* | 115200 | 115200 |
* | 230400 | 230400 |
* | 460800 | 460800 |
* | 921600 | 921600 |
* | 2000000 | 2000000 |
* | 3000000 | 3000000 |
* | 3200000 | 3200000 |
* | 4000000 | 4000000 |
* +---------------------+--------------------+
*/
return dev_pm_opp_set_level(perf_dev, baud);
}
static void qcom_geni_serial_set_termios(struct uart_port *uport,
struct ktermios *termios,
const struct ktermios *old)
@@ -1317,7 +1361,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
/* baud rate */
baud = uart_get_baud_rate(uport, termios, old, 300, 8000000);
ret = geni_serial_set_rate(uport, baud);
ret = port->dev_data->set_rate(uport, baud);
if (ret)
return;
@@ -1604,8 +1648,27 @@ static int geni_serial_resources_off(struct uart_port *uport)
return 0;
}
static int geni_serial_resource_init(struct qcom_geni_serial_port *port)
static int geni_serial_resource_state(struct uart_port *uport, bool power_on)
{
return power_on ? geni_serial_resources_on(uport) : geni_serial_resources_off(uport);
}
static int geni_serial_pwr_init(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
int ret;
ret = dev_pm_domain_attach_list(port->se.dev,
&port->dev_data->pd_data, &port->pd_list);
if (ret <= 0)
return -EINVAL;
return 0;
}
static int geni_serial_resource_init(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
int ret;
port->se.clk = devm_clk_get(port->se.dev, "se");
@@ -1650,10 +1713,10 @@ static void qcom_geni_serial_pm(struct uart_port *uport,
old_state = UART_PM_STATE_OFF;
if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
geni_serial_resources_on(uport);
pm_runtime_resume_and_get(uport->dev);
else if (new_state == UART_PM_STATE_OFF &&
old_state == UART_PM_STATE_ON)
geni_serial_resources_off(uport);
pm_runtime_put_sync(uport->dev);
}
@@ -1756,13 +1819,16 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
port->se.dev = &pdev->dev;
port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
ret = geni_serial_resource_init(port);
ret = port->dev_data->resources_init(uport);
if (ret)
return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
if (!res) {
ret = -EINVAL;
goto error;
}
uport->mapbase = res->start;
uport->rs485_config = qcom_geni_rs485_config;
@@ -1774,19 +1840,26 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
if (!data->console) {
port->rx_buf = devm_kzalloc(uport->dev,
DMA_RX_BUF_SIZE, GFP_KERNEL);
if (!port->rx_buf)
return -ENOMEM;
if (!port->rx_buf) {
ret = -ENOMEM;
goto error;
}
}
port->name = devm_kasprintf(uport->dev, GFP_KERNEL,
"qcom_geni_serial_%s%d",
uart_console(uport) ? "console" : "uart", uport->line);
if (!port->name)
return -ENOMEM;
if (!port->name) {
ret = -ENOMEM;
goto error;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
if (irq < 0) {
ret = irq;
goto error;
}
uport->irq = irq;
uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE);
@@ -1808,16 +1881,18 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
IRQF_TRIGGER_HIGH, port->name, uport);
if (ret) {
dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
return ret;
goto error;
}
ret = uart_get_rs485_mode(uport);
if (ret)
return ret;
goto error;
devm_pm_runtime_enable(port->se.dev);
ret = uart_add_one_port(drv, uport);
if (ret)
return ret;
goto error;
if (port->wakeup_irq > 0) {
device_init_wakeup(&pdev->dev, true);
@@ -1827,11 +1902,15 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, false);
ida_free(&port_ida, uport->line);
uart_remove_one_port(drv, uport);
return ret;
goto error;
}
}
return 0;
error:
dev_pm_domain_detach_list(port->pd_list);
return ret;
}
static void qcom_geni_serial_remove(struct platform_device *pdev)
@@ -1844,6 +1923,31 @@ static void qcom_geni_serial_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, false);
ida_free(&port_ida, uport->line);
uart_remove_one_port(drv, &port->uport);
dev_pm_domain_detach_list(port->pd_list);
}
static int __maybe_unused qcom_geni_serial_runtime_suspend(struct device *dev)
{
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport;
int ret = 0;
if (port->dev_data->power_state)
ret = port->dev_data->power_state(uport, false);
return ret;
}
static int __maybe_unused qcom_geni_serial_runtime_resume(struct device *dev)
{
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport;
int ret = 0;
if (port->dev_data->power_state)
ret = port->dev_data->power_state(uport, true);
return ret;
}
static int qcom_geni_serial_suspend(struct device *dev)
@@ -1881,14 +1985,46 @@ static int qcom_geni_serial_resume(struct device *dev)
static const struct qcom_geni_device_data qcom_geni_console_data = {
.console = true,
.mode = GENI_SE_FIFO,
.resources_init = geni_serial_resource_init,
.set_rate = geni_serial_set_rate,
.power_state = geni_serial_resource_state,
};
static const struct qcom_geni_device_data qcom_geni_uart_data = {
.console = false,
.mode = GENI_SE_DMA,
.resources_init = geni_serial_resource_init,
.set_rate = geni_serial_set_rate,
.power_state = geni_serial_resource_state,
};
static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
.console = true,
.mode = GENI_SE_FIFO,
.pd_data = {
.pd_flags = PD_FLAG_DEV_LINK_ON,
.pd_names = (const char*[]) { "power", "perf" },
.num_pd_names = 2,
},
.resources_init = geni_serial_pwr_init,
.set_rate = geni_serial_set_level,
};
static const struct qcom_geni_device_data sa8255p_qcom_geni_uart_data = {
.console = false,
.mode = GENI_SE_DMA,
.pd_data = {
.pd_flags = PD_FLAG_DEV_LINK_ON,
.pd_names = (const char*[]) { "power", "perf" },
.num_pd_names = 2,
},
.resources_init = geni_serial_pwr_init,
.set_rate = geni_serial_set_level,
};
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
SET_RUNTIME_PM_OPS(qcom_geni_serial_runtime_suspend,
qcom_geni_serial_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_suspend, qcom_geni_serial_resume)
};
@@ -1897,10 +2033,18 @@ static const struct of_device_id qcom_geni_serial_match_table[] = {
.compatible = "qcom,geni-debug-uart",
.data = &qcom_geni_console_data,
},
{
.compatible = "qcom,sa8255p-geni-debug-uart",
.data = &sa8255p_qcom_geni_console_data,
},
{
.compatible = "qcom,geni-uart",
.data = &qcom_geni_uart_data,
},
{
.compatible = "qcom,sa8255p-geni-uart",
.data = &sa8255p_qcom_geni_uart_data,
},
{}
};
MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);

View File

@@ -2830,6 +2830,8 @@ OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart",
s5pv210_early_console_setup);
OF_EARLYCON_DECLARE(artpec8, "axis,artpec8-uart",
s5pv210_early_console_setup);
OF_EARLYCON_DECLARE(exynos850, "samsung,exynos850-uart",
s5pv210_early_console_setup);
static int __init gs101_early_console_setup(struct earlycon_device *device,
const char *opt)

View File

@@ -11,6 +11,7 @@
#define DEFAULT_SYMBOL_NAMESPACE "SERIAL_NXP_SC16IS7XX"
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -49,18 +50,10 @@
#define SC16IS7XX_SPR_REG (0x07) /* Scratch Pad */
#define SC16IS7XX_TXLVL_REG (0x08) /* TX FIFO level */
#define SC16IS7XX_RXLVL_REG (0x09) /* RX FIFO level */
#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction
* - only on 75x/76x
*/
#define SC16IS7XX_IOSTATE_REG (0x0b) /* I/O State
* - only on 75x/76x
*/
#define SC16IS7XX_IOINTENA_REG (0x0c) /* I/O Interrupt Enable
* - only on 75x/76x
*/
#define SC16IS7XX_IOCONTROL_REG (0x0e) /* I/O Control
* - only on 75x/76x
*/
#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction - only on 75x/76x */
#define SC16IS7XX_IOSTATE_REG (0x0b) /* I/O State - only on 75x/76x */
#define SC16IS7XX_IOINTENA_REG (0x0c) /* I/O Interrupt Enable - only on 75x/76x */
#define SC16IS7XX_IOCONTROL_REG (0x0e) /* I/O Control - only on 75x/76x */
#define SC16IS7XX_EFCR_REG (0x0f) /* Extra Features Control */
/* TCR/TLR Register set: Only if ((MCR[2] == 1) && (EFR[4] == 1)) */
@@ -80,12 +73,9 @@
/* IER register bits */
#define SC16IS7XX_IER_RDI_BIT BIT(0) /* Enable RX data interrupt */
#define SC16IS7XX_IER_THRI_BIT BIT(1) /* Enable TX holding register
* interrupt */
#define SC16IS7XX_IER_RLSI_BIT BIT(2) /* Enable RX line status
* interrupt */
#define SC16IS7XX_IER_MSI_BIT BIT(3) /* Enable Modem status
* interrupt */
#define SC16IS7XX_IER_THRI_BIT BIT(1) /* Enable TX holding register interrupt */
#define SC16IS7XX_IER_RLSI_BIT BIT(2) /* Enable RX line status interrupt */
#define SC16IS7XX_IER_MSI_BIT BIT(3) /* Enable Modem status interrupt */
/* IER register bits - write only if (EFR[4] == 1) */
#define SC16IS7XX_IER_SLEEP_BIT BIT(4) /* Enable Sleep mode */
@@ -118,9 +108,8 @@
* - only on 75x/76x
*/
#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */
#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state
* from active (LOW)
* to inactive (HIGH)
#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state from active
* (LOW) to inactive (HIGH)
*/
/* LCR register bits */
#define SC16IS7XX_LCR_LENGTH0_BIT BIT(0) /* Word length bit 0 */
@@ -136,8 +125,7 @@
*
* STOP length bit table:
* 0 -> 1 stop bit
* 1 -> 1-1.5 stop bits if
* word length is 5,
* 1 -> 1-1.5 stop bits if word length is 5,
* 2 stop bits otherwise
*/
#define SC16IS7XX_LCR_PARITY_BIT BIT(3) /* Parity bit enable */
@@ -149,29 +137,22 @@
#define SC16IS7XX_LCR_WORD_LEN_6 (0x01)
#define SC16IS7XX_LCR_WORD_LEN_7 (0x02)
#define SC16IS7XX_LCR_WORD_LEN_8 (0x03)
#define SC16IS7XX_LCR_CONF_MODE_A SC16IS7XX_LCR_DLAB_BIT /* Special
* reg set */
#define SC16IS7XX_LCR_CONF_MODE_B 0xBF /* Enhanced
* reg set */
#define SC16IS7XX_LCR_REG_SET_SPECIAL SC16IS7XX_LCR_DLAB_BIT /* Special reg set */
#define SC16IS7XX_LCR_REG_SET_ENHANCED 0xBF /* Enhanced reg set */
/* MCR register bits */
#define SC16IS7XX_MCR_DTR_BIT BIT(0) /* DTR complement
* - only on 75x/76x
*/
#define SC16IS7XX_MCR_DTR_BIT BIT(0) /* DTR complement - only on 75x/76x */
#define SC16IS7XX_MCR_RTS_BIT BIT(1) /* RTS complement */
#define SC16IS7XX_MCR_TCRTLR_BIT BIT(2) /* TCR/TLR register enable */
#define SC16IS7XX_MCR_TCRTLR_BIT BIT(2) /* TCR/TLR registers enable */
#define SC16IS7XX_MCR_LOOP_BIT BIT(4) /* Enable loopback test mode */
#define SC16IS7XX_MCR_XONANY_BIT BIT(5) /* Enable Xon Any
* - write enabled
* if (EFR[4] == 1)
* - write enabled if (EFR[4] == 1)
*/
#define SC16IS7XX_MCR_IRDA_BIT BIT(6) /* Enable IrDA mode
* - write enabled
* if (EFR[4] == 1)
* - write enabled if (EFR[4] == 1)
*/
#define SC16IS7XX_MCR_CLKSEL_BIT BIT(7) /* Divide clock by 4
* - write enabled
* if (EFR[4] == 1)
* - write enabled if (EFR[4] == 1)
*/
/* LSR register bits */
@@ -192,28 +173,19 @@
/* MSR register bits */
#define SC16IS7XX_MSR_DCTS_BIT BIT(0) /* Delta CTS Clear To Send */
#define SC16IS7XX_MSR_DDSR_BIT BIT(1) /* Delta DSR Data Set Ready
* or (IO4)
#define SC16IS7XX_MSR_DDSR_BIT BIT(1) /* Delta DSR Data Set Ready or (IO4)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_DRI_BIT BIT(2) /* Delta RI Ring Indicator
* or (IO7)
#define SC16IS7XX_MSR_DRI_BIT BIT(2) /* Delta RI Ring Indicator or (IO7)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_DCD_BIT BIT(3) /* Delta CD Carrier Detect
* or (IO6)
#define SC16IS7XX_MSR_DCD_BIT BIT(3) /* Delta CD Carrier Detect or (IO6)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_CTS_BIT BIT(4) /* CTS */
#define SC16IS7XX_MSR_DSR_BIT BIT(5) /* DSR (IO4)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_RI_BIT BIT(6) /* RI (IO7)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_CD_BIT BIT(7) /* CD (IO6)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_DSR_BIT BIT(5) /* DSR (IO4) - only on 75x/76x */
#define SC16IS7XX_MSR_RI_BIT BIT(6) /* RI (IO7) - only on 75x/76x */
#define SC16IS7XX_MSR_CD_BIT BIT(7) /* CD (IO6) - only on 75x/76x */
/*
* TCR register bits
@@ -252,54 +224,42 @@
#define SC16IS7XX_IOCONTROL_SRESET_BIT BIT(3) /* Software Reset */
/* EFCR register bits */
#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0) /* Enable 9-bit or Multidrop
* mode (RS485) */
#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0) /* Enable 9-bit or Multidrop mode (RS485) */
#define SC16IS7XX_EFCR_RXDISABLE_BIT BIT(1) /* Disable receiver */
#define SC16IS7XX_EFCR_TXDISABLE_BIT BIT(2) /* Disable transmitter */
#define SC16IS7XX_EFCR_AUTO_RS485_BIT BIT(4) /* Auto RS485 RTS direction */
#define SC16IS7XX_EFCR_RTS_INVERT_BIT BIT(5) /* RTS output inversion */
#define SC16IS7XX_EFCR_IRDA_MODE_BIT BIT(7) /* IrDA mode
* 0 = rate upto 115.2 kbit/s
* - Only 75x/76x
* 1 = rate upto 1.152 Mbit/s
* - Only 76x
* 0 = rate up to 115.2 kbit/s - Only 75x/76x
* 1 = rate up to 1.152 Mbit/s - Only 76x
*/
/* EFR register bits */
#define SC16IS7XX_EFR_AUTORTS_BIT BIT(6) /* Auto RTS flow ctrl enable */
#define SC16IS7XX_EFR_AUTOCTS_BIT BIT(7) /* Auto CTS flow ctrl enable */
#define SC16IS7XX_EFR_XOFF2_DETECT_BIT BIT(5) /* Enable Xoff2 detection */
#define SC16IS7XX_EFR_ENABLE_BIT BIT(4) /* Enable enhanced functions
* and writing to IER[7:4],
* FCR[5:4], MCR[7:5]
#define SC16IS7XX_EFR_ENABLE_BIT BIT(4) /* Enable enhanced functions and writing to
* IER[7:4], FCR[5:4], MCR[7:5]
*/
#define SC16IS7XX_EFR_SWFLOW3_BIT BIT(3)
#define SC16IS7XX_EFR_SWFLOW2_BIT BIT(2)
/*
* SWFLOW bits 3 & 2 table:
* 00 -> no transmitter flow
* control
* 01 -> transmitter generates
* XON2 and XOFF2
* 10 -> transmitter generates
* XON1 and XOFF1
* 11 -> transmitter generates
* XON1, XON2, XOFF1 and
* XOFF2
* 00 -> no transmitter flow control
* 01 -> transmitter generates XON2 and XOFF2
* 10 -> transmitter generates XON1 and XOFF1
* 11 -> transmitter generates XON1, XON2,
* XOFF1 and XOFF2
*/
#define SC16IS7XX_EFR_SWFLOW1_BIT BIT(1)
#define SC16IS7XX_EFR_SWFLOW0_BIT BIT(0)
/*
* SWFLOW bits 1 & 0 table:
* 00 -> no received flow
* control
* 01 -> receiver compares
* XON2 and XOFF2
* 10 -> receiver compares
* XON1 and XOFF1
* 11 -> receiver compares
* XON1, XON2, XOFF1 and
* XOFF2
* 00 -> no received flow control
* 01 -> receiver compares XON2 and XOFF2
* 10 -> receiver compares XON1 and XOFF1
* 11 -> receiver compares XON1, XON2,
* XOFF1 and XOFF2
*/
#define SC16IS7XX_EFR_FLOWCTRL_BITS (SC16IS7XX_EFR_AUTORTS_BIT | \
SC16IS7XX_EFR_AUTOCTS_BIT | \
@@ -328,7 +288,7 @@ struct sc16is7xx_one_config {
struct sc16is7xx_one {
struct uart_port port;
struct regmap *regmap;
struct mutex efr_lock; /* EFR registers access */
struct mutex lock; /* For registers sharing same address space. */
struct kthread_work tx_work;
struct kthread_work reg_work;
struct kthread_delayed_work ms_work;
@@ -358,16 +318,16 @@ static DEFINE_IDA(sc16is7xx_lines);
static struct uart_driver sc16is7xx_uart = {
.owner = THIS_MODULE,
.driver_name = SC16IS7XX_NAME,
.driver_name = KBUILD_MODNAME,
.dev_name = "ttySC",
.nr = SC16IS7XX_MAX_DEVS,
};
#define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e)))
#define to_sc16is7xx_one(p) container_of((p), struct sc16is7xx_one, port)
static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
unsigned int val = 0;
regmap_read(one->regmap, reg, &val);
@@ -377,21 +337,21 @@ static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg)
static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
regmap_write(one->regmap, reg, val);
}
static void sc16is7xx_fifo_read(struct uart_port *port, u8 *rxbuf, unsigned int rxlen)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, rxbuf, rxlen);
}
static void sc16is7xx_fifo_write(struct uart_port *port, u8 *txbuf, u8 to_send)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
/*
* Don't send zero-length data, at least on SPI it confuses the chip
@@ -406,7 +366,7 @@ static void sc16is7xx_fifo_write(struct uart_port *port, u8 *txbuf, u8 to_send)
static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
u8 mask, u8 val)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
regmap_update_bits(one->regmap, reg, mask, val);
}
@@ -419,52 +379,56 @@ static void sc16is7xx_power(struct uart_port *port, int on)
}
/*
* In an amazing feat of design, the Enhanced Features Register (EFR)
* shares the address of the Interrupt Identification Register (IIR).
* Access to EFR is switched on by writing a magic value (0xbf) to the
* Line Control Register (LCR). Any interrupt firing during this time will
* see the EFR where it expects the IIR to be, leading to
* In an amazing feat of design, the enhanced register set shares the
* addresses 0x02 and 0x04-0x07 with the general register set.
* The special register set also shares the addresses 0x00-0x01 with the
* general register set.
*
* Access to the enhanced or special register set is enabled by writing a magic
* value to the Line Control Register (LCR). When enhanced register set access
* is enabled, for example, any interrupt firing during this time will see the
* EFR where it expects the IIR to be, leading to
* "Unexpected interrupt" messages.
*
* Prevent this possibility by claiming a mutex while accessing the EFR,
* and claiming the same mutex from within the interrupt handler. This is
* similar to disabling the interrupt, but that doesn't work because the
* bulk of the interrupt processing is run as a workqueue job in thread
* context.
* Prevent this possibility by claiming a mutex when access to the enhanced
* or special register set is enabled, and claiming the same mutex from within
* the interrupt handler. This is similar to disabling the interrupt, but that
* doesn't work because the bulk of the interrupt processing is run as a
* workqueue job in thread context.
*/
static void sc16is7xx_efr_lock(struct uart_port *port)
static void sc16is7xx_regs_lock(struct uart_port *port, u8 register_set)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
mutex_lock(&one->efr_lock);
mutex_lock(&one->lock);
/* Backup content of LCR. */
one->old_lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
/* Enable access to Enhanced register set */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B);
/* Enable access to the desired register set */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, register_set);
/* Disable cache updates when writing to EFR registers */
/* Disable cache updates when writing to non-general registers */
regcache_cache_bypass(one->regmap, true);
}
static void sc16is7xx_efr_unlock(struct uart_port *port)
static void sc16is7xx_regs_unlock(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
/* Re-enable cache updates when writing to normal registers */
/* Re-enable cache updates when writing to general registers */
regcache_cache_bypass(one->regmap, false);
/* Restore original content of LCR */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, one->old_lcr);
mutex_unlock(&one->efr_lock);
mutex_unlock(&one->lock);
}
static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
lockdep_assert_held_once(&port->lock);
@@ -477,7 +441,7 @@ static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
static void sc16is7xx_ier_set(struct uart_port *port, u8 bit)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
lockdep_assert_held_once(&port->lock);
@@ -535,10 +499,11 @@ EXPORT_SYMBOL_GPL(sc16is762_devtype);
static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SC16IS7XX_RHR_REG:
case SC16IS7XX_IIR_REG:
case SC16IS7XX_LSR_REG:
case SC16IS7XX_MSR_REG:
case SC16IS7XX_RHR_REG: /* Shared address space with THR & DLL */
case SC16IS7XX_IIR_REG: /* Shared address space with FCR & EFR */
case SC16IS7XX_LSR_REG: /* Shared address space with XON2 */
case SC16IS7XX_MSR_REG: /* Shared address space with TCR & XOFF1 */
case SC16IS7XX_SPR_REG: /* Shared address space with TLR & XOFF2 */
case SC16IS7XX_TXLVL_REG:
case SC16IS7XX_RXLVL_REG:
case SC16IS7XX_IOSTATE_REG:
@@ -578,8 +543,6 @@ static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg)
*/
static int sc16is7xx_set_baud(struct uart_port *port, int baud)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
u8 lcr;
unsigned int prescaler = 1;
unsigned long clk = port->uartclk, div = clk / 16 / baud;
@@ -593,23 +556,15 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
SC16IS7XX_MCR_CLKSEL_BIT,
prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT);
mutex_lock(&one->efr_lock);
/* Backup LCR and access special register set (DLL/DLH) */
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
SC16IS7XX_LCR_CONF_MODE_A);
/* Access special register set (DLL/DLH) */
sc16is7xx_regs_lock(port, SC16IS7XX_LCR_REG_SET_SPECIAL);
/* Write the new divisor */
regcache_cache_bypass(one->regmap, true);
sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256);
sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256);
regcache_cache_bypass(one->regmap, false);
/* Restore LCR and access to general register set */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
mutex_unlock(&one->efr_lock);
/* Restore access to general register set */
sc16is7xx_regs_unlock(port);
return DIV_ROUND_CLOSEST((clk / prescaler) / 16, div);
}
@@ -617,7 +572,7 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
unsigned int iir)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
unsigned int lsr = 0, bytes_read, i;
bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC);
u8 ch, flag;
@@ -756,7 +711,8 @@ static void sc16is7xx_update_mlines(struct sc16is7xx_one *one)
unsigned long flags;
unsigned int status, changed;
lockdep_assert_held_once(&one->efr_lock);
/* Lock required as MSR address is shared with TCR and XOFF1. */
lockdep_assert_held_once(&one->lock);
status = sc16is7xx_get_hwmctrl(port);
changed = status ^ one->old_mctrl;
@@ -782,18 +738,15 @@ static void sc16is7xx_update_mlines(struct sc16is7xx_one *one)
static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
{
bool rc = true;
unsigned int iir, rxlen;
struct uart_port *port = &s->p[portno].port;
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
mutex_lock(&one->efr_lock);
guard(mutex)(&one->lock);
iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG);
if (iir & SC16IS7XX_IIR_NO_INT_BIT) {
rc = false;
goto out_port_irq;
}
if (iir & SC16IS7XX_IIR_NO_INT_BIT)
return false;
iir &= SC16IS7XX_IIR_ID_MASK;
@@ -833,18 +786,14 @@ static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
break;
}
out_port_irq:
mutex_unlock(&one->efr_lock);
return rc;
return true;
}
static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
{
struct sc16is7xx_port *s = dev_id;
bool keep_polling;
struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
do {
int i;
@@ -871,16 +820,15 @@ static void sc16is7xx_poll_proc(struct kthread_work *ws)
static void sc16is7xx_tx_proc(struct kthread_work *ws)
{
struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = container_of(ws, struct sc16is7xx_one, tx_work);
struct uart_port *port = &one->port;
if ((port->rs485.flags & SER_RS485_ENABLED) &&
(port->rs485.delay_rts_before_send > 0))
msleep(port->rs485.delay_rts_before_send);
mutex_lock(&one->efr_lock);
guard(mutex)(&one->lock);
sc16is7xx_handle_tx(port);
mutex_unlock(&one->efr_lock);
}
static void sc16is7xx_reconf_rs485(struct uart_port *port)
@@ -905,7 +853,7 @@ static void sc16is7xx_reconf_rs485(struct uart_port *port)
static void sc16is7xx_reg_proc(struct kthread_work *ws)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(ws, reg_work);
struct sc16is7xx_one *one = container_of(ws, struct sc16is7xx_one, reg_work);
struct sc16is7xx_one_config config;
unsigned long irqflags;
@@ -943,13 +891,12 @@ static void sc16is7xx_reg_proc(struct kthread_work *ws)
static void sc16is7xx_ms_proc(struct kthread_work *ws)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(ws, ms_work.work);
struct sc16is7xx_one *one = container_of(ws, struct sc16is7xx_one, ms_work.work);
struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev);
if (one->port.state) {
mutex_lock(&one->efr_lock);
sc16is7xx_update_mlines(one);
mutex_unlock(&one->efr_lock);
scoped_guard(mutex, &one->lock)
sc16is7xx_update_mlines(one);
kthread_queue_delayed_work(&s->kworker, &one->ms_work, HZ);
}
@@ -957,7 +904,7 @@ static void sc16is7xx_ms_proc(struct kthread_work *ws)
static void sc16is7xx_enable_ms(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
lockdep_assert_held_once(&port->lock);
@@ -968,7 +915,7 @@ static void sc16is7xx_enable_ms(struct uart_port *port)
static void sc16is7xx_start_tx(struct uart_port *port)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
kthread_queue_work(&s->kworker, &one->tx_work);
}
@@ -1007,7 +954,7 @@ static unsigned int sc16is7xx_tx_empty(struct uart_port *port)
static unsigned int sc16is7xx_get_mctrl(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
/* Called with port lock taken so we can only return cached value */
return one->old_mctrl;
@@ -1016,7 +963,7 @@ static unsigned int sc16is7xx_get_mctrl(struct uart_port *port)
static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
one->config.flags |= SC16IS7XX_RECONF_MD;
kthread_queue_work(&s->kworker, &one->reg_work);
@@ -1033,7 +980,7 @@ static void sc16is7xx_set_termios(struct uart_port *port,
struct ktermios *termios,
const struct ktermios *old)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
unsigned int lcr, flow = 0;
int baud;
unsigned long flags;
@@ -1106,12 +1053,12 @@ static void sc16is7xx_set_termios(struct uart_port *port,
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
/* Update EFR registers */
sc16is7xx_efr_lock(port);
sc16is7xx_regs_lock(port, SC16IS7XX_LCR_REG_SET_ENHANCED);
sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
SC16IS7XX_EFR_FLOWCTRL_BITS, flow);
sc16is7xx_efr_unlock(port);
sc16is7xx_regs_unlock(port);
/* Get baud rate generator configuration */
baud = uart_get_baud_rate(port, termios, old,
@@ -1136,7 +1083,7 @@ static int sc16is7xx_config_rs485(struct uart_port *port, struct ktermios *termi
struct serial_rs485 *rs485)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
if (rs485->flags & SER_RS485_ENABLED) {
/*
@@ -1156,14 +1103,14 @@ static int sc16is7xx_config_rs485(struct uart_port *port, struct ktermios *termi
static int sc16is7xx_startup(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
unsigned int val;
unsigned long flags;
sc16is7xx_power(port, 1);
/* Reset FIFOs*/
/* Reset FIFOs */
val = SC16IS7XX_FCR_RXRESET_BIT | SC16IS7XX_FCR_TXRESET_BIT;
sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, val);
udelay(5);
@@ -1191,8 +1138,7 @@ static int sc16is7xx_startup(struct uart_port *port)
/* This bit must be written with LCR[7] = 0 */
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
SC16IS7XX_MCR_IRDA_BIT,
one->irda_mode ?
SC16IS7XX_MCR_IRDA_BIT : 0);
one->irda_mode ? SC16IS7XX_MCR_IRDA_BIT : 0);
/* Enable the Rx and Tx FIFO */
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
@@ -1220,7 +1166,7 @@ static int sc16is7xx_startup(struct uart_port *port)
static void sc16is7xx_shutdown(struct uart_port *port)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port);
kthread_cancel_delayed_work_sync(&one->ms_work);
@@ -1510,6 +1456,75 @@ static int sc16is7xx_reset(struct device *dev, struct regmap *regmap)
return 0;
}
static int sc16is7xx_setup_channel(struct sc16is7xx_one *one, int i,
bool *port_registered)
{
struct uart_port *port = &one->port;
int ret;
ret = ida_alloc_max(&sc16is7xx_lines, SC16IS7XX_MAX_DEVS - 1, GFP_KERNEL);
if (ret < 0)
return ret;
port->line = ret;
/* Initialize port data */
port->type = PORT_SC16IS7XX;
port->fifosize = SC16IS7XX_FIFO_SIZE;
port->flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY;
port->iobase = i;
/*
* Use all ones as membase to make sure uart_configure_port() in
* serial_core.c does not abort for SPI/I2C devices where the
* membase address is not applicable.
*/
port->membase = (void __iomem *)~0;
port->iotype = UPIO_PORT;
port->rs485_config = sc16is7xx_config_rs485;
port->rs485_supported = sc16is7xx_rs485_supported;
port->ops = &sc16is7xx_ops;
one->old_mctrl = 0;
mutex_init(&one->lock);
ret = uart_get_rs485_mode(port);
if (ret)
return ret;
/* Enable access to general register set */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, 0x00);
/* Disable all interrupts */
sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0);
/* Disable TX/RX */
sc16is7xx_port_write(port, SC16IS7XX_EFCR_REG,
SC16IS7XX_EFCR_RXDISABLE_BIT |
SC16IS7XX_EFCR_TXDISABLE_BIT);
/* Initialize kthread work structs */
kthread_init_work(&one->tx_work, sc16is7xx_tx_proc);
kthread_init_work(&one->reg_work, sc16is7xx_reg_proc);
kthread_init_delayed_work(&one->ms_work, sc16is7xx_ms_proc);
/* Register port */
ret = uart_add_one_port(&sc16is7xx_uart, port);
if (ret)
return ret;
*port_registered = true;
sc16is7xx_regs_lock(port, SC16IS7XX_LCR_REG_SET_ENHANCED);
/* Enable write access to enhanced features */
sc16is7xx_port_write(port, SC16IS7XX_EFR_REG,
SC16IS7XX_EFR_ENABLE_BIT);
sc16is7xx_regs_unlock(port);
/* Go to suspend mode */
sc16is7xx_power(port, 0);
return 0;
}
int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype,
struct regmap *regmaps[], int irq)
{
@@ -1539,10 +1554,8 @@ int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype,
/* Alloc port structure */
s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL);
if (!s) {
dev_err(dev, "Error allocating port structure\n");
if (!s)
return -ENOMEM;
}
/* Always ask for fixed clock rate from a property. */
device_property_read_u32(dev, "clock-frequency", &uartclk);
@@ -1595,76 +1608,14 @@ int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype,
}
for (i = 0; i < devtype->nr_uart; ++i) {
ret = ida_alloc_max(&sc16is7xx_lines,
SC16IS7XX_MAX_DEVS - 1, GFP_KERNEL);
if (ret < 0)
goto out_ports;
s->p[i].port.line = ret;
/* Initialize port data */
s->p[i].port.dev = dev;
s->p[i].port.irq = irq;
s->p[i].port.type = PORT_SC16IS7XX;
s->p[i].port.fifosize = SC16IS7XX_FIFO_SIZE;
s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY;
s->p[i].port.iobase = i;
/*
* Use all ones as membase to make sure uart_configure_port() in
* serial_core.c does not abort for SPI/I2C devices where the
* membase address is not applicable.
*/
s->p[i].port.membase = (void __iomem *)~0;
s->p[i].port.iotype = UPIO_PORT;
s->p[i].port.uartclk = freq;
s->p[i].port.rs485_config = sc16is7xx_config_rs485;
s->p[i].port.rs485_supported = sc16is7xx_rs485_supported;
s->p[i].port.ops = &sc16is7xx_ops;
s->p[i].old_mctrl = 0;
s->p[i].regmap = regmaps[i];
mutex_init(&s->p[i].efr_lock);
ret = uart_get_rs485_mode(&s->p[i].port);
ret = sc16is7xx_setup_channel(&s->p[i], i, &port_registered[i]);
if (ret)
goto out_ports;
/* Disable all interrupts */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_IER_REG, 0);
/* Disable TX/RX */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG,
SC16IS7XX_EFCR_RXDISABLE_BIT |
SC16IS7XX_EFCR_TXDISABLE_BIT);
/* Initialize kthread work structs */
kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
kthread_init_delayed_work(&s->p[i].ms_work, sc16is7xx_ms_proc);
/* Register port */
ret = uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
if (ret)
goto out_ports;
port_registered[i] = true;
/* Enable EFR */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG,
SC16IS7XX_LCR_CONF_MODE_B);
regcache_cache_bypass(regmaps[i], true);
/* Enable write access to enhanced features */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFR_REG,
SC16IS7XX_EFR_ENABLE_BIT);
regcache_cache_bypass(regmaps[i], false);
/* Restore access to general registers */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, 0x00);
/* Go to suspend mode */
sc16is7xx_power(&s->p[i].port, 0);
}
sc16is7xx_setup_irda_ports(s);
@@ -1814,4 +1765,4 @@ module_exit(sc16is7xx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>");
MODULE_DESCRIPTION("SC16IS7xx tty serial core driver");
MODULE_DESCRIPTION(KBUILD_MODNAME " tty serial core driver");

View File

@@ -8,7 +8,6 @@
#include <linux/regmap.h>
#include <linux/types.h>
#define SC16IS7XX_NAME "sc16is7xx"
#define SC16IS7XX_MAX_PORTS 2 /* Maximum number of UART ports per IC. */
struct device;

View File

@@ -52,7 +52,7 @@ MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table);
static struct i2c_driver sc16is7xx_i2c_driver = {
.driver = {
.name = SC16IS7XX_NAME,
.name = KBUILD_MODNAME,
.of_match_table = sc16is7xx_dt_ids,
},
.probe = sc16is7xx_i2c_probe,
@@ -63,5 +63,5 @@ static struct i2c_driver sc16is7xx_i2c_driver = {
module_i2c_driver(sc16is7xx_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SC16IS7xx I2C interface driver");
MODULE_DESCRIPTION(KBUILD_MODNAME " interface driver");
MODULE_IMPORT_NS("SERIAL_NXP_SC16IS7XX");

View File

@@ -75,7 +75,7 @@ MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table);
static struct spi_driver sc16is7xx_spi_driver = {
.driver = {
.name = SC16IS7XX_NAME,
.name = KBUILD_MODNAME,
.of_match_table = sc16is7xx_dt_ids,
},
.probe = sc16is7xx_spi_probe,
@@ -86,5 +86,5 @@ static struct spi_driver sc16is7xx_spi_driver = {
module_spi_driver(sc16is7xx_spi_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SC16IS7xx SPI interface driver");
MODULE_DESCRIPTION(KBUILD_MODNAME " interface driver");
MODULE_IMPORT_NS("SERIAL_NXP_SC16IS7XX");

View File

@@ -1034,9 +1034,8 @@ static int uart_set_info_user(struct tty_struct *tty, struct serial_struct *ss)
{
struct uart_state *state = tty->driver_data;
struct tty_port *port = &state->port;
int retval;
down_write(&tty->termios_rwsem);
guard(rwsem_write)(&tty->termios_rwsem);
/*
* This semaphore protects port->count. It is also
* very useful to prevent opens. Also, take the
@@ -1044,11 +1043,8 @@ static int uart_set_info_user(struct tty_struct *tty, struct serial_struct *ss)
* module insertion/removal doesn't change anything
* under us.
*/
mutex_lock(&port->mutex);
retval = uart_set_info(tty, port, state, ss);
mutex_unlock(&port->mutex);
up_write(&tty->termios_rwsem);
return retval;
guard(mutex)(&port->mutex);
return uart_set_info(tty, port, state, ss);
}
/**
@@ -1560,85 +1556,66 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
void __user *uarg = (void __user *)arg;
int ret = -ENOIOCTLCMD;
/*
* These ioctls don't rely on the hardware to be present.
*/
switch (cmd) {
case TIOCSERCONFIG:
down_write(&tty->termios_rwsem);
ret = uart_do_autoconfig(tty, state);
up_write(&tty->termios_rwsem);
break;
/* This ioctl doesn't rely on the hardware to be present. */
if (cmd == TIOCSERCONFIG) {
guard(rwsem_write)(&tty->termios_rwsem);
return uart_do_autoconfig(tty, state);
}
if (ret != -ENOIOCTLCMD)
goto out;
if (tty_io_error(tty))
return -EIO;
if (tty_io_error(tty)) {
ret = -EIO;
goto out;
}
/*
* The following should only be used when hardware is present.
*/
switch (cmd) {
case TIOCMIWAIT:
ret = uart_wait_modem_status(state, arg);
break;
}
if (ret != -ENOIOCTLCMD)
goto out;
/* This should only be used when the hardware is present. */
if (cmd == TIOCMIWAIT)
return uart_wait_modem_status(state, arg);
/* rs485_config requires more locking than others */
if (cmd == TIOCSRS485)
down_write(&tty->termios_rwsem);
mutex_lock(&port->mutex);
uport = uart_port_check(state);
scoped_guard(mutex, &port->mutex) {
uport = uart_port_check(state);
if (!uport || tty_io_error(tty)) {
ret = -EIO;
goto out_up;
if (!uport || tty_io_error(tty)) {
ret = -EIO;
break;
}
/*
* All these rely on hardware being present and need to be
* protected against the tty being hung up.
*/
switch (cmd) {
case TIOCSERGETLSR: /* Get line status register */
ret = uart_get_lsr_info(tty, state, uarg);
break;
case TIOCGRS485:
ret = uart_get_rs485_config(uport, uarg);
break;
case TIOCSRS485:
ret = uart_set_rs485_config(tty, uport, uarg);
break;
case TIOCSISO7816:
ret = uart_set_iso7816_config(state->uart_port, uarg);
break;
case TIOCGISO7816:
ret = uart_get_iso7816_config(state->uart_port, uarg);
break;
default:
if (uport->ops->ioctl)
ret = uport->ops->ioctl(uport, cmd, arg);
break;
}
}
/*
* All these rely on hardware being present and need to be
* protected against the tty being hung up.
*/
switch (cmd) {
case TIOCSERGETLSR: /* Get line status register */
ret = uart_get_lsr_info(tty, state, uarg);
break;
case TIOCGRS485:
ret = uart_get_rs485_config(uport, uarg);
break;
case TIOCSRS485:
ret = uart_set_rs485_config(tty, uport, uarg);
break;
case TIOCSISO7816:
ret = uart_set_iso7816_config(state->uart_port, uarg);
break;
case TIOCGISO7816:
ret = uart_get_iso7816_config(state->uart_port, uarg);
break;
default:
if (uport->ops->ioctl)
ret = uport->ops->ioctl(uport, cmd, arg);
break;
}
out_up:
mutex_unlock(&port->mutex);
if (cmd == TIOCSRS485)
up_write(&tty->termios_rwsem);
out:
return ret;
}
@@ -1651,11 +1628,10 @@ static void uart_set_ldisc(struct tty_struct *tty)
if (!tty_port_initialized(port))
return;
mutex_lock(&state->port.mutex);
guard(mutex)(&state->port.mutex);
uport = uart_port_check(state);
if (uport && uport->ops->set_ldisc)
uport->ops->set_ldisc(uport, &tty->termios);
mutex_unlock(&state->port.mutex);
}
static void uart_set_termios(struct tty_struct *tty,
@@ -1729,9 +1705,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
state = drv->state + tty->index;
port = &state->port;
spin_lock_irq(&port->lock);
guard(spinlock_irq)(&port->lock);
--port->count;
spin_unlock_irq(&port->lock);
return;
}
@@ -1843,20 +1818,18 @@ static void uart_hangup(struct tty_struct *tty)
struct uart_state *state = tty->driver_data;
struct tty_port *port = &state->port;
struct uart_port *uport;
unsigned long flags;
pr_debug("uart_hangup(%d)\n", tty->index);
mutex_lock(&port->mutex);
guard(mutex)(&port->mutex);
uport = uart_port_check(state);
WARN(!uport, "hangup of detached port!\n");
if (tty_port_active(port)) {
uart_flush_buffer(tty);
uart_shutdown(tty, state);
spin_lock_irqsave(&port->lock, flags);
port->count = 0;
spin_unlock_irqrestore(&port->lock, flags);
scoped_guard(spinlock_irqsave, &port->lock)
port->count = 0;
tty_port_set_active(port, false);
tty_port_tty_set(port, NULL);
if (uport && !uart_console(uport))
@@ -1864,7 +1837,6 @@ static void uart_hangup(struct tty_struct *tty)
wake_up_interruptible(&port->open_wait);
wake_up_interruptible(&port->delta_msr_wait);
}
mutex_unlock(&port->mutex);
}
/* uport == NULL if uart_port has already been removed */
@@ -2969,11 +2941,11 @@ static ssize_t console_show(struct device *dev,
struct uart_port *uport;
bool console = false;
mutex_lock(&port->mutex);
uport = uart_port_check(state);
if (uport)
console = uart_console_registered(uport);
mutex_unlock(&port->mutex);
scoped_guard(mutex, &port->mutex) {
uport = uart_port_check(state);
if (uport)
console = uart_console_registered(uport);
}
return sprintf(buf, "%c\n", console ? 'Y' : 'N');
}
@@ -3158,17 +3130,14 @@ static void serial_core_remove_one_port(struct uart_driver *drv,
struct tty_port *port = &state->port;
struct uart_port *uart_port;
mutex_lock(&port->mutex);
uart_port = uart_port_check(state);
if (uart_port != uport)
dev_alert(uport->dev, "Removing wrong port: %p != %p\n",
uart_port, uport);
scoped_guard(mutex, &port->mutex) {
uart_port = uart_port_check(state);
if (uart_port != uport)
dev_alert(uport->dev, "Removing wrong port: %p != %p\n", uart_port, uport);
if (!uart_port) {
mutex_unlock(&port->mutex);
return;
if (!uart_port)
return;
}
mutex_unlock(&port->mutex);
/*
* Remove the devices from the tty layer
@@ -3197,11 +3166,10 @@ static void serial_core_remove_one_port(struct uart_driver *drv,
uport->type = PORT_UNKNOWN;
uport->port_dev = NULL;
mutex_lock(&port->mutex);
guard(mutex)(&port->mutex);
WARN_ON(atomic_dec_return(&state->refcount) < 0);
wait_event(state->remove_wait, !atomic_read(&state->refcount));
state->uart_port = NULL;
mutex_unlock(&port->mutex);
}
/**
@@ -3354,7 +3322,7 @@ void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port
struct serial_ctrl_device *ctrl_dev = serial_core_get_ctrl_dev(port_dev);
int ctrl_id = port->ctrl_id;
mutex_lock(&port_mutex);
guard(mutex)(&port_mutex);
port->flags |= UPF_DEAD;
@@ -3366,8 +3334,6 @@ void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port
/* Drop the serial core controller device if no ports are using it */
if (!serial_core_ctrl_find(drv, phys_dev, ctrl_id))
serial_base_ctrl_device_remove(ctrl_dev);
mutex_unlock(&port_mutex);
}
/**
@@ -3536,6 +3502,14 @@ int uart_get_rs485_mode(struct uart_port *port)
if (!(port->rs485_supported.flags & SER_RS485_ENABLED))
return 0;
/*
* Retrieve properties only if a firmware node exists. If no firmware
* node exists, then don't touch rs485 config and keep initial rs485
* properties set by driver.
*/
if (!dev_fwnode(dev))
return 0;
ret = device_property_read_u32_array(dev, "rs485-rts-delay",
rs485_delay, 2);
if (!ret) {

View File

@@ -17,29 +17,32 @@
*/
#undef DEBUG
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/ctype.h>
#include <linux/cpufreq.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/ktime.h>
#include <linux/major.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/scatterlist.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_sci.h>
#include <linux/sh_dma.h>
#include <linux/slab.h>
@@ -50,15 +53,186 @@
#include <linux/tty_flip.h>
#ifdef CONFIG_SUPERH
#include <asm/sh_bios.h>
#include <asm/platform_early.h>
#include <asm/sh_bios.h>
#endif
#include "rsci.h"
#include "serial_mctrl_gpio.h"
#include "sh-sci.h"
#include "sh-sci-common.h"
#define SCI_MAJOR 204
#define SCI_MINOR_START 8
/*
* SCI register subset common for all port types.
* Not all registers will exist on all parts.
*/
enum {
SCSMR, /* Serial Mode Register */
SCBRR, /* Bit Rate Register */
SCSCR, /* Serial Control Register */
SCxSR, /* Serial Status Register */
SCFCR, /* FIFO Control Register */
SCFDR, /* FIFO Data Count Register */
SCxTDR, /* Transmit (FIFO) Data Register */
SCxRDR, /* Receive (FIFO) Data Register */
SCLSR, /* Line Status Register */
SCTFDR, /* Transmit FIFO Data Count Register */
SCRFDR, /* Receive FIFO Data Count Register */
SCSPTR, /* Serial Port Register */
HSSRR, /* Sampling Rate Register */
SCPCR, /* Serial Port Control Register */
SCPDR, /* Serial Port Data Register */
SCDL, /* BRG Frequency Division Register */
SCCKS, /* BRG Clock Select Register */
HSRTRGR, /* Rx FIFO Data Count Trigger Register */
HSTTRGR, /* Tx FIFO Data Count Trigger Register */
SEMR, /* Serial extended mode register */
};
/* SCSMR (Serial Mode Register) */
#define SCSMR_C_A BIT(7) /* Communication Mode */
#define SCSMR_CSYNC BIT(7) /* - Clocked synchronous mode */
#define SCSMR_ASYNC 0 /* - Asynchronous mode */
#define SCSMR_CHR BIT(6) /* 7-bit Character Length */
#define SCSMR_PE BIT(5) /* Parity Enable */
#define SCSMR_ODD BIT(4) /* Odd Parity */
#define SCSMR_STOP BIT(3) /* Stop Bit Length */
#define SCSMR_CKS 0x0003 /* Clock Select */
/* Serial Mode Register, SCIFA/SCIFB only bits */
#define SCSMR_CKEDG BIT(12) /* Transmit/Receive Clock Edge Select */
#define SCSMR_SRC_MASK 0x0700 /* Sampling Control */
#define SCSMR_SRC_16 0x0000 /* Sampling rate 1/16 */
#define SCSMR_SRC_5 0x0100 /* Sampling rate 1/5 */
#define SCSMR_SRC_7 0x0200 /* Sampling rate 1/7 */
#define SCSMR_SRC_11 0x0300 /* Sampling rate 1/11 */
#define SCSMR_SRC_13 0x0400 /* Sampling rate 1/13 */
#define SCSMR_SRC_17 0x0500 /* Sampling rate 1/17 */
#define SCSMR_SRC_19 0x0600 /* Sampling rate 1/19 */
#define SCSMR_SRC_27 0x0700 /* Sampling rate 1/27 */
/* Serial Control Register, SCI only bits */
#define SCSCR_TEIE BIT(2) /* Transmit End Interrupt Enable */
/* Serial Control Register, SCIFA/SCIFB only bits */
#define SCSCR_TDRQE BIT(15) /* Tx Data Transfer Request Enable */
#define SCSCR_RDRQE BIT(14) /* Rx Data Transfer Request Enable */
/* Serial Control Register, HSCIF-only bits */
#define HSSCR_TOT_SHIFT 14
/* SCxSR (Serial Status Register) on SCI */
#define SCI_TDRE BIT(7) /* Transmit Data Register Empty */
#define SCI_RDRF BIT(6) /* Receive Data Register Full */
#define SCI_ORER BIT(5) /* Overrun Error */
#define SCI_FER BIT(4) /* Framing Error */
#define SCI_PER BIT(3) /* Parity Error */
#define SCI_TEND BIT(2) /* Transmit End */
#define SCI_RESERVED 0x03 /* All reserved bits */
#define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER)
#define SCI_RDxF_CLEAR (u32)(~(SCI_RESERVED | SCI_RDRF))
#define SCI_ERROR_CLEAR (u32)(~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER))
#define SCI_TDxE_CLEAR (u32)(~(SCI_RESERVED | SCI_TEND | SCI_TDRE))
#define SCI_BREAK_CLEAR (u32)(~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER))
/* SCxSR (Serial Status Register) on SCIF, SCIFA, SCIFB, HSCIF */
#define SCIF_ER BIT(7) /* Receive Error */
#define SCIF_TEND BIT(6) /* Transmission End */
#define SCIF_TDFE BIT(5) /* Transmit FIFO Data Empty */
#define SCIF_BRK BIT(4) /* Break Detect */
#define SCIF_FER BIT(3) /* Framing Error */
#define SCIF_PER BIT(2) /* Parity Error */
#define SCIF_RDF BIT(1) /* Receive FIFO Data Full */
#define SCIF_DR BIT(0) /* Receive Data Ready */
/* SCIF only (optional) */
#define SCIF_PERC 0xf000 /* Number of Parity Errors */
#define SCIF_FERC 0x0f00 /* Number of Framing Errors */
/*SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721 only */
#define SCIFA_ORER BIT(9) /* Overrun Error */
#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_BRK | SCIF_ER)
#define SCIF_RDxF_CLEAR (u32)(~(SCIF_DR | SCIF_RDF))
#define SCIF_ERROR_CLEAR (u32)(~(SCIF_PER | SCIF_FER | SCIF_ER))
#define SCIF_TDxE_CLEAR (u32)(~(SCIF_TDFE))
#define SCIF_BREAK_CLEAR (u32)(~(SCIF_PER | SCIF_FER | SCIF_BRK))
/* SCFCR (FIFO Control Register) */
#define SCFCR_RTRG1 BIT(7) /* Receive FIFO Data Count Trigger */
#define SCFCR_RTRG0 BIT(6)
#define SCFCR_TTRG1 BIT(5) /* Transmit FIFO Data Count Trigger */
#define SCFCR_TTRG0 BIT(4)
#define SCFCR_MCE BIT(3) /* Modem Control Enable */
#define SCFCR_TFRST BIT(2) /* Transmit FIFO Data Register Reset */
#define SCFCR_RFRST BIT(1) /* Receive FIFO Data Register Reset */
#define SCFCR_LOOP BIT(0) /* Loopback Test */
/* SCLSR (Line Status Register) on (H)SCIF */
#define SCLSR_TO BIT(2) /* Timeout */
#define SCLSR_ORER BIT(0) /* Overrun Error */
/* SCSPTR (Serial Port Register), optional */
#define SCSPTR_RTSIO BIT(7) /* Serial Port RTS# Pin Input/Output */
#define SCSPTR_RTSDT BIT(6) /* Serial Port RTS# Pin Data */
#define SCSPTR_CTSIO BIT(5) /* Serial Port CTS# Pin Input/Output */
#define SCSPTR_CTSDT BIT(4) /* Serial Port CTS# Pin Data */
#define SCSPTR_SCKIO BIT(3) /* Serial Port Clock Pin Input/Output */
#define SCSPTR_SCKDT BIT(2) /* Serial Port Clock Pin Data */
#define SCSPTR_SPB2IO BIT(1) /* Serial Port Break Input/Output */
#define SCSPTR_SPB2DT BIT(0) /* Serial Port Break Data */
/* HSSRR HSCIF */
#define HSCIF_SRE BIT(15) /* Sampling Rate Register Enable */
#define HSCIF_SRDE BIT(14) /* Sampling Point Register Enable */
#define HSCIF_SRHP_SHIFT 8
#define HSCIF_SRHP_MASK 0x0f00
/* SCPCR (Serial Port Control Register), SCIFA/SCIFB only */
#define SCPCR_RTSC BIT(4) /* Serial Port RTS# Pin / Output Pin */
#define SCPCR_CTSC BIT(3) /* Serial Port CTS# Pin / Input Pin */
#define SCPCR_SCKC BIT(2) /* Serial Port SCK Pin / Output Pin */
#define SCPCR_RXDC BIT(1) /* Serial Port RXD Pin / Input Pin */
#define SCPCR_TXDC BIT(0) /* Serial Port TXD Pin / Output Pin */
/* SCPDR (Serial Port Data Register), SCIFA/SCIFB only */
#define SCPDR_RTSD BIT(4) /* Serial Port RTS# Output Pin Data */
#define SCPDR_CTSD BIT(3) /* Serial Port CTS# Input Pin Data */
#define SCPDR_SCKD BIT(2) /* Serial Port SCK Output Pin Data */
#define SCPDR_RXDD BIT(1) /* Serial Port RXD Input Pin Data */
#define SCPDR_TXDD BIT(0) /* Serial Port TXD Output Pin Data */
/*
* BRG Clock Select Register (Some SCIF and HSCIF)
* The Baud Rate Generator for external clock can provide a clock source for
* the sampling clock. It outputs either its frequency divided clock, or the
* (undivided) (H)SCK external clock.
*/
#define SCCKS_CKS BIT(15) /* Select (H)SCK (1) or divided SC_CLK (0) */
#define SCCKS_XIN BIT(14) /* SC_CLK uses bus clock (1) or SCIF_CLK (0) */
#define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND)
#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_DR | SCIF_RDF)
#define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE)
#define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER)
#define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER)
#define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK)
#define SCxSR_ERRORS(port) (to_sci_port(port)->params->error_mask)
#define SCxSR_RDxF_CLEAR(port) \
(((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR)
#define SCxSR_ERROR_CLEAR(port) \
(to_sci_port(port)->params->error_clear)
#define SCxSR_TDxE_CLEAR(port) \
(((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR)
#define SCxSR_BREAK_CLEAR(port) \
(((port)->type == PORT_SCI) ? SCI_BREAK_CLEAR : SCIF_BREAK_CLEAR)
#define SCIx_IRQ_IS_MUXED(port) \
((port)->irqs[SCIx_ERI_IRQ] == \
(port)->irqs[SCIx_RXI_IRQ]) || \
@@ -1024,8 +1198,16 @@ static int sci_handle_fifo_overrun(struct uart_port *port)
status = s->ops->read_reg(port, s->params->overrun_reg);
if (status & s->params->overrun_mask) {
status &= ~s->params->overrun_mask;
s->ops->write_reg(port, s->params->overrun_reg, status);
if (s->type == SCI_PORT_RSCI) {
/*
* All of the CFCLR_*C clearing bits match the corresponding
* CSR_*status bits. So, reuse the overrun mask for clearing.
*/
s->ops->clear_SCxSR(port, s->params->overrun_mask);
} else {
status &= ~s->params->overrun_mask;
s->ops->write_reg(port, s->params->overrun_reg, status);
}
port->icount.overrun++;

View File

@@ -1,178 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/bitops.h>
#include <linux/serial_core.h>
#include <linux/io.h>
#define SCI_MAJOR 204
#define SCI_MINOR_START 8
/*
* SCI register subset common for all port types.
* Not all registers will exist on all parts.
*/
enum {
SCSMR, /* Serial Mode Register */
SCBRR, /* Bit Rate Register */
SCSCR, /* Serial Control Register */
SCxSR, /* Serial Status Register */
SCFCR, /* FIFO Control Register */
SCFDR, /* FIFO Data Count Register */
SCxTDR, /* Transmit (FIFO) Data Register */
SCxRDR, /* Receive (FIFO) Data Register */
SCLSR, /* Line Status Register */
SCTFDR, /* Transmit FIFO Data Count Register */
SCRFDR, /* Receive FIFO Data Count Register */
SCSPTR, /* Serial Port Register */
HSSRR, /* Sampling Rate Register */
SCPCR, /* Serial Port Control Register */
SCPDR, /* Serial Port Data Register */
SCDL, /* BRG Frequency Division Register */
SCCKS, /* BRG Clock Select Register */
HSRTRGR, /* Rx FIFO Data Count Trigger Register */
HSTTRGR, /* Tx FIFO Data Count Trigger Register */
SEMR, /* Serial extended mode register */
};
/* SCSMR (Serial Mode Register) */
#define SCSMR_C_A BIT(7) /* Communication Mode */
#define SCSMR_CSYNC BIT(7) /* - Clocked synchronous mode */
#define SCSMR_ASYNC 0 /* - Asynchronous mode */
#define SCSMR_CHR BIT(6) /* 7-bit Character Length */
#define SCSMR_PE BIT(5) /* Parity Enable */
#define SCSMR_ODD BIT(4) /* Odd Parity */
#define SCSMR_STOP BIT(3) /* Stop Bit Length */
#define SCSMR_CKS 0x0003 /* Clock Select */
/* Serial Mode Register, SCIFA/SCIFB only bits */
#define SCSMR_CKEDG BIT(12) /* Transmit/Receive Clock Edge Select */
#define SCSMR_SRC_MASK 0x0700 /* Sampling Control */
#define SCSMR_SRC_16 0x0000 /* Sampling rate 1/16 */
#define SCSMR_SRC_5 0x0100 /* Sampling rate 1/5 */
#define SCSMR_SRC_7 0x0200 /* Sampling rate 1/7 */
#define SCSMR_SRC_11 0x0300 /* Sampling rate 1/11 */
#define SCSMR_SRC_13 0x0400 /* Sampling rate 1/13 */
#define SCSMR_SRC_17 0x0500 /* Sampling rate 1/17 */
#define SCSMR_SRC_19 0x0600 /* Sampling rate 1/19 */
#define SCSMR_SRC_27 0x0700 /* Sampling rate 1/27 */
/* Serial Control Register, SCI only bits */
#define SCSCR_TEIE BIT(2) /* Transmit End Interrupt Enable */
/* Serial Control Register, SCIFA/SCIFB only bits */
#define SCSCR_TDRQE BIT(15) /* Tx Data Transfer Request Enable */
#define SCSCR_RDRQE BIT(14) /* Rx Data Transfer Request Enable */
/* Serial Control Register, HSCIF-only bits */
#define HSSCR_TOT_SHIFT 14
/* SCxSR (Serial Status Register) on SCI */
#define SCI_TDRE BIT(7) /* Transmit Data Register Empty */
#define SCI_RDRF BIT(6) /* Receive Data Register Full */
#define SCI_ORER BIT(5) /* Overrun Error */
#define SCI_FER BIT(4) /* Framing Error */
#define SCI_PER BIT(3) /* Parity Error */
#define SCI_TEND BIT(2) /* Transmit End */
#define SCI_RESERVED 0x03 /* All reserved bits */
#define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER)
#define SCI_RDxF_CLEAR (u32)(~(SCI_RESERVED | SCI_RDRF))
#define SCI_ERROR_CLEAR (u32)(~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER))
#define SCI_TDxE_CLEAR (u32)(~(SCI_RESERVED | SCI_TEND | SCI_TDRE))
#define SCI_BREAK_CLEAR (u32)(~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER))
/* SCxSR (Serial Status Register) on SCIF, SCIFA, SCIFB, HSCIF */
#define SCIF_ER BIT(7) /* Receive Error */
#define SCIF_TEND BIT(6) /* Transmission End */
#define SCIF_TDFE BIT(5) /* Transmit FIFO Data Empty */
#define SCIF_BRK BIT(4) /* Break Detect */
#define SCIF_FER BIT(3) /* Framing Error */
#define SCIF_PER BIT(2) /* Parity Error */
#define SCIF_RDF BIT(1) /* Receive FIFO Data Full */
#define SCIF_DR BIT(0) /* Receive Data Ready */
/* SCIF only (optional) */
#define SCIF_PERC 0xf000 /* Number of Parity Errors */
#define SCIF_FERC 0x0f00 /* Number of Framing Errors */
/*SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721 only */
#define SCIFA_ORER BIT(9) /* Overrun Error */
#define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_BRK | SCIF_ER)
#define SCIF_RDxF_CLEAR (u32)(~(SCIF_DR | SCIF_RDF))
#define SCIF_ERROR_CLEAR (u32)(~(SCIF_PER | SCIF_FER | SCIF_ER))
#define SCIF_TDxE_CLEAR (u32)(~(SCIF_TDFE))
#define SCIF_BREAK_CLEAR (u32)(~(SCIF_PER | SCIF_FER | SCIF_BRK))
/* SCFCR (FIFO Control Register) */
#define SCFCR_RTRG1 BIT(7) /* Receive FIFO Data Count Trigger */
#define SCFCR_RTRG0 BIT(6)
#define SCFCR_TTRG1 BIT(5) /* Transmit FIFO Data Count Trigger */
#define SCFCR_TTRG0 BIT(4)
#define SCFCR_MCE BIT(3) /* Modem Control Enable */
#define SCFCR_TFRST BIT(2) /* Transmit FIFO Data Register Reset */
#define SCFCR_RFRST BIT(1) /* Receive FIFO Data Register Reset */
#define SCFCR_LOOP BIT(0) /* Loopback Test */
/* SCLSR (Line Status Register) on (H)SCIF */
#define SCLSR_TO BIT(2) /* Timeout */
#define SCLSR_ORER BIT(0) /* Overrun Error */
/* SCSPTR (Serial Port Register), optional */
#define SCSPTR_RTSIO BIT(7) /* Serial Port RTS# Pin Input/Output */
#define SCSPTR_RTSDT BIT(6) /* Serial Port RTS# Pin Data */
#define SCSPTR_CTSIO BIT(5) /* Serial Port CTS# Pin Input/Output */
#define SCSPTR_CTSDT BIT(4) /* Serial Port CTS# Pin Data */
#define SCSPTR_SCKIO BIT(3) /* Serial Port Clock Pin Input/Output */
#define SCSPTR_SCKDT BIT(2) /* Serial Port Clock Pin Data */
#define SCSPTR_SPB2IO BIT(1) /* Serial Port Break Input/Output */
#define SCSPTR_SPB2DT BIT(0) /* Serial Port Break Data */
/* HSSRR HSCIF */
#define HSCIF_SRE BIT(15) /* Sampling Rate Register Enable */
#define HSCIF_SRDE BIT(14) /* Sampling Point Register Enable */
#define HSCIF_SRHP_SHIFT 8
#define HSCIF_SRHP_MASK 0x0f00
/* SCPCR (Serial Port Control Register), SCIFA/SCIFB only */
#define SCPCR_RTSC BIT(4) /* Serial Port RTS# Pin / Output Pin */
#define SCPCR_CTSC BIT(3) /* Serial Port CTS# Pin / Input Pin */
#define SCPCR_SCKC BIT(2) /* Serial Port SCK Pin / Output Pin */
#define SCPCR_RXDC BIT(1) /* Serial Port RXD Pin / Input Pin */
#define SCPCR_TXDC BIT(0) /* Serial Port TXD Pin / Output Pin */
/* SCPDR (Serial Port Data Register), SCIFA/SCIFB only */
#define SCPDR_RTSD BIT(4) /* Serial Port RTS# Output Pin Data */
#define SCPDR_CTSD BIT(3) /* Serial Port CTS# Input Pin Data */
#define SCPDR_SCKD BIT(2) /* Serial Port SCK Output Pin Data */
#define SCPDR_RXDD BIT(1) /* Serial Port RXD Input Pin Data */
#define SCPDR_TXDD BIT(0) /* Serial Port TXD Output Pin Data */
/*
* BRG Clock Select Register (Some SCIF and HSCIF)
* The Baud Rate Generator for external clock can provide a clock source for
* the sampling clock. It outputs either its frequency divided clock, or the
* (undivided) (H)SCK external clock.
*/
#define SCCKS_CKS BIT(15) /* Select (H)SCK (1) or divided SC_CLK (0) */
#define SCCKS_XIN BIT(14) /* SC_CLK uses bus clock (1) or SCIF_CLK (0) */
#define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND)
#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_DR | SCIF_RDF)
#define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE)
#define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER)
#define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER)
#define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK)
#define SCxSR_ERRORS(port) (to_sci_port(port)->params->error_mask)
#define SCxSR_RDxF_CLEAR(port) \
(((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR)
#define SCxSR_ERROR_CLEAR(port) \
(to_sci_port(port)->params->error_clear)
#define SCxSR_TDxE_CLEAR(port) \
(((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR)
#define SCxSR_BREAK_CLEAR(port) \
(((port)->type == PORT_SCI) ? SCI_BREAK_CLEAR : SCIF_BREAK_CLEAR)

View File

@@ -1133,6 +1133,9 @@ static int sprd_clk_init(struct uart_port *uport)
clk_uart = devm_clk_get(uport->dev, "uart");
if (IS_ERR(clk_uart)) {
if (PTR_ERR(clk_uart) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_warn(uport->dev, "uart%d can't get uart clock\n",
uport->line);
clk_uart = NULL;
@@ -1140,6 +1143,9 @@ static int sprd_clk_init(struct uart_port *uport)
clk_parent = devm_clk_get(uport->dev, "source");
if (IS_ERR(clk_parent)) {
if (PTR_ERR(clk_parent) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_warn(uport->dev, "uart%d can't get source clock\n",
uport->line);
clk_parent = NULL;

View File

@@ -190,7 +190,6 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
* @port: Pointer to the UART port
* @uartclk: Reference clock
* @pclk: APB clock
* @cdns_uart_driver: Pointer to UART driver
* @baud: Current baud rate
* @clk_rate_change_nb: Notifier block for clock changes
* @quirks: Flags for RXBS support.
@@ -204,7 +203,6 @@ struct cdns_uart {
struct uart_port *port;
struct clk *uartclk;
struct clk *pclk;
struct uart_driver *cdns_uart_driver;
unsigned int baud;
struct notifier_block clk_rate_change_nb;
u32 quirks;
@@ -1465,7 +1463,6 @@ static struct console cdns_uart_console = {
static int cdns_uart_suspend(struct device *device)
{
struct uart_port *port = dev_get_drvdata(device);
struct cdns_uart *cdns_uart = port->private_data;
int may_wake;
may_wake = device_may_wakeup(device);
@@ -1489,7 +1486,7 @@ static int cdns_uart_suspend(struct device *device)
* Call the API provided in serial_core.c file which handles
* the suspend.
*/
return uart_suspend_port(cdns_uart->cdns_uart_driver, port);
return uart_suspend_port(&cdns_uart_uart_driver, port);
}
/**
@@ -1550,7 +1547,7 @@ static int cdns_uart_resume(struct device *device)
uart_port_unlock_irqrestore(port, flags);
}
return uart_resume_port(cdns_uart->cdns_uart_driver, port);
return uart_resume_port(&cdns_uart_uart_driver, port);
}
#endif /* ! CONFIG_PM_SLEEP */
static int __maybe_unused cdns_runtime_suspend(struct device *dev)
@@ -1686,8 +1683,6 @@ static int cdns_uart_probe(struct platform_device *pdev)
}
}
cdns_uart_data->cdns_uart_driver = &cdns_uart_uart_driver;
match = of_match_node(cdns_uart_of_match, pdev->dev.of_node);
if (match && match->data) {
const struct cdns_platform_data *data = match->data;
@@ -1862,7 +1857,7 @@ err_out_clk_dis_pclk:
clk_disable_unprepare(cdns_uart_data->pclk);
err_out_unregister_driver:
if (!instances)
uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
uart_unregister_driver(&cdns_uart_uart_driver);
return rc;
}
@@ -1880,7 +1875,7 @@ static void cdns_uart_remove(struct platform_device *pdev)
clk_notifier_unregister(cdns_uart_data->uartclk,
&cdns_uart_data->clk_rate_change_nb);
#endif
uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
uart_remove_one_port(&cdns_uart_uart_driver, port);
port->mapbase = 0;
clk_disable_unprepare(cdns_uart_data->uartclk);
clk_disable_unprepare(cdns_uart_data->pclk);
@@ -1896,7 +1891,7 @@ static void cdns_uart_remove(struct platform_device *pdev)
reset_control_assert(cdns_uart_data->rstc);
if (!--instances)
uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
uart_unregister_driver(&cdns_uart_uart_driver);
}
static struct platform_driver cdns_uart_platform_driver = {

View File

@@ -76,7 +76,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port)
mutex_unlock(&buf->lock);
if (restart)
queue_work(system_unbound_wq, &buf->work);
queue_work(system_dfl_wq, &buf->work);
}
EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive);
@@ -530,7 +530,7 @@ void tty_flip_buffer_push(struct tty_port *port)
struct tty_bufhead *buf = &port->buf;
tty_flip_buffer_commit(buf->tail);
queue_work(system_unbound_wq, &buf->work);
queue_work(system_dfl_wq, &buf->work);
}
EXPORT_SYMBOL(tty_flip_buffer_push);
@@ -560,7 +560,7 @@ int tty_insert_flip_string_and_push_buffer(struct tty_port *port,
tty_flip_buffer_commit(buf->tail);
spin_unlock_irqrestore(&port->lock, flags);
queue_work(system_unbound_wq, &buf->work);
queue_work(system_dfl_wq, &buf->work);
return size;
}
@@ -613,7 +613,7 @@ void tty_buffer_set_lock_subclass(struct tty_port *port)
bool tty_buffer_restart_work(struct tty_port *port)
{
return queue_work(system_unbound_wq, &port->buf.work);
return queue_work(system_dfl_wq, &port->buf.work);
}
bool tty_buffer_cancel_work(struct tty_port *port)

View File

@@ -424,8 +424,6 @@ static void do_compute_shiftstate(void)
/* We still have to export this method to vt.c */
void vt_set_leds_compute_shiftstate(void)
{
unsigned long flags;
/*
* When VT is switched, the keyboard led needs to be set once.
* Ensure that after the switch is completed, the state of the
@@ -434,9 +432,8 @@ void vt_set_leds_compute_shiftstate(void)
vt_switch = true;
set_leds();
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
do_compute_shiftstate();
spin_unlock_irqrestore(&kbd_event_lock, flags);
}
/*
@@ -625,13 +622,12 @@ static void fn_compose(struct vc_data *vc)
static void fn_spawn_con(struct vc_data *vc)
{
spin_lock(&vt_spawn_con.lock);
guard(spinlock)(&vt_spawn_con.lock);
if (vt_spawn_con.pid)
if (kill_pid(vt_spawn_con.pid, vt_spawn_con.sig, 1)) {
put_pid(vt_spawn_con.pid);
vt_spawn_con.pid = NULL;
}
spin_unlock(&vt_spawn_con.lock);
}
static void fn_SAK(struct vc_data *vc)
@@ -762,13 +758,9 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag)
return;
if ((unsigned)value < ARRAY_SIZE(func_table)) {
unsigned long flags;
spin_lock_irqsave(&func_buf_lock, flags);
guard(spinlock_irqsave)(&func_buf_lock);
if (func_table[value])
puts_queue(vc, func_table[value]);
spin_unlock_irqrestore(&func_buf_lock, flags);
} else
pr_err("k_fn called with value=%d\n", value);
}
@@ -1140,8 +1132,7 @@ static unsigned char getledstate(void)
void setledstate(struct kbd_struct *kb, unsigned int led)
{
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
guard(spinlock_irqsave)(&led_lock);
if (!(led & ~7)) {
ledioctl = led;
kb->ledmode = LED_SHOW_IOCTL;
@@ -1149,7 +1140,6 @@ void setledstate(struct kbd_struct *kb, unsigned int led)
kb->ledmode = LED_SHOW_FLAGS;
set_leds();
spin_unlock_irqrestore(&led_lock, flags);
}
static inline unsigned char getleds(void)
@@ -1172,14 +1162,9 @@ static inline unsigned char getleds(void)
int vt_get_leds(unsigned int console, int flag)
{
struct kbd_struct *kb = &kbd_table[console];
int ret;
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
ret = vc_kbd_led(kb, flag);
spin_unlock_irqrestore(&led_lock, flags);
return ret;
guard(spinlock_irqsave)(&led_lock);
return vc_kbd_led(kb, flag);
}
EXPORT_SYMBOL_GPL(vt_get_leds);
@@ -1213,11 +1198,10 @@ void vt_set_led_state(unsigned int console, int leds)
void vt_kbd_con_start(unsigned int console)
{
struct kbd_struct *kb = &kbd_table[console];
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
guard(spinlock_irqsave)(&led_lock);
clr_vc_kbd_led(kb, VC_SCROLLOCK);
set_leds();
spin_unlock_irqrestore(&led_lock, flags);
}
/**
@@ -1230,11 +1214,10 @@ void vt_kbd_con_start(unsigned int console)
void vt_kbd_con_stop(unsigned int console)
{
struct kbd_struct *kb = &kbd_table[console];
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
guard(spinlock_irqsave)(&led_lock);
set_vc_kbd_led(kb, VC_SCROLLOCK);
set_leds();
spin_unlock_irqrestore(&led_lock, flags);
}
/*
@@ -1246,12 +1229,11 @@ void vt_kbd_con_stop(unsigned int console)
static void kbd_bh(struct tasklet_struct *unused)
{
unsigned int leds;
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
leds = getleds();
leds |= (unsigned int)kbd->lockstate << 8;
spin_unlock_irqrestore(&led_lock, flags);
scoped_guard(spinlock_irqsave, &led_lock) {
leds = getleds();
leds |= (unsigned int)kbd->lockstate << 8;
}
if (vt_switch) {
ledstate = ~leds;
@@ -1525,15 +1507,13 @@ static void kbd_event(struct input_handle *handle, unsigned int event_type,
unsigned int event_code, int value)
{
/* We are called with interrupts disabled, just take the lock */
spin_lock(&kbd_event_lock);
if (event_type == EV_MSC && event_code == MSC_RAW &&
kbd_is_hw_raw(handle->dev))
kbd_rawcode(value);
if (event_type == EV_KEY && event_code <= KEY_MAX)
kbd_keycode(event_code, value, kbd_is_hw_raw(handle->dev));
spin_unlock(&kbd_event_lock);
scoped_guard(spinlock, &kbd_event_lock) {
if (event_type == EV_MSC && event_code == MSC_RAW &&
kbd_is_hw_raw(handle->dev))
kbd_rawcode(value);
if (event_type == EV_KEY && event_code <= KEY_MAX)
kbd_keycode(event_code, value, kbd_is_hw_raw(handle->dev));
}
tasklet_schedule(&keyboard_tasklet);
do_poke_blanked_console = 1;
@@ -1566,10 +1546,9 @@ static bool kbd_match(struct input_handler *handler, struct input_dev *dev)
static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct input_handle *handle;
int error;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
struct input_handle __free(kfree) *handle = kzalloc(sizeof(*handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
@@ -1579,18 +1558,18 @@ static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
error = input_register_handle(handle);
if (error)
goto err_free_handle;
return error;
error = input_open_device(handle);
if (error)
goto err_unregister_handle;
retain_and_null_ptr(handle);
return 0;
err_unregister_handle:
input_unregister_handle(handle);
err_free_handle:
kfree(handle);
return error;
}
@@ -1681,77 +1660,64 @@ int __init kbd_init(void)
*/
int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
{
unsigned long flags;
int asize;
int ret = 0;
switch (cmd) {
case KDGKBDIACR:
{
struct kbdiacrs __user *a = udp;
struct kbdiacr *dia;
int i;
dia = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacr),
GFP_KERNEL);
struct kbdiacr __free(kfree) *dia = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacr),
GFP_KERNEL);
if (!dia)
return -ENOMEM;
/* Lock the diacriticals table, make a copy and then
copy it after we unlock */
spin_lock_irqsave(&kbd_event_lock, flags);
asize = accent_table_size;
for (i = 0; i < asize; i++) {
dia[i].diacr = conv_uni_to_8bit(
accent_table[i].diacr);
dia[i].base = conv_uni_to_8bit(
accent_table[i].base);
dia[i].result = conv_uni_to_8bit(
accent_table[i].result);
scoped_guard(spinlock_irqsave, &kbd_event_lock) {
asize = accent_table_size;
for (i = 0; i < asize; i++) {
dia[i].diacr = conv_uni_to_8bit(accent_table[i].diacr);
dia[i].base = conv_uni_to_8bit(accent_table[i].base);
dia[i].result = conv_uni_to_8bit(accent_table[i].result);
}
}
spin_unlock_irqrestore(&kbd_event_lock, flags);
if (put_user(asize, &a->kb_cnt))
ret = -EFAULT;
else if (copy_to_user(a->kbdiacr, dia,
asize * sizeof(struct kbdiacr)))
ret = -EFAULT;
kfree(dia);
return ret;
return -EFAULT;
if (copy_to_user(a->kbdiacr, dia, asize * sizeof(struct kbdiacr)))
return -EFAULT;
return 0;
}
case KDGKBDIACRUC:
{
struct kbdiacrsuc __user *a = udp;
void *buf;
buf = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacruc),
GFP_KERNEL);
void __free(kfree) *buf = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacruc),
GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
/* Lock the diacriticals table, make a copy and then
copy it after we unlock */
spin_lock_irqsave(&kbd_event_lock, flags);
asize = accent_table_size;
memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc));
spin_unlock_irqrestore(&kbd_event_lock, flags);
scoped_guard(spinlock_irqsave, &kbd_event_lock) {
asize = accent_table_size;
memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc));
}
if (put_user(asize, &a->kb_cnt))
ret = -EFAULT;
else if (copy_to_user(a->kbdiacruc, buf,
asize*sizeof(struct kbdiacruc)))
ret = -EFAULT;
kfree(buf);
return ret;
return -EFAULT;
if (copy_to_user(a->kbdiacruc, buf, asize * sizeof(struct kbdiacruc)))
return -EFAULT;
return 0;
}
case KDSKBDIACR:
{
struct kbdiacrs __user *a = udp;
struct kbdiacr *dia = NULL;
struct kbdiacr __free(kfree) *dia = NULL;
unsigned int ct;
int i;
@@ -1769,7 +1735,7 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
return PTR_ERR(dia);
}
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
accent_table_size = ct;
for (i = 0; i < ct; i++) {
accent_table[i].diacr =
@@ -1779,8 +1745,7 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
accent_table[i].result =
conv_8bit_to_uni(dia[i].result);
}
spin_unlock_irqrestore(&kbd_event_lock, flags);
kfree(dia);
return 0;
}
@@ -1788,7 +1753,7 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
{
struct kbdiacrsuc __user *a = udp;
unsigned int ct;
void *buf = NULL;
void __free(kfree) *buf = NULL;
if (!perm)
return -EPERM;
@@ -1805,17 +1770,15 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
if (IS_ERR(buf))
return PTR_ERR(buf);
}
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
if (ct)
memcpy(accent_table, buf,
ct * sizeof(struct kbdiacruc));
accent_table_size = ct;
spin_unlock_irqrestore(&kbd_event_lock, flags);
kfree(buf);
return 0;
}
}
return ret;
return 0;
}
/**
@@ -1829,33 +1792,29 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
int vt_do_kdskbmode(unsigned int console, unsigned int arg)
{
struct kbd_struct *kb = &kbd_table[console];
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
switch(arg) {
case K_RAW:
kb->kbdmode = VC_RAW;
break;
return 0;
case K_MEDIUMRAW:
kb->kbdmode = VC_MEDIUMRAW;
break;
return 0;
case K_XLATE:
kb->kbdmode = VC_XLATE;
do_compute_shiftstate();
break;
return 0;
case K_UNICODE:
kb->kbdmode = VC_UNICODE;
do_compute_shiftstate();
break;
return 0;
case K_OFF:
kb->kbdmode = VC_OFF;
break;
return 0;
default:
ret = -EINVAL;
return -EINVAL;
}
spin_unlock_irqrestore(&kbd_event_lock, flags);
return ret;
}
/**
@@ -1869,75 +1828,68 @@ int vt_do_kdskbmode(unsigned int console, unsigned int arg)
int vt_do_kdskbmeta(unsigned int console, unsigned int arg)
{
struct kbd_struct *kb = &kbd_table[console];
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
switch(arg) {
case K_METABIT:
clr_vc_kbd_mode(kb, VC_META);
break;
return 0;
case K_ESCPREFIX:
set_vc_kbd_mode(kb, VC_META);
break;
return 0;
default:
ret = -EINVAL;
return -EINVAL;
}
spin_unlock_irqrestore(&kbd_event_lock, flags);
return ret;
}
int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
int perm)
int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm)
{
struct kbkeycode tmp;
int kc = 0;
int kc;
if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
return -EFAULT;
switch (cmd) {
case KDGETKEYCODE:
kc = getkeycode(tmp.scancode);
if (kc >= 0)
kc = put_user(kc, &user_kbkc->keycode);
break;
if (kc < 0)
return kc;
return put_user(kc, &user_kbkc->keycode);
case KDSETKEYCODE:
if (!perm)
return -EPERM;
kc = setkeycode(tmp.scancode, tmp.keycode);
break;
return setkeycode(tmp.scancode, tmp.keycode);
}
return kc;
return 0;
}
static unsigned short vt_kdgkbent(unsigned char kbdmode, unsigned char idx,
unsigned char map)
{
unsigned short *key_map, val;
unsigned long flags;
unsigned short *key_map;
/* Ensure another thread doesn't free it under us */
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
key_map = key_maps[map];
if (key_map) {
val = U(key_map[idx]);
unsigned short val = U(key_map[idx]);
if (kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
val = K_HOLE;
} else
val = idx ? K_HOLE : K_NOSUCHMAP;
spin_unlock_irqrestore(&kbd_event_lock, flags);
return K_HOLE;
return val;
}
return val;
return idx ? K_HOLE : K_NOSUCHMAP;
}
static int vt_kdskbent(unsigned char kbdmode, unsigned char idx,
unsigned char map, unsigned short val)
{
unsigned long flags;
unsigned short *key_map, *new_map, oldval;
unsigned short *key_map, oldval;
if (!idx && val == K_NOSUCHMAP) {
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
/* deallocate map */
key_map = key_maps[map];
if (map && key_map) {
@@ -1947,7 +1899,6 @@ static int vt_kdskbent(unsigned char kbdmode, unsigned char idx,
keymap_count--;
}
}
spin_unlock_irqrestore(&kbd_event_lock, flags);
return 0;
}
@@ -1965,45 +1916,36 @@ static int vt_kdskbent(unsigned char kbdmode, unsigned char idx,
return 0;
#endif
new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
unsigned short __free(kfree) *new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
if (!new_map)
return -ENOMEM;
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
key_map = key_maps[map];
if (key_map == NULL) {
int j;
if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
!capable(CAP_SYS_RESOURCE)) {
spin_unlock_irqrestore(&kbd_event_lock, flags);
kfree(new_map);
if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !capable(CAP_SYS_RESOURCE))
return -EPERM;
}
key_maps[map] = new_map;
key_map = new_map;
key_map = key_maps[map] = no_free_ptr(new_map);
key_map[0] = U(K_ALLOCATED);
for (j = 1; j < NR_KEYS; j++)
key_map[j] = U(K_HOLE);
keymap_count++;
} else
kfree(new_map);
}
oldval = U(key_map[idx]);
if (val == oldval)
goto out;
return 0;
/* Attention Key */
if ((oldval == K_SAK || val == K_SAK) && !capable(CAP_SYS_ADMIN)) {
spin_unlock_irqrestore(&kbd_event_lock, flags);
if ((oldval == K_SAK || val == K_SAK) && !capable(CAP_SYS_ADMIN))
return -EPERM;
}
key_map[idx] = U(val);
if (!map && (KTYP(oldval) == KT_SHIFT || KTYP(val) == KT_SHIFT))
do_compute_shiftstate();
out:
spin_unlock_irqrestore(&kbd_event_lock, flags);
return 0;
}
@@ -2049,9 +1991,6 @@ static char *vt_kdskbsent(char *kbs, unsigned char cur)
int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
{
unsigned char kb_func;
unsigned long flags;
char *kbs;
int ret;
if (get_user(kb_func, &user_kdgkb->kb_func))
return -EFAULT;
@@ -2063,57 +2002,50 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
/* size should have been a struct member */
ssize_t len = sizeof(user_kdgkb->kb_string);
kbs = kmalloc(len, GFP_KERNEL);
char __free(kfree) *kbs = kmalloc(len, GFP_KERNEL);
if (!kbs)
return -ENOMEM;
spin_lock_irqsave(&func_buf_lock, flags);
len = strscpy(kbs, func_table[kb_func] ? : "", len);
spin_unlock_irqrestore(&func_buf_lock, flags);
scoped_guard(spinlock_irqsave, &func_buf_lock)
len = strscpy(kbs, func_table[kb_func] ? : "", len);
if (len < 0) {
ret = -ENOSPC;
break;
}
ret = copy_to_user(user_kdgkb->kb_string, kbs, len + 1) ?
-EFAULT : 0;
break;
if (len < 0)
return -ENOSPC;
if (copy_to_user(user_kdgkb->kb_string, kbs, len + 1))
return -EFAULT;
return 0;
}
case KDSKBSENT:
if (!perm || !capable(CAP_SYS_TTY_CONFIG))
return -EPERM;
kbs = strndup_user(user_kdgkb->kb_string,
sizeof(user_kdgkb->kb_string));
char __free(kfree) *kbs = strndup_user(user_kdgkb->kb_string,
sizeof(user_kdgkb->kb_string));
if (IS_ERR(kbs))
return PTR_ERR(kbs);
spin_lock_irqsave(&func_buf_lock, flags);
guard(spinlock_irqsave)(&func_buf_lock);
kbs = vt_kdskbsent(kbs, kb_func);
spin_unlock_irqrestore(&func_buf_lock, flags);
ret = 0;
break;
return 0;
}
kfree(kbs);
return ret;
return 0;
}
int vt_do_kdskled(unsigned int console, int cmd, unsigned long arg, int perm)
{
struct kbd_struct *kb = &kbd_table[console];
unsigned long flags;
unsigned char ucval;
switch(cmd) {
/* the ioctls below read/set the flags usually shown in the leds */
/* don't use them - they will go away without warning */
case KDGKBLED:
spin_lock_irqsave(&kbd_event_lock, flags);
ucval = kb->ledflagstate | (kb->default_ledflagstate << 4);
spin_unlock_irqrestore(&kbd_event_lock, flags);
scoped_guard(spinlock_irqsave, &kbd_event_lock)
ucval = kb->ledflagstate | (kb->default_ledflagstate << 4);
return put_user(ucval, (char __user *)arg);
case KDSKBLED:
@@ -2121,11 +2053,11 @@ int vt_do_kdskled(unsigned int console, int cmd, unsigned long arg, int perm)
return -EPERM;
if (arg & ~0x77)
return -EINVAL;
spin_lock_irqsave(&led_lock, flags);
kb->ledflagstate = (arg & 7);
kb->default_ledflagstate = ((arg >> 4) & 7);
set_leds();
spin_unlock_irqrestore(&led_lock, flags);
scoped_guard(spinlock_irqsave, &led_lock) {
kb->ledflagstate = (arg & 7);
kb->default_ledflagstate = ((arg >> 4) & 7);
set_leds();
}
return 0;
/* the ioctls below only set the lights, not the functions */
@@ -2182,11 +2114,8 @@ int vt_do_kdgkbmeta(unsigned int console)
*/
void vt_reset_unicode(unsigned int console)
{
unsigned long flags;
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
kbd_table[console].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
spin_unlock_irqrestore(&kbd_event_lock, flags);
}
/**
@@ -2211,22 +2140,19 @@ int vt_get_shift_state(void)
void vt_reset_keyboard(unsigned int console)
{
struct kbd_struct *kb = &kbd_table[console];
unsigned long flags;
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
set_vc_kbd_mode(kb, VC_REPEAT);
clr_vc_kbd_mode(kb, VC_CKMODE);
clr_vc_kbd_mode(kb, VC_APPLIC);
clr_vc_kbd_mode(kb, VC_CRLF);
kb->lockstate = 0;
kb->slockstate = 0;
spin_lock(&led_lock);
guard(spinlock)(&led_lock);
kb->ledmode = LED_SHOW_FLAGS;
kb->ledflagstate = kb->default_ledflagstate;
spin_unlock(&led_lock);
/* do not do set_leds here because this causes an endless tasklet loop
when the keyboard hasn't been initialized yet */
spin_unlock_irqrestore(&kbd_event_lock, flags);
}
/**
@@ -2256,11 +2182,9 @@ int vt_get_kbd_mode_bit(unsigned int console, int bit)
void vt_set_kbd_mode_bit(unsigned int console, int bit)
{
struct kbd_struct *kb = &kbd_table[console];
unsigned long flags;
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
set_vc_kbd_mode(kb, bit);
spin_unlock_irqrestore(&kbd_event_lock, flags);
}
/**
@@ -2275,9 +2199,7 @@ void vt_set_kbd_mode_bit(unsigned int console, int bit)
void vt_clr_kbd_mode_bit(unsigned int console, int bit)
{
struct kbd_struct *kb = &kbd_table[console];
unsigned long flags;
spin_lock_irqsave(&kbd_event_lock, flags);
guard(spinlock_irqsave)(&kbd_event_lock);
clr_vc_kbd_mode(kb, bit);
spin_unlock_irqrestore(&kbd_event_lock, flags);
}

View File

@@ -348,10 +348,11 @@ static int vc_selection(struct vc_data *vc, struct tiocl_selection *v,
return 0;
}
v->xs = min_t(u16, v->xs - 1, vc->vc_cols - 1);
v->ys = min_t(u16, v->ys - 1, vc->vc_rows - 1);
v->xe = min_t(u16, v->xe - 1, vc->vc_cols - 1);
v->ye = min_t(u16, v->ye - 1, vc->vc_rows - 1);
/* Historically 0 => max value */
v->xs = umin(v->xs - 1, vc->vc_cols - 1);
v->ys = umin(v->ys - 1, vc->vc_rows - 1);
v->xe = umin(v->xe - 1, vc->vc_cols - 1);
v->ye = umin(v->ye - 1, vc->vc_rows - 1);
if (mouse_reporting() && (v->sel_mode & TIOCL_SELMOUSEREPORT)) {
mouse_report(tty, v->sel_mode & TIOCL_SELBUTTONMASK, v->xs,

View File

@@ -4862,7 +4862,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
return ret;
}
c = (font.width+7)/8 * vpitch * font.charcount;
c = DIV_ROUND_UP(font.width, 8) * vpitch * font.charcount;
if (op->data && font.charcount > op->charcount)
return -ENOSPC;
@@ -4894,7 +4894,7 @@ static int con_font_set(struct vc_data *vc, const struct console_font_op *op)
return -EINVAL;
if (vpitch < op->height)
return -EINVAL;
size = (op->width+7)/8 * vpitch * op->charcount;
size = DIV_ROUND_UP(op->width, 8) * vpitch * op->charcount;
if (size > max_font_size)
return -ENOSPC;

View File

@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
tty_tiocsti_test
tty_tstamp_update

View File

@@ -1,5 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS = -O2 -Wall
TEST_GEN_PROGS := tty_tstamp_update
TEST_GEN_PROGS := tty_tstamp_update tty_tiocsti_test
LDLIBS += -lcap
include ../lib.mk
# Add libcap for TIOCSTI test
$(OUTPUT)/tty_tiocsti_test: LDLIBS += -lcap

View File

@@ -0,0 +1 @@
CONFIG_LEGACY_TIOCSTI=y

View File

@@ -0,0 +1,650 @@
// SPDX-License-Identifier: GPL-2.0
/*
* TTY Tests - TIOCSTI
*
* Copyright © 2025 Abhinav Saxena <xandfury@gmail.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <pwd.h>
#include <termios.h>
#include <grp.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <pty.h>
#include <utmp.h>
#include "../kselftest_harness.h"
enum test_type {
TEST_PTY_TIOCSTI_BASIC,
TEST_PTY_TIOCSTI_FD_PASSING,
/* other tests cases such as serial may be added. */
};
/*
* Test Strategy:
* - Basic tests: Use PTY with/without TIOCSCTTY (controlling terminal for
* current process)
* - FD passing tests: Child creates PTY, parent receives FD (demonstrates
* security issue)
*
* SECURITY VULNERABILITY DEMONSTRATION:
* FD passing tests show that TIOCSTI uses CURRENT process credentials, not
* opener credentials. This means privileged processes can be given FDs from
* unprivileged processes and successfully perform TIOCSTI operations that the
* unprivileged process couldn't do directly.
*
* Attack scenario:
* 1. Unprivileged process opens TTY (direct TIOCSTI fails due to lack of
* privileges)
* 2. Unprivileged process passes FD to privileged process via SCM_RIGHTS
* 3. Privileged process can use TIOCSTI on the FD (succeeds due to its
* privileges)
* 4. Result: Effective privilege escalation via file descriptor passing
*
* This matches the kernel logic in tiocsti():
* 1. if (!tty_legacy_tiocsti && !capable(CAP_SYS_ADMIN)) return -EIO;
* 2. if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
* return -EPERM;
* Note: Both checks use capable() on CURRENT process, not FD opener!
*
* If the file credentials were also checked along with the capable() checks
* then the results for FD pass tests would be consistent with the basic tests.
*/
FIXTURE(tiocsti)
{
int pty_master_fd; /* PTY - for basic tests */
int pty_slave_fd;
bool has_pty;
bool initial_cap_sys_admin;
int original_legacy_tiocsti_setting;
bool can_modify_sysctl;
};
FIXTURE_VARIANT(tiocsti)
{
const enum test_type test_type;
const bool controlling_tty; /* true=current->signal->tty == tty */
const int legacy_tiocsti; /* 0=restricted, 1=permissive */
const bool requires_cap; /* true=with CAP_SYS_ADMIN, false=without */
const int expected_success; /* 0=success, -EIO/-EPERM=specific error */
};
/*
* Tests Controlling Terminal Variants (current->signal->tty == tty)
*
* TIOCSTI Test Matrix:
*
* | legacy_tiocsti | CAP_SYS_ADMIN | Expected Result | Error |
* |----------------|---------------|-----------------|-------|
* | 1 (permissive) | true | SUCCESS | - |
* | 1 (permissive) | false | SUCCESS | - |
* | 0 (restricted) | true | SUCCESS | - |
* | 0 (restricted) | false | FAILURE | -EIO |
*/
/* clang-format off */
FIXTURE_VARIANT_ADD(tiocsti, basic_pty_permissive_withcap) {
.test_type = TEST_PTY_TIOCSTI_BASIC,
.controlling_tty = true,
.legacy_tiocsti = 1,
.requires_cap = true,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, basic_pty_permissive_nocap) {
.test_type = TEST_PTY_TIOCSTI_BASIC,
.controlling_tty = true,
.legacy_tiocsti = 1,
.requires_cap = false,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, basic_pty_restricted_withcap) {
.test_type = TEST_PTY_TIOCSTI_BASIC,
.controlling_tty = true,
.legacy_tiocsti = 0,
.requires_cap = true,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, basic_pty_restricted_nocap) {
.test_type = TEST_PTY_TIOCSTI_BASIC,
.controlling_tty = true,
.legacy_tiocsti = 0,
.requires_cap = false,
.expected_success = -EIO, /* FAILURE: legacy restriction */
}; /* clang-format on */
/*
* Note for FD Passing Test Variants
* Since we're testing the scenario where an unprivileged process pass an FD
* to a privileged one, .requires_cap here means the caps of the child process.
* Not the parent; parent would always be privileged.
*/
/* clang-format off */
FIXTURE_VARIANT_ADD(tiocsti, fdpass_pty_permissive_withcap) {
.test_type = TEST_PTY_TIOCSTI_FD_PASSING,
.controlling_tty = true,
.legacy_tiocsti = 1,
.requires_cap = true,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, fdpass_pty_permissive_nocap) {
.test_type = TEST_PTY_TIOCSTI_FD_PASSING,
.controlling_tty = true,
.legacy_tiocsti = 1,
.requires_cap = false,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, fdpass_pty_restricted_withcap) {
.test_type = TEST_PTY_TIOCSTI_FD_PASSING,
.controlling_tty = true,
.legacy_tiocsti = 0,
.requires_cap = true,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, fdpass_pty_restricted_nocap) {
.test_type = TEST_PTY_TIOCSTI_FD_PASSING,
.controlling_tty = true,
.legacy_tiocsti = 0,
.requires_cap = false,
.expected_success = -EIO,
}; /* clang-format on */
/*
* Non-Controlling Terminal Variants (current->signal->tty != tty)
*
* TIOCSTI Test Matrix:
*
* | legacy_tiocsti | CAP_SYS_ADMIN | Expected Result | Error |
* |----------------|---------------|-----------------|-------|
* | 1 (permissive) | true | SUCCESS | - |
* | 1 (permissive) | false | FAILURE | -EPERM|
* | 0 (restricted) | true | SUCCESS | - |
* | 0 (restricted) | false | FAILURE | -EIO |
*/
/* clang-format off */
FIXTURE_VARIANT_ADD(tiocsti, basic_nopty_permissive_withcap) {
.test_type = TEST_PTY_TIOCSTI_BASIC,
.controlling_tty = false,
.legacy_tiocsti = 1,
.requires_cap = true,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, basic_nopty_permissive_nocap) {
.test_type = TEST_PTY_TIOCSTI_BASIC,
.controlling_tty = false,
.legacy_tiocsti = 1,
.requires_cap = false,
.expected_success = -EPERM,
};
FIXTURE_VARIANT_ADD(tiocsti, basic_nopty_restricted_withcap) {
.test_type = TEST_PTY_TIOCSTI_BASIC,
.controlling_tty = false,
.legacy_tiocsti = 0,
.requires_cap = true,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, basic_nopty_restricted_nocap) {
.test_type = TEST_PTY_TIOCSTI_BASIC,
.controlling_tty = false,
.legacy_tiocsti = 0,
.requires_cap = false,
.expected_success = -EIO,
};
FIXTURE_VARIANT_ADD(tiocsti, fdpass_nopty_permissive_withcap) {
.test_type = TEST_PTY_TIOCSTI_FD_PASSING,
.controlling_tty = false,
.legacy_tiocsti = 1,
.requires_cap = true,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, fdpass_nopty_permissive_nocap) {
.test_type = TEST_PTY_TIOCSTI_FD_PASSING,
.controlling_tty = false,
.legacy_tiocsti = 1,
.requires_cap = false,
.expected_success = -EPERM,
};
FIXTURE_VARIANT_ADD(tiocsti, fdpass_nopty_restricted_withcap) {
.test_type = TEST_PTY_TIOCSTI_FD_PASSING,
.controlling_tty = false,
.legacy_tiocsti = 0,
.requires_cap = true,
.expected_success = 0,
};
FIXTURE_VARIANT_ADD(tiocsti, fdpass_nopty_restricted_nocap) {
.test_type = TEST_PTY_TIOCSTI_FD_PASSING,
.controlling_tty = false,
.legacy_tiocsti = 0,
.requires_cap = false,
.expected_success = -EIO,
}; /* clang-format on */
/* Helper function to send FD via SCM_RIGHTS */
static int send_fd_via_socket(int socket_fd, int fd_to_send)
{
struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
char cmsg_buf[CMSG_SPACE(sizeof(int))];
char dummy_data = 'F';
struct iovec iov = { .iov_base = &dummy_data, .iov_len = 1 };
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buf;
msg.msg_controllen = sizeof(cmsg_buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int));
return sendmsg(socket_fd, &msg, 0) < 0 ? -1 : 0;
}
/* Helper function to receive FD via SCM_RIGHTS */
static int recv_fd_via_socket(int socket_fd)
{
struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
char cmsg_buf[CMSG_SPACE(sizeof(int))];
char dummy_data;
struct iovec iov = { .iov_base = &dummy_data, .iov_len = 1 };
int received_fd = -1;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buf;
msg.msg_controllen = sizeof(cmsg_buf);
if (recvmsg(socket_fd, &msg, 0) < 0)
return -1;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int));
break;
}
}
return received_fd;
}
static inline bool has_cap_sys_admin(void)
{
cap_t caps = cap_get_proc();
if (!caps)
return false;
cap_flag_value_t cap_val;
bool has_cap = (cap_get_flag(caps, CAP_SYS_ADMIN, CAP_EFFECTIVE,
&cap_val) == 0) &&
(cap_val == CAP_SET);
cap_free(caps);
return has_cap;
}
/*
* Switch to non-root user and clear all capabilities
*/
static inline bool drop_all_privs(struct __test_metadata *_metadata)
{
/* Drop supplementary groups */
ASSERT_EQ(setgroups(0, NULL), 0);
/* Switch to non-root user */
ASSERT_EQ(setgid(1000), 0);
ASSERT_EQ(setuid(1000), 0);
/* Clear all capabilities */
cap_t empty = cap_init();
ASSERT_NE(empty, NULL);
ASSERT_EQ(cap_set_proc(empty), 0);
cap_free(empty);
/* Prevent privilege regain */
ASSERT_EQ(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), 0);
/* Verify privilege drop */
ASSERT_FALSE(has_cap_sys_admin());
return true;
}
static inline int get_legacy_tiocsti_setting(struct __test_metadata *_metadata)
{
FILE *fp;
int value = -1;
fp = fopen("/proc/sys/dev/tty/legacy_tiocsti", "r");
if (!fp) {
/* legacy_tiocsti sysctl not available (kernel < 6.2) */
return -1;
}
if (fscanf(fp, "%d", &value) == 1 && fclose(fp) == 0) {
if (value < 0 || value > 1)
value = -1; /* Invalid value */
} else {
value = -1; /* Failed to parse */
}
return value;
}
static inline bool set_legacy_tiocsti_setting(struct __test_metadata *_metadata,
int value)
{
FILE *fp;
bool success = false;
/* Sanity-check the value */
ASSERT_GE(value, 0);
ASSERT_LE(value, 1);
/*
* Try to open for writing; if we lack permission, return false so
* the test harness will skip variants that need to change it
*/
fp = fopen("/proc/sys/dev/tty/legacy_tiocsti", "w");
if (!fp)
return false;
/* Write the new setting */
if (fprintf(fp, "%d\n", value) > 0 && fclose(fp) == 0)
success = true;
else
TH_LOG("Failed to write legacy_tiocsti: %s", strerror(errno));
return success;
}
/*
* TIOCSTI injection test function
* @tty_fd: TTY slave file descriptor to test TIOCSTI on
* Returns: 0 on success, -errno on failure
*/
static inline int test_tiocsti_injection(struct __test_metadata *_metadata,
int tty_fd)
{
int ret;
char inject_char = 'V';
errno = 0;
ret = ioctl(tty_fd, TIOCSTI, &inject_char);
return ret == 0 ? 0 : -errno;
}
/*
* Child process: test TIOCSTI directly with capability/controlling
* terminal setup
*/
static void run_basic_tiocsti_test(struct __test_metadata *_metadata,
FIXTURE_DATA(tiocsti) * self,
const FIXTURE_VARIANT(tiocsti) * variant)
{
/* Handle capability requirements */
if (self->initial_cap_sys_admin && !variant->requires_cap)
ASSERT_TRUE(drop_all_privs(_metadata));
if (variant->controlling_tty) {
/*
* Create new session and set PTY as
* controlling terminal
*/
pid_t sid = setsid();
ASSERT_GE(sid, 0);
ASSERT_EQ(ioctl(self->pty_slave_fd, TIOCSCTTY, 0), 0);
}
/*
* Validate test environment setup and verify final
* capability state matches expectation
* after potential drop.
*/
ASSERT_TRUE(self->has_pty);
ASSERT_EQ(has_cap_sys_admin(), variant->requires_cap);
/* Test TIOCSTI and validate result */
int result = test_tiocsti_injection(_metadata, self->pty_slave_fd);
/* Check against expected result from variant */
EXPECT_EQ(result, variant->expected_success);
_exit(0);
}
/*
* Child process: create PTY and then pass FD to parent via SCM_RIGHTS
*/
static void run_fdpass_tiocsti_test(struct __test_metadata *_metadata,
const FIXTURE_VARIANT(tiocsti) * variant,
int sockfd)
{
signal(SIGHUP, SIG_IGN);
/* Handle privilege dropping */
if (!variant->requires_cap && has_cap_sys_admin())
ASSERT_TRUE(drop_all_privs(_metadata));
/* Create child's PTY */
int child_master_fd, child_slave_fd;
ASSERT_EQ(openpty(&child_master_fd, &child_slave_fd, NULL, NULL, NULL),
0);
if (variant->controlling_tty) {
pid_t sid = setsid();
ASSERT_GE(sid, 0);
ASSERT_EQ(ioctl(child_slave_fd, TIOCSCTTY, 0), 0);
}
/* Test child's direct TIOCSTI for reference */
int direct_result = test_tiocsti_injection(_metadata, child_slave_fd);
EXPECT_EQ(direct_result, variant->expected_success);
/* Send FD to parent */
ASSERT_EQ(send_fd_via_socket(sockfd, child_slave_fd), 0);
/* Wait for parent completion signal */
char sync_byte;
ssize_t bytes_read = read(sockfd, &sync_byte, 1);
ASSERT_EQ(bytes_read, 1);
close(child_master_fd);
close(child_slave_fd);
close(sockfd);
_exit(0);
}
FIXTURE_SETUP(tiocsti)
{
/* Create PTY pair for basic tests */
self->has_pty = (openpty(&self->pty_master_fd, &self->pty_slave_fd,
NULL, NULL, NULL) == 0);
if (!self->has_pty) {
self->pty_master_fd = -1;
self->pty_slave_fd = -1;
}
self->initial_cap_sys_admin = has_cap_sys_admin();
self->original_legacy_tiocsti_setting =
get_legacy_tiocsti_setting(_metadata);
if (self->original_legacy_tiocsti_setting < 0)
SKIP(return,
"legacy_tiocsti sysctl not available (kernel < 6.2)");
/* Common skip conditions */
if (variant->test_type == TEST_PTY_TIOCSTI_BASIC && !self->has_pty)
SKIP(return, "PTY not available for controlling terminal test");
if (variant->test_type == TEST_PTY_TIOCSTI_FD_PASSING &&
!self->initial_cap_sys_admin)
SKIP(return, "FD Pass tests require CAP_SYS_ADMIN");
if (variant->requires_cap && !self->initial_cap_sys_admin)
SKIP(return, "Test requires initial CAP_SYS_ADMIN");
/* Test if we can modify the sysctl (requires appropriate privileges) */
self->can_modify_sysctl = set_legacy_tiocsti_setting(
_metadata, self->original_legacy_tiocsti_setting);
/* Sysctl setup based on variant */
if (self->can_modify_sysctl &&
self->original_legacy_tiocsti_setting != variant->legacy_tiocsti) {
if (!set_legacy_tiocsti_setting(_metadata,
variant->legacy_tiocsti))
SKIP(return, "Failed to set legacy_tiocsti sysctl");
} else if (!self->can_modify_sysctl &&
self->original_legacy_tiocsti_setting !=
variant->legacy_tiocsti)
SKIP(return, "legacy_tiocsti setting mismatch");
}
FIXTURE_TEARDOWN(tiocsti)
{
/*
* Backup restoration -
* each test should restore its own sysctl changes
*/
if (self->can_modify_sysctl) {
int current_value = get_legacy_tiocsti_setting(_metadata);
if (current_value != self->original_legacy_tiocsti_setting) {
TH_LOG("Backup: Restoring legacy_tiocsti from %d to %d",
current_value,
self->original_legacy_tiocsti_setting);
set_legacy_tiocsti_setting(
_metadata,
self->original_legacy_tiocsti_setting);
}
}
if (self->has_pty) {
if (self->pty_master_fd >= 0)
close(self->pty_master_fd);
if (self->pty_slave_fd >= 0)
close(self->pty_slave_fd);
}
}
TEST_F(tiocsti, test)
{
int status;
pid_t child_pid;
if (variant->test_type == TEST_PTY_TIOCSTI_BASIC) {
/* ===== BASIC TIOCSTI TEST ===== */
child_pid = fork();
ASSERT_GE(child_pid, 0);
/* Perform the actual test in the child process */
if (child_pid == 0)
run_basic_tiocsti_test(_metadata, self, variant);
} else {
/* ===== FD PASSING SECURITY TEST ===== */
int sockpair[2];
ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair), 0);
child_pid = fork();
ASSERT_GE(child_pid, 0);
if (child_pid == 0) {
/* Child process - create PTY and send FD */
close(sockpair[0]);
run_fdpass_tiocsti_test(_metadata, variant,
sockpair[1]);
}
/* Parent process - receive FD and test TIOCSTI */
close(sockpair[1]);
int received_fd = recv_fd_via_socket(sockpair[0]);
ASSERT_GE(received_fd, 0);
bool parent_has_cap = self->initial_cap_sys_admin;
TH_LOG("=== TIOCSTI FD Passing Test Context ===");
TH_LOG("legacy_tiocsti: %d, Parent CAP_SYS_ADMIN: %s, Child: %s",
variant->legacy_tiocsti, parent_has_cap ? "yes" : "no",
variant->requires_cap ? "kept" : "dropped");
/* SECURITY TEST: Try TIOCSTI with FD opened by child */
int result = test_tiocsti_injection(_metadata, received_fd);
/* Log security concern if demonstrated */
if (result == 0 && !variant->requires_cap) {
TH_LOG("*** SECURITY CONCERN DEMONSTRATED ***");
TH_LOG("Privileged parent can use TIOCSTI on FD from unprivileged child");
TH_LOG("This shows current process credentials are used, not opener credentials");
}
EXPECT_EQ(result, variant->expected_success)
{
TH_LOG("FD passing: expected error %d, got %d",
variant->expected_success, result);
}
/* Signal child completion */
char sync_byte = 'D';
ssize_t bytes_written = write(sockpair[0], &sync_byte, 1);
ASSERT_EQ(bytes_written, 1);
close(received_fd);
close(sockpair[0]);
}
/* Common child process cleanup for both test types */
ASSERT_EQ(waitpid(child_pid, &status, 0), child_pid);
if (WIFSIGNALED(status)) {
TH_LOG("Child terminated by signal %d", WTERMSIG(status));
ASSERT_FALSE(WIFSIGNALED(status))
{
TH_LOG("Child process failed assertion");
}
} else {
EXPECT_EQ(WEXITSTATUS(status), 0);
}
}
TEST_HARNESS_MAIN