TTY/Serial patches for 5.7-rc1
Here is the big set of TTY / Serial patches for 5.7-rc1 Lots of console fixups and reworking in here, serial core tweaks (doesn't that ever get old, why are we still creating new serial devices?), serial driver updates, line-protocol driver updates, and some vt cleanups and fixes included in here as well. All have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXoHT8w8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yl3CwCgj/97IKb4K49nV2rDgiV+t/ELWqUAnjBp+Zpd H2BEdhwCFhq/5CJHKXWH =JTm1 -----END PGP SIGNATURE----- Merge tag 'tty-5.7-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 patches for 5.7-rc1 Lots of console fixups and reworking in here, serial core tweaks (doesn't that ever get old, why are we still creating new serial devices?), serial driver updates, line-protocol driver updates, and some vt cleanups and fixes included in here as well. All have been in linux-next with no reported issues" * tag 'tty-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (161 commits) serial: 8250: Optimize irq enable after console write serial: 8250: Fix rs485 delay after console write vt: vt_ioctl: fix use-after-free in vt_in_use() vt: vt_ioctl: fix VT_DISALLOCATE freeing in-use virtual console tty: serial: make SERIAL_SPRD depend on COMMON_CLK tty: serial: fsl_lpuart: fix return value checking tty: serial: fsl_lpuart: move dma_request_chan() ARM: dts: tango4: Make /serial compatible with ns16550a ARM: dts: mmp*: Make the serial ports compatible with xscale-uart ARM: dts: mmp*: Fix serial port names ARM: dts: mmp2-brownstone: Don't redeclare phandle references ARM: dts: pxa*: Make the serial ports compatible with xscale-uart ARM: dts: pxa*: Fix serial port names ARM: dts: pxa*: Don't redeclare phandle references serial: omap: drop unused dt-bindings header serial: 8250: 8250_omap: Add DMA support for UARTs on K3 SoCs serial: 8250: 8250_omap: Work around errata causing spurious IRQs with DMA serial: 8250: 8250_omap: Extend driver data to pass FIFO trigger info serial: 8250: 8250_omap: Move locking out from __dma_rx_do_complete() serial: 8250: 8250_omap: Account for data in flight during DMA teardown ...
This commit is contained in:
commit
1f944f976d
|
@ -154,3 +154,10 @@ Description:
|
|||
device specification. For example, when user sets 7bytes on
|
||||
16550A, which has 1/4/8/14 bytes trigger, the RX trigger is
|
||||
automatically changed to 4 bytes.
|
||||
|
||||
What: /sys/class/tty/ttyS0/console
|
||||
Date: February 2020
|
||||
Contact: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
Description:
|
||||
Allows user to detach or attach back the given device as
|
||||
kernel console. It shows and accepts a boolean variable.
|
||||
|
|
|
@ -8,6 +8,10 @@ Required properties:
|
|||
Optional properties:
|
||||
- fsl,dte-mode : Indicate the uart works in DTE mode. The uart works
|
||||
in DCE mode by default.
|
||||
- fsl,inverted-tx , fsl,inverted-rx : Indicate that the hardware attached
|
||||
to the peripheral inverts the signal transmitted or received,
|
||||
respectively, and that the peripheral should invert its output/input
|
||||
using the INVT/INVR registers.
|
||||
- rs485-rts-delay, rs485-rts-active-low, rs485-rx-during-tx,
|
||||
linux,rs485-enabled-at-boot-time: see rs485.txt. Note that for RS485
|
||||
you must enable either the "uart-has-rtscts" or the "rts-gpios"
|
||||
|
|
|
@ -6,6 +6,8 @@ Required properties:
|
|||
on Vybrid vf610 SoC with 8-bit register organization
|
||||
- "fsl,ls1021a-lpuart" for lpuart compatible with the one integrated
|
||||
on LS1021A SoC with 32-bit big-endian register organization
|
||||
- "fsl,ls1028a-lpuart" for lpuart compatible with the one integrated
|
||||
on LS1028A SoC with 32-bit little-endian register organization
|
||||
- "fsl,imx7ulp-lpuart" for lpuart compatible with the one integrated
|
||||
on i.MX7ULP SoC with 32-bit little-endian register organization
|
||||
- "fsl,imx8qxp-lpuart" for lpuart compatible with the one integrated
|
||||
|
@ -15,10 +17,10 @@ Required properties:
|
|||
- reg : Address and length of the register set for the device
|
||||
- interrupts : Should contain uart interrupt
|
||||
- clocks : phandle + clock specifier pairs, one for each entry in clock-names
|
||||
- clock-names : For vf610/ls1021a/imx7ulp, "ipg" clock is for uart bus/baud
|
||||
clock. For imx8qxp lpuart, "ipg" clock is bus clock that is used to access
|
||||
lpuart controller registers, it also requires "baud" clock for module to
|
||||
receive/transmit data.
|
||||
- clock-names : For vf610/ls1021a/ls1028a/imx7ulp, "ipg" clock is for uart
|
||||
bus/baud clock. For imx8qxp lpuart, "ipg" clock is bus clock that is used
|
||||
to access lpuart controller registers, it also requires "baud" clock for
|
||||
module to receive/transmit data.
|
||||
|
||||
Optional properties:
|
||||
- dmas: A list of two dma specifiers, one for each entry in dma-names.
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
device_type = "memory";
|
||||
reg = <0x00000000 0x08000000>;
|
||||
};
|
||||
};
|
||||
|
||||
soc {
|
||||
apb@d4000000 {
|
||||
uart3: uart@d4018000 {
|
||||
&uart3 {
|
||||
status = "okay";
|
||||
};
|
||||
twsi1: i2c@d4011000 {
|
||||
};
|
||||
|
||||
&twsi1 {
|
||||
status = "okay";
|
||||
pmic: max8925@3c {
|
||||
compatible = "maxium,max8925";
|
||||
|
@ -185,10 +185,8 @@
|
|||
no-insert-detect = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
rtc: rtc@d4010000 {
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&rtc {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -208,8 +208,8 @@
|
|||
clocks = <&soc_clocks MMP2_CLK_TIMER>;
|
||||
};
|
||||
|
||||
uart1: uart@d4030000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart1: serial@d4030000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4030000 0x1000>;
|
||||
interrupts = <27>;
|
||||
clocks = <&soc_clocks MMP2_CLK_UART0>;
|
||||
|
@ -218,8 +218,8 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
uart2: uart@d4017000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart2: serial@d4017000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4017000 0x1000>;
|
||||
interrupts = <28>;
|
||||
clocks = <&soc_clocks MMP2_CLK_UART1>;
|
||||
|
@ -228,8 +228,8 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
uart3: uart@d4018000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart3: serial@d4018000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4018000 0x1000>;
|
||||
interrupts = <24>;
|
||||
clocks = <&soc_clocks MMP2_CLK_UART2>;
|
||||
|
@ -238,8 +238,8 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
uart4: uart@d4016000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart4: serial@d4016000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4016000 0x1000>;
|
||||
interrupts = <46>;
|
||||
clocks = <&soc_clocks MMP2_CLK_UART3>;
|
||||
|
|
|
@ -318,8 +318,8 @@
|
|||
clocks = <&soc_clocks MMP2_CLK_TIMER>;
|
||||
};
|
||||
|
||||
uart1: uart@d4030000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart1: serial@d4030000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4030000 0x1000>;
|
||||
interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&soc_clocks MMP2_CLK_UART0>;
|
||||
|
@ -328,8 +328,8 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
uart2: uart@d4017000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart2: serial@d4017000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4017000 0x1000>;
|
||||
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&soc_clocks MMP2_CLK_UART1>;
|
||||
|
@ -338,8 +338,8 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
uart3: uart@d4018000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart3: serial@d4018000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4018000 0x1000>;
|
||||
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&soc_clocks MMP2_CLK_UART2>;
|
||||
|
@ -348,8 +348,8 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
uart4: uart@d4016000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart4: serial@d4016000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4016000 0x1000>;
|
||||
interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&soc_clocks MMP2_CLK_UART3>;
|
||||
|
|
|
@ -18,18 +18,16 @@
|
|||
memory {
|
||||
reg = <0x00000000 0x04000000>;
|
||||
};
|
||||
|
||||
soc {
|
||||
apb@d4000000 {
|
||||
uart1: uart@d4017000 {
|
||||
status = "okay";
|
||||
};
|
||||
twsi1: i2c@d4011000 {
|
||||
status = "okay";
|
||||
};
|
||||
rtc: rtc@d4010000 {
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uart1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&twsi1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&rtc {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -55,27 +55,30 @@
|
|||
interrupts = <13>;
|
||||
};
|
||||
|
||||
uart1: uart@d4017000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart1: serial@d4017000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4017000 0x1000>;
|
||||
reg-shift = <2>;
|
||||
interrupts = <27>;
|
||||
clocks = <&soc_clocks PXA168_CLK_UART0>;
|
||||
resets = <&soc_clocks PXA168_CLK_UART0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
uart2: uart@d4018000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart2: serial@d4018000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4018000 0x1000>;
|
||||
reg-shift = <2>;
|
||||
interrupts = <28>;
|
||||
clocks = <&soc_clocks PXA168_CLK_UART1>;
|
||||
resets = <&soc_clocks PXA168_CLK_UART1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
uart3: uart@d4026000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart3: serial@d4026000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4026000 0x1000>;
|
||||
reg-shift = <2>;
|
||||
interrupts = <29>;
|
||||
clocks = <&soc_clocks PXA168_CLK_UART2>;
|
||||
resets = <&soc_clocks PXA168_CLK_UART2>;
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
memory {
|
||||
reg = <0x00000000 0x10000000>;
|
||||
};
|
||||
};
|
||||
|
||||
soc {
|
||||
apb@d4000000 {
|
||||
uart1: uart@d4017000 {
|
||||
&uart1 {
|
||||
status = "okay";
|
||||
};
|
||||
twsi1: i2c@d4011000 {
|
||||
};
|
||||
|
||||
&twsi1 {
|
||||
status = "okay";
|
||||
|
||||
pmic: 88pm860x@34 {
|
||||
|
@ -163,10 +163,8 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
rtc: rtc@d4010000 {
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&rtc {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -67,27 +67,30 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
uart1: uart@d4017000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart1: serial@d4017000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4017000 0x1000>;
|
||||
reg-shift = <2>;
|
||||
interrupts = <27>;
|
||||
clocks = <&soc_clocks PXA910_CLK_UART0>;
|
||||
resets = <&soc_clocks PXA910_CLK_UART0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
uart2: uart@d4018000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart2: serial@d4018000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4018000 0x1000>;
|
||||
reg-shift = <2>;
|
||||
interrupts = <28>;
|
||||
clocks = <&soc_clocks PXA910_CLK_UART1>;
|
||||
resets = <&soc_clocks PXA910_CLK_UART1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
uart3: uart@d4036000 {
|
||||
compatible = "mrvl,mmp-uart";
|
||||
uart3: serial@d4036000 {
|
||||
compatible = "mrvl,mmp-uart", "intel,xscale-uart";
|
||||
reg = <0xd4036000 0x1000>;
|
||||
reg-shift = <2>;
|
||||
interrupts = <59>;
|
||||
clocks = <&soc_clocks PXA910_CLK_UART2>;
|
||||
resets = <&soc_clocks PXA910_CLK_UART2>;
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
};
|
||||
|
||||
uart: serial@10700 {
|
||||
compatible = "ralink,rt2880-uart";
|
||||
compatible = "ralink,rt2880-uart", "ns16550a";
|
||||
reg = <0x10700 0x30>;
|
||||
interrupts = <1 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-frequency = <7372800>;
|
||||
|
|
|
@ -249,7 +249,7 @@ config ARCH_TEGRA
|
|||
This enables support for the NVIDIA Tegra SoC family.
|
||||
|
||||
config ARCH_SPRD
|
||||
bool "Spreadtrum SoC platform"
|
||||
tristate "Spreadtrum SoC platform"
|
||||
help
|
||||
Support for Spreadtrum ARM based SoCs
|
||||
|
||||
|
|
|
@ -333,6 +333,79 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
|
||||
lpuart0: serial@2260000 {
|
||||
compatible = "fsl,ls1028a-lpuart";
|
||||
reg = <0x0 0x2260000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 232 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 4 1>;
|
||||
clock-names = "ipg";
|
||||
dma-names = "rx","tx";
|
||||
dmas = <&edma0 1 32>,
|
||||
<&edma0 1 33>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
lpuart1: serial@2270000 {
|
||||
compatible = "fsl,ls1028a-lpuart";
|
||||
reg = <0x0 0x2270000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 233 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 4 1>;
|
||||
clock-names = "ipg";
|
||||
dma-names = "rx","tx";
|
||||
dmas = <&edma0 1 30>,
|
||||
<&edma0 1 31>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
lpuart2: serial@2280000 {
|
||||
compatible = "fsl,ls1028a-lpuart";
|
||||
reg = <0x0 0x2280000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 234 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 4 1>;
|
||||
clock-names = "ipg";
|
||||
dma-names = "rx","tx";
|
||||
dmas = <&edma0 1 28>,
|
||||
<&edma0 1 29>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
lpuart3: serial@2290000 {
|
||||
compatible = "fsl,ls1028a-lpuart";
|
||||
reg = <0x0 0x2290000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 235 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 4 1>;
|
||||
clock-names = "ipg";
|
||||
dma-names = "rx","tx";
|
||||
dmas = <&edma0 1 26>,
|
||||
<&edma0 1 27>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
lpuart4: serial@22a0000 {
|
||||
compatible = "fsl,ls1028a-lpuart";
|
||||
reg = <0x0 0x22a0000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 236 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 4 1>;
|
||||
clock-names = "ipg";
|
||||
dma-names = "rx","tx";
|
||||
dmas = <&edma0 1 24>,
|
||||
<&edma0 1 25>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
lpuart5: serial@22b0000 {
|
||||
compatible = "fsl,ls1028a-lpuart";
|
||||
reg = <0x0 0x22b0000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 237 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 4 1>;
|
||||
clock-names = "ipg";
|
||||
dma-names = "rx","tx";
|
||||
dmas = <&edma0 1 22>,
|
||||
<&edma0 1 23>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
edma0: dma-controller@22c0000 {
|
||||
#dma-cells = <2>;
|
||||
compatible = "fsl,ls1028a-edma";
|
||||
|
|
|
@ -347,8 +347,6 @@ int braille_register_console(struct console *console, int index,
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (!(console->flags & CON_BRL))
|
||||
return 0;
|
||||
if (!console_options)
|
||||
/* Only support VisioBraille for now */
|
||||
console_options = "57600o8";
|
||||
|
@ -371,8 +369,6 @@ int braille_unregister_console(struct console *console)
|
|||
{
|
||||
if (braille_co != console)
|
||||
return -EINVAL;
|
||||
if (!(console->flags & CON_BRL))
|
||||
return 0;
|
||||
unregister_keyboard_notifier(&keyboard_notifier_block);
|
||||
unregister_vt_notifier(&vt_notifier_block);
|
||||
braille_co = NULL;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* hvc_console.h
|
||||
* Copyright (C) 2005 IBM Corporation
|
||||
|
|
|
@ -243,6 +243,7 @@ done:
|
|||
/* Fall back to a 3 byte encoding */
|
||||
word.bytes = 3;
|
||||
word.word &= 0x00ffffff;
|
||||
/* Fall through */
|
||||
case 3:
|
||||
/* 3 byte encoding */
|
||||
word.word |= 0x82000000;
|
||||
|
|
|
@ -97,7 +97,19 @@ struct gsm_msg {
|
|||
u8 ctrl; /* Control byte + flags */
|
||||
unsigned int len; /* Length of data block (can be zero) */
|
||||
unsigned char *data; /* Points into buffer but not at the start */
|
||||
unsigned char buffer[0];
|
||||
unsigned char buffer[];
|
||||
};
|
||||
|
||||
enum gsm_dlci_state {
|
||||
DLCI_CLOSED,
|
||||
DLCI_OPENING, /* Sending SABM not seen UA */
|
||||
DLCI_OPEN, /* SABM/UA complete */
|
||||
DLCI_CLOSING, /* Sending DISC not seen UA/DM */
|
||||
};
|
||||
|
||||
enum gsm_dlci_mode {
|
||||
DLCI_MODE_ABM, /* Normal Asynchronous Balanced Mode */
|
||||
DLCI_MODE_ADM, /* Asynchronous Disconnected Mode */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -113,32 +125,25 @@ struct gsm_msg {
|
|||
struct gsm_dlci {
|
||||
struct gsm_mux *gsm;
|
||||
int addr;
|
||||
int state;
|
||||
#define DLCI_CLOSED 0
|
||||
#define DLCI_OPENING 1 /* Sending SABM not seen UA */
|
||||
#define DLCI_OPEN 2 /* SABM/UA complete */
|
||||
#define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */
|
||||
enum gsm_dlci_state state;
|
||||
struct mutex mutex;
|
||||
|
||||
/* Link layer */
|
||||
int mode;
|
||||
#define DLCI_MODE_ABM 0 /* Normal Asynchronous Balanced Mode */
|
||||
#define DLCI_MODE_ADM 1 /* Asynchronous Disconnected Mode */
|
||||
enum gsm_dlci_mode mode;
|
||||
spinlock_t lock; /* Protects the internal state */
|
||||
struct timer_list t1; /* Retransmit timer for SABM and UA */
|
||||
int retries;
|
||||
/* Uplink tty if active */
|
||||
struct tty_port port; /* The tty bound to this DLCI if there is one */
|
||||
struct kfifo *fifo; /* Queue fifo for the DLCI */
|
||||
struct kfifo _fifo; /* For new fifo API porting only */
|
||||
struct kfifo fifo; /* Queue fifo for the DLCI */
|
||||
int adaption; /* Adaption layer in use */
|
||||
int prev_adaption;
|
||||
u32 modem_rx; /* Our incoming virtual modem lines */
|
||||
u32 modem_tx; /* Our outgoing modem lines */
|
||||
int dead; /* Refuse re-open */
|
||||
bool dead; /* Refuse re-open */
|
||||
/* Flow control */
|
||||
int throttled; /* Private copy of throttle state */
|
||||
int constipated; /* Throttle status for outgoing */
|
||||
bool throttled; /* Private copy of throttle state */
|
||||
bool constipated; /* Throttle status for outgoing */
|
||||
/* Packetised I/O */
|
||||
struct sk_buff *skb; /* Frame being sent */
|
||||
struct sk_buff_head skb_list; /* Queued frames */
|
||||
|
@ -168,6 +173,20 @@ struct gsm_control {
|
|||
int error; /* Error if any */
|
||||
};
|
||||
|
||||
enum gsm_mux_state {
|
||||
GSM_SEARCH,
|
||||
GSM_START,
|
||||
GSM_ADDRESS,
|
||||
GSM_CONTROL,
|
||||
GSM_LEN,
|
||||
GSM_DATA,
|
||||
GSM_FCS,
|
||||
GSM_OVERRUN,
|
||||
GSM_LEN0,
|
||||
GSM_LEN1,
|
||||
GSM_SSOF,
|
||||
};
|
||||
|
||||
/*
|
||||
* Each GSM mux we have is represented by this structure. If we are
|
||||
* operating as an ldisc then we use this structure as our ldisc
|
||||
|
@ -192,22 +211,11 @@ struct gsm_mux {
|
|||
|
||||
/* Framing Layer */
|
||||
unsigned char *buf;
|
||||
int state;
|
||||
#define GSM_SEARCH 0
|
||||
#define GSM_START 1
|
||||
#define GSM_ADDRESS 2
|
||||
#define GSM_CONTROL 3
|
||||
#define GSM_LEN 4
|
||||
#define GSM_DATA 5
|
||||
#define GSM_FCS 6
|
||||
#define GSM_OVERRUN 7
|
||||
#define GSM_LEN0 8
|
||||
#define GSM_LEN1 9
|
||||
#define GSM_SSOF 10
|
||||
enum gsm_mux_state state;
|
||||
unsigned int len;
|
||||
unsigned int address;
|
||||
unsigned int count;
|
||||
int escape;
|
||||
bool escape;
|
||||
int encoding;
|
||||
u8 control;
|
||||
u8 fcs;
|
||||
|
@ -224,9 +232,9 @@ struct gsm_mux {
|
|||
unsigned int mru;
|
||||
unsigned int mtu;
|
||||
int initiator; /* Did we initiate connection */
|
||||
int dead; /* Has the mux been shut down */
|
||||
bool dead; /* Has the mux been shut down */
|
||||
struct gsm_dlci *dlci[NUM_DLCI];
|
||||
int constipated; /* Asked by remote to shut up */
|
||||
bool constipated; /* Asked by remote to shut up */
|
||||
|
||||
spinlock_t tx_lock;
|
||||
unsigned int tx_bytes; /* TX data outstanding */
|
||||
|
@ -796,7 +804,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
|
|||
|
||||
total_size = 0;
|
||||
while (1) {
|
||||
len = kfifo_len(dlci->fifo);
|
||||
len = kfifo_len(&dlci->fifo);
|
||||
if (len == 0)
|
||||
return total_size;
|
||||
|
||||
|
@ -820,7 +828,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
|
|||
*dp++ = gsm_encode_modem(dlci);
|
||||
break;
|
||||
}
|
||||
WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len);
|
||||
WARN_ON(kfifo_out_locked(&dlci->fifo, dp , len, &dlci->lock) != len);
|
||||
__gsm_data_queue(dlci, msg);
|
||||
total_size += size;
|
||||
}
|
||||
|
@ -1034,9 +1042,9 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
|
|||
fc = (modem & MDM_FC) || !(modem & MDM_RTR);
|
||||
if (fc && !dlci->constipated) {
|
||||
/* Need to throttle our output on this device */
|
||||
dlci->constipated = 1;
|
||||
dlci->constipated = true;
|
||||
} else if (!fc && dlci->constipated) {
|
||||
dlci->constipated = 0;
|
||||
dlci->constipated = false;
|
||||
gsm_dlci_data_kick(dlci);
|
||||
}
|
||||
|
||||
|
@ -1199,8 +1207,8 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
|
|||
struct gsm_dlci *dlci = gsm->dlci[0];
|
||||
/* Modem wishes to close down */
|
||||
if (dlci) {
|
||||
dlci->dead = 1;
|
||||
gsm->dead = 1;
|
||||
dlci->dead = true;
|
||||
gsm->dead = true;
|
||||
gsm_dlci_begin_close(dlci);
|
||||
}
|
||||
}
|
||||
|
@ -1211,7 +1219,7 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
|
|||
break;
|
||||
case CMD_FCON:
|
||||
/* Modem can accept data again */
|
||||
gsm->constipated = 0;
|
||||
gsm->constipated = false;
|
||||
gsm_control_reply(gsm, CMD_FCON, NULL, 0);
|
||||
/* Kick the link in case it is idling */
|
||||
spin_lock_irqsave(&gsm->tx_lock, flags);
|
||||
|
@ -1220,7 +1228,7 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
|
|||
break;
|
||||
case CMD_FCOFF:
|
||||
/* Modem wants us to STFU */
|
||||
gsm->constipated = 1;
|
||||
gsm->constipated = true;
|
||||
gsm_control_reply(gsm, CMD_FCOFF, NULL, 0);
|
||||
break;
|
||||
case CMD_MSC:
|
||||
|
@ -1424,9 +1432,9 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
|
|||
dlci->state = DLCI_CLOSED;
|
||||
if (dlci->addr != 0) {
|
||||
tty_port_tty_hangup(&dlci->port, false);
|
||||
kfifo_reset(dlci->fifo);
|
||||
kfifo_reset(&dlci->fifo);
|
||||
} else
|
||||
dlci->gsm->dead = 1;
|
||||
dlci->gsm->dead = true;
|
||||
wake_up(&dlci->gsm->event);
|
||||
/* A DLCI 0 close is a MUX termination so we need to kick that
|
||||
back to userspace somehow */
|
||||
|
@ -1496,6 +1504,9 @@ static void gsm_dlci_t1(struct timer_list *t)
|
|||
} else
|
||||
gsm_dlci_close(dlci);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: unhandled state: %d\n", __func__, dlci->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1645,8 +1656,7 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr)
|
|||
return NULL;
|
||||
spin_lock_init(&dlci->lock);
|
||||
mutex_init(&dlci->mutex);
|
||||
dlci->fifo = &dlci->_fifo;
|
||||
if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) {
|
||||
if (kfifo_alloc(&dlci->fifo, 4096, GFP_KERNEL) < 0) {
|
||||
kfree(dlci);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1681,7 +1691,7 @@ static void gsm_dlci_free(struct tty_port *port)
|
|||
|
||||
del_timer_sync(&dlci->t1);
|
||||
dlci->gsm->dlci[dlci->addr] = NULL;
|
||||
kfifo_free(dlci->fifo);
|
||||
kfifo_free(&dlci->fifo);
|
||||
while ((dlci->skb = skb_dequeue(&dlci->skb_list)))
|
||||
dev_kfree_skb(dlci->skb);
|
||||
kfree(dlci);
|
||||
|
@ -1810,6 +1820,10 @@ static void gsm_queue(struct gsm_mux *gsm)
|
|||
case DLCI_OPENING:
|
||||
gsm_dlci_open(dlci);
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: unhandled state: %d\n", __func__,
|
||||
dlci->state);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DM: /* DM can be valid unsolicited */
|
||||
|
@ -1923,6 +1937,9 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
|||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1959,7 +1976,7 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
|||
}
|
||||
|
||||
if (c == GSM1_ESCAPE) {
|
||||
gsm->escape = 1;
|
||||
gsm->escape = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1969,7 +1986,7 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
|||
|
||||
if (gsm->escape) {
|
||||
c ^= GSM1_ESCAPE_BITS;
|
||||
gsm->escape = 0;
|
||||
gsm->escape = false;
|
||||
}
|
||||
switch (gsm->state) {
|
||||
case GSM_START: /* First byte after SOF */
|
||||
|
@ -1997,6 +2014,9 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
|||
break;
|
||||
case GSM_OVERRUN: /* Over-long - eg a dropped SOF */
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2061,7 +2081,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm)
|
|||
struct gsm_dlci *dlci = gsm->dlci[0];
|
||||
struct gsm_msg *txq, *ntxq;
|
||||
|
||||
gsm->dead = 1;
|
||||
gsm->dead = true;
|
||||
|
||||
spin_lock(&gsm_mux_lock);
|
||||
for (i = 0; i < MAX_MUX; i++) {
|
||||
|
@ -2078,7 +2098,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm)
|
|||
del_timer_sync(&gsm->t2_timer);
|
||||
/* Now we are sure T2 has stopped */
|
||||
if (dlci)
|
||||
dlci->dead = 1;
|
||||
dlci->dead = true;
|
||||
|
||||
/* Free up any link layer users */
|
||||
mutex_lock(&gsm->mutex);
|
||||
|
@ -2132,7 +2152,7 @@ static int gsm_activate_mux(struct gsm_mux *gsm)
|
|||
dlci = gsm_dlci_alloc(gsm, 0);
|
||||
if (dlci == NULL)
|
||||
return -ENOMEM;
|
||||
gsm->dead = 0; /* Tty opens are now permissible */
|
||||
gsm->dead = false; /* Tty opens are now permissible */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2216,7 +2236,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
|
|||
gsm->encoding = 1;
|
||||
gsm->mru = 64; /* Default to encoding 1 so these should be 64 */
|
||||
gsm->mtu = 64;
|
||||
gsm->dead = 1; /* Avoid early tty opens */
|
||||
gsm->dead = true; /* Avoid early tty opens */
|
||||
|
||||
return gsm;
|
||||
}
|
||||
|
@ -2618,11 +2638,11 @@ static int gsmld_ioctl(struct tty_struct *tty, struct file *file,
|
|||
switch (cmd) {
|
||||
case GSMIOC_GETCONF:
|
||||
gsm_copy_config_values(gsm, &c);
|
||||
if (copy_to_user((void *)arg, &c, sizeof(c)))
|
||||
if (copy_to_user((void __user *)arg, &c, sizeof(c)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case GSMIOC_SETCONF:
|
||||
if (copy_from_user(&c, (void *)arg, sizeof(c)))
|
||||
if (copy_from_user(&c, (void __user *)arg, sizeof(c)))
|
||||
return -EFAULT;
|
||||
return gsm_config(gsm, &c);
|
||||
case GSMIOC_GETFIRST:
|
||||
|
@ -2769,7 +2789,7 @@ static void gsm_destroy_network(struct gsm_dlci *dlci)
|
|||
{
|
||||
struct gsm_mux_net *mux_net;
|
||||
|
||||
pr_debug("destroy network interface");
|
||||
pr_debug("destroy network interface\n");
|
||||
if (!dlci->net)
|
||||
return;
|
||||
mux_net = netdev_priv(dlci->net);
|
||||
|
@ -2798,7 +2818,7 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc)
|
|||
if (nc->adaption != 3 && nc->adaption != 4)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
pr_debug("create network interface");
|
||||
pr_debug("create network interface\n");
|
||||
|
||||
netname = "gsm%d";
|
||||
if (nc->if_name[0] != '\0')
|
||||
|
@ -2806,7 +2826,7 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc)
|
|||
net = alloc_netdev(sizeof(struct gsm_mux_net), netname,
|
||||
NET_NAME_UNKNOWN, gsm_mux_net_init);
|
||||
if (!net) {
|
||||
pr_err("alloc_netdev failed");
|
||||
pr_err("alloc_netdev failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
net->mtu = dlci->gsm->mtu;
|
||||
|
@ -2824,7 +2844,7 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc)
|
|||
dlci->data = gsm_mux_rx_netchar;
|
||||
dlci->net = net;
|
||||
|
||||
pr_debug("register netdev");
|
||||
pr_debug("register netdev\n");
|
||||
retval = register_netdev(net);
|
||||
if (retval) {
|
||||
pr_err("network register fail %d\n", retval);
|
||||
|
@ -3030,7 +3050,7 @@ static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
|
|||
if (dlci->state == DLCI_CLOSED)
|
||||
return -EINVAL;
|
||||
/* Stuff the bytes into the fifo queue */
|
||||
sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock);
|
||||
sent = kfifo_in_locked(&dlci->fifo, buf, len, &dlci->lock);
|
||||
/* Need to kick the channel */
|
||||
gsm_dlci_data_kick(dlci);
|
||||
return sent;
|
||||
|
@ -3041,7 +3061,7 @@ static int gsmtty_write_room(struct tty_struct *tty)
|
|||
struct gsm_dlci *dlci = tty->driver_data;
|
||||
if (dlci->state == DLCI_CLOSED)
|
||||
return -EINVAL;
|
||||
return TX_SIZE - kfifo_len(dlci->fifo);
|
||||
return TX_SIZE - kfifo_len(&dlci->fifo);
|
||||
}
|
||||
|
||||
static int gsmtty_chars_in_buffer(struct tty_struct *tty)
|
||||
|
@ -3049,7 +3069,7 @@ static int gsmtty_chars_in_buffer(struct tty_struct *tty)
|
|||
struct gsm_dlci *dlci = tty->driver_data;
|
||||
if (dlci->state == DLCI_CLOSED)
|
||||
return -EINVAL;
|
||||
return kfifo_len(dlci->fifo);
|
||||
return kfifo_len(&dlci->fifo);
|
||||
}
|
||||
|
||||
static void gsmtty_flush_buffer(struct tty_struct *tty)
|
||||
|
@ -3061,7 +3081,7 @@ static void gsmtty_flush_buffer(struct tty_struct *tty)
|
|||
then the data being transmitted can't simply be junked once
|
||||
it has first hit the stack. Until then we can just blow it
|
||||
away */
|
||||
kfifo_reset(dlci->fifo);
|
||||
kfifo_reset(&dlci->fifo);
|
||||
/* Need to unhook this DLCI from the transmit queue logic */
|
||||
}
|
||||
|
||||
|
@ -3152,7 +3172,7 @@ static void gsmtty_throttle(struct tty_struct *tty)
|
|||
return;
|
||||
if (C_CRTSCTS(tty))
|
||||
dlci->modem_tx &= ~TIOCM_DTR;
|
||||
dlci->throttled = 1;
|
||||
dlci->throttled = true;
|
||||
/* Send an MSC with DTR cleared */
|
||||
gsmtty_modem_update(dlci, 0);
|
||||
}
|
||||
|
@ -3164,7 +3184,7 @@ static void gsmtty_unthrottle(struct tty_struct *tty)
|
|||
return;
|
||||
if (C_CRTSCTS(tty))
|
||||
dlci->modem_tx |= TIOCM_DTR;
|
||||
dlci->throttled = 0;
|
||||
dlci->throttled = false;
|
||||
/* Send an MSC with DTR set */
|
||||
gsmtty_modem_update(dlci, 0);
|
||||
}
|
||||
|
|
|
@ -87,9 +87,6 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#undef VERSION
|
||||
#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))
|
||||
|
||||
#include <linux/poll.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
@ -127,11 +124,8 @@ struct n_hdlc_buf_list {
|
|||
/**
|
||||
* struct n_hdlc - per device instance data structure
|
||||
* @magic - magic value for structure
|
||||
* @flags - miscellaneous control flags
|
||||
* @tty - ptr to TTY structure
|
||||
* @backup_tty - TTY to use if tty gets closed
|
||||
* @tbusy - reentrancy flag for tx wakeup code
|
||||
* @woke_up - FIXME: describe this field
|
||||
* @woke_up - tx wakeup needs to be run again as it was called while @tbusy
|
||||
* @tx_buf_list - list of pending transmit frame buffers
|
||||
* @rx_buf_list - list of received frame buffers
|
||||
* @tx_free_buf_list - list unused transmit frame buffers
|
||||
|
@ -139,11 +133,8 @@ struct n_hdlc_buf_list {
|
|||
*/
|
||||
struct n_hdlc {
|
||||
int magic;
|
||||
__u32 flags;
|
||||
struct tty_struct *tty;
|
||||
struct tty_struct *backup_tty;
|
||||
int tbusy;
|
||||
int woke_up;
|
||||
bool tbusy;
|
||||
bool woke_up;
|
||||
struct n_hdlc_buf_list tx_buf_list;
|
||||
struct n_hdlc_buf_list rx_buf_list;
|
||||
struct n_hdlc_buf_list tx_free_buf_list;
|
||||
|
@ -161,39 +152,14 @@ static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list);
|
|||
|
||||
/* Local functions */
|
||||
|
||||
static struct n_hdlc *n_hdlc_alloc (void);
|
||||
|
||||
/* debug level can be set by insmod for debugging purposes */
|
||||
#define DEBUG_LEVEL_INFO 1
|
||||
static int debuglevel;
|
||||
static struct n_hdlc *n_hdlc_alloc(void);
|
||||
|
||||
/* max frame size for memory allocations */
|
||||
static int maxframe = 4096;
|
||||
|
||||
/* TTY callbacks */
|
||||
|
||||
static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
|
||||
__u8 __user *buf, size_t nr);
|
||||
static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
|
||||
const unsigned char *buf, size_t nr);
|
||||
static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
static __poll_t n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
|
||||
poll_table *wait);
|
||||
static int n_hdlc_tty_open(struct tty_struct *tty);
|
||||
static void n_hdlc_tty_close(struct tty_struct *tty);
|
||||
static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp,
|
||||
char *fp, int count);
|
||||
static void n_hdlc_tty_wakeup(struct tty_struct *tty);
|
||||
|
||||
#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
|
||||
|
||||
#define tty2n_hdlc(tty) ((struct n_hdlc *) ((tty)->disc_data))
|
||||
#define n_hdlc2tty(n_hdlc) ((n_hdlc)->tty)
|
||||
|
||||
static void flush_rx_queue(struct tty_struct *tty)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
struct n_hdlc_buf *buf;
|
||||
|
||||
while ((buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list)))
|
||||
|
@ -202,79 +168,22 @@ static void flush_rx_queue(struct tty_struct *tty)
|
|||
|
||||
static void flush_tx_queue(struct tty_struct *tty)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
struct n_hdlc_buf *buf;
|
||||
|
||||
while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list)))
|
||||
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf);
|
||||
}
|
||||
|
||||
static struct tty_ldisc_ops n_hdlc_ldisc = {
|
||||
.owner = THIS_MODULE,
|
||||
.magic = TTY_LDISC_MAGIC,
|
||||
.name = "hdlc",
|
||||
.open = n_hdlc_tty_open,
|
||||
.close = n_hdlc_tty_close,
|
||||
.read = n_hdlc_tty_read,
|
||||
.write = n_hdlc_tty_write,
|
||||
.ioctl = n_hdlc_tty_ioctl,
|
||||
.poll = n_hdlc_tty_poll,
|
||||
.receive_buf = n_hdlc_tty_receive,
|
||||
.write_wakeup = n_hdlc_tty_wakeup,
|
||||
.flush_buffer = flush_rx_queue,
|
||||
};
|
||||
|
||||
/**
|
||||
* n_hdlc_release - release an n_hdlc per device line discipline info structure
|
||||
* @n_hdlc - per device line discipline info structure
|
||||
*/
|
||||
static void n_hdlc_release(struct n_hdlc *n_hdlc)
|
||||
static void n_hdlc_free_buf_list(struct n_hdlc_buf_list *list)
|
||||
{
|
||||
struct tty_struct *tty = n_hdlc2tty (n_hdlc);
|
||||
struct n_hdlc_buf *buf;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__);
|
||||
|
||||
/* Ensure that the n_hdlcd process is not hanging on select()/poll() */
|
||||
wake_up_interruptible (&tty->read_wait);
|
||||
wake_up_interruptible (&tty->write_wait);
|
||||
|
||||
if (tty->disc_data == n_hdlc)
|
||||
tty->disc_data = NULL; /* Break the tty->n_hdlc link */
|
||||
|
||||
/* Release transmit and receive buffers */
|
||||
for(;;) {
|
||||
buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list);
|
||||
if (buf) {
|
||||
do {
|
||||
buf = n_hdlc_buf_get(list);
|
||||
kfree(buf);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
for(;;) {
|
||||
buf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list);
|
||||
if (buf) {
|
||||
kfree(buf);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
for(;;) {
|
||||
buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
|
||||
if (buf) {
|
||||
kfree(buf);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
for(;;) {
|
||||
buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
|
||||
if (buf) {
|
||||
kfree(buf);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
kfree(n_hdlc);
|
||||
|
||||
} /* end of n_hdlc_release() */
|
||||
} while (buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* n_hdlc_tty_close - line discipline close
|
||||
|
@ -285,34 +194,26 @@ static void n_hdlc_release(struct n_hdlc *n_hdlc)
|
|||
*/
|
||||
static void n_hdlc_tty_close(struct tty_struct *tty)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_close() called\n",__FILE__,__LINE__);
|
||||
|
||||
if (n_hdlc != NULL) {
|
||||
if (n_hdlc->magic != HDLC_MAGIC) {
|
||||
printk (KERN_WARNING"n_hdlc: trying to close unopened tty!\n");
|
||||
pr_warn("n_hdlc: trying to close unopened tty!\n");
|
||||
return;
|
||||
}
|
||||
#if defined(TTY_NO_WRITE_SPLIT)
|
||||
clear_bit(TTY_NO_WRITE_SPLIT,&tty->flags);
|
||||
clear_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
|
||||
#endif
|
||||
tty->disc_data = NULL;
|
||||
if (tty == n_hdlc->backup_tty)
|
||||
n_hdlc->backup_tty = NULL;
|
||||
if (tty != n_hdlc->tty)
|
||||
return;
|
||||
if (n_hdlc->backup_tty) {
|
||||
n_hdlc->tty = n_hdlc->backup_tty;
|
||||
} else {
|
||||
n_hdlc_release (n_hdlc);
|
||||
}
|
||||
}
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_close() success\n",__FILE__,__LINE__);
|
||||
/* Ensure that the n_hdlcd process is not hanging on select()/poll() */
|
||||
wake_up_interruptible(&tty->read_wait);
|
||||
wake_up_interruptible(&tty->write_wait);
|
||||
|
||||
n_hdlc_free_buf_list(&n_hdlc->rx_free_buf_list);
|
||||
n_hdlc_free_buf_list(&n_hdlc->tx_free_buf_list);
|
||||
n_hdlc_free_buf_list(&n_hdlc->rx_buf_list);
|
||||
n_hdlc_free_buf_list(&n_hdlc->tx_buf_list);
|
||||
kfree(n_hdlc);
|
||||
} /* end of n_hdlc_tty_close() */
|
||||
|
||||
/**
|
||||
|
@ -321,42 +222,33 @@ static void n_hdlc_tty_close(struct tty_struct *tty)
|
|||
*
|
||||
* Returns 0 if success, otherwise error code
|
||||
*/
|
||||
static int n_hdlc_tty_open (struct tty_struct *tty)
|
||||
static int n_hdlc_tty_open(struct tty_struct *tty)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_open() called (device=%s)\n",
|
||||
__FILE__,__LINE__,
|
||||
tty->name);
|
||||
pr_debug("%s() called (device=%s)\n", __func__, tty->name);
|
||||
|
||||
/* There should not be an existing table for this slot. */
|
||||
if (n_hdlc) {
|
||||
printk (KERN_ERR"n_hdlc_tty_open:tty already associated!\n" );
|
||||
pr_err("%s: tty already associated!\n", __func__);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
n_hdlc = n_hdlc_alloc();
|
||||
if (!n_hdlc) {
|
||||
printk (KERN_ERR "n_hdlc_alloc failed\n");
|
||||
pr_err("%s: n_hdlc_alloc failed\n", __func__);
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
tty->disc_data = n_hdlc;
|
||||
n_hdlc->tty = tty;
|
||||
tty->receive_room = 65536;
|
||||
|
||||
#if defined(TTY_NO_WRITE_SPLIT)
|
||||
/* change tty_io write() to not split large writes into 8K chunks */
|
||||
set_bit(TTY_NO_WRITE_SPLIT,&tty->flags);
|
||||
#endif
|
||||
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
|
||||
|
||||
/* flush receive data from driver */
|
||||
tty_driver_flush_buffer(tty);
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__);
|
||||
|
||||
return 0;
|
||||
|
||||
} /* end of n_tty_hdlc_open() */
|
||||
|
@ -376,25 +268,21 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
|
|||
unsigned long flags;
|
||||
struct n_hdlc_buf *tbuf;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_send_frames() called\n",__FILE__,__LINE__);
|
||||
check_again:
|
||||
check_again:
|
||||
|
||||
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
|
||||
if (n_hdlc->tbusy) {
|
||||
n_hdlc->woke_up = 1;
|
||||
n_hdlc->woke_up = true;
|
||||
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
|
||||
return;
|
||||
}
|
||||
n_hdlc->tbusy = 1;
|
||||
n_hdlc->woke_up = 0;
|
||||
n_hdlc->tbusy = true;
|
||||
n_hdlc->woke_up = false;
|
||||
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
|
||||
|
||||
tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
|
||||
while (tbuf) {
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)sending frame %p, count=%d\n",
|
||||
__FILE__,__LINE__,tbuf,tbuf->count);
|
||||
pr_debug("sending frame %p, count=%d\n", tbuf, tbuf->count);
|
||||
|
||||
/* Send the next block of data to device */
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
|
@ -411,9 +299,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
|
|||
actual = tbuf->count;
|
||||
|
||||
if (actual == tbuf->count) {
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)frame %p completed\n",
|
||||
__FILE__,__LINE__,tbuf);
|
||||
pr_debug("frame %p completed\n", tbuf);
|
||||
|
||||
/* free current transmit buffer */
|
||||
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf);
|
||||
|
@ -424,9 +310,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
|
|||
/* get next pending transmit buffer */
|
||||
tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
|
||||
} else {
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)frame %p pending\n",
|
||||
__FILE__,__LINE__,tbuf);
|
||||
pr_debug("frame %p pending\n", tbuf);
|
||||
|
||||
/*
|
||||
* the buffer was not accepted by driver,
|
||||
|
@ -442,15 +326,11 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
|
|||
|
||||
/* Clear the re-entry flag */
|
||||
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
|
||||
n_hdlc->tbusy = 0;
|
||||
n_hdlc->tbusy = false;
|
||||
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
|
||||
|
||||
if (n_hdlc->woke_up)
|
||||
goto check_again;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_send_frames() exit\n",__FILE__,__LINE__);
|
||||
|
||||
} /* end of n_hdlc_send_frames() */
|
||||
|
||||
/**
|
||||
|
@ -461,21 +341,9 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
|
|||
*/
|
||||
static void n_hdlc_tty_wakeup(struct tty_struct *tty)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_wakeup() called\n",__FILE__,__LINE__);
|
||||
|
||||
if (!n_hdlc)
|
||||
return;
|
||||
|
||||
if (tty != n_hdlc->tty) {
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
return;
|
||||
}
|
||||
|
||||
n_hdlc_send_frames (n_hdlc, tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
|
||||
n_hdlc_send_frames(n_hdlc, tty);
|
||||
} /* end of n_hdlc_tty_wakeup() */
|
||||
|
||||
/**
|
||||
|
@ -491,59 +359,50 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty)
|
|||
static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data,
|
||||
char *flags, int count)
|
||||
{
|
||||
register struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
|
||||
register struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
register struct n_hdlc_buf *buf;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_receive() called count=%d\n",
|
||||
__FILE__,__LINE__, count);
|
||||
|
||||
/* This can happen if stuff comes in on the backup tty */
|
||||
if (!n_hdlc || tty != n_hdlc->tty)
|
||||
return;
|
||||
pr_debug("%s() called count=%d\n", __func__, count);
|
||||
|
||||
/* verify line is using HDLC discipline */
|
||||
if (n_hdlc->magic != HDLC_MAGIC) {
|
||||
printk("%s(%d) line not using HDLC discipline\n",
|
||||
__FILE__,__LINE__);
|
||||
pr_err("line not using HDLC discipline\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( count>maxframe ) {
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d) rx count>maxframesize, data discarded\n",
|
||||
__FILE__,__LINE__);
|
||||
if (count > maxframe) {
|
||||
pr_debug("rx count>maxframesize, data discarded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* get a free HDLC buffer */
|
||||
buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list);
|
||||
if (!buf) {
|
||||
/* no buffers in free list, attempt to allocate another rx buffer */
|
||||
/* unless the maximum count has been reached */
|
||||
/*
|
||||
* no buffers in free list, attempt to allocate another rx
|
||||
* buffer unless the maximum count has been reached
|
||||
*/
|
||||
if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT)
|
||||
buf = kmalloc(struct_size(buf, buf, maxframe),
|
||||
GFP_ATOMIC);
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d) no more rx buffers, data discarded\n",
|
||||
__FILE__,__LINE__);
|
||||
pr_debug("no more rx buffers, data discarded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* copy received data to HDLC buffer */
|
||||
memcpy(buf->buf,data,count);
|
||||
buf->count=count;
|
||||
memcpy(buf->buf, data, count);
|
||||
buf->count = count;
|
||||
|
||||
/* add HDLC buffer to list of received frames */
|
||||
n_hdlc_buf_put(&n_hdlc->rx_buf_list, buf);
|
||||
|
||||
/* wake up any blocked reads and perform async signalling */
|
||||
wake_up_interruptible (&tty->read_wait);
|
||||
if (n_hdlc->tty->fasync != NULL)
|
||||
kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN);
|
||||
wake_up_interruptible(&tty->read_wait);
|
||||
if (tty->fasync != NULL)
|
||||
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
|
||||
|
||||
} /* end of n_hdlc_tty_receive() */
|
||||
|
||||
|
@ -559,22 +418,15 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data,
|
|||
static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
|
||||
__u8 __user *buf, size_t nr)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
int ret = 0;
|
||||
struct n_hdlc_buf *rbuf;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_read() called\n",__FILE__,__LINE__);
|
||||
|
||||
/* Validate the pointers */
|
||||
if (!n_hdlc)
|
||||
return -EIO;
|
||||
|
||||
/* verify user access to buffer */
|
||||
if (!access_ok(buf, nr)) {
|
||||
printk(KERN_WARNING "%s(%d) n_hdlc_tty_read() can't verify user "
|
||||
"buffer\n", __FILE__, __LINE__);
|
||||
pr_warn("%s(%d) %s() can't verify user buffer\n",
|
||||
__FILE__, __LINE__, __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
|
@ -644,29 +496,20 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
|
|||
static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
|
||||
const unsigned char *data, size_t count)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
int error = 0;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
struct n_hdlc_buf *tbuf;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_write() called count=%zd\n",
|
||||
__FILE__,__LINE__,count);
|
||||
|
||||
/* Verify pointers */
|
||||
if (!n_hdlc)
|
||||
return -EIO;
|
||||
pr_debug("%s() called count=%zd\n", __func__, count);
|
||||
|
||||
if (n_hdlc->magic != HDLC_MAGIC)
|
||||
return -EIO;
|
||||
|
||||
/* verify frame size */
|
||||
if (count > maxframe ) {
|
||||
if (debuglevel & DEBUG_LEVEL_INFO)
|
||||
printk (KERN_WARNING
|
||||
"n_hdlc_tty_write: truncating user packet "
|
||||
"from %lu to %d\n", (unsigned long) count,
|
||||
maxframe );
|
||||
if (count > maxframe) {
|
||||
pr_debug("%s: truncating user packet from %zu to %d\n",
|
||||
__func__, count, maxframe);
|
||||
count = maxframe;
|
||||
}
|
||||
|
||||
|
@ -685,14 +528,6 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
|
|||
}
|
||||
schedule();
|
||||
|
||||
n_hdlc = tty2n_hdlc (tty);
|
||||
if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
|
||||
tty != n_hdlc->tty) {
|
||||
printk("n_hdlc_tty_write: %p invalid after wait!\n", n_hdlc);
|
||||
error = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (signal_pending(current)) {
|
||||
error = -EINTR;
|
||||
break;
|
||||
|
@ -708,8 +543,8 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
|
|||
|
||||
/* Send the data */
|
||||
tbuf->count = error = count;
|
||||
n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
|
||||
n_hdlc_send_frames(n_hdlc,tty);
|
||||
n_hdlc_buf_put(&n_hdlc->tx_buf_list, tbuf);
|
||||
n_hdlc_send_frames(n_hdlc, tty);
|
||||
}
|
||||
|
||||
return error;
|
||||
|
@ -728,32 +563,30 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
|
|||
static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
int error = 0;
|
||||
int count;
|
||||
unsigned long flags;
|
||||
struct n_hdlc_buf *buf = NULL;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_ioctl() called %d\n",
|
||||
__FILE__,__LINE__,cmd);
|
||||
pr_debug("%s() called %d\n", __func__, cmd);
|
||||
|
||||
/* Verify the status of the device */
|
||||
if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC)
|
||||
if (n_hdlc->magic != HDLC_MAGIC)
|
||||
return -EBADF;
|
||||
|
||||
switch (cmd) {
|
||||
case FIONREAD:
|
||||
/* report count of read data available */
|
||||
/* in next available frame (if any) */
|
||||
spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);
|
||||
spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock, flags);
|
||||
buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
|
||||
struct n_hdlc_buf, list_item);
|
||||
if (buf)
|
||||
count = buf->count;
|
||||
else
|
||||
count = 0;
|
||||
spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);
|
||||
spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock, flags);
|
||||
error = put_user(count, (int __user *)arg);
|
||||
break;
|
||||
|
||||
|
@ -761,12 +594,12 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
|
|||
/* get the pending tx byte count in the driver */
|
||||
count = tty_chars_in_buffer(tty);
|
||||
/* add size of next output frame in queue */
|
||||
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags);
|
||||
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
|
||||
buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
|
||||
struct n_hdlc_buf, list_item);
|
||||
if (buf)
|
||||
count += buf->count;
|
||||
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags);
|
||||
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
|
||||
error = put_user(count, (int __user *)arg);
|
||||
break;
|
||||
|
||||
|
@ -799,16 +632,16 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
|
|||
static __poll_t n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
|
||||
poll_table *wait)
|
||||
{
|
||||
struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
|
||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||
__poll_t mask = 0;
|
||||
|
||||
if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_tty_poll() called\n",__FILE__,__LINE__);
|
||||
|
||||
if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) {
|
||||
/* queue current process into any wait queue that */
|
||||
/* may awaken in the future (read and write) */
|
||||
if (n_hdlc->magic != HDLC_MAGIC)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* queue the current process into any wait queue that may awaken in the
|
||||
* future (read and write)
|
||||
*/
|
||||
poll_wait(filp, &tty->read_wait, wait);
|
||||
poll_wait(filp, &tty->write_wait, wait);
|
||||
|
||||
|
@ -822,10 +655,27 @@ static __poll_t n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
|
|||
if (!tty_is_writelocked(tty) &&
|
||||
!list_empty(&n_hdlc->tx_free_buf_list.list))
|
||||
mask |= EPOLLOUT | EPOLLWRNORM; /* writable */
|
||||
}
|
||||
|
||||
return mask;
|
||||
} /* end of n_hdlc_tty_poll() */
|
||||
|
||||
static void n_hdlc_alloc_buf(struct n_hdlc_buf_list *list, unsigned int count,
|
||||
const char *name)
|
||||
{
|
||||
struct n_hdlc_buf *buf;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
buf = kmalloc(struct_size(buf, buf, maxframe), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
pr_debug("%s(), kmalloc() failed for %s buffer %u\n",
|
||||
__func__, name, i);
|
||||
return;
|
||||
}
|
||||
n_hdlc_buf_put(list, buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* n_hdlc_alloc - allocate an n_hdlc instance data structure
|
||||
*
|
||||
|
@ -833,8 +683,6 @@ static __poll_t n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
|
|||
*/
|
||||
static struct n_hdlc *n_hdlc_alloc(void)
|
||||
{
|
||||
struct n_hdlc_buf *buf;
|
||||
int i;
|
||||
struct n_hdlc *n_hdlc = kzalloc(sizeof(*n_hdlc), GFP_KERNEL);
|
||||
|
||||
if (!n_hdlc)
|
||||
|
@ -850,27 +698,11 @@ static struct n_hdlc *n_hdlc_alloc(void)
|
|||
INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list);
|
||||
INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list);
|
||||
|
||||
/* allocate free rx buffer list */
|
||||
for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) {
|
||||
buf = kmalloc(struct_size(buf, buf, maxframe), GFP_KERNEL);
|
||||
if (buf)
|
||||
n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,buf);
|
||||
else if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_alloc(), kalloc() failed for rx buffer %d\n",__FILE__,__LINE__, i);
|
||||
}
|
||||
|
||||
/* allocate free tx buffer list */
|
||||
for(i=0;i<DEFAULT_TX_BUF_COUNT;i++) {
|
||||
buf = kmalloc(struct_size(buf, buf, maxframe), GFP_KERNEL);
|
||||
if (buf)
|
||||
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,buf);
|
||||
else if (debuglevel >= DEBUG_LEVEL_INFO)
|
||||
printk("%s(%d)n_hdlc_alloc(), kalloc() failed for tx buffer %d\n",__FILE__,__LINE__, i);
|
||||
}
|
||||
n_hdlc_alloc_buf(&n_hdlc->rx_free_buf_list, DEFAULT_RX_BUF_COUNT, "rx");
|
||||
n_hdlc_alloc_buf(&n_hdlc->tx_free_buf_list, DEFAULT_TX_BUF_COUNT, "tx");
|
||||
|
||||
/* Initialize the control block */
|
||||
n_hdlc->magic = HDLC_MAGIC;
|
||||
n_hdlc->flags = 0;
|
||||
|
||||
return n_hdlc;
|
||||
|
||||
|
@ -938,54 +770,50 @@ static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
|
|||
return buf;
|
||||
} /* end of n_hdlc_buf_get() */
|
||||
|
||||
static const char hdlc_banner[] __initconst =
|
||||
KERN_INFO "HDLC line discipline maxframe=%u\n";
|
||||
static const char hdlc_register_ok[] __initconst =
|
||||
KERN_INFO "N_HDLC line discipline registered.\n";
|
||||
static const char hdlc_register_fail[] __initconst =
|
||||
KERN_ERR "error registering line discipline: %d\n";
|
||||
static struct tty_ldisc_ops n_hdlc_ldisc = {
|
||||
.owner = THIS_MODULE,
|
||||
.magic = TTY_LDISC_MAGIC,
|
||||
.name = "hdlc",
|
||||
.open = n_hdlc_tty_open,
|
||||
.close = n_hdlc_tty_close,
|
||||
.read = n_hdlc_tty_read,
|
||||
.write = n_hdlc_tty_write,
|
||||
.ioctl = n_hdlc_tty_ioctl,
|
||||
.poll = n_hdlc_tty_poll,
|
||||
.receive_buf = n_hdlc_tty_receive,
|
||||
.write_wakeup = n_hdlc_tty_wakeup,
|
||||
.flush_buffer = flush_rx_queue,
|
||||
};
|
||||
|
||||
static int __init n_hdlc_init(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* range check maxframe arg */
|
||||
if (maxframe < 4096)
|
||||
maxframe = 4096;
|
||||
else if (maxframe > 65535)
|
||||
maxframe = 65535;
|
||||
|
||||
printk(hdlc_banner, maxframe);
|
||||
maxframe = clamp(maxframe, 4096, MAX_HDLC_FRAME_SIZE);
|
||||
|
||||
status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc);
|
||||
if (!status)
|
||||
printk(hdlc_register_ok);
|
||||
pr_info("N_HDLC line discipline registered with maxframe=%d\n",
|
||||
maxframe);
|
||||
else
|
||||
printk(hdlc_register_fail, status);
|
||||
pr_err("N_HDLC: error registering line discipline: %d\n",
|
||||
status);
|
||||
|
||||
return status;
|
||||
|
||||
} /* end of init_module() */
|
||||
|
||||
#ifdef CONFIG_SPARC
|
||||
#undef __exitdata
|
||||
#define __exitdata
|
||||
#endif
|
||||
|
||||
static const char hdlc_unregister_ok[] __exitdata =
|
||||
KERN_INFO "N_HDLC: line discipline unregistered\n";
|
||||
static const char hdlc_unregister_fail[] __exitdata =
|
||||
KERN_ERR "N_HDLC: can't unregister line discipline (err = %d)\n";
|
||||
|
||||
static void __exit n_hdlc_exit(void)
|
||||
{
|
||||
/* Release tty registration of line discipline */
|
||||
int status = tty_unregister_ldisc(N_HDLC);
|
||||
|
||||
if (status)
|
||||
printk(hdlc_unregister_fail, status);
|
||||
pr_err("N_HDLC: can't unregister line discipline (err = %d)\n",
|
||||
status);
|
||||
else
|
||||
printk(hdlc_unregister_ok);
|
||||
pr_info("N_HDLC: line discipline unregistered\n");
|
||||
}
|
||||
|
||||
module_init(n_hdlc_init);
|
||||
|
@ -993,6 +821,5 @@ module_exit(n_hdlc_exit);
|
|||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Paul Fulghum paulkf@microgate.com");
|
||||
module_param(debuglevel, int, 0);
|
||||
module_param(maxframe, int, 0);
|
||||
MODULE_ALIAS_LDISC(N_HDLC);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* n_tracesink.h - Kernel driver API to route trace data in kernel space.
|
||||
*
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
#ifdef N_TTY_TRACE
|
||||
# define n_tty_trace(f, args...) trace_printk(f, ##args)
|
||||
#else
|
||||
# define n_tty_trace(f, args...)
|
||||
# define n_tty_trace(f, args...) no_printk(f, ##args)
|
||||
#endif
|
||||
|
||||
struct n_tty_data {
|
||||
|
@ -654,9 +654,9 @@ static size_t __process_echoes(struct tty_struct *tty)
|
|||
op = echo_buf(ldata, tail + 1);
|
||||
|
||||
switch (op) {
|
||||
case ECHO_OP_ERASE_TAB: {
|
||||
unsigned int num_chars, num_bs;
|
||||
|
||||
case ECHO_OP_ERASE_TAB:
|
||||
if (MASK(ldata->echo_commit) == MASK(tail + 2))
|
||||
goto not_yet_stored;
|
||||
num_chars = echo_buf(ldata, tail + 2);
|
||||
|
@ -687,7 +687,7 @@ static size_t __process_echoes(struct tty_struct *tty)
|
|||
}
|
||||
tail += 3;
|
||||
break;
|
||||
|
||||
}
|
||||
case ECHO_OP_SET_CANON_COL:
|
||||
ldata->canon_column = ldata->column;
|
||||
tail += 2;
|
||||
|
|
|
@ -301,7 +301,7 @@ struct ctrl_dl {
|
|||
unsigned int DCD:1;
|
||||
unsigned int RI:1;
|
||||
unsigned int CTS:1;
|
||||
unsigned int reserverd:4;
|
||||
unsigned int reserved:4;
|
||||
u8 port;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
@ -839,40 +839,39 @@ static char *interrupt2str(u16 interrupt)
|
|||
static char buf[TMP_BUF_MAX];
|
||||
char *p = buf;
|
||||
|
||||
interrupt & MDM_DL1 ? p += snprintf(p, TMP_BUF_MAX, "MDM_DL1 ") : NULL;
|
||||
interrupt & MDM_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"MDM_DL2 ") : NULL;
|
||||
if (interrupt & MDM_DL1)
|
||||
p += scnprintf(p, TMP_BUF_MAX, "MDM_DL1 ");
|
||||
if (interrupt & MDM_DL2)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "MDM_DL2 ");
|
||||
if (interrupt & MDM_UL1)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "MDM_UL1 ");
|
||||
if (interrupt & MDM_UL2)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "MDM_UL2 ");
|
||||
if (interrupt & DIAG_DL1)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "DIAG_DL1 ");
|
||||
if (interrupt & DIAG_DL2)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "DIAG_DL2 ");
|
||||
|
||||
interrupt & MDM_UL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"MDM_UL1 ") : NULL;
|
||||
interrupt & MDM_UL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"MDM_UL2 ") : NULL;
|
||||
if (interrupt & DIAG_UL)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "DIAG_UL ");
|
||||
|
||||
interrupt & DIAG_DL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"DIAG_DL1 ") : NULL;
|
||||
interrupt & DIAG_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"DIAG_DL2 ") : NULL;
|
||||
if (interrupt & APP1_DL)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "APP1_DL ");
|
||||
if (interrupt & APP2_DL)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "APP2_DL ");
|
||||
|
||||
interrupt & DIAG_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"DIAG_UL ") : NULL;
|
||||
if (interrupt & APP1_UL)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "APP1_UL ");
|
||||
if (interrupt & APP2_UL)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "APP2_UL ");
|
||||
|
||||
interrupt & APP1_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"APP1_DL ") : NULL;
|
||||
interrupt & APP2_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"APP2_DL ") : NULL;
|
||||
if (interrupt & CTRL_DL)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "CTRL_DL ");
|
||||
if (interrupt & CTRL_UL)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "CTRL_UL ");
|
||||
|
||||
interrupt & APP1_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"APP1_UL ") : NULL;
|
||||
interrupt & APP2_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"APP2_UL ") : NULL;
|
||||
|
||||
interrupt & CTRL_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"CTRL_DL ") : NULL;
|
||||
interrupt & CTRL_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"CTRL_UL ") : NULL;
|
||||
|
||||
interrupt & RESET ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
|
||||
"RESET ") : NULL;
|
||||
if (interrupt & RESET)
|
||||
p += scnprintf(p, TMP_BUF_MAX - (p - buf), "RESET ");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Driver for 8250/16550-type serial ports
|
||||
*
|
||||
|
@ -156,7 +156,9 @@ void serial8250_rpm_put(struct uart_8250_port *p);
|
|||
void serial8250_rpm_get_tx(struct uart_8250_port *p);
|
||||
void serial8250_rpm_put_tx(struct uart_8250_port *p);
|
||||
|
||||
int serial8250_em485_init(struct uart_8250_port *p);
|
||||
int serial8250_em485_config(struct uart_port *port, struct serial_rs485 *rs485);
|
||||
void serial8250_em485_start_tx(struct uart_8250_port *p);
|
||||
void serial8250_em485_stop_tx(struct uart_8250_port *p);
|
||||
void serial8250_em485_destroy(struct uart_8250_port *p);
|
||||
|
||||
/* MCR <-> TIOCM conversion */
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
*
|
||||
* Based on 8250_lpc18xx.c:
|
||||
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
|
||||
*
|
||||
* The bcm2835aux is capable of RTS auto flow-control, but this driver doesn't
|
||||
* take advantage of it yet. When adding support, be sure not to enable it
|
||||
* simultaneously to rs485.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
@ -16,16 +20,64 @@
|
|||
|
||||
#include "8250.h"
|
||||
|
||||
#define BCM2835_AUX_UART_CNTL 8
|
||||
#define BCM2835_AUX_UART_CNTL_RXEN 0x01 /* Receiver enable */
|
||||
#define BCM2835_AUX_UART_CNTL_TXEN 0x02 /* Transmitter enable */
|
||||
#define BCM2835_AUX_UART_CNTL_AUTORTS 0x04 /* RTS set by RX fill level */
|
||||
#define BCM2835_AUX_UART_CNTL_AUTOCTS 0x08 /* CTS stops transmitter */
|
||||
#define BCM2835_AUX_UART_CNTL_RTS3 0x00 /* RTS set until 3 chars left */
|
||||
#define BCM2835_AUX_UART_CNTL_RTS2 0x10 /* RTS set until 2 chars left */
|
||||
#define BCM2835_AUX_UART_CNTL_RTS1 0x20 /* RTS set until 1 chars left */
|
||||
#define BCM2835_AUX_UART_CNTL_RTS4 0x30 /* RTS set until 4 chars left */
|
||||
#define BCM2835_AUX_UART_CNTL_RTSINV 0x40 /* Invert auto RTS polarity */
|
||||
#define BCM2835_AUX_UART_CNTL_CTSINV 0x80 /* Invert auto CTS polarity */
|
||||
|
||||
/**
|
||||
* struct bcm2835aux_data - driver private data of BCM2835 auxiliary UART
|
||||
* @clk: clock producer of the port's uartclk
|
||||
* @line: index of the port's serial8250_ports[] entry
|
||||
* @cntl: cached copy of CNTL register
|
||||
*/
|
||||
struct bcm2835aux_data {
|
||||
struct clk *clk;
|
||||
int line;
|
||||
u32 cntl;
|
||||
};
|
||||
|
||||
static void bcm2835aux_rs485_start_tx(struct uart_8250_port *up)
|
||||
{
|
||||
if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
|
||||
struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev);
|
||||
|
||||
data->cntl &= ~BCM2835_AUX_UART_CNTL_RXEN;
|
||||
serial_out(up, BCM2835_AUX_UART_CNTL, data->cntl);
|
||||
}
|
||||
|
||||
/*
|
||||
* On the bcm2835aux, the MCR register contains no other
|
||||
* flags besides RTS. So no need for a read-modify-write.
|
||||
*/
|
||||
if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND)
|
||||
serial8250_out_MCR(up, 0);
|
||||
else
|
||||
serial8250_out_MCR(up, UART_MCR_RTS);
|
||||
}
|
||||
|
||||
static void bcm2835aux_rs485_stop_tx(struct uart_8250_port *up)
|
||||
{
|
||||
if (up->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
|
||||
serial8250_out_MCR(up, 0);
|
||||
else
|
||||
serial8250_out_MCR(up, UART_MCR_RTS);
|
||||
|
||||
if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
|
||||
struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev);
|
||||
|
||||
data->cntl |= BCM2835_AUX_UART_CNTL_RXEN;
|
||||
serial_out(up, BCM2835_AUX_UART_CNTL, data->cntl);
|
||||
}
|
||||
}
|
||||
|
||||
static int bcm2835aux_serial_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_8250_port up = { };
|
||||
|
@ -47,6 +99,14 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev)
|
|||
up.port.fifosize = 8;
|
||||
up.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE |
|
||||
UPF_SKIP_TEST | UPF_IOREMAP;
|
||||
up.port.rs485_config = serial8250_em485_config;
|
||||
up.rs485_start_tx = bcm2835aux_rs485_start_tx;
|
||||
up.rs485_stop_tx = bcm2835aux_rs485_stop_tx;
|
||||
|
||||
/* initialize cached copy with power-on reset value */
|
||||
data->cntl = BCM2835_AUX_UART_CNTL_RXEN | BCM2835_AUX_UART_CNTL_TXEN;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
/* get the clock - this also enables the HW */
|
||||
data->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
@ -102,8 +162,6 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev)
|
|||
}
|
||||
data->line = ret;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
return 0;
|
||||
|
||||
dis_clk:
|
||||
|
@ -137,6 +195,24 @@ static struct platform_driver bcm2835aux_serial_driver = {
|
|||
};
|
||||
module_platform_driver(bcm2835aux_serial_driver);
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
||||
|
||||
static int __init early_bcm2835aux_setup(struct earlycon_device *device,
|
||||
const char *options)
|
||||
{
|
||||
if (!device->port.membase)
|
||||
return -ENODEV;
|
||||
|
||||
device->port.iotype = UPIO_MEM32;
|
||||
device->port.regshift = 2;
|
||||
|
||||
return early_serial8250_setup(device, NULL);
|
||||
}
|
||||
|
||||
OF_EARLYCON_DECLARE(bcm2835aux, "brcm,bcm2835-aux-uart",
|
||||
early_bcm2835aux_setup);
|
||||
#endif
|
||||
|
||||
MODULE_DESCRIPTION("BCM2835 auxiliar UART driver");
|
||||
MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -608,6 +608,14 @@ static int univ8250_console_setup(struct console *co, char *options)
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int univ8250_console_exit(struct console *co)
|
||||
{
|
||||
struct uart_port *port;
|
||||
|
||||
port = &serial8250_ports[co->index].port;
|
||||
return serial8250_console_exit(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* univ8250_console_match - non-standard console matching
|
||||
* @co: registering console
|
||||
|
@ -666,6 +674,7 @@ static struct console univ8250_console = {
|
|||
.write = univ8250_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = univ8250_console_setup,
|
||||
.exit = univ8250_console_exit,
|
||||
.match = univ8250_console_match,
|
||||
.flags = CON_PRINTBUFFER | CON_ANYTIME,
|
||||
.index = -1,
|
||||
|
@ -1007,14 +1016,18 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
|
|||
uart->port.unthrottle = up->port.unthrottle;
|
||||
uart->port.rs485_config = up->port.rs485_config;
|
||||
uart->port.rs485 = up->port.rs485;
|
||||
uart->rs485_start_tx = up->rs485_start_tx;
|
||||
uart->rs485_stop_tx = up->rs485_stop_tx;
|
||||
uart->dma = up->dma;
|
||||
|
||||
/* Take tx_loadsz from fifosize if it wasn't set separately */
|
||||
if (uart->port.fifosize && !uart->tx_loadsz)
|
||||
uart->tx_loadsz = uart->port.fifosize;
|
||||
|
||||
if (up->port.dev)
|
||||
if (up->port.dev) {
|
||||
uart->port.dev = up->port.dev;
|
||||
uart_get_rs485_mode(uart->port.dev, &uart->port.rs485);
|
||||
}
|
||||
|
||||
if (up->port.flags & UPF_FIXED_TYPE)
|
||||
uart->port.type = up->port.type;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/* Synopsys DesignWare 8250 library header file. */
|
||||
|
||||
#include <linux/types.h>
|
||||
|
|
|
@ -135,7 +135,7 @@ struct exar8250 {
|
|||
unsigned int nr;
|
||||
struct exar8250_board *board;
|
||||
void __iomem *virt;
|
||||
int line[0];
|
||||
int line[];
|
||||
};
|
||||
|
||||
static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old)
|
||||
|
|
|
@ -156,6 +156,11 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ehl_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
static const struct dw_dma_platform_data qrk_serial_dma_pdata = {
|
||||
.nr_channels = 2,
|
||||
|
@ -356,6 +361,7 @@ static const struct lpss8250_board byt_board = {
|
|||
static const struct lpss8250_board ehl_board = {
|
||||
.freq = 200000000,
|
||||
.base_baud = 12500000,
|
||||
.setup = ehl_serial_setup,
|
||||
};
|
||||
|
||||
static const struct lpss8250_board qrk_board = {
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */
|
||||
#define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */
|
||||
#define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */
|
||||
#define MTK_UART_DEBUG0 0x18
|
||||
#define MTK_UART_IER_XOFFI 0x20 /* Enable XOFF character interrupt */
|
||||
#define MTK_UART_IER_RTSI 0x40 /* Enable RTS Modem status interrupt */
|
||||
#define MTK_UART_IER_CTSI 0x80 /* Enable CTS Modem status interrupt */
|
||||
|
@ -388,9 +389,18 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
|||
static int __maybe_unused mtk8250_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mtk8250_data *data = dev_get_drvdata(dev);
|
||||
struct uart_8250_port *up = serial8250_get_port(data->line);
|
||||
|
||||
clk_disable_unprepare(data->uart_clk);
|
||||
/* wait until UART in idle status */
|
||||
while
|
||||
(serial_in(up, MTK_UART_DEBUG0));
|
||||
|
||||
if (data->clk_count == 0U) {
|
||||
dev_dbg(dev, "%s clock count is 0\n", __func__);
|
||||
} else {
|
||||
clk_disable_unprepare(data->bus_clk);
|
||||
data->clk_count--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -400,17 +410,17 @@ static int __maybe_unused mtk8250_runtime_resume(struct device *dev)
|
|||
struct mtk8250_data *data = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(data->uart_clk);
|
||||
if (err) {
|
||||
dev_warn(dev, "Can't enable clock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (data->clk_count > 0U) {
|
||||
dev_dbg(dev, "%s clock count is %d\n", __func__,
|
||||
data->clk_count);
|
||||
} else {
|
||||
err = clk_prepare_enable(data->bus_clk);
|
||||
if (err) {
|
||||
dev_warn(dev, "Can't enable bus clock\n");
|
||||
return err;
|
||||
}
|
||||
data->clk_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -419,12 +429,14 @@ static void
|
|||
mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
|
||||
{
|
||||
if (!state)
|
||||
if (!mtk8250_runtime_resume(port->dev))
|
||||
pm_runtime_get_sync(port->dev);
|
||||
|
||||
serial8250_do_pm(port, state, old);
|
||||
|
||||
if (state)
|
||||
pm_runtime_put_sync_suspend(port->dev);
|
||||
if (!pm_runtime_put_sync_suspend(port->dev))
|
||||
mtk8250_runtime_suspend(port->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
|
@ -501,6 +513,8 @@ static int mtk8250_probe(struct platform_device *pdev)
|
|||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->clk_count = 0;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
err = mtk8250_probe_of(pdev, &uart.port, data);
|
||||
if (err)
|
||||
|
@ -533,6 +547,7 @@ static int mtk8250_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
err = mtk8250_runtime_resume(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -541,9 +556,6 @@ static int mtk8250_probe(struct platform_device *pdev)
|
|||
if (data->line < 0)
|
||||
return data->line;
|
||||
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
data->rx_wakeup_irq = platform_get_irq_optional(pdev, 1);
|
||||
|
||||
return 0;
|
||||
|
@ -556,11 +568,13 @@ static int mtk8250_remove(struct platform_device *pdev)
|
|||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
serial8250_unregister_port(data->line);
|
||||
mtk8250_runtime_suspend(&pdev->dev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
mtk8250_runtime_suspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/console.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/of_address.h>
|
||||
|
@ -26,67 +25,16 @@ struct of_serial_info {
|
|||
int line;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA
|
||||
static void tegra_serial_handle_break(struct uart_port *p)
|
||||
{
|
||||
unsigned int status, tmout = 10000;
|
||||
|
||||
do {
|
||||
status = p->serial_in(p, UART_LSR);
|
||||
if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))
|
||||
status = p->serial_in(p, UART_RX);
|
||||
else
|
||||
break;
|
||||
if (--tmout == 0)
|
||||
break;
|
||||
udelay(1);
|
||||
} while (1);
|
||||
}
|
||||
#else
|
||||
static inline void tegra_serial_handle_break(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int of_8250_rs485_config(struct uart_port *port,
|
||||
struct serial_rs485 *rs485)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
|
||||
/* Clamp the delays to [0, 100ms] */
|
||||
rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U);
|
||||
rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U);
|
||||
|
||||
port->rs485 = *rs485;
|
||||
|
||||
/*
|
||||
* Both serial8250_em485_init and serial8250_em485_destroy
|
||||
* are idempotent
|
||||
*/
|
||||
if (rs485->flags & SER_RS485_ENABLED) {
|
||||
int ret = serial8250_em485_init(up);
|
||||
|
||||
if (ret) {
|
||||
rs485->flags &= ~SER_RS485_ENABLED;
|
||||
port->rs485.flags &= ~SER_RS485_ENABLED;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
serial8250_em485_destroy(up);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill a struct uart_port for a given device node
|
||||
*/
|
||||
static int of_platform_serial_setup(struct platform_device *ofdev,
|
||||
int type, struct uart_port *port,
|
||||
int type, struct uart_8250_port *up,
|
||||
struct of_serial_info *info)
|
||||
{
|
||||
struct resource resource;
|
||||
struct device_node *np = ofdev->dev.of_node;
|
||||
struct uart_port *port = &up->port;
|
||||
u32 clk, spd, prop;
|
||||
int ret, irq;
|
||||
|
||||
|
@ -207,13 +155,11 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
|
|||
port->flags |= UPF_SKIP_TEST;
|
||||
|
||||
port->dev = &ofdev->dev;
|
||||
port->rs485_config = of_8250_rs485_config;
|
||||
port->rs485_config = serial8250_em485_config;
|
||||
up->rs485_start_tx = serial8250_em485_start_tx;
|
||||
up->rs485_stop_tx = serial8250_em485_stop_tx;
|
||||
|
||||
switch (type) {
|
||||
case PORT_TEGRA:
|
||||
port->handle_break = tegra_serial_handle_break;
|
||||
break;
|
||||
|
||||
case PORT_RT2880:
|
||||
port->iotype = UPIO_AU;
|
||||
break;
|
||||
|
@ -258,7 +204,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
|
|||
return -ENOMEM;
|
||||
|
||||
memset(&port8250, 0, sizeof(port8250));
|
||||
ret = of_platform_serial_setup(ofdev, port_type, &port8250.port, info);
|
||||
ret = of_platform_serial_setup(ofdev, port_type, &port8250, info);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
|
@ -358,7 +304,6 @@ static const struct of_device_id of_platform_serial_table[] = {
|
|||
{ .compatible = "ns16550", .data = (void *)PORT_16550, },
|
||||
{ .compatible = "ns16750", .data = (void *)PORT_16750, },
|
||||
{ .compatible = "ns16850", .data = (void *)PORT_16850, },
|
||||
{ .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, },
|
||||
{ .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, },
|
||||
{ .compatible = "ralink,rt2880-uart", .data = (void *)PORT_RT2880, },
|
||||
{ .compatible = "intel,xscale-uart", .data = (void *)PORT_XSCALE, },
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
* The same errata is applicable to AM335x and DRA7x processors too.
|
||||
*/
|
||||
#define UART_ERRATA_CLOCK_DISABLE (1 << 3)
|
||||
#define UART_HAS_EFR2 BIT(4)
|
||||
|
||||
#define OMAP_UART_FCR_RX_TRIG 6
|
||||
#define OMAP_UART_FCR_TX_TRIG 4
|
||||
|
@ -93,6 +94,10 @@
|
|||
#define OMAP_UART_REV_52 0x0502
|
||||
#define OMAP_UART_REV_63 0x0603
|
||||
|
||||
/* Enhanced features register 2 */
|
||||
#define UART_OMAP_EFR2 0x23
|
||||
#define UART_OMAP_EFR2_TIMEOUT_BEHAVE BIT(6)
|
||||
|
||||
struct omap8250_priv {
|
||||
int line;
|
||||
u8 habit;
|
||||
|
@ -105,6 +110,8 @@ struct omap8250_priv {
|
|||
u8 delayed_restore;
|
||||
u16 quot;
|
||||
|
||||
u8 tx_trigger;
|
||||
u8 rx_trigger;
|
||||
bool is_suspending;
|
||||
int wakeirq;
|
||||
int wakeups_enabled;
|
||||
|
@ -118,6 +125,17 @@ struct omap8250_priv {
|
|||
bool throttled;
|
||||
};
|
||||
|
||||
struct omap8250_dma_params {
|
||||
u32 rx_size;
|
||||
u8 rx_trigger;
|
||||
u8 tx_trigger;
|
||||
};
|
||||
|
||||
struct omap8250_platdata {
|
||||
struct omap8250_dma_params *dma_params;
|
||||
u8 habit;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
static void omap_8250_rx_dma_flush(struct uart_8250_port *p);
|
||||
#else
|
||||
|
@ -295,8 +313,8 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
|
|||
serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_RESTORE(16) |
|
||||
OMAP_UART_TCR_HALT(52));
|
||||
serial_out(up, UART_TI752_TLR,
|
||||
TRIGGER_TLR_MASK(TX_TRIGGER) << UART_TI752_TLR_TX |
|
||||
TRIGGER_TLR_MASK(RX_TRIGGER) << UART_TI752_TLR_RX);
|
||||
TRIGGER_TLR_MASK(priv->tx_trigger) << UART_TI752_TLR_TX |
|
||||
TRIGGER_TLR_MASK(priv->rx_trigger) << UART_TI752_TLR_RX);
|
||||
|
||||
serial_out(up, UART_LCR, 0);
|
||||
|
||||
|
@ -435,8 +453,8 @@ static void omap_8250_set_termios(struct uart_port *port,
|
|||
* This is because threshold and trigger values are the same.
|
||||
*/
|
||||
up->fcr = UART_FCR_ENABLE_FIFO;
|
||||
up->fcr |= TRIGGER_FCR_MASK(TX_TRIGGER) << OMAP_UART_FCR_TX_TRIG;
|
||||
up->fcr |= TRIGGER_FCR_MASK(RX_TRIGGER) << OMAP_UART_FCR_RX_TRIG;
|
||||
up->fcr |= TRIGGER_FCR_MASK(priv->tx_trigger) << OMAP_UART_FCR_TX_TRIG;
|
||||
up->fcr |= TRIGGER_FCR_MASK(priv->rx_trigger) << OMAP_UART_FCR_RX_TRIG;
|
||||
|
||||
priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY |
|
||||
OMAP_UART_SCR_TX_TRIG_GRANU1_MASK;
|
||||
|
@ -651,7 +669,7 @@ static int omap_8250_startup(struct uart_port *port)
|
|||
priv->wer |= OMAP_UART_TX_WAKEUP_EN;
|
||||
serial_out(up, UART_OMAP_WER, priv->wer);
|
||||
|
||||
if (up->dma)
|
||||
if (up->dma && !(priv->habit & UART_HAS_EFR2))
|
||||
up->dma->rx_dma(up);
|
||||
|
||||
pm_runtime_mark_last_busy(port->dev);
|
||||
|
@ -676,6 +694,8 @@ static void omap_8250_shutdown(struct uart_port *port)
|
|||
pm_runtime_get_sync(port->dev);
|
||||
|
||||
serial_out(up, UART_OMAP_WER, 0);
|
||||
if (priv->habit & UART_HAS_EFR2)
|
||||
serial_out(up, UART_OMAP_EFR2, 0x0);
|
||||
|
||||
up->ier = 0;
|
||||
serial_out(up, UART_IER, 0);
|
||||
|
@ -699,14 +719,12 @@ static void omap_8250_shutdown(struct uart_port *port)
|
|||
static void omap_8250_throttle(struct uart_port *port)
|
||||
{
|
||||
struct omap8250_priv *priv = port->private_data;
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
unsigned long flags;
|
||||
|
||||
pm_runtime_get_sync(port->dev);
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
port->ops->stop_rx(port);
|
||||
priv->throttled = true;
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
|
@ -714,36 +732,6 @@ static void omap_8250_throttle(struct uart_port *port)
|
|||
pm_runtime_put_autosuspend(port->dev);
|
||||
}
|
||||
|
||||
static int omap_8250_rs485_config(struct uart_port *port,
|
||||
struct serial_rs485 *rs485)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
|
||||
/* Clamp the delays to [0, 100ms] */
|
||||
rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U);
|
||||
rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U);
|
||||
|
||||
port->rs485 = *rs485;
|
||||
|
||||
/*
|
||||
* Both serial8250_em485_init and serial8250_em485_destroy
|
||||
* are idempotent
|
||||
*/
|
||||
if (rs485->flags & SER_RS485_ENABLED) {
|
||||
int ret = serial8250_em485_init(up);
|
||||
|
||||
if (ret) {
|
||||
rs485->flags &= ~SER_RS485_ENABLED;
|
||||
port->rs485.flags &= ~SER_RS485_ENABLED;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
serial8250_em485_destroy(up);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_8250_unthrottle(struct uart_port *port)
|
||||
{
|
||||
struct omap8250_priv *priv = port->private_data;
|
||||
|
@ -757,6 +745,7 @@ static void omap_8250_unthrottle(struct uart_port *port)
|
|||
if (up->dma)
|
||||
up->dma->rx_dma(up);
|
||||
up->ier |= UART_IER_RLSI | UART_IER_RDI;
|
||||
port->read_status_mask |= UART_LSR_DR;
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
|
@ -767,32 +756,50 @@ static void omap_8250_unthrottle(struct uart_port *port)
|
|||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
static int omap_8250_rx_dma(struct uart_8250_port *p);
|
||||
|
||||
/* Must be called while priv->rx_dma_lock is held */
|
||||
static void __dma_rx_do_complete(struct uart_8250_port *p)
|
||||
{
|
||||
struct omap8250_priv *priv = p->port.private_data;
|
||||
struct uart_8250_dma *dma = p->dma;
|
||||
struct tty_port *tty_port = &p->port.state->port;
|
||||
struct dma_chan *rxchan = dma->rxchan;
|
||||
dma_cookie_t cookie;
|
||||
struct dma_tx_state state;
|
||||
int count;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&priv->rx_dma_lock, flags);
|
||||
|
||||
if (!dma->rx_running)
|
||||
goto unlock;
|
||||
goto out;
|
||||
|
||||
cookie = dma->rx_cookie;
|
||||
dma->rx_running = 0;
|
||||
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
|
||||
dmaengine_tx_status(rxchan, cookie, &state);
|
||||
|
||||
count = dma->rx_size - state.residue;
|
||||
count = dma->rx_size - state.residue + state.in_flight_bytes;
|
||||
if (count < dma->rx_size) {
|
||||
dmaengine_terminate_async(rxchan);
|
||||
|
||||
/*
|
||||
* Poll for teardown to complete which guarantees in
|
||||
* flight data is drained.
|
||||
*/
|
||||
if (state.in_flight_bytes) {
|
||||
int poll_count = 25;
|
||||
|
||||
while (dmaengine_tx_status(rxchan, cookie, NULL) &&
|
||||
poll_count--)
|
||||
cpu_relax();
|
||||
|
||||
if (!poll_count)
|
||||
dev_err(p->port.dev, "teardown incomplete\n");
|
||||
}
|
||||
}
|
||||
if (!count)
|
||||
goto out;
|
||||
ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
|
||||
|
||||
p->port.icount.rx += ret;
|
||||
p->port.icount.buf_overrun += count - ret;
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
|
||||
out:
|
||||
|
||||
tty_flip_buffer_push(tty_port);
|
||||
}
|
||||
|
@ -818,8 +825,12 @@ static void __dma_rx_complete(void *param)
|
|||
return;
|
||||
}
|
||||
__dma_rx_do_complete(p);
|
||||
if (!priv->throttled)
|
||||
if (!priv->throttled) {
|
||||
p->ier |= UART_IER_RLSI | UART_IER_RDI;
|
||||
serial_out(p, UART_IER, p->ier);
|
||||
if (!(priv->habit & UART_HAS_EFR2))
|
||||
omap_8250_rx_dma(p);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&p->port.lock, flags);
|
||||
}
|
||||
|
@ -845,10 +856,8 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p)
|
|||
if (WARN_ON_ONCE(ret))
|
||||
priv->rx_dma_broken = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
|
||||
|
||||
__dma_rx_do_complete(p);
|
||||
dmaengine_terminate_all(dma->rxchan);
|
||||
spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
|
||||
}
|
||||
|
||||
static int omap_8250_rx_dma(struct uart_8250_port *p)
|
||||
|
@ -864,8 +873,20 @@ static int omap_8250_rx_dma(struct uart_8250_port *p)
|
|||
|
||||
spin_lock_irqsave(&priv->rx_dma_lock, flags);
|
||||
|
||||
if (dma->rx_running)
|
||||
if (dma->rx_running) {
|
||||
enum dma_status state;
|
||||
|
||||
state = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, NULL);
|
||||
if (state == DMA_COMPLETE) {
|
||||
/*
|
||||
* Disable RX interrupts to allow RX DMA completion
|
||||
* callback to run.
|
||||
*/
|
||||
p->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
|
||||
serial_out(p, UART_IER, p->ier);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
|
||||
dma->rx_size, DMA_DEV_TO_MEM,
|
||||
|
@ -1036,6 +1057,46 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
|
|||
return omap_8250_rx_dma(up);
|
||||
}
|
||||
|
||||
static unsigned char omap_8250_handle_rx_dma(struct uart_8250_port *up,
|
||||
u8 iir, unsigned char status)
|
||||
{
|
||||
if ((status & (UART_LSR_DR | UART_LSR_BI)) &&
|
||||
(iir & UART_IIR_RDI)) {
|
||||
if (handle_rx_dma(up, iir)) {
|
||||
status = serial8250_rx_chars(up, status);
|
||||
omap_8250_rx_dma(up);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir,
|
||||
unsigned char status)
|
||||
{
|
||||
/*
|
||||
* Queue a new transfer if FIFO has data.
|
||||
*/
|
||||
if ((status & (UART_LSR_DR | UART_LSR_BI)) &&
|
||||
(up->ier & UART_IER_RDI)) {
|
||||
omap_8250_rx_dma(up);
|
||||
serial_out(up, UART_OMAP_EFR2, UART_OMAP_EFR2_TIMEOUT_BEHAVE);
|
||||
} else if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) {
|
||||
/*
|
||||
* Disable RX timeout, read IIR to clear
|
||||
* current timeout condition, clear EFR2 to
|
||||
* periodic timeouts, re-enable interrupts.
|
||||
*/
|
||||
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
omap_8250_rx_dma_flush(up);
|
||||
serial_in(up, UART_IIR);
|
||||
serial_out(up, UART_OMAP_EFR2, 0x0);
|
||||
up->ier |= UART_IER_RLSI | UART_IER_RDI;
|
||||
serial_out(up, UART_IER, up->ier);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is mostly serial8250_handle_irq(). We have a slightly different DMA
|
||||
* hoook for RX/TX and need different logic for them in the ISR. Therefore we
|
||||
|
@ -1044,6 +1105,7 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
|
|||
static int omap_8250_dma_handle_irq(struct uart_port *port)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
struct omap8250_priv *priv = up->port.private_data;
|
||||
unsigned char status;
|
||||
unsigned long flags;
|
||||
u8 iir;
|
||||
|
@ -1053,19 +1115,18 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
|
|||
iir = serial_port_in(port, UART_IIR);
|
||||
if (iir & UART_IIR_NO_INT) {
|
||||
serial8250_rpm_put(up);
|
||||
return 0;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
status = serial_port_in(port, UART_LSR);
|
||||
|
||||
if (status & (UART_LSR_DR | UART_LSR_BI)) {
|
||||
if (handle_rx_dma(up, iir)) {
|
||||
status = serial8250_rx_chars(up, status);
|
||||
omap_8250_rx_dma(up);
|
||||
}
|
||||
}
|
||||
if (priv->habit & UART_HAS_EFR2)
|
||||
am654_8250_handle_rx_dma(up, iir, status);
|
||||
else
|
||||
status = omap_8250_handle_rx_dma(up, iir, status);
|
||||
|
||||
serial8250_modem_status(up);
|
||||
if (status & UART_LSR_THRE && up->dma->tx_err) {
|
||||
if (uart_tx_stopped(&up->port) ||
|
||||
|
@ -1107,18 +1168,41 @@ static int omap8250_no_handle_irq(struct uart_port *port)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const u8 omap4_habit = UART_ERRATA_CLOCK_DISABLE;
|
||||
static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE;
|
||||
static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE;
|
||||
static struct omap8250_dma_params am654_dma = {
|
||||
.rx_size = SZ_2K,
|
||||
.rx_trigger = 1,
|
||||
.tx_trigger = TX_TRIGGER,
|
||||
};
|
||||
|
||||
static struct omap8250_dma_params am33xx_dma = {
|
||||
.rx_size = RX_TRIGGER,
|
||||
.rx_trigger = RX_TRIGGER,
|
||||
.tx_trigger = TX_TRIGGER,
|
||||
};
|
||||
|
||||
static struct omap8250_platdata am654_platdata = {
|
||||
.dma_params = &am654_dma,
|
||||
.habit = UART_HAS_EFR2,
|
||||
};
|
||||
|
||||
static struct omap8250_platdata am33xx_platdata = {
|
||||
.dma_params = &am33xx_dma,
|
||||
.habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE,
|
||||
};
|
||||
|
||||
static struct omap8250_platdata omap4_platdata = {
|
||||
.dma_params = &am33xx_dma,
|
||||
.habit = UART_ERRATA_CLOCK_DISABLE,
|
||||
};
|
||||
|
||||
static const struct of_device_id omap8250_dt_ids[] = {
|
||||
{ .compatible = "ti,am654-uart" },
|
||||
{ .compatible = "ti,am654-uart", .data = &am654_platdata, },
|
||||
{ .compatible = "ti,omap2-uart" },
|
||||
{ .compatible = "ti,omap3-uart" },
|
||||
{ .compatible = "ti,omap4-uart", .data = &omap4_habit, },
|
||||
{ .compatible = "ti,am3352-uart", .data = &am3352_habit, },
|
||||
{ .compatible = "ti,am4372-uart", .data = &am3352_habit, },
|
||||
{ .compatible = "ti,dra742-uart", .data = &dra742_habit, },
|
||||
{ .compatible = "ti,omap4-uart", .data = &omap4_platdata, },
|
||||
{ .compatible = "ti,am3352-uart", .data = &am33xx_platdata, },
|
||||
{ .compatible = "ti,am4372-uart", .data = &am33xx_platdata, },
|
||||
{ .compatible = "ti,dra742-uart", .data = &omap4_platdata, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap8250_dt_ids);
|
||||
|
@ -1129,10 +1213,10 @@ static int omap8250_probe(struct platform_device *pdev)
|
|||
struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct omap8250_priv *priv;
|
||||
const struct omap8250_platdata *pdata;
|
||||
struct uart_8250_port up;
|
||||
int ret;
|
||||
void __iomem *membase;
|
||||
const struct of_device_id *id;
|
||||
|
||||
if (!regs || !irq) {
|
||||
dev_err(&pdev->dev, "missing registers or irq\n");
|
||||
|
@ -1187,7 +1271,9 @@ static int omap8250_probe(struct platform_device *pdev)
|
|||
up.port.shutdown = omap_8250_shutdown;
|
||||
up.port.throttle = omap_8250_throttle;
|
||||
up.port.unthrottle = omap_8250_unthrottle;
|
||||
up.port.rs485_config = omap_8250_rs485_config;
|
||||
up.port.rs485_config = serial8250_em485_config;
|
||||
up.rs485_start_tx = serial8250_em485_start_tx;
|
||||
up.rs485_stop_tx = serial8250_em485_stop_tx;
|
||||
up.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
|
||||
|
||||
ret = of_alias_get_id(np, "serial");
|
||||
|
@ -1211,9 +1297,9 @@ static int omap8250_probe(struct platform_device *pdev)
|
|||
|
||||
priv->wakeirq = irq_of_parse_and_map(np, 1);
|
||||
|
||||
id = of_match_device(of_match_ptr(omap8250_dt_ids), &pdev->dev);
|
||||
if (id && id->data)
|
||||
priv->habit |= *(u8 *)id->data;
|
||||
pdata = of_device_get_match_data(&pdev->dev);
|
||||
if (pdata)
|
||||
priv->habit |= pdata->habit;
|
||||
|
||||
if (!up.port.uartclk) {
|
||||
up.port.uartclk = DEFAULT_CLK_SPEED;
|
||||
|
@ -1230,6 +1316,7 @@ static int omap8250_probe(struct platform_device *pdev)
|
|||
spin_lock_init(&priv->rx_dma_lock);
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
|
||||
/*
|
||||
|
@ -1243,12 +1330,13 @@ static int omap8250_probe(struct platform_device *pdev)
|
|||
pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
|
||||
|
||||
pm_runtime_irq_safe(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
omap_serial_fill_features_erratas(&up, priv);
|
||||
up.port.handle_irq = omap8250_no_handle_irq;
|
||||
priv->rx_trigger = RX_TRIGGER;
|
||||
priv->tx_trigger = TX_TRIGGER;
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
/*
|
||||
* Oh DMA support. If there are no DMA properties in the DT then
|
||||
|
@ -1260,13 +1348,26 @@ static int omap8250_probe(struct platform_device *pdev)
|
|||
*/
|
||||
ret = of_property_count_strings(np, "dma-names");
|
||||
if (ret == 2) {
|
||||
struct omap8250_dma_params *dma_params = NULL;
|
||||
|
||||
up.dma = &priv->omap8250_dma;
|
||||
priv->omap8250_dma.fn = the_no_dma_filter_fn;
|
||||
priv->omap8250_dma.tx_dma = omap_8250_tx_dma;
|
||||
priv->omap8250_dma.rx_dma = omap_8250_rx_dma;
|
||||
priv->omap8250_dma.rx_size = RX_TRIGGER;
|
||||
priv->omap8250_dma.rxconf.src_maxburst = RX_TRIGGER;
|
||||
priv->omap8250_dma.txconf.dst_maxburst = TX_TRIGGER;
|
||||
up.dma->fn = the_no_dma_filter_fn;
|
||||
up.dma->tx_dma = omap_8250_tx_dma;
|
||||
up.dma->rx_dma = omap_8250_rx_dma;
|
||||
if (pdata)
|
||||
dma_params = pdata->dma_params;
|
||||
|
||||
if (dma_params) {
|
||||
up.dma->rx_size = dma_params->rx_size;
|
||||
up.dma->rxconf.src_maxburst = dma_params->rx_trigger;
|
||||
up.dma->txconf.dst_maxburst = dma_params->tx_trigger;
|
||||
priv->rx_trigger = dma_params->rx_trigger;
|
||||
priv->tx_trigger = dma_params->tx_trigger;
|
||||
} else {
|
||||
up.dma->rx_size = RX_TRIGGER;
|
||||
up.dma->rxconf.src_maxburst = RX_TRIGGER;
|
||||
up.dma->txconf.dst_maxburst = TX_TRIGGER;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ret = serial8250_register_8250_port(&up);
|
||||
|
@ -1464,7 +1565,7 @@ static int omap8250_runtime_resume(struct device *dev)
|
|||
if (omap8250_lost_context(up))
|
||||
omap8250_restore_regs(up);
|
||||
|
||||
if (up->dma && up->dma->rxchan)
|
||||
if (up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2))
|
||||
omap_8250_rx_dma(up);
|
||||
|
||||
priv->latency = priv->calc_latency;
|
||||
|
|
|
@ -53,7 +53,7 @@ struct serial_private {
|
|||
unsigned int nr;
|
||||
struct pci_serial_quirk *quirk;
|
||||
const struct pciserial_board *board;
|
||||
int line[0];
|
||||
int line[];
|
||||
};
|
||||
|
||||
static const struct pci_device_id pci_use_msi[] = {
|
||||
|
|
|
@ -557,17 +557,6 @@ static void serial8250_clear_fifos(struct uart_8250_port *p)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void serial8250_em485_rts_after_send(struct uart_8250_port *p)
|
||||
{
|
||||
unsigned char mcr = serial8250_in_MCR(p);
|
||||
|
||||
if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
|
||||
mcr |= UART_MCR_RTS;
|
||||
else
|
||||
mcr &= ~UART_MCR_RTS;
|
||||
serial8250_out_MCR(p, mcr);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t);
|
||||
static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t);
|
||||
|
||||
|
@ -615,7 +604,7 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put);
|
|||
*
|
||||
* Return 0 - success, -errno - otherwise
|
||||
*/
|
||||
int serial8250_em485_init(struct uart_8250_port *p)
|
||||
static int serial8250_em485_init(struct uart_8250_port *p)
|
||||
{
|
||||
if (p->em485)
|
||||
return 0;
|
||||
|
@ -632,11 +621,12 @@ int serial8250_em485_init(struct uart_8250_port *p)
|
|||
p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx;
|
||||
p->em485->port = p;
|
||||
p->em485->active_timer = NULL;
|
||||
serial8250_em485_rts_after_send(p);
|
||||
p->em485->tx_stopped = true;
|
||||
|
||||
p->rs485_stop_tx(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serial8250_em485_init);
|
||||
|
||||
/**
|
||||
* serial8250_em485_destroy() - put uart_8250_port into normal state
|
||||
|
@ -664,6 +654,52 @@ void serial8250_em485_destroy(struct uart_8250_port *p)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(serial8250_em485_destroy);
|
||||
|
||||
/**
|
||||
* serial8250_em485_config() - generic ->rs485_config() callback
|
||||
* @port: uart port
|
||||
* @rs485: rs485 settings
|
||||
*
|
||||
* Generic callback usable by 8250 uart drivers to activate rs485 settings
|
||||
* if the uart is incapable of driving RTS as a Transmit Enable signal in
|
||||
* hardware, relying on software emulation instead.
|
||||
*/
|
||||
int serial8250_em485_config(struct uart_port *port, struct serial_rs485 *rs485)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
|
||||
/* pick sane settings if the user hasn't */
|
||||
if (!!(rs485->flags & SER_RS485_RTS_ON_SEND) ==
|
||||
!!(rs485->flags & SER_RS485_RTS_AFTER_SEND)) {
|
||||
rs485->flags |= SER_RS485_RTS_ON_SEND;
|
||||
rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
|
||||
}
|
||||
|
||||
/* clamp the delays to [0, 100ms] */
|
||||
rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U);
|
||||
rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U);
|
||||
|
||||
memset(rs485->padding, 0, sizeof(rs485->padding));
|
||||
port->rs485 = *rs485;
|
||||
|
||||
/*
|
||||
* Both serial8250_em485_init() and serial8250_em485_destroy()
|
||||
* are idempotent.
|
||||
*/
|
||||
if (rs485->flags & SER_RS485_ENABLED) {
|
||||
int ret = serial8250_em485_init(up);
|
||||
|
||||
if (ret) {
|
||||
rs485->flags &= ~SER_RS485_ENABLED;
|
||||
port->rs485.flags &= ~SER_RS485_ENABLED;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
serial8250_em485_destroy(up);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serial8250_em485_config);
|
||||
|
||||
/*
|
||||
* These two wrappers ensure that enable_runtime_pm_tx() can be called more than
|
||||
* once and disable_runtime_pm_tx() will still disable RPM because the fifo is
|
||||
|
@ -1318,8 +1354,8 @@ out_lock:
|
|||
fintek_8250_probe(up);
|
||||
|
||||
if (up->capabilities != old_capabilities) {
|
||||
pr_warn("%s: detected caps %08x should be %08x\n",
|
||||
port->name, old_capabilities, up->capabilities);
|
||||
dev_warn(port->dev, "detected caps %08x should be %08x\n",
|
||||
old_capabilities, up->capabilities);
|
||||
}
|
||||
out:
|
||||
DEBUG_AUTOCONF("iir=%d ", scratch);
|
||||
|
@ -1394,9 +1430,21 @@ static void serial8250_stop_rx(struct uart_port *port)
|
|||
serial8250_rpm_put(up);
|
||||
}
|
||||
|
||||
static void __do_stop_tx_rs485(struct uart_8250_port *p)
|
||||
/**
|
||||
* serial8250_em485_stop_tx() - generic ->rs485_stop_tx() callback
|
||||
* @up: uart 8250 port
|
||||
*
|
||||
* Generic callback usable by 8250 uart drivers to stop rs485 transmission.
|
||||
*/
|
||||
void serial8250_em485_stop_tx(struct uart_8250_port *p)
|
||||
{
|
||||
serial8250_em485_rts_after_send(p);
|
||||
unsigned char mcr = serial8250_in_MCR(p);
|
||||
|
||||
if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
|
||||
mcr |= UART_MCR_RTS;
|
||||
else
|
||||
mcr &= ~UART_MCR_RTS;
|
||||
serial8250_out_MCR(p, mcr);
|
||||
|
||||
/*
|
||||
* Empty the RX FIFO, we are not interested in anything
|
||||
|
@ -1410,6 +1458,8 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p)
|
|||
serial_port_out(&p->port, UART_IER, p->ier);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx);
|
||||
|
||||
static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t)
|
||||
{
|
||||
struct uart_8250_em485 *em485;
|
||||
|
@ -1422,8 +1472,9 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t)
|
|||
serial8250_rpm_get(p);
|
||||
spin_lock_irqsave(&p->port.lock, flags);
|
||||
if (em485->active_timer == &em485->stop_tx_timer) {
|
||||
__do_stop_tx_rs485(p);
|
||||
p->rs485_stop_tx(p);
|
||||
em485->active_timer = NULL;
|
||||
em485->tx_stopped = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&p->port.lock, flags);
|
||||
serial8250_rpm_put(p);
|
||||
|
@ -1444,7 +1495,7 @@ static void __stop_tx_rs485(struct uart_8250_port *p)
|
|||
struct uart_8250_em485 *em485 = p->em485;
|
||||
|
||||
/*
|
||||
* __do_stop_tx_rs485 is going to set RTS according to config
|
||||
* rs485_stop_tx() is going to set RTS according to config
|
||||
* AND flush RX FIFO if required.
|
||||
*/
|
||||
if (p->port.rs485.delay_rts_after_send > 0) {
|
||||
|
@ -1452,7 +1503,9 @@ static void __stop_tx_rs485(struct uart_8250_port *p)
|
|||
start_hrtimer_ms(&em485->stop_tx_timer,
|
||||
p->port.rs485.delay_rts_after_send);
|
||||
} else {
|
||||
__do_stop_tx_rs485(p);
|
||||
p->rs485_stop_tx(p);
|
||||
em485->active_timer = NULL;
|
||||
em485->tx_stopped = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1477,8 +1530,6 @@ static inline void __stop_tx(struct uart_8250_port *p)
|
|||
if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
|
||||
return;
|
||||
|
||||
em485->active_timer = NULL;
|
||||
|
||||
__stop_tx_rs485(p);
|
||||
}
|
||||
__do_stop_tx(p);
|
||||
|
@ -1528,25 +1579,42 @@ static inline void __start_tx(struct uart_port *port)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void start_tx_rs485(struct uart_port *port)
|
||||
/**
|
||||
* serial8250_em485_start_tx() - generic ->rs485_start_tx() callback
|
||||
* @up: uart 8250 port
|
||||
*
|
||||
* Generic callback usable by 8250 uart drivers to start rs485 transmission.
|
||||
* Assumes that setting the RTS bit in the MCR register means RTS is high.
|
||||
* (Some chips use inverse semantics.) Further assumes that reception is
|
||||
* stoppable by disabling the UART_IER_RDI interrupt. (Some chips set the
|
||||
* UART_LSR_DR bit even when UART_IER_RDI is disabled, foiling this approach.)
|
||||
*/
|
||||
void serial8250_em485_start_tx(struct uart_8250_port *up)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
struct uart_8250_em485 *em485 = up->em485;
|
||||
unsigned char mcr;
|
||||
unsigned char mcr = serial8250_in_MCR(up);
|
||||
|
||||
if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX))
|
||||
serial8250_stop_rx(&up->port);
|
||||
|
||||
em485->active_timer = NULL;
|
||||
|
||||
mcr = serial8250_in_MCR(up);
|
||||
if (!!(up->port.rs485.flags & SER_RS485_RTS_ON_SEND) !=
|
||||
!!(mcr & UART_MCR_RTS)) {
|
||||
if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND)
|
||||
mcr |= UART_MCR_RTS;
|
||||
else
|
||||
mcr &= ~UART_MCR_RTS;
|
||||
serial8250_out_MCR(up, mcr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serial8250_em485_start_tx);
|
||||
|
||||
static inline void start_tx_rs485(struct uart_port *port)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
struct uart_8250_em485 *em485 = up->em485;
|
||||
|
||||
em485->active_timer = NULL;
|
||||
|
||||
if (em485->tx_stopped) {
|
||||
em485->tx_stopped = false;
|
||||
|
||||
up->rs485_start_tx(up);
|
||||
|
||||
if (up->port.rs485.delay_rts_before_send > 0) {
|
||||
em485->active_timer = &em485->start_tx_timer;
|
||||
|
@ -1683,7 +1751,7 @@ void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr)
|
|||
lsr &= port->read_status_mask;
|
||||
|
||||
if (lsr & UART_LSR_BI) {
|
||||
pr_debug("%s: handling break\n", __func__);
|
||||
dev_dbg(port->dev, "handling break\n");
|
||||
flag = TTY_BREAK;
|
||||
} else if (lsr & UART_LSR_PE)
|
||||
flag = TTY_PARITY;
|
||||
|
@ -1815,6 +1883,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
|
|||
unsigned char status;
|
||||
unsigned long flags;
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
bool skip_rx = false;
|
||||
|
||||
if (iir & UART_IIR_NO_INT)
|
||||
return 0;
|
||||
|
@ -1823,7 +1892,20 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
|
|||
|
||||
status = serial_port_in(port, UART_LSR);
|
||||
|
||||
if (status & (UART_LSR_DR | UART_LSR_BI)) {
|
||||
/*
|
||||
* If port is stopped and there are no error conditions in the
|
||||
* FIFO, then don't drain the FIFO, as this may lead to TTY buffer
|
||||
* overflow. Not servicing, RX FIFO would trigger auto HW flow
|
||||
* control when FIFO occupancy reaches preset threshold, thus
|
||||
* halting RX. This only works when auto HW flow control is
|
||||
* available.
|
||||
*/
|
||||
if (!(status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) &&
|
||||
(port->status & (UPSTAT_AUTOCTS | UPSTAT_AUTORTS)) &&
|
||||
!(port->read_status_mask & UART_LSR_DR))
|
||||
skip_rx = true;
|
||||
|
||||
if (status & (UART_LSR_DR | UART_LSR_BI) && !skip_rx) {
|
||||
if (!up->dma || handle_rx_dma(up, iir))
|
||||
status = serial8250_rx_chars(up, status);
|
||||
}
|
||||
|
@ -1924,6 +2006,13 @@ void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
unsigned char mcr;
|
||||
|
||||
if (port->rs485.flags & SER_RS485_ENABLED) {
|
||||
if (serial8250_in_MCR(up) & UART_MCR_RTS)
|
||||
mctrl |= TIOCM_RTS;
|
||||
else
|
||||
mctrl &= ~TIOCM_RTS;
|
||||
}
|
||||
|
||||
mcr = serial8250_TIOCM_to_MCR(mctrl);
|
||||
|
||||
mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
|
||||
|
@ -2134,7 +2223,7 @@ int serial8250_do_startup(struct uart_port *port)
|
|||
*/
|
||||
if (!(port->flags & UPF_BUGGY_UART) &&
|
||||
(serial_port_in(port, UART_LSR) == 0xff)) {
|
||||
pr_info_ratelimited("%s: LSR safety check engaged!\n", port->name);
|
||||
dev_info_ratelimited(port->dev, "LSR safety check engaged!\n");
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2166,8 +2255,7 @@ int serial8250_do_startup(struct uart_port *port)
|
|||
(port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) {
|
||||
/* Bounds checking of TX threshold (valid 0 to fifosize-2) */
|
||||
if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) {
|
||||
pr_err("%s TX FIFO Threshold errors, skipping\n",
|
||||
port->name);
|
||||
dev_err(port->dev, "TX FIFO Threshold errors, skipping\n");
|
||||
} else {
|
||||
serial_port_out(port, UART_ALTR_AFR,
|
||||
UART_ALTR_EN_TXFIFO_LW);
|
||||
|
@ -2268,8 +2356,7 @@ int serial8250_do_startup(struct uart_port *port)
|
|||
if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
|
||||
if (!(up->bugs & UART_BUG_TXEN)) {
|
||||
up->bugs |= UART_BUG_TXEN;
|
||||
pr_debug("%s - enabling bad tx status workarounds\n",
|
||||
port->name);
|
||||
dev_dbg(port->dev, "enabling bad tx status workarounds\n");
|
||||
}
|
||||
} else {
|
||||
up->bugs &= ~UART_BUG_TXEN;
|
||||
|
@ -2294,10 +2381,14 @@ dont_test_tx_en:
|
|||
* Request DMA channels for both RX and TX.
|
||||
*/
|
||||
if (up->dma) {
|
||||
retval = serial8250_request_dma(up);
|
||||
if (retval) {
|
||||
pr_warn_ratelimited("%s - failed to request DMA\n",
|
||||
port->name);
|
||||
const char *msg = NULL;
|
||||
|
||||
if (uart_console(port))
|
||||
msg = "forbid DMA for kernel console";
|
||||
else if (serial8250_request_dma(up))
|
||||
msg = "failed to request DMA";
|
||||
if (msg) {
|
||||
dev_warn_ratelimited(port->dev, "%s\n", msg);
|
||||
up->dma = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -2880,7 +2971,7 @@ static int do_serial8250_get_rxtrig(struct tty_port *port)
|
|||
return rxtrig_bytes;
|
||||
}
|
||||
|
||||
static ssize_t serial8250_get_attr_rx_trig_bytes(struct device *dev,
|
||||
static ssize_t rx_trig_bytes_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tty_port *port = dev_get_drvdata(dev);
|
||||
|
@ -2926,7 +3017,7 @@ static int do_serial8250_set_rxtrig(struct tty_port *port, unsigned char bytes)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t serial8250_set_attr_rx_trig_bytes(struct device *dev,
|
||||
static ssize_t rx_trig_bytes_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct tty_port *port = dev_get_drvdata(dev);
|
||||
|
@ -2947,18 +3038,16 @@ static ssize_t serial8250_set_attr_rx_trig_bytes(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(rx_trig_bytes, S_IRUSR | S_IWUSR | S_IRGRP,
|
||||
serial8250_get_attr_rx_trig_bytes,
|
||||
serial8250_set_attr_rx_trig_bytes);
|
||||
static DEVICE_ATTR_RW(rx_trig_bytes);
|
||||
|
||||
static struct attribute *serial8250_dev_attrs[] = {
|
||||
&dev_attr_rx_trig_bytes.attr,
|
||||
NULL,
|
||||
};
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group serial8250_dev_attr_group = {
|
||||
.attrs = serial8250_dev_attrs,
|
||||
};
|
||||
};
|
||||
|
||||
static void register_dev_spec_attr_grp(struct uart_8250_port *up)
|
||||
{
|
||||
|
@ -2987,6 +3076,9 @@ static void serial8250_config_port(struct uart_port *port, int flags)
|
|||
if (flags & UART_CONFIG_TYPE)
|
||||
autoconfig(up);
|
||||
|
||||
if (port->rs485.flags & SER_RS485_ENABLED)
|
||||
port->rs485_config(port, &port->rs485);
|
||||
|
||||
/* if access method is AU, it is a 16550 with a quirk */
|
||||
if (port->type == PORT_16550A && port->iotype == UPIO_AU)
|
||||
up->bugs |= UART_BUG_NOMSR;
|
||||
|
@ -3127,10 +3219,14 @@ static void serial8250_console_restore(struct uart_8250_port *up)
|
|||
* any possible real use of the port...
|
||||
*
|
||||
* The console_lock must be held when we get here.
|
||||
*
|
||||
* Doing runtime PM is really a bad idea for the kernel console.
|
||||
* Thus, we assume the function is called when device is powered up.
|
||||
*/
|
||||
void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
struct uart_8250_em485 *em485 = up->em485;
|
||||
struct uart_port *port = &up->port;
|
||||
unsigned long flags;
|
||||
unsigned int ier;
|
||||
|
@ -3138,8 +3234,6 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
|||
|
||||
touch_nmi_watchdog();
|
||||
|
||||
serial8250_rpm_get(up);
|
||||
|
||||
if (oops_in_progress)
|
||||
locked = spin_trylock_irqsave(&port->lock, flags);
|
||||
else
|
||||
|
@ -3161,6 +3255,12 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
|||
up->canary = 0;
|
||||
}
|
||||
|
||||
if (em485) {
|
||||
if (em485->tx_stopped)
|
||||
up->rs485_start_tx(up);
|
||||
mdelay(port->rs485.delay_rts_before_send);
|
||||
}
|
||||
|
||||
uart_console_write(port, s, count, serial8250_console_putchar);
|
||||
|
||||
/*
|
||||
|
@ -3168,6 +3268,13 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
|||
* and restore the IER
|
||||
*/
|
||||
wait_for_xmitr(up, BOTH_EMPTY);
|
||||
|
||||
if (em485) {
|
||||
mdelay(port->rs485.delay_rts_after_send);
|
||||
if (em485->tx_stopped)
|
||||
up->rs485_stop_tx(up);
|
||||
}
|
||||
|
||||
serial_port_out(port, UART_IER, ier);
|
||||
|
||||
/*
|
||||
|
@ -3182,7 +3289,6 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
|||
|
||||
if (locked)
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
serial8250_rpm_put(up);
|
||||
}
|
||||
|
||||
static unsigned int probe_baud(struct uart_port *port)
|
||||
|
@ -3206,6 +3312,7 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
|
|||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
int ret;
|
||||
|
||||
if (!port->iobase && !port->membase)
|
||||
return -ENODEV;
|
||||
|
@ -3215,7 +3322,22 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
|
|||
else if (probe)
|
||||
baud = probe_baud(port);
|
||||
|
||||
return uart_set_options(port, port->cons, baud, parity, bits, flow);
|
||||
ret = uart_set_options(port, port->cons, baud, parity, bits, flow);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (port->dev)
|
||||
pm_runtime_get_sync(port->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serial8250_console_exit(struct uart_port *port)
|
||||
{
|
||||
if (port->dev)
|
||||
pm_runtime_put_sync(port->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SERIAL_8250_CONSOLE */
|
||||
|
|
|
@ -123,7 +123,7 @@ static int serial_pxa_probe(struct platform_device *pdev)
|
|||
uart.port.regshift = 2;
|
||||
uart.port.irq = irqres->start;
|
||||
uart.port.fifosize = 64;
|
||||
uart.port.flags = UPF_IOREMAP | UPF_SKIP_TEST;
|
||||
uart.port.flags = UPF_IOREMAP | UPF_SKIP_TEST | UPF_FIXED_TYPE;
|
||||
uart.port.dev = &pdev->dev;
|
||||
uart.port.uartclk = clk_get_rate(data->clk);
|
||||
uart.port.pm = serial_pxa_pm;
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Serial Port driver for Tegra devices
|
||||
*
|
||||
* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
struct tegra_uart {
|
||||
struct clk *clk;
|
||||
struct reset_control *rst;
|
||||
int line;
|
||||
};
|
||||
|
||||
static void tegra_uart_handle_break(struct uart_port *p)
|
||||
{
|
||||
unsigned int status, tmout = 10000;
|
||||
|
||||
do {
|
||||
status = p->serial_in(p, UART_LSR);
|
||||
if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))
|
||||
status = p->serial_in(p, UART_RX);
|
||||
else
|
||||
break;
|
||||
if (--tmout == 0)
|
||||
break;
|
||||
udelay(1);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int tegra_uart_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct uart_8250_port port8250;
|
||||
struct tegra_uart *uart;
|
||||
struct uart_port *port;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
uart = devm_kzalloc(&pdev->dev, sizeof(*uart), GFP_KERNEL);
|
||||
if (!uart)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&port8250, 0, sizeof(port8250));
|
||||
|
||||
port = &port8250.port;
|
||||
spin_lock_init(&port->lock);
|
||||
|
||||
port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT |
|
||||
UPF_FIXED_TYPE;
|
||||
port->iotype = UPIO_MEM32;
|
||||
port->regshift = 2;
|
||||
port->type = PORT_TEGRA;
|
||||
port->irqflags |= IRQF_SHARED;
|
||||
port->dev = &pdev->dev;
|
||||
port->handle_break = tegra_uart_handle_break;
|
||||
|
||||
ret = of_alias_get_id(pdev->dev.of_node, "serial");
|
||||
if (ret >= 0)
|
||||
port->line = ret;
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
port->irq = ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
port->membase = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!port->membase)
|
||||
return -ENOMEM;
|
||||
|
||||
port->mapbase = res->start;
|
||||
port->mapsize = resource_size(res);
|
||||
|
||||
uart->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
|
||||
if (IS_ERR(uart->rst))
|
||||
return PTR_ERR(uart->rst);
|
||||
|
||||
if (device_property_read_u32(&pdev->dev, "clock-frequency",
|
||||
&port->uartclk)) {
|
||||
uart->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(uart->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(uart->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
port->uartclk = clk_get_rate(uart->clk);
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(uart->rst);
|
||||
if (ret)
|
||||
goto err_clkdisable;
|
||||
|
||||
ret = serial8250_register_8250_port(&port8250);
|
||||
if (ret < 0)
|
||||
goto err_clkdisable;
|
||||
|
||||
platform_set_drvdata(pdev, uart);
|
||||
uart->line = ret;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clkdisable:
|
||||
clk_disable_unprepare(uart->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_uart_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_uart *uart = platform_get_drvdata(pdev);
|
||||
|
||||
serial8250_unregister_port(uart->line);
|
||||
reset_control_assert(uart->rst);
|
||||
clk_disable_unprepare(uart->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_uart_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_uart *uart = dev_get_drvdata(dev);
|
||||
struct uart_8250_port *port8250 = serial8250_get_port(uart->line);
|
||||
struct uart_port *port = &port8250->port;
|
||||
|
||||
serial8250_suspend_port(uart->line);
|
||||
|
||||
if (!uart_console(port) || console_suspend_enabled)
|
||||
clk_disable_unprepare(uart->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_uart_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_uart *uart = dev_get_drvdata(dev);
|
||||
struct uart_8250_port *port8250 = serial8250_get_port(uart->line);
|
||||
struct uart_port *port = &port8250->port;
|
||||
|
||||
if (!uart_console(port) || console_suspend_enabled)
|
||||
clk_prepare_enable(uart->clk);
|
||||
|
||||
serial8250_resume_port(uart->line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tegra_uart_pm_ops, tegra_uart_suspend,
|
||||
tegra_uart_resume);
|
||||
|
||||
static const struct of_device_id tegra_uart_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-uart", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_uart_of_match);
|
||||
|
||||
static const struct acpi_device_id tegra_uart_acpi_match[] = {
|
||||
{ "NVDA0100", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, tegra_uart_acpi_match);
|
||||
|
||||
static struct platform_driver tegra_uart_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-uart",
|
||||
.pm = &tegra_uart_pm_ops,
|
||||
.of_match_table = tegra_uart_of_match,
|
||||
.acpi_match_table = ACPI_PTR(tegra_uart_acpi_match),
|
||||
},
|
||||
.probe = tegra_uart_probe,
|
||||
.remove = tegra_uart_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(tegra_uart_driver);
|
||||
|
||||
MODULE_AUTHOR("Jeff Brasen <jbrasen@nvidia.com>");
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra 8250 Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -500,6 +500,15 @@ config SERIAL_8250_PXA
|
|||
applicable to both devicetree and legacy boards, and early console is
|
||||
part of its support.
|
||||
|
||||
config SERIAL_8250_TEGRA
|
||||
tristate "8250 support for Tegra serial ports"
|
||||
default SERIAL_8250
|
||||
depends on SERIAL_8250
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
help
|
||||
Select this option if you have machine with an NVIDIA Tegra SoC and
|
||||
wish to enable 8250 serial driver for the Tegra serial interfaces.
|
||||
|
||||
config SERIAL_OF_PLATFORM
|
||||
tristate "Devicetree based probing for 8250 ports"
|
||||
depends on SERIAL_8250 && OF
|
||||
|
|
|
@ -37,6 +37,7 @@ obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
|
|||
obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
|
||||
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
|
||||
obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o
|
||||
obj-$(CONFIG_SERIAL_8250_TEGRA) += 8250_tegra.o
|
||||
obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o
|
||||
|
||||
CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt
|
||||
|
|
|
@ -261,15 +261,6 @@ config SERIAL_SAMSUNG_UARTS
|
|||
Select the number of available UART ports for the Samsung S3C
|
||||
serial driver
|
||||
|
||||
config SERIAL_SAMSUNG_DEBUG
|
||||
bool "Samsung SoC serial debug"
|
||||
depends on SERIAL_SAMSUNG && DEBUG_LL
|
||||
help
|
||||
Add support for debugging the serial driver. Since this is
|
||||
generally being used as a console, we use our own output
|
||||
routines that go via the low-level debug printascii()
|
||||
function.
|
||||
|
||||
config SERIAL_SAMSUNG_CONSOLE
|
||||
bool "Support for console on Samsung SoC serial port"
|
||||
depends on SERIAL_SAMSUNG=y
|
||||
|
@ -1111,7 +1102,7 @@ config SERIAL_SC16IS7XX_SPI
|
|||
help
|
||||
Enable SC16IS7xx driver on SPI bus,
|
||||
If required say y, and say n to spi if not required,
|
||||
This is additional support to exsisting driver.
|
||||
This is additional support to existing driver.
|
||||
You must select at least one bus for the driver to be built.
|
||||
|
||||
config SERIAL_TIMBERDALE
|
||||
|
@ -1279,6 +1270,7 @@ config SERIAL_AR933X
|
|||
tristate "AR933X serial port support"
|
||||
depends on HAVE_CLK && ATH79
|
||||
select SERIAL_CORE
|
||||
select SERIAL_MCTRL_GPIO if GPIOLIB
|
||||
help
|
||||
If you have an Atheros AR933X SOC based board and want to use the
|
||||
built-in UART of the SoC, say Y to this option.
|
||||
|
@ -1452,8 +1444,8 @@ config SERIAL_MEN_Z135
|
|||
|
||||
config SERIAL_SPRD
|
||||
tristate "Support for Spreadtrum serial"
|
||||
depends on ARCH_SPRD
|
||||
select SERIAL_CORE
|
||||
depends on COMMON_CLK
|
||||
help
|
||||
This enables the driver for the Spreadtrum's serial.
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
@ -29,6 +30,8 @@
|
|||
|
||||
#include <asm/mach-ath79/ar933x_uart.h>
|
||||
|
||||
#include "serial_mctrl_gpio.h"
|
||||
|
||||
#define DRIVER_NAME "ar933x-uart"
|
||||
|
||||
#define AR933X_UART_MAX_SCALE 0xff
|
||||
|
@ -47,6 +50,8 @@ struct ar933x_uart_port {
|
|||
unsigned int min_baud;
|
||||
unsigned int max_baud;
|
||||
struct clk *clk;
|
||||
struct mctrl_gpios *gpios;
|
||||
struct gpio_desc *rts_gpiod;
|
||||
};
|
||||
|
||||
static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
|
||||
|
@ -100,6 +105,18 @@ static inline void ar933x_uart_stop_tx_interrupt(struct ar933x_uart_port *up)
|
|||
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
|
||||
}
|
||||
|
||||
static inline void ar933x_uart_start_rx_interrupt(struct ar933x_uart_port *up)
|
||||
{
|
||||
up->ier |= AR933X_UART_INT_RX_VALID;
|
||||
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
|
||||
}
|
||||
|
||||
static inline void ar933x_uart_stop_rx_interrupt(struct ar933x_uart_port *up)
|
||||
{
|
||||
up->ier &= ~AR933X_UART_INT_RX_VALID;
|
||||
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
|
||||
}
|
||||
|
||||
static inline void ar933x_uart_putc(struct ar933x_uart_port *up, int ch)
|
||||
{
|
||||
unsigned int rdata;
|
||||
|
@ -125,11 +142,21 @@ static unsigned int ar933x_uart_tx_empty(struct uart_port *port)
|
|||
|
||||
static unsigned int ar933x_uart_get_mctrl(struct uart_port *port)
|
||||
{
|
||||
return TIOCM_CAR;
|
||||
struct ar933x_uart_port *up =
|
||||
container_of(port, struct ar933x_uart_port, port);
|
||||
int ret = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
|
||||
|
||||
mctrl_gpio_get(up->gpios, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ar933x_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||
{
|
||||
struct ar933x_uart_port *up =
|
||||
container_of(port, struct ar933x_uart_port, port);
|
||||
|
||||
mctrl_gpio_set(up->gpios, mctrl);
|
||||
}
|
||||
|
||||
static void ar933x_uart_start_tx(struct uart_port *port)
|
||||
|
@ -140,6 +167,37 @@ static void ar933x_uart_start_tx(struct uart_port *port)
|
|||
ar933x_uart_start_tx_interrupt(up);
|
||||
}
|
||||
|
||||
static void ar933x_uart_wait_tx_complete(struct ar933x_uart_port *up)
|
||||
{
|
||||
unsigned int status;
|
||||
unsigned int timeout = 60000;
|
||||
|
||||
/* Wait up to 60ms for the character(s) to be sent. */
|
||||
do {
|
||||
status = ar933x_uart_read(up, AR933X_UART_CS_REG);
|
||||
if (--timeout == 0)
|
||||
break;
|
||||
udelay(1);
|
||||
} while (status & AR933X_UART_CS_TX_BUSY);
|
||||
|
||||
if (timeout == 0)
|
||||
dev_err(up->port.dev, "waiting for TX timed out\n");
|
||||
}
|
||||
|
||||
static void ar933x_uart_rx_flush(struct ar933x_uart_port *up)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
/* clear RX_VALID interrupt */
|
||||
ar933x_uart_write(up, AR933X_UART_INT_REG, AR933X_UART_INT_RX_VALID);
|
||||
|
||||
/* remove characters from the RX FIFO */
|
||||
do {
|
||||
ar933x_uart_write(up, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR);
|
||||
status = ar933x_uart_read(up, AR933X_UART_DATA_REG);
|
||||
} while (status & AR933X_UART_DATA_RX_CSR);
|
||||
}
|
||||
|
||||
static void ar933x_uart_stop_tx(struct uart_port *port)
|
||||
{
|
||||
struct ar933x_uart_port *up =
|
||||
|
@ -153,8 +211,7 @@ static void ar933x_uart_stop_rx(struct uart_port *port)
|
|||
struct ar933x_uart_port *up =
|
||||
container_of(port, struct ar933x_uart_port, port);
|
||||
|
||||
up->ier &= ~AR933X_UART_INT_RX_VALID;
|
||||
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
|
||||
ar933x_uart_stop_rx_interrupt(up);
|
||||
}
|
||||
|
||||
static void ar933x_uart_break_ctl(struct uart_port *port, int break_state)
|
||||
|
@ -336,11 +393,20 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
|
|||
static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
|
||||
{
|
||||
struct circ_buf *xmit = &up->port.state->xmit;
|
||||
struct serial_rs485 *rs485conf = &up->port.rs485;
|
||||
int count;
|
||||
bool half_duplex_send = false;
|
||||
|
||||
if (uart_tx_stopped(&up->port))
|
||||
return;
|
||||
|
||||
if ((rs485conf->flags & SER_RS485_ENABLED) &&
|
||||
(up->port.x_char || !uart_circ_empty(xmit))) {
|
||||
ar933x_uart_stop_rx_interrupt(up);
|
||||
gpiod_set_value(up->rts_gpiod, !!(rs485conf->flags & SER_RS485_RTS_ON_SEND));
|
||||
half_duplex_send = true;
|
||||
}
|
||||
|
||||
count = up->port.fifosize;
|
||||
do {
|
||||
unsigned int rdata;
|
||||
|
@ -368,8 +434,14 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
|
|||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||
uart_write_wakeup(&up->port);
|
||||
|
||||
if (!uart_circ_empty(xmit))
|
||||
if (!uart_circ_empty(xmit)) {
|
||||
ar933x_uart_start_tx_interrupt(up);
|
||||
} else if (half_duplex_send) {
|
||||
ar933x_uart_wait_tx_complete(up);
|
||||
ar933x_uart_rx_flush(up);
|
||||
ar933x_uart_start_rx_interrupt(up);
|
||||
gpiod_set_value(up->rts_gpiod, !!(rs485conf->flags & SER_RS485_RTS_AFTER_SEND));
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id)
|
||||
|
@ -427,8 +499,7 @@ static int ar933x_uart_startup(struct uart_port *port)
|
|||
AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE);
|
||||
|
||||
/* Enable RX interrupts */
|
||||
up->ier = AR933X_UART_INT_RX_VALID;
|
||||
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
|
||||
ar933x_uart_start_rx_interrupt(up);
|
||||
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
|
||||
|
@ -511,6 +582,21 @@ static const struct uart_ops ar933x_uart_ops = {
|
|||
.verify_port = ar933x_uart_verify_port,
|
||||
};
|
||||
|
||||
static int ar933x_config_rs485(struct uart_port *port,
|
||||
struct serial_rs485 *rs485conf)
|
||||
{
|
||||
struct ar933x_uart_port *up =
|
||||
container_of(port, struct ar933x_uart_port, port);
|
||||
|
||||
if ((rs485conf->flags & SER_RS485_ENABLED) &&
|
||||
!up->rts_gpiod) {
|
||||
dev_err(port->dev, "RS485 needs rts-gpio\n");
|
||||
return 1;
|
||||
}
|
||||
port->rs485 = *rs485conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_AR933X_CONSOLE
|
||||
static struct ar933x_uart_port *
|
||||
ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
|
||||
|
@ -680,6 +766,8 @@ static int ar933x_uart_probe(struct platform_device *pdev)
|
|||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
uart_get_rs485_mode(&pdev->dev, &port->rs485);
|
||||
|
||||
port->mapbase = mem_res->start;
|
||||
port->line = id;
|
||||
port->irq = irq_res->start;
|
||||
|
@ -690,6 +778,7 @@ static int ar933x_uart_probe(struct platform_device *pdev)
|
|||
port->regshift = 2;
|
||||
port->fifosize = AR933X_UART_FIFO_SIZE;
|
||||
port->ops = &ar933x_uart_ops;
|
||||
port->rs485_config = ar933x_config_rs485;
|
||||
|
||||
baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1);
|
||||
up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD);
|
||||
|
@ -697,6 +786,18 @@ static int ar933x_uart_probe(struct platform_device *pdev)
|
|||
baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP);
|
||||
up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD);
|
||||
|
||||
up->gpios = mctrl_gpio_init(port, 0);
|
||||
if (IS_ERR(up->gpios) && PTR_ERR(up->gpios) != -ENOSYS)
|
||||
return PTR_ERR(up->gpios);
|
||||
|
||||
up->rts_gpiod = mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS);
|
||||
|
||||
if ((port->rs485.flags & SER_RS485_ENABLED) &&
|
||||
!up->rts_gpiod) {
|
||||
dev_err(&pdev->dev, "lacking rts-gpio, disabling RS485\n");
|
||||
port->rs485.flags &= ~SER_RS485_ENABLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_AR933X_CONSOLE
|
||||
ar933x_console_ports[up->port.line] = up;
|
||||
#endif
|
||||
|
|
|
@ -20,15 +20,12 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/platform_data/atmel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/suspend.h>
|
||||
|
@ -2679,18 +2676,8 @@ static struct console atmel_console = {
|
|||
|
||||
#define ATMEL_CONSOLE_DEVICE (&atmel_console)
|
||||
|
||||
static inline bool atmel_is_console_port(struct uart_port *port)
|
||||
{
|
||||
return port->cons && port->cons->index == port->line;
|
||||
}
|
||||
|
||||
#else
|
||||
#define ATMEL_CONSOLE_DEVICE NULL
|
||||
|
||||
static inline bool atmel_is_console_port(struct uart_port *port)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct uart_driver atmel_uart = {
|
||||
|
@ -2719,14 +2706,14 @@ static int atmel_serial_suspend(struct platform_device *pdev,
|
|||
struct uart_port *port = platform_get_drvdata(pdev);
|
||||
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
|
||||
|
||||
if (atmel_is_console_port(port) && console_suspend_enabled) {
|
||||
if (uart_console(port) && console_suspend_enabled) {
|
||||
/* Drain the TX shifter */
|
||||
while (!(atmel_uart_readl(port, ATMEL_US_CSR) &
|
||||
ATMEL_US_TXEMPTY))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (atmel_is_console_port(port) && !console_suspend_enabled) {
|
||||
if (uart_console(port) && !console_suspend_enabled) {
|
||||
/* Cache register values as we won't get a full shutdown/startup
|
||||
* cycle
|
||||
*/
|
||||
|
@ -2762,7 +2749,7 @@ static int atmel_serial_resume(struct platform_device *pdev)
|
|||
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
|
||||
unsigned long flags;
|
||||
|
||||
if (atmel_is_console_port(port) && !console_suspend_enabled) {
|
||||
if (uart_console(port) && !console_suspend_enabled) {
|
||||
atmel_uart_writel(port, ATMEL_US_MR, atmel_port->cache.mr);
|
||||
atmel_uart_writel(port, ATMEL_US_IER, atmel_port->cache.imr);
|
||||
atmel_uart_writel(port, ATMEL_US_BRGR, atmel_port->cache.brgr);
|
||||
|
@ -2916,7 +2903,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
|
|||
goto err_add_port;
|
||||
|
||||
#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
|
||||
if (atmel_is_console_port(&atmel_port->uart)
|
||||
if (uart_console(&atmel_port->uart)
|
||||
&& ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) {
|
||||
/*
|
||||
* The serial core enabled the clock for us, so undo
|
||||
|
@ -2959,7 +2946,7 @@ err_add_port:
|
|||
kfree(atmel_port->rx_ring.buf);
|
||||
atmel_port->rx_ring.buf = NULL;
|
||||
err_alloc_ring:
|
||||
if (!atmel_is_console_port(&atmel_port->uart)) {
|
||||
if (!uart_console(&atmel_port->uart)) {
|
||||
clk_put(atmel_port->clk);
|
||||
atmel_port->clk = NULL;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* include/linux/atmel_serial.h
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Driver for CPM (SCC/SMC) serial ports
|
||||
*
|
||||
|
@ -13,6 +13,8 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/fs_uart_pd.h>
|
||||
|
||||
struct gpio_desc;
|
||||
|
||||
#if defined(CONFIG_CPM2)
|
||||
#include "cpm_uart_cpm2.h"
|
||||
#elif defined(CONFIG_CPM1)
|
||||
|
@ -80,7 +82,7 @@ struct uart_cpm_port {
|
|||
int wait_closing;
|
||||
/* value to combine with opcode to form cpm command */
|
||||
u32 command;
|
||||
int gpios[NUM_GPIOS];
|
||||
struct gpio_desc *gpios[NUM_GPIOS];
|
||||
};
|
||||
|
||||
extern int cpm_uart_nr;
|
||||
|
|
|
@ -30,8 +30,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
@ -88,11 +87,11 @@ static void cpm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|||
struct uart_cpm_port *pinfo =
|
||||
container_of(port, struct uart_cpm_port, port);
|
||||
|
||||
if (pinfo->gpios[GPIO_RTS] >= 0)
|
||||
gpio_set_value(pinfo->gpios[GPIO_RTS], !(mctrl & TIOCM_RTS));
|
||||
if (pinfo->gpios[GPIO_RTS])
|
||||
gpiod_set_value(pinfo->gpios[GPIO_RTS], !(mctrl & TIOCM_RTS));
|
||||
|
||||
if (pinfo->gpios[GPIO_DTR] >= 0)
|
||||
gpio_set_value(pinfo->gpios[GPIO_DTR], !(mctrl & TIOCM_DTR));
|
||||
if (pinfo->gpios[GPIO_DTR])
|
||||
gpiod_set_value(pinfo->gpios[GPIO_DTR], !(mctrl & TIOCM_DTR));
|
||||
}
|
||||
|
||||
static unsigned int cpm_uart_get_mctrl(struct uart_port *port)
|
||||
|
@ -101,23 +100,23 @@ static unsigned int cpm_uart_get_mctrl(struct uart_port *port)
|
|||
container_of(port, struct uart_cpm_port, port);
|
||||
unsigned int mctrl = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
|
||||
|
||||
if (pinfo->gpios[GPIO_CTS] >= 0) {
|
||||
if (gpio_get_value(pinfo->gpios[GPIO_CTS]))
|
||||
if (pinfo->gpios[GPIO_CTS]) {
|
||||
if (gpiod_get_value(pinfo->gpios[GPIO_CTS]))
|
||||
mctrl &= ~TIOCM_CTS;
|
||||
}
|
||||
|
||||
if (pinfo->gpios[GPIO_DSR] >= 0) {
|
||||
if (gpio_get_value(pinfo->gpios[GPIO_DSR]))
|
||||
if (pinfo->gpios[GPIO_DSR]) {
|
||||
if (gpiod_get_value(pinfo->gpios[GPIO_DSR]))
|
||||
mctrl &= ~TIOCM_DSR;
|
||||
}
|
||||
|
||||
if (pinfo->gpios[GPIO_DCD] >= 0) {
|
||||
if (gpio_get_value(pinfo->gpios[GPIO_DCD]))
|
||||
if (pinfo->gpios[GPIO_DCD]) {
|
||||
if (gpiod_get_value(pinfo->gpios[GPIO_DCD]))
|
||||
mctrl &= ~TIOCM_CAR;
|
||||
}
|
||||
|
||||
if (pinfo->gpios[GPIO_RI] >= 0) {
|
||||
if (!gpio_get_value(pinfo->gpios[GPIO_RI]))
|
||||
if (pinfo->gpios[GPIO_RI]) {
|
||||
if (!gpiod_get_value(pinfo->gpios[GPIO_RI]))
|
||||
mctrl |= TIOCM_RNG;
|
||||
}
|
||||
|
||||
|
@ -1139,6 +1138,7 @@ static int cpm_uart_init_port(struct device_node *np,
|
|||
{
|
||||
const u32 *data;
|
||||
void __iomem *mem, *pram;
|
||||
struct device *dev = pinfo->port.dev;
|
||||
int len;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -1211,29 +1211,23 @@ static int cpm_uart_init_port(struct device_node *np,
|
|||
}
|
||||
|
||||
for (i = 0; i < NUM_GPIOS; i++) {
|
||||
int gpio;
|
||||
struct gpio_desc *gpiod;
|
||||
|
||||
pinfo->gpios[i] = -1;
|
||||
pinfo->gpios[i] = NULL;
|
||||
|
||||
gpio = of_get_gpio(np, i);
|
||||
gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
|
||||
|
||||
if (gpio_is_valid(gpio)) {
|
||||
ret = gpio_request(gpio, "cpm_uart");
|
||||
if (ret) {
|
||||
pr_err("can't request gpio #%d: %d\n", i, ret);
|
||||
continue;
|
||||
}
|
||||
if (gpiod) {
|
||||
if (i == GPIO_RTS || i == GPIO_DTR)
|
||||
ret = gpio_direction_output(gpio, 0);
|
||||
ret = gpiod_direction_output(gpiod, 0);
|
||||
else
|
||||
ret = gpio_direction_input(gpio);
|
||||
ret = gpiod_direction_input(gpiod);
|
||||
if (ret) {
|
||||
pr_err("can't set direction for gpio #%d: %d\n",
|
||||
i, ret);
|
||||
gpio_free(gpio);
|
||||
continue;
|
||||
}
|
||||
pinfo->gpios[i] = gpio;
|
||||
pinfo->gpios[i] = gpiod;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -170,6 +170,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
|
|||
int __init setup_earlycon(char *buf)
|
||||
{
|
||||
const struct earlycon_id **p_match;
|
||||
bool empty_compatible = true;
|
||||
|
||||
if (!buf || !buf[0])
|
||||
return -EINVAL;
|
||||
|
@ -177,6 +178,7 @@ int __init setup_earlycon(char *buf)
|
|||
if (early_con.flags & CON_ENABLED)
|
||||
return -EALREADY;
|
||||
|
||||
again:
|
||||
for (p_match = __earlycon_table; p_match < __earlycon_table_end;
|
||||
p_match++) {
|
||||
const struct earlycon_id *match = *p_match;
|
||||
|
@ -185,6 +187,10 @@ int __init setup_earlycon(char *buf)
|
|||
if (strncmp(buf, match->name, len))
|
||||
continue;
|
||||
|
||||
/* prefer entries with empty compatible */
|
||||
if (empty_compatible && *match->compatible)
|
||||
continue;
|
||||
|
||||
if (buf[len]) {
|
||||
if (buf[len] != ',')
|
||||
continue;
|
||||
|
@ -195,6 +201,11 @@ int __init setup_earlycon(char *buf)
|
|||
return register_earlycon(buf, match);
|
||||
}
|
||||
|
||||
if (empty_compatible) {
|
||||
empty_compatible = false;
|
||||
goto again;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
|
|
@ -200,7 +200,7 @@ static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port)
|
|||
/*
|
||||
* This is a reserved bit and I only saw it read as 0. But to be
|
||||
* sure not to be confused too much by new devices adhere to the
|
||||
* warning in the reference manual that reserverd bits might
|
||||
* warning in the reference manual that reserved bits might
|
||||
* read as 1 in the future.
|
||||
*/
|
||||
rxdata &= ~SW_UARTn_RXDATAX_BERR;
|
||||
|
|
|
@ -234,6 +234,7 @@ static DEFINE_IDA(fsl_lpuart_ida);
|
|||
enum lpuart_type {
|
||||
VF610_LPUART,
|
||||
LS1021A_LPUART,
|
||||
LS1028A_LPUART,
|
||||
IMX7ULP_LPUART,
|
||||
IMX8QXP_LPUART,
|
||||
};
|
||||
|
@ -278,11 +279,16 @@ static const struct lpuart_soc_data vf_data = {
|
|||
.iotype = UPIO_MEM,
|
||||
};
|
||||
|
||||
static const struct lpuart_soc_data ls_data = {
|
||||
static const struct lpuart_soc_data ls1021a_data = {
|
||||
.devtype = LS1021A_LPUART,
|
||||
.iotype = UPIO_MEM32BE,
|
||||
};
|
||||
|
||||
static const struct lpuart_soc_data ls1028a_data = {
|
||||
.devtype = LS1028A_LPUART,
|
||||
.iotype = UPIO_MEM32,
|
||||
};
|
||||
|
||||
static struct lpuart_soc_data imx7ulp_data = {
|
||||
.devtype = IMX7ULP_LPUART,
|
||||
.iotype = UPIO_MEM32,
|
||||
|
@ -297,7 +303,8 @@ static struct lpuart_soc_data imx8qxp_data = {
|
|||
|
||||
static const struct of_device_id lpuart_dt_ids[] = {
|
||||
{ .compatible = "fsl,vf610-lpuart", .data = &vf_data, },
|
||||
{ .compatible = "fsl,ls1021a-lpuart", .data = &ls_data, },
|
||||
{ .compatible = "fsl,ls1021a-lpuart", .data = &ls1021a_data, },
|
||||
{ .compatible = "fsl,ls1028a-lpuart", .data = &ls1028a_data, },
|
||||
{ .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, },
|
||||
{ .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, },
|
||||
{ /* sentinel */ }
|
||||
|
@ -307,6 +314,11 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
|
|||
/* Forward declare this for the dma callbacks*/
|
||||
static void lpuart_dma_tx_complete(void *arg);
|
||||
|
||||
static inline bool is_ls1028a_lpuart(struct lpuart_port *sport)
|
||||
{
|
||||
return sport->devtype == LS1028A_LPUART;
|
||||
}
|
||||
|
||||
static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport)
|
||||
{
|
||||
return sport->devtype == IMX8QXP_LPUART;
|
||||
|
@ -409,6 +421,7 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
|
|||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
struct scatterlist *sgl = sport->tx_sgl;
|
||||
struct device *dev = sport->port.dev;
|
||||
struct dma_chan *chan = sport->dma_tx_chan;
|
||||
int ret;
|
||||
|
||||
if (sport->dma_tx_in_progress)
|
||||
|
@ -427,17 +440,19 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
|
|||
sg_set_buf(sgl + 1, xmit->buf, xmit->head);
|
||||
}
|
||||
|
||||
ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
|
||||
ret = dma_map_sg(chan->device->dev, sgl, sport->dma_tx_nents,
|
||||
DMA_TO_DEVICE);
|
||||
if (!ret) {
|
||||
dev_err(dev, "DMA mapping error for TX.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
|
||||
sport->dma_tx_desc = dmaengine_prep_slave_sg(chan, sgl,
|
||||
ret, DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!sport->dma_tx_desc) {
|
||||
dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
|
||||
dma_unmap_sg(chan->device->dev, sgl, sport->dma_tx_nents,
|
||||
DMA_TO_DEVICE);
|
||||
dev_err(dev, "Cannot prepare TX slave DMA!\n");
|
||||
return;
|
||||
}
|
||||
|
@ -446,7 +461,7 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
|
|||
sport->dma_tx_desc->callback_param = sport;
|
||||
sport->dma_tx_in_progress = true;
|
||||
sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
|
||||
dma_async_issue_pending(sport->dma_tx_chan);
|
||||
dma_async_issue_pending(chan);
|
||||
}
|
||||
|
||||
static bool lpuart_stopped_or_empty(struct uart_port *port)
|
||||
|
@ -459,11 +474,13 @@ static void lpuart_dma_tx_complete(void *arg)
|
|||
struct lpuart_port *sport = arg;
|
||||
struct scatterlist *sgl = &sport->tx_sgl[0];
|
||||
struct circ_buf *xmit = &sport->port.state->xmit;
|
||||
struct dma_chan *chan = sport->dma_tx_chan;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
|
||||
dma_unmap_sg(chan->device->dev, sgl, sport->dma_tx_nents,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
|
||||
|
||||
|
@ -529,15 +546,16 @@ static bool lpuart_is_32(struct lpuart_port *sport)
|
|||
static void lpuart_flush_buffer(struct uart_port *port)
|
||||
{
|
||||
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
|
||||
struct dma_chan *chan = sport->dma_tx_chan;
|
||||
u32 val;
|
||||
|
||||
if (sport->lpuart_dma_tx_use) {
|
||||
if (sport->dma_tx_in_progress) {
|
||||
dma_unmap_sg(sport->port.dev, &sport->tx_sgl[0],
|
||||
dma_unmap_sg(chan->device->dev, &sport->tx_sgl[0],
|
||||
sport->dma_tx_nents, DMA_TO_DEVICE);
|
||||
sport->dma_tx_in_progress = false;
|
||||
}
|
||||
dmaengine_terminate_all(sport->dma_tx_chan);
|
||||
dmaengine_terminate_all(chan);
|
||||
}
|
||||
|
||||
if (lpuart_is_32(sport)) {
|
||||
|
@ -993,6 +1011,7 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
|
|||
struct tty_port *port = &sport->port.state->port;
|
||||
struct dma_tx_state state;
|
||||
enum dma_status dmastat;
|
||||
struct dma_chan *chan = sport->dma_rx_chan;
|
||||
struct circ_buf *ring = &sport->rx_ring;
|
||||
unsigned long flags;
|
||||
int count = 0;
|
||||
|
@ -1053,10 +1072,7 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
|
|||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
dmastat = dmaengine_tx_status(sport->dma_rx_chan,
|
||||
sport->dma_rx_cookie,
|
||||
&state);
|
||||
|
||||
dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state);
|
||||
if (dmastat == DMA_ERROR) {
|
||||
dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
@ -1064,7 +1080,8 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
|
|||
}
|
||||
|
||||
/* CPU claims ownership of RX DMA buffer */
|
||||
dma_sync_sg_for_cpu(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
|
||||
dma_sync_sg_for_cpu(chan->device->dev, &sport->rx_sgl, 1,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
/*
|
||||
* ring->head points to the end of data already written by the DMA.
|
||||
|
@ -1106,7 +1123,7 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
|
|||
sport->port.icount.rx += count;
|
||||
}
|
||||
|
||||
dma_sync_sg_for_device(sport->port.dev, &sport->rx_sgl, 1,
|
||||
dma_sync_sg_for_device(chan->device->dev, &sport->rx_sgl, 1,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
|
@ -1138,6 +1155,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
|
|||
struct tty_port *port = &sport->port.state->port;
|
||||
struct tty_struct *tty = port->tty;
|
||||
struct ktermios *termios = &tty->termios;
|
||||
struct dma_chan *chan = sport->dma_rx_chan;
|
||||
|
||||
baud = tty_get_baud_rate(tty);
|
||||
|
||||
|
@ -1159,7 +1177,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
|
|||
return -ENOMEM;
|
||||
|
||||
sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
|
||||
nent = dma_map_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
|
||||
nent = dma_map_sg(chan->device->dev, &sport->rx_sgl, 1,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
if (!nent) {
|
||||
dev_err(sport->port.dev, "DMA Rx mapping error\n");
|
||||
|
@ -1170,7 +1189,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
|
|||
dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
dma_rx_sconfig.src_maxburst = 1;
|
||||
dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
|
||||
ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig);
|
||||
ret = dmaengine_slave_config(chan, &dma_rx_sconfig);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(sport->port.dev,
|
||||
|
@ -1178,7 +1197,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
|
|||
return ret;
|
||||
}
|
||||
|
||||
sport->dma_rx_desc = dmaengine_prep_dma_cyclic(sport->dma_rx_chan,
|
||||
sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan,
|
||||
sg_dma_address(&sport->rx_sgl),
|
||||
sport->rx_sgl.length,
|
||||
sport->rx_sgl.length / 2,
|
||||
|
@ -1192,7 +1211,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
|
|||
sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
|
||||
sport->dma_rx_desc->callback_param = sport;
|
||||
sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
|
||||
dma_async_issue_pending(sport->dma_rx_chan);
|
||||
dma_async_issue_pending(chan);
|
||||
|
||||
if (lpuart_is_32(sport)) {
|
||||
unsigned long temp = lpuart32_read(&sport->port, UARTBAUD);
|
||||
|
@ -1210,11 +1229,12 @@ static void lpuart_dma_rx_free(struct uart_port *port)
|
|||
{
|
||||
struct lpuart_port *sport = container_of(port,
|
||||
struct lpuart_port, port);
|
||||
struct dma_chan *chan = sport->dma_rx_chan;
|
||||
|
||||
if (sport->dma_rx_chan)
|
||||
dmaengine_terminate_all(sport->dma_rx_chan);
|
||||
if (chan)
|
||||
dmaengine_terminate_all(chan);
|
||||
|
||||
dma_unmap_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
|
||||
dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
|
||||
kfree(sport->rx_ring.buf);
|
||||
sport->rx_ring.tail = 0;
|
||||
sport->rx_ring.head = 0;
|
||||
|
@ -1490,11 +1510,37 @@ static void rx_dma_timer_init(struct lpuart_port *sport)
|
|||
add_timer(&sport->lpuart_timer);
|
||||
}
|
||||
|
||||
static void lpuart_request_dma(struct lpuart_port *sport)
|
||||
{
|
||||
sport->dma_tx_chan = dma_request_chan(sport->port.dev, "tx");
|
||||
if (IS_ERR(sport->dma_tx_chan)) {
|
||||
dev_info_once(sport->port.dev,
|
||||
"DMA tx channel request failed, operating without tx DMA (%ld)\n",
|
||||
PTR_ERR(sport->dma_tx_chan));
|
||||
sport->dma_tx_chan = NULL;
|
||||
}
|
||||
|
||||
sport->dma_rx_chan = dma_request_chan(sport->port.dev, "rx");
|
||||
if (IS_ERR(sport->dma_rx_chan)) {
|
||||
dev_info_once(sport->port.dev,
|
||||
"DMA rx channel request failed, operating without rx DMA (%ld)\n",
|
||||
PTR_ERR(sport->dma_rx_chan));
|
||||
sport->dma_rx_chan = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void lpuart_tx_dma_startup(struct lpuart_port *sport)
|
||||
{
|
||||
u32 uartbaud;
|
||||
int ret;
|
||||
|
||||
if (!sport->dma_tx_chan)
|
||||
goto err;
|
||||
|
||||
ret = lpuart_dma_tx_request(&sport->port);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
|
||||
init_waitqueue_head(&sport->dma_wait);
|
||||
sport->lpuart_dma_tx_use = true;
|
||||
if (lpuart_is_32(sport)) {
|
||||
|
@ -1505,14 +1551,24 @@ static void lpuart_tx_dma_startup(struct lpuart_port *sport)
|
|||
writeb(readb(sport->port.membase + UARTCR5) |
|
||||
UARTCR5_TDMAS, sport->port.membase + UARTCR5);
|
||||
}
|
||||
} else {
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
sport->lpuart_dma_tx_use = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void lpuart_rx_dma_startup(struct lpuart_port *sport)
|
||||
{
|
||||
if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
|
||||
int ret;
|
||||
|
||||
if (!sport->dma_rx_chan)
|
||||
goto err;
|
||||
|
||||
ret = lpuart_start_rx_dma(sport);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* set Rx DMA timeout */
|
||||
sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
|
||||
if (!sport->dma_rx_timeout)
|
||||
|
@ -1520,9 +1576,11 @@ static void lpuart_rx_dma_startup(struct lpuart_port *sport)
|
|||
|
||||
sport->lpuart_dma_rx_use = true;
|
||||
rx_dma_timer_init(sport);
|
||||
} else {
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
sport->lpuart_dma_rx_use = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int lpuart_startup(struct uart_port *port)
|
||||
|
@ -1541,6 +1599,8 @@ static int lpuart_startup(struct uart_port *port)
|
|||
sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_RXSIZE_OFF) &
|
||||
UARTPFIFO_FIFOSIZE_MASK);
|
||||
|
||||
lpuart_request_dma(sport);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
lpuart_setup_watermark_enable(sport);
|
||||
|
@ -1587,11 +1647,23 @@ static int lpuart32_startup(struct uart_port *port)
|
|||
sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_RXSIZE_OFF) &
|
||||
UARTFIFO_FIFOSIZE_MASK);
|
||||
|
||||
/*
|
||||
* The LS1028A has a fixed length of 16 words. Although it supports the
|
||||
* RX/TXSIZE fields their encoding is different. Eg the reference manual
|
||||
* states 0b101 is 16 words.
|
||||
*/
|
||||
if (is_ls1028a_lpuart(sport)) {
|
||||
sport->rxfifo_size = 16;
|
||||
sport->txfifo_size = 16;
|
||||
sport->port.fifosize = sport->txfifo_size;
|
||||
}
|
||||
|
||||
lpuart_request_dma(sport);
|
||||
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
lpuart32_setup_watermark_enable(sport);
|
||||
|
||||
|
||||
lpuart_rx_dma_startup(sport);
|
||||
lpuart_tx_dma_startup(sport);
|
||||
|
||||
|
@ -1615,6 +1687,11 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport)
|
|||
dmaengine_terminate_all(sport->dma_tx_chan);
|
||||
}
|
||||
}
|
||||
|
||||
if (sport->dma_tx_chan)
|
||||
dma_release_channel(sport->dma_tx_chan);
|
||||
if (sport->dma_rx_chan)
|
||||
dma_release_channel(sport->dma_rx_chan);
|
||||
}
|
||||
|
||||
static void lpuart_shutdown(struct uart_port *port)
|
||||
|
@ -1811,11 +1888,12 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
|
|||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate)
|
||||
static void __lpuart32_serial_setbrg(struct uart_port *port,
|
||||
unsigned int baudrate, bool use_rx_dma,
|
||||
bool use_tx_dma)
|
||||
{
|
||||
u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
|
||||
u32 clk = sport->port.uartclk;
|
||||
u32 clk = port->uartclk;
|
||||
|
||||
/*
|
||||
* The idea is to use the best OSR (over-sampling rate) possible.
|
||||
|
@ -1861,10 +1939,10 @@ lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate)
|
|||
|
||||
/* handle buadrate outside acceptable rate */
|
||||
if (baud_diff > ((baudrate / 100) * 3))
|
||||
dev_warn(sport->port.dev,
|
||||
dev_warn(port->dev,
|
||||
"unacceptable baud rate difference of more than 3%%\n");
|
||||
|
||||
tmp = lpuart32_read(&sport->port, UARTBAUD);
|
||||
tmp = lpuart32_read(port, UARTBAUD);
|
||||
|
||||
if ((osr > 3) && (osr < 8))
|
||||
tmp |= UARTBAUD_BOTHEDGE;
|
||||
|
@ -1875,14 +1953,23 @@ lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate)
|
|||
tmp &= ~UARTBAUD_SBR_MASK;
|
||||
tmp |= sbr & UARTBAUD_SBR_MASK;
|
||||
|
||||
if (!sport->lpuart_dma_rx_use)
|
||||
if (!use_rx_dma)
|
||||
tmp &= ~UARTBAUD_RDMAE;
|
||||
if (!sport->lpuart_dma_tx_use)
|
||||
if (!use_tx_dma)
|
||||
tmp &= ~UARTBAUD_TDMAE;
|
||||
|
||||
lpuart32_write(&sport->port, tmp, UARTBAUD);
|
||||
lpuart32_write(port, tmp, UARTBAUD);
|
||||
}
|
||||
|
||||
static void lpuart32_serial_setbrg(struct lpuart_port *sport,
|
||||
unsigned int baudrate)
|
||||
{
|
||||
__lpuart32_serial_setbrg(&sport->port, baudrate,
|
||||
sport->lpuart_dma_rx_use,
|
||||
sport->lpuart_dma_tx_use);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
|
@ -2376,6 +2463,30 @@ static int __init lpuart32_early_console_setup(struct earlycon_device *device,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init ls1028a_early_console_setup(struct earlycon_device *device,
|
||||
const char *opt)
|
||||
{
|
||||
u32 cr;
|
||||
|
||||
if (!device->port.membase)
|
||||
return -ENODEV;
|
||||
|
||||
device->port.iotype = UPIO_MEM32;
|
||||
device->con->write = lpuart32_early_write;
|
||||
|
||||
/* set the baudrate */
|
||||
if (device->port.uartclk && device->baud)
|
||||
__lpuart32_serial_setbrg(&device->port, device->baud,
|
||||
false, false);
|
||||
|
||||
/* enable transmitter */
|
||||
cr = lpuart32_read(&device->port, UARTCTRL);
|
||||
cr |= UARTCTRL_TE;
|
||||
lpuart32_write(&device->port, cr, UARTCTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init lpuart32_imx_early_console_setup(struct earlycon_device *device,
|
||||
const char *opt)
|
||||
{
|
||||
|
@ -2390,6 +2501,7 @@ static int __init lpuart32_imx_early_console_setup(struct earlycon_device *devic
|
|||
}
|
||||
OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1028a-lpuart", ls1028a_early_console_setup);
|
||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup);
|
||||
EARLYCON_DECLARE(lpuart, lpuart_early_console_setup);
|
||||
EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup);
|
||||
|
@ -2520,16 +2632,6 @@ static int lpuart_probe(struct platform_device *pdev)
|
|||
|
||||
sport->port.rs485_config(&sport->port, &sport->port.rs485);
|
||||
|
||||
sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx");
|
||||
if (!sport->dma_tx_chan)
|
||||
dev_info(sport->port.dev, "DMA tx channel request failed, "
|
||||
"operating without tx DMA\n");
|
||||
|
||||
sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev, "rx");
|
||||
if (!sport->dma_rx_chan)
|
||||
dev_info(sport->port.dev, "DMA rx channel request failed, "
|
||||
"operating without rx DMA\n");
|
||||
|
||||
return 0;
|
||||
|
||||
failed_attach_port:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* icom.h
|
||||
*
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
|
@ -61,7 +61,6 @@
|
|||
#define IFX_SPI_HEADER_F (-2)
|
||||
|
||||
#define PO_POST_DELAY 200
|
||||
#define IFX_MDM_RST_PMU 4
|
||||
|
||||
/* forward reference */
|
||||
static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev);
|
||||
|
@ -81,7 +80,7 @@ static struct notifier_block ifx_modem_reboot_notifier_block = {
|
|||
|
||||
static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev)
|
||||
{
|
||||
gpio_set_value(IFX_MDM_RST_PMU, 1);
|
||||
gpiod_set_value(ifx_dev->gpio.pmu_reset, 1);
|
||||
msleep(PO_POST_DELAY);
|
||||
|
||||
return 0;
|
||||
|
@ -107,7 +106,7 @@ static int ifx_modem_reboot_callback(struct notifier_block *nfb,
|
|||
*/
|
||||
static inline void mrdy_set_high(struct ifx_spi_device *ifx)
|
||||
{
|
||||
gpio_set_value(ifx->gpio.mrdy, 1);
|
||||
gpiod_set_value(ifx->gpio.mrdy, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,7 +116,7 @@ static inline void mrdy_set_high(struct ifx_spi_device *ifx)
|
|||
*/
|
||||
static inline void mrdy_set_low(struct ifx_spi_device *ifx)
|
||||
{
|
||||
gpio_set_value(ifx->gpio.mrdy, 0);
|
||||
gpiod_set_value(ifx->gpio.mrdy, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,7 +243,7 @@ static inline void swap_buf_32(unsigned char *buf, int len, void *end)
|
|||
*/
|
||||
static void mrdy_assert(struct ifx_spi_device *ifx_dev)
|
||||
{
|
||||
int val = gpio_get_value(ifx_dev->gpio.srdy);
|
||||
int val = gpiod_get_value(ifx_dev->gpio.srdy);
|
||||
if (!val) {
|
||||
if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING,
|
||||
&ifx_dev->flags)) {
|
||||
|
@ -691,7 +690,7 @@ complete_exit:
|
|||
clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags));
|
||||
|
||||
queue_length = kfifo_len(&ifx_dev->tx_fifo);
|
||||
srdy = gpio_get_value(ifx_dev->gpio.srdy);
|
||||
srdy = gpiod_get_value(ifx_dev->gpio.srdy);
|
||||
if (!srdy)
|
||||
ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_SRDY);
|
||||
|
||||
|
@ -898,7 +897,7 @@ static irqreturn_t ifx_spi_srdy_interrupt(int irq, void *dev)
|
|||
static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev)
|
||||
{
|
||||
struct ifx_spi_device *ifx_dev = dev;
|
||||
int val = gpio_get_value(ifx_dev->gpio.reset_out);
|
||||
int val = gpiod_get_value(ifx_dev->gpio.reset_out);
|
||||
int solreset = test_bit(MR_START, &ifx_dev->mdm_reset_state);
|
||||
|
||||
if (val == 0) {
|
||||
|
@ -954,14 +953,14 @@ static int ifx_spi_reset(struct ifx_spi_device *ifx_dev)
|
|||
* to reset properly
|
||||
*/
|
||||
set_bit(MR_START, &ifx_dev->mdm_reset_state);
|
||||
gpio_set_value(ifx_dev->gpio.po, 0);
|
||||
gpio_set_value(ifx_dev->gpio.reset, 0);
|
||||
gpiod_set_value(ifx_dev->gpio.po, 0);
|
||||
gpiod_set_value(ifx_dev->gpio.reset, 0);
|
||||
msleep(25);
|
||||
gpio_set_value(ifx_dev->gpio.reset, 1);
|
||||
gpiod_set_value(ifx_dev->gpio.reset, 1);
|
||||
msleep(1);
|
||||
gpio_set_value(ifx_dev->gpio.po, 1);
|
||||
gpiod_set_value(ifx_dev->gpio.po, 1);
|
||||
msleep(1);
|
||||
gpio_set_value(ifx_dev->gpio.po, 0);
|
||||
gpiod_set_value(ifx_dev->gpio.po, 0);
|
||||
ret = wait_event_timeout(ifx_dev->mdm_reset_wait,
|
||||
test_bit(MR_COMPLETE,
|
||||
&ifx_dev->mdm_reset_state),
|
||||
|
@ -992,22 +991,23 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
|
|||
int srdy;
|
||||
struct ifx_modem_platform_data *pl_data;
|
||||
struct ifx_spi_device *ifx_dev;
|
||||
struct device *dev = &spi->dev;
|
||||
|
||||
if (saved_ifx_dev) {
|
||||
dev_dbg(&spi->dev, "ignoring subsequent detection");
|
||||
dev_dbg(dev, "ignoring subsequent detection");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pl_data = dev_get_platdata(&spi->dev);
|
||||
pl_data = dev_get_platdata(dev);
|
||||
if (!pl_data) {
|
||||
dev_err(&spi->dev, "missing platform data!");
|
||||
dev_err(dev, "missing platform data!");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* initialize structure to hold our device variables */
|
||||
ifx_dev = kzalloc(sizeof(struct ifx_spi_device), GFP_KERNEL);
|
||||
if (!ifx_dev) {
|
||||
dev_err(&spi->dev, "spi device allocation failed");
|
||||
dev_err(dev, "spi device allocation failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
saved_ifx_dev = ifx_dev;
|
||||
|
@ -1026,7 +1026,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
|
|||
spi->bits_per_word = spi_bpw;
|
||||
ret = spi_setup(spi);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "SPI setup wasn't successful %d", ret);
|
||||
dev_err(dev, "SPI setup wasn't successful %d", ret);
|
||||
kfree(ifx_dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -1049,7 +1049,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
|
|||
&ifx_dev->tx_bus,
|
||||
GFP_KERNEL);
|
||||
if (!ifx_dev->tx_buffer) {
|
||||
dev_err(&spi->dev, "DMA-TX buffer allocation failed");
|
||||
dev_err(dev, "DMA-TX buffer allocation failed");
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
@ -1058,7 +1058,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
|
|||
&ifx_dev->rx_bus,
|
||||
GFP_KERNEL);
|
||||
if (!ifx_dev->rx_buffer) {
|
||||
dev_err(&spi->dev, "DMA-RX buffer allocation failed");
|
||||
dev_err(dev, "DMA-RX buffer allocation failed");
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
@ -1075,122 +1075,83 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
|
|||
/* create our tty port */
|
||||
ret = ifx_spi_create_port(ifx_dev);
|
||||
if (ret != 0) {
|
||||
dev_err(&spi->dev, "create default tty port failed");
|
||||
dev_err(dev, "create default tty port failed");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ifx_dev->gpio.reset = pl_data->rst_pmu;
|
||||
ifx_dev->gpio.po = pl_data->pwr_on;
|
||||
ifx_dev->gpio.mrdy = pl_data->mrdy;
|
||||
ifx_dev->gpio.srdy = pl_data->srdy;
|
||||
ifx_dev->gpio.reset_out = pl_data->rst_out;
|
||||
|
||||
dev_info(&spi->dev, "gpios %d, %d, %d, %d, %d",
|
||||
ifx_dev->gpio.reset, ifx_dev->gpio.po, ifx_dev->gpio.mrdy,
|
||||
ifx_dev->gpio.srdy, ifx_dev->gpio.reset_out);
|
||||
|
||||
/* Configure gpios */
|
||||
ret = gpio_request(ifx_dev->gpio.reset, "ifxModem");
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET)",
|
||||
ifx_dev->gpio.reset);
|
||||
ifx_dev->gpio.reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ifx_dev->gpio.reset)) {
|
||||
dev_err(dev, "could not obtain reset GPIO\n");
|
||||
ret = PTR_ERR(ifx_dev->gpio.reset);
|
||||
goto error_ret;
|
||||
}
|
||||
ret += gpio_direction_output(ifx_dev->gpio.reset, 0);
|
||||
ret += gpio_export(ifx_dev->gpio.reset, 1);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Unable to configure GPIO%d (RESET)",
|
||||
ifx_dev->gpio.reset);
|
||||
ret = -EBUSY;
|
||||
goto error_ret2;
|
||||
gpiod_set_consumer_name(ifx_dev->gpio.reset, "ifxModem reset");
|
||||
ifx_dev->gpio.po = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ifx_dev->gpio.po)) {
|
||||
dev_err(dev, "could not obtain power GPIO\n");
|
||||
ret = PTR_ERR(ifx_dev->gpio.po);
|
||||
goto error_ret;
|
||||
}
|
||||
gpiod_set_consumer_name(ifx_dev->gpio.po, "ifxModem power");
|
||||
ifx_dev->gpio.mrdy = devm_gpiod_get(dev, "mrdy", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ifx_dev->gpio.mrdy)) {
|
||||
dev_err(dev, "could not obtain mrdy GPIO\n");
|
||||
ret = PTR_ERR(ifx_dev->gpio.mrdy);
|
||||
goto error_ret;
|
||||
}
|
||||
gpiod_set_consumer_name(ifx_dev->gpio.mrdy, "ifxModem mrdy");
|
||||
ifx_dev->gpio.srdy = devm_gpiod_get(dev, "srdy", GPIOD_IN);
|
||||
if (IS_ERR(ifx_dev->gpio.srdy)) {
|
||||
dev_err(dev, "could not obtain srdy GPIO\n");
|
||||
ret = PTR_ERR(ifx_dev->gpio.srdy);
|
||||
goto error_ret;
|
||||
}
|
||||
gpiod_set_consumer_name(ifx_dev->gpio.srdy, "ifxModem srdy");
|
||||
ifx_dev->gpio.reset_out = devm_gpiod_get(dev, "rst_out", GPIOD_IN);
|
||||
if (IS_ERR(ifx_dev->gpio.reset_out)) {
|
||||
dev_err(dev, "could not obtain rst_out GPIO\n");
|
||||
ret = PTR_ERR(ifx_dev->gpio.reset_out);
|
||||
goto error_ret;
|
||||
}
|
||||
gpiod_set_consumer_name(ifx_dev->gpio.reset_out, "ifxModem reset out");
|
||||
ifx_dev->gpio.pmu_reset = devm_gpiod_get(dev, "pmu_reset", GPIOD_ASIS);
|
||||
if (IS_ERR(ifx_dev->gpio.pmu_reset)) {
|
||||
dev_err(dev, "could not obtain pmu_reset GPIO\n");
|
||||
ret = PTR_ERR(ifx_dev->gpio.pmu_reset);
|
||||
goto error_ret;
|
||||
}
|
||||
gpiod_set_consumer_name(ifx_dev->gpio.pmu_reset, "ifxModem PMU reset");
|
||||
|
||||
ret = gpio_request(ifx_dev->gpio.po, "ifxModem");
|
||||
ret += gpio_direction_output(ifx_dev->gpio.po, 0);
|
||||
ret += gpio_export(ifx_dev->gpio.po, 1);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Unable to configure GPIO%d (ON)",
|
||||
ifx_dev->gpio.po);
|
||||
ret = -EBUSY;
|
||||
goto error_ret3;
|
||||
}
|
||||
|
||||
ret = gpio_request(ifx_dev->gpio.mrdy, "ifxModem");
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Unable to allocate GPIO%d (MRDY)",
|
||||
ifx_dev->gpio.mrdy);
|
||||
goto error_ret3;
|
||||
}
|
||||
ret += gpio_export(ifx_dev->gpio.mrdy, 1);
|
||||
ret += gpio_direction_output(ifx_dev->gpio.mrdy, 0);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Unable to configure GPIO%d (MRDY)",
|
||||
ifx_dev->gpio.mrdy);
|
||||
ret = -EBUSY;
|
||||
goto error_ret4;
|
||||
}
|
||||
|
||||
ret = gpio_request(ifx_dev->gpio.srdy, "ifxModem");
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Unable to allocate GPIO%d (SRDY)",
|
||||
ifx_dev->gpio.srdy);
|
||||
ret = -EBUSY;
|
||||
goto error_ret4;
|
||||
}
|
||||
ret += gpio_export(ifx_dev->gpio.srdy, 1);
|
||||
ret += gpio_direction_input(ifx_dev->gpio.srdy);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Unable to configure GPIO%d (SRDY)",
|
||||
ifx_dev->gpio.srdy);
|
||||
ret = -EBUSY;
|
||||
goto error_ret5;
|
||||
}
|
||||
|
||||
ret = gpio_request(ifx_dev->gpio.reset_out, "ifxModem");
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET_OUT)",
|
||||
ifx_dev->gpio.reset_out);
|
||||
goto error_ret5;
|
||||
}
|
||||
ret += gpio_export(ifx_dev->gpio.reset_out, 1);
|
||||
ret += gpio_direction_input(ifx_dev->gpio.reset_out);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Unable to configure GPIO%d (RESET_OUT)",
|
||||
ifx_dev->gpio.reset_out);
|
||||
ret = -EBUSY;
|
||||
goto error_ret6;
|
||||
}
|
||||
|
||||
ret = request_irq(gpio_to_irq(ifx_dev->gpio.reset_out),
|
||||
ret = request_irq(gpiod_to_irq(ifx_dev->gpio.reset_out),
|
||||
ifx_spi_reset_interrupt,
|
||||
IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DRVNAME,
|
||||
ifx_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Unable to get irq %x\n",
|
||||
gpio_to_irq(ifx_dev->gpio.reset_out));
|
||||
goto error_ret6;
|
||||
dev_err(dev, "Unable to get irq %x\n",
|
||||
gpiod_to_irq(ifx_dev->gpio.reset_out));
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = ifx_spi_reset(ifx_dev);
|
||||
|
||||
ret = request_irq(gpio_to_irq(ifx_dev->gpio.srdy),
|
||||
ret = request_irq(gpiod_to_irq(ifx_dev->gpio.srdy),
|
||||
ifx_spi_srdy_interrupt, IRQF_TRIGGER_RISING, DRVNAME,
|
||||
ifx_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Unable to get irq %x",
|
||||
gpio_to_irq(ifx_dev->gpio.srdy));
|
||||
goto error_ret7;
|
||||
dev_err(dev, "Unable to get irq %x",
|
||||
gpiod_to_irq(ifx_dev->gpio.srdy));
|
||||
goto error_ret2;
|
||||
}
|
||||
|
||||
/* set pm runtime power state and register with power system */
|
||||
pm_runtime_set_active(&spi->dev);
|
||||
pm_runtime_enable(&spi->dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/* handle case that modem is already signaling SRDY */
|
||||
/* no outgoing tty open at this point, this just satisfies the
|
||||
* modem's read and should reset communication properly
|
||||
*/
|
||||
srdy = gpio_get_value(ifx_dev->gpio.srdy);
|
||||
srdy = gpiod_get_value(ifx_dev->gpio.srdy);
|
||||
|
||||
if (srdy) {
|
||||
mrdy_assert(ifx_dev);
|
||||
|
@ -1199,18 +1160,8 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
|
|||
mrdy_set_low(ifx_dev);
|
||||
return 0;
|
||||
|
||||
error_ret7:
|
||||
free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), ifx_dev);
|
||||
error_ret6:
|
||||
gpio_free(ifx_dev->gpio.srdy);
|
||||
error_ret5:
|
||||
gpio_free(ifx_dev->gpio.mrdy);
|
||||
error_ret4:
|
||||
gpio_free(ifx_dev->gpio.reset);
|
||||
error_ret3:
|
||||
gpio_free(ifx_dev->gpio.po);
|
||||
error_ret2:
|
||||
gpio_free(ifx_dev->gpio.reset_out);
|
||||
free_irq(gpiod_to_irq(ifx_dev->gpio.reset_out), ifx_dev);
|
||||
error_ret:
|
||||
ifx_spi_free_device(ifx_dev);
|
||||
saved_ifx_dev = NULL;
|
||||
|
@ -1234,14 +1185,8 @@ static int ifx_spi_spi_remove(struct spi_device *spi)
|
|||
pm_runtime_disable(&spi->dev);
|
||||
|
||||
/* free irq */
|
||||
free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), ifx_dev);
|
||||
free_irq(gpio_to_irq(ifx_dev->gpio.srdy), ifx_dev);
|
||||
|
||||
gpio_free(ifx_dev->gpio.srdy);
|
||||
gpio_free(ifx_dev->gpio.mrdy);
|
||||
gpio_free(ifx_dev->gpio.reset);
|
||||
gpio_free(ifx_dev->gpio.po);
|
||||
gpio_free(ifx_dev->gpio.reset_out);
|
||||
free_irq(gpiod_to_irq(ifx_dev->gpio.reset_out), ifx_dev);
|
||||
free_irq(gpiod_to_irq(ifx_dev->gpio.srdy), ifx_dev);
|
||||
|
||||
/* free allocations */
|
||||
ifx_spi_free_device(ifx_dev);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/****************************************************************************
|
||||
*
|
||||
* Driver for the IFX spi modem.
|
||||
|
@ -10,6 +10,8 @@
|
|||
#ifndef _IFX6X60_H
|
||||
#define _IFX6X60_H
|
||||
|
||||
struct gpio_desc;
|
||||
|
||||
#define DRVNAME "ifx6x60"
|
||||
#define TTYNAME "ttyIFX"
|
||||
|
||||
|
@ -94,11 +96,12 @@ struct ifx_spi_device {
|
|||
|
||||
struct {
|
||||
/* gpio lines */
|
||||
unsigned short srdy; /* slave-ready gpio */
|
||||
unsigned short mrdy; /* master-ready gpio */
|
||||
unsigned short reset; /* modem-reset gpio */
|
||||
unsigned short po; /* modem-on gpio */
|
||||
unsigned short reset_out; /* modem-in-reset gpio */
|
||||
struct gpio_desc *srdy; /* slave-ready gpio */
|
||||
struct gpio_desc *mrdy; /* master-ready gpio */
|
||||
struct gpio_desc *reset; /* modem-reset gpio */
|
||||
struct gpio_desc *po; /* modem-on gpio */
|
||||
struct gpio_desc *reset_out; /* modem-in-reset gpio */
|
||||
struct gpio_desc *pmu_reset; /* PMU reset gpio */
|
||||
/* state/stats */
|
||||
int unack_srdy_int_nb;
|
||||
} gpio;
|
||||
|
|
|
@ -195,6 +195,8 @@ struct imx_port {
|
|||
unsigned int have_rtscts:1;
|
||||
unsigned int have_rtsgpio:1;
|
||||
unsigned int dte_mode:1;
|
||||
unsigned int inverted_tx:1;
|
||||
unsigned int inverted_rx:1;
|
||||
struct clk *clk_ipg;
|
||||
struct clk *clk_per;
|
||||
const struct imx_uart_data *devdata;
|
||||
|
@ -1335,7 +1337,7 @@ static int imx_uart_startup(struct uart_port *port)
|
|||
int retval, i;
|
||||
unsigned long flags;
|
||||
int dma_is_inited = 0;
|
||||
u32 ucr1, ucr2, ucr4;
|
||||
u32 ucr1, ucr2, ucr3, ucr4;
|
||||
|
||||
retval = clk_prepare_enable(sport->clk_per);
|
||||
if (retval)
|
||||
|
@ -1387,11 +1389,29 @@ static int imx_uart_startup(struct uart_port *port)
|
|||
|
||||
imx_uart_writel(sport, ucr1, UCR1);
|
||||
|
||||
ucr4 = imx_uart_readl(sport, UCR4) & ~UCR4_OREN;
|
||||
ucr4 = imx_uart_readl(sport, UCR4) & ~(UCR4_OREN | UCR4_INVR);
|
||||
if (!sport->dma_is_enabled)
|
||||
ucr4 |= UCR4_OREN;
|
||||
if (sport->inverted_rx)
|
||||
ucr4 |= UCR4_INVR;
|
||||
imx_uart_writel(sport, ucr4, UCR4);
|
||||
|
||||
ucr3 = imx_uart_readl(sport, UCR3) & ~UCR3_INVT;
|
||||
/*
|
||||
* configure tx polarity before enabling tx
|
||||
*/
|
||||
if (sport->inverted_tx)
|
||||
ucr3 |= UCR3_INVT;
|
||||
|
||||
if (!imx_uart_is_imx1(sport)) {
|
||||
ucr3 |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
|
||||
|
||||
if (sport->dte_mode)
|
||||
/* disable broken interrupts */
|
||||
ucr3 &= ~(UCR3_RI | UCR3_DCD);
|
||||
}
|
||||
imx_uart_writel(sport, ucr3, UCR3);
|
||||
|
||||
ucr2 = imx_uart_readl(sport, UCR2) & ~UCR2_ATEN;
|
||||
ucr2 |= (UCR2_RXEN | UCR2_TXEN);
|
||||
if (!sport->have_rtscts)
|
||||
|
@ -1404,20 +1424,6 @@ static int imx_uart_startup(struct uart_port *port)
|
|||
ucr2 &= ~UCR2_RTSEN;
|
||||
imx_uart_writel(sport, ucr2, UCR2);
|
||||
|
||||
if (!imx_uart_is_imx1(sport)) {
|
||||
u32 ucr3;
|
||||
|
||||
ucr3 = imx_uart_readl(sport, UCR3);
|
||||
|
||||
ucr3 |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
|
||||
|
||||
if (sport->dte_mode)
|
||||
/* disable broken interrupts */
|
||||
ucr3 &= ~(UCR3_RI | UCR3_DCD);
|
||||
|
||||
imx_uart_writel(sport, ucr3, UCR3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable modem status interrupts
|
||||
*/
|
||||
|
@ -2184,6 +2190,12 @@ static int imx_uart_probe_dt(struct imx_port *sport,
|
|||
if (of_get_property(np, "rts-gpios", NULL))
|
||||
sport->have_rtsgpio = 1;
|
||||
|
||||
if (of_get_property(np, "fsl,inverted-tx", NULL))
|
||||
sport->inverted_tx = 1;
|
||||
|
||||
if (of_get_property(np, "fsl,inverted-rx", NULL))
|
||||
sport->inverted_rx = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/************************************************************************
|
||||
* Copyright 2003 Digi International (www.digi.com)
|
||||
*
|
||||
|
|
|
@ -169,15 +169,13 @@ static int configure_kgdboc(void)
|
|||
if (!p)
|
||||
goto noconfig;
|
||||
|
||||
cons = console_drivers;
|
||||
while (cons) {
|
||||
for_each_console(cons) {
|
||||
int idx;
|
||||
if (cons->device && cons->device(cons, &idx) == p &&
|
||||
idx == tty_line) {
|
||||
kgdboc_io_ops.is_console = 1;
|
||||
break;
|
||||
}
|
||||
cons = cons->next;
|
||||
}
|
||||
|
||||
kgdb_tty_driver = p;
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
|
|
|
@ -37,8 +37,6 @@
|
|||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_data/serial-omap.h>
|
||||
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
#define OMAP_MAX_HSUART_PORTS 10
|
||||
|
||||
#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y))
|
||||
|
|
|
@ -310,32 +310,32 @@ static ssize_t port_show_regs(struct file *file, char __user *user_buf,
|
|||
if (!buf)
|
||||
return 0;
|
||||
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"PCH EG20T port[%d] regs:\n", priv->port.line);
|
||||
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"=================================\n");
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"IER: \t0x%02x\n", ioread8(priv->membase + UART_IER));
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"IIR: \t0x%02x\n", ioread8(priv->membase + UART_IIR));
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"LCR: \t0x%02x\n", ioread8(priv->membase + UART_LCR));
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"MCR: \t0x%02x\n", ioread8(priv->membase + UART_MCR));
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"LSR: \t0x%02x\n", ioread8(priv->membase + UART_LSR));
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"MSR: \t0x%02x\n", ioread8(priv->membase + UART_MSR));
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"BRCSR: \t0x%02x\n",
|
||||
ioread8(priv->membase + PCH_UART_BRCSR));
|
||||
|
||||
lcr = ioread8(priv->membase + UART_LCR);
|
||||
iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"DLL: \t0x%02x\n", ioread8(priv->membase + UART_DLL));
|
||||
len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
len += scnprintf(buf + len, PCH_REGS_BUFSIZE - len,
|
||||
"DLM: \t0x%02x\n", ioread8(priv->membase + UART_DLM));
|
||||
iowrite8(lcr, priv->membase + UART_LCR);
|
||||
|
||||
|
|
|
@ -768,11 +768,6 @@ static int __init pic32_console_init(void)
|
|||
}
|
||||
console_initcall(pic32_console_init);
|
||||
|
||||
static inline bool is_pic32_console_port(struct uart_port *port)
|
||||
{
|
||||
return port->cons && port->cons->index == port->line;
|
||||
}
|
||||
|
||||
/*
|
||||
* Late console initialization.
|
||||
*/
|
||||
|
@ -873,8 +868,7 @@ static int pic32_uart_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_PIC32_CONSOLE
|
||||
if (is_pic32_console_port(port) &&
|
||||
(pic32_console.flags & CON_ENABLED)) {
|
||||
if (uart_console(port) && (pic32_console.flags & CON_ENABLED)) {
|
||||
/* The peripheral clock has been enabled by console_setup,
|
||||
* so disable it till the port is used.
|
||||
*/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* PIC32 Integrated Serial Driver.
|
||||
*
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
/* UART specific GENI registers */
|
||||
#define SE_UART_LOOPBACK_CFG 0x22c
|
||||
#define SE_UART_IO_MACRO_CTRL 0x240
|
||||
#define SE_UART_TX_TRANS_CFG 0x25c
|
||||
#define SE_UART_TX_WORD_LEN 0x268
|
||||
#define SE_UART_TX_STOP_BIT_LEN 0x26c
|
||||
|
@ -95,6 +96,12 @@
|
|||
#define CTS_RTS_SORTED BIT(1)
|
||||
#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED)
|
||||
|
||||
/* UART pin swap value */
|
||||
#define DEFAULT_IO_MACRO_IO0_IO1_MASK GENMASK(3, 0)
|
||||
#define IO_MACRO_IO0_SEL 0x3
|
||||
#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4)
|
||||
#define IO_MACRO_IO2_IO3_SWAP 0x4640
|
||||
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
#define CONSOLE_RX_BYTES_PW 1
|
||||
#else
|
||||
|
@ -113,12 +120,14 @@ struct qcom_geni_serial_port {
|
|||
unsigned int baud;
|
||||
unsigned int tx_bytes_pw;
|
||||
unsigned int rx_bytes_pw;
|
||||
u32 *rx_fifo;
|
||||
void *rx_fifo;
|
||||
u32 loopback;
|
||||
bool brk;
|
||||
|
||||
unsigned int tx_remaining;
|
||||
int wakeup_irq;
|
||||
bool rx_tx_swap;
|
||||
bool cts_rts_swap;
|
||||
};
|
||||
|
||||
static const struct uart_ops qcom_geni_console_pops;
|
||||
|
@ -505,7 +514,6 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
|
|||
|
||||
static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
|
||||
{
|
||||
unsigned char *buf;
|
||||
struct tty_port *tport;
|
||||
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
|
||||
u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE;
|
||||
|
@ -517,8 +525,7 @@ static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
|
|||
if (drop)
|
||||
return 0;
|
||||
|
||||
buf = (unsigned char *)port->rx_fifo;
|
||||
ret = tty_insert_flip_string(tport, buf, bytes);
|
||||
ret = tty_insert_flip_string(tport, port->rx_fifo, bytes);
|
||||
if (ret != bytes) {
|
||||
dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
|
||||
__func__, ret, bytes);
|
||||
|
@ -818,17 +825,7 @@ static void get_tx_fifo_size(struct qcom_geni_serial_port *port)
|
|||
|
||||
static void qcom_geni_serial_shutdown(struct uart_port *uport)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* Stop the console before stopping the current tx */
|
||||
if (uart_console(uport))
|
||||
console_stop(uport->cons);
|
||||
|
||||
disable_irq(uport->irq);
|
||||
spin_lock_irqsave(&uport->lock, flags);
|
||||
qcom_geni_serial_stop_tx(uport);
|
||||
qcom_geni_serial_stop_rx(uport);
|
||||
spin_unlock_irqrestore(&uport->lock, flags);
|
||||
}
|
||||
|
||||
static int qcom_geni_serial_port_setup(struct uart_port *uport)
|
||||
|
@ -836,6 +833,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
|
|||
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
|
||||
u32 rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
|
||||
u32 proto;
|
||||
u32 pin_swap;
|
||||
|
||||
if (uart_console(uport)) {
|
||||
port->tx_bytes_pw = 1;
|
||||
|
@ -856,6 +854,20 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
|
|||
get_tx_fifo_size(port);
|
||||
|
||||
writel(rxstale, uport->membase + SE_UART_RX_STALE_CNT);
|
||||
|
||||
pin_swap = readl(uport->membase + SE_UART_IO_MACRO_CTRL);
|
||||
if (port->rx_tx_swap) {
|
||||
pin_swap &= ~DEFAULT_IO_MACRO_IO2_IO3_MASK;
|
||||
pin_swap |= IO_MACRO_IO2_IO3_SWAP;
|
||||
}
|
||||
if (port->cts_rts_swap) {
|
||||
pin_swap &= ~DEFAULT_IO_MACRO_IO0_IO1_MASK;
|
||||
pin_swap |= IO_MACRO_IO0_SEL;
|
||||
}
|
||||
/* Configure this register if RX-TX, CTS-RTS pins are swapped */
|
||||
if (port->rx_tx_swap || port->cts_rts_swap)
|
||||
writel(pin_swap, uport->membase + SE_UART_IO_MACRO_CTRL);
|
||||
|
||||
/*
|
||||
* Make an unconditional cancel on the main sequencer to reset
|
||||
* it else we could end up in data loss scenarios.
|
||||
|
@ -868,12 +880,6 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
|
|||
false, false, true);
|
||||
geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2);
|
||||
geni_se_select_mode(&port->se, GENI_SE_FIFO);
|
||||
if (!uart_console(uport)) {
|
||||
port->rx_fifo = devm_kcalloc(uport->dev,
|
||||
port->rx_fifo_depth, sizeof(u32), GFP_KERNEL);
|
||||
if (!port->rx_fifo)
|
||||
return -ENOMEM;
|
||||
}
|
||||
port->setup = true;
|
||||
|
||||
return 0;
|
||||
|
@ -1284,6 +1290,13 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
|
|||
port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
|
||||
port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
|
||||
|
||||
if (!console) {
|
||||
port->rx_fifo = devm_kcalloc(uport->dev,
|
||||
port->rx_fifo_depth, sizeof(u32), GFP_KERNEL);
|
||||
if (!port->rx_fifo)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
port->name = devm_kasprintf(uport->dev, GFP_KERNEL,
|
||||
"qcom_geni_serial_%s%d",
|
||||
uart_console(uport) ? "console" : "uart", uport->line);
|
||||
|
@ -1299,6 +1312,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
|
|||
if (!console)
|
||||
port->wakeup_irq = platform_get_irq_optional(pdev, 1);
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "rx-tx-swap"))
|
||||
port->rx_tx_swap = true;
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "cts-rts-swap"))
|
||||
port->cts_rts_swap = true;
|
||||
|
||||
uport->private_data = drv;
|
||||
platform_set_drvdata(pdev, port);
|
||||
port->handle_rx = console ? handle_rx_console : handle_rx_uart;
|
||||
|
|
|
@ -329,7 +329,7 @@ struct sc16is7xx_port {
|
|||
struct task_struct *kworker_task;
|
||||
struct kthread_work irq_work;
|
||||
struct mutex efr_lock;
|
||||
struct sc16is7xx_one p[0];
|
||||
struct sc16is7xx_one p[];
|
||||
};
|
||||
|
||||
static unsigned long sc16is7xx_lines;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/serial.h> /* for serial_state and serial_icounter_struct */
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/security.h>
|
||||
|
@ -40,6 +41,8 @@ static struct lock_class_key port_lock_key;
|
|||
|
||||
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
|
||||
|
||||
#define SYSRQ_TIMEOUT (HZ * 5)
|
||||
|
||||
static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
|
||||
struct ktermios *old_termios);
|
||||
static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
|
||||
|
@ -1908,6 +1911,24 @@ static int uart_proc_show(struct seq_file *m, void *v)
|
|||
}
|
||||
#endif
|
||||
|
||||
static inline bool uart_console_enabled(struct uart_port *port)
|
||||
{
|
||||
return uart_console(port) && (port->cons->flags & CON_ENABLED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that the serial console lock is initialised early.
|
||||
* If this port is a console, then the spinlock is already initialised.
|
||||
*/
|
||||
static inline void uart_port_spin_lock_init(struct uart_port *port)
|
||||
{
|
||||
if (uart_console(port))
|
||||
return;
|
||||
|
||||
spin_lock_init(&port->lock);
|
||||
lockdep_set_class(&port->lock, &port_lock_key);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
|
||||
/**
|
||||
* uart_console_write - write a console message to a serial port
|
||||
|
@ -2060,16 +2081,7 @@ uart_set_options(struct uart_port *port, struct console *co,
|
|||
struct ktermios termios;
|
||||
static struct ktermios dummy;
|
||||
|
||||
/*
|
||||
* Ensure that the serial console lock is initialised
|
||||
* early.
|
||||
* If this port is a console, then the spinlock is already
|
||||
* initialised.
|
||||
*/
|
||||
if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
|
||||
spin_lock_init(&port->lock);
|
||||
lockdep_set_class(&port->lock, &port_lock_key);
|
||||
}
|
||||
uart_port_spin_lock_init(port);
|
||||
|
||||
memset(&termios, 0, sizeof(struct ktermios));
|
||||
|
||||
|
@ -2605,7 +2617,7 @@ struct tty_driver *uart_console_device(struct console *co, int *index)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(uart_console_device);
|
||||
|
||||
static ssize_t uart_get_attr_uartclk(struct device *dev,
|
||||
static ssize_t uartclk_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2615,7 +2627,7 @@ static ssize_t uart_get_attr_uartclk(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.baud_base * 16);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_type(struct device *dev,
|
||||
static ssize_t type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2624,7 +2636,8 @@ static ssize_t uart_get_attr_type(struct device *dev,
|
|||
uart_get_info(port, &tmp);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.type);
|
||||
}
|
||||
static ssize_t uart_get_attr_line(struct device *dev,
|
||||
|
||||
static ssize_t line_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2634,7 +2647,7 @@ static ssize_t uart_get_attr_line(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.line);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_port(struct device *dev,
|
||||
static ssize_t port_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2648,7 +2661,7 @@ static ssize_t uart_get_attr_port(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "0x%lX\n", ioaddr);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_irq(struct device *dev,
|
||||
static ssize_t irq_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2658,7 +2671,7 @@ static ssize_t uart_get_attr_irq(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.irq);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_flags(struct device *dev,
|
||||
static ssize_t flags_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2668,7 +2681,7 @@ static ssize_t uart_get_attr_flags(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "0x%X\n", tmp.flags);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_xmit_fifo_size(struct device *dev,
|
||||
static ssize_t xmit_fifo_size_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2678,8 +2691,7 @@ static ssize_t uart_get_attr_xmit_fifo_size(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.xmit_fifo_size);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uart_get_attr_close_delay(struct device *dev,
|
||||
static ssize_t close_delay_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2689,8 +2701,7 @@ static ssize_t uart_get_attr_close_delay(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.close_delay);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uart_get_attr_closing_wait(struct device *dev,
|
||||
static ssize_t closing_wait_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2700,7 +2711,7 @@ static ssize_t uart_get_attr_closing_wait(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.closing_wait);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_custom_divisor(struct device *dev,
|
||||
static ssize_t custom_divisor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2710,7 +2721,7 @@ static ssize_t uart_get_attr_custom_divisor(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.custom_divisor);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_io_type(struct device *dev,
|
||||
static ssize_t io_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2720,7 +2731,7 @@ static ssize_t uart_get_attr_io_type(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.io_type);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_iomem_base(struct device *dev,
|
||||
static ssize_t iomem_base_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2730,7 +2741,7 @@ static ssize_t uart_get_attr_iomem_base(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)tmp.iomem_base);
|
||||
}
|
||||
|
||||
static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev,
|
||||
static ssize_t iomem_reg_shift_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
@ -2740,40 +2751,92 @@ static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL);
|
||||
static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL);
|
||||
static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL);
|
||||
static DEVICE_ATTR(irq, S_IRUSR | S_IRGRP, uart_get_attr_irq, NULL);
|
||||
static DEVICE_ATTR(flags, S_IRUSR | S_IRGRP, uart_get_attr_flags, NULL);
|
||||
static DEVICE_ATTR(xmit_fifo_size, S_IRUSR | S_IRGRP, uart_get_attr_xmit_fifo_size, NULL);
|
||||
static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL);
|
||||
static DEVICE_ATTR(close_delay, S_IRUSR | S_IRGRP, uart_get_attr_close_delay, NULL);
|
||||
static DEVICE_ATTR(closing_wait, S_IRUSR | S_IRGRP, uart_get_attr_closing_wait, NULL);
|
||||
static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divisor, NULL);
|
||||
static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL);
|
||||
static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL);
|
||||
static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL);
|
||||
static ssize_t console_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tty_port *port = dev_get_drvdata(dev);
|
||||
struct uart_state *state = container_of(port, struct uart_state, port);
|
||||
struct uart_port *uport;
|
||||
bool console = false;
|
||||
|
||||
mutex_lock(&port->mutex);
|
||||
uport = uart_port_check(state);
|
||||
if (uport)
|
||||
console = uart_console_enabled(uport);
|
||||
mutex_unlock(&port->mutex);
|
||||
|
||||
return sprintf(buf, "%c\n", console ? 'Y' : 'N');
|
||||
}
|
||||
|
||||
static ssize_t console_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct tty_port *port = dev_get_drvdata(dev);
|
||||
struct uart_state *state = container_of(port, struct uart_state, port);
|
||||
struct uart_port *uport;
|
||||
bool oldconsole, newconsole;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &newconsole);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&port->mutex);
|
||||
uport = uart_port_check(state);
|
||||
if (uport) {
|
||||
oldconsole = uart_console_enabled(uport);
|
||||
if (oldconsole && !newconsole) {
|
||||
ret = unregister_console(uport->cons);
|
||||
} else if (!oldconsole && newconsole) {
|
||||
if (uart_console(uport))
|
||||
register_console(uport->cons);
|
||||
else
|
||||
ret = -ENOENT;
|
||||
}
|
||||
} else {
|
||||
ret = -ENXIO;
|
||||
}
|
||||
mutex_unlock(&port->mutex);
|
||||
|
||||
return ret < 0 ? ret : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(uartclk);
|
||||
static DEVICE_ATTR_RO(type);
|
||||
static DEVICE_ATTR_RO(line);
|
||||
static DEVICE_ATTR_RO(port);
|
||||
static DEVICE_ATTR_RO(irq);
|
||||
static DEVICE_ATTR_RO(flags);
|
||||
static DEVICE_ATTR_RO(xmit_fifo_size);
|
||||
static DEVICE_ATTR_RO(close_delay);
|
||||
static DEVICE_ATTR_RO(closing_wait);
|
||||
static DEVICE_ATTR_RO(custom_divisor);
|
||||
static DEVICE_ATTR_RO(io_type);
|
||||
static DEVICE_ATTR_RO(iomem_base);
|
||||
static DEVICE_ATTR_RO(iomem_reg_shift);
|
||||
static DEVICE_ATTR_RW(console);
|
||||
|
||||
static struct attribute *tty_dev_attrs[] = {
|
||||
&dev_attr_uartclk.attr,
|
||||
&dev_attr_type.attr,
|
||||
&dev_attr_line.attr,
|
||||
&dev_attr_port.attr,
|
||||
&dev_attr_irq.attr,
|
||||
&dev_attr_flags.attr,
|
||||
&dev_attr_xmit_fifo_size.attr,
|
||||
&dev_attr_uartclk.attr,
|
||||
&dev_attr_close_delay.attr,
|
||||
&dev_attr_closing_wait.attr,
|
||||
&dev_attr_custom_divisor.attr,
|
||||
&dev_attr_io_type.attr,
|
||||
&dev_attr_iomem_base.attr,
|
||||
&dev_attr_iomem_reg_shift.attr,
|
||||
NULL,
|
||||
};
|
||||
&dev_attr_console.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group tty_dev_attr_group = {
|
||||
.attrs = tty_dev_attrs,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* uart_add_one_port - attach a driver-defined port structure
|
||||
|
@ -2824,14 +2887,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this port is a console, then the spinlock is already
|
||||
* initialised.
|
||||
*/
|
||||
if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
|
||||
spin_lock_init(&uport->lock);
|
||||
lockdep_set_class(&uport->lock, &port_lock_key);
|
||||
}
|
||||
uart_port_spin_lock_init(uport);
|
||||
|
||||
if (uport->cons && uport->dev)
|
||||
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
|
||||
|
||||
|
@ -3082,6 +3139,60 @@ void uart_insert_char(struct uart_port *port, unsigned int status,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(uart_insert_char);
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ_SERIAL
|
||||
static const char sysrq_toggle_seq[] = CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE;
|
||||
|
||||
static void uart_sysrq_on(struct work_struct *w)
|
||||
{
|
||||
int sysrq_toggle_seq_len = strlen(sysrq_toggle_seq);
|
||||
|
||||
sysrq_toggle_support(1);
|
||||
pr_info("SysRq is enabled by magic sequence '%*pE' on serial\n",
|
||||
sysrq_toggle_seq_len, sysrq_toggle_seq);
|
||||
}
|
||||
static DECLARE_WORK(sysrq_enable_work, uart_sysrq_on);
|
||||
|
||||
/**
|
||||
* uart_try_toggle_sysrq - Enables SysRq from serial line
|
||||
* @port: uart_port structure where char(s) after BREAK met
|
||||
* @ch: new character in the sequence after received BREAK
|
||||
*
|
||||
* Enables magic SysRq when the required sequence is met on port
|
||||
* (see CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE).
|
||||
*
|
||||
* Returns false if @ch is out of enabling sequence and should be
|
||||
* handled some other way, true if @ch was consumed.
|
||||
*/
|
||||
static bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch)
|
||||
{
|
||||
int sysrq_toggle_seq_len = strlen(sysrq_toggle_seq);
|
||||
|
||||
if (!sysrq_toggle_seq_len)
|
||||
return false;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(sysrq_toggle_seq) >= U8_MAX);
|
||||
if (sysrq_toggle_seq[port->sysrq_seq] != ch) {
|
||||
port->sysrq_seq = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (++port->sysrq_seq < sysrq_toggle_seq_len) {
|
||||
port->sysrq = jiffies + SYSRQ_TIMEOUT;
|
||||
return true;
|
||||
}
|
||||
|
||||
schedule_work(&sysrq_enable_work);
|
||||
|
||||
port->sysrq = 0;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static inline bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_MAGIC_SYSRQ_SERIAL))
|
||||
|
@ -3091,10 +3202,14 @@ int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
|
|||
return 0;
|
||||
|
||||
if (ch && time_before(jiffies, port->sysrq)) {
|
||||
if (sysrq_mask()) {
|
||||
handle_sysrq(ch);
|
||||
port->sysrq = 0;
|
||||
return 1;
|
||||
}
|
||||
if (uart_try_toggle_sysrq(port, ch))
|
||||
return 1;
|
||||
}
|
||||
port->sysrq = 0;
|
||||
|
||||
return 0;
|
||||
|
@ -3110,32 +3225,33 @@ int uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch)
|
|||
return 0;
|
||||
|
||||
if (ch && time_before(jiffies, port->sysrq)) {
|
||||
if (sysrq_mask()) {
|
||||
port->sysrq_ch = ch;
|
||||
port->sysrq = 0;
|
||||
return 1;
|
||||
}
|
||||
if (uart_try_toggle_sysrq(port, ch))
|
||||
return 1;
|
||||
}
|
||||
port->sysrq = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uart_prepare_sysrq_char);
|
||||
|
||||
void uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long irqflags)
|
||||
void uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long flags)
|
||||
__releases(&port->lock)
|
||||
{
|
||||
int sysrq_ch;
|
||||
if (port->has_sysrq) {
|
||||
int sysrq_ch = port->sysrq_ch;
|
||||
|
||||
if (!port->has_sysrq) {
|
||||
spin_unlock_irqrestore(&port->lock, irqflags);
|
||||
return;
|
||||
}
|
||||
|
||||
sysrq_ch = port->sysrq_ch;
|
||||
port->sysrq_ch = 0;
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, irqflags);
|
||||
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
if (sysrq_ch)
|
||||
handle_sysrq(sysrq_ch);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uart_unlock_and_check_sysrq);
|
||||
|
||||
|
@ -3149,15 +3265,13 @@ int uart_handle_break(struct uart_port *port)
|
|||
if (port->handle_break)
|
||||
port->handle_break(port);
|
||||
|
||||
if (port->has_sysrq) {
|
||||
if (port->cons && port->cons->index == port->line) {
|
||||
if (port->has_sysrq && uart_console(port)) {
|
||||
if (!port->sysrq) {
|
||||
port->sysrq = jiffies + HZ*5;
|
||||
port->sysrq = jiffies + SYSRQ_TIMEOUT;
|
||||
return 1;
|
||||
}
|
||||
port->sysrq = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (port->flags & UPF_SAK)
|
||||
do_SAK(state->port.tty);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Helpers for controlling modem lines via GPIO
|
||||
*
|
||||
|
|
|
@ -618,10 +618,10 @@ static void sifive_serial_shutdown(struct uart_port *port)
|
|||
*
|
||||
* On the V0 SoC, the UART IP block is derived from the CPU clock source
|
||||
* after a synchronous divide-by-two divider, so any CPU clock rate change
|
||||
* requires the UART baud rate to be updated. This presumably could corrupt any
|
||||
* serial word currently being transmitted or received. It would probably
|
||||
* be better to stop receives and transmits, then complete the baud rate
|
||||
* change, then re-enable them.
|
||||
* requires the UART baud rate to be updated. This presumably corrupts any
|
||||
* serial word currently being transmitted or received. In order to avoid
|
||||
* corrupting the output data stream, we drain the transmit queue before
|
||||
* allowing the clock's rate to be changed.
|
||||
*/
|
||||
static int sifive_serial_clk_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
|
@ -629,6 +629,26 @@ static int sifive_serial_clk_notifier(struct notifier_block *nb,
|
|||
struct clk_notifier_data *cnd = data;
|
||||
struct sifive_serial_port *ssp = notifier_to_sifive_serial_port(nb);
|
||||
|
||||
if (event == PRE_RATE_CHANGE) {
|
||||
/*
|
||||
* The TX watermark is always set to 1 by this driver, which
|
||||
* means that the TX busy bit will lower when there are 0 bytes
|
||||
* left in the TX queue -- in other words, when the TX FIFO is
|
||||
* empty.
|
||||
*/
|
||||
__ssp_wait_for_xmitr(ssp);
|
||||
/*
|
||||
* On the cycle the TX FIFO goes empty there is still a full
|
||||
* UART frame left to be transmitted in the shift register.
|
||||
* The UART provides no way for software to directly determine
|
||||
* when that last frame has been transmitted, so we just sleep
|
||||
* here instead. As we're not tracking the number of stop bits
|
||||
* they're just worst cased here. The rest of the serial
|
||||
* framing parameters aren't configurable by software.
|
||||
*/
|
||||
udelay(DIV_ROUND_UP(12 * 1000 * 1000, ssp->baud_rate));
|
||||
}
|
||||
|
||||
if (event == POST_RATE_CHANGE && ssp->clkin_rate != cnd->new_rate) {
|
||||
ssp->clkin_rate = cnd->new_rate;
|
||||
__ssp_update_div(ssp);
|
||||
|
@ -709,6 +729,29 @@ static const char *sifive_serial_type(struct uart_port *port)
|
|||
return port->type == PORT_SIFIVE_V0 ? "SiFive UART v0" : NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
static int sifive_serial_poll_get_char(struct uart_port *port)
|
||||
{
|
||||
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
|
||||
char is_empty, ch;
|
||||
|
||||
ch = __ssp_receive_char(ssp, &is_empty);
|
||||
if (is_empty)
|
||||
return NO_POLL_CHAR;
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
static void sifive_serial_poll_put_char(struct uart_port *port,
|
||||
unsigned char c)
|
||||
{
|
||||
struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
|
||||
|
||||
__ssp_wait_for_xmitr(ssp);
|
||||
__ssp_transmit_char(ssp, c);
|
||||
}
|
||||
#endif /* CONFIG_CONSOLE_POLL */
|
||||
|
||||
/*
|
||||
* Early console support
|
||||
*/
|
||||
|
@ -877,6 +920,10 @@ static const struct uart_ops sifive_serial_uops = {
|
|||
.request_port = sifive_serial_request_port,
|
||||
.config_port = sifive_serial_config_port,
|
||||
.verify_port = sifive_serial_verify_port,
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
.poll_get_char = sifive_serial_poll_get_char,
|
||||
.poll_put_char = sifive_serial_poll_put_char,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct uart_driver sifive_serial_uart_driver = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Drivers for CSR SiRFprimaII onboard UARTs.
|
||||
*
|
||||
|
|
|
@ -1013,7 +1013,7 @@ static void sprd_console_write(struct console *co, const char *s,
|
|||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static int __init sprd_console_setup(struct console *co, char *options)
|
||||
static int sprd_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct sprd_uart_port *sprd_uart_port;
|
||||
int baud = 115200;
|
||||
|
@ -1102,29 +1102,6 @@ static struct uart_driver sprd_uart_driver = {
|
|||
.cons = SPRD_CONSOLE,
|
||||
};
|
||||
|
||||
static int sprd_probe_dt_alias(int index, struct device *dev)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret = index;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_OF))
|
||||
return ret;
|
||||
|
||||
np = dev->of_node;
|
||||
if (!np)
|
||||
return ret;
|
||||
|
||||
ret = of_alias_get_id(np, "serial");
|
||||
if (ret < 0)
|
||||
ret = index;
|
||||
else if (ret >= ARRAY_SIZE(sprd_port) || sprd_port[ret] != NULL) {
|
||||
dev_warn(dev, "requested serial port %d not available.\n", ret);
|
||||
ret = index;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_remove(struct platform_device *dev)
|
||||
{
|
||||
struct sprd_uart_port *sup = platform_get_drvdata(dev);
|
||||
|
@ -1132,14 +1109,13 @@ static int sprd_remove(struct platform_device *dev)
|
|||
if (sup) {
|
||||
uart_remove_one_port(&sprd_uart_driver, &sup->port);
|
||||
sprd_port[sup->port.line] = NULL;
|
||||
sprd_rx_free_buf(sup);
|
||||
sprd_ports_num--;
|
||||
}
|
||||
|
||||
if (!sprd_ports_num)
|
||||
uart_unregister_driver(&sprd_uart_driver);
|
||||
|
||||
sprd_rx_free_buf(sup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1147,7 +1123,8 @@ static bool sprd_uart_is_console(struct uart_port *uport)
|
|||
{
|
||||
struct console *cons = sprd_uart_driver.cons;
|
||||
|
||||
if (cons && cons->index >= 0 && cons->index == uport->line)
|
||||
if ((cons && cons->index >= 0 && cons->index == uport->line) ||
|
||||
of_console_check(uport->dev->of_node, SPRD_TTY_NAME, uport->line))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -1203,14 +1180,11 @@ static int sprd_probe(struct platform_device *pdev)
|
|||
int index;
|
||||
int ret;
|
||||
|
||||
for (index = 0; index < ARRAY_SIZE(sprd_port); index++)
|
||||
if (sprd_port[index] == NULL)
|
||||
break;
|
||||
|
||||
if (index == ARRAY_SIZE(sprd_port))
|
||||
return -EBUSY;
|
||||
|
||||
index = sprd_probe_dt_alias(index, &pdev->dev);
|
||||
index = of_alias_get_id(pdev->dev.of_node, "serial");
|
||||
if (index < 0 || index >= ARRAY_SIZE(sprd_port)) {
|
||||
dev_err(&pdev->dev, "got a wrong serial alias id %d\n", index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sprd_port[index] = devm_kzalloc(&pdev->dev, sizeof(*sprd_port[index]),
|
||||
GFP_KERNEL);
|
||||
|
@ -1262,10 +1236,8 @@ static int sprd_probe(struct platform_device *pdev)
|
|||
sprd_ports_num++;
|
||||
|
||||
ret = uart_add_one_port(&sprd_uart_driver, up);
|
||||
if (ret) {
|
||||
sprd_port[index] = NULL;
|
||||
if (ret)
|
||||
sprd_remove(pdev);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, up);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) Maxime Coquelin 2015
|
||||
* Copyright (C) STMicroelectronics SA 2017
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* timbuart.c timberdale FPGA GPIO driver
|
||||
* Copyright (c) 2009 Intel Corporation
|
||||
|
|
|
@ -650,8 +650,8 @@ static unsigned int cdns_uart_tx_empty(struct uart_port *port)
|
|||
unsigned int status;
|
||||
|
||||
status = readl(port->membase + CDNS_UART_SR) &
|
||||
CDNS_UART_SR_TXEMPTY;
|
||||
return status ? TIOCSER_TEMT : 0;
|
||||
(CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE);
|
||||
return (status == CDNS_UART_SR_TXEMPTY) ? TIOCSER_TEMT : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -693,20 +693,8 @@ static void cdns_uart_set_termios(struct uart_port *port,
|
|||
u32 cval = 0;
|
||||
unsigned int baud, minbaud, maxbaud;
|
||||
unsigned long flags;
|
||||
unsigned int ctrl_reg, mode_reg, val;
|
||||
int err;
|
||||
unsigned int ctrl_reg, mode_reg;
|
||||
|
||||
/* Wait for the transmit FIFO to empty before making changes */
|
||||
if (!(readl(port->membase + CDNS_UART_CR) &
|
||||
CDNS_UART_CR_TX_DIS)) {
|
||||
err = readl_poll_timeout(port->membase + CDNS_UART_SR,
|
||||
val, (val & CDNS_UART_SR_TXEMPTY),
|
||||
1000, TX_TIMEOUT);
|
||||
if (err) {
|
||||
dev_err(port->dev, "timed out waiting for tx empty");
|
||||
return;
|
||||
}
|
||||
}
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
|
||||
/* Disable the TX and RX to set baud rate */
|
||||
|
|
|
@ -63,6 +63,18 @@ static bool sysrq_on(void)
|
|||
return sysrq_enabled || sysrq_always_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* sysrq_mask - Getter for sysrq_enabled mask.
|
||||
*
|
||||
* Return: 1 if sysrq is always enabled, enabled sysrq_key_op mask otherwise.
|
||||
*/
|
||||
int sysrq_mask(void)
|
||||
{
|
||||
if (sysrq_always_enabled)
|
||||
return 1;
|
||||
return sysrq_enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
* A value of 1 means 'all', other nonzero values are an op mask:
|
||||
*/
|
||||
|
|
|
@ -35,18 +35,18 @@
|
|||
/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
|
||||
#define isspace(c) ((c) == ' ')
|
||||
|
||||
extern void poke_blanked_console(void);
|
||||
|
||||
/* FIXME: all this needs locking */
|
||||
/* Variables for selection control. */
|
||||
/* Use a dynamic buffer, instead of static (Dec 1994) */
|
||||
struct vc_data *sel_cons; /* must not be deallocated */
|
||||
static int use_unicode;
|
||||
static volatile int sel_start = -1; /* cleared by clear_selection */
|
||||
static int sel_end;
|
||||
static int sel_buffer_lth;
|
||||
static char *sel_buffer;
|
||||
static DEFINE_MUTEX(sel_lock);
|
||||
static struct vc_selection {
|
||||
struct mutex lock;
|
||||
struct vc_data *cons; /* must not be deallocated */
|
||||
char *buffer;
|
||||
unsigned int buf_len;
|
||||
volatile int start; /* cleared by clear_selection */
|
||||
int end;
|
||||
} vc_sel = {
|
||||
.lock = __MUTEX_INITIALIZER(vc_sel.lock),
|
||||
.start = -1,
|
||||
};
|
||||
|
||||
/* clear_selection, highlight and highlight_pointer can be called
|
||||
from interrupt (via scrollback/front) */
|
||||
|
@ -54,22 +54,21 @@ static DEFINE_MUTEX(sel_lock);
|
|||
/* set reverse video on characters s-e of console with selection. */
|
||||
static inline void highlight(const int s, const int e)
|
||||
{
|
||||
invert_screen(sel_cons, s, e-s+2, 1);
|
||||
invert_screen(vc_sel.cons, s, e-s+2, 1);
|
||||
}
|
||||
|
||||
/* use complementary color to show the pointer */
|
||||
static inline void highlight_pointer(const int where)
|
||||
{
|
||||
complement_pos(sel_cons, where);
|
||||
complement_pos(vc_sel.cons, where);
|
||||
}
|
||||
|
||||
static u32
|
||||
sel_pos(int n)
|
||||
sel_pos(int n, bool unicode)
|
||||
{
|
||||
if (use_unicode)
|
||||
return screen_glyph_unicode(sel_cons, n / 2);
|
||||
return inverse_translate(sel_cons, screen_glyph(sel_cons, n),
|
||||
0);
|
||||
if (unicode)
|
||||
return screen_glyph_unicode(vc_sel.cons, n / 2);
|
||||
return inverse_translate(vc_sel.cons, screen_glyph(vc_sel.cons, n), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,13 +80,18 @@ sel_pos(int n)
|
|||
void clear_selection(void)
|
||||
{
|
||||
highlight_pointer(-1); /* hide the pointer */
|
||||
if (sel_start != -1) {
|
||||
highlight(sel_start, sel_end);
|
||||
sel_start = -1;
|
||||
if (vc_sel.start != -1) {
|
||||
highlight(vc_sel.start, vc_sel.end);
|
||||
vc_sel.start = -1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clear_selection);
|
||||
|
||||
bool vc_is_sel(struct vc_data *vc)
|
||||
{
|
||||
return vc == vc_sel.cons;
|
||||
}
|
||||
|
||||
/*
|
||||
* User settable table: what characters are to be considered alphabetic?
|
||||
* 128 bits. Locked by the console lock.
|
||||
|
@ -186,9 +190,10 @@ static int __set_selection_kernel(struct tiocl_selection *v, struct tty_struct *
|
|||
struct vc_data *vc = vc_cons[fg_console].d;
|
||||
int new_sel_start, new_sel_end, spc;
|
||||
char *bp, *obp;
|
||||
int i, ps, pe, multiplier;
|
||||
int i, ps, pe;
|
||||
u32 c;
|
||||
int mode, ret = 0;
|
||||
int ret = 0;
|
||||
bool unicode;
|
||||
|
||||
poke_blanked_console();
|
||||
|
||||
|
@ -211,41 +216,35 @@ static int __set_selection_kernel(struct tiocl_selection *v, struct tty_struct *
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (ps > pe) /* make sel_start <= sel_end */
|
||||
if (ps > pe) /* make vc_sel.start <= vc_sel.end */
|
||||
swap(ps, pe);
|
||||
|
||||
if (sel_cons != vc_cons[fg_console].d) {
|
||||
if (vc_sel.cons != vc_cons[fg_console].d) {
|
||||
clear_selection();
|
||||
sel_cons = vc_cons[fg_console].d;
|
||||
vc_sel.cons = vc_cons[fg_console].d;
|
||||
}
|
||||
mode = vt_do_kdgkbmode(fg_console);
|
||||
if (mode == K_UNICODE)
|
||||
use_unicode = 1;
|
||||
else
|
||||
use_unicode = 0;
|
||||
unicode = vt_do_kdgkbmode(fg_console) == K_UNICODE;
|
||||
|
||||
switch (v->sel_mode)
|
||||
{
|
||||
switch (v->sel_mode) {
|
||||
case TIOCL_SELCHAR: /* character-by-character selection */
|
||||
new_sel_start = ps;
|
||||
new_sel_end = pe;
|
||||
break;
|
||||
case TIOCL_SELWORD: /* word-by-word selection */
|
||||
spc = isspace(sel_pos(ps));
|
||||
for (new_sel_start = ps; ; ps -= 2)
|
||||
{
|
||||
if ((spc && !isspace(sel_pos(ps))) ||
|
||||
(!spc && !inword(sel_pos(ps))))
|
||||
spc = isspace(sel_pos(ps, unicode));
|
||||
for (new_sel_start = ps; ; ps -= 2) {
|
||||
if ((spc && !isspace(sel_pos(ps, unicode))) ||
|
||||
(!spc && !inword(sel_pos(ps, unicode))))
|
||||
break;
|
||||
new_sel_start = ps;
|
||||
if (!(ps % vc->vc_size_row))
|
||||
break;
|
||||
}
|
||||
spc = isspace(sel_pos(pe));
|
||||
for (new_sel_end = pe; ; pe += 2)
|
||||
{
|
||||
if ((spc && !isspace(sel_pos(pe))) ||
|
||||
(!spc && !inword(sel_pos(pe))))
|
||||
|
||||
spc = isspace(sel_pos(pe, unicode));
|
||||
for (new_sel_end = pe; ; pe += 2) {
|
||||
if ((spc && !isspace(sel_pos(pe, unicode))) ||
|
||||
(!spc && !inword(sel_pos(pe, unicode))))
|
||||
break;
|
||||
new_sel_end = pe;
|
||||
if (!((pe + 2) % vc->vc_size_row))
|
||||
|
@ -253,9 +252,9 @@ static int __set_selection_kernel(struct tiocl_selection *v, struct tty_struct *
|
|||
}
|
||||
break;
|
||||
case TIOCL_SELLINE: /* line-by-line selection */
|
||||
new_sel_start = ps - ps % vc->vc_size_row;
|
||||
new_sel_end = pe + vc->vc_size_row
|
||||
- pe % vc->vc_size_row - 2;
|
||||
new_sel_start = rounddown(ps, vc->vc_size_row);
|
||||
new_sel_end = rounddown(pe, vc->vc_size_row) +
|
||||
vc->vc_size_row - 2;
|
||||
break;
|
||||
case TIOCL_SELPOINTER:
|
||||
highlight_pointer(pe);
|
||||
|
@ -270,56 +269,56 @@ static int __set_selection_kernel(struct tiocl_selection *v, struct tty_struct *
|
|||
/* select to end of line if on trailing space */
|
||||
if (new_sel_end > new_sel_start &&
|
||||
!atedge(new_sel_end, vc->vc_size_row) &&
|
||||
isspace(sel_pos(new_sel_end))) {
|
||||
isspace(sel_pos(new_sel_end, unicode))) {
|
||||
for (pe = new_sel_end + 2; ; pe += 2)
|
||||
if (!isspace(sel_pos(pe)) ||
|
||||
if (!isspace(sel_pos(pe, unicode)) ||
|
||||
atedge(pe, vc->vc_size_row))
|
||||
break;
|
||||
if (isspace(sel_pos(pe)))
|
||||
if (isspace(sel_pos(pe, unicode)))
|
||||
new_sel_end = pe;
|
||||
}
|
||||
if (sel_start == -1) /* no current selection */
|
||||
if (vc_sel.start == -1) /* no current selection */
|
||||
highlight(new_sel_start, new_sel_end);
|
||||
else if (new_sel_start == sel_start)
|
||||
else if (new_sel_start == vc_sel.start)
|
||||
{
|
||||
if (new_sel_end == sel_end) /* no action required */
|
||||
if (new_sel_end == vc_sel.end) /* no action required */
|
||||
return 0;
|
||||
else if (new_sel_end > sel_end) /* extend to right */
|
||||
highlight(sel_end + 2, new_sel_end);
|
||||
else if (new_sel_end > vc_sel.end) /* extend to right */
|
||||
highlight(vc_sel.end + 2, new_sel_end);
|
||||
else /* contract from right */
|
||||
highlight(new_sel_end + 2, sel_end);
|
||||
highlight(new_sel_end + 2, vc_sel.end);
|
||||
}
|
||||
else if (new_sel_end == sel_end)
|
||||
else if (new_sel_end == vc_sel.end)
|
||||
{
|
||||
if (new_sel_start < sel_start) /* extend to left */
|
||||
highlight(new_sel_start, sel_start - 2);
|
||||
if (new_sel_start < vc_sel.start) /* extend to left */
|
||||
highlight(new_sel_start, vc_sel.start - 2);
|
||||
else /* contract from left */
|
||||
highlight(sel_start, new_sel_start - 2);
|
||||
highlight(vc_sel.start, new_sel_start - 2);
|
||||
}
|
||||
else /* some other case; start selection from scratch */
|
||||
{
|
||||
clear_selection();
|
||||
highlight(new_sel_start, new_sel_end);
|
||||
}
|
||||
sel_start = new_sel_start;
|
||||
sel_end = new_sel_end;
|
||||
vc_sel.start = new_sel_start;
|
||||
vc_sel.end = new_sel_end;
|
||||
|
||||
/* Allocate a new buffer before freeing the old one ... */
|
||||
multiplier = use_unicode ? 4 : 1; /* chars can take up to 4 bytes */
|
||||
bp = kmalloc_array((sel_end - sel_start) / 2 + 1, multiplier,
|
||||
/* chars can take up to 4 bytes with unicode */
|
||||
bp = kmalloc_array((vc_sel.end - vc_sel.start) / 2 + 1, unicode ? 4 : 1,
|
||||
GFP_KERNEL);
|
||||
if (!bp) {
|
||||
printk(KERN_WARNING "selection: kmalloc() failed\n");
|
||||
clear_selection();
|
||||
return -ENOMEM;
|
||||
}
|
||||
kfree(sel_buffer);
|
||||
sel_buffer = bp;
|
||||
kfree(vc_sel.buffer);
|
||||
vc_sel.buffer = bp;
|
||||
|
||||
obp = bp;
|
||||
for (i = sel_start; i <= sel_end; i += 2) {
|
||||
c = sel_pos(i);
|
||||
if (use_unicode)
|
||||
for (i = vc_sel.start; i <= vc_sel.end; i += 2) {
|
||||
c = sel_pos(i, unicode);
|
||||
if (unicode)
|
||||
bp += store_utf8(c, bp);
|
||||
else
|
||||
*bp++ = c;
|
||||
|
@ -335,7 +334,7 @@ static int __set_selection_kernel(struct tiocl_selection *v, struct tty_struct *
|
|||
obp = bp;
|
||||
}
|
||||
}
|
||||
sel_buffer_lth = bp - sel_buffer;
|
||||
vc_sel.buf_len = bp - vc_sel.buffer;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -344,11 +343,11 @@ int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
|
|||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sel_lock);
|
||||
mutex_lock(&vc_sel.lock);
|
||||
console_lock();
|
||||
ret = __set_selection_kernel(v, tty);
|
||||
console_unlock();
|
||||
mutex_unlock(&sel_lock);
|
||||
mutex_unlock(&vc_sel.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -380,26 +379,26 @@ int paste_selection(struct tty_struct *tty)
|
|||
tty_buffer_lock_exclusive(&vc->port);
|
||||
|
||||
add_wait_queue(&vc->paste_wait, &wait);
|
||||
mutex_lock(&sel_lock);
|
||||
while (sel_buffer && sel_buffer_lth > pasted) {
|
||||
mutex_lock(&vc_sel.lock);
|
||||
while (vc_sel.buffer && vc_sel.buf_len > pasted) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (signal_pending(current)) {
|
||||
ret = -EINTR;
|
||||
break;
|
||||
}
|
||||
if (tty_throttled(tty)) {
|
||||
mutex_unlock(&sel_lock);
|
||||
mutex_unlock(&vc_sel.lock);
|
||||
schedule();
|
||||
mutex_lock(&sel_lock);
|
||||
mutex_lock(&vc_sel.lock);
|
||||
continue;
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
count = sel_buffer_lth - pasted;
|
||||
count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL,
|
||||
count = vc_sel.buf_len - pasted;
|
||||
count = tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted, NULL,
|
||||
count);
|
||||
pasted += count;
|
||||
}
|
||||
mutex_unlock(&sel_lock);
|
||||
mutex_unlock(&vc_sel.lock);
|
||||
remove_wait_queue(&vc->paste_wait, &wait);
|
||||
__set_current_state(TASK_RUNNING);
|
||||
|
||||
|
|
|
@ -890,8 +890,9 @@ static void hide_softcursor(struct vc_data *vc)
|
|||
|
||||
static void hide_cursor(struct vc_data *vc)
|
||||
{
|
||||
if (vc == sel_cons)
|
||||
if (vc_is_sel(vc))
|
||||
clear_selection();
|
||||
|
||||
vc->vc_sw->con_cursor(vc, CM_ERASE);
|
||||
hide_softcursor(vc);
|
||||
}
|
||||
|
@ -901,7 +902,7 @@ static void set_cursor(struct vc_data *vc)
|
|||
if (!con_is_fg(vc) || console_blanked || vc->vc_mode == KD_GRAPHICS)
|
||||
return;
|
||||
if (vc->vc_deccm) {
|
||||
if (vc == sel_cons)
|
||||
if (vc_is_sel(vc))
|
||||
clear_selection();
|
||||
add_softcursor(vc);
|
||||
if ((vc->vc_cursor_type & 0x0f) != 1)
|
||||
|
@ -1074,6 +1075,17 @@ static void visual_deinit(struct vc_data *vc)
|
|||
module_put(vc->vc_sw->owner);
|
||||
}
|
||||
|
||||
static void vc_port_destruct(struct tty_port *port)
|
||||
{
|
||||
struct vc_data *vc = container_of(port, struct vc_data, port);
|
||||
|
||||
kfree(vc);
|
||||
}
|
||||
|
||||
static const struct tty_port_operations vc_port_ops = {
|
||||
.destruct = vc_port_destruct,
|
||||
};
|
||||
|
||||
int vc_allocate(unsigned int currcons) /* return 0 on success */
|
||||
{
|
||||
struct vt_notifier_param param;
|
||||
|
@ -1099,6 +1111,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
|
|||
|
||||
vc_cons[currcons].d = vc;
|
||||
tty_port_init(&vc->port);
|
||||
vc->port.ops = &vc_port_ops;
|
||||
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
|
||||
|
||||
visual_init(vc, currcons, 1);
|
||||
|
@ -1207,7 +1220,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
|
|||
}
|
||||
}
|
||||
|
||||
if (vc == sel_cons)
|
||||
if (vc_is_sel(vc))
|
||||
clear_selection();
|
||||
|
||||
old_rows = vc->vc_rows;
|
||||
|
@ -1901,18 +1914,16 @@ static void set_mode(struct vc_data *vc, int on_off)
|
|||
/* console_lock is held */
|
||||
static void setterm_command(struct vc_data *vc)
|
||||
{
|
||||
switch(vc->vc_par[0]) {
|
||||
switch (vc->vc_par[0]) {
|
||||
case 1: /* set color for underline mode */
|
||||
if (vc->vc_can_do_color &&
|
||||
vc->vc_par[1] < 16) {
|
||||
if (vc->vc_can_do_color && vc->vc_par[1] < 16) {
|
||||
vc->vc_ulcolor = color_table[vc->vc_par[1]];
|
||||
if (vc->vc_underline)
|
||||
update_attr(vc);
|
||||
}
|
||||
break;
|
||||
case 2: /* set color for half intensity mode */
|
||||
if (vc->vc_can_do_color &&
|
||||
vc->vc_par[1] < 16) {
|
||||
if (vc->vc_can_do_color && vc->vc_par[1] < 16) {
|
||||
vc->vc_halfcolor = color_table[vc->vc_par[1]];
|
||||
if (vc->vc_intensity == 0)
|
||||
update_attr(vc);
|
||||
|
@ -1926,7 +1937,7 @@ static void setterm_command(struct vc_data *vc)
|
|||
update_attr(vc);
|
||||
break;
|
||||
case 9: /* set blanking interval */
|
||||
blankinterval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60;
|
||||
blankinterval = min(vc->vc_par[1], 60U) * 60;
|
||||
poke_blanked_console();
|
||||
break;
|
||||
case 10: /* set bell frequency in Hz */
|
||||
|
@ -1950,7 +1961,7 @@ static void setterm_command(struct vc_data *vc)
|
|||
poke_blanked_console();
|
||||
break;
|
||||
case 14: /* set vesa powerdown interval */
|
||||
vesa_off_interval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60 * HZ;
|
||||
vesa_off_interval = min(vc->vc_par[1], 60U) * 60 * HZ;
|
||||
break;
|
||||
case 15: /* activate the previous console */
|
||||
set_console(last_console);
|
||||
|
@ -2576,8 +2587,6 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
|
|||
if (in_interrupt())
|
||||
return count;
|
||||
|
||||
might_sleep();
|
||||
|
||||
console_lock();
|
||||
vc = tty->driver_data;
|
||||
if (vc == NULL) {
|
||||
|
@ -3253,6 +3262,7 @@ static int con_install(struct tty_driver *driver, struct tty_struct *tty)
|
|||
|
||||
tty->driver_data = vc;
|
||||
vc->port.tty = tty;
|
||||
tty_port_get(&vc->port);
|
||||
|
||||
if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
|
||||
tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
|
||||
|
@ -3288,6 +3298,13 @@ static void con_shutdown(struct tty_struct *tty)
|
|||
console_unlock();
|
||||
}
|
||||
|
||||
static void con_cleanup(struct tty_struct *tty)
|
||||
{
|
||||
struct vc_data *vc = tty->driver_data;
|
||||
|
||||
tty_port_put(&vc->port);
|
||||
}
|
||||
|
||||
static int default_color = 7; /* white */
|
||||
static int default_italic_color = 2; // green (ASCII)
|
||||
static int default_underline_color = 3; // cyan (ASCII)
|
||||
|
@ -3413,7 +3430,8 @@ static const struct tty_operations con_ops = {
|
|||
.throttle = con_throttle,
|
||||
.unthrottle = con_unthrottle,
|
||||
.resize = vt_resize,
|
||||
.shutdown = con_shutdown
|
||||
.shutdown = con_shutdown,
|
||||
.cleanup = con_cleanup,
|
||||
};
|
||||
|
||||
static struct cdev vc0_cdev;
|
||||
|
|
|
@ -39,11 +39,32 @@
|
|||
#include <linux/kbd_diacr.h>
|
||||
#include <linux/selection.h>
|
||||
|
||||
char vt_dont_switch;
|
||||
extern struct tty_driver *console_driver;
|
||||
bool vt_dont_switch;
|
||||
|
||||
#define VT_IS_IN_USE(i) (console_driver->ttys[i] && console_driver->ttys[i]->count)
|
||||
#define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || vc_cons[i].d == sel_cons)
|
||||
static inline bool vt_in_use(unsigned int i)
|
||||
{
|
||||
const struct vc_data *vc = vc_cons[i].d;
|
||||
|
||||
/*
|
||||
* console_lock must be held to prevent the vc from being deallocated
|
||||
* while we're checking whether it's in-use.
|
||||
*/
|
||||
WARN_CONSOLE_UNLOCKED();
|
||||
|
||||
return vc && kref_read(&vc->port.kref) > 1;
|
||||
}
|
||||
|
||||
static inline bool vt_busy(int i)
|
||||
{
|
||||
if (vt_in_use(i))
|
||||
return true;
|
||||
if (i == fg_console)
|
||||
return true;
|
||||
if (vc_is_sel(vc_cons[i].d))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Console (vt and kd) routines, as defined by USL SVR4 manual, and by
|
||||
|
@ -289,16 +310,14 @@ static int vt_disallocate(unsigned int vc_num)
|
|||
int ret = 0;
|
||||
|
||||
console_lock();
|
||||
if (VT_BUSY(vc_num))
|
||||
if (vt_busy(vc_num))
|
||||
ret = -EBUSY;
|
||||
else if (vc_num)
|
||||
vc = vc_deallocate(vc_num);
|
||||
console_unlock();
|
||||
|
||||
if (vc && vc_num >= MIN_NR_CONSOLES) {
|
||||
tty_port_destroy(&vc->port);
|
||||
kfree(vc);
|
||||
}
|
||||
if (vc && vc_num >= MIN_NR_CONSOLES)
|
||||
tty_port_put(&vc->port);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -311,17 +330,15 @@ static void vt_disallocate_all(void)
|
|||
|
||||
console_lock();
|
||||
for (i = 1; i < MAX_NR_CONSOLES; i++)
|
||||
if (!VT_BUSY(i))
|
||||
if (!vt_busy(i))
|
||||
vc[i] = vc_deallocate(i);
|
||||
else
|
||||
vc[i] = NULL;
|
||||
console_unlock();
|
||||
|
||||
for (i = 1; i < MAX_NR_CONSOLES; i++) {
|
||||
if (vc[i] && i >= MIN_NR_CONSOLES) {
|
||||
tty_port_destroy(&vc[i]->port);
|
||||
kfree(vc[i]);
|
||||
}
|
||||
if (vc[i] && i >= MIN_NR_CONSOLES)
|
||||
tty_port_put(&vc[i]->port);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,22 +352,13 @@ int vt_ioctl(struct tty_struct *tty,
|
|||
{
|
||||
struct vc_data *vc = tty->driver_data;
|
||||
struct console_font_op op; /* used in multiple places here */
|
||||
unsigned int console;
|
||||
unsigned int console = vc->vc_num;
|
||||
unsigned char ucval;
|
||||
unsigned int uival;
|
||||
void __user *up = (void __user *)arg;
|
||||
int i, perm;
|
||||
int ret = 0;
|
||||
|
||||
console = vc->vc_num;
|
||||
|
||||
|
||||
if (!vc_cons_allocated(console)) { /* impossible? */
|
||||
ret = -ENOIOCTLCMD;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* To have permissions to do most of the vt ioctls, we either have
|
||||
* to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
|
||||
|
@ -641,15 +649,16 @@ int vt_ioctl(struct tty_struct *tty,
|
|||
struct vt_stat __user *vtstat = up;
|
||||
unsigned short state, mask;
|
||||
|
||||
/* Review: FIXME: Console lock ? */
|
||||
if (put_user(fg_console + 1, &vtstat->v_active))
|
||||
ret = -EFAULT;
|
||||
else {
|
||||
state = 1; /* /dev/tty0 is always open */
|
||||
console_lock(); /* required by vt_in_use() */
|
||||
for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask;
|
||||
++i, mask <<= 1)
|
||||
if (VT_IS_IN_USE(i))
|
||||
if (vt_in_use(i))
|
||||
state |= mask;
|
||||
console_unlock();
|
||||
ret = put_user(state, &vtstat->v_state);
|
||||
}
|
||||
break;
|
||||
|
@ -659,10 +668,11 @@ int vt_ioctl(struct tty_struct *tty,
|
|||
* Returns the first available (non-opened) console.
|
||||
*/
|
||||
case VT_OPENQRY:
|
||||
/* FIXME: locking ? - but then this is a stupid API */
|
||||
console_lock(); /* required by vt_in_use() */
|
||||
for (i = 0; i < MAX_NR_CONSOLES; ++i)
|
||||
if (! VT_IS_IN_USE(i))
|
||||
if (!vt_in_use(i))
|
||||
break;
|
||||
console_unlock();
|
||||
uival = i < MAX_NR_CONSOLES ? (i+1) : -1;
|
||||
goto setint;
|
||||
|
||||
|
@ -1011,12 +1021,12 @@ int vt_ioctl(struct tty_struct *tty,
|
|||
case VT_LOCKSWITCH:
|
||||
if (!capable(CAP_SYS_TTY_CONFIG))
|
||||
return -EPERM;
|
||||
vt_dont_switch = 1;
|
||||
vt_dont_switch = true;
|
||||
break;
|
||||
case VT_UNLOCKSWITCH:
|
||||
if (!capable(CAP_SYS_TTY_CONFIG))
|
||||
return -EPERM;
|
||||
vt_dont_switch = 0;
|
||||
vt_dont_switch = false;
|
||||
break;
|
||||
case VT_GETHIFONTMASK:
|
||||
ret = put_user(vc->vc_hi_font_mask,
|
||||
|
@ -1180,14 +1190,9 @@ long vt_compat_ioctl(struct tty_struct *tty,
|
|||
{
|
||||
struct vc_data *vc = tty->driver_data;
|
||||
struct console_font_op op; /* used in multiple places here */
|
||||
unsigned int console = vc->vc_num;
|
||||
void __user *up = compat_ptr(arg);
|
||||
int perm;
|
||||
|
||||
|
||||
if (!vc_cons_allocated(console)) /* impossible? */
|
||||
return -ENOIOCTLCMD;
|
||||
|
||||
/*
|
||||
* To have permissions to do most of the vt ioctls, we either have
|
||||
* to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
|
||||
|
|
|
@ -148,6 +148,7 @@ struct console {
|
|||
struct tty_driver *(*device)(struct console *, int *);
|
||||
void (*unblank)(void);
|
||||
int (*setup)(struct console *, char *);
|
||||
int (*exit)(struct console *);
|
||||
int (*match)(struct console *, char *name, int idx, char *options);
|
||||
short flags;
|
||||
short index;
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
#include <linux/tiocl.h>
|
||||
#include <linux/vt_buffer.h>
|
||||
|
||||
extern struct vc_data *sel_cons;
|
||||
struct tty_struct;
|
||||
struct vc_data;
|
||||
|
||||
extern void clear_selection(void);
|
||||
extern int set_selection_user(const struct tiocl_selection __user *sel,
|
||||
|
@ -24,6 +24,8 @@ extern int sel_loadlut(char __user *p);
|
|||
extern int mouse_reporting(void);
|
||||
extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry);
|
||||
|
||||
bool vc_is_sel(struct vc_data *vc);
|
||||
|
||||
extern int console_blanked;
|
||||
|
||||
extern const unsigned char color_table[];
|
||||
|
|
|
@ -81,6 +81,7 @@ struct uart_8250_em485 {
|
|||
struct hrtimer stop_tx_timer; /* "rs485 stop tx" timer */
|
||||
struct hrtimer *active_timer; /* pointer to active timer */
|
||||
struct uart_8250_port *port; /* for hrtimer callbacks */
|
||||
unsigned int tx_stopped:1; /* tx is currently stopped */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -132,6 +133,8 @@ struct uart_8250_port {
|
|||
void (*dl_write)(struct uart_8250_port *, int);
|
||||
|
||||
struct uart_8250_em485 *em485;
|
||||
void (*rs485_start_tx)(struct uart_8250_port *);
|
||||
void (*rs485_stop_tx)(struct uart_8250_port *);
|
||||
|
||||
/* Serial port overrun backoff */
|
||||
struct delayed_work overrun_backoff;
|
||||
|
@ -176,6 +179,7 @@ void serial8250_set_defaults(struct uart_8250_port *up);
|
|||
void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
||||
unsigned int count);
|
||||
int serial8250_console_setup(struct uart_port *port, char *options, bool probe);
|
||||
int serial8250_console_exit(struct uart_port *port);
|
||||
|
||||
extern void serial8250_set_isa_configurator(void (*v)
|
||||
(int port, struct uart_port *up,
|
||||
|
|
|
@ -243,6 +243,7 @@ struct uart_port {
|
|||
unsigned long sysrq; /* sysrq timeout */
|
||||
unsigned int sysrq_ch; /* char for sysrq */
|
||||
unsigned char has_sysrq;
|
||||
unsigned char sysrq_seq; /* index in sysrq_toggle_seq */
|
||||
|
||||
unsigned char hub6; /* this should be in the 8250 driver */
|
||||
unsigned char suspended;
|
||||
|
@ -461,8 +462,7 @@ extern void uart_insert_char(struct uart_port *port, unsigned int status,
|
|||
|
||||
extern int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch);
|
||||
extern int uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch);
|
||||
extern void uart_unlock_and_check_sysrq(struct uart_port *port,
|
||||
unsigned long irqflags);
|
||||
extern void uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long flags);
|
||||
extern int uart_handle_break(struct uart_port *port);
|
||||
|
||||
/*
|
||||
|
|
|
@ -3,12 +3,7 @@
|
|||
#define LINUX_IFX_MODEM_H
|
||||
|
||||
struct ifx_modem_platform_data {
|
||||
unsigned short rst_out; /* modem reset out */
|
||||
unsigned short pwr_on; /* power on */
|
||||
unsigned short rst_pmu; /* reset modem */
|
||||
unsigned short tx_pwr; /* modem power threshold */
|
||||
unsigned short srdy; /* SRDY */
|
||||
unsigned short mrdy; /* MRDY */
|
||||
unsigned char modem_type; /* Modem type */
|
||||
unsigned long max_hz; /* max SPI frequency */
|
||||
unsigned short use_dma:1; /* spi protocol driver supplies
|
||||
|
|
|
@ -50,6 +50,7 @@ int unregister_sysrq_key(int key, struct sysrq_key_op *op);
|
|||
struct sysrq_key_op *__sysrq_get_key_op(int key);
|
||||
|
||||
int sysrq_toggle_support(int enable_mask);
|
||||
int sysrq_mask(void);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -71,6 +72,12 @@ static inline int unregister_sysrq_key(int key, struct sysrq_key_op *op)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int sysrq_mask(void)
|
||||
{
|
||||
/* Magic SysRq disabled mask */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_SYSRQ_H */
|
||||
|
|
|
@ -28,8 +28,9 @@
|
|||
#define BROKEN_GRAPHICS_PROGRAMS 1
|
||||
#endif
|
||||
|
||||
extern void kd_mksound(unsigned int hz, unsigned int ticks);
|
||||
extern int kbd_rate(struct kbd_repeat *rep);
|
||||
void kd_mksound(unsigned int hz, unsigned int ticks);
|
||||
int kbd_rate(struct kbd_repeat *rep);
|
||||
|
||||
extern int fg_console, last_console, want_console;
|
||||
|
||||
/* console.c */
|
||||
|
@ -131,11 +132,11 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new);
|
|||
int vt_waitactive(int n);
|
||||
void change_console(struct vc_data *new_vc);
|
||||
void reset_vc(struct vc_data *vc);
|
||||
extern int do_unbind_con_driver(const struct consw *csw, int first, int last,
|
||||
int do_unbind_con_driver(const struct consw *csw, int first, int last,
|
||||
int deflt);
|
||||
int vty_init(const struct file_operations *console_fops);
|
||||
|
||||
extern char vt_dont_switch;
|
||||
extern bool vt_dont_switch;
|
||||
extern int default_utf8;
|
||||
extern int global_cursor_default;
|
||||
|
||||
|
@ -146,7 +147,7 @@ struct vt_spawn_console {
|
|||
};
|
||||
extern struct vt_spawn_console vt_spawn_con;
|
||||
|
||||
extern int vt_move_to_console(unsigned int vt, int alloc);
|
||||
int vt_move_to_console(unsigned int vt, int alloc);
|
||||
|
||||
/* Interfaces for VC notification of character events (for accessibility etc) */
|
||||
|
||||
|
@ -155,35 +156,34 @@ struct vt_notifier_param {
|
|||
unsigned int c; /* Printed char */
|
||||
};
|
||||
|
||||
extern int register_vt_notifier(struct notifier_block *nb);
|
||||
extern int unregister_vt_notifier(struct notifier_block *nb);
|
||||
int register_vt_notifier(struct notifier_block *nb);
|
||||
int unregister_vt_notifier(struct notifier_block *nb);
|
||||
|
||||
extern void hide_boot_cursor(bool hide);
|
||||
void hide_boot_cursor(bool hide);
|
||||
|
||||
/* keyboard provided interfaces */
|
||||
extern int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm);
|
||||
extern int vt_do_kdskbmode(int console, unsigned int arg);
|
||||
extern int vt_do_kdskbmeta(int console, unsigned int arg);
|
||||
extern int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
|
||||
int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm);
|
||||
int vt_do_kdskbmode(int console, unsigned int arg);
|
||||
int vt_do_kdskbmeta(int console, unsigned int arg);
|
||||
int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
|
||||
int perm);
|
||||
extern int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe,
|
||||
int perm, int console);
|
||||
extern int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb,
|
||||
int perm);
|
||||
extern int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm);
|
||||
extern int vt_do_kdgkbmode(int console);
|
||||
extern int vt_do_kdgkbmeta(int console);
|
||||
extern void vt_reset_unicode(int console);
|
||||
extern int vt_get_shift_state(void);
|
||||
extern void vt_reset_keyboard(int console);
|
||||
extern int vt_get_leds(int console, int flag);
|
||||
extern int vt_get_kbd_mode_bit(int console, int bit);
|
||||
extern void vt_set_kbd_mode_bit(int console, int bit);
|
||||
extern void vt_clr_kbd_mode_bit(int console, int bit);
|
||||
extern void vt_set_led_state(int console, int leds);
|
||||
extern void vt_set_led_state(int console, int leds);
|
||||
extern void vt_kbd_con_start(int console);
|
||||
extern void vt_kbd_con_stop(int console);
|
||||
int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
|
||||
int console);
|
||||
int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm);
|
||||
int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm);
|
||||
int vt_do_kdgkbmode(int console);
|
||||
int vt_do_kdgkbmeta(int console);
|
||||
void vt_reset_unicode(int console);
|
||||
int vt_get_shift_state(void);
|
||||
void vt_reset_keyboard(int console);
|
||||
int vt_get_leds(int console, int flag);
|
||||
int vt_get_kbd_mode_bit(int console, int bit);
|
||||
void vt_set_kbd_mode_bit(int console, int bit);
|
||||
void vt_clr_kbd_mode_bit(int console, int bit);
|
||||
void vt_set_led_state(int console, int leds);
|
||||
void vt_set_led_state(int console, int leds);
|
||||
void vt_kbd_con_start(int console);
|
||||
void vt_kbd_con_stop(int console);
|
||||
|
||||
void vc_scrolldelta_helper(struct vc_data *c, int lines,
|
||||
unsigned int rolled_over, void *_base, unsigned int size);
|
||||
|
|
|
@ -1772,9 +1772,6 @@ static void call_console_drivers(const char *ext_text, size_t ext_len,
|
|||
|
||||
trace_console_rcuidle(text, len);
|
||||
|
||||
if (!console_drivers)
|
||||
return;
|
||||
|
||||
for_each_console(con) {
|
||||
if (exclusive_console && con != exclusive_console)
|
||||
continue;
|
||||
|
@ -2653,19 +2650,17 @@ void register_console(struct console *newcon)
|
|||
struct console_cmdline *c;
|
||||
static bool has_preferred;
|
||||
|
||||
if (console_drivers)
|
||||
for_each_console(bcon)
|
||||
if (WARN(bcon == newcon,
|
||||
"console '%s%d' already registered\n",
|
||||
for_each_console(bcon) {
|
||||
if (WARN(bcon == newcon, "console '%s%d' already registered\n",
|
||||
bcon->name, bcon->index))
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* before we register a new CON_BOOT console, make sure we don't
|
||||
* already have a valid console
|
||||
*/
|
||||
if (console_drivers && newcon->flags & CON_BOOT) {
|
||||
/* find the last or real console */
|
||||
if (newcon->flags & CON_BOOT) {
|
||||
for_each_console(bcon) {
|
||||
if (!(bcon->flags & CON_BOOT)) {
|
||||
pr_info("Too late to register bootconsole %s%d\n",
|
||||
|
@ -2813,7 +2808,7 @@ EXPORT_SYMBOL(register_console);
|
|||
|
||||
int unregister_console(struct console *console)
|
||||
{
|
||||
struct console *a, *b;
|
||||
struct console *con;
|
||||
int res;
|
||||
|
||||
pr_info("%sconsole [%s%d] disabled\n",
|
||||
|
@ -2821,26 +2816,30 @@ int unregister_console(struct console *console)
|
|||
console->name, console->index);
|
||||
|
||||
res = _braille_unregister_console(console);
|
||||
if (res)
|
||||
if (res < 0)
|
||||
return res;
|
||||
if (res > 0)
|
||||
return 0;
|
||||
|
||||
res = 1;
|
||||
res = -ENODEV;
|
||||
console_lock();
|
||||
if (console_drivers == console) {
|
||||
console_drivers=console->next;
|
||||
res = 0;
|
||||
} else if (console_drivers) {
|
||||
for (a=console_drivers->next, b=console_drivers ;
|
||||
a; b=a, a=b->next) {
|
||||
if (a == console) {
|
||||
b->next = a->next;
|
||||
} else {
|
||||
for_each_console(con) {
|
||||
if (con->next == console) {
|
||||
con->next = console->next;
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!res && (console->flags & CON_EXTENDED))
|
||||
if (res)
|
||||
goto out_disable_unlock;
|
||||
|
||||
if (console->flags & CON_EXTENDED)
|
||||
nr_ext_console_drivers--;
|
||||
|
||||
/*
|
||||
|
@ -2853,6 +2852,16 @@ int unregister_console(struct console *console)
|
|||
console->flags &= ~CON_ENABLED;
|
||||
console_unlock();
|
||||
console_sysfs_notify();
|
||||
|
||||
if (console->exit)
|
||||
res = console->exit(console);
|
||||
|
||||
return res;
|
||||
|
||||
out_disable_unlock:
|
||||
console->flags &= ~CON_ENABLED;
|
||||
console_unlock();
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_console);
|
||||
|
|
|
@ -229,25 +229,8 @@ static int proc_dopipe_max_size(struct ctl_table *table, int write,
|
|||
void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
/* Note: sysrq code uses its own private copy */
|
||||
static int __sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE;
|
||||
|
||||
static int sysrq_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = proc_dointvec(table, write, buffer, lenp, ppos);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (write)
|
||||
sysrq_toggle_support(__sysrq_enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||
#endif
|
||||
|
||||
static struct ctl_table kern_table[];
|
||||
|
@ -747,7 +730,7 @@ static struct ctl_table kern_table[] = {
|
|||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
{
|
||||
.procname = "sysrq",
|
||||
.data = &__sysrq_enabled,
|
||||
.data = NULL,
|
||||
.maxlen = sizeof (int),
|
||||
.mode = 0644,
|
||||
.proc_handler = sysrq_sysctl_handler,
|
||||
|
@ -2835,6 +2818,26 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
static int sysrq_sysctl_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int tmp, ret;
|
||||
|
||||
tmp = sysrq_mask();
|
||||
|
||||
ret = __do_proc_dointvec(&tmp, table, write, buffer,
|
||||
lenp, ppos, NULL, NULL);
|
||||
if (ret || !write)
|
||||
return ret;
|
||||
|
||||
if (write)
|
||||
sysrq_toggle_support(tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
|
||||
void __user *buffer,
|
||||
size_t *lenp, loff_t *ppos,
|
||||
|
|
|
@ -430,6 +430,16 @@ config MAGIC_SYSRQ_SERIAL
|
|||
This option allows you to decide whether you want to enable the
|
||||
magic SysRq key.
|
||||
|
||||
config MAGIC_SYSRQ_SERIAL_SEQUENCE
|
||||
string "Char sequence that enables magic SysRq over serial"
|
||||
depends on MAGIC_SYSRQ_SERIAL
|
||||
default ""
|
||||
help
|
||||
Specifies a sequence of characters that can follow BREAK to enable
|
||||
SysRq on a serial console.
|
||||
|
||||
If unsure, leave an empty string and the option will not be enabled.
|
||||
|
||||
config DEBUG_FS
|
||||
bool "Debug Filesystem"
|
||||
help
|
||||
|
|
Loading…
Reference in New Issue