diff --git a/Documentation/devicetree/bindings/serial/8250.yaml b/Documentation/devicetree/bindings/serial/8250.yaml index bb7b9c87a807..8c2c177faf89 100644 --- a/Documentation/devicetree/bindings/serial/8250.yaml +++ b/Documentation/devicetree/bindings/serial/8250.yaml @@ -23,7 +23,8 @@ allOf: then: properties: compatible: - const: aspeed,ast2500-vuart + contains: + const: aspeed,ast2500-vuart - if: properties: compatible: @@ -188,6 +189,11 @@ properties: - loongson,ls2k2000-uart - const: loongson,ls2k1500-uart - const: ns16550a + - items: + - enum: + - aspeed,ast2600-vuart + - aspeed,ast2700-vuart + - const: aspeed,ast2500-vuart reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/serial/maxim,max310x.yaml b/Documentation/devicetree/bindings/serial/maxim,max310x.yaml index 889eeaca64a0..e598dda4d13f 100644 --- a/Documentation/devicetree/bindings/serial/maxim,max310x.yaml +++ b/Documentation/devicetree/bindings/serial/maxim,max310x.yaml @@ -9,6 +9,13 @@ title: Maxim MAX310X Advanced Universal Asynchronous Receiver-Transmitter (UART) maintainers: - Hugo Villeneuve +description: + The MAX310X is a family of SPI/I2C UARTs with one (max3107, max3108), + two (max3109) or four (max14830) channels. Single-channel parts are + described as a serial node with RS-485 properties on the chip node; + multi-channel parts use one "serial@N" child node per channel, each + carrying its own serial/RS-485 properties. + properties: compatible: enum: @@ -49,8 +56,55 @@ required: allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# - - $ref: /schemas/serial/serial.yaml# - - $ref: /schemas/serial/rs485.yaml# + + - if: + properties: + compatible: + contains: + enum: + - maxim,max3107 + - maxim,max3108 + then: + allOf: + - $ref: /schemas/serial/serial.yaml# + - $ref: /schemas/serial/rs485.yaml# + + - if: + properties: + compatible: + contains: + enum: + - maxim,max3109 + - maxim,max14830 + then: + properties: + "#address-cells": + const: 1 + "#size-cells": + const: 0 + patternProperties: + "^serial@[0-3]$": + type: object + description: A single UART channel of the chip. + allOf: + - $ref: /schemas/serial/serial.yaml# + - $ref: /schemas/serial/rs485.yaml# + properties: + reg: + description: UART channel number on the chip. + maximum: 3 + required: + - reg + unevaluatedProperties: false + + - if: + properties: + compatible: + contains: + const: maxim,max3109 + then: + patternProperties: + "^serial@[23]$": false unevaluatedProperties: false @@ -70,5 +124,39 @@ examples: interrupts = <7 IRQ_TYPE_LEVEL_LOW>; gpio-controller; #gpio-cells = <2>; + rs485-rts-active-low; + linux,rs485-enabled-at-boot-time; + }; + }; + + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + serial@0 { + compatible = "maxim,max14830"; + reg = <0>; + spi-max-frequency = <26000000>; + clocks = <&xtal4m>; + clock-names = "xtal"; + interrupt-parent = <&gpio3>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + + serial@0 { + reg = <0>; + rs485-rts-active-low; + linux,rs485-enabled-at-boot-time; + }; + + serial@2 { + reg = <2>; + rs485-rts-active-low; + }; }; }; diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index 49f51b002879..664305de4986 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -54,6 +54,7 @@ properties: - anlogic,dr1v90-uart - brcm,bcm11351-dw-apb-uart - brcm,bcm21664-dw-apb-uart + - google,lga-uart - rockchip,px30-uart - rockchip,rk1808-uart - rockchip,rk3036-uart @@ -72,6 +73,7 @@ properties: - rockchip,rk3576-uart - rockchip,rk3588-uart - rockchip,rv1103b-uart + - rockchip,rv1106-uart - rockchip,rv1108-uart - rockchip,rv1126-uart - sophgo,sg2044-uart diff --git a/MAINTAINERS b/MAINTAINERS index 3bec7701e733..01969d5b5227 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13785,12 +13785,6 @@ F: include/net/ip_vs.h F: include/uapi/linux/ip_vs.h F: net/netfilter/ipvs/ -IPWIRELESS DRIVER -M: Jiri Kosina -M: David Sterba -S: Odd Fixes -F: drivers/tty/ipwireless/ - IRON DEVICE AUDIO CODEC DRIVERS M: Kiseok Jo L: linux-sound@vger.kernel.org diff --git a/arch/arm64/boot/dts/mediatek/mt6795-sony-xperia-m5.dts b/arch/arm64/boot/dts/mediatek/mt6795-sony-xperia-m5.dts index 0e086dd487d9..63659e240b1d 100644 --- a/arch/arm64/boot/dts/mediatek/mt6795-sony-xperia-m5.dts +++ b/arch/arm64/boot/dts/mediatek/mt6795-sony-xperia-m5.dts @@ -20,6 +20,7 @@ aliases { mmc2 = &mmc2; serial0 = &uart0; serial1 = &uart1; + serial2 = &uart2; }; backlight_lcd0: backlight { diff --git a/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts b/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts index 8c3e2e2578bc..7bccad118d24 100644 --- a/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts +++ b/arch/arm64/boot/dts/mediatek/mt7622-rfb1.dts @@ -20,6 +20,7 @@ / { aliases { serial0 = &uart0; + serial2 = &uart2; }; chosen { diff --git a/arch/arm64/boot/dts/mediatek/mt7986a-bananapi-bpi-r3.dts b/arch/arm64/boot/dts/mediatek/mt7986a-bananapi-bpi-r3.dts index 19f538d160ab..637e5567c1e0 100644 --- a/arch/arm64/boot/dts/mediatek/mt7986a-bananapi-bpi-r3.dts +++ b/arch/arm64/boot/dts/mediatek/mt7986a-bananapi-bpi-r3.dts @@ -21,6 +21,8 @@ / { aliases { serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; ethernet0 = &gmac0; ethernet1 = &gmac1; }; diff --git a/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts b/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts index 5d8e3d3f6c20..9dfd882ca484 100644 --- a/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts +++ b/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts @@ -16,6 +16,8 @@ / { aliases { serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; }; chosen { diff --git a/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi b/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi index 5ea499f349f6..69cab9f9c530 100644 --- a/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8183-kukui.dtsi @@ -13,6 +13,7 @@ / { aliases { serial0 = &uart0; + serial1 = &uart1; mmc0 = &mmc0; mmc1 = &mmc1; }; diff --git a/arch/arm64/boot/dts/mediatek/mt8192-asurada-hayato-r1.dts b/arch/arm64/boot/dts/mediatek/mt8192-asurada-hayato-r1.dts index ac2673e56fb8..233711cb4556 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192-asurada-hayato-r1.dts +++ b/arch/arm64/boot/dts/mediatek/mt8192-asurada-hayato-r1.dts @@ -9,6 +9,10 @@ / { model = "Google Hayato rev1"; chassis-type = "convertible"; compatible = "google,hayato-rev1", "google,hayato", "mediatek,mt8192"; + + aliases { + serial1 = &uart1; + }; }; &keyboard_controller { diff --git a/arch/arm64/boot/dts/mediatek/mt8195-demo.dts b/arch/arm64/boot/dts/mediatek/mt8195-demo.dts index 1f59b5786b81..13132a35080f 100644 --- a/arch/arm64/boot/dts/mediatek/mt8195-demo.dts +++ b/arch/arm64/boot/dts/mediatek/mt8195-demo.dts @@ -19,6 +19,7 @@ / { aliases { serial0 = &uart0; + serial1 = &uart1; }; chosen { diff --git a/arch/arm64/boot/dts/mediatek/mt8365-evk.dts b/arch/arm64/boot/dts/mediatek/mt8365-evk.dts index a30ee523b0b5..c535de34dfac 100644 --- a/arch/arm64/boot/dts/mediatek/mt8365-evk.dts +++ b/arch/arm64/boot/dts/mediatek/mt8365-evk.dts @@ -24,6 +24,8 @@ aliases { mmc0 = &mmc0; mmc1 = &mmc1; serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; }; chosen { diff --git a/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi b/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi index 0dfd0c7de41d..669eaec662f3 100644 --- a/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi @@ -33,6 +33,8 @@ aliases { mmc0 = &mmc0; mmc1 = &mmc1; serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; }; backlight_lcm1: backlight-lcm1 { diff --git a/arch/arm64/boot/dts/mediatek/mt8390-grinn-genio-sbc.dtsi b/arch/arm64/boot/dts/mediatek/mt8390-grinn-genio-sbc.dtsi index 888248a75e93..51f12328a1d6 100644 --- a/arch/arm64/boot/dts/mediatek/mt8390-grinn-genio-sbc.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8390-grinn-genio-sbc.dtsi @@ -17,6 +17,8 @@ aliases { i2c5 = &i2c5; i2c6 = &i2c6; serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; }; chosen { diff --git a/arch/arm64/boot/dts/mediatek/mt8390-tungsten-smarc.dtsi b/arch/arm64/boot/dts/mediatek/mt8390-tungsten-smarc.dtsi index 8256279c5629..f2ad9265be31 100644 --- a/arch/arm64/boot/dts/mediatek/mt8390-tungsten-smarc.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8390-tungsten-smarc.dtsi @@ -31,6 +31,8 @@ aliases { rtc0 = &rv3028; rtc1 = &mt6359rtc; serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; }; backlight_lcd0: backlight-lcd0 { diff --git a/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi b/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi index 7ea45b6bc663..aef908df530e 100644 --- a/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi @@ -22,6 +22,7 @@ aliases { mmc0 = &mmc0; mmc1 = &mmc1; serial0 = &uart0; + serial1 = &uart1; }; chosen { diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 33ef33ffc756..acffc5c17f92 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -571,7 +571,6 @@ CONFIG_PPDEV=m CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_VIRTIO=m CONFIG_NVRAM=y -CONFIG_IPWIRELESS=m CONFIG_I2C_CHARDEV=m CONFIG_I2C_HYDRA=m CONFIG_I2C_MPC=m diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index df6832a4c237..5d6b0dd4d423 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -291,15 +291,6 @@ config GOLDFISH_TTY_EARLY_CONSOLE default y if GOLDFISH_TTY=y select SERIAL_EARLYCON -config IPWIRELESS - tristate "IPWireless 3G UMTS PCMCIA card support" - depends on PCMCIA && NETDEVICES && HAS_IOPORT - select PPP - help - This is a driver for 3G UMTS PCMCIA card from IPWireless company. In - some countries (for example Czech Republic, T-Mobile ISP) this card - is shipped for service called UMTS 4G. - config N_GSM tristate "GSM MUX line discipline support (EXPERIMENTAL)" depends on NET diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 8ca1a0a2229f..c6a063a541fd 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -26,5 +26,3 @@ obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o obj-$(CONFIG_VCC) += vcc.o obj-$(CONFIG_RPMSG_TTY) += rpmsg_tty.o - -obj-y += ipwireless/ diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 81eaca751541..28af0fd98181 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -443,23 +443,23 @@ static int rs_startup(struct tty_struct *tty, struct serial_state *info) struct tty_port *port = &info->tport; unsigned long flags; int retval=0; - unsigned long page; + void *buffer; - page = get_zeroed_page(GFP_KERNEL); - if (!page) + buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buffer) return -ENOMEM; local_irq_save(flags); if (tty_port_initialized(port)) { - free_page(page); + kfree(buffer); goto errout; } if (info->xmit.buf) - free_page(page); + kfree(buffer); else - info->xmit.buf = (unsigned char *) page; + info->xmit.buf = buffer; #ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d ...", info->line); @@ -537,7 +537,7 @@ static void rs_shutdown(struct tty_struct *tty, struct serial_state *info) */ free_irq(IRQ_AMIGA_VERTB, info); - free_page((unsigned long)info->xmit.buf); + kfree(info->xmit.buf); info->xmit.buf = NULL; info->IER = 0; diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 16edd71a0d8d..fa723a4ba7b3 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -17,6 +17,7 @@ #include #include #include +#include /* Goldfish tty register's offsets */ #define GOLDFISH_TTY_REG_BYTES_READY 0x04 @@ -37,7 +38,6 @@ struct goldfish_tty { spinlock_t lock; void __iomem *base; u32 irq; - int opencount; struct console console; u32 version; struct device *dev; @@ -49,15 +49,23 @@ static u32 goldfish_tty_line_count = 8; static u32 goldfish_tty_current_line_count; static struct goldfish_tty *goldfish_ttys; +static inline void gf_write_addr(unsigned long addr, void __iomem *portl, void __iomem *porth) +{ + gf_iowrite32(lower_32_bits(addr), portl); +#ifdef CONFIG_64BIT + gf_iowrite32(upper_32_bits(addr), porth); +#endif +} + static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, size_t count, bool is_write) { - unsigned long irq_flags; void __iomem *base = qtty->base; - spin_lock_irqsave(&qtty->lock, irq_flags); - gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR, - base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); + guard(spinlock_irqsave)(&qtty->lock); + + gf_write_addr(address, base + GOLDFISH_TTY_REG_DATA_PTR, + base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); gf_iowrite32(count, base + GOLDFISH_TTY_REG_DATA_LEN); if (is_write) @@ -66,8 +74,6 @@ static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, else gf_iowrite32(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_REG_CMD); - - spin_unlock_irqrestore(&qtty->lock, irq_flags); } static void goldfish_tty_rw(struct goldfish_tty *qtty, unsigned long addr, @@ -409,7 +415,7 @@ static void goldfish_tty_remove(struct platform_device *pdev) { struct goldfish_tty *qtty = platform_get_drvdata(pdev); - mutex_lock(&goldfish_tty_lock); + guard(mutex)(&goldfish_tty_lock); unregister_console(&qtty->console); tty_unregister_device(goldfish_tty_driver, qtty->console.index); @@ -420,7 +426,6 @@ static void goldfish_tty_remove(struct platform_device *pdev) goldfish_tty_current_line_count--; if (goldfish_tty_current_line_count == 0) goldfish_tty_delete_driver(); - mutex_unlock(&goldfish_tty_lock); } #ifdef CONFIG_GOLDFISH_TTY_EARLY_CONSOLE diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index c2a4e88b328f..5866195de26a 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -79,7 +79,7 @@ config HVC_UDBG config HVC_DCC bool "ARM JTAG DCC console" - depends on ARM || ARM64 + depends on (ARM && (CPU_V6 || CPU_V6K || CPU_V7)) || ARM64 select HVC_DRIVER select SERIAL_CORE_CONSOLE help diff --git a/drivers/tty/ipwireless/Makefile b/drivers/tty/ipwireless/Makefile deleted file mode 100644 index a665d021e24d..000000000000 --- a/drivers/tty/ipwireless/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the IPWireless driver -# - -obj-$(CONFIG_IPWIRELESS) += ipwireless.o - -ipwireless-y := hardware.o main.o network.o tty.o - diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c deleted file mode 100644 index 0bfcbca6e2df..000000000000 --- a/drivers/tty/ipwireless/hardware.c +++ /dev/null @@ -1,1770 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#include -#include -#include -#include -#include -#include - -#include "hardware.h" -#include "setup_protocol.h" -#include "network.h" -#include "main.h" - -static void ipw_send_setup_packet(struct ipw_hardware *hw); -static void handle_received_SETUP_packet(struct ipw_hardware *ipw, - unsigned int address, - const unsigned char *data, int len, - int is_last); -static void ipwireless_setup_timer(struct timer_list *t); -static void handle_received_CTRL_packet(struct ipw_hardware *hw, - unsigned int channel_idx, const unsigned char *data, int len); - -/*#define TIMING_DIAGNOSTICS*/ - -#ifdef TIMING_DIAGNOSTICS - -static struct timing_stats { - unsigned long last_report_time; - unsigned long read_time; - unsigned long write_time; - unsigned long read_bytes; - unsigned long write_bytes; - unsigned long start_time; -}; - -static void start_timing(void) -{ - timing_stats.start_time = jiffies; -} - -static void end_read_timing(unsigned length) -{ - timing_stats.read_time += (jiffies - start_time); - timing_stats.read_bytes += length + 2; - report_timing(); -} - -static void end_write_timing(unsigned length) -{ - timing_stats.write_time += (jiffies - start_time); - timing_stats.write_bytes += length + 2; - report_timing(); -} - -static void report_timing(void) -{ - unsigned long since = jiffies - timing_stats.last_report_time; - - /* If it's been more than one second... */ - if (since >= HZ) { - int first = (timing_stats.last_report_time == 0); - - timing_stats.last_report_time = jiffies; - if (!first) - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": %u us elapsed - read %lu bytes in %u us, wrote %lu bytes in %u us\n", - jiffies_to_usecs(since), - timing_stats.read_bytes, - jiffies_to_usecs(timing_stats.read_time), - timing_stats.write_bytes, - jiffies_to_usecs(timing_stats.write_time)); - - timing_stats.read_time = 0; - timing_stats.write_time = 0; - timing_stats.read_bytes = 0; - timing_stats.write_bytes = 0; - } -} -#else -static void start_timing(void) { } -static void end_read_timing(unsigned length) { } -static void end_write_timing(unsigned length) { } -#endif - -/* Imported IPW definitions */ - -#define LL_MTU_V1 318 -#define LL_MTU_V2 250 -#define LL_MTU_MAX (LL_MTU_V1 > LL_MTU_V2 ? LL_MTU_V1 : LL_MTU_V2) - -#define PRIO_DATA 2 -#define PRIO_CTRL 1 -#define PRIO_SETUP 0 - -/* Addresses */ -#define ADDR_SETUP_PROT 0 - -/* Protocol ids */ -enum { - /* Identifier for the Com Data protocol */ - TL_PROTOCOLID_COM_DATA = 0, - - /* Identifier for the Com Control protocol */ - TL_PROTOCOLID_COM_CTRL = 1, - - /* Identifier for the Setup protocol */ - TL_PROTOCOLID_SETUP = 2 -}; - -/* Number of bytes in NL packet header (cannot do - * sizeof(nl_packet_header) since it's a bitfield) */ -#define NL_FIRST_PACKET_HEADER_SIZE 3 - -/* Number of bytes in NL packet header (cannot do - * sizeof(nl_packet_header) since it's a bitfield) */ -#define NL_FOLLOWING_PACKET_HEADER_SIZE 1 - -struct nl_first_packet_header { - unsigned char protocol:3; - unsigned char address:3; - unsigned char packet_rank:2; - unsigned char length_lsb; - unsigned char length_msb; -}; - -struct nl_packet_header { - unsigned char protocol:3; - unsigned char address:3; - unsigned char packet_rank:2; -}; - -/* Value of 'packet_rank' above */ -#define NL_INTERMEDIATE_PACKET 0x0 -#define NL_LAST_PACKET 0x1 -#define NL_FIRST_PACKET 0x2 - -union nl_packet { - /* Network packet header of the first packet (a special case) */ - struct nl_first_packet_header hdr_first; - /* Network packet header of the following packets (if any) */ - struct nl_packet_header hdr; - /* Complete network packet (header + data) */ - unsigned char rawpkt[LL_MTU_MAX]; -} __attribute__ ((__packed__)); - -#define HW_VERSION_UNKNOWN -1 -#define HW_VERSION_1 1 -#define HW_VERSION_2 2 - -/* IPW I/O ports */ -#define IOIER 0x00 /* Interrupt Enable Register */ -#define IOIR 0x02 /* Interrupt Source/ACK register */ -#define IODCR 0x04 /* Data Control Register */ -#define IODRR 0x06 /* Data Read Register */ -#define IODWR 0x08 /* Data Write Register */ -#define IOESR 0x0A /* Embedded Driver Status Register */ -#define IORXR 0x0C /* Rx Fifo Register (Host to Embedded) */ -#define IOTXR 0x0E /* Tx Fifo Register (Embedded to Host) */ - -/* I/O ports and bit definitions for version 1 of the hardware */ - -/* IER bits*/ -#define IER_RXENABLED 0x1 -#define IER_TXENABLED 0x2 - -/* ISR bits */ -#define IR_RXINTR 0x1 -#define IR_TXINTR 0x2 - -/* DCR bits */ -#define DCR_RXDONE 0x1 -#define DCR_TXDONE 0x2 -#define DCR_RXRESET 0x4 -#define DCR_TXRESET 0x8 - -/* I/O ports and bit definitions for version 2 of the hardware */ - -struct MEMCCR { - unsigned short reg_config_option; /* PCCOR: Configuration Option Register */ - unsigned short reg_config_and_status; /* PCCSR: Configuration and Status Register */ - unsigned short reg_pin_replacement; /* PCPRR: Pin Replacemant Register */ - unsigned short reg_socket_and_copy; /* PCSCR: Socket and Copy Register */ - unsigned short reg_ext_status; /* PCESR: Extendend Status Register */ - unsigned short reg_io_base; /* PCIOB: I/O Base Register */ -}; - -struct MEMINFREG { - unsigned short memreg_tx_old; /* TX Register (R/W) */ - unsigned short pad1; - unsigned short memreg_rx_done; /* RXDone Register (R/W) */ - unsigned short pad2; - unsigned short memreg_rx; /* RX Register (R/W) */ - unsigned short pad3; - unsigned short memreg_pc_interrupt_ack; /* PC intr Ack Register (W) */ - unsigned short pad4; - unsigned long memreg_card_present;/* Mask for Host to check (R) for - * CARD_PRESENT_VALUE */ - unsigned short memreg_tx_new; /* TX2 (new) Register (R/W) */ -}; - -#define CARD_PRESENT_VALUE (0xBEEFCAFEUL) - -#define MEMTX_TX 0x0001 -#define MEMRX_RX 0x0001 -#define MEMRX_RX_DONE 0x0001 -#define MEMRX_PCINTACKK 0x0001 - -#define NL_NUM_OF_PRIORITIES 3 -#define NL_NUM_OF_PROTOCOLS 3 -#define NL_NUM_OF_ADDRESSES NO_OF_IPW_CHANNELS - -struct ipw_hardware { - unsigned int base_port; - short hw_version; - unsigned short ll_mtu; - spinlock_t lock; - - int initializing; - int init_loops; - struct timer_list setup_timer; - - /* Flag if hw is ready to send next packet */ - int tx_ready; - /* Count of pending packets to be sent */ - int tx_queued; - struct list_head tx_queue[NL_NUM_OF_PRIORITIES]; - - int rx_bytes_queued; - struct list_head rx_queue; - /* Pool of rx_packet structures that are not currently used. */ - struct list_head rx_pool; - int rx_pool_size; - /* True if reception of data is blocked while userspace processes it. */ - int blocking_rx; - /* True if there is RX data ready on the hardware. */ - int rx_ready; - unsigned short last_memtx_serial; - /* - * Newer versions of the V2 card firmware send serial numbers in the - * MemTX register. 'serial_number_detected' is set true when we detect - * a non-zero serial number (indicating the new firmware). Thereafter, - * the driver can safely ignore the Timer Recovery re-sends to avoid - * out-of-sync problems. - */ - int serial_number_detected; - struct work_struct work_rx; - - /* True if we are to send the set-up data to the hardware. */ - int to_setup; - - /* Card has been removed */ - int removed; - /* Saved irq value when we disable the interrupt. */ - int irq; - /* True if this driver is shutting down. */ - int shutting_down; - /* Modem control lines */ - unsigned int control_lines[NL_NUM_OF_ADDRESSES]; - struct ipw_rx_packet *packet_assembler[NL_NUM_OF_ADDRESSES]; - - struct tasklet_struct tasklet; - - /* The handle for the network layer, for the sending of events to it. */ - struct ipw_network *network; - struct MEMINFREG __iomem *memory_info_regs; - struct MEMCCR __iomem *memregs_CCR; - void (*reboot_callback) (void *data); - void *reboot_callback_data; - - unsigned short __iomem *memreg_tx; -}; - -/* - * Packet info structure for tx packets. - * Note: not all the fields defined here are required for all protocols - */ -struct ipw_tx_packet { - struct list_head queue; - /* channel idx + 1 */ - unsigned char dest_addr; - /* SETUP, CTRL or DATA */ - unsigned char protocol; - /* Length of data block, which starts at the end of this structure */ - unsigned short length; - /* Sending state */ - /* Offset of where we've sent up to so far */ - unsigned long offset; - /* Count of packet fragments, starting at 0 */ - int fragment_count; - - /* Called after packet is sent and before is freed */ - void (*packet_callback) (void *cb_data, unsigned int packet_length); - void *callback_data; -}; - -/* Signals from DTE */ -#define COMCTRL_RTS 0 -#define COMCTRL_DTR 1 - -/* Signals from DCE */ -#define COMCTRL_CTS 2 -#define COMCTRL_DCD 3 -#define COMCTRL_DSR 4 -#define COMCTRL_RI 5 - -struct ipw_control_packet_body { - /* DTE signal or DCE signal */ - unsigned char sig_no; - /* 0: set signal, 1: clear signal */ - unsigned char value; -} __attribute__ ((__packed__)); - -struct ipw_control_packet { - struct ipw_tx_packet header; - struct ipw_control_packet_body body; -}; - -struct ipw_rx_packet { - struct list_head queue; - unsigned int capacity; - unsigned int length; - unsigned int protocol; - unsigned int channel_idx; -}; - -static char *data_type(const unsigned char *buf, unsigned length) -{ - struct nl_packet_header *hdr = (struct nl_packet_header *) buf; - - if (length == 0) - return " "; - - if (hdr->packet_rank & NL_FIRST_PACKET) { - switch (hdr->protocol) { - case TL_PROTOCOLID_COM_DATA: return "DATA "; - case TL_PROTOCOLID_COM_CTRL: return "CTRL "; - case TL_PROTOCOLID_SETUP: return "SETUP"; - default: return "???? "; - } - } else - return " "; -} - -#define DUMP_MAX_BYTES 64 - -static void dump_data_bytes(const char *type, const unsigned char *data, - unsigned length) -{ - char prefix[56]; - - sprintf(prefix, IPWIRELESS_PCCARD_NAME ": %s %s ", - type, data_type(data, length)); - print_hex_dump_bytes(prefix, 0, (void *)data, - length < DUMP_MAX_BYTES ? length : DUMP_MAX_BYTES); -} - -static void swap_packet_bitfield_to_le(unsigned char *data) -{ -#ifdef __BIG_ENDIAN_BITFIELD - unsigned char tmp = *data, ret = 0; - - /* - * transform bits from aa.bbb.ccc to ccc.bbb.aa - */ - ret |= (tmp & 0xc0) >> 6; - ret |= (tmp & 0x38) >> 1; - ret |= (tmp & 0x07) << 5; - *data = ret & 0xff; -#endif -} - -static void swap_packet_bitfield_from_le(unsigned char *data) -{ -#ifdef __BIG_ENDIAN_BITFIELD - unsigned char tmp = *data, ret = 0; - - /* - * transform bits from ccc.bbb.aa to aa.bbb.ccc - */ - ret |= (tmp & 0xe0) >> 5; - ret |= (tmp & 0x1c) << 1; - ret |= (tmp & 0x03) << 6; - *data = ret & 0xff; -#endif -} - -static void do_send_fragment(struct ipw_hardware *hw, unsigned char *data, - unsigned length) -{ - unsigned i; - unsigned long flags; - - start_timing(); - BUG_ON(length > hw->ll_mtu); - - if (ipwireless_debug) - dump_data_bytes("send", data, length); - - spin_lock_irqsave(&hw->lock, flags); - - hw->tx_ready = 0; - swap_packet_bitfield_to_le(data); - - if (hw->hw_version == HW_VERSION_1) { - outw((unsigned short) length, hw->base_port + IODWR); - - for (i = 0; i < length; i += 2) { - unsigned short d = data[i]; - __le16 raw_data; - - if (i + 1 < length) - d |= data[i + 1] << 8; - raw_data = cpu_to_le16(d); - outw(raw_data, hw->base_port + IODWR); - } - - outw(DCR_TXDONE, hw->base_port + IODCR); - } else if (hw->hw_version == HW_VERSION_2) { - outw((unsigned short) length, hw->base_port); - - for (i = 0; i < length; i += 2) { - unsigned short d = data[i]; - __le16 raw_data; - - if (i + 1 < length) - d |= data[i + 1] << 8; - raw_data = cpu_to_le16(d); - outw(raw_data, hw->base_port); - } - while ((i & 3) != 2) { - outw((unsigned short) 0xDEAD, hw->base_port); - i += 2; - } - writew(MEMRX_RX, &hw->memory_info_regs->memreg_rx); - } - - spin_unlock_irqrestore(&hw->lock, flags); - - end_write_timing(length); -} - -static void do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet) -{ - unsigned short fragment_data_len; - unsigned short data_left = packet->length - packet->offset; - unsigned short header_size; - union nl_packet pkt; - - header_size = - (packet->fragment_count == 0) - ? NL_FIRST_PACKET_HEADER_SIZE - : NL_FOLLOWING_PACKET_HEADER_SIZE; - fragment_data_len = hw->ll_mtu - header_size; - if (data_left < fragment_data_len) - fragment_data_len = data_left; - - /* - * hdr_first is now in machine bitfield order, which will be swapped - * to le just before it goes to hw - */ - pkt.hdr_first.protocol = packet->protocol; - pkt.hdr_first.address = packet->dest_addr; - pkt.hdr_first.packet_rank = 0; - - /* First packet? */ - if (packet->fragment_count == 0) { - pkt.hdr_first.packet_rank |= NL_FIRST_PACKET; - pkt.hdr_first.length_lsb = (unsigned char) packet->length; - pkt.hdr_first.length_msb = - (unsigned char) (packet->length >> 8); - } - - memcpy(pkt.rawpkt + header_size, - ((unsigned char *) packet) + sizeof(struct ipw_tx_packet) + - packet->offset, fragment_data_len); - packet->offset += fragment_data_len; - packet->fragment_count++; - - /* Last packet? (May also be first packet.) */ - if (packet->offset == packet->length) - pkt.hdr_first.packet_rank |= NL_LAST_PACKET; - do_send_fragment(hw, pkt.rawpkt, header_size + fragment_data_len); - - /* If this packet has unsent data, then re-queue it. */ - if (packet->offset < packet->length) { - /* - * Re-queue it at the head of the highest priority queue so - * it goes before all other packets - */ - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - list_add(&packet->queue, &hw->tx_queue[0]); - hw->tx_queued++; - spin_unlock_irqrestore(&hw->lock, flags); - } else { - if (packet->packet_callback) - packet->packet_callback(packet->callback_data, - packet->length); - kfree(packet); - } -} - -static void ipw_setup_hardware(struct ipw_hardware *hw) -{ - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (hw->hw_version == HW_VERSION_1) { - /* Reset RX FIFO */ - outw(DCR_RXRESET, hw->base_port + IODCR); - /* SB: Reset TX FIFO */ - outw(DCR_TXRESET, hw->base_port + IODCR); - - /* Enable TX and RX interrupts. */ - outw(IER_TXENABLED | IER_RXENABLED, hw->base_port + IOIER); - } else { - /* - * Set INTRACK bit (bit 0), which means we must explicitly - * acknowledge interrupts by clearing bit 2 of reg_config_and_status. - */ - unsigned short csr = readw(&hw->memregs_CCR->reg_config_and_status); - - csr |= 1; - writew(csr, &hw->memregs_CCR->reg_config_and_status); - } - spin_unlock_irqrestore(&hw->lock, flags); -} - -/* - * If 'packet' is NULL, then this function allocates a new packet, setting its - * length to 0 and ensuring it has the specified minimum amount of free space. - * - * If 'packet' is not NULL, then this function enlarges it if it doesn't - * have the specified minimum amount of free space. - * - */ -static struct ipw_rx_packet *pool_allocate(struct ipw_hardware *hw, - struct ipw_rx_packet *packet, - int minimum_free_space) -{ - - if (!packet) { - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (!list_empty(&hw->rx_pool)) { - packet = list_first_entry(&hw->rx_pool, - struct ipw_rx_packet, queue); - hw->rx_pool_size--; - spin_unlock_irqrestore(&hw->lock, flags); - list_del(&packet->queue); - } else { - const int min_capacity = - ipwireless_ppp_mru(hw->network) + 2; - int new_capacity; - - spin_unlock_irqrestore(&hw->lock, flags); - new_capacity = - (minimum_free_space > min_capacity - ? minimum_free_space - : min_capacity); - packet = kmalloc(sizeof(struct ipw_rx_packet) - + new_capacity, GFP_ATOMIC); - if (!packet) - return NULL; - packet->capacity = new_capacity; - } - packet->length = 0; - } - - if (packet->length + minimum_free_space > packet->capacity) { - struct ipw_rx_packet *old_packet = packet; - - packet = kmalloc(sizeof(struct ipw_rx_packet) + - old_packet->length + minimum_free_space, - GFP_ATOMIC); - if (!packet) { - kfree(old_packet); - return NULL; - } - memcpy(packet, old_packet, - sizeof(struct ipw_rx_packet) - + old_packet->length); - packet->capacity = old_packet->length + minimum_free_space; - kfree(old_packet); - } - - return packet; -} - -static void pool_free(struct ipw_hardware *hw, struct ipw_rx_packet *packet) -{ - if (hw->rx_pool_size > 6) - kfree(packet); - else { - hw->rx_pool_size++; - list_add(&packet->queue, &hw->rx_pool); - } -} - -static void queue_received_packet(struct ipw_hardware *hw, - unsigned int protocol, - unsigned int address, - const unsigned char *data, int length, - int is_last) -{ - unsigned int channel_idx = address - 1; - struct ipw_rx_packet *packet = NULL; - unsigned long flags; - - /* Discard packet if channel index is out of range. */ - if (channel_idx >= NL_NUM_OF_ADDRESSES) { - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": data packet has bad address %u\n", address); - return; - } - - /* - * ->packet_assembler is safe to touch unlocked, this is the only place - */ - if (protocol == TL_PROTOCOLID_COM_DATA) { - struct ipw_rx_packet **assem = - &hw->packet_assembler[channel_idx]; - - /* - * Create a new packet, or assembler already contains one - * enlarge it by 'length' bytes. - */ - (*assem) = pool_allocate(hw, *assem, length); - if (!(*assem)) { - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": no memory for incoming data packet, dropped!\n"); - return; - } - (*assem)->protocol = protocol; - (*assem)->channel_idx = channel_idx; - - /* Append this packet data onto existing data. */ - memcpy((unsigned char *)(*assem) + - sizeof(struct ipw_rx_packet) - + (*assem)->length, data, length); - (*assem)->length += length; - if (is_last) { - packet = *assem; - *assem = NULL; - /* Count queued DATA bytes only */ - spin_lock_irqsave(&hw->lock, flags); - hw->rx_bytes_queued += packet->length; - spin_unlock_irqrestore(&hw->lock, flags); - } - } else { - /* If it's a CTRL packet, don't assemble, just queue it. */ - packet = pool_allocate(hw, NULL, length); - if (!packet) { - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": no memory for incoming ctrl packet, dropped!\n"); - return; - } - packet->protocol = protocol; - packet->channel_idx = channel_idx; - memcpy((unsigned char *)packet + sizeof(struct ipw_rx_packet), - data, length); - packet->length = length; - } - - /* - * If this is the last packet, then send the assembled packet on to the - * network layer. - */ - if (packet) { - spin_lock_irqsave(&hw->lock, flags); - list_add_tail(&packet->queue, &hw->rx_queue); - /* Block reception of incoming packets if queue is full. */ - hw->blocking_rx = - (hw->rx_bytes_queued >= IPWIRELESS_RX_QUEUE_SIZE); - - spin_unlock_irqrestore(&hw->lock, flags); - schedule_work(&hw->work_rx); - } -} - -/* - * Workqueue callback - */ -static void ipw_receive_data_work(struct work_struct *work_rx) -{ - struct ipw_hardware *hw = - container_of(work_rx, struct ipw_hardware, work_rx); - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - while (!list_empty(&hw->rx_queue)) { - struct ipw_rx_packet *packet = - list_first_entry(&hw->rx_queue, - struct ipw_rx_packet, queue); - - if (hw->shutting_down) - break; - list_del(&packet->queue); - - /* - * Note: ipwireless_network_packet_received must be called in a - * process context (i.e. via schedule_work) because the tty - * output code can sleep in the tty_flip_buffer_push call. - */ - if (packet->protocol == TL_PROTOCOLID_COM_DATA) { - if (hw->network != NULL) { - /* If the network hasn't been disconnected. */ - spin_unlock_irqrestore(&hw->lock, flags); - /* - * This must run unlocked due to tty processing - * and mutex locking - */ - ipwireless_network_packet_received( - hw->network, - packet->channel_idx, - (unsigned char *)packet - + sizeof(struct ipw_rx_packet), - packet->length); - spin_lock_irqsave(&hw->lock, flags); - } - /* Count queued DATA bytes only */ - hw->rx_bytes_queued -= packet->length; - } else { - /* - * This is safe to be called locked, callchain does - * not block - */ - handle_received_CTRL_packet(hw, packet->channel_idx, - (unsigned char *)packet - + sizeof(struct ipw_rx_packet), - packet->length); - } - pool_free(hw, packet); - /* - * Unblock reception of incoming packets if queue is no longer - * full. - */ - hw->blocking_rx = - hw->rx_bytes_queued >= IPWIRELESS_RX_QUEUE_SIZE; - if (hw->shutting_down) - break; - } - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void handle_received_CTRL_packet(struct ipw_hardware *hw, - unsigned int channel_idx, - const unsigned char *data, int len) -{ - const struct ipw_control_packet_body *body = - (const struct ipw_control_packet_body *) data; - unsigned int changed_mask; - - if (len != sizeof(struct ipw_control_packet_body)) { - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": control packet was %d bytes - wrong size!\n", - len); - return; - } - - switch (body->sig_no) { - case COMCTRL_CTS: - changed_mask = IPW_CONTROL_LINE_CTS; - break; - case COMCTRL_DCD: - changed_mask = IPW_CONTROL_LINE_DCD; - break; - case COMCTRL_DSR: - changed_mask = IPW_CONTROL_LINE_DSR; - break; - case COMCTRL_RI: - changed_mask = IPW_CONTROL_LINE_RI; - break; - default: - changed_mask = 0; - } - - if (changed_mask != 0) { - if (body->value) - hw->control_lines[channel_idx] |= changed_mask; - else - hw->control_lines[channel_idx] &= ~changed_mask; - if (hw->network) - ipwireless_network_notify_control_line_change( - hw->network, - channel_idx, - hw->control_lines[channel_idx], - changed_mask); - } -} - -static void handle_received_packet(struct ipw_hardware *hw, - const union nl_packet *packet, - unsigned short len) -{ - unsigned int protocol = packet->hdr.protocol; - unsigned int address = packet->hdr.address; - unsigned int header_length; - const unsigned char *data; - unsigned int data_len; - int is_last = packet->hdr.packet_rank & NL_LAST_PACKET; - - if (packet->hdr.packet_rank & NL_FIRST_PACKET) - header_length = NL_FIRST_PACKET_HEADER_SIZE; - else - header_length = NL_FOLLOWING_PACKET_HEADER_SIZE; - - data = packet->rawpkt + header_length; - data_len = len - header_length; - switch (protocol) { - case TL_PROTOCOLID_COM_DATA: - case TL_PROTOCOLID_COM_CTRL: - queue_received_packet(hw, protocol, address, data, data_len, - is_last); - break; - case TL_PROTOCOLID_SETUP: - handle_received_SETUP_packet(hw, address, data, data_len, - is_last); - break; - } -} - -static void acknowledge_data_read(struct ipw_hardware *hw) -{ - if (hw->hw_version == HW_VERSION_1) - outw(DCR_RXDONE, hw->base_port + IODCR); - else - writew(MEMRX_PCINTACKK, - &hw->memory_info_regs->memreg_pc_interrupt_ack); -} - -/* - * Retrieve a packet from the IPW hardware. - */ -static void do_receive_packet(struct ipw_hardware *hw) -{ - unsigned len; - unsigned i; - unsigned char pkt[LL_MTU_MAX]; - - start_timing(); - - if (hw->hw_version == HW_VERSION_1) { - len = inw(hw->base_port + IODRR); - if (len > hw->ll_mtu) { - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": received a packet of %u bytes - longer than the MTU!\n", len); - outw(DCR_RXDONE | DCR_RXRESET, hw->base_port + IODCR); - return; - } - - for (i = 0; i < len; i += 2) { - __le16 raw_data = inw(hw->base_port + IODRR); - unsigned short data = le16_to_cpu(raw_data); - - pkt[i] = (unsigned char) data; - pkt[i + 1] = (unsigned char) (data >> 8); - } - } else { - len = inw(hw->base_port); - if (len > hw->ll_mtu) { - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": received a packet of %u bytes - longer than the MTU!\n", len); - writew(MEMRX_PCINTACKK, - &hw->memory_info_regs->memreg_pc_interrupt_ack); - return; - } - - for (i = 0; i < len; i += 2) { - __le16 raw_data = inw(hw->base_port); - unsigned short data = le16_to_cpu(raw_data); - - pkt[i] = (unsigned char) data; - pkt[i + 1] = (unsigned char) (data >> 8); - } - - while ((i & 3) != 2) { - inw(hw->base_port); - i += 2; - } - } - - acknowledge_data_read(hw); - - swap_packet_bitfield_from_le(pkt); - - if (ipwireless_debug) - dump_data_bytes("recv", pkt, len); - - handle_received_packet(hw, (union nl_packet *) pkt, len); - - end_read_timing(len); -} - -static int get_current_packet_priority(struct ipw_hardware *hw) -{ - /* - * If we're initializing, don't send anything of higher priority than - * PRIO_SETUP. The network layer therefore need not care about - * hardware initialization - any of its stuff will simply be queued - * until setup is complete. - */ - return (hw->to_setup || hw->initializing - ? PRIO_SETUP + 1 : NL_NUM_OF_PRIORITIES); -} - -/* - * return 1 if something has been received from hw - */ -static int get_packets_from_hw(struct ipw_hardware *hw) -{ - int received = 0; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - while (hw->rx_ready && !hw->blocking_rx) { - received = 1; - hw->rx_ready--; - spin_unlock_irqrestore(&hw->lock, flags); - - do_receive_packet(hw); - - spin_lock_irqsave(&hw->lock, flags); - } - spin_unlock_irqrestore(&hw->lock, flags); - - return received; -} - -/* - * Send pending packet up to given priority, prioritize SETUP data until - * hardware is fully setup. - * - * return 1 if more packets can be sent - */ -static int send_pending_packet(struct ipw_hardware *hw, int priority_limit) -{ - int more_to_send = 0; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (hw->tx_queued && hw->tx_ready) { - int priority; - struct ipw_tx_packet *packet = NULL; - - /* Pick a packet */ - for (priority = 0; priority < priority_limit; priority++) { - if (!list_empty(&hw->tx_queue[priority])) { - packet = list_first_entry( - &hw->tx_queue[priority], - struct ipw_tx_packet, - queue); - - hw->tx_queued--; - list_del(&packet->queue); - - break; - } - } - if (!packet) { - hw->tx_queued = 0; - spin_unlock_irqrestore(&hw->lock, flags); - return 0; - } - - spin_unlock_irqrestore(&hw->lock, flags); - - /* Send */ - do_send_packet(hw, packet); - - /* Check if more to send */ - spin_lock_irqsave(&hw->lock, flags); - for (priority = 0; priority < priority_limit; priority++) - if (!list_empty(&hw->tx_queue[priority])) { - more_to_send = 1; - break; - } - - if (!more_to_send) - hw->tx_queued = 0; - } - spin_unlock_irqrestore(&hw->lock, flags); - - return more_to_send; -} - -/* - * Send and receive all queued packets. - */ -static void ipwireless_do_tasklet(struct tasklet_struct *t) -{ - struct ipw_hardware *hw = from_tasklet(hw, t, tasklet); - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (hw->shutting_down) { - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - - if (hw->to_setup == 1) { - /* - * Initial setup data sent to hardware - */ - hw->to_setup = 2; - spin_unlock_irqrestore(&hw->lock, flags); - - ipw_setup_hardware(hw); - ipw_send_setup_packet(hw); - - send_pending_packet(hw, PRIO_SETUP + 1); - get_packets_from_hw(hw); - } else { - int priority_limit = get_current_packet_priority(hw); - int again; - - spin_unlock_irqrestore(&hw->lock, flags); - - do { - again = send_pending_packet(hw, priority_limit); - again |= get_packets_from_hw(hw); - } while (again); - } -} - -/* - * return true if the card is physically present. - */ -static int is_card_present(struct ipw_hardware *hw) -{ - if (hw->hw_version == HW_VERSION_1) - return inw(hw->base_port + IOIR) != 0xFFFF; - else - return readl(&hw->memory_info_regs->memreg_card_present) == - CARD_PRESENT_VALUE; -} - -static irqreturn_t ipwireless_handle_v1_interrupt(int irq, - struct ipw_hardware *hw) -{ - unsigned short irqn; - - irqn = inw(hw->base_port + IOIR); - - /* Check if card is present */ - if (irqn == 0xFFFF) - return IRQ_NONE; - else if (irqn != 0) { - unsigned short ack = 0; - unsigned long flags; - - /* Transmit complete. */ - if (irqn & IR_TXINTR) { - ack |= IR_TXINTR; - spin_lock_irqsave(&hw->lock, flags); - hw->tx_ready = 1; - spin_unlock_irqrestore(&hw->lock, flags); - } - /* Received data */ - if (irqn & IR_RXINTR) { - ack |= IR_RXINTR; - spin_lock_irqsave(&hw->lock, flags); - hw->rx_ready++; - spin_unlock_irqrestore(&hw->lock, flags); - } - if (ack != 0) { - outw(ack, hw->base_port + IOIR); - tasklet_schedule(&hw->tasklet); - } - return IRQ_HANDLED; - } - return IRQ_NONE; -} - -static void acknowledge_pcmcia_interrupt(struct ipw_hardware *hw) -{ - unsigned short csr = readw(&hw->memregs_CCR->reg_config_and_status); - - csr &= 0xfffd; - writew(csr, &hw->memregs_CCR->reg_config_and_status); -} - -static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq, - struct ipw_hardware *hw) -{ - int tx = 0; - int rx = 0; - int rx_repeat = 0; - int try_mem_tx_old; - unsigned long flags; - - do { - - unsigned short memtx = readw(hw->memreg_tx); - unsigned short memtx_serial; - unsigned short memrxdone = - readw(&hw->memory_info_regs->memreg_rx_done); - - try_mem_tx_old = 0; - - /* check whether the interrupt was generated by ipwireless card */ - if (!(memtx & MEMTX_TX) && !(memrxdone & MEMRX_RX_DONE)) { - - /* check if the card uses memreg_tx_old register */ - if (hw->memreg_tx == &hw->memory_info_regs->memreg_tx_new) { - memtx = readw(&hw->memory_info_regs->memreg_tx_old); - if (memtx & MEMTX_TX) { - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": Using memreg_tx_old\n"); - hw->memreg_tx = - &hw->memory_info_regs->memreg_tx_old; - } else { - return IRQ_NONE; - } - } else - return IRQ_NONE; - } - - /* - * See if the card is physically present. Note that while it is - * powering up, it appears not to be present. - */ - if (!is_card_present(hw)) { - acknowledge_pcmcia_interrupt(hw); - return IRQ_HANDLED; - } - - memtx_serial = memtx & (unsigned short) 0xff00; - if (memtx & MEMTX_TX) { - writew(memtx_serial, hw->memreg_tx); - - if (hw->serial_number_detected) { - if (memtx_serial != hw->last_memtx_serial) { - hw->last_memtx_serial = memtx_serial; - spin_lock_irqsave(&hw->lock, flags); - hw->rx_ready++; - spin_unlock_irqrestore(&hw->lock, flags); - rx = 1; - } else - /* Ignore 'Timer Recovery' duplicates. */ - rx_repeat = 1; - } else { - /* - * If a non-zero serial number is seen, then enable - * serial number checking. - */ - if (memtx_serial != 0) { - hw->serial_number_detected = 1; - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME - ": memreg_tx serial num detected\n"); - - spin_lock_irqsave(&hw->lock, flags); - hw->rx_ready++; - spin_unlock_irqrestore(&hw->lock, flags); - } - rx = 1; - } - } - if (memrxdone & MEMRX_RX_DONE) { - writew(0, &hw->memory_info_regs->memreg_rx_done); - spin_lock_irqsave(&hw->lock, flags); - hw->tx_ready = 1; - spin_unlock_irqrestore(&hw->lock, flags); - tx = 1; - } - if (tx) - writew(MEMRX_PCINTACKK, - &hw->memory_info_regs->memreg_pc_interrupt_ack); - - acknowledge_pcmcia_interrupt(hw); - - if (tx || rx) - tasklet_schedule(&hw->tasklet); - else if (!rx_repeat) { - if (hw->memreg_tx == &hw->memory_info_regs->memreg_tx_new) { - if (hw->serial_number_detected) - printk(KERN_WARNING IPWIRELESS_PCCARD_NAME - ": spurious interrupt - new_tx mode\n"); - else { - printk(KERN_WARNING IPWIRELESS_PCCARD_NAME - ": no valid memreg_tx value - switching to the old memreg_tx\n"); - hw->memreg_tx = - &hw->memory_info_regs->memreg_tx_old; - try_mem_tx_old = 1; - } - } else - printk(KERN_WARNING IPWIRELESS_PCCARD_NAME - ": spurious interrupt - old_tx mode\n"); - } - - } while (try_mem_tx_old == 1); - - return IRQ_HANDLED; -} - -irqreturn_t ipwireless_interrupt(int irq, void *dev_id) -{ - struct ipw_dev *ipw = dev_id; - - if (ipw->hardware->hw_version == HW_VERSION_1) - return ipwireless_handle_v1_interrupt(irq, ipw->hardware); - else - return ipwireless_handle_v2_v3_interrupt(irq, ipw->hardware); -} - -static void flush_packets_to_hw(struct ipw_hardware *hw) -{ - int priority_limit; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - priority_limit = get_current_packet_priority(hw); - spin_unlock_irqrestore(&hw->lock, flags); - - while (send_pending_packet(hw, priority_limit)); -} - -static void send_packet(struct ipw_hardware *hw, int priority, - struct ipw_tx_packet *packet) -{ - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - list_add_tail(&packet->queue, &hw->tx_queue[priority]); - hw->tx_queued++; - spin_unlock_irqrestore(&hw->lock, flags); - - flush_packets_to_hw(hw); -} - -/* Create data packet, non-atomic allocation */ -static void *alloc_data_packet(int data_size, - unsigned char dest_addr, - unsigned char protocol) -{ - struct ipw_tx_packet *packet = kzalloc( - sizeof(struct ipw_tx_packet) + data_size, - GFP_ATOMIC); - - if (!packet) - return NULL; - - INIT_LIST_HEAD(&packet->queue); - packet->dest_addr = dest_addr; - packet->protocol = protocol; - packet->length = data_size; - - return packet; -} - -static void *alloc_ctrl_packet(int header_size, - unsigned char dest_addr, - unsigned char protocol, - unsigned char sig_no) -{ - /* - * sig_no is located right after ipw_tx_packet struct in every - * CTRL or SETUP packets, we can use ipw_control_packet as a - * common struct - */ - struct ipw_control_packet *packet = kzalloc(header_size, GFP_ATOMIC); - - if (!packet) - return NULL; - - INIT_LIST_HEAD(&packet->header.queue); - packet->header.dest_addr = dest_addr; - packet->header.protocol = protocol; - packet->header.length = header_size - sizeof(struct ipw_tx_packet); - packet->body.sig_no = sig_no; - - return packet; -} - -int ipwireless_send_packet(struct ipw_hardware *hw, unsigned int channel_idx, - const u8 *data, unsigned int length, - void (*callback) (void *cb, unsigned int length), - void *callback_data) -{ - struct ipw_tx_packet *packet; - - packet = alloc_data_packet(length, (channel_idx + 1), - TL_PROTOCOLID_COM_DATA); - if (!packet) - return -ENOMEM; - packet->packet_callback = callback; - packet->callback_data = callback_data; - memcpy((unsigned char *) packet + sizeof(struct ipw_tx_packet), data, - length); - - send_packet(hw, PRIO_DATA, packet); - return 0; -} - -static int set_control_line(struct ipw_hardware *hw, int prio, - unsigned int channel_idx, int line, int state) -{ - struct ipw_control_packet *packet; - int protocolid = TL_PROTOCOLID_COM_CTRL; - - if (prio == PRIO_SETUP) - protocolid = TL_PROTOCOLID_SETUP; - - packet = alloc_ctrl_packet(sizeof(struct ipw_control_packet), - (channel_idx + 1), protocolid, line); - if (!packet) - return -ENOMEM; - packet->header.length = sizeof(struct ipw_control_packet_body); - packet->body.value = (state == 0 ? 0 : 1); - send_packet(hw, prio, &packet->header); - return 0; -} - - -static int set_DTR(struct ipw_hardware *hw, int priority, - unsigned int channel_idx, int state) -{ - if (state != 0) - hw->control_lines[channel_idx] |= IPW_CONTROL_LINE_DTR; - else - hw->control_lines[channel_idx] &= ~IPW_CONTROL_LINE_DTR; - - return set_control_line(hw, priority, channel_idx, COMCTRL_DTR, state); -} - -static int set_RTS(struct ipw_hardware *hw, int priority, - unsigned int channel_idx, int state) -{ - if (state != 0) - hw->control_lines[channel_idx] |= IPW_CONTROL_LINE_RTS; - else - hw->control_lines[channel_idx] &= ~IPW_CONTROL_LINE_RTS; - - return set_control_line(hw, priority, channel_idx, COMCTRL_RTS, state); -} - -int ipwireless_set_DTR(struct ipw_hardware *hw, unsigned int channel_idx, - int state) -{ - return set_DTR(hw, PRIO_CTRL, channel_idx, state); -} - -int ipwireless_set_RTS(struct ipw_hardware *hw, unsigned int channel_idx, - int state) -{ - return set_RTS(hw, PRIO_CTRL, channel_idx, state); -} - -struct ipw_setup_get_version_query_packet { - struct ipw_tx_packet header; - struct tl_setup_get_version_qry body; -}; - -struct ipw_setup_config_packet { - struct ipw_tx_packet header; - struct tl_setup_config_msg body; -}; - -struct ipw_setup_config_done_packet { - struct ipw_tx_packet header; - struct tl_setup_config_done_msg body; -}; - -struct ipw_setup_open_packet { - struct ipw_tx_packet header; - struct tl_setup_open_msg body; -}; - -struct ipw_setup_info_packet { - struct ipw_tx_packet header; - struct tl_setup_info_msg body; -}; - -struct ipw_setup_reboot_msg_ack { - struct ipw_tx_packet header; - struct TlSetupRebootMsgAck body; -}; - -/* This handles the actual initialization of the card */ -static void __handle_setup_get_version_rsp(struct ipw_hardware *hw) -{ - struct ipw_setup_config_packet *config_packet; - struct ipw_setup_config_done_packet *config_done_packet; - struct ipw_setup_open_packet *open_packet; - struct ipw_setup_info_packet *info_packet; - int port; - unsigned int channel_idx; - - /* generate config packet */ - for (port = 1; port <= NL_NUM_OF_ADDRESSES; port++) { - config_packet = alloc_ctrl_packet( - sizeof(struct ipw_setup_config_packet), - ADDR_SETUP_PROT, - TL_PROTOCOLID_SETUP, - TL_SETUP_SIGNO_CONFIG_MSG); - if (!config_packet) - goto exit_nomem; - config_packet->header.length = sizeof(struct tl_setup_config_msg); - config_packet->body.port_no = port; - config_packet->body.prio_data = PRIO_DATA; - config_packet->body.prio_ctrl = PRIO_CTRL; - send_packet(hw, PRIO_SETUP, &config_packet->header); - } - config_done_packet = alloc_ctrl_packet( - sizeof(struct ipw_setup_config_done_packet), - ADDR_SETUP_PROT, - TL_PROTOCOLID_SETUP, - TL_SETUP_SIGNO_CONFIG_DONE_MSG); - if (!config_done_packet) - goto exit_nomem; - config_done_packet->header.length = sizeof(struct tl_setup_config_done_msg); - send_packet(hw, PRIO_SETUP, &config_done_packet->header); - - /* generate open packet */ - for (port = 1; port <= NL_NUM_OF_ADDRESSES; port++) { - open_packet = alloc_ctrl_packet( - sizeof(struct ipw_setup_open_packet), - ADDR_SETUP_PROT, - TL_PROTOCOLID_SETUP, - TL_SETUP_SIGNO_OPEN_MSG); - if (!open_packet) - goto exit_nomem; - open_packet->header.length = sizeof(struct tl_setup_open_msg); - open_packet->body.port_no = port; - send_packet(hw, PRIO_SETUP, &open_packet->header); - } - for (channel_idx = 0; - channel_idx < NL_NUM_OF_ADDRESSES; channel_idx++) { - int ret; - - ret = set_DTR(hw, PRIO_SETUP, channel_idx, - (hw->control_lines[channel_idx] & - IPW_CONTROL_LINE_DTR) != 0); - if (ret) { - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": error setting DTR (%d)\n", ret); - return; - } - - ret = set_RTS(hw, PRIO_SETUP, channel_idx, - (hw->control_lines [channel_idx] & - IPW_CONTROL_LINE_RTS) != 0); - if (ret) { - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": error setting RTS (%d)\n", ret); - return; - } - } - /* - * For NDIS we assume that we are using sync PPP frames, for COM async. - * This driver uses NDIS mode too. We don't bother with translation - * from async -> sync PPP. - */ - info_packet = alloc_ctrl_packet(sizeof(struct ipw_setup_info_packet), - ADDR_SETUP_PROT, - TL_PROTOCOLID_SETUP, - TL_SETUP_SIGNO_INFO_MSG); - if (!info_packet) - goto exit_nomem; - info_packet->header.length = sizeof(struct tl_setup_info_msg); - info_packet->body.driver_type = NDISWAN_DRIVER; - info_packet->body.major_version = NDISWAN_DRIVER_MAJOR_VERSION; - info_packet->body.minor_version = NDISWAN_DRIVER_MINOR_VERSION; - send_packet(hw, PRIO_SETUP, &info_packet->header); - - /* Initialization is now complete, so we clear the 'to_setup' flag */ - hw->to_setup = 0; - - return; - -exit_nomem: - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": not enough memory to alloc control packet\n"); - hw->to_setup = -1; -} - -static void handle_setup_get_version_rsp(struct ipw_hardware *hw, - unsigned char vers_no) -{ - timer_delete(&hw->setup_timer); - hw->initializing = 0; - printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": card is ready.\n"); - - if (vers_no == TL_SETUP_VERSION) - __handle_setup_get_version_rsp(hw); - else - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": invalid hardware version no %u\n", - (unsigned int) vers_no); -} - -static void ipw_send_setup_packet(struct ipw_hardware *hw) -{ - struct ipw_setup_get_version_query_packet *ver_packet; - - ver_packet = alloc_ctrl_packet( - sizeof(struct ipw_setup_get_version_query_packet), - ADDR_SETUP_PROT, TL_PROTOCOLID_SETUP, - TL_SETUP_SIGNO_GET_VERSION_QRY); - if (!ver_packet) - return; - ver_packet->header.length = sizeof(struct tl_setup_get_version_qry); - - /* - * Response is handled in handle_received_SETUP_packet - */ - send_packet(hw, PRIO_SETUP, &ver_packet->header); -} - -static void handle_received_SETUP_packet(struct ipw_hardware *hw, - unsigned int address, - const unsigned char *data, int len, - int is_last) -{ - const union ipw_setup_rx_msg *rx_msg = (const union ipw_setup_rx_msg *) data; - - if (address != ADDR_SETUP_PROT) { - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": setup packet has bad address %d\n", address); - return; - } - - switch (rx_msg->sig_no) { - case TL_SETUP_SIGNO_GET_VERSION_RSP: - if (hw->to_setup) - handle_setup_get_version_rsp(hw, - rx_msg->version_rsp_msg.version); - break; - - case TL_SETUP_SIGNO_OPEN_MSG: - if (ipwireless_debug) { - unsigned int channel_idx = rx_msg->open_msg.port_no - 1; - - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": OPEN_MSG [channel %u] reply received\n", - channel_idx); - } - break; - - case TL_SETUP_SIGNO_INFO_MSG_ACK: - if (ipwireless_debug) - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME - ": card successfully configured as NDISWAN\n"); - break; - - case TL_SETUP_SIGNO_REBOOT_MSG: - if (hw->to_setup) - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME - ": Setup not completed - ignoring reboot msg\n"); - else { - struct ipw_setup_reboot_msg_ack *packet; - - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME - ": Acknowledging REBOOT message\n"); - packet = alloc_ctrl_packet( - sizeof(struct ipw_setup_reboot_msg_ack), - ADDR_SETUP_PROT, TL_PROTOCOLID_SETUP, - TL_SETUP_SIGNO_REBOOT_MSG_ACK); - if (!packet) { - pr_err(IPWIRELESS_PCCARD_NAME - ": Not enough memory to send reboot packet"); - break; - } - packet->header.length = - sizeof(struct TlSetupRebootMsgAck); - send_packet(hw, PRIO_SETUP, &packet->header); - if (hw->reboot_callback) - hw->reboot_callback(hw->reboot_callback_data); - } - break; - - default: - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": unknown setup message %u received\n", - (unsigned int) rx_msg->sig_no); - } -} - -static void do_close_hardware(struct ipw_hardware *hw) -{ - unsigned int irqn; - - if (hw->hw_version == HW_VERSION_1) { - /* Disable TX and RX interrupts. */ - outw(0, hw->base_port + IOIER); - - /* Acknowledge any outstanding interrupt requests */ - irqn = inw(hw->base_port + IOIR); - if (irqn & IR_TXINTR) - outw(IR_TXINTR, hw->base_port + IOIR); - if (irqn & IR_RXINTR) - outw(IR_RXINTR, hw->base_port + IOIR); - - synchronize_irq(hw->irq); - } -} - -struct ipw_hardware *ipwireless_hardware_create(void) -{ - int i; - struct ipw_hardware *hw = - kzalloc_obj(struct ipw_hardware); - - if (!hw) - return NULL; - - hw->irq = -1; - hw->initializing = 1; - hw->tx_ready = 1; - hw->rx_bytes_queued = 0; - hw->rx_pool_size = 0; - hw->last_memtx_serial = (unsigned short) 0xffff; - for (i = 0; i < NL_NUM_OF_PRIORITIES; i++) - INIT_LIST_HEAD(&hw->tx_queue[i]); - - INIT_LIST_HEAD(&hw->rx_queue); - INIT_LIST_HEAD(&hw->rx_pool); - spin_lock_init(&hw->lock); - tasklet_setup(&hw->tasklet, ipwireless_do_tasklet); - INIT_WORK(&hw->work_rx, ipw_receive_data_work); - timer_setup(&hw->setup_timer, ipwireless_setup_timer, 0); - - return hw; -} - -void ipwireless_init_hardware_v1(struct ipw_hardware *hw, - unsigned int base_port, - void __iomem *attr_memory, - void __iomem *common_memory, - int is_v2_card, - void (*reboot_callback) (void *data), - void *reboot_callback_data) -{ - if (hw->removed) { - hw->removed = 0; - enable_irq(hw->irq); - } - hw->base_port = base_port; - hw->hw_version = (is_v2_card ? HW_VERSION_2 : HW_VERSION_1); - hw->ll_mtu = (hw->hw_version == HW_VERSION_1 ? LL_MTU_V1 : LL_MTU_V2); - hw->memregs_CCR = (struct MEMCCR __iomem *) - ((unsigned short __iomem *) attr_memory + 0x200); - hw->memory_info_regs = (struct MEMINFREG __iomem *) common_memory; - hw->memreg_tx = &hw->memory_info_regs->memreg_tx_new; - hw->reboot_callback = reboot_callback; - hw->reboot_callback_data = reboot_callback_data; -} - -void ipwireless_init_hardware_v2_v3(struct ipw_hardware *hw) -{ - hw->initializing = 1; - hw->init_loops = 0; - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": waiting for card to start up...\n"); - ipwireless_setup_timer(&hw->setup_timer); -} - -static void ipwireless_setup_timer(struct timer_list *t) -{ - struct ipw_hardware *hw = timer_container_of(hw, t, setup_timer); - - hw->init_loops++; - - if (hw->init_loops == TL_SETUP_MAX_VERSION_QRY && - hw->hw_version == HW_VERSION_2 && - hw->memreg_tx == &hw->memory_info_regs->memreg_tx_new) { - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": failed to startup using TX2, trying TX\n"); - - hw->memreg_tx = &hw->memory_info_regs->memreg_tx_old; - hw->init_loops = 0; - } - /* Give up after a certain number of retries */ - if (hw->init_loops == TL_SETUP_MAX_VERSION_QRY) { - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": card failed to start up!\n"); - hw->initializing = 0; - } else { - /* Do not attempt to write to the board if it is not present. */ - if (is_card_present(hw)) { - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - hw->to_setup = 1; - hw->tx_ready = 1; - spin_unlock_irqrestore(&hw->lock, flags); - tasklet_schedule(&hw->tasklet); - } - - mod_timer(&hw->setup_timer, - jiffies + msecs_to_jiffies(TL_SETUP_VERSION_QRY_TMO)); - } -} - -/* - * Stop any interrupts from executing so that, once this function returns, - * other layers of the driver can be sure they won't get any more callbacks. - * Thus must be called on a proper process context. - */ -void ipwireless_stop_interrupts(struct ipw_hardware *hw) -{ - if (!hw->shutting_down) { - /* Tell everyone we are going down. */ - hw->shutting_down = 1; - timer_delete(&hw->setup_timer); - - /* Prevent the hardware from sending any more interrupts */ - do_close_hardware(hw); - } -} - -void ipwireless_hardware_free(struct ipw_hardware *hw) -{ - int i; - struct ipw_rx_packet *rp, *rq; - struct ipw_tx_packet *tp, *tq; - - ipwireless_stop_interrupts(hw); - - flush_work(&hw->work_rx); - - for (i = 0; i < NL_NUM_OF_ADDRESSES; i++) - kfree(hw->packet_assembler[i]); - - for (i = 0; i < NL_NUM_OF_PRIORITIES; i++) - list_for_each_entry_safe(tp, tq, &hw->tx_queue[i], queue) { - list_del(&tp->queue); - kfree(tp); - } - - list_for_each_entry_safe(rp, rq, &hw->rx_queue, queue) { - list_del(&rp->queue); - kfree(rp); - } - - list_for_each_entry_safe(rp, rq, &hw->rx_pool, queue) { - list_del(&rp->queue); - kfree(rp); - } - kfree(hw); -} - -/* - * Associate the specified network with this hardware, so it will receive events - * from it. - */ -void ipwireless_associate_network(struct ipw_hardware *hw, - struct ipw_network *network) -{ - hw->network = network; -} diff --git a/drivers/tty/ipwireless/hardware.h b/drivers/tty/ipwireless/hardware.h deleted file mode 100644 index e524a8fcc2ad..000000000000 --- a/drivers/tty/ipwireless/hardware.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#ifndef _IPWIRELESS_CS_HARDWARE_H_ -#define _IPWIRELESS_CS_HARDWARE_H_ - -#include -#include -#include - -#define IPW_CONTROL_LINE_CTS 0x0001 -#define IPW_CONTROL_LINE_DCD 0x0002 -#define IPW_CONTROL_LINE_DSR 0x0004 -#define IPW_CONTROL_LINE_RI 0x0008 -#define IPW_CONTROL_LINE_DTR 0x0010 -#define IPW_CONTROL_LINE_RTS 0x0020 - -struct ipw_hardware; -struct ipw_network; - -struct ipw_hardware *ipwireless_hardware_create(void); -void ipwireless_hardware_free(struct ipw_hardware *hw); -irqreturn_t ipwireless_interrupt(int irq, void *dev_id); -int ipwireless_set_DTR(struct ipw_hardware *hw, unsigned int channel_idx, - int state); -int ipwireless_set_RTS(struct ipw_hardware *hw, unsigned int channel_idx, - int state); -int ipwireless_send_packet(struct ipw_hardware *hw, - unsigned int channel_idx, - const unsigned char *data, - unsigned int length, - void (*packet_sent_callback) (void *cb, - unsigned int length), - void *sent_cb_data); -void ipwireless_associate_network(struct ipw_hardware *hw, - struct ipw_network *net); -void ipwireless_stop_interrupts(struct ipw_hardware *hw); -void ipwireless_init_hardware_v1(struct ipw_hardware *hw, - unsigned int base_port, - void __iomem *attr_memory, - void __iomem *common_memory, - int is_v2_card, - void (*reboot_cb) (void *data), - void *reboot_cb_data); -void ipwireless_init_hardware_v2_v3(struct ipw_hardware *hw); -void ipwireless_sleep(unsigned int tenths); - -#endif diff --git a/drivers/tty/ipwireless/main.c b/drivers/tty/ipwireless/main.c deleted file mode 100644 index a2875823290e..000000000000 --- a/drivers/tty/ipwireless/main.c +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#include "hardware.h" -#include "network.h" -#include "main.h" -#include "tty.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static const struct pcmcia_device_id ipw_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0100), - PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0200), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, ipw_ids); - -static void ipwireless_detach(struct pcmcia_device *link); - -/* - * Module params - */ -/* Debug mode: more verbose, print sent/recv bytes */ -int ipwireless_debug; -int ipwireless_loopback; -int ipwireless_out_queue = 10; - -module_param_named(debug, ipwireless_debug, int, 0); -module_param_named(loopback, ipwireless_loopback, int, 0); -module_param_named(out_queue, ipwireless_out_queue, int, 0); -MODULE_PARM_DESC(debug, "switch on debug messages [0]"); -MODULE_PARM_DESC(loopback, - "debug: enable ras_raw channel [0]"); -MODULE_PARM_DESC(out_queue, "debug: set size of outgoing PPP queue [10]"); - -/* Executes in process context. */ -static void signalled_reboot_work(struct work_struct *work_reboot) -{ - struct ipw_dev *ipw = container_of(work_reboot, struct ipw_dev, - work_reboot); - struct pcmcia_device *link = ipw->link; - pcmcia_reset_card(link->socket); -} - -static void signalled_reboot_callback(void *callback_data) -{ - struct ipw_dev *ipw = (struct ipw_dev *) callback_data; - - /* Delegate to process context. */ - schedule_work(&ipw->work_reboot); -} - -static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) -{ - struct ipw_dev *ipw = priv_data; - int ret; - - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - - /* 0x40 causes it to generate level mode interrupts. */ - /* 0x04 enables IREQ pin. */ - p_dev->config_index |= 0x44; - p_dev->io_lines = 16; - ret = pcmcia_request_io(p_dev); - if (ret) - return ret; - - if (!request_region(p_dev->resource[0]->start, - resource_size(p_dev->resource[0]), - IPWIRELESS_PCCARD_NAME)) { - ret = -EBUSY; - goto exit; - } - - p_dev->resource[2]->flags |= - WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE; - - ret = pcmcia_request_window(p_dev, p_dev->resource[2], 0); - if (ret != 0) - goto exit1; - - ret = pcmcia_map_mem_page(p_dev, p_dev->resource[2], p_dev->card_addr); - if (ret != 0) - goto exit1; - - ipw->is_v2_card = resource_size(p_dev->resource[2]) == 0x100; - - ipw->common_memory = ioremap(p_dev->resource[2]->start, - resource_size(p_dev->resource[2])); - if (!ipw->common_memory) { - ret = -ENOMEM; - goto exit1; - } - if (!request_mem_region(p_dev->resource[2]->start, - resource_size(p_dev->resource[2]), - IPWIRELESS_PCCARD_NAME)) { - ret = -EBUSY; - goto exit2; - } - - p_dev->resource[3]->flags |= WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | - WIN_ENABLE; - p_dev->resource[3]->end = 0; /* this used to be 0x1000 */ - ret = pcmcia_request_window(p_dev, p_dev->resource[3], 0); - if (ret != 0) - goto exit3; - - ret = pcmcia_map_mem_page(p_dev, p_dev->resource[3], 0); - if (ret != 0) - goto exit3; - - ipw->attr_memory = ioremap(p_dev->resource[3]->start, - resource_size(p_dev->resource[3])); - if (!ipw->attr_memory) { - ret = -ENOMEM; - goto exit3; - } - if (!request_mem_region(p_dev->resource[3]->start, - resource_size(p_dev->resource[3]), - IPWIRELESS_PCCARD_NAME)) { - ret = -EBUSY; - goto exit4; - } - - return 0; - -exit4: - iounmap(ipw->attr_memory); -exit3: - release_mem_region(p_dev->resource[2]->start, - resource_size(p_dev->resource[2])); -exit2: - iounmap(ipw->common_memory); -exit1: - release_region(p_dev->resource[0]->start, - resource_size(p_dev->resource[0])); -exit: - pcmcia_disable_device(p_dev); - return ret; -} - -static int config_ipwireless(struct ipw_dev *ipw) -{ - struct pcmcia_device *link = ipw->link; - int ret = 0; - - ipw->is_v2_card = 0; - link->config_flags |= CONF_AUTO_SET_IO | CONF_AUTO_SET_IOMEM | - CONF_ENABLE_IRQ; - - ret = pcmcia_loop_config(link, ipwireless_probe, ipw); - if (ret != 0) - return ret; - - INIT_WORK(&ipw->work_reboot, signalled_reboot_work); - - ipwireless_init_hardware_v1(ipw->hardware, link->resource[0]->start, - ipw->attr_memory, ipw->common_memory, - ipw->is_v2_card, signalled_reboot_callback, - ipw); - - ret = pcmcia_request_irq(link, ipwireless_interrupt); - if (ret != 0) - goto exit; - - printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": Card type %s\n", - ipw->is_v2_card ? "V2/V3" : "V1"); - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": I/O ports %pR, irq %d\n", link->resource[0], - (unsigned int) link->irq); - if (ipw->attr_memory && ipw->common_memory) - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": attr memory %pR, common memory %pR\n", - link->resource[3], - link->resource[2]); - - ipw->network = ipwireless_network_create(ipw->hardware); - if (!ipw->network) - goto exit; - - ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network); - if (!ipw->tty) - goto exit; - - ipwireless_init_hardware_v2_v3(ipw->hardware); - - /* - * Do the RequestConfiguration last, because it enables interrupts. - * Then we don't get any interrupts before we're ready for them. - */ - ret = pcmcia_enable_device(link); - if (ret != 0) - goto exit; - - return 0; - -exit: - if (ipw->common_memory) { - release_mem_region(link->resource[2]->start, - resource_size(link->resource[2])); - iounmap(ipw->common_memory); - } - if (ipw->attr_memory) { - release_mem_region(link->resource[3]->start, - resource_size(link->resource[3])); - iounmap(ipw->attr_memory); - } - pcmcia_disable_device(link); - return -1; -} - -static void release_ipwireless(struct ipw_dev *ipw) -{ - release_region(ipw->link->resource[0]->start, - resource_size(ipw->link->resource[0])); - if (ipw->common_memory) { - release_mem_region(ipw->link->resource[2]->start, - resource_size(ipw->link->resource[2])); - iounmap(ipw->common_memory); - } - if (ipw->attr_memory) { - release_mem_region(ipw->link->resource[3]->start, - resource_size(ipw->link->resource[3])); - iounmap(ipw->attr_memory); - } - pcmcia_disable_device(ipw->link); -} - -/* - * ipwireless_attach() creates an "instance" of the driver, allocating - * local data structures for one device (one interface). The device - * is registered with Card Services. - * - * The pcmcia_device structure is initialized, but we don't actually - * configure the card at this point -- we wait until we receive a - * card insertion event. - */ -static int ipwireless_attach(struct pcmcia_device *link) -{ - struct ipw_dev *ipw; - int ret; - - ipw = kzalloc_obj(struct ipw_dev); - if (!ipw) - return -ENOMEM; - - ipw->link = link; - link->priv = ipw; - - ipw->hardware = ipwireless_hardware_create(); - if (!ipw->hardware) { - kfree(ipw); - return -ENOMEM; - } - /* RegisterClient will call config_ipwireless */ - - ret = config_ipwireless(ipw); - - if (ret != 0) { - ipwireless_detach(link); - return ret; - } - - return 0; -} - -/* - * This deletes a driver "instance". The device is de-registered with - * Card Services. If it has been released, all local data structures - * are freed. Otherwise, the structures will be freed when the device - * is released. - */ -static void ipwireless_detach(struct pcmcia_device *link) -{ - struct ipw_dev *ipw = link->priv; - - release_ipwireless(ipw); - - if (ipw->tty != NULL) - ipwireless_tty_free(ipw->tty); - if (ipw->network != NULL) - ipwireless_network_free(ipw->network); - if (ipw->hardware != NULL) - ipwireless_hardware_free(ipw->hardware); - kfree(ipw); -} - -static struct pcmcia_driver me = { - .owner = THIS_MODULE, - .probe = ipwireless_attach, - .remove = ipwireless_detach, - .name = IPWIRELESS_PCCARD_NAME, - .id_table = ipw_ids -}; - -/* - * Module insertion : initialisation of the module. - * Register the card with cardmgr... - */ -static int __init init_ipwireless(void) -{ - int ret; - - ret = ipwireless_tty_init(); - if (ret != 0) - return ret; - - ret = pcmcia_register_driver(&me); - if (ret != 0) - ipwireless_tty_release(); - - return ret; -} - -/* - * Module removal - */ -static void __exit exit_ipwireless(void) -{ - pcmcia_unregister_driver(&me); - ipwireless_tty_release(); -} - -module_init(init_ipwireless); -module_exit(exit_ipwireless); - -MODULE_AUTHOR(IPWIRELESS_PCMCIA_AUTHOR); -MODULE_DESCRIPTION(IPWIRELESS_PCCARD_NAME " " IPWIRELESS_PCMCIA_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/ipwireless/main.h b/drivers/tty/ipwireless/main.h deleted file mode 100644 index a5728a5b3f83..000000000000 --- a/drivers/tty/ipwireless/main.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#ifndef _IPWIRELESS_CS_H_ -#define _IPWIRELESS_CS_H_ - -#include -#include - -#include -#include - -#include "hardware.h" - -#define IPWIRELESS_PCCARD_NAME "ipwireless" -#define IPWIRELESS_PCMCIA_VERSION "1.1" -#define IPWIRELESS_PCMCIA_AUTHOR \ - "Stephen Blackheath, Ben Martel, Jiri Kosina and David Sterba" - -#define IPWIRELESS_TX_QUEUE_SIZE 262144 -#define IPWIRELESS_RX_QUEUE_SIZE 262144 - -#define IPWIRELESS_STATE_DEBUG - -struct ipw_hardware; -struct ipw_network; -struct ipw_tty; - -struct ipw_dev { - struct pcmcia_device *link; - int is_v2_card; - - void __iomem *attr_memory; - - void __iomem *common_memory; - - /* Hardware context */ - struct ipw_hardware *hardware; - /* Network layer context */ - struct ipw_network *network; - /* TTY device context */ - struct ipw_tty *tty; - struct work_struct work_reboot; -}; - -/* Module parametres */ -extern int ipwireless_debug; -extern int ipwireless_loopback; -extern int ipwireless_out_queue; - -#endif diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c deleted file mode 100644 index ad2c5157a018..000000000000 --- a/drivers/tty/ipwireless/network.c +++ /dev/null @@ -1,517 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "network.h" -#include "hardware.h" -#include "main.h" -#include "tty.h" - -#define MAX_ASSOCIATED_TTYS 2 - -#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) - -struct ipw_network { - /* Hardware context, used for calls to hardware layer. */ - struct ipw_hardware *hardware; - /* Context for kernel 'generic_ppp' functionality */ - struct ppp_channel *ppp_channel; - /* tty context connected with IPW console */ - struct ipw_tty *associated_ttys[NO_OF_IPW_CHANNELS][MAX_ASSOCIATED_TTYS]; - /* True if ppp needs waking up once we're ready to xmit */ - int ppp_blocked; - /* Number of packets queued up in hardware module. */ - int outgoing_packets_queued; - /* Spinlock to avoid interrupts during shutdown */ - spinlock_t lock; - struct mutex close_lock; - - /* PPP ioctl data, not actually used anywere */ - unsigned int flags; - unsigned int rbits; - u32 xaccm[8]; - u32 raccm; - int mru; - - int shutting_down; - unsigned int ras_control_lines; - - struct work_struct work_go_online; - struct work_struct work_go_offline; -}; - -static void notify_packet_sent(void *callback_data, unsigned int packet_length) -{ - struct ipw_network *network = callback_data; - unsigned long flags; - - spin_lock_irqsave(&network->lock, flags); - network->outgoing_packets_queued--; - if (network->ppp_channel != NULL) { - if (network->ppp_blocked) { - network->ppp_blocked = 0; - spin_unlock_irqrestore(&network->lock, flags); - ppp_output_wakeup(network->ppp_channel); - if (ipwireless_debug) - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME - ": ppp unblocked\n"); - } else - spin_unlock_irqrestore(&network->lock, flags); - } else - spin_unlock_irqrestore(&network->lock, flags); -} - -/* - * Called by the ppp system when it has a packet to send to the hardware. - */ -static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel, - struct sk_buff *skb) -{ - struct ipw_network *network = ppp_channel->private; - unsigned long flags; - - spin_lock_irqsave(&network->lock, flags); - if (network->outgoing_packets_queued < ipwireless_out_queue) { - unsigned char *buf; - static unsigned char header[] = { - PPP_ALLSTATIONS, /* 0xff */ - PPP_UI, /* 0x03 */ - }; - int ret; - - network->outgoing_packets_queued++; - spin_unlock_irqrestore(&network->lock, flags); - - /* - * If we have the requested amount of headroom in the skb we - * were handed, then we can add the header efficiently. - */ - if (skb_headroom(skb) >= 2) { - memcpy(skb_push(skb, 2), header, 2); - ret = ipwireless_send_packet(network->hardware, - IPW_CHANNEL_RAS, skb->data, - skb->len, - notify_packet_sent, - network); - if (ret < 0) { - skb_pull(skb, 2); - return 0; - } - } else { - /* Otherwise (rarely) we do it inefficiently. */ - buf = kmalloc(skb->len + 2, GFP_ATOMIC); - if (!buf) - return 0; - memcpy(buf + 2, skb->data, skb->len); - memcpy(buf, header, 2); - ret = ipwireless_send_packet(network->hardware, - IPW_CHANNEL_RAS, buf, - skb->len + 2, - notify_packet_sent, - network); - kfree(buf); - if (ret < 0) - return 0; - } - kfree_skb(skb); - return 1; - } else { - /* - * Otherwise reject the packet, and flag that the ppp system - * needs to be unblocked once we are ready to send. - */ - network->ppp_blocked = 1; - spin_unlock_irqrestore(&network->lock, flags); - if (ipwireless_debug) - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": ppp blocked\n"); - return 0; - } -} - -/* Handle an ioctl call that has come in via ppp. (copy of ppp_async_ioctl() */ -static int ipwireless_ppp_ioctl(struct ppp_channel *ppp_channel, - unsigned int cmd, unsigned long arg) -{ - struct ipw_network *network = ppp_channel->private; - int err, val; - u32 accm[8]; - int __user *user_arg = (int __user *) arg; - - err = -EFAULT; - switch (cmd) { - case PPPIOCGFLAGS: - val = network->flags | network->rbits; - if (put_user(val, user_arg)) - break; - err = 0; - break; - - case PPPIOCSFLAGS: - if (get_user(val, user_arg)) - break; - network->flags = val & ~SC_RCV_BITS; - network->rbits = val & SC_RCV_BITS; - err = 0; - break; - - case PPPIOCGASYNCMAP: - if (put_user(network->xaccm[0], user_arg)) - break; - err = 0; - break; - - case PPPIOCSASYNCMAP: - if (get_user(network->xaccm[0], user_arg)) - break; - err = 0; - break; - - case PPPIOCGRASYNCMAP: - if (put_user(network->raccm, user_arg)) - break; - err = 0; - break; - - case PPPIOCSRASYNCMAP: - if (get_user(network->raccm, user_arg)) - break; - err = 0; - break; - - case PPPIOCGXASYNCMAP: - if (copy_to_user((void __user *) arg, network->xaccm, - sizeof(network->xaccm))) - break; - err = 0; - break; - - case PPPIOCSXASYNCMAP: - if (copy_from_user(accm, (void __user *) arg, sizeof(accm))) - break; - accm[2] &= ~0x40000000U; /* can't escape 0x5e */ - accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ - memcpy(network->xaccm, accm, sizeof(network->xaccm)); - err = 0; - break; - - case PPPIOCGMRU: - if (put_user(network->mru, user_arg)) - break; - err = 0; - break; - - case PPPIOCSMRU: - if (get_user(val, user_arg)) - break; - if (val < PPP_MRU) - val = PPP_MRU; - network->mru = val; - err = 0; - break; - - default: - err = -ENOTTY; - } - - return err; -} - -static const struct ppp_channel_ops ipwireless_ppp_channel_ops = { - .start_xmit = ipwireless_ppp_start_xmit, - .ioctl = ipwireless_ppp_ioctl -}; - -static void do_go_online(struct work_struct *work_go_online) -{ - struct ipw_network *network = - container_of(work_go_online, struct ipw_network, - work_go_online); - unsigned long flags; - - spin_lock_irqsave(&network->lock, flags); - if (!network->ppp_channel) { - struct ppp_channel *channel; - - spin_unlock_irqrestore(&network->lock, flags); - channel = kzalloc_obj(struct ppp_channel); - if (!channel) { - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": unable to allocate PPP channel\n"); - return; - } - channel->private = network; - channel->mtu = 16384; /* Wild guess */ - channel->hdrlen = 2; - channel->ops = &ipwireless_ppp_channel_ops; - - network->flags = 0; - network->rbits = 0; - network->mru = PPP_MRU; - memset(network->xaccm, 0, sizeof(network->xaccm)); - network->xaccm[0] = ~0U; - network->xaccm[3] = 0x60000000U; - network->raccm = ~0U; - if (ppp_register_channel(channel) < 0) { - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": unable to register PPP channel\n"); - kfree(channel); - return; - } - spin_lock_irqsave(&network->lock, flags); - network->ppp_channel = channel; - } - spin_unlock_irqrestore(&network->lock, flags); -} - -static void do_go_offline(struct work_struct *work_go_offline) -{ - struct ipw_network *network = - container_of(work_go_offline, struct ipw_network, - work_go_offline); - unsigned long flags; - - mutex_lock(&network->close_lock); - spin_lock_irqsave(&network->lock, flags); - if (network->ppp_channel != NULL) { - struct ppp_channel *channel = network->ppp_channel; - - network->ppp_channel = NULL; - spin_unlock_irqrestore(&network->lock, flags); - mutex_unlock(&network->close_lock); - ppp_unregister_channel(channel); - } else { - spin_unlock_irqrestore(&network->lock, flags); - mutex_unlock(&network->close_lock); - } -} - -void ipwireless_network_notify_control_line_change(struct ipw_network *network, - unsigned int channel_idx, - unsigned int control_lines, - unsigned int changed_mask) -{ - int i; - - if (channel_idx == IPW_CHANNEL_RAS) - network->ras_control_lines = control_lines; - - for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) { - struct ipw_tty *tty = - network->associated_ttys[channel_idx][i]; - - /* - * If it's associated with a tty (other than the RAS channel - * when we're online), then send the data to that tty. The RAS - * channel's data is handled above - it always goes through - * ppp_generic. - */ - if (tty) - ipwireless_tty_notify_control_line_change(tty, - channel_idx, - control_lines, - changed_mask); - } -} - -/* - * Some versions of firmware stuff packets with 0xff 0x03 (PPP: ALLSTATIONS, UI) - * bytes, which are required on sent packet, but not always present on received - * packets - */ -static struct sk_buff *ipw_packet_received_skb(unsigned char *data, - unsigned int length) -{ - struct sk_buff *skb; - - if (length > 2 && data[0] == PPP_ALLSTATIONS && data[1] == PPP_UI) { - length -= 2; - data += 2; - } - - skb = dev_alloc_skb(length + 4); - if (skb == NULL) - return NULL; - skb_reserve(skb, 2); - skb_put_data(skb, data, length); - - return skb; -} - -void ipwireless_network_packet_received(struct ipw_network *network, - unsigned int channel_idx, - unsigned char *data, - unsigned int length) -{ - int i; - unsigned long flags; - - for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) { - struct ipw_tty *tty = network->associated_ttys[channel_idx][i]; - - if (!tty) - continue; - - /* - * If it's associated with a tty (other than the RAS channel - * when we're online), then send the data to that tty. The RAS - * channel's data is handled above - it always goes through - * ppp_generic. - */ - if (channel_idx == IPW_CHANNEL_RAS - && (network->ras_control_lines & - IPW_CONTROL_LINE_DCD) != 0 - && ipwireless_tty_is_modem(tty)) { - /* - * If data came in on the RAS channel and this tty is - * the modem tty, and we are online, then we send it to - * the PPP layer. - */ - mutex_lock(&network->close_lock); - spin_lock_irqsave(&network->lock, flags); - if (network->ppp_channel != NULL) { - struct sk_buff *skb; - - spin_unlock_irqrestore(&network->lock, - flags); - - /* Send the data to the ppp_generic module. */ - skb = ipw_packet_received_skb(data, length); - if (skb) - ppp_input(network->ppp_channel, skb); - } else - spin_unlock_irqrestore(&network->lock, - flags); - mutex_unlock(&network->close_lock); - } - /* Otherwise we send it out the tty. */ - else - ipwireless_tty_received(tty, data, length); - } -} - -struct ipw_network *ipwireless_network_create(struct ipw_hardware *hw) -{ - struct ipw_network *network = - kzalloc_obj(struct ipw_network); - - if (!network) - return NULL; - - spin_lock_init(&network->lock); - mutex_init(&network->close_lock); - - network->hardware = hw; - - INIT_WORK(&network->work_go_online, do_go_online); - INIT_WORK(&network->work_go_offline, do_go_offline); - - ipwireless_associate_network(hw, network); - - return network; -} - -void ipwireless_network_free(struct ipw_network *network) -{ - network->shutting_down = 1; - - ipwireless_ppp_close(network); - flush_work(&network->work_go_online); - flush_work(&network->work_go_offline); - - ipwireless_stop_interrupts(network->hardware); - ipwireless_associate_network(network->hardware, NULL); - - kfree(network); -} - -void ipwireless_associate_network_tty(struct ipw_network *network, - unsigned int channel_idx, - struct ipw_tty *tty) -{ - int i; - - for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) - if (network->associated_ttys[channel_idx][i] == NULL) { - network->associated_ttys[channel_idx][i] = tty; - break; - } -} - -void ipwireless_disassociate_network_ttys(struct ipw_network *network, - unsigned int channel_idx) -{ - int i; - - for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) - network->associated_ttys[channel_idx][i] = NULL; -} - -void ipwireless_ppp_open(struct ipw_network *network) -{ - if (ipwireless_debug) - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": online\n"); - schedule_work(&network->work_go_online); -} - -void ipwireless_ppp_close(struct ipw_network *network) -{ - /* Disconnect from the wireless network. */ - if (ipwireless_debug) - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": offline\n"); - schedule_work(&network->work_go_offline); -} - -int ipwireless_ppp_channel_index(struct ipw_network *network) -{ - int ret = -1; - unsigned long flags; - - spin_lock_irqsave(&network->lock, flags); - if (network->ppp_channel != NULL) - ret = ppp_channel_index(network->ppp_channel); - spin_unlock_irqrestore(&network->lock, flags); - - return ret; -} - -int ipwireless_ppp_unit_number(struct ipw_network *network) -{ - int ret = -1; - unsigned long flags; - - spin_lock_irqsave(&network->lock, flags); - if (network->ppp_channel != NULL) - ret = ppp_unit_number(network->ppp_channel); - spin_unlock_irqrestore(&network->lock, flags); - - return ret; -} - -int ipwireless_ppp_mru(const struct ipw_network *network) -{ - return network->mru; -} diff --git a/drivers/tty/ipwireless/network.h b/drivers/tty/ipwireless/network.h deleted file mode 100644 index 784932a59a73..000000000000 --- a/drivers/tty/ipwireless/network.h +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#ifndef _IPWIRELESS_CS_NETWORK_H_ -#define _IPWIRELESS_CS_NETWORK_H_ - -#include - -struct ipw_network; -struct ipw_tty; -struct ipw_hardware; - -/* Definitions of the different channels on the PCMCIA UE */ -#define IPW_CHANNEL_RAS 0 -#define IPW_CHANNEL_DIALLER 1 -#define IPW_CHANNEL_CONSOLE 2 -#define NO_OF_IPW_CHANNELS 5 - -void ipwireless_network_notify_control_line_change(struct ipw_network *net, - unsigned int channel_idx, unsigned int control_lines, - unsigned int control_mask); -void ipwireless_network_packet_received(struct ipw_network *net, - unsigned int channel_idx, unsigned char *data, - unsigned int length); -struct ipw_network *ipwireless_network_create(struct ipw_hardware *hw); -void ipwireless_network_free(struct ipw_network *net); -void ipwireless_associate_network_tty(struct ipw_network *net, - unsigned int channel_idx, struct ipw_tty *tty); -void ipwireless_disassociate_network_ttys(struct ipw_network *net, - unsigned int channel_idx); - -void ipwireless_ppp_open(struct ipw_network *net); - -void ipwireless_ppp_close(struct ipw_network *net); -int ipwireless_ppp_channel_index(struct ipw_network *net); -int ipwireless_ppp_unit_number(struct ipw_network *net); -int ipwireless_ppp_mru(const struct ipw_network *net); - -#endif diff --git a/drivers/tty/ipwireless/setup_protocol.h b/drivers/tty/ipwireless/setup_protocol.h deleted file mode 100644 index d4a7ae257ca5..000000000000 --- a/drivers/tty/ipwireless/setup_protocol.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#ifndef _IPWIRELESS_CS_SETUP_PROTOCOL_H_ -#define _IPWIRELESS_CS_SETUP_PROTOCOL_H_ - -/* Version of the setup protocol and transport protocols */ -#define TL_SETUP_VERSION 1 - -#define TL_SETUP_VERSION_QRY_TMO 1000 -#define TL_SETUP_MAX_VERSION_QRY 30 - -/* Message numbers 0-9 are obsoleted and must not be reused! */ -#define TL_SETUP_SIGNO_GET_VERSION_QRY 10 -#define TL_SETUP_SIGNO_GET_VERSION_RSP 11 -#define TL_SETUP_SIGNO_CONFIG_MSG 12 -#define TL_SETUP_SIGNO_CONFIG_DONE_MSG 13 -#define TL_SETUP_SIGNO_OPEN_MSG 14 -#define TL_SETUP_SIGNO_CLOSE_MSG 15 - -#define TL_SETUP_SIGNO_INFO_MSG 20 -#define TL_SETUP_SIGNO_INFO_MSG_ACK 21 - -#define TL_SETUP_SIGNO_REBOOT_MSG 22 -#define TL_SETUP_SIGNO_REBOOT_MSG_ACK 23 - -/* Synchronous start-messages */ -struct tl_setup_get_version_qry { - unsigned char sig_no; /* TL_SETUP_SIGNO_GET_VERSION_QRY */ -} __attribute__ ((__packed__)); - -struct tl_setup_get_version_rsp { - unsigned char sig_no; /* TL_SETUP_SIGNO_GET_VERSION_RSP */ - unsigned char version; /* TL_SETUP_VERSION */ -} __attribute__ ((__packed__)); - -struct tl_setup_config_msg { - unsigned char sig_no; /* TL_SETUP_SIGNO_CONFIG_MSG */ - unsigned char port_no; - unsigned char prio_data; - unsigned char prio_ctrl; -} __attribute__ ((__packed__)); - -struct tl_setup_config_done_msg { - unsigned char sig_no; /* TL_SETUP_SIGNO_CONFIG_DONE_MSG */ -} __attribute__ ((__packed__)); - -/* Asynchronous messages */ -struct tl_setup_open_msg { - unsigned char sig_no; /* TL_SETUP_SIGNO_OPEN_MSG */ - unsigned char port_no; -} __attribute__ ((__packed__)); - -struct tl_setup_close_msg { - unsigned char sig_no; /* TL_SETUP_SIGNO_CLOSE_MSG */ - unsigned char port_no; -} __attribute__ ((__packed__)); - -/* Driver type - for use in tl_setup_info_msg.driver_type */ -#define COMM_DRIVER 0 -#define NDISWAN_DRIVER 1 -#define NDISWAN_DRIVER_MAJOR_VERSION 2 -#define NDISWAN_DRIVER_MINOR_VERSION 0 - -/* - * It should not matter when this message comes over as we just store the - * results and send the ACK. - */ -struct tl_setup_info_msg { - unsigned char sig_no; /* TL_SETUP_SIGNO_INFO_MSG */ - unsigned char driver_type; - unsigned char major_version; - unsigned char minor_version; -} __attribute__ ((__packed__)); - -struct tl_setup_info_msgAck { - unsigned char sig_no; /* TL_SETUP_SIGNO_INFO_MSG_ACK */ -} __attribute__ ((__packed__)); - -struct TlSetupRebootMsgAck { - unsigned char sig_no; /* TL_SETUP_SIGNO_REBOOT_MSG_ACK */ -} __attribute__ ((__packed__)); - -/* Define a union of all the msgs that the driver can receive from the card.*/ -union ipw_setup_rx_msg { - unsigned char sig_no; - struct tl_setup_get_version_rsp version_rsp_msg; - struct tl_setup_open_msg open_msg; - struct tl_setup_close_msg close_msg; - struct tl_setup_info_msg InfoMsg; - struct tl_setup_info_msgAck info_msg_ack; -} __attribute__ ((__packed__)); - -#endif /* _IPWIRELESS_CS_SETUP_PROTOCOL_H_ */ diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c deleted file mode 100644 index ffdbcdd041a3..000000000000 --- a/drivers/tty/ipwireless/tty.c +++ /dev/null @@ -1,627 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tty.h" -#include "network.h" -#include "hardware.h" -#include "main.h" - -#define IPWIRELESS_PCMCIA_START (0) -#define IPWIRELESS_PCMCIA_MINORS (24) -#define IPWIRELESS_PCMCIA_MINOR_RANGE (8) - -#define TTYTYPE_MODEM (0) -#define TTYTYPE_MONITOR (1) -#define TTYTYPE_RAS_RAW (2) - -struct ipw_tty { - struct tty_port port; - int index; - struct ipw_hardware *hardware; - unsigned int channel_idx; - unsigned int secondary_channel_idx; - int tty_type; - struct ipw_network *network; - unsigned int control_lines; - struct mutex ipw_tty_mutex; - int tx_bytes_queued; -}; - -static struct ipw_tty *ttys[IPWIRELESS_PCMCIA_MINORS]; - -static struct tty_driver *ipw_tty_driver; - -static char *tty_type_name(int tty_type) -{ - static char *channel_names[] = { - "modem", - "monitor", - "RAS-raw" - }; - - return channel_names[tty_type]; -} - -static struct ipw_tty *get_tty(int index) -{ - /* - * The 'ras_raw' channel is only available when 'loopback' mode - * is enabled. - * Number of minor starts with 16 (_RANGE * _RAS_RAW). - */ - if (!ipwireless_loopback && index >= - IPWIRELESS_PCMCIA_MINOR_RANGE * TTYTYPE_RAS_RAW) - return NULL; - - return ttys[index]; -} - -static int ipw_open(struct tty_struct *linux_tty, struct file *filp) -{ - struct ipw_tty *tty = get_tty(linux_tty->index); - - if (!tty) - return -ENODEV; - - mutex_lock(&tty->ipw_tty_mutex); - if (tty->port.count == 0) - tty->tx_bytes_queued = 0; - - tty->port.count++; - - tty->port.tty = linux_tty; - linux_tty->driver_data = tty; - - if (tty->tty_type == TTYTYPE_MODEM) - ipwireless_ppp_open(tty->network); - - mutex_unlock(&tty->ipw_tty_mutex); - - return 0; -} - -static void do_ipw_close(struct ipw_tty *tty) -{ - tty->port.count--; - - if (tty->port.count == 0) { - struct tty_struct *linux_tty = tty->port.tty; - - if (linux_tty != NULL) { - tty->port.tty = NULL; - linux_tty->driver_data = NULL; - - if (tty->tty_type == TTYTYPE_MODEM) - ipwireless_ppp_close(tty->network); - } - } -} - -static void ipw_hangup(struct tty_struct *linux_tty) -{ - struct ipw_tty *tty = linux_tty->driver_data; - - if (!tty) - return; - - mutex_lock(&tty->ipw_tty_mutex); - if (tty->port.count == 0) { - mutex_unlock(&tty->ipw_tty_mutex); - return; - } - - do_ipw_close(tty); - - mutex_unlock(&tty->ipw_tty_mutex); -} - -static void ipw_close(struct tty_struct *linux_tty, struct file *filp) -{ - ipw_hangup(linux_tty); -} - -/* Take data received from hardware, and send it out the tty */ -void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, - unsigned int length) -{ - int work = 0; - - mutex_lock(&tty->ipw_tty_mutex); - - if (!tty->port.count) { - mutex_unlock(&tty->ipw_tty_mutex); - return; - } - mutex_unlock(&tty->ipw_tty_mutex); - - work = tty_insert_flip_string(&tty->port, data, length); - - if (work != length) - printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME - ": %d chars not inserted to flip buffer!\n", - length - work); - - if (work) - tty_flip_buffer_push(&tty->port); -} - -static void ipw_write_packet_sent_callback(void *callback_data, - unsigned int packet_length) -{ - struct ipw_tty *tty = callback_data; - - /* - * Packet has been sent, so we subtract the number of bytes from our - * tally of outstanding TX bytes. - */ - tty->tx_bytes_queued -= packet_length; -} - -static ssize_t ipw_write(struct tty_struct *linux_tty, const u8 *buf, - size_t count) -{ - struct ipw_tty *tty = linux_tty->driver_data; - int room, ret; - - if (!tty) - return -ENODEV; - - mutex_lock(&tty->ipw_tty_mutex); - if (!tty->port.count) { - mutex_unlock(&tty->ipw_tty_mutex); - return -EINVAL; - } - - room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; - if (room < 0) - room = 0; - /* Don't allow caller to write any more than we have room for */ - if (count > room) - count = room; - - if (count == 0) { - mutex_unlock(&tty->ipw_tty_mutex); - return 0; - } - - ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS, - buf, count, - ipw_write_packet_sent_callback, tty); - if (ret < 0) { - mutex_unlock(&tty->ipw_tty_mutex); - return 0; - } - - tty->tx_bytes_queued += count; - mutex_unlock(&tty->ipw_tty_mutex); - - return count; -} - -static unsigned int ipw_write_room(struct tty_struct *linux_tty) -{ - struct ipw_tty *tty = linux_tty->driver_data; - int room; - - /* FIXME: Exactly how is the tty object locked here .. */ - if (!tty) - return 0; - - if (!tty->port.count) - return 0; - - room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; - if (room < 0) - room = 0; - - return room; -} - -static int ipwireless_get_serial_info(struct tty_struct *linux_tty, - struct serial_struct *ss) -{ - struct ipw_tty *tty = linux_tty->driver_data; - - if (!tty) - return -ENODEV; - - if (!tty->port.count) - return -EINVAL; - - ss->type = PORT_UNKNOWN; - ss->line = tty->index; - ss->baud_base = 115200; - return 0; -} - -static int ipwireless_set_serial_info(struct tty_struct *linux_tty, - struct serial_struct *ss) -{ - return 0; /* Keeps the PCMCIA scripts happy. */ -} - -static unsigned int ipw_chars_in_buffer(struct tty_struct *linux_tty) -{ - struct ipw_tty *tty = linux_tty->driver_data; - - if (!tty) - return 0; - - if (!tty->port.count) - return 0; - - return tty->tx_bytes_queued; -} - -static int get_control_lines(struct ipw_tty *tty) -{ - unsigned int my = tty->control_lines; - unsigned int out = 0; - - if (my & IPW_CONTROL_LINE_RTS) - out |= TIOCM_RTS; - if (my & IPW_CONTROL_LINE_DTR) - out |= TIOCM_DTR; - if (my & IPW_CONTROL_LINE_CTS) - out |= TIOCM_CTS; - if (my & IPW_CONTROL_LINE_DSR) - out |= TIOCM_DSR; - if (my & IPW_CONTROL_LINE_DCD) - out |= TIOCM_CD; - - return out; -} - -static int set_control_lines(struct ipw_tty *tty, unsigned int set, - unsigned int clear) -{ - int ret; - - if (set & TIOCM_RTS) { - ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1); - if (ret) - return ret; - if (tty->secondary_channel_idx != -1) { - ret = ipwireless_set_RTS(tty->hardware, - tty->secondary_channel_idx, 1); - if (ret) - return ret; - } - } - if (set & TIOCM_DTR) { - ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1); - if (ret) - return ret; - if (tty->secondary_channel_idx != -1) { - ret = ipwireless_set_DTR(tty->hardware, - tty->secondary_channel_idx, 1); - if (ret) - return ret; - } - } - if (clear & TIOCM_RTS) { - ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0); - if (tty->secondary_channel_idx != -1) { - ret = ipwireless_set_RTS(tty->hardware, - tty->secondary_channel_idx, 0); - if (ret) - return ret; - } - } - if (clear & TIOCM_DTR) { - ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0); - if (tty->secondary_channel_idx != -1) { - ret = ipwireless_set_DTR(tty->hardware, - tty->secondary_channel_idx, 0); - if (ret) - return ret; - } - } - return 0; -} - -static int ipw_tiocmget(struct tty_struct *linux_tty) -{ - struct ipw_tty *tty = linux_tty->driver_data; - /* FIXME: Exactly how is the tty object locked here .. */ - - if (!tty) - return -ENODEV; - - if (!tty->port.count) - return -EINVAL; - - return get_control_lines(tty); -} - -static int -ipw_tiocmset(struct tty_struct *linux_tty, - unsigned int set, unsigned int clear) -{ - struct ipw_tty *tty = linux_tty->driver_data; - /* FIXME: Exactly how is the tty object locked here .. */ - - if (!tty) - return -ENODEV; - - if (!tty->port.count) - return -EINVAL; - - return set_control_lines(tty, set, clear); -} - -static int ipw_ioctl(struct tty_struct *linux_tty, - unsigned int cmd, unsigned long arg) -{ - struct ipw_tty *tty = linux_tty->driver_data; - - if (!tty) - return -ENODEV; - - if (!tty->port.count) - return -EINVAL; - - /* FIXME: Exactly how is the tty object locked here .. */ - if (tty->tty_type == TTYTYPE_MODEM) { - switch (cmd) { - case PPPIOCGCHAN: - { - int chan = ipwireless_ppp_channel_index( - tty->network); - - if (chan < 0) - return -ENODEV; - if (put_user(chan, (int __user *) arg)) - return -EFAULT; - } - return 0; - - case PPPIOCGUNIT: - { - int unit = ipwireless_ppp_unit_number( - tty->network); - - if (unit < 0) - return -ENODEV; - if (put_user(unit, (int __user *) arg)) - return -EFAULT; - } - return 0; - - case FIONREAD: - { - int val = 0; - - if (put_user(val, (int __user *) arg)) - return -EFAULT; - } - return 0; - case TCFLSH: - return tty_perform_flush(linux_tty, arg); - } - } - return -ENOIOCTLCMD; -} - -static int add_tty(int j, - struct ipw_hardware *hardware, - struct ipw_network *network, int channel_idx, - int secondary_channel_idx, int tty_type) -{ - ttys[j] = kzalloc_obj(struct ipw_tty); - if (!ttys[j]) - return -ENOMEM; - ttys[j]->index = j; - ttys[j]->hardware = hardware; - ttys[j]->channel_idx = channel_idx; - ttys[j]->secondary_channel_idx = secondary_channel_idx; - ttys[j]->network = network; - ttys[j]->tty_type = tty_type; - mutex_init(&ttys[j]->ipw_tty_mutex); - tty_port_init(&ttys[j]->port); - - tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL); - ipwireless_associate_network_tty(network, channel_idx, ttys[j]); - - if (secondary_channel_idx != -1) - ipwireless_associate_network_tty(network, - secondary_channel_idx, - ttys[j]); - /* check if we provide raw device (if loopback is enabled) */ - if (get_tty(j)) - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": registering %s device ttyIPWp%d\n", - tty_type_name(tty_type), j); - - return 0; -} - -struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware, - struct ipw_network *network) -{ - int i, j; - - for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) { - int allfree = 1; - - for (j = i; j < IPWIRELESS_PCMCIA_MINORS; - j += IPWIRELESS_PCMCIA_MINOR_RANGE) - if (ttys[j] != NULL) { - allfree = 0; - break; - } - - if (allfree) { - j = i; - - if (add_tty(j, hardware, network, - IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS, - TTYTYPE_MODEM)) - return NULL; - - j += IPWIRELESS_PCMCIA_MINOR_RANGE; - if (add_tty(j, hardware, network, - IPW_CHANNEL_DIALLER, -1, - TTYTYPE_MONITOR)) - return NULL; - - j += IPWIRELESS_PCMCIA_MINOR_RANGE; - if (add_tty(j, hardware, network, - IPW_CHANNEL_RAS, -1, - TTYTYPE_RAS_RAW)) - return NULL; - - return ttys[i]; - } - } - return NULL; -} - -/* - * Must be called before ipwireless_network_free(). - */ -void ipwireless_tty_free(struct ipw_tty *tty) -{ - int j; - struct ipw_network *network = ttys[tty->index]->network; - - for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS; - j += IPWIRELESS_PCMCIA_MINOR_RANGE) { - struct ipw_tty *ttyj = ttys[j]; - - if (ttyj) { - mutex_lock(&ttyj->ipw_tty_mutex); - if (get_tty(j)) - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": deregistering %s device ttyIPWp%d\n", - tty_type_name(ttyj->tty_type), j); - if (ttyj->port.tty != NULL) { - mutex_unlock(&ttyj->ipw_tty_mutex); - tty_vhangup(ttyj->port.tty); - /* FIXME: Exactly how is the tty object locked here - against a parallel ioctl etc */ - /* FIXME2: hangup does not mean all processes - * are gone */ - mutex_lock(&ttyj->ipw_tty_mutex); - } - while (ttyj->port.count) - do_ipw_close(ttyj); - ipwireless_disassociate_network_ttys(network, - ttyj->channel_idx); - tty_unregister_device(ipw_tty_driver, j); - tty_port_destroy(&ttyj->port); - ttys[j] = NULL; - mutex_unlock(&ttyj->ipw_tty_mutex); - kfree(ttyj); - } - } -} - -static const struct tty_operations tty_ops = { - .open = ipw_open, - .close = ipw_close, - .hangup = ipw_hangup, - .write = ipw_write, - .write_room = ipw_write_room, - .ioctl = ipw_ioctl, - .chars_in_buffer = ipw_chars_in_buffer, - .tiocmget = ipw_tiocmget, - .tiocmset = ipw_tiocmset, - .set_serial = ipwireless_set_serial_info, - .get_serial = ipwireless_get_serial_info, -}; - -int ipwireless_tty_init(void) -{ - int result; - - ipw_tty_driver = tty_alloc_driver(IPWIRELESS_PCMCIA_MINORS, - TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); - if (IS_ERR(ipw_tty_driver)) - return PTR_ERR(ipw_tty_driver); - - ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME; - ipw_tty_driver->name = "ttyIPWp"; - ipw_tty_driver->major = 0; - ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START; - ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL; - ipw_tty_driver->init_termios = tty_std_termios; - ipw_tty_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - ipw_tty_driver->init_termios.c_ispeed = 9600; - ipw_tty_driver->init_termios.c_ospeed = 9600; - tty_set_operations(ipw_tty_driver, &tty_ops); - result = tty_register_driver(ipw_tty_driver); - if (result) { - printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": failed to register tty driver\n"); - tty_driver_kref_put(ipw_tty_driver); - return result; - } - - return 0; -} - -void ipwireless_tty_release(void) -{ - tty_unregister_driver(ipw_tty_driver); - tty_driver_kref_put(ipw_tty_driver); -} - -int ipwireless_tty_is_modem(struct ipw_tty *tty) -{ - return tty->tty_type == TTYTYPE_MODEM; -} - -void -ipwireless_tty_notify_control_line_change(struct ipw_tty *tty, - unsigned int channel_idx, - unsigned int control_lines, - unsigned int changed_mask) -{ - unsigned int old_control_lines = tty->control_lines; - - tty->control_lines = (tty->control_lines & ~changed_mask) - | (control_lines & changed_mask); - - /* - * If DCD is de-asserted, we close the tty so pppd can tell that we - * have gone offline. - */ - if ((old_control_lines & IPW_CONTROL_LINE_DCD) - && !(tty->control_lines & IPW_CONTROL_LINE_DCD) - && tty->port.tty) { - tty_hangup(tty->port.tty); - } -} - diff --git a/drivers/tty/ipwireless/tty.h b/drivers/tty/ipwireless/tty.h deleted file mode 100644 index ec698d9f338b..000000000000 --- a/drivers/tty/ipwireless/tty.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * IPWireless 3G PCMCIA Network Driver - * - * Original code - * by Stephen Blackheath , - * Ben Martel - * - * Copyrighted as follows: - * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) - * - * Various driver changes and rewrites, port to new kernels - * Copyright (C) 2006-2007 Jiri Kosina - * - * Misc code cleanups and updates - * Copyright (C) 2007 David Sterba - */ - -#ifndef _IPWIRELESS_CS_TTY_H_ -#define _IPWIRELESS_CS_TTY_H_ - -#include -#include - -#include -#include - -struct ipw_tty; -struct ipw_network; -struct ipw_hardware; - -int ipwireless_tty_init(void); -void ipwireless_tty_release(void); - -struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hw, - struct ipw_network *net); -void ipwireless_tty_free(struct ipw_tty *tty); -void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, - unsigned int length); -int ipwireless_tty_is_modem(struct ipw_tty *tty); -void ipwireless_tty_notify_control_line_change(struct ipw_tty *tty, - unsigned int channel_idx, - unsigned int control_lines, - unsigned int changed_mask); - -#endif diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index e9d044a331b0..7500efcdfc21 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -651,11 +651,11 @@ static int acpi_serdev_do_lookup(struct acpi_device *adev, INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, acpi_serdev_parse_resource, lookup); - acpi_dev_free_resource_list(&resource_list); - if (ret < 0) return -EINVAL; + acpi_dev_free_resource_list(&resource_list); + return 0; } diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 77fe0588fd6b..9337fec9394e 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -177,7 +177,9 @@ static unsigned int __maybe_unused serial_icr_read(struct uart_8250_port *up, void serial8250_clear_fifos(struct uart_8250_port *p); void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p); -void serial8250_fifo_wait_for_lsr_thre(struct uart_8250_port *up, unsigned int count); +void serial8250_fifo_wait_for_lsr_thre(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + unsigned int count); void serial8250_rpm_get(struct uart_8250_port *p); void serial8250_rpm_put(struct uart_8250_port *p); @@ -334,6 +336,13 @@ int fintek_8250_probe(struct uart_8250_port *uart); static inline int fintek_8250_probe(struct uart_8250_port *uart) { return 0; } #endif +#if IS_REACHABLE(CONFIG_SERIAL_8250_HUB6) +bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2); +#else +static inline bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2) +{ return false; } +#endif + #ifdef CONFIG_ARCH_OMAP1 #include static inline int is_omap1_8250(struct uart_8250_port *pt) diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 742004d63c6f..cb7d594d263e 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -1099,10 +1099,8 @@ static int brcmuart_probe(struct platform_device *pdev) } ret = devm_request_irq(dev, dma_irq, brcmuart_isr, IRQF_SHARED, "uart DMA irq", &new_port->port); - if (ret) { - dev_err_probe(dev, ret, "unable to register IRQ handler\n"); + if (ret) goto err1; - } } platform_set_drvdata(pdev, priv); brcmuart_init_debugfs(priv, dev_name(&pdev->dev)); diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index f49862d90eeb..b875d394796f 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -390,12 +390,34 @@ void __init serial8250_register_ports(struct uart_driver *drv, struct device *de #ifdef CONFIG_SERIAL_8250_CONSOLE -static void univ8250_console_write(struct console *co, const char *s, - unsigned int count) +static void univ8250_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) { struct uart_8250_port *up = &serial8250_ports[co->index]; - serial8250_console_write(up, s, count); + serial8250_console_write(up, wctxt, true); +} + +static void univ8250_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + + serial8250_console_write(up, wctxt, false); +} + +static void univ8250_console_device_lock(struct console *co, unsigned long *flags) +{ + struct uart_port *up = &serial8250_ports[co->index].port; + + __uart_port_lock_irqsave(up, flags); +} + +static void univ8250_console_device_unlock(struct console *co, unsigned long flags) +{ + struct uart_port *up = &serial8250_ports[co->index].port; + + __uart_port_unlock_irqrestore(up, flags); } static int univ8250_console_setup(struct console *co, char *options) @@ -496,12 +518,15 @@ static int univ8250_console_match(struct console *co, char *name, int idx, static struct console univ8250_console = { .name = "ttyS", - .write = univ8250_console_write, + .write_atomic = univ8250_console_write_atomic, + .write_thread = univ8250_console_write_thread, + .device_lock = univ8250_console_device_lock, + .device_unlock = univ8250_console_device_unlock, .device = uart_console_device, .setup = univ8250_console_setup, .exit = univ8250_console_exit, .match = univ8250_console_match, - .flags = CON_PRINTBUFFER | CON_ANYTIME, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON, .index = -1, .data = &serial8250_reg, }; @@ -584,13 +609,19 @@ void serial8250_suspend_port(int line) struct uart_8250_port *up = &serial8250_ports[line]; struct uart_port *port = &up->port; - if (!console_suspend_enabled && uart_console(port) && - port->type != PORT_8250) { - unsigned char canary = 0xa5; + if (uart_console(port)) { + /* No irq_work may be queued when suspending */ + scoped_guard(uart_port_lock_irq, port) + up->console_msr_work_allow = false; + irq_work_sync(&up->console_msr_work); - serial_out(up, UART_SCR, canary); - if (serial_in(up, UART_SCR) == canary) - up->canary = canary; + if (!console_suspend_enabled && port->type != PORT_8250) { + unsigned char canary = 0xa5; + + serial_out(up, UART_SCR, canary); + if (serial_in(up, UART_SCR) == canary) + up->canary = canary; + } } uart_suspend_port(&serial8250_reg, port); @@ -620,6 +651,18 @@ void serial8250_resume_port(int line) port->uartclk = 921600*16; } uart_resume_port(&serial8250_reg, port); + + if (uart_console(port)) { + + guard(uart_port_lock_irq)(port); + + /* irq_work allowed again */ + up->console_msr_work_allow = true; + + /* Handle any pending MSR changes */ + if (up->msr_saved_flags) + serial8250_modem_status(up); + } } EXPORT_SYMBOL(serial8250_resume_port); @@ -802,10 +845,16 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) uart->port.get_divisor = up->port.get_divisor; if (up->port.set_divisor) uart->port.set_divisor = up->port.set_divisor; + if (up->port.get_rxtrig) + uart->port.get_rxtrig = up->port.get_rxtrig; + if (up->port.set_rxtrig) + uart->port.set_rxtrig = up->port.set_rxtrig; if (up->port.startup) uart->port.startup = up->port.startup; if (up->port.shutdown) uart->port.shutdown = up->port.shutdown; + if (up->port.break_ctl) + uart->port.break_ctl = up->port.break_ctl; if (up->port.pm) uart->port.pm = up->port.pm; if (up->port.handle_break) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 5fba913f3301..51d026f20825 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -156,7 +156,7 @@ static int dw8250_idle_enter(struct uart_port *p) * * FIXME: frame_time delay is too long with very low baudrates. */ - serial8250_fifo_wait_for_lsr_thre(up, p->fifosize); + serial8250_fifo_wait_for_lsr_thre(up, NULL, p->fifosize); ndelay(p->frame_time); serial_port_out(p, UART_MCR, up->mcr | UART_MCR_LOOP); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index dc0371857ecb..44ec209f37c4 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -23,6 +23,7 @@ * console=uart8250,mmio32,0xff5e0000,115200n8 */ +#include #include #include #include @@ -177,6 +178,23 @@ OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup); OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup); +static int __init early_serial8250_xscale_setup(struct earlycon_device *device, + const char *options) +{ + /* + * Adjust for BE32 register accesses: drop any hardcoded + * address for the big endian byte target, add it explicitly + * if running on BE32. + */ + device->port.membase = PTR_ALIGN_DOWN(device->port.membase, 4); + if (IS_ENABLED(CONFIG_CPU_ENDIAN_BE32)) + device->port.membase += 3; + device->port.regshift = 2; + + return early_serial8250_setup(device, options); +} +OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_xscale_setup); + static int __init early_serial8250_rs2_setup(struct earlycon_device *device, const char *options) { @@ -184,7 +202,6 @@ static int __init early_serial8250_rs2_setup(struct earlycon_device *device, return early_serial8250_setup(device, options); } -OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_rs2_setup); OF_EARLYCON_DECLARE(uart, "mrvl,mmp-uart", early_serial8250_rs2_setup); OF_EARLYCON_DECLARE(uart, "mrvl,pxa-uart", early_serial8250_rs2_setup); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index c682c0d0dffa..836792e861bf 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -1140,22 +1140,15 @@ static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) static struct platform_device *__xr17v35x_register_gpio(struct pci_dev *pcidev, const struct software_node *node) { - struct platform_device *pdev; + struct platform_device_info pdevinfo = { + .name = "gpio_exar", + .id = PLATFORM_DEVID_AUTO, + .parent = &pcidev->dev, + .fwnode = dev_fwnode(&pcidev->dev), + .swnode = node, + }; - pdev = platform_device_alloc("gpio_exar", PLATFORM_DEVID_AUTO); - if (!pdev) - return NULL; - - pdev->dev.parent = &pcidev->dev; - device_set_node(&pdev->dev, dev_fwnode(&pcidev->dev)); - - if (device_add_software_node(&pdev->dev, node) < 0 || - platform_device_add(pdev) < 0) { - platform_device_put(pdev); - return NULL; - } - - return pdev; + return platform_device_register_full(&pdevinfo); } static void __xr17v35x_unregister_gpio(struct platform_device *pdev) @@ -1642,14 +1635,14 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { .exit = pci_xr17v35x_exit, }; -#define CTI_EXAR_DEVICE(devid, bd) { \ - PCI_DEVICE_SUB( \ - PCI_VENDOR_ID_EXAR, \ - PCI_DEVICE_ID_EXAR_##devid, \ - PCI_SUBVENDOR_ID_CONNECT_TECH, \ - PCI_ANY_ID), 0, 0, \ - (kernel_ulong_t)&bd \ - } +#define CTI_EXAR_DEVICE(devid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_SUBVENDOR_ID_CONNECT_TECH, \ + PCI_ANY_ID), \ + .driver_data = (kernel_ulong_t)&bd \ +} #define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } @@ -1658,18 +1651,18 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { PCI_VENDOR_ID_EXAR, \ PCI_DEVICE_ID_EXAR_##devid, \ PCI_SUBVENDOR_ID_IBM, \ - PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \ - (kernel_ulong_t)&bd \ - } + PCI_SUBDEVICE_ID_IBM_##sdevid), \ + .driver_data = (kernel_ulong_t)&bd \ +} #define USR_DEVICE(devid, sdevid, bd) { \ PCI_DEVICE_SUB( \ PCI_VENDOR_ID_USR, \ PCI_DEVICE_ID_EXAR_##devid, \ PCI_VENDOR_ID_EXAR, \ - PCI_SUBDEVICE_ID_USR_##sdevid), 0, 0, \ - (kernel_ulong_t)&bd \ - } + PCI_SUBDEVICE_ID_USR_##sdevid), \ + .driver_data = (kernel_ulong_t)&bd \ +} static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(ACCESSIO, COM_2S, pbn_exar_XR17C15x), @@ -1726,7 +1719,7 @@ static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(COMMTECH, 4224PCI335, pbn_fastcom335_4), EXAR_DEVICE(COMMTECH, 2324PCI335, pbn_fastcom335_4), EXAR_DEVICE(COMMTECH, 2328PCI335, pbn_fastcom335_8), - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, exar_pci_tbl); diff --git a/drivers/tty/serial/8250/8250_hub6.c b/drivers/tty/serial/8250/8250_hub6.c index 273f59b9bca5..b6767633c966 100644 --- a/drivers/tty/serial/8250/8250_hub6.c +++ b/drivers/tty/serial/8250/8250_hub6.c @@ -7,6 +7,8 @@ #include #include +#include "8250.h" + #define HUB6(card, port) \ { \ .iobase = 0x302, \ @@ -41,6 +43,12 @@ static struct platform_device hub6_device = { }, }; +bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2) +{ + return port1->iobase == port2->iobase && port1->hub6 == port2->hub6; +} +EXPORT_SYMBOL_GPL(hub6_match_port); + static int __init hub6_init(void) { return platform_device_register(&hub6_device); diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index e6a56cf54ae0..841d64f6a707 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -521,7 +522,7 @@ static int mtk8250_probe(struct platform_device *pdev) struct uart_8250_port uart = {}; struct mtk8250_data *data; struct resource *regs; - int irq, err; + int irq, err, line; struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); irq = platform_get_irq(pdev, 0); @@ -575,6 +576,10 @@ static int mtk8250_probe(struct platform_device *pdev) #endif if (is_of_node(fwnode)) { + line = of_alias_get_id(pdev->dev.of_node, "serial"); + if (line >= 0) + uart.port.line = line; + /* Disable Rate Fix function */ writel(0x0, uart.port.membase + (MTK_UART_RATE_FIX << uart.port.regshift)); diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c new file mode 100644 index 000000000000..ce873fbd62e9 --- /dev/null +++ b/drivers/tty/serial/8250/8250_mxpcie.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Moxa PCIe multiport serial device driver + * + * Copyright (C) 2025 Moxa Inc. (support@moxa.com) + * Author: Crescent Hsieh + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "8250.h" + +#define PCI_DEVICE_ID_MOXA_CP102E 0x1024 +#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025 +#define PCI_DEVICE_ID_MOXA_CP102N 0x1027 +#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045 +#define PCI_DEVICE_ID_MOXA_CP104N 0x1046 +#define PCI_DEVICE_ID_MOXA_CP112N 0x1121 +#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144 +#define PCI_DEVICE_ID_MOXA_CP114N 0x1145 +#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160 +#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161 +#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182 +#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183 +#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322 +#define PCI_DEVICE_ID_MOXA_CP132N 0x1323 +#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342 +#define PCI_DEVICE_ID_MOXA_CP134N 0x1343 +#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381 +#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683 + +/* Bits in PCI device ID encoding board capabilities */ +#define MOXA_DEV_ID_IFACE_MASK GENMASK(11, 8) /* Supported serial interface */ +#define MOXA_DEV_ID_NPORTS_MASK GENMASK(7, 4) /* Number of UART ports */ + +/* UART */ +#define MOXA_PUART_BASE_BAUD 921600 +#define MOXA_PUART_OFFSET 0x200 +#define MOXA_PUART_TX_TRIG_DEFAULT 0 +#define MOXA_PUART_RX_TRIG_DEFAULT 96 +#define MOXA_PUART_RX_FLOW_LOW_DEFAULT 16 +#define MOXA_PUART_RX_FLOW_HIGH_DEFAULT 110 + +/* Special Function Register (SFR) */ +#define MOXA_PUART_SFR 0x07 +#define MOXA_PUART_SFR_FORCE_TX BIT(0) +#define MOXA_PUART_SFR_950 BIT(5) + +/* Enhanced Function Register (EFR) */ +#define MOXA_PUART_EFR 0x0A +#define MOXA_PUART_EFR_ENHANCED BIT(4) +#define MOXA_PUART_EFR_AUTO_RTS BIT(6) +#define MOXA_PUART_EFR_AUTO_CTS BIT(7) +#define MOXA_PUART_EFR_RX_FLOW_MASK GENMASK(1, 0) +#define MOXA_PUART_EFR_RX_FLOW_DISABLED 0x0 +#define MOXA_PUART_EFR_RX_FLOW_XON2_XOFF2 0x1 +#define MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1 0x2 +#define MOXA_PUART_EFR_RX_FLOW_COPY_TX 0x3 +#define MOXA_PUART_EFR_TX_FLOW_MASK GENMASK(3, 2) +#define MOXA_PUART_EFR_TX_FLOW_DISABLED 0x0 +#define MOXA_PUART_EFR_TX_FLOW_XON2_XOFF2 0x1 +#define MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1 0x2 +#define MOXA_PUART_EFR_TX_FLOW_RESERVED 0x3 + +#define MOXA_PUART_XON1 0x0B +#define MOXA_PUART_XON2 0x0C +#define MOXA_PUART_XOFF1 0x0D +#define MOXA_PUART_XOFF2 0x0E +#define MOXA_PUART_TTL 0x10 /* Tx Interrupt Trigger Level */ +#define MOXA_PUART_RTL 0x11 /* Rx Interrupt Trigger Level */ +#define MOXA_PUART_FCL 0x12 /* Flow Control Low Trigger Level */ +#define MOXA_PUART_FCH 0x13 /* Flow Control High Trigger Level */ +#define MOXA_PUART_RX_FIFO_CNT 0x15 /* Rx FIFO Data Counter */ +#define MOXA_PUART_TX_FIFO_CNT 0x16 /* Tx FIFO Data Counter */ + +#define MOXA_PUART_RX_FIFO_MEM 0x100 /* Memory Space to Rx FIFO Data Register */ +#define MOXA_PUART_TX_FIFO_MEM 0x100 /* Memory Space to Tx FIFO Data Register */ + +#define MOXA_GPIO_DIRECTION 0x09 +#define MOXA_GPIO_OUTPUT 0x0A + +#define MOXA_GPIO_PIN2 BIT(2) + +#define MOXA_UIR_OFFSET 0x04 +#define MOXA_UIR_RS232 0x00 +#define MOXA_UIR_RS422 0x01 +#define MOXA_UIR_RS485_4W 0x0B +#define MOXA_UIR_RS485_2W 0x0F + +#define MOXA_EVEN_RS_MASK GENMASK(3, 0) +#define MOXA_ODD_RS_MASK GENMASK(7, 4) + +struct mxpcie8250_port { + int line; + u8 rx_trig_level; +}; + +struct mxpcie8250 { + unsigned int supp_rs; + unsigned int num_ports; + void __iomem *bar1_base; /* UART registers (MMIO) */ + void __iomem *bar2_base; /* UIR / GPIO / CPLD (IO) */ + struct mxpcie8250_port port[] __counted_by(num_ports); +}; + +enum { + MOXA_SUPP_RS232 = BIT(0), + MOXA_SUPP_RS422 = BIT(1), + MOXA_SUPP_RS485 = BIT(2), +}; + +static const struct serial_rs485 mxpcie8250_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX | SER_RS485_MODE_RS422, +}; + +static bool mxpcie8250_is_mini_pcie(unsigned short device) +{ + if (device == PCI_DEVICE_ID_MOXA_CP102N || + device == PCI_DEVICE_ID_MOXA_CP104N || + device == PCI_DEVICE_ID_MOXA_CP112N || + device == PCI_DEVICE_ID_MOXA_CP114N || + device == PCI_DEVICE_ID_MOXA_CP132N || + device == PCI_DEVICE_ID_MOXA_CP134N) + return true; + + return false; +} + +static unsigned int mxpcie8250_get_supp_rs(unsigned short device) +{ + switch (device & MOXA_DEV_ID_IFACE_MASK) { + case 0x0000: + case 0x0600: + return MOXA_SUPP_RS232; + case 0x0100: + return MOXA_SUPP_RS232 | MOXA_SUPP_RS422 | MOXA_SUPP_RS485; + case 0x0300: + return MOXA_SUPP_RS422 | MOXA_SUPP_RS485; + default: + return 0; + } +} + +static unsigned short mxpcie8250_get_nports(unsigned short device) +{ + switch (device) { + case PCI_DEVICE_ID_MOXA_CP116E_A_A: + case PCI_DEVICE_ID_MOXA_CP116E_A_B: + return 8; + default: + return FIELD_GET(MOXA_DEV_ID_NPORTS_MASK, device); + } +} + +static void mxpcie8250_set_interface(struct mxpcie8250 *priv, + unsigned int port_idx, + u8 mode) +{ + void __iomem *uir_addr = priv->bar2_base + MOXA_UIR_OFFSET + port_idx / 2; + u8 cval; + + cval = ioread8(uir_addr); + + if (port_idx % 2) + FIELD_MODIFY(MOXA_ODD_RS_MASK, &cval, mode); + else + FIELD_MODIFY(MOXA_EVEN_RS_MASK, &cval, mode); + + iowrite8(cval, uir_addr); +} + +/* + * Moxa PCIe multiport serial boards support switching serial interfaces + * via the ioctl() command "TIOCSRS485". Supported modes and corresponding + * flags in "serial_rs485": + * + * RS232 = (no flags set) + * RS422 = SER_RS485_ENABLED | SER_RS485_MODE_RS422 + * RS485_2W (half-duplex) = SER_RS485_ENABLED + * RS485_4W (full-duplex) = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX + */ +static int mxpcie8250_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct mxpcie8250 *priv = dev_get_drvdata(port->dev); + u8 mode = MOXA_UIR_RS232; + + if (rs485->flags & SER_RS485_ENABLED) { + if (rs485->flags & SER_RS485_MODE_RS422) + mode = MOXA_UIR_RS422; + else if (rs485->flags & SER_RS485_RX_DURING_TX) + mode = MOXA_UIR_RS485_4W; + else + mode = MOXA_UIR_RS485_2W; + } else if (!(priv->supp_rs & MOXA_SUPP_RS232)) { + return -ENODEV; + } + mxpcie8250_set_interface(priv, port->port_id, mode); + + return 0; +} + +static void mxpcie8250_set_termios(struct uart_port *port, + struct ktermios *new, + const struct ktermios *old) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct tty_struct *tty = port->state->port.tty; + unsigned int cflag = tty->termios.c_cflag; + u8 efr, val; + + serial8250_do_set_termios(port, new, old); + + up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS | UPSTAT_AUTOXOFF); + + efr = serial_in(up, MOXA_PUART_EFR); + efr &= ~(MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS); + + if (cflag & CRTSCTS) { + efr |= (MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS); + up->port.status |= (UPSTAT_AUTORTS | UPSTAT_AUTOCTS); + } + /* Set on-chip software flow control character */ + serial_out(up, MOXA_PUART_XON1, START_CHAR(tty)); + serial_out(up, MOXA_PUART_XON2, START_CHAR(tty)); + serial_out(up, MOXA_PUART_XOFF1, STOP_CHAR(tty)); + serial_out(up, MOXA_PUART_XOFF2, STOP_CHAR(tty)); + + val = I_IXON(tty) ? MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_RX_FLOW_DISABLED; + FIELD_MODIFY(MOXA_PUART_EFR_RX_FLOW_MASK, &efr, val); + + val = I_IXOFF(tty) ? MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_TX_FLOW_DISABLED; + FIELD_MODIFY(MOXA_PUART_EFR_TX_FLOW_MASK, &efr, val); + + if (I_IXOFF(tty)) + up->port.status |= UPSTAT_AUTOXOFF; + + serial_out(up, MOXA_PUART_EFR, efr); +} + +static int mxpcie8250_get_rxtrig(struct uart_port *port) +{ + return serial_port_in(port, MOXA_PUART_RTL); +} + +static int mxpcie8250_set_rxtrig(struct uart_port *port, unsigned char bytes) +{ + struct mxpcie8250 *priv = dev_get_drvdata(port->dev); + + if (bytes > port->fifosize) + return -EINVAL; + + serial_port_out(port, MOXA_PUART_RTL, bytes); + priv->port[port->port_id].rx_trig_level = bytes; + + return 0; +} + +static int mxpcie8250_startup(struct uart_port *port) +{ + struct mxpcie8250 *priv = dev_get_drvdata(port->dev); + struct uart_8250_port *up = up_to_u8250p(port); + int ret; + + ret = serial8250_do_startup(port); + if (ret) + return ret; + + /* + * The TX FIFO write pointer (w_ptr) and read pointer (r_ptr) + * are driven by different clocks: w_ptr uses the PCIe clock + * and r_ptr uses the UART clock. When TX FIFO flush is requested, + * w_ptr may be cleared before r_ptr, so the UART can still observe + * pending TX data. + * + * It is recommended to clear the FIFOs at least 5 times to ensure + * both pointers are reset. + */ + for (unsigned int i = 0; i < 5; ++i) + serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + + serial_out(up, MOXA_PUART_EFR, MOXA_PUART_EFR_ENHANCED); + serial_out(up, MOXA_PUART_SFR, MOXA_PUART_SFR_950); + + serial_out(up, MOXA_PUART_TTL, MOXA_PUART_TX_TRIG_DEFAULT); + serial_out(up, MOXA_PUART_RTL, priv->port[port->port_id].rx_trig_level); + serial_out(up, MOXA_PUART_FCL, MOXA_PUART_RX_FLOW_LOW_DEFAULT); + serial_out(up, MOXA_PUART_FCH, MOXA_PUART_RX_FLOW_HIGH_DEFAULT); + + return 0; +} + +static void mxpcie8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + serial_out(up, MOXA_PUART_EFR, 0); + serial_out(up, MOXA_PUART_SFR, 0); + + serial8250_do_shutdown(port); +} + +static void mxpcie8250_throttle(struct uart_port *port) +{ + guard(uart_port_lock_irqsave)(port); + + port->ops->stop_rx(port); +} + +static void mxpcie8250_unthrottle(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + guard(uart_port_lock_irqsave)(port); + + up->ier |= UART_IER_RLSI | UART_IER_RDI; + port->read_status_mask |= UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void mxpcie8250_rx_chars(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + unsigned int count; + u8 *buf; + + count = serial_in(up, MOXA_PUART_RX_FIFO_CNT); + count = min(count, port->fifosize); + count = tty_prepare_flip_string(tport, &buf, count); + if (!count) + return; + + for (unsigned int i = 0; i < count; ++i) + buf[i] = serial_in(up, MOXA_PUART_RX_FIFO_MEM + i); + + port->icount.rx += count; + tty_flip_buffer_push(tport); +} + +static bool mxpcie8250_should_rx(struct uart_8250_port *up, u16 lsr) +{ + struct uart_port *port = &up->port; + + if (!(lsr & (UART_LSR_DR | UART_LSR_BI))) + return false; + + if (!(port->status & (UPSTAT_AUTOCTS | UPSTAT_AUTORTS))) + return true; + if (lsr & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) + return true; + if (port->read_status_mask & UART_LSR_DR) + return true; + + return false; +} + +static void mxpcie8250_tx_chars(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + unsigned int offset = 0; + unsigned char c; + + uart_port_tx_limited(port, c, port->fifosize - serial_in(up, MOXA_PUART_TX_FIFO_CNT), + true, + serial_out(up, MOXA_PUART_TX_FIFO_MEM + offset++, c), + ({})); +} + +static int mxpcie8250_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u16 lsr; + u8 iir; + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return 0; + + guard(uart_port_lock_check_sysrq_irqsave)(port); + + lsr = serial_lsr_in(up); + if (mxpcie8250_should_rx(up, lsr)) { + if (!(lsr & UART_LSR_BRK_ERROR_BITS)) + mxpcie8250_rx_chars(up); + else + lsr = serial8250_rx_chars(up, lsr); + } + + serial8250_modem_status(up); + + if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) + mxpcie8250_tx_chars(up); + + return 1; +} + +static void mxpcie8250_software_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct tty_struct *tty = port->state->port.tty; + unsigned int baud, quot; + u8 sfr, tx_byte = 0x01; + + guard(uart_port_lock_irqsave)(port); + + if (break_state == -1) { + serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB); + serial_dl_write(up, 0); + serial_out(up, UART_LCR, up->lcr); + + serial_out(up, MOXA_PUART_TX_FIFO_MEM, tx_byte); + + sfr = serial_in(up, MOXA_PUART_SFR); + serial_out(up, MOXA_PUART_SFR, sfr | MOXA_PUART_SFR_FORCE_TX); + + up->lcr |= UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + } else { + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + + sfr = serial_in(up, MOXA_PUART_SFR); + serial_out(up, MOXA_PUART_SFR, sfr & ~MOXA_PUART_SFR_FORCE_TX); + + serial_out(up, UART_FCR, UART_FCR_CLEAR_XMIT); + + baud = tty_get_baud_rate(tty); + quot = uart_get_divisor(port, baud); + serial8250_do_set_divisor(port, baud, quot); + serial_out(up, UART_LCR, up->lcr); + } +} + +static void mxpcie8250_break_ctl(struct uart_port *port, int break_state) +{ + if (port->rs485.flags & SER_RS485_ENABLED && + !(port->rs485.flags & SER_RS485_MODE_RS422)) + mxpcie8250_software_break_ctl(port, break_state); + else + serial8250_do_break_ctl(port, break_state); +} + +static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv) +{ + void __iomem *bar2_base = priv->bar2_base; + unsigned short device = pdev->device; + u8 cval; + + /* Initial terminator */ + if (device == PCI_DEVICE_ID_MOXA_CP114EL || + device == PCI_DEVICE_ID_MOXA_CP118EL_A) { + iowrite8(0xff, bar2_base + MOXA_GPIO_DIRECTION); + iowrite8(0x00, bar2_base + MOXA_GPIO_OUTPUT); + } + /* + * Enable hardware buffer to prevent break signal output when system boots up. + * This hardware buffer is only supported on Mini PCIe series. + */ + if (mxpcie8250_is_mini_pcie(device)) { + /* Set GPIO direction */ + cval = ioread8(bar2_base + MOXA_GPIO_DIRECTION); + cval |= MOXA_GPIO_PIN2; + iowrite8(cval, bar2_base + MOXA_GPIO_DIRECTION); + /* Enable low GPIO */ + cval = ioread8(bar2_base + MOXA_GPIO_OUTPUT); + cval &= ~MOXA_GPIO_PIN2; + iowrite8(cval, bar2_base + MOXA_GPIO_OUTPUT); + } +} + +static void mxpcie8250_setup_port(struct pci_dev *pdev, + struct mxpcie8250 *priv, + struct uart_8250_port *up, + int idx) +{ + unsigned short device = pdev->device; + int offset = idx * MOXA_PUART_OFFSET; + u8 init_mode = MOXA_UIR_RS232; + + if (priv->supp_rs & MOXA_SUPP_RS485) { + up->port.rs485_config = mxpcie8250_rs485_config; + up->port.rs485_supported = mxpcie8250_rs485_supported; + } + if (!(priv->supp_rs & MOXA_SUPP_RS232)) { + init_mode = MOXA_UIR_RS422; + up->port.rs485.flags = SER_RS485_ENABLED | SER_RS485_MODE_RS422; + } + mxpcie8250_set_interface(priv, idx, init_mode); + + if (idx == 3 && + (device == PCI_DEVICE_ID_MOXA_CP104EL_A || + device == PCI_DEVICE_ID_MOXA_CP114EL || + device == PCI_DEVICE_ID_MOXA_CP134EL_A)) + offset = 7 * MOXA_PUART_OFFSET; + + up->port.mapbase = pci_resource_start(pdev, FL_BASE1) + offset; + up->port.membase = pcim_iomap_table(pdev)[FL_BASE1] + offset; +} + +static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct uart_8250_port up = {}; + struct mxpcie8250 *priv; + unsigned short device = pdev->device; + unsigned int num_ports; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + num_ports = mxpcie8250_get_nports(device); + + priv = devm_kzalloc(dev, struct_size(priv, port, num_ports), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->num_ports = num_ports; + priv->supp_rs = mxpcie8250_get_supp_rs(device); + + priv->bar1_base = pcim_iomap(pdev, FL_BASE1, 0); + if (!priv->bar1_base) + return -ENOMEM; + + priv->bar2_base = pcim_iomap(pdev, FL_BASE2, 0); + if (!priv->bar2_base) + return -ENOMEM; + + mxpcie8250_init_board(pdev, priv); + + up.port.dev = dev; + up.port.irq = pdev->irq; + up.port.uartclk = MOXA_PUART_BASE_BAUD * 16; + up.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + up.port.type = PORT_MUEX50; + + up.port.iotype = UPIO_MEM; + up.port.iobase = 0; + up.port.regshift = 0; + + up.port.set_termios = mxpcie8250_set_termios; + up.port.get_rxtrig = mxpcie8250_get_rxtrig; + up.port.set_rxtrig = mxpcie8250_set_rxtrig; + up.port.startup = mxpcie8250_startup; + up.port.shutdown = mxpcie8250_shutdown; + up.port.throttle = mxpcie8250_throttle; + up.port.unthrottle = mxpcie8250_unthrottle; + up.port.handle_irq = mxpcie8250_handle_irq; + up.port.break_ctl = mxpcie8250_break_ctl; + + for (unsigned int i = 0; i < num_ports; i++) { + mxpcie8250_setup_port(pdev, priv, &up, i); + + dev_dbg(dev, "Setup PCI port: port %lx, irq %d, type %d\n", + up.port.iobase, up.port.irq, up.port.iotype); + + priv->port[i].line = serial8250_register_8250_port(&up); + if (priv->port[i].line < 0) { + dev_err(dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + up.port.iobase, up.port.irq, + up.port.iotype, priv->port[i].line); + break; + } + priv->port[i].rx_trig_level = MOXA_PUART_RX_TRIG_DEFAULT; + } + pci_set_drvdata(pdev, priv); + + return 0; +} + +static void mxpcie8250_remove(struct pci_dev *pdev) +{ + struct mxpcie8250 *priv = pci_get_drvdata(pdev); + + for (unsigned int i = 0; i < priv->num_ports; i++) + serial8250_unregister_port(priv->port[i].line); +} + +static const struct pci_device_id mxpcie8250_pci_ids[] = { + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102E) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102EL) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114EL) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132EL) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138E_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A) }, + { } +}; +MODULE_DEVICE_TABLE(pci, mxpcie8250_pci_ids); + +static struct pci_driver mxpcie8250_pci_driver = { + .name = "8250_mxpcie", + .id_table = mxpcie8250_pci_ids, + .probe = mxpcie8250_probe, + .remove = mxpcie8250_remove, +}; +module_pci_driver(mxpcie8250_pci_driver); + +MODULE_AUTHOR("Moxa Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Moxa PCIe Multiport Serial Device Driver"); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 160f4c71de06..b1561c499acd 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -5,6 +5,7 @@ * Copyright (C) 2006 Arnd Bergmann , IBM Corp. */ +#include #include #include #include @@ -156,6 +157,17 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (ret) goto err_pmruntime; + if (IS_ENABLED(CONFIG_CPU_XSCALE) && type == PORT_XSCALE) { + /* + * Adjust for BE32 register accesses: drop any hardcoded + * address for the big endian byte target, add it explicitly + * if running on BE32. + */ + port->mapbase = PTR_ALIGN_DOWN(port->mapbase, 4); + if (IS_ENABLED(CONFIG_CPU_ENDIAN_BE32)) + port->mapbase += 3; + } + /* Get clk rate through clk driver if present */ if (!port->uartclk) { struct clk *bus_clk; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 58b4e525bdb6..c24a59896982 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -76,25 +76,6 @@ #define PCI_DEVICE_ID_WCHIC_CH384_4S 0x3470 #define PCI_DEVICE_ID_WCHIC_CH384_8S 0x3853 -#define PCI_DEVICE_ID_MOXA_CP102E 0x1024 -#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025 -#define PCI_DEVICE_ID_MOXA_CP102N 0x1027 -#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045 -#define PCI_DEVICE_ID_MOXA_CP104N 0x1046 -#define PCI_DEVICE_ID_MOXA_CP112N 0x1121 -#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144 -#define PCI_DEVICE_ID_MOXA_CP114N 0x1145 -#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160 -#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161 -#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182 -#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183 -#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322 -#define PCI_DEVICE_ID_MOXA_CP132N 0x1323 -#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342 -#define PCI_DEVICE_ID_MOXA_CP134N 0x1343 -#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381 -#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683 - #define PCI_DEVICE_ID_ADDIDATA_CPCI7500 0x7003 #define PCI_DEVICE_ID_ADDIDATA_CPCI7500_NG 0x7024 #define PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG 0x7025 @@ -2002,138 +1983,6 @@ pci_sunix_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, 0); } -#define MOXA_PUART_GPIO_EN 0x09 -#define MOXA_PUART_GPIO_OUT 0x0A - -#define MOXA_GPIO_PIN2 BIT(2) - -#define MOXA_RS232 0x00 -#define MOXA_RS422 0x01 -#define MOXA_RS485_4W 0x0B -#define MOXA_RS485_2W 0x0F -#define MOXA_UIR_OFFSET 0x04 -#define MOXA_EVEN_RS_MASK GENMASK(3, 0) -#define MOXA_ODD_RS_MASK GENMASK(7, 4) - -enum { - MOXA_SUPP_RS232 = BIT(0), - MOXA_SUPP_RS422 = BIT(1), - MOXA_SUPP_RS485 = BIT(2), -}; - -static unsigned short moxa_get_nports(unsigned short device) -{ - switch (device) { - case PCI_DEVICE_ID_MOXA_CP116E_A_A: - case PCI_DEVICE_ID_MOXA_CP116E_A_B: - return 8; - } - - return FIELD_GET(0x00F0, device); -} - -static bool pci_moxa_is_mini_pcie(unsigned short device) -{ - if (device == PCI_DEVICE_ID_MOXA_CP102N || - device == PCI_DEVICE_ID_MOXA_CP104N || - device == PCI_DEVICE_ID_MOXA_CP112N || - device == PCI_DEVICE_ID_MOXA_CP114N || - device == PCI_DEVICE_ID_MOXA_CP132N || - device == PCI_DEVICE_ID_MOXA_CP134N) - return true; - - return false; -} - -static unsigned int pci_moxa_supported_rs(struct pci_dev *dev) -{ - switch (dev->device & 0x0F00) { - case 0x0000: - case 0x0600: - return MOXA_SUPP_RS232; - case 0x0100: - return MOXA_SUPP_RS232 | MOXA_SUPP_RS422 | MOXA_SUPP_RS485; - case 0x0300: - return MOXA_SUPP_RS422 | MOXA_SUPP_RS485; - } - return 0; -} - -static int pci_moxa_set_interface(const struct pci_dev *dev, - unsigned int port_idx, - u8 mode) -{ - resource_size_t iobar_addr = pci_resource_start(dev, 2); - resource_size_t UIR_addr = iobar_addr + MOXA_UIR_OFFSET + port_idx / 2; - u8 val; - - val = inb(UIR_addr); - - if (port_idx % 2) { - val &= ~MOXA_ODD_RS_MASK; - val |= FIELD_PREP(MOXA_ODD_RS_MASK, mode); - } else { - val &= ~MOXA_EVEN_RS_MASK; - val |= FIELD_PREP(MOXA_EVEN_RS_MASK, mode); - } - outb(val, UIR_addr); - - return 0; -} - -static int pci_moxa_init(struct pci_dev *dev) -{ - unsigned short device = dev->device; - resource_size_t iobar_addr = pci_resource_start(dev, 2); - unsigned int i, num_ports = moxa_get_nports(device); - u8 val, init_mode = MOXA_RS232; - - if (!IS_ENABLED(CONFIG_HAS_IOPORT)) - return serial_8250_warn_need_ioport(dev); - - if (!(pci_moxa_supported_rs(dev) & MOXA_SUPP_RS232)) { - init_mode = MOXA_RS422; - } - for (i = 0; i < num_ports; ++i) - pci_moxa_set_interface(dev, i, init_mode); - - /* - * Enable hardware buffer to prevent break signal output when system boots up. - * This hardware buffer is only supported on Mini PCIe series. - */ - if (pci_moxa_is_mini_pcie(device)) { - /* Set GPIO direction */ - val = inb(iobar_addr + MOXA_PUART_GPIO_EN); - val |= MOXA_GPIO_PIN2; - outb(val, iobar_addr + MOXA_PUART_GPIO_EN); - /* Enable low GPIO */ - val = inb(iobar_addr + MOXA_PUART_GPIO_OUT); - val &= ~MOXA_GPIO_PIN2; - outb(val, iobar_addr + MOXA_PUART_GPIO_OUT); - } - - return num_ports; -} - -static int -pci_moxa_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) -{ - unsigned int bar = FL_GET_BASE(board->flags); - int offset; - - if (!IS_ENABLED(CONFIG_HAS_IOPORT)) - return serial_8250_warn_need_ioport(priv->dev); - - if (board->num_ports == 4 && idx == 3) - offset = 7 * board->uart_offset; - else - offset = idx * board->uart_offset; - - return setup_port(priv, port, bar, offset, 0); -} - #define SB_OPTR_IMR0 0x0c /* Interrupt mask register, p0 to p7 */ static int pci_systembase_init(struct pci_dev *dev) { @@ -2996,17 +2845,6 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .setup = pci_fintek_setup, .init = pci_fintek_init, }, - /* - * MOXA - */ - { - .vendor = PCI_VENDOR_ID_MOXA, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_moxa_init, - .setup = pci_moxa_setup, - }, { .vendor = 0x1c29, .device = 0x1204, @@ -3225,9 +3063,6 @@ enum pci_board_num_t { pbn_titan_2_4000000, pbn_titan_4_4000000, pbn_titan_8_4000000, - pbn_moxa_2, - pbn_moxa_4, - pbn_moxa_8, }; /* @@ -4005,24 +3840,6 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 0x200, .first_offset = 0x1000, }, - [pbn_moxa_2] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_moxa_4] = { - .flags = FL_BASE1, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_moxa_8] = { - .flags = FL_BASE1, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 0x200, - }, }; #define REPORT_CONFIG(option) \ @@ -4076,6 +3893,9 @@ static const struct pci_device_id blacklist[] = { { PCI_VDEVICE(PERICOM, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(PERICOM), }, { PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(PERICOM), }, + /* Moxa devices */ + { PCI_VDEVICE(MOXA, PCI_ANY_ID), REPORT_8250_CONFIG(MOXA), }, + /* End of the black list */ { } }; @@ -5950,65 +5770,6 @@ static const struct pci_device_id serial_pci_tbl[] = { .driver_data = pbn_ni8430_4, }, - /* - * MOXA - */ - { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102E), - .driver_data = pbn_moxa_2, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102EL), - .driver_data = pbn_moxa_2, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102N), - .driver_data = pbn_moxa_2, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A), - .driver_data = pbn_moxa_4, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104N), - .driver_data = pbn_moxa_4, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112N), - .driver_data = pbn_moxa_2, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114EL), - .driver_data = pbn_moxa_4, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114N), - .driver_data = pbn_moxa_4, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A), - .driver_data = pbn_moxa_8, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B), - .driver_data = pbn_moxa_8, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A), - .driver_data = pbn_moxa_8, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I), - .driver_data = pbn_moxa_8, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132EL), - .driver_data = pbn_moxa_2, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132N), - .driver_data = pbn_moxa_2, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A), - .driver_data = pbn_moxa_4, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134N), - .driver_data = pbn_moxa_4, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138E_A), - .driver_data = pbn_moxa_8, - }, { - PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A), - .driver_data = pbn_moxa_8, - }, - /* * ADDI-DATA GmbH communication cards */ diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c index ad3a7bc31d6f..af946d12e764 100644 --- a/drivers/tty/serial/8250/8250_platform.c +++ b/drivers/tty/serial/8250/8250_platform.c @@ -284,6 +284,7 @@ static struct platform_driver serial8250_isa_driver = { .driver = { .name = "serial8250", .acpi_match_table = acpi_platform_serial_table, + .probe_type = PROBE_FORCE_SYNCHRONOUS, }, }; diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 7a837fdf9df1..6bfdeff5fe22 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -28,351 +28,351 @@ static const struct pnp_device_id pnp_dev_table[] = { /* Archtek America Corp. */ /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "AAC000F", 0 }, + { .id = "AAC000F", .driver_data = 0 }, /* Anchor Datacomm BV */ /* SXPro 144 External Data Fax Modem Plug & Play */ - { "ADC0001", 0 }, + { .id = "ADC0001", .driver_data = 0 }, /* SXPro 288 External Data Fax Modem Plug & Play */ - { "ADC0002", 0 }, + { .id = "ADC0002", .driver_data = 0 }, /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ - { "AEI0250", 0 }, + { .id = "AEI0250", .driver_data = 0 }, /* Actiontec ISA PNP 56K X2 Fax Modem */ - { "AEI1240", 0 }, + { .id = "AEI1240", .driver_data = 0 }, /* Rockwell 56K ACF II Fax+Data+Voice Modem */ - { "AKY1021", 0 /*SPCI_FL_NO_SHIRQ*/ }, + { .id ="AKY1021", .driver_data = 0 /*SPCI_FL_NO_SHIRQ*/ }, /* * ALi Fast Infrared Controller * Native driver (ali-ircc) is broken so at least * it can be used with irtty-sir. */ - { "ALI5123", 0 }, + { .id = "ALI5123", .driver_data = 0 }, /* AZT3005 PnP SOUND DEVICE */ - { "AZT4001", 0 }, + { .id = "AZT4001", .driver_data = 0 }, /* Best Data Products Inc. Smart One 336F PnP Modem */ - { "BDP3336", 0 }, + { .id = "BDP3336", .driver_data = 0 }, /* Boca Research */ /* Boca Complete Ofc Communicator 14.4 Data-FAX */ - { "BRI0A49", 0 }, + { .id = "BRI0A49", .driver_data = 0 }, /* Boca Research 33,600 ACF Modem */ - { "BRI1400", 0 }, + { .id = "BRI1400", .driver_data = 0 }, /* Boca 33.6 Kbps Internal FD34FSVD */ - { "BRI3400", 0 }, + { .id = "BRI3400", .driver_data = 0 }, /* Computer Peripherals Inc */ /* EuroViVa CommCenter-33.6 SP PnP */ - { "CPI4050", 0 }, + { .id = "CPI4050", .driver_data = 0 }, /* Creative Labs */ /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ - { "CTL3001", 0 }, + { .id = "CTL3001", .driver_data = 0 }, /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ - { "CTL3011", 0 }, + { .id = "CTL3011", .driver_data = 0 }, /* Davicom ISA 33.6K Modem */ - { "DAV0336", 0 }, + { .id = "DAV0336", .driver_data = 0 }, /* Creative */ /* Creative Modem Blaster Flash56 DI5601-1 */ - { "DMB1032", 0 }, + { .id = "DMB1032", .driver_data = 0 }, /* Creative Modem Blaster V.90 DI5660 */ - { "DMB2001", 0 }, + { .id = "DMB2001", .driver_data = 0 }, /* E-Tech */ /* E-Tech CyberBULLET PC56RVP */ - { "ETT0002", 0 }, + { .id = "ETT0002", .driver_data = 0 }, /* FUJITSU */ /* Fujitsu 33600 PnP-I2 R Plug & Play */ - { "FUJ0202", 0 }, + { .id = "FUJ0202", .driver_data = 0 }, /* Fujitsu FMV-FX431 Plug & Play */ - { "FUJ0205", 0 }, + { .id = "FUJ0205", .driver_data = 0 }, /* Fujitsu 33600 PnP-I4 R Plug & Play */ - { "FUJ0206", 0 }, + { .id = "FUJ0206", .driver_data = 0 }, /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ - { "FUJ0209", 0 }, + { .id = "FUJ0209", .driver_data = 0 }, /* Archtek America Corp. */ /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "GVC000F", 0 }, + { .id = "GVC000F", .driver_data = 0 }, /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */ - { "GVC0303", 0 }, + { .id = "GVC0303", .driver_data = 0 }, /* Hayes */ /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ - { "HAY0001", 0 }, + { .id = "HAY0001", .driver_data = 0 }, /* Hayes Optima 336 V.34 + FAX + Voice PnP */ - { "HAY000C", 0 }, + { .id = "HAY000C", .driver_data = 0 }, /* Hayes Optima 336B V.34 + FAX + Voice PnP */ - { "HAY000D", 0 }, + { .id = "HAY000D", .driver_data = 0 }, /* Hayes Accura 56K Ext Fax Modem PnP */ - { "HAY5670", 0 }, + { .id = "HAY5670", .driver_data = 0 }, /* Hayes Accura 56K Ext Fax Modem PnP */ - { "HAY5674", 0 }, + { .id = "HAY5674", .driver_data = 0 }, /* Hayes Accura 56K Fax Modem PnP */ - { "HAY5675", 0 }, + { .id = "HAY5675", .driver_data = 0 }, /* Hayes 288, V.34 + FAX */ - { "HAYF000", 0 }, + { .id = "HAYF000", .driver_data = 0 }, /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ - { "HAYF001", 0 }, + { .id = "HAYF001", .driver_data = 0 }, /* IBM */ /* IBM Thinkpad 701 Internal Modem Voice */ - { "IBM0033", 0 }, + { .id = "IBM0033", .driver_data = 0 }, /* Intermec */ /* Intermec CV60 touchscreen port */ - { "PNP4972", 0 }, + { .id = "PNP4972", .driver_data = 0 }, /* Intertex */ /* Intertex 28k8 33k6 Voice EXT PnP */ - { "IXDC801", 0 }, + { .id = "IXDC801", .driver_data = 0 }, /* Intertex 33k6 56k Voice EXT PnP */ - { "IXDC901", 0 }, + { .id = "IXDC901", .driver_data = 0 }, /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { "IXDD801", 0 }, + { .id = "IXDD801", .driver_data = 0 }, /* Intertex 33k6 56k Voice SP EXT PnP */ - { "IXDD901", 0 }, + { .id = "IXDD901", .driver_data = 0 }, /* Intertex 28k8 33k6 Voice SP INT PnP */ - { "IXDF401", 0 }, + { .id = "IXDF401", .driver_data = 0 }, /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { "IXDF801", 0 }, + { .id = "IXDF801", .driver_data = 0 }, /* Intertex 33k6 56k Voice SP EXT PnP */ - { "IXDF901", 0 }, + { .id = "IXDF901", .driver_data = 0 }, /* Kortex International */ /* KORTEX 28800 Externe PnP */ - { "KOR4522", 0 }, + { .id = "KOR4522", .driver_data = 0 }, /* KXPro 33.6 Vocal ASVD PnP */ - { "KORF661", 0 }, + { .id = "KORF661", .driver_data = 0 }, /* Lasat */ /* LASAT Internet 33600 PnP */ - { "LAS4040", 0 }, + { .id = "LAS4040", .driver_data = 0 }, /* Lasat Safire 560 PnP */ - { "LAS4540", 0 }, + { .id = "LAS4540", .driver_data = 0 }, /* Lasat Safire 336 PnP */ - { "LAS5440", 0 }, + { .id = "LAS5440", .driver_data = 0 }, /* Microcom, Inc. */ /* Microcom TravelPorte FAST V.34 Plug & Play */ - { "MNP0281", 0 }, + { .id = "MNP0281", .driver_data = 0 }, /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ - { "MNP0336", 0 }, + { .id = "MNP0336", .driver_data = 0 }, /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ - { "MNP0339", 0 }, + { .id = "MNP0339", .driver_data = 0 }, /* Microcom DeskPorte 28.8P Plug & Play */ - { "MNP0342", 0 }, + { .id = "MNP0342", .driver_data = 0 }, /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { "MNP0500", 0 }, + { .id = "MNP0500", .driver_data = 0 }, /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { "MNP0501", 0 }, + { .id = "MNP0501", .driver_data = 0 }, /* Microcom DeskPorte 28.8S Internal Plug & Play */ - { "MNP0502", 0 }, + { .id = "MNP0502", .driver_data = 0 }, /* Motorola */ /* Motorola BitSURFR Plug & Play */ - { "MOT1105", 0 }, + { .id = "MOT1105", .driver_data = 0 }, /* Motorola TA210 Plug & Play */ - { "MOT1111", 0 }, + { .id = "MOT1111", .driver_data = 0 }, /* Motorola HMTA 200 (ISDN) Plug & Play */ - { "MOT1114", 0 }, + { .id = "MOT1114", .driver_data = 0 }, /* Motorola BitSURFR Plug & Play */ - { "MOT1115", 0 }, + { .id = "MOT1115", .driver_data = 0 }, /* Motorola Lifestyle 28.8 Internal */ - { "MOT1190", 0 }, + { .id = "MOT1190", .driver_data = 0 }, /* Motorola V.3400 Plug & Play */ - { "MOT1501", 0 }, + { .id = "MOT1501", .driver_data = 0 }, /* Motorola Lifestyle 28.8 V.34 Plug & Play */ - { "MOT1502", 0 }, + { .id = "MOT1502", .driver_data = 0 }, /* Motorola Power 28.8 V.34 Plug & Play */ - { "MOT1505", 0 }, + { .id = "MOT1505", .driver_data = 0 }, /* Motorola ModemSURFR External 28.8 Plug & Play */ - { "MOT1509", 0 }, + { .id = "MOT1509", .driver_data = 0 }, /* Motorola Premier 33.6 Desktop Plug & Play */ - { "MOT150A", 0 }, + { .id = "MOT150A", .driver_data = 0 }, /* Motorola VoiceSURFR 56K External PnP */ - { "MOT150F", 0 }, + { .id = "MOT150F", .driver_data = 0 }, /* Motorola ModemSURFR 56K External PnP */ - { "MOT1510", 0 }, + { .id = "MOT1510", .driver_data = 0 }, /* Motorola ModemSURFR 56K Internal PnP */ - { "MOT1550", 0 }, + { .id = "MOT1550", .driver_data = 0 }, /* Motorola ModemSURFR Internal 28.8 Plug & Play */ - { "MOT1560", 0 }, + { .id = "MOT1560", .driver_data = 0 }, /* Motorola Premier 33.6 Internal Plug & Play */ - { "MOT1580", 0 }, + { .id = "MOT1580", .driver_data = 0 }, /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ - { "MOT15B0", 0 }, + { .id = "MOT15B0", .driver_data = 0 }, /* Motorola VoiceSURFR 56K Internal PnP */ - { "MOT15F0", 0 }, + { .id = "MOT15F0", .driver_data = 0 }, /* Com 1 */ /* Deskline K56 Phone System PnP */ - { "MVX00A1", 0 }, + { .id = "MVX00A1", .driver_data = 0 }, /* PC Rider K56 Phone System PnP */ - { "MVX00F2", 0 }, + { .id = "MVX00F2", .driver_data = 0 }, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ - { "nEC8241", 0 }, + { .id = "nEC8241", .driver_data = 0 }, /* Pace 56 Voice Internal Plug & Play Modem */ - { "PMC2430", 0 }, + { .id = "PMC2430", .driver_data = 0 }, /* Generic */ /* Generic standard PC COM port */ - { "PNP0500", 0 }, + { .id = "PNP0500", .driver_data = 0 }, /* Generic 16550A-compatible COM port */ - { "PNP0501", 0 }, + { .id = "PNP0501", .driver_data = 0 }, /* Compaq 14400 Modem */ - { "PNPC000", 0 }, + { .id = "PNPC000", .driver_data = 0 }, /* Compaq 2400/9600 Modem */ - { "PNPC001", 0 }, + { .id = "PNPC001", .driver_data = 0 }, /* Dial-Up Networking Serial Cable between 2 PCs */ - { "PNPC031", 0 }, + { .id = "PNPC031", .driver_data = 0 }, /* Dial-Up Networking Parallel Cable between 2 PCs */ - { "PNPC032", 0 }, + { .id = "PNPC032", .driver_data = 0 }, /* Standard 9600 bps Modem */ - { "PNPC100", 0 }, + { .id = "PNPC100", .driver_data = 0 }, /* Standard 14400 bps Modem */ - { "PNPC101", 0 }, + { .id = "PNPC101", .driver_data = 0 }, /* Standard 28800 bps Modem*/ - { "PNPC102", 0 }, + { .id = "PNPC102", .driver_data = 0 }, /* Standard Modem*/ - { "PNPC103", 0 }, + { .id = "PNPC103", .driver_data = 0 }, /* Standard 9600 bps Modem*/ - { "PNPC104", 0 }, + { .id = "PNPC104", .driver_data = 0 }, /* Standard 14400 bps Modem*/ - { "PNPC105", 0 }, + { .id = "PNPC105", .driver_data = 0 }, /* Standard 28800 bps Modem*/ - { "PNPC106", 0 }, + { .id = "PNPC106", .driver_data = 0 }, /* Standard Modem */ - { "PNPC107", 0 }, + { .id = "PNPC107", .driver_data = 0 }, /* Standard 9600 bps Modem */ - { "PNPC108", 0 }, + { .id = "PNPC108", .driver_data = 0 }, /* Standard 14400 bps Modem */ - { "PNPC109", 0 }, + { .id = "PNPC109", .driver_data = 0 }, /* Standard 28800 bps Modem */ - { "PNPC10A", 0 }, + { .id = "PNPC10A", .driver_data = 0 }, /* Standard Modem */ - { "PNPC10B", 0 }, + { .id = "PNPC10B", .driver_data = 0 }, /* Standard 9600 bps Modem */ - { "PNPC10C", 0 }, + { .id = "PNPC10C", .driver_data = 0 }, /* Standard 14400 bps Modem */ - { "PNPC10D", 0 }, + { .id = "PNPC10D", .driver_data = 0 }, /* Standard 28800 bps Modem */ - { "PNPC10E", 0 }, + { .id = "PNPC10E", .driver_data = 0 }, /* Standard Modem */ - { "PNPC10F", 0 }, + { .id = "PNPC10F", .driver_data = 0 }, /* Standard PCMCIA Card Modem */ - { "PNP2000", 0 }, + { .id = "PNP2000", .driver_data = 0 }, /* Rockwell */ /* Modular Technology */ /* Rockwell 33.6 DPF Internal PnP */ /* Modular Technology 33.6 Internal PnP */ - { "ROK0030", 0 }, + { .id = "ROK0030", .driver_data = 0 }, /* Kortex International */ /* KORTEX 14400 Externe PnP */ - { "ROK0100", 0 }, + { .id = "ROK0100", .driver_data = 0 }, /* Rockwell 28.8 */ - { "ROK4120", 0 }, + { .id = "ROK4120", .driver_data = 0 }, /* Viking Components, Inc */ /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ - { "ROK4920", 0 }, + { .id = "ROK4920", .driver_data = 0 }, /* Rockwell */ /* British Telecom */ /* Modular Technology */ /* Rockwell 33.6 DPF External PnP */ /* BT Prologue 33.6 External PnP */ /* Modular Technology 33.6 External PnP */ - { "RSS00A0", 0 }, + { .id = "RSS00A0", .driver_data = 0 }, /* Viking 56K FAX INT */ - { "RSS0262", 0 }, + { .id = "RSS0262", .driver_data = 0 }, /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */ - { "RSS0250", 0 }, + { .id = "RSS0250", .driver_data = 0 }, /* SupraExpress 28.8 Data/Fax PnP modem */ - { "SUP1310", 0 }, + { .id = "SUP1310", .driver_data = 0 }, /* SupraExpress 336i PnP Voice Modem */ - { "SUP1381", 0 }, + { .id = "SUP1381", .driver_data = 0 }, /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1421", 0 }, + { .id = "SUP1421", .driver_data = 0 }, /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1590", 0 }, + { .id = "SUP1590", .driver_data = 0 }, /* SupraExpress 336i Sp ASVD */ - { "SUP1620", 0 }, + { .id = "SUP1620", .driver_data = 0 }, /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1760", 0 }, + { .id = "SUP1760", .driver_data = 0 }, /* SupraExpress 56i Sp Intl */ - { "SUP2171", 0 }, + { .id = "SUP2171", .driver_data = 0 }, /* Phoebe Micro */ /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ - { "TEX0011", 0 }, + { .id = "TEX0011", .driver_data = 0 }, /* Archtek America Corp. */ /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "UAC000F", 0 }, + { .id = "UAC000F", .driver_data = 0 }, /* 3Com Corp. */ /* Gateway Telepath IIvi 33.6 */ - { "USR0000", 0 }, + { .id = "USR0000", .driver_data = 0 }, /* U.S. Robotics Sporster 33.6K Fax INT PnP */ - { "USR0002", 0 }, + { .id = "USR0002", .driver_data = 0 }, /* Sportster Vi 14.4 PnP FAX Voicemail */ - { "USR0004", 0 }, + { .id = "USR0004", .driver_data = 0 }, /* U.S. Robotics 33.6K Voice INT PnP */ - { "USR0006", 0 }, + { .id = "USR0006", .driver_data = 0 }, /* U.S. Robotics 33.6K Voice EXT PnP */ - { "USR0007", 0 }, + { .id = "USR0007", .driver_data = 0 }, /* U.S. Robotics Courier V.Everything INT PnP */ - { "USR0009", 0 }, + { .id = "USR0009", .driver_data = 0 }, /* U.S. Robotics 33.6K Voice INT PnP */ - { "USR2002", 0 }, + { .id = "USR2002", .driver_data = 0 }, /* U.S. Robotics 56K Voice INT PnP */ - { "USR2070", 0 }, + { .id = "USR2070", .driver_data = 0 }, /* U.S. Robotics 56K Voice EXT PnP */ - { "USR2080", 0 }, + { .id = "USR2080", .driver_data = 0 }, /* U.S. Robotics 56K FAX INT */ - { "USR3031", 0 }, + { .id = "USR3031", .driver_data = 0 }, /* U.S. Robotics 56K FAX INT */ - { "USR3050", 0 }, + { .id = "USR3050", .driver_data = 0 }, /* U.S. Robotics 56K Voice INT PnP */ - { "USR3070", 0 }, + { .id = "USR3070", .driver_data = 0 }, /* U.S. Robotics 56K Voice EXT PnP */ - { "USR3080", 0 }, + { .id = "USR3080", .driver_data = 0 }, /* U.S. Robotics 56K Voice INT PnP */ - { "USR3090", 0 }, + { .id = "USR3090", .driver_data = 0 }, /* U.S. Robotics 56K Message */ - { "USR9100", 0 }, + { .id = "USR9100", .driver_data = 0 }, /* U.S. Robotics 56K FAX EXT PnP*/ - { "USR9160", 0 }, + { .id = "USR9160", .driver_data = 0 }, /* U.S. Robotics 56K FAX INT PnP*/ - { "USR9170", 0 }, + { .id = "USR9170", .driver_data = 0 }, /* U.S. Robotics 56K Voice EXT PnP*/ - { "USR9180", 0 }, + { .id = "USR9180", .driver_data = 0 }, /* U.S. Robotics 56K Voice INT PnP*/ - { "USR9190", 0 }, + { .id = "USR9190", .driver_data = 0 }, /* Wacom tablets */ - { "WACFXXX", 0 }, + { .id = "WACFXXX", .driver_data = 0 }, /* Compaq touchscreen */ - { "FPI2002", 0 }, + { .id = "FPI2002", .driver_data = 0 }, /* Fujitsu Stylistic touchscreens */ - { "FUJ02B2", 0 }, - { "FUJ02B3", 0 }, + { .id = "FUJ02B2", .driver_data = 0 }, + { .id = "FUJ02B3", .driver_data = 0 }, /* Fujitsu Stylistic LT touchscreens */ - { "FUJ02B4", 0 }, + { .id = "FUJ02B4", .driver_data = 0 }, /* Passive Fujitsu Stylistic touchscreens */ - { "FUJ02B6", 0 }, - { "FUJ02B7", 0 }, - { "FUJ02B8", 0 }, - { "FUJ02B9", 0 }, - { "FUJ02BC", 0 }, + { .id = "FUJ02B6", .driver_data = 0 }, + { .id = "FUJ02B7", .driver_data = 0 }, + { .id = "FUJ02B8", .driver_data = 0 }, + { .id = "FUJ02B9", .driver_data = 0 }, + { .id = "FUJ02BC", .driver_data = 0 }, /* Fujitsu Wacom Tablet PC device */ - { "FUJ02E5", 0 }, + { .id = "FUJ02E5", .driver_data = 0 }, /* Fujitsu P-series tablet PC device */ - { "FUJ02E6", 0 }, + { .id = "FUJ02E6", .driver_data = 0 }, /* Fujitsu Wacom 2FGT Tablet PC device */ - { "FUJ02E7", 0 }, + { .id = "FUJ02E7", .driver_data = 0 }, /* Fujitsu Wacom 1FGT Tablet PC device */ - { "FUJ02E9", 0 }, + { .id = "FUJ02E9", .driver_data = 0 }, /* * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 * in disguise). */ - { "LTS0001", 0 }, + { .id = "LTS0001", .driver_data = 0 }, /* Rockwell's (PORALiNK) 33600 INT PNP */ - { "WCI0003", 0 }, + { .id = "WCI0003", .driver_data = 0 }, /* Unknown PnP modems */ - { "PNPCXXX", UNKNOWN_DEV }, + { .id = "PNPCXXX", .driver_data = UNKNOWN_DEV }, /* More unknown PnP modems */ - { "PNPDXXX", UNKNOWN_DEV }, + { .id = "PNPDXXX", .driver_data = UNKNOWN_DEV }, /* * Winbond CIR port, should not be probed. We should keep track of * it to prevent the legacy serial driver from probing it. */ - { "WEC1022", CIR_PORT }, + { .id = "WEC1022", .driver_data = CIR_PORT }, /* * SMSC IrCC SIR/FIR port, should not be probed by serial driver as * well so its own driver can bind to it. */ - { "SMCF010", CIR_PORT }, - { "", 0 } + { .id = "SMCF010", .driver_data = CIR_PORT }, + { } }; MODULE_DEVICE_TABLE(pnp, pnp_dev_table); @@ -521,6 +521,7 @@ static struct pnp_driver serial_pnp_driver = { .remove = serial_pnp_remove, .driver = { .pm = pm_sleep_ptr(&serial_pnp_pm_ops), + .probe_type = PROBE_FORCE_SYNCHRONOUS, }, .id_table = pnp_dev_table, }; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index e94a0802cbdd..38fa45e74a37 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -311,6 +311,14 @@ static const struct serial8250_config uart_config[] = { .rxtrig_bytes = {1, 8, 16, 30}, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, + [PORT_MUEX50] = { + .name = "Moxa MUEx50 UART", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {15, 31, 63, 111}, + .flags = UART_CAP_FIFO, + }, }; /* Uart divisor latch read */ @@ -474,16 +482,10 @@ static void set_io_from_upio(struct uart_port *p) static void serial_port_out_sync(struct uart_port *p, int offset, int value) { - switch (p->iotype) { - case UPIO_MEM: - case UPIO_MEM16: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_AU: + if (uart_iotype_mmio(p->iotype)) { p->serial_out(p, offset, value); p->serial_in(p, UART_LCR); /* safe, no side-effects */ - break; - default: + } else { p->serial_out(p, offset, value); } } @@ -710,7 +712,12 @@ static void serial8250_clear_interrupts(struct uart_port *port) serial_port_in(port, UART_MSR); } -static void serial8250_clear_IER(struct uart_8250_port *up) +/* + * Only to be directly used by serial8250_console_write() and + * serial8250_put_poll_char(), which do not require the port lock. + * Use serial8250_clear_IER() instead for all other cases. + */ +static void __serial8250_clear_IER(struct uart_8250_port *up) { if (up->capabilities & UART_CAP_UUE) serial_out(up, UART_IER, UART_IER_UUE); @@ -718,6 +725,14 @@ static void serial8250_clear_IER(struct uart_8250_port *up) serial_out(up, UART_IER, 0); } +static inline void serial8250_clear_IER(struct uart_8250_port *up) +{ + /* Port locked to synchronize UART_IER access against the console */ + lockdep_assert_held_once(&up->port.lock); + + __serial8250_clear_IER(up); +} + /* * This is a quickie test to see how big the FIFO is. * It doesn't work at all the time, more's the pity. @@ -1290,9 +1305,6 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p, bool toggle_ier) { unsigned char mcr = serial8250_in_MCR(p); - /* Port locked to synchronize UART_IER access against the console. */ - lockdep_assert_held_once(&p->port.lock); - if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) mcr |= UART_MCR_RTS; else @@ -1308,6 +1320,16 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p, bool toggle_ier) serial8250_clear_and_reinit_fifos(p); if (toggle_ier) { + /* + * Port locked to synchronize UART_IER access against + * the console. The lockdep_assert must be restricted + * to this condition because only here is it + * guaranteed that the port lock is held. The other + * hardware access in this function is synchronized + * by console ownership. + */ + lockdep_assert_held_once(&p->port.lock); + p->ier |= UART_IER_RLSI | UART_IER_RDI; serial_port_out(&p->port, UART_IER, p->ier); } @@ -1950,7 +1972,7 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) serial8250_do_set_mctrl(port, mctrl); } -static void serial8250_break_ctl(struct uart_port *port, int break_state) +void serial8250_do_break_ctl(struct uart_port *port, int break_state) { struct uart_8250_port *up = up_to_u8250p(port); @@ -1963,6 +1985,15 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) up->lcr &= ~UART_LCR_SBC; serial_port_out(port, UART_LCR, up->lcr); } +EXPORT_SYMBOL_GPL(serial8250_do_break_ctl); + +static void serial8250_break_ctl(struct uart_port *port, int break_state) +{ + if (port->break_ctl) + port->break_ctl(port, break_state); + else + serial8250_do_break_ctl(port, break_state); +} /* Returns true if @bits were set, false on timeout */ static bool wait_for_lsr(struct uart_8250_port *up, int bits) @@ -2059,10 +2090,13 @@ static void serial8250_put_poll_char(struct uart_port *port, guard(serial8250_rpm)(up); /* - * First save the IER then disable the interrupts + * First, save the IER, then disable the interrupts. The special + * variant to clear the IER is used because KDB/KGDB printing + * is synchronized via CPU quiescence without holding the port + * lock. */ ier = serial_port_in(port, UART_IER); - serial8250_clear_IER(up); + __serial8250_clear_IER(up); wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); /* @@ -2891,13 +2925,7 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) unsigned int size = serial8250_port_size(up); struct uart_port *port = &up->port; - switch (port->iotype) { - case UPIO_AU: - case UPIO_TSI: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_MEM16: - case UPIO_MEM: + if (uart_iotype_mmio(port->iotype)) { if (!port->mapbase) return -EINVAL; @@ -2911,14 +2939,9 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) return -ENOMEM; } } - return 0; - case UPIO_HUB6: - case UPIO_PORT: + } else if (uart_iotype_io(port->iotype)) { if (!request_region(port->iobase, size, "serial")) return -EBUSY; - return 0; - case UPIO_UNKNOWN: - break; } return 0; @@ -2929,15 +2952,9 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) unsigned int size = serial8250_port_size(up); struct uart_port *port = &up->port; - switch (port->iotype) { - case UPIO_AU: - case UPIO_TSI: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_MEM16: - case UPIO_MEM: + if (uart_iotype_mmio(port->iotype)) { if (!port->mapbase) - break; + return; if (port->flags & UPF_IOREMAP) { iounmap(port->membase); @@ -2945,14 +2962,8 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) } release_mem_region(port->mapbase, size); - break; - - case UPIO_HUB6: - case UPIO_PORT: + } else if (uart_iotype_io(port->iotype)) { release_region(port->iobase, size); - break; - case UPIO_UNKNOWN: - break; } } @@ -3024,9 +3035,14 @@ static ssize_t rx_trig_bytes_show(struct device *dev, struct device_attribute *attr, char *buf) { struct tty_port *port = dev_get_drvdata(dev); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; int rxtrig_bytes; - rxtrig_bytes = do_serial8250_get_rxtrig(port); + if (uport->get_rxtrig) + rxtrig_bytes = uport->get_rxtrig(uport); + else + rxtrig_bytes = do_serial8250_get_rxtrig(port); if (rxtrig_bytes < 0) return rxtrig_bytes; @@ -3069,6 +3085,8 @@ static ssize_t rx_trig_bytes_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct tty_port *port = dev_get_drvdata(dev); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; unsigned char bytes; int ret; @@ -3079,7 +3097,10 @@ static ssize_t rx_trig_bytes_store(struct device *dev, if (ret < 0) return ret; - ret = do_serial8250_set_rxtrig(port, bytes); + if (uport->set_rxtrig) + ret = uport->set_rxtrig(uport, bytes); + else + ret = do_serial8250_set_rxtrig(port, bytes); if (ret < 0) return ret; @@ -3227,13 +3248,32 @@ void serial8250_set_defaults(struct uart_8250_port *up) } EXPORT_SYMBOL_GPL(serial8250_set_defaults); -void serial8250_fifo_wait_for_lsr_thre(struct uart_8250_port *up, unsigned int count) +void serial8250_fifo_wait_for_lsr_thre(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + unsigned int count) { unsigned int i; + /* + * For console writing, enter/exit an unsafe section for each byte + * in order to pass the ownership as quickly as possible if a higher + * priority context wants ownership. Otherwise, an attempt to take + * over the ownership might timeout. The new owner will wait for + * UART_LSR_THRE before reusing the fifo. + */ for (i = 0; i < count; i++) { - if (wait_for_lsr(up, UART_LSR_THRE)) + bool tx_ready; + + if (wctxt && !nbcon_enter_unsafe(wctxt)) return; + + tx_ready = wait_for_lsr(up, UART_LSR_THRE); + + if (wctxt) + nbcon_exit_unsafe(wctxt); + + if (tx_ready) + break; } } EXPORT_SYMBOL_NS_GPL(serial8250_fifo_wait_for_lsr_thre, "SERIAL_8250"); @@ -3242,7 +3282,11 @@ EXPORT_SYMBOL_NS_GPL(serial8250_fifo_wait_for_lsr_thre, "SERIAL_8250"); static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) { + struct uart_8250_port *up = up_to_u8250p(port); + serial_port_out(port, UART_TX, ch); + + up->console_line_ended = (ch == '\n'); } static void serial8250_console_wait_putchar(struct uart_port *port, unsigned char ch) @@ -3285,8 +3329,9 @@ static void serial8250_console_restore(struct uart_8250_port *up) * It sends fifosize bytes and then waits for the fifo * to get empty. */ -static void serial8250_console_fifo_write(struct uart_8250_port *up, - const char *s, unsigned int count) +static void __serial8250_console_fifo_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + const char *s, unsigned int count) { const char *end = s + count; unsigned int fifosize = up->tx_loadsz; @@ -3297,9 +3342,16 @@ static void serial8250_console_fifo_write(struct uart_8250_port *up, while (s != end) { /* Allow timeout for each byte of a possibly full FIFO */ - serial8250_fifo_wait_for_lsr_thre(up, fifosize); + serial8250_fifo_wait_for_lsr_thre(up, wctxt, fifosize); + /* + * Fill the FIFO. If a handover or takeover occurs, writing + * must be aborted since the string data is no longer valid. + */ for (i = 0; i < fifosize && s != end; ++i) { + if (!nbcon_enter_unsafe(wctxt)) + return; + if (*s == '\n' && !cr_sent) { serial8250_console_putchar(port, '\r'); cr_sent = true; @@ -3307,6 +3359,8 @@ static void serial8250_console_fifo_write(struct uart_8250_port *up, serial8250_console_putchar(port, *s++); cr_sent = false; } + + nbcon_exit_unsafe(wctxt); } tx_count = i; } @@ -3315,39 +3369,92 @@ static void serial8250_console_fifo_write(struct uart_8250_port *up, * Allow timeout for each byte written since the caller will only wait * for UART_LSR_BOTH_EMPTY using the timeout of a single character */ - serial8250_fifo_wait_for_lsr_thre(up, tx_count); + serial8250_fifo_wait_for_lsr_thre(up, wctxt, tx_count); +} + +static void serial8250_console_fifo_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt) +{ + __serial8250_console_fifo_write(up, wctxt, wctxt->outbuf, wctxt->len); +} + +static void __serial8250_console_byte_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + const char *s, unsigned int count) +{ + struct uart_port *port = &up->port; + const char *end = s + count; + + /* + * Write out the message. If a handover or takeover occurs, writing + * must be aborted since the string data is no longer valid. + */ + while (s != end) { + if (!nbcon_enter_unsafe(wctxt)) + return; + + uart_console_write(port, s++, 1, serial8250_console_wait_putchar); + + nbcon_exit_unsafe(wctxt); + } +} + +static void serial8250_console_byte_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt) +{ + __serial8250_console_byte_write(up, wctxt, wctxt->outbuf, wctxt->len); } /* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - * - * Doing runtime PM is really a bad idea for the kernel console. - * Thus, we assume the function is called when device is powered up. + * Print the console line using the appropriate variant. If ownership is lost + * at any time during printing, the printing is aborted. */ -void serial8250_console_write(struct uart_8250_port *up, const char *s, - unsigned int count) +static void __serial8250_console_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + bool use_fifo) +{ + /* + * If the console printer did not fully output the previous line, it + * must have been handed or taken over. Insert a newline in order to + * maintain clean output. + */ + if (!up->console_line_ended) { + if (use_fifo) + __serial8250_console_fifo_write(up, wctxt, "\n", 1); + else + __serial8250_console_byte_write(up, wctxt, "\n", 1); + } + + if (use_fifo) + serial8250_console_fifo_write(up, wctxt); + else + serial8250_console_byte_write(up, wctxt); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ +void serial8250_console_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + bool is_atomic) { struct uart_8250_em485 *em485 = up->em485; struct uart_port *port = &up->port; - unsigned long flags; - unsigned int ier, use_fifo; - int locked = 1; + unsigned int ier; + bool use_fifo; - touch_nmi_watchdog(); - - if (oops_in_progress) - locked = uart_port_trylock_irqsave(port, &flags); - else - uart_port_lock_irqsave(port, &flags); + if (!nbcon_enter_unsafe(wctxt)) + return; /* - * First save the IER then disable the interrupts + * First, save the IER, then disable the interrupts. The special + * variant to clear the IER is used because emergency and panic + * printing is synchronized only by nbcon ownership without + * holding the port lock. */ ier = serial_port_in(port, UART_IER); - serial8250_clear_IER(up); + __serial8250_clear_IER(up); /* check scratch reg to see if port powered off during system sleep */ if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { @@ -3381,10 +3488,17 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, */ !uart_console_hwflow_active(&up->port); - if (likely(use_fifo)) - serial8250_console_fifo_write(up, s, count); - else - uart_console_write(port, s, count, serial8250_console_wait_putchar); + nbcon_exit_unsafe(wctxt); + + __serial8250_console_write(up, wctxt, use_fifo); + + /* + * Re-enter an unsafe section in order to perform final actions + * (such as re-enabling interrupts). If ownership was lost, this + * context must reacquire ownership. + */ + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); /* * Finally, wait for transmitter to become empty @@ -3394,10 +3508,21 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, if (em485) { mdelay(port->rs485.delay_rts_after_send); + + /* Toggle unsafe after possibly long delay */ + nbcon_exit_unsafe(wctxt); + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + if (em485->tx_stopped) up->rs485_stop_tx(up, false); } + /* Toggle unsafe after possibly long delay */ + nbcon_exit_unsafe(wctxt); + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + serial_port_out(port, UART_IER, ier); /* @@ -3407,11 +3532,25 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, * call it if we have saved something in the saved flags * while processing with interrupts off. */ - if (up->msr_saved_flags) - serial8250_modem_status(up); + if (up->msr_saved_flags) { + if (is_atomic) { + /* + * For atomic, MSR handling must be deferred to + * irq_work because this may be a context that does + * not permit waking up tasks. + * + * But no irq_work may be queued when suspending. + * In that case, the MSR handling will occur during + * resume in serial8250_resume_port(). + */ + if (up->console_msr_work_allow) + irq_work_queue(&up->console_msr_work); + } else { + serial8250_modem_status(up); + } + } - if (locked) - uart_port_unlock_irqrestore(port, flags); + nbcon_exit_unsafe(wctxt); } static unsigned int probe_baud(struct uart_port *port) @@ -3429,8 +3568,24 @@ static unsigned int probe_baud(struct uart_port *port) return (port->uartclk / 16) / quot; } +/* + * irq_work handler to perform modem control during console output. + * Only triggered via ->write_atomic() callback because it may be + * in a scheduler or NMI context, unable to wake tasks. + */ +static void console_msr_handler(struct irq_work *iwp) +{ + struct uart_8250_port *up = container_of(iwp, struct uart_8250_port, console_msr_work); + struct uart_port *port = &up->port; + + guard(uart_port_lock)(port); + + serial8250_modem_status(up); +} + int serial8250_console_setup(struct uart_port *port, char *options, bool probe) { + struct uart_8250_port *up = up_to_u8250p(port); int baud = 9600; int bits = 8; int parity = 'n'; @@ -3440,6 +3595,10 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) if (!port->iobase && !port->membase) return -ENODEV; + up->console_line_ended = true; + up->console_msr_work_allow = true; + init_irq_work(&up->console_msr_work, console_msr_handler); + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else if (probe) @@ -3460,6 +3619,10 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) int serial8250_console_exit(struct uart_port *port) { + struct uart_8250_port *up = up_to_u8250p(port); + + irq_work_sync(&up->console_msr_work); + if (port->dev) pm_runtime_put_sync(port->dev); diff --git a/drivers/tty/serial/8250/8250_rsa.c b/drivers/tty/serial/8250/8250_rsa.c index fff9395948e3..da971437e89c 100644 --- a/drivers/tty/serial/8250/8250_rsa.c +++ b/drivers/tty/serial/8250/8250_rsa.c @@ -19,35 +19,33 @@ static const struct uart_ops *core_port_base_ops; static int rsa8250_request_resource(struct uart_8250_port *up) { struct uart_port *port = &up->port; - unsigned long start = UART_RSA_BASE << port->regshift; - unsigned int size = 8 << port->regshift; + unsigned long start; + unsigned int size; - switch (port->iotype) { - case UPIO_HUB6: - case UPIO_PORT: - start += port->iobase; - if (!request_region(start, size, "serial-rsa")) - return -EBUSY; - return 0; - default: + if (!uart_iotype_io(port->iotype)) return -EINVAL; - } + + start = UART_RSA_BASE << port->regshift; + start += port->iobase; + size = 8 << port->regshift; + + if (!request_region(start, size, "serial-rsa")) + return -EBUSY; + return 0; } static void rsa8250_release_resource(struct uart_8250_port *up) { struct uart_port *port = &up->port; - unsigned long offset = UART_RSA_BASE << port->regshift; - unsigned int size = 8 << port->regshift; + unsigned long offset; + unsigned int size; - switch (port->iotype) { - case UPIO_HUB6: - case UPIO_PORT: - release_region(port->iobase + offset, size); - break; - default: - break; - } + if (!uart_iotype_io(port->iotype)) + return; + + offset = UART_RSA_BASE << port->regshift; + size = 8 << port->regshift; + release_region(port->iobase + offset, size); } static void univ8250_config_port(struct uart_port *port, int flags) diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index fc3e58d62233..fa6f33303cb0 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -141,6 +141,16 @@ config SERIAL_8250_EXAR 422x PCIe serial cards that are not covered by the more generic SERIAL_8250_PCI option. +config SERIAL_8250_MOXA_PCIE + tristate "8250/16550 Moxa PCIe device support" + depends on SERIAL_8250 && PCI + default SERIAL_8250 + help + Say Y here if you have a Moxa PCIe serial card. + + To compile this driver as a module, choose M here: the + module will be called 8250_mxpcie. + config SERIAL_8250_HP300 tristate depends on SERIAL_8250 && HP300 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 6d21402b4435..46adadcfe072 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o +obj-$(CONFIG_SERIAL_8250_MOXA_PCIE) += 8250_mxpcie.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o obj-$(CONFIG_SERIAL_8250_NI) += 8250_ni.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 9abaeecd05fc..c4824c201e1c 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2543,7 +2543,7 @@ static int pl011_console_setup(struct console *co, char *options) /* Allow pins to be muxed in and configured */ pinctrl_pm_select_default_state(uap->port.dev); - ret = clk_prepare(uap->clk); + ret = clk_prepare_enable(uap->clk); if (ret) return ret; @@ -2572,6 +2572,15 @@ static int pl011_console_setup(struct console *co, char *options) return uart_set_options(&uap->port, co, baud, parity, bits, flow); } +static int pl011_console_exit(struct console *co) +{ + struct uart_amba_port *uap = amba_ports[co->index]; + + clk_disable_unprepare(uap->clk); + + return 0; +} + /** * pl011_console_match - non-standard console matching * @co: registering console @@ -2641,8 +2650,6 @@ pl011_console_write_atomic(struct console *co, struct nbcon_write_context *wctxt if (!nbcon_enter_unsafe(wctxt)) return; - clk_enable(uap->clk); - if (!uap->vendor->always_enabled) { old_cr = pl011_read(uap, REG_CR); pl011_write((old_cr & ~UART011_CR_CTSEN) | (UART01x_CR_UARTEN | UART011_CR_TXE), @@ -2659,8 +2666,6 @@ pl011_console_write_atomic(struct console *co, struct nbcon_write_context *wctxt if (!uap->vendor->always_enabled) pl011_write(old_cr, uap, REG_CR); - clk_disable(uap->clk); - nbcon_exit_unsafe(wctxt); } @@ -2673,8 +2678,6 @@ pl011_console_write_thread(struct console *co, struct nbcon_write_context *wctxt if (!nbcon_enter_unsafe(wctxt)) return; - clk_enable(uap->clk); - if (!uap->vendor->always_enabled) { old_cr = pl011_read(uap, REG_CR); pl011_write((old_cr & ~UART011_CR_CTSEN) | (UART01x_CR_UARTEN | UART011_CR_TXE), @@ -2703,8 +2706,6 @@ pl011_console_write_thread(struct console *co, struct nbcon_write_context *wctxt if (!uap->vendor->always_enabled) pl011_write(old_cr, uap, REG_CR); - clk_disable(uap->clk); - nbcon_exit_unsafe(wctxt); } @@ -2725,6 +2726,7 @@ static struct console amba_console = { .name = "ttyAMA", .device = uart_console_device, .setup = pl011_console_setup, + .exit = pl011_console_exit, .match = pl011_console_match, .write_atomic = pl011_console_write_atomic, .write_thread = pl011_console_write_thread, @@ -3092,21 +3094,45 @@ static void pl011_remove(struct amba_device *dev) static int pl011_suspend(struct device *dev) { struct uart_amba_port *uap = dev_get_drvdata(dev); + int ret; if (!uap) return -EINVAL; - return uart_suspend_port(&amba_reg, &uap->port); + ret = uart_suspend_port(&amba_reg, &uap->port); + if (ret) + return ret; + + if (console_suspend_enabled && uap->port.suspended && + uart_console_registered(&uap->port)) + clk_disable_unprepare(uap->clk); + + return 0; } static int pl011_resume(struct device *dev) { struct uart_amba_port *uap = dev_get_drvdata(dev); + bool resume_console; + int ret; if (!uap) return -EINVAL; - return uart_resume_port(&amba_reg, &uap->port); + resume_console = console_suspend_enabled && + uap->port.suspended && + uart_console_registered(&uap->port); + if (resume_console) { + ret = clk_prepare_enable(uap->clk); + if (ret) + return ret; + } + + ret = uart_resume_port(&amba_reg, &uap->port); + if (ret && resume_console) + clk_disable_unprepare(uap->clk); + + return ret; } #endif diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 544695cb184c..1754cf252b7c 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -838,11 +838,12 @@ static int bcm_uart_probe(struct platform_device *pdev) port->irq = ret; clk = clk_get(&pdev->dev, "refclk"); - if (IS_ERR(clk) && pdev->dev.of_node) - clk = of_clk_get(pdev->dev.of_node, 0); - - if (IS_ERR(clk)) - return -ENODEV; + if (IS_ERR(clk)) { + if (pdev->dev.of_node) + clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(clk)) + return -ENODEV; + } port->iotype = UPIO_MEM; port->ops = &bcm_uart_ops; diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c index b778a20ec9b1..39f54bb7b485 100644 --- a/drivers/tty/serial/cpm_uart.c +++ b/drivers/tty/serial/cpm_uart.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -1530,16 +1529,14 @@ static int cpm_uart_probe(struct platform_device *ofdev) /* initialize the device pointer for the port */ pinfo->port.dev = &ofdev->dev; - pinfo->port.irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); - if (!pinfo->port.irq) - return -EINVAL; + pinfo->port.irq = platform_get_irq(ofdev, 0); + if (pinfo->port.irq < 0) + return pinfo->port.irq; ret = cpm_uart_init_port(ofdev->dev.of_node, pinfo); if (!ret) return uart_add_one_port(&cpm_reg, &pinfo->port); - irq_dispose_mapping(pinfo->port.irq); - return ret; } diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index ab9af37f6cda..ce740cdc7ceb 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -75,19 +75,12 @@ static void __init earlycon_print_info(struct earlycon_device *device) { struct console *earlycon = device->con; struct uart_port *port = &device->port; + char ioinfos[64]; - if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 || - port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE) - pr_info("%s%d at MMIO%s %pa (options '%s')\n", - earlycon->name, earlycon->index, - (port->iotype == UPIO_MEM) ? "" : - (port->iotype == UPIO_MEM16) ? "16" : - (port->iotype == UPIO_MEM32) ? "32" : "32be", - &port->mapbase, device->options); - else - pr_info("%s%d at I/O port 0x%lx (options '%s')\n", - earlycon->name, earlycon->index, - port->iobase, device->options); + uart_get_ioinfos(port, ioinfos, sizeof(ioinfos)); + + pr_info("%s%d%s (options '%s')\n", earlycon->name, earlycon->index, + ioinfos, device->options); } static int __init parse_options(struct earlycon_device *device, char *options) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 251a50c8aa38..513dbe95f7e9 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -2080,6 +2081,9 @@ static const struct uart_ops imx_uart_pops = { static struct imx_port *imx_uart_ports[UART_NR]; +/* Held across uart_add/remove_one_port(); console callbacks must not take it. */ +static DEFINE_MUTEX(imx_uart_ports_lock); + #if IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE) static void imx_uart_console_putchar(struct uart_port *port, unsigned char ch) { @@ -2602,41 +2606,38 @@ static int imx_uart_probe(struct platform_device *pdev) if (txirq > 0) { ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0, dev_name(&pdev->dev), sport); - if (ret) { - dev_err(&pdev->dev, "failed to request rx irq: %d\n", - ret); + if (ret) goto err_clk; - } ret = devm_request_irq(&pdev->dev, txirq, imx_uart_txint, 0, dev_name(&pdev->dev), sport); - if (ret) { - dev_err(&pdev->dev, "failed to request tx irq: %d\n", - ret); + if (ret) goto err_clk; - } ret = devm_request_irq(&pdev->dev, rtsirq, imx_uart_rtsint, 0, dev_name(&pdev->dev), sport); - if (ret) { - dev_err(&pdev->dev, "failed to request rts irq: %d\n", - ret); + if (ret) goto err_clk; - } } else { ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0, dev_name(&pdev->dev), sport); - if (ret) { - dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + if (ret) goto err_clk; - } } - imx_uart_ports[sport->port.line] = sport; - platform_set_drvdata(pdev, sport); - ret = uart_add_one_port(&imx_uart_uart_driver, &sport->port); + scoped_guard(mutex, &imx_uart_ports_lock) { + if (imx_uart_ports[sport->port.line]) { + ret = -EBUSY; + } else { + imx_uart_ports[sport->port.line] = sport; + ret = uart_add_one_port(&imx_uart_uart_driver, + &sport->port); + if (ret) + imx_uart_ports[sport->port.line] = NULL; + } + } err_clk: clk_disable_unprepare(sport->clk_ipg); @@ -2648,7 +2649,9 @@ static void imx_uart_remove(struct platform_device *pdev) { struct imx_port *sport = platform_get_drvdata(pdev); + guard(mutex)(&imx_uart_ports_lock); uart_remove_one_port(&imx_uart_uart_driver, &sport->port); + imx_uart_ports[sport->port.line] = NULL; } static void imx_uart_restore_context(struct imx_port *sport) diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 5a955c80a853..09648d643897 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -363,7 +363,7 @@ static int param_set_kgdboc_var(const char *kmessage, mutex_lock(&config_mutex); - strcpy(config, kmessage); + strscpy(config, kmessage); /* Chop out \n char as a result of echo */ if (len && config[len - 1] == '\n') config[len - 1] = '\0'; diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c index 285b0fe41a86..920fe7ff5083 100644 --- a/drivers/tty/serial/ma35d1_serial.c +++ b/drivers/tty/serial/ma35d1_serial.c @@ -608,8 +608,14 @@ static int __init ma35d1serial_console_setup(struct console *co, char *options) if (!np || !p) return -ENODEV; - if (of_property_read_u32_array(np, "reg", val32, ARRAY_SIZE(val32)) != 0) + if (of_property_read_u32_array(np, "reg", val32, ARRAY_SIZE(val32)) != 0) { + of_node_put(np); + ma35d1serial_uart_nodes[co->index] = NULL; return -EINVAL; + } + + of_node_put(np); + ma35d1serial_uart_nodes[co->index] = NULL; p->port.iobase = val32[1]; p->port.membase = ioremap(p->port.iobase, MA35_UART_REG_SIZE); @@ -648,8 +654,10 @@ static void ma35d1serial_console_init_port(void) of_node_get(np); ma35d1serial_uart_nodes[i] = np; i++; - if (i == MA35_UART_NR) + if (i == MA35_UART_NR) { + of_node_put(np); break; + } } } } diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 44b745fa26c6..a5a5cd68f696 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -724,6 +724,7 @@ static int max3100_probe(struct spi_device *spi) max3100s[i]->port.ops = &max3100_ops; max3100s[i]->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; max3100s[i]->port.line = i; + max3100s[i]->port.iotype = UPIO_BUS; max3100s[i]->port.type = PORT_MAX3100; max3100s[i]->port.dev = &spi->dev; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 5fe12577ce8b..9e62778e4191 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -1400,30 +1399,18 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); + /* + * Set up each port's state before registering the gpiochip, + * since the gpiochip callbacks will read s->p[i].regmap as + * soon as gpiolib exposes the controller. + */ for (i = 0; i < devtype->nr; i++) { - unsigned int line; - - line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX); - if (line == MAX310X_UART_NRMAX) { - ret = -ERANGE; - goto out_uart; - } - - /* Initialize port data */ - s->p[i].port.line = line; s->p[i].port.dev = dev; s->p[i].port.irq = irq; s->p[i].port.type = PORT_MAX310X; s->p[i].port.fifosize = MAX310X_FIFO_SIZE; s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; - s->p[i].port.iotype = UPIO_PORT; - s->p[i].port.iobase = i; - /* - * Use all ones as membase to make sure uart_configure_port() in - * serial_core.c does not abort for SPI/I2C devices where the - * membase address is not applicable. - */ - s->p[i].port.membase = (void __iomem *)~0; + s->p[i].port.iotype = UPIO_BUS; s->p[i].port.uartclk = uartclk; s->p[i].port.rs485_config = max310x_rs485_config; s->p[i].port.rs485_supported = max310x_rs485_supported; @@ -1440,20 +1427,16 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty INIT_WORK(&s->p[i].md_work, max310x_md_proc); /* Initialize queue for changing RS485 mode */ INIT_WORK(&s->p[i].rs_work, max310x_rs_proc); - - /* Register port */ - ret = uart_add_one_port(&max310x_uart, &s->p[i].port); - if (ret) - goto out_uart; - - set_bit(line, max310x_lines); - - /* Go to suspend mode */ - max310x_power(&s->p[i].port, 0); } #ifdef CONFIG_GPIOLIB - /* Setup GPIO controller */ + /* + * Register the GPIO controller before adding the UART ports so + * that consumers referencing the chip's own GPIOs from device + * tree (for example rs485-term-gpios = <&max310x ...>) can + * resolve them at uart_add_one_port() time instead of receiving + * -EPROBE_DEFER from their own provider. + */ s->gpio.owner = THIS_MODULE; s->gpio.parent = dev; s->gpio.label = devtype->name; @@ -1471,6 +1454,64 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty goto out_uart; #endif + for (i = 0; i < devtype->nr; i++) { + struct fwnode_handle *saved_fwnode = dev_fwnode(dev); + struct device_node *port_np = NULL; + struct device_node *child; + unsigned int line; + + line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX); + if (line == MAX310X_UART_NRMAX) { + ret = -ERANGE; + goto out_uart; + } + s->p[i].port.line = line; + + /* Locate the matching "serial@i" DT subnode, if any. */ + for_each_available_child_of_node(dev->of_node, child) { + u32 reg; + + if (!of_node_name_eq(child, "serial")) + continue; + if (of_property_read_u32(child, "reg", ®)) + continue; + if (reg == i) { + port_np = child; + break; + } + } + + /* + * Temporarily retarget dev's fwnode to the per-port subnode + * so uart_get_rs485_mode() picks up the per-port properties. + * For single-port variants, fall back to the chip's own + * fwnode so legacy DTs that declare rs485 properties at the + * top level keep working. + */ + if (port_np) { + device_set_node(dev, of_fwnode_handle(port_np)); + ret = uart_get_rs485_mode(&s->p[i].port); + device_set_node(dev, saved_fwnode); + of_node_put(port_np); + if (ret) + goto out_uart; + } else if (devtype->nr == 1) { + ret = uart_get_rs485_mode(&s->p[i].port); + if (ret) + goto out_uart; + } + + /* Register port */ + ret = uart_add_one_port(&max310x_uart, &s->p[i].port); + if (ret) + goto out_uart; + + set_bit(line, max310x_lines); + + /* Go to suspend mode */ + max310x_power(&s->p[i].port, 0); + } + /* Setup interrupt */ ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist, IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), s); diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 6fad57fee912..9138fa29d301 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -16,6 +16,7 @@ #include #include #include +#include #define MEN_Z135_MAX_PORTS 12 #define MEN_Z135_BASECLK 29491200 @@ -811,7 +812,7 @@ static int men_z135_probe(struct mcb_device *mdev, if (!uart) return -ENOMEM; - uart->rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + uart->rxbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!uart->rxbuf) return -ENOMEM; @@ -841,7 +842,7 @@ static int men_z135_probe(struct mcb_device *mdev, return 0; err: - free_page((unsigned long) uart->rxbuf); + kfree(uart->rxbuf); dev_err(dev, "Failed to add UART: %d\n", err); return err; @@ -858,7 +859,7 @@ static void men_z135_remove(struct mcb_device *mdev) line--; uart_remove_one_port(&men_z135_driver, &uart->port); - free_page((unsigned long) uart->rxbuf); + kfree(uart->rxbuf); } static const struct mcb_device_id men_z135_ids[] = { diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 37eb701b0b46..b566206f42a2 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -645,6 +645,8 @@ static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) /* Check if it is an interrupt for this port */ psc_num = (port->mapbase & 0xf00) >> 8; + if (psc_num >= ARRAY_SIZE(psc_mclk_clk)) + return IRQ_NONE; if (test_bit(psc_num, &fifoc_int) || test_bit(psc_num + 16, &fifoc_int)) return mpc5xxx_uart_process_int(port); @@ -663,6 +665,8 @@ static int mpc512x_psc_alloc_clock(struct uart_port *port) int err; psc_num = (port->mapbase & 0xf00) >> 8; + if (psc_num >= ARRAY_SIZE(psc_mclk_clk)) + return -EINVAL; clk = devm_clk_get(port->dev, "mclk"); if (IS_ERR(clk)) { @@ -711,6 +715,8 @@ static void mpc512x_psc_relse_clock(struct uart_port *port) struct clk *clk; psc_num = (port->mapbase & 0xf00) >> 8; + if (psc_num >= ARRAY_SIZE(psc_mclk_clk)) + return; clk = psc_mclk_clk[psc_num]; if (clk) { clk_disable_unprepare(clk); @@ -733,6 +739,8 @@ static int mpc512x_psc_endis_clock(struct uart_port *port, int enable) return 0; psc_num = (port->mapbase & 0xf00) >> 8; + if (psc_num >= ARRAY_SIZE(psc_mclk_clk)) + return -ENODEV; psc_clk = psc_mclk_clk[psc_num]; if (!psc_clk) { dev_err(port->dev, "Failed to get PSC clock entry!\n"); diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 8e52be2b34ea..33ea496c93b2 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -404,29 +404,21 @@ static int mvebu_uart_startup(struct uart_port *port) ret = devm_request_irq(port->dev, mvuart->irq[UART_IRQ_SUM], mvebu_uart_isr, port->irqflags, dev_name(port->dev), port); - if (ret) { - dev_err(port->dev, "unable to request IRQ %d\n", - mvuart->irq[UART_IRQ_SUM]); + if (ret) return ret; - } } else { /* New bindings with an IRQ for RX and TX (both UART) */ ret = devm_request_irq(port->dev, mvuart->irq[UART_RX_IRQ], mvebu_uart_rx_isr, port->irqflags, dev_name(port->dev), port); - if (ret) { - dev_err(port->dev, "unable to request IRQ %d\n", - mvuart->irq[UART_RX_IRQ]); + if (ret) return ret; - } ret = devm_request_irq(port->dev, mvuart->irq[UART_TX_IRQ], mvebu_uart_tx_isr, port->irqflags, dev_name(port->dev), port); if (ret) { - dev_err(port->dev, "unable to request IRQ %d\n", - mvuart->irq[UART_TX_IRQ]); devm_free_irq(port->dev, mvuart->irq[UART_RX_IRQ], port); return ret; diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 80e31c4d9536..db5c62b0322f 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -678,6 +678,11 @@ static void pch_request_dma(struct uart_port *port) /* Get DMA's dev information */ dma_dev = pci_get_slot(priv->pdev->bus, PCI_DEVFN(PCI_SLOT(priv->pdev->devfn), 0)); + if (!dma_dev) { + dev_err(priv->port.dev, "%s: failed to get DMA device\n", + __func__); + return; + } /* Set Tx DMA */ param = &priv->param_tx; @@ -1662,7 +1667,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, if (priv == NULL) goto init_port_alloc_err; - rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + rxbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!rxbuf) goto init_port_free_txbuf; @@ -1735,7 +1740,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE pch_uart_ports[board->line_no] = NULL; #endif - free_page((unsigned long)rxbuf); + kfree(rxbuf); init_port_free_txbuf: kfree(priv); init_port_alloc_err: @@ -1750,7 +1755,7 @@ static void pch_uart_exit_port(struct eg20t_port *priv) snprintf(name, sizeof(name), "uart%d_regs", priv->port.line); debugfs_lookup_and_remove(name, NULL); uart_remove_one_port(&pch_uart_driver, &priv->port); - free_page((unsigned long)priv->rxbuf.buf); + kfree(priv->rxbuf.buf); } static void pch_uart_pci_remove(struct pci_dev *pdev) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 1e39122ec09f..3633723acef8 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -7,13 +7,19 @@ /* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */ #define __DISABLE_TRACE_MMIO__ +#define CREATE_TRACE_POINTS +#include + #include #include +#include #include #include #include +#include #include #include +#include #include #include #include @@ -84,6 +90,7 @@ #define DEF_TX_WM 2 #define DEF_FIFO_WIDTH_BITS 32 #define UART_RX_WM 2 +#define CONSOLE_TX_CHUNK_SIZE 32 /* SE_UART_LOOPBACK_CFG */ #define RX_TX_SORTED BIT(0) @@ -102,16 +109,14 @@ #define DMA_RX_BUF_SIZE 2048 static DEFINE_IDA(port_ida); -#define DOMAIN_IDX_POWER 0 -#define DOMAIN_IDX_PERF 1 struct qcom_geni_device_data { bool console; enum geni_se_xfer_mode mode; - struct dev_pm_domain_attach_data pd_data; - int (*resources_init)(struct uart_port *uport); - int (*set_rate)(struct uart_port *uport, unsigned int baud); - int (*power_state)(struct uart_port *uport, bool state); + int (*resources_init)(struct geni_se *se); + int (*set_rate)(struct geni_se *se, unsigned long baud); + int (*power_on)(struct geni_se *se); + int (*power_off)(struct geni_se *se); }; struct qcom_geni_private_data { @@ -143,14 +148,16 @@ struct qcom_geni_serial_port { unsigned int tx_remaining; unsigned int tx_queued; + bool tx_dma_stale; int wakeup_irq; bool rx_tx_swap; bool cts_rts_swap; bool manual_flow; - + bool tx_kick_enabled; + struct irq_work tx_kick; struct qcom_geni_private_data private_data; const struct qcom_geni_device_data *dev_data; - struct dev_pm_domain_list *pd_list; + struct notifier_block panic_nb; }; static const struct uart_ops qcom_geni_console_pops; @@ -162,6 +169,9 @@ static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport); static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); static int qcom_geni_serial_port_setup(struct uart_port *uport); +static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport); +static void qcom_geni_serial_resume_tx(struct uart_port *uport); +static void qcom_geni_serial_poll_rx_fifo_locked(struct uart_port *uport); static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport) { @@ -227,7 +237,7 @@ static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags) static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport) { unsigned int mctrl = TIOCM_DSR | TIOCM_CAR; - u32 geni_ios; + u32 geni_ios = 0; if (uart_console(uport)) { mctrl |= TIOCM_CTS; @@ -237,6 +247,8 @@ static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport) mctrl |= TIOCM_CTS; } + trace_geni_serial_get_mctrl(uport->dev, mctrl, geni_ios); + return mctrl; } @@ -255,6 +267,8 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport, if (port->manual_flow && !(mctrl & TIOCM_RTS) && !uport->suspended) uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY; writel(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR); + + trace_geni_serial_set_mctrl(uport->dev, mctrl, uart_manual_rfr); } static const char *qcom_geni_serial_get_type(struct uart_port *uport) @@ -478,7 +492,7 @@ static void qcom_geni_serial_wr_char(struct uart_port *uport, unsigned char ch) static void __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, - unsigned int count) + unsigned int count, bool poll_rx) { struct qcom_geni_private_data *private_data = uport->private_data; @@ -510,6 +524,8 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_TX_FIFO_WATERMARK_EN, true)) break; + if (poll_rx) + qcom_geni_serial_poll_rx_fifo_locked(uport); chars_to_write = min_t(size_t, count - i, avail / 2); uart_console_write(uport, s + i, chars_to_write, qcom_geni_serial_wr_char); @@ -529,49 +545,135 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, qcom_geni_serial_poll_tx_done(uport); } -static void qcom_geni_serial_console_write(struct console *co, const char *s, - unsigned int count) +static void qcom_geni_serial_console_takeover(struct uart_port *uport, + bool preserve_tx) { - struct uart_port *uport; - struct qcom_geni_serial_port *port; - u32 m_irq_en, s_irq_en; - bool locked = true; - unsigned long flags; + if (qcom_geni_serial_main_active(uport)) { + struct qcom_geni_serial_port *port = to_dev_port(uport); - WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); + if (preserve_tx) { + if (port->tx_remaining == 0) + qcom_geni_serial_poll_tx_done(uport); + else + qcom_geni_serial_drain_fifo(uport); + } + + qcom_geni_serial_cancel_tx_cmd(uport); + } +} + +static void qcom_geni_serial_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct qcom_geni_serial_port *port; + struct uart_port *uport; + unsigned int offset = 0; port = get_port_from_line(co->index, true, NULL); if (IS_ERR(port)) return; uport = &port->uport; - if (oops_in_progress) - locked = uart_port_trylock_irqsave(uport, &flags); - else - uart_port_lock_irqsave(uport, &flags); + if (!nbcon_enter_unsafe(wctxt)) + return; - m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); - s_irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); - writel(0, uport->membase + SE_GENI_M_IRQ_EN); - writel(0, uport->membase + SE_GENI_S_IRQ_EN); + qcom_geni_serial_console_takeover(uport, true); + if (!nbcon_exit_unsafe(wctxt)) + return; - if (qcom_geni_serial_main_active(uport)) { - /* Wait for completion or drain FIFO */ - if (!locked || port->tx_remaining == 0) - qcom_geni_serial_poll_tx_done(uport); - else - qcom_geni_serial_drain_fifo(uport); + while (offset < wctxt->len) { + /* + * Printk records can be much larger than the FIFO. Limit one + * M-side command to 32 source bytes so atomic console output can + * take over between commands instead of waiting for the record. + */ + unsigned int count = min_t(unsigned int, wctxt->len - offset, + CONSOLE_TX_CHUNK_SIZE); - qcom_geni_serial_cancel_tx_cmd(uport); + if (!nbcon_enter_unsafe(wctxt)) + return; + + __qcom_geni_serial_console_write(uport, wctxt->outbuf + offset, + count, true); + offset += count; + + if (!nbcon_exit_unsafe(wctxt)) + return; } - __qcom_geni_serial_console_write(uport, s, count); + if (!nbcon_enter_unsafe(wctxt)) + return; + + qcom_geni_serial_resume_tx(uport); + nbcon_exit_unsafe(wctxt); +} + +static void qcom_geni_serial_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct qcom_geni_serial_port *port; + struct uart_port *uport; + u32 m_irq_en; + + port = get_port_from_line(co->index, true, NULL); + if (IS_ERR(port)) + return; + + uport = &port->uport; + if (!nbcon_enter_unsafe(wctxt)) + return; + + m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + writel(m_irq_en & ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN), + uport->membase + SE_GENI_M_IRQ_EN); + + /* Atomic console output takes priority over an active normal TX command. */ + qcom_geni_serial_console_takeover(uport, false); + __qcom_geni_serial_console_write(uport, wctxt->outbuf, wctxt->len, false); writel(m_irq_en, uport->membase + SE_GENI_M_IRQ_EN); - writel(s_irq_en, uport->membase + SE_GENI_S_IRQ_EN); + nbcon_exit_unsafe(wctxt); - if (locked) - uart_port_unlock_irqrestore(uport, flags); + /* Restart TTY data left queued when atomic output canceled M TX. */ + if (READ_ONCE(port->tx_kick_enabled)) + irq_work_queue(&port->tx_kick); +} + +static void qcom_geni_serial_console_device_lock(struct console *co, + unsigned long *flags) +{ + struct qcom_geni_serial_port *port; + + port = get_port_from_line(co->index, true, NULL); + if (IS_ERR(port)) + return; + + __uart_port_lock_irqsave(&port->uport, flags); +} + +static void qcom_geni_serial_console_device_unlock(struct console *co, + unsigned long flags) +{ + struct qcom_geni_serial_port *port; +#ifdef CONFIG_MAGIC_SYSRQ_SERIAL + u8 sysrq_ch; +#endif + + port = get_port_from_line(co->index, true, NULL); + if (IS_ERR(port)) + return; + +#ifdef CONFIG_MAGIC_SYSRQ_SERIAL + /* The threaded console writer can receive a SysRq character. */ + sysrq_ch = port->uport.sysrq_ch; + port->uport.sysrq_ch = 0; +#endif + __uart_port_unlock_irqrestore(&port->uport, flags); + +#ifdef CONFIG_MAGIC_SYSRQ_SERIAL + if (sysrq_ch) + handle_sysrq(sysrq_ch); +#endif } static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) @@ -684,6 +786,8 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); + trace_geni_serial_tx_data(uport->dev, tail, xmit_size); + qcom_geni_set_rs485_mode(uport, SER_RS485_RTS_ON_SEND); qcom_geni_serial_setup_tx(uport, xmit_size); @@ -697,6 +801,7 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) } port->tx_remaining = xmit_size; + port->tx_dma_stale = false; } static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport) @@ -724,6 +829,29 @@ static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport) writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); } +/* Caller holds the UART port lock. */ +static void qcom_geni_serial_resume_tx(struct uart_port *uport) +{ + if (!uart_tx_stopped(uport) && + !kfifo_is_empty(&uport->state->port.xmit_fifo)) + qcom_geni_serial_start_tx_fifo(uport); +} + +static void qcom_geni_serial_restart_tx(struct irq_work *work) +{ + struct qcom_geni_serial_port *port = container_of(work, + struct qcom_geni_serial_port, tx_kick); + struct uart_port *uport = &port->uport; + + if (!READ_ONCE(port->tx_kick_enabled) || !uport->state || uport->suspended) + return; + + uart_port_lock(uport); + if (READ_ONCE(port->tx_kick_enabled) && uport->state && !uport->suspended) + qcom_geni_serial_resume_tx(uport); + uart_port_unlock(uport); +} + static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport) { u32 irq_en; @@ -786,6 +914,34 @@ static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop) handle_rx_console(uport, total_bytes, drop); } +/* Caller holds the UART port lock. */ +static void qcom_geni_serial_poll_rx_fifo_locked(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + struct tty_port *tport = &uport->state->port; + u32 s_irq_status; + bool drop_rx = false; + + s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); + writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); + + if (s_irq_status & S_RX_FIFO_WR_ERR_EN) { + uport->icount.overrun++; + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + } + + 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_GP_IRQ_3_EN)) { + uport->icount.brk++; + port->brk = true; + } + + qcom_geni_serial_handle_rx_fifo(uport, drop_rx); +} + static void qcom_geni_serial_stop_rx_fifo(struct uart_port *uport) { u32 irq_en; @@ -796,10 +952,6 @@ static void qcom_geni_serial_stop_rx_fifo(struct uart_port *uport) 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); - if (!qcom_geni_serial_secondary_active(uport)) return; @@ -833,10 +985,6 @@ static void qcom_geni_serial_start_rx_fifo(struct uart_port *uport) 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) @@ -863,37 +1011,31 @@ static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport) uport->membase + SE_DMA_RX_IRQ_CLR); } - 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); + if (!port->rx_dma_addr) { + dev_err(uport->dev, "RX DMA buffer not mapped\n"); + return; } + + dma_sync_single_for_device(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + geni_se_rx_init_dma(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); } 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; @@ -901,22 +1043,20 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) 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; + dma_sync_single_for_cpu(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN); if (!rx_in) dev_warn_ratelimited(uport->dev, "serial engine reports 0 RX bytes in!\n"); - else if (!drop) + else if (!drop) { + trace_geni_serial_rx_data(uport->dev, port->rx_buf, rx_in); handle_rx_uart(uport, rx_in); - - 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); } + + dma_sync_single_for_device(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + geni_se_rx_init_dma(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); } static void qcom_geni_serial_start_rx(struct uart_port *uport) @@ -1029,6 +1169,7 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; unsigned int fifo_len = kfifo_len(&tport->xmit_fifo); + bool tx_dma_stale = port->tx_dma_stale; /* * Only advance the kfifo if it still contains the bytes that were @@ -1039,12 +1180,13 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) * kfifo->in, making kfifo_len() wrap to UART_XMIT_SIZE - tx_remaining * and triggering a spurious large DMA transfer of stale data. */ - if (fifo_len >= port->tx_remaining) + if (!tx_dma_stale && fifo_len >= port->tx_remaining) 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; + port->tx_dma_stale = false; if (!kfifo_is_empty(&tport->xmit_fifo)) qcom_geni_serial_start_tx_dma(uport); @@ -1072,21 +1214,11 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) uart_port_lock(uport); - 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; - if (s_irq_status & S_RX_FIFO_WR_ERR_EN) { uport->icount.overrun++; tty_insert_flip_char(tport, 0, TTY_OVERRUN); @@ -1101,12 +1233,35 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) port->brk = true; } + m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); + dma_tx_status = readl(uport->membase + SE_DMA_TX_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); + + trace_geni_serial_irq(uport->dev, m_irq_status, s_irq_status, + dma_tx_status, dma_rx_status); + + writel(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); + writel(dma_tx_status, uport->membase + SE_DMA_TX_IRQ_CLR); + + if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN)) + goto handle_rx; + if (dma) { if (dma_tx_status & TX_DMA_DONE) { qcom_geni_serial_handle_tx_dma(uport); qcom_geni_set_rs485_mode(uport, SER_RS485_RTS_AFTER_SEND); + } + } 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); } +handle_rx: + if (dma) { if (dma_rx_status) { if (dma_rx_status & RX_RESET_DONE) goto out_unlock; @@ -1123,12 +1278,6 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) 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); } @@ -1170,6 +1319,11 @@ static int setup_fifos(struct qcom_geni_serial_port *port) static void qcom_geni_serial_shutdown(struct uart_port *uport) { + struct qcom_geni_serial_port *port = to_dev_port(uport); + + /* Atomic console output queues tx_kick without taking the port lock. */ + WRITE_ONCE(port->tx_kick_enabled, false); + irq_work_sync(&port->tx_kick); disable_irq(uport->irq); uart_port_lock_irq(uport); @@ -1178,10 +1332,16 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) qcom_geni_serial_cancel_tx_cmd(uport); uart_port_unlock_irq(uport); + + pm_runtime_put_sync(uport->dev); } static void qcom_geni_serial_flush_buffer_fifo(struct uart_port *uport) { + struct qcom_geni_serial_port *port = to_dev_port(uport); + + if (port->tx_dma_addr) + port->tx_dma_stale = true; qcom_geni_serial_cancel_tx_cmd(uport); } @@ -1245,25 +1405,43 @@ static int qcom_geni_serial_startup(struct uart_port *uport) { int ret; struct qcom_geni_serial_port *port = to_dev_port(uport); + struct tty_port *tport = &uport->state->port; + + ret = pm_runtime_resume_and_get(uport->dev); + if (ret < 0) { + dev_err(uport->dev, "Failed to resume and get %d\n", ret); + return ret; + } if (!port->setup) { ret = qcom_geni_serial_port_setup(uport); - if (ret) + if (ret) { + pm_runtime_put_sync(uport->dev); return ret; + } } + /* + * Skip the close-time transmit drain for console ports so that + * shutdown can proceed without waiting for pending TX completion. + */ + if (uart_console(uport)) + tport->closing_wait = ASYNC_CLOSING_WAIT_NONE; + uart_port_lock_irq(uport); qcom_geni_serial_start_rx(uport); uart_port_unlock_irq(uport); enable_irq(uport->irq); + WRITE_ONCE(port->tx_kick_enabled, true); return 0; } -static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud) +static int geni_serial_set_rate(struct geni_se *se, unsigned long baud) { - struct qcom_geni_serial_port *port = to_dev_port(uport); + struct qcom_geni_serial_port *port = dev_get_drvdata(se->dev); + struct uart_port *uport = &port->uport; unsigned long clk_rate; unsigned int avg_bw_core, clk_idx; unsigned int clk_div; @@ -1279,7 +1457,7 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud) ret = geni_se_clk_freq_match(&port->se, baud * sampling_rate, &clk_idx, &clk_rate, false); if (ret) { - dev_err(port->se.dev, "Failed to find src clk for baud rate: %d ret: %d\n", + dev_err(port->se.dev, "Failed to find src clk for baud rate: %lu ret: %d\n", baud, ret); return ret; } @@ -1291,8 +1469,8 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud) return -EINVAL; } - dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u, clk_idx = %u\n", - baud * sampling_rate, clk_rate, clk_div, clk_idx); + trace_geni_serial_clk_cfg(uport->dev, baud * sampling_rate, clk_rate, + clk_div, clk_idx); uport->uartclk = clk_rate; port->clk_rate = clk_rate; @@ -1317,42 +1495,6 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud) return 0; } -static int geni_serial_set_level(struct uart_port *uport, unsigned int baud) -{ - struct qcom_geni_serial_port *port = to_dev_port(uport); - struct device *perf_dev = port->pd_list->pd_devs[DOMAIN_IDX_PERF]; - - /* - * The performance protocol sets UART communication - * speeds by selecting different performance levels - * through the OPP framework. - * - * Supported perf levels for baudrates in firmware are below - * +---------------------+--------------------+ - * | Perf level value | Baudrate values | - * +---------------------+--------------------+ - * | 300 | 300 | - * | 1200 | 1200 | - * | 2400 | 2400 | - * | 4800 | 4800 | - * | 9600 | 9600 | - * | 19200 | 19200 | - * | 38400 | 38400 | - * | 57600 | 57600 | - * | 115200 | 115200 | - * | 230400 | 230400 | - * | 460800 | 460800 | - * | 921600 | 921600 | - * | 2000000 | 2000000 | - * | 3000000 | 3000000 | - * | 3200000 | 3200000 | - * | 4000000 | 4000000 | - * +---------------------+--------------------+ - */ - - return dev_pm_opp_set_level(perf_dev, baud); -} - static void qcom_geni_serial_set_termios(struct uart_port *uport, struct ktermios *termios, const struct ktermios *old) @@ -1371,7 +1513,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, /* baud rate */ baud = uart_get_baud_rate(uport, termios, old, 300, 8000000); - ret = port->dev_data->set_rate(uport, baud); + ret = port->dev_data->set_rate(&port->se, baud); if (ret) return; @@ -1452,6 +1594,10 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, writel(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN); writel(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN); writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); + + trace_geni_serial_set_termios(uport->dev, baud, bits_per_char, + tx_trans_cfg, tx_parity_cfg, rx_trans_cfg, + rx_parity_cfg, stop_bit_len); } #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE @@ -1496,7 +1642,7 @@ static void qcom_geni_serial_earlycon_write(struct console *con, { struct earlycon_device *dev = con->data; - __qcom_geni_serial_console_write(&dev->port, s, n); + __qcom_geni_serial_console_write(&dev->port, s, n, false); } #ifdef CONFIG_CONSOLE_POLL @@ -1599,10 +1745,13 @@ static void console_unregister(struct uart_driver *drv) static struct console cons_ops = { .name = "ttyMSM", - .write = qcom_geni_serial_console_write, + .write_atomic = qcom_geni_serial_console_write_atomic, + .write_thread = qcom_geni_serial_console_write_thread, + .device_lock = qcom_geni_serial_console_device_lock, + .device_unlock = qcom_geni_serial_console_device_unlock, .device = uart_console_device, .setup = qcom_geni_console_setup, - .flags = CON_PRINTBUFFER, + .flags = CON_PRINTBUFFER | CON_NBCON, .index = -1, .data = &qcom_geni_console_driver, }; @@ -1632,114 +1781,6 @@ static struct uart_driver qcom_geni_uart_driver = { .nr = CONFIG_SERIAL_QCOM_GENI_UART_PORTS, }; -static int geni_serial_resources_on(struct uart_port *uport) -{ - struct qcom_geni_serial_port *port = to_dev_port(uport); - int ret; - - ret = geni_icc_enable(&port->se); - if (ret) - return ret; - - ret = geni_se_resources_on(&port->se); - if (ret) { - geni_icc_disable(&port->se); - return ret; - } - - if (port->clk_rate) - dev_pm_opp_set_rate(uport->dev, port->clk_rate); - - return 0; -} - -static int geni_serial_resources_off(struct uart_port *uport) -{ - struct qcom_geni_serial_port *port = to_dev_port(uport); - int ret; - - dev_pm_opp_set_rate(uport->dev, 0); - ret = geni_se_resources_off(&port->se); - if (ret) - return ret; - - geni_icc_disable(&port->se); - - return 0; -} - -static int geni_serial_resource_state(struct uart_port *uport, bool power_on) -{ - return power_on ? geni_serial_resources_on(uport) : geni_serial_resources_off(uport); -} - -static int geni_serial_pwr_init(struct uart_port *uport) -{ - struct qcom_geni_serial_port *port = to_dev_port(uport); - int ret; - - ret = dev_pm_domain_attach_list(port->se.dev, - &port->dev_data->pd_data, &port->pd_list); - if (ret <= 0) - return -EINVAL; - - return 0; -} - -static int geni_serial_resource_init(struct uart_port *uport) -{ - struct qcom_geni_serial_port *port = to_dev_port(uport); - int ret; - - port->se.clk = devm_clk_get(port->se.dev, "se"); - if (IS_ERR(port->se.clk)) { - ret = PTR_ERR(port->se.clk); - dev_err(port->se.dev, "Err getting SE Core clk %d\n", ret); - return ret; - } - - ret = geni_icc_get(&port->se, NULL); - if (ret) - return ret; - - port->se.icc_paths[GENI_TO_CORE].avg_bw = GENI_DEFAULT_BW; - port->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW; - - /* Set BW for register access */ - ret = geni_icc_set_bw(&port->se); - if (ret) - return ret; - - ret = devm_pm_opp_set_clkname(port->se.dev, "se"); - if (ret) - return ret; - - /* OPP table is optional */ - ret = devm_pm_opp_of_add_table(port->se.dev); - if (ret && ret != -ENODEV) { - dev_err(port->se.dev, "invalid OPP table in device tree\n"); - return ret; - } - - return 0; -} - -static void qcom_geni_serial_pm(struct uart_port *uport, - unsigned int new_state, unsigned int old_state) -{ - - /* If we've never been called, treat it as off */ - if (old_state == UART_PM_STATE_UNDEFINED) - old_state = UART_PM_STATE_OFF; - - if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) - pm_runtime_resume_and_get(uport->dev); - else if (new_state == UART_PM_STATE_OFF && - old_state == UART_PM_STATE_ON) - pm_runtime_put_sync(uport->dev); - -} - /** * qcom_geni_rs485_config - Configure RS485 settings for the UART port * @uport: Pointer to the UART port structure @@ -1778,7 +1819,6 @@ static const struct uart_ops qcom_geni_console_pops = { .poll_put_char = qcom_geni_serial_poll_put_char, .poll_init = qcom_geni_serial_poll_init, #endif - .pm = qcom_geni_serial_pm, }; static const struct uart_ops qcom_geni_uart_pops = { @@ -1796,9 +1836,24 @@ static const struct uart_ops qcom_geni_uart_pops = { .type = qcom_geni_serial_get_type, .set_mctrl = qcom_geni_serial_set_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl, - .pm = qcom_geni_serial_pm, }; +static int qcom_geni_serial_panic_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct qcom_geni_serial_port *port = + container_of(nb, struct qcom_geni_serial_port, panic_nb); + struct uart_port *uport = &port->uport; + + if (pm_runtime_status_suspended(uport->dev)) + return NOTIFY_OK; + + qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_rx(uport); + + return NOTIFY_OK; +} + static int qcom_geni_serial_probe(struct platform_device *pdev) { int ret = 0; @@ -1840,7 +1895,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) port->se.dev = &pdev->dev; port->se.wrapper = dev_get_drvdata(pdev->dev.parent); - ret = port->dev_data->resources_init(uport); + ret = port->dev_data->resources_init(&port->se); if (ret) return ret; @@ -1865,6 +1920,14 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) ret = -ENOMEM; goto error; } + + port->rx_dma_addr = dma_map_single(pdev->dev.parent, port->rx_buf, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(pdev->dev.parent, port->rx_dma_addr)) { + ret = -EIO; + dev_err(&pdev->dev, "Failed to map RX DMA buffer: %d\n", ret); + goto error; + } } port->name = devm_kasprintf(uport->dev, GFP_KERNEL, @@ -1895,6 +1958,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) port->private_data.drv = drv; uport->private_data = &port->private_data; + init_irq_work(&port->tx_kick, qcom_geni_serial_restart_tx); platform_set_drvdata(pdev, port); irq_set_status_flags(uport->irq, IRQ_NOAUTOEN); @@ -1922,14 +1986,29 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) devm_pm_runtime_enable(port->se.dev); + ret = pm_runtime_resume_and_get(uport->dev); + if (ret < 0) { + dev_err(uport->dev, "Failed to resume and get %d\n", ret); + goto error; + } + ret = uart_add_one_port(drv, uport); + pm_runtime_put(uport->dev); if (ret) goto error; + port->panic_nb.notifier_call = qcom_geni_serial_panic_notifier; + atomic_notifier_chain_register(&panic_notifier_list, &port->panic_nb); + return 0; error: - dev_pm_domain_detach_list(port->pd_list); + if (port->rx_dma_addr) { + dma_unmap_single(pdev->dev.parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + port->rx_dma_addr = 0; + } + dev_pm_domain_detach_list(port->se.pd_list); return ret; } @@ -1939,35 +2018,48 @@ static void qcom_geni_serial_remove(struct platform_device *pdev) struct uart_port *uport = &port->uport; struct uart_driver *drv = port->private_data.drv; + atomic_notifier_chain_unregister(&panic_notifier_list, &port->panic_nb); + + WRITE_ONCE(port->tx_kick_enabled, false); + irq_work_sync(&port->tx_kick); dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); ida_free(&port_ida, uport->line); uart_remove_one_port(drv, &port->uport); - dev_pm_domain_detach_list(port->pd_list); + + if (port->rx_dma_addr) { + dma_unmap_single(pdev->dev.parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + port->rx_dma_addr = 0; + } + + dev_pm_domain_detach_list(port->se.pd_list); } static int __maybe_unused qcom_geni_serial_runtime_suspend(struct device *dev) { struct qcom_geni_serial_port *port = dev_get_drvdata(dev); - struct uart_port *uport = &port->uport; - int ret = 0; - if (port->dev_data->power_state) - ret = port->dev_data->power_state(uport, false); - - return ret; + return port->dev_data->power_off ? + port->dev_data->power_off(&port->se) : 0; } static int __maybe_unused qcom_geni_serial_runtime_resume(struct device *dev) { struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; - int ret = 0; + int ret; - if (port->dev_data->power_state) - ret = port->dev_data->power_state(uport, true); + if (port->dev_data->power_on) { + ret = port->dev_data->power_on(&port->se); + if (ret) + return ret; + } - return ret; + if (port->se.has_opp && port->clk_rate) + return dev_pm_opp_set_rate(uport->dev, port->clk_rate); + + return 0; } static int qcom_geni_serial_suspend(struct device *dev) @@ -1975,6 +2067,7 @@ static int qcom_geni_serial_suspend(struct device *dev) struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; struct qcom_geni_private_data *private_data = uport->private_data; + int ret; /* * This is done so we can hit the lowest possible state in suspend @@ -1984,7 +2077,19 @@ static int qcom_geni_serial_suspend(struct device *dev) geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ACTIVE_ONLY); geni_icc_set_bw(&port->se); } - return uart_suspend_port(private_data->drv, uport); + + ret = uart_suspend_port(private_data->drv, uport); + if (ret) + return ret; + + /* + * When no_console_suspend is set the console must remain active + * across system sleep, so skip the force suspend path. + */ + if (!console_suspend_enabled && uart_console(uport)) + return 0; + + return pm_runtime_force_suspend(dev); } static int qcom_geni_serial_resume(struct device *dev) @@ -1994,6 +2099,10 @@ static int qcom_geni_serial_resume(struct device *dev) struct uart_port *uport = &port->uport; struct qcom_geni_private_data *private_data = uport->private_data; + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + ret = uart_resume_port(private_data->drv, uport); if (uart_console(uport)) { geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ALWAYS); @@ -2006,42 +2115,34 @@ static int qcom_geni_serial_resume(struct device *dev) static const struct qcom_geni_device_data qcom_geni_console_data = { .console = true, .mode = GENI_SE_FIFO, - .resources_init = geni_serial_resource_init, + .resources_init = geni_se_resources_init, .set_rate = geni_serial_set_rate, - .power_state = geni_serial_resource_state, + .power_on = geni_se_resources_activate, + .power_off = geni_se_resources_deactivate, }; static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = { .console = true, .mode = GENI_SE_FIFO, - .pd_data = { - .pd_flags = PD_FLAG_DEV_LINK_ON, - .pd_names = (const char*[]) { "power", "perf" }, - .num_pd_names = 2, - }, - .resources_init = geni_serial_pwr_init, - .set_rate = geni_serial_set_level, + .resources_init = geni_se_domain_attach, + .set_rate = geni_se_set_perf_level, }; #endif static const struct qcom_geni_device_data qcom_geni_uart_data = { .console = false, .mode = GENI_SE_DMA, - .resources_init = geni_serial_resource_init, + .resources_init = geni_se_resources_init, .set_rate = geni_serial_set_rate, - .power_state = geni_serial_resource_state, + .power_on = geni_se_resources_activate, + .power_off = geni_se_resources_deactivate, }; static const struct qcom_geni_device_data sa8255p_qcom_geni_uart_data = { .console = false, .mode = GENI_SE_DMA, - .pd_data = { - .pd_flags = PD_FLAG_DEV_LINK_ON, - .pd_names = (const char*[]) { "power", "perf" }, - .num_pd_names = 2, - }, - .resources_init = geni_serial_pwr_init, - .set_rate = geni_serial_set_level, + .resources_init = geni_se_domain_attach, + .set_rate = geni_se_set_perf_level, }; static const struct dev_pm_ops qcom_geni_serial_pm_ops = { @@ -2050,6 +2151,18 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = { SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_suspend, qcom_geni_serial_resume) }; +static void qcom_geni_serial_sys_shutdown(struct platform_device *pdev) +{ + struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + struct uart_port *uport = &port->uport; + + if (pm_runtime_status_suspended(uport->dev)) + return; + + qcom_geni_serial_stop_tx(uport); + qcom_geni_serial_stop_rx(uport); +} + static const struct of_device_id qcom_geni_serial_match_table[] = { #if IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE) { @@ -2076,6 +2189,7 @@ MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); static struct platform_driver qcom_geni_serial_platform_driver = { .remove = qcom_geni_serial_remove, .probe = qcom_geni_serial_probe, + .shutdown = qcom_geni_serial_sys_shutdown, .driver = { .name = "qcom_geni_serial", .of_match_table = qcom_geni_serial_match_table, diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 6d99a02dd439..51e81ec0ffdb 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -197,7 +197,7 @@ struct rp2_card { }; #define RP_ID(prod) PCI_VDEVICE(RP, (prod)) -#define RP_CAP(ports, smpte) (((ports) << 8) | ((smpte) << 0)) +#define RP_CAP(ports, smpte) .driver_data = (((ports) << 8) | ((smpte) << 0)) static inline void rp2_decode_cap(const struct pci_device_id *id, int *ports, int *smpte) diff --git a/drivers/tty/serial/rsci.c b/drivers/tty/serial/rsci.c index b00c9e385169..40db9daa4272 100644 --- a/drivers/tty/serial/rsci.c +++ b/drivers/tty/serial/rsci.c @@ -265,8 +265,6 @@ static void rsci_set_termios(struct uart_port *port, struct ktermios *termios, } baud = uart_get_baud_rate(port, termios, old, 0, max_freq); - if (!baud) - goto done; /* Divided Functional Clock using standard Bit Rate Register */ err = sci_scbrr_calc(s, baud, &brr1, &srr1, &cks1); @@ -278,7 +276,6 @@ static void rsci_set_termios(struct uart_port *port, struct ktermios *termios, cks = cks1; } -done: if (best_clk >= 0) dev_dbg(port->dev, "Using clk %pC for %u%+d bps\n", s->clks[best_clk], baud, min_err); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 9b152ead050f..7107a0fb1e7b 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1486,14 +1486,7 @@ static int sc16is7xx_setup_channel(struct sc16is7xx_one *one, int i, port->type = PORT_SC16IS7XX; port->fifosize = SC16IS7XX_FIFO_SIZE; port->flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; - port->iobase = i; - /* - * Use all ones as membase to make sure uart_configure_port() in - * serial_core.c does not abort for SPI/I2C devices where the - * membase address is not applicable. - */ - port->membase = (void __iomem *)~0; - port->iotype = UPIO_PORT; + port->iotype = UPIO_BUS; port->rs485_config = sc16is7xx_config_rs485; port->rs485_supported = sc16is7xx_rs485_supported; port->ops = &sc16is7xx_ops; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a530ad372b43..95774b0f1484 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -33,6 +33,7 @@ #include #include "serial_base.h" +#include "8250/8250.h" /* For hub6_match_port() */ /* * This is used to lock changes in serial line configuration. @@ -1962,6 +1963,34 @@ static const char *uart_type(struct uart_port *port) return str; } +bool uart_iotype_mmio(enum uart_iotype iotype) +{ + switch (iotype) { + case UPIO_MEM: + case UPIO_MEM32: + case UPIO_AU: + case UPIO_TSI: + case UPIO_MEM32BE: + case UPIO_MEM16: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(uart_iotype_mmio); + +bool uart_iotype_io(enum uart_iotype iotype) +{ + switch (iotype) { + case UPIO_PORT: + case UPIO_HUB6: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(uart_iotype_io); + #ifdef CONFIG_PROC_FS static void uart_line_info(struct seq_file *m, struct uart_state *state) @@ -1969,9 +1998,9 @@ static void uart_line_info(struct seq_file *m, struct uart_state *state) struct tty_port *port = &state->port; enum uart_pm_state pm_state; struct uart_port *uport; + char ioinfos[64]; char stat_buf[32]; unsigned int status; - int mmio; guard(mutex)(&port->mutex); @@ -1979,13 +2008,10 @@ static void uart_line_info(struct seq_file *m, struct uart_state *state) if (!uport) return; - mmio = uport->iotype >= UPIO_MEM; - seq_printf(m, "%u: uart:%s %s%08llX irq:%u", - uport->line, uart_type(uport), - mmio ? "mmio:0x" : "port:", - mmio ? (unsigned long long)uport->mapbase - : (unsigned long long)uport->iobase, - uport->irq); + seq_printf(m, "%u: uart:%s", uport->line, uart_type(uport)); + uart_get_ioinfos(uport, ioinfos, sizeof(ioinfos)); + seq_printf(m, "%s", ioinfos); + seq_printf(m, " irq:%u", uport->irq); if (uport->type == PORT_UNKNOWN) { seq_putc(m, '\n'); @@ -2459,38 +2485,47 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) } EXPORT_SYMBOL(uart_resume_port); +static const char *uart_get_mmio_width(struct uart_port *port) +{ + switch (port->iotype) { + case UPIO_MEM16: + return "16"; + case UPIO_MEM32: + case UPIO_MEM32BE: + return "32"; + case UPIO_AU: + case UPIO_MEM: + default: + return ""; + } +} + +void uart_get_ioinfos(struct uart_port *port, char *buf, size_t size) +{ + buf[0] = '\0'; + + if (uart_iotype_mmio(port->iotype)) { + scnprintf(buf, size, " MMIO%s:%pa", uart_get_mmio_width(port), &port->mapbase); + } else if (uart_iotype_io(port->iotype)) { + if (port->iotype == UPIO_PORT) + scnprintf(buf, size, " I/O:0x%lx", port->iobase); + else if (port->iotype == UPIO_HUB6) + scnprintf(buf, size, " I/O:0x%lx, offset 0x%x", port->iobase, port->hub6); + } +} +EXPORT_SYMBOL(uart_get_ioinfos); + static inline void uart_report_port(struct uart_driver *drv, struct uart_port *port) { - char address[64]; + char ioinfos[64]; - switch (port->iotype) { - case UPIO_PORT: - snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase); - break; - case UPIO_HUB6: - snprintf(address, sizeof(address), - "I/O 0x%lx offset 0x%x", port->iobase, port->hub6); - break; - case UPIO_MEM: - case UPIO_MEM16: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_AU: - case UPIO_TSI: - snprintf(address, sizeof(address), - "MMIO 0x%llx", (unsigned long long)port->mapbase); - break; - default: - strscpy(address, "*unknown*", sizeof(address)); - break; - } + uart_get_ioinfos(port, ioinfos, sizeof(ioinfos)); - pr_info("%s%s%s at %s (irq = %u, base_baud = %u) is a %s\n", - port->dev ? dev_name(port->dev) : "", - port->dev ? ": " : "", - port->name, - address, port->irq, port->uartclk / 16, uart_type(port)); + pr_info("%s%s%s%s (irq = %u, base_baud = %u) is a %s\n", + port->dev ? dev_name(port->dev) : "", + port->dev ? ": " : "", + port->name, ioinfos, port->irq, port->uartclk / 16, uart_type(port)); /* The magic multiplier feature is a bit obscure, so report it too. */ if (port->flags & UPF_MAGIC_MULTIPLIER) @@ -2507,11 +2542,10 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, { unsigned int flags; - /* - * If there isn't a port here, don't do anything further. - */ - if (!port->iobase && !port->mapbase && !port->membase) - return; + /* If there isn't a port here, don't do anything further. */ + if (uart_iotype_mmio(port->iotype) || uart_iotype_io(port->iotype)) + if (!port->iobase && !port->mapbase && !port->membase) + return; /* * Now do the auto configuration stuff. Note that config_port @@ -2777,8 +2811,10 @@ int uart_register_driver(struct uart_driver *drv) for (i = 0; i < drv->nr; i++) tty_port_destroy(&drv->state[i].port); tty_driver_kref_put(normal); + drv->tty_driver = NULL; out_kfree: kfree(drv->state); + drv->state = NULL; out: return retval; } @@ -3056,7 +3092,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u struct uart_state *state; struct tty_port *port; struct device *tty_dev; - int num_groups; if (uport->line >= drv->nr) return -EINVAL; @@ -3068,6 +3103,22 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u if (state->uart_port) return -EINVAL; + uport->name = kasprintf(GFP_KERNEL, "%s%u", drv->dev_name, + drv->tty_driver->name_base + uport->line); + if (!uport->name) + return -ENOMEM; + + /* + * uart_configure_port() may set uport->attr_group and register the + * console. Allocate room for both groups and a NULL terminator first. + */ + uport->tty_groups = kzalloc_objs(*uport->tty_groups, 3); + if (!uport->tty_groups) { + kfree(uport->name); + return -ENOMEM; + } + uport->tty_groups[0] = &tty_dev_attr_group; + /* Link the port to the driver state table and vice versa */ atomic_set(&state->refcount, 1); init_waitqueue_head(&state->remove_wait); @@ -3084,10 +3135,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u state->pm_state = UART_PM_STATE_UNDEFINED; uart_port_set_cons(uport, drv->cons); uport->minor = drv->tty_driver->minor_start + uport->line; - uport->name = kasprintf(GFP_KERNEL, "%s%u", drv->dev_name, - drv->tty_driver->name_base + uport->line); - if (!uport->name) - return -ENOMEM; if (uport->cons && uport->dev) of_console_check(uport->dev->of_node, uport->cons->name, uport->line); @@ -3102,15 +3149,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u port->console = uart_console(uport); - num_groups = 2; - if (uport->attr_group) - num_groups++; - - uport->tty_groups = kzalloc_objs(*uport->tty_groups, num_groups); - if (!uport->tty_groups) - return -ENOMEM; - - uport->tty_groups[0] = &tty_dev_attr_group; if (uport->attr_group) uport->tty_groups[1] = uport->attr_group; @@ -3208,23 +3246,16 @@ bool uart_match_port(const struct uart_port *port1, { if (port1->iotype != port2->iotype) return false; - - switch (port1->iotype) { - case UPIO_PORT: + else if (port1->iotype == UPIO_PORT) return port1->iobase == port2->iobase; - case UPIO_HUB6: - return port1->iobase == port2->iobase && - port1->hub6 == port2->hub6; - case UPIO_MEM: - case UPIO_MEM16: - case UPIO_MEM32: - case UPIO_MEM32BE: - case UPIO_AU: - case UPIO_TSI: + else if (port1->iotype == UPIO_HUB6) + return hub6_match_port(port1, port2); + else if (uart_iotype_mmio(port1->iotype)) return port1->mapbase == port2->mapbase; - default: + else if (port1->iotype == UPIO_BUS) + return true; + else return false; - } } EXPORT_SYMBOL(uart_match_port); diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 7b02c5ca4afd..dff13a9f2614 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -256,9 +256,6 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx) gpios); if (ret) { /* alternatively implement polling */ - dev_err(port->dev, - "failed to request irq for %s (idx=%d, err=%d)\n", - mctrl_gpios_desc[i].name, idx, ret); return ERR_PTR(ret); } } diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 787e7cdc5e9c..92193eb26abf 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2719,8 +2719,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, max_freq = max(max_freq, s->clk_rates[i]); baud = uart_get_baud_rate(port, termios, old, 0, max_freq / min_sr(s)); - if (!baud) - goto done; /* * There can be multiple sources for the sampling clock. Find the one diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 092755f35683..3fdf5939a62c 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -741,11 +741,8 @@ static int sprd_startup(struct uart_port *port) ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq, IRQF_SHARED, sp->name, port); - if (ret) { - dev_err(port->dev, "fail to request serial irq %d, ret=%d\n", - port->irq, ret); + if (ret) return ret; - } fc = serial_in(port, SPRD_CTL1); fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; serial_out(port, SPRD_CTL1, fc); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a77bbccb274d..48569035da56 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3140,8 +3140,10 @@ static int tty_cdev_add(struct tty_driver *driver, dev_t dev, driver->cdevs[index]->ops = &tty_fops; driver->cdevs[index]->owner = driver->owner; err = cdev_add(driver->cdevs[index], dev, count); - if (err) + if (err) { kobject_put(&driver->cdevs[index]->kobj); + driver->cdevs[index] = NULL; + } return err; } @@ -3278,7 +3280,7 @@ EXPORT_SYMBOL_GPL(tty_register_device_attr); void tty_unregister_device(struct tty_driver *driver, unsigned index) { device_destroy(&tty_class, MKDEV(driver->major, driver->minor_start) + index); - if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { + if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) && driver->cdevs[index]) { cdev_del(driver->cdevs[index]); driver->cdevs[index] = NULL; } diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c index ef8741c3e662..37929fb56174 100644 --- a/drivers/tty/tty_jobctrl.c +++ b/drivers/tty/tty_jobctrl.c @@ -506,29 +506,23 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t if (pgrp_nr < 0) return -EINVAL; - spin_lock_irq(&real_tty->ctrl.lock); + guard(spinlock_irq)(&real_tty->ctrl.lock); if (!current->signal->tty || (current->signal->tty != real_tty) || - (real_tty->ctrl.session != task_session(current))) { - retval = -ENOTTY; - goto out_unlock_ctrl; - } - rcu_read_lock(); + (real_tty->ctrl.session != task_session(current))) + return -ENOTTY; + + guard(rcu)(); pgrp = find_vpid(pgrp_nr); - retval = -ESRCH; if (!pgrp) - goto out_unlock; - retval = -EPERM; + return -ESRCH; if (session_of_pgrp(pgrp) != task_session(current)) - goto out_unlock; - retval = 0; + return -EPERM; + put_pid(real_tty->ctrl.pgrp); real_tty->ctrl.pgrp = get_pid(pgrp); -out_unlock: - rcu_read_unlock(); -out_unlock_ctrl: - spin_unlock_irq(&real_tty->ctrl.lock); - return retval; + + return 0; } /** @@ -542,7 +536,6 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t */ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) { - unsigned long flags; pid_t sid; /* @@ -552,17 +545,13 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; - spin_lock_irqsave(&real_tty->ctrl.lock, flags); - if (!real_tty->ctrl.session) - goto err; - sid = pid_vnr(real_tty->ctrl.session); - spin_unlock_irqrestore(&real_tty->ctrl.lock, flags); + scoped_guard(spinlock_irqsave, &real_tty->ctrl.lock) { + if (!real_tty->ctrl.session) + return -ENOTTY; + sid = pid_vnr(real_tty->ctrl.session); + } return put_user(sid, p); - -err: - spin_unlock_irqrestore(&real_tty->ctrl.lock, flags); - return -ENOTTY; } /* diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 7d40eacc21b3..bf1502fd5bd4 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -53,8 +53,6 @@ #define HEADER_SIZE 4u #define CON_BUF_SIZE (IS_ENABLED(CONFIG_BASE_SMALL) ? 256 : PAGE_SIZE) -DEFINE_FREE(free_page_ptr, void *, if (_T) free_page((unsigned long)_T)); - /* * Our minor space: * @@ -371,7 +369,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) loff_t pos; bool viewed, attr, uni_mode; - char *con_buf __free(free_page_ptr) = (char *)__get_free_page(GFP_KERNEL); + char *con_buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!con_buf) return -ENOMEM; @@ -596,7 +594,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) if (use_unicode(inode)) return -EOPNOTSUPP; - char *con_buf __free(free_page_ptr) = (char *)__get_free_page(GFP_KERNEL); + char *con_buf __free(kfree) = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!con_buf) return -ENOMEM; diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index deb3b3f93461..ecefe4a666f2 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -598,6 +598,8 @@ static int vt_setactivate(struct vt_setactivate __user *sa) return -EFAULT; if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES) return -ENXIO; + if (vsa.mode.mode != VT_AUTO && vsa.mode.mode != VT_PROCESS) + return -EINVAL; vsa.console--; vsa.console = array_index_nospec(vsa.console, MAX_NR_CONSOLES); diff --git a/include/linux/goldfish.h b/include/linux/goldfish.h index bcc17f95b906..98a4719c6776 100644 --- a/include/linux/goldfish.h +++ b/include/linux/goldfish.h @@ -2,8 +2,6 @@ #ifndef __LINUX_GOLDFISH_H #define __LINUX_GOLDFISH_H -#include -#include #include /* Helpers for Goldfish virtual platform */ @@ -15,26 +13,4 @@ #define gf_iowrite32 iowrite32 #endif -static inline void gf_write_ptr(const void *ptr, void __iomem *portl, - void __iomem *porth) -{ - const unsigned long addr = (unsigned long)ptr; - - gf_iowrite32(lower_32_bits(addr), portl); -#ifdef CONFIG_64BIT - gf_iowrite32(upper_32_bits(addr), porth); -#endif -} - -static inline void gf_write_dma_addr(const dma_addr_t addr, - void __iomem *portl, - void __iomem *porth) -{ - gf_iowrite32(lower_32_bits(addr), portl); -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - gf_iowrite32(upper_32_bits(addr), porth); -#endif -} - - #endif /* __LINUX_GOLDFISH_H */ diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index a95b2d143d24..eeb868e93e15 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -150,8 +150,20 @@ struct uart_8250_port { #define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS u16 lsr_saved_flags; u16 lsr_save_mask; + + /* + * Track when a console line has been fully written to the + * hardware, i.e. true when the most recent byte written to + * UART_TX by the console was '\n'. + */ + bool console_line_ended; + + /* Allow queuing irq_work for MSR handling */ + bool console_msr_work_allow; + #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA unsigned char msr_saved_flags; + struct irq_work console_msr_work; struct uart_8250_dma *dma; const struct uart_8250_ops *ops; @@ -192,6 +204,7 @@ void serial8250_do_shutdown(struct uart_port *port); void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl); +void serial8250_do_break_ctl(struct uart_port *port, int break_state); void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, unsigned int quot); int fsl8250_handle_irq(struct uart_port *port); @@ -203,8 +216,8 @@ void serial8250_tx_chars(struct uart_8250_port *up); unsigned int serial8250_modem_status(struct uart_8250_port *up); void serial8250_init_port(struct uart_8250_port *up); void serial8250_set_defaults(struct uart_8250_port *up); -void serial8250_console_write(struct uart_8250_port *up, const char *s, - unsigned int count); +void serial8250_console_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, bool in_atomic); int serial8250_console_setup(struct uart_port *port, char *options, bool probe); int serial8250_console_exit(struct uart_port *port); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index bdc214386e4a..904760876ca8 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -437,6 +437,7 @@ enum uart_iotype { UPIO_TSI = SERIAL_IO_TSI, /* Tsi108/109 type IO */ UPIO_MEM32BE = SERIAL_IO_MEM32BE, /* 32b big endian */ UPIO_MEM16 = SERIAL_IO_MEM16, /* 16b little endian */ + UPIO_BUS = SERIAL_IO_BUS, /* Serial bus I/O access (ex: SPI, I2C) */ }; struct uart_port { @@ -459,10 +460,13 @@ struct uart_port { unsigned int baud, unsigned int quot, unsigned int quot_frac); + int (*get_rxtrig)(struct uart_port *port); + int (*set_rxtrig)(struct uart_port *port, unsigned char bytes); int (*startup)(struct uart_port *port); void (*shutdown)(struct uart_port *port); void (*throttle)(struct uart_port *port); void (*unthrottle)(struct uart_port *port); + void (*break_ctl)(struct uart_port *port, int break_state); int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int old); @@ -1338,4 +1342,9 @@ static inline int uart_handle_break(struct uart_port *port) !((cflag) & CLOCAL)) int uart_get_rs485_mode(struct uart_port *port); + +void uart_get_ioinfos(struct uart_port *port, char *buf, size_t size); +bool uart_iotype_mmio(enum uart_iotype iotype); +bool uart_iotype_io(enum uart_iotype iotype); + #endif /* LINUX_SERIAL_CORE_H */ diff --git a/include/trace/events/qcom_geni_serial.h b/include/trace/events/qcom_geni_serial.h index 417ec01f9fc8..e1aa551d525e 100644 --- a/include/trace/events/qcom_geni_serial.h +++ b/include/trace/events/qcom_geni_serial.h @@ -97,18 +97,17 @@ DECLARE_EVENT_CLASS(geni_serial_data, TP_ARGS(dev, buf, len), TP_STRUCT__entry(__string(name, dev_name(dev)) - __field(unsigned int, len) __dynamic_array(u8, data, len) ), TP_fast_assign(__assign_str(name); - __entry->len = len; memcpy(__get_dynamic_array(data), buf, len); ), TP_printk("%s: len=%u data=%s", - __get_str(name), __entry->len, - __print_hex(__get_dynamic_array(data), __entry->len)) + __get_str(name), __get_dynamic_array_len(data), + __print_hex(__get_dynamic_array(data), + __get_dynamic_array_len(data))) ); DEFINE_EVENT(geni_serial_data, geni_serial_tx_data, diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h index de9b4733607e..e6f61538fc28 100644 --- a/include/uapi/linux/serial.h +++ b/include/uapi/linux/serial.h @@ -72,6 +72,7 @@ struct serial_struct { #define SERIAL_IO_TSI 5 #define SERIAL_IO_MEM32BE 6 #define SERIAL_IO_MEM16 7 +#define SERIAL_IO_BUS 8 #define UART_CLEAR_FIFO 0x01 #define UART_USE_FIFO 0x02 diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 9c007a106330..377884e3856a 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -100,6 +100,9 @@ /* TXX9 type number */ #define PORT_TXX9 64 +/* Moxa MUEx50 UART */ +#define PORT_MUEX50 65 + /*Digi jsm */ #define PORT_JSM 69