mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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:
@@ -125,6 +125,8 @@ properties:
|
|||||||
- nxp,lpc1850-uart
|
- nxp,lpc1850-uart
|
||||||
- opencores,uart16550-rtlsvn105
|
- opencores,uart16550-rtlsvn105
|
||||||
- ti,da830-uart
|
- ti,da830-uart
|
||||||
|
- loongson,ls2k0500-uart
|
||||||
|
- loongson,ls2k1500-uart
|
||||||
- const: ns16550a
|
- const: ns16550a
|
||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
@@ -169,6 +171,18 @@ properties:
|
|||||||
- nvidia,tegra194-uart
|
- nvidia,tegra194-uart
|
||||||
- nvidia,tegra234-uart
|
- nvidia,tegra234-uart
|
||||||
- const: nvidia,tegra20-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:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ properties:
|
|||||||
power-domains:
|
power-domains:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
uart-has-rtscts: false
|
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ properties:
|
|||||||
- const: samsung,exynos850-uart
|
- const: samsung,exynos850-uart
|
||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
|
- axis,artpec9-uart
|
||||||
- samsung,exynos7870-uart
|
- samsung,exynos7870-uart
|
||||||
|
- samsung,exynos8890-uart
|
||||||
- const: samsung,exynos8895-uart
|
- const: samsung,exynos8895-uart
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ properties:
|
|||||||
- rockchip,rk3328-uart
|
- rockchip,rk3328-uart
|
||||||
- rockchip,rk3368-uart
|
- rockchip,rk3368-uart
|
||||||
- rockchip,rk3399-uart
|
- rockchip,rk3399-uart
|
||||||
|
- rockchip,rk3506-uart
|
||||||
- rockchip,rk3528-uart
|
- rockchip,rk3528-uart
|
||||||
- rockchip,rk3562-uart
|
- rockchip,rk3562-uart
|
||||||
- rockchip,rk3568-uart
|
- rockchip,rk3568-uart
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ CONFIG_KEYBOARD_GPIO_POLLED=y
|
|||||||
# CONFIG_VT is not set
|
# CONFIG_VT is not set
|
||||||
# CONFIG_LEGACY_PTYS is not set
|
# CONFIG_LEGACY_PTYS is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_8250_NR_UARTS=6
|
CONFIG_SERIAL_8250_NR_UARTS=6
|
||||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=6
|
CONFIG_SERIAL_8250_RUNTIME_UARTS=6
|
||||||
|
|||||||
@@ -138,7 +138,6 @@ CONFIG_SERIO_RAW=y
|
|||||||
# CONFIG_VT is not set
|
# CONFIG_VT is not set
|
||||||
# CONFIG_LEGACY_PTYS is not set
|
# CONFIG_LEGACY_PTYS is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_8250_NR_UARTS=6
|
CONFIG_SERIAL_8250_NR_UARTS=6
|
||||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=6
|
CONFIG_SERIAL_8250_RUNTIME_UARTS=6
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ CONFIG_NETDEVICES=y
|
|||||||
CONFIG_HIX5HD2_GMAC=y
|
CONFIG_HIX5HD2_GMAC=y
|
||||||
CONFIG_HIP04_ETH=y
|
CONFIG_HIP04_ETH=y
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_8250_NR_UARTS=2
|
CONFIG_SERIAL_8250_NR_UARTS=2
|
||||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=2
|
CONFIG_SERIAL_8250_RUNTIME_UARTS=2
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ CONFIG_KEYBOARD_GPIO_POLLED=y
|
|||||||
# CONFIG_UNIX98_PTYS is not set
|
# CONFIG_UNIX98_PTYS is not set
|
||||||
# CONFIG_LEGACY_PTYS is not set
|
# CONFIG_LEGACY_PTYS is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
CONFIG_SERIAL_NONSTANDARD=y
|
CONFIG_SERIAL_NONSTANDARD=y
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ CONFIG_INPUT_DA9063_ONKEY=y
|
|||||||
CONFIG_INPUT_ADXL34X=y
|
CONFIG_INPUT_ADXL34X=y
|
||||||
# CONFIG_LEGACY_PTYS is not set
|
# CONFIG_LEGACY_PTYS is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
# CONFIG_SERIAL_8250_16550A_VARIANTS is not set
|
# CONFIG_SERIAL_8250_16550A_VARIANTS is not set
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
# CONFIG_SERIAL_8250_PCI is not set
|
# CONFIG_SERIAL_8250_PCI is not set
|
||||||
|
|||||||
@@ -380,7 +380,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
uart0: serial@1ff40800 {
|
uart0: serial@1ff40800 {
|
||||||
compatible = "ns16550a";
|
compatible = "loongson,ls2k0500-uart", "ns16550a";
|
||||||
reg = <0x0 0x1ff40800 0x0 0x10>;
|
reg = <0x0 0x1ff40800 0x0 0x10>;
|
||||||
clock-frequency = <100000000>;
|
clock-frequency = <100000000>;
|
||||||
interrupt-parent = <&eiointc>;
|
interrupt-parent = <&eiointc>;
|
||||||
|
|||||||
@@ -297,7 +297,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
uart0: serial@1fe20000 {
|
uart0: serial@1fe20000 {
|
||||||
compatible = "ns16550a";
|
compatible = "loongson,ls2k1000-uart", "loongson,ls2k0500-uart", "ns16550a";
|
||||||
reg = <0x0 0x1fe20000 0x0 0x10>;
|
reg = <0x0 0x1fe20000 0x0 0x10>;
|
||||||
clock-frequency = <125000000>;
|
clock-frequency = <125000000>;
|
||||||
interrupt-parent = <&liointc0>;
|
interrupt-parent = <&liointc0>;
|
||||||
|
|||||||
@@ -250,7 +250,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
uart0: serial@1fe001e0 {
|
uart0: serial@1fe001e0 {
|
||||||
compatible = "ns16550a";
|
compatible = "loongson,ls2k2000-uart", "loongson,ls2k1500-uart", "ns16550a";
|
||||||
reg = <0x0 0x1fe001e0 0x0 0x10>;
|
reg = <0x0 0x1fe001e0 0x0 0x10>;
|
||||||
clock-frequency = <100000000>;
|
clock-frequency = <100000000>;
|
||||||
interrupt-parent = <&liointc>;
|
interrupt-parent = <&liointc>;
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ CONFIG_B43LEGACY=y
|
|||||||
CONFIG_BRCMSMAC=y
|
CONFIG_BRCMSMAC=y
|
||||||
CONFIG_ISDN=y
|
CONFIG_ISDN=y
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
# CONFIG_SERIAL_8250_PCI is not set
|
# CONFIG_SERIAL_8250_PCI is not set
|
||||||
CONFIG_SERIAL_8250_NR_UARTS=2
|
CONFIG_SERIAL_8250_NR_UARTS=2
|
||||||
|
|||||||
@@ -119,7 +119,6 @@ CONFIG_INPUT_UINPUT=y
|
|||||||
CONFIG_VT=y
|
CONFIG_VT=y
|
||||||
CONFIG_VT_HW_CONSOLE_BINDING=y
|
CONFIG_VT_HW_CONSOLE_BINDING=y
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
# CONFIG_HW_RANDOM is not set
|
# CONFIG_HW_RANDOM is not set
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ CONFIG_INPUT_UINPUT=y
|
|||||||
CONFIG_INPUT_PWM_VIBRA=y
|
CONFIG_INPUT_PWM_VIBRA=y
|
||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_8250_INGENIC=y
|
CONFIG_SERIAL_8250_INGENIC=y
|
||||||
CONFIG_HW_RANDOM=y
|
CONFIG_HW_RANDOM=y
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ CONFIG_MARVELL_PHY=y
|
|||||||
# CONFIG_SERIO_SERPORT is not set
|
# CONFIG_SERIO_SERPORT is not set
|
||||||
# CONFIG_VT is not set
|
# CONFIG_VT is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
CONFIG_SERIAL_ALTERA_JTAGUART=y
|
CONFIG_SERIAL_ALTERA_JTAGUART=y
|
||||||
|
|||||||
@@ -119,7 +119,6 @@ CONFIG_INPUT_MISC=y
|
|||||||
CONFIG_INPUT_UINPUT=m
|
CONFIG_INPUT_UINPUT=m
|
||||||
CONFIG_LEGACY_PTY_COUNT=64
|
CONFIG_LEGACY_PTY_COUNT=64
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_8250_NR_UARTS=8
|
CONFIG_SERIAL_8250_NR_UARTS=8
|
||||||
CONFIG_SERIAL_8250_EXTENDED=y
|
CONFIG_SERIAL_8250_EXTENDED=y
|
||||||
|
|||||||
@@ -158,7 +158,6 @@ CONFIG_SERIO_SERPORT=m
|
|||||||
CONFIG_SERIO_RAW=m
|
CONFIG_SERIO_RAW=m
|
||||||
# CONFIG_LEGACY_PTYS is not set
|
# CONFIG_LEGACY_PTYS is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_8250_NR_UARTS=8
|
CONFIG_SERIAL_8250_NR_UARTS=8
|
||||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=8
|
CONFIG_SERIAL_8250_RUNTIME_UARTS=8
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ CONFIG_IBM_EMAC=y
|
|||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
# CONFIG_VT is not set
|
# CONFIG_VT is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_8250_EXTENDED=y
|
CONFIG_SERIAL_8250_EXTENDED=y
|
||||||
CONFIG_SERIAL_8250_SHARE_IRQ=y
|
CONFIG_SERIAL_8250_SHARE_IRQ=y
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ CONFIG_LITEX_LITEETH=y
|
|||||||
# CONFIG_VT is not set
|
# CONFIG_VT is not set
|
||||||
# CONFIG_LEGACY_PTYS is not set
|
# CONFIG_LEGACY_PTYS is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
CONFIG_SERIAL_NONSTANDARD=y
|
CONFIG_SERIAL_NONSTANDARD=y
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ CONFIG_VIRTIO_BLK=y
|
|||||||
# CONFIG_LEGACY_PTYS is not set
|
# CONFIG_LEGACY_PTYS is not set
|
||||||
# CONFIG_LDISC_AUTOLOAD is not set
|
# CONFIG_LDISC_AUTOLOAD is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_8250_NR_UARTS=1
|
CONFIG_SERIAL_8250_NR_UARTS=1
|
||||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=1
|
CONFIG_SERIAL_8250_RUNTIME_UARTS=1
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ CONFIG_MARVELL_PHY=y
|
|||||||
# CONFIG_INPUT_MOUSE is not set
|
# CONFIG_INPUT_MOUSE is not set
|
||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
CONFIG_HW_RANDOM=y
|
CONFIG_HW_RANDOM=y
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ CONFIG_MARVELL_PHY=y
|
|||||||
# CONFIG_INPUT_MOUSE is not set
|
# CONFIG_INPUT_MOUSE is not set
|
||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
CONFIG_HW_RANDOM=y
|
CONFIG_HW_RANDOM=y
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ CONFIG_MARVELL_PHY=y
|
|||||||
# CONFIG_INPUT_MOUSE is not set
|
# CONFIG_INPUT_MOUSE is not set
|
||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
CONFIG_HW_RANDOM=y
|
CONFIG_HW_RANDOM=y
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ CONFIG_MARVELL_PHY=y
|
|||||||
# CONFIG_INPUT_MOUSE is not set
|
# CONFIG_INPUT_MOUSE is not set
|
||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
CONFIG_HW_RANDOM=y
|
CONFIG_HW_RANDOM=y
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ CONFIG_MARVELL_PHY=y
|
|||||||
# CONFIG_INPUT_MOUSE is not set
|
# CONFIG_INPUT_MOUSE is not set
|
||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
CONFIG_SERIAL_8250=y
|
CONFIG_SERIAL_8250=y
|
||||||
# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
|
||||||
CONFIG_SERIAL_8250_CONSOLE=y
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
CONFIG_SERIAL_OF_PLATFORM=y
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
# CONFIG_HWMON is not set
|
# CONFIG_HWMON is not set
|
||||||
|
|||||||
@@ -487,25 +487,20 @@ static void moxa_wait_finish(void __iomem *ofsAddr)
|
|||||||
|
|
||||||
static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
|
static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
guard(spinlock_irqsave)(&moxafunc_lock);
|
||||||
spin_lock_irqsave(&moxafunc_lock, flags);
|
|
||||||
writew(arg, ofsAddr + FuncArg);
|
writew(arg, ofsAddr + FuncArg);
|
||||||
writew(cmd, ofsAddr + FuncCode);
|
writew(cmd, ofsAddr + FuncCode);
|
||||||
moxa_wait_finish(ofsAddr);
|
moxa_wait_finish(ofsAddr);
|
||||||
spin_unlock_irqrestore(&moxafunc_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
|
static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
guard(spinlock_irqsave)(&moxafunc_lock);
|
||||||
u16 ret;
|
|
||||||
spin_lock_irqsave(&moxafunc_lock, flags);
|
|
||||||
writew(arg, ofsAddr + FuncArg);
|
writew(arg, ofsAddr + FuncArg);
|
||||||
writew(cmd, ofsAddr + FuncCode);
|
writew(cmd, ofsAddr + FuncCode);
|
||||||
moxa_wait_finish(ofsAddr);
|
moxa_wait_finish(ofsAddr);
|
||||||
ret = readw(ofsAddr + FuncArg);
|
|
||||||
spin_unlock_irqrestore(&moxafunc_lock, flags);
|
return readw(ofsAddr + FuncArg);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void moxa_low_water_check(void __iomem *ofsAddr)
|
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)
|
if (ret)
|
||||||
goto err_free;
|
goto err_free;
|
||||||
|
|
||||||
spin_lock_bh(&moxa_lock);
|
scoped_guard(spinlock_bh, &moxa_lock) {
|
||||||
brd->ready = 1;
|
brd->ready = 1;
|
||||||
if (!timer_pending(&moxaTimer))
|
if (!timer_pending(&moxaTimer))
|
||||||
mod_timer(&moxaTimer, jiffies + HZ / 50);
|
mod_timer(&moxaTimer, jiffies + HZ / 50);
|
||||||
spin_unlock_bh(&moxa_lock);
|
}
|
||||||
|
|
||||||
first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD;
|
first_idx = (brd - moxa_boards) * MAX_PORTS_PER_BOARD;
|
||||||
for (i = 0; i < brd->numPorts; i++)
|
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;
|
unsigned int a, opened, first_idx;
|
||||||
|
|
||||||
mutex_lock(&moxa_openlock);
|
scoped_guard(mutex, &moxa_openlock) {
|
||||||
spin_lock_bh(&moxa_lock);
|
scoped_guard(spinlock_bh, &moxa_lock)
|
||||||
brd->ready = 0;
|
brd->ready = 0;
|
||||||
spin_unlock_bh(&moxa_lock);
|
|
||||||
|
|
||||||
/* pci hot-un-plug support */
|
/* 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;
|
|
||||||
for (a = 0; a < brd->numPorts; a++)
|
for (a = 0; a < brd->numPorts; a++)
|
||||||
if (tty_port_initialized(&brd->ports[a].port))
|
if (tty_port_initialized(&brd->ports[a].port))
|
||||||
opened++;
|
tty_port_tty_hangup(&brd->ports[a].port, false);
|
||||||
mutex_unlock(&moxa_openlock);
|
|
||||||
if (!opened)
|
for (a = 0; a < MAX_PORTS_PER_BOARD; a++)
|
||||||
break;
|
tty_port_destroy(&brd->ports[a].port);
|
||||||
msleep(50);
|
|
||||||
mutex_lock(&moxa_openlock);
|
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;
|
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)
|
static bool moxa_carrier_raised(struct tty_port *port)
|
||||||
{
|
{
|
||||||
struct moxa_port *ch = container_of(port, struct moxa_port, port);
|
struct moxa_port *ch = container_of(port, struct moxa_port, port);
|
||||||
int dcd;
|
|
||||||
|
|
||||||
spin_lock_irq(&port->lock);
|
guard(spinlock_irq)(&port->lock);
|
||||||
dcd = ch->DCDState;
|
return ch->DCDState;
|
||||||
spin_unlock_irq(&port->lock);
|
|
||||||
return dcd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void moxa_dtr_rts(struct tty_port *port, bool active)
|
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_board_conf *brd;
|
||||||
struct moxa_port *ch;
|
struct moxa_port *ch;
|
||||||
int port;
|
int port = tty->index;
|
||||||
|
|
||||||
port = tty->index;
|
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &moxa_openlock) {
|
||||||
if (mutex_lock_interruptible(&moxa_openlock))
|
brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
|
||||||
return -ERESTARTSYS;
|
if (!brd->ready)
|
||||||
brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
|
return -ENODEV;
|
||||||
if (!brd->ready) {
|
|
||||||
mutex_unlock(&moxa_openlock);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) {
|
if (port % MAX_PORTS_PER_BOARD >= brd->numPorts)
|
||||||
mutex_unlock(&moxa_openlock);
|
return -ENODEV;
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
|
ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
|
||||||
ch->port.count++;
|
ch->port.count++;
|
||||||
tty->driver_data = ch;
|
tty->driver_data = ch;
|
||||||
tty_port_tty_set(&ch->port, tty);
|
tty_port_tty_set(&ch->port, tty);
|
||||||
mutex_lock(&ch->port.mutex);
|
|
||||||
if (!tty_port_initialized(&ch->port)) {
|
guard(mutex)(&ch->port.mutex);
|
||||||
ch->statusflags = 0;
|
if (!tty_port_initialized(&ch->port)) {
|
||||||
moxa_set_tty_param(tty, &tty->termios);
|
ch->statusflags = 0;
|
||||||
MoxaPortLineCtrl(ch, true, true);
|
moxa_set_tty_param(tty, &tty->termios);
|
||||||
MoxaPortEnable(ch);
|
MoxaPortLineCtrl(ch, true, true);
|
||||||
MoxaSetFifo(ch, ch->type == PORT_16550A);
|
MoxaPortEnable(ch);
|
||||||
tty_port_set_initialized(&ch->port, true);
|
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);
|
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)
|
static ssize_t moxa_write(struct tty_struct *tty, const u8 *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct moxa_port *ch = tty->driver_data;
|
struct moxa_port *ch = tty->driver_data;
|
||||||
unsigned long flags;
|
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
if (ch == NULL)
|
if (ch == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&moxa_lock, flags);
|
scoped_guard(spinlock_irqsave, &moxa_lock)
|
||||||
len = MoxaPortWriteData(tty, buf, count);
|
len = MoxaPortWriteData(tty, buf, count);
|
||||||
spin_unlock_irqrestore(&moxa_lock, flags);
|
|
||||||
|
|
||||||
set_bit(LOWWAIT, &ch->statusflags);
|
set_bit(LOWWAIT, &ch->statusflags);
|
||||||
return len;
|
return len;
|
||||||
@@ -1349,12 +1333,10 @@ static int moxa_tiocmset(struct tty_struct *tty,
|
|||||||
bool dtr_active, rts_active;
|
bool dtr_active, rts_active;
|
||||||
struct moxa_port *ch;
|
struct moxa_port *ch;
|
||||||
|
|
||||||
mutex_lock(&moxa_openlock);
|
guard(mutex)(&moxa_openlock);
|
||||||
ch = tty->driver_data;
|
ch = tty->driver_data;
|
||||||
if (!ch) {
|
if (!ch)
|
||||||
mutex_unlock(&moxa_openlock);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
|
||||||
|
|
||||||
MoxaPortGetLineOut(ch, &dtr_active, &rts_active);
|
MoxaPortGetLineOut(ch, &dtr_active, &rts_active);
|
||||||
if (set & TIOCM_RTS)
|
if (set & TIOCM_RTS)
|
||||||
@@ -1366,7 +1348,7 @@ static int moxa_tiocmset(struct tty_struct *tty,
|
|||||||
if (clear & TIOCM_DTR)
|
if (clear & TIOCM_DTR)
|
||||||
dtr_active = false;
|
dtr_active = false;
|
||||||
MoxaPortLineCtrl(ch, dtr_active, rts_active);
|
MoxaPortLineCtrl(ch, dtr_active, rts_active);
|
||||||
mutex_unlock(&moxa_openlock);
|
|
||||||
return 0;
|
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)
|
static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
dcd = !!dcd;
|
dcd = !!dcd;
|
||||||
|
|
||||||
spin_lock_irqsave(&p->port.lock, flags);
|
scoped_guard(spinlock_irqsave, &p->port.lock) {
|
||||||
if (dcd != p->DCDState) {
|
if (dcd == p->DCDState)
|
||||||
p->DCDState = dcd;
|
return;
|
||||||
spin_unlock_irqrestore(&p->port.lock, flags);
|
|
||||||
if (!dcd)
|
p->DCDState = dcd;
|
||||||
tty_port_tty_hangup(&p->port, true);
|
|
||||||
}
|
}
|
||||||
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,
|
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;
|
u16 __iomem *ip;
|
||||||
unsigned int card, port, served = 0;
|
unsigned int card, port, served = 0;
|
||||||
|
|
||||||
spin_lock(&moxa_lock);
|
guard(spinlock)(&moxa_lock);
|
||||||
for (card = 0; card < MAX_BOARDS; card++) {
|
for (card = 0; card < MAX_BOARDS; card++) {
|
||||||
brd = &moxa_boards[card];
|
brd = &moxa_boards[card];
|
||||||
if (!brd->ready)
|
if (!brd->ready)
|
||||||
@@ -1525,7 +1506,6 @@ static void moxa_poll(struct timer_list *unused)
|
|||||||
|
|
||||||
if (served)
|
if (served)
|
||||||
mod_timer(&moxaTimer, jiffies + HZ / 50);
|
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);
|
baud = MoxaPortSetBaud(port, baud);
|
||||||
|
|
||||||
if (termio->c_iflag & (IXON | IXOFF | IXANY)) {
|
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[VSTART], ofsAddr + FuncArg);
|
||||||
writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
|
writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
|
||||||
writeb(FC_SetXonXoff, ofsAddr + FuncCode);
|
writeb(FC_SetXonXoff, ofsAddr + FuncCode);
|
||||||
moxa_wait_finish(ofsAddr);
|
moxa_wait_finish(ofsAddr);
|
||||||
spin_unlock_irq(&moxafunc_lock);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return baud;
|
return baud;
|
||||||
}
|
}
|
||||||
@@ -2098,13 +2076,13 @@ static int moxa_get_serial_info(struct tty_struct *tty,
|
|||||||
|
|
||||||
if (!info)
|
if (!info)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
mutex_lock(&info->port.mutex);
|
guard(mutex)(&info->port.mutex);
|
||||||
ss->type = info->type;
|
ss->type = info->type;
|
||||||
ss->line = info->port.tty->index;
|
ss->line = info->port.tty->index;
|
||||||
ss->flags = info->port.flags;
|
ss->flags = info->port.flags;
|
||||||
ss->baud_base = 921600;
|
ss->baud_base = 921600;
|
||||||
ss->close_delay = jiffies_to_msecs(info->port.close_delay) / 10;
|
ss->close_delay = jiffies_to_msecs(info->port.close_delay) / 10;
|
||||||
mutex_unlock(&info->port.mutex);
|
|
||||||
return 0;
|
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);
|
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 (!capable(CAP_SYS_ADMIN)) {
|
||||||
if (close_delay != info->port.close_delay ||
|
if (close_delay != info->port.close_delay ||
|
||||||
ss->type != info->type ||
|
ss->type != info->type ||
|
||||||
((ss->flags & ~ASYNC_USR_MASK) !=
|
((ss->flags & ~ASYNC_USR_MASK) !=
|
||||||
(info->port.flags & ~ASYNC_USR_MASK))) {
|
(info->port.flags & ~ASYNC_USR_MASK))) {
|
||||||
mutex_unlock(&info->port.mutex);
|
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -2136,7 +2113,7 @@ static int moxa_set_serial_info(struct tty_struct *tty,
|
|||||||
|
|
||||||
info->type = ss->type;
|
info->type = ss->type;
|
||||||
}
|
}
|
||||||
mutex_unlock(&info->port.mutex);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
* 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
|
* Send an initial MSC message after DLCI open to set the initial
|
||||||
* modem status lines. This is only done for basic mode.
|
* modem status lines. This is only done for basic mode.
|
||||||
|
|||||||
@@ -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)
|
static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
struct n_hdlc_buf *tbuf;
|
struct n_hdlc_buf *tbuf;
|
||||||
ssize_t actual;
|
ssize_t actual;
|
||||||
|
|
||||||
check_again:
|
check_again:
|
||||||
|
scoped_guard(spinlock_irqsave, &n_hdlc->tx_buf_list.spinlock) {
|
||||||
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
|
if (n_hdlc->tbusy) {
|
||||||
if (n_hdlc->tbusy) {
|
n_hdlc->woke_up = true;
|
||||||
n_hdlc->woke_up = true;
|
return;
|
||||||
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
|
}
|
||||||
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);
|
tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
|
||||||
while (tbuf) {
|
while (tbuf) {
|
||||||
@@ -324,9 +321,8 @@ check_again:
|
|||||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||||
|
|
||||||
/* Clear the re-entry flag */
|
/* Clear the re-entry flag */
|
||||||
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
|
scoped_guard(spinlock_irqsave, &n_hdlc->tx_buf_list.spinlock)
|
||||||
n_hdlc->tbusy = false;
|
n_hdlc->tbusy = false;
|
||||||
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
|
|
||||||
|
|
||||||
if (n_hdlc->woke_up)
|
if (n_hdlc->woke_up)
|
||||||
goto check_again;
|
goto check_again;
|
||||||
@@ -584,9 +580,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
|||||||
unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||||
int error = 0;
|
|
||||||
int count;
|
int count;
|
||||||
unsigned long flags;
|
|
||||||
struct n_hdlc_buf *buf = NULL;
|
struct n_hdlc_buf *buf = NULL;
|
||||||
|
|
||||||
pr_debug("%s() called %d\n", __func__, cmd);
|
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:
|
case FIONREAD:
|
||||||
/* report count of read data available */
|
/* report count of read data available */
|
||||||
/* in next available frame (if any) */
|
/* in next available frame (if any) */
|
||||||
spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock, flags);
|
scoped_guard(spinlock_irqsave, &n_hdlc->rx_buf_list.spinlock) {
|
||||||
buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
|
buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
|
||||||
struct n_hdlc_buf, list_item);
|
struct n_hdlc_buf, list_item);
|
||||||
if (buf)
|
if (buf)
|
||||||
count = buf->count;
|
count = buf->count;
|
||||||
else
|
else
|
||||||
count = 0;
|
count = 0;
|
||||||
spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock, flags);
|
}
|
||||||
error = put_user(count, (int __user *)arg);
|
return put_user(count, (int __user *)arg);
|
||||||
break;
|
|
||||||
|
|
||||||
case TIOCOUTQ:
|
case TIOCOUTQ:
|
||||||
/* get the pending tx byte count in the driver */
|
/* get the pending tx byte count in the driver */
|
||||||
count = tty_chars_in_buffer(tty);
|
count = tty_chars_in_buffer(tty);
|
||||||
/* add size of next output frame in queue */
|
/* add size of next output frame in queue */
|
||||||
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
|
scoped_guard(spinlock_irqsave, &n_hdlc->tx_buf_list.spinlock) {
|
||||||
buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
|
buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
|
||||||
struct n_hdlc_buf, list_item);
|
struct n_hdlc_buf, list_item);
|
||||||
if (buf)
|
if (buf)
|
||||||
count += buf->count;
|
count += buf->count;
|
||||||
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
|
}
|
||||||
error = put_user(count, (int __user *)arg);
|
return put_user(count, (int __user *)arg);
|
||||||
break;
|
|
||||||
|
|
||||||
case TCFLSH:
|
case TCFLSH:
|
||||||
switch (arg) {
|
switch (arg) {
|
||||||
@@ -628,11 +620,8 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
|||||||
fallthrough; /* to default */
|
fallthrough; /* to default */
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error = n_tty_ioctl_helper(tty, cmd, arg);
|
return n_tty_ioctl_helper(tty, cmd, arg);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return error;
|
|
||||||
|
|
||||||
} /* end of n_hdlc_tty_ioctl() */
|
} /* 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,
|
static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
|
||||||
struct n_hdlc_buf *buf)
|
struct n_hdlc_buf *buf)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
guard(spinlock_irqsave)(&buf_list->spinlock);
|
||||||
|
|
||||||
spin_lock_irqsave(&buf_list->spinlock, flags);
|
|
||||||
|
|
||||||
list_add(&buf->list_item, &buf_list->list);
|
list_add(&buf->list_item, &buf_list->list);
|
||||||
buf_list->count++;
|
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,
|
static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
|
||||||
struct n_hdlc_buf *buf)
|
struct n_hdlc_buf *buf)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
guard(spinlock_irqsave)(&buf_list->spinlock);
|
||||||
|
|
||||||
spin_lock_irqsave(&buf_list->spinlock, flags);
|
|
||||||
|
|
||||||
list_add_tail(&buf->list_item, &buf_list->list);
|
list_add_tail(&buf->list_item, &buf_list->list);
|
||||||
buf_list->count++;
|
buf_list->count++;
|
||||||
|
|
||||||
spin_unlock_irqrestore(&buf_list->spinlock, flags);
|
|
||||||
} /* end of n_hdlc_buf_put() */
|
} /* 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)
|
static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
struct n_hdlc_buf *buf;
|
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,
|
buf = list_first_entry_or_null(&buf_list->list,
|
||||||
struct n_hdlc_buf, list_item);
|
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--;
|
buf_list->count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&buf_list->spinlock, flags);
|
|
||||||
return buf;
|
return buf;
|
||||||
} /* end of n_hdlc_buf_get() */
|
} /* end of n_hdlc_buf_get() */
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
static void n_tty_packet_mode_flush(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
if (tty->link->ctrl.packet) {
|
if (tty->link->ctrl.packet) {
|
||||||
spin_lock_irqsave(&tty->ctrl.lock, flags);
|
scoped_guard(spinlock_irqsave, &tty->ctrl.lock)
|
||||||
tty->ctrl.pktstatus |= TIOCPKT_FLUSHREAD;
|
tty->ctrl.pktstatus |= TIOCPKT_FLUSHREAD;
|
||||||
spin_unlock_irqrestore(&tty->ctrl.lock, flags);
|
|
||||||
wake_up_interruptible(&tty->link->read_wait);
|
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)
|
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);
|
reset_buffer_flags(tty->disc_data);
|
||||||
n_tty_kick_worker(tty);
|
n_tty_kick_worker(tty);
|
||||||
|
|
||||||
if (tty->link)
|
if (tty->link)
|
||||||
n_tty_packet_mode_flush(tty);
|
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 nr, old, echoed;
|
||||||
size_t head;
|
size_t head;
|
||||||
|
|
||||||
mutex_lock(&ldata->output_lock);
|
scoped_guard(mutex, &ldata->output_lock) {
|
||||||
head = ldata->echo_head;
|
head = ldata->echo_head;
|
||||||
ldata->echo_mark = head;
|
ldata->echo_mark = head;
|
||||||
old = ldata->echo_commit - ldata->echo_tail;
|
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
|
* Process committed echoes if the accumulated # of bytes is over the threshold
|
||||||
* block is accumulated) */
|
* (and try again each time another block is accumulated)
|
||||||
nr = head - ldata->echo_tail;
|
*/
|
||||||
if (nr < ECHO_COMMIT_WATERMARK ||
|
nr = head - ldata->echo_tail;
|
||||||
(nr % ECHO_BLOCK > old % ECHO_BLOCK)) {
|
if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK))
|
||||||
mutex_unlock(&ldata->output_lock);
|
return;
|
||||||
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)
|
if (echoed && tty->ops->flush_chars)
|
||||||
tty->ops->flush_chars(tty);
|
tty->ops->flush_chars(tty);
|
||||||
}
|
}
|
||||||
@@ -768,10 +762,10 @@ static void process_echoes(struct tty_struct *tty)
|
|||||||
if (ldata->echo_mark == ldata->echo_tail)
|
if (ldata->echo_mark == ldata->echo_tail)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&ldata->output_lock);
|
scoped_guard(mutex, &ldata->output_lock) {
|
||||||
ldata->echo_commit = ldata->echo_mark;
|
ldata->echo_commit = ldata->echo_mark;
|
||||||
echoed = __process_echoes(tty);
|
echoed = __process_echoes(tty);
|
||||||
mutex_unlock(&ldata->output_lock);
|
}
|
||||||
|
|
||||||
if (echoed && tty->ops->flush_chars)
|
if (echoed && tty->ops->flush_chars)
|
||||||
tty->ops->flush_chars(tty);
|
tty->ops->flush_chars(tty);
|
||||||
@@ -786,10 +780,9 @@ static void flush_echoes(struct tty_struct *tty)
|
|||||||
ldata->echo_commit == ldata->echo_head)
|
ldata->echo_commit == ldata->echo_head)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&ldata->output_lock);
|
guard(mutex)(&ldata->output_lock);
|
||||||
ldata->echo_commit = ldata->echo_head;
|
ldata->echo_commit = ldata->echo_head;
|
||||||
__process_echoes(tty);
|
__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)) {
|
if (L_NOFLSH(tty)) {
|
||||||
/* signal only */
|
/* signal only */
|
||||||
__isig(sig, tty);
|
__isig(sig, tty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
} else { /* signal and flush */
|
/* signal and flush */
|
||||||
up_read(&tty->termios_rwsem);
|
up_read(&tty->termios_rwsem);
|
||||||
down_write(&tty->termios_rwsem);
|
scoped_guard(rwsem_write, &tty->termios_rwsem) {
|
||||||
|
|
||||||
__isig(sig, tty);
|
__isig(sig, tty);
|
||||||
|
|
||||||
/* clear echo buffer */
|
/* clear echo buffer */
|
||||||
mutex_lock(&ldata->output_lock);
|
scoped_guard(mutex, &ldata->output_lock) {
|
||||||
ldata->echo_head = ldata->echo_tail = 0;
|
ldata->echo_head = ldata->echo_tail = 0;
|
||||||
ldata->echo_mark = ldata->echo_commit = 0;
|
ldata->echo_mark = ldata->echo_commit = 0;
|
||||||
mutex_unlock(&ldata->output_lock);
|
}
|
||||||
|
|
||||||
/* clear output buffer */
|
/* clear output buffer */
|
||||||
tty_driver_flush_buffer(tty);
|
tty_driver_flush_buffer(tty);
|
||||||
@@ -1100,10 +1094,8 @@ static void isig(int sig, struct tty_struct *tty)
|
|||||||
/* notify pty master of flush */
|
/* notify pty master of flush */
|
||||||
if (tty->link)
|
if (tty->link)
|
||||||
n_tty_packet_mode_flush(tty);
|
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;
|
size_t n, rcvd = 0;
|
||||||
int room, overflow;
|
int room, overflow;
|
||||||
|
|
||||||
down_read(&tty->termios_rwsem);
|
guard(rwsem_read)(&tty->termios_rwsem);
|
||||||
|
|
||||||
do {
|
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);
|
n_tty_kick_worker(tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
up_read(&tty->termios_rwsem);
|
|
||||||
|
|
||||||
return rcvd;
|
return rcvd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1879,10 +1869,9 @@ static void n_tty_close(struct tty_struct *tty)
|
|||||||
if (tty->link)
|
if (tty->link)
|
||||||
n_tty_packet_mode_flush(tty);
|
n_tty_packet_mode_flush(tty);
|
||||||
|
|
||||||
down_write(&tty->termios_rwsem);
|
guard(rwsem_write)(&tty->termios_rwsem);
|
||||||
vfree(ldata);
|
vfree(ldata);
|
||||||
tty->disc_data = NULL;
|
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;
|
u8 cs;
|
||||||
if (kb != kbuf)
|
if (kb != kbuf)
|
||||||
break;
|
break;
|
||||||
spin_lock_irq(&tty->link->ctrl.lock);
|
scoped_guard(spinlock_irq, &tty->link->ctrl.lock) {
|
||||||
cs = tty->link->ctrl.pktstatus;
|
cs = tty->link->ctrl.pktstatus;
|
||||||
tty->link->ctrl.pktstatus = 0;
|
tty->link->ctrl.pktstatus = 0;
|
||||||
spin_unlock_irq(&tty->link->ctrl.lock);
|
}
|
||||||
*kb++ = cs;
|
*kb++ = cs;
|
||||||
nr--;
|
nr--;
|
||||||
break;
|
break;
|
||||||
@@ -2357,7 +2346,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
down_read(&tty->termios_rwsem);
|
guard(rwsem_read)(&tty->termios_rwsem);
|
||||||
|
|
||||||
/* Write out any echoed characters that are still pending */
|
/* Write out any echoed characters that are still pending */
|
||||||
process_echoes(tty);
|
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;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
|
||||||
while (nr > 0) {
|
while (nr > 0) {
|
||||||
mutex_lock(&ldata->output_lock);
|
scoped_guard(mutex, &ldata->output_lock)
|
||||||
num = tty->ops->write(tty, b, nr);
|
num = tty->ops->write(tty, b, nr);
|
||||||
mutex_unlock(&ldata->output_lock);
|
|
||||||
if (num < 0) {
|
if (num < 0) {
|
||||||
retval = num;
|
retval = num;
|
||||||
goto break_out;
|
goto break_out;
|
||||||
@@ -2424,7 +2412,7 @@ break_out:
|
|||||||
remove_wait_queue(&tty->write_wait, &wait);
|
remove_wait_queue(&tty->write_wait, &wait);
|
||||||
if (nr && tty->fasync)
|
if (nr && tty->fasync)
|
||||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||||
up_read(&tty->termios_rwsem);
|
|
||||||
return (b - buf) ? b - buf : retval;
|
return (b - buf) ? b - buf : retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2498,12 +2486,11 @@ static int n_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
|||||||
case TIOCOUTQ:
|
case TIOCOUTQ:
|
||||||
return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
|
return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
|
||||||
case TIOCINQ:
|
case TIOCINQ:
|
||||||
down_write(&tty->termios_rwsem);
|
scoped_guard(rwsem_write, &tty->termios_rwsem)
|
||||||
if (L_ICANON(tty) && !L_EXTPROC(tty))
|
if (L_ICANON(tty) && !L_EXTPROC(tty))
|
||||||
num = inq_canon(ldata);
|
num = inq_canon(ldata);
|
||||||
else
|
else
|
||||||
num = read_cnt(ldata);
|
num = read_cnt(ldata);
|
||||||
up_write(&tty->termios_rwsem);
|
|
||||||
return put_user(num, (unsigned int __user *) arg);
|
return put_user(num, (unsigned int __user *) arg);
|
||||||
default:
|
default:
|
||||||
return n_tty_ioctl_helper(tty, cmd, arg);
|
return n_tty_ioctl_helper(tty, cmd, arg);
|
||||||
|
|||||||
@@ -57,9 +57,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
|
|||||||
set_bit(TTY_IO_ERROR, &tty->flags);
|
set_bit(TTY_IO_ERROR, &tty->flags);
|
||||||
wake_up_interruptible(&tty->read_wait);
|
wake_up_interruptible(&tty->read_wait);
|
||||||
wake_up_interruptible(&tty->write_wait);
|
wake_up_interruptible(&tty->write_wait);
|
||||||
spin_lock_irq(&tty->ctrl.lock);
|
scoped_guard(spinlock_irq, &tty->ctrl.lock)
|
||||||
tty->ctrl.packet = false;
|
tty->ctrl.packet = false;
|
||||||
spin_unlock_irq(&tty->ctrl.lock);
|
|
||||||
/* Review - krefs on tty_link ?? */
|
/* Review - krefs on tty_link ?? */
|
||||||
if (!tty->link)
|
if (!tty->link)
|
||||||
return;
|
return;
|
||||||
@@ -70,10 +69,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
|
|||||||
set_bit(TTY_OTHER_CLOSED, &tty->flags);
|
set_bit(TTY_OTHER_CLOSED, &tty->flags);
|
||||||
#ifdef CONFIG_UNIX98_PTYS
|
#ifdef CONFIG_UNIX98_PTYS
|
||||||
if (tty->driver == ptm_driver) {
|
if (tty->driver == ptm_driver) {
|
||||||
mutex_lock(&devpts_mutex);
|
guard(mutex)(&devpts_mutex);
|
||||||
if (tty->link->driver_data)
|
if (tty->link->driver_data)
|
||||||
devpts_pty_kill(tty->link->driver_data);
|
devpts_pty_kill(tty->link->driver_data);
|
||||||
mutex_unlock(&devpts_mutex);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
tty_vhangup(tty->link);
|
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 */
|
/* Set the packet mode on a pty */
|
||||||
static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
|
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;
|
return -EFAULT;
|
||||||
|
|
||||||
spin_lock_irq(&tty->ctrl.lock);
|
guard(spinlock_irq)(&tty->ctrl.lock);
|
||||||
if (pktmode) {
|
if (!want_pktmode) {
|
||||||
if (!tty->ctrl.packet) {
|
|
||||||
tty->link->ctrl.pktstatus = 0;
|
|
||||||
smp_mb();
|
|
||||||
tty->ctrl.packet = true;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
tty->ctrl.packet = false;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -210,10 +210,9 @@ static void pty_flush_buffer(struct tty_struct *tty)
|
|||||||
|
|
||||||
tty_buffer_flush(to, NULL);
|
tty_buffer_flush(to, NULL);
|
||||||
if (to->ctrl.packet) {
|
if (to->ctrl.packet) {
|
||||||
spin_lock_irq(&tty->ctrl.lock);
|
guard(spinlock_irq)(&tty->ctrl.lock);
|
||||||
tty->ctrl.pktstatus |= TIOCPKT_FLUSHWRITE;
|
tty->ctrl.pktstatus |= TIOCPKT_FLUSHWRITE;
|
||||||
wake_up_interruptible(&to->read_wait);
|
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' &&
|
STOP_CHAR(tty) == '\023' &&
|
||||||
START_CHAR(tty) == '\021');
|
START_CHAR(tty) == '\021');
|
||||||
if ((old_flow != new_flow) || extproc) {
|
if ((old_flow != new_flow) || extproc) {
|
||||||
spin_lock_irq(&tty->ctrl.lock);
|
scoped_guard(spinlock_irq, &tty->ctrl.lock) {
|
||||||
if (old_flow != new_flow) {
|
if (old_flow != new_flow) {
|
||||||
tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
|
tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
|
||||||
if (new_flow)
|
if (new_flow)
|
||||||
tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;
|
tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;
|
||||||
else
|
else
|
||||||
tty->ctrl.pktstatus |= TIOCPKT_NOSTOP;
|
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);
|
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;
|
struct tty_struct *pty = tty->link;
|
||||||
|
|
||||||
/* For a PTY we need to lock the tty side */
|
/* 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)))
|
if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
|
||||||
goto done;
|
return 0;
|
||||||
|
|
||||||
/* Signal the foreground process group of both ptys */
|
/* Signal the foreground process group of both ptys */
|
||||||
pgrp = tty_get_pgrp(tty);
|
pgrp = tty_get_pgrp(tty);
|
||||||
@@ -304,8 +303,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws)
|
|||||||
|
|
||||||
tty->winsize = *ws;
|
tty->winsize = *ws;
|
||||||
pty->winsize = *ws; /* Never used so will go away soon */
|
pty->winsize = *ws; /* Never used so will go away soon */
|
||||||
done:
|
|
||||||
mutex_unlock(&tty->winsize_mutex);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,28 +319,26 @@ done:
|
|||||||
*/
|
*/
|
||||||
static void pty_start(struct tty_struct *tty)
|
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) {
|
scoped_guard(spinlock_irqsave, &tty->ctrl.lock) {
|
||||||
spin_lock_irqsave(&tty->ctrl.lock, flags);
|
|
||||||
tty->ctrl.pktstatus &= ~TIOCPKT_STOP;
|
tty->ctrl.pktstatus &= ~TIOCPKT_STOP;
|
||||||
tty->ctrl.pktstatus |= TIOCPKT_START;
|
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)
|
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) {
|
scoped_guard(spinlock_irqsave, &tty->ctrl.lock) {
|
||||||
spin_lock_irqsave(&tty->ctrl.lock, flags);
|
|
||||||
tty->ctrl.pktstatus &= ~TIOCPKT_START;
|
tty->ctrl.pktstatus &= ~TIOCPKT_START;
|
||||||
tty->ctrl.pktstatus |= TIOCPKT_STOP;
|
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,
|
static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
|
||||||
struct file *file, int idx)
|
struct file *file, int idx)
|
||||||
{
|
{
|
||||||
struct tty_struct *tty;
|
guard(mutex)(&devpts_mutex);
|
||||||
|
|
||||||
mutex_lock(&devpts_mutex);
|
|
||||||
tty = devpts_get_priv(file->f_path.dentry);
|
|
||||||
mutex_unlock(&devpts_mutex);
|
|
||||||
/* Master must be open before slave */
|
/* Master must be open before slave */
|
||||||
if (!tty)
|
return devpts_get_priv(file->f_path.dentry) ? : ERR_PTR(-EIO);
|
||||||
return ERR_PTR(-EIO);
|
|
||||||
return tty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
|
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. */
|
/* find a device that is not in use. */
|
||||||
mutex_lock(&devpts_mutex);
|
scoped_guard(mutex, &devpts_mutex)
|
||||||
index = devpts_new_index(fsi);
|
index = devpts_new_index(fsi);
|
||||||
mutex_unlock(&devpts_mutex);
|
|
||||||
|
|
||||||
retval = index;
|
retval = index;
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
goto out_put_fsi;
|
goto out_put_fsi;
|
||||||
|
|
||||||
|
|
||||||
mutex_lock(&tty_mutex);
|
/* The tty returned here is locked so we can safely drop the mutex */
|
||||||
tty = tty_init_dev(ptm_driver, index);
|
scoped_guard(mutex, &tty_mutex)
|
||||||
/* The tty returned here is locked so we can safely
|
tty = tty_init_dev(ptm_driver, index);
|
||||||
drop the mutex */
|
|
||||||
mutex_unlock(&tty_mutex);
|
|
||||||
|
|
||||||
retval = PTR_ERR(tty);
|
retval = PTR_ERR(tty);
|
||||||
if (IS_ERR(tty))
|
if (IS_ERR(tty))
|
||||||
|
|||||||
@@ -98,15 +98,6 @@ struct serial8250_config {
|
|||||||
|
|
||||||
extern unsigned int nr_uarts;
|
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) \
|
#define SERIAL8250_PORT_FLAGS(_base, _irq, _flags) \
|
||||||
{ \
|
{ \
|
||||||
.iobase = _base, \
|
.iobase = _base, \
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ struct irq_info {
|
|||||||
static DEFINE_HASHTABLE(irq_lists, IRQ_HASH_BITS);
|
static DEFINE_HASHTABLE(irq_lists, IRQ_HASH_BITS);
|
||||||
static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */
|
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.
|
* This is the serial driver's interrupt routine.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ static int dw8250_clk_notifier_cb(struct notifier_block *nb,
|
|||||||
* deferred event handling complication.
|
* deferred event handling complication.
|
||||||
*/
|
*/
|
||||||
if (event == POST_RATE_CHANGE) {
|
if (event == POST_RATE_CHANGE) {
|
||||||
queue_work(system_unbound_wq, &d->clk_work);
|
queue_work(system_dfl_wq, &d->clk_work);
|
||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -680,7 +680,7 @@ static int dw8250_probe(struct platform_device *pdev)
|
|||||||
err = clk_notifier_register(data->clk, &data->clk_notifier);
|
err = clk_notifier_register(data->clk, &data->clk_notifier);
|
||||||
if (err)
|
if (err)
|
||||||
return dev_err_probe(dev, err, "Failed to set the clock notifier\n");
|
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);
|
platform_set_drvdata(pdev, data);
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev,
|
|||||||
unsigned char status;
|
unsigned char status;
|
||||||
int err;
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@@ -833,7 +833,7 @@ static int cti_port_setup_common(struct exar8250 *priv,
|
|||||||
port->port.port_id = idx;
|
port->port.port_id = idx;
|
||||||
port->port.uartclk = priv->osc_freq;
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|||||||
280
drivers/tty/serial/8250/8250_keba.c
Normal file
280
drivers/tty/serial/8250/8250_keba.c
Normal 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");
|
||||||
238
drivers/tty/serial/8250/8250_loongson.c
Normal file
238
drivers/tty/serial/8250/8250_loongson.c
Normal 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");
|
||||||
@@ -95,7 +95,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
|
|||||||
u32 spd;
|
u32 spd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
memset(port, 0, sizeof *port);
|
memset(port, 0, sizeof(*port));
|
||||||
|
|
||||||
pm_runtime_enable(&ofdev->dev);
|
pm_runtime_enable(&ofdev->dev);
|
||||||
pm_runtime_get_sync(&ofdev->dev);
|
pm_runtime_get_sync(&ofdev->dev);
|
||||||
|
|||||||
@@ -95,6 +95,11 @@
|
|||||||
#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381
|
#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381
|
||||||
#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683
|
#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 */
|
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
|
||||||
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
|
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
|
||||||
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588
|
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588
|
||||||
@@ -165,7 +170,15 @@ static int
|
|||||||
setup_port(struct serial_private *priv, struct uart_8250_port *port,
|
setup_port(struct serial_private *priv, struct uart_8250_port *port,
|
||||||
u8 bar, unsigned int offset, int regshift)
|
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,
|
0,
|
||||||
pbn_ADDIDATA_PCIe_8_3906250 },
|
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_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
|
||||||
PCI_VENDOR_ID_IBM, 0x0299,
|
PCI_VENDOR_ID_IBM, 0x0299,
|
||||||
0, 0, pbn_b0_bt_2_115200 },
|
0, 0, pbn_b0_bt_2_115200 },
|
||||||
|
|||||||
@@ -671,7 +671,7 @@ static int pci1xxxx_resume(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int pci1xxxx_setup(struct pci_dev *pdev,
|
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;
|
int ret;
|
||||||
|
|
||||||
@@ -698,12 +698,12 @@ static int pci1xxxx_setup(struct pci_dev *pdev,
|
|||||||
* C0 and later revisions support Burst operation.
|
* C0 and later revisions support Burst operation.
|
||||||
* RTS workaround in mctrl is applicable only to B0.
|
* RTS workaround in mctrl is applicable only to B0.
|
||||||
*/
|
*/
|
||||||
if (rev >= 0xC0)
|
if (priv->dev_rev >= 0xC0)
|
||||||
port->port.handle_irq = pci1xxxx_handle_irq;
|
port->port.handle_irq = pci1xxxx_handle_irq;
|
||||||
else if (rev == 0xB0)
|
else if (priv->dev_rev == 0xB0)
|
||||||
port->port.set_mctrl = pci1xxxx_set_mctrl;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -821,7 +821,7 @@ static int pci1xxxx_serial_probe(struct pci_dev *pdev,
|
|||||||
else
|
else
|
||||||
uart.port.irq = pci_irq_vector(pdev, 0);
|
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) {
|
if (rc) {
|
||||||
dev_warn(dev, "Failed to setup port %u\n", i);
|
dev_warn(dev, "Failed to setup port %u\n", i);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -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");
|
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,
|
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)
|
if (bar >= PCI_STD_NUM_BARS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
|
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.iotype = UPIO_MEM;
|
||||||
port->port.iobase = 0;
|
port->port.iobase = 0;
|
||||||
port->port.mapbase = pci_resource_start(dev, bar) + offset;
|
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;
|
port->port.regshift = regshift;
|
||||||
} else if (IS_ENABLED(CONFIG_HAS_IOPORT)) {
|
} else if (IS_ENABLED(CONFIG_HAS_IOPORT)) {
|
||||||
port->port.iotype = UPIO_PORT;
|
port->port.iotype = UPIO_PORT;
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ struct pci_dev;
|
|||||||
struct uart_8250_port;
|
struct uart_8250_port;
|
||||||
|
|
||||||
int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, u8 bar,
|
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);
|
int serial_8250_warn_need_ioport(struct pci_dev *dev);
|
||||||
|
|||||||
@@ -29,10 +29,8 @@
|
|||||||
* Configuration:
|
* Configuration:
|
||||||
* share_irqs: Whether we pass IRQF_SHARED to request_irq().
|
* share_irqs: Whether we pass IRQF_SHARED to request_irq().
|
||||||
* This option is unsafe when used on edge-triggered interrupts.
|
* 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;
|
static bool share_irqs = IS_ENABLED(CONFIG_SERIAL_8250_SHARE_IRQ);
|
||||||
unsigned int skip_txen_test;
|
|
||||||
|
|
||||||
unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
|
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)
|
static void __init __serial8250_isa_init_ports(void)
|
||||||
{
|
{
|
||||||
int i, irqflag = 0;
|
int i;
|
||||||
|
|
||||||
if (nr_uarts > UART_NR)
|
if (nr_uarts > UART_NR)
|
||||||
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_port_ops = *univ8250_port_base_ops;
|
||||||
univ8250_rsa_support(&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++) {
|
for (i = 0; i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++) {
|
||||||
struct uart_8250_port *up = serial8250_get_port(i);
|
struct uart_8250_port *up = serial8250_get_port(i);
|
||||||
struct uart_port *port = &up->port;
|
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->iotype = old_serial_port[i].io_type;
|
||||||
port->regshift = old_serial_port[i].iomem_reg_shift;
|
port->regshift = old_serial_port[i].iomem_reg_shift;
|
||||||
|
|
||||||
port->irqflags |= irqflag;
|
if (share_irqs)
|
||||||
|
port->irqflags |= IRQF_SHARED;
|
||||||
|
|
||||||
if (serial8250_isa_config != NULL)
|
if (serial8250_isa_config != NULL)
|
||||||
serial8250_isa_config(i, &up->port, &up->capabilities);
|
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)
|
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);
|
struct uart_8250_port *uart __free(kfree) = kzalloc(sizeof(*uart), GFP_KERNEL);
|
||||||
if (!uart)
|
if (!uart)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (share_irqs)
|
|
||||||
irqflag = IRQF_SHARED;
|
|
||||||
|
|
||||||
for (i = 0; p && p->flags != 0; p++, i++) {
|
for (i = 0; p && p->flags != 0; p++, i++) {
|
||||||
uart->port.iobase = p->iobase;
|
uart->port.iobase = p->iobase;
|
||||||
uart->port.membase = p->membase;
|
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.get_mctrl = p->get_mctrl;
|
||||||
uart->port.pm = p->pm;
|
uart->port.pm = p->pm;
|
||||||
uart->port.dev = &dev->dev;
|
uart->port.dev = &dev->dev;
|
||||||
uart->port.irqflags |= irqflag;
|
|
||||||
|
if (share_irqs)
|
||||||
|
uart->port.irqflags |= IRQF_SHARED;
|
||||||
|
|
||||||
ret = serial8250_register_8250_port(uart);
|
ret = serial8250_register_8250_port(uart);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&dev->dev, "unable to register port at index %d "
|
dev_err(&dev->dev, "unable to register port at index %d "
|
||||||
@@ -380,40 +377,10 @@ module_exit(serial8250_exit);
|
|||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_DESCRIPTION("Generic 8250/16x50 serial platform driver");
|
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_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices (unsafe)");
|
||||||
|
|
||||||
module_param(nr_uarts, uint, 0644);
|
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_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);
|
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, ¶m_ops_uint, &share_irqs, 0644);
|
|
||||||
module_param_cb(nr_uarts, ¶m_ops_uint, &nr_uarts, 0644);
|
|
||||||
module_param_cb(skip_txen_test, ¶m_ops_uint, &skip_txen_test, 0644);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
MODULE_ALIAS("8250_core");
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -209,27 +209,3 @@ void rsa_reset(struct uart_8250_port *up)
|
|||||||
|
|
||||||
serial_out(up, UART_RSA_FRR, 0);
|
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,
|
|
||||||
¶m_array_ops, .arr = &__param_arr_probe_rsa,
|
|
||||||
0444, -1, 0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -34,23 +34,6 @@ config SERIAL_8250
|
|||||||
Most people will say Y or M here, so that they can use serial mice,
|
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.
|
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
|
config SERIAL_8250_PNP
|
||||||
bool "8250/16550 PNP device support" if EXPERT
|
bool "8250/16550 PNP device support" if EXPERT
|
||||||
depends on SERIAL_8250 && PNP
|
depends on SERIAL_8250 && PNP
|
||||||
@@ -430,6 +413,19 @@ config SERIAL_8250_IOC3
|
|||||||
behind the IOC3 device on those systems. Maximum baud speed is
|
behind the IOC3 device on those systems. Maximum baud speed is
|
||||||
38400bps using this driver.
|
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
|
config SERIAL_8250_RT288X
|
||||||
bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support"
|
bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support"
|
||||||
depends on SERIAL_8250
|
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
|
not booting kernel because the serial console remains silent in case
|
||||||
they forgot to update the command line.
|
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
|
config SERIAL_8250_LPC18XX
|
||||||
tristate "NXP LPC18xx/43xx serial port support"
|
tristate "NXP LPC18xx/43xx serial port support"
|
||||||
depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST)
|
depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST)
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
|
|||||||
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
|
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
|
||||||
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
|
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
|
||||||
obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.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_LPC18XX) += 8250_lpc18xx.o
|
||||||
obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
|
obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
|
||||||
obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o
|
obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o
|
||||||
|
|||||||
@@ -1044,7 +1044,7 @@ config SERIAL_SCCNXP_CONSOLE
|
|||||||
|
|
||||||
config SERIAL_SC16IS7XX
|
config SERIAL_SC16IS7XX
|
||||||
tristate "NXP SC16IS7xx UART support"
|
tristate "NXP SC16IS7xx UART support"
|
||||||
depends on SPI_MASTER || I2C
|
depends on SPI_MASTER || I2C || COMPILE_TEST
|
||||||
select SERIAL_CORE
|
select SERIAL_CORE
|
||||||
select SERIAL_SC16IS7XX_SPI if SPI_MASTER
|
select SERIAL_SC16IS7XX_SPI if SPI_MASTER
|
||||||
select SERIAL_SC16IS7XX_I2C if I2C
|
select SERIAL_SC16IS7XX_I2C if I2C
|
||||||
|
|||||||
@@ -560,6 +560,64 @@ static int ar933x_uart_verify_port(struct uart_port *port,
|
|||||||
return 0;
|
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 = {
|
static const struct uart_ops ar933x_uart_ops = {
|
||||||
.tx_empty = ar933x_uart_tx_empty,
|
.tx_empty = ar933x_uart_tx_empty,
|
||||||
.set_mctrl = ar933x_uart_set_mctrl,
|
.set_mctrl = ar933x_uart_set_mctrl,
|
||||||
@@ -576,6 +634,10 @@ static const struct uart_ops ar933x_uart_ops = {
|
|||||||
.request_port = ar933x_uart_request_port,
|
.request_port = ar933x_uart_request_port,
|
||||||
.config_port = ar933x_uart_config_port,
|
.config_port = ar933x_uart_config_port,
|
||||||
.verify_port = ar933x_uart_verify_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,
|
static int ar933x_config_rs485(struct uart_port *port, struct ktermios *termios,
|
||||||
|
|||||||
@@ -3087,6 +3087,8 @@ static int lpuart_suspend_noirq(struct device *dev)
|
|||||||
static int lpuart_resume_noirq(struct device *dev)
|
static int lpuart_resume_noirq(struct device *dev)
|
||||||
{
|
{
|
||||||
struct lpuart_port *sport = dev_get_drvdata(dev);
|
struct lpuart_port *sport = dev_get_drvdata(dev);
|
||||||
|
struct tty_port *port = &sport->port.state->port;
|
||||||
|
bool wake_active;
|
||||||
u32 stat;
|
u32 stat;
|
||||||
|
|
||||||
pinctrl_pm_select_default_state(dev);
|
pinctrl_pm_select_default_state(dev);
|
||||||
@@ -3098,6 +3100,12 @@ static int lpuart_resume_noirq(struct device *dev)
|
|||||||
if (lpuart_is_32(sport)) {
|
if (lpuart_is_32(sport)) {
|
||||||
stat = lpuart32_read(&sport->port, UARTSTAT);
|
stat = lpuart32_read(&sport->port, UARTSTAT);
|
||||||
lpuart32_write(&sport->port, stat, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1723,6 +1723,7 @@ static int icom_probe(struct pci_dev *dev,
|
|||||||
retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg);
|
retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
dev_err(&dev->dev, "PCI Config read FAILED\n");
|
dev_err(&dev->dev, "PCI Config read FAILED\n");
|
||||||
|
retval = pcibios_err_to_errno(retval);
|
||||||
goto probe_exit0;
|
goto probe_exit0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/dma/imx-dma.h>
|
#include <linux/dma/imx-dma.h>
|
||||||
|
|
||||||
#include "serial_mctrl_gpio.h"
|
#include "serial_mctrl_gpio.h"
|
||||||
@@ -2697,16 +2697,32 @@ static void imx_uart_save_context(struct imx_port *sport)
|
|||||||
/* called with irq off */
|
/* called with irq off */
|
||||||
static void imx_uart_enable_wakeup(struct imx_port *sport, bool on)
|
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);
|
uart_port_lock_irq(&sport->port);
|
||||||
|
|
||||||
|
usr1 = imx_uart_readl(sport, USR1);
|
||||||
ucr3 = imx_uart_readl(sport, UCR3);
|
ucr3 = imx_uart_readl(sport, UCR3);
|
||||||
if (on) {
|
if (on) {
|
||||||
imx_uart_writel(sport, USR1_AWAKE, USR1);
|
imx_uart_writel(sport, USR1_AWAKE, USR1);
|
||||||
ucr3 |= UCR3_AWAKEN;
|
ucr3 |= UCR3_AWAKEN;
|
||||||
} else {
|
} else {
|
||||||
ucr3 &= ~UCR3_AWAKEN;
|
ucr3 &= ~UCR3_AWAKEN;
|
||||||
|
wake_active = usr1 & USR1_AWAKE;
|
||||||
}
|
}
|
||||||
imx_uart_writel(sport, ucr3, UCR3);
|
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;
|
ucr1 |= UCR1_RTSDEN;
|
||||||
} else {
|
} else {
|
||||||
ucr1 &= ~UCR1_RTSDEN;
|
ucr1 &= ~UCR1_RTSDEN;
|
||||||
|
wake_active = wake_active || (usr1 & USR1_RTSD);
|
||||||
}
|
}
|
||||||
imx_uart_writel(sport, ucr1, UCR1);
|
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);
|
uart_port_unlock_irq(&sport->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
* @unused: Unused variable
|
||||||
*
|
*
|
||||||
* This function periodically polls the Serial MUX to check for new data.
|
* This function periodically polls the Serial MUX to check for new data.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/pm_domain.h>
|
||||||
#include <linux/pm_opp.h>
|
#include <linux/pm_opp.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
@@ -101,10 +102,16 @@
|
|||||||
#define DMA_RX_BUF_SIZE 2048
|
#define DMA_RX_BUF_SIZE 2048
|
||||||
|
|
||||||
static DEFINE_IDA(port_ida);
|
static DEFINE_IDA(port_ida);
|
||||||
|
#define DOMAIN_IDX_POWER 0
|
||||||
|
#define DOMAIN_IDX_PERF 1
|
||||||
|
|
||||||
struct qcom_geni_device_data {
|
struct qcom_geni_device_data {
|
||||||
bool console;
|
bool console;
|
||||||
enum geni_se_xfer_mode mode;
|
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 {
|
struct qcom_geni_private_data {
|
||||||
@@ -142,6 +149,7 @@ struct qcom_geni_serial_port {
|
|||||||
|
|
||||||
struct qcom_geni_private_data private_data;
|
struct qcom_geni_private_data private_data;
|
||||||
const struct qcom_geni_device_data *dev_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;
|
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;
|
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,
|
static void qcom_geni_serial_set_termios(struct uart_port *uport,
|
||||||
struct ktermios *termios,
|
struct ktermios *termios,
|
||||||
const struct ktermios *old)
|
const struct ktermios *old)
|
||||||
@@ -1317,7 +1361,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
|
|||||||
/* baud rate */
|
/* baud rate */
|
||||||
baud = uart_get_baud_rate(uport, termios, old, 300, 8000000);
|
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)
|
if (ret)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1604,8 +1648,27 @@ static int geni_serial_resources_off(struct uart_port *uport)
|
|||||||
return 0;
|
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;
|
int ret;
|
||||||
|
|
||||||
port->se.clk = devm_clk_get(port->se.dev, "se");
|
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;
|
old_state = UART_PM_STATE_OFF;
|
||||||
|
|
||||||
if (new_state == UART_PM_STATE_ON && 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 &&
|
else if (new_state == UART_PM_STATE_OFF &&
|
||||||
old_state == UART_PM_STATE_ON)
|
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.dev = &pdev->dev;
|
||||||
port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
|
port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
|
||||||
ret = geni_serial_resource_init(port);
|
ret = port->dev_data->resources_init(uport);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res)
|
if (!res) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
uport->mapbase = res->start;
|
uport->mapbase = res->start;
|
||||||
|
|
||||||
uport->rs485_config = qcom_geni_rs485_config;
|
uport->rs485_config = qcom_geni_rs485_config;
|
||||||
@@ -1774,19 +1840,26 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
|
|||||||
if (!data->console) {
|
if (!data->console) {
|
||||||
port->rx_buf = devm_kzalloc(uport->dev,
|
port->rx_buf = devm_kzalloc(uport->dev,
|
||||||
DMA_RX_BUF_SIZE, GFP_KERNEL);
|
DMA_RX_BUF_SIZE, GFP_KERNEL);
|
||||||
if (!port->rx_buf)
|
if (!port->rx_buf) {
|
||||||
return -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
port->name = devm_kasprintf(uport->dev, GFP_KERNEL,
|
port->name = devm_kasprintf(uport->dev, GFP_KERNEL,
|
||||||
"qcom_geni_serial_%s%d",
|
"qcom_geni_serial_%s%d",
|
||||||
uart_console(uport) ? "console" : "uart", uport->line);
|
uart_console(uport) ? "console" : "uart", uport->line);
|
||||||
if (!port->name)
|
if (!port->name) {
|
||||||
return -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0)
|
if (irq < 0) {
|
||||||
return irq;
|
ret = irq;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
uport->irq = irq;
|
uport->irq = irq;
|
||||||
uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE);
|
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);
|
IRQF_TRIGGER_HIGH, port->name, uport);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
|
dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
|
||||||
return ret;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = uart_get_rs485_mode(uport);
|
ret = uart_get_rs485_mode(uport);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto error;
|
||||||
|
|
||||||
|
devm_pm_runtime_enable(port->se.dev);
|
||||||
|
|
||||||
ret = uart_add_one_port(drv, uport);
|
ret = uart_add_one_port(drv, uport);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto error;
|
||||||
|
|
||||||
if (port->wakeup_irq > 0) {
|
if (port->wakeup_irq > 0) {
|
||||||
device_init_wakeup(&pdev->dev, true);
|
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);
|
device_init_wakeup(&pdev->dev, false);
|
||||||
ida_free(&port_ida, uport->line);
|
ida_free(&port_ida, uport->line);
|
||||||
uart_remove_one_port(drv, uport);
|
uart_remove_one_port(drv, uport);
|
||||||
return ret;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
dev_pm_domain_detach_list(port->pd_list);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_geni_serial_remove(struct platform_device *pdev)
|
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);
|
device_init_wakeup(&pdev->dev, false);
|
||||||
ida_free(&port_ida, uport->line);
|
ida_free(&port_ida, uport->line);
|
||||||
uart_remove_one_port(drv, &port->uport);
|
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)
|
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 = {
|
static const struct qcom_geni_device_data qcom_geni_console_data = {
|
||||||
.console = true,
|
.console = true,
|
||||||
.mode = GENI_SE_FIFO,
|
.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 = {
|
static const struct qcom_geni_device_data qcom_geni_uart_data = {
|
||||||
.console = false,
|
.console = false,
|
||||||
.mode = GENI_SE_DMA,
|
.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 = {
|
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)
|
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",
|
.compatible = "qcom,geni-debug-uart",
|
||||||
.data = &qcom_geni_console_data,
|
.data = &qcom_geni_console_data,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "qcom,sa8255p-geni-debug-uart",
|
||||||
|
.data = &sa8255p_qcom_geni_console_data,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.compatible = "qcom,geni-uart",
|
.compatible = "qcom,geni-uart",
|
||||||
.data = &qcom_geni_uart_data,
|
.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);
|
MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
|
||||||
|
|||||||
@@ -2830,6 +2830,8 @@ OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart",
|
|||||||
s5pv210_early_console_setup);
|
s5pv210_early_console_setup);
|
||||||
OF_EARLYCON_DECLARE(artpec8, "axis,artpec8-uart",
|
OF_EARLYCON_DECLARE(artpec8, "axis,artpec8-uart",
|
||||||
s5pv210_early_console_setup);
|
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,
|
static int __init gs101_early_console_setup(struct earlycon_device *device,
|
||||||
const char *opt)
|
const char *opt)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#define DEFAULT_SYMBOL_NAMESPACE "SERIAL_NXP_SC16IS7XX"
|
#define DEFAULT_SYMBOL_NAMESPACE "SERIAL_NXP_SC16IS7XX"
|
||||||
|
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
|
#include <linux/cleanup.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
@@ -49,18 +50,10 @@
|
|||||||
#define SC16IS7XX_SPR_REG (0x07) /* Scratch Pad */
|
#define SC16IS7XX_SPR_REG (0x07) /* Scratch Pad */
|
||||||
#define SC16IS7XX_TXLVL_REG (0x08) /* TX FIFO level */
|
#define SC16IS7XX_TXLVL_REG (0x08) /* TX FIFO level */
|
||||||
#define SC16IS7XX_RXLVL_REG (0x09) /* RX FIFO level */
|
#define SC16IS7XX_RXLVL_REG (0x09) /* RX FIFO level */
|
||||||
#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction
|
#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction - only on 75x/76x */
|
||||||
* - 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_IOSTATE_REG (0x0b) /* I/O State
|
#define SC16IS7XX_IOCONTROL_REG (0x0e) /* I/O Control - only on 75x/76x */
|
||||||
* - 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 */
|
#define SC16IS7XX_EFCR_REG (0x0f) /* Extra Features Control */
|
||||||
|
|
||||||
/* TCR/TLR Register set: Only if ((MCR[2] == 1) && (EFR[4] == 1)) */
|
/* TCR/TLR Register set: Only if ((MCR[2] == 1) && (EFR[4] == 1)) */
|
||||||
@@ -80,12 +73,9 @@
|
|||||||
|
|
||||||
/* IER register bits */
|
/* IER register bits */
|
||||||
#define SC16IS7XX_IER_RDI_BIT BIT(0) /* Enable RX data interrupt */
|
#define SC16IS7XX_IER_RDI_BIT BIT(0) /* Enable RX data interrupt */
|
||||||
#define SC16IS7XX_IER_THRI_BIT BIT(1) /* Enable TX holding register
|
#define SC16IS7XX_IER_THRI_BIT BIT(1) /* Enable TX holding register interrupt */
|
||||||
* interrupt */
|
#define SC16IS7XX_IER_RLSI_BIT BIT(2) /* Enable RX line status interrupt */
|
||||||
#define SC16IS7XX_IER_RLSI_BIT BIT(2) /* Enable RX line status
|
#define SC16IS7XX_IER_MSI_BIT BIT(3) /* Enable Modem status interrupt */
|
||||||
* interrupt */
|
|
||||||
#define SC16IS7XX_IER_MSI_BIT BIT(3) /* Enable Modem status
|
|
||||||
* interrupt */
|
|
||||||
|
|
||||||
/* IER register bits - write only if (EFR[4] == 1) */
|
/* IER register bits - write only if (EFR[4] == 1) */
|
||||||
#define SC16IS7XX_IER_SLEEP_BIT BIT(4) /* Enable Sleep mode */
|
#define SC16IS7XX_IER_SLEEP_BIT BIT(4) /* Enable Sleep mode */
|
||||||
@@ -118,9 +108,8 @@
|
|||||||
* - only on 75x/76x
|
* - only on 75x/76x
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */
|
#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */
|
||||||
#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state
|
#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state from active
|
||||||
* from active (LOW)
|
* (LOW) to inactive (HIGH)
|
||||||
* to inactive (HIGH)
|
|
||||||
*/
|
*/
|
||||||
/* LCR register bits */
|
/* LCR register bits */
|
||||||
#define SC16IS7XX_LCR_LENGTH0_BIT BIT(0) /* Word length bit 0 */
|
#define SC16IS7XX_LCR_LENGTH0_BIT BIT(0) /* Word length bit 0 */
|
||||||
@@ -136,8 +125,7 @@
|
|||||||
*
|
*
|
||||||
* STOP length bit table:
|
* STOP length bit table:
|
||||||
* 0 -> 1 stop bit
|
* 0 -> 1 stop bit
|
||||||
* 1 -> 1-1.5 stop bits if
|
* 1 -> 1-1.5 stop bits if word length is 5,
|
||||||
* word length is 5,
|
|
||||||
* 2 stop bits otherwise
|
* 2 stop bits otherwise
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_LCR_PARITY_BIT BIT(3) /* Parity bit enable */
|
#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_6 (0x01)
|
||||||
#define SC16IS7XX_LCR_WORD_LEN_7 (0x02)
|
#define SC16IS7XX_LCR_WORD_LEN_7 (0x02)
|
||||||
#define SC16IS7XX_LCR_WORD_LEN_8 (0x03)
|
#define SC16IS7XX_LCR_WORD_LEN_8 (0x03)
|
||||||
#define SC16IS7XX_LCR_CONF_MODE_A SC16IS7XX_LCR_DLAB_BIT /* Special
|
#define SC16IS7XX_LCR_REG_SET_SPECIAL SC16IS7XX_LCR_DLAB_BIT /* Special reg set */
|
||||||
* reg set */
|
#define SC16IS7XX_LCR_REG_SET_ENHANCED 0xBF /* Enhanced reg set */
|
||||||
#define SC16IS7XX_LCR_CONF_MODE_B 0xBF /* Enhanced
|
|
||||||
* reg set */
|
|
||||||
|
|
||||||
/* MCR register bits */
|
/* MCR register bits */
|
||||||
#define SC16IS7XX_MCR_DTR_BIT BIT(0) /* DTR complement
|
#define SC16IS7XX_MCR_DTR_BIT BIT(0) /* DTR complement - only on 75x/76x */
|
||||||
* - only on 75x/76x
|
|
||||||
*/
|
|
||||||
#define SC16IS7XX_MCR_RTS_BIT BIT(1) /* RTS complement */
|
#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_LOOP_BIT BIT(4) /* Enable loopback test mode */
|
||||||
#define SC16IS7XX_MCR_XONANY_BIT BIT(5) /* Enable Xon Any
|
#define SC16IS7XX_MCR_XONANY_BIT BIT(5) /* Enable Xon Any
|
||||||
* - write enabled
|
* - write enabled if (EFR[4] == 1)
|
||||||
* if (EFR[4] == 1)
|
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_MCR_IRDA_BIT BIT(6) /* Enable IrDA mode
|
#define SC16IS7XX_MCR_IRDA_BIT BIT(6) /* Enable IrDA mode
|
||||||
* - write enabled
|
* - write enabled if (EFR[4] == 1)
|
||||||
* if (EFR[4] == 1)
|
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_MCR_CLKSEL_BIT BIT(7) /* Divide clock by 4
|
#define SC16IS7XX_MCR_CLKSEL_BIT BIT(7) /* Divide clock by 4
|
||||||
* - write enabled
|
* - write enabled if (EFR[4] == 1)
|
||||||
* if (EFR[4] == 1)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* LSR register bits */
|
/* LSR register bits */
|
||||||
@@ -192,28 +173,19 @@
|
|||||||
|
|
||||||
/* MSR register bits */
|
/* MSR register bits */
|
||||||
#define SC16IS7XX_MSR_DCTS_BIT BIT(0) /* Delta CTS Clear To Send */
|
#define SC16IS7XX_MSR_DCTS_BIT BIT(0) /* Delta CTS Clear To Send */
|
||||||
#define SC16IS7XX_MSR_DDSR_BIT BIT(1) /* Delta DSR Data Set Ready
|
#define SC16IS7XX_MSR_DDSR_BIT BIT(1) /* Delta DSR Data Set Ready or (IO4)
|
||||||
* or (IO4)
|
|
||||||
* - only on 75x/76x
|
* - only on 75x/76x
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_MSR_DRI_BIT BIT(2) /* Delta RI Ring Indicator
|
#define SC16IS7XX_MSR_DRI_BIT BIT(2) /* Delta RI Ring Indicator or (IO7)
|
||||||
* or (IO7)
|
|
||||||
* - only on 75x/76x
|
* - only on 75x/76x
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_MSR_DCD_BIT BIT(3) /* Delta CD Carrier Detect
|
#define SC16IS7XX_MSR_DCD_BIT BIT(3) /* Delta CD Carrier Detect or (IO6)
|
||||||
* or (IO6)
|
|
||||||
* - only on 75x/76x
|
* - only on 75x/76x
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_MSR_CTS_BIT BIT(4) /* CTS */
|
#define SC16IS7XX_MSR_CTS_BIT BIT(4) /* CTS */
|
||||||
#define SC16IS7XX_MSR_DSR_BIT BIT(5) /* DSR (IO4)
|
#define SC16IS7XX_MSR_DSR_BIT BIT(5) /* DSR (IO4) - only on 75x/76x */
|
||||||
* - 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_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
|
* TCR register bits
|
||||||
@@ -252,54 +224,42 @@
|
|||||||
#define SC16IS7XX_IOCONTROL_SRESET_BIT BIT(3) /* Software Reset */
|
#define SC16IS7XX_IOCONTROL_SRESET_BIT BIT(3) /* Software Reset */
|
||||||
|
|
||||||
/* EFCR register bits */
|
/* EFCR register bits */
|
||||||
#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0) /* Enable 9-bit or Multidrop
|
#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0) /* Enable 9-bit or Multidrop mode (RS485) */
|
||||||
* mode (RS485) */
|
|
||||||
#define SC16IS7XX_EFCR_RXDISABLE_BIT BIT(1) /* Disable receiver */
|
#define SC16IS7XX_EFCR_RXDISABLE_BIT BIT(1) /* Disable receiver */
|
||||||
#define SC16IS7XX_EFCR_TXDISABLE_BIT BIT(2) /* Disable transmitter */
|
#define SC16IS7XX_EFCR_TXDISABLE_BIT BIT(2) /* Disable transmitter */
|
||||||
#define SC16IS7XX_EFCR_AUTO_RS485_BIT BIT(4) /* Auto RS485 RTS direction */
|
#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_RTS_INVERT_BIT BIT(5) /* RTS output inversion */
|
||||||
#define SC16IS7XX_EFCR_IRDA_MODE_BIT BIT(7) /* IrDA mode
|
#define SC16IS7XX_EFCR_IRDA_MODE_BIT BIT(7) /* IrDA mode
|
||||||
* 0 = rate upto 115.2 kbit/s
|
* 0 = rate up to 115.2 kbit/s - Only 75x/76x
|
||||||
* - Only 75x/76x
|
* 1 = rate up to 1.152 Mbit/s - Only 76x
|
||||||
* 1 = rate upto 1.152 Mbit/s
|
|
||||||
* - Only 76x
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* EFR register bits */
|
/* EFR register bits */
|
||||||
#define SC16IS7XX_EFR_AUTORTS_BIT BIT(6) /* Auto RTS flow ctrl enable */
|
#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_AUTOCTS_BIT BIT(7) /* Auto CTS flow ctrl enable */
|
||||||
#define SC16IS7XX_EFR_XOFF2_DETECT_BIT BIT(5) /* Enable Xoff2 detection */
|
#define SC16IS7XX_EFR_XOFF2_DETECT_BIT BIT(5) /* Enable Xoff2 detection */
|
||||||
#define SC16IS7XX_EFR_ENABLE_BIT BIT(4) /* Enable enhanced functions
|
#define SC16IS7XX_EFR_ENABLE_BIT BIT(4) /* Enable enhanced functions and writing to
|
||||||
* and writing to IER[7:4],
|
* IER[7:4], FCR[5:4], MCR[7:5]
|
||||||
* FCR[5:4], MCR[7:5]
|
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_EFR_SWFLOW3_BIT BIT(3)
|
#define SC16IS7XX_EFR_SWFLOW3_BIT BIT(3)
|
||||||
#define SC16IS7XX_EFR_SWFLOW2_BIT BIT(2)
|
#define SC16IS7XX_EFR_SWFLOW2_BIT BIT(2)
|
||||||
/*
|
/*
|
||||||
* SWFLOW bits 3 & 2 table:
|
* SWFLOW bits 3 & 2 table:
|
||||||
* 00 -> no transmitter flow
|
* 00 -> no transmitter flow control
|
||||||
* control
|
* 01 -> transmitter generates XON2 and XOFF2
|
||||||
* 01 -> transmitter generates
|
* 10 -> transmitter generates XON1 and XOFF1
|
||||||
* XON2 and XOFF2
|
* 11 -> transmitter generates XON1, XON2,
|
||||||
* 10 -> transmitter generates
|
* XOFF1 and XOFF2
|
||||||
* XON1 and XOFF1
|
|
||||||
* 11 -> transmitter generates
|
|
||||||
* XON1, XON2, XOFF1 and
|
|
||||||
* XOFF2
|
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_EFR_SWFLOW1_BIT BIT(1)
|
#define SC16IS7XX_EFR_SWFLOW1_BIT BIT(1)
|
||||||
#define SC16IS7XX_EFR_SWFLOW0_BIT BIT(0)
|
#define SC16IS7XX_EFR_SWFLOW0_BIT BIT(0)
|
||||||
/*
|
/*
|
||||||
* SWFLOW bits 1 & 0 table:
|
* SWFLOW bits 1 & 0 table:
|
||||||
* 00 -> no received flow
|
* 00 -> no received flow control
|
||||||
* control
|
* 01 -> receiver compares XON2 and XOFF2
|
||||||
* 01 -> receiver compares
|
* 10 -> receiver compares XON1 and XOFF1
|
||||||
* XON2 and XOFF2
|
* 11 -> receiver compares XON1, XON2,
|
||||||
* 10 -> receiver compares
|
* XOFF1 and XOFF2
|
||||||
* XON1 and XOFF1
|
|
||||||
* 11 -> receiver compares
|
|
||||||
* XON1, XON2, XOFF1 and
|
|
||||||
* XOFF2
|
|
||||||
*/
|
*/
|
||||||
#define SC16IS7XX_EFR_FLOWCTRL_BITS (SC16IS7XX_EFR_AUTORTS_BIT | \
|
#define SC16IS7XX_EFR_FLOWCTRL_BITS (SC16IS7XX_EFR_AUTORTS_BIT | \
|
||||||
SC16IS7XX_EFR_AUTOCTS_BIT | \
|
SC16IS7XX_EFR_AUTOCTS_BIT | \
|
||||||
@@ -328,7 +288,7 @@ struct sc16is7xx_one_config {
|
|||||||
struct sc16is7xx_one {
|
struct sc16is7xx_one {
|
||||||
struct uart_port port;
|
struct uart_port port;
|
||||||
struct regmap *regmap;
|
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 tx_work;
|
||||||
struct kthread_work reg_work;
|
struct kthread_work reg_work;
|
||||||
struct kthread_delayed_work ms_work;
|
struct kthread_delayed_work ms_work;
|
||||||
@@ -358,16 +318,16 @@ static DEFINE_IDA(sc16is7xx_lines);
|
|||||||
|
|
||||||
static struct uart_driver sc16is7xx_uart = {
|
static struct uart_driver sc16is7xx_uart = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.driver_name = SC16IS7XX_NAME,
|
.driver_name = KBUILD_MODNAME,
|
||||||
.dev_name = "ttySC",
|
.dev_name = "ttySC",
|
||||||
.nr = SC16IS7XX_MAX_DEVS,
|
.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)
|
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;
|
unsigned int val = 0;
|
||||||
|
|
||||||
regmap_read(one->regmap, reg, &val);
|
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)
|
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);
|
regmap_write(one->regmap, reg, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sc16is7xx_fifo_read(struct uart_port *port, u8 *rxbuf, unsigned int rxlen)
|
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);
|
regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, rxbuf, rxlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sc16is7xx_fifo_write(struct uart_port *port, u8 *txbuf, u8 to_send)
|
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
|
* 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,
|
static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
|
||||||
u8 mask, u8 val)
|
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);
|
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)
|
* In an amazing feat of design, the enhanced register set shares the
|
||||||
* shares the address of the Interrupt Identification Register (IIR).
|
* addresses 0x02 and 0x04-0x07 with the general register set.
|
||||||
* Access to EFR is switched on by writing a magic value (0xbf) to the
|
* The special register set also shares the addresses 0x00-0x01 with the
|
||||||
* Line Control Register (LCR). Any interrupt firing during this time will
|
* general register set.
|
||||||
* see the EFR where it expects the IIR to be, leading to
|
*
|
||||||
|
* 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.
|
* "Unexpected interrupt" messages.
|
||||||
*
|
*
|
||||||
* Prevent this possibility by claiming a mutex while accessing the EFR,
|
* Prevent this possibility by claiming a mutex when access to the enhanced
|
||||||
* and claiming the same mutex from within the interrupt handler. This is
|
* or special register set is enabled, and claiming the same mutex from within
|
||||||
* similar to disabling the interrupt, but that doesn't work because the
|
* the interrupt handler. This is similar to disabling the interrupt, but that
|
||||||
* bulk of the interrupt processing is run as a workqueue job in thread
|
* doesn't work because the bulk of the interrupt processing is run as a
|
||||||
* context.
|
* 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. */
|
/* Backup content of LCR. */
|
||||||
one->old_lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
|
one->old_lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
|
||||||
|
|
||||||
/* Enable access to Enhanced register set */
|
/* Enable access to the desired register set */
|
||||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B);
|
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);
|
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);
|
regcache_cache_bypass(one->regmap, false);
|
||||||
|
|
||||||
/* Restore original content of LCR */
|
/* Restore original content of LCR */
|
||||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, one->old_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)
|
static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
|
||||||
{
|
{
|
||||||
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
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);
|
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)
|
static void sc16is7xx_ier_set(struct uart_port *port, u8 bit)
|
||||||
{
|
{
|
||||||
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
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);
|
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)
|
static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg)
|
||||||
{
|
{
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case SC16IS7XX_RHR_REG:
|
case SC16IS7XX_RHR_REG: /* Shared address space with THR & DLL */
|
||||||
case SC16IS7XX_IIR_REG:
|
case SC16IS7XX_IIR_REG: /* Shared address space with FCR & EFR */
|
||||||
case SC16IS7XX_LSR_REG:
|
case SC16IS7XX_LSR_REG: /* Shared address space with XON2 */
|
||||||
case SC16IS7XX_MSR_REG:
|
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_TXLVL_REG:
|
||||||
case SC16IS7XX_RXLVL_REG:
|
case SC16IS7XX_RXLVL_REG:
|
||||||
case SC16IS7XX_IOSTATE_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)
|
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 int prescaler = 1;
|
||||||
unsigned long clk = port->uartclk, div = clk / 16 / baud;
|
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,
|
SC16IS7XX_MCR_CLKSEL_BIT,
|
||||||
prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT);
|
prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT);
|
||||||
|
|
||||||
mutex_lock(&one->efr_lock);
|
/* Access special register set (DLL/DLH) */
|
||||||
|
sc16is7xx_regs_lock(port, SC16IS7XX_LCR_REG_SET_SPECIAL);
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Write the new divisor */
|
/* Write the new divisor */
|
||||||
regcache_cache_bypass(one->regmap, true);
|
|
||||||
sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256);
|
sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256);
|
||||||
sc16is7xx_port_write(port, SC16IS7XX_DLL_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 */
|
/* Restore access to general register set */
|
||||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
sc16is7xx_regs_unlock(port);
|
||||||
|
|
||||||
mutex_unlock(&one->efr_lock);
|
|
||||||
|
|
||||||
return DIV_ROUND_CLOSEST((clk / prescaler) / 16, div);
|
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,
|
static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
|
||||||
unsigned int iir)
|
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;
|
unsigned int lsr = 0, bytes_read, i;
|
||||||
bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC);
|
bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC);
|
||||||
u8 ch, flag;
|
u8 ch, flag;
|
||||||
@@ -756,7 +711,8 @@ static void sc16is7xx_update_mlines(struct sc16is7xx_one *one)
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned int status, changed;
|
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);
|
status = sc16is7xx_get_hwmctrl(port);
|
||||||
changed = status ^ one->old_mctrl;
|
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)
|
static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
|
||||||
{
|
{
|
||||||
bool rc = true;
|
|
||||||
unsigned int iir, rxlen;
|
unsigned int iir, rxlen;
|
||||||
struct uart_port *port = &s->p[portno].port;
|
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);
|
iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG);
|
||||||
if (iir & SC16IS7XX_IIR_NO_INT_BIT) {
|
if (iir & SC16IS7XX_IIR_NO_INT_BIT)
|
||||||
rc = false;
|
return false;
|
||||||
goto out_port_irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
iir &= SC16IS7XX_IIR_ID_MASK;
|
iir &= SC16IS7XX_IIR_ID_MASK;
|
||||||
|
|
||||||
@@ -833,18 +786,14 @@ static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_port_irq:
|
return true;
|
||||||
mutex_unlock(&one->efr_lock);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
|
static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
|
struct sc16is7xx_port *s = dev_id;
|
||||||
bool keep_polling;
|
bool keep_polling;
|
||||||
|
|
||||||
struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -871,16 +820,15 @@ static void sc16is7xx_poll_proc(struct kthread_work *ws)
|
|||||||
|
|
||||||
static void sc16is7xx_tx_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 = container_of(ws, struct sc16is7xx_one, tx_work);
|
||||||
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
struct uart_port *port = &one->port;
|
||||||
|
|
||||||
if ((port->rs485.flags & SER_RS485_ENABLED) &&
|
if ((port->rs485.flags & SER_RS485_ENABLED) &&
|
||||||
(port->rs485.delay_rts_before_send > 0))
|
(port->rs485.delay_rts_before_send > 0))
|
||||||
msleep(port->rs485.delay_rts_before_send);
|
msleep(port->rs485.delay_rts_before_send);
|
||||||
|
|
||||||
mutex_lock(&one->efr_lock);
|
guard(mutex)(&one->lock);
|
||||||
sc16is7xx_handle_tx(port);
|
sc16is7xx_handle_tx(port);
|
||||||
mutex_unlock(&one->efr_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sc16is7xx_reconf_rs485(struct uart_port *port)
|
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)
|
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;
|
struct sc16is7xx_one_config config;
|
||||||
unsigned long irqflags;
|
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)
|
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);
|
struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev);
|
||||||
|
|
||||||
if (one->port.state) {
|
if (one->port.state) {
|
||||||
mutex_lock(&one->efr_lock);
|
scoped_guard(mutex, &one->lock)
|
||||||
sc16is7xx_update_mlines(one);
|
sc16is7xx_update_mlines(one);
|
||||||
mutex_unlock(&one->efr_lock);
|
|
||||||
|
|
||||||
kthread_queue_delayed_work(&s->kworker, &one->ms_work, HZ);
|
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)
|
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);
|
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
||||||
|
|
||||||
lockdep_assert_held_once(&port->lock);
|
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)
|
static void sc16is7xx_start_tx(struct uart_port *port)
|
||||||
{
|
{
|
||||||
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
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);
|
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)
|
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 */
|
/* Called with port lock taken so we can only return cached value */
|
||||||
return one->old_mctrl;
|
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)
|
static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||||
{
|
{
|
||||||
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
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;
|
one->config.flags |= SC16IS7XX_RECONF_MD;
|
||||||
kthread_queue_work(&s->kworker, &one->reg_work);
|
kthread_queue_work(&s->kworker, &one->reg_work);
|
||||||
@@ -1033,7 +980,7 @@ static void sc16is7xx_set_termios(struct uart_port *port,
|
|||||||
struct ktermios *termios,
|
struct ktermios *termios,
|
||||||
const struct ktermios *old)
|
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;
|
unsigned int lcr, flow = 0;
|
||||||
int baud;
|
int baud;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
@@ -1106,12 +1053,12 @@ static void sc16is7xx_set_termios(struct uart_port *port,
|
|||||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
||||||
|
|
||||||
/* Update EFR registers */
|
/* 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_XON1_REG, termios->c_cc[VSTART]);
|
||||||
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
|
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
|
||||||
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
|
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
|
||||||
SC16IS7XX_EFR_FLOWCTRL_BITS, flow);
|
SC16IS7XX_EFR_FLOWCTRL_BITS, flow);
|
||||||
sc16is7xx_efr_unlock(port);
|
sc16is7xx_regs_unlock(port);
|
||||||
|
|
||||||
/* Get baud rate generator configuration */
|
/* Get baud rate generator configuration */
|
||||||
baud = uart_get_baud_rate(port, termios, old,
|
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 serial_rs485 *rs485)
|
||||||
{
|
{
|
||||||
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
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) {
|
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)
|
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);
|
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
sc16is7xx_power(port, 1);
|
sc16is7xx_power(port, 1);
|
||||||
|
|
||||||
/* Reset FIFOs*/
|
/* Reset FIFOs */
|
||||||
val = SC16IS7XX_FCR_RXRESET_BIT | SC16IS7XX_FCR_TXRESET_BIT;
|
val = SC16IS7XX_FCR_RXRESET_BIT | SC16IS7XX_FCR_TXRESET_BIT;
|
||||||
sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, val);
|
sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, val);
|
||||||
udelay(5);
|
udelay(5);
|
||||||
@@ -1191,8 +1138,7 @@ static int sc16is7xx_startup(struct uart_port *port)
|
|||||||
/* This bit must be written with LCR[7] = 0 */
|
/* This bit must be written with LCR[7] = 0 */
|
||||||
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
|
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
|
||||||
SC16IS7XX_MCR_IRDA_BIT,
|
SC16IS7XX_MCR_IRDA_BIT,
|
||||||
one->irda_mode ?
|
one->irda_mode ? SC16IS7XX_MCR_IRDA_BIT : 0);
|
||||||
SC16IS7XX_MCR_IRDA_BIT : 0);
|
|
||||||
|
|
||||||
/* Enable the Rx and Tx FIFO */
|
/* Enable the Rx and Tx FIFO */
|
||||||
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
|
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)
|
static void sc16is7xx_shutdown(struct uart_port *port)
|
||||||
{
|
{
|
||||||
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
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);
|
kthread_cancel_delayed_work_sync(&one->ms_work);
|
||||||
|
|
||||||
@@ -1510,6 +1456,75 @@ static int sc16is7xx_reset(struct device *dev, struct regmap *regmap)
|
|||||||
return 0;
|
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,
|
int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype,
|
||||||
struct regmap *regmaps[], int irq)
|
struct regmap *regmaps[], int irq)
|
||||||
{
|
{
|
||||||
@@ -1539,10 +1554,8 @@ int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype,
|
|||||||
|
|
||||||
/* Alloc port structure */
|
/* Alloc port structure */
|
||||||
s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL);
|
s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL);
|
||||||
if (!s) {
|
if (!s)
|
||||||
dev_err(dev, "Error allocating port structure\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
/* Always ask for fixed clock rate from a property. */
|
/* Always ask for fixed clock rate from a property. */
|
||||||
device_property_read_u32(dev, "clock-frequency", &uartclk);
|
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) {
|
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.dev = dev;
|
||||||
s->p[i].port.irq = irq;
|
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.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];
|
s->p[i].regmap = regmaps[i];
|
||||||
|
|
||||||
mutex_init(&s->p[i].efr_lock);
|
ret = sc16is7xx_setup_channel(&s->p[i], i, &port_registered[i]);
|
||||||
|
|
||||||
ret = uart_get_rs485_mode(&s->p[i].port);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_ports;
|
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);
|
sc16is7xx_setup_irda_ports(s);
|
||||||
@@ -1814,4 +1765,4 @@ module_exit(sc16is7xx_exit);
|
|||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>");
|
MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>");
|
||||||
MODULE_DESCRIPTION("SC16IS7xx tty serial core driver");
|
MODULE_DESCRIPTION(KBUILD_MODNAME " tty serial core driver");
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
#define SC16IS7XX_NAME "sc16is7xx"
|
|
||||||
#define SC16IS7XX_MAX_PORTS 2 /* Maximum number of UART ports per IC. */
|
#define SC16IS7XX_MAX_PORTS 2 /* Maximum number of UART ports per IC. */
|
||||||
|
|
||||||
struct device;
|
struct device;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table);
|
|||||||
|
|
||||||
static struct i2c_driver sc16is7xx_i2c_driver = {
|
static struct i2c_driver sc16is7xx_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = SC16IS7XX_NAME,
|
.name = KBUILD_MODNAME,
|
||||||
.of_match_table = sc16is7xx_dt_ids,
|
.of_match_table = sc16is7xx_dt_ids,
|
||||||
},
|
},
|
||||||
.probe = sc16is7xx_i2c_probe,
|
.probe = sc16is7xx_i2c_probe,
|
||||||
@@ -63,5 +63,5 @@ static struct i2c_driver sc16is7xx_i2c_driver = {
|
|||||||
module_i2c_driver(sc16is7xx_i2c_driver);
|
module_i2c_driver(sc16is7xx_i2c_driver);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_DESCRIPTION("SC16IS7xx I2C interface driver");
|
MODULE_DESCRIPTION(KBUILD_MODNAME " interface driver");
|
||||||
MODULE_IMPORT_NS("SERIAL_NXP_SC16IS7XX");
|
MODULE_IMPORT_NS("SERIAL_NXP_SC16IS7XX");
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table);
|
|||||||
|
|
||||||
static struct spi_driver sc16is7xx_spi_driver = {
|
static struct spi_driver sc16is7xx_spi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = SC16IS7XX_NAME,
|
.name = KBUILD_MODNAME,
|
||||||
.of_match_table = sc16is7xx_dt_ids,
|
.of_match_table = sc16is7xx_dt_ids,
|
||||||
},
|
},
|
||||||
.probe = sc16is7xx_spi_probe,
|
.probe = sc16is7xx_spi_probe,
|
||||||
@@ -86,5 +86,5 @@ static struct spi_driver sc16is7xx_spi_driver = {
|
|||||||
module_spi_driver(sc16is7xx_spi_driver);
|
module_spi_driver(sc16is7xx_spi_driver);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_DESCRIPTION("SC16IS7xx SPI interface driver");
|
MODULE_DESCRIPTION(KBUILD_MODNAME " interface driver");
|
||||||
MODULE_IMPORT_NS("SERIAL_NXP_SC16IS7XX");
|
MODULE_IMPORT_NS("SERIAL_NXP_SC16IS7XX");
|
||||||
|
|||||||
@@ -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 uart_state *state = tty->driver_data;
|
||||||
struct tty_port *port = &state->port;
|
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
|
* This semaphore protects port->count. It is also
|
||||||
* very useful to prevent opens. Also, take the
|
* 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
|
* module insertion/removal doesn't change anything
|
||||||
* under us.
|
* under us.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&port->mutex);
|
guard(mutex)(&port->mutex);
|
||||||
retval = uart_set_info(tty, port, state, ss);
|
return uart_set_info(tty, port, state, ss);
|
||||||
mutex_unlock(&port->mutex);
|
|
||||||
up_write(&tty->termios_rwsem);
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1560,85 +1556,66 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
|||||||
void __user *uarg = (void __user *)arg;
|
void __user *uarg = (void __user *)arg;
|
||||||
int ret = -ENOIOCTLCMD;
|
int ret = -ENOIOCTLCMD;
|
||||||
|
|
||||||
|
/* This ioctl doesn't rely on the hardware to be present. */
|
||||||
/*
|
if (cmd == TIOCSERCONFIG) {
|
||||||
* These ioctls don't rely on the hardware to be present.
|
guard(rwsem_write)(&tty->termios_rwsem);
|
||||||
*/
|
return uart_do_autoconfig(tty, state);
|
||||||
switch (cmd) {
|
|
||||||
case TIOCSERCONFIG:
|
|
||||||
down_write(&tty->termios_rwsem);
|
|
||||||
ret = uart_do_autoconfig(tty, state);
|
|
||||||
up_write(&tty->termios_rwsem);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret != -ENOIOCTLCMD)
|
if (tty_io_error(tty))
|
||||||
goto out;
|
return -EIO;
|
||||||
|
|
||||||
if (tty_io_error(tty)) {
|
/* This should only be used when the hardware is present. */
|
||||||
ret = -EIO;
|
if (cmd == TIOCMIWAIT)
|
||||||
goto out;
|
return uart_wait_modem_status(state, arg);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/* rs485_config requires more locking than others */
|
/* rs485_config requires more locking than others */
|
||||||
if (cmd == TIOCSRS485)
|
if (cmd == TIOCSRS485)
|
||||||
down_write(&tty->termios_rwsem);
|
down_write(&tty->termios_rwsem);
|
||||||
|
|
||||||
mutex_lock(&port->mutex);
|
scoped_guard(mutex, &port->mutex) {
|
||||||
uport = uart_port_check(state);
|
uport = uart_port_check(state);
|
||||||
|
|
||||||
if (!uport || tty_io_error(tty)) {
|
if (!uport || tty_io_error(tty)) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto out_up;
|
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)
|
if (cmd == TIOCSRS485)
|
||||||
up_write(&tty->termios_rwsem);
|
up_write(&tty->termios_rwsem);
|
||||||
out:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1651,11 +1628,10 @@ static void uart_set_ldisc(struct tty_struct *tty)
|
|||||||
if (!tty_port_initialized(port))
|
if (!tty_port_initialized(port))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&state->port.mutex);
|
guard(mutex)(&state->port.mutex);
|
||||||
uport = uart_port_check(state);
|
uport = uart_port_check(state);
|
||||||
if (uport && uport->ops->set_ldisc)
|
if (uport && uport->ops->set_ldisc)
|
||||||
uport->ops->set_ldisc(uport, &tty->termios);
|
uport->ops->set_ldisc(uport, &tty->termios);
|
||||||
mutex_unlock(&state->port.mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_set_termios(struct tty_struct *tty,
|
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;
|
state = drv->state + tty->index;
|
||||||
port = &state->port;
|
port = &state->port;
|
||||||
spin_lock_irq(&port->lock);
|
guard(spinlock_irq)(&port->lock);
|
||||||
--port->count;
|
--port->count;
|
||||||
spin_unlock_irq(&port->lock);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1843,20 +1818,18 @@ static void uart_hangup(struct tty_struct *tty)
|
|||||||
struct uart_state *state = tty->driver_data;
|
struct uart_state *state = tty->driver_data;
|
||||||
struct tty_port *port = &state->port;
|
struct tty_port *port = &state->port;
|
||||||
struct uart_port *uport;
|
struct uart_port *uport;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
pr_debug("uart_hangup(%d)\n", tty->index);
|
pr_debug("uart_hangup(%d)\n", tty->index);
|
||||||
|
|
||||||
mutex_lock(&port->mutex);
|
guard(mutex)(&port->mutex);
|
||||||
uport = uart_port_check(state);
|
uport = uart_port_check(state);
|
||||||
WARN(!uport, "hangup of detached port!\n");
|
WARN(!uport, "hangup of detached port!\n");
|
||||||
|
|
||||||
if (tty_port_active(port)) {
|
if (tty_port_active(port)) {
|
||||||
uart_flush_buffer(tty);
|
uart_flush_buffer(tty);
|
||||||
uart_shutdown(tty, state);
|
uart_shutdown(tty, state);
|
||||||
spin_lock_irqsave(&port->lock, flags);
|
scoped_guard(spinlock_irqsave, &port->lock)
|
||||||
port->count = 0;
|
port->count = 0;
|
||||||
spin_unlock_irqrestore(&port->lock, flags);
|
|
||||||
tty_port_set_active(port, false);
|
tty_port_set_active(port, false);
|
||||||
tty_port_tty_set(port, NULL);
|
tty_port_tty_set(port, NULL);
|
||||||
if (uport && !uart_console(uport))
|
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->open_wait);
|
||||||
wake_up_interruptible(&port->delta_msr_wait);
|
wake_up_interruptible(&port->delta_msr_wait);
|
||||||
}
|
}
|
||||||
mutex_unlock(&port->mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* uport == NULL if uart_port has already been removed */
|
/* 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;
|
struct uart_port *uport;
|
||||||
bool console = false;
|
bool console = false;
|
||||||
|
|
||||||
mutex_lock(&port->mutex);
|
scoped_guard(mutex, &port->mutex) {
|
||||||
uport = uart_port_check(state);
|
uport = uart_port_check(state);
|
||||||
if (uport)
|
if (uport)
|
||||||
console = uart_console_registered(uport);
|
console = uart_console_registered(uport);
|
||||||
mutex_unlock(&port->mutex);
|
}
|
||||||
|
|
||||||
return sprintf(buf, "%c\n", console ? 'Y' : 'N');
|
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 tty_port *port = &state->port;
|
||||||
struct uart_port *uart_port;
|
struct uart_port *uart_port;
|
||||||
|
|
||||||
mutex_lock(&port->mutex);
|
scoped_guard(mutex, &port->mutex) {
|
||||||
uart_port = uart_port_check(state);
|
uart_port = uart_port_check(state);
|
||||||
if (uart_port != uport)
|
if (uart_port != uport)
|
||||||
dev_alert(uport->dev, "Removing wrong port: %p != %p\n",
|
dev_alert(uport->dev, "Removing wrong port: %p != %p\n", uart_port, uport);
|
||||||
uart_port, uport);
|
|
||||||
|
|
||||||
if (!uart_port) {
|
if (!uart_port)
|
||||||
mutex_unlock(&port->mutex);
|
return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
mutex_unlock(&port->mutex);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove the devices from the tty layer
|
* 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->type = PORT_UNKNOWN;
|
||||||
uport->port_dev = NULL;
|
uport->port_dev = NULL;
|
||||||
|
|
||||||
mutex_lock(&port->mutex);
|
guard(mutex)(&port->mutex);
|
||||||
WARN_ON(atomic_dec_return(&state->refcount) < 0);
|
WARN_ON(atomic_dec_return(&state->refcount) < 0);
|
||||||
wait_event(state->remove_wait, !atomic_read(&state->refcount));
|
wait_event(state->remove_wait, !atomic_read(&state->refcount));
|
||||||
state->uart_port = NULL;
|
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);
|
struct serial_ctrl_device *ctrl_dev = serial_core_get_ctrl_dev(port_dev);
|
||||||
int ctrl_id = port->ctrl_id;
|
int ctrl_id = port->ctrl_id;
|
||||||
|
|
||||||
mutex_lock(&port_mutex);
|
guard(mutex)(&port_mutex);
|
||||||
|
|
||||||
port->flags |= UPF_DEAD;
|
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 */
|
/* Drop the serial core controller device if no ports are using it */
|
||||||
if (!serial_core_ctrl_find(drv, phys_dev, ctrl_id))
|
if (!serial_core_ctrl_find(drv, phys_dev, ctrl_id))
|
||||||
serial_base_ctrl_device_remove(ctrl_dev);
|
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))
|
if (!(port->rs485_supported.flags & SER_RS485_ENABLED))
|
||||||
return 0;
|
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",
|
ret = device_property_read_u32_array(dev, "rs485-rts-delay",
|
||||||
rs485_delay, 2);
|
rs485_delay, 2);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
|
|||||||
@@ -17,29 +17,32 @@
|
|||||||
*/
|
*/
|
||||||
#undef DEBUG
|
#undef DEBUG
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/console.h>
|
#include <linux/console.h>
|
||||||
#include <linux/ctype.h>
|
|
||||||
#include <linux/cpufreq.h>
|
#include <linux/cpufreq.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dmaengine.h>
|
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/ktime.h>
|
#include <linux/ktime.h>
|
||||||
#include <linux/major.h>
|
#include <linux/major.h>
|
||||||
#include <linux/minmax.h>
|
#include <linux/minmax.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/serial.h>
|
#include <linux/serial.h>
|
||||||
|
#include <linux/serial_core.h>
|
||||||
#include <linux/serial_sci.h>
|
#include <linux/serial_sci.h>
|
||||||
#include <linux/sh_dma.h>
|
#include <linux/sh_dma.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@@ -50,15 +53,186 @@
|
|||||||
#include <linux/tty_flip.h>
|
#include <linux/tty_flip.h>
|
||||||
|
|
||||||
#ifdef CONFIG_SUPERH
|
#ifdef CONFIG_SUPERH
|
||||||
#include <asm/sh_bios.h>
|
|
||||||
#include <asm/platform_early.h>
|
#include <asm/platform_early.h>
|
||||||
|
#include <asm/sh_bios.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "rsci.h"
|
#include "rsci.h"
|
||||||
#include "serial_mctrl_gpio.h"
|
#include "serial_mctrl_gpio.h"
|
||||||
#include "sh-sci.h"
|
|
||||||
#include "sh-sci-common.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) \
|
#define SCIx_IRQ_IS_MUXED(port) \
|
||||||
((port)->irqs[SCIx_ERI_IRQ] == \
|
((port)->irqs[SCIx_ERI_IRQ] == \
|
||||||
(port)->irqs[SCIx_RXI_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);
|
status = s->ops->read_reg(port, s->params->overrun_reg);
|
||||||
if (status & s->params->overrun_mask) {
|
if (status & s->params->overrun_mask) {
|
||||||
status &= ~s->params->overrun_mask;
|
if (s->type == SCI_PORT_RSCI) {
|
||||||
s->ops->write_reg(port, s->params->overrun_reg, status);
|
/*
|
||||||
|
* 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++;
|
port->icount.overrun++;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -1133,6 +1133,9 @@ static int sprd_clk_init(struct uart_port *uport)
|
|||||||
|
|
||||||
clk_uart = devm_clk_get(uport->dev, "uart");
|
clk_uart = devm_clk_get(uport->dev, "uart");
|
||||||
if (IS_ERR(clk_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",
|
dev_warn(uport->dev, "uart%d can't get uart clock\n",
|
||||||
uport->line);
|
uport->line);
|
||||||
clk_uart = NULL;
|
clk_uart = NULL;
|
||||||
@@ -1140,6 +1143,9 @@ static int sprd_clk_init(struct uart_port *uport)
|
|||||||
|
|
||||||
clk_parent = devm_clk_get(uport->dev, "source");
|
clk_parent = devm_clk_get(uport->dev, "source");
|
||||||
if (IS_ERR(clk_parent)) {
|
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",
|
dev_warn(uport->dev, "uart%d can't get source clock\n",
|
||||||
uport->line);
|
uport->line);
|
||||||
clk_parent = NULL;
|
clk_parent = NULL;
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
|
|||||||
* @port: Pointer to the UART port
|
* @port: Pointer to the UART port
|
||||||
* @uartclk: Reference clock
|
* @uartclk: Reference clock
|
||||||
* @pclk: APB clock
|
* @pclk: APB clock
|
||||||
* @cdns_uart_driver: Pointer to UART driver
|
|
||||||
* @baud: Current baud rate
|
* @baud: Current baud rate
|
||||||
* @clk_rate_change_nb: Notifier block for clock changes
|
* @clk_rate_change_nb: Notifier block for clock changes
|
||||||
* @quirks: Flags for RXBS support.
|
* @quirks: Flags for RXBS support.
|
||||||
@@ -204,7 +203,6 @@ struct cdns_uart {
|
|||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
struct clk *uartclk;
|
struct clk *uartclk;
|
||||||
struct clk *pclk;
|
struct clk *pclk;
|
||||||
struct uart_driver *cdns_uart_driver;
|
|
||||||
unsigned int baud;
|
unsigned int baud;
|
||||||
struct notifier_block clk_rate_change_nb;
|
struct notifier_block clk_rate_change_nb;
|
||||||
u32 quirks;
|
u32 quirks;
|
||||||
@@ -1465,7 +1463,6 @@ static struct console cdns_uart_console = {
|
|||||||
static int cdns_uart_suspend(struct device *device)
|
static int cdns_uart_suspend(struct device *device)
|
||||||
{
|
{
|
||||||
struct uart_port *port = dev_get_drvdata(device);
|
struct uart_port *port = dev_get_drvdata(device);
|
||||||
struct cdns_uart *cdns_uart = port->private_data;
|
|
||||||
int may_wake;
|
int may_wake;
|
||||||
|
|
||||||
may_wake = device_may_wakeup(device);
|
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
|
* Call the API provided in serial_core.c file which handles
|
||||||
* the suspend.
|
* 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);
|
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 */
|
#endif /* ! CONFIG_PM_SLEEP */
|
||||||
static int __maybe_unused cdns_runtime_suspend(struct device *dev)
|
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);
|
match = of_match_node(cdns_uart_of_match, pdev->dev.of_node);
|
||||||
if (match && match->data) {
|
if (match && match->data) {
|
||||||
const struct cdns_platform_data *data = 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);
|
clk_disable_unprepare(cdns_uart_data->pclk);
|
||||||
err_out_unregister_driver:
|
err_out_unregister_driver:
|
||||||
if (!instances)
|
if (!instances)
|
||||||
uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
|
uart_unregister_driver(&cdns_uart_uart_driver);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1880,7 +1875,7 @@ static void cdns_uart_remove(struct platform_device *pdev)
|
|||||||
clk_notifier_unregister(cdns_uart_data->uartclk,
|
clk_notifier_unregister(cdns_uart_data->uartclk,
|
||||||
&cdns_uart_data->clk_rate_change_nb);
|
&cdns_uart_data->clk_rate_change_nb);
|
||||||
#endif
|
#endif
|
||||||
uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
|
uart_remove_one_port(&cdns_uart_uart_driver, port);
|
||||||
port->mapbase = 0;
|
port->mapbase = 0;
|
||||||
clk_disable_unprepare(cdns_uart_data->uartclk);
|
clk_disable_unprepare(cdns_uart_data->uartclk);
|
||||||
clk_disable_unprepare(cdns_uart_data->pclk);
|
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);
|
reset_control_assert(cdns_uart_data->rstc);
|
||||||
|
|
||||||
if (!--instances)
|
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 = {
|
static struct platform_driver cdns_uart_platform_driver = {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port)
|
|||||||
mutex_unlock(&buf->lock);
|
mutex_unlock(&buf->lock);
|
||||||
|
|
||||||
if (restart)
|
if (restart)
|
||||||
queue_work(system_unbound_wq, &buf->work);
|
queue_work(system_dfl_wq, &buf->work);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive);
|
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;
|
struct tty_bufhead *buf = &port->buf;
|
||||||
|
|
||||||
tty_flip_buffer_commit(buf->tail);
|
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);
|
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);
|
tty_flip_buffer_commit(buf->tail);
|
||||||
spin_unlock_irqrestore(&port->lock, flags);
|
spin_unlock_irqrestore(&port->lock, flags);
|
||||||
|
|
||||||
queue_work(system_unbound_wq, &buf->work);
|
queue_work(system_dfl_wq, &buf->work);
|
||||||
|
|
||||||
return size;
|
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)
|
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)
|
bool tty_buffer_cancel_work(struct tty_port *port)
|
||||||
|
|||||||
@@ -424,8 +424,6 @@ static void do_compute_shiftstate(void)
|
|||||||
/* We still have to export this method to vt.c */
|
/* We still have to export this method to vt.c */
|
||||||
void vt_set_leds_compute_shiftstate(void)
|
void vt_set_leds_compute_shiftstate(void)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When VT is switched, the keyboard led needs to be set once.
|
* When VT is switched, the keyboard led needs to be set once.
|
||||||
* Ensure that after the switch is completed, the state of the
|
* 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;
|
vt_switch = true;
|
||||||
set_leds();
|
set_leds();
|
||||||
|
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||||
do_compute_shiftstate();
|
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)
|
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 (vt_spawn_con.pid)
|
||||||
if (kill_pid(vt_spawn_con.pid, vt_spawn_con.sig, 1)) {
|
if (kill_pid(vt_spawn_con.pid, vt_spawn_con.sig, 1)) {
|
||||||
put_pid(vt_spawn_con.pid);
|
put_pid(vt_spawn_con.pid);
|
||||||
vt_spawn_con.pid = NULL;
|
vt_spawn_con.pid = NULL;
|
||||||
}
|
}
|
||||||
spin_unlock(&vt_spawn_con.lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn_SAK(struct vc_data *vc)
|
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;
|
return;
|
||||||
|
|
||||||
if ((unsigned)value < ARRAY_SIZE(func_table)) {
|
if ((unsigned)value < ARRAY_SIZE(func_table)) {
|
||||||
unsigned long flags;
|
guard(spinlock_irqsave)(&func_buf_lock);
|
||||||
|
|
||||||
spin_lock_irqsave(&func_buf_lock, flags);
|
|
||||||
if (func_table[value])
|
if (func_table[value])
|
||||||
puts_queue(vc, func_table[value]);
|
puts_queue(vc, func_table[value]);
|
||||||
spin_unlock_irqrestore(&func_buf_lock, flags);
|
|
||||||
|
|
||||||
} else
|
} else
|
||||||
pr_err("k_fn called with value=%d\n", value);
|
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)
|
void setledstate(struct kbd_struct *kb, unsigned int led)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
guard(spinlock_irqsave)(&led_lock);
|
||||||
spin_lock_irqsave(&led_lock, flags);
|
|
||||||
if (!(led & ~7)) {
|
if (!(led & ~7)) {
|
||||||
ledioctl = led;
|
ledioctl = led;
|
||||||
kb->ledmode = LED_SHOW_IOCTL;
|
kb->ledmode = LED_SHOW_IOCTL;
|
||||||
@@ -1149,7 +1140,6 @@ void setledstate(struct kbd_struct *kb, unsigned int led)
|
|||||||
kb->ledmode = LED_SHOW_FLAGS;
|
kb->ledmode = LED_SHOW_FLAGS;
|
||||||
|
|
||||||
set_leds();
|
set_leds();
|
||||||
spin_unlock_irqrestore(&led_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned char getleds(void)
|
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)
|
int vt_get_leds(unsigned int console, int flag)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[console];
|
struct kbd_struct *kb = &kbd_table[console];
|
||||||
int ret;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&led_lock, flags);
|
guard(spinlock_irqsave)(&led_lock);
|
||||||
ret = vc_kbd_led(kb, flag);
|
return vc_kbd_led(kb, flag);
|
||||||
spin_unlock_irqrestore(&led_lock, flags);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(vt_get_leds);
|
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)
|
void vt_kbd_con_start(unsigned int console)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[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);
|
clr_vc_kbd_led(kb, VC_SCROLLOCK);
|
||||||
set_leds();
|
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)
|
void vt_kbd_con_stop(unsigned int console)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[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_vc_kbd_led(kb, VC_SCROLLOCK);
|
||||||
set_leds();
|
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)
|
static void kbd_bh(struct tasklet_struct *unused)
|
||||||
{
|
{
|
||||||
unsigned int leds;
|
unsigned int leds;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&led_lock, flags);
|
scoped_guard(spinlock_irqsave, &led_lock) {
|
||||||
leds = getleds();
|
leds = getleds();
|
||||||
leds |= (unsigned int)kbd->lockstate << 8;
|
leds |= (unsigned int)kbd->lockstate << 8;
|
||||||
spin_unlock_irqrestore(&led_lock, flags);
|
}
|
||||||
|
|
||||||
if (vt_switch) {
|
if (vt_switch) {
|
||||||
ledstate = ~leds;
|
ledstate = ~leds;
|
||||||
@@ -1525,15 +1507,13 @@ static void kbd_event(struct input_handle *handle, unsigned int event_type,
|
|||||||
unsigned int event_code, int value)
|
unsigned int event_code, int value)
|
||||||
{
|
{
|
||||||
/* We are called with interrupts disabled, just take the lock */
|
/* We are called with interrupts disabled, just take the lock */
|
||||||
spin_lock(&kbd_event_lock);
|
scoped_guard(spinlock, &kbd_event_lock) {
|
||||||
|
if (event_type == EV_MSC && event_code == MSC_RAW &&
|
||||||
if (event_type == EV_MSC && event_code == MSC_RAW &&
|
kbd_is_hw_raw(handle->dev))
|
||||||
kbd_is_hw_raw(handle->dev))
|
kbd_rawcode(value);
|
||||||
kbd_rawcode(value);
|
if (event_type == EV_KEY && event_code <= KEY_MAX)
|
||||||
if (event_type == EV_KEY && event_code <= KEY_MAX)
|
kbd_keycode(event_code, value, kbd_is_hw_raw(handle->dev));
|
||||||
kbd_keycode(event_code, value, kbd_is_hw_raw(handle->dev));
|
}
|
||||||
|
|
||||||
spin_unlock(&kbd_event_lock);
|
|
||||||
|
|
||||||
tasklet_schedule(&keyboard_tasklet);
|
tasklet_schedule(&keyboard_tasklet);
|
||||||
do_poke_blanked_console = 1;
|
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,
|
static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
const struct input_device_id *id)
|
const struct input_device_id *id)
|
||||||
{
|
{
|
||||||
struct input_handle *handle;
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
|
struct input_handle __free(kfree) *handle = kzalloc(sizeof(*handle), GFP_KERNEL);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1579,18 +1558,18 @@ static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
|
|||||||
|
|
||||||
error = input_register_handle(handle);
|
error = input_register_handle(handle);
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_handle;
|
return error;
|
||||||
|
|
||||||
error = input_open_device(handle);
|
error = input_open_device(handle);
|
||||||
if (error)
|
if (error)
|
||||||
goto err_unregister_handle;
|
goto err_unregister_handle;
|
||||||
|
|
||||||
|
retain_and_null_ptr(handle);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_unregister_handle:
|
err_unregister_handle:
|
||||||
input_unregister_handle(handle);
|
input_unregister_handle(handle);
|
||||||
err_free_handle:
|
|
||||||
kfree(handle);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1681,77 +1660,64 @@ int __init kbd_init(void)
|
|||||||
*/
|
*/
|
||||||
int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
|
int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
int asize;
|
int asize;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case KDGKBDIACR:
|
case KDGKBDIACR:
|
||||||
{
|
{
|
||||||
struct kbdiacrs __user *a = udp;
|
struct kbdiacrs __user *a = udp;
|
||||||
struct kbdiacr *dia;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
dia = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacr),
|
struct kbdiacr __free(kfree) *dia = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacr),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!dia)
|
if (!dia)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Lock the diacriticals table, make a copy and then
|
/* Lock the diacriticals table, make a copy and then
|
||||||
copy it after we unlock */
|
copy it after we unlock */
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
scoped_guard(spinlock_irqsave, &kbd_event_lock) {
|
||||||
|
asize = accent_table_size;
|
||||||
asize = accent_table_size;
|
for (i = 0; i < asize; i++) {
|
||||||
for (i = 0; i < asize; i++) {
|
dia[i].diacr = conv_uni_to_8bit(accent_table[i].diacr);
|
||||||
dia[i].diacr = conv_uni_to_8bit(
|
dia[i].base = conv_uni_to_8bit(accent_table[i].base);
|
||||||
accent_table[i].diacr);
|
dia[i].result = conv_uni_to_8bit(accent_table[i].result);
|
||||||
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))
|
if (put_user(asize, &a->kb_cnt))
|
||||||
ret = -EFAULT;
|
return -EFAULT;
|
||||||
else if (copy_to_user(a->kbdiacr, dia,
|
if (copy_to_user(a->kbdiacr, dia, asize * sizeof(struct kbdiacr)))
|
||||||
asize * sizeof(struct kbdiacr)))
|
return -EFAULT;
|
||||||
ret = -EFAULT;
|
return 0;
|
||||||
kfree(dia);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
case KDGKBDIACRUC:
|
case KDGKBDIACRUC:
|
||||||
{
|
{
|
||||||
struct kbdiacrsuc __user *a = udp;
|
struct kbdiacrsuc __user *a = udp;
|
||||||
void *buf;
|
|
||||||
|
|
||||||
buf = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacruc),
|
void __free(kfree) *buf = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacruc),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Lock the diacriticals table, make a copy and then
|
/* Lock the diacriticals table, make a copy and then
|
||||||
copy it after we unlock */
|
copy it after we unlock */
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
scoped_guard(spinlock_irqsave, &kbd_event_lock) {
|
||||||
|
asize = accent_table_size;
|
||||||
asize = accent_table_size;
|
memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc));
|
||||||
memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc));
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
|
|
||||||
if (put_user(asize, &a->kb_cnt))
|
if (put_user(asize, &a->kb_cnt))
|
||||||
ret = -EFAULT;
|
return -EFAULT;
|
||||||
else if (copy_to_user(a->kbdiacruc, buf,
|
if (copy_to_user(a->kbdiacruc, buf, asize * sizeof(struct kbdiacruc)))
|
||||||
asize*sizeof(struct kbdiacruc)))
|
return -EFAULT;
|
||||||
ret = -EFAULT;
|
|
||||||
kfree(buf);
|
return 0;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case KDSKBDIACR:
|
case KDSKBDIACR:
|
||||||
{
|
{
|
||||||
struct kbdiacrs __user *a = udp;
|
struct kbdiacrs __user *a = udp;
|
||||||
struct kbdiacr *dia = NULL;
|
struct kbdiacr __free(kfree) *dia = NULL;
|
||||||
unsigned int ct;
|
unsigned int ct;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -1769,7 +1735,7 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
|
|||||||
return PTR_ERR(dia);
|
return PTR_ERR(dia);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||||
accent_table_size = ct;
|
accent_table_size = ct;
|
||||||
for (i = 0; i < ct; i++) {
|
for (i = 0; i < ct; i++) {
|
||||||
accent_table[i].diacr =
|
accent_table[i].diacr =
|
||||||
@@ -1779,8 +1745,7 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
|
|||||||
accent_table[i].result =
|
accent_table[i].result =
|
||||||
conv_8bit_to_uni(dia[i].result);
|
conv_8bit_to_uni(dia[i].result);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
kfree(dia);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1788,7 +1753,7 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
|
|||||||
{
|
{
|
||||||
struct kbdiacrsuc __user *a = udp;
|
struct kbdiacrsuc __user *a = udp;
|
||||||
unsigned int ct;
|
unsigned int ct;
|
||||||
void *buf = NULL;
|
void __free(kfree) *buf = NULL;
|
||||||
|
|
||||||
if (!perm)
|
if (!perm)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
@@ -1804,18 +1769,16 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
|
|||||||
ct, sizeof(struct kbdiacruc));
|
ct, sizeof(struct kbdiacruc));
|
||||||
if (IS_ERR(buf))
|
if (IS_ERR(buf))
|
||||||
return PTR_ERR(buf);
|
return PTR_ERR(buf);
|
||||||
}
|
}
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||||
if (ct)
|
if (ct)
|
||||||
memcpy(accent_table, buf,
|
memcpy(accent_table, buf,
|
||||||
ct * sizeof(struct kbdiacruc));
|
ct * sizeof(struct kbdiacruc));
|
||||||
accent_table_size = ct;
|
accent_table_size = ct;
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
kfree(buf);
|
|
||||||
return 0;
|
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)
|
int vt_do_kdskbmode(unsigned int console, unsigned int arg)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[console];
|
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) {
|
switch(arg) {
|
||||||
case K_RAW:
|
case K_RAW:
|
||||||
kb->kbdmode = VC_RAW;
|
kb->kbdmode = VC_RAW;
|
||||||
break;
|
return 0;
|
||||||
case K_MEDIUMRAW:
|
case K_MEDIUMRAW:
|
||||||
kb->kbdmode = VC_MEDIUMRAW;
|
kb->kbdmode = VC_MEDIUMRAW;
|
||||||
break;
|
return 0;
|
||||||
case K_XLATE:
|
case K_XLATE:
|
||||||
kb->kbdmode = VC_XLATE;
|
kb->kbdmode = VC_XLATE;
|
||||||
do_compute_shiftstate();
|
do_compute_shiftstate();
|
||||||
break;
|
return 0;
|
||||||
case K_UNICODE:
|
case K_UNICODE:
|
||||||
kb->kbdmode = VC_UNICODE;
|
kb->kbdmode = VC_UNICODE;
|
||||||
do_compute_shiftstate();
|
do_compute_shiftstate();
|
||||||
break;
|
return 0;
|
||||||
case K_OFF:
|
case K_OFF:
|
||||||
kb->kbdmode = VC_OFF;
|
kb->kbdmode = VC_OFF;
|
||||||
break;
|
return 0;
|
||||||
default:
|
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)
|
int vt_do_kdskbmeta(unsigned int console, unsigned int arg)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[console];
|
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) {
|
switch(arg) {
|
||||||
case K_METABIT:
|
case K_METABIT:
|
||||||
clr_vc_kbd_mode(kb, VC_META);
|
clr_vc_kbd_mode(kb, VC_META);
|
||||||
break;
|
return 0;
|
||||||
case K_ESCPREFIX:
|
case K_ESCPREFIX:
|
||||||
set_vc_kbd_mode(kb, VC_META);
|
set_vc_kbd_mode(kb, VC_META);
|
||||||
break;
|
return 0;
|
||||||
default:
|
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 vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm)
|
||||||
int perm)
|
|
||||||
{
|
{
|
||||||
struct kbkeycode tmp;
|
struct kbkeycode tmp;
|
||||||
int kc = 0;
|
int kc;
|
||||||
|
|
||||||
if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
|
if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case KDGETKEYCODE:
|
case KDGETKEYCODE:
|
||||||
kc = getkeycode(tmp.scancode);
|
kc = getkeycode(tmp.scancode);
|
||||||
if (kc >= 0)
|
if (kc < 0)
|
||||||
kc = put_user(kc, &user_kbkc->keycode);
|
return kc;
|
||||||
break;
|
return put_user(kc, &user_kbkc->keycode);
|
||||||
case KDSETKEYCODE:
|
case KDSETKEYCODE:
|
||||||
if (!perm)
|
if (!perm)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
kc = setkeycode(tmp.scancode, tmp.keycode);
|
return setkeycode(tmp.scancode, tmp.keycode);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return kc;
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned short vt_kdgkbent(unsigned char kbdmode, unsigned char idx,
|
static unsigned short vt_kdgkbent(unsigned char kbdmode, unsigned char idx,
|
||||||
unsigned char map)
|
unsigned char map)
|
||||||
{
|
{
|
||||||
unsigned short *key_map, val;
|
unsigned short *key_map;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
/* Ensure another thread doesn't free it under us */
|
/* 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];
|
key_map = key_maps[map];
|
||||||
if (key_map) {
|
if (key_map) {
|
||||||
val = U(key_map[idx]);
|
unsigned short val = U(key_map[idx]);
|
||||||
if (kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
|
if (kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
|
||||||
val = K_HOLE;
|
return K_HOLE;
|
||||||
} else
|
return val;
|
||||||
val = idx ? K_HOLE : K_NOSUCHMAP;
|
}
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
|
|
||||||
return val;
|
return idx ? K_HOLE : K_NOSUCHMAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vt_kdskbent(unsigned char kbdmode, unsigned char idx,
|
static int vt_kdskbent(unsigned char kbdmode, unsigned char idx,
|
||||||
unsigned char map, unsigned short val)
|
unsigned char map, unsigned short val)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned short *key_map, oldval;
|
||||||
unsigned short *key_map, *new_map, oldval;
|
|
||||||
|
|
||||||
if (!idx && val == K_NOSUCHMAP) {
|
if (!idx && val == K_NOSUCHMAP) {
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||||
/* deallocate map */
|
/* deallocate map */
|
||||||
key_map = key_maps[map];
|
key_map = key_maps[map];
|
||||||
if (map && key_map) {
|
if (map && key_map) {
|
||||||
@@ -1947,7 +1899,6 @@ static int vt_kdskbent(unsigned char kbdmode, unsigned char idx,
|
|||||||
keymap_count--;
|
keymap_count--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1965,45 +1916,36 @@ static int vt_kdskbent(unsigned char kbdmode, unsigned char idx,
|
|||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
|
unsigned short __free(kfree) *new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
|
||||||
if (!new_map)
|
if (!new_map)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||||
key_map = key_maps[map];
|
key_map = key_maps[map];
|
||||||
if (key_map == NULL) {
|
if (key_map == NULL) {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
|
if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !capable(CAP_SYS_RESOURCE))
|
||||||
!capable(CAP_SYS_RESOURCE)) {
|
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
kfree(new_map);
|
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
|
||||||
key_maps[map] = new_map;
|
key_map = key_maps[map] = no_free_ptr(new_map);
|
||||||
key_map = new_map;
|
|
||||||
key_map[0] = U(K_ALLOCATED);
|
key_map[0] = U(K_ALLOCATED);
|
||||||
for (j = 1; j < NR_KEYS; j++)
|
for (j = 1; j < NR_KEYS; j++)
|
||||||
key_map[j] = U(K_HOLE);
|
key_map[j] = U(K_HOLE);
|
||||||
keymap_count++;
|
keymap_count++;
|
||||||
} else
|
}
|
||||||
kfree(new_map);
|
|
||||||
|
|
||||||
oldval = U(key_map[idx]);
|
oldval = U(key_map[idx]);
|
||||||
if (val == oldval)
|
if (val == oldval)
|
||||||
goto out;
|
return 0;
|
||||||
|
|
||||||
/* Attention Key */
|
/* Attention Key */
|
||||||
if ((oldval == K_SAK || val == K_SAK) && !capable(CAP_SYS_ADMIN)) {
|
if ((oldval == K_SAK || val == K_SAK) && !capable(CAP_SYS_ADMIN))
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
|
||||||
|
|
||||||
key_map[idx] = U(val);
|
key_map[idx] = U(val);
|
||||||
if (!map && (KTYP(oldval) == KT_SHIFT || KTYP(val) == KT_SHIFT))
|
if (!map && (KTYP(oldval) == KT_SHIFT || KTYP(val) == KT_SHIFT))
|
||||||
do_compute_shiftstate();
|
do_compute_shiftstate();
|
||||||
out:
|
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
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)
|
int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
||||||
{
|
{
|
||||||
unsigned char kb_func;
|
unsigned char kb_func;
|
||||||
unsigned long flags;
|
|
||||||
char *kbs;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (get_user(kb_func, &user_kdgkb->kb_func))
|
if (get_user(kb_func, &user_kdgkb->kb_func))
|
||||||
return -EFAULT;
|
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 */
|
/* size should have been a struct member */
|
||||||
ssize_t len = sizeof(user_kdgkb->kb_string);
|
ssize_t len = sizeof(user_kdgkb->kb_string);
|
||||||
|
|
||||||
kbs = kmalloc(len, GFP_KERNEL);
|
char __free(kfree) *kbs = kmalloc(len, GFP_KERNEL);
|
||||||
if (!kbs)
|
if (!kbs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
spin_lock_irqsave(&func_buf_lock, flags);
|
scoped_guard(spinlock_irqsave, &func_buf_lock)
|
||||||
len = strscpy(kbs, func_table[kb_func] ? : "", len);
|
len = strscpy(kbs, func_table[kb_func] ? : "", len);
|
||||||
spin_unlock_irqrestore(&func_buf_lock, flags);
|
|
||||||
|
|
||||||
if (len < 0) {
|
if (len < 0)
|
||||||
ret = -ENOSPC;
|
return -ENOSPC;
|
||||||
break;
|
|
||||||
}
|
if (copy_to_user(user_kdgkb->kb_string, kbs, len + 1))
|
||||||
ret = copy_to_user(user_kdgkb->kb_string, kbs, len + 1) ?
|
return -EFAULT;
|
||||||
-EFAULT : 0;
|
|
||||||
break;
|
return 0;
|
||||||
}
|
}
|
||||||
case KDSKBSENT:
|
case KDSKBSENT:
|
||||||
if (!perm || !capable(CAP_SYS_TTY_CONFIG))
|
if (!perm || !capable(CAP_SYS_TTY_CONFIG))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
kbs = strndup_user(user_kdgkb->kb_string,
|
char __free(kfree) *kbs = strndup_user(user_kdgkb->kb_string,
|
||||||
sizeof(user_kdgkb->kb_string));
|
sizeof(user_kdgkb->kb_string));
|
||||||
if (IS_ERR(kbs))
|
if (IS_ERR(kbs))
|
||||||
return PTR_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);
|
kbs = vt_kdskbsent(kbs, kb_func);
|
||||||
spin_unlock_irqrestore(&func_buf_lock, flags);
|
|
||||||
|
|
||||||
ret = 0;
|
return 0;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(kbs);
|
return 0;
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int vt_do_kdskled(unsigned int console, int cmd, unsigned long arg, int perm)
|
int vt_do_kdskled(unsigned int console, int cmd, unsigned long arg, int perm)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[console];
|
struct kbd_struct *kb = &kbd_table[console];
|
||||||
unsigned long flags;
|
|
||||||
unsigned char ucval;
|
unsigned char ucval;
|
||||||
|
|
||||||
switch(cmd) {
|
switch(cmd) {
|
||||||
/* the ioctls below read/set the flags usually shown in the leds */
|
/* the ioctls below read/set the flags usually shown in the leds */
|
||||||
/* don't use them - they will go away without warning */
|
/* don't use them - they will go away without warning */
|
||||||
case KDGKBLED:
|
case KDGKBLED:
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
scoped_guard(spinlock_irqsave, &kbd_event_lock)
|
||||||
ucval = kb->ledflagstate | (kb->default_ledflagstate << 4);
|
ucval = kb->ledflagstate | (kb->default_ledflagstate << 4);
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
return put_user(ucval, (char __user *)arg);
|
return put_user(ucval, (char __user *)arg);
|
||||||
|
|
||||||
case KDSKBLED:
|
case KDSKBLED:
|
||||||
@@ -2121,11 +2053,11 @@ int vt_do_kdskled(unsigned int console, int cmd, unsigned long arg, int perm)
|
|||||||
return -EPERM;
|
return -EPERM;
|
||||||
if (arg & ~0x77)
|
if (arg & ~0x77)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
spin_lock_irqsave(&led_lock, flags);
|
scoped_guard(spinlock_irqsave, &led_lock) {
|
||||||
kb->ledflagstate = (arg & 7);
|
kb->ledflagstate = (arg & 7);
|
||||||
kb->default_ledflagstate = ((arg >> 4) & 7);
|
kb->default_ledflagstate = ((arg >> 4) & 7);
|
||||||
set_leds();
|
set_leds();
|
||||||
spin_unlock_irqrestore(&led_lock, flags);
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* the ioctls below only set the lights, not the functions */
|
/* 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)
|
void vt_reset_unicode(unsigned int console)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||||
|
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
|
||||||
kbd_table[console].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
|
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)
|
void vt_reset_keyboard(unsigned int console)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[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);
|
set_vc_kbd_mode(kb, VC_REPEAT);
|
||||||
clr_vc_kbd_mode(kb, VC_CKMODE);
|
clr_vc_kbd_mode(kb, VC_CKMODE);
|
||||||
clr_vc_kbd_mode(kb, VC_APPLIC);
|
clr_vc_kbd_mode(kb, VC_APPLIC);
|
||||||
clr_vc_kbd_mode(kb, VC_CRLF);
|
clr_vc_kbd_mode(kb, VC_CRLF);
|
||||||
kb->lockstate = 0;
|
kb->lockstate = 0;
|
||||||
kb->slockstate = 0;
|
kb->slockstate = 0;
|
||||||
spin_lock(&led_lock);
|
guard(spinlock)(&led_lock);
|
||||||
kb->ledmode = LED_SHOW_FLAGS;
|
kb->ledmode = LED_SHOW_FLAGS;
|
||||||
kb->ledflagstate = kb->default_ledflagstate;
|
kb->ledflagstate = kb->default_ledflagstate;
|
||||||
spin_unlock(&led_lock);
|
|
||||||
/* do not do set_leds here because this causes an endless tasklet loop
|
/* do not do set_leds here because this causes an endless tasklet loop
|
||||||
when the keyboard hasn't been initialized yet */
|
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)
|
void vt_set_kbd_mode_bit(unsigned int console, int bit)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[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, bit);
|
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)
|
void vt_clr_kbd_mode_bit(unsigned int console, int bit)
|
||||||
{
|
{
|
||||||
struct kbd_struct *kb = &kbd_table[console];
|
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);
|
clr_vc_kbd_mode(kb, bit);
|
||||||
spin_unlock_irqrestore(&kbd_event_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,10 +348,11 @@ static int vc_selection(struct vc_data *vc, struct tiocl_selection *v,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
v->xs = min_t(u16, v->xs - 1, vc->vc_cols - 1);
|
/* Historically 0 => max value */
|
||||||
v->ys = min_t(u16, v->ys - 1, vc->vc_rows - 1);
|
v->xs = umin(v->xs - 1, vc->vc_cols - 1);
|
||||||
v->xe = min_t(u16, v->xe - 1, vc->vc_cols - 1);
|
v->ys = umin(v->ys - 1, vc->vc_rows - 1);
|
||||||
v->ye = min_t(u16, v->ye - 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)) {
|
if (mouse_reporting() && (v->sel_mode & TIOCL_SELMOUSEREPORT)) {
|
||||||
mouse_report(tty, v->sel_mode & TIOCL_SELBUTTONMASK, v->xs,
|
mouse_report(tty, v->sel_mode & TIOCL_SELBUTTONMASK, v->xs,
|
||||||
|
|||||||
@@ -4862,7 +4862,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
|
|||||||
return ret;
|
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)
|
if (op->data && font.charcount > op->charcount)
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
@@ -4894,7 +4894,7 @@ static int con_font_set(struct vc_data *vc, const struct console_font_op *op)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (vpitch < op->height)
|
if (vpitch < op->height)
|
||||||
return -EINVAL;
|
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)
|
if (size > max_font_size)
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
|
|
||||||
|
|||||||
1
tools/testing/selftests/tty/.gitignore
vendored
1
tools/testing/selftests/tty/.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
tty_tiocsti_test
|
||||||
tty_tstamp_update
|
tty_tstamp_update
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
CFLAGS = -O2 -Wall
|
CFLAGS = -O2 -Wall
|
||||||
TEST_GEN_PROGS := tty_tstamp_update
|
TEST_GEN_PROGS := tty_tstamp_update tty_tiocsti_test
|
||||||
|
LDLIBS += -lcap
|
||||||
|
|
||||||
include ../lib.mk
|
include ../lib.mk
|
||||||
|
|
||||||
|
# Add libcap for TIOCSTI test
|
||||||
|
$(OUTPUT)/tty_tiocsti_test: LDLIBS += -lcap
|
||||||
|
|||||||
1
tools/testing/selftests/tty/config
Normal file
1
tools/testing/selftests/tty/config
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_LEGACY_TIOCSTI=y
|
||||||
650
tools/testing/selftests/tty/tty_tiocsti_test.c
Normal file
650
tools/testing/selftests/tty/tty_tiocsti_test.c
Normal 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
|
||||||
Reference in New Issue
Block a user