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:
Linus Torvalds 2020-03-31 16:18:55 -07:00
commit 1f944f976d
80 changed files with 2548 additions and 1789 deletions

View File

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

View File

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

View File

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

View File

@ -19,176 +19,174 @@
device_type = "memory";
reg = <0x00000000 0x08000000>;
};
};
soc {
apb@d4000000 {
uart3: uart@d4018000 {
status = "okay";
};
twsi1: i2c@d4011000 {
status = "okay";
pmic: max8925@3c {
compatible = "maxium,max8925";
reg = <0x3c>;
interrupts = <1>;
interrupt-parent = <&intcmux4>;
interrupt-controller;
#interrupt-cells = <1>;
maxim,tsc-irq = <0>;
&uart3 {
status = "okay";
};
regulators {
SDV1 {
regulator-min-microvolt = <637500>;
regulator-max-microvolt = <1425000>;
regulator-boot-on;
regulator-always-on;
};
SDV2 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2225000>;
regulator-boot-on;
regulator-always-on;
};
SDV3 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO1 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO2 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO3 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO4 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO5 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO6 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO7 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO8 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO9 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO10 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
};
LDO11 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO12 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO13 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO14 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO15 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO16 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO17 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO18 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO19 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO20 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
};
backlight {
maxim,max8925-dual-string = <0>;
};
charger {
batt-detect = <0>;
topoff-threshold = <1>;
fast-charge = <7>;
no-temp-support = <0>;
no-insert-detect = <0>;
};
};
&twsi1 {
status = "okay";
pmic: max8925@3c {
compatible = "maxium,max8925";
reg = <0x3c>;
interrupts = <1>;
interrupt-parent = <&intcmux4>;
interrupt-controller;
#interrupt-cells = <1>;
maxim,tsc-irq = <0>;
regulators {
SDV1 {
regulator-min-microvolt = <637500>;
regulator-max-microvolt = <1425000>;
regulator-boot-on;
regulator-always-on;
};
rtc: rtc@d4010000 {
status = "okay";
SDV2 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2225000>;
regulator-boot-on;
regulator-always-on;
};
SDV3 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO1 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO2 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO3 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO4 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO5 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO6 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO7 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO8 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO9 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO10 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
};
LDO11 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO12 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO13 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO14 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO15 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO16 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO17 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO18 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO19 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO20 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
};
backlight {
maxim,max8925-dual-string = <0>;
};
charger {
batt-detect = <0>;
topoff-threshold = <1>;
fast-charge = <7>;
no-temp-support = <0>;
no-insert-detect = <0>;
};
};
};
&rtc {
status = "okay";
};

View File

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

View File

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

View File

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

View File

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

View File

