diff options
Diffstat (limited to '')
104 files changed, 2908 insertions, 1283 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 276a793168a6..8faec13961cd 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1172,10 +1172,10 @@ specified, the serial port must already be setup and configured. - uart[8250],io,<addr>[,options] - uart[8250],mmio,<addr>[,options] - uart[8250],mmio32,<addr>[,options] - uart[8250],mmio32be,<addr>[,options] + uart[8250],io,<addr>[,options[,uartclk]] + uart[8250],mmio,<addr>[,options[,uartclk]] + uart[8250],mmio32,<addr>[,options[,uartclk]] + uart[8250],mmio32be,<addr>[,options[,uartclk]] uart[8250],0x<addr>[,options] Start an early, polled-mode console on the 8250/16550 UART at the specified I/O port or MMIO address. @@ -1184,7 +1184,9 @@ If none of [io|mmio|mmio32|mmio32be], <addr> is assumed to be equivalent to 'mmio'. 'options' are specified in the same format described for "console=ttyS<n>"; if - unspecified, the h/w is not initialized. + unspecified, the h/w is not initialized. 'uartclk' is + the uart clock frequency; if unspecified, it is set + to 'BASE_BAUD' * 16. pl011,<addr> pl011,mmio32,<addr> diff --git a/Documentation/devicetree/bindings/serial/8250.yaml b/Documentation/devicetree/bindings/serial/8250.yaml index 34b8e59aa9d4..c55865fd371a 100644 --- a/Documentation/devicetree/bindings/serial/8250.yaml +++ b/Documentation/devicetree/bindings/serial/8250.yaml @@ -62,7 +62,6 @@ properties: - const: mrvl,pxa-uart - const: nuvoton,wpcm450-uart - const: nuvoton,npcm750-uart - - const: nuvoton,npcm845-uart - const: nvidia,tegra20-uart - const: nxp,lpc3220-uart - items: @@ -94,6 +93,10 @@ properties: - ns16550a - items: - enum: + - nuvoton,npcm845-uart + - const: nuvoton,npcm750-uart + - items: + - enum: - ralink,mt7620a-uart - ralink,rt3052-uart - ralink,rt3883-uart @@ -200,12 +203,13 @@ properties: deprecated: true aspeed,lpc-io-reg: - $ref: '/schemas/types.yaml#/definitions/uint32' + $ref: /schemas/types.yaml#/definitions/uint32-array + maxItems: 1 description: | The VUART LPC address. Only applicable to aspeed,ast2500-vuart. aspeed,lpc-interrupts: - $ref: "/schemas/types.yaml#/definitions/uint32-array" + $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 2 maxItems: 2 description: | diff --git a/Documentation/devicetree/bindings/serial/8250_omap.yaml b/Documentation/devicetree/bindings/serial/8250_omap.yaml index 53dc1212ad2e..eb3488d8f9ee 100644 --- a/Documentation/devicetree/bindings/serial/8250_omap.yaml +++ b/Documentation/devicetree/bindings/serial/8250_omap.yaml @@ -70,11 +70,6 @@ properties: dsr-gpios: true rng-gpios: true dcd-gpios: true - rs485-rts-delay: true - rs485-rts-active-low: true - rs485-rx-during-tx: true - rs485-rts-active-high: true - linux,rs485-enabled-at-boot-time: true rts-gpio: true power-domains: true clock-frequency: true @@ -109,12 +104,12 @@ else: examples: - | - serial@49042000 { - compatible = "ti,omap3-uart"; - reg = <0x49042000 0x400>; - interrupts = <80>; - dmas = <&sdma 81 &sdma 82>; - dma-names = "tx", "rx"; - ti,hwmods = "uart4"; - clock-frequency = <48000000>; - }; + serial@49042000 { + compatible = "ti,omap3-uart"; + reg = <0x49042000 0x400>; + interrupts = <80>; + dmas = <&sdma 81 &sdma 82>; + dma-names = "tx", "rx"; + ti,hwmods = "uart4"; + clock-frequency = <48000000>; + }; diff --git a/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml b/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml index 7822705ad16c..3cbdde85ed71 100644 --- a/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml +++ b/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml @@ -19,6 +19,9 @@ description: | is active since power-on and does not need any clock gating and is usable as very early serial console. +allOf: + - $ref: serial.yaml# + properties: compatible: oneOf: @@ -69,14 +72,14 @@ required: - clocks - clock-names -additionalProperties: false +unevaluatedProperties: false examples: - | serial@84c0 { - compatible = "amlogic,meson-gx-uart"; - reg = <0x84c0 0x14>; - interrupts = <26>; - clocks = <&xtal>, <&pclk>, <&xtal>; - clock-names = "xtal", "pclk", "baud"; + compatible = "amlogic,meson-gx-uart"; + reg = <0x84c0 0x14>; + interrupts = <26>; + clocks = <&xtal>, <&pclk>, <&xtal>; + clock-names = "xtal", "pclk", "baud"; }; diff --git a/Documentation/devicetree/bindings/serial/cdns,uart.yaml b/Documentation/devicetree/bindings/serial/cdns,uart.yaml index 876b8cf1cafb..a8b323d7bf94 100644 --- a/Documentation/devicetree/bindings/serial/cdns,uart.yaml +++ b/Documentation/devicetree/bindings/serial/cdns,uart.yaml @@ -9,9 +9,6 @@ title: Cadence UART Controller maintainers: - Michal Simek <michal.simek@xilinx.com> -allOf: - - $ref: /schemas/serial.yaml# - properties: compatible: oneOf: @@ -46,6 +43,9 @@ properties: port does not use this pin. type: boolean + power-domains: + maxItems: 1 + required: - compatible - reg @@ -53,14 +53,25 @@ required: - clocks - clock-names +allOf: + - $ref: serial.yaml# + - if: + properties: + compatible: + contains: + const: cdns,uart-r1p8 + then: + properties: + power-domains: false + unevaluatedProperties: false examples: - | uart0: serial@e0000000 { - compatible = "xlnx,xuartps", "cdns,uart-r1p8"; - clocks = <&clkc 23>, <&clkc 40>; - clock-names = "uart_clk", "pclk"; - reg = <0xE0000000 0x1000>; - interrupts = <0 27 4>; + compatible = "xlnx,xuartps", "cdns,uart-r1p8"; + clocks = <&clkc 23>, <&clkc 40>; + clock-names = "uart_clk", "pclk"; + reg = <0xe0000000 0x1000>; + interrupts = <0 27 4>; }; diff --git a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml index 8b643bae3c7b..920539926d7e 100644 --- a/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl,s32-linflexuart.yaml @@ -16,7 +16,7 @@ maintainers: - Chester Lin <clin@suse.com> allOf: - - $ref: "serial.yaml" + - $ref: serial.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/serial/fsl-imx-uart.yaml b/Documentation/devicetree/bindings/serial/fsl-imx-uart.yaml index 9d949296a142..4cbe76e1715b 100644 --- a/Documentation/devicetree/bindings/serial/fsl-imx-uart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl-imx-uart.yaml @@ -10,8 +10,8 @@ maintainers: - Fabio Estevam <festevam@gmail.com> allOf: - - $ref: "serial.yaml" - - $ref: "rs485.yaml" + - $ref: serial.yaml# + - $ref: rs485.yaml# properties: compatible: @@ -83,13 +83,6 @@ properties: are sensible for most use cases. If you need low latency processing on slow connections this needs to be configured appropriately. - uart-has-rtscts: true - - rs485-rts-delay: true - rs485-rts-active-low: true - rs485-rx-during-tx: true - linux,rs485-enabled-at-boot-time: true - required: - compatible - reg diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.yaml b/Documentation/devicetree/bindings/serial/fsl-lpuart.yaml index 74f75f669e77..ab81722293d3 100644 --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.yaml @@ -10,7 +10,8 @@ maintainers: - Fugang Duan <fugang.duan@nxp.com> allOf: - - $ref: "rs485.yaml" + - $ref: rs485.yaml# + - $ref: serial.yaml# properties: compatible: @@ -64,9 +65,6 @@ properties: - const: rx - const: tx - rs485-rts-active-low: true - linux,rs485-enabled-at-boot-time: true - required: - compatible - reg diff --git a/Documentation/devicetree/bindings/serial/fsl-mxs-auart.yaml b/Documentation/devicetree/bindings/serial/fsl-mxs-auart.yaml index 14c7594c88c6..6a400a5e6fc7 100644 --- a/Documentation/devicetree/bindings/serial/fsl-mxs-auart.yaml +++ b/Documentation/devicetree/bindings/serial/fsl-mxs-auart.yaml @@ -10,7 +10,7 @@ maintainers: - Fabio Estevam <festevam@gmail.com> allOf: - - $ref: "serial.yaml" + - $ref: serial.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/serial/pl011.yaml b/Documentation/devicetree/bindings/serial/pl011.yaml index 80af72859876..9571041030b7 100644 --- a/Documentation/devicetree/bindings/serial/pl011.yaml +++ b/Documentation/devicetree/bindings/serial/pl011.yaml @@ -10,6 +10,7 @@ maintainers: - Rob Herring <robh@kernel.org> allOf: + - $ref: /schemas/arm/primecell.yaml# - $ref: serial.yaml# # Need a custom select here or 'arm,primecell' will match on lots of nodes diff --git a/Documentation/devicetree/bindings/serial/qcom,msm-uart.txt b/Documentation/devicetree/bindings/serial/qcom,msm-uart.txt deleted file mode 100644 index ce8c90161959..000000000000 --- a/Documentation/devicetree/bindings/serial/qcom,msm-uart.txt +++ /dev/null @@ -1,25 +0,0 @@ -* MSM Serial UART - -The MSM serial UART hardware is designed for low-speed use cases where a -dma-engine isn't needed. From a software perspective it's mostly compatible -with the MSM serial UARTDM except that it only supports reading and writing one -character at a time. - -Required properties: -- compatible: Should contain "qcom,msm-uart" -- reg: Should contain UART register location and length. -- interrupts: Should contain UART interrupt. -- clocks: Should contain the core clock. -- clock-names: Should be "core". - -Example: - -A uart device at 0xa9c00000 with interrupt 11. - -serial@a9c00000 { - compatible = "qcom,msm-uart"; - reg = <0xa9c00000 0x1000>; - interrupts = <11>; - clocks = <&uart_cxc>; - clock-names = "core"; -}; diff --git a/Documentation/devicetree/bindings/serial/qcom,msm-uart.yaml b/Documentation/devicetree/bindings/serial/qcom,msm-uart.yaml new file mode 100644 index 000000000000..a052aaef21f4 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/qcom,msm-uart.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/qcom,msm-uart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM SoC Serial UART + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> + +description: + The MSM serial UART hardware is designed for low-speed use cases where a + dma-engine isn't needed. From a software perspective it's mostly compatible + with the MSM serial UARTDM except that it only supports reading and writing + one character at a time. + +properties: + compatible: + const: qcom,msm-uart + + clocks: + maxItems: 1 + + clock-names: + items: + - const: core + + interrupts: + maxItems: 1 + + reg: + maxItems: 1 + +required: + - compatible + - clock-names + - clocks + - interrupts + - reg + +unevaluatedProperties: false + +allOf: + - $ref: /schemas/serial/serial.yaml# + +examples: + - | + serial@a9c00000 { + compatible = "qcom,msm-uart"; + reg = <0xa9c00000 0x1000>; + interrupts = <11>; + clocks = <&uart_cxc>; + clock-names = "core"; + }; diff --git a/Documentation/devicetree/bindings/serial/renesas,em-uart.yaml b/Documentation/devicetree/bindings/serial/renesas,em-uart.yaml index b25aca733b72..12d0fa34f9f9 100644 --- a/Documentation/devicetree/bindings/serial/renesas,em-uart.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,em-uart.yaml @@ -66,9 +66,9 @@ examples: - | #include <dt-bindings/interrupt-controller/arm-gic.h> uart0: serial@e1020000 { - compatible = "renesas,em-uart"; - reg = <0xe1020000 0x38>; - interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&usia_u0_sclk>; - clock-names = "sclk"; + compatible = "renesas,em-uart"; + reg = <0xe1020000 0x38>; + interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&usia_u0_sclk>; + clock-names = "sclk"; }; diff --git a/Documentation/devicetree/bindings/serial/renesas,hscif.yaml b/Documentation/devicetree/bindings/serial/renesas,hscif.yaml index 1957b9d782e8..afedb6edfc34 100644 --- a/Documentation/devicetree/bindings/serial/renesas,hscif.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,hscif.yaml @@ -131,20 +131,20 @@ examples: #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/power/r8a7795-sysc.h> aliases { - serial1 = &hscif1; + serial1 = &hscif1; }; hscif1: serial@e6550000 { - compatible = "renesas,hscif-r8a7795", "renesas,rcar-gen3-hscif", - "renesas,hscif"; - reg = <0xe6550000 96>; - interrupts = <GIC_SPI 155 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&cpg CPG_MOD 519>, <&cpg CPG_CORE R8A7795_CLK_S3D1>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac1 0x33>, <&dmac1 0x32>, <&dmac2 0x33>, <&dmac2 0x32>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; - resets = <&cpg 519>; - uart-has-rtscts; + compatible = "renesas,hscif-r8a7795", "renesas,rcar-gen3-hscif", + "renesas,hscif"; + reg = <0xe6550000 96>; + interrupts = <GIC_SPI 155 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 519>, <&cpg CPG_CORE R8A7795_CLK_S3D1>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac1 0x33>, <&dmac1 0x32>, <&dmac2 0x33>, <&dmac2 0x32>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + resets = <&cpg 519>; + uart-has-rtscts; }; diff --git a/Documentation/devicetree/bindings/serial/renesas,sci.yaml b/Documentation/devicetree/bindings/serial/renesas,sci.yaml index bf7708a7a2c0..dc445b327e0b 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,sci.yaml @@ -91,19 +91,19 @@ examples: #include <dt-bindings/interrupt-controller/arm-gic.h> aliases { - serial0 = &sci0; + serial0 = &sci0; }; sci0: serial@1004d000 { - compatible = "renesas,r9a07g044-sci", "renesas,sci"; - reg = <0x1004d000 0x400>; - interrupts = <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "eri", "rxi", "txi", "tei"; - clocks = <&cpg CPG_MOD R9A07G044_SCI0_CLKP>; - clock-names = "fck"; - power-domains = <&cpg>; - resets = <&cpg R9A07G044_SCI0_RST>; + compatible = "renesas,r9a07g044-sci", "renesas,sci"; + reg = <0x1004d000 0x400>; + interrupts = <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "eri", "rxi", "txi", "tei"; + clocks = <&cpg CPG_MOD R9A07G044_SCI0_CLKP>; + clock-names = "fck"; + power-domains = <&cpg>; + resets = <&cpg R9A07G044_SCI0_RST>; }; diff --git a/Documentation/devicetree/bindings/serial/renesas,scif.yaml b/Documentation/devicetree/bindings/serial/renesas,scif.yaml index f81f2d67a1ed..1989bd67d04e 100644 --- a/Documentation/devicetree/bindings/serial/renesas,scif.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,scif.yaml @@ -180,19 +180,19 @@ examples: #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/power/r8a7791-sysc.h> aliases { - serial0 = &scif0; + serial0 = &scif0; }; scif0: serial@e6e60000 { - compatible = "renesas,scif-r8a7791", "renesas,rcar-gen2-scif", - "renesas,scif"; - reg = <0xe6e60000 64>; - interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7791_CLK_ZS>, - <&scif_clk>; - clock-names = "fck", "brg_int", "scif_clk"; - dmas = <&dmac0 0x29>, <&dmac0 0x2a>, <&dmac1 0x29>, <&dmac1 0x2a>; - dma-names = "tx", "rx", "tx", "rx"; - power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; - resets = <&cpg 721>; + compatible = "renesas,scif-r8a7791", "renesas,rcar-gen2-scif", + "renesas,scif"; + reg = <0xe6e60000 64>; + interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 721>, <&cpg CPG_CORE R8A7791_CLK_ZS>, + <&scif_clk>; + clock-names = "fck", "brg_int", "scif_clk"; + dmas = <&dmac0 0x29>, <&dmac0 0x2a>, <&dmac1 0x29>, <&dmac1 0x2a>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 721>; }; diff --git a/Documentation/devicetree/bindings/serial/renesas,scifa.yaml b/Documentation/devicetree/bindings/serial/renesas,scifa.yaml index 3c67d3202e1b..4c3b5e7270da 100644 --- a/Documentation/devicetree/bindings/serial/renesas,scifa.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,scifa.yaml @@ -95,18 +95,18 @@ examples: #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/power/r8a7790-sysc.h> aliases { - serial0 = &scifa0; + serial0 = &scifa0; }; scifa0: serial@e6c40000 { - compatible = "renesas,scifa-r8a7790", "renesas,rcar-gen2-scifa", - "renesas,scifa"; - reg = <0xe6c40000 64>; - interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&cpg CPG_MOD 204>; - clock-names = "fck"; - power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; - resets = <&cpg 204>; - dmas = <&dmac0 0x21>, <&dmac0 0x22>, <&dmac1 0x21>, <&dmac1 0x22>; - dma-names = "tx", "rx", "tx", "rx"; + compatible = "renesas,scifa-r8a7790", "renesas,rcar-gen2-scifa", + "renesas,scifa"; + reg = <0xe6c40000 64>; + interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 204>; + clock-names = "fck"; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 204>; + dmas = <&dmac0 0x21>, <&dmac0 0x22>, <&dmac1 0x21>, <&dmac1 0x22>; + dma-names = "tx", "rx", "tx", "rx"; }; diff --git a/Documentation/devicetree/bindings/serial/renesas,scifb.yaml b/Documentation/devicetree/bindings/serial/renesas,scifb.yaml index d5571c7a4424..2f7cbbb48960 100644 --- a/Documentation/devicetree/bindings/serial/renesas,scifb.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,scifb.yaml @@ -94,10 +94,10 @@ examples: #include <dt-bindings/clock/r8a7740-clock.h> #include <dt-bindings/interrupt-controller/arm-gic.h> scifb: serial@e6c30000 { - compatible = "renesas,scifb-r8a7740", "renesas,scifb"; - reg = <0xe6c30000 0x100>; - interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&mstp2_clks R8A7740_CLK_SCIFB>; - clock-names = "fck"; - power-domains = <&pd_a3sp>; + compatible = "renesas,scifb-r8a7740", "renesas,scifb"; + reg = <0xe6c30000 0x100>; + interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp2_clks R8A7740_CLK_SCIFB>; + clock-names = "fck"; + power-domains = <&pd_a3sp>; }; diff --git a/Documentation/devicetree/bindings/serial/rs485.yaml b/Documentation/devicetree/bindings/serial/rs485.yaml index 789763cf427a..303a443d9e29 100644 --- a/Documentation/devicetree/bindings/serial/rs485.yaml +++ b/Documentation/devicetree/bindings/serial/rs485.yaml @@ -51,6 +51,12 @@ properties: description: GPIO pin to enable RS485 bus termination. maxItems: 1 + rs485-rx-during-tx-gpios: + description: Output GPIO pin that sets the state of rs485-rx-during-tx. This + signal can be used to control the RX part of an RS485 transceiver. Thereby + the active state enables RX during TX. + maxItems: 1 + additionalProperties: true ... diff --git a/Documentation/devicetree/bindings/serial/serial.yaml b/Documentation/devicetree/bindings/serial/serial.yaml index 11e822bf09e2..e05ad3ac2abc 100644 --- a/Documentation/devicetree/bindings/serial/serial.yaml +++ b/Documentation/devicetree/bindings/serial/serial.yaml @@ -141,13 +141,13 @@ additionalProperties: true examples: - | serial@1234 { - compatible = "ns16550a"; - reg = <0x1234 0x20>; - interrupts = <1>; - - bluetooth { - compatible = "brcm,bcm4330-bt"; - interrupt-parent = <&gpio>; - interrupts = <10>; - }; + compatible = "ns16550a"; + reg = <0x1234 0x20>; + interrupts = <1>; + + bluetooth { + compatible = "brcm,bcm4330-bt"; + interrupt-parent = <&gpio>; + interrupts = <10>; + }; }; diff --git a/Documentation/devicetree/bindings/serial/sifive-serial.yaml b/Documentation/devicetree/bindings/serial/sifive-serial.yaml index b0a8871e3641..b0df1cac4968 100644 --- a/Documentation/devicetree/bindings/serial/sifive-serial.yaml +++ b/Documentation/devicetree/bindings/serial/sifive-serial.yaml @@ -53,13 +53,13 @@ unevaluatedProperties: false examples: - | - #include <dt-bindings/clock/sifive-fu540-prci.h> - serial@10010000 { + #include <dt-bindings/clock/sifive-fu540-prci.h> + serial@10010000 { compatible = "sifive,fu540-c000-uart", "sifive,uart0"; interrupt-parent = <&plic0>; interrupts = <80>; reg = <0x10010000 0x1000>; clocks = <&prci FU540_PRCI_CLK_TLCLK>; - }; + }; ... diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index b9c2287c5d1e..2becdfab4f15 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -67,6 +67,14 @@ properties: - const: baudclk - const: apb_pclk + dmas: + minItems: 2 + + dma-names: + items: + - const: rx + - const: tx + snps,uart-16550-compatible: description: reflects the value of UART_16550_COMPATIBLE configuration parameter. Define this if your UART does not implement the busy functionality. diff --git a/Documentation/devicetree/bindings/serial/st,stm32-uart.yaml b/Documentation/devicetree/bindings/serial/st,stm32-uart.yaml index 85876c668f6d..1df8ffe95fc6 100644 --- a/Documentation/devicetree/bindings/serial/st,stm32-uart.yaml +++ b/Documentation/devicetree/bindings/serial/st,stm32-uart.yaml @@ -35,8 +35,6 @@ properties: description: enable hardware flow control (deprecated) $ref: /schemas/types.yaml#/definitions/flag - uart-has-rtscts: true - rx-tx-swap: true dmas: @@ -60,11 +58,6 @@ properties: wakeup-source: true - rs485-rts-delay: true - rs485-rts-active-low: true - linux,rs485-enabled-at-boot-time: true - rs485-rx-during-tx: true - rx-threshold: description: If value is set to 1, RX FIFO threshold is disabled. diff --git a/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.yaml b/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.yaml index 2f4390e8d4e8..08dcb275d8e2 100644 --- a/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.yaml +++ b/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.yaml @@ -63,7 +63,7 @@ required: - xlnx,use-parity allOf: - - $ref: /schemas/serial.yaml# + - $ref: serial.yaml# - if: properties: xlnx,use-parity: @@ -76,7 +76,7 @@ unevaluatedProperties: false examples: - | - serial@800c0000 { + serial@800c0000 { compatible = "xlnx,xps-uartlite-1.00.a"; reg = <0x800c0000 0x10000>; interrupts = <0x0 0x6e 0x1>; @@ -84,5 +84,5 @@ examples: current-speed = <115200>; xlnx,data-bits = <8>; xlnx,use-parity = <0>; - }; + }; ... diff --git a/Documentation/driver-api/tty/n_gsm.rst b/Documentation/driver-api/tty/n_gsm.rst index 35d7381515b0..9447b8a3b8e2 100644 --- a/Documentation/driver-api/tty/n_gsm.rst +++ b/Documentation/driver-api/tty/n_gsm.rst @@ -25,6 +25,8 @@ Config Initiator #. Switch the serial line to using the n_gsm line discipline by using ``TIOCSETD`` ioctl. +#. Configure the mux using ``GSMIOC_GETCONF_EXT``/``GSMIOC_SETCONF_EXT`` ioctl if needed. + #. Configure the mux using ``GSMIOC_GETCONF``/``GSMIOC_SETCONF`` ioctl. #. Obtain base gsmtty number for the used serial port. @@ -42,6 +44,7 @@ Config Initiator int ldisc = N_GSM0710; struct gsm_config c; + struct gsm_config_ext ce; struct termios configuration; uint32_t first; @@ -62,6 +65,12 @@ Config Initiator /* use n_gsm line discipline */ ioctl(fd, TIOCSETD, &ldisc); + /* get n_gsm extended configuration */ + ioctl(fd, GSMIOC_GETCONF_EXT, &ce); + /* use keep-alive once every 5s for modem connection supervision */ + ce.keep_alive = 500; + /* set the new extended configuration */ + ioctl(fd, GSMIOC_SETCONF_EXT, &ce); /* get n_gsm configuration */ ioctl(fd, GSMIOC_GETCONF, &c); /* we are initiator and need encoding 0 (basic) */ @@ -106,6 +115,9 @@ Config Requester #. Switch the serial line to using the *n_gsm* line discipline by using ``TIOCSETD`` ioctl. +#. Configure the mux using ``GSMIOC_GETCONF_EXT``/``GSMIOC_SETCONF_EXT`` + ioctl if needed. + #. Configure the mux using ``GSMIOC_GETCONF``/``GSMIOC_SETCONF`` ioctl. #. Obtain base gsmtty number for the used serial port:: @@ -119,6 +131,7 @@ Config Requester int ldisc = N_GSM0710; struct gsm_config c; + struct gsm_config_ext ce; struct termios configuration; uint32_t first; @@ -132,6 +145,12 @@ Config Requester /* use n_gsm line discipline */ ioctl(fd, TIOCSETD, &ldisc); + /* get n_gsm extended configuration */ + ioctl(fd, GSMIOC_GETCONF_EXT, &ce); + /* use keep-alive once every 5s for peer connection supervision */ + ce.keep_alive = 500; + /* set the new extended configuration */ + ioctl(fd, GSMIOC_SETCONF_EXT, &ce); /* get n_gsm configuration */ ioctl(fd, GSMIOC_GETCONF, &c); /* we are requester and need encoding 0 (basic) */ diff --git a/Documentation/fpga/dfl.rst b/Documentation/fpga/dfl.rst index 15b670926084..80255e2dc3e6 100644 --- a/Documentation/fpga/dfl.rst +++ b/Documentation/fpga/dfl.rst @@ -75,6 +75,125 @@ convenient for software to locate each feature by walking through this list, and can be implemented in register regions of any FPGA device. +Device Feature Header - Version 0 +================================= +Version 0 (DFHv0) is the original version of the Device Feature Header. +All multi-byte quantities in DFHv0 are little-endian. +The format of DFHv0 is shown below:: + + +-----------------------------------------------------------------------+ + |63 Type 60|59 DFH VER 52|51 Rsvd 41|40 EOL|39 Next 16|15 REV 12|11 ID 0| 0x00 + +-----------------------------------------------------------------------+ + |63 GUID_L 0| 0x08 + +-----------------------------------------------------------------------+ + |63 GUID_H 0| 0x10 + +-----------------------------------------------------------------------+ + +- Offset 0x00 + + * Type - The type of DFH (e.g. FME, AFU, or private feature). + * DFH VER - The version of the DFH. + * Rsvd - Currently unused. + * EOL - Set if the DFH is the end of the Device Feature List (DFL). + * Next - The offset in bytes of the next DFH in the DFL from the DFH start, + and the start of a DFH must be aligned to an 8 byte boundary. + If EOL is set, Next is the size of MMIO of the last feature in the list. + * REV - The revision of the feature associated with this header. + * ID - The feature ID if Type is private feature. + +- Offset 0x08 + + * GUID_L - Least significant 64 bits of a 128-bit Globally Unique Identifier + (present only if Type is FME or AFU). + +- Offset 0x10 + + * GUID_H - Most significant 64 bits of a 128-bit Globally Unique Identifier + (present only if Type is FME or AFU). + + +Device Feature Header - Version 1 +================================= +Version 1 (DFHv1) of the Device Feature Header adds the following functionality: + +* Provides a standardized mechanism for features to describe + parameters/capabilities to software. +* Standardize the use of a GUID for all DFHv1 types. +* Decouples the DFH location from the register space of the feature itself. + +All multi-byte quantities in DFHv1 are little-endian. +The format of Version 1 of the Device Feature Header (DFH) is shown below:: + + +-----------------------------------------------------------------------+ + |63 Type 60|59 DFH VER 52|51 Rsvd 41|40 EOL|39 Next 16|15 REV 12|11 ID 0| 0x00 + +-----------------------------------------------------------------------+ + |63 GUID_L 0| 0x08 + +-----------------------------------------------------------------------+ + |63 GUID_H 0| 0x10 + +-----------------------------------------------------------------------+ + |63 Reg Address/Offset 1| Rel 0| 0x18 + +-----------------------------------------------------------------------+ + |63 Reg Size 32|Params 31|30 Group 16|15 Instance 0| 0x20 + +-----------------------------------------------------------------------+ + |63 Next 35|34RSV33|EOP32|31 Param Version 16|15 Param ID 0| 0x28 + +-----------------------------------------------------------------------+ + |63 Parameter Data 0| 0x30 + +-----------------------------------------------------------------------+ + + ... + + +-----------------------------------------------------------------------+ + |63 Next 35|34RSV33|EOP32|31 Param Version 16|15 Param ID 0| + +-----------------------------------------------------------------------+ + |63 Parameter Data 0| + +-----------------------------------------------------------------------+ + +- Offset 0x00 + + * Type - The type of DFH (e.g. FME, AFU, or private feature). + * DFH VER - The version of the DFH. + * Rsvd - Currently unused. + * EOL - Set if the DFH is the end of the Device Feature List (DFL). + * Next - The offset in bytes of the next DFH in the DFL from the DFH start, + and the start of a DFH must be aligned to an 8 byte boundary. + If EOL is set, Next is the size of MMIO of the last feature in the list. + * REV - The revision of the feature associated with this header. + * ID - The feature ID if Type is private feature. + +- Offset 0x08 + + * GUID_L - Least significant 64 bits of a 128-bit Globally Unique Identifier. + +- Offset 0x10 + + * GUID_H - Most significant 64 bits of a 128-bit Globally Unique Identifier. + +- Offset 0x18 + + * Reg Address/Offset - If Rel bit is set, then the value is the high 63 bits + of a 16-bit aligned absolute address of the feature's registers. Otherwise + the value is the offset from the start of the DFH of the feature's registers. + +- Offset 0x20 + + * Reg Size - Size of feature's register set in bytes. + * Params - Set if DFH has a list of parameter blocks. + * Group - Id of group if feature is part of a group. + * Instance - Id of feature instance within a group. + +- Offset 0x28 if feature has parameters + + * Next - Offset to the next parameter block in 8 byte words. If EOP set, + size in 8 byte words of last parameter. + * Param Version - Version of Param ID. + * Param ID - ID of parameter. + +- Offset 0x30 + + * Parameter Data - Parameter data whose size and format is defined by + version and ID of the parameter. + + FIU - FME (FPGA Management Engine) ================================== The FPGA Management Engine performs reconfiguration and other infrastructure diff --git a/MAINTAINERS b/MAINTAINERS index 376d51b99b75..124214ba6feb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13703,6 +13703,13 @@ L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-mchp-pci1xxxx.c +MICROCHIP PCIe UART DRIVER +M: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> +M: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> +L: linux-serial@vger.kernel.org +S: Maintained +F: drivers/tty/serial/8250/8250_pci1xxxx.c + MICROCHIP PWM DRIVER M: Claudiu Beznea <claudiu.beznea@microchip.com> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) diff --git a/arch/arm/include/asm/semihost.h b/arch/arm/include/asm/semihost.h new file mode 100644 index 000000000000..f365787e7c23 --- /dev/null +++ b/arch/arm/include/asm/semihost.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2012 ARM Ltd. + * Author: Marc Zyngier <marc.zyngier@arm.com> + * + * Adapted for ARM and earlycon: + * Copyright (C) 2014 Linaro Ltd. + * Author: Rob Herring <robh@kernel.org> + */ + +#ifndef _ARM_SEMIHOST_H_ +#define _ARM_SEMIHOST_H_ + +#ifdef CONFIG_THUMB2_KERNEL +#define SEMIHOST_SWI "0xab" +#else +#define SEMIHOST_SWI "0x123456" +#endif + +struct uart_port; + +static inline void smh_putc(struct uart_port *port, unsigned char c) +{ + asm volatile("mov r1, %0\n" + "mov r0, #3\n" + "svc " SEMIHOST_SWI "\n" + : : "r" (&c) : "r0", "r1", "memory"); +} + +#endif /* _ARM_SEMIHOST_H_ */ diff --git a/arch/arm64/include/asm/semihost.h b/arch/arm64/include/asm/semihost.h new file mode 100644 index 000000000000..87e353dab868 --- /dev/null +++ b/arch/arm64/include/asm/semihost.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2012 ARM Ltd. + * Author: Marc Zyngier <marc.zyngier@arm.com> + * + * Adapted for ARM and earlycon: + * Copyright (C) 2014 Linaro Ltd. + * Author: Rob Herring <robh@kernel.org> + */ + +#ifndef _ARM64_SEMIHOST_H_ +#define _ARM64_SEMIHOST_H_ + +struct uart_port; + +static inline void smh_putc(struct uart_port *port, unsigned char c) +{ + asm volatile("mov x1, %0\n" + "mov x0, #3\n" + "hlt 0xf000\n" + : : "r" (&c) : "x0", "x1", "memory"); +} + +#endif /* _ARM64_SEMIHOST_H_ */ diff --git a/arch/riscv/include/asm/semihost.h b/arch/riscv/include/asm/semihost.h new file mode 100644 index 000000000000..557a34938193 --- /dev/null +++ b/arch/riscv/include/asm/semihost.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 tinylab.org + * Author: Bin Meng <bmeng@tinylab.org> + */ + +#ifndef _RISCV_SEMIHOST_H_ +#define _RISCV_SEMIHOST_H_ + +struct uart_port; + +static inline void smh_putc(struct uart_port *port, unsigned char c) +{ + asm volatile("addi a1, %0, 0\n" + "addi a0, zero, 3\n" + ".balign 16\n" + ".option push\n" + ".option norvc\n" + "slli zero, zero, 0x1f\n" + "ebreak\n" + "srai zero, zero, 0x7\n" + ".option pop\n" + : : "r" (&c) : "a0", "a1", "memory"); +} + +#endif /* _RISCV_SEMIHOST_H_ */ diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index b2735be81ab2..1577eba6fe0e 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -377,8 +377,8 @@ static void async_mode(MGSLPC_INFO *info); static void tx_timeout(struct timer_list *t); -static int carrier_raised(struct tty_port *port); -static void dtr_rts(struct tty_port *port, int onoff); +static bool carrier_raised(struct tty_port *port); +static void dtr_rts(struct tty_port *port, bool active); #if SYNCLINK_GENERIC_HDLC #define dev_to_port(D) (dev_to_hdlc(D)->priv) @@ -1309,7 +1309,7 @@ static int startup(MGSLPC_INFO * info, struct tty_struct *tty) if (tty) clear_bit(TTY_IO_ERROR, &tty->flags); - tty_port_set_initialized(&info->port, 1); + tty_port_set_initialized(&info->port, true); return 0; } @@ -1359,7 +1359,7 @@ static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty) if (tty) set_bit(TTY_IO_ERROR, &tty->flags); - tty_port_set_initialized(&info->port, 0); + tty_port_set_initialized(&info->port, false); } static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty) @@ -2430,7 +2430,7 @@ static void mgslpc_hangup(struct tty_struct *tty) tty_port_hangup(&info->port); } -static int carrier_raised(struct tty_port *port) +static bool carrier_raised(struct tty_port *port) { MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port); unsigned long flags; @@ -2439,18 +2439,16 @@ static int carrier_raised(struct tty_port *port) get_signals(info); spin_unlock_irqrestore(&info->lock, flags); - if (info->serial_signals & SerialSignal_DCD) - return 1; - return 0; + return info->serial_signals & SerialSignal_DCD; } -static void dtr_rts(struct tty_port *port, int onoff) +static void dtr_rts(struct tty_port *port, bool active) { MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port); unsigned long flags; spin_lock_irqsave(&info->lock, flags); - if (onoff) + if (active) info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; else info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index b9aae85ba930..0a4227bc9462 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -13,6 +13,7 @@ #include <linux/dfl.h> #include <linux/fpga-dfl.h> #include <linux/module.h> +#include <linux/overflow.h> #include <linux/uaccess.h> #include "dfl.h" @@ -342,6 +343,8 @@ static void release_dfl_dev(struct device *dev) if (ddev->mmio_res.parent) release_resource(&ddev->mmio_res); + kfree(ddev->params); + ida_free(&dfl_device_ida, ddev->id); kfree(ddev->irqs); kfree(ddev); @@ -380,7 +383,16 @@ dfl_dev_add(struct dfl_feature_platform_data *pdata, ddev->type = feature_dev_id_type(pdev); ddev->feature_id = feature->id; ddev->revision = feature->revision; + ddev->dfh_version = feature->dfh_version; ddev->cdev = pdata->dfl_cdev; + if (feature->param_size) { + ddev->params = kmemdup(feature->params, feature->param_size, GFP_KERNEL); + if (!ddev->params) { + ret = -ENOMEM; + goto put_dev; + } + ddev->param_size = feature->param_size; + } /* add mmio resource */ parent_res = &pdev->resource[feature->resource_index]; @@ -708,20 +720,27 @@ struct build_feature_devs_info { * struct dfl_feature_info - sub feature info collected during feature dev build * * @fid: id of this sub feature. + * @revision: revision of this sub feature + * @dfh_version: version of Device Feature Header (DFH) * @mmio_res: mmio resource of this sub feature. * @ioaddr: mapped base address of mmio resource. * @node: node in sub_features linked list. * @irq_base: start of irq index in this sub feature. * @nr_irqs: number of irqs of this sub feature. + * @param_size: size DFH parameters. + * @params: DFH parameter data. */ struct dfl_feature_info { u16 fid; u8 revision; + u8 dfh_version; struct resource mmio_res; void __iomem *ioaddr; struct list_head node; unsigned int irq_base; unsigned int nr_irqs; + unsigned int param_size; + u64 params[]; }; static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev, @@ -797,7 +816,17 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) feature->dev = fdev; feature->id = finfo->fid; feature->revision = finfo->revision; + feature->dfh_version = finfo->dfh_version; + if (finfo->param_size) { + feature->params = devm_kmemdup(binfo->dev, + finfo->params, finfo->param_size, + GFP_KERNEL); + if (!feature->params) + return -ENOMEM; + + feature->param_size = finfo->param_size; + } /* * the FIU header feature has some fundamental functions (sriov * set, port enable/disable) needed for the dfl bus device and @@ -934,56 +963,115 @@ static u16 feature_id(u64 value) return 0; } +static u64 *find_param(u64 *params, resource_size_t max, int param_id) +{ + u64 *end = params + max / sizeof(u64); + u64 v, next; + + while (params < end) { + v = *params; + if (param_id == FIELD_GET(DFHv1_PARAM_HDR_ID, v)) + return params; + + if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) + break; + + next = FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, v); + params += next; + } + + return NULL; +} + +/** + * dfh_find_param() - find parameter block for the given parameter id + * @dfl_dev: dfl device + * @param_id: id of dfl parameter + * @psize: destination to store size of parameter data in bytes + * + * Return: pointer to start of parameter data, PTR_ERR otherwise. + */ +void *dfh_find_param(struct dfl_device *dfl_dev, int param_id, size_t *psize) +{ + u64 *phdr = find_param(dfl_dev->params, dfl_dev->param_size, param_id); + + if (!phdr) + return ERR_PTR(-ENOENT); + + if (psize) + *psize = (FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, *phdr) - 1) * sizeof(u64); + + return phdr + 1; +} +EXPORT_SYMBOL_GPL(dfh_find_param); + static int parse_feature_irqs(struct build_feature_devs_info *binfo, - resource_size_t ofst, u16 fid, - unsigned int *irq_base, unsigned int *nr_irqs) + resource_size_t ofst, struct dfl_feature_info *finfo) { void __iomem *base = binfo->ioaddr + ofst; unsigned int i, ibase, inr = 0; + void *params = finfo->params; enum dfl_id_type type; + u16 fid = finfo->fid; int virq; + u64 *p; u64 v; - type = feature_dev_id_type(binfo->feature_dev); + switch (finfo->dfh_version) { + case 0: + /* + * DFHv0 only provides MMIO resource information for each feature + * in the DFL header. There is no generic interrupt information. + * Instead, features with interrupt functionality provide + * the information in feature specific registers. + */ + type = feature_dev_id_type(binfo->feature_dev); + if (type == PORT_ID) { + switch (fid) { + case PORT_FEATURE_ID_UINT: + v = readq(base + PORT_UINT_CAP); + ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v); + inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v); + break; + case PORT_FEATURE_ID_ERROR: + v = readq(base + PORT_ERROR_CAP); + ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v); + inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v); + break; + } + } else if (type == FME_ID) { + switch (fid) { + case FME_FEATURE_ID_GLOBAL_ERR: + v = readq(base + FME_ERROR_CAP); + ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v); + inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v); + break; + } + } + break; - /* - * Ideally DFL framework should only read info from DFL header, but - * current version DFL only provides mmio resources information for - * each feature in DFL Header, no field for interrupt resources. - * Interrupt resource information is provided by specific mmio - * registers of each private feature which supports interrupt. So in - * order to parse and assign irq resources, DFL framework has to look - * into specific capability registers of these private features. - * - * Once future DFL version supports generic interrupt resource - * information in common DFL headers, the generic interrupt parsing - * code will be added. But in order to be compatible to old version - * DFL, the driver may still fall back to these quirks. - */ - if (type == PORT_ID) { - switch (fid) { - case PORT_FEATURE_ID_UINT: - v = readq(base + PORT_UINT_CAP); - ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v); - inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v); - break; - case PORT_FEATURE_ID_ERROR: - v = readq(base + PORT_ERROR_CAP); - ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v); - inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v); + case 1: + /* + * DFHv1 provides interrupt resource information in DFHv1 + * parameter blocks. + */ + p = find_param(params, finfo->param_size, DFHv1_PARAM_ID_MSI_X); + if (!p) break; - } - } else if (type == FME_ID) { - if (fid == FME_FEATURE_ID_GLOBAL_ERR) { - v = readq(base + FME_ERROR_CAP); - ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v); - inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v); - } + + p++; + ibase = FIELD_GET(DFHv1_PARAM_MSI_X_STARTV, *p); + inr = FIELD_GET(DFHv1_PARAM_MSI_X_NUMV, *p); + break; + + default: + dev_warn(binfo->dev, "unexpected DFH version %d\n", finfo->dfh_version); + break; } if (!inr) { - *irq_base = 0; - *nr_irqs = 0; + finfo->irq_base = 0; + finfo->nr_irqs = 0; return 0; } @@ -1006,12 +1094,37 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo, } } - *irq_base = ibase; - *nr_irqs = inr; + finfo->irq_base = ibase; + finfo->nr_irqs = inr; return 0; } +static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max) +{ + int size = 0; + u64 v, next; + + if (!FIELD_GET(DFHv1_CSR_SIZE_GRP_HAS_PARAMS, + readq(dfh_base + DFHv1_CSR_SIZE_GRP))) + return 0; + + while (size + DFHv1_PARAM_HDR < max) { + v = readq(dfh_base + DFHv1_PARAM_HDR + size); + + next = FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, v); + if (!next) + return -EINVAL; + + size += next * sizeof(u64); + + if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) + return size; + } + + return -ENOENT; +} + /* * when create sub feature instances, for private features, it doesn't need * to provide resource size and feature id as they could be read from DFH @@ -1023,39 +1136,69 @@ static int create_feature_instance(struct build_feature_devs_info *binfo, resource_size_t ofst, resource_size_t size, u16 fid) { - unsigned int irq_base, nr_irqs; struct dfl_feature_info *finfo; + resource_size_t start, end; + int dfh_psize = 0; u8 revision = 0; + u64 v, addr_off; + u8 dfh_ver = 0; int ret; - u64 v; if (fid != FEATURE_ID_AFU) { v = readq(binfo->ioaddr + ofst); revision = FIELD_GET(DFH_REVISION, v); - + dfh_ver = FIELD_GET(DFH_VERSION, v); /* read feature size and id if inputs are invalid */ size = size ? size : feature_size(v); fid = fid ? fid : feature_id(v); + if (dfh_ver == 1) { + dfh_psize = dfh_get_param_size(binfo->ioaddr + ofst, size); + if (dfh_psize < 0) { + dev_err(binfo->dev, + "failed to read size of DFHv1 parameters %d\n", + dfh_psize); + return dfh_psize; + } + dev_dbg(binfo->dev, "dfhv1_psize %d\n", dfh_psize); + } } if (binfo->len - ofst < size) return -EINVAL; - ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs); - if (ret) - return ret; - - finfo = kzalloc(sizeof(*finfo), GFP_KERNEL); + finfo = kzalloc(struct_size(finfo, params, dfh_psize / sizeof(u64)), GFP_KERNEL); if (!finfo) return -ENOMEM; + memcpy_fromio(finfo->params, binfo->ioaddr + ofst + DFHv1_PARAM_HDR, dfh_psize); + finfo->param_size = dfh_psize; + finfo->fid = fid; finfo->revision = revision; - finfo->mmio_res.start = binfo->start + ofst; - finfo->mmio_res.end = finfo->mmio_res.start + size - 1; + finfo->dfh_version = dfh_ver; + if (dfh_ver == 1) { + v = readq(binfo->ioaddr + ofst + DFHv1_CSR_ADDR); + addr_off = FIELD_GET(DFHv1_CSR_ADDR_MASK, v); + if (FIELD_GET(DFHv1_CSR_ADDR_REL, v)) + start = addr_off << 1; + else + start = binfo->start + ofst + addr_off; + + v = readq(binfo->ioaddr + ofst + DFHv1_CSR_SIZE_GRP); + end = start + FIELD_GET(DFHv1_CSR_SIZE_GRP_SIZE, v) - 1; + } else { + start = binfo->start + ofst; + end = start + size - 1; + } finfo->mmio_res.flags = IORESOURCE_MEM; - finfo->irq_base = irq_base; - finfo->nr_irqs = nr_irqs; + finfo->mmio_res.start = start; + finfo->mmio_res.end = end; + + ret = parse_feature_irqs(binfo, ofst, finfo); + if (ret) { + kfree(finfo); + return ret; + } list_add_tail(&finfo->node, &binfo->sub_features); binfo->feature_num++; diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 06cfcd5e84bb..20eaddce6988 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -74,11 +74,47 @@ #define DFH_REVISION GENMASK_ULL(15, 12) /* Feature revision */ #define DFH_NEXT_HDR_OFST GENMASK_ULL(39, 16) /* Offset to next DFH */ #define DFH_EOL BIT_ULL(40) /* End of list */ +#define DFH_VERSION GENMASK_ULL(59, 52) /* DFH version */ #define DFH_TYPE GENMASK_ULL(63, 60) /* Feature type */ #define DFH_TYPE_AFU 1 #define DFH_TYPE_PRIVATE 3 #define DFH_TYPE_FIU 4 +/* + * DFHv1 Register Offset definitons + * In DHFv1, DFH + GUID + CSR_START + CSR_SIZE_GROUP + PARAM_HDR + PARAM_DATA + * as common header registers + */ +#define DFHv1_CSR_ADDR 0x18 /* CSR Register start address */ +#define DFHv1_CSR_SIZE_GRP 0x20 /* Size of Reg Block and Group/tag */ +#define DFHv1_PARAM_HDR 0x28 /* Optional First Param header */ + +/* + * CSR Rel Bit, 1'b0 = relative (offset from feature DFH start), + * 1'b1 = absolute (ARM or other non-PCIe use) + */ +#define DFHv1_CSR_ADDR_REL BIT_ULL(0) + +/* CSR Header Register Bit Definitions */ +#define DFHv1_CSR_ADDR_MASK GENMASK_ULL(63, 1) /* 63:1 of CSR address */ + +/* CSR SIZE Goup Register Bit Definitions */ +#define DFHv1_CSR_SIZE_GRP_INSTANCE_ID GENMASK_ULL(15, 0) /* Enumeration instantiated IP */ +#define DFHv1_CSR_SIZE_GRP_GROUPING_ID GENMASK_ULL(30, 16) /* Group Features/interfaces */ +#define DFHv1_CSR_SIZE_GRP_HAS_PARAMS BIT_ULL(31) /* Presence of Parameters */ +#define DFHv1_CSR_SIZE_GRP_SIZE GENMASK_ULL(63, 32) /* Size of CSR Block in bytes */ + +/* PARAM Header Register Bit Definitions */ +#define DFHv1_PARAM_HDR_ID GENMASK_ULL(15, 0) /* Id of this Param */ +#define DFHv1_PARAM_HDR_VER GENMASK_ULL(31, 16) /* Version Param */ +#define DFHv1_PARAM_HDR_NEXT_OFFSET GENMASK_ULL(63, 35) /* Offset of next Param */ +#define DFHv1_PARAM_HDR_NEXT_EOP BIT_ULL(32) +#define DFHv1_PARAM_DATA 0x08 /* Offset of Param data from Param header */ + +#define DFHv1_PARAM_ID_MSI_X 0x1 +#define DFHv1_PARAM_MSI_X_NUMV GENMASK_ULL(63, 32) +#define DFHv1_PARAM_MSI_X_STARTV GENMASK_ULL(31, 0) + /* Next AFU Register Bitfield */ #define NEXT_AFU_NEXT_DFH_OFST GENMASK_ULL(23, 0) /* Offset to next AFU */ @@ -231,6 +267,7 @@ struct dfl_feature_irq_ctx { * * @dev: ptr to pdev of the feature device which has the sub feature. * @id: sub feature id. + * @revision: revisition of the instance of a feature. * @resource_index: each sub feature has one mmio resource for its registers. * this index is used to find its mmio resource from the * feature dev (platform device)'s resources. @@ -240,6 +277,9 @@ struct dfl_feature_irq_ctx { * @ops: ops of this sub feature. * @ddev: ptr to the dfl device of this sub feature. * @priv: priv data of this feature. + * @dfh_version: version of the DFH + * @param_size: size of dfh parameters + * @params: point to memory copy of dfh parameters */ struct dfl_feature { struct platform_device *dev; @@ -252,6 +292,9 @@ struct dfl_feature { const struct dfl_feature_ops *ops; struct dfl_device *ddev; void *priv; + u8 dfh_version; + unsigned int param_size; + void *params; }; #define FEATURE_DEV_ID_UNUSED (-1) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index fd70794bfcee..80d586b0be7a 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -14,7 +14,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/qcom-geni-se.h> +#include <linux/soc/qcom/geni-se.h> #include <linux/spinlock.h> #define SE_I2C_TX_TRANS_LEN 0x26c diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index fc00274070b6..103fce0c49e6 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -647,7 +647,7 @@ static void ipoctal_hangup(struct tty_struct *tty) tty_port_hangup(&channel->tty_port); ipoctal_reset_channel(channel); - tty_port_set_initialized(&channel->tty_port, 0); + tty_port_set_initialized(&channel->tty_port, false); wake_up_interruptible(&channel->tty_port.open_wait); } @@ -659,7 +659,7 @@ static void ipoctal_shutdown(struct tty_struct *tty) return; ipoctal_reset_channel(channel); - tty_port_set_initialized(&channel->tty_port, 0); + tty_port_set_initialized(&channel->tty_port, false); } static void ipoctal_cleanup(struct tty_struct *tty) diff --git a/drivers/mmc/core/sdio_uart.c b/drivers/mmc/core/sdio_uart.c index ae7ef2e038be..50536fe59f1a 100644 --- a/drivers/mmc/core/sdio_uart.c +++ b/drivers/mmc/core/sdio_uart.c @@ -526,7 +526,7 @@ static void sdio_uart_irq(struct sdio_func *func) port->in_sdio_uart_irq = NULL; } -static int uart_carrier_raised(struct tty_port *tport) +static bool uart_carrier_raised(struct tty_port *tport) { struct sdio_uart_port *port = container_of(tport, struct sdio_uart_port, port); @@ -535,28 +535,27 @@ static int uart_carrier_raised(struct tty_port *tport) return 1; ret = sdio_uart_get_mctrl(port); sdio_uart_release_func(port); - if (ret & TIOCM_CAR) - return 1; - return 0; + + return ret & TIOCM_CAR; } /** * uart_dtr_rts - port helper to set uart signals * @tport: tty port to be updated - * @onoff: set to turn on DTR/RTS + * @active: set to turn on DTR/RTS * * Called by the tty port helpers when the modem signals need to be * adjusted during an open, close and hangup. */ -static void uart_dtr_rts(struct tty_port *tport, int onoff) +static void uart_dtr_rts(struct tty_port *tport, bool active) { struct sdio_uart_port *port = container_of(tport, struct sdio_uart_port, port); int ret = sdio_uart_claim_func(port); if (ret) return; - if (onoff == 0) + if (!active) sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); else sdio_uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c index d73c4c2ed4e1..443d6bae19d1 100644 --- a/drivers/pps/clients/pps-ldisc.c +++ b/drivers/pps/clients/pps-ldisc.c @@ -13,7 +13,7 @@ #include <linux/pps_kernel.h> #include <linux/bug.h> -static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status) +static void pps_tty_dcd_change(struct tty_struct *tty, bool active) { struct pps_device *pps; struct pps_event_time ts; @@ -29,11 +29,11 @@ static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status) return; /* Now do the PPS event report */ - pps_event(pps, &ts, status ? PPS_CAPTUREASSERT : + pps_event(pps, &ts, active ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, NULL); dev_dbg(pps->dev, "PPS %s at %lu\n", - status ? "assert" : "clear", jiffies); + active ? "assert" : "clear", jiffies); } static int (*alias_n_tty_open)(struct tty_struct *tty); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 72ba83c1bc79..0b05cd76b7d0 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -629,7 +629,7 @@ static int raw3215_startup(struct raw3215_info *raw) if (tty_port_initialized(&raw->port)) return 0; raw->line_pos = 0; - tty_port_set_initialized(&raw->port, 1); + tty_port_set_initialized(&raw->port, true); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw3215_try_io(raw); spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); @@ -659,7 +659,7 @@ static void raw3215_shutdown(struct raw3215_info *raw) spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); remove_wait_queue(&raw->empty_wait, &wait); set_current_state(TASK_RUNNING); - tty_port_set_initialized(&raw->port, 1); + tty_port_set_initialized(&raw->port, true); } spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); } diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index f0475b93ca73..795a2e1d59b3 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -14,7 +14,7 @@ #include <linux/of_platform.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> -#include <linux/qcom-geni-se.h> +#include <linux/soc/qcom/geni-se.h> /** * DOC: Overview diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index e1b2e0b65779..babb039bcb43 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -12,7 +12,7 @@ #include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/pm_runtime.h> -#include <linux/qcom-geni-se.h> +#include <linux/soc/qcom/geni-se.h> #include <linux/spi/spi.h> #include <linux/spinlock.h> diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 90ff07f2cbf7..20a34599859f 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -701,7 +701,7 @@ static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, return -ENOIOCTLCMD; } -static void gb_tty_dtr_rts(struct tty_port *port, int on) +static void gb_tty_dtr_rts(struct tty_port *port, bool active) { struct gb_tty *gb_tty; u8 newctrl; @@ -709,7 +709,7 @@ static void gb_tty_dtr_rts(struct tty_port *port, int on) gb_tty = container_of(port, struct gb_tty, port); newctrl = gb_tty->ctrlout; - if (on) + if (active) newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); else newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index f52266766df9..d7515d61659e 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -502,7 +502,7 @@ static int startup(struct tty_struct *tty, struct serial_state *info) */ change_speed(tty, info, NULL); - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); local_irq_restore(flags); return 0; @@ -556,7 +556,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) set_bit(TTY_IO_ERROR, &tty->flags); - tty_port_set_initialized(&info->tport, 0); + tty_port_set_initialized(&info->tport, false); local_irq_restore(flags); } @@ -1329,7 +1329,7 @@ static void rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(tty, info); info->tport.count = 0; - tty_port_set_active(&info->tport, 0); + tty_port_set_active(&info->tport, false); info->tport.tty = NULL; wake_up_interruptible(&info->tport.open_wait); } @@ -1454,18 +1454,18 @@ static const struct tty_operations serial_ops = { .proc_show = rs_proc_show, }; -static int amiga_carrier_raised(struct tty_port *port) +static bool amiga_carrier_raised(struct tty_port *port) { return !(ciab.pra & SER_DCD); } -static void amiga_dtr_rts(struct tty_port *port, int raise) +static void amiga_dtr_rts(struct tty_port *port, bool active) { struct serial_state *info = container_of(port, struct serial_state, tport); unsigned long flags; - if (raise) + if (active) info->MCR |= SER_DTR|SER_RTS; else info->MCR &= ~(SER_DTR|SER_RTS); diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index a683e21df19c..10c10cfdf92a 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -376,7 +376,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) /* We are ready... raise DTR/RTS */ if (C_BAUD(tty)) if (hp->ops->dtr_rts) - hp->ops->dtr_rts(hp, 1); + hp->ops->dtr_rts(hp, true); tty_port_set_initialized(&hp->port, true); } @@ -406,7 +406,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) if (C_HUPCL(tty)) if (hp->ops->dtr_rts) - hp->ops->dtr_rts(hp, 0); + hp->ops->dtr_rts(hp, false); if (hp->ops->notifier_del) hp->ops->notifier_del(hp, hp->data); diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 18d005814e4b..9668f821db01 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h @@ -66,7 +66,7 @@ struct hv_ops { int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear); /* Callbacks to handle tty ports */ - void (*dtr_rts)(struct hvc_struct *hp, int raise); + void (*dtr_rts)(struct hvc_struct *hp, bool active); }; /* Register a vterm and a slot index for use as a console (console_init) */ diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index 7d49a872de48..543f35ddf523 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -658,13 +658,13 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) /** * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS * @hp: Pointer the HVC device (struct hvc_struct) - * @raise: Non-zero to raise or zero to lower DTR/RTS lines + * @active: True to raise or false to lower DTR/RTS lines * * This routine notifies the HVC back-end to raise or lower DTR/RTS * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to * drop the IUCV connection (similar to hang up the modem). */ -static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) +static void hvc_iucv_dtr_rts(struct hvc_struct *hp, bool active) { struct hvc_iucv_private *priv; struct iucv_path *path; @@ -672,7 +672,7 @@ static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) /* Raising the DTR/RTS is ignored as IUCV connections can be * established at any times. */ - if (raise) + if (active) return; priv = hvc_iucv_get_private(hp->vtermno); diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 4ba24963685e..1de1a09bf82d 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -52,6 +52,7 @@ #include <linux/device.h> #include <linux/init.h> +#include <linux/completion.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/kref.h> @@ -285,6 +286,7 @@ struct hvcs_struct { char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */ struct list_head next; /* list management */ struct vio_dev *vdev; + struct completion *destroyed; }; static LIST_HEAD(hvcs_structs); @@ -432,7 +434,7 @@ static ssize_t hvcs_index_show(struct device *dev, struct device_attribute *attr static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL); -static struct attribute *hvcs_attrs[] = { +static struct attribute *hvcs_dev_attrs[] = { &dev_attr_partner_vtys.attr, &dev_attr_partner_clcs.attr, &dev_attr_current_vty.attr, @@ -441,9 +443,7 @@ static struct attribute *hvcs_attrs[] = { NULL, }; -static struct attribute_group hvcs_attr_group = { - .attrs = hvcs_attrs, -}; +ATTRIBUTE_GROUPS(hvcs_dev); static ssize_t rescan_show(struct device_driver *ddp, char *buf) { @@ -468,6 +468,13 @@ static ssize_t rescan_store(struct device_driver *ddp, const char * buf, static DRIVER_ATTR_RW(rescan); +static struct attribute *hvcs_attrs[] = { + &driver_attr_rescan.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(hvcs); + static void hvcs_kick(void) { hvcs_kicked = 1; @@ -658,11 +665,13 @@ static void hvcs_destruct_port(struct tty_port *p) { struct hvcs_struct *hvcsd = container_of(p, struct hvcs_struct, port); struct vio_dev *vdev; + struct completion *comp; unsigned long flags; spin_lock(&hvcs_structs_lock); spin_lock_irqsave(&hvcsd->lock, flags); + comp = hvcsd->destroyed; /* the list_del poisons the pointers */ list_del(&(hvcsd->next)); @@ -682,15 +691,16 @@ static void hvcs_destruct_port(struct tty_port *p) hvcsd->p_unit_address = 0; hvcsd->p_partition_ID = 0; + hvcsd->destroyed = NULL; hvcs_return_index(hvcsd->index); memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1); spin_unlock_irqrestore(&hvcsd->lock, flags); spin_unlock(&hvcs_structs_lock); - sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group); - kfree(hvcsd); + if (comp) + complete(comp); } static const struct tty_port_operations hvcs_port_ops = { @@ -721,7 +731,6 @@ static int hvcs_probe( { struct hvcs_struct *hvcsd; int index, rc; - int retval; if (!dev || !id) { printk(KERN_ERR "HVCS: probed with invalid parameter.\n"); @@ -778,13 +787,6 @@ static int hvcs_probe( list_add_tail(&(hvcsd->next), &hvcs_structs); spin_unlock(&hvcs_structs_lock); - retval = sysfs_create_group(&dev->dev.kobj, &hvcs_attr_group); - if (retval) { - printk(KERN_ERR "HVCS: Can't create sysfs attrs for vty-server@%X\n", - hvcsd->vdev->unit_address); - return retval; - } - printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address); /* @@ -797,6 +799,7 @@ static int hvcs_probe( static void hvcs_remove(struct vio_dev *dev) { struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev); + DECLARE_COMPLETION_ONSTACK(comp); unsigned long flags; struct tty_struct *tty; @@ -804,24 +807,22 @@ static void hvcs_remove(struct vio_dev *dev) spin_lock_irqsave(&hvcsd->lock, flags); - tty = hvcsd->port.tty; + hvcsd->destroyed = ∁ + tty = tty_port_tty_get(&hvcsd->port); spin_unlock_irqrestore(&hvcsd->lock, flags); /* - * Let the last holder of this object cause it to be removed, which - * would probably be tty_hangup below. - */ - tty_port_put(&hvcsd->port); - - /* - * The hangup is a scheduled function which will auto chain call - * hvcs_hangup. The tty should always be valid at this time unless a + * The tty should always be valid at this time unless a * simultaneous tty close already cleaned up the hvcs_struct. */ - if (tty) - tty_hangup(tty); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + tty_port_put(&hvcsd->port); + wait_for_completion(&comp); printk(KERN_INFO "HVCS: vty-server@%X removed from the" " vio bus.\n", dev->unit_address); }; @@ -831,6 +832,10 @@ static struct vio_driver hvcs_vio_driver = { .probe = hvcs_probe, .remove = hvcs_remove, .name = hvcs_driver_name, + .driver = { + .groups = hvcs_groups, + .dev_groups = hvcs_dev_groups, + }, }; /* Only called from hvcs_get_pi please */ @@ -1171,7 +1176,10 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) hvcsd = tty->driver_data; spin_lock_irqsave(&hvcsd->lock, flags); - if (--hvcsd->port.count == 0) { + if (hvcsd->port.count == 0) { + spin_unlock_irqrestore(&hvcsd->lock, flags); + return; + } else if (--hvcsd->port.count == 0) { vio_disable_interrupts(hvcsd->vdev); @@ -1215,12 +1223,9 @@ static void hvcs_hangup(struct tty_struct * tty) { struct hvcs_struct *hvcsd = tty->driver_data; unsigned long flags; - int temp_open_count; int irq; spin_lock_irqsave(&hvcsd->lock, flags); - /* Preserve this so that we know how many kref refs to put */ - temp_open_count = hvcsd->port.count; /* * Don't kref put inside the spinlock because the destruction @@ -1230,11 +1235,7 @@ static void hvcs_hangup(struct tty_struct * tty) vio_disable_interrupts(hvcsd->vdev); hvcsd->todo_mask = 0; - - /* I don't think the tty needs the hvcs_struct pointer after a hangup */ - tty->driver_data = NULL; hvcsd->port.tty = NULL; - hvcsd->port.count = 0; /* This will drop any buffered data on the floor which is OK in a hangup @@ -1247,21 +1248,6 @@ static void hvcs_hangup(struct tty_struct * tty) spin_unlock_irqrestore(&hvcsd->lock, flags); free_irq(irq, hvcsd); - - /* - * We need to kref_put() for every open_count we have since the - * tty_hangup() function doesn't invoke a close per open connection on a - * non-console device. - */ - while(temp_open_count) { - --temp_open_count; - /* - * The final put will trigger destruction of the hvcs_struct. - * NOTE: If this hangup was signaled from user space then the - * final put will never happen. - */ - tty_port_put(&hvcsd->port); - } } /* @@ -1525,13 +1511,6 @@ static int __init hvcs_module_init(void) pr_info("HVCS: Driver registered.\n"); - /* This needs to be done AFTER the vio_register_driver() call or else - * the kobjects won't be initialized properly. - */ - rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan); - if (rc) - pr_warn("HVCS: Failed to create rescan file (err %d)\n", rc); - return 0; } @@ -1556,8 +1535,6 @@ static void __exit hvcs_module_exit(void) hvcs_pi_buff = NULL; spin_unlock(&hvcs_pi_lock); - driver_remove_file(&hvcs_vio_driver.driver, &driver_attr_rescan); - tty_unregister_driver(hvcs_tty_driver); hvcs_free_index_list(); diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 35b6fddf0341..42fa4c878b2e 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -501,16 +501,16 @@ static int moxa_tiocmset(struct tty_struct *tty, static void moxa_poll(struct timer_list *); static void moxa_set_tty_param(struct tty_struct *, const struct ktermios *); static void moxa_shutdown(struct tty_port *); -static int moxa_carrier_raised(struct tty_port *); -static void moxa_dtr_rts(struct tty_port *, int); +static bool moxa_carrier_raised(struct tty_port *); +static void moxa_dtr_rts(struct tty_port *, bool); /* * moxa board interface functions: */ static void MoxaPortEnable(struct moxa_port *); static void MoxaPortDisable(struct moxa_port *); static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t); -static int MoxaPortGetLineOut(struct moxa_port *, int *, int *); -static void MoxaPortLineCtrl(struct moxa_port *, int, int); +static int MoxaPortGetLineOut(struct moxa_port *, bool *, bool *); +static void MoxaPortLineCtrl(struct moxa_port *, bool, bool); static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int); static int MoxaPortLineStatus(struct moxa_port *); static void MoxaPortFlushData(struct moxa_port *, int); @@ -1432,7 +1432,7 @@ static void moxa_shutdown(struct tty_port *port) MoxaPortFlushData(ch, 2); } -static int moxa_carrier_raised(struct tty_port *port) +static bool moxa_carrier_raised(struct tty_port *port) { struct moxa_port *ch = container_of(port, struct moxa_port, port); int dcd; @@ -1443,10 +1443,10 @@ static int moxa_carrier_raised(struct tty_port *port) return dcd; } -static void moxa_dtr_rts(struct tty_port *port, int onoff) +static void moxa_dtr_rts(struct tty_port *port, bool active) { struct moxa_port *ch = container_of(port, struct moxa_port, port); - MoxaPortLineCtrl(ch, onoff, onoff); + MoxaPortLineCtrl(ch, active, active); } @@ -1481,10 +1481,10 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) if (!tty_port_initialized(&ch->port)) { ch->statusflags = 0; moxa_set_tty_param(tty, &tty->termios); - MoxaPortLineCtrl(ch, 1, 1); + MoxaPortLineCtrl(ch, true, true); MoxaPortEnable(ch); MoxaSetFifo(ch, ch->type == PORT_16550A); - tty_port_set_initialized(&ch->port, 1); + tty_port_set_initialized(&ch->port, true); } mutex_unlock(&ch->port.mutex); mutex_unlock(&moxa_openlock); @@ -1557,19 +1557,21 @@ static unsigned int moxa_chars_in_buffer(struct tty_struct *tty) static int moxa_tiocmget(struct tty_struct *tty) { struct moxa_port *ch = tty->driver_data; - int flag = 0, dtr, rts; + bool dtr_active, rts_active; + int flag = 0; + int status; - MoxaPortGetLineOut(ch, &dtr, &rts); - if (dtr) + MoxaPortGetLineOut(ch, &dtr_active, &rts_active); + if (dtr_active) flag |= TIOCM_DTR; - if (rts) + if (rts_active) flag |= TIOCM_RTS; - dtr = MoxaPortLineStatus(ch); - if (dtr & 1) + status = MoxaPortLineStatus(ch); + if (status & 1) flag |= TIOCM_CTS; - if (dtr & 2) + if (status & 2) flag |= TIOCM_DSR; - if (dtr & 4) + if (status & 4) flag |= TIOCM_CD; return flag; } @@ -1577,8 +1579,8 @@ static int moxa_tiocmget(struct tty_struct *tty) static int moxa_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { + bool dtr_active, rts_active; struct moxa_port *ch; - int dtr, rts; mutex_lock(&moxa_openlock); ch = tty->driver_data; @@ -1587,16 +1589,16 @@ static int moxa_tiocmset(struct tty_struct *tty, return -EINVAL; } - MoxaPortGetLineOut(ch, &dtr, &rts); + MoxaPortGetLineOut(ch, &dtr_active, &rts_active); if (set & TIOCM_RTS) - rts = 1; + rts_active = true; if (set & TIOCM_DTR) - dtr = 1; + dtr_active = true; if (clear & TIOCM_RTS) - rts = 0; + rts_active = false; if (clear & TIOCM_DTR) - dtr = 0; - MoxaPortLineCtrl(ch, dtr, rts); + dtr_active = false; + MoxaPortLineCtrl(ch, dtr_active, rts_active); mutex_unlock(&moxa_openlock); return 0; } @@ -1664,8 +1666,8 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle, u16 __iomem *ip) { struct tty_struct *tty = tty_port_tty_get(&p->port); + bool inited = tty_port_initialized(&p->port); void __iomem *ofsAddr; - unsigned int inited = tty_port_initialized(&p->port); u16 intr; if (tty) { @@ -1877,12 +1879,12 @@ static void MoxaPortFlushData(struct moxa_port *port, int mode) * * Function 13: Get the DTR/RTS state of this port. * Syntax: - * int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); + * int MoxaPortGetLineOut(int port, bool *dtrState, bool *rtsState); * int port : port number (0 - 127) - * int * dtrState : pointer to INT to receive the current DTR + * bool * dtr_active : pointer to bool to receive the current DTR * state. (if NULL, this function will not * write to this address) - * int * rtsState : pointer to INT to receive the current RTS + * bool * rts_active : pointer to bool to receive the current RTS * state. (if NULL, this function will not * write to this address) * @@ -1892,10 +1894,10 @@ static void MoxaPortFlushData(struct moxa_port *port, int mode) * * Function 14: Setting the DTR/RTS output state of this port. * Syntax: - * void MoxaPortLineCtrl(int port, int dtrState, int rtsState); + * void MoxaPortLineCtrl(int port, bool dtrState, bool rtsState); * int port : port number (0 - 127) - * int dtrState : DTR output state (0: off, 1: on) - * int rtsState : RTS output state (0: off, 1: on) + * bool dtr_active : DTR output state + * bool rts_active : RTS output state * * * Function 15: Setting the flow control of this port. @@ -2103,24 +2105,24 @@ static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio, return baud; } -static int MoxaPortGetLineOut(struct moxa_port *port, int *dtrState, - int *rtsState) +static int MoxaPortGetLineOut(struct moxa_port *port, bool *dtr_active, + bool *rts_active) { - if (dtrState) - *dtrState = !!(port->lineCtrl & DTR_ON); - if (rtsState) - *rtsState = !!(port->lineCtrl & RTS_ON); + if (dtr_active) + *dtr_active = port->lineCtrl & DTR_ON; + if (rts_active) + *rts_active = port->lineCtrl & RTS_ON; return 0; } -static void MoxaPortLineCtrl(struct moxa_port *port, int dtr, int rts) +static void MoxaPortLineCtrl(struct moxa_port *port, bool dtr_active, bool rts_active) { u8 mode = 0; - if (dtr) + if (dtr_active) mode |= DTR_ON; - if (rts) + if (rts_active) mode |= RTS_ON; port->lineCtrl = mode; moxafunc(port->tableAddr, FC_LineControl, mode); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 2436e0b10f9a..ef3116e87975 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -458,13 +458,14 @@ static void __mxser_stop_tx(struct mxser_port *info) outb(info->IER, info->ioaddr + UART_IER); } -static int mxser_carrier_raised(struct tty_port *port) +static bool mxser_carrier_raised(struct tty_port *port) { struct mxser_port *mp = container_of(port, struct mxser_port, port); - return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0; + + return inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD; } -static void mxser_dtr_rts(struct tty_port *port, int on) +static void mxser_dtr_rts(struct tty_port *port, bool active) { struct mxser_port *mp = container_of(port, struct mxser_port, port); unsigned long flags; @@ -472,7 +473,7 @@ static void mxser_dtr_rts(struct tty_port *port, int on) spin_lock_irqsave(&mp->slock, flags); mcr = inb(mp->ioaddr + UART_MCR); - if (on) + if (active) mcr |= UART_MCR_DTR | UART_MCR_RTS; else mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); @@ -1063,7 +1064,7 @@ static int mxser_set_serial_info(struct tty_struct *tty, } else { retval = mxser_activate(port, tty); if (retval == 0) - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); } mutex_unlock(&port->mutex); return retval; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 348d85c13f91..7aa05ebed8e1 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -318,6 +318,11 @@ struct gsm_mux { struct gsm_control *pending_cmd;/* Our current pending command */ spinlock_t control_lock; /* Protects the pending command */ + /* Keep-alive */ + struct timer_list ka_timer; /* Keep-alive response timer */ + u8 ka_num; /* Keep-alive match pattern */ + signed int ka_retries; /* Keep-alive retry counter, -1 if not yet initialized */ + /* Configuration */ int adaption; /* 1 or 2 supported */ u8 ftype; /* UI or UIH */ @@ -325,6 +330,7 @@ struct gsm_mux { unsigned int t3; /* Power wake-up timer in seconds. */ int n2; /* Retry count */ u8 k; /* Window size */ + u32 keep_alive; /* Control channel keep-alive in 10ms */ /* Statistics (not currently exposed) */ unsigned long bad_fcs; @@ -540,6 +546,11 @@ static u8 gsm_encode_modem(const struct gsm_dlci *dlci) modembits |= MDM_IC; if (dlci->modem_tx & TIOCM_CD || dlci->gsm->initiator) modembits |= MDM_DV; + /* special mappings for passive side to operate as UE */ + if (dlci->modem_tx & TIOCM_OUT1) + modembits |= MDM_IC; + if (dlci->modem_tx & TIOCM_OUT2) + modembits |= MDM_DV; return modembits; } @@ -1531,6 +1542,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, if (brk & 0x01) tty_insert_flip_char(&dlci->port, 0, TTY_BREAK); dlci->modem_rx = mlines; + wake_up_interruptible(&dlci->gsm->event); } /** @@ -1897,11 +1909,13 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, const u8 *data, int clen) { struct gsm_control *ctrl; + struct gsm_dlci *dlci; unsigned long flags; spin_lock_irqsave(&gsm->control_lock, flags); ctrl = gsm->pending_cmd; + dlci = gsm->dlci[0]; command |= 1; /* Does the reply match our command */ if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) { @@ -1916,6 +1930,53 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, /* Or did we receive the PN response to our PN command */ } else if (command == CMD_PN) { gsm_control_negotiation(gsm, 0, data, clen); + /* Or did we receive the TEST response to our TEST command */ + } else if (command == CMD_TEST && clen == 1 && *data == gsm->ka_num) { + gsm->ka_retries = -1; /* trigger new keep-alive message */ + if (dlci && !dlci->dead) + mod_timer(&gsm->ka_timer, jiffies + gsm->keep_alive * HZ / 100); + } + spin_unlock_irqrestore(&gsm->control_lock, flags); +} + +/** + * gsm_control_keep_alive - check timeout or start keep-alive + * @t: timer contained in our gsm object + * + * Called off the keep-alive timer expiry signaling that our link + * partner is not responding anymore. Link will be closed. + * This is also called to startup our timer. + */ + +static void gsm_control_keep_alive(struct timer_list *t) +{ + struct gsm_mux *gsm = from_timer(gsm, t, ka_timer); + unsigned long flags; + + spin_lock_irqsave(&gsm->control_lock, flags); + if (gsm->ka_num && gsm->ka_retries == 0) { + /* Keep-alive expired -> close the link */ + if (debug & DBG_ERRORS) + pr_debug("%s keep-alive timed out\n", __func__); + spin_unlock_irqrestore(&gsm->control_lock, flags); + if (gsm->dlci[0]) + gsm_dlci_begin_close(gsm->dlci[0]); + return; + } else if (gsm->keep_alive && gsm->dlci[0] && !gsm->dlci[0]->dead) { + if (gsm->ka_retries > 0) { + /* T2 expired for keep-alive -> resend */ + gsm->ka_retries--; + } else { + /* Start keep-alive timer */ + gsm->ka_num++; + if (!gsm->ka_num) + gsm->ka_num++; + gsm->ka_retries = (signed int)gsm->n2; + } + gsm_control_command(gsm, CMD_TEST, &gsm->ka_num, + sizeof(gsm->ka_num)); + mod_timer(&gsm->ka_timer, + jiffies + gsm->t2 * HZ / 100); } spin_unlock_irqrestore(&gsm->control_lock, flags); } @@ -2059,14 +2120,16 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) tty_port_tty_hangup(&dlci->port, false); gsm_dlci_clear_queues(dlci->gsm, dlci); /* Ensure that gsmtty_open() can return. */ - tty_port_set_initialized(&dlci->port, 0); + tty_port_set_initialized(&dlci->port, false); wake_up_interruptible(&dlci->port.open_wait); - } else + } else { + del_timer(&dlci->gsm->ka_timer); dlci->gsm->dead = true; + } /* A DLCI 0 close is a MUX termination so we need to kick that back to userspace somehow */ gsm_dlci_data_kick(dlci); - wake_up(&dlci->gsm->event); + wake_up_all(&dlci->gsm->event); } /** @@ -2078,6 +2141,8 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) static void gsm_dlci_open(struct gsm_dlci *dlci) { + struct gsm_mux *gsm = dlci->gsm; + /* Note that SABM UA .. SABM UA first UA lost can mean that we go open -> open */ del_timer(&dlci->t1); @@ -2087,8 +2152,15 @@ static void gsm_dlci_open(struct gsm_dlci *dlci) if (debug & DBG_ERRORS) pr_debug("DLCI %d goes open.\n", dlci->addr); /* Send current modem state */ - if (dlci->addr) + if (dlci->addr) { gsm_modem_update(dlci, 0); + } else { + /* Start keep-alive control */ + gsm->ka_num = 0; + gsm->ka_retries = -1; + mod_timer(&gsm->ka_timer, + jiffies + gsm->keep_alive * HZ / 100); + } gsm_dlci_data_kick(dlci); wake_up(&dlci->gsm->event); } @@ -2267,6 +2339,7 @@ static void gsm_dlci_begin_close(struct gsm_dlci *dlci) dlci->state = DLCI_CLOSING; gsm_command(dlci->gsm, dlci->addr, DISC|PF); mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); + wake_up_interruptible(&gsm->event); } /** @@ -2840,6 +2913,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc) /* Finish outstanding timers, making sure they are done */ del_timer_sync(&gsm->kick_timer); del_timer_sync(&gsm->t2_timer); + del_timer_sync(&gsm->ka_timer); /* Finish writing to ldisc */ flush_work(&gsm->tx_work); @@ -2987,6 +3061,7 @@ static struct gsm_mux *gsm_alloc_mux(void) INIT_LIST_HEAD(&gsm->tx_data_list); timer_setup(&gsm->kick_timer, gsm_kick_timer, 0); timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0); + timer_setup(&gsm->ka_timer, gsm_control_keep_alive, 0); INIT_WORK(&gsm->tx_work, gsmld_write_task); init_waitqueue_head(&gsm->event); spin_lock_init(&gsm->control_lock); @@ -3003,6 +3078,7 @@ static struct gsm_mux *gsm_alloc_mux(void) gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ gsm->mtu = 64; gsm->dead = true; /* Avoid early tty opens */ + gsm->keep_alive = 0; /* Disabled */ /* Store the instance to the mux array or abort if no space is * available. @@ -3138,6 +3214,29 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c) return 0; } +static void gsm_copy_config_ext_values(struct gsm_mux *gsm, + struct gsm_config_ext *ce) +{ + memset(ce, 0, sizeof(*ce)); + ce->keep_alive = gsm->keep_alive; +} + +static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce) +{ + unsigned int i; + + /* + * Check that userspace doesn't put stuff in here to prevent breakages + * in the future. + */ + for (i = 0; i < ARRAY_SIZE(ce->reserved); i++) + if (ce->reserved[i]) + return -EINVAL; + + gsm->keep_alive = ce->keep_alive; + return 0; +} + /** * gsmld_output - write to link * @gsm: our mux @@ -3456,6 +3555,7 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct gsm_config c; + struct gsm_config_ext ce; struct gsm_mux *gsm = tty->disc_data; unsigned int base; @@ -3472,6 +3572,15 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd, case GSMIOC_GETFIRST: base = mux_num_to_base(gsm); return put_user(base + 1, (__u32 __user *)arg); + case GSMIOC_GETCONF_EXT: + gsm_copy_config_ext_values(gsm, &ce); + if (copy_to_user((void __user *)arg, &ce, sizeof(ce))) + return -EFAULT; + return 0; + case GSMIOC_SETCONF_EXT: + if (copy_from_user(&ce, (void __user *)arg, sizeof(ce))) + return -EFAULT; + return gsm_config_ext(gsm, &ce); default: return n_tty_ioctl_helper(tty, cmd, arg); } @@ -3770,16 +3879,43 @@ static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk) return -EPROTONOSUPPORT; } -static int gsm_carrier_raised(struct tty_port *port) +/** + * gsm_wait_modem_change - wait for modem status line change + * @dlci: channel + * @mask: modem status line bits + * + * The function returns if: + * - any given modem status line bit changed + * - the wait event function got interrupted (e.g. by a signal) + * - the underlying DLCI was closed + * - the underlying ldisc device was removed + */ +static int gsm_wait_modem_change(struct gsm_dlci *dlci, u32 mask) +{ + struct gsm_mux *gsm = dlci->gsm; + u32 old = dlci->modem_rx; + int ret; + + ret = wait_event_interruptible(gsm->event, gsm->dead || + dlci->state != DLCI_OPEN || + (old ^ dlci->modem_rx) & mask); + if (gsm->dead) + return -ENODEV; + if (dlci->state != DLCI_OPEN) + return -EL2NSYNC; + return ret; +} + +static bool gsm_carrier_raised(struct tty_port *port) { struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); struct gsm_mux *gsm = dlci->gsm; /* Not yet open so no carrier info */ if (dlci->state != DLCI_OPEN) - return 0; + return false; if (debug & DBG_CD_ON) - return 1; + return true; /* * Basic mode with control channel in ADM mode may not respond @@ -3787,16 +3923,16 @@ static int gsm_carrier_raised(struct tty_port *port) */ if (gsm->encoding == GSM_BASIC_OPT && gsm->dlci[0]->mode == DLCI_MODE_ADM && !dlci->modem_rx) - return 1; + return true; return dlci->modem_rx & TIOCM_CD; } -static void gsm_dtr_rts(struct tty_port *port, int onoff) +static void gsm_dtr_rts(struct tty_port *port, bool active) { struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); unsigned int modem_tx = dlci->modem_tx; - if (onoff) + if (active) modem_tx |= TIOCM_DTR | TIOCM_RTS; else modem_tx &= ~(TIOCM_DTR | TIOCM_RTS); @@ -3880,7 +4016,7 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) dlci->modem_rx = 0; /* We could in theory open and close before we wait - eg if we get a DM straight back. This is ok as that will have caused a hangup */ - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); /* Start sending off SABM messages */ if (gsm->initiator) gsm_dlci_begin_open(dlci); @@ -4029,6 +4165,8 @@ static int gsmtty_ioctl(struct tty_struct *tty, gsm_destroy_network(dlci); mutex_unlock(&dlci->mutex); return 0; + case TIOCMIWAIT: + return gsm_wait_modem_change(dlci, (u32)arg); default: return -ENOIOCTLCMD; } diff --git a/drivers/tty/serial/8250/8250_dfl.c b/drivers/tty/serial/8250/8250_dfl.c new file mode 100644 index 000000000000..6c5ff019df4b --- /dev/null +++ b/drivers/tty/serial/8250/8250_dfl.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for FPGA UART + * + * Copyright (C) 2022 Intel Corporation. + * + * Authors: + * Ananda Ravuri <ananda.ravuri@intel.com> + * Matthew Gerlach <matthew.gerlach@linux.intel.com> + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/dfl.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/types.h> + +#include <linux/serial.h> +#include <linux/serial_8250.h> + +#define DFHv1_PARAM_ID_CLK_FRQ 0x2 +#define DFHv1_PARAM_ID_FIFO_LEN 0x3 + +#define DFHv1_PARAM_ID_REG_LAYOUT 0x4 +#define DFHv1_PARAM_REG_LAYOUT_WIDTH GENMASK_ULL(63, 32) +#define DFHv1_PARAM_REG_LAYOUT_SHIFT GENMASK_ULL(31, 0) + +struct dfl_uart { + int line; +}; + +static int dfh_get_u64_param_val(struct dfl_device *dfl_dev, int param_id, u64 *pval) +{ + size_t psize; + u64 *p; + + p = dfh_find_param(dfl_dev, param_id, &psize); + if (IS_ERR(p)) + return PTR_ERR(p); + + if (psize != sizeof(*pval)) + return -EINVAL; + + *pval = *p; + + return 0; +} + +static int dfl_uart_get_params(struct dfl_device *dfl_dev, struct uart_8250_port *uart) +{ + struct device *dev = &dfl_dev->dev; + u64 fifo_len, clk_freq, reg_layout; + u32 reg_width; + int ret; + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_CLK_FRQ, &clk_freq); + if (ret) + return dev_err_probe(dev, ret, "missing CLK_FRQ param\n"); + + uart->port.uartclk = clk_freq; + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_FIFO_LEN, &fifo_len); + if (ret) + return dev_err_probe(dev, ret, "missing FIFO_LEN param\n"); + + switch (fifo_len) { + case 32: + uart->port.type = PORT_ALTR_16550_F32; + break; + + case 64: + uart->port.type = PORT_ALTR_16550_F64; + break; + + case 128: + uart->port.type = PORT_ALTR_16550_F128; + break; + + default: + return dev_err_probe(dev, -EINVAL, "unsupported FIFO_LEN %llu\n", fifo_len); + } + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_REG_LAYOUT, ®_layout); + if (ret) + return dev_err_probe(dev, ret, "missing REG_LAYOUT param\n"); + + uart->port.regshift = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_SHIFT, reg_layout); + reg_width = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_WIDTH, reg_layout); + switch (reg_width) { + case 4: + uart->port.iotype = UPIO_MEM32; + break; + + case 2: + uart->port.iotype = UPIO_MEM16; + break; + + default: + return dev_err_probe(dev, -EINVAL, "unsupported reg-width %u\n", reg_width); + + } + + return 0; +} + +static int dfl_uart_probe(struct dfl_device *dfl_dev) +{ + struct device *dev = &dfl_dev->dev; + struct uart_8250_port uart = { }; + struct dfl_uart *dfluart; + int ret; + + uart.port.flags = UPF_IOREMAP; + uart.port.mapbase = dfl_dev->mmio_res.start; + uart.port.mapsize = resource_size(&dfl_dev->mmio_res); + + ret = dfl_uart_get_params(dfl_dev, &uart); + if (ret < 0) + return dev_err_probe(dev, ret, "failed uart feature walk\n"); + + if (dfl_dev->num_irqs == 1) + uart.port.irq = dfl_dev->irqs[0]; + + dfluart = devm_kzalloc(dev, sizeof(*dfluart), GFP_KERNEL); + if (!dfluart) + return -ENOMEM; + + dfluart->line = serial8250_register_8250_port(&uart); + if (dfluart->line < 0) + return dev_err_probe(dev, dfluart->line, "unable to register 8250 port.\n"); + + dev_set_drvdata(dev, dfluart); + + return 0; +} + +static void dfl_uart_remove(struct dfl_device *dfl_dev) +{ + struct dfl_uart *dfluart = dev_get_drvdata(&dfl_dev->dev); + + serial8250_unregister_port(dfluart->line); +} + +#define FME_FEATURE_ID_UART 0x24 + +static const struct dfl_device_id dfl_uart_ids[] = { + { FME_ID, FME_FEATURE_ID_UART }, + { } +}; +MODULE_DEVICE_TABLE(dfl, dfl_uart_ids); + +static struct dfl_driver dfl_uart_driver = { + .drv = { + .name = "dfl-uart", + }, + .id_table = dfl_uart_ids, + .probe = dfl_uart_probe, + .remove = dfl_uart_remove, +}; +module_dfl_driver(dfl_uart_driver); + +MODULE_DESCRIPTION("DFL Intel UART driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index f271becfc46c..0ebde0ab8167 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -136,11 +136,11 @@ static void __init init_port(struct earlycon_device *device) unsigned char c; unsigned int ier; - serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ + serial8250_early_out(port, UART_LCR, UART_LCR_WLEN8); /* 8n1 */ ier = serial8250_early_in(port, UART_IER); serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */ serial8250_early_out(port, UART_FCR, 0); /* no fifo */ - serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ + serial8250_early_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); if (port->uartclk) { divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 8e9f247590bd..c55be6fda0ca 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -24,6 +24,7 @@ #include <asm/io.h> #include "8250.h" +#include "8250_pcilib.h" /* * init function returns: @@ -89,28 +90,7 @@ static int setup_port(struct serial_private *priv, struct uart_8250_port *port, u8 bar, unsigned int offset, int regshift) { - struct pci_dev *dev = priv->dev; - - if (bar >= PCI_STD_NUM_BARS) - return -EINVAL; - - if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { - if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev)) - return -ENOMEM; - - port->port.iotype = UPIO_MEM; - port->port.iobase = 0; - port->port.mapbase = pci_resource_start(dev, bar) + offset; - port->port.membase = pcim_iomap_table(dev)[bar] + offset; - port->port.regshift = regshift; - } else { - port->port.iotype = UPIO_PORT; - port->port.iobase = pci_resource_start(dev, bar) + offset; - port->port.mapbase = 0; - port->port.membase = NULL; - port->port.regshift = 0; - } - return 0; + return serial8250_pci_setup_port(priv->dev, port, bar, offset, regshift); } /* @@ -5757,3 +5737,4 @@ module_pci_driver(serial_pci_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); MODULE_DEVICE_TABLE(pci, serial_pci_tbl); +MODULE_IMPORT_NS(SERIAL_8250_PCI); diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c new file mode 100644 index 000000000000..a3b25779d921 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Probe module for 8250/16550-type MCHP PCI serial ports. + * + * Based on drivers/tty/serial/8250/8250_pci.c, + * + * Copyright (C) 2022 Microchip Technology Inc., All Rights Reserved. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/units.h> +#include <linux/tty.h> + +#include <asm/byteorder.h> + +#include "8250.h" +#include "8250_pcilib.h" + +#define PCI_DEVICE_ID_EFAR_PCI12000 0xa002 +#define PCI_DEVICE_ID_EFAR_PCI11010 0xa012 +#define PCI_DEVICE_ID_EFAR_PCI11101 0xa022 +#define PCI_DEVICE_ID_EFAR_PCI11400 0xa032 +#define PCI_DEVICE_ID_EFAR_PCI11414 0xa042 + +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_4p 0x0001 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p012 0x0002 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p013 0x0003 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p023 0x0004 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p123 0x0005 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p01 0x0006 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p02 0x0007 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p03 0x0008 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p12 0x0009 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p13 0x000a +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p23 0x000b +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p0 0x000c +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p1 0x000d +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p2 0x000e +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 0x000f + +#define PCI_SUBDEVICE_ID_EFAR_PCI12000 PCI_DEVICE_ID_EFAR_PCI12000 +#define PCI_SUBDEVICE_ID_EFAR_PCI11010 PCI_DEVICE_ID_EFAR_PCI11010 +#define PCI_SUBDEVICE_ID_EFAR_PCI11101 PCI_DEVICE_ID_EFAR_PCI11101 +#define PCI_SUBDEVICE_ID_EFAR_PCI11400 PCI_DEVICE_ID_EFAR_PCI11400 +#define PCI_SUBDEVICE_ID_EFAR_PCI11414 PCI_DEVICE_ID_EFAR_PCI11414 + +#define UART_ACTV_REG 0x11 +#define UART_BLOCK_SET_ACTIVE BIT(0) + +#define UART_PCI_CTRL_REG 0x80 +#define UART_PCI_CTRL_SET_MULTIPLE_MSI BIT(4) +#define UART_PCI_CTRL_D3_CLK_ENABLE BIT(0) + +#define ADCL_CFG_REG 0x40 +#define ADCL_CFG_POL_SEL BIT(2) +#define ADCL_CFG_PIN_SEL BIT(1) +#define ADCL_CFG_EN BIT(0) + +#define UART_BIT_SAMPLE_CNT 16 +#define BAUD_CLOCK_DIV_INT_MSK GENMASK(31, 8) +#define ADCL_CFG_RTS_DELAY_MASK GENMASK(11, 8) +#define UART_CLOCK_DEFAULT (62500 * HZ_PER_KHZ) + +#define UART_WAKE_REG 0x8C +#define UART_WAKE_MASK_REG 0x90 +#define UART_WAKE_N_PIN BIT(2) +#define UART_WAKE_NCTS BIT(1) +#define UART_WAKE_INT BIT(0) +#define UART_WAKE_SRCS \ + (UART_WAKE_N_PIN | UART_WAKE_NCTS | UART_WAKE_INT) + +#define UART_BAUD_CLK_DIVISOR_REG 0x54 + +#define UART_RESET_REG 0x94 +#define UART_RESET_D3_RESET_DISABLE BIT(16) + +#define MAX_PORTS 4 +#define PORT_OFFSET 0x100 + +static const int logical_to_physical_port_idx[][MAX_PORTS] = { + {0, 1, 2, 3}, /* PCI12000, PCI11010, PCI11101, PCI11400, PCI11414 */ + {0, 1, 2, 3}, /* PCI4p */ + {0, 1, 2, -1}, /* PCI3p012 */ + {0, 1, 3, -1}, /* PCI3p013 */ + {0, 2, 3, -1}, /* PCI3p023 */ + {1, 2, 3, -1}, /* PCI3p123 */ + {0, 1, -1, -1}, /* PCI2p01 */ + {0, 2, -1, -1}, /* PCI2p02 */ + {0, 3, -1, -1}, /* PCI2p03 */ + {1, 2, -1, -1}, /* PCI2p12 */ + {1, 3, -1, -1}, /* PCI2p13 */ + {2, 3, -1, -1}, /* PCI2p23 */ + {0, -1, -1, -1}, /* PCI1p0 */ + {1, -1, -1, -1}, /* PCI1p1 */ + {2, -1, -1, -1}, /* PCI1p2 */ + {3, -1, -1, -1}, /* PCI1p3 */ +}; + +struct pci1xxxx_8250 { + unsigned int nr; + void __iomem *membase; + int line[]; +}; + +static int pci1xxxx_get_num_ports(struct pci_dev *dev) +{ + switch (dev->subsystem_device) { + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p0: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p1: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p2: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3: + case PCI_SUBDEVICE_ID_EFAR_PCI12000: + case PCI_SUBDEVICE_ID_EFAR_PCI11010: + case PCI_SUBDEVICE_ID_EFAR_PCI11101: + case PCI_SUBDEVICE_ID_EFAR_PCI11400: + default: + return 1; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p01: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p02: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p03: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p12: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p13: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p23: + return 2; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p012: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p123: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p013: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p023: + return 3; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_4p: + case PCI_SUBDEVICE_ID_EFAR_PCI11414: + return 4; + } +} + +static unsigned int pci1xxxx_get_divisor(struct uart_port *port, + unsigned int baud, unsigned int *frac) +{ + unsigned int quot; + + /* + * Calculate baud rate sampling period in nanoseconds. + * Fractional part x denotes x/255 parts of a nanosecond. + */ + quot = NSEC_PER_SEC / (baud * UART_BIT_SAMPLE_CNT); + *frac = (NSEC_PER_SEC - quot * baud * UART_BIT_SAMPLE_CNT) * + 255 / UART_BIT_SAMPLE_CNT / baud; + + return quot; +} + +static void pci1xxxx_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int frac) +{ + writel(FIELD_PREP(BAUD_CLOCK_DIV_INT_MSK, quot) | frac, + port->membase + UART_BAUD_CLK_DIVISOR_REG); +} + +static int pci1xxxx_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + u32 delay_in_baud_periods; + u32 baud_period_in_ns; + u32 mode_cfg = 0; + u32 clock_div; + + /* + * pci1xxxx's uart hardware supports only RTS delay after + * Tx and in units of bit times to a maximum of 15 + */ + if (rs485->flags & SER_RS485_ENABLED) { + mode_cfg = ADCL_CFG_EN | ADCL_CFG_PIN_SEL; + + if (!(rs485->flags & SER_RS485_RTS_ON_SEND)) + mode_cfg |= ADCL_CFG_POL_SEL; + + if (rs485->delay_rts_after_send) { + clock_div = readl(port->membase + UART_BAUD_CLK_DIVISOR_REG); + baud_period_in_ns = + FIELD_GET(BAUD_CLOCK_DIV_INT_MSK, clock_div) * + UART_BIT_SAMPLE_CNT; + delay_in_baud_periods = + rs485->delay_rts_after_send * NSEC_PER_MSEC / + baud_period_in_ns; + delay_in_baud_periods = + min_t(u32, delay_in_baud_periods, + FIELD_MAX(ADCL_CFG_RTS_DELAY_MASK)); + mode_cfg |= FIELD_PREP(ADCL_CFG_RTS_DELAY_MASK, + delay_in_baud_periods); + rs485->delay_rts_after_send = + baud_period_in_ns * delay_in_baud_periods / + NSEC_PER_MSEC; + } + } + writel(mode_cfg, port->membase + ADCL_CFG_REG); + return 0; +} + +static const struct serial_rs485 pci1xxxx_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, + .delay_rts_after_send = 1, + /* Delay RTS before send is not supported */ +}; + +static bool pci1xxxx_port_suspend(int line) +{ + struct uart_8250_port *up = serial8250_get_port(line); + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + unsigned long flags; + bool ret = false; + u8 wakeup_mask; + + mutex_lock(&tport->mutex); + if (port->suspended == 0 && port->dev) { + wakeup_mask = readb(up->port.membase + UART_WAKE_MASK_REG); + + spin_lock_irqsave(&port->lock, flags); + port->mctrl &= ~TIOCM_OUT2; + port->ops->set_mctrl(port, port->mctrl); + spin_unlock_irqrestore(&port->lock, flags); + + ret = (wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS; + } + + writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG); + mutex_unlock(&tport->mutex); + + return ret; +} + +static void pci1xxxx_port_resume(int line) +{ + struct uart_8250_port *up = serial8250_get_port(line); + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + unsigned long flags; + + mutex_lock(&tport->mutex); + writeb(UART_BLOCK_SET_ACTIVE, port->membase + UART_ACTV_REG); + writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG); + + if (port->suspended == 0) { + spin_lock_irqsave(&port->lock, flags); + port->mctrl |= TIOCM_OUT2; + port->ops->set_mctrl(port, port->mctrl); + spin_unlock_irqrestore(&port->lock, flags); + } + mutex_unlock(&tport->mutex); +} + +static int pci1xxxx_suspend(struct device *dev) +{ + struct pci1xxxx_8250 *priv = dev_get_drvdata(dev); + struct pci_dev *pcidev = to_pci_dev(dev); + bool wakeup = false; + unsigned int data; + void __iomem *p; + int i; + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) { + serial8250_suspend_port(priv->line[i]); + wakeup |= pci1xxxx_port_suspend(priv->line[i]); + } + } + + p = pci_ioremap_bar(pcidev, 0); + if (!p) { + dev_err(dev, "remapping of bar 0 memory failed"); + return -ENOMEM; + } + + data = readl(p + UART_RESET_REG); + writel(data | UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG); + + if (wakeup) + writeb(UART_PCI_CTRL_D3_CLK_ENABLE, p + UART_PCI_CTRL_REG); + + iounmap(p); + device_set_wakeup_enable(dev, true); + pci_wake_from_d3(pcidev, true); + + return 0; +} + +static int pci1xxxx_resume(struct device *dev) +{ + struct pci1xxxx_8250 *priv = dev_get_drvdata(dev); + struct pci_dev *pcidev = to_pci_dev(dev); + unsigned int data; + void __iomem *p; + int i; + + p = pci_ioremap_bar(pcidev, 0); + if (!p) { + dev_err(dev, "remapping of bar 0 memory failed"); + return -ENOMEM; + } + + data = readl(p + UART_RESET_REG); + writel(data & ~UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG); + iounmap(p); + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) { + pci1xxxx_port_resume(priv->line[i]); + serial8250_resume_port(priv->line[i]); + } + } + + return 0; +} + +static int pci1xxxx_setup(struct pci_dev *pdev, + struct uart_8250_port *port, int port_idx) +{ + int ret; + + port->port.flags |= UPF_FIXED_TYPE | UPF_SKIP_TEST; + port->port.type = PORT_MCHP16550A; + port->port.set_termios = serial8250_do_set_termios; + port->port.get_divisor = pci1xxxx_get_divisor; + port->port.set_divisor = pci1xxxx_set_divisor; + port->port.rs485_config = pci1xxxx_rs485_config; + port->port.rs485_supported = pci1xxxx_rs485_supported; + + ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0); + if (ret < 0) + return ret; + + writeb(UART_BLOCK_SET_ACTIVE, port->port.membase + UART_ACTV_REG); + writeb(UART_WAKE_SRCS, port->port.membase + UART_WAKE_REG); + writeb(UART_WAKE_N_PIN, port->port.membase + UART_WAKE_MASK_REG); + + return 0; +} + +static unsigned int pci1xxxx_get_max_port(int subsys_dev) +{ + unsigned int i = MAX_PORTS; + + if (subsys_dev < ARRAY_SIZE(logical_to_physical_port_idx)) + while (i--) { + if (logical_to_physical_port_idx[subsys_dev][i] != -1) + return logical_to_physical_port_idx[subsys_dev][i] + 1; + } + + if (subsys_dev == PCI_SUBDEVICE_ID_EFAR_PCI11414) + return 4; + + return 1; +} + +static int pci1xxxx_logical_to_physical_port_translate(int subsys_dev, int port) +{ + if (subsys_dev < ARRAY_SIZE(logical_to_physical_port_idx)) + return logical_to_physical_port_idx[subsys_dev][port]; + + return logical_to_physical_port_idx[0][port]; +} + +static int pci1xxxx_serial_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct pci1xxxx_8250 *priv; + struct uart_8250_port uart; + unsigned int max_vec_reqd; + unsigned int nr_ports, i; + int num_vectors; + int subsys_dev; + int port_idx; + int rc; + + rc = pcim_enable_device(pdev); + if (rc) + return rc; + + nr_ports = pci1xxxx_get_num_ports(pdev); + + priv = devm_kzalloc(dev, struct_size(priv, line, nr_ports), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->membase = pci_ioremap_bar(pdev, 0); + if (!priv->membase) + return -ENOMEM; + + pci_set_master(pdev); + + priv->nr = nr_ports; + + subsys_dev = pdev->subsystem_device; + max_vec_reqd = pci1xxxx_get_max_port(subsys_dev); + + num_vectors = pci_alloc_irq_vectors(pdev, 1, max_vec_reqd, PCI_IRQ_ALL_TYPES); + if (num_vectors < 0) { + pci_iounmap(pdev, priv->membase); + return num_vectors; + } + + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT; + uart.port.uartclk = UART_CLOCK_DEFAULT; + uart.port.dev = dev; + + if (num_vectors == max_vec_reqd) + writeb(UART_PCI_CTRL_SET_MULTIPLE_MSI, priv->membase + UART_PCI_CTRL_REG); + + for (i = 0; i < nr_ports; i++) { + priv->line[i] = -ENODEV; + + port_idx = pci1xxxx_logical_to_physical_port_translate(subsys_dev, i); + + if (num_vectors == max_vec_reqd) + uart.port.irq = pci_irq_vector(pdev, port_idx); + else + uart.port.irq = pci_irq_vector(pdev, 0); + + rc = pci1xxxx_setup(pdev, &uart, port_idx); + if (rc) { + dev_warn(dev, "Failed to setup port %u\n", i); + continue; + } + + priv->line[i] = serial8250_register_8250_port(&uart); + if (priv->line[i] < 0) { + dev_warn(dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype, + priv->line[i]); + } + } + + pci_set_drvdata(pdev, priv); + + return 0; +} + +static void pci1xxxx_serial_remove(struct pci_dev *dev) +{ + struct pci1xxxx_8250 *priv = pci_get_drvdata(dev); + unsigned int i; + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) + serial8250_unregister_port(priv->line[i]); + } + + pci_free_irq_vectors(dev); + pci_iounmap(dev, priv->membase); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_pm_ops, pci1xxxx_suspend, pci1xxxx_resume); + +static const struct pci_device_id pci1xxxx_pci_tbl[] = { + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11010) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11101) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11400) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11414) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI12000) }, + {} +}; +MODULE_DEVICE_TABLE(pci, pci1xxxx_pci_tbl); + +static struct pci_driver pci1xxxx_pci_driver = { + .name = "pci1xxxx serial", + .probe = pci1xxxx_serial_probe, + .remove = pci1xxxx_serial_remove, + .driver = { + .pm = pm_sleep_ptr(&pci1xxxx_pm_ops), + }, + .id_table = pci1xxxx_pci_tbl, +}; +module_pci_driver(pci1xxxx_pci_driver); + +static_assert((ARRAY_SIZE(logical_to_physical_port_idx) == PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 + 1)); + +MODULE_IMPORT_NS(SERIAL_8250_PCI); +MODULE_DESCRIPTION("Microchip Technology Inc. PCIe to UART module"); +MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>"); +MODULE_AUTHOR("Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pcilib.c b/drivers/tty/serial/8250/8250_pcilib.c new file mode 100644 index 000000000000..d234e9194feb --- /dev/null +++ b/drivers/tty/serial/8250/8250_pcilib.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 8250 PCI library. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + */ +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/types.h> + +#include "8250.h" +#include "8250_pcilib.h" + +int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, + u8 bar, unsigned int offset, int regshift) +{ + if (bar >= PCI_STD_NUM_BARS) + return -EINVAL; + + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev)) + return -ENOMEM; + + port->port.iotype = UPIO_MEM; + port->port.iobase = 0; + port->port.mapbase = pci_resource_start(dev, bar) + offset; + port->port.membase = pcim_iomap_table(dev)[bar] + offset; + port->port.regshift = regshift; + } else { + port->port.iotype = UPIO_PORT; + port->port.iobase = pci_resource_start(dev, bar) + offset; + port->port.mapbase = 0; + port->port.membase = NULL; + port->port.regshift = 0; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(serial8250_pci_setup_port, SERIAL_8250_PCI); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pcilib.h b/drivers/tty/serial/8250/8250_pcilib.h new file mode 100644 index 000000000000..1aaf1b50ce9c --- /dev/null +++ b/drivers/tty/serial/8250/8250_pcilib.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * 8250 PCI library header file. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + */ + +#include <linux/types.h> + +struct pci_dev; + +struct uart_8250_port; + +int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, u8 bar, + unsigned int offset, int regshift); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index beba8f38b3dc..fa43df05342b 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -313,6 +313,14 @@ static const struct serial8250_config uart_config[] = { .rxtrig_bytes = {1, 4, 8, 14}, .flags = UART_CAP_FIFO, }, + [PORT_MCHP16550A] = { + .name = "MCHP16550A", + .fifo_size = 256, + .tx_loadsz = 256, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01, + .rxtrig_bytes = {2, 66, 130, 194}, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -1050,11 +1058,12 @@ static void autoconfig_16550a(struct uart_8250_port *up) serial_out(up, UART_LCR, 0); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status1 = serial_in(up, UART_IIR) >> 5; + status1 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO | + UART_IIR_FIFO_ENABLED); serial_out(up, UART_FCR, 0); serial_out(up, UART_LCR, 0); - if (status1 == 7) + if (status1 == (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED)) up->port.type = PORT_16550A_FSL64; else DEBUG_AUTOCONF("Motorola 8xxx DUART "); @@ -1122,17 +1131,20 @@ static void autoconfig_16550a(struct uart_8250_port *up) */ serial_out(up, UART_LCR, 0); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status1 = serial_in(up, UART_IIR) >> 5; + status1 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status2 = serial_in(up, UART_IIR) >> 5; + status2 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED); serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_LCR, 0); DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2); - if (status1 == 6 && status2 == 7) { + if (status1 == UART_IIR_FIFO_ENABLED_16550A && + status2 == (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED_16550A)) { up->port.type = PORT_16750; up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP; return; @@ -1236,14 +1248,14 @@ static void autoconfig(struct uart_8250_port *up) * Mask out IER[7:4] bits for test as some UARTs (e.g. TL * 16C754B) allow only to modify them if an EFR bit is set. */ - scratch2 = serial_in(up, UART_IER) & 0x0f; - serial_out(up, UART_IER, 0x0F); + scratch2 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; + serial_out(up, UART_IER, UART_IER_ALL_INTR); #ifdef __i386__ outb(0, 0x080); #endif - scratch3 = serial_in(up, UART_IER) & 0x0f; + scratch3 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; serial_out(up, UART_IER, scratch); - if (scratch2 != 0 || scratch3 != 0x0F) { + if (scratch2 != 0 || scratch3 != UART_IER_ALL_INTR) { /* * We failed; there's nothing here */ @@ -1267,10 +1279,10 @@ static void autoconfig(struct uart_8250_port *up) * that conflicts with COM 1-4 --- we hope! */ if (!(port->flags & UPF_SKIP_TEST)) { - serial8250_out_MCR(up, UART_MCR_LOOP | 0x0A); - status1 = serial_in(up, UART_MSR) & 0xF0; + serial8250_out_MCR(up, UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_RTS); + status1 = serial_in(up, UART_MSR) & UART_MSR_STATUS_BITS; serial8250_out_MCR(up, save_mcr); - if (status1 != 0x90) { + if (status1 != (UART_MSR_DCD | UART_MSR_CTS)) { spin_unlock_irqrestore(&port->lock, flags); DEBUG_AUTOCONF("LOOP test failed (%02x) ", status1); @@ -1293,22 +1305,19 @@ static void autoconfig(struct uart_8250_port *up) serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); - /* Assign this as it is to truncate any bits above 7. */ - scratch = serial_in(up, UART_IIR); - - switch (scratch >> 6) { - case 0: + switch (serial_in(up, UART_IIR) & UART_IIR_FIFO_ENABLED) { + case UART_IIR_FIFO_ENABLED_8250: autoconfig_8250(up); break; - case 1: - port->type = PORT_UNKNOWN; - break; - case 2: + case UART_IIR_FIFO_ENABLED_16550: port->type = PORT_16550; break; - case 3: + case UART_IIR_FIFO_ENABLED_16550A: autoconfig_16550a(up); break; + default: + port->type = PORT_UNKNOWN; + break; } #ifdef CONFIG_SERIAL_8250_RSA @@ -1394,7 +1403,7 @@ static void autoconfig_irq(struct uart_8250_port *up) serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); } - serial_out(up, UART_IER, 0x0f); /* enable all intrs */ + serial_out(up, UART_IER, UART_IER_ALL_INTR); serial_in(up, UART_LSR); serial_in(up, UART_RX); serial_in(up, UART_IIR); @@ -1511,8 +1520,6 @@ static inline void __stop_tx(struct uart_8250_port *p) u16 lsr = serial_lsr_in(p); u64 stop_delay = 0; - p->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if (!(lsr & UART_LSR_THRE)) return; /* diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index b0f62345bc84..978dc196c29b 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -129,9 +129,13 @@ config SERIAL_8250_DMA This builds DMA support that can be used with 8250/16650 compatible UART controllers that support DMA signaling. +config SERIAL_8250_PCILIB + bool + config SERIAL_8250_PCI tristate "8250/16550 PCI device support" depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB default SERIAL_8250 help This builds standard PCI serial support. You may be able to @@ -291,6 +295,17 @@ config SERIAL_8250_HUB6 To compile this driver as a module, choose M here: the module will be called 8250_hub6. +config SERIAL_8250_PCI1XXXX + tristate "Microchip 8250 based serial port" + depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB + default SERIAL_8250 + help + Select this option if you have a setup with Microchip PCIe + Switch with serial port enabled and wish to enable 8250 + serial driver for the serial interface. This driver support + will ensure to support baud rates upto 1.5Mpbs. + # # Misc. options/drivers. # @@ -370,6 +385,18 @@ config SERIAL_8250_FSL erratum for Freescale 16550 UARTs in the 8250 driver. It also enables support for ACPI enumeration. +config SERIAL_8250_DFL + tristate "DFL bus driver for Altera 16550 UART" + depends on SERIAL_8250 && FPGA_DFL + help + This option enables support for a Device Feature List (DFL) bus + driver for the Altera 16550 UART. One or more Altera 16550 UARTs + can be instantiated in a FPGA and then be discovered during + enumeration of the DFL bus. + + To compile this driver as a module, chose M here: the + module will be called 8250_dfl. + config SERIAL_8250_DW tristate "Support for Synopsys DesignWare 8250 quirks" depends on SERIAL_8250 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 1615bfdde2a0..4fc2fc1f41b6 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o 8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o 8250_base-$(CONFIG_SERIAL_8250_DWLIB) += 8250_dwlib.o 8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o +8250_base-$(CONFIG_SERIAL_8250_PCILIB) += 8250_pcilib.o obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o @@ -26,8 +27,10 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o +obj-$(CONFIG_SERIAL_8250_PCI1XXXX) += 8250_pci1xxxx.o obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o +obj-$(CONFIG_SERIAL_8250_DFL) += 8250_dfl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.o diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index ed0672d2d0ef..625358f44419 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -73,17 +73,17 @@ config SERIAL_AMBA_PL011_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) -config SERIAL_EARLYCON_ARM_SEMIHOST - bool "Early console using ARM semihosting" - depends on ARM64 || ARM +config SERIAL_EARLYCON_SEMIHOST + bool "Early console using Arm compatible semihosting" + depends on ARM64 || ARM || RISCV select SERIAL_CORE select SERIAL_CORE_CONSOLE select SERIAL_EARLYCON help - Support for early debug console using ARM semihosting. This enables - the console before standard serial driver is probed. This is enabled - with "earlycon=smh" on the kernel command line. The console is - enabled when early_param is processed. + Support for early debug console using Arm compatible semihosting. + This enables the console before standard serial driver is probed. + This is enabled with "earlycon=smh" on the kernel command line. + The console is enabled when early_param is processed. config SERIAL_EARLYCON_RISCV_SBI bool "Early console using RISC-V SBI" @@ -1507,7 +1507,7 @@ config SERIAL_MILBEAUT_USIO_CONSOLE config SERIAL_LITEUART tristate "LiteUART serial port support" depends on HAS_IOMEM - depends on OF || COMPILE_TEST + depends on OF depends on LITEX || COMPILE_TEST select SERIAL_CORE help diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 238a9557b487..cd9afd9e3018 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o -obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o +obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o # These Sparc drivers have to appear before others such as 8250 diff --git a/drivers/tty/serial/earlycon-arm-semihost.c b/drivers/tty/serial/earlycon-semihost.c index fcdec5f42376..e4692a8433f9 100644 --- a/drivers/tty/serial/earlycon-arm-semihost.c +++ b/drivers/tty/serial/earlycon-semihost.c @@ -11,30 +11,7 @@ #include <linux/console.h> #include <linux/init.h> #include <linux/serial_core.h> - -#ifdef CONFIG_THUMB2_KERNEL -#define SEMIHOST_SWI "0xab" -#else -#define SEMIHOST_SWI "0x123456" -#endif - -/* - * Semihosting-based debug console - */ -static void smh_putc(struct uart_port *port, unsigned char c) -{ -#ifdef CONFIG_ARM64 - asm volatile("mov x1, %0\n" - "mov x0, #3\n" - "hlt 0xf000\n" - : : "r" (&c) : "x0", "x1", "memory"); -#else - asm volatile("mov r1, %0\n" - "mov r0, #3\n" - "svc " SEMIHOST_SWI "\n" - : : "r" (&c) : "r0", "r1", "memory"); -#endif -} +#include <asm/semihost.h> static void smh_write(struct console *con, const char *s, unsigned n) { diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 4f6e9bf57169..a5fbb6ed38ae 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -120,7 +120,13 @@ static int __init parse_options(struct earlycon_device *device, char *options) } if (options) { + char *uartclk; + device->baud = simple_strtoul(options, NULL, 0); + uartclk = strchr(options, ','); + if (uartclk && kstrtouint(uartclk + 1, 0, &port->uartclk) < 0) + pr_warn("[%s] unsupported earlycon uart clkrate option\n", + options); length = min(strcspn(options, " ") + 1, (size_t)(sizeof(device->options))); strscpy(device->options, options, length); @@ -139,7 +145,8 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match) buf = NULL; spin_lock_init(&port->lock); - port->uartclk = BASE_BAUD * 16; + if (!port->uartclk) + port->uartclk = BASE_BAUD * 16; if (port->mapbase) port->membase = earlycon_map(port->mapbase, 64); diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 5e69fb73f570..e945f41b93d4 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -5,6 +5,8 @@ * Copyright 2012-2014 Freescale Semiconductor, Inc. */ +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/clk.h> #include <linux/console.h> #include <linux/delay.h> @@ -181,7 +183,7 @@ #define UARTCTRL_SBK 0x00010000 #define UARTCTRL_MA1IE 0x00008000 #define UARTCTRL_MA2IE 0x00004000 -#define UARTCTRL_IDLECFG 0x00000100 +#define UARTCTRL_IDLECFG GENMASK(10, 8) #define UARTCTRL_LOOPS 0x00000080 #define UARTCTRL_DOZEEN 0x00000040 #define UARTCTRL_RSRC 0x00000020 @@ -199,6 +201,7 @@ #define UARTDATA_MASK 0x3ff #define UARTMODIR_IREN 0x00020000 +#define UARTMODIR_RTSWATER GENMASK(10, 8) #define UARTMODIR_TXCTSSRC 0x00000020 #define UARTMODIR_TXCTSC 0x00000010 #define UARTMODIR_RXRTSE 0x00000008 @@ -212,6 +215,7 @@ #define UARTFIFO_RXUF 0x00010000 #define UARTFIFO_TXFLUSH 0x00008000 #define UARTFIFO_RXFLUSH 0x00004000 +#define UARTFIFO_RXIDEN GENMASK(12, 10) #define UARTFIFO_TXOFE 0x00000200 #define UARTFIFO_RXUFE 0x00000100 #define UARTFIFO_TXFE 0x00000080 @@ -238,7 +242,7 @@ #define DRIVER_NAME "fsl-lpuart" #define DEV_NAME "ttyLP" -#define UART_NR 6 +#define UART_NR 8 /* IMX lpuart has four extra unused regs located at the beginning */ #define IMX_REG_OFF 0x10 @@ -248,6 +252,7 @@ enum lpuart_type { LS1021A_LPUART, LS1028A_LPUART, IMX7ULP_LPUART, + IMX8ULP_LPUART, IMX8QXP_LPUART, IMXRT1050_LPUART, }; @@ -260,6 +265,7 @@ struct lpuart_port { unsigned int txfifo_size; unsigned int rxfifo_size; + u8 rx_watermark; bool lpuart_dma_tx_use; bool lpuart_dma_rx_use; struct dma_chan *dma_tx_chan; @@ -286,38 +292,52 @@ struct lpuart_soc_data { enum lpuart_type devtype; char iotype; u8 reg_off; + u8 rx_watermark; }; static const struct lpuart_soc_data vf_data = { .devtype = VF610_LPUART, .iotype = UPIO_MEM, + .rx_watermark = 1, }; static const struct lpuart_soc_data ls1021a_data = { .devtype = LS1021A_LPUART, .iotype = UPIO_MEM32BE, + .rx_watermark = 1, }; static const struct lpuart_soc_data ls1028a_data = { .devtype = LS1028A_LPUART, .iotype = UPIO_MEM32, + .rx_watermark = 1, }; static struct lpuart_soc_data imx7ulp_data = { .devtype = IMX7ULP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, +}; + +static struct lpuart_soc_data imx8ulp_data = { + .devtype = IMX8ULP_LPUART, + .iotype = UPIO_MEM32, + .reg_off = IMX_REG_OFF, + .rx_watermark = 3, }; static struct lpuart_soc_data imx8qxp_data = { .devtype = IMX8QXP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 31, }; static struct lpuart_soc_data imxrt1050_data = { .devtype = IMXRT1050_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, }; static const struct of_device_id lpuart_dt_ids[] = { @@ -325,6 +345,7 @@ static const struct of_device_id lpuart_dt_ids[] = { { .compatible = "fsl,ls1021a-lpuart", .data = &ls1021a_data, }, { .compatible = "fsl,ls1028a-lpuart", .data = &ls1028a_data, }, { .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, }, + { .compatible = "fsl,imx8ulp-lpuart", .data = &imx8ulp_data, }, { .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, }, { .compatible = "fsl,imxrt1050-lpuart", .data = &imxrt1050_data}, { /* sentinel */ } @@ -345,6 +366,11 @@ static inline bool is_imx7ulp_lpuart(struct lpuart_port *sport) return sport->devtype == IMX7ULP_LPUART; } +static inline bool is_imx8ulp_lpuart(struct lpuart_port *sport) +{ + return sport->devtype == IMX8ULP_LPUART; +} + static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport) { return sport->devtype == IMX8QXP_LPUART; @@ -1387,9 +1413,9 @@ static int lpuart32_config_rs485(struct uart_port *port, struct ktermios *termio * Note: UART is assumed to be active high. */ if (rs485->flags & SER_RS485_RTS_ON_SEND) - modem &= ~UARTMODEM_TXRTSPOL; - else if (rs485->flags & SER_RS485_RTS_AFTER_SEND) modem |= UARTMODEM_TXRTSPOL; + else if (rs485->flags & SER_RS485_RTS_AFTER_SEND) + modem &= ~UARTMODEM_TXRTSPOL; } lpuart32_write(&sport->port, modem, UARTMODIR); @@ -1462,12 +1488,32 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state) static void lpuart32_break_ctl(struct uart_port *port, int break_state) { - unsigned long temp; + unsigned long temp, modem; + struct tty_struct *tty; + unsigned int cflag = 0; + + tty = tty_port_tty_get(&port->state->port); + if (tty) { + cflag = tty->termios.c_cflag; + tty_kref_put(tty); + } temp = lpuart32_read(port, UARTCTRL) & ~UARTCTRL_SBK; + modem = lpuart32_read(port, UARTMODIR); - if (break_state != 0) + if (break_state != 0) { temp |= UARTCTRL_SBK; + /* + * LPUART CTS has higher priority than SBK, need to disable CTS before + * asserting SBK to avoid any interference if flow control is enabled. + */ + if (cflag & CRTSCTS && modem & UARTMODIR_TXCTSE) + lpuart32_write(port, modem & ~UARTMODIR_TXCTSE, UARTMODIR); + } else { + /* Re-enable the CTS when break off. */ + if (cflag & CRTSCTS && !(modem & UARTMODIR_TXCTSE)) + lpuart32_write(port, modem | UARTMODIR_TXCTSE, UARTMODIR); + } lpuart32_write(port, temp, UARTCTRL); } @@ -1497,8 +1543,10 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); } + if (uart_console(&sport->port)) + sport->rx_watermark = 1; writeb(0, sport->port.membase + UARTTWFIFO); - writeb(1, sport->port.membase + UARTRWFIFO); + writeb(sport->rx_watermark, sport->port.membase + UARTRWFIFO); /* Restore cr2 */ writeb(cr2_saved, sport->port.membase + UARTCR2); @@ -1523,19 +1571,30 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) ctrl = lpuart32_read(&sport->port, UARTCTRL); ctrl_saved = ctrl; ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE | - UARTCTRL_RIE | UARTCTRL_RE); + UARTCTRL_RIE | UARTCTRL_RE | UARTCTRL_ILIE); lpuart32_write(&sport->port, ctrl, UARTCTRL); /* enable FIFO mode */ val = lpuart32_read(&sport->port, UARTFIFO); val |= UARTFIFO_TXFE | UARTFIFO_RXFE; val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; + val |= FIELD_PREP(UARTFIFO_RXIDEN, 0x3); lpuart32_write(&sport->port, val, UARTFIFO); /* set the watermark */ - val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); + if (uart_console(&sport->port)) + sport->rx_watermark = 1; + val = (sport->rx_watermark << UARTWATER_RXWATER_OFF) | + (0x0 << UARTWATER_TXWATER_OFF); lpuart32_write(&sport->port, val, UARTWATER); + /* set RTS watermark */ + if (!uart_console(&sport->port)) { + val = lpuart32_read(&sport->port, UARTMODIR); + val |= FIELD_PREP(UARTMODIR_RTSWATER, sport->rxfifo_size >> 1); + lpuart32_write(&sport->port, val, UARTMODIR); + } + /* Restore cr2 */ lpuart32_write(&sport->port, ctrl_saved, UARTCTRL); } @@ -1547,7 +1606,8 @@ static void lpuart32_setup_watermark_enable(struct lpuart_port *sport) lpuart32_setup_watermark(sport); temp = lpuart32_read(&sport->port, UARTCTRL); - temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE; + temp |= UARTCTRL_RE | UARTCTRL_TE; + temp |= FIELD_PREP(UARTCTRL_IDLECFG, 0x7); lpuart32_write(&sport->port, temp, UARTCTRL); } @@ -1679,19 +1739,23 @@ static int lpuart_startup(struct uart_port *port) return 0; } +static void lpuart32_hw_disable(struct lpuart_port *sport) +{ + unsigned long temp; + + temp = lpuart32_read(&sport->port, UARTCTRL); + temp &= ~(UARTCTRL_RIE | UARTCTRL_ILIE | UARTCTRL_RE | + UARTCTRL_TIE | UARTCTRL_TE); + lpuart32_write(&sport->port, temp, UARTCTRL); +} + static void lpuart32_configure(struct lpuart_port *sport) { unsigned long temp; - if (sport->lpuart_dma_rx_use) { - /* RXWATER must be 0 */ - temp = lpuart32_read(&sport->port, UARTWATER); - temp &= ~(UARTWATER_WATER_MASK << UARTWATER_RXWATER_OFF); - lpuart32_write(&sport->port, temp, UARTWATER); - } temp = lpuart32_read(&sport->port, UARTCTRL); if (!sport->lpuart_dma_rx_use) - temp |= UARTCTRL_RIE; + temp |= UARTCTRL_RIE | UARTCTRL_ILIE; if (!sport->lpuart_dma_tx_use) temp |= UARTCTRL_TIE; lpuart32_write(&sport->port, temp, UARTCTRL); @@ -1703,11 +1767,12 @@ static void lpuart32_hw_setup(struct lpuart_port *sport) spin_lock_irqsave(&sport->port.lock, flags); - lpuart32_setup_watermark_enable(sport); + lpuart32_hw_disable(sport); lpuart_rx_dma_startup(sport); lpuart_tx_dma_startup(sport); + lpuart32_setup_watermark_enable(sport); lpuart32_configure(sport); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1796,10 +1861,19 @@ static void lpuart32_shutdown(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); - /* disable Rx/Tx and interrupts */ + /* clear status */ + temp = lpuart32_read(&sport->port, UARTSTAT); + lpuart32_write(&sport->port, temp, UARTSTAT); + + /* disable Rx/Tx DMA */ + temp = lpuart32_read(port, UARTBAUD); + temp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); + lpuart32_write(port, temp, UARTBAUD); + + /* disable Rx/Tx and interrupts and break condition */ temp = lpuart32_read(port, UARTCTRL); - temp &= ~(UARTCTRL_TE | UARTCTRL_RE | - UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); + temp &= ~(UARTCTRL_TE | UARTCTRL_RE | UARTCTRL_ILIE | + UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE | UARTCTRL_SBK); lpuart32_write(port, temp, UARTCTRL); spin_unlock_irqrestore(&port->lock, flags); @@ -2631,7 +2705,7 @@ static int lpuart_global_reset(struct lpuart_port *sport) return ret; } - if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) { + if (is_imx7ulp_lpuart(sport) || is_imx8ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) { /* * If the transmitter is used by earlycon, wait for transmit engine to * complete and then reset. @@ -2688,6 +2762,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; sport->devtype = sdata->devtype; + sport->rx_watermark = sdata->rx_watermark; ret = platform_get_irq(pdev, 0); if (ret < 0) return ret; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 757825edb0cd..523f296d5747 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -210,12 +210,8 @@ struct imx_port { struct mctrl_gpios *gpios; - /* shadow registers */ - unsigned int ucr1; - unsigned int ucr2; - unsigned int ucr3; - unsigned int ucr4; - unsigned int ufcr; + /* counter to stop 0xff flood */ + int idle_counter; /* DMA fields */ unsigned int dma_is_enabled:1; @@ -273,59 +269,14 @@ static const struct of_device_id imx_uart_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_uart_dt_ids); -static void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset) -{ - switch (offset) { - case UCR1: - sport->ucr1 = val; - break; - case UCR2: - sport->ucr2 = val; - break; - case UCR3: - sport->ucr3 = val; - break; - case UCR4: - sport->ucr4 = val; - break; - case UFCR: - sport->ufcr = val; - break; - default: - break; - } +static inline void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset) +{ writel(val, sport->port.membase + offset); } -static u32 imx_uart_readl(struct imx_port *sport, u32 offset) +static inline u32 imx_uart_readl(struct imx_port *sport, u32 offset) { - switch (offset) { - case UCR1: - return sport->ucr1; - break; - case UCR2: - /* - * UCR2_SRST is the only bit in the cached registers that might - * differ from the value that was last written. As it only - * automatically becomes one after being cleared, reread - * conditionally. - */ - if (!(sport->ucr2 & UCR2_SRST)) - sport->ucr2 = readl(sport->port.membase + offset); - return sport->ucr2; - break; - case UCR3: - return sport->ucr3; - break; - case UCR4: - return sport->ucr4; - break; - case UFCR: - return sport->ufcr; - break; - default: - return readl(sport->port.membase + offset); - } + return readl(sport->port.membase + offset); } static inline unsigned imx_uart_uts_reg(struct imx_port *sport) @@ -398,6 +349,41 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) } /* called with port.lock taken and irqs off */ +static void imx_uart_soft_reset(struct imx_port *sport) +{ + int i = 10; + u32 ucr2, ubir, ubmr, uts; + + /* + * According to the Reference Manual description of the UART SRST bit: + * + * "Reset the transmit and receive state machines, + * all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD + * and UTS[6-3]". + * + * We don't need to restore the old values from USR1, USR2, URXD and + * UTXD. UBRC is read only, so only save/restore the other three + * registers. + */ + ubir = imx_uart_readl(sport, UBIR); + ubmr = imx_uart_readl(sport, UBMR); + uts = imx_uart_readl(sport, IMX21_UTS); + + ucr2 = imx_uart_readl(sport, UCR2); + imx_uart_writel(sport, ucr2 & ~UCR2_SRST, UCR2); + + while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0)) + udelay(1); + + /* Restore the registers */ + imx_uart_writel(sport, ubir, UBIR); + imx_uart_writel(sport, ubmr, UBMR); + imx_uart_writel(sport, uts, IMX21_UTS); + + sport->idle_counter = 0; +} + +/* called with port.lock taken and irqs off */ static void imx_uart_start_rx(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -476,7 +462,8 @@ static void imx_uart_stop_tx(struct uart_port *port) imx_uart_rts_inactive(sport, &ucr2); imx_uart_writel(sport, ucr2, UCR2); - imx_uart_start_rx(port); + if (!port->rs485_rx_during_tx_gpio) + imx_uart_start_rx(port); sport->tx_state = OFF; } @@ -705,7 +692,8 @@ static void imx_uart_start_tx(struct uart_port *port) imx_uart_rts_inactive(sport, &ucr2); imx_uart_writel(sport, ucr2, UCR2); - if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) + if (!(port->rs485.flags & SER_RS485_RX_DURING_TX) && + !port->rs485_rx_during_tx_gpio) imx_uart_stop_rx(port); sport->tx_state = WAIT_AFTER_RTS; @@ -771,7 +759,7 @@ static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id) imx_uart_writel(sport, USR1_RTSD, USR1); usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS; - uart_handle_cts_change(&sport->port, !!usr1); + uart_handle_cts_change(&sport->port, usr1); wake_up_interruptible(&sport->port.state->port.delta_msr_wait); return IRQ_HANDLED; @@ -801,33 +789,73 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id) return IRQ_HANDLED; } +/* Check if hardware Rx flood is in progress, and issue soft reset to stop it. + * This is to be called from Rx ISRs only when some bytes were actually + * received. + * + * A way to reproduce the flood (checked on iMX6SX) is: open iMX UART at 9600 + * 8N1, and from external source send 0xf0 char at 115200 8N1. In about 90% of + * cases this starts a flood of "receiving" of 0xff characters by the iMX6 UART + * that is terminated by any activity on RxD line, or could be stopped by + * issuing soft reset to the UART (just stop/start of RX does not help). Note + * that what we do here is sending isolated start bit about 2.4 times shorter + * than it is to be on UART configured baud rate. + */ +static void imx_uart_check_flood(struct imx_port *sport, u32 usr2) +{ + /* To detect hardware 0xff flood we monitor RxD line between RX + * interrupts to isolate "receiving" of char(s) with no activity + * on RxD line, that'd never happen on actual data transfers. + * + * We use USR2_WAKE bit to check for activity on RxD line, but we have a + * race here if we clear USR2_WAKE when receiving of a char is in + * progress, so we might get RX interrupt later with USR2_WAKE bit + * cleared. Note though that as we don't try to clear USR2_WAKE when we + * detected no activity, this race may hide actual activity only once. + * + * Yet another case where receive interrupt may occur without RxD + * activity is expiration of aging timer, so we consider this as well. + * + * We use 'idle_counter' to ensure that we got at least so many RX + * interrupts without any detected activity on RxD line. 2 cases + * described plus 1 to be on the safe side gives us a margin of 3, + * below. In practice I was not able to produce a false positive to + * induce soft reset at regular data transfers even using 1 as the + * margin, so 3 is actually very strong. + * + * We count interrupts, not chars in 'idle-counter' for simplicity. + */ + + if (usr2 & USR2_WAKE) { + imx_uart_writel(sport, USR2_WAKE, USR2); + sport->idle_counter = 0; + } else if (++sport->idle_counter > 3) { + dev_warn(sport->port.dev, "RX flood detected: soft reset."); + imx_uart_soft_reset(sport); /* also clears 'sport->idle_counter' */ + } +} + static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int rx, flg, ignored = 0; struct tty_port *port = &sport->port.state->port; + u32 usr2, rx; - while (imx_uart_readl(sport, USR2) & USR2_RDR) { - u32 usr2; + /* If we received something, check for 0xff flood */ + usr2 = imx_uart_readl(sport, USR2); + if (usr2 & USR2_RDR) + imx_uart_check_flood(sport, usr2); - flg = TTY_NORMAL; + while ((rx = imx_uart_readl(sport, URXD0)) & URXD_CHARRDY) { + unsigned int flg = TTY_NORMAL; sport->port.icount.rx++; - rx = imx_uart_readl(sport, URXD0); - - usr2 = imx_uart_readl(sport, USR2); - if (usr2 & USR2_BRCD) { - imx_uart_writel(sport, USR2_BRCD, USR2); - if (uart_handle_break(&sport->port)) - continue; - } - - if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) - continue; - if (unlikely(rx & URXD_ERR)) { - if (rx & URXD_BRK) + if (rx & URXD_BRK) { sport->port.icount.brk++; + if (uart_handle_break(&sport->port)) + continue; + } else if (rx & URXD_PRERR) sport->port.icount.parity++; else if (rx & URXD_FRMERR) @@ -835,11 +863,8 @@ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) if (rx & URXD_OVRRUN) sport->port.icount.overrun++; - if (rx & sport->port.ignore_status_mask) { - if (++ignored > 100) - goto out; + if (rx & sport->port.ignore_status_mask) continue; - } rx &= (sport->port.read_status_mask | 0xFF); @@ -853,16 +878,17 @@ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) flg = TTY_OVERRUN; sport->port.sysrq = 0; + } else if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) { + continue; } if (sport->port.ignore_status_mask & URXD_DUMMY_READ) - goto out; + continue; if (tty_insert_flip_char(port, rx, flg) == 0) sport->port.icount.buf_overrun++; } -out: tty_flip_buffer_push(port); return IRQ_HANDLED; @@ -1147,55 +1173,62 @@ static void imx_uart_dma_rx_callback(void *data) status = dmaengine_tx_status(chan, sport->rx_cookie, &state); if (status == DMA_ERROR) { + spin_lock(&sport->port.lock); imx_uart_clear_rx_errors(sport); + spin_unlock(&sport->port.lock); return; } - if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { + /* + * The state-residue variable represents the empty space + * relative to the entire buffer. Taking this in consideration + * the head is always calculated base on the buffer total + * length - DMA transaction residue. The UART script from the + * SDMA firmware will jump to the next buffer descriptor, + * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4). + * Taking this in consideration the tail is always at the + * beginning of the buffer descriptor that contains the head. + */ - /* - * The state-residue variable represents the empty space - * relative to the entire buffer. Taking this in consideration - * the head is always calculated base on the buffer total - * length - DMA transaction residue. The UART script from the - * SDMA firmware will jump to the next buffer descriptor, - * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4). - * Taking this in consideration the tail is always at the - * beginning of the buffer descriptor that contains the head. - */ + /* Calculate the head */ + rx_ring->head = sg_dma_len(sgl) - state.residue; + + /* Calculate the tail. */ + bd_size = sg_dma_len(sgl) / sport->rx_periods; + rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size; - /* Calculate the head */ - rx_ring->head = sg_dma_len(sgl) - state.residue; + if (rx_ring->head <= sg_dma_len(sgl) && + rx_ring->head > rx_ring->tail) { - /* Calculate the tail. */ - bd_size = sg_dma_len(sgl) / sport->rx_periods; - rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size; + /* Move data from tail to head */ + r_bytes = rx_ring->head - rx_ring->tail; - if (rx_ring->head <= sg_dma_len(sgl) && - rx_ring->head > rx_ring->tail) { + /* If we received something, check for 0xff flood */ + spin_lock(&sport->port.lock); + imx_uart_check_flood(sport, imx_uart_readl(sport, USR2)); + spin_unlock(&sport->port.lock); - /* Move data from tail to head */ - r_bytes = rx_ring->head - rx_ring->tail; + if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { /* CPU claims ownership of RX DMA buffer */ dma_sync_sg_for_cpu(sport->port.dev, sgl, 1, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); w_bytes = tty_insert_flip_string(port, - sport->rx_buf + rx_ring->tail, r_bytes); + sport->rx_buf + rx_ring->tail, r_bytes); /* UART retrieves ownership of RX DMA buffer */ dma_sync_sg_for_device(sport->port.dev, sgl, 1, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); if (w_bytes != r_bytes) sport->port.icount.buf_overrun++; sport->port.icount.rx += w_bytes; - } else { - WARN_ON(rx_ring->head > sg_dma_len(sgl)); - WARN_ON(rx_ring->head <= rx_ring->tail); } + } else { + WARN_ON(rx_ring->head > sg_dma_len(sgl)); + WARN_ON(rx_ring->head <= rx_ring->tail); } if (w_bytes) { @@ -1271,6 +1304,8 @@ static void imx_uart_clear_rx_errors(struct imx_port *sport) imx_uart_writel(sport, USR2_ORE, USR2); } + sport->idle_counter = 0; + } #define TXTL_DEFAULT 2 /* reset default */ @@ -1398,7 +1433,7 @@ static void imx_uart_disable_dma(struct imx_port *sport) static int imx_uart_startup(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - int retval, i; + int retval; unsigned long flags; int dma_is_inited = 0; u32 ucr1, ucr2, ucr3, ucr4, uts; @@ -1430,15 +1465,9 @@ static int imx_uart_startup(struct uart_port *port) dma_is_inited = 1; spin_lock_irqsave(&sport->port.lock, flags); - /* Reset fifo's and state machines */ - i = 100; - ucr2 = imx_uart_readl(sport, UCR2); - ucr2 &= ~UCR2_SRST; - imx_uart_writel(sport, ucr2, UCR2); - - while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0)) - udelay(1); + /* Reset fifo's and state machines */ + imx_uart_soft_reset(sport); /* * Finally, clear and enable interrupts @@ -1564,7 +1593,8 @@ static void imx_uart_shutdown(struct uart_port *port) spin_lock_irqsave(&sport->port.lock, flags); ucr1 = imx_uart_readl(sport, UCR1); - ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN | UCR1_ATDMAEN); + ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN | + UCR1_ATDMAEN | UCR1_SNDBRK); /* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */ if (port->rs485.flags & SER_RS485_ENABLED && port->rs485.flags & SER_RS485_RTS_ON_SEND && @@ -1593,8 +1623,6 @@ static void imx_uart_flush_buffer(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; struct scatterlist *sgl = &sport->tx_sgl[0]; - u32 ucr2; - int i = 100, ubir, ubmr, uts; if (!sport->dma_chan_tx) return; @@ -1612,32 +1640,8 @@ static void imx_uart_flush_buffer(struct uart_port *port) sport->dma_is_txing = 0; } - /* - * According to the Reference Manual description of the UART SRST bit: - * - * "Reset the transmit and receive state machines, - * all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD - * and UTS[6-3]". - * - * We don't need to restore the old values from USR1, USR2, URXD and - * UTXD. UBRC is read only, so only save/restore the other three - * registers. - */ - ubir = imx_uart_readl(sport, UBIR); - ubmr = imx_uart_readl(sport, UBMR); - uts = imx_uart_readl(sport, IMX21_UTS); - - ucr2 = imx_uart_readl(sport, UCR2); - ucr2 &= ~UCR2_SRST; - imx_uart_writel(sport, ucr2, UCR2); - - while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0)) - udelay(1); + imx_uart_soft_reset(sport); - /* Restore the registers */ - imx_uart_writel(sport, ubir, UBIR); - imx_uart_writel(sport, ubmr, UBMR); - imx_uart_writel(sport, uts, IMX21_UTS); } static void @@ -1955,6 +1959,10 @@ static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termio rs485conf->flags & SER_RS485_RX_DURING_TX) imx_uart_start_rx(port); + if (port->rs485_rx_during_tx_gpio) + gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, + !!(rs485conf->flags & SER_RS485_RX_DURING_TX)); + return 0; } @@ -2340,13 +2348,6 @@ static int imx_uart_probe(struct platform_device *pdev) return ret; } - /* initialize shadow register values */ - sport->ucr1 = readl(sport->port.membase + UCR1); - sport->ucr2 = readl(sport->port.membase + UCR2); - sport->ucr3 = readl(sport->port.membase + UCR3); - sport->ucr4 = readl(sport->port.membase + UCR4); - sport->ufcr = readl(sport->port.membase + UFCR); - ret = uart_get_rs485_mode(&sport->port); if (ret) { clk_disable_unprepare(sport->clk_ipg); @@ -2374,6 +2375,11 @@ static int imx_uart_probe(struct platform_device *pdev) ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | UCR1_RTSDEN); imx_uart_writel(sport, ucr1, UCR1); + /* Disable Ageing Timer interrupt */ + ucr2 = imx_uart_readl(sport, UCR2); + ucr2 &= ~UCR2_ATEN; + imx_uart_writel(sport, ucr2, UCR2); + /* * In case RS485 is enabled without GPIO RTS control, the UART IP * is used to control CTS signal. Keep both the UART and Receiver diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index 062812fe1b09..80de3a42b67b 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -5,7 +5,9 @@ * Copyright (C) 2019-2020 Antmicro <www.antmicro.com> */ +#include <linux/bits.h> #include <linux/console.h> +#include <linux/interrupt.h> #include <linux/litex.h> #include <linux/module.h> #include <linux/of.h> @@ -38,13 +40,13 @@ #define OFF_EV_ENABLE 0x14 /* events */ -#define EV_TX 0x1 -#define EV_RX 0x2 +#define EV_TX BIT(0) +#define EV_RX BIT(1) struct liteuart_port { struct uart_port port; struct timer_list timer; - u32 id; + u8 irq_reg; }; #define to_liteuart_port(port) container_of(port, struct liteuart_port, port) @@ -57,7 +59,7 @@ static struct console liteuart_console; static struct uart_driver liteuart_driver = { .owner = THIS_MODULE, - .driver_name = "liteuart", + .driver_name = KBUILD_MODNAME, .dev_name = "ttyLXU", .major = 0, .minor = 0, @@ -67,38 +69,95 @@ static struct uart_driver liteuart_driver = { #endif }; -static void liteuart_timer(struct timer_list *t) +static void liteuart_update_irq_reg(struct uart_port *port, bool set, u8 mask) +{ + struct liteuart_port *uart = to_liteuart_port(port); + + if (set) + uart->irq_reg |= mask; + else + uart->irq_reg &= ~mask; + + if (port->irq) + litex_write8(port->membase + OFF_EV_ENABLE, uart->irq_reg); +} + +static void liteuart_stop_tx(struct uart_port *port) +{ + liteuart_update_irq_reg(port, false, EV_TX); +} + +static void liteuart_start_tx(struct uart_port *port) +{ + liteuart_update_irq_reg(port, true, EV_TX); +} + +static void liteuart_stop_rx(struct uart_port *port) +{ + struct liteuart_port *uart = to_liteuart_port(port); + + /* just delete timer */ + del_timer(&uart->timer); +} + +static void liteuart_rx_chars(struct uart_port *port) { - struct liteuart_port *uart = from_timer(uart, t, timer); - struct uart_port *port = &uart->port; unsigned char __iomem *membase = port->membase; - unsigned int flg = TTY_NORMAL; - int ch; - unsigned long status; + u8 ch; - while ((status = !litex_read8(membase + OFF_RXEMPTY)) == 1) { + while (!litex_read8(membase + OFF_RXEMPTY)) { ch = litex_read8(membase + OFF_RXTX); port->icount.rx++; /* necessary for RXEMPTY to refresh its value */ - litex_write8(membase + OFF_EV_PENDING, EV_TX | EV_RX); + litex_write8(membase + OFF_EV_PENDING, EV_RX); /* no overflow bits in status */ if (!(uart_handle_sysrq_char(port, ch))) - uart_insert_char(port, status, 0, ch, flg); - - tty_flip_buffer_push(&port->state->port); + uart_insert_char(port, 1, 0, ch, TTY_NORMAL); } - mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); + tty_flip_buffer_push(&port->state->port); } -static void liteuart_putchar(struct uart_port *port, unsigned char ch) +static void liteuart_tx_chars(struct uart_port *port) { - while (litex_read8(port->membase + OFF_TXFULL)) - cpu_relax(); + u8 ch; - litex_write8(port->membase + OFF_RXTX, ch); + uart_port_tx(port, ch, + !litex_read8(port->membase + OFF_TXFULL), + litex_write8(port->membase + OFF_RXTX, ch)); +} + +static irqreturn_t liteuart_interrupt(int irq, void *data) +{ + struct liteuart_port *uart = data; + struct uart_port *port = &uart->port; + unsigned long flags; + u8 isr; + + /* + * if polling, the context would be "in_serving_softirq", so use + * irq[save|restore] spin_lock variants to cover all possibilities + */ + spin_lock_irqsave(&port->lock, flags); + isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg; + if (isr & EV_RX) + liteuart_rx_chars(port); + if (isr & EV_TX) + liteuart_tx_chars(port); + spin_unlock_irqrestore(&port->lock, flags); + + return IRQ_RETVAL(isr); +} + +static void liteuart_timer(struct timer_list *t) +{ + struct liteuart_port *uart = from_timer(uart, t, timer); + struct uart_port *port = &uart->port; + + liteuart_interrupt(0, port); + mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); } static unsigned int liteuart_tx_empty(struct uart_port *port) @@ -120,60 +179,49 @@ static unsigned int liteuart_get_mctrl(struct uart_port *port) return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; } -static void liteuart_stop_tx(struct uart_port *port) -{ -} - -static void liteuart_start_tx(struct uart_port *port) +static int liteuart_startup(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; - unsigned char ch; - - if (unlikely(port->x_char)) { - litex_write8(port->membase + OFF_RXTX, port->x_char); - port->icount.tx++; - port->x_char = 0; - } else if (!uart_circ_empty(xmit)) { - while (xmit->head != xmit->tail) { - ch = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - liteuart_putchar(port, ch); + struct liteuart_port *uart = to_liteuart_port(port); + unsigned long flags; + int ret; + + if (port->irq) { + ret = request_irq(port->irq, liteuart_interrupt, 0, + KBUILD_MODNAME, uart); + if (ret) { + dev_warn(port->dev, + "line %d irq %d failed: switch to polling\n", + port->line, port->irq); + port->irq = 0; } } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static void liteuart_stop_rx(struct uart_port *port) -{ - struct liteuart_port *uart = to_liteuart_port(port); + spin_lock_irqsave(&port->lock, flags); + /* only enabling rx irqs during startup */ + liteuart_update_irq_reg(port, true, EV_RX); + spin_unlock_irqrestore(&port->lock, flags); - /* just delete timer */ - del_timer(&uart->timer); -} + if (!port->irq) { + timer_setup(&uart->timer, liteuart_timer, 0); + mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); + } -static void liteuart_break_ctl(struct uart_port *port, int break_state) -{ - /* LiteUART doesn't support sending break signal */ + return 0; } -static int liteuart_startup(struct uart_port *port) +static void liteuart_shutdown(struct uart_port *port) { struct liteuart_port *uart = to_liteuart_port(port); + unsigned long flags; - /* disable events */ - litex_write8(port->membase + OFF_EV_ENABLE, 0); - - /* prepare timer for polling */ - timer_setup(&uart->timer, liteuart_timer, 0); - mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); - - return 0; -} + spin_lock_irqsave(&port->lock, flags); + liteuart_update_irq_reg(port, false, EV_RX | EV_TX); + spin_unlock_irqrestore(&port->lock, flags); -static void liteuart_shutdown(struct uart_port *port) -{ + if (port->irq) + free_irq(port->irq, port); + else + del_timer_sync(&uart->timer); } static void liteuart_set_termios(struct uart_port *port, struct ktermios *new, @@ -196,15 +244,6 @@ static const char *liteuart_type(struct uart_port *port) return "liteuart"; } -static void liteuart_release_port(struct uart_port *port) -{ -} - -static int liteuart_request_port(struct uart_port *port) -{ - return 0; -} - static void liteuart_config_port(struct uart_port *port, int flags) { /* @@ -231,13 +270,10 @@ static const struct uart_ops liteuart_ops = { .stop_tx = liteuart_stop_tx, .start_tx = liteuart_start_tx, .stop_rx = liteuart_stop_rx, - .break_ctl = liteuart_break_ctl, .startup = liteuart_startup, .shutdown = liteuart_shutdown, .set_termios = liteuart_set_termios, .type = liteuart_type, - .release_port = liteuart_release_port, - .request_port = liteuart_request_port, .config_port = liteuart_config_port, .verify_port = liteuart_verify_port, }; @@ -249,6 +285,23 @@ static int liteuart_probe(struct platform_device *pdev) struct xa_limit limit; int dev_id, ret; + uart = devm_kzalloc(&pdev->dev, sizeof(struct liteuart_port), GFP_KERNEL); + if (!uart) + return -ENOMEM; + + port = &uart->port; + + /* get membase */ + port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + + ret = platform_get_irq_optional(pdev, 0); + if (ret < 0 && ret != -ENXIO) + return ret; + if (ret > 0) + port->irq = ret; + /* look for aliases; auto-enumerate for free index if not found */ dev_id = of_alias_get_id(pdev->dev.of_node, "serial"); if (dev_id < 0) @@ -256,32 +309,16 @@ static int liteuart_probe(struct platform_device *pdev) else limit = XA_LIMIT(dev_id, dev_id); - uart = devm_kzalloc(&pdev->dev, sizeof(struct liteuart_port), GFP_KERNEL); - if (!uart) - return -ENOMEM; - ret = xa_alloc(&liteuart_array, &dev_id, uart, limit, GFP_KERNEL); if (ret) return ret; - uart->id = dev_id; - port = &uart->port; - - /* get membase */ - port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); - if (IS_ERR(port->membase)) { - ret = PTR_ERR(port->membase); - goto err_erase_id; - } - /* values not from device tree */ port->dev = &pdev->dev; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; port->ops = &liteuart_ops; - port->regshift = 2; port->fifosize = 16; - port->iobase = 1; port->type = PORT_UNKNOWN; port->line = dev_id; spin_lock_init(&port->lock); @@ -295,7 +332,7 @@ static int liteuart_probe(struct platform_device *pdev) return 0; err_erase_id: - xa_erase(&liteuart_array, uart->id); + xa_erase(&liteuart_array, dev_id); return ret; } @@ -303,10 +340,10 @@ err_erase_id: static int liteuart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); - struct liteuart_port *uart = to_liteuart_port(port); + unsigned int line = port->line; uart_remove_one_port(&liteuart_driver, port); - xa_erase(&liteuart_array, uart->id); + xa_erase(&liteuart_array, line); return 0; } @@ -321,13 +358,21 @@ static struct platform_driver liteuart_platform_driver = { .probe = liteuart_probe, .remove = liteuart_remove, .driver = { - .name = "liteuart", + .name = KBUILD_MODNAME, .of_match_table = liteuart_of_match, }, }; #ifdef CONFIG_SERIAL_LITEUART_CONSOLE +static void liteuart_putchar(struct uart_port *port, unsigned char ch) +{ + while (litex_read8(port->membase + OFF_TXFULL)) + cpu_relax(); + + litex_write8(port->membase + OFF_RXTX, ch); +} + static void liteuart_console_write(struct console *co, const char *s, unsigned int count) { @@ -367,7 +412,7 @@ static int liteuart_console_setup(struct console *co, char *options) } static struct console liteuart_console = { - .name = "liteuart", + .name = KBUILD_MODNAME, .write = liteuart_console_write, .device = uart_console_device, .setup = liteuart_console_setup, @@ -415,12 +460,10 @@ static int __init liteuart_init(void) return res; res = platform_driver_register(&liteuart_platform_driver); - if (res) { + if (res) uart_unregister_driver(&liteuart_driver); - return res; - } - return 0; + return res; } static void __exit liteuart_exit(void) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index bb74f23251fe..86dcbff8faa3 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -247,7 +247,7 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx) cts = (rx & MAX3100_CTS) > 0; if (s->cts != cts) { s->cts = cts; - uart_handle_cts_change(&s->port, cts ? TIOCM_CTS : 0); + uart_handle_cts_change(&s->port, cts); } return ret; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 4eb24e3407f8..e9cacfe7e032 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -819,8 +819,7 @@ static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno) if (ists & MAX310X_IRQ_CTS_BIT) { lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); - uart_handle_cts_change(port, - !!(lsr & MAX310X_LSR_CTS_BIT)); + uart_handle_cts_change(port, lsr & MAX310X_LSR_CTS_BIT); } if (rxlen) max310x_handle_rx(port, rxlen); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 843798e63084..90953e679e38 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1120,6 +1120,7 @@ msm_find_best_baud(struct uart_port *port, unsigned int baud, static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, unsigned long *saved_flags) + __must_hold(&port->lock) { unsigned int rxstale, watermark, mask; struct msm_port *msm_port = to_msm_port(port); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 9576ba8bbc40..cc83b772b7ca 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1775,7 +1775,7 @@ static void pch_uart_exit_port(struct eg20t_port *priv) char name[32]; snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); - debugfs_remove(debugfs_lookup(name, NULL)); + debugfs_lookup_and_remove(name, NULL); uart_remove_one_port(&pch_uart_driver, &priv->port); free_page((unsigned long)priv->rxbuf.buf); } diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index ba3435263c1f..196a4e678451 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -889,6 +889,8 @@ static int pic32_uart_probe(struct platform_device *pdev) sport->irq_rx = irq_of_parse_and_map(np, 1); sport->irq_tx = irq_of_parse_and_map(np, 2); sport->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(sport->clk)) + return PTR_ERR(sport->clk); sport->dev = &pdev->dev; /* Hardware flow control: gpios diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 57f04f8bf504..d69592e5e2ec 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -16,7 +16,7 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/pm_wakeirq.h> -#include <linux/qcom-geni-se.h> +#include <linux/soc/qcom/geni-se.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/slab.h> @@ -39,76 +39,70 @@ #define SE_UART_MANUAL_RFR 0x2ac /* SE_UART_TRANS_CFG */ -#define UART_TX_PAR_EN BIT(0) -#define UART_CTS_MASK BIT(1) - -/* SE_UART_TX_WORD_LEN */ -#define TX_WORD_LEN_MSK GENMASK(9, 0) +#define UART_TX_PAR_EN BIT(0) +#define UART_CTS_MASK BIT(1) /* SE_UART_TX_STOP_BIT_LEN */ -#define TX_STOP_BIT_LEN_MSK GENMASK(23, 0) -#define TX_STOP_BIT_LEN_1 0 -#define TX_STOP_BIT_LEN_1_5 1 -#define TX_STOP_BIT_LEN_2 2 - -/* SE_UART_TX_TRANS_LEN */ -#define TX_TRANS_LEN_MSK GENMASK(23, 0) +#define TX_STOP_BIT_LEN_1 0 +#define TX_STOP_BIT_LEN_2 2 /* SE_UART_RX_TRANS_CFG */ -#define UART_RX_INS_STATUS_BIT BIT(2) -#define UART_RX_PAR_EN BIT(3) +#define UART_RX_PAR_EN BIT(3) /* SE_UART_RX_WORD_LEN */ -#define RX_WORD_LEN_MASK GENMASK(9, 0) +#define RX_WORD_LEN_MASK GENMASK(9, 0) /* SE_UART_RX_STALE_CNT */ -#define RX_STALE_CNT GENMASK(23, 0) +#define RX_STALE_CNT GENMASK(23, 0) /* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */ -#define PAR_CALC_EN BIT(0) -#define PAR_MODE_MSK GENMASK(2, 1) -#define PAR_MODE_SHFT 1 -#define PAR_EVEN 0x00 -#define PAR_ODD 0x01 -#define PAR_SPACE 0x10 -#define PAR_MARK 0x11 +#define PAR_CALC_EN BIT(0) +#define PAR_EVEN 0x00 +#define PAR_ODD 0x01 +#define PAR_SPACE 0x10 /* SE_UART_MANUAL_RFR register fields */ -#define UART_MANUAL_RFR_EN BIT(31) -#define UART_RFR_NOT_READY BIT(1) -#define UART_RFR_READY BIT(0) +#define UART_MANUAL_RFR_EN BIT(31) +#define UART_RFR_NOT_READY BIT(1) +#define UART_RFR_READY BIT(0) /* UART M_CMD OP codes */ -#define UART_START_TX 0x1 -#define UART_START_BREAK 0x4 -#define UART_STOP_BREAK 0x5 +#define UART_START_TX 0x1 /* UART S_CMD OP codes */ -#define UART_START_READ 0x1 -#define UART_PARAM 0x1 - -#define UART_OVERSAMPLING 32 -#define STALE_TIMEOUT 16 -#define DEFAULT_BITS_PER_CHAR 10 -#define GENI_UART_CONS_PORTS 1 -#define GENI_UART_PORTS 3 -#define DEF_FIFO_DEPTH_WORDS 16 -#define DEF_TX_WM 2 -#define DEF_FIFO_WIDTH_BITS 32 -#define UART_RX_WM 2 +#define UART_START_READ 0x1 +#define UART_PARAM 0x1 +#define UART_PARAM_RFR_OPEN BIT(7) + +#define UART_OVERSAMPLING 32 +#define STALE_TIMEOUT 16 +#define DEFAULT_BITS_PER_CHAR 10 +#define GENI_UART_CONS_PORTS 1 +#define GENI_UART_PORTS 3 +#define DEF_FIFO_DEPTH_WORDS 16 +#define DEF_TX_WM 2 +#define DEF_FIFO_WIDTH_BITS 32 +#define UART_RX_WM 2 /* SE_UART_LOOPBACK_CFG */ -#define RX_TX_SORTED BIT(0) -#define CTS_RTS_SORTED BIT(1) -#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED) +#define RX_TX_SORTED BIT(0) +#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 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 DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4) #define IO_MACRO_IO2_IO3_SWAP 0x4640 /* We always configure 4 bytes per FIFO word */ -#define BYTES_PER_FIFO_WORD 4 +#define BYTES_PER_FIFO_WORD 4U + +#define DMA_RX_BUF_SIZE 2048 + +struct qcom_geni_device_data { + bool console; + enum geni_se_xfer_mode mode; +}; struct qcom_geni_private_data { /* NOTE: earlycon port will have NULL here */ @@ -128,10 +122,11 @@ struct qcom_geni_serial_port { u32 tx_fifo_depth; u32 tx_fifo_width; u32 rx_fifo_depth; + dma_addr_t tx_dma_addr; + dma_addr_t rx_dma_addr; bool setup; - int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop); unsigned int baud; - void *rx_fifo; + void *rx_buf; u32 loopback; bool brk; @@ -141,44 +136,42 @@ struct qcom_geni_serial_port { bool cts_rts_swap; struct qcom_geni_private_data private_data; + const struct qcom_geni_device_data *dev_data; }; static const struct uart_ops qcom_geni_console_pops; static const struct uart_ops qcom_geni_uart_pops; static struct uart_driver qcom_geni_console_driver; static struct uart_driver qcom_geni_uart_driver; -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); -static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port); -static void qcom_geni_serial_stop_rx(struct uart_port *uport); -static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop); -#define to_dev_port(ptr, member) \ - container_of(ptr, struct qcom_geni_serial_port, member) +static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport) +{ + return container_of(uport, struct qcom_geni_serial_port, uport); +} static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = { [0] = { .uport = { - .iotype = UPIO_MEM, - .ops = &qcom_geni_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .line = 0, + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, }, }, [1] = { .uport = { - .iotype = UPIO_MEM, - .ops = &qcom_geni_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .line = 1, + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 1, }, }, [2] = { .uport = { - .iotype = UPIO_MEM, - .ops = &qcom_geni_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .line = 2, + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 2, }, }, }; @@ -195,7 +188,7 @@ static struct qcom_geni_serial_port qcom_geni_console_port = { static int qcom_geni_serial_request_port(struct uart_port *uport) { struct platform_device *pdev = to_platform_device(uport->dev); - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); uport->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(uport->membase)) @@ -232,7 +225,7 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport, unsigned int mctrl) { u32 uart_manual_rfr = 0; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); if (uart_console(uport)) return; @@ -262,6 +255,16 @@ static struct qcom_geni_serial_port *get_port_from_line(int line, bool console) return port; } +static bool qcom_geni_serial_main_active(struct uart_port *uport) +{ + return readl(uport->membase + SE_GENI_STATUS) & M_GENI_CMD_ACTIVE; +} + +static bool qcom_geni_serial_secondary_active(struct uart_port *uport) +{ + return readl(uport->membase + SE_GENI_STATUS) & S_GENI_CMD_ACTIVE; +} + static bool qcom_geni_serial_poll_bit(struct uart_port *uport, int offset, int field, bool set) { @@ -273,7 +276,7 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, struct qcom_geni_private_data *private_data = uport->private_data; if (private_data->drv) { - port = to_dev_port(uport, uport); + port = to_dev_port(uport); baud = port->baud; if (!baud) baud = 115200; @@ -338,7 +341,6 @@ static void qcom_geni_serial_abort_rx(struct uart_port *uport) } #ifdef CONFIG_CONSOLE_POLL - static int qcom_geni_serial_get_char(struct uart_port *uport) { struct qcom_geni_private_data *private_data = uport->private_data; @@ -521,12 +523,12 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, spin_unlock_irqrestore(&uport->lock, flags); } -static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) +static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) { u32 i; unsigned char buf[sizeof(u32)]; struct tty_port *tport; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); tport = &uport->state->port; for (i = 0; i < bytes; ) { @@ -556,30 +558,21 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) } if (!drop) tty_flip_buffer_push(tport); - return 0; } #else -static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) +static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) { - return -EPERM; -} +} #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ -static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) +static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) { - 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; - u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw; + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct tty_port *tport = &uport->state->port; int ret; - tport = &uport->state->port; - ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words); - if (drop) - return 0; - - ret = tty_insert_flip_string(tport, port->rx_fifo, bytes); + ret = tty_insert_flip_string(tport, port->rx_buf, bytes); if (ret != bytes) { dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n", __func__, ret, bytes); @@ -587,19 +580,82 @@ static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) } uport->icount.rx += ret; tty_flip_buffer_push(tport); - return ret; } -static void qcom_geni_serial_start_tx(struct uart_port *uport) +static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport) { - u32 irq_en; - u32 status; + return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS); +} + +static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + bool done; + u32 m_irq_en; - status = readl(uport->membase + SE_GENI_STATUS); - if (status & M_GENI_CMD_ACTIVE) + if (!qcom_geni_serial_main_active(uport)) return; - if (!qcom_geni_serial_tx_empty(uport)) + if (port->rx_dma_addr) { + geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, + port->tx_remaining); + port->tx_dma_addr = 0; + port->tx_remaining = 0; + } + + m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + writel(m_irq_en, uport->membase + SE_GENI_M_IRQ_EN); + geni_se_cancel_m_cmd(&port->se); + + done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, + S_CMD_CANCEL_EN, true); + if (!done) { + geni_se_abort_m_cmd(&port->se); + done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_ABORT_EN, true); + if (!done) + dev_err_ratelimited(uport->dev, "M_CMD_ABORT_EN not set"); + writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); + } + + writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); +} + +static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct circ_buf *xmit = &uport->state->xmit; + unsigned int xmit_size; + int ret; + + if (port->tx_dma_addr) + return; + + xmit_size = uart_circ_chars_pending(xmit); + if (xmit_size < WAKEUP_CHARS) + uart_write_wakeup(uport); + + xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + qcom_geni_serial_setup_tx(uport, xmit_size); + + ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail], + xmit_size, &port->tx_dma_addr); + if (ret) { + dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret); + qcom_geni_serial_stop_tx_dma(uport); + return; + } + + port->tx_remaining = xmit_size; +} + +static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport) +{ + u32 irq_en; + + if (qcom_geni_serial_main_active(uport) || + !qcom_geni_serial_tx_empty(uport)) return; irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); @@ -609,19 +665,17 @@ static void qcom_geni_serial_start_tx(struct uart_port *uport) writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); } -static void qcom_geni_serial_stop_tx(struct uart_port *uport) +static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport) { u32 irq_en; - u32 status; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); irq_en &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN); writel(0, uport->membase + SE_GENI_TX_WATERMARK_REG); writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - status = readl(uport->membase + SE_GENI_STATUS); /* Possible stop tx is called multiple times. */ - if (!(status & M_GENI_CMD_ACTIVE)) + if (!qcom_geni_serial_main_active(uport)) return; geni_se_cancel_m_cmd(&port->se); @@ -635,32 +689,34 @@ static void qcom_geni_serial_stop_tx(struct uart_port *uport) writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } -static void qcom_geni_serial_start_rx(struct uart_port *uport) +static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop) { - u32 irq_en; u32 status; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - - status = readl(uport->membase + SE_GENI_STATUS); - if (status & S_GENI_CMD_ACTIVE) - qcom_geni_serial_stop_rx(uport); - - geni_se_setup_s_cmd(&port->se, UART_START_READ, 0); + u32 word_cnt; + u32 last_word_byte_cnt; + u32 last_word_partial; + u32 total_bytes; - irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); - irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; - writel(irq_en, uport->membase + SE_GENI_S_IRQ_EN); + status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); + word_cnt = status & RX_FIFO_WC_MSK; + last_word_partial = status & RX_LAST; + last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >> + RX_LAST_BYTE_VALID_SHFT; - irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); - irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; - writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); + if (!word_cnt) + return; + total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1); + if (last_word_partial && last_word_byte_cnt) + total_bytes += last_word_byte_cnt; + else + total_bytes += BYTES_PER_FIFO_WORD; + handle_rx_console(uport, total_bytes, drop); } -static void qcom_geni_serial_stop_rx(struct uart_port *uport) +static void qcom_geni_serial_stop_rx_fifo(struct uart_port *uport) { u32 irq_en; - u32 status; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); u32 s_irq_status; irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); @@ -671,9 +727,7 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport) irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - status = readl(uport->membase + SE_GENI_STATUS); - /* Possible stop rx is called multiple times. */ - if (!(status & S_GENI_CMD_ACTIVE)) + if (!qcom_geni_serial_secondary_active(uport)) return; geni_se_cancel_s_cmd(&port->se); @@ -686,52 +740,154 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport) s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); /* Flush the Rx buffer */ if (s_irq_status & S_RX_FIFO_LAST_EN) - qcom_geni_serial_handle_rx(uport, true); + qcom_geni_serial_handle_rx_fifo(uport, true); writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); - status = readl(uport->membase + SE_GENI_STATUS); - if (status & S_GENI_CMD_ACTIVE) + if (qcom_geni_serial_secondary_active(uport)) qcom_geni_serial_abort_rx(uport); } -static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop) +static void qcom_geni_serial_start_rx_fifo(struct uart_port *uport) { - u32 status; - u32 word_cnt; - u32 last_word_byte_cnt; - u32 last_word_partial; - u32 total_bytes; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + u32 irq_en; + struct qcom_geni_serial_port *port = to_dev_port(uport); - status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); - word_cnt = status & RX_FIFO_WC_MSK; - last_word_partial = status & RX_LAST; - last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >> - RX_LAST_BYTE_VALID_SHFT; + if (qcom_geni_serial_secondary_active(uport)) + qcom_geni_serial_stop_rx_fifo(uport); - if (!word_cnt) + geni_se_setup_s_cmd(&port->se, UART_START_READ, 0); + + irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); + irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; + writel(irq_en, uport->membase + SE_GENI_S_IRQ_EN); + + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; + writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); +} + +static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + + if (!qcom_geni_serial_secondary_active(uport)) return; - total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1); - if (last_word_partial && last_word_byte_cnt) - total_bytes += last_word_byte_cnt; - else - total_bytes += BYTES_PER_FIFO_WORD; - port->handle_rx(uport, total_bytes, drop); + + geni_se_cancel_s_cmd(&port->se); + qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, + S_CMD_CANCEL_EN, true); + + if (qcom_geni_serial_secondary_active(uport)) + qcom_geni_serial_abort_rx(uport); + + if (port->rx_dma_addr) { + geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, + DMA_RX_BUF_SIZE); + port->rx_dma_addr = 0; + } +} + +static void qcom_geni_serial_start_rx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + int ret; + + if (qcom_geni_serial_secondary_active(uport)) + qcom_geni_serial_stop_rx_dma(uport); + + geni_se_setup_s_cmd(&port->se, UART_START_READ, UART_PARAM_RFR_OPEN); + + ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, + DMA_RX_BUF_SIZE, + &port->rx_dma_addr); + if (ret) { + dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret); + qcom_geni_serial_stop_rx_dma(uport); + } +} + +static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + u32 rx_in; + int ret; + + if (!qcom_geni_serial_secondary_active(uport)) + return; + + if (!port->rx_dma_addr) + return; + + geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); + port->rx_dma_addr = 0; + + rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN); + if (!rx_in) { + dev_warn(uport->dev, "serial engine reports 0 RX bytes in!\n"); + return; + } + + if (!drop) + handle_rx_uart(uport, rx_in, drop); + + ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, + DMA_RX_BUF_SIZE, + &port->rx_dma_addr); + if (ret) { + dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret); + qcom_geni_serial_stop_rx_dma(uport); + } +} + +static void qcom_geni_serial_start_rx(struct uart_port *uport) +{ + uport->ops->start_rx(uport); +} + +static void qcom_geni_serial_stop_rx(struct uart_port *uport) +{ + uport->ops->stop_rx(uport); +} + +static void qcom_geni_serial_stop_tx(struct uart_port *uport) +{ + uport->ops->stop_tx(uport); +} + +static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport, + unsigned int chunk) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct circ_buf *xmit = &uport->state->xmit; + unsigned int tx_bytes, c, remaining = chunk; + u8 buf[BYTES_PER_FIFO_WORD]; + + while (remaining) { + memset(buf, 0, sizeof(buf)); + tx_bytes = min(remaining, BYTES_PER_FIFO_WORD); + + for (c = 0; c < tx_bytes ; c++) { + buf[c] = xmit->buf[xmit->tail]; + uart_xmit_advance(uport, 1); + } + + iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); + + remaining -= tx_bytes; + port->tx_remaining -= tx_bytes; + } } -static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, - bool active) +static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, + bool done, bool active) { - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); struct circ_buf *xmit = &uport->state->xmit; size_t avail; - size_t remaining; size_t pending; - int i; u32 status; u32 irq_en; unsigned int chunk; - int tail; status = readl(uport->membase + SE_GENI_TX_FIFO_STATUS); @@ -743,14 +899,13 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, /* All data has been transmitted and acknowledged as received */ if (!pending && !status && done) { - qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_tx_fifo(uport); goto out_write_wakeup; } avail = port->tx_fifo_depth - (status & TX_FIFO_WC); avail *= BYTES_PER_FIFO_WORD; - tail = xmit->tail; chunk = min(avail, pending); if (!chunk) goto out_write_wakeup; @@ -765,29 +920,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, uport->membase + SE_GENI_M_IRQ_EN); } - remaining = chunk; - for (i = 0; i < chunk; ) { - unsigned int tx_bytes; - u8 buf[sizeof(u32)]; - int c; - - memset(buf, 0, sizeof(buf)); - tx_bytes = min_t(size_t, remaining, BYTES_PER_FIFO_WORD); - - for (c = 0; c < tx_bytes ; c++) { - buf[c] = xmit->buf[tail++]; - tail &= UART_XMIT_SIZE - 1; - } - - iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); - - i += tx_bytes; - uport->icount.tx += tx_bytes; - remaining -= tx_bytes; - port->tx_remaining -= tx_bytes; - } - - xmit->tail = tail; + qcom_geni_serial_send_chunk_fifo(uport, chunk); /* * The tx fifo watermark is level triggered and latched. Though we had @@ -809,16 +942,36 @@ out_write_wakeup: uart_write_wakeup(uport); } +static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct circ_buf *xmit = &uport->state->xmit; + + uart_xmit_advance(uport, port->tx_remaining); + geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); + port->tx_dma_addr = 0; + port->tx_remaining = 0; + + if (!uart_circ_empty(xmit)) + qcom_geni_serial_start_tx_dma(uport); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(uport); +} + static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) { u32 m_irq_en; u32 m_irq_status; u32 s_irq_status; u32 geni_status; + u32 dma; + u32 dma_tx_status; + u32 dma_rx_status; struct uart_port *uport = dev; bool drop_rx = false; struct tty_port *tport = &uport->state->port; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); if (uport->suspended) return IRQ_NONE; @@ -827,10 +980,15 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); + dma_tx_status = readl(uport->membase + SE_DMA_TX_IRQ_STAT); + dma_rx_status = readl(uport->membase + SE_DMA_RX_IRQ_STAT); geni_status = readl(uport->membase + SE_GENI_STATUS); + dma = readl(uport->membase + SE_GENI_DMA_MODE_EN); m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); writel(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); + writel(dma_tx_status, uport->membase + SE_DMA_TX_IRQ_CLR); + writel(dma_rx_status, uport->membase + SE_DMA_RX_IRQ_CLR); if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN)) goto out_unlock; @@ -840,23 +998,44 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) tty_insert_flip_char(tport, 0, TTY_OVERRUN); } - if (m_irq_status & m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) - qcom_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN, - geni_status & M_GENI_CMD_ACTIVE); - - if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) { + if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) { if (s_irq_status & S_GP_IRQ_0_EN) uport->icount.parity++; drop_rx = true; - } else if (s_irq_status & S_GP_IRQ_2_EN || - s_irq_status & S_GP_IRQ_3_EN) { + } else if (s_irq_status & (S_GP_IRQ_2_EN | S_GP_IRQ_3_EN)) { uport->icount.brk++; port->brk = true; } - if (s_irq_status & S_RX_FIFO_WATERMARK_EN || - s_irq_status & S_RX_FIFO_LAST_EN) - qcom_geni_serial_handle_rx(uport, drop_rx); + if (dma) { + if (dma_tx_status & TX_DMA_DONE) + qcom_geni_serial_handle_tx_dma(uport); + + if (dma_rx_status) { + if (dma_rx_status & RX_RESET_DONE) + goto out_unlock; + + if (dma_rx_status & RX_DMA_PARITY_ERR) { + uport->icount.parity++; + drop_rx = true; + } + + if (dma_rx_status & RX_DMA_BREAK) + uport->icount.brk++; + + if (dma_rx_status & (RX_DMA_DONE | RX_EOT)) + qcom_geni_serial_handle_rx_dma(uport, drop_rx); + } + } else { + if (m_irq_status & m_irq_en & + (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) + qcom_geni_serial_handle_tx_fifo(uport, + m_irq_status & M_CMD_DONE_EN, + geni_status & M_GENI_CMD_ACTIVE); + + if (s_irq_status & (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN)) + qcom_geni_serial_handle_rx_fifo(uport, drop_rx); + } out_unlock: uart_unlock_and_check_sysrq(uport); @@ -876,11 +1055,11 @@ static int setup_fifos(struct qcom_geni_serial_port *port) uport->fifosize = (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE; - if (port->rx_fifo && (old_rx_fifo_depth != port->rx_fifo_depth) && port->rx_fifo_depth) { - port->rx_fifo = devm_krealloc(uport->dev, port->rx_fifo, - port->rx_fifo_depth * sizeof(u32), - GFP_KERNEL); - if (!port->rx_fifo) + if (port->rx_buf && (old_rx_fifo_depth != port->rx_fifo_depth) && port->rx_fifo_depth) { + port->rx_buf = devm_krealloc(uport->dev, port->rx_buf, + port->rx_fifo_depth * sizeof(u32), + GFP_KERNEL); + if (!port->rx_buf) return -ENOMEM; } @@ -891,11 +1070,13 @@ static int setup_fifos(struct qcom_geni_serial_port *port) static void qcom_geni_serial_shutdown(struct uart_port *uport) { disable_irq(uport->irq); + qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_rx(uport); } static int qcom_geni_serial_port_setup(struct uart_port *uport) { - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); u32 rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; u32 proto; u32 pin_swap; @@ -937,7 +1118,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) geni_se_config_packing(&port->se, BITS_PER_BYTE, BYTES_PER_FIFO_WORD, false, true, true); geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2); - geni_se_select_mode(&port->se, GENI_SE_FIFO); + geni_se_select_mode(&port->se, port->dev_data->mode); qcom_geni_serial_start_rx(uport); port->setup = true; @@ -947,7 +1128,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) static int qcom_geni_serial_startup(struct uart_port *uport) { int ret; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); if (!port->setup) { ret = qcom_geni_serial_port_setup(uport); @@ -1033,7 +1214,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, u32 stop_bit_len; unsigned int clk_div; u32 ser_clk_cfg; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); unsigned long clk_rate; u32 ver, sampling_rate; unsigned int avg_bw_core; @@ -1137,11 +1318,6 @@ out_restart_rx: qcom_geni_serial_start_rx(uport); } -static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport) -{ - return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS); -} - #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE static int qcom_geni_console_setup(struct console *co, char *options) { @@ -1323,7 +1499,7 @@ static struct uart_driver qcom_geni_uart_driver = { static void qcom_geni_serial_pm(struct uart_port *uport, unsigned int new_state, unsigned int old_state) { - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + struct qcom_geni_serial_port *port = to_dev_port(uport); /* If we've never been called, treat it as off */ if (old_state == UART_PM_STATE_UNDEFINED) @@ -1341,10 +1517,10 @@ static void qcom_geni_serial_pm(struct uart_port *uport, static const struct uart_ops qcom_geni_console_pops = { .tx_empty = qcom_geni_serial_tx_empty, - .stop_tx = qcom_geni_serial_stop_tx, - .start_tx = qcom_geni_serial_start_tx, - .stop_rx = qcom_geni_serial_stop_rx, - .start_rx = qcom_geni_serial_start_rx, + .stop_tx = qcom_geni_serial_stop_tx_fifo, + .start_tx = qcom_geni_serial_start_tx_fifo, + .stop_rx = qcom_geni_serial_stop_rx_fifo, + .start_rx = qcom_geni_serial_start_rx_fifo, .set_termios = qcom_geni_serial_set_termios, .startup = qcom_geni_serial_startup, .request_port = qcom_geni_serial_request_port, @@ -1362,9 +1538,10 @@ static const struct uart_ops qcom_geni_console_pops = { static const struct uart_ops qcom_geni_uart_pops = { .tx_empty = qcom_geni_serial_tx_empty, - .stop_tx = qcom_geni_serial_stop_tx, - .start_tx = qcom_geni_serial_start_tx, - .stop_rx = qcom_geni_serial_stop_rx, + .stop_tx = qcom_geni_serial_stop_tx_dma, + .start_tx = qcom_geni_serial_start_tx_dma, + .start_rx = qcom_geni_serial_start_rx_dma, + .stop_rx = qcom_geni_serial_stop_rx_dma, .set_termios = qcom_geni_serial_set_termios, .startup = qcom_geni_serial_startup, .request_port = qcom_geni_serial_request_port, @@ -1384,13 +1561,14 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) struct uart_port *uport; struct resource *res; int irq; - bool console = false; struct uart_driver *drv; + const struct qcom_geni_device_data *data; - if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart")) - console = true; + data = of_device_get_match_data(&pdev->dev); + if (!data) + return -EINVAL; - if (console) { + if (data->console) { drv = &qcom_geni_console_driver; line = of_alias_get_id(pdev->dev.of_node, "serial"); } else { @@ -1400,7 +1578,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) line = of_alias_get_id(pdev->dev.of_node, "hsuart"); } - port = get_port_from_line(line, console); + port = get_port_from_line(line, data->console); if (IS_ERR(port)) { dev_err(&pdev->dev, "Invalid line %d\n", line); return PTR_ERR(port); @@ -1412,6 +1590,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return -ENODEV; uport->dev = &pdev->dev; + port->dev_data = data; port->se.dev = &pdev->dev; port->se.wrapper = dev_get_drvdata(pdev->dev.parent); port->se.clk = devm_clk_get(&pdev->dev, "se"); @@ -1430,10 +1609,10 @@ 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) + if (!data->console) { + port->rx_buf = devm_kzalloc(uport->dev, + DMA_RX_BUF_SIZE, GFP_KERNEL); + if (!port->rx_buf) return -ENOMEM; } @@ -1460,7 +1639,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) uport->irq = irq; uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE); - if (!console) + if (!data->console) port->wakeup_irq = platform_get_irq_optional(pdev, 1); if (of_property_read_bool(pdev->dev.of_node, "rx-tx-swap")) @@ -1482,7 +1661,6 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) port->private_data.drv = drv; uport->private_data = &port->private_data; platform_set_drvdata(pdev, port); - port->handle_rx = console ? handle_rx_console : handle_rx_uart; ret = uart_add_one_port(drv, uport); if (ret) @@ -1594,6 +1772,16 @@ static int qcom_geni_serial_sys_hib_resume(struct device *dev) return ret; } +static const struct qcom_geni_device_data qcom_geni_console_data = { + .console = true, + .mode = GENI_SE_FIFO, +}; + +static const struct qcom_geni_device_data qcom_geni_uart_data = { + .console = false, + .mode = GENI_SE_DMA, +}; + static const struct dev_pm_ops qcom_geni_serial_pm_ops = { .suspend = pm_sleep_ptr(qcom_geni_serial_sys_suspend), .resume = pm_sleep_ptr(qcom_geni_serial_sys_resume), @@ -1604,8 +1792,14 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = { }; static const struct of_device_id qcom_geni_serial_match_table[] = { - { .compatible = "qcom,geni-debug-uart", }, - { .compatible = "qcom,geni-uart", }, + { + .compatible = "qcom,geni-debug-uart", + .data = &qcom_geni_console_data, + }, + { + .compatible = "qcom,geni-uart", + .data = &qcom_geni_uart_data, + }, {} }; MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 39f92eb1e698..29c94be09159 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1423,25 +1423,6 @@ static int sc16is7xx_probe(struct device *dev, } sched_set_fifo(s->kworker_task); -#ifdef CONFIG_GPIOLIB - if (devtype->nr_gpio) { - /* Setup GPIO cotroller */ - s->gpio.owner = THIS_MODULE; - s->gpio.parent = dev; - s->gpio.label = dev_name(dev); - s->gpio.direction_input = sc16is7xx_gpio_direction_input; - s->gpio.get = sc16is7xx_gpio_get; - s->gpio.direction_output = sc16is7xx_gpio_direction_output; - s->gpio.set = sc16is7xx_gpio_set; - s->gpio.base = -1; - s->gpio.ngpio = devtype->nr_gpio; - s->gpio.can_sleep = 1; - ret = gpiochip_add_data(&s->gpio, s); - if (ret) - goto out_thread; - } -#endif - /* reset device, purging any pending irq / data */ regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT, SC16IS7XX_IOCONTROL_SRESET_BIT); @@ -1518,6 +1499,25 @@ static int sc16is7xx_probe(struct device *dev, s->p[u].irda_mode = true; } +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) { + /* Setup GPIO cotroller */ + s->gpio.owner = THIS_MODULE; + s->gpio.parent = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = sc16is7xx_gpio_direction_input; + s->gpio.get = sc16is7xx_gpio_get; + s->gpio.direction_output = sc16is7xx_gpio_direction_output; + s->gpio.set = sc16is7xx_gpio_set; + s->gpio.base = -1; + s->gpio.ngpio = devtype->nr_gpio; + s->gpio.can_sleep = 1; + ret = gpiochip_add_data(&s->gpio, s); + if (ret) + goto out_thread; + } +#endif + /* * Setup interrupt. We first try to acquire the IRQ line as level IRQ. * If that succeeds, we can allow sharing the interrupt as well. @@ -1537,18 +1537,19 @@ static int sc16is7xx_probe(struct device *dev, if (!ret) return 0; -out_ports: - for (i--; i >= 0; i--) { - uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); - clear_bit(s->p[i].port.line, &sc16is7xx_lines); - } - #ifdef CONFIG_GPIOLIB if (devtype->nr_gpio) gpiochip_remove(&s->gpio); out_thread: #endif + +out_ports: + for (i--; i >= 0; i--) { + uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); + clear_bit(s->p[i].port.line, &sc16is7xx_lines); + } + kthread_stop(s->kworker_task); out_clk: diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 7df687822634..4f2fc5f7bb19 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -913,23 +913,13 @@ static int sccnxp_probe(struct platform_device *pdev) } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER) return -EPROBE_DEFER; - clk = devm_clk_get(&pdev->dev, NULL); + clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); if (ret == -EPROBE_DEFER) goto err_out; uartclk = 0; } else { - ret = clk_prepare_enable(clk); - if (ret) - goto err_out; - - ret = devm_add_action_or_reset(&pdev->dev, - (void(*)(void *))clk_disable_unprepare, - clk); - if (ret) - goto err_out; - uartclk = clk_get_rate(clk); } diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index e5b9773db5e3..1cf08b33456c 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1046,6 +1046,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) if (tup->cdata->fifo_mode_enable_status) { ret = tegra_uart_wait_fifo_mode_enabled(tup); if (ret < 0) { + clk_disable_unprepare(tup->uart_clk); dev_err(tup->uport.dev, "Failed to enable FIFO mode: %d\n", ret); return ret; @@ -1067,6 +1068,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) */ ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD); if (ret < 0) { + clk_disable_unprepare(tup->uart_clk); dev_err(tup->uport.dev, "Failed to set baud rate\n"); return ret; } @@ -1226,10 +1228,13 @@ static int tegra_uart_startup(struct uart_port *u) dev_name(u->dev), tup); if (ret < 0) { dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq); - goto fail_hw_init; + goto fail_request_irq; } return 0; +fail_request_irq: + /* tup->uart_clk is already enabled in tegra_uart_hw_init */ + clk_disable_unprepare(tup->uart_clk); fail_hw_init: if (!tup->use_rx_pio) tegra_uart_dma_channel_free(tup, true); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ec874f3a567c..2bd32c8ece39 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -169,9 +169,9 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) #define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0) #define uart_clear_mctrl(port, clear) uart_update_mctrl(port, 0, clear) -static void uart_port_dtr_rts(struct uart_port *uport, int raise) +static void uart_port_dtr_rts(struct uart_port *uport, bool active) { - if (raise) + if (active) uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); else uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); @@ -182,7 +182,7 @@ static void uart_port_dtr_rts(struct uart_port *uport, int raise) * will be serialised by the per-port mutex. */ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, - int init_hw) + bool init_hw) { struct uart_port *uport = uart_port_check(state); unsigned long flags; @@ -239,7 +239,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, * port is open and ready to respond. */ if (init_hw && C_BAUD(tty)) - uart_port_dtr_rts(uport, 1); + uart_port_dtr_rts(uport, true); } /* @@ -254,7 +254,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, } static int uart_startup(struct tty_struct *tty, struct uart_state *state, - int init_hw) + bool init_hw) { struct tty_port *port = &state->port; int retval; @@ -290,7 +290,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) set_bit(TTY_IO_ERROR, &tty->flags); if (tty_port_initialized(port)) { - tty_port_set_initialized(port, 0); + tty_port_set_initialized(port, false); /* * Turn off DTR and RTS early. @@ -302,7 +302,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) } if (!tty || C_HUPCL(tty)) - uart_port_dtr_rts(uport, 0); + uart_port_dtr_rts(uport, false); uart_port_shutdown(port); } @@ -312,7 +312,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) * a DCD drop (hangup) at just the right time. Clear suspended bit so * we don't try to resume a port that has been shutdown. */ - tty_port_set_suspended(port, 0); + tty_port_set_suspended(port, false); /* * Do not free() the transmit buffer page under the port lock since @@ -997,7 +997,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, uart_change_speed(tty, state, NULL); } } else { - retval = uart_startup(tty, state, 1); + retval = uart_startup(tty, state, true); if (retval == 0) tty_port_set_initialized(port, true); if (retval > 0) @@ -1165,7 +1165,7 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) */ uport->ops->config_port(uport, flags); - ret = uart_startup(tty, state, 1); + ret = uart_startup(tty, state, true); if (ret == 0) tty_port_set_initialized(port, true); if (ret > 0) @@ -1725,7 +1725,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) * a DCD drop (hangup) at just the right time. Clear suspended bit so * we don't try to resume a port that has been shutdown. */ - tty_port_set_suspended(port, 0); + tty_port_set_suspended(port, false); /* * Free the transmit buffer. @@ -1827,7 +1827,7 @@ static void uart_hangup(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); port->count = 0; spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); + tty_port_set_active(port, false); tty_port_tty_set(port, NULL); if (uport && !uart_console(uport)) uart_change_pm(state, UART_PM_STATE_OFF); @@ -1861,7 +1861,7 @@ static void uart_port_shutdown(struct tty_port *port) } } -static int uart_carrier_raised(struct tty_port *port) +static bool uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; @@ -1875,18 +1875,17 @@ static int uart_carrier_raised(struct tty_port *port) * continue and not sleep */ if (WARN_ON(!uport)) - return 1; + return true; spin_lock_irq(&uport->lock); uart_enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); uart_port_deref(uport); - if (mctrl & TIOCM_CAR) - return 1; - return 0; + + return mctrl & TIOCM_CAR; } -static void uart_dtr_rts(struct tty_port *port, int raise) +static void uart_dtr_rts(struct tty_port *port, bool active) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; @@ -1894,7 +1893,7 @@ static void uart_dtr_rts(struct tty_port *port, int raise) uport = uart_port_ref(state); if (!uport) return; - uart_port_dtr_rts(uport, raise); + uart_port_dtr_rts(uport, active); uart_port_deref(uport); } @@ -1943,9 +1942,9 @@ static int uart_port_activate(struct tty_port *port, struct tty_struct *tty) /* * Start up the serial port. */ - ret = uart_startup(tty, state, 0); + ret = uart_startup(tty, state, false); if (ret > 0) - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return ret; } @@ -2349,8 +2348,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) int tries; unsigned int mctrl; - tty_port_set_suspended(port, 1); - tty_port_set_initialized(port, 0); + tty_port_set_suspended(port, true); + tty_port_set_initialized(port, false); spin_lock_irq(&uport->lock); ops->stop_tx(uport); @@ -2461,7 +2460,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) uart_rs485_config(uport); ops->start_tx(uport); spin_unlock_irq(&uport->lock); - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); } else { /* * Failed to resume - maybe hardware went away? @@ -2472,7 +2471,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) } } - tty_port_set_suspended(port, 0); + tty_port_set_suspended(port, false); } mutex_unlock(&port->mutex); @@ -3258,11 +3257,11 @@ EXPORT_SYMBOL(uart_match_port); /** * uart_handle_dcd_change - handle a change of carrier detect state * @uport: uart_port structure for the open port - * @status: new carrier detect status, nonzero if active + * @active: new carrier detect status * * Caller must hold uport->lock. */ -void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) +void uart_handle_dcd_change(struct uart_port *uport, bool active) { struct tty_port *port = &uport->state->port; struct tty_struct *tty = port->tty; @@ -3274,7 +3273,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) ld = tty_ldisc_ref(tty); if (ld) { if (ld->ops->dcd_change) - ld->ops->dcd_change(tty, status); + ld->ops->dcd_change(tty, active); tty_ldisc_deref(ld); } } @@ -3282,7 +3281,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) uport->icount.dcd++; if (uart_dcd_enabled(uport)) { - if (status) + if (active) wake_up_interruptible(&port->open_wait); else if (tty) tty_hangup(tty); @@ -3293,11 +3292,11 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change); /** * uart_handle_cts_change - handle a change of clear-to-send state * @uport: uart_port structure for the open port - * @status: new clear to send status, nonzero if active + * @active: new clear-to-send status * * Caller must hold uport->lock. */ -void uart_handle_cts_change(struct uart_port *uport, unsigned int status) +void uart_handle_cts_change(struct uart_port *uport, bool active) { lockdep_assert_held_once(&uport->lock); @@ -3305,13 +3304,13 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) if (uart_softcts_mode(uport)) { if (uport->hw_stopped) { - if (status) { + if (active) { uport->hw_stopped = 0; uport->ops->start_tx(uport); uart_write_wakeup(uport); } } else { - if (!status) { + if (!active) { uport->hw_stopped = 1; uport->ops->stop_tx(uport); } @@ -3415,6 +3414,7 @@ int uart_get_rs485_mode(struct uart_port *port) struct device *dev = port->dev; u32 rs485_delay[2]; int ret; + int rx_during_tx_gpio_flag; ret = device_property_read_u32_array(dev, "rs485-rts-delay", rs485_delay, 2); @@ -3463,6 +3463,17 @@ int uart_get_rs485_mode(struct uart_port *port) if (port->rs485_term_gpio) port->rs485_supported.flags |= SER_RS485_TERMINATE_BUS; + rx_during_tx_gpio_flag = (rs485conf->flags & SER_RS485_RX_DURING_TX) ? + GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + port->rs485_rx_during_tx_gpio = devm_gpiod_get_optional(dev, + "rs485-rx-during-tx", + rx_during_tx_gpio_flag); + if (IS_ERR(port->rs485_rx_during_tx_gpio)) { + ret = PTR_ERR(port->rs485_rx_during_tx_gpio); + port->rs485_rx_during_tx_gpio = NULL; + return dev_err_probe(dev, ret, "Cannot get rs485-rx-during-tx-gpios\n"); + } + return 0; } EXPORT_SYMBOL_GPL(uart_get_rs485_mode); diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 409e91d6829a..767ff9fdb2e5 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -226,7 +226,11 @@ static int stm32_usart_config_rs485(struct uart_port *port, struct ktermios *ter stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - rs485conf->flags |= SER_RS485_RX_DURING_TX; + if (port->rs485_rx_during_tx_gpio) + gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, + !!(rs485conf->flags & SER_RS485_RX_DURING_TX)); + else + rs485conf->flags |= SER_RS485_RX_DURING_TX; if (rs485conf->flags & SER_RS485_ENABLED) { cr1 = readl_relaxed(port->membase + ofs->cr1); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 16c746a63258..7d38c33ef506 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -87,10 +87,10 @@ static int receive_chars_getchar(struct uart_port *port) if (c == CON_HUP) { hung_up = 1; - uart_handle_dcd_change(port, 0); + uart_handle_dcd_change(port, false); } else if (hung_up) { hung_up = 0; - uart_handle_dcd_change(port, 1); + uart_handle_dcd_change(port, true); } if (port->state == NULL) { @@ -133,7 +133,7 @@ static int receive_chars_read(struct uart_port *port) bytes_read = 1; } else if (stat == CON_HUP) { hung_up = 1; - uart_handle_dcd_change(port, 0); + uart_handle_dcd_change(port, false); continue; } else { /* HV_EWOULDBLOCK, etc. */ @@ -143,7 +143,7 @@ static int receive_chars_read(struct uart_port *port) if (hung_up) { hung_up = 0; - uart_handle_dcd_change(port, 1); + uart_handle_dcd_change(port, true); } if (port->sysrq != 0 && *con_read_page) { diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index b09b6496ee3e..32c7a5b43f8e 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1468,6 +1468,8 @@ static int ucc_uart_remove(struct platform_device *ofdev) uart_remove_one_port(&ucc_uart_driver, &qe_port->port); + of_node_put(qe_port->np); + kfree(qe_port); return 0; diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 72b76cdde534..33f258d6fef9 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -694,7 +694,7 @@ static void hangup(struct tty_struct *tty) info->port.count = 0; info->port.tty = NULL; spin_unlock_irqrestore(&info->port.lock, flags); - tty_port_set_active(&info->port, 0); + tty_port_set_active(&info->port, false); mutex_unlock(&info->port.mutex); wake_up_interruptible(&info->port.open_wait); @@ -2354,7 +2354,7 @@ static int startup(struct slgt_info *info) if (info->port.tty) clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - tty_port_set_initialized(&info->port, 1); + tty_port_set_initialized(&info->port, true); return 0; } @@ -2401,7 +2401,7 @@ static void shutdown(struct slgt_info *info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - tty_port_set_initialized(&info->port, 0); + tty_port_set_initialized(&info->port, false); } static void program_hw(struct slgt_info *info) @@ -3126,7 +3126,7 @@ static int tiocmset(struct tty_struct *tty, return 0; } -static int carrier_raised(struct tty_port *port) +static bool carrier_raised(struct tty_port *port) { unsigned long flags; struct slgt_info *info = container_of(port, struct slgt_info, port); @@ -3134,16 +3134,17 @@ static int carrier_raised(struct tty_port *port) spin_lock_irqsave(&info->lock,flags); get_gtsignals(info); spin_unlock_irqrestore(&info->lock,flags); - return (info->signals & SerialSignal_DCD) ? 1 : 0; + + return info->signals & SerialSignal_DCD; } -static void dtr_rts(struct tty_port *port, int on) +static void dtr_rts(struct tty_port *port, bool active) { unsigned long flags; struct slgt_info *info = container_of(port, struct slgt_info, port); spin_lock_irqsave(&info->lock,flags); - if (on) + if (active) info->signals |= SerialSignal_RTS | SerialSignal_DTR; else info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); @@ -3162,14 +3163,14 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, int retval; bool do_clocal = false; unsigned long flags; - int cd; + bool cd; struct tty_port *port = &info->port; DBGINFO(("%s block_til_ready\n", tty->driver->name)); if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) { /* nonblock mode is set or port is not enabled */ - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return 0; } @@ -3226,7 +3227,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open--; if (!retval) - tty_port_set_active(port, 1); + tty_port_set_active(port, true); DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval)); return retval; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 3149114bf130..36fb945fdad4 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1224,14 +1224,16 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, { struct tty_struct *tty; - if (driver->ops->lookup) + if (driver->ops->lookup) { if (!file) tty = ERR_PTR(-EIO); else tty = driver->ops->lookup(driver, file, idx); - else + } else { + if (idx >= driver->num) + return ERR_PTR(-EINVAL); tty = driver->ttys[idx]; - + } if (!IS_ERR(tty)) tty_kref_get(tty); return tty; diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index ce511557b98b..12983ce4e43e 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -270,13 +270,13 @@ EXPORT_SYMBOL(tty_termios_copy_hw); * between the two termios structures, or a speed change is needed. */ -int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b) +bool tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b) { if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed) - return 1; + return true; if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL)) - return 1; - return 0; + return true; + return false; } EXPORT_SYMBOL(tty_termios_hw_change); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index dce08a6d7b5e..a788a6bf487d 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -367,7 +367,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) goto out; if (tty_port_initialized(port)) { - tty_port_set_initialized(port, 0); + tty_port_set_initialized(port, false); /* * Drop DTR/RTS if HUPCL is set. This causes any attached * modem to hang up the line. @@ -403,7 +403,7 @@ void tty_port_hangup(struct tty_port *port) set_bit(TTY_IO_ERROR, &tty->flags); port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); + tty_port_set_active(port, false); tty_port_shutdown(port, tty); tty_kref_put(tty); wake_up_interruptible(&port->open_wait); @@ -444,10 +444,10 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); * to hide some internal details. This will eventually become entirely * internal to the tty port. */ -int tty_port_carrier_raised(struct tty_port *port) +bool tty_port_carrier_raised(struct tty_port *port) { if (port->ops->carrier_raised == NULL) - return 1; + return true; return port->ops->carrier_raised(port); } EXPORT_SYMBOL(tty_port_carrier_raised); @@ -463,7 +463,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised); void tty_port_raise_dtr_rts(struct tty_port *port) { if (port->ops->dtr_rts) - port->ops->dtr_rts(port, 1); + port->ops->dtr_rts(port, true); } EXPORT_SYMBOL(tty_port_raise_dtr_rts); @@ -478,7 +478,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts); void tty_port_lower_dtr_rts(struct tty_port *port) { if (port->ops->dtr_rts) - port->ops->dtr_rts(port, 0); + port->ops->dtr_rts(port, false); } EXPORT_SYMBOL(tty_port_lower_dtr_rts); @@ -518,14 +518,14 @@ int tty_port_block_til_ready(struct tty_port *port, * the port has just hung up or is in another error state. */ if (tty_io_error(tty)) { - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return 0; } if (filp == NULL || (filp->f_flags & O_NONBLOCK)) { /* Indicate we are open */ if (C_BAUD(tty)) tty_port_raise_dtr_rts(port); - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return 0; } @@ -588,7 +588,7 @@ int tty_port_block_til_ready(struct tty_port *port, port->blocked_open--; spin_unlock_irqrestore(&port->lock, flags); if (retval == 0) - tty_port_set_active(port, 1); + tty_port_set_active(port, true); return retval; } EXPORT_SYMBOL(tty_port_block_til_ready); @@ -695,7 +695,7 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) wake_up_interruptible(&port->open_wait); } spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); + tty_port_set_active(port, false); } EXPORT_SYMBOL(tty_port_close_end); @@ -788,7 +788,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, return retval; } } - tty_port_set_initialized(port, 1); + tty_port_set_initialized(port, true); } mutex_unlock(&port->mutex); return tty_port_block_til_ready(port, tty, filp); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 981d2bfcf9a5..57a5c23b51d4 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -316,73 +316,55 @@ void schedule_console_callback(void) * Code to manage unicode-based screen buffers */ -#ifdef NO_VC_UNI_SCREEN -/* this disables and optimizes related code away at compile time */ -#define get_vc_uniscr(vc) NULL -#else -#define get_vc_uniscr(vc) vc->vc_uni_screen -#endif - -#define VC_UNI_SCREEN_DEBUG 0 - -typedef uint32_t char32_t; - /* * Our screen buffer is preceded by an array of line pointers so that * scrolling only implies some pointer shuffling. */ -struct uni_screen { - char32_t *lines[0]; -}; -static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows) +static u32 **vc_uniscr_alloc(unsigned int cols, unsigned int rows) { - struct uni_screen *uniscr; + u32 **uni_lines; void *p; - unsigned int memsize, i; + unsigned int memsize, i, col_size = cols * sizeof(**uni_lines); /* allocate everything in one go */ - memsize = cols * rows * sizeof(char32_t); - memsize += rows * sizeof(char32_t *); - p = vzalloc(memsize); - if (!p) + memsize = col_size * rows; + memsize += rows * sizeof(*uni_lines); + uni_lines = vzalloc(memsize); + if (!uni_lines) return NULL; /* initial line pointers */ - uniscr = p; - p = uniscr->lines + rows; + p = uni_lines + rows; for (i = 0; i < rows; i++) { - uniscr->lines[i] = p; - p += cols * sizeof(char32_t); + uni_lines[i] = p; + p += col_size; } - return uniscr; + + return uni_lines; } -static void vc_uniscr_free(struct uni_screen *uniscr) +static void vc_uniscr_free(u32 **uni_lines) { - vfree(uniscr); + vfree(uni_lines); } -static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr) +static void vc_uniscr_set(struct vc_data *vc, u32 **new_uni_lines) { - vc_uniscr_free(vc->vc_uni_screen); - vc->vc_uni_screen = new_uniscr; + vc_uniscr_free(vc->vc_uni_lines); + vc->vc_uni_lines = new_uni_lines; } -static void vc_uniscr_putc(struct vc_data *vc, char32_t uc) +static void vc_uniscr_putc(struct vc_data *vc, u32 uc) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) - uniscr->lines[vc->state.y][vc->state.x] = uc; + if (vc->vc_uni_lines) + vc->vc_uni_lines[vc->state.y][vc->state.x] = uc; } static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; + if (vc->vc_uni_lines) { + u32 *ln = vc->vc_uni_lines[vc->state.y]; unsigned int x = vc->state.x, cols = vc->vc_cols; memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln)); @@ -392,10 +374,8 @@ static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr) static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; + if (vc->vc_uni_lines) { + u32 *ln = vc->vc_uni_lines[vc->state.y]; unsigned int x = vc->state.x, cols = vc->vc_cols; memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln)); @@ -406,86 +386,84 @@ static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr) static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; - - memset32(&ln[x], ' ', nr); - } + if (vc->vc_uni_lines) + memset32(&vc->vc_uni_lines[vc->state.y][x], ' ', nr); } static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + if (vc->vc_uni_lines) + while (nr--) + memset32(vc->vc_uni_lines[y++], ' ', vc->vc_cols); +} + +/* juggling array rotation algorithm (complexity O(N), size complexity O(1)) */ +static void juggle_array(u32 **array, unsigned int size, unsigned int nr) +{ + unsigned int gcd_idx; - if (uniscr) { - unsigned int cols = vc->vc_cols; + for (gcd_idx = 0; gcd_idx < gcd(nr, size); gcd_idx++) { + u32 *gcd_idx_val = array[gcd_idx]; + unsigned int dst_idx = gcd_idx; - while (nr--) - memset32(uniscr->lines[y++], ' ', cols); + while (1) { + unsigned int src_idx = (dst_idx + nr) % size; + if (src_idx == gcd_idx) + break; + + array[dst_idx] = array[src_idx]; + dst_idx = src_idx; + } + + array[dst_idx] = gcd_idx_val; } } -static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b, - enum con_scroll dir, unsigned int nr) +static void vc_uniscr_scroll(struct vc_data *vc, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + u32 **uni_lines = vc->vc_uni_lines; + unsigned int size = bottom - top; - if (uniscr) { - unsigned int i, j, k, sz, d, clear; + if (!uni_lines) + return; - sz = b - t; - clear = b - nr; - d = nr; - if (dir == SM_DOWN) { - clear = t; - d = sz - nr; - } - for (i = 0; i < gcd(d, sz); i++) { - char32_t *tmp = uniscr->lines[t + i]; - j = i; - while (1) { - k = j + d; - if (k >= sz) - k -= sz; - if (k == i) - break; - uniscr->lines[t + j] = uniscr->lines[t + k]; - j = k; - } - uniscr->lines[t + j] = tmp; - } - vc_uniscr_clear_lines(vc, clear, nr); + if (dir == SM_DOWN) { + juggle_array(&uni_lines[top], size, size - nr); + vc_uniscr_clear_lines(vc, top, nr); + } else { + juggle_array(&uni_lines[top], size, nr); + vc_uniscr_clear_lines(vc, bottom - nr, nr); } } -static void vc_uniscr_copy_area(struct uni_screen *dst, +static void vc_uniscr_copy_area(u32 **dst_lines, unsigned int dst_cols, unsigned int dst_rows, - struct uni_screen *src, + u32 **src_lines, unsigned int src_cols, unsigned int src_top_row, unsigned int src_bot_row) { unsigned int dst_row = 0; - if (!dst) + if (!dst_lines) return; while (src_top_row < src_bot_row) { - char32_t *src_line = src->lines[src_top_row]; - char32_t *dst_line = dst->lines[dst_row]; + u32 *src_line = src_lines[src_top_row]; + u32 *dst_line = dst_lines[dst_row]; - memcpy(dst_line, src_line, src_cols * sizeof(char32_t)); + memcpy(dst_line, src_line, src_cols * sizeof(*src_line)); if (dst_cols - src_cols) memset32(dst_line + src_cols, ' ', dst_cols - src_cols); src_top_row++; dst_row++; } while (dst_row < dst_rows) { - char32_t *dst_line = dst->lines[dst_row]; + u32 *dst_line = dst_lines[dst_row]; memset32(dst_line, ' ', dst_cols); dst_row++; @@ -500,23 +478,20 @@ static void vc_uniscr_copy_area(struct uni_screen *dst, */ int vc_uniscr_check(struct vc_data *vc) { - struct uni_screen *uniscr; + u32 **uni_lines; unsigned short *p; int x, y, mask; - if (__is_defined(NO_VC_UNI_SCREEN)) - return -EOPNOTSUPP; - WARN_CONSOLE_UNLOCKED(); if (!vc->vc_utf) return -ENODATA; - if (vc->vc_uni_screen) + if (vc->vc_uni_lines) return 0; - uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); - if (!uniscr) + uni_lines = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); + if (!uni_lines) return -ENOMEM; /* @@ -528,14 +503,15 @@ int vc_uniscr_check(struct vc_data *vc) p = (unsigned short *)vc->vc_origin; mask = vc->vc_hi_font_mask | 0xff; for (y = 0; y < vc->vc_rows; y++) { - char32_t *line = uniscr->lines[y]; + u32 *line = uni_lines[y]; for (x = 0; x < vc->vc_cols; x++) { u16 glyph = scr_readw(p++) & mask; line[x] = inverse_translate(vc, glyph, true); } } - vc->vc_uni_screen = uniscr; + vc->vc_uni_lines = uni_lines; + return 0; } @@ -547,11 +523,12 @@ int vc_uniscr_check(struct vc_data *vc) void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, unsigned int row, unsigned int col, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + u32 **uni_lines = vc->vc_uni_lines; int offset = row * vc->vc_size_row + col * 2; unsigned long pos; - BUG_ON(!uniscr); + if (WARN_ON_ONCE(!uni_lines)) + return; pos = (unsigned long)screenpos(vc, offset, viewed); if (pos >= vc->vc_origin && pos < vc->vc_scr_end) { @@ -562,7 +539,7 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, */ row = (pos - vc->vc_origin) / vc->vc_size_row; col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2; - memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t)); + memcpy(dest, &uni_lines[row][col], nr * sizeof(u32)); } else { /* * Scrollback is active. For now let's simply backtranslate @@ -572,7 +549,7 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, */ u16 *p = (u16 *)pos; int mask = vc->vc_hi_font_mask | 0xff; - char32_t *uni_buf = dest; + u32 *uni_buf = dest; while (nr--) { u16 glyph = scr_readw(p++) & mask; *uni_buf++ = inverse_translate(vc, glyph, true); @@ -580,64 +557,31 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, } } -/* this is for validation and debugging only */ -static void vc_uniscr_debug_check(struct vc_data *vc) +static void con_scroll(struct vc_data *vc, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); - unsigned short *p; - int x, y, mask; + unsigned int rows = bottom - top; + u16 *clear, *dst, *src; - if (!VC_UNI_SCREEN_DEBUG || !uniscr) + if (top + nr >= bottom) + nr = rows - 1; + if (bottom > vc->vc_rows || top >= bottom || nr < 1) return; - WARN_CONSOLE_UNLOCKED(); - - /* - * Make sure our unicode screen translates into the same glyphs - * as the actual screen. This is brutal indeed. - */ - p = (unsigned short *)vc->vc_origin; - mask = vc->vc_hi_font_mask | 0xff; - for (y = 0; y < vc->vc_rows; y++) { - char32_t *line = uniscr->lines[y]; - for (x = 0; x < vc->vc_cols; x++) { - u16 glyph = scr_readw(p++) & mask; - char32_t uc = line[x]; - int tc = conv_uni_to_pc(vc, uc); - if (tc == -4) - tc = conv_uni_to_pc(vc, 0xfffd); - if (tc == -4) - tc = conv_uni_to_pc(vc, '?'); - if (tc != glyph) - pr_err_ratelimited( - "%s: mismatch at %d,%d: glyph=%#x tc=%#x\n", - __func__, x, y, glyph, tc); - } - } -} - - -static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, - enum con_scroll dir, unsigned int nr) -{ - u16 *clear, *d, *s; - - if (t + nr >= b) - nr = b - t - 1; - if (b > vc->vc_rows || t >= b || nr < 1) - return; - vc_uniscr_scroll(vc, t, b, dir, nr); - if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr)) + vc_uniscr_scroll(vc, top, bottom, dir, nr); + if (con_is_visible(vc) && + vc->vc_sw->con_scroll(vc, top, bottom, dir, nr)) return; - s = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * t); - d = (u16 *)(vc->vc_origin + vc->vc_size_row * (t + nr)); + src = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * top); + dst = (u16 *)(vc->vc_origin + vc->vc_size_row * (top + nr)); if (dir == SM_UP) { - clear = s + (b - t - nr) * vc->vc_cols; - swap(s, d); + clear = src + (rows - nr) * vc->vc_cols; + swap(src, dst); } - scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); + scr_memmovew(dst, src, (rows - nr) * vc->vc_size_row); scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr); } @@ -1201,7 +1145,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, unsigned int new_cols, new_rows, new_row_size, new_screen_size; unsigned int user; unsigned short *oldscreen, *newscreen; - struct uni_screen *new_uniscr = NULL; + u32 **new_uniscr = NULL; WARN_CONSOLE_UNLOCKED(); @@ -1245,7 +1189,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (!newscreen) return -ENOMEM; - if (get_vc_uniscr(vc)) { + if (vc->vc_uni_lines) { new_uniscr = vc_uniscr_alloc(new_cols, new_rows); if (!new_uniscr) { kfree(newscreen); @@ -1297,7 +1241,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, end = old_origin + old_row_size * min(old_rows, new_rows); vc_uniscr_copy_area(new_uniscr, new_cols, new_rows, - get_vc_uniscr(vc), rlth/2, first_copied_row, + vc->vc_uni_lines, rlth/2, first_copied_row, min(old_rows, new_rows)); vc_uniscr_set(vc, new_uniscr); @@ -2959,7 +2903,6 @@ rescan_last_byte: goto rescan_last_byte; } con_flush(vc, &draw); - vc_uniscr_debug_check(vc); console_conditional_schedule(); notify_update(vc); console_unlock(); @@ -3156,8 +3099,14 @@ static struct tty_driver *vt_console_device(struct console *c, int *index) return console_driver; } +static int vt_console_setup(struct console *co, char *options) +{ + return co->index >= MAX_NR_CONSOLES ? -EINVAL : 0; +} + static struct console vt_console_driver = { .name = "tty", + .setup = vt_console_setup, .write = vt_console_print, .device = vt_console_device, .unblank = unblank_screen, @@ -4574,26 +4523,30 @@ void reset_palette(struct vc_data *vc) /* * Font switching * - * Currently we only support fonts up to 32 pixels wide, at a maximum height - * of 32 pixels. Userspace fontdata is stored with 32 bytes (shorts/ints, - * depending on width) reserved for each character which is kinda wasty, but - * this is done in order to maintain compatibility with the EGA/VGA fonts. It - * is up to the actual low-level console-driver convert data into its favorite - * format (maybe we should add a `fontoffset' field to the `display' - * structure so we won't have to convert the fontdata all the time. + * Currently we only support fonts up to 128 pixels wide, at a maximum height + * of 128 pixels. Userspace fontdata may have to be stored with 32 bytes + * (shorts/ints, depending on width) reserved for each character which is + * kinda wasty, but this is done in order to maintain compatibility with the + * EGA/VGA fonts. It is up to the actual low-level console-driver convert data + * into its favorite format (maybe we should add a `fontoffset' field to the + * `display' structure so we won't have to convert the fontdata all the time. * /Jes */ -#define max_font_size 65536 +#define max_font_width 64 +#define max_font_height 128 +#define max_font_glyphs 512 +#define max_font_size (max_font_glyphs*max_font_width*max_font_height) static int con_font_get(struct vc_data *vc, struct console_font_op *op) { struct console_font font; int rc = -EINVAL; int c; + unsigned int vpitch = op->op == KD_FONT_OP_GET_TALL ? op->height : 32; if (op->data) { - font.data = kmalloc(max_font_size, GFP_KERNEL); + font.data = kvmalloc(max_font_size, GFP_KERNEL); if (!font.data) return -ENOMEM; } else @@ -4603,7 +4556,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) if (vc->vc_mode != KD_TEXT) rc = -EINVAL; else if (vc->vc_sw->con_font_get) - rc = vc->vc_sw->con_font_get(vc, &font); + rc = vc->vc_sw->con_font_get(vc, &font, vpitch); else rc = -ENOSYS; console_unlock(); @@ -4611,7 +4564,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) if (rc) goto out; - c = (font.width+7)/8 * 32 * font.charcount; + c = (font.width+7)/8 * vpitch * font.charcount; if (op->data && font.charcount > op->charcount) rc = -ENOSPC; @@ -4628,7 +4581,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) rc = -EFAULT; out: - kfree(font.data); + kvfree(font.data); return rc; } @@ -4637,16 +4590,20 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op) struct console_font font; int rc = -EINVAL; int size; + unsigned int vpitch = op->op == KD_FONT_OP_SET_TALL ? op->height : 32; if (vc->vc_mode != KD_TEXT) return -EINVAL; if (!op->data) return -EINVAL; - if (op->charcount > 512) + if (op->charcount > max_font_glyphs) + return -EINVAL; + if (op->width <= 0 || op->width > max_font_width || !op->height || + op->height > max_font_height) return -EINVAL; - if (op->width <= 0 || op->width > 32 || !op->height || op->height > 32) + if (vpitch < op->height) return -EINVAL; - size = (op->width+7)/8 * 32 * op->charcount; + size = (op->width+7)/8 * vpitch * op->charcount; if (size > max_font_size) return -ENOSPC; @@ -4664,7 +4621,7 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op) else if (vc->vc_sw->con_font_set) { if (vc_is_sel(vc)) clear_selection(); - rc = vc->vc_sw->con_font_set(vc, &font, op->flags); + rc = vc->vc_sw->con_font_set(vc, &font, vpitch, op->flags); } else rc = -ENOSYS; console_unlock(); @@ -4710,8 +4667,10 @@ int con_font_op(struct vc_data *vc, struct console_font_op *op) { switch (op->op) { case KD_FONT_OP_SET: + case KD_FONT_OP_SET_TALL: return con_font_set(vc, op); case KD_FONT_OP_GET: + case KD_FONT_OP_GET_TALL: return con_font_get(vc, op); case KD_FONT_OP_SET_DEFAULT: return con_font_default(vc, op); @@ -4740,10 +4699,11 @@ EXPORT_SYMBOL_GPL(screen_glyph); u32 screen_glyph_unicode(const struct vc_data *vc, int n) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + u32 **uni_lines = vc->vc_uni_lines; + + if (uni_lines) + return uni_lines[n / vc->vc_cols][n % vc->vc_cols]; - if (uniscr) - return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols]; return inverse_translate(vc, screen_glyph(vc, n * 2), true); } EXPORT_SYMBOL_GPL(screen_glyph_unicode); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 36bf051b345b..11da5fb284d0 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -651,13 +651,13 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) return tty_port_open(&acm->port, tty, filp); } -static void acm_port_dtr_rts(struct tty_port *port, int raise) +static void acm_port_dtr_rts(struct tty_port *port, bool active) { struct acm *acm = container_of(port, struct acm, port); int val; int res; - if (raise) + if (active) val = USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS; else val = 0; diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index da19a5fa414f..c3ea3a46ed76 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -169,7 +169,7 @@ static int usb_console_setup(struct console *co, char *options) tty_save_termios(tty); tty_kref_put(tty); } - tty_port_set_initialized(&port->port, 1); + tty_port_set_initialized(&port->port, true); } /* Now that any required fake tty operations are completed restore * the tty port count */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 164521ee10c6..f8404073558b 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -754,7 +754,7 @@ static struct usb_serial_driver *search_serial_device( return NULL; } -static int serial_port_carrier_raised(struct tty_port *port) +static bool serial_port_carrier_raised(struct tty_port *port) { struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); struct usb_serial_driver *drv = p->serial->type; @@ -762,10 +762,10 @@ static int serial_port_carrier_raised(struct tty_port *port) if (drv->carrier_raised) return drv->carrier_raised(p); /* No carrier control - don't block */ - return 1; + return true; } -static void serial_port_dtr_rts(struct tty_port *port, int on) +static void serial_port_dtr_rts(struct tty_port *port, bool on) { struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); struct usb_serial_driver *drv = p->serial->type; diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index d9c682ae0392..e8e4f82cd4a1 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -497,7 +497,7 @@ static int newport_blank(struct vc_data *c, int blank, int mode_switch) return 1; } -static int newport_set_font(int unit, struct console_font *op) +static int newport_set_font(int unit, struct console_font *op, unsigned int vpitch) { int w = op->width; int h = op->height; @@ -507,7 +507,7 @@ static int newport_set_font(int unit, struct console_font *op) /* ladis: when I grow up, there will be a day... and more sizes will * be supported ;-) */ - if ((w != 8) || (h != 16) + if ((w != 8) || (h != 16) || (vpitch != 32) || (op->charcount != 256 && op->charcount != 512)) return -EINVAL; @@ -569,9 +569,10 @@ static int newport_font_default(struct vc_data *vc, struct console_font *op, cha return newport_set_def_font(vc->vc_num, op); } -static int newport_font_set(struct vc_data *vc, struct console_font *font, unsigned flags) +static int newport_font_set(struct vc_data *vc, struct console_font *font, + unsigned int vpitch, unsigned int flags) { - return newport_set_font(vc->vc_num, font); + return newport_set_font(vc->vc_num, font, vpitch); } static bool newport_scroll(struct vc_data *vc, unsigned int t, unsigned int b, diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index f304163e87e9..2cea69418a83 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -169,7 +169,8 @@ static int sticon_set_def_font(int unit, struct console_font *op) return 0; } -static int sticon_set_font(struct vc_data *vc, struct console_font *op) +static int sticon_set_font(struct vc_data *vc, struct console_font *op, + unsigned int vpitch) { struct sti_struct *sti = sticon_sti; int vc_cols, vc_rows, vc_old_cols, vc_old_rows; @@ -181,7 +182,7 @@ static int sticon_set_font(struct vc_data *vc, struct console_font *op) struct sti_cooked_font *cooked_font; unsigned char *data = op->data, *p; - if ((w < 6) || (h < 6) || (w > 32) || (h > 32) + if ((w < 6) || (h < 6) || (w > 32) || (h > 32) || (vpitch != 32) || (op->charcount != 256 && op->charcount != 512)) return -EINVAL; pitch = ALIGN(w, 8) / 8; @@ -267,9 +268,9 @@ static int sticon_font_default(struct vc_data *vc, struct console_font *op, char } static int sticon_font_set(struct vc_data *vc, struct console_font *font, - unsigned int flags) + unsigned int vpitch, unsigned int flags) { - return sticon_set_font(vc, font); + return sticon_set_font(vc, font, vpitch); } static void sticon_init(struct vc_data *c, int init) diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index fcdf017e2665..e25ba523892e 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1029,7 +1029,7 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight) } static int vgacon_font_set(struct vc_data *c, struct console_font *font, - unsigned int flags) + unsigned int vpitch, unsigned int flags) { unsigned charcount = font->charcount; int rc; @@ -1037,7 +1037,7 @@ static int vgacon_font_set(struct vc_data *c, struct console_font *font, if (vga_video_type < VIDEO_TYPE_EGAM) return -EINVAL; - if (font->width != VGA_FONTWIDTH || + if (font->width != VGA_FONTWIDTH || font->height > 32 || vpitch != 32 || (charcount != 256 && charcount != 512)) return -EINVAL; @@ -1050,9 +1050,9 @@ static int vgacon_font_set(struct vc_data *c, struct console_font *font, return rc; } -static int vgacon_font_get(struct vc_data *c, struct console_font *font) +static int vgacon_font_get(struct vc_data *c, struct console_font *font, unsigned int vpitch) { - if (vga_video_type < VIDEO_TYPE_EGAM) + if (vga_video_type < VIDEO_TYPE_EGAM || vpitch != 32) return -EINVAL; font->width = VGA_FONTWIDTH; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 94d4ed0331c9..0a2c47df01f4 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2266,7 +2266,7 @@ static int fbcon_debug_leave(struct vc_data *vc) return 0; } -static int fbcon_get_font(struct vc_data *vc, struct console_font *font) +static int fbcon_get_font(struct vc_data *vc, struct console_font *font, unsigned int vpitch) { u8 *fontdata = vc->vc_font.data; u8 *data = font->data; @@ -2274,6 +2274,8 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) font->width = vc->vc_font.width; font->height = vc->vc_font.height; + if (font->height > vpitch) + return -ENOSPC; font->charcount = vc->vc_hi_font_mask ? 512 : 256; if (!font->data) return 0; @@ -2285,8 +2287,8 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); - memset(data + j, 0, 32 - j); - data += 32; + memset(data + j, 0, vpitch - j); + data += vpitch; fontdata += j; } } else if (font->width <= 16) { @@ -2296,8 +2298,8 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); - memset(data + j, 0, 64 - j); - data += 64; + memset(data + j, 0, 2*vpitch - j); + data += 2*vpitch; fontdata += j; } } else if (font->width <= 24) { @@ -2311,8 +2313,8 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) *data++ = fontdata[2]; fontdata += sizeof(u32); } - memset(data, 0, 3 * (32 - j)); - data += 3 * (32 - j); + memset(data, 0, 3 * (vpitch - j)); + data += 3 * (vpitch - j); } } else { j = vc->vc_font.height * 4; @@ -2321,8 +2323,8 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) for (i = 0; i < font->charcount; i++) { memcpy(data, fontdata, j); - memset(data + j, 0, 128 - j); - data += 128; + memset(data + j, 0, 4 * vpitch - j); + data += 4 * vpitch; fontdata += j; } } @@ -2457,19 +2459,12 @@ err_out: } /* - * User asked to set font; we are guaranteed that - * a) width and height are in range 1..32 - * b) charcount does not exceed 512 - * but lets not assume that, since someone might someday want to use larger - * fonts. And charcount of 512 is small for unicode support. - * - * However, user space gives the font in 32 rows , regardless of - * actual font height. So a new API is needed if support for larger fonts - * is ever implemented. + * User asked to set font; we are guaranteed that charcount does not exceed 512 + * but lets not assume that, since charcount of 512 is small for unicode support. */ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, - unsigned int flags) + unsigned int vpitch, unsigned int flags) { struct fb_info *info = fbcon_info_from_console(vc->vc_num); unsigned charcount = font->charcount; @@ -2515,7 +2510,7 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, FNTSIZE(new_data) = size; REFCOUNT(new_data) = 0; /* usage counter */ for (i=0; i< charcount; i++) { - memcpy(new_data + i*h*pitch, data + i*32*pitch, h*pitch); + memcpy(new_data + i*h*pitch, data + i*vpitch*pitch, h*pitch); } /* Since linux has a nice crc32 function use it for counting font diff --git a/include/linux/console.h b/include/linux/console.h index 1e36958aa656..d3195664baa5 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -60,8 +60,9 @@ struct consw { int (*con_switch)(struct vc_data *vc); int (*con_blank)(struct vc_data *vc, int blank, int mode_switch); int (*con_font_set)(struct vc_data *vc, struct console_font *font, - unsigned int flags); - int (*con_font_get)(struct vc_data *vc, struct console_font *font); + unsigned int vpitch, unsigned int flags); + int (*con_font_get)(struct vc_data *vc, struct console_font *font, + unsigned int vpitch); int (*con_font_default)(struct vc_data *vc, struct console_font *font, char *name); int (*con_resize)(struct vc_data *vc, unsigned int width, diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index 1518568aaf0f..539f1cd45309 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -18,7 +18,6 @@ #include <linux/workqueue.h> struct uni_pagedict; -struct uni_screen; #define NPAR 16 #define VC_TABSTOPS_COUNT 256U @@ -159,7 +158,7 @@ struct vc_data { struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */ struct uni_pagedict *uni_pagedict; struct uni_pagedict **uni_pagedict_loc; /* [!] Location of uni_pagedict variable for this console */ - struct uni_screen *vc_uni_screen; /* unicode screen content */ + u32 **vc_uni_lines; /* unicode screen content */ /* additional information is in vt_kern.h */ }; diff --git a/include/linux/dfl.h b/include/linux/dfl.h index 431636a0dc78..0a7a00a0ee7f 100644 --- a/include/linux/dfl.h +++ b/include/linux/dfl.h @@ -27,11 +27,15 @@ enum dfl_id_type { * @id: id of the dfl device. * @type: type of DFL FIU of the device. See enum dfl_id_type. * @feature_id: feature identifier local to its DFL FIU type. + * @revision: revision of this dfl device feature. * @mmio_res: mmio resource of this dfl device. * @irqs: list of Linux IRQ numbers of this dfl device. * @num_irqs: number of IRQs supported by this dfl device. * @cdev: pointer to DFL FPGA container device this dfl device belongs to. * @id_entry: matched id entry in dfl driver's id table. + * @dfh_version: version of DFH for the device + * @param_size: size of the block parameters in bytes + * @params: pointer to block of parameters copied memory */ struct dfl_device { struct device dev; @@ -44,6 +48,9 @@ struct dfl_device { unsigned int num_irqs; struct dfl_fpga_cdev *cdev; const struct dfl_device_id *id_entry; + u8 dfh_version; + unsigned int param_size; + void *params; }; /** @@ -84,4 +91,5 @@ void dfl_driver_unregister(struct dfl_driver *dfl_drv); module_driver(__dfl_driver, dfl_driver_register, \ dfl_driver_unregister) +void *dfh_find_param(struct dfl_device *dfl_dev, int param_id, size_t *pcount); #endif /* __LINUX_DFL_H */ diff --git a/include/linux/serial.h b/include/linux/serial.h index 3d6fe3ef92cf..bfda927dde15 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -12,6 +12,11 @@ #include <uapi/linux/serial.h> #include <uapi/linux/serial_reg.h> +#define UART_IER_ALL_INTR (UART_IER_MSI | \ + UART_IER_RLSI | \ + UART_IER_THRI | \ + UART_IER_RDI) + /* Helper for dealing with UART_LCR_WLEN* defines */ #define UART_LCR_WLEN(x) ((x) - 5) @@ -23,6 +28,11 @@ static inline bool uart_lsr_tx_empty(u16 lsr) return (lsr & UART_LSR_BOTH_EMPTY) == UART_LSR_BOTH_EMPTY; } +#define UART_MSR_STATUS_BITS (UART_MSR_DCD | \ + UART_MSR_RI | \ + UART_MSR_DSR | \ + UART_MSR_CTS) + /* * Counters of the input lines (CTS, DSR, RI, CD) interrupts */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index fd59f600094a..9e3e5e0d11b2 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -579,6 +579,7 @@ struct uart_port { struct serial_rs485 rs485; struct serial_rs485 rs485_supported; /* Supported mask for serial_rs485 */ struct gpio_desc *rs485_term_gpio; /* enable RS485 bus termination */ + struct gpio_desc *rs485_rx_during_tx_gpio; /* Output GPIO that sets the state of RS485 RX during TX */ struct serial_iso7816 iso7816; void *private_data; /* generic platform data pointer */ }; @@ -781,7 +782,7 @@ static inline int uart_poll_timeout(struct uart_port *port) struct earlycon_device { struct console *con; struct uart_port port; - char options[16]; /* e.g., 115200n8 */ + char options[32]; /* e.g., 115200n8 */ unsigned int baud; }; @@ -896,10 +897,8 @@ static inline bool uart_softcts_mode(struct uart_port *uport) * The following are helper functions for the low level drivers. */ -extern void uart_handle_dcd_change(struct uart_port *uport, - unsigned int status); -extern void uart_handle_cts_change(struct uart_port *uport, - unsigned int status); +extern void uart_handle_dcd_change(struct uart_port *uport, bool active); +extern void uart_handle_cts_change(struct uart_port *uport, bool active); extern void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag); diff --git a/include/linux/qcom-geni-se.h b/include/linux/soc/qcom/geni-se.h index f5672785c0c4..400213daa461 100644 --- a/include/linux/qcom-geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -103,6 +103,7 @@ struct geni_se { #define SE_DMA_TX_FSM_RST 0xc58 #define SE_DMA_RX_IRQ_STAT 0xd40 #define SE_DMA_RX_IRQ_CLR 0xd44 +#define SE_DMA_RX_LEN_IN 0xd54 #define SE_DMA_RX_FSM_RST 0xd58 #define SE_HW_PARAM_0 0xe24 #define SE_HW_PARAM_1 0xe28 @@ -235,6 +236,8 @@ struct geni_se { #define RX_SBE BIT(2) #define RX_RESET_DONE BIT(3) #define RX_FLUSH_DONE BIT(4) +#define RX_DMA_PARITY_ERR BIT(5) +#define RX_DMA_BREAK GENMASK(8, 7) #define RX_GENI_GP_IRQ GENMASK(10, 5) #define RX_GENI_CANCEL_IRQ BIT(11) #define RX_GENI_GP_IRQ_EXT GENMASK(13, 12) diff --git a/include/linux/tty.h b/include/linux/tty.h index 730c3301d710..093935e97f42 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -453,7 +453,7 @@ unsigned char tty_get_char_size(unsigned int cflag); unsigned char tty_get_frame_size(unsigned int cflag); void tty_termios_copy_hw(struct ktermios *new, const struct ktermios *old); -int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b); +bool tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b); int tty_set_termios(struct tty_struct *tty, struct ktermios *kt); void tty_wakeup(struct tty_struct *tty); diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index dcb61ec11424..49dc172dedc7 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -170,7 +170,7 @@ int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, * send, please arise a tasklet or workqueue to do the real data transfer. * Do not send data in this hook, it may lead to a deadlock. * - * @dcd_change: [DRV] ``void ()(struct tty_struct *tty, unsigned int status)`` + * @dcd_change: [DRV] ``void ()(struct tty_struct *tty, bool active)`` * * Tells the discipline that the DCD pin has changed its status. Used * exclusively by the %N_PPS (Pulse-Per-Second) line discipline. @@ -238,7 +238,7 @@ struct tty_ldisc_ops { void (*receive_buf)(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count); void (*write_wakeup)(struct tty_struct *tty); - void (*dcd_change)(struct tty_struct *tty, unsigned int status); + void (*dcd_change)(struct tty_struct *tty, bool active); int (*receive_buf2)(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count); void (*lookahead_buf)(struct tty_struct *tty, const unsigned char *cp, diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index fa3c3bdaa234..edf685a24f7c 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -15,8 +15,8 @@ struct tty_struct; /** * struct tty_port_operations -- operations on tty_port - * @carrier_raised: return 1 if the carrier is raised on @port - * @dtr_rts: raise the DTR line if @raise is nonzero, otherwise lower DTR + * @carrier_raised: return true if the carrier is raised on @port + * @dtr_rts: raise the DTR line if @active is true, otherwise lower DTR * @shutdown: called when the last close completes or a hangup finishes IFF the * port was initialized. Do not use to free resources. Turn off the device * only. Called under the port mutex to serialize against @activate and @@ -31,8 +31,8 @@ struct tty_struct; * the port itself. */ struct tty_port_operations { - int (*carrier_raised)(struct tty_port *port); - void (*dtr_rts)(struct tty_port *port, int raise); + bool (*carrier_raised)(struct tty_port *port); + void (*dtr_rts)(struct tty_port *port, bool active); void (*shutdown)(struct tty_port *port); int (*activate)(struct tty_port *port, struct tty_struct *tty); void (*destruct)(struct tty_port *port); @@ -230,7 +230,7 @@ static inline void tty_port_set_kopened(struct tty_port *port, bool val) struct tty_struct *tty_port_tty_get(struct tty_port *port); void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); -int tty_port_carrier_raised(struct tty_port *port); +bool tty_port_carrier_raised(struct tty_port *port); void tty_port_raise_dtr_rts(struct tty_port *port); void tty_port_lower_dtr_rts(struct tty_port *port); void tty_port_hangup(struct tty_port *port); diff --git a/include/uapi/linux/gsmmux.h b/include/uapi/linux/gsmmux.h index cb8693b39cb7..a703780aa095 100644 --- a/include/uapi/linux/gsmmux.h +++ b/include/uapi/linux/gsmmux.h @@ -19,8 +19,7 @@ struct gsm_config unsigned int mtu; unsigned int k; unsigned int i; - unsigned int unused[8]; /* Padding for expansion without - breaking stuff */ + unsigned int unused[8]; /* Can not be used */ }; #define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config) @@ -29,9 +28,9 @@ struct gsm_config struct gsm_netconfig { unsigned int adaption; /* Adaption to use in network mode */ unsigned short protocol;/* Protocol to use - only ETH_P_IP supported */ - unsigned short unused2; + unsigned short unused2; /* Can not be used */ char if_name[IFNAMSIZ]; /* interface name format string */ - __u8 unused[28]; /* For future use */ + __u8 unused[28]; /* Can not be used */ }; #define GSMIOC_ENABLE_NET _IOW('G', 2, struct gsm_netconfig) @@ -40,4 +39,14 @@ struct gsm_netconfig { /* get the base tty number for a configured gsmmux tty */ #define GSMIOC_GETFIRST _IOR('G', 4, __u32) +struct gsm_config_ext { + __u32 keep_alive; /* Control channel keep-alive in 1/100th of a + * second (0 to disable) + */ + __u32 reserved[7]; /* For future use, must be initialized to zero */ +}; + +#define GSMIOC_GETCONF_EXT _IOR('G', 5, struct gsm_config_ext) +#define GSMIOC_SETCONF_EXT _IOW('G', 6, struct gsm_config_ext) + #endif diff --git a/include/uapi/linux/kd.h b/include/uapi/linux/kd.h index ee929ece4112..6b384065c013 100644 --- a/include/uapi/linux/kd.h +++ b/include/uapi/linux/kd.h @@ -161,19 +161,25 @@ struct console_font_op { unsigned int flags; /* KD_FONT_FLAG_* */ unsigned int width, height; /* font size */ unsigned int charcount; - unsigned char __user *data; /* font data with height fixed to 32 */ + unsigned char __user *data; /* font data with vpitch fixed to 32 for + * KD_FONT_OP_SET/GET + */ }; struct console_font { unsigned int width, height; /* font size */ unsigned int charcount; - unsigned char *data; /* font data with height fixed to 32 */ + unsigned char *data; /* font data with vpitch fixed to 32 for + * KD_FONT_OP_SET/GET + */ }; #define KD_FONT_OP_SET 0 /* Set font */ #define KD_FONT_OP_GET 1 /* Get font */ #define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */ #define KD_FONT_OP_COPY 3 /* Obsolete, do not use */ +#define KD_FONT_OP_SET_TALL 4 /* Set font with vpitch = height */ +#define KD_FONT_OP_GET_TALL 5 /* Get font with vpitch = height */ #define KD_FONT_FLAG_DONT_RECALC 1 /* Don't recalculate hw charcell size [compat] */ diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 3ba34d8378bd..281fa286555c 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -207,6 +207,9 @@ /* Atheros AR933X SoC */ #define PORT_AR933X 99 +/* MCHP 16550A UART with 256 byte FIFOs */ +#define PORT_MCHP16550A 100 + /* ARC (Synopsys) on-chip UART */ #define PORT_ARC 101 diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index bab3b39266cc..08b3527e1b93 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -44,6 +44,11 @@ #define UART_IIR_RX_TIMEOUT 0x0c /* OMAP RX Timeout interrupt */ #define UART_IIR_XOFF 0x10 /* OMAP XOFF/Special Character */ #define UART_IIR_CTS_RTS_DSR 0x20 /* OMAP CTS/RTS/DSR Change */ +#define UART_IIR_64BYTE_FIFO 0x20 /* 16750 64 bytes FIFO */ +#define UART_IIR_FIFO_ENABLED 0xc0 /* FIFOs enabled / port type identification */ +#define UART_IIR_FIFO_ENABLED_8250 0x00 /* 8250: no FIFO */ +#define UART_IIR_FIFO_ENABLED_16550 0x80 /* 16550: (broken/unusable) FIFO */ +#define UART_IIR_FIFO_ENABLED_16550A 0xc0 /* 16550A: FIFO enabled */ #define UART_FCR 2 /* Out: FIFO Control Register */ #define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 8009e0e93216..5697df9d4394 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -119,7 +119,7 @@ static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty) } /* we block the open until the dlc->state becomes BT_CONNECTED */ -static int rfcomm_dev_carrier_raised(struct tty_port *port) +static bool rfcomm_dev_carrier_raised(struct tty_port *port) { struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port); |