@ -18,155 +18,153 @@
memory {
reg = <0x00000000 0x10000000>;
};
};
soc {
apb@d4000000 {
uart1: uart@d4017000 {
status = "okay";
&uart1 {
status = "okay";
};
&twsi1 {
status = "okay";
pmic: 88pm860x@34 {
compatible = "marvell,88pm860x";
reg = <0x34>;
interrupts = <4>;
interrupt-parent = <&intc>;
interrupt-controller;
#interrupt-cells = <1>;
marvell,88pm860x-irq-read-clr;
marvell,88pm860x-slave-addr = <0x11>;
regulators {
BUCK1 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
twsi1: i2c@d4011000 {
status = "okay";
pmic: 88pm860x@34 {
compatible = "marvell,88pm860x";
reg = <0x34>;
interrupts = <4>;
interrupt-parent = <&intc>;
interrupt-controller;
#interrupt-cells = <1>;
marvell,88pm860x-irq-read-clr;
marvell,88pm860x-slave-addr = <0x11>;
regulators {
BUCK1 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
BUCK2 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
BUCK3 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <3000000>;
regulator-boot-on;
regulator-always-on;
};
LDO1 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <2800000>;
regulator-boot-on;
regulator-always-on;
};
LDO2 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO3 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO4 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO5 {
regulator-min-microvolt = <2900000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO6 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO7 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2900000>;
regulator-boot-on;
regulator-always-on;
};
LDO8 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2900000>;
regulator-boot-on;
regulator-always-on;
};
LDO9 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO10 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO12 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO13 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO14 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
};
rtc {
marvell,88pm860x-vrtc = <1>;
};
touch {
marvell,88pm860x-gpadc-prebias = <1>;
marvell,88pm860x-gpadc-slot-cycle = <1>;
marvell,88pm860x-tsi-prebias = <6>;
marvell,88pm860x-pen-prebias = <16>;
marvell,88pm860x-pen-prechg = <2>;
marvell,88pm860x-resistor-X = <300>;
};
backlights {
backlight-0 {
marvell,88pm860x-iset = <4>;
marvell,88pm860x-pwm = <3>;
};
backlight-2 {
};
};
leds {
led0-red {
marvell,88pm860x-iset = <12>;
};
led0-green {
marvell,88pm860x-iset = <12>;
};
led0-blue {
marvell,88pm860x-iset = <12>;
};
};
};
BUCK2 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
rtc: rtc@d4010000 {
status = "okay";
BUCK3 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <3000000>;
regulator-boot-on;
regulator-always-on;
};
LDO1 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <2800000>;
regulator-boot-on;
regulator-always-on;
};
LDO2 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO3 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO4 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO5 {
regulator-min-microvolt = <2900000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO6 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO7 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2900000>;
regulator-boot-on;
regulator-always-on;
};
LDO8 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2900000>;
regulator-boot-on;
regulator-always-on;
};
LDO9 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO10 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO12 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO13 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO14 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
};
rtc {
marvell,88pm860x-vrtc = <1>;
};
touch {
marvell,88pm860x-gpadc-prebias = <1>;
marvell,88pm860x-gpadc-slot-cycle = <1>;
marvell,88pm860x-tsi-prebias = <6>;
marvell,88pm860x-pen-prebias = <16>;
marvell,88pm860x-pen-prechg = <2>;
marvell,88pm860x-resistor-X = <300>;
};
backlights {
backlight-0 {
marvell,88pm860x-iset = <4>;
marvell,88pm860x-pwm = <3>;
};
backlight-2 {
};
};
leds {
led0-red {
marvell,88pm860x-iset = <12>;
};
led0-green {
marvell,88pm860x-iset = <12>;
};
led0-blue {
marvell,88pm860x-iset = <12>;
};
};
};
};
&rtc {
status = "okay";
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* hvc_console.h
* Copyright (C) 2005 IBM Corporation

View File

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

View File

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

View File

@ -18,7 +18,7 @@
* All HDLC data is frame oriented which means:
*
* 1. tty write calls represent one complete transmit frame of data
* The device driver should accept the complete frame or none of
* The device driver should accept the complete frame or none of
* the frame (busy) in the write method. Each write call should have
* a byte count in the range of 2-65535 bytes (2 is min HDLC frame
* with 1 addr byte and 1 ctrl byte). The max byte count of 65535
@ -39,7 +39,7 @@
* tty read calls.
*
* 3. tty read calls returns an entire frame of data or nothing.
*
*
* 4. all send and receive data is considered raw. No processing
* or translation is performed by the line discipline, regardless
* of the tty flags
@ -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>
@ -107,7 +104,7 @@
/*
* Buffers for individual HDLC frames
*/
#define MAX_HDLC_FRAME_SIZE 65535
#define MAX_HDLC_FRAME_SIZE 65535
#define DEFAULT_RX_BUF_COUNT 10
#define MAX_RX_BUF_COUNT 60
#define DEFAULT_TX_BUF_COUNT 3
@ -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) {
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() */
do {
buf = n_hdlc_buf_get(list);
kfree(buf);
} 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");
return;
}
#if defined(TTY_NO_WRITE_SPLIT)
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 (n_hdlc->magic != HDLC_MAGIC) {
pr_warn("n_hdlc: trying to close unopened tty!\n");
return;
}
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)n_hdlc_tty_close() success\n",__FILE__,__LINE__);
#if defined(TTY_NO_WRITE_SPLIT)
clear_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
#endif
tty->disc_data = NULL;
/* 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,44 +222,35 @@ 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;
pr_debug("%s() called (device=%s)\n", __func__, tty->name);
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)n_hdlc_tty_open() called (device=%s)\n",
__FILE__,__LINE__,
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,26 +268,22 @@ 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:
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
check_again:
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
if (n_hdlc->tbusy) {
n_hdlc->woke_up = 1;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
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);
actual = tty->ops->write(tty, tbuf->buf, tbuf->count);
@ -409,24 +297,20 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
/* pretending it was accepted by driver */
if (actual < 0)
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);
/* wait up sleeping writers */
wake_up_interruptible(&tty->write_wait);
/* 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,
@ -436,21 +320,17 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
break;
}
}
if (!tbuf)
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
/* Clear the re-entry flag */
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
n_hdlc->tbusy = 0;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
if (n_hdlc->woke_up)
goto check_again;
n_hdlc->tbusy = false;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)n_hdlc_send_frames() exit\n",__FILE__,__LINE__);
if (n_hdlc->woke_up)
goto check_again;
} /* 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);
struct n_hdlc *n_hdlc = tty->disc_data;
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);
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__);
return;
}
if ( count>maxframe ) {
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d) rx count>maxframesize, data discarded\n",
__FILE__,__LINE__);
pr_err("line not using HDLC discipline\n");
return;
}
/* get a free HDLC buffer */
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() */
@ -553,28 +412,21 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data,
* @file - pointer to open file object
* @buf - pointer to returned data buffer
* @nr - size of returned data buffer
*
*
* Returns the number of bytes returned or error code.
*/
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;
}
@ -610,7 +462,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, rbuf);
break;
}
/* no data */
if (tty_io_nonblock(tty, file)) {
ret = -EAGAIN;
@ -629,7 +481,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
__set_current_state(TASK_RUNNING);
return ret;
} /* end of n_hdlc_tty_read() */
/**
@ -638,43 +490,34 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
* @file - pointer to file object data
* @data - pointer to transmit data (one frame)
* @count - size of transmit frame in bytes
*
*
* Returns the number of bytes written (or error code).
*/
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;
}
add_wait_queue(&tty->write_wait, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list);
if (tbuf)
break;
@ -684,15 +527,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
break;
}
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;
@ -702,18 +537,18 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
if (!error) {
if (!error) {
/* Retrieve the user's buffer */
memcpy(tbuf->buf, data, count);
/* 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;
} /* end of n_hdlc_tty_write() */
/**
@ -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;
@ -783,7 +616,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
break;
}
return error;
} /* end of n_hdlc_tty_ioctl() */
/**
@ -791,7 +624,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
* @tty - pointer to tty instance data
* @filp - pointer to open file object for device
* @poll_table - wait queue for operations
*
*
* Determine which operations (read/write) will not block and return info
* to caller.
* Returns a bit mask containing info on which ops will not block.
@ -799,33 +632,50 @@ 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;
poll_wait(filp, &tty->read_wait, wait);
poll_wait(filp, &tty->write_wait, wait);
/*
* 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);
/* set bits for operations that won't block */
if (!list_empty(&n_hdlc->rx_buf_list.list))
mask |= EPOLLIN | EPOLLRDNORM; /* readable */
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= EPOLLHUP;
if (tty_hung_up_p(filp))
mask |= EPOLLHUP;
if (!tty_is_writelocked(tty) &&
!list_empty(&n_hdlc->tx_free_buf_list.list))
mask |= EPOLLOUT | EPOLLWRNORM; /* writable */
/* set bits for operations that won't block */
if (!list_empty(&n_hdlc->rx_buf_list.list))
mask |= EPOLLIN | EPOLLRDNORM; /* readable */
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= EPOLLHUP;
if (tty_hung_up_p(filp))
mask |= EPOLLHUP;
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,30 +698,14 @@ 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;
} /* end of n_hdlc_alloc() */
/**
@ -915,7 +747,7 @@ static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
/**
* n_hdlc_buf_get - remove and return an HDLC buffer from list
* @buf_list - pointer to HDLC buffer list
*
*
* Remove and return an HDLC buffer from the head of the specified HDLC buffer
* list.
* Returns a pointer to HDLC buffer if available, otherwise %NULL.
@ -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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
clk_disable_unprepare(data->bus_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,16 +410,16 @@ 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;
}
err = clk_prepare_enable(data->bus_clk);
if (err) {
dev_warn(dev, "Can't enable bus 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)
pm_runtime_get_sync(port->dev);
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;
}

View File

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

View File

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

View File

@ -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[] = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -260,15 +260,6 @@ config SERIAL_SAMSUNG_UARTS
help
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"
@ -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.

View File

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

View File

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

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* include/linux/atmel_serial.h
*

View File

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

View File

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

View File

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

View File

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

View File

@ -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,39 +1510,77 @@ 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 && !lpuart_dma_tx_request(&sport->port)) {
init_waitqueue_head(&sport->dma_wait);
sport->lpuart_dma_tx_use = true;
if (lpuart_is_32(sport)) {
uartbaud = lpuart32_read(&sport->port, UARTBAUD);
lpuart32_write(&sport->port,
uartbaud | UARTBAUD_TDMAE, UARTBAUD);
} else {
writeb(readb(sport->port.membase + UARTCR5) |
UARTCR5_TDMAS, sport->port.membase + UARTCR5);
}
if (!sport->dma_tx_chan)
goto err;
ret = lpuart_dma_tx_request(&sport->port);
if (ret)
goto err;
init_waitqueue_head(&sport->dma_wait);
sport->lpuart_dma_tx_use = true;
if (lpuart_is_32(sport)) {
uartbaud = lpuart32_read(&sport->port, UARTBAUD);
lpuart32_write(&sport->port,
uartbaud | UARTBAUD_TDMAE, UARTBAUD);
} else {
sport->lpuart_dma_tx_use = false;
writeb(readb(sport->port.membase + UARTCR5) |
UARTCR5_TDMAS, sport->port.membase + UARTCR5);
}
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)) {
/* set Rx DMA timeout */
sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
if (!sport->dma_rx_timeout)
sport->dma_rx_timeout = 1;
int ret;
sport->lpuart_dma_rx_use = true;
rx_dma_timer_init(sport);
} else {
sport->lpuart_dma_rx_use = false;
}
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)
sport->dma_rx_timeout = 1;
sport->lpuart_dma_rx_use = true;
rx_dma_timer_init(sport);
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:

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* icom.h
*

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/************************************************************************
* Copyright 2003 Digi International (www.digi.com)
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* PIC32 Integrated Serial Driver.
*

View File

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

View File

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

View File

@ -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,9 +3202,13 @@ int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
return 0;
if (ch && time_before(jiffies, port->sysrq)) {
handle_sysrq(ch);
port->sysrq = 0;
return 1;
if (sysrq_mask()) {
handle_sysrq(ch);
port->sysrq = 0;
return 1;
}
if (uart_try_toggle_sysrq(port, ch))
return 1;
}
port->sysrq = 0;
@ -3110,9 +3225,13 @@ int uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch)
return 0;
if (ch && time_before(jiffies, port->sysrq)) {
port->sysrq_ch = ch;
port->sysrq = 0;
return 1;
if (sysrq_mask()) {
port->sysrq_ch = ch;
port->sysrq = 0;
return 1;
}
if (uart_try_toggle_sysrq(port, ch))
return 1;
}
port->sysrq = 0;
@ -3120,22 +3239,19 @@ int uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch)
}
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;
port->sysrq_ch = 0;
spin_unlock_irqrestore(&port->lock, flags);
if (sysrq_ch)
handle_sysrq(sysrq_ch);
} else {
spin_unlock_irqrestore(&port->lock, flags);
}
sysrq_ch = port->sysrq_ch;
port->sysrq_ch = 0;
spin_unlock_irqrestore(&port->lock, irqflags);
if (sysrq_ch)
handle_sysrq(sysrq_ch);
}
EXPORT_SYMBOL_GPL(uart_unlock_and_check_sysrq);
@ -3149,14 +3265,12 @@ 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->sysrq) {
port->sysrq = jiffies + HZ*5;
return 1;
}
port->sysrq = 0;
if (port->has_sysrq && uart_console(port)) {
if (!port->sysrq) {
port->sysrq = jiffies + SYSRQ_TIMEOUT;
return 1;
}
port->sysrq = 0;
}
if (port->flags & UPF_SAK)

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Helpers for controlling modem lines via GPIO
*

View File

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

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Drivers for CSR SiRFprimaII onboard UARTs.
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,57 +216,51 @@ 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)
{
case TIOCL_SELCHAR: /* character-by-character selection */
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, 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, 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;
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))))
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))))
break;
new_sel_end = pe;
if (!((pe + 2) % vc->vc_size_row))
break;
}
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;
break;
case TIOCL_SELPOINTER:
highlight_pointer(pe);
return 0;
default:
return -EINVAL;
if (!((pe + 2) % vc->vc_size_row))
break;
}
break;
case TIOCL_SELLINE: /* line-by-line selection */
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);
return 0;
default:
return -EINVAL;
}
/* remove the pointer */
@ -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);

View File

@ -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,67 +1914,65 @@ 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]) {
case 1: /* set color for underline mode */
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) {
vc->vc_halfcolor = color_table[vc->vc_par[1]];
if (vc->vc_intensity == 0)
update_attr(vc);
}
break;
case 8: /* store colors as defaults */
vc->vc_def_color = vc->vc_attr;
if (vc->vc_hi_font_mask == 0x100)
vc->vc_def_color >>= 1;
default_attr(vc);
update_attr(vc);
break;
case 9: /* set blanking interval */
blankinterval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60;
poke_blanked_console();
break;
case 10: /* set bell frequency in Hz */
if (vc->vc_npar >= 1)
vc->vc_bell_pitch = vc->vc_par[1];
else
vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
break;
case 11: /* set bell duration in msec */
if (vc->vc_npar >= 1)
vc->vc_bell_duration = (vc->vc_par[1] < 2000) ?
msecs_to_jiffies(vc->vc_par[1]) : 0;
else
vc->vc_bell_duration = DEFAULT_BELL_DURATION;
break;
case 12: /* bring specified console to the front */
if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1))
set_console(vc->vc_par[1] - 1);
break;
case 13: /* unblank the screen */
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;
break;
case 15: /* activate the previous console */
set_console(last_console);
break;
case 16: /* set cursor blink duration in msec */
if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 &&
vc->vc_par[1] <= USHRT_MAX)
vc->vc_cur_blink_ms = vc->vc_par[1];
else
vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
break;
switch (vc->vc_par[0]) {
case 1: /* set color for underline mode */
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) {
vc->vc_halfcolor = color_table[vc->vc_par[1]];
if (vc->vc_intensity == 0)
update_attr(vc);
}
break;
case 8: /* store colors as defaults */
vc->vc_def_color = vc->vc_attr;
if (vc->vc_hi_font_mask == 0x100)
vc->vc_def_color >>= 1;
default_attr(vc);
update_attr(vc);
break;
case 9: /* set blanking interval */
blankinterval = min(vc->vc_par[1], 60U) * 60;
poke_blanked_console();
break;
case 10: /* set bell frequency in Hz */
if (vc->vc_npar >= 1)
vc->vc_bell_pitch = vc->vc_par[1];
else
vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
break;
case 11: /* set bell duration in msec */
if (vc->vc_npar >= 1)
vc->vc_bell_duration = (vc->vc_par[1] < 2000) ?
msecs_to_jiffies(vc->vc_par[1]) : 0;
else
vc->vc_bell_duration = DEFAULT_BELL_DURATION;
break;
case 12: /* bring specified console to the front */
if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1))
set_console(vc->vc_par[1] - 1);
break;
case 13: /* unblank the screen */
poke_blanked_console();
break;
case 14: /* set vesa powerdown interval */
vesa_off_interval = min(vc->vc_par[1], 60U) * 60 * HZ;
break;
case 15: /* activate the previous console */
set_console(last_console);
break;
case 16: /* set cursor blink duration in msec */
if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 &&
vc->vc_par[1] <= USHRT_MAX)
vc->vc_cur_blink_ms = vc->vc_par[1];
else
vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
break;
}
}
@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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",
bcon->name, bcon->index))
return;
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);

View File

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

View File

